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
39 SSH_PORT = 22
40
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
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
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
74
75
77 """
78 Policy for automatically rejecting the unknown hostname & key. This is
79 used by L{SSHClient}.
80 """
81
86
87
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 """
96
97
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
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
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
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
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
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
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
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
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
311 self._policy.missing_host_key(self, server_hostkey_name, server_key)
312
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
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
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
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
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
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
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
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