def login(self, server, username, password='', original_prompt=r'[#$]', login_timeout=10, auto_prompt_reset=True, sync_multiplier=1): cmd = 'telnet -l {} {}'.format(username, server) spawn._spawn(self, cmd) # pylint: disable=protected-access i = self.expect('(?i)(?:password)', timeout=login_timeout) if i == 0: self.sendline(password) i = self.expect([original_prompt, 'Login incorrect'], timeout=login_timeout) else: raise pxssh.ExceptionPxssh('could not log in: did not see a password prompt') if i: raise pxssh.ExceptionPxssh('could not log in: password was incorrect') if not self.sync_original_prompt(sync_multiplier): self.close() raise pxssh.ExceptionPxssh('could not synchronize with original prompt') if auto_prompt_reset: if not self.set_unique_prompt(): self.close() message = 'could not set shell prompt (recieved: {}, expected: {}).' raise pxssh.ExceptionPxssh(message.format(self.before, self.PROMPT)) return True
def login(self, server, username, password='', login_timeout=10, auto_prompt_reset=True, sync_multiplier=1, port=23): args = ['telnet'] if username is not None: args += ['-l', username] args += [server, str(port)] cmd = ' '.join(args) spawn._spawn(self, cmd) # pylint: disable=protected-access if password is None: i = self.expect([self.original_prompt, 'Login timed out'], timeout=login_timeout) else: i = self.expect('(?i)(?:password)', timeout=login_timeout) if i == 0: self.sendline(password) i = self.expect([self.original_prompt, 'Login incorrect'], timeout=login_timeout) else: raise pxssh.ExceptionPxssh('could not log in: did not see a password prompt') if i: raise pxssh.ExceptionPxssh('could not log in: password was incorrect') if not self.sync_original_prompt(sync_multiplier): self.close() raise pxssh.ExceptionPxssh('could not synchronize with original prompt') if auto_prompt_reset: if not self.set_unique_prompt(): self.close() message = 'could not set shell prompt (recieved: {}, expected: {}).' raise pxssh.ExceptionPxssh(message.format(self.before, self.PROMPT)) return True
def adbLogin(self, device_id, original_prompt=r"[#$]"): """login process for android devices over adb""" adb_id = get_adb_id(device_id) subprocess.call(["adb", "-s", adb_id, "root"]) time.sleep(2) subprocess.call(["adb", "-s", adb_id, "wait-for-device"]) spawn._spawn(self, "adb -s " + adb_id + " shell") index = self.expect([original_prompt, TIMEOUT, EOF]) if index == 1 or index == 2: return False return True
def login(self, server, username, password='', login_timeout=10, auto_prompt_reset=True, sync_multiplier=1, port=23): args = ['telnet'] if username is not None: args += ['-l', username] args += [server, str(port)] cmd = ' '.join(args) spawn._spawn(self, cmd) # pylint: disable=protected-access try: i = self.expect('(?i)(?:password)', timeout=login_timeout) if i == 0: self.sendline(password) i = self.expect([self.original_prompt, 'Login incorrect'], timeout=login_timeout) if i: raise pxssh.ExceptionPxssh( 'could not log in: password was incorrect') except TIMEOUT: if not password: # No password promt before TIMEOUT & no password provided # so assume everything is okay pass else: raise pxssh.ExceptionPxssh( 'could not log in: did not see a password prompt') if not self.sync_original_prompt(sync_multiplier): self.close() raise pxssh.ExceptionPxssh( 'could not synchronize with original prompt') if auto_prompt_reset: if not self.set_unique_prompt(): self.close() message = 'could not set shell prompt (recieved: {}, expected: {}).' raise pxssh.ExceptionPxssh( message.format(self.before, self.PROMPT)) return True
def login(self, host, auto_prompt_reset=True): # pylint: disable=arguments-differ """ Radically simplified login without the 'New certificate -- always accept it.' stuff.""" self._original_host = host spawn._spawn(self, 'ssh', args=[host]) # pylint: disable=protected-access if not self.sync_original_prompt(): self.close() raise pxssh.ExceptionPxssh('could not synchronize with original prompt') # We appear to be in. # Set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise pxssh.ExceptionPxssh('could not set shell prompt') return True
def login(self, server, username, password='', original_prompt=r'[#$]', login_timeout=10, auto_prompt_reset=True, sync_multiplier=1, port=23): cmd = 'telnet -l {} {} {}'.format(username, server, port) spawn._spawn(self, cmd) # pylint: disable=protected-access try: i = self.expect('(?i)(?:password)', timeout=login_timeout) if i == 0: self.sendline(password) i = self.expect([original_prompt, 'Login incorrect'], timeout=login_timeout) if i: raise pxssh.ExceptionPxssh( 'could not log in: password was incorrect') except TIMEOUT: if not password: # There was no password prompt before TIMEOUT, and we didn't # have a password to enter. Assume everything is OK. pass else: raise pxssh.ExceptionPxssh( 'could not log in: did not see a password prompt') if not self.sync_original_prompt(sync_multiplier): self.close() raise pxssh.ExceptionPxssh( 'could not synchronize with original prompt') if auto_prompt_reset: if not self.set_unique_prompt(): self.close() message = 'could not set shell prompt (recieved: {}, expected: {}).' raise pxssh.ExceptionPxssh( message.format(self.before, self.PROMPT)) return True
def login(self,server,username,passwd,timeout=30): host=username+"@"+server cmd="sftp "+host spawn._spawn(self, cmd) try: i=self.expect(["Are you sure you want to continue connecting","Permanently added","password"],5) if i==0: self.sendline("yes") self.expect(["Are you sure you want to continue connecting","Permanently added","password"],5) if i==1: self.sendline("yes") self.expect(["Are you sure you want to continue connecting","Permanently added","password"],5) if i==2: self.sendline(passwd) self.expect(self.PROMPT,5) except pexpect.TIMEOUT: self.err_str=self.before return Sftp.LOGIN_ERROR self.logined=True return Sftp.OP_OK
async def login(self, host: str, username=None, password="", login_timeout=10) -> bool: """Log into the Ruckus device.""" spawn._spawn(self, f"ssh {host}") login_regex_array = [ "Please login: "******"(?i)are you sure you want to continue connecting", EOF, TIMEOUT, ] i = await self.expect(login_regex_array, timeout=login_timeout, async_=True) if i == 1: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") i = await self.expect(login_regex_array, timeout=login_timeout, async_=True) if i == 2: raise ConnectionError(CONNECT_ERROR_EOF) if i == 3: raise ConnectionError(CONNECT_ERROR_TIMEOUT) self.sendline(username) await self.expect("Password: "******"> ", "Login incorrect"], async_=True) if i == 1: raise AuthenticationError(LOGIN_ERROR_LOGIN_INCORRECT) return True
def login(self, server, username=None, password='', terminal_type='ansi', original_prompt=r"[#$]", login_timeout=10, port=None, auto_prompt_reset=True, ssh_key=None, quiet=True, sync_multiplier=1, check_local_ip=True, password_regex=r'(?i)(?:password:)|(?:passphrase for key)', ssh_tunnels={}, spawn_local_ssh=True, sync_original_prompt=True, ssh_config=None, cmd='ssh'): '''This logs the user into the given server. It uses 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called :file:`~/.hushlogin` on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an :class:`ExceptionPxssh` exception. In some situations it is not possible or desirable to reset the original prompt. In this case, pass ``auto_prompt_reset=False`` to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the :meth:`prompt` method. If the original prompt is not reset then this will disable the :meth:`prompt` method unless you manually set the :attr:`PROMPT` attribute. Set ``password_regex`` if there is a MOTD message with `password` in it. Changing this is like playing in traffic, don't (p)expect it to match straight away. If you require to connect to another SSH server from the your original SSH connection set ``spawn_local_ssh`` to `False` and this will use your current session to do so. Setting this option to `False` and not having an active session will trigger an error. Set ``ssh_key`` to a file path to an SSH private key to use that SSH key for the session authentication. Set ``ssh_key`` to `True` to force passing the current SSH authentication socket to the desired ``hostname``. Set ``ssh_config`` to a file path string of an SSH client config file to pass that file to the client to handle itself. You may set any options you wish in here, however doing so will require you to post extra information that you may not want to if you run into issues. Alter the ``cmd`` to change the ssh client used, or to prepend it with network namespaces. For example ```cmd="ip netns exec vlan2 ssh"``` to execute the ssh in network namespace named ```vlan```. ''' session_regex_array = [ "(?i)are you sure you want to continue connecting", original_prompt, password_regex, "(?i)permission denied", "(?i)terminal type", TIMEOUT ] session_init_regex_array = [] session_init_regex_array.extend(session_regex_array) session_init_regex_array.extend( ["(?i)connection closed by remote host", EOF]) ssh_options = ''.join( [" -o '%s=%s'" % (o, v) for (o, v) in self.options.items()]) if quiet: ssh_options = ssh_options + ' -q' if not check_local_ip: ssh_options = ssh_options + " -o'NoHostAuthenticationForLocalhost=yes'" if self.force_password: ssh_options = ssh_options + ' ' + self.SSH_OPTS if ssh_config is not None: if spawn_local_ssh and not os.path.isfile(ssh_config): raise ExceptionPxssh( 'SSH config does not exist or is not a file.') ssh_options = ssh_options + ' -F ' + ssh_config if port is not None: ssh_options = ssh_options + ' -p %s' % (str(port)) if ssh_key is not None: # Allow forwarding our SSH key to the current session if ssh_key == True: ssh_options = ssh_options + ' -A' else: if spawn_local_ssh and not os.path.isfile(ssh_key): raise ExceptionPxssh( 'private ssh key does not exist or is not a file.') ssh_options = ssh_options + ' -i %s' % (ssh_key) # SSH tunnels, make sure you know what you're putting into the lists # under each heading. Do not expect these to open 100% of the time, # The port you're requesting might be bound. # # The structure should be like this: # { 'local': ['2424:localhost:22'], # Local SSH tunnels # 'remote': ['2525:localhost:22'], # Remote SSH tunnels # 'dynamic': [8888] } # Dynamic/SOCKS tunnels if ssh_tunnels != {} and isinstance({}, type(ssh_tunnels)): tunnel_types = {'local': 'L', 'remote': 'R', 'dynamic': 'D'} for tunnel_type in tunnel_types: cmd_type = tunnel_types[tunnel_type] if tunnel_type in ssh_tunnels: tunnels = ssh_tunnels[tunnel_type] for tunnel in tunnels: if spawn_local_ssh == False: tunnel = quote(str(tunnel)) ssh_options = ssh_options + ' -' + cmd_type + ' ' + str( tunnel) if username is not None: ssh_options = ssh_options + ' -l ' + username elif ssh_config is None: raise TypeError('login() needs either a username or an ssh_config') else: # make sure ssh_config has an entry for the server with a username with open(ssh_config, 'rt') as f: lines = [l.strip() for l in f.readlines()] server_regex = r'^Host\s+%s\s*$' % server user_regex = r'^User\s+\w+\s*$' config_has_server = False server_has_username = False for line in lines: if not config_has_server and re.match(server_regex, line, re.IGNORECASE): config_has_server = True elif config_has_server and 'hostname' in line.lower(): pass elif config_has_server and re.match(server_regex, line, re.IGNORECASE): server_has_username = False # insurance break # we have left the relevant section elif config_has_server and re.match(user_regex, line, re.IGNORECASE): server_has_username = True break if lines: del line del lines if not config_has_server: raise TypeError('login() ssh_config has no Host entry for %s' % server) elif not server_has_username: raise TypeError('login() ssh_config has no user entry for %s' % server) cmd += " %s %s" % (ssh_options, server) if self.debug_command_string: return (cmd) # Are we asking for a local ssh command or to spawn one in another session? if spawn_local_ssh: spawn._spawn(self, cmd) else: self.sendline(cmd) # This does not distinguish between a remote server 'password' prompt # and a local ssh 'passphrase' prompt (for unlocking a private key). i = self.expect(session_init_regex_array, timeout=login_timeout) # First phase if i == 0: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") i = self.expect(session_regex_array) if i == 2: # password or passphrase self.sendline(password) i = self.expect(session_regex_array) if i == 4: self.sendline(terminal_type) i = self.expect(session_regex_array) if i == 7: self.close() raise ExceptionPxssh('Could not establish connection to host') # Second phase if i == 0: # This is weird. This should not happen twice in a row. self.close() raise ExceptionPxssh( 'Weird error. Got "are you sure" prompt twice.') elif i == 1: # can occur if you have a public key pair set to authenticate. ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. pass elif i == 2: # password prompt again # For incorrect passwords, some ssh servers will # ask for the password again, others return 'denied' right away. # If we get the password prompt again then this means # we didn't get the password right the first time. self.close() raise ExceptionPxssh('password refused') elif i == 3: # permission denied -- password was bad. self.close() raise ExceptionPxssh('permission denied') elif i == 4: # terminal type again? WTF? self.close() raise ExceptionPxssh( 'Weird error. Got "terminal type" prompt twice.') elif i == 5: # Timeout #This is tricky... I presume that we are at the command-line prompt. #It may be that the shell prompt was so weird that we couldn't match #it. Or it may be that we couldn't log in for some other reason. I #can't be sure, but it's safe to guess that we did login because if #I presume wrong and we are not logged in then this should be caught #later when I try to set the shell prompt. pass elif i == 6: # Connection closed by remote host self.close() raise ExceptionPxssh('connection closed') else: # Unexpected self.close() raise ExceptionPxssh('unexpected login response') if sync_original_prompt: if not self.sync_original_prompt(sync_multiplier): self.close() raise ExceptionPxssh( 'could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxssh('could not set shell prompt ' '(received: %r, expected: %r).' % ( self.before, self.PROMPT, )) return True
def login (self, server, username, password='', terminal_type='ansi', original_prompt=r"[#$]", login_timeout=10, port=None, auto_prompt_reset=True, ssh_key=None, quiet=True, sync_multiplier=1, check_local_ip=True): '''This logs the user into the given server. It uses 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called :file:`~/.hushlogin` on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an :class:`ExceptionPxssh` exception. In some situations it is not possible or desirable to reset the original prompt. In this case, pass ``auto_prompt_reset=False`` to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the :meth:`prompt` method. If the original prompt is not reset then this will disable the :meth:`prompt` method unless you manually set the :attr:`PROMPT` attribute. ''' ssh_options = ''.join([" -o '%s=%s'" % (o, v) for (o, v) in self.options.items()]) if quiet: ssh_options = ssh_options + ' -q' if not check_local_ip: ssh_options = ssh_options + " -o'NoHostAuthenticationForLocalhost=yes'" if self.force_password: ssh_options = ssh_options + ' ' + self.SSH_OPTS if port is not None: ssh_options = ssh_options + ' -p %s'%(str(port)) if ssh_key is not None: try: os.path.isfile(ssh_key) except: raise ExceptionPxssh('private ssh key does not exist') ssh_options = ssh_options + ' -i %s' % (ssh_key) cmd = "ssh %s -l %s %s" % (ssh_options, username, server) # This does not distinguish between a remote server 'password' prompt # and a local ssh 'passphrase' prompt (for unlocking a private key). spawn._spawn(self, cmd) i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT, "(?i)connection closed by remote host", EOF], timeout=login_timeout) # First phase if i==0: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) if i==2: # password or passphrase self.sendline(password) i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) if i==4: self.sendline(terminal_type) i = self.expect(["(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) if i==7: self.close() raise ExceptionPxssh('Could not establish connection to host') # Second phase if i==0: # This is weird. This should not happen twice in a row. self.close() raise ExceptionPxssh('Weird error. Got "are you sure" prompt twice.') elif i==1: # can occur if you have a public key pair set to authenticate. ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. pass elif i==2: # password prompt again # For incorrect passwords, some ssh servers will # ask for the password again, others return 'denied' right away. # If we get the password prompt again then this means # we didn't get the password right the first time. self.close() raise ExceptionPxssh('password refused') elif i==3: # permission denied -- password was bad. self.close() raise ExceptionPxssh('permission denied') elif i==4: # terminal type again? WTF? self.close() raise ExceptionPxssh('Weird error. Got "terminal type" prompt twice.') elif i==5: # Timeout #This is tricky... I presume that we are at the command-line prompt. #It may be that the shell prompt was so weird that we couldn't match #it. Or it may be that we couldn't log in for some other reason. I #can't be sure, but it's safe to guess that we did login because if #I presume wrong and we are not logged in then this should be caught #later when I try to set the shell prompt. pass elif i==6: # Connection closed by remote host self.close() raise ExceptionPxssh('connection closed') else: # Unexpected self.close() raise ExceptionPxssh('unexpected login response') if not self.sync_original_prompt(sync_multiplier): self.close() raise ExceptionPxssh('could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxssh('could not set shell prompt ' '(received: %r, expected: %r).' % ( self.before, self.PROMPT,)) return True
def login_host_first_time(host, user, password, new_password, expect_fail=False, timeout=120): cmd_expected = [ ( # 'ssh -l {} -o UserKnownHostsFile=/dev/null {}'.format(user, host), # 'ssh -t -q -l {} {}'.format(user, host), 'ssh', ('Are you sure you want to continue connecting (yes/no)?', ), ('Failed to get "continue connecting" prompt', )), ( 'yes', ("{}@{}\'s password:"******"{}={}"'.format(k, v) for k, v in SSH_OPTS.items()]) # cmd = '{} {} -l {} {}'.format(cmd, options, user, host) cmd = '{} {} -l {} {}'.format(cmd, options, user, host) LOG.info( 'first time login: sending cmd:{}, \nexpected:{}\n'.format( cmd, expected_output)) spawn._spawn(connect, cmd) connect.force_password = True first_cmd = False else: LOG.info('sending cmd:{}\n'.format(cmd)) connect.sendline(cmd) all_output = list(expected_output + errors) LOG.info('expecting outputs:{}\n'.format(all_output)) index = connect.expect(all_output) LOG.info('actually got: index:{}, output:{}\n\n'.format( index, all_output[index])) LOG.info('actually got: before:\n{}\nafter:{}\n\n'.format( connect.before, connect.after)) if index >= len(expected_output): LOG.info( 'error: sent:{}, returned:{}, returned-index:{}\n'.format( cmd, all_output[index], index)) LOG.info('OUTPUT:before:{}\nafter:{}\n'.format( connect.before, connect.after)) if r'\(current\) UNIX password:'******'connection broke when expecting "\(current\) UNIX password:", take as good?' ) return True, password break time.sleep(2) except Exception as e: message = 'failed to connect to {}, got error: {}\nbefore:{}\nafter:{}\n'.format( message, e, connect.before, connect.after) LOG.info(message) assert expect_fail, message else: LOG.info( 'OK, logged in for the first time, new password set:{}\n'.format( new_password)) LOG.info('close the connection') try: connect.logout() except Exception as e: LOG.info('got error when closing connection:{}\n'.format(e)) wait_time = 180 LOG.info('Wait {} seconds after change/reset the password of sysadmin'. format(wait_time)) time.sleep(wait_time) return True, new_password finally: pass # if close_at_exit: # LOG.info('close ssh connection to {}'.format(host)) # connect.close() return False, ''
def log_in_raw(host, user, password, expect_fail=False): message = ' host:{} as user:{} with password:{}\n'.format( host, user, password) LOG.info('logging onto {}, expecting failure:{}\n'.format( message, expect_fail)) connect = get_pxssh_session() options = ' '.join(['-o {}={}'.format(k, v) for k, v in SSH_OPTS.items()]) cmd = 'ssh {} -l {} {}'.format(options, user, host) # cmd = 'ssh -q -l {} {}'.format(user, host) LOG.info('send cmd:{}\n'.format(cmd)) spawn._spawn(connect, cmd) connect.force_password = True index = connect.expect( ['Are you sure you want to continue connecting (yes/no)?']) if index != 0: LOG.info( 'failed to get expected result from cmd, \ncmd:{}\nindex:{}\n'. format(cmd, index)) cmd = 'yes' LOG.info('send cmd:{}\n'.format(cmd)) connect.sendline(cmd) index = connect.expect(['password:'******'failed to get expected result from cmd, \ncmd:{}\nindex:{}\n'. format(cmd, index)) LOG.info('send password:{}\n'.format(password)) connect.sendline(password) prompt = Prompt.CONTROLLER_PROMPT error = 'Permission denied, please try again.' index = connect.expect([prompt, error]) if index != 0: msg = '{}, got index:{}'.format(message, index) if not expect_fail: LOG.info('failed to get expected prompt, {}'.format(msg)) assert False, 'failed to get expected/login, {}'.format(msg) else: LOG.info('as expected, failed to login, {}\n'.format(msg)) return None else: msg = 'logged in, {}\noutput before:{}, after:{}\n'.format( message, connect.before, connect.after) if expect_fail: LOG.info('Error, expecting to fail but actually {}'.format(msg)) assert False, 'Error, expecting to fail but actually {}'.format( msg) else: LOG.info('OK, logged in, will verify it, {}\n'.format(message)) cmd = '(date; uuid; hostname; id; ifconfig | \grep 128.224 -B1 -A7) 2>/dev/null' LOG.info('send cmd: {}\n'.format(cmd)) connect.sendline(cmd) index = connect.expect([prompt, TIMEOUT]) LOG.info('returned:\nbefore:{}\nafter:{}\n'.format( connect.before, connect.after)) if 1 == index: LOG.info('timeout:') return None return connect
def login (self, server, username, password='', terminal_type='ansi', original_prompt=r"[#$]", login_timeout=10, port=None, auto_prompt_reset=True, ssh_key=None, quiet=True, sync_multiplier=1, check_local_ip=True, password_regex=r'(?i)(?:password:)|(?:passphrase for key)', ssh_tunnels={}, spawn_local_ssh=True, sync_original_prompt=True, ssh_config=None): '''This logs the user into the given server. It uses 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called :file:`~/.hushlogin` on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an :class:`ExceptionPxssh` exception. In some situations it is not possible or desirable to reset the original prompt. In this case, pass ``auto_prompt_reset=False`` to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the :meth:`prompt` method. If the original prompt is not reset then this will disable the :meth:`prompt` method unless you manually set the :attr:`PROMPT` attribute. Set ``password_regex`` if there is a MOTD message with `password` in it. Changing this is like playing in traffic, don't (p)expect it to match straight away. If you require to connect to another SSH server from the your original SSH connection set ``spawn_local_ssh`` to `False` and this will use your current session to do so. Setting this option to `False` and not having an active session will trigger an error. Set ``ssh_key`` to a file path to an SSH private key to use that SSH key for the session authentication. Set ``ssh_key`` to `True` to force passing the current SSH authentication socket to the desired ``hostname``. Set ``ssh_config`` to a file path string of an SSH client config file to pass that file to the client to handle itself. You may set any options you wish in here, however doing so will require you to post extra information that you may not want to if you run into issues. ''' session_regex_array = ["(?i)are you sure you want to continue connecting", original_prompt, password_regex, "(?i)permission denied", "(?i)terminal type", TIMEOUT] session_init_regex_array = [] session_init_regex_array.extend(session_regex_array) session_init_regex_array.extend(["(?i)connection closed by remote host", EOF]) ssh_options = ''.join([" -o '%s=%s'" % (o, v) for (o, v) in self.options.items()]) if quiet: ssh_options = ssh_options + ' -q' if not check_local_ip: ssh_options = ssh_options + " -o'NoHostAuthenticationForLocalhost=yes'" if self.force_password: ssh_options = ssh_options + ' ' + self.SSH_OPTS if ssh_config is not None: if spawn_local_ssh and not os.path.isfile(ssh_config): raise ExceptionPxssh('SSH config does not exist or is not a file.') ssh_options = ssh_options + '-F ' + ssh_config if port is not None: ssh_options = ssh_options + ' -p %s'%(str(port)) if ssh_key is not None: # Allow forwarding our SSH key to the current session if ssh_key==True: ssh_options = ssh_options + ' -A' else: if spawn_local_ssh and not os.path.isfile(ssh_key): raise ExceptionPxssh('private ssh key does not exist or is not a file.') ssh_options = ssh_options + ' -i %s' % (ssh_key) # SSH tunnels, make sure you know what you're putting into the lists # under each heading. Do not expect these to open 100% of the time, # The port you're requesting might be bound. # # The structure should be like this: # { 'local': ['2424:localhost:22'], # Local SSH tunnels # 'remote': ['2525:localhost:22'], # Remote SSH tunnels # 'dynamic': [8888] } # Dynamic/SOCKS tunnels if ssh_tunnels!={} and isinstance({},type(ssh_tunnels)): tunnel_types = { 'local':'L', 'remote':'R', 'dynamic':'D' } for tunnel_type in tunnel_types: cmd_type = tunnel_types[tunnel_type] if tunnel_type in ssh_tunnels: tunnels = ssh_tunnels[tunnel_type] for tunnel in tunnels: if spawn_local_ssh==False: tunnel = quote(str(tunnel)) ssh_options = ssh_options + ' -' + cmd_type + ' ' + str(tunnel) cmd = "ssh %s -l %s %s" % (ssh_options, username, server) if self.debug_command_string: return(cmd) # Are we asking for a local ssh command or to spawn one in another session? if spawn_local_ssh: spawn._spawn(self, cmd) else: self.sendline(cmd) # This does not distinguish between a remote server 'password' prompt # and a local ssh 'passphrase' prompt (for unlocking a private key). i = self.expect(session_init_regex_array, timeout=login_timeout) # First phase if i==0: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") i = self.expect(session_regex_array) if i==2: # password or passphrase self.sendline(password) i = self.expect(session_regex_array) if i==4: self.sendline(terminal_type) i = self.expect(session_regex_array) if i==7: self.close() raise ExceptionPxssh('Could not establish connection to host') # Second phase if i==0: # This is weird. This should not happen twice in a row. self.close() raise ExceptionPxssh('Weird error. Got "are you sure" prompt twice.') elif i==1: # can occur if you have a public key pair set to authenticate. ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. pass elif i==2: # password prompt again # For incorrect passwords, some ssh servers will # ask for the password again, others return 'denied' right away. # If we get the password prompt again then this means # we didn't get the password right the first time. self.close() raise ExceptionPxssh('password refused') elif i==3: # permission denied -- password was bad. self.close() raise ExceptionPxssh('permission denied') elif i==4: # terminal type again? WTF? self.close() raise ExceptionPxssh('Weird error. Got "terminal type" prompt twice.') elif i==5: # Timeout #This is tricky... I presume that we are at the command-line prompt. #It may be that the shell prompt was so weird that we couldn't match #it. Or it may be that we couldn't log in for some other reason. I #can't be sure, but it's safe to guess that we did login because if #I presume wrong and we are not logged in then this should be caught #later when I try to set the shell prompt. pass elif i==6: # Connection closed by remote host self.close() raise ExceptionPxssh('connection closed') else: # Unexpected self.close() raise ExceptionPxssh('unexpected login response') if sync_original_prompt: if not self.sync_original_prompt(sync_multiplier): self.close() raise ExceptionPxssh('could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxssh('could not set shell prompt ' '(received: %r, expected: %r).' % ( self.before, self.PROMPT,)) return True
def login( self, server, username, password='', terminal_type='ansi', original_prompt=r"[#$]", login_timeout=10, port=None, auto_prompt_reset=True, ssh_key=None, quiet=True, sync_multiplier=1, check_local_ip=True, password_regex=r'(?i)(?:password:)|(?:passphrase for key)', ssh_tunnels={}, spawn_local_ssh=True, sync_original_prompt=True, ssh_config=None, cipher=None, host_key_changed_regex="(?i)WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED", no_cipher_found_regex="(?i)no matching cipher found|(?i)Unknown cipher type", host_key_check_regex="(?i)are you sure you want to continue connecting", access_denied_regex="(?i)access denied|(?i)permission denied", terminal_type_regex="(?i)terminal type", too_many_hosts_regex="(?i)maximum number of ssh sessions are active", connection_closed_regex="(?i)connection closed by remote host", verbose_level=0, check_kex_only=False, kex_filter_regex=r'(kex:\s.*[\s\S]?)'): '''This logs the user into the given server. It uses 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called :file:`~/.hushlogin` on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an :class:`ExceptionPxssh` exception. In some situations it is not possible or desirable to reset the original prompt. In this case, pass ``auto_prompt_reset=False`` to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the :meth:`prompt` method. If the original prompt is not reset then this will disable the :meth:`prompt` method unless you manually set the :attr:`PROMPT` attribute. Set ``password_regex`` if there is a MOTD message with `password` in it. Changing this is like playing in traffic, don't (p)expect it to match straight away. If you require to connect to another SSH server from the your original SSH connection set ``spawn_local_ssh`` to `False` and this will use your current session to do so. Setting this option to `False` and not having an active session will trigger an error. Set ``ssh_key`` to a file path to an SSH private key to use that SSH key for the session authentication. Set ``ssh_key`` to `True` to force passing the current SSH authentication socket to the desired ``hostname``. Set ``ssh_config`` to a file path string of an SSH client config file to pass that file to the client to handle itself. You may set any options you wish in here, however doing so will require you to post extra information that you may not want to if you run into issues. ''' #[JB] - added line below due to Sonicwall password prompt difference in 6.1.x vs 6.2.x and access denied regex is not a option variable #session_regex_array = ["(?i)are you sure you want to continue connecting", original_prompt, password_regex, "(?i)permission denied", "(?i)terminal type", TIMEOUT] session_regex_array = [ host_key_changed_regex, no_cipher_found_regex, host_key_check_regex, original_prompt, password_regex, access_denied_regex, terminal_type_regex, TIMEOUT ] session_init_regex_array = [] session_init_regex_array.extend(session_regex_array) session_init_regex_array.extend( [too_many_hosts_regex, connection_closed_regex, EOF]) ssh_options = ''.join( [" -o '%s=%s'" % (o, v) for (o, v) in self.options.items()]) if quiet: ssh_options = ssh_options + ' -q' if not check_local_ip: ssh_options = ssh_options + " -o'NoHostAuthenticationForLocalhost=yes'" if self.force_password: ssh_options = ssh_options + ' ' + self.SSH_OPTS if ssh_config is not None: if spawn_local_ssh and not os.path.isfile(ssh_config): raise ExceptionPxssh( 'SSH config does not exist or is not a file.') ssh_options = ssh_options + '-F ' + ssh_config if port is not None: ssh_options = ssh_options + ' -p %s' % (str(port)) if ssh_key is not None: # Allow forwarding our SSH key to the current session if ssh_key == True: ssh_options = ssh_options + ' -A' else: if spawn_local_ssh and not os.path.isfile(ssh_key): raise ExceptionPxssh( 'private ssh key does not exist or is not a file.') ssh_options = ssh_options + ' -i %s' % (ssh_key) # SSH tunnels, make sure you know what you're putting into the lists # under each heading. Do not expect these to open 100% of the time, # The port you're requesting might be bound. # # The structure should be like this: # { 'local': ['2424:localhost:22'], # Local SSH tunnels # 'remote': ['2525:localhost:22'], # Remote SSH tunnels # 'dynamic': [8888] } # Dynamic/SOCKS tunnels if ssh_tunnels != {} and isinstance({}, type(ssh_tunnels)): tunnel_types = {'local': 'L', 'remote': 'R', 'dynamic': 'D'} for tunnel_type in tunnel_types: cmd_type = tunnel_types[tunnel_type] if tunnel_type in ssh_tunnels: tunnels = ssh_tunnels[tunnel_type] for tunnel in tunnels: if spawn_local_ssh == False: tunnel = quote(str(tunnel)) ssh_options = ssh_options + ' -' + cmd_type + ' ' + str( tunnel) #[JB] - added cipher option for ssh #cmd = "ssh %s -l %s %s" % (ssh_options, username, server) #cmd = "ssh %s -c %s -l %s %s" % (ssh_options, cipher, username, server) if cipher != None: cmd = "ssh %s -c %s " % (ssh_options, cipher) else: cmd = "ssh %s " % (ssh_options) if verbose_level == 1: cmd = cmd + "-v " elif verbose_level == 2: cmd = cmd + "-vv " elif verbose_level == 3: cmd = cmd + "-vvv " cmd = cmd + "-l %s %s" % (username, server) if self.debug_command_string: return (cmd) logging.debug('cmd=' + cmd) # Are we asking for a local ssh command or to spawn one in another session? if spawn_local_ssh: spawn._spawn(self, cmd) else: self.sendline(cmd) # This does not distinguish between a remote server 'password' prompt # and a local ssh 'passphrase' prompt (for unlocking a private key). if check_kex_only: session_init_regex_array = [host_key_changed_regex, no_cipher_found_regex, host_key_check_regex, terminal_type_regex, \ password_regex, access_denied_regex, TIMEOUT, too_many_hosts_regex, connection_closed_regex, EOF] kex_output = "" i = self.expect(session_init_regex_array, timeout=login_timeout) if i == 0: self.close() raise ExceptionPxsshHostKeyChanged('Host key changed') if i == 1: self.close() raise ExceptionPxsshNoCipherFound('No Cipher found') if i == 2: #kex_output = self.before + self.after kex_output = self.before.decode() + self.after.decode() self.sendline("yes") i = self.expect(session_init_regex_array, timeout=login_timeout) if i == 3: #kex_output = self.before + self.after kex_output = self.before.decode() + self.after.decode() self.sendline(terminal_type) i = self.expect(session_init_regex_array, timeout=login_timeout) # Second pass if i == 4: #kex_output = kex_output + self.before + self.after kex_output = kex_output + self.before.decode( ) + self.after.decode() kex_output_list = re.findall(kex_filter_regex, kex_output) kex_output_filtered = ''.join(kex_output_list) self.close() return kex_output_filtered if i == 5: self.close() raise ExceptionPxsshAccessDenied('access denied') if i == 6: self.close() raise ExceptionPxsshLoginTimeout('timeout') if i == 7: self.close() raise ExceptionPxsshMaxHostsExceeded('max hosts exceeded') if i == 8: self.close() raise ExceptionPxssh('connection closed') if i == 9: self.close() raise ExceptionPxssh('Could not establish connection to host') self.close() return '' else: i = self.expect(session_init_regex_array, timeout=login_timeout) # First phase if i == 0: raise ExceptionPxsshHostKeyChanged('Host key changed') if i == 1: self.close() raise ExceptionPxsshNoCipherFound('No Cipher found') #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #if i==0: if i == 2: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") #[JB] - added timeout variable to expect functions due to inconsistent timeout behavior & Sonicwall login banner behavior differences for several firewalls (extended time to receive "Access denied" message) #i = self.expect(session_regex_array) i = self.expect(session_regex_array, timeout=login_timeout) #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #if i==2: # password or passphrase if i == 4: # password or passphrase self.sendline(password) #[JB] - added line below due to Sonicwall login banner and password prompt difference in 6.1.x vs 6.2.x session_regex_array = [host_key_changed_regex, no_cipher_found_regex, host_key_check_regex, original_prompt, \ "do not check for password prompt again and update access denied string", access_denied_regex, terminal_type_regex, TIMEOUT] #[JB] - added timeout variable to expect functions due to inconsistent timeout behavior & Sonicwall login banner behavior differences for several firewalls (extended time to receive "Access denied" message) #i = self.expect(session_regex_array) i = self.expect(session_regex_array, timeout=login_timeout) #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #if i==4: if i == 6: self.sendline(terminal_type) #[JB] - added timeout variable to expect functions due to inconsistent timeout behavior & Sonicwall login banner behavior differences for several firewalls (extended time to receive "Access denied" message) #i = self.expect(session_regex_array) i = self.expect(session_regex_array, timeout=login_timeout) if i == 8: self.close() raise ExceptionPxsshMaxHostsExceeded('max hosts exceeded') if i == 9: self.close() raise ExceptionPxssh('Connection closed by host') #[JB] - Increase check of i by 2 to check for Host key change at i==0 and too many hosts at i==8 #if i==7: if i == 10: self.close() raise ExceptionPxssh('Could not establish connection to host') # Second phase #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #if i==0: if i == 2: # This is weird. This should not happen twice in a row. self.close() raise ExceptionPxssh( 'Weird error. Got "are you sure" prompt twice.') #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #elif i==1: # can occur if you have a public key pair set to authenticate. elif i == 3: # can occur if you have a public key pair set to authenticate. ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. pass #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #elif i==2: # password prompt again elif i == 4: # password prompt again # For incorrect passwords, some ssh servers will # ask for the password again, others return 'denied' right away. # If we get the password prompt again then this means # we didn't get the password right the first time. self.close() raise ExceptionPxssh('password refused') #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #elif i==3: # permission denied -- password was bad. elif i == 5: # permission denied -- password was bad. self.close() #[JB] - added specific exception for Access Denied #raise ExceptionPxssh('permission denied') raise ExceptionPxsshAccessDenied('access denied') #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #elif i==4: # terminal type again? WTF? elif i == 6: # terminal type again? WTF? self.close() raise ExceptionPxssh( 'Weird error. Got "terminal type" prompt twice.') #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #elif i==5: # Timeout elif i == 7: # Timeout #This is tricky... I presume that we are at the command-line prompt. #It may be that the shell prompt was so weird that we couldn't match #it. Or it may be that we couldn't log in for some other reason. I #can't be sure, but it's safe to guess that we did login because if #I presume wrong and we are not logged in then this should be caught #later when I try to set the shell prompt. #[JB] - added timeout variable to expect functions due to inconsistent timeout behavior & Sonicwall login banner behavior differences for several firewalls (extended time to receive "Access denied" message) #pass - [JB] - not commented in parent function #raise ExceptionPxssh('timeout') - [JB] - commented in parent function; strange that the close function was after the raise exception command in parent, but both lines are commented #self.close() - [JB] - commented in parent function; strange that the close function was after the raise exception command in parent, but both lines are commented self.close() raise ExceptionPxsshLoginTimeout('timeout') #[JB] - Increase check of i by 2 to check for Host key change (i==0) & No Cipher found (i==1) #elif i==6: # Connection closed by remote host elif i == 8: # Connection closed by remote host self.close() raise ExceptionPxssh('connection closed') else: # Unexpected self.close() raise ExceptionPxssh('unexpected login response') if sync_original_prompt: if not self.sync_original_prompt(sync_multiplier): self.close() raise ExceptionPxssh( 'could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxssh('could not set shell prompt ' '(received: %r, expected: %r).' % ( self.before, self.PROMPT, )) return True
def login (self, executable, options='', username='', password='', login_timeout=1, auto_prompt_reset=True, sync_multiplier=1): '''This logs the user into the given server. It uses 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called :file:`~/.hushlogin` on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an :class:`ExceptionPxssh` exception. In some situations it is not possible or desirable to reset the original prompt. In this case, pass ``auto_prompt_reset=False`` to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the :meth:`prompt` method. If the original prompt is not reset then this will disable the :meth:`prompt` method unless you manually set the :attr:`PROMPT` attribute. ''' print("pxshell::login(%s,%s)" % (username, password)) # archive the options self.login_timeout = login_timeout self.sync_multiplier = sync_multiplier # double check if executable == '': raise ExceptionPxShell('Unconfigured child application: %s' % executable) # keep this simple in here cmd = executable + ' ' + options # fire it up. spawn._spawn(self, cmd) # run the login sequence... self._login_state_machine(username, password) # try to resolve the interface if not self.sync_original_prompt(self.sync_multiplier): self.close() raise ExceptionPxShell('could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxShell('could not set shell prompt ' '(recieved: %r, expected: %r).' % ( self.before, self.interpreter.unique_prompt(),)) # update to the prompt newinfo = self.prompt(timeout=0.1)
def login (self,server,username,password='',switch_port=23,terminal_type='ansi',original_prompt=r"[#$]",login_timeout=10,port=None,auto_prompt_reset=True,ssh_key=None): cmd = "telnet %s %d" % (server,switch_port) spawn._spawn(self, cmd) stage=0 # stage=1 -> get "login:"******"password:"******"(%s)|(%s)|(%s)"%(login_en,login_zh_utf8,login_zh_gbk), "(?i)connection closed by (remote)|(foreign) host", "(?i)Connection refused", TIMEOUT,EOF ] i = self.expect( exp_list, timeout=login_timeout) if i==0: stage=1 # get the login: prompt self.debug('get login prompt('+self.after+')') else: exp_msg=str(exp_list[i]) self.close() raise ExceptionPxtelnet(exp_msg) elif stage==1: self.sendline(username) exp_list=[ "(%s)|(%s)|(%s)"%(pass_en,pass_zh_utf8,pass_zh_gbk), original_prompt, "(?i)connection closed by (remote)|(foreign) host", "(?i)Connection refused", TIMEOUT,EOF] i = self.expect( exp_list, timeout=login_timeout) if i==0: stage=2 # get the passwd: prompt self.debug('get passwd prompt('+self.after+')') elif i==1: stage=3 # no passwd,into shell break else: exp_msg=str(exp_list[i]) self.close() raise ExceptionPxtelnet(exp_msg) elif stage==2: self.sendline(password) exp_list=[ original_prompt, "(3004-300)|(Login incorrect)", "(?i)terminal type", TIMEOUT] i = self.expect( exp_list,timeout=login_timeout) if i==0: stage=3 self.debug('get shell('+self.after+')') break elif i==2: #require teminal type self.sendline('vt100') else: exp_msg=str(exp_list[i])+'|'+self.after self.close() raise ExceptionPxtelnet(exp_msg) if not self.sync_original_prompt(): self.close() raise ExceptionPxtelnet ('could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxtelnet ('could not set shell prompt\n'+self.before) return True
def login(self, executable, options='', username='', password='', login_timeout=1, auto_prompt_reset=True, sync_multiplier=1): '''This logs the user into the given server. It uses 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called :file:`~/.hushlogin` on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an :class:`ExceptionPxssh` exception. In some situations it is not possible or desirable to reset the original prompt. In this case, pass ``auto_prompt_reset=False`` to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the :meth:`prompt` method. If the original prompt is not reset then this will disable the :meth:`prompt` method unless you manually set the :attr:`PROMPT` attribute. ''' print("pxshell::login(%s,%s)" % (username, password)) # archive the options self.login_timeout = login_timeout self.sync_multiplier = sync_multiplier # double check if executable == '': raise ExceptionPxShell('Unconfigured child application: %s' % executable) # keep this simple in here cmd = executable + ' ' + options # fire it up. spawn._spawn(self, cmd) # run the login sequence... self._login_state_machine(username, password) # try to resolve the interface if not self.sync_original_prompt(self.sync_multiplier): self.close() raise ExceptionPxShell( 'could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxShell('could not set shell prompt ' '(recieved: %r, expected: %r).' % ( self.before, self.interpreter.unique_prompt(), )) # update to the prompt newinfo = self.prompt(timeout=0.1)
def login(self, server, username, password='', terminal_type='ansi', original_prompt=r"[#$]", login_timeout=10, port=None, auto_prompt_reset=True): """This logs the user into the given server. It uses the 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called "~/.hushlogin" on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an ExceptionPxssh exception. In some situations it is not possible or desirable to reset the original prompt. In this case, set 'auto_prompt_reset' to False to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the prompt() method. If the original prompt is not reset then this will disable the prompt() method unless you manually set the PROMPT attribute. """ ssh_options = '-q' if self.force_password: ssh_options = ssh_options + ' ' + self.SSH_OPTS if port is not None: ssh_options = ssh_options + ' -p %s' % (str(port)) cmd = "ssh %s -l %s %s" % (ssh_options, username, server) # This does not distinguish between a remote server 'password' prompt # and a local ssh 'passphrase' prompt (for unlocking a private key). spawn._spawn(self, cmd) i = self.expect([ "(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT, "(?i)connection closed by remote host" ], timeout=login_timeout) # First phase if i == 0: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") i = self.expect([ "(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT ]) if i == 2: # password or passphrase self.sendline(password) i = self.expect([ "(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT ]) if i == 4: self.sendline(terminal_type) i = self.expect([ "(?i)are you sure you want to continue connecting", original_prompt, "(?i)(?:password)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT ]) # Second phase if i == 0: # This is weird. This should not happen twice in a row. self.close() raise ExceptionPxssh( 'Weird error. Got "are you sure" prompt twice.') # can occur if you have a public key pair set to authenticate. elif i == 1: # TODO: May NOT be OK if expect() got tricked and matched a false # prompt. pass elif i == 2: # password prompt again # For incorrect passwords, some ssh servers will # ask for the password again, others return 'denied' right away. # If we get the password prompt again then this means # we didn't get the password right the first time. self.close() raise ExceptionPxssh('password refused') elif i == 3: # permission denied -- password was bad. self.close() raise ExceptionPxssh('permission denied') elif i == 4: # terminal type again? WTF? self.close() raise ExceptionPxssh( 'Weird error. Got "terminal type" prompt twice.') elif i == 5: # Timeout # This is tricky... I presume that we are at the command-line prompt. # It may be that the shell prompt was so weird that we couldn't match # it. Or it may be that we couldn't log in for some other reason. I # can't be sure, but it's safe to guess that we did login because if # I presume wrong and we are not logged in then this should be caught # later when I try to set the shell prompt. pass elif i == 6: # Connection closed by remote host self.close() raise ExceptionPxssh('connection closed') else: # Unexpected self.close() raise ExceptionPxssh('unexpected login response') if not self.synch_original_prompt(): self.close() raise ExceptionPxssh('could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxssh('could not set shell prompt\n' + self.before) return True
def login( self, server='', username='', password='', terminal_type='ansi', original_prompt=r'[#$]', login_timeout=10, port=None, auto_prompt_reset=True, ssh_key=None, quiet=True, sync_multiplier=1, check_local_ip=True, password_regex=r'(?i)(?:password:)|(?:passphrase for key)', ssh_tunnels={}, spawn_local_ssh=True, sync_original_prompt=True, ssh_config=None, remove_known_hosts=False, ping_before_connect=True, attempt=1, ): '''Overrides login from parent class, add to find the prompt after the ssh connection is established if auto_prompt_reset is set to False. Otherwise, login() function set the ssh prompt to '[PEXPECT]$' by default. ''' self.is_logged_in = False # Try connecting to the server if server is specified if server: # Retry specified attempts before raising exception for i in range(attempt): output = '' pattern = '' self.pid = None # Set pid to None to avoid AssertionError: The pid member must be None. if ping_before_connect: # Set ping command and expected pattern cmd = 'ping -c4 {}'.format(server) pattern = '4 received' if self.verbose: if i == 0: log.debug( '{}/{} attempt :\t"{}"\tpattern="{}"'.format( i + 1, attempt, cmd.strip(), pattern)) else: log.debug( '{}/{} attempts:\t"{}"\tpattern="{}"'.format( i + 1, attempt, cmd.strip(), pattern)) output = run(cmd, timeout=10, encoding=self.encoding) # output = run(cmd, timeout=10).decode('utf-8') # Ping to make sure host is reachable before establish ssh connection if pattern in output: if remove_known_hosts: run('rm {}'.format( os.path.expanduser('~/.ssh/known_hosts')), timeout=5, encoding=self.encoding) try: if not self.is_logged_in: self.is_logged_in = super(Connection, self).login( server, username, password=password, terminal_type=terminal_type, original_prompt=original_prompt, login_timeout=login_timeout, port=port, auto_prompt_reset=auto_prompt_reset, ssh_key=ssh_key, quiet=quiet, sync_multiplier=sync_multiplier, check_local_ip=check_local_ip, password_regex=password_regex, ssh_tunnels=ssh_tunnels, spawn_local_ssh=spawn_local_ssh, sync_original_prompt=sync_original_prompt, ssh_config=ssh_config, ) # Get prompt upon login and set it as default prompt self.PROMPT = self._get_prompt(original_prompt) except: if i + 1 >= attempt: # if self.verbose: # print('Unable to reach host {}, make sure host is reachable!'.format(server)) # raise pxssh.ExceptionPxssh('Unable to reach host {}, make sure host is reachable!'.format(server)) # import traceback # traceback.print_exc() log.exception( 'Unable to reach host {}, make sure host is reachable!' .format(server)) continue break else: if i + 1 >= attempt: # if self.verbose: # print('Unable to reach host {}, make sure host is reachable!'.format(server)) # raise pxssh.ExceptionPxssh('Unable to reach host {}, make sure host is reachable!'.format(server)) # import traceback # traceback.print_exc() log.warning( 'Unable to reach host {}, make sure host is reachable!' .format(server)) # If server not specified, just run an instance of bash shell # Note: Useing ssh to localhost above would work in most cases, but in # the case where the login credential is not known, such as running in # another organization's environment. else: if remove_known_hosts: run('rm {}'.format(os.path.expanduser('~/.ssh/known_hosts')), timeout=5, encoding=self.encoding) # TODO: Try to detect and spawn the current running shell # current_shell = run('echo $0', timeout=10, encoding=self.encoding) # log.debug('Current shell: {}'.format(current_shell)) spawn._spawn(self, 'bash') self.expect(original_prompt) # cd to home directory to ensure local session starts in the same # directory as ssh session, since bash does not change directory # when start a new session. self.sendline('cd', original_prompt, timeout=5, regex=True) self.is_logged_in = True # Get prompt upon login and set it as default prompt self.PROMPT = self._get_prompt(original_prompt) return self.is_logged_in
def login (self, server, username, password='', terminal_type='ansi', original_prompt=r"[#$]", login_timeout=10, port=None, password_regex=r'(?i)(?:password:)|(?:passphrase for key)', auto_prompt_reset=True, ssh_key=None, quiet=True, sync_multiplier=1, check_local_ip=True, sync_original_prompt=True): '''This logs the user into the given server. It uses 'original_prompt' to try to find the prompt right after login. When it finds the prompt it immediately tries to reset the prompt to something more easily matched. The default 'original_prompt' is very optimistic and is easily fooled. It's more reliable to try to match the original prompt as exactly as possible to prevent false matches by server strings such as the "Message Of The Day". On many systems you can disable the MOTD on the remote server by creating a zero-length file called :file:`~/.hushlogin` on the remote server. If a prompt cannot be found then this will not necessarily cause the login to fail. In the case of a timeout when looking for the prompt we assume that the original prompt was so weird that we could not match it, so we use a few tricks to guess when we have reached the prompt. Then we hope for the best and blindly try to reset the prompt to something more unique. If that fails then login() raises an :class:`ExceptionPxssh` exception. In some situations it is not possible or desirable to reset the original prompt. In this case, pass ``auto_prompt_reset=False`` to inhibit setting the prompt to the UNIQUE_PROMPT. Remember that pxssh uses a unique prompt in the :meth:`prompt` method. If the original prompt is not reset then this will disable the :meth:`prompt` method unless you manually set the :attr:`PROMPT` attribute. Set ``password_regex`` if there is a MOTD message with `password` in it. Changing this is like playing in traffic, don't (p)expect it to match straight away. ''' session_regex_array = ["(?i)are you sure you want to continue connecting", original_prompt, password_regex, "(?i)permission denied", "(?i)terminal type", TIMEOUT] session_init_regex_array = [] session_init_regex_array.extend(session_regex_array) session_init_regex_array.extend(["(?i)connection closed by remote host", EOF]) ssh_options = ''.join([" -o '%s=%s'" % (o, v) for (o, v) in self.options.items()]) if quiet: ssh_options = ssh_options + ' -q' if not check_local_ip: ssh_options = ssh_options + " -o'NoHostAuthenticationForLocalhost=yes'" if self.force_password: ssh_options = ssh_options + ' ' + self.SSH_OPTS if port is not None: ssh_options = ssh_options + ' -p %s'%(str(port)) if ssh_key is not None: try: os.path.isfile(ssh_key) except: raise ExceptionPxssh('private ssh key does not exist') ssh_options = ssh_options + ' -i %s' % (ssh_key) cmd = "ssh %s -l %s %s" % (ssh_options, username, server) # This does not distinguish between a remote server 'password' prompt # and a local ssh 'passphrase' prompt (for unlocking a private key). spawn._spawn(self, cmd) i = self.expect(session_init_regex_array, timeout=login_timeout) # First phase if i==0: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") i = self.expect(session_regex_array) if i==2: # password or passphrase self.sendline(password) i = self.expect(session_regex_array) if i==4: self.sendline(terminal_type) i = self.expect(session_regex_array) if i==7: self.close() raise ExceptionPxssh('Could not establish connection to host') # Second phase if i==0: # This is weird. This should not happen twice in a row. self.close() raise ExceptionPxssh('Weird error. Got "are you sure" prompt twice.') elif i==1: # can occur if you have a public key pair set to authenticate. ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. pass elif i==2: # password prompt again # For incorrect passwords, some ssh servers will # ask for the password again, others return 'denied' right away. # If we get the password prompt again then this means # we didn't get the password right the first time. self.close() raise ExceptionPxssh('password refused') elif i==3: # permission denied -- password was bad. self.close() raise ExceptionPxssh('permission denied') elif i==4: # terminal type again? WTF? self.close() raise ExceptionPxssh('Weird error. Got "terminal type" prompt twice.') elif i==5: # Timeout #This is tricky... I presume that we are at the command-line prompt. #It may be that the shell prompt was so weird that we couldn't match #it. Or it may be that we couldn't log in for some other reason. I #can't be sure, but it's safe to guess that we did login because if #I presume wrong and we are not logged in then this should be caught #later when I try to set the shell prompt. pass elif i==6: # Connection closed by remote host self.close() raise ExceptionPxssh('connection closed') else: # Unexpected self.close() raise ExceptionPxssh('unexpected login response') if sync_original_prompt: if not self.sync_original_prompt(sync_multiplier): self.close() raise ExceptionPxssh('could not synchronize with original prompt') # We appear to be in. # set shell prompt to something unique. if auto_prompt_reset: if not self.set_unique_prompt(): self.close() raise ExceptionPxssh('could not set shell prompt ' '(received: %r, expected: %r).' % ( self.before, self.PROMPT,)) return True
def login (self, server, username, password='', terminal_type='ansi', original_prompt=r"[#$]", login_timeout=10, port=None, auto_prompt_reset=True, ssh_key=None, quiet=True, sync_multiplier=1, check_local_ip=True): # cp ssh doesn't have an options field so ignore ALL options ssh_options = '' if port is not None: ssh_options = ssh_options + ' -p %s'%(str(port)) cmd = "ssh %s -l %s %s" % (ssh_options, username, server) # This does not distinguish between a remote server 'password' prompt # and a local ssh 'passphrase' prompt (for unlocking a private key). spawn._spawn(self, cmd) i = self.expect(["(?i)Do you trust the host key", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT, "(?i)connection closed by remote host", EOF], timeout=login_timeout) # First phase if i==0: # New certificate -- always accept it. # This is what you get if SSH does not have the remote host's # public key stored in the 'known_hosts' cache. self.sendline("yes") i = self.expect(["(?i)Do you trust the host key", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) if i==2: # password or passphrase self.sendline(password) i = self.expect(["(?i)Do you trust the host key", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) if i==4: self.sendline(terminal_type) i = self.expect(["(?i)Do you trust the host key", original_prompt, "(?i)(?:password:)|(?:passphrase for key)", "(?i)permission denied", "(?i)terminal type", TIMEOUT]) if i==7: self.close() raise ExceptionPxssh('Could not establish connection to host') # Second phase if i==0: # This is weird. This should not happen twice in a row. self.close() raise ExceptionPxssh('Weird error. Got "are you sure" prompt twice.') elif i==1: # can occur if you have a public key pair set to authenticate. ### TODO: May NOT be OK if expect() got tricked and matched a false prompt. pass elif i==2: # password prompt again # For incorrect passwords, some ssh servers will # ask for the password again, others return 'denied' right away. # If we get the password prompt again then this means # we didn't get the password right the first time. self.close() raise ExceptionPxssh('password refused') elif i==3: # permission denied -- password was bad. self.close() raise ExceptionPxssh('permission denied') elif i==4: # terminal type again? WTF? self.close() raise ExceptionPxssh('Weird error. Got "terminal type" prompt twice.') elif i==5: # Timeout #This is tricky... I presume that we are at the command-line prompt. #It may be that the shell_sample prompt was so weird that we couldn't match #it. Or it may be that we couldn't log in for some other reason. I #can't be sure, but it's safe to guess that we did login because if #I presume wrong and we are not logged in then this should be caught #later when I try to set the shell_sample prompt. pass elif i==6: # Connection closed by remote host self.close() raise ExceptionPxssh('connection closed') else: # Unexpected self.close() raise ExceptionPxssh('unexpected login response') return True