~bzr-pqm/bzr/bzr.dev

1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
1
# Copyright (C) 2005 Robey Pointer <robey@lag.net>
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
2
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
3
#
4
# This program is free software; you can redistribute it and/or modify
5
# it under the terms of the GNU General Public License as published by
6
# the Free Software Foundation; either version 2 of the License, or
7
# (at your option) any later version.
8
#
9
# This program is distributed in the hope that it will be useful,
10
# but WITHOUT ANY WARRANTY; without even the implied warranty of
11
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12
# GNU General Public License for more details.
13
#
14
# You should have received a copy of the GNU General Public License
15
# along with this program; if not, write to the Free Software
4183.7.1 by Sabin Iacob
update FSF mailing address
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
17
18
"""Foundation SSH support for SFTP and smart server."""
19
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
20
import errno
4304.2.1 by Vincent Ladeuil
Fix bug #367726 by reverting some default user handling introduced
21
import getpass
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
22
import logging
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
23
import os
24
import socket
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
25
import subprocess
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
26
import sys
27
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
28
from bzrlib import (
29
    config,
30
    errors,
31
    osutils,
32
    trace,
33
    ui,
34
    )
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
35
36
try:
37
    import paramiko
38
except ImportError, e:
2104.5.1 by John Arbash Meinel
Remove the strict dependency on paramiko for ssh access
39
    # If we have an ssh subprocess, we don't strictly need paramiko for all ssh
40
    # access
41
    paramiko = None
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
42
else:
43
    from paramiko.sftp_client import SFTPClient
44
45
46
SYSTEM_HOSTKEYS = {}
47
BZR_HOSTKEYS = {}
48
49
1951.1.5 by Andrew Bennetts
Fix some missing imports with a bit of help from pyflakes.
50
_paramiko_version = getattr(paramiko, '__version_info__', (0, 0, 0))
51
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
52
# Paramiko 1.5 tries to open a socket.AF_UNIX in order to connect
53
# to ssh-agent. That attribute doesn't exist on win32 (it does in cygwin)
54
# so we get an AttributeError exception. So we will not try to
55
# connect to an agent if we are on win32 and using Paramiko older than 1.6
56
_use_ssh_agent = (sys.platform != 'win32' or _paramiko_version >= (1, 6, 0))
57
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
58
59
class SSHVendorManager(object):
60
    """Manager for manage SSH vendors."""
61
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
62
    # Note, although at first sign the class interface seems similar to
2221.5.22 by Dmitry Vasiliev
Updated note about registry.Registry
63
    # bzrlib.registry.Registry it is not possible/convenient to directly use
64
    # the Registry because the class just has "get()" interface instead of the
65
    # Registry's "get(key)".
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
66
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
67
    def __init__(self):
68
        self._ssh_vendors = {}
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
69
        self._cached_ssh_vendor = None
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
70
        self._default_ssh_vendor = None
71
72
    def register_default_vendor(self, vendor):
73
        """Register default SSH vendor."""
74
        self._default_ssh_vendor = vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
75
76
    def register_vendor(self, name, vendor):
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
77
        """Register new SSH vendor by name."""
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
78
        self._ssh_vendors[name] = vendor
79
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
80
    def clear_cache(self):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
81
        """Clear previously cached lookup result."""
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
82
        self._cached_ssh_vendor = None
83
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
84
    def _get_vendor_by_environment(self, environment=None):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
85
        """Return the vendor or None based on BZR_SSH environment variable.
86
87
        :raises UnknownSSH: if the BZR_SSH environment variable contains
88
                            unknown vendor name
89
        """
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
90
        if environment is None:
91
            environment = os.environ
92
        if 'BZR_SSH' in environment:
93
            vendor_name = environment['BZR_SSH']
94
            try:
95
                vendor = self._ssh_vendors[vendor_name]
96
            except KeyError:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
97
                raise errors.UnknownSSH(vendor_name)
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
98
            return vendor
99
        return None
100
101
    def _get_ssh_version_string(self, args):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
