From 97832ab82365558bd71eec3b904c4da4eb12f5c0 Mon Sep 17 00:00:00 2001 From: William Lallemand Date: Fri, 12 Jan 2024 17:32:48 +0100 Subject: [PATCH] MEDIUM: ssl: implements 'default-crt' keyword for bind Lines The 'default-crt' bind keyword allows to specify multiples default/fallback certificates, allowing one to have an RSA as well as an ECDSA default. --- doc/configuration.txt | 20 +++++++++++++++++++- include/haproxy/ssl_sock.h | 2 +- src/cfgparse-ssl.c | 6 ++++-- src/ssl_sock.c | 9 +++++---- 4 files changed, 29 insertions(+), 8 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index 9008fc0468..d9b7b8b6ad 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -15474,11 +15474,12 @@ crt This means that when loading certificates from a directory, it is highly recommended to load the default one first as a file or to ensure that it will always be the first one in the directory. In order to chose multiple default - certificates (1 rsa and 1 ecdsa), there are 2 options: + certificates (1 rsa and 1 ecdsa), there are 3 options: - A multi-cert bundle can be configured as the first certificate (`crt foobar.pem` in the configuration where the existing files are `foobar.pem.ecdsa` and `foobar.pem.rsa`. - Or a '*' filter for each certificate in a crt-list line. + - The 'default-crt' keyword can be used. Note that the same cert may be loaded multiple times without side effects. @@ -15585,6 +15586,23 @@ crt-list default.pem.rsa * default.pem.ecdsa * +default-crt + This option does the same as the "crt" option, with the difference that this + certificate will be used as a default one. It is possible to add multiple + default certificates to have an ECDSA and an RSA one, having more is not + really useful. + + A default certificate is used when no "strict-sni" option is used on the bind + line. A default certificate is provided when the servername extension was not + used by the client, or when the servername does not match any configured + certificate. + + Example: + + bind *:443 default-crt foobar.pem.rsa default-crt foobar.pem.ecdsa crt website.pem.rsa + + See also the "crt" keyword. + defer-accept Is an optional keyword which is supported only on certain Linux kernels. It states that a connection will only be accepted once some data arrive on it, diff --git a/include/haproxy/ssl_sock.h b/include/haproxy/ssl_sock.h index 0befb570cc..773bb320a0 100644 --- a/include/haproxy/ssl_sock.h +++ b/include/haproxy/ssl_sock.h @@ -123,7 +123,7 @@ void ssl_async_fd_free(int fd); #endif struct issuer_chain* ssl_get0_issuer_chain(X509 *cert); int ssl_load_global_issuer_from_BIO(BIO *in, char *fp, char **err); -int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err); +int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, int is_default, char **err); int ssl_sock_load_srv_cert(char *path, struct server *server, int create_if_none, char **err); void ssl_free_global_issuers(void); int ssl_initialize_random(void); diff --git a/src/cfgparse-ssl.c b/src/cfgparse-ssl.c index 5666336770..3f728258dd 100644 --- a/src/cfgparse-ssl.c +++ b/src/cfgparse-ssl.c @@ -777,6 +777,7 @@ static int bind_parse_ciphersuites(char **args, int cur_arg, struct proxy *px, s static int bind_parse_crt(char **args, int cur_arg, struct proxy *px, struct bind_conf *conf, char **err) { char path[MAXPATHLEN]; + int default_crt = *args[cur_arg] == 'd' ? 1 : 0; if (!*args[cur_arg + 1]) { memprintf(err, "'%s' : missing certificate location", args[cur_arg]); @@ -789,10 +790,10 @@ static int bind_parse_crt(char **args, int cur_arg, struct proxy *px, struct bin memprintf(err, "'%s' : path too long", args[cur_arg]); return ERR_ALERT | ERR_FATAL; } - return ssl_sock_load_cert(path, conf, err); + return ssl_sock_load_cert(path, conf, default_crt, err); } - return ssl_sock_load_cert(args[cur_arg + 1], conf, err); + return ssl_sock_load_cert(args[cur_arg + 1], conf, default_crt, err); } /* parse the "crt-list" bind keyword. Returns a set of ERR_* flags possibly with an error in . */ @@ -2240,6 +2241,7 @@ static struct bind_kw_list bind_kws = { "SSL", { }, { { "crt-ignore-err", bind_parse_ignore_err, 1 }, /* set error IDs to ignore on verify depth == 0 */ { "crt-list", bind_parse_crt_list, 1 }, /* load a list of crt from this location */ { "curves", bind_parse_curves, 1 }, /* set SSL curve suite */ + { "default-crt", bind_parse_crt, 1 }, /* load SSL certificates from this location */ { "ecdhe", bind_parse_ecdhe, 1 }, /* defines named curve for elliptic curve Diffie-Hellman */ { "force-sslv3", bind_parse_tls_method_options, 0 }, /* force SSLv3 */ { "force-tlsv10", bind_parse_tls_method_options, 0 }, /* force TLSv10 */ diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 0c5fe96f22..c5603d25e0 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -3760,19 +3760,20 @@ int ssl_sock_load_cert_list_file(char *file, int dir, struct bind_conf *bind_con } /* Returns a set of ERR_* flags possibly with an error in . */ -int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err) +int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, int is_default, char **err) { struct stat buf; int cfgerr = 0; struct ckch_store *ckchs; struct ckch_inst *ckch_inst = NULL; int found = 0; /* did we found a file to load ? */ - int is_default = 0; /* if the SNI trees were empty the first "crt" become a default certificate, * it can be applied on multiple certificates if it's a bundle */ - if (eb_is_empty(&bind_conf->sni_ctx) && eb_is_empty(&bind_conf->sni_w_ctx)) - is_default = 1; + if (is_default == 0) { + if (eb_is_empty(&bind_conf->sni_ctx) && eb_is_empty(&bind_conf->sni_w_ctx)) + is_default = 1; + } if ((ckchs = ckchs_lookup(path))) { /* we found the ckchs in the tree, we can use it directly */