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 self._agent = None
127
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
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
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
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
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
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
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
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
316 self._policy.missing_host_key(self, server_hostkey_name, server_key)
317
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
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
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
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
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
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
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
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