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 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 f = open(filename, 'w') 190 f.write('# SSH host keys collected by paramiko\n') 191 for hostname, keys in self._host_keys.iteritems(): 192 for keytype, key in keys.iteritems(): 193 f.write('%s %s %s\n' % (hostname, keytype, key.get_base64())) 194 f.close()
195
196 - def get_host_keys(self):
197 """ 198 Get the local L{HostKeys} object. This can be used to examine the 199 local host keys or change them. 200 201 @return: the local host keys 202 @rtype: L{HostKeys} 203 """ 204 return self._host_keys
205
206 - def set_log_channel(self, name):
207 """ 208 Set the channel for logging. The default is C{"paramiko.transport"} 209 but it can be set to anything you want. 210 211 @param name: new channel name for logging 212 @type name: str 213 """ 214 self._log_channel = name
215
216 - def set_missing_host_key_policy(self, policy):
217 """ 218 Set the policy to use when connecting to a server that doesn't have a 219 host key in either the system or local L{HostKeys} objects. The 220 default policy is to reject all unknown servers (using L{RejectPolicy}). 221 You may substitute L{AutoAddPolicy} or write your own policy class. 222 223 @param policy: the policy to use when receiving a host key from a 224 previously-unknown server 225 @type policy: L{MissingHostKeyPolicy} 226 """ 227 self._policy = policy
228
229 - def connect(self, hostname, port=SSH_PORT, username=None, password=None, pkey=None, 230 key_filename=None, timeout=None, allow_agent=True, look_for_keys=True, 231 compress=False):
232 """ 233 Connect to an SSH server and authenticate to it. The server's host key 234 is checked against the system host keys (see L{load_system_host_keys}) 235 and any local host keys (L{load_host_keys}). If the server's hostname 236 is not found in either set of host keys, the missing host key policy 237 is used (see L{set_missing_host_key_policy}). The default policy is 238 to reject the key and raise an L{SSHException}. 239 240 Authentication is attempted in the following order of priority: 241 242 - The C{pkey} or C{key_filename} passed in (if any) 243 - Any key we can find through an SSH agent 244 - Any "id_rsa" or "id_dsa" key discoverable in C{~/.ssh/} 245 - Plain username/password auth, if a password was given 246 247 If a private key requires a password to unlock it, and a password is 248 passed in, that password will be used to attempt to unlock the key. 249 250 @param hostname: the server to connect to 251 @type hostname: str 252 @param port: the server port to connect to 253 @type port: int 254 @param username: the username to authenticate as (defaults to the 255 current local username) 256 @type username: str 257 @param password: a password to use for authentication or for unlocking 258 a private key 259 @type password: str 260 @param pkey: an optional private key to use for authentication 261 @type pkey: L{PKey} 262 @param key_filename: the filename, or list of filenames, of optional 263 private key(s) to try for authentication 264 @type key_filename: str or list(str) 265 @param timeout: an optional timeout (in seconds) for the TCP connect 266 @type timeout: float 267 @param allow_agent: set to False to disable connecting to the SSH agent 268 @type allow_agent: bool 269 @param look_for_keys: set to False to disable searching for discoverable 270 private key files in C{~/.ssh/} 271 @type look_for_keys: bool 272 @param compress: set to True to turn on compression 273 @type compress: bool 274 275 @raise BadHostKeyException: if the server's host key could not be 276 verified 277 @raise AuthenticationException: if authentication failed 278 @raise SSHException: if there was any other error connecting or 279 establishing an SSH session 280 @raise socket.error: if a socket error occurred while connecting 281 """ 282 for (family, socktype, proto, canonname, sockaddr) in socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM): 283 if socktype == socket.SOCK_STREAM: 284 af = family 285 addr = sockaddr 286 break 287 else: 288 # some OS like AIX don't indicate SOCK_STREAM support, so just guess. :( 289 af, _, _, _, addr = socket.getaddrinfo(hostname, port, socket.AF_UNSPEC, socket.SOCK_STREAM) 290 sock = socket.socket(af, socket.SOCK_STREAM) 291 if timeout is not None: 292 try: 293 sock.settimeout(timeout) 294 except: 295 pass 296 sock.connect(addr) 297 t = self._transport = Transport(sock) 298 t.use_compression(compress=compress) 299 if self._log_channel is not None: 300 t.set_log_channel(self._log_channel) 301 t.start_client() 302 ResourceManager.register(self, t) 303 304 server_key = t.get_remote_server_key() 305 keytype = server_key.get_name() 306 307 if port == SSH_PORT: 308 server_hostkey_name = hostname 309 else: 310 server_hostkey_name = "[%s]:%d" % (hostname, port) 311 our_server_key = self._system_host_keys.get(server_hostkey_name, {}).get(keytype, None) 312 if our_server_key is None: 313 our_server_key = self._host_keys.get(server_hostkey_name, {}).get(keytype, None) 314 if our_server_key is None: 315 # will raise exception if the key is rejected; let that fall out 316 self._policy.missing_host_key(self, server_hostkey_name, server_key) 317 # if the callback returns, assume the key is ok 318 our_server_key = server_key 319 320 if server_key != our_server_key: 321 raise BadHostKeyException(hostname, server_key, our_server_key) 322 323 if username is None: 324 username = getpass.getuser() 325 326 if key_filename is None: 327 key_filenames = [] 328 elif isinstance(key_filename, (str, unicode)): 329 key_filenames = [ key_filename ] 330 else: 331 key_filenames = key_filename 332 self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys)
333
334 - def close(self):
335 """ 336 Close this SSHClient and its underlying L{Transport}. 337 """ 338 if self._transport is None: 339 return 340 self._transport.close() 341 self._transport = None 342 343 if self._agent != None: 344 self._agent.close() 345 self._agent = None
346
347 - def exec_command(self, command, bufsize=-1):
348 """ 349 Execute a command on the SSH server. A new L{Channel} is opened and 350 the requested command is executed. The command's input and output 351 streams are returned as python C{file}-like objects representing 352 stdin, stdout, and stderr. 353 354 @param command: the command to execute 355 @type command: str 356 @param bufsize: interpreted the same way as by the built-in C{file()} function in python 357 @type bufsize: int 358 @return: the stdin, stdout, and stderr of the executing command 359 @rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile}) 360 361 @raise SSHException: if the server fails to execute the command 362 """ 363 chan = self._transport.open_session() 364 chan.exec_command(command) 365 stdin = chan.makefile('wb', bufsize) 366 stdout = chan.makefile('rb', bufsize) 367 stderr = chan.makefile_stderr('rb', bufsize) 368 return stdin, stdout, stderr
369
370 - def invoke_shell(self, term='vt100', width=80, height=24):
371 """ 372 Start an interactive shell session on the SSH server. A new L{Channel} 373 is opened and connected to a pseudo-terminal using the requested 374 terminal type and size. 375 376 @param term: the terminal type to emulate (for example, C{"vt100"}) 377 @type term: str 378 @param width: the width (in characters) of the terminal window 379 @type width: int 380 @param height: the height (in characters) of the terminal window 381 @type height: int 382 @return: a new channel connected to the remote shell 383 @rtype: L{Channel} 384 385 @raise SSHException: if the server fails to invoke a shell 386 """ 387 chan = self._transport.open_session() 388 chan.get_pty(term, width, height) 389 chan.invoke_shell() 390 return chan
391
392 - def open_sftp(self):
393 """ 394 Open an SFTP session on the SSH server. 395 396 @return: a new SFTP session object 397 @rtype: L{SFTPClient} 398 """ 399 return self._transport.open_sftp_client()
400
401 - def get_transport(self):
402 """ 403 Return the underlying L{Transport} object for this SSH connection. 404 This can be used to perform lower-level tasks, like opening specific 405 kinds of channels. 406 407 @return: the Transport for this connection 408 @rtype: L{Transport} 409 """ 410 return self._transport
411
412 - def _auth(self, username, password, pkey, key_filenames, allow_agent, look_for_keys):
413 """ 414 Try, in order: 415 416 - The key passed in, if one was passed in. 417 - Any key we can find through an SSH agent (if allowed). 418 - Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (if allowed). 419 - Plain username/password auth, if a password was given. 420 421 (The password might be needed to unlock a private key.) 422 """ 423 saved_exception = None 424 425 if pkey is not None: 426 try: 427 self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint())) 428 self._transport.auth_publickey(username, pkey) 429 return 430 except SSHException, e: 431 saved_exception = e 432 433 for key_filename in key_filenames: 434 for pkey_class in (RSAKey, DSSKey): 435 try: 436 key = pkey_class.from_private_key_file(key_filename, password) 437 self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename)) 438 self._transport.auth_publickey(username, key) 439 return 440 except SSHException, e: 441 saved_exception = e 442 443 if allow_agent: 444 if self._agent == None: 445 self._agent = Agent() 446 447 for key in self._agent.get_keys(): 448 try: 449 self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint())) 450 self._transport.auth_publickey(username, key) 451 return 452 except SSHException, e: 453 saved_exception = e 454 455 keyfiles = [] 456 rsa_key = os.path.expanduser('~/.ssh/id_rsa') 457 dsa_key = os.path.expanduser('~/.ssh/id_dsa') 458 if os.path.isfile(rsa_key): 459 keyfiles.append((RSAKey, rsa_key)) 460 if os.path.isfile(dsa_key): 461 keyfiles.append((DSSKey, dsa_key)) 462 # look in ~/ssh/ for windows users: 463 rsa_key = os.path.expanduser('~/ssh/id_rsa') 464 dsa_key = os.path.expanduser('~/ssh/id_dsa') 465 if os.path.isfile(rsa_key): 466 keyfiles.append((RSAKey, rsa_key)) 467 if os.path.isfile(dsa_key): 468 keyfiles.append((DSSKey, dsa_key)) 469 470 if not look_for_keys: 471 keyfiles = [] 472 473 for pkey_class, filename in keyfiles: 474 try: 475 key = pkey_class.from_private_key_file(filename, password) 476 self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename)) 477 self._transport.auth_publickey(username, key) 478 return 479 except SSHException, e: 480 saved_exception = e 481 except IOError, e: 482 saved_exception = e 483 484 if password is not None: 485 try: 486 self._transport.auth_password(username, password) 487 return 488 except SSHException, e: 489 saved_exception = e 490 491 # if we got an auth-failed exception earlier, re-raise it 492 if saved_exception is not None: 493 raise saved_exception 494 raise SSHException('No authentication methods available')
495
496 - def _log(self, level, msg):
497 self._transport._log(level, msg)
498