1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 """\
21 Providing mechanisms to C{X2goControlSession*} backends for checking host validity.
22
23 """
24 __NAME__ = 'x2gocheckhosts-pylib'
25
26
27 import paramiko
28 import binascii
29 import uuid
30
31
32 import sshproxy
33 import log
34 import x2go_exceptions
35
37 """\
38 Policy for making host key information available to Python X2go after a
39 Paramiko/SSH connect has been attempted. This class needs information
40 about the associated L{X2goSession} instance.
41
42 Once called, the L{missing_host_key} method of this class will try to call
43 L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
44 in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
45 which then will return C{True} by default if not customized in your application.
46
47 To accept host key checks, make sure to either customize the
48 L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
49 method and hook some interactive user dialog to either of them.
50
51 """
52 - def __init__(self, caller=None, session_instance=None):
53 """\
54 @param caller: calling instance
55 @type caller: C{class}
56 @param session_instance: an X2go session instance
57 @type session_instance: L{X2goSession} instance
58
59 """
60 self.caller = caller
61 self.session_instance = session_instance
62
64 """\
65 Handle a missing host key situation. This method calls
66
67 Once called, the L{missing_host_key} method will try to call
68 L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
69 in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
70 which then will return C{True} by default if not customized in your application.
71
72 To accept host key checks, make sure to either customize the
73 L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
74 method and hook some interactive user dialog to either of them.
75
76 @param client: SSH client (C{X2goControlSession*}) instance
77 @type client: C{X2goControlSession*} instance
78 @param hostname: remote hostname
79 @type hostname: C{str}
80 @param key: host key to validate
81 @type key: Paramiko/SSH key instance
82
83 """
84 self.client = client
85 self.hostname = hostname
86 if (self.hostname.find(']') == -1) and (self.hostname.find(':') == -1):
87
88 self.hostname = '[%s]:22' % self.hostname
89 self.key = key
90 client._log(paramiko.common.DEBUG, 'Interactively Checking %s host key for %s: %s' %
91 (self.key.get_name(), self.hostname, binascii.hexlify(self.key.get_fingerprint())))
92 if self.session_instance:
93 self.session_instance.logger('SSH host key verification for host %s with %s fingerprint ,,%s\'\' initiated. We are seeing this X2go server for the first time.' % (self.get_hostname(), self.get_key_name(), self.get_key_fingerprint_with_colons()), loglevel=log.loglevel_NOTICE)
94 _valid = self.session_instance.HOOK_check_host_dialog(self.get_hostname_name(),
95 port=self.get_hostname_port(),
96 fingerprint=self.get_key_fingerprint_with_colons(),
97 fingerprint_type=self.get_key_name(),
98 )
99 if _valid:
100 paramiko.AutoAddPolicy().missing_host_key(client, self.hostname, key)
101 else:
102 if type(self.caller) in (sshproxy.X2goSSHProxy, ):
103 raise x2go_exceptions.X2goSSHProxyHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % hostname)
104 else:
105 raise x2go_exceptions.X2goHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % hostname)
106 else:
107 raise x2go_exceptions.SSHException('Policy has collected host key information on %s for further introspection' % hostname)
108
110 """\
111 Retrieve the Paramiko SSH/Client.
112
113 @return: the associated X2go control session instance.
114 @rtype: C{X2goControlSession*} instance
115
116 """
117 return self.client
118
120 """\
121 Retrieve the server hostname:port expression of the server to be validated.
122
123 @return: hostname:port
124 @rtype: C{str}
125
126 """
127 return self.hostname
128
130 """\
131 Retrieve the server hostname string of the server to be validated.
132
133 @return: hostname
134 @rtype: C{str}
135
136 """
137 return self.get_hostname().split(':')[0].lstrip('[').rstrip(']')
138
140 """\
141 Retrieve the server port of the server to be validated.
142
143 @return: port
144 @rtype: C{str}
145
146 """
147 return self.get_hostname().split(':')[1]
148
150 """\
151 Retrieve the host key of the server to be validated.
152
153 @return: host key
154 @rtype: Paramiko/SSH key instance
155
156 """
157 return self.key
158
160 """\
161 Retrieve the host key name of the server to be validated.
162
163 @return: host key name (RSA, DSA, ...)
164 @rtype: C{str}
165
166 """
167 return self.key.get_name().upper()
168
170 """\
171 Retrieve the host key fingerprint of the server to be validated.
172
173 @return: host key fingerprint
174 @rtype: C{str}
175
176 """
177 return binascii.hexlify(self.key.get_fingerprint())
178
180 """\
181 Retrieve the (colonized) host key fingerprint of the server
182 to be validated.
183
184 @return: host key fingerprint (with colons)
185 @rtype: C{str}
186
187 """
188 _fingerprint = self.get_key_fingerprint()
189 _colon_fingerprint = ''
190 idx = 0
191 for char in _fingerprint:
192 idx += 1
193 _colon_fingerprint += char
194 if idx % 2 == 0:
195 _colon_fingerprint += ':'
196 return _colon_fingerprint.rstrip(':')
197
198
200 """\
201 Perform a Paramiko/SSH host key check by connecting to the host and
202 validating the results (i.e. by validating raised exceptions during the
203 connect process).
204
205 @param x2go_sshclient_instance: a Paramiko/SSH client instance to be used for testing host key validity.
206 @type x2go_sshclient_instance: C{X2goControlSession*} instance
207 @param hostname: hostname of server to validate
208 @type hostname: C{str}
209 @param port: port of server to validate
210 @type port: C{int}
211 @return: returns a tuple with the following components (<host_ok>, <hostname>, <port>, <fingerprint>, <fingerprint_type>)
212 @rtype: C{tuple}
213
214 """
215 _hostname = hostname
216 _port = port
217 _fingerprint = 'NO-FINGERPRINT'
218 _fingerprint_type = 'SOME-KEY-TYPE'
219
220 _check_policy = X2goInteractiveAddPolicy()
221 x2go_sshclient_instance.set_missing_host_key_policy(_check_policy)
222
223 host_ok = False
224 try:
225 paramiko.SSHClient.connect(x2go_sshclient_instance, hostname=hostname, port=port, username='foo', password="".join([random.choice(string.letters+string.digits) for x in range(1, 20)]))
226 except x2go_exceptions.AuthenticationException:
227 host_ok = True
228 x2go_sshclient_instance.logger('SSH host key verification for host [%s]:%s succeeded. Host is already known to the client\'s Paramiko/SSH sub-system.' % (_hostname, _port), loglevel=log.loglevel_NOTICE)
229 except x2go_exceptions.SSHException, e:
230 msg = str(e)
231 if msg.startswith('Policy has collected host key information on '):
232 _hostname = _check_policy.get_hostname().split(':')[0].lstrip('[').rstrip(']')
233 _port = _check_policy.get_hostname().split(':')[1]
234 _fingerprint = _check_policy.get_key_fingerprint_with_colons()
235 _fingerprint_type = _check_policy.get_key_name()
236 else:
237 raise(e)
238 x2go_sshclient_instance.set_missing_host_key_policy(paramiko.RejectPolicy())
239 except:
240
241 pass
242
243 return (host_ok, _hostname, _port, _fingerprint, _fingerprint_type)
244