summaryrefslogtreecommitdiff
path: root/tools
diff options
context:
space:
mode:
authorDongHun Kwak <dh0128.kwak@samsung.com>2022-09-16 07:45:09 +0900
committerDongHun Kwak <dh0128.kwak@samsung.com>2022-09-16 07:45:09 +0900
commit1e860c1f8b2b0b48617f2ca664aa9acf2fafc8d3 (patch)
treea945f12e0ecb03e7c1db7db427f469b46f5c71f9 /tools
parent0fca6c1d0d6088ff4558a6ffc3bc220998699392 (diff)
downloadgpg2-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.in1
-rw-r--r--tools/gpg-connect-agent.c2
-rw-r--r--tools/gpg-wks-server.c82
-rw-r--r--tools/gpgconf-comp.c2645
-rw-r--r--tools/gpgconf.c163
-rw-r--r--tools/gpgconf.h13
-rw-r--r--tools/gpgtar-create.c390
-rw-r--r--tools/gpgtar-extract.c288
-rw-r--r--tools/gpgtar-list.c302
-rw-r--r--tools/gpgtar.c34
-rw-r--r--tools/gpgtar.h13
-rw-r--r--tools/wks-util.c16
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);