diff options
author | Stef Walter <stefw@collabora.co.uk> | 2011-06-08 21:21:54 +0200 |
---|---|---|
committer | Stef Walter <stefw@collabora.co.uk> | 2011-06-08 21:21:54 +0200 |
commit | 21333019a5afceb5f07637fb50b784a4ecd9f9ff (patch) | |
tree | 41a8c5ea0a8e06ea7fb1411a3fd856d72b9596bb | |
parent | 7c1edab7e6c1c6939ecdeaefc5f006772298f9eb (diff) |
Refactor configuration
* Move configuration loading into conf.c
* Have user modules with same name merge/override modules in system.
-rw-r--r-- | p11-kit/Makefile.am | 2 | ||||
-rw-r--r-- | p11-kit/conf.c | 429 | ||||
-rw-r--r-- | p11-kit/conf.h | 24 | ||||
-rw-r--r-- | p11-kit/hash.c | 117 | ||||
-rw-r--r-- | p11-kit/hash.h | 10 | ||||
-rw-r--r-- | p11-kit/modules.c | 366 | ||||
-rw-r--r-- | p11-kit/private.h | 11 | ||||
-rw-r--r-- | p11-kit/util.c | 17 | ||||
-rw-r--r-- | tests/conf-test.c | 40 |
9 files changed, 610 insertions, 406 deletions
diff --git a/p11-kit/Makefile.am b/p11-kit/Makefile.am index 99d7d1a..6f2fc19 100644 --- a/p11-kit/Makefile.am +++ b/p11-kit/Makefile.am @@ -10,10 +10,10 @@ inc_HEADERS = \ pkcs11.h MODULE_SRCS = \ + util.c util.h \ conf.c conf.h \ debug.c debug.h \ hash.c hash.h \ - util.c util.h \ modules.c \ proxy.c \ private.h \ diff --git a/p11-kit/conf.c b/p11-kit/conf.c index 51fe579..8d4703e 100644 --- a/p11-kit/conf.c +++ b/p11-kit/conf.c @@ -40,6 +40,7 @@ #include "conf.h" #define DEBUG_FLAG DEBUG_CONF #include "debug.h" +#include "private.h" #include <sys/param.h> #include <sys/stat.h> @@ -49,6 +50,7 @@ #include <ctype.h> #include <dirent.h> #include <errno.h> +#include <pwd.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> @@ -56,23 +58,6 @@ #include <unistd.h> static void -errmsg (conf_error_func error_func, const char* msg, ...) -{ - #define MAX_MSGLEN 1024 - char buf[MAX_MSGLEN]; - va_list ap; - - if (!error_func) - return; - - va_start (ap, msg); - vsnprintf (buf, MAX_MSGLEN, msg, ap); - buf[MAX_MSGLEN - 1] = 0; - error_func (buf); - va_end (ap); -} - -static void strcln (char* data, char ch) { char* p; @@ -112,22 +97,64 @@ strtrim (char* data) return data; } +static int +strequal (const char *one, const char *two) +{ + return strcmp (one, two) == 0; +} + +static char* +strconcat (const char *first, ...) +{ + size_t length = 0; + const char *arg; + char *result, *at; + va_list va; + + va_start (va, first); + + for (arg = first; arg; arg = va_arg (va, const char*)) + length += strlen (arg); + + va_end (va); + + at = result = malloc (length + 1); + if (!result) { + errno = ENOMEM; + return NULL; + } + + va_start (va, first); + + for (arg = first; arg; arg = va_arg (va, const char*)) { + length = strlen (arg); + memcpy (at, arg, length); + at += length; + } + + va_end (va); + + *at = 0; + return result; +} + /* ----------------------------------------------------------------------------- * CONFIG PARSER */ static char* -read_config_file (const char* filename, int flags, - conf_error_func error_func) +read_config_file (const char* filename, int flags) { char* config = NULL; FILE* f = NULL; + int error = 0; long len; assert (filename); f = fopen (filename, "r"); if (f == NULL) { + error = errno; if ((flags & CONF_IGNORE_MISSING) && (errno == ENOENT || errno == ENOTDIR)) { debug ("config file does not exist"); @@ -136,7 +163,8 @@ read_config_file (const char* filename, int flags, errno = ENOMEM; return config; } - errmsg (error_func, "couldn't open config file: %s", filename); + _p11_warning ("couldn't open config file: %s", filename); + errno = error; return NULL; } @@ -144,19 +172,23 @@ read_config_file (const char* filename, int flags, if (fseek (f, 0, SEEK_END) == -1 || (len = ftell (f)) == -1 || fseek (f, 0, SEEK_SET) == -1) { - errmsg (error_func, "couldn't seek config file: %s", filename); + error = errno; + _p11_warning ("couldn't seek config file: %s", filename); + errno = error; return NULL; } if ((config = (char*)malloc (len + 2)) == NULL) { - errmsg (error_func, "out of memory"); + _p11_warning ("out of memory"); errno = ENOMEM; return NULL; } /* And read in one block */ if (fread (config, 1, len, f) != len) { - errmsg (error_func, "couldn't read config file: %s", filename); + error = errno; + _p11_warning ("couldn't read config file: %s", filename); + errno = error; return NULL; } @@ -172,28 +204,69 @@ read_config_file (const char* filename, int flags, return config; } +int +_p11_conf_merge_defaults (hash_t *ht, hash_t *defaults) +{ + hash_iter_t hi; + void *key; + void *value; + + hash_iterate (defaults, &hi); + while (hash_next (&hi, &key, &value)) { + /* Only override if not set */ + if (hash_get (ht, key)) + continue; + key = strdup (key); + if (key == NULL) { + errno = ENOMEM; + return -1; + } + value = strdup (value); + if (value == NULL) { + free (key); + errno = ENOMEM; + return -1; + } + if (!hash_set (ht, key, value)) { + free (key); + free (value); + errno = ENOMEM; + return -1; + } + key = NULL; + value = NULL; + } + + return 0; +} + hash_t* -conf_parse_file (const char* filename, int flags, - conf_error_func error_func) +_p11_conf_parse_file (const char* filename, int flags) { char *name; char *value; hash_t *ht = NULL; - char *config; + char *data; char *next; char *end; + int error = 0; assert (filename); debug ("reading config file: %s", filename); /* Adds an extra newline to end of file */ - config = read_config_file (filename, flags, error_func); - if (!config) + data = read_config_file (filename, flags); + if (!data) return NULL; ht = hash_create (hash_string_hash, hash_string_equal, free, free); - next = config; + if (ht == NULL) { + free (data); + errno = ENOMEM; + return NULL; + } + next = data; /* Go through lines and process them */ while ((end = strchr (next, '\n')) != NULL) { @@ -208,8 +281,8 @@ conf_parse_file (const char* filename, int flags, /* Look for the break between name: value on the same line */ value = name + strcspn (name, ":"); if (!*value) { - errmsg (error_func, "%s: invalid config line: %s", filename, name); - errno = EINVAL; + _p11_warning ("%s: invalid config line: %s", filename, name); + error = EINVAL; break; } @@ -222,13 +295,13 @@ conf_parse_file (const char* filename, int flags, name = strdup (name); if (!name) { - errno = ENOMEM; + error = ENOMEM; break; } value = strdup (value); if (!value) { free (name); - errno = ENOMEM; + error = ENOMEM; break; } @@ -237,17 +310,299 @@ conf_parse_file (const char* filename, int flags, if (!hash_set (ht, name, value)) { free (name); free (value); - errno = ENOMEM; + error = ENOMEM; break; } } - /* Unsuccessful? */ - if (end != NULL) { + free (data); + + if (error != 0) { hash_free (ht); ht = NULL; + errno = error; } - free (config); return ht; } + +static char* +expand_user_path (const char *path) +{ + const char *env; + struct passwd *pwd; + int error = 0; + + if (path[0] == '~' && path[1] == '/') { + env = getenv ("HOME"); + if (env && env[0]) { + return strconcat (env, path + 1, NULL); + } else { + pwd = getpwuid (getuid ()); + if (!pwd) { + error = errno; + _p11_warning ("couldn't lookup home directory for user %d: %s", + getuid (), strerror (errno)); + errno = error; + return NULL; + } + return strconcat (pwd->pw_dir, path + 1, NULL); + } + } + + return strdup (path); +} + +static int +user_config_mode (hash_t *config, int defmode) +{ + const char *mode; + + /* Whether we should use or override from user directory */ + mode = hash_get (config, "user-config"); + if (mode == NULL) { + return defmode; + } else if (strequal (mode, "none")) { + return CONF_USER_NONE; + } else if (strequal (mode, "merge")) { + return CONF_USER_MERGE; + } else if (strequal (mode, "only")) { + return CONF_USER_ONLY; + } else if (strequal (mode, "override")) { + return CONF_USER_ONLY; + } else { + _p11_warning ("invalid mode for 'user-config': %s", mode); + return CONF_USER_INVALID; + } +} + +hash_t* +_p11_conf_load_globals (const char *system_conf, const char *user_conf, + int *user_mode) +{ + hash_t *config = NULL; + hash_t *uconfig = NULL; + hash_t *result = NULL; + char *path = NULL; + int error = 0; + int mode; + + /* + * This loads the system and user configs. This depends on the user-config + * value in both the system and user configs. A bit more complex than + * you might imagine, since user-config can be set to 'none' in the + * user configuration, essentially turning itself off. + */ + + /* Load the main configuration */ + config = _p11_conf_parse_file (system_conf, CONF_IGNORE_MISSING); + if (!config) + goto finished; + + /* Whether we should use or override from user directory */ + mode = user_config_mode (config, CONF_USER_NONE); + if (mode == CONF_USER_INVALID) { + error = EINVAL; + goto finished; + } + + if (mode != CONF_USER_NONE) { + path = expand_user_path (user_conf); + if (!path) { + error = errno; + goto finished; + } + + /* Load up the user configuration */ + uconfig = _p11_conf_parse_file (path, CONF_IGNORE_MISSING); + if (!uconfig) { + error = errno; + goto finished; + } + + /* Figure out what the user mode is, defaulting to system mode if not set */ + mode = user_config_mode (uconfig, mode); + if (mode == CONF_USER_INVALID) { + error = EINVAL; + goto finished; + } + + /* If merging, then supplement user config with system values */ + if (mode == CONF_USER_MERGE) { + if (_p11_conf_merge_defaults (uconfig, config) < 0) { + error = errno; + goto finished; + } + } + + /* If user config valid at all, then replace system with what we have */ + if (mode != CONF_USER_NONE) { + hash_free (config); + config = uconfig; + uconfig = NULL; + } + } + + if (user_mode) + *user_mode = mode; + + result = config; + config = NULL; + +finished: + free (path); + hash_free (config); + hash_free (uconfig); + errno = error; + return result; +} + +static int +load_config_from_file (const char *configfile, const char *name, hash_t *configs) +{ + hash_t *config; + hash_t *prev; + char *key; + int error = 0; + + assert (configfile); + + config = _p11_conf_parse_file (configfile, 0); + if (!config) + return -1; + + prev = hash_get (configs, name); + if (prev == NULL) { + key = strdup (name); + if (key == NULL) + error = ENOMEM; + else if (!hash_set (configs, key, config)) + error = errno; + else + config = NULL; + } else { + if (_p11_conf_merge_defaults (prev, config) < 0) + error = errno; + } + + /* If still set */ + hash_free (config); + + if (error) { + errno = error; + return -1; + } + + return 0; +} + +static int +load_configs_from_directory (const char *directory, hash_t *configs) +{ + struct dirent *dp; + struct stat st; + DIR *dir; + int error = 0; + int is_dir; + char *path; + int count = 0; + + debug ("loading module configs in: %s", directory); + + /* First we load all the modules */ + dir = opendir (directory); + if (!dir) { + error = errno; + if (errno == ENOENT || errno == ENOTDIR) + return 0; + _p11_warning ("couldn't list directory: %s", directory); + errno = error; + return -1; + } + + /* We're within a global mutex, so readdir is safe */ + while ((dp = readdir(dir)) != NULL) { + path = strconcat (directory, "/", dp->d_name, NULL); + if (!path) { + error = ENOMEM; + break; + } + + is_dir = 0; +#ifdef HAVE_STRUCT_DIRENT_D_TYPE + if(dp->d_type != DT_UNKNOWN) { + is_dir = (dp->d_type == DT_DIR); + } else +#endif + { + if (stat (path, &st) < 0) { + error = errno; + _p11_warning ("couldn't stat path: %s", path); + free (path); + break; + } + is_dir = S_ISDIR (st.st_mode); + } + + if (!is_dir && load_config_from_file (path, dp->d_name, configs) < 0) { + error = errno; + free (path); + break; + } + + free (path); + count ++; + } + + closedir (dir); + + if (error) { + errno = error; + return -1; + } + + return count; +} + +hash_t* +_p11_conf_load_modules (int mode, const char *system_dir, const char *user_dir) +{ + hash_t *configs; + char *path; + int error = 0; + + /* A hash table of name -> config */ + configs = hash_create (hash_string_hash, hash_string_equal, + free, (hash_destroy_func)hash_free); + + /* Load each user config first, if user config is allowed */ + if (mode != CONF_USER_NONE) { + path = expand_user_path (user_dir); + if (!path) + error = errno; + else if (load_configs_from_directory (path, configs) < 0) + error = errno; + free (path); + if (error != 0) { + hash_free (configs); + errno = error; + return NULL; + } + } + + /* + * Now unless user config is overriding, load system modules. + * Basically if a value for the same config name is not already + * loaded above (in the user configs) then they're loaded here. + */ + if (mode != CONF_USER_ONLY) { + if (load_configs_from_directory (system_dir, configs) < 0) { + error = errno; + hash_free (configs); + errno = error; + return NULL; + } + } + + return configs; +} diff --git a/p11-kit/conf.h b/p11-kit/conf.h index 81e0fb8..dc48210 100644 --- a/p11-kit/conf.h +++ b/p11-kit/conf.h @@ -42,10 +42,28 @@ enum { CONF_IGNORE_MISSING = 0x01, }; +enum { + CONF_USER_INVALID = 0, + CONF_USER_NONE = 1, + CONF_USER_MERGE, + CONF_USER_ONLY +}; + typedef void (*conf_error_func) (const char *message); -hash_t* conf_parse_file (const char *filename, - int flags, - conf_error_func error_func); +int _p11_conf_merge_defaults (hash_t *config, + hash_t *defaults); + +/* Returns a hash of char *key -> char *value */ +hash_t* _p11_conf_parse_file (const char *filename, + int flags); + +/* Returns a hash of char *key -> char *value */ +hash_t* _p11_conf_load_globals (const char *system_conf, const char *user_conf, + int *user_mode); + +/* Returns a hash of char* name -> hash_t *config */ +hash_t* _p11_conf_load_modules (int user_mode, const char *system_dir, + const char *user_dir); #endif /* __CONF_H__ */ diff --git a/p11-kit/hash.c b/p11-kit/hash.c index cfa6bf7..d116916 100644 --- a/p11-kit/hash.c +++ b/p11-kit/hash.c @@ -100,10 +100,6 @@ struct hash { #define int_calloc calloc #define int_free free -/* - * Hash creation functions. - */ - static hash_entry_t** alloc_array(hash_t* ht, unsigned int max) { @@ -138,46 +134,30 @@ hash_create (hash_hash_func hash_func, return ht; } -void -hash_free (hash_t* ht) +static hash_entry_t* +next_entry (hash_iter_t* hi) { - hash_iter_t hi; - - if (!ht) - return; - - hash_iterate (ht, &hi); - while (hash_next (&hi, NULL, NULL)) { - if (ht->key_destroy_func) - ht->key_destroy_func (hi.ths->key); - if (ht->value_destroy_func) - ht->value_destroy_func (hi.ths->val); - free (hi.ths); + hash_entry_t *he = hi->next; + while (!he) { + if (hi->index > hi->ht->max) + return NULL; + he = hi->ht->array[hi->index++]; } - - if (ht->array) - int_free (ht->array); - - int_free (ht); + hi->next = he->next; + return he; } -/* - * Hash iteration functions. - */ + int hash_next (hash_iter_t* hi, void **key, void **value) { - hi->ths = hi->next; - while (!hi->ths) { - if (hi->index > hi->ht->max) - return 0; - hi->ths = hi->ht->array[hi->index++]; - } - hi->next = hi->ths->next; + hash_entry_t *he = next_entry (hi); + if (he == NULL) + return 0; if (key) - *key = hi->ths->key; + *key = he->key; if (value) - *value = hi->ths->val; + *value = he->val; return 1; } @@ -186,19 +166,15 @@ hash_iterate (hash_t* ht, hash_iter_t *hi) { hi->ht = ht; hi->index = 0; - hi->ths = NULL; hi->next = NULL; } -/* - * Expanding a hash table - */ - static int expand_array (hash_t* ht) { hash_iter_t hi; - hash_entry_t** new_array; + hash_entry_t *he; + hash_entry_t **new_array; unsigned int new_max; new_max = ht->max * 2 + 1; @@ -208,10 +184,10 @@ expand_array (hash_t* ht) return 0; hash_iterate (ht, &hi); - while (hash_next (&hi, NULL, NULL)) { - unsigned int i = hi.ths->hash & new_max; - hi.ths->next = new_array[i]; - new_array[i] = hi.ths; + while ((he = next_entry (&hi)) != NULL) { + unsigned int i = he->hash & new_max; + he->next = new_array[i]; + new_array[i] = he; } if(ht->array) @@ -301,7 +277,7 @@ hash_set (hash_t* ht, void* key, void* val) } int -hash_remove (hash_t* ht, const void* key) +hash_steal (hash_t *ht, const void *key, void **stolen_key, void **stolen_value) { hash_entry_t** hep = find_entry (ht, key, 0); @@ -309,15 +285,32 @@ hash_remove (hash_t* ht, const void* key) hash_entry_t* old = *hep; *hep = (*hep)->next; --ht->count; - if (ht->key_destroy_func) - ht->key_destroy_func (old->key); - if (ht->value_destroy_func) - ht->value_destroy_func (old->val); + if (stolen_key) + *stolen_key = old->key; + if (stolen_value) + *stolen_value = old->val; free (old); return 1; } return 0; + +} + +int +hash_remove (hash_t *ht, const void *key) +{ + void *old_key; + void *old_value; + + if (!hash_steal (ht, key, &old_key, &old_value)) + return 0; + + if (ht->key_destroy_func) + ht->key_destroy_func (old_key); + if (ht->value_destroy_func) + ht->value_destroy_func (old_value); + return 1; } void @@ -344,6 +337,30 @@ hash_clear (hash_t* ht) ht->count = 0; } +void +hash_free (hash_t* ht) +{ + hash_entry_t *he; + hash_iter_t hi; + + if (!ht) + return; + + hash_iterate (ht, &hi); + while ((he = next_entry (&hi)) != NULL) { + if (ht->key_destroy_func) + ht->key_destroy_func (he->key); + if (ht->value_destroy_func) + ht->value_destroy_func (he->val); + free (he); + } + + if (ht->array) + int_free (ht->array); + + int_free (ht); +} + unsigned int hash_count (hash_t* ht) { diff --git a/p11-kit/hash.h b/p11-kit/hash.h index 649b98b..8c3060a 100644 --- a/p11-kit/hash.h +++ b/p11-kit/hash.h @@ -81,7 +81,6 @@ typedef struct hash hash_t; typedef struct hash_iter { hash_t* ht; - struct hash_entry* ths; struct hash_entry* next; unsigned int index; } hash_iter_t; @@ -140,6 +139,15 @@ int hash_remove (hash_t* ht, const void* key); /* + * hash_steal: Remove a value from the hash table without calling destroy funcs + * - returns 1 if the entry was found + */ +int hash_steal (hash_t *ht, + const void *key, + void **stolen_key, + void **stolen_value); + +/* * hash_first: Start enumerating through the hash table * - returns a hash iterator */ diff --git a/p11-kit/modules.c b/p11-kit/modules.c index 268a3d5..999770d 100644 --- a/p11-kit/modules.c +++ b/p11-kit/modules.c @@ -51,7 +51,6 @@ #include <dlfcn.h> #include <errno.h> #include <pthread.h> -#include <pwd.h> #include <stdarg.h> #include <stddef.h> #include <stdlib.h> @@ -130,71 +129,6 @@ static struct _Shared { } gl = { NULL, NULL }; /* ----------------------------------------------------------------------------- - * UTILITIES - */ - -static void -warning (const char* msg, ...) -{ - char buffer[512]; - va_list va; - - va_start (va, msg); - - vsnprintf(buffer, sizeof (buffer) - 1, msg, va); - buffer[sizeof (buffer) - 1] = 0; - fprintf (stderr, "p11-kit: %s\n", buffer); - - va_end (va); -} - -static void -conf_error (const char *buffer) -{ - /* called from conf.c */ - fprintf (stderr, "p11-kit: %s\n", buffer); -} - -static char* -strconcat (const char *first, ...) -{ - size_t length = 0; - const char *arg; - char *result, *at; - va_list va; - - va_start (va, first); - - for (arg = first; arg; arg = va_arg (va, const char*)) - length += strlen (arg); - - va_end (va); - - at = result = malloc (length + 1); - if (!result) - return NULL; - - va_start (va, first); - - for (arg = first; arg; arg = va_arg (va, const char*)) { - length = strlen (arg); - memcpy (at, arg, length); - at += length; - } - - va_end (va); - - *at = 0; - return result; -} - -static int -strequal (const char *one, const char *two) -{ - return strcmp (one, two) == 0; -} - -/* ----------------------------------------------------------------------------- * P11-KIT FUNCTIONALITY */ @@ -322,7 +256,7 @@ dlopen_and_get_function_list (Module *mod, const char *path) mod->dl_module = dlopen (path, RTLD_LOCAL | RTLD_NOW); if (mod->dl_module == NULL) { - warning ("couldn't load module: %s: %s", path, dlerror ()); + _p11_warning ("couldn't load module: %s: %s", path, dlerror ()); return CKR_GENERAL_ERROR; } @@ -330,15 +264,15 @@ dlopen_and_get_function_list (Module *mod, const char *path) gfl = dlsym (mod->dl_module, "C_GetFunctionList"); if (!gfl) { - warning ("couldn't find C_GetFunctionList entry point in module: %s: %s", - path, dlerror ()); + _p11_warning ("couldn't find C_GetFunctionList entry point in module: %s: %s", + path, dlerror ()); return CKR_GENERAL_ERROR; } rv = gfl (&mod->funcs); if (rv != CKR_OK) { - warning ("call to C_GetFunctiontList failed in module: %s: %s", - path, p11_kit_strerror (rv)); + _p11_warning ("call to C_GetFunctiontList failed in module: %s: %s", + path, p11_kit_strerror (rv)); return rv; } @@ -380,38 +314,32 @@ load_module_from_file_unlocked (const char *path, Module **result) } static CK_RV -load_module_from_config_unlocked (const char *configfile, const char *name) +take_config_and_load_module_unlocked (char **name, hash_t **config) { Module *mod, *prev; const char *path; CK_RV rv; - assert (configfile); - - mod = alloc_module_unlocked (); - if (!mod) - return CKR_HOST_MEMORY; + assert (name); + assert (*name); + assert (config); + assert (*config); - mod->config = conf_parse_file (configfile, 0, conf_error); - if (!mod->config) { - free_module_unlocked (mod); - if (errno == ENOMEM) - return CKR_HOST_MEMORY; - return CKR_GENERAL_ERROR; + path = hash_get (*config, "module"); + if (path == NULL) { + debug ("no module path for module, skipping: %s", *name); + return CKR_OK; } - mod->name = strdup (name); - if (!mod->name) { - free_module_unlocked (mod); + mod = alloc_module_unlocked (); + if (!mod) return CKR_HOST_MEMORY; - } - path = hash_get (mod->config, "module"); - if (path == NULL) { - free_module_unlocked (mod); - debug ("no module path specified in config, skipping: %s", configfile); - return CKR_OK; - } + /* Take ownership of thes evariables */ + mod->config = *config; + *config = NULL; + mod->name = *name; + *name = NULL; rv = dlopen_and_get_function_list (mod, path); if (rv != CKR_OK) { @@ -432,7 +360,7 @@ load_module_from_config_unlocked (const char *configfile, const char *name) /* Refuse to load duplicate module */ if (prev) { - warning ("duplicate configured module: %s: %s", mod->name, path); + _p11_warning ("duplicate configured module: %s: %s", mod->name, path); free_module_unlocked (mod); return CKR_GENERAL_ERROR; } @@ -453,233 +381,61 @@ load_module_from_config_unlocked (const char *configfile, const char *name) } static CK_RV -load_modules_from_config_unlocked (const char *directory) -{ - struct dirent *dp; - struct stat st; - CK_RV rv = CKR_OK; - DIR *dir; - int is_dir; - char *path; - - debug ("loading module configs in: %s", directory); - - /* First we load all the modules */ - dir = opendir (directory); - if (!dir) { - if (errno == ENOENT || errno == ENOTDIR) - return CKR_OK; - warning ("couldn't list directory: %s", directory); - return CKR_GENERAL_ERROR; - } - - /* We're within a global mutex, so readdir is safe */ - while ((dp = readdir(dir)) != NULL) { - path = strconcat (directory, "/", dp->d_name, NULL); - if (!path) { - rv = CKR_HOST_MEMORY; - break; - } - - is_dir = 0; -#ifdef HAVE_STRUCT_DIRENT_D_TYPE - if(dp->d_type != DT_UNKNOWN) { - is_dir = (dp->d_type == DT_DIR); - } else -#endif - { - if (stat (path, &st) < 0) { - warning ("couldn't stat path: %s", path); - free (path); - rv = CKR_GENERAL_ERROR; - break; - } - is_dir = S_ISDIR (st.st_mode); - } - - if (is_dir) - rv = CKR_OK; - else - rv = load_module_from_config_unlocked (path, dp->d_name); - - free (path); - - if (rv != CKR_OK) - break; - } - - closedir (dir); - - return rv; -} - -static char* -expand_user_path (const char *path) -{ - const char *env; - struct passwd *pwd; - - if (path[0] == '~' && path[1] == '/') { - env = getenv ("HOME"); - if (env && env[0]) { - return strconcat (env, path + 1, NULL); - } else { - pwd = getpwuid (getuid ()); - if (!pwd) - return NULL; - return strconcat (pwd->pw_dir, path + 1, NULL); - } - } - - return strdup (path); -} - -enum { - USER_CONFIG_INVALID = 0, - USER_CONFIG_NONE = 1, - USER_CONFIG_MERGE, - USER_CONFIG_OVERRIDE -}; - -static int -user_config_mode (hash_t *config, int defmode) -{ - const char *mode; - - /* Whether we should use or override from user directory */ - mode = hash_get (config, "user-config"); - if (mode == NULL) { - return defmode; - } else if (strequal (mode, "none")) { - return USER_CONFIG_NONE; - } else if (strequal (mode, "merge")) { - return USER_CONFIG_MERGE; - } else if (strequal (mode, "override")) { - return USER_CONFIG_OVERRIDE; - } else { - warning ("invalid mode for 'user-config': %s", mode); - return USER_CONFIG_INVALID; - } -} - -static CK_RV -load_config_files_unlocked (int *user_mode) +load_registered_modules_unlocked (void) { - hash_t *config = NULL; - hash_t *uconfig = NULL; - void *key = NULL; - void *value = NULL; - char *path; - int mode; - CK_RV rv = CKR_GENERAL_ERROR; hash_iter_t hi; + hash_t *configs; + void *key; + char *name; + hash_t *config; + int mode; + CK_RV rv; - /* Should only be called after everything has been unloaded */ - assert (!gl.config); - - /* Load the main configuration */ - config = conf_parse_file (P11_SYSTEM_CONF, CONF_IGNORE_MISSING, conf_error); - if (!config) { - rv = (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR; - goto finished; - } - - /* Whether we should use or override from user directory */ - mode = user_config_mode (config, USER_CONFIG_NONE); - if (mode == USER_CONFIG_INVALID) - goto finished; - - if (mode != USER_CONFIG_NONE) { - path = expand_user_path (P11_USER_CONF); - if (!path) - goto finished; - - /* Load up the user configuration */ - uconfig = conf_parse_file (path, CONF_IGNORE_MISSING, conf_error); - free (path); + if (gl.config) + return CKR_OK; - if (!uconfig) { - rv = (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR; - goto finished; - } + /* Load the global configuration files */ + config = _p11_conf_load_globals (P11_SYSTEM_CONF, P11_USER_CONF, &mode); + if (config == NULL) + return (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR; - /* Figure out what the user mode is */ - mode = user_config_mode (uconfig, mode); - if (mode == USER_CONFIG_INVALID) - goto finished; - - /* Merge everything into the system config */ - if (mode == USER_CONFIG_MERGE) { - hash_iterate (uconfig, &hi); - while (hash_next (&hi, &key, &value)) { - key = strdup (key); - if (key == NULL) - goto finished; - value = strdup (value); - if (value == NULL) - goto finished; - if (!hash_set (config, key, value)) - goto finished; - key = NULL; - value = NULL; - } + assert (mode != CONF_USER_INVALID); - /* Override the system config */ - } else if (mode == USER_CONFIG_OVERRIDE) { - hash_free (config); - config = uconfig; - uconfig = NULL; - } + configs = _p11_conf_load_modules (mode, P11_SYSTEM_MODULES, P11_USER_MODULES); + if (configs == NULL) { + rv = (errno == ENOMEM) ? CKR_HOST_MEMORY : CKR_GENERAL_ERROR; + hash_free (config); + return rv; } + assert (gl.config == NULL); gl.config = config; - config = NULL; - rv = CKR_OK; - - if (user_mode) - *user_mode = mode; - -finished: - hash_free (config); - hash_free (uconfig); - free (key); - free (value); - return rv; -} - -static CK_RV -load_registered_modules_unlocked (void) -{ - char *path; - int mode; - CK_RV rv; - rv = load_config_files_unlocked (&mode); - if (rv != CKR_OK) - return rv; + /* + * Now go through each config and turn it into a module. As we iterate + * we steal the values of the config. + */ + hash_iterate (configs, &hi); + while (hash_next (&hi, &key, NULL)) { + if (!hash_steal (configs, name, (void**)&name, (void**)config)) + assert (0 && "not reached"); - assert (gl.config); - assert (mode != USER_CONFIG_INVALID); + rv = take_config_and_load_module_unlocked (&name, &config); - /* Load each module from the main list */ - if (mode != USER_CONFIG_OVERRIDE) { - rv = load_modules_from_config_unlocked (P11_SYSTEM_MODULES); - if (rv != CKR_OK); - return rv; - } + /* + * These variables will be cleared if ownership is transeferred + * by the above function call. + */ + free (name); + hash_free (config); - /* Load each module from the user list */ - if (mode != USER_CONFIG_NONE) { - path = expand_user_path (P11_USER_MODULES); - if (!path) - rv = CKR_GENERAL_ERROR; - else - rv = load_modules_from_config_unlocked (path); - free (path); - if (rv != CKR_OK); + if (rv != CKR_OK) { + hash_free (configs); return rv; + } } + hash_free (configs); return CKR_OK; } diff --git a/p11-kit/private.h b/p11-kit/private.h index 56d5394..e8abe93 100644 --- a/p11-kit/private.h +++ b/p11-kit/private.h @@ -35,12 +35,17 @@ #ifndef __P11_KIT_PRIVATE_H__ #define __P11_KIT_PRIVATE_H__ +#include "pkcs11.h" +#include "pthread.h" + extern pthread_mutex_t _p11_mutex; #define _p11_lock() pthread_mutex_lock (&_p11_mutex); #define _p11_unlock() pthread_mutex_unlock (&_p11_mutex); +void _p11_warning (const char* msg, ...); + CK_FUNCTION_LIST_PTR_PTR _p11_kit_registered_modules_unlocked (void); CK_RV _p11_kit_initialize_registered_unlocked_reentrant (void); @@ -49,4 +54,10 @@ CK_RV _p11_kit_finalize_registered_unlocked_reentrant (void); void _p11_kit_proxy_after_fork (void); +CK_RV _p11_load_config_files_unlocked (const char *system_conf, + const char *user_conf, + int *user_mode); + +const char* _p11_kit_clear_message (void); + #endif /* __P11_KIT_PRIVATE_H__ */ diff --git a/p11-kit/util.c b/p11-kit/util.c index e4b0f49..7ea125f 100644 --- a/p11-kit/util.c +++ b/p11-kit/util.c @@ -37,10 +37,13 @@ #include "config.h" #include "p11-kit.h" +#include "private.h" #include "util.h" #include <assert.h> +#include <stdarg.h> #include <stdlib.h> +#include <stdio.h> #include <string.h> void* @@ -123,3 +126,17 @@ p11_kit_space_strdup (const unsigned char *string, size_t max_length) result[length] = 0; return result; } + +void +_p11_warning (const char* msg, ...) +{ + char buffer[512]; + va_list va; + + va_start (va, msg); + vsnprintf (buffer, sizeof (buffer) - 1, msg, va); + va_end (va); + + buffer[sizeof (buffer) - 1] = 0; + fprintf (stderr, "p11-kit: %s\n", buffer); +} diff --git a/tests/conf-test.c b/tests/conf-test.c index d9929aa..2ef4e5c 100644 --- a/tests/conf-test.c +++ b/tests/conf-test.c @@ -44,18 +44,12 @@ static int n_errors = 0; static void -error_func (const char *buffer) -{ - ++n_errors; -} - -static void test_parse_conf_1 (CuTest *tc) { hash_t *ht; const char *value; - ht = conf_parse_file (SRCDIR "/files/test-1.conf", 0, error_func); + ht = _p11_conf_parse_file (SRCDIR "/files/test-1.conf", 0); CuAssertPtrNotNull (tc, ht); value = hash_get (ht, "key1"); @@ -79,7 +73,7 @@ test_parse_ignore_missing (CuTest *tc) hash_t *ht; n_errors = 0; - ht = conf_parse_file (SRCDIR "/files/non-existant.conf", CONF_IGNORE_MISSING, error_func); + ht = _p11_conf_parse_file (SRCDIR "/files/non-existant.conf", CONF_IGNORE_MISSING); CuAssertPtrNotNull (tc, ht); CuAssertIntEquals (tc, 0, hash_count (ht)); @@ -93,11 +87,38 @@ test_parse_fail_missing (CuTest *tc) hash_t *ht; n_errors = 0; - ht = conf_parse_file (SRCDIR "/files/non-existant.conf", 0, error_func); + ht = _p11_conf_parse_file (SRCDIR "/files/non-existant.conf", 0); CuAssertPtrEquals (tc, ht, NULL); CuAssertIntEquals (tc, 1, n_errors); } +static void +test_merge_defaults (CuTest *tc) +{ + hash_t *values; + hash_t *defaults; + + values = hash_create (hash_string_hash, hash_string_equal, free, free); + defaults = hash_create (hash_string_hash, hash_string_equal, free, free); + + hash_set (values, strdup ("one"), strdup ("real1")); + hash_set (values, strdup ("two"), strdup ("real2")); + + hash_set (defaults, strdup ("two"), strdup ("default2")); + hash_set (defaults, strdup ("three"), strdup ("default3")); + + if (!_p11_conf_merge_defaults (values, defaults)) + CuFail (tc, "should not be reached"); + + hash_free (defaults); + + CuAssertStrEquals (tc, hash_get (values, "one"), "real1"); + CuAssertStrEquals (tc, hash_get (values, "two"), "real2"); + CuAssertStrEquals (tc, hash_get (values, "three"), "default3"); + + hash_free (values); +} + int main (void) { @@ -108,6 +129,7 @@ main (void) SUITE_ADD_TEST (suite, test_parse_conf_1); SUITE_ADD_TEST (suite, test_parse_ignore_missing); SUITE_ADD_TEST (suite, test_parse_fail_missing); + SUITE_ADD_TEST (suite, test_merge_defaults); CuSuiteRun (suite); CuSuiteSummary (suite, output); |