Return null from SSL_get0_peer_certificates if unauthenticated.
SSL_get0_peer_certificates is documented to return NULL if the peer was
anonymous, but it actually returns a non-NULL empty list (except in SSL
3.0 where the Certificate message and thus ssl_parse_cert_chain is
skipped).
Make the implementation match the documentation.
Change-Id: Ib3e25d2155f316cc5e9eb3ab7f74b78e08b8a86b
Reviewed-on: https://boringssl-review.googlesource.com/18226
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>
diff --git a/ssl/handshake_client.cc b/ssl/handshake_client.cc
index 260d3cd0..32714d1 100644
--- a/ssl/handshake_client.cc
+++ b/ssl/handshake_client.cc
@@ -1087,15 +1087,14 @@
CBS_init(&cbs, ssl->init_msg, ssl->init_num);
uint8_t alert = SSL_AD_DECODE_ERROR;
- sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
- hs->peer_pubkey.reset();
- hs->new_session->certs =
- ssl_parse_cert_chain(&alert, &hs->peer_pubkey, NULL, &cbs, ssl->ctx->pool)
- .release();
- if (hs->new_session->certs == NULL) {
+ UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
+ if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey, NULL, &cbs,
+ ssl->ctx->pool)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
return -1;
}
+ sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
+ hs->new_session->certs = chain.release();
if (sk_CRYPTO_BUFFER_num(hs->new_session->certs) == 0 ||
CBS_len(&cbs) != 0 ||
diff --git a/ssl/handshake_server.cc b/ssl/handshake_server.cc
index 38fbef4..44c3b01 100644
--- a/ssl/handshake_server.cc
+++ b/ssl/handshake_server.cc
@@ -1228,20 +1228,18 @@
CBS certificate_msg;
CBS_init(&certificate_msg, ssl->init_msg, ssl->init_num);
- sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
- hs->peer_pubkey.reset();
uint8_t alert = SSL_AD_DECODE_ERROR;
- hs->new_session->certs =
- ssl_parse_cert_chain(&alert, &hs->peer_pubkey,
- ssl->retain_only_sha256_of_client_certs
- ? hs->new_session->peer_sha256
- : NULL,
- &certificate_msg, ssl->ctx->pool)
- .release();
- if (hs->new_session->certs == NULL) {
+ UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain;
+ if (!ssl_parse_cert_chain(&alert, &chain, &hs->peer_pubkey,
+ ssl->retain_only_sha256_of_client_certs
+ ? hs->new_session->peer_sha256
+ : NULL,
+ &certificate_msg, ssl->ctx->pool)) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, alert);
return -1;
}
+ sk_CRYPTO_BUFFER_pop_free(hs->new_session->certs, CRYPTO_BUFFER_free);
+ hs->new_session->certs = chain.release();
if (CBS_len(&certificate_msg) != 0 ||
!ssl->ctx->x509_method->session_cache_objects(hs->new_session.get())) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 5a2e560..085beef 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -920,18 +920,21 @@
int ssl_has_certificate(const SSL *ssl);
/* ssl_parse_cert_chain parses a certificate list from |cbs| in the format used
- * by a TLS Certificate message. On success, it returns a newly-allocated
- * |CRYPTO_BUFFER| list and advances |cbs|. Otherwise, it returns nullptr and
- * sets |*out_alert| to an alert to send to the peer.
+ * by a TLS Certificate message. On success, it advances |cbs| and returns
+ * true. Otherwise, it returns false and sets |*out_alert| to an alert to send
+ * to the peer.
*
- * If the list is non-empty then |*out_pubkey| will be set to a freshly
- * allocated public-key from the leaf certificate.
+ * If the list is non-empty then |*out_chain| and |*out_pubkey| will be set to
+ * the certificate chain and the leaf certificate's public key
+ * respectively. Otherwise, both will be set to nullptr.
*
* If the list is non-empty and |out_leaf_sha256| is non-NULL, it writes the
* SHA-256 hash of the leaf to |out_leaf_sha256|. */
-UniquePtr<STACK_OF(CRYPTO_BUFFER)> ssl_parse_cert_chain(
- uint8_t *out_alert, UniquePtr<EVP_PKEY> *out_pubkey,
- uint8_t *out_leaf_sha256, CBS *cbs, CRYPTO_BUFFER_POOL *pool);
+bool ssl_parse_cert_chain(uint8_t *out_alert,
+ UniquePtr<STACK_OF(CRYPTO_BUFFER)> *out_chain,
+ UniquePtr<EVP_PKEY> *out_pubkey,
+ uint8_t *out_leaf_sha256, CBS *cbs,
+ CRYPTO_BUFFER_POOL *pool);
/* ssl_add_cert_chain adds |ssl|'s certificate chain to |cbb| in the format used
* by a TLS Certificate message. If there is no certificate chain, it emits an
diff --git a/ssl/ssl_cert.cc b/ssl/ssl_cert.cc
index 4f457f6..0fcf48d 100644
--- a/ssl/ssl_cert.cc
+++ b/ssl/ssl_cert.cc
@@ -382,39 +382,47 @@
ssl_has_private_key(ssl);
}
-UniquePtr<STACK_OF(CRYPTO_BUFFER)>
- ssl_parse_cert_chain(uint8_t *out_alert, UniquePtr<EVP_PKEY> *out_pubkey,
- uint8_t *out_leaf_sha256, CBS *cbs,
- CRYPTO_BUFFER_POOL *pool) {
- UniquePtr<EVP_PKEY> pubkey;
- UniquePtr<STACK_OF(CRYPTO_BUFFER)> ret(sk_CRYPTO_BUFFER_new_null());
- if (!ret) {
- *out_alert = SSL_AD_INTERNAL_ERROR;
- OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return nullptr;
- }
+bool ssl_parse_cert_chain(uint8_t *out_alert,
+ UniquePtr<STACK_OF(CRYPTO_BUFFER)> *out_chain,
+ UniquePtr<EVP_PKEY> *out_pubkey,
+ uint8_t *out_leaf_sha256, CBS *cbs,
+ CRYPTO_BUFFER_POOL *pool) {
+ out_chain->reset();
+ out_pubkey->reset();
CBS certificate_list;
if (!CBS_get_u24_length_prefixed(cbs, &certificate_list)) {
*out_alert = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- return nullptr;
+ return false;
}
+ if (CBS_len(&certificate_list) == 0) {
+ return true;
+ }
+
+ UniquePtr<STACK_OF(CRYPTO_BUFFER)> chain(sk_CRYPTO_BUFFER_new_null());
+ if (!chain) {
+ *out_alert = SSL_AD_INTERNAL_ERROR;
+ OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
+ return false;
+ }
+
+ UniquePtr<EVP_PKEY> pubkey;
while (CBS_len(&certificate_list) > 0) {
CBS certificate;
if (!CBS_get_u24_length_prefixed(&certificate_list, &certificate) ||
CBS_len(&certificate) == 0) {
*out_alert = SSL_AD_DECODE_ERROR;
OPENSSL_PUT_ERROR(SSL, SSL_R_CERT_LENGTH_MISMATCH);
- return nullptr;
+ return false;
}
- if (sk_CRYPTO_BUFFER_num(ret.get()) == 0) {
+ if (sk_CRYPTO_BUFFER_num(chain.get()) == 0) {
pubkey = ssl_cert_parse_pubkey(&certificate);
if (!pubkey) {
*out_alert = SSL_AD_DECODE_ERROR;
- return nullptr;
+ return false;
}
/* Retain the hash of the leaf certificate if requested. */
@@ -426,16 +434,17 @@
CRYPTO_BUFFER *buf =
CRYPTO_BUFFER_new_from_CBS(&certificate, pool);
if (buf == NULL ||
- !sk_CRYPTO_BUFFER_push(ret.get(), buf)) {
+ !sk_CRYPTO_BUFFER_push(chain.get(), buf)) {
*out_alert = SSL_AD_INTERNAL_ERROR;
CRYPTO_BUFFER_free(buf);
OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
- return nullptr;
+ return false;
}
}
+ *out_chain = std::move(chain);
*out_pubkey = std::move(pubkey);
- return ret;
+ return true;
}
int ssl_add_cert_chain(SSL *ssl, CBB *cbb) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index b410175..48e2181 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -1716,14 +1716,16 @@
return false;
}
- // However, for historical reasons, the chain includes the leaf on the
+ // However, for historical reasons, the X509 chain includes the leaf on the
// client, but does not on the server.
- if (sk_X509_num(SSL_get_peer_cert_chain(client.get())) != 1) {
+ if (sk_X509_num(SSL_get_peer_cert_chain(client.get())) != 1 ||
+ sk_CRYPTO_BUFFER_num(SSL_get0_peer_certificates(client.get())) != 1) {
fprintf(stderr, "Client peer chain was incorrect.\n");
return false;
}
- if (sk_X509_num(SSL_get_peer_cert_chain(server.get())) != 0) {
+ if (sk_X509_num(SSL_get_peer_cert_chain(server.get())) != 0 ||
+ sk_CRYPTO_BUFFER_num(SSL_get0_peer_certificates(server.get())) != 1) {
fprintf(stderr, "Server peer chain was incorrect.\n");
return false;
}
@@ -1731,6 +1733,48 @@
return true;
}
+static bool TestNoPeerCertificate(bool is_dtls, const SSL_METHOD *method,
+ uint16_t version) {
+ bssl::UniquePtr<X509> cert = GetTestCertificate();
+ bssl::UniquePtr<EVP_PKEY> key = GetTestKey();
+ if (!cert || !key) {
+ return false;
+ }
+
+ // Configure an anonymous client.
+ bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(method)),
+ client_ctx(SSL_CTX_new(method));
+ if (!server_ctx || !client_ctx ||
+ !SSL_CTX_use_certificate(server_ctx.get(), cert.get()) ||
+ !SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()) ||
+ !SSL_CTX_set_min_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(server_ctx.get(), version) ||
+ !SSL_CTX_set_min_proto_version(client_ctx.get(), version) ||
+ !SSL_CTX_set_max_proto_version(client_ctx.get(), version)) {
+ return false;
+ }
+ SSL_CTX_set_verify(
+ server_ctx.get(), SSL_VERIFY_PEER, nullptr);
+ SSL_CTX_set_cert_verify_callback(server_ctx.get(), VerifySucceed, NULL);
+ SSL_CTX_set_cert_verify_callback(client_ctx.get(), VerifySucceed, NULL);
+
+ bssl::UniquePtr<SSL> client, server;
+ if (!ConnectClientAndServer(&client, &server, client_ctx.get(),
+ server_ctx.get(), nullptr /* no session */)) {
+ return false;
+ }
+
+ // Client and server should both see the leaf certificate.
+ bssl::UniquePtr<X509> peer(SSL_get_peer_certificate(server.get()));
+ if (peer ||
+ SSL_get0_peer_certificates(server.get()) != nullptr) {
+ fprintf(stderr, "Server peer certificate was non-null.\n");
+ return false;
+ }
+
+ return true;
+}
+
static bool TestRetainOnlySHA256OfCerts(bool is_dtls, const SSL_METHOD *method,
uint16_t version) {
bssl::UniquePtr<X509> cert = GetTestCertificate();
@@ -3900,6 +3944,7 @@
!ForEachVersion(TestSequenceNumber) ||
!ForEachVersion(TestOneSidedShutdown) ||
!ForEachVersion(TestGetPeerCertificate) ||
+ !ForEachVersion(TestNoPeerCertificate) ||
!ForEachVersion(TestRetainOnlySHA256OfCerts) ||
!TestClientHello() ||
!ForEachVersion(TestSessionIDContext) ||
diff --git a/ssl/tls13_both.cc b/ssl/tls13_both.cc
index 338975b..627f038 100644
--- a/ssl/tls13_both.cc
+++ b/ssl/tls13_both.cc
@@ -196,7 +196,9 @@
CBS cbs, context, certificate_list;
CBS_init(&cbs, ssl->init_msg, ssl->init_num);
if (!CBS_get_u8_length_prefixed(&cbs, &context) ||
- CBS_len(&context) != 0) {
+ CBS_len(&context) != 0 ||
+ !CBS_get_u24_length_prefixed(&cbs, &certificate_list) ||
+ CBS_len(&cbs) != 0) {
ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
return 0;
@@ -209,12 +211,6 @@
return 0;
}
- if (!CBS_get_u24_length_prefixed(&cbs, &certificate_list)) {
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- return 0;
- }
-
const bool retain_sha256 =
ssl->server && ssl->retain_only_sha256_of_client_certs;
UniquePtr<EVP_PKEY> pkey;
@@ -326,10 +322,10 @@
}
}
- if (CBS_len(&cbs) != 0) {
- OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
- ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
- return 0;
+ /* Store a null certificate list rather than an empty one if the peer didn't
+ * send certificates. */
+ if (sk_CRYPTO_BUFFER_num(certs.get()) == 0) {
+ certs.reset();
}
hs->peer_pubkey = std::move(pkey);