Bug 1036735: Add TLS_FALLBACK_SCSV cipher suite to version fallback
authorAdam Langley <agl@chromium.org>
Wed, 13 Aug 2014 15:03:58 -0700
changeset 11232 45cb71fd7bca
parent 11231 9c4309308d12
child 11233 e5f7dcf561d2
push id463
push userwtc@google.com
push date2014-08-13 22:04 +0000
bugs1036735
Bug 1036735: Add TLS_FALLBACK_SCSV cipher suite to version fallback connections (draft-ietf-tls-downgrade-scsv). r=wtc,martin.thomson.
lib/ssl/SSLerrs.h
lib/ssl/ssl.h
lib/ssl/ssl3con.c
lib/ssl/ssl3prot.h
lib/ssl/sslerr.h
lib/ssl/sslimpl.h
lib/ssl/sslproto.h
lib/ssl/sslsock.c
--- 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
@@ -183,16 +183,19 @@ SSL_IMPORT PRFileDesc *DTLS_ImportFD(PRF
 #define SSL_ENABLE_ALPN 26
 
 /* SSL_REUSE_SERVER_ECDHE_KEY controls whether the ECDHE server key is
  * reused for multiple handshakes or generated each time.
  * SSL_REUSE_SERVER_ECDHE_KEY is currently enabled by default.
  */
 #define SSL_REUSE_SERVER_ECDHE_KEY 27
 
+#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
@@ -322,16 +322,17 @@ typedef struct sslOptionsStr {
     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 reuseServerECDHEKey    : 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
@@ -76,17 +76,18 @@ static sslOptions ssl_defaults = {
     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_TRUE     /* reuseServerECDHEKey */
+    PR_TRUE,    /* reuseServerECDHEKey */
+    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
@@ -784,16 +785,20 @@ SSL_OptionSet(PRFileDesc *fd, PRInt32 wh
       case SSL_ENABLE_ALPN:
         ss->opt.enableALPN = on;
         break;
 
       case SSL_REUSE_SERVER_ECDHE_KEY:
         ss->opt.reuseServerECDHEKey = 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,
@@ -858,16 +863,17 @@ SSL_OptionGet(PRFileDesc *fd, PRInt32 wh
                                   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_REUSE_SERVER_ECDHE_KEY:
                                   on = ss->opt.reuseServerECDHEKey; 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);
@@ -924,16 +930,19 @@ SSL_OptionGetDefault(PRInt32 which, PRBo
     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_REUSE_SERVER_ECDHE_KEY:
        on = ssl_defaults.reuseServerECDHEKey;
        break;
+    case SSL_ENABLE_FALLBACK_SCSV:
+       on = ssl_defaults.enableFallbackSCSV;
+       break;
 
     default:
         PORT_SetError(SEC_ERROR_INVALID_ARGS);
         rv = SECFailure;
     }
 
     *pOn = on;
     return rv;
@@ -1103,16 +1112,20 @@ SSL_OptionSetDefault(PRInt32 which, PRBo
       case SSL_ENABLE_ALPN:
         ssl_defaults.enableALPN = on;
         break;
 
       case SSL_REUSE_SERVER_ECDHE_KEY:
         ssl_defaults.reuseServerECDHEKey = 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. */