def _get_security_context(name_type, mech, spn, username, password, delegate, wrap_required, channel_bindings=None): if username is not None: username = gssapi.Name(base=username, name_type=name_type) server_name = gssapi.Name(spn, name_type=gssapi.NameType.hostbased_service) # first try and get the cred from the existing cache, if that fails # then get a new ticket with the password (if specified). The cache # can only be used for Kerberos, NTLM/SPNEGO must have acquire the # cred with a pass cred = None kerb_oid = GSSAPIContext._AUTH_PROVIDERS['kerberos'] kerb_mech = gssapi.OID.from_int_seq(kerb_oid) if mech == kerb_mech: try: cred = gssapi.Credentials(name=username, usage='initiate', mechs=[mech]) except gssapi.exceptions.GSSError as err: # we can't acquire the cred if no password was supplied if password is None: raise err pass elif username is None or password is None: raise ValueError("Can only use implicit credentials with kerberos " "authentication") if cred is None: # error when trying to access the existing cache, get our own # credentials with the password specified b_password = to_bytes(password) cred = gssapi.raw.acquire_cred_with_password(username, b_password, usage='initiate', mechs=[mech]) cred = cred.creds flags = gssapi.RequirementFlag.mutual_authentication | \ gssapi.RequirementFlag.out_of_sequence_detection if delegate: flags |= gssapi.RequirementFlag.delegate_to_peer if wrap_required: flags |= gssapi.RequirementFlag.confidentiality context = gssapi.SecurityContext(name=server_name, creds=cred, usage='initiate', mech=mech, flags=flags, channel_bindings=channel_bindings) return context
def generate_request_header(self, response, host, is_preemptive=False): # This method needs to be shimmed because `host` isn't exposed to # __init__() and we need to derive things from it. Also, __init__() # can't fail, in the strictest compatability sense. try: if self.principal is not None: gss_stage = "acquiring credentials" name = gssapi.Name(self.principal, gssapi.NameType.hostbased_service) self.creds = gssapi.Credentials(name=name, usage="initiate") # contexts still need to be stored by host, but hostname_override # allows use of an arbitrary hostname for the GSSAPI exchange (eg, # in cases of aliased hosts, internal vs external, CNAMEs w/ # name-based HTTP hosting) if self.service is not None: gss_stage = "initiating context" kerb_host = host if self.hostname_override: kerb_host = self.hostname_override kerb_spn = "{0}@{1}".format(self.service, kerb_host) self.target_name = gssapi.Name( kerb_spn, gssapi.NameType.hostbased_service) return HTTPSPNEGOAuth.generate_request_header( self, response, host, is_preemptive) except gssapi.exceptions.GSSError as error: msg = error.gen_message() log.exception( "generate_request_header(): {0} failed:".format(gss_stage)) log.exception(msg) raise SPNEGOExchangeError("%s failed: %s" % (gss_stage, msg))
def init_cred(self, call, target="nfs@jupiter", source=None, oid=None): # STUB - need intelligent way to set defaults good_major = [GSS_S_COMPLETE, GSS_S_CONTINUE_NEEDED] p = Packer() up = GSSUnpacker('') # Set target (of form nfs@SERVER) target = gssapi.Name(target, gssapi.NameType.hostbased_service) # Set source (of form USERNAME) if source is not None: source = gssapi.Name(source, gssapi.NT_USER_NAME) gss_cred = gssapi.Credential(gssapi.INITIATE, source.ptr) # XXX else: # Just use default cred gss_cred = None # RFC2203 5.2.2. Context Creation Requests # When GSS_Init_sec_context() is called, the parameters # replay_det_req_flag and sequence_req_flag must be turned off. # Note - by default, out_of_sequence_detection flag (sequence_req_flag) is used by gssapi.init_sec_context() # and we have 'An expected per-message token was not received' error (GSS_S_GAP_TOKEN). # To prevent this, we need to use default flags without out_of_sequence_detection bit. flags = gssapi.IntEnumFlagSet( gssapi.RequirementFlag, [gssapi.RequirementFlag.mutual_authentication]) context = gssapi.SecurityContext(name=target, creds=gss_cred, flags=flags) input_token = None handle = b'' proc = RPCSEC_GSS_INIT while not context.complete: # Call initSecContext. If it returns COMPLETE, we are done. # If it returns CONTINUE_NEEDED, we must send d['token'] # to the target, which will run it through acceptSecContext, # and give us back a token we need to send through initSecContext. # Repeat as necessary. output_token = context.step(input_token) if context.complete: # XXX if res.major == CONTINUE there is a bug in library code # STUB - now what? Just use context? # XXX need to use res.seq_window # XXX - what if handle still '' ? self._add_context(context, handle) break # Send token to target using protocol of RFC 2203 sect 5.2.2 credinfo = CredInfo(self, context=handle, gss_proc=proc) proc = RPCSEC_GSS_CONTINUE_INIT p.reset() p.pack_opaque(output_token) header, reply = call(p.get_buffer(), credinfo) up.reset(reply) res = up.unpack_rpc_gss_init_res() up.done() # res now holds relevent output from target's acceptSecContext call if res.gss_major not in good_major: raise GSSError(res.gss_major, res.gss_minor) handle = res.handle # Should not change between calls input_token = res.gss_token # This needs to be sent to SecurityContext.step() return CredInfo(self, context=handle)
def _login_gssapi(self): """Login using kerberos credentials (uses gssapi).""" login_url = urlparse.urljoin(self._hub_url, "auth/krb5login/") # read default values from settings principal = self._conf.get("KRB_PRINCIPAL") keytab = self._conf.get("KRB_KEYTAB") service = self._conf.get("KRB_SERVICE") realm = self._conf.get("KRB_REALM") ccache = self._conf.get("KRB_CCACHE") import requests import gssapi import requests_gssapi request_args = {} # NOTE behavior difference from hub proxy overall: # HubProxy by default DOES NOT verify https connections :( # See the constructor. It could be repeated here by defaulting verify to False, # but let's not do that, instead you must have an unbroken SSL setup to # use this auth method. if self._conf.get("CA_CERT"): request_args["verify"] = self._conf["CA_CERT"] server_name = self.get_server_principal(service=service, realm=realm) server_name = gssapi.Name(server_name, gssapi.NameType.kerberos_principal) auth_args = { "target_name": server_name, } if principal is not None: if keytab is None: raise ImproperlyConfigured( "Cannot specify a principal without a keytab") name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) store = {"client_keytab": keytab} if ccache is not None: store["ccache"] = "FILE:" + ccache auth_args["creds"] = gssapi.Credentials(name=name, store=store, usage="initiate") # We only do one request, but a Session is used to allow requests to write # the new session ID into the cookiejar. with requests.Session() as s: s.cookies = self._transport.cookiejar response = s.get(login_url, auth=requests_gssapi.HTTPSPNEGOAuth(**auth_args), allow_redirects=False, **request_args) self._logger and self._logger.debug("Login response: %s %s", response, response.headers) response.raise_for_status()
def _acquire_creds(self, username, password): # 3 use cases with Kerberos Auth # 1. Both the user and pass is supplied so we want to create a new # ticket with the pass # 2. Only the user is supplied so we will attempt to get the cred # from the existing store # 3. The user is not supplied so we will attempt to get the default # cred from the existing store log.info("GSSAPI: Acquiring credentials handle") if username and password: log.debug("GSSAPI: Acquiring credentials handle for user %s with " "password" % username) user = gssapi.Name(base=username, name_type=gssapi.NameType.user) bpass = password.encode('utf-8') try: creds = gssapi.raw.acquire_cred_with_password(user, bpass, usage='initiate') except AttributeError: raise SMBAuthenticationError("Cannot get GSSAPI credential " "with password as the necessary " "GSSAPI extensions are not " "available") except gssapi.exceptions.GSSError as er: raise SMBAuthenticationError("Failed to acquire GSSAPI " "credential with password: %s" % str(er)) # acquire_cred_with_password returns a wrapper, we want the creds # object inside this wrapper creds = creds.creds elif username: log.debug("GSSAPI: Acquiring credentials handle for user %s from " "existing cache" % username) user = gssapi.Name(base=username, name_type=gssapi.NameType.user) try: creds = gssapi.Credentials(name=user, usage='initiate') except gssapi.exceptions.MissingCredentialsError as er: raise SMBAuthenticationError("Failed to acquire GSSAPI " "credential for user %s from the " "exisiting cache: %s" % (str(user), str(er))) else: log.debug("GSSAPI: Acquiring credentials handle for default user " "in cache") try: creds = gssapi.Credentials(name=None, usage='initiate') except gssapi.exceptions.GSSError as er: raise SMBAuthenticationError("Failed to acquire default " "GSSAPI credential from the " "existing cache: %s" % str(er)) user = creds.name log.info("GSSAPI: Acquired credentials for user %s" % str(user)) return creds
def generate_request_header(self, response, host, is_preemptive=False): """ Generates the GSSAPI authentication token with kerberos. If any GSSAPI step fails, raise KerberosExchangeError with failure detail. """ gssflags = [ gssapi.RequirementFlag.mutual_authentication, gssapi.RequirementFlag.out_of_sequence_detection ] if self.delegate: gssflags.append(gssapi.RequirementFlag.delegate_to_peer) try: # contexts still need to be stored by host, but hostname_override # allows use of an arbitrary hostname for the GSSAPI exchange # (eg, in cases of aliased hosts, internal vs external, CNAMEs # w/ name-based HTTP hosting) kerb_host = host if self.hostname_override: kerb_host = self.hostname_override kerb_spn = "{0}@{1}".format(self.service, kerb_host) creds = None if self.principal: gss_stage = "acquiring credentials" creds = gssapi.Credentials(name=gssapi.Name(self.principal), usage="initiate") gss_stage = "initiating context" self.context[host] = gssapi.SecurityContext( usage="initiate", flags=gssflags, name=gssapi.Name(kerb_spn), creds=creds) gss_stage = "stepping context" if is_preemptive: gss_response = self.context[host].step() else: gss_response = self.context[host].step( _negotiate_value(response)) return "Negotiate {0}".format(gss_response) except gssapi.exceptions.GSSError as error: msg = error.gen_message() log.exception( "generate_request_header(): {0} failed:".format(gss_stage)) log.exception(msg) raise KerberosExchangeError("%s failed: %s" % (gss_stage, msg))
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 1-element tuple with the requested target_name or the True value to request the target_name from DNS """ if connection.sasl_credentials and 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 = gssapi.Name('ldap@' + hostname, gssapi.NameType.hostbased_service) else: target_name = gssapi.Name('ldap@' + connection.sasl_credentials[0], gssapi.NameType.hostbased_service) else: target_name = gssapi.Name('ldap@' + connection.server.host, gssapi.NameType.hostbased_service) creds = gssapi.Credentials(name=gssapi.Name(connection.user), usage='initiate') if connection.user else None ctx = gssapi.SecurityContext(name=target_name, mech=gssapi.MechType.kerberos, creds=creds) in_token = None try: while True: out_token = ctx.step(in_token) if out_token is None: out_token = '' result = send_sasl_negotiation(connection, controls, out_token) in_token = result['saslCreds'] try: # This raised an exception in gssapi<1.1.2 if the context was # incomplete, but was fixed in # https://github.com/pythongssapi/python-gssapi/pull/70 if ctx.complete: break except gssapi.exceptions.MissingContextError: pass unwrapped_token = ctx.unwrap(in_token) if len(unwrapped_token.message) != 4: raise LDAPCommunicationError("Incorrect response from server") server_security_layers = unwrapped_token.message[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]) out_token = ctx.wrap(bytes(client_security_layers), False) return send_sasl_negotiation(connection, controls, out_token.message) except (gssapi.exceptions.GSSError, LDAPCommunicationError): abort_sasl_negotiation(connection, controls) raise
def get_context(self, host): service_name = gssapi.Name(self.service_name or '{0}@{1}'.format(self.service, host), gssapi.NameType.hostbased_service) logging.debug("get_context(): service name={0}".format(service_name)) if self.negotiate_client_name: creds = gssapi.Credentials(name=gssapi.Name(self.negotiate_client_name), usage='initiate') else: creds = None return gssapi.SecurityContext(name=service_name, creds=creds)
def test_login_gssapi_krb_opts(requests_session): """Login with gssapi method prepares auth using correct gssapi parameters according to config.""" hub_url = "https://hub.example.com/myapp/endpoint" login_url = "https://hub.example.com/myapp/auth/krb5login/" conf = PyConfigParser() conf.load_from_dict({ "HUB_URL": hub_url, "AUTH_METHOD": "gssapi", "CA_CERT": "/some/ca-bundle.pem", "KRB_PRINCIPAL": "*****@*****.**", "KRB_SERVICE": "SVC", "KRB_REALM": "REALM.EXAMPLE.COM", "KRB_KEYTAB": "some-keytab", "KRB_CCACHE": "some-cache", }) transport = FakeTransport() proxy = HubProxy(conf, transport=transport) mock_get = requests_session.return_value.get calls_before = len(mock_get.mock_calls) with mock.patch("requests_gssapi.HTTPSPNEGOAuth") as mock_auth: with mock.patch("gssapi.Credentials") as mock_creds: # Force a login proxy._login(force=True) get_call = mock_get.mock_calls[calls_before] # It should have prepared credentials with the details from config mock_creds.assert_called_once_with( name=gssapi.Name("*****@*****.**", gssapi.NameType.kerberos_principal), store={ "client_keytab": "some-keytab", "ccache": "FILE:some-cache" }, usage="initiate", ) # It should have prepared auth with those credentials and our configured # server principal mock_auth.assert_called_once_with( creds=mock_creds.return_value, target_name=gssapi.Name("SVC/[email protected]", gssapi.NameType.kerberos_principal), ) # It should have used the configured CA bundle when issuing the request assert get_call[2]["verify"] == "/some/ca-bundle.pem"
def _gssapi_check(self): environ = request.environ if environ["wsgi.multithread"]: abort( 400, "GSSAPI is not compatible with multi-threaded WSGI servers.", ) ccache = environ.get("KRB5CCNAME") if not ccache: abort(400, "KRB5CCNAME missing.") raise ValueError("KRB5CCNAME missing") principal = environ.get("GSS_NAME") if not principal: abort(400, "GSS_NAME missing.") gss_name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) try: creds = gssapi.Credentials(usage="initiate", name=gss_name, store={"ccache": ccache}) except gssapi.exceptions.GSSError as e: abort(403, f"Invalid credentials {e}") else: if creds.lifetime <= 0: abort(401, "Credential lifetime has expired.")
def get_host_info(self, host): """ Two things can happen here. If we have a session we will add a cookie for that. If not we will set an Authorization header. """ (host, extra_headers, x509) = SSLTransport.get_host_info(self, host) if not isinstance(extra_headers, list): extra_headers = [] session_cookie = getattr(context, 'session_cookie', None) if session_cookie: extra_headers.append(('Cookie', session_cookie)) return (host, extra_headers, x509) # Set the remote host principal service = "HTTP@" + host.split(':')[0] try: name = gssapi.Name(service, gssapi.NameType.hostbased_service) self._sec_context = gssapi.SecurityContext(name=name, flags=self.flags) response = self._sec_context.step() except gssapi.exceptions.GSSError as e: self._handle_exception(e, service=service) self._set_auth_header(extra_headers, response) return (host, extra_headers, x509)
def __init__(self, client_service, keyfile, keytab, server, realm, ldap_uri=None, auth_type=None): if client_service.endswith(realm) or "@" not in client_service: raise ValueError( "Client service name must be a GSS name (service@host), " "not '{}'.".format(client_service)) self.client_service = client_service self.keytab = keytab self.server = server self.realm = realm self.ldap_uri = ldap_uri self.auth_type = auth_type self.service_name = gssapi.Name('HTTP@{}'.format(server), gssapi.NameType.hostbased_service) self.keystore = IPASecStore() # use in-process MEMORY ccache. Handler process don't need a TGT. self.ccache = 'MEMORY:Custodia_{}'.format(secrets.token_hex()) with ccache_env(self.ccache): # Init creds immediately to make sure they are valid. Creds # can also be re-inited by _auth_header to avoid expiry. self.creds = self._init_creds() self.ikk = IPAKEMKeys({ 'server_keys': keyfile, 'ldap_uri': ldap_uri }) self.kemcli = KEMClient(self._server_keys(), self._client_keys())
def __gsshandler(self, token): if token == "": token = None try: if not self.gss_vc: name = gssapi.Name('imap@' + self.hostname, gssapi.NameType.hostbased_service) self.gss_vc = gssapi.SecurityContext(usage="initiate", name=name) if not self.gss_vc.complete: response = self.gss_vc.step(token) return response if response else "" # Don't bother checking qop because we're over a TLS channel # already. But hey, if some server started encrypting tomorrow, # we'd be ready since krb5 always requests integrity and # confidentiality support. response = self.gss_vc.unwrap(token) response = self.gss_vc.wrap(response.message, response.encrypted) return response.message if response.message else "" except gssapi.exceptions.GSSError as err: # GSSAPI errored out on us; respond with None to cancel the # authentication self.ui.debug('imap', err.gen_message()) return None
def handle(self, src, addr): data = b'' while True: data += src.recv(1024) if b'\r\n\r\n' in data: break service = gssapi.Name('HTTP@%s' % self.upstream[0], gssapi.NameType.hostbased_service) ctx = gssapi.SecurityContext(name=service, usage='initiate') token = ctx.step() b64token = base64.b64encode(token).encode('ascii') headers, data = data.split(b'\r\n\r\n', 1) headers = headers.split('\r\n') replaced = False for i, header in enumerate(headers): if header.startswith('Proxy-Authorization:'): headers[i] = b'Proxy-Authorization: Negotiate %s' % b64token replaced = True break if not replaced: headers.append(b'Proxy-Authorization: Negotiate %s' % b64token) dst = create_connection(self.upstream) dst.sendall(b'\r\n'.join(headers) + b'\r\n\r\n' + data) forwarders = (gevent.spawn(forward, src, dst), gevent.spawn(forward, dst, src)) gevent.joinall(forwarders)
def init_creds(self): name = gssapi.Name(self.client_service, gssapi.NameType.hostbased_service) store = {'client_keytab': self.keytab, 'ccache': 'MEMORY:Custodia_%s' % b64encode( os.urandom(8)).decode('ascii')} return gssapi.Credentials(name=name, store=store, usage='initiate')
def get_gssapi_token(principal, host, domain): ''' Get the gssapi token for Kerberos connection principal The service principal host Host url where we would like to authenticate domain Kerberos user domain ''' if not HAS_GSSAPI: raise ImportError('The gssapi library is not imported.') service = '{0}/{1}@{2}'.format(principal, host, domain) log.debug('Retrieving gsspi token for service {0}'.format(service)) service_name = gssapi.Name(service, gssapi.C_NT_USER_NAME) ctx = gssapi.InitContext(service_name) in_token = None while not ctx.established: out_token = ctx.step(in_token) if out_token: encoded_token = base64.b64encode(out_token) return encoded_token if ctx.established: break if not in_token: raise salt.exceptions.CommandExecutionError( 'Can\'t receive token, no response from server') raise salt.exceptions.CommandExecutionError( 'Context established, but didn\'t receive token')
def test_login(k5env, client, caplog, db, settings): caplog.set_level(logging.DEBUG) response = client.get('/login/') assert response.status_code == 401 response = client.get('/login/', HTTP_AUTHORIZATION='Negotiate xxx') assert response.status_code == 401 assert '_auth_user_id' not in client.session response = client.get('/login/', HTTP_AUTHORIZATION=k5env.spnego()) assert response.status_code == 401 assert '_auth_user_id' not in client.session # create an user... user = User.objects.create(username=k5env.user_princ, is_active=False) # still no good, user is inactive response = client.get('/login/', HTTP_AUTHORIZATION=k5env.spnego()) assert response.status_code == 401 assert '_auth_user_id' not in client.session user.is_active = True user.save() # and retry. response = client.get('/login/', HTTP_AUTHORIZATION=k5env.spnego()) assert response.status_code == 302 assert int(client.session['_auth_user_id']) == user.id # break service name resolution settings.GSSAPI_NAME = gssapi.Name('HTTP@localhost', gssapi.NameType.hostbased_service) response = client.get('/login/', HTTP_AUTHORIZATION=k5env.spnego()) assert response.status_code == 401
def use_api_as_principal(principal, keytab): with ipautil.private_ccache() as ccache_file: try: old_principal = getattr(context, "principal", None) name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) store = {"ccache": ccache_file, "client_keytab": keytab} gssapi.Credentials(name=name, usage="initiate", store=store) # Finalize API when TGT obtained using host keytab exists if not api.isdone("finalize"): api.finalize() # Now we have a TGT, connect to IPA try: if api.Backend.rpcclient.isconnected(): api.Backend.rpcclient.disconnect() api.Backend.rpcclient.connect() yield except gssapi.exceptions.GSSError as e: raise Exception( "Unable to bind to IPA server. Error initializing " "principal %s in %s: %s" % (principal, keytab, str(e))) finally: if api.Backend.rpcclient.isconnected(): api.Backend.rpcclient.disconnect() setattr(context, "principal", old_principal)
def get_auth_info(self, use_cookie=True): """ Two things can happen here. If we have a session we will add a cookie for that. If not we will set an Authorization header. """ if not isinstance(self._extra_headers, list): self._extra_headers = [] # Remove any existing Cookie first self._remove_extra_header('Cookie') if use_cookie: session_cookie = getattr(context, 'session_cookie', None) if session_cookie: self._extra_headers.append(('Cookie', session_cookie)) return # Set the remote host principal host = self._get_host() service = self.service + "@" + host.split(':')[0] try: creds = None if self.ccache: creds = gssapi.Credentials(usage='initiate', store={'ccache': self.ccache}) name = gssapi.Name(service, gssapi.NameType.hostbased_service) self._sec_context = gssapi.SecurityContext(creds=creds, name=name, flags=self.flags) response = self._sec_context.step() except gssapi.exceptions.GSSError as e: self._handle_exception(e, service=service) self._set_auth_header(response)
def authenticate(self, request): if request.method == 'OPTIONS': return authorization = request.META.get('HTTP_AUTHORIZATION', '') if authorization.startswith('Negotiate '): host = socket.gethostbyaddr(request.META['SERVER_ADDR'])[0] service_name = 'HTTP/{}'.format(host) service_name = gssapi.Name(service_name) # The browser is authenticating using GSSAPI, trim off 'Negotiate ' and decode: in_token = base64.b64decode(authorization[10:]) server_creds = gssapi.Credentials(name=service_name, usage='accept') ctx = gssapi.SecurityContext(creds=server_creds) # Feed the input token to the context, and get an output token in return out_token = ctx.step(in_token) if out_token: request.negotiate_token = base64.b64encode(out_token).decode() if ctx.complete: name = str(ctx.initiator_name) User = get_user_model() user = User.objects.get(**{self.principal_name_field: name}) return user, None else: raise HTTPUnauthorized
def get_environ_creds(self, environ): # If we have a ccache ... ccache_name = environ.get('KRB5CCNAME') if ccache_name is None: logger.debug('no ccache, need login') return None # ... make sure we have a name ... principal = environ.get('GSS_NAME') if principal is None: logger.debug('no Principal Name, need login') return None # ... and use it to resolve the ccache name (Issue: 6972 ) gss_name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) # Fail if Kerberos credentials are expired or missing creds = get_credentials_if_valid(name=gss_name, ccache_name=ccache_name) if not creds: logger.debug( 'ccache expired or invalid, deleting session, need login') return None return ccache_name
def __init__(self, client_service, keyfile, keytab, server, realm, ldap_uri=None, auth_type=None): self.client_service = client_service self.keytab = keytab # Init creds immediately to make sure they are valid. Creds # can also be re-inited by _auth_header to avoid expiry. # self.creds = self.init_creds() self.service_name = gssapi.Name('HTTP@%s' % (server, ), gssapi.NameType.hostbased_service) self.server = server self.ikk = IPAKEMKeys({'server_keys': keyfile, 'ldap_uri': ldap_uri}) self.kemcli = KEMClient(self._server_keys(server, realm), self._client_keys()) self.keystore = self._keystore(realm, ldap_uri, auth_type) # FIXME: Remove warnings about missing subjAltName for the # requests module urllib3.disable_warnings()
def connect(self): """Connect and authenticate to the server.""" self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) server_address = (self.host, self.port) self.sock.connect(server_address) self.stream = self.sock.makefile(mode='rwb') service_name = gssapi.Name(self.service_name, name_type=gssapi.NameType.hostbased_service) self.ctx = gssapi.SecurityContext(name=service_name, usage='initiate') in_token = None while not self.ctx.complete: out_token = self.ctx.step(in_token) if out_token: out_encoded = base64.standard_b64encode(out_token) self._write_line(out_encoded) if self.ctx.complete: break in_encoded = self._read_line() in_token = base64.standard_b64decode(in_encoded) if not in_token: raise GSSError('No response from server.') _LOGGER.debug('Successfully authenticated.') return True
def process_gssapi_request(token): if current_app.config['KERBEROS_HTTP_HOST']: hostname = current_app.config['KERBEROS_HTTP_HOST'] else: hostname = gethostname() service_name = gssapi.Name("HTTP@%s" % hostname, gssapi.NameType.hostbased_service) try: stage = "initialize server context" sc = gssapi.SecurityContext(usage="accept") stage = "step context" token = sc.step(token if token != "" else None) token = token if token is not None else "" # The current architecture cannot support continuation here stage = "checking completion" if not sc.complete: current_app.logger.error( 'Multiple GSSAPI round trips not supported') raise Forbidden("Attempted multiple GSSAPI round trips") current_app.logger.debug('Completed GSSAPI negotiation') stage = "getting remote user" user = str(sc.initiator_name) return user, token except gssapi.exceptions.GSSError as e: current_app.logger.error('Unable to authenticate: failed to %s: %s' % (stage, e.gen_msg())) raise Forbidden("Authentication failed")
def get(self, path): """ Perform a GET request with GSSAPI authentication """ # Generate token service_name = gssapi.Name('HTTP@{0}'.format(self.url.netloc), gssapi.NameType.hostbased_service) ctx = gssapi.SecurityContext(usage="initiate", name=service_name) data = b64encode(ctx.step()).decode() # Make the connection connection = httplib.HTTPSConnection(self.url.netloc, 443) log.debug("GET {0}".format(path)) connection.putrequest("GET", path) connection.putheader("Authorization", "Negotiate {0}".format(data)) connection.putheader("Referer", self.url_string) connection.endheaders() # Perform the request, convert response into lines response = connection.getresponse() if response.status != 200: raise ReportError( "Failed to fetch tickets: {0}".format(response.status)) lines = response.read().decode("utf8").strip().split("\n")[1:] log.debug("Tickets fetched:") log.debug(pretty(lines)) return lines
def init_cred(self, call, target="nfs@jupiter", source=None, oid=None): # STUB - need intelligent way to set defaults good_major = [gssapi.GSS_S_COMPLETE, gssapi.GSS_S_CONTINUE_NEEDED] p = Packer() up = GSSUnpacker('') # Set target (of form nfs@SERVER) target = gssapi.Name(target, gssapi.NT_HOSTBASED_SERVICE) # Set source (of form USERNAME) if source is not None: source = gssapi.Name(source, gssapi.NT_USER_NAME) gss_cred = gssapi.Credential(gssapi.INITIATE, source.ptr) # XXX else: # Just use default cred gss_cred = None context = gssapi.Context() token = None handle = '' proc = RPCSEC_GSS_INIT while True: # Call initSecContext. If it returns COMPLETE, we are done. # If it returns CONTINUE_NEEDED, we must send d['token'] # to the target, which will run it through acceptSecContext, # and give us back a token we need to send through initSecContext. # Repeat as necessary. token = context.init(target, token, gss_cred) if context.open: # XXX if res.major == CONTINUE there is a bug in library code # STUB - now what? Just use context? # XXX need to use res.seq_window # XXX - what if handle still '' ? self._add_context(context, handle) break # Send token to target using protocol of RFC 2203 sect 5.2.2 credinfo = CredInfo(self, context=handle, gss_proc=proc) proc = RPCSEC_GSS_CONTINUE_INIT p.reset() p.pack_opaque(token) header, reply = call(p.get_buffer(), credinfo) up.reset(reply) res = up.unpack_rpc_gss_init_res() up.done() # res now holds relevent output from target's acceptSecContext call if res.gss_major not in good_major: raise gssapi.Error(res.gss_major, res.gss_minor) handle = res.handle # Should not change between calls token = res.gss_token # This needs to be sent to initSecContext return CredInfo(self, context=handle)
def start(self, connection): try: if self.client_name: creds = gssapi.Credentials( name=gssapi.Name(self.client_name)) else: creds = None hostname = self.get_hostname(connection) name = gssapi.Name(b'@'.join([self.service, hostname]), gssapi.NameType.hostbased_service) context = gssapi.SecurityContext(name=name, creds=creds) return context.step(None) except gssapi.raw.misc.GSSError: if self.fail_soft: return NotImplemented else: raise
def __init__(self, hostname, service="WSMAN"): spn=service+"/"+hostname gss_spn = gssapi.Name(spn, name_type=gssapi.NameType.kerberos_principal) self._ctx = gssapi.SecurityContext(name=gss_spn) self.token = "".join(base64.encodestring(self._ctx.step()).split("\n"))
def login(principal, password): # Acquire new credentials name = gssapi.Name(principal, gssapi.NameType.kerberos_principal) acquire_credentials = gssapi.raw.acquire_cred_with_password(name, password) credentials = acquire_credentials.creds # Store credentials in the cache gssapi.raw.store_cred_into(STORE, credentials, usage='initiate', overwrite=True) return credentials
def spnego(): service_name = gssapi.Name(k5realm.http_princ) service_name.canonicalize(gssapi.MechType.kerberos) # first attempt ctx = gssapi.SecurityContext(usage='initiate', name=service_name) return 'Negotiate %s' % base64.b64encode( ctx.step()).decode('ascii')