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