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