This patch adds the global 'ssl-engine' keyword. First arg is an engine identifier followed by a list of default_algorithms the engine will operate.
If the openssl version is too old, an error is reported when the option is used. --- doc/configuration.txt | 16 ++++++ include/common/mini-clist.h | 7 +++ src/ssl_sock.c | 119 +++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 141 insertions(+), 1 deletion(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index c2ede71..ecd1769 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -589,6 +589,7 @@ The following keywords are supported in the "global" section : - spread-checks - server-state-base - server-state-file + - ssl-engine - tune.buffers.limit - tune.buffers.reserve - tune.bufsize @@ -1240,6 +1241,21 @@ spread-checks <0..50, in percent> and +/- 50%. A value between 2 and 5 seems to show good results. The default value remains at 0. +ssl-engine <name> [algo <comma-seperated list of algorithms>] + Sets the OpenSSL engine to <name>. List of valid values for <name> may be + obtained using the command "openssl engine". This statement may be used + multiple times, it will simply enable multiple crypto engines. Referencing an + unsupported engine will prevent haproxy from starting. Note that many engines + will lead to lower HTTPS performance than pure software with recent + processors. The optional command "algo" sets the default algorithms an ENGINE + will supply using the OPENSSL function ENGINE_set_default_string(). A value + of "ALL" uses the engine for all cryptographic operations. If no list of + algo is specified then the value of "ALL" is used. A comma-seperated list + of different algorithms may be specified, including: RSA, DSA, DH, EC, RAND, + CIPHERS, DIGESTS, PKEY, PKEY_CRYPTO, PKEY_ASN1. This is the same format that + openssl configuration file uses: + https://www.openssl.org/docs/man1.0.2/apps/config.html + tune.buffers.limit <number> Sets a hard limit on the number of buffers which may be allocated per process. The default value is zero which means unlimited. The minimum non-zero value diff --git a/include/common/mini-clist.h b/include/common/mini-clist.h index da24b33..7000927 100644 --- a/include/common/mini-clist.h +++ b/include/common/mini-clist.h @@ -61,6 +61,13 @@ struct cond_wordlist { char *s; }; +/* this is the same as above with the additional pointer to an argument. */ +struct arg1_wordlist { + struct list list; + void *arg1; + char *word; +}; + /* First undefine some macros which happen to also be defined on OpenBSD, * in sys/queue.h, used by sys/event.h */ diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 232a497..b173d77 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -52,6 +52,7 @@ #ifndef OPENSSL_NO_DH #include <openssl/dh.h> #endif +#include <openssl/engine.h> #include <import/lru.h> #include <import/xxhash.h> @@ -168,6 +169,9 @@ static struct { struct list tlskeys_reference = LIST_HEAD_INIT(tlskeys_reference); #endif +struct list openssl_engines = LIST_HEAD_INIT(openssl_engines); +static unsigned int openssl_engines_initialized; + #ifndef OPENSSL_NO_DH static int ssl_dh_ptr_index = -1; static DH *global_dh = NULL; @@ -262,6 +266,55 @@ struct ocsp_cbk_arg { }; }; +static int ssl_init_single_engine(const char *engine_id, const char *def_algorithms) +{ + int err_code = ERR_ABORT; + ENGINE *engine; + + /* grab the structural reference to the engine */ + engine = ENGINE_by_id(engine_id); + if (engine == NULL) { + Alert("ssl-engine %s: failed to get structural reference\n", engine_id); + goto fail_get; + } + + if (!ENGINE_init(engine)) { + /* the engine couldn't initialise, release it */ + Alert("ssl-engine %s: failed to initialize\n", engine_id); + goto fail_init; + } + + if (ENGINE_set_default_string(engine, def_algorithms) == 0) { + Alert("ssl-engine %s: failed on ENGINE_set_default_string\n", engine_id); + goto fail_set_method; + } + err_code = 0; + +fail_set_method: + /* release the functional reference from ENGINE_init() */ + ENGINE_finish(engine); + +fail_init: + /* release the structural reference from ENGINE_by_id() */ + ENGINE_free(engine); + +fail_get: + return err_code; +} + +static int ssl_init_engines(void) +{ + int err_code = 0; + struct arg1_wordlist *wl, *wlb; + + list_for_each_entry_safe(wl, wlb, &openssl_engines, list) { + err_code = ssl_init_single_engine(wl->word, wl->arg1); + if (err_code == ERR_ABORT) + break; + } + return err_code; +} + /* * This function returns the number of seconds elapsed * since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the @@ -2329,7 +2382,6 @@ static int ssl_sock_load_cert_chain_file(SSL_CTX *ctx, const char *file, struct if (BIO_read_filename(in, file) <= 0) goto end; - passwd_cb = SSL_CTX_get_default_passwd_cb(ctx); passwd_cb_userdata = SSL_CTX_get_default_passwd_cb_userdata(ctx); @@ -2513,6 +2565,12 @@ int ssl_sock_load_cert(char *path, struct bind_conf *bind_conf, char **err) int j; #endif + if (!openssl_engines_initialized) { + if (ssl_init_engines()) + return 1; + openssl_engines_initialized = 1; + } + if (stat(path, &buf) == 0) { dir = opendir(path); if (!dir) @@ -2654,6 +2712,12 @@ int ssl_sock_load_cert_list_file(char *file, struct bind_conf *bind_conf, struct return 1; } + if (!openssl_engines_initialized) { + if (ssl_init_engines()) + return 1; + openssl_engines_initialized = 1; + } + while (fgets(thisline, sizeof(thisline), f) != NULL) { int arg, newarg, cur_arg, i, ssl_b = 0, ssl_e = 0; char *end; @@ -6375,6 +6439,48 @@ static int ssl_parse_global_ca_crt_base(char **args, int section_type, struct pr return 0; } +/* parse the "ssl-engine" keyword in global section. + * Returns <0 on alert, >0 on warning, 0 on success. + */ +static int ssl_parse_global_ssl_engine(char **args, int section_type, struct proxy *curpx, + struct proxy *defpx, const char *file, int line, + char **err) +{ + char *algo; + struct arg1_wordlist *wl; + + if (*(args[1]) == 0) { + memprintf(err, "global statement '%s' expects a valid engine name as an argument.", args[0]); + return -1; + } + + if (*(args[2]) == 0) { + memprintf(err, "statement '%s' expects algorithm names as an argument.", args[0]); + /* if no list of algorithms is given, it defaults to ALL */ + algo = strdup("ALL"); + goto add_engine; + } + + /* otherwise the expected format is ssl-engine <engine_name> algo <list of algo> */ + if (strcmp(args[2], "algo") != 0) { + memprintf(err, "global statement '%s' expects to have algo keyword.", args[0]); + return -1; + } + + if (*(args[3]) == 0) { + memprintf(err, "global statement '%s' expects algorithm names as an argument.", args[0]); + return -1; + } + algo = strdup(args[3]); + +add_engine: + wl = calloc(1, sizeof(*wl)); + wl->word = strdup(args[1]); + wl->arg1 = algo; + LIST_ADD(&openssl_engines, &wl->list); + return 0; +} + /* parse the "ssl-default-bind-ciphers" / "ssl-default-server-ciphers" keywords * in global section. Returns <0 on alert, >0 on warning, 0 on success. */ @@ -6948,6 +7054,7 @@ static struct cfg_kw_list cfg_kws = {ILH, { #ifndef OPENSSL_NO_DH { CFG_GLOBAL, "ssl-dh-param-file", ssl_parse_global_dh_param_file }, #endif + { CFG_GLOBAL, "ssl-engine", ssl_parse_global_ssl_engine }, { CFG_GLOBAL, "tune.ssl.cachesize", ssl_parse_global_int }, #ifndef OPENSSL_NO_DH { CFG_GLOBAL, "tune.ssl.default-dh-param", ssl_parse_global_default_dh }, @@ -7015,6 +7122,7 @@ static void __ssl_sock_init(void) srv_register_keywords(&srv_kws); cfg_register_keywords(&cfg_kws); cli_register_kw(&cli_kws); + ENGINE_load_builtin_engines(); #if (defined SSL_CTRL_SET_TLSEXT_TICKET_KEY_CB && TLS_TICKETS_NO > 0) hap_register_post_check(tlskeys_finalize_config); #endif @@ -7067,6 +7175,7 @@ static void __ssl_sock_init(void) __attribute__((destructor)) static void __ssl_sock_deinit(void) { + struct arg1_wordlist *wl, *wlb; #if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined SSL_NO_GENERATE_CERTIFICATES) lru64_destroy(ssl_ctx_lru_tree); #endif @@ -7093,6 +7202,14 @@ static void __ssl_sock_deinit(void) } #endif + /* free up engine list */ + list_for_each_entry_safe(wl, wlb, &openssl_engines, list) { + free(wl->arg1); + free(wl->word); + LIST_DEL(&wl->list); + free(wl); + } + ERR_remove_state(0); ERR_free_strings(); -- 1.9.1