Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OID of MechToken does not match the first in the MechTypeList? #278

Closed
nurzhannogerbek opened this issue Apr 11, 2019 · 22 comments · Fixed by #352
Closed

OID of MechToken does not match the first in the MechTypeList? #278

nurzhannogerbek opened this issue Apr 11, 2019 · 22 comments · Fixed by #352

Comments

@nurzhannogerbek
Copy link

nurzhannogerbek commented Apr 11, 2019

Hello! I am little bit confused and need some advice.

In my Golang (version go1.11.5 linux/amd64) application I am trying to use this library to create SSO (single sign on) authorization by Kerberos and Active Directory.

I have several things:

  1. SPN name for my microservice.
  2. krb5.keytab for my microservice.
  3. krb5.conf file for kerberos client where I set REALM and KDC information.

As you can see my main.go file looks pretty simple:

func main() {
    l := log.New(os.Stderr, "", log.Ldate|log.Ltime|log.Lshortfile)

    kt, err := keytab.Load("./configurations/krb5.keytab"); if err != nil {
        l.Fatalf("Error on \"keytab.Load\": %v", err)
    }

    mux := http.NewServeMux()

    mux.Handle("/", spnego.SPNEGOKRB5Authenticate(http.HandlerFunc(controllers.GetInformation), kt, service.Logger(l)))

    log.Fatal(http.ListenAndServe(":8000", mux))
}

This code raise such ERROR:

2019/04/11 20:07:54 spnego.go:95: 172.28.88.133:2359 - SPNEGO validation error: defective token detected: OID of MechToken does not match the first in the MechTypeList
2019/04/11 20:07:54 spnego.go:95: 172.28.88.133:2359 - SPNEGO Kerberos authentication failed

OID looks like this: 1.2.840.113554.1.2.2

My MechTypeList looks like this:

MechTypeList{true false {[1.2.840.48018.1.2.2 1.2.840.113554.1.2.2 1.3.6.1.4.1.311.2.2.30 1.3.6.1.4.1.311.2.2.10] {[] 0} [96 130 6 130 6 9 42 134 72 ***] [] <nil> <nil>} {0 [] [] <nil> <nil>} <nil> <nil>}

It seems like MechToken comparison failes. What's wrong happens? What can you advice for fix this problem? Let my know if you need some additional information.

@shanmugh
Copy link
Contributor

spnego.SPNEGOKRB5Authenticate only accepts OIDSPNEGO whose MechType is 1.3.6.1.5.5.2
https://github.com/jcmturner/gokrb5/blob/master/gssapi/gssapi.go#L122-L128

The default OID token generated by curl on Linux is of MechType OIDKRB5 (1.2.840.113554.1.2.2).

I had to use the credential cache to generate a suitable OID token to satisfy the requirement. It will be great if the library can support OIBKRB5 as well.

Sample code:
`func main() {
krb5Conf, err := config.Load("/etc/krb5.conf")
if err != nil {
log.Fatalf("Unable to read krb5.conf - %s", err.Error())
}

if runtime.GOOS == "linux" {
	cacheFile = strings.Split(os.Getenv("KRB5CCNAME"), ":")[1]
}

ccache, err := credentials.LoadCCache(cacheFile)
if err != nil {
	log.Fatalf("Unable to read credential cache - %s", err.Error())
}

krbClient, err := client.NewClientFromCCache(ccache, krb5Conf)
if err != nil {
	log.Fatalf("Failed to create kerberos client - %s", err.Error())
}

    # replace with your SPN
s := spnego.SPNEGOClient(krbClient, "HTTP/<SPN>")

err = s.AcquireCred()
if err != nil {
	log.Fatalf("could not acquire client credenital: %v", err)
}

st, err := s.InitSecContext()
if err != nil {
	log.Fatalf("could not initialize context: %v", err)
}

nb, err := st.Marshal()
if err != nil {
	log.Fatal(krberror.Errorf(err, krberror.EncodingError, "could not marshal SPNEGO"))
}

fmt.Printf("%s", base64.StdEncoding.EncodeToString(nb))

}`

@nurzhannogerbek
Copy link
Author

nurzhannogerbek commented Apr 12, 2019

The problem was solved partially only by changing the code in the file:

gopkg.in\jsmturner\gokrb5.v7\spnego\negotianToken.go

