/src/p11-kit/p11-kit/conf.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (c) 2005 Stefan Walter |
3 | | * Copyright (c) 2011 Collabora Ltd. |
4 | | * |
5 | | * Redistribution and use in source and binary forms, with or without |
6 | | * modification, are permitted provided that the following conditions |
7 | | * are met: |
8 | | * |
9 | | * * Redistributions of source code must retain the above |
10 | | * copyright notice, this list of conditions and the |
11 | | * following disclaimer. |
12 | | * * Redistributions in binary form must reproduce the |
13 | | * above copyright notice, this list of conditions and |
14 | | * the following disclaimer in the documentation and/or |
15 | | * other materials provided with the distribution. |
16 | | * * The names of contributors to this software may not be |
17 | | * used to endorse or promote products derived from this |
18 | | * software without specific prior written permission. |
19 | | * |
20 | | * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
21 | | * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
22 | | * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
23 | | * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
24 | | * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
25 | | * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
26 | | * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
27 | | * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
28 | | * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
29 | | * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF |
30 | | * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH |
31 | | * DAMAGE. |
32 | | * |
33 | | * |
34 | | * CONTRIBUTORS |
35 | | * Stef Walter <stef@memberwebs.com> |
36 | | */ |
37 | | |
38 | | #include "config.h" |
39 | | |
40 | | #include "conf.h" |
41 | 0 | #define P11_DEBUG_FLAG P11_DEBUG_CONF |
42 | | #include "debug.h" |
43 | | #include "lexer.h" |
44 | | #include "message.h" |
45 | | #include "path.h" |
46 | | #include "private.h" |
47 | | |
48 | | #include <sys/param.h> |
49 | | #include <sys/stat.h> |
50 | | #include <sys/types.h> |
51 | | #ifdef OS_UNIX |
52 | | #include <unistd.h> |
53 | | #endif |
54 | | |
55 | | #include <assert.h> |
56 | | #include <ctype.h> |
57 | | #include <dirent.h> |
58 | | #include <errno.h> |
59 | | #include <stdio.h> |
60 | | #include <stdlib.h> |
61 | | #include <string.h> |
62 | | |
63 | | #ifdef ENABLE_NLS |
64 | | #include <libintl.h> |
65 | 0 | #define _(x) dgettext(PACKAGE_NAME, x) |
66 | | #else |
67 | | #define _(x) (x) |
68 | | #endif |
69 | | |
70 | | /* For testing, when the tests assuming user config are run as root. */ |
71 | | bool p11_conf_force_user_config = false; |
72 | | |
73 | | static int |
74 | | strequal (const char *one, const char *two) |
75 | 0 | { |
76 | 0 | return strcmp (one, two) == 0; |
77 | 0 | } |
78 | | |
79 | | /* ----------------------------------------------------------------------------- |
80 | | * CONFIG PARSER |
81 | | */ |
82 | | |
83 | | bool |
84 | | _p11_conf_merge_defaults (p11_dict *map, |
85 | | p11_dict *defaults) |
86 | 0 | { |
87 | 0 | p11_dictiter iter; |
88 | 0 | void *key; |
89 | 0 | void *value; |
90 | |
|
91 | 0 | p11_dict_iterate (defaults, &iter); |
92 | 0 | while (p11_dict_next (&iter, &key, &value)) { |
93 | | /* Only override if not set */ |
94 | 0 | if (p11_dict_get (map, key)) |
95 | 0 | continue; |
96 | 0 | key = strdup (key); |
97 | 0 | return_val_if_fail (key != NULL, false); |
98 | 0 | value = strdup (value); |
99 | 0 | return_val_if_fail (key != NULL, false); |
100 | 0 | if (!p11_dict_set (map, key, value)) |
101 | 0 | return_val_if_reached (false); |
102 | 0 | } |
103 | | |
104 | 0 | return true; |
105 | 0 | } |
106 | | |
107 | | p11_dict * |
108 | | _p11_conf_parse_file (const char* filename, |
109 | | struct stat *sb, |
110 | | int flags) |
111 | 0 | { |
112 | 0 | p11_dict *map = NULL; |
113 | 0 | void *data; |
114 | 0 | p11_lexer lexer; |
115 | 0 | bool failed = false; |
116 | 0 | size_t length; |
117 | 0 | p11_mmap *mmap; |
118 | 0 | int error; |
119 | |
|
120 | 0 | assert (filename); |
121 | | |
122 | 0 | p11_debug ("reading config file: %s", filename); |
123 | |
|
124 | 0 | mmap = p11_mmap_open (filename, sb, &data, &length); |
125 | 0 | if (mmap == NULL) { |
126 | 0 | error = errno; |
127 | 0 | if ((flags & CONF_IGNORE_MISSING) && |
128 | 0 | (error == ENOENT || error == ENOTDIR)) { |
129 | 0 | p11_debug ("config file does not exist"); |
130 | |
|
131 | 0 | } else if ((flags & CONF_IGNORE_ACCESS_DENIED) && |
132 | 0 | (error == EPERM || error == EACCES)) { |
133 | 0 | p11_debug ("config file is inaccessible"); |
134 | |
|
135 | 0 | } else { |
136 | 0 | p11_message_err (error, "couldn't open config file: %s", filename); |
137 | 0 | errno = error; |
138 | 0 | return NULL; |
139 | 0 | } |
140 | 0 | } |
141 | | |
142 | 0 | map = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, free, free); |
143 | 0 | return_val_if_fail (map != NULL, NULL); |
144 | | |
145 | | /* Empty config fall through above */ |
146 | 0 | if (mmap == NULL) |
147 | 0 | return map; |
148 | | |
149 | 0 | p11_lexer_init (&lexer, filename, data, length); |
150 | 0 | while (p11_lexer_next (&lexer, &failed)) { |
151 | 0 | switch (lexer.tok_type) { |
152 | 0 | case TOK_FIELD: |
153 | 0 | p11_debug ("config value: %s: %s", lexer.tok.field.name, |
154 | 0 | lexer.tok.field.value); |
155 | 0 | if (!p11_dict_set (map, lexer.tok.field.name, lexer.tok.field.value)) |
156 | 0 | return_val_if_reached (NULL); |
157 | 0 | lexer.tok.field.name = NULL; |
158 | 0 | lexer.tok.field.value = NULL; |
159 | 0 | break; |
160 | 0 | case TOK_PEM: |
161 | 0 | p11_message (_("%s: unexpected pem block"), filename); |
162 | 0 | failed = true; |
163 | 0 | break; |
164 | 0 | case TOK_SECTION: |
165 | 0 | p11_message (_("%s: unexpected section header"), filename); |
166 | 0 | failed = true; |
167 | 0 | break; |
168 | 0 | case TOK_EOF: |
169 | 0 | assert_not_reached (); |
170 | 0 | break; |
171 | 0 | } |
172 | | |
173 | 0 | if (failed) |
174 | 0 | break; |
175 | 0 | } |
176 | | |
177 | 0 | p11_lexer_done (&lexer); |
178 | 0 | p11_mmap_close (mmap); |
179 | |
|
180 | 0 | if (failed) { |
181 | 0 | p11_dict_free (map); |
182 | 0 | map = NULL; |
183 | 0 | errno = EINVAL; |
184 | 0 | } |
185 | |
|
186 | 0 | return map; |
187 | 0 | } |
188 | | |
189 | | static int |
190 | | user_config_mode (p11_dict *config, |
191 | | int defmode) |
192 | 0 | { |
193 | 0 | const char *mode; |
194 | | |
195 | | /* Whether we should use or override from user directory */ |
196 | 0 | mode = p11_dict_get (config, "user-config"); |
197 | 0 | if (mode == NULL) { |
198 | 0 | return defmode; |
199 | 0 | } else if (strequal (mode, "none")) { |
200 | 0 | return CONF_USER_NONE; |
201 | 0 | } else if (strequal (mode, "merge")) { |
202 | 0 | return CONF_USER_MERGE; |
203 | 0 | } else if (strequal (mode, "only")) { |
204 | 0 | return CONF_USER_ONLY; |
205 | 0 | } else if (strequal (mode, "override")) { |
206 | 0 | return CONF_USER_ONLY; |
207 | 0 | } else { |
208 | 0 | p11_message (_("invalid mode for 'user-config': %s"), mode); |
209 | 0 | return CONF_USER_INVALID; |
210 | 0 | } |
211 | 0 | } |
212 | | |
213 | | p11_dict * |
214 | | _p11_conf_load_globals (const char *system_conf, const char *user_conf, |
215 | | int *user_mode) |
216 | 0 | { |
217 | 0 | p11_dict *config = NULL; |
218 | 0 | p11_dict *uconfig = NULL; |
219 | 0 | p11_dict *result = NULL; |
220 | 0 | char *path = NULL; |
221 | 0 | int error = 0; |
222 | 0 | int flags; |
223 | 0 | int mode; |
224 | | |
225 | | /* |
226 | | * This loads the system and user configs. This depends on the user-config |
227 | | * value in both the system and user configs. A bit more complex than |
228 | | * you might imagine, since user-config can be set to 'none' in the |
229 | | * user configuration, essentially turning itself off. |
230 | | */ |
231 | | |
232 | | /* Load the main configuration */ |
233 | 0 | config = _p11_conf_parse_file (system_conf, NULL, CONF_IGNORE_MISSING); |
234 | 0 | if (!config) |
235 | 0 | goto finished; |
236 | | |
237 | | /* Whether we should use or override from user directory */ |
238 | 0 | mode = user_config_mode (config, CONF_USER_MERGE); |
239 | 0 | if (mode == CONF_USER_INVALID) { |
240 | 0 | error = EINVAL; |
241 | 0 | goto finished; |
242 | 0 | } |
243 | | |
244 | 0 | if (mode != CONF_USER_NONE && !p11_conf_force_user_config) { |
245 | 0 | if (getauxval (AT_SECURE)) { |
246 | 0 | p11_debug ("skipping user config in setuid or setgid program"); |
247 | 0 | mode = CONF_USER_NONE; |
248 | 0 | #ifdef OS_UNIX |
249 | 0 | } else if (getuid () == 0) { |
250 | 0 | p11_debug ("skipping user config in program running as root"); |
251 | 0 | mode = CONF_USER_NONE; |
252 | 0 | #endif |
253 | 0 | } else if (secure_getenv ("P11_KIT_NO_USER_CONFIG")) { |
254 | 0 | p11_debug ("skipping user config due to P11_NO_USER_CONFIG"); |
255 | 0 | mode = CONF_USER_NONE; |
256 | 0 | } |
257 | 0 | } |
258 | |
|
259 | 0 | if (mode != CONF_USER_NONE) { |
260 | 0 | path = p11_path_expand (user_conf); |
261 | 0 | if (!path) { |
262 | 0 | error = errno; |
263 | 0 | goto finished; |
264 | 0 | } |
265 | | |
266 | | /* Load up the user configuration, ignore selinux denying us access */ |
267 | 0 | flags = CONF_IGNORE_MISSING | CONF_IGNORE_ACCESS_DENIED; |
268 | 0 | uconfig = _p11_conf_parse_file (path, NULL, flags); |
269 | 0 | if (!uconfig) { |
270 | 0 | error = errno; |
271 | 0 | goto finished; |
272 | 0 | } |
273 | | |
274 | | /* Figure out what the user mode is, defaulting to system mode if not set */ |
275 | 0 | mode = user_config_mode (uconfig, mode); |
276 | 0 | if (mode == CONF_USER_INVALID) { |
277 | 0 | error = EINVAL; |
278 | 0 | goto finished; |
279 | 0 | } |
280 | | |
281 | | /* If merging, then supplement user config with system values */ |
282 | 0 | if (mode == CONF_USER_MERGE) { |
283 | 0 | if (!_p11_conf_merge_defaults (uconfig, config)) { |
284 | 0 | error = errno; |
285 | 0 | goto finished; |
286 | 0 | } |
287 | 0 | } |
288 | | |
289 | | /* If user config valid at all, then replace system with what we have */ |
290 | 0 | if (mode != CONF_USER_NONE) { |
291 | 0 | p11_dict_free (config); |
292 | 0 | config = uconfig; |
293 | 0 | uconfig = NULL; |
294 | 0 | } |
295 | 0 | } |
296 | | |
297 | 0 | if (user_mode) |
298 | 0 | *user_mode = mode; |
299 | |
|
300 | 0 | result = config; |
301 | 0 | config = NULL; |
302 | |
|
303 | 0 | finished: |
304 | 0 | free (path); |
305 | 0 | p11_dict_free (config); |
306 | 0 | p11_dict_free (uconfig); |
307 | 0 | errno = error; |
308 | 0 | return result; |
309 | 0 | } |
310 | | |
311 | | static char * |
312 | | calc_name_from_filename (const char *fname) |
313 | 0 | { |
314 | | /* We eventually want to settle on .module */ |
315 | 0 | static const char *const suffix = ".module"; |
316 | 0 | static const size_t suffix_len = 7; |
317 | 0 | const char *c = fname; |
318 | 0 | size_t fname_len; |
319 | 0 | size_t name_len; |
320 | 0 | char *name; |
321 | |
|
322 | 0 | assert (fname); |
323 | | |
324 | | /* Make sure the filename starts with an alphanumeric */ |
325 | 0 | if (!isalnum(*c)) |
326 | 0 | return NULL; |
327 | 0 | ++c; |
328 | | |
329 | | /* Only allow alnum, _, -, and . */ |
330 | 0 | while (*c) { |
331 | 0 | if (!isalnum(*c) && *c != '_' && *c != '-' && *c != '.') |
332 | 0 | return NULL; |
333 | 0 | ++c; |
334 | 0 | } |
335 | | |
336 | | /* Make sure we have one of the suffixes */ |
337 | 0 | fname_len = strlen (fname); |
338 | 0 | if (suffix_len >= fname_len) |
339 | 0 | return NULL; |
340 | 0 | name_len = (fname_len - suffix_len); |
341 | 0 | if (strcmp (fname + name_len, suffix) != 0) |
342 | 0 | return NULL; |
343 | | |
344 | 0 | name = malloc (name_len + 1); |
345 | 0 | return_val_if_fail (name != NULL, NULL); |
346 | 0 | memcpy (name, fname, name_len); |
347 | 0 | name[name_len] = 0; |
348 | 0 | return name; |
349 | 0 | } |
350 | | |
351 | | static bool |
352 | | load_config_from_file (const char *configfile, |
353 | | struct stat *sb, |
354 | | const char *name, |
355 | | p11_dict *configs, |
356 | | int flags) |
357 | 0 | { |
358 | 0 | p11_dict *config; |
359 | 0 | p11_dict *prev; |
360 | 0 | char *key; |
361 | 0 | int error = 0; |
362 | |
|
363 | 0 | assert (configfile); |
364 | | |
365 | 0 | key = calc_name_from_filename (name); |
366 | 0 | if (key == NULL) { |
367 | 0 | p11_message (_("invalid config filename, will be ignored in the future: %s"), configfile); |
368 | 0 | key = strdup (name); |
369 | 0 | return_val_if_fail (key != NULL, false); |
370 | 0 | } |
371 | | |
372 | 0 | config = _p11_conf_parse_file (configfile, sb, flags); |
373 | 0 | if (!config) { |
374 | 0 | free (key); |
375 | 0 | return false; |
376 | 0 | } |
377 | | |
378 | 0 | prev = p11_dict_get (configs, key); |
379 | 0 | if (prev == NULL) { |
380 | 0 | if (!p11_dict_set (configs, key, config)) |
381 | 0 | return_val_if_reached (false); |
382 | 0 | config = NULL; |
383 | 0 | } else { |
384 | 0 | if (!_p11_conf_merge_defaults (prev, config)) |
385 | 0 | error = errno; |
386 | 0 | free (key); |
387 | 0 | } |
388 | | |
389 | | /* If still set */ |
390 | 0 | p11_dict_free (config); |
391 | |
|
392 | 0 | if (error) { |
393 | 0 | errno = error; |
394 | 0 | return false; |
395 | 0 | } |
396 | | |
397 | 0 | return true; |
398 | 0 | } |
399 | | |
400 | | static bool |
401 | | load_configs_from_directory (const char *directory, |
402 | | p11_dict *configs, |
403 | | int flags) |
404 | 0 | { |
405 | 0 | struct dirent *dp; |
406 | 0 | struct stat st; |
407 | 0 | DIR *dir; |
408 | 0 | int error = 0; |
409 | 0 | bool is_dir; |
410 | 0 | char *path; |
411 | |
|
412 | 0 | p11_debug ("loading module configs in: %s", directory); |
413 | | |
414 | | /* First we load all the modules */ |
415 | 0 | dir = opendir (directory); |
416 | 0 | if (!dir) { |
417 | 0 | error = errno; |
418 | 0 | if ((flags & CONF_IGNORE_MISSING) && |
419 | 0 | (errno == ENOENT || errno == ENOTDIR)) { |
420 | 0 | p11_debug ("module configs do not exist"); |
421 | 0 | return true; |
422 | 0 | } else if ((flags & CONF_IGNORE_ACCESS_DENIED) && |
423 | 0 | (errno == EPERM || errno == EACCES)) { |
424 | 0 | p11_debug ("couldn't list inacessible module configs"); |
425 | 0 | return true; |
426 | 0 | } |
427 | 0 | p11_message_err (error, _("couldn't list directory: %s"), directory); |
428 | 0 | errno = error; |
429 | 0 | return false; |
430 | 0 | } |
431 | | |
432 | 0 | while ((dp = readdir(dir)) != NULL) { |
433 | 0 | path = p11_path_build (directory, dp->d_name, NULL); |
434 | 0 | return_val_if_fail (path != NULL, false); |
435 | | |
436 | 0 | if (stat (path, &st) < 0) { |
437 | 0 | error = errno; |
438 | 0 | p11_message_err (error, _("couldn't stat path: %s"), path); |
439 | 0 | free (path); |
440 | 0 | break; |
441 | 0 | } |
442 | | |
443 | 0 | is_dir = S_ISDIR (st.st_mode); |
444 | |
|
445 | 0 | if (!is_dir && !load_config_from_file (path, &st, dp->d_name, configs, flags)) { |
446 | 0 | error = errno; |
447 | 0 | free (path); |
448 | 0 | break; |
449 | 0 | } |
450 | | |
451 | 0 | free (path); |
452 | 0 | } |
453 | | |
454 | 0 | closedir (dir); |
455 | |
|
456 | 0 | if (error) { |
457 | 0 | errno = error; |
458 | 0 | return false; |
459 | 0 | } |
460 | | |
461 | 0 | return true; |
462 | 0 | } |
463 | | |
464 | | p11_dict * |
465 | | _p11_conf_load_modules (int mode, |
466 | | const char *package_dir, |
467 | | const char *system_dir, |
468 | | const char *user_dir) |
469 | 0 | { |
470 | 0 | p11_dict *configs; |
471 | 0 | char *path; |
472 | 0 | int error = 0; |
473 | 0 | int flags; |
474 | | |
475 | | /* A hash table of name -> config */ |
476 | 0 | configs = p11_dict_new (p11_dict_str_hash, p11_dict_str_equal, |
477 | 0 | free, (p11_destroyer)p11_dict_free); |
478 | | |
479 | | /* Load each user config first, if user config is allowed */ |
480 | 0 | if (mode != CONF_USER_NONE) { |
481 | 0 | flags = CONF_IGNORE_MISSING | CONF_IGNORE_ACCESS_DENIED; |
482 | 0 | path = p11_path_expand (user_dir); |
483 | 0 | if (!path) |
484 | 0 | error = errno; |
485 | 0 | else if (!load_configs_from_directory (path, configs, flags)) |
486 | 0 | error = errno; |
487 | 0 | free (path); |
488 | 0 | if (error != 0) { |
489 | 0 | p11_dict_free (configs); |
490 | 0 | errno = error; |
491 | 0 | return NULL; |
492 | 0 | } |
493 | 0 | } |
494 | | |
495 | | /* |
496 | | * Now unless user config is overriding, load system modules. |
497 | | * Basically if a value for the same config name is not already |
498 | | * loaded above (in the user configs) then they're loaded here. |
499 | | */ |
500 | 0 | if (mode != CONF_USER_ONLY) { |
501 | 0 | flags = CONF_IGNORE_MISSING; |
502 | 0 | if (!load_configs_from_directory (system_dir, configs, flags) || |
503 | 0 | !load_configs_from_directory (package_dir, configs, flags)) { |
504 | 0 | error = errno; |
505 | 0 | p11_dict_free (configs); |
506 | 0 | errno = error; |
507 | 0 | return NULL; |
508 | 0 | } |
509 | 0 | } |
510 | | |
511 | 0 | return configs; |
512 | 0 | } |
513 | | |
514 | | bool |
515 | | _p11_conf_parse_boolean (const char *string, |
516 | | bool default_value) |
517 | 0 | { |
518 | 0 | if (!string) |
519 | 0 | return default_value; |
520 | | |
521 | 0 | if (strcmp (string, "yes") == 0) { |
522 | 0 | return true; |
523 | 0 | } else if (strcmp (string, "no") == 0) { |
524 | 0 | return false; |
525 | 0 | } else { |
526 | 0 | p11_message (_("invalid setting '%s' defaulting to '%s'"), |
527 | 0 | string, default_value ? "yes" : "no"); |
528 | 0 | return default_value; |
529 | 0 | } |
530 | 0 | } |