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


Reply via email to