pythongssapi / python-gssapi

A Python interface to RFC 2743/2744 (plus common extensions)
ISC License
104 stars 46 forks source link

I'm having trouble making this snippet to work #307

Closed flixman closed 1 year ago

flixman commented 1 year ago

What went wrong?

I am trying to store a tgt on a custom ccache. Acquiring the ticket and storing it on the ccache works, but when trying to load it back I am getting an error about default keytab not existing.

How do we reproduce?

Run twice the following snippet. The first time will ask for password for the principal, and the second time you will get the exception :gssapi.raw.misc.GSSError: Major (851968): Unspecified GSS failure. Minor code may provide more information, Minor (2): Key table file '/etc/krb5.keytab' not found"

import gssapi
import os
import sys

principal = 'PRINCIPAL@PRINCIPAL.COM'

class Kerberos:
    def __init__(self):
        self.store = dict(ccache=os.environ['KRB5CCNAME'])

    def kinit(self, principal) -> gssapi.raw.AcquireCredResult:
        name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
        password = input(f'Please input the password for {principal}: ')
        cred = gssapi.raw.acquire_cred_with_password(name=name, password=password.encode())
        gssapi.raw.store_cred_into(self.store, cred.creds, overwrite=True)
        return cred

    def get(self, principal) -> gssapi.raw.AcquireCredResult:
        name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
        try:
            return gssapi.raw.acquire_cred_from(dict_store=self.store, name=name)
        except gssapi.raw.misc.GSSError:
            return self.kinit(principal)

k = Kerberos()
t = k.get(principal)
print(t.creds, t.lifetime)

Component versions (python-gssapi, Kerberos, OS / distro, etc.)

Docker linux container, running on MacOS python-gssapi 1.8.2 pip install gssapi

jborean93 commented 1 year ago

Unfortunately python-gssapi is really just a shim over the GSSAPI library so any behaviour you see here is really just coming from there. I know the acquirecred* APIs are quite picky when it comes to dealing with the stores but if you want to figure out why this is happening you'll need to ask the GSSPI folks themselves.

Fortunately an alternative I know that works is something that uses the python krb5 library I've written to provide a similar interface to the krb5 API. This allows you to replicate kinit in process.

def kinit(username: str, password: str) -> gssapi.raw.creds.Creds:
    ctx = krb5.init_context()

    princ = krb5.parse_name_flags(ctx, username.encode())
    init_opt = krb5.get_init_creds_opt_alloc(ctx)

    # Can set various flags here like forwardable if you wish
    # get_init_creds_opt_set_forwardable(init_opt, True)

    cred = krb5.get_init_creds_password(
        ctx,
        princ,
        init_opt,
        password=password.encode(),
    )

    mem_ccache = krb5.cc_new_unique(ctx, b"MEMORY")
    krb5.cc_initialize(ctx, mem_ccache, princ)
    krb5.cc_store_cred(ctx, mem_ccache, cred)

    kerberos = gssapi.OID.from_int_seq("1.2.840.113554.1.2.2")
    ccache_name = mem_ccache.name or b""
    if mem_ccache.cache_type:
        ccache_name = mem_ccache.cache_type + b":" + ccache_name

    return gssapi.raw.acquire_cred_from(
        {b"ccache": ccache_name},
        name=gssapi.Name(base=username, name_type=gssapi.NameType.user),
        mechs=[kerberos],
        usage="initiate",
    ).creds

You are free to adapt this to store it in a file ccache if you wish but I know it works and is able to specify more properties, like forwardable, that isn't really possible through GSSAPI.

flixman commented 1 year ago

@jborean93 thank you!