This option causes clients to ignore HelloRequest messages completely. This can be suitable in cases where a server tries to perform concurrent application data and handshake flow, e.g. because they are trying to “renew” symmetric keys. Change-Id: I2779f7eff30d82163f2c34a625ec91dc34fab548 Reviewed-on: https://boringssl-review.googlesource.com/6431 Reviewed-by: David Benjamin <davidben@chromium.org> Reviewed-by: Adam Langley <agl@google.com>kris/onging/CECPQ3_patch15
@@ -2663,6 +2663,7 @@ enum ssl_renegotiate_mode_t { | |||||
ssl_renegotiate_never = 0, | ssl_renegotiate_never = 0, | ||||
ssl_renegotiate_once, | ssl_renegotiate_once, | ||||
ssl_renegotiate_freely, | ssl_renegotiate_freely, | ||||
ssl_renegotiate_ignore, | |||||
}; | }; | ||||
/* SSL_set_renegotiate_mode configures how |ssl|, a client, reacts to | /* SSL_set_renegotiate_mode configures how |ssl|, a client, reacts to | ||||
@@ -2671,8 +2672,10 @@ enum ssl_renegotiate_mode_t { | |||||
* | * | ||||
* The renegotiation mode defaults to |ssl_renegotiate_never|, but may be set | * The renegotiation mode defaults to |ssl_renegotiate_never|, but may be set | ||||
* at any point in a connection's lifetime. Set it to |ssl_renegotiate_once| to | * at any point in a connection's lifetime. Set it to |ssl_renegotiate_once| to | ||||
* allow one renegotiation and |ssl_renegotiate_freely| to allow all | |||||
* renegotiations. | |||||
* allow one renegotiation, |ssl_renegotiate_freely| to allow all | |||||
* renegotiations or |ssl_renegotiate_ignore| to ignore HelloRequest messages. | |||||
* Note that ignoring HelloRequest messages may cause the connection to stall | |||||
* if the server waits for the renegotiation to complete. | |||||
* | * | ||||
* There is no support in BoringSSL for initiating renegotiations as a client | * There is no support in BoringSSL for initiating renegotiations as a client | ||||
* or server. */ | * or server. */ | ||||
@@ -346,6 +346,8 @@ static int ssl3_can_renegotiate(SSL *ssl) { | |||||
return ssl->s3->total_renegotiations == 0; | return ssl->s3->total_renegotiations == 0; | ||||
case ssl_renegotiate_freely: | case ssl_renegotiate_freely: | ||||
return 1; | return 1; | ||||
case ssl_renegotiate_ignore: | |||||
return 1; | |||||
} | } | ||||
assert(0); | assert(0); | ||||
@@ -567,6 +569,10 @@ start: | |||||
goto err; | goto err; | ||||
} | } | ||||
if (s->renegotiate_mode == ssl_renegotiate_ignore) { | |||||
goto start; | |||||
} | |||||
/* Renegotiation is only supported at quiescent points in the application | /* Renegotiation is only supported at quiescent points in the application | ||||
* protocol, namely in HTTPS, just before reading the HTTP response. Require | * protocol, namely in HTTPS, just before reading the HTTP response. Require | ||||
* the record-layer be idle and avoid complexities of sending a handshake | * the record-layer be idle and avoid complexities of sending a handshake | ||||
@@ -1205,6 +1205,9 @@ static bool DoExchange(ScopedSSL_SESSION *out_session, SSL_CTX *ssl_ctx, | |||||
if (config->renegotiate_freely) { | if (config->renegotiate_freely) { | ||||
SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely); | SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_freely); | ||||
} | } | ||||
if (config->renegotiate_ignore) { | |||||
SSL_set_renegotiate_mode(ssl.get(), ssl_renegotiate_ignore); | |||||
} | |||||
if (!config->check_close_notify) { | if (!config->check_close_notify) { | ||||
SSL_set_quiet_shutdown(ssl.get(), 1); | SSL_set_quiet_shutdown(ssl.get(), 1); | ||||
} | } | ||||
@@ -782,6 +782,11 @@ type ProtocolBugs struct { | |||||
// connections where the client offers a non-empty session ID or session | // connections where the client offers a non-empty session ID or session | ||||
// ticket. | // ticket. | ||||
FailIfSessionOffered bool | FailIfSessionOffered bool | ||||
// SendHelloRequestBeforeEveryAppDataRecord, if true, causes a | |||||
// HelloRequest handshake message to be sent before each application | |||||
// data record. This only makes sense for a server. | |||||
SendHelloRequestBeforeEveryAppDataRecord bool | |||||
} | } | ||||
func (c *Config) serverInit() { | func (c *Config) serverInit() { | ||||
@@ -1152,6 +1152,10 @@ func (c *Conn) Write(b []byte) (int, error) { | |||||
c.sendAlertLocked(alertLevelError, c.config.Bugs.SendSpuriousAlert) | c.sendAlertLocked(alertLevelError, c.config.Bugs.SendSpuriousAlert) | ||||
} | } | ||||
if c.config.Bugs.SendHelloRequestBeforeEveryAppDataRecord { | |||||
c.writeRecord(recordTypeHandshake, []byte{typeHelloRequest, 0, 0, 0}) | |||||
} | |||||
// SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext | // SSL 3.0 and TLS 1.0 are susceptible to a chosen-plaintext | ||||
// attack when using block mode ciphers due to predictable IVs. | // attack when using block mode ciphers due to predictable IVs. | ||||
// This can be prevented by splitting each Application Data | // This can be prevented by splitting each Application Data | ||||
@@ -3920,6 +3920,28 @@ func addRenegotiationTests() { | |||||
"-expect-total-renegotiations", "2", | "-expect-total-renegotiations", "2", | ||||
}, | }, | ||||
}) | }) | ||||
testCases = append(testCases, testCase{ | |||||
name: "Renegotiate-Client-NoIgnore", | |||||
config: Config{ | |||||
Bugs: ProtocolBugs{ | |||||
SendHelloRequestBeforeEveryAppDataRecord: true, | |||||
}, | |||||
}, | |||||
shouldFail: true, | |||||
expectedError: ":NO_RENEGOTIATION:", | |||||
}) | |||||
testCases = append(testCases, testCase{ | |||||
name: "Renegotiate-Client-Ignore", | |||||
config: Config{ | |||||
Bugs: ProtocolBugs{ | |||||
SendHelloRequestBeforeEveryAppDataRecord: true, | |||||
}, | |||||
}, | |||||
flags: []string{ | |||||
"-renegotiate-ignore", | |||||
"-expect-total-renegotiations", "0", | |||||
}, | |||||
}) | |||||
} | } | ||||
func addDTLSReplayTests() { | func addDTLSReplayTests() { | ||||
@@ -96,6 +96,7 @@ const Flag<bool> kBoolFlags[] = { | |||||
{ "-expect-verify-result", &TestConfig::expect_verify_result }, | { "-expect-verify-result", &TestConfig::expect_verify_result }, | ||||
{ "-renegotiate-once", &TestConfig::renegotiate_once }, | { "-renegotiate-once", &TestConfig::renegotiate_once }, | ||||
{ "-renegotiate-freely", &TestConfig::renegotiate_freely }, | { "-renegotiate-freely", &TestConfig::renegotiate_freely }, | ||||
{ "-renegotiate-ignore", &TestConfig::renegotiate_ignore }, | |||||
{ "-disable-npn", &TestConfig::disable_npn }, | { "-disable-npn", &TestConfig::disable_npn }, | ||||
}; | }; | ||||
@@ -98,6 +98,7 @@ struct TestConfig { | |||||
int expect_total_renegotiations = 0; | int expect_total_renegotiations = 0; | ||||
bool renegotiate_once = false; | bool renegotiate_once = false; | ||||
bool renegotiate_freely = false; | bool renegotiate_freely = false; | ||||
bool renegotiate_ignore = false; | |||||
bool disable_npn = false; | bool disable_npn = false; | ||||
}; | }; | ||||