123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778 |
- # Copyright (C) 2013-2014 science + computing ag
- # Author: Sebastian Deiss <sebastian.deiss@t-online.de>
- #
- #
- # This file is part of paramiko.
- #
- # Paramiko is free software; you can redistribute it and/or modify it under the
- # terms of the GNU Lesser General Public License as published by the Free
- # Software Foundation; either version 2.1 of the License, or (at your option)
- # any later version.
- #
- # Paramiko is distributed in the hope that it will be useful, but WITHOUT ANY
- # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
- # A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
- # details.
- #
- # You should have received a copy of the GNU Lesser General Public License
- # along with Paramiko; if not, write to the Free Software Foundation, Inc.,
- # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
- """
- This module provides GSS-API / SSPI authentication as defined in :rfc:`4462`.
- .. note:: Credential delegation is not supported in server mode.
- .. seealso:: :doc:`/api/kex_gss`
- .. versionadded:: 1.15
- """
- import struct
- import os
- import sys
- #: A boolean constraint that indicates if GSS-API / SSPI is available.
- GSS_AUTH_AVAILABLE = True
- #: A tuple of the exception types used by the underlying GSSAPI implementation.
- GSS_EXCEPTIONS = ()
- #: :var str _API: Constraint for the used API
- _API = None
- try:
- import gssapi
- if hasattr(gssapi, "__title__") and gssapi.__title__ == "python-gssapi":
- # old, unmaintained python-gssapi package
- _API = "MIT" # keep this for compatibility
- GSS_EXCEPTIONS = (gssapi.GSSException,)
- else:
- _API = "PYTHON-GSSAPI-NEW"
- GSS_EXCEPTIONS = (
- gssapi.exceptions.GeneralError,
- gssapi.raw.misc.GSSError,
- )
- except (ImportError, OSError):
- try:
- import pywintypes
- import sspicon
- import sspi
- _API = "SSPI"
- GSS_EXCEPTIONS = (pywintypes.error,)
- except ImportError:
- GSS_AUTH_AVAILABLE = False
- _API = None
- from paramiko.common import MSG_USERAUTH_REQUEST
- from paramiko.ssh_exception import SSHException
- from paramiko._version import __version_info__
- def GSSAuth(auth_method, gss_deleg_creds=True):
- """
- Provide SSH2 GSS-API / SSPI authentication.
- :param str auth_method: The name of the SSH authentication mechanism
- (gssapi-with-mic or gss-keyex)
- :param bool gss_deleg_creds: Delegate client credentials or not.
- We delegate credentials by default.
- :return: Either an `._SSH_GSSAPI_OLD` or `._SSH_GSSAPI_NEW` (Unix)
- object or an `_SSH_SSPI` (Windows) object
- :rtype: object
- :raises: ``ImportError`` -- If no GSS-API / SSPI module could be imported.
- :see: `RFC 4462 <http://www.ietf.org/rfc/rfc4462.txt>`_
- :note: Check for the available API and return either an `._SSH_GSSAPI_OLD`
- (MIT GSSAPI using python-gssapi package) object, an
- `._SSH_GSSAPI_NEW` (MIT GSSAPI using gssapi package) object
- or an `._SSH_SSPI` (MS SSPI) object.
- If there is no supported API available,
- ``None`` will be returned.
- """
- if _API == "MIT":
- return _SSH_GSSAPI_OLD(auth_method, gss_deleg_creds)
- elif _API == "PYTHON-GSSAPI-NEW":
- return _SSH_GSSAPI_NEW(auth_method, gss_deleg_creds)
- elif _API == "SSPI" and os.name == "nt":
- return _SSH_SSPI(auth_method, gss_deleg_creds)
- else:
- raise ImportError("Unable to import a GSS-API / SSPI module!")
- class _SSH_GSSAuth(object):
- """
- Contains the shared variables and methods of `._SSH_GSSAPI_OLD`,
- `._SSH_GSSAPI_NEW` and `._SSH_SSPI`.
- """
- def __init__(self, auth_method, gss_deleg_creds):
- """
- :param str auth_method: The name of the SSH authentication mechanism
- (gssapi-with-mic or gss-keyex)
- :param bool gss_deleg_creds: Delegate client credentials or not
- """
- self._auth_method = auth_method
- self._gss_deleg_creds = gss_deleg_creds
- self._gss_host = None
- self._username = None
- self._session_id = None
- self._service = "ssh-connection"
- """
- OpenSSH supports Kerberos V5 mechanism only for GSS-API authentication,
- so we also support the krb5 mechanism only.
- """
- self._krb5_mech = "1.2.840.113554.1.2.2"
- # client mode
- self._gss_ctxt = None
- self._gss_ctxt_status = False
- # server mode
- self._gss_srv_ctxt = None
- self._gss_srv_ctxt_status = False
- self.cc_file = None
- def set_service(self, service):
- """
- This is just a setter to use a non default service.
- I added this method, because RFC 4462 doesn't specify "ssh-connection"
- as the only service value.
- :param str service: The desired SSH service
- """
- if service.find("ssh-"):
- self._service = service
- def set_username(self, username):
- """
- Setter for C{username}. If GSS-API Key Exchange is performed, the
- username is not set by C{ssh_init_sec_context}.
- :param str username: The name of the user who attempts to login
- """
- self._username = username
- def ssh_gss_oids(self, mode="client"):
- """
- This method returns a single OID, because we only support the
- Kerberos V5 mechanism.
- :param str mode: Client for client mode and server for server mode
- :return: A byte sequence containing the number of supported
- OIDs, the length of the OID and the actual OID encoded with
- DER
- :note: In server mode we just return the OID length and the DER encoded
- OID.
- """
- from pyasn1.type.univ import ObjectIdentifier
- from pyasn1.codec.der import encoder
- OIDs = self._make_uint32(1)
- krb5_OID = encoder.encode(ObjectIdentifier(self._krb5_mech))
- OID_len = self._make_uint32(len(krb5_OID))
- if mode == "server":
- return OID_len + krb5_OID
- return OIDs + OID_len + krb5_OID
- def ssh_check_mech(self, desired_mech):
- """
- Check if the given OID is the Kerberos V5 OID (server mode).
- :param str desired_mech: The desired GSS-API mechanism of the client
- :return: ``True`` if the given OID is supported, otherwise C{False}
- """
- from pyasn1.codec.der import decoder
- mech, __ = decoder.decode(desired_mech)
- if mech.__str__() != self._krb5_mech:
- return False
- return True
- # Internals
- # -------------------------------------------------------------------------
- def _make_uint32(self, integer):
- """
- Create a 32 bit unsigned integer (The byte sequence of an integer).
- :param int integer: The integer value to convert
- :return: The byte sequence of an 32 bit integer
- """
- return struct.pack("!I", integer)
- def _ssh_build_mic(self, session_id, username, service, auth_method):
- """
- Create the SSH2 MIC filed for gssapi-with-mic.
- :param str session_id: The SSH session ID
- :param str username: The name of the user who attempts to login
- :param str service: The requested SSH service
- :param str auth_method: The requested SSH authentication mechanism
- :return: The MIC as defined in RFC 4462. The contents of the
- MIC field are:
- string session_identifier,
- byte SSH_MSG_USERAUTH_REQUEST,
- string user-name,
- string service (ssh-connection),
- string authentication-method
- (gssapi-with-mic or gssapi-keyex)
- """
- mic = self._make_uint32(len(session_id))
- mic += session_id
- mic += struct.pack("B", MSG_USERAUTH_REQUEST)
- mic += self._make_uint32(len(username))
- mic += username.encode()
- mic += self._make_uint32(len(service))
- mic += service.encode()
- mic += self._make_uint32(len(auth_method))
- mic += auth_method.encode()
- return mic
- class _SSH_GSSAPI_OLD(_SSH_GSSAuth):
- """
- Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
- using the older (unmaintained) python-gssapi package.
- :see: `.GSSAuth`
- """
- def __init__(self, auth_method, gss_deleg_creds):
- """
- :param str auth_method: The name of the SSH authentication mechanism
- (gssapi-with-mic or gss-keyex)
- :param bool gss_deleg_creds: Delegate client credentials or not
- """
- _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
- if self._gss_deleg_creds:
- self._gss_flags = (
- gssapi.C_PROT_READY_FLAG,
- gssapi.C_INTEG_FLAG,
- gssapi.C_MUTUAL_FLAG,
- gssapi.C_DELEG_FLAG,
- )
- else:
- self._gss_flags = (
- gssapi.C_PROT_READY_FLAG,
- gssapi.C_INTEG_FLAG,
- gssapi.C_MUTUAL_FLAG,
- )
- def ssh_init_sec_context(
- self, target, desired_mech=None, username=None, recv_token=None
- ):
- """
- Initialize a GSS-API context.
- :param str username: The name of the user who attempts to login
- :param str target: The hostname of the target to connect to
- :param str desired_mech: The negotiated GSS-API mechanism
- ("pseudo negotiated" mechanism, because we
- support just the krb5 mechanism :-))
- :param str recv_token: The GSS-API token received from the Server
- :raises:
- `.SSHException` -- Is raised if the desired mechanism of the client
- is not supported
- :return: A ``String`` if the GSS-API has returned a token or
- ``None`` if no token was returned
- """
- from pyasn1.codec.der import decoder
- self._username = username
- self._gss_host = target
- targ_name = gssapi.Name(
- "host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE
- )
- ctx = gssapi.Context()
- ctx.flags = self._gss_flags
- if desired_mech is None:
- krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
- else:
- mech, __ = decoder.decode(desired_mech)
- if mech.__str__() != self._krb5_mech:
- raise SSHException("Unsupported mechanism OID.")
- else:
- krb5_mech = gssapi.OID.mech_from_string(self._krb5_mech)
- token = None
- try:
- if recv_token is None:
- self._gss_ctxt = gssapi.InitContext(
- peer_name=targ_name,
- mech_type=krb5_mech,
- req_flags=ctx.flags,
- )
- token = self._gss_ctxt.step(token)
- else:
- token = self._gss_ctxt.step(recv_token)
- except gssapi.GSSException:
- message = "{} Target: {}".format(sys.exc_info()[1], self._gss_host)
- raise gssapi.GSSException(message)
- self._gss_ctxt_status = self._gss_ctxt.established
- return token
- def ssh_get_mic(self, session_id, gss_kex=False):
- """
- Create the MIC token for a SSH2 message.
- :param str session_id: The SSH session ID
- :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
- :return: gssapi-with-mic:
- Returns the MIC token from GSS-API for the message we created
- with ``_ssh_build_mic``.
- gssapi-keyex:
- Returns the MIC token from GSS-API with the SSH session ID as
- message.
- """
- self._session_id = session_id
- if not gss_kex:
- mic_field = self._ssh_build_mic(
- self._session_id,
- self._username,
- self._service,
- self._auth_method,
- )
- mic_token = self._gss_ctxt.get_mic(mic_field)
- else:
- # for key exchange with gssapi-keyex
- mic_token = self._gss_srv_ctxt.get_mic(self._session_id)
- return mic_token
- def ssh_accept_sec_context(self, hostname, recv_token, username=None):
- """
- Accept a GSS-API context (server mode).
- :param str hostname: The servers hostname
- :param str username: The name of the user who attempts to login
- :param str recv_token: The GSS-API Token received from the server,
- if it's not the initial call.
- :return: A ``String`` if the GSS-API has returned a token or ``None``
- if no token was returned
- """
- # hostname and username are not required for GSSAPI, but for SSPI
- self._gss_host = hostname
- self._username = username
- if self._gss_srv_ctxt is None:
- self._gss_srv_ctxt = gssapi.AcceptContext()
- token = self._gss_srv_ctxt.step(recv_token)
- self._gss_srv_ctxt_status = self._gss_srv_ctxt.established
- return token
- def ssh_check_mic(self, mic_token, session_id, username=None):
- """
- Verify the MIC token for a SSH2 message.
- :param str mic_token: The MIC token received from the client
- :param str session_id: The SSH session ID
- :param str username: The name of the user who attempts to login
- :return: None if the MIC check was successful
- :raises: ``gssapi.GSSException`` -- if the MIC check failed
- """
- self._session_id = session_id
- self._username = username
- if self._username is not None:
- # server mode
- mic_field = self._ssh_build_mic(
- self._session_id,
- self._username,
- self._service,
- self._auth_method,
- )
- self._gss_srv_ctxt.verify_mic(mic_field, mic_token)
- else:
- # for key exchange with gssapi-keyex
- # client mode
- self._gss_ctxt.verify_mic(self._session_id, mic_token)
- @property
- def credentials_delegated(self):
- """
- Checks if credentials are delegated (server mode).
- :return: ``True`` if credentials are delegated, otherwise ``False``
- """
- if self._gss_srv_ctxt.delegated_cred is not None:
- return True
- return False
- def save_client_creds(self, client_token):
- """
- Save the Client token in a file. This is used by the SSH server
- to store the client credentials if credentials are delegated
- (server mode).
- :param str client_token: The GSS-API token received form the client
- :raises:
- ``NotImplementedError`` -- Credential delegation is currently not
- supported in server mode
- """
- raise NotImplementedError
- if __version_info__ < (2, 5):
- # provide the old name for strict backward compatibility
- _SSH_GSSAPI = _SSH_GSSAPI_OLD
- class _SSH_GSSAPI_NEW(_SSH_GSSAuth):
- """
- Implementation of the GSS-API MIT Kerberos Authentication for SSH2,
- using the newer, currently maintained gssapi package.
- :see: `.GSSAuth`
- """
- def __init__(self, auth_method, gss_deleg_creds):
- """
- :param str auth_method: The name of the SSH authentication mechanism
- (gssapi-with-mic or gss-keyex)
- :param bool gss_deleg_creds: Delegate client credentials or not
- """
- _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
- if self._gss_deleg_creds:
- self._gss_flags = (
- gssapi.RequirementFlag.protection_ready,
- gssapi.RequirementFlag.integrity,
- gssapi.RequirementFlag.mutual_authentication,
- gssapi.RequirementFlag.delegate_to_peer,
- )
- else:
- self._gss_flags = (
- gssapi.RequirementFlag.protection_ready,
- gssapi.RequirementFlag.integrity,
- gssapi.RequirementFlag.mutual_authentication,
- )
- def ssh_init_sec_context(
- self, target, desired_mech=None, username=None, recv_token=None
- ):
- """
- Initialize a GSS-API context.
- :param str username: The name of the user who attempts to login
- :param str target: The hostname of the target to connect to
- :param str desired_mech: The negotiated GSS-API mechanism
- ("pseudo negotiated" mechanism, because we
- support just the krb5 mechanism :-))
- :param str recv_token: The GSS-API token received from the Server
- :raises: `.SSHException` -- Is raised if the desired mechanism of the
- client is not supported
- :raises: ``gssapi.exceptions.GSSError`` if there is an error signaled
- by the GSS-API implementation
- :return: A ``String`` if the GSS-API has returned a token or ``None``
- if no token was returned
- """
- from pyasn1.codec.der import decoder
- self._username = username
- self._gss_host = target
- targ_name = gssapi.Name(
- "host@" + self._gss_host,
- name_type=gssapi.NameType.hostbased_service,
- )
- if desired_mech is not None:
- mech, __ = decoder.decode(desired_mech)
- if mech.__str__() != self._krb5_mech:
- raise SSHException("Unsupported mechanism OID.")
- krb5_mech = gssapi.MechType.kerberos
- token = None
- if recv_token is None:
- self._gss_ctxt = gssapi.SecurityContext(
- name=targ_name,
- flags=self._gss_flags,
- mech=krb5_mech,
- usage="initiate",
- )
- token = self._gss_ctxt.step(token)
- else:
- token = self._gss_ctxt.step(recv_token)
- self._gss_ctxt_status = self._gss_ctxt.complete
- return token
- def ssh_get_mic(self, session_id, gss_kex=False):
- """
- Create the MIC token for a SSH2 message.
- :param str session_id: The SSH session ID
- :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not
- :return: gssapi-with-mic:
- Returns the MIC token from GSS-API for the message we created
- with ``_ssh_build_mic``.
- gssapi-keyex:
- Returns the MIC token from GSS-API with the SSH session ID as
- message.
- :rtype: str
- """
- self._session_id = session_id
- if not gss_kex:
- mic_field = self._ssh_build_mic(
- self._session_id,
- self._username,
- self._service,
- self._auth_method,
- )
- mic_token = self._gss_ctxt.get_signature(mic_field)
- else:
- # for key exchange with gssapi-keyex
- mic_token = self._gss_srv_ctxt.get_signature(self._session_id)
- return mic_token
- def ssh_accept_sec_context(self, hostname, recv_token, username=None):
- """
- Accept a GSS-API context (server mode).
- :param str hostname: The servers hostname
- :param str username: The name of the user who attempts to login
- :param str recv_token: The GSS-API Token received from the server,
- if it's not the initial call.
- :return: A ``String`` if the GSS-API has returned a token or ``None``
- if no token was returned
- """
- # hostname and username are not required for GSSAPI, but for SSPI
- self._gss_host = hostname
- self._username = username
- if self._gss_srv_ctxt is None:
- self._gss_srv_ctxt = gssapi.SecurityContext(usage="accept")
- token = self._gss_srv_ctxt.step(recv_token)
- self._gss_srv_ctxt_status = self._gss_srv_ctxt.complete
- return token
- def ssh_check_mic(self, mic_token, session_id, username=None):
- """
- Verify the MIC token for a SSH2 message.
- :param str mic_token: The MIC token received from the client
- :param str session_id: The SSH session ID
- :param str username: The name of the user who attempts to login
- :return: None if the MIC check was successful
- :raises: ``gssapi.exceptions.GSSError`` -- if the MIC check failed
- """
- self._session_id = session_id
- self._username = username
- if self._username is not None:
- # server mode
- mic_field = self._ssh_build_mic(
- self._session_id,
- self._username,
- self._service,
- self._auth_method,
- )
- self._gss_srv_ctxt.verify_signature(mic_field, mic_token)
- else:
- # for key exchange with gssapi-keyex
- # client mode
- self._gss_ctxt.verify_signature(self._session_id, mic_token)
- @property
- def credentials_delegated(self):
- """
- Checks if credentials are delegated (server mode).
- :return: ``True`` if credentials are delegated, otherwise ``False``
- :rtype: bool
- """
- if self._gss_srv_ctxt.delegated_creds is not None:
- return True
- return False
- def save_client_creds(self, client_token):
- """
- Save the Client token in a file. This is used by the SSH server
- to store the client credentials if credentials are delegated
- (server mode).
- :param str client_token: The GSS-API token received form the client
- :raises: ``NotImplementedError`` -- Credential delegation is currently
- not supported in server mode
- """
- raise NotImplementedError
- class _SSH_SSPI(_SSH_GSSAuth):
- """
- Implementation of the Microsoft SSPI Kerberos Authentication for SSH2.
- :see: `.GSSAuth`
- """
- def __init__(self, auth_method, gss_deleg_creds):
- """
- :param str auth_method: The name of the SSH authentication mechanism
- (gssapi-with-mic or gss-keyex)
- :param bool gss_deleg_creds: Delegate client credentials or not
- """
- _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)
- if self._gss_deleg_creds:
- self._gss_flags = (
- sspicon.ISC_REQ_INTEGRITY
- | sspicon.ISC_REQ_MUTUAL_AUTH
- | sspicon.ISC_REQ_DELEGATE
- )
- else:
- self._gss_flags = (
- sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH
- )
- def ssh_init_sec_context(
- self, target, desired_mech=None, username=None, recv_token=None
- ):
- """
- Initialize a SSPI context.
- :param str username: The name of the user who attempts to login
- :param str target: The FQDN of the target to connect to
- :param str desired_mech: The negotiated SSPI mechanism
- ("pseudo negotiated" mechanism, because we
- support just the krb5 mechanism :-))
- :param recv_token: The SSPI token received from the Server
- :raises:
- `.SSHException` -- Is raised if the desired mechanism of the client
- is not supported
- :return: A ``String`` if the SSPI has returned a token or ``None`` if
- no token was returned
- """
- from pyasn1.codec.der import decoder
- self._username = username
- self._gss_host = target
- error = 0
- targ_name = "host/" + self._gss_host
- if desired_mech is not None:
- mech, __ = decoder.decode(desired_mech)
- if mech.__str__() != self._krb5_mech:
- raise SSHException("Unsupported mechanism OID.")
- try:
- if recv_token is None:
- self._gss_ctxt = sspi.ClientAuth(
- "Kerberos", scflags=self._gss_flags, targetspn=targ_name
- )
- error, token = self._gss_ctxt.authorize(recv_token)
- token = token[0].Buffer
- except pywintypes.error as e:
- e.strerror += ", Target: {}".format(self._gss_host)
- raise
- if error == 0:
- """
- if the status is GSS_COMPLETE (error = 0) the context is fully
- established an we can set _gss_ctxt_status to True.
- """
- self._gss_ctxt_status = True
- token = None
- """
- You won't get another token if the context is fully established,
- so i set token to None instead of ""
- """
- return token
- def ssh_get_mic(self, session_id, gss_kex=False):
- """
- Create the MIC token for a SSH2 message.
- :param str session_id: The SSH session ID
- :param bool gss_kex: Generate the MIC for Key Exchange with SSPI or not
- :return: gssapi-with-mic:
- Returns the MIC token from SSPI for the message we created
- with ``_ssh_build_mic``.
- gssapi-keyex:
- Returns the MIC token from SSPI with the SSH session ID as
- message.
- """
- self._session_id = session_id
- if not gss_kex:
- mic_field = self._ssh_build_mic(
- self._session_id,
- self._username,
- self._service,
- self._auth_method,
- )
- mic_token = self._gss_ctxt.sign(mic_field)
- else:
- # for key exchange with gssapi-keyex
- mic_token = self._gss_srv_ctxt.sign(self._session_id)
- return mic_token
- def ssh_accept_sec_context(self, hostname, username, recv_token):
- """
- Accept a SSPI context (server mode).
- :param str hostname: The servers FQDN
- :param str username: The name of the user who attempts to login
- :param str recv_token: The SSPI Token received from the server,
- if it's not the initial call.
- :return: A ``String`` if the SSPI has returned a token or ``None`` if
- no token was returned
- """
- self._gss_host = hostname
- self._username = username
- targ_name = "host/" + self._gss_host
- self._gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=targ_name)
- error, token = self._gss_srv_ctxt.authorize(recv_token)
- token = token[0].Buffer
- if error == 0:
- self._gss_srv_ctxt_status = True
- token = None
- return token
- def ssh_check_mic(self, mic_token, session_id, username=None):
- """
- Verify the MIC token for a SSH2 message.
- :param str mic_token: The MIC token received from the client
- :param str session_id: The SSH session ID
- :param str username: The name of the user who attempts to login
- :return: None if the MIC check was successful
- :raises: ``sspi.error`` -- if the MIC check failed
- """
- self._session_id = session_id
- self._username = username
- if username is not None:
- # server mode
- mic_field = self._ssh_build_mic(
- self._session_id,
- self._username,
- self._service,
- self._auth_method,
- )
- # Verifies data and its signature. If verification fails, an
- # sspi.error will be raised.
- self._gss_srv_ctxt.verify(mic_field, mic_token)
- else:
- # for key exchange with gssapi-keyex
- # client mode
- # Verifies data and its signature. If verification fails, an
- # sspi.error will be raised.
- self._gss_ctxt.verify(self._session_id, mic_token)
- @property
- def credentials_delegated(self):
- """
- Checks if credentials are delegated (server mode).
- :return: ``True`` if credentials are delegated, otherwise ``False``
- """
- return self._gss_flags & sspicon.ISC_REQ_DELEGATE and (
- self._gss_srv_ctxt_status or self._gss_flags
- )
- def save_client_creds(self, client_token):
- """
- Save the Client token in a file. This is used by the SSH server
- to store the client credentails if credentials are delegated
- (server mode).
- :param str client_token: The SSPI token received form the client
- :raises:
- ``NotImplementedError`` -- Credential delegation is currently not
- supported in server mode
- """
- raise NotImplementedError
|