From 872f9c2139ed0e8fadb6d48de10d66fe132a2cf1 Mon Sep 17 00:00:00 2001 From: Grant Zhang Date: Sat, 21 Jan 2017 01:10:18 +0000 Subject: [PATCH] MEDIUM: ssl: add basic support for OpenSSL crypto engine 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/proto/ssl_sock.h | 2 + src/haproxy.c | 4 ++ src/ssl_sock.c | 147 +++++++++++++++++++++++++++++++++------ 4 files changed, 148 insertions(+), 21 deletions(-) diff --git a/doc/configuration.txt b/doc/configuration.txt index bd9a99f13c..8d5ddc1535 100644 --- a/doc/configuration.txt +++ b/doc/configuration.txt @@ -590,6 +590,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 @@ -1258,6 +1259,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 [algo ] + Sets the OpenSSL engine to . List of valid values for 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 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/proto/ssl_sock.h b/include/proto/ssl_sock.h index 6f779faaed..8779770e35 100644 --- a/include/proto/ssl_sock.h +++ b/include/proto/ssl_sock.h @@ -68,7 +68,9 @@ struct tls_keys_ref *tlskeys_ref_lookupid(int unique_id); #endif #ifndef OPENSSL_NO_DH int ssl_sock_load_global_dh_param_from_file(const char *filename); +void ssl_free_dh(void); #endif +void ssl_free_engines(void); SSL_CTX *ssl_sock_create_cert(struct connection *conn, const char *servername, unsigned int key); SSL_CTX *ssl_sock_get_generated_cert(unsigned int key, struct bind_conf *bind_conf); diff --git a/src/haproxy.c b/src/haproxy.c index 5ccebd1054..6ecae257d5 100644 --- a/src/haproxy.c +++ b/src/haproxy.c @@ -109,6 +109,7 @@ #include #include #include +#include /* list of config files */ static struct list cfg_cfgfiles = LIST_HEAD_INIT(cfg_cfgfiles); @@ -2161,6 +2162,9 @@ int main(int argc, char **argv) for (proc = 0; proc < global.nbproc; proc++) while (waitpid(-1, NULL, 0) == -1 && errno == EINTR); } +#ifndef OPENSSL_NO_DH + ssl_free_dh(); +#endif exit(0); /* parent must leave */ } diff --git a/src/ssl_sock.c b/src/ssl_sock.c index 21d50c753d..bad9b9964b 100644 --- a/src/ssl_sock.c +++ b/src/ssl_sock.c @@ -52,6 +52,7 @@ #ifndef OPENSSL_NO_DH #include #endif +#include #include #include @@ -207,6 +208,13 @@ static int ssl_capture_ptr_index = -1; struct list tlskeys_reference = LIST_HEAD_INIT(tlskeys_reference); #endif +static unsigned int openssl_engines_initialized; +struct list openssl_engines = LIST_HEAD_INIT(openssl_engines); +struct ssl_engine_list { + struct list list; + ENGINE *e; +}; + #ifndef OPENSSL_NO_DH static int ssl_dh_ptr_index = -1; static DH *global_dh = NULL; @@ -302,6 +310,47 @@ 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; + struct ssl_engine_list *el; + + /* 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; + } + + el = calloc(1, sizeof(*el)); + el->e = engine; + LIST_ADD(&openssl_engines, &el->list); + return 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; +} + /* * This function returns the number of seconds elapsed * since the Epoch, 1970-01-01 00:00:00 +0000 (UTC) and the @@ -6929,6 +6978,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; + int ret = -1; + + if (*(args[1]) == 0) { + memprintf(err, "global statement '%s' expects a valid engine name as an argument.", args[0]); + return ret; + } + + if (*(args[2]) == 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 algo */ + if (strcmp(args[2], "algo") != 0) { + memprintf(err, "global statement '%s' expects to have algo keyword.", args[0]); + return ret; + } + + if (*(args[3]) == 0) { + memprintf(err, "global statement '%s' expects algorithm names as an argument.", args[0]); + return ret; + } + algo = strdup(args[3]); + +add_engine: + if (ssl_init_single_engine(args[1], algo)==0) { + openssl_engines_initialized++; + ret = 0; + } + free(algo); + return ret; +} + /* parse the "ssl-default-bind-ciphers" / "ssl-default-server-ciphers" keywords * in global section. Returns <0 on alert, >0 on warning, 0 on success. */ @@ -7537,6 +7628,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 }, @@ -7610,6 +7702,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 @@ -7671,39 +7764,51 @@ static void __ssl_sock_init(void) #ifndef OPENSSL_NO_DH ssl_dh_ptr_index = SSL_CTX_get_ex_new_index(0, NULL, NULL, NULL, NULL); + hap_register_post_deinit(ssl_free_dh); #endif + hap_register_post_deinit(ssl_free_engines); /* Load SSL string for the verbose & debug mode. */ ERR_load_SSL_strings(); } -__attribute__((destructor)) -static void __ssl_sock_deinit(void) -{ -#if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined SSL_NO_GENERATE_CERTIFICATES) - lru64_destroy(ssl_ctx_lru_tree); -#endif +void ssl_free_engines(void) { + struct ssl_engine_list *wl, *wlb; + /* free up engine list */ + list_for_each_entry_safe(wl, wlb, &openssl_engines, list) { + ENGINE_finish(wl->e); + ENGINE_free(wl->e); + LIST_DEL(&wl->list); + free(wl); + } +} #ifndef OPENSSL_NO_DH - if (local_dh_1024) { - DH_free(local_dh_1024); - local_dh_1024 = NULL; - } - - if (local_dh_2048) { - DH_free(local_dh_2048); - local_dh_2048 = NULL; - } - - if (local_dh_4096) { - DH_free(local_dh_4096); - local_dh_4096 = NULL; - } - +void ssl_free_dh(void) { + if (local_dh_1024) { + DH_free(local_dh_1024); + local_dh_1024 = NULL; + } + if (local_dh_2048) { + DH_free(local_dh_2048); + local_dh_2048 = NULL; + } + if (local_dh_4096) { + DH_free(local_dh_4096); + local_dh_4096 = NULL; + } if (global_dh) { DH_free(global_dh); global_dh = NULL; } +} +#endif + +__attribute__((destructor)) +static void __ssl_sock_deinit(void) +{ +#if (defined SSL_CTRL_SET_TLSEXT_HOSTNAME && !defined SSL_NO_GENERATE_CERTIFICATES) + lru64_destroy(ssl_ctx_lru_tree); #endif ERR_remove_state(0); -- 2.39.5