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.
This commit is contained in:
Grant Zhang 2017-01-21 01:10:18 +00:00 committed by Willy Tarreau
parent 7f80eb2383
commit 872f9c2139
4 changed files with 149 additions and 22 deletions

View File

@ -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 <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

View File

@ -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);

View File

@ -109,6 +109,7 @@
#include <proto/task.h>
#include <proto/dns.h>
#include <proto/vars.h>
#include <proto/ssl_sock.h>
/* 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 */
}

View File

@ -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>
@ -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 <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 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,12 +7764,46 @@ 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();
}
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
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)
{
@ -7684,28 +7811,6 @@ static void __ssl_sock_deinit(void)
lru64_destroy(ssl_ctx_lru_tree);
#endif
#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;
}
if (global_dh) {
DH_free(global_dh);
global_dh = NULL;
}
#endif
ERR_remove_state(0);
ERR_free_strings();