diff --git a/common/miscellaneous.c b/common/miscellaneous.c index e40dcd55f..c3775547b 100644 --- a/common/miscellaneous.c +++ b/common/miscellaneous.c @@ -760,3 +760,83 @@ parse_debug_flag (const char *string, unsigned int *debugvar, *debugvar |= result; return 0; } + + + +/* Parse an --comaptibility_flags style argument consisting of comma + * separated strings. + * + * Returns: 0 on success or -1 and ERRNO set on error. On success the + * supplied variable is updated by the parsed flags. + * + * If STRING is NULL the enabled flags are printed. + */ +int +parse_compatibility_flags (const char *string, unsigned int *flagvar, + const struct compatibility_flags_s *flags) + +{ + unsigned long result = 0; + int i, j; + + if (!string) + { + if (flagvar) + { + log_info ("enabled compatibility flags:"); + for (i=0; flags[i].name; i++) + if ((*flagvar & flags[i].flag)) + log_printf (" %s", flags[i].name); + log_printf ("\n"); + } + return 0; + } + + while (spacep (string)) + string++; + + if (!strcmp (string, "?") || !strcmp (string, "help")) + { + log_info ("available compatibility flags:\n"); + for (i=0; flags[i].name; i++) + log_info (" %s\n", flags[i].name); + if (flags[i].flag != 77) + exit (0); + } + else + { + char **words; + words = strtokenize (string, ","); + if (!words) + return -1; + for (i=0; words[i]; i++) + { + if (*words[i]) + { + for (j=0; flags[j].name; j++) + if (!strcmp (words[i], flags[j].name)) + { + result |= flags[j].flag; + break; + } + if (!flags[j].name) + { + if (!strcmp (words[i], "none")) + { + *flagvar = 0; + result = 0; + } + else if (!strcmp (words[i], "all")) + result = ~0; + else + log_info ("unknown compatibility flag '%s' ignored\n", + words[i]); + } + } + } + xfree (words); + } + + *flagvar |= result; + return 0; +} diff --git a/common/util.h b/common/util.h index 9f46457ba..82b3a34af 100644 --- a/common/util.h +++ b/common/util.h @@ -358,6 +358,15 @@ struct debug_flags_s int parse_debug_flag (const char *string, unsigned int *debugvar, const struct debug_flags_s *flags); +struct compatibility_flags_s +{ + unsigned int flag; + const char *name; + const char *desc; +}; +int parse_compatibility_flags (const char *string, unsigned int *flagvar, + const struct compatibility_flags_s *flags); + /*-- Simple replacement functions. */ diff --git a/doc/gpgsm.texi b/doc/gpgsm.texi index 06a85b1ed..ba91aed92 100644 --- a/doc/gpgsm.texi +++ b/doc/gpgsm.texi @@ -710,6 +710,14 @@ Include ephemeral flagged keys in the output of key listings. Note that they are included anyway if the key specification for a listing is given as fingerprint or keygrip. +@item --compatibility-flags @var{flags} +@opindex compatibility-flags +Set compatibility flags to work around problems due to non-compliant +certificates or data. The @var{flags} are given as a comma separated +list of flag names and are OR-ed together. The special flag "none" +clears the list and allows to start over with an empty list. To get a +list of available flags the sole word "help" can be used. + @item --debug-level @var{level} @opindex debug-level Select the debug level for investigating problems. @var{level} may be diff --git a/sm/certlist.c b/sm/certlist.c index c3e4e821b..b1ae58c52 100644 --- a/sm/certlist.c +++ b/sm/certlist.c @@ -52,9 +52,11 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) { gpg_error_t err; unsigned int use; + unsigned int encr_bits, sign_bits; char *extkeyusages; int have_ocsp_signing = 0; + err = ksba_cert_get_ext_key_usages (cert, &extkeyusages); if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = 0; /* no policy given */ @@ -158,10 +160,13 @@ cert_usage_p (ksba_cert_t cert, int mode, int silent) return gpg_error (GPG_ERR_WRONG_KEY_USAGE); } - if ((use & ((mode&1)? - (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT): - (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) - ) + encr_bits = (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT); + if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR)) + encr_bits |= KSBA_KEYUSAGE_KEY_AGREEMENT; + + sign_bits = (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION); + + if ((use & ((mode&1)? encr_bits : sign_bits))) return 0; if (!silent) diff --git a/sm/gpgsm.c b/sm/gpgsm.c index 87067cf31..27168904c 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -200,6 +200,7 @@ enum cmd_and_opt_values { oIgnoreCertExtension, oIgnoreCertWithOID, oRequireCompliance, + oCompatibilityFlags, oNoAutostart }; @@ -422,6 +423,7 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), ARGPARSE_s_s (oLCmessages, "lc-messages", "@"), ARGPARSE_s_s (oXauthority, "xauthority", "@"), + ARGPARSE_s_s (oCompatibilityFlags, "compatibility-flags", "@"), ARGPARSE_header (NULL, ""), /* Stop the header group. */ @@ -457,6 +459,14 @@ static struct debug_flags_s debug_flags [] = }; +/* The list of compatibility flags. */ +static struct compatibility_flags_s compatibility_flags [] = + { + { COMPAT_ALLOW_KA_TO_ENCR, "allow-ka-to-encr" }, + { 0, NULL } + }; + + /* Global variable to keep an error count. */ int gpgsm_errors_seen = 0; @@ -1233,6 +1243,15 @@ main ( int argc, char **argv) case oDebugNoChainValidation: opt.no_chain_validation = 1; break; case oDebugIgnoreExpiration: opt.ignore_expiration = 1; break; + case oCompatibilityFlags: + if (parse_compatibility_flags (pargs.r.ret_str, &opt.compat_flags, + compatibility_flags)) + { + pargs.r_opt = ARGPARSE_INVALID_ARG; + pargs.err = ARGPARSE_PRINT_ERROR; + } + break; + case oStatusFD: ctrl.status_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 1); break; @@ -1509,6 +1528,8 @@ main ( int argc, char **argv) gcry_control (GCRYCTL_RESUME_SECMEM_WARN); set_debug (); + if (opt.verbose) /* Print the compatibility flags. */ + parse_compatibility_flags (NULL, &opt.compat_flags, compatibility_flags); gnupg_set_compliance_extra_info (opt.min_rsa_length); /* Although we always use gpgsm_exit, we better install a regualr diff --git a/sm/gpgsm.h b/sm/gpgsm.h index 5e7db4bec..53ef165a1 100644 --- a/sm/gpgsm.h +++ b/sm/gpgsm.h @@ -150,6 +150,8 @@ struct * mode. */ int require_compliance; + /* Compatibility flags (COMPAT_FLAG_xxxx). */ + unsigned int compat_flags; } opt; /* Debug values and macros. */ @@ -169,6 +171,18 @@ struct #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) + +/* Compatibility flags */ +/* Telesec RSA cards produced for NRW in 2022 came with only the + * keyAgreement bit set. This flag allows there use for encryption + * anyway. Example cert: + * Issuer: /CN=DOI CA 10a/OU=DOI/O=PKI-1-Verwaltung/C=DE + * key usage: digitalSignature nonRepudiation keyAgreement + * policies: 1.3.6.1.4.1.7924.1.1:N: + */ +#define COMPAT_ALLOW_KA_TO_ENCR 1 + + /* Forward declaration for an object defined in server.c */ struct server_local_s; diff --git a/sm/keylist.c b/sm/keylist.c index 4f2d009fd..2d51aa74d 100644 --- a/sm/keylist.c +++ b/sm/keylist.c @@ -251,9 +251,11 @@ print_capabilities (ksba_cert_t cert, estream_t fp) { gpg_error_t err; unsigned int use; + unsigned int is_encr, is_sign, is_cert; size_t buflen; char buffer[1]; + err = ksba_cert_get_user_data (cert, "is_qualified", &buffer, sizeof (buffer), &buflen); if (!err && buflen) @@ -285,17 +287,33 @@ print_capabilities (ksba_cert_t cert, estream_t fp) return; } + is_encr = is_sign = is_cert = 0; + if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) + is_encr = 1; + if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + is_sign = 1; + if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + is_cert = 1; + + /* We need to returned the faked key usage to frontends so that they + * can select the right key. Note that we don't do this for the + * human readable keyUsage. */ + if ((opt.compat_flags & COMPAT_ALLOW_KA_TO_ENCR) + && (use & KSBA_KEYUSAGE_KEY_AGREEMENT)) + is_encr = 1; + + if (is_encr) es_putc ('e', fp); - if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + if (is_sign) es_putc ('s', fp); - if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + if (is_cert) es_putc ('c', fp); - if ((use & (KSBA_KEYUSAGE_KEY_ENCIPHERMENT|KSBA_KEYUSAGE_DATA_ENCIPHERMENT))) + if (is_encr) es_putc ('E', fp); - if ((use & (KSBA_KEYUSAGE_DIGITAL_SIGNATURE|KSBA_KEYUSAGE_NON_REPUDIATION))) + if (is_sign) es_putc ('S', fp); - if ((use & KSBA_KEYUSAGE_KEY_CERT_SIGN)) + if (is_cert) es_putc ('C', fp); }