Before:

if !mt.OID.Equal(n.MechTypes[0]) {
    return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "OID of MechToken does not match the first in the MechTypeList"}
}

After:

if !mt.OID.Equal(n.MechTypes[1]) {
    return false, gssapi.Status{Code: gssapi.StatusDefectiveToken, Message: "OID of MechToken does not match the first in the MechTypeList"}
}

In the same time I notice that if I make GET request to the URL from client application it raise error: HTTP 401 Unauthorized (No 'Access-Control-Allow-Origin' header is present on the requested resource). My changes works only when I make request from browser. Requests do not pass through AJAX (AXIOS). Do you have any ideas?

@nurzhannogerbek
Copy link
Author

nurzhannogerbek commented Apr 12, 2019

@shanmugh Hello! Thank you for your answer. I am little bit confused. As I understand you correctly, you recommend not to use SPNEGOKRB5Authenticate function, right?

@jcmturner
Copy link
Owner

@NogerbekNurzhan can you provide some more details on the client you are using?

The lines you have quoted from gokrb5.v7\spnego\negotianToken.go are there to implement in accordance to RFC4178. The RFC states that the initial negotiation message can optionally contain the initial mechanism token for the preferred mechanism of the client.

However looking at the Verify() method I think there may be so error in the logic. Some of the checks should not be inside the for loop.

@BryceDFisher
Copy link

@jcmturner I'm having the same problem. It appears that when authenticating from a browser with a Microsoft Server 2016 domain controller, the first MechType is the gssapi.OIDMSLegacyKRB5, but for some reason the KRB5Token.OID is unmarshalling as the gssapi.OIDKRB5 mech type.

Looking at the implementation of those types, it looks like they might be able to be used interchangeably?

@slasyz
Copy link

slasyz commented May 24, 2019

I'm having the same problem as @NogerbekNurzhan and @BryceDFisher.

RFC4178 states:

If either the initiator's preferred mechanism is not accepted by the target or this mechanism is accepted but is not the acceptor's most preferred mechanism (i.e., the MIC token exchange as described in Section 5 is required), GSS_Accept_sec_context() indicates GSS_S_CONTINUE_NEEDED. The acceptor MUST output a negotiation token containing a request-mic state.

So, if I understand it correctly, sending negotiation token with negstate=request-mic is not implemented in spnego.SPNEGOKRB5Authenticate. My current solution is to check message of status returned by nego.AcceptSecContext(&st) in handler, and if it is equal to "OID of MechToken does not match the first in the MechTypeList", then handler should send response token with following fields:

spnego.NegTokenResp{
	NegState:      3, // request-mic
	SupportedMech: gssapi.OID(gssapi.OIDKRB5),
	MechListMIC:   /* result of asn1.Marshal(st.NegTokenInit.MechTypes) */,
}

But this solution is far from perfect, and as @BryceDFisher noted, it looks like there those types can be used interchangeably (I tried to comment the equality check in spnego/negotiationToken.go and it worked), so there is no need in another negotiation round in this case.

@bwmarrin
Copy link

bwmarrin commented Aug 7, 2019

I'm also having the same issue.

I setup an example based on the httpServer.go example, I just swapped in a keytab file I generated on the AD server.

My AD server is Windows Server 2019 Core

Then I added the url of this test site to my "Local intranet" listing.

Open the site up with Google Chrome and I get the below error.

SPNEGO validation error: defective token detected: OID of MechToken does not match the first in the MechTypeList

As a quick dirty test, I commented out https://github.com/jcmturner/gokrb5/blob/master/spnego/negotiationToken.go#L174 to see what would happen - and everything worked (or seems to), and my I got the results showing my logged in user info.

If there's any added details you want that could help debug this, I'm game to help. Having this library working well would be pretty handy for me.

@bwmarrin
Copy link

@jcmturner just checking if you've had time to review any of this? It seems you've been off the radar for a bit in terms of this repo - which I understand that life gets in the way. However, without a fix to this here I'll need to fork and apply changes to my own copy of this package and that's something I'd rather not do :)

@keith6014
Copy link

@jcmturner . Wondering if you had time to review this? I too am in the same situation.

@jgiannuzzi
Copy link
Contributor

If anyone is interested, I have made fixes for these two issues

