pythongssapi / requests-gssapi

An authentication handler for using GSSAPI with Python Requests. Drop-in replacement for old requests-kerberos.
Other
32 stars 21 forks source link

Support for explicit GSSAPI mech #18

Closed optiz0r closed 4 years ago

optiz0r commented 5 years ago

gssapi has recently added support for SPNEGO from RFC4178 (https://github.com/pythongssapi/python-gssapi/commit/2347e3f76b043d8b96a5b28692a9af833b738b89), but to use this it seems an explicit mech must be passed into the SecurityContext constructor. HTTPSPNEGO doesn't currently provide a way for the caller to specify an explicit mech, and gssapi's autodetection may not pick the desired variant in all cases.

frozencemetery commented 5 years ago

Technically, python-gssapi has supported SPNEGO since its inception - what's new is just the ability to filter the negotiated mechanisms.

Can you elaborate on your use case here? Note that it's currently possible to specify an explicit mechanism through the Credential object passed in.

optiz0r commented 5 years ago

Thanks for getting back to me. The use case is for connecting to a service powered by go's krb5 library which seems to explicitly require an RFC4178 style exchange and which I couldn't get to be automatically negotiated by the requests library. The example usage of the modifications in this PR is described here: https://github.com/wintoncode/vault-plugin-auth-kerberos/issues/22#issuecomment-510892327

I hadn't spotted the Credentials object could be used for this instead which would remove the need for this PR to be merged. I will take a look and see if I can get my example code working via that method.

optiz0r commented 5 years ago

If I understand correctly, I think you're referring to the mechs parameter of the gssapi.Credentials constructor. If so, I cannot get this to work:

spnego = gssapi.OID.from_int_seq("1.3.6.1.5.5.2")
creds = gssapi.Credentials(usage="initiate", mechs=[spnego])
target = gssapi.Name("HTTP@{0}:8200".format(vhost), gssapi.NameType.hostbased_service)
auth = HTTPSPNEGOAuth(target_name=target, creds=creds)
r = requests.post("https://{0}:8200/v1/auth/kerberos/login".format(vhost), auth=auth, verify='/etc/pki/tls/cert.pem')

Results in the same error from the application server as if I'd not passed in creds at all:

{"errors":["SPNEGO negotiation token is not a NegTokenInit: OID 1.2.840.113554.1.2.2 does not match SPNEGO OID 1.3.6.1.5.5.2"]}

The only way I've found to match the expectations of the backend server is to pass the spnego object through to the gsssapi.SecurityContext.

It's entirely possible that I've completely misunderstood and have got it all wrong.

simo5 commented 5 years ago

Just a comment, I usually do not like to demean other people code, but that Go library is not worth wasting time. It is a bad halfassed reimplementation of some basic krb/gssapi/spnego code all jumbled together that supports only a fraction of the three or four protocols it "implements". For example it implements only encrypted timestamps as a pre-authentication mechanism. Even if you get it working in the common case it will be an endless source of issues in the future. Also note I didn't even bother to check if it properly implements the 20 years of security countermeasures that a robust Kerberos library needs to implement...

optiz0r commented 4 years ago

Hashicorp are taking ownership of the project I was trying to use this with and have published some alternate code using requests-kerberos which does not require any custom patches at https://github.com/hashicorp/vault-plugin-auth-kerberos.

import requests

service = "HTTP@vault.domain"
rc, vc = kerberos.authGSSClientInit(service=service, mech_oid=kerberos.GSS_MECH_OID_SPNEGO)
kerberos.authGSSClientStep(vc, "")
kerberos_token = kerberos.authGSSClientResponse(vc)

r = requests.post("https://vault.domain:8200/v1/auth/kerberos/login",
                  json={'authorization': 'Negotiate ' + kerberos_token})