diff options
author | DongHun Kwak <dh0128.kwak@samsung.com> | 2022-09-16 07:45:09 +0900 |
---|---|---|
committer | DongHun Kwak <dh0128.kwak@samsung.com> | 2022-09-16 07:45:09 +0900 |
commit | 1e860c1f8b2b0b48617f2ca664aa9acf2fafc8d3 (patch) | |
tree | a945f12e0ecb03e7c1db7db427f469b46f5c71f9 /tools | |
parent | 0fca6c1d0d6088ff4558a6ffc3bc220998699392 (diff) | |
download | gpg2-7e8cdc8644800e891ba4b38bcee30edea8cd6dbf.tar.gz gpg2-7e8cdc8644800e891ba4b38bcee30edea8cd6dbf.tar.bz2 gpg2-7e8cdc8644800e891ba4b38bcee30edea8cd6dbf.zip |
Imported Upstream version 2.2.37upstream/2.2.37
Diffstat (limited to 'tools')
-rw-r--r-- | tools/Makefile.in | 1 | ||||
-rw-r--r-- | tools/gpg-connect-agent.c | 2 | ||||
-rw-r--r-- | tools/gpg-wks-server.c | 82 | ||||
-rw-r--r-- | tools/gpgconf-comp.c | 2645 | ||||
-rw-r--r-- | tools/gpgconf.c | 163 | ||||
-rw-r--r-- | tools/gpgconf.h | 13 | ||||
-rw-r--r-- | tools/gpgtar-create.c | 390 | ||||
-rw-r--r-- | tools/gpgtar-extract.c | 288 | ||||
-rw-r--r-- | tools/gpgtar-list.c | 302 | ||||
-rw-r--r-- | tools/gpgtar.c | 34 | ||||
-rw-r--r-- | tools/gpgtar.h | 13 | ||||
-rw-r--r-- | tools/wks-util.c | 16 |
12 files changed, 1935 insertions, 2014 deletions
diff --git a/tools/Makefile.in b/tools/Makefile.in index 2c98779..e1b7da9 100644 --- a/tools/Makefile.in +++ b/tools/Makefile.in @@ -465,6 +465,7 @@ GNUPG_PINENTRY_PGM = @GNUPG_PINENTRY_PGM@ GNUPG_PROTECT_TOOL_PGM = @GNUPG_PROTECT_TOOL_PGM@ GNUPG_SCDAEMON_PGM = @GNUPG_SCDAEMON_PGM@ GPGKEYS_LDAP = @GPGKEYS_LDAP@ +GPGRT_CONFIG = @GPGRT_CONFIG@ GPG_ERROR_CFLAGS = @GPG_ERROR_CFLAGS@ GPG_ERROR_CONFIG = @GPG_ERROR_CONFIG@ GPG_ERROR_LIBS = @GPG_ERROR_LIBS@ diff --git a/tools/gpg-connect-agent.c b/tools/gpg-connect-agent.c index 250d6ca..eecfd67 100644 --- a/tools/gpg-connect-agent.c +++ b/tools/gpg-connect-agent.c @@ -1177,7 +1177,7 @@ main (int argc, char **argv) early_system_init (); gnupg_rl_initialize (); set_strusage (my_strusage); - log_set_prefix ("gpg-connect-agent", GPGRT_LOG_WITH_PREFIX); + log_set_prefix ("gpg-connect-agent", GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY); /* Make sure that our subsystems are ready. */ i18n_init(); diff --git a/tools/gpg-wks-server.c b/tools/gpg-wks-server.c index 96f0a03..74bb551 100644 --- a/tools/gpg-wks-server.c +++ b/tools/gpg-wks-server.c @@ -808,17 +808,65 @@ get_policy_flags (policy_flags_t policy, const char *mbox) } +/* Create the name for the pending file from NONCE and ADDRSPEC and + * store it at R_NAME. */ +static gpg_error_t +make_pending_fname (const char *nonce, const char *addrspec, char **r_name) +{ + gpg_error_t err = 0; + const char *domain; + char *addrspechash = NULL; + char sha1buf[20]; + + *r_name = NULL; + + domain = addrspec? strchr (addrspec, '@') : NULL; + if (!domain || !domain[1] || domain == addrspec) + { + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + domain++; + if (strchr (domain, '/') || strchr (domain, '\\')) + { + log_info ("invalid domain detected ('%s')\n", domain); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, addrspec, domain - addrspec - 1); + addrspechash = zb32_encode (sha1buf, 8*20); + if (!addrspechash) + { + err = gpg_error_from_syserror (); + goto leave; + } + + *r_name = strconcat (nonce, ".", addrspechash, NULL); + if (!*r_name) + { + err = gpg_error_from_syserror (); + goto leave; + } + + leave: + xfree (addrspechash); + return err; +} + + /* We store the key under the name of the nonce we will then send to * the user. On success the nonce is stored at R_NONCE and the file - * name at R_FNAME. */ + * name at R_FNAME. ADDRSPEC is used as part of the pending file name + * so that the nonce is associated with an address */ static gpg_error_t -store_key_as_pending (const char *dir, estream_t key, +store_key_as_pending (const char *dir, estream_t key, const char *addrspec, char **r_nonce, char **r_fname) { gpg_error_t err; char *dname = NULL; char *fname = NULL; char *nonce = NULL; + char *pendingname = NULL; estream_t outfp = NULL; char buffer[1024]; size_t nbytes, nwritten; @@ -847,7 +895,11 @@ store_key_as_pending (const char *dir, estream_t key, goto leave; } - fname = strconcat (dname, "/", nonce, NULL); + err = make_pending_fname (nonce, addrspec, &pendingname); + if (err) + goto leave; + + fname = strconcat (dname, "/", pendingname, NULL); if (!fname) { err = gpg_error_from_syserror (); @@ -916,6 +968,7 @@ store_key_as_pending (const char *dir, estream_t key, xfree (fname); } xfree (dname); + xfree (pendingname); return err; } @@ -1206,7 +1259,7 @@ process_new_key (server_ctx_t ctx, estream_t key) xfree (nonce); xfree (fname); - err = store_key_as_pending (dname, key, &nonce, &fname); + err = store_key_as_pending (dname, key, sl->mbox, &nonce, &fname); if (err) goto leave; @@ -1362,6 +1415,7 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) char *fnewname = NULL; estream_t key = NULL; char *hash = NULL; + char *pendingname = NULL; const char *domain; const char *s; uidinfo_list_t sl; @@ -1378,7 +1432,21 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) domain = strchr (address, '@'); log_assert (domain && domain[1]); domain++; - fname = make_filename_try (opt.directory, domain, "pending", nonce, NULL); + if (strchr (domain, '/') || strchr (domain, '\\') + || strchr (nonce, '/') || strchr (nonce, '\\')) + { + log_info ("invalid domain or nonce received ('%s', '%s')\n", + domain, nonce); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } + + err = make_pending_fname (nonce, address, &pendingname); + if (err) + goto leave; + + fname = make_filename_try (opt.directory, domain, "pending", pendingname, + NULL); if (!fname) { err = gpg_error_from_syserror (); @@ -1490,6 +1558,7 @@ check_and_publish (server_ctx_t ctx, const char *address, const char *nonce) xfree (fnewname); xfree (fname); xfree (nonce2); + xfree (pendingname); return err; } @@ -1716,7 +1785,8 @@ expire_one_domain (const char *top_dirname, const char *domain) __func__, gpg_strerror (err)); goto leave; } - if (strlen (dentry->d_name) != 32) + /* The old files are 32 bytes, those created since 2.3.8 are 65 bytes. */ + if (strlen (dentry->d_name) != 32 && strlen (dentry->d_name) != 65) { log_info ("garbage file '%s' ignored\n", fname); continue; diff --git a/tools/gpgconf-comp.c b/tools/gpgconf-comp.c index fa4e054..c9bdfeb 100644 --- a/tools/gpgconf-comp.c +++ b/tools/gpgconf-comp.c @@ -1,6 +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-2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -27,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> @@ -43,7 +43,6 @@ # include <grp.h> #endif -/* For log_logv(), asctimestamp(), gnupg_get_time (). */ #include "../common/util.h" #include "../common/i18n.h" #include "../common/exechelp.h" @@ -62,13 +61,6 @@ #define GPGNAME GPG_NAME #endif - -/* TODO: - Components: Add more components and their options. - Robustness: Do more validation. Call programs to do validation for us. - Add options to change backend binary path. - Extract binary path for some backends from gpgsm/gpg config. -*/ #if (__GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )) @@ -110,93 +102,19 @@ static void gpg_agent_runtime_change (int killflag); static void scdaemon_runtime_change (int killflag); static void dirmngr_runtime_change (int killflag); -/* Backend configuration. Backends are used to decide how the default - and current value of an option can be determined, and how the - option can be changed. To every option in every component belongs - exactly one backend that controls and determines the option. Some - backends are programs from the GPG system. Others might be - implemented by GPGConf itself. If you change this enum, don't - forget to update GC_BACKEND below. */ -typedef enum - { - /* Any backend, used for find_option (). */ - GC_BACKEND_ANY, - - /* The Gnu Privacy Guard. */ - GC_BACKEND_GPG, - - /* The Gnu Privacy Guard for S/MIME. */ - GC_BACKEND_GPGSM, - - /* The GPG Agent. */ - GC_BACKEND_GPG_AGENT, - - /* The GnuPG SCDaemon. */ - GC_BACKEND_SCDAEMON, - - /* The GnuPG directory manager. */ - GC_BACKEND_DIRMNGR, - - /* The LDAP server list file for the director manager. */ - GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST, - - /* The Pinentry (not a part of GnuPG, proper). */ - GC_BACKEND_PINENTRY, - - /* The number of the above entries. */ - GC_BACKEND_NR - } gc_backend_t; - - -/* To be able to implement generic algorithms for the various - backends, we collect all information about them in this struct. */ -static const struct -{ - /* The name of the backend. */ - const char *name; - - /* The name of the program that acts as the backend. Some backends - don't have an associated program, but are implemented directly by - GPGConf. In this case, PROGRAM is NULL. */ - char *program; - - /* The module name (GNUPG_MODULE_NAME_foo) as defined by - ../common/util.h. This value is used to get the actual installed - path of the program. 0 is used if no backend program is - available. */ - char module_name; - /* The runtime change callback. If KILLFLAG is true the component - is killed and not just reloaded. */ - void (*runtime_change) (int killflag); - /* The option name for the configuration filename of this backend. - This must be an absolute filename. It can be an option from a - different backend (but then ordering of the options might - matter). Note: This must be unique among all components. */ - const char *option_config_filename; + +/* STRING_ARRAY is a malloced array with malloced strings. It is used + * a space to store strings so that other objects may point to these + * strings. It shall never be shrinked or any items changes. + * STRING_ARRAY itself may be reallocated to increase the size of the + * table. STRING_ARRAY_USED is the number of items currently used, + * STRING_ARRAY_SIZE is the number of calloced slots. */ +static char **string_array; +static size_t string_array_used; +static size_t string_array_size; - /* If this is a file backend rather than a program backend, then - this is the name of the option associated with the file. */ - const char *option_name; -} gc_backend[GC_BACKEND_NR] = - { - { NULL }, /* GC_BACKEND_ANY dummy entry. */ - { GPG_DISP_NAME, GPGNAME, GNUPG_MODULE_NAME_GPG, - NULL, GPGCONF_NAME "-" GPG_NAME ".conf" }, - { GPGSM_DISP_NAME, GPGSM_NAME, GNUPG_MODULE_NAME_GPGSM, - NULL, GPGCONF_NAME "-" GPGSM_NAME ".conf" }, - { GPG_AGENT_DISP_NAME, GPG_AGENT_NAME, GNUPG_MODULE_NAME_AGENT, - gpg_agent_runtime_change, GPGCONF_NAME"-" GPG_AGENT_NAME ".conf" }, - { SCDAEMON_DISP_NAME, SCDAEMON_NAME, GNUPG_MODULE_NAME_SCDAEMON, - scdaemon_runtime_change, GPGCONF_NAME"-" SCDAEMON_NAME ".conf" }, - { DIRMNGR_DISP_NAME, DIRMNGR_NAME, GNUPG_MODULE_NAME_DIRMNGR, - dirmngr_runtime_change, GPGCONF_NAME "-" DIRMNGR_NAME ".conf" }, - { DIRMNGR_DISP_NAME " LDAP Server List", NULL, 0, - NULL, "ldapserverlist-file", "LDAP Server" }, - { "Pinentry", "pinentry", GNUPG_MODULE_NAME_PINENTRY, - NULL, GPGCONF_NAME "-pinentry.conf" }, - }; /* Option configuration. */ @@ -342,15 +260,16 @@ static const struct }; -/* Option flags. The flags which are used by the backends are defined +/* Option flags. The flags which are used by the components are defined by gc-opt-flags.h, included above. YOU MUST NOT CHANGE THE NUMBERS OF THE EXISTING FLAGS, AS THEY ARE PART OF THE EXTERNAL INTERFACE. */ -/* Some entries in the option list are not options, but mark the - beginning of a new group of options. These entries have the GROUP - flag set. */ +/* Some entries in the emitted option list are not options, but mark + the beginning of a new group of options. These entries have the + GROUP flag set. Note that this is internally also known as a + header line. */ #define GC_OPT_FLAG_GROUP (1UL << 0) /* The ARG_OPT flag for an option indicates that the argument is optional. This is never set for GC_ARG_TYPE_NONE options. */ @@ -359,6 +278,10 @@ static const struct several times. A comma separated list of arguments is used as the argument value. */ #define GC_OPT_FLAG_LIST (1UL << 2) +/* The RUNTIME flag for an option indicates that the option can be + changed at runtime. */ +#define GC_OPT_FLAG_RUNTIME (1UL << 3) + /* A human-readable description for each flag. */ @@ -378,734 +301,355 @@ static const struct }; -/* To each option, or group marker, the information in the GC_OPTION - struct is provided. If you change this, don't forget to update the - option list of each component. */ -struct gc_option + +/* Each option we want to support in gpgconf has the needed + * information in a static list per componenet. This struct describes + * the info for a single option. */ +struct known_option_s { /* If this is NULL, then this is a terminator in an array of unknown - length. Otherwise, if this entry is a group marker (see FLAGS), - then this is the name of the group described by this entry. - Otherwise it is the name of the option described by this - entry. The name must not contain a colon. */ + * length. Otherwise it is the name of the option described by this + * entry. The name must not contain a colon. */ const char *name; - /* The option flags. If the GROUP flag is set, then this entry is a - group marker, not an option, and only the fields LEVEL, - DESC_DOMAIN and DESC are valid. In all other cases, this entry - describes a new option and all fields are valid. */ + /* The option flags. */ unsigned long flags; - /* The expert level. This field is valid for options and groups. A - group has the expert level of the lowest-level option in the - group. */ + /* The expert level. */ gc_expert_level_t level; - /* A gettext domain in which the following description can be found. - If this is NULL, then DESC is not translated. Valid for groups - and options. - - Note that we try to keep the description of groups within the - gnupg domain. - - IMPORTANT: If you add a new domain please make sure to add a code - set switching call to the function my_dgettext further below. */ - const char *desc_domain; - - /* A gettext description for this group or option. If it starts - with a '|', then the string up to the next '|' describes the - argument, and the description follows the second '|'. - - In general enclosing these description in N_() is not required - because the description should be identical to the one in the - help menu of the respective program. */ - const char *desc; - - /* The following fields are only valid for options. */ - - /* The type of the option argument. */ + /* The complex type of the option argument; the default of 0 is used + * for a standard type as returned by --dump-option-table. */ gc_arg_type_t arg_type; +}; +typedef struct known_option_s known_option_t; - /* The backend that implements this option. */ - gc_backend_t backend; - - /* The following fields are set to NULL at startup (because all - option's are declared as static variables). They are at the end - of the list so that they can be omitted from the option - declarations. */ - - /* This is true if the option is supported by this version of the - backend. */ - int active; - /* The default value for this option. This is NULL if the option is - not present in the backend, the empty string if no default is - available, and otherwise a quoted string. */ - char *default_value; +/* The known options of the GC_COMPONENT_GPG_AGENT component. */ +static known_option_t known_options_gpg_agent[] = + { + { "verbose", GC_OPT_FLAG_LIST|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "disable-scdaemon", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, + GC_LEVEL_ADVANCED }, + { "enable-ssh-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "ssh-fingerprint-digest", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "enable-putty-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "enable-extended-key-format", GC_OPT_FLAG_RUNTIME, GC_LEVEL_INVISIBLE }, + { "debug-level", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED}, + { "log-file", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, + /**/ GC_ARG_TYPE_FILENAME }, + { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + + { "default-cache-ttl", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "default-cache-ttl-ssh", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED }, + { "max-cache-ttl", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "max-cache-ttl-ssh", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "allow-emacs-pinentry", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED }, + { "grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "no-allow-external-cache", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "no-allow-mark-trusted", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED }, + { "no-allow-loopback-pinentry", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + + { "enforce-passphrase-constraints", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "min-passphrase-len", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED }, + { "min-passphrase-nonalpha", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "check-passphrase-pattern", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, + /**/ GC_ARG_TYPE_FILENAME }, + { "check-sym-passphrase-pattern", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, + /**/ GC_ARG_TYPE_FILENAME }, + { "max-passphrase-days", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "enable-passphrase-history", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "pinentry-timeout", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED }, + + { NULL } + }; - /* The default argument is only valid if the "optional arg" flag is - set, and specifies the default argument (value) that is used if - the argument is omitted. */ - char *default_arg; - /* The current value of this option. */ - char *value; +/* The known options of the GC_COMPONENT_SCDAEMON component. */ +static known_option_t known_options_scdaemon[] = + { + { "verbose", GC_OPT_FLAG_LIST|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "reader-port", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "ctapi-driver", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED }, + { "pcsc-driver", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED }, + { "disable-ccid", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT }, + { "disable-pinpad", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "enable-pinpad-varlen", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "card-timeout", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + { "debug-level", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED}, + { "log-file", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, + GC_ARG_TYPE_FILENAME }, + { "deny-admin", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC }, + + { NULL } + }; - /* The new flags for this option. The only defined flag is actually - GC_OPT_FLAG_DEFAULT, and it means that the option should be - deleted. In this case, NEW_VALUE is NULL. */ - unsigned long new_flags; - /* The new value of this option. */ - char *new_value; -}; -typedef struct gc_option gc_option_t; +/* The known options of the GC_COMPONENT_GPG component. */ +static known_option_t known_options_gpg[] = + { + { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "default-key", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "encrypt-to", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED, + GC_ARG_TYPE_ALIAS_LIST}, + { "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-import", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "no-auto-key-import", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT }, + { "no-auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "include-key-block", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "no-include-key-block", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "disable-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT }, + { "max-cert-depth", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "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. + * The meta information is taken from the table below. */ + { "default_pubkey_algo", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "compliance_de_vs", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + + { NULL } + }; +static const char *known_pseudo_options_gpg[] = + {/* v-- ARGPARSE_TYPE_STRING */ + "default_pubkey_algo:0:2:@:", + /* A basic compliance check for gpg. We use gpg here but the + * result is valid for all components. + * v-- ARGPARSE_TYPE_INT */ + "compliance_de_vs:0:1:@:", + NULL + }; -/* Use this macro to terminate an option list. */ -#define GC_OPTION_NULL { NULL } - -#ifndef BUILD_WITH_AGENT -#define gc_options_gpg_agent NULL -#else -/* The options of the GC_COMPONENT_GPG_AGENT component. */ -static gc_option_t gc_options_gpg_agent[] = +/* The known options of the GC_COMPONENT_GPGSM component. */ +static known_option_t known_options_gpgsm[] = { - /* The configuration file to which we write the changes. */ - { GPGCONF_NAME"-" GPG_AGENT_NAME ".conf", - GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, - NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG_AGENT }, - - { "Monitor", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the diagnostic output") }, - { "verbose", GC_OPT_FLAG_LIST|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "verbose", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "quiet", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "be somewhat more quiet", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - - { "Configuration", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the configuration") }, - { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "gnupg", "|FILE|read options from FILE", - GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG_AGENT }, - { "disable-scdaemon", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", "do not use the SCdaemon", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "enable-ssh-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", "enable ssh support", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "ssh-fingerprint-digest", - GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, - "gnupg", "|ALGO|use ALGO to show ssh fingerprints", - GC_ARG_TYPE_STRING, GC_BACKEND_GPG_AGENT }, - { "enable-putty-support", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", "enable putty support", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "enable-extended-key-format", GC_OPT_FLAG_RUNTIME, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - - { "Debug", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Options useful for debugging") }, - { "debug-level", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, - "gnupg", "|LEVEL|set the debugging level to LEVEL", - GC_ARG_TYPE_STRING, GC_BACKEND_GPG_AGENT }, - { "log-file", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, - "gnupg", N_("|FILE|write server mode logs to FILE"), - GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG_AGENT }, - { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_UINT32, GC_BACKEND_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", - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, - { "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-emacs-pinentry", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_ADVANCED, - "gnupg", "allow passphrase to be prompted through Emacs", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, - "gnupg", NULL, - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "no-allow-external-cache", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_BASIC, "gnupg", "disallow the use of an external password cache", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "no-allow-mark-trusted", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_ADVANCED, "gnupg", "disallow clients to mark keys as \"trusted\"", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "no-allow-loopback-pinentry", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_EXPERT, "gnupg", "disallow caller to override the pinentry", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - - { "Passphrase policy", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Options enforcing a passphrase policy") }, - { "enforce-passphrase-constraints", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_EXPERT, "gnupg", - N_("do not allow bypassing the passphrase policy"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "min-passphrase-len", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_ADVANCED, "gnupg", - N_("|N|set minimal required length for new passphrases to N"), - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, - { "min-passphrase-nonalpha", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_EXPERT, "gnupg", - N_("|N|require at least N non-alpha characters for a new passphrase"), - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, - { "check-passphrase-pattern", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_EXPERT, - "gnupg", N_("|FILE|check new passphrases against pattern in FILE"), - GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG_AGENT }, - { "max-passphrase-days", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_EXPERT, "gnupg", - N_("|N|expire the passphrase after N days"), - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, - { "enable-passphrase-history", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_EXPERT, "gnupg", - N_("do not allow the reuse of old passphrases"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT }, - { "pinentry-timeout", GC_OPT_FLAG_RUNTIME, - GC_LEVEL_ADVANCED, "gnupg", - N_("|N|set the Pinentry timeout to N seconds"), - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT }, - - GC_OPTION_NULL + { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "default-key", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "encrypt-to", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "disable-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT }, + { "p12-charset", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "keyserver", GC_OPT_FLAG_LIST, GC_LEVEL_INVISIBLE, + GC_ARG_TYPE_LDAP_SERVER }, + { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT }, + { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED }, + { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, + GC_ARG_TYPE_FILENAME }, + { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "enable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "enable-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "include-certs", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT }, + { "disable-policy-checks", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "auto-issuer-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "cipher-algo", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "disable-trusted-cert-crl-check", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT }, + + /* 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 }; -#endif /*BUILD_WITH_AGENT*/ -#ifndef BUILD_WITH_SCDAEMON -#define gc_options_scdaemon NULL -#else -/* The options of the GC_COMPONENT_SCDAEMON component. */ -static gc_option_t gc_options_scdaemon[] = +/* The known options of the GC_COMPONENT_DIRMNGR component. */ +static known_option_t known_options_dirmngr[] = { - /* The configuration file to which we write the changes. */ - { GPGCONF_NAME"-"SCDAEMON_NAME".conf", - GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, - NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_SCDAEMON }, - - { "Monitor", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the diagnostic output") }, - { "verbose", GC_OPT_FLAG_LIST|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "verbose", - GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", "be somewhat more quiet", - GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - - { "Configuration", - GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, - "gnupg", N_("Options controlling the configuration") }, - { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "gnupg", "|FILE|read options from FILE", - GC_ARG_TYPE_FILENAME, GC_BACKEND_SCDAEMON }, - { "reader-port", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "|N|connect to reader at port N", - GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, - { "ctapi-driver", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, - "gnupg", "|NAME|use NAME as ct-API driver", - GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, - { "pcsc-driver", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, - "gnupg", "|NAME|use NAME as PC/SC driver", - GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, - { "disable-ccid", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT, - "gnupg", "do not use the internal CCID driver", - GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - { "disable-pinpad", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "do not use a reader's pinpad", - GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - { "enable-pinpad-varlen", - GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "use variable length input for pinpad", - GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - { "card-timeout", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "|N|disconnect the card after N seconds of inactivity", - GC_ARG_TYPE_UINT32, GC_BACKEND_SCDAEMON }, - - { "Debug", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Options useful for debugging") }, - { "debug-level", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, - "gnupg", "|LEVEL|set the debugging level to LEVEL", - GC_ARG_TYPE_STRING, GC_BACKEND_SCDAEMON }, - { "log-file", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED, - "gnupg", N_("|FILE|write a log to FILE"), - GC_ARG_TYPE_FILENAME, GC_BACKEND_SCDAEMON }, - - { "Security", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the security") }, - { "deny-admin", GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC, - "gnupg", "deny the use of admin card commands", - GC_ARG_TYPE_NONE, GC_BACKEND_SCDAEMON }, - - - GC_OPTION_NULL + { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC }, + { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "resolver-timeout", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "nameserver", 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 }, + { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE }, + { "force", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "use-tor", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "ldapserver", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, + GC_ARG_TYPE_LDAP_SERVER }, + { "disable-http", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "ignore-http-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "honor-http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "disable-ldap", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "ignore-ldap-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "only-ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "add-servers", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "ldaptimeout", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "max-replies", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "allow-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "ocsp-responder", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "ocsp-signer", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + { "allow-version-check", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC }, + { "ignore-ocsp-service-url", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED }, + + + { NULL } }; -#endif /*BUILD_WITH_SCDAEMON*/ -#ifndef BUILD_WITH_GPG -#define gc_options_gpg NULL -#else -/* The options of the GC_COMPONENT_GPG component. */ -static gc_option_t gc_options_gpg[] = + +/* The known options of the GC_COMPONENT_PINENTRY component. */ +static known_option_t known_options_pinentry[] = { - /* The configuration file to which we write the changes. */ - { GPGCONF_NAME"-"GPG_NAME".conf", - GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, - NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG }, - - { "Monitor", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the diagnostic output") }, - { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, - "gnupg", "verbose", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", "be somewhat more quiet", - GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - - { "Configuration", - GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, - "gnupg", N_("Options controlling the configuration") }, - { "default-key", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("|NAME|use NAME as default secret key"), - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - { "encrypt-to", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("|NAME|encrypt to user ID NAME as well"), - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - { "group", GC_OPT_FLAG_LIST, GC_LEVEL_ADVANCED, - "gnupg", N_("|SPEC|set up email aliases"), - GC_ARG_TYPE_ALIAS_LIST, GC_BACKEND_GPG }, - { "options", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG }, - { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - 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", - (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - { "compliance_de_vs", - (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - { "trust-model", - GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - - - { "Debug", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Options useful for debugging") }, - { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED, - "gnupg", "|LEVEL|set the debugging level to LEVEL", - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", N_("|FILE|write server mode logs to FILE"), - GC_ARG_TYPE_FILENAME, GC_BACKEND_GPG }, -/* { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, */ -/* NULL, NULL, */ -/* GC_ARG_TYPE_UINT32, GC_BACKEND_GPG }, */ - - { "Keyserver", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Configuration for Keyservers") }, - { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "gnupg", N_("|URL|use keyserver at URL"), /* Deprecated - use dirmngr */ - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - { "allow-pka-lookup", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("allow PKA lookups (DNS requests)"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "auto-key-locate", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", N_("|MECHANISMS|use MECHANISMS to locate keys by mail address"), - GC_ARG_TYPE_STRING, GC_BACKEND_GPG }, - { "auto-key-import", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("import missing key from a signature"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "include-key-block", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("include the public key in signatures"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - NULL, NULL, GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "no-auto-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "disable-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "gnupg", N_("disable all access to the dirmngr"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPG }, - { "max-cert-depth", - GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG }, - { "completes-needed", - GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG }, - { "marginals-needed", - GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_UINT32, GC_BACKEND_GPG }, - - - GC_OPTION_NULL + { NULL } }; -#endif /*BUILD_WITH_GPG*/ -#ifndef BUILD_WITH_GPGSM -#define gc_options_gpgsm NULL -#else -/* The options of the GC_COMPONENT_GPGSM component. */ -static gc_option_t gc_options_gpgsm[] = - { - /* The configuration file to which we write the changes. */ - { GPGCONF_NAME"-"GPGSM_NAME".conf", - GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, - NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_GPGSM }, - - { "Monitor", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the diagnostic output") }, - { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, - "gnupg", "verbose", - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", "be somewhat more quiet", - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - - { "Configuration", - GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, - "gnupg", N_("Options controlling the configuration") }, - { "default-key", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("|NAME|use NAME as default secret key"), - GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, - { "encrypt-to", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("|NAME|encrypt to user ID NAME as well"), - GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, - { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "gnupg", "|FILE|read options from FILE", - GC_ARG_TYPE_FILENAME, GC_BACKEND_GPGSM }, - { "prefer-system-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", "use system's dirmngr if available", - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "disable-dirmngr", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "gnupg", N_("disable all access to the dirmngr"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "p12-charset", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", N_("|NAME|use encoding NAME for PKCS#12 passphrases"), - GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, - { "keyserver", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, - "gnupg", N_("|SPEC|use this keyserver to lookup keys"), - GC_ARG_TYPE_LDAP_SERVER, GC_BACKEND_GPGSM }, - { "default_pubkey_algo", - (GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_NO_CHANGE), GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, - { "compliance", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - NULL, NULL, - GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, - - { "Debug", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Options useful for debugging") }, - { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED, - "gnupg", "|LEVEL|set the debugging level to LEVEL", - GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, - { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", N_("|FILE|write server mode logs to FILE"), - GC_ARG_TYPE_FILENAME, GC_BACKEND_GPGSM }, - { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_UINT32, GC_BACKEND_GPGSM }, - - { "Security", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the security") }, - { "disable-crl-checks", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", "never consult a CRL", - 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, - "gnupg", N_("do not check CRLs for root certificates"), - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "enable-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", "check validity using OCSP", - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "include-certs", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "gnupg", "|N|number of certificates to include", - GC_ARG_TYPE_INT32, GC_BACKEND_GPGSM }, - { "disable-policy-checks", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", "do not check certificate policies", - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "auto-issuer-key-retrieve", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", "fetch missing issuer certificates", - GC_ARG_TYPE_NONE, GC_BACKEND_GPGSM }, - { "cipher-algo", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", "|NAME|use cipher algorithm NAME", - GC_ARG_TYPE_STRING, GC_BACKEND_GPGSM }, - - GC_OPTION_NULL - }; -#endif /*BUILD_WITH_GPGSM*/ + +/* Our main option info object. We copy all required information from the + * gpgrt_opt_t items but convert the flags value to bit flags. */ +struct gc_option_s +{ + const char *name; /* The same as gpgrt_opt_t.long_opt. */ + const char *desc; /* The same as gpgrt_opt_t.description. */ + unsigned int is_header:1; /* This is a header item. */ + unsigned int is_list:1; /* This is a list style option. */ + unsigned int opt_arg:1; /* The option's argument is optional. */ + unsigned int runtime:1; /* The option is runtime changeable. */ -#ifndef BUILD_WITH_DIRMNGR -#define gc_options_dirmngr NULL -#else -/* The options of the GC_COMPONENT_DIRMNGR component. */ -static gc_option_t gc_options_dirmngr[] = - { - /* The configuration file to which we write the changes. */ - { GPGCONF_NAME"-"DIRMNGR_NAME".conf", - GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, - NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR }, - - { "Monitor", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the diagnostic output") }, - { "verbose", GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, - "dirmngr", "verbose", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "quiet", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "be somewhat more quiet", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "no-greeting", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - - { "Format", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the format of the output") }, - { "sh", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "sh-style command output", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "csh", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "csh-style command output", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - - { "Configuration", - GC_OPT_FLAG_GROUP, GC_LEVEL_EXPERT, - "gnupg", N_("Options controlling the configuration") }, - { "options", GC_OPT_FLAG_NONE, GC_LEVEL_EXPERT, - "dirmngr", "|FILE|read options from FILE", - GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR }, - { "resolver-timeout", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_INT32, GC_BACKEND_DIRMNGR }, - { "nameserver", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, - - { "Debug", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Options useful for debugging") }, - { "debug-level", GC_OPT_FLAG_ARG_OPT, GC_LEVEL_ADVANCED, - "dirmngr", "|LEVEL|set the debugging level to LEVEL", - GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, - { "no-detach", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "do not detach from the console", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "log-file", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", N_("|FILE|write server mode logs to FILE"), - GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR }, - { "debug-wait", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, - { "faked-system-time", GC_OPT_FLAG_NONE, GC_LEVEL_INVISIBLE, - NULL, NULL, - GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, - - { "Enforcement", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the interactivity and enforcement") }, - { "batch", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "run without asking a user", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "force", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "force loading of outdated CRLs", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "allow-version-check", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "allow online software version check", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - - { "Tor", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Options controlling the use of Tor") }, - { "use-tor", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "route all network traffic via TOR", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - - { "Keyserver", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Configuration for Keyservers") }, - { "keyserver", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "gnupg", N_("|URL|use keyserver at URL"), - GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, - - { "HTTP", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Configuration for HTTP servers") }, - { "disable-http", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "inhibit the use of HTTP", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "ignore-http-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "ignore HTTP CRL distribution points", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "|URL|redirect all HTTP requests to URL", - GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, - { "honor-http-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "gnupg", N_("use system's HTTP proxy setting"), - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - - { "LDAP", - GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC, - "gnupg", N_("Configuration of LDAP servers to use") }, - { "disable-ldap", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "inhibit the use of LDAP", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "ignore-ldap-dp", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "ignore LDAP CRL distribution points", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "|HOST|use HOST for LDAP queries", - GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, - { "only-ldap-proxy", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "do not use fallback hosts with --ldap-proxy", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "add-servers", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "add new servers discovered in CRL distribution points" - " to serverlist", GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "ldaptimeout", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "|N|set LDAP timeout to N seconds", - GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, - /* The following entry must not be removed, as it is required for - the GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST. */ - { "ldapserverlist-file", - GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, - "dirmngr", "|FILE|read LDAP server list from FILE", - GC_ARG_TYPE_FILENAME, GC_BACKEND_DIRMNGR }, - /* This entry must come after at least one entry for - GC_BACKEND_DIRMNGR in this component, so that the entry for - "ldapserverlist-file will be initialized before this one. */ - { "LDAP Server", GC_OPT_FLAG_ARG_OPT|GC_OPT_FLAG_LIST, GC_LEVEL_BASIC, - "gnupg", N_("LDAP server list"), - GC_ARG_TYPE_LDAP_SERVER, GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST }, - { "max-replies", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "|N|do not return more than N items in one query", - GC_ARG_TYPE_UINT32, GC_BACKEND_DIRMNGR }, - - { "OCSP", - GC_OPT_FLAG_GROUP, GC_LEVEL_ADVANCED, - "gnupg", N_("Configuration for OCSP") }, - { "allow-ocsp", GC_OPT_FLAG_NONE, GC_LEVEL_BASIC, - "dirmngr", "allow sending OCSP requests", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "ignore-ocsp-service-url", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "ignore certificate contained OCSP service URLs", - GC_ARG_TYPE_NONE, GC_BACKEND_DIRMNGR }, - { "ocsp-responder", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "|URL|use OCSP responder at URL", - GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, - { "ocsp-signer", GC_OPT_FLAG_NONE, GC_LEVEL_ADVANCED, - "dirmngr", "|FPR|OCSP response signed by FPR", - GC_ARG_TYPE_STRING, GC_BACKEND_DIRMNGR }, - - - GC_OPTION_NULL - }; -#endif /*BUILD_WITH_DIRMNGR*/ + 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. */ + unsigned int no_arg_desc:1; /* The argument has a default ???. */ + unsigned int no_change:1; /* User shall not change the option. */ -/* The options of the GC_COMPONENT_PINENTRY component. */ -static gc_option_t gc_options_pinentry[] = - { - /* A dummy option to allow gc_component_list_components to find the - pinentry backend. Needs to be a conf file. */ - { GPGCONF_NAME"-pinentry.conf", - GC_OPT_FLAG_NONE, GC_LEVEL_INTERNAL, - NULL, NULL, GC_ARG_TYPE_FILENAME, GC_BACKEND_PINENTRY }, + unsigned int attr_ignore:1; /* The ARGPARSE_ATTR_IGNORE. */ + unsigned int attr_force:1; /* The ARGPARSE_ATTR_FORCE. */ - GC_OPTION_NULL - }; + /* The expert level - copied from known_options. */ + gc_expert_level_t level; + + /* The complex type - copied from known_options. */ + gc_arg_type_t arg_type; + + /* The default value for this option. This is NULL if the option is + not present in the component, the empty string if no default is + available, and otherwise a quoted string. This is currently + malloced.*/ + char *default_value; + + /* The current value of this option. */ + char *value; + + /* The new flags for this option. The only defined flag is actually + GC_OPT_FLAG_DEFAULT, and it means that the option should be + deleted. In this case, NEW_VALUE is NULL. */ + unsigned long new_flags; + + /* The new value of this option. */ + char *new_value; +}; +typedef struct gc_option_s gc_option_t; /* The information associated with each component. */ -static const struct +static struct { - /* The name of this component. Must not contain a colon (':') - character. */ + /* The name of the component. Some components don't have an + * associated program, but are implemented directly by GPGConf. In + * this case, PROGRAM is NULL. */ + char *program; + + /* The displayed name of this component. Must not contain a colon + * (':') character. */ const char *name; /* The gettext domain for the description DESC. If this is NULL, then the description is not translated. */ const char *desc_domain; - /* The description for this domain. */ + /* The description of this component. */ const char *desc; - /* The list of options for this component, terminated by - GC_OPTION_NULL. */ + /* The module name (GNUPG_MODULE_NAME_foo) as defined by + * ../common/util.h. This value is used to get the actual installed + * path of the program. 0 is used if no program for the component + * is available. */ + char module_name; + + /* The name for the configuration filename of this component. */ + const char *option_config_filename; + + /* 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); + + /* The table of known options as read from the component including + * header lines and such. This is suitable to be passed to + * gpgrt_argparser. Will be filled in by + * retrieve_options_from_program. */ + gnupg_opt_t *opt_table; + + /* The full table including data from OPT_TABLE. The end of the + * table is marked by NULL entry for NAME. Will be filled in by + * retrieve_options_from_program. */ gc_option_t *options; -} gc_component[] = + +} gc_component[GC_COMPONENT_NR] = { - { "gpg", "gnupg", N_("OpenPGP"), gc_options_gpg }, - { "gpg-agent","gnupg", N_("Private Keys"), gc_options_gpg_agent }, - { "scdaemon", "gnupg", N_("Smartcards"), gc_options_scdaemon }, - { "gpgsm", "gnupg", N_("S/MIME"), gc_options_gpgsm }, - { "dirmngr", "gnupg", N_("Network"), gc_options_dirmngr }, - { "pinentry", "gnupg", N_("Passphrase Entry"), gc_options_pinentry } + /* Note: The order of the items must match the order given in the + * gc_component_id_t enumeration. The order is often used by + * frontends to display the backend options thus do not change the + * order without considering the user experience. */ + { NULL }, /* DUMMY for GC_COMPONENT_ANY */ + + { GPG_NAME, GPG_DISP_NAME, "gnupg", N_("OpenPGP"), + GNUPG_MODULE_NAME_GPG, GPG_NAME ".conf", + 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_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, NULL, gpg_agent_runtime_change }, + + { SCDAEMON_NAME, SCDAEMON_DISP_NAME, "gnupg", N_("Smartcards"), + GNUPG_MODULE_NAME_SCDAEMON, SCDAEMON_NAME ".conf", + 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, NULL, dirmngr_runtime_change }, + + { "pinentry", "Pinentry", "gnupg", N_("Passphrase Entry"), + GNUPG_MODULE_NAME_PINENTRY, NULL, + known_options_pinentry } }; -/* Structure used to collect error output of the backend programs. */ +/* Structure used to collect error output of the component programs. */ struct error_line_s; typedef struct error_line_s *error_line_t; struct error_line_s @@ -1156,7 +700,6 @@ gpg_agent_runtime_change (int killflag) const char *pgmname; const char *argv[5]; pid_t pid = (pid_t)(-1); - char *abs_homedir = NULL; int i = 0; pgmname = gnupg_module_name (GNUPG_MODULE_NAME_CONNECT_AGENT); @@ -1177,7 +720,6 @@ gpg_agent_runtime_change (int killflag) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[1], gpg_strerror (err)); gnupg_release_process (pid); - xfree (abs_homedir); } @@ -1188,7 +730,6 @@ scdaemon_runtime_change (int killflag) const char *pgmname; const char *argv[9]; pid_t pid = (pid_t)(-1); - char *abs_homedir = NULL; int i = 0; (void)killflag; /* For scdaemon kill and reload are synonyms. */ @@ -1220,7 +761,6 @@ scdaemon_runtime_change (int killflag) gc_error (0, 0, "error running '%s %s': %s", pgmname, argv[4], gpg_strerror (err)); gnupg_release_process (pid); - xfree (abs_homedir); } @@ -1320,79 +860,44 @@ gc_component_launch (int component) } -/* Unconditionally restart COMPONENT. */ -void -gc_component_kill (int component) +static void +do_runtime_change (int component, int killflag) { - int runtime[GC_BACKEND_NR]; - gc_option_t *option; - gc_backend_t backend; - - /* Set a flag for the backends to be reloaded. */ - for (backend = 0; backend < GC_BACKEND_NR; backend++) - runtime[backend] = 0; + int runtime[GC_COMPONENT_NR] = { 0 }; if (component < 0) { for (component = 0; component < GC_COMPONENT_NR; component++) - { - option = gc_component[component].options; - for (; option && option->name; option++) - runtime[option->backend] = 1; - } + runtime [component] = 1; } else { - assert (component < GC_COMPONENT_NR); - option = gc_component[component].options; - for (; option && option->name; option++) - runtime[option->backend] = 1; + log_assert (component >= 0 && component < GC_COMPONENT_NR); + runtime [component] = 1; } - /* Do the restart for the selected backends. */ - for (backend = GC_BACKEND_NR-1; backend; backend--) + /* Do the restart for the selected components. */ + for (component = GC_COMPONENT_NR-1; component >= 0; component--) { - if (runtime[backend] && gc_backend[backend].runtime_change) - (*gc_backend[backend].runtime_change) (1); + if (runtime[component] && gc_component[component].runtime_change) + (*gc_component[component].runtime_change) (killflag); } } -/* Unconditionally reload COMPONENT or all components if COMPONENT is -1. */ +/* Unconditionally restart COMPONENT. */ void -gc_component_reload (int component) +gc_component_kill (int component) { - int runtime[GC_BACKEND_NR]; - gc_option_t *option; - gc_backend_t backend; - - /* Set a flag for the backends to be reloaded. */ - for (backend = 0; backend < GC_BACKEND_NR; backend++) - runtime[backend] = 0; + do_runtime_change (component, 1); +} - if (component < 0) - { - for (component = 0; component < GC_COMPONENT_NR; component++) - { - option = gc_component[component].options; - for (; option && option->name; option++) - runtime[option->backend] = 1; - } - } - else - { - assert (component < GC_COMPONENT_NR); - option = gc_component[component].options; - for (; option && option->name; option++) - runtime[option->backend] = 1; - } - /* Do the reload for all selected backends. */ - for (backend = 0; backend < GC_BACKEND_NR; backend++) - { - if (runtime[backend] && gc_backend[backend].runtime_change) - (*gc_backend[backend].runtime_change) (0); - } +/* Unconditionally reload COMPONENT or all components if COMPONENT is -1. */ +void +gc_component_reload (int component) +{ + do_runtime_change (component, 0); } @@ -1568,44 +1073,24 @@ percent_deescape (const char *src) void gc_component_list_components (estream_t out) { - gc_component_t component; - gc_option_t *option; - gc_backend_t backend; - int backend_seen[GC_BACKEND_NR]; + gc_component_id_t component; const char *desc; const char *pgmname; for (component = 0; component < GC_COMPONENT_NR; component++) { - option = gc_component[component].options; - if (option) - { - for (backend = 0; backend < GC_BACKEND_NR; backend++) - backend_seen[backend] = 0; - - pgmname = ""; - for (; option && option->name; option++) - { - if ((option->flags & GC_OPT_FLAG_GROUP)) - continue; - backend = option->backend; - if (backend_seen[backend]) - continue; - backend_seen[backend] = 1; - assert (backend != GC_BACKEND_ANY); - if (gc_backend[backend].program - && !gc_backend[backend].module_name) - continue; - pgmname = gnupg_module_name (gc_backend[backend].module_name); - break; - } + if (!gc_component[component].program) + continue; + if (gc_component[component].module_name) + pgmname = gnupg_module_name (gc_component[component].module_name); + else + pgmname = ""; - desc = gc_component[component].desc; - desc = my_dgettext (gc_component[component].desc_domain, desc); - es_fprintf (out, "%s:%s:", - gc_component[component].name, gc_percent_escape (desc)); - es_fprintf (out, "%s\n", gc_percent_escape (pgmname)); - } + desc = gc_component[component].desc; + desc = my_dgettext (gc_component[component].desc_domain, desc); + es_fprintf (out, "%s:%s:", + gc_component[component].program, gc_percent_escape (desc)); + es_fprintf (out, "%s\n", gc_percent_escape (pgmname)); } } @@ -1712,9 +1197,6 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) { gpg_error_t err; unsigned int result; - int backend_seen[GC_BACKEND_NR]; - gc_backend_t backend; - gc_option_t *option; const char *pgmname; const char *argv[6]; int i; @@ -1723,35 +1205,17 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) estream_t errfp; error_line_t errlines; - for (backend = 0; backend < GC_BACKEND_NR; backend++) - backend_seen[backend] = 0; + log_assert (component >= 0 && component < GC_COMPONENT_NR); - option = gc_component[component].options; - for (; option && option->name; option++) - { - if ((option->flags & GC_OPT_FLAG_GROUP)) - continue; - backend = option->backend; - if (backend_seen[backend]) - continue; - backend_seen[backend] = 1; - assert (backend != GC_BACKEND_ANY); - if (!gc_backend[backend].program) - continue; - if (!gc_backend[backend].module_name) - continue; - - break; - } - if (! option || ! option->name) + if (!gc_component[component].program) + return 0; + if (!gc_component[component].module_name) return 0; - pgmname = gnupg_module_name (gc_backend[backend].module_name); + pgmname = gnupg_module_name (gc_component[component].module_name); i = 0; if (!gnupg_default_homedir_p () - && backend != GC_BACKEND_ANY - && backend != GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST - && backend != GC_BACKEND_PINENTRY) + && component != GC_COMPONENT_PINENTRY) { argv[i++] = "--homedir"; argv[i++] = gnupg_homedir (); @@ -1801,7 +1265,7 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) desc = gc_component[component].desc; desc = my_dgettext (gc_component[component].desc_domain, desc); es_fprintf (out, "%s:%s:", - gc_component[component].name, gc_percent_escape (desc)); + gc_component[component].program, gc_percent_escape (desc)); es_fputs (gc_percent_escape (pgmname), out); es_fprintf (out, ":%d:%d:", !(result & 1), !(result & 2)); for (errptr = errlines; errptr; errptr = errptr->next) @@ -1836,7 +1300,7 @@ gc_component_check_options (int component, estream_t out, const char *conf_file) void gc_check_programs (estream_t out) { - gc_component_t component; + gc_component_id_t component; for (component = 0; component < GC_COMPONENT_NR; component++) gc_component_check_options (component, out, NULL); @@ -1849,12 +1313,12 @@ gc_check_programs (estream_t out) int gc_component_find (const char *name) { - gc_component_t idx; + gc_component_id_t idx; for (idx = 0; idx < GC_COMPONENT_NR; idx++) { - if (gc_component[idx].options - && !strcmp (name, gc_component[idx].name)) + if (gc_component[idx].program + && !strcmp (name, gc_component[idx].program)) return idx; } return -1; @@ -1863,14 +1327,21 @@ gc_component_find (const char *name) /* List the option OPTION. */ static void -list_one_option (const gc_option_t *option, estream_t out) +list_one_option (gc_component_id_t component, + const gc_option_t *option, estream_t out) { const char *desc = NULL; char *arg_name = NULL; + unsigned long flags; + const char *desc_domain = gc_component[component].desc_domain; + + /* Don't show options with the ignore attribute. */ + if (option->attr_ignore && !option->attr_force) + return; if (option->desc) { - desc = my_dgettext (option->desc_domain, option->desc); + desc = my_dgettext (desc_domain, option->desc); if (*desc == '|') { @@ -1896,16 +1367,24 @@ list_one_option (const gc_option_t *option, estream_t out) es_fprintf (out, "%s", option->name); /* The flags field. */ - es_fprintf (out, ":%lu", option->flags); + flags = 0; + if (option->is_header) flags |= GC_OPT_FLAG_GROUP; + if (option->is_list) flags |= GC_OPT_FLAG_LIST; + if (option->runtime) flags |= GC_OPT_FLAG_RUNTIME; + if (option->has_default) flags |= GC_OPT_FLAG_DEFAULT; + if (option->def_in_desc) flags |= GC_OPT_FLAG_DEF_DESC; + if (option->no_arg_desc) flags |= GC_OPT_FLAG_NO_ARG_DESC; + if (option->no_change) flags |= GC_OPT_FLAG_NO_CHANGE; + if (option->attr_force) flags |= GC_OPT_FLAG_NO_CHANGE; + es_fprintf (out, ":%lu", flags); if (opt.verbose) { es_putc (' ', out); - if (!option->flags) + if (!flags) es_fprintf (out, "none"); else { - unsigned long flags = option->flags; unsigned long flag = 0; unsigned long first = 1; @@ -1951,16 +1430,17 @@ list_one_option (const gc_option_t *option, estream_t out) /* The default value field. */ es_fprintf (out, ":%s", option->default_value ? option->default_value : ""); - /* The default argument field. */ - es_fprintf (out, ":%s", option->default_arg ? option->default_arg : ""); + /* The default argument field. This was never used and is thus empty. */ + es_fprintf (out, ":"); /* The value field. */ if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE - && (option->flags & GC_OPT_FLAG_LIST) - && option->value) - /* The special format "1,1,1,1,...,1" is converted to a number - here. */ - es_fprintf (out, ":%u", (unsigned int)((strlen (option->value) + 1) / 2)); + && option->is_list && option->value) + { + /* The special format "1,1,1,1,...,1" is converted to a number + here. */ + es_fprintf (out, ":%u", (unsigned int)((strlen (option->value) + 1) / 2)); + } else es_fprintf (out, ":%s", option->value ? option->value : ""); @@ -1976,17 +1456,14 @@ gc_component_list_options (int component, estream_t out) { const gc_option_t *option = gc_component[component].options; - while (option && option->name) + for ( ; option && option->name; option++) { /* Do not output unknown or internal options. */ - if (!(option->flags & GC_OPT_FLAG_GROUP) - && (!option->active || option->level == GC_LEVEL_INTERNAL)) - { - option++; + if (!option->is_header + && option->level == GC_LEVEL_INTERNAL) continue; - } - if (option->flags & GC_OPT_FLAG_GROUP) + if (option->is_header) { const gc_option_t *group_option = option + 1; gc_expert_level_t level = GC_LEVEL_NR; @@ -1997,13 +1474,12 @@ gc_component_list_options (int component, estream_t out) maintain manually, we calculate it here. The value in the global static table is ignored. */ - while (group_option->name) + for ( ; group_option->name; group_option++) { - if (group_option->flags & GC_OPT_FLAG_GROUP) + if (group_option->is_header) break; if (group_option->level < level) level = group_option->level; - group_option++; } /* Check if group is empty. */ @@ -2012,88 +1488,100 @@ gc_component_list_options (int component, estream_t out) gc_option_t opt_copy; /* Fix up the group level. */ - memcpy (&opt_copy, option, sizeof (opt_copy)); + opt_copy = *option; opt_copy.level = level; - list_one_option (&opt_copy, out); + list_one_option (component, &opt_copy, out); } } else - list_one_option (option, out); + list_one_option (component, option, out); + } +} - option++; + +/* Return true if the option NAME is known and that we want it as + * gpgconf managed option. */ +static known_option_t * +is_known_option (gc_component_id_t component, const char *name) +{ + known_option_t *option = gc_component[component].known_options; + if (option) + { + for (; option->name; option++) + if (!strcmp (option->name, name)) + break; } + return (option && option->name)? option : NULL; } -/* Find the option NAME in component COMPONENT, for the backend - BACKEND. If BACKEND is GC_BACKEND_ANY, any backend will match. */ +/* Find the option NAME in component COMPONENT. Returns pointer to + * the option descriptor or NULL if not found. */ static gc_option_t * -find_option (gc_component_t component, const char *name, - gc_backend_t backend) +find_option (gc_component_id_t component, const char *name) { gc_option_t *option = gc_component[component].options; - while (option->name) + + if (option) { - if (!(option->flags & GC_OPT_FLAG_GROUP) - && !strcmp (option->name, name) - && (backend == GC_BACKEND_ANY || option->backend == backend)) - break; - option++; + for (; option->name; option++) + { + if (!option->is_header + && !strcmp (option->name, name)) + return option; + } } - return option->name ? option : NULL; + return NULL; } + -/* Determine the configuration filename for the component COMPONENT - and backend BACKEND. */ -static char * -get_config_filename (gc_component_t component, gc_backend_t backend) +struct read_line_wrapper_parm_s { - char *filename = NULL; - gc_option_t *option = find_option - (component, gc_backend[backend].option_config_filename, GC_BACKEND_ANY); - assert (option); - assert (option->arg_type == GC_ARG_TYPE_FILENAME); - assert (!(option->flags & GC_OPT_FLAG_LIST)); - - if (!option->active || !option->default_value) - gc_error (1, 0, "Option %s, needed by backend %s, was not initialized", - gc_backend[backend].option_config_filename, - gc_backend[backend].name); - - if (option->value && *option->value) - filename = percent_deescape (&option->value[1]); - else if (option->default_value && *option->default_value) - filename = percent_deescape (&option->default_value[1]); - else - filename = ""; - -#if HAVE_W32CE_SYSTEM - if (!(filename[0] == '/' || filename[0] == '\\')) -#elif defined(HAVE_DOSISH_SYSTEM) - if (!(filename[0] - && filename[1] == ':' - && (filename[2] == '/' || filename[2] == '\\')) /* x:\ or x:/ */ - && !((filename[0] == '\\' && filename[1] == '\\') - || (filename[0] == '/' && filename[1] == '/'))) /* \\server */ -#else - if (filename[0] != '/') -#endif - gc_error (1, 0, "Option %s, needed by backend %s, is not absolute", - gc_backend[backend].option_config_filename, - gc_backend[backend].name); + const char *pgmname; + estream_t fp; + char *line; + size_t line_len; + const char **extra_lines; + int extra_lines_idx; + char *extra_line_buffer; +}; - return filename; + +/* 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 from backend - * BACKEND, which we already know is a program-type backend. With +/* Retrieve the options for the component COMPONENT. With * ONLY_INSTALLED set components which are not installed are silently * ignored. */ static void -retrieve_options_from_program (gc_component_t component, gc_backend_t backend, - int only_installed) +retrieve_options_from_program (gc_component_id_t component, int only_installed) { gpg_error_t err; const char *pgmname; @@ -2101,33 +1589,216 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend, estream_t outfp; int exitcode; pid_t pid; + known_option_t *known_option; + gc_option_t *option; char *line = NULL; - size_t line_len = 0; + size_t line_len; ssize_t length; - estream_t config; - char *config_filename; + const char *config_name; + gnupg_argparse_t pargs; + int dummy_argc; + char *twopartconfig_name = NULL; + gnupg_opt_t *opt_table = NULL; /* A malloced option table. */ + size_t opt_table_used = 0; /* Its current length. */ + size_t opt_table_size = 0; /* Its allocated length. */ + gc_option_t *opt_info = NULL; /* A malloced options table. */ + 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_backend[backend].module_name - ? gnupg_module_name (gc_backend[backend].module_name) - : gc_backend[backend].program ); - i = 0; - if (!gnupg_default_homedir_p () - && backend != GC_BACKEND_ANY - && backend != GC_BACKEND_DIRMNGR_LDAP_SERVER_LIST - && backend != GC_BACKEND_PINENTRY) - { - argv[i++] = "--homedir"; - argv[i++] = gnupg_homedir (); - } - argv[i++] = "--gpgconf-list"; - argv[i++] = NULL; + pgmname = (gc_component[component].module_name + ? gnupg_module_name (gc_component[component].module_name) + : gc_component[component].program ); if (only_installed && gnupg_access (pgmname, X_OK)) { return; /* The component is not installed. */ } + + /* First we need to read the option table from the program. */ + argv[0] = "--dump-option-table"; + argv[1] = NULL; + err = gnupg_spawn_process (pgmname, argv, NULL, NULL, 0, + NULL, &outfp, NULL, &pid); + if (err) + { + gc_error (1, 0, "could not gather option table from '%s': %s", + pgmname, gpg_strerror (err)); + } + + 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 + && (line[length - 1] == '\n' || line[length - 1] == '\r')) + line[--length] = '\0'; + + if (split_fields_colon (line, fields, DIM (fields)) < 4) + { + gc_error (0,0, "WARNING: invalid line in option table of '%s'\n", + pgmname); + continue; + } + + optname = fields[0]; + short_opt = atoi (fields[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. */ + else if ((known_option = is_known_option (component, optname))) + ; /* Yes we want this one. */ + else + continue; /* No need to store this option description. */ + + /* The +1 here is to make sure that we will have a zero item at + * the end of the table. */ + if (opt_table_used + 1 >= opt_table_size) + { + /* Note that this also does the initial allocation. */ + opt_table_size += 128; + opt_table = xreallocarray (opt_table, + opt_table_used, + opt_table_size, + sizeof *opt_table); + } + /* The +1 here is to make sure that we will have a zero item at + * the end of the table. */ + if (opt_info_used + 1 >= opt_info_size) + { + /* Note that this also does the initial allocation. */ + opt_info_size += 128; + opt_info = xreallocarray (opt_info, + opt_info_used, + opt_info_size, + sizeof *opt_info); + } + /* The +1 here accounts for the two items we are going to add to + * the global string table. */ + if (string_array_used + 1 >= string_array_size) + { + string_array_size += 256; + string_array = xreallocarray (string_array, + string_array_used, + string_array_size, + sizeof *string_array); + } + string_array[string_array_used++] = optname = xstrdup (fields[0]); + string_array[string_array_used++] = optdesc = xstrdup (fields[3]); + + /* Create an option table which can then be supplied to + * gpgrt_parser. Unfortunately there is no private pointer in + * the public option table struct so that we can't add extra + * data we need here. Thus we need to build up another table + * for such info and for ease of use we also copy the tehre the + * data from the option table. It is not possible to use the + * 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. */ + 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 + * options (opt_info_t) we want to have a real empty string. */ + opt_info[opt_info_used].name = optname; + if (*optdesc == '@' && !optdesc[1]) + opt_info[opt_info_used].desc = optdesc+1; + else + opt_info[opt_info_used].desc = optdesc; + + /* Unfortunately we need to remap the types. */ + switch ((optflags & ARGPARSE_TYPE_MASK)) + { + case ARGPARSE_TYPE_INT: arg_type = GC_ARG_TYPE_INT32; break; + case ARGPARSE_TYPE_LONG: arg_type = GC_ARG_TYPE_INT32; break; + case ARGPARSE_TYPE_ULONG: arg_type = GC_ARG_TYPE_UINT32; break; + case ARGPARSE_TYPE_STRING: arg_type = GC_ARG_TYPE_STRING; break; + 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; + if (known_option) + { + if ((known_option->flags & GC_OPT_FLAG_LIST)) + opt_info[opt_info_used].is_list = 1; + /* FIXME: The next can also be taken from opt_table->flags. + * We need to check the code whether both specifications match. */ + if ((known_option->flags & GC_OPT_FLAG_ARG_OPT)) + opt_info[opt_info_used].opt_arg = 1; + /* Same here. */ + if ((known_option->flags & GC_OPT_FLAG_RUNTIME)) + opt_info[opt_info_used].runtime = 1; + + opt_info[opt_info_used].level = known_option->level; + /* Override the received argtype by a complex type. */ + if (known_option->arg_type) + opt_info[opt_info_used].arg_type = known_option->arg_type; + } + 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) + gc_error (1, 0, "running %s failed (exitcode=%d): %s", + pgmname, exitcode, gpg_strerror (err)); + gnupg_release_process (pid); + + /* Make the gpgrt option table and the internal option table available. */ + gc_component[component].opt_table = opt_table; + gc_component[component].options = opt_info; + + + /* Now read the default options. */ + argv[0] = "--gpgconf-list"; + argv[1] = NULL; err = gnupg_spawn_process (pgmname, argv, NULL, NULL, 0, NULL, &outfp, NULL, &pid); if (err) @@ -2138,7 +1809,6 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend, while ((length = es_read_line (outfp, &line, &line_len, NULL)) > 0) { - gc_option_t *option; char *linep; unsigned long flags = 0; char *default_value = NULL; @@ -2184,7 +1854,7 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend, if (end) *(end++) = '\0'; - if (flags & GC_OPT_FLAG_DEFAULT) + if ((flags & GC_OPT_FLAG_DEFAULT)) default_value = linep; linep = end; @@ -2192,15 +1862,24 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend, /* Look up the option in the component and install the configuration data. */ - option = find_option (component, line, backend); + option = find_option (component, line); if (option) { - if (option->active) - gc_error (1, errno, "option %s returned twice from %s", + 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; + + 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; - option->flags |= flags; if (default_value && *default_value) option->default_value = xstrdup (default_value); } @@ -2218,204 +1897,127 @@ retrieve_options_from_program (gc_component_t component, gc_backend_t backend, /* At this point, we can parse the configuration file. */ - config_filename = get_config_filename (component, backend); + config_name = gc_component[component].option_config_filename; + if (!config_name) + gc_error (1, 0, "name of config file for %s is not known\n", pgmname); - config = es_fopen (config_filename, "r"); - if (!config) - { - if (errno != ENOENT) - gc_error (0, errno, "warning: can not open config file %s", - config_filename); - } - else + if (!gnupg_default_homedir_p ()) { - while ((length = es_read_line (config, &line, &line_len, NULL)) > 0) - { - char *name; - char *value; - gc_option_t *option; - - name = line; - while (*name == ' ' || *name == '\t') - name++; - if (!*name || *name == '#' || *name == '\r' || *name == '\n') - continue; - - value = name; - while (*value && *value != ' ' && *value != '\t' - && *value != '#' && *value != '\r' && *value != '\n') - value++; - if (*value == ' ' || *value == '\t') - { - char *end; - - *(value++) = '\0'; - while (*value == ' ' || *value == '\t') - value++; - - end = value; - while (*end && *end != '#' && *end != '\r' && *end != '\n') - end++; - while (end > value && (end[-1] == ' ' || end[-1] == '\t')) - end--; - *end = '\0'; - } - else - *value = '\0'; - - /* Look up the option in the component and install the - configuration data. */ - option = find_option (component, line, backend); - if (option) - { - char *opt_value; - - if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE) - { - if (*value) - gc_error (0, 0, - "warning: ignoring argument %s for option %s", - value, name); - opt_value = xstrdup ("1"); - } - else if (gc_arg_type[option->arg_type].fallback - == GC_ARG_TYPE_STRING) - opt_value = xasprintf ("\"%s", gc_percent_escape (value)); - else - { - /* FIXME: Verify that the number is sane. */ - opt_value = xstrdup (value); - } - - /* Now enter the option into the table. */ - if (!(option->flags & GC_OPT_FLAG_LIST)) - { - if (option->value) - xfree (option->value); - option->value = opt_value; - } - else - { - if (!option->value) - option->value = opt_value; - else - { - char *old = option->value; - option->value = xasprintf ("%s,%s", old, opt_value); - xfree (old); - xfree (opt_value); - } - } - } - } - - if (length < 0 || es_ferror (config)) - gc_error (1, errno, "error reading from %s", config_filename); - if (es_fclose (config)) - gc_error (1, errno, "error closing %s", config_filename); - } - - xfree (line); -} - - -/* Retrieve the options for the component COMPONENT from backend - BACKEND, which we already know is of type file list. */ -static void -retrieve_options_from_file (gc_component_t component, gc_backend_t backend) -{ - gc_option_t *list_option; - gc_option_t *config_option; - char *list_filename; - gpgrt_stream_t list_file; - char *line = NULL; - size_t line_len = 0; - ssize_t length; - char *list = NULL; - - list_option = find_option (component, - gc_backend[backend].option_name, GC_BACKEND_ANY); - assert (list_option); - assert (!list_option->active); + /* This is not the default homedir. We need to take an absolute + * config name for the user config file; gpgrt_argparser + * fortunately supports this. */ + char *tmp = make_filename (gnupg_homedir (), config_name, NULL); + twopartconfig_name = xstrconcat (config_name, PATHSEP_S, tmp, NULL); + xfree (tmp); + config_name = twopartconfig_name; + } + + memset (&pargs, 0, sizeof pargs); + dummy_argc = 0; + pargs.argc = &dummy_argc; + pargs.flags = (ARGPARSE_FLAG_KEEP + | ARGPARSE_FLAG_SYS + | ARGPARSE_FLAG_USER + | ARGPARSE_FLAG_WITHATTR); + if (opt.verbose) + pargs.flags |= ARGPARSE_FLAG_VERBOSE; - list_filename = get_config_filename (component, backend); - list_file = gpgrt_fopen (list_filename, "r"); - if (!list_file) - gc_error (0, errno, "warning: can not open list file %s", list_filename); - else + while (gnupg_argparser (&pargs, opt_table, config_name)) { + char *opt_value; - while ((length = gpgrt_read_line (list_file, &line, &line_len, NULL)) > 0) - { - char *start; - char *end; - char *new_list; + if (pargs.r_opt == ARGPARSE_CONFFILE) + { + /* log_debug ("current conffile='%s'\n", */ + /* pargs.r_type? pargs.r.ret_str: "[cmdline]"); */ + continue; + } + if ((pargs.r_type & ARGPARSE_OPT_IGNORE)) + continue; - start = line; - while (*start == ' ' || *start == '\t') - start++; - if (!*start || *start == '#' || *start == '\r' || *start == '\n') - continue; - - end = start; - while (*end && *end != '#' && *end != '\r' && *end != '\n') - end++; - /* Walk back to skip trailing white spaces. Looks evil, but - works because of the conditions on START and END imposed - at this point (END is at least START + 1, and START is - not a whitespace character). */ - while (*(end - 1) == ' ' || *(end - 1) == '\t') - end--; - *end = '\0'; - /* FIXME: Oh, no! This is so lame! Should use realloc and - really append. */ - if (list) - { - new_list = xasprintf ("%s,\"%s", list, gc_percent_escape (start)); - xfree (list); - list = new_list; - } - else - list = xasprintf ("\"%s", gc_percent_escape (start)); - } - if (length < 0 || gpgrt_ferror (list_file)) - gc_error (1, errno, "can not read list file %s", list_filename); - } + /* We only have the short option. Search in the option table + * for the long option name. */ + for (i=0; opt_table[i].short_opt; i++) + if (opt_table[i].short_opt == pargs.r_opt) + break; + if (!opt_table[i].short_opt || !opt_table[i].long_opt) + continue; /* No or only a short option - ignore. */ + + /* Look up the option from the config file in our list of + * supported options. */ + option= find_option (component, opt_table[i].long_opt); + if (!option) + continue; /* We don't want to handle this option. */ + + /* Set the force and ignore attributes. The idea is that there + * is no way to clear them again, thus we set them when first + * encountered. */ + if ((pargs.r_type & ARGPARSE_ATTR_FORCE)) + option->attr_force = 1; + if ((pargs.r_type & ARGPARSE_ATTR_IGNORE)) + option->attr_ignore = 1; + + /* If an option has been ignored, there is no need to return + * that option with gpgconf --list-options. */ + if (option->attr_ignore) + continue; - list_option->active = 1; - list_option->value = list; + switch ((pargs.r_type & ARGPARSE_TYPE_MASK)) + { + case ARGPARSE_TYPE_INT: + opt_value = xasprintf ("%d", pargs.r.ret_int); + break; + case ARGPARSE_TYPE_LONG: + opt_value = xasprintf ("%ld", pargs.r.ret_long); + break; + case ARGPARSE_TYPE_ULONG: + opt_value = xasprintf ("%lu", pargs.r.ret_ulong); + break; + case ARGPARSE_TYPE_STRING: + 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. */ + break; + } - /* Fix up the read-only flag. */ - config_option = find_option - (component, gc_backend[backend].option_config_filename, GC_BACKEND_ANY); - if (config_option->flags & GC_OPT_FLAG_NO_CHANGE) - list_option->flags |= GC_OPT_FLAG_NO_CHANGE; + /* Now enter the value read from the config file into the table. */ + if (!option->is_list) + { + xfree (option->value); + option->value = opt_value; + } + else if (!option->value) /* LIST but first item. */ + option->value = opt_value; + else + { + char *old = option->value; + option->value = xstrconcat (old, ",", opt_value, NULL); + xfree (old); + xfree (opt_value); + } + } - if (list_file && gpgrt_fclose (list_file)) - gc_error (1, errno, "error closing %s", list_filename); xfree (line); + xfree (twopartconfig_name); } -/* Retrieve the currently active options and their defaults from all - involved backends for this component. Using -1 for component will - retrieve all options from all installed components. */ +/* Retrieve the currently active options and their defaults for this + component. Using -1 for component will retrieve all options from + all installed 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; - - for (backend = 0; backend < GC_BACKEND_NR; backend++) - backend_seen[backend] = 0; if (component == -1) { process_all = 1; component = 0; - assert (component < GC_COMPONENT_NR); } do @@ -2423,31 +2025,8 @@ gc_component_retrieve_options (int component) if (component == GC_COMPONENT_PINENTRY) continue; /* Skip this dummy component. */ - option = gc_component[component].options; - - while (option && 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, - process_all); - else - retrieve_options_from_file (component, backend); - } - option++; - } + if (gc_component[component].program) + retrieve_options_from_program (component, process_all); } while (process_all && ++component < GC_COMPONENT_NR); @@ -2460,15 +2039,14 @@ gc_component_retrieve_options (int component) * type GC_ARG_TYPE_NONE. If VERBATIM is set the profile parsing mode * is used. */ static void -option_check_validity (gc_option_t *option, unsigned long flags, +option_check_validity (gc_component_id_t component, + gc_option_t *option, unsigned long flags, char *new_value, unsigned long *new_value_nr, int verbatim) { char *arg; - if (!option->active) - gc_error (1, 0, "option %s not supported by backend %s", - option->name, gc_backend[option->backend].name); + (void)component; if (option->new_flags || option->new_value) gc_error (1, 0, "option %s already changed", option->name); @@ -2497,7 +2075,7 @@ option_check_validity (gc_option_t *option, unsigned long flags, gc_error (1, 0, "garbage after argument for option %s", option->name); - if (!(option->flags & GC_OPT_FLAG_LIST)) + if (!option->is_list) { if (*new_value_nr != 1) gc_error (1, 0, "argument for non-list option %s of type 0 " @@ -2518,10 +2096,10 @@ option_check_validity (gc_option_t *option, unsigned long flags, { if (*arg == '\0' || (*arg == ',' && !verbatim)) { - if (!(option->flags & GC_OPT_FLAG_ARG_OPT)) + if (!option->opt_arg) gc_error (1, 0, "argument required for option %s", option->name); - if (*arg == ',' && !verbatim && !(option->flags & GC_OPT_FLAG_LIST)) + if (*arg == ',' && !verbatim && !option->is_list) 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) @@ -2634,374 +2212,8 @@ copy_file (const char *src_name, const char *dst_name) #endif /* HAVE_W32_SYSTEM */ -/* Test whether the config filename stored at FNAMEP is in the global - * config directory. In this case change the filename to use the - * standard filename of the homedir. This shall be used before - * writing a config file to cope wit the case of a missing user config - * file name but an existsing global config file - this would - * otherwise lead to an attempt to write to the global file (which - * will be silently rejected due to insufficient permissions) and not - * the creation of a new user config file. */ -static void -munge_config_filename (char **fnamep) -{ - char *fname = *fnamep; - char *p1; - int c, rc; - - p1 = strrchr (fname, '/'); -#ifdef HAVE_W32_SYSTEM - { - char *p2 = strchr (fname, '\\'); - if (!p1 && p2) - p1 = p2; - else if (p2 && p2 > p1) - p1 = p2; - } -#endif - if (!p1 || !*p1) - return; /* No directory part - strange but no need for acting. */ - c = *p1; - *p1 = 0; - rc = compare_filenames (fname, gnupg_sysconfdir ()); - *p1 = c; - if (!rc && p1[1]) /* Sysconfdir - use homedir instead. */ - { - char *newfname = xstrconcat (gnupg_homedir (), p1, NULL); -#ifdef HAVE_W32_SYSTEM - for (p1=newfname; *p1; p1++) - if (*p1 == '/') - *p1 = '\\'; -#endif - xfree (fname); - *fnamep = newfname; - } -} - - -/* Create and verify the new configuration file for the specified - * backend and component. Returns 0 on success and -1 on error. This - * function may store pointers to malloced strings in SRC_FILENAMEP, - * DEST_FILENAMEP, and ORIG_FILENAMEP. Those must be freed by the - * caller. The strings refer to three versions of the configuration - * file: - * - * SRC_FILENAME: The updated configuration is written to this file. - * DEST_FILENAME: Name of the configuration file read by the - * component. - * ORIG_FILENAME: A backup of the previous configuration file. - * - * To apply the configuration change, rename SRC_FILENAME to - * DEST_FILENAME. To revert to the previous configuration, rename - * ORIG_FILENAME to DEST_FILENAME. */ -static int -change_options_file (gc_component_t component, gc_backend_t backend, - char **src_filenamep, char **dest_filenamep, - char **orig_filenamep) -{ - static const char marker[] = "###+++--- " GPGCONF_DISP_NAME " ---+++###"; - /* True if we are within the marker in the config file. */ - int in_marker = 0; - gc_option_t *option; - char *line = NULL; - size_t line_len; - ssize_t length; - int res; - int fd; - gpgrt_stream_t src_file = NULL; - gpgrt_stream_t dest_file = NULL; - char *src_filename; - char *dest_filename; - char *orig_filename; - char *arg; - char *cur_arg = NULL; - - option = find_option (component, - gc_backend[backend].option_name, GC_BACKEND_ANY); - assert (option); - assert (option->active); - assert (gc_arg_type[option->arg_type].fallback != GC_ARG_TYPE_NONE); - - /* FIXME. Throughout the function, do better error reporting. */ - /* Note that get_config_filename() calls percent_deescape(), so we - call this before processing the arguments. */ - dest_filename = xstrdup (get_config_filename (component, backend)); - src_filename = xasprintf ("%s.%s.%i.new", - dest_filename, GPGCONF_NAME, (int)getpid ()); - orig_filename = xasprintf ("%s.%s.%i.bak", - dest_filename, GPGCONF_NAME, (int)getpid ()); - - arg = option->new_value; - if (arg && arg[0] == '\0') - arg = NULL; - else if (arg) - { - char *end; - - arg++; - end = strchr (arg, ','); - if (end) - *end = '\0'; - - cur_arg = percent_deescape (arg); - if (end) - { - *end = ','; - arg = end + 1; - } - else - arg = NULL; - } - -#ifdef HAVE_W32_SYSTEM - res = copy_file (dest_filename, orig_filename); -#else - res = link (dest_filename, orig_filename); -#endif - if (res < 0 && errno != ENOENT) - { - xfree (dest_filename); - xfree (src_filename); - xfree (orig_filename); - return -1; - } - if (res < 0) - { - xfree (orig_filename); - orig_filename = NULL; - } - - /* We now initialize the return strings, so the caller can do the - cleanup for us. */ - *src_filenamep = src_filename; - *dest_filenamep = dest_filename; - *orig_filenamep = orig_filename; - - /* Use open() so that we can use O_EXCL. */ - fd = open (src_filename, O_CREAT | O_EXCL | O_WRONLY, 0644); - if (fd < 0) - return -1; - src_file = gpgrt_fdopen (fd, "w"); - res = errno; - if (!src_file) - { - gpg_err_set_errno (res); - return -1; - } - - /* Only if ORIG_FILENAME is not NULL did the configuration file - exist already. In this case, we will copy its content into the - new configuration file, changing it to our liking in the - process. */ - if (orig_filename) - { - dest_file = gpgrt_fopen (dest_filename, "r"); - if (!dest_file) - goto change_file_one_err; - - while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0) - { - int disable = 0; - char *start; - - if (!strncmp (marker, line, sizeof (marker) - 1)) - { - if (!in_marker) - in_marker = 1; - else - break; - } - - start = line; - while (*start == ' ' || *start == '\t') - start++; - if (*start && *start != '\r' && *start != '\n' && *start != '#') - { - char *end; - char *endp; - char saved_end; - - endp = start; - end = endp; - - /* Search for the end of the line. */ - while (*endp && *endp != '#' && *endp != '\r' && *endp != '\n') - { - endp++; - if (*endp && *endp != ' ' && *endp != '\t' - && *endp != '\r' && *endp != '\n' && *endp != '#') - end = endp + 1; - } - saved_end = *end; - *end = '\0'; - - if ((option->new_flags & GC_OPT_FLAG_DEFAULT) - || !cur_arg || strcmp (start, cur_arg)) - disable = 1; - else - { - /* Find next argument. */ - if (arg) - { - char *arg_end; - - arg++; - arg_end = strchr (arg, ','); - if (arg_end) - *arg_end = '\0'; - - cur_arg = percent_deescape (arg); - if (arg_end) - { - *arg_end = ','; - arg = arg_end + 1; - } - else - arg = NULL; - } - else - cur_arg = NULL; - } - - *end = saved_end; - } - - if (disable) - { - if (!in_marker) - { - gpgrt_fprintf (src_file, - "# %s disabled this option here at %s\n", - GPGCONF_DISP_NAME, asctimestamp (gnupg_get_time ())); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - gpgrt_fprintf (src_file, "# %s", line); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - } - } - else - { - gpgrt_fprintf (src_file, "%s", line); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - } - } - if (length < 0 || gpgrt_ferror (dest_file)) - goto change_file_one_err; - } - - if (!in_marker) - { - /* There was no marker. This is the first time we edit the - file. We add our own marker at the end of the file and - proceed. Note that we first write a newline, this guards us - against files which lack the newline at the end of the last - line, while it doesn't hurt us in all other cases. */ - gpgrt_fprintf (src_file, "\n%s\n", marker); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - } - - /* At this point, we have copied everything up to the end marker - into the new file, except for the arguments we are going to add. - Now, dump the new arguments and write the end marker, possibly - followed by the rest of the original file. */ - while (cur_arg) - { - gpgrt_fprintf (src_file, "%s\n", cur_arg); - - /* Find next argument. */ - if (arg) - { - char *end; - - arg++; - end = strchr (arg, ','); - if (end) - *end = '\0'; - - cur_arg = percent_deescape (arg); - if (end) - { - *end = ','; - arg = end + 1; - } - else - arg = NULL; - } - else - cur_arg = NULL; - } - - gpgrt_fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ())); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - - if (!in_marker) - { - gpgrt_fprintf (src_file, "# %s edited this configuration file.\n", - GPGCONF_DISP_NAME); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - gpgrt_fprintf (src_file, "# It will disable options before this marked " - "block, but it will\n"); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - gpgrt_fprintf (src_file, "# never change anything below these lines.\n"); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - } - if (dest_file) - { - while ((length = gpgrt_read_line (dest_file, &line, &line_len, NULL)) > 0) - { - gpgrt_fprintf (src_file, "%s", line); - if (gpgrt_ferror (src_file)) - goto change_file_one_err; - } - if (length < 0 || gpgrt_ferror (dest_file)) - goto change_file_one_err; - } - xfree (line); - line = NULL; - - res = gpgrt_fclose (src_file); - if (res) - { - res = errno; - close (fd); - if (dest_file) - gpgrt_fclose (dest_file); - gpg_err_set_errno (res); - return -1; - } - close (fd); - if (dest_file) - { - res = gpgrt_fclose (dest_file); - if (res) - return -1; - } - return 0; - - change_file_one_err: - xfree (line); - res = errno; - if (src_file) - { - gpgrt_fclose (src_file); - close (fd); - } - if (dest_file) - gpgrt_fclose (dest_file); - gpg_err_set_errno (res); - return -1; -} - - /* Create and verify the new configuration file for the specified - * backend and component. Returns 0 on success and -1 on error. If + * component. Returns 0 on success and -1 on error. If * VERBATIM is set the profile mode is used. This function may store * pointers to malloced strings in SRC_FILENAMEP, DEST_FILENAMEP, and * ORIG_FILENAMEP. Those must be freed by the caller. The strings @@ -3016,7 +2228,7 @@ change_options_file (gc_component_t component, gc_backend_t backend, * DEST_FILENAME. To revert to the previous configuration, rename * ORIG_FILENAME to DEST_FILENAME. */ static int -change_options_program (gc_component_t component, gc_backend_t backend, +change_options_program (gc_component_id_t component, char **src_filenamep, char **dest_filenamep, char **orig_filenamep, int verbatim) @@ -3038,9 +2250,15 @@ change_options_program (gc_component_t component, gc_backend_t backend, /* Special hack for gpg, see below. */ int utf8strings_seen = 0; + /* FIXME. Throughout the function, do better error reporting. */ - dest_filename = xstrdup (get_config_filename (component, backend)); - munge_config_filename (&dest_filename); + if (!gc_component[component].option_config_filename) + gc_error (1, 0, "name of config file for %s is not known\n", + gc_component[component].name); + + dest_filename = make_absfilename + (gnupg_homedir (), gc_component[component].option_config_filename, NULL); + src_filename = xasprintf ("%s.%s.%i.new", dest_filename, GPGCONF_NAME, (int)getpid ()); orig_filename = xasprintf ("%s.%s.%i.bak", @@ -3105,7 +2323,7 @@ change_options_program (gc_component_t component, gc_backend_t backend, else break; } - else if (backend == GC_BACKEND_GPG && in_marker + else if (component == GC_COMPONENT_GPG && in_marker && ! strcmp ("utf8-strings\n", line)) { /* Strip duplicated entries. */ @@ -3130,7 +2348,7 @@ change_options_program (gc_component_t component, gc_backend_t backend, saved_end = *end; *end = '\0'; - option = find_option (component, start, backend); + option = find_option (component, start); *end = saved_end; if (option && ((option->new_flags & GC_OPT_FLAG_DEFAULT) || option->new_value)) @@ -3179,15 +2397,13 @@ change_options_program (gc_component_t component, gc_backend_t backend, followed by the rest of the original file. */ /* We have to turn on UTF8 strings for GnuPG. */ - if (backend == GC_BACKEND_GPG && ! utf8strings_seen) + if (component == GC_COMPONENT_GPG && ! utf8strings_seen) gpgrt_fprintf (src_file, "utf8-strings\n"); option = gc_component[component].options; - while (option->name) + for ( ; option->name; option++) { - if (!(option->flags & GC_OPT_FLAG_GROUP) - && option->backend == backend - && option->new_value) + if (!option->is_header && option->new_value) { char *arg = option->new_value; @@ -3202,7 +2418,7 @@ change_options_program (gc_component_t component, gc_backend_t backend, 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; @@ -3252,13 +2468,12 @@ change_options_program (gc_component_t component, gc_backend_t backend, arg = end; } - assert (arg == NULL || *arg == '\0' || *arg == ','); + log_assert (arg == NULL || *arg == '\0' || *arg == ','); if (arg && *arg == ',') arg++; } while (arg && *arg); } - option++; } gpgrt_fprintf (src_file, "%s %s\n", marker, asctimestamp (gnupg_get_time ())); @@ -3331,27 +2546,29 @@ change_options_program (gc_component_t component, gc_backend_t backend, * gc_process_gpgconf_conf. If VERBATIM is set the profile parsing * mode is used. */ static void -change_one_value (gc_option_t *option, int *runtime, +change_one_value (gc_component_id_t component, + gc_option_t *option, int *r_runtime, unsigned long flags, char *new_value, int verbatim) { unsigned long new_value_nr = 0; - option_check_validity (option, flags, new_value, &new_value_nr, verbatim); + option_check_validity (component, option, + flags, new_value, &new_value_nr, verbatim); - if (option->flags & GC_OPT_FLAG_RUNTIME) - runtime[option->backend] = 1; + if (option->runtime) + *r_runtime = 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)) + && option->is_list) { char *str; /* 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'; @@ -3377,11 +2594,10 @@ gc_component_change_options (int component, estream_t in, estream_t out, { int err = 0; int block = 0; - int runtime[GC_BACKEND_NR]; - char *src_filename[GC_BACKEND_NR]; - char *dest_filename[GC_BACKEND_NR]; - char *orig_filename[GC_BACKEND_NR]; - gc_backend_t backend; + int runtime = 0; + char *src_filename = NULL; + char *dest_filename = NULL; + char *orig_filename = NULL; gc_option_t *option; char *line = NULL; size_t line_len = 0; @@ -3390,14 +2606,6 @@ gc_component_change_options (int component, estream_t in, estream_t out, if (component == GC_COMPONENT_PINENTRY) return; /* Dummy component for now. */ - for (backend = 0; backend < GC_BACKEND_NR; backend++) - { - runtime[backend] = 0; - src_filename[backend] = NULL; - dest_filename[backend] = NULL; - orig_filename[backend] = NULL; - } - if (in) { /* Read options from the file IN. */ @@ -3450,18 +2658,18 @@ gc_component_change_options (int component, estream_t in, estream_t out, linep = end; } - option = find_option (component, line, GC_BACKEND_ANY); + option = find_option (component, line); if (!option) gc_error (1, 0, "unknown option %s", line); - if ((option->flags & GC_OPT_FLAG_NO_CHANGE)) + if (option->no_change) { gc_error (0, 0, "ignoring new value for option %s", option->name); continue; } - change_one_value (option, runtime, flags, new_value, 0); + change_one_value (component, option, &runtime, flags, new_value, 0); } if (length < 0 || gpgrt_ferror (in)) gc_error (1, errno, "error reading stream 'in'"); @@ -3473,27 +2681,24 @@ gc_component_change_options (int component, estream_t in, estream_t out, option = gc_component[component].options; while (option && option->name) { - /* Go on if we have already seen this backend, or if there is - nothing to do. */ - if (src_filename[option->backend] - || !(option->new_flags || option->new_value)) + /* Go on if there is nothing to do. */ + if (src_filename || !(option->new_flags || option->new_value)) { option++; continue; } - if (gc_backend[option->backend].program) + if (gc_component[component].program) { - err = change_options_program (component, option->backend, - &src_filename[option->backend], - &dest_filename[option->backend], - &orig_filename[option->backend], + err = change_options_program (component, + &src_filename, + &dest_filename, + &orig_filename, verbatim); if (! err) { /* External verification. */ - err = gc_component_check_options (component, out, - src_filename[option->backend]); + err = gc_component_check_options (component, out, src_filename); if (err) { gc_error (0, 0, @@ -3504,12 +2709,6 @@ gc_component_change_options (int component, estream_t in, estream_t out, } } - else - err = change_options_file (component, option->backend, - &src_filename[option->backend], - &dest_filename[option->backend], - &orig_filename[option->backend]); - if (err) break; @@ -3524,70 +2723,62 @@ gc_component_change_options (int component, estream_t in, estream_t out, block = 1; gnupg_block_all_signals (); - if (! err && ! opt.dry_run) + if (!err && !opt.dry_run) { - int i; - - for (i = 0; i < GC_BACKEND_NR; i++) - { - if (src_filename[i]) - { - /* FIXME: Make a verification here. */ + if (src_filename) + { + /* FIXME: Make a verification here. */ - assert (dest_filename[i]); + log_assert (dest_filename); - if (orig_filename[i]) - err = gnupg_rename_file (src_filename[i], dest_filename[i], NULL); - else - { + if (orig_filename) + err = gnupg_rename_file (src_filename, dest_filename, NULL); + else + { #ifdef HAVE_W32_SYSTEM - /* We skip the unlink if we expect the file not to - be there. */ - err = gnupg_rename_file (src_filename[i], dest_filename[i], NULL); + /* We skip the unlink if we expect the file not to be + * there. */ + err = gnupg_rename_file (src_filename, dest_filename, NULL); #else /* HAVE_W32_SYSTEM */ - /* This is a bit safer than rename() because we - expect DEST_FILENAME not to be there. If it - happens to be there, this will fail. */ - err = link (src_filename[i], dest_filename[i]); - if (!err) - err = unlink (src_filename[i]); + /* This is a bit safer than rename() because we expect + * DEST_FILENAME not to be there. If it happens to be + * there, this will fail. */ + err = link (src_filename, dest_filename); + if (!err) + err = unlink (src_filename); #endif /* !HAVE_W32_SYSTEM */ - } - if (err) - break; - xfree (src_filename[i]); - src_filename[i] = NULL; - } - } + } + if (!err) + { + xfree (src_filename); + src_filename = NULL; + } + } } if (err || opt.dry_run) { - int i; int saved_errno = errno; /* An error occurred or a dry-run is requested. */ - for (i = 0; i < GC_BACKEND_NR; i++) - { - if (src_filename[i]) - { - /* The change was not yet committed. */ - unlink (src_filename[i]); - if (orig_filename[i]) - unlink (orig_filename[i]); - } - else - { - /* The changes were already committed. FIXME: This is a - tad dangerous, as we don't know if we don't overwrite - a version of the file that is even newer than the one - we just installed. */ - if (orig_filename[i]) - gnupg_rename_file (orig_filename[i], dest_filename[i], NULL); - else - unlink (dest_filename[i]); - } - } + if (src_filename) + { + /* The change was not yet committed. */ + unlink (src_filename); + if (orig_filename) + unlink (orig_filename); + } + else + { + /* The changes were already committed. FIXME: This is a tad + dangerous, as we don't know if we don't overwrite a + version of the file that is even newer than the one we + just installed. */ + if (orig_filename) + gnupg_rename_file (orig_filename, dest_filename, NULL); + else + unlink (dest_filename); + } if (err) gc_error (1, saved_errno, "could not commit changes"); @@ -3597,36 +2788,28 @@ gc_component_change_options (int component, estream_t in, estream_t out, /* If it all worked, notify the daemons of the changes. */ if (opt.runtime) - for (backend = 0; backend < GC_BACKEND_NR; backend++) - { - if (runtime[backend] && gc_backend[backend].runtime_change) - (*gc_backend[backend].runtime_change) (0); - } + do_runtime_change (component, 0); - /* Move the per-process backup file into its place. */ - for (backend = 0; backend < GC_BACKEND_NR; backend++) - if (orig_filename[backend]) - { - char *backup_filename; - assert (dest_filename[backend]); + /* Move the per-process backup file into its place. */ + if (orig_filename) + { + char *backup_filename; - backup_filename = xasprintf ("%s.%s.bak", - dest_filename[backend], GPGCONF_NAME); - gnupg_rename_file (orig_filename[backend], backup_filename, NULL); - xfree (backup_filename); - } + log_assert (dest_filename); + backup_filename = xasprintf ("%s.%s.bak", + dest_filename, GPGCONF_NAME); + gnupg_rename_file (orig_filename, backup_filename, NULL); + xfree (backup_filename); + } leave: if (block) gnupg_unblock_all_signals (); xfree (line); - for (backend = 0; backend < GC_BACKEND_NR; backend++) - { - xfree (src_filename[backend]); - xfree (dest_filename[backend]); - xfree (orig_filename[backend]); - } + xfree (src_filename); + xfree (dest_filename); + xfree (orig_filename); } @@ -3758,8 +2941,8 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, int lineno = 0; int in_rule = 0; int got_match = 0; - int runtime[GC_BACKEND_NR]; - int backend_id, component_id; + int runtime[GC_COMPONENT_NR] = { 0 }; + int component_id; char *fname; if (fname_arg) @@ -3768,9 +2951,6 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, fname = make_filename (gnupg_sysconfdir (), GPGCONF_NAME EXTSEP_S "conf", NULL); - for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++) - runtime[backend_id] = 0; - config = gpgrt_fopen (fname, "r"); if (!config) { @@ -3787,7 +2967,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, while ((length = gpgrt_read_line (config, &line, &line_len, NULL)) > 0) { - char *key, *component, *option, *flags, *value; + char *key, *compname, *option, *flags, *value; char *empty; gc_option_t *option_info = NULL; char *p; @@ -3821,7 +3001,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, continue; } *p++ = 0; - component = p; + compname = p; } else if (!in_rule) { @@ -3832,18 +3012,18 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, } else { - component = key; + compname = key; key = NULL; } in_rule = 1; /* Parse the component. */ - while (*component == ' ' || *component == '\t') - component++; - for (p=component; *p && !strchr (" \t\r\n", *p); p++) + while (*compname == ' ' || *compname == '\t') + compname++; + for (p=compname; *p && !strchr (" \t\r\n", *p); p++) ; - if (p == component) + if (p == compname) { gc_error (0, 0, "missing component at '%s', line %d", fname, lineno); @@ -3857,7 +3037,7 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, empty = p; *p++ = 0; option = p; - component_id = gc_component_find (component); + component_id = gc_component_find (compname); if (component_id < 0) { gc_error (0, 0, "unknown component at '%s', line %d", @@ -3889,11 +3069,15 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, flags = p; if ( component_id != -1) { - option_info = find_option (component_id, option, GC_BACKEND_ANY); + /* We need to make sure that we got the option list for the + * component. */ + if (!gc_component[component_id].options) + gc_component_retrieve_options (component_id); + option_info = find_option (component_id, option); if (!option_info) { - gc_error (0, 0, "unknown option at '%s', line %d", - fname, lineno); + gc_error (0, 0, "unknown option '%s' at '%s', line %d", + option, fname, lineno); gpgconf_write_status (STATUS_WARNING, "gpgconf.conf %d file '%s' line %d " "unknown option", @@ -4014,9 +3198,9 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, else if (!strcmp (flags, "default")) newflags |= GC_OPT_FLAG_DEFAULT; else if (!strcmp (flags, "no-change")) - option_info->flags |= GC_OPT_FLAG_NO_CHANGE; + option_info->no_change = 1; else if (!strcmp (flags, "change")) - option_info->flags &= ~GC_OPT_FLAG_NO_CHANGE; + option_info->no_change = 0; if (defaults) { @@ -4030,7 +3214,8 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, xfree (option_info->new_value); option_info->new_value = NULL; } - change_one_value (option_info, runtime, newflags, value, 0); + change_one_value (component_id, option_info, + runtime, newflags, value, 0); } } } @@ -4061,9 +3246,10 @@ gc_process_gpgconf_conf (const char *fname_arg, int update, int defaults, 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); + for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++) + if (runtime[component_id] + && gc_component[component_id].runtime_change) + (*gc_component[component_id].runtime_change) (0); } } @@ -4085,8 +3271,7 @@ gc_apply_profile (const char *fname) ssize_t length; estream_t fp; int lineno = 0; - int runtime[GC_BACKEND_NR]; - int backend_id; + int runtime[GC_COMPONENT_NR] = { 0 }; int component_id = -1; int skip_section = 0; int error_count = 0; @@ -4095,9 +3280,6 @@ gc_apply_profile (const char *fname) if (!fname) fname = "-"; - for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++) - runtime[backend_id] = 0; - if (!(!strcmp (fname, "-") || strchr (fname, '/') @@ -4188,7 +3370,7 @@ gc_apply_profile (const char *fname) *p++ = 0; value = p; - option_info = find_option (component_id, name, GC_BACKEND_ANY); + option_info = find_option (component_id, name); if (!option_info) { error_count++; @@ -4235,7 +3417,7 @@ gc_apply_profile (const char *fname) xfree (option_info->new_value); option_info->new_value = NULL; } - change_one_value (option_info, runtime, newflags, value, 1); + change_one_value (component_id, option_info, runtime, newflags, value, 1); } if (length < 0 || es_ferror (fp)) @@ -4268,9 +3450,10 @@ gc_apply_profile (const char *fname) 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); + for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++) + if (runtime[component_id] + && gc_component[component_id].runtime_change) + (*gc_component[component_id].runtime_change) (0); } } diff --git a/tools/gpgconf.c b/tools/gpgconf.c index 80a4062..1b3f2be 100644 --- a/tools/gpgconf.c +++ b/tools/gpgconf.c @@ -46,6 +46,12 @@ enum cmd_and_opt_values oRuntime = 'r', oComponent = 'c', oNull = '0', + aListDirs = 'L', + aKill = 'K', + aReload = 'R', + aShowVersions = 'V', + aShowConfigs = 'X', + oNoVerbose = 500, oHomedir, oBuilddir, @@ -61,15 +67,10 @@ enum cmd_and_opt_values aListConfig, aCheckConfig, aQuerySWDB, - aListDirs, aLaunch, - aKill, aCreateSocketDir, aRemoveSocketDir, - aApplyProfile, - aReload, - aShowConfigs, - aShowVersions + aApplyProfile }; @@ -100,8 +101,8 @@ static ARGPARSE_OPTS opts[] = { aKill, "kill", 256, N_("kill a given component")}, { aCreateSocketDir, "create-socketdir", 256, "@"}, { aRemoveSocketDir, "remove-socketdir", 256, "@"}, - ARGPARSE_c (aShowVersions, "show-versions", "@"), - ARGPARSE_c (aShowConfigs, "show-configs", "@"), + ARGPARSE_c (aShowVersions, "show-versions", ""), + ARGPARSE_c (aShowConfigs, "show-configs", ""), { 301, NULL, 0, N_("@\nOptions:\n ") }, @@ -608,7 +609,7 @@ main (int argc, char **argv) early_system_init (); gnupg_reopen_std (GPGCONF_NAME); set_strusage (my_strusage); - log_set_prefix (GPGCONF_NAME, GPGRT_LOG_WITH_PREFIX); + log_set_prefix (GPGCONF_NAME, GPGRT_LOG_WITH_PREFIX|GPGRT_LOG_NO_REGISTRY); /* Make sure that our subsystems are ready. */ i18n_init(); @@ -678,6 +679,12 @@ main (int argc, char **argv) fname = argc ? *argv : NULL; + /* Set the configuraton directories for use by gpgrt_argparser. We + * don't have a configuration file for this program but we have code + * which reads the component's config files. */ + gnupg_set_confdir (GNUPG_CONFDIR_SYS, gnupg_sysconfdir ()); + gnupg_set_confdir (GNUPG_CONFDIR_USER, gnupg_homedir ()); + switch (cmd) { case aListComponents: @@ -1035,8 +1042,43 @@ get_revision_from_blurb (const char *blurb, int *r_len) static void show_version_gnupg (estream_t fp, const char *prefix) { + char *fname, *p; + size_t n; + estream_t verfp; + char line[100]; + es_fprintf (fp, "%s%sGnuPG %s (%s)\n%s%s\n", prefix, *prefix?"":"* ", - strusage (13), BUILD_REVISION, prefix, gpgrt_strusage (17)); + strusage (13), BUILD_REVISION, prefix, strusage (17)); + + /* Show the GnuPG VS-Desktop version in --show-configs mode */ + if (prefix && *prefix == '#') + { + fname = make_filename (gnupg_bindir (), NULL); + n = strlen (fname); + if (n > 10 && (!ascii_strcasecmp (fname + n - 10, "/GnuPG/bin") + || !ascii_strcasecmp (fname + n - 10, "\\GnuPG\\bin"))) + { + /* Append VERSION to the ../../ direcory. Note that VERSION + * is only 7 bytes and thus fits. */ + strcpy (fname + n - 9, "VERSION"); + verfp = es_fopen (fname, "r"); + if (!verfp) + es_fprintf (fp, "%s[VERSION file not found]\n", prefix); + else if (!es_fgets (line, sizeof line, verfp)) + es_fprintf (fp, "%s[VERSION file is empty]\n", prefix); + else + { + trim_spaces (line); + for (p=line; *p; p++) + if (*p < ' ' || *p > '~' || *p == '[') + *p = '?'; + es_fprintf (fp, "%s%s\n", prefix, line); + } + es_fclose (verfp); + } + xfree (fname); + } + #ifdef HAVE_W32_SYSTEM { OSVERSIONINFO osvi = { sizeof (osvi) }; @@ -1277,7 +1319,8 @@ show_other_registry_entries (estream_t outfp) { 1, "HKLM\\Software\\Gpg4win:Desktop-Version" }, { 1, "HKLM\\Software\\Gpg4win:VS-Desktop-Version" }, { 1, "\\" GNUPG_REGISTRY_DIR ":HomeDir" }, - { 2, "Software\\Microsoft\\Office\\Outlook\\Addins\\GNU.GpgOL" + { 1, "\\" GNUPG_REGISTRY_DIR ":DefaultLogFile" }, + { 2, "\\Software\\Microsoft\\Office\\Outlook\\Addins\\GNU.GpgOL" ":LoadBehavior" }, { 2, "HKCU\\Software\\Microsoft\\Office\\16.0\\Outlook\\Options\\Mail:" "ReadAsPlain" }, @@ -1309,6 +1352,7 @@ show_other_registry_entries (estream_t outfp) int group = 0; char *namebuf = NULL; const char *name; + int from_hklm; for (idx=0; (name = names[idx].name); idx++) { @@ -1322,7 +1366,7 @@ show_other_registry_entries (estream_t outfp) name = namebuf; } - value = read_w32_reg_string (name); + value = read_w32_reg_string (name, &from_hklm); if (!value) continue; @@ -1337,12 +1381,11 @@ show_other_registry_entries (estream_t outfp) } if (group == 3) - es_fprintf (outfp, "### %s=%s\n", names[idx].name, value); + es_fprintf (outfp, "### %s=%s%s\n", names[idx].name, value, + from_hklm? " [hklm]":""); else - es_fprintf (outfp, "### %s\n### ->%s<-\n", name, value); - - /* FIXME: We may want to add an indiction whethe found via HKLM - * or HKCU. */ + es_fprintf (outfp, "### %s\n### ->%s<-%s\n", name, value, + from_hklm? " [hklm]":""); xfree (value); } @@ -1350,8 +1393,78 @@ show_other_registry_entries (estream_t outfp) es_fprintf (outfp, "###\n"); xfree (namebuf); } + + +/* Print registry entries take from a configuration file. */ +static void +show_registry_entries_from_file (estream_t outfp) +{ + gpg_error_t err; + char *fname; + estream_t fp; + char *line = NULL; + size_t length_of_line = 0; + size_t maxlen; + ssize_t len; + char *value = NULL; + int from_hklm; + int any = 0; + + fname = make_filename (gnupg_datadir (), "gpgconf.rnames", NULL); + fp = es_fopen (fname, "r"); + if (!fp) + { + err = gpg_error_from_syserror (); + if (gpg_err_code (err) != GPG_ERR_ENOENT) + log_error ("error opening '%s': %s\n", fname, gpg_strerror (err)); + goto leave; + } + + maxlen = 2048; /* Set limit. */ + while ((len = es_read_line (fp, &line, &length_of_line, &maxlen)) > 0) + { + if (!maxlen) + { + err = gpg_error (GPG_ERR_LINE_TOO_LONG); + log_error ("error reading '%s': %s\n", fname, gpg_strerror (err)); + goto leave; + } + trim_spaces (line); + if (*line == '#') + continue; + + xfree (value); + value = read_w32_reg_string (line, &from_hklm); + if (!value) + continue; + + if (!any) + { + any = 1; + es_fprintf (outfp, "### Taken from gpgconf.rnames:\n"); + } + + es_fprintf (outfp, "### %s\n### ->%s<-%s\n", line, value, + from_hklm? " [hklm]":""); + + } + if (len < 0 || es_ferror (fp)) + { + err = gpg_error_from_syserror (); + log_error ("error reading '%s': %s\n", fname, gpg_strerror (err)); + } + + leave: + if (any) + es_fprintf (outfp, "###\n"); + xfree (value); + xfree (line); + es_fclose (fp); + xfree (fname); +} #endif /*HAVE_W32_SYSTEM*/ + /* Show all config files. */ static void show_configs (estream_t outfp) @@ -1374,6 +1487,15 @@ show_configs (estream_t outfp) show_version_gnupg (outfp, "### "); es_fprintf (outfp, "### Libgcrypt %s\n", gcry_check_version (NULL)); es_fprintf (outfp, "### GpgRT %s\n", gpg_error_check_version (NULL)); +#ifdef HAVE_W32_SYSTEM + es_fprintf (outfp, "### Codepages:"); + if (GetConsoleCP () != GetConsoleOutputCP ()) + es_fprintf (outfp, " %u/%u", GetConsoleCP (), GetConsoleOutputCP ()); + else + es_fprintf (outfp, " %u", GetConsoleCP ()); + es_fprintf (outfp, " %u", GetACP ()); + es_fprintf (outfp, " %u\n", GetOEMCP ()); +#endif es_fprintf (outfp, "###\n\n"); list_dirs (outfp, NULL, 1); @@ -1422,14 +1544,16 @@ show_configs (estream_t outfp) if ((sl->flags & 1)) { char *p; + int from_hklm; if (!any) { any = 1; es_fprintf (outfp, "###\n### Encountered in config files:\n"); } - if ((p = read_w32_reg_string (sl->d))) - es_fprintf (outfp, "### %s ->%s<-\n", sl->d, p); + if ((p = read_w32_reg_string (sl->d, &from_hklm))) + es_fprintf (outfp, "### %s ->%s<-%s\n", sl->d, p, + from_hklm? " [hklm]":""); else es_fprintf (outfp, "### %s [not set]\n", sl->d); xfree (p); @@ -1438,6 +1562,7 @@ show_configs (estream_t outfp) if (!any) es_fprintf (outfp, "###\n"); show_other_registry_entries (outfp); + show_registry_entries_from_file (outfp); #endif /*HAVE_W32_SYSTEM*/ free_strlist (list); diff --git a/tools/gpgconf.h b/tools/gpgconf.h index adb90a1..83aee9a 100644 --- a/tools/gpgconf.h +++ b/tools/gpgconf.h @@ -46,21 +46,24 @@ void gpgconf_failure (gpg_error_t err) GPGRT_ATTR_NORETURN; /* Component system. Each component is a set of options that can be * configured at the same time. If you change this, don't forget to - * update GC_COMPONENT in gpgconf-comp.c. */ + * update gc_component[] in gpgconf-comp.c. */ typedef enum { + /* Any component, used as a wildcard arg. */ + GC_COMPONENT_ANY, + /* The classic GPG for OpenPGP. */ GC_COMPONENT_GPG, + /* GPG for S/MIME. */ + GC_COMPONENT_GPGSM, + /* The GPG Agent. */ GC_COMPONENT_GPG_AGENT, /* The Smardcard Daemon. */ GC_COMPONENT_SCDAEMON, - /* GPG for S/MIME. */ - GC_COMPONENT_GPGSM, - /* The LDAP Directory Manager for CRLs. */ GC_COMPONENT_DIRMNGR, @@ -69,7 +72,7 @@ typedef enum /* The number of components. */ GC_COMPONENT_NR - } gc_component_t; + } gc_component_id_t; /* Initialize the components. */ diff --git a/tools/gpgtar-create.c b/tools/gpgtar-create.c index f1b0485..e642da0 100644 --- a/tools/gpgtar-create.c +++ b/tools/gpgtar-create.c @@ -1,4 +1,6 @@ /* gpgtar-create.c - Create a TAR archive + * Copyright (C) 2016-2017, 2019-2022 g10 Code GmbH + * Copyright (C) 2010, 2012, 2013 Werner Koch * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -15,6 +17,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later */ #include <config.h> @@ -25,20 +28,21 @@ #include <sys/types.h> #include <sys/stat.h> #include <dirent.h> +#include <unistd.h> #ifdef HAVE_W32_SYSTEM # define WIN32_LEAN_AND_MEAN # include <windows.h> #else /*!HAVE_W32_SYSTEM*/ -# include <unistd.h> # include <pwd.h> # include <grp.h> #endif /*!HAVE_W32_SYSTEM*/ -#include <assert.h> #include "../common/i18n.h" -#include "../common/exectool.h" +#include <gpg-error.h> +#include "../common/exechelp.h" #include "../common/sysutils.h" #include "../common/ccparray.h" +#include "../common/membuf.h" #include "gpgtar.h" #ifndef HAVE_LSTAT @@ -46,6 +50,11 @@ #endif +/* Count the number of written headers. Extended headers are not + * counted. */ +static unsigned long global_header_count; + + /* Object to control the file scanning. */ struct scanctrl_s; typedef struct scanctrl_s *scanctrl_t; @@ -105,7 +114,7 @@ fillup_entry_w32 (tar_header_t hdr) for (p=hdr->name; *p; p++) if (*p == '/') *p = '\\'; - wfname = utf8_to_wchar (hdr->name); + wfname = gpgrt_fname_to_wchar (hdr->name); for (p=hdr->name; *p; p++) if (*p == '\\') *p = '/'; @@ -285,8 +294,10 @@ add_entry (const char *dname, const char *entryname, scanctrl_t scanctrl) xfree (hdr); else { + /* FIXME: We don't have the extended info yet available so we + * can't print them. */ if (opt.verbose) - gpgtar_print_header (hdr, log_get_stream ()); + gpgtar_print_header (hdr, NULL, log_get_stream ()); *scanctrl->flist_tail = hdr; scanctrl->flist_tail = &hdr->next; } @@ -334,7 +345,7 @@ scan_directory (const char *dname, scanctrl_t scanctrl) for (p=fname; *p; p++) if (*p == '/') *p = '\\'; - wfname = utf8_to_wchar (fname); + wfname = gpgrt_fname_to_wchar (fname); xfree (fname); if (!wfname) { @@ -437,7 +448,7 @@ scan_recursive (const char *dname, scanctrl_t scanctrl) } scanctrl->nestlevel++; - assert (scanctrl->flist_tail); + log_assert (scanctrl->flist_tail); start_tail = scanctrl->flist_tail; scan_directory (dname, scanctrl); stop_tail = scanctrl->flist_tail; @@ -488,7 +499,7 @@ store_xoctal (char *buffer, size_t length, unsigned long long value) size_t n; unsigned long long v; - assert (length > 1); + log_assert (length > 1); v = value; n = length; @@ -593,16 +604,75 @@ store_gname (char *buffer, size_t length, unsigned long gid) } +static void +compute_checksum (void *record) +{ + struct ustar_raw_header *raw = record; + unsigned long chksum = 0; + unsigned char *p; + size_t n; + + memset (raw->checksum, ' ', sizeof raw->checksum); + p = record; + for (n=0; n < RECORDSIZE; n++) + chksum += *p++; + store_xoctal (raw->checksum, sizeof raw->checksum - 1, chksum); + raw->checksum[7] = ' '; +} + + + +/* Read a symlink without truncating it. Caller must release the + * returned buffer. Returns NULL on error. */ +#ifndef HAVE_W32_SYSTEM +static char * +myreadlink (const char *name) +{ + char *buffer; + size_t size; + int nread; + + for (size = 1024; size <= 65536; size *= 2) + { + buffer = xtrymalloc (size); + if (!buffer) + return NULL; + + nread = readlink (name, buffer, size - 1); + if (nread < 0) + { + xfree (buffer); + return NULL; + } + if (nread < size - 1) + { + buffer[nread] = 0; + return buffer; /* Got it. */ + } + + xfree (buffer); + } + gpg_err_set_errno (ERANGE); + return NULL; +} +#endif /*Unix*/ + + + +/* Build a header. If the filename or the link name ist too long + * allocate an exthdr and use a replacement file name in RECORD. + * Caller should always release R_EXTHDR; this function initializes it + * to point to NULL. */ static gpg_error_t -build_header (void *record, tar_header_t hdr) +build_header (void *record, tar_header_t hdr, strlist_t *r_exthdr) { gpg_error_t err; struct ustar_raw_header *raw = record; size_t namelen, n; - unsigned long chksum; - unsigned char *p; + strlist_t sl; memset (record, 0, RECORDSIZE); + *r_exthdr = NULL; /* Store name and prefix. */ namelen = strlen (hdr->name); @@ -623,10 +693,23 @@ build_header (void *record, tar_header_t hdr) } else { - err = gpg_error (GPG_ERR_TOO_LARGE); - log_error ("error storing file '%s': %s\n", - hdr->name, gpg_strerror (err)); - return err; + /* Too long - prepare extended header. */ + sl = add_to_strlist_try (r_exthdr, hdr->name); + if (!sl) + { + err = gpg_error_from_syserror (); + log_error ("error storing file '%s': %s\n", + hdr->name, gpg_strerror (err)); + return err; + } + sl->flags = 1; /* Mark as path */ + /* The name we use is not POSIX compliant but because we + * expect that (for security issues) a tarball will anyway + * be extracted to a unique new directory, a simple counter + * will do. To ease testing we also put in the PID. The + * count is bumped after the header has been written. */ + snprintf (raw->name, sizeof raw->name-1, "_@paxheader.%u.%lu", + (unsigned int)getpid(), global_header_count + 1); } } @@ -659,6 +742,7 @@ build_header (void *record, tar_header_t hdr) if (hdr->typeflag == TF_SYMLINK) { int nread; + char *p; nread = readlink (hdr->name, raw->linkname, sizeof raw->linkname -1); if (nread < 0) @@ -669,22 +753,133 @@ build_header (void *record, tar_header_t hdr) return err; } raw->linkname[nread] = 0; + if (nread == sizeof raw->linkname -1) + { + /* Truncated - read again and store as extended header. */ + p = myreadlink (hdr->name); + if (!p) + { + err = gpg_error_from_syserror (); + log_error ("error reading symlink '%s': %s\n", + hdr->name, gpg_strerror (err)); + return err; + } + + sl = add_to_strlist_try (r_exthdr, p); + xfree (p); + if (!sl) + { + err = gpg_error_from_syserror (); + log_error ("error storing syslink '%s': %s\n", + hdr->name, gpg_strerror (err)); + return err; + } + sl->flags = 2; /* Mark as linkpath */ + } } -#endif /*HAVE_W32_SYSTEM*/ +#endif /*!HAVE_W32_SYSTEM*/ - /* Compute the checksum. */ - memset (raw->checksum, ' ', sizeof raw->checksum); - chksum = 0; - p = record; - for (n=0; n < RECORDSIZE; n++) - chksum += *p++; - store_xoctal (raw->checksum, sizeof raw->checksum - 1, chksum); - raw->checksum[7] = ' '; + compute_checksum (record); return 0; } +/* Add an extended header record (NAME,VALUE) to the buffer MB. */ +static void +add_extended_header_record (membuf_t *mb, const char *name, const char *value) +{ + size_t n, n0, n1; + char numbuf[35]; + size_t valuelen; + + /* To avoid looping in most cases, we guess the initial value. */ + valuelen = strlen (value); + n1 = valuelen > 95? 3 : 2; + do + { + n0 = n1; + /* (3 for the space before name, the '=', and the LF.) */ + n = n0 + strlen (name) + valuelen + 3; + snprintf (numbuf, sizeof numbuf, "%zu", n); + n1 = strlen (numbuf); + } + while (n0 != n1); + put_membuf_str (mb, numbuf); + put_membuf (mb, " ", 1); + put_membuf_str (mb, name); + put_membuf (mb, "=", 1); + put_membuf (mb, value, valuelen); + put_membuf (mb, "\n", 1); +} + + + +/* Write the extended header specified by EXTHDR to STREAM. */ +static gpg_error_t +write_extended_header (estream_t stream, const void *record, strlist_t exthdr) +{ + gpg_error_t err = 0; + struct ustar_raw_header raw; + strlist_t sl; + membuf_t mb; + char *buffer, *p; + size_t buflen; + + init_membuf (&mb, 2*RECORDSIZE); + + for (sl=exthdr; sl; sl = sl->next) + { + if (sl->flags == 1) + add_extended_header_record (&mb, "path", sl->d); + else if (sl->flags == 2) + add_extended_header_record (&mb, "linkpath", sl->d); + } + + buffer = get_membuf (&mb, &buflen); + if (!buffer) + { + err = gpg_error_from_syserror (); + log_error ("error building extended header: %s\n", gpg_strerror (err)); + goto leave; + } + + /* We copy the header from the standard header record, so that an + * extracted extended header (using a non-pax aware software) is + * written with the same properties as the original file. The real + * entry will overwrite it anyway. Of course we adjust the size and + * the type. */ + memcpy (&raw, record, RECORDSIZE); + store_xoctal (raw.size, sizeof raw.size, buflen); + raw.typeflag[0] = 'x'; /* Mark as extended header. */ + compute_checksum (&raw); + + err = write_record (stream, &raw); + if (err) + goto leave; + + for (p = buffer; buflen >= RECORDSIZE; p += RECORDSIZE, buflen -= RECORDSIZE) + { + err = write_record (stream, p); + if (err) + goto leave; + } + if (buflen) + { + /* Reuse RAW for builidng the last record. */ + memcpy (&raw, p, buflen); + memset ((char*)&raw+buflen, 0, RECORDSIZE - buflen); + err = write_record (stream, &raw); + if (err) + goto leave; + } + + leave: + xfree (buffer); + return err; +} + + static gpg_error_t write_file (estream_t stream, tar_header_t hdr) { @@ -692,9 +887,10 @@ write_file (estream_t stream, tar_header_t hdr) char record[RECORDSIZE]; estream_t infp; size_t nread, nbytes; + strlist_t exthdr = NULL; int any; - err = build_header (record, hdr); + err = build_header (record, hdr, &exthdr); if (err) { if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED) @@ -707,7 +903,7 @@ write_file (estream_t stream, tar_header_t hdr) if (hdr->typeflag == TF_REGULAR) { - infp = es_fopen (hdr->name, "rb"); + infp = es_fopen (hdr->name, "rb,sysopen"); if (!infp) { err = gpg_error_from_syserror (); @@ -719,9 +915,12 @@ write_file (estream_t stream, tar_header_t hdr) else infp = NULL; + if (exthdr && (err = write_extended_header (stream, record, exthdr))) + goto leave; err = write_record (stream, record); if (err) goto leave; + global_header_count++; if (hdr->typeflag == TF_REGULAR) { @@ -741,6 +940,8 @@ write_file (estream_t stream, tar_header_t hdr) any? " (file shrunk?)":""); goto leave; } + else if (nbytes < RECORDSIZE) + memset (record + nbytes, 0, RECORDSIZE - nbytes); any = 1; err = write_record (stream, record); if (err) @@ -757,6 +958,7 @@ write_file (estream_t stream, tar_header_t hdr) else if ((err = es_fclose (infp))) log_error ("error closing file '%s': %s\n", hdr->name, gpg_strerror (err)); + free_strlist (exthdr); return err; } @@ -791,8 +993,8 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, tar_header_t hdr, *start_tail; estream_t files_from_stream = NULL; estream_t outstream = NULL; - estream_t cipher_stream = NULL; int eof_seen = 0; + pid_t pid = (pid_t)(-1); memset (scanctrl, 0, sizeof *scanctrl); scanctrl->flist_tail = &scanctrl->flist; @@ -945,64 +1147,37 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, if (files_from_stream && files_from_stream != es_stdin) es_fclose (files_from_stream); - if (opt.outfile) - { - if (!strcmp (opt.outfile, "-")) - outstream = es_stdout; - else - outstream = es_fopen (opt.outfile, "wb"); - if (!outstream) - { - err = gpg_error_from_syserror (); - goto leave; - } - } - else - { - outstream = es_stdout; - } - - if (outstream == es_stdout) - es_set_binary (es_stdout); - - if (encrypt || sign) - { - cipher_stream = outstream; - outstream = es_fopenmem (0, "rwb"); - if (! outstream) - { - err = gpg_error_from_syserror (); - goto leave; - } - } - - for (hdr = scanctrl->flist; hdr; hdr = hdr->next) - { - err = write_file (outstream, hdr); - if (err) - goto leave; - } - err = write_eof_mark (outstream); - if (err) - goto leave; - if (encrypt || sign) { strlist_t arg; ccparray_t ccp; const char **argv; - err = es_fseek (outstream, 0, SEEK_SET); - if (err) - goto leave; - /* '--encrypt' may be combined with '--symmetric', but 'encrypt' - is set either way. Clear it if no recipients are specified. - XXX: Fix command handling. */ + * is set either way. Clear it if no recipients are specified. + */ if (opt.symmetric && opt.recipients == NULL) encrypt = 0; ccparray_init (&ccp, 0); + if (opt.batch) + ccparray_put (&ccp, "--batch"); + if (opt.answer_yes) + ccparray_put (&ccp, "--yes"); + if (opt.answer_no) + ccparray_put (&ccp, "--no"); + if (opt.require_compliance) + ccparray_put (&ccp, "--require-compliance"); + if (opt.status_fd != -1) + { + static char tmpbuf[40]; + + snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%d", opt.status_fd); + ccparray_put (&ccp, tmpbuf); + } + + ccparray_put (&ccp, "--output"); + ccparray_put (&ccp, opt.outfile? opt.outfile : "-"); if (encrypt) ccparray_put (&ccp, "--encrypt"); if (sign) @@ -1030,27 +1205,76 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, goto leave; } - err = gnupg_exec_tool_stream (opt.gpg_program, argv, - outstream, NULL, cipher_stream, NULL, NULL); + err = gnupg_spawn_process (opt.gpg_program, argv, NULL, NULL, + (GNUPG_SPAWN_KEEP_STDOUT + | GNUPG_SPAWN_KEEP_STDERR), + &outstream, NULL, NULL, &pid); xfree (argv); if (err) goto leave; + es_set_binary (outstream); + } + else if (opt.outfile) /* No crypto */ + { + if (!strcmp (opt.outfile, "-")) + outstream = es_stdout; + else + outstream = es_fopen (opt.outfile, "wb,sysopen"); + if (!outstream) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (outstream == es_stdout) + es_set_binary (es_stdout); + + } + else /* Also no crypto. */ + { + outstream = es_stdout; + es_set_binary (outstream); + } + + + for (hdr = scanctrl->flist; hdr; hdr = hdr->next) + { + err = write_file (outstream, hdr); + if (err) + goto leave; + } + err = write_eof_mark (outstream); + if (err) + goto leave; + + + if (pid != (pid_t)(-1)) + { + int exitcode; + + err = es_fclose (outstream); + outstream = NULL; + if (err) + log_error ("error closing pipe: %s\n", gpg_strerror (err)); + else + { + err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); + if (err) + log_error ("running %s failed (exitcode=%d): %s", + opt.gpg_program, exitcode, gpg_strerror (err)); + gnupg_release_process (pid); + pid = (pid_t)(-1); + } } leave: if (!err) { gpg_error_t first_err; - if (outstream != es_stdout) + if (outstream != es_stdout || pid != (pid_t)(-1)) first_err = es_fclose (outstream); else first_err = es_fflush (outstream); outstream = NULL; - if (cipher_stream != es_stdout) - err = es_fclose (cipher_stream); - else - err = es_fflush (cipher_stream); - cipher_stream = NULL; if (! err) err = first_err; } @@ -1060,8 +1284,6 @@ gpgtar_create (char **inpattern, const char *files_from, int null_names, opt.outfile ? opt.outfile : "-", gpg_strerror (err)); if (outstream && outstream != es_stdout) es_fclose (outstream); - if (cipher_stream && cipher_stream != es_stdout) - es_fclose (cipher_stream); if (opt.outfile) gnupg_remove (opt.outfile); } diff --git a/tools/gpgtar-extract.c b/tools/gpgtar-extract.c index 3da100c..832039b 100644 --- a/tools/gpgtar-extract.c +++ b/tools/gpgtar-extract.c @@ -1,4 +1,6 @@ /* gpgtar-extract.c - Extract from a TAR archive + * Copyright (C) 2016-2017, 2019-2022 g10 Code GmbH + * Copyright (C) 2010, 2012, 2013 Werner Koch * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -15,6 +17,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later */ #include <config.h> @@ -25,39 +28,79 @@ #include <sys/types.h> #include <sys/stat.h> #include <unistd.h> -#include <assert.h> #include "../common/i18n.h" -#include "../common/exectool.h" +#include <gpg-error.h> +#include "../common/exechelp.h" #include "../common/sysutils.h" #include "../common/ccparray.h" #include "gpgtar.h" +static gpg_error_t +check_suspicious_name (const char *name) +{ + size_t n; + + n = strlen (name); +#ifdef HAVE_DOSISH_SYSTEM + if (strchr (name, '\\')) + { + log_error ("filename '%s' contains a backslash - " + "can't extract on this system\n", name); + return gpg_error (GPG_ERR_INV_NAME); + } +#endif /*HAVE_DOSISH_SYSTEM*/ + + if (!n + || strstr (name, "//") + || strstr (name, "/../") + || !strncmp (name, "../", 3) + || (n >= 3 && !strcmp (name+n-3, "/.." ))) + { + log_error ("filename '%s' has suspicious parts - not extracting\n", + name); + return gpg_error (GPG_ERR_INV_NAME); + } + + return 0; +} + static gpg_error_t extract_regular (estream_t stream, const char *dirname, - tarinfo_t info, tar_header_t hdr) + tarinfo_t info, tar_header_t hdr, strlist_t exthdr) { gpg_error_t err; char record[RECORDSIZE]; size_t n, nbytes, nwritten; - char *fname; + char *fname_buffer = NULL; + const char *fname; estream_t outfp = NULL; + strlist_t sl; - fname = strconcat (dirname, "/", hdr->name, NULL); - if (!fname) + fname = hdr->name; + for (sl = exthdr; sl; sl = sl->next) + if (sl->flags == 1) + fname = sl->d; + + err = check_suspicious_name (fname); + if (err) + goto leave; + + fname_buffer = strconcat (dirname, "/", fname, NULL); + if (!fname_buffer) { err = gpg_error_from_syserror (); log_error ("error creating filename: %s\n", gpg_strerror (err)); goto leave; } - else - err = 0; + fname = fname_buffer; + if (opt.dry_run) - outfp = es_fopenmem (0, "wb"); + outfp = es_fopen ("/dev/null", "wb"); else - outfp = es_fopen (fname, "wb"); + outfp = es_fopen (fname, "wb,sysopen"); if (!outfp) { err = gpg_error_from_syserror (); @@ -97,29 +140,36 @@ extract_regular (estream_t stream, const char *dirname, log_error ("error removing incomplete file '%s': %s\n", fname, gpg_strerror (gpg_error_from_syserror ())); } - xfree (fname); + xfree (fname_buffer); return err; } static gpg_error_t -extract_directory (const char *dirname, tar_header_t hdr) +extract_directory (const char *dirname, tar_header_t hdr, strlist_t exthdr) { gpg_error_t err; - char *fname; - size_t prefixlen; + const char *name; + char *fname = NULL; + strlist_t sl; + + name = hdr->name; + for (sl = exthdr; sl; sl = sl->next) + if (sl->flags == 1) + name = sl->d; + + err = check_suspicious_name (name); + if (err) + goto leave; - prefixlen = strlen (dirname) + 1; - fname = strconcat (dirname, "/", hdr->name, NULL); + fname = strconcat (dirname, "/", name, NULL); if (!fname) { err = gpg_error_from_syserror (); log_error ("error creating filename: %s\n", gpg_strerror (err)); goto leave; } - else - err = 0; - + /* Remove a possible trailing slash. */ if (fname[strlen (fname)-1] == '/') fname[strlen (fname)-1] = 0; @@ -136,8 +186,13 @@ extract_directory (const char *dirname, tar_header_t hdr) { /* Try to create the directory with parents but keep the original error code in case of a failure. */ - char *p; int rc = 0; + char *p; + size_t prefixlen; + + /* (PREFIXLEN is the length of the new directory we use to + * extract the tarball.) */ + prefixlen = strlen (dirname) + 1; for (p = fname+prefixlen; (p = strchr (p, '/')); p++) { @@ -165,36 +220,15 @@ extract_directory (const char *dirname, tar_header_t hdr) static gpg_error_t extract (estream_t stream, const char *dirname, tarinfo_t info, - tar_header_t hdr) + tar_header_t hdr, strlist_t exthdr) { gpg_error_t err; size_t n; - n = strlen (hdr->name); -#ifdef HAVE_DOSISH_SYSTEM - if (strchr (hdr->name, '\\')) - { - log_error ("filename '%s' contains a backslash - " - "can't extract on this system\n", hdr->name); - return gpg_error (GPG_ERR_INV_NAME); - } -#endif /*HAVE_DOSISH_SYSTEM*/ - - if (!n - || strstr (hdr->name, "//") - || strstr (hdr->name, "/../") - || !strncmp (hdr->name, "../", 3) - || (n >= 3 && !strcmp (hdr->name+n-3, "/.." ))) - { - log_error ("filename '%s' as suspicious parts - not extracting\n", - hdr->name); - return gpg_error (GPG_ERR_INV_NAME); - } - if (hdr->typeflag == TF_REGULAR || hdr->typeflag == TF_UNKNOWN) - err = extract_regular (stream, dirname, info, hdr); + err = extract_regular (stream, dirname, info, hdr, exthdr); else if (hdr->typeflag == TF_DIRECTORY) - err = extract_directory (dirname, hdr); + err = extract_directory (dirname, hdr, exthdr); else { char record[RECORDSIZE]; @@ -283,34 +317,53 @@ gpg_error_t gpgtar_extract (const char *filename, int decrypt) { gpg_error_t err; - estream_t stream; - estream_t cipher_stream = NULL; + estream_t stream = NULL; tar_header_t header = NULL; + strlist_t extheader = NULL; const char *dirprefix = NULL; char *dirname = NULL; struct tarinfo_s tarinfo_buffer; tarinfo_t tarinfo = &tarinfo_buffer; + pid_t pid = (pid_t)(-1); + char *logfilename = NULL; + memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer); - if (filename) + if (opt.directory) + dirname = xtrystrdup (opt.directory); + else { - if (!strcmp (filename, "-")) - stream = es_stdin; - else - stream = es_fopen (filename, "rb"); - if (!stream) + if (opt.filename) { - err = gpg_error_from_syserror (); - log_error ("error opening '%s': %s\n", filename, gpg_strerror (err)); - return err; + dirprefix = strrchr (opt.filename, '/'); + if (dirprefix) + dirprefix++; + else + dirprefix = opt.filename; + } + else if (filename) + { + dirprefix = strrchr (filename, '/'); + if (dirprefix) + dirprefix++; + else + dirprefix = filename; + } + + if (!dirprefix || !*dirprefix) + dirprefix = "GPGARCH"; + + dirname = create_directory (dirprefix); + if (!dirname) + { + err = gpg_error (GPG_ERR_GENERAL); + goto leave; } } - else - stream = es_stdin; - if (stream == es_stdin) - es_set_binary (es_stdin); + if (opt.verbose) + log_info ("extracting to '%s/'\n", dirname); if (decrypt) { @@ -318,19 +371,34 @@ gpgtar_extract (const char *filename, int decrypt) ccparray_t ccp; const char **argv; - cipher_stream = stream; - stream = es_fopenmem (0, "rwb"); - if (! stream) - { - err = gpg_error_from_syserror (); - goto leave; - } - ccparray_init (&ccp, 0); + if (opt.batch) + ccparray_put (&ccp, "--batch"); + if (opt.require_compliance) + ccparray_put (&ccp, "--require-compliance"); + if (opt.status_fd != -1) + { + static char tmpbuf[40]; + snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%d", opt.status_fd); + ccparray_put (&ccp, tmpbuf); + } + if (opt.with_log) + { + ccparray_put (&ccp, "--log-file"); + logfilename = xstrconcat (dirname, ".log", NULL); + ccparray_put (&ccp, logfilename); + } + ccparray_put (&ccp, "--output"); + ccparray_put (&ccp, "-"); ccparray_put (&ccp, "--decrypt"); for (arg = opt.gpg_arguments; arg; arg = arg->next) ccparray_put (&ccp, arg->d); + if (filename) + { + ccparray_put (&ccp, "--"); + ccparray_put (&ccp, filename); + } ccparray_put (&ccp, NULL); argv = ccparray_get (&ccp, NULL); @@ -340,72 +408,78 @@ gpgtar_extract (const char *filename, int decrypt) goto leave; } - err = gnupg_exec_tool_stream (opt.gpg_program, argv, - cipher_stream, NULL, stream, NULL, NULL); + err = gnupg_spawn_process (opt.gpg_program, argv, NULL, NULL, + ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN) + | GNUPG_SPAWN_KEEP_STDERR), + NULL, &stream, NULL, &pid); xfree (argv); if (err) goto leave; - - err = es_fseek (stream, 0, SEEK_SET); - if (err) - goto leave; + es_set_binary (stream); } - - if (opt.directory) - dirname = xtrystrdup (opt.directory); - else + else if (filename) { - if (opt.filename) - { - dirprefix = strrchr (opt.filename, '/'); - if (dirprefix) - dirprefix++; - else - dirprefix = opt.filename; - } - else if (filename) - { - dirprefix = strrchr (filename, '/'); - if (dirprefix) - dirprefix++; - else - dirprefix = filename; - } - - if (!dirprefix || !*dirprefix) - dirprefix = "GPGARCH"; - - dirname = create_directory (dirprefix); - if (!dirname) + if (!strcmp (filename, "-")) + stream = es_stdin; + else + stream = es_fopen (filename, "rb,sysopen"); + if (!stream) { - err = gpg_error (GPG_ERR_GENERAL); - goto leave; + err = gpg_error_from_syserror (); + log_error ("error opening '%s': %s\n", filename, gpg_strerror (err)); + return err; } + if (stream == es_stdin) + es_set_binary (es_stdin); + } + else + { + stream = es_stdin; + es_set_binary (es_stdin); } - if (opt.verbose) - log_info ("extracting to '%s/'\n", dirname); for (;;) { - err = gpgtar_read_header (stream, tarinfo, &header); + err = gpgtar_read_header (stream, tarinfo, &header, &extheader); if (err || header == NULL) goto leave; - err = extract (stream, dirname, tarinfo, header); + err = extract (stream, dirname, tarinfo, header, extheader); if (err) goto leave; + free_strlist (extheader); + extheader = NULL; xfree (header); header = NULL; } + if (pid != (pid_t)(-1)) + { + int exitcode; + + err = es_fclose (stream); + stream = NULL; + if (err) + log_error ("error closing pipe: %s\n", gpg_strerror (err)); + else + { + err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); + if (err) + log_error ("running %s failed (exitcode=%d): %s", + opt.gpg_program, exitcode, gpg_strerror (err)); + gnupg_release_process (pid); + pid = (pid_t)(-1); + } + } + leave: + free_strlist (extheader); xfree (header); xfree (dirname); + xfree (logfilename); if (stream != es_stdin) es_fclose (stream); - if (stream != cipher_stream) - es_fclose (cipher_stream); return err; } diff --git a/tools/gpgtar-list.c b/tools/gpgtar-list.c index 396e837..08ab967 100644 --- a/tools/gpgtar-list.c +++ b/tools/gpgtar-list.c @@ -1,4 +1,6 @@ /* gpgtar-list.c - List a TAR archive + * Copyright (C) 2016-2017, 2019-2022 g10 Code GmbH + * Copyright (C) 2010, 2012, 2013 Werner Koch * Copyright (C) 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. @@ -15,6 +17,7 @@ * * You should have received a copy of the GNU General Public License * along with this program; if not, see <https://www.gnu.org/licenses/>. + * SPDX-License-Identifier: GPL-3.0-or-later */ #include <config.h> @@ -22,11 +25,12 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <assert.h> #include "../common/i18n.h" +#include <gpg-error.h> #include "gpgtar.h" -#include "../common/exectool.h" +#include "../common/exechelp.h" +#include "../common/sysutils.h" #include "../common/ccparray.h" @@ -160,11 +164,15 @@ parse_header (const void *record, const char *filename, tarinfo_t info) case '5': header->typeflag = TF_DIRECTORY; break; case '6': header->typeflag = TF_FIFO; break; case '7': header->typeflag = TF_RESERVED; break; + case 'g': header->typeflag = TF_GEXTHDR; break; + case 'x': header->typeflag = TF_EXTHDR; break; default: header->typeflag = TF_UNKNOWN; break; } /* Compute the number of data records following this header. */ - if (header->typeflag == TF_REGULAR || header->typeflag == TF_UNKNOWN) + if (header->typeflag == TF_REGULAR + || header->typeflag == TF_EXTHDR + || header->typeflag == TF_UNKNOWN) header->nrecords = (header->size + RECORDSIZE-1)/RECORDSIZE; else header->nrecords = 0; @@ -181,18 +189,93 @@ parse_header (const void *record, const char *filename, tarinfo_t info) return header; } +/* Parse the extended header. This funcion may modify BUFFER. */ +static gpg_error_t +parse_extended_header (const char *fname, + char *buffer, size_t buflen, strlist_t *r_exthdr) +{ + unsigned int reclen; + unsigned char *p, *record; + strlist_t sl; + + while (buflen) + { + record = buffer; /* Remember begin of record. */ + reclen = 0; + for (p = buffer; buflen && digitp (p); buflen--, p++) + { + reclen *= 10; + reclen += (*p - '0'); + } + if (!buflen || *p != ' ') + { + log_error ("%s: malformed record length in extended header\n", fname); + return gpg_error (GPG_ERR_INV_RECORD); + } + p++; /* Skip space. */ + buflen--; + if (buflen + (p-record) < reclen) + { + log_error ("%s: extended header record larger" + " than total extended header data\n", fname); + return gpg_error (GPG_ERR_INV_RECORD); + } + if (reclen < (p-record)+2 || record[reclen-1] != '\n') + { + log_error ("%s: malformed extended header record\n", fname); + return gpg_error (GPG_ERR_INV_RECORD); + } + record[reclen-1] = 0; /* For convenience change LF to a Nul. */ + reclen -= (p-record); + /* P points to the begin of the keyword and RECLEN is the + * remaining length of the record excluding the LF. */ + if (memchr (p, 0, reclen-1) + && (!strncmp (p, "path=", 5) || !strncmp (p, "linkpath=", 9))) + { + log_error ("%s: extended header record has an embedded nul" + " - ignoring\n", fname); + } + else if (!strncmp (p, "path=", 5)) + { + sl = add_to_strlist_try (r_exthdr, p+5); + if (!sl) + return gpg_error_from_syserror (); + sl->flags = 1; /* Mark as path */ + } + else if (!strncmp (p, "linkpath=", 9)) + { + sl = add_to_strlist_try (r_exthdr, p+9); + if (!sl) + return gpg_error_from_syserror (); + sl->flags = 2; /* Mark as linkpath */ + } + + buffer = p + reclen; + buflen -= reclen; + } + + return 0; +} /* Read the next block, assuming it is a tar header. Returns a header - object on success in R_HEADER, or an error. If the stream is - consumed, R_HEADER is set to NULL. In case of an error an error - message has been printed. */ + * object on success in R_HEADER, or an error. If the stream is + * consumed (i.e. end-of-archive), R_HEADER is set to NULL. In case + * of an error an error message is printed. If the header is an + * extended header, a string list is allocated and stored at + * R_EXTHEADER; the caller should provide a pointer to NULL. Such an + * extended header is fully processed here and the returned R_HEADER + * has then the next regular header. */ static gpg_error_t -read_header (estream_t stream, tarinfo_t info, tar_header_t *r_header) +read_header (estream_t stream, tarinfo_t info, + tar_header_t *r_header, strlist_t *r_extheader) { gpg_error_t err; char record[RECORDSIZE]; int i; + tar_header_t hdr; + char *buffer; + size_t buflen, nrec; err = read_record (stream, record); if (err) @@ -224,7 +307,67 @@ read_header (estream_t stream, tarinfo_t info, tar_header_t *r_header) } *r_header = parse_header (record, es_fname_get (stream), info); - return *r_header ? 0 : gpg_error_from_syserror (); + if (!*r_header) + return gpg_error_from_syserror (); + hdr = *r_header; + + if (hdr->typeflag != TF_EXTHDR || !r_extheader) + return 0; + + /* Read the extended header. */ + if (!hdr->nrecords) + { + /* More than 64k for an extedned header is surely too large. */ + log_info ("%s: warning: empty extended header\n", + es_fname_get (stream)); + return 0; + } + if (hdr->nrecords > 65536 / RECORDSIZE) + { + /* More than 64k for an extedned header is surely too large. */ + log_error ("%s: extended header too large - skipping\n", + es_fname_get (stream)); + return 0; + } + + buffer = xtrymalloc (hdr->nrecords * RECORDSIZE); + if (!buffer) + { + err = gpg_error_from_syserror (); + log_error ("%s: error allocating space for extended header: %s\n", + es_fname_get (stream), gpg_strerror (err)); + return err; + } + buflen = 0; + + for (nrec=0; nrec < hdr->nrecords;) + { + err = read_record (stream, buffer + buflen); + if (err) + { + xfree (buffer); + return err; + } + info->nblocks++; + nrec++; + if (nrec < hdr->nrecords || (hdr->size && !(hdr->size % RECORDSIZE))) + buflen += RECORDSIZE; + else + buflen += (hdr->size % RECORDSIZE); + } + + err = parse_extended_header (es_fname_get (stream), + buffer, buflen, r_extheader); + if (err) + { + free_strlist (*r_extheader); + *r_extheader = NULL; + } + + xfree (buffer); + /* Now tha the extedned header has been read, we read the next + * header without allowing an extended header. */ + return read_header (stream, info, r_header, NULL); } @@ -249,11 +392,13 @@ skip_data (estream_t stream, tarinfo_t info, tar_header_t header) static void -print_header (tar_header_t header, estream_t out) +print_header (tar_header_t header, strlist_t extheader, estream_t out) { unsigned long mask; char modestr[10+1]; int i; + strlist_t sl; + const char *name, *linkname; *modestr = '?'; switch (header->typeflag) @@ -266,6 +411,8 @@ print_header (tar_header_t header, estream_t out) case TF_DIRECTORY:*modestr = 'd'; break; case TF_FIFO: *modestr = 'f'; break; case TF_RESERVED: *modestr = '='; break; + case TF_EXTHDR: break; + case TF_GEXTHDR: break; case TF_UNKNOWN: break; case TF_NOTSUP: break; } @@ -279,9 +426,25 @@ print_header (tar_header_t header, estream_t out) modestr[9] = modestr[9] == 'x'? 't':'T'; modestr[10] = 0; - es_fprintf (out, "%s %lu %lu/%lu %12llu %s %s\n", + /* FIXME: We do not parse the linkname unless its part of an + * extended header. */ + name = header->name; + linkname = header->typeflag == TF_SYMLINK? "?" : NULL; + + for (sl = extheader; sl; sl = sl->next) + { + if (sl->flags == 1) + name = sl->d; + else if (sl->flags == 2) + linkname = sl->d; + } + + es_fprintf (out, "%s %lu %lu/%lu %12llu %s %s%s%s\n", modestr, header->nlink, header->uid, header->gid, header->size, - isotimestamp (header->mtime), header->name); + isotimestamp (header->mtime), + name, + linkname? " -> " : "", + linkname? linkname : ""); } @@ -292,52 +455,43 @@ gpg_error_t gpgtar_list (const char *filename, int decrypt) { gpg_error_t err; - estream_t stream; - estream_t cipher_stream = NULL; + estream_t stream = NULL; tar_header_t header = NULL; + strlist_t extheader = NULL; struct tarinfo_s tarinfo_buffer; tarinfo_t tarinfo = &tarinfo_buffer; + pid_t pid = (pid_t)(-1); memset (&tarinfo_buffer, 0, sizeof tarinfo_buffer); - if (filename) - { - if (!strcmp (filename, "-")) - stream = es_stdin; - else - stream = es_fopen (filename, "rb"); - if (!stream) - { - err = gpg_error_from_syserror (); - log_error ("error opening '%s': %s\n", filename, gpg_strerror (err)); - return err; - } - } - else - stream = es_stdin; - - if (stream == es_stdin) - es_set_binary (es_stdin); - if (decrypt) { strlist_t arg; ccparray_t ccp; const char **argv; - cipher_stream = stream; - stream = es_fopenmem (0, "rwb"); - if (! stream) - { - err = gpg_error_from_syserror (); - goto leave; - } - ccparray_init (&ccp, 0); + if (opt.batch) + ccparray_put (&ccp, "--batch"); + if (opt.require_compliance) + ccparray_put (&ccp, "--require-compliance"); + if (opt.status_fd != -1) + { + static char tmpbuf[40]; + snprintf (tmpbuf, sizeof tmpbuf, "--status-fd=%d", opt.status_fd); + ccparray_put (&ccp, tmpbuf); + } + ccparray_put (&ccp, "--output"); + ccparray_put (&ccp, "-"); ccparray_put (&ccp, "--decrypt"); for (arg = opt.gpg_arguments; arg; arg = arg->next) ccparray_put (&ccp, arg->d); + if (filename) + { + ccparray_put (&ccp, "--"); + ccparray_put (&ccp, filename); + } ccparray_put (&ccp, NULL); argv = ccparray_get (&ccp, NULL); @@ -347,50 +501,90 @@ gpgtar_list (const char *filename, int decrypt) goto leave; } - err = gnupg_exec_tool_stream (opt.gpg_program, argv, - cipher_stream, NULL, stream, NULL, NULL); + err = gnupg_spawn_process (opt.gpg_program, argv, NULL, NULL, + ((filename? 0 : GNUPG_SPAWN_KEEP_STDIN) + | GNUPG_SPAWN_KEEP_STDERR), + NULL, &stream, NULL, &pid); xfree (argv); if (err) goto leave; - - err = es_fseek (stream, 0, SEEK_SET); - if (err) - goto leave; + es_set_binary (stream); + } + else if (filename) /* No decryption requested. */ + { + if (!strcmp (filename, "-")) + stream = es_stdin; + else + stream = es_fopen (filename, "rb,sysopen"); + if (!stream) + { + err = gpg_error_from_syserror (); + log_error ("error opening '%s': %s\n", filename, gpg_strerror (err)); + goto leave; + } + if (stream == es_stdin) + es_set_binary (es_stdin); + } + else + { + stream = es_stdin; + es_set_binary (es_stdin); } for (;;) { - err = read_header (stream, tarinfo, &header); + err = read_header (stream, tarinfo, &header, &extheader); if (err || header == NULL) goto leave; - print_header (header, es_stdout); + print_header (header, extheader, es_stdout); if (skip_data (stream, tarinfo, header)) goto leave; + free_strlist (extheader); + extheader = NULL; xfree (header); header = NULL; } + if (pid != (pid_t)(-1)) + { + int exitcode; + + err = es_fclose (stream); + stream = NULL; + if (err) + log_error ("error closing pipe: %s\n", gpg_strerror (err)); + else + { + err = gnupg_wait_process (opt.gpg_program, pid, 1, &exitcode); + if (err) + log_error ("running %s failed (exitcode=%d): %s", + opt.gpg_program, exitcode, gpg_strerror (err)); + gnupg_release_process (pid); + pid = (pid_t)(-1); + } + } leave: + free_strlist (extheader); xfree (header); if (stream != es_stdin) es_fclose (stream); - if (stream != cipher_stream) - es_fclose (cipher_stream); return err; } + gpg_error_t -gpgtar_read_header (estream_t stream, tarinfo_t info, tar_header_t *r_header) +gpgtar_read_header (estream_t stream, tarinfo_t info, + tar_header_t *r_header, strlist_t *r_extheader) { - return read_header (stream, info, r_header); + return read_header (stream, info, r_header, r_extheader); } void -gpgtar_print_header (tar_header_t header, estream_t out) +gpgtar_print_header (tar_header_t header, strlist_t extheader, estream_t out) { if (header && out) - print_header (header, out); + print_header (header, extheader, out); } diff --git a/tools/gpgtar.c b/tools/gpgtar.c index c2f24ec..e86ed32 100644 --- a/tools/gpgtar.c +++ b/tools/gpgtar.c @@ -34,7 +34,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <assert.h> #define INCLUDED_BY_MAIN_MODULE 1 #include "../common/util.h" @@ -77,12 +76,19 @@ enum cmd_and_opt_values oNull, oUtf8Strings, + oBatch, + oAnswerYes, + oAnswerNo, + oStatusFD, + oRequireCompliance, + oWithLog, + /* Compatibility with gpg-zip. */ oGpgArgs, oTarArgs, /* Debugging. */ - oDryRun, + oDryRun }; @@ -113,6 +119,13 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oOpenPGP, "openpgp", "@"), ARGPARSE_s_n (oCMS, "cms", "@"), + ARGPARSE_s_n (oBatch, "batch", "@"), + ARGPARSE_s_n (oAnswerYes, "yes", "@"), + ARGPARSE_s_n (oAnswerNo, "no", "@"), + ARGPARSE_s_i (oStatusFD, "status-fd", "@"), + ARGPARSE_s_n (oRequireCompliance, "require-compliance", "@"), + ARGPARSE_s_n (oWithLog, "with-log", "@"), + ARGPARSE_group (302, N_("@\nTar options:\n ")), ARGPARSE_s_s (oDirectory, "directory", @@ -259,7 +272,7 @@ shell_parse_stringlist (const char *str, strlist_t *r_list) break; case doublequote: - assert (s > str || !"cannot be quoted at first char"); + log_assert (s > str || !"cannot be quoted at first char"); if (*s == doublequote && *(s - 1) != '\\') quoted = unquoted; else @@ -267,7 +280,7 @@ shell_parse_stringlist (const char *str, strlist_t *r_list) break; default: - assert (! "reached"); + log_assert (! "reached"); } } @@ -373,6 +386,13 @@ parse_arguments (ARGPARSE_ARGS *pargs, ARGPARSE_OPTS *popts) case oOpenPGP: /* Dummy option for now. */ break; case oCMS: /* Dummy option for now. */ break; + case oBatch: opt.batch = 1; break; + case oAnswerYes: opt.answer_yes = 1; break; + case oAnswerNo: opt.answer_no = 1; break; + case oStatusFD: opt.status_fd = pargs->r.ret_int; break; + case oRequireCompliance: opt.require_compliance = 1; break; + case oWithLog: opt.with_log = 1; break; + case oGpgArgs:; { strlist_t list; @@ -438,9 +458,13 @@ main (int argc, char **argv) /* Make sure that our subsystems are ready. */ i18n_init(); init_common_subsystems (&argc, &argv); + gnupg_init_signals (0, NULL); log_assert (sizeof (struct ustar_raw_header) == 512); + /* Set default options */ + opt.status_fd = -1; + /* Parse the command line. */ pargs.argc = &argc; pargs.argv = &argv; @@ -523,7 +547,7 @@ main (int argc, char **argv) /* Read the next record from STREAM. RECORD is a buffer provided by - the caller and must be at leadt of size RECORDSIZE. The function + the caller and must be at least of size RECORDSIZE. The function return 0 on success and error code on failure; a diagnostic printed as well. Note that there is no need for an EOF indicator because a tarball has an explicit EOF record. */ diff --git a/tools/gpgtar.h b/tools/gpgtar.h index 599f17f..9f3c90f 100644 --- a/tools/gpgtar.h +++ b/tools/gpgtar.h @@ -41,6 +41,12 @@ struct int symmetric; const char *filename; const char *directory; + int batch; + int answer_yes; + int answer_no; + int status_fd; + int require_compliance; + int with_log; } opt; @@ -95,6 +101,8 @@ typedef enum TF_DIRECTORY, TF_FIFO, TF_RESERVED, + TF_GEXTHDR, /* Global extended header. */ + TF_EXTHDR, /* Extended header. */ TF_UNKNOWN, /* Needs to be treated as regular file. */ TF_NOTSUP /* Not supported (used with --create). */ } typeflag_t; @@ -140,8 +148,9 @@ gpg_error_t gpgtar_extract (const char *filename, int decrypt); /*-- gpgtar-list.c --*/ gpg_error_t gpgtar_list (const char *filename, int decrypt); gpg_error_t gpgtar_read_header (estream_t stream, tarinfo_t info, - tar_header_t *r_header); -void gpgtar_print_header (tar_header_t header, estream_t out); + tar_header_t *r_header, strlist_t *r_extheader); +void gpgtar_print_header (tar_header_t header, strlist_t extheader, + estream_t out); #endif /*GPGTAR_H*/ diff --git a/tools/wks-util.c b/tools/wks-util.c index 025b1b7..4fa28bb 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -790,6 +790,12 @@ wks_fname_from_userid (const char *userid, int hash_only, domain = strchr (addrspec, '@'); log_assert (domain); domain++; + if (strchr (domain, '/') || strchr (domain, '\\')) + { + log_info ("invalid domain detected ('%s')\n", domain); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto leave; + } /* Hash user ID and create filename. */ s = strchr (addrspec, '@'); @@ -845,6 +851,11 @@ wks_compute_hu_fname (char **r_fname, const char *addrspec) if (!domain || !domain[1] || domain == addrspec) return gpg_error (GPG_ERR_INV_ARG); domain++; + if (strchr (domain, '/') || strchr (domain, '\\')) + { + log_info ("invalid domain detected ('%s')\n", domain); + return gpg_error (GPG_ERR_NOT_FOUND); + } gcry_md_hash_buffer (GCRY_MD_SHA1, sha1buf, addrspec, domain - addrspec - 1); hash = zb32_encode (sha1buf, 8*20); @@ -893,6 +904,11 @@ ensure_policy_file (const char *addrspec) if (!domain || !domain[1] || domain == addrspec) return gpg_error (GPG_ERR_INV_ARG); domain++; + if (strchr (domain, '/') || strchr (domain, '\\')) + { + log_info ("invalid domain detected ('%s')\n", domain); + return gpg_error (GPG_ERR_NOT_FOUND); + } /* Create the filename. */ fname = make_filename_try (opt.directory, domain, "policy", NULL); |