~bzr-pqm/bzr/bzr.dev

5594.1.1 by Vincent Ladeuil
Fix socketpair-based SSH transport leaking socket into other child processes
1
# Copyright (C) 2006-2011 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
6379.6.7 by Jelmer Vernooij
Move importing from future until after doc string, otherwise the doc string will disappear.
18
"""Foundation SSH support for SFTP and smart server."""
19
6379.6.3 by Jelmer Vernooij
Use absolute_import.
20
from __future__ import absolute_import
21
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
22
import errno
4304.2.1 by Vincent Ladeuil
Fix bug #367726 by reverting some default user handling introduced
23
import getpass
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
24
import logging
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
25
import os
26
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.
27
import subprocess
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
28
import sys
29
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
30
from bzrlib import (
31
    config,
32
    errors,
33
    osutils,
34
    trace,
35
    ui,
36
    )
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
37
38
try:
39
    import paramiko
40
except ImportError, e:
2104.5.1 by John Arbash Meinel
Remove the strict dependency on paramiko for ssh access
41
    # If we have an ssh subprocess, we don't strictly need paramiko for all ssh
42
    # access
43
    paramiko = None
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
44
else:
45
    from paramiko.sftp_client import SFTPClient
46
47
48
SYSTEM_HOSTKEYS = {}
49
BZR_HOSTKEYS = {}
50
51
1951.1.5 by Andrew Bennetts
Fix some missing imports with a bit of help from pyflakes.
52
_paramiko_version = getattr(paramiko, '__version_info__', (0, 0, 0))
53
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
54
# Paramiko 1.5 tries to open a socket.AF_UNIX in order to connect
55
# to ssh-agent. That attribute doesn't exist on win32 (it does in cygwin)
56
# so we get an AttributeError exception. So we will not try to
57
# connect to an agent if we are on win32 and using Paramiko older than 1.6
58
_use_ssh_agent = (sys.platform != 'win32' or _paramiko_version >= (1, 6, 0))
59
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
60
61
class SSHVendorManager(object):
62
    """Manager for manage SSH vendors."""
63
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
64
    # Note, although at first sign the class interface seems similar to
2221.5.22 by Dmitry Vasiliev
Updated note about registry.Registry
65
    # bzrlib.registry.Registry it is not possible/convenient to directly use
66
    # the Registry because the class just has "get()" interface instead of the
67
    # Registry's "get(key)".
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
68
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
69
    def __init__(self):
70
        self._ssh_vendors = {}
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
71
        self._cached_ssh_vendor = None
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
72
        self._default_ssh_vendor = None
73
74
    def register_default_vendor(self, vendor):
75
        """Register default SSH vendor."""
76
        self._default_ssh_vendor = vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
77
78
    def register_vendor(self, name, vendor):
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
79
        """Register new SSH vendor by name."""
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
80
        self._ssh_vendors[name] = vendor
81
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
82
    def clear_cache(self):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
83
        """Clear previously cached lookup result."""
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
84
        self._cached_ssh_vendor = None
85
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
86
    def _get_vendor_by_environment(self, environment=None):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
87
        """Return the vendor or None based on BZR_SSH environment variable.
88
89
        :raises UnknownSSH: if the BZR_SSH environment variable contains
90
                            unknown vendor name
91
        """
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
92
        if environment is None:
93
            environment = os.environ
94
        if 'BZR_SSH' in environment:
95
            vendor_name = environment['BZR_SSH']
96
            try:
97
                vendor = self._ssh_vendors[vendor_name]
98
            except KeyError:
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
99
                vendor = self._get_vendor_from_path(vendor_name)
100
                if vendor is None:
101
                    raise errors.UnknownSSH(vendor_name)
102
                vendor.executable_path = vendor_name
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
103
            return vendor
104
        return None
105
106
    def _get_ssh_version_string(self, args):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
107
        """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
108
        try:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
109
            p = subprocess.Popen(args,
110
                                 stdout=subprocess.PIPE,
111
                                 stderr=subprocess.PIPE,
112
                                 **os_specific_subprocess_params())
113
            stdout, stderr = p.communicate()
114
        except OSError:
115
            stdout = stderr = ''
116
        return stdout + stderr
117
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
118
    def _get_vendor_by_version_string(self, version, progname):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
119
        """Return the vendor or None based on output from the subprocess.
120
121
        :param version: The output of 'ssh -V' like command.
