diff options
Diffstat (limited to 'g10')
-rw-r--r-- | g10/Makefile.am | 6 | ||||
-rw-r--r-- | g10/gpg.c | 39 | ||||
-rw-r--r-- | g10/gpgv.c | 32 | ||||
-rw-r--r-- | g10/keydb.h | 1 | ||||
-rw-r--r-- | g10/keyid.c | 124 | ||||
-rw-r--r-- | g10/main.h | 2 | ||||
-rw-r--r-- | g10/mainproc.c | 19 | ||||
-rw-r--r-- | g10/options.h | 4 | ||||
-rw-r--r-- | g10/t-keydb-get-keyblock.c | 9 | ||||
-rw-r--r-- | g10/t-keydb.c | 10 | ||||
-rw-r--r-- | g10/t-keyid.c | 129 | ||||
-rw-r--r-- | g10/t-stutter.c | 9 | ||||
-rw-r--r-- | g10/test-stubs.c | 18 | ||||
-rw-r--r-- | g10/test.c | 1 | ||||
-rw-r--r-- | g10/verify.c | 33 |
15 files changed, 383 insertions, 53 deletions
diff --git a/g10/Makefile.am b/g10/Makefile.am index c5691f551..e8d8e9017 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -183,7 +183,7 @@ gpgv_LDFLAGS = t_common_ldadd = -module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter +module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter t-keyid t_rmd160_SOURCES = t-rmd160.c rmd160.c t_rmd160_LDADD = $(t_common_ldadd) t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source) @@ -200,6 +200,10 @@ t_stutter_SOURCES = t-stutter.c test-stubs.c \ t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ $(LIBICONV) $(t_common_ldadd) +t_keyid_SOURCES = t-keyid.c test-stubs.c $(common_source) +t_keyid_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ + $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) $(NETLIBS) \ + $(LIBICONV) $(t_common_ldadd) $(PROGRAMS): $(needed_libs) ../common/libgpgrl.a @@ -451,6 +451,7 @@ enum cmd_and_opt_values oCompatibilityFlags, oAddDesigRevoker, oAssertSigner, + oAssertPubkeyAlgo, oKbxBufferSize, oNoop @@ -715,6 +716,7 @@ static gpgrt_opt_t opts[] = { #endif ARGPARSE_s_s (oAddDesigRevoker, "add-desig-revoker", "@"), ARGPARSE_s_s (oAssertSigner, "assert-signer", "@"), + ARGPARSE_s_s (oAssertPubkeyAlgo,"assert-pubkey-algo", "@"), ARGPARSE_header ("Input", N_("Options controlling the input")), @@ -1044,9 +1046,12 @@ static struct compatibility_flags_s compatibility_flags [] = /* 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 +/* 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; +/* If opt.assert_pubkey_algo is used and this variable is not true + * gpg will be forced to return EXIT_FAILURE. */ +int assert_pubkey_algo_false = 0; static int utf8_strings = @@ -3770,6 +3775,18 @@ main (int argc, char **argv) add_to_strlist (&opt.assert_signer_list, pargs.r.ret_str); break; + case oAssertPubkeyAlgo: + if (!opt.assert_pubkey_algos) + opt.assert_pubkey_algos = xstrdup (pargs.r.ret_str); + else + { + char *tmp = opt.assert_pubkey_algos; + opt.assert_pubkey_algos = xstrconcat (tmp, ",", + pargs.r.ret_str, NULL); + xfree (tmp); + } + break; + case oKbxBufferSize: keybox_set_buffersize (pargs.r.ret_ulong, 0); break; @@ -5472,6 +5489,17 @@ emergency_cleanup (void) void g10_exit( int rc ) { + 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; + else if (opt.assert_pubkey_algos && assert_pubkey_algo_false) + rc = 1; + /* If we had an error but not printed an error message, do it now. * Note that write_status_failure will never print a second failure * status line. */ @@ -5496,15 +5524,6 @@ g10_exit( int rc ) gnupg_block_all_signals (); emergency_cleanup (); - 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 f2895563e..c3b09f752 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -68,6 +68,7 @@ enum cmd_and_opt_values { oWeakDigest, oEnableSpecialFilenames, oDebug, + oAssertPubkeyAlgo, aTest }; @@ -91,6 +92,7 @@ static gpgrt_opt_t opts[] = { N_("|ALGO|reject signatures made with ALGO")), ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), ARGPARSE_s_s (oDebug, "debug", "@"), + ARGPARSE_s_s (oAssertPubkeyAlgo,"assert-pubkey-algo", "@"), ARGPARSE_end () }; @@ -119,6 +121,7 @@ static struct debug_flags_s debug_flags [] = int g10_errors_seen = 0; int assert_signer_true = 0; +int assert_pubkey_algo_false = 0; static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) @@ -251,6 +254,19 @@ main( int argc, char **argv ) case oEnableSpecialFilenames: enable_special_filenames (); break; + + case oAssertPubkeyAlgo: + if (!opt.assert_pubkey_algos) + opt.assert_pubkey_algos = xstrdup (pargs.r.ret_str); + else + { + char *tmp = opt.assert_pubkey_algos; + opt.assert_pubkey_algos = xstrconcat (tmp, ",", + pargs.r.ret_str, NULL); + xfree (tmp); + } + break; + default : pargs.err = ARGPARSE_PRINT_ERROR; break; } } @@ -288,10 +304,18 @@ main( int argc, char **argv ) void -g10_exit( int rc ) -{ - rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; - exit(rc ); +g10_exit (int rc) +{ + if (rc) + ; + else if (log_get_errorcount(0)) + rc = 2; + else if (g10_errors_seen) + rc = 1; + else if (opt.assert_pubkey_algos && assert_pubkey_algo_false) + rc = 1; + + exit (rc); } diff --git a/g10/keydb.h b/g10/keydb.h index b18f6e93a..798c24da3 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -487,6 +487,7 @@ const char *key_origin_string (int origin); /*-- keyid.c --*/ int pubkey_letter( int algo ); char *pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize); +int compare_pubkey_string (const char *astr, const char *bstr); #define PUBKEY_STRING_SIZE 32 u32 v3_keyid (gcry_mpi_t a, u32 *ki); void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk ); diff --git a/g10/keyid.c b/g10/keyid.c index 89bba0d7a..ce977de0b 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -140,6 +140,130 @@ pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) } +/* Helper for compare_pubkey_string. This skips leading spaces, + * commas and optional condition operators and returns a pointer to + * the first non-space character or NULL in case of an error. The + * length of a prefix consisting of letters is then returned ar PFXLEN + * and the value of the number (e.g. 384 for "brainpoolP384r1") at + * NUMBER. R_LENGTH receives the entire length of the algorithm name + * which is terminated by a space, nul, or a comma. If R_CONDITION is + * not NULL, 0 is stored for a leading "=", 1 for a ">", 2 for a ">=", + * -1 for a "<", and -2 for a "<=". If R_CONDITION is NULL no + * condition prefix is allowed. */ +static const char * +parse_one_algo_string (const char *str, size_t *pfxlen, unsigned int *number, + size_t *r_length, int *r_condition) +{ + int condition = 0; + const char *result; + + while (spacep (str) || *str ==',') + str++; + if (!r_condition) + ; + else if (*str == '>' && str[1] == '=') + condition = 2, str += 2; + else if (*str == '>' ) + condition = 1, str += 1; + else if (*str == '<' && str[1] == '=') + condition = -2, str += 2; + else if (*str == '<') + condition = -1, str += 1; + else if (*str == '=') /* Default. */ + str += 1; + + if (!alphap (str)) + return NULL; /* Error. */ + + *pfxlen = 1; + for (result = str++; alphap (str); str++) + ++*pfxlen; + while (*str == '-' || *str == '+') + str++; + *number = atoi (str); + while (*str && !spacep (str) && *str != ',') + str++; + + *r_length = str - result; + if (r_condition) + *r_condition = condition; + return result; +} + +/* Helper for compare_pubkey_string. If BPARSED is set to 0 on + * return, an error in ASTR or BSTR was found and further checks are + * not possible. */ +static int +compare_pubkey_string_part (const char *astr, const char *bstr_arg, + size_t *bparsed) +{ + const char *bstr = bstr_arg; + size_t alen, apfxlen, blen, bpfxlen; + unsigned int anumber, bnumber; + int condition; + + *bparsed = 0; + astr = parse_one_algo_string (astr, &apfxlen, &anumber, &alen, &condition); + if (!astr) + return 0; /* Invalid algorithm name. */ + bstr = parse_one_algo_string (bstr, &bpfxlen, &bnumber, &blen, &condition); + if (!bstr) + return 0; /* Invalid algorithm name. */ + *bparsed = blen + (bstr - bstr_arg); + if (apfxlen != bpfxlen || ascii_strncasecmp (astr, bstr, apfxlen)) + return 0; /* false. */ + switch (condition) + { + case 2: return anumber >= bnumber; + case 1: return anumber > bnumber; + case -1: return anumber < bnumber; + case -2: return anumber <= bnumber; + } + + return alen == blen && !ascii_strncasecmp (astr, bstr, alen); +} + + +/* Check whether ASTR matches the constraints given by BSTR. ASTR may + * be any algo string like "rsa2048", "ed25519" and BSTR may be a + * constraint which is in the simplest case just another algo string. + * BSTR may have more that one string in which case they are comma + * separated and any match will return true. It is possible to prefix + * BSTR with ">", ">=", "<=", or "<". That prefix operator is applied + * to the number part of the algorithm, i.e. the first sequence of + * digits found before end-of-string or a comma. Examples: + * + * | ASTR | BSTR | result | + * |----------+----------------------+--------| + * | rsa2048 | rsa2048 | true | + * | rsa2048 | >=rsa2048 | true | + * | rsa2048 | >rsa2048 | false | + * | ed25519 | >rsa1024 | false | + * | ed25519 | ed25519 | true | + * | nistp384 | >nistp256 | true | + * | nistp521 | >=rsa3072, >nistp384 | true | + */ +int +compare_pubkey_string (const char *astr, const char *bstr) +{ + size_t bparsed; + int result; + + while (*bstr) + { + result = compare_pubkey_string_part (astr, bstr, &bparsed); + if (result) + return 1; + if (!bparsed) + return 0; /* Syntax error in ASTR or BSTR. */ + bstr += bparsed; + } + + return 0; +} + + + /* Hash a public key and allow to specify the to be used format. * Note that if the v5 format is requested for a v4 key, a 0x04 as * version is hashed instead of the 0x05. */ diff --git a/g10/main.h b/g10/main.h index b29e23e51..2482fbde2 100644 --- a/g10/main.h +++ b/g10/main.h @@ -84,6 +84,7 @@ struct weakhash /*-- gpg.c --*/ extern int g10_errors_seen; extern int assert_signer_true; +extern int assert_pubkey_algo_false; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((__noreturn__)); @@ -494,6 +495,7 @@ 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); +void check_assert_pubkey_algo (const char *algostr, 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 430d7ff08..5f3f6df86 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -1876,6 +1876,8 @@ check_sig_and_print (CTX c, kbnode_t node) const void *extrahash = NULL; size_t extrahashlen = 0; kbnode_t included_keyblock = NULL; + char pkstrbuf[PUBKEY_STRING_SIZE] = { 0 }; + if (opt.skip_verify) { @@ -2409,8 +2411,14 @@ check_sig_and_print (CTX c, kbnode_t node) show_notation (sig, 0, 2, 0); } + /* Fill PKSTRBUF with the algostring in case we later need it. */ + if (pk) + pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); + /* For good signatures print the VALIDSIG status line. */ - if (!rc && (is_status_enabled () || opt.assert_signer_list) && pk) + if (!rc && (is_status_enabled () + || opt.assert_signer_list + || opt.assert_pubkey_algos) && pk) { char pkhex[MAX_FINGERPRINT_LEN*2+1]; char mainpkhex[MAX_FINGERPRINT_LEN*2+1]; @@ -2432,6 +2440,8 @@ check_sig_and_print (CTX c, kbnode_t node) mainpkhex); /* Handle the --assert-signer option. */ check_assert_signer_list (mainpkhex, pkhex); + /* Handle the --assert-pubkey-algo option. */ + check_assert_pubkey_algo (pkstrbuf, pkhex); } /* Print compliance warning for Good signatures. */ @@ -2464,13 +2474,6 @@ check_sig_and_print (CTX c, kbnode_t node) if (opt.verbose) { - char pkstrbuf[PUBKEY_STRING_SIZE]; - - if (pk) - pubkey_string (pk, pkstrbuf, sizeof pkstrbuf); - else - *pkstrbuf = 0; - log_info (_("%s signature, digest algorithm %s%s%s\n"), sig->sig_class==0x00?_("binary"): sig->sig_class==0x01?_("textmode"):_("unknown"), diff --git a/g10/options.h b/g10/options.h index 146b78361..1e1110334 100644 --- a/g10/options.h +++ b/g10/options.h @@ -241,6 +241,10 @@ struct * modify to be uppercase if they represent a fingerrint */ strlist_t assert_signer_list; + /* A single string with the comma delimited args from + * --assert-pubkey_algo. */ + char *assert_pubkey_algos; + 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 e40be9cc1..90ce6e9a6 100644 --- a/g10/t-keydb-get-keyblock.c +++ b/g10/t-keydb-get-keyblock.c @@ -67,12 +67,3 @@ 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 9055d5b94..4c78dac48 100644 --- a/g10/t-keydb.c +++ b/g10/t-keydb.c @@ -105,13 +105,3 @@ 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-keyid.c b/g10/t-keyid.c new file mode 100644 index 000000000..d42399027 --- /dev/null +++ b/g10/t-keyid.c @@ -0,0 +1,129 @@ +/* t-keyid.c - Tests for keyid.c. + * Copyright (C) 2024 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * 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> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#define LEAN_T_SUPPORT 1 + +#define PGM "t-keyid" + +#include "gpg.h" +#include "keydb.h" +#include "../common/t-support.h" + + + +static int verbose; + + +static void +test_compare_pubkey_string (void) +{ + static struct { const char *astr; const char *bstr; int expected; } t[] = + { + { "rsa2048" , "rsa2048" , 1 }, + { "rsa2048" , ">=rsa2048" , 1 }, + { "rsa2048" , ">rsa2048" , 0 }, + { "ed25519" , ">rsa1024" , 0 }, + { "ed25519" , "ed25519" , 1 }, + { "ed25519" , ",,,=ed25519" , 1 }, + { "nistp384" , ">nistp256" , 1 }, + { "nistp521" , ">=rsa3072, >nistp384", 1 }, + { " nistp521" , ">=rsa3072, >nistp384 ", 1 }, + { " nistp521 " , " >=rsa3072, >nistp384 ", 1 }, + { " =nistp521 " , " >=rsa3072, >nistp384,,", 1 }, + { "nistp384" , ">nistp384" , 0 }, + { "nistp384" , ">=nistp384" , 1 }, + { "brainpoolP384" , ">=brainpoolp256", 1 }, + { "brainpoolP384" , ">brainpoolp384" , 0 }, + { "brainpoolP384" , ">=brainpoolp384", 1 }, + { "brainpoolP256r1", ">brainpoolp256r1", 0 }, + { "brainpoolP384r1", ">brainpoolp384r1" , 0 }, + { "brainpoolP384r1", ">=brainpoolp384r1", 1 }, + { "brainpoolP384r1", ">=brainpoolp384" , 1 }, + { "", "", 0} + }; + int idx; + int result; + + for (idx=0; idx < DIM(t); idx++) + { + result = compare_pubkey_string (t[idx].astr, t[idx].bstr); + if (result != t[idx].expected) + { + fail (idx); + if (verbose) + log_debug ("\"%s\", \"%s\" want %d got %d\n", + t[idx].astr, t[idx].bstr, t[idx].expected, result); + } + } + +} + + +int +main (int argc, char **argv) +{ + int last_argc = -1; + + no_exit_on_fail = 1; + + if (argc) + { argc--; argv++; } + while (argc && last_argc != argc ) + { + last_argc = argc; + if (!strcmp (*argv, "--")) + { + argc--; argv++; + break; + } + else if (!strcmp (*argv, "--help")) + { + fputs ("usage: " PGM " [FILE]\n" + "Options:\n" + " --verbose Print timings etc.\n" + " --debug Flyswatter\n" + , stdout); + exit (0); + } + else if (!strcmp (*argv, "--verbose")) + { + verbose++; + argc--; argv++; + } + else if (!strcmp (*argv, "--debug")) + { + verbose += 2; + argc--; argv++; + } + else if (!strncmp (*argv, "--", 2)) + { + fprintf (stderr, PGM ": unknown option '%s'\n", *argv); + exit (1); + } + } + + test_compare_pubkey_string (); + + return !!errcount; +} diff --git a/g10/t-stutter.c b/g10/t-stutter.c index 7b2ea4b37..503a92004 100644 --- a/g10/t-stutter.c +++ b/g10/t-stutter.c @@ -611,12 +611,3 @@ 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/test-stubs.c b/g10/test-stubs.c index 6ae0f4eb7..d9bead754 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -43,6 +43,9 @@ #include "call-agent.h" int g10_errors_seen; +int assert_signer_true = 0; +int assert_pubkey_algo_false = 0; + void @@ -580,3 +583,18 @@ impex_filter_getval (void *cookie, const char *propname) (void)propname; return NULL; } + + +void +check_assert_signer_list (const char *mainpkhex, const char *pkhex) +{ + (void)mainpkhex; + (void)pkhex; +} + +void +check_assert_pubkey_algo (const char *algostr, const char *pkhex) +{ + (void)algostr; + (void)pkhex; +} diff --git a/g10/test.c b/g10/test.c index 648148a10..f6c697a35 100644 --- a/g10/test.c +++ b/g10/test.c @@ -15,6 +15,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> diff --git a/g10/verify.c b/g10/verify.c index e9792939d..1c3de767c 100644 --- a/g10/verify.c +++ b/g10/verify.c @@ -333,7 +333,7 @@ check_assert_signer_list (const char *mainpkhex, const char *pkhex) assert_signer_true = 1; write_status_text (STATUS_ASSERT_SIGNER, item->d); if (!opt.quiet) - log_info ("signer '%s' matched\n", item->d); + log_info ("asserted signer '%s'\n", item->d); goto leave; } } @@ -388,7 +388,7 @@ check_assert_signer_list (const char *mainpkhex, const char *pkhex) assert_signer_true = 1; write_status_text (STATUS_ASSERT_SIGNER, p); if (!opt.quiet) - log_info ("signer '%s' matched '%s', line %d\n", + log_info ("asserted signer '%s' (%s:%d)\n", p, fname, lnr); goto leave; } @@ -405,3 +405,32 @@ check_assert_signer_list (const char *mainpkhex, const char *pkhex) leave: es_fclose (fp); } + + +/* This function shall be called with the signer's public key + * algorithm ALGOSTR iff a signature is fully valid. If the option + * --assert-pubkey-algo is active the functions checks whether the + * signing key's algo is valid according to that list; in this case a + * global flag is set. */ +void +check_assert_pubkey_algo (const char *algostr, const char *pkhex) +{ + if (!opt.assert_pubkey_algos) + return; /* Nothing to do. */ + + if (compare_pubkey_string (algostr, opt.assert_pubkey_algos)) + { + write_status_strings (STATUS_ASSERT_PUBKEY_ALGO, + pkhex, " 1 ", algostr, NULL); + if (!opt.quiet) + log_info ("asserted signer '%s' with algo %s\n", pkhex, algostr); + } + else + { + if (!opt.quiet) + log_info ("denied signer '%s' with algo %s\n", pkhex, algostr); + assert_pubkey_algo_false = 1; + write_status_strings (STATUS_ASSERT_PUBKEY_ALGO, + pkhex, " 0 ", algostr, NULL); + } +} |