diff options
-rw-r--r-- | NEWS | 12 | ||||
-rw-r--r-- | paramiko/agent.py | 10 | ||||
-rw-r--r-- | paramiko/client.py | 7 | ||||
-rw-r--r-- | paramiko/config.py | 4 | ||||
-rw-r--r-- | paramiko/hostkeys.py | 18 | ||||
-rw-r--r-- | paramiko/transport.py | 2 |
6 files changed, 40 insertions, 13 deletions
@@ -24,6 +24,18 @@ v1.11.0 (DD MM YYYY) dependent on ctypes for constructing appropriate structures and had ctypes implementations of all functionality. Thanks to Jason R. Coombs for the patch. +* #87: Ensure updates to `known_hosts` files account for any updates to said + files after Paramiko initially read them. (Includes related fix to guard + against duplicate entries during subsequent `known_hosts` loads.) Thanks to + `@sunweaver` for the contribution. + +v1.10.2 (DD MM 2013) +-------------------- + +* #153, #67: Warn on parse failure when reading known_hosts + file. Thanks to `@glasserc` for patch. +* #146: Indentation fixes for readability. Thanks to Abhinav Upadhyay for catch + & patch. v1.10.1 (5th Apr 2013) ---------------------- diff --git a/paramiko/agent.py b/paramiko/agent.py index 1dd30636..d4ff7036 100644 --- a/paramiko/agent.py +++ b/paramiko/agent.py @@ -255,11 +255,11 @@ class AgentServerProxy(AgentSSH): self.close() def connect(self): - conn_sock = self.__t.open_forward_agent_channel() - if conn_sock is None: - raise SSHException('lost ssh-agent') - conn_sock.set_name('auth-agent') - self._connect(conn_sock) + conn_sock = self.__t.open_forward_agent_channel() + if conn_sock is None: + raise SSHException('lost ssh-agent') + conn_sock.set_name('auth-agent') + self._connect(conn_sock) def close(self): """ diff --git a/paramiko/client.py b/paramiko/client.py index 5b719581..493d5481 100644 --- a/paramiko/client.py +++ b/paramiko/client.py @@ -186,8 +186,13 @@ class SSHClient (object): @raise IOError: if the file could not be written """ + + # update local host keys from file (in case other SSH clients + # have written to the known_hosts file meanwhile. + if self.known_hosts is not None: + self.load_host_keys(self.known_hosts) + f = open(filename, 'w') - f.write('# SSH host keys collected by paramiko\n') for hostname, keys in self._host_keys.iteritems(): for keytype, key in keys.iteritems(): f.write('%s %s %s\n' % (hostname, keytype, key.get_base64())) diff --git a/paramiko/config.py b/paramiko/config.py index e41bae43..31caf29e 100644 --- a/paramiko/config.py +++ b/paramiko/config.py @@ -135,7 +135,7 @@ class SSHConfig (object): else: host['config']['identityfile'] = [value] elif key not in host['config']: - host['config'].update({key: value}) + host['config'].update({key: value}) self._config.append(host) def lookup(self, hostname): @@ -252,5 +252,5 @@ class SSHConfig (object): config[k][item] = config[k][item].\ replace(find, str(replace)) else: - config[k] = config[k].replace(find, str(replace)) + config[k] = config[k].replace(find, str(replace)) return config diff --git a/paramiko/hostkeys.py b/paramiko/hostkeys.py index e739312a..f048b715 100644 --- a/paramiko/hostkeys.py +++ b/paramiko/hostkeys.py @@ -28,6 +28,7 @@ import UserDict from paramiko.common import * from paramiko.dsskey import DSSKey from paramiko.rsakey import RSAKey +from paramiko.util import get_logger class InvalidHostKey(Exception): @@ -48,7 +49,7 @@ class HostKeyEntry: self.hostnames = hostnames self.key = key - def from_line(cls, line): + def from_line(cls, line, lineno=None): """ Parses the given line of text to find the names for the host, the type of key, and the key data. The line is expected to be in the @@ -61,9 +62,12 @@ class HostKeyEntry: @param line: a line from an OpenSSH known_hosts file @type line: str """ + log = get_logger('paramiko.hostkeys') fields = line.split(' ') if len(fields) < 3: # Bad number of fields + log.warn("Not enough fields found in known_hosts in line %s (%r)" % + (lineno, line)) return None fields = fields[:3] @@ -78,6 +82,7 @@ class HostKeyEntry: elif keytype == 'ssh-dss': key = DSSKey(data=base64.decodestring(key)) else: + log.warn("Unable to handle key of type %s" % (keytype,)) return None except binascii.Error, e: raise InvalidHostKey(line, e) @@ -160,13 +165,18 @@ class HostKeys (UserDict.DictMixin): @raise IOError: if there was an error reading the file """ f = open(filename, 'r') - for line in f: + for lineno, line in enumerate(f): line = line.strip() if (len(line) == 0) or (line[0] == '#'): continue - e = HostKeyEntry.from_line(line) + e = HostKeyEntry.from_line(line, lineno) if e is not None: - self._entries.append(e) + _hostnames = e.hostnames + for h in _hostnames: + if self.check(h, e.key): + e.hostnames.remove(h) + if len(e.hostnames): + self._entries.append(e) f.close() def save(self, filename): diff --git a/paramiko/transport.py b/paramiko/transport.py index fd6dab76..201a2531 100644 --- a/paramiko/transport.py +++ b/paramiko/transport.py @@ -1439,7 +1439,7 @@ class Transport (threading.Thread): break self.clear_to_send_lock.release() if time.time() > start + self.clear_to_send_timeout: - raise SSHException('Key-exchange timed out waiting for key negotiation') + raise SSHException('Key-exchange timed out waiting for key negotiation') try: self._send_message(data) finally: |