The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
engine.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or modify
3  * it under the terms of the GNU General Public License as published by
4  * the Free Software Foundation; either version 2 of the License, or
5  * (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software
14  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
15  */
16 
17 /**
18  * $Id: 6267e530bb6b6102c5630b30becfbf269014360c $
19  *
20  * @file tls/engine.c
21  * @brief Initialise and manage OpenSSL engines
22  *
23  * @copyright 2021 The FreeRADIUS server project
24  * @copyright 2021 Arran Cudbard-Bell <a.cudbardb@freeradius.org>
25  */
26 RCSID("$Id: 6267e530bb6b6102c5630b30becfbf269014360c $")
27 USES_APPLE_DEPRECATED_API /* OpenSSL API has been deprecated by Apple */
28 
29 #ifdef WITH_TLS
30 #define LOG_PREFIX "tls"
31 
32 #include <freeradius-devel/tls/base.h>
33 #include <freeradius-devel/tls/engine.h>
34 #include <freeradius-devel/tls/log.h>
35 #include <freeradius-devel/tls/strerror.h>
36 #include <freeradius-devel/util/dlist.h>
37 #include <freeradius-devel/util/rb.h>
38 #include <freeradius-devel/util/strerror.h>
39 #include <freeradius-devel/util/value.h>
40 
41 #if OPENSSL_VERSION_NUMBER < 0x30000000L
42 #include <openssl/engine.h>
43 
44 /** Our wrapper around an OpenSSL engine
45  *
46  */
47 typedef struct {
48  fr_rb_node_t node; //!< rbtree node fields.
49 
50  char const *id; //!< Engine identifier.
51  char const *instance; //!< Instance identifier for the engine.
52 
53  ENGINE *e; //!< Engine that was loaded.
54  fr_tls_engine_ctrl_list_t *pre_ctrls; //!< Pre controls applied to the engine.
55  fr_tls_engine_ctrl_list_t *post_ctrls; //!< Post controls applied to the engine.
56 } tls_engine_t;
57 
58 /** Engines that we've loaded
59  *
60  * This is the global set of OpenSSL engines that are in use by the
61  * current configuration.
62  *
63  * We could use OpenSSL's reference counting system, but this doesn't
64  * work well for dynamically loaded engines, where we may want one
65  * instance of the engine per thread.
66  */
67 static fr_rb_tree_t *tls_engines;
68 
69 /** Compares two engines
70  *
71  */
72 static int8_t tls_engine_cmp(void const *one, void const *two)
73 {
74  tls_engine_t const *a = talloc_get_type_abort_const(one, tls_engine_t);
75  tls_engine_t const *b = talloc_get_type_abort_const(two, tls_engine_t);
76  int8_t ret;
77 
78  ret = CMP(strcmp(a->id, b->id), 0);
79  if (ret != 0) return ret;
80 
81  /*
82  * May not have an instance ID
83  */
84  if (!a->instance && !b->instance) return 0;
85  if (!a->instance) return -1;
86  if (!b->instance) return +1;
87 
88  return CMP(strcmp(a->instance, b->instance), 0);
89 }
90 
91 /** Add the list of supported engine commands to the error stack
92  *
93  * Uses OpenSSL's ridiculously complex ENGINE_ctrl API to provide useful
94  * information about the controls the given engine provides.
95  *
96  * @param[in] e Engine to return commands for.
97  */
98 static void CC_HINT(nonnull) tls_engine_control_notfound_strerror(ENGINE *e, char const *bad_ctrl)
99 {
100  int cmd, ret;
101  TALLOC_CTX *pool;
102 
103  /*
104  * ENGINE_HAS_CTRL_FUNCTION doesn't seem
105  * to be available in OpenSSL 1.1.0 so
106  * we fudge it with this.
107  */
108  bool first = true;
109 
110  pool = talloc_pool(NULL, 256); /* Avoid lots of mallocs */
111  if (unlikely(!pool)) return;
112 
113  fr_strerror_printf("engine %s does not export control %s", ENGINE_get_id(e), bad_ctrl);
114 
115  for (cmd = ENGINE_ctrl(e, ENGINE_CTRL_GET_FIRST_CMD_TYPE, 0, NULL, NULL);
116  cmd > 0;
117  cmd = ENGINE_ctrl(e, ENGINE_CTRL_GET_NEXT_CMD_TYPE, cmd, NULL, NULL)) {
118  size_t name_len, desc_len;
119  char *name, *desc;
120  char const *flags;
121 
122  if (!ENGINE_cmd_is_executable(e, cmd)) continue;
123 
124  /*
125  * Copy the ctrl name out to a temporary buffer
126  */
127  name_len = ENGINE_ctrl(e, ENGINE_CTRL_GET_NAME_LEN_FROM_CMD, 0, NULL, NULL);
128  if (unlikely(name_len == 0)) continue;
129 
130  name = talloc_array(pool, char, name_len + 1);
131  if (unlikely(!name)) break;
132 
133  if (unlikely(ENGINE_ctrl(e, ENGINE_CTRL_GET_NAME_FROM_CMD, 0, name, NULL) <= 0)) break;
134 
135  /*
136  * Copy the ctrl description out to a temporary buffer
137  */
138  desc_len = ENGINE_ctrl(e, ENGINE_CTRL_GET_DESC_LEN_FROM_CMD, 0, NULL, NULL);
139  if (desc_len > 0) {
140  desc = talloc_array(pool, char, desc_len + 1);
141  if (unlikely(!desc)) break;
142 
143  if (unlikely(ENGINE_ctrl(e, ENGINE_CTRL_GET_DESC_FROM_CMD, 0, desc, NULL) <= 0)) break;
144  } else {
145  desc = NULL;
146  }
147 
148  ret = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, 0, NULL, NULL);
149  if (ret & ENGINE_CMD_FLAG_NO_INPUT) {
150  flags = "none";
151  } else if ((ret & ENGINE_CMD_FLAG_NUMERIC) && (ret & ENGINE_CMD_FLAG_STRING)) {
152  flags = "number and string";
153  } else if (ret & ENGINE_CMD_FLAG_NUMERIC) {
154  flags = "number";
155  } else if (ret & ENGINE_CMD_FLAG_STRING) {
156  flags = "string";
157  } else {
158  flags = "unavailable";
159  }
160 
161  if (first) {
162  fr_strerror_const_push("available controls are:");
163  first = false;
164  }
165  fr_strerror_printf_push("%s, arg(s) %s%s%s", name, flags, desc ? " - " : "", desc ? desc : "");
166  talloc_free_children(pool);
167  }
168  if (first) fr_strerror_const_push("no controls available");
169 
170  talloc_free(pool);
171 }
172 
173 /** Duplicate an engine control
174  *
175  * @param[in] ctx To allocate new control in.
176  * @param[in] in control to copy.
177  * @return
178  * - A copy of the engine control on success.
179  * - NULL on failure.
180  */
181 static inline CC_HINT(always_inline) fr_tls_engine_ctrl_t *tls_engine_ctrl_dup(TALLOC_CTX *ctx,
182  fr_tls_engine_ctrl_t const *in)
183 {
184  fr_tls_engine_ctrl_t *n;
185 
186  n = talloc(ctx, fr_tls_engine_ctrl_t);
187  if (unlikely(!n)) {
188  fr_strerror_const("Out of memory");
189  return n;
190  }
191 
192  *n = (fr_tls_engine_ctrl_t){
193  .name = talloc_typed_strdup(n, in->name),
194  .value = talloc_typed_strdup(n, in->value)
195  };
196 
197  return n;
198 }
199 
200 /** Unloads the underlying OpenSSL engine
201  *
202  */
203 static int _tls_engine_free(tls_engine_t *our_e)
204 {
205  /*
206  * Make memory leaks very explicit
207  * so someone will investigate.
208  */
209  if (unlikely(ENGINE_finish(our_e->e) != 1)) {
210  fr_tls_log(NULL, "de-init on engine %s failed", our_e->id);
211  return -1;
212  }
213 
214  if (unlikely(ENGINE_free(our_e->e) != 1)) {
215  fr_tls_log(NULL, "free on engine %s failed", our_e->id);
216  return -1;
217  }
218 
219  return 0;
220 }
221 
222 /** Initialise an OpenSSL engine, adding it to our list of engines
223  *
224  * @note Errors should be retrieved with fr_strerror().
225  *
226  * @param[out] e_out The engine that was just initialised.
227  * The caller must not free/finish this engine
228  * it will be called up when the server exits.
229  * @param[in] id Engine identifier. This is usually identifier
230  * that matches OpenSSL's engine ID e.g. "pkcs11".
231  * @param[in] instance Instance identifier for a given engine.
232  * This is useful for "dynamic" engines, i.e. ones
233  * OpenSSL dynamically loads.
234  * @param[in] pre_ctrls Engine ctls to be used after obtaining a
235  * structural reference but before obtaining a
236  * functional reference (after loading before init).
237  * Will be duplicated to avoid ordering issues.
238  * @param[in] post_ctrls Engine ctls to be used before unloading an
239  * engine (to shut it down in a graceful way).
240  * Will be duplicated to avoid ordering issues.
241  * @return
242  * - 0 on success.
243  * - -1 on failure.
244  */
245 int fr_tls_engine_init(ENGINE **e_out,
246  char const *id, char const *instance,
247  fr_tls_engine_ctrl_list_t const *pre_ctrls, fr_tls_engine_ctrl_list_t const *post_ctrls)
248 {
249  tls_engine_t *our_e = NULL;
250  ENGINE *e;
251  fr_tls_engine_ctrl_t *ctrl = NULL, *n;
252 
253  if (!tls_engines) {
254  tls_engines = fr_rb_inline_alloc(NULL, tls_engine_t, node, tls_engine_cmp, talloc_free_data);
255  if (unlikely(!tls_engines)) {
256  oom:
257  fr_strerror_const("Out of memory");
258  return -1;
259  }
260  } else {
261  tls_engine_t *found = NULL;
262 
263  found = fr_rb_find(tls_engines, &(tls_engine_t){ .id = id, .instance = instance });
264  if (found) {
265  fr_strerror_printf("engine %s%s%s%salready initialised", id,
266  instance ? " (" : "",
267  instance ? instance : "",
268  instance ? ") " : "");
269  return -1;
270  }
271  }
272 
273  e = ENGINE_by_id(id);
274  if (!e) {
275  fr_strerror_printf("%s engine is not available", id);
276  return -1;
277  }
278 
279  if (pre_ctrls) while ((ctrl = fr_dlist_next(pre_ctrls, ctrl))) {
280  int cmd, flags, ret;
281 
282  cmd = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FROM_NAME, 0, UNCONST(void *, ctrl->name), NULL);
283  if (cmd == 0) {
284  fr_strerror_printf("%s engine does not implement \"%s\" control", id, ctrl->name);
285  /*
286  * Dumps all available controls to
287  * the error stack.
288  */
289  tls_engine_control_notfound_strerror(e, ctrl->name);
290  error:
291  ENGINE_free(e);
292  return -1;
293  }
294 
295  /*
296  * If the command has the ENGINE_CMD_FLAG_NO_INPUT flag set,
297  * arg must be NULL and ENGINE_ctrl() is called with i set to
298  * 0 and p set to NULL. Otherwise, arg must not be NULL.
299  * If the command accepts string input, i is set to 0 and arg
300  * is passed as the p argument to ENGINE_ctrl(). Otherwise, arg
301  * is converted with strtol(3) and passed as the i argument to
302  * ENGINE_ctrl(), setting p to NULL.
303  */
304  flags = ENGINE_ctrl(e, ENGINE_CTRL_GET_CMD_FLAGS, 0, NULL, NULL);
305  if (flags & ENGINE_CMD_FLAG_NO_INPUT) {
306  ret = ENGINE_ctrl(e, cmd, 0, NULL, NULL);
307  /*
308  * Do an explicit sanity check for this
309  */
310  } else if (unlikely((flags & ENGINE_CMD_FLAG_STRING) && (flags & ENGINE_CMD_FLAG_NUMERIC))) {
311  fr_strerror_printf("File bug against freeradius-server stating "
312  "both numeric and string commands needed for OpenSSL engine controls");
313  goto error;
314  /*
315  * We do an explicit conversion to provide more useful feedback
316  * to the user in case the log
317  */
318  } else if (flags & ENGINE_CMD_FLAG_NUMERIC) {
319  fr_value_box_t vb;
320 
321  if (fr_value_box_cast(NULL, &vb, FR_TYPE_INT32, NULL, fr_box_strvalue(ctrl->value)) < 0) {
322  fr_strerror_printf_push("control %s requires an integer value", ctrl->name);
323  goto error;
324  }
325  ret = ENGINE_ctrl(e, cmd, vb.vb_int32, NULL, 0);
326  } else if (flags & ENGINE_CMD_FLAG_STRING) {
327  ret = ENGINE_ctrl(e, cmd, 0, UNCONST(void *, ctrl->value), NULL);
328  } else {
329  fr_strerror_printf("control %s exports invalid flags", ctrl->name);
330  goto error;
331  }
332 
333  /*
334  * ENGINE_ctrl_cmd() and ENGINE_ctrl_cmd_string() return 1 on
335  * success or 0 on error.
336  */
337  if (ret != 1) {
338  fr_tls_strerror_printf("control %s failed (%i)", ctrl->name, ret);
339  goto error;
340  }
341  }
342 
343  if (unlikely(ENGINE_init(e) != 1)) {
344  fr_tls_strerror_printf("failed initialising engine %s", id);
345  goto error;
346  }
347 
348  our_e = talloc(tls_engines, tls_engine_t);
349  if (unlikely(!our_e)) goto oom;
350 
351  *our_e = (tls_engine_t){
352  .id = talloc_typed_strdup(our_e, id),
353  .instance = talloc_typed_strdup(our_e, instance),
354  .e = e
355  };
356  talloc_set_destructor(our_e, _tls_engine_free);
357 
358  /*
359  * Duplicate pre and post ctrl lists
360  *
361  * This will allow us to create thread-specific
362  * dynamic engines later.
363  */
364  fr_dlist_talloc_init(our_e->pre_ctrls, fr_tls_engine_ctrl_t, entry);
365  fr_dlist_talloc_init(our_e->post_ctrls, fr_tls_engine_ctrl_t, entry);
366 
367  if (pre_ctrls) {
368  ctrl = NULL;
369  while ((ctrl = fr_dlist_next(pre_ctrls, ctrl))) {
370  n = tls_engine_ctrl_dup(our_e, ctrl);
371  if (unlikely(!n)) {
372  talloc_free(our_e);
373  return -1;
374  }
375  fr_dlist_insert_tail(our_e->pre_ctrls, n);
376  }
377  }
378 
379  if (post_ctrls) {
380  ctrl = NULL;
381  while ((ctrl = fr_dlist_next(post_ctrls, ctrl))) {
382  n = tls_engine_ctrl_dup(our_e, ctrl);
383  if (unlikely(!n)) {
384  talloc_free(our_e);
385  return -1;
386  }
387  fr_dlist_insert_tail(our_e->post_ctrls, n);
388  }
389  }
390 
391  *e_out = e;
392  return 0;
393 }
394 
395 /** Retrieve a pointer to an OpenSSL engine
396  *
397  * Does not change the reference count to the engine (we don't use this
398  * particular OpenSSL feature).
399  *
400  * If the engine is not found in the current engine tree and auto_init
401  * if true then it will be initialised with no pre or post ctrls.
402  *
403  * @note Errors should be retrieved with fr_strerror().
404  *
405  * @param[out] e_out The engine that was just initialised.
406  * The caller must not free/finish this engine
407  * it will be called up when the server exits.
408  * @param[in] id Engine identifier. This is usually identifier
409  * that matches OpenSSL's engine ID e.g. "pkcs11".
410  * @param[in] instance Instance identifier for a given engine.
411  * This is useful for "dynamic" engines, i.e. ones
412  * OpenSSL dl loads.
413  * @param[in] auto_init If the engine hasn't already been initialised
414  * auto-initialise it now, with no pre or post
415  * ctrls.
416  * @return
417  * - 0 on success.
418  * - -1 on failure.
419  */
420 int fr_tls_engine(ENGINE **e_out, char const *id, char const *instance, bool auto_init)
421 {
422  tls_engine_t *found = NULL;
423 
424  if (!tls_engines) {
425  if (!auto_init) {
426  not_init:
427  fr_strerror_printf("engine %s%s%s%snot initialised", id,
428  instance ? " (" : "",
429  instance ? instance : "",
430  instance ? ") " : "");
431  return -1;
432  }
433 
434  do_init:
435  return fr_tls_engine_init(e_out, id, instance, NULL, NULL);
436  }
437 
438 
439  found = fr_rb_find(tls_engines, &(tls_engine_t){ .id = id, .instance = instance });
440  if (!found) {
441  if (!auto_init) goto not_init;
442  goto do_init;
443  }
444 
445  *e_out = found->e;
446  return 0;
447 }
448 
449 /** Should be called after any engine configuration has been completed
450  *
451  */
452 void fr_tls_engine_load_builtin(void)
453 {
454  ENGINE_load_builtin_engines(); /* Needed to load AES-NI engine (also loads rdrand, boo) */
455 
456  /*
457  * Mitigate against CrossTalk (CVE-2020-0543)
458  */
459  if (!tls_engines || !fr_rb_find(tls_engines, &(tls_engine_t){ .id = "rdrand" })) {
460  ENGINE *rand_engine;
461 
462  ENGINE_register_all_RAND(); /* Give rand engines a chance to register */
463 
464  /*
465  * If OpenSSL settled on Intel's rdrand
466  * unregister it and unload rdrand.
467  */
468  rand_engine = ENGINE_get_default_RAND();
469  if (rand_engine && (strcmp(ENGINE_get_id(rand_engine), "rdrand") == 0)) {
470  ENGINE_unregister_RAND(rand_engine);
471  ENGINE_finish(rand_engine); /* Unload rdrand */
472  }
473  }
474  ENGINE_register_all_complete();
475 }
476 
477 /** Free any engines we've loaded
478  *
479  */
480 void fr_tls_engine_free_all(void)
481 {
482  TALLOC_FREE(tls_engines);
483 
484 #if OPENSSL_VERSION_NUMBER < 0x10100000L
485  /*
486  * Free any lingering memory
487  * OpenSSL man pages say to do this.
488  */
489  ENGINE_cleanup();
490 #endif
491 }
492 
493 #endif
494 #endif
int n
Definition: acutest.h:577
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define USES_APPLE_DEPRECATED_API
Definition: build.h:468
#define RCSID(id)
Definition: build.h:481
#define CMP(_a, _b)
Same as CMP_PREFER_SMALLER use when you don't really care about ordering, you just want an ordering.
Definition: build.h:110
#define unlikely(_x)
Definition: build.h:379
static fr_slen_t in
Definition: dict.h:821
static void * fr_dlist_next(fr_dlist_head_t const *list_head, void const *ptr)
Get the next item in a list.
Definition: dlist.h:555
static int fr_dlist_insert_tail(fr_dlist_head_t *list_head, void *ptr)
Insert an item into the tail of a list.
Definition: dlist.h:378
#define fr_dlist_talloc_init(_head, _type, _field)
Initialise the head structure of a doubly linked list.
Definition: dlist.h:275
talloc_free(reap)
@ FR_TYPE_INT32
32 Bit signed integer.
Definition: merged_model.c:105
#define fr_rb_inline_alloc(_ctx, _type, _field, _data_cmp, _data_free)
Allocs a red black tree.
Definition: rb.h:271
void * fr_rb_find(fr_rb_tree_t const *tree, void const *data)
The main red black tree structure.
Definition: rb.h:73
static char const * name
char * talloc_typed_strdup(TALLOC_CTX *ctx, char const *p)
Call talloc_strdup, setting the type on the new chunk correctly.
Definition: talloc.c:445
void talloc_free_data(void *data)
A wrapper that can be passed to tree or hash alloc functions that take a fr_free_t.
Definition: talloc.c:41
#define talloc_get_type_abort_const
Definition: talloc.h:282
#define fr_strerror_printf(_fmt,...)
Log to thread local error buffer.
Definition: strerror.h:64
#define fr_strerror_printf_push(_fmt,...)
Add a message to an existing stack of messages at the tail.
Definition: strerror.h:84
#define fr_strerror_const_push(_msg)
Definition: strerror.h:227
#define fr_strerror_const(_msg)
Definition: strerror.h:223
int fr_value_box_cast(TALLOC_CTX *ctx, fr_value_box_t *dst, fr_type_t dst_type, fr_dict_attr_t const *dst_enumv, fr_value_box_t const *src)
Convert one type of fr_value_box_t to another.
Definition: value.c:3352
#define fr_box_strvalue(_val)
Definition: value.h:285
int nonnull(2, 5))