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 _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, 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)
def unwrap(self, data): if data: data = b64encode(data) result = krb.authGSSClientUnwrap(self._ctx, data) 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, _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)
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)
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))
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