]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: improve crt-list format to support negation
authorEmmanuel Hocdet <manu@gandi.net>
Tue, 7 May 2013 18:20:06 +0000 (20:20 +0200)
committerWilly Tarreau <w@1wt.eu>
Tue, 7 May 2013 20:11:54 +0000 (22:11 +0200)
Improve the crt-list file format to allow a rule to negate a certain SNI :

        <crtfile> [[!]<snifilter> ...]

This can be useful when a domain supports a wildcard but you don't want to
deliver the wildcard cert for certain specific domains.

doc/configuration.txt
include/types/ssl_sock.h
src/ssl_sock.c

index 67c18ecc57762141b7f9f559cce605111305dc21..eb26285d9802dc8fb7c927553513ff82dec223f1 100644 (file)
@@ -7224,17 +7224,19 @@ crt-ignore-err <errors>
 
 crt-list <file>
   This setting is only available when support for OpenSSL was built in. It
-  designates a list of PEM file with an optional SNI filter per certificate,
-  with the following format for each line :
-
-        <crtfile> [<snifilter>]
-
-  Wildcards are supported in the SNI filter. The certificates will be presented
-  to clients who provide a valid TLS Server Name Indication field matching one
-  of SNI filter. If no SNI filter is specified the CN and alt subjects are
-  used. This directive may be specified multiple times. See the "crt" option
-  for more information. The default certificate is still needed to meet OpenSSL
-  expectations. If it is not used, the strict-sni option may be used.
+  designates a list of PEM file with an optional list of SNI filter per
+  certificate, with the following format for each line :
+
+        <crtfile> [[!]<snifilter> ...]
+
+  Wildcards are supported in the SNI filter. Negative filter are also supported,
+  only useful in combination with a wildcard filter to exclude a particular SNI.
+  The certificates will be presented to clients who provide a valid TLS Server
+  Name Indication field matching one of the SNI filters. If no SNI filter is
+  specified, the CN and alt subjects are used. This directive may be specified
+  multiple times. See the "crt" option for more information. The default
+  certificate is still needed to meet OpenSSL expectations. If it is not used,
+  the 'strict-sni' option may be used.
 
 defer-accept
   Is an optional keyword which is supported only on certain Linux kernels. It
index 1ded15e53d05dee197c59106b017e87d3fe0832f..a0b2d79ce7b49800e9ae00dc760256c745dc197c 100644 (file)
@@ -28,6 +28,7 @@
 struct sni_ctx {
        SSL_CTX *ctx;             /* context associated to the certificate */
        int order;                /* load order for the certificate */
+       int neg;                  /* reject if match */
        struct ebmb_node name;    /* node holding the servername value */
 };
 
index 3ee699195f5bfac94327b228f938426c1f629a6c..34f24b56dc6fb8f26250d26e096ecdeb6dd84285 100644 (file)
@@ -189,16 +189,15 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
 {
        const char *servername;
        const char *wildp = NULL;
-       struct ebmb_node *node;
+       struct ebmb_node *node, *n;
        int i;
        (void)al; /* shut gcc stupid warning */
 
        servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name);
        if (!servername) {
-               if (s->strict_sni)
-                       return SSL_TLSEXT_ERR_ALERT_FATAL;
-               else
-                       return SSL_TLSEXT_ERR_NOACK;
+               return (s->strict_sni ?
+                       SSL_TLSEXT_ERR_ALERT_FATAL :
+                       SSL_TLSEXT_ERR_ALERT_WARNING);
        }
 
        for (i = 0; i < trash.size; i++) {
@@ -212,25 +211,27 @@ static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct bind_conf *s)
 
        /* lookup in full qualified names */
        node = ebst_lookup(&s->sni_ctx, trash.str);
