1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 """
20 `.AuthHandler`
21 """
22
23 import weakref
24 from paramiko.common import cMSG_SERVICE_REQUEST, cMSG_DISCONNECT, \
25 DISCONNECT_SERVICE_NOT_AVAILABLE, DISCONNECT_NO_MORE_AUTH_METHODS_AVAILABLE, \
26 cMSG_USERAUTH_REQUEST, cMSG_SERVICE_ACCEPT, DEBUG, AUTH_SUCCESSFUL, INFO, \
27 cMSG_USERAUTH_SUCCESS, cMSG_USERAUTH_FAILURE, AUTH_PARTIALLY_SUCCESSFUL, \
28 cMSG_USERAUTH_INFO_REQUEST, WARNING, AUTH_FAILED, cMSG_USERAUTH_PK_OK, \
29 cMSG_USERAUTH_INFO_RESPONSE, MSG_SERVICE_REQUEST, MSG_SERVICE_ACCEPT, \
30 MSG_USERAUTH_REQUEST, MSG_USERAUTH_SUCCESS, MSG_USERAUTH_FAILURE, \
31 MSG_USERAUTH_BANNER, MSG_USERAUTH_INFO_REQUEST, MSG_USERAUTH_INFO_RESPONSE
32
33 from paramiko.message import Message
34 from paramiko.py3compat import bytestring
35 from paramiko.ssh_exception import SSHException, AuthenticationException, \
36 BadAuthenticationType, PartialAuthentication
37 from paramiko.server import InteractiveQuery
38
39
41 """
42 Internal class to handle the mechanics of authentication.
43 """
44
46 self.transport = weakref.proxy(transport)
47 self.username = None
48 self.authenticated = False
49 self.auth_event = None
50 self.auth_method = ''
51 self.banner = None
52 self.password = None
53 self.private_key = None
54 self.interactive_handler = None
55 self.submethods = None
56
57 self.auth_username = None
58 self.auth_fail_count = 0
59
61 return self.authenticated
62
64 if self.transport.server_mode:
65 return self.auth_username
66 else:
67 return self.username
68
70 self.transport.lock.acquire()
71 try:
72 self.auth_event = event
73 self.auth_method = 'none'
74 self.username = username
75 self._request_auth()
76 finally:
77 self.transport.lock.release()
78
80 self.transport.lock.acquire()
81 try:
82 self.auth_event = event
83 self.auth_method = 'publickey'
84 self.username = username
85 self.private_key = key
86 self._request_auth()
87 finally:
88 self.transport.lock.release()
89
91 self.transport.lock.acquire()
92 try:
93 self.auth_event = event
94 self.auth_method = 'password'
95 self.username = username
96 self.password = password
97 self._request_auth()
98 finally:
99 self.transport.lock.release()
100
102 """
103 response_list = handler(title, instructions, prompt_list)
104 """
105 self.transport.lock.acquire()
106 try:
107 self.auth_event = event
108 self.auth_method = 'keyboard-interactive'
109 self.username = username
110 self.interactive_handler = handler
111 self.submethods = submethods
112 self._request_auth()
113 finally:
114 self.transport.lock.release()
115
117 if self.auth_event is not None:
118 self.auth_event.set()
119
120
121
127
136
145
157
178
190
221
243
256
258 if not self.transport.server_mode:
259
260 m = Message()
261 m.add_byte(cMSG_USERAUTH_FAILURE)
262 m.add_string('none')
263 m.add_boolean(False)
264 self.transport._send_message(m)
265 return
266 if self.authenticated:
267
268 return
269 username = m.get_text()
270 service = m.get_text()
271 method = m.get_text()
272 self.transport._log(DEBUG, 'Auth request (type=%s) service=%s, username=%s' % (method, service, username))
273 if service != 'ssh-connection':
274 self._disconnect_service_not_available()
275 return
276 if (self.auth_username is not None) and (self.auth_username != username):
277 self.transport._log(WARNING, 'Auth rejected because the client attempted to change username in mid-flight')
278 self._disconnect_no_more_auth()
279 return
280 self.auth_username = username
281
282 if method == 'none':
283 result = self.transport.server_object.check_auth_none(username)
284 elif method == 'password':
285 changereq = m.get_boolean()
286 password = m.get_binary()
287 try:
288 password = password.decode('UTF-8')
289 except UnicodeError:
290
291
292 pass
293 if changereq:
294
295
296 self.transport._log(DEBUG, 'Auth request to change passwords (rejected)')
297 newpassword = m.get_binary()
298 try:
299 newpassword = newpassword.decode('UTF-8', 'replace')
300 except UnicodeError:
301 pass
302 result = AUTH_FAILED
303 else:
304 result = self.transport.server_object.check_auth_password(username, password)
305 elif method == 'publickey':
306 sig_attached = m.get_boolean()
307 keytype = m.get_text()
308 keyblob = m.get_binary()
309 try:
310 key = self.transport._key_info[keytype](Message(keyblob))
311 except SSHException as e:
312 self.transport._log(INFO, 'Auth rejected: public key: %s' % str(e))
313 key = None
314 except:
315 self.transport._log(INFO, 'Auth rejected: unsupported or mangled public key')
316 key = None
317 if key is None:
318 self._disconnect_no_more_auth()
319 return
320
321 result = self.transport.server_object.check_auth_publickey(username, key)
322 if result != AUTH_FAILED:
323
324 if not sig_attached:
325
326
327 m = Message()
328 m.add_byte(cMSG_USERAUTH_PK_OK)
329 m.add_string(keytype)
330 m.add_string(keyblob)
331 self.transport._send_message(m)
332 return
333 sig = Message(m.get_binary())
334 blob = self._get_session_blob(key, service, username)
335 if not key.verify_ssh_sig(blob, sig):
336 self.transport._log(INFO, 'Auth rejected: invalid signature')
337 result = AUTH_FAILED
338 elif method == 'keyboard-interactive':
339 lang = m.get_string()
340 submethods = m.get_string()
341 result = self.transport.server_object.check_auth_interactive(username, submethods)
342 if isinstance(result, InteractiveQuery):
343
344 self._interactive_query(result)
345 return
346 else:
347 result = self.transport.server_object.check_auth_none(username)
348
349 self._send_auth_result(username, method, result)
350
352 self.transport._log(INFO, 'Authentication (%s) successful!' % self.auth_method)
353 self.authenticated = True
354 self.transport._auth_trigger()
355 if self.auth_event is not None:
356 self.auth_event.set()
357
375
381
382
401
403 if not self.transport.server_mode:
404 raise SSHException('Illegal info response from server')
405 n = m.get_int()
406 responses = []
407 for i in range(n):
408 responses.append(m.get_text())
409 result = self.transport.server_object.check_auth_interactive_response(responses)
410 if isinstance(type(result), InteractiveQuery):
411
412 self._interactive_query(result)
413 return
414 self._send_auth_result(self.auth_username, 'keyboard-interactive', result)
415
416 _handler_table = {
417 MSG_SERVICE_REQUEST: _parse_service_request,
418 MSG_SERVICE_ACCEPT: _parse_service_accept,
419 MSG_USERAUTH_REQUEST: _parse_userauth_request,
420 MSG_USERAUTH_SUCCESS: _parse_userauth_success,
421 MSG_USERAUTH_FAILURE: _parse_userauth_failure,
422 MSG_USERAUTH_BANNER: _parse_userauth_banner,
423 MSG_USERAUTH_INFO_REQUEST: _parse_userauth_info_request,
424 MSG_USERAUTH_INFO_RESPONSE: _parse_userauth_info_response,
425 }
426