2772.3.1 by Martin Pool
Fix detection of ssh implementation on Windows
122
        :param args: Command line that was run.
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
123
        """
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
124
        vendor = None
125
        if 'OpenSSH' in version:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
126
            trace.mutter('ssh implementation is OpenSSH')
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
127
            vendor = OpenSSHSubprocessVendor()
128
        elif 'SSH Secure Shell' in version:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
129
            trace.mutter('ssh implementation is SSH Corp.')
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
130
            vendor = SSHCorpSubprocessVendor()
5444.2.3 by Matthew Gordon
Added missing check for GNU lsh in _get_vendor_by_version_string().
131
        elif 'lsh' in version:
132
            trace.mutter('ssh implementation is GNU lsh.')
133
            vendor = LSHSubprocessVendor()
4595.17.2 by Martin
Merge bzr.dev 4789 to resolve conflict from the disabling of plink auto-detection, and relocate NEWS
134
        # As plink user prompts are not handled currently, don't auto-detect
135
        # it by inspection below, but keep this vendor detection for if a path
136
        # is given in BZR_SSH. See https://bugs.launchpad.net/bugs/414743
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
137
        elif 'plink' in version and progname == 'plink':
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
138
            # Checking if "plink" was the executed argument as Windows
5448.2.1 by Martin
Fix some "its" vs. "it's" spelling confusion in bzrlib code... also, ahem, a name in the NEWS file
139
            # sometimes reports 'ssh -V' incorrectly with 'plink' in its
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
140
            # version.  See https://bugs.launchpad.net/bzr/+bug/107155
141
            trace.mutter("ssh implementation is Putty's plink.")
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
142
            vendor = PLinkSubprocessVendor()
143
        return vendor
144
145
    def _get_vendor_by_inspection(self):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
146
        """Return the vendor or None by checking for known SSH implementations."""
4595.17.2 by Martin
Merge bzr.dev 4789 to resolve conflict from the disabling of plink auto-detection, and relocate NEWS
147
        version = self._get_ssh_version_string(['ssh', '-V'])
148
        return self._get_vendor_by_version_string(version, "ssh")
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
149
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
150
    def _get_vendor_from_path(self, path):
151
        """Return the vendor or None using the program at the given path"""
152
        version = self._get_ssh_version_string([path, '-V'])
153
        return self._get_vendor_by_version_string(version, 
154
            os.path.splitext(os.path.basename(path))[0])
155
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
156
    def get_vendor(self, environment=None):
2221.5.15 by Dmitry Vasiliev
Added docstrings for all SSHVendorManager's methods
157
        """Find out what version of SSH is on the system.
158
159
        :raises SSHVendorNotFound: if no any SSH vendor is found
160
        :raises UnknownSSH: if the BZR_SSH environment variable contains
161
                            unknown vendor name
162
        """
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
163
        if self._cached_ssh_vendor is None:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
164
            vendor = self._get_vendor_by_environment(environment)
165
            if vendor is None:
166
                vendor = self._get_vendor_by_inspection()
167
                if vendor is None:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
168
                    trace.mutter('falling back to default implementation')
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
169
                    vendor = self._default_ssh_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
170
                    if vendor is None:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
171
                        raise errors.SSHVendorNotFound()
2221.5.8 by Dmitry Vasiliev
Added SSHVendorManager.clear_cache() method
172
            self._cached_ssh_vendor = vendor
173
        return self._cached_ssh_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
174
175
_ssh_vendor_manager = SSHVendorManager()
176
_get_ssh_vendor = _ssh_vendor_manager.get_vendor
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
177
register_default_ssh_vendor = _ssh_vendor_manager.register_default_vendor
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
178
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
179
180
5050.2.1 by Martin
Drive-by fix for breakin killing off ssh child processes
181
def _ignore_signals():
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
182
    # TODO: This should possibly ignore SIGHUP as well, but bzr currently
183
    # doesn't handle it itself.
184
    # <https://launchpad.net/products/bzr/+bug/41433/+index>
185
    import signal
186
    signal.signal(signal.SIGINT, signal.SIG_IGN)
5050.2.1 by Martin
Drive-by fix for breakin killing off ssh child processes
187
    # GZ 2010-02-19: Perhaps make this check if breakin is installed instead
188
    if signal.getsignal(signal.SIGQUIT) != signal.SIG_DFL:
189
        signal.signal(signal.SIGQUIT, signal.SIG_IGN)
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
190
191
3353.1.3 by Andrew Bennetts
Always adapt sockets to look like paramiko Channels before passing them to paramiko's SFTPClient.
192
class SocketAsChannelAdapter(object):
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
193
    """Simple wrapper for a socket that pretends to be a paramiko Channel."""
194
195
    def __init__(self, sock):
196
        self.__socket = sock
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
197
3353.1.2 by Andrew Bennetts
Add get_name to LoopbackSFTP. Makes the current tests pass with current paramiko.
198
    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.
199
        return "bzr SocketAsChannelAdapter"
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
200
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
201
    def send(self, data):
202
        return self.__socket.send(data)
203
204
    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.
205
        try:
206
            return self.__socket.recv(n)
207
        except socket.error, e:
208
            if e.args[0] in (errno.EPIPE, errno.ECONNRESET, errno.ECONNABORTED,
209
                             errno.EBADF):
210
                # Connection has closed.  Paramiko expects an empty string in
211
                # this case, not an exception.
212
                return ''
213
            raise
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
214
215
    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.
216
        # TODO: jam 20051215 this function is necessary to support the
217
        # pipelined() function. In reality, it probably should use
218
        # poll() or select() to actually return if there is data
219
        # 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
220
        return True
221
222
    def close(self):
223
        self.__socket.close()
224
225
226
class SSHVendor(object):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
227
    """Abstract base class for SSH vendor implementations."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
