Author: brane
Date: Fri Jan  2 19:26:19 2026
New Revision: 1931058

Log:
Split the client certificate callback implementation into two functions,
one that uses OSSL_STORE and the ancient/original that reads PKCS#12 files.

* buckets/ssl_buckets.c
  (ssl_read_client_cert_uri): New, conditionally present. Reads certificates
   and private keys from a keystore, which can be a PKCS#12 file or an
   OS-specific certificat stor URI. The implementation was moved from
   ssl_need_client_cert() with minor changes.

  (ssl_need_client_cert): Delegate to ssl_read_client_cert_uri() first, then
   continue with the file-based attempt if no certificates were found.

Modified:
   serf/trunk/buckets/ssl_buckets.c

Modified: serf/trunk/buckets/ssl_buckets.c
==============================================================================
--- serf/trunk/buckets/ssl_buckets.c    Fri Jan  2 14:10:38 2026        
(r1931057)
+++ serf/trunk/buckets/ssl_buckets.c    Fri Jan  2 19:26:19 2026        
(r1931058)
@@ -1609,225 +1609,201 @@ static int ssl_pass_cb(UI *ui, UI_STRING
     return 1;
 }
 
-#endif
-
-static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
+static int ssl_read_client_cert_uri(serf_ssl_context_t *ctx,
+                                    SSL *ssl, X509 **cert, EVP_PKEY **pkey)
 {
-    serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
-#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+    /* NOTE: If we're here, ctx->cert_uri_callback will be set. */
+
     STACK_OF(X509) *leaves;
     STACK_OF(X509) *intermediates;
     STACK_OF(EVP_PKEY) *keys;
     X509_STORE *requests;
     UI_METHOD *ui_method;
-#endif
-    apr_status_t status;
-    int retrying_success = 0;
+    int result = 0;
 
-    serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
-              "Server requests a client certificate.\n");
+    STACK_OF(X509_NAME) *requested = NULL;
+    const char *cert_uri = NULL;
+    OSSL_STORE_CTX *store = NULL;
+    OSSL_STORE_INFO *info = NULL;
 
