~ubuntu-branches/ubuntu/raring/paramiko/raring

« back to all changes in this revision

Viewing changes to .pc/hostkey.patch/paramiko/client.py

  • Committer: Package Import Robot
  • Author(s): Michael Gilbert
  • Date: 2012-11-12 23:14:26 UTC
  • mfrom: (0.1.13 sid)
  • Revision ID: package-import@ubuntu.com-20121112231426-49vh9xcky219ercd
Tags: 1.7.7.1-3.1
* Non-maintainer upload.
* Drop problematic hostkey.patch (closes: #682050).

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
 
SSH_PORT = 22
40
 
 
41
 
class MissingHostKeyPolicy (object):
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
 
 
52
 
    def missing_host_key(self, client, hostname, key):
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
 
 
62
 
class AutoAddPolicy (MissingHostKeyPolicy):
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
 
 
68
 
    def missing_host_key(self, client, hostname, key):
69
 
        client._host_keys.add(hostname, key.get_name(), key)
70
 
        if client._host_keys_filename is not None:
71
 
            client.save_host_keys(client._host_keys_filename)
72
 
        client._log(DEBUG, 'Adding %s host key for %s: %s' %
73
 
                    (key.get_name(), hostname, hexlify(key.get_fingerprint())))
74
 
 
75
 
 
76
 
class RejectPolicy (MissingHostKeyPolicy):
77
 
    """
78
 
    Policy for automatically rejecting the unknown hostname & key.  This is
79
 
    used by L{SSHClient}.
80
 
    """
81
 
 
82
 
    def missing_host_key(self, client, hostname, key):
83
 
        client._log(DEBUG, 'Rejecting %s host key for %s: %s' %
84
 
                    (key.get_name(), hostname, hexlify(key.get_fingerprint())))
85
 
        raise SSHException('Unknown server %s' % hostname)
86
 
 
87
 
 
88
 
class WarningPolicy (MissingHostKeyPolicy):
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
 
    """
93
 
    def missing_host_key(self, client, hostname, key):
94
 
        warnings.warn('Unknown %s host key for %s: %s' %
95
 
                      (key.get_name(), hostname, hexlify(key.get_fingerprint())))
96
 
 
97
 
 
98
 
class SSHClient (object):
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
 
 
116
 
    def __init__(self):
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
 
 
128
 
    def load_system_host_keys(self, filename=None):
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
 
            # try the user's .ssh key file, and mask exceptions
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
 
 
158
 
    def load_host_keys(self, filename):
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
 
 
178
 
    def save_host_keys(self, filename):
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
 
 
196
 
    def get_host_keys(self):
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
 
 
206
 
    def set_log_channel(self, name):
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
 
 
216
 
    def set_missing_host_key_policy(self, policy):
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
 
            # some OS like AIX don't indicate SOCK_STREAM support, so just guess. :(
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
 
            # will raise exception if the key is rejected; let that fall out
316
 
            self._policy.missing_host_key(self, server_hostkey_name, server_key)
317
 
            # if the callback returns, assume the key is ok
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
 
 
334
 
    def close(self):
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
 
 
347
 
    def exec_command(self, command, bufsize=-1):
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
 
 
370
 
    def invoke_shell(self, term='vt100', width=80, height=24):
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
 
 
392
 
    def open_sftp(self):
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
 
 
401
 
    def get_transport(self):
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
 
        # look in ~/ssh/ for windows users:
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
 
        # if we got an auth-failed exception earlier, re-raise it
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