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 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
277 if timeout is not None:
278 try:
279 sock.settimeout(timeout)
280 except:
281 pass
282
283 sock.connect((hostname, port))
284 t = self._transport = Transport(sock)
285
286 if self._log_channel is not None:
287 t.set_log_channel(self._log_channel)
288 t.start_client()
289 ResourceManager.register(self, t)
290
291 server_key = t.get_remote_server_key()
292 keytype = server_key.get_name()
293
294 our_server_key = self._system_host_keys.get(hostname, {}).get(keytype, None)
295 if our_server_key is None:
296 our_server_key = self._host_keys.get(hostname, {}).get(keytype, None)
297 if our_server_key is None:
298
299 self._policy.missing_host_key(self, hostname, server_key)
300
301 our_server_key = server_key
302
303 if server_key != our_server_key:
304 raise BadHostKeyException(hostname, server_key, our_server_key)
305
306 if username is None:
307 username = getpass.getuser()
308
309 if key_filename is None:
310 key_filenames = []
311 elif isinstance(key_filename, (str, unicode)):
312 key_filenames = [ key_filename ]
313 else:
314 key_filenames = key_filename
315 self._auth(username, password, pkey, key_filenames, allow_agent, look_for_keys)
316
318 """
319 Close this SSHClient and its underlying L{Transport}.
320 """
321 if self._transport is None:
322 return
323 self._transport.close()
324 self._transport = None
325
327 """
328 Execute a command on the SSH server. A new L{Channel} is opened and
329 the requested command is executed. The command's input and output
330 streams are returned as python C{file}-like objects representing
331 stdin, stdout, and stderr.
332
333 @param command: the command to execute
334 @type command: str
335 @param bufsize: interpreted the same way as by the built-in C{file()} function in python
336 @type bufsize: int
337 @return: the stdin, stdout, and stderr of the executing command
338 @rtype: tuple(L{ChannelFile}, L{ChannelFile}, L{ChannelFile})
339
340 @raise SSHException: if the server fails to execute the command
341 """
342 chan = self._transport.open_session()
343 chan.exec_command(command)
344 stdin = chan.makefile('wb', bufsize)
345 stdout = chan.makefile('rb', bufsize)
346 stderr = chan.makefile_stderr('rb', bufsize)
347 return stdin, stdout, stderr
348
350 """
351 Start an interactive shell session on the SSH server. A new L{Channel}
352 is opened and connected to a pseudo-terminal using the requested
353 terminal type and size.
354
355 @param term: the terminal type to emulate (for example, C{"vt100"})
356 @type term: str
357 @param width: the width (in characters) of the terminal window
358 @type width: int
359 @param height: the height (in characters) of the terminal window
360 @type height: int
361 @return: a new channel connected to the remote shell
362 @rtype: L{Channel}
363
364 @raise SSHException: if the server fails to invoke a shell
365 """
366 chan = self._transport.open_session()
367 chan.get_pty(term, width, height)
368 chan.invoke_shell()
369 return chan
370
372 """
373 Open an SFTP session on the SSH server.
374
375 @return: a new SFTP session object
376 @rtype: L{SFTPClient}
377 """
378 return self._transport.open_sftp_client()
379
381 """
382 Return the underlying L{Transport} object for this SSH connection.
383 This can be used to perform lower-level tasks, like opening specific
384 kinds of channels.
385
386 @return: the Transport for this connection
387 @rtype: L{Transport}
388 """
389 return self._transport
390
391 - def _auth(self, username, password, pkey, key_filenames, allow_agent, look_for_keys):
392 """
393 Try, in order:
394
395 - The key passed in, if one was passed in.
396 - Any key we can find through an SSH agent (if allowed).
397 - Any "id_rsa" or "id_dsa" key discoverable in ~/.ssh/ (if allowed).
398 - Plain username/password auth, if a password was given.
399
400 (The password might be needed to unlock a private key.)
401 """
402 saved_exception = None
403
404 if pkey is not None:
405 try:
406 self._log(DEBUG, 'Trying SSH key %s' % hexlify(pkey.get_fingerprint()))
407 self._transport.auth_publickey(username, pkey)
408 return
409 except SSHException, e:
410 saved_exception = e
411
412 for key_filename in key_filenames:
413 for pkey_class in (RSAKey, DSSKey):
414 try:
415 key = pkey_class.from_private_key_file(key_filename, password)
416 self._log(DEBUG, 'Trying key %s from %s' % (hexlify(key.get_fingerprint()), key_filename))
417 self._transport.auth_publickey(username, key)
418 return
419 except SSHException, e:
420 saved_exception = e
421
422 if allow_agent:
423 for key in Agent().get_keys():
424 try:
425 self._log(DEBUG, 'Trying SSH agent key %s' % hexlify(key.get_fingerprint()))
426 self._transport.auth_publickey(username, key)
427 return
428 except SSHException, e:
429 saved_exception = e
430
431 keyfiles = []
432 rsa_key = os.path.expanduser('~/.ssh/id_rsa')
433 dsa_key = os.path.expanduser('~/.ssh/id_dsa')
434 if os.path.isfile(rsa_key):
435 keyfiles.append((RSAKey, rsa_key))
436 if os.path.isfile(dsa_key):
437 keyfiles.append((DSSKey, dsa_key))
438
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 if not look_for_keys:
447 keyfiles = []
448
449 for pkey_class, filename in keyfiles:
450 try:
451 key = pkey_class.from_private_key_file(filename, password)
452 self._log(DEBUG, 'Trying discovered key %s in %s' % (hexlify(key.get_fingerprint()), filename))
453 self._transport.auth_publickey(username, key)
454 return
455 except SSHException, e:
456 saved_exception = e
457 except IOError, e:
458 saved_exception = e
459
460 if password is not None:
461 try:
462 self._transport.auth_password(username, password)
463 return
464 except SSHException, e:
465 saved_exception = e
466
467
468 if saved_exception is not None:
469 raise saved_exception
470 raise SSHException('No authentication methods available')
471
472 - def _log(self, level, msg):
473 self._transport._log(level, msg)
474