~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()
4636.3.1 by Alexander Belchenko
Disabled auto-detection of plink SSH client to avoid many problems. On Windows paramiko wil be used as default. Nevertheless user can force usage of plink via env variable BZR_SSH=plink
126
        # Auto-detect of plink vendor disabled, on Windows recommended
127
        # default ssh-client is paramiko
128
        # see https://bugs.launchpad.net/bugs/414743
129
        #~elif 'plink' in version and args[0] == 'plink':
130
        #~    # Checking if "plink" was the executed argument as Windows
131
        #~    # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
132
        #~    # version.  See https://bugs.launchpad.net/bzr/+bug/107155
133
        #~    trace.mutter("ssh implementation is Putty's plink.")
134
        #~    vendor = PLinkSubprocessVendor()
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
135
        return vendor
136
137
    def _get_vendor_by_inspection(self):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
138
        """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
139
        for args in (['ssh', '-V'], ['plink', '-V']):
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
140
            version = self._get_ssh_version_string(args)
2767.3.1 by Martin Albisetti
Fixed bug #107155
141
            vendor = self._get_vendor_by_version_string(version, args)
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
142
            if vendor is not None:
143
                return vendor
144
        return None
145
146
    def get_vendor(self, environment=None):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
147
        """Find out what version of SSH is on the system.
148
149
        :raises SSHVendorNotFound: if no any SSH vendor is found
150
        :raises UnknownSSH: if the BZR_SSH environment variable contains
151
                            unknown vendor name
152
        """
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
153
        if self._cached_ssh_vendor is None:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
154
            vendor = self._get_vendor_by_environment(environment)
155
            if vendor is None:
156
                vendor = self._get_vendor_by_inspection()
157
                if vendor is None:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
158
                    trace.mutter('falling back to default implementation')
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
159
                    vendor = self._default_ssh_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
160
                    if vendor is None:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
161
                        raise errors.SSHVendorNotFound()
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
162
            self._cached_ssh_vendor = vendor
163
        return self._cached_ssh_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
164
165
_ssh_vendor_manager = SSHVendorManager()
166
_get_ssh_vendor = _ssh_vendor_manager.get_vendor
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
167
register_default_ssh_vendor = _ssh_vendor_manager.register_default_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
168
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
169
170
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
171
def _ignore_sigint():
172
    # TODO: This should possibly ignore SIGHUP as well, but bzr currently
173
    # doesn't handle it itself.
174
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
175
    import signal
176
    signal.signal(signal.SIGINT, signal.SIG_IGN)
177
178
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
179
class SocketAsChannelAdapter(object):
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
180
    """Simple wrapper for a socket that pretends to be a paramiko Channel."""
181
182
    def __init__(self, sock):
183
        self.__socket = sock
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
184
3353.1.2 by Andrew Bennetts
Add get_name to LoopbackSFTP. Makes the current tests pass with current paramiko.
185
    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.
186
        return "bzr SocketAsChannelAdapter"
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
187
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
188
    def send(self, data):
189
        return self.__socket.send(data)
190
191
    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.
192
        try:
193
            return self.__socket.recv(n)
194
        except socket.error, e:
195
            if e.args[0] in (errno.EPIPE, errno.ECONNRESET, errno.ECONNABORTED,
196
                             errno.EBADF):
197
                # Connection has closed.  Paramiko expects an empty string in
198
                # this case, not an exception.
199
                return ''
200
            raise
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
201
202
    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.
203
        # TODO: jam 20051215 this function is necessary to support the
204
        # pipelined() function. In reality, it probably should use
205
        # poll() or select() to actually return if there is data
206
        # 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
207
        return True
208
209
    def close(self):
210
        self.__socket.close()
211
212
213
class SSHVendor(object):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
214
    """Abstract base class for SSH vendor implementations."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
215
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
216
    def connect_sftp(self, username, password, host, port):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
217
        """Make an SSH connection, and return an SFTPClient.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
218
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
219
        :param username: an ascii string
220
        :param password: an ascii string
221
        :param host: a host name as an ascii string
222
        :param port: a port number
223
        :type port: int
224
225
        :raises: ConnectionError if it cannot connect.
226
227
        :rtype: paramiko.sftp_client.SFTPClient
228
        """
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
229
        raise NotImplementedError(self.connect_sftp)