102
        """Return SSH version string from the subprocess."""
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
103
        try:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
104
            p = subprocess.Popen(args,
105
                                 stdout=subprocess.PIPE,
106
                                 stderr=subprocess.PIPE,
107
                                 **os_specific_subprocess_params())
108
            stdout, stderr = p.communicate()
109
        except OSError:
110
            stdout = stderr = ''
111
        return stdout + stderr
112
2772.3.1 by Martin Pool
Fix detection of ssh implementation on Windows
113
    def _get_vendor_by_version_string(self, version, args):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
114
        """Return the vendor or None based on output from the subprocess.
115
116
        :param version: The output of 'ssh -V' like command.
2772.3.1 by Martin Pool
Fix detection of ssh implementation on Windows
117
        :param args: Command line that was run.
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
118
        """
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
119
        vendor = None
120
        if 'OpenSSH' in version:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
121
            trace.mutter('ssh implementation is OpenSSH')
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
122
            vendor = OpenSSHSubprocessVendor()
123
        elif 'SSH Secure Shell' in version:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
124
            trace.mutter('ssh implementation is SSH Corp.')
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
125
            vendor = SSHCorpSubprocessVendor()
2772.3.1 by Martin Pool
Fix detection of ssh implementation on Windows
126
        elif 'plink' in version and args[0] == 'plink':
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
127
            # Checking if "plink" was the executed argument as Windows
128
            # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
129
            # version.  See https://bugs.launchpad.net/bzr/+bug/107155
130
            trace.mutter("ssh implementation is Putty's plink.")
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
131
            vendor = PLinkSubprocessVendor()
132
        return vendor
133
134
    def _get_vendor_by_inspection(self):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
135
        """Return the vendor or None by checking for known SSH implementations."""
3220.1.2 by Dmitry Vasiliev
Re-enabled auto-detection of plink vendor and fixed tests
136
        for args in (['ssh', '-V'], ['plink', '-V']):
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
137
            version = self._get_ssh_version_string(args)
2767.3.1 by Martin Albisetti
Fixed bug #107155
138
            vendor = self._get_vendor_by_version_string(version, args)
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
139
            if vendor is not None:
140
                return vendor
141
        return None
142
143
    def get_vendor(self, environment=None):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
144
        """Find out what version of SSH is on the system.
145
146
        :raises SSHVendorNotFound: if no any SSH vendor is found
147
        :raises UnknownSSH: if the BZR_SSH environment variable contains
148
                            unknown vendor name
149
        """
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
150
        if self._cached_ssh_vendor is None:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
151
            vendor = self._get_vendor_by_environment(environment)
152
            if vendor is None:
153
                vendor = self._get_vendor_by_inspection()
154
                if vendor is None:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
155
                    trace.mutter('falling back to default implementation')
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
156
                    vendor = self._default_ssh_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
157
                    if vendor is None:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
158
                        raise errors.SSHVendorNotFound()
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
159
            self._cached_ssh_vendor = vendor
160
        return self._cached_ssh_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
161
162
_ssh_vendor_manager = SSHVendorManager()
163
_get_ssh_vendor = _ssh_vendor_manager.get_vendor
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
164
register_default_ssh_vendor = _ssh_vendor_manager.register_default_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
165
register_ssh_vendor = _ssh_vendor_manager.register_vendor
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
166
167
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
168
def _ignore_sigint():
169
    # TODO: This should possibly ignore SIGHUP as well, but bzr currently
170
    # doesn't handle it itself.
171
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
172
    import signal
173
    signal.signal(signal.SIGINT, signal.SIG_IGN)
174
175
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
176
class SocketAsChannelAdapter(object):
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
177
    """Simple wrapper for a socket that pretends to be a paramiko Channel."""
178
179
    def __init__(self, sock):
180
        self.__socket = sock
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
181
3353.1.2 by Andrew Bennetts
Add get_name to LoopbackSFTP. Makes the current tests pass with current paramiko.
182
    def get_name(self):
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
183
        return "bzr SocketAsChannelAdapter"
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
184
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
185
    def send(self, data):
