From: Shimi Gersner <sgers...@microsoft.com> The use of Common Name is fading out in favor of the RFC recommended way of using SAN extensions. For example, Chrome from version 58 will only match server name against SAN.
The following patch adds an optional flag to attach SAN extension of type DNS to the generated certificate based on the server name. --- doc/configuration.txt | 8 ++++++ include/haproxy/listener-t.h | 1 + src/cfgparse-ssl.c | 14 ++++++++++ src/ssl_sock.c | 53 ++++++++++++++++++++++++++++++++++++ 4 files changed, 76 insertions(+) diff --git a/doc/configuration.txt b/doc/configuration.txt index 1d3878bc1..9a7ae43f0 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -12166,6 +12166,14 @@ ca-sign-use-chain Enabling this flag will attach all public certificates encoded in `ca-sign-file` to the served certificate to the client, enabling trust. +ca-sign-use-san + This setting is only available when support for OpenSSL was built in. It is + the CA private key passphrase. This setting is optional and used only when + the dynamic generation of certificates is enabled. See + 'generate-certificates' for details. + Enabling this flag will add SAN extenstion of type DNS with the requested server name + inside the generated certificate. + ca-verify-file <cafile> This setting designates a PEM file from which to load CA certificates used to verify client's certificate. It designates CA certificates which must not be diff --git a/include/haproxy/listener-t.h b/include/haproxy/listener-t.h index 38ca2839f..47524ffd9 100644 --- a/include/haproxy/listener-t.h +++ b/include/haproxy/listener-t.h @@ -164,6 +164,7 @@ struct bind_conf { char *ca_sign_pass; /* CAKey passphrase */ int ca_sign_use_chain; /* Optionally attached the certificate chain to the served certificate */ + int ca_sign_use_san; /* Optionally add SAN entry to the generated certificate */ struct cert_key_and_chain * ca_sign_ckch; /* CA and possible certificate chain for ca generation */ #endif struct proxy *frontend; /* the frontend all these listeners belong to, or NULL */ diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index bf8d95a61..9d8d16dae 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -552,6 +552,19 @@ static int bind_parse_ca_sign_use_chain(char **args, int cur_arg, struct proxy * #endif } +/* parse the "ca-sign-use-san" bind keyword */ +static int bind_parse_ca_sign_use_san(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) +{ +#if (!defined(SSL_NO_GENERATE_CERTIFICATES) && defined(SSL_CTRL_SET_TLSEXT_HOSTNAME)) + conf->ca_sign_use_san = 1; + return 0; +#else + memprintf(err, "%sthis version of openssl cannot generate SSL certificates.\n", + err && *err ? *err : ""); + return ERR_ALERT | ERR_FATAL; +#endif +} + /* parse the "ca-sign-pass" bind keyword */ static int bind_parse_ca_sign_pass(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) { @@ -1723,6 +1736,7 @@ static struct bind_kw_list bind_kws = { "SSL", { }, { { "ca-sign-file", bind_parse_ca_sign_file, 1 }, /* set CAFile used to generate and sign server certs */ { "ca-sign-pass", bind_parse_ca_sign_pass, 1 }, /* set CAKey passphrase */ { "ca-sign-use-chain", bind_parse_ca_sign_use_chain, 0 }, /* enable attaching ca chain to generated certificate */ + { "ca-sign-use-san", bind_parse_ca_sign_use_san, 0 }, /* enable adding SAN extension to generated certificate */ { "ciphers", bind_parse_ciphers, 1 }, /* set SSL cipher suite */ #if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) { "ciphersuites", bind_parse_ciphersuites, 1 }, /* set TLS 1.3 cipher suite */ diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 7b2c6caef..adb27cc4a 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -1745,6 +1745,54 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out, #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME #ifndef SSL_NO_GENERATE_CERTIFICATES +int ssl_sock_add_san_ext(X509V3_CTX* ctx, X509* cert, const char *servername) { + int failure = 0; + X509_EXTENSION *san_ext = NULL; + char *san_name = NULL; + CONF *conf = NULL; + size_t buffer_size = strlen(servername)+sizeof("DNS:"); // Includes terminator + + conf = NCONF_new(NULL); + if (!conf) { + failure = 1; + goto cleanup; + } + + san_name = calloc(1, buffer_size); + if (!san_name) { + failure = 1; + goto cleanup; + } + + if ((buffer_size-1) != snprintf(san_name, buffer_size, "DNS:%s", servername)) { + failure = 1; + goto cleanup; + } + + // Build an extension based on the DNS entry above + san_ext = X509V3_EXT_nconf_nid(conf, ctx, NID_subject_alt_name, san_name); + if (!san_ext) { + failure = 1; + goto cleanup; + } + + // Add the extension + if (!X509_add_ext(cert, san_ext, -1 /* Add to end*/)) { + failure = 1; + goto cleanup; + } + + // Success + failure = 0; + +cleanup: + if (NULL != san_ext) X509_EXTENSION_free(san_ext); + if (NULL != san_name) free(san_name); + if (NULL != conf) NCONF_free(conf); + + return failure; +} + /* Create a X509 certificate with the specified servername and serial. This * function returns a SSL_CTX object or NULL if an error occurs. */ static SSL_CTX * @@ -1828,6 +1876,11 @@ ssl_sock_do_create_cert(const char *servername, struct bind_conf *bind_conf, SSL X509_EXTENSION_free(ext); } + /* Add SAN extension */ + if (bind_conf->ca_sign_use_san && ssl_sock_add_san_ext(&ctx, newcrt, servername)) { + goto mkcert_error; + } + /* Sign the certificate with the CA private key */ key_type = EVP_PKEY_base_id(capkey); -- 2.27.0