gpg: New option --assert-signer.

* g10/gpg.c (enum cmd_and_opt_values): Add oAssertSigner.
(opts): Add "assert-signer".
(main): Set option.
(assert_signer_true): New var.
(g10_exit): Evaluate new var.
* g10/main.h (assert_signer_true): Declare new var.
* common/status.h (STATUS_ASSERT_SIGNER): New.
* g10/options.h (opt): Add field assert_signer_list.
* g10/verify.c (is_fingerprint): New.
(check_assert_signer_list): New.
* g10/mainproc.c (check_sig_and_print): Call that function.  Clear
assert_signer_true on a warning.

* g10/gpgv.c: Add dummy function and vars.
* g10/t-keydb-get-keyblock.c: Ditto.
* g10/t-keydb.c: Ditto.
* g10/t-stutter.c: Ditto.
--
This commit is contained in:
Werner Koch 2023-04-05 21:32:23 +02:00
parent 42ccbd6c78
commit c9e95b8dee
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
13 changed files with 216 additions and 15 deletions

4
NEWS
View File

@ -1,11 +1,13 @@
Noteworthy changes in version 2.4.1 (unreleased)
------------------------------------------------
* If the ~/.gnupg home directory does not exist, the keyboxd is now
* If the ~/.gnupg directory does not exist, the keyboxd is now
automagically enabled.
* gpg: New option --add-desig-revoker. [rG3d094e2bcf]
* gpg: New option --assert-signer.
* gpg: New list-option "show-unusable-sigs". Also show
"[self-signature]" instead of the user-id in key signature
listings. [rG103acfe9ca]

View File

@ -53,6 +53,7 @@ enum
STATUS_NEED_PASSPHRASE,
STATUS_VALIDSIG,
STATUS_ASSERT_SIGNER,
STATUS_SIG_ID,
STATUS_ENC_TO,
STATUS_NODATA,

View File

@ -522,6 +522,11 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB:
Epoch or an ISO 8601 string which can be detected by the presence
of the letter 'T'.
*** ASSERT_SIGNER <fingerprint>
This is emitted for the matching <fingerprint> when option
--assert-signer is used. The fingerprint is printed with
uppercase hex digits.
*** SIG_ID <radix64_string> <sig_creation_date> <sig-timestamp>
This is emitted only for signatures of class 0 or 1 which have
been verified okay. The string is a signature id and may be used

View File

@ -264,11 +264,11 @@ out the actual signed data, but there are other pitfalls with this
format as well. It is suggested to avoid cleartext signatures in
favor of detached signatures.
Note: Sometimes the use of the @command{gpgv} tool is easier than
using the full-fledged @command{gpg} with this option. @command{gpgv}
is designed to compare signed data against a list of trusted keys and
returns with success only for a good signature. It has its own manual
page.
Note: To check whether a file was signed by a certain key the option
@option{--assert-signer} can be used. As an alternative the
@command{gpgv} tool can be used. @command{gpgv} is designed to
compare signed data against a list of trusted keys and returns with
success only for a good signature. It has its own manual page.
@item --multifile
@ -1889,6 +1889,24 @@ Set what trust model GnuPG should follow. The models are:
must be enabled explicitly.
@end table
@item --always-trust
@opindex always-trust
Identical to @option{--trust-model always}.
@item --assert-signer @var{fpr_or_file}
@opindex assert-signer
This option checks whether at least one valid signature on a file has
been made with the specified key. The key is either specified as a
fingerprint or a file listing fingerprints. The fingerprint must be
given or listed in compact format (no colons or spaces in between).
This option can be given multiple times and each fingerprint is
checked against the signing key as well as the corresponding primary
key. If @var{fpr_or_file} specifies a file, empty lines are ignored
as well as all lines starting with a hash sign. With this option gpg
is guaranteed to return with an exit code of 0 if and only if a
signature has been encountered, is valid, and the key matches one of
the fingerprints given by this option.
@item --auto-key-locate @var{mechanisms}
@itemx --no-auto-key-locate
@ -3856,10 +3874,6 @@ Display the keyring name at the head of key listings to show which
keyring a given key resides on. This option is deprecated: use
@option{--list-options [no-]show-keyring} instead.
@item --always-trust
@opindex always-trust
Identical to @option{--trust-model always}. This option is deprecated.
@item --show-notation
@itemx --no-show-notation
@opindex show-notation

