]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MINOR: ssl: move certificate selection in a dedicate function
authorWilliam Lallemand <wlallemand@haproxy.com>
Thu, 23 Nov 2023 15:35:52 +0000 (16:35 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Fri, 24 Nov 2023 19:07:27 +0000 (20:07 +0100)
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.

src/ssl_sock.c

index bd4f1d103ea0eb9de2aabf241d3f0b04737d1071..5219f3e2b3f756b3bd5e83141323e150ed1e5fa4 100644 (file)
@@ -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 <bind_conf> and a chosen <servername> (must be in lowercase)
+ * RSA <have_rsa_sig> and ECDSA <have_ecdsa_sig> 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)