Browse code

tls: add support for OpenSSL engine and private keys in HSM

- add support for OpenSSL engine and loading private keys from HSM
- for when kamailio is a TLS edge proxy and needs to use HSM
- currently we initialize the engine in worker processes as PKCS#11
libraries are not guaranteed to be fork() safe

- new config params
- engine: name the OpenSSL engine
- engine_config: an OpenSSL config format file used to bootstrap engines
- engine_algorithms: list of algorithms to delegate to the engine

- tested with Gemalto SafeNet Luna (AWS CloudHSM) with RSA and EC private keys
TLSv1.2 and PFS cipher suites

AntonyA authored on 15/03/2018 13:41:29
Showing 8 changed files
1 1
new file mode 100644
... ...
@@ -0,0 +1,63 @@
1
+<?xml version="1.0" encoding='ISO-8859-1'?>
2
+<!DOCTYPE book PUBLIC "-//OASIS//DTD DocBook XML V4.4//EN"
3
+"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd" [
4
+
5
+<!-- Include general documentation entities -->
6
+<!ENTITY % docentities SYSTEM "../../../../doc/docbook/entities.xml">
7
+%docentities;
8
+
9
+]>
10
+
11
+<section id="tls.hsm_howto" xmlns:xi="http://www.w3.org/2001/XInclude">
12
+    <sectioninfo>
13
+    </sectioninfo>
14
+
15
+	<title>HSM Howto</title>
16
+		<para>
17
+			This documents OpenSSL engine support for private keys in HSM. 
18
+		</para>
19
+		<para>
20
+		        Assumptions: an OpenSSL engine configured with private key. We still require the certificate file
21
+			and list of CA certificates per a regular TLS configuration.
22
+		</para>
23
+		<para>
24
+		<programlisting>
25
+AWS CloudHSM Example
26
+--------------------
27
+
28
+...
29
+# Example for AWS CloudHSM (SafeNet Luna)
30
+modparam("tls", "engine", "gem")
31
+modparam("tls", "engine_config", "/usr/local/etc/kamailio/luna.conf")
32
+modparam("tls", "engine_algorithms", "ALL)
33
+...
34
+
35
+/usr/local/etc/kamailio/luna.cnf is a OpenSSL config format file used to
36
+bootstrap the engine, e.g., pass the PIN.
37
+
38
+...
39
+# the key kamailio is mandatory
40
+kamailio = openssl_init
41
+
42
+[ openssl_init ]
43
+engines = engine_section
44
+
45
+[ engine_section ]
46
+# gem is the name of the SafeNet Luna OpenSSL engine
47
+gem = gem_section
48
+
49
+[ gem_section ]
50
+# from SafeNet documentation
51
+ENGINE_INIT = 0:20:21:password=1234-ABCD-5678-EFGH
52
+...
53
+
54
+
55
+Thales nShield Connect
56
+----------------------
57
+
58
+Place holder
59
+		</programlisting>
60
+		</para>
61
+
62
+
63
+</section>
... ...
@@ -1196,4 +1196,56 @@ end
1196 1196
 	    </example>
1197 1197
 	</section>
1198 1198
 
1199
-</section>
1199
+	<section id="tls.p.engine">
1200
+	<title><varname>engine</varname> (string)</title>
1201
+	<para>
1202
+	        If OpenSSL is compiled with engine support this will allow algorithms to be offloaded and
1203
+	        private keys from HSM to be used. Currently only a single global engine is supported.
1204
+		However, private keys can be specified per_domain.
1205
+	</para>
1206
+
1207
+	<para>
1208
+	        To use private keys from the HSM, the name is the HSM key label prefixed by <varname>/engine:</varname>.
1209
+	</para>
1210
+		<programlisting format="linespecific">
1211
+...
1212
+## example for the Gem engine
1213
+modparam("tls", "engine", "gem")
1214
+# can also be set per-domain in tls.cfg
1215
+modparam("tls", "private_key", "/engine:my_HSM_key_label")
1216
+
1217
+## example for engine_pkcs11
1218
+modparam("tls", "engine", "pkcs11")
1219
+modparam("tls", "private_key", "/engine:pkcs11:token=MYTOKEN;object=MYKEYLABEL")
1220
+
1221
+modparam("tls", "engine_conf", "/usr/local/etc/kamailio/openssl.cnf")
1222
+modparam("tls", "engine_algorithms", "ALL")
1223
+...
1224
+                </programlisting>
1225
+	<para>
1226
+		By default OpenSSL engine support  is disabled (NONE). This global param is not supported in the tls config file.
1227
+	</para>
1228
+        </section>
1229
+
1230
+	<section id="tls.p.engine_config">
1231
+	<title><varname>engine_config</varname> (string)</title>
1232
+	<para>
1233
+	        A OpenSSL configuration file to initialize the engine. Typically used to send PIN to HSMs to unlock
1234
+	        private keys. See the HSM howto for an example. This global param is not supported in the tls config file.
1235
+	</para>
1236
+        </section>
1237
+
1238
+
1239
+	<section id="tls.p.engine_algorithms">
1240
+	<title><varname>engine_algorithms</varname> (string)</title>
1241
+	<para>
1242
+	        A list of cryptographic methods to be set as default in the engine.
1243
+	        This is a comma-separated list of values
1244
+	        from ALL RSA DSA DH EC RAND CIPHERS DIGESTS PKEY PKEY_CRYPTO PKEY_ASN1.
1245
+		Not all methods are supported by every engine.
1246
+	</para>
1247
+	<para>
1248
+	        The default is not to set any methods as default. This global param is not supported in the tls config file.
1249
+	</para>
1250
+        </section>
1251
+ </section>
... ...
@@ -271,6 +271,7 @@ make -C modules/tls extra_defs="-DTLS_WR_DEBUG -DTLS_RD_DEBUG"
271 271
 	</section>
272 272
 
273 273
 	<xi:include href="certs_howto.xml"/>
274
+	<xi:include href="hsm_howto.xml"/>
274 275
 	<xi:include href="params.xml"/>
275 276
 	<xi:include href="functions.xml"/>
276 277
 	<xi:include href="rpc.xml"/>
... ...
@@ -27,6 +27,13 @@
27 27
 #include <stdlib.h>
28 28
 #include <openssl/ssl.h>
29 29
 #include <openssl/opensslv.h>
30
+
31
+#ifndef OPENSSL_NO_ENGINE
32
+#include <openssl/engine.h>
33
+#include "tls_map.h"
34
+extern EVP_PKEY * tls_engine_private_key(const char* key_id);
35
+#endif
36
+
30 37
 #if OPENSSL_VERSION_NUMBER >= 0x00907000L
31 38
 # include <openssl/ui.h>
32 39
 #endif
... ...
@@ -1113,7 +1120,109 @@ err:
1113 1120
 #endif
1114 1121
 }
1115 1122
 
1123
+#ifndef OPENSSL_NO_ENGINE
1124
+/*
1125
+ * Implement a hash map from SSL_CTX to private key
1126
+ * as HSM keys need to be process local
1127
+ */
1128
+static map_void_t private_key_map;
1129
+
1130
+/**
1131
+ * @brief Return a private key from the lookup table
1132
+ * @param p SSL_CTX*
1133
+ * @return EVP_PKEY on success, NULL on error
1134
+ */
1135
+EVP_PKEY* tls_lookup_private_key(SSL_CTX* ctx)
1136
+{
1137
+	void *pkey;
1138
+	char ctx_str[64];
1139
+	snprintf(ctx_str, 64, "SSL_CTX-%p", ctx);
1140
+	pkey =  map_get(&private_key_map, ctx_str);
1141
+	LM_DBG("Private key lookup for %s: %p\n", ctx_str, pkey);
1142
+	if (pkey)
1143
+		return *(EVP_PKEY**)pkey;
1144
+	else
1145
+		return NULL;
1146
+}
1147
+
1148
+
1149
+
1150
+/**
1151
+ * @brief Load a private key from an OpenSSL engine
1152
+ * @param d TLS domain
1153
+ * @return 0 on success, -1 on error
1154
+ *
1155
+ * Do this in mod_child() as PKCS#11 libraries are not guaranteed
1156
+ * to be fork() safe
1157
+ *
1158
+ * private_key setting which starts with /engine: is assumed to be
1159
+ * an HSM key and not a file-based key
1160
+ *
1161
+ * We store the private key in a local memory hash table as
1162
+ * HSM keys must be process-local. We use the SSL_CTX* address
1163
+ * as the key. We cannot put the key into d->ctx[i] as that is
1164
+ * in shared memory.
1165
+ */
1166
+static int load_engine_private_key(tls_domain_t* d)
1167
+{
1168
+	int idx, ret_pwd, i;
1169
+	EVP_PKEY *pkey;
1170
+	int procs_no;
1171
+	char ctx_str[64];
1172
+
1173
+	if (!d->pkey_file.s || !d->pkey_file.len) {
1174
+		DBG("%s: No private key specified\n", tls_domain_str(d));
1175
+		return 0;
1176
+	}
1177
+	if (strncmp(d->pkey_file.s, "/engine:", 8) != 0)
1178
+		return 0;
1179
+	procs_no = get_max_procs();
1180
+	for (i = 0; i<procs_no; i++) {
1181
+		snprintf(ctx_str, 64, "SSL_CTX-%p", d->ctx[i]);
1182
+		for(idx = 0, ret_pwd = 0; idx < 3; idx++) {
1183
+			if (i) {
1184
+				map_set(&private_key_map, ctx_str, pkey);
1185
+				ret_pwd = 1;
1186
+			} else {
1187
+				pkey = tls_engine_private_key(d->pkey_file.s+8);
1188
+				if (pkey) {
1189
+					map_set(&private_key_map, ctx_str, pkey);
1190
+					// store the key for i = 0 to perform certificate sanity check
1191
+					ret_pwd = SSL_CTX_use_PrivateKey(d->ctx[i], pkey);
1192
+				} else {
1193
+					ret_pwd = 0;
1194
+				}
1195
+			}
1196
+			if (ret_pwd) {
1197
+				break;
1198
+			} else {
1199
+				ERR("%s: Unable to load private key '%s'\n",
1200
+				    tls_domain_str(d), d->pkey_file.s);
1201
+				TLS_ERR("load_private_key:");
1202
+				continue;
1203
+			}
1204
+		}
1205
+
1206
+		if (!ret_pwd) {
1207
+			ERR("%s: Unable to load engine key label '%s'\n",
1208
+			    tls_domain_str(d), d->pkey_file.s);
1209
+			TLS_ERR("load_private_key:");
1210
+			return -1;
1211
+		}
1212
+		if (i == 0 && !SSL_CTX_check_private_key(d->ctx[i])) {
1213
+			ERR("%s: Key '%s' does not match the public key of the"
1214
+			    " certificate\n", tls_domain_str(d), d->pkey_file.s);
1215
+			TLS_ERR("load_engine_private_key:");
1216
+			return -1;
1217
+		}
1218
+	}
1219
+
1116 1220
 
1221
+	LM_INFO("%s: Key '%s' successfully loaded\n",
1222
+		tls_domain_str(d), d->pkey_file.s);
1223
+	return 0;
1224
+}
1225
+#endif
1117 1226
 /**
1118 1227
  * @brief Load a private key from a file 
1119 1228
  * @param d TLS domain
... ...
@@ -1137,8 +1246,19 @@ static int load_private_key(tls_domain_t* d)
1137 1246
 		SSL_CTX_set_default_passwd_cb_userdata(d->ctx[i], d->pkey_file.s);
1138 1247
 		
1139 1248
 		for(idx = 0, ret_pwd = 0; idx < 3; idx++) {
1249
+#ifndef OPENSSL_NO_ENGINE
1250
+			// in PROC_INIT skip loading HSM keys due to
1251
+			// fork() issues with PKCS#11 libaries
1252
+			if (strncmp(d->pkey_file.s, "/engine:", 8) != 0) {
1253
+				ret_pwd = SSL_CTX_use_PrivateKey_file(d->ctx[i], d->pkey_file.s,
1254
+					SSL_FILETYPE_PEM);
1255
+			} else {
1256
+				ret_pwd = 1;
1257
+			}
1258
+#else
1140 1259
 			ret_pwd = SSL_CTX_use_PrivateKey_file(d->ctx[i], d->pkey_file.s,
1141 1260
 					SSL_FILETYPE_PEM);
1261
+#endif
1142 1262
 			if (ret_pwd) {
1143 1263
 				break;
1144 1264
 			} else {
... ...
@@ -1155,7 +1275,12 @@ static int load_private_key(tls_domain_t* d)
1155 1275
 			TLS_ERR("load_private_key:");
1156 1276
 			return -1;
1157 1277
 		}
1158
-		
1278
+#ifndef OPENSSL_NO_ENGINE
1279
+		if (strncmp(d->pkey_file.s, "/engine:", 8) == 0) {
1280
+			// skip private key validity check for HSM keys
1281
+			continue;
1282
+		}
1283
+#endif
1159 1284
 		if (!SSL_CTX_check_private_key(d->ctx[i])) {
1160 1285
 			ERR("%s: Key '%s' does not match the public key of the"
1161 1286
 					" certificate\n", tls_domain_str(d), d->pkey_file.s);
... ...
@@ -1170,6 +1295,36 @@ static int load_private_key(tls_domain_t* d)
1170 1295
 }
1171 1296
 
1172 1297
 
1298
+#ifndef OPENSSL_NO_ENGINE
1299
+/**
1300
+ * @brief Initialize engine private keys
1301
+ *
1302
+ * PKCS#11 libraries are not guaranteed to be fork() safe
1303
+ * so we fix private keys in the child
1304
+ */
1305
+int tls_fix_engine_keys(tls_domains_cfg_t* cfg, tls_domain_t* srv_defaults,
1306
+				tls_domain_t* cli_defaults)
1307
+{
1308
+	tls_domain_t* d;
1309
+	d = cfg->srv_list;
1310
+	while(d) {
1311
+		if (load_engine_private_key(d) < 0) return -1;
1312
+		d = d->next;
1313
+	}
1314
+
1315
+	d = cfg->cli_list;
1316
+	while(d) {
1317
+		if (load_engine_private_key(d) < 0) return -1;
1318
+		d = d->next;
1319
+	}
1320
+
1321
+	if (load_engine_private_key(cfg->srv_default) < 0) return -1;
1322
+	if (load_engine_private_key(cfg->cli_default) < 0) return -1;
1323
+
1324
+	return 0;
1325
+
1326
+}
1327
+#endif
1173 1328
 /**
1174 1329
  * @brief Initialize attributes of all domains from default domains if necessary
1175 1330
  *
1176 1331
new file mode 100644
... ...
@@ -0,0 +1,195 @@
1
+/** 
2
+ * Copyright (c) 2014 rxi
3
+ *
4
+ * This library is free software; you can redistribute it and/or modify it
5
+ * under the terms of the MIT license. See LICENSE for details.
6
+ */
7
+
8
+#include <stdlib.h>
9
+#include <string.h>
10
+
11
+#include "../../core/mem/mem.h"
12
+#include "tls_map.h"
13
+
14
+struct map_node_t {
15
+  unsigned hash;
16
+  void *value;
17
+  map_node_t *next;
18
+  /* char key[]; */
19
+  /* char value[]; */
20
+};
21
+
22
+
23
+static unsigned map_hash(const char *str) {
24
+  unsigned hash = 5381;
25
+  while (*str) {
26
+    hash = ((hash << 5) + hash) ^ *str++;
27
+  }
28
+  return hash;
29
+}
30
+
31
+
32
+static map_node_t *map_newnode(const char *key, void *value, int vsize) {
33
+  map_node_t *node;
34
+  int ksize = strlen(key) + 1;
35
+  int voffset = ksize + ((sizeof(void*) - ksize) % sizeof(void*));
36
+  node = pkg_malloc(sizeof(*node) + voffset + vsize);
37
+  if (!node) return NULL;
38
+  memcpy(node + 1, key, ksize);
39
+  node->hash = map_hash(key);
40
+  node->value = ((char*) (node + 1)) + voffset;
41
+  memcpy(node->value, value, vsize);
42
+  return node;
43
+}
44
+
45
+
46
+static int map_bucketidx(map_base_t *m, unsigned hash) {
47
+  /* If the implementation is changed to allow a non-power-of-2 bucket count,
48
+   * the line below should be changed to use mod instead of AND */
49
+  return hash & (m->nbuckets - 1);
50
+}
51
+
52
+
53
+static void map_addnode(map_base_t *m, map_node_t *node) {
54
+  int n = map_bucketidx(m, node->hash);
55
+  node->next = m->buckets[n];
56
+  m->buckets[n] = node;
57
+}
58
+
59
+
60
+static int map_resize(map_base_t *m, int nbuckets) {
61
+  map_node_t *nodes, *node, *next;
62
+  map_node_t **buckets;
63
+  int i; 
64
+  /* Chain all nodes together */
65
+  nodes = NULL;
66
+  i = m->nbuckets;
67
+  while (i--) {
68
+    node = (m->buckets)[i];
69
+    while (node) {
70
+      next = node->next;
71
+      node->next = nodes;
72
+      nodes = node;
73
+      node = next;
74
+    }
75
+  }
76
+  /* Reset buckets */
77
+  buckets = realloc(m->buckets, sizeof(*m->buckets) * nbuckets);
78
+  if (buckets != NULL) {
79
+    m->buckets = buckets;
80
+    m->nbuckets = nbuckets;
81
+  }
82
+  if (m->buckets) {
83
+    memset(m->buckets, 0, sizeof(*m->buckets) * m->nbuckets);
84
+    /* Re-add nodes to buckets */
85
+    node = nodes;
86
+    while (node) {
87
+      next = node->next;
88
+      map_addnode(m, node);
89
+      node = next;
90
+    }
91
+  }
92
+  /* Return error code if realloc() failed */
93
+  return (buckets == NULL) ? -1 : 0;
94
+}
95
+
96
+
97
+static map_node_t **map_getref(map_base_t *m, const char *key) {
98
+  unsigned hash = map_hash(key);
99
+  map_node_t **next;
100
+  if (m->nbuckets > 0) {
101
+    next = &m->buckets[map_bucketidx(m, hash)];
102
+    while (*next) {
103
+      if ((*next)->hash == hash && !strcmp((char*) (*next + 1), key)) {
104
+        return next;
105
+      }
106
+      next = &(*next)->next;
107
+    }
108
+  }
109
+  return NULL;
110
+}
111
+
112
+
113
+void map_deinit_(map_base_t *m) {
114
+  map_node_t *next, *node;
115
+  int i;
116
+  i = m->nbuckets;
117
+  while (i--) {
118
+    node = m->buckets[i];
119
+    while (node) {
120
+      next = node->next;
121
+      pkg_free(node);
122
+      node = next;
123
+    }
124
+  }
125
+  pkg_free(m->buckets);
126
+}
127
+
128
+
129
+void *map_get_(map_base_t *m, const char *key) {
130
+  map_node_t **next = map_getref(m, key);
131
+  return next ? (*next)->value : NULL;
132
+}
133
+
134
+
135
+int map_set_(map_base_t *m, const char *key, void *value, int vsize) {
136
+  int n, err;
137
+  map_node_t **next, *node;
138
+  /* Find & replace existing node */
139
+  next = map_getref(m, key);
140
+  if (next) {
141
+    memcpy((*next)->value, value, vsize);
142
+    return 0;
143
+  }
144
+  /* Add new node */
145
+  node = map_newnode(key, value, vsize);
146
+  if (node == NULL) goto fail;
147
+  if (m->nnodes >= m->nbuckets) {
148
+    n = (m->nbuckets > 0) ? (m->nbuckets << 1) : 1;
149
+    err = map_resize(m, n);
150
+    if (err) goto fail;
151
+  }
152
+  map_addnode(m, node);
153
+  m->nnodes++;
154
+  return 0;
155
+  fail:
156
+  if (node) pkg_free(node);
157
+  return -1;
158
+}
159
+
160
+
161
+void map_remove_(map_base_t *m, const char *key) {
162
+  map_node_t *node;
163
+  map_node_t **next = map_getref(m, key);
164
+  if (next) {
165
+    node = *next;
166
+    *next = (*next)->next;
167
+    pkg_free(node);
168
+    m->nnodes--;
169
+  }
170
+}
171
+
172
+
173
+map_iter_t map_iter_(void) {
174
+  map_iter_t iter;
175
+  iter.bucketidx = -1;
176
+  iter.node = NULL;
177
+  return iter;
178
+}
179
+
180
+
181
+const char *map_next_(map_base_t *m, map_iter_t *iter) {
182
+  if (iter->node) {
183
+    iter->node = iter->node->next;
184
+    if (iter->node == NULL) goto nextBucket;
185
+  } else {
186
+    nextBucket:
187
+    do {
188
+      if (++iter->bucketidx >= m->nbuckets) {
189
+        return NULL;
190
+      }
191
+      iter->node = m->buckets[iter->bucketidx];
192
+    } while (iter->node == NULL);
193
+  }
194
+  return (char*) (iter->node + 1);
195
+}
0 196
new file mode 100644
... ...
@@ -0,0 +1,77 @@
1
+/** 
2
+ * Copyright (c) 2014 rxi
3
+ *
4
+ * This library is free software; you can redistribute it and/or modify it
5
+ * under the terms of the MIT license. See LICENSE for details.
6
+ */
7
+
8
+#ifndef _TLS_MAP_H
9
+#define _TLS_MAP_H
10
+
11
+#include <string.h>
12
+
13
+#define MAP_VERSION "0.1.0"
14
+
15
+struct map_node_t;
16
+typedef struct map_node_t map_node_t;
17
+
18
+typedef struct {
19
+  map_node_t **buckets;
20
+  unsigned nbuckets, nnodes;
21
+} map_base_t;
22
+
23
+typedef struct {
24
+  unsigned bucketidx;
25
+  map_node_t *node;
26
+} map_iter_t;
27
+
28
+
29
+#define map_t(T)\
30
+  struct { map_base_t base; T *ref; T tmp; }
31
+
32
+
33
+#define map_init(m)\
34
+  memset(m, 0, sizeof(*(m)))
35
+
36
+
37
+#define map_deinit(m)\
38
+  map_deinit_(&(m)->base)
39
+
40
+
41
+#define map_get(m, key)\
42
+  ( (m)->ref = map_get_(&(m)->base, key) )
43
+
44
+
45
+#define map_set(m, key, value)\
46
+  ( (m)->tmp = (value),\
47
+    map_set_(&(m)->base, key, &(m)->tmp, sizeof((m)->tmp)) )
48
+
49
+
50
+#define map_remove(m, key)\
51
+  map_remove_(&(m)->base, key)
52
+
53
+
54
+#define map_iter(m)\
55
+  map_iter_()
56
+
57
+
58
+#define map_next(m, iter)\
59
+  map_next_(&(m)->base, iter)
60
+
61
+
62
+void map_deinit_(map_base_t *m);
63
+void *map_get_(map_base_t *m, const char *key);
64
+int map_set_(map_base_t *m, const char *key, void *value, int vsize);
65
+void map_remove_(map_base_t *m, const char *key);
66
+map_iter_t map_iter_(void);
67
+const char *map_next_(map_base_t *m, map_iter_t *iter);
68
+
69
+
70
+typedef map_t(void*) map_void_t;
71
+typedef map_t(char*) map_str_t;
72
+typedef map_t(int) map_int_t;
73
+typedef map_t(char) map_char_t;
74
+typedef map_t(float) map_float_t;
75
+typedef map_t(double) map_double_t;
76
+
77
+#endif /* _TLS_MAP_H */
... ...
@@ -131,6 +131,23 @@ tls_domain_t srv_defaults = {
131 131
 };
132 132
 
133 133
 
134
+#ifndef OPENSSL_NO_ENGINE
135
+
136
+typedef struct tls_engine {
137
+        str engine;
138
+        str engine_config;
139
+        str engine_algorithms;
140
+} tls_engine_t;
141
+#include <openssl/conf.h>
142
+#include <openssl/engine.h>
143
+
144
+static ENGINE *ksr_tls_engine;
145
+static tls_engine_t tls_engine_settings = {
146
+	STR_STATIC_INIT("NONE"),
147
+	STR_STATIC_INIT("NONE"),
148
+	STR_STATIC_INIT("ALL"),
149
+};
150
+#endif /* OPENSSL_NO_ENGINE */
134 151
 /*
135 152
  * Default settings for client domains when using external config file
136 153
  */
... ...
@@ -189,6 +206,11 @@ static param_export_t params[] = {
189 206
 	{"crl",                 PARAM_STR,    &default_tls_cfg.crl          },
190 207
 	{"cipher_list",         PARAM_STR,    &default_tls_cfg.cipher_list  },
191 208
 	{"connection_timeout",  PARAM_INT,    &default_tls_cfg.con_lifetime },
209
+#ifndef OPENSSL_NO_ENGINE
210
+	{"engine",              PARAM_STR,    &tls_engine_settings.engine  },
211
+	{"engine_config",       PARAM_STR,    &tls_engine_settings.engine_config },
212
+	{"engine_algorithms",   PARAM_STR,    &tls_engine_settings.engine_algorithms },
213
+#endif /* OPENSSL_NO_ENGINE */
192 214
 	{"tls_log",             PARAM_INT,    &default_tls_cfg.log          },
193 215
 	{"tls_debug",           PARAM_INT,    &default_tls_cfg.debug        },
194 216
 	{"session_cache",       PARAM_INT,    &default_tls_cfg.session_cache},
... ...
@@ -361,10 +383,15 @@ error:
361 383
 }
362 384
 
363 385
 
386
+#ifndef OPENSSL_NO_ENGINE
387
+static int tls_engine_init();
388
+int tls_fix_engine_keys(tls_domains_cfg_t*, tls_domain_t*, tls_domain_t*);
389
+#endif
364 390
 static int mod_child(int rank)
365 391
 {
366 392
 	if (tls_disable || (tls_domains_cfg==0))
367 393
 		return 0;
394
+
368 395
 	/* fix tls config only from the main proc/PROC_INIT., when we know
369 396
 	 * the exact process number and before any other process starts*/
370 397
 	if (rank == PROC_INIT){
... ...
@@ -378,6 +405,21 @@ static int mod_child(int rank)
378 405
 				return -1;
379 406
 		}
380 407
 	}
408
+#ifndef OPENSSL_NO_ENGINE
409
+	/*
410
+	 * after the child is fork()ed we go through the TLS domains
411
+	 * and fix up private keys from engine
412
+	 */
413
+	if (!strncmp(tls_engine_settings.engine.s, "NONE", 4)) return 0;
414
+
415
+	if (rank > 0) {
416
+		if (tls_engine_init() < 0)
417
+			return -1;
418
+		if (tls_fix_engine_keys(*tls_domains_cfg, &srv_defaults, &cli_defaults)<0)
419
+			return -1;
420
+		LM_INFO("OpenSSL Engine loaded private keys in child: %d\n", rank);
421
+	}
422
+#endif
381 423
 	return 0;
382 424
 }
383 425
 
... ...
@@ -496,3 +538,54 @@ int mod_register(char *path, int *dlflags, void *p1, void *p2)
496 538
 
497 539
 	return 0;
498 540
 }
541
+
542
+
543
+
544
+#ifndef OPENSSL_NO_ENGINE
545
+/*
546
+ * initialize OpenSSL engine in child process
547
+ * PKCS#11 libraries are not guaranteed to be fork() safe
548
+ *
549
+ */
550
+static int tls_engine_init()
551
+{
552
+	LM_DBG("With OpenSSL engine support\n");
553
+	if (strncmp(tls_engine_settings.engine.s, "NONE", 4)) {
554
+		int err = 0;
555
+		ENGINE_load_builtin_engines();
556
+		OPENSSL_load_builtin_modules();
557
+		if (strncmp(tls_engine_settings.engine_config.s, "NONE", 4)) {
558
+			err = CONF_modules_load_file(tls_engine_settings.engine_config.s, "kamailio", 0);
559
+			if (!err) {
560
+				LM_ERR("OpenSSL failed to load ENGINE configuration file: %*s\n", tls_engine_settings.engine_config.len, tls_engine_settings.engine_config.s);
561
+				goto error;
562
+			}
563
+		}
564
+		ksr_tls_engine = ENGINE_by_id(tls_engine_settings.engine.s);
565
+		if (!ksr_tls_engine) {
566
+			LM_ERR("OpenSSL failed to obtain ENGINE: %*s\n", tls_engine_settings.engine_config.len, tls_engine_settings.engine_config.s);
567
+			goto error;
568
+		}
569
+		err = ENGINE_init(ksr_tls_engine);
570
+		if (!err) {
571
+			LM_ERR("OpenSSL ENGINE_init() failed\n");
572
+			goto error;
573
+		}
574
+		if (strncmp(tls_engine_settings.engine_algorithms.s, "NONE", 4)) {
575
+			err = ENGINE_set_default_string(ksr_tls_engine, tls_engine_settings.engine_algorithms.s);
576
+			if (!err) {
577
+				LM_ERR("OpenSSL ENGINE could not set algorithms\n");
578
+				goto error;
579
+			}
580
+		}
581
+		LM_INFO("OpenSSL engine %*s initialized\n", tls_engine_settings.engine.len, tls_engine_settings.engine.s);
582
+	}
583
+	return 0;
584
+error:
585
+	return -1;
586
+}
587
+
588
+EVP_PKEY *tls_engine_private_key(const char* key_id) {
589
+	return ENGINE_load_private_key(ksr_tls_engine, key_id, NULL, NULL);
590
+}
591
+#endif
... ...
@@ -379,7 +379,10 @@ static void tls_dump_cert_info(char* s, X509* cert)
379 379
 }
380 380
 
381 381
 
382
-
382
+#ifndef OPENSSL_NO_ENGINE
383
+// lookup HSM keys in process-local memory
384
+EVP_PKEY * tls_lookup_private_key(SSL_CTX*);
385
+#endif
383 386
 /** wrapper around SSL_accept, usin SSL return convention.
384 387
  * It will also log critical errors and certificate debugging info.
385 388
  * @param c - tcp connection with tls (extra_data must be a filled
... ...
@@ -410,6 +413,12 @@ int tls_accept(struct tcp_connection *c, int* error)
410 413
 		BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state);
411 414
 		goto err;
412 415
 	}
416
+#ifndef OPENSSL_NO_ENGINE
417
+	/* check if we have a HSM key */
418
+	EVP_PKEY *pkey = tls_lookup_private_key(SSL_get_SSL_CTX(ssl));
419
+	if (pkey)
420
+		SSL_use_PrivateKey(ssl, pkey);
421
+#endif
413 422
 	ret = SSL_accept(ssl);
414 423
 	if (unlikely(ret == 1)) {
415 424
 		DBG("TLS accept successful\n");
... ...
@@ -475,6 +484,13 @@ int tls_connect(struct tcp_connection *c, int* error)
475 484
 		BUG("Invalid connection state %d (bug in TLS code)\n", tls_c->state);
476 485
 		goto err;
477 486
 	}
487
+#ifndef OPENSSL_NO_ENGINE
488
+	// lookup HSM private key in process-local memory
489
+	EVP_PKEY *pkey = tls_lookup_private_key(SSL_get_SSL_CTX(ssl));
490
+	if (pkey) {
491
+		SSL_use_PrivateKey(ssl, pkey);
492
+	}
493
+#endif
478 494
 	ret = SSL_connect(ssl);
479 495
 	if (unlikely(ret == 1)) {
480 496
 		DBG("TLS connect successful\n");