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