Add |SSL_set1_host| and |SSL_set_hostflags|.

This allows code that uses OpenSSL's suggested pattern for 1.1.0 [1] to
work.

[1] https://wiki.openssl.org/index.php/Hostname_validation

Change-Id: I6d1b983074d5ad8645400cef887c1cc20f7bf2a1
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/50645
Reviewed-by: David Benjamin <davidben@google.com>
fips-20220613
Adam Langley 3 years ago committed by Adam Langley
parent 731d6cbef9
commit 7e7e6b693f
  1. 14
      include/openssl/ssl.h
  2. 88
      ssl/ssl_test.cc
  3. 17
      ssl/ssl_x509.cc

@ -2459,6 +2459,15 @@ OPENSSL_EXPORT int (*SSL_CTX_get_verify_callback(const SSL_CTX *ctx))(
OPENSSL_EXPORT int (*SSL_get_verify_callback(const SSL *ssl))(
int ok, X509_STORE_CTX *store_ctx);
// SSL_set1_host sets a DNS name that will be required to be present in the
// verified leaf certificate.
//
// Note: unless _some_ name checking is performed, certificate validation is
// ineffective. Simply checking that a host has some certificate from a CA is
// rarely meaningful—you have to check that the CA believed that the host was
// who you expect to be talking to.
OPENSSL_EXPORT int SSL_set1_host(SSL *ssl, const char *hostname);
// SSL_CTX_set_verify_depth sets the maximum depth of a certificate chain
// accepted in verification. This number does not include the leaf, so a depth
// of 1 allows the leaf and one CA certificate.
@ -2632,6 +2641,11 @@ OPENSSL_EXPORT int SSL_set_verify_algorithm_prefs(SSL *ssl,
const uint16_t *prefs,
size_t num_prefs);
// SSL_set_hostflags calls |X509_VERIFY_PARAM_set_hostflags| on the
// |X509_VERIFY_PARAM| associated with this |SSL*|. The |flags| argument
// should be one of the |X509_CHECK_*| constants.
OPENSSL_EXPORT void SSL_set_hostflags(SSL *ssl, unsigned flags);
// Client certificate CA list.
//

@ -39,6 +39,7 @@
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include <openssl/x509.h>
#include <openssl/x509v3.h>
#include "internal.h"
#include "../crypto/internal.h"
@ -1498,6 +1499,8 @@ static bool CreateClientAndServer(bssl::UniquePtr<SSL> *out_client,
struct ClientConfig {
SSL_SESSION *session = nullptr;
std::string servername;
std::string verify_hostname;
unsigned hostflags = 0;
bool early_data = false;
};
@ -1520,6 +1523,12 @@ static bool ConnectClientAndServer(bssl::UniquePtr<SSL> *out_client,
!SSL_set_tlsext_host_name(client.get(), config.servername.c_str())) {
return false;
}
if (!config.verify_hostname.empty()) {
if (!SSL_set1_host(client.get(), config.verify_hostname.c_str())) {
return false;
}
SSL_set_hostflags(client.get(), config.hostflags);
}
SSL_set_shed_handshake_config(client.get(), shed_handshake_config);
SSL_set_shed_handshake_config(server.get(), shed_handshake_config);
@ -8002,5 +8011,84 @@ TEST(SSLTest, PermuteExtensions) {
}
}
TEST(SSLTest, HostMatching) {
static const char kCertPEM[] =
"-----BEGIN CERTIFICATE-----\n"
"MIIB9jCCAZ2gAwIBAgIQeudG9R61BOxUvWkeVhU5DTAKBggqhkjOPQQDAjApMRAw\n"
"DgYDVQQKEwdBY21lIENvMRUwEwYDVQQDEwxleGFtcGxlMy5jb20wHhcNMjExMjA2\n"
"MjA1NjU2WhcNMjIxMjA2MjA1NjU2WjApMRAwDgYDVQQKEwdBY21lIENvMRUwEwYD\n"
"VQQDEwxleGFtcGxlMy5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAS7l2VO\n"
"Bl2TjVm9WfGk24+hMbVFUNB+RVHWbCvFvNZAoWiIJ2z34RLGInyZvCZ8xLAvsuaW\n"
"ULDDaoeDl1M0t4Hmo4GmMIGjMA4GA1UdDwEB/wQEAwIChDATBgNVHSUEDDAKBggr\n"
"BgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBTTJWurcc1t+VPQBko3\n"
"Gsw6cbcWSTBMBgNVHREERTBDggxleGFtcGxlMS5jb22CDGV4YW1wbGUyLmNvbYIP\n"
"YSouZXhhbXBsZTQuY29tgg4qLmV4YW1wbGU1LmNvbYcEAQIDBDAKBggqhkjOPQQD\n"
"AgNHADBEAiAAv0ljHJGrgyzZDkG6XvNZ5ewxRfnXcZuD0Y7E4giCZgIgNK1qjilu\n"
"5DyVbfKeeJhOCtGxqE1dWLXyJBnoRomSYBY=\n"
"-----END CERTIFICATE-----\n";
bssl::UniquePtr<X509> cert(CertFromPEM(kCertPEM));
ASSERT_TRUE(cert);
static const char kKeyPEM[] =
"-----BEGIN PRIVATE KEY-----\n"
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQghsaSZhUzZAcQlLyJ\n"
"MDuy7WPdyqNsAX9rmEP650LF/q2hRANCAAS7l2VOBl2TjVm9WfGk24+hMbVFUNB+\n"
"RVHWbCvFvNZAoWiIJ2z34RLGInyZvCZ8xLAvsuaWULDDaoeDl1M0t4Hm\n"
"-----END PRIVATE KEY-----\n";
bssl::UniquePtr<EVP_PKEY> key(KeyFromPEM(kKeyPEM));
ASSERT_TRUE(key);
bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
ASSERT_TRUE(server_ctx);
ASSERT_TRUE(SSL_CTX_use_certificate(server_ctx.get(), cert.get()));
ASSERT_TRUE(SSL_CTX_use_PrivateKey(server_ctx.get(), key.get()));
bssl::UniquePtr<SSL_CTX> client_ctx(SSL_CTX_new(TLS_method()));
ASSERT_TRUE(client_ctx);
ASSERT_TRUE(X509_STORE_add_cert(SSL_CTX_get_cert_store(client_ctx.get()),
cert.get()));
SSL_CTX_set_verify(client_ctx.get(),
SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT,
nullptr);
struct TestCase {
std::string hostname;
unsigned flags;
bool should_match;
};
std::vector<TestCase> kTests = {
// These two names are present as SANs in the certificate.
{"example1.com", 0, true},
{"example2.com", 0, true},
// This is the CN of the certificate, but that shouldn't matter if a SAN
// extension is present.
{"example3.com", 0, false},
// a*.example4.com is a SAN, which is invalid because partial wildcards
// aren't a thing except for the OpenSSL verifier.
{"abc.example4.com", 0, true},
// ... but they can be turned off.
{"abc.example4.com", X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS, false},
// *.example5.com is a SAN in the certificate, which is a normal and valid
// wildcard.
{"abc.example5.com", 0, true},
// This name is not present.
{"notexample1.com", 0, false},
// The IPv4 address 1.2.3.4 is a SAN, but that shouldn't match against a
// hostname that happens to be its textual representation.
{"1.2.3.4", 0, false},
};
bssl::UniquePtr<SSL> client, server;
ClientConfig config;
for (const TestCase &test : kTests) {
SCOPED_TRACE(test.hostname);
config.verify_hostname = test.hostname;
config.hostflags = test.flags;
EXPECT_EQ(test.should_match,
ConnectClientAndServer(&client, &server, client_ctx.get(),
server_ctx.get(), config));
}
}
} // namespace
BSSL_NAMESPACE_END

@ -1304,6 +1304,23 @@ int SSL_set1_verify_cert_store(SSL *ssl, X509_STORE *store) {
return set_cert_store(&ssl->config->cert->verify_store, store, 1);
}
int SSL_set1_host(SSL *ssl, const char *hostname) {
check_ssl_x509_method(ssl);
if (!ssl->config) {
return 0;
}
return X509_VERIFY_PARAM_set1_host(ssl->config->param, hostname,
strlen(hostname));
}
void SSL_set_hostflags(SSL *ssl, unsigned flags) {
check_ssl_x509_method(ssl);
if (!ssl->config) {
return;
}
X509_VERIFY_PARAM_set_hostflags(ssl->config->param, flags);
}
int SSL_alert_from_verify_result(long result) {
switch (result) {
case X509_V_ERR_CERT_CHAIN_TOO_LONG:

Loading…
Cancel
Save