]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
BUG/MINOR: ssl: Encrypted keys could not be loaded when given alongside certificate
authorRemi Tricot-Le Breton <rlebreton@haproxy.com>
Mon, 26 Jan 2026 10:54:45 +0000 (11:54 +0100)
committerWilliam Lallemand <wlallemand@haproxy.com>
Mon, 26 Jan 2026 13:09:13 +0000 (14:09 +0100)
The SSL passphrase callback function was only called when loading
private keys from a dedicated file (separate from the corresponding
certificate) but not when both the certificate and the key were in the
same file.
We can now load them properly, regardless of how they are provided.
A flas had to be added in the 'passphrase_cb_data' structure because in
the 'ssl_sock_load_pem_into_ckch' function, when calling
'PEM_read_bio_PrivateKey' there might be no private key in the PEM file
which would mean that the callback never gets called (and cannot set the
'passphrase_idx' to -1).

This patch can be backported to 3.3.

include/haproxy/ssl_sock-t.h
src/ssl_ckch.c
src/ssl_sock.c

index 593f51a5b704080e80188aa20be5726e1b8b52d8..b1d660f3042093cae80c5a5a35619f6d41c33244 100644 (file)
@@ -361,6 +361,7 @@ struct passphrase_cb_data {
        const char *path;
        struct ckch_data *ckch_data;
        int passphrase_idx;
+       int callback_called;
 };
 
 #endif /* USE_OPENSSL */
index 900e89de3404e341587750ad13ca66cb42686be1..749002b088250e44208cef18e9c55c5856502d49 100644 (file)
@@ -593,7 +593,7 @@ int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct ckch_data *d
        BIO *in = NULL;
        int ret = 1;
        EVP_PKEY *key = NULL;
-       struct passphrase_cb_data cb_data = { path, data, 0 };
+       struct passphrase_cb_data cb_data = { path, data, 0, 0 };
 
        if (buf) {
                /* reading from a buffer */
@@ -625,7 +625,7 @@ int ssl_sock_load_key_into_ckch(const char *path, char *buf, struct ckch_data *d
         */
        do {
                key = PEM_read_bio_PrivateKey(in, NULL, ssl_sock_passwd_cb, &cb_data);
-       } while (!key && cb_data.passphrase_idx != -1);
+       } while (!key && cb_data.passphrase_idx != -1 && cb_data.callback_called);
 
        if (key == NULL) {
                memprintf(err, "%sunable to load private key from file '%s'.\n",
@@ -667,6 +667,7 @@ int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct ckch_data *d
        HASSL_DH *dh = NULL;
        STACK_OF(X509) *chain = NULL;
        struct issuer_chain *issuer_chain = NULL;
+       struct passphrase_cb_data cb_data = { path, data, 0, 0 };
 
        if (buf) {
                /* reading from a buffer */
@@ -691,8 +692,18 @@ int ssl_sock_load_pem_into_ckch(const char *path, char *buf, struct ckch_data *d
                }
        }
 
-       /* Read Private Key */
-       key = PEM_read_bio_PrivateKey(in, NULL, NULL, NULL);
+       /* Read Private Key
+        * Since multiple private keys might have different passphrases that are
+        * stored in a local cache, we want to try all the already known
+        * passphrases first before raising an error. The passphrase_idx field
+        * of the cb_data parameter will be modified in the callback and set to
+        * -1 after the external passphrase tool is called.
+        */
+       /* We don't know yet if the private key requires a password. */
+       data->encrypted_privkey = 0;
+       do {
+               key = PEM_read_bio_PrivateKey(in, NULL, ssl_sock_passwd_cb, &cb_data);
+       } while (!key && cb_data.passphrase_idx != -1 && cb_data.callback_called);
        /* no need to check for errors here, because the private key could be loaded later */
 
 #ifndef OPENSSL_NO_DH
index 3f25b93ce1583ef887df4ac27e6f97c8a7f18146..1fcec4ab4bf40420484505173bc774b651c99bce 100644 (file)
@@ -3813,6 +3813,8 @@ int ssl_sock_passwd_cb(char *buf, int size, int rwflag, void *userdata)
        if (!data || data->passphrase_idx == -1)
                return -1;
 
+       data->callback_called = 1;
+
        ckch_data = data->ckch_data;
 
        if (ckch_data)