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
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
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
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
72
73
75 """
76 Policy for automatically rejecting the unknown hostname & key. This is
77 used by L{SSHClient}.
78 """
79
84
85
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 """
94
95
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
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
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
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
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
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
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
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
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
306 self._policy.missing_host_key(self, hostname, server_key)
307
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
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
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
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
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
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
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
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