Team Ai
Datasetpublic

codekingpro/portable-devtools

sourceHugging Faceupdated 5mo agoView on Hugging Face
1likes14kdownloads
ssh_gss.py779 linesDownload Raw Back to paramiko
1# Copyright (C) 2013-2014 science + computing ag2# 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 the8# terms of the GNU Lesser General Public License as published by the Free9# 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 ANY13# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR14# A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more15# details.16#17# You should have received a copy of the GNU Lesser General Public License18# along with Paramiko; if not, write to the Free Software Foundation, Inc.,19# 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA.20 21 22"""23This module provides GSS-API / SSPI  authentication as defined in :rfc:`4462`.24 25.. note:: Credential delegation is not supported in server mode.26 27.. seealso:: :doc:`/api/kex_gss`28 29.. versionadded:: 1.1530"""31 32import struct33import os34import sys35 36 37#: A boolean constraint that indicates if GSS-API / SSPI is available.38GSS_AUTH_AVAILABLE = True39 40 41#: A tuple of the exception types used by the underlying GSSAPI implementation.42GSS_EXCEPTIONS = ()43 44 45#: :var str _API: Constraint for the used API46_API = None47 48try:49    import gssapi50 51    if hasattr(gssapi, "__title__") and gssapi.__title__ == "python-gssapi":52        # old, unmaintained python-gssapi package53        _API = "MIT"  # keep this for compatibility54        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 pywintypes64        import sspicon65        import sspi66 67        _API = "SSPI"68        GSS_EXCEPTIONS = (pywintypes.error,)69    except ImportError:70        GSS_AUTH_AVAILABLE = False71        _API = None72 73from paramiko.common import MSG_USERAUTH_REQUEST74from paramiko.ssh_exception import SSHException75from paramiko._version import __version_info__76 77 78def GSSAuth(auth_method, gss_deleg_creds=True):79    """80    Provide SSH2 GSS-API / SSPI authentication.81 82    :param str auth_method: The name of the SSH authentication mechanism83                            (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) object88    :rtype: object89 90    :raises: ``ImportError`` -- If no GSS-API / SSPI module could be imported.91 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, an95           `._SSH_GSSAPI_NEW` (MIT GSSAPI using gssapi package) object96           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!")108 109 110class _SSH_GSSAuth:111    """112    Contains the shared variables and methods of `._SSH_GSSAPI_OLD`,113    `._SSH_GSSAPI_NEW` and `._SSH_SSPI`.114    """115 116    def __init__(self, auth_method, gss_deleg_creds):117        """118        :param str auth_method: The name of the SSH authentication mechanism119                                (gssapi-with-mic or gss-keyex)120        :param bool gss_deleg_creds: Delegate client credentials or not121        """122        self._auth_method = auth_method123        self._gss_deleg_creds = gss_deleg_creds124        self._gss_host = None125        self._username = None126        self._session_id = None127        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"133 134        # client mode135        self._gss_ctxt = None136        self._gss_ctxt_status = False137 138        # server mode139        self._gss_srv_ctxt = None140        self._gss_srv_ctxt_status = False141        self.cc_file = None142 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.148 149        :param str service: The desired SSH service150        """151        if service.find("ssh-"):152            self._service = service153 154    def set_username(self, username):155        """156        Setter for C{username}. If GSS-API Key Exchange is performed, the157        username is not set by C{ssh_init_sec_context}.158 159        :param str username: The name of the user who attempts to login160        """161        self._username = username162 163    def ssh_gss_oids(self, mode="client"):164        """165        This method returns a single OID, because we only support the166        Kerberos V5 mechanism.167 168        :param str mode: Client for client mode and server for server mode169        :return: A byte sequence containing the number of supported170                 OIDs, the length of the OID and the actual OID encoded with171                 DER172        :note: In server mode we just return the OID length and the DER encoded173               OID.174        """175        from pyasn1.type.univ import ObjectIdentifier176        from pyasn1.codec.der import encoder177 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_OID183        return OIDs + OID_len + krb5_OID184 185    def ssh_check_mech(self, desired_mech):186        """187        Check if the given OID is the Kerberos V5 OID (server mode).188 189        :param str desired_mech: The desired GSS-API mechanism of the client190        :return: ``True`` if the given OID is supported, otherwise C{False}191        """192        from pyasn1.codec.der import decoder193 194        mech, __ = decoder.decode(desired_mech)195        if mech.__str__() != self._krb5_mech:196            return False197        return True198 199    # Internals200    # -------------------------------------------------------------------------201    def _make_uint32(self, integer):202        """203        Create a 32 bit unsigned integer (The byte sequence of an integer).204 205        :param int integer: The integer value to convert206        :return: The byte sequence of an 32 bit integer207        """208        return struct.pack("!I", integer)209 210    def _ssh_build_mic(self, session_id, username, service, auth_method):211        """212        Create the SSH2 MIC filed for gssapi-with-mic.213 214        :param str session_id: The SSH session ID215        :param str username: The name of the user who attempts to login216        :param str service: The requested SSH service217        :param str auth_method: The requested SSH authentication mechanism218        :return: The MIC as defined in RFC 4462. The contents of the219                 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-method225                           (gssapi-with-mic or gssapi-keyex)226        """227        mic = self._make_uint32(len(session_id))228        mic += session_id229        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 mic237 238 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.243 244    :see: `.GSSAuth`245    """246 247    def __init__(self, auth_method, gss_deleg_creds):248        """249        :param str auth_method: The name of the SSH authentication mechanism250                                (gssapi-with-mic or gss-keyex)251        :param bool gss_deleg_creds: Delegate client credentials or not252        """253        _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)254 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            )268 269    def ssh_init_sec_context(270        self, target, desired_mech=None, username=None, recv_token=None271    ):272        """273        Initialize a GSS-API context.274 275        :param str username: The name of the user who attempts to login276        :param str target: The hostname of the target to connect to277        :param str desired_mech: The negotiated GSS-API mechanism278                                 ("pseudo negotiated" mechanism, because we279                                 support just the krb5 mechanism :-))280        :param str recv_token: The GSS-API token received from the Server281        :raises:282            `.SSHException` -- Is raised if the desired mechanism of the client283            is not supported284        :return: A ``String`` if the GSS-API has returned a token or285            ``None`` if no token was returned286        """287        from pyasn1.codec.der import decoder288 289        self._username = username290        self._gss_host = target291        targ_name = gssapi.Name(292            "host@" + self._gss_host, gssapi.C_NT_HOSTBASED_SERVICE293        )294        ctx = gssapi.Context()295        ctx.flags = self._gss_flags296        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 = None305        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.established319        return token320 321    def ssh_get_mic(self, session_id, gss_kex=False):322        """323        Create the MIC token for a SSH2 message.324 325        :param str session_id: The SSH session ID326        :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not327        :return: gssapi-with-mic:328                 Returns the MIC token from GSS-API for the message we created329                 with ``_ssh_build_mic``.330                 gssapi-keyex:331                 Returns the MIC token from GSS-API with the SSH session ID as332                 message.333        """334        self._session_id = session_id335        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-keyex345            mic_token = self._gss_srv_ctxt.get_mic(self._session_id)346        return mic_token347 348    def ssh_accept_sec_context(self, hostname, recv_token, username=None):349        """350        Accept a GSS-API context (server mode).351 352        :param str hostname: The servers hostname353        :param str username: The name of the user who attempts to login354        :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 returned358        """359        # hostname and username are not required for GSSAPI, but for SSPI360        self._gss_host = hostname361        self._username = username362        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.established366        return token367 368    def ssh_check_mic(self, mic_token, session_id, username=None):369        """370        Verify the MIC token for a SSH2 message.371 372        :param str mic_token: The MIC token received from the client373        :param str session_id: The SSH session ID374        :param str username: The name of the user who attempts to login375        :return: None if the MIC check was successful376        :raises: ``gssapi.GSSException`` -- if the MIC check failed377        """378        self._session_id = session_id379        self._username = username380        if self._username is not None:381            # server mode382            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-keyex391            # client mode392            self._gss_ctxt.verify_mic(self._session_id, mic_token)393 394    @property395    def credentials_delegated(self):396        """397        Checks if credentials are delegated (server mode).398 399        :return: ``True`` if credentials are delegated, otherwise ``False``400        """401        if self._gss_srv_ctxt.delegated_cred is not None:402            return True403        return False404 405    def save_client_creds(self, client_token):406        """407        Save the Client token in a file. This is used by the SSH server408        to store the client credentials if credentials are delegated409        (server mode).410 411        :param str client_token: The GSS-API token received form the client412        :raises:413            ``NotImplementedError`` -- Credential delegation is currently not414            supported in server mode415        """416        raise NotImplementedError417 418 419if __version_info__ < (2, 5):420    # provide the old name for strict backward compatibility421    _SSH_GSSAPI = _SSH_GSSAPI_OLD422 423 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.428 429    :see: `.GSSAuth`430    """431 432    def __init__(self, auth_method, gss_deleg_creds):433        """434        :param str auth_method: The name of the SSH authentication mechanism435                                (gssapi-with-mic or gss-keyex)436        :param bool gss_deleg_creds: Delegate client credentials or not437        """438        _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)439 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            )453 454    def ssh_init_sec_context(455        self, target, desired_mech=None, username=None, recv_token=None456    ):457        """458        Initialize a GSS-API context.459 460        :param str username: The name of the user who attempts to login461        :param str target: The hostname of the target to connect to462        :param str desired_mech: The negotiated GSS-API mechanism463                                 ("pseudo negotiated" mechanism, because we464                                 support just the krb5 mechanism :-))465        :param str recv_token: The GSS-API token received from the Server466        :raises: `.SSHException` -- Is raised if the desired mechanism of the467                 client is not supported468        :raises: ``gssapi.exceptions.GSSError`` if there is an error signaled469                                                by the GSS-API implementation470        :return: A ``String`` if the GSS-API has returned a token or ``None``471                 if no token was returned472        """473        from pyasn1.codec.der import decoder474 475        self._username = username476        self._gss_host = target477        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.kerberos486        token = None487        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.complete498        return token499 500    def ssh_get_mic(self, session_id, gss_kex=False):501        """502        Create the MIC token for a SSH2 message.503 504        :param str session_id: The SSH session ID505        :param bool gss_kex: Generate the MIC for GSS-API Key Exchange or not506        :return: gssapi-with-mic:507                 Returns the MIC token from GSS-API for the message we created508                 with ``_ssh_build_mic``.509                 gssapi-keyex:510                 Returns the MIC token from GSS-API with the SSH session ID as511                 message.512        :rtype: str513        """514        self._session_id = session_id515        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-keyex525            mic_token = self._gss_srv_ctxt.get_signature(self._session_id)526        return mic_token527 528    def ssh_accept_sec_context(self, hostname, recv_token, username=None):529        """530        Accept a GSS-API context (server mode).531 532        :param str hostname: The servers hostname533        :param str username: The name of the user who attempts to login534        :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 returned538        """539        # hostname and username are not required for GSSAPI, but for SSPI540        self._gss_host = hostname541        self._username = username542        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.complete546        return token547 548    def ssh_check_mic(self, mic_token, session_id, username=None):549        """550        Verify the MIC token for a SSH2 message.551 552        :param str mic_token: The MIC token received from the client553        :param str session_id: The SSH session ID554        :param str username: The name of the user who attempts to login555        :return: None if the MIC check was successful556        :raises: ``gssapi.exceptions.GSSError`` -- if the MIC check failed557        """558        self._session_id = session_id559        self._username = username560        if self._username is not None:561            # server mode562            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-keyex571            # client mode572            self._gss_ctxt.verify_signature(self._session_id, mic_token)573 574    @property575    def credentials_delegated(self):576        """577        Checks if credentials are delegated (server mode).578 579        :return: ``True`` if credentials are delegated, otherwise ``False``580        :rtype: bool581        """582        if self._gss_srv_ctxt.delegated_creds is not None:583            return True584        return False585 586    def save_client_creds(self, client_token):587        """588        Save the Client token in a file. This is used by the SSH server589        to store the client credentials if credentials are delegated590        (server mode).591 592        :param str client_token: The GSS-API token received form the client593        :raises: ``NotImplementedError`` -- Credential delegation is currently594                 not supported in server mode595        """596        raise NotImplementedError597 598 599class _SSH_SSPI(_SSH_GSSAuth):600    """601    Implementation of the Microsoft SSPI Kerberos Authentication for SSH2.602 603    :see: `.GSSAuth`604    """605 606    def __init__(self, auth_method, gss_deleg_creds):607        """608        :param str auth_method: The name of the SSH authentication mechanism609                                (gssapi-with-mic or gss-keyex)610        :param bool gss_deleg_creds: Delegate client credentials or not611        """612        _SSH_GSSAuth.__init__(self, auth_method, gss_deleg_creds)613 614        if self._gss_deleg_creds:615            self._gss_flags = (616                sspicon.ISC_REQ_INTEGRITY617                | sspicon.ISC_REQ_MUTUAL_AUTH618                | sspicon.ISC_REQ_DELEGATE619            )620        else:621            self._gss_flags = (622                sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_MUTUAL_AUTH623            )624 625    def ssh_init_sec_context(626        self, target, desired_mech=None, username=None, recv_token=None627    ):628        """629        Initialize a SSPI context.630 631        :param str username: The name of the user who attempts to login632        :param str target: The FQDN of the target to connect to633        :param str desired_mech: The negotiated SSPI mechanism634                                 ("pseudo negotiated" mechanism, because we635                                 support just the krb5 mechanism :-))636        :param recv_token: The SSPI token received from the Server637        :raises:638            `.SSHException` -- Is raised if the desired mechanism of the client639            is not supported640        :return: A ``String`` if the SSPI has returned a token or ``None`` if641                 no token was returned642        """643        from pyasn1.codec.der import decoder644 645        self._username = username646        self._gss_host = target647        error = 0648        targ_name = "host/" + self._gss_host649        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_name657                )658            error, token = self._gss_ctxt.authorize(recv_token)659            token = token[0].Buffer660        except pywintypes.error as e:661            e.strerror += ", Target: {}".format(self._gss_host)662            raise663 664        if error == 0:665            """666            if the status is GSS_COMPLETE (error = 0) the context is fully667            established an we can set _gss_ctxt_status to True.668            """669            self._gss_ctxt_status = True670            token = None671            """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 token676 677    def ssh_get_mic(self, session_id, gss_kex=False):678        """679        Create the MIC token for a SSH2 message.680 681        :param str session_id: The SSH session ID682        :param bool gss_kex: Generate the MIC for Key Exchange with SSPI or not683        :return: gssapi-with-mic:684                 Returns the MIC token from SSPI for the message we created685                 with ``_ssh_build_mic``.686                 gssapi-keyex:687                 Returns the MIC token from SSPI with the SSH session ID as688                 message.689        """690        self._session_id = session_id691        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-keyex701            mic_token = self._gss_srv_ctxt.sign(self._session_id)702        return mic_token703 704    def ssh_accept_sec_context(self, hostname, username, recv_token):705        """706        Accept a SSPI context (server mode).707 708        :param str hostname: The servers FQDN709        :param str username: The name of the user who attempts to login710        :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`` if713                 no token was returned714        """715        self._gss_host = hostname716        self._username = username717        targ_name = "host/" + self._gss_host718        self._gss_srv_ctxt = sspi.ServerAuth("Kerberos", spn=targ_name)719        error, token = self._gss_srv_ctxt.authorize(recv_token)720        token = token[0].Buffer721        if error == 0:722            self._gss_srv_ctxt_status = True723            token = None724        return token725 726    def ssh_check_mic(self, mic_token, session_id, username=None):727        """728        Verify the MIC token for a SSH2 message.729 730        :param str mic_token: The MIC token received from the client731        :param str session_id: The SSH session ID732        :param str username: The name of the user who attempts to login733        :return: None if the MIC check was successful734        :raises: ``sspi.error`` -- if the MIC check failed735        """736        self._session_id = session_id737        self._username = username738        if username is not None:739            # server mode740            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, an747            # sspi.error will be raised.748            self._gss_srv_ctxt.verify(mic_field, mic_token)749        else:750            # for key exchange with gssapi-keyex751            # client mode752            # Verifies data and its signature.  If verification fails, an753            # sspi.error will be raised.754            self._gss_ctxt.verify(self._session_id, mic_token)755 756    @property757    def credentials_delegated(self):758        """759        Checks if credentials are delegated (server mode).760 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_flags765        )766 767    def save_client_creds(self, client_token):768        """769        Save the Client token in a file. This is used by the SSH server770        to store the client credentails if credentials are delegated771        (server mode).772 773        :param str client_token: The SSPI token received form the client774        :raises:775            ``NotImplementedError`` -- Credential delegation is currently not776            supported in server mode777        """778        raise NotImplementedError779 
codekingpro/portable-devtools · Team Ai