def authenticate(self,
                  service=_SPN,
                  principal=None,
                  flags=kerberos.GSS_C_MUTUAL_FLAG,
                  user=_USER,
                  domain=_DOMAIN,
                  password=_PASSWORD,
                  mech_oid=kerberos.GSS_MECH_OID_KRB5,
                  upn=_UPN,
                  protect=0):
     res, ctx = kerberos.authGSSClientInit(service, principal, flags, user,
                                           domain, password, mech_oid)
     res = kerberos.authGSSClientStep(ctx, "")
     payload = kerberos.authGSSClientResponse(ctx)
     response = self.db.command('saslStart',
                                mechanism='GSSAPI',
                                payload=payload)
     while res == kerberos.AUTH_GSS_CONTINUE:
         res = kerberos.authGSSClientStep(ctx, response['payload'])
         payload = kerberos.authGSSClientResponse(ctx) or ''
         response = self.db.command(
             'saslContinue',
             conversationId=response['conversationId'],
             payload=payload)
     kerberos.authGSSClientUnwrap(ctx, response['payload'])
     kerberos.authGSSClientWrap(ctx, kerberos.authGSSClientResponse(ctx),
                                upn, protect)
     response = self.db.command('saslContinue',
                                conversationId=response['conversationId'],
                                payload=kerberos.authGSSClientResponse(ctx))
     self.assertTrue(response['done'])
 def authenticate(self,
                  service=_SPN,
                  principal=None,
                  flags=kerberos.GSS_C_MUTUAL_FLAG,
                  user=_USER,
                  domain=_DOMAIN,
                  password=_PASSWORD,
                  mech_oid=kerberos.GSS_MECH_OID_KRB5,
                  upn=_UPN,
                  protect=0):
         res, ctx = kerberos.authGSSClientInit(
             service, principal, flags, user, domain, password, mech_oid)
         res = kerberos.authGSSClientStep(ctx, "")
         payload = kerberos.authGSSClientResponse(ctx)
         response = self.db.command(
             'saslStart', mechanism='GSSAPI', payload=payload)
         while res == kerberos.AUTH_GSS_CONTINUE:
             res = kerberos.authGSSClientStep(ctx, response['payload'])
             payload = kerberos.authGSSClientResponse(ctx) or ''
             response = self.db.command(
                'saslContinue',
                conversationId=response['conversationId'],
                payload=payload)
         kerberos.authGSSClientUnwrap(ctx, response['payload'])
         kerberos.authGSSClientWrap(ctx,
                                    kerberos.authGSSClientResponse(ctx),
                                    upn,
                                    protect)
         response = self.db.command(
            'saslContinue',
            conversationId=response['conversationId'],
            payload=kerberos.authGSSClientResponse(ctx))
         self.assertTrue(response['done'])
    def test_authenticate(self):
        res, ctx = kerberos.authGSSClientInit(_SPN, None,
                                              kerberos.GSS_C_MUTUAL_FLAG,
                                              _USER, _DOMAIN, _PASSWORD)
        self.assertEqual(res, kerberos.AUTH_GSS_COMPLETE)

        res = kerberos.authGSSClientStep(ctx, "")
        self.assertEqual(res, kerberos.AUTH_GSS_CONTINUE)

        payload = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(payload, str)

        response = self.db.command('saslStart',
                                   mechanism='GSSAPI',
                                   payload=payload)
        while res == kerberos.AUTH_GSS_CONTINUE:
            res = kerberos.authGSSClientStep(ctx, response['payload'])
            payload = kerberos.authGSSClientResponse(ctx) or ''
            response = self.db.command(
                'saslContinue',
                conversationId=response['conversationId'],
                payload=payload)

        res = kerberos.authGSSClientUnwrap(ctx, response['payload'])
        self.assertEqual(res, 1)

        unwrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(unwrapped, str)
        self.assertIsInstance(kerberos.authGSSClientResponseConf(ctx), int)

        # RFC-4752
        challenge_bytes = base64.standard_b64decode(unwrapped)
        self.assertEqual(4, len(challenge_bytes))

        # Manually create an authorization message and encrypt it. This
        # is the "no security layer" message as detailed in RFC-4752,
        # section 3.1, final paragraph. This is also the message created
        # by calling authGSSClientWrap with the "user" option.
        msg = base64.standard_b64encode(b"\x01\x00\x00\x00" +
                                        _UPN.encode("utf8")).decode("utf8")
        res = kerberos.authGSSClientWrap(ctx, msg)
        self.assertEqual(res, 1)

        custom = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(custom, str)

        # Wrap using unwrapped and user principal.
        res = kerberos.authGSSClientWrap(ctx, unwrapped, _UPN)
        self.assertEqual(res, 1)

        wrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(wrapped, str)

        # Actually complete authentication, using our custom message.
        response = self.db.command('saslContinue',
                                   conversationId=response['conversationId'],
                                   payload=custom)
        self.assertTrue(response['done'])

        self.assertIsInstance(kerberos.authGSSClientUsername(ctx), str)
Exemple #4
0
def _windows_sasl_gssapi(connection, controls):
    """ Performs a bind using the Kerberos v5 ("GSSAPI") SASL mechanism
    from RFC 4752 using the winkerberos package that works natively on most
    windows operating systems.
    """
    target_name = _common_determine_target_name(connection)
    # initiation happens before beginning the SASL bind when using windows kerberos
    authz_id, _ = _common_determine_authz_id_and_creds(connection)
    gssflags = (winkerberos.GSS_C_MUTUAL_FLAG | winkerberos.GSS_C_SEQUENCE_FLAG
                | winkerberos.GSS_C_INTEG_FLAG | winkerberos.GSS_C_CONF_FLAG)
    _, ctx = winkerberos.authGSSClientInit(target_name, gssflags=gssflags)

    in_token = b''
    try:
        negotiation_complete = False
        while not negotiation_complete:
            # GSSAPI is a "client goes first" SASL mechanism. Send the first "response" to the server and
            # recieve its first challenge.
            # Despite this, we can get channel binding, which includes CBTs for windows environments computed from
            # the peer certificate, before starting.
            status = winkerberos.authGSSClientStep(
                ctx,
                base64.b64encode(in_token).decode('utf-8'),
                channel_bindings=get_channel_bindings(connection.socket))
            # figure out if we're done with our sasl negotiation
            negotiation_complete = (status == winkerberos.AUTH_GSS_COMPLETE)
            out_token = winkerberos.authGSSClientResponse(ctx) or ''
            out_token_bytes = base64.b64decode(out_token)
            result = send_sasl_negotiation(connection, controls,
                                           out_token_bytes)
            in_token = result['saslCreds'] or b''

        winkerberos.authGSSClientUnwrap(
            ctx,
            base64.b64encode(in_token).decode('utf-8'))
        negotiated_token = ''
        if winkerberos.authGSSClientResponse(ctx):
            negotiated_token = base64.standard_b64decode(
                winkerberos.authGSSClientResponse(ctx))
        client_security_layers = _common_process_end_token_get_security_layers(
            negotiated_token)
        # manually construct a message indicating use of authorization-only layer
        # see winkerberos example: https://github.com/mongodb/winkerberos/blob/master/test/test_winkerberos.py
        authz_only_msg = base64.b64encode(
            bytes(client_security_layers) + authz_id).decode('utf-8')
        winkerberos.authGSSClientWrap(ctx, authz_only_msg)
        out_token = winkerberos.authGSSClientResponse(ctx) or ''

        return send_sasl_negotiation(connection, controls,
                                     base64.b64decode(out_token))
    except (winkerberos.GSSError, LDAPCommunicationError):
        abort_sasl_negotiation(connection, controls)
        raise
    def test_authenticate(self):
        res, ctx = kerberos.authGSSClientInit(
            _SPN,
            _PRINCIPAL,
            kerberos.GSS_C_MUTUAL_FLAG,
            _USER,
            _DOMAIN,
            _PASSWORD)
        self.assertEqual(res, kerberos.AUTH_GSS_COMPLETE)

        res = kerberos.authGSSClientStep(ctx, "")
        self.assertEqual(res, kerberos.AUTH_GSS_CONTINUE)

        payload = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(payload, str)

        response = self.db.command(
            'saslStart', mechanism='GSSAPI', payload=payload)
        while res == kerberos.AUTH_GSS_CONTINUE:
            res = kerberos.authGSSClientStep(ctx, response['payload'])
            payload = kerberos.authGSSClientResponse(ctx) or ''
            response = self.db.command(
               'saslContinue',
               conversationId=response['conversationId'],
               payload=payload)

        res = kerberos.authGSSClientUnwrap(ctx, response['payload'])
        self.assertEqual(res, 1)

        unwrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(unwrapped, str)

        # Try just rewrapping (no user)
        res = kerberos.authGSSClientWrap(ctx, unwrapped)
        self.assertEqual(res, 1)

        wrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(wrapped, str)

        # Actually complete authentication
        res = kerberos.authGSSClientWrap(ctx, unwrapped, _UPN)
        self.assertEqual(res, 1)

        wrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(wrapped, str)

        response = self.db.command(
           'saslContinue',
           conversationId=response['conversationId'],
           payload=wrapped)
        self.assertTrue(response['done'])

        self.assertIsInstance(kerberos.authGSSClientUsername(ctx), str)
    def test_authenticate(self):
        res, ctx = kerberos.authGSSClientInit(_SPN, _PRINCIPAL,
                                              kerberos.GSS_C_MUTUAL_FLAG,
                                              _USER, _DOMAIN, _PASSWORD)
        self.assertEqual(res, kerberos.AUTH_GSS_COMPLETE)

        res = kerberos.authGSSClientStep(ctx, "")
        self.assertEqual(res, kerberos.AUTH_GSS_CONTINUE)

        payload = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(payload, str)

        response = self.db.command('saslStart',
                                   mechanism='GSSAPI',
                                   payload=payload)
        while res == kerberos.AUTH_GSS_CONTINUE:
            res = kerberos.authGSSClientStep(ctx, response['payload'])
            payload = kerberos.authGSSClientResponse(ctx) or ''
            response = self.db.command(
                'saslContinue',
                conversationId=response['conversationId'],
                payload=payload)

        res = kerberos.authGSSClientUnwrap(ctx, response['payload'])
        self.assertEqual(res, 1)

        unwrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(unwrapped, str)

        # Try just rewrapping (no user)
        res = kerberos.authGSSClientWrap(ctx, unwrapped)
        self.assertEqual(res, 1)

        wrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(wrapped, str)

        # Actually complete authentication
        res = kerberos.authGSSClientWrap(ctx, unwrapped, _UPN)
        self.assertEqual(res, 1)

        wrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(wrapped, str)

        response = self.db.command('saslContinue',
                                   conversationId=response['conversationId'],
                                   payload=wrapped)
        self.assertTrue(response['done'])

        self.assertIsInstance(kerberos.authGSSClientUsername(ctx), str)
