Kerberos Bug Introduced in d777e2918a77?

Weijun Wang weijun.wang at oracle.com
Thu Apr 23 14:14:39 UTC 2015


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


More information about the security-dev mailing list