1 | /* |
2 | * Copyright 2002-2018 The OpenSSL Project Authors. All Rights Reserved. |
3 | * |
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | * this file except in compliance with the License. You can obtain a copy |
6 | * in the file LICENSE in the source distribution or at |
7 | * https://www.openssl.org/source/license.html |
8 | */ |
9 | |
10 | #include "internal/cryptlib.h" |
11 | #include <stdio.h> |
12 | #include <ctype.h> |
13 | #include <openssl/crypto.h> |
14 | #include "internal/conf.h" |
15 | #include "internal/dso.h" |
16 | #include <openssl/x509.h> |
17 | #include <openssl/trace.h> |
18 | |
19 | #define DSO_mod_init_name "OPENSSL_init" |
20 | #define DSO_mod_finish_name "OPENSSL_finish" |
21 | |
22 | /* |
23 | * This structure contains a data about supported modules. entries in this |
24 | * table correspond to either dynamic or static modules. |
25 | */ |
26 | |
27 | struct conf_module_st { |
28 | /* DSO of this module or NULL if static */ |
29 | DSO *dso; |
30 | /* Name of the module */ |
31 | char *name; |
32 | /* Init function */ |
33 | conf_init_func *init; |
34 | /* Finish function */ |
35 | conf_finish_func *finish; |
36 | /* Number of successfully initialized modules */ |
37 | int links; |
38 | void *usr_data; |
39 | }; |
40 | |
41 | /* |
42 | * This structure contains information about modules that have been |
43 | * successfully initialized. There may be more than one entry for a given |
44 | * module. |
45 | */ |
46 | |
47 | struct conf_imodule_st { |
48 | CONF_MODULE *pmod; |
49 | char *name; |
50 | char *value; |
51 | unsigned long flags; |
52 | void *usr_data; |
53 | }; |
54 | |
55 | static STACK_OF(CONF_MODULE) *supported_modules = NULL; |
56 | static STACK_OF(CONF_IMODULE) *initialized_modules = NULL; |
57 | |
58 | static void module_free(CONF_MODULE *md); |
59 | static void module_finish(CONF_IMODULE *imod); |
60 | static int module_run(const CONF *cnf, const char *name, const char *value, |
61 | unsigned long flags); |
62 | static CONF_MODULE *module_add(DSO *dso, const char *name, |
63 | conf_init_func *ifunc, |
64 | conf_finish_func *ffunc); |
65 | static CONF_MODULE *module_find(const char *name); |
66 | static int module_init(CONF_MODULE *pmod, const char *name, const char *value, |
67 | const CONF *cnf); |
68 | static CONF_MODULE *module_load_dso(const CONF *cnf, const char *name, |
69 | const char *value); |
70 | |
71 | /* Main function: load modules from a CONF structure */ |
72 | |
73 | int CONF_modules_load(const CONF *cnf, const char *appname, |
74 | unsigned long flags) |
75 | { |
76 | STACK_OF(CONF_VALUE) *values; |
77 | CONF_VALUE *vl; |
78 | char *vsection = NULL; |
79 | |
80 | int ret, i; |
81 | |
82 | if (!cnf) |
83 | return 1; |
84 | |
85 | if (appname) |
86 | vsection = NCONF_get_string(cnf, NULL, appname); |
87 | |
88 | if (!appname || (!vsection && (flags & CONF_MFLAGS_DEFAULT_SECTION))) |
89 | vsection = NCONF_get_string(cnf, NULL, "openssl_conf" ); |
90 | |
91 | if (!vsection) { |
92 | ERR_clear_error(); |
93 | return 1; |
94 | } |
95 | |
96 | OSSL_TRACE1(CONF, "Configuration in section %s\n" , vsection); |
97 | values = NCONF_get_section(cnf, vsection); |
98 | |
99 | if (!values) |
100 | return 0; |
101 | |
102 | for (i = 0; i < sk_CONF_VALUE_num(values); i++) { |
103 | vl = sk_CONF_VALUE_value(values, i); |
104 | ret = module_run(cnf, vl->name, vl->value, flags); |
105 | OSSL_TRACE3(CONF, "Running module %s (%s) returned %d\n" , |
106 | vl->name, vl->value, ret); |
107 | if (ret <= 0) |
108 | if (!(flags & CONF_MFLAGS_IGNORE_ERRORS)) |
109 | return ret; |
110 | } |
111 | |
112 | return 1; |
113 | |
114 | } |
115 | |
116 | int CONF_modules_load_file(const char *filename, const char *appname, |
117 | unsigned long flags) |
118 | { |
119 | char *file = NULL; |
120 | CONF *conf = NULL; |
121 | int ret = 0; |
122 | conf = NCONF_new(NULL); |
123 | if (conf == NULL) |
124 | goto err; |
125 | |
126 | if (filename == NULL) { |
127 | file = CONF_get1_default_config_file(); |
128 | if (!file) |
129 | goto err; |
130 | } else |
131 | file = (char *)filename; |
132 | |
133 | if (NCONF_load(conf, file, NULL) <= 0) { |
134 | if ((flags & CONF_MFLAGS_IGNORE_MISSING_FILE) && |
135 | (ERR_GET_REASON(ERR_peek_last_error()) == CONF_R_NO_SUCH_FILE)) { |
136 | ERR_clear_error(); |
137 | ret = 1; |
138 | } |
139 | goto err; |
140 | } |
141 | |
142 | ret = CONF_modules_load(conf, appname, flags); |
143 | |
144 | err: |
145 | if (filename == NULL) |
146 | OPENSSL_free(file); |
147 | NCONF_free(conf); |
148 | |
149 | if (flags & CONF_MFLAGS_IGNORE_RETURN_CODES) |
150 | return 1; |
151 | |
152 | return ret; |
153 | } |
154 | |
155 | static int module_run(const CONF *cnf, const char *name, const char *value, |
156 | unsigned long flags) |
157 | { |
158 | CONF_MODULE *md; |
159 | int ret; |
160 | |
161 | md = module_find(name); |
162 | |
163 | /* Module not found: try to load DSO */ |
164 | if (!md && !(flags & CONF_MFLAGS_NO_DSO)) |
165 | md = module_load_dso(cnf, name, value); |
166 | |
167 | if (!md) { |
168 | if (!(flags & CONF_MFLAGS_SILENT)) { |
169 | CONFerr(CONF_F_MODULE_RUN, CONF_R_UNKNOWN_MODULE_NAME); |
170 | ERR_add_error_data(2, "module=" , name); |
171 | } |
172 | return -1; |
173 | } |
174 | |
175 | ret = module_init(md, name, value, cnf); |
176 | |
177 | if (ret <= 0) { |
178 | if (!(flags & CONF_MFLAGS_SILENT)) { |
179 | char rcode[DECIMAL_SIZE(ret) + 1]; |
180 | |
181 | CONFerr(CONF_F_MODULE_RUN, CONF_R_MODULE_INITIALIZATION_ERROR); |
182 | BIO_snprintf(rcode, sizeof(rcode), "%-8d" , ret); |
183 | ERR_add_error_data(6, "module=" , name, ", value=" , value, |
184 | ", retcode=" , rcode); |
185 | } |
186 | } |
187 | |
188 | return ret; |
189 | } |
190 | |
191 | /* Load a module from a DSO */ |
192 | static CONF_MODULE *module_load_dso(const CONF *cnf, |
193 | const char *name, const char *value) |
194 | { |
195 | DSO *dso = NULL; |
196 | conf_init_func *ifunc; |
197 | conf_finish_func *ffunc; |
198 | const char *path = NULL; |
199 | int errcode = 0; |
200 | CONF_MODULE *md; |
201 | |
202 | /* Look for alternative path in module section */ |
203 | path = NCONF_get_string(cnf, value, "path" ); |
204 | if (path == NULL) { |
205 | ERR_clear_error(); |
206 | path = name; |
207 | } |
208 | dso = DSO_load(NULL, path, NULL, 0); |
209 | if (dso == NULL) { |
210 | errcode = CONF_R_ERROR_LOADING_DSO; |
211 | goto err; |
212 | } |
213 | ifunc = (conf_init_func *)DSO_bind_func(dso, DSO_mod_init_name); |
214 | if (ifunc == NULL) { |
215 | errcode = CONF_R_MISSING_INIT_FUNCTION; |
216 | goto err; |
217 | } |
218 | ffunc = (conf_finish_func *)DSO_bind_func(dso, DSO_mod_finish_name); |
219 | /* All OK, add module */ |
220 | md = module_add(dso, name, ifunc, ffunc); |
221 | |
222 | if (md == NULL) |
223 | goto err; |
224 | |
225 | return md; |
226 | |
227 | err: |
228 | DSO_free(dso); |
229 | CONFerr(CONF_F_MODULE_LOAD_DSO, errcode); |
230 | ERR_add_error_data(4, "module=" , name, ", path=" , path); |
231 | return NULL; |
232 | } |
233 | |
234 | /* add module to list */ |
235 | static CONF_MODULE *module_add(DSO *dso, const char *name, |
236 | conf_init_func *ifunc, conf_finish_func *ffunc) |
237 | { |
238 | CONF_MODULE *tmod = NULL; |
239 | if (supported_modules == NULL) |
240 | supported_modules = sk_CONF_MODULE_new_null(); |
241 | if (supported_modules == NULL) |
242 | return NULL; |
243 | if ((tmod = OPENSSL_zalloc(sizeof(*tmod))) == NULL) { |
244 | CONFerr(CONF_F_MODULE_ADD, ERR_R_MALLOC_FAILURE); |
245 | return NULL; |
246 | } |
247 | |
248 | tmod->dso = dso; |
249 | tmod->name = OPENSSL_strdup(name); |
250 | tmod->init = ifunc; |
251 | tmod->finish = ffunc; |
252 | if (tmod->name == NULL) { |
253 | OPENSSL_free(tmod); |
254 | return NULL; |
255 | } |
256 | |
257 | if (!sk_CONF_MODULE_push(supported_modules, tmod)) { |
258 | OPENSSL_free(tmod->name); |
259 | OPENSSL_free(tmod); |
260 | return NULL; |
261 | } |
262 | |
263 | return tmod; |
264 | } |
265 | |
266 | /* |
267 | * Find a module from the list. We allow module names of the form |
268 | * modname.XXXX to just search for modname to allow the same module to be |
269 | * initialized more than once. |
270 | */ |
271 | |
272 | static CONF_MODULE *module_find(const char *name) |
273 | { |
274 | CONF_MODULE *tmod; |
275 | int i, nchar; |
276 | char *p; |
277 | p = strrchr(name, '.'); |
278 | |
279 | if (p) |
280 | nchar = p - name; |
281 | else |
282 | nchar = strlen(name); |
283 | |
284 | for (i = 0; i < sk_CONF_MODULE_num(supported_modules); i++) { |
285 | tmod = sk_CONF_MODULE_value(supported_modules, i); |
286 | if (strncmp(tmod->name, name, nchar) == 0) |
287 | return tmod; |
288 | } |
289 | |
290 | return NULL; |
291 | |
292 | } |
293 | |
294 | /* initialize a module */ |
295 | static int module_init(CONF_MODULE *pmod, const char *name, const char *value, |
296 | const CONF *cnf) |
297 | { |
298 | int ret = 1; |
299 | int init_called = 0; |
300 | CONF_IMODULE *imod = NULL; |
301 | |
302 | /* Otherwise add initialized module to list */ |
303 | imod = OPENSSL_malloc(sizeof(*imod)); |
304 | if (imod == NULL) |
305 | goto err; |
306 | |
307 | imod->pmod = pmod; |
308 | imod->name = OPENSSL_strdup(name); |
309 | imod->value = OPENSSL_strdup(value); |
310 | imod->usr_data = NULL; |
311 | |
312 | if (!imod->name || !imod->value) |
313 | goto memerr; |
314 | |
315 | /* Try to initialize module */ |
316 | if (pmod->init) { |
317 | ret = pmod->init(imod, cnf); |
318 | init_called = 1; |
319 | /* Error occurred, exit */ |
320 | if (ret <= 0) |
321 | goto err; |
322 | } |
323 | |
324 | if (initialized_modules == NULL) { |
325 | initialized_modules = sk_CONF_IMODULE_new_null(); |
326 | if (!initialized_modules) { |
327 | CONFerr(CONF_F_MODULE_INIT, ERR_R_MALLOC_FAILURE); |
328 | goto err; |
329 | } |
330 | } |
331 | |
332 | if (!sk_CONF_IMODULE_push(initialized_modules, imod)) { |
333 | CONFerr(CONF_F_MODULE_INIT, ERR_R_MALLOC_FAILURE); |
334 | goto err; |
335 | } |
336 | |
337 | pmod->links++; |
338 | |
339 | return ret; |
340 | |
341 | err: |
342 | |
343 | /* We've started the module so we'd better finish it */ |
344 | if (pmod->finish && init_called) |
345 | pmod->finish(imod); |
346 | |
347 | memerr: |
348 | if (imod) { |
349 | OPENSSL_free(imod->name); |
350 | OPENSSL_free(imod->value); |
351 | OPENSSL_free(imod); |
352 | } |
353 | |
354 | return -1; |
355 | |
356 | } |
357 | |
358 | /* |
359 | * Unload any dynamic modules that have a link count of zero: i.e. have no |
360 | * active initialized modules. If 'all' is set then all modules are unloaded |
361 | * including static ones. |
362 | */ |
363 | |
364 | void CONF_modules_unload(int all) |
365 | { |
366 | int i; |
367 | CONF_MODULE *md; |
368 | CONF_modules_finish(); |
369 | /* unload modules in reverse order */ |
370 | for (i = sk_CONF_MODULE_num(supported_modules) - 1; i >= 0; i--) { |
371 | md = sk_CONF_MODULE_value(supported_modules, i); |
372 | /* If static or in use and 'all' not set ignore it */ |
373 | if (((md->links > 0) || !md->dso) && !all) |
374 | continue; |
375 | /* Since we're working in reverse this is OK */ |
376 | (void)sk_CONF_MODULE_delete(supported_modules, i); |
377 | module_free(md); |
378 | } |
379 | if (sk_CONF_MODULE_num(supported_modules) == 0) { |
380 | sk_CONF_MODULE_free(supported_modules); |
381 | supported_modules = NULL; |
382 | } |
383 | } |
384 | |
385 | /* unload a single module */ |
386 | static void module_free(CONF_MODULE *md) |
387 | { |
388 | DSO_free(md->dso); |
389 | OPENSSL_free(md->name); |
390 | OPENSSL_free(md); |
391 | } |
392 | |
393 | /* finish and free up all modules instances */ |
394 | |
395 | void CONF_modules_finish(void) |
396 | { |
397 | CONF_IMODULE *imod; |
398 | while (sk_CONF_IMODULE_num(initialized_modules) > 0) { |
399 | imod = sk_CONF_IMODULE_pop(initialized_modules); |
400 | module_finish(imod); |
401 | } |
402 | sk_CONF_IMODULE_free(initialized_modules); |
403 | initialized_modules = NULL; |
404 | } |
405 | |
406 | /* finish a module instance */ |
407 | |
408 | static void module_finish(CONF_IMODULE *imod) |
409 | { |
410 | if (!imod) |
411 | return; |
412 | if (imod->pmod->finish) |
413 | imod->pmod->finish(imod); |
414 | imod->pmod->links--; |
415 | OPENSSL_free(imod->name); |
416 | OPENSSL_free(imod->value); |
417 | OPENSSL_free(imod); |
418 | } |
419 | |
420 | /* Add a static module to OpenSSL */ |
421 | |
422 | int CONF_module_add(const char *name, conf_init_func *ifunc, |
423 | conf_finish_func *ffunc) |
424 | { |
425 | if (module_add(NULL, name, ifunc, ffunc)) |
426 | return 1; |
427 | else |
428 | return 0; |
429 | } |
430 | |
431 | void conf_modules_free_int(void) |
432 | { |
433 | CONF_modules_finish(); |
434 | CONF_modules_unload(1); |
435 | } |
436 | |
437 | /* Utility functions */ |
438 | |
439 | const char *CONF_imodule_get_name(const CONF_IMODULE *md) |
440 | { |
441 | return md->name; |
442 | } |
443 | |
444 | const char *CONF_imodule_get_value(const CONF_IMODULE *md) |
445 | { |
446 | return md->value; |
447 | } |
448 | |
449 | void *CONF_imodule_get_usr_data(const CONF_IMODULE *md) |
450 | { |
451 | return md->usr_data; |
452 | } |
453 | |
454 | void CONF_imodule_set_usr_data(CONF_IMODULE *md, void *usr_data) |
455 | { |
456 | md->usr_data = usr_data; |
457 | } |
458 | |
459 | CONF_MODULE *CONF_imodule_get_module(const CONF_IMODULE *md) |
460 | { |
461 | return md->pmod; |
462 | } |
463 | |
464 | unsigned long CONF_imodule_get_flags(const CONF_IMODULE *md) |
465 | { |
466 | return md->flags; |
467 | } |
468 | |
469 | void CONF_imodule_set_flags(CONF_IMODULE *md, unsigned long flags) |
470 | { |
471 | md->flags = flags; |
472 | } |
473 | |
474 | void *CONF_module_get_usr_data(CONF_MODULE *pmod) |
475 | { |
476 | return pmod->usr_data; |
477 | } |
478 | |
479 | void CONF_module_set_usr_data(CONF_MODULE *pmod, void *usr_data) |
480 | { |
481 | pmod->usr_data = usr_data; |
482 | } |
483 | |
484 | /* Return default config file name */ |
485 | |
486 | char *CONF_get1_default_config_file(void) |
487 | { |
488 | char *file, *sep = "" ; |
489 | int len; |
490 | |
491 | if ((file = ossl_safe_getenv("OPENSSL_CONF" )) != NULL) |
492 | return OPENSSL_strdup(file); |
493 | |
494 | len = strlen(X509_get_default_cert_area()); |
495 | #ifndef OPENSSL_SYS_VMS |
496 | len++; |
497 | sep = "/" ; |
498 | #endif |
499 | len += strlen(OPENSSL_CONF); |
500 | |
501 | file = OPENSSL_malloc(len + 1); |
502 | |
503 | if (file == NULL) |
504 | return NULL; |
505 | BIO_snprintf(file, len + 1, "%s%s%s" , X509_get_default_cert_area(), |
506 | sep, OPENSSL_CONF); |
507 | |
508 | return file; |
509 | } |
510 | |
511 | /* |
512 | * This function takes a list separated by 'sep' and calls the callback |
513 | * function giving the start and length of each member optionally stripping |
514 | * leading and trailing whitespace. This can be used to parse comma separated |
515 | * lists for example. |
516 | */ |
517 | |
518 | int CONF_parse_list(const char *list_, int sep, int nospc, |
519 | int (*list_cb) (const char *elem, int len, void *usr), |
520 | void *arg) |
521 | { |
522 | int ret; |
523 | const char *lstart, *tmpend, *p; |
524 | |
525 | if (list_ == NULL) { |
526 | CONFerr(CONF_F_CONF_PARSE_LIST, CONF_R_LIST_CANNOT_BE_NULL); |
527 | return 0; |
528 | } |
529 | |
530 | lstart = list_; |
531 | for (;;) { |
532 | if (nospc) { |
533 | while (*lstart && isspace((unsigned char)*lstart)) |
534 | lstart++; |
535 | } |
536 | p = strchr(lstart, sep); |
537 | if (p == lstart || *lstart == '\0') |
538 | ret = list_cb(NULL, 0, arg); |
539 | else { |
540 | if (p) |
541 | tmpend = p - 1; |
542 | else |
543 | tmpend = lstart + strlen(lstart) - 1; |
544 | if (nospc) { |
545 | while (isspace((unsigned char)*tmpend)) |
546 | tmpend--; |
547 | } |
548 | ret = list_cb(lstart, tmpend - lstart + 1, arg); |
549 | } |
550 | if (ret <= 0) |
551 | return ret; |
552 | if (p == NULL) |
553 | return 1; |
554 | lstart = p + 1; |
555 | } |
556 | } |
557 | |