gpgconf: Fix --list-options for forced options

* tools/gpgconf-comp.c: Remove assert.h and replace all assert calls
by log_assert.
(known_options_gpg): Add "keyserver" as invisible.  Remove "options".
(known_pseudo_options_gpg, known_pseudo_options_gpgsm): New.
(gc_component): Add field known_pseudo_options.
(struct read_line_wrapper_parm_s): New.
(read_line_wrapper): New.
(retrieve_options_from_program): Use read_line_wrapper to handle
pseudo options.
(retrieve_options_from_program): Ignore to be ignored options.  Add
failsafe code to avoid calling percent_escape with NULL.
--

GnuPG-bug-id: 5341,5800

Due to reading the list of options from the argparser of the
component, we had no more information about the pseudo options and
thus could not emit them.  Well, there is just one pseudo option right
now, but with this change we have a generalized concept for them:
Pseudo options are used to convey information from a component to
gpgconf; for example build-in values.

This is a combined backport of patches to master.
This commit is contained in:
Werner Koch 2022-01-26 13:32:17 +01:00
parent d8e6d1e9ed
commit 85300587cc
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 126 additions and 87 deletions

View File

@ -1912,31 +1912,8 @@ gpgconf_list (const char *configfile)
{
char *configfile_esc = percent_escape (configfile, NULL);
es_printf ("%s-%s.conf:%lu:\"%s\n",
GPGCONF_NAME, GPG_NAME,
GC_OPT_FLAG_DEFAULT,
configfile_esc ? configfile_esc : "/dev/null");
es_printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("default-key:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("try-secret-key:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("auto-key-locate:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("auto-key-import:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("include-key-block:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("auto-key-retrieve:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("group:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("compliance:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "gnupg");
es_printf ("default-new-key-algo:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("trust-model:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("disable-dirmngr:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("max-cert-depth:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("completes-needed:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("marginals-needed:%lu:\n", GC_OPT_FLAG_NONE);
/* The next one is an info only item and should match the macros at
the top of keygen.c */

View File

@ -1715,39 +1715,20 @@ main ( int argc, char **argv)
{
case aGPGConfList:
{ /* List options and default values in the GPG Conf format. */
char *config_filename_esc = percent_escape (opt.config_filename, NULL);
es_printf ("%s-%s.conf:%lu:\"%s\n",
GPGCONF_NAME, GPGSM_NAME,
GC_OPT_FLAG_DEFAULT, config_filename_esc);
xfree (config_filename_esc);
es_printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("disable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("enable-crl-checks:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("disable-trusted-cert-crl-check:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("enable-ocsp:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("include-certs:%lu:%d:\n", GC_OPT_FLAG_DEFAULT,
DEFAULT_INCLUDE_CERTS);
es_printf ("disable-policy-checks:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("auto-issuer-key-retrieve:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("disable-dirmngr:%lu:\n", GC_OPT_FLAG_NONE);
es_printf ("cipher-algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT,
DEFAULT_CIPHER_ALGO);
es_printf ("p12-charset:%lu:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("default-key:%lu:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_DEFAULT);
es_printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE);
/* The next one is an info only item and should match what
proc_parameters actually implements. */
es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT,
"RSA-3072");
es_printf ("compliance:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "gnupg");
}
break;
case aGPGConfTest:

View File

@ -1,7 +1,7 @@
/* gpgconf-comp.c - Configuration utility for GnuPG.
* Copyright (C) 2004, 2007-2011 Free Software Foundation, Inc.
* Copyright (C) 2016 Werner Koch
* Copyright (C) 2020, 2021 g10 Code GmbH
* Copyright (C) 2020-2022 g10 Code GmbH
*
* This file is part of GnuPG.
*
@ -28,7 +28,6 @@
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <stdarg.h>
@ -396,14 +395,13 @@ static known_option_t known_options_gpg[] =
{ "encrypt-to", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC },
{ "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED,
GC_ARG_TYPE_ALIAS_LIST},
{ "options", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE,
GC_ARG_TYPE_FILENAME },
{ "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT },
{ "default-new-key-algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
{ "trust-model", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
{ "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED },
{ "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED,
GC_ARG_TYPE_FILENAME },
{ "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
{ "auto-key-locate", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED },
{ "auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT },
{ "no-auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
@ -412,12 +410,17 @@ static known_option_t known_options_gpg[] =
{ "completes-needed", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
{ "marginals-needed", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
/* The next is a pseudo option which we read via --gpgconf-list */
{ "default_pubkey_algo",
(GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE },
/* The next is a pseudo option which we read via --gpgconf-list.
* The meta information is taken from the table below. */
{ "default_pubkey_algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
{ NULL }
};
static const char *known_pseudo_options_gpg[] =
{/* v-- ARGPARSE_TYPE_STRING */
"default_pubkey_algo:0:2:@:",
NULL
};
/* The known options of the GC_COMPONENT_GPGSM component. */
@ -448,12 +451,18 @@ static known_option_t known_options_gpgsm[] =
{ "cipher-algo", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED },
{ "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT },
/* Pseudo option follows. */
{ "default_pubkey_algo",
(GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE },
/* Pseudo option follows. See also table below. */
{ "default_pubkey_algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE },
{ NULL }
};
static const char *known_pseudo_options_gpgsm[] =
{/* v-- ARGPARSE_TYPE_STRING */
"default_pubkey_algo:0:2:@:",
NULL
};
/* The known options of the GC_COMPONENT_DIRMNGR component. */
@ -516,7 +525,7 @@ struct gc_option_s
unsigned int opt_arg:1; /* The option's argument is optional. */
unsigned int runtime:1; /* The option is runtime changeable. */
unsigned int active:1; /* Has been announced in gpgconf-list. */
unsigned int gpgconf_list:1; /* Has been announced in gpgconf-list. */
unsigned int has_default:1; /* The option has a default value. */
unsigned int def_in_desc:1; /* The default is in the descrition. */
@ -584,6 +593,9 @@ static struct
/* The static table of known options for this component. */
known_option_t *known_options;
/* The static table of known pseudo options for this component or NULL. */
const char **known_pseudo_options;
/* The runtime change callback. If KILLFLAG is true the component
is killed and not just reloaded. */
void (*runtime_change) (int killflag);
@ -605,23 +617,23 @@ static struct
{ GPG_NAME, GPG_DISP_NAME, "gnupg", N_("OpenPGP"),
GNUPG_MODULE_NAME_GPG, GPG_NAME ".conf",
known_options_gpg },
known_options_gpg, known_pseudo_options_gpg },
{ GPGSM_NAME, GPGSM_DISP_NAME, "gnupg", N_("S/MIME"),
GNUPG_MODULE_NAME_GPGSM, GPGSM_NAME ".conf",
known_options_gpgsm },
known_options_gpgsm, known_pseudo_options_gpgsm },
{ GPG_AGENT_NAME, GPG_AGENT_DISP_NAME, "gnupg", N_("Private Keys"),
GNUPG_MODULE_NAME_AGENT, GPG_AGENT_NAME ".conf",
known_options_gpg_agent, gpg_agent_runtime_change },
known_options_gpg_agent, NULL, gpg_agent_runtime_change },
{ SCDAEMON_NAME, SCDAEMON_DISP_NAME, "gnupg", N_("Smartcards"),
GNUPG_MODULE_NAME_SCDAEMON, SCDAEMON_NAME ".conf",
known_options_scdaemon, scdaemon_runtime_change},
known_options_scdaemon, NULL, scdaemon_runtime_change},
{ DIRMNGR_NAME, DIRMNGR_DISP_NAME, "gnupg", N_("Network"),
GNUPG_MODULE_NAME_DIRMNGR, DIRMNGR_NAME ".conf",
known_options_dirmngr, dirmngr_runtime_change },
known_options_dirmngr, NULL, dirmngr_runtime_change },
{ "pinentry", "Pinentry", "gnupg", N_("Passphrase Entry"),
GNUPG_MODULE_NAME_PINENTRY, NULL,
@ -1441,7 +1453,7 @@ gc_component_list_options (int component, estream_t out)
{
/* Do not output unknown or internal options. */
if (!option->is_header
&& (!option->active || option->level == GC_LEVEL_INTERNAL))
&& option->level == GC_LEVEL_INTERNAL)
continue;
if (option->is_header)
@ -1517,6 +1529,47 @@ find_option (gc_component_id_t component, const char *name)
struct read_line_wrapper_parm_s
{
const char *pgmname;
estream_t fp;
char *line;
size_t line_len;
const char **extra_lines;
int extra_lines_idx;
char *extra_line_buffer;
};
/* Helper for retrieve_options_from_program. */
static ssize_t
read_line_wrapper (struct read_line_wrapper_parm_s *parm)
{
ssize_t length;
const char *extra_line;
if (parm->fp)
{
length = es_read_line (parm->fp, &parm->line, &parm->line_len, NULL);
if (length > 0)
return length;
if (length < 0 || es_ferror (parm->fp))
gc_error (1, errno, "error reading from %s", parm->pgmname);
if (es_fclose (parm->fp))
gc_error (1, errno, "error closing %s", parm->pgmname);
/* EOF seen. */
parm->fp = NULL;
}
/* Return the made up lines. */
if (!parm->extra_lines
|| !(extra_line = parm->extra_lines[parm->extra_lines_idx]))
return -1; /* This is really the EOF. */
parm->extra_lines_idx++;
xfree (parm->extra_line_buffer);
parm->extra_line_buffer = xstrdup (extra_line);
return strlen (parm->extra_line_buffer);
}
/* Retrieve the options for the component COMPONENT. With
* ONLY_INSTALLED set components which are not installed are silently
* ignored. */
@ -1532,7 +1585,7 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
known_option_t *known_option;
gc_option_t *option;
char *line = NULL;
size_t line_len = 0;
size_t line_len;
ssize_t length;
const char *config_name;
gnupg_argparse_t pargs;
@ -1545,6 +1598,8 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
size_t opt_info_used = 0; /* Its current length. */
size_t opt_info_size = 0; /* Its allocated length. */
int i;
struct read_line_wrapper_parm_s read_line_parm;
int pseudo_count;
pgmname = (gc_component[component].module_name
? gnupg_module_name (gc_component[component].module_name)
@ -1567,13 +1622,31 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
pgmname, gpg_strerror (err));
}
while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0)
read_line_parm.pgmname = pgmname;
read_line_parm.fp = outfp;
read_line_parm.line = line;
read_line_parm.line_len = line_len = 0;
read_line_parm.extra_line_buffer = NULL;
read_line_parm.extra_lines = gc_component[component].known_pseudo_options;
read_line_parm.extra_lines_idx = 0;
pseudo_count = 0;
while ((length = read_line_wrapper (&read_line_parm)) > 0)
{
char *fields[4];
char *optname, *optdesc;
unsigned int optflags;
int short_opt;
gc_arg_type_t arg_type;
int pseudo = 0;
if (read_line_parm.extra_line_buffer)
{
line = read_line_parm.extra_line_buffer;
pseudo = 1;
pseudo_count++;
}
else
line = read_line_parm.line;
/* Strip newline and carriage return, if present. */
while (length > 0
@ -1589,14 +1662,13 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
optname = fields[0];
short_opt = atoi (fields[1]);
if (short_opt < 1)
if (short_opt < 1 && !pseudo)
{
gc_error (0,0, "WARNING: bad short option in option table of '%s'\n",
pgmname);
continue;
}
optflags = strtoul (fields[2], NULL, 10);
if ((optflags & ARGPARSE_OPT_HEADER))
known_option = NULL; /* We want all header-only options. */
@ -1628,7 +1700,7 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
sizeof *opt_info);
}
/* The +1 here accounts for the two items we are going to add to
* the global string table. */
* the global string table. */
if (string_array_used + 1 >= string_array_size)
{
string_array_size += 256;
@ -1649,11 +1721,14 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
* known_option_s for this because that one does not carry
* header lines and it might also be problematic to use such
* static tables for caching options and default values. */
opt_table[opt_table_used].long_opt = optname;
opt_table[opt_table_used].short_opt = short_opt;
opt_table[opt_table_used].description = optdesc;
opt_table[opt_table_used].flags = optflags;
opt_table_used++;
if (!pseudo)
{
opt_table[opt_table_used].long_opt = optname;
opt_table[opt_table_used].short_opt = short_opt;
opt_table[opt_table_used].description = optdesc;
opt_table[opt_table_used].flags = optflags;
opt_table_used++;
}
/* Note that as per argparser specs the opt_table uses "@" to
* specifify an empty description. In the DESC script of
@ -1674,6 +1749,8 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
default: arg_type = GC_ARG_TYPE_NONE; break;
}
opt_info[opt_info_used].arg_type = arg_type;
if (pseudo) /* Pseudo options are always no_change. */
opt_info[opt_info_used].no_change = 1;
if ((optflags & ARGPARSE_OPT_HEADER))
opt_info[opt_info_used].is_header = 1;
@ -1696,11 +1773,10 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
}
opt_info_used++;
}
if (length < 0 || es_ferror (outfp))
gc_error (1, errno, "error reading from %s", pgmname);
if (es_fclose (outfp))
gc_error (1, errno, "error closing %s", pgmname);
log_assert (opt_table_used == opt_info_used);
xfree (read_line_parm.extra_line_buffer);
line = read_line_parm.line;
line_len = read_line_parm.line_len;
log_assert (opt_table_used + pseudo_count == opt_info_used);
err = gnupg_wait_process (pgmname, pid, 1, &exitcode);
if (err)
@ -1782,18 +1858,20 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
option = find_option (component, line);
if (option)
{
if (option->active)
if (option->gpgconf_list)
gc_error (1, errno,
"option %s returned twice from \"%s --gpgconf-list\"",
line, pgmname);
option->active = 1;
option->gpgconf_list = 1;
/* Runtime is duplicated - see above. */
option->runtime = !!(flags & GC_OPT_FLAG_RUNTIME);
option->has_default = !!(flags & GC_OPT_FLAG_DEFAULT);
option->def_in_desc = !!(flags & GC_OPT_FLAG_DEF_DESC);
option->no_arg_desc = !!(flags & GC_OPT_FLAG_NO_ARG_DESC);
option->no_change = !!(flags & GC_OPT_FLAG_NO_CHANGE);
if ((flags & GC_OPT_FLAG_DEFAULT))
option->has_default = 1;
if ((flags & GC_OPT_FLAG_DEF_DESC))
option->def_in_desc = 1;
if ((flags & GC_OPT_FLAG_NO_ARG_DESC))
option->no_arg_desc = 1;
if ((flags & GC_OPT_FLAG_NO_CHANGE))
option->no_change = 1;
if (default_value && *default_value)
option->default_value = xstrdup (default_value);
@ -1846,6 +1924,8 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
/* pargs.r_type? pargs.r.ret_str: "[cmdline]"); */
continue;
}
if ((pargs.r_type & ARGPARSE_OPT_IGNORE))
continue;
/* We only have the short option. Search in the option table
* for the long option name. */
@ -1886,7 +1966,10 @@ retrieve_options_from_program (gc_component_id_t component, int only_installed)
opt_value = xasprintf ("%lu", pargs.r.ret_ulong);
break;
case ARGPARSE_TYPE_STRING:
opt_value = xasprintf ("\"%s", gc_percent_escape (pargs.r.ret_str));
if (!pargs.r.ret_str)
opt_value = xstrdup ("\"(none)"); /* We should not see this. */
else
opt_value = xasprintf ("\"%s", gc_percent_escape (pargs.r.ret_str));
break;
default: /* ARGPARSE_TYPE_NONE or any unknown type. */
opt_value = xstrdup ("1"); /* Make sure we have some value. */
@ -1955,9 +2038,7 @@ option_check_validity (gc_component_id_t component,
{
char *arg;
if (!option->active)
gc_error (1, 0, "option %s not supported by component %s",
option->name, gc_component[component].name);
(void)component;
if (option->new_flags || option->new_value)
gc_error (1, 0, "option %s already changed", option->name);
@ -2329,7 +2410,7 @@ change_options_program (gc_component_id_t component,
else if (gc_arg_type[option->arg_type].fallback
== GC_ARG_TYPE_NONE)
{
assert (*arg == '1');
log_assert (*arg == '1');
gpgrt_fprintf (src_file, "%s\n", option->name);
if (gpgrt_ferror (src_file))
goto change_one_err;
@ -2379,7 +2460,7 @@ change_options_program (gc_component_id_t component,
arg = end;
}
assert (arg == NULL || *arg == '\0' || *arg == ',');
log_assert (arg == NULL || *arg == '\0' || *arg == ',');
if (arg && *arg == ',')
arg++;
}
@ -2479,7 +2560,7 @@ change_one_value (gc_component_id_t component,
/* We convert the number to a list of 1's for convenient
list handling. */
assert (new_value_nr > 0);
log_assert (new_value_nr > 0);
option->new_value = xmalloc ((2 * (new_value_nr - 1) + 1) + 1);
str = option->new_value;
*(str++) = '1';