1
0
mirror of git://git.gnupg.org/gnupg.git synced 2024-12-22 10:19:57 +01:00

gpgconf: New command --apply-profile.

* tools/gpgconf.c (aApplyProfile): New.
(opts): New command --apply-profile.
(main): Implement that command.
* tools/gpgconf-comp.c (option_check_validity): Add arg VERBATIM.
(change_options_program): Ditto.
(change_one_value): Ditto.
(gc_component_change_options): Ditto.
(gc_apply_profile): New.

--

Here is an example for a profile

--8<---------------cut here---------------start------------->8---
# foo.prf - Sample profile

[gpg]
compliance de-vs
default-new-key-algo brainpoolP256r1+brainpoolP256r1

[gpgsm]
enable-crl-checks

[gpg-agent]
default-cache-ttl 900
max-cache-ttl [] 3600
no-allow-mark-trusted
no-allow-external-cache
enforce-passphrase-constraints
min-passphrase-len 9
min-passphrase-nonalpha 0

[dirmngr]
keyserver hkp://keys.gnupg.net
allow-ocsp
--8<---------------cut here---------------end--------------->8---

Note that flags inside of brackets are allowed after the option name.
The only defined flag for now is "[default]".  In case the value
starts with a bracket, it is possible to insert "[]" as a nop-flag.

Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
Werner Koch 2016-12-16 16:00:15 +01:00
parent 6ca3c28da4
commit 76cd64a5ba
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
6 changed files with 293 additions and 38 deletions

View File

@ -40,6 +40,8 @@ helpfiles = help.txt help.be.txt help.ca.txt help.cs.txt \
help.pt_BR.txt help.ro.txt help.ru.txt help.sk.txt \ help.pt_BR.txt help.ro.txt help.ru.txt help.sk.txt \
help.sv.txt help.tr.txt help.zh_CN.txt help.zh_TW.txt help.sv.txt help.tr.txt help.zh_CN.txt help.zh_TW.txt
profiles =
EXTRA_DIST = samplekeys.asc mksamplekeys com-certs.pem qualified.txt \ EXTRA_DIST = samplekeys.asc mksamplekeys com-certs.pem qualified.txt \
gnupg-logo.eps gnupg-logo.pdf gnupg-logo.png gnupg-logo-tr.png \ gnupg-logo.eps gnupg-logo.pdf gnupg-logo.png gnupg-logo-tr.png \
gnupg-module-overview.png gnupg-module-overview.pdf \ gnupg-module-overview.png gnupg-module-overview.pdf \
@ -54,7 +56,7 @@ BUILT_SOURCES = gnupg-module-overview.png gnupg-module-overview.pdf \
info_TEXINFOS = gnupg.texi info_TEXINFOS = gnupg.texi
dist_pkgdata_DATA = $(helpfiles) dist_pkgdata_DATA = $(helpfiles) $(profiles)
nobase_dist_doc_DATA = FAQ DETAILS HACKING DCO TRANSLATE OpenPGP KEYSERVER \ nobase_dist_doc_DATA = FAQ DETAILS HACKING DCO TRANSLATE OpenPGP KEYSERVER \
$(examples) $(examples)

View File

