~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

  • Committer: Patch Queue Manager
  • Date: 2016-02-01 19:13:13 UTC
  • mfrom: (6614.2.2 trunk)
  • Revision ID: pqm@pqm.ubuntu.com-20160201191313-wdfvmfff1djde6oq
(vila) Release 2.7.0 (Vincent Ladeuil)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2006-2010 Robey Pointer <robey@lag.net>
 
1
# Copyright (C) 2006-2011 Robey Pointer <robey@lag.net>
2
2
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
17
17
 
18
18
"""Foundation SSH support for SFTP and smart server."""
19
19
 
 
20
from __future__ import absolute_import
 
21
 
20
22
import errno
21
23
import getpass
22
24
import logging
24
26
import socket
25
27
import subprocess
26
28
import sys
 
29
from binascii import hexlify
27
30
 
28
31
from bzrlib import (
29
32
    config,
126
129
        elif 'SSH Secure Shell' in version:
127
130
            trace.mutter('ssh implementation is SSH Corp.')
128
131
            vendor = SSHCorpSubprocessVendor()
 
132
        elif 'lsh' in version:
 
133
            trace.mutter('ssh implementation is GNU lsh.')
 
134
            vendor = LSHSubprocessVendor()
129
135
        # As plink user prompts are not handled currently, don't auto-detect
130
136
        # it by inspection below, but keep this vendor detection for if a path
131
137
        # is given in BZR_SSH. See https://bugs.launchpad.net/bugs/414743
271
277
class ParamikoVendor(SSHVendor):
272
278
    """Vendor that uses paramiko."""
273
279
 
 
280
    def _hexify(self, s):
 
281
        return hexlify(s).upper()
 
282
 
274
283
    def _connect(self, username, password, host, port):
275
284
        global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
276
285
 
284
293
            self._raise_connection_error(host, port=port, orig_error=e)
285
294
 
286
295
        server_key = t.get_remote_server_key()
287
 
        server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
 
296
        server_key_hex = self._hexify(server_key.get_fingerprint())
288
297
        keytype = server_key.get_name()
289
298
        if host in SYSTEM_HOSTKEYS and keytype in SYSTEM_HOSTKEYS[host]:
290
299
            our_server_key = SYSTEM_HOSTKEYS[host][keytype]
291
 
            our_server_key_hex = paramiko.util.hexify(
292
 
                our_server_key.get_fingerprint())
 
300
            our_server_key_hex = self._hexify(our_server_key.get_fingerprint())
293
301
        elif host in BZR_HOSTKEYS and keytype in BZR_HOSTKEYS[host]:
294
302
            our_server_key = BZR_HOSTKEYS[host][keytype]
295
 
            our_server_key_hex = paramiko.util.hexify(
296
 
                our_server_key.get_fingerprint())
 
303
            our_server_key_hex = self._hexify(our_server_key.get_fingerprint())
297
304
        else:
298
305
            trace.warning('Adding %s host key for %s: %s'
299
306
                          % (keytype, host, server_key_hex))
303
310
            else:
304
311
                BZR_HOSTKEYS.setdefault(host, {})[keytype] = server_key
305
312
            our_server_key = server_key
306
 
            our_server_key_hex = paramiko.util.hexify(
307
 
                our_server_key.get_fingerprint())
 
313
            our_server_key_hex = self._hexify(our_server_key.get_fingerprint())
308
314
            save_host_keys()
309
315
        if server_key != our_server_key:
310
316
            filename1 = os.path.expanduser('~/.ssh/known_hosts')
349
355
class SubprocessVendor(SSHVendor):
350
356
    """Abstract base class for vendors that use pipes to a subprocess."""
351
357
 
 
358
    # In general stderr should be inherited from the parent process so prompts
 
359
    # are visible on the terminal. This can be overriden to another file for
 
360
    # tests, but beware of using PIPE which may hang due to not being read.
 
361
    _stderr_target = None
 
362
 
352
363
    def _connect(self, argv):
353
364
        # Attempt to make a socketpair to use as stdin/stdout for the SSH
354
365
        # subprocess.  We prefer sockets to pipes because they support
356
367
        # whatever) chunks.
357
368
        try:
358
369
            my_sock, subproc_sock = socket.socketpair()
 
370
            osutils.set_fd_cloexec(my_sock)
359
371
        except (AttributeError, socket.error):
360
372
            # This platform doesn't support socketpair(), so just use ordinary
361
373
            # pipes instead.
362
374
            stdin = stdout = subprocess.PIPE
363
 
            sock = None
 
375
            my_sock, subproc_sock = None, None
364
376
        else:
365
377
            stdin = stdout = subproc_sock
366
 
            sock = my_sock
367
378
        proc = subprocess.Popen(argv, stdin=stdin, stdout=stdout,
 
379
                                stderr=self._stderr_target,
368
380
                                **os_specific_subprocess_params())
369
 
        return SSHSubprocessConnection(proc, sock=sock)
 
381
        if subproc_sock is not None:
 
382
            subproc_sock.close()
 
383
        return SSHSubprocessConnection(proc, sock=my_sock)
370
384
 
371
385
    def connect_sftp(self, username, password, host, port):
372
386
        try:
403
417
                                  command=None):