Exemple #7
0
    def wrap(self, data, protect=None):
        if data:
            data = b64encode(data)

        result = krb.authGSSClientWrap(self._ctx, data, None, 0)

        if result < 0:
            raise GSSAPIAdapterException(result)

        out_data = krb.authGSSClientResponse(self._ctx)
        return WrappedToken(b64decode(out_data))
    def test_authenticate(self):
        res, ctx = kerberos.authGSSClientInit(
            _SPN,
            None,
            kerberos.GSS_C_MUTUAL_FLAG,
            _USER,
            _DOMAIN,
            _PASSWORD)
        self.assertEqual(res, kerberos.AUTH_GSS_COMPLETE)

        res = kerberos.authGSSClientStep(ctx, "")
        self.assertEqual(res, kerberos.AUTH_GSS_CONTINUE)

        payload = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(payload, str)

        response = self.db.command(
            'saslStart', mechanism='GSSAPI', payload=payload)
        while res == kerberos.AUTH_GSS_CONTINUE:
            res = kerberos.authGSSClientStep(ctx, response['payload'])
            payload = kerberos.authGSSClientResponse(ctx) or ''
            response = self.db.command(
               'saslContinue',
               conversationId=response['conversationId'],
               payload=payload)

        res = kerberos.authGSSClientUnwrap(ctx, response['payload'])
        self.assertEqual(res, 1)

        unwrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(unwrapped, str)
        self.assertIsInstance(kerberos.authGSSClientResponseConf(ctx), int)

        # RFC-4752
        challenge_bytes = base64.standard_b64decode(unwrapped)
        self.assertEqual(4, len(challenge_bytes))

        # Manually create an authorization message and encrypt it. This
        # is the "no security layer" message as detailed in RFC-4752,
        # section 3.1, final paragraph. This is also the message created
        # by calling authGSSClientWrap with the "user" option.
        msg = base64.standard_b64encode(
            b"\x01\x00\x00\x00" + _UPN.encode("utf8")).decode("utf8")
        res = kerberos.authGSSClientWrap(ctx, msg)
        self.assertEqual(res, 1)

        custom = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(custom, str)

        # Wrap using unwrapped and user principal.
        res = kerberos.authGSSClientWrap(ctx, unwrapped, _UPN)
        self.assertEqual(res, 1)

        wrapped = kerberos.authGSSClientResponse(ctx)
        self.assertIsInstance(wrapped, str)

        # Actually complete authentication, using our custom message.
        response = self.db.command(
           'saslContinue',
           conversationId=response['conversationId'],
           payload=custom)
        self.assertTrue(response['done'])

        self.assertIsInstance(kerberos.authGSSClientUserName(ctx), str)
