diff --git a/kippo/core/ssh.py b/kippo/core/ssh.py index e667866..0c402b1 100644 --- a/kippo/core/ssh.py +++ b/kippo/core/ssh.py @@ -215,6 +215,11 @@ class HoneyPotAvatar(avatar.ConchUser): userdb = core.auth.UserDB() self.uid = self.gid = userdb.getUID(self.username) + # sftp support enabled only when option is explicitly set + if self.env.cfg.has_option('honeypot', 'sftp_enabled'): + if ( self.env.cfg.get('honeypot', 'sftp_enabled') == "true" ): + self.subsystemLookup['sftp'] = filetransfer.FileTransferServer + if not self.uid: self.home = '/root' else: @@ -304,4 +309,181 @@ def getDSAKeys(): privateKeyString = f.read() return publicKeyString, privateKeyString + +class KippoSFTPFile: + implements(conchinterfaces.ISFTPFile) + + def __init__(self, server, filename, flags, attrs): + self.server = server + self.filename = filename + self.transfer_completed = 0 + self.bytes_written = 0 + openFlags = 0 + if flags & FXF_READ == FXF_READ and flags & FXF_WRITE == 0: + openFlags = os.O_RDONLY + if flags & FXF_WRITE == FXF_WRITE and flags & FXF_READ == 0: + openFlags = os.O_WRONLY + if flags & FXF_WRITE == FXF_WRITE and flags & FXF_READ == FXF_READ: + openFlags = os.O_RDWR + if flags & FXF_APPEND == FXF_APPEND: + openFlags |= os.O_APPEND + if flags & FXF_CREAT == FXF_CREAT: + openFlags |= os.O_CREAT + if flags & FXF_TRUNC == FXF_TRUNC: + openFlags |= os.O_TRUNC + if flags & FXF_EXCL == FXF_EXCL: + openFlags |= os.O_EXCL + if attrs.has_key("permissions"): + mode = attrs["permissions"] + del attrs["permissions"] + else: + mode = 0777 + fd = server.fs.open(filename, openFlags, mode) + if attrs: + self.server.setAttrs(filename, attrs) + self.fd = fd + + # cache a copy of file in memory to read from in readChunk + if flags & FXF_READ == FXF_READ: + self.contents = self.server.fs.file_contents(self.filename) + + def close(self): + if ( self.bytes_written > 0 ): + self.server.fs.update_size(self.filename, self.bytes_written) + return self.server.fs.close(self.fd) + + def readChunk(self, offset, length): + return self.contents[offset:offset+length] + + def writeChunk(self, offset, data): + self.server.fs.lseek(self.fd, offset, os.SEEK_SET) + self.server.fs.write(self.fd, data) + self.bytes_written += len(data) + + def getAttrs(self): + s = self.server.fs.fstat(self.fd) + return self.server._getAttrs(s) + + def setAttrs(self, attrs): + raise NotImplementedError + +class KippoSFTPDirectory: + + def __init__(self, server, directory): + self.server = server + self.files = server.fs.listdir(directory) + self.dir = directory + + def __iter__(self): + return self + + def next(self): + try: + f = self.files.pop(0) + except IndexError: + raise StopIteration + else: + s = self.server.fs.lstat(os.path.join(self.dir, f)) + longname = twisted.conch.ls.lsLine(f, s) + attrs = self.server._getAttrs(s) + return (f, longname, attrs) + + def close(self): + self.files = [] + +class KippoSFTPServer: + implements(conchinterfaces.ISFTPServer) + + def __init__(self, avatar): + self.avatar = avatar + # FIXME we should not copy fs here, but do this at avatar instantiation + self.fs = fs.HoneyPotFilesystem(copy.deepcopy(self.avatar.env.fs)) + + def _absPath(self, path): + home = self.avatar.home + return os.path.abspath(os.path.join(home, path)) + + def _setAttrs(self, path, attrs): + if attrs.has_key("uid") and attrs.has_key("gid"): + self.fs.chown(path, attrs["uid"], attrs["gid"]) + if attrs.has_key("permissions"): + self.fs.chmod(path, attrs["permissions"]) + if attrs.has_key("atime") and attrs.has_key("mtime"): + self.fs.utime(path, attrs["atime"], attrs["mtime"]) + + def _getAttrs(self, s): + return { + "size" : s.st_size, + "uid" : s.st_uid, + "gid" : s.st_gid, + "permissions" : s.st_mode, + "atime" : int(s.st_atime), + "mtime" : int(s.st_mtime) + } + + def gotVersion(self, otherVersion, extData): + return {} + + def openFile(self, filename, flags, attrs): + print "SFTP openFile: %s" % filename + return KippoSFTPFile(self, self._absPath(filename), flags, attrs) + + def removeFile(self, filename): + print "SFTP removeFile: %s" % filename + return self.fs.remove(self._absPath(filename)) + + def renameFile(self, oldpath, newpath): + print "SFTP renameFile: %s %s" % (oldpath, newpath) + return self.fs.rename(self._absPath(oldpath), self._absPath(newpath)) + + def makeDirectory(self, path, attrs): + print "SFTP makeDirectory: %s" % path + path = self._absPath(path) + self.fs.mkdir2(path) + self._setAttrs(path, attrs) + return + + def removeDirectory(self, path): + print "SFTP removeDirectory: %s" % path + return self.fs.rmdir(self._absPath(path)) + + def openDirectory(self, path): + print "SFTP OpenDirectory: %s" % path + return KippoSFTPDirectory(self, self._absPath(path)) + + def getAttrs(self, path, followLinks): + print "SFTP getAttrs: %s" % path + path = self._absPath(path) + if followLinks: + s = self.fs.stat(path) + else: + s = self.fs.lstat(path) + return self._getAttrs(s) + + def setAttrs(self, path, attrs): + print "SFTP setAttrs: %s" % path + path = self._absPath(path) + return self._setAttrs(path, attrs) + + def readLink(self, path): + print "SFTP readLink: %s" % path + path = self._absPath(path) + return self.fs.readlink(path) + + def makeLink(self, linkPath, targetPath): + print "SFTP makeLink: %s" % path + linkPath = self._absPath(linkPath) + targetPath = self._absPath(targetPath) + return self.fs.symlink(targetPath, linkPath) + + def realPath(self, path): + print "SFTP realPath: %s" % path + return self.fs.realpath(self._absPath(path)) + + def extendedRequest(self, extName, extData): + raise NotImplementedError + +components.registerAdapter( KippoSFTPServer, HoneyPotAvatar, conchinterfaces.ISFTPServer) + + # vim: set et sw=4 et: