]> git.ipfire.org Git - thirdparty/haproxy.git/commitdiff
MEDIUM: ssl: add basic support for OpenSSL crypto engine
authorGrant Zhang <gzhang@fastly.com>
Sat, 21 Jan 2017 01:10:18 +0000 (01:10 +0000)
committerWilly Tarreau <w@1wt.eu>
Sat, 27 May 2017 05:05:00 +0000 (07:05 +0200)
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
include/proto/ssl_sock.h
src/haproxy.c
src/ssl_sock.c

index bd9a99f13c99a300e300d0b0068737a081295967..8d5ddc153581624e7421d2ca5ca1669d75f642d0 100644 (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
index 6f779faaed6df305d68e2b5ed42cecd65c684df3..8779770e35d61545e87203ad650923ed41244132 100644 (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);
index 5ccebd1054d750fe1784838e0ffb1d3a456eec42..6ecae257d5ad4e8dad1db652784a40cf1d292d35 100644 (file)
 #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 */
                }
 
index 21d50c753df61b4793bbf06db2fc7031bdcbabc4..bad9b9964b66bc0e6a47acf244cd2cd54a9bd513 100644 (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,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);