~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/transport/ssh.py

  • Committer: Danny van Heumen
  • Date: 2010-03-09 21:42:11 UTC
  • mto: (4634.139.5 2.0)
  • mto: This revision was merged to the branch mainline in revision 5160.
  • Revision ID: danny@dannyvanheumen.nl-20100309214211-iqh42x6qcikgd9p3
Reverted now-useless TODO list.

Show diffs side-by-side

added added

removed removed

Lines of Context:
13
13
#
14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
"""Foundation SSH support for SFTP and smart server."""
19
19
 
20
20
import errno
21
21
import getpass
 
22
import logging
22
23
import os
23
24
import socket
24
25
import subprocess
122
123
        elif 'SSH Secure Shell' in version:
123
124
            trace.mutter('ssh implementation is SSH Corp.')
124
125
            vendor = SSHCorpSubprocessVendor()
125
 
        elif 'plink' in version and args[0] == 'plink':
126
 
            # Checking if "plink" was the executed argument as Windows
127
 
            # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
128
 
            # version.  See https://bugs.launchpad.net/bzr/+bug/107155
129
 
            trace.mutter("ssh implementation is Putty's plink.")
130
 
            vendor = PLinkSubprocessVendor()
 
126
        # Auto-detect of plink vendor disabled, on Windows recommended
 
127
        # default ssh-client is paramiko
 
128
        # see https://bugs.launchpad.net/bugs/414743
 
129
        #~elif 'plink' in version and args[0] == 'plink':
 
130
        #~    # Checking if "plink" was the executed argument as Windows
 
131
        #~    # sometimes reports 'ssh -V' incorrectly with 'plink' in it's
 
132
        #~    # version.  See https://bugs.launchpad.net/bzr/+bug/107155
 
133
        #~    trace.mutter("ssh implementation is Putty's plink.")
 
134
        #~    vendor = PLinkSubprocessVendor()
131
135
        return vendor
132
136
 
133
137
    def _get_vendor_by_inspection(self):
180
184
 
181
185
    def get_name(self):
182
186
        return "bzr SocketAsChannelAdapter"
183
 
    
 
187
 
184
188
    def send(self, data):
185
189
        return self.__socket.send(data)
186
190
 
211
215
 
212
216
    def connect_sftp(self, username, password, host, port):
213
217
        """Make an SSH connection, and return an SFTPClient.
214
 
        
 
218
 
215
219
        :param username: an ascii string
216
220
        :param password: an ascii string
217
221
        :param host: a host name as an ascii string
226
230
 
227
231
    def connect_ssh(self, username, password, host, port, command):
228
232
        """Make an SSH connection.
229
 
        
 
233
 
230
234
        :returns: something with a `close` method, and a `get_filelike_channels`
231
235
            method that returns a pair of (read, write) filelike objects.
232
236
        """
391
395
    def _get_vendor_specific_argv(self, username, host, port, subsystem=None,
392
396
                                  command=None):
393
397
        """Returns the argument list to run the subprocess with.
394
 
        
 
398
 
395
399
        Exactly one of 'subsystem' and 'command' must be specified.
