mirror of
git://git.gnupg.org/gnupg.git
synced 2024-12-22 10:19:57 +01:00
Support for a global gpgconf configuration file.
This commit is contained in:
parent
9491ab44c5
commit
ed84b0f787
3
NEWS
3
NEWS
@ -10,6 +10,9 @@ Noteworthy changes in version 2.0.3
|
||||
|
||||
* 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)
|
||||
------------------------------------------------
|
||||
|
@ -1,3 +1,7 @@
|
||||
2007-03-06 Werner Koch <wk@g10code.com>
|
||||
|
||||
* gpg-agent.c (main) <gpgconf>: Add entries for all ttl options.
|
||||
|
||||
2007-02-20 Werner Koch <wk@g10code.com>
|
||||
|
||||
* call-pinentry.c (start_pinentry): Fix for OS X to allow loading
|
||||
|
@ -168,6 +168,7 @@ static ARGPARSE_OPTS opts[] = {
|
||||
#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 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread)
|
||||
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 @@ main (int argc, char **argv )
|
||||
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",
|
||||
|
@ -1,3 +1,7 @@
|
||||
2007-03-06 Werner Koch <wk@g10code.com>
|
||||
|
||||
* examples/gpgconf.conf: New.
|
||||
|
||||
2007-03-04 David Shaw <dshaw@jabberwocky.com>
|
||||
|
||||
* gpg.texi (GPG Esoteric Options): Document
|
||||
|
@ -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 \
|
||||
|
59
doc/examples/gpgconf.conf
Normal file
59
doc/examples/gpgconf.conf
Normal file
@ -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]
|
||||
|
||||
|
@ -336,7 +336,7 @@ been accessed recently. The default are 2 hours (7200 seconds).
|
||||
|
||||
@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}
|
||||
|
@ -199,6 +199,7 @@ throughout this section.
|
||||
* 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 @@ List all options of the component @var{component}.
|
||||
|
||||
@item --change-options @var{component}
|
||||
Change the options of the component @var{component}.
|
||||
|
||||
@item --apply-defaults
|
||||
Update all configuration files with values taken from the global
|
||||
configuration file (usually @file{/etc/gnupg/gpgconf.conf}).
|
||||
|
||||
@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 @@ If this flag is set, a (runtime) default is available. This and the
|
||||
@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.
|
||||
|
||||
@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 @@ $ echo 'force:16:' | gpgconf --change-options dirmngr
|
||||
The @code{--runtime} option can influence when the changes take
|
||||
effect.
|
||||
|
||||
@mansect files
|
||||
@node Files used by gpgconf
|
||||
@subsection Files used by gpgconf
|
||||
|
||||
@table @file
|
||||
|
||||
@item /etc/gnupg/gpg-agent.conf
|
||||
@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.
|
||||
@end table
|
||||
|
||||
|
||||
@mansect see also
|
||||
@command{gpg}(1),
|
||||
@command{gpgsm}(1),
|
||||
|
145
doc/vuln-announce-2007-multiple-message.txt
Normal file
145
doc/vuln-announce-2007-multiple-message.txt
Normal file
@ -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@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@example.net>
|
||||
gpg: Good signature from "Alfa Test (demo key) <alfa@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
|
@ -1,3 +1,18 @@
|
||||
2007-03-06 Werner Koch <wk@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@g10code.com>
|
||||
|
||||
* Makefile.am (symcryptrun_LDADD): Add LIBICONV.
|
||||
|
@ -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 @@ static struct
|
||||
/* 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 @@ static struct
|
||||
{ "runtime" },
|
||||
{ "default" },
|
||||
{ "default desc" },
|
||||
{ "no arg desc" }
|
||||
{ "no arg desc" },
|
||||
{ "no change" }
|
||||
};
|
||||
|
||||
|
||||
@ -471,20 +479,36 @@ static gc_option_t gc_options_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",
|
||||
{ "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,17 +1563,30 @@ retrieve_options_from_file (gc_component_t component, gc_backend_t backend)
|
||||
|
||||
|
||||
/* 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;
|
||||
|
||||
if (component == -1)
|
||||
{
|
||||
process_all = 1;
|
||||
component = 0;
|
||||
assert (component < GC_COMPONENT_NR);
|
||||
}
|
||||
|
||||
do
|
||||
{
|
||||
option = gc_component[component].options;
|
||||
|
||||
while (option->name)
|
||||
{
|
||||
if (!(option->flags & GC_OPT_FLAG_GROUP))
|
||||
@ -1573,7 +1610,9 @@ gc_component_retrieve_options (int component)
|
||||
option++;
|
||||
}
|
||||
}
|
||||
while (process_all && ++component < GC_COMPONENT_NR);
|
||||
|
||||
}
|
||||
|
||||
/* 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 @@ option_check_validity (gc_option_t *option, unsigned long flags,
|
||||
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 @@ change_options_program (gc_component_t component, gc_backend_t backend,
|
||||
}
|
||||
|
||||
|
||||
/* 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,12 +2375,14 @@ gc_component_change_options (int component, FILE *in)
|
||||
orig_pathname[backend] = NULL;
|
||||
}
|
||||
|
||||
if (in)
|
||||
{
|
||||
/* 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 = "";
|
||||
unsigned long new_value_nr = 0;
|
||||
|
||||
/* Strip newline and carriage return, if present. */
|
||||
while (length > 0
|
||||
@ -2329,18 +2413,17 @@ gc_component_change_options (int component, FILE *in)
|
||||
linep = end;
|
||||
}
|
||||
|
||||
/* Extract default value, if present. Default to empty if
|
||||
not. */
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@ -2348,34 +2431,14 @@ gc_component_change_options (int component, FILE *in)
|
||||
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 ((option->flags & GC_OPT_FLAG_NO_CHANGE))
|
||||
{
|
||||
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';
|
||||
gc_error (0, 0, "ignoring new value for option %s",
|
||||
option->name);
|
||||
continue;
|
||||
}
|
||||
*(str++) = '\0';
|
||||
}
|
||||
else
|
||||
option->new_value = xstrdup (new_value);
|
||||
|
||||
change_one_value (option, runtime, flags, new_value);
|
||||
}
|
||||
}
|
||||
|
||||
@ -2500,3 +2563,365 @@ gc_component_change_options (int component, FILE *in)
|
||||
|
||||
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;
|
||||
}
|
||||
|
@ -44,6 +44,8 @@ enum cmd_and_opt_values
|
||||
aListComponents,
|
||||
aListOptions,
|
||||
aChangeOptions,
|
||||
aApplyDefaults,
|
||||
aCheckConfig
|
||||
|
||||
};
|
||||
|
||||
@ -56,6 +58,10 @@ static ARGPARSE_OPTS opts[] =
|
||||
{ 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 @@ static ARGPARSE_OPTS opts[] =
|
||||
{ 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 @@ main (int argc, char **argv)
|
||||
case aListComponents:
|
||||
case aListOptions:
|
||||
case aChangeOptions:
|
||||
case aApplyDefaults:
|
||||
case aCheckConfig:
|
||||
cmd = pargs.r_opt;
|
||||
break;
|
||||
|
||||
@ -189,11 +196,34 @@ main (int argc, char **argv)
|
||||
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);
|
||||
}
|
||||
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;
|
||||
|
@ -56,4 +56,8 @@ void gc_component_list_options (int component, FILE *out);
|
||||
/* 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*/
|
||||
|
Loading…
x
Reference in New Issue
Block a user