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

Reply via email to