summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2024-09-11 14:24:58 +0200
committerWerner Koch <wk@gnupg.org>2024-09-11 14:30:40 +0200
commit33e571a74a7d6153ba65aeecc72539a10f1f0ae4 (patch)
treeb3ddc5b1932d90e29017a05fbb9318664b8a0eec
parentbuild: Remove configure option --enable-gpg-is-gpg2 (diff)
downloadgnupg2-33e571a74a7d6153ba65aeecc72539a10f1f0ae4.tar.xz
gnupg2-33e571a74a7d6153ba65aeecc72539a10f1f0ae4.zip
gpgsm: New option --assert-signer
* sm/gpgsm.c (oAssertSigner, oNoop): New. (opts): Add option --assert-signer. (assert_signer_true): New var. (main): Set new option. (gpgsm_exit): Handle assert_signer_true. * sm/gpgsm.h (opt): Add field assert_signer_list. * sm/verify.c (is_x509_fingerprint): New. (check_assert_signer_list): New. (gpgsm_verify): Handle option. -- GnuPG-bug-id: 7286
-rw-r--r--NEWS8
-rw-r--r--doc/gpgsm.texi15
-rw-r--r--sm/gpgsm.c25
-rw-r--r--sm/gpgsm.h5
-rw-r--r--sm/verify.c134
-rw-r--r--tools/rfc822parse.c1
6 files changed, 184 insertions, 4 deletions
diff --git a/NEWS b/NEWS
index 5180f2ba6..1f2f98cf5 100644
--- a/NEWS
+++ b/NEWS
@@ -1,7 +1,13 @@
Noteworthy changes in version 2.5.1 (unreleased)
------------------------------------------------
- * gpg: New option --proc-all-sigs. [T7261]
+ * gpg: The support for composite Kyber+ECC public key algorithms
+ does now use the final FIPS-203 and LibrePGP specifications. The
+ experimental keys from 2.5.0 are no longer supported. [T6815]
+
+ * gpg: New option --proc-all-sigs. [T7261]
+
+ * gpgsm: New option --assert-signer. [T7286]
Release-info: https://dev.gnupg.org/T7191
diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi
index 1316318a6..2cb50539a 100644
--- a/doc/gpgsm.texi
+++ b/doc/gpgsm.texi
@@ -732,6 +732,21 @@ instead to make sure that the gpgsm process exits with a failure if
the compliance rules are not fulfilled. Note that this option has
currently an effect only in "de-vs" mode.
+@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).
+As of now only SHA-1 fingerprints are allowed. 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 gpgsm 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 --always-trust
@opindex always-trust
Force encryption to the specified certificates without any validation
diff --git a/sm/gpgsm.c b/sm/gpgsm.c
index 70463e734..400479b1b 100644
--- a/sm/gpgsm.c
+++ b/sm/gpgsm.c
@@ -217,7 +217,10 @@ enum cmd_and_opt_values {
oCompatibilityFlags,
oKbxBufferSize,
oAlwaysTrust,
- oNoAutostart
+ oNoAutostart,
+ oAssertSigner,
+
+ oNoop
};
@@ -311,6 +314,7 @@ static gpgrt_opt_t opts[] = {
N_("|FILE|take policy information from FILE")),
ARGPARSE_s_s (oCompliance, "compliance", "@"),
ARGPARSE_p_u (oMinRSALength, "min-rsa-length", "@"),
+ ARGPARSE_s_s (oAssertSigner, "assert-signer", "@"),
ARGPARSE_s_n (oNoCommonCertsImport, "no-common-certs-import", "@"),
ARGPARSE_s_s (oIgnoreCertExtension, "ignore-cert-extension", "@"),
ARGPARSE_s_s (oIgnoreCertWithOID, "ignore-cert-with-oid", "@"),
@@ -502,6 +506,9 @@ static struct compatibility_flags_s compatibility_flags [] =
/* Global variable to keep an error count. */
int gpgsm_errors_seen = 0;
+/* If opt.assert_signer_list is used and this variable is not true
+ * gpg will be forced to return EXIT_FAILURE. */
+int assert_signer_true = 0;
/* It is possible that we are currentlu running under setuid permissions */
static int maybe_setuid = 1;
@@ -1518,6 +1525,12 @@ main ( int argc, char **argv)
keybox_set_buffersize (pargs.r.ret_ulong, 0);
break;
+ case oAssertSigner:
+ add_to_strlist (&opt.assert_signer_list, pargs.r.ret_str);
+ break;
+
+ case oNoop: break;
+
default:
if (configname)
pargs.err = ARGPARSE_PRINT_WARNING;
@@ -2329,6 +2342,15 @@ emergency_cleanup (void)
void
gpgsm_exit (int rc)
{
+ if (rc)
+ ;
+ else if (log_get_errorcount(0))
+ rc = 2;
+ else if (gpgsm_errors_seen)
+ rc = 1;
+ else if (opt.assert_signer_list && !assert_signer_true)
+ rc = 1;
+
gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE);
if (opt.debug & DBG_MEMSTAT_VALUE)
{
@@ -2338,7 +2360,6 @@ gpgsm_exit (int rc)
if (opt.debug)
gcry_control (GCRYCTL_DUMP_SECMEM_STATS );
emergency_cleanup ();
- rc = rc? rc : log_get_errorcount(0)? 2 : gpgsm_errors_seen? 1 : 0;
exit (rc);
}
diff --git a/sm/gpgsm.h b/sm/gpgsm.h
index e9f74be8c..5f69db0e3 100644
--- a/sm/gpgsm.h
+++ b/sm/gpgsm.h
@@ -181,6 +181,10 @@ struct
* attribute values. */
strlist_t attributes;
+ /* The list of --assert-signer option values. Note: The values are
+ * modified to uppercase if they represent a fingerrint */
+ strlist_t assert_signer_list;
+
/* Compatibility flags (COMPAT_FLAG_xxxx). */
unsigned int compat_flags;
} opt;
@@ -312,6 +316,7 @@ struct rootca_flags_s
/*-- gpgsm.c --*/
extern int gpgsm_errors_seen;
+extern int assert_signer_true;
void gpgsm_exit (int rc);
void gpgsm_init_default_ctrl (struct server_control_s *ctrl);
diff --git a/sm/verify.c b/sm/verify.c
index 39226ed28..cd9659313 100644
--- a/sm/verify.c
+++ b/sm/verify.c
@@ -37,6 +37,11 @@
#include "../common/i18n.h"
#include "../common/compliance.h"
+
+static void check_assert_signer_list (ctrl_t ctrl, const char *pkhex);
+
+
+
static char *
strtimestamp_r (ksba_isotime_t atime)
{
@@ -647,6 +652,8 @@ gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp,
*keyexptime? keyexptime : "0",
info_pkalgo, algo);
xfree (tstr);
+ /* Handle the --assert-signer option. */
+ check_assert_signer_list (ctrl, fpr);
xfree (fpr);
gpgsm_status (ctrl, STATUS_VALIDSIG, buf);
xfree (buf);
@@ -747,3 +754,130 @@ gpgsm_verify (ctrl_t ctrl, estream_t in_fp, estream_t data_fp,
return rc;
}
+
+
+
+static int
+is_x509_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; /* SHA1 or SHA256 fingerprint. */
+
+ return 0;
+}
+
+
+/* This function shall be called with the X.509 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.
+ *
+ * Note: This function is mainly a copy of the fucntion from gpg. The
+ * status emit function and the single X.509 fingerprint makes the
+ * differences.
+ */
+static void
+check_assert_signer_list (ctrl_t ctrl, 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_x509_fingerprint (item->d))
+ {
+ ascii_strupr (item->d);
+ if (!strcmp (item->d, pkhex))
+ {
+ assert_signer_true = 1;
+ gpgsm_status (ctrl, STATUS_ASSERT_SIGNER, item->d);
+ if (!opt.quiet)
+ log_info ("asserted signer '%s'\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, pkhex))
+ {
+ assert_signer_true = 1;
+ gpgsm_status (ctrl, STATUS_ASSERT_SIGNER, p);
+ if (!opt.quiet)
+ log_info ("asserted signer '%s' (%s:%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);
+}
diff --git a/tools/rfc822parse.c b/tools/rfc822parse.c
index 5aa233b12..fa1cf2a3e 100644
--- a/tools/rfc822parse.c
+++ b/tools/rfc822parse.c
@@ -725,7 +725,6 @@ const char *
rfc822parse_enum_header_lines (rfc822parse_t msg, void **context)
{
HDR_LINE l;
- part_t part;
if (!msg) /* Close. */
return NULL;