-       if (!node) {
-               if (!wildp) {
-                       if (s->strict_sni)
-                               return SSL_TLSEXT_ERR_ALERT_FATAL;
-                       else
-                               return SSL_TLSEXT_ERR_ALERT_WARNING;
+
+       /* lookup a not neg filter */
+       for (n = node; n; n = ebmb_next_dup(n)) {
+               if (!container_of(n, struct sni_ctx, name)->neg) {
+                       node = n;
+                       break;
                }
-               /* lookup in full wildcards names */
+               wildp = NULL; /* never match a wildcard after matching a neg */
+       }
+       if (!node && wildp) {
+               /* lookup in wildcards names */
                node = ebst_lookup(&s->sni_w_ctx, wildp);
-               if (!node) {
-                       if (s->strict_sni)
-                               return SSL_TLSEXT_ERR_ALERT_FATAL;
-                       else
-                               return SSL_TLSEXT_ERR_ALERT_WARNING;
-               }
+       }
+       if (!node || container_of(node, struct sni_ctx, name)->neg) {
+               return (s->strict_sni ?
+                       SSL_TLSEXT_ERR_ALERT_FATAL :
+                       SSL_TLSEXT_ERR_ALERT_WARNING);
        }
 
        /* switch ctx */
-       SSL_set_SSL_CTX(ssl,  container_of(node, struct sni_ctx, name)->ctx);
+       SSL_set_SSL_CTX(ssl, container_of(node, struct sni_ctx, name)->ctx);
        return SSL_TLSEXT_ERR_OK;
 }
 #endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
@@ -309,24 +310,32 @@ end:
 }
 #endif
 
-int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int len, int order)
+static int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int order)
 {
        struct sni_ctx *sc;
-       int wild = 0;
-       int j;
-
-       if (len) {
-               if (*name == '*') {
-                       wild = 1;
-                       name++;
-                       len--;
-               }
+       int wild = 0, neg = 0;
+
+       if (*name == '!') {
+               neg = 1;
+               name++;
+       }
+       if (*name == '*') {
+               wild = 1;
+               name++;
+       }
+       /* !* filter is a nop */
+       if (neg && wild)
+               return order;
+       if (*name) {
+               int j, len;
+               len = strlen(name);
                sc = malloc(sizeof(struct sni_ctx) + len + 1);
                for (j = 0; j < len; j++)
                        sc->name.key[j] = tolower(name[j]);
                sc->name.key[len] = 0;
-               sc->order = order++;
                sc->ctx = ctx;
+               sc->order = order++;
+               sc->neg = neg;
                if (wild)
                        ebst_insert(&s->sni_w_ctx, &sc->name);
                else
@@ -338,11 +347,11 @@ int ssl_sock_add_cert_sni(SSL_CTX *ctx, struct bind_conf *s, char *name, int len
 /* Loads a certificate key and CA chain from a file. Returns 0 on error, -1 if
  * an early error happens and the caller must call SSL_CTX_free() by itelf.
  */
-int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_conf *s, char **sni_filter, int fcount)
+static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_conf *s, char **sni_filter, int fcount)
 {
        BIO *in;
        X509 *x = NULL, *ca;
-       int i, len, err;
+       int i, err;
        int ret = -1;
        int order = 0;
        X509_NAME *xname;
@@ -364,7 +373,7 @@ int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_co
 
        if (fcount) {
                while (fcount--)
-                       order = ssl_sock_add_cert_sni(ctx, s, sni_filter[fcount], strlen(sni_filter[fcount]), order);
+                       order = ssl_sock_add_cert_sni(ctx, s, sni_filter[fcount], order);
        }
        else {
 #ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
@@ -374,8 +383,7 @@ int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_co
                                GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
                                if (name->type == GEN_DNS) {
                                        if (ASN1_STRING_to_UTF8((unsigned char **)&str, name->d.dNSName) >= 0) {
-                                               len = strlen(str);
-                                               order = ssl_sock_add_cert_sni(ctx, s, str, len, order);
+                                               order = ssl_sock_add_cert_sni(ctx, s, str, order);
                                                OPENSSL_free(str);
                                        }
                                }
@@ -388,8 +396,7 @@ int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct bind_co
                while ((i = X509_NAME_get_index_by_NID(xname, NID_commonName, i)) != -1) {
                        X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, i);
                        if (ASN1_STRING_to_UTF8((unsigned char **)&str, entry->value) >= 0) {
-                               len = strlen(str);
-                               order = ssl_sock_add_cert_sni(ctx, s, str, len, order);
+                               order = ssl_sock_add_cert_sni(ctx, s, str, order);
                                OPENSSL_free(str);
                        }
                }
@@ -538,7 +545,7 @@ static int ssl_initialize_random()
 
 int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct proxy *curproxy, char **err)
 {
-       char thisline[65536];
+       char thisline[LINESIZE];
        FILE *f;
        int linenum = 0;
        int cfgerr = 0;
@@ -591,6 +598,8 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct
                        }
                        line++;
                }
+               if (cfgerr)
+                       break;
 
                /* empty line */
                if (!arg)