228
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
229
    def connect_sftp(self, username, password, host, port):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
230
        """Make an SSH connection, and return an SFTPClient.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
231
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
232
        :param username: an ascii string
233
        :param password: an ascii string
234
        :param host: a host name as an ascii string
235
        :param port: a port number
236
        :type port: int
237
238
        :raises: ConnectionError if it cannot connect.
239
240
        :rtype: paramiko.sftp_client.SFTPClient
241
        """
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
242
        raise NotImplementedError(self.connect_sftp)
243
244
    def connect_ssh(self, username, password, host, port, command):
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
245
        """Make an SSH connection.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
246
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
247
        :returns: an SSHConnection.
1951.1.12 by Andrew Bennetts
Cosmetic tweaks.
248
        """
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
249
        raise NotImplementedError(self.connect_ssh)
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
250
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
251
    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
252
                                msg='Unable to connect to SSH host'):
253
        """Raise a SocketConnectionError with properly formatted host.
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
254
255
        This just unifies all the locations that try to raise ConnectionError,
256
        so that they format things properly.
257
        """
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
258
        raise errors.SocketConnectionError(host=host, port=port, msg=msg,
259
                                           orig_error=orig_error)
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
260
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
261
262
class LoopbackVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
263
    """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.
264
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
265
    def connect_sftp(self, username, password, host, port):
266
        sock = socket.socket()
267
        try:
268
            sock.connect((host, port))
269
        except socket.error, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
270
            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.
271
        return SFTPClient(SocketAsChannelAdapter(sock))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
272
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
273
register_ssh_vendor('loopback', LoopbackVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
274
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
275
276
class ParamikoVendor(SSHVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
277
    """Vendor that uses paramiko."""
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
278
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
279
    def _connect(self, username, password, host, port):
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
280
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
281
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
282
        load_host_keys()
283
284
        try:
285
            t = paramiko.Transport((host, port or 22))
286
            t.set_log_channel('bzr.paramiko')
287
            t.start_client()
288
        except (paramiko.SSHException, socket.error), e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
289
            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.
290
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
291
        server_key = t.get_remote_server_key()
292
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
293
        keytype = server_key.get_name()
1711.9.10 by John Arbash Meinel
Update transport/ssh.py to remove has_key usage
294
        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
295
            our_server_key = SYSTEM_HOSTKEYS[host][keytype]
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
296
            our_server_key_hex = paramiko.util.hexify(
297
                our_server_key.get_fingerprint())
1711.9.10 by John Arbash Meinel
Update transport/ssh.py to remove has_key usage
298
        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
299
            our_server_key = BZR_HOSTKEYS[host][keytype]
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
300
            our_server_key_hex = paramiko.util.hexify(
301
                our_server_key.get_fingerprint())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
302
        else:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
303
            trace.warning('Adding %s host key for %s: %s'
304
                          % (keytype, host, server_key_hex))
2127.3.1 by Alexander Belchenko
Use BZR_HOSTKEYS.add instead of deprecated dict-like paramiko interface
305
            add = getattr(BZR_HOSTKEYS, 'add', None)
306
            if add is not None: # paramiko >= 1.X.X
307
                BZR_HOSTKEYS.add(host, keytype, server_key)
308
            else:
1551.9.2 by Aaron Bentley
Bugfix for paramiko connections
309
                BZR_HOSTKEYS.setdefault(host, {})[keytype] = server_key
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
310
            our_server_key = server_key
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
311
            our_server_key_hex = paramiko.util.hexify(
312
                our_server_key.get_fingerprint())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
313
            save_host_keys()
314
        if server_key != our_server_key:
315
            filename1 = os.path.expanduser('~/.ssh/known_hosts')
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
316
            filename2 = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
317
            raise errors.TransportError(
318
                '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
319
                (host, our_server_key_hex, server_key_hex),
320
                ['Try editing %s or %s' % (filename1, filename2)])
321
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
322
        _paramiko_auth(username, password, host, port, t)
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
323
        return t
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
324
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
325
    def connect_sftp(self, username, password, host, port):
326
        t = self._connect(username, password, host, port)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
327
        try:
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
328
            return t.open_sftp_client()
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
329
        except paramiko.SSHException, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
330
            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
331
                                         msg='Unable to start sftp client')
2018.1.9 by Andrew Bennetts
Implement ParamikoVendor.connect_ssh
332
333
    def connect_ssh(self, username, password, host, port, command):
334
        t = self._connect(username, password, host, port)
335
        try:
336
            channel = t.open_session()
337
            cmdline = ' '.join(command)
338
            channel.exec_command(cmdline)
339
            return _ParamikoSSHConnection(channel)
340
        except paramiko.SSHException, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
341
            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
342
                                         msg='Unable to invoke remote bzr')
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
343
5430.6.1 by Andrew Bennetts
Simplify connect_sftp/ssh error handling, hopefully resolving intermittent test failure in test_bad_connection_ssh.
344
_ssh_connection_errors = (EOFError, OSError, IOError, socket.error)
2104.5.1 by John Arbash Meinel
Remove the strict dependency on paramiko for ssh access
345
if paramiko is not None:
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
346
    vendor = ParamikoVendor()
347
    register_ssh_vendor('paramiko', vendor)
348
    register_ssh_vendor('none', vendor)
2221.5.5 by Dmitry Vasiliev
Added 'register_default_vendor' method to the SSHVendorManager
349
    register_default_ssh_vendor(vendor)
5430.6.1 by Andrew Bennetts
Simplify connect_sftp/ssh error handling, hopefully resolving intermittent test failure in test_bad_connection_ssh.
350
    _ssh_connection_errors += (paramiko.SSHException,)
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
351
    del vendor
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
6331.5.1 by Martin Packman
Stop requiring a bzr subprocess in bt.test_sftp_transport for ssh connection error test
357
    # In general stderr should be inherited from the parent process so prompts
358
    # are visible on the terminal. This can be overriden to another file for
359
    # tests, but beware of using PIPE which may hang due to not being read.
360
    _stderr_target = None
361
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
362
    def _connect(self, argv):
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
363
        # Attempt to make a socketpair to use as stdin/stdout for the SSH
364
        # subprocess.  We prefer sockets to pipes because they support
365
        # non-blocking short reads, allowing us to optimistically read 64k (or
366
        # whatever) chunks.
367
        try:
368
            my_sock, subproc_sock = socket.socketpair()
5582.6.1 by Max Bowsher
Fix socketpair-based SSH transport leaking socket into other child processes.
369
            osutils.set_fd_cloexec(my_sock)
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
370
        except (AttributeError, socket.error):
371
            # This platform doesn't support socketpair(), so just use ordinary
372
            # pipes instead.
373
            stdin = stdout = subprocess.PIPE
5050.54.3 by Max Bowsher
Also close the subprocess side of the socketpair within bzrlib.
374
            my_sock, subproc_sock = None, None
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
375
        else:
376
            stdin = stdout = subproc_sock
377
        proc = subprocess.Popen(argv, stdin=stdin, stdout=stdout,
6331.5.1 by Martin Packman
Stop requiring a bzr subprocess in bt.test_sftp_transport for ssh connection error test
378
                                stderr=self._stderr_target,
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
379
                                **os_specific_subprocess_params())
5050.54.3 by Max Bowsher
Also close the subprocess side of the socketpair within bzrlib.
380
        if subproc_sock is not None:
381
            subproc_sock.close()
382
        return SSHSubprocessConnection(proc, sock=my_sock)
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
383
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
384
    def connect_sftp(self, username, password, host, port):
385
        try:
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
386
            argv = self._get_vendor_specific_argv(username, host, port,
387
                                                  subsystem='sftp')
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
388
            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.
389
            return SFTPClient(SocketAsChannelAdapter(sock))
5430.6.1 by Andrew Bennetts
Simplify connect_sftp/ssh error handling, hopefully resolving intermittent test failure in test_bad_connection_ssh.
390
        except _ssh_connection_errors, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
391
            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.
392
2018.1.1 by Andrew Bennetts
Make bzr+ssh:// actually work (at least with absolute paths).
393
    def connect_ssh(self, username, password, host, port, command):
394
        try:
395
            argv = self._get_vendor_specific_argv(username, host, port,
396
                                                  command=command)
2018.1.6 by Andrew Bennetts
Remove a little bit of duplication in ssh.py
397
            return self._connect(argv)
5430.6.1 by Andrew Bennetts
Simplify connect_sftp/ssh error handling, hopefully resolving intermittent test failure in test_bad_connection_ssh.
398
        except _ssh_connection_errors, e:
2052.4.2 by John Arbash Meinel
Refactor all 'raise ConnectionError' into a helper function
399
            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).
400
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
401
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
402
                                  command=None):
403
        """Returns the argument list to run the subprocess with.
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
404
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
405
        Exactly one of 'subsystem' and 'command' must be specified.