186
        return self.__socket.send(data)
187
188
    def recv(self, n):
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
189
        try:
190
            return self.__socket.recv(n)
191
        except socket.error, e:
192
            if e.args[0] in (errno.EPIPE, errno.ECONNRESET, errno.ECONNABORTED,
193
                             errno.EBADF):
194
                # Connection has closed.  Paramiko expects an empty string in
195
                # this case, not an exception.
196
                return ''
197
            raise
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
198
199
    def recv_ready(self):
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
200
        # TODO: jam 20051215 this function is necessary to support the
201
        # pipelined() function. In reality, it probably should use
202
        # poll() or select() to actually return if there is data
203
        # available, otherwise we probably don't get any benefit
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
204
        return True
205
206
    def close(self):
207
        self.__socket.close()
208
209
210
class SSHVendor(object):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
211
    """Abstract base class for SSH vendor implementations."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
212
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
213
    def connect_sftp(self, username, password, host, port):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
214
        """Make an SSH connection, and return an SFTPClient.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
215
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
216
        :param username: an ascii string
217
        :param password: an ascii string
218
        :param host: a host name as an ascii string
219
        :param port: a port number
220
        :type port: int
221
222
        :raises: ConnectionError if it cannot connect.
223
224
        :rtype: paramiko.sftp_client.SFTPClient
225
        """
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
226
        raise NotImplementedError(self.connect_sftp)
227
228
    def connect_ssh(self, username, password, host, port, command):
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
229
        """Make an SSH connection.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
230
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
231
        :returns: something with a `close` method, and a `get_filelike_channels`
232
            method that returns a pair of (read, write) filelike objects.
1951.1.12 by Andrew Bennetts
Cosmetic tweaks.
233
        """
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
234
        raise NotImplementedError(self.connect_ssh)
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
235
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
236
    def _raise_connection_error(self, host, port=None, orig_error=None,
2052.4.4 by John Arbash Meinel
Create a SocketConnectionError to make creating nice errors easier
237
                                msg='Unable to connect to SSH host'):
238
        """Raise a SocketConnectionError with properly formatted host.
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
239
240
        This just unifies all the locations that try to raise ConnectionError,
241
        so that they format things properly.
242
        """
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
243
        raise errors.SocketConnectionError(host=host, port=port, msg=msg,
244
                                           orig_error=orig_error)
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
245
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
246
247
class LoopbackVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
248
    """SSH "vendor" that connects over a plain TCP socket, not SSH."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
249
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
250
    def connect_sftp(self, username, password, host, port):
251
        sock = socket.socket()
252
        try:
253
            sock.connect((host, port))
254
        except socket.error, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
255
            self._raise_connection_error(host, port=port, orig_error=e)
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
256
        return SFTPClient(SocketAsChannelAdapter(sock))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