@ -8,7 +8,7 @@
# Empty lines and comment lines, indicated by a hash mark as first non # Empty lines and comment lines, indicated by a hash mark as first non
# white space character, are ignored. The line is separated by white # white space character, are ignored. The line is separated by white
# space into fields. The first field is used to match the user or # space into fields. The first field is used to match the user or
# group and must start at the first column, the file is processes # group and must start at the first column, the file is processed
# sequential until a matching rule is found. A rule may contain # sequential until a matching rule is found. A rule may contain
# several lines; continuation lines are indicated by a indenting them. # several lines; continuation lines are indicated by a indenting them.
# #
@ -23,7 +23,7 @@
# * - Matches any user. # * - Matches any user.
# All other variants are not defined and reserved for future use. # All other variants are not defined and reserved for future use.
# #
# <component> and <option> are as specified by gpgconf. # <component> and <option> are as specified by gpgconf.
# <flag> may be one of: # <flag> may be one of:
# default - Delete the option so that the default is used. # default - Delete the option so that the default is used.
# no-change - Mark the field as non changeable by gpgconf. # no-change - Mark the field as non changeable by gpgconf.
@ -35,7 +35,7 @@
# gpg-agent min-passphrase-len 6 # gpg-agent min-passphrase-len 6
# #
# * gpg-agent min-passphrase-len [no-change] 8 # * gpg-agent min-passphrase-len [no-change] 8
# gpg-agent min-passphrase-nonalpha [no-change] 1 # gpg-agent min-passphrase-nonalpha [no-change] 1
# gpg-agent max-passphrase-days [no-change] 700 # gpg-agent max-passphrase-days [no-change] 700
# gpg-agent enable-passphrase-history [no-change] # gpg-agent enable-passphrase-history [no-change]
# gpg-agent enforce-passphrase-constraints [default] # gpg-agent enforce-passphrase-constraints [default]
@ -44,7 +44,7 @@
# gpg-agent max-cache-ttl-ssh [no-change] 10800 # gpg-agent max-cache-ttl-ssh [no-change] 10800
# gpg-agent allow-mark-trusted [default] # gpg-agent allow-mark-trusted [default]
# gpg-agent allow-mark-trusted [no-change] # gpg-agent allow-mark-trusted [no-change]
# gpgsm enable-ocsp # gpgsm enable-ocsp
#=========== #===========
# All users in the group "staff" are allowed to change the value for # All users in the group "staff" are allowed to change the value for
# --allow-mark-trusted; gpgconf's default is not to allow a change # --allow-mark-trusted; gpgconf's default is not to allow a change

View File

@ -279,6 +279,15 @@ Change the options of the component @var{component}.
@item --check-options @var{component} @item --check-options @var{component}
Check the options for the component @var{component}. Check the options for the component @var{component}.
@item --apply-profile @var{file}
Apply the configuration settings listed in @var{file} to the
configuration files. If @var{file} has no suffix and no slashes the
command first tries to read a file with the suffix @code{.prf} from
the the data directory (@code{gpgconf --list-dirs datadir}) before it
reads the file verbatim. A profile is divided into sections using the
bracketed component name. Each section then lists the option which
shall go into the respective configuration file.
@item --apply-defaults @item --apply-defaults
Update all configuration files with values taken from the global Update all configuration files with values taken from the global
configuration file (usually @file{/etc/gnupg/gpgconf.conf}). configuration file (usually @file{/etc/gnupg/gpgconf.conf}).

View File

