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
75from paramiko._version import __version_info__
78def GSSAuth(auth_method, gss_deleg_creds=True):
79 """
80 Provide SSH2 GSS-API / SSPI authentication.
82 :param str auth_method: The name of the SSH authentication mechanism
83 (gssapi-with-mic or gss-keyex)
84 :param bool gss_deleg_creds: Delegate client credentials or not.
85 We delegate credentials by default.
86 :return: Either an `._SSH_GSSAPI_OLD` or `._SSH_GSSAPI_NEW` (Unix)
87 object or an `_SSH_SSPI` (Windows) object
88 :rtype: object
90 :raises: ``ImportError`` -- If no GSS-API / SSPI module could be imported.
92 :see: `RFC 4462 <http://www.ietf.org/rfc/rfc4462.txt>`_
93 :note: Check for the available API and return either an `._SSH_GSSAPI_OLD`
94 (MIT GSSAPI using python-gssapi package) object, an
95 `._SSH_GSSAPI_NEW` (MIT GSSAPI using gssapi package) object
96 or an `._SSH_SSPI` (MS SSPI) object.
97 If there is no supported API available,
98 ``None`` will be returned.
99 """
100 if _API == "MIT":
101 return _SSH_GSSAPI_OLD(auth_method, gss_deleg_creds)
102 elif _API == "PYTHON-GSSAPI-NEW":
103 return _SSH_GSSAPI_NEW(auth_method, gss_deleg_creds)
104 elif _API == "SSPI" and os.name == "nt":
105 return _SSH_SSPI(auth_method, gss_deleg_creds)
106 else:
107 raise ImportError("Unable to import a GSS-API / SSPI module!")
110class _SSH_GSSAuth:
111 """
112 Contains the shared variables and methods of `._SSH_GSSAPI_OLD`,
113 `._SSH_GSSAPI_NEW` and `._SSH_SSPI`.
114 """
116 def __init__(self, auth_method, gss_deleg_creds):
117 """
118 :param str auth_method: The name of the SSH authentication mechanism
119 (gssapi-with-mic or gss-keyex)
120 :param bool gss_deleg_creds: Delegate client credentials or not
121 """
122 self._auth_method = auth_method
123 self._gss_deleg_creds = gss_deleg_creds
124 self._gss_host = None
125 self._username = None
126 self._session_id = None
127 self._service = "ssh-connection"
128 """
129 OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,
130 so we also support the krb5 mechanism only.
131 """
132 self._krb5_mech = "1.2.840.113554.1.2.2"
134 # client mode
135 self._gss_ctxt = None
136 self._gss_ctxt_status = False
138 # server mode
139 self._gss_srv_ctxt = None
140 self._gss_srv_ctxt_status = False
141 self.cc_file = None
143 def set_service(self, service):
144 """
145 This is just a setter to use a non default service.
146 I added this method, because RFC 4462 doesn't specify "ssh-connection"
147 as the only service value.
149 :param str service: The desired SSH service
150 """
151 if service.find("ssh-"):
152 self._service = service
154 def set_username(self, username):
155 """
156 Setter for C{username}. If GSS-API Key Exchange is performed, the
157 username is not set by C{ssh_init_sec_context}.
159 :param str username: The name of the user who attempts to login
160 """
161 self._username = username
163 def ssh_gss_oids(self, mode="client"):
164 """
165 This method returns a single OID, because we only support the
166 Kerberos V5 mechanism.
168 :param str mode: Client for client mode and server for server mode
169 :return: A byte sequence containing the number of supported
170 OIDs, the length of the OID and the actual OID encoded with
171 DER
172 :note: In server mode we just return the OID length and the DER encoded
173 OID.
174 """
175 from pyasn1.type.univ import ObjectIdentifier
176 from pyasn1.codec.der import encoder
178 OIDs = self._make_uint32(1)
179 krb5_OID = encoder.encode(ObjectIdentifier(self._krb5_mech))
180 OID_len = self._make_uint32(len(krb5_OID))
181 if mode == "server":
182 return OID_len + krb5_OID
183 return OIDs + OID_len + krb5_OID
185 def ssh_check_mech(self, desired_mech):
186 """
187 Check if the given OID is the Kerberos V5 OID (server mode).
189 :param str desired_mech: The desired GSS-API mechanism of the client
190 :return: ``True`` if the given OID is supported, otherwise C{False}
191 """
192 from pyasn1.codec.der import decoder
194 mech, __ = decoder.decode(desired_mech)
195 if mech.__str__() != self._krb5_mech:
196 return False
197 return True
199 # Internals
200 # -------------------------------------------------------------------------
201 def _make_uint32(self, integer):
202 """
203 Create a 32 bit unsigned integer (The byte sequence of an integer).
205 :param int integer: The integer value to convert
206 :return: The byte sequence of an 32 bit integer
207 """
208 return struct.pack("!I", integer)
210 def _ssh_build_mic(self, session_id, username, service, auth_method):
211 """
212 Create the SSH2 MIC filed for gssapi-with-mic.
214 :param str session_id: The SSH session ID
215 :param str username: The name of the user who attempts to login
216 :param str service: The requested SSH service
217 :param str auth_method: The requested SSH authentication mechanism
218 :return: The MIC as defined in RFC 4462. The contents of the
219 MIC field are:
220 string session_identifier,
221 byte SSH_MSG_USERAUTH_REQUEST,
222 string user-name,
223 string service (ssh-connection),
224 string authentication-method
225 (gssapi-with-mic or gssapi-keyex)
226 """
227 mic = self._make_uint32(len(session_id))
228 mic += session_id
229 mic += struct.pack("B", MSG_USERAUTH_REQUEST)
230 mic += self._make_uint32(len(username))
231 mic += username.encode()
232 mic += self._make_uint32(len(service))
233 mic += service.encode()
234 mic += self._make_uint32(len(auth_method))
235 mic += auth_method.encode()
236 return mic
239class _SSH_GSSAPI_OLD(_SSH_GSSAuth):
240 """
241 Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
242 using the older (unmaintained) python-gssapi package.
244 :see: `.GSSAuth`
245 """
247 def __init__(self, auth_method, gss_deleg_creds):
248 """
249 :param str auth_method: The name of the SSH authentication mechanism
250 (gssapi-with-mic or gss-keyex)
251 :param bool gss_deleg_creds: Delegate client credentials or not
252 """
253 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
255 if self._gss_deleg_creds:
256 self._gss_flags = (
257 gssapi.C_PROT_READY_FLAG,
258 gssapi.C_INTEG_FLAG,
259 gssapi.C_MUTUAL_FLAG,
260 gssapi.C_DELEG_FLAG,
261 )
262 else:
263 self._gss_flags = (
264 gssapi.C_PROT_READY_FLAG,
265 gssapi.C_INTEG_FLAG,
266 gssapi.C_MUTUAL_FLAG,
267 )
269 def ssh_init_sec_context(
270 self, target, desired_mech=None, username=None, recv_token=None
271 ):
272 """
273 Initialize a GSS-API context.
275 :param str username: The name of the user who attempts to login
276 :param str target: The hostname of the target to connect to
277 :param str desired_mech: The negotiated GSS-API mechanism
278 ("pseudo negotiated" mechanism, because we
279 support just the krb5 mechanism :-))
280 :param str recv_token: The GSS-API token received from the Server
281 :raises:
282 `.SSHException` -- Is raised if the desired mechanism of the client
283 is not supported
284 :return: A ``String`` if the GSS-API has returned a token or
285 ``None`` if no token was returned
286 """
287 from pyasn1.codec.der import decoder
289 self._username = username
290 self._gss_host = target
291 targ_name = gssapi.Name(
292 "host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE
293 )
294 ctx = gssapi.Context()
295 ctx.flags = self._gss_flags
296 if desired_mech is None:
297 krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
298 else:
299 mech, __ = decoder.decode(desired_mech)
300 if mech.__str__() != self._krb5_mech:
301 raise SSHException("Unsupported mechanism OID.")
302 else:
303 krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
304 token = None
305 try:
306 if recv_token is None:
307 self._gss_ctxt = gssapi.InitContext(
308 peer_name=targ_name,
309 mech_type=krb5_mech,
310 req_flags=ctx.flags,
311 )
312 token = self._gss_ctxt.step(token)
313 else:
314 token = self._gss_ctxt.step(recv_token)
315 except gssapi.GSSException:
316 message = "{} Target: {}".format(sys.exc_info()[1], self._gss_host)
317 raise gssapi.GSSException(message)
318 self._gss_ctxt_status = self._gss_ctxt.established
319 return token
321 def ssh_get_mic(self, session_id, gss_kex=False):
322 """
323 Create the MIC token for a SSH2 message.
325 :param str session_id: The SSH session ID
326 :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
327 :return: gssapi-with-mic:
328 Returns the MIC token from GSS-API for the message we created
329 with ``_ssh_build_mic``.
330 gssapi-keyex:
331 Returns the MIC token from GSS-API with the SSH session ID as
332 message.
333 """
334 self._session_id = session_id
335 if not gss_kex:
336 mic_field = self._ssh_build_mic(
337 self._session_id,
338 self._username,
339 self._service,
340 self._auth_method,
341 )
342 mic_token = self._gss_ctxt.get_mic(mic_field)
343 else:
344 # for key exchange with gssapi-keyex
345 mic_token = self._gss_srv_ctxt.get_mic(self._session_id)
346 return mic_token
348 def ssh_accept_sec_context(self, hostname, recv_token, username=None):
349 """
350 Accept a GSS-API context (server mode).
352 :param str hostname: The servers hostname
353 :param str username: The name of the user who attempts to login
354 :param str recv_token: The GSS-API Token received from the server,
355 if it's not the initial call.
356 :return: A ``String`` if the GSS-API has returned a token or ``None``
357 if no token was returned
358 """
359 # hostname and username are not required for GSSAPI, but for SSPI
360 self._gss_host = hostname
361 self._username = username
362 if self._gss_srv_ctxt is None:
363 self._gss_srv_ctxt = gssapi.AcceptContext()
364 token = self._gss_srv_ctxt.step(recv_token)
365 self._gss_srv_ctxt_status = self._gss_srv_ctxt.established
366 return token
368 def ssh_check_mic(self, mic_token, session_id, username=None):
369 """
370 Verify the MIC token for a SSH2 message.
372 :param str mic_token: The MIC token received from the client
373 :param str session_id: The SSH session ID
374 :param str username: The name of the user who attempts to login
375 :return: None if the MIC check was successful
376 :raises: ``gssapi.GSSException`` -- if the MIC check failed
377 """
378 self._session_id = session_id
379 self._username = username
380 if self._username is not None:
381 # server mode
382 mic_field = self._ssh_build_mic(
383 self._session_id,
384 self._username,
385 self._service,
386 self._auth_method,
387 )
388 self._gss_srv_ctxt.verify_mic(mic_field, mic_token)
389 else:
390 # for key exchange with gssapi-keyex
391 # client mode
392 self._gss_ctxt.verify_mic(self._session_id, mic_token)
394 @property
395 def credentials_delegated(self):
396 """
397 Checks if credentials are delegated (server mode).
399 :return: ``True`` if credentials are delegated, otherwise ``False``
400 """
401 if self._gss_srv_ctxt.delegated_cred is not None:
402 return True
403 return False
405 def save_client_creds(self, client_token):
406 """
407 Save the Client token in a file. This is used by the SSH server
408 to store the client credentials if credentials are delegated
409 (server mode).
411 :param str client_token: The GSS-API token received form the client
412 :raises:
413 ``NotImplementedError`` -- Credential delegation is currently not
414 supported in server mode
415 """
416 raise NotImplementedError
419if __version_info__ < (2, 5):
420 # provide the old name for strict backward compatibility
421 _SSH_GSSAPI = _SSH_GSSAPI_OLD
424class _SSH_GSSAPI_NEW(_SSH_GSSAuth):
425 """
426 Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
427 using the newer, currently maintained gssapi package.
429 :see: `.GSSAuth`
430 """
432 def __init__(self, auth_method, gss_deleg_creds):
433 """
434 :param str auth_method: The name of the SSH authentication mechanism
435 (gssapi-with-mic or gss-keyex)
436 :param bool gss_deleg_creds: Delegate client credentials or not
437 """
438 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
440 if self._gss_deleg_creds:
441 self._gss_flags = (
442 gssapi.RequirementFlag.protection_ready,
443 gssapi.RequirementFlag.integrity,
444 gssapi.RequirementFlag.mutual_authentication,
445 gssapi.RequirementFlag.delegate_to_peer,
446 )
447 else:
448 self._gss_flags = (
449 gssapi.RequirementFlag.protection_ready,
450 gssapi.RequirementFlag.integrity,
451 gssapi.RequirementFlag.mutual_authentication,
452 )
454 def ssh_init_sec_context(
455 self, target, desired_mech=None, username=None, recv_token=None
456 ):
457 """
458 Initialize a GSS-API context.
460 :param str username: The name of the user who attempts to login
461 :param str target: The hostname of the target to connect to
462 :param str desired_mech: The negotiated GSS-API mechanism
463 ("pseudo negotiated" mechanism, because we
464 support just the krb5 mechanism :-))
465 :param str recv_token: The GSS-API token received from the Server
466 :raises: `.SSHException` -- Is raised if the desired mechanism of the
467 client is not supported
468 :raises: ``gssapi.exceptions.GSSError`` if there is an error signaled
469 by the GSS-API implementation
470 :return: A ``String`` if the GSS-API has returned a token or ``None``
471 if no token was returned
472 """
473 from pyasn1.codec.der import decoder
475 self._username = username
476 self._gss_host = target
477 targ_name = gssapi.Name(
478 "host@" + self._gss_host,
479 name_type=gssapi.NameType.hostbased_service,
480 )
481 if desired_mech is not None:
482 mech, __ = decoder.decode(desired_mech)
483 if mech.__str__() != self._krb5_mech:
484 raise SSHException("Unsupported mechanism OID.")
485 krb5_mech = gssapi.MechType.kerberos
486 token = None
487 if recv_token is None:
488 self._gss_ctxt = gssapi.SecurityContext(
489 name=targ_name,
490 flags=self._gss_flags,
491 mech=krb5_mech,
492 usage="initiate",
493 )
494 token = self._gss_ctxt.step(token)
495 else:
496 token = self._gss_ctxt.step(recv_token)
497 self._gss_ctxt_status = self._gss_ctxt.complete
498 return token
500 def ssh_get_mic(self, session_id, gss_kex=False):
501 """
502 Create the MIC token for a SSH2 message.
504 :param str session_id: The SSH session ID
505 :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
506 :return: gssapi-with-mic:
507 Returns the MIC token from GSS-API for the message we created
508 with ``_ssh_build_mic``.
509 gssapi-keyex:
510 Returns the MIC token from GSS-API with the SSH session ID as
511 message.
512 :rtype: str
513 """
514 self._session_id = session_id
515 if not gss_kex:
516 mic_field = self._ssh_build_mic(
517 self._session_id,
518 self._username,
519 self._service,
520 self._auth_method,
521 )
522 mic_token = self._gss_ctxt.get_signature(mic_field)
523 else:
524 # for key exchange with gssapi-keyex
525 mic_token = self._gss_srv_ctxt.get_signature(self._session_id)
526 return mic_token
528 def ssh_accept_sec_context(self, hostname, recv_token, username=None):
529 """
530 Accept a GSS-API context (server mode).
532 :param str hostname: The servers hostname
533 :param str username: The name of the user who attempts to login
534 :param str recv_token: The GSS-API Token received from the server,
535 if it's not the initial call.
536 :return: A ``String`` if the GSS-API has returned a token or ``None``
537 if no token was returned
538 """
539 # hostname and username are not required for GSSAPI, but for SSPI
540 self._gss_host = hostname
541 self._username = username
542 if self._gss_srv_ctxt is None:
543 self._gss_srv_ctxt = gssapi.SecurityContext(usage="accept")
544 token = self._gss_srv_ctxt.step(recv_token)
545 self._gss_srv_ctxt_status = self._gss_srv_ctxt.complete
546 return token
548 def ssh_check_mic(self, mic_token, session_id, username=None):
549 """
550 Verify the MIC token for a SSH2 message.
552 :param str mic_token: The MIC token received from the client
553 :param str session_id: The SSH session ID
554 :param str username: The name of the user who attempts to login
555 :return: None if the MIC check was successful
556 :raises: ``gssapi.exceptions.GSSError`` -- if the MIC check failed
557 """
558 self._session_id = session_id
559 self._username = username
560 if self._username is not None:
561 # server mode
562 mic_field = self._ssh_build_mic(
563 self._session_id,
564 self._username,
565 self._service,
566 self._auth_method,
567 )
568 self._gss_srv_ctxt.verify_signature(mic_field, mic_token)
569 else:
570 # for key exchange with gssapi-keyex
571 # client mode
572 self._gss_ctxt.verify_signature(self._session_id, mic_token)
574 @property
575 def credentials_delegated(self):
576 """
577 Checks if credentials are delegated (server mode).
579 :return: ``True`` if credentials are delegated, otherwise ``False``
580 :rtype: bool
581 """
582 if self._gss_srv_ctxt.delegated_creds is not None:
583 return True
584 return False
586 def save_client_creds(self, client_token):
587 """
588 Save the Client token in a file. This is used by the SSH server
589 to store the client credentials if credentials are delegated
590 (server mode).
592 :param str client_token: The GSS-API token received form the client
593 :raises: ``NotImplementedError`` -- Credential delegation is currently
594 not supported in server mode
595 """
596 raise NotImplementedError
599class _SSH_SSPI(_SSH_GSSAuth):
600 """
601 Implementation of the Microsoft SSPI Kerberos Authentication for SSH2.
603 :see: `.GSSAuth`
604 """
606 def __init__(self, auth_method, gss_deleg_creds):
607 """
608 :param str auth_method: The name of the SSH authentication mechanism
609 (gssapi-with-mic or gss-keyex)
610 :param bool gss_deleg_creds: Delegate client credentials or not
611 """
612 _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
614 if self._gss_deleg_creds:
615 self._gss_flags = (
616 sspicon.ISC_REQ_INTEGRITY
617 | sspicon.ISC_REQ_MUTUAL_AUTH
618 | sspicon.ISC_REQ_DELEGATE
619 )
620 else:
621 self._gss_flags = (
622 sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH
623 )
625 def ssh_init_sec_context(
626 self, target, desired_mech=None, username=None, recv_token=None
627 ):
628 """
629 Initialize a SSPI context.
631 :param str username: The name of the user who attempts to login
632 :param str target: The FQDN of the target to connect to
633 :param str desired_mech: The negotiated SSPI mechanism
634 ("pseudo negotiated" mechanism, because we
635 support just the krb5 mechanism :-))
636 :param recv_token: The SSPI token received from the Server
637 :raises:
638 `.SSHException` -- Is raised if the desired mechanism of the client
639 is not supported
640 :return: A ``String`` if the SSPI has returned a token or ``None`` if
641 no token was returned
642 """
643 from pyasn1.codec.der import decoder
645 self._username = username
646 self._gss_host = target
647 error = 0
648 targ_name = "host/" + self._gss_host
649 if desired_mech is not None:
650 mech, __ = decoder.decode(desired_mech)
651 if mech.__str__() != self._krb5_mech:
652 raise SSHException("Unsupported mechanism OID.")
653 try:
654 if recv_token is None:
655 self._gss_ctxt = sspi.ClientAuth(
656 "Kerberos", scflags=self._gss_flags, targetspn=targ_name
657 )
658 error, token = self._gss_ctxt.authorize(recv_token)
659 token = token[0].Buffer
660 except pywintypes.error as e:
661 e.strerror += ", Target: {}".format(self._gss_host)
662 raise
664 if error == 0:
665 """
666 if the status is GSS_COMPLETE (error = 0) the context is fully
667 established an we can set _gss_ctxt_status to True.
668 """
669 self._gss_ctxt_status = True
670 token = None
671 """
672 You won't get another token if the context is fully established,
673 so i set token to None instead of ""
674 """
675 return token
677 def ssh_get_mic(self, session_id, gss_kex=False):
678 """
679 Create the MIC token for a SSH2 message.
681 :param str session_id: The SSH session ID
682 :param bool gss_kex: Generate the MIC for Key Exchange with SSPI or not
683 :return: gssapi-with-mic:
684 Returns the MIC token from SSPI for the message we created
685 with ``_ssh_build_mic``.
686 gssapi-keyex:
687 Returns the MIC token from SSPI with the SSH session ID as
688 message.
689 """
690 self._session_id = session_id
691 if not gss_kex:
692 mic_field = self._ssh_build_mic(
693 self._session_id,
694 self._username,
695 self._service,
696 self._auth_method,
697 )
698 mic_token = self._gss_ctxt.sign(mic_field)
699 else:
700 # for key exchange with gssapi-keyex
701 mic_token = self._gss_srv_ctxt.sign(self._session_id)
702 return mic_token
704 def ssh_accept_sec_context(self, hostname, username, recv_token):
705 """
706 Accept a SSPI context (server mode).
708 :param str hostname: The servers FQDN
709 :param str username: The name of the user who attempts to login
710 :param str recv_token: The SSPI Token received from the server,
711 if it's not the initial call.
712 :return: A ``String`` if the SSPI has returned a token or ``None`` if
713 no token was returned
714 """
715 self._gss_host = hostname
716 self._username = username
717 targ_name = "host/" + self._gss_host
718 self._gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=targ_name)
719 error, token = self._gss_srv_ctxt.authorize(recv_token)
720 token = token[0].Buffer
721 if error == 0:
722 self._gss_srv_ctxt_status = True
723 token = None
724 return token
726 def ssh_check_mic(self, mic_token, session_id, username=None):
727 """
728 Verify the MIC token for a SSH2 message.
730 :param str mic_token: The MIC token received from the client
731 :param str session_id: The SSH session ID
732 :param str username: The name of the user who attempts to login
733 :return: None if the MIC check was successful
734 :raises: ``sspi.error`` -- if the MIC check failed
735 """
736 self._session_id = session_id
737 self._username = username
738 if username is not None:
739 # server mode
740 mic_field = self._ssh_build_mic(
741 self._session_id,
742 self._username,
743 self._service,
744 self._auth_method,
745 )
746 # Verifies data and its signature. If verification fails, an
747 # sspi.error will be raised.
748 self._gss_srv_ctxt.verify(mic_field, mic_token)
749 else:
750 # for key exchange with gssapi-keyex
751 # client mode
752 # Verifies data and its signature. If verification fails, an
753 # sspi.error will be raised.
754 self._gss_ctxt.verify(self._session_id, mic_token)
756 @property
757 def credentials_delegated(self):
758 """
759 Checks if credentials are delegated (server mode).
761 :return: ``True`` if credentials are delegated, otherwise ``False``
762 """
763 return self._gss_flags & sspicon.ISC_REQ_DELEGATE and (
764 self._gss_srv_ctxt_status or self._gss_flags
765 )
767 def save_client_creds(self, client_token):
768 """
769 Save the Client token in a file. This is used by the SSH server
770 to store the client credentails if credentials are delegated
771 (server mode).
773 :param str client_token: The SSPI token received form the client
774 :raises:
775 ``NotImplementedError`` -- Credential delegation is currently not
776 supported in server mode
777 """
778 raise NotImplementedError