From fc0421fde990a28018054aa2c5c85eb89ec59400 Mon Sep 17 00:00:00 2001 From: Emeric Brun Date: Fri, 7 Sep 2012 17:30:07 +0200 Subject: [PATCH] MEDIUM: ssl: add support for SNI and wildcard certificates A side effect of this change is that the "ssl" keyword on "bind" lines is now just a boolean and that "crt" is needed to designate certificate files or directories. Note that much refcounting was needed to have the free() work correctly due to the number of cert aliases which can make a context be shared by multiple names. --- include/proto/ssl_sock.h | 8 +- include/types/protocols.h | 5 +- include/types/ssl_sock.h | 34 ++++ src/cfgparse.c | 119 ++++-------- src/haproxy.c | 8 +- src/ssl_sock.c | 392 +++++++++++++++++++++++++++++++++++++- 6 files changed, 475 insertions(+), 91 deletions(-) create mode 100644 include/types/ssl_sock.h diff --git a/include/proto/ssl_sock.h b/include/proto/ssl_sock.h index c630f08d8..9d0462442 100644 --- a/include/proto/ssl_sock.h +++ b/include/proto/ssl_sock.h @@ -21,12 +21,18 @@ #ifndef _PROTO_SSL_SOCK_H #define _PROTO_SSL_SOCK_H +#include +#include #include extern struct data_ops ssl_sock; int ssl_sock_handshake(struct connection *conn, unsigned int flag); -void ssl_sock_infocbk(const SSL *ssl, int where, int ret); +int ssl_sock_load_cert(char *path, struct ssl_conf *ssl_conf, struct proxy *proxy); +int ssl_sock_prepare_ctx(struct ssl_conf *ssl_conf, SSL_CTX *ctx, struct proxy *proxy); +void ssl_sock_free_certs(struct ssl_conf *ssl_conf); +int ssl_sock_prepare_all_ctx(struct ssl_conf *ssl_conf, struct proxy *px); +void ssl_sock_free_all_ctx(struct ssl_conf *ssl_conf); #endif /* _PROTO_SSL_SOCK_H */ diff --git a/include/types/protocols.h b/include/types/protocols.h index 1ff448eed..bd8b35515 100644 --- a/include/types/protocols.h +++ b/include/types/protocols.h @@ -98,11 +98,12 @@ enum { struct ssl_conf { #ifdef USE_OPENSSL char *ciphers; /* cipher suite to use if non-null */ - char *cert; /* ssl main certificate */ int nosslv3; /* disable SSLv3 */ int notlsv1; /* disable TLSv1 */ int prefer_server_ciphers; /* Prefer server ciphers */ - SSL_CTX *ctx; /* SSL configuration */ + SSL_CTX *default_ctx; /* SSL context of first/default certificate */ + struct eb_root sni_ctx; /* sni_ctx tree of all known certs full-names sorted by name */ + struct eb_root sni_w_ctx; /* sni_ctx tree of all known certs wildcards sorted by name */ #endif int ref_cnt; /* number of users of this config, maybe 0 on error */ struct list by_fe; /* next binding for the same frontend, or NULL */ diff --git a/include/types/ssl_sock.h b/include/types/ssl_sock.h new file mode 100644 index 000000000..1ded15e53 --- /dev/null +++ b/include/types/ssl_sock.h @@ -0,0 +1,34 @@ +/* + * include/types/ssl_sock.h + * SSL settings for listeners and servers + * + * Copyright (C) 2012 EXCELIANCE, Emeric Brun + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation, version 2.1 + * exclusively. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef _TYPES_SSL_SOCK_H +#define _TYPES_SSL_SOCK_H + +#include +#include + +struct sni_ctx { + SSL_CTX *ctx; /* context associated to the certificate */ + int order; /* load order for the certificate */ + struct ebmb_node name; /* node holding the servername value */ +}; + +#endif /* _TYPES_SSL_SOCK_H */ diff --git a/src/cfgparse.c b/src/cfgparse.c index 27bed6ce5..282c128e6 100644 --- a/src/cfgparse.c +++ b/src/cfgparse.c @@ -62,14 +62,15 @@ #include #include #include -#include #include -#ifdef USE_OPENSSL -#include -#endif /*USE_OPENSSL */ #include #include +#ifdef USE_OPENSSL +#include +#include +#include +#endif /*USE_OPENSSL */ /* This is the SSLv3 CLIENT HELLO packet used in conjunction with the * ssl-hello-chk option to ensure that the remote server speaks SSL. @@ -1909,7 +1910,6 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) if (!ssl_conf) ssl_conf = ssl_conf_alloc(&curproxy->conf.ssl_bind, file, linenum, args[1]); - ssl_conf->cert = strdup(args[cur_arg + 1]); for (l = curproxy->listen; l != last_listen; l = l->next) { if (!l->ssl_conf) { @@ -1918,6 +1918,33 @@ int cfg_parse_listen(const char *file, int linenum, char **args, int kwm) } } + cur_arg += 1; + continue; +#else + Alert("parsing [%s:%d] : '%s' : '%s' option not implemented.\n", + file, linenum, args[0], args[cur_arg]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; +#endif + } + + if (!strcmp(args[cur_arg], "crt")) { /* use ssl certificate */ +#ifdef USE_OPENSSL + if (!*args[cur_arg + 1]) { + Alert("parsing [%s:%d] : '%s' : missing certificate.\n", + file, linenum, args[0]); + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + + if (!ssl_conf) + ssl_conf = ssl_conf_alloc(&curproxy->conf.ssl_bind, file, linenum, args[1]); + + if (ssl_sock_load_cert(args[cur_arg + 1], ssl_conf, curproxy) > 0) { + err_code |= ERR_ALERT | ERR_FATAL; + goto out; + } + cur_arg += 2; continue; #else @@ -6883,94 +6910,26 @@ out_uri_auth_compat: } #ifdef USE_OPENSSL -#ifndef SSL_OP_CIPHER_SERVER_PREFERENCE /* needs OpenSSL >= 0.9.7 */ -#define SSL_OP_CIPHER_SERVER_PREFERENCE 0 -#endif - -#ifndef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION /* needs OpenSSL >= 0.9.7 */ -#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 0 -#endif -#ifndef SSL_OP_NO_COMPRESSION /* needs OpenSSL >= 0.9.9 */ -#define SSL_OP_NO_COMPRESSION 0 -#endif -#ifndef SSL_MODE_RELEASE_BUFFERS /* needs OpenSSL >= 1.0.0 */ -#define SSL_MODE_RELEASE_BUFFERS 0 -#endif /* Configure SSL for each bind line. * Note: if configuration fails at some point, the ->ctx member * remains NULL so that listeners can later detach. */ list_for_each_entry(ssl_conf, &curproxy->conf.ssl_bind, by_fe) { - int ssloptions = - SSL_OP_ALL | /* all known workarounds for bugs */ - SSL_OP_NO_SSLv2 | - SSL_OP_NO_COMPRESSION | - SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; - int sslmode = - SSL_MODE_ENABLE_PARTIAL_WRITE | - SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | - SSL_MODE_RELEASE_BUFFERS; - SSL_CTX *ctx; - - if (!ssl_conf->cert) { - Alert("Proxy '%s': no SSL certificate specified for bind '%s' at [%s:%d] (use 'ssl').\n", + if (!ssl_conf->default_ctx) { + Alert("Proxy '%s': no SSL certificate specified for bind '%s' at [%s:%d] (use 'crt').\n", curproxy->id, ssl_conf->arg, ssl_conf->file, ssl_conf->line); cfgerr++; continue; } - ctx = SSL_CTX_new(SSLv23_server_method()); - if (!ctx) { - Alert("Proxy '%s': unable to allocate SSL context for bind '%s' at [%s:%d] using cert '%s'.\n", - curproxy->id, ssl_conf->arg, ssl_conf->file, ssl_conf->line, ssl_conf->cert); - cfgerr++; - continue; - } - if (ssl_conf->nosslv3) - ssloptions |= SSL_OP_NO_SSLv3; - if (ssl_conf->notlsv1) - ssloptions |= SSL_OP_NO_TLSv1; - if (ssl_conf->prefer_server_ciphers) - ssloptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; - SSL_CTX_set_options(ctx, ssloptions); - SSL_CTX_set_mode(ctx, sslmode); - SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); if (shared_context_init(global.tune.sslcachesize) < 0) { Alert("Unable to allocate SSL session cache.\n"); cfgerr++; - SSL_CTX_free(ctx); - continue; - } - shared_context_set_cache(ctx); - if (ssl_conf->ciphers && - !SSL_CTX_set_cipher_list(ctx, ssl_conf->ciphers)) { - Alert("Proxy '%s': unable to set SSL cipher list to '%s' for bind '%s' at [%s:%d] using cert '%s'.\n", - curproxy->id, ssl_conf->ciphers, ssl_conf->arg, - ssl_conf->file, ssl_conf->line, ssl_conf->cert); - cfgerr++; - SSL_CTX_free(ctx); continue; } - SSL_CTX_set_info_callback(ctx, ssl_sock_infocbk); - - if (SSL_CTX_use_PrivateKey_file(ctx, ssl_conf->cert, SSL_FILETYPE_PEM) <= 0) { - Alert("Proxy '%s': unable to load SSL private key from file '%s' in bind '%s' at [%s:%d].\n", - curproxy->id, ssl_conf->cert, ssl_conf->arg, ssl_conf->file, ssl_conf->line); - cfgerr++; - SSL_CTX_free(ctx); - continue; - } - - if (SSL_CTX_use_certificate_chain_file(ctx, ssl_conf->cert) <= 0) { - Alert("Proxy '%s': unable to load SSL certificate from file '%s' in bind '%s' at [%s:%d].\n", - curproxy->id, ssl_conf->cert, ssl_conf->arg, ssl_conf->file, ssl_conf->line); - cfgerr++; - SSL_CTX_free(ctx); - continue; - } - - ssl_conf->ctx = ctx; + /* initialize all certificate contexts */ + cfgerr += ssl_sock_prepare_all_ctx(ssl_conf, curproxy); } #endif /* USE_OPENSSL */ @@ -6998,7 +6957,7 @@ out_uri_auth_compat: } #ifdef USE_OPENSSL if (listener->ssl_conf) { - if (listener->ssl_conf->ctx) { + if (listener->ssl_conf->default_ctx) { listener->data = &ssl_sock; /* SSL data layer */ } else { @@ -7042,10 +7001,8 @@ out_uri_auth_compat: if (ssl_conf->ref_cnt) continue; - if (ssl_conf->ctx) - SSL_CTX_free(ssl_conf->ctx); + ssl_sock_free_all_ctx(ssl_conf); free(ssl_conf->ciphers); - free(ssl_conf->cert); free(ssl_conf->file); free(ssl_conf->arg); LIST_DEL(&ssl_conf->by_fe); diff --git a/src/haproxy.c b/src/haproxy.c index e16646000..42430586b 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -92,6 +92,10 @@ #include #endif +#ifdef USE_OPENSSL +#include +#endif + /*********************************************************************/ /*********************************************************************/ @@ -1015,10 +1019,8 @@ void deinit(void) /* Release unused SSL configs. */ list_for_each_entry_safe(ssl_conf, ssl_back, &p->conf.ssl_bind, by_fe) { - if (ssl_conf->ctx) - SSL_CTX_free(ssl_conf->ctx); + ssl_sock_free_all_ctx(ssl_conf); free(ssl_conf->ciphers); - free(ssl_conf->cert); free(ssl_conf->file); free(ssl_conf->arg); LIST_DEL(&ssl_conf->by_fe); diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 4047c7120..531d56b49 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -11,10 +11,14 @@ */ #define _GNU_SOURCE +#include +#include #include #include #include #include +#include +#include #include #include @@ -23,6 +27,10 @@ #include #include +#include +#include +#include +#include #include #include @@ -32,18 +40,21 @@ #include #include +#include + +#include +#include + #include #include #include #include #include #include +#include #include #include -#include - - static int sslconns = 0; void ssl_sock_infocbk(const SSL *ssl, int where, int ret) @@ -58,6 +69,379 @@ void ssl_sock_infocbk(const SSL *ssl, int where, int ret) } } +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME +/* Sets the SSL ctx of to match the advertised server name. Returns a + * warning when no match is found, which implies the default (first) cert + * will keep being used. + */ +static int ssl_sock_switchctx_cbk(SSL *ssl, int *al, struct ssl_conf *s) +{ + const char *servername; + const char *wildp = NULL; + struct ebmb_node *node; + int i; + (void)al; /* shut gcc stupid warning */ + + servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); + if (!servername) + return SSL_TLSEXT_ERR_NOACK; + + for (i = 0; i < trashlen; i++) { + if (!servername[i]) + break; + trash[i] = tolower(servername[i]); + if (!wildp && (trash[i] == '.')) + wildp = &trash[i]; + } + trash[i] = 0; + + /* lookup in full qualified names */ + node = ebst_lookup(&s->sni_ctx, trash); + if (!node) { + if (!wildp) + return SSL_TLSEXT_ERR_ALERT_WARNING; + + /* lookup in full wildcards names */ + node = ebst_lookup(&s->sni_w_ctx, wildp); + if (!node) + return SSL_TLSEXT_ERR_ALERT_WARNING; + } + + /* switch 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 */ + +/* 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 ssl_conf *s) +{ + BIO *in; + X509 *x = NULL, *ca; + int i, len, err; + int ret = -1; + int order = 0; + X509_NAME *xname; + char *str; + struct sni_ctx *sc; +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + STACK_OF(GENERAL_NAME) *names; +#endif + + in = BIO_new(BIO_s_file()); + if (in == NULL) + goto end; + + if (BIO_read_filename(in, file) <= 0) + goto end; + + x = PEM_read_bio_X509_AUX(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata); + if (x == NULL) + goto end; + +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + names = X509_get_ext_d2i(x, NID_subject_alt_name, NULL, NULL); + if (names) { + for (i = 0; i < sk_GENERAL_NAME_num(names); i++) { + 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) { + if ((len = strlen(str))) { + int j; + + if (*str != '*') { + sc = malloc(sizeof(struct sni_ctx) + len + 1); + for (j = 0; j < len; j++) + sc->name.key[j] = tolower(str[j]); + sc->name.key[len] = 0; + sc->order = order++; + sc->ctx = ctx; + ebst_insert(&s->sni_ctx, &sc->name); + } + else { + sc = malloc(sizeof(struct sni_ctx) + len); + for (j = 1; j < len; j++) + sc->name.key[j-1] = tolower(str[j]); + sc->name.key[len-1] = 0; + sc->order = order++; + sc->ctx = ctx; + ebst_insert(&s->sni_w_ctx, &sc->name); + } + } + OPENSSL_free(str); + } + } + } + sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free); + } +#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */ + + xname = X509_get_subject_name(x); + i = -1; + 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) { + if ((len = strlen(str))) { + int j; + + if (*str != '*') { + sc = malloc(sizeof(struct sni_ctx) + len + 1); + for (j = 0; j < len; j++) + sc->name.key[j] = tolower(str[j]); + sc->name.key[len] = 0; + sc->order = order++; + sc->ctx = ctx; + ebst_insert(&s->sni_ctx, &sc->name); + } + else { + sc = malloc(sizeof(struct sni_ctx) + len); + for (j = 1; j < len; j++) + sc->name.key[j-1] = tolower(str[j]); + sc->name.key[len-1] = 0; + sc->order = order++; + sc->ctx = ctx; + ebst_insert(&s->sni_w_ctx, &sc->name); + } + } + OPENSSL_free(str); + } + } + + ret = 0; /* the caller must not free the SSL_CTX argument anymore */ + if (!SSL_CTX_use_certificate(ctx, x)) + goto end; + + if (ctx->extra_certs != NULL) { + sk_X509_pop_free(ctx->extra_certs, X509_free); + ctx->extra_certs = NULL; + } + + while ((ca = PEM_read_bio_X509(in, NULL, ctx->default_passwd_callback, ctx->default_passwd_callback_userdata))) { + if (!SSL_CTX_add_extra_chain_cert(ctx, ca)) { + X509_free(ca); + goto end; + } + } + + err = ERR_get_error(); + if (!err || (ERR_GET_LIB(err) == ERR_LIB_PEM && ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { + /* we successfully reached the last cert in the file */ + ret = 1; + } + ERR_clear_error(); + +end: + if (x) + X509_free(x); + + if (in) + BIO_free(in); + + return ret; +} + +int ssl_sock_load_cert_file(const char *path, struct ssl_conf *ssl_conf, struct proxy *curproxy) +{ + int ret; + SSL_CTX *ctx; + + ctx = SSL_CTX_new(SSLv23_server_method()); + if (!ctx) { + Alert("Proxy '%s': unable to allocate SSL context for bind '%s' at [%s:%d] using cert '%s'.\n", + curproxy->id, ssl_conf->arg, ssl_conf->file, ssl_conf->line, path); + return 1; + } + + if (SSL_CTX_use_PrivateKey_file(ctx, path, SSL_FILETYPE_PEM) <= 0) { + Alert("Proxy '%s': unable to load SSL private key from file '%s' in bind '%s' at [%s:%d].\n", + curproxy->id, path, ssl_conf->arg, ssl_conf->file, ssl_conf->line); + SSL_CTX_free(ctx); + return 1; + } + + ret = ssl_sock_load_cert_chain_file(ctx, path, ssl_conf); + if (ret <= 0) { + Alert("Proxy '%s': unable to load SSL certificate from file '%s' in bind '%s' at [%s:%d].\n", + curproxy->id, path, ssl_conf->arg, ssl_conf->file, ssl_conf->line); + if (ret < 0) /* serious error, must do that ourselves */ + SSL_CTX_free(ctx); + return 1; + } + /* we must not free the SSL_CTX anymore below, since it's already in + * the tree, so it will be discovered and cleaned in time. + */ +#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME + if (ssl_conf->default_ctx) { + Alert("Proxy '%s': file '%s' : this version of openssl cannot load multiple SSL certificates in bind '%s' at [%s:%d].\n", + curproxy->id, path, ssl_conf->arg, ssl_conf->file, ssl_conf->line); + return 1; + } +#endif + if (!ssl_conf->default_ctx) + ssl_conf->default_ctx = ctx; + + return 0; +} + +int ssl_sock_load_cert(char *path, struct ssl_conf *ssl_conf, struct proxy *curproxy) +{ + struct dirent *de; + DIR *dir; + struct stat buf; + int pathlen = 0; + char *end, *fp; + int cfgerr = 0; + + if (!(dir = opendir(path))) + return ssl_sock_load_cert_file(path, ssl_conf, curproxy); + + /* strip trailing slashes, including first one */ + for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--) + *end = 0; + + if (end >= path) + pathlen = end + 1 - path; + fp = malloc(pathlen + 1 + NAME_MAX + 1); + + while ((de = readdir(dir))) { + snprintf(fp, pathlen + 1 + NAME_MAX + 1, "%s/%s", path, de->d_name); + if (stat(fp, &buf) != 0) { + Alert("Proxy '%s': unable to stat SSL certificate from file '%s' in bind '%s' at [%s:%d] : %s.\n", + curproxy->id, fp, ssl_conf->arg, ssl_conf->file, ssl_conf->line, strerror(errno)); + cfgerr++; + continue; + } + if (!S_ISREG(buf.st_mode)) + continue; + cfgerr += ssl_sock_load_cert_file(fp, ssl_conf, curproxy); + } + free(fp); + closedir(dir); + return cfgerr; +} + +#ifndef SSL_OP_CIPHER_SERVER_PREFERENCE /* needs OpenSSL >= 0.9.7 */ +#define SSL_OP_CIPHER_SERVER_PREFERENCE 0 +#endif + +#ifndef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION /* needs OpenSSL >= 0.9.7 */ +#define SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION 0 +#endif +#ifndef SSL_OP_NO_COMPRESSION /* needs OpenSSL >= 0.9.9 */ +#define SSL_OP_NO_COMPRESSION 0 +#endif +#ifndef SSL_MODE_RELEASE_BUFFERS /* needs OpenSSL >= 1.0.0 */ +#define SSL_MODE_RELEASE_BUFFERS 0 +#endif +int ssl_sock_prepare_ctx(struct ssl_conf *ssl_conf, SSL_CTX *ctx, struct proxy *curproxy) +{ + int cfgerr = 0; + int ssloptions = + SSL_OP_ALL | /* all known workarounds for bugs */ + SSL_OP_NO_SSLv2 | + SSL_OP_NO_COMPRESSION | + SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION; + int sslmode = + SSL_MODE_ENABLE_PARTIAL_WRITE | + SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER | + SSL_MODE_RELEASE_BUFFERS; + + if (ssl_conf->nosslv3) + ssloptions |= SSL_OP_NO_SSLv3; + if (ssl_conf->notlsv1) + ssloptions |= SSL_OP_NO_TLSv1; + if (ssl_conf->prefer_server_ciphers) + ssloptions |= SSL_OP_CIPHER_SERVER_PREFERENCE; + + SSL_CTX_set_options(ctx, ssloptions); + SSL_CTX_set_mode(ctx, sslmode); + SSL_CTX_set_verify(ctx, SSL_VERIFY_NONE, NULL); + + shared_context_set_cache(ctx); + if (ssl_conf->ciphers && + !SSL_CTX_set_cipher_list(ctx, ssl_conf->ciphers)) { + Alert("Proxy '%s': unable to set SSL cipher list to '%s' for bind '%s' at [%s:%d].\n", + curproxy->id, ssl_conf->ciphers, ssl_conf->arg, ssl_conf->file, ssl_conf->line); + cfgerr++; + } + + SSL_CTX_set_info_callback(ctx, ssl_sock_infocbk); +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME + SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_cbk); + SSL_CTX_set_tlsext_servername_arg(ctx, ssl_conf); +#endif + return cfgerr; +} + +/* Walks down the two trees in ssl_conf and prepares all certs. The pointer may + * be NULL, in which case nothing is done. Returns the number of errors + * encountered. + */ +int ssl_sock_prepare_all_ctx(struct ssl_conf *ssl_conf, struct proxy *px) +{ + struct ebmb_node *node; + struct sni_ctx *sni; + int err = 0; + + if (!ssl_conf) + return 0; + + node = ebmb_first(&ssl_conf->sni_ctx); + while (node) { + sni = ebmb_entry(node, struct sni_ctx, name); + if (!sni->order) /* only initialize the CTX on its first occurrence */ + err += ssl_sock_prepare_ctx(ssl_conf, sni->ctx, px); + node = ebmb_next(node); + } + + node = ebmb_first(&ssl_conf->sni_w_ctx); + while (node) { + sni = ebmb_entry(node, struct sni_ctx, name); + if (!sni->order) /* only initialize the CTX on its first occurrence */ + err += ssl_sock_prepare_ctx(ssl_conf, sni->ctx, px); + node = ebmb_next(node); + } + return err; +} + +/* Walks down the two trees in ssl_conf and frees all the certs. The pointer may + * be NULL, in which case nothing is done. The default_ctx is nullified too. + */ +void ssl_sock_free_all_ctx(struct ssl_conf *ssl_conf) +{ + struct ebmb_node *node, *back; + struct sni_ctx *sni; + + if (!ssl_conf) + return; + + node = ebmb_first(&ssl_conf->sni_ctx); + while (node) { + sni = ebmb_entry(node, struct sni_ctx, name); + back = ebmb_next(node); + ebmb_delete(node); + if (!sni->order) /* only free the CTX on its first occurrence */ + SSL_CTX_free(sni->ctx); + free(sni); + node = back; + } + + node = ebmb_first(&ssl_conf->sni_w_ctx); + while (node) { + sni = ebmb_entry(node, struct sni_ctx, name); + back = ebmb_next(node); + ebmb_delete(node); + if (!sni->order) /* only free the CTX on its first occurrence */ + SSL_CTX_free(sni->ctx); + free(sni); + node = back; + } + + ssl_conf->default_ctx = NULL; +} + /* * This function is called if SSL * context is not yet allocated. The function * is designed to be called before any other data-layer operation and sets the @@ -96,7 +480,7 @@ static int ssl_sock_init(struct connection *conn) } else if (target_client(&conn->target)) { /* Alloc a new SSL session ctx */ - conn->data_ctx = SSL_new(target_client(&conn->target)->ssl_conf->ctx); + conn->data_ctx = SSL_new(target_client(&conn->target)->ssl_conf->default_ctx); if (!conn->data_ctx) return -1;