diff -pu a/nss/lib/ssl/ssl3con.c b/nss/lib/ssl/ssl3con.c --- a/nss/lib/ssl/ssl3con.c 2014-01-17 18:46:51.999581198 -0800 +++ b/nss/lib/ssl/ssl3con.c 2014-01-17 18:47:05.509804656 -0800 @@ -3473,6 +3473,9 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffe case certificate_unknown: error = SSL_ERROR_CERTIFICATE_UNKNOWN_ALERT; break; case illegal_parameter: error = SSL_ERROR_ILLEGAL_PARAMETER_ALERT;break; + case inappropriate_fallback: + error = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT; + break; /* All alerts below are TLS only. */ case unknown_ca: error = SSL_ERROR_UNKNOWN_CA_ALERT; break; @@ -4986,6 +4989,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo int num_suites; int actual_count = 0; PRBool isTLS = PR_FALSE; + PRBool requestingResume = PR_FALSE, fallbackSCSV = PR_FALSE; PRInt32 total_exten_len = 0; unsigned paddingExtensionLen; unsigned numCompressionMethods; @@ -5128,6 +5132,7 @@ ssl3_SendClientHello(sslSocket *ss, PRBo } if (sid) { + requestingResume = PR_TRUE; SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits ); PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID, @@ -5246,8 +5251,15 @@ ssl3_SendClientHello(sslSocket *ss, PRBo if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); } return SECFailure; /* count_cipher_suites has set error code. */ } + + fallbackSCSV = ss->opt.enableFallbackSCSV && (!requestingResume || + ss->version < sid->version); + /* make room for SCSV */ if (ss->ssl3.hs.sendingSCSV) { - ++num_suites; /* make room for SCSV */ + ++num_suites; + } + if (fallbackSCSV) { + ++num_suites; } /* count compression methods */ @@ -5353,6 +5365,15 @@ ssl3_SendClientHello(sslSocket *ss, PRBo } actual_count++; } + if (fallbackSCSV) { + rv = ssl3_AppendHandshakeNumber(ss, TLS_FALLBACK_SCSV, + sizeof(ssl3CipherSuite)); + if (rv != SECSuccess) { + if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); } + return rv; /* err set by ssl3_AppendHandshake* */ + } + actual_count++; + } for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) { ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i]; if (config_match(suite, ss->ssl3.policy, PR_TRUE, &ss->vrange)) { @@ -8084,6 +8105,19 @@ ssl3_HandleClientHello(sslSocket *ss, SS goto loser; /* malformed */ } + /* If the ClientHello version is less than our maximum version, check for a + * TLS_FALLBACK_SCSV and reject the connection if found. */ + if (ss->vrange.max > ss->clientHelloVersion) { + for (i = 0; i + 1 < suites.len; i += 2) { + PRUint16 suite_i = (suites.data[i] << 8) | suites.data[i + 1]; + if (suite_i != TLS_FALLBACK_SCSV) + continue; + desc = inappropriate_fallback; + errCode = SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT; + goto alert_loser; + } + } + /* grab the list of compression methods. */ rv = ssl3_ConsumeHandshakeVariable(ss, &comps, 1, &b, &length); if (rv != SECSuccess) { diff -pu a/nss/lib/ssl/ssl3prot.h b/nss/lib/ssl/ssl3prot.h --- a/nss/lib/ssl/ssl3prot.h 2014-01-17 17:59:03.242109996 -0800 +++ b/nss/lib/ssl/ssl3prot.h 2014-01-17 18:47:05.509804656 -0800 @@ -98,6 +98,7 @@ typedef enum { protocol_version = 70, insufficient_security = 71, internal_error = 80, + inappropriate_fallback = 86, /* could also be sent for SSLv3 */ user_canceled = 90, no_renegotiation = 100, diff -pu a/nss/lib/ssl/sslerr.h b/nss/lib/ssl/sslerr.h --- a/nss/lib/ssl/sslerr.h 2014-01-17 17:59:03.242109996 -0800 +++ b/nss/lib/ssl/sslerr.h 2014-01-17 18:47:05.509804656 -0800 @@ -196,6 +196,7 @@ SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM SSL_ERROR_BAD_CHANNEL_ID_DATA = (SSL_ERROR_BASE + 129), SSL_ERROR_INVALID_CHANNEL_ID_KEY = (SSL_ERROR_BASE + 130), SSL_ERROR_GET_CHANNEL_ID_FAILED = (SSL_ERROR_BASE + 131), +SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT = (SSL_ERROR_BASE + 132), SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */ } SSLErrorCodes; diff -pu a/nss/lib/ssl/SSLerrs.h b/nss/lib/ssl/SSLerrs.h --- a/nss/lib/ssl/SSLerrs.h 2014-01-17 17:59:03.242109996 -0800 +++ b/nss/lib/ssl/SSLerrs.h 2014-01-17 18:47:05.509804656 -0800 @@ -421,3 +421,8 @@ ER3(SSL_ERROR_INVALID_CHANNEL_ID_KEY, (S ER3(SSL_ERROR_GET_CHANNEL_ID_FAILED, (SSL_ERROR_BASE + 131), "The application could not get a TLS Channel ID.") + +ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 132), +"The connection was using a lesser TLS version as a result of a previous" +" handshake failure, but the server indicated that it should not have been" +" needed.") diff -pu a/nss/lib/ssl/ssl.h b/nss/lib/ssl/ssl.h --- a/nss/lib/ssl/ssl.h 2014-01-17 18:46:51.999581198 -0800 +++ b/nss/lib/ssl/ssl.h 2014-01-17 18:48:54.971613341 -0800 @@ -183,6 +183,8 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF /* Request Signed Certificate Timestamps via TLS extension (client) */ #define SSL_ENABLE_SIGNED_CERT_TIMESTAMPS 27 +#define SSL_ENABLE_FALLBACK_SCSV 28 /* Send fallback SCSV in + * handshakes. */ #ifdef SSL_DEPRECATED_FUNCTION /* Old deprecated function names */ diff -pu a/nss/lib/ssl/sslimpl.h b/nss/lib/ssl/sslimpl.h --- a/nss/lib/ssl/sslimpl.h 2014-01-17 18:46:51.999581198 -0800 +++ b/nss/lib/ssl/sslimpl.h 2014-01-17 18:51:17.963962287 -0800 @@ -338,6 +338,7 @@ typedef struct sslOptionsStr { unsigned int enableNPN : 1; /* 26 */ unsigned int enableALPN : 1; /* 27 */ unsigned int enableSignedCertTimestamps : 1; /* 28 */ + unsigned int enableFallbackSCSV : 1; /* 29 */ } sslOptions; typedef enum { sslHandshakingUndetermined = 0, diff -pu a/nss/lib/ssl/sslproto.h b/nss/lib/ssl/sslproto.h --- a/nss/lib/ssl/sslproto.h 2014-01-17 18:10:16.793281867 -0800 +++ b/nss/lib/ssl/sslproto.h 2014-01-17 18:47:05.509804656 -0800 @@ -172,6 +172,11 @@ */ #define TLS_EMPTY_RENEGOTIATION_INFO_SCSV 0x00FF +/* TLS_FALLBACK_SCSV is a signaling cipher suite value that indicates that a + * handshake is the result of TLS version fallback. This value is not IANA + * assigned. */ +#define TLS_FALLBACK_SCSV 0x5600 + /* Cipher Suite Values starting with 0xC000 are defined in informational * RFCs. */ diff -pu a/nss/lib/ssl/sslsock.c b/nss/lib/ssl/sslsock.c --- a/nss/lib/ssl/sslsock.c 2014-01-17 18:46:52.009581364 -0800 +++ b/nss/lib/ssl/sslsock.c 2014-01-17 18:59:17.931852364 -0800 @@ -88,7 +88,8 @@ static sslOptions ssl_defaults = { PR_FALSE, /* enableOCSPStapling */ PR_TRUE, /* enableNPN */ PR_FALSE, /* enableALPN */ - PR_FALSE /* enableSignedCertTimestamps */ + PR_FALSE, /* enableSignedCertTimestamps */ + PR_FALSE /* enableFallbackSCSV */ }; /* @@ -792,6 +793,10 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh ss->opt.enableSignedCertTimestamps = on; break; + case SSL_ENABLE_FALLBACK_SCSV: + ss->opt.enableFallbackSCSV = on; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); rv = SECFailure; @@ -867,6 +872,7 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS: on = ss->opt.enableSignedCertTimestamps; break; + case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -933,6 +939,9 @@ SSL_OptionGetDefault(PRInt32 which, PRBo case SSL_ENABLE_SIGNED_CERT_TIMESTAMPS: on = ssl_defaults.enableSignedCertTimestamps; break; + case SSL_ENABLE_FALLBACK_SCSV: + on = ssl_defaults.enableFallbackSCSV; + break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); @@ -1112,6 +1121,10 @@ SSL_OptionSetDefault(PRInt32 which, PRBo ssl_defaults.enableSignedCertTimestamps = on; break; + case SSL_ENABLE_FALLBACK_SCSV: + ssl_defaults.enableFallbackSCSV = on; + break; + default: PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure;