diff options
Diffstat (limited to 'src/ssl_sock.c')
-rw-r--r-- | src/ssl_sock.c | 844 |
1 files changed, 249 insertions, 595 deletions
diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 96d826e..e6bf3ff 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -72,6 +72,7 @@ #include <haproxy/shctx.h> #include <haproxy/ssl_ckch.h> #include <haproxy/ssl_crtlist.h> +#include <haproxy/ssl_gencert.h> #include <haproxy/ssl_sock.h> #include <haproxy/ssl_utils.h> #include <haproxy/stats.h> @@ -135,9 +136,12 @@ struct global_ssl global_ssl = { #ifdef HAVE_SSL_KEYLOG .keylog = 0, #endif + .security_level = -1, #ifndef OPENSSL_NO_OCSP .ocsp_update.delay_max = SSL_OCSP_UPDATE_DELAY_MAX, .ocsp_update.delay_min = SSL_OCSP_UPDATE_DELAY_MIN, + .ocsp_update.mode = SSL_SOCK_OCSP_UPDATE_OFF, + .ocsp_update.disable = 0, #endif }; @@ -156,7 +160,7 @@ enum { SSL_ST_STATS_COUNT /* must be the last member of the enum */ }; -static struct name_desc ssl_stats[] = { +static struct stat_col ssl_stats[] = { [SSL_ST_SESS] = { .name = "ssl_sess", .desc = "Total number of ssl sessions established" }, [SSL_ST_REUSED_SESS] = { .name = "ssl_reused_sess", @@ -171,13 +175,37 @@ static struct ssl_counters { long long failed_handshake; } ssl_counters; -static void ssl_fill_stats(void *data, struct field *stats) +static int ssl_fill_stats(void *data, struct field *stats, unsigned int *selected_field) { struct ssl_counters *counters = data; + unsigned int current_field = (selected_field != NULL ? *selected_field : 0); - stats[SSL_ST_SESS] = mkf_u64(FN_COUNTER, counters->sess); - stats[SSL_ST_REUSED_SESS] = mkf_u64(FN_COUNTER, counters->reused_sess); - stats[SSL_ST_FAILED_HANDSHAKE] = mkf_u64(FN_COUNTER, counters->failed_handshake); + for (; current_field < SSL_ST_STATS_COUNT; current_field++) { + struct field metric = { 0 }; + + switch (current_field) { + case SSL_ST_SESS: + metric = mkf_u64(FN_COUNTER, counters->sess); + break; + case SSL_ST_REUSED_SESS: + metric = mkf_u64(FN_COUNTER, counters->reused_sess); + break; + case SSL_ST_FAILED_HANDSHAKE: + metric = mkf_u64(FN_COUNTER, counters->failed_handshake); + break; + default: + /* not used for frontends. If a specific metric + * is requested, return an error. Otherwise continue. + */ + if (selected_field != NULL) + return 0; + continue; + } + stats[current_field] = metric; + if (selected_field != NULL) + break; + } + return 1; } static struct stats_module ssl_stats_module = { @@ -504,38 +532,8 @@ static HASSL_DH *global_dh = NULL; static HASSL_DH *local_dh_1024 = NULL; static HASSL_DH *local_dh_2048 = NULL; static HASSL_DH *local_dh_4096 = NULL; -#if (HA_OPENSSL_VERSION_NUMBER < 0x3000000fL) -static DH *ssl_get_tmp_dh_cbk(SSL *ssl, int export, int keylen); -#else -static void ssl_sock_set_tmp_dh_from_pkey(SSL_CTX *ctx, EVP_PKEY *pkey); -#endif #endif /* OPENSSL_NO_DH */ -#if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined SSL_NO_GENERATE_CERTIFICATES) -/* X509V3 Extensions that will be added on generated certificates */ -#define X509V3_EXT_SIZE 5 -static char *x509v3_ext_names[X509V3_EXT_SIZE] = { - "basicConstraints", - "nsComment", - "subjectKeyIdentifier", - "authorityKeyIdentifier", - "keyUsage", -}; -static char *x509v3_ext_values[X509V3_EXT_SIZE] = { - "CA:FALSE", - "\"OpenSSL Generated Certificate\"", - "hash", - "keyid,issuer:always", - "nonRepudiation,digitalSignature,keyEncipherment" -}; -/* LRU cache to store generated certificate */ -static struct lru64_head *ssl_ctx_lru_tree = NULL; -static unsigned int ssl_ctx_lru_seed = 0; -static unsigned int ssl_ctx_serial; -__decl_rwlock(ssl_ctx_lru_rwlock); - -#endif // SSL_CTRL_SET_TLSEXT_HOSTNAME - /* The order here matters for picking a default context, * keep the most common keytype at the bottom of the list */ @@ -1109,40 +1107,40 @@ static int tlskeys_finalize_config(void) * Returns 1 if no ".ocsp" file found, 0 if OCSP status extension is * successfully enabled, or -1 in other error case. */ -static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data *data, STACK_OF(X509) *chain) +static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_store *store, STACK_OF(X509) *chain) { + struct ckch_data *data = store->data; X509 *x, *issuer; int i, ret = -1; struct certificate_ocsp *ocsp = NULL, *iocsp; char *warn = NULL; unsigned char *p; -#ifndef USE_OPENSSL_WOLFSSL -#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) - int (*callback) (SSL *, void *); -#else - void (*callback) (void); -#endif +#ifdef USE_OPENSSL_WOLFSSL + /* typedef int(*tlsextStatusCb)(WOLFSSL* ssl, void*); */ + tlsextStatusCb callback = NULL; +#elif (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) + int (*callback) (SSL *, void *) = NULL; #else - tlsextStatusCb callback; + void (*callback) (void) = NULL; #endif struct buffer *ocsp_uri = get_trash_chunk(); char *err = NULL; size_t path_len; + int inc_refcount_store = 0; + int enable_auto_update = (store->conf.ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) || + (store->conf.ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_DFLT && + global_ssl.ocsp_update.mode == SSL_SOCK_OCSP_UPDATE_ON); x = data->cert; if (!x) goto out; ssl_ocsp_get_uri_from_cert(x, ocsp_uri, &err); - /* We should have an "OCSP URI" field in order for auto update to work. */ - if (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON && b_data(ocsp_uri) == 0) - goto out; - - /* In case of ocsp update mode set to 'on', this function might be - * called with no known ocsp response. If no ocsp uri can be found in - * the certificate, nothing needs to be done here. */ if (!data->ocsp_response && !data->ocsp_cid) { - if (data->ocsp_update_mode != SSL_SOCK_OCSP_UPDATE_ON || b_data(ocsp_uri) == 0) { + /* In case of ocsp update mode set to 'on', this function might + * be called with no known ocsp response. If no ocsp uri can be + * found in the certificate, nothing needs to be done here. */ + if (!enable_auto_update || b_data(ocsp_uri) == 0) { ret = 0; goto out; } @@ -1163,8 +1161,10 @@ static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data * if (!issuer) goto out; - if (!data->ocsp_cid) + if (!data->ocsp_cid) { data->ocsp_cid = OCSP_cert_to_id(0, x, issuer); + inc_refcount_store = 1; + } if (!data->ocsp_cid) goto out; @@ -1185,12 +1185,11 @@ static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data * if (iocsp == ocsp) ocsp = NULL; -#ifndef SSL_CTX_get_tlsext_status_cb -# define SSL_CTX_get_tlsext_status_cb(ctx, cb) \ - *cb = (void (*) (void))ctx->tlsext_status_cb; -#endif SSL_CTX_get_tlsext_status_cb(ctx, &callback); + if (inc_refcount_store) + iocsp->refcount_store++; + if (!callback) { struct ocsp_cbk_arg *cb_arg; EVP_PKEY *pkey; @@ -1282,7 +1281,7 @@ static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data * */ memcpy(iocsp->path, path, path_len + 1); - if (data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) { + if (enable_auto_update) { ssl_ocsp_update_insert(iocsp); /* If we are during init the update task is not * scheduled yet so a wakeup won't do anything. @@ -1294,7 +1293,7 @@ static int ssl_sock_load_ocsp(const char *path, SSL_CTX *ctx, struct ckch_data * if (ocsp_update_task) task_wakeup(ocsp_update_task, TASK_WOKEN_MSG); } - } else if (iocsp->uri && data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) { + } else if (iocsp->uri && enable_auto_update) { /* This unlikely case can happen if a series of "del ssl * crt-list" / "add ssl crt-list" commands are made on the CLI. * In such a case, the OCSP response tree entry will be created @@ -1910,342 +1909,6 @@ static int ssl_sock_advertise_alpn_protos(SSL *s, const unsigned char **out, } #endif -#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME -#ifndef SSL_NO_GENERATE_CERTIFICATES - -/* Configure a DNS SAN extension on a certificate. */ -int ssl_sock_add_san_ext(X509V3_CTX* ctx, X509* cert, const char *servername) { - int failure = 0; - X509_EXTENSION *san_ext = NULL; - CONF *conf = NULL; - struct buffer *san_name = get_trash_chunk(); - - conf = NCONF_new(NULL); - if (!conf) { - failure = 1; - goto cleanup; - } - - /* Build an extension based on the DNS entry above */ - chunk_appendf(san_name, "DNS:%s", servername); - san_ext = X509V3_EXT_nconf_nid(conf, ctx, NID_subject_alt_name, san_name->area); - 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 != 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 * -ssl_sock_do_create_cert(const char *servername, struct bind_conf *bind_conf, SSL *ssl) -{ - X509 *cacert = bind_conf->ca_sign_ckch->cert; - EVP_PKEY *capkey = bind_conf->ca_sign_ckch->key; - SSL_CTX *ssl_ctx = NULL; - X509 *newcrt = NULL; - EVP_PKEY *pkey = NULL; - SSL *tmp_ssl = NULL; - CONF *ctmp = NULL; - X509_NAME *name; - const EVP_MD *digest; - X509V3_CTX ctx; - unsigned int i; - int key_type; - - /* Get the private key of the default certificate and use it */ -#ifdef HAVE_SSL_CTX_get0_privatekey - pkey = SSL_CTX_get0_privatekey(bind_conf->default_ctx); -#else - tmp_ssl = SSL_new(bind_conf->default_ctx); - if (tmp_ssl) - pkey = SSL_get_privatekey(tmp_ssl); -#endif - if (!pkey) - goto mkcert_error; - - /* Create the certificate */ - if (!(newcrt = X509_new())) - goto mkcert_error; - - /* Set version number for the certificate (X509v3) and the serial - * number */ - if (X509_set_version(newcrt, 2L) != 1) - goto mkcert_error; - ASN1_INTEGER_set(X509_get_serialNumber(newcrt), _HA_ATOMIC_ADD_FETCH(&ssl_ctx_serial, 1)); - - /* Set duration for the certificate */ - if (!X509_gmtime_adj(X509_getm_notBefore(newcrt), (long)-60*60*24) || - !X509_gmtime_adj(X509_getm_notAfter(newcrt),(long)60*60*24*365)) - goto mkcert_error; - - /* set public key in the certificate */ - if (X509_set_pubkey(newcrt, pkey) != 1) - goto mkcert_error; - - /* Set issuer name from the CA */ - if (!(name = X509_get_subject_name(cacert))) - goto mkcert_error; - if (X509_set_issuer_name(newcrt, name) != 1) - goto mkcert_error; - - /* Set the subject name using the same, but the CN */ - name = X509_NAME_dup(name); - if (X509_NAME_add_entry_by_txt(name, "CN", MBSTRING_ASC, - (const unsigned char *)servername, - -1, -1, 0) != 1) { - X509_NAME_free(name); - goto mkcert_error; - } - if (X509_set_subject_name(newcrt, name) != 1) { - X509_NAME_free(name); - goto mkcert_error; - } - X509_NAME_free(name); - - /* Add x509v3 extensions as specified */ - ctmp = NCONF_new(NULL); - X509V3_set_ctx(&ctx, cacert, newcrt, NULL, NULL, 0); - for (i = 0; i < X509V3_EXT_SIZE; i++) { - X509_EXTENSION *ext; - - if (!(ext = X509V3_EXT_nconf(ctmp, &ctx, x509v3_ext_names[i], x509v3_ext_values[i]))) - goto mkcert_error; - if (!X509_add_ext(newcrt, ext, -1)) { - X509_EXTENSION_free(ext); - goto mkcert_error; - } - X509_EXTENSION_free(ext); - } - - /* Add SAN extension */ - if (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); - - if (key_type == EVP_PKEY_DSA) - digest = EVP_sha1(); - else if (key_type == EVP_PKEY_RSA) - digest = EVP_sha256(); - else if (key_type == EVP_PKEY_EC) - digest = EVP_sha256(); - else { -#ifdef ASN1_PKEY_CTRL_DEFAULT_MD_NID - int nid; - - if (EVP_PKEY_get_default_digest_nid(capkey, &nid) <= 0) - goto mkcert_error; - if (!(digest = EVP_get_digestbynid(nid))) - goto mkcert_error; -#else - goto mkcert_error; -#endif - } - - if (!(X509_sign(newcrt, capkey, digest))) - goto mkcert_error; - - /* Create and set the new SSL_CTX */ - if (!(ssl_ctx = SSL_CTX_new(SSLv23_server_method()))) - goto mkcert_error; - if (!SSL_CTX_use_PrivateKey(ssl_ctx, pkey)) - goto mkcert_error; - if (!SSL_CTX_use_certificate(ssl_ctx, newcrt)) - goto mkcert_error; - if (!SSL_CTX_check_private_key(ssl_ctx)) - goto mkcert_error; - - /* Build chaining the CA cert and the rest of the chain, keep these order */ -#if defined(SSL_CTX_add1_chain_cert) - if (!SSL_CTX_add1_chain_cert(ssl_ctx, bind_conf->ca_sign_ckch->cert)) { - goto mkcert_error; - } - - if (bind_conf->ca_sign_ckch->chain) { - for (i = 0; i < sk_X509_num(bind_conf->ca_sign_ckch->chain); i++) { - X509 *chain_cert = sk_X509_value(bind_conf->ca_sign_ckch->chain, i); - if (!SSL_CTX_add1_chain_cert(ssl_ctx, chain_cert)) { - goto mkcert_error; - } - } - } -#endif - - if (newcrt) X509_free(newcrt); - -#ifndef OPENSSL_NO_DH -#if (HA_OPENSSL_VERSION_NUMBER < 0x3000000fL) - SSL_CTX_set_tmp_dh_callback(ssl_ctx, ssl_get_tmp_dh_cbk); -#else - ssl_sock_set_tmp_dh_from_pkey(ssl_ctx, pkey); -#endif -#endif - -#if (HA_OPENSSL_VERSION_NUMBER >= 0x10101000L) -#if defined(SSL_CTX_set1_curves_list) - { - const char *ecdhe = (bind_conf->ssl_conf.ecdhe ? bind_conf->ssl_conf.ecdhe : ECDHE_DEFAULT_CURVE); - if (!SSL_CTX_set1_curves_list(ssl_ctx, ecdhe)) - goto end; - } -#endif -#else -#if defined(SSL_CTX_set_tmp_ecdh) && !defined(OPENSSL_NO_ECDH) - { - const char *ecdhe = (bind_conf->ssl_conf.ecdhe ? bind_conf->ssl_conf.ecdhe : ECDHE_DEFAULT_CURVE); - EC_KEY *ecc; - int nid; - - if ((nid = OBJ_sn2nid(ecdhe)) == NID_undef) - goto end; - if (!(ecc = EC_KEY_new_by_curve_name(nid))) - goto end; - SSL_CTX_set_tmp_ecdh(ssl_ctx, ecc); - EC_KEY_free(ecc); - } -#endif /* defined(SSL_CTX_set_tmp_ecdh) && !defined(OPENSSL_NO_ECDH) */ -#endif /* HA_OPENSSL_VERSION_NUMBER >= 0x10101000L */ - end: - return ssl_ctx; - - mkcert_error: - if (ctmp) NCONF_free(ctmp); - if (tmp_ssl) SSL_free(tmp_ssl); - if (ssl_ctx) SSL_CTX_free(ssl_ctx); - if (newcrt) X509_free(newcrt); - return NULL; -} - - -/* Do a lookup for a certificate in the LRU cache used to store generated - * certificates and immediately assign it to the SSL session if not null. */ -SSL_CTX * -ssl_sock_assign_generated_cert(unsigned int key, struct bind_conf *bind_conf, SSL *ssl) -{ - struct lru64 *lru = NULL; - - if (ssl_ctx_lru_tree) { - HA_RWLOCK_WRLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - lru = lru64_lookup(key, ssl_ctx_lru_tree, bind_conf->ca_sign_ckch->cert, 0); - if (lru && lru->domain) { - if (ssl) - SSL_set_SSL_CTX(ssl, (SSL_CTX *)lru->data); - HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - return (SSL_CTX *)lru->data; - } - HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - } - return NULL; -} - -/* Same as <ssl_sock_assign_generated_cert> but without SSL session. This - * function is not thread-safe, it should only be used to check if a certificate - * exists in the lru cache (with no warranty it will not be removed by another - * thread). It is kept for backward compatibility. */ -SSL_CTX * -ssl_sock_get_generated_cert(unsigned int key, struct bind_conf *bind_conf) -{ - return ssl_sock_assign_generated_cert(key, bind_conf, NULL); -} - -/* Set a certificate int the LRU cache used to store generated - * certificate. Return 0 on success, otherwise -1 */ -int -ssl_sock_set_generated_cert(SSL_CTX *ssl_ctx, unsigned int key, struct bind_conf *bind_conf) -{ - struct lru64 *lru = NULL; - - if (ssl_ctx_lru_tree) { - HA_RWLOCK_WRLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - lru = lru64_get(key, ssl_ctx_lru_tree, bind_conf->ca_sign_ckch->cert, 0); - if (!lru) { - HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - return -1; - } - if (lru->domain && lru->data) - lru->free((SSL_CTX *)lru->data); - lru64_commit(lru, ssl_ctx, bind_conf->ca_sign_ckch->cert, 0, (void (*)(void *))SSL_CTX_free); - HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - return 0; - } - return -1; -} - -/* Compute the key of the certificate. */ -unsigned int -ssl_sock_generated_cert_key(const void *data, size_t len) -{ - return XXH32(data, len, ssl_ctx_lru_seed); -} - -/* Generate a cert and immediately assign it to the SSL session so that the cert's - * refcount is maintained regardless of the cert's presence in the LRU cache. - */ -static int -ssl_sock_generate_certificate(const char *servername, struct bind_conf *bind_conf, SSL *ssl) -{ - X509 *cacert = bind_conf->ca_sign_ckch->cert; - SSL_CTX *ssl_ctx = NULL; - struct lru64 *lru = NULL; - unsigned int key; - - key = ssl_sock_generated_cert_key(servername, strlen(servername)); - if (ssl_ctx_lru_tree) { - HA_RWLOCK_WRLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - lru = lru64_get(key, ssl_ctx_lru_tree, cacert, 0); - if (lru && lru->domain) - ssl_ctx = (SSL_CTX *)lru->data; - if (!ssl_ctx && lru) { - ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl); - lru64_commit(lru, ssl_ctx, cacert, 0, (void (*)(void *))SSL_CTX_free); - } - SSL_set_SSL_CTX(ssl, ssl_ctx); - HA_RWLOCK_WRUNLOCK(SSL_GEN_CERTS_LOCK, &ssl_ctx_lru_rwlock); - return 1; - } - else { - ssl_ctx = ssl_sock_do_create_cert(servername, bind_conf, ssl); - SSL_set_SSL_CTX(ssl, ssl_ctx); - /* No LRU cache, this CTX will be released as soon as the session dies */ - SSL_CTX_free(ssl_ctx); - return 1; - } - return 0; -} -static int -ssl_sock_generate_certificate_from_conn(struct bind_conf *bind_conf, SSL *ssl) -{ - unsigned int key; - struct connection *conn = SSL_get_ex_data(ssl, ssl_app_data_index); - - if (conn_get_dst(conn)) { - key = ssl_sock_generated_cert_key(conn->dst, get_addr_len(conn->dst)); - if (ssl_sock_assign_generated_cert(key, bind_conf, ssl)) - return 1; - } - return 0; -} -#endif /* !defined SSL_NO_GENERATE_CERTIFICATES */ - #if (HA_OPENSSL_VERSION_NUMBER < 0x1010000fL) static void ctx_set_SSLv3_func(SSL_CTX *ctx, set_context_func c) @@ -2351,7 +2014,7 @@ static void ssl_sock_switchctx_set(SSL *ssl, SSL_CTX *ctx) * * This function does a lookup in the bind_conf sni tree so the caller should lock its tree. */ -static __maybe_unused struct sni_ctx *ssl_sock_chose_sni_ctx(struct bind_conf *s, const char *servername, +struct sni_ctx *ssl_sock_chose_sni_ctx(struct bind_conf *s, const char *servername, int have_rsa_sig, int have_ecdsa_sig) { struct ebmb_node *node, *n, *node_ecdsa = NULL, *node_rsa = NULL, *node_anonymous = NULL; @@ -2365,6 +2028,9 @@ static __maybe_unused struct sni_ctx *ssl_sock_chose_sni_ctx(struct bind_conf *s break; } } + /* if the servername is empty look for the default in the wildcard list */ + if (!*servername) + wildp = servername; /* Look for an ECDSA, RSA and DSA certificate, first in the single * name and if not found in the wildcard */ @@ -2463,7 +2129,8 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg) int has_rsa_sig = 0, has_ecdsa_sig = 0; struct sni_ctx *sni_ctx; const char *servername; - size_t servername_len; + size_t servername_len = 0; + int default_lookup = 0; /* did we lookup for a default yet? */ int allow_early = 0; int i; @@ -2551,14 +2218,16 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg) goto allow_early; } #endif - /* without SNI extension, is the default_ctx (need SSL_TLSEXT_ERR_NOACK) */ - if (!s->strict_sni) { - HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); - ssl_sock_switchctx_set(ssl, s->default_ctx); - HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - goto allow_early; - } - goto abort; + + /* no servername field is not compatible with strict-sni */ + if (s->strict_sni) + goto abort; + + /* without servername extension, look for the defaults which is + * defined by an empty servername string */ + servername = ""; + servername_len = 0; + default_lookup = 1; } /* extract/check clientHello information */ @@ -2634,14 +2303,14 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg) } } +sni_lookup: /* we need to transform this a NULL-ended string in lowecase */ for (i = 0; i < trash.size && i < servername_len; i++) trash.area[i] = tolower(servername[i]); trash.area[i] = 0; - servername = trash.area; HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); - sni_ctx = ssl_sock_chose_sni_ctx(s, servername, has_rsa_sig, has_ecdsa_sig); + sni_ctx = ssl_sock_chose_sni_ctx(s, trash.area, has_rsa_sig, has_ecdsa_sig); if (sni_ctx) { /* switch ctx */ struct ssl_bind_conf *conf = sni_ctx->conf; @@ -2658,17 +2327,20 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *arg) HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); #if (!defined SSL_NO_GENERATE_CERTIFICATES) - if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate(servername, s, ssl)) { + if (s->options & BC_O_GENERATE_CERTS && ssl_sock_generate_certificate(trash.area, s, ssl)) { /* switch ctx done in ssl_sock_generate_certificate */ goto allow_early; } #endif - if (!s->strict_sni) { - /* no certificate match, is the default_ctx */ - HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); - ssl_sock_switchctx_set(ssl, s->default_ctx); - HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - goto allow_early; + + if (!s->strict_sni && !default_lookup) { + /* we didn't find a SNI, and we didn't look for a default + * look again to find a matching default cert */ + servername = ""; + servername_len = 0; + default_lookup = 1; + + goto sni_lookup; } /* We are about to raise an handshake error so the servername extension @@ -2722,6 +2394,7 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv) const char *wildp = NULL; struct ebmb_node *node, *n; struct bind_conf *s = priv; + int default_lookup = 0; /* did we lookup for a default yet? */ #ifdef USE_QUIC const uint8_t *extension_data; size_t extension_len; @@ -2761,12 +2434,15 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv) #endif if (s->strict_sni) return SSL_TLSEXT_ERR_ALERT_FATAL; - HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); - ssl_sock_switchctx_set(ssl, s->default_ctx); - HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - return SSL_TLSEXT_ERR_NOACK; + + /* without servername extension, look for the defaults which is + * defined by an empty servername string */ + servername = ""; + default_lookup = 1; } +sni_lookup: + for (i = 0; i < trash.size; i++) { if (!servername[i]) break; @@ -2775,6 +2451,8 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv) wildp = &trash.area[i]; } trash.area[i] = 0; + if(!*trash.area) /* handle the default which in wildcard tree */ + wildp = trash.area; HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); node = NULL; @@ -2804,24 +2482,35 @@ int ssl_sock_switchctx_cbk(SSL *ssl, int *al, void *priv) return SSL_TLSEXT_ERR_OK; } #endif - if (s->strict_sni) { - HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - return SSL_TLSEXT_ERR_ALERT_FATAL; - } - ssl_sock_switchctx_set(ssl, s->default_ctx); HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - return SSL_TLSEXT_ERR_OK; + + if (!s->strict_sni && !default_lookup) { + /* we didn't find a SNI, and we didn't look for a default + * look again to find a matching default cert */ + servername = ""; + default_lookup = 1; + + goto sni_lookup; + } + return SSL_TLSEXT_ERR_ALERT_FATAL; } +#if defined(OPENSSL_IS_AWSLC) + /* Note that ssl_sock_switchctx_set() calls SSL_set_SSL_CTX() which propagates the + * "early data enabled" setting from the SSL_CTX object to the SSL objects. + * So enable early data for this SSL_CTX context if configured. + */ + if (s->ssl_conf.early_data) + SSL_CTX_set_early_data_enabled(container_of(node, struct sni_ctx, name)->ctx, 1); +#endif /* switch ctx */ ssl_sock_switchctx_set(ssl, container_of(node, struct sni_ctx, name)->ctx); HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); return SSL_TLSEXT_ERR_OK; } #endif /* (!) OPENSSL_IS_BORINGSSL */ -#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */ -#if 0 && defined(USE_OPENSSL_WOLFSSL) +#if defined(USE_OPENSSL_WOLFSSL) /* This implement the equivalent of the clientHello Callback but using the cert_cb. * WolfSSL is able to extract the sigalgs and ciphers of the client byt using the API * provided in https://github.com/wolfSSL/wolfssl/pull/6963 @@ -2833,6 +2522,7 @@ static int ssl_sock_switchctx_wolfSSL_cbk(WOLFSSL* ssl, void* arg) struct bind_conf *s = arg; int has_rsa_sig = 0, has_ecdsa_sig = 0; const char *servername; + int default_lookup = 0; struct sni_ctx *sni_ctx; int i; @@ -2844,14 +2534,13 @@ static int ssl_sock_switchctx_wolfSSL_cbk(WOLFSSL* ssl, void* arg) servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (!servername) { - /* without SNI extension, is the default_ctx (need SSL_TLSEXT_ERR_NOACK) */ - if (!s->strict_sni) { - HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); - ssl_sock_switchctx_set(ssl, s->default_ctx); - HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - goto allow_early; - } - goto abort; + if (s->strict_sni) + goto abort; + + /* without servername extension, look for the defaults which is + * defined by an empty servername string */ + servername = ""; + default_lookup = 1; } /* extract sigalgs and ciphers */ @@ -2895,6 +2584,8 @@ static int ssl_sock_switchctx_wolfSSL_cbk(WOLFSSL* ssl, void* arg) } } +sni_lookup: + /* we need to transform this into a NULL-ended string in lowecase */ for (i = 0; i < trash.size && servername[i] != '\0'; i++) trash.area[i] = tolower(servername[i]); @@ -2916,12 +2607,13 @@ static int ssl_sock_switchctx_wolfSSL_cbk(WOLFSSL* ssl, void* arg) } HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - if (!s->strict_sni) { - /* no certificate match, is the default_ctx */ - HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); - ssl_sock_switchctx_set(ssl, s->default_ctx); - HA_RWLOCK_RDUNLOCK(SNI_LOCK, &s->sni_lock); - goto allow_early; + if (!s->strict_sni && !default_lookup) { + /* we didn't find a SNI, and we didn't look for a default + * look again to find a matching default cert */ + servername = ""; + default_lookup = 1; + + goto sni_lookup; } /* We are about to raise an handshake error so the servername extension @@ -3224,7 +2916,7 @@ static HASSL_DH *ssl_get_tmp_dh(EVP_PKEY *pkey) #if (HA_OPENSSL_VERSION_NUMBER < 0x3000000fL) /* Returns Diffie-Hellman parameters matching the private key length but not exceeding global_ssl.default_dh_param */ -static HASSL_DH *ssl_get_tmp_dh_cbk(SSL *ssl, int export, int keylen) +HASSL_DH *ssl_get_tmp_dh_cbk(SSL *ssl, int export, int keylen) { EVP_PKEY *pkey = SSL_get_privatekey(ssl); @@ -3250,7 +2942,7 @@ static int ssl_sock_set_tmp_dh(SSL_CTX *ctx, HASSL_DH *dh) } #if (HA_OPENSSL_VERSION_NUMBER >= 0x3000000fL) -static void ssl_sock_set_tmp_dh_from_pkey(SSL_CTX *ctx, EVP_PKEY *pkey) +void ssl_sock_set_tmp_dh_from_pkey(SSL_CTX *ctx, EVP_PKEY *pkey) { HASSL_DH *dh = NULL; if (pkey && (dh = ssl_get_tmp_dh(pkey))) { @@ -3335,7 +3027,7 @@ static int ckch_inst_add_cert_sni(SSL_CTX *ctx, struct ckch_inst *ckch_inst, struct pkey_info kinfo, char *name, int order) { struct sni_ctx *sc; - int wild = 0, neg = 0; + int wild = 0, neg = 0, default_crt = 0; if (*name == '!') { neg = 1; @@ -3344,11 +3036,14 @@ static int ckch_inst_add_cert_sni(SSL_CTX *ctx, struct ckch_inst *ckch_inst, if (*name == '*') { wild = 1; name++; + /* if this was only a '*' filter, this is a default cert */ + if (!*name) + default_crt = 1; } /* !* filter is a nop */ if (neg && wild) return order; - if (*name) { + if (*name || default_crt) { int j, len; len = strlen(name); for (j = 0; j < len && j < trash.size; j++) @@ -3420,14 +3115,6 @@ void ssl_sock_load_cert_sni(struct ckch_inst *ckch_inst, struct bind_conf *bind_ else ebst_insert(&bind_conf->sni_ctx, &sc0->name); } - - /* replace the default_ctx if required with the instance's ctx. */ - if (ckch_inst->is_default) { - SSL_CTX_free(bind_conf->default_ctx); - SSL_CTX_up_ref(ckch_inst->ctx); - bind_conf->default_ctx = ckch_inst->ctx; - bind_conf->default_inst = ckch_inst; - } } /* @@ -3625,9 +3312,10 @@ end: * The value 0 means there is no error nor warning and * the operation succeed. */ -static int ssl_sock_put_ckch_into_ctx(const char *path, struct ckch_data *data, SSL_CTX *ctx, char **err) +static int ssl_sock_put_ckch_into_ctx(const char *path, struct ckch_store *store, SSL_CTX *ctx, char **err) { int errcode = 0; + struct ckch_data *data = store->data; STACK_OF(X509) *find_chain = NULL; ERR_clear_error(); @@ -3679,7 +3367,7 @@ static int ssl_sock_put_ckch_into_ctx(const char *path, struct ckch_data *data, * ocsp tree even if no ocsp_response was known during init, unless the * frontend's conf disables ocsp update explicitly. */ - if (ssl_sock_load_ocsp(path, ctx, data, find_chain) < 0) { + if (ssl_sock_load_ocsp(path, ctx, store, find_chain) < 0) { if (data->ocsp_response) memprintf(err, "%s '%s.ocsp' is present and activates OCSP but it is impossible to compute the OCSP certificate ID (maybe the issuer could not be found)'.\n", err && *err ? *err : "", path); @@ -3744,7 +3432,7 @@ end: * ERR_WARN if a warning is available into err */ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf, - struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, struct ckch_inst **ckchi, char **err) + struct ssl_bind_conf *ssl_conf, char **sni_filter, int fcount, int is_default, struct ckch_inst **ckchi, char **err) { SSL_CTX *ctx; int i; @@ -3775,7 +3463,10 @@ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct goto error; } - errcode |= ssl_sock_put_ckch_into_ctx(path, data, ctx, err); + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); + + errcode |= ssl_sock_put_ckch_into_ctx(path, ckchs, ctx, err); if (errcode & ERR_CODE) goto error; @@ -3857,20 +3548,16 @@ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct * the tree, so it will be discovered and cleaned in time. */ -#ifndef SSL_CTRL_SET_TLSEXT_HOSTNAME - if (bind_conf->default_ctx) { - memprintf(err, "%sthis version of openssl cannot load multiple SSL certificates.\n", - err && *err ? *err : ""); - errcode |= ERR_ALERT | ERR_FATAL; - goto error; - } -#endif - if (!bind_conf->default_ctx) { - bind_conf->default_ctx = ctx; - bind_conf->default_ssl_conf = ssl_conf; + if (is_default) { ckch_inst->is_default = 1; - SSL_CTX_up_ref(ctx); - bind_conf->default_inst = ckch_inst; + + /* insert an empty SNI which will be used to lookup default certificate */ + order = ckch_inst_add_cert_sni(ctx, ckch_inst, bind_conf, ssl_conf, kinfo, "*", order); + if (order < 0) { + memprintf(err, "%sunable to create a sni context.\n", err && *err ? *err : ""); + errcode |= ERR_ALERT | ERR_FATAL; + goto error; + } } /* Always keep a reference to the newly constructed SSL_CTX in the @@ -3892,9 +3579,6 @@ int ckch_inst_new_load_store(const char *path, struct ckch_store *ckchs, struct error: /* free the allocated sni_ctxs */ if (ckch_inst) { - if (ckch_inst->is_default) - SSL_CTX_free(ctx); - ckch_inst_free(ckch_inst); ckch_inst = NULL; } @@ -3936,6 +3620,9 @@ int ckch_inst_new_load_srv_store(const char *path, struct ckch_store *ckchs, goto error; } + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); + errcode |= ssl_sock_put_srv_ckch_into_ctx(path, data, ctx, err); if (errcode & ERR_CODE) goto error; @@ -3967,12 +3654,14 @@ error: /* Returns a set of ERR_* flags possibly with an error in <err>. */ static int ssl_sock_load_ckchs(const char *path, struct ckch_store *ckchs, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf, - char **sni_filter, int fcount, struct ckch_inst **ckch_inst, char **err) + char **sni_filter, int fcount, + int is_default, + struct ckch_inst **ckch_inst, char **err) { int errcode = 0; /* we found the ckchs in the tree, we can use it directly */ - errcode |= ckch_inst_new_load_store(path, ckchs, bind_conf, ssl_conf, sni_filter, fcount, ckch_inst, err); + errcode |= ckch_inst_new_load_store(path, ckchs, bind_conf, ssl_conf, sni_filter, fcount, is_default, ckch_inst, err); if (errcode & ERR_CODE) return errcode; @@ -4081,9 +3770,17 @@ int ssl_sock_load_cert_list_file(char *file, int dir, struct bind_conf *bind_con list_for_each_entry(entry, &crtlist->ord_entries, by_crtlist) { struct ckch_store *store; struct ckch_inst *ckch_inst = NULL; + int is_default = 0; store = entry->node.key; - cfgerr |= ssl_sock_load_ckchs(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, &ckch_inst, err); + + /* if the SNI trees were empty the first "crt" become a default certificate, + * it can be applied on multiple certificates if it's a bundle */ + if (eb_is_empty(&bind_conf->sni_ctx) && eb_is_empty(&bind_conf->sni_w_ctx)) + is_default = 1; + + + cfgerr |= ssl_sock_load_ckchs(store->path, store, bind_conf, entry->ssl_conf, entry->filters, entry->fcount, is_default, &ckch_inst, err); if (cfgerr & ERR_CODE) { memprintf(err, "error processing line %d in file '%s' : %s", entry->linenum, file, *err); goto error; @@ -4125,7 +3822,7 @@ error: } /* Returns a set of ERR_* flags possibly with an error in <err>. */ -int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err) +int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, int is_default, char **err) { struct stat buf; int cfgerr = 0; @@ -4133,25 +3830,32 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err) struct ckch_inst *ckch_inst = NULL; int found = 0; /* did we found a file to load ? */ + /* if the SNI trees were empty the first "crt" become a default certificate, + * it can be applied on multiple certificates if it's a bundle */ + if (is_default == 0) { + if (eb_is_empty(&bind_conf->sni_ctx) && eb_is_empty(&bind_conf->sni_w_ctx)) + is_default = 1; + } + if ((ckchs = ckchs_lookup(path))) { - /* we found the ckchs in the tree, we can use it directly */ - cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, &ckch_inst, err); - /* This certificate has an 'ocsp-update' already set in a - * previous crt-list so we must raise an error. */ - if (ckchs->data->ocsp_update_mode == SSL_SOCK_OCSP_UPDATE_ON) { - memprintf(err, "%sIncompatibilities found in OCSP update mode for certificate %s\n", err && *err ? *err: "", path); - cfgerr |= ERR_ALERT | ERR_FATAL; - } + cfgerr |= ckch_conf_cmp_empty(&ckchs->conf, err); + if (cfgerr & ERR_CODE) { + memprintf(err, "Can't load '%s', is already defined with incompatible parameters:\n %s", path, err ? *err : ""); + return cfgerr; + } + + /* we found the ckchs in the tree, we can use it directly */ + cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, is_default, &ckch_inst, err); found++; } else if (stat(path, &buf) == 0) { found++; if (S_ISDIR(buf.st_mode) == 0) { - ckchs = ckchs_load_cert_file(path, err); + ckchs = ckch_store_new_load_files_path(path, err); if (!ckchs) cfgerr |= ERR_ALERT | ERR_FATAL; - cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, &ckch_inst, err); + cfgerr |= ssl_sock_load_ckchs(path, ckchs, bind_conf, NULL, NULL, 0, is_default, &ckch_inst, err); } else { cfgerr |= ssl_sock_load_cert_list_file(path, 1, bind_conf, bind_conf->frontend, err); } @@ -4171,15 +3875,15 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err) continue; if ((ckchs = ckchs_lookup(fp))) { - cfgerr |= ssl_sock_load_ckchs(fp, ckchs, bind_conf, NULL, NULL, 0, &ckch_inst, err); + cfgerr |= ssl_sock_load_ckchs(fp, ckchs, bind_conf, NULL, NULL, 0, is_default, &ckch_inst, err); found++; } else { if (stat(fp, &buf) == 0) { found++; - ckchs = ckchs_load_cert_file(fp, err); + ckchs = ckch_store_new_load_files_path(fp, err); if (!ckchs) cfgerr |= ERR_ALERT | ERR_FATAL; - cfgerr |= ssl_sock_load_ckchs(fp, ckchs, bind_conf, NULL, NULL, 0, &ckch_inst, err); + cfgerr |= ssl_sock_load_ckchs(fp, ckchs, bind_conf, NULL, NULL, 0, is_default, &ckch_inst, err); } } } @@ -4229,7 +3933,7 @@ int ssl_sock_load_srv_cert(char *path, struct server *server, int create_if_none /* We do not manage directories on backend side. */ if (S_ISDIR(buf.st_mode) == 0) { ++found; - ckchs = ckchs_load_cert_file(path, err); + ckchs = ckch_store_new_load_files_path(path, err); if (!ckchs) cfgerr |= ERR_ALERT | ERR_FATAL; cfgerr |= ssl_sock_load_srv_ckchs(path, ckchs, server, &server->ssl_ctx.inst, err); @@ -4274,6 +3978,9 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf) ctx = SSL_CTX_new(SSLv23_server_method()); bind_conf->initial_ctx = ctx; + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); + if (conf_ssl_methods->flags && (conf_ssl_methods->min || conf_ssl_methods->max)) ha_warning("Proxy '%s': no-sslv3/no-tlsv1x are ignored for bind '%s' at [%s:%d]. " "Use only 'ssl-min-ver' and 'ssl-max-ver' to fix.\n", @@ -4384,7 +4091,7 @@ ssl_sock_initial_ctx(struct bind_conf *bind_conf) # endif /* ! SSL_OP_NO_ANTI_REPLAY */ SSL_CTX_set_client_hello_cb(ctx, ssl_sock_switchctx_cbk, NULL); SSL_CTX_set_tlsext_servername_callback(ctx, ssl_sock_switchctx_err_cbk); -# elif 0 && defined(USE_OPENSSL_WOLFSSL) +# elif defined(USE_OPENSSL_WOLFSSL) SSL_CTX_set_cert_cb(ctx, ssl_sock_switchctx_wolfSSL_cbk, bind_conf); # else /* ! OPENSSL_IS_BORINGSSL && ! HAVE_SSL_CLIENT_HELLO_CB */ @@ -5270,6 +4977,8 @@ int ssl_sock_prepare_srv_ctx(struct server *srv) cfgerr++; return cfgerr; } + if (global_ssl.security_level > -1) + SSL_CTX_set_security_level(ctx, global_ssl.security_level); srv->ssl_ctx.ctx = ctx; } @@ -5429,6 +5138,16 @@ static int ssl_sock_prepare_srv_ssl_ctx(const struct server *srv, SSL_CTX *ctx) cfgerr++; } +#ifdef SSL_CTRL_SET_MSG_CALLBACK + SSL_CTX_set_msg_callback(ctx, ssl_sock_msgcbk); +#endif + +#ifdef HAVE_SSL_KEYLOG + /* only activate the keylog callback if it was required to prevent performance loss */ + if (global_ssl.keylog > 0) + SSL_CTX_set_keylog_callback(ctx, SSL_CTX_keylog); +#endif + #ifdef HAVE_SSL_CTX_SET_CIPHERSUITES if (srv->ssl_ctx.ciphersuites && !SSL_CTX_set_ciphersuites(ctx, srv->ssl_ctx.ciphersuites)) { @@ -5547,16 +5266,12 @@ int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf) to initial_ctx in ssl_initial_ctx. */ errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, NULL, bind_conf->initial_ctx, NULL, &errmsg); } - if (bind_conf->default_ctx) { - errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, bind_conf->default_ssl_conf, bind_conf->default_ctx, bind_conf->default_inst, &errmsg); - } node = ebmb_first(&bind_conf->sni_ctx); while (node) { sni = ebmb_entry(node, struct sni_ctx, name); - if (!sni->order && sni->ctx != bind_conf->default_ctx) { - /* only initialize the CTX on its first occurrence and - if it is not the default_ctx */ + if (!sni->order) { + /* only initialize the CTX on its first occurrence */ errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, sni->conf, sni->ctx, sni->ckch_inst, &errmsg); } node = ebmb_next(node); @@ -5565,9 +5280,8 @@ int ssl_sock_prepare_all_ctx(struct bind_conf *bind_conf) node = ebmb_first(&bind_conf->sni_w_ctx); while (node) { sni = ebmb_entry(node, struct sni_ctx, name); - if (!sni->order && sni->ctx != bind_conf->default_ctx) { - /* only initialize the CTX on its first occurrence and - if it is not the default_ctx */ + if (!sni->order) { + /* only initialize the CTX on its first occurrence */ errcode |= ssl_sock_prep_ctx_and_inst(bind_conf, sni->conf, sni->ctx, sni->ckch_inst, &errmsg); } node = ebmb_next(node); @@ -5594,14 +5308,17 @@ int ssl_sock_prepare_bind_conf(struct bind_conf *bind_conf) int alloc_ctx; int err; + /* check if some certificates were loaded but no ssl keyword is used */ if (!(bind_conf->options & BC_O_USE_SSL)) { - if (bind_conf->default_ctx) { + if (!eb_is_empty(&bind_conf->sni_ctx) || !eb_is_empty(&bind_conf->sni_w_ctx)) { ha_warning("Proxy '%s': A certificate was specified but SSL was not enabled on bind '%s' at [%s:%d] (use 'ssl').\n", px->id, bind_conf->arg, bind_conf->file, bind_conf->line); } return 0; } - if (!bind_conf->default_ctx) { + + /* check if we have certificates */ + if (eb_is_empty(&bind_conf->sni_ctx) && eb_is_empty(&bind_conf->sni_w_ctx)) { if (bind_conf->strict_sni && !(bind_conf->options & BC_O_GENERATE_CERTS)) { ha_warning("Proxy '%s': no SSL certificate specified for bind '%s' at [%s:%d], ssl connections will fail (use 'crt').\n", px->id, bind_conf->arg, bind_conf->file, bind_conf->line); @@ -5612,10 +5329,23 @@ int ssl_sock_prepare_bind_conf(struct bind_conf *bind_conf) return -1; } } + + if ((bind_conf->options & BC_O_GENERATE_CERTS)) { + struct sni_ctx *sni_ctx; + + /* if we use the generate-certificates option, look for the first default cert available */ + sni_ctx = ssl_sock_chose_sni_ctx(bind_conf, "", 1, 1); + if (!sni_ctx) { + ha_alert("Proxy '%s': no SSL certificate specified for bind '%s' and 'generate-certificates' option at [%s:%d] (use 'crt').\n", + px->id, bind_conf->arg, bind_conf->file, bind_conf->line); + return -1; + } + } + if (!ssl_shctx && global.tune.sslcachesize) { alloc_ctx = shctx_init(&ssl_shctx, global.tune.sslcachesize, sizeof(struct sh_ssl_sess_hdr) + SHSESS_BLOCK_MIN_SIZE, -1, - sizeof(*sh_ssl_sess_tree)); + sizeof(*sh_ssl_sess_tree), "ssl cache"); if (alloc_ctx <= 0) { if (alloc_ctx == SHCTX_E_INIT_LOCK) ha_alert("Unable to initialize the lock for the shared SSL session cache. You can retry using the global statement 'tune.ssl.force-private-cache' but it could increase CPU usage due to renegotiations if nbproc > 1.\n"); @@ -5713,10 +5443,6 @@ void ssl_sock_free_all_ctx(struct bind_conf *bind_conf) SSL_CTX_free(bind_conf->initial_ctx); bind_conf->initial_ctx = NULL; - SSL_CTX_free(bind_conf->default_ctx); - bind_conf->default_ctx = NULL; - bind_conf->default_inst = NULL; - bind_conf->default_ssl_conf = NULL; } @@ -5746,81 +5472,6 @@ void ssl_sock_destroy_bind_conf(struct bind_conf *bind_conf) bind_conf->ca_sign_file = NULL; } -/* Load CA cert file and private key used to generate certificates */ -int -ssl_sock_load_ca(struct bind_conf *bind_conf) -{ - struct proxy *px = bind_conf->frontend; - struct ckch_data *data = NULL; - int ret = 0; - char *err = NULL; - - if (!(bind_conf->options & BC_O_GENERATE_CERTS)) - return ret; - -#if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined SSL_NO_GENERATE_CERTIFICATES) - if (global_ssl.ctx_cache) { - ssl_ctx_lru_tree = lru64_new(global_ssl.ctx_cache); - } - ssl_ctx_lru_seed = (unsigned int)time(NULL); - ssl_ctx_serial = now_ms; -#endif - - if (!bind_conf->ca_sign_file) { - ha_alert("Proxy '%s': cannot enable certificate generation, " - "no CA certificate File configured at [%s:%d].\n", - px->id, bind_conf->file, bind_conf->line); - goto failed; - } - - /* Allocate cert structure */ - data = calloc(1, sizeof(*data)); - if (!data) { - ha_alert("Proxy '%s': Failed to read CA certificate file '%s' at [%s:%d]. Chain allocation failure\n", - px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line); - goto failed; - } - - /* Try to parse file */ - if (ssl_sock_load_files_into_ckch(bind_conf->ca_sign_file, data, &err)) { - ha_alert("Proxy '%s': Failed to read CA certificate file '%s' at [%s:%d]. Chain loading failed: %s\n", - px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line, err); - free(err); - goto failed; - } - - /* Fail if missing cert or pkey */ - if ((!data->cert) || (!data->key)) { - ha_alert("Proxy '%s': Failed to read CA certificate file '%s' at [%s:%d]. Chain missing certificate or private key\n", - px->id, bind_conf->ca_sign_file, bind_conf->file, bind_conf->line); - goto failed; - } - - /* Final assignment to bind */ - bind_conf->ca_sign_ckch = data; - return ret; - - failed: - if (data) { - ssl_sock_free_cert_key_and_chain_contents(data); - free(data); - } - - bind_conf->options &= ~BC_O_GENERATE_CERTS; - ret++; - return ret; -} - -/* Release CA cert and private key used to generate certificated */ -void -ssl_sock_free_ca(struct bind_conf *bind_conf) -{ - if (bind_conf->ca_sign_ckch) { - ssl_sock_free_cert_key_and_chain_contents(bind_conf->ca_sign_ckch); - ha_free(&bind_conf->ca_sign_ckch); - } -} - /* * Try to allocate the BIO and SSL session objects of <conn> connection with <bio> and * <ssl> as addresses, <bio_meth> as BIO method and <ssl_ctx> as SSL context inherited settings. @@ -6060,7 +5711,7 @@ static int ssl_sock_init(struct connection *conn, void **xprt_ctx) #ifdef SSL_READ_EARLY_DATA_SUCCESS if (bc->ssl_conf.early_data) { - b_alloc(&ctx->early_buf); + b_alloc(&ctx->early_buf, DB_MUX_RX); SSL_set_max_early_data(ctx->ssl, /* Only allow early data if we managed to allocate * a buffer. @@ -6516,19 +6167,26 @@ static int ssl_unsubscribe(struct connection *conn, void *xprt_ctx, int event_ty * It should be called with the takeover lock for the old thread held. * Returns 0 on success, and -1 on failure */ -static int ssl_takeover(struct connection *conn, void *xprt_ctx, int orig_tid) +static int ssl_takeover(struct connection *conn, void *xprt_ctx, int orig_tid, int release) { struct ssl_sock_ctx *ctx = xprt_ctx; - struct tasklet *tl = tasklet_new(); + struct tasklet *tl = NULL; - if (!tl) - return -1; + if (!release) { + tl = tasklet_new(); + if (!tl) + return -1; + } ctx->wait_event.tasklet->context = NULL; tasklet_wakeup_on(ctx->wait_event.tasklet, orig_tid); + ctx->wait_event.tasklet = tl; - ctx->wait_event.tasklet->process = ssl_sock_io_cb; - ctx->wait_event.tasklet->context = ctx; + if (!release) { + ctx->wait_event.tasklet->process = ssl_sock_io_cb; + ctx->wait_event.tasklet->context = ctx; + } + return 0; } @@ -6558,7 +6216,7 @@ static void ssl_set_used(struct connection *conn, void *xprt_ctx) if (!ctx || !ctx->wait_event.tasklet) return; - HA_ATOMIC_OR(&ctx->wait_event.tasklet->state, TASK_F_USR1); + HA_ATOMIC_AND(&ctx->wait_event.tasklet->state, ~TASK_F_USR1); if (ctx->xprt) xprt_set_used(conn, ctx->xprt, ctx->xprt_ctx); } @@ -7873,6 +7531,8 @@ static void __ssl_sock_init(void) xprt_register(XPRT_SSL, &ssl_sock); #if HA_OPENSSL_VERSION_NUMBER < 0x10100000L SSL_library_init(); +#elif HA_OPENSSL_VERSION_NUMBER >= 0x10100000L + OPENSSL_init_ssl(0, NULL); #endif #if (!defined(OPENSSL_NO_COMP) && !defined(SSL_OP_NO_COMPRESSION)) cm = SSL_COMP_get_compression_methods(); @@ -8068,12 +7728,6 @@ void ssl_free_dh(void) { static void __ssl_sock_deinit(void) { -#if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined SSL_NO_GENERATE_CERTIFICATES) - if (ssl_ctx_lru_tree) { - lru64_destroy(ssl_ctx_lru_tree); - HA_RWLOCK_DESTROY(&ssl_ctx_lru_rwlock); - } -#endif #if (HA_OPENSSL_VERSION_NUMBER < 0x10100000L) ERR_remove_state(0); |