Coverage for /pythoncovmergedfiles/medio/medio/src/paramiko/paramiko/ssh_gss.py: 21%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1# Copyright (C) 2013-2014 science + computing ag
2# Author: Sebastian Deiss <sebastian.deiss@t-online.de>
3#
4#
5# This file is part of paramiko.
6#
7# Paramiko is free software; you can redistribute it and/or modify it under the
8# terms of the GNU Lesser General Public License as published by the Free
9# Software Foundation; either version 2.1 of the License, or (at your option)
10# any later version.
11#
12# Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
13# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
14# A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
15# details.
16#
17# You should have received a copy of the GNU Lesser General Public License
18# along with Paramiko; if not, write to the Free Software Foundation, Inc.,
19# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22"""
23This module provides GSS-API / SSPI authentication as defined in :rfc:`4462`.
25.. note:: Credential delegation is not supported in server mode.
27.. seealso:: :doc:`/api/kex_gss`
29.. versionadded:: 1.15
30"""
32import struct
33import os
34import sys
37#: A boolean constraint that indicates if GSS-API / SSPI is available.
38GSS_AUTH_AVAILABLE = True
41#: A tuple of the exception types used by the underlying GSSAPI implementation.
42GSS_EXCEPTIONS = ()
45#: :var str _API: Constraint for the used API
46_API = None
48try:
49 import gssapi
51 if hasattr(gssapi, "__title__") and gssapi.__title__ == "python-gssapi":
52 # old, unmaintained python-gssapi package
53 _API = "MIT" # keep this for compatibility
54 GSS_EXCEPTIONS = (gssapi.GSSException,)
55 else:
56 _API = "PYTHON-GSSAPI-NEW"
57 GSS_EXCEPTIONS = (
58 gssapi.exceptions.GeneralError,
59 gssapi.raw.misc.GSSError,
60 )
61except (ImportError, OSError):
62 try:
63 import pywintypes
64 import sspicon
65 import sspi
67 _API = "SSPI"
68 GSS_EXCEPTIONS = (pywintypes.error,)
69 except ImportError:
70 GSS_AUTH_AVAILABLE = False
71 _API = None
73from paramiko.common import MSG_USERAUTH_REQUEST
74from paramiko.ssh_exception import SSHException
77def GSSAuth(auth_method, gss_deleg_creds=True):
78 """
79 Provide SSH2 GSS-API / SSPI authentication.
81 :param str auth_method: The name of the SSH authentication mechanism
82 (gssapi-with-mic or gss-keyex)
83 :param bool gss_deleg_creds: Delegate client credentials or not.
84 We delegate credentials by default.
85 :return: Either an `._SSH_GSSAPI_OLD` or `._SSH_GSSAPI_NEW` (Unix)
86 object or an `_SSH_SSPI` (Windows) object
87 :rtype: object
89 :raises: ``ImportError`` -- If no GSS-API / SSPI module could be imported.
91 :see: `RFC 4462 <http://www.ietf.org/rfc/rfc4462.txt>`_
92 :note: Check for the available API and return either an `._SSH_GSSAPI_OLD`
93 (MIT GSSAPI using python-gssapi package) object, an
94 `._SSH_GSSAPI_NEW` (MIT GSSAPI using gssapi package) object
95 or an `._SSH_SSPI` (MS SSPI) object.
96 If there is no supported API available,
97 ``None`` will be returned.
98 """
99 if _API == "MIT":
100 return _SSH_GSSAPI_OLD(auth_method, gss_deleg_creds)
101 elif _API == "PYTHON-GSSAPI-NEW":
102 return _SSH_GSSAPI_NEW(auth_method, gss_deleg_creds)
103 elif _API == "SSPI" and os.name == "nt":
104 return _SSH_SSPI(auth_method, gss_deleg_creds)
105 else:
106 raise ImportError("Unable to import a GSS-API / SSPI module!")
109class _SSH_GSSAuth:
110 """
111 Contains the shared variables and methods of `._SSH_GSSAPI_OLD`,
112 `._SSH_GSSAPI_NEW` and `._SSH_SSPI`.
113 """
115 def __init__(self, auth_method, gss_deleg_creds):
116 """
117 :param str auth_method: The name of the SSH authentication mechanism
118 (gssapi-with-mic or gss-keyex)
119 :param bool gss_deleg_creds: Delegate client credentials or not
120 """
121 self._auth_method = auth_method
122 self._gss_deleg_creds = gss_deleg_creds
123 self._gss_host = None
124 self._username = None
125 self._session_id = None
126 self._service = "ssh-connection"
127 """
128 OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,
129 so we also support the krb5 mechanism only.
130 """
131 self._krb5_mech = "1.2.840.113554.1.2.2"
133 # client mode
134 self._gss_ctxt = None
135 self._gss_ctxt_status = False
137 # server mode
138 self._gss_srv_ctxt = None
139 self._gss_srv_ctxt_status = False
140 self.cc_file = None
142 def set_service(self, service):
143 """
144 This is just a setter to use a non default service.
145 I added this method, because RFC 4462 doesn't specify "ssh-connection"
146 as the only service value.
148 :param str service: The desired SSH service
149 """
150 if service.find("ssh-"):
151 self._service = service
153 def set_username(self, username):
154 """
155 Setter for C{username}. If GSS-API Key Exchange is performed, the
156 username is not set by C{ssh_init_sec_context}.
158 :param str username: The name of the user who attempts to login
159 """
160 self._username = username
162 def ssh_gss_oids(self, mode="client"):
163 """
164 This method returns a single OID, because we only support the
165 Kerberos V5 mechanism.
167 :param str mode: Client for client mode and server for server mode
168 :return: A byte sequence containing the number of supported
169 OIDs, the length of the OID and the actual OID encoded with
170 DER
171 :note: In server mode we just return the OID length and the DER encoded
172 OID.
173 """
174 from pyasn1.type.univ import ObjectIdentifier
175 from pyasn1.codec.der import encoder
177 OIDs = self._make_uint32(1)
178 krb5_OID = encoder.encode(ObjectIdentifier(self._krb5_mech))
179 OID_len = self._make_uint32(len(krb5_OID))
180 if mode == "server":
181 return OID_len + krb5_OID
182 return OIDs + OID_len + krb5_OID
184 def ssh_check_mech(self, desired_mech):
185 """
186 Check if the given OID is the Kerberos V5 OID (server mode).
188 :param str desired_mech: The desired GSS-API mechanism of the client
189 :return: ``True`` if the given OID is supported, otherwise C{False}
190 """
191 from pyasn1.codec.der import decoder
193 mech, __ = decoder.decode(desired_mech)
194 if mech.__str__() != self._krb5_mech:
195 return False
196 return True
198 # Internals
199 # -------------------------------------------------------------------------
200 def _make_uint32(self, integer):
201 """
202 Create a 32 bit unsigned integer (The byte sequence of an integer).
204 :param int integer: The integer value to convert
205 :return: The byte sequence of an 32 bit integer
206 """
207 return struct.pack("!I", integer)
209 def _ssh_build_mic(self, session_id, username, service, auth_method):
210 """
211 Create the SSH2 MIC filed for gssapi-with-mic.
213 :param str session_id: The SSH session ID
214 :param str username: The name of the user who attempts to login
215 :param str service: The requested SSH service
216 :param str auth_method: The requested SSH authentication mechanism
217 :return: The MIC as defined in RFC 4462. The contents of the
218 MIC field are:
219 string session_identifier,
220 byte SSH_MSG_USERAUTH_REQUEST,
221 string user-name,
222 string service (ssh-connection),
223 string authentication-method
224 (gssapi-with-mic or gssapi-keyex)
225 """
226 mic = self._make_uint32(len(session_id))
227 mic += session_id
228 mic += struct.pack("B", MSG_USERAUTH_REQUEST)
229 mic += self._make_uint32(len(username))
230 mic += username.encode()
231 mic += self._make_uint32(len(service))
232 mic += service.encode()
233 mic += self._make_uint32(len(auth_method))
234 mic += auth_method.encode()
235 return mic
238class _SSH_GSSAPI_OLD(_SSH_GSSAuth):
239 """
240 Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
241 using the older (unmaintained) python-gssapi package.
243 :see: `.GSSAuth`
244 """
246 def __init__(self, auth_method, gss_deleg_creds):
247 """
248 :param str auth_method: The name of the SSH authentication mechanism
249 (gssapi-with-mic or gss-keyex)
250 :param bool gss_deleg_creds: Delegate client credentials or not
251 """
252 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
254 if self._gss_deleg_creds:
255 self._gss_flags = (
256 gssapi.C_PROT_READY_FLAG,
257 gssapi.C_INTEG_FLAG,
258 gssapi.C_MUTUAL_FLAG,
259 gssapi.C_DELEG_FLAG,
260 )
261 else:
262 self._gss_flags = (
263 gssapi.C_PROT_READY_FLAG,
264 gssapi.C_INTEG_FLAG,
265 gssapi.C_MUTUAL_FLAG,
266 )
268 def ssh_init_sec_context(
269 self, target, desired_mech=None, username=None, recv_token=None
270 ):
271 """
272 Initialize a GSS-API context.
274 :param str username: The name of the user who attempts to login
275 :param str target: The hostname of the target to connect to
276 :param str desired_mech: The negotiated GSS-API mechanism
277 ("pseudo negotiated" mechanism, because we
278 support just the krb5 mechanism :-))
279 :param str recv_token: The GSS-API token received from the Server
280 :raises:
281 `.SSHException` -- Is raised if the desired mechanism of the client
282 is not supported
283 :return: A ``String`` if the GSS-API has returned a token or
284 ``None`` if no token was returned
285 """
286 from pyasn1.codec.der import decoder
288 self._username = username
289 self._gss_host = target
290 targ_name = gssapi.Name(
291 "host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE
292 )
293 ctx = gssapi.Context()
294 ctx.flags = self._gss_flags
295 if desired_mech is None:
296 krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
297 else:
298 mech, __ = decoder.decode(desired_mech)
299 if mech.__str__() != self._krb5_mech:
300 raise SSHException("Unsupported mechanism OID.")
301 else:
302 krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
303 token = None
304 try:
305 if recv_token is None:
306 self._gss_ctxt = gssapi.InitContext(
307 peer_name=targ_name,
308 mech_type=krb5_mech,
309 req_flags=ctx.flags,
310 )
311 token = self._gss_ctxt.step(token)
312 else:
313 token = self._gss_ctxt.step(recv_token)
314 except gssapi.GSSException:
315 message = "{} Target: {}".format(sys.exc_info()[1], self._gss_host)
316 raise gssapi.GSSException(message)
317 self._gss_ctxt_status = self._gss_ctxt.established
318 return token
320 def ssh_get_mic(self, session_id, gss_kex=False):
321 """
322 Create the MIC token for a SSH2 message.
324 :param str session_id: The SSH session ID
325 :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
326 :return: gssapi-with-mic:
327 Returns the MIC token from GSS-API for the message we created
328 with ``_ssh_build_mic``.
329 gssapi-keyex:
330 Returns the MIC token from GSS-API with the SSH session ID as
331 message.
332 """
333 self._session_id = session_id
334 if not gss_kex:
335 mic_field = self._ssh_build_mic(
336 self._session_id,
337 self._username,
338 self._service,
339 self._auth_method,
340 )
341 mic_token = self._gss_ctxt.get_mic(mic_field)
342 else:
343 # for key exchange with gssapi-keyex
344 mic_token = self._gss_srv_ctxt.get_mic(self._session_id)
345 return mic_token
347 def ssh_accept_sec_context(self, hostname, recv_token, username=None):
348 """
349 Accept a GSS-API context (server mode).
351 :param str hostname: The servers hostname
352 :param str username: The name of the user who attempts to login
353 :param str recv_token: The GSS-API Token received from the server,
354 if it's not the initial call.
355 :return: A ``String`` if the GSS-API has returned a token or ``None``
356 if no token was returned
357 """
358 # hostname and username are not required for GSSAPI, but for SSPI
359 self._gss_host = hostname
360 self._username = username
361 if self._gss_srv_ctxt is None:
362 self._gss_srv_ctxt = gssapi.AcceptContext()
363 token = self._gss_srv_ctxt.step(recv_token)
364 self._gss_srv_ctxt_status = self._gss_srv_ctxt.established
365 return token
367 def ssh_check_mic(self, mic_token, session_id, username=None):
368 """
369 Verify the MIC token for a SSH2 message.
371 :param str mic_token: The MIC token received from the client
372 :param str session_id: The SSH session ID
373 :param str username: The name of the user who attempts to login
374 :return: None if the MIC check was successful
375 :raises: ``gssapi.GSSException`` -- if the MIC check failed
376 """
377 self._session_id = session_id
378 self._username = username
379 if self._username is not None:
380 # server mode
381 mic_field = self._ssh_build_mic(
382 self._session_id,
383 self._username,
384 self._service,
385 self._auth_method,
386 )
387 self._gss_srv_ctxt.verify_mic(mic_field, mic_token)
388 else:
389 # for key exchange with gssapi-keyex
390 # client mode
391 self._gss_ctxt.verify_mic(self._session_id, mic_token)
393 @property
394 def credentials_delegated(self):
395 """
396 Checks if credentials are delegated (server mode).
398 :return: ``True`` if credentials are delegated, otherwise ``False``
399 """
400 if self._gss_srv_ctxt.delegated_cred is not None:
401 return True
402 return False
404 def save_client_creds(self, client_token):
405 """
406 Save the Client token in a file. This is used by the SSH server
407 to store the client credentials if credentials are delegated
408 (server mode).
410 :param str client_token: The GSS-API token received form the client
411 :raises:
412 ``NotImplementedError`` -- Credential delegation is currently not
413 supported in server mode
414 """
415 raise NotImplementedError
418class _SSH_GSSAPI_NEW(_SSH_GSSAuth):
419 """
420 Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
421 using the newer, currently maintained gssapi package.
423 :see: `.GSSAuth`
424 """
426 def __init__(self, auth_method, gss_deleg_creds):
427 """
428 :param str auth_method: The name of the SSH authentication mechanism
429 (gssapi-with-mic or gss-keyex)
430 :param bool gss_deleg_creds: Delegate client credentials or not
431 """
432 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
434 if self._gss_deleg_creds:
435 self._gss_flags = (
436 gssapi.RequirementFlag.protection_ready,
437 gssapi.RequirementFlag.integrity,
438 gssapi.RequirementFlag.mutual_authentication,
439 gssapi.RequirementFlag.delegate_to_peer,
440 )
441 else:
442 self._gss_flags = (
443 gssapi.RequirementFlag.protection_ready,
444 gssapi.RequirementFlag.integrity,
445 gssapi.RequirementFlag.mutual_authentication,
446 )
448 def ssh_init_sec_context(
449 self, target, desired_mech=None, username=None, recv_token=None
450 ):
451 """
452 Initialize a GSS-API context.
454 :param str username: The name of the user who attempts to login
455 :param str target: The hostname of the target to connect to
456 :param str desired_mech: The negotiated GSS-API mechanism
457 ("pseudo negotiated" mechanism, because we
458 support just the krb5 mechanism :-))
459 :param str recv_token: The GSS-API token received from the Server
460 :raises: `.SSHException` -- Is raised if the desired mechanism of the
461 client is not supported
462 :raises: ``gssapi.exceptions.GSSError`` if there is an error signaled
463 by the GSS-API implementation
464 :return: A ``String`` if the GSS-API has returned a token or ``None``
465 if no token was returned
466 """
467 from pyasn1.codec.der import decoder
469 self._username = username
470 self._gss_host = target
471 targ_name = gssapi.Name(
472 "host@" + self._gss_host,
473 name_type=gssapi.NameType.hostbased_service,
474 )
475 if desired_mech is not None:
476 mech, __ = decoder.decode(desired_mech)
477 if mech.__str__() != self._krb5_mech:
478 raise SSHException("Unsupported mechanism OID.")
479 krb5_mech = gssapi.MechType.kerberos
480 token = None
481 if recv_token is None:
482 self._gss_ctxt = gssapi.SecurityContext(
483 name=targ_name,
484 flags=self._gss_flags,
485 mech=krb5_mech,
486 usage="initiate",
487 )
488 token = self._gss_ctxt.step(token)
489 else:
490 token = self._gss_ctxt.step(recv_token)
491 self._gss_ctxt_status = self._gss_ctxt.complete
492 return token
494 def ssh_get_mic(self, session_id, gss_kex=False):
495 """
496 Create the MIC token for a SSH2 message.
498 :param str session_id: The SSH session ID
499 :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
500 :return: gssapi-with-mic:
501 Returns the MIC token from GSS-API for the message we created
502 with ``_ssh_build_mic``.
503 gssapi-keyex:
504 Returns the MIC token from GSS-API with the SSH session ID as
505 message.
506 :rtype: str
507 """
508 self._session_id = session_id
509 if not gss_kex:
510 mic_field = self._ssh_build_mic(
511 self._session_id,
512 self._username,
513 self._service,
514 self._auth_method,
515 )
516 mic_token = self._gss_ctxt.get_signature(mic_field)
517 else:
518 # for key exchange with gssapi-keyex
519 mic_token = self._gss_srv_ctxt.get_signature(self._session_id)
520 return mic_token
522 def ssh_accept_sec_context(self, hostname, recv_token, username=None):
523 """
524 Accept a GSS-API context (server mode).
526 :param str hostname: The servers hostname
527 :param str username: The name of the user who attempts to login
528 :param str recv_token: The GSS-API Token received from the server,
529 if it's not the initial call.
530 :return: A ``String`` if the GSS-API has returned a token or ``None``
531 if no token was returned
532 """
533 # hostname and username are not required for GSSAPI, but for SSPI
534 self._gss_host = hostname
535 self._username = username
536 if self._gss_srv_ctxt is None:
537 self._gss_srv_ctxt = gssapi.SecurityContext(usage="accept")
538 token = self._gss_srv_ctxt.step(recv_token)
539 self._gss_srv_ctxt_status = self._gss_srv_ctxt.complete
540 return token
542 def ssh_check_mic(self, mic_token, session_id, username=None):
543 """
544 Verify the MIC token for a SSH2 message.
546 :param str mic_token: The MIC token received from the client
547 :param str session_id: The SSH session ID
548 :param str username: The name of the user who attempts to login
549 :return: None if the MIC check was successful
550 :raises: ``gssapi.exceptions.GSSError`` -- if the MIC check failed
551 """
552 self._session_id = session_id
553 self._username = username
554 if self._username is not None:
555 # server mode
556 mic_field = self._ssh_build_mic(
557 self._session_id,
558 self._username,
559 self._service,
560 self._auth_method,
561 )
562 self._gss_srv_ctxt.verify_signature(mic_field, mic_token)
563 else:
564 # for key exchange with gssapi-keyex
565 # client mode
566 self._gss_ctxt.verify_signature(self._session_id, mic_token)
568 @property
569 def credentials_delegated(self):
570 """
571 Checks if credentials are delegated (server mode).
573 :return: ``True`` if credentials are delegated, otherwise ``False``
574 :rtype: bool
575 """
576 if self._gss_srv_ctxt.delegated_creds is not None:
577 return True
578 return False
580 def save_client_creds(self, client_token):
581 """
582 Save the Client token in a file. This is used by the SSH server
583 to store the client credentials if credentials are delegated
584 (server mode).
586 :param str client_token: The GSS-API token received form the client
587 :raises: ``NotImplementedError`` -- Credential delegation is currently
588 not supported in server mode
589 """
590 raise NotImplementedError
593class _SSH_SSPI(_SSH_GSSAuth):
594 """
595 Implementation of the Microsoft SSPI Kerberos Authentication for SSH2.
597 :see: `.GSSAuth`
598 """
600 def __init__(self, auth_method, gss_deleg_creds):
601 """
602 :param str auth_method: The name of the SSH authentication mechanism
603 (gssapi-with-mic or gss-keyex)
604 :param bool gss_deleg_creds: Delegate client credentials or not
605 """
606 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
608 if self._gss_deleg_creds:
609 self._gss_flags = (
610 sspicon.ISC_REQ_INTEGRITY
611 | sspicon.ISC_REQ_MUTUAL_AUTH
612 | sspicon.ISC_REQ_DELEGATE
613 )
614 else:
615 self._gss_flags = (
616 sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH
617 )
619 def ssh_init_sec_context(
620 self, target, desired_mech=None, username=None, recv_token=None
621 ):
622 """
623 Initialize a SSPI context.
625 :param str username: The name of the user who attempts to login
626 :param str target: The FQDN of the target to connect to
627 :param str desired_mech: The negotiated SSPI mechanism
628 ("pseudo negotiated" mechanism, because we
629 support just the krb5 mechanism :-))
630 :param recv_token: The SSPI token received from the Server
631 :raises:
632 `.SSHException` -- Is raised if the desired mechanism of the client
633 is not supported
634 :return: A ``String`` if the SSPI has returned a token or ``None`` if
635 no token was returned
636 """
637 from pyasn1.codec.der import decoder
639 self._username = username
640 self._gss_host = target
641 error = 0
642 targ_name = "host/" + self._gss_host
643 if desired_mech is not None:
644 mech, __ = decoder.decode(desired_mech)
645 if mech.__str__() != self._krb5_mech:
646 raise SSHException("Unsupported mechanism OID.")
647 try:
648 if recv_token is None:
649 self._gss_ctxt = sspi.ClientAuth(
650 "Kerberos", scflags=self._gss_flags, targetspn=targ_name
651 )
652 error, token = self._gss_ctxt.authorize(recv_token)
653 token = token[0].Buffer
654 except pywintypes.error as e:
655 e.strerror += ", Target: {}".format(self._gss_host)
656 raise
658 if error == 0:
659 """
660 if the status is GSS_COMPLETE (error = 0) the context is fully
661 established an we can set _gss_ctxt_status to True.
662 """
663 self._gss_ctxt_status = True
664 token = None
665 """
666 You won't get another token if the context is fully established,
667 so i set token to None instead of ""
668 """
669 return token
671 def ssh_get_mic(self, session_id, gss_kex=False):
672 """
673 Create the MIC token for a SSH2 message.
675 :param str session_id: The SSH session ID
676 :param bool gss_kex: Generate the MIC for Key Exchange with SSPI or not
677 :return: gssapi-with-mic:
678 Returns the MIC token from SSPI for the message we created
679 with ``_ssh_build_mic``.
680 gssapi-keyex:
681 Returns the MIC token from SSPI with the SSH session ID as
682 message.
683 """
684 self._session_id = session_id
685 if not gss_kex:
686 mic_field = self._ssh_build_mic(
687 self._session_id,
688 self._username,
689 self._service,
690 self._auth_method,
691 )
692 mic_token = self._gss_ctxt.sign(mic_field)
693 else:
694 # for key exchange with gssapi-keyex
695 mic_token = self._gss_srv_ctxt.sign(self._session_id)
696 return mic_token
698 def ssh_accept_sec_context(self, hostname, username, recv_token):
699 """
700 Accept a SSPI context (server mode).
702 :param str hostname: The servers FQDN
703 :param str username: The name of the user who attempts to login
704 :param str recv_token: The SSPI Token received from the server,
705 if it's not the initial call.
706 :return: A ``String`` if the SSPI has returned a token or ``None`` if
707 no token was returned
708 """
709 self._gss_host = hostname
710 self._username = username
711 targ_name = "host/" + self._gss_host
712 self._gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=targ_name)
713 error, token = self._gss_srv_ctxt.authorize(recv_token)
714 token = token[0].Buffer
715 if error == 0:
716 self._gss_srv_ctxt_status = True
717 token = None
718 return token
720 def ssh_check_mic(self, mic_token, session_id, username=None):
721 """
722 Verify the MIC token for a SSH2 message.
724 :param str mic_token: The MIC token received from the client
725 :param str session_id: The SSH session ID
726 :param str username: The name of the user who attempts to login
727 :return: None if the MIC check was successful
728 :raises: ``sspi.error`` -- if the MIC check failed
729 """
730 self._session_id = session_id
731 self._username = username
732 if username is not None:
733 # server mode
734 mic_field = self._ssh_build_mic(
735 self._session_id,
736 self._username,
737 self._service,
738 self._auth_method,
739 )
740 # Verifies data and its signature. If verification fails, an
741 # sspi.error will be raised.
742 self._gss_srv_ctxt.verify(mic_field, mic_token)
743 else:
744 # for key exchange with gssapi-keyex
745 # client mode
746 # Verifies data and its signature. If verification fails, an
747 # sspi.error will be raised.
748 self._gss_ctxt.verify(self._session_id, mic_token)
750 @property
751 def credentials_delegated(self):
752 """
753 Checks if credentials are delegated (server mode).
755 :return: ``True`` if credentials are delegated, otherwise ``False``
756 """
757 return self._gss_flags & sspicon.ISC_REQ_DELEGATE and (
758 self._gss_srv_ctxt_status or self._gss_flags
759 )
761 def save_client_creds(self, client_token):
762 """
763 Save the Client token in a file. This is used by the SSH server
764 to store the client credentials if credentials are delegated
765 (server mode).
767 :param str client_token: The SSPI token received form the client
768 :raises:
769 ``NotImplementedError`` -- Credential delegation is currently not
770 supported in server mode
771 """
772 raise NotImplementedError