Package paramiko :: Module client
[frames] | no frames]

Source Code for Module paramiko.client

  1  # Copyright (C) 2006-2007  Robey Pointer <robeypointer@gmail.com> 
  2  # 
  3  # This file is part of paramiko. 
  4  # 
  5  # Paramiko is free software; you can redistribute it and/or modify it under the 
  6  # terms of the GNU Lesser General Public License as published by the Free 
  7  # Software Foundation; either version 2.1 of the License, or (at your option) 
  8  # any later version. 
  9  # 
 10  # Paramiko is distrubuted in the hope that it will be useful, but WITHOUT ANY 
 11  # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 
 12  # A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more 
 13  # details. 
 14  # 
 15  # You should have received a copy of the GNU Lesser General Public License 
 16  # along with Paramiko; if not, write to the Free Software Foundation, Inc., 
 17  # 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA. 
 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.config import SSH_PORT 
 32  from paramiko.dsskey import DSSKey 
 33  from paramiko.hostkeys import HostKeys 
 34  from paramiko.resource import ResourceManager 
 35  from paramiko.rsakey import RSAKey 
 36  from paramiko.ssh_exception import PasswordRequiredException, SSHException, BadHostKeyException 
 37  from paramiko.transport import Transport 
 38  from paramiko.util import retry_on_signal 
 39   
 40   
41 -class MissingHostKeyPolicy (object):
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
52 - def missing_host_key(self, client, hostname, key):
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
62 -class AutoAddPolicy (MissingHostKeyPolicy):
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
68 - def missing_host_key(self, client, hostname, key):
69 client._host_keys.add(hostname, key.get_name(), key) 70 if client._host_keys_filename is not None: 71 client.save_host_keys(client._host_keys_filename) 72 client._log(DEBUG, 'Adding %s host key for %s: %s' % 73 (key.get_name(), hostname, hexlify(key.get_fingerprint())))
74 75
76 -class RejectPolicy (MissingHostKeyPolicy):
77 """ 78 Policy for automatically rejecting the unknown hostname & key. This is 79 used by L{SSHClient}. 80 """ 81
82 - def missing_host_key(self, client, hostname, key):
83 client._log(DEBUG, 'Rejecting %s host key for %s: %s' % 84 (key.get_name(), hostname, hexlify(key.get_fingerprint()))) 85 raise SSHException('Server %r not found in known_hosts' % hostname)
86 87
88 -class WarningPolicy (MissingHostKeyPolicy):
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 """
93 - def missing_host_key(self, client, hostname, key):
94 warnings.warn('Unknown %s host key for %s: %s' % 95 (key.get_name(), hostname, hexlify(key.get_fingerprint())))
96 97
98 -class SSHClient (object):
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
116 - def __init__(self):
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 self._agent = None
127
128 - def load_system_host_keys(self, filename=None):
129 """ 130 Load host keys from a system (read-only) file. Host keys read with 131 this method will not be saved back by L{save_host_keys}. 132 133 This method can be called multiple times. Each new set of host keys 134 will be merged with the existing set (new replacing old if there are 135 conflicts). 136 137 If C{filename} is left as C{None}, an attempt will be made to read 138 keys from the user's local "known hosts" file, as used by OpenSSH, 139 and no exception will be raised if the file can't be read. This is 140 probably only useful on posix. 141 142 @param filename: the filename to read, or C{None} 143 @type filename: str 144 145 @raise IOError: if a filename was provided and the file could not be 146 read 147 """ 148 if filename is None: 149 # try the user's .ssh key file, and mask exceptions 150 filename = os.path.expanduser('~/.ssh/known_hosts') 151 try: 152 self._system_host_keys.load(filename) 153 except IOError: 154 pass 155 return 156 self._system_host_keys.load(filename)
157
158 - def load_host_keys(self, filename):
159 """ 160 Load host keys from a local host-key file. Host keys read with this 161 method will be checked I{after} keys loaded via L{load_system_host_keys}, 162 but will be saved back by L{save_host_keys} (so they can be modified). 163 The missing host key policy L{AutoAddPolicy} adds keys to this set and 164 saves them, when connecting to a previously-unknown server. 165 166 This method can be called multiple times. Each new set of host keys 167 will be merged with the existing set (new replacing old if there are 168 conflicts). When automatically saving, the last hostname is used. 169 170 @param filename: the filename to read 171 @type filename: str 172 173 @raise IOError: if the filename could not be read 174 """ 175 self._host_keys_filename = filename 176 self._host_keys.load(filename)
177
178 - def save_host_keys(self, filename):
179 """ 180 Save the host keys back to a file. Only the host keys loaded with 181 L{load_host_keys} (plus any added directly) will be saved -- not any 182 host keys loaded with L{load_system_host_keys}. 183 184 @param filename: the filename to save to 185 @type filename: str 186 187 @raise IOError: if the file could not be written 188 """ 189 190 # update local host keys from file (in case other SSH clients 191 # have written to the known_hosts file meanwhile. 192 if self.known_hosts is not None: 193 self.load_host_keys(self.known_hosts) 194 195 f = open(filename, 'w') 196 for hostname, keys in self._host_keys.iteritems(): 197 for keytype, key in keys.iteritems(): 198 f.write('%s %s %s\n' % (hostname, keytype, key.get_base64())) 199 f.close()
200
201 - def get_host_keys(self):
202 """ 203 Get the local L{HostKeys} object. This can be used to examine the 204 local host keys or change them. 205 206 @return: the local host keys 207 @rtype: L{HostKeys} 208 """ 209 return self._host_keys
210
211 - def set_log_channel(self, name):
212 """ 213 Set the channel for logging. The default is C{"paramiko.transport"} 214 but it can be set to anything you want. 215 216 @param name: new channel name for logging 217 @type name: str 218 """ 219 self._log_channel = name
220
221 - def set_missing_host_key_policy(self, policy):
222 """ 223 Set the policy to use when connecting to a server that doesn't have a 224 host key in either the system or local L{HostKeys} objects. The 225 default policy is to reject all unknown servers (using L{RejectPolicy}). 226 You may substitute L{AutoAddPolicy} or write your own policy class. 227 228 @param policy: the policy to use when receiving a host key from a 229 previously-unknown server 230 @type policy: L{MissingHostKeyPolicy} 231 """ 232 self._policy = policy
233
234 - def connect(self, hostname, port=SSH_PORT, username=None, password=None, passphrase=None, 235 pkey=None, key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, 236 compress=False, sock=None):
237 """ 238 Connect to an SSH server and authenticate to it. The server's host key 239 is checked against the system host keys (see L{load_system_host_keys}) 240 and any local host keys (L{load_host_keys}). If the server's hostname 241 is not found in either set of host keys, the missing host key policy 242 is used (see L{set_missing_host_key_policy}). The default policy is 243 to reject the key and raise an L{SSHException}. 244 245 Authentication is attempted in the following order of priority: 246 247 - The C{pkey} or C{key_filename} passed in (if any) 248 - Any key we can find through an SSH agent 249 - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/} 250 - Plain username/password auth, if a password was given 251 252 If a private key requires a password to unlock it, and a password is 253 passed in, that password will be used to attempt to unlock the key. 254 255 @param hostname: the server to connect to 256 @type hostname: str 257 @param port: the server port to connect to 258 @type port: int 259 @param username: the username to authenticate as (defaults to the 260 current local username) 261 @type username: str 262 @param password: a password to use for authentication or for unlocking 263 a private key 264 @type password: str 265 @param passphrase: a passphrase to use for unlocking 266 a private key in case the password is already needed for two-factor 267 authentication 268 @type passphrase: str 269 @param pkey: an optional private key to use for authentication 270 @type pkey: L{PKey} 271 @param key_filename: the filename, or list of filenames, of optional 272 private key(s) to try for authentication 273 @type key_filename: str or list(str) 274 @param timeout: an optional timeout (in seconds) for the TCP connect 275 @type timeout: float 276 @param allow_agent: set to False to disable connecting to the SSH agent 277 @type allow_agent: bool 278 @param look_for_keys: set to False to disable searching for discoverable 279 private key files in C{~/.ssh/} 280 @type look_for_keys: bool 281 @param compress: set to True to turn on compression 282 @type compress: bool 283 @param sock: an open socket or socket-like object (such as a 284 L{Channel}) to use for communication to the target host 285 @type sock: socket 286 287 @raise BadHostKeyException: if the server's host key could not be 288 verified 289 @raise AuthenticationException: if authentication failed 290 @raise SSHException: if there was any other error connecting or 291 establishing an SSH session 292 @raise socket.error: if a socket error occurred while connecting 293 """ 294 if not sock: 295 for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): 296 if socktype == socket.SOCK_STREAM: 297 af = family 298 addr = sockaddr 299 break 300 else: 301 # some OS like AIX don't indicate SOCK_STREAM support, so just guess. :( 302 af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM) 303 sock = socket.socket(af, socket.SOCK_STREAM) 304 if timeout is not None: 305 try: 306 sock.settimeout(timeout) 307 except: 308 pass 309 retry_on_signal(lambda: sock.connect(addr)) 310 311 t = self._transport = Transport(sock) 312 t.use_compression(compress=compress) 313 if self._log_channel is not None: 314 t.set_log_channel(self._log_channel) 315 t.start_client() 316 ResourceManager.register(self, t) 317 318 server_key = t.get_remote_server_key() 319 keytype = server_key.get_name() 320 321 if port == SSH_PORT: 322 server_hostkey_name = hostname 323 else: 324 server_hostkey_name = "[%s]:%d" % (hostname, port) 325 our_server_key = self._system_host_keys.get(server_hostkey_name, {}).get(keytype, None) 326 if our_server_key is None: 327 our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None) 328 if our_server_key is None: 329 # will raise exception if the key is rejected; let that fall out 330 self._policy.missing_host_key(self, server_hostkey_name, server_key) 331 # if the callback returns, assume the key is ok 332 our_server_key = server_key 333 334 if server_key != our_server_key: 335 raise BadHostKeyException(hostname, server_key, our_server_key) 336 337 if username is None: 338 username = getpass.getuser() 339 340 if key_filename is None: 341 key_filenames = [] 342 elif isinstance(key_filename, (str, unicode)): 343 key_filenames = [ key_filename ] 344 else: 345 key_filenames = key_filename 346 self._auth(username, password, passphrase, pkey, key_filenames, allow_agent, look_for_keys)
347
348 - def close(self):
349 """ 350 Close this SSHClient and its underlying L{Transport}. 351 """ 352 if self._transport is None: 353 return 354 self._transport.close() 355 self._transport = None 356 357 if self._agent != None: 358 self._agent.close() 359 self._agent = None
360
361 - def exec_command(self, command, bufsize=-1, timeout=None, get_pty=False):
362 """ 363 Execute a command on the SSH server. A new L{Channel} is opened and 364 the requested command is executed. The command's input and output 365 streams are returned as python C{file}-like objects representing 366 stdin, stdout, and stderr. 367 368 @param command: the command to execute 369 @type command: str 370 @param bufsize: interpreted the same way as by the built-in C{file()} function in python 371 @type bufsize: int 372 @param timeout: set command's channel timeout. See L{Channel.settimeout}.settimeout 373 @type timeout: int 374 @return: the stdin, stdout, and stderr of the executing command 375 @rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile}) 376 377 @raise SSHException: if the server fails to execute the command 378 """ 379 chan = self._transport.open_session() 380 if(get_pty): 381 chan.get_pty() 382 chan.settimeout(timeout) 383 chan.exec_command(command) 384 stdin = chan.makefile('wb', bufsize) 385 stdout = chan.makefile('rb', bufsize) 386 stderr = chan.makefile_stderr('rb', bufsize) 387 return stdin, stdout, stderr
388
389 - def invoke_shell(self, term='vt100', width=80, height=24, width_pixels=0, 390 height_pixels=0):
391 """ 392 Start an interactive shell session on the SSH server. A new L{Channel} 393 is opened and connected to a pseudo-terminal using the requested 394 terminal type and size. 395 396 @param term: the terminal type to emulate (for example, C{"vt100"}) 397 @type term: str 398 @param width: the width (in characters) of the terminal window 399 @type width: int 400 @param height: the height (in characters) of the terminal window 401 @type height: int 402 @param width_pixels: the width (in pixels) of the terminal window 403 @type width_pixels: int 404 @param height_pixels: the height (in pixels) of the terminal window 405 @type height_pixels: int 406 @return: a new channel connected to the remote shell 407 @rtype: L{Channel} 408 409 @raise SSHException: if the server fails to invoke a shell 410 """ 411 chan = self._transport.open_session() 412 chan.get_pty(term, width, height, width_pixels, height_pixels) 413 chan.invoke_shell() 414 return chan
415
416 - def open_sftp(self):
417 """ 418 Open an SFTP session on the SSH server. 419 420 @return: a new SFTP session object 421 @rtype: L{SFTPClient} 422 """ 423 return self._transport.open_sftp_client()
424
425 - def get_transport(self):
426 """ 427 Return the underlying L{Transport} object for this SSH connection. 428 This can be used to perform lower-level tasks, like opening specific 429 kinds of channels. 430 431 @return: the Transport for this connection 432 @rtype: L{Transport} 433 """ 434 return self._transport
435
436 - def _auth(self, username, password, passphrase, pkey, key_filenames, allow_agent, look_for_keys):
437 """ 438 Try, in order: 439 440 - The key passed in, if one was passed in. 441 - Any key we can find through an SSH agent (if allowed). 442 - Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (if allowed). 443 - Plain username/password auth, if a password was given. 444 445 The password might be needed to unlock a private key, or for 446 two-factor authentication [for which it is required]. 447 448 If the SSH key needs unlocking via passphrase and two-factor 449 auth requires a password, the passphrase is unused for unlocking 450 the key whereas the password is used for server authentication. 451 """ 452 saved_exception = None 453 two_factor = False 454 allowed_types = [] 455 if passphrase is None: passphrase = password 456 457 if pkey is not None: 458 try: 459 self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint())) 460 allowed_types = self._transport.auth_publickey(username, pkey) 461 two_factor = (allowed_types == ['password']) 462 if not two_factor: 463 return 464 except SSHException, e: 465 saved_exception = e 466 467 if not two_factor and allow_agent: 468 if self._agent == None: 469 self._agent = Agent() 470 471 for key in self._agent.get_keys(): 472 try: 473 self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint())) 474 # for 2-factor auth a successfully auth'd key will result in ['password'] 475 allowed_types = self._transport.auth_publickey(username, key) 476 two_factor = (allowed_types == ['password']) 477 if not two_factor: 478 return 479 break 480 except SSHException, e: 481 saved_exception = e 482 483 if not two_factor: 484 for key_filename in key_filenames: 485 for pkey_class in (RSAKey, DSSKey): 486 try: 487 key = pkey_class.from_private_key_file(key_filename, passphrase) 488 self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename)) 489 allowed_types = self._transport.auth_publickey(username, key) 490 two_factor = (allowed_types == ['password']) 491 if not two_factor: 492 return 493 break 494 except PasswordRequiredException, e: 495 saved_exception = e 496 break 497 except SSHException, e: 498 saved_exception = e 499 500 if not two_factor: 501 keyfiles = [] 502 rsa_key = os.path.expanduser('~/.ssh/id_rsa') 503 dsa_key = os.path.expanduser('~/.ssh/id_dsa') 504 if os.path.isfile(rsa_key): 505 keyfiles.append((RSAKey, rsa_key)) 506 if os.path.isfile(dsa_key): 507 keyfiles.append((DSSKey, dsa_key)) 508 # look in ~/ssh/ for windows users: 509 rsa_key = os.path.expanduser('~/ssh/id_rsa') 510 dsa_key = os.path.expanduser('~/ssh/id_dsa') 511 if os.path.isfile(rsa_key): 512 keyfiles.append((RSAKey, rsa_key)) 513 if os.path.isfile(dsa_key): 514 keyfiles.append((DSSKey, dsa_key)) 515 516 if not look_for_keys: 517 keyfiles = [] 518 519 for pkey_class, filename in keyfiles: 520 try: 521 key = pkey_class.from_private_key_file(filename, passphrase) 522 self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename)) 523 # for 2-factor auth a successfully auth'd key will result in ['password'] 524 allowed_types = self._transport.auth_publickey(username, key) 525 two_factor = (allowed_types == ['password']) 526 if not two_factor: 527 return 528 break 529 except SSHException, e: 530 saved_exception = e 531 except IOError, e: 532 saved_exception = e 533 534 if password is not None: 535 try: 536 self._transport.auth_password(username, password) 537 return 538 except SSHException, e: 539 saved_exception = e 540 elif two_factor: 541 raise SSHException('Two-factor authentication requires a password') 542 543 # if we got an auth-failed exception earlier, re-raise it 544 if saved_exception is not None: 545 raise saved_exception 546 raise SSHException('No authentication methods available')
547
548 - def _log(self, level, msg):
549 self._transport._log(level, msg)
550