Index: net/third_party/nss/ssl/ssl3con.c |
=================================================================== |
--- net/third_party/nss/ssl/ssl3con.c (revision 152010) |
+++ net/third_party/nss/ssl/ssl3con.c (working copy) |
@@ -2285,9 +2285,10 @@ |
capRecordVersion = ((flags & ssl_SEND_FLAG_CAP_RECORD_VERSION) != 0); |
if (capRecordVersion) { |
- /* ssl_SEND_FLAG_CAP_RECORD_VERSION can only be used with |
- * TLS ClientHello. */ |
+ /* ssl_SEND_FLAG_CAP_RECORD_VERSION can only be used with the |
+ * TLS initial ClientHello. */ |
PORT_Assert(!IS_DTLS(ss)); |
+ PORT_Assert(!ss->firstHsDone); |
PORT_Assert(type == content_handshake); |
PORT_Assert(ss->ssl3.hs.ws == wait_server_hello); |
} |
@@ -4010,7 +4011,7 @@ |
int num_suites; |
int actual_count = 0; |
PRBool isTLS = PR_FALSE; |
- PRBool serverVersionKnown = PR_FALSE; |
+ PRBool requestingResume = PR_FALSE; |
PRInt32 total_exten_len = 0; |
unsigned numCompressionMethods; |
PRInt32 flags; |
@@ -4040,6 +4041,23 @@ |
return rv; |
} |
+ /* |
+ * During a renegotiation, ss->clientHelloVersion will be used again to |
+ * work around a Windows SChannel bug. Ensure that it is still enabled. |
+ */ |
+ if (ss->firstHsDone) { |
+ if (SSL3_ALL_VERSIONS_DISABLED(&ss->vrange)) { |
+ PORT_SetError(SSL_ERROR_SSL_DISABLED); |
+ return SECFailure; |
+ } |
+ |
+ if (ss->clientHelloVersion < ss->vrange.min || |
+ ss->clientHelloVersion > ss->vrange.max) { |
+ PORT_SetError(SSL_ERROR_NO_CYPHER_OVERLAP); |
+ return SECFailure; |
+ } |
+ } |
+ |
/* We ignore ss->sec.ci.sid here, and use ssl_Lookup because Lookup |
* handles expired entries and other details. |
* XXX If we've been called from ssl2_BeginClientHandshake, then |
@@ -4087,9 +4105,41 @@ |
sidOK = PR_FALSE; |
} |
- if (sidOK && ssl3_NegotiateVersion(ss, sid->version, |
- PR_FALSE) != SECSuccess) { |
- sidOK = PR_FALSE; |
+ /* TLS 1.0 (RFC 2246) Appendix E says: |
+ * Whenever a client already knows the highest protocol known to |
+ * a server (for example, when resuming a session), it should |
+ * initiate the connection in that native protocol. |
+ * So we pass sid->version to ssl3_NegotiateVersion() here, except |
+ * when renegotiating. |
+ * |
+ * Windows SChannel compares the client_version inside the RSA |
+ * EncryptedPreMasterSecret of a renegotiation with the |
+ * client_version of the initial ClientHello rather than the |
+ * ClientHello in the renegotiation. To work around this bug, we |
+ * continue to use the client_version used in the initial |
+ * ClientHello when renegotiating. |
+ */ |
+ if (sidOK) { |
+ if (ss->firstHsDone) { |
+ /* |
+ * The client_version of the initial ClientHello is still |
+ * available in ss->clientHelloVersion. Ensure that |
+ * sid->version is bounded within |
+ * [ss->vrange.min, ss->clientHelloVersion], otherwise we |
+ * can't use sid. |
+ */ |
+ if (sid->version >= ss->vrange.min && |
+ sid->version <= ss->clientHelloVersion) { |
+ ss->version = ss->clientHelloVersion; |
+ } else { |
+ sidOK = PR_FALSE; |
+ } |
+ } else { |
+ if (ssl3_NegotiateVersion(ss, sid->version, |
+ PR_FALSE) != SECSuccess) { |
+ sidOK = PR_FALSE; |
+ } |
+ } |
} |
if (!sidOK) { |
@@ -4101,7 +4151,7 @@ |
} |
if (sid) { |
- serverVersionKnown = PR_TRUE; |
+ requestingResume = PR_TRUE; |
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits ); |
/* Are we attempting a stateless session resume? */ |
@@ -4116,10 +4166,22 @@ |
} else { |
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_misses ); |
- rv = ssl3_NegotiateVersion(ss, SSL_LIBRARY_VERSION_MAX_SUPPORTED, |
- PR_TRUE); |
- if (rv != SECSuccess) |
- return rv; /* error code was set */ |
+ /* |
+ * Windows SChannel compares the client_version inside the RSA |
+ * EncryptedPreMasterSecret of a renegotiation with the |
+ * client_version of the initial ClientHello rather than the |
+ * ClientHello in the renegotiation. To work around this bug, we |
+ * continue to use the client_version used in the initial |
+ * ClientHello when renegotiating. |
+ */ |
+ if (ss->firstHsDone) { |
+ ss->version = ss->clientHelloVersion; |
+ } else { |
+ rv = ssl3_NegotiateVersion(ss, SSL_LIBRARY_VERSION_MAX_SUPPORTED, |
+ PR_TRUE); |
+ if (rv != SECSuccess) |
+ return rv; /* error code was set */ |
+ } |
sid = ssl3_NewSessionID(ss, PR_FALSE); |
if (!sid) { |
@@ -4219,6 +4281,10 @@ |
return rv; /* err set by ssl3_AppendHandshake* */ |
} |
+ if (ss->firstHsDone) { |
+ /* Work around the Windows SChannel bug described above. */ |
+ PORT_Assert(ss->version == ss->clientHelloVersion); |
+ } |
ss->clientHelloVersion = ss->version; |
if (IS_DTLS(ss)) { |
PRUint16 version; |
@@ -4338,7 +4404,7 @@ |
} |
flags = 0; |
- if (!serverVersionKnown && !IS_DTLS(ss)) { |
+ if (!ss->firstHsDone && !requestingResume && !IS_DTLS(ss)) { |
flags |= ssl_SEND_FLAG_CAP_RECORD_VERSION; |
} |
rv = ssl3_FlushHandshake(ss, flags); |