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:
William Lallemand 2019-07-18 19:28:17 +02:00 committed by William Lallemand
parent a59191b894
commit 36b8463777

View File

@ -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,
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)
{ {
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,18 +3178,13 @@ 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]); certs_and_keys = ckch_n->ckch;
if (stat(fp, &buf) == 0) {
if (ssl_sock_load_crt_file_into_ckch(fp, &certs_and_keys[n], err) == 1) {
rv = 1;
goto end;
}
}
}
/* 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
@ -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,7 +3368,8 @@ 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,
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)
{ {
memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n", memprintf(err, "%sunable to stat SSL certificate from file '%s' : %s.\n",
@ -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);
} }