Kerberos Bug Introduced in d777e2918a77?
Daniel Jones
daniel.jones at engineerbetter.com
Tue Apr 28 10:14:15 UTC 2015
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 <weijun.wang at oracle.com> 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 <weijun.wang at oracle.com
>> <mailto:weijun.wang at oracle.com>> 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
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://mail.openjdk.org/pipermail/security-dev/attachments/20150428/80f0fabc/attachment.htm>
More information about the security-dev
mailing list