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;