Host key docs (order + tweaks)

This commit is contained in:
Jeff Forcier 2014-02-24 10:08:46 -08:00
parent 1556853713
commit 4bcac18d17
2 changed files with 80 additions and 84 deletions

View File

@ -16,9 +16,6 @@
# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
"""
`.HostKeys`
"""
import base64
import binascii
@ -31,95 +28,21 @@ from paramiko.rsakey import RSAKey
from paramiko.util import get_logger, constant_time_bytes_eq
class InvalidHostKey(Exception):
def __init__(self, line, exc):
self.line = line
self.exc = exc
self.args = (line, exc)
class HostKeyEntry:
"""
Representation of a line in an OpenSSH-style "known hosts" file.
"""
def __init__(self, hostnames=None, key=None):
self.valid = (hostnames is not None) and (key is not None)
self.hostnames = hostnames
self.key = key
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
format used by the openssh known_hosts file.
Lines are expected to not have leading or trailing whitespace.
We don't bother to check for comments or empty lines. All of
that should be taken care of before sending the line to us.
: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.info("Not enough fields found in known_hosts in line %s (%r)" %
(lineno, line))
return None
fields = fields[:3]
names, keytype, key = fields
names = names.split(',')
# Decide what kind of key we're looking at and create an object
# to hold it accordingly.
try:
if keytype == 'ssh-rsa':
key = RSAKey(data=base64.decodestring(key))
elif keytype == 'ssh-dss':
key = DSSKey(data=base64.decodestring(key))
else:
log.info("Unable to handle key of type %s" % (keytype,))
return None
except binascii.Error, e:
raise InvalidHostKey(line, e)
return cls(names, key)
from_line = classmethod(from_line)
def to_line(self):
"""
Returns a string in OpenSSH known_hosts file format, or None if
the object is not in a valid state. A trailing newline is
included.
"""
if self.valid:
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
self.key.get_base64())
return None
def __repr__(self):
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)
class HostKeys (UserDict.DictMixin):
"""
Representation of an openssh-style "known hosts" file. Host keys can be
Representation of an OpenSSH-style "known hosts" file. Host keys can be
read from one or more files, and then individual hosts can be looked up to
verify server keys during SSH negotiation.
A HostKeys object can be treated like a dict; any dict lookup is equivalent
to calling `lookup`.
A `.HostKeys` object can be treated like a dict; any dict lookup is
equivalent to calling `lookup`.
.. versionadded:: 1.5.3
"""
def __init__(self, filename=None):
"""
Create a new HostKeys object, optionally loading keys from an openssh
Create a new HostKeys object, optionally loading keys from an OpenSSH
style host-key file.
:param filename: filename to load host keys from, or ``None``
@ -150,7 +73,7 @@ class HostKeys (UserDict.DictMixin):
def load(self, filename):
"""
Read a file of known SSH host keys, in the format used by openssh.
Read a file of known SSH host keys, in the format used by OpenSSH.
This type of file unfortunately doesn't exist on Windows, but on
posix, it will usually be stored in
``os.path.expanduser("~/.ssh/known_hosts")``.
@ -181,7 +104,7 @@ class HostKeys (UserDict.DictMixin):
def save(self, filename):
"""
Save host keys into a file, in the format used by openssh. The order of
Save host keys into a file, in the format used by OpenSSH. The order of
keys in the file will be preserved when possible (if these keys were
loaded from a file originally). The single exception is that combined
lines will be split into individual key lines, which is arguably a bug.
@ -314,7 +237,7 @@ class HostKeys (UserDict.DictMixin):
def hash_host(hostname, salt=None):
"""
Return a "hashed" form of the hostname, as used by openssh when storing
Return a "hashed" form of the hostname, as used by OpenSSH when storing
hashed hostnames in the known_hosts file.
:param hostname: the hostname to hash
@ -336,3 +259,75 @@ class HostKeys (UserDict.DictMixin):
return hostkey.replace('\n', '')
hash_host = staticmethod(hash_host)
class InvalidHostKey(Exception):
def __init__(self, line, exc):
self.line = line
self.exc = exc
self.args = (line, exc)
class HostKeyEntry:
"""
Representation of a line in an OpenSSH-style "known hosts" file.
"""
def __init__(self, hostnames=None, key=None):
self.valid = (hostnames is not None) and (key is not None)
self.hostnames = hostnames
self.key = key
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
format used by the OpenSSH known_hosts file.
Lines are expected to not have leading or trailing whitespace.
We don't bother to check for comments or empty lines. All of
that should be taken care of before sending the line to us.
: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.info("Not enough fields found in known_hosts in line %s (%r)" %
(lineno, line))
return None
fields = fields[:3]
names, keytype, key = fields
names = names.split(',')
# Decide what kind of key we're looking at and create an object
# to hold it accordingly.
try:
if keytype == 'ssh-rsa':
key = RSAKey(data=base64.decodestring(key))
elif keytype == 'ssh-dss':
key = DSSKey(data=base64.decodestring(key))
else:
log.info("Unable to handle key of type %s" % (keytype,))
return None
except binascii.Error, e:
raise InvalidHostKey(line, e)
return cls(names, key)
from_line = classmethod(from_line)
def to_line(self):
"""
Returns a string in OpenSSH known_hosts file format, or None if
the object is not in a valid state. A trailing newline is
included.
"""
if self.valid:
return '%s %s %s\n' % (','.join(self.hostnames), self.key.get_name(),
self.key.get_base64())
return None
def __repr__(self):
return '<HostKeyEntry %r: %r>' % (self.hostnames, self.key)

View File

@ -2,3 +2,4 @@ Host keys / ``known_hosts`` files
=================================
.. automodule:: paramiko.hostkeys
:member-order: bysource