diff options
author | Werner Koch <[email protected]> | 2007-03-06 20:44:41 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2007-03-06 20:44:41 +0000 |
commit | ed84b0f787a48fe2cfdd55b3acf70ca6b4732291 (patch) | |
tree | 24607f518b4e1064785728d8efcdfb3971d69f56 /tools | |
parent | Ported multiple-messages protection. (diff) | |
download | gnupg-ed84b0f787a48fe2cfdd55b3acf70ca6b4732291.tar.gz gnupg-ed84b0f787a48fe2cfdd55b3acf70ca6b4732291.zip |
Support for a global gpgconf configuration file.
Diffstat (limited to 'tools')
-rw-r--r-- | tools/ChangeLog | 15 | ||||
-rw-r--r-- | tools/gpgconf-comp.c | 655 | ||||
-rw-r--r-- | tools/gpgconf.c | 34 | ||||
-rw-r--r-- | tools/gpgconf.h | 4 |
4 files changed, 591 insertions, 117 deletions
diff --git a/tools/ChangeLog b/tools/ChangeLog index 0cfadae02..56247c6d8 100644 --- a/tools/ChangeLog +++ b/tools/ChangeLog @@ -1,3 +1,18 @@ +2007-03-06 Werner Koch <[email protected]> + + * gpgconf-comp.c: Include pwd.h and grp.h. + (GC_OPT_FLAG_NO_CHANGE): New. + (gc_component_change_options): Implement it. + (gc_options_gpg_agent): Add options for all ttl values and + min-passphrase-length. Apply new flag to some of them. + (gc_process_gpgconf_conf, key_matches_user_or_group): New. + (gc_component_change_options): Factor some code out to .. + (change_one_value): .. new. + (gc_component_retrieve_options): Allow -1 for COMPONENT to iterate + over al components. + * gpgconf.c (main): New commands --check-config and + --apply-defaults. Call gc_process_gpgconf_conf. + 2007-01-31 Werner Koch <[email protected]> * Makefile.am (symcryptrun_LDADD): Add LIBICONV. diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index e1d96d2c8..1eb6353fb 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -33,6 +33,8 @@ #include <time.h> #include <stdarg.h> #include <signal.h> +#include <pwd.h> +#include <grp.h> /* For log_logv(), asctimestamp(), gnupg_get_time (). */ #define JNLIB_NEED_LOG_LOGV @@ -321,6 +323,11 @@ static struct /* The NO_ARG_DESC flag for an option indicates that the argument has a default, which is described by the value of the ARGDEF field. */ #define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6) +/* The NO_CHANGE flag for an option indicates that the user should not + be allowed to chnage this option using the standard gpgconf method. + Frontends using gpgconf should grey out such otions, so that only + the current value is displayed. */ +#define GC_OPT_FLAG_NO_CHANGE (1UL <<7) /* A human-readable description for each flag. */ static struct @@ -334,7 +341,8 @@ static struct { "runtime" }, { "default" }, { "default desc" }, - { "no arg desc" } + { "no arg desc" }, + { "no change" } }; @@ -471,20 +479,36 @@ static gc_option_t gc_options_gpg_agent[] = { "Security", GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, "gnupg", N_("Options controlling the security") }, - { "default-cache-ttl", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "|N|expire cached PINs after N seconds", + { "default-cache-ttl", GC_OPT_FLAG_RUNTIME, + GC_LEVEL_BASIC, "gnupg", + "|N|expire cached PINs after N seconds", GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, - { "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "do not use the PIN cache when signing", + { "default-cache-ttl-ssh", GC_OPT_FLAG_RUNTIME, + GC_LEVEL_ADVANCED, "gnupg", + N_("|N|expire SSH keys after N seconds"), + GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, + { "max-cache-ttl", GC_OPT_FLAG_RUNTIME, + GC_LEVEL_EXPERT, "gnupg", + N_("|N|set maximum PIN cache lifetime to N seconds"), + GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, + { "max-cache-ttl-ssh", GC_OPT_FLAG_RUNTIME, + GC_LEVEL_EXPERT, "gnupg", + N_("|N|set maximum SSH key lifetime to N seconds"), + GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, + { "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME, + GC_LEVEL_BASIC, "gnupg", "do not use the PIN cache when signing", GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "allow-mark-trusted", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, - "gnupg", "allow clients to mark keys as \"trusted\"", + { "allow-mark-trusted", GC_OPT_FLAG_RUNTIME | GC_OPT_FLAG_NO_CHANGE, + GC_LEVEL_ADVANCED, "gnupg", "allow clients to mark keys as \"trusted\"", GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, + { "min-passphrase-len", GC_OPT_FLAG_RUNTIME, + GC_LEVEL_EXPERT, "gnupg", + N_("|N|set minimal required length for new passphrases to N"), + GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, { "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, "gnupg", "do not grab keyboard and mouse", GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - GC_OPTION_NULL }; @@ -1539,41 +1563,56 @@ retrieve_options_from_file (gc_component_t component, gc_backend_t backend) /* Retrieve the currently active options and their defaults from all - involved backends for this component. */ + involved backends for this component. Using -1 for component will + retrieve all options from all components. */ void gc_component_retrieve_options (int component) { + int process_all = 0; int backend_seen[GC_BACKEND_NR]; gc_backend_t backend; - gc_option_t *option = gc_component[component].options; + gc_option_t *option; for (backend = 0; backend < GC_BACKEND_NR; backend++) backend_seen[backend] = 0; - while (option->name) + if (component == -1) { - if (!(option->flags & GC_OPT_FLAG_GROUP)) - { - backend = option->backend; - - if (backend_seen[backend]) - { - option++; - continue; - } - backend_seen[backend] = 1; - - assert (backend != GC_BACKEND_ANY); + process_all = 1; + component = 0; + assert (component < GC_COMPONENT_NR); + } + + do + { + option = gc_component[component].options; - if (gc_backend[backend].program) - retrieve_options_from_program (component, backend); - else - retrieve_options_from_file (component, backend); - } - option++; + while (option->name) + { + if (!(option->flags & GC_OPT_FLAG_GROUP)) + { + backend = option->backend; + + if (backend_seen[backend]) + { + option++; + continue; + } + backend_seen[backend] = 1; + + assert (backend != GC_BACKEND_ANY); + + if (gc_backend[backend].program) + retrieve_options_from_program (component, backend); + else + retrieve_options_from_file (component, backend); + } + option++; + } } -} + while (process_all && ++component < GC_COMPONENT_NR); +} /* Perform a simple validity check based on the type. Return in NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of @@ -1585,7 +1624,8 @@ option_check_validity (gc_option_t *option, unsigned long flags, char *arg; if (!option->active) - gc_error (1, 0, "option %s not supported by backend", option->name); + gc_error (1, 0, "option %s not supported by backend %s", + option->name, gc_backend[option->backend].name); if (option->new_flags || option->new_value) gc_error (1, 0, "option %s already changed", option->name); @@ -2270,7 +2310,49 @@ change_options_program (gc_component_t component, gc_backend_t backend, } -/* Read the modifications from IN and apply them. */ +/* Common code for gc_component_change_options and + gc_process_gpgconf_conf. */ +static void +change_one_value (gc_option_t *option, int *runtime, + unsigned long flags, char *new_value) +{ + unsigned long new_value_nr = 0; + + option_check_validity (option, flags, new_value, &new_value_nr); + + if (option->flags & GC_OPT_FLAG_RUNTIME) + runtime[option->backend] = 1; + + option->new_flags = flags; + if (!(flags & GC_OPT_FLAG_DEFAULT)) + { + if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE + && (option->flags & GC_OPT_FLAG_LIST)) + { + char *str; + + /* We convert the number to a list of 1's for convenient + list handling. */ + assert (new_value_nr > 0); + option->new_value = xmalloc ((2 * (new_value_nr - 1) + 1) + 1); + str = option->new_value; + *(str++) = '1'; + while (--new_value_nr > 0) + { + *(str++) = ','; + *(str++) = '1'; + } + *(str++) = '\0'; + } + else + option->new_value = xstrdup (new_value); + } +} + + +/* Read the modifications from IN and apply them. If IN is NULL the + modifications are expected to already have been set to the global + table. */ void gc_component_change_options (int component, FILE *in) { @@ -2293,90 +2375,71 @@ gc_component_change_options (int component, FILE *in) orig_pathname[backend] = NULL; } - while ((length = read_line (in, &line, &line_len, NULL)) > 0) + if (in) { - char *linep; - unsigned long flags = 0; - char *new_value = ""; - unsigned long new_value_nr = 0; - - /* Strip newline and carriage return, if present. */ - while (length > 0 - && (line[length - 1] == '\n' || line[length - 1] == '\r')) - line[--length] = '\0'; - - linep = strchr (line, ':'); - if (linep) - *(linep++) = '\0'; - - /* Extract additional flags. Default to none. */ - if (linep) - { - char *end; - char *tail; - - end = strchr (linep, ':'); - if (end) - *(end++) = '\0'; - - errno = 0; - flags = strtoul (linep, &tail, 0); - if (errno) - gc_error (1, errno, "malformed flags in option %s", line); - if (!(*tail == '\0' || *tail == ':' || *tail == ' ')) - gc_error (1, 0, "garbage after flags in option %s", line); - - linep = end; - } - - /* Extract default value, if present. Default to empty if - not. */ - if (linep) - { - char *end; - - end = strchr (linep, ':'); - if (end) - *(end++) = '\0'; - - new_value = linep; - - linep = end; - } - - option = find_option (component, line, GC_BACKEND_ANY); - if (!option) - gc_error (1, 0, "unknown option %s", line); - - option_check_validity (option, flags, new_value, &new_value_nr); - - if (option->flags & GC_OPT_FLAG_RUNTIME) - runtime[option->backend] = 1; - - option->new_flags = flags; - if (!(flags & GC_OPT_FLAG_DEFAULT)) - { - if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE - && (option->flags & GC_OPT_FLAG_LIST)) - { - char *str; - - /* We convert the number to a list of 1's for - convenient list handling. */ - assert (new_value_nr > 0); - option->new_value = xmalloc ((2 * (new_value_nr - 1) + 1) + 1); - str = option->new_value; - *(str++) = '1'; - while (--new_value_nr > 0) - { - *(str++) = ','; - *(str++) = '1'; - } - *(str++) = '\0'; - } - else - option->new_value = xstrdup (new_value); - } + /* Read options from the file IN. */ + while ((length = read_line (in, &line, &line_len, NULL)) > 0) + { + char *linep; + unsigned long flags = 0; + char *new_value = ""; + + /* Strip newline and carriage return, if present. */ + while (length > 0 + && (line[length - 1] == '\n' || line[length - 1] == '\r')) + line[--length] = '\0'; + + linep = strchr (line, ':'); + if (linep) + *(linep++) = '\0'; + + /* Extract additional flags. Default to none. */ + if (linep) + { + char *end; + char *tail; + + end = strchr (linep, ':'); + if (end) + *(end++) = '\0'; + + errno = 0; + flags = strtoul (linep, &tail, 0); + if (errno) + gc_error (1, errno, "malformed flags in option %s", line); + if (!(*tail == '\0' || *tail == ':' || *tail == ' ')) + gc_error (1, 0, "garbage after flags in option %s", line); + + linep = end; + } + + /* Don't allow setting of the no change flag. */ + flags &= ~GC_OPT_FLAG_NO_CHANGE; + + /* Extract default value, if present. Default to empty if not. */ + if (linep) + { + char *end; + end = strchr (linep, ':'); + if (end) + *(end++) = '\0'; + new_value = linep; + linep = end; + } + + option = find_option (component, line, GC_BACKEND_ANY); + if (!option) + gc_error (1, 0, "unknown option %s", line); + + if ((option->flags & GC_OPT_FLAG_NO_CHANGE)) + { + gc_error (0, 0, "ignoring new value for option %s", + option->name); + continue; + } + + change_one_value (option, runtime, flags, new_value); + } } /* Now that we have collected and locally verified the changes, @@ -2500,3 +2563,365 @@ gc_component_change_options (int component, FILE *in) xfree (line); } + + +/* Check whether USER matches the current user of one of its group. + This function may change USER. Returns true is there is a + match. */ +static int +key_matches_user_or_group (char *user) +{ + char *group; + int n; + + if (*user == '*' && user[1] == 0) + return 1; /* A single asterisk matches all users. */ + + group = strchr (user, ':'); + if (group) + *group++ = 0; + + /* First check whether the user matches. */ + if (*user) + { + static char *my_name; + + if (!my_name) + { + struct passwd *pw = getpwuid ( getuid () ); + if (!pw) + gc_error (1, errno, "getpwuid failed for current user"); + my_name = xstrdup (pw->pw_name); + } + if (!strcmp (user, my_name)) + return 1; /* Found. */ + } + + /* If that failed, check whether a group matches. */ + if (group && *group) + { + static char *my_group; + static char **my_supgroups; + + if (!my_group) + { + struct group *gr = getgrgid ( getgid () ); + if (!gr) + gc_error (1, errno, "getgrgid failed for current user"); + my_group = xstrdup (gr->gr_name); + } + if (!strcmp (group, my_group)) + return 1; /* Found. */ + + if (!my_supgroups) + { + int ngids; + gid_t *gids; + + ngids = getgroups (0, NULL); + gids = xcalloc (ngids+1, sizeof *gids); + ngids = getgroups (ngids, gids); + if (ngids < 0) + gc_error (1, errno, "getgroups failed for current user"); + my_supgroups = xcalloc (ngids+1, sizeof *my_supgroups); + for (n=0; n < ngids; n++) + { + struct group *gr = getgrgid ( gids[n] ); + if (!gr) + gc_error (1, errno, "getgrgid failed for supplementary group"); + my_supgroups[n] = xstrdup (gr->gr_name); + } + xfree (gids); + } + + for (n=0; my_supgroups[n]; n++) + if (!strcmp (group, my_supgroups[n])) + return 1; /* Found. */ + } + + return 0; /* No match. */ +} + + + +/* Read and process the global configuration file for gpgconf. This + optional file is used to update our internal tables at runtime and + may also be used to set new default values. If FNAME is NULL the + default name will be used. With UPDATE set to true the internal + tables are actually updated; if not set, only a syntax check is + done. If DEFAULTS is true the global options are written to the + configuration files. + + Returns 0 on success or if the config file is not present; -1 is + returned on error. */ +int +gc_process_gpgconf_conf (const char *fname, int update, int defaults) +{ + int result = 0; + char *line = NULL; + size_t line_len = 0; + ssize_t length; + FILE *config; + int lineno = 0; + int in_rule = 0; + int got_match = 0; + int runtime[GC_BACKEND_NR]; + int used_components[GC_COMPONENT_NR]; + int backend_id, component_id; + + if (!fname) + fname = GNUPG_SYSCONFDIR "/gpgconf.conf"; + + for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++) + runtime[backend_id] = 0; + for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++) + used_components[component_id] = 0; + + config = fopen (fname, "r"); + if (!config) + { + /* Do not print an error if the file is not available, except + when runnign in syntax check mode. */ + if (errno != ENOENT || !update) + { + gc_error (0, errno, "can not open global config file `%s'", fname); + result = -1; + } + return result; + } + + while ((length = read_line (config, &line, &line_len, NULL)) > 0) + { + char *key, *component, *option, *flags, *value; + char *empty; + gc_option_t *option_info = NULL; + char *p; + int is_continuation; + + lineno++; + key = line; + while (*key == ' ' || *key == '\t') + key++; + if (!*key || *key == '#' || *key == '\r' || *key == '\n') + continue; + + is_continuation = (key != line); + + /* Parse the key field. */ + if (!is_continuation && got_match) + break; /* Finish after the first match. */ + else if (!is_continuation) + { + in_rule = 0; + for (p=key+1; *p && !strchr (" \t\r\n", *p); p++) + ; + if (!*p) + { + gc_error (0, 0, "missing rule at `%s', line %d", fname, lineno); + result = -1; + continue; + } + *p++ = 0; + component = p; + } + else if (!in_rule) + { + gc_error (0, 0, "continuation but no rule at `%s', line %d", + fname, lineno); + result = -1; + continue; + } + else + { + component = key; + key = NULL; + } + + in_rule = 1; + + /* Parse the component. */ + while (*component == ' ' || *component == '\t') + component++; + for (p=component; *p && !strchr (" \t\r\n", *p); p++) + ; + if (p == component) + { + gc_error (0, 0, "missing component at `%s', line %d", + fname, lineno); + result = -1; + continue; + } + empty = p; + *p++ = 0; + option = p; + component_id = gc_component_find (component); + if (component_id < 0) + { + gc_error (0, 0, "unknown component at `%s', line %d", + fname, lineno); + result = -1; + } + + /* Parse the option name. */ + while (*option == ' ' || *option == '\t') + option++; + for (p=option; *p && !strchr (" \t\r\n", *p); p++) + ; + if (p == option) + { + gc_error (0, 0, "missing option at `%s', line %d", + fname, lineno); + result = -1; + continue; + } + *p++ = 0; + flags = p; + if ( component_id != -1) + { + option_info = find_option (component_id, option, GC_BACKEND_ANY); + if (!option_info) + { + gc_error (0, 0, "unknown option at `%s', line %d", + fname, lineno); + result = -1; + } + } + + + /* Parse the optional flags. */ + while (*flags == ' ' || *flags == '\t') + flags++; + if (*flags == '[') + { + flags++; + p = strchr (flags, ']'); + if (!p) + { + gc_error (0, 0, "syntax error in rule at `%s', line %d", + fname, lineno); + result = -1; + continue; + } + *p++ = 0; + value = p; + } + else /* No flags given. */ + { + value = flags; + flags = NULL; + } + + /* Parse the optional value. */ + while (*value == ' ' || *value == '\t') + value++; + for (p=value; *p && !strchr ("\r\n", *p); p++) + ; + if (p == value) + value = empty; /* No value given; let it point to an empty string. */ + else + { + /* Strip trailing white space. */ + *p = 0; + for (p--; p > value && (*p == ' ' || *p == '\t'); p--) + *p = 0; + } + + /* Check flag combinations. */ + if (!flags) + ; + else if (!strcmp (flags, "default")) + { + if (*value) + { + gc_error (0, 0, "flag \"default\" may not be combined " + "with a value at `%s', line %d", + fname, lineno); + result = -1; + } + } + else if (!strcmp (flags, "change")) + ; + else if (!strcmp (flags, "no-change")) + ; + else + { + gc_error (0, 0, "unknown flag at `%s', line %d", + fname, lineno); + result = -1; + } + + + /* Check whether the key matches but do this only if we are not + running in syntax check mode. */ + if ( update + && !result + && (got_match || (key && key_matches_user_or_group (key))) ) + { + int newflags = 0; + + got_match = 1; + + /* Apply the flags from gpgconf.conf. */ + if (!flags) + ; + else if (!strcmp (flags, "default")) + newflags |= GC_OPT_FLAG_DEFAULT; + else if (!strcmp (flags, "no-change")) + option_info->flags |= GC_OPT_FLAG_NO_CHANGE; + else if (!strcmp (flags, "change")) + option_info->flags &= ~GC_OPT_FLAG_NO_CHANGE; + + if (defaults) + { + assert (component_id >= 0 && component_id < GC_COMPONENT_NR); + used_components[component_id] = 1; + + /* Here we explicitly allow to update the value again. */ + if (newflags) + { + option_info->new_flags = 0; + } + if (*value) + { + xfree (option_info->new_value); + option_info->new_value = NULL; + } + change_one_value (option_info, runtime, newflags, value); + } + } + } + + if (length < 0 || ferror (config)) + { + gc_error (0, errno, "error reading from `%s'", fname); + result = -1; + } + if (fclose (config) && ferror (config)) + gc_error (0, errno, "error closing `%s'", fname); + + xfree (line); + + /* If it all worked, process the options. */ + if (!result && update && defaults) + { + /* We need to switch off the runtime update, so that we can do + it later all at once. */ + int save_opt_runtime = opt.runtime; + opt.runtime = 0; + + for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++) + { + gc_component_change_options (component_id, NULL); + } + opt.runtime = save_opt_runtime; + + if (opt.runtime) + { + for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++) + if (runtime[backend_id] && gc_backend[backend_id].runtime_change) + (*gc_backend[backend_id].runtime_change) (); + } + } + + return result; +} diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 87ba45ae1..6d73e88e0 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -44,6 +44,8 @@ enum cmd_and_opt_values aListComponents, aListOptions, aChangeOptions, + aApplyDefaults, + aCheckConfig }; @@ -56,6 +58,10 @@ static ARGPARSE_OPTS opts[] = { aListComponents, "list-components", 256, N_("list all components") }, { aListOptions, "list-options", 256, N_("|COMPONENT|list options") }, { aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") }, + { aApplyDefaults, "apply-defaults", 256, + N_("apply global default values") }, + { aCheckConfig, "check-config", 256, + N_("check global configuration file") }, { 301, NULL, 0, N_("@\nOptions:\n ") }, @@ -64,7 +70,6 @@ static ARGPARSE_OPTS opts[] = { oQuiet, "quiet", 0, N_("quiet") }, { oDryRun, "dry-run", 0, N_("do not make any changes") }, { oRuntime, "runtime", 0, N_("activate changes at runtime, if possible") }, - /* hidden options */ { oNoVerbose, "no-verbose", 0, "@"}, {0} @@ -149,6 +154,8 @@ main (int argc, char **argv) case aListComponents: case aListOptions: case aChangeOptions: + case aApplyDefaults: + case aCheckConfig: cmd = pargs.r_opt; break; @@ -189,11 +196,34 @@ main (int argc, char **argv) exit (1); } gc_component_retrieve_options (idx); + if (gc_process_gpgconf_conf (NULL, 1, 0)) + exit (1); if (cmd == aListOptions) gc_component_list_options (idx, stdout); else - gc_component_change_options (idx, stdin); + gc_component_change_options (idx, stdin); } + break; + + case aCheckConfig: + if (gc_process_gpgconf_conf (fname, 0, 0)) + exit (1); + break; + + case aApplyDefaults: + if (fname) + { + fputs (_("usage: gpgconf [options] "), stderr); + putc ('\n',stderr); + fputs (_("No argument allowed"), stderr); + putc ('\n',stderr); + exit (2); + } + gc_component_retrieve_options (-1); + if (gc_process_gpgconf_conf (NULL, 1, 1)) + exit (1); + break; + } return 0; diff --git a/tools/gpgconf.h b/tools/gpgconf.h index c083c26aa..b3ff258b0 100644 --- a/tools/gpgconf.h +++ b/tools/gpgconf.h @@ -56,4 +56,8 @@ void gc_component_list_options (int component, FILE *out); /* Read the modifications from IN and apply them. */ void gc_component_change_options (int component, FILE *in); +/* Process global configuration file. */ +int gc_process_gpgconf_conf (const char *fname, int update, int defaults); + + #endif /*GPGCONF_H*/ |