View File

@ -446,6 +446,7 @@ enum cmd_and_opt_values
oRequireCompliance,
oCompatibilityFlags,
oAddDesigRevoker,
oAssertSigner,
oNoop
};
@ -708,7 +709,7 @@ static gpgrt_opt_t opts[] = {
ARGPARSE_s_n (oNoAutoTrustNewKey, "no-auto-trust-new-key", "@"),
#endif
ARGPARSE_s_s (oAddDesigRevoker, "add-desig-revoker", "@"),
ARGPARSE_s_s (oAssertSigner, "assert-signer", "@"),
ARGPARSE_header ("Input", N_("Options controlling the input")),
@ -1032,8 +1033,12 @@ static struct compatibility_flags_s compatibility_flags [] =
/* The list of the default AKL methods. */
#define DEFAULT_AKL_LIST "local,wkd"
/* Can be set to true to force gpg to return with EXIT_FAILURE. */
int g10_errors_seen = 0;
/* If opt.assert_signer_list is used and this variabale is not true
* gpg will be forced to return EXIT_FAILURE. */
int assert_signer_true = 0;
static int utf8_strings =
#ifdef HAVE_W32_SYSTEM
@ -3734,6 +3739,11 @@ main (int argc, char **argv)
append_to_strlist (&opt.desig_revokers, pargs.r.ret_str);
break;
case oAssertSigner:
add_to_strlist (&opt.assert_signer_list, pargs.r.ret_str);
break;
case oNoop: break;
default:
@ -5448,7 +5458,15 @@ g10_exit( int rc )
gnupg_block_all_signals ();
emergency_cleanup ();
rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0;
if (rc)
;
else if (log_get_errorcount(0))
rc = 2;
else if (g10_errors_seen)
rc = 1;
else if (opt.assert_signer_list && !assert_signer_true)
rc = 1;
exit (rc);
}

View File

@ -118,7 +118,7 @@ static struct debug_flags_s debug_flags [] =
int g10_errors_seen = 0;
int assert_signer_true = 0;
static char *
make_libversion (const char *libname, const char *(*getfnc)(const char*))

View File

@ -83,6 +83,7 @@ struct weakhash
/*-- gpg.c --*/
extern int g10_errors_seen;
extern int assert_signer_true;
#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
void g10_exit(int rc) __attribute__ ((__noreturn__));
@ -492,6 +493,7 @@ void print_file_status( int status, const char *name, int what );
int verify_signatures (ctrl_t ctrl, int nfiles, char **files );
int verify_files (ctrl_t ctrl, int nfiles, char **files );
int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp);
void check_assert_signer_list (const char *mainpkhex, const char *pkhex);
/*-- decrypt.c --*/
int decrypt_message (ctrl_t ctrl, const char *filename );

View File

@ -2410,7 +2410,7 @@ check_sig_and_print (CTX c, kbnode_t node)
}
/* For good signatures print the VALIDSIG status line. */
if (!rc && is_status_enabled () && pk)
if (!rc && (is_status_enabled () || opt.assert_signer_list) && pk)
{
char pkhex[MAX_FINGERPRINT_LEN*2+1];
char mainpkhex[MAX_FINGERPRINT_LEN*2+1];
@ -2430,6 +2430,8 @@ check_sig_and_print (CTX c, kbnode_t node)
sig->digest_algo,
sig->sig_class,
mainpkhex);
/* Handle the --assert-signer option. */
check_assert_signer_list (mainpkhex, pkhex);
}
/* Print compliance warning for Good signatures. */
@ -2510,6 +2512,7 @@ check_sig_and_print (CTX c, kbnode_t node)
is not a detached signature. */
log_info (_("WARNING: not a detached signature; "
"file '%s' was NOT verified!\n"), dfile);
assert_signer_true = 0;
}
xfree (dfile);
}

View File

