mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2025-08-07 23:56:57 +02:00
MEDIUM: ssl: split the loading of the certificates
Split the functions which open the certificates. Instead of opening directly the certificates and inserting them directly into a SSL_CTX, we use a struct cert_key_and_chain to store them in memory and then we associate a SSL_CTX to the certificate stored in that structure. Introduce the struct ckch_node for the multi-cert bundles so we can store multiple cert_key_and_chain in the same structure. The functions ssl_sock_load_multi_cert() and ssl_sock_load_cert_file() were modified so they don't open the certicates anymore on the filesystem. (they still open the sctl and ocsp though). These functions were renamed ssl_sock_load_ckchn() and ssl_sock_load_multi_ckchn(). The new function ckchn_load_cert_file() is in charge of loading the files in the cert_key_and_chain. (TODO: load ocsp and sctl from there too). The ultimate goal is to be able to load a certificate from a certificate tree without doing any filesystem access, so we don't try to open it again if it was already loaded, and we share its configuration.
This commit is contained in:
parent
a59191b894
commit
36b8463777
162
src/ssl_sock.c
162
src/ssl_sock.c
@ -2865,6 +2865,15 @@ struct cert_key_and_chain {
|
|||||||
X509 **chain_certs;
|
X509 **chain_certs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
* this is used to store 1 to SSL_SOCK_NUM_KEYTYPES cert_key_and_chain and
|
||||||
|
* metadata.
|
||||||
|
*/
|
||||||
|
struct ckch_node {
|
||||||
|
struct cert_key_and_chain *ckch;
|
||||||
|
int multi; /* is it a multi-cert bundle ? */
|
||||||
|
};
|
||||||
|
|
||||||
#define SSL_SOCK_POSSIBLE_KT_COMBOS (1<<(SSL_SOCK_NUM_KEYTYPES))
|
#define SSL_SOCK_POSSIBLE_KT_COMBOS (1<<(SSL_SOCK_NUM_KEYTYPES))
|
||||||
|
|
||||||
struct key_combo_ctx {
|
struct key_combo_ctx {
|
||||||
@ -3079,9 +3088,65 @@ static void ssl_sock_populate_sni_keytypes_hplr(const char *str, struct eb_root
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This function allocate a ckch_node and populate it with certificates from files.
|
||||||
|
*/
|
||||||
|
static struct ckch_node *ckchn_load_cert_file(char *path, int multi, char **err)
|
||||||
|
{
|
||||||
|
struct ckch_node *ckchn;
|
||||||
|
char fp[MAXPATHLEN+1] = {0};
|
||||||
|
int n = 0;
|
||||||
|
|
||||||
/* Given a path that does not exist, try to check for path.rsa, path.dsa and path.ecdsa files.
|
ckchn = calloc(1, sizeof(*ckchn));
|
||||||
* If any are found, group these files into a set of SSL_CTX*
|
if (!ckchn) {
|
||||||
|
memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
ckchn->ckch = calloc(1, sizeof(*ckchn->ckch) * (multi ? SSL_SOCK_NUM_KEYTYPES : 1));
|
||||||
|
|
||||||
|
if (!ckchn->ckch) {
|
||||||
|
memprintf(err, "%sunable to allocate memory.\n", err && *err ? *err : "");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!multi) {
|
||||||
|
|
||||||
|
if (ssl_sock_load_crt_file_into_ckch(path, ckchn->ckch, err) == 1)
|
||||||
|
goto end;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
int found = 0;
|
||||||
|
|
||||||
|
/* Load all possible certs and keys */
|
||||||
|
for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
|
||||||
|
struct stat buf;
|
||||||
|
snprintf(fp, sizeof(fp), "%s.%s", path, SSL_SOCK_KEYTYPE_NAMES[n]);
|
||||||
|
if (stat(fp, &buf) == 0) {
|
||||||
|
if (ssl_sock_load_crt_file_into_ckch(fp, &ckchn->ckch[n], err) == 1)
|
||||||
|
goto end;
|
||||||
|
found = 1;
|
||||||
|
ckchn->multi = 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!found) {
|
||||||
|
memprintf(err, "%sDidn't find any certificate.\n", err && *err ? *err : "");
|
||||||
|
goto end;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ckchn;
|
||||||
|
|
||||||
|
end:
|
||||||
|
if (ckchn)
|
||||||
|
free(ckchn->ckch);
|
||||||
|
free(ckchn);
|
||||||
|
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Take a ckch_node which contains a multi-certificate bundle.
|
||||||
|
* Group these certificates into a set of SSL_CTX*
|
||||||
* based on shared and unique CN and SAN entries. Add these SSL_CTX* to the SNI tree.
|
* based on shared and unique CN and SAN entries. Add these SSL_CTX* to the SNI tree.
|
||||||
*
|
*
|
||||||
* This will allow the user to explicitly group multiple cert/keys for a single purpose
|
* This will allow the user to explicitly group multiple cert/keys for a single purpose
|
||||||
@ -3089,14 +3154,16 @@ static void ssl_sock_populate_sni_keytypes_hplr(const char *str, struct eb_root
|
|||||||
* Returns
|
* Returns
|
||||||
* 0 on success
|
* 0 on success
|
||||||
* 1 on failure
|
* 1 on failure
|
||||||
|
*
|
||||||
|
* TODO: This function shouldn't access files anymore, sctl and ocsp file access
|
||||||
|
* should be migrated to the ssl_sock_load_crt_file_into_ckch() function
|
||||||
*/
|
*/
|
||||||
static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
|
static int ssl_sock_load_multi_ckchn(const char *path, struct ckch_node *ckch_n,
|
||||||
char **sni_filter, int fcount, char **err)
|
struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
|
||||||
|
char **sni_filter, int fcount, char **err)
|
||||||
{
|
{
|
||||||
char fp[MAXPATHLEN+1] = {0};
|
int i = 0, n = 0;
|
||||||
int n = 0;
|
struct cert_key_and_chain *certs_and_keys;
|
||||||
int i = 0;
|
|
||||||
struct cert_key_and_chain certs_and_keys[SSL_SOCK_NUM_KEYTYPES] = { {0} };
|
|
||||||
struct eb_root sni_keytypes_map = { {0} };
|
struct eb_root sni_keytypes_map = { {0} };
|
||||||
struct ebmb_node *node;
|
struct ebmb_node *node;
|
||||||
struct ebmb_node *next;
|
struct ebmb_node *next;
|
||||||
@ -3111,19 +3178,14 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
|
|||||||
STACK_OF(GENERAL_NAME) *names = NULL;
|
STACK_OF(GENERAL_NAME) *names = NULL;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* Load all possible certs and keys */
|
if (!ckch_n || !ckch_n->ckch || !ckch_n->multi) {
|
||||||
for (n = 0; n < SSL_SOCK_NUM_KEYTYPES; n++) {
|
memprintf(err, "%sunable to load SSL certificate file '%s' file does not exist.\n",
|
||||||
struct stat buf;
|
err && *err ? *err : "", path);
|
||||||
|
return 1;
|
||||||
snprintf(fp, sizeof(fp), "%s.%s", path, SSL_SOCK_KEYTYPE_NAMES[n]);
|
|
||||||
if (stat(fp, &buf) == 0) {
|
|
||||||
if (ssl_sock_load_crt_file_into_ckch(fp, &certs_and_keys[n], err) == 1) {
|
|
||||||
rv = 1;
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
certs_and_keys = ckch_n->ckch;
|
||||||
|
|
||||||
/* Process each ckch and update keytypes for each CN/SAN
|
/* Process each ckch and update keytypes for each CN/SAN
|
||||||
* for example, if CN/SAN www.a.com is associated with
|
* for example, if CN/SAN www.a.com is associated with
|
||||||
* certs with keytype 0 and 2, then at the end of the loop,
|
* certs with keytype 0 and 2, then at the end of the loop,
|
||||||
@ -3234,6 +3296,7 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
|
|||||||
|
|
||||||
#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
|
#if (defined SSL_CTRL_SET_TLSEXT_STATUS_REQ_CB && !defined OPENSSL_NO_OCSP)
|
||||||
/* Load OCSP Info into context */
|
/* Load OCSP Info into context */
|
||||||
|
/* TODO: store OCSP in ckch */
|
||||||
if (ssl_sock_load_ocsp(cur_ctx, cur_file) < 0) {
|
if (ssl_sock_load_ocsp(cur_ctx, cur_file) < 0) {
|
||||||
if (err)
|
if (err)
|
||||||
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",
|
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",
|
||||||
@ -3250,6 +3313,7 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
|
|||||||
|
|
||||||
/* Load DH params into the ctx to support DHE keys */
|
/* Load DH params into the ctx to support DHE keys */
|
||||||
#ifndef OPENSSL_NO_DH
|
#ifndef OPENSSL_NO_DH
|
||||||
|
/* TODO store DH in ckch */
|
||||||
if (ssl_dh_ptr_index >= 0)
|
if (ssl_dh_ptr_index >= 0)
|
||||||
SSL_CTX_set_ex_data(cur_ctx, ssl_dh_ptr_index, NULL);
|
SSL_CTX_set_ex_data(cur_ctx, ssl_dh_ptr_index, NULL);
|
||||||
|
|
||||||
@ -3304,8 +3368,9 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
|
|||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
/* This is a dummy, that just logs an error and returns error */
|
/* This is a dummy, that just logs an error and returns error */
|
||||||
static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
|
static int ssl_sock_load_multi_ckchn(const char *path, struct ckch_node *ckch_n,
|
||||||
char **sni_filter, int fcount, char **err)
|
struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
|
||||||
|
char **sni_filter, int fcount, char **err)
|
||||||
{
|
{
|
||||||
memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
|
memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
|
||||||
err && *err ? *err : "", path, strerror(errno));
|
err && *err ? *err : "", path, strerror(errno));
|
||||||
@ -3314,7 +3379,7 @@ static int ssl_sock_load_multi_cert(const char *path, struct bind_conf *bind_con
|
|||||||
|
|
||||||
#endif /* #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL: Support for loading multiple certs into a single SSL_CTX */
|
#endif /* #if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL: Support for loading multiple certs into a single SSL_CTX */
|
||||||
|
|
||||||
static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
|
static int ssl_sock_load_ckchn(const char *path, struct ckch_node *ckch_n, struct bind_conf *bind_conf, struct ssl_bind_conf *ssl_conf,
|
||||||
char **sni_filter, int fcount, char **err)
|
char **sni_filter, int fcount, char **err)
|
||||||
{
|
{
|
||||||
SSL_CTX *ctx;
|
SSL_CTX *ctx;
|
||||||
@ -3328,13 +3393,13 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
|
|||||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||||
STACK_OF(GENERAL_NAME) *names;
|
STACK_OF(GENERAL_NAME) *names;
|
||||||
#endif
|
#endif
|
||||||
struct cert_key_and_chain ckch;
|
struct cert_key_and_chain *ckch;
|
||||||
|
|
||||||
memset(&ckch, 0, sizeof(ckch));
|
if (!ckch_n || !ckch_n->ckch)
|
||||||
|
|
||||||
if (ssl_sock_load_crt_file_into_ckch(path, &ckch, err) == 1)
|
|
||||||
return 1;
|
return 1;
|
||||||
|
|
||||||
|
ckch = ckch_n->ckch;
|
||||||
|
|
||||||
ctx = SSL_CTX_new(SSLv23_server_method());
|
ctx = SSL_CTX_new(SSLv23_server_method());
|
||||||
if (!ctx) {
|
if (!ctx) {
|
||||||
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
|
memprintf(err, "%sunable to allocate SSL context for cert '%s'.\n",
|
||||||
@ -3342,12 +3407,12 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
|
|||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ssl_sock_put_ckch_into_ctx(path, &ckch, ctx, err) != 0) {
|
if (ssl_sock_put_ckch_into_ctx(path, ckch, ctx, err) != 0) {
|
||||||
SSL_CTX_free(ctx);
|
SSL_CTX_free(ctx);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
pkey = X509_get_pubkey(ckch.cert);
|
pkey = X509_get_pubkey(ckch->cert);
|
||||||
if (pkey) {
|
if (pkey) {
|
||||||
kinfo.bits = EVP_PKEY_bits(pkey);
|
kinfo.bits = EVP_PKEY_bits(pkey);
|
||||||
switch(EVP_PKEY_base_id(pkey)) {
|
switch(EVP_PKEY_base_id(pkey)) {
|
||||||
@ -3370,7 +3435,7 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME
|
||||||
names = X509_get_ext_d2i(ckch.cert, NID_subject_alt_name, NULL, NULL);
|
names = X509_get_ext_d2i(ckch->cert, NID_subject_alt_name, NULL, NULL);
|
||||||
if (names) {
|
if (names) {
|
||||||
for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
|
for (i = 0; i < sk_GENERAL_NAME_num(names); i++) {
|
||||||
GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
|
GENERAL_NAME *name = sk_GENERAL_NAME_value(names, i);
|
||||||
@ -3384,7 +3449,7 @@ static int ssl_sock_load_cert_file(const char *path, struct bind_conf *bind_conf
|
|||||||
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
|
sk_GENERAL_NAME_pop_free(names, GENERAL_NAME_free);
|
||||||
}
|
}
|
||||||
#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
|
#endif /* SSL_CTRL_SET_TLSEXT_HOSTNAME */
|
||||||
xname = X509_get_subject_name(ckch.cert);
|
xname = X509_get_subject_name(ckch->cert);
|
||||||
i = -1;
|
i = -1;
|
||||||
while ((i = X509_NAME_get_index_by_NID(xname, NID_commonName, i)) != -1) {
|
while ((i = X509_NAME_get_index_by_NID(xname, NID_commonName, i)) != -1) {
|
||||||
X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, i);
|
X509_NAME_ENTRY *entry = X509_NAME_get_entry(xname, i);
|
||||||
@ -3467,6 +3532,7 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
|
|||||||
char *end;
|
char *end;
|
||||||
char fp[MAXPATHLEN+1];
|
char fp[MAXPATHLEN+1];
|
||||||
int cfgerr = 0;
|
int cfgerr = 0;
|
||||||
|
struct ckch_node *ckchn;
|
||||||
#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
|
#if HA_OPENSSL_VERSION_NUMBER >= 0x1000200fL
|
||||||
int is_bundle;
|
int is_bundle;
|
||||||
int j;
|
int j;
|
||||||
@ -3474,8 +3540,12 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
|
|||||||
|
|
||||||
if (stat(path, &buf) == 0) {
|
if (stat(path, &buf) == 0) {
|
||||||
dir = opendir(path);
|
dir = opendir(path);
|
||||||
if (!dir)
|
if (!dir) {
|
||||||
return ssl_sock_load_cert_file(path, bind_conf, NULL, NULL, 0, err);
|
ckchn = ckchn_load_cert_file(path, 0, err);
|
||||||
|
if (!ckchn)
|
||||||
|
return 1;
|
||||||
|
return ssl_sock_load_ckchn(path, ckchn, bind_conf, NULL, NULL, 0, err);
|
||||||
|
}
|
||||||
|
|
||||||
/* strip trailing slashes, including first one */
|
/* strip trailing slashes, including first one */
|
||||||
for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--)
|
for (end = path + strlen(path) - 1; end >= path && *end == '/'; end--)
|
||||||
@ -3535,7 +3605,10 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
snprintf(fp, sizeof(fp), "%s/%s", path, dp);
|
snprintf(fp, sizeof(fp), "%s/%s", path, dp);
|
||||||
cfgerr += ssl_sock_load_multi_cert(fp, bind_conf, NULL, NULL, 0, err);
|
ckchn = ckchn_load_cert_file(fp, 1, err);
|
||||||
|
if (!ckchn)
|
||||||
|
return 1;
|
||||||
|
cfgerr += ssl_sock_load_multi_ckchn(fp, ckchn, bind_conf, NULL, NULL, 0, err);
|
||||||
|
|
||||||
/* Successfully processed the bundle */
|
/* Successfully processed the bundle */
|
||||||
goto ignore_entry;
|
goto ignore_entry;
|
||||||
@ -3543,7 +3616,11 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
cfgerr += ssl_sock_load_cert_file(fp, bind_conf, NULL, NULL, 0, err);
|
ckchn = ckchn_load_cert_file(fp, 0, err);
|
||||||
|
if (!ckchn)
|
||||||
|
return 1;
|
||||||
|
cfgerr += ssl_sock_load_ckchn(fp, ckchn, bind_conf, NULL, NULL, 0, err);
|
||||||
|
|
||||||
ignore_entry:
|
ignore_entry:
|
||||||
free(de);
|
free(de);
|
||||||
}
|
}
|
||||||
@ -3553,7 +3630,10 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err)
|
|||||||
return cfgerr;
|
return cfgerr;
|
||||||
}
|
}
|
||||||
|
|
||||||
cfgerr = ssl_sock_load_multi_cert(path, bind_conf, NULL, NULL, 0, err);
|
ckchn = ckchn_load_cert_file(fp, 1, err);
|
||||||
|
if (!ckchn)
|
||||||
|
return 1;
|
||||||
|
cfgerr = ssl_sock_load_multi_ckchn(path, ckchn, bind_conf, NULL, NULL, 0, err);
|
||||||
|
|
||||||
return cfgerr;
|
return cfgerr;
|
||||||
}
|
}
|
||||||
@ -3611,6 +3691,7 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct
|
|||||||
struct stat buf;
|
struct stat buf;
|
||||||
int linenum = 0;
|
int linenum = 0;
|
||||||
int cfgerr = 0;
|
int cfgerr = 0;
|
||||||
|
struct ckch_node *ckchn;
|
||||||
|
|
||||||
if ((f = fopen(file, "r")) == NULL) {
|
if ((f = fopen(file, "r")) == NULL) {
|
||||||
memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
|
memprintf(err, "cannot open file '%s' : %s", file, strerror(errno));
|
||||||
@ -3738,10 +3819,17 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (stat(crt_path, &buf) == 0) {
|
if (stat(crt_path, &buf) == 0) {
|
||||||
cfgerr = ssl_sock_load_cert_file(crt_path, bind_conf, ssl_conf,
|
|
||||||
|
ckchn = ckchn_load_cert_file(crt_path, 0, err);
|
||||||
|
if (!ckchn)
|
||||||
|
return 1;
|
||||||
|
cfgerr = ssl_sock_load_ckchn(crt_path, ckchn, bind_conf, ssl_conf,
|
||||||
&args[cur_arg], arg - cur_arg - 1, err);
|
&args[cur_arg], arg - cur_arg - 1, err);
|
||||||
} else {
|
} else {
|
||||||
cfgerr = ssl_sock_load_multi_cert(crt_path, bind_conf, ssl_conf,
|
ckchn = ckchn_load_cert_file(crt_path, 1, err);
|
||||||
|
if (!ckchn)
|
||||||
|
return 1;
|
||||||
|
cfgerr = ssl_sock_load_multi_ckchn(crt_path, ckchn, bind_conf, ssl_conf,
|
||||||
&args[cur_arg], arg - cur_arg - 1, err);
|
&args[cur_arg], arg - cur_arg - 1, err);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user