406
        """
407
        raise NotImplementedError(self._get_vendor_specific_argv)
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
408
409
410
class OpenSSHSubprocessVendor(SubprocessVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
411
    """SSH vendor that uses the 'ssh' executable from OpenSSH."""
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
412
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
413
    executable_path = 'ssh'
414
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
415
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
416
                                  command=None):
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
417
        args = [self.executable_path,
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
418
                '-oForwardX11=no', '-oForwardAgent=no',
5459.4.1 by Neil Martinsen-Burrell
dont force openssh to use protocol=2
419
                '-oClearAllForwardings=yes',
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
420
                '-oNoHostAuthenticationForLocalhost=yes']
421
        if port is not None:
422
            args.extend(['-p', str(port)])
423
        if username is not None:
424
            args.extend(['-l', username])
425
        if subsystem is not None:
426
            args.extend(['-s', host, subsystem])
427
        else:
428
            args.extend([host] + command)
429
        return args
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
430
1951.1.11 by Andrew Bennetts
Change register_ssh_vendor to take an instance rather than a class.
431
register_ssh_vendor('openssh', OpenSSHSubprocessVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
432
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
433
434
class SSHCorpSubprocessVendor(SubprocessVendor):
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
435
    """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.
436
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
437
    executable_path = 'ssh'