I have made a branch with both fixes: https://github.com/jgiannuzzi/gokrb5/tree/compatibility_fixes

To use it, you can either add this to your Gopkg.toml (if you use dep):

[[constraint]]
  name = "gopkg.in/jcmturner/gokrb5.v7"
  source = "github.com/jgiannuzzi/gokrb5"
  branch = "compatibility_fixes"

or add this to your go.mod (if you use Go modules):

replace gopkg.in/jcmturner/gokrb5.v7 => github.com/jgiannuzzi/gokrb5 v7.2.5-0.20191023085252-766f96a24b2b+incompatible

@jcmturner
Copy link
Owner

Hi all, sorry for being away for a while. I hope to start looking into this again but it may take me a while to get back up to speed so please be patient. I also need to fix the integration test automation that no longer seems to run.

Thanks for the contributions. I want to make sure that we solve this in the right way with regards to the RFCs etc. Therefore I want to get a good understanding of what is going on here and what has been contributed so that we produce the correct long term solution and not just a work around.

Thanks.

@bwmarrin
Copy link

bwmarrin commented Nov 23, 2019

@jcmturner phew, that's really great news. I really didn't want fork this and find myself trying to maintain it - especially since I don't know the RFC as well as you surely do.

I am happy to help with testing or otherwise if it helps. If you setup a way to donate, I'll happily buy you a beer, or ten. I know keeping up maintenance on libraries can be very hard to do as life changes.

jankaspar added a commit to armadaproject/armada that referenced this issue Nov 27, 2019
* Add minimal kerberos support.

* Clean up Kerberos implementation.

* White space.

* Try fixed gokrb5 library
jcmturner/gokrb5#278

* Add more logging.

* Add windows client kerberos support, refactor client connections.

* Fix executor config including docs.

* Add kerberos support for linux clients.

* Fix not checked error.

* Update go.mod.

* Fix url parsing for spn.

* Fix credentials file loading.

* Add env option to executor helm chart.

* Add user name suffix support for Kerberos auth.

* Remove logging of the token.

* RefreshKerberos cache file every 5 minutes.

* Remove unnecessary logging.
@jcmturner
Copy link
Owner

jcmturner commented Jan 3, 2020

Hi all, thanks very much for all your contributions and information. This is has been really helpful!

After reviewing this thread again and the PRs some have been kind enough to send I have put together the following to summarise what I think the position is and how we should proceed.

Firstly there are three issues raised here:

  1. It appears that when using Microsoft Windows 2016 as the AD KDC we see the following error:
OID of MechToken does not match the first in the MechTypeList
  1. Some clients such as older versions of curl send raw KRB5 tokens that are not wrapped in SPNEGO tokens
  2. A negotiation state of request-mic is not yet supported in gokrb5

ACTION: I will dedicate this github issue (#278) to the first issue and will be raising separate github issues to cover the other two. Please can you follow and comment on the relevant issues so we can decouple them from now on.

@jcmturner
Copy link
Owner

From now on I am focusing here on the first issue that arises when using Microsoft Windows Server 2016.

Some background: RFC4178 states that the initial negotiation message can optionally contain the initial mechanism token for the preferred mechanism of the client.
Hence the check at:

if !mt.OID.Equal(n.MechTypes[0]) {

It is this that generates the error seen.

It seems that the Microsoft Server 2016 AD DCs are doing one of two things which is the root cause:
a) It is using gssapi.OIDMSLegacyKRB5 and gssapi.OIDKRB5 interchangeably.
b) It is not ordering the mech type list in the negotiation token in order of preference. So the first item in the list may not be the prefered type that has been proactively included in the initial negotiation token.

From a purist point of view I think the true issue here is in Windows Server 2016 not following the RFC correctly. ACTION: I will look to post something on the Microsoft Community site to see if someone can take a look at that.

Nevertheless this does not help with the problem people are experiencing in the short term. Therefore I suggest gokrb5 implements a work around for now even though it is not in strict compliance with the RFC. I think this is acceptable as I don’t see any impact on security.

Given that there are two possible root causes behind Windows Server 2016 behaviour there are two possible solutions for which PRs have already been submitted for each (thanks!). For each of the root causes we have:
a) #293
b) #325

ACTION: We need to confirm which is happening to select which work around to merge. Is it “a” or “b”? I don’t have access to a Windows Server 2016 instance to check currently so I am hoping that either @jgiannuzzi or @BryceDFisher would be able to help investigate. Can you help here?

@jcmturner
Copy link
Owner

jcmturner commented Jan 3, 2020

@jcmturner
Copy link
Owner

@jgiannuzzi , @BryceDFisher I've just thought that the issue here is probably not with Windows 2016 as the DC. This will just send back Kerberos tickets. It is the client that wraps it up into SPNEGO tokens and therefore the issue is most likely with the client that is being used. I assume you are both using a browser when seeing this issue? Can you comment back with the detail of the browser and its version? Thanks!

@jgiannuzzi
Copy link
Contributor

@jcmturner I have seen this issue with Google Chrome, Microsoft Edge, and Internet Explorer 11; all on Windows 10. I unfortunately do not have access to this setup anymore, and thus to the exact versions, but I could try reproducing it in a VM. Let me know if that would be useful.

With regards to the root cause, as per #325, what I observed was that the created tokens would contain the MechToken in the MechTypes, but not always as the first one.

I think the affected browsers all use the same Microsoft API to create the SPNEGO tokens, and it is probably that API that creates them in that specific way on Windows 10. In that case, any other client using that API would potentially generate the same kind of tokens, and not just browsers.

@savely-krasovsky
Copy link

savely-krasovsky commented Jan 13, 2020

By the way I have the same issue using Windows Server 2012 as DC. Client: Windows 7 SP1; Chrome 79.

@jcmturner
Copy link
Owner

I have been able to recreate this on Windows 2012. I believe the issue is on the client side not the DC as it is the client that forms the SPNEGO ticket. You can see in the wireshark screen shot below that the order of the preferred mech types does not match the mech token being sent. That is the OID of the mech token is not the same as the first item in the mech types list. A strict interpretation of this is the client is sending its 2nd preference which does not make sense.

image

Both the KRB5 and MS KRB5 mech types are in the list so I don't think they are being used interchangeably. Therefore I don't think that PR #293 is the correct approach.
PR #325 works around this by checking that the mech type of the token is a least one of those in the mech type list. However I have to question what now is the value of this check. Therefore I think that this validation step adds little value and therefore the best work around is to just remove it.

@jgiannuzzi
Copy link
Contributor

Hey @jcmturner, thanks a lot for looking into this!

I wonder if removing that check entirely means that we don't check anywhere the OID of the MechToken. Maybe we should at least verify that it is one of our supported mechs? I guess this could be done in KRB5Token.Unmarshal or KRB5Token.Verify?

As a side note, whilst researching why this MSLegacyKRB5 OID even exists, I found this note from Microsoft, which states in point 8 that

Windows 2000 incorrectly encoded the OID for the Kerberos protocol in the supportedMech field. Rather than the OID { iso(1) member-body(2) United States(840) mit(113554) infosys(1) gssapi(2) krb5(2) }, an implementation error truncated the values at 16 bits. Therefore, the OID became { iso(1) member-body(2) United States(840) ???(48018) infosys(1) gssapi(2) krb5 (2) }.

Windows clients will fail if the accepter accepts the preferred mechanism token (1.2.840.48018.1.2.2), and produces a response token with the supportedMech being the standard Kerberos OID (1.2.840.113554.1.2.2).

My understanding is that we currently send back a static response token with the standard Kerberos OID, no matter what. So if we want to support Windows 2000, it looks like we should change that. And if we do not want to support it, then we do not need to support the MSLegacyKRB5 OID when accepting requests.

Thoughts?

@jcmturner
Copy link
Owner

Thanks @jgiannuzzi this is really useful. My view is that Windows 2000 is so old and isn't supported by MS any more so I don't see that we should be supporting it either.

As you suggest it would make sense to add to the unmarshal at...

func (m *KRB5Token) Unmarshal(b []byte) error {
var oid asn1.ObjectIdentifier
r, err := asn1.UnmarshalWithParams(b, &oid, fmt.Sprintf("application,explicit,tag:%v", 0))
if err != nil {
return fmt.Errorf("error unmarshalling KRB5Token OID: %v", err)
}
m.OID = oid

a check that this OID is 1.2.840.113554.1.2.2

@jcmturner
Copy link
Owner

I merged in #353 for this.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

9 participants