Exemple #9
0
def _authenticate_gssapi(credentials, sock_info):
    """Authenticate using GSSAPI.
    """
    if not HAVE_KERBEROS:
        raise ConfigurationError('The "kerberos" module must be '
                                 'installed to use GSSAPI authentication.')

    try:
        username = credentials.username
        password = credentials.password
        props = credentials.mechanism_properties
        # Starting here and continuing through the while loop below - establish
        # the security context. See RFC 4752, Section 3.1, first paragraph.
        host = sock_info.address[0]
        if props.canonicalize_host_name:
            host = socket.getfqdn(host)
        service = props.service_name + '@' + host
        if props.service_realm is not None:
            service = service + '@' + props.service_realm

        if password is not None:
            if _USE_PRINCIPAL:
                # Note that, though we use unquote_plus for unquoting URI
                # options, we use quote here. Microsoft's UrlUnescape (used
                # by WinKerberos) doesn't support +.
                principal = ":".join((quote(username), quote(password)))
                result, ctx = kerberos.authGSSClientInit(
                    service, principal, gssflags=kerberos.GSS_C_MUTUAL_FLAG)
            else:
                if '@' in username:
                    user, domain = username.split('@', 1)
                else:
                    user, domain = username, None
                result, ctx = kerberos.authGSSClientInit(
                    service, gssflags=kerberos.GSS_C_MUTUAL_FLAG,
                    user=user, domain=domain, password=password)
        else:
            result, ctx = kerberos.authGSSClientInit(
                service, gssflags=kerberos.GSS_C_MUTUAL_FLAG)

        if result != kerberos.AUTH_GSS_COMPLETE:
            raise OperationFailure('Kerberos context failed to initialize.')

        try:
            # pykerberos uses a weird mix of exceptions and return values
            # to indicate errors.
            # 0 == continue, 1 == complete, -1 == error
            # Only authGSSClientStep can return 0.
            if kerberos.authGSSClientStep(ctx, '') != 0:
                raise OperationFailure('Unknown kerberos '
                                       'failure in step function.')

            # Start a SASL conversation with mongod/s
            # Note: pykerberos deals with base64 encoded byte strings.
            # Since mongo accepts base64 strings as the payload we don't
            # have to use bson.binary.Binary.
            payload = kerberos.authGSSClientResponse(ctx)
            cmd = SON([('saslStart', 1),
                       ('mechanism', 'GSSAPI'),
                       ('payload', payload),
                       ('autoAuthorize', 1)])
            response = sock_info.command('$external', cmd)

            # Limit how many times we loop to catch protocol / library issues
            for _ in range(10):
                result = kerberos.authGSSClientStep(ctx,
                                                    str(response['payload']))
                if result == -1:
                    raise OperationFailure('Unknown kerberos '
                                           'failure in step function.')

                payload = kerberos.authGSSClientResponse(ctx) or ''

                cmd = SON([('saslContinue', 1),
                           ('conversationId', response['conversationId']),
                           ('payload', payload)])
                response = sock_info.command('$external', cmd)

                if result == kerberos.AUTH_GSS_COMPLETE:
                    break
            else:
                raise OperationFailure('Kerberos '
                                       'authentication failed to complete.')

            # Once the security context is established actually authenticate.
            # See RFC 4752, Section 3.1, last two paragraphs.
            if kerberos.authGSSClientUnwrap(ctx,
                                            str(response['payload'])) != 1:
                raise OperationFailure('Unknown kerberos '
                                       'failure during GSS_Unwrap step.')

            if kerberos.authGSSClientWrap(ctx,
                                          kerberos.authGSSClientResponse(ctx),
                                          username) != 1:
                raise OperationFailure('Unknown kerberos '
                                       'failure during GSS_Wrap step.')

            payload = kerberos.authGSSClientResponse(ctx)
            cmd = SON([('saslContinue', 1),
                       ('conversationId', response['conversationId']),
                       ('payload', payload)])
            sock_info.command('$external', cmd)

        finally:
            kerberos.authGSSClientClean(ctx)

    except kerberos.KrbError as exc:
        raise OperationFailure(str(exc))
