From: Emmanuel Hocdet Date: Tue, 7 May 2013 18:20:06 +0000 (+0200) Subject: MEDIUM: ssl: improve crt-list format to support negation X-Git-Tag: v1.5-dev19~41 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7c41a1b59b005a75914121a604ede449374b8de7;p=thirdparty%2Fhaproxy.git MEDIUM: ssl: improve crt-list format to support negation Improve the crt-list file format to allow a rule to negate a certain SNI : [[!] ...] This can be useful when a domain supports a wildcard but you don't want to deliver the wildcard cert for certain specific domains. --- diff --git a/doc/configuration.txt b/doc/configuration.txt index 67c18ecc57..eb26285d98 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -7224,17 +7224,19 @@ crt-ignore-err crt-list 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 : - - [] - - 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 : + + [[!] ...] + + 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 diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h index 1ded15e53d..a0b2d79ce7 100644 --- a/include/types/ssl_sock.h +++ b/include/types/ssl_sock.h @@ -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 */ }; diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 3ee699195f..34f24b56dc 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -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)