257
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
258
register_ssh_vendor('loopback', LoopbackVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
259
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
260
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
261
class _ParamikoSSHConnection(object):
262
    def __init__(self, channel):
263
        self.channel = channel
264
265
    def get_filelike_channels(self):
266
        return self.channel.makefile('rb'), self.channel.makefile('wb')
267
268
    def close(self):
269
        return self.channel.close()
270
271
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
272
class ParamikoVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
273
    """Vendor that uses paramiko."""
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
274
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
275
    def _connect(self, username, password, host, port):
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
276
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
277
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
278
        load_host_keys()
279
280
        try:
281
            t = paramiko.Transport((host, port or 22))
282
            t.set_log_channel('bzr.paramiko')
283
            t.start_client()
284
        except (paramiko.SSHException, socket.error), e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
285
            self._raise_connection_error(host, port=port, orig_error=e)
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
286
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
287
        server_key = t.get_remote_server_key()
288
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
289
        keytype = server_key.get_name()
1711.9.10 by John Arbash Meinel
Update transport/ssh.py to remove has_key usage
290
        if host in SYSTEM_HOSTKEYS and keytype in SYSTEM_HOSTKEYS[host]:
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
291
            our_server_key = SYSTEM_HOSTKEYS[host][keytype]
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
292
            our_server_key_hex = paramiko.util.hexify(
293
                our_server_key.get_fingerprint())
1711.9.10 by John Arbash Meinel
Update transport/ssh.py to remove has_key usage
294
        elif host in BZR_HOSTKEYS and keytype in BZR_HOSTKEYS[host]:
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
295
            our_server_key = BZR_HOSTKEYS[host][keytype]
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
296
            our_server_key_hex = paramiko.util.hexify(
297
                our_server_key.get_fingerprint())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
298
        else:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
299
            trace.warning('Adding %s host key for %s: %s'
300
                          % (keytype, host, server_key_hex))
2127.3.1 by Alexander Belchenko
Use BZR_HOSTKEYS.add instead of deprecated dict-like paramiko interface
301
            add = getattr(BZR_HOSTKEYS, 'add', None)
302
            if add is not None: # paramiko >= 1.X.X
303
                BZR_HOSTKEYS.add(host, keytype, server_key)
304
            else:
1551.9.2 by Aaron Bentley
Bugfix for paramiko connections
305
                BZR_HOSTKEYS.setdefault(host, {})[keytype] = server_key
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
306
            our_server_key = server_key
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
307
            our_server_key_hex = paramiko.util.hexify(
308
                our_server_key.get_fingerprint())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
309
            save_host_keys()
310
        if server_key != our_server_key:
311
            filename1 = os.path.expanduser('~/.ssh/known_hosts')
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
312
            filename2 = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
313
            raise errors.TransportError(
314
                'Host keys for %s do not match!  %s != %s' %
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
315
                (host, our_server_key_hex, server_key_hex),
316
                ['Try editing %s or %s' % (filename1, filename2)])
317
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
318
        _paramiko_auth(username, password, host, port, t)
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
319
        return t
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
320
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
321
    def connect_sftp(self, username, password, host, port):
322
        t = self._connect(username, password, host, port)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
323
        try:
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
324
            return t.open_sftp_client()
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
325
        except paramiko.SSHException, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
326
            self._raise_connection_error(host, port=port, orig_error=e,
2052.4.4 by John Arbash Meinel
Create a SocketConnectionError to make creating nice errors easier
327
                                         msg='Unable to start sftp client')
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
328
329
    def connect_ssh(self, username, password, host, port, command):
330
        t = self._connect(username, password, host, port)
331
        try:
332
            channel = t.open_session()
333
            cmdline = ' '.join(command)
334
            channel.exec_command(cmdline)
335
            return _ParamikoSSHConnection(channel)
336
        except paramiko.SSHException, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
337
            self._raise_connection_error(host, port=port, orig_error=e,
2052.4.4 by John Arbash Meinel
Create a SocketConnectionError to make creating nice errors easier
338
                                         msg='Unable to invoke remote bzr')
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
339
2104.5.1 by John Arbash Meinel
Remove the strict dependency on paramiko for ssh access
340
if paramiko is not None:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
341
    vendor = ParamikoVendor()
342
    register_ssh_vendor('paramiko', vendor)
343
    register_ssh_vendor('none', vendor)
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
344
    register_default_ssh_vendor(vendor)
3066.2.1 by John Arbash Meinel
We don't require paramiko for bzr+ssh.
345
    _sftp_connection_errors = (EOFError, paramiko.SSHException)
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
346
    del vendor
3066.2.1 by John Arbash Meinel
We don't require paramiko for bzr+ssh.
347
else:
348
    _sftp_connection_errors = (EOFError,)
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
349
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
350
351
class SubprocessVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
352
    """Abstract base class for vendors that use pipes to a subprocess."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
353
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
354
    def _connect(self, argv):
355
        proc = subprocess.Popen(argv,
356
                                stdin=subprocess.PIPE,
357
                                stdout=subprocess.PIPE,
358
                                **os_specific_subprocess_params())
359
        return SSHSubprocess(proc)
360
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
361
    def connect_sftp(self, username, password, host, port):
362
        try:
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
363
            argv = self._get_vendor_specific_argv(username, host, port,
364
                                                  subsystem='sftp')
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
365
            sock = self._connect(argv)
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
366
            return SFTPClient(SocketAsChannelAdapter(sock))
3066.2.1 by John Arbash Meinel
We don't require paramiko for bzr+ssh.
367
        except _sftp_connection_errors, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
368
            self._raise_connection_error(host, port=port, orig_error=e)
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
369
        except (OSError, IOError), e:
370
            # If the machine is fast enough, ssh can actually exit
371
            # before we try and send it the sftp request, which
372
            # raises a Broken Pipe
373
            if e.errno not in (errno.EPIPE,):
374
                raise
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
375
            self._raise_connection_error(host, port=port, orig_error=e)
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
376
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
377
    def connect_ssh(self, username, password, host, port, command):
378
        try:
379
            argv = self._get_vendor_specific_argv(username, host, port,
380
                                                  command=command)
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
381
            return self._connect(argv)
382
        except (EOFError), e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
383
            self._raise_connection_error(host, port=port, orig_error=e)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
384
        except (OSError, IOError), e:
385
            # If the machine is fast enough, ssh can actually exit
386
            # before we try and send it the sftp request, which
387
            # raises a Broken Pipe
388
            if e.errno not in (errno.EPIPE,):
389
                raise
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
390
            self._raise_connection_error(host, port=port, orig_error=e)
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
391
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
392
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
393
                                  command=None):
394
        """Returns the argument list to run the subprocess with.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
395
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
396
        Exactly one of 'subsystem' and 'command' must be specified.
397
        """
398
        raise NotImplementedError(self._get_vendor_specific_argv)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
399
400
401
class OpenSSHSubprocessVendor(SubprocessVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
402
    """SSH vendor that uses the 'ssh' executable from OpenSSH."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
403
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
404
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
405
                                  command=None):
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
406
        args = ['ssh',
407
                '-oForwardX11=no', '-oForwardAgent=no',
408
                '-oClearAllForwardings=yes', '-oProtocol=2',
409
                '-oNoHostAuthenticationForLocalhost=yes']
