1# Copyright (c) 2009-2014 Upi Tamminen <desaster@gmail.com> 2# See the COPYRIGHT file for more information 3 4""" 5This module contains ... 6""" 7 8from __future__ import absolute_import, division 9 10import os 11 12import twisted 13import twisted.conch.ls 14from twisted.conch.interfaces import ISFTPFile, ISFTPServer 15from twisted.conch.ssh import filetransfer 16from twisted.conch.ssh.filetransfer import FXF_APPEND, FXF_CREAT, FXF_EXCL, FXF_READ, FXF_TRUNC, FXF_WRITE 17from twisted.python import log 18from twisted.python.compat import nativeString 19 20from zope.interface import implementer 21 22import cowrie.shell.pwd as pwd 23from cowrie.core.config import CowrieConfig 24 25 26@implementer(ISFTPFile) 27class CowrieSFTPFile(object): 28 """ 29 SFTPTFile 30 """ 31 transfer_completed = 0 32 bytesReceived = 0 33 bytesReceivedLimit = CowrieConfig().getint('honeypot', 'download_limit_size', fallback=0) 34 35 def __init__(self, sftpserver, filename, flags, attrs): 36 self.sftpserver = sftpserver 37 self.filename = filename 38 39 openFlags = 0 40 if flags & FXF_READ == FXF_READ and flags & FXF_WRITE == 0: 41 openFlags = os.O_RDONLY 42 if flags & FXF_WRITE == FXF_WRITE and flags & FXF_READ == 0: 43 openFlags = os.O_WRONLY 44 if flags & FXF_WRITE == FXF_WRITE and flags & FXF_READ == FXF_READ: 45 openFlags = os.O_RDWR 46 if flags & FXF_APPEND == FXF_APPEND: 47 openFlags |= os.O_APPEND 48 if flags & FXF_CREAT == FXF_CREAT: 49 openFlags |= os.O_CREAT 50 if flags & FXF_TRUNC == FXF_TRUNC: 51 openFlags |= os.O_TRUNC 52 if flags & FXF_EXCL == FXF_EXCL: 53 openFlags |= os.O_EXCL 54 if "permissions" in attrs: 55 filemode = attrs["permissions"] 56 del attrs["permissions"] 57 else: 58 filemode = 0o777 59 fd = sftpserver.fs.open(filename, openFlags, filemode) 60 if attrs: 61 self.sftpserver.setAttrs(filename, attrs) 62 self.fd = fd 63 64 # Cache a copy of file in memory to read from in readChunk 65 if flags & FXF_READ == FXF_READ: 66 self.contents = self.sftpserver.fs.file_contents(self.filename) 67 68 def close(self): 69 if self.bytesReceived > 0: 70 self.sftpserver.fs.update_size(self.filename, self.bytesReceived) 71 return self.sftpserver.fs.close(self.fd) 72 73 def readChunk(self, offset, length): 74 return self.contents[offset:offset + length] 75 76 def writeChunk(self, offset, data): 77 self.bytesReceived += len(data) 78 if self.bytesReceivedLimit and self.bytesReceived > self.bytesReceivedLimit: 79 raise filetransfer.SFTPError(filetransfer.FX_FAILURE, "Quota exceeded") 80 self.sftpserver.fs.lseek(self.fd, offset, os.SEEK_SET) 81 self.sftpserver.fs.write(self.fd, data) 82 83 def getAttrs(self): 84 s = self.sftpserver.fs.stat(self.filename) 85 return self.sftpserver.getAttrs(s) 86 87 def setAttrs(self, attrs): 88 raise NotImplementedError 89 90 91class CowrieSFTPDirectory(object): 92 93 def __init__(self, server, directory): 94 self.server = server 95 self.files = server.fs.listdir(directory) 96 self.files = [".", ".."] + self.files 97 self.dir = directory 98 99 def __iter__(self): 100 return self 101 102 def next(self): 103 """ 104 Py2 compatibility 105 """ 106 return self.__next__() 107 108 def __next__(self): 109 try: 110 f = self.files.pop(0) 111 except IndexError: 112 raise StopIteration 113 114 if f == "..": 115 directory = self.dir.strip().split("/") 116 pdir = "/" + "/".join(directory[:-1]) 117 s1 = self.server.fs.lstat(pdir) 118 s = self.server.fs.lstat(pdir) 119 s1.st_uid = pwd.Passwd().getpwuid(s.st_uid)["pw_name"] 120 s1.st_gid = pwd.Group().getgrgid(s.st_gid)["gr_name"] 121 longname = twisted.conch.ls.lsLine(f, s1) 122 attrs = self.server._getAttrs(s) 123 return (f, longname, attrs) 124 elif f == ".": 125 s1 = self.server.fs.lstat(self.dir) 126 s = self.server.fs.lstat(self.dir) 127 s1.st_uid = pwd.Passwd().getpwuid(s.st_uid)["pw_name"] 128 s1.st_gid = pwd.Group().getgrgid(s.st_gid)["gr_name"] 129 longname = twisted.conch.ls.lsLine(f, s1) 130 attrs = self.server._getAttrs(s) 131 return (f, longname, attrs) 132 else: 133 s = self.server.fs.lstat(os.path.join(self.dir, f)) 134 s2 = self.server.fs.lstat(os.path.join(self.dir, f)) 135 s2.st_uid = pwd.Passwd().getpwuid(s.st_uid)["pw_name"] 136 s2.st_gid = pwd.Group().getgrgid(s.st_gid)["gr_name"] 137 longname = twisted.conch.ls.lsLine(f, s2) 138 attrs = self.server._getAttrs(s) 139 return (f, longname, attrs) 140 141 def close(self): 142 self.files = [] 143 144 145@implementer(ISFTPServer) 146class SFTPServerForCowrieUser(object): 147 148 def __init__(self, avatar): 149 self.avatar = avatar 150 self.avatar.server.initFileSystem(self.avatar.home) 151 self.fs = self.avatar.server.fs 152 153 def _absPath(self, path): 154 home = self.avatar.home 155 return os.path.abspath(os.path.join(nativeString(home), nativeString(path))) 156 157 def _setAttrs(self, path, attrs): 158 if "uid" in attrs and "gid" in attrs: 159 self.fs.chown(path, attrs["uid"], attrs["gid"]) 160 if "permissions" in attrs: 161 self.fs.chmod(path, attrs["permissions"]) 162 if "atime" in attrs and "mtime" in attrs: 163 self.fs.utime(path, attrs["atime"], attrs["mtime"]) 164 165 def _getAttrs(self, s): 166 return { 167 "size": s.st_size, 168 "uid": s.st_uid, 169 "gid": s.st_gid, 170 "permissions": s.st_mode, 171 "atime": int(s.st_atime), 172 "mtime": int(s.st_mtime) 173 } 174 175 def gotVersion(self, otherVersion, extData): 176 return {} 177 178 def openFile(self, filename, flags, attrs): 179 log.msg("SFTP openFile: {}".format(filename)) 180 return CowrieSFTPFile(self, self._absPath(filename), flags, attrs) 181 182 def removeFile(self, filename): 183 log.msg("SFTP removeFile: {}".format(filename)) 184 return self.fs.remove(self._absPath(filename)) 185 186 def renameFile(self, oldpath, newpath): 187 log.msg("SFTP renameFile: {} {}".format(oldpath, newpath)) 188 return self.fs.rename(self._absPath(oldpath), self._absPath(newpath)) 189 190 def makeDirectory(self, path, attrs): 191 log.msg("SFTP makeDirectory: {}".format(path)) 192 path = self._absPath(path) 193 self.fs.mkdir2(path) 194 self._setAttrs(path, attrs) 195 196 def removeDirectory(self, path): 197 log.msg("SFTP removeDirectory: {}".format(path)) 198 return self.fs.rmdir(self._absPath(path)) 199 200 def openDirectory(self, path): 201 log.msg("SFTP OpenDirectory: {}".format(path)) 202 return CowrieSFTPDirectory(self, self._absPath(path)) 203 204 def getAttrs(self, path, followLinks): 205 log.msg("SFTP getAttrs: {}".format(path)) 206 path = self._absPath(path) 207 if followLinks: 208 s = self.fs.stat(path) 209 else: 210 s = self.fs.lstat(path) 211 return self._getAttrs(s) 212 213 def setAttrs(self, path, attrs): 214 log.msg("SFTP setAttrs: {}".format(path)) 215 path = self._absPath(path) 216 return self._setAttrs(path, attrs) 217 218 def readLink(self, path): 219 log.msg("SFTP readLink: {}".format(path)) 220 path = self._absPath(path) 221 return self.fs.readlink(path) 222 223 def makeLink(self, linkPath, targetPath): 224 log.msg("SFTP makeLink: {} {}".format(linkPath, targetPath)) 225 linkPath = self._absPath(linkPath) 226 targetPath = self._absPath(targetPath) 227 return self.fs.symlink(targetPath, linkPath) 228 229 def realPath(self, path): 230 return self.fs.realpath(self._absPath(path)) 231 232 def extendedRequest(self, extName, extData): 233 raise NotImplementedError 234