Bug 1036735, backported patch. Add TLS_FALLBACK_SCSV cipher suite to version fallback connections (draft-ietf-tls-downgrade-scsv). Original author: Adam Langley (agl). r=wtc,martin.thomson
--- a/lib/ssl/SSLerrs.h
+++ b/lib/ssl/SSLerrs.h
@@ -413,8 +413,12 @@ ER3(SSL_ERROR_DIGEST_FAILURE, (SSL_ERROR
ER3(SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM, (SSL_ERROR_BASE + 128),
"Incorrect signature algorithm specified in a digitally-signed element.")
ER3(SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK, (SSL_ERROR_BASE + 129),
"The next protocol negotiation extension was enabled, but the callback was cleared prior to being needed.")
ER3(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL, (SSL_ERROR_BASE + 130),
"The server supports no protocols that the client advertises in the ALPN extension.")
+
+ER3(SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT, (SSL_ERROR_BASE + 131),
+"The server rejected the handshake because the client downgraded to a lower "
+"TLS version than the server supports.")
--- a/lib/ssl/ssl.h
+++ b/lib/ssl/ssl.h
@@ -177,16 +177,19 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
* the application layer protocol negotiation; otherwise, the ALPN extension
* will not be negotiated. ALPN is not negotiated for renegotiation handshakes,
* even though the ALPN specification defines a way to use ALPN during
* renegotiations. SSL_ENABLE_ALPN is currently disabled by default, but this
* may change in future versions.
*/
#define SSL_ENABLE_ALPN 26
+#define SSL_ENABLE_FALLBACK_SCSV 28 /* Send fallback SCSV in
+ * handshakes. */
+
#ifdef SSL_DEPRECATED_FUNCTION
/* Old deprecated function names */
SSL_IMPORT SECStatus SSL_Enable(PRFileDesc *fd, int option, PRBool on);
SSL_IMPORT SECStatus SSL_EnableDefault(int option, PRBool on);
#endif
/* New function names */
SSL_IMPORT SECStatus SSL_OptionSet(PRFileDesc *fd, PRInt32 option, PRBool on);
--- a/lib/ssl/ssl3con.c
+++ b/lib/ssl/ssl3con.c
@@ -3347,16 +3347,19 @@ ssl3_HandleAlert(sslSocket *ss, sslBuffe
case no_certificate: error = SSL_ERROR_NO_CERTIFICATE; break;
case bad_certificate: error = SSL_ERROR_BAD_CERT_ALERT; break;
case unsupported_certificate:error = SSL_ERROR_UNSUPPORTED_CERT_ALERT;break;
case certificate_revoked: error = SSL_ERROR_REVOKED_CERT_ALERT; break;
case certificate_expired: error = SSL_ERROR_EXPIRED_CERT_ALERT; break;
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;
case access_denied: error = SSL_ERROR_ACCESS_DENIED_ALERT; break;
case decode_error: error = SSL_ERROR_DECODE_ERROR_ALERT; break;
case decrypt_error: error = SSL_ERROR_DECRYPT_ERROR_ALERT; break;
case export_restriction: error = SSL_ERROR_EXPORT_RESTRICTION_ALERT;
break;
@@ -4868,16 +4871,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
sslSessionID * sid;
ssl3CipherSpec * cwSpec;
SECStatus rv;
int i;
int length;
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;
PRInt32 flags;
SSL_TRC(3, ("%d: SSL3[%d]: send client_hello handshake", SSL_GETPID(),
ss->fd));
@@ -5010,16 +5014,17 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
if (ss->sec.uncache)
(*ss->sec.uncache)(sid);
ssl_FreeSID(sid);
sid = NULL;
}
}
if (sid) {
+ requestingResume = PR_TRUE;
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_hits );
PRINT_BUF(4, (ss, "client, found session-id:", sid->u.ssl3.sessionID,
sid->u.ssl3.sessionIDLength));
ss->ssl3.policy = sid->u.ssl3.policy;
} else {
SSL_AtomicIncrementLong(& ssl3stats.sch_sid_cache_misses );
@@ -5124,18 +5129,25 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
}
/* how many suites are permitted by policy and user preference? */
num_suites = count_cipher_suites(ss, ss->ssl3.policy, PR_TRUE);
if (!num_suites) {
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 */
numCompressionMethods = 0;
for (i = 0; i < compressionMethodsCount; i++) {
if (compressionEnabled(ss, compressions[i]))
numCompressionMethods++;
}
@@ -5231,16 +5243,25 @@ ssl3_SendClientHello(sslSocket *ss, PRBo
rv = ssl3_AppendHandshakeNumber(ss, TLS_EMPTY_RENEGOTIATION_INFO_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++;
}
+ 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)) {
actual_count++;
if (actual_count > num_suites) {
if (sid->u.ssl3.lock) { PR_RWLock_Unlock(sid->u.ssl3.lock); }
/* set error card removal/insertion error */
PORT_SetError(SSL_ERROR_TOKEN_INSERTION_REMOVAL);
@@ -7706,16 +7727,29 @@ ssl3_HandleClientHello(sslSocket *ss, SS
}
/* grab the list of cipher suites. */
rv = ssl3_ConsumeHandshakeVariable(ss, &suites, 2, &b, &length);
if (rv != SECSuccess) {
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) {
goto loser; /* malformed */
}
desc = handshake_failure;
--- a/lib/ssl/ssl3prot.h
+++ b/lib/ssl/ssl3prot.h
@@ -93,16 +93,17 @@ typedef enum {
unknown_ca = 48,
access_denied = 49,
decode_error = 50,
decrypt_error = 51,
export_restriction = 60,
protocol_version = 70,
insufficient_security = 71,
internal_error = 80,
+ inappropriate_fallback = 86, /* could also be sent for SSLv3 */
user_canceled = 90,
no_renegotiation = 100,
/* Alerts for client hello extensions */
unsupported_extension = 110,
certificate_unobtainable = 111,
unrecognized_name = 112,
bad_certificate_status_response = 113,
--- a/lib/ssl/sslerr.h
+++ b/lib/ssl/sslerr.h
@@ -191,13 +191,15 @@ SSL_ERROR_RX_UNEXPECTED_CERT_STATUS
SSL_ERROR_UNSUPPORTED_HASH_ALGORITHM = (SSL_ERROR_BASE + 126),
SSL_ERROR_DIGEST_FAILURE = (SSL_ERROR_BASE + 127),
SSL_ERROR_INCORRECT_SIGNATURE_ALGORITHM = (SSL_ERROR_BASE + 128),
SSL_ERROR_NEXT_PROTOCOL_NO_CALLBACK = (SSL_ERROR_BASE + 129),
SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL = (SSL_ERROR_BASE + 130),
+SSL_ERROR_INAPPROPRIATE_FALLBACK_ALERT = (SSL_ERROR_BASE + 131),
+
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */
#endif /* __SSL_ERR_H_ */
--- a/lib/ssl/sslimpl.h
+++ b/lib/ssl/sslimpl.h
@@ -321,16 +321,18 @@ typedef struct sslOptionsStr {
unsigned int enableDeflate : 1; /* 19 */
unsigned int enableRenegotiation : 2; /* 20-21 */
unsigned int requireSafeNegotiation : 1; /* 22 */
unsigned int enableFalseStart : 1; /* 23 */
unsigned int cbcRandomIV : 1; /* 24 */
unsigned int enableOCSPStapling : 1; /* 25 */
unsigned int enableNPN : 1; /* 26 */
unsigned int enableALPN : 1; /* 27 */
+ unsigned int dummy : 1; /* 28 */
+ unsigned int enableFallbackSCSV : 1; /* 29 */
} sslOptions;
typedef enum { sslHandshakingUndetermined = 0,
sslHandshakingAsClient,
sslHandshakingAsServer
} sslHandshakingType;
typedef struct sslServerCertsStr {
--- a/lib/ssl/sslproto.h
+++ b/lib/ssl/sslproto.h
@@ -203,16 +203,21 @@
#define TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 0x00A2
/* TLS "Signaling Cipher Suite Value" (SCSV). May be requested by client.
* Must NEVER be chosen by server. SSL 3.0 server acknowledges by sending
* back an empty Renegotiation Info (RI) server hello extension.
*/
#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.
+ */
+#define TLS_FALLBACK_SCSV 0x5600
+
/* Cipher Suite Values starting with 0xC000 are defined in informational
* RFCs.
*/
#define TLS_ECDH_ECDSA_WITH_NULL_SHA 0xC001
#define TLS_ECDH_ECDSA_WITH_RC4_128_SHA 0xC002
#define TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA 0xC003
#define TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA 0xC004
#define TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA 0xC005
--- a/lib/ssl/sslsock.c
+++ b/lib/ssl/sslsock.c
@@ -75,17 +75,19 @@ static sslOptions ssl_defaults = {
PR_FALSE, /* enableSessionTickets */
PR_FALSE, /* enableDeflate */
2, /* enableRenegotiation (default: requires extension) */
PR_FALSE, /* requireSafeNegotiation */
PR_FALSE, /* enableFalseStart */
PR_TRUE, /* cbcRandomIV */
PR_FALSE, /* enableOCSPStapling */
PR_TRUE, /* enableNPN */
- PR_FALSE /* enableALPN */
+ PR_FALSE, /* enableALPN */
+ PR_FALSE, /* dummy */
+ PR_FALSE /* enableFallbackSCSV */
};
/*
* default range of enabled SSL/TLS protocols
*/
static SSLVersionRange versions_defaults_stream = {
SSL_LIBRARY_VERSION_3_0,
SSL_LIBRARY_VERSION_TLS_1_0
@@ -779,16 +781,20 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
case SSL_ENABLE_NPN:
ss->opt.enableNPN = on;
break;
case SSL_ENABLE_ALPN:
ss->opt.enableALPN = on;
break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ ss->opt.enableFallbackSCSV = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
/* We can't use the macros for releasing the locks here,
* because ss->opt.noLocks might have changed just above.
* We must release these locks (monitors) here, if we aquired them above,
@@ -851,16 +857,17 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
on = ss->opt.enableRenegotiation; break;
case SSL_REQUIRE_SAFE_NEGOTIATION:
on = ss->opt.requireSafeNegotiation; break;
case SSL_ENABLE_FALSE_START: on = ss->opt.enableFalseStart; break;
case SSL_CBC_RANDOM_IV: on = ss->opt.cbcRandomIV; break;
case SSL_ENABLE_OCSP_STAPLING: on = ss->opt.enableOCSPStapling; break;
case SSL_ENABLE_NPN: on = ss->opt.enableNPN; break;
case SSL_ENABLE_ALPN: on = ss->opt.enableALPN; break;
+ case SSL_ENABLE_FALLBACK_SCSV: on = ss->opt.enableFallbackSCSV; break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
ssl_ReleaseSSL3HandshakeLock(ss);
ssl_Release1stHandshakeLock(ss);
@@ -914,16 +921,19 @@ SSL_OptionGetDefault(PRInt32 which, PRBo
break;
case SSL_ENABLE_FALSE_START: on = ssl_defaults.enableFalseStart; break;
case SSL_CBC_RANDOM_IV: on = ssl_defaults.cbcRandomIV; break;
case SSL_ENABLE_OCSP_STAPLING:
on = ssl_defaults.enableOCSPStapling;
break;
case SSL_ENABLE_NPN: on = ssl_defaults.enableNPN; break;
case SSL_ENABLE_ALPN: on = ssl_defaults.enableALPN; break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ on = ssl_defaults.enableFallbackSCSV;
+ break;
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
rv = SECFailure;
}
*pOn = on;
return rv;
@@ -1089,16 +1099,20 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
case SSL_ENABLE_NPN:
ssl_defaults.enableNPN = on;
break;
case SSL_ENABLE_ALPN:
ssl_defaults.enableALPN = on;
break;
+ case SSL_ENABLE_FALLBACK_SCSV:
+ ssl_defaults.enableFallbackSCSV = on;
+ break;
+
default:
PORT_SetError(SEC_ERROR_INVALID_ARGS);
return SECFailure;
}
return SECSuccess;
}
/* function tells us if the cipher suite is one that we no longer support. */