410
        if port is not None:
411
            args.extend(['-p', str(port)])
412
        if username is not None:
413
            args.extend(['-l', username])
414
        if subsystem is not None:
415
            args.extend(['-s', host, subsystem])
416
        else:
417
            args.extend([host] + command)
418
        return args
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
419
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
420
register_ssh_vendor('openssh', OpenSSHSubprocessVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
421
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
422
423
class SSHCorpSubprocessVendor(SubprocessVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
424
    """SSH vendor that uses the 'ssh' executable from SSH Corporation."""
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
425
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
426
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
427
                                  command=None):
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
428
        args = ['ssh', '-x']
429
        if port is not None:
430
            args.extend(['-p', str(port)])
431
        if username is not None:
432
            args.extend(['-l', username])
433
        if subsystem is not None:
434
            args.extend(['-s', subsystem, host])
435
        else:
436
            args.extend([host] + command)
437
        return args
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
438
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
439
register_ssh_vendor('ssh', SSHCorpSubprocessVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
440
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
441
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
442
class PLinkSubprocessVendor(SubprocessVendor):
443
    """SSH vendor that uses the 'plink' executable from Putty."""
444
445
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
446
                                  command=None):
3220.1.1 by Dmitry Vasiliev
Now plink closes the pipe if unable to open ssh connection and connection error can be reported
447
        args = ['plink', '-x', '-a', '-ssh', '-2', '-batch']
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
448
        if port is not None:
449
            args.extend(['-P', str(port)])
450
        if username is not None:
451
            args.extend(['-l', username])
452
        if subsystem is not None:
2221.5.3 by Dmitry Vasiliev
Fixed plink's arguments order. Added tests for such a case.
453
            args.extend(['-s', host, subsystem])
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
454
        else:
455
            args.extend([host] + command)
456
        return args
457
458
register_ssh_vendor('plink', PLinkSubprocessVendor())
459
460
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
461
def _paramiko_auth(username, password, host, port, paramiko_transport):
4222.3.4 by Jelmer Vernooij
Default to getpass.getuser() in AuthenticationConfig.get_user(), but allow
462
    auth = config.AuthenticationConfig()
3777.1.5 by Aaron Bentley
Remove AuthenticationConfig handling from Paramiko SSHVendor
463
    # paramiko requires a username, but it might be none if nothing was
464
    # supplied.  If so, use the local username.
2900.2.15 by Vincent Ladeuil
AuthenticationConfig can be queried for logins too (first step).
465
    if username is None:
4304.2.1 by Vincent Ladeuil
Fix bug #367726 by reverting some default user handling introduced
466
        username = auth.get_user('ssh', host, port=port,
467
                                 default=getpass.getuser())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
468
    if _use_ssh_agent:
469
        agent = paramiko.Agent()
470
        for key in agent.get_keys():
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
471
            trace.mutter('Trying SSH agent key %s'
472
                         % paramiko.util.hexify(key.get_fingerprint()))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
473
            try:
474
                paramiko_transport.auth_publickey(username, key)
475
                return
476
            except paramiko.SSHException, e:
477
                pass
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
478
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
479
    # okay, try finding id_rsa or id_dss?  (posix only)
480
    if _try_pkey_auth(paramiko_transport, paramiko.RSAKey, username, 'id_rsa'):
481
        return
482
    if _try_pkey_auth(paramiko_transport, paramiko.DSSKey, username, 'id_dsa'):
483
        return
484
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
485
    # If we have gotten this far, we are about to try for passwords, do an
486
    # auth_none check to see if it is even supported.
487
    supported_auth_types = []
488
    try:
489
        # Note that with paramiko <1.7.5 this logs an INFO message:
490
        #    Authentication type (none) not permitted.
491
        # So we explicitly disable the logging level for this action
492
        old_level = paramiko_transport.logger.level
493
        paramiko_transport.logger.setLevel(logging.WARNING)
494
        try:
495
            paramiko_transport.auth_none(username)
496
        finally:
497
            paramiko_transport.logger.setLevel(old_level)
498
    except paramiko.BadAuthenticationType, e:
499
        # Supported methods are in the exception
500
        supported_auth_types = e.allowed_types
501
    except paramiko.SSHException, e:
502
        # Don't know what happened, but just ignore it
503
        pass
504
    if 'password' not in supported_auth_types:
505
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
4555.1.3 by John Arbash Meinel
Reformat the errors so they aren't so long.
506
            '\n  %s@%s\nsupported auth types: %s'
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
507
            % (username, host, supported_auth_types))
508
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
509
    if password:
510
        try:
511
            paramiko_transport.auth_password(username, password)
512
            return
513
        except paramiko.SSHException, e:
514
            pass
515
516
    # give up and ask for a password
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
517
    password = auth.get_password('ssh', host, username, port=port)
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
518
    # get_password can still return None, which means we should not prompt
519
    if password is not None:
520
        try:
521
            paramiko_transport.auth_password(username, password)
522
        except paramiko.SSHException, e:
4555.1.3 by John Arbash Meinel
Reformat the errors so they aren't so long.
523
            raise errors.ConnectionError(
524
                'Unable to authenticate to SSH host as'
525
                '\n  %s@%s\n' % (username, host), e)
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
526
    else:
4555.1.3 by John Arbash Meinel
Reformat the errors so they aren't so long.
527
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
528
                                     '  %s@%s' % (username, host))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
529
530
531
def _try_pkey_auth(paramiko_transport, pkey_class, username, filename):
532
    filename = os.path.expanduser('~/.ssh/' + filename)
533
    try:
534
        key = pkey_class.from_private_key_file(filename)
535
        paramiko_transport.auth_publickey(username, key)
536
        return True
537
    except paramiko.PasswordRequiredException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
538
        password = ui.ui_factory.get_password(
539
            prompt='SSH %(filename)s password', filename=filename)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
540
        try:
541
            key = pkey_class.from_private_key_file(filename, password)
542
            paramiko_transport.auth_publickey(username, key)
543
            return True
544
        except paramiko.SSHException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
545
            trace.mutter('SSH authentication via %s key failed.'
546
                         % (os.path.basename(filename),))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
547
    except paramiko.SSHException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
548
        trace.mutter('SSH authentication via %s key failed.'
549
                     % (os.path.basename(filename),))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
550
    except IOError:
551
        pass
552
    return False
553
554
555
def load_host_keys():
556
    """
557
    Load system host keys (probably doesn't work on windows) and any
558
    "discovered" keys from previous sessions.
559
    """
560
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
561
    try:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
562
        SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(
563
            os.path.expanduser('~/.ssh/known_hosts'))
2358.3.1 by Martin Pool
Update some too-general exception blocks
564
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
565
        trace.mutter('failed to load system host keys: ' + str(e))
566
    bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
567
    try:
568
        BZR_HOSTKEYS = paramiko.util.load_host_keys(bzr_hostkey_path)
2358.3.1 by Martin Pool
Update some too-general exception blocks
569
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
570
        trace.mutter('failed to load bzr host keys: ' + str(e))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
571
        save_host_keys()
572
573
574
def save_host_keys():
575
    """
576
    Save "discovered" host keys in $(config)/ssh_host_keys/.
577
    """
578
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
579
    bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
580
    config.ensure_config_dir_exists()
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
581
582
    try:
583
        f = open(bzr_hostkey_path, 'w')
584
        f.write('# SSH host keys collected by bzr\n')
585
        for hostname, keys in BZR_HOSTKEYS.iteritems():
586
            for keytype, key in keys.iteritems():
587
                f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
588
        f.close()
589
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
590
        trace.mutter('failed to save bzr host keys: ' + str(e))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
591
592
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
593
def os_specific_subprocess_params():
594
    """Get O/S specific subprocess parameters."""
595
    if sys.platform == 'win32':
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
596
        # setting the process group and closing fds is not supported on
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
597
        # win32
598
        return {}
599
    else:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
600
        # We close fds other than the pipes as the child process does not need
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
601
        # them to be open.
602
        #
603
        # We also set the child process to ignore SIGINT.  Normally the signal
604
        # would be sent to every process in the foreground process group, but
605
        # this causes it to be seen only by bzr and not by ssh.  Python will
606
        # generate a KeyboardInterrupt in bzr, and we will then have a chance
607
        # to release locks or do other cleanup over ssh before the connection
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
608
        # goes away.
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
609
        # <https://launchpad.net/products/bzr/+bug/5987>
610
        #
611
        # Running it in a separate process group is not good because then it
612
        # can't get non-echoed input of a password or passphrase.
613
        # <https://launchpad.net/products/bzr/+bug/40508>
614
        return {'preexec_fn': _ignore_sigint,
615
                'close_fds': True,
616
                }
617
1951.1.12 by Andrew Bennetts
Cosmetic tweaks.
618
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
619
class SSHSubprocess(object):
620
    """A socket-like object that talks to an ssh subprocess via pipes."""
621
622
    def __init__(self, proc):
623
        self.proc = proc
624
625
    def send(self, data):
626
        return os.write(self.proc.stdin.fileno(), data)
627
628
    def recv(self, count):
629
        return os.read(self.proc.stdout.fileno(), count)
630
631
    def close(self):
632
        self.proc.stdin.close()
633
        self.proc.stdout.close()
634
        self.proc.wait()
635
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
636
    def get_filelike_channels(self):
637
        return (self.proc.stdout, self.proc.stdin)
2221.5.21 by Dmitry Vasiliev
Reverted trailing whitespace removal
638