@ -235,6 +235,10 @@ struct
value. */
int limit_card_insert_tries;
/* The list of --assert-signer option values. Note: The values are
* modify to be uppercase if they represent a fingerrint */
strlist_t assert_signer_list;
struct
{
/* If set, require an 0x19 backsig to be present on signatures

View File

@ -67,3 +67,12 @@ do_test (int argc, char *argv[])
release_kbnode (kb1);
xfree (ctrl);
}
int assert_signer_true = 0;
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
(void)mainpkhex;
(void)pkhex;
}

View File

@ -105,3 +105,13 @@ do_test (int argc, char *argv[])
keydb_release (hd2);
xfree (ctrl);
}
int assert_signer_true = 0;
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
(void)mainpkhex;
(void)pkhex;
}

View File

@ -611,3 +611,12 @@ do_test (int argc, char *argv[])
xfree (filename);
}
int assert_signer_true = 0;
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
(void)mainpkhex;
(void)pkhex;
}

View File

@ -1,6 +1,8 @@
/* verify.c - Verify signed data
* Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, 2005, 2006,
* 2007, 2010 Free Software Foundation, Inc.
* Copyright (C) 2003, 2006-2008, 2010-2011, 2015-2017,
* 2020, 2023 g10 Code GmbH
*
* This file is part of GnuPG.
*
@ -16,6 +18,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>
@ -281,3 +284,124 @@ gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp)
release_armor_context (afx);
return rc;
}
static int
is_fingerprint (const char *string)
{
int n;
if (!string || !*string)
return 0;
for (n=0; hexdigitp (string); string++)
n++;
if (!*string && (n == 40 || n == 64))
return 1; /* v4 or v5 fingerprint. */
return 0;
}
/* This function shall be called with the main and subkey fingerprint
* iff a signature is fully valid. If the option --assert-signer is
* active it check whether the signing key matches one of the keys
* given by this option and if so, sets a global flag. */
void
check_assert_signer_list (const char *mainpkhex, const char *pkhex)
{
gpg_error_t err;
strlist_t item;
const char *fname;
estream_t fp = NULL;
int lnr;
int n, c;
char *p, *pend;
char line[256];
if (!opt.assert_signer_list)
return; /* Nothing to do. */
if (assert_signer_true)
return; /* Already one valid signature seen. */
for (item = opt.assert_signer_list; item; item = item->next)
{
if (is_fingerprint (item->d))
{
ascii_strupr (item->d);
if (!strcmp (item->d, mainpkhex) || !strcmp (item->d, pkhex))
{
assert_signer_true = 1;
write_status_text (STATUS_ASSERT_SIGNER, item->d);
if (!opt.quiet)
log_info ("signer '%s' matched\n", item->d);
goto leave;
}
}
else /* Assume this is a file - read and compare. */
{
fname = item->d;
es_fclose (fp);
fp = es_fopen (fname, "r");
if (!fp)
{
err = gpg_error_from_syserror ();
log_error (_("error opening '%s': %s\n"),
fname, gpg_strerror (err));
continue;
}
lnr = 0;
err = 0;
while (es_fgets (line, DIM(line)-1, fp))
{
lnr++;
n = strlen (line);
if (!n || line[n-1] != '\n')
{
/* Eat until end of line. */
while ( (c=es_getc (fp)) != EOF && c != '\n')
;
err = gpg_error (GPG_ERR_INCOMPLETE_LINE);
log_error (_("file '%s', line %d: %s\n"),
fname, lnr, gpg_strerror (err));
continue;
}
line[--n] = 0; /* Chop the LF. */
if (n && line[n-1] == '\r')
line[--n] = 0; /* Chop an optional CR. */
/* Allow for empty lines and spaces */
for (p=line; spacep (p); p++)
;
if (!*p || *p == '#')
continue;
/* Get the first token and ignore trailing stuff. */
for (pend = p; *pend && !spacep (pend); pend++)
;
*pend = 0;
ascii_strupr (p);
if (!strcmp (p, mainpkhex) || !strcmp (p, pkhex))
{
assert_signer_true = 1;
write_status_text (STATUS_ASSERT_SIGNER, p);
if (!opt.quiet)
log_info ("signer '%s' matched '%s', line %d\n",
p, fname, lnr);
goto leave;
}
}
if (!err && !es_feof (fp))
{
err = gpg_error_from_syserror ();
log_error (_("error reading '%s', line %d: %s\n"),
fname, lnr, gpg_strerror (err));
}
}
}
leave:
es_fclose (fp);
}