124
119
if 'OpenSSH' in version:
125
mutter('ssh implementation is OpenSSH')
120
trace.mutter('ssh implementation is OpenSSH')
126
121
vendor = OpenSSHSubprocessVendor()
127
122
elif 'SSH Secure Shell' in version:
128
mutter('ssh implementation is SSH Corp.')
123
trace.mutter('ssh implementation is SSH Corp.')
129
124
vendor = SSHCorpSubprocessVendor()
130
125
elif 'plink' in version and args[0] == 'plink':
131
# Checking if "plink" was the executed argument as Windows sometimes
132
# reports 'ssh -V' incorrectly with 'plink' in it's version.
133
# See https://bugs.launchpad.net/bzr/+bug/107155
134
mutter("ssh implementation is Putty's 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.")
135
130
vendor = PLinkSubprocessVendor()
156
151
if vendor is None:
157
152
vendor = self._get_vendor_by_inspection()
158
153
if vendor is None:
159
mutter('falling back to default implementation')
154
trace.mutter('falling back to default implementation')
160
155
vendor = self._default_ssh_vendor
161
156
if vendor is None:
162
raise SSHVendorNotFound()
157
raise errors.SSHVendorNotFound()
163
158
self._cached_ssh_vendor = vendor
164
159
return self._cached_ssh_vendor
229
224
This just unifies all the locations that try to raise ConnectionError,
230
225
so that they format things properly.
232
raise SocketConnectionError(host=host, port=port, msg=msg,
233
orig_error=orig_error)
227
raise errors.SocketConnectionError(host=host, port=port, msg=msg,
228
orig_error=orig_error)
236
231
class LoopbackVendor(SSHVendor):
237
232
"""SSH "vendor" that connects over a plain TCP socket, not SSH."""
239
234
def connect_sftp(self, username, password, host, port):
240
235
sock = socket.socket()
273
268
except (paramiko.SSHException, socket.error), e:
274
269
self._raise_connection_error(host, port=port, orig_error=e)
276
271
server_key = t.get_remote_server_key()
277
272
server_key_hex = paramiko.util.hexify(server_key.get_fingerprint())
278
273
keytype = server_key.get_name()
279
274
if host in SYSTEM_HOSTKEYS and keytype in SYSTEM_HOSTKEYS[host]:
280
275
our_server_key = SYSTEM_HOSTKEYS[host][keytype]
281
our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
276
our_server_key_hex = paramiko.util.hexify(
277
our_server_key.get_fingerprint())
282
278
elif host in BZR_HOSTKEYS and keytype in BZR_HOSTKEYS[host]:
283
279
our_server_key = BZR_HOSTKEYS[host][keytype]
284
our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
280
our_server_key_hex = paramiko.util.hexify(
281
our_server_key.get_fingerprint())
286
warning('Adding %s host key for %s: %s' % (keytype, host, server_key_hex))
283
trace.warning('Adding %s host key for %s: %s'
284
% (keytype, host, server_key_hex))
287
285
add = getattr(BZR_HOSTKEYS, 'add', None)
288
286
if add is not None: # paramiko >= 1.X.X
289
287
BZR_HOSTKEYS.add(host, keytype, server_key)
291
289
BZR_HOSTKEYS.setdefault(host, {})[keytype] = server_key
292
290
our_server_key = server_key
293
our_server_key_hex = paramiko.util.hexify(our_server_key.get_fingerprint())
291
our_server_key_hex = paramiko.util.hexify(
292
our_server_key.get_fingerprint())
295
294
if server_key != our_server_key:
296
295
filename1 = os.path.expanduser('~/.ssh/known_hosts')
297
filename2 = pathjoin(config_dir(), 'ssh_host_keys')
298
raise TransportError('Host keys for %s do not match! %s != %s' % \
296
filename2 = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
297
raise errors.TransportError(
298
'Host keys for %s do not match! %s != %s' %
299
299
(host, our_server_key_hex, server_key_hex),
300
300
['Try editing %s or %s' % (filename1, filename2)])
302
_paramiko_auth(username, password, host, t)
302
_paramiko_auth(username, password, host, port, t)
305
305
def connect_sftp(self, username, password, host, port):
306
306
t = self._connect(username, password, host, port)
454
454
register_ssh_vendor('plink', PLinkSubprocessVendor())
457
def _paramiko_auth(username, password, host, paramiko_transport):
457
def _paramiko_auth(username, password, host, port, paramiko_transport):
458
458
# paramiko requires a username, but it might be none if nothing was supplied
459
459
# use the local username, just in case.
460
460
# We don't override username, because if we aren't using paramiko,
461
461
# the username might be specified in ~/.ssh/config and we don't want to
462
462
# force it to something else
463
463
# Also, it would mess up the self.relpath() functionality
464
username = username or getpass.getuser()
464
auth = config.AuthenticationConfig()
466
username = auth.get_user('ssh', host, port=port)
468
# Default to local user
469
username = getpass.getuser()
466
471
if _use_ssh_agent:
467
472
agent = paramiko.Agent()
468
473
for key in agent.get_keys():
469
mutter('Trying SSH agent key %s' % paramiko.util.hexify(key.get_fingerprint()))
474
trace.mutter('Trying SSH agent key %s'
475
% paramiko.util.hexify(key.get_fingerprint()))
471
477
paramiko_transport.auth_publickey(username, key)
473
479
except paramiko.SSHException, e:
476
482
# okay, try finding id_rsa or id_dss? (posix only)
477
483
if _try_pkey_auth(paramiko_transport, paramiko.RSAKey, username, 'id_rsa'):
489
495
# give up and ask for a password
490
password = bzrlib.ui.ui_factory.get_password(
491
prompt='SSH %(user)s@%(host)s password',
492
user=username, host=host)
496
password = auth.get_password('ssh', host, username, port=port)
494
498
paramiko_transport.auth_password(username, password)
495
499
except paramiko.SSHException, e:
496
raise ConnectionError('Unable to authenticate to SSH host as %s@%s' %
500
raise errors.ConnectionError(
501
'Unable to authenticate to SSH host as %s@%s' % (username, host), e)
500
504
def _try_pkey_auth(paramiko_transport, pkey_class, username, filename):
504
508
paramiko_transport.auth_publickey(username, key)
506
510
except paramiko.PasswordRequiredException:
507
password = bzrlib.ui.ui_factory.get_password(
508
prompt='SSH %(filename)s password',
511
password = ui.ui_factory.get_password(
512
prompt='SSH %(filename)s password', filename=filename)
511
514
key = pkey_class.from_private_key_file(filename, password)
512
515
paramiko_transport.auth_publickey(username, key)
514
517
except paramiko.SSHException:
515
mutter('SSH authentication via %s key failed.' % (os.path.basename(filename),))
518
trace.mutter('SSH authentication via %s key failed.'
519
% (os.path.basename(filename),))
516
520
except paramiko.SSHException:
517
mutter('SSH authentication via %s key failed.' % (os.path.basename(filename),))
521
trace.mutter('SSH authentication via %s key failed.'
522
% (os.path.basename(filename),))
528
533
global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
530
SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(os.path.expanduser('~/.ssh/known_hosts'))
535
SYSTEM_HOSTKEYS = paramiko.util.load_host_keys(
536
os.path.expanduser('~/.ssh/known_hosts'))
531
537
except IOError, e:
532
mutter('failed to load system host keys: ' + str(e))
533
bzr_hostkey_path = pathjoin(config_dir(), 'ssh_host_keys')
538
trace.mutter('failed to load system host keys: ' + str(e))
539
bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
535
541
BZR_HOSTKEYS = paramiko.util.load_host_keys(bzr_hostkey_path)
536
542
except IOError, e:
537
mutter('failed to load bzr host keys: ' + str(e))
543
trace.mutter('failed to load bzr host keys: ' + str(e))
543
549
Save "discovered" host keys in $(config)/ssh_host_keys/.
545
551
global SYSTEM_HOSTKEYS, BZR_HOSTKEYS
546
bzr_hostkey_path = pathjoin(config_dir(), 'ssh_host_keys')
547
ensure_config_dir_exists()
552
bzr_hostkey_path = osutils.pathjoin(config.config_dir(), 'ssh_host_keys')
553
config.ensure_config_dir_exists()
550
556
f = open(bzr_hostkey_path, 'w')