[svn] GnuPG - r4446 - in trunk: . agent doc doc/examples tools
svn author wk
cvs at cvs.gnupg.org
Tue Mar 6 21:44:43 CET 2007
Author: wk
Date: 2007-03-06 21:44:41 +0100 (Tue, 06 Mar 2007)
New Revision: 4446
Added:
trunk/doc/examples/gpgconf.conf
trunk/doc/vuln-announce-2007-multiple-message.txt
Modified:
trunk/NEWS
trunk/agent/ChangeLog
trunk/agent/gpg-agent.c
trunk/doc/ChangeLog
trunk/doc/Makefile.am
trunk/doc/gpg-agent.texi
trunk/doc/tools.texi
trunk/tools/ChangeLog
trunk/tools/gpgconf-comp.c
trunk/tools/gpgconf.c
trunk/tools/gpgconf.h
Log:
Support for a global gpgconf configuration file.
Modified: trunk/NEWS
===================================================================
--- trunk/NEWS 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/NEWS 2007-03-06 20:44:41 UTC (rev 4446)
@@ -10,7 +10,10 @@
* New --verify-option show-primary-uid-only.
+ * gpgconf may now read a global configuration file to select which
+ options are changeable by a frontend.
+
Noteworthy changes in version 2.0.2 (2007-01-31)
------------------------------------------------
Modified: trunk/agent/ChangeLog
===================================================================
--- trunk/agent/ChangeLog 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/agent/ChangeLog 2007-03-06 20:44:41 UTC (rev 4446)
@@ -1,3 +1,7 @@
+2007-03-06 Werner Koch <wk at g10code.com>
+
+ * gpg-agent.c (main) <gpgconf>: Add entries for all ttl options.
+
2007-02-20 Werner Koch <wk at g10code.com>
* call-pinentry.c (start_pinentry): Fix for OS X to allow loading
Modified: trunk/agent/gpg-agent.c
===================================================================
--- trunk/agent/gpg-agent.c 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/agent/gpg-agent.c 2007-03-06 20:44:41 UTC (rev 4446)
@@ -168,6 +168,7 @@
#define DEFAULT_CACHE_TTL (10*60) /* 10 minutes */
#define DEFAULT_CACHE_TTL_SSH (30*60) /* 30 minutes */
#define MAX_CACHE_TTL (120*60) /* 2 hours */
+#define MAX_CACHE_TTL_SSH (120*60) /* 2 hours */
#define MIN_PASSPHRASE_LEN (8)
@@ -408,7 +409,7 @@
opt.def_cache_ttl = DEFAULT_CACHE_TTL;
opt.def_cache_ttl_ssh = DEFAULT_CACHE_TTL_SSH;
opt.max_cache_ttl = MAX_CACHE_TTL;
- opt.max_cache_ttl_ssh = MAX_CACHE_TTL;
+ opt.max_cache_ttl_ssh = MAX_CACHE_TTL_SSH;
opt.min_passphrase_len = MIN_PASSPHRASE_LEN;
opt.ignore_cache_for_signing = 0;
opt.allow_mark_trusted = 0;
@@ -775,6 +776,14 @@
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME );
printf ("default-cache-ttl:%lu:%d:\n",
GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL );
+ printf ("default-cache-ttl-ssh:%lu:%d:\n",
+ GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, DEFAULT_CACHE_TTL_SSH );
+ printf ("max-cache-ttl:%lu:%d:\n",
+ GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL );
+ printf ("max-cache-ttl-ssh:%lu:%d:\n",
+ GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MAX_CACHE_TTL_SSH );
+ printf ("min-passphrase-len:%lu:%d:\n",
+ GC_OPT_FLAG_DEFAULT|GC_OPT_FLAG_RUNTIME, MIN_PASSPHRASE_LEN );
printf ("no-grab:%lu:\n",
GC_OPT_FLAG_NONE|GC_OPT_FLAG_RUNTIME);
printf ("ignore-cache-for-signing:%lu:\n",
Modified: trunk/doc/ChangeLog
===================================================================
--- trunk/doc/ChangeLog 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/doc/ChangeLog 2007-03-06 20:44:41 UTC (rev 4446)
@@ -1,3 +1,7 @@
+2007-03-06 Werner Koch <wk at g10code.com>
+
+ * examples/gpgconf.conf: New.
+
2007-03-04 David Shaw <dshaw at jabberwocky.com>
* gpg.texi (GPG Esoteric Options): Document
Modified: trunk/doc/Makefile.am
===================================================================
--- trunk/doc/Makefile.am 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/doc/Makefile.am 2007-03-06 20:44:41 UTC (rev 4446)
@@ -19,7 +19,8 @@
## Process this file with automake to produce Makefile.in
-examples = examples/README examples/scd-event examples/trustlist.txt
+examples = examples/README examples/scd-event examples/trustlist.txt \
+ examples/gpgconf.conf
EXTRA_DIST = DETAILS HACKING TRANSLATE OpenPGP KEYSERVER samplekeys.asc \
gnupg-badge-openpgp.eps gnupg-badge-openpgp.jpg \
Added: trunk/doc/examples/gpgconf.conf
===================================================================
--- trunk/doc/examples/gpgconf.conf 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/doc/examples/gpgconf.conf 2007-03-06 20:44:41 UTC (rev 4446)
@@ -0,0 +1,59 @@
+# gpgconf.conf - configuration for gpgconf
+#----------------------------------------------------------------------
+# This file is read by gpgconf(1) to setup defaults for all or
+# specified users and groups. It may be used to change the hardwired
+# defaults in gpgconf and to enforce certain values for the various
+# GnuPG related configuration files.
+#
+# Empty lines and comment lines, indicated by a hash mark as first non
+# white space character, are ignored. The line is separated by white
+# space into fields. The first field is used to match the user or
+# group and must start at the first column, the file is processes
+# sequential until a matching rle is found. A rule may contain
+# several lines, continuation lines are indicated by a indenting them.
+#
+# Syntax of a line:
+# <key>|WS <component> <option> ["["<flag>"]"] [<value>]
+#
+# Examples for the <key> field:
+# foo - Matches the user "foo".
+# foo: - Matches the user "foo".
+# foo:staff - Matches the user "foo" or the group "staff".
+# :staff - Matches the group "staff".
+# * - Matches any user.
+# All other variants are not defined and reserved for future use.
+#
+# <component> and <option> are as specified by gpgconf.
+# <flag> may be one of:
+# default - Delete the option so that the default is used.
+# no-change - Mark the field as non changeable by gpgconf.
+# change - Mark the field as changeable by gpgconf.
+#
+# Example file:
+#==========
+# :staff gpg-agent allow-mark-trusted [change]
+# gpg-agent min-passphrase-len 6
+#
+# * gpg-agent min-passphrase-len [no-change] 12
+# gpg-agent allow-mark-trusted [default]
+# gpg-agent allow-mark-trusted [no-change]
+# gpgsm enable-ocsp
+#===========
+# All users in the group "staff" are allowed to change the value for
+# --allow-mark-trusted; gpgconf's default is not to allow a change
+# through its interface. When "gpgconf --apply-defaults" is used,
+# "allow-mark-trusted" will get enabled and "min-passphrase-len" set
+# to 6. All other users are not allowed to change
+# "min-passphrase-len" and "allow-mark-trusted". When "gpgconf
+# --apply-defaults" is used for them, "min-passphrase-len" is set to
+# 12, "allow-mark-trusted" deleted from the config file and
+# "enable-ocsp" is put into the config file of gpgsm. The latter may
+# be changed by any user.
+#-------------------------------------------------------------------
+
+
+# Allow all users to change the allow-mark-trusted option.
+# (This was the default prior to gnupg 2.0.3)
+* gpg-agent allow-mark-trusted [change]
+
+
Modified: trunk/doc/gpg-agent.texi
===================================================================
--- trunk/doc/gpg-agent.texi 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/doc/gpg-agent.texi 2007-03-06 20:44:41 UTC (rev 4446)
@@ -336,7 +336,7 @@
@item --min-passphrase-len @var{n}
@opindex min-passphrase-len
-Set the minimal length of a passphrase. When entereing a new passphrase
+Set the minimal length of a passphrase. When entering a new passphrase
shorter than this value a warning will be displayed. Defaults to 8.
@item --pinentry-program @var{filename}
Modified: trunk/doc/tools.texi
===================================================================
--- trunk/doc/tools.texi 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/doc/tools.texi 2007-03-06 20:44:41 UTC (rev 4446)
@@ -199,6 +199,7 @@
* Listing components:: List all gpgconf components.
* Listing options:: List all options of a component.
* Changing options:: Changing options of a component.
+* Files used by gpgconf:: What files are used by gpgconf.
@end menu
@manpause
@@ -219,8 +220,18 @@
@item --change-options @var{component}
Change the options of the component @var{component}.
+
+ at item --apply-defaults
+Update all configuration files with values taken from the global
+configuration file (usually @file{/etc/gnupg/gpgconf.conf}).
+
+ at item --check-config [@var{filename}]
+Run a syntax check ion the global configuration file. If @var{filename}
+is given, check that file instead.
+
@end table
+
@mansect options
The following options may be used:
@@ -486,6 +497,11 @@
@item no arg desc (64)
If this flag is set, and the @code{optional arg} flag is set, then the
option has a special meaning if no argument is given.
+
+ at item no change (128)
+If this flag is set, gpgconf ignores requests to change the value. GUI
+frontends should grey out this option. Note, that manual changes of the
+configuration files are still possible.
@end table
@item level
@@ -658,6 +674,20 @@
The @code{--runtime} option can influence when the changes take
effect.
+ at mansect files
+ at node Files used by gpgconf
+ at subsection Files used by gpgconf
+
+ at table @file
+
+ at item /etc/gnupg/gpg-agent.conf
+ at cindex gpgconf.conf
+ If this file exists, it is processed as a global configuration file.
+ A commented example can be found in the @file{examples} directory of
+ the distribution.
+ at end table
+
+
@mansect see also
@command{gpg}(1),
@command{gpgsm}(1),
Added: trunk/doc/vuln-announce-2007-multiple-message.txt
===================================================================
--- trunk/doc/vuln-announce-2007-multiple-message.txt 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/doc/vuln-announce-2007-multiple-message.txt 2007-03-06 20:44:41 UTC (rev 4446)
@@ -0,0 +1,145 @@
+ Multiple Messages Problem in GnuPG and GPGME
+ ==============================================
+ 2007-03-05
+
+
+Summary
+=======
+
+Gerardo Richarte from Core Security Technologies identified a problem
+when using GnuPG in streaming mode.
+
+The problem is actually a variant of a well known problem in the way
+signed material is presented in a MUA. It is possible to insert
+additional text before or after a signed (or signed and encrypted)
+OpenPGP message and make the user believe that this additional text is
+also covered by the signature. The Core Security advisory describes
+several variants of the attack; they all boil down to the fact that it
+might not be possible to identify which part of a message is actually
+signed if gpg is not used correctly.
+
+[ Please do not send private mail in response to this message. The
+ mailing list gnupg-devel is the best place to discuss this problem
+ (please subscribe first so you don't need moderator approval [1]). ]
+
+
+Impact
+======
+
+All applications using GnuPG without properly using the status
+interface to verify signed or signed and encrypted messages.
+
+All GPGME versions up to and including 1.1.3.
+
+Starting with version 1.4.7 and 2.0.3, GnuPG implements an additional
+and sufficient protection against this common usage problem.
+
+Detached signatures are in no way affected by this problem.
+
+
+Description
+===========
+
+When using gpg (or gpg2) in a pipeline or with redirected input and
+output additional data may be inserted into a message. This allows to
+forge a signed message by prefixing it with arbitrary material. A way
+to create such a message is:
+
+ echo "This is my sneaky plaintext message" > foobar.txt
+ gpg -z0 --output prefix.gpg --store foobar.txt
+ cat prefix.gpg original-signed-message.gpg > forged.gpg
+
+Using gpg naively this results in:
+
+ $ gpg <forged.gpg
+ This is my sneaky plaintext message
+ Either I'm dead or my watch has stopped.
+ -- Groucho Marx's last words
+ gpg: Signature made Mon Feb 26 09:57:04 2007 CET using DSA key ID 68697734
+ gpg: Good signature from "Alfa Test (demo key) <alfa at example.net>"
+ [...]
+
+and thus gives the impression that the sneaky message is part of the
+signed Groucho quote. The correct way to use gpg with redirection is
+by taking care of the status interface:
+
+ $ gpg --status-fd 1 <forged.gpg
+ [GNUPG:] PLAINTEXT 62 1172479053 foobar.txt
+ [GNUPG:] PLAINTEXT_LENGTH 36
+ This is my sneaky plaintext message
+ [GNUPG:] PLAINTEXT 62 1172480224 original-signed-message
+ [GNUPG:] PLAINTEXT_LENGTH 86
+ Either I'm dead or my watch has stopped.
+ -- Groucho Marx's last words
+ gpg: Signature made Mon Feb 26 09:57:04 2007 CET using DSA key ID 68697734
+ [GNUPG:] SIG_ID UncMPBJYgbG/uszJVNKoCAz+hvY 2007-02-26 1172480224
+ [GNUPG:] GOODSIG 2D727CC768697734 Alfa Test (demo key) <alfa at example.net>
+ gpg: Good signature from "Alfa Test (demo key) <alfa at example.net>"
+ [...]
+
+Here the PLAINTEXT status lines clearly identify the start of a new
+message.
+
+Note, that using gpg on the command line is in almost all cases not
+done with redirection but by letting gpg save the the signed message.
+In this case gpg will save the message to different files or in case
+the file names are identical, prompt the over to overwrite the first
+one again.
+
+Because the problem of identifying the actual signed content when
+mixing the signed data and the signature is very common, the long
+standing suggestion for all digital signatures is to use a detached
+signature. A detached signature allows to clearly identify what is
+signed and what is the signature. This is also the reason why
+PGP/MIME signed messages are in general to be preferred over the old
+style clear signed messages.
+
+
+Solution
+========
+
+Given that there are many applications in use which are subject to the
+described problem, we have decided to change GnuPG so that such forged
+OpenPGP messages are detected and the signature verification will
+fail. GnuPG 1.4.7 has been released today and is available from the
+usual places [2]. If you don't want to update, a minimal patch
+against GnuPG 1.4.6 is available at
+
+ ftp://ftp.gnupg.org/gcrypt/gnupg/patches/gnupg-1.4.6-multiple-message.patch
+
+Many applications are using the library GPGME which implements an easy
+way to process OpenPGP messages using gpg. We have updated GPGME to
+make it immune against this problem even if an old version of gpg is
+being used. GPGME 1.1.4 is available from the usual places [2]. A
+patch (against version 1.1.3 or 1.1.2) is available at
+
+ ftp://ftp.gnupg.org/gcrypt/gpgme/patches/gpgme-1.1.3-multiple-message.patch
+
+Please note that - after applying one of these patches - some
+vulnerable applications (mainly MUAs) may fail to handle certain
+messages which are composed of several OpenPGP messages. To continue
+the support of such messages fixing the application is required as
+there is no way for GnuPG to do it.
+
+
+Support
+=======
+
+g10 Code GmbH [3], a Duesseldorf based company owned and headed by
+GnuPG's principal author, is currently funding GnuPG development.
+Support contracts or other financial backing will greatly help us to
+improve the quality of GnuPG.
+
+
+Thanks
+======
+
+Gerardo Richarte found this problem. David Shaw greatly helped to
+analyse and describe the core of the problem.
+
+
+
+
+[1] See http://lists.gnupg.org/mailman/listinfo/gnupg-devel
+[2] See http://www.gnupg.org/download/
+[3] See http://www.gnupg.org/service.html
Modified: trunk/tools/ChangeLog
===================================================================
--- trunk/tools/ChangeLog 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/tools/ChangeLog 2007-03-06 20:44:41 UTC (rev 4446)
@@ -1,3 +1,18 @@
+2007-03-06 Werner Koch <wk at g10code.com>
+
+ * gpgconf-comp.c: Include pwd.h and grp.h.
+ (GC_OPT_FLAG_NO_CHANGE): New.
+ (gc_component_change_options): Implement it.
+ (gc_options_gpg_agent): Add options for all ttl values and
+ min-passphrase-length. Apply new flag to some of them.
+ (gc_process_gpgconf_conf, key_matches_user_or_group): New.
+ (gc_component_change_options): Factor some code out to ..
+ (change_one_value): .. new.
+ (gc_component_retrieve_options): Allow -1 for COMPONENT to iterate
+ over al components.
+ * gpgconf.c (main): New commands --check-config and
+ --apply-defaults. Call gc_process_gpgconf_conf.
+
2007-01-31 Werner Koch <wk at g10code.com>
* Makefile.am (symcryptrun_LDADD): Add LIBICONV.
Modified: trunk/tools/gpgconf-comp.c
===================================================================
--- trunk/tools/gpgconf-comp.c 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/tools/gpgconf-comp.c 2007-03-06 20:44:41 UTC (rev 4446)
@@ -33,6 +33,8 @@
#include <time.h>
#include <stdarg.h>
#include <signal.h>
+#include <pwd.h>
+#include <grp.h>
/* For log_logv(), asctimestamp(), gnupg_get_time (). */
#define JNLIB_NEED_LOG_LOGV
@@ -321,6 +323,11 @@
/* The NO_ARG_DESC flag for an option indicates that the argument has
a default, which is described by the value of the ARGDEF field. */
#define GC_OPT_FLAG_NO_ARG_DESC (1UL << 6)
+/* The NO_CHANGE flag for an option indicates that the user should not
+ be allowed to chnage this option using the standard gpgconf method.
+ Frontends using gpgconf should grey out such otions, so that only
+ the current value is displayed. */
+#define GC_OPT_FLAG_NO_CHANGE (1UL <<7)
/* A human-readable description for each flag. */
static struct
@@ -334,7 +341,8 @@
{ "runtime" },
{ "default" },
{ "default desc" },
- { "no arg desc" }
+ { "no arg desc" },
+ { "no change" }
};
@@ -471,20 +479,36 @@
{ "Security",
GC_OPT_FLAG_GROUP, GC_LEVEL_BASIC,
"gnupg", N_("Options controlling the security") },
- { "default-cache-ttl", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC,
- "gnupg", "|N|expire cached PINs after N seconds",
+ { "default-cache-ttl", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_BASIC, "gnupg",
+ "|N|expire cached PINs after N seconds",
GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
- { "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME, GC_LEVEL_BASIC,
- "gnupg", "do not use the PIN cache when signing",
+ { "default-cache-ttl-ssh", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_ADVANCED, "gnupg",
+ N_("|N|expire SSH keys after N seconds"),
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
+ { "max-cache-ttl", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_EXPERT, "gnupg",
+ N_("|N|set maximum PIN cache lifetime to N seconds"),
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
+ { "max-cache-ttl-ssh", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_EXPERT, "gnupg",
+ N_("|N|set maximum SSH key lifetime to N seconds"),
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
+ { "ignore-cache-for-signing", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_BASIC, "gnupg", "do not use the PIN cache when signing",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
- { "allow-mark-trusted", GC_OPT_FLAG_RUNTIME, GC_LEVEL_ADVANCED,
- "gnupg", "allow clients to mark keys as \"trusted\"",
+ { "allow-mark-trusted", GC_OPT_FLAG_RUNTIME | GC_OPT_FLAG_NO_CHANGE,
+ GC_LEVEL_ADVANCED, "gnupg", "allow clients to mark keys as \"trusted\"",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
+ { "min-passphrase-len", GC_OPT_FLAG_RUNTIME,
+ GC_LEVEL_EXPERT, "gnupg",
+ N_("|N|set minimal required length for new passphrases to N"),
+ GC_ARG_TYPE_UINT32, GC_BACKEND_GPG_AGENT },
{ "no-grab", GC_OPT_FLAG_RUNTIME, GC_LEVEL_EXPERT,
"gnupg", "do not grab keyboard and mouse",
GC_ARG_TYPE_NONE, GC_BACKEND_GPG_AGENT },
-
GC_OPTION_NULL
};
@@ -1539,41 +1563,56 @@
/* Retrieve the currently active options and their defaults from all
- involved backends for this component. */
+ involved backends for this component. Using -1 for component will
+ retrieve all options from all components. */
void
gc_component_retrieve_options (int component)
{
+ int process_all = 0;
int backend_seen[GC_BACKEND_NR];
gc_backend_t backend;
- gc_option_t *option = gc_component[component].options;
+ gc_option_t *option;
for (backend = 0; backend < GC_BACKEND_NR; backend++)
backend_seen[backend] = 0;
- while (option->name)
+ if (component == -1)
{
- if (!(option->flags & GC_OPT_FLAG_GROUP))
- {
- backend = option->backend;
+ process_all = 1;
+ component = 0;
+ assert (component < GC_COMPONENT_NR);
+ }
+
+ do
+ {
+ option = gc_component[component].options;
- if (backend_seen[backend])
- {
- option++;
- continue;
- }
- backend_seen[backend] = 1;
+ while (option->name)
+ {
+ if (!(option->flags & GC_OPT_FLAG_GROUP))
+ {
+ backend = option->backend;
+
+ if (backend_seen[backend])
+ {
+ option++;
+ continue;
+ }
+ backend_seen[backend] = 1;
+
+ assert (backend != GC_BACKEND_ANY);
+
+ if (gc_backend[backend].program)
+ retrieve_options_from_program (component, backend);
+ else
+ retrieve_options_from_file (component, backend);
+ }
+ option++;
+ }
+ }
+ while (process_all && ++component < GC_COMPONENT_NR);
- assert (backend != GC_BACKEND_ANY);
-
- if (gc_backend[backend].program)
- retrieve_options_from_program (component, backend);
- else
- retrieve_options_from_file (component, backend);
- }
- option++;
- }
}
-
/* Perform a simple validity check based on the type. Return in
NEW_VALUE_NR the value of the number in NEW_VALUE if OPTION is of
@@ -1585,7 +1624,8 @@
char *arg;
if (!option->active)
- gc_error (1, 0, "option %s not supported by backend", option->name);
+ gc_error (1, 0, "option %s not supported by backend %s",
+ option->name, gc_backend[option->backend].name);
if (option->new_flags || option->new_value)
gc_error (1, 0, "option %s already changed", option->name);
@@ -2270,7 +2310,49 @@
}
-/* Read the modifications from IN and apply them. */
+/* Common code for gc_component_change_options and
+ gc_process_gpgconf_conf. */
+static void
+change_one_value (gc_option_t *option, int *runtime,
+ unsigned long flags, char *new_value)
+{
+ unsigned long new_value_nr = 0;
+
+ option_check_validity (option, flags, new_value, &new_value_nr);
+
+ if (option->flags & GC_OPT_FLAG_RUNTIME)
+ runtime[option->backend] = 1;
+
+ option->new_flags = flags;
+ if (!(flags & GC_OPT_FLAG_DEFAULT))
+ {
+ if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE
+ && (option->flags & GC_OPT_FLAG_LIST))
+ {
+ char *str;
+
+ /* We convert the number to a list of 1's for convenient
+ list handling. */
+ assert (new_value_nr > 0);
+ option->new_value = xmalloc ((2 * (new_value_nr - 1) + 1) + 1);
+ str = option->new_value;
+ *(str++) = '1';
+ while (--new_value_nr > 0)
+ {
+ *(str++) = ',';
+ *(str++) = '1';
+ }
+ *(str++) = '\0';
+ }
+ else
+ option->new_value = xstrdup (new_value);
+ }
+}
+
+
+/* Read the modifications from IN and apply them. If IN is NULL the
+ modifications are expected to already have been set to the global
+ table. */
void
gc_component_change_options (int component, FILE *in)
{
@@ -2293,90 +2375,71 @@
orig_pathname[backend] = NULL;
}
- while ((length = read_line (in, &line, &line_len, NULL)) > 0)
+ if (in)
{
- char *linep;
- unsigned long flags = 0;
- char *new_value = "";
- unsigned long new_value_nr = 0;
+ /* Read options from the file IN. */
+ while ((length = read_line (in, &line, &line_len, NULL)) > 0)
+ {
+ char *linep;
+ unsigned long flags = 0;
+ char *new_value = "";
+
+ /* Strip newline and carriage return, if present. */
+ while (length > 0
+ && (line[length - 1] == '\n' || line[length - 1] == '\r'))
+ line[--length] = '\0';
+
+ linep = strchr (line, ':');
+ if (linep)
+ *(linep++) = '\0';
+
+ /* Extract additional flags. Default to none. */
+ if (linep)
+ {
+ char *end;
+ char *tail;
- /* Strip newline and carriage return, if present. */
- while (length > 0
- && (line[length - 1] == '\n' || line[length - 1] == '\r'))
- line[--length] = '\0';
+ end = strchr (linep, ':');
+ if (end)
+ *(end++) = '\0';
+
+ errno = 0;
+ flags = strtoul (linep, &tail, 0);
+ if (errno)
+ gc_error (1, errno, "malformed flags in option %s", line);
+ if (!(*tail == '\0' || *tail == ':' || *tail == ' '))
+ gc_error (1, 0, "garbage after flags in option %s", line);
+
+ linep = end;
+ }
- linep = strchr (line, ':');
- if (linep)
- *(linep++) = '\0';
-
- /* Extract additional flags. Default to none. */
- if (linep)
- {
- char *end;
- char *tail;
-
- end = strchr (linep, ':');
- if (end)
- *(end++) = '\0';
-
- errno = 0;
- flags = strtoul (linep, &tail, 0);
- if (errno)
- gc_error (1, errno, "malformed flags in option %s", line);
- if (!(*tail == '\0' || *tail == ':' || *tail == ' '))
- gc_error (1, 0, "garbage after flags in option %s", line);
-
- linep = end;
- }
-
- /* Extract default value, if present. Default to empty if
- not. */
- if (linep)
- {
- char *end;
-
- end = strchr (linep, ':');
- if (end)
- *(end++) = '\0';
-
- new_value = linep;
-
- linep = end;
- }
-
- option = find_option (component, line, GC_BACKEND_ANY);
- if (!option)
- gc_error (1, 0, "unknown option %s", line);
-
- option_check_validity (option, flags, new_value, &new_value_nr);
-
- if (option->flags & GC_OPT_FLAG_RUNTIME)
- runtime[option->backend] = 1;
-
- option->new_flags = flags;
- if (!(flags & GC_OPT_FLAG_DEFAULT))
- {
- if (gc_arg_type[option->arg_type].fallback == GC_ARG_TYPE_NONE
- && (option->flags & GC_OPT_FLAG_LIST))
- {
- char *str;
-
- /* We convert the number to a list of 1's for
- convenient list handling. */
- assert (new_value_nr > 0);
- option->new_value = xmalloc ((2 * (new_value_nr - 1) + 1) + 1);
- str = option->new_value;
- *(str++) = '1';
- while (--new_value_nr > 0)
- {
- *(str++) = ',';
- *(str++) = '1';
- }
- *(str++) = '\0';
- }
- else
- option->new_value = xstrdup (new_value);
- }
+ /* Don't allow setting of the no change flag. */
+ flags &= ~GC_OPT_FLAG_NO_CHANGE;
+
+ /* Extract default value, if present. Default to empty if not. */
+ if (linep)
+ {
+ char *end;
+ end = strchr (linep, ':');
+ if (end)
+ *(end++) = '\0';
+ new_value = linep;
+ linep = end;
+ }
+
+ option = find_option (component, line, GC_BACKEND_ANY);
+ if (!option)
+ gc_error (1, 0, "unknown option %s", line);
+
+ if ((option->flags & GC_OPT_FLAG_NO_CHANGE))
+ {
+ gc_error (0, 0, "ignoring new value for option %s",
+ option->name);
+ continue;
+ }
+
+ change_one_value (option, runtime, flags, new_value);
+ }
}
/* Now that we have collected and locally verified the changes,
@@ -2500,3 +2563,365 @@
xfree (line);
}
+
+
+/* Check whether USER matches the current user of one of its group.
+ This function may change USER. Returns true is there is a
+ match. */
+static int
+key_matches_user_or_group (char *user)
+{
+ char *group;
+ int n;
+
+ if (*user == '*' && user[1] == 0)
+ return 1; /* A single asterisk matches all users. */
+
+ group = strchr (user, ':');
+ if (group)
+ *group++ = 0;
+
+ /* First check whether the user matches. */
+ if (*user)
+ {
+ static char *my_name;
+
+ if (!my_name)
+ {
+ struct passwd *pw = getpwuid ( getuid () );
+ if (!pw)
+ gc_error (1, errno, "getpwuid failed for current user");
+ my_name = xstrdup (pw->pw_name);
+ }
+ if (!strcmp (user, my_name))
+ return 1; /* Found. */
+ }
+
+ /* If that failed, check whether a group matches. */
+ if (group && *group)
+ {
+ static char *my_group;
+ static char **my_supgroups;
+
+ if (!my_group)
+ {
+ struct group *gr = getgrgid ( getgid () );
+ if (!gr)
+ gc_error (1, errno, "getgrgid failed for current user");
+ my_group = xstrdup (gr->gr_name);
+ }
+ if (!strcmp (group, my_group))
+ return 1; /* Found. */
+
+ if (!my_supgroups)
+ {
+ int ngids;
+ gid_t *gids;
+
+ ngids = getgroups (0, NULL);
+ gids = xcalloc (ngids+1, sizeof *gids);
+ ngids = getgroups (ngids, gids);
+ if (ngids < 0)
+ gc_error (1, errno, "getgroups failed for current user");
+ my_supgroups = xcalloc (ngids+1, sizeof *my_supgroups);
+ for (n=0; n < ngids; n++)
+ {
+ struct group *gr = getgrgid ( gids[n] );
+ if (!gr)
+ gc_error (1, errno, "getgrgid failed for supplementary group");
+ my_supgroups[n] = xstrdup (gr->gr_name);
+ }
+ xfree (gids);
+ }
+
+ for (n=0; my_supgroups[n]; n++)
+ if (!strcmp (group, my_supgroups[n]))
+ return 1; /* Found. */
+ }
+
+ return 0; /* No match. */
+}
+
+
+
+/* Read and process the global configuration file for gpgconf. This
+ optional file is used to update our internal tables at runtime and
+ may also be used to set new default values. If FNAME is NULL the
+ default name will be used. With UPDATE set to true the internal
+ tables are actually updated; if not set, only a syntax check is
+ done. If DEFAULTS is true the global options are written to the
+ configuration files.
+
+ Returns 0 on success or if the config file is not present; -1 is
+ returned on error. */
+int
+gc_process_gpgconf_conf (const char *fname, int update, int defaults)
+{
+ int result = 0;
+ char *line = NULL;
+ size_t line_len = 0;
+ ssize_t length;
+ FILE *config;
+ int lineno = 0;
+ int in_rule = 0;
+ int got_match = 0;
+ int runtime[GC_BACKEND_NR];
+ int used_components[GC_COMPONENT_NR];
+ int backend_id, component_id;
+
+ if (!fname)
+ fname = GNUPG_SYSCONFDIR "/gpgconf.conf";
+
+ for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+ runtime[backend_id] = 0;
+ for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
+ used_components[component_id] = 0;
+
+ config = fopen (fname, "r");
+ if (!config)
+ {
+ /* Do not print an error if the file is not available, except
+ when runnign in syntax check mode. */
+ if (errno != ENOENT || !update)
+ {
+ gc_error (0, errno, "can not open global config file `%s'", fname);
+ result = -1;
+ }
+ return result;
+ }
+
+ while ((length = read_line (config, &line, &line_len, NULL)) > 0)
+ {
+ char *key, *component, *option, *flags, *value;
+ char *empty;
+ gc_option_t *option_info = NULL;
+ char *p;
+ int is_continuation;
+
+ lineno++;
+ key = line;
+ while (*key == ' ' || *key == '\t')
+ key++;
+ if (!*key || *key == '#' || *key == '\r' || *key == '\n')
+ continue;
+
+ is_continuation = (key != line);
+
+ /* Parse the key field. */
+ if (!is_continuation && got_match)
+ break; /* Finish after the first match. */
+ else if (!is_continuation)
+ {
+ in_rule = 0;
+ for (p=key+1; *p && !strchr (" \t\r\n", *p); p++)
+ ;
+ if (!*p)
+ {
+ gc_error (0, 0, "missing rule at `%s', line %d", fname, lineno);
+ result = -1;
+ continue;
+ }
+ *p++ = 0;
+ component = p;
+ }
+ else if (!in_rule)
+ {
+ gc_error (0, 0, "continuation but no rule at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ continue;
+ }
+ else
+ {
+ component = key;
+ key = NULL;
+ }
+
+ in_rule = 1;
+
+ /* Parse the component. */
+ while (*component == ' ' || *component == '\t')
+ component++;
+ for (p=component; *p && !strchr (" \t\r\n", *p); p++)
+ ;
+ if (p == component)
+ {
+ gc_error (0, 0, "missing component at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ continue;
+ }
+ empty = p;
+ *p++ = 0;
+ option = p;
+ component_id = gc_component_find (component);
+ if (component_id < 0)
+ {
+ gc_error (0, 0, "unknown component at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ }
+
+ /* Parse the option name. */
+ while (*option == ' ' || *option == '\t')
+ option++;
+ for (p=option; *p && !strchr (" \t\r\n", *p); p++)
+ ;
+ if (p == option)
+ {
+ gc_error (0, 0, "missing option at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ continue;
+ }
+ *p++ = 0;
+ flags = p;
+ if ( component_id != -1)
+ {
+ option_info = find_option (component_id, option, GC_BACKEND_ANY);
+ if (!option_info)
+ {
+ gc_error (0, 0, "unknown option at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ }
+ }
+
+
+ /* Parse the optional flags. */
+ while (*flags == ' ' || *flags == '\t')
+ flags++;
+ if (*flags == '[')
+ {
+ flags++;
+ p = strchr (flags, ']');
+ if (!p)
+ {
+ gc_error (0, 0, "syntax error in rule at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ continue;
+ }
+ *p++ = 0;
+ value = p;
+ }
+ else /* No flags given. */
+ {
+ value = flags;
+ flags = NULL;
+ }
+
+ /* Parse the optional value. */
+ while (*value == ' ' || *value == '\t')
+ value++;
+ for (p=value; *p && !strchr ("\r\n", *p); p++)
+ ;
+ if (p == value)
+ value = empty; /* No value given; let it point to an empty string. */
+ else
+ {
+ /* Strip trailing white space. */
+ *p = 0;
+ for (p--; p > value && (*p == ' ' || *p == '\t'); p--)
+ *p = 0;
+ }
+
+ /* Check flag combinations. */
+ if (!flags)
+ ;
+ else if (!strcmp (flags, "default"))
+ {
+ if (*value)
+ {
+ gc_error (0, 0, "flag \"default\" may not be combined "
+ "with a value at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ }
+ }
+ else if (!strcmp (flags, "change"))
+ ;
+ else if (!strcmp (flags, "no-change"))
+ ;
+ else
+ {
+ gc_error (0, 0, "unknown flag at `%s', line %d",
+ fname, lineno);
+ result = -1;
+ }
+
+
+ /* Check whether the key matches but do this only if we are not
+ running in syntax check mode. */
+ if ( update
+ && !result
+ && (got_match || (key && key_matches_user_or_group (key))) )
+ {
+ int newflags = 0;
+
+ got_match = 1;
+
+ /* Apply the flags from gpgconf.conf. */
+ if (!flags)
+ ;
+ else if (!strcmp (flags, "default"))
+ newflags |= GC_OPT_FLAG_DEFAULT;
+ else if (!strcmp (flags, "no-change"))
+ option_info->flags |= GC_OPT_FLAG_NO_CHANGE;
+ else if (!strcmp (flags, "change"))
+ option_info->flags &= ~GC_OPT_FLAG_NO_CHANGE;
+
+ if (defaults)
+ {
+ assert (component_id >= 0 && component_id < GC_COMPONENT_NR);
+ used_components[component_id] = 1;
+
+ /* Here we explicitly allow to update the value again. */
+ if (newflags)
+ {
+ option_info->new_flags = 0;
+ }
+ if (*value)
+ {
+ xfree (option_info->new_value);
+ option_info->new_value = NULL;
+ }
+ change_one_value (option_info, runtime, newflags, value);
+ }
+ }
+ }
+
+ if (length < 0 || ferror (config))
+ {
+ gc_error (0, errno, "error reading from `%s'", fname);
+ result = -1;
+ }
+ if (fclose (config) && ferror (config))
+ gc_error (0, errno, "error closing `%s'", fname);
+
+ xfree (line);
+
+ /* If it all worked, process the options. */
+ if (!result && update && defaults)
+ {
+ /* We need to switch off the runtime update, so that we can do
+ it later all at once. */
+ int save_opt_runtime = opt.runtime;
+ opt.runtime = 0;
+
+ for (component_id = 0; component_id < GC_COMPONENT_NR; component_id++)
+ {
+ gc_component_change_options (component_id, NULL);
+ }
+ opt.runtime = save_opt_runtime;
+
+ if (opt.runtime)
+ {
+ for (backend_id = 0; backend_id < GC_BACKEND_NR; backend_id++)
+ if (runtime[backend_id] && gc_backend[backend_id].runtime_change)
+ (*gc_backend[backend_id].runtime_change) ();
+ }
+ }
+
+ return result;
+}
Modified: trunk/tools/gpgconf.c
===================================================================
--- trunk/tools/gpgconf.c 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/tools/gpgconf.c 2007-03-06 20:44:41 UTC (rev 4446)
@@ -44,6 +44,8 @@
aListComponents,
aListOptions,
aChangeOptions,
+ aApplyDefaults,
+ aCheckConfig
};
@@ -56,6 +58,10 @@
{ aListComponents, "list-components", 256, N_("list all components") },
{ aListOptions, "list-options", 256, N_("|COMPONENT|list options") },
{ aChangeOptions, "change-options", 256, N_("|COMPONENT|change options") },
+ { aApplyDefaults, "apply-defaults", 256,
+ N_("apply global default values") },
+ { aCheckConfig, "check-config", 256,
+ N_("check global configuration file") },
{ 301, NULL, 0, N_("@\nOptions:\n ") },
@@ -64,7 +70,6 @@
{ oQuiet, "quiet", 0, N_("quiet") },
{ oDryRun, "dry-run", 0, N_("do not make any changes") },
{ oRuntime, "runtime", 0, N_("activate changes at runtime, if possible") },
-
/* hidden options */
{ oNoVerbose, "no-verbose", 0, "@"},
{0}
@@ -149,6 +154,8 @@
case aListComponents:
case aListOptions:
case aChangeOptions:
+ case aApplyDefaults:
+ case aCheckConfig:
cmd = pargs.r_opt;
break;
@@ -189,11 +196,34 @@
exit (1);
}
gc_component_retrieve_options (idx);
+ if (gc_process_gpgconf_conf (NULL, 1, 0))
+ exit (1);
if (cmd == aListOptions)
gc_component_list_options (idx, stdout);
else
- gc_component_change_options (idx, stdin);
+ gc_component_change_options (idx, stdin);
}
+ break;
+
+ case aCheckConfig:
+ if (gc_process_gpgconf_conf (fname, 0, 0))
+ exit (1);
+ break;
+
+ case aApplyDefaults:
+ if (fname)
+ {
+ fputs (_("usage: gpgconf [options] "), stderr);
+ putc ('\n',stderr);
+ fputs (_("No argument allowed"), stderr);
+ putc ('\n',stderr);
+ exit (2);
+ }
+ gc_component_retrieve_options (-1);
+ if (gc_process_gpgconf_conf (NULL, 1, 1))
+ exit (1);
+ break;
+
}
return 0;
Modified: trunk/tools/gpgconf.h
===================================================================
--- trunk/tools/gpgconf.h 2007-03-06 06:39:38 UTC (rev 4445)
+++ trunk/tools/gpgconf.h 2007-03-06 20:44:41 UTC (rev 4446)
@@ -56,4 +56,8 @@
/* Read the modifications from IN and apply them. */
void gc_component_change_options (int component, FILE *in);
+/* Process global configuration file. */
+int gc_process_gpgconf_conf (const char *fname, int update, int defaults);
+
+
#endif /*GPGCONF_H*/
More information about the Gnupg-commits
mailing list