diff options
author | NIIBE Yutaka <[email protected]> | 2022-12-05 03:01:06 +0000 |
---|---|---|
committer | NIIBE Yutaka <[email protected]> | 2022-12-05 03:01:06 +0000 |
commit | ac87fc1a9b423256cef246aa82ee30d6fb951ffc (patch) | |
tree | acfd9504052fe0189f65a0ebcc2d3f378b3f0143 | |
parent | w32: Fix spawn functions for Windows for _fd use case. (diff) | |
parent | gpg: New export option "mode1003". (diff) | |
download | gnupg-ac87fc1a9b423256cef246aa82ee30d6fb951ffc.tar.gz gnupg-ac87fc1a9b423256cef246aa82ee30d6fb951ffc.zip |
Merge branch 'master' into gniibe/t6275
50 files changed, 1119 insertions, 288 deletions
diff --git a/Makefile.am b/Makefile.am index 76c298c4b..796255b5f 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" ) @@ -213,12 +213,13 @@ 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 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/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=<nonce>] [--openpgp] <hexstring_with_keygrip>\n" + "EXPORT_KEY [--cache-nonce=<nonce>] [--openpgp|--mode1003] <hexkeygrip>\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/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); diff --git a/common/all-tests.scm b/common/all-tests.scm index 54f1153a5..7ff7d244d 100644 --- a/common/all-tests.scm +++ b/common/all-tests.scm @@ -19,27 +19,31 @@ ;; 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))) - (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" - ))) + (let ((name-ext (string-append name (getenv "EXEEXT")))) + (test::binary #f + (path-join "common" name-ext) + (path-join (getenv "objdir") "common" name-ext)))) + `("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/common/sysutils.c b/common/sysutils.c index bbed309a8..01510ddb0 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, @@ -1195,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*/ } 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) { 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..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. @@ -1694,6 +1702,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/doc/gpg.texi b/doc/gpg.texi index 25065f8e4..1a3cb9e25 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} @@ -2673,12 +2710,27 @@ 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 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/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 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/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/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/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; } diff --git a/g10/export.c b/g10/export.c index edf27bc4d..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. * @@ -63,15 +64,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 +84,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 +109,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,10 +134,16 @@ 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 }, + {"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}, @@ -184,6 +196,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 +211,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 +233,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 +253,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; } @@ -624,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 @@ -1233,29 +1429,51 @@ 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 + * 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, + int cleartext, int mode1003, 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, mode1003, + 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, 0, + NULL, + &wrappedkey, &wrappedkeylen, + NULL, NULL, 0); + } xfree (prompt); if (err) @@ -1282,14 +1500,21 @@ 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 && mode1003) + err = secret_key_to_mode1003 (s_skey, pk); + else 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) @@ -1795,8 +2020,10 @@ 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, - hexgrip, pk); + cleartext, + !!(options & EXPORT_MODE1003), + &cache_nonce, + hexgrip, pk, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) @@ -1872,6 +2099,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 +2365,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 +2407,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; @@ -2602,74 +2926,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 @@ -2715,7 +2971,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, 0, NULL, hexgrip, NULL, + &skey); if (err) goto leave; @@ -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/keygen.c b/g10/keygen.c index c2f2dcc9d..4dcf7a494 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -5286,8 +5286,8 @@ card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, goto leave; } - err = receive_seckey_from_agent (ctrl, cipherhd, 0, - &cache_nonce, hexgrip, sk); + err = receive_seckey_from_agent (ctrl, cipherhd, 0, 0, + &cache_nonce, hexgrip, sk, NULL); if (err) { log_error ("error getting secret key from agent: %s\n", 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..62d2651be 100644 --- a/g10/main.h +++ b/g10/main.h @@ -431,10 +431,10 @@ 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); + 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); @@ -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/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. */ diff --git a/g10/options.h b/g10/options.h index b439ef120..c10862687 100644 --- a/g10/options.h +++ b/g10/options.h @@ -406,6 +406,8 @@ 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 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; 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; +} 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")) 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) 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, 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); diff --git a/tests/cms/Makefile.am b/tests/cms/Makefile.am index 999bd4fb8..60fdf0281 100644 --- a/tests/cms/Makefile.am +++ b/tests/cms/Makefile.am @@ -28,13 +28,15 @@ 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. 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) \ diff --git a/tests/cms/all-tests.scm b/tests/cms/all-tests.scm index 78ad00678..6d4c5822a 100644 --- a/tests/cms/all-tests.scm +++ b/tests/cms/all-tests.scm @@ -30,12 +30,14 @@ (make-environment-cache (test::scm #f + #f (path-join "tests" "cms" "setup.scm") (in-srcdir "tests" "cms" "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 @@ -28,6 +28,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 @@ -40,6 +40,7 @@ (make-environment-cache (test::scm #f + #f (path-join "tests" "gpgme" "setup.scm" "tests" "gpg") (in-srcdir "tests" "gpgme" "setup.scm") "--" "tests" "gpg"))) @@ -47,6 +48,7 @@ (make-environment-cache (test::scm #f + #f (path-join "tests" "gpgme" "setup.scm" "lang" "python" "tests") (in-srcdir "tests" "gpgme" "setup.scm") "--" "lang" "python" "tests"))) @@ -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/ffi.c b/tests/gpgscm/ffi.c index 58659c1b8..879a05e09 100644 --- a/tests/gpgscm/ffi.c +++ b/tests/gpgscm/ffi.c @@ -360,6 +360,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"); diff --git a/tests/gpgscm/tests.scm b/tests/gpgscm/tests.scm index 4311b38ca..6a11e55f1 100644 --- a/tests/gpgscm/tests.scm +++ b/tests/gpgscm/tests.scm @@ -677,14 +677,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) (process-spawn-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. @@ -692,9 +692,9 @@ (process-spawn-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 proc retcode logfd expect-failure) + (define (new variant name directory spawn proc retcode logfd expect-failure) (package ;; XXX: OO glue. @@ -723,7 +723,11 @@ (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") + (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)) @@ -772,7 +776,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/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 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 @@ -30,6 +30,7 @@ (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/defs.scm b/tests/openpgp/defs.scm index 57174b56e..1ac25bf65 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)))) diff --git a/tests/openpgp/fake-pinentry.c b/tests/openpgp/fake-pinentry.c index fb0c6ae1b..18b60574b 100644 --- a/tests/openpgp/fake-pinentry.c +++ b/tests/openpgp/fake-pinentry.c @@ -196,28 +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; - int got_environment_user_data; 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"); - got_environment_user_data = !!args; - if (! args) - args = ""; + if (log_stream) + fclose (log_stream); + log_stream = NULL; - restart: logfile = option_value (args, "--logfile"); if (logfile) { @@ -232,7 +224,7 @@ main (int argc, char **argv) if (! log_stream) { perror (logfile); - return 1; + return -1; } } @@ -251,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)) @@ -282,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"); @@ -290,18 +298,12 @@ main (int argc, char **argv) } else if (strncmp (buffer, OPT_USER_DATA, strlen (OPT_USER_DATA)) == 0) { - if (got_environment_user_data) + if (parse_pinentry_user_data (buffer + strlen (OPT_USER_DATA), + &passphrase) < 0) { - reply ("OK - I already got the data from the environment.\n"); - continue; + /* Failure. */ + return 1; } - - 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; } reply ("OK\n"); @@ -313,6 +315,7 @@ main (int argc, char **argv) if (log_stream) fclose (log_stream); - free (option_user_data); + if (passphrase) + free (passphrase); return 0; } 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")) 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 @@ -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")))) @@ -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)))) 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 \ 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 <[email protected]> * uid: Joe <[email protected]> @@ -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 a1640d772..1472f7035 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++; @@ -177,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; @@ -202,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) @@ -223,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); @@ -239,6 +258,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 +352,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 +531,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); @@ -531,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) @@ -1102,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", @@ -1174,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); |