230
231
    def connect_ssh(self, username, password, host, port, command):
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
232
        """Make an SSH connection.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
233
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
234
        :returns: something with a `close` method, and a `get_filelike_channels`
235
            method that returns a pair of (read, write) filelike objects.
1951.1.12 by Andrew Bennetts
Cosmetic tweaks.
236
        """
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
237
        raise NotImplementedError(self.connect_ssh)
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
238
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
239
    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
240
                                msg='Unable to connect to SSH host'):
241
        """Raise a SocketConnectionError with properly formatted host.
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
242
243
        This just unifies all the locations that try to raise ConnectionError,
244
        so that they format things properly.
245
        """
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
246
        raise errors.SocketConnectionError(host=host, port=port, msg=msg,
247
                                           orig_error=orig_error)
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
248
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
249
250
class LoopbackVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
251
    """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.
252
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
253
    def connect_sftp(self, username, password, host, port):
254
        sock = socket.socket()
255
        try:
256
            sock.connect((host, port))
257
        except socket.error, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
258
            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.
259
        return SFTPClient(SocketAsChannelAdapter(sock))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
260
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
261
register_ssh_vendor('loopback', LoopbackVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
262
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
263
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
264
class _ParamikoSSHConnection(object):
265
    def __init__(self, channel):
266
        self.channel = channel
267
268
    def get_filelike_channels(self):
269
        return self.channel.makefile('rb'), self.channel.makefile('wb')
270
271
    def close(self):
272
        return self.channel.close()
273
274
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
275
class ParamikoVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
276
    """Vendor that uses paramiko."""
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
277
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
278
    def _connect(self, username, password, host, port):
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
279
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
280
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
281
        load_host_keys()
282
283
        try:
284
            t = paramiko.Transport((host, port or 22))
285
            t.set_log_channel('bzr.paramiko')
286
            t.start_client()
287
        except (paramiko.SSHException, socket.error), e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
288
            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.
289
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
290
        server_key = t.get_remote_server_key()
291
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
292
        keytype = server_key.get_name()
1711.9.10 by John Arbash Meinel
Update transport/ssh.py to remove has_key usage
293
        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
294
            our_server_key = SYSTEM_HOSTKEYS[host][keytype]
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
295
            our_server_key_hex = paramiko.util.hexify(
296
                our_server_key.get_fingerprint())
1711.9.10 by John Arbash Meinel
Update transport/ssh.py to remove has_key usage
297
        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
298
            our_server_key = BZR_HOSTKEYS[host][keytype]
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
299
            our_server_key_hex = paramiko.util.hexify(
300
                our_server_key.get_fingerprint())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
301
        else:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
302
            trace.warning('Adding %s host key for %s: %s'
303
                          % (keytype, host, server_key_hex))
2127.3.1 by Alexander Belchenko
Use BZR_HOSTKEYS.add instead of deprecated dict-like paramiko interface
304
            add = getattr(BZR_HOSTKEYS, 'add', None)
305
            if add is not None: # paramiko >= 1.X.X
306
                BZR_HOSTKEYS.add(host, keytype, server_key)
307
            else:
1551.9.2 by Aaron Bentley
Bugfix for paramiko connections
308
                BZR_HOSTKEYS.setdefault(host, {})[keytype] = server_key
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
309
            our_server_key = server_key
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
310
            our_server_key_hex = paramiko.util.hexify(
311
                our_server_key.get_fingerprint())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
312
            save_host_keys()
313
        if server_key != our_server_key:
314
            filename1 = os.path.expanduser('~/.ssh/known_hosts')
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
315
            filename2 = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
316
            raise errors.TransportError(
317
                '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
318
                (host, our_server_key_hex, server_key_hex),
319
                ['Try editing %s or %s' % (filename1, filename2)])
320
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
321
        _paramiko_auth(username, password, host, port, t)
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
322
        return t
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
323
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
324
    def connect_sftp(self, username, password, host, port):
325
        t = self._connect(username, password, host, port)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
326
        try:
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
327
            return t.open_sftp_client()
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
328
        except paramiko.SSHException, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
329
            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
330
                                         msg='Unable to start sftp client')
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
331
332
    def connect_ssh(self, username, password, host, port, command):
333
        t = self._connect(username, password, host, port)
334
        try:
335
            channel = t.open_session()
336
            cmdline = ' '.join(command)
337
            channel.exec_command(cmdline)
338
            return _ParamikoSSHConnection(channel)
339
        except paramiko.SSHException, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
340
            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
341
                                         msg='Unable to invoke remote bzr')
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
342
2104.5.1 by John Arbash Meinel
Remove the strict dependency on paramiko for ssh access
343
if paramiko is not None:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
344
    vendor = ParamikoVendor()
345
    register_ssh_vendor('paramiko', vendor)
346
    register_ssh_vendor('none', vendor)
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
347
    register_default_ssh_vendor(vendor)
3066.2.1 by John Arbash Meinel
We don't require paramiko for bzr+ssh.
348
    _sftp_connection_errors = (EOFError, paramiko.SSHException)
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
349
    del vendor
3066.2.1 by John Arbash Meinel
We don't require paramiko for bzr+ssh.
350
else:
351
    _sftp_connection_errors = (EOFError,)
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
352
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
353
354
class SubprocessVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
355
    """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.
356
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
357
    def _connect(self, argv):
358
        proc = subprocess.Popen(argv,
359
                                stdin=subprocess.PIPE,
360
                                stdout=subprocess.PIPE,
361
                                **os_specific_subprocess_params())
362
        return SSHSubprocess(proc)
363
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
364
    def connect_sftp(self, username, password, host, port):
365
        try:
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
366
            argv = self._get_vendor_specific_argv(username, host, port,
367
                                                  subsystem='sftp')
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
368
            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.
369
            return SFTPClient(SocketAsChannelAdapter(sock))
3066.2.1 by John Arbash Meinel
We don't require paramiko for bzr+ssh.
370
        except _sftp_connection_errors, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
371
            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.
372
        except (OSError, IOError), e:
373
            # If the machine is fast enough, ssh can actually exit
374
            # before we try and send it the sftp request, which
375
            # raises a Broken Pipe
376
            if e.errno not in (errno.EPIPE,):
377
                raise
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
378
            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.
379
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
380
    def connect_ssh(self, username, password, host, port, command):
381
        try:
382
            argv = self._get_vendor_specific_argv(username, host, port,
383
                                                  command=command)
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
384
            return self._connect(argv)
385
        except (EOFError), e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
386
            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).
387
        except (OSError, IOError), e:
388
            # If the machine is fast enough, ssh can actually exit
389
            # before we try and send it the sftp request, which
390
            # raises a Broken Pipe
391
            if e.errno not in (errno.EPIPE,):
392
                raise
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
393
            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).
394
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
395
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
396
                                  command=None):
397
        """Returns the argument list to run the subprocess with.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
398
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
399
        Exactly one of 'subsystem' and 'command' must be specified.
400
        """
401
        raise NotImplementedError(self._get_vendor_specific_argv)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
402
403
404
class OpenSSHSubprocessVendor(SubprocessVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
405
    """SSH vendor that uses the 'ssh' executable from OpenSSH."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
406
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
407
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
408
                                  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.
409
        args = ['ssh',
410
                '-oForwardX11=no', '-oForwardAgent=no',
411
                '-oClearAllForwardings=yes', '-oProtocol=2',
412
                '-oNoHostAuthenticationForLocalhost=yes']
413
        if port is not None:
414
            args.extend(['-p', str(port)])
415
        if username is not None:
416
            args.extend(['-l', username])
417
        if subsystem is not None:
418
            args.extend(['-s', host, subsystem])
419
        else:
420
            args.extend([host] + command)
421
        return args
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
422
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
423
register_ssh_vendor('openssh', OpenSSHSubprocessVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
424
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
425
426
class SSHCorpSubprocessVendor(SubprocessVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
427
    """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.
428
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
429
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
430
                                  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.
431
        args = ['ssh', '-x']
432
        if port is not None:
433
            args.extend(['-p', str(port)])
434
        if username is not None:
435
            args.extend(['-l', username])
436
        if subsystem is not None:
437
            args.extend(['-s', subsystem, host])
438
        else:
439
            args.extend([host] + command)
440
        return args
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
441
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
442
register_ssh_vendor('ssh', SSHCorpSubprocessVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
443
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
444
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
445
class PLinkSubprocessVendor(SubprocessVendor):
446
    """SSH vendor that uses the 'plink' executable from Putty."""
447
448
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
449
                                  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
450
        args = ['plink', '-x', '-a', '-ssh', '-2', '-batch']
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
451
        if port is not None:
452
            args.extend(['-P', str(port)])
453
        if username is not None:
454
            args.extend(['-l', username])
455
        if subsystem is not None:
2221.5.3 by Dmitry Vasiliev
Fixed plink's arguments order. Added tests for such a case.
456
            args.extend(['-s', host, subsystem])
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
457
        else:
458
            args.extend([host] + command)
459
        return args
460
461
register_ssh_vendor('plink', PLinkSubprocessVendor())
462
463
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
464
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
465
    auth = config.AuthenticationConfig()
3777.1.5 by Aaron Bentley
Remove AuthenticationConfig handling from Paramiko SSHVendor
466
    # paramiko requires a username, but it might be none if nothing was
467
    # supplied.  If so, use the local username.
2900.2.15 by Vincent Ladeuil
AuthenticationConfig can be queried for logins too (first step).
468
    if username is None:
4304.2.1 by Vincent Ladeuil
Fix bug #367726 by reverting some default user handling introduced
469
        username = auth.get_user('ssh', host, port=port,
470
                                 default=getpass.getuser())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
471
    if _use_ssh_agent:
472
        agent = paramiko.Agent()
473
        for key in agent.get_keys():
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
474
            trace.mutter('Trying SSH agent key %s'
475
                         % paramiko.util.hexify(key.get_fingerprint()))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
476
            try:
477
                paramiko_transport.auth_publickey(username, key)
478
                return
479
            except paramiko.SSHException, e:
480
                pass
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
481
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
482
    # okay, try finding id_rsa or id_dss?  (posix only)
483
    if _try_pkey_auth(paramiko_transport, paramiko.RSAKey, username, 'id_rsa'):
484
        return
485
    if _try_pkey_auth(paramiko_transport, paramiko.DSSKey, username, 'id_dsa'):
486
        return
487
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
488
    # If we have gotten this far, we are about to try for passwords, do an
489
    # auth_none check to see if it is even supported.
490
    supported_auth_types = []
491
    try:
492
        # Note that with paramiko <1.7.5 this logs an INFO message:
493
        #    Authentication type (none) not permitted.
494
        # So we explicitly disable the logging level for this action
495
        old_level = paramiko_transport.logger.level
496
        paramiko_transport.logger.setLevel(logging.WARNING)
497
        try:
498
            paramiko_transport.auth_none(username)
499
        finally:
500
            paramiko_transport.logger.setLevel(old_level)
501
    except paramiko.BadAuthenticationType, e:
502
        # Supported methods are in the exception
503
        supported_auth_types = e.allowed_types
504
    except paramiko.SSHException, e:
505
        # Don't know what happened, but just ignore it
506
        pass
4634.56.1 by Andrew Bennetts
Try paramiko's auth_password if the server supports 'keyboard-interactive' auth, even if it doesn't support 'password'.
507
    # We treat 'keyboard-interactive' and 'password' auth methods identically,
508
    # because Paramiko's auth_password method will automatically try
509
    # 'keyboard-interactive' auth (using the password as the response) if
510
    # 'password' auth is not available.  Apparently some Debian and Gentoo
511
    # OpenSSH servers require this.
512
    # XXX: It's possible for a server to require keyboard-interactive auth that
513
    # requires something other than a single password, but we currently don't
514
    # support that.
515
    if ('password' not in supported_auth_types and
516
        'keyboard-interactive' not in supported_auth_types):
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
517
        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.
518
            '\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
519
            % (username, host, supported_auth_types))
520
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
521
    if password:
522
        try:
523
            paramiko_transport.auth_password(username, password)
524
            return
525
        except paramiko.SSHException, e:
526
            pass
527
528
    # give up and ask for a password
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
529
    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
530
    # get_password can still return None, which means we should not prompt
531
    if password is not None:
532
        try:
533
            paramiko_transport.auth_password(username, password)
534
        except paramiko.SSHException, e:
4555.1.3 by John Arbash Meinel
Reformat the errors so they aren't so long.
535
            raise errors.ConnectionError(
536
                'Unable to authenticate to SSH host as'
537
                '\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
538
    else:
4555.1.3 by John Arbash Meinel
Reformat the errors so they aren't so long.
539
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
540
                                     '  %s@%s' % (username, host))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
541
542
543
def _try_pkey_auth(paramiko_transport, pkey_class, username, filename):
544
    filename = os.path.expanduser('~/.ssh/' + filename)
545
    try:
546
        key = pkey_class.from_private_key_file(filename)
547
        paramiko_transport.auth_publickey(username, key)
548
        return True
549
    except paramiko.PasswordRequiredException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
550
        password = ui.ui_factory.get_password(
551
            prompt='SSH %(filename)s password', filename=filename)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
552
        try:
553
            key = pkey_class.from_private_key_file(filename, password)
554
            paramiko_transport.auth_publickey(username, key)
555
            return True
556
        except paramiko.SSHException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
557
            trace.mutter('SSH authentication via %s key failed.'
558
                         % (os.path.basename(filename),))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
559
    except paramiko.SSHException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
560
        trace.mutter('SSH authentication via %s key failed.'
561
                     % (os.path.basename(filename),))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
562
    except IOError:
563
        pass
564
    return False
565
566
567
def load_host_keys():
568
    """
569
    Load system host keys (probably doesn't work on windows) and any
570
    "discovered" keys from previous sessions.
571
    """
572
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
573
    try:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
574
        SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(
575
            os.path.expanduser('~/.ssh/known_hosts'))
2358.3.1 by Martin Pool
Update some too-general exception blocks
576
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
577
        trace.mutter('failed to load system host keys: ' + str(e))
578
    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
579
    try:
580
        BZR_HOSTKEYS = paramiko.util.load_host_keys(bzr_hostkey_path)
2358.3.1 by Martin Pool
Update some too-general exception blocks
581
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
582
        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
583
        save_host_keys()
584
585
586
def save_host_keys():
587
    """
588
    Save "discovered" host keys in $(config)/ssh_host_keys/.
589
    """
590
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
591
    bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
592
    config.ensure_config_dir_exists()
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
593
594
    try:
595
        f = open(bzr_hostkey_path, 'w')
596
        f.write('# SSH host keys collected by bzr\n')
597
        for hostname, keys in BZR_HOSTKEYS.iteritems():
598
            for keytype, key in keys.iteritems():
599
                f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
600
        f.close()
601
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
602
        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
603
604
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
605
def os_specific_subprocess_params():
606
    """Get O/S specific subprocess parameters."""
607
    if sys.platform == 'win32':
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
608
        # 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.
609
        # win32
610
        return {}
611
    else:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
612
        # 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.
613
        # them to be open.
614
        #
615
        # We also set the child process to ignore SIGINT.  Normally the signal
616
        # would be sent to every process in the foreground process group, but
617
        # this causes it to be seen only by bzr and not by ssh.  Python will
618
        # generate a KeyboardInterrupt in bzr, and we will then have a chance
619
        # 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
620
        # 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.
621
        # <https://launchpad.net/products/bzr/+bug/5987>
622
        #
623
        # Running it in a separate process group is not good because then it
624
        # can't get non-echoed input of a password or passphrase.
625
        # <https://launchpad.net/products/bzr/+bug/40508>
626
        return {'preexec_fn': _ignore_sigint,
627
                'close_fds': True,
628
                }
629
1951.1.12 by Andrew Bennetts
Cosmetic tweaks.
630
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
631
class SSHSubprocess(object):
632
    """A socket-like object that talks to an ssh subprocess via pipes."""
633
634
    def __init__(self, proc):
635
        self.proc = proc
636
637
    def send(self, data):
638
        return os.write(self.proc.stdin.fileno(), data)
639
640
    def recv(self, count):
641
        return os.read(self.proc.stdout.fileno(), count)
642
643
    def close(self):
644
        self.proc.stdin.close()
645
        self.proc.stdout.close()
646
        self.proc.wait()
647
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
648
    def get_filelike_channels(self):
649
        return (self.proc.stdout, self.proc.stdin)
2221.5.21 by Dmitry Vasiliev
Reverted trailing whitespace removal
650