438
1951.1.9 by Andrew Bennetts
Add docstrings and tweak method names in ssh.py
439
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
440
                                  command=None):
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
441
        args = [self.executable_path, '-x']
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
442
        if port is not None:
443
            args.extend(['-p', str(port)])
444
        if username is not None:
445
            args.extend(['-l', username])
446
        if subsystem is not None:
447
            args.extend(['-s', subsystem, host])
448
        else:
449
            args.extend([host] + command)
450
        return args
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
451
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
452
register_ssh_vendor('sshcorp', SSHCorpSubprocessVendor())
1951.1.10 by Andrew Bennetts
Move register_ssh_vendor, _ssh_vendor and _get_ssh_vendor into ssh.py
453
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
454
5444.2.1 by Matthew Gordon
Added GNU lsh support to supported SSH vendors.
455
class LSHSubprocessVendor(SubprocessVendor):
456
    """SSH vendor that uses the 'lsh' executable from GNU"""
457
458
    executable_path = 'lsh'
459
460
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
461
                                  command=None):
462
        args = [self.executable_path]
463
        if port is not None:
464
            args.extend(['-p', str(port)])
465
        if username is not None:
466
            args.extend(['-l', username])
467
        if subsystem is not None:
468
            args.extend(['--subsystem', subsystem, host])
469
        else:
470
            args.extend([host] + command)
471
        return args
472
473
register_ssh_vendor('lsh', LSHSubprocessVendor())
474
475
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
476
class PLinkSubprocessVendor(SubprocessVendor):
477
    """SSH vendor that uses the 'plink' executable from Putty."""
478
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
479
    executable_path = 'plink'
480
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
481
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
482
                                  command=None):
4595.17.1 by Martin
Add ability to give a path to a particular ssh client in BZR_SSH envvar
483
        args = [self.executable_path, '-x', '-a', '-ssh', '-2', '-batch']
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
484
        if port is not None:
485
            args.extend(['-P', str(port)])
486
        if username is not None:
487
            args.extend(['-l', username])
488
        if subsystem is not None:
2221.5.3 by Dmitry Vasiliev
Fixed plink's arguments order. Added tests for such a case.
489
            args.extend(['-s', host, subsystem])
2221.5.1 by Dmitry Vasiliev
Added support for Putty's SSH implementation
490
        else:
491
            args.extend([host] + command)
492
        return args
493
494
register_ssh_vendor('plink', PLinkSubprocessVendor())
495
496
2900.2.8 by Vincent Ladeuil
Make sftp and bzr+ssh aware of authentication config.
497
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
498
    auth = config.AuthenticationConfig()
3777.1.5 by Aaron Bentley
Remove AuthenticationConfig handling from Paramiko SSHVendor
499
    # paramiko requires a username, but it might be none if nothing was
500
    # supplied.  If so, use the local username.
2900.2.15 by Vincent Ladeuil
AuthenticationConfig can be queried for logins too (first step).
501
    if username is None:
4304.2.1 by Vincent Ladeuil
Fix bug #367726 by reverting some default user handling introduced
502
        username = auth.get_user('ssh', host, port=port,
503
                                 default=getpass.getuser())
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
504
    if _use_ssh_agent:
505
        agent = paramiko.Agent()
506
        for key in agent.get_keys():
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
507
            trace.mutter('Trying SSH agent key %s'
508
                         % paramiko.util.hexify(key.get_fingerprint()))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
509
            try:
510
                paramiko_transport.auth_publickey(username, key)
511
                return
512
            except paramiko.SSHException, e:
513
                pass
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
514
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
515
    # okay, try finding id_rsa or id_dss?  (posix only)
516
    if _try_pkey_auth(paramiko_transport, paramiko.RSAKey, username, 'id_rsa'):
517
        return
518
    if _try_pkey_auth(paramiko_transport, paramiko.DSSKey, username, 'id_dsa'):
519
        return
520
4555.1.1 by John Arbash Meinel
Fix bug #375867, check if password is a supported auth type
521
    # If we have gotten this far, we are about to try for passwords, do an
522
    # auth_none check to see if it is even supported.
523
    supported_auth_types = []
524
    try:
525
        # Note that with paramiko <1.7.5 this logs an INFO message:
526
        #    Authentication type (none) not permitted.
527
        # So we explicitly disable the logging level for this action
528
        old_level = paramiko_transport.logger.level
529
        paramiko_transport.logger.setLevel(logging.WARNING)
530
        try:
531
            paramiko_transport.auth_none(username)
532
        finally:
533
            paramiko_transport.logger.setLevel(old_level)
534
    except paramiko.BadAuthenticationType, e:
535
        # Supported methods are in the exception
536
        supported_auth_types = e.allowed_types
537
    except paramiko.SSHException, e:
538
        # Don't know what happened, but just ignore it
539
        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'.
540
    # We treat 'keyboard-interactive' and 'password' auth methods identically,
541
    # because Paramiko's auth_password method will automatically try
542
    # 'keyboard-interactive' auth (using the password as the response) if
543
    # 'password' auth is not available.  Apparently some Debian and Gentoo
544
    # OpenSSH servers require this.
545
    # XXX: It's possible for a server to require keyboard-interactive auth that
546
    # requires something other than a single password, but we currently don't
547
    # support that.
548
    if ('password' not in supported_auth_types and
549
        '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
550
        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.
551
            '\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
552
            % (username, host, supported_auth_types))
553
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
554
    if password:
555
        try:
556
            paramiko_transport.auth_password(username, password)
557
            return
558
        except paramiko.SSHException, e:
559
            pass
560
561
    # give up and ask for a password
2900.2.12 by Vincent Ladeuil
Since all schemes query AuthenticationConfig then prompt user, make that
562
    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
563
    # get_password can still return None, which means we should not prompt
564
    if password is not None:
565
        try:
566
            paramiko_transport.auth_password(username, password)
567
        except paramiko.SSHException, e:
4555.1.3 by John Arbash Meinel
Reformat the errors so they aren't so long.
568
            raise errors.ConnectionError(
569
                'Unable to authenticate to SSH host as'
570
                '\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
571
    else:
4555.1.3 by John Arbash Meinel
Reformat the errors so they aren't so long.
572
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
573
                                     '  %s@%s' % (username, host))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
574
575
576
def _try_pkey_auth(paramiko_transport, pkey_class, username, filename):
577
    filename = os.path.expanduser('~/.ssh/' + filename)
578
    try:
579
        key = pkey_class.from_private_key_file(filename)
580
        paramiko_transport.auth_publickey(username, key)
