summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--NEWS12
-rw-r--r--paramiko/agent.py10
-rw-r--r--paramiko/client.py7
-rw-r--r--paramiko/config.py4
-rw-r--r--paramiko/hostkeys.py18
-rw-r--r--paramiko/transport.py2
6 files changed, 40 insertions, 13 deletions
diff --git a/NEWS b/NEWS
index 2bb5341f..ef85effe 100644
--- a/NEWS
+++ b/NEWS
@@ -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: