1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17   
 18   
 19  """ 
 20  L{SSHClient}. 
 21  """ 
 22   
 23  from binascii import hexlify 
 24  import getpass 
 25  import os 
 26  import socket 
 27  import warnings 
 28   
 29  from paramiko.agent import Agent 
 30  from paramiko.common import * 
 31  from paramiko.dsskey import DSSKey 
 32  from paramiko.hostkeys import HostKeys 
 33  from paramiko.resource import ResourceManager 
 34  from paramiko.rsakey import RSAKey 
 35  from paramiko.ssh_exception import SSHException, BadHostKeyException 
 36  from paramiko.transport import Transport 
 37   
 38   
 39  SSH_PORT = 22 
 40   
 42      """ 
 43      Interface for defining the policy that L{SSHClient} should use when the 
 44      SSH server's hostname is not in either the system host keys or the 
 45      application's keys.  Pre-made classes implement policies for automatically 
 46      adding the key to the application's L{HostKeys} object (L{AutoAddPolicy}), 
 47      and for automatically rejecting the key (L{RejectPolicy}). 
 48   
 49      This function may be used to ask the user to verify the key, for example. 
 50      """ 
 51   
 53          """ 
 54          Called when an L{SSHClient} receives a server key for a server that 
 55          isn't in either the system or local L{HostKeys} object.  To accept 
 56          the key, simply return.  To reject, raised an exception (which will 
 57          be passed to the calling application). 
 58          """ 
 59          pass 
   60   
 61   
 63      """ 
 64      Policy for automatically adding the hostname and new host key to the 
 65      local L{HostKeys} object, and saving it.  This is used by L{SSHClient}. 
 66      """ 
 67   
  74   
 75   
 77      """ 
 78      Policy for automatically rejecting the unknown hostname & key.  This is 
 79      used by L{SSHClient}. 
 80      """ 
 81   
  86   
 87   
 89      """ 
 90      Policy for logging a python-style warning for an unknown host key, but 
 91      accepting it. This is used by L{SSHClient}. 
 92      """ 
  96   
 97   
 99      """ 
100      A high-level representation of a session with an SSH server.  This class 
101      wraps L{Transport}, L{Channel}, and L{SFTPClient} to take care of most 
102      aspects of authenticating and opening channels.  A typical use case is:: 
103   
104          client = SSHClient() 
105          client.load_system_host_keys() 
106          client.connect('ssh.example.com') 
107          stdin, stdout, stderr = client.exec_command('ls -l') 
108   
109      You may pass in explicit overrides for authentication and server host key 
110      checking.  The default mechanism is to try to use local key files or an 
111      SSH agent (if one is running). 
112   
113      @since: 1.6 
114      """ 
115   
117          """ 
118          Create a new SSHClient. 
119          """ 
120          self._system_host_keys = HostKeys() 
121          self._host_keys = HostKeys() 
122          self._host_keys_filename = None 
123          self._log_channel = None 
124          self._policy = RejectPolicy() 
125          self._transport = None 
 126   
128          """ 
129          Load host keys from a system (read-only) file.  Host keys read with 
130          this method will not be saved back by L{save_host_keys}. 
131   
132          This method can be called multiple times.  Each new set of host keys 
133          will be merged with the existing set (new replacing old if there are 
134          conflicts). 
135   
136          If C{filename} is left as C{None}, an attempt will be made to read 
137          keys from the user's local "known hosts" file, as used by OpenSSH, 
138          and no exception will be raised if the file can't be read.  This is 
139          probably only useful on posix. 
140   
141          @param filename: the filename to read, or C{None} 
142          @type filename: str 
143   
144          @raise IOError: if a filename was provided and the file could not be 
145              read 
146          """ 
147          if filename is None: 
148               
149              filename = os.path.expanduser('~/.ssh/known_hosts') 
150              try: 
151                  self._system_host_keys.load(filename) 
152              except IOError: 
153                  pass 
154              return 
155          self._system_host_keys.load(filename) 
 156   
158          """ 
159          Load host keys from a local host-key file.  Host keys read with this 
160          method will be checked I{after} keys loaded via L{load_system_host_keys}, 
161          but will be saved back by L{save_host_keys} (so they can be modified). 
162          The missing host key policy L{AutoAddPolicy} adds keys to this set and 
163          saves them, when connecting to a previously-unknown server. 
164   
165          This method can be called multiple times.  Each new set of host keys 
166          will be merged with the existing set (new replacing old if there are 
167          conflicts).  When automatically saving, the last hostname is used. 
168   
169          @param filename: the filename to read 
170          @type filename: str 
171   
172          @raise IOError: if the filename could not be read 
173          """ 
174          self._host_keys_filename = filename 
175          self._host_keys.load(filename) 
 176   
178          """ 
179          Save the host keys back to a file.  Only the host keys loaded with 
180          L{load_host_keys} (plus any added directly) will be saved -- not any 
181          host keys loaded with L{load_system_host_keys}. 
182   
183          @param filename: the filename to save to 
184          @type filename: str 
185   
186          @raise IOError: if the file could not be written 
187          """ 
188          f = open(filename, 'w') 
189          f.write('# SSH host keys collected by paramiko\n') 
190          for hostname, keys in self._host_keys.iteritems(): 
191              for keytype, key in keys.iteritems(): 
192                  f.write('%s %s %s\n' % (hostname, keytype, key.get_base64())) 
193          f.close() 
 194   
196          """ 
197          Get the local L{HostKeys} object.  This can be used to examine the 
198          local host keys or change them. 
199   
200          @return: the local host keys 
201          @rtype: L{HostKeys} 
202          """ 
203          return self._host_keys 
 204   
206          """ 
207          Set the channel for logging.  The default is C{"paramiko.transport"} 
208          but it can be set to anything you want. 
209   
210          @param name: new channel name for logging 
211          @type name: str 
212          """ 
213          self._log_channel = name 
 214   
216          """ 
217          Set the policy to use when connecting to a server that doesn't have a 
218          host key in either the system or local L{HostKeys} objects.  The 
219          default policy is to reject all unknown servers (using L{RejectPolicy}). 
220          You may substitute L{AutoAddPolicy} or write your own policy class. 
221   
222          @param policy: the policy to use when receiving a host key from a 
223              previously-unknown server 
224          @type policy: L{MissingHostKeyPolicy} 
225          """ 
226          self._policy = policy 
 227   
228 -    def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, 
229                  key_filename=None, timeout=None, allow_agent=True, look_for_keys=True): 
 230          """ 
231          Connect to an SSH server and authenticate to it.  The server's host key 
232          is checked against the system host keys (see L{load_system_host_keys}) 
233          and any local host keys (L{load_host_keys}).  If the server's hostname 
234          is not found in either set of host keys, the missing host key policy 
235          is used (see L{set_missing_host_key_policy}).  The default policy is 
236          to reject the key and raise an L{SSHException}. 
237   
238          Authentication is attempted in the following order of priority: 
239   
240              - The C{pkey} or C{key_filename} passed in (if any) 
241              - Any key we can find through an SSH agent 
242              - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/} 
243              - Plain username/password auth, if a password was given 
244   
245          If a private key requires a password to unlock it, and a password is 
246          passed in, that password will be used to attempt to unlock the key. 
247   
248          @param hostname: the server to connect to 
249          @type hostname: str 
250          @param port: the server port to connect to 
251          @type port: int 
252          @param username: the username to authenticate as (defaults to the 
253              current local username) 
254          @type username: str 
255          @param password: a password to use for authentication or for unlocking 
256              a private key 
257          @type password: str 
258          @param pkey: an optional private key to use for authentication 
259          @type pkey: L{PKey} 
260          @param key_filename: the filename, or list of filenames, of optional 
261              private key(s) to try for authentication 
262          @type key_filename: str or list(str) 
263          @param timeout: an optional timeout (in seconds) for the TCP connect 
264          @type timeout: float 
265          @param allow_agent: set to False to disable connecting to the SSH agent 
266          @type allow_agent: bool 
267          @param look_for_keys: set to False to disable searching for discoverable 
268              private key files in C{~/.ssh/} 
269          @type look_for_keys: bool 
270   
271          @raise BadHostKeyException: if the server's host key could not be 
272              verified 
273          @raise AuthenticationException: if authentication failed 
274          @raise SSHException: if there was any other error connecting or 
275              establishing an SSH session 
276          @raise socket.error: if a socket error occurred while connecting 
277          """ 
278          for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): 
279              if socktype == socket.SOCK_STREAM: 
280                  af = family 
281                  addr = sockaddr 
282                  break 
283          else: 
284              raise SSHException('No suitable address family for %s' % hostname) 
285          sock = socket.socket(af, socket.SOCK_STREAM) 
286          if timeout is not None: 
287              try: 
288                  sock.settimeout(timeout) 
289              except: 
290                  pass 
291          sock.connect(addr) 
292          t = self._transport = Transport(sock) 
293   
294          if self._log_channel is not None: 
295              t.set_log_channel(self._log_channel) 
296          t.start_client() 
297          ResourceManager.register(self, t) 
298   
299          server_key = t.get_remote_server_key() 
300          keytype = server_key.get_name() 
301   
302          if port == SSH_PORT: 
303              server_hostkey_name = hostname 
304          else: 
305              server_hostkey_name = "[%s]:%d" % (hostname, port) 
306          our_server_key = self._system_host_keys.get(server_hostkey_name, {}).get(keytype, None) 
307          if our_server_key is None: 
308              our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None) 
309          if our_server_key is None: 
310               
311              self._policy.missing_host_key(self, server_hostkey_name, server_key) 
312               
313              our_server_key = server_key 
314   
315          if server_key != our_server_key: 
316              raise BadHostKeyException(hostname, server_key, our_server_key) 
317   
318          if username is None: 
319              username = getpass.getuser() 
320   
321          if key_filename is None: 
322              key_filenames = [] 
323          elif isinstance(key_filename, (str, unicode)): 
324              key_filenames = [ key_filename ] 
325          else: 
326              key_filenames = key_filename 
327          self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys) 
 328   
330          """ 
331          Close this SSHClient and its underlying L{Transport}. 
332          """ 
333          if self._transport is None: 
334              return 
335          self._transport.close() 
336          self._transport = None 
 337   
339          """ 
340          Execute a command on the SSH server.  A new L{Channel} is opened and 
341          the requested command is executed.  The command's input and output 
342          streams are returned as python C{file}-like objects representing 
343          stdin, stdout, and stderr. 
344   
345          @param command: the command to execute 
346          @type command: str 
347          @param bufsize: interpreted the same way as by the built-in C{file()} function in python 
348          @type bufsize: int 
349          @return: the stdin, stdout, and stderr of the executing command 
350          @rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile}) 
351   
352          @raise SSHException: if the server fails to execute the command 
353          """ 
354          chan = self._transport.open_session() 
355          chan.exec_command(command) 
356          stdin = chan.makefile('wb', bufsize) 
357          stdout = chan.makefile('rb', bufsize) 
358          stderr = chan.makefile_stderr('rb', bufsize) 
359          return stdin, stdout, stderr 
 360   
362          """ 
363          Start an interactive shell session on the SSH server.  A new L{Channel} 
364          is opened and connected to a pseudo-terminal using the requested 
365          terminal type and size. 
366   
367          @param term: the terminal type to emulate (for example, C{"vt100"}) 
368          @type term: str 
369          @param width: the width (in characters) of the terminal window 
370          @type width: int 
371          @param height: the height (in characters) of the terminal window 
372          @type height: int 
373          @return: a new channel connected to the remote shell 
374          @rtype: L{Channel} 
375   
376          @raise SSHException: if the server fails to invoke a shell 
377          """ 
378          chan = self._transport.open_session() 
379          chan.get_pty(term, width, height) 
380          chan.invoke_shell() 
381          return chan 
 382   
384          """ 
385          Open an SFTP session on the SSH server. 
386   
387          @return: a new SFTP session object 
388          @rtype: L{SFTPClient} 
389          """ 
390          return self._transport.open_sftp_client() 
 391   
393          """ 
394          Return the underlying L{Transport} object for this SSH connection. 
395          This can be used to perform lower-level tasks, like opening specific 
396          kinds of channels. 
397   
398          @return: the Transport for this connection 
399          @rtype: L{Transport} 
400          """ 
401          return self._transport 
 402   
403 -    def _auth(self, username, password, pkey, key_filenames, allow_agent, look_for_keys): 
 404          """ 
405          Try, in order: 
406   
407              - The key passed in, if one was passed in. 
408              - Any key we can find through an SSH agent (if allowed). 
409              - Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (if allowed). 
410              - Plain username/password auth, if a password was given. 
411   
412          (The password might be needed to unlock a private key.) 
413          """ 
414          saved_exception = None 
415   
416          if pkey is not None: 
417              try: 
418                  self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint())) 
419                  self._transport.auth_publickey(username, pkey) 
420                  return 
421              except SSHException, e: 
422                  saved_exception = e 
423   
424          for key_filename in key_filenames: 
425              for pkey_class in (RSAKey, DSSKey): 
426                  try: 
427                      key = pkey_class.from_private_key_file(key_filename, password) 
428                      self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename)) 
429                      self._transport.auth_publickey(username, key) 
430                      return 
431                  except SSHException, e: 
432                      saved_exception = e 
433   
434          if allow_agent: 
435              for key in Agent().get_keys(): 
436                  try: 
437                      self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint())) 
438                      self._transport.auth_publickey(username, key) 
439                      return 
440                  except SSHException, e: 
441                      saved_exception = e 
442   
443          keyfiles = [] 
444          rsa_key = os.path.expanduser('~/.ssh/id_rsa') 
445          dsa_key = os.path.expanduser('~/.ssh/id_dsa') 
446          if os.path.isfile(rsa_key): 
447              keyfiles.append((RSAKey, rsa_key)) 
448          if os.path.isfile(dsa_key): 
449              keyfiles.append((DSSKey, dsa_key)) 
450           
451          rsa_key = os.path.expanduser('~/ssh/id_rsa') 
452          dsa_key = os.path.expanduser('~/ssh/id_dsa') 
453          if os.path.isfile(rsa_key): 
454              keyfiles.append((RSAKey, rsa_key)) 
455          if os.path.isfile(dsa_key): 
456              keyfiles.append((DSSKey, dsa_key)) 
457   
458          if not look_for_keys: 
459              keyfiles = [] 
460   
461          for pkey_class, filename in keyfiles: 
462              try: 
463                  key = pkey_class.from_private_key_file(filename, password) 
464                  self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename)) 
465                  self._transport.auth_publickey(username, key) 
466                  return 
467              except SSHException, e: 
468                  saved_exception = e 
469              except IOError, e: 
470                  saved_exception = e 
471   
472          if password is not None: 
473              try: 
474                  self._transport.auth_password(username, password) 
475                  return 
476              except SSHException, e: 
477                  saved_exception = e 
478   
479           
480          if saved_exception is not None: 
481              raise saved_exception 
482          raise SSHException('No authentication methods available') 
 483   
484 -    def _log(self, level, msg): 
 485          self._transport._log(level, msg) 
  486