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
30
31 import sshproxy
32 import log
33 import x2go_exceptions
34 import random
35 import string
36
38 """\
39 Policy for making host key information available to Python X2Go after a
40 Paramiko/SSH connect has been attempted. This class needs information
41 about the associated L{X2goSession} instance.
42
43 Once called, the L{missing_host_key} method of this class will try to call
44 L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
45 in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
46 which then will return C{True} by default if not customized in your application.
47
48 To accept host key checks, make sure to either customize the
49 L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
50 method and hook some interactive user dialog to either of them.
51
52 """
53 - def __init__(self, caller=None, session_instance=None):
54 """\
55 @param caller: calling instance
56 @type caller: C{class}
57 @param session_instance: an X2Go session instance
58 @type session_instance: L{X2goSession} instance
59
60 """
61 self.caller = caller
62 self.session_instance = session_instance
63
65 """\
66 Handle a missing host key situation. This method calls
67
68 Once called, the L{missing_host_key} method will try to call
69 L{X2goSession.HOOK_check_host_dialog()}. This hook method---if not re-defined
70 in your application---will then try to call the L{X2goClient.HOOK_check_host_dialog()},
71 which then will return C{True} by default if not customized in your application.
72
73 To accept host key checks, make sure to either customize the
74 L{X2goClient.HOOK_check_host_dialog()} method or the L{X2goSession.HOOK_check_host_dialog()}
75 method and hook some interactive user dialog to either of them.
76
77 @param client: SSH client (C{X2goControlSession*}) instance
78 @type client: C{X2goControlSession*} instance
79 @param hostname: remote hostname
80 @type hostname: C{str}
81 @param key: host key to validate
82 @type key: Paramiko/SSH key instance
83
84 @raise X2goHostKeyException: if the X2Go server host key is not in the C{known_hosts} file
85 @raise X2goSSHProxyHostKeyException: if the SSH proxy host key is not in the C{known_hosts} file
86 @raise SSHException: if this instance does not know its {self.session_instance}
87
88 """
89 self.client = client
90 self.hostname = hostname
91 if (self.hostname.find(']') == -1) and (self.hostname.find(':') == -1):
92
93 self.hostname = '[%s]:22' % self.hostname
94 self.key = key
95 client._log(paramiko.common.DEBUG, 'Interactively Checking %s host key for %s: %s' %
96 (self.key.get_name(), self.hostname, binascii.hexlify(self.key.get_fingerprint())))
97 if self.session_instance:
98 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)
99 _valid = self.session_instance.HOOK_check_host_dialog(self.get_hostname_name(),
100 port=self.get_hostname_port(),
101 fingerprint=self.get_key_fingerprint_with_colons(),
102 fingerprint_type=self.get_key_name(),
103 )
104 if _valid:
105 paramiko.AutoAddPolicy().missing_host_key(client, self.hostname, key)
106 else:
107 if type(self.caller) in (sshproxy.X2goSSHProxy, ):
108 raise x2go_exceptions.X2goSSHProxyHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % hostname)
109 else:
110 raise x2go_exceptions.X2goHostKeyException('Invalid host %s is not authorized for access. Add the host to Paramiko/SSH\'s known_hosts file.' % hostname)
111 else:
112 raise x2go_exceptions.SSHException('Policy has collected host key information on %s for further introspection' % hostname)
113
115 """\
116 Retrieve the Paramiko SSH/Client.
117
118 @return: the associated X2Go control session instance.
119 @rtype: C{X2goControlSession*} instance
120
121 """
122 return self.client
123
125 """\
126 Retrieve the server hostname:port expression of the server to be validated.
127
128 @return: hostname:port
129 @rtype: C{str}
130
131 """
132 return self.hostname
133
135 """\
136 Retrieve the server hostname string of the server to be validated.
137
138 @return: hostname
139 @rtype: C{str}
140
141 """
142 return self.get_hostname().split(':')[0].lstrip('[').rstrip(']')
143
145 """\
146 Retrieve the server port of the server to be validated.
147
148 @return: port
149 @rtype: C{str}
150
151 """
152 return self.get_hostname().split(':')[1]
153
155 """\
156 Retrieve the host key of the server to be validated.
157
158 @return: host key
159 @rtype: Paramiko/SSH key instance
160
161 """
162 return self.key
163
165 """\
166 Retrieve the host key name of the server to be validated.
167
168 @return: host key name (RSA, DSA, ...)
169 @rtype: C{str}
170
171 """
172 return self.key.get_name().upper()
173
175 """\
176 Retrieve the host key fingerprint of the server to be validated.
177
178 @return: host key fingerprint
179 @rtype: C{str}
180
181 """
182 return binascii.hexlify(self.key.get_fingerprint())
183
185 """\
186 Retrieve the (colonized) host key fingerprint of the server
187 to be validated.
188
189 @return: host key fingerprint (with colons)
190 @rtype: C{str}
191
192 """
193 _fingerprint = self.get_key_fingerprint()
194 _colon_fingerprint = ''
195 idx = 0
196 for char in _fingerprint:
197 idx += 1
198 _colon_fingerprint += char
199 if idx % 2 == 0:
200 _colon_fingerprint += ':'
201 return _colon_fingerprint.rstrip(':')
202
203
205 """\
206 Perform a Paramiko/SSH host key check by connecting to the host and
207 validating the results (i.e. by validating raised exceptions during the
208 connect process).
209
210 @param x2go_sshclient_instance: a Paramiko/SSH client instance to be used for testing host key validity.
211 @type x2go_sshclient_instance: C{X2goControlSession*} instance
212 @param hostname: hostname of server to validate
213 @type hostname: C{str}
214 @param port: port of server to validate
215 @type port: C{int}
216
217 @return: returns a tuple with the following components (<host_ok>, <hostname>, <port>, <fingerprint>, <fingerprint_type>)
218 @rtype: C{tuple}
219
220 @raise SSHException: if an SSH exception occurred, that we did not provocate in L{X2goInteractiveAddPolicy.missing_host_key()}
221
222 """
223 _hostname = hostname
224 _port = port
225 _fingerprint = 'NO-FINGERPRINT'
226 _fingerprint_type = 'SOME-KEY-TYPE'
227
228 _check_policy = X2goInteractiveAddPolicy()
229 x2go_sshclient_instance.set_missing_host_key_policy(_check_policy)
230
231 host_ok = False
232 try:
233 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)]))
234 except x2go_exceptions.AuthenticationException:
235 host_ok = True
236 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)
237 except x2go_exceptions.SSHException, e:
238 msg = str(e)
239 if msg.startswith('Policy has collected host key information on '):
240 _hostname = _check_policy.get_hostname().split(':')[0].lstrip('[').rstrip(']')
241 _port = _check_policy.get_hostname().split(':')[1]
242 _fingerprint = _check_policy.get_key_fingerprint_with_colons()
243 _fingerprint_type = _check_policy.get_key_name()
244 else:
245 raise(e)
246 x2go_sshclient_instance.set_missing_host_key_policy(paramiko.RejectPolicy())
247 except:
248
249 pass
250
251 return (host_ok, _hostname, _port, _fingerprint, _fingerprint_type)
252