581
        return True
582
    except paramiko.PasswordRequiredException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
583
        password = ui.ui_factory.get_password(
5863.6.1 by Jelmer Vernooij
Require a unicode prompt to be passed into all methods that prompt.
584
            prompt=u'SSH %(filename)s password',
585
            filename=filename.decode(osutils._fs_enc))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
586
        try:
587
            key = pkey_class.from_private_key_file(filename, password)
588
            paramiko_transport.auth_publickey(username, key)
589
            return True
590
        except paramiko.SSHException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
591
            trace.mutter('SSH authentication via %s key failed.'
592
                         % (os.path.basename(filename),))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
593
    except paramiko.SSHException:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
594
        trace.mutter('SSH authentication via %s key failed.'
595
                     % (os.path.basename(filename),))
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
596
    except IOError:
597
        pass
598
    return False
599
600
601
def load_host_keys():
602
    """
603
    Load system host keys (probably doesn't work on windows) and any
604
    "discovered" keys from previous sessions.
605
    """
606
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
607
    try:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
608
        SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(
609
            os.path.expanduser('~/.ssh/known_hosts'))
2358.3.1 by Martin Pool
Update some too-general exception blocks
610
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
611
        trace.mutter('failed to load system host keys: ' + str(e))
612
    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
613
    try:
614
        BZR_HOSTKEYS = paramiko.util.load_host_keys(bzr_hostkey_path)
2358.3.1 by Martin Pool
Update some too-general exception blocks
615
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
616
        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
617
        save_host_keys()
618
619
620
def save_host_keys():
621
    """
622
    Save "discovered" host keys in $(config)/ssh_host_keys/.
623
    """
624
    global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
625
    bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
626
    config.ensure_config_dir_exists()
1951.1.4 by Andrew Bennetts
Start moving SSH connection code into bzrlib/transport/ssh.py
627
628
    try:
629
        f = open(bzr_hostkey_path, 'w')
630
        f.write('# SSH host keys collected by bzr\n')
631
        for hostname, keys in BZR_HOSTKEYS.iteritems():
632
            for keytype, key in keys.iteritems():
633
                f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
634
        f.close()
635
    except IOError, e:
2900.2.18 by Vincent Ladeuil
Previous commits didn't check the test suite enough.
636
        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
637
638
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
639
def os_specific_subprocess_params():
640
    """Get O/S specific subprocess parameters."""
641
    if sys.platform == 'win32':
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
642
        # 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.
643
        # win32
644
        return {}
645
    else:
3943.8.1 by Marius Kruger
remove all trailing whitespace from bzr source
646
        # 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.
647
        # them to be open.
648
        #
649
        # We also set the child process to ignore SIGINT.  Normally the signal
650
        # would be sent to every process in the foreground process group, but
651
        # this causes it to be seen only by bzr and not by ssh.  Python will
652
        # generate a KeyboardInterrupt in bzr, and we will then have a chance
653
        # 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
654
        # 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.
655
        # <https://launchpad.net/products/bzr/+bug/5987>
656
        #
657
        # Running it in a separate process group is not good because then it
658
        # can't get non-echoed input of a password or passphrase.
659
        # <https://launchpad.net/products/bzr/+bug/40508>
5050.2.1 by Martin
Drive-by fix for breakin killing off ssh child processes
660
        return {'preexec_fn': _ignore_signals,
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
661
                'close_fds': True,
662
                }
663
4824.1.1 by Andrew Bennetts
Terminate SSHSubprocesses when no refs to them are left, in case .close is never called.
664
import weakref
665
_subproc_weakrefs = set()
666
5050.54.1 by Max Bowsher
Do close the socket used for stdin/out to a ssh subprocess.
667
def _close_ssh_proc(proc, sock):
5050.30.1 by Andrew Bennetts
Fix AttributeError in _close_ssh_proc.
668
    """Carefully close stdin/stdout and reap the SSH process.
669
670
    If the pipes are already closed and/or the process has already been
671
    wait()ed on, that's ok, and no error is raised.  The goal is to do our best
672
    to clean up (whether or not a clean up was already tried).
673
    """
5050.54.1 by Max Bowsher
Do close the socket used for stdin/out to a ssh subprocess.
674
    funcs = []
675
    for closeable in (proc.stdin, proc.stdout, sock):
676
        # We expect that either proc (a subprocess.Popen) will have stdin and
677
        # stdout streams to close, or that we will have been passed a socket to
678
        # close, with the option not in use being None.
679
        if closeable is not None:
680
            funcs.append(closeable.close)
681
    funcs.append(proc.wait)
682
    for func in funcs:
683
        try:
684
            func()
4824.1.1 by Andrew Bennetts
Terminate SSHSubprocesses when no refs to them are left, in case .close is never called.
685
        except OSError:
5050.30.1 by Andrew Bennetts
Fix AttributeError in _close_ssh_proc.
686
            # It's ok for the pipe to already be closed, or the process to
687
            # already be finished.
688
            continue
4824.1.1 by Andrew Bennetts
Terminate SSHSubprocesses when no refs to them are left, in case .close is never called.
689
1951.1.12 by Andrew Bennetts
Cosmetic tweaks.
690
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
691
class SSHConnection(object):
692
    """Abstract base class for SSH connections."""
693
694
    def get_sock_or_pipes(self):
695
        """Returns a (kind, io_object) pair.
696
697
        If kind == 'socket', then io_object is a socket.
698
699
        If kind == 'pipes', then io_object is a pair of file-like objects
700
        (read_from, write_to).
701
        """
702
        raise NotImplementedError(self.get_sock_or_pipes)
703
704
    def close(self):
705
        raise NotImplementedError(self.close)
706
707
708
class SSHSubprocessConnection(SSHConnection):
5284.5.3 by Andrew Bennetts
Docstring tweaks.
709
    """A connection to an ssh subprocess via pipes or a socket.
710
711
    This class is also socket-like enough to be used with
712
    SocketAsChannelAdapter (it has 'send' and 'recv' methods).
713
    """
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
714
715
    def __init__(self, proc, sock=None):
716
        """Constructor.
717
718
        :param proc: a subprocess.Popen
719
        :param sock: if proc.stdin/out is a socket from a socketpair, then sock
720
            should bzrlib's half of that socketpair.  If not passed, proc's
721
            stdin/out is assumed to be ordinary pipes.
722
        """
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
723
        self.proc = proc
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
724
        self._sock = sock
4824.1.1 by Andrew Bennetts
Terminate SSHSubprocesses when no refs to them are left, in case .close is never called.
725
        # Add a weakref to proc that will attempt to do the same as self.close
726
        # to avoid leaving processes lingering indefinitely.
727
        def terminate(ref):
728
            _subproc_weakrefs.remove(ref)
5050.54.1 by Max Bowsher
Do close the socket used for stdin/out to a ssh subprocess.
729
            _close_ssh_proc(proc, sock)
4824.1.1 by Andrew Bennetts
Terminate SSHSubprocesses when no refs to them are left, in case .close is never called.
730
        _subproc_weakrefs.add(weakref.ref(self, terminate))
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
731
732
    def send(self, data):
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
733
        if self._sock is not None:
734
            return self._sock.send(data)
735
        else:
736
            return os.write(self.proc.stdin.fileno(), data)
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
737
738
    def recv(self, count):
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
739
        if self._sock is not None:
5303.1.1 by Vincent Ladeuil
Fix typo: recv() on sockets, read() on files ;)
740
            return self._sock.recv(count)
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
741
        else:
742
            return os.read(self.proc.stdout.fileno(), count)
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
743
744
    def close(self):
5050.54.1 by Max Bowsher
Do close the socket used for stdin/out to a ssh subprocess.
745
        _close_ssh_proc(self.proc, self._sock)
1951.1.7 by Andrew Bennetts
Move more generic SSH code from sftp.py into ssh.py, and start unifying the connection establishing logic.
746
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
747
    def get_sock_or_pipes(self):
748
        if self._sock is not None:
749
            return 'socket', self._sock
750
        else:
751
            return 'pipes', (self.proc.stdout, self.proc.stdin)
752
753
754
class _ParamikoSSHConnection(SSHConnection):
5284.5.3 by Andrew Bennetts
Docstring tweaks.
755
    """An SSH connection via paramiko."""
756
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
757
    def __init__(self, channel):
758
        self.channel = channel
759
760
    def get_sock_or_pipes(self):
5284.5.2 by Andrew Bennetts
Use the socket-medium with paramiko connections as well as socketpair-to-subprocess connections, as quick inspection of the paramiko source suggests it handles EINTR ok.
761
        return ('socket', self.channel)
5284.5.1 by Andrew Bennetts
Use socketpairs (rather than pipes) for SSH subprocesses where possible, and formalise some internal APIs a little more.
762
763
    def close(self):
764
        return self.channel.close()
765
2221.5.21 by Dmitry Vasiliev
Reverted trailing whitespace removal
766