404
418
        args = [self.executable_path,
405
419
                '-oForwardX11=no', '-oForwardAgent=no',
406
 
                '-oClearAllForwardings=yes', '-oProtocol=2',
 
420
                '-oClearAllForwardings=yes',
407
421
                '-oNoHostAuthenticationForLocalhost=yes']
408
422
        if port is not None:
409
423
            args.extend(['-p', str(port)])
439
453
register_ssh_vendor('sshcorp', SSHCorpSubprocessVendor())
440
454
 
441
455
 
 
456
class LSHSubprocessVendor(SubprocessVendor):
 
457
    """SSH vendor that uses the 'lsh' executable from GNU"""
 
458
 
 
459
    executable_path = 'lsh'
 
460
 
 
461
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
 
462
                                  command=None):
 
463
        args = [self.executable_path]
 
464
        if port is not None:
 
465
            args.extend(['-p', str(port)])
 
466
        if username is not None:
 
467
            args.extend(['-l', username])
 
468
        if subsystem is not None:
 
469
            args.extend(['--subsystem', subsystem, host])
 
470
        else:
 
471
            args.extend([host] + command)
 
472
        return args
 
473
 
 
474
register_ssh_vendor('lsh', LSHSubprocessVendor())
 
475
 
 
476
 
442
477
class PLinkSubprocessVendor(SubprocessVendor):
443
478
    """SSH vendor that uses the 'plink' executable from Putty."""
444
479
 
471
506
        agent = paramiko.Agent()
472
507
        for key in agent.get_keys():
473
508
            trace.mutter('Trying SSH agent key %s'
474
 
                         % paramiko.util.hexify(key.get_fingerprint()))
 
509
                         % self._hexify(key.get_fingerprint()))
475
510
            try:
476
511
                paramiko_transport.auth_publickey(username, key)
477
512
                return
547
582
        return True
548
583
    except paramiko.PasswordRequiredException:
549
584
        password = ui.ui_factory.get_password(
550
 
            prompt='SSH %(filename)s password', filename=filename)
 
585
            prompt=u'SSH %(filename)s password',
 
586
            filename=filename.decode(osutils._fs_enc))
551
587
        try:
552
588
            key = pkey_class.from_private_key_file(filename, password)
553
589
            paramiko_transport.auth_publickey(username, key)
629
665
import weakref
630
666
_subproc_weakrefs = set()
631
667
 
632
 
def _close_ssh_proc(proc):
 
668
def _close_ssh_proc(proc, sock):
633
669
    """Carefully close stdin/stdout and reap the SSH process.
634
670
 
635
671
    If the pipes are already closed and/or the process has already been
636
672
    wait()ed on, that's ok, and no error is raised.  The goal is to do our best
637
673
    to clean up (whether or not a clean up was already tried).
638
674
    """
639
 
    dotted_names = ['stdin.close', 'stdout.close', 'wait']
640
 
    for dotted_name in dotted_names:
641
 
        attrs = dotted_name.split('.')
642
 
        try:
643
 
            obj = proc
644
 
            for attr in attrs:
645
 
                obj = getattr(obj, attr)
646
 
        except AttributeError:
647
 
            # It's ok for proc.stdin or proc.stdout to be None.
648
 
            continue
649
 
        try:
650
 
            obj()
 
675
    funcs = []
 
676
    for closeable in (proc.stdin, proc.stdout, sock):
 
677
        # We expect that either proc (a subprocess.Popen) will have stdin and
 
678
        # stdout streams to close, or that we will have been passed a socket to
 
679
        # close, with the option not in use being None.
 
680
        if closeable is not None:
 
681
            funcs.append(closeable.close)
 
682
    funcs.append(proc.wait)
 
683
    for func in funcs:
 
684
        try:
 
685
            func()
651
686
        except OSError:
652
687
            # It's ok for the pipe to already be closed, or the process to
653
688
            # already be finished.
692
727
        # to avoid leaving processes lingering indefinitely.
693
728
        def terminate(ref):
694
729
            _subproc_weakrefs.remove(ref)
695
 
            _close_ssh_proc(proc)
 
730
            _close_ssh_proc(proc, sock)
696
731
        _subproc_weakrefs.add(weakref.ref(self, terminate))
697
732
 
698
733
    def send(self, data):
708
743
            return os.read(self.proc.stdout.fileno(), count)
709
744
 
710
745
    def close(self):
711
 
        _close_ssh_proc(self.proc)
 
746
        _close_ssh_proc(self.proc, self._sock)
712
747
 
713
748
    def get_sock_or_pipes(self):
714
749
        if self._sock is not None: