1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """\
21 Monkey Patch and feature map for Python Paramiko
22
23 """
24
25 import paramiko
26 import re
27 try:
28 from paramiko.config import SSH_PORT
29 except ImportError:
30 SSH_PORT=22
31 import platform
32 from utils import compare_versions
33
34 PARAMIKO_VERSION = paramiko.__version__.split()[0]
35 PARAMIKO_FEATURE = {
36 'forward-ssh-agent': compare_versions(PARAMIKO_VERSION, ">=", '1.8.0') and (platform.system() != "Windows"),
37 'use-compression': compare_versions(PARAMIKO_VERSION, ">=", '1.7.7.1'),
38 'hash-host-entries': compare_versions(PARAMIKO_VERSION, ">=", '99'),
39 'host-entries-reloadable': compare_versions(PARAMIKO_VERSION, ">=", '1.11.0'),
40 'preserve-known-hosts': compare_versions(PARAMIKO_VERSION, ">=", '1.11.0'),
41 'ecdsa-hostkeys': compare_versions(PARAMIKO_VERSION, ">=", '1.11.6'),
42 }
43
45 """\
46 Available since paramiko 1.11.0...
47
48 This method has been taken from SSHClient class in Paramiko and
49 has been improved and adapted to latest SSH implementations.
50
51 Save the host keys back to a file.
52 Only the host keys loaded with
53 L{load_host_keys} (plus any added directly) will be saved -- not any
54 host keys loaded with L{load_system_host_keys}.
55
56 @param filename: the filename to save to
57 @type filename: str
58
59 @raise IOError: if the file could not be written
60
61 """
62
63
64 if self.known_hosts is not None:
65 self.load_host_keys(self.known_hosts)
66
67 f = open(filename, 'w')
68
69 _host_keys = self.get_host_keys()
70 for hostname, keys in _host_keys.iteritems():
71
72 for keytype, key in keys.iteritems():
73 f.write('%s %s %s\n' % (hostname, keytype, key.get_base64()))
74
75 f.close()
76
77
79 """\
80 Available since paramiko 1.11.0...
81
82 Read a file of known SSH host keys, in the format used by openssh.
83 This type of file unfortunately doesn't exist on Windows, but on
84 posix, it will usually be stored in
85 C{os.path.expanduser("~/.ssh/known_hosts")}.
86
87 If this method is called multiple times, the host keys are merged,
88 not cleared. So multiple calls to C{load} will just call L{add},
89 replacing any existing entries and adding new ones.
90
91 @param filename: name of the file to read host keys from
92 @type filename: str
93
94 @raise IOError: if there was an error reading the file
95
96 """
97 f = open(filename, 'r')
98 for line in f:
99 line = line.strip()
100 if (len(line) == 0) or (line[0] == '#'):
101 continue
102 e = paramiko.hostkeys.HostKeyEntry.from_line(line)
103 if e is not None:
104 _hostnames = e.hostnames
105 for h in _hostnames:
106 if self.check(h, e.key):
107 e.hostnames.remove(h)
108 if len(e.hostnames):
109 self._entries.append(e)
110 f.close()
111
112
113 -def _HostKeys_add(self, hostname, keytype, key, hash_hostname=True):
114 """\
115 Add a host key entry to the table. Any existing entry for a
116 C{(hostname, keytype)} pair will be replaced.
117
118 @param hostname: the hostname (or IP) to add
119 @type hostname: str
120 @param keytype: key type (C{"ssh-rsa"}, C{"ssh-dss"} or C{"ecdsa-sha2-nistp256"})
121 @type keytype: str
122 @param key: the key to add
123 @type key: L{PKey}
124
125 """
126
127 if re.match('^\[.*\]\:'+str(SSH_PORT)+'$', hostname):
128
129 hostname = hostname.split(':')[-2].lstrip('[').rstrip(']')
130
131 for e in self._entries:
132 if (hostname in e.hostnames) and (e.key.get_name() == keytype):
133 e.key = key
134 return
135 if not hostname.startswith('|1|') and hash_hostname:
136 hostname = self.hash_host(hostname)
137 self._entries.append(paramiko.hostkeys.HostKeyEntry([hostname], key))
138
139
141 if not PARAMIKO_FEATURE['preserve-known-hosts']:
142 paramiko.SSHClient.save_host_keys = _SSHClient_save_host_keys
143 if not PARAMIKO_FEATURE['host-entries-reloadable']:
144 paramiko.hostkeys.HostKeys.load = _HostKeys_load
145 if not PARAMIKO_FEATURE['hash-host-entries']:
146 paramiko.hostkeys.HostKeys.add = _HostKeys_add
147