/brz/remove-bazaar

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