396
400
        """
397
401
        raise NotImplementedError(self._get_vendor_specific_argv)
458
462
 
459
463
 
460
464
def _paramiko_auth(username, password, host, port, paramiko_transport):
461
 
    # paramiko requires a username, but it might be none if nothing was supplied
462
 
    # use the local username, just in case.
463
 
    # We don't override username, because if we aren't using paramiko,
464
 
    # the username might be specified in ~/.ssh/config and we don't want to
465
 
    # force it to something else
466
 
    # Also, it would mess up the self.relpath() functionality
467
465
    auth = config.AuthenticationConfig()
 
466
    # paramiko requires a username, but it might be none if nothing was
 
467
    # supplied.  If so, use the local username.
468
468
    if username is None:
469
 
        username = auth.get_user('ssh', host, port=port)
470
 
        if username is None:
471
 
            # Default to local user
472
 
            username = getpass.getuser()
473
 
 
 
469
        username = auth.get_user('ssh', host, port=port,
 
470
                                 default=getpass.getuser())
474
471
    if _use_ssh_agent:
475
472
        agent = paramiko.Agent()
476
473
        for key in agent.get_keys():
488
485
    if _try_pkey_auth(paramiko_transport, paramiko.DSSKey, username, 'id_dsa'):
489
486
        return
490
487
 
 
488
    # If we have gotten this far, we are about to try for passwords, do an
 
489
    # auth_none check to see if it is even supported.
 
490
    supported_auth_types = []
 
491
    try:
 
492
        # Note that with paramiko <1.7.5 this logs an INFO message:
 
493
        #    Authentication type (none) not permitted.
 
494
        # So we explicitly disable the logging level for this action
 
495
        old_level = paramiko_transport.logger.level
 
496
        paramiko_transport.logger.setLevel(logging.WARNING)
 
497
        try:
 
498
            paramiko_transport.auth_none(username)
 
499
        finally:
 
500
            paramiko_transport.logger.setLevel(old_level)
 
501
    except paramiko.BadAuthenticationType, e:
 
502
        # Supported methods are in the exception
 
503
        supported_auth_types = e.allowed_types
 
504
    except paramiko.SSHException, e:
 
505
        # Don't know what happened, but just ignore it
 
506
        pass
 
507
    # We treat 'keyboard-interactive' and 'password' auth methods identically,
 
508
    # because Paramiko's auth_password method will automatically try
 
509
    # 'keyboard-interactive' auth (using the password as the response) if
 
510
    # 'password' auth is not available.  Apparently some Debian and Gentoo
 
511
    # OpenSSH servers require this.
 
512
    # XXX: It's possible for a server to require keyboard-interactive auth that
 
513
    # requires something other than a single password, but we currently don't
 
514
    # support that.
 
515
    if ('password' not in supported_auth_types and
 
516
        'keyboard-interactive' not in supported_auth_types):
 
517
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
 
518
            '\n  %s@%s\nsupported auth types: %s'
 
519
            % (username, host, supported_auth_types))
 
520
 
491
521
    if password:
492
522
        try:
493
523
            paramiko_transport.auth_password(username, password)
497
527
 
498
528
    # give up and ask for a password
499
529
    password = auth.get_password('ssh', host, username, port=port)
500
 
    try:
501
 
        paramiko_transport.auth_password(username, password)
502
 
    except paramiko.SSHException, e:
503
 
        raise errors.ConnectionError(
504
 
            'Unable to authenticate to SSH host as %s@%s' % (username, host), e)
 
530
    # get_password can still return None, which means we should not prompt
 
531
    if password is not None:
 
532
        try:
 
533
            paramiko_transport.auth_password(username, password)
 
534
        except paramiko.SSHException, e:
 
535
            raise errors.ConnectionError(
 
536
                'Unable to authenticate to SSH host as'
 
537
                '\n  %s@%s\n' % (username, host), e)
 
538
    else:
 
539
        raise errors.ConnectionError('Unable to authenticate to SSH host as'
 
540
                                     '  %s@%s' % (username, host))
505
541
 
506
542
 
507
543
def _try_pkey_auth(paramiko_transport, pkey_class, username, filename):
569
605
def os_specific_subprocess_params():
570
606
    """Get O/S specific subprocess parameters."""
571
607
    if sys.platform == 'win32':
572
 
        # setting the process group and closing fds is not supported on 
 
608
        # setting the process group and closing fds is not supported on
573
609
        # win32
574
610
        return {}
575
611
    else:
576
 
        # We close fds other than the pipes as the child process does not need 
 
612
        # We close fds other than the pipes as the child process does not need
577
613
        # them to be open.
578
614
        #
579
615
        # We also set the child process to ignore SIGINT.  Normally the signal
581
617
        # this causes it to be seen only by bzr and not by ssh.  Python will
582
618
        # generate a KeyboardInterrupt in bzr, and we will then have a chance
583
619
        # to release locks or do other cleanup over ssh before the connection
584
 
        # goes away.  
 
620
        # goes away.
585
621
        # <https://launchpad.net/products/bzr/+bug/5987>
586
622
        #
587
623
        # Running it in a separate process group is not good because then it