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.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   
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('Unknown server %s' % 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
127 - def load_system_host_keys(self, filename=None):
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 # try the user's .ssh key file, and mask exceptions 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
157 - def load_host_keys(self, filename):
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
177 - def save_host_keys(self, filename):
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
195 - def get_host_keys(self):
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
205 - def set_log_channel(self, name):
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
215 - def set_missing_host_key_policy(self, policy):
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 # will raise exception if the key is rejected; let that fall out 311 self._policy.missing_host_key(self, server_hostkey_name, server_key) 312 # if the callback returns, assume the key is ok 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
329 - def close(self):
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
338 - def exec_command(self, command, bufsize=-1):
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
361 - def invoke_shell(self, term='vt100', width=80, height=24):
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
383 - def open_sftp(self):
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
392 - def get_transport(self):
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 # look in ~/ssh/ for windows users: 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 # if we got an auth-failed exception earlier, re-raise it 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