Exemple #10
0
def sasl_gssapi(connection, controls):
    """
    Performs a bind using the Kerberos v5 ("GSSAPI") SASL mechanism
    from RFC 4752. Does not support any security layers, only authentication!
    sasl_credentials can be empty or a tuple with one or two elements.
    The first element determines which service principal to request a ticket
    for and can be one of the following:
    - None or False, to use the hostname from the Server object
    - True to perform a reverse DNS lookup to retrieve the canonical hostname
      for the hosts IP address
    - A string containing the hostname
    The optional second element is what authorization ID to request.
    - If omitted or None, the authentication ID is used as the authorization ID
    - If a string, the authorization ID to use. Should start with "dn:" or
      "user:"******"""
    # pylint: disable=too-many-branches
    target_name = None
    authz_id = b''
    if connection.sasl_credentials:
        if (len(connection.sasl_credentials) >= 1
                and connection.sasl_credentials[0]):
            if connection.sasl_credentials[0] is True:
                hostname = \
                    socket.gethostbyaddr(connection.socket.getpeername()[0])[0]
                target_name = 'ldap@' + hostname

            else:
                target_name = 'ldap@' + connection.sasl_credentials[0]
        if (len(connection.sasl_credentials) >= 2
                and connection.sasl_credentials[1]):
            authz_id = connection.sasl_credentials[1].encode("utf-8")
    if target_name is None:
        target_name = 'ldap@' + connection.server.host

    gssflags = (kerberos.GSS_C_MUTUAL_FLAG | kerberos.GSS_C_SEQUENCE_FLAG
                | kerberos.GSS_C_INTEG_FLAG | kerberos.GSS_C_CONF_FLAG)
    channel_bindings = get_channel_bindings(connection.socket)
    _, ctx = kerberos.authGSSClientInit(target_name, gssflags=gssflags)

    in_token = b''
    try:
        while True:
            if channel_bindings:
                status = kerberos.authGSSClientStep(
                    ctx,
                    base64.b64encode(in_token).decode('ascii'),
                    channel_bindings=channel_bindings)
            else:
                status = kerberos.authGSSClientStep(
                    ctx,
                    base64.b64encode(in_token).decode('ascii'))
            out_token = kerberos.authGSSClientResponse(ctx) or ''
            result = send_sasl_negotiation(connection, controls,
                                           base64.b64decode(out_token))
            in_token = result['saslCreds'] or b''
            if status == kerberos.AUTH_GSS_COMPLETE:
                break

        kerberos.authGSSClientUnwrap(
            ctx,
            base64.b64encode(in_token).decode('ascii'))
        unwrapped_token = base64.b64decode(
            kerberos.authGSSClientResponse(ctx) or '')

        if len(unwrapped_token) != 4:
            raise LDAPCommunicationError('Incorrect response from server')

        server_security_layers = unwrapped_token[0]
        if not isinstance(server_security_layers, int):
            server_security_layers = ord(server_security_layers)
        if server_security_layers in (0, NO_SECURITY_LAYER):
            if unwrapped_token.message[1:] != '\x00\x00\x00':
                raise LDAPCommunicationError(
                    'Server max buffer size must be 0 if no security layer')
        if not server_security_layers & NO_SECURITY_LAYER:
            raise LDAPCommunicationError(
                'Server requires a security layer, but this is not implemented'
            )

        client_security_layers = bytearray([NO_SECURITY_LAYER, 0, 0, 0])
        kerberos.authGSSClientWrap(
            ctx,
            base64.b64encode(bytes(client_security_layers) +
                             authz_id).decode('ascii'))
        out_token = kerberos.authGSSClientResponse(ctx) or ''

        return send_sasl_negotiation(connection, controls,
                                     base64.b64decode(out_token))
    except (kerberos.GSSError, LDAPCommunicationError):
        abort_sasl_negotiation(connection, controls)
        raise