SFTP: Support statvfs extensions, avoid logging actual data, and decline shell sessions politely.

This commit is contained in:
david-sarah 2010-05-16 08:43:47 -07:00
parent 31994aaf74
commit 6a2b0229f0
2 changed files with 54 additions and 22 deletions

View File

@ -1,5 +1,5 @@
import os, tempfile, heapq, binascii, traceback, array, stat
import os, tempfile, heapq, binascii, traceback, array, stat, struct
from stat import S_IFREG, S_IFDIR
from time import time, strftime, localtime
@ -41,17 +41,15 @@ warnings.filterwarnings("ignore", category=DeprecationWarning,
noisy = True
use_foolscap_logging = True
from allmydata.util.log import NOISY, OPERATIONAL, SCARY
if use_foolscap_logging:
from allmydata.util.log import msg as logmsg, err as logerr, \
NOISY, OPERATIONAL, SCARY, PrefixingLogMixin
from allmydata.util.log import msg as logmsg, err as logerr, PrefixingLogMixin
else:
def logmsg(s, level=None):
print s
def logerr(s, level=None):
print s
NOISY = None
OPERATIONAL = None
SCARY = None
class PrefixingLogMixin:
def __init__(self, facility=None):
pass
@ -290,7 +288,7 @@ class EncryptedTemporaryFile(PrefixingLogMixin):
return plaintext
def write(self, plaintext):
if noisy: self.log(".write(%r)" % (plaintext,), level=NOISY)
if noisy: self.log(".write(<data of length %r>)" % (len(plaintext),), level=NOISY)
index = self.file.tell()
ciphertext = self._crypt(index, plaintext)
self.file.write(ciphertext)
@ -357,7 +355,7 @@ class OverwriteableFileConsumer(PrefixingLogMixin):
p.resumeProducing()
def write(self, data):
if noisy: self.log(".write(%r)" % (data,), level=NOISY)
if noisy: self.log(".write(<data of length %r>)" % (len(data),), level=NOISY)
if self.check_abort():
self.close()
return
@ -833,7 +831,8 @@ class SFTPUserHandler(ConchUser, PrefixingLogMixin):
def openShell(self, protocol):
self.log(".openShell(%r)" % (protocol,), level=OPERATIONAL)
raise NotImplementedError
protocol.write("This server supports only SFTP, not shell sessions.\n")
protocol.processEnded(Reason(ProcessTerminated(exitCode=1)))
def execCommand(self, protocol, cmd):
self.log(".execCommand(%r, %r)" % (protocol, cmd), level=OPERATIONAL)
@ -1167,9 +1166,22 @@ class SFTPUserHandler(ConchUser, PrefixingLogMixin):
def extendedRequest(self, extendedName, extendedData):
self.log(".extendedRequest(%r, %r)" % (extendedName, extendedData), level=OPERATIONAL)
# A client 'df' command requires the 'statvfs@openssh.com' extension,
# but there's little point to implementing that since we would only
# have faked values to report.
if extendedName == 'statvfs@openssh.com' or extendedName == 'fstatvfs@openssh.com':
# <http://dev.libssh.org/ticket/11>
return struct.pack('>QQQQQQQQQQQ',
1024, # uint64 f_bsize /* file system block size */
1024, # uint64 f_frsize /* fundamental fs block size */
628318530, # uint64 f_blocks /* number of blocks (unit f_frsize) */
314159265, # uint64 f_bfree /* free blocks in file system */
314159265, # uint64 f_bavail /* free blocks for non-root */
200000000, # uint64 f_files /* total file inodes */
100000000, # uint64 f_ffree /* free file inodes */
100000000, # uint64 f_favail /* free file inodes for non-root */
0x1AF5, # uint64 f_fsid /* file system id */
2, # uint64 f_flag /* bit mask = ST_NOSUID; not ST_RDONLY */
65535, # uint64 f_namemax /* maximum filename length */
)
raise SFTPError(FX_OP_UNSUPPORTED, "extendedRequest %r" % extendedName)
def realPath(self, pathstring):

View File

@ -266,9 +266,6 @@ class Handler(GridTestMixin, ShouldFailMixin, unittest.TestCase):
d.addCallback(lambda ign:
self.shouldFailWithSFTPError(sftp.FX_OP_UNSUPPORTED, "makeLink link file",
self.handler.makeLink, "link", "file"))
d.addCallback(lambda ign:
self.shouldFailWithSFTPError(sftp.FX_OP_UNSUPPORTED, "extendedRequest foo bar",
self.handler.extendedRequest, "foo", "bar"))
return d
@ -913,7 +910,7 @@ class Handler(GridTestMixin, ShouldFailMixin, unittest.TestCase):
self.handler.makeDirectory, "small", {}))
return d
def test_execCommand(self):
def test_execCommand_and_openShell(self):
class FakeProtocol:
def __init__(self):
self.output = ""
@ -923,20 +920,43 @@ class Handler(GridTestMixin, ShouldFailMixin, unittest.TestCase):
def processEnded(self, reason):
self.reason = reason
protocol_ok = FakeProtocol()
protocol_df = FakeProtocol()
protocol_error = FakeProtocol()
protocol_shell = FakeProtocol()
d = self._set_up("execCommand")
d = self._set_up("execCommand_and_openShell")
d.addCallback(lambda ign: self.handler.execCommand(protocol_ok, "df -P -k /"))
d.addCallback(lambda ign: self.failUnlessIn("1024-blocks", protocol_ok.output))
d.addCallback(lambda ign: self.failUnless(isinstance(protocol_ok.reason.value, ProcessDone)))
d.addCallback(lambda ign: self.handler.execCommand(protocol_df, "df -P -k /"))
d.addCallback(lambda ign: self.failUnlessIn("1024-blocks", protocol_df.output))
d.addCallback(lambda ign: self.failUnless(isinstance(protocol_df.reason.value, ProcessDone)))
d.addCallback(lambda ign: self.handler.eofReceived())
d.addCallback(lambda ign: self.handler.closed())
d.addCallback(lambda ign: self.handler.execCommand(protocol_error, "error"))
d.addCallback(lambda ign: self.failUnlessEqual(protocol_error.output, ""))
d.addCallback(lambda ign: self.failUnlessEqual("", protocol_error.output))
d.addCallback(lambda ign: self.failUnless(isinstance(protocol_error.reason.value, ProcessTerminated)))
d.addCallback(lambda ign: self.failUnlessEqual(protocol_error.reason.value.exitCode, 1))
d.addCallback(lambda ign: self.handler.closed())
d.addCallback(lambda ign: self.handler.openShell(protocol_shell))
d.addCallback(lambda ign: self.failUnlessIn("only SFTP", protocol_shell.output))
d.addCallback(lambda ign: self.failUnless(isinstance(protocol_shell.reason.value, ProcessTerminated)))
d.addCallback(lambda ign: self.failUnlessEqual(protocol_shell.reason.value.exitCode, 1))
d.addCallback(lambda ign: self.handler.closed())
return d
def test_extendedRequest(self):
d = self._set_up("extendedRequest")
d.addCallback(lambda ign: self.handler.extendedRequest("statvfs@openssh.com", "/"))
def _check(res):
self.failUnless(isinstance(res, str))
self.failUnlessEqual(len(res), 8*11)
d.addCallback(_check)
d.addCallback(lambda ign:
self.shouldFailWithSFTPError(sftp.FX_OP_UNSUPPORTED, "extendedRequest foo bar",
self.handler.extendedRequest, "foo", "bar"))
return d