summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--NEWS4
-rw-r--r--common/status.h1
-rw-r--r--doc/DETAILS5
-rw-r--r--doc/gpg.texi32
-rw-r--r--g10/gpg.c24
-rw-r--r--g10/gpgv.c2
-rw-r--r--g10/main.h2
-rw-r--r--g10/mainproc.c5
-rw-r--r--g10/options.h4
-rw-r--r--g10/t-keydb-get-keyblock.c9
-rw-r--r--g10/t-keydb.c10
-rw-r--r--g10/t-stutter.c9
-rw-r--r--g10/verify.c124
13 files changed, 216 insertions, 15 deletions
diff --git a/NEWS b/NEWS
index 7ca5b1335..894743db5 100644
--- a/NEWS
+++ b/NEWS
@@ -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]
diff --git a/common/status.h b/common/status.h
index 0c481d247..e4cf23ee1 100644
--- a/common/status.h
+++ b/common/status.h
@@ -53,6 +53,7 @@ enum
STATUS_NEED_PASSPHRASE,
STATUS_VALIDSIG,
+ STATUS_ASSERT_SIGNER,
STATUS_SIG_ID,
STATUS_ENC_TO,
STATUS_NODATA,
diff --git a/doc/DETAILS b/doc/DETAILS
index eee640a01..fd95e511c 100644
--- a/doc/DETAILS
+++ b/doc/DETAILS
@@ -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
diff --git a/doc/gpg.texi b/doc/gpg.texi
index b526deeca..eb7c35cac 100644
--- a/doc/gpg.texi
+++ b/doc/gpg.texi
@@ -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
diff --git a/g10/gpg.c b/g10/gpg.c
index f52d13a76..b759cc1cf 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -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);
}
diff --git a/g10/gpgv.c b/g10/gpgv.c
index ceded4af9..f2895563e 100644
--- a/g10/gpgv.c
+++ b/g10/gpgv.c
@@ -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*))
diff --git a/g10/main.h b/g10/main.h
index 3d71d0c09..b29e23e51 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -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 );
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 4710386ea..ce0fdaaac 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -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);
}
diff --git a/g10/options.h b/g10/options.h
index 3ecf57ffb..9015e321f 100644
--- a/g10/options.h
+++ b/g10/options.h
@@ -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
diff --git a/g10/t-keydb-get-keyblock.c b/g10/t-keydb-get-keyblock.c
index 90ce6e9a6..e40be9cc1 100644
--- a/g10/t-keydb-get-keyblock.c
+++ b/g10/t-keydb-get-keyblock.c
@@ -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;
+}
diff --git a/g10/t-keydb.c b/g10/t-keydb.c
index 4c78dac48..9055d5b94 100644
--- a/g10/t-keydb.c
+++ b/g10/t-keydb.c
@@ -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;
+}
diff --git a/g10/t-stutter.c b/g10/t-stutter.c
index 503a92004..7b2ea4b37 100644
--- a/g10/t-stutter.c
+++ b/g10/t-stutter.c
@@ -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;
+}
diff --git a/g10/verify.c b/g10/verify.c
index fc18882b0..e9792939d 100644
--- a/g10/verify.c
+++ b/g10/verify.c
@@ -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);
+}