Add |SSL_set_retain_only_sha256_of_client_certs|.
Previously the option to retain only the SHA-256 hash of client certificates could only be set at the |SSL_CTX| level. This change makes |SSL| objects inherit the setting from the |SSL_CTX|, but allows it to be overridden on a per-|SSL| basis. Change-Id: Id435934af3d425d5f008d2f3b9751d1d0884ee55 Reviewed-on: https://boringssl-review.googlesource.com/12182 Reviewed-by: David Benjamin <davidben@google.com> Commit-Queue: David Benjamin <davidben@google.com> CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
This commit is contained in:
parent
a933c38f1a
commit
bbaf367969
@ -3116,6 +3116,14 @@ OPENSSL_EXPORT size_t SSL_get_server_random(const SSL *ssl, uint8_t *out,
|
||||
* NULL if one has not been negotiated yet or there is no pending handshake. */
|
||||
OPENSSL_EXPORT const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl);
|
||||
|
||||
/* SSL_set_retain_only_sha256_of_client_certs, on a server, sets whether only
|
||||
* the SHA-256 hash of peer's certificate should be saved in memory and in the
|
||||
* session. This can save memory, ticket size and session cache space. If
|
||||
* enabled, |SSL_get_peer_certificate| will return NULL after the handshake
|
||||
* completes. See the |peer_sha256| field of |SSL_SESSION| for the hash. */
|
||||
OPENSSL_EXPORT void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl,
|
||||
int enable);
|
||||
|
||||
/* SSL_CTX_set_retain_only_sha256_of_client_certs, on a server, sets whether
|
||||
* only the SHA-256 hash of peer's certificate should be saved in memory and in
|
||||
* the session. This can save memory, ticket size and session cache space. If
|
||||
@ -4200,6 +4208,11 @@ struct ssl_st {
|
||||
* we'll advertise support. */
|
||||
unsigned tlsext_channel_id_enabled:1;
|
||||
|
||||
/* retain_only_sha256_of_client_certs is true if we should compute the SHA256
|
||||
* hash of the peer's certificate and then discard it to save memory and
|
||||
* session space. Only effective on the server side. */
|
||||
unsigned retain_only_sha256_of_client_certs:1;
|
||||
|
||||
/* TODO(agl): remove once node.js not longer references this. */
|
||||
int tlsext_status_type;
|
||||
};
|
||||
|
@ -482,7 +482,7 @@ int ssl3_accept(SSL *ssl) {
|
||||
/* If we aren't retaining peer certificates then we can discard it
|
||||
* now. */
|
||||
if (ssl->s3->new_session != NULL &&
|
||||
ssl->ctx->retain_only_sha256_of_client_certs) {
|
||||
ssl->retain_only_sha256_of_client_certs) {
|
||||
X509_free(ssl->s3->new_session->x509_peer);
|
||||
ssl->s3->new_session->x509_peer = NULL;
|
||||
sk_X509_pop_free(ssl->s3->new_session->x509_chain, X509_free);
|
||||
@ -1313,7 +1313,7 @@ static int ssl3_get_client_certificate(SSL *ssl) {
|
||||
CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
|
||||
uint8_t alert;
|
||||
STACK_OF(X509) *chain = ssl_parse_cert_chain(
|
||||
ssl, &alert, ssl->ctx->retain_only_sha256_of_client_certs
|
||||
ssl, &alert, ssl->retain_only_sha256_of_client_certs
|
||||
? ssl->s3->new_session->peer_sha256
|
||||
: NULL,
|
||||
&certificate_msg);
|
||||
@ -1352,7 +1352,7 @@ static int ssl3_get_client_certificate(SSL *ssl) {
|
||||
ssl->s3->new_session->verify_result = X509_V_OK;
|
||||
} else {
|
||||
/* The hash would have been filled in. */
|
||||
if (ssl->ctx->retain_only_sha256_of_client_certs) {
|
||||
if (ssl->retain_only_sha256_of_client_certs) {
|
||||
ssl->s3->new_session->peer_sha256_valid = 1;
|
||||
}
|
||||
|
||||
|
@ -412,6 +412,8 @@ SSL *SSL_new(SSL_CTX *ctx) {
|
||||
assert(ssl->sid_ctx_length <= sizeof ssl->sid_ctx);
|
||||
memcpy(&ssl->sid_ctx, &ctx->sid_ctx, sizeof(ssl->sid_ctx));
|
||||
ssl->verify_callback = ctx->default_verify_callback;
|
||||
ssl->retain_only_sha256_of_client_certs =
|
||||
ctx->retain_only_sha256_of_client_certs;
|
||||
|
||||
ssl->param = X509_VERIFY_PARAM_new();
|
||||
if (!ssl->param) {
|
||||
@ -2908,6 +2910,10 @@ const SSL_CIPHER *SSL_get_pending_cipher(const SSL *ssl) {
|
||||
return ssl->s3->tmp.new_cipher;
|
||||
}
|
||||
|
||||
void SSL_set_retain_only_sha256_of_client_certs(SSL *ssl, int enabled) {
|
||||
ssl->retain_only_sha256_of_client_certs = !!enabled;
|
||||
}
|
||||
|
||||
void SSL_CTX_set_retain_only_sha256_of_client_certs(SSL_CTX *ctx, int enabled) {
|
||||
ctx->retain_only_sha256_of_client_certs = !!enabled;
|
||||
}
|
||||
|
@ -641,7 +641,13 @@ int ssl_session_is_resumable(const SSL *ssl, const SSL_SESSION *session) {
|
||||
* version. */
|
||||
ssl->version == session->ssl_version &&
|
||||
/* Only resume if the session's cipher matches the negotiated one. */
|
||||
ssl->s3->tmp.new_cipher == session->cipher;
|
||||
ssl->s3->tmp.new_cipher == session->cipher &&
|
||||
/* If the session contains a client certificate (either the full
|
||||
* certificate or just the hash) then require that the form of the
|
||||
* certificate matches the current configuration. */
|
||||
((session->x509_peer == NULL && !session->peer_sha256_valid) ||
|
||||
session->peer_sha256_valid ==
|
||||
ssl->retain_only_sha256_of_client_certs);
|
||||
}
|
||||
|
||||
/* ssl_lookup_session looks up |session_id| in the session cache and sets
|
||||
|
@ -1436,6 +1436,25 @@ static bool CheckHandshakeProperties(SSL *ssl, bool is_resume) {
|
||||
}
|
||||
}
|
||||
|
||||
bool expected_sha256_client_cert = config->expect_sha256_client_cert_initial;
|
||||
if (is_resume) {
|
||||
expected_sha256_client_cert = config->expect_sha256_client_cert_resume;
|
||||
}
|
||||
|
||||
if (SSL_get_session(ssl)->peer_sha256_valid != expected_sha256_client_cert) {
|
||||
fprintf(stderr,
|
||||
"Unexpected SHA-256 client cert state: expected:%d is_resume:%d.\n",
|
||||
expected_sha256_client_cert, is_resume);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (expected_sha256_client_cert &&
|
||||
SSL_get_session(ssl)->x509_peer != nullptr) {
|
||||
fprintf(stderr, "Have both client cert and SHA-256 hash: is_resume:%d.\n",
|
||||
is_resume);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -1595,6 +1614,12 @@ static bool DoExchange(bssl::UniquePtr<SSL_SESSION> *out_session,
|
||||
if (config->max_cert_list > 0) {
|
||||
SSL_set_max_cert_list(ssl.get(), config->max_cert_list);
|
||||
}
|
||||
if (!is_resume && config->retain_only_sha256_client_cert_initial) {
|
||||
SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
|
||||
}
|
||||
if (is_resume && config->retain_only_sha256_client_cert_resume) {
|
||||
SSL_set_retain_only_sha256_of_client_certs(ssl.get(), 1);
|
||||
}
|
||||
|
||||
int sock = Connect(config->port);
|
||||
if (sock == -1) {
|
||||
|
@ -9532,6 +9532,87 @@ func addCertificateTests() {
|
||||
}
|
||||
}
|
||||
|
||||
func addRetainOnlySHA256ClientCertTests() {
|
||||
for _, ver := range tlsVersions {
|
||||
// Test that enabling
|
||||
// SSL_CTX_set_retain_only_sha256_of_client_certs without
|
||||
// actually requesting a client certificate is a no-op.
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "RetainOnlySHA256-NoCert-" + ver.name,
|
||||
config: Config{
|
||||
MinVersion: ver.version,
|
||||
MaxVersion: ver.version,
|
||||
},
|
||||
flags: []string{
|
||||
"-retain-only-sha256-client-cert-initial",
|
||||
"-retain-only-sha256-client-cert-resume",
|
||||
},
|
||||
resumeSession: true,
|
||||
})
|
||||
|
||||
// Test that when retaining only a SHA-256 certificate is
|
||||
// enabled, the hash appears as expected.
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "RetainOnlySHA256-Cert-" + ver.name,
|
||||
config: Config{
|
||||
MinVersion: ver.version,
|
||||
MaxVersion: ver.version,
|
||||
Certificates: []Certificate{rsaCertificate},
|
||||
},
|
||||
flags: []string{
|
||||
"-verify-peer",
|
||||
"-retain-only-sha256-client-cert-initial",
|
||||
"-retain-only-sha256-client-cert-resume",
|
||||
"-expect-sha256-client-cert-initial",
|
||||
"-expect-sha256-client-cert-resume",
|
||||
},
|
||||
resumeSession: true,
|
||||
})
|
||||
|
||||
// Test that when the config changes from on to off, a
|
||||
// resumption is rejected because the server now wants the full
|
||||
// certificate chain.
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "RetainOnlySHA256-OnOff-" + ver.name,
|
||||
config: Config{
|
||||
MinVersion: ver.version,
|
||||
MaxVersion: ver.version,
|
||||
Certificates: []Certificate{rsaCertificate},
|
||||
},
|
||||
flags: []string{
|
||||
"-verify-peer",
|
||||
"-retain-only-sha256-client-cert-initial",
|
||||
"-expect-sha256-client-cert-initial",
|
||||
},
|
||||
resumeSession: true,
|
||||
expectResumeRejected: true,
|
||||
})
|
||||
|
||||
// Test that when the config changes from off to on, a
|
||||
// resumption is rejected because the server now wants just the
|
||||
// hash.
|
||||
testCases = append(testCases, testCase{
|
||||
testType: serverTest,
|
||||
name: "RetainOnlySHA256-OffOn-" + ver.name,
|
||||
config: Config{
|
||||
MinVersion: ver.version,
|
||||
MaxVersion: ver.version,
|
||||
Certificates: []Certificate{rsaCertificate},
|
||||
},
|
||||
flags: []string{
|
||||
"-verify-peer",
|
||||
"-retain-only-sha256-client-cert-resume",
|
||||
"-expect-sha256-client-cert-resume",
|
||||
},
|
||||
resumeSession: true,
|
||||
expectResumeRejected: true,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func worker(statusChan chan statusMsg, c chan *testCase, shimPath string, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
@ -9658,6 +9739,7 @@ func main() {
|
||||
addPeekTests()
|
||||
addRecordVersionTests()
|
||||
addCertificateTests()
|
||||
addRetainOnlySHA256ClientCertTests()
|
||||
|
||||
var wg sync.WaitGroup
|
||||
|
||||
|
@ -108,6 +108,14 @@ const Flag<bool> kBoolFlags[] = {
|
||||
{ "-peek-then-read", &TestConfig::peek_then_read },
|
||||
{ "-enable-grease", &TestConfig::enable_grease },
|
||||
{ "-use-exporter-between-reads", &TestConfig::use_exporter_between_reads },
|
||||
{ "-retain-only-sha256-client-cert-initial",
|
||||
&TestConfig::retain_only_sha256_client_cert_initial },
|
||||
{ "-retain-only-sha256-client-cert-resume",
|
||||
&TestConfig::retain_only_sha256_client_cert_resume },
|
||||
{ "-expect-sha256-client-cert-initial",
|
||||
&TestConfig::expect_sha256_client_cert_initial },
|
||||
{ "-expect-sha256-client-cert-resume",
|
||||
&TestConfig::expect_sha256_client_cert_resume },
|
||||
};
|
||||
|
||||
const Flag<std::string> kStringFlags[] = {
|
||||
|
@ -122,6 +122,10 @@ struct TestConfig {
|
||||
int expect_cipher_no_aes = 0;
|
||||
std::string expect_peer_cert_file;
|
||||
int resumption_delay = 0;
|
||||
bool retain_only_sha256_client_cert_initial = false;
|
||||
bool retain_only_sha256_client_cert_resume = false;
|
||||
bool expect_sha256_client_cert_initial = false;
|
||||
bool expect_sha256_client_cert_resume = false;
|
||||
};
|
||||
|
||||
bool ParseConfig(int argc, char **argv, TestConfig *out_config);
|
||||
|
@ -175,7 +175,7 @@ int tls13_process_certificate(SSL *ssl, int allow_anonymous) {
|
||||
}
|
||||
|
||||
const int retain_sha256 =
|
||||
ssl->server && ssl->ctx->retain_only_sha256_of_client_certs;
|
||||
ssl->server && ssl->retain_only_sha256_of_client_certs;
|
||||
int ret = 0;
|
||||
|
||||
STACK_OF(X509) *chain = sk_X509_new_null();
|
||||
|
Loading…
Reference in New Issue
Block a user