From 9f7ff43672478b213582125a7d288c96c5bb73b9 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 17 Nov 2022 14:22:04 +0100 Subject: scd: Redact --debug cardio output of a VERIFY APDU. * scd/apdu.c (pcsc_send_apdu) [DBG_CARD_IO]: Detect and redact a VERIFY. (send_apdu_ccid): Ditto. -- This should handle the most common case. GnuPG-bug-id: 5085 --- scd/apdu.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/scd/apdu.c b/scd/apdu.c index e83815ba5..ffada1d78 100644 --- a/scd/apdu.c +++ b/scd/apdu.c @@ -773,7 +773,14 @@ pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen, return err; if (DBG_CARD_IO) - log_printhex (apdu, apdulen, " PCSC_data:"); + { + /* Do not dump the PIN in a VERIFY command. */ + if (apdulen > 5 && apdu[1] == 0x20) + log_debug ("PCSC_data: %02X %02X %02X %02X %02X [redacted]\n", + apdu[0], apdu[1], apdu[2], apdu[3], apdu[4]); + else + log_printhex (apdu, apdulen, "PCSC_data:"); + } if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1)) send_pci.protocol = PCSC_PROTOCOL_T1; @@ -1555,7 +1562,14 @@ send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen, return err; if (DBG_CARD_IO) - log_printhex (apdu, apdulen, " raw apdu:"); + { + /* Do not dump the PIN in a VERIFY command. */ + if (apdulen > 5 && apdu[1] == 0x20) + log_debug (" raw apdu: %02x%02x%02x%02x%02x [redacted]\n", + apdu[0], apdu[1], apdu[2], apdu[3], apdu[4]); + else + log_printhex (apdu, apdulen, " raw apdu:"); + } maxbuflen = *buflen; if (pininfo) -- cgit v1.2.3 From ce5bed2800e59d5866b60648ee6487e5fcb72012 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 18 Nov 2022 19:23:09 +0900 Subject: w32: Fix gnupg_tmpfile for possible failure. * common/sysutils.c (gnupg_tmpfile): Use different value for next attempt. -- The resolution of system timer is typically in the range of 10 milliseconds to 16 milliseconds. Thus, before the change, it may fail. Actually, it failed with Wine emulation. Signed-off-by: NIIBE Yutaka --- common/sysutils.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/common/sysutils.c b/common/sysutils.c index bbed309a8..ee9a25715 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -609,7 +609,7 @@ gnupg_tmpfile (void) char *name, *p; HANDLE file; int pid = GetCurrentProcessId (); - unsigned int value; + unsigned int value = 0; int i; SECURITY_ATTRIBUTES sec_attr; @@ -634,12 +634,9 @@ gnupg_tmpfile (void) for (attempts=0; attempts < 10; attempts++) { p = name; - value = (GetTickCount () ^ ((pid<<16) & 0xffff0000)); + value += (GetTickCount () ^ ((pid<<16) & 0xffff0000)); for (i=0; i < 8; i++) - { - *p++ = tohex (((value >> 28) & 0x0f)); - value <<= 4; - } + *p++ = tohex (((value >> (7 - i)*4) & 0x0f)); strcpy (p, ".tmp"); file = CreateFile (buffer, GENERIC_READ | GENERIC_WRITE, -- cgit v1.2.3 From 7ab2e4386ffe6e435129f84860e1d90779268197 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 22 Nov 2022 15:03:43 +0900 Subject: tests: Use 233 for invalid value of FD. * tests/openpgp/issue2941.scm: Use 233. -- Forward port from 2.2 branch of: 43722438a826e1a162723a23452018ccf1b640ec On Windows machine (emulated by Wine), 23 may be valid value for handle. Signed-off-by: NIIBE Yutaka --- tests/openpgp/issue2941.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/openpgp/issue2941.scm b/tests/openpgp/issue2941.scm index 8f625ebe3..306df6cb5 100755 --- a/tests/openpgp/issue2941.scm +++ b/tests/openpgp/issue2941.scm @@ -29,6 +29,6 @@ (for-each-p "Checking invocation with invalid file descriptors (issue2941)." (lambda (option) - (check-failure `(,(string-append "--" option "=23") --sign gpg.conf))) + (check-failure `(,(string-append "--" option "=233") --sign gpg.conf))) '("status-fd" "attribute-fd" "logger-fd" "override-session-key-fd" "passphrase-fd" "command-fd")) -- cgit v1.2.3 From 1372b17731872d6db732b7623b27ea3e90db6331 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 22 Nov 2022 15:09:52 +0900 Subject: tests: Keep .log files in objdir. * tests/gpgscm/tests.scm (open-log-file): Keep the log file in objdir. -- Forward port from 2.2 branch of: 1c88104a3f00f7ca3790fbaab8f67b2b68cd6e18 Before the change, it is at ephemeral temp directory which is removed. This is not useful at all. Possibly, it was done before the introduce of ephemeral temp directory for each test and not changed. Signed-off-by: NIIBE Yutaka --- tests/gpgscm/tests.scm | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/gpgscm/tests.scm b/tests/gpgscm/tests.scm index cebe419f8..31e140d74 100644 --- a/tests/gpgscm/tests.scm +++ b/tests/gpgscm/tests.scm @@ -725,7 +725,9 @@ (define (open-log-file) (unless log-file-name - (set! log-file-name (string-append (basename name) ".log"))) + (set! log-file-name (path-join + (getenv "objdir") + (string-append name ".log")))) (catch '() (unlink log-file-name)) (open log-file-name (logior O_RDWR O_BINARY O_CREAT) #o600)) -- cgit v1.2.3 From 7fe524e1828ec80f1d776711614e5620a42779f8 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 22 Nov 2022 15:11:42 +0900 Subject: tests:gpgscm:w32: Fix for GetTempPath. * tests/gpgscm/ffi.c (do_get_temp_path): Remove the last backslash. -- Forward port from 2.2 branch of: 9a75460652d6055983930e80e022396f613ed6f7 Signed-off-by: NIIBE Yutaka --- tests/gpgscm/ffi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/gpgscm/ffi.c b/tests/gpgscm/ffi.c index aefd7c385..ce18e0794 100644 --- a/tests/gpgscm/ffi.c +++ b/tests/gpgscm/ffi.c @@ -357,6 +357,11 @@ do_get_temp_path (scheme *sc, pointer args) #ifdef HAVE_W32_SYSTEM if (GetTempPath (MAX_PATH+1, buffer) == 0) FFI_RETURN_STRING (sc, "/temp"); + else + { + size_t len = strlen (buffer); + buffer[len-1] = 0; + } FFI_RETURN_STRING (sc, buffer); #else FFI_RETURN_STRING (sc, "/tmp"); -- cgit v1.2.3 From 7071f3076287f2967ab512eed46b868970702fac Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Tue, 22 Nov 2022 15:19:47 +0900 Subject: tests:w32: Fix for non-dot file name for Windows. * tests/migrations/from-classic.scm (assert-migrated): Handle the case on Windows. -- Forward port from 2.2 branch of: 754175a46d3bc34e9ef8098dbd05abdfd61ada64 Signed-off-by: NIIBE Yutaka --- tests/migrations/from-classic.scm | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/migrations/from-classic.scm b/tests/migrations/from-classic.scm index b473d702a..2033a4a77 100755 --- a/tests/migrations/from-classic.scm +++ b/tests/migrations/from-classic.scm @@ -26,7 +26,7 @@ (call-check `(,@GPG --list-secret-keys))) (define (assert-migrated) - (unless (file-exists? ".gpg-v21-migrated") + (unless (or (file-exists? ".gpg-v21-migrated") (file-exists? "gpg-v21-migrated")) (error "Not migrated")) (for-each -- cgit v1.2.3 From 1246e16432b4240ad81c0bd757d7458b609dfd96 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 24 Nov 2022 15:22:32 +0900 Subject: tests: Fix to support --enable-all-tests and variants. * tests/gpgscm/tests.scm (test::scm): Add VARIANT argument. (tests::new): Likewise. (open-log-file, report): Support VARIANT. * tests/gpgme/all-tests.scm (setup-c, setup-py): Follow the change. * tests/cms/all-tests.scm: Likewise. * tests/cms/run-tests.scm: Likewise. * tests/migrations/all-tests.scm: Likewise. * tests/migrations/run-tests.scm: Likewise. * tests/openpgp/all-tests.scm: Likewise. * tests/openpgp/run-tests.scm: Likewise. -- Forward port from 2.2 branch of: 0fd7a902070ad9bdd835fa57dbadff25917bca42 Fixes-commit: 1c88104a3f00f7ca3790fbaab8f67b2b68cd6e18 Signed-off-by: NIIBE Yutaka --- tests/cms/all-tests.scm | 4 ++++ tests/cms/run-tests.scm | 2 ++ tests/gpgme/all-tests.scm | 3 +++ tests/gpgscm/tests.scm | 17 +++++++++++------ tests/migrations/all-tests.scm | 1 + tests/migrations/run-tests.scm | 1 + tests/openpgp/all-tests.scm | 18 ++++++++++-------- tests/openpgp/run-tests.scm | 6 ++++-- 8 files changed, 36 insertions(+), 16 deletions(-) diff --git a/tests/cms/all-tests.scm b/tests/cms/all-tests.scm index 78ad00678..1c04a4a32 100644 --- a/tests/cms/all-tests.scm +++ b/tests/cms/all-tests.scm @@ -29,13 +29,17 @@ (define setup (make-environment-cache (test::scm + #f #f (path-join "tests" "cms" "setup.scm") (in-srcdir "tests" "cms" "setup.scm") + (path-join "tests" "gpgsm" "setup.scm") + (in-srcdir "tests" "gpgsm" "setup.scm") "--" "tests" "gpg"))) (map (lambda (name) (test::scm setup + #f (path-join "tests" "cms" name) (in-srcdir "tests" "cms" name))) (parse-makefile-expand (in-srcdir "tests" "cms" "Makefile.am") diff --git a/tests/cms/run-tests.scm b/tests/cms/run-tests.scm index caae86d5b..7079ed3ca 100644 --- a/tests/cms/run-tests.scm +++ b/tests/cms/run-tests.scm @@ -27,6 +27,7 @@ (define setup (make-environment-cache (test::scm + #f #f (path-join "tests" "cms" "setup.scm") (in-srcdir "tests" "cms" "setup.scm")))) @@ -35,5 +36,6 @@ (load-tests "tests" "cms") (map (lambda (name) (test::scm setup + #f (path-join "tests" "cms" name) (in-srcdir "tests" "cms" name))) tests))) diff --git a/tests/gpgme/all-tests.scm b/tests/gpgme/all-tests.scm index f72f8af97..1746c4ee1 100644 --- a/tests/gpgme/all-tests.scm +++ b/tests/gpgme/all-tests.scm @@ -39,6 +39,7 @@ (define setup-c (make-environment-cache (test::scm + #f #f (path-join "tests" "gpgme" "setup.scm" "tests" "gpg") (in-srcdir "tests" "gpgme" "setup.scm") @@ -46,6 +47,7 @@ (define setup-py (make-environment-cache (test::scm + #f #f (path-join "tests" "gpgme" "setup.scm" "lang" "python" "tests") (in-srcdir "tests" "gpgme" "setup.scm") @@ -71,6 +73,7 @@ (map (lambda (name) (apply test::scm `(,(:setup cmpnts) + #f ,(apply path-join `("tests" "gpgme" ,@(:path cmpnts) ,name)) ,(in-srcdir "tests" "gpgme" "wrap.scm") diff --git a/tests/gpgscm/tests.scm b/tests/gpgscm/tests.scm index 31e140d74..db1025bbb 100644 --- a/tests/gpgscm/tests.scm +++ b/tests/gpgscm/tests.scm @@ -679,14 +679,14 @@ name)) (package - (define (scm setup name path . args) + (define (scm setup variant name path . args) ;; Start the process. (define (spawn-scm args' in out err) (spawn-process-fd `(,*argv0* ,@(verbosity (*verbose*)) ,(locate-test (test-name path)) ,@(if setup (force setup) '()) ,@args' ,@args) in out err)) - (new name #f spawn-scm #f #f CLOSED_FD (expect-failure? name))) + (new variant name #f spawn-scm #f #f CLOSED_FD (expect-failure? name))) (define (binary setup name path . args) ;; Start the process. @@ -694,9 +694,9 @@ (spawn-process-fd `(,(test-name path) ,@(if setup (force setup) '()) ,@args' ,@args) in out err)) - (new name #f spawn-binary #f #f CLOSED_FD (expect-failure? name))) + (new #f name #f spawn-binary #f #f CLOSED_FD (expect-failure? name))) - (define (new name directory spawn pid retcode logfd expect-failure) + (define (new variant name directory spawn pid retcode logfd expect-failure) (package ;; XXX: OO glue. @@ -727,7 +727,9 @@ (unless log-file-name (set! log-file-name (path-join (getenv "objdir") - (string-append name ".log")))) + (if variant + (string-append name "." variant ".log") + (string-append name ".log"))))) (catch '() (unlink log-file-name)) (open log-file-name (logior O_RDWR O_BINARY O_CREAT) #o600)) @@ -776,7 +778,10 @@ (seek logfd 0 SEEK_SET) (splice logfd STDERR_FILENO) (close logfd)) - (echo (string-append (status-string) ":") name)) + (echo (string-append (status-string) ":") + (if variant + (string-append "<" variant ">" name) + name))) (define (xml) (xx::tag diff --git a/tests/migrations/all-tests.scm b/tests/migrations/all-tests.scm index 421f69679..ba95f5c50 100644 --- a/tests/migrations/all-tests.scm +++ b/tests/migrations/all-tests.scm @@ -28,6 +28,7 @@ (map (lambda (name) (test::scm #f + #f (path-join "tests" "migrations" name) (in-srcdir "tests" "migrations" name))) (parse-makefile-expand (in-srcdir "tests" "migrations" "Makefile.am") diff --git a/tests/migrations/run-tests.scm b/tests/migrations/run-tests.scm index f44334c7d..d4db14f56 100644 --- a/tests/migrations/run-tests.scm +++ b/tests/migrations/run-tests.scm @@ -23,5 +23,6 @@ (load-tests "tests" "migrations") (map (lambda (name) (test::scm #f + #f (path-join "tests" "migrations" name) (in-srcdir "tests" "migrations" name))) tests))) diff --git a/tests/openpgp/all-tests.scm b/tests/openpgp/all-tests.scm index 98a8a6507..e40e02dc3 100644 --- a/tests/openpgp/all-tests.scm +++ b/tests/openpgp/all-tests.scm @@ -29,6 +29,7 @@ (define setup (make-environment-cache (test::scm + #f #f (path-join "tests" "openpgp" "setup.scm") (in-srcdir "tests" "openpgp" "setup.scm")))) @@ -40,7 +41,8 @@ (make-environment-cache (test::scm #f - (qualify (path-join "tests" "openpgp" "setup.scm") variant) + variant + (path-join "tests" "openpgp" "setup.scm") (in-srcdir "tests" "openpgp" "setup.scm") (string-append "--" variant)))) @@ -62,7 +64,8 @@ (define tests (map (lambda (name) (test::scm setup - (qualify (path-join "tests" "openpgp" name) "standard") + "standard" + (path-join "tests" "openpgp" name) (in-srcdir "tests" "openpgp" name))) all-tests)) (when *run-all-tests* @@ -73,17 +76,16 @@ (if keyboxd-enabled? (map (lambda (name) (test::scm setup-use-keyboxd - (qualify (path-join "tests" "openpgp" name) - "keyboxd") + "keyboxd" + (path-join "tests" "openpgp" name) (in-srcdir "tests" "openpgp" name) "--use-keyboxd")) all-tests)) ;; The third pass uses the legact pubring.gpg (map (lambda (name) (test::scm setup-use-keyring - (qualify (path-join "tests" "openpgp" name) - "keyring") + "keyring" + (path-join "tests" "openpgp" name) (in-srcdir "tests" "openpgp" name) - "--use-keyring")) all-tests) - ))) + "--use-keyring")) all-tests)))) tests) diff --git a/tests/openpgp/run-tests.scm b/tests/openpgp/run-tests.scm index 8f9435943..faf52d80b 100644 --- a/tests/openpgp/run-tests.scm +++ b/tests/openpgp/run-tests.scm @@ -28,6 +28,7 @@ (define setup (make-environment-cache (test::scm + #f #f (path-join "tests" "openpgp" "setup.scm") (in-srcdir "tests" "openpgp" "setup.scm")))) @@ -55,11 +56,12 @@ (if use-keyboxd? (map (lambda (name) (test::scm setup-use-keyboxd - (qualify (path-join "tests" "openpgp" name) - "keyboxd") + "keyboxd" + (path-join "tests" "openpgp" name) (in-srcdir "tests" "openpgp" name) "--use-keyboxd")) tests) (map (lambda (name) (test::scm setup + #f (path-join "tests" "openpgp" name) (in-srcdir "tests" "openpgp" name))) tests)))) -- cgit v1.2.3 From d70779bdc60c40c895dcdcb846c10a7b9703263b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 25 Nov 2022 09:21:58 +0100 Subject: dirmngr: Silence ocsp debug output. * dirmngr/ocsp.c (check_signature_core): No debug output -- Also typo and doc fixes. --- dirmngr/ocsp.c | 7 +++++-- doc/DETAILS | 4 ++++ sm/verify.c | 4 ++-- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/dirmngr/ocsp.c b/dirmngr/ocsp.c index 0da96ba15..7267d623e 100644 --- a/dirmngr/ocsp.c +++ b/dirmngr/ocsp.c @@ -504,8 +504,11 @@ check_signature_core (ctrl_t ctrl, ksba_cert_t cert, gcry_sexp_t s_sig, goto leave; } - gcry_log_debugsxp ("sig ", s_sig); - gcry_log_debugsxp ("hash", s_hash); + if (DBG_CRYPTO) + { + gcry_log_debugsxp ("sig ", s_sig); + gcry_log_debugsxp ("hash", s_hash); + } err = gcry_pk_verify (s_sig, s_hash, s_pkey); if (err) diff --git a/doc/DETAILS b/doc/DETAILS index 70dabe0f8..9581f3032 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1694,6 +1694,10 @@ Description of some debug flags: - RFC-6337 :: ECC in OpenPGP - RFC-7292 :: PKCS #12: Personal Information Exchange Syntax v1.1 - RFC-8351 :: The PKCS #8 EncryptedPrivateKeyInfo Media Type + - RFC-8550 :: S/MIME Version 4.0 Certificate Handling + - RFC-8551 :: S/MIME Version 4.0 Message Specification + - RFC-2634 :: Enhanced Security Services for S/MIME + - RFC-5035 :: Enhanced Security Services (ESS) Update - draft-koch-openpgp-2015-rfc4880bis :: Updates to RFC-4880 diff --git a/sm/verify.c b/sm/verify.c index 10d5d5c35..2e40c021f 100644 --- a/sm/verify.c +++ b/sm/verify.c @@ -634,8 +634,8 @@ gpgsm_verify (ctrl_t ctrl, int in_fd, int data_fd, estream_t out_fp) /* FIXME: INFO_PKALGO correctly shows ECDSA but PKALGO is then * ECC. We should use the ECDSA here and need to find a way to - * figure this oult without using the bodus assumtion in - * gpgsm_check_cms_signature that ECC is alwas ECDSA. */ + * figure this out without using the bogus assumption in + * gpgsm_check_cms_signature that ECC is always ECDSA. */ fpr = gpgsm_get_fingerprint_hexstring (cert, GCRY_MD_SHA1); tstr = strtimestamp_r (sigtime); -- cgit v1.2.3 From 1324dc3490b02c4ff818655db1474c594f04e4ec Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 25 Nov 2022 16:04:54 +0100 Subject: gpg: New option --list-filter * g10/gpg.c (oListFilter): New. (opts): Add --list-filter. (main): Parse oListFilter. * g10/keylist.c: Include init.h and recsel.h. (struct list_filter_s, list_filter): New. (release_list_filter): New. (cleanup_keylist_globals): New. (parse_and_set_list_filter): New. (list_keyblock): Implement --list-filter type "select". * g10/import.c (impex_filter_getval): Add scope support and new property names "key-size", "algostr", "origin", "lastupd", and "url". -- This option is pretty useful to select keys based on their properties. The scope thing can be sued to limit a selection to just the primary key or to subkeys. For example: gpg -k --list-filter 'select=revoked-f && sub/algostr=ed25519' Lists all non-revoked keys with an ed25519 (signing)-subkey. --- doc/gpg.texi | 41 ++++++++++++++++++++++++++-- g10/gpg.c | 7 +++++ g10/gpgv.c | 8 ++++++ g10/import.c | 71 ++++++++++++++++++++++++++++++++++++++++++------ g10/keylist.c | 82 +++++++++++++++++++++++++++++++++++++++++++++++++++++--- g10/main.h | 1 + g10/test-stubs.c | 8 ++++++ 7 files changed, 204 insertions(+), 14 deletions(-) diff --git a/doc/gpg.texi b/doc/gpg.texi index 25065f8e4..ed75a613a 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -1338,6 +1338,13 @@ Assume "yes" on most questions. Should not be used in an option file. Assume "no" on most questions. Should not be used in an option file. +@item --list-filter @{select=@var{expr}@} +@opindex list-filter +A list filter can be used to output only certain keys during key +listsin command. For the availbale property names, see the description +of @option{--import-filter}. + + @item --list-options @var{parameters} @opindex list-options This is a space or comma delimited string that gives options used when @@ -2550,11 +2557,21 @@ The available filter types are: Self-signatures are not considered. Currently only implemented for --import-filter. + @item select + This filter is only implemented by @option{--list-filter}. All + property names may be used. + @end table For the syntax of the expression see the chapter "FILTER EXPRESSIONS". The property names for the expressions depend on the actual filter -type and are indicated in the following table. +type and are indicated in the following table. Note that all property +names may also be used by @option{--list-filter}. + +Property names may be prefix with a scope delimited by a slash. Valid +scopes are "pub" for public and secret primary keys, "sub" for public +and secret subkeys, "uid" for for user-ID packets, and "sig" for +signature packets. Invalid scopes are currently ignored. The available properties are: @@ -2567,10 +2584,18 @@ The available properties are: The addr-spec part of a user id with mailbox or the empty string. (keep-uid) + @item algostr + A string with the key algorithm description. For example "rsa3072" + or "ed25519". + @item key_algo A number with the public key algorithm of a key or subkey packet. (drop-subkey) + @item key_size + A number with the effective key size of a key or subkey packet. + (drop-subkey) + @item key_created @itemx key_created_d The first is the timestamp a public key or subkey packet was @@ -2593,7 +2618,7 @@ The available properties are: been revoked. @item disabled - Boolean indicating whether a primary key is disabled. (not used) + Boolean indicating whether a primary key is disabled. @item secret Boolean indicating whether a key or subkey is a secret one. @@ -2616,6 +2641,18 @@ The available properties are: @item sig_digest_algo A number with the digest algorithm of a signature packet. (drop-sig) + @item origin + A string with the key origin or a question mark. For example the + string ``wkd'' is used if a key originated from a Web Key Directory + lookup. + + @item lastupd + The timestamp the key was last updated from a keyserver or the Web + Key Directory. + + @item url + A string with the the URL associated wit the last key lookup. + @end table @item --export-options @var{parameters} diff --git a/g10/gpg.c b/g10/gpg.c index 1514254b9..68c0454ee 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -327,6 +327,7 @@ enum cmd_and_opt_values oExportOptions, oExportFilter, oListOptions, + oListFilter, oVerifyOptions, oTempDir, oExecPath, @@ -794,6 +795,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_header ("Keylist", N_("Options controlling key listings")), ARGPARSE_s_s (oListOptions, "list-options", "@"), + ARGPARSE_s_s (oListFilter, "list-filter", "@"), ARGPARSE_s_n (oFullTimestrings, "full-timestrings", "@"), ARGPARSE_s_n (oShowPhotos, "show-photos", "@"), ARGPARSE_s_n (oNoShowPhotos, "no-show-photos", "@"), @@ -3357,6 +3359,11 @@ main (int argc, char **argv) if (rc) log_error (_("invalid filter option: %s\n"), gpg_strerror (rc)); break; + case oListFilter: + rc = parse_and_set_list_filter (pargs.r.ret_str); + if (rc) + log_error (_("invalid filter option: %s\n"), gpg_strerror (rc)); + break; case oListOptions: if(!parse_list_options(pargs.r.ret_str)) { diff --git a/g10/gpgv.c b/g10/gpgv.c index 3bb99dc6c..ceded4af9 100644 --- a/g10/gpgv.c +++ b/g10/gpgv.c @@ -812,3 +812,11 @@ get_revocation_reason (PKT_signature *sig, char **r_reason, *r_comment = NULL; return 0; } + +const char * +impex_filter_getval (void *cookie, const char *propname) +{ + (void)cookie; + (void)propname; + return NULL; +} diff --git a/g10/import.c b/g10/import.c index c3ef89323..9fab46ca6 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1430,7 +1430,8 @@ check_prefs (ctrl_t ctrl, kbnode_t keyblock) } -/* Helper for apply_*_filter in import.c and export.c. */ +/* Helper for apply_*_filter in import.c and export.c and also used by + * keylist.c. */ const char * impex_filter_getval (void *cookie, const char *propname) { @@ -1440,11 +1441,32 @@ impex_filter_getval (void *cookie, const char *propname) kbnode_t node = parm->node; static char numbuf[20]; const char *result; + const char *s; + enum { scpNone = 0, scpPub, scpSub, scpUid, scpSig} scope = 0; log_assert (ctrl && ctrl->magic == SERVER_CONTROL_MAGIC); - if (node->pkt->pkttype == PKT_USER_ID - || node->pkt->pkttype == PKT_ATTRIBUTE) + /* We allow a prefix delimited by a slash to limit the scope of the + * keyword. Note that "pub" also includes "sec" and "sub" includes + * "ssb". */ + if ((s=strchr (propname, '/')) && s != propname) + { + size_t n = s - propname; + if (!strncmp (propname, "pub", n)) + scope = scpPub; + else if (!strncmp (propname, "sub", n)) + scope = scpSub; + else if (!strncmp (propname, "uid", n)) + scope = scpUid; + else if (!strncmp (propname, "sig", n)) + scope = scpSig; + + propname = s + 1; + } + + if ((node->pkt->pkttype == PKT_USER_ID + || node->pkt->pkttype == PKT_ATTRIBUTE) + && (!scope || scope == scpUid)) { PKT_user_id *uid = node->pkt->pkt.user_id; @@ -1473,7 +1495,8 @@ impex_filter_getval (void *cookie, const char *propname) else result = NULL; } - else if (node->pkt->pkttype == PKT_SIGNATURE) + else if (node->pkt->pkttype == PKT_SIGNATURE + && (!scope || scope == scpSig)) { PKT_signature *sig = node->pkt->pkt.signature; @@ -1503,10 +1526,12 @@ impex_filter_getval (void *cookie, const char *propname) else result = NULL; } - else if (node->pkt->pkttype == PKT_PUBLIC_KEY - || node->pkt->pkttype == PKT_SECRET_KEY - || node->pkt->pkttype == PKT_PUBLIC_SUBKEY - || node->pkt->pkttype == PKT_SECRET_SUBKEY) + else if (((node->pkt->pkttype == PKT_PUBLIC_KEY + || node->pkt->pkttype == PKT_SECRET_KEY) + && (!scope || scope == scpPub)) + || ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY) + && (!scope || scope == scpSub))) { PKT_public_key *pk = node->pkt->pkt.public_key; @@ -1520,6 +1545,16 @@ impex_filter_getval (void *cookie, const char *propname) snprintf (numbuf, sizeof numbuf, "%d", pk->pubkey_algo); result = numbuf; } + else if (!strcmp (propname, "key_size")) + { + snprintf (numbuf, sizeof numbuf, "%u", nbits_from_pk (pk)); + result = numbuf; + } + else if (!strcmp (propname, "algostr")) + { + pubkey_string (pk, parm->hexfpr, sizeof parm->hexfpr); + result = parm->hexfpr; + } else if (!strcmp (propname, "key_created")) { snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->timestamp); @@ -1556,6 +1591,26 @@ impex_filter_getval (void *cookie, const char *propname) hexfingerprint (pk, parm->hexfpr, sizeof parm->hexfpr); result = parm->hexfpr; } + else if (!strcmp (propname, "origin")) + { + result = key_origin_string (pk->keyorg); + } + else if (!strcmp (propname, "lastupd")) + { + snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->keyupdate); + result = numbuf; + } + else if (!strcmp (propname, "url")) + { + if (pk->updateurl && *pk->updateurl) + { + /* Fixme: This might get truncated. */ + mem2str (parm->hexfpr, pk->updateurl, sizeof parm->hexfpr); + result = parm->hexfpr; + } + else + result = ""; + } else result = NULL; } diff --git a/g10/keylist.c b/g10/keylist.c index f8e4ff32a..1ced732a4 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -44,6 +44,8 @@ #include "../common/mbox-util.h" #include "../common/zb32.h" #include "tofu.h" +#include "../common/init.h" +#include "../common/recsel.h" #include "../common/compliance.h" #include "../common/pkscreening.h" @@ -64,16 +66,26 @@ struct keylist_context int no_validity; /* Do not show validity. */ }; - -static void list_keyblock (ctrl_t ctrl, - kbnode_t keyblock, int secret, int has_secret, - int fpr, struct keylist_context *listctx); +/* An object and a global instance to store selectors created from + * --list-filter select=EXPR. + */ +struct list_filter_s +{ + recsel_expr_t selkey; +}; +struct list_filter_s list_filter; /* The stream used to write attribute packets to. */ static estream_t attrib_fp; + + +static void list_keyblock (ctrl_t ctrl, + kbnode_t keyblock, int secret, int has_secret, + int fpr, struct keylist_context *listctx); + /* Release resources from a keylist context. */ static void keylist_context_release (struct keylist_context *listctx) @@ -82,6 +94,49 @@ keylist_context_release (struct keylist_context *listctx) } +static void +release_list_filter (struct list_filter_s *filt) +{ + recsel_release (filt->selkey); + filt->selkey = NULL; +} + + +static void +cleanup_keylist_globals (void) +{ + release_list_filter (&list_filter); +} + + +/* Parse and set an list filter from string. STRING has the format + * "NAME=EXPR" with NAME being the name of the filter. Spaces before + * and after NAME are not allowed. If this function is all called + * several times all expressions for the same NAME are concatenated. + * Supported filter names are: + * + * - select :: If the expression evaluates to true for a certain key + * this key will be listed. The expression may use any + * variable defined for the export and import filters. + * + */ +gpg_error_t +parse_and_set_list_filter (const char *string) +{ + gpg_error_t err; + + /* Auto register the cleanup function. */ + register_mem_cleanup_func (cleanup_keylist_globals); + + if (!strncmp (string, "select=", 7)) + err = recsel_parse_expr (&list_filter.selkey, string+7); + else + err = gpg_error (GPG_ERR_INV_NAME); + + return err; +} + + /* List the keys. If list is NULL, all available keys are listed. * With LOCATE_MODE set the locate algorithm is used to find a key; if * in addition NO_LOCAL is set the locate does not look into the local @@ -2163,6 +2218,7 @@ reorder_keyblock (KBNODE keyblock) do_reorder_keyblock (keyblock, 0); } + static void list_keyblock (ctrl_t ctrl, KBNODE keyblock, int secret, int has_secret, int fpr, @@ -2170,6 +2226,24 @@ list_keyblock (ctrl_t ctrl, { reorder_keyblock (keyblock); + if (list_filter.selkey) + { + int selected = 0; + struct impex_filter_parm_s parm; + parm.ctrl = ctrl; + + for (parm.node = keyblock; parm.node; parm.node = parm.node->next) + { + if (recsel_select (list_filter.selkey, impex_filter_getval, &parm)) + { + selected = 1; + break; + } + } + if (!selected) + return; /* Skip this one. */ + } + if (opt.with_colons) list_keyblock_colon (ctrl, keyblock, secret, has_secret); else if ((opt.list_options & LIST_SHOW_ONLY_FPR_MBOX)) diff --git a/g10/main.h b/g10/main.h index 2f14f374b..6d5ba77ac 100644 --- a/g10/main.h +++ b/g10/main.h @@ -464,6 +464,7 @@ void release_revocation_reason_info (struct revocation_reason_info *reason); void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode, int no_local); void secret_key_list (ctrl_t ctrl, strlist_t list ); +gpg_error_t parse_and_set_list_filter (const char *string); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, diff --git a/g10/test-stubs.c b/g10/test-stubs.c index cfe33b1d0..6ae0f4eb7 100644 --- a/g10/test-stubs.c +++ b/g10/test-stubs.c @@ -572,3 +572,11 @@ get_revocation_reason (PKT_signature *sig, char **r_reason, *r_comment = NULL; return 0; } + +const char * +impex_filter_getval (void *cookie, const char *propname) +{ + (void)cookie; + (void)propname; + return NULL; +} -- cgit v1.2.3 From 2aacd843ad6bab248f7d2bfc727d44a7f95811e5 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 28 Nov 2022 08:21:59 +0100 Subject: gpg: Make --require-compliance work with out --status-fd * g10/mainproc.c (proc_encrypted): Set complaince_de_vs also if require-compliance is set. -- Without this fix require-compliance would fail if no --status-fd was used. --- g10/mainproc.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/g10/mainproc.c b/g10/mainproc.c index f8f3c15bc..330ad10c5 100644 --- a/g10/mainproc.c +++ b/g10/mainproc.c @@ -726,7 +726,7 @@ proc_encrypted (CTX c, PACKET *pkt) } /* Compute compliance with CO_DE_VS. */ - if (!result && is_status_enabled () + if (!result && (is_status_enabled () || opt.flags.require_compliance) /* Overriding session key voids compliance. */ && !opt.override_session_key /* Check symmetric cipher. */ -- cgit v1.2.3 From a4698d0fb20a11bcb5f5e4485ee334af02b48ee1 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Mon, 28 Nov 2022 16:57:14 +0900 Subject: gpg: Fix double-free in gpg --card-edit. * g10/card-util.c (change_name): Don't free ISONAME here. -- Signed-off-by: NIIBE Yutaka --- g10/card-util.c | 1 - 1 file changed, 1 deletion(-) diff --git a/g10/card-util.c b/g10/card-util.c index 339194fb5..02de241f2 100644 --- a/g10/card-util.c +++ b/g10/card-util.c @@ -843,7 +843,6 @@ change_name (void) { tty_printf (_("Error: Combined name too long " "(limit is %d characters).\n"), 39); - xfree (isoname); rc = gpg_error (GPG_ERR_TOO_LARGE); goto leave; } -- cgit v1.2.3 From c985b52e71a83d14099f00f80e3588bd3ad28b98 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 28 Nov 2022 12:43:11 +0100 Subject: gpg: New export-filter export-revocs * g10/options.h (EXPORT_REVOCS): New. * g10/export.c (export_select_filter): New. (struct export_filter_attic_s): Add field. (cleanup_export_globals): Cleanup. (parse_export_options): Add option "export-revocs". (parse_and_set_export_filter): Parse the select type. (do_export_revocs): New. (do_export_stream): Add a way to select things for export. --- doc/gpg.texi | 5 +++ g10/export.c | 124 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--- g10/options.h | 1 + 3 files changed, 124 insertions(+), 6 deletions(-) diff --git a/doc/gpg.texi b/doc/gpg.texi index ed75a613a..c82a4aa4d 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -2710,6 +2710,11 @@ opposite meaning. The options are: running the @option{--edit-key} command "minimize" before export except that the local copy of the key is not modified. Defaults to no. + @item export-revocs + Export only standalone revocation certificates of the key. This + option does not export revocations of 3rd party certificate + revocations. + @item export-dane Instead of outputting the key material output OpenPGP DANE records suitable to put into DNS zone files. An ORIGIN line is printed before diff --git a/g10/export.c b/g10/export.c index edf27bc4d..0187788f0 100644 --- a/g10/export.c +++ b/g10/export.c @@ -63,15 +63,17 @@ struct export_stats_s }; -/* A global variable to store the selector created from +/* Global variables to store the selectors created from * --export-filter keep-uid=EXPR. * --export-filter drop-subkey=EXPR. + * --export-filter select=EXPR. * * FIXME: We should put this into the CTRL object but that requires a * lot more changes right now. */ static recsel_expr_t export_keep_uid; static recsel_expr_t export_drop_subkey; +static recsel_expr_t export_select_filter; /* An object used for a linked list to implement the @@ -81,6 +83,7 @@ struct export_filter_attic_s struct export_filter_attic_s *next; recsel_expr_t export_keep_uid; recsel_expr_t export_drop_subkey; + recsel_expr_t export_select_filter; }; static struct export_filter_attic_s *export_filter_attic; @@ -105,6 +108,8 @@ cleanup_export_globals (void) export_keep_uid = NULL; recsel_release (export_drop_subkey); export_drop_subkey = NULL; + recsel_release (export_select_filter); + export_select_filter = NULL; } @@ -128,6 +133,9 @@ parse_export_options(char *str,unsigned int *options,int noisy) {"export-dane", EXPORT_DANE_FORMAT, NULL, NULL }, + {"export-revocs", EXPORT_REVOCS, NULL, + N_("export only revocation certificates") }, + {"backup", EXPORT_BACKUP, NULL, N_("use the GnuPG key backup format")}, {"export-backup", EXPORT_BACKUP, NULL, NULL }, @@ -184,6 +192,8 @@ parse_export_options(char *str,unsigned int *options,int noisy) * * - secret :: 1 for a secret subkey, else 0. * - key_algo :: Public key algorithm id + * + * - select :: The key is only exported if the filter returns true. */ gpg_error_t parse_and_set_export_filter (const char *string) @@ -197,6 +207,8 @@ parse_and_set_export_filter (const char *string) err = recsel_parse_expr (&export_keep_uid, string+9); else if (!strncmp (string, "drop-subkey=", 12)) err = recsel_parse_expr (&export_drop_subkey, string+12); + else if (!strncmp (string, "select=", 7)) + err = recsel_parse_expr (&export_select_filter, string+7); else err = gpg_error (GPG_ERR_INV_NAME); @@ -217,6 +229,8 @@ push_export_filters (void) export_keep_uid = NULL; item->export_drop_subkey = export_drop_subkey; export_drop_subkey = NULL; + item->export_select_filter = export_select_filter; + export_select_filter = NULL; item->next = export_filter_attic; export_filter_attic = item; } @@ -235,6 +249,7 @@ pop_export_filters (void) cleanup_export_globals (); export_keep_uid = item->export_keep_uid; export_drop_subkey = item->export_drop_subkey; + export_select_filter = item->export_select_filter; } @@ -1872,6 +1887,78 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, } +/* Helper for do_export_stream which writes the own revocations + * certificates (if any) from KEYBLOCK to OUT. */ +static gpg_error_t +do_export_revocs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, + iobuf_t out, unsigned int options, int *any) +{ + gpg_error_t err = 0; + kbnode_t kbctx, node; + PKT_signature *sig; + + (void)ctrl; + + /* NB: walk_kbnode skips packets marked as deleted. */ + for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) + { + if (node->pkt->pkttype != PKT_SIGNATURE) + continue; + sig = node->pkt->pkt.signature; + + /* We are only interested in revocation certifcates. */ + if (!(IS_KEY_REV (sig) || IS_UID_REV (sig) || IS_SUBKEY_REV (sig))) + continue; + + if (!(sig->keyid[0] == keyid[0] && sig->keyid[1] == keyid[1])) + continue; /* Not a self-signature. */ + + /* Do not export signature packets which are marked as not + * exportable. */ + if (!(options & EXPORT_LOCAL_SIGS) + && !sig->flags.exportable) + continue; /* not exportable */ + + /* Do not export packets with a "sensitive" revocation key + * unless the user wants us to. */ + if (!(options & EXPORT_SENSITIVE_REVKEYS) + && sig->revkey) + { + int i; + + for (i = 0; i < sig->numrevkeys; i++) + if ((sig->revkey[i].class & 0x40)) + break; + if (i < sig->numrevkeys) + continue; + } + + if (!sig->flags.checked) + { + log_info ("signature not marked as checked - ignored\n"); + continue; + } + if (!sig->flags.valid) + { + log_info ("signature not not valid - ignored\n"); + continue; + } + + err = build_packet (out, node->pkt); + if (err) + { + log_error ("build_packet(%d) failed: %s\n", + node->pkt->pkttype, gpg_strerror (err)); + goto leave; + } + *any = 1; + } + + leave: + return err; +} + + /* For secret key export we need to setup a decryption context. * Returns 0 and the context at r_cipherhd. */ static gpg_error_t @@ -2066,13 +2153,33 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, NULL, NULL); commit_kbnode (&keyblock); } - else if (export_keep_uid || export_drop_subkey) + else if (export_keep_uid || export_drop_subkey || export_select_filter) { /* Need to merge so that for example the "usage" property * has been setup. */ merge_keys_and_selfsig (ctrl, keyblock); } + + if (export_select_filter) + { + int selected = 0; + struct impex_filter_parm_s parm; + parm.ctrl = ctrl; + + for (parm.node = keyblock; parm.node; parm.node = parm.node->next) + { + if (recsel_select (export_select_filter, + impex_filter_getval, &parm)) + { + selected = 1; + break; + } + } + if (!selected) + continue; /* Skip this keyblock. */ + } + if (export_keep_uid) { commit_kbnode (&keyblock); @@ -2088,10 +2195,15 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, } /* And write it. */ - err = do_export_one_keyblock (ctrl, keyblock, keyid, - out_help? out_help : out, - secret, options, stats, any, - desc, ndesc, descindex, cipherhd); + if ((options & EXPORT_REVOCS)) + err = do_export_revocs (ctrl, keyblock, keyid, + out_help? out_help : out, + options, any); + else + err = do_export_one_keyblock (ctrl, keyblock, keyid, + out_help? out_help : out, + secret, options, stats, any, + desc, ndesc, descindex, cipherhd); if (err) break; diff --git a/g10/options.h b/g10/options.h index b439ef120..ed4865168 100644 --- a/g10/options.h +++ b/g10/options.h @@ -406,6 +406,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define EXPORT_CLEAN (1<<5) #define EXPORT_DANE_FORMAT (1<<7) #define EXPORT_BACKUP (1<<10) +#define EXPORT_REVOCS (1<<11) #define LIST_SHOW_PHOTOS (1<<0) #define LIST_SHOW_POLICY_URLS (1<<1) -- cgit v1.2.3 From 34fafa50f19c708abc9119aa6369425c47317da5 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 29 Nov 2022 10:43:54 +0100 Subject: wkd: Make use of --debug extprog. * tools/wks-util.c (debug_gpg_invocation): New. (get_key_status_cb): Enable debug output. (wks_get_key): Show gpg invocation. (wks_list_key): Ditto. (wks_filter_uid): Ditto. --- tools/wks-util.c | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/tools/wks-util.c b/tools/wks-util.c index a1640d772..a25ca47a5 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -150,6 +150,21 @@ free_uidinfo_list (uidinfo_list_t list) } +static void +debug_gpg_invocation (const char *func, const char **argv) +{ + int i; + + if (!(opt.debug & DBG_EXTPROG_VALUE)) + return; + + log_debug ("%s: exec '%s' with", func, opt.gpg_program); + for (i=0; argv[i]; i++) + log_printf (" '%s'", argv[i]); + log_printf ("\n"); +} + + struct get_key_status_parm_s { @@ -164,7 +179,8 @@ get_key_status_cb (void *opaque, const char *keyword, char *args) { struct get_key_status_parm_s *parm = opaque; - /*log_debug ("%s: %s\n", keyword, args);*/ + if (DBG_CRYPTO) + log_debug ("%s: %s\n", keyword, args); if (!strcmp (keyword, "EXPORTED")) { parm->count++; @@ -239,6 +255,7 @@ wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec, goto leave; } parm.fpr = fingerprint; + debug_gpg_invocation (__func__, argv); err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL, NULL, key, get_key_status_cb, &parm); @@ -332,6 +349,7 @@ wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes) err = gpg_error_from_syserror (); goto leave; } + debug_gpg_invocation (__func__, argv); err = gnupg_exec_tool_stream (opt.gpg_program, argv, key, NULL, listing, key_status_cb, NULL); @@ -510,6 +528,7 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid, err = gpg_error_from_syserror (); goto leave; } + debug_gpg_invocation (__func__, argv); err = gnupg_exec_tool_stream (opt.gpg_program, argv, key, NULL, newkey, key_status_cb, NULL); -- cgit v1.2.3 From fbc52f550174aa40d43dba5a89c6c3f9817ea477 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 29 Nov 2022 15:28:35 +0100 Subject: doc: Comment typo fix -- --- dirmngr/crlcache.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dirmngr/crlcache.c b/dirmngr/crlcache.c index f0893a64a..befc6b94b 100644 --- a/dirmngr/crlcache.c +++ b/dirmngr/crlcache.c @@ -984,8 +984,8 @@ make_db_file_name (const char *issuer_hash) /* Hash the file FNAME and return the MD5 digest in MD5BUFFER. The - caller must allocate MD%buffer wityh at least 16 bytes. Returns 0 - on success. */ + * caller must allocate MD5buffer with at least 16 bytes. Returns 0 + * on success. */ static int hash_dbfile (const char *fname, unsigned char *md5buffer) { -- cgit v1.2.3 From c3f9f2d497b174ceb12c2dde35225a6ba016c835 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 29 Nov 2022 16:47:44 +0100 Subject: wkd: New option --add-revocs and some fixes. * tools/gpg-wks.h (opt): Add add_revocs. * tools/wks-util.c (wks_get_key): Add arg 'binary'. (wks_armor_key): New. (wks_find_add_revocs): New. (wks_cmd_install_key): Get key in binary mode and add revocations if enabled. * tools/gpg-wks-client.c (oAddRevocs): New. (opts): Add --add-revocs. (parse_arguments): Set option, (command_send): Get key in binary mode, add revocations if enabled, and explictly armor key. Remove kludge to skip the Content-type line in no_encrypt mode. (mirror_one_keys_userid): Always filter the key to get rid of the armor as received from dirmngr. Add revocations from the local keyring. -- Note that this also fixes an oddity of the new mirror command which used to store the keys armored as received from dirmngr. --- doc/wks.texi | 8 +++ tools/gpg-wks-client.c | 96 ++++++++++++++++++++++--------- tools/gpg-wks.h | 6 +- tools/wks-util.c | 151 +++++++++++++++++++++++++++++++++++++++++++++++-- 4 files changed, 228 insertions(+), 33 deletions(-) diff --git a/doc/wks.texi b/doc/wks.texi index 73934fb54..a7805a34a 100644 --- a/doc/wks.texi +++ b/doc/wks.texi @@ -213,6 +213,14 @@ operation. The format of @var{file} is one mail address (just the addrspec, e.g. "postel@@isi.edu") per line. Empty lines and lines starting with a '#' are ignored. +@item --add-revocs +@opindex add-revocs +If enabled append revocation certificates for the same addrspec as +used in the WKD to the key. Modern gpg version are able to import and +apply them for existing keys. Note that when used with the +@option{--mirror} command the revocation are searched in the local +keyring and not in an LDAP directory. + @item --verbose @opindex verbose Enable extra informational output. diff --git a/tools/gpg-wks-client.c b/tools/gpg-wks-client.c index f77df8813..c3cb392c4 100644 --- a/tools/gpg-wks-client.c +++ b/tools/gpg-wks-client.c @@ -76,6 +76,7 @@ enum cmd_and_opt_values oWithColons, oBlacklist, oNoAutostart, + oAddRevocs, oDummy }; @@ -102,9 +103,9 @@ static gpgrt_opt_t opts[] = { ARGPARSE_c (aRemoveKey, "remove-key", "remove a key from a directory"), ARGPARSE_c (aPrintWKDHash, "print-wkd-hash", - "Print the WKD identifier for the given user ids"), + "print the WKD identifier for the given user ids"), ARGPARSE_c (aPrintWKDURL, "print-wkd-url", - "Print the WKD URL for the given user id"), + "print the WKD URL for the given user id"), ARGPARSE_group (301, ("@\nOptions:\n ")), @@ -119,6 +120,7 @@ static gpgrt_opt_t opts[] = { ARGPARSE_s_n (oWithColons, "with-colons", "@"), ARGPARSE_s_s (oBlacklist, "blacklist", "@"), ARGPARSE_s_s (oDirectory, "directory", "@"), + ARGPARSE_s_n (oAddRevocs, "add-revocs", "add revocation certificates"), ARGPARSE_s_s (oFakeSubmissionAddr, "fake-submission-addr", "@"), @@ -257,6 +259,9 @@ parse_arguments (gpgrt_argparse_t *pargs, gpgrt_opt_t *popts) case oBlacklist: add_blacklist (pargs->r.ret_str); break; + case oAddRevocs: + opt.add_revocs = 1; + break; case aSupported: case aCreate: @@ -1164,7 +1169,7 @@ command_send (const char *fingerprint, const char *userid) err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; } - err = wks_get_key (&key, fingerprint, addrspec, 0); + err = wks_get_key (&key, fingerprint, addrspec, 0, 1); if (err) goto leave; @@ -1232,7 +1237,7 @@ command_send (const char *fingerprint, const char *userid) estream_t newkey; es_rewind (key); - err = wks_filter_uid (&newkey, key, thisuid->uid, 0); + err = wks_filter_uid (&newkey, key, thisuid->uid, 1); if (err) { log_error ("error filtering key: %s\n", gpg_strerror (err)); @@ -1257,11 +1262,47 @@ command_send (const char *fingerprint, const char *userid) * the key again. */ es_fclose (key); key = NULL; - err = wks_get_key (&key, fingerprint, addrspec, 1); + err = wks_get_key (&key, fingerprint, addrspec, 1, 1); if (err) goto leave; } + if (opt.add_revocs) + { + if (es_fseek (key, 0, SEEK_END)) + { + err = gpg_error_from_syserror (); + log_error ("error seeking stream: %s\n", gpg_strerror (err)); + goto leave; + } + err = wks_find_add_revocs (key, addrspec); + if (err) + { + log_error ("error finding revocations for '%s': %s\n", + addrspec, gpg_strerror (err)); + goto leave; + } + } + + + /* Now put the armor around the key. */ + { + estream_t newkey; + + es_rewind (key); + err = wks_armor_key (&newkey, key, + no_encrypt? NULL + /* */ : ("Content-Type: application/pgp-keys\n" + "\n")); + if (err) + { + log_error ("error armoring key: %s\n", gpg_strerror (err)); + goto leave; + } + es_fclose (key); + key = newkey; + } + /* Hack to support posteo but let them disable this by setting the * new policy-version flag. */ if (policy->protocol_version < 3 @@ -1306,7 +1347,7 @@ command_send (const char *fingerprint, const char *userid) if (no_encrypt) { void *data; - size_t datalen, n; + size_t datalen; if (posteo_hack) { @@ -1331,16 +1372,7 @@ command_send (const char *fingerprint, const char *userid) goto leave; } key = NULL; - /* We need to skip over the first line which has a content-type - * header not needed here. */ - for (n=0; n < datalen ; n++) - if (((const char *)data)[n] == '\n') - { - n++; - break; - } - - err = mime_maker_add_body_data (mime, (char*)data + n, datalen - n); + err = mime_maker_add_body_data (mime, data, datalen); xfree (data); if (err) goto leave; @@ -1827,7 +1859,7 @@ domain_matches_mbox (const char *domain, const char *mbox) /* Core of mirror_one_key with the goal of mirroring just one uid. * UIDLIST is used to figure out whether the given MBOX occurs several - * times in UIDLIST and then to single out the newwest one. This is + * times in UIDLIST and then to single out the newest one. This is * so that for a key with * uid: Joe Someone * uid: Joe @@ -1868,24 +1900,36 @@ mirror_one_keys_userid (estream_t key, const char *mbox, uidinfo_list_t uidlist, err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; } - /* FIXME: Consult blacklist. */ - - /* Only if we have more than one user id we bother to run the - * filter. In this case the result will be put into NEWKEY*/ + /* Always filter the key so that the result will be non-armored. */ es_rewind (key); - if (uidlist->next) + err = wks_filter_uid (&newkey, key, thisuid->uid, 1); + if (err) { - err = wks_filter_uid (&newkey, key, thisuid->uid, 0); + log_error ("error filtering key %s: %s\n", fpr, gpg_strerror (err)); + err = gpg_error (GPG_ERR_NO_PUBKEY); + goto leave; + } + + if (opt.add_revocs) + { + if (es_fseek (newkey, 0, SEEK_END)) + { + err = gpg_error_from_syserror (); + log_error ("error seeking stream: %s\n", gpg_strerror (err)); + goto leave; + } + err = wks_find_add_revocs (newkey, mbox); if (err) { - log_error ("error filtering key %s: %s\n", fpr, gpg_strerror (err)); - err = gpg_error (GPG_ERR_NO_PUBKEY); + log_error ("error finding revocations for '%s': %s\n", + mbox, gpg_strerror (err)); goto leave; } + es_rewind (newkey); } - err = wks_install_key_core (newkey? newkey : key, mbox); + err = wks_install_key_core (newkey, mbox); if (opt.verbose) log_info ("key %s published for '%s'\n", fpr, mbox); mirror_one_key_parm.nuids++; diff --git a/tools/gpg-wks.h b/tools/gpg-wks.h index 32aa8c328..59a0aca74 100644 --- a/tools/gpg-wks.h +++ b/tools/gpg-wks.h @@ -39,6 +39,7 @@ struct int use_sendmail; int with_colons; int no_autostart; + int add_revocs; const char *output; const char *gpg_program; const char *directory; @@ -91,11 +92,14 @@ void wks_set_status_fd (int fd); void wks_write_status (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3); void free_uidinfo_list (uidinfo_list_t list); gpg_error_t wks_get_key (estream_t *r_key, const char *fingerprint, - const char *addrspec, int exact); + const char *addrspec, int exact, int binary); gpg_error_t wks_list_key (estream_t key, char **r_fpr, uidinfo_list_t *r_mboxes); gpg_error_t wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid, int binary); +gpg_error_t wks_armor_key (estream_t *r_newkey, estream_t key, + const char *prefix); +gpg_error_t wks_find_add_revocs (estream_t key, const char *addrspec); gpg_error_t wks_send_mime (mime_maker_t mime); gpg_error_t wks_parse_policy (policy_flags_t flags, estream_t stream, int ignore_unknown); diff --git a/tools/wks-util.c b/tools/wks-util.c index a25ca47a5..1472f7035 100644 --- a/tools/wks-util.c +++ b/tools/wks-util.c @@ -193,10 +193,11 @@ get_key_status_cb (void *opaque, const char *keyword, char *args) * mail address ADDRSPEC is included in the key. If EXACT is set the * returned user id must match Addrspec exactly and not just in the * addr-spec (mailbox) part. The key is returned as a new memory - * stream at R_KEY. */ + * stream at R_KEY. If BINARY is set the returned key is + * non-armored. */ gpg_error_t wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec, - int exact) + int exact, int binary) { gpg_error_t err; ccparray_t ccp; @@ -218,8 +219,9 @@ wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec, } /* Prefix the key with the MIME content type. */ - es_fputs ("Content-Type: application/pgp-keys\n" - "\n", key); + if (!binary) + es_fputs ("Content-Type: application/pgp-keys\n" + "\n", key); filterexp = es_bsprintf ("keep-uid=%s= %s", exact? "uid":"mbox", addrspec); if (!filterexp) @@ -239,7 +241,8 @@ wks_get_key (estream_t *r_key, const char *fingerprint, const char *addrspec, ccparray_put (&ccp, "--batch"); ccparray_put (&ccp, "--status-fd=2"); ccparray_put (&ccp, "--always-trust"); - ccparray_put (&ccp, "--armor"); + if (!binary) + ccparray_put (&ccp, "--armor"); ccparray_put (&ccp, "--export-options=export-minimal"); ccparray_put (&ccp, "--export-filter"); ccparray_put (&ccp, filterexp); @@ -550,6 +553,124 @@ wks_filter_uid (estream_t *r_newkey, estream_t key, const char *uid, } +/* Put the ascii-armor around KEY and return that as a new estream + * object at R_NEWKEY. Caller must make sure that KEY has been seeked + * to the right position (usually by calling es_rewind). The + * resulting NEWKEY has already been rewound. If PREFIX is not NULL, + * its content is written to NEWKEY propr to the armor; this may be + * used for MIME headers. */ +gpg_error_t +wks_armor_key (estream_t *r_newkey, estream_t key, const char *prefix) +{ + gpg_error_t err; + estream_t newkey; + struct b64state b64state; + char buffer[4096]; + size_t nread; + + *r_newkey = NULL; + + newkey = es_fopenmem (0, "w+b"); + if (!newkey) + { + err = gpg_error_from_syserror (); + return err; + } + + if (prefix) + es_fputs (prefix, newkey); + + err = b64enc_start_es (&b64state, newkey, "PGP PUBLIC KEY BLOCK"); + if (err) + goto leave; + + do + { + nread = es_fread (buffer, 1, sizeof buffer, key); + if (!nread) + break; + err = b64enc_write (&b64state, buffer, nread); + if (err) + goto leave; + } + while (!es_feof (key) && !es_ferror (key)); + if (!es_feof (key) || es_ferror (key)) + { + err = gpg_error_from_syserror (); + goto leave; + } + + err = b64enc_finish (&b64state); + if (err) + goto leave; + + es_rewind (newkey); + *r_newkey = newkey; + newkey = NULL; + + leave: + es_fclose (newkey); + return err; +} + + +/* Run gpg to export the revocation certificates for ADDRSPEC. Add + * them to KEY which is expected to be non-armored keyblock. */ +gpg_error_t +wks_find_add_revocs (estream_t key, const char *addrspec) +{ + gpg_error_t err; + ccparray_t ccp; + const char **argv = NULL; + char *filterexp = NULL; + + filterexp = es_bsprintf ("select=mbox= %s", addrspec); + if (!filterexp) + { + err = gpg_error_from_syserror (); + log_error ("error allocating memory buffer: %s\n", gpg_strerror (err)); + goto leave; + } + + ccparray_init (&ccp, 0); + + ccparray_put (&ccp, "--no-options"); + if (opt.verbose < 2) + ccparray_put (&ccp, "--quiet"); + else + ccparray_put (&ccp, "--verbose"); + ccparray_put (&ccp, "--batch"); + ccparray_put (&ccp, "--status-fd=2"); + ccparray_put (&ccp, "--export-options=export-revocs"); + ccparray_put (&ccp, "--export-filter"); + ccparray_put (&ccp, filterexp); + ccparray_put (&ccp, "--export"); + ccparray_put (&ccp, addrspec); + + ccparray_put (&ccp, NULL); + argv = ccparray_get (&ccp, NULL); + if (!argv) + { + err = gpg_error_from_syserror (); + goto leave; + } + debug_gpg_invocation (__func__, argv); + err = gnupg_exec_tool_stream (opt.gpg_program, argv, NULL, + NULL, key, + key_status_cb, NULL); + if (err) + { + log_error ("exporting revocs failed: %s\n", gpg_strerror (err)); + goto leave; + } + + leave: + xfree (filterexp); + xfree (argv); + return err; +} + + /* Helper to write mail to the output(s). */ gpg_error_t wks_send_mime (mime_maker_t mime) @@ -1121,7 +1242,7 @@ wks_cmd_install_key (const char *fname, const char *userid) { /* FNAME looks like a fingerprint. Get the key from the * standard keyring. */ - err = wks_get_key (&fp, fname, addrspec, 0); + err = wks_get_key (&fp, fname, addrspec, 0, 1); if (err) { log_error ("error getting key '%s' (uid='%s'): %s\n", @@ -1193,6 +1314,24 @@ wks_cmd_install_key (const char *fname, const char *userid) fp = fp2; } + if (opt.add_revocs) + { + if (es_fseek (fp, 0, SEEK_END)) + { + err = gpg_error_from_syserror (); + log_error ("error seeking stream: %s\n", gpg_strerror (err)); + goto leave; + } + err = wks_find_add_revocs (fp, addrspec); + if (err) + { + log_error ("error finding revocations for '%s': %s\n", + addrspec, gpg_strerror (err)); + goto leave; + } + es_rewind (fp); + } + err = wks_install_key_core (fp, addrspec); if (!opt.quiet) log_info ("key %s published for '%s'\n", fpr, addrspec); -- cgit v1.2.3 From de0c563f29719d535faccaa67af7f9d5e684ec5a Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 30 Nov 2022 11:47:01 +0900 Subject: doc: Deprecate scd-event option of scdaemon. -- Signed-off-by: NIIBE Yutaka --- doc/scdaemon.texi | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/scdaemon.texi b/doc/scdaemon.texi index b7d83aa89..a1d1cbc08 100644 --- a/doc/scdaemon.texi +++ b/doc/scdaemon.texi @@ -433,7 +433,8 @@ name may be changed on the command line (@pxref{option --options}). @cindex scd-event If this file is present and executable, it will be called on every card reader's status change. An example of this script is provided with the -distribution +source code distribution. This option is deprecated in favor of the +@command{DEVINFO --watch}. @item reader_@var{n}.status This file is created by @command{scdaemon} to let other applications now -- cgit v1.2.3 From 8e8971403f7510f4d7c746fc0ffcdef73c0baf8b Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 30 Nov 2022 15:56:03 +0900 Subject: w32: Fix gnupg_unsetenv. * common/sysutils.c (gnupg_unsetenv): Don't use nonstandard extension of "NAME", but "NAME=". -- Microsoft implementation of putenv works to remove an environment variable by "NAME=". POSIX doesn't say that putenv with "NAME=" has same effect. GNU implementation doesn't support this way for removal of environment variable. Signed-off-by: NIIBE Yutaka --- common/sysutils.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/common/sysutils.c b/common/sysutils.c index ee9a25715..01510ddb0 100644 --- a/common/sysutils.c +++ b/common/sysutils.c @@ -1192,19 +1192,28 @@ gnupg_unsetenv (const char *name) #else /*!HAVE_UNSETENV*/ { char *buf; + int r; if (!name) { gpg_err_set_errno (EINVAL); return -1; } - buf = xtrystrdup (name); + buf = strconcat (name, "=", NULL); if (!buf) return -1; + + r = putenv (buf); +# ifdef HAVE_W32_SYSTEM + /* For Microsoft implementation, we can free the memory in this + use case. */ + xfree (buf); +# else # if __GNUC__ # warning no unsetenv - trying putenv but leaking memory. # endif - return putenv (buf); +# endif + return r; } #endif /*!HAVE_UNSETENV*/ } -- cgit v1.2.3 From 50c65153605042863fda59fe2ec837f1a7b14658 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Wed, 30 Nov 2022 16:50:10 +0900 Subject: w32: Fix for tests on semihosted environment. * Makefile.am (all-local): Make links with EXEEXT. -- Signed-off-by: NIIBE Yutaka --- Makefile.am | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/Makefile.am b/Makefile.am index 76c298c4b..57c60cab4 100644 --- a/Makefile.am +++ b/Makefile.am @@ -131,24 +131,24 @@ all-local: @cat $(srcdir)/tests/gpgconf.ctl.in > bin/gpgconf.ctl (set -e; cd bin; \ for i in gpg gpgv; \ - do ln -sf ../g10/$$i .; done; \ + do ln -sf ../g10/$$i$(EXEEXT) .; done; \ for i in gpgsm; \ - do ln -sf ../sm/$$i .; done; \ + do ln -sf ../sm/$$i$(EXEEXT) .; done; \ for i in gpg-agent; \ - do ln -sf ../agent/$$i .; done; \ + do ln -sf ../agent/$$i$(EXEEXT) .; done; \ for i in dirmngr; \ - do ln -sf ../dirmngr/$$i .; done; \ + do ln -sf ../dirmngr/$$i$(EXEEXT) .; done; \ for i in gpgconf gpg-connect-agent gpgtar gpg-card; \ - do ln -sf ../tools/$$i .; done; \ + do ln -sf ../tools/$$i$(EXEEXT) .; done; \ cd ../libexec ; \ for i in keyboxd; \ - do ln -sf ../kbx/$$i .; done; \ + do ln -sf ../kbx/$$i$(EXEEXT) .; done; \ for i in scdaemon; \ - do ln -sf ../scd/$$i .; done; \ + do ln -sf ../scd/$$i$(EXEEXT) .; done; \ for i in gpg-preset-passphrase; \ - do ln -sf ../agent/$$i .; done; \ + do ln -sf ../agent/$$i$(EXEEXT) .; done; \ for i in tpm2daemon; \ - do [ -f ../tpm2d/$$i ] && ln -sf ../tpm2d/$$i .; done; \ + do [ -f ../tpm2d/$$i$(EXEEXT) ] && ln -sf ../tpm2d/$$i$(EXEEXT) .; done; \ echo "created links to binaries" ) -- cgit v1.2.3 From e1e26a49bf2c8c74360594ced29286c7dca468e9 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 1 Dec 2022 09:21:41 +0900 Subject: tests,w32: Fix for semihosted environment. * tests/cms/Makefile.am (OLD_TESTS_ENVIRONMENT): Add EXEEXT. -- Signed-off-by: NIIBE Yutaka --- tests/cms/Makefile.am | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/cms/Makefile.am b/tests/cms/Makefile.am index 999bd4fb8..44bd1ceab 100644 --- a/tests/cms/Makefile.am +++ b/tests/cms/Makefile.am @@ -34,7 +34,7 @@ AM_CFLAGS = OLD_TESTS_ENVIRONMENT = GNUPGHOME=`/bin/pwd` GPG_AGENT_INFO= LC_ALL=C \ GNUPG_BUILD_ROOT="$(abs_top_builddir)" \ GNUPG_IN_TEST_SUITE=fact \ - GPGSM="$(GPGSM)" "$(srcdir)/runtest" + GPGSM="$(GPGSM)$(EXEEXT)" "$(srcdir)/runtest" TESTS_ENVIRONMENT = LC_ALL=C \ EXEEXT=$(EXEEXT) \ -- cgit v1.2.3 From a9a1ee8726335c28398b41d39a5881c0f9bcb5de Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 1 Dec 2022 10:33:20 +0900 Subject: tests: Fix tests under cms. * tests/cms/all-tests.scm: Remove merge mistake. -- Fixes-commit: 1246e16432b4240ad81c0bd757d7458b609dfd96 Signed-off-by: NIIBE Yutaka --- tests/cms/all-tests.scm | 2 -- 1 file changed, 2 deletions(-) diff --git a/tests/cms/all-tests.scm b/tests/cms/all-tests.scm index 1c04a4a32..6d4c5822a 100644 --- a/tests/cms/all-tests.scm +++ b/tests/cms/all-tests.scm @@ -33,8 +33,6 @@ #f (path-join "tests" "cms" "setup.scm") (in-srcdir "tests" "cms" "setup.scm") - (path-join "tests" "gpgsm" "setup.scm") - (in-srcdir "tests" "gpgsm" "setup.scm") "--" "tests" "gpg"))) (map (lambda (name) -- cgit v1.2.3 From f182c284fb2c093f8dbfc6a4b815762f36cc5217 Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 1 Dec 2022 13:59:06 +0900 Subject: tests: Support semihosted environment. * Makefile.am (check-all): Add EXEEXT. * agent/all-tests.scm: Append EXEEXT. * common/all-tests.scm: Likewise. * g10/all-tests.scm: Likewise. * g13/all-tests.scm: Likewise. -- Signed-off-by: NIIBE Yutaka --- Makefile.am | 2 +- agent/all-tests.scm | 7 ++++--- common/all-tests.scm | 7 ++++--- g10/all-tests.scm | 7 ++++--- g13/all-tests.scm | 7 ++++--- 5 files changed, 17 insertions(+), 13 deletions(-) diff --git a/Makefile.am b/Makefile.am index 57c60cab4..6751f4613 100644 --- a/Makefile.am +++ b/Makefile.am @@ -218,7 +218,7 @@ TESTS_ENVIRONMENT = \ .PHONY: check-all release sign-release check-all: $(TESTS_ENVIRONMENT) \ - $(abs_top_builddir)/tests/gpgscm/gpgscm \ + $(abs_top_builddir)/tests/gpgscm/gpgscm$(EXEEXT) \ $(abs_srcdir)/tests/run-tests.scm $(TESTFLAGS) $(TESTS) # Names of to help the release target. diff --git a/agent/all-tests.scm b/agent/all-tests.scm index 9376adf6e..0d4ddd720 100644 --- a/agent/all-tests.scm +++ b/agent/all-tests.scm @@ -27,9 +27,10 @@ (parse-makefile-expand filename expander key)) (map (lambda (name) - (test::binary #f - (path-join "agent" name) - (path-join (getenv "objdir") "agent" name))) + (let ((name-ext (string-append name (getenv "EXEEXT")))) + (test::binary #f + (path-join "agent" name-ext) + (path-join (getenv "objdir") "agent" name-ext)))) (parse-makefile-expand (in-srcdir "agent" "Makefile.am") (lambda (filename port key) (parse-makefile port key)) "module_tests"))) diff --git a/common/all-tests.scm b/common/all-tests.scm index 54f1153a5..6cbbcbe67 100644 --- a/common/all-tests.scm +++ b/common/all-tests.scm @@ -19,9 +19,10 @@ ;; XXX: Currently, the makefile parser does not understand this ;; Makefile.am, so we hardcode the list of tests here. (map (lambda (name) - (test::binary #f - (path-join "common" name) - (path-join (getenv "objdir") "common" name))) + (let ((name-ext (string-append name (getenv "EXEEXT")))) + (test::binary #f + (path-join "common" name-ext) + (path-join (getenv "objdir") "common" name-ext)))) (list "t-stringhelp" "t-timestuff" "t-convert" diff --git a/g10/all-tests.scm b/g10/all-tests.scm index 982220b28..02fcde7b5 100644 --- a/g10/all-tests.scm +++ b/g10/all-tests.scm @@ -27,9 +27,10 @@ (parse-makefile-expand filename expander key)) (map (lambda (name) - (test::binary #f - (path-join "g10" name) - (path-join (getenv "objdir") "g10" name))) + (let ((name-ext (string-append name (getenv "EXEEXT")))) + (test::binary #f + (path-join "g10" name-ext) + (path-join (getenv "objdir") "g10" name-ext)))) (parse-makefile-expand (in-srcdir "g10" "Makefile.am") (lambda (filename port key) (parse-makefile port key)) "module_tests"))) diff --git a/g13/all-tests.scm b/g13/all-tests.scm index c96f09231..813372358 100644 --- a/g13/all-tests.scm +++ b/g13/all-tests.scm @@ -34,9 +34,10 @@ "g13")))) (if g13-enabled? (map (lambda (name) - (test::binary #f - (path-join "g13" name) - (path-join (getenv "objdir") "g13" name))) + (let ((name-ext (string-append name (getenv "EXEEXT")))) + (test::binary #f + (path-join "g13" name-ext) + (path-join (getenv "objdir") "g13" name-ext)))) (parse-makefile-expand (in-srcdir "g13" "Makefile.am") (lambda (filename port key) (parse-makefile port key)) "module_tests")) -- cgit v1.2.3 From 594c3274d669ddebd145bef33013a66d3e03e27d Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 1 Dec 2022 17:09:45 +0900 Subject: tests: More fix for semihosted environment. * common/all-tests.scm: Conditionalize by *win32*. * tests/cms/Makefile.am (GPGSM): Add missing GPGSM. * tests/pkits/Makefile.am (TESTS_ENVIRONMENT): Add EXEEXT. -- Signed-off-by: NIIBE Yutaka --- common/all-tests.scm | 45 ++++++++++++++++++++++++--------------------- tests/cms/Makefile.am | 2 ++ tests/pkits/Makefile.am | 2 +- 3 files changed, 27 insertions(+), 22 deletions(-) diff --git a/common/all-tests.scm b/common/all-tests.scm index 6cbbcbe67..7ff7d244d 100644 --- a/common/all-tests.scm +++ b/common/all-tests.scm @@ -23,24 +23,27 @@ (test::binary #f (path-join "common" name-ext) (path-join (getenv "objdir") "common" name-ext)))) - (list "t-stringhelp" - "t-timestuff" - "t-convert" - "t-percent" - "t-gettime" - "t-sysutils" - "t-sexputil" - "t-session-env" - "t-openpgp-oid" - "t-ssh-utils" - "t-mapstrings" - "t-zb32" - "t-mbox-util" - "t-iobuf" - "t-strlist" - "t-name-value" - "t-ccparray" - "t-recsel" - "t-exechelp" - "t-exectool" - ))) + `("t-stringhelp" + "t-timestuff" + "t-convert" + "t-percent" + "t-gettime" + "t-sysutils" + "t-sexputil" + "t-session-env" + "t-openpgp-oid" + "t-ssh-utils" + "t-mapstrings" + "t-zb32" + "t-mbox-util" + "t-iobuf" + "t-strlist" + "t-name-value" + "t-ccparray" + "t-recsel" + ,@(if *win32* + '("t-w32-reg" + "t-w32-cmdline") + '("t-exechelp" + "t-exectool")) + ))) diff --git a/tests/cms/Makefile.am b/tests/cms/Makefile.am index 44bd1ceab..60fdf0281 100644 --- a/tests/cms/Makefile.am +++ b/tests/cms/Makefile.am @@ -28,6 +28,8 @@ include $(top_srcdir)/am/cmacros.am AM_CFLAGS = +GPGSM = ../../sm/gpgsm + # Note that we need to use /bin/pwd so that we don't get into trouble # if the shell used for inittests would uses an internal version of # pwd which handles symlinks differently. diff --git a/tests/pkits/Makefile.am b/tests/pkits/Makefile.am index 0b90f86ba..d25dd91a6 100644 --- a/tests/pkits/Makefile.am +++ b/tests/pkits/Makefile.am @@ -23,7 +23,7 @@ GPGSM = ../../sm/gpgsm TESTS_ENVIRONMENT = GNUPGHOME=`/bin/pwd` GPG_AGENT_INFO= LC_ALL=C \ GNUPG_BUILD_ROOT="$(abs_top_builddir)" \ GNUPG_IN_TEST_SUITE=fact \ - GPGSM=$(GPGSM) silent=yes + GPGSM=$(GPGSM)$(EXEEXT) silent=yes testscripts = import-all-certs validate-all-certs \ -- cgit v1.2.3 From 1b434111a18680a97e9997af71c261058cabc5fa Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Thu, 1 Dec 2022 17:11:32 +0900 Subject: tests: Put a workaround for semihosted environment. * tests/openpgp/defs.scm [*win32*]: Use --build-prefix option. -- On the semihosted environment, output of simple gpgconf --list-components includes drive name (like Z:), which results failure of command invocation. This is a workaround. Signed-off-by: NIIBE Yutaka --- tests/openpgp/defs.scm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/openpgp/defs.scm b/tests/openpgp/defs.scm index 5122a8e30..bf3714f50 100644 --- a/tests/openpgp/defs.scm +++ b/tests/openpgp/defs.scm @@ -146,6 +146,9 @@ (gpg-conf' "" args)) (define (gpg-conf' input args) (let ((s (call-popen `(,(tool-hardcoded 'gpgconf) + ,@(if *win32* + (list '--build-prefix (getenv "objdir")) + '()) ,@args) input))) (map (lambda (line) (map percent-decode (string-split line #\:))) (string-split-newlines s)))) -- cgit v1.2.3 From 2d8ac55d26e70f34c75ae892d84db90454b23c79 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 30 Nov 2022 11:00:35 +0100 Subject: gpgsm: Change default algo to AES-256. * sm/gpgsm.c (DEFAULT_CIPHER_ALGO): Change. --- sm/gpgsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sm/gpgsm.c b/sm/gpgsm.c index bc22d68b3..3247a0f2e 100644 --- a/sm/gpgsm.c +++ b/sm/gpgsm.c @@ -516,7 +516,7 @@ static int default_include_certs = DEFAULT_INCLUDE_CERTS; static int default_validation_model; /* The default cipher algo. */ -#define DEFAULT_CIPHER_ALGO "AES" +#define DEFAULT_CIPHER_ALGO "AES256" static char *build_list (const char *text, -- cgit v1.2.3 From e094616cb75d7a4b551ae3db9ea1d5073e3237b6 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 1 Dec 2022 10:10:19 +0100 Subject: agent: Fix import of protected v5 keys. * agent/cvt-openpgp.c (convert_from_openpgp_main): Take care of version 5 keys. -- GnuPG-bug-id: 6294 --- agent/cvt-openpgp.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 76932a7a8..d170fdedc 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -802,9 +802,10 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, if (!list) goto bad_seckey; value = gcry_sexp_nth_data (list, 1, &valuelen); - if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4')) + if (!value || valuelen != 1 + || !(value[0] == '3' || value[0] == '4' || value[0] == '5')) goto bad_seckey; - is_v4 = (value[0] == '4'); + is_v4 = (value[0] == '4' || value[0] == '5'); gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "protection", 0); @@ -948,7 +949,7 @@ convert_from_openpgp_main (ctrl_t ctrl, gcry_sexp_t s_pgp, int dontcare_exist, gcry_sexp_release (top_list); top_list = NULL; #if 0 - log_debug ("XXX is_v4=%d\n", is_v4); + log_debug ("XXX is v4_or_later=%d\n", is_v4); log_debug ("XXX pubkey_algo=%d\n", pubkey_algo); log_debug ("XXX is_protected=%d\n", is_protected); log_debug ("XXX protect_algo=%d\n", protect_algo); -- cgit v1.2.3 From 7663fdd983d2ebc4a4f86e4f2ec6d518e036b90f Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 2 Dec 2022 09:58:51 +0900 Subject: tests: Fix make check-all. * Makefile.am (TESTS_ENVIRONMENT): Add GNUPG_IN_TEST_SUITE. -- Fixes-commit: 50d12860ef21e8480474fd94a0b4465d0339086d Signed-off-by: NIIBE Yutaka --- Makefile.am | 1 + 1 file changed, 1 insertion(+) diff --git a/Makefile.am b/Makefile.am index 6751f4613..796255b5f 100644 --- a/Makefile.am +++ b/Makefile.am @@ -213,6 +213,7 @@ TESTS_ENVIRONMENT = \ abs_top_srcdir=$(abs_top_srcdir) \ objdir=$(abs_top_builddir) \ GNUPG_BUILD_ROOT="$(abs_top_builddir)" \ + GNUPG_IN_TEST_SUITE=fact \ GPGSCM_PATH=$(abs_top_srcdir)/tests/gpgscm .PHONY: check-all release sign-release -- cgit v1.2.3 From 7c6b014d3bc6ea4237dd46c99f3a3d570796851b Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 2 Dec 2022 16:16:07 +0900 Subject: tests: Fix fake-pinentry for Windows. * tests/openpgp/fake-pinentry.c (main): Override PINENTRY_USER_DATA, by the option. -- In the Assuan implementation for Windows, spawn function doesn't call the atfork callback. Thus, the environment variable is not updated by gpg-agent when it spawns pinentry. Reliable way is the interaction to override the option. Signed-off-by: NIIBE Yutaka --- tests/openpgp/fake-pinentry.c | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/openpgp/fake-pinentry.c b/tests/openpgp/fake-pinentry.c index fb0c6ae1b..02c4e0d13 100644 --- a/tests/openpgp/fake-pinentry.c +++ b/tests/openpgp/fake-pinentry.c @@ -201,7 +201,6 @@ main (int argc, char **argv) { char *args; char *option_user_data = NULL; - int got_environment_user_data; char *logfile; char *passphrasefile; char *passphrase; @@ -213,7 +212,6 @@ main (int argc, char **argv) setvbuf (stdout, NULL, _IOLBF, BUFSIZ); args = getenv ("PINENTRY_USER_DATA"); - got_environment_user_data = !!args; if (! args) args = ""; @@ -290,12 +288,7 @@ main (int argc, char **argv) } else if (strncmp (buffer, OPT_USER_DATA, strlen (OPT_USER_DATA)) == 0) { - if (got_environment_user_data) - { - reply ("OK - I already got the data from the environment.\n"); - continue; - } - + /* Prefer interactive data to the one from environment variable. */ if (log_stream) fclose (log_stream); log_stream = NULL; -- cgit v1.2.3 From 0a93b5b96a3bfcca52a8288e9847f06e495b461e Mon Sep 17 00:00:00 2001 From: NIIBE Yutaka Date: Fri, 2 Dec 2022 16:55:49 +0900 Subject: tests: Simplify fake-pinentry to use the option only. * tests/openpgp/fake-pinentry.c (parse_pinentry_user_data): New. (main): Don't use PINENTRY_USER_DATA env var. -- Since environment variable is unreliable, use the option only. Signed-off-by: NIIBE Yutaka --- tests/openpgp/fake-pinentry.c | 72 ++++++++++++++++++++++++------------------- 1 file changed, 41 insertions(+), 31 deletions(-) diff --git a/tests/openpgp/fake-pinentry.c b/tests/openpgp/fake-pinentry.c index 02c4e0d13..18b60574b 100644 --- a/tests/openpgp/fake-pinentry.c +++ b/tests/openpgp/fake-pinentry.c @@ -196,26 +196,20 @@ option_value (const char *line, const char *name) return NULL; } -int -main (int argc, char **argv) +static int +parse_pinentry_user_data (const char *args, + char **r_passphrase) { - char *args; - char *option_user_data = NULL; char *logfile; char *passphrasefile; char *passphrase; - /* We get our options via PINENTRY_USER_DATA. */ - (void) argc, (void) argv; - - setvbuf (stdin, NULL, _IOLBF, BUFSIZ); - setvbuf (stdout, NULL, _IOLBF, BUFSIZ); + *r_passphrase = NULL; - args = getenv ("PINENTRY_USER_DATA"); - if (! args) - args = ""; + if (log_stream) + fclose (log_stream); + log_stream = NULL; - restart: logfile = option_value (args, "--logfile"); if (logfile) { @@ -230,7 +224,7 @@ main (int argc, char **argv) if (! log_stream) { perror (logfile); - return 1; + return -1; } } @@ -249,20 +243,31 @@ main (int argc, char **argv) { reply ("# Passphrasefile '%s' is empty. Terminating.\n", passphrasefile); - return 1; + return -1; } rstrip (passphrase); } else - { - passphrase = skip_options (args); - if (*passphrase == 0) - passphrase = "no PINENTRY_USER_DATA -- using default passphrase"; - } + passphrase = strdup (skip_options (args)); + + *r_passphrase = passphrase; + return 0; +} + - reply ("# fake-pinentry(%u) started. Passphrase='%s'.\n", - (unsigned int)getpid (), passphrase); +int +main (int argc, char **argv) +{ + char *passphrase = NULL; + + /* We get our options via PINENTRY_USER_DATA. */ + (void) argc, (void) argv; + + setvbuf (stdin, NULL, _IOLBF, BUFSIZ); + setvbuf (stdout, NULL, _IOLBF, BUFSIZ); + + reply ("# fake-pinentry(%u) started.\n", (unsigned int)getpid ()); reply ("OK - what's up?\n"); while (! feof (stdin)) @@ -280,7 +285,12 @@ main (int argc, char **argv) #define OPT_USER_DATA "OPTION pinentry-user-data=" if (strncmp (buffer, "GETPIN", 6) == 0) - reply ("D %s\n", passphrase); + { + if (passphrase) + reply ("D %s\n", passphrase); + else + reply ("D deafult\n"); + } else if (strncmp (buffer, "BYE", 3) == 0) { reply ("OK\n"); @@ -288,13 +298,12 @@ main (int argc, char **argv) } else if (strncmp (buffer, OPT_USER_DATA, strlen (OPT_USER_DATA)) == 0) { - /* Prefer interactive data to the one from environment variable. */ - if (log_stream) - fclose (log_stream); - log_stream = NULL; - free (option_user_data); - option_user_data = args = strdup (buffer + strlen (OPT_USER_DATA)); - goto restart; + if (parse_pinentry_user_data (buffer + strlen (OPT_USER_DATA), + &passphrase) < 0) + { + /* Failure. */ + return 1; + } } reply ("OK\n"); @@ -306,6 +315,7 @@ main (int argc, char **argv) if (log_stream) fclose (log_stream); - free (option_user_data); + if (passphrase) + free (passphrase); return 0; } -- cgit v1.2.3 From 1d88e14de751b19ae5637c4f6b0a1882c2f3b554 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Thu, 1 Dec 2022 12:28:15 +0100 Subject: gpg: Remove a mostly duplicated function. * g10/export.c (receive_seckey_from_agent): Add arg r_key. (do_export_one_keyblock): Pass NULL for new arg. (receive_raw_seckey_from_agent): Remove. (export_secret_ssh_key): Use receive_seckey_from_agent. * g10/keygen.c (card_store_key_with_backup): Pass NULL for new arg. --- g10/export.c | 113 ++++++++++++++++++----------------------------------------- g10/keygen.c | 2 +- g10/main.h | 2 +- 3 files changed, 36 insertions(+), 81 deletions(-) diff --git a/g10/export.c b/g10/export.c index 0187788f0..3d4413068 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1249,28 +1249,45 @@ print_status_exported (PKT_public_key *pk) * clear. * * CACHE_NONCE_ADDR is used to share nonce for multiple key retrievals. + * + * If PK is NULL, the raw key is returned (e.g. for ssh export) at + * R_KEY. CLEARTEXT and CACHE_NONCE_ADDR ared ignored in this case. */ gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, - PKT_public_key *pk) + PKT_public_key *pk, gcry_sexp_t *r_key) { gpg_error_t err = 0; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; unsigned char *key = NULL; size_t keylen, realkeylen; - gcry_sexp_t s_skey; + gcry_sexp_t s_skey = NULL; char *prompt; + if (r_key) + *r_key = NULL; if (opt.verbose) log_info ("key %s: asking agent for the secret parts\n", hexgrip); - prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT,1); - err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, cache_nonce_addr, - &wrappedkey, &wrappedkeylen, - pk->keyid, pk->main_keyid, pk->pubkey_algo); + if (pk) + { + prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT, 1); + err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, + cache_nonce_addr, + &wrappedkey, &wrappedkeylen, + pk->keyid, pk->main_keyid, pk->pubkey_algo); + } + else + { + prompt = gpg_format_keydesc (ctrl, NULL, FORMAT_KEYDESC_KEYGRIP, 1); + err = agent_export_key (ctrl, hexgrip, prompt, 0, + NULL, + &wrappedkey, &wrappedkeylen, + NULL, NULL, 0); + } xfree (prompt); if (err) @@ -1297,14 +1314,19 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen); if (!err) { - if (cleartext) + if (pk && cleartext) err = cleartext_secret_key_to_openpgp (s_skey, pk); - else + else if (pk) err = transfer_format_to_openpgp (s_skey, pk); - gcry_sexp_release (s_skey); + else if (r_key) + { + *r_key = s_skey; + s_skey = NULL; + } } unwraperror: + gcry_sexp_release (s_skey); xfree (key); xfree (wrappedkey); if (err) @@ -1811,7 +1833,7 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, { err = receive_seckey_from_agent (ctrl, cipherhd, cleartext, &cache_nonce, - hexgrip, pk); + hexgrip, pk, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) @@ -2714,74 +2736,6 @@ export_ssh_key (ctrl_t ctrl, const char *userid) } -/* Simplified version of receive_seckey_from_agent used to get the raw - * key. */ -gpg_error_t -receive_raw_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, - const char *hexgrip, gcry_sexp_t *r_key) -{ - gpg_error_t err = 0; - unsigned char *wrappedkey = NULL; - size_t wrappedkeylen; - unsigned char *key = NULL; - size_t keylen, realkeylen; - gcry_sexp_t s_skey = NULL; - - *r_key = NULL; - if (opt.verbose) - log_info ("key %s: asking agent for the secret parts\n", hexgrip); - - { - char * prompt = gpg_format_keydesc (ctrl, NULL, FORMAT_KEYDESC_KEYGRIP, 1); - err = agent_export_key (ctrl, hexgrip, prompt, 0, NULL, - &wrappedkey, &wrappedkeylen, - NULL, NULL, 0); - xfree (prompt); - } - if (err) - goto leave; - - if (wrappedkeylen < 24) - { - err = gpg_error (GPG_ERR_INV_LENGTH); - goto leave; - } - keylen = wrappedkeylen - 8; - key = xtrymalloc_secure (keylen); - if (!key) - { - err = gpg_error_from_syserror (); - goto leave; - } - err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen); - if (err) - goto leave; - realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err); - if (!realkeylen) - goto leave; /* Invalid csexp. */ - - err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen); - if (!err) - { - gcry_log_debugsxp ("skey", s_skey); - *r_key = s_skey; - s_skey = NULL; - } - - leave: - gcry_sexp_release (s_skey); - xfree (key); - xfree (wrappedkey); - if (err) - { - log_error ("key %s: error receiving key from agent:" - " %s%s\n", hexgrip, gpg_strerror (err), - ""); - } - return err; -} - - /* Export the key identified by USERID in the SSH secret key format. * The USERID must be given in keygrip format (prefixed with a '&') * and thus no OpenPGP key is required. The exported key is not @@ -2827,7 +2781,8 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid) if ((err = get_keywrap_key (ctrl, &cipherhd))) goto leave; - err = receive_raw_seckey_from_agent (ctrl, cipherhd, hexgrip, &skey); + err = receive_seckey_from_agent (ctrl, cipherhd, 0, NULL, hexgrip, NULL, + &skey); if (err) goto leave; diff --git a/g10/keygen.c b/g10/keygen.c index c2f2dcc9d..8e2c092fc 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -5287,7 +5287,7 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, } err = receive_seckey_from_agent (ctrl, cipherhd, 0, - &cache_nonce, hexgrip, sk); + &cache_nonce, hexgrip, sk, NULL); if (err) { log_error ("error getting secret key from agent: %s\n", diff --git a/g10/main.h b/g10/main.h index 6d5ba77ac..968465ebd 100644 --- a/g10/main.h +++ b/g10/main.h @@ -434,7 +434,7 @@ gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, - PKT_public_key *pk); + PKT_public_key *pk, gcry_sexp_t *r_key); gpg_error_t write_keyblock_to_output (kbnode_t keyblock, int with_armor, unsigned int options); -- cgit v1.2.3 From 1a85ee9a431bd2243e0ad79ce5eefa78e274a491 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 2 Dec 2022 10:03:36 +0100 Subject: gpg: New export option "mode1003". * agent/command.c (cmd_export_key): Add option --mode1003. (command_has_option): Ditto. * g10/build-packet.c (do_key): Implement mode 1003. * g10/parse-packet.c (parse_key): Ditto. * g10/options.h (EXPORT_MODE1003): New.o * g10/call-agent.c (agent_export_key): Add arg mode1003. * g10/export.c (parse_export_options): Add "mode1003" (secret_key_to_mode1003): New. (receive_seckey_from_agent): Add arg mode1003. (do_export_one_keyblock): Pass option down. -- This option allows to export a secret key in GnuPG's native format. Thus no re-encryption is required and further the public key parameters are also authenticated if a protection passphrase has been used. Note that --import is not yet able to handle this new mode. Although old version of GnuPG will bail out with "invalid packet" if a mode1003 exported secret key is seen. --- agent/command.c | 36 +++++++--- doc/DETAILS | 8 +++ doc/gpg.texi | 10 +++ g10/build-packet.c | 24 ++++++- g10/call-agent.c | 20 ++++-- g10/call-agent.h | 2 +- g10/export.c | 202 +++++++++++++++++++++++++++++++++++++++++++++++++++-- g10/keygen.c | 2 +- g10/main.h | 2 +- g10/options.h | 1 + g10/parse-packet.c | 48 +++++++++++-- 11 files changed, 320 insertions(+), 35 deletions(-) diff --git a/agent/command.c b/agent/command.c index 840f9f38e..8b5434bfb 100644 --- a/agent/command.c +++ b/agent/command.c @@ -2935,7 +2935,7 @@ cmd_import_key (assuan_context_t ctx, char *line) static const char hlp_export_key[] = - "EXPORT_KEY [--cache-nonce=] [--openpgp] \n" + "EXPORT_KEY [--cache-nonce=] [--openpgp|--mode1003] \n" "\n" "Export a secret key from the key store. The key will be encrypted\n" "using the current session's key wrapping key (cf. command KEYWRAP_KEY)\n" @@ -2943,9 +2943,10 @@ static const char hlp_export_key[] = "prior to using this command. The function takes the keygrip as argument.\n" "\n" "If --openpgp is used, the secret key material will be exported in RFC 4880\n" - "compatible passphrase-protected form. Without --openpgp, the secret key\n" - "material will be exported in the clear (after prompting the user to unlock\n" - "it, if needed).\n"; + "compatible passphrase-protected form. If --mode1003 is use the secret key\n" + "is exported as s-expression as storred locally. Without those options,\n" + "the secret key material will be exported in the clear (after prompting\n" + "the user to unlock it, if needed).\n"; static gpg_error_t cmd_export_key (assuan_context_t ctx, char *line) { @@ -2958,7 +2959,7 @@ cmd_export_key (assuan_context_t ctx, char *line) gcry_cipher_hd_t cipherhd = NULL; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; - int openpgp; + int openpgp, mode1003; char *cache_nonce; char *passphrase = NULL; unsigned char *shadow_info = NULL; @@ -2969,6 +2970,10 @@ cmd_export_key (assuan_context_t ctx, char *line) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); openpgp = has_option (line, "--openpgp"); + mode1003 = has_option (line, "--mode1003"); + if (mode1003) + openpgp = 0; + cache_nonce = option_value (line, "--cache-nonce"); if (cache_nonce) { @@ -3003,11 +3008,17 @@ cmd_export_key (assuan_context_t ctx, char *line) } /* Get the key from the file. With the openpgp flag we also ask for - the passphrase so that we can use it to re-encrypt it. */ - err = agent_key_from_file (ctrl, cache_nonce, - ctrl->server_local->keydesc, grip, - &shadow_info, CACHE_MODE_IGNORE, NULL, &s_skey, - openpgp ? &passphrase : NULL, NULL); + * the passphrase so that we can use it to re-encrypt it. In + * mode1003 we return the key as-is. FIXME: if the key is still in + * OpenPGP-native mode we should first convert it to our internal + * protection. */ + if (mode1003) + err = agent_raw_key_from_file (ctrl, grip, &s_skey, NULL); + else + err = agent_key_from_file (ctrl, cache_nonce, + ctrl->server_local->keydesc, grip, + &shadow_info, CACHE_MODE_IGNORE, NULL, &s_skey, + openpgp ? &passphrase : NULL, NULL); if (err) goto leave; if (shadow_info) @@ -4150,6 +4161,11 @@ command_has_option (const char *cmd, const char *cmdopt) if (!strcmp (cmdopt, "newsymkey")) return 1; } + else if (!strcmp (cmd, "EXPORT_KEY")) + { + if (!strcmp (cmdopt, "mode1003")) + return 1; + } return 0; } diff --git a/doc/DETAILS b/doc/DETAILS index 9581f3032..a3fe802a2 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1504,6 +1504,14 @@ CREATE TABLE signatures ( - One octet with the length of the following serial number. - The serial number. Regardless of what the length octet indicates no more than 16 octets are stored. + - 3 :: The internal representation of a private key: For v4 keys we + first write 4 octets big endian length of the following + s-expression with the protected or unprotected private key; + for v5 keys this is not necessarily because that length + header is always there. The actual data are N octets of + s-expression. Any protection (including the real S2K) is + part of that data. Note that the public key aparemters are + repeated in th s-expression. Note that gpg stores the GNU S2K Extension Number internally as an S2K Specifier with an offset of 1000. diff --git a/doc/gpg.texi b/doc/gpg.texi index c82a4aa4d..1a3cb9e25 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -2721,6 +2721,16 @@ opposite meaning. The options are: each record to allow diverting the records to the corresponding zone file. + @item mode1003 + Enable the use of a new secret key export format. This format + avoids the re-encryption as required with the current OpenPGP format + and also improves the security of the secret key if it has been + protected with a passphrase. Note that an unprotected key is + exported as-is and thus not secure; the general rule to convey + secret keys in an OpenPGP encrypted file still applies with this + mode. Versions of GnuPG before 2.4.0 are not able to import such a + secret file. + @end table @item --with-colons diff --git a/g10/build-packet.c b/g10/build-packet.c index cc953557d..f33d156b3 100644 --- a/g10/build-packet.c +++ b/g10/build-packet.c @@ -674,7 +674,8 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) count += 8; /* Salt. */ if (ski->s2k.mode == 3) count++; /* S2K.COUNT */ - if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002) + if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002 + && ski->s2k.mode != 1003) count += ski->ivlen; iobuf_put (a, count); @@ -704,8 +705,9 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) if (ski->s2k.mode == 3) iobuf_put (a, ski->s2k.count); - /* For our special modes 1001, 1002 we do not need an IV. */ - if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002) + /* For our special modes 1001..1003 we do not need an IV. */ + if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002 + && ski->s2k.mode != 1003) iobuf_write (a, ski->iv, ski->ivlen); } @@ -733,6 +735,22 @@ do_key (iobuf_t out, int ctb, PKT_public_key *pk) /* The serial number gets stored in the IV field. */ iobuf_write (a, ski->iv, ski->ivlen); } + else if (ski->s2k.mode == 1003) + { + /* GnuPG extension - Store raw s-expression. */ + byte *p; + unsigned int ndatabits; + + log_assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE)); + + p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits); + /* For v5 keys we first write the number of octets of the + * following key material. */ + if (is_v5) + write_32 (a, p? (ndatabits+7)/8 : 0); + if (p) + iobuf_write (a, p, (ndatabits+7)/8 ); + } else if (ski->is_protected) { /* The secret key is protected - write it out as it is. */ diff --git a/g10/call-agent.c b/g10/call-agent.c index 27b5cacfb..66812e998 100644 --- a/g10/call-agent.c +++ b/g10/call-agent.c @@ -2997,13 +2997,15 @@ agent_import_key (ctrl_t ctrl, const char *desc, char **cache_nonce_addr, keygrip, DESC a prompt to be displayed with the agent's passphrase question (needs to be plus+percent escaped). if OPENPGP_PROTECTED is not zero, ensure that the key material is returned in RFC - 4880-compatible passphrased-protected form. If CACHE_NONCE_ADDR is - not NULL the agent is advised to first try a passphrase associated - with that nonce. On success the key is stored as a canonical - S-expression at R_RESULT and R_RESULTLEN. */ + 4880-compatible passphrased-protected form; if instead MODE1003 is + not zero the raw gpg-agent private key format is requested (either + protected or unprotected). If CACHE_NONCE_ADDR is not NULL the + agent is advised to first try a passphrase associated with that + nonce. On success the key is stored as a canonical S-expression at + R_RESULT and R_RESULTLEN. */ gpg_error_t agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, - int openpgp_protected, char **cache_nonce_addr, + int openpgp_protected, int mode1003, char **cache_nonce_addr, unsigned char **r_result, size_t *r_resultlen, u32 *keyid, u32 *mainkeyid, int pubkey_algo) { @@ -3028,6 +3030,12 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, return err; dfltparm.ctx = agent_ctx; + /* Check that the gpg-agent supports the --mode1003 option. */ + if (mode1003 && assuan_transact (agent_ctx, + "GETINFO cmd_has_option EXPORT_KEY mode1003", + NULL, NULL, NULL, NULL, NULL, NULL)) + return gpg_error (GPG_ERR_NOT_SUPPORTED); + if (desc) { snprintf (line, DIM(line), "SETKEYDESC %s", desc); @@ -3038,7 +3046,7 @@ agent_export_key (ctrl_t ctrl, const char *hexkeygrip, const char *desc, } snprintf (line, DIM(line), "EXPORT_KEY %s%s%s %s", - openpgp_protected ? "--openpgp ":"", + mode1003? "--mode1003" : openpgp_protected ? "--openpgp ":"", cache_nonce_addr && *cache_nonce_addr? "--cache-nonce=":"", cache_nonce_addr && *cache_nonce_addr? *cache_nonce_addr:"", hexkeygrip); diff --git a/g10/call-agent.h b/g10/call-agent.h index a4cbc3162..a3f234ade 100644 --- a/g10/call-agent.h +++ b/g10/call-agent.h @@ -231,7 +231,7 @@ gpg_error_t agent_import_key (ctrl_t ctrl, const char *desc, /* Receive a key from the agent. */ gpg_error_t agent_export_key (ctrl_t ctrl, const char *keygrip, const char *desc, int openpgp_protected, - char **cache_nonce_addr, + int mode1003, char **cache_nonce_addr, unsigned char **r_result, size_t *r_resultlen, u32 *keyid, u32 *mainkeyid, int pubkey_algo); diff --git a/g10/export.c b/g10/export.c index 3d4413068..cab00d10c 100644 --- a/g10/export.c +++ b/g10/export.c @@ -2,6 +2,7 @@ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, * 2005, 2010 Free Software Foundation, Inc. * Copyright (C) 1998-2016 Werner Koch + * Copyright (C) 2022 g10 Code GmbH * * This file is part of GnuPG. * @@ -140,6 +141,9 @@ parse_export_options(char *str,unsigned int *options,int noisy) N_("use the GnuPG key backup format")}, {"export-backup", EXPORT_BACKUP, NULL, NULL }, + {"mode1003", EXPORT_MODE1003, NULL, + N_("export secret keys using the GnuPG format") }, + /* Aliases for backward compatibility */ {"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL}, {"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL}, @@ -639,6 +643,183 @@ canon_pk_algo (enum gcry_pk_algos algo) } +/* Take an s-expression wit the public and private key and change the + * parameter array in PK to include the secret parameters. */ +static gpg_error_t +secret_key_to_mode1003 (gcry_sexp_t s_key, PKT_public_key *pk) +{ + gpg_error_t err; + gcry_sexp_t list = NULL; + gcry_sexp_t l2; + enum gcry_pk_algos pk_algo; + struct seckey_info *ski; + int idx; + char *string; + size_t npkey, nskey; + gcry_mpi_t pub_params[10] = { NULL }; + + /* We look for a private-key, then the first element in it tells us + the type */ + list = gcry_sexp_find_token (s_key, "protected-private-key", 0); + if (!list) + list = gcry_sexp_find_token (s_key, "private-key", 0); + if (!list) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + + log_assert (!pk->seckey_info); + + /* Parse the gcrypt PK algo and check that it is okay. */ + l2 = gcry_sexp_cadr (list); + if (!l2) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + gcry_sexp_release (list); + list = l2; + string = gcry_sexp_nth_string (list, 0); + if (!string) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + pk_algo = gcry_pk_map_name (string); + xfree (string); string = NULL; + if (gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NPKEY, NULL, &npkey) + || gcry_pk_algo_info (pk_algo, GCRYCTL_GET_ALGO_NSKEY, NULL, &nskey) + || !npkey || npkey >= nskey) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + + /* Check that the pubkey algo and the received parameters matches + * those from the public key. */ + switch (canon_pk_algo (pk_algo)) + { + case GCRY_PK_RSA: + if (!is_RSA (pk->pubkey_algo) || npkey != 2) + err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */ + else + err = gcry_sexp_extract_param (list, NULL, "ne", + &pub_params[0], + &pub_params[1], + NULL); + break; + + case GCRY_PK_DSA: + if (!is_DSA (pk->pubkey_algo) || npkey != 4) + err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */ + else + err = gcry_sexp_extract_param (list, NULL, "pqgy", + &pub_params[0], + &pub_params[1], + &pub_params[2], + &pub_params[3], + NULL); + break; + + case GCRY_PK_ELG: + if (!is_ELGAMAL (pk->pubkey_algo) || npkey != 3) + err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */ + else + err = gcry_sexp_extract_param (list, NULL, "pgy", + &pub_params[0], + &pub_params[1], + &pub_params[2], + NULL); + break; + + case GCRY_PK_ECC: + err = 0; + if (!(pk->pubkey_algo == PUBKEY_ALGO_ECDSA + || pk->pubkey_algo == PUBKEY_ALGO_ECDH + || pk->pubkey_algo == PUBKEY_ALGO_EDDSA)) + { + err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Does not match. */ + goto leave; + } + npkey = 2; + if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) + npkey++; + /* Dedicated check of the curve. */ + pub_params[0] = NULL; + err = match_curve_skey_pk (list, pk); + if (err) + goto leave; + /* ... and of the Q parameter. */ + err = sexp_extract_param_sos (list, "q", &pub_params[1]); + if (!err && (gcry_mpi_cmp (pk->pkey[1], pub_params[1]))) + err = gpg_error (GPG_ERR_BAD_PUBKEY); + break; + + default: + err = gpg_error (GPG_ERR_PUBKEY_ALGO); /* Unknown. */ + break; + } + if (err) + goto leave; + + nskey = npkey + 1; /* We only have one skey param. */ + if (nskey > PUBKEY_MAX_NSKEY) + { + err = gpg_error (GPG_ERR_BAD_SECKEY); + goto leave; + } + + /* Check that the public key parameters match. For ECC we already + * did this in the switch above. */ + if (canon_pk_algo (pk_algo) != GCRY_PK_ECC) + { + for (idx=0; idx < npkey; idx++) + if (gcry_mpi_cmp (pk->pkey[idx], pub_params[idx])) + { + err = gpg_error (GPG_ERR_BAD_PUBKEY); + goto leave; + } + } + + /* Store the maybe protected secret key as an s-expression. */ + pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); + if (!ski) + { + err = gpg_error_from_syserror (); + goto leave; + } + + pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); + if (!ski) + { + err = gpg_error_from_syserror (); + goto leave; + } + + ski->is_protected = 1; + ski->s2k.mode = 1003; + + { + unsigned char *buf; + size_t buflen; + + err = make_canon_sexp (s_key, &buf, &buflen); + if (err) + goto leave; + pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, buf, buflen*8); + for (idx=npkey+1; idx < PUBKEY_MAX_NSKEY; idx++) + pk->pkey[idx] = NULL; + } + + leave: + gcry_sexp_release (list); + for (idx=0; idx < DIM(pub_params); idx++) + gcry_mpi_release (pub_params[idx]); + return err; +} + + /* Take a cleartext dump of a secret key in PK and change the * parameter array in PK to include the secret parameters. */ static gpg_error_t @@ -1248,6 +1429,11 @@ print_status_exported (PKT_public_key *pk) * passphrase-protected. Otherwise, store secret key material in the * clear. * + * If MODE1003 is set, the key is requested in raw GnuPG format from + * the agent. This usually does not require a passphrase unless the + * gpg-agent has not yet used the key and needs to convert it to its + * internal format first. + * * CACHE_NONCE_ADDR is used to share nonce for multiple key retrievals. * * If PK is NULL, the raw key is returned (e.g. for ssh export) at @@ -1255,7 +1441,7 @@ print_status_exported (PKT_public_key *pk) */ gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, - int cleartext, + int cleartext, int mode1003, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk, gcry_sexp_t *r_key) { @@ -1275,7 +1461,7 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, if (pk) { prompt = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_EXPORT, 1); - err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, + err = agent_export_key (ctrl, hexgrip, prompt, !cleartext, mode1003, cache_nonce_addr, &wrappedkey, &wrappedkeylen, pk->keyid, pk->main_keyid, pk->pubkey_algo); @@ -1283,7 +1469,7 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, else { prompt = gpg_format_keydesc (ctrl, NULL, FORMAT_KEYDESC_KEYGRIP, 1); - err = agent_export_key (ctrl, hexgrip, prompt, 0, + err = agent_export_key (ctrl, hexgrip, prompt, 0, 0, NULL, &wrappedkey, &wrappedkeylen, NULL, NULL, 0); @@ -1314,7 +1500,9 @@ receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen); if (!err) { - if (pk && cleartext) + if (pk && mode1003) + err = secret_key_to_mode1003 (s_skey, pk); + else if (pk && cleartext) err = cleartext_secret_key_to_openpgp (s_skey, pk); else if (pk) err = transfer_format_to_openpgp (s_skey, pk); @@ -1832,7 +2020,9 @@ do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, else if (!err) { err = receive_seckey_from_agent (ctrl, cipherhd, - cleartext, &cache_nonce, + cleartext, + !!(options & EXPORT_MODE1003), + &cache_nonce, hexgrip, pk, NULL); if (err) { @@ -2781,7 +2971,7 @@ export_secret_ssh_key (ctrl_t ctrl, const char *userid) if ((err = get_keywrap_key (ctrl, &cipherhd))) goto leave; - err = receive_seckey_from_agent (ctrl, cipherhd, 0, NULL, hexgrip, NULL, + err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0, NULL, hexgrip, NULL, &skey); if (err) goto leave; diff --git a/g10/keygen.c b/g10/keygen.c index 8e2c092fc..4dcf7a494 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -5286,7 +5286,7 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, goto leave; } - err = receive_seckey_from_agent (ctrl, cipherhd, 0, + err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0, &cache_nonce, hexgrip, sk, NULL); if (err) { diff --git a/g10/main.h b/g10/main.h index 968465ebd..62d2651be 100644 --- a/g10/main.h +++ b/g10/main.h @@ -431,7 +431,7 @@ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, void **r_data, size_t *r_datalen); gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, - int cleartext, + int cleartext, int mode1003, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk, gcry_sexp_t *r_key); diff --git a/g10/options.h b/g10/options.h index ed4865168..c10862687 100644 --- a/g10/options.h +++ b/g10/options.h @@ -407,6 +407,7 @@ EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; #define EXPORT_DANE_FORMAT (1<<7) #define EXPORT_BACKUP (1<<10) #define EXPORT_REVOCS (1<<11) +#define EXPORT_MODE1003 (1<<12) #define LIST_SHOW_PHOTOS (1<<0) #define LIST_SHOW_POLICY_URLS (1<<1) diff --git a/g10/parse-packet.c b/g10/parse-packet.c index b6aebbb69..a033732ec 100644 --- a/g10/parse-packet.c +++ b/g10/parse-packet.c @@ -2752,11 +2752,15 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, break; case 1001: if (list_mode) - es_fprintf (listfp, "\tgnu-dummy S2K"); + es_fprintf (listfp, "\tgnu-dummy"); break; case 1002: if (list_mode) - es_fprintf (listfp, "\tgnu-divert-to-card S2K"); + es_fprintf (listfp, "\tgnu-divert-to-card"); + break; + case 1003: + if (list_mode) + es_fprintf (listfp, "\tgnu-mode1003"); break; default: if (list_mode) @@ -2768,7 +2772,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, } /* Print some info. */ - if (list_mode) + if (list_mode && ski->s2k.mode != 1003) { es_fprintf (listfp, ", algo: %d,%s hash: %d", ski->algo, @@ -2779,8 +2783,9 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, es_fprintf (listfp, ", salt: "); es_write_hexstring (listfp, ski->s2k.salt, 8, 0, NULL); } - es_putc ('\n', listfp); - } + } + if (list_mode) + es_putc ('\n', listfp); /* Read remaining protection parameters. */ if (ski->s2k.mode == 3) @@ -2838,7 +2843,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, ski->ivlen = openpgp_cipher_blocklen (ski->algo); log_assert (ski->ivlen <= sizeof (temp)); - if (ski->s2k.mode == 1001) + if (ski->s2k.mode == 1001 || ski->s2k.mode == 1003) ski->ivlen = 0; else if (ski->s2k.mode == 1002) ski->ivlen = snlen < 16 ? snlen : 16; @@ -2850,7 +2855,7 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, } for (i = 0; i < ski->ivlen; i++, pktlen--) temp[i] = iobuf_get_noeof (inp); - if (list_mode) + if (list_mode && ski->s2k.mode != 1003) { es_fprintf (listfp, ski->s2k.mode == 1002 ? "\tserial-number: " @@ -2888,6 +2893,35 @@ parse_key (IOBUF inp, int pkttype, unsigned long pktlen, 10 * 8); pktlen = 0; } + else if (ski->s2k.mode == 1003) + { + void *tmpp; + + if (pktlen < 2) /* At least two bytes for parenthesis. */ + { + err = gpg_error (GPG_ERR_INV_PACKET); + goto leave; + } + + tmpp = read_rest (inp, pktlen); + if (list_mode) + { + if (mpi_print_mode) + { + char *tmpsxp = canon_sexp_to_string (tmpp, pktlen); + es_fprintf (listfp, "\tskey[%d]: %s\n", npkey, + tmpsxp? trim_trailing_spaces (tmpsxp) + /* */: "[invalid S-expression]"); + xfree (tmpsxp); + } + else + es_fprintf (listfp, "\tskey[%d]: [s-expression %lu octets]\n", + npkey, pktlen); + } + pk->pkey[npkey] = gcry_mpi_set_opaque (NULL, + tmpp, tmpp? pktlen * 8 : 0); + pktlen = 0; + } else if (ski->is_protected) { void *tmpp; -- cgit v1.2.3