Beispiel #1
0
    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
Beispiel #2
0
    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))
Beispiel #3
0
    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)
Beispiel #4
0
    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()
Beispiel #5
0
    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
Beispiel #6
0
    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))
Beispiel #7
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 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
Beispiel #8
0
 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)
Beispiel #9
0
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"
Beispiel #10
0
    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.")
Beispiel #11
0
    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)
Beispiel #12
0
    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())
Beispiel #13
0
    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)
Beispiel #15
0
 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')
Beispiel #16
0
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')
Beispiel #17
0
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
Beispiel #18
0
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)
Beispiel #19
0
    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
Beispiel #21
0
    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
Beispiel #22
0
    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()
Beispiel #23
0
    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
Beispiel #24
0
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")
Beispiel #25
0
    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
Beispiel #26
0
 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)
Beispiel #27
0
 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
Beispiel #28
0
  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"))
Beispiel #29
0
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
Beispiel #30
0
        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')