Hi Weijun, Thanks for your help. Sorry it's taken me a while to reply, I was give other priorities over looking into this issue.
Your suggested workaround does indeed work. I've switched elements of the byte array around with a debugger connected to the app, and that solves the problem. Thankfully the position of the elements in the ticket seems to be predictable. It's a brittle and low-level fix, but at least it works, so thanks! I noticed in the bug report Internet Explorer is mentioned. We see the same behaviour with IE, Firefox and Chrome. On Thu, Apr 23, 2015 at 3:14 PM, Weijun Wang <[email protected]> wrote: > Hi Daniel > > Sorry for the trouble. > > On 4/23/2015 7:25 PM, Daniel Jones wrote: > >> Hi all, >> >> Thanks to everyone taking the time to look into this. >> >> Before I get into the detail of the technical issue, can anyone >> postulate as to *how quickly fixes tend to make it into releases of >> OpenJDK*? Are we talking days, weeks, or months? I'm just trying to >> advise my client on the best mitigation strategy until the issue is >> resolved. >> > > Oracle does not releases binaries for OpenJDK, you only get the source > codes on http://hg.openjdk.java.net. Once we made a fix, the changeset > will be there. So if you build your own JDK, that's quite fast. > > If you use Oracle JDK, it has a release schedule at > > https://www.java.com/en/download/faq/release_dates.xml > > So it's months. > > >> The Spring code in question actually changed this morning to throw a >> more useful error: >> >> https://github.com/spring-projects/spring-security-kerberos/commit/f046bd7c69d6dad74eb06a7651cd68060b31ff6f >> >> On the other hand, your program has >> context.acceptSecContext(kerberosTicket, 0, >> kerberosTicket.length); >> String user = context.getSrcName().toString(); >> which is not standard. The acceptSecContext call should be in a loop >> until a context is established, and then you can call getSrcName(). >> > > If you look at the class spec in > > > > http://hg.openjdk.java.net/jdk8u/jdk8u-dev/jdk/file/debb4ce14251/src/share/classes/org/ietf/jgss/GSSContext.java > > the formal context establishment should be like > > * while (!context.isEstablished()) { > * inToken = readToken(); > * outToken = context.acceptSecContext(inToken, 0, > * inToken.length); > * // send output token if generated > * if (outToken != null) > * sendToken(outToken); > * } > > In your case, before 8048194, it just happens that the while loop only > runs once. > > What is the client here? A browser? > > On the other hand, I am looking at your code and see if there is a > workaround. Can you capture some packets and send to me? Especially I'd > like the content of "kerberosTicket" in > > byte[] responseToken = context.acceptSecContext(kerberosTicket, 0, > kerberosTicket.length); > > In my experiment, the first 48 bytes look like this: > > 0000: 60 82 02 2D 06 06 2B 06 01 05 05 02 A0 82 02 21 > 0010: 30 82 02 1D A0 18 30 16 06 09 2A 86 48 82 F7 12 > 0020: 01 02 02 06 09 2A 86 48 86 F7 12 01 02 02 A1 04 > > Here, the OIDs are the 11 bytes from 0x18 and 0x23 starting with 06 09. > You can see the only difference between the 2 OIDs are 82 (at 0x1D) and 86 > (at 0x28). If you swap them, Java will happily accept it. > > Please note this is only a workaround and the real fix should be inside > JDK. If your packet is bigger the 2nd byte might be 83 and you need to look > a bit further for the OIDs (still starting with 06 09). > > Thanks > Max > > >> >> I'm not sure that this would work. I'm not at all familiar with what is >> best practice in handling Kerberos tickets, but let me explain what >> happens at present in u40: >> >> 1. Spring's SunJaasKerberosTicketValidator gets a GSSContextImpl, and >> calls acceptSecContext on it. >> 2. The GSSContextImpl calls an overloaded accetSecContext method, which >> successfully creates a SpNegoContext and assigns it as the mechCtxt >> member. >> 3. GSSContextImpl#acceptSecContext then calls >> SpNegoContext#acceptSecContext >> 4. In SpNegoContext#acceptSecContext we have the new functionality that >> only looks at the top item of the list of OIDs from the service >> ticket. >> 5. The inner mechContext of the SpNegoContext is not set. >> 6. We return back to the Spring code, with a GSSContextImpl wrapping a >> SpNegoContext >> 7. Spring calls GSSContextImpl#getSrcName(), which delegates to >> SpNegoContext#getSrcName(), which returns null as its mechContext >> member is null. >> > > Yes. > > >> >> Spring passes the whole ticket into GSSContextImpl, and doesn't know >> about OIDs and the list of acceptable mechanisms. It seems like it's a >> responsibility of either SpNegoContext or GSSContextImpl to know about >> this list and iterate over it. >> > > Yes. Unfortunately Java does not understand that 1st OID now. > > >> If the Spring code were to attempt a repeat, how should it know that the >> inner context was not set? What action should it perform next? It's >> passed in the ticket it knows about, and got back a populated byte[], >> without any exceptions. What would it use to determine that one of the >> side affects of GSSContextImpl#acceptSecContext hasn't succeeded? >> > > Like what I quoted above, loop until context.isEstablished() is true. Of > course, this means the client side must also coded formally to loop on its > side. I don't know what the client is and not sure if its application > protocol has this loop defined. > > >> Does the above make sense? Please do get in touch if I an provide any >> other assistance in helping with the issue, and thanks again to everyone >> looking into it. >> > > Thanks. > > >> >> >> On Thu, Apr 23, 2015 at 1:58 AM, Weijun Wang <[email protected] >> <mailto:[email protected]>> wrote: >> >> Hi Daniel >> >> I've read more about your bug report and know what's happening. >> >> You are proposing 2 OIDs, [1.2.840.48018.1.2.2, >> 1.2.840.113554.1.2.2], 1st one being Microsoft's own krb5 OID and >> 2nd the standard one. Java only understands the 2nd one but before >> that changeset it blindly accepts the mechToken without looking at >> the OID. Since it's also krb5, the mechToken is processed correctly >> and everything goes on. After the changeset, it does not recognize >> the OID anymore and asks the client to send another mechToken with >> 1.2.840.113554.1.2.2. >> >> I believe a lot of people is using the Microsoft OID. I will see if >> it's possible to recognize both OIDs. >> >> On the other hand, your program has >> >> context.acceptSecContext(kerberosTicket, 0, >> kerberosTicket.length); >> String user = context.getSrcName().toString(); >> >> which is not standard. The acceptSecContext call should be in a loop >> until a context is established, and then you can call getSrcName(). >> Can you try that? Hopefully after the client sees the server request >> for a 1.2.840.113554.1.2.2 mechToken it can send one and the server >> can go on. >> >> Thanks >> Max >> >> >> On 4/23/2015 7:22 AM, Weijun Wang wrote: >> >> Hi Daniel >> >> Thanks for the report. >> >> In fact, the bug behind the changeset you mentioned -- 8048194 >> -- was >> just meant to make your case work. Before that, the server blindly >> accepts the mechToken and process it no matter if the OID is >> supported. >> Now it first looks at the OID and accept the token if it >> supports the >> OID; otherwise, only the negotiated result (its supported OID) >> is sent >> back, and waits for the client sending the correct mechToken in >> the next >> round. >> >> It seems the logic above is not implemented correctly, can you >> show me >> the full stack of your NullPointerException? If it includes any >> sensitive info you can write me privately. >> >> Thanks >> Max >> >> On 4/23/2015 12:21 AM, Rob McKenna wrote: >> >> Hi Daniel, >> >> Thanks for the report, I'm cc'ing the security-dev alias. >> >> -Rob >> >> On 22/04/15 13:10, Daniel Jones wrote: >> >> Hi all, >> >> Apologies if this is the wrong mailing list - please >> direct me to the >> correct one if so. >> >> I believe I've found a bug in OpenJDK 1.8.0_40, >> introduced in commit >> d777e2918a77: >> >> http://hg.openjdk.java.net/jdk8u/jdk8u40/jdk/file/d777e2918a77/src/share/classes/sun/security/jgss/spnego/SpNegoContext.java >> >> >> >> The change introduced on line 548 means that an >> authentication >> mechanism is >> only accepted if the OID of the mechanism desired is the >> *first* in the >> list of mechanisms specified as acceptable in the >> incoming ticket. >> >> In the case of my current client their service tickets >> are specifying 4 >> acceptable mechanism OIDs, but the only available >> mechanism's OID >> appears >> second on that list. So whilst the server *can *satisfy >> the ticket, the >> code change on line 548 prevents this from happening. >> >> Using the same server code, the same Kerberos KDC, and >> OpenJDK 1.8.0_31, >> everything works. Changing only the JDK results in the >> mechContext not >> being properly populated, which in turn causes a >> NullPointerException >> from >> some Spring Security Kerberos code. >> >> Has anyone else experienced this? >> >> >> >> >> >> >> -- >> Regards, >> >> Daniel Jones >> EngineerBetter.com >> > -- Regards, Daniel Jones EngineerBetter.com
