JDK-8012082 : SASL: auth-conf negotiated, but unencrypted data is accepted, reset to unencrypt
  • Type: Bug
  • Component: security-libs
  • Sub-Component: javax.security
  • Affected Version: 6,7,8
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • Submitted: 2013-03-11
  • Updated: 2013-06-26
  • Resolved: 2013-05-01
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 7 JDK 8
7u40Fixed 8 b89Fixed
Description
FULL PRODUCT VERSION :
java version  " 1.7.0_10 " 
Java(TM) SE Runtime Environment (build 1.7.0_10-b18)
Java HotSpot(TM) 64-Bit Server VM (build 23.6-b04, mixed mode)


ADDITIONAL OS VERSION INFORMATION :
Linux ip-10-197-49-232 3.2.0-35-virtual #55-Ubuntu SMP Wed Dec 5 18:02:05 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux

A DESCRIPTION OF THE PROBLEM :
In the Java SASL code library: a client or a server can cause the connection to not  be encrypted even though encryption was negotiated in the connection establishment.

If auth-conf (encryption of data transmitted through the connection) was negotiated for the QOP (quality of protection) for the connection, the server or the client will still accept unencrypted data and further more, if unencrypted data is received, it will cause the connection state to be reset to *send* and expect unencrypted data.

The bug is in the Java SASL library code. The details are below. In tracing this bug, I used the source code from OpenJDK 7u6 - build b24 (http://www.java.net/download/openjdk/jdk7u6/promoted/b24/openjdk-7u6-fcs-src-b24-28_aug_2012.zip).  All code snip-its given in this bug report are from that source. I think I am justified in doing so (using the OpenJDK source to trace a bug in the Oracle JDK) because of the following text on the OpenJDK faq site (http://openjdk.java.net/faq/):

--------------------
Oracle JDK and OpenJDK
Is Oracle JDK based on OpenJDK?

Yes. Oracle JDK is based on the OpenJDK source code. In addition, it contains closed-source components. The final result is licensed under a Binary Code License.
--------------------

The bug is reproducible using OpenJDK 7 or Oracle JDK 7.

Analysis of bug:

The problem is that a class variable is passed into both org.ietf.jgss.GSSContext.wrap() and org.ietf.jgss.GSSContext.unwrap() (the Java equivalents for the GSS_Wrap and GSS_Unwrap calls from the GSS-API standard: RFC 2743 - http://tools.ietf.org/html/rfc2743). In the wrap call, this variable is used for an input to tell the library that the data should be encrypted. In the unwrap call, it is used for an output value. It tells the caller whether the buffer was encrypted or not as various other information about the buffer. The caller is then supposed to check this flag and decide whether or not to keep/use the data. But in the Java SASL library, the flag is not checked (problem 1). In addition, since it is a class variable that is also passed to the wrap method and because it was not reset to the values negotiated during connection setup as to whether encryption should be done or not, after the client sends a non-encrypted message, all further messages from the server to the client will also be non-encrypted (problem 2). Note the code for the client side is very similar. In fact the server and client side SASL classes both inherit from the same base class and the base class defines the wrap and unwrap calls. So, the client code has the same problem.

  From the GSS-API library code: Here is the description from org.ietf.jgss.GSSContext.unwrap() about the msgProp parameter:

     * @param msgProp upon return from the method, this object will contain
     * the applied QOP, the privacy state of the message, and supplementary
     * information stating if the token was a duplicate, old, out of
     * sequence or arriving after a gap.
                                                                                
 *  *  *
                                                                                
    public byte [] unwrap(byte[] inBuf, int offset, int len,
                          MessageProp msgProp) throws GSSException;

Here is the description from org.ietf.jgss.GSSContext.wrap() about the msgProp parameter:

     * @param msgProp instance of MessageProp that is used by the
     * application to set the desired QOP and privacy state. Set the
     * desired QOP to 0 to request the default QOP. Upon return from this
     * method, this object will contain the the actual privacy state that
     * was applied to the message by the underlying mechanism.

 *  *  *

    public byte[] wrap(byte inBuf[], int offset, int len,
                       MessageProp msgProp) throws GSSException;

Note that QOP in this library (the GSS-API library) does not mean the same thing as the QOP in the SASL library. Here we do not care about the QOP, we care about the  " privacy state "  (meaning encryption). The (GSS-API) QOP will always correctly be set to 0 (which means the default QOP will be used which just means that the library will choose its own encryption and signing methods).

(both code snip-its are from: jdk/src/share/classes/org/ietf/jgss/GSSContext.java)


Now, back to the SASL library code:  from: jdk/src/share/classes/com/sun/security/sasl/gsskerb/GssKrb5Base.java:

abstract class GssKrb5Base extends AbstractSaslImpl {
 * * *
    protected GSSContext secCtx = null;
    protected MessageProp msgProp;              // QOP and privacy for unwrap
 * * *
    public byte[] unwrap(byte[] incoming, int start, int len)
        throws SaslException {
 * * *
        try {
            byte[] answer = secCtx.unwrap(incoming, start, len, msgProp);
            if (logger.isLoggable(Level.FINEST)) {
                traceOutput(myClassName,  " KRB501:Unwrap " ,  " incoming:  " ,
                    incoming, start, len);
                traceOutput(myClassName,  " KRB502:Unwrap " ,  " unwrapped:  " ,
                    answer, 0, answer.length);
            }
            return answer;
        } catch (GSSException e) {
            throw new SaslException( " Problems unwrapping SASL buffer " , e);
        }
    }

 * * *

    public byte[] wrap(byte[] outgoing, int start, int len) throws SaslException {
 * * *
        // Generate GSS token
        try {
            byte[] answer = secCtx.wrap(outgoing, start, len, msgProp);
            if (logger.isLoggable(Level.FINEST)) {
                traceOutput(myClassName,  " KRB503:Wrap " ,  " outgoing:  " ,
                    outgoing, start, len);
                traceOutput(myClassName,  " KRB504:Wrap " ,  " wrapped:  " ,
                    answer, 0, answer.length);
            }
            return answer;

        } catch (GSSException e) {
            throw new SaslException( " Problem performing GSS wrap " , e);
        }
    }

from: jdk/src/share/classes/com/sun/security/sasl/gsskerb/GssKrb5Server.java:

final class GssKrb5Server extends GssKrb5Base implements SaslServer {
 * * *
    private byte[] doHandshake2(byte[] responseData) throws SaslException {
 * * *
            if ((selectedQop&PRIVACY_PROTECTION) != 0) {
                privacy = true;
                integrity = true;
            } else if ((selectedQop&INTEGRITY_ONLY_PROTECTION) != 0) {
                integrity = true;
            }
            msgProp = new MessageProp(JGSS_QOP, privacy);

The function com.sun.security.sasl.gsskerb.GssKrb5Server.doHandshake2() is called at the end of the SASL negotiation and is used to perform the SASL QOP negotiation.  In the SASL library, the QOP specifies whether the data sent on a connection should be encrypted and integrity protected, just integrity protected, or neither encrypted or integrity protected.

Next a grep through the SASL code to see anywhere else where the msgProp class variable is used:

xyz@ubuntu:~/trees/java/openjdk-7u6-build24/jdk/src/share/classes/com/sun/security/sasl/gsskerb$ find . -name  " *.java "  |xargs grep  msgProp
./GssKrb5Server.java:            msgProp = new MessageProp(JGSS_QOP, privacy);
./GssKrb5Base.java:    protected MessageProp msgProp;              // QOP and privacy for unwrap
./GssKrb5Base.java:            byte[] answer = secCtx.unwrap(incoming, start, len, msgProp);
./GssKrb5Base.java:            byte[] answer = secCtx.wrap(outgoing, start, len, msgProp);
./GssKrb5Client.java:            msgProp = new MessageProp(JGSS_QOP, privacy);

Note that there is no checking of this variable after a GSSContext.wrap() call and no resetting before an GSSContext.unwrap() call.  Thus, if the server (or client) receives an unencrypted buffer they will accept it and in turn start sending unencrypted data on that connection.


STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Write some code to set up a SASL connection and have auth-conf be negotiated as the QOP (make it the only QOP supported by the client and the server).  Have the client or the server send an unencrypted message.  (I used a python client for this purpose.)  Notice that the other party will accept the unencrypted buffer and then start sending all data unencrypted.  Note that both the Java SASL server and client are vulnerable to this bug.

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The Java SASL client or server should reject the unencrypted message (or at least provide some way for the upper layer to reject it), and it should not send its data unencrypted.
ACTUAL -
The Java SASL client or server will accept the unencrypted buffer and then start sending all data unencrypted.  Note that both the Java SASL server and client are vulnerable to this bug.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
No error messages.  That is the problem.  The server and client should *not* accept unencrypted data when encryption was negotiated for the connection.  And furthermore, they should *not* send all of their data unencrypted just because their peer sent some unencrypted data.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
/* Sorry, this part would be too lengthy.  If needed, I can provide it, just not in the initial filling...  Let me know if this is absolutely necessary.  The python client that I spoke of was part of our product. */
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
No real workaround.
Comments
sun/security/krb5/auto/SaslGSS.java passed from B89 - B95 in JDK8
25-06-2013