class MyTSFTPRequestHandler(socketserver.BaseRequestHandler): timeout = 60 auth_timeout = 60 def setup(self): self.transport = Transport(self.request) self.transport.load_server_moduli() so = self.transport.get_security_options() so.digests = ('hmac-sha1', ) so.compression = ('*****@*****.**', 'none') self.transport.add_server_key(self.server.host_key) self.transport.set_subsystem_handler('sftp', MyTSFTPServer, MyTSFTPServerInterface) def handle(self): try: self.transport.start_server(server=MyTServerInterface()) except SSHException as e: logger.error("SSH error: %s" % str(e)) self.transport.close() except EOFError as e: logger.error("Socket error: %s" % str(e)) except Exception as e: logger.error("Error: %s" % str(e)) def handle_timeout(self): self.transport.close()
def __ensure_connection(self, transport: paramiko.Transport, force: bool = False) -> paramiko.Transport: if transport is None or not transport.is_active() or force: if transport is not None: transport.close() transport = paramiko.Transport((self.__host, self.__port)) logger.info('Connecting to {} on port {}'.format( self.__host, self.__port)) try: if isinstance(self.__credential, PasswordCredential): logger.debug('Authenticating using a password') transport.connect(username=self.__credential.username, password=self.__credential.password) elif isinstance(self.__credential, PubKeyCredential): logger.debug('Authenticating using a public key') key = self.__get_key_from_file( self.__credential.public_key, self.__credential.passphrase) transport.connect(username=self.__credential.username, pkey=key) else: raise RuntimeError('Unknown kind of credential') logger.info('Connection (re)established') except paramiko.SSHException: raise ConnectionError( 'Cerulean was disconnected and could not reconnect') return transport
def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip(b"\r\n").split(b" ") try: self.data["version"] = banner[0].decode() self.data["os"] = banner[1].decode() except IndexError: pass s.send(banner[0] + b"\r\n") self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data["pubkey_name"] = pubkey.get_name() fp = pubkey.get_fingerprint() self.data["pubkey_fingerprint"] = fp.hex() except Exception as e: print(repr(e)) return None finally: tran.close() return True
def _on_open_port(args): host, port, socket = args try: ssh_conn = Transport(socket) if key_type is not None: new_preferred_keys = [key_type] new_preferred_keys.extend(ssh_conn._preferred_keys) ssh_conn._preferred_keys = tuple(new_preferred_keys) try: ssh_conn.start_client() key = ssh_conn.get_remote_server_key() key_md5 = md5(str(key)).hexdigest() fingerprint = ':'.join( a + b for a, b in zip(key_md5[::2], key_md5[1::2])) data_cb(host, port, True, (key.get_name(), fingerprint, b64encode(str(key)))) finally: ssh_conn.close() except (socket_error, NoValidConnectionsError): data_cb(host, port, None, None) except Exception as e: data_cb(host, port, False, 'Exception: {}: {}'.format(type(e), str(e))) finally: socket.close()
def upload_file(host, port, usr, psw, local_path, remote_path): file_count = 0 print('-' * 50 + '\n') print('Start uploading files') transport = Transport((host, port)) transport.connect(username=usr, password=psw) sftp = SFTPClient.from_transport(transport) for file_name in listdir(local_path): local_file = local_path + '\\' + file_name remote_file = remote_path + '/' + file_name if file_name.split('.')[-1] == 'css': new_css = cssmini(open(local_file, 'r').read()) with open('tmp.css', 'w') as tmp_css: tmp_css.write(new_css) tmp_css.close() sftp.put(cur_path + tmp_css.name, remote_file) remove(cur_path + tmp_css.name) elif file_name.split('.')[-1] == 'js': new_js = jsmini(open(local_file, 'r', encoding='utf-8').read()) with open('tmp.js', 'w', encoding='utf-8') as tmp_js: tmp_js.write(new_js) tmp_js.close() sftp.put(cur_path + tmp_js.name, remote_file) remove(cur_path + tmp_js.name) else: sftp.put(local_file, remote_file) print(file_name + 'Upload completed') file_count += 1 transport.close() print('All files have been uploaded, Connection has been closed, Number of files:{}'.format(file_count)) print('-' * 50 + '\n')
class CSftpParamiko: def __init__(self): # set host, user, passwd self.hostname = '172.16.3.36' self.username = '******' self.password = '******' # self.hostname = HOST_NAME # self.username = USER_NAME # self.password = PASSWORD def __exit__(self): pass def fileSave(self, local_file, remote_path): # connect to sftp server try: self.transport = Transport((self.hostname, 22)) self.sftp = self.transport.connect(username=self.username, password=self.password) self.sftp_client = SFTPClient.from_transport(self.transport) except: print("[SFTP_P]: connect fail") try: self.sftp_client.put(local_file, remote_path) except: print("[SFTP_P]: put fail") try: self.sftp_client.close() self.transport.close() except: print("[SFTP_P]: close fail")
def sftpclient(sftpserver): transport = Transport((sftpserver.host, sftpserver.port)) transport.connect(username="******", password="******") sftpclient = SFTPClient.from_transport(transport) yield sftpclient sftpclient.close() transport.close()
def _get_server_keys(hostname): server_keys = [] # key_type_list = ["ssh-ed25519", "ssh-rsa", "ecdsa-sha2-nistp256"] # default key_type used by ssh-keysca # Supported key_type for OS # alinux ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # ubuntu1404 ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # ubuntu1604 ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # centos7 ssh-rsa,ssh-ed25519,ecdsa-sha2-nistp256 # centos6 ssh-rsa key_type_list = ["ssh-rsa"] for key_type in key_type_list: transport = None try: sock = socket.socket() sock.settimeout(5) sock.connect((hostname, 22)) transport = Transport(sock) transport._preferred_keys = [key_type] transport.start_client() server_keys.append(transport.get_remote_server_key()) except Exception: pass finally: if transport: transport.close() if not server_keys: logging.error("Failed retrieving server key from host '%s'", hostname) return hostname, [(server_key.get_base64(), server_key.get_name()) for server_key in server_keys]
class SFTPUploader(object): """Uploads files to (s)ftp""" def __init__(self, sftp_settings): self.transport = Transport( (sftp_settings['HOST'], int(sftp_settings['PORT']))) self.transport.connect(username=sftp_settings['USER'], password=sftp_settings['PASSWORD']) self.connection = SFTPClient.from_transport(self.transport) logger.debug( "SFTPUploader initiated. Sending files to {host}:{port}".format( host=sftp_settings['HOST'], port=sftp_settings['PORT'])) def __del__(self): try: self.connection.close() self.transport.close() except AttributeError: pass logger.debug("SFTPUploader session completed. Connection closed.") def upload_file(self, local_filepath, filename): logger.debug("SFTPUploader: Uploading file {filepath}".format( filepath=local_filepath)) try: self.connection.remove(path='./{filename}'.format( filename=filename)) except IOError: pass self.connection.put( localpath=local_filepath, remotepath='./{filename}'.format(filename=filename))
def trans(): """ Create `LoopSocket`-based server/client `Transport`s, yielding the latter. Uses `NullServer` under the hood. """ # NOTE: based on the setup/teardown/start_server/verify_finished methods # found in ye olde test_auth.py # "Network" setup socks = LoopSocket() sockc = LoopSocket() sockc.link(socks) tc = Transport(sockc) ts = Transport(socks) # Start up the in-memory server host_key = RSAKey.from_private_key_file(_support('test_rsa.key')) ts.add_server_key(host_key) event = threading.Event() server = NullServer() ts.start_server(event, server) # Tests frequently need to call Transport.connect on the client side, etc yield tc # Close things down tc.close() ts.close() socks.close() sockc.close()
def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip('\r\n').split(' ') try: self.data["version"] = banner[0] self.data["os"] = banner[1] except IndexError: pass s.send('{}\r\n'.format(banner[0])) self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data["pubkey_name"] = pubkey.get_name() fp = pubkey.get_fingerprint() self.data["pubkey_fingerprint"] = ':'.join(map(lambda x:x.encode('hex'), fp)) except Exception as e: cprint(str(e), 'error') return None finally: tran.close() self.clear() return True
def mx(src: str): spl = src.split(':') if len(spl) != 2: raise BaseException('Invalid url') map_id = spl[1] print('Downloading from MX: {}'.format(map_id)) with urlopen('https://tm.mania-exchange.com/tracks/download/{}'.format( map_id)) as res: _, params = cgi.parse_header(res.headers.get('Content-Disposition', '')) filename = params.get('filename', '{}.Map.Gbx'.format(map_id)) data = res.read() t = Transport((settings['host'], settings['port'])) print('Connecting') t.connect(username=settings['user'], pkey=paramiko.rsakey.RSAKey.from_private_key_file( settings['pkey'])) print('Connected') client = sftp.SFTPClient.from_transport(t) print('Uploading file') dst = settings['dest'] with client.open('{}/{}'.format(dst, filename), 'wb') as file: file.write(data) print('Done') client.close() t.close()
def remote_scp(ftp_type, host_ip, remote_path, local_path, username, password): ssh_port = 22 try: conn = Transport((host_ip, ssh_port)) conn.connect(username=username, password=password) sftp = SFTPClient.from_transport(conn) if ftp_type == 'remoteRead': print('read') if not local_path: filename = os.path.split(remote_path) local_path = os.path.join('/tmp', filename[-1]) print('开始从服务器下载文件......') sftp.get(remote_path, local_path) print(f'文件{filename[-1]}已经下载到本地') if ftp_type == "remoteWrite": print('write') sftp.put(local_path, remote_path) conn.close() return True except IOError as e: print('没有找到目录', e) except Exception as e: print('error!!!', e)
def transport(self): transport = Transport((self.host, self.port)) transport.connect(username=self.username, password=self.password) sftp = SFTPClient.from_transport(transport) sftp.put(f'{self.project_url}/web/caweb/html.zip', f'{self.nginx_url}/html.zip') sftp.put(f'{self.project_url}/server/caserver.zip', f'{self.nginx_url}/server/new_caserver.zip') print("transport finished!") transport.close()
def connect(username, hostname='lxplus.cern.ch', port=22): "Connect to a given host" print "Connecting to %s@%s" % (username, hostname) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((hostname, port)) except Exception as err: print '*** Connect failed: ' + str(err) sys.exit(1) transport = Transport(sock) try: transport.start_client() except paramiko.SSHException as err: print "SSH negotiation failed\n%s" % str(err) try: keys = paramiko.util.load_host_keys(\ os.path.expanduser('~/.ssh/known_hosts')) except IOError: try: keys = paramiko.util.load_host_keys(\ os.path.expanduser('~/ssh/known_hosts')) except IOError: print '*** Unable to open host keys file' keys = {} # check server's host key -- this is important. key = transport.get_remote_server_key() if not keys.has_key(hostname): print '*** WARNING: Unknown host key!' elif not keys[hostname].has_key(key.get_name()): print '*** WARNING: Unknown host key!' elif keys[hostname][key.get_name()] != key: print '*** WARNING: Host key has changed!!!' sys.exit(1) else: pass # get username if username == '': default_username = getpass.getuser() username = raw_input('Username [%s]: ' % default_username) if len(username) == 0: username = default_username agent_auth(transport, username) if not transport.is_authenticated(): manual_auth(transport, username, hostname) if not transport.is_authenticated(): print '*** Authentication failed. :(' transport.close() sys.exit(1) return transport, sock
def connect(username, hostname='lxplus.cern.ch', port=22): "Connect to a given host" print "Connecting to %s@%s" % (username, hostname) try: sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) sock.connect((hostname, port)) except Exception as err: print '*** Connect failed: ' + str(err) sys.exit(1) transport = Transport(sock) try: transport.start_client() except paramiko.SSHException as err: print "SSH negotiation failed\n%s" % str(err) try: keys = paramiko.util.load_host_keys(\ os.path.expanduser('~/.ssh/known_hosts')) except IOError: try: keys = paramiko.util.load_host_keys(\ os.path.expanduser('~/ssh/known_hosts')) except IOError: print '*** Unable to open host keys file' keys = {} # check server's host key -- this is important. key = transport.get_remote_server_key() if not keys.has_key(hostname): print '*** WARNING: Unknown host key!' elif not keys[hostname].has_key(key.get_name()): print '*** WARNING: Unknown host key!' elif keys[hostname][key.get_name()] != key: print '*** WARNING: Host key has changed!!!' sys.exit(1) else: pass # get username if username == '': default_username = getpass.getuser() username = raw_input('Username [%s]: ' % default_username) if len(username) == 0: username = default_username agent_auth(transport, username) if not transport.is_authenticated(): manual_auth(transport, username, hostname) if not transport.is_authenticated(): print '*** Authentication failed. :(' transport.close() sys.exit(1) return transport, sock
def sshAuthentication(self, clientsock): # setup logging paramiko.util.log_to_file(C.SYSLOG_FILE) # Check that SSH server parameters have been set: if (self.sshData == None): return clientsock, False, None else: # Load private key of the server filekey = self.sshData["hostKeyFile"] if (not filekey.startswith("/")): filekey = C.YENCAP_CONF_HOME + "/" + filekey # Build a key object from the file path: if (self.sshData["hostKeyType"] == "dss"): priv_host_key = paramiko.DSSKey(filename=filekey) elif (self.sshData["hostKeyType"] == "rsa"): priv_host_key = paramiko.RSAKey(filename=filekey) try: event = threading.Event() # Create a new SSH session over an existing socket, or socket-like object. t = Transport(clientsock) # Add a host key to the list of keys used for server mode. t.add_server_key(priv_host_key) # paramiko.ServerInterface defines an interface for controlling the behavior of paramiko in server mode. server = SSHServerModule() # Negotiate a new SSH2 session as a server. t.start_server(event, server) while 1: event.wait(0.1) if not t.is_active(): return clientsock, False, None if event.isSet(): break # Return the next channel opened by the client over this transport, in server mode. channel = t.accept(20) if channel is None: return clientsock, False, None except Exception, e: LogManager.getInstance().logError("Caught exception: %s: %s" % (str(e.__class__), str(e))) traceback.print_exc() try: t.close() except: pass return clientsock, False, None
def sshAuthentication(self, clientsock): # setup logging paramiko.util.log_to_file(C.SYSLOG_FILE) # Check that SSH server parameters have been set: if (self.sshData == None): return clientsock, False, None else: # Load private key of the server filekey = self.sshData["hostKeyFile"] if (not filekey.startswith("/")): filekey = C.YENCAP_CONF_KEYS + "/" + filekey # Build a key object from the file path: if (self.sshData["hostKeyType"] == "dss"): priv_host_key = paramiko.DSSKey(filename=filekey) elif (self.sshData["hostKeyType"] == "rsa"): priv_host_key = paramiko.RSAKey(filename=filekey) try: event = threading.Event() # Create a new SSH session over an existing socket, or socket-like object. t = Transport(clientsock) # Add a host key to the list of keys used for server mode. t.add_server_key(priv_host_key) # paramiko.ServerInterface defines an interface for controlling the behavior of paramiko in server mode. server = SSHServerModule() # Negotiate a new SSH2 session as a server. t.start_server(event, server) while 1: event.wait(0.1) if not t.is_active(): return clientsock, False, None if event.isSet(): break # Return the next channel opened by the client over this transport, in server mode. channel = t.accept(20) if channel is None: return clientsock, False, None except Exception, e: LogManager.getInstance().logError("Caught exception: %s: %s" % (str(e.__class__), str(e))) traceback.print_exc() try: t.close() except: pass return clientsock, False, None
def get_remote_file(ssh_config: SshClientConfig, local_path: Path, remote_path: Path): """Use SFTP to copy a file from a remote server to the local server. :param ssh_config: Configuration of the SSH session used for SFTP :param local_path: Destination path of the file being transferred :param remote_path: Source path of the file being transferred """ ssh_transport = Transport((ssh_config.remote_server, int(ssh_config.ssh_port))) ssh_key = RSAKey.from_private_key_file("/home/mycroft/.ssh/id_rsa") ssh_transport.connect(hostkey=None, username=ssh_config.remote_user, pkey=ssh_key) sftp_client = SFTPClient.from_transport(ssh_transport) sftp_client.get(str(remote_path), str(local_path)) sftp_client.close() ssh_transport.close()
class SftpAuth: def __init__(self, user, hostname, port, rsa_key, remote_dir): self.user = user self.hostname = hostname self.port = port self.rsa_key = rsa_key self.remote_dir = remote_dir logger.info(self.__repr__()) def __repr__(self): return "The sftp instance was initialized. " \ "user={}, hostname={}, port={}, rsa_key={}, remote_dir={}"\ .format(self.user, self.hostname, self.port, self.rsa_key, self.remote_dir) def set_transport(self): logger.debug("Try to initialize transport.") try: self.transport = Transport(f"{self.hostname}:{self.port}") self.transport.start_client(event=None, timeout=15) self.transport.get_remote_server_key() rsa_key = RSAKey.from_private_key_file(self.rsa_key) self.transport.auth_publickey(self.user, rsa_key) except Exception as e: logger.debug(e) print(json.dumps({ "error": { "code": 00, "message": e }, }), flush=True) raise logger.info("Transport was initialized.") def __enter__(self): self.set_transport() self.sftp = SFTPClient.from_transport(self.transport) try: logger.info("Try to make a directory") self.sftp.mkdir(self.remote_dir) except Exception as e: logger.info(e) logger.info(f"Change the current directory into {self.remote_dir}") self.sftp.chdir(self.remote_dir) return self.sftp def __exit__(self, exc_type, exc_val, exc_tb): self.transport.close() self.sftp.close()
def sftp_client(ip,uname,pw,r_path,l_path): # util.log_to_file('/root/download_config/paramiko.log') try: t=Transport((ip,22)) t.connect(username=uname,password=pw) sftp=SFTPClient.from_transport(t) sftp.get(r_path,l_path) t.close() # ssh = SSHClient() # ssh.set_missing_host_key_policy(AutoAddPolicy()) # ssh.connect(ip,22,uname,pw,look_for_keys=False,allow_agent=False) # t=ssh.get_transport() # sftp=SFTPClient.from_transport(t) # sftp.get(r_path,l_path) # t.close() except: print 'sftp %s failed' % (ip)
def upload_report_to_sftp(self, client_id, report_date, absolute_filename): """ Upload the given file, using SFTP, to the configured FTP server. The file should be uploaded to the appropriate directory for the specified client and the date of the report. """ try: client = Clients.objects.get(id=client_id) except Clients.DoesNotExist: logger.exception(u'No configuration for client {0}.'.format(client_id)) raise filename = basename(absolute_filename) base_folder, env_folder, year_folder, month_folder = self._get_sftp_dirs(client, report_date) try: logger.debug(u'SFTP logging on to {0} as {1}'.format(settings.SFTP_SERVER, settings.SFTP_USERNAME)) transport = Transport((settings.SFTP_SERVER, settings.SFTP_PORT)) transport.connect(username=settings.SFTP_USERNAME, password=settings.SFTP_PASSWORD) sftp = SFTPClient.from_transport(transport) logger.debug(u'SFTP dir {0}/{1}/{2}/{3}'.format(base_folder, env_folder, year_folder, month_folder)) sftp.chdir(base_folder) self._make_or_change_sftp_dir(sftp, env_folder) self._make_or_change_sftp_dir(sftp, year_folder) self._make_or_change_sftp_dir(sftp, month_folder) logger.debug(u'SFTP uploading {0}'.format(filename)) sftp.put(absolute_filename, filename) except Exception: logger.exception(u'Unrecoverable exception during SFTP upload process.') raise finally: logger.debug(u'SFTP logging off') try: sftp.close() except Exception: logger.exception(u'SFTP exception while closing SFTP session.') try: transport.close() except Exception: logger.exception(u'SFTP exception while closing SSH connection.')
class MyTSFTPRequestHandler(SocketServer.BaseRequestHandler): timeout = 60 auth_timeout = 60 def setup(self): self.transport = Transport(self.request) self.transport.load_server_moduli() so = self.transport.get_security_options() so.digests = ('hmac-sha1', ) so.compression = ('*****@*****.**', 'none') self.transport.add_server_key(self.server.host_key) self.transport.set_subsystem_handler( 'sftp', MyTSFTPServer, MyTSFTPServerInterface) def handle(self): self.transport.start_server(server=MyTServerInterface()) def handle_timeout(self): self.transport.close()
def __cacheIfRemote(self, importTitle, path): protocol = None for prefix in DataImportFactory.remoteProtocols: if path.startswith(prefix): protocol = prefix if not protocol: return path fileName = path.split("/").pop() outPath = os.path.join(self.__system.getConfig().getOutputPath(), "imports", "{0}_{1}".format(importTitle, fileName)) if not os.path.exists(os.path.dirname(outPath)): os.makedirs(os.path.dirname(outPath)) if not os.path.exists(outPath): self.__getLog().info("Downloading {0} to {1}".format( path, outPath)) if protocol == "sftp://": loginInfo, hostPath = path.split("@") userName, password = loginInfo.rsplit(":", 1) userName = userName.split(protocol).pop() hostName, remotePath = hostPath.split("/", 1) transport = Transport((hostName, 22)) transport.connect(username=userName, password=password) sftp = SFTPClient.from_transport(transport) sftp.get("/{0}".format(remotePath), outPath) transport.close() else: r = requests.get(path, stream=True) with open(outPath, "wb") as outFile: for chunk in r.iter_content(): outFile.write(chunk) else: self.__getLog().info("Using existing {0} for {1}".format( outPath, path)) return outPath
class MyTSFTPRequestHandler(SocketServer.BaseRequestHandler): timeout = 60 auth_timeout = 60 def setup(self): self.transport = Transport(self.request) self.transport.load_server_moduli() so = self.transport.get_security_options() so.digests = ('hmac-sha1', ) so.compression = ('*****@*****.**', 'none') self.transport.add_server_key(self.server.host_key) self.transport.set_subsystem_handler( 'sftp', MyTSFTPServer, MyTSFTPServerInterface) def handle(self): self.transport.start_server(server=MyTServerInterface()) def handle_timeout(self): try: self.transport.close() finally: super(MyTSFTPRequestHandler, self).handle_timeout()
def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip('\r\n').split(' ') try: self.data.version = banner[0] self.data.os = banner[1] except IndexError: pass s.send('{}\r\n'.format(banner[0])) self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() # use paramiko to get hostkey because of lazyless... tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data.pubkey_name = pubkey.get_name() fp = pubkey.get_fingerprint() self.data.pubkey_fingerprint = ':'.join( map(lambda x: x.encode('hex'), fp)) ServicesInfo.add(ip, port, 'ssh', self.data) except Exception as e: cprint(str(e), 'error') return None finally: tran.close() self.clear() return True
def run(self, ip, port=22, timeout=2): try: socket.setdefaulttimeout(timeout) s = socket.socket() s.connect((ip, port)) banner = s.recv(50).strip('\r\n').split(' ') try: self.data.version = banner[0] self.data.os = banner[1] except IndexError: pass s.send('{}\r\n'.format(banner[0])) self._raw_recv = s.recv(2048) s.close() self._parse_raw_data() # use paramiko to get hostkey because of lazyless... tran = Transport((ip, port)) tran.start_client() pubkey = tran.get_remote_server_key() self.data.pubkey_name = pubkey.get_name() fp = pubkey.get_fingerprint() self.data.pubkey_fingerprint = ':'.join(map(lambda x:x.encode('hex'), fp)) ServicesInfo.add(ip, port, 'ssh', self.data) except Exception as e: cprint(str(e), 'error') return None finally: tran.close() self.clear() return True
def handle_connection(self, host_key): try: DoGSSAPIKeyExchange = False t = Transport(self.sock, gss_kex=DoGSSAPIKeyExchange) t.set_subsystem_handler('netconf', self.subsys) t.set_gss_host(socket.getfqdn("")) try: t.load_server_moduli() except: Logger.warning('Failed to load moduli -- gex will be unsupported.') t.add_server_key(host_key) server = Server() t.start_server(server=server) # wait for auth self.channel = t.accept(20) if self.channel is None: Logger.error('No SSH channel') return Logger.info('Waiting for message') server.event.wait(10) Logger.info('Closing') ##self.channel.close() Logger.info('Client connection closed') except ConnectionResetError as e: Logger.debug(5,'Connection reset by peer') except SSHException: Logger.error('SSH negotiation failed, client connection dropped') except Exception as e: Logger.error('Caught exception: ' + str(e.__class__) + ': ' + str(e)) traceback.print_exc() try: t.close() except: pass
def main(): if len(sys.argv) < 2: print('Usage: {} <map file>'.format(sys.argv[0])) raise BaseException('') src = sys.argv[1] if not os.path.isfile(src): if src.startswith('mxupload:'): mx(src) return print('File not found {}'.format(src)) raise BaseException('') t = Transport((settings['host'], settings['port'])) print('Connecting') t.connect(username=settings['user'], pkey=paramiko.rsakey.RSAKey.from_private_key_file( settings['pkey'])) print('Connected') client = sftp.SFTPClient.from_transport(t) print('Uploading file') dst = settings['dest'] client.put(sys.argv[1], '{}/{}'.format(dst, os.path.basename(src))) print('Done') client.close() t.close()
class TransportTest(unittest.TestCase): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def setup_test_server(self, client_options=None, server_options=None): host_key = RSAKey.from_private_key_file(test_path('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) if client_options is not None: client_options(self.tc.get_security_options()) if server_options is not None: server_options(self.ts.get_security_options()) event = threading.Event() self.server = NullServer() self.assertTrue(not event.is_set()) self.ts.start_server(event, self.server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) def test_1_security_options(self): o = self.tc.get_security_options() self.assertEqual(type(o), SecurityOptions) self.assertTrue(('aes256-cbc', 'blowfish-cbc') != o.ciphers) o.ciphers = ('aes256-cbc', 'blowfish-cbc') self.assertEqual(('aes256-cbc', 'blowfish-cbc'), o.ciphers) try: o.ciphers = ('aes256-cbc', 'made-up-cipher') self.assertTrue(False) except ValueError: pass try: o.ciphers = 23 self.assertTrue(False) except TypeError: pass def test_2_compute_key(self): self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929 self.tc.H = b'\x0C\x83\x07\xCD\xE6\x85\x6F\xF3\x0B\xA9\x36\x84\xEB\x0F\x04\xC2\x52\x0E\x9E\xD3' self.tc.session_id = self.tc.H key = self.tc._compute_key('C', 32) self.assertEqual( b'207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995', hexlify(key).upper()) def test_3_simple(self): """ verify that we can establish an ssh link with ourselves across the loopback sockets. this is hardly "simple" but it's simpler than the later tests. :) """ host_key = RSAKey.from_private_key_file(test_path('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.assertEqual(None, self.tc.get_username()) self.assertEqual(None, self.ts.get_username()) self.assertEqual(False, self.tc.is_authenticated()) self.assertEqual(False, self.ts.is_authenticated()) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) self.assertEqual('slowdive', self.tc.get_username()) self.assertEqual('slowdive', self.ts.get_username()) self.assertEqual(True, self.tc.is_authenticated()) self.assertEqual(True, self.ts.is_authenticated()) def test_3a_long_banner(self): """ verify that a long banner doesn't mess up the handshake. """ host_key = RSAKey.from_private_key_file(test_path('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.socks.send(LONG_BANNER) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) def test_4_special(self): """ verify that the client can demand odd handshake settings, and can renegotiate keys in mid-stream. """ def force_algorithms(options): options.ciphers = ('aes256-cbc', ) options.digests = ('hmac-md5-96', ) self.setup_test_server(client_options=force_algorithms) self.assertEqual('aes256-cbc', self.tc.local_cipher) self.assertEqual('aes256-cbc', self.tc.remote_cipher) self.assertEqual(12, self.tc.packetizer.get_mac_size_out()) self.assertEqual(12, self.tc.packetizer.get_mac_size_in()) self.tc.send_ignore(1024) self.tc.renegotiate_keys() self.ts.send_ignore(1024) def test_5_keepalive(self): """ verify that the keepalive will be sent. """ self.setup_test_server() self.assertEqual(None, getattr(self.server, '_global_request', None)) self.tc.set_keepalive(1) time.sleep(2) self.assertEqual('*****@*****.**', self.server._global_request) def test_6_exec_command(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) try: chan.exec_command('no') self.assertTrue(False) except SSHException: pass chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('', f.readline()) f = chan.makefile_stderr() self.assertEqual('This is on stderr.\n', f.readline()) self.assertEqual('', f.readline()) # now try it with combined stdout/stderr chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() chan.set_combine_stderr(True) f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('This is on stderr.\n', f.readline()) self.assertEqual('', f.readline()) def test_6a_channel_can_be_used_as_context_manager(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() with self.tc.open_session() as chan: with self.ts.accept(1.0) as schan: chan.exec_command('yes') schan.send('Hello there.\n') schan.close() f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('', f.readline()) def test_7_invoke_shell(self): """ verify that invoke_shell() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) chan.send('communist j. cat\n') f = schan.makefile() self.assertEqual('communist j. cat\n', f.readline()) chan.close() self.assertEqual('', f.readline()) def test_8_channel_exception(self): """ verify that ChannelException is thrown for a bad open-channel request. """ self.setup_test_server() try: chan = self.tc.open_channel('bogus') self.fail('expected exception') except ChannelException as e: self.assertTrue(e.code == OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) def test_9_exit_status(self): """ verify that get_exit_status() works. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) chan.exec_command('yes') schan.send('Hello there.\n') self.assertTrue(not chan.exit_status_ready()) # trigger an EOF schan.shutdown_read() schan.shutdown_write() schan.send_exit_status(23) schan.close() f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('', f.readline()) count = 0 while not chan.exit_status_ready(): time.sleep(0.1) count += 1 if count > 50: raise Exception("timeout") self.assertEqual(23, chan.recv_exit_status()) chan.close() def test_A_select(self): """ verify that select() on a channel works. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.send('hello\n') # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(b'hello\n', chan.recv(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.close() # detect eof? for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(bytes(), chan.recv(16)) # make sure the pipe is still open for now... p = chan._pipe self.assertEqual(False, p._closed) chan.close() # ...and now is closed. self.assertEqual(True, p._closed) def test_B_renegotiate(self): """ verify that a transport can correctly renegotiate mid-stream. """ self.setup_test_server() self.tc.packetizer.REKEY_BYTES = 16384 chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) self.assertEqual(self.tc.H, self.tc.session_id) for i in range(20): chan.send('x' * 1024) chan.close() # allow a few seconds for the rekeying to complete for i in range(50): if self.tc.H != self.tc.session_id: break time.sleep(0.1) self.assertNotEqual(self.tc.H, self.tc.session_id) schan.close() def test_C_compression(self): """ verify that zlib compression is basically working. """ def force_compression(o): o.compression = ('zlib', ) self.setup_test_server(force_compression, force_compression) chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) bytes = self.tc.packetizer._Packetizer__sent_bytes chan.send('x' * 1024) bytes2 = self.tc.packetizer._Packetizer__sent_bytes # tests show this is actually compressed to *52 bytes*! including packet overhead! nice!! :) self.assertTrue(bytes2 - bytes < 1024) self.assertEqual(52, bytes2 - bytes) chan.close() schan.close() def test_D_x11(self): """ verify that an x11 port can be requested and opened. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) requested = [] def handler(c, addr_port): addr, port = addr_port requested.append((addr, port)) self.tc._queue_incoming_channel(c) self.assertEqual(None, getattr(self.server, '_x11_screen_number', None)) cookie = chan.request_x11(0, single_connection=True, handler=handler) self.assertEqual(0, self.server._x11_screen_number) self.assertEqual('MIT-MAGIC-COOKIE-1', self.server._x11_auth_protocol) self.assertEqual(cookie, self.server._x11_auth_cookie) self.assertEqual(True, self.server._x11_single_connection) x11_server = self.ts.open_x11_channel(('localhost', 6093)) x11_client = self.tc.accept() self.assertEqual('localhost', requested[0][0]) self.assertEqual(6093, requested[0][1]) x11_server.send('hello') self.assertEqual(b'hello', x11_client.recv(5)) x11_server.close() x11_client.close() chan.close() schan.close() def test_E_reverse_port_forwarding(self): """ verify that a client can ask the server to open a reverse port for forwarding. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) requested = [] def handler(c, origin_addr_port, server_addr_port): requested.append(origin_addr_port) requested.append(server_addr_port) self.tc._queue_incoming_channel(c) port = self.tc.request_port_forward('127.0.0.1', 0, handler) self.assertEqual(port, self.server._listen.getsockname()[1]) cs = socket.socket() cs.connect(('127.0.0.1', port)) ss, _ = self.server._listen.accept() sch = self.ts.open_forwarded_tcpip_channel(ss.getsockname(), ss.getpeername()) cch = self.tc.accept() sch.send('hello') self.assertEqual(b'hello', cch.recv(5)) sch.close() cch.close() ss.close() cs.close() # now cancel it. self.tc.cancel_port_forward('127.0.0.1', port) self.assertTrue(self.server._listen is None) def test_F_port_forwarding(self): """ verify that a client can forward new connections from a locally- forwarded port. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) # open a port on the "server" that the client will ask to forward to. greeting_server = socket.socket() greeting_server.bind(('127.0.0.1', 0)) greeting_server.listen(1) greeting_port = greeting_server.getsockname()[1] cs = self.tc.open_channel('direct-tcpip', ('127.0.0.1', greeting_port), ('', 9000)) sch = self.ts.accept(1.0) cch = socket.socket() cch.connect(self.server._tcpip_dest) ss, _ = greeting_server.accept() ss.send(b'Hello!\n') ss.close() sch.send(cch.recv(8192)) sch.close() self.assertEqual(b'Hello!\n', cs.recv(7)) cs.close() def test_G_stderr_select(self): """ verify that select() on a channel works even if only stderr is receiving data. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.send_stderr('hello\n') # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(b'hello\n', chan.recv_stderr(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.close() chan.close() def test_H_send_ready(self): """ verify that send_ready() indicates when a send would not block. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) self.assertEqual(chan.send_ready(), True) total = 0 K = '*' * 1024 limit = 1 + (64 * 2**15) while total < limit: chan.send(K) total += len(K) if not chan.send_ready(): break self.assertTrue(total < limit) schan.close() chan.close() self.assertEqual(chan.send_ready(), True) def test_I_rekey_deadlock(self): """ Regression test for deadlock when in-transit messages are received after MSG_KEXINIT is sent Note: When this test fails, it may leak threads. """ # Test for an obscure deadlocking bug that can occur if we receive # certain messages while initiating a key exchange. # # The deadlock occurs as follows: # # In the main thread: # 1. The user's program calls Channel.send(), which sends # MSG_CHANNEL_DATA to the remote host. # 2. Packetizer discovers that REKEY_BYTES has been exceeded, and # sets the __need_rekey flag. # # In the Transport thread: # 3. Packetizer notices that the __need_rekey flag is set, and raises # NeedRekeyException. # 4. In response to NeedRekeyException, the transport thread sends # MSG_KEXINIT to the remote host. # # On the remote host (using any SSH implementation): # 5. The MSG_CHANNEL_DATA is received, and MSG_CHANNEL_WINDOW_ADJUST is sent. # 6. The MSG_KEXINIT is received, and a corresponding MSG_KEXINIT is sent. # # In the main thread: # 7. The user's program calls Channel.send(). # 8. Channel.send acquires Channel.lock, then calls Transport._send_user_message(). # 9. Transport._send_user_message waits for Transport.clear_to_send # to be set (i.e., it waits for re-keying to complete). # Channel.lock is still held. # # In the Transport thread: # 10. MSG_CHANNEL_WINDOW_ADJUST is received; Channel._window_adjust # is called to handle it. # 11. Channel._window_adjust tries to acquire Channel.lock, but it # blocks because the lock is already held by the main thread. # # The result is that the Transport thread never processes the remote # host's MSG_KEXINIT packet, because it becomes deadlocked while # handling the preceding MSG_CHANNEL_WINDOW_ADJUST message. # We set up two separate threads for sending and receiving packets, # while the main thread acts as a watchdog timer. If the timer # expires, a deadlock is assumed. class SendThread(threading.Thread): def __init__(self, chan, iterations, done_event): threading.Thread.__init__(self, None, None, self.__class__.__name__) self.setDaemon(True) self.chan = chan self.iterations = iterations self.done_event = done_event self.watchdog_event = threading.Event() self.last = None def run(self): try: for i in range(1, 1 + self.iterations): if self.done_event.is_set(): break self.watchdog_event.set() #print i, "SEND" self.chan.send("x" * 2048) finally: self.done_event.set() self.watchdog_event.set() class ReceiveThread(threading.Thread): def __init__(self, chan, done_event): threading.Thread.__init__(self, None, None, self.__class__.__name__) self.setDaemon(True) self.chan = chan self.done_event = done_event self.watchdog_event = threading.Event() def run(self): try: while not self.done_event.is_set(): if self.chan.recv_ready(): chan.recv(65536) self.watchdog_event.set() else: if random.randint(0, 1): time.sleep(random.randint(0, 500) / 1000.0) finally: self.done_event.set() self.watchdog_event.set() self.setup_test_server() self.ts.packetizer.REKEY_BYTES = 2048 chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) # Monkey patch the client's Transport._handler_table so that the client # sends MSG_CHANNEL_WINDOW_ADJUST whenever it receives an initial # MSG_KEXINIT. This is used to simulate the effect of network latency # on a real MSG_CHANNEL_WINDOW_ADJUST message. self.tc._handler_table = self.tc._handler_table.copy( ) # copy per-class dictionary _negotiate_keys = self.tc._handler_table[MSG_KEXINIT] def _negotiate_keys_wrapper(self, m): if self.local_kex_init is None: # Remote side sent KEXINIT # Simulate in-transit MSG_CHANNEL_WINDOW_ADJUST by sending it # before responding to the incoming MSG_KEXINIT. m2 = Message() m2.add_byte(cMSG_CHANNEL_WINDOW_ADJUST) m2.add_int(chan.remote_chanid) m2.add_int(1) # bytes to add self._send_message(m2) return _negotiate_keys(self, m) self.tc._handler_table[MSG_KEXINIT] = _negotiate_keys_wrapper # Parameters for the test iterations = 500 # The deadlock does not happen every time, but it # should after many iterations. timeout = 5 # This event is set when the test is completed done_event = threading.Event() # Start the sending thread st = SendThread(schan, iterations, done_event) st.start() # Start the receiving thread rt = ReceiveThread(chan, done_event) rt.start() # Act as a watchdog timer, checking deadlocked = False while not deadlocked and not done_event.is_set(): for event in (st.watchdog_event, rt.watchdog_event): event.wait(timeout) if done_event.is_set(): break if not event.is_set(): deadlocked = True break event.clear() # Tell the threads to stop (if they haven't already stopped). Note # that if one or more threads are deadlocked, they might hang around # forever (until the process exits). done_event.set() # Assertion: We must not have detected a timeout. self.assertFalse(deadlocked) # Close the channels schan.close() chan.close() def test_J_sanitze_packet_size(self): """ verify that we conform to the rfc of packet and window sizes. """ for val, correct in [(4095, MIN_PACKET_SIZE), (None, DEFAULT_MAX_PACKET_SIZE), (2**32, MAX_WINDOW_SIZE)]: self.assertEqual(self.tc._sanitize_packet_size(val), correct) def test_K_sanitze_window_size(self): """ verify that we conform to the rfc of packet and window sizes. """ for val, correct in [(32767, MIN_WINDOW_SIZE), (None, DEFAULT_WINDOW_SIZE), (2**32, MAX_WINDOW_SIZE)]: self.assertEqual(self.tc._sanitize_window_size(val), correct) def test_L_handshake_timeout(self): """ verify that we can get a hanshake timeout. """ host_key = RSAKey.from_private_key_file(test_path('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.tc.handshake_timeout = 0.000000000001 self.ts.start_server(event, server) self.assertRaises(EOFError, self.tc.connect, hostkey=public_host_key, username='******', password='******')
class Transporter(object): def __init__(self, \ timeout, \ remoteMachine, \ port, \ username, \ password, \ loggerName, \ pk, \ bufsize = 8192): import logging self.logger = logging.getLogger(loggerName) self.timeout = timeout self.remoteMachine = remoteMachine self.port = port self.username = username self.password = password self.bufsize = bufsize self.pk = DSSKey.from_private_key_file(expanduser(pk)) self.logger.info('Object succesfully created') self.logger.debug ('timeout = %s, remoteMachine = %s, port = %s, username = %s'%(self.timeout, self.remoteMachine, self.port, self.username)) #util.log_to_file('paramiko.log') def Link(self,\ sftp=None,\ ssh=True): self.sock = socket() self.sock.settimeout(self.timeout) self.Errors = [] self.protocol = [] self.logger.info('Trying to start link using parameters [SSH=%s,SFTP=%s]'%(ssh,sftp)) try: self.sock.connect((self.remoteMachine, self.port)) except: self.Unlink() self.logger.error('Link: Could not open a socket [remoteMachine = %s, port = %s]' % (self.remoteMachine, self.port)) self.transport = Transport(self.sock) try: if not self.password: self.transport.connect(username = self.username, pkey = self.pk) self.logger.info('Trying to connect using private key..') else: self.transport.connect(username = self.username, password = self.password) self.logger.info('Trying to connect using password..') except: if not self.password: self.logger.error('Private Key not installed and no password specified!') self.Unlink() self.logger.error('Could not connect using SFTP [remoteMachine = %s, Username = %s, port = %s]' %(self.remoteMachine, self.username, self.port)) sftp=None if ssh: try: self.protocol.append(mySSHClient()) self.ssh = self.protocol[-1] self.ssh.set_missing_host_key_policy(AutoAddPolicy()) self.ssh.connect(self.remoteMachine, port = self.port, username = self.username, password = self.password) self.logger.info ('ssh connection created..') except: self.logger.error('Could not connect using SSH [remoteMachine = %s, Username = %s, port = %s]'%(self.remoteMachine, self.username, self.port)) if sftp: try: if ssh == None: self.logger.error ('SSH must be enabled in order to connect with SFTP') self.protocol.append(SFTPClient.from_transport(self.transport)) self.sftp = self.protocol[-1] self.client = self.sftp self.logger.info('sftp connection created..') except: self.logger.error ('Could not bring up SFTP connection') self.sftp=None return ssh, sftp def RemoteCommand (self, command, check_exit=True, OSD=None): try: if not self.ssh: raise Exception, 'SSH must be enabled on remote machine in order to run commands.' remoteHost = self.ssh if not command is 'close_connection': self.command = command stdin, self.output, stderr = remoteHost.exec_command(self.command, timeout=40) output_lines = self.output.readlines() fprint=''.join(output_lines) if OSD: print fprint #On Screen Display formatted output self.logger.debug('output of remote command `%s` on [%s]: %s' %(self.command,\ self.remoteMachine,\ fprint)) if check_exit: errorList = stderr.readlines() if len(errorList) !=0: self.logger.error(errorList) raise Exception, 'Failed to execute command %s on [%s]' %(self.command, \ self.remoteMachine) else: self.logger.info('Command `%s` on [%s] completed successfuly'%(self.command,\ self.remoteMachine)) else: try: self.logger.info ('Trying to close connections..') self.Unlink() self.logger.info ('Connections closed succesfully..') except: self.logger.error('Could not close connections! ' ) return output_lines except: raise Exception, 'Could not complete the command %s'%(self.command) #raise def CopyToRemote(self, \ localFolder, \ remoteFolder, \ fileName, \ copyProtocol='scp', \ scpCommand='scp -o ConnectTimeout=30 -p -P', \ response = True): ''' Gets: localFolder, remoteFolder, fileName Returns: Response. ''' localFilePath = pJoin(localFolder, fileName) remoteFilePath = pJoin(remoteFolder, fileName) if copyProtocol == 'sftp': try: self.sftp.put(localFilePath, remoteFilePath) self.logger.info ('Copied file %s to %s:%s'%(localFilePath, self.remoteMachine, remoteFilePath)) except AttributeError: self.logger.error('SFTP connection is not open') response = False except: raise Exception, 'Could not connect using SFTP' return response elif copyProtocol == 'scp': try: scpCopy=getstatusoutput('%s %s %s %s@%s:%s '%(self.scpCommand, self.port, localFilePath, self.username, \ self.remoteMachine, remoteFilePath)) if scpCopy[0] != 0: self.logger.error (scpCopy[1]) self.logger.error('Error %s: %s'%(scpCopy[0],self.Errors)) response = False else: self.logger.info('Copied file from "%s" to "%s:%s" successfuly' % (localFilePath, \ self.remoteMachine, remoteFilePath)) except: self.logger.error('Could not transfer file %s to [%s] using SCP'%(remoteFilePath,self.remoteMachine)) response = False else: raise Exception, 'Unknown copy protocol "%s", scp and sftp support only'%copyProtocol response = False return response def RenameLocalFile (self, localFolder, OrigFileName, NewFileName): self.logger.info ('Renaming %s to %s'%(OrigFileName, NewFileName)) localFilePath = pJoin(localFolder, OrigFileName) renamedFilePath = pJoin(localFolder, NewFileName) try: rename (localFilePath, renamedFilePath) except: raise Exception, 'Could not rename file %s to %s'%(localFilePath, renamedFilePath) def Unlink(self): try: self.ssh.close() except: self.logger.error('No SSH client to close...') try: self.sftp.close() except: self.logger.error('No SFTP client to close...') try: self.transport.close() except: self.logger.error('No transport to close...') try: self.sock.close() except: self.logger('No socket to close...') def dotTmp(self, \ localFileFolder, \ localFileName, \ remoteFileFolder, \ remoteFileName, \ index, \ runType = 'put'): ''' Gets: localFileFolder, localFileName, remoteFileFolder, remoteFileName, index, runType. Returns: True ''' self.Errors = [] response = True localFilePath = pJoin(localFileFolder, localFileName) remoteTempFilePath = pJoin(remoteFileFolder, '%s.%s.tmp' % (remoteFileName, index)) remoteFilePath = pJoin(remoteFileFolder, '%s.%s' % (remoteFileName, index)) try: if runType == 'put': self.client.put(localFilePath, remoteTempFilePath) elif runType == 'open': self._WriteLocalDataToRemoteFile(localFilePath, remoteTempFilePath) except: self.logger.error('Failed creating "%s" on the remote server "%s"' % (remoteFilePath, self.remoteMachine)) response = False try: self.client.remove(remoteFilePath) except: pass try: self.client.rename(remoteTempFilePath, remoteFilePath) except: self.logger.error('Failed moving "%s" to "%s" on the remote server "%s"' % (remoteTempFilePath, remoteFilePath, self.remoteMachine)) response = False if len(self.Errors) != 0: self.logger.error(self.Errors) return response def MD5Compare (self, MD5File, localFolder, DPIFileType, DateTimeFormat='%Y%m%d%H%M%S', Collect=True, Rename=True, PRX_MD5Command = 'list-files', copyProtocol='scp'): # ''' # Gets: a file name made from pickle. # a local folder where to files should be saved. # Returns: a list of changed files. # ''' CurrentTimeDate=datetime.now().strftime(DateTimeFormat) listedFiles = self.GetMD5FromPrx(rqstType = DPIFileType, md5cmd = PRX_MD5Command) CurrentMD5Dict = {self.remoteMachine : listedFiles} if not path.exists(MD5File): # MD5File does not exist LastSavedDict = {} else: LastSavedDict = self._readMD5fromFile(MD5File) if LastSavedDict.get(self.remoteMachine, 0) == 0: # Machine not found in MD5File (First time accessing) LocalSavedDict = {} for ftype in DPIFileType: LocalSavedDict[ftype] = {} else: LocalSavedDict = LastSavedDict[self.remoteMachine] DPIMD5Dict = CurrentMD5Dict[self.remoteMachine] self.logger.debug ('Local Saved Files before copy = %s'%LocalSavedDict) if LocalSavedDict == DPIMD5Dict: ## If there was not change in files. self.logger.info('All files are up to date. No files copied') else: changedFiles = [] for ftype in DPIFileType: for fileName, md5 in DPIMD5Dict.get(ftype, {}).iteritems(): if md5 != LocalSavedDict.get(ftype, {}).get(fileName, ''): #if LocalSavedDict.get(ftype, {}).get(md5, None): if Collect: self.Collector(localFolder=localFolder, remoteFolder=ftype, copyProtocol=copyProtocol,collectedFileName=fileName,collectedSuffix='',\ getLatestOnly = False, removeRemoteFiles = False) if Rename: (name, ext) = path.splitext(fileName) self.RenameLocalFile(OrigFileName = fileName, NewFileName = name+'.'+self.remoteMachine+'.'+CurrentTimeDate+ext+'.report', \ localFolder=localFolder) if ftype not in LocalSavedDict: LocalSavedDict[ftype] = {} LocalSavedDict[ftype][fileName] = md5 changedFiles.append(fileName) LastSavedDict[self.remoteMachine] = LocalSavedDict self.logger.debug('Local Saved Files after copy = %s'%LocalSavedDict) self._writeMD5toFile(MD5File,LastSavedDict) def CreatePrxStat(self, remoteMachine, localFolder, DPI_Statistics, filename, Prefix, Suffix, cmdTimeout, DateTimeFormat='%Y%m%d%H%M%S'): ''' Create statistic file from command (list-protocols --statistics) ''' CurrentTimeDate=datetime.now().strftime(DateTimeFormat) command = self.RemoteCommand(DPI_Statistics, cmdTimeout) timestamp = int(time.time()) try: f = '%s-%s.%s.%s%s' % (Prefix, filename, remoteMachine, CurrentTimeDate, Suffix) statfile = open(localFolder + f, 'w') #statfile.write('timestamp: ' + str(timestamp) + os.linesep) #statfile.write('system-id: ' + remoteMachine + os.linesep) statfile.write('timestamp: ' + str(timestamp) + '\n') statfile.write('system-id: ' + remoteMachine + '\n') for linestat in command: statfile.write(linestat) statfile.close() except: self.logger.error('Failed to Create %s file' % f) raise def dotDone(self, \ localFileFolder, \ localFileName, \ remoteFileFolder, \ remoteFileName, \ doneFileFolder, \ doneFileName, \ doneFileSuffix, \ index, \ runType = 'put'): ''' Gets: localFileFolder, localFileName, remoteFileFolder, remoteFileName, doneFileFolder, doneFileName, doneFileSuffix, index, runType. Returns: True ''' self.Errors = [] response = True localFilePath = pJoin(localFileFolder, localFileName) doneFilePath = pJoin(doneFileFolder, '%s.%s.%s' % (doneFileName, doneFileSuffix, index)) remoteFilePath = pJoin(remoteFileFolder, '%s.%s' % (remoteFileName, index)) remoteDoneFilePath = pJoin(doneFileFolder, '%s.delta.%s' % (remoteFileName, index)) try: if runType == 'put': self.client.put(localFilePath, remoteFilePath) self.client.put(localFilePath, remoteDoneFilePath) elif runType == 'open': self._WriteLocalDataToRemoteFile(localFilePath, remoteFilePath) self._WriteLocalDataToRemoteFile(localFilePath, remoteDoneFilePath) except: self.logger.error('Failed creating "%s" on the remote server "%s"' % (remoteFilePath, self.remoteMachine)) response = False try: self.client.open(doneFilePath, 'w').close() except: self.logger.error('Failed creating done file "%s" on the remote server "%s"' % (doneFilePath, self.remoteMachine)) response = False if len(self.Errors) != 0: self.logger.error(self.Errors) return response def Collector(self, \ localFolder, \ remoteFolder, \ copyProtocol = 'sftp', \ collectedFileName = '', \ collectedSuffix = '', \ getLatestOnly = True, \ removeRemoteFiles = True): ''' Gets: localFolder, remoteFolder, collectedFileName, collectedSuffix, getLatestOnly, removeRemoteFiles. Returns: True ''' self.Errors = [] response = True CollectedFiles = [] RemoteFilesDict = {} if copyProtocol == 'sftp': if collectedFileName != '' and collectedSuffix == '': CollectedFiles.append(collectedFileName) remoteFilePath = pJoin(remoteFolder, collectedFileName) try: RemoteFilesDict[0] = self.client.stat(remoteFilePath) self.logger.debug ('Collected file name: %s'%(collectedFileName)) except: self.logger.error ('Could not get file stat for %s'%(collectedFileName)) elif collectedSuffix != '' and collectedFileName == '': RemoteFiles = self.client.listdir(remoteFolder) self.logger.debug ('Files in remote dir: %s'%(RemoteFiles)) try: suffix = search('(.*)(\.)(.*)', collectedSuffix).group(3) self.logger.debug ('Filtered Suffix: %s'%(suffix)) except: self.logger.error('Invalid files suffix given: %s' % collectedSuffix) response = False for remoteFile in RemoteFiles: try: search('\.%s$' % suffix, remoteFile).group() CollectedFiles.append(remoteFile) remoteFilePath = pJoin(remoteFolder, remoteFile) RemoteFilesDict[remoteFile] = self.client.stat(remoteFilePath) except: pass #if remoteFile.endswith(collectedSuffix): # CollectedFiles.append(remoteFile) # remoteFilePath = pJoin(remoteFolder, remoteFile) # RemoteFilesDict[remoteFile] = self.client.stat(remoteFilePath) self.logger.debug ('Remote Files Dic: %s'%RemoteFilesDict) else: raise Exception, 'No remote file name or suffix specified' response = False if getLatestOnly == True and len(RemoteFilesDict) != 0: latestRemoteFile = self._GetLatestFile(RemoteFilesDict) self._GetRemoteFile(localFolder, remoteFolder, latestRemoteFile, copyProtocol) if removeRemoteFiles == True: self._RemoveRemoteFile(remoteFolder, latestRemoteFile) elif getLatestOnly == False and len(RemoteFilesDict) != 0: for collectedFile in CollectedFiles: try: self._GetRemoteFile(localFolder, remoteFolder, collectedFile, copyProtocol=copyProtocol) except: self.logger.error('Failed getting "%s" from remote host "%s" to local folder "%s"' \ % (pJoin(remoteFolder, collectedFile), self.remoteMachine, localFolder)) response = False if removeRemoteFiles == True: self._RemoveRemoteFile(remoteFolder, collectedFile) else: response = False self.logger.debug ('%s'%(len(RemoteFilesDict))) return response elif copyProtocol == 'scp': if getLatestOnly == True: self.logger.error ('Get latest only is not supported when using scp') getLatestOnly == False if removeRemoteFiles == True: self.logger.error ('Remote file removal is not supported when using scp') removeRemoteFiles == False try: self._GetRemoteFile(localFolder, remoteFolder, collectedFileName, copyProtocol=copyProtocol) except: response = False raise Exception, ('Could not get file %s from %s to %s'%(collectedFileName, remoteFolder, localFolder)) return response def _writeMD5toFile(self, MD5File, dictionary={}): ## Write MD5 dictionary to file try: MD5ToSave = open (MD5File, 'wb') pickle.dump(dictionary, MD5ToSave) MD5ToSave.close() return MD5ToSave except: self.logger.error ('could not write MD5 file:\n%s:%s'%(sys.exc_info()[0],sys.exc_info()[1])) def _readMD5fromFile (self, MD5File): ## Read last saved MD5 dictionary from file. LastSavedFile = open (MD5File,'rb') SavedMD5Dict = pickle.load(LastSavedFile) LastSavedFile.close() return SavedMD5Dict def _WriteLocalDataToRemoteFile(self, localFilePath, remoteFilePath): ''' Gets: localFilePath, remoteFilePath. Returns: N/A ''' fh = open(localFilePath, 'r') Lines = fh.readlines() fh.close() rfh = self.client.open(filename = remoteFilePath, mode = 'w', bufsize = self.bufsize) for line in Lines: rfh.write(line) rfh.close() def _GetRemoteFile(self, localFolder, remoteFolder, collectedFileName, copyProtocol): ''' Gets: localFolder, remoteFolder, collectedFileName. Returns: N/A ''' remoteFilePath = pJoin(remoteFolder, collectedFileName) localFilePath = pJoin(localFolder, collectedFileName) if copyProtocol == 'sftp': try: self.client.get(remoteFilePath, localFilePath) self.logger.info('Copied file from "%s:%s" to "%s" successfuly' % (self.remoteMachine, remoteFilePath,\ localFilePath)) except: self.logger.error('Could not get file %s from [%s] using SFTP'%(remoteFilePath,self.remoteMachine)) elif copyProtocol == 'scp': scpCopy=getstatusoutput('scp -o ConnectTimeout=5 -p -P %s %s@%s:%s %s'%(self.port, self.username, self.remoteMachine, \ remoteFilePath, localFilePath)) if scpCopy[0] != 0: self.logger.error (scpCopy[1]) self.logger.error('Could not get file %s from [%s] using SCP'%(remoteFilePath,self.remoteMachine)) self.logger.error(self.Errors) print ('%s'%self.Errors[0]) else: self.logger.info('Copied file from "%s:%s" to "%s" successfuly' % (self.remoteMachine, \ remoteFilePath, localFilePath)) else: self.logger.error('Unknown copy protocol %s, scp and sftp support only'%copyProtocol) def GetMD5FromPrx (self, rqstType, md5cmd='list-files'): try: fileType = {} for r in rqstType: fileType[r] = {} command = self.RemoteCommand(md5cmd) found = False ignoreFileType = True for line in command: lineList = line.split() for fType in fileType: #.keys(): if fType == line[1:].strip()[:-1]: if fType not in rqstType: ignoreFileType = True else: ignoreFileType = False type = fType found = True break else: found = False if found: continue if lineList and not ignoreFileType: try: #fileType[type].update({lineList[-2] : lineList[-1]}) ## {md5:file} fileType[type].update({lineList[-1] : lineList[-2]}) ## {file:md5} except IndexError: ignoreFileType = True self.logger.debug ('File Dictionary on PRX %s'%fileType) return fileType except: self.logger.error ('Could not get md5cmd from PRX: %s:%s'%(sys.exc_info()[0],sys.exc_info()[1])) def _GetLatestFile(self, SftpClientFilesDict): ''' Gets: SftpClientFilesDict. Returns: latestFileName. ''' latest = 0 for sftpClientFile in SftpClientFilesDict.iterkeys(): if SftpClientFilesDict[sftpClientFile].st_mtime > latest: latestFileName = sftpClientFile latest = SftpClientFilesDict[sftpClientFile].st_mtime return latestFileName def _RemoveRemoteFile(self, remoteFileFolder, remoteFile): ''' Gets: remoteFileFolder, remoteFile. Returns: N/A. ''' remoteFilePath = pJoin(remoteFileFolder, remoteFile) try: self.client.remove(remoteFilePath) self.logger.info('Removing remote file "%s"' % remoteFilePath) except: raise Exception, 'Failed removing remote file "%s"...' % remoteFilePath
class TransportTest(unittest.TestCase): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def setup_test_server( self, client_options=None, server_options=None, connect_kwargs=None, ): host_key = RSAKey.from_private_key_file(_support('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) if client_options is not None: client_options(self.tc.get_security_options()) if server_options is not None: server_options(self.ts.get_security_options()) event = threading.Event() self.server = NullServer() self.assertTrue(not event.is_set()) self.ts.start_server(event, self.server) if connect_kwargs is None: connect_kwargs = dict( hostkey=public_host_key, username='******', password='******', ) self.tc.connect(**connect_kwargs) event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) def test_1_security_options(self): o = self.tc.get_security_options() self.assertEqual(type(o), SecurityOptions) self.assertTrue(('aes256-cbc', 'blowfish-cbc') != o.ciphers) o.ciphers = ('aes256-cbc', 'blowfish-cbc') self.assertEqual(('aes256-cbc', 'blowfish-cbc'), o.ciphers) try: o.ciphers = ('aes256-cbc', 'made-up-cipher') self.assertTrue(False) except ValueError: pass try: o.ciphers = 23 self.assertTrue(False) except TypeError: pass def test_1b_security_options_reset(self): o = self.tc.get_security_options() # should not throw any exceptions o.ciphers = o.ciphers o.digests = o.digests o.key_types = o.key_types o.kex = o.kex o.compression = o.compression def test_2_compute_key(self): self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929 self.tc.H = b'\x0C\x83\x07\xCD\xE6\x85\x6F\xF3\x0B\xA9\x36\x84\xEB\x0F\x04\xC2\x52\x0E\x9E\xD3' self.tc.session_id = self.tc.H key = self.tc._compute_key('C', 32) self.assertEqual(b'207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995', hexlify(key).upper()) def test_3_simple(self): """ verify that we can establish an ssh link with ourselves across the loopback sockets. this is hardly "simple" but it's simpler than the later tests. :) """ host_key = RSAKey.from_private_key_file(_support('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.assertEqual(None, self.tc.get_username()) self.assertEqual(None, self.ts.get_username()) self.assertEqual(False, self.tc.is_authenticated()) self.assertEqual(False, self.ts.is_authenticated()) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) self.assertEqual('slowdive', self.tc.get_username()) self.assertEqual('slowdive', self.ts.get_username()) self.assertEqual(True, self.tc.is_authenticated()) self.assertEqual(True, self.ts.is_authenticated()) def test_3a_long_banner(self): """ verify that a long banner doesn't mess up the handshake. """ host_key = RSAKey.from_private_key_file(_support('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.socks.send(LONG_BANNER) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) def test_4_special(self): """ verify that the client can demand odd handshake settings, and can renegotiate keys in mid-stream. """ def force_algorithms(options): options.ciphers = ('aes256-cbc',) options.digests = ('hmac-md5-96',) self.setup_test_server(client_options=force_algorithms) self.assertEqual('aes256-cbc', self.tc.local_cipher) self.assertEqual('aes256-cbc', self.tc.remote_cipher) self.assertEqual(12, self.tc.packetizer.get_mac_size_out()) self.assertEqual(12, self.tc.packetizer.get_mac_size_in()) self.tc.send_ignore(1024) self.tc.renegotiate_keys() self.ts.send_ignore(1024) @slow def test_5_keepalive(self): """ verify that the keepalive will be sent. """ self.setup_test_server() self.assertEqual(None, getattr(self.server, '_global_request', None)) self.tc.set_keepalive(1) time.sleep(2) self.assertEqual('*****@*****.**', self.server._global_request) def test_6_exec_command(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) try: chan.exec_command(b'command contains \xfc and is not a valid UTF-8 string') self.assertTrue(False) except SSHException: pass chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('', f.readline()) f = chan.makefile_stderr() self.assertEqual('This is on stderr.\n', f.readline()) self.assertEqual('', f.readline()) # now try it with combined stdout/stderr chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() chan.set_combine_stderr(True) f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('This is on stderr.\n', f.readline()) self.assertEqual('', f.readline()) def test_6a_channel_can_be_used_as_context_manager(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() with self.tc.open_session() as chan: with self.ts.accept(1.0) as schan: chan.exec_command('yes') schan.send('Hello there.\n') schan.close() f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('', f.readline()) def test_7_invoke_shell(self): """ verify that invoke_shell() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) chan.send('communist j. cat\n') f = schan.makefile() self.assertEqual('communist j. cat\n', f.readline()) chan.close() self.assertEqual('', f.readline()) def test_8_channel_exception(self): """ verify that ChannelException is thrown for a bad open-channel request. """ self.setup_test_server() try: chan = self.tc.open_channel('bogus') self.fail('expected exception') except ChannelException as e: self.assertTrue(e.code == OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) def test_9_exit_status(self): """ verify that get_exit_status() works. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) chan.exec_command('yes') schan.send('Hello there.\n') self.assertTrue(not chan.exit_status_ready()) # trigger an EOF schan.shutdown_read() schan.shutdown_write() schan.send_exit_status(23) schan.close() f = chan.makefile() self.assertEqual('Hello there.\n', f.readline()) self.assertEqual('', f.readline()) count = 0 while not chan.exit_status_ready(): time.sleep(0.1) count += 1 if count > 50: raise Exception("timeout") self.assertEqual(23, chan.recv_exit_status()) chan.close() def test_A_select(self): """ verify that select() on a channel works. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.send('hello\n') # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(b'hello\n', chan.recv(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.close() # detect eof? for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(bytes(), chan.recv(16)) # make sure the pipe is still open for now... p = chan._pipe self.assertEqual(False, p._closed) chan.close() # ...and now is closed. self.assertEqual(True, p._closed) def test_B_renegotiate(self): """ verify that a transport can correctly renegotiate mid-stream. """ self.setup_test_server() self.tc.packetizer.REKEY_BYTES = 16384 chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) self.assertEqual(self.tc.H, self.tc.session_id) for i in range(20): chan.send('x' * 1024) chan.close() # allow a few seconds for the rekeying to complete for i in range(50): if self.tc.H != self.tc.session_id: break time.sleep(0.1) self.assertNotEqual(self.tc.H, self.tc.session_id) schan.close() def test_C_compression(self): """ verify that zlib compression is basically working. """ def force_compression(o): o.compression = ('zlib',) self.setup_test_server(force_compression, force_compression) chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) bytes = self.tc.packetizer._Packetizer__sent_bytes chan.send('x' * 1024) bytes2 = self.tc.packetizer._Packetizer__sent_bytes block_size = self.tc._cipher_info[self.tc.local_cipher]['block-size'] mac_size = self.tc._mac_info[self.tc.local_mac]['size'] # tests show this is actually compressed to *52 bytes*! including packet overhead! nice!! :) self.assertTrue(bytes2 - bytes < 1024) self.assertEqual(16 + block_size + mac_size, bytes2 - bytes) chan.close() schan.close() def test_D_x11(self): """ verify that an x11 port can be requested and opened. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) requested = [] def handler(c, addr_port): addr, port = addr_port requested.append((addr, port)) self.tc._queue_incoming_channel(c) self.assertEqual(None, getattr(self.server, '_x11_screen_number', None)) cookie = chan.request_x11(0, single_connection=True, handler=handler) self.assertEqual(0, self.server._x11_screen_number) self.assertEqual('MIT-MAGIC-COOKIE-1', self.server._x11_auth_protocol) self.assertEqual(cookie, self.server._x11_auth_cookie) self.assertEqual(True, self.server._x11_single_connection) x11_server = self.ts.open_x11_channel(('localhost', 6093)) x11_client = self.tc.accept() self.assertEqual('localhost', requested[0][0]) self.assertEqual(6093, requested[0][1]) x11_server.send('hello') self.assertEqual(b'hello', x11_client.recv(5)) x11_server.close() x11_client.close() chan.close() schan.close() def test_E_reverse_port_forwarding(self): """ verify that a client can ask the server to open a reverse port for forwarding. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) requested = [] def handler(c, origin_addr_port, server_addr_port): requested.append(origin_addr_port) requested.append(server_addr_port) self.tc._queue_incoming_channel(c) port = self.tc.request_port_forward('127.0.0.1', 0, handler) self.assertEqual(port, self.server._listen.getsockname()[1]) cs = socket.socket() cs.connect(('127.0.0.1', port)) ss, _ = self.server._listen.accept() sch = self.ts.open_forwarded_tcpip_channel(ss.getsockname(), ss.getpeername()) cch = self.tc.accept() sch.send('hello') self.assertEqual(b'hello', cch.recv(5)) sch.close() cch.close() ss.close() cs.close() # now cancel it. self.tc.cancel_port_forward('127.0.0.1', port) self.assertTrue(self.server._listen is None) def test_F_port_forwarding(self): """ verify that a client can forward new connections from a locally- forwarded port. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) # open a port on the "server" that the client will ask to forward to. greeting_server = socket.socket() greeting_server.bind(('127.0.0.1', 0)) greeting_server.listen(1) greeting_port = greeting_server.getsockname()[1] cs = self.tc.open_channel('direct-tcpip', ('127.0.0.1', greeting_port), ('', 9000)) sch = self.ts.accept(1.0) cch = socket.socket() cch.connect(self.server._tcpip_dest) ss, _ = greeting_server.accept() ss.send(b'Hello!\n') ss.close() sch.send(cch.recv(8192)) sch.close() self.assertEqual(b'Hello!\n', cs.recv(7)) cs.close() def test_G_stderr_select(self): """ verify that select() on a channel works even if only stderr is receiving data. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.send_stderr('hello\n') # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(b'hello\n', chan.recv_stderr(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.close() chan.close() def test_H_send_ready(self): """ verify that send_ready() indicates when a send would not block. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) self.assertEqual(chan.send_ready(), True) total = 0 K = '*' * 1024 limit = 1+(64 * 2 ** 15) while total < limit: chan.send(K) total += len(K) if not chan.send_ready(): break self.assertTrue(total < limit) schan.close() chan.close() self.assertEqual(chan.send_ready(), True) def test_I_rekey_deadlock(self): """ Regression test for deadlock when in-transit messages are received after MSG_KEXINIT is sent Note: When this test fails, it may leak threads. """ # Test for an obscure deadlocking bug that can occur if we receive # certain messages while initiating a key exchange. # # The deadlock occurs as follows: # # In the main thread: # 1. The user's program calls Channel.send(), which sends # MSG_CHANNEL_DATA to the remote host. # 2. Packetizer discovers that REKEY_BYTES has been exceeded, and # sets the __need_rekey flag. # # In the Transport thread: # 3. Packetizer notices that the __need_rekey flag is set, and raises # NeedRekeyException. # 4. In response to NeedRekeyException, the transport thread sends # MSG_KEXINIT to the remote host. # # On the remote host (using any SSH implementation): # 5. The MSG_CHANNEL_DATA is received, and MSG_CHANNEL_WINDOW_ADJUST is sent. # 6. The MSG_KEXINIT is received, and a corresponding MSG_KEXINIT is sent. # # In the main thread: # 7. The user's program calls Channel.send(). # 8. Channel.send acquires Channel.lock, then calls Transport._send_user_message(). # 9. Transport._send_user_message waits for Transport.clear_to_send # to be set (i.e., it waits for re-keying to complete). # Channel.lock is still held. # # In the Transport thread: # 10. MSG_CHANNEL_WINDOW_ADJUST is received; Channel._window_adjust # is called to handle it. # 11. Channel._window_adjust tries to acquire Channel.lock, but it # blocks because the lock is already held by the main thread. # # The result is that the Transport thread never processes the remote # host's MSG_KEXINIT packet, because it becomes deadlocked while # handling the preceding MSG_CHANNEL_WINDOW_ADJUST message. # We set up two separate threads for sending and receiving packets, # while the main thread acts as a watchdog timer. If the timer # expires, a deadlock is assumed. class SendThread(threading.Thread): def __init__(self, chan, iterations, done_event): threading.Thread.__init__(self, None, None, self.__class__.__name__) self.setDaemon(True) self.chan = chan self.iterations = iterations self.done_event = done_event self.watchdog_event = threading.Event() self.last = None def run(self): try: for i in range(1, 1+self.iterations): if self.done_event.is_set(): break self.watchdog_event.set() #print i, "SEND" self.chan.send("x" * 2048) finally: self.done_event.set() self.watchdog_event.set() class ReceiveThread(threading.Thread): def __init__(self, chan, done_event): threading.Thread.__init__(self, None, None, self.__class__.__name__) self.setDaemon(True) self.chan = chan self.done_event = done_event self.watchdog_event = threading.Event() def run(self): try: while not self.done_event.is_set(): if self.chan.recv_ready(): chan.recv(65536) self.watchdog_event.set() else: if random.randint(0, 1): time.sleep(random.randint(0, 500) / 1000.0) finally: self.done_event.set() self.watchdog_event.set() self.setup_test_server() self.ts.packetizer.REKEY_BYTES = 2048 chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) # Monkey patch the client's Transport._handler_table so that the client # sends MSG_CHANNEL_WINDOW_ADJUST whenever it receives an initial # MSG_KEXINIT. This is used to simulate the effect of network latency # on a real MSG_CHANNEL_WINDOW_ADJUST message. self.tc._handler_table = self.tc._handler_table.copy() # copy per-class dictionary _negotiate_keys = self.tc._handler_table[MSG_KEXINIT] def _negotiate_keys_wrapper(self, m): if self.local_kex_init is None: # Remote side sent KEXINIT # Simulate in-transit MSG_CHANNEL_WINDOW_ADJUST by sending it # before responding to the incoming MSG_KEXINIT. m2 = Message() m2.add_byte(cMSG_CHANNEL_WINDOW_ADJUST) m2.add_int(chan.remote_chanid) m2.add_int(1) # bytes to add self._send_message(m2) return _negotiate_keys(self, m) self.tc._handler_table[MSG_KEXINIT] = _negotiate_keys_wrapper # Parameters for the test iterations = 500 # The deadlock does not happen every time, but it # should after many iterations. timeout = 5 # This event is set when the test is completed done_event = threading.Event() # Start the sending thread st = SendThread(schan, iterations, done_event) st.start() # Start the receiving thread rt = ReceiveThread(chan, done_event) rt.start() # Act as a watchdog timer, checking deadlocked = False while not deadlocked and not done_event.is_set(): for event in (st.watchdog_event, rt.watchdog_event): event.wait(timeout) if done_event.is_set(): break if not event.is_set(): deadlocked = True break event.clear() # Tell the threads to stop (if they haven't already stopped). Note # that if one or more threads are deadlocked, they might hang around # forever (until the process exits). done_event.set() # Assertion: We must not have detected a timeout. self.assertFalse(deadlocked) # Close the channels schan.close() chan.close() def test_J_sanitze_packet_size(self): """ verify that we conform to the rfc of packet and window sizes. """ for val, correct in [(4095, MIN_PACKET_SIZE), (None, DEFAULT_MAX_PACKET_SIZE), (2**32, MAX_WINDOW_SIZE)]: self.assertEqual(self.tc._sanitize_packet_size(val), correct) def test_K_sanitze_window_size(self): """ verify that we conform to the rfc of packet and window sizes. """ for val, correct in [(32767, MIN_WINDOW_SIZE), (None, DEFAULT_WINDOW_SIZE), (2**32, MAX_WINDOW_SIZE)]: self.assertEqual(self.tc._sanitize_window_size(val), correct) @slow def test_L_handshake_timeout(self): """ verify that we can get a hanshake timeout. """ # Tweak client Transport instance's Packetizer instance so # its read_message() sleeps a bit. This helps prevent race conditions # where the client Transport's timeout timer thread doesn't even have # time to get scheduled before the main client thread finishes # handshaking with the server. # (Doing this on the server's transport *sounds* more 'correct' but # actually doesn't work nearly as well for whatever reason.) class SlowPacketizer(Packetizer): def read_message(self): time.sleep(1) return super(SlowPacketizer, self).read_message() # NOTE: prettttty sure since the replaced .packetizer Packetizer is now # no longer doing anything with its copy of the socket...everything'll # be fine. Even tho it's a bit squicky. self.tc.packetizer = SlowPacketizer(self.tc.sock) # Continue with regular test red tape. host_key = RSAKey.from_private_key_file(_support('test_rsa.key')) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.tc.handshake_timeout = 0.000000000001 self.ts.start_server(event, server) self.assertRaises(EOFError, self.tc.connect, hostkey=public_host_key, username='******', password='******') def test_M_select_after_close(self): """ verify that select works when a channel is already closed. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) schan.close() # give client a moment to receive close notification time.sleep(0.1) r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) def test_channel_send_misc(self): """ verify behaviours sending various instances to a channel """ self.setup_test_server() text = u"\xa7 slice me nicely" with self.tc.open_session() as chan: schan = self.ts.accept(1.0) if schan is None: self.fail("Test server transport failed to accept") sfile = schan.makefile() # TypeError raised on non string or buffer type self.assertRaises(TypeError, chan.send, object()) self.assertRaises(TypeError, chan.sendall, object()) # sendall() accepts a unicode instance chan.sendall(text) expected = text.encode("utf-8") self.assertEqual(sfile.read(len(expected)), expected) @needs_builtin('buffer') def test_channel_send_buffer(self): """ verify sending buffer instances to a channel """ self.setup_test_server() data = 3 * b'some test data\n whole' with self.tc.open_session() as chan: schan = self.ts.accept(1.0) if schan is None: self.fail("Test server transport failed to accept") sfile = schan.makefile() # send() accepts buffer instances sent = 0 while sent < len(data): sent += chan.send(buffer(data, sent, 8)) self.assertEqual(sfile.read(len(data)), data) # sendall() accepts a buffer instance chan.sendall(buffer(data)) self.assertEqual(sfile.read(len(data)), data) @needs_builtin('memoryview') def test_channel_send_memoryview(self): """ verify sending memoryview instances to a channel """ self.setup_test_server() data = 3 * b'some test data\n whole' with self.tc.open_session() as chan: schan = self.ts.accept(1.0) if schan is None: self.fail("Test server transport failed to accept") sfile = schan.makefile() # send() accepts memoryview slices sent = 0 view = memoryview(data) while sent < len(view): sent += chan.send(view[sent:sent+8]) self.assertEqual(sfile.read(len(data)), data) # sendall() accepts a memoryview instance chan.sendall(memoryview(data)) self.assertEqual(sfile.read(len(data)), data) def test_server_rejects_open_channel_without_auth(self): try: self.setup_test_server(connect_kwargs={}) self.tc.open_session() except ChannelException as e: assert e.code == OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED else: assert False, "Did not raise ChannelException!" def test_server_rejects_arbitrary_global_request_without_auth(self): self.setup_test_server(connect_kwargs={}) # NOTE: this dummy global request kind would normally pass muster # from the test server. self.tc.global_request('acceptable') # Global requests never raise exceptions, even on failure (not sure why # this was the original design...ugh.) Best we can do to tell failure # happened is that the client transport's global_response was set back # to None; if it had succeeded, it would be the response Message. err = "Unauthed global response incorrectly succeeded!" assert self.tc.global_response is None, err def test_server_rejects_port_forward_without_auth(self): # NOTE: at protocol level port forward requests are treated same as a # regular global request, but Paramiko server implements a special-case # method for it, so it gets its own test. (plus, THAT actually raises # an exception on the client side, unlike the general case...) self.setup_test_server(connect_kwargs={}) try: self.tc.request_port_forward('localhost', 1234) except SSHException as e: assert "forwarding request denied" in str(e) else: assert False, "Did not raise SSHException!"
class AuthTest (unittest.TestCase): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def start_server(self): host_key = RSAKey.from_private_key_file(test_path('test_rsa.key')) self.public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) self.event = threading.Event() self.server = NullServer() self.assertTrue(not self.event.is_set()) self.ts.start_server(self.event, self.server) def verify_finished(self): self.event.wait(1.0) self.assertTrue(self.event.is_set()) self.assertTrue(self.ts.is_active()) def test_1_bad_auth_type(self): """ verify that we get the right exception when an unsupported auth type is requested. """ self.start_server() try: self.tc.connect(hostkey=self.public_host_key, username='******', password='******') self.assertTrue(False) except: etype, evalue, etb = sys.exc_info() self.assertEqual(BadAuthenticationType, etype) self.assertEqual(['publickey'], evalue.allowed_types) def test_2_bad_password(self): """ verify that a bad password gets the right exception, and that a retry with the right password works. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) try: self.tc.auth_password(username='******', password='******') self.assertTrue(False) except: etype, evalue, etb = sys.exc_info() self.assertTrue(issubclass(etype, AuthenticationException)) self.tc.auth_password(username='******', password='******') self.verify_finished() def test_3_multipart_auth(self): """ verify that multipart auth works. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password(username='******', password='******') self.assertEqual(['publickey'], remain) key = DSSKey.from_private_key_file(test_path('test_dss.key')) remain = self.tc.auth_publickey(username='******', key=key) self.assertEqual([], remain) self.verify_finished() def test_4_interactive_auth(self): """ verify keyboard-interactive auth works. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) def handler(title, instructions, prompts): self.got_title = title self.got_instructions = instructions self.got_prompts = prompts return ['cat'] remain = self.tc.auth_interactive('commie', handler) self.assertEqual(self.got_title, 'password') self.assertEqual(self.got_prompts, [('Password', False)]) self.assertEqual([], remain) self.verify_finished() def test_5_interactive_auth_fallback(self): """ verify that a password auth attempt will fallback to "interactive" if password auth isn't supported but interactive is. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password('commie', 'cat') self.assertEqual([], remain) self.verify_finished() def test_6_auth_utf8(self): """ verify that utf-8 encoding happens in authentication. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password('utf8', _pwd) self.assertEqual([], remain) self.verify_finished() def test_7_auth_non_utf8(self): """ verify that non-utf-8 encoded passwords can be used for broken servers. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password('non-utf8', '\xff') self.assertEqual([], remain) self.verify_finished() def test_8_auth_gets_disconnected(self): """ verify that we catch a server disconnecting during auth, and report it as an auth failure. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) try: remain = self.tc.auth_password('bad-server', 'hello') except: etype, evalue, etb = sys.exc_info() self.assertTrue(issubclass(etype, AuthenticationException)) def test_9_auth_non_responsive(self): """ verify that authentication times out if server takes to long to respond (or never responds). """ self.tc.auth_timeout = 1 # 1 second, to speed up test self.start_server() self.tc.connect() try: remain = self.tc.auth_password('unresponsive-server', 'hello') except: etype, evalue, etb = sys.exc_info() self.assertTrue(issubclass(etype, AuthenticationException)) self.assertTrue('Authentication timeout' in str(evalue))
class SSHHandler(ServerInterface, BaseRequestHandler): telnet_handler = None pty_handler = None host_key = None username = None def __init__(self, request, client_address, server): self.request = request self.client_address = client_address self.tcp_server = server # Keep track of channel information from the transport self.channels = {} try: self.client = request._sock except AttributeError as e: self.client = request # Transport turns the socket into an SSH transport self.transport = Transport(self.client) # Create the PTY handler class by mixing in TelnetHandlerClass = self.telnet_handler class MixedPtyHandler(TelnetToPtyHandler, TelnetHandlerClass): # BaseRequestHandler does not inherit from object, must call the __init__ directly def __init__(self, *args): super(MixedPtyHandler, self).__init__(*args) TelnetHandlerClass.__init__(self, *args) self.pty_handler = MixedPtyHandler # Call the base class to run the handler BaseRequestHandler.__init__(self, request, client_address, server) def setup(self): """Setup the connection.""" log.debug('New request from address %s, port %d', self.client_address) try: self.transport.load_server_moduli() except: log.exception('(Failed to load moduli -- gex will be unsupported.)') raise try: self.transport.add_server_key(self.host_key) except: if self.host_key is None: log.critical('Host key not set! SSHHandler MUST define the host_key parameter.') raise NotImplementedError('Host key not set! SSHHandler instance must define ' 'the host_key parameter. Try host_key = paramiko_ssh' '.getRsaKeyFile("server_rsa.key").') try: # Tell transport to use this object as a server log.debug('Starting SSH server-side negotiation') self.transport.start_server(server=self) except SSHException as e: log.warn('SSH negotiation failed. %s', e) raise # Accept any requested channels while True: channel = self.transport.accept(20) if channel is None: # check to see if any thread is running any_running = False for c, thread in self.channels.items(): if thread.is_alive(): any_running = True break if not any_running: break else: log.info('Accepted channel %s', channel) class dummy_request(object): def __init__(self): self._sock = None @classmethod def streamserver_handle(cls, socket, address): """Translate this class for use in a StreamServer""" request = cls.dummy_request() request._sock = socket server = None cls(request, address, server) def finish(self): """Called when the socket closes from the client.""" self.transport.close() def check_channel_request(self, kind, chanid): if kind == 'session': return OPEN_SUCCEEDED return OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED def set_username(self, username): self.username = username log.info('User logged in: %s' % username) # Handle User Authentication ###### # Override these with functions to use for callbacks authCallback = None authCallbackKey = None authCallbackUsername = None def get_allowed_auths(self, username): methods = [] if self.authCallbackUsername is not None: methods.append('none') if self.authCallback is not None: methods.append('password') if self.authCallbackKey is not None: methods.append('publickey') if not methods: # If no methods were defined, use none methods.append('none') log.debug('Configured authentication methods: %r', methods) return ','.join(methods) def check_auth_password(self, username, password): # print 'check_auth_password(%s, %s)' % (username, password) try: self.authCallback(username, password) except: return AUTH_FAILED else: self.set_username(username) return AUTH_SUCCESSFUL def check_auth_publickey(self, username, key): # print 'Auth attempt with key: ' + hexlify(key.get_fingerprint()) try: self.authCallbackKey(username, key) except: return AUTH_FAILED else: self.set_username(username) return AUTH_SUCCESSFUL # if (username == 'xx') and (key == self.good_pub_key): # return AUTH_SUCCESSFUL def check_auth_none(self, username): if self.authCallbackUsername is None: self.set_username(username) return AUTH_SUCCESSFUL try: self.authCallbackUsername(username) except: return AUTH_FAILED else: self.set_username(username) return AUTH_SUCCESSFUL def check_channel_shell_request(self, channel): '''Request to start a shell on the given channel''' try: self.channels[channel].start() except KeyError: log.error('Requested to start a channel (%r) that was not previously set up.', channel) return False else: return True def check_channel_pty_request(self, channel, term, width, height, pixelwidth, pixelheight, modes): """Request to allocate a PTY terminal.""" # self.sshterm = term # print "term: %r, modes: %r" % (term, modes) log.debug('PTY requested. Setting up %r.', self.telnet_handler) pty_thread = Thread(target=self.start_pty_request, args=(channel, term, modes)) self.channels[channel] = pty_thread return True def start_pty_request(self, channel, term, modes): """Start a PTY - intended to run it a (green)thread.""" request = self.dummy_request() request._sock = channel request.modes = modes request.term = term request.username = self.username # modes = http://www.ietf.org/rfc/rfc4254.txt page 18 # for i in xrange(50): # print "%r: %r" % (int(m[i*5].encode('hex'), 16), int(''.join(m[i*5+1:i*5+5]).encode('hex'), 16)) # This should block until the user quits the pty self.pty_handler(request, self.client_address, self.tcp_server) # Shutdown the entire session self.transport.close()
# -*- coding: utf-8 -*- __author__ = 'Administrator' from paramiko import Transport,RSAKey,SFTPClient import os host="54.187.91.18" port=22 private_key_file=os.path.expanduser("~/.ssh/id_rsa") key=RSAKey.from_private_key_file(private_key_file) t=Transport((host,port)) t.connect(username="******",pkey=key) sftp=SFTPClient.from_transport(t) try: sftp.put("/data/websites/test/a.php","/data/websites/") except Exception,e: print(e) sftp.close() t.close()
def exec_async_task(app, server_id, user_id, login_name, password, secret_key): with app.app_context(): tasks = ServerTask.query.filter_by(user_id=user_id).filter_by(server_id=server_id).\ filter_by(status=Status.Wait).all() server = Server.query.get_or_404(int(server_id)) t = Transport((server.ip,int(server.ssh_port))) try: t.connect(username=login_name, password=password + secret_key) except Exception: return False sftp = SFTPClient.from_transport(t) default_name_pawd = ServerLogin(user_id=user_id,server_id=server.id,login_name=login_name,password=password) db.session.add(default_name_pawd) for task in tasks: task.be_work() db.session.commit() if task.filename.rsplit('.', 1)[1] == 'gz': dir = task.filename.split('.')[0] + str(task.id) sh.cd(current_app.config['MAGIC_MIRROR_TMPFILE_PATH']) sh.tar.bake('xzf')(task.filename) sh.mv.bake(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + task.filename.split('.')[0])(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir) files = os.listdir(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir) filenames = [] for file in files: if os.path.isfile(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir + '/' + file): filenames.append((current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir + '/' + file,task.filename.split('.')[0] + '/' + file)) elif task.filename.rsplit('.', 1)[1] == 'rar': dir = task.filename.split('.')[0] + str(task.id) sh.cd(current_app.config['MAGIC_MIRROR_TMPFILE_PATH']) sh.rar.bake('x')(task.filename) sh.mv.bake(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + task.filename.split('.')[0])(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir) files = os.listdir(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir) filenames = [] for file in files: if os.path.isfile(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir + '/' + file): filenames.append((current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + dir + '/' + file,task.filename.split('.')[0] + '/' + file)) elif task.filename.rsplit('.', 1)[1] == 'beam': filenames = [(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + task.filename,'ebin/' + task.filename)] else: filenames = [(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + task.filename,'db_update/' + datetime.now().strftime("%Y%m%d") + '.sql')] log = 'lastupload.status' sh.touch(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + log) logfilename = (current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + log, log) if task.model == 'login': for source,target in filenames: remotepath = current_app.config['MAGIC_MIRROR_LOGIN_PATH'] + target if task.filename.rsplit('.', 1)[1] == 'sql': oldfiles = sftp.listdir(current_app.config['MAGIC_MIRROR_LOGIN_PATH'] + 'db_update/') for oldfile in oldfiles: sftp.remove(current_app.config['MAGIC_MIRROR_LOGIN_PATH'] + 'db_update/' + oldfile) localpath = source sftp.put(localpath,remotepath) logsource = logfilename[0] logtarget = current_app.config['MAGIC_MIRROR_LOGIN_PATH'] + logfilename[1] sftp.put(logsource,logtarget) time = 0 while(sftp.stat(logtarget).st_mode == mode_664 and time < 60): sleep(2) time += 1 if sftp.stat(logtarget).st_mode == mode_674: task.be_wrong() db.session.commit() continue if task.model == 'android_qa': for source,target in filenames: remotepath = current_app.config['MAGIC_MIRROR_ANDROID_QA_PATH'] + target if task.filename.rsplit('.', 1)[1] == 'sql': oldfiles = sftp.listdir(current_app.config['MAGIC_MIRROR_ANDROID_QA_PATH'] + 'db_update/') for oldfile in oldfiles: sftp.remove(current_app.config['MAGIC_MIRROR_ANDROID_QA_PATH'] + 'db_update/' + oldfile) localpath = source sftp.put(localpath,remotepath) logsource = logfilename[0] logtarget = current_app.config['MAGIC_MIRROR_ANDROID_QA_PATH'] + logfilename[1] sftp.put(logsource,logtarget) time = 0 while(sftp.stat(logtarget).st_mode == mode_664 and time < 60): sleep(2) time += 1 if sftp.stat(logtarget).st_mode == mode_674: task.be_wrong() db.session.commit() continue if task.model == 'android_review': for source,target in filenames: remotepath = current_app.config['MAGIC_MIRROR_ANDROID_REVIEW_PATH'] + target if task.filename.rsplit('.', 1)[1] == 'sql': oldfiles = sftp.listdir(current_app.config['MAGIC_MIRROR_ANDROID_REVIEW_PATH'] + 'db_update/') for oldfile in oldfiles: sftp.remove(current_app.config['MAGIC_MIRROR_ANDROID_REVIEW_PATH'] + 'db_update/' + oldfile) localpath = source sftp.put(localpath,remotepath) logsource = logfilename[0] logtarget = current_app.config['MAGIC_MIRROR_ANDROID_REVIEW_PATH'] + logfilename[1] sftp.put(logsource,logtarget) time = 0 while(sftp.stat(logtarget).st_mode == mode_664 and time < 60): sleep(2) time += 1 if sftp.stat(logtarget).st_mode == mode_674: task.be_wrong() db.session.commit() continue if task.model == 'android_online': for source,target in filenames: remotepath = current_app.config['MAGIC_MIRROR_ANDROID_ONLINE_PATH'] + target if task.filename.rsplit('.', 1)[1] == 'sql': oldfiles = sftp.listdir(current_app.config['MAGIC_MIRROR_ANDROID_ONLINE_PATH'] + 'db_update/') for oldfile in oldfiles: sftp.remove(current_app.config['MAGIC_MIRROR_ANDROID_ONLINE_PATH'] + 'db_update/' + oldfile) localpath = source sftp.put(localpath,remotepath) logsource = logfilename[0] logtarget = current_app.config['MAGIC_MIRROR_ANDROID_ONLINE_PATH'] + logfilename[1] sftp.put(logsource,logtarget) time = 0 while(sftp.stat(logtarget).st_mode == mode_664 and time < 60): sleep(2) time += 1 if sftp.stat(logtarget).st_mode == mode_674: task.be_wrong() db.session.commit() continue if task.model == 'ios_qa': for source,target in filenames: remotepath = current_app.config['MAGIC_MIRROR_IOS_QA_PATH'] + target if task.filename.rsplit('.', 1)[1] == 'sql': oldfiles = sftp.listdir(current_app.config['MAGIC_MIRROR_IOS_QA_PATH'] + 'db_update/') for oldfile in oldfiles: sftp.remove(current_app.config['MAGIC_MIRROR_IOS_QA_PATH'] + 'db_update/' + oldfile) localpath = source sftp.put(localpath,remotepath) logsource = logfilename[0] logtarget = current_app.config['MAGIC_MIRROR_IOS_QA_PATH'] + logfilename[1] sftp.put(logsource,logtarget) time = 0 while(sftp.stat(logtarget).st_mode == mode_664 and time < 60): sleep(2) time += 1 if sftp.stat(logtarget).st_mode == mode_674: task.be_wrong() db.session.commit() continue if task.model == 'ios_review': for source,target in filenames: remotepath = current_app.config['MAGIC_MIRROR_IOS_REVIEW_PATH'] + target if task.filename.rsplit('.', 1)[1] == 'sql': oldfiles = sftp.listdir(current_app.config['MAGIC_MIRROR_IOS_REVIEW_PATH'] + 'db_update/') for oldfile in oldfiles: sftp.remove(current_app.config['MAGIC_MIRROR_IOS_REVIEW_PATH'] + 'db_update/' + oldfile) localpath = source sftp.put(localpath,remotepath) logsource = logfilename[0] logtarget = current_app.config['MAGIC_MIRROR_IOS_REVIEW_PATH'] + logfilename[1] sftp.put(logsource,logtarget) time = 0 while(sftp.stat(logtarget).st_mode == mode_664 and time < 60): sleep(2) time += 1 if sftp.stat(logtarget).st_mode == mode_674: task.be_wrong() db.session.commit() continue if task.model == 'ios_online': for source,target in filenames: remotepath = current_app.config['MAGIC_MIRROR_IOS_ONLINE_PATH'] + target if task.filename.rsplit('.', 1)[1] == 'sql': oldfiles = sftp.listdir(current_app.config['MAGIC_MIRROR_IOS_ONLINE_PATH'] + 'db_update/') for oldfile in oldfiles: sftp.remove(current_app.config['MAGIC_MIRROR_IOS_ONLINE_PATH'] + 'db_update/' + oldfile) localpath = source sftp.put(localpath,remotepath) logsource = logfilename[0] logtarget = current_app.config['MAGIC_MIRROR_IOS_ONLINE_PATH'] + logfilename[1] sftp.put(logsource,logtarget) time = 0 while(sftp.stat(logtarget).st_mode == mode_664 and time < 60): sleep(2) time += 1 if sftp.stat(logtarget).st_mode == mode_674: task.be_wrong() db.session.commit() continue task.be_done() if not ServerTask.query.filter_by(filename=task.filename).filter(ServerTask.id!=task.id).\ filter(ServerTask.status!=Status.Done).filter(ServerTask.status!=Status.Wrong).first(): if task.filename.rsplit('.', 1)[1] in ('gz','rar'): sh.rm.bake('-rf')(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + task.filename.split('.')[0] + str(task.id)) sh.rm.bake('-f')(current_app.config['MAGIC_MIRROR_TMPFILE_PATH'] + task.filename) t.close() sftp.close() return True
class SFTP(FileBackend): def __init__(self, config): FileBackend.__init__(self, config) self._init_config() self.session = None self._init_session() DEFAULT_CONFIG = { 'sftp_host': 'localhost', 'sftp_port': 22, 'sftp_user': '******', 'sftp_password': '', 'output_path': ''} def _init_config(self): some_changes = False if 'type' in self.config: for option, value in SFTP.DEFAULT_CONFIG.items(): if not option in self.config: self.config[option] = value some_changes = True logging.info('set default sftp config.') else: self.config['type'] = 'sftp' self.config.update(SFTP.DEFAULT_CONFIG) some_changes = True logging.info('set default sftp config.') if some_changes: self.config.save() # cast config types self.config['sftp_port'] = int(self.config['sftp_port']) def _init_session(self): self.connect() def connect(self): self._authenticate() self.state = self.STATE_CONNECTED def _authenticate(self): self._transport = SFTPTransport((self.config['sftp_host'], self.config['sftp_port'])) self._transport.connect(username=self.config['sftp_user'], password=self.config['sftp_password']) self.session = SFTPClient.from_transport(self._transport) logging.info('SFTP Authorization succeed') def disconnect(self): self.session.close() self._transport.close() def file_create_folder(self, path): if self.state == self.STATE_CONNECTED: dirlist = path.split('/') current_dirlist = [''] missing_dirlist = [] current_dirlist.extend(dirlist[:]) while len(current_dirlist) > 0: current_path = '/'.join(current_dirlist) try: self.session.chdir(current_path) break except: missing_dirlist.append(current_dirlist.pop()) missing_dirlist.reverse() for dirname in missing_dirlist: dir_contents = self.session.listdir() if not dirname in dir_contents: self.session.mkdir(dirname) logging.info('Create remote directory %s' % self.session.getcwd() + '/' + dirname) self.session.chdir(dirname) elif self.state == self.STATE_DISCONNECTED: raise self.ConnectionException('SFTP is %s' % self.state) else: raise NotImplementedError() def put_file(self, path, file_handle): if self.state == self.STATE_CONNECTED: dirpath = '/'.join(path.split('/')[:-1]) self.file_create_folder(dirpath) try: self.session.putfo(fl=file_handle, remotepath='/' + path) logging.info('Create remote file %s' % '/' + path) except Exception as ex: logging.error(ex) elif self.state == self.STATE_DISCONNECTED: raise self.ConnectionException('SFTP is %s' % self.state) else: raise NotImplementedError()
def upload_report(self, filepath, file_name, report_date): """ >>> a = CSVgenerator('123') >>> a.upload_report(None, 'upload.txt', '2014-05-06') """ # appropriate changes required for doc test to run - dev only. # test_file_path = join(settings.REPORTS_ROOT, '16b6a354-ff32-4be4-b648-fe51fc5b1508.csv') # # trace.info('----- {}'.format(abspath(test_file_path))) # absolute_filename = abspath(test_file_path) # test_filename = '16b6a354-ff32-4be4-b648-fe51fc5b1508.csv' #TODO remove after QA testing. if not filepath: test_file_path = join(settings.REPORTS_ROOT, file_name) absolute_filename = abspath(test_file_path) else: absolute_filename = abspath(filepath) try: report_date = report_date if isinstance(report_date, str) else report_date.strftime(settings.DATE_FORMAT_YMD) except Exception: log.exception('') try: #TODO use sftp credentials, once available. #TODO get client upload location for per-client report. # ftp_client_dir = client.ftp_client_dir # if ftp_client_dir == '': # ftp_client_dir = getattr(settings, 'DEFAULT_SFTP_LOCATION', 'default') # logger.exception(u'No FTP configuration for client {} using default value.'.format(client.name)) # year_folder, month_folder, _ = report_date.split('-') base_folder_path = self._sftp_settings.get('path', '') base_path = base_folder_path.split('/') base_folders = [i for i in base_path[1:-1]] base_folder = '/' + join(*base_folders) env_folder = str(base_path[-1:][0]) from paramiko import Transport, SFTPClient try: log.info(u'SFTP logging on to {0} as {1}'.format(settings.SFTP_SERVER, settings.SFTP_USERNAME)) transport = Transport(( self._sftp_settings.get('server', ''), self._sftp_settings.get('port', 22)) ) transport.connect( username=self._sftp_settings.get('username', ''), password=self._sftp_settings.get('password', '') ) sftp = SFTPClient.from_transport(transport) log.info(u'SFTP dir {0}/{1}/{2}/{3}'.format(base_folder, env_folder, year_folder, month_folder)) try: sftp.chdir(base_folder) except Exception: log.debug('Unable to change to base folder on ftp server.') self._make_or_change_sftp_dir(sftp, env_folder) self._make_or_change_sftp_dir(sftp, year_folder) self._make_or_change_sftp_dir(sftp, month_folder) log.debug(u'SFTP uploading {0}'.format(filepath)) sftp.put(absolute_filename, file_name) except Exception: log.exception(u'Unrecoverable exception during SFTP upload process.') finally: log.debug(u'SFTP logging off') try: sftp.close() except Exception: log.exception(u'SFTP exception while closing SFTP session.') try: transport.close() except Exception: log.exception(u'SFTP exception while closing SSH connection.') except Exception: log.debug('Error while uploading report.')
class TransportTest (unittest.TestCase): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def setup_test_server(self): host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() self.server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, self.server) self.tc.connect(hostkey=public_host_key) self.tc.auth_password(username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_1_security_options(self): o = self.tc.get_security_options() self.assertEquals(type(o), SecurityOptions) self.assert_(('aes256-cbc', 'blowfish-cbc') != o.ciphers) o.ciphers = ('aes256-cbc', 'blowfish-cbc') self.assertEquals(('aes256-cbc', 'blowfish-cbc'), o.ciphers) try: o.ciphers = ('aes256-cbc', 'made-up-cipher') self.assert_(False) except ValueError: pass try: o.ciphers = 23 self.assert_(False) except TypeError: pass def test_2_compute_key(self): self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929L self.tc.H = unhexlify('0C8307CDE6856FF30BA93684EB0F04C2520E9ED3') self.tc.session_id = self.tc.H key = self.tc._compute_key('C', 32) self.assertEquals('207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995', hexlify(key).upper()) def test_3_simple(self): """ verify that we can establish an ssh link with ourselves across the loopback sockets. this is hardly "simple" but it's simpler than the later tests. :) """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.assertEquals(None, self.tc.get_username()) self.assertEquals(None, self.ts.get_username()) self.assertEquals(False, self.tc.is_authenticated()) self.assertEquals(False, self.ts.is_authenticated()) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) self.assertEquals('slowdive', self.tc.get_username()) self.assertEquals('slowdive', self.ts.get_username()) self.assertEquals(True, self.tc.is_authenticated()) self.assertEquals(True, self.ts.is_authenticated()) def test_4_special(self): """ verify that the client can demand odd handshake settings, and can renegotiate keys in mid-stream. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, server) options = self.tc.get_security_options() options.ciphers = ('aes256-cbc',) options.digests = ('hmac-md5-96',) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) self.assertEquals('aes256-cbc', self.tc.local_cipher) self.assertEquals('aes256-cbc', self.tc.remote_cipher) self.assertEquals(12, self.tc.packetizer.get_mac_size_out()) self.assertEquals(12, self.tc.packetizer.get_mac_size_in()) self.tc.send_ignore(1024) self.tc.renegotiate_keys() self.ts.send_ignore(1024) def test_5_keepalive(self): """ verify that the keepalive will be sent. """ self.tc.set_hexdump(True) host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) self.assertEquals(None, getattr(server, '_global_request', None)) self.tc.set_keepalive(1) time.sleep(2) self.assertEquals('*****@*****.**', server._global_request) def test_6_bad_auth_type(self): """ verify that we get the right exception when an unsupported auth type is requested. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, server) try: self.tc.connect(hostkey=public_host_key, username='******', password='******') self.assert_(False) except: etype, evalue, etb = sys.exc_info() self.assertEquals(BadAuthenticationType, etype) self.assertEquals(['publickey'], evalue.allowed_types) def test_7_bad_password(self): """ verify that a bad password gets the right exception, and that a retry with the right password works. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, server) self.tc.ultra_debug = True self.tc.connect(hostkey=public_host_key) try: self.tc.auth_password(username='******', password='******') self.assert_(False) except: etype, evalue, etb = sys.exc_info() self.assert_(issubclass(etype, SSHException)) self.tc.auth_password(username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_8_multipart_auth(self): """ verify that multipart auth works. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, server) self.tc.ultra_debug = True self.tc.connect(hostkey=public_host_key) remain = self.tc.auth_password(username='******', password='******') self.assertEquals(['publickey'], remain) key = DSSKey.from_private_key_file('tests/test_dss.key') remain = self.tc.auth_publickey(username='******', key=key) self.assertEquals([], remain) event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_9_interactive_auth(self): """ verify keyboard-interactive auth works. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, server) self.tc.ultra_debug = True self.tc.connect(hostkey=public_host_key) def handler(title, instructions, prompts): self.got_title = title self.got_instructions = instructions self.got_prompts = prompts return ['cat'] remain = self.tc.auth_interactive('commie', handler) self.assertEquals(self.got_title, 'password') self.assertEquals(self.got_prompts, [('Password', False)]) self.assertEquals([], remain) event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_A_interactive_auth_fallback(self): """ verify that a password auth attempt will fallback to "interactive" if password auth isn't supported but interactive is. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, server) self.tc.ultra_debug = True self.tc.connect(hostkey=public_host_key) remain = self.tc.auth_password('commie', 'cat') self.assertEquals([], remain) event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_B_exec_command(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) try: chan.exec_command('no') self.assert_(False) except SSHException, x: pass chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() f = chan.makefile() self.assertEquals('Hello there.\n', f.readline()) self.assertEquals('', f.readline()) f = chan.makefile_stderr() self.assertEquals('This is on stderr.\n', f.readline()) self.assertEquals('', f.readline()) # now try it with combined stdout/stderr chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() chan.set_combine_stderr(True) f = chan.makefile() self.assertEquals('Hello there.\n', f.readline()) self.assertEquals('This is on stderr.\n', f.readline()) self.assertEquals('', f.readline())
class TransportTest(ParamikoTest): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def setup_test_server(self, client_options=None, server_options=None): host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) if client_options is not None: client_options(self.tc.get_security_options()) if server_options is not None: server_options(self.ts.get_security_options()) event = threading.Event() self.server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, self.server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_1_security_options(self): o = self.tc.get_security_options() self.assertEquals(type(o), SecurityOptions) self.assert_(('aes256-cbc', 'blowfish-cbc') != o.ciphers) o.ciphers = ('aes256-cbc', 'blowfish-cbc') self.assertEquals(('aes256-cbc', 'blowfish-cbc'), o.ciphers) try: o.ciphers = ('aes256-cbc', 'made-up-cipher') self.assert_(False) except ValueError: pass try: o.ciphers = 23 self.assert_(False) except TypeError: pass def test_2_compute_key(self): self.tc.K = long(123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929) self.tc.H = unhexlify('0C8307CDE6856FF30BA93684EB0F04C2520E9ED3') self.tc.session_id = self.tc.H key = self.tc._compute_key('C', 32) self.assertEquals('207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995', hexlify(key).upper()) def test_3_simple(self): """ verify that we can establish an ssh link with ourselves across the loopback sockets. this is hardly "simple" but it's simpler than the later tests. :) """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.assertEquals(None, self.tc.get_username()) self.assertEquals(None, self.ts.get_username()) self.assertEquals(False, self.tc.is_authenticated()) self.assertEquals(False, self.ts.is_authenticated()) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) self.assertEquals('slowdive', self.tc.get_username()) self.assertEquals('slowdive', self.ts.get_username()) self.assertEquals(True, self.tc.is_authenticated()) self.assertEquals(True, self.ts.is_authenticated()) def test_3a_long_banner(self): """ verify that a long banner doesn't mess up the handshake. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.socks.send(LONG_BANNER) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_4_special(self): """ verify that the client can demand odd handshake settings, and can renegotiate keys in mid-stream. """ def force_algorithms(options): options.ciphers = ('aes256-cbc',) options.digests = ('hmac-md5-96',) self.setup_test_server(client_options=force_algorithms) self.assertEquals('aes256-cbc', self.tc.local_cipher) self.assertEquals('aes256-cbc', self.tc.remote_cipher) self.assertEquals(12, self.tc.packetizer.get_mac_size_out()) self.assertEquals(12, self.tc.packetizer.get_mac_size_in()) self.tc.send_ignore(1024) self.tc.renegotiate_keys() self.ts.send_ignore(1024) def test_5_keepalive(self): """ verify that the keepalive will be sent. """ self.setup_test_server() self.assertEquals(None, getattr(self.server, '_global_request', None)) self.tc.set_keepalive(1) time.sleep(2) self.assertEquals('*****@*****.**', self.server._global_request) def test_6_exec_command(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) try: chan.exec_command('no') self.assert_(False) except SSHException, x: pass chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() f = chan.makefile() self.assertEquals('Hello there.\n', f.readline()) self.assertEquals('', f.readline()) f = chan.makefile_stderr() self.assertEquals('This is on stderr.\n', f.readline()) self.assertEquals('', f.readline()) # now try it with combined stdout/stderr chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() chan.set_combine_stderr(True) f = chan.makefile() self.assertEquals('Hello there.\n', f.readline()) self.assertEquals('This is on stderr.\n', f.readline()) self.assertEquals('', f.readline())
class TransportTest(unittest.TestCase): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def setup_test_server( self, client_options=None, server_options=None, connect_kwargs=None ): host_key = RSAKey.from_private_key_file(_support("test_rsa.key")) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) if client_options is not None: client_options(self.tc.get_security_options()) if server_options is not None: server_options(self.ts.get_security_options()) event = threading.Event() self.server = NullServer() self.assertTrue(not event.is_set()) self.ts.start_server(event, self.server) if connect_kwargs is None: connect_kwargs = dict( hostkey=public_host_key, username="******", password="******", ) self.tc.connect(**connect_kwargs) event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) def test_security_options(self): o = self.tc.get_security_options() self.assertEqual(type(o), SecurityOptions) self.assertTrue(("aes256-cbc", "blowfish-cbc") != o.ciphers) o.ciphers = ("aes256-cbc", "blowfish-cbc") self.assertEqual(("aes256-cbc", "blowfish-cbc"), o.ciphers) try: o.ciphers = ("aes256-cbc", "made-up-cipher") self.assertTrue(False) except ValueError: pass try: o.ciphers = 23 self.assertTrue(False) except TypeError: pass def testb_security_options_reset(self): o = self.tc.get_security_options() # should not throw any exceptions o.ciphers = o.ciphers o.digests = o.digests o.key_types = o.key_types o.kex = o.kex o.compression = o.compression def test_compute_key(self): self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929 # noqa self.tc.H = b"\x0C\x83\x07\xCD\xE6\x85\x6F\xF3\x0B\xA9\x36\x84\xEB\x0F\x04\xC2\x52\x0E\x9E\xD3" # noqa self.tc.session_id = self.tc.H key = self.tc._compute_key("C", 32) self.assertEqual( b"207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995", # noqa hexlify(key).upper(), ) def test_simple(self): """ verify that we can establish an ssh link with ourselves across the loopback sockets. this is hardly "simple" but it's simpler than the later tests. :) """ host_key = RSAKey.from_private_key_file(_support("test_rsa.key")) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.assertEqual(None, self.tc.get_username()) self.assertEqual(None, self.ts.get_username()) self.assertEqual(False, self.tc.is_authenticated()) self.assertEqual(False, self.ts.is_authenticated()) self.ts.start_server(event, server) self.tc.connect( hostkey=public_host_key, username="******", password="******" ) event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) self.assertEqual("slowdive", self.tc.get_username()) self.assertEqual("slowdive", self.ts.get_username()) self.assertEqual(True, self.tc.is_authenticated()) self.assertEqual(True, self.ts.is_authenticated()) def testa_long_banner(self): """ verify that a long banner doesn't mess up the handshake. """ host_key = RSAKey.from_private_key_file(_support("test_rsa.key")) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.socks.send(LONG_BANNER) self.ts.start_server(event, server) self.tc.connect( hostkey=public_host_key, username="******", password="******" ) event.wait(1.0) self.assertTrue(event.is_set()) self.assertTrue(self.ts.is_active()) def test_special(self): """ verify that the client can demand odd handshake settings, and can renegotiate keys in mid-stream. """ def force_algorithms(options): options.ciphers = ("aes256-cbc",) options.digests = ("hmac-md5-96",) self.setup_test_server(client_options=force_algorithms) self.assertEqual("aes256-cbc", self.tc.local_cipher) self.assertEqual("aes256-cbc", self.tc.remote_cipher) self.assertEqual(12, self.tc.packetizer.get_mac_size_out()) self.assertEqual(12, self.tc.packetizer.get_mac_size_in()) self.tc.send_ignore(1024) self.tc.renegotiate_keys() self.ts.send_ignore(1024) @slow def test_keepalive(self): """ verify that the keepalive will be sent. """ self.setup_test_server() self.assertEqual(None, getattr(self.server, "_global_request", None)) self.tc.set_keepalive(1) time.sleep(2) self.assertEqual("*****@*****.**", self.server._global_request) def test_exec_command(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) try: chan.exec_command( b"command contains \xfc and is not a valid UTF-8 string" ) self.assertTrue(False) except SSHException: pass chan = self.tc.open_session() chan.exec_command("yes") schan = self.ts.accept(1.0) schan.send("Hello there.\n") schan.send_stderr("This is on stderr.\n") schan.close() f = chan.makefile() self.assertEqual("Hello there.\n", f.readline()) self.assertEqual("", f.readline()) f = chan.makefile_stderr() self.assertEqual("This is on stderr.\n", f.readline()) self.assertEqual("", f.readline()) # now try it with combined stdout/stderr chan = self.tc.open_session() chan.exec_command("yes") schan = self.ts.accept(1.0) schan.send("Hello there.\n") schan.send_stderr("This is on stderr.\n") schan.close() chan.set_combine_stderr(True) f = chan.makefile() self.assertEqual("Hello there.\n", f.readline()) self.assertEqual("This is on stderr.\n", f.readline()) self.assertEqual("", f.readline()) def testa_channel_can_be_used_as_context_manager(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() with self.tc.open_session() as chan: with self.ts.accept(1.0) as schan: chan.exec_command("yes") schan.send("Hello there.\n") schan.close() f = chan.makefile() self.assertEqual("Hello there.\n", f.readline()) self.assertEqual("", f.readline()) def test_invoke_shell(self): """ verify that invoke_shell() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) chan.send("communist j. cat\n") f = schan.makefile() self.assertEqual("communist j. cat\n", f.readline()) chan.close() self.assertEqual("", f.readline()) def test_channel_exception(self): """ verify that ChannelException is thrown for a bad open-channel request. """ self.setup_test_server() try: self.tc.open_channel("bogus") self.fail("expected exception") except ChannelException as e: self.assertTrue(e.code == OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) def test_exit_status(self): """ verify that get_exit_status() works. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) chan.exec_command("yes") schan.send("Hello there.\n") self.assertTrue(not chan.exit_status_ready()) # trigger an EOF schan.shutdown_read() schan.shutdown_write() schan.send_exit_status(23) schan.close() f = chan.makefile() self.assertEqual("Hello there.\n", f.readline()) self.assertEqual("", f.readline()) count = 0 while not chan.exit_status_ready(): time.sleep(0.1) count += 1 if count > 50: raise Exception("timeout") self.assertEqual(23, chan.recv_exit_status()) chan.close() def test_select(self): """ verify that select() on a channel works. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.send("hello\n") # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(b"hello\n", chan.recv(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.close() # detect eof? for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(bytes(), chan.recv(16)) # make sure the pipe is still open for now... p = chan._pipe self.assertEqual(False, p._closed) chan.close() # ...and now is closed. self.assertEqual(True, p._closed) def test_renegotiate(self): """ verify that a transport can correctly renegotiate mid-stream. """ self.setup_test_server() self.tc.packetizer.REKEY_BYTES = 16384 chan = self.tc.open_session() chan.exec_command("yes") schan = self.ts.accept(1.0) self.assertEqual(self.tc.H, self.tc.session_id) for i in range(20): chan.send("x" * 1024) chan.close() # allow a few seconds for the rekeying to complete for i in range(50): if self.tc.H != self.tc.session_id: break time.sleep(0.1) self.assertNotEqual(self.tc.H, self.tc.session_id) schan.close() def test_compression(self): """ verify that zlib compression is basically working. """ def force_compression(o): o.compression = ("zlib",) self.setup_test_server(force_compression, force_compression) chan = self.tc.open_session() chan.exec_command("yes") schan = self.ts.accept(1.0) bytes = self.tc.packetizer._Packetizer__sent_bytes chan.send("x" * 1024) bytes2 = self.tc.packetizer._Packetizer__sent_bytes block_size = self.tc._cipher_info[self.tc.local_cipher]["block-size"] mac_size = self.tc._mac_info[self.tc.local_mac]["size"] # tests show this is actually compressed to *52 bytes*! including # packet overhead! nice!! :) self.assertTrue(bytes2 - bytes < 1024) self.assertEqual(16 + block_size + mac_size, bytes2 - bytes) chan.close() schan.close() def test_x11(self): """ verify that an x11 port can be requested and opened. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command("yes") schan = self.ts.accept(1.0) requested = [] def handler(c, addr_port): addr, port = addr_port requested.append((addr, port)) self.tc._queue_incoming_channel(c) self.assertEqual( None, getattr(self.server, "_x11_screen_number", None) ) cookie = chan.request_x11(0, single_connection=True, handler=handler) self.assertEqual(0, self.server._x11_screen_number) self.assertEqual("MIT-MAGIC-COOKIE-1", self.server._x11_auth_protocol) self.assertEqual(cookie, self.server._x11_auth_cookie) self.assertEqual(True, self.server._x11_single_connection) x11_server = self.ts.open_x11_channel(("localhost", 6093)) x11_client = self.tc.accept() self.assertEqual("localhost", requested[0][0]) self.assertEqual(6093, requested[0][1]) x11_server.send("hello") self.assertEqual(b"hello", x11_client.recv(5)) x11_server.close() x11_client.close() chan.close() schan.close() def test_reverse_port_forwarding(self): """ verify that a client can ask the server to open a reverse port for forwarding. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command("yes") self.ts.accept(1.0) requested = [] def handler(c, origin_addr_port, server_addr_port): requested.append(origin_addr_port) requested.append(server_addr_port) self.tc._queue_incoming_channel(c) port = self.tc.request_port_forward("127.0.0.1", 0, handler) self.assertEqual(port, self.server._listen.getsockname()[1]) cs = socket.socket() cs.connect(("127.0.0.1", port)) ss, _ = self.server._listen.accept() sch = self.ts.open_forwarded_tcpip_channel( ss.getsockname(), ss.getpeername() ) cch = self.tc.accept() sch.send("hello") self.assertEqual(b"hello", cch.recv(5)) sch.close() cch.close() ss.close() cs.close() # now cancel it. self.tc.cancel_port_forward("127.0.0.1", port) self.assertTrue(self.server._listen is None) def test_port_forwarding(self): """ verify that a client can forward new connections from a locally- forwarded port. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command("yes") self.ts.accept(1.0) # open a port on the "server" that the client will ask to forward to. greeting_server = socket.socket() greeting_server.bind(("127.0.0.1", 0)) greeting_server.listen(1) greeting_port = greeting_server.getsockname()[1] cs = self.tc.open_channel( "direct-tcpip", ("127.0.0.1", greeting_port), ("", 9000) ) sch = self.ts.accept(1.0) cch = socket.socket() cch.connect(self.server._tcpip_dest) ss, _ = greeting_server.accept() ss.send(b"Hello!\n") ss.close() sch.send(cch.recv(8192)) sch.close() self.assertEqual(b"Hello!\n", cs.recv(7)) cs.close() def test_stderr_select(self): """ verify that select() on a channel works even if only stderr is receiving data. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.send_stderr("hello\n") # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) self.assertEqual(b"hello\n", chan.recv_stderr(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([], r) self.assertEqual([], w) self.assertEqual([], e) schan.close() chan.close() def test_send_ready(self): """ verify that send_ready() indicates when a send would not block. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) self.assertEqual(chan.send_ready(), True) total = 0 K = "*" * 1024 limit = 1 + (64 * 2 ** 15) while total < limit: chan.send(K) total += len(K) if not chan.send_ready(): break self.assertTrue(total < limit) schan.close() chan.close() self.assertEqual(chan.send_ready(), True) def test_rekey_deadlock(self): """ Regression test for deadlock when in-transit messages are received after MSG_KEXINIT is sent Note: When this test fails, it may leak threads. """ # Test for an obscure deadlocking bug that can occur if we receive # certain messages while initiating a key exchange. # # The deadlock occurs as follows: # # In the main thread: # 1. The user's program calls Channel.send(), which sends # MSG_CHANNEL_DATA to the remote host. # 2. Packetizer discovers that REKEY_BYTES has been exceeded, and # sets the __need_rekey flag. # # In the Transport thread: # 3. Packetizer notices that the __need_rekey flag is set, and raises # NeedRekeyException. # 4. In response to NeedRekeyException, the transport thread sends # MSG_KEXINIT to the remote host. # # On the remote host (using any SSH implementation): # 5. The MSG_CHANNEL_DATA is received, and MSG_CHANNEL_WINDOW_ADJUST # is sent. # 6. The MSG_KEXINIT is received, and a corresponding MSG_KEXINIT is # sent. # # In the main thread: # 7. The user's program calls Channel.send(). # 8. Channel.send acquires Channel.lock, then calls # Transport._send_user_message(). # 9. Transport._send_user_message waits for Transport.clear_to_send # to be set (i.e., it waits for re-keying to complete). # Channel.lock is still held. # # In the Transport thread: # 10. MSG_CHANNEL_WINDOW_ADJUST is received; Channel._window_adjust # is called to handle it. # 11. Channel._window_adjust tries to acquire Channel.lock, but it # blocks because the lock is already held by the main thread. # # The result is that the Transport thread never processes the remote # host's MSG_KEXINIT packet, because it becomes deadlocked while # handling the preceding MSG_CHANNEL_WINDOW_ADJUST message. # We set up two separate threads for sending and receiving packets, # while the main thread acts as a watchdog timer. If the timer # expires, a deadlock is assumed. class SendThread(threading.Thread): def __init__(self, chan, iterations, done_event): threading.Thread.__init__( self, None, None, self.__class__.__name__ ) self.setDaemon(True) self.chan = chan self.iterations = iterations self.done_event = done_event self.watchdog_event = threading.Event() self.last = None def run(self): try: for i in range(1, 1 + self.iterations): if self.done_event.is_set(): break self.watchdog_event.set() # print i, "SEND" self.chan.send("x" * 2048) finally: self.done_event.set() self.watchdog_event.set() class ReceiveThread(threading.Thread): def __init__(self, chan, done_event): threading.Thread.__init__( self, None, None, self.__class__.__name__ ) self.setDaemon(True) self.chan = chan self.done_event = done_event self.watchdog_event = threading.Event() def run(self): try: while not self.done_event.is_set(): if self.chan.recv_ready(): chan.recv(65536) self.watchdog_event.set() else: if random.randint(0, 1): time.sleep(random.randint(0, 500) / 1000.0) finally: self.done_event.set() self.watchdog_event.set() self.setup_test_server() self.ts.packetizer.REKEY_BYTES = 2048 chan = self.tc.open_session() chan.exec_command("yes") schan = self.ts.accept(1.0) # Monkey patch the client's Transport._handler_table so that the client # sends MSG_CHANNEL_WINDOW_ADJUST whenever it receives an initial # MSG_KEXINIT. This is used to simulate the effect of network latency # on a real MSG_CHANNEL_WINDOW_ADJUST message. self.tc._handler_table = ( self.tc._handler_table.copy() ) # copy per-class dictionary _negotiate_keys = self.tc._handler_table[MSG_KEXINIT] def _negotiate_keys_wrapper(self, m): if self.local_kex_init is None: # Remote side sent KEXINIT # Simulate in-transit MSG_CHANNEL_WINDOW_ADJUST by sending it # before responding to the incoming MSG_KEXINIT. m2 = Message() m2.add_byte(cMSG_CHANNEL_WINDOW_ADJUST) m2.add_int(chan.remote_chanid) m2.add_int(1) # bytes to add self._send_message(m2) return _negotiate_keys(self, m) self.tc._handler_table[MSG_KEXINIT] = _negotiate_keys_wrapper # Parameters for the test iterations = 500 # The deadlock does not happen every time, but it # should after many iterations. timeout = 5 # This event is set when the test is completed done_event = threading.Event() # Start the sending thread st = SendThread(schan, iterations, done_event) st.start() # Start the receiving thread rt = ReceiveThread(chan, done_event) rt.start() # Act as a watchdog timer, checking deadlocked = False while not deadlocked and not done_event.is_set(): for event in (st.watchdog_event, rt.watchdog_event): event.wait(timeout) if done_event.is_set(): break if not event.is_set(): deadlocked = True break event.clear() # Tell the threads to stop (if they haven't already stopped). Note # that if one or more threads are deadlocked, they might hang around # forever (until the process exits). done_event.set() # Assertion: We must not have detected a timeout. self.assertFalse(deadlocked) # Close the channels schan.close() chan.close() def test_sanitze_packet_size(self): """ verify that we conform to the rfc of packet and window sizes. """ for val, correct in [ (4095, MIN_PACKET_SIZE), (None, DEFAULT_MAX_PACKET_SIZE), (2 ** 32, MAX_WINDOW_SIZE), ]: self.assertEqual(self.tc._sanitize_packet_size(val), correct) def test_sanitze_window_size(self): """ verify that we conform to the rfc of packet and window sizes. """ for val, correct in [ (32767, MIN_WINDOW_SIZE), (None, DEFAULT_WINDOW_SIZE), (2 ** 32, MAX_WINDOW_SIZE), ]: self.assertEqual(self.tc._sanitize_window_size(val), correct) @slow def test_handshake_timeout(self): """ verify that we can get a hanshake timeout. """ # Tweak client Transport instance's Packetizer instance so # its read_message() sleeps a bit. This helps prevent race conditions # where the client Transport's timeout timer thread doesn't even have # time to get scheduled before the main client thread finishes # handshaking with the server. # (Doing this on the server's transport *sounds* more 'correct' but # actually doesn't work nearly as well for whatever reason.) class SlowPacketizer(Packetizer): def read_message(self): time.sleep(1) return super(SlowPacketizer, self).read_message() # NOTE: prettttty sure since the replaced .packetizer Packetizer is now # no longer doing anything with its copy of the socket...everything'll # be fine. Even tho it's a bit squicky. self.tc.packetizer = SlowPacketizer(self.tc.sock) # Continue with regular test red tape. host_key = RSAKey.from_private_key_file(_support("test_rsa.key")) public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assertTrue(not event.is_set()) self.tc.handshake_timeout = 0.000000000001 self.ts.start_server(event, server) self.assertRaises( EOFError, self.tc.connect, hostkey=public_host_key, username="******", password="******", ) def test_select_after_close(self): """ verify that select works when a channel is already closed. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) schan.close() # give client a moment to receive close notification time.sleep(0.1) r, w, e = select.select([chan], [], [], 0.1) self.assertEqual([chan], r) self.assertEqual([], w) self.assertEqual([], e) def test_channel_send_misc(self): """ verify behaviours sending various instances to a channel """ self.setup_test_server() text = u"\xa7 slice me nicely" with self.tc.open_session() as chan: schan = self.ts.accept(1.0) if schan is None: self.fail("Test server transport failed to accept") sfile = schan.makefile() # TypeError raised on non string or buffer type self.assertRaises(TypeError, chan.send, object()) self.assertRaises(TypeError, chan.sendall, object()) # sendall() accepts a unicode instance chan.sendall(text) expected = text.encode("utf-8") self.assertEqual(sfile.read(len(expected)), expected) @needs_builtin("buffer") def test_channel_send_buffer(self): """ verify sending buffer instances to a channel """ self.setup_test_server() data = 3 * b"some test data\n whole" with self.tc.open_session() as chan: schan = self.ts.accept(1.0) if schan is None: self.fail("Test server transport failed to accept") sfile = schan.makefile() # send() accepts buffer instances sent = 0 while sent < len(data): sent += chan.send(buffer(data, sent, 8)) # noqa self.assertEqual(sfile.read(len(data)), data) # sendall() accepts a buffer instance chan.sendall(buffer(data)) # noqa self.assertEqual(sfile.read(len(data)), data) @needs_builtin("memoryview") def test_channel_send_memoryview(self): """ verify sending memoryview instances to a channel """ self.setup_test_server() data = 3 * b"some test data\n whole" with self.tc.open_session() as chan: schan = self.ts.accept(1.0) if schan is None: self.fail("Test server transport failed to accept") sfile = schan.makefile() # send() accepts memoryview slices sent = 0 view = memoryview(data) while sent < len(view): sent += chan.send(view[sent : sent + 8]) self.assertEqual(sfile.read(len(data)), data) # sendall() accepts a memoryview instance chan.sendall(memoryview(data)) self.assertEqual(sfile.read(len(data)), data) def test_server_rejects_open_channel_without_auth(self): try: self.setup_test_server(connect_kwargs={}) self.tc.open_session() except ChannelException as e: assert e.code == OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED else: assert False, "Did not raise ChannelException!" def test_server_rejects_arbitrary_global_request_without_auth(self): self.setup_test_server(connect_kwargs={}) # NOTE: this dummy global request kind would normally pass muster # from the test server. self.tc.global_request("acceptable") # Global requests never raise exceptions, even on failure (not sure why # this was the original design...ugh.) Best we can do to tell failure # happened is that the client transport's global_response was set back # to None; if it had succeeded, it would be the response Message. err = "Unauthed global response incorrectly succeeded!" assert self.tc.global_response is None, err def test_server_rejects_port_forward_without_auth(self): # NOTE: at protocol level port forward requests are treated same as a # regular global request, but Paramiko server implements a special-case # method for it, so it gets its own test. (plus, THAT actually raises # an exception on the client side, unlike the general case...) self.setup_test_server(connect_kwargs={}) try: self.tc.request_port_forward("localhost", 1234) except SSHException as e: assert "forwarding request denied" in str(e) else: assert False, "Did not raise SSHException!" def _send_unimplemented(self, server_is_sender): self.setup_test_server() sender, recipient = self.tc, self.ts if server_is_sender: sender, recipient = self.ts, self.tc recipient._send_message = Mock() msg = Message() msg.add_byte(cMSG_UNIMPLEMENTED) sender._send_message(msg) # TODO: I hate this but I literally don't see a good way to know when # the recipient has received the sender's message (there are no # existing threading events in play that work for this), esp in this # case where we don't WANT a response (as otherwise we could # potentially try blocking on the sender's receipt of a reply...maybe). time.sleep(0.1) assert not recipient._send_message.called def test_server_does_not_respond_to_MSG_UNIMPLEMENTED(self): self._send_unimplemented(server_is_sender=False) def test_client_does_not_respond_to_MSG_UNIMPLEMENTED(self): self._send_unimplemented(server_is_sender=True) def _send_client_message(self, message_type): self.setup_test_server(connect_kwargs={}) self.ts._send_message = Mock() # NOTE: this isn't 100% realistic (most of these message types would # have actual other fields in 'em) but it suffices to test the level of # message dispatch we're interested in here. msg = Message() # TODO: really not liking the whole cMSG_XXX vs MSG_XXX duality right # now, esp since the former is almost always just byte_chr(the # latter)...but since that's the case... msg.add_byte(byte_chr(message_type)) self.tc._send_message(msg) # No good way to actually wait for server action (see above tests re: # MSG_UNIMPLEMENTED). Grump. time.sleep(0.1) def _expect_unimplemented(self): # Ensure MSG_UNIMPLEMENTED was sent (implies it hit end of loop instead # of truly handling the given message). # NOTE: When bug present, this will actually be the first thing that # fails (since in many cases actual message handling doesn't involve # sending a message back right away). assert self.ts._send_message.call_count == 1 reply = self.ts._send_message.call_args[0][0] reply.rewind() # Because it's pre-send, not post-receive assert reply.get_byte() == cMSG_UNIMPLEMENTED def test_server_transports_reject_client_message_types(self): # TODO: handle Transport's own tables too, not just its inner auth # handler's table. See TODOs in auth_handler.py for message_type in AuthHandler._client_handler_table: self._send_client_message(message_type) self._expect_unimplemented() # Reset for rest of loop self.tearDown() self.setUp() def test_server_rejects_client_MSG_USERAUTH_SUCCESS(self): self._send_client_message(MSG_USERAUTH_SUCCESS) # Sanity checks assert not self.ts.authenticated assert not self.ts.auth_handler.authenticated # Real fix's behavior self._expect_unimplemented()
def submitJobToFramework(self, **kwargs): jobCommand = 'job' daemonArgs = DaemonArgs(self.config) daemonArgs.command = jobCommand unScheduledJob = kwargs['unScheduledJob'] is_fileFeeder = False fileFeederUploadedFile = None del daemonArgs.param[:] # go through all parameters for parameter in unScheduledJob.parameters.all(): # add parameter to daemonArgs.param if parameter.service and parameter.param_key and parameter.param_value: # check if a file feeder is used if parameter.service == settings.FILE_FEEDER_ID: is_fileFeeder = True fileFeederUploadedFile = parameter.param_value remoteFeederFile = os.path.join(self.sftpRemotePath, parameter.param_value) parameterString = '%s.%s=%s' % ( parameter.service, parameter.param_key, remoteFeederFile ) else: parameterString = '%s.%s=%s' % ( parameter.service, parameter.param_key, parameter.param_value ) self.logger.debug("add parameter string: %s" % parameterString) daemonArgs.param.append([parameterString]) # in case of a filefeeder upload file to framework server if is_fileFeeder: self.logger.debug("is file feeder") sftp = None transport = None try: transport = Transport((self.sftpHost, self.sftpPort)) if self.sftpPassword: transport.connect(username=self.sftpUsername, password=self.sftpPassword) else: privateKey = None if self.sftpPrivateKeyType and self.sftpPrivateKeyType.lower() == 'rsa': privateKey = RSAKey.from_private_key_file(self.sftpPrivateKey, password=self.sftpPrivateKeyPassword ) if self.sftpPrivateKeyType and self.sftpPrivateKeyType.lower() == 'dss': privateKey = DSSKey.from_private_key_file(self.sftpPrivateKey, password=self.sftpPrivateKeyPassword ) transport.connect(username=self.sftpUsername, pkey=privateKey) sftp = SFTPClient.from_transport(transport) filePath = os.path.join( settings.MEDIA_ROOT, fileFeederUploadedFile ) remotePath = os.path.join( self.sftpRemotePath, fileFeederUploadedFile ) self.logger.debug("uploading file from %s to %s on remote machine" % (filePath, remotePath)) sftp.put(filePath, remotePath) # sftp.put(filePath, remotePath, confirm=False) sftp.chmod( remotePath, 0644 ) self.logger.debug("put OK") except IOError as e: self.logger.error("IOError: %s. Will continue with next scheduled job." % e) self.saveJob(Job.FAILED_STATUS, None, unScheduledJob) except PasswordRequiredException as e: self.logger.error("PasswordRequiredException: %s. Will continue with next scheduled job." % e) self.saveJob(Job.FAILED_STATUS, None, unScheduledJob) except SSHException as e: self.logger.error("SSH Exception: %s. Will continue with next scheduled job." % e) self.saveJob(Job.FAILED_STATUS, None, unScheduledJob) except Exception as e: self.logger.error("Unkown SFTP problem. Will continue with next scheduled job. %s" % e) self.saveJob(Job.FAILED_STATUS, None, unScheduledJob) finally: if sftp is not None: sftp.close() if transport is not None: transport.close() # set job workflow daemonArgs.jd_workflow = unScheduledJob.workflow.name frameworkJobId = None try: setattr(daemonArgs, jobCommand, 'submit') frameworkJobId = self.sendFrameworkCommand(jobCommand, daemonArgs) self.saveJob(Job.PROCESSING_STATUS, frameworkJobId, unScheduledJob) except WorkflowNotDeployedException: # The workflow is not deployed in the framework. To prevent the scheduler retrying continuously # we disable this job unScheduledJob.status = Schedule.DEACTIVATE_STATUS unScheduledJob.save() except: self.saveJob(Job.FAILED_STATUS, None, unScheduledJob) finally: daemonArgs.clean(jobCommand) if unScheduledJob.scheduled_start is not None: unScheduledJob.status = Schedule.DEACTIVATED_STATUS unScheduledJob.save()
class AuthTest (unittest.TestCase): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def start_server(self): host_key = RSAKey.from_private_key_file(_support('test_rsa.key')) self.public_host_key = RSAKey(data=host_key.asbytes()) self.ts.add_server_key(host_key) self.event = threading.Event() self.server = NullServer() self.assertTrue(not self.event.is_set()) self.ts.start_server(self.event, self.server) def verify_finished(self): self.event.wait(1.0) self.assertTrue(self.event.is_set()) self.assertTrue(self.ts.is_active()) def test_bad_auth_type(self): """ verify that we get the right exception when an unsupported auth type is requested. """ self.start_server() try: self.tc.connect(hostkey=self.public_host_key, username='******', password='******') self.assertTrue(False) except: etype, evalue, etb = sys.exc_info() self.assertEqual(BadAuthenticationType, etype) self.assertEqual(['publickey'], evalue.allowed_types) def test_bad_password(self): """ verify that a bad password gets the right exception, and that a retry with the right password works. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) try: self.tc.auth_password(username='******', password='******') self.assertTrue(False) except: etype, evalue, etb = sys.exc_info() self.assertTrue(issubclass(etype, AuthenticationException)) self.tc.auth_password(username='******', password='******') self.verify_finished() def test_no_auth(self): """ Test that a no auth connection is created when not providing any credentials. """ self.start_server() try: self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_none(username='******') self.assertTrue(False) except: etype, evalue, etb = sys.exc_info() self.assertTrue(issubclass(etype, AuthenticationException)) self.verify_finished() def test_multipart_auth(self): """ verify that multipart auth works. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password(username='******', password='******') self.assertEqual(['publickey'], remain) key = DSSKey.from_private_key_file(_support('test_dss.key')) remain = self.tc.auth_publickey(username='******', key=key) self.assertEqual([], remain) self.verify_finished() def test_interactive_auth(self): """ verify keyboard-interactive auth works. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) def handler(title, instructions, prompts): self.got_title = title self.got_instructions = instructions self.got_prompts = prompts return ['cat'] remain = self.tc.auth_interactive('commie', handler) self.assertEqual(self.got_title, 'password') self.assertEqual(self.got_prompts, [('Password', False)]) self.assertEqual([], remain) self.verify_finished() def test_interactive_auth_fallback(self): """ verify that a password auth attempt will fallback to "interactive" if password auth isn't supported but interactive is. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password('commie', 'cat') self.assertEqual([], remain) self.verify_finished() def test_auth_utf8(self): """ verify that utf-8 encoding happens in authentication. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password('utf8', _pwd) self.assertEqual([], remain) self.verify_finished() def test_auth_non_utf8(self): """ verify that non-utf-8 encoded passwords can be used for broken servers. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) remain = self.tc.auth_password('non-utf8', '\xff') self.assertEqual([], remain) self.verify_finished() def test_auth_gets_disconnected(self): """ verify that we catch a server disconnecting during auth, and report it as an auth failure. """ self.start_server() self.tc.connect(hostkey=self.public_host_key) try: remain = self.tc.auth_password('bad-server', 'hello') except: etype, evalue, etb = sys.exc_info() self.assertTrue(issubclass(etype, AuthenticationException)) @slow def test_auth_non_responsive(self): """ verify that authentication times out if server takes to long to respond (or never responds). """ self.tc.auth_timeout = 1 # 1 second, to speed up test self.start_server() self.tc.connect() try: remain = self.tc.auth_password('unresponsive-server', 'hello') except: etype, evalue, etb = sys.exc_info() self.assertTrue(issubclass(etype, AuthenticationException)) self.assertTrue('Authentication timeout' in str(evalue))
class TransportTest(ParamikoTest): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def setup_test_server(self, client_options=None, server_options=None): host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) if client_options is not None: client_options(self.tc.get_security_options()) if server_options is not None: server_options(self.ts.get_security_options()) event = threading.Event() self.server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, self.server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_1_security_options(self): o = self.tc.get_security_options() self.assertEquals(type(o), SecurityOptions) self.assert_(('aes256-cbc', 'blowfish-cbc') != o.ciphers) o.ciphers = ('aes256-cbc', 'blowfish-cbc') self.assertEquals(('aes256-cbc', 'blowfish-cbc'), o.ciphers) try: o.ciphers = ('aes256-cbc', 'made-up-cipher') self.assert_(False) except ValueError: pass try: o.ciphers = 23 self.assert_(False) except TypeError: pass def test_2_compute_key(self): self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929L self.tc.H = unhexlify('0C8307CDE6856FF30BA93684EB0F04C2520E9ED3') self.tc.session_id = self.tc.H key = self.tc._compute_key('C', 32) self.assertEquals( '207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995', hexlify(key).upper()) def test_3_simple(self): """ verify that we can establish an ssh link with ourselves across the loopback sockets. this is hardly "simple" but it's simpler than the later tests. :) """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.assertEquals(None, self.tc.get_username()) self.assertEquals(None, self.ts.get_username()) self.assertEquals(False, self.tc.is_authenticated()) self.assertEquals(False, self.ts.is_authenticated()) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) self.assertEquals('slowdive', self.tc.get_username()) self.assertEquals('slowdive', self.ts.get_username()) self.assertEquals(True, self.tc.is_authenticated()) self.assertEquals(True, self.ts.is_authenticated()) def test_3a_long_banner(self): """ verify that a long banner doesn't mess up the handshake. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=str(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.socks.send(LONG_BANNER) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_4_special(self): """ verify that the client can demand odd handshake settings, and can renegotiate keys in mid-stream. """ def force_algorithms(options): options.ciphers = ('aes256-cbc', ) options.digests = ('hmac-md5-96', ) self.setup_test_server(client_options=force_algorithms) self.assertEquals('aes256-cbc', self.tc.local_cipher) self.assertEquals('aes256-cbc', self.tc.remote_cipher) self.assertEquals(12, self.tc.packetizer.get_mac_size_out()) self.assertEquals(12, self.tc.packetizer.get_mac_size_in()) self.tc.send_ignore(1024) self.tc.renegotiate_keys() self.ts.send_ignore(1024) def test_5_keepalive(self): """ verify that the keepalive will be sent. """ self.setup_test_server() self.assertEquals(None, getattr(self.server, '_global_request', None)) self.tc.set_keepalive(1) time.sleep(2) self.assertEquals('*****@*****.**', self.server._global_request) def test_6_exec_command(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) try: chan.exec_command('no') self.assert_(False) except SSHException, x: pass chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() f = chan.makefile() self.assertEquals('Hello there.\n', f.readline()) self.assertEquals('', f.readline()) f = chan.makefile_stderr() self.assertEquals('This is on stderr.\n', f.readline()) self.assertEquals('', f.readline()) # now try it with combined stdout/stderr chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send('Hello there.\n') schan.send_stderr('This is on stderr.\n') schan.close() chan.set_combine_stderr(True) f = chan.makefile() self.assertEquals('Hello there.\n', f.readline()) self.assertEquals('This is on stderr.\n', f.readline()) self.assertEquals('', f.readline())
class SFTPStore(Store): """implements the sftp:// storage backend configuration via openssh/sftp style urls and .ssh/config files does not support password authentication or password protected authentication keys""" def __init__(self, url, **kw): if self.netloc.find('@') != -1: user, self.netloc = self.netloc.split('@') else: user = None self.config = SSHHostConfig(self.netloc, user) host_keys = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts')) try: self.hostkey = list(host_keys[self.config['hostkeyalias']].values())[0] except: print(str(self.config)) raise if('identityfile' in self.config): key_file = os.path.expanduser(self.config['identityfile']) #not really nice but i don't see a cleaner way atm... try: self.auth_key = RSAKey (key_file) except SSHException as e: if e.message == 'Unable to parse file': self.auth_key = DSAKey (key_file) else: raise else: filename = os.path.expanduser('~/.ssh/id_rsa') if os.path.exists(filename): self.auth_key = RSAKey(filename) else: filename = os.path.expanduser('~/.ssh/id_dsa') if (os.path.exists(filename)): self.auth_key = DSSKey (filename) self.__connect() def __connect(self): self.t = Transport((self.config['hostname'], self.config['port'])) self.t.connect(username = self.config['user'], pkey = self.auth_key) self.client = SFTPClient.from_transport(self.t) self.client.chdir(self.path) def __build_fn(self, name): return "%s/%s" % (self.path, name) def list(self, type): return list(filter(type_patterns[type].match, self.client.listdir(self.path))) def get(self, type, name): return self.client.open(self.__build_fn(name), mode = 'rb') def put(self, type, name, fp): remote_file = self.client.open(self.__build_fn(name), mode = 'wb') buf = fp.read(4096) while (len(buf) > 0): remote_file.write(buf) buf = fp.read(4096) remote_file.close() def delete(self, type, name): self.client.remove(self.__build_fn(name)) def stat(self, type, name): try: stat = self.client.stat(self.__build_fn(name)) return {'size': stat.st_size} except IOError: raise NotFoundError def close(self): """connection has to be explicitly closed, otherwise it will hold the process running idefinitly""" self.client.close() self.t.close()
class sftp_client: logger = logging.getLogger('sftprobe.client') def __init__(self, servaddr, username="", password="", key=None): self.transport_ = Transport(servaddr) self.key_ = key self.user_ = username self.pwd_ = password self.session_ = None @classmethod def get_command(classobj, cmdstr): cmdustr = cmdstr.upper() if cmdustr == "PUT": return sftp_commands.Put elif cmdustr == "GET": return sftp_commands.Get elif cmdustr == "LS": return sftp_commands.List elif cmdustr == "CD": return sftp_commands.ChangeDir else: raise Exception("Unknown SFTP command {0}".format(cmdstr)) def connect(self): if not self.session_ and not self.transport_.is_active(): if self.key_: # Try private key, if available self.transport_.connect(username=self.user_, pkey=self.key_) elif len(self.pwd_) > 0: # Next, try password, if available self.transport_.connect(username=self.user_, password=self.pwd_) else: # Try just the user name self.transport_.connect(username=self.user_) self.session_ = SFTPClient.from_transport(self.transport_) def close(self): if self.session_: self.session_.close() self.session_ = None self.transport_.close() self.transport_ = None def get_status(self): if self.transport_.is_active(): peer = self.transport_.getpeername() return "Connected to host [{0}], port {1}.".format( peer[0], peer[1]) else: return "Not connected." def exec_sftp_cmd(self, cmd, **kwargs): self.connect() if cmd == sftp_commands.List: return self.session_.listdir(kwargs["RemotePath"]) elif cmd == sftp_commands.ChangeDir: return self.session_.chdir(kwargs["RemotePath"]) elif cmd == sftp_commands.Get: file_attrs = self.session_.get(kwargs["RemotePath"], kwargs["LocalPath"]) elif cmd == sftp_commands.Put: file_attrs = self.session_.put(kwargs["LocalPath"], kwargs["RemotePath"]) def do_changedir(self, path): return self.exec_sftp_cmd(sftp_commands.ChangeDir, RemotePath=path if len(path) > 0 else None) def do_listdir(self, path): return self.exec_sftp_cmd(sftp_commands.List, RemotePath=path if len(path) > 0 else ".") def do_get(self, rpth, lpth): return self.exec_sftp_cmd(sftp_commands.Get, RemotePath=rpth, LocalPath=lpth) def do_put(self, lpth, rpth): return self.exec_sftp_cmd(sftp_commands.Put, LocalPath=lpth, RemotePath=rpth)
class TransportTest(ParamikoTest): def setUp(self): self.socks = LoopSocket() self.sockc = LoopSocket() self.sockc.link(self.socks) self.tc = Transport(self.sockc) self.ts = Transport(self.socks) def tearDown(self): self.tc.close() self.ts.close() self.socks.close() self.sockc.close() def setup_test_server(self, client_options=None, server_options=None): host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=bytes(host_key)) self.ts.add_server_key(host_key) if client_options is not None: client_options(self.tc.get_security_options()) if server_options is not None: server_options(self.ts.get_security_options()) event = threading.Event() self.server = NullServer() self.assert_(not event.isSet()) self.ts.start_server(event, self.server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_1_security_options(self): o = self.tc.get_security_options() self.assertEquals(type(o), SecurityOptions) self.assert_((b'aes256-cbc', b'blowfish-cbc') != o.ciphers) o.ciphers = (b'aes256-cbc', b'blowfish-cbc') self.assertEquals((b'aes256-cbc', b'blowfish-cbc'), o.ciphers) try: o.ciphers = (b'aes256-cbc', b'made-up-cipher') self.assert_(False) except ValueError: pass try: o.ciphers = 23 self.assert_(False) except TypeError: pass def test_2_compute_key(self): self.tc.K = 123281095979686581523377256114209720774539068973101330872763622971399429481072519713536292772709507296759612401802191955568143056534122385270077606457721553469730659233569339356140085284052436697480759510519672848743794433460113118986816826624865291116513647975790797391795651716378444844877749505443714557929 self.tc.H = unhexlify(b'0C8307CDE6856FF30BA93684EB0F04C2520E9ED3') self.tc.session_id = self.tc.H key = self.tc._compute_key(b'C', 32) self.assertEquals(b'207E66594CA87C44ECCBA3B3CD39FDDB378E6FDB0F97C54B2AA0CFBF900CD995', hexlify(key).upper()) def test_3_simple(self): """ verify that we can establish an ssh link with ourselves across the loopback sockets. this is hardly "simple" but it's simpler than the later tests. :) """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=bytes(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.assertEquals(None, self.tc.get_username()) self.assertEquals(None, self.ts.get_username()) self.assertEquals(False, self.tc.is_authenticated()) self.assertEquals(False, self.ts.is_authenticated()) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) self.assertEquals('slowdive', self.tc.get_username()) self.assertEquals('slowdive', self.ts.get_username()) self.assertEquals(True, self.tc.is_authenticated()) self.assertEquals(True, self.ts.is_authenticated()) def test_3a_long_banner(self): """ verify that a long banner doesn't mess up the handshake. """ host_key = RSAKey.from_private_key_file('tests/test_rsa.key') public_host_key = RSAKey(data=bytes(host_key)) self.ts.add_server_key(host_key) event = threading.Event() server = NullServer() self.assert_(not event.isSet()) self.socks.send(LONG_BANNER) self.ts.start_server(event, server) self.tc.connect(hostkey=public_host_key, username='******', password='******') event.wait(1.0) self.assert_(event.isSet()) self.assert_(self.ts.is_active()) def test_4_special(self): """ verify that the client can demand odd handshake settings, and can renegotiate keys in mid-stream. """ def force_algorithms(options): options.ciphers = (b'aes256-cbc',) options.digests = (b'hmac-md5-96',) self.setup_test_server(client_options=force_algorithms) self.assertEquals(b'aes256-cbc', self.tc.local_cipher) self.assertEquals(b'aes256-cbc', self.tc.remote_cipher) self.assertEquals(12, self.tc.packetizer.get_mac_size_out()) self.assertEquals(12, self.tc.packetizer.get_mac_size_in()) self.tc.send_ignore(1024) self.tc.renegotiate_keys() self.ts.send_ignore(1024) def test_5_keepalive(self): """ verify that the keepalive will be sent. """ self.setup_test_server() self.assertEquals(None, getattr(self.server, '_global_request', None)) self.tc.set_keepalive(1) time.sleep(2) self.assertEquals(b'*****@*****.**', self.server._global_request) def test_6_exec_command(self): """ verify that exec_command() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) try: chan.exec_command('no') self.assert_(False) except SSHException as x: pass chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send(b'Hello there.\n') schan.send_stderr(b'This is on stderr.\n') schan.close() f = chan.makefile() self.assertEquals(b'Hello there.\n', f.readline()) self.assertEquals(b'', f.readline()) f = chan.makefile_stderr() self.assertEquals(b'This is on stderr.\n', f.readline()) self.assertEquals(b'', f.readline()) # now try it with combined stdout/stderr chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) schan.send(b'Hello there.\n') schan.send_stderr(b'This is on stderr.\n') schan.close() chan.set_combine_stderr(True) f = chan.makefile() self.assertEquals(b'Hello there.\n', f.readline()) self.assertEquals(b'This is on stderr.\n', f.readline()) self.assertEquals(b'', f.readline()) def test_7_invoke_shell(self): """ verify that invoke_shell() does something reasonable. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) chan.send(b'communist j. cat\n') f = schan.makefile() self.assertEquals(b'communist j. cat\n', f.readline()) chan.close() self.assertEquals(b'', f.readline()) def test_8_channel_exception(self): """ verify that ChannelException is thrown for a bad open-channel request. """ self.setup_test_server() try: chan = self.tc.open_channel(b'bogus') self.fail('expected exception') except ChannelException as x: self.assert_(x.code == OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED) def test_9_exit_status(self): """ verify that get_exit_status() works. """ self.setup_test_server() chan = self.tc.open_session() schan = self.ts.accept(1.0) chan.exec_command('yes') schan.send(b'Hello there.\n') self.assert_(not chan.exit_status_ready()) # trigger an EOF schan.shutdown_read() schan.shutdown_write() schan.send_exit_status(23) schan.close() f = chan.makefile() self.assertEquals(b'Hello there.\n', f.readline()) self.assertEquals(b'', f.readline()) count = 0 while not chan.exit_status_ready(): time.sleep(0.1) count += 1 if count > 50: raise Exception("timeout") self.assertEquals(23, chan.recv_exit_status()) chan.close() def test_A_select(self): """ verify that select() on a channel works. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEquals([], r) self.assertEquals([], w) self.assertEquals([], e) schan.send(b'hello\n') # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEquals([chan], r) self.assertEquals([], w) self.assertEquals([], e) self.assertEquals(b'hello\n', chan.recv(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEquals([], r) self.assertEquals([], w) self.assertEquals([], e) schan.close() # detect eof? for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEquals([chan], r) self.assertEquals([], w) self.assertEquals([], e) self.assertEquals(b'', chan.recv(16)) # make sure the pipe is still open for now... p = chan._pipe self.assertEquals(False, p._closed) chan.close() # ...and now is closed. self.assertEquals(True, p._closed) def test_B_renegotiate(self): """ verify that a transport can correctly renegotiate mid-stream. """ self.setup_test_server() self.tc.packetizer.REKEY_BYTES = 16384 chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) self.assertEquals(self.tc.H, self.tc.session_id) for i in range(20): chan.send(b'x' * 1024) chan.close() # allow a few seconds for the rekeying to complete for i in range(50): if self.tc.H != self.tc.session_id: break time.sleep(0.1) self.assertNotEquals(self.tc.H, self.tc.session_id) schan.close() def test_C_compression(self): """ verify that zlib compression is basically working. """ def force_compression(o): o.compression = (b'zlib',) self.setup_test_server(force_compression, force_compression) chan = self.tc.open_session() chan.exec_command(b'yes') schan = self.ts.accept(1.0) bytes = self.tc.packetizer._Packetizer__sent_bytes chan.send(b'x' * 1024) bytes2 = self.tc.packetizer._Packetizer__sent_bytes # tests show this is actually compressed to *52 bytes*! including packet overhead! nice!! :) self.assert_(bytes2 - bytes < 1024) self.assertEquals(52, bytes2 - bytes) chan.close() schan.close() def test_D_x11(self): """ verify that an x11 port can be requested and opened. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command(b'yes') schan = self.ts.accept(1.0) requested = [] def handler(c, addr): requested.append(addr) self.tc._queue_incoming_channel(c) self.assertEquals(None, getattr(self.server, '_x11_screen_number', None)) cookie = chan.request_x11(0, single_connection=True, handler=handler) self.assertEquals(0, self.server._x11_screen_number) self.assertEquals(b'MIT-MAGIC-COOKIE-1', self.server._x11_auth_protocol) self.assertEquals(cookie, self.server._x11_auth_cookie) self.assertEquals(True, self.server._x11_single_connection) x11_server = self.ts.open_x11_channel(('localhost', 6093)) x11_client = self.tc.accept() self.assertEquals('localhost', requested[0][0]) self.assertEquals(6093, requested[0][1]) x11_server.send(b'hello') self.assertEquals(b'hello', x11_client.recv(5)) x11_server.close() x11_client.close() chan.close() schan.close() def test_E_reverse_port_forwarding(self): """ verify that a client can ask the server to open a reverse port for forwarding. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) requested = [] def handler(c, origin_addr, server_addr): requested.append(origin_addr) requested.append(server_addr) self.tc._queue_incoming_channel(c) port = self.tc.request_port_forward('127.0.0.1', 0, handler) self.assertEquals(port, self.server._listen.getsockname()[1]) cs = socket.socket() cs.connect((b'127.0.0.1', port)) ss, _ = self.server._listen.accept() sch = self.ts.open_forwarded_tcpip_channel(ss.getsockname(), ss.getpeername()) cch = self.tc.accept() sch.send(b'hello') self.assertEquals(b'hello', cch.recv(5)) sch.close() cch.close() ss.close() cs.close() # now cancel it. self.tc.cancel_port_forward(b'127.0.0.1', port) self.assertTrue(self.server._listen is None) def test_F_port_forwarding(self): """ verify that a client can forward new connections from a locally- forwarded port. """ self.setup_test_server() chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) # open a port on the "server" that the client will ask to forward to. greeting_server = socket.socket() greeting_server.bind(('127.0.0.1', 0)) greeting_server.listen(1) greeting_port = greeting_server.getsockname()[1] cs = self.tc.open_channel(b'direct-tcpip', ('127.0.0.1', greeting_port), ('', 9000)) sch = self.ts.accept(1.0) cch = socket.socket() cch.connect(self.server._tcpip_dest) ss, _ = greeting_server.accept() ss.send(b'Hello!\n') ss.close() sch.send(cch.recv(8192)) sch.close() self.assertEquals(b'Hello!\n', cs.recv(7)) cs.close() def test_G_stderr_select(self): """ verify that select() on a channel works even if only stderr is receiving data. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) # nothing should be ready r, w, e = select.select([chan], [], [], 0.1) self.assertEquals([], r) self.assertEquals([], w) self.assertEquals([], e) schan.send_stderr(b'hello\n') # something should be ready now (give it 1 second to appear) for i in range(10): r, w, e = select.select([chan], [], [], 0.1) if chan in r: break time.sleep(0.1) self.assertEquals([chan], r) self.assertEquals([], w) self.assertEquals([], e) self.assertEquals(b'hello\n', chan.recv_stderr(6)) # and, should be dead again now r, w, e = select.select([chan], [], [], 0.1) self.assertEquals([], r) self.assertEquals([], w) self.assertEquals([], e) schan.close() chan.close() def test_H_send_ready(self): """ verify that send_ready() indicates when a send would not block. """ self.setup_test_server() chan = self.tc.open_session() chan.invoke_shell() schan = self.ts.accept(1.0) self.assertEquals(chan.send_ready(), True) total = 0 K = b'*' * 1024 while total < 1024 * 1024: chan.send(K) total += len(K) if not chan.send_ready(): break self.assert_(total < 1024 * 1024) schan.close() chan.close() self.assertEquals(chan.send_ready(), True) def test_I_rekey_deadlock(self): """ Regression test for deadlock when in-transit messages are received after MSG_KEXINIT is sent Note: When this test fails, it may leak threads. """ # Test for an obscure deadlocking bug that can occur if we receive # certain messages while initiating a key exchange. # # The deadlock occurs as follows: # # In the main thread: # 1. The user's program calls Channel.send(), which sends # MSG_CHANNEL_DATA to the remote host. # 2. Packetizer discovers that REKEY_BYTES has been exceeded, and # sets the __need_rekey flag. # # In the Transport thread: # 3. Packetizer notices that the __need_rekey flag is set, and raises # NeedRekeyException. # 4. In response to NeedRekeyException, the transport thread sends # MSG_KEXINIT to the remote host. # # On the remote host (using any SSH implementation): # 5. The MSG_CHANNEL_DATA is received, and MSG_CHANNEL_WINDOW_ADJUST is sent. # 6. The MSG_KEXINIT is received, and a corresponding MSG_KEXINIT is sent. # # In the main thread: # 7. The user's program calls Channel.send(). # 8. Channel.send acquires Channel.lock, then calls Transport._send_user_message(). # 9. Transport._send_user_message waits for Transport.clear_to_send # to be set (i.e., it waits for re-keying to complete). # Channel.lock is still held. # # In the Transport thread: # 10. MSG_CHANNEL_WINDOW_ADJUST is received; Channel._window_adjust # is called to handle it. # 11. Channel._window_adjust tries to acquire Channel.lock, but it # blocks because the lock is already held by the main thread. # # The result is that the Transport thread never processes the remote # host's MSG_KEXINIT packet, because it becomes deadlocked while # handling the preceding MSG_CHANNEL_WINDOW_ADJUST message. # We set up two separate threads for sending and receiving packets, # while the main thread acts as a watchdog timer. If the timer # expires, a deadlock is assumed. class SendThread(threading.Thread): def __init__(self, chan, iterations, done_event): threading.Thread.__init__(self, None, None, self.__class__.__name__) self.setDaemon(True) self.chan = chan self.iterations = iterations self.done_event = done_event self.watchdog_event = threading.Event() self.last = None def run(self): try: for i in range(1, 1+self.iterations): if self.done_event.isSet(): break self.watchdog_event.set() #print i, "SEND" self.chan.send(b"x" * 2048) finally: self.done_event.set() self.watchdog_event.set() class ReceiveThread(threading.Thread): def __init__(self, chan, done_event): threading.Thread.__init__(self, None, None, self.__class__.__name__) self.setDaemon(True) self.chan = chan self.done_event = done_event self.watchdog_event = threading.Event() def run(self): try: while not self.done_event.isSet(): if self.chan.recv_ready(): chan.recv(65536) self.watchdog_event.set() else: if random.randint(0, 1): time.sleep(random.randint(0, 500) / 1000.0) finally: self.done_event.set() self.watchdog_event.set() self.setup_test_server() self.ts.packetizer.REKEY_BYTES = 2048 chan = self.tc.open_session() chan.exec_command('yes') schan = self.ts.accept(1.0) # Monkey patch the client's Transport._handler_table so that the client # sends MSG_CHANNEL_WINDOW_ADJUST whenever it receives an initial # MSG_KEXINIT. This is used to simulate the effect of network latency # on a real MSG_CHANNEL_WINDOW_ADJUST message. self.tc._handler_table = self.tc._handler_table.copy() # copy per-class dictionary _negotiate_keys = self.tc._handler_table[MSG_KEXINIT] def _negotiate_keys_wrapper(self, m): if self.local_kex_init is None: # Remote side sent KEXINIT # Simulate in-transit MSG_CHANNEL_WINDOW_ADJUST by sending it # before responding to the incoming MSG_KEXINIT. m2 = Message() m2.add_byte(chr(MSG_CHANNEL_WINDOW_ADJUST).encode()) m2.add_int(chan.remote_chanid) m2.add_int(1) # bytes to add self._send_message(m2) return _negotiate_keys(self, m) self.tc._handler_table[MSG_KEXINIT] = _negotiate_keys_wrapper # Parameters for the test iterations = 500 # The deadlock does not happen every time, but it # should after many iterations. timeout = 5 # This event is set when the test is completed done_event = threading.Event() # Start the sending thread st = SendThread(schan, iterations, done_event) st.start() # Start the receiving thread rt = ReceiveThread(chan, done_event) rt.start() # Act as a watchdog timer, checking deadlocked = False while not deadlocked and not done_event.isSet(): for event in (st.watchdog_event, rt.watchdog_event): event.wait(timeout) if done_event.isSet(): break if not event.isSet(): deadlocked = True break event.clear() # Tell the threads to stop (if they haven't already stopped). Note # that if one or more threads are deadlocked, they might hang around # forever (until the process exits). done_event.set() # Assertion: We must not have detected a timeout. self.assertFalse(deadlocked) # Close the channels schan.close() chan.close()
class IrmaSFTP(FTPInterface): """Irma SFTP handler This class handles the connection with a sftp server functions for interacting with it. """ _Exception = IrmaSFTPError # ================================== # Constructor and Destructor stuff # ================================== def __init__(self, host, port, auth, key_path, user, passwd, dst_user=None, upload_path='uploads', hash_check=False, autoconnect=True): self._conn = None self._client = None super().__init__(host, port, auth, key_path, user, passwd, dst_user, upload_path, hash_check, autoconnect) def connected(self): return self._conn is not None # ============================ # Overridden private methods # ============================ def _connect(self): self._conn = Transport((self._host, self._port)) self._conn.window_size = pow(2, 27) self._conn.packetizer.REKEY_BYTES = pow(2, 32) self._conn.packetizer.REKEY_PACKETS = pow(2, 32) if self._auth == 'key': pkey = RSAKey.from_private_key_file(self._key_path) self._conn.connect(username=self._user, pkey=pkey) else: self._conn.connect(username=self._user, password=self._passwd) self._client = SFTPClient.from_transport(self._conn) def _disconnect(self, *, force=False): self._client = None if not force: self._conn.close() self._conn = None def _upload(self, remote, fobj): self._client.putfo(fobj, remote) def _download(self, remote, fobj): self._client.getfo(remote, fobj) def _ls(self, remote): return self._client.listdir(remote) def _is_file(self, remote): return not self._is_dir(remote) def _is_dir(self, remote): st = self._client.stat(remote) return stat.S_ISDIR(st.st_mode) def _rm(self, remote): self._client.remove(remote) def _rmdir(self, remote): self._client.rmdir(remote) def _mkdir(self, remote): self._client.mkdir(remote) def _mv(self, oldremote, newremote): self._client.rename(oldremote, newremote)