@ -1,6 +1,6 @@
/* gpgconf-comp.c - Configuration utility for GnuPG. /* gpgconf-comp.c - Configuration utility for GnuPG.
* Copyright (C) 2004, 2007, 2008, 2009, 2010, * Copyright (C) 2004, 2007-2011 Free Software Foundation, Inc.
* 2011 Free Software Foundation, Inc. * Copyright (C) 2016 Werner Koch
* *
* This file is part of GnuPG. * This file is part of GnuPG.
* *
@ -703,9 +703,15 @@ static gc_option_t gc_options_gpg[] =
{ "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED, { "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED,
"gnupg", N_("|SPEC|set up email aliases"), "gnupg", N_("|SPEC|set up email aliases"),
GC_ARG_TYPE_ALIAS_LIST, GC_BACKEND_GPG }, GC_ARG_TYPE_ALIAS_LIST, GC_BACKEND_GPG },
{ "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, { "options", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
"gnupg", "|FILE|read options from FILE", NULL, NULL,
GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG }, GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG },
{ "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
NULL, NULL,
GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
{ "default-new-key-algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
NULL, NULL,
GC_ARG_TYPE_STRING, GC_BACKEND_GPG },
{ "default_pubkey_algo", { "default_pubkey_algo",
(GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE, (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE,
NULL, NULL, NULL, NULL,
@ -816,6 +822,9 @@ static gc_option_t gc_options_gpgsm[] =
{ "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, { "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC,
"gnupg", "never consult a CRL", "gnupg", "never consult a CRL",
GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
{ "enable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
NULL, NULL,
GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
{ "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, { "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT,
"gnupg", N_("do not check CRLs for root certificates"), "gnupg", N_("do not check CRLs for root certificates"),
GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM },
@ -2333,11 +2342,13 @@ gc_component_retrieve_options (int component)
/* Perform a simple validity check based on the type. Return in /* 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 * NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
type GC_ARG_TYPE_NONE. */ * type GC_ARG_TYPE_NONE. If VERBATIM is set the profile parsing mode
* is used. */
static void static void
option_check_validity (gc_option_t *option, unsigned long flags, option_check_validity (gc_option_t *option, unsigned long flags,
char *new_value, unsigned long *new_value_nr) char *new_value, unsigned long *new_value_nr,
int verbatim)
{ {
char *arg; char *arg;
@ -2391,17 +2402,17 @@ option_check_validity (gc_option_t *option, unsigned long flags,
arg = new_value; arg = new_value;
do do
{ {
if (*arg == '\0' || *arg == ',') if (*arg == '\0' || (*arg == ',' && !verbatim))
{ {
if (!(option->flags & GC_OPT_FLAG_ARG_OPT)) if (!(option->flags & GC_OPT_FLAG_ARG_OPT))
gc_error (1, 0, "argument required for option %s", option->name); gc_error (1, 0, "argument required for option %s", option->name);
if (*arg == ',' && !(option->flags & GC_OPT_FLAG_LIST)) if (*arg == ',' && !verbatim && !(option->flags & GC_OPT_FLAG_LIST))
gc_error (1, 0, "list found for non-list option %s", option->name); gc_error (1, 0, "list found for non-list option %s", option->name);
} }
else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_STRING) else if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_STRING)
{ {
if (*arg != '"') if (*arg != '"' && !verbatim)
gc_error (1, 0, "string argument for option %s must begin " gc_error (1, 0, "string argument for option %s must begin "
"with a quote (\") character", option->name); "with a quote (\") character", option->name);
@ -2409,7 +2420,7 @@ option_check_validity (gc_option_t *option, unsigned long flags,
we do not quote arguments in configuration files, and we do not quote arguments in configuration files, and
thus no argument is indistinguishable from the empty thus no argument is indistinguishable from the empty
string. */ string. */
if (arg[1] == '\0' || arg[1] == ',') if (arg[1] == '\0' || (arg[1] == ',' && !verbatim))
gc_error (1, 0, "empty string argument for option %s is " gc_error (1, 0, "empty string argument for option %s is "
"currently not allowed. Please report this!", "currently not allowed. Please report this!",
option->name); option->name);
@ -2426,7 +2437,7 @@ option_check_validity (gc_option_t *option, unsigned long flags,
gc_error (1, errno, "invalid argument for option %s", gc_error (1, errno, "invalid argument for option %s",
option->name); option->name);
if (*arg != '\0' && *arg != ',') if (*arg != '\0' && (*arg != ',' || verbatim))
gc_error (1, 0, "garbage after argument for option %s", gc_error (1, 0, "garbage after argument for option %s",
option->name); option->name);
} }
@ -2442,17 +2453,18 @@ option_check_validity (gc_option_t *option, unsigned long flags,
gc_error (1, errno, "invalid argument for option %s", gc_error (1, errno, "invalid argument for option %s",
option->name); option->name);
if (*arg != '\0' && *arg != ',') if (*arg != '\0' && (*arg != ',' || verbatim))
gc_error (1, 0, "garbage after argument for option %s", gc_error (1, 0, "garbage after argument for option %s",
option->name); option->name);
} }
arg = strchr (arg, ','); arg = verbatim? strchr (arg, ',') : NULL;
if (arg) if (arg)
arg++; arg++;
} }
while (arg && *arg); while (arg && *arg);
} }
#ifdef HAVE_W32_SYSTEM #ifdef HAVE_W32_SYSTEM
int int
copy_file (const char *src_name, const char *dst_name) copy_file (const char *src_name, const char *dst_name)
@ -2816,11 +2828,13 @@ change_options_file (gc_component_t component, gc_backend_t backend,
/* Create and verify the new configuration file for the specified /* Create and verify the new configuration file for the specified
backend and component. Returns 0 on success and -1 on error. */ * backend and component. Returns 0 on success and -1 on error. If
* VERBATIM is set the profile mode is used. */
static int static int
change_options_program (gc_component_t component, gc_backend_t backend, change_options_program (gc_component_t component, gc_backend_t backend,
char **src_filenamep, char **dest_filenamep, char **src_filenamep, char **dest_filenamep,
char **orig_filenamep) char **orig_filenamep,
int verbatim)
{ {
static const char marker[] = "###+++--- " GPGCONF_DISP_NAME " ---+++###"; static const char marker[] = "###+++--- " GPGCONF_DISP_NAME " ---+++###";
/* True if we are within the marker in the config file. */ /* True if we are within the marker in the config file. */
@ -3008,15 +3022,20 @@ change_options_program (gc_component_t component, gc_backend_t backend,
{ {
char *end; char *end;
assert (*arg == '"'); if (!verbatim)
arg++; {
log_assert (*arg == '"');
arg++;
end = strchr (arg, ','); end = strchr (arg, ',');
if (end) if (end)
*end = '\0'; *end = '\0';
}
else
end = NULL;
fprintf (src_file, "%s %s\n", option->name, fprintf (src_file, "%s %s\n", option->name,
percent_deescape (arg)); verbatim? arg : percent_deescape (arg));
if (ferror (src_file)) if (ferror (src_file))
goto change_one_err; goto change_one_err;
@ -3117,14 +3136,15 @@ change_options_program (gc_component_t component, gc_backend_t backend,
/* Common code for gc_component_change_options and /* Common code for gc_component_change_options and
gc_process_gpgconf_conf. */ * gc_process_gpgconf_conf. If VERBATIM is set the profile parsing
* mode is used. */
static void static void
change_one_value (gc_option_t *option, int *runtime, change_one_value (gc_option_t *option, int *runtime,
unsigned long flags, char *new_value) unsigned long flags, char *new_value, int verbatim)
{ {
unsigned long new_value_nr = 0; unsigned long new_value_nr = 0;
option_check_validity (option, flags, new_value, &new_value_nr); option_check_validity (option, flags, new_value, &new_value_nr, verbatim);
if (option->flags & GC_OPT_FLAG_RUNTIME) if (option->flags & GC_OPT_FLAG_RUNTIME)
runtime[option->backend] = 1; runtime[option->backend] = 1;
@ -3158,9 +3178,10 @@ change_one_value (gc_option_t *option, int *runtime,
/* Read the modifications from IN and apply them. If IN is NULL the /* Read the modifications from IN and apply them. If IN is NULL the
modifications are expected to already have been set to the global modifications are expected to already have been set to the global
table. */ table. If VERBATIM is set the profile mode is used. */
void void
gc_component_change_options (int component, estream_t in, estream_t out) gc_component_change_options (int component, estream_t in, estream_t out,
int verbatim)
{ {
int err = 0; int err = 0;
int runtime[GC_BACKEND_NR]; int runtime[GC_BACKEND_NR];
@ -3247,7 +3268,7 @@ gc_component_change_options (int component, estream_t in, estream_t out)
continue; continue;
} }
change_one_value (option, runtime, flags, new_value); change_one_value (option, runtime, flags, new_value, 0);
} }
} }
@ -3271,7 +3292,8 @@ gc_component_change_options (int component, estream_t in, estream_t out)
err = change_options_program (component, option->backend, err = change_options_program (component, option->backend,
&src_filename[option->backend], &src_filename[option->backend],
&dest_filename[option->backend], &dest_filename[option->backend],
&orig_filename[option->backend]); &orig_filename[option->backend],
verbatim);
if (! err) if (! err)
{ {
/* External verification. */ /* External verification. */
@ -3789,7 +3811,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
xfree (option_info->new_value); xfree (option_info->new_value);
option_info->new_value = NULL; option_info->new_value = NULL;
} }
change_one_value (option_info, runtime, newflags, value); change_one_value (option_info, runtime, newflags, value, 0);
} }
} }
} }
@ -3814,7 +3836,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++) for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
{ {
gc_component_change_options (component_id, NULL, NULL); gc_component_change_options (component_id, NULL, NULL, 0);
} }
opt.runtime = save_opt_runtime; opt.runtime = save_opt_runtime;
@ -3829,3 +3851,210 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults,
xfree (fname); xfree (fname);
return result; return result;
} }
/*
* Apply the profile FNAME to all known configure files.
*/
gpg_error_t
gc_apply_profile (const char *fname)
{
gpg_error_t err;
char *fname_buffer = NULL;
char *line = NULL;
size_t line_len = 0;
ssize_t length;
estream_t fp;
int lineno = 0;
int runtime[GC_BACKEND_NR];
int backend_id;
int component_id = -1;
int skip_section = 0;
int error_count = 0;
int newflags;
if (!fname)
fname = "-";
for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
runtime[backend_id] = 0;
if (!(!strcmp (fname, "-")
|| strchr (fname, '/')
#ifdef HAVE_W32_SYSTEM
|| strchr (fname, '\\')
#endif
|| strchr (fname, '.')))
{
/* FNAME looks like a standard profile name. Check whether one
* is installed and use that instead of the given file name. */
fname_buffer = xstrconcat (gnupg_datadir (), DIRSEP_S,
fname, ".prf", NULL);
if (!access (fname_buffer, F_OK))
fname = fname_buffer;
}
fp = !strcmp (fname, "-")? es_stdin : es_fopen (fname, "r");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error ("can't open '%s': %s\n", fname, gpg_strerror (err));
return err;
}
if (opt.verbose)
log_info ("applying profile '%s'\n", fname);
err = 0;
while ((length = es_read_line (fp, &line, &line_len, NULL)) > 0)
{
char *name, *flags, *value;
gc_option_t *option_info = NULL;
char *p;
lineno++;
name = line;
while (*name == ' ' || *name == '\t')
name++;
if (!*name || *name == '#' || *name == '\r' || *name == '\n')
continue;
trim_trailing_spaces (name);
/* Check whether this is a new section. */
if (*name == '[')
{
name++;
skip_section = 0;
/* New section: Get the name of the component. */
p = strchr (name, ']');
if (!p)
{
error_count++;
log_info ("%s:%d:%d: error: syntax error in section tag\n",
fname, lineno, (int)(name - line));
skip_section = 1;
continue;
}
*p++ = 0;
if (*p)
log_info ("%s:%d:%d: warning: garbage after section tag\n",
fname, lineno, (int)(p - line));
trim_spaces (name);
component_id = gc_component_find (name);
if (component_id < 0)
{
log_info ("%s:%d:%d: warning: skipping unknown section '%s'\n",
fname, lineno, (int)(name - line), name );
skip_section = 1;
}
continue;
}
if (skip_section)
continue;
if (component_id < 0)
{
error_count++;
log_info ("%s:%d:%d: error: not in a valid section\n",
fname, lineno, (int)(name - line));
skip_section = 1;
continue;
}
/* Parse the option name. */
for (p = name; *p && !spacep (p); p++)
;
*p++ = 0;
value = p;
option_info = find_option (component_id, name, GC_BACKEND_ANY);
if (!option_info)
{
error_count++;
log_info ("%s:%d:%d: error: unknown option '%s' in section '%s'\n",
fname, lineno, (int)(name - line),
name, gc_component[component_id].name);
continue;
}
/* Parse the optional flags. */
trim_spaces (value);
flags = value;
if (*flags == '[')
{
flags++;
p = strchr (flags, ']');
if (!p)
{
log_info ("%s:%d:%d: warning: invalid flag specification\n",
fname, lineno, (int)(p - line));
continue;
}
*p++ = 0;
value = p;
trim_spaces (value);
}
else /* No flags given. */
flags = NULL;
/* Set required defaults. */
if (gc_arg_type[option_info->arg_type].fallback == GC_ARG_TYPE_NONE
&& !*value)
value = "1";
/* Check and save this option. */
newflags = 0;
if (flags && !strcmp (flags, "default"))
newflags |= GC_OPT_FLAG_DEFAULT;
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, 1);
}
if (length < 0 || es_ferror (fp))
{
err = gpg_error_from_syserror ();
error_count++;
log_error (_("%s:%u: read error: %s\n"),
fname, lineno, gpg_strerror (err));
}
if (es_fclose (fp))
log_error (_("error closing '%s'\n"), fname);
if (error_count)
log_error (_("error parsing '%s'\n"), fname);
xfree (line);
/* If it all worked, process the options. */
if (!err)
{
/* 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, NULL, 1);
}
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) (0);
}
}
xfree (fname_buffer);
return err;
}

View File

@ -60,6 +60,7 @@ enum cmd_and_opt_values
aKill, aKill,
aCreateSocketDir, aCreateSocketDir,
aRemoveSocketDir, aRemoveSocketDir,
aApplyProfile,
aReload aReload
}; };
@ -76,6 +77,8 @@ static ARGPARSE_OPTS opts[] =
{ aCheckOptions, "check-options", 256, N_("|COMPONENT|check options") }, { aCheckOptions, "check-options", 256, N_("|COMPONENT|check options") },
{ aApplyDefaults, "apply-defaults", 256, { aApplyDefaults, "apply-defaults", 256,
N_("apply global default values") }, N_("apply global default values") },
{ aApplyProfile, "apply-profile", 256,
N_("|FILE|update configuration files using FILE") },
{ aListDirs, "list-dirs", 256, { aListDirs, "list-dirs", 256,
N_("get the configuration directories for @GPGCONF@") }, N_("get the configuration directories for @GPGCONF@") },
{ aListConfig, "list-config", 256, { aListConfig, "list-config", 256,
@ -495,6 +498,7 @@ main (int argc, char **argv)
case aChangeOptions: case aChangeOptions:
case aCheckOptions: case aCheckOptions:
case aApplyDefaults: case aApplyDefaults:
case aApplyProfile:
case aListConfig: case aListConfig:
case aCheckConfig: case aCheckConfig:
case aQuerySWDB: case aQuerySWDB:
@ -568,7 +572,8 @@ main (int argc, char **argv)
if (cmd == aListOptions) if (cmd == aListOptions)
gc_component_list_options (idx, get_outfp (&outfp)); gc_component_list_options (idx, get_outfp (&outfp));
else if (cmd == aChangeOptions) else if (cmd == aChangeOptions)
gc_component_change_options (idx, es_stdin, get_outfp (&outfp)); gc_component_change_options (idx, es_stdin,
get_outfp (&outfp), 0);
} }
} }
break; break;
@ -659,6 +664,12 @@ main (int argc, char **argv)
exit (1); exit (1);
break; break;
case aApplyProfile:
gc_component_retrieve_options (-1);
if (gc_apply_profile (fname))
exit (1);
break;
case aListDirs: case aListDirs:
/* Show the system configuration directories for gpgconf. */ /* Show the system configuration directories for gpgconf. */
get_outfp (&outfp); get_outfp (&outfp);

View File

@ -72,7 +72,8 @@ void gc_component_retrieve_options (int component);
void gc_component_list_options (int component, estream_t out); void gc_component_list_options (int component, estream_t out);
/* Read the modifications from IN and apply them. */ /* Read the modifications from IN and apply them. */
void gc_component_change_options (int component, estream_t in, estream_t out); void gc_component_change_options (int component, estream_t in, estream_t out,
int verbatim);
/* Check the options of a single component. Returns 0 if everything /* Check the options of a single component. Returns 0 if everything
is OK. */ is OK. */
@ -83,5 +84,8 @@ int gc_component_check_options (int component, estream_t out,
int gc_process_gpgconf_conf (const char *fname, int update, int defaults, int gc_process_gpgconf_conf (const char *fname, int update, int defaults,
estream_t listfp); estream_t listfp);
/* Apply a profile. */
gpg_error_t gc_apply_profile (const char *fname);
#endif /*GPGCONF_H*/ #endif /*GPGCONF_H*/