-    if (ctx->cached_cert) {
-        *cert = ctx->cached_cert;
-        *pkey = ctx->cached_cert_pw;
-        return 1;
+    int retrying_success;
+    apr_status_t status;
+    int store_error;
+    X509 *x509;
+    int type;
+
+    if (ctx->cert_uri_success) {
+        retrying_success = 1;
+        status = APR_SUCCESS;
+        cert_uri = ctx->cert_uri_success;
+        ctx->cert_uri_success = NULL;
+    } else {
+        retrying_success = 0;
+        status = ctx->cert_uri_callback(ctx->cert_uri_userdata, &cert_uri);
     }
 
-#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
-    /* FIXME: This is completely messed up. Extract the OPENSSL_STORE
-              part into a separate function instead of using break
-              + ERR_peek_error() for something that, for lack of a more
-              friendly term, might be called "error handling". */
+    if (status || !cert_uri) {
+        return 0;
+    }
 
-    /* until further notice */
-    *cert = NULL;
-    *pkey = NULL;
+    ctx->cert_uri = cert_uri;
+
+    ui_method = UI_create_method("passphrase");
+    UI_method_set_reader(ui_method, ssl_pass_cb);
+
+    store = OSSL_STORE_open(cert_uri, ui_method, ctx, NULL, NULL);
+    if (!store) {
+        char ebuf[1024];
+        ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
+        apr_snprintf(ebuf, sizeof(ebuf), "could not open URI: %s", cert_uri);
+        dispatch_ssl_error(&global_error_ctx, ctx->fatal_err, ebuf);
+
+        log_ssl_error(&global_error_ctx, ctx->fatal_err);
+        UI_destroy_method(ui_method);
+        return 0;
+    }
 
+    /* walk the store, what are we working with */
     leaves = sk_X509_new_null();
     intermediates = sk_X509_new_null();
     keys = sk_EVP_PKEY_new_null();
     requests = X509_STORE_new();
 
-    ui_method = UI_create_method("passphrase");
-    UI_method_set_reader(ui_method, ssl_pass_cb);
+    /* NOTE: No more direct 'return's from this point onwards!
+             The cleanup code must be called. */
 
-    while (ctx->cert_uri_callback) {
-        const char *cert_uri = NULL;
-        OSSL_STORE_CTX *store = NULL;
-        OSSL_STORE_INFO *info;
-        X509 *x509;
-        STACK_OF(X509_NAME) *requested;
-        int type;
-
-        retrying_success = 0;
+    /* server side request some certs? this list may be empty */
+    requested = SSL_get_client_CA_list(ssl);
 
-        if (ctx->cert_uri_success) {
-            status = APR_SUCCESS;
-            cert_uri = ctx->cert_uri_success;
-            ctx->cert_uri_success = NULL;
-            retrying_success = 1;
-        } else {
-            status = ctx->cert_uri_callback(ctx->cert_uri_userdata, &cert_uri);
-        }
-
-        if (status || !cert_uri) {
+    store_error = 0;
+    for (;;) {
+        info = OSSL_STORE_load(store);
+        if (OSSL_STORE_eof(store)) {
+            /* NOTE: OSSL_STORE_eof() is not signalled until after the
+                     first OSSL_STORE_load() fails. */
             break;
         }
 
-        ctx->cert_uri = cert_uri;
-
-        /* server side request some certs? this list may be empty */
-        requested = SSL_get_client_CA_list(ssl);
-
-        store = OSSL_STORE_open(cert_uri, ui_method, ctx, NULL, NULL);
-        if (!store) {
+        if (!info) {
             char ebuf[1024];
             ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
-            apr_snprintf(ebuf, sizeof(ebuf), "could not open URI: %s", 
cert_uri);
+            apr_snprintf(ebuf, sizeof(ebuf), "could not read URI: %s", 
cert_uri);
             dispatch_ssl_error(&global_error_ctx, ctx->fatal_err, ebuf);
-
-            log_ssl_error(&global_error_ctx, ctx->fatal_err);
+            store_error = 1;
             break;
         }
 
-        /* walk the store, what are we working with */
+        type = OSSL_STORE_INFO_get_type(info);
+        if (type == OSSL_STORE_INFO_CERT) {
+            X509 *c = OSSL_STORE_INFO_get1_CERT(info);
 
-        for (;;) {
-            info = OSSL_STORE_load(store);
-            if (OSSL_STORE_eof(store)) {
-                /* NOTE: OSSL_STORE_eof() is not signalled until *after* the
-                         first OSSL_STORE_load() fails. */
-                break;
-            }
+            int n, i;
 
-            if (!info) {
-                char ebuf[1024];
-                ctx->fatal_err = SERF_ERROR_SSL_CERT_FAILED;
-                apr_snprintf(ebuf, sizeof(ebuf), "could not read URI: %s", 
cert_uri);
-                dispatch_ssl_error(&global_error_ctx, ctx->fatal_err, ebuf);
+            int is_ca = X509_check_ca(c);
 
-                log_ssl_error(&global_error_ctx, ctx->fatal_err);
-                break;
+            /* split into leaves and intermediate certs */
+            if (is_ca) {
+                sk_X509_push(intermediates, c);
+            }
+            else {
+                sk_X509_push(leaves, c);
             }
 
-            type = OSSL_STORE_INFO_get_type(info);
-            if (type == OSSL_STORE_INFO_CERT) {
-                X509 *c = OSSL_STORE_INFO_get1_CERT(info);
-
-                int n, i;
-
-                int is_ca = X509_check_ca(c);
-
-                /* split into leaves and intermediate certs */
-                if (is_ca) {
-                    sk_X509_push(intermediates, c);
-                }
-                else {
-                    sk_X509_push(leaves, c);
-                }
-
-                /* any cert with an issuer matching our requested CAs is also
-                 * added to the requests list, except for leaf certs which are
-                 * marked as requested with a flag so we can skip the chain
-                 * check later. */
-                n = sk_X509_NAME_num(requested);
-                for (i = 0; i < n; ++i) {
-                    X509_NAME *name = sk_X509_NAME_value(requested, i);
-                    if (X509_NAME_cmp(name, X509_get_issuer_name(c)) == 0) {
-                        if (is_ca) {
-                            X509_STORE_add_cert(requests, c);
-                        }
-                        else {
-                            X509_set_ex_data(c, ssl_x509_ex_data_idx,
-                                             (void *)1);
-                        }
+            /* any cert with an issuer matching our requested CAs is also
+             * added to the requests list, except for leaf certs which are
+             * marked as requested with a flag so we can skip the chain
+             * check later. */
+            n = sk_X509_NAME_num(requested);
+            for (i = 0; i < n; ++i) {
+                X509_NAME *name = sk_X509_NAME_value(requested, i);
+                if (X509_NAME_cmp(name, X509_get_issuer_name(c)) == 0) {
+                    if (is_ca) {
+                        X509_STORE_add_cert(requests, c);
+                    }
+                    else {
+                        X509_set_ex_data(c, ssl_x509_ex_data_idx,
+                                         (void *)1);
                     }
                 }
-
-            } else if (type == OSSL_STORE_INFO_PKEY) {
-                EVP_PKEY *k = OSSL_STORE_INFO_get1_PKEY(info);
-
-                sk_EVP_PKEY_push(keys, k);
             }
 
-            OSSL_STORE_INFO_free(info);
-        }
-
-        OSSL_STORE_close(store);
+        } else if (type == OSSL_STORE_INFO_PKEY) {
+            EVP_PKEY *k = OSSL_STORE_INFO_get1_PKEY(info);
 
-        if (ERR_peek_error()) {
-            break;
+            sk_EVP_PKEY_push(keys, k);
         }
 
-        /* walk the leaf certificates, choose the best one */
-
-        while ((x509 = sk_X509_pop(leaves))) {
-
-            EVP_PKEY *k = NULL;
-            int i, n, found = 0;
-
-            /* no key, skip */
-            n = sk_EVP_PKEY_num(keys);
-            for (i = 0; i < n; ++i) {
-                k = sk_EVP_PKEY_value(keys, i);
-                if (X509_check_private_key(x509, k)) {
-                    found = 1;
-                    break;
-                }
-            }
-            if (!found) {
-                continue;
-            }
+        OSSL_STORE_INFO_free(info);
+    }
 
-            /* CAs requested? if so, skip non matches, if not, accept all */
-            if (sk_X509_NAME_num(requested) &&
-                    !X509_get_ex_data(x509, ssl_x509_ex_data_idx)) {
-                STACK_OF(X509) *chain;
+    OSSL_STORE_close(store);
 
-                chain = X509_build_chain(x509, intermediates, requests, 0, 
NULL,
-                                         NULL);
+    if (store_error || ERR_peek_error()) {
+        log_ssl_error(&global_error_ctx, ctx->fatal_err);
+        result = 0;
+        goto cleanup;
+    }
 
-                if (!chain) {
-                    continue;
-                }
+    /* walk the leaf certificates, choose the best one */
 
-                sk_X509_pop_free(chain, X509_free);
+    *cert = NULL;
+    *pkey = NULL;
+    while ((x509 = sk_X509_pop(leaves)))
+    {
+        EVP_PKEY *k = NULL;
+        int i, n, found = 0;
+
+        /* no key, skip */
+        n = sk_EVP_PKEY_num(keys);
+        for (i = 0; i < n; ++i) {
+            k = sk_EVP_PKEY_value(keys, i);
+            if (X509_check_private_key(x509, k)) {
+                found = 1;
+                break;
             }
+        }
+        if (!found) {
+            continue;
+        }
 
-            /* no best candidate yet? we're in first place */
-            if (!*cert) {
-                EVP_PKEY_up_ref(k);
-                *cert = x509; /* don't dup, we're returning this */
-                *pkey = k;
+        /* CAs requested? if so, skip non matches, if not, accept all */
+        if (sk_X509_NAME_num(requested)
+            && !X509_get_ex_data(x509, ssl_x509_ex_data_idx)) {
+            STACK_OF(X509) *chain = X509_build_chain(x509, intermediates,
+                                                     requests, 0, NULL, NULL);
+            if (!chain) {
                 continue;
             }
 
-            /* were we issued after the previous best? */
-            if (ASN1_TIME_compare(X509_get0_notBefore(*cert),
-                    X509_get0_notBefore(x509)) < 0) {
-                X509_free(*cert);
-                EVP_PKEY_free(*pkey);
-                EVP_PKEY_up_ref(k);
-                *cert = x509; /* don't dup, we're returning this */
-                *pkey = k;
-                continue;
-            }
+            sk_X509_pop_free(chain, X509_free);
+        }
 
-            X509_free(x509);
+        /* no best candidate yet? we're in first place */
+        if (!*cert) {
+            EVP_PKEY_up_ref(k);
+            *cert = x509; /* don't dup, we're returning this */
+            *pkey = k;
+            continue;
         }
 
-        break;
-    }
+        /* were we issued after the previous best? */
+        if (ASN1_TIME_compare(X509_get0_notBefore(*cert),
+                              X509_get0_notBefore(x509)) < 0) {
+            X509_free(*cert);
+            EVP_PKEY_free(*pkey);
+            EVP_PKEY_up_ref(k);
+            *cert = x509; /* don't dup, we're returning this */
+            *pkey = k;
+            continue;
+        }
 
-    sk_X509_pop_free(leaves, X509_free);
-    sk_X509_pop_free(intermediates, X509_free);
-    sk_EVP_PKEY_pop_free(keys, EVP_PKEY_free);
-    X509_STORE_free(requests);
-    UI_destroy_method(ui_method);
+        X509_free(x509);
+    }
 
     if (ERR_peek_error()) {
         log_ssl_error(&global_error_ctx, ctx->fatal_err);
-        return -1;
+        result = -1;
+        goto cleanup;
     }
 
     /* we settled on a cert and key, cache it for later */
@@ -1857,9 +1833,44 @@ static int ssl_need_client_cert(SSL *ssl
                                    ctx->cert_pw_cache_pool);
         }
 
+        result = 1;
+    }
+
+  cleanup:
+    sk_X509_pop_free(leaves, X509_free);
+    sk_X509_pop_free(intermediates, X509_free);
+    sk_EVP_PKEY_pop_free(keys, EVP_PKEY_free);
+    X509_STORE_free(requests);
+    UI_destroy_method(ui_method);
+
+    return result;
+}
+
+#endif
+
+static int ssl_need_client_cert(SSL *ssl, X509 **cert, EVP_PKEY **pkey)
+{
+    serf_ssl_context_t *ctx = SSL_get_app_data(ssl);
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+#endif
+    apr_status_t status;
+    int retrying_success = 0;
+
+    serf__log(LOGLVL_DEBUG, LOGCOMP_SSL, __FILE__, ctx->config,
+              "Server requests a client certificate.\n");
+
+    if (ctx->cached_cert) {
+        *cert = ctx->cached_cert;
+        *pkey = ctx->cached_cert_pw;
         return 1;
     }
 
+#if defined(SERF_HAVE_OSSL_STORE_OPEN_EX)
+    if (ctx->cert_uri_callback) {
+        int result = ssl_read_client_cert_uri(ctx, ssl, cert, pkey);
+        if (result != 0)
+            return result;
+    }
 #endif
 
     while (ctx->cert_callback) {

Reply via email to