From: William Lallemand Date: Thu, 23 Nov 2023 15:35:52 +0000 (+0100) Subject: MINOR: ssl: move certificate selection in a dedicate function X-Git-Tag: v2.9-dev12~87 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9f2e07bf7b01dd32dcbc55d1452b7d50ac2c911d;p=thirdparty%2Fhaproxy.git MINOR: ssl: move certificate selection in a dedicate function The certificate selection used in the WolfSSL cert_cb and in the OpenSSL clienthello callback is the same, the function was duplicate to achieve the same. This patch move the selection code to a common function called ssl_sock_chose_sni_ctx(). The servername string is still lowered in the callback, however the search for the first dot in the string (wildp) is done in ssl_sock_chose_sni_ctx() The function uses the same certificate selection algorithm as before, it needs to know if you need rsa or ecdsa, the bind_conf to achieve the lookup, and the servername string. This patch moves the code for WolSSL only. --- diff --git a/src/ssl_sock.c b/src/ssl_sock.c index bd4f1d103e..5219f3e2b3 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -2319,6 +2319,94 @@ static void ssl_sock_switchctx_set(SSL *ssl, SSL_CTX *ctx) SSL_set_SSL_CTX(ssl, ctx); } +/* + * Return the right sni_ctx for a and a chosen (must be in lowercase) + * RSA and ECDSA capabilities of the client can also be used. + * + * 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, + int have_rsa_sig, int have_ecdsa_sig) +{ + struct ebmb_node *node, *n, *node_ecdsa = NULL, *node_rsa = NULL, *node_anonymous = NULL; + const char *wildp = NULL; + int i; + + /* look for the first dot for wildcard search */ + for (i = 0; servername[i] != '\0'; i++) { + if (servername[i] == '.') { + wildp = &servername[i]; + break; + } + } + + /* Look for an ECDSA, RSA and DSA certificate, first in the single + * name and if not found in the wildcard */ + for (i = 0; i < 2; i++) { + if (i == 0) /* lookup in full qualified names */ + node = ebst_lookup(&s->sni_ctx, trash.area); + else if (i == 1 && wildp) /* lookup in wildcards names */ + node = ebst_lookup(&s->sni_w_ctx, wildp); + else + break; + + for (n = node; n; n = ebmb_next_dup(n)) { + + /* lookup a not neg filter */ + if (!container_of(n, struct sni_ctx, name)->neg) { + struct sni_ctx *sni, *sni_tmp; + int skip = 0; + + if (i == 1 && wildp) { /* wildcard */ + /* If this is a wildcard, look for an exclusion on the same crt-list line */ + sni = container_of(n, struct sni_ctx, name); + list_for_each_entry(sni_tmp, &sni->ckch_inst->sni_ctx, by_ckch_inst) { + if (sni_tmp->neg && (strcmp((const char *)sni_tmp->name.key, trash.area) == 0)) { + skip = 1; + break; + } + } + if (skip) + continue; + } + + switch(container_of(n, struct sni_ctx, name)->kinfo.sig) { + case TLSEXT_signature_ecdsa: + if (!node_ecdsa) + node_ecdsa = n; + break; + case TLSEXT_signature_rsa: + if (!node_rsa) + node_rsa = n; + break; + default: /* TLSEXT_signature_anonymous|dsa */ + if (!node_anonymous) + node_anonymous = n; + break; + } + } + } + } + /* Once the certificates are found, select them depending on what is + * supported in the client and by key_signature priority order: EDSA > + * RSA > DSA */ + if (have_ecdsa_sig && node_ecdsa) + node = node_ecdsa; + else if (have_rsa_sig && node_rsa) + node = node_rsa; + else if (node_anonymous) + node = node_anonymous; + else if (node_ecdsa) + node = node_ecdsa; /* no ecdsa signature case (< TLSv1.2) */ + else + node = node_rsa; /* no rsa signature case (far far away) */ + + if (node) + return container_of(node, struct sni_ctx, name); + + return NULL; +} + #ifdef HAVE_SSL_CLIENT_HELLO_CB int ssl_sock_switchctx_err_cbk(SSL *ssl, int *al, void *priv) @@ -2783,10 +2871,8 @@ 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; - - char *wildp = NULL; const char *servername; - struct ebmb_node *node, *n, *node_ecdsa = NULL, *node_rsa = NULL, *node_anonymous = NULL; + struct sni_ctx *sni_ctx; int i; if (!s) { @@ -2848,84 +2934,18 @@ static int ssl_sock_switchctx_wolfSSL_cbk(WOLFSSL* ssl, void* arg) } } - for (i = 0; i < trash.size; i++) { - if (!servername[i]) - break; - trash.area[i] = tolower((unsigned char)servername[i]); - if (!wildp && (trash.area[i] == '.')) - wildp = &trash.area[i]; - } + /* 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]); trash.area[i] = 0; servername = trash.area; - HA_RWLOCK_RDLOCK(SNI_LOCK, &s->sni_lock); - - /* Look for an ECDSA, RSA and DSA certificate, first in the single - * name and if not found in the wildcard */ - for (i = 0; i < 2; i++) { - if (i == 0) /* lookup in full qualified names */ - node = ebst_lookup(&s->sni_ctx, servername); - else if (i == 1 && wildp) /* lookup in wildcards names */ - node = ebst_lookup(&s->sni_w_ctx, wildp); - else - break; - - for (n = node; n; n = ebmb_next_dup(n)) { - - /* lookup a not neg filter */ - if (!container_of(n, struct sni_ctx, name)->neg) { - struct sni_ctx *sni, *sni_tmp; - int skip = 0; - - if (i == 1 && wildp) { /* wildcard */ - /* If this is a wildcard, look for an exclusion on the same crt-list line */ - sni = container_of(n, struct sni_ctx, name); - list_for_each_entry(sni_tmp, &sni->ckch_inst->sni_ctx, by_ckch_inst) { - if (sni_tmp->neg && (strcmp((const char *)sni_tmp->name.key, servername) == 0)) { - skip = 1; - break; - } - } - if (skip) - continue; - } - - switch(container_of(n, struct sni_ctx, name)->kinfo.sig) { - case TLSEXT_signature_ecdsa: - if (!node_ecdsa) - node_ecdsa = n; - break; - case TLSEXT_signature_rsa: - if (!node_rsa) - node_rsa = n; - break; - default: /* TLSEXT_signature_anonymous|dsa */ - if (!node_anonymous) - node_anonymous = n; - break; - } - } - } - } - /* Once the certificates are found, select them depending on what is - * supported in the client and by key_signature priority order: EDSA > - * RSA > DSA */ - if (has_ecdsa_sig && node_ecdsa) - node = node_ecdsa; - else if (has_rsa_sig && node_rsa) - node = node_rsa; - else if (node_anonymous) - node = node_anonymous; - else if (node_ecdsa) - node = node_ecdsa; /* no ecdsa signature case (< TLSv1.2) */ - else - node = node_rsa; /* no rsa signature case (far far away) */ - - if (node) { + sni_ctx = ssl_sock_chose_sni_ctx(s, servername, has_rsa_sig, has_ecdsa_sig); + if (sni_ctx) { /* switch ctx */ - struct ssl_bind_conf *conf = container_of(node, struct sni_ctx, name)->conf; - ssl_sock_switchctx_set(ssl, container_of(node, struct sni_ctx, name)->ctx); + struct ssl_bind_conf *conf = sni_ctx->conf; + ssl_sock_switchctx_set(ssl, sni_ctx->ctx); if (conf) { methodVersions[conf->ssl_methods.min].ssl_set_version(ssl, SET_MIN); methodVersions[conf->ssl_methods.max].ssl_set_version(ssl, SET_MAX); @@ -2969,7 +2989,6 @@ allow_early: } #endif - #ifndef OPENSSL_NO_DH static inline HASSL_DH *ssl_new_dh_fromdata(BIGNUM *p, BIGNUM *g)