From e7fd617327ed9d30f093a78a016511ab5c984ba4 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 18 May 2024 23:21:05 +0200 Subject: Merging upstream version 2.2.43. Signed-off-by: Daniel Baumann --- agent/Makefile.am | 11 +- agent/Makefile.in | 32 +-- agent/agent.h | 25 +- agent/call-pinentry.c | 22 +- agent/call-scd.c | 2 + agent/command-ssh.c | 6 +- agent/command.c | 211 ++++++++------ agent/cvt-openpgp.c | 6 +- agent/findkey.c | 595 ++++++++++++++++++---------------------- agent/genkey.c | 6 +- agent/gpg-agent.c | 16 +- agent/gpg-agent.w32-manifest.in | 9 +- agent/learncard.c | 11 +- agent/pkdecrypt.c | 2 +- agent/pksign.c | 2 +- agent/protect-tool.c | 13 +- agent/protect.c | 154 +++-------- agent/t-protect.c | 2 +- agent/trustlist.c | 27 +- 19 files changed, 523 insertions(+), 629 deletions(-) (limited to 'agent') diff --git a/agent/Makefile.am b/agent/Makefile.am index f0ba964..ad7e967 100644 --- a/agent/Makefile.am +++ b/agent/Makefile.am @@ -1,3 +1,4 @@ +# Makefile.am - agent # Copyright (C) 2001, 2003, 2004, 2005 Free Software Foundation, Inc. # # This file is part of GnuPG. @@ -35,10 +36,10 @@ AM_CPPFLAGS = include $(top_srcdir)/am/cmacros.am if HAVE_W32_SYSTEM -gpg_agent_robjs = $(resource_objs) gpg-agent-w32info.o -gpg-agent-w32info.o : gpg-agent.w32-manifest +gpg_agent_rc_objs = $(resource_objs) gpg-agent-w32info.o +gpg-agent-w32info.o : gpg-agent.w32-manifest ../common/w32info-rc.h else -gpg_agent_robjs = +gpg_agent_rc_objs = endif AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) @@ -74,9 +75,9 @@ gpg_agent_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) \ gpg_agent_LDADD = $(commonpth_libs) \ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ - $(gpg_agent_robjs) + $(gpg_agent_rc_objs) gpg_agent_LDFLAGS = $(extra_bin_ldflags) -gpg_agent_DEPENDENCIES = $(gpg_agent_robjs) +gpg_agent_DEPENDENCIES = $(gpg_agent_rc_objs) gpg_protect_tool_SOURCES = \ protect-tool.c \ diff --git a/agent/Makefile.in b/agent/Makefile.in index ab8735f..9ec037c 100644 --- a/agent/Makefile.in +++ b/agent/Makefile.in @@ -14,6 +14,7 @@ @SET_MAKE@ +# Makefile.am - agent # Copyright (C) 2001, 2003, 2004, 2005 Free Software Foundation, Inc. # # This file is part of GnuPG. @@ -151,17 +152,16 @@ ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \ $(top_srcdir)/m4/gpg-error.m4 $(top_srcdir)/m4/iconv.m4 \ - $(top_srcdir)/m4/isc-posix.m4 $(top_srcdir)/m4/ksba.m4 \ - $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \ - $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \ - $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libassuan.m4 \ - $(top_srcdir)/m4/libgcrypt.m4 $(top_srcdir)/m4/nls.m4 \ - $(top_srcdir)/m4/npth.m4 $(top_srcdir)/m4/ntbtls.m4 \ - $(top_srcdir)/m4/pkg.m4 $(top_srcdir)/m4/po.m4 \ - $(top_srcdir)/m4/progtest.m4 $(top_srcdir)/m4/readline.m4 \ - $(top_srcdir)/m4/socklen.m4 $(top_srcdir)/m4/sys_socket_h.m4 \ - $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/acinclude.m4 \ - $(top_srcdir)/configure.ac + $(top_srcdir)/m4/ksba.m4 $(top_srcdir)/m4/lcmessage.m4 \ + $(top_srcdir)/m4/ldap.m4 $(top_srcdir)/m4/lib-ld.m4 \ + $(top_srcdir)/m4/lib-link.m4 $(top_srcdir)/m4/lib-prefix.m4 \ + $(top_srcdir)/m4/libassuan.m4 $(top_srcdir)/m4/libgcrypt.m4 \ + $(top_srcdir)/m4/nls.m4 $(top_srcdir)/m4/npth.m4 \ + $(top_srcdir)/m4/ntbtls.m4 $(top_srcdir)/m4/pkg.m4 \ + $(top_srcdir)/m4/po.m4 $(top_srcdir)/m4/progtest.m4 \ + $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/socklen.m4 \ + $(top_srcdir)/m4/sys_socket_h.m4 $(top_srcdir)/m4/tar-ustar.m4 \ + $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ $(ACLOCAL_M4) DIST_COMMON = $(srcdir)/Makefile.am $(am__DIST_COMMON) @@ -533,8 +533,8 @@ libcommon = ../common/libcommon.a libcommonpth = ../common/libcommonpth.a libcommontls = ../common/libcommontls.a libcommontlsnpth = ../common/libcommontlsnpth.a -@HAVE_W32_SYSTEM_FALSE@gpg_agent_robjs = -@HAVE_W32_SYSTEM_TRUE@gpg_agent_robjs = $(resource_objs) gpg-agent-w32info.o +@HAVE_W32_SYSTEM_FALSE@gpg_agent_rc_objs = +@HAVE_W32_SYSTEM_TRUE@gpg_agent_rc_objs = $(resource_objs) gpg-agent-w32info.o AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(GPG_ERROR_CFLAGS) gpg_agent_SOURCES = \ gpg-agent.c agent.h \ @@ -563,10 +563,10 @@ gpg_agent_CFLAGS = $(AM_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) \ gpg_agent_LDADD = $(commonpth_libs) \ $(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \ $(GPG_ERROR_LIBS) $(LIBINTL) $(NETLIBS) $(LIBICONV) \ - $(gpg_agent_robjs) + $(gpg_agent_rc_objs) gpg_agent_LDFLAGS = $(extra_bin_ldflags) -gpg_agent_DEPENDENCIES = $(gpg_agent_robjs) +gpg_agent_DEPENDENCIES = $(gpg_agent_rc_objs) gpg_protect_tool_SOURCES = \ protect-tool.c \ protect.c cvt-openpgp.c @@ -1397,7 +1397,7 @@ uninstall-am: uninstall-binPROGRAMS uninstall-libexecPROGRAMS @HAVE_W32_SYSTEM_TRUE@.rc.o: @HAVE_W32_SYSTEM_TRUE@ $(WINDRES) $(DEFAULT_INCLUDES) $(INCLUDES) "$<" "$@" -@HAVE_W32_SYSTEM_TRUE@gpg-agent-w32info.o : gpg-agent.w32-manifest +@HAVE_W32_SYSTEM_TRUE@gpg-agent-w32info.o : gpg-agent.w32-manifest ../common/w32info-rc.h # Make sure that all libs are build before we use them. This is # important for things like make -j2. diff --git a/agent/agent.h b/agent/agent.h index 56e13ec..d32b892 100644 --- a/agent/agent.h +++ b/agent/agent.h @@ -132,13 +132,6 @@ struct passphrase change. */ int enable_passphrase_history; - /* If set the extended key format is used for new keys. Note that - * this may have the value 2 in which case - * --disable-extended-key-format won't have any effect and thus - * effectivley locking it. This is required to support existing - * profiles which lock the use of --enable-extended-key-format. */ - int enable_extended_key_format; - int running_detached; /* We are running detached from the tty. */ /* If this global option is true, the passphrase cache is ignored @@ -429,10 +422,10 @@ void start_command_handler_ssh (ctrl_t, gnupg_fd_t); gpg_error_t agent_modify_description (const char *in, const char *comment, const gcry_sexp_t key, char **result); int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force, - time_t timestamp, + const void *buffer, size_t length, + int force, int reallyforce, const char *serialno, const char *keyref, - const char *dispserialno); + const char *dispserialno, time_t timestamp); gpg_error_t agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, @@ -441,7 +434,8 @@ gpg_error_t agent_key_from_file (ctrl_t ctrl, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, gcry_sexp_t *result, - char **r_passphrase); + char **r_passphrase, + uint64_t *r_timestamp); gpg_error_t agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, gcry_sexp_t *result); gpg_error_t agent_keymeta_from_file (ctrl_t ctrl, const unsigned char *grip, @@ -533,7 +527,7 @@ unsigned char get_standard_s2k_count_rfc4880 (void); unsigned long get_standard_s2k_time (void); int agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char **result, size_t *resultlen, - unsigned long s2k_count, int use_ocb); + unsigned long s2k_count); gpg_error_t agent_unprotect (ctrl_t ctrl, const unsigned char *protectedkey, const char *passphrase, gnupg_isotime_t protected_at, @@ -552,10 +546,10 @@ gpg_error_t s2k_hash_passphrase (const char *passphrase, int hashalgo, const unsigned char *s2ksalt, unsigned int s2kcount, unsigned char *key, size_t keylen); -gpg_error_t agent_write_shadow_key (int maybe_update, - const unsigned char *grip, +gpg_error_t agent_write_shadow_key (const unsigned char *grip, const char *serialno, const char *keyid, const unsigned char *pkbuf, int force, + int reallyforce, const char *dispserialno); @@ -636,7 +630,8 @@ void agent_card_killscd (void); /*-- learncard.c --*/ -int agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force); +int agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, + int force, int reallyforce); /*-- cvt-openpgp.c --*/ diff --git a/agent/call-pinentry.c b/agent/call-pinentry.c index 5fcf98b..cbb6bae 100644 --- a/agent/call-pinentry.c +++ b/agent/call-pinentry.c @@ -57,7 +57,9 @@ * passphrase will be rendered as zbase32 which results for 150 bits * in a string of 30 characters. That fits nicely into the 5 * character blocking which pinentry can do. 128 bits would actually - * be sufficient but can't be formatted nicely. */ + * be sufficient but can't be formatted nicely. Please do not change + * this value because pattern check files may let such passwords + * always pass. */ #define DEFAULT_GENPIN_BITS 150 /* The assuan context of the current pinentry. */ @@ -551,6 +553,7 @@ start_pinentry (ctrl_t ctrl) "passphrase visible on the screen?") }, { "tt-visi",N_("|pinentry-tt|Make passphrase visible") }, { "tt-hide",N_("|pinentry-tt|Hide passphrase") }, + { "capshint", N_("Caps Lock is on") }, { NULL, NULL} }; char *optstr; @@ -834,21 +837,20 @@ estimate_passphrase_quality (const char *pw) /* Generate a random passphrase in zBase32 encoding (RFC-6189) to be - * used by Pinentry to suggest a passphrase. */ + * used by Pinentry to suggest a passphrase. Note that we have the + * same algorithm in gpg.c for --gen-random at level 30. It is + * important that we always output exactly 30 characters to match the + * special exception we have in the pattern file for symmetric + * encryption. */ static char * generate_pin (void) { - unsigned int nbits = opt.min_passphrase_len * 8; - size_t nbytes; + unsigned int nbits = DEFAULT_GENPIN_BITS; + size_t nbytes = nbytes = (nbits + 7) / 8; void *rand; char *generated; - if (nbits < 128) - nbits = DEFAULT_GENPIN_BITS; - - nbytes = (nbits + 7) / 8; - - rand = gcry_random_bytes_secure (nbytes, GCRY_STRONG_RANDOM); + rand = gcry_random_bytes_secure (nbytes, GCRY_STRONG_RANDOM); if (!rand) { log_error ("failed to generate random pin\n"); diff --git a/agent/call-scd.c b/agent/call-scd.c index c5b95f4..cd66070 100644 --- a/agent/call-scd.c +++ b/agent/call-scd.c @@ -501,7 +501,9 @@ agent_scd_check_aliveness (void) none of these context are actually in use. */ struct scd_local_s *sl; +#ifndef HAVE_W32_SYSTEM assuan_set_flag (primary_scd_ctx, ASSUAN_NO_WAITPID, 1); +#endif assuan_release (primary_scd_ctx); for (sl=scd_local_list; sl; sl = sl->next_local) diff --git a/agent/command-ssh.c b/agent/command-ssh.c index 21dd53c..bd1a418 100644 --- a/agent/command-ssh.c +++ b/agent/command-ssh.c @@ -2499,7 +2499,7 @@ card_key_available (ctrl_t ctrl, gcry_sexp_t *r_pk, char **cardsn) /* (Shadow)-key is not available in our key storage. */ agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno); - err = agent_write_shadow_key (0, grip, serialno, authkeyid, pkbuf, 0, + err = agent_write_shadow_key (grip, serialno, authkeyid, pkbuf, 0, 0, dispserialno); xfree (dispserialno); if (err) @@ -3028,7 +3028,7 @@ ssh_key_to_protected_buffer (gcry_sexp_t key, const char *passphrase, buffer_new, buffer_new_n); if (*passphrase) - err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0, -1); + err = agent_protect (buffer_new, passphrase, buffer, buffer_n, 0); else { /* The key derivation function does not support zero length @@ -3160,7 +3160,7 @@ ssh_identity_register (ctrl_t ctrl, ssh_key_type_spec_t *spec, /* Store this key to our key storage. We do not store a creation * timestamp because we simply do not know. */ err = agent_write_private_key (key_grip_raw, buffer, buffer_n, 0, 0, - NULL, NULL, NULL); + NULL, NULL, NULL, 0); if (err) goto out; diff --git a/agent/command.c b/agent/command.c index b682c55..940e017 100644 --- a/agent/command.c +++ b/agent/command.c @@ -975,7 +975,6 @@ cmd_genkey (assuan_context_t ctx, char *line) } - static const char hlp_readkey[] = "READKEY [--no-data] \n" @@ -1040,20 +1039,11 @@ cmd_readkey (assuan_context_t ctx, char *line) } agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno); - if (agent_key_available (grip)) - { - /* Shadow-key is not available in our key storage. */ - rc = agent_write_shadow_key (0, grip, serialno, keyid, pkbuf, 0, - dispserialno); - } - else - { - /* Shadow-key is available in our key storage but ne check - * whether we need to update it with a new display-s/n or - * whatever. */ - rc = agent_write_shadow_key (1, grip, serialno, keyid, pkbuf, 0, - dispserialno); - } + /* Shadow-key is or is not available in our key storage. In + * any case we need to check whether we need to update with + * a new display-s/n or whatever. */ + rc = agent_write_shadow_key (grip, serialno, keyid, pkbuf, 0, 0, + dispserialno); if (rc) goto leave; @@ -1292,9 +1282,6 @@ cmd_keyinfo (assuan_context_t ctx, char *line) char hexgrip[41]; int disabled, ttl, confirm, is_ssh; - if (ctrl->restricted) - return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); - if (has_option (line, "--ssh-list")) list_mode = 2; else @@ -1343,6 +1330,9 @@ cmd_keyinfo (assuan_context_t ctx, char *line) char *dirname; gnupg_dirent_t dir_entry; + if (ctrl->restricted) + return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); + dirname = make_filename_try (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, NULL); if (!dirname) @@ -1865,16 +1855,18 @@ cmd_learn (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); gpg_error_t err; - int send, sendinfo, force; + int send, sendinfo, force, reallyforce; send = has_option (line, "--send"); sendinfo = send? 1 : has_option (line, "--sendinfo"); force = has_option (line, "--force"); + reallyforce = has_option (line, "--reallyforce"); if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); - err = agent_handle_learn (ctrl, send, sendinfo? ctx : NULL, force); + err = agent_handle_learn (ctrl, send, sendinfo? ctx : NULL, + force, reallyforce); return leave_cmd (ctx, err); } @@ -1951,7 +1943,7 @@ cmd_passwd (assuan_context_t ctx, char *line) opt_verify? NULL : cache_nonce, ctrl->server_local->keydesc, grip, &shadow_info, CACHE_MODE_IGNORE, NULL, - &s_skey, &passphrase); + &s_skey, &passphrase, NULL); if (err) ; else if (shadow_info) @@ -2435,14 +2427,14 @@ cmd_import_key (assuan_context_t ctx, char *line) if (passphrase) { err = agent_protect (key, passphrase, &finalkey, &finalkeylen, - ctrl->s2k_count, -1); + ctrl->s2k_count); if (!err) - err = agent_write_private_key (grip, finalkey, finalkeylen, force, - opt_timestamp, NULL, NULL, NULL); + err = agent_write_private_key (grip, finalkey, finalkeylen, force, 0, + NULL, NULL, NULL, opt_timestamp); } else - err = agent_write_private_key (grip, key, realkeylen, force, - opt_timestamp, NULL, NULL, NULL); + err = agent_write_private_key (grip, key, realkeylen, force, 0, + NULL, NULL, NULL, opt_timestamp); leave: gcry_sexp_release (openpgp_sexp); @@ -2532,7 +2524,7 @@ cmd_export_key (assuan_context_t ctx, char *line) err = agent_key_from_file (ctrl, cache_nonce, ctrl->server_local->keydesc, grip, &shadow_info, CACHE_MODE_IGNORE, NULL, &s_skey, - openpgp ? &passphrase : NULL); + openpgp ? &passphrase : NULL, NULL); if (err) goto leave; if (shadow_info) @@ -2677,28 +2669,30 @@ cmd_delete_key (assuan_context_t ctx, char *line) -#if SIZEOF_TIME_T > SIZEOF_UNSIGNED_LONG -#define KEYTOCARD_TIMESTAMP_FORMAT "(10:created-at10:%010llu))" -#else -#define KEYTOCARD_TIMESTAMP_FORMAT "(10:created-at10:%010lu))" -#endif - static const char hlp_keytocard[] = - "KEYTOCARD [--force] \n" - "\n"; + "KEYTOCARD [--force] [ []]\n" + "\n" + "TIMESTAMP is required for OpenPGP and defaults to the Epoch.\n" + "ECDH are the hexified ECDH parameters for OpenPGP.\n" + "SERIALNO is used for checking; use \"-\" to disable the check."; static gpg_error_t cmd_keytocard (assuan_context_t ctx, char *line) { ctrl_t ctrl = assuan_get_pointer (ctx); int force; gpg_error_t err = 0; + char *argv[5]; + int argc; unsigned char grip[20]; + const char *serialno, *keyref; gcry_sexp_t s_skey = NULL; unsigned char *keydata; size_t keydatalen; - const char *serialno, *timestamp_str, *id; unsigned char *shadow_info = NULL; - time_t timestamp; + uint64_t timestamp; + char *ecdh_params = NULL; + unsigned int ecdh_params_len; + unsigned int extralen1, extralen2; if (ctrl->restricted) return leave_cmd (ctx, gpg_error (GPG_ERR_FORBIDDEN)); @@ -2706,7 +2700,14 @@ cmd_keytocard (assuan_context_t ctx, char *line) force = has_option (line, "--force"); line = skip_options (line); - err = parse_keygrip (ctx, line, grip); + argc = split_fields (line, argv, DIM (argv)); + if (argc < 3) + { + err = gpg_error (GPG_ERR_MISSING_VALUE); + goto leave; + } + + err = parse_keygrip (ctx, argv[0], grip); if (err) goto leave; @@ -2716,82 +2717,112 @@ cmd_keytocard (assuan_context_t ctx, char *line) goto leave; } - /* Fixme: Replace the parsing code by split_fields(). */ - line += 40; - while (*line && (*line == ' ' || *line == '\t')) - line++; - serialno = line; - while (*line && (*line != ' ' && *line != '\t')) - line++; - if (!*line) - { - err = gpg_error (GPG_ERR_MISSING_VALUE); - goto leave; - } - *line = '\0'; - line++; - while (*line && (*line == ' ' || *line == '\t')) - line++; - id = line; - while (*line && (*line != ' ' && *line != '\t')) - line++; - if (!*line) - { - err = gpg_error (GPG_ERR_MISSING_VALUE); - goto leave; - } - *line = '\0'; - line++; - while (*line && (*line == ' ' || *line == '\t')) - line++; - timestamp_str = line; - while (*line && (*line != ' ' && *line != '\t')) - line++; - if (*line) - *line = '\0'; + /* Note that checking of the s/n is currently not implemented but we + * want to provide a clean interface if we ever implement it. */ + serialno = argv[1]; + if (!strcmp (serialno, "-")) + serialno = NULL; - if ((timestamp = isotime2epoch (timestamp_str)) == (time_t)(-1)) - { - err = gpg_error (GPG_ERR_INV_TIME); - goto leave; - } + keyref = argv[2]; err = agent_key_from_file (ctrl, NULL, ctrl->server_local->keydesc, grip, &shadow_info, CACHE_MODE_IGNORE, NULL, - &s_skey, NULL); + &s_skey, NULL, ×tamp); if (err) + goto leave; + + if (shadow_info) { - xfree (shadow_info); + /* Key is already on a smartcard - wer can't extract it. */ + err = gpg_error (GPG_ERR_UNUSABLE_SECKEY); goto leave; } - if (shadow_info) + + /* Default to the creation time as stored in the private key. The + * parameter is here so that gpg can make sure that the timestamp is + * used. It is also important for OpenPGP cards to allow computing + * of the fingerprint. Same goes for the ECDH params. */ + if (argc > 3) { - /* Key is on a smartcard already. */ - xfree (shadow_info); - gcry_sexp_release (s_skey); - err = gpg_error (GPG_ERR_UNUSABLE_SECKEY); + timestamp = isotime2epoch_u64 (argv[3]); + if (argc > 4) + { + size_t n; + + err = parse_hexstring (ctx, argv[4], &n); + if (err) + goto leave; /* Badly formatted ecdh params. */ + n /= 2; + if (n < 4) + { + err = set_error (GPG_ERR_ASS_PARAMETER, "ecdh param too short"); + goto leave; + } + ecdh_params_len = n; + ecdh_params = xtrymalloc (ecdh_params_len); + if (!ecdh_params) + { + err = gpg_error_from_syserror (); + goto leave; + } + if (hex2bin (argv[4], ecdh_params, ecdh_params_len) < 0) + { + err = set_error (GPG_ERR_BUG, "hex2bin"); + goto leave; + } + } + } + else if (timestamp == (uint64_t)(-1)) + timestamp = isotime2epoch_u64 ("19700101T000000"); + + if (timestamp == (uint64_t)(-1)) + { + err = gpg_error (GPG_ERR_INV_TIME); goto leave; } - keydatalen = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); - keydata = xtrymalloc_secure (keydatalen + 30); + /* Note: We can't use make_canon_sexp because we need to allocate a + * few extra bytes for our hack below. The 20 for extralen2 + * accounts for the sexp length of ecdh_params. */ + keydatalen = gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, NULL, 0); + extralen1 = 30; + extralen2 = ecdh_params? (20+20+ecdh_params_len) : 0; + keydata = xtrymalloc_secure (keydatalen + extralen1 + extralen2); if (keydata == NULL) { err = gpg_error_from_syserror (); - gcry_sexp_release (s_skey); goto leave; } - gcry_sexp_sprint (s_skey, GCRYSEXP_FMT_CANON, keydata, keydatalen); gcry_sexp_release (s_skey); + s_skey = NULL; + keydatalen--; /* Decrement for last '\0'. */ - /* Add timestamp "created-at" in the private key */ - snprintf (keydata+keydatalen-1, 30, KEYTOCARD_TIMESTAMP_FORMAT, timestamp); + + /* Hack to insert the timestamp "created-at" into the private key. */ + snprintf (keydata+keydatalen-1, extralen1, "(10:created-at10:%010llu))", + (unsigned long long)timestamp); keydatalen += 10 + 19 - 1; - err = divert_writekey (ctrl, force, serialno, id, keydata, keydatalen); + + /* Hack to insert the timestamp "ecdh-params" into the private key. */ + if (ecdh_params) + { + snprintf (keydata+keydatalen-1, extralen2, "(11:ecdh-params%u:", + ecdh_params_len); + keydatalen += strlen (keydata+keydatalen-1) -1; + memcpy (keydata+keydatalen, ecdh_params, ecdh_params_len); + keydatalen += ecdh_params_len; + memcpy (keydata+keydatalen, "))", 3); + keydatalen += 2; + } + + err = divert_writekey (ctrl, force, serialno, keyref, keydata, keydatalen); xfree (keydata); leave: + xfree (ecdh_params); + gcry_sexp_release (s_skey); + xfree (shadow_info); return leave_cmd (ctx, err); } diff --git a/agent/cvt-openpgp.c b/agent/cvt-openpgp.c index 78f2989..fe51506 100644 --- a/agent/cvt-openpgp.c +++ b/agent/cvt-openpgp.c @@ -1068,9 +1068,9 @@ convert_from_openpgp_native (ctrl_t ctrl, if (!agent_protect (*r_key, passphrase, &protectedkey, &protectedkeylen, - ctrl->s2k_count, -1)) + ctrl->s2k_count)) agent_write_private_key (grip, protectedkey, protectedkeylen, - 1, 0, NULL, NULL, NULL); + 1/*force*/, 0, NULL, NULL, NULL, 0); xfree (protectedkey); } else @@ -1079,7 +1079,7 @@ convert_from_openpgp_native (ctrl_t ctrl, agent_write_private_key (grip, *r_key, gcry_sexp_canon_len (*r_key, 0, NULL,NULL), - 1, 0, NULL, NULL, NULL); + 1/*force*/, 0, NULL, NULL, NULL, 0); } } diff --git a/agent/findkey.c b/agent/findkey.c index dadcc3c..a5055bc 100644 --- a/agent/findkey.c +++ b/agent/findkey.c @@ -1,7 +1,8 @@ /* findkey.c - Locate the secret key * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2007, * 2010, 2011 Free Software Foundation, Inc. - * Copyright (C) 2014 Werner Koch + * Copyright (C) 2014, 2019 Werner Koch + * Copyright (C) 2023 g10 Code GmbH * * This file is part of GnuPG. * @@ -26,10 +27,8 @@ #include #include #include -#include #include #include -#include #include /* (we use pth_sleep) */ #include "agent.h" @@ -41,6 +40,13 @@ #define O_BINARY 0 #endif + +static gpg_error_t read_key_file (const unsigned char *grip, + gcry_sexp_t *result, nvc_t *r_keymeta, + char **r_orig_key_value); +static gpg_error_t is_shadowed_key (gcry_sexp_t s_skey); + + /* Helper to pass data to the check callback of the unprotect function. */ struct try_unprotect_arg_s { @@ -52,96 +58,134 @@ struct try_unprotect_arg_s }; -/* Return the file name for the 20 byte keygrip GRIP. Return NULL on - * error. */ +/* Return the file name for the 20 byte keygrip GRIP. With FOR_NEW + * create a file name for later renaming to the actual name. Return + * NULL on error. */ static char * -fname_from_keygrip (const unsigned char *grip) +fname_from_keygrip (const unsigned char *grip, int for_new) { - char hexgrip[40+4+1]; + char hexgrip[40+4+4+1]; bin2hex (grip, 20, hexgrip); - strcpy (hexgrip+40, ".key"); + strcpy (hexgrip+40, for_new? ".key.tmp" : ".key"); return make_filename_try (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, hexgrip, NULL); } -/* Note: Ownership of FNAME and FP are moved to this function. - * OLD_FORMAT is true if the file exists but is still in the - * non-extended mode format. If MAYBE_UPDATE is set the function - * assumes that the file exists but writes it only if it figures that - * an update is required. */ -static gpg_error_t -write_extended_private_key (int maybe_update, - char *fname, estream_t fp, - int old_format, int newkey, - const void *buf, size_t len, time_t timestamp, - const char *serialno, const char *keyref, - const char *dispserialno) +/* Write the S-expression formatted key (BUFFER,LENGTH) to our key + * storage. With FORCE passed as true an existing key with the given + * GRIP will be overwritten. If SERIALNO and KEYREF are given a Token + * line is added to the key if the extended format is used. If + * TIMESTAMP is not zero and the key doies not yet exists it will be + * recorded as creation date. */ +int +agent_write_private_key (const unsigned char *grip, + const void *buffer, size_t length, + int force, int reallyforce, + const char *serialno, const char *keyref, + const char *dispserialno, + time_t timestamp) { gpg_error_t err; + char *oldfname = NULL; + char *fname = NULL; + estream_t fp; + int newkey = 0; nvc_t pk = NULL; gcry_sexp_t key = NULL; + int is_regular; int remove = 0; char *token0 = NULL; char *token = NULL; char *dispserialno_buffer = NULL; char **tokenfields = NULL; + int blocksigs = 0; + char *orig_key_value = NULL; + const char *s; + int force_modify = 0; + + oldfname = fname_from_keygrip (grip, 0); + if (!oldfname) + return out_of_core (); - if (old_format || newkey) + err = read_key_file (grip, &key, &pk, &orig_key_value); + if (err) { - /* We must create a new NVC if the key is still in the old - * format and of course if it is a new key. */ - pk = nvc_new_private_key (); - if (!pk) + if (gpg_err_code (err) == GPG_ERR_ENOENT) + newkey = 1; + else { - err = gpg_error_from_syserror (); + log_error ("can't open '%s': %s\n", oldfname, gpg_strerror (err)); goto leave; } - maybe_update = 0; /* Always write. */ } - else - { /* Parse the existing NVC. */ - int lineno = 0; - err = nvc_parse_private_key (&pk, &lineno, fp); - if (err) + nvc_modified (pk, 1); /* Clear that flag after a read. */ + + if (!pk) + { + /* Key is still in the old format or does not exist - create a + * new container. */ + pk = nvc_new_private_key (); + if (!pk) { - log_error ("error parsing '%s' line %d: %s\n", - fname, lineno, gpg_strerror (err)); + err = gpg_error_from_syserror (); goto leave; } + force_modify = 1; } - es_clearerr (fp); - err = gcry_sexp_sscan (&key, NULL, buf, len); + /* Check whether we already have a regular key. */ + is_regular = (key && gpg_err_code (is_shadowed_key (key)) != GPG_ERR_TRUE); + + /* Turn (BUFFER,LENGTH) into a gcrypt s-expression and set it into + * our name value container. */ + gcry_sexp_release (key); + err = gcry_sexp_sscan (&key, NULL, buffer, length); if (err) goto leave; - err = nvc_set_private_key (pk, key); if (err) goto leave; - /* If a timestamp has been supplied and the key is new write a - * creation timestamp. Note that we can't add this item if we are - * still in the old format. We also add an extra check that there - * is no Created item yet. */ - if (timestamp && newkey && !nvc_lookup (pk, "Created:")) + /* Detect whether the key value actually changed and if not clear + * the modified flag. This extra check is required because + * read_key_file removes the Key entry from the container and we + * then create a new Key entry which might be the same, though. */ + if (!force_modify + && orig_key_value && (s = nvc_get_string (pk, "Key:")) + && !strcmp (orig_key_value, s)) { - gnupg_isotime_t timebuf; + nvc_modified (pk, 1); /* Clear that flag. */ + } + xfree (orig_key_value); + orig_key_value = NULL; - epoch2isotime (timebuf, timestamp); - err = nvc_add (pk, "Created:", timebuf); - if (err) - goto leave; + /* Check that we do not update a regular key with a shadow key. */ + if (is_regular && gpg_err_code (is_shadowed_key (key)) == GPG_ERR_TRUE) + { + if (!reallyforce) + { + log_info ("updating regular key file '%s'" + " by a shadow key inhibited\n", oldfname); + err = 0; /* Simply ignore the error. */ + goto leave; + } + } + /* Check that we update a regular key only in force mode. */ + if (is_regular && !force) + { + log_error ("secret key file '%s' already exists\n", oldfname); + err = gpg_error (GPG_ERR_EEXIST); + goto leave; } /* If requested write a Token line. */ if (serialno && keyref) { nve_t item; - const char *s; size_t token0len; if (dispserialno) @@ -179,50 +223,63 @@ write_extended_private_key (int maybe_update, err = nvc_add (pk, "Token:", token); if (err) goto leave; - maybe_update = 0; /* Force write. */ } else { /* Token exists: Update the display s/n. It may have * changed due to changes in a newer software version. */ - if (maybe_update && s && (tokenfields = strtokenize (s, " \t\n")) + if (s && (tokenfields = strtokenize (s, " \t\n")) && tokenfields[0] && tokenfields[1] && tokenfields[2] && tokenfields[3] && !strcmp (tokenfields[3], dispserialno)) ; /* No need to update Token entry. */ else { - err = nve_set (item, token); + err = nve_set (pk, item, token); if (err) goto leave; - maybe_update = 0; /* Force write. */ } } } - err = es_fseek (fp, 0, SEEK_SET); - if (err) - goto leave; - - if (!maybe_update) + /* If a timestamp has been supplied and the key is new, write a + * creation timestamp. (We douple check that there is no Created + * item yet.)*/ + if (timestamp && newkey && !nvc_lookup (pk, "Created:")) { - err = nvc_write (pk, fp); - if (!err) - err = es_fflush (fp); + gnupg_isotime_t timebuf; + + epoch2isotime (timebuf, timestamp); + err = nvc_add (pk, "Created:", timebuf); if (err) - { - log_error ("error writing '%s': %s\n", fname, gpg_strerror (err)); - remove = 1; - goto leave; - } + goto leave; + } - if (ftruncate (es_fileno (fp), es_ftello (fp))) - { - err = gpg_error_from_syserror (); - log_error ("error truncating '%s': %s\n", fname, gpg_strerror (err)); - remove = 1; - goto leave; - } + /* Check whether we need to write the file at all. */ + if (!nvc_modified (pk, 0)) + { + err = 0; + goto leave; + } + + /* Create a temporary file for writing. */ + fname = fname_from_keygrip (grip, 1); + fp = fname ? es_fopen (fname, "wbx,mode=-rw") : NULL; + if (!fp) + { + err = gpg_error_from_syserror (); + log_error ("can't create '%s': %s\n", fname, gpg_strerror (err)); + goto leave; + } + + err = nvc_write (pk, fp); + if (!err && es_fflush (fp)) + err = gpg_error_from_syserror (); + if (err) + { + log_error ("error writing '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; } if (es_fclose (fp)) @@ -232,17 +289,28 @@ write_extended_private_key (int maybe_update, remove = 1; goto leave; } - else - fp = NULL; + fp = NULL; - if (!maybe_update) - bump_key_eventcounter (); + err = gnupg_rename_file (fname, oldfname, &blocksigs); + if (err) + { + err = gpg_error_from_syserror (); + log_error ("error renaming '%s': %s\n", fname, gpg_strerror (err)); + remove = 1; + goto leave; + } + + bump_key_eventcounter (); leave: + if (blocksigs) + gnupg_unblock_all_signals (); es_fclose (fp); - if (remove) + if (remove && fname) gnupg_remove (fname); + xfree (orig_key_value); xfree (fname); + xfree (oldfname); xfree (token); xfree (token0); xfree (dispserialno_buffer); @@ -252,136 +320,6 @@ write_extended_private_key (int maybe_update, return err; } -/* Write an S-expression formatted key to our key storage. With FORCE - * passed as true an existing key with the given GRIP will get - * overwritten. If TIMESTAMP is not zero and the key does not yet - * exists it will be recorded as creation date. If SERIALNO, KEYREF, - * of DISPSERIALNO are not NULL they will be recorded as well. */ -int -agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, - int force, time_t timestamp, - const char *serialno, const char *keyref, - const char *dispserialno) -{ - char *fname; - estream_t fp; - - fname = fname_from_keygrip (grip); - if (!fname) - return gpg_error_from_syserror (); - - /* FIXME: Write to a temp file first so that write failures during - key updates won't lead to a key loss. */ - - if (!force && !gnupg_access (fname, F_OK)) - { - log_error ("secret key file '%s' already exists\n", fname); - xfree (fname); - return gpg_error (GPG_ERR_EEXIST); - } - - fp = es_fopen (fname, force? "rb+,mode=-rw" : "wbx,mode=-rw"); - if (!fp) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - - if (force && gpg_err_code (tmperr) == GPG_ERR_ENOENT) - { - fp = es_fopen (fname, "wbx,mode=-rw"); - if (!fp) - tmperr = gpg_error_from_syserror (); - } - if (!fp) - { - log_error ("can't create '%s': %s\n", fname, gpg_strerror (tmperr)); - xfree (fname); - return tmperr; - } - } - else if (force) - { - gpg_error_t rc; - char first; - - /* See if an existing key is in extended format. */ - if (es_fread (&first, 1, 1, fp) != 1) - { - rc = gpg_error_from_syserror (); - log_error ("error reading first byte from '%s': %s\n", - fname, strerror (errno)); - xfree (fname); - es_fclose (fp); - return rc; - } - - rc = es_fseek (fp, 0, SEEK_SET); - if (rc) - { - log_error ("error seeking in '%s': %s\n", fname, strerror (errno)); - xfree (fname); - es_fclose (fp); - return rc; - } - - if (first != '(') - { - /* Key is already in the extended format. */ - return write_extended_private_key (0, fname, fp, 0, 0, - buffer, length, - timestamp, serialno, keyref, - dispserialno); - } - if (first == '(' && opt.enable_extended_key_format) - { - /* Key is in the old format - but we want the extended format. */ - return write_extended_private_key (0, fname, fp, 1, 0, - buffer, length, - timestamp, serialno, keyref, - dispserialno); - } - } - - if (opt.enable_extended_key_format) - return write_extended_private_key (0, fname, fp, 0, 1, - buffer, length, - timestamp, serialno, keyref, - dispserialno); - - if (es_fwrite (buffer, length, 1, fp) != 1) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("error writing '%s': %s\n", fname, gpg_strerror (tmperr)); - es_fclose (fp); - gnupg_remove (fname); - xfree (fname); - return tmperr; - } - - /* When force is given, the file might have to be truncated. */ - if (force && ftruncate (es_fileno (fp), es_ftello (fp))) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("error truncating '%s': %s\n", fname, gpg_strerror (tmperr)); - es_fclose (fp); - gnupg_remove (fname); - xfree (fname); - return tmperr; - } - - if (es_fclose (fp)) - { - gpg_error_t tmperr = gpg_error_from_syserror (); - log_error ("error closing '%s': %s\n", fname, gpg_strerror (tmperr)); - gnupg_remove (fname); - xfree (fname); - return tmperr; - } - bump_key_eventcounter (); - xfree (fname); - return 0; -} - /* Callback function to try the unprotection from the passphrase query code. */ @@ -395,7 +333,7 @@ try_unprotect_cb (struct pin_entry_info_s *pi) gnupg_isotime_t now, protected_at, tmptime; char *desc = NULL; - assert (!arg->unprotected_key); + log_assert (!arg->unprotected_key); arg->change_required = 0; err = agent_unprotect (ctrl, arg->protected_key, pi->pin, protected_at, @@ -772,7 +710,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, } else { - assert (arg.unprotected_key); + log_assert (arg.unprotected_key); if (arg.change_required) { /* The callback told as that the user should change their @@ -780,7 +718,7 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, size_t canlen, erroff; gcry_sexp_t s_skey; - assert (arg.unprotected_key); + log_assert (arg.unprotected_key); canlen = gcry_sexp_canon_len (arg.unprotected_key, 0, NULL, NULL); rc = gcry_sexp_sscan (&s_skey, &erroff, (char*)arg.unprotected_key, canlen); @@ -824,39 +762,43 @@ unprotect (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, /* Read the key identified by GRIP from the private key directory and * return it as an gcrypt S-expression object in RESULT. If R_KEYMETA - * is not NULl and the extended key format is used, the meta data - * items are stored there. However the "Key:" item is removed from - * it. On failure returns an error code and stores NULL at RESULT. */ + * is not NULL, the meta data items are stored there. However the + * "Key:" item is removed. If R_ORIG_KEY_VALUE is non-NULL and the + * Key items was removed, its value is stored at that R_ORIG_KEY_VALUE + * and the caller must free it. Returns an error code and stores NULL + * at RESULT. */ static gpg_error_t -read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) +read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta, + char **r_orig_key_value) { gpg_error_t err; char *fname; - estream_t fp; + estream_t fp = NULL; struct stat st; - unsigned char *buf; + unsigned char *buf = NULL; size_t buflen, erroff; gcry_sexp_t s_skey; - char hexgrip[40+4+1]; char first; *result = NULL; if (r_keymeta) *r_keymeta = NULL; + if (r_orig_key_value) + *r_orig_key_value = NULL; - bin2hex (grip, 20, hexgrip); - strcpy (hexgrip+40, ".key"); - - fname = make_filename (gnupg_homedir (), GNUPG_PRIVATE_KEYS_DIR, - hexgrip, NULL); + fname = fname_from_keygrip (grip, 0); + if (!fname) + { + err = gpg_error_from_syserror (); + goto leave; + } fp = es_fopen (fname, "rb"); if (!fp) { err = gpg_error_from_syserror (); if (gpg_err_code (err) != GPG_ERR_ENOENT) log_error ("can't open '%s': %s\n", fname, gpg_strerror (err)); - xfree (fname); - return err; + goto leave; } if (es_fread (&first, 1, 1, fp) != 1) @@ -864,18 +806,14 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) err = gpg_error_from_syserror (); log_error ("error reading first byte from '%s': %s\n", fname, gpg_strerror (err)); - xfree (fname); - es_fclose (fp); - return err; + goto leave; } if (es_fseek (fp, 0, SEEK_SET)) { err = gpg_error_from_syserror (); log_error ("error seeking in '%s': %s\n", fname, gpg_strerror (err)); - xfree (fname); - es_fclose (fp); - return err; + goto leave; } if (first != '(') @@ -885,8 +823,6 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) int line; err = nvc_parse_private_key (&pk, &line, fp); - es_fclose (fp); - if (err) log_error ("error parsing '%s' line %d: %s\n", fname, line, gpg_strerror (err)); @@ -897,24 +833,37 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) log_error ("error getting private key from '%s': %s\n", fname, gpg_strerror (err)); else - nvc_delete_named (pk, "Key:"); + { + if (r_orig_key_value) + { + const char *s = nvc_get_string (pk, "Key:"); + if (s) + { + *r_orig_key_value = xtrystrdup (s); + if (!*r_orig_key_value) + { + err = gpg_error_from_syserror (); + nvc_release (pk); + goto leave; + } + } + } + nvc_delete_named (pk, "Key:"); + } } if (!err && r_keymeta) *r_keymeta = pk; else nvc_release (pk); - xfree (fname); - return err; + goto leave; } if (fstat (es_fileno (fp), &st)) { err = gpg_error_from_syserror (); log_error ("can't stat '%s': %s\n", fname, gpg_strerror (err)); - xfree (fname); - es_fclose (fp); - return err; + goto leave; } buflen = st.st_size; @@ -924,11 +873,7 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) err = gpg_error_from_syserror (); log_error ("error allocating %zu bytes for '%s': %s\n", buflen, fname, gpg_strerror (err)); - xfree (fname); - es_fclose (fp); - xfree (buf); - return err; - + goto leave; } if (es_fread (buf, buflen, 1, fp) != 1) @@ -936,25 +881,24 @@ read_key_file (const unsigned char *grip, gcry_sexp_t *result, nvc_t *r_keymeta) err = gpg_error_from_syserror (); log_error ("error reading %zu bytes from '%s': %s\n", buflen, fname, gpg_strerror (err)); - xfree (fname); - es_fclose (fp); - xfree (buf); - return err; + goto leave; } /* Convert the file into a gcrypt S-expression object. */ err = gcry_sexp_sscan (&s_skey, &erroff, (char*)buf, buflen); - xfree (fname); - es_fclose (fp); - xfree (buf); if (err) { log_error ("failed to build S-Exp (off=%u): %s\n", (unsigned int)erroff, gpg_strerror (err)); - return err; + goto leave; } *result = s_skey; - return 0; + + leave: + es_fclose (fp); + xfree (fname); + xfree (buf); + return err; } @@ -997,20 +941,24 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, const char *desc_text, const unsigned char *grip, unsigned char **shadow_info, cache_mode_t cache_mode, lookup_ttl_t lookup_ttl, - gcry_sexp_t *result, char **r_passphrase) + gcry_sexp_t *result, char **r_passphrase, + uint64_t *r_timestamp) { gpg_error_t err; unsigned char *buf; size_t len, buflen, erroff; gcry_sexp_t s_skey; + nvc_t keymeta = NULL; *result = NULL; if (shadow_info) *shadow_info = NULL; if (r_passphrase) *r_passphrase = NULL; + if (r_timestamp) + *r_timestamp = (uint64_t)(-1); - err = read_key_file (grip, &s_skey, NULL); + err = read_key_file (grip, &s_skey, &keymeta, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) @@ -1023,7 +971,19 @@ agent_key_from_file (ctrl_t ctrl, const char *cache_nonce, now. */ err = make_canon_sexp (s_skey, &buf, &len); if (err) - return err; + { + nvc_release (keymeta); + return err; + } + + if (r_timestamp && keymeta) + { + const char *created = nvc_get_string (keymeta, "Created:"); + + if (created) + *r_timestamp = isotime2epoch_u64 (created); + } + nvc_release (keymeta); switch (agent_private_key_type (buf)) { @@ -1325,6 +1285,29 @@ agent_is_eddsa_key (gcry_sexp_t s_key) } +/* This function returns GPG_ERR_TRUE if S_SKEY represents a shadowed + * key. 0 is return for other key types. Any other error may occur + * if S_SKEY is not valid. */ +static gpg_error_t +is_shadowed_key (gcry_sexp_t s_skey) +{ + gpg_error_t err; + unsigned char *buf; + size_t buflen; + + err = make_canon_sexp (s_skey, &buf, &buflen); + if (err) + return err; + + if (agent_private_key_type (buf) == PRIVATE_KEY_SHADOWED) + err = gpg_error (GPG_ERR_TRUE); + + wipememory (buf, buflen); + xfree (buf); + return err; +} + + /* Return the key for the keygrip GRIP. The result is stored at RESULT. This function extracts the key from the private key database and returns it as an S-expression object as it is. On @@ -1340,7 +1323,7 @@ agent_raw_key_from_file (ctrl_t ctrl, const unsigned char *grip, *result = NULL; - err = read_key_file (grip, &s_skey, NULL); + err = read_key_file (grip, &s_skey, NULL, NULL); if (!err) *result = s_skey; return err; @@ -1356,7 +1339,7 @@ agent_keymeta_from_file (ctrl_t ctrl, const unsigned char *grip, (void)ctrl; - err = read_key_file (grip, &s_skey, r_keymeta); + err = read_key_file (grip, &s_skey, r_keymeta, NULL); gcry_sexp_release (s_skey); return err; } @@ -1394,7 +1377,7 @@ agent_public_key_from_file (ctrl_t ctrl, *result = NULL; - err = read_key_file (grip, &s_skey, NULL); + err = read_key_file (grip, &s_skey, NULL, NULL); if (err) return err; @@ -1431,7 +1414,7 @@ agent_public_key_from_file (ctrl_t ctrl, such a task. After all that is what we do in protect.c. Need to find common patterns and write a straightformward API to use them. */ - assert (sizeof (size_t) <= sizeof (void*)); + log_assert (sizeof (size_t) <= sizeof (void*)); format = xtrymalloc (15+4+7*npkey+10+15+1+1); if (!format) @@ -1456,14 +1439,14 @@ agent_public_key_from_file (ctrl_t ctrl, *p++ = '('; *p++ = *s++; p = stpcpy (p, " %m)"); - assert (argidx < DIM (args)); + log_assert (argidx < DIM (args)); args[argidx++] = &array[idx]; } *p++ = ')'; if (uri) { p = stpcpy (p, "(uri %b)"); - assert (argidx+1 < DIM (args)); + log_assert (argidx+1 < DIM (args)); uri_intlen = (int)uri_length; args[argidx++] = (void *)&uri_intlen; args[argidx++] = (void *)&uri; @@ -1471,14 +1454,14 @@ agent_public_key_from_file (ctrl_t ctrl, if (comment) { p = stpcpy (p, "(comment %b)"); - assert (argidx+1 < DIM (args)); + log_assert (argidx+1 < DIM (args)); comment_intlen = (int)comment_length; args[argidx++] = (void *)&comment_intlen; args[argidx++] = (void*)&comment; } *p++ = ')'; *p = 0; - assert (argidx < DIM (args)); + log_assert (argidx < DIM (args)); args[argidx] = NULL; err = gcry_sexp_build_array (&list, NULL, format, args); @@ -1541,7 +1524,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, { gcry_sexp_t sexp; - err = read_key_file (grip, &sexp, NULL); + err = read_key_file (grip, &sexp, NULL, NULL); if (err) { if (gpg_err_code (err) == GPG_ERR_ENOENT) @@ -1575,7 +1558,7 @@ agent_key_info_from_file (ctrl_t ctrl, const unsigned char *grip, if (!err) { n = gcry_sexp_canon_len (s, 0, NULL, NULL); - assert (n); + log_assert (n); *r_shadow_info = xtrymalloc (n); if (!*r_shadow_info) err = gpg_error_from_syserror (); @@ -1625,7 +1608,7 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text, char *default_desc = NULL; int key_type; - err = read_key_file (grip, &s_skey, NULL); + err = read_key_file (grip, &s_skey, NULL, NULL); if (gpg_err_code (err) == GPG_ERR_ENOENT) err = gpg_error (GPG_ERR_NO_SECKEY); if (err) @@ -1722,29 +1705,22 @@ agent_delete_key (ctrl_t ctrl, const char *desc_text, /* Write an S-expression formatted shadow key to our key storage. - Shadow key is created by an S-expression public key in PKBUF and - card's SERIALNO and the IDSTRING. With FORCE passed as true an - existing key with the given GRIP will get overwritten. If - DISPSERIALNO is not NULL the human readable s/n will also be - recorded in the key file. If MAYBE_UPDATE is set it is assumed that - the shadow key already exists and we test whether we should update - it (FORCE is ignored in this case). */ + * Shadow key is created by an S-expression public key in PKBUF and + * card's SERIALNO and the IDSTRING. With FORCE passed as true an + * existing key with the given GRIP will get overwritten. If + * REALLYFORCE is also true, even a private key will be overwritten by + * a shadown key. If DISPSERIALNO is not NULL the human readable s/n + * will also be recorded in the key file. */ gpg_error_t -agent_write_shadow_key (int maybe_update, const unsigned char *grip, +agent_write_shadow_key (const unsigned char *grip, const char *serialno, const char *keyid, - const unsigned char *pkbuf, int force, + const unsigned char *pkbuf, int force, int reallyforce, const char *dispserialno) { gpg_error_t err; unsigned char *shadow_info; unsigned char *shdkey; size_t len; - char *fname = NULL; - estream_t fp = NULL; - char first; - - if (maybe_update && !opt.enable_extended_key_format) - return 0; /* Silently ignore. */ /* Just in case some caller did not parse the stuff correctly, skip * leading spaces. */ @@ -1766,62 +1742,11 @@ agent_write_shadow_key (int maybe_update, const unsigned char *grip, } len = gcry_sexp_canon_len (shdkey, 0, NULL, NULL); - - if (maybe_update) /* Update mode. */ - { - fname = fname_from_keygrip (grip); - if (!fname) - { - err = gpg_error_from_syserror (); - goto leave; - } - - fp = es_fopen (fname, "rb+,mode=-rw"); - if (!fp) - { - err = gpg_error_from_syserror (); - log_error ("shadow key file '%s' disappeared\n", fname); - goto leave; - } - - /* See if an existing key is in extended format. */ - if (es_fread (&first, 1, 1, fp) != 1) - { - err = gpg_error_from_syserror (); - log_error ("error reading first byte from '%s': %s\n", - fname, gpg_strerror (err)); - goto leave; - } - - if (es_fseek (fp, 0, SEEK_SET)) - { - err = gpg_error_from_syserror (); - log_error ("error seeking in '%s': %s\n", fname, gpg_strerror (err)); - goto leave; - } - - /* "(first == '(')" indicates that the key is in the old format. */ - err = write_extended_private_key (maybe_update, - fname, fp, (first == '('), 0, - shdkey, len, - 0, serialno, keyid, - dispserialno); - fname = NULL; /* Ownership was transferred. */ - fp = NULL; /* Ditto. */ - } - else /* Standard mode */ - { - err = agent_write_private_key (grip, shdkey, len, force, 0, - serialno, keyid, dispserialno); - } - - leave: - xfree (fname); - es_fclose (fp); + err = agent_write_private_key (grip, shdkey, len, force, reallyforce, + serialno, keyid, dispserialno, 0); xfree (shdkey); if (err) - log_error ("error %s key: %s\n", maybe_update? "updating":"writing", - gpg_strerror (err)); + log_error ("error writing key: %s\n", gpg_strerror (err)); return err; } diff --git a/agent/genkey.c b/agent/genkey.c index a944ac7..d080bac 100644 --- a/agent/genkey.c +++ b/agent/genkey.c @@ -59,7 +59,7 @@ store_key (gcry_sexp_t private, const char *passphrase, int force, { unsigned char *p; - rc = agent_protect (buf, passphrase, &p, &len, s2k_count, -1); + rc = agent_protect (buf, passphrase, &p, &len, s2k_count); if (rc) { xfree (buf); @@ -69,8 +69,8 @@ store_key (gcry_sexp_t private, const char *passphrase, int force, buf = p; } - rc = agent_write_private_key (grip, buf, len, force, timestamp, - NULL, NULL, NULL); + rc = agent_write_private_key (grip, buf, len, force, 0, + NULL, NULL, NULL, timestamp); xfree (buf); return rc; } diff --git a/agent/gpg-agent.c b/agent/gpg-agent.c index 53b86dd..c3b71b0 100644 --- a/agent/gpg-agent.c +++ b/agent/gpg-agent.c @@ -116,8 +116,6 @@ enum cmd_and_opt_values oCheckSymPassphrasePattern, oMaxPassphraseDays, oEnablePassphraseHistory, - oDisableExtendedKeyFormat, - oEnableExtendedKeyFormat, oStealSocket, oUseStandardSocket, oNoUseStandardSocket, @@ -227,8 +225,6 @@ static ARGPARSE_OPTS opts[] = { /* */ "@" #endif ), - ARGPARSE_s_n (oDisableExtendedKeyFormat, "disable-extended-key-format", "@"), - ARGPARSE_s_n (oEnableExtendedKeyFormat, "enable-extended-key-format", "@"), ARGPARSE_s_i (oListenBacklog, "listen-backlog", "@"), ARGPARSE_op_u (oAutoExpandSecmem, "auto-expand-secmem", "@"), ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), @@ -303,7 +299,8 @@ static ARGPARSE_OPTS opts[] = { ARGPARSE_s_n (oNoUseStandardSocket, "no-use-standard-socket", "@"), /* Dummy options. */ - + ARGPARSE_s_n (oNoop, "disable-extended-key-format", "@"), + ARGPARSE_s_n (oNoop, "enable-extended-key-format", "@"), ARGPARSE_end () /* End of list */ }; @@ -874,7 +871,6 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) opt.check_sym_passphrase_pattern = NULL; opt.max_passphrase_days = MAX_PASSPHRASE_DAYS; opt.enable_passphrase_history = 0; - opt.enable_extended_key_format = 1; opt.ignore_cache_for_signing = 0; opt.allow_mark_trusted = 1; opt.sys_trustlist_name = NULL; @@ -956,14 +952,6 @@ parse_rereadable_options (ARGPARSE_ARGS *pargs, int reread) opt.enable_passphrase_history = 1; break; - case oEnableExtendedKeyFormat: - opt.enable_extended_key_format = 2; - break; - case oDisableExtendedKeyFormat: - if (opt.enable_extended_key_format != 2) - opt.enable_extended_key_format = 0; - break; - case oIgnoreCacheForSigning: opt.ignore_cache_for_signing = 1; break; case oAllowMarkTrusted: opt.allow_mark_trusted = 1; break; diff --git a/agent/gpg-agent.w32-manifest.in b/agent/gpg-agent.w32-manifest.in index d865aef..1ec3268 100644 --- a/agent/gpg-agent.w32-manifest.in +++ b/agent/gpg-agent.w32-manifest.in @@ -1,6 +1,6 @@ -GNU Privacy Guard (Private key daemon) +GNU Privacy Guard (Private Key Daemon) + + + + + + + diff --git a/agent/learncard.c b/agent/learncard.c index 2e491e1..f007b6d 100644 --- a/agent/learncard.c +++ b/agent/learncard.c @@ -297,9 +297,12 @@ send_cert_back (ctrl_t ctrl, const char *id, void *assuan_context) } /* Perform the learn operation. If ASSUAN_CONTEXT is not NULL and - SEND is true all new certificates are send back via Assuan. */ + SEND is true all new certificates are send back via Assuan. If + REALLYFORCE is true a private key will be overwritten by a stub + key. */ int -agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force) +agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, + int force, int reallyforce) { int rc; struct kpinfo_cb_parm_s parm; @@ -413,8 +416,8 @@ agent_handle_learn (ctrl_t ctrl, int send, void *assuan_context, int force) char *dispserialno; agent_card_getattr (ctrl, "$DISPSERIALNO", &dispserialno); - rc = agent_write_shadow_key (0, grip, serialno, item->id, pubkey, - force, dispserialno); + rc = agent_write_shadow_key (grip, serialno, item->id, pubkey, + force, reallyforce, dispserialno); xfree (dispserialno); } xfree (pubkey); diff --git a/agent/pkdecrypt.c b/agent/pkdecrypt.c index 6aed96b..ccd395d 100644 --- a/agent/pkdecrypt.c +++ b/agent/pkdecrypt.c @@ -69,7 +69,7 @@ agent_pkdecrypt (ctrl_t ctrl, const char *desc_text, } rc = agent_key_from_file (ctrl, NULL, desc_text, ctrl->keygrip, &shadow_info, - CACHE_MODE_NORMAL, NULL, &s_skey, NULL); + CACHE_MODE_NORMAL, NULL, &s_skey, NULL, NULL); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NO_SECKEY) diff --git a/agent/pksign.c b/agent/pksign.c index 09d61b8..571541d 100644 --- a/agent/pksign.c +++ b/agent/pksign.c @@ -311,7 +311,7 @@ agent_pksign_do (ctrl_t ctrl, const char *cache_nonce, err = agent_key_from_file (ctrl, cache_nonce, desc_text, ctrl->keygrip, &shadow_info, cache_mode, lookup_ttl, - &s_skey, NULL); + &s_skey, NULL, NULL); if (err) { if (gpg_err_code (err) != GPG_ERR_NO_SECKEY) diff --git a/agent/protect-tool.c b/agent/protect-tool.c index fb2c71d..35ed993 100644 --- a/agent/protect-tool.c +++ b/agent/protect-tool.c @@ -98,7 +98,6 @@ static const char *opt_passphrase; static char *opt_prompt; static int opt_status_msg; static const char *opt_agent_program; -static int opt_debug_use_ocb; static char *get_passphrase (int promptno); static void release_passphrase (char *pw); @@ -345,8 +344,7 @@ read_and_protect (const char *fname) return; pw = get_passphrase (1); - rc = agent_protect (key, pw, &result, &resultlen, 0, - opt_debug_use_ocb? 1 : -1); + rc = agent_protect (key, pw, &result, &resultlen, 0); release_passphrase (pw); xfree (key); if (rc) @@ -605,7 +603,7 @@ main (int argc, char **argv ) case oHaveCert: opt_have_cert = 1; break; case oPrompt: opt_prompt = pargs.r.ret_str; break; case oStatusMsg: opt_status_msg = 1; break; - case oDebugUseOCB: opt_debug_use_ocb = 1; break; + case oDebugUseOCB: /* dummy */; break; default: pargs.err = ARGPARSE_PRINT_ERROR; break; } @@ -809,14 +807,15 @@ agent_askpin (ctrl_t ctrl, * to stdout. */ int agent_write_private_key (const unsigned char *grip, - const void *buffer, size_t length, int force, - time_t timestamp, + const void *buffer, size_t length, + int force, int reallyforce, const char *serialno, const char *keyref, - const char *dispserialno) + const char *dispserialno, time_t timestamp) { char hexgrip[40+4+1]; char *p; + (void)reallyforce; (void)force; (void)timestamp; (void)serialno; diff --git a/agent/protect.c b/agent/protect.c index 87df685..6bf1400 100644 --- a/agent/protect.c +++ b/agent/protect.c @@ -366,12 +366,11 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, const char *passphrase, const char *timestamp_exp, size_t timestamp_exp_len, unsigned char **result, size_t *resultlen, - unsigned long s2k_count, int use_ocb) + unsigned long s2k_count) { gcry_cipher_hd_t hd; const char *modestr; - unsigned char hashvalue[20]; - int blklen, enclen, outlen; + int enclen, outlen; unsigned char *iv = NULL; unsigned int ivsize; /* Size of the buffer allocated for IV. */ const unsigned char *s2ksalt; /* Points into IV. */ @@ -385,44 +384,26 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, *resultlen = 0; *result = NULL; - modestr = (use_ocb? "openpgp-s2k3-ocb-aes" - /* */: "openpgp-s2k3-sha1-" PROT_CIPHER_STRING "-cbc"); + modestr = "openpgp-s2k3-ocb-aes"; rc = gcry_cipher_open (&hd, PROT_CIPHER, - use_ocb? GCRY_CIPHER_MODE_OCB : - GCRY_CIPHER_MODE_CBC, + GCRY_CIPHER_MODE_OCB, GCRY_CIPHER_SECURE); if (rc) return rc; /* We need to work on a copy of the data because this makes it * easier to add the trailer and the padding and more important we - * have to prefix the text with 2 parenthesis. In CBC mode we - * have to allocate enough space for: - * - * (()(4:hash4:sha120:)) + padding - * - * we always append a full block of random bytes as padding but - * encrypt only what is needed for a full blocksize. In OCB mode we + * have to prefix the text with 2 parenthesis. Due to OCB mode we * have to allocate enough space for just: * * (()) */ - blklen = gcry_cipher_get_algo_blklen (PROT_CIPHER); - if (use_ocb) - { - /* (( )) */ - outlen = 2 + protlen + 2 ; - enclen = outlen + 16 /* taglen */; - outbuf = gcry_malloc_secure (enclen); - } - else - { - /* (( )( 4:hash 4:sha1 20: )) */ - outlen = 2 + protlen + 2 + 6 + 6 + 23 + 2 + blklen; - enclen = outlen/blklen * blklen; - outbuf = gcry_malloc_secure (outlen); - } + + /* (( )) */ + outlen = 2 + protlen + 2 ; + enclen = outlen + 16 /* taglen */; + outbuf = gcry_malloc_secure (enclen); if (!outbuf) { rc = out_of_core (); @@ -432,10 +413,10 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, /* Allocate a buffer for the nonce and the salt. */ if (!rc) { - /* Allocate random bytes to be used as IV, padding and s2k salt - * or in OCB mode for a nonce and the s2k salt. The IV/nonce is - * set later because for OCB we need to set the key first. */ - ivsize = (use_ocb? 12 : (blklen*2)) + 8; + /* Allocate random bytes to be used as nonce and s2k salt. The + * nonce is set later because for OCB we need to set the key + * first. */ + ivsize = 12 + 8; iv = xtrymalloc (ivsize); if (!iv) rc = gpg_error_from_syserror (); @@ -471,40 +452,17 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, goto leave; /* Set the IV/nonce. */ - rc = gcry_cipher_setiv (hd, iv, use_ocb? 12 : blklen); + rc = gcry_cipher_setiv (hd, iv, 12); if (rc) goto leave; - if (use_ocb) - { - /* In OCB Mode we use only the public key parameters as AAD. */ - rc = gcry_cipher_authenticate (hd, hashbegin, protbegin - hashbegin); - if (!rc) - rc = gcry_cipher_authenticate (hd, timestamp_exp, timestamp_exp_len); - if (!rc) - rc = gcry_cipher_authenticate - (hd, protbegin+protlen, hashlen - (protbegin+protlen - hashbegin)); - } - else - { - /* Hash the entire expression for CBC mode. Because - * TIMESTAMP_EXP won't get protected, we can't simply hash a - * continuous buffer but need to call md_write several times. */ - gcry_md_hd_t md; - - rc = gcry_md_open (&md, GCRY_MD_SHA1, 0 ); - if (!rc) - { - gcry_md_write (md, hashbegin, protbegin - hashbegin); - gcry_md_write (md, protbegin, protlen); - gcry_md_write (md, timestamp_exp, timestamp_exp_len); - gcry_md_write (md, protbegin+protlen, - hashlen - (protbegin+protlen - hashbegin)); - memcpy (hashvalue, gcry_md_read (md, GCRY_MD_SHA1), 20); - gcry_md_close (md); - } - } - + /* In OCB Mode we use only the public key parameters as AAD. */ + rc = gcry_cipher_authenticate (hd, hashbegin, protbegin - hashbegin); + if (!rc) + rc = gcry_cipher_authenticate (hd, timestamp_exp, timestamp_exp_len); + if (!rc) + rc = gcry_cipher_authenticate + (hd, protbegin+protlen, hashlen - (protbegin+protlen - hashbegin)); /* Encrypt. */ if (!rc) @@ -514,36 +472,15 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, *p++ = '('; memcpy (p, protbegin, protlen); p += protlen; - if (use_ocb) - { - *p++ = ')'; - *p++ = ')'; - } - else - { - memcpy (p, ")(4:hash4:sha120:", 17); - p += 17; - memcpy (p, hashvalue, 20); - p += 20; - *p++ = ')'; - *p++ = ')'; - memcpy (p, iv+blklen, blklen); /* Add padding. */ - p += blklen; - } - assert ( p - outbuf == outlen); - if (use_ocb) - { - gcry_cipher_final (hd); - rc = gcry_cipher_encrypt (hd, outbuf, outlen, NULL, 0); - if (!rc) - { - log_assert (outlen + 16 == enclen); - rc = gcry_cipher_gettag (hd, outbuf + outlen, 16); - } - } - else + *p++ = ')'; + *p++ = ')'; + log_assert ( p - outbuf == outlen); + gcry_cipher_final (hd); + rc = gcry_cipher_encrypt (hd, outbuf, outlen, NULL, 0); + if (!rc) { - rc = gcry_cipher_encrypt (hd, outbuf, enclen, NULL, 0); + log_assert (outlen + 16 == enclen); + rc = gcry_cipher_gettag (hd, outbuf + outlen, 16); } } @@ -554,24 +491,24 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, gcry_cipher_close (hd); /* Now allocate the buffer we want to return. This is - - (protected openpgp-s2k3-sha1-aes-cbc - ((sha1 salt no_of_iterations) 16byte_iv) - encrypted_octet_string) - - in canoncical format of course. We use asprintf and %n modifier - and dummy values as placeholders. */ + * + * (protected openpgp-s2k3-ocb-aes + * ((sha1 salt no_of_iterations) 12byte_nonce) + * encrypted_octet_string) + * + * in canoncical format of course. We use asprintf and %n modifier + * and dummy values as placeholders. */ { char countbuf[35]; snprintf (countbuf, sizeof countbuf, "%lu", - s2k_count ? s2k_count : get_standard_s2k_count ()); + s2k_count ? s2k_count : get_standard_s2k_count ()); p = xtryasprintf ("(9:protected%d:%s((4:sha18:%n_8bytes_%u:%s)%d:%n%*s)%d:%n%*s)", (int)strlen (modestr), modestr, &saltpos, (unsigned int)strlen (countbuf), countbuf, - use_ocb? 12 : blklen, &ivpos, use_ocb? 12 : blklen, "", + 12, &ivpos, 12, "", enclen, &encpos, enclen, ""); if (!p) { @@ -585,7 +522,7 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, *resultlen = strlen (p); *result = (unsigned char*)p; memcpy (p+saltpos, s2ksalt, 8); - memcpy (p+ivpos, iv, use_ocb? 12 : blklen); + memcpy (p+ivpos, iv, 12); memcpy (p+encpos, outbuf, enclen); xfree (iv); xfree (outbuf); @@ -601,13 +538,11 @@ do_encryption (const unsigned char *hashbegin, size_t hashlen, /* Protect the key encoded in canonical format in PLAINKEY. We assume - a valid S-Exp here. With USE_UCB set to -1 the default scheme is - used (ie. either CBC or OCB), set to 0 the old CBC mode is used, - and set to 1 OCB is used. */ + * a valid S-Exp here. */ int agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char **result, size_t *resultlen, - unsigned long s2k_count, int use_ocb) + unsigned long s2k_count) { int rc; const char *parmlist; @@ -624,9 +559,6 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, unsigned char *p; int have_curve = 0; - if (use_ocb == -1) - use_ocb = !!opt.enable_extended_key_format; - /* Create an S-expression with the protected-at timestamp. */ memcpy (timestamp_exp, "(12:protected-at15:", 19); gnupg_get_isotime (timestamp_exp+19); @@ -730,7 +662,7 @@ agent_protect (const unsigned char *plainkey, const char *passphrase, rc = do_encryption (hash_begin, hash_end - hash_begin + 1, prot_begin, prot_end - prot_begin + 1, passphrase, timestamp_exp, sizeof (timestamp_exp), - &protected, &protectedlen, s2k_count, use_ocb); + &protected, &protectedlen, s2k_count); if (rc) return rc; diff --git a/agent/t-protect.c b/agent/t-protect.c index 88b5525..e6edbff 100644 --- a/agent/t-protect.c +++ b/agent/t-protect.c @@ -196,7 +196,7 @@ test_agent_protect (void) { ret = agent_protect ((const unsigned char*)specs[i].key, specs[i].passphrase, - &specs[i].result, &specs[i].resultlen, 0, -1); + &specs[i].result, &specs[i].resultlen, 0); if (gpg_err_code (ret) != specs[i].ret_expected) { printf ("agent_protect(%d) returned '%i/%s'; expected '%i/%s'\n", diff --git a/agent/trustlist.c b/agent/trustlist.c index 086d8ae..5617370 100644 --- a/agent/trustlist.c +++ b/agent/trustlist.c @@ -45,6 +45,8 @@ struct trustitem_s int relax:1; /* Relax checking of root certificate constraints. */ int cm:1; /* Use chain model for validation. */ + int qual:1; /* Root CA for qualified signatures. */ + int de_vs:1; /* Root CA for de-vs compliant PKI. */ } flags; unsigned char fpr[20]; /* The binary fingerprint. */ }; @@ -322,6 +324,10 @@ read_one_trustfile (const char *fname, int systrust, ti->flags.relax = 1; else if (n == 2 && !memcmp (p, "cm", 2)) ti->flags.cm = 1; + else if (n == 4 && !memcmp (p, "qual", 4) && systrust) + ti->flags.qual = 1; + else if (n == 4 && !memcmp (p, "de-vs", 4) && systrust) + ti->flags.de_vs = 1; else log_error ("flag '%.*s' in '%s', line %d ignored\n", n, p, fname, lnr); @@ -474,17 +480,20 @@ istrusted_internal (ctrl_t ctrl, const char *fpr, int *r_disabled, in a locked state. */ if (already_locked) ; - else if (ti->flags.relax) + else if (ti->flags.relax || ti->flags.cm || ti->flags.qual + || ti->flags.de_vs) { unlock_trusttable (); locked = 0; - err = agent_write_status (ctrl, "TRUSTLISTFLAG", "relax", NULL); - } - else if (ti->flags.cm) - { - unlock_trusttable (); - locked = 0; - err = agent_write_status (ctrl, "TRUSTLISTFLAG", "cm", NULL); + err = 0; + if (ti->flags.relax) + err = agent_write_status (ctrl,"TRUSTLISTFLAG", "relax",NULL); + if (!err && ti->flags.cm) + err = agent_write_status (ctrl,"TRUSTLISTFLAG", "cm", NULL); + if (!err && ti->flags.qual) + err = agent_write_status (ctrl,"TRUSTLISTFLAG", "qual",NULL); + if (!err && ti->flags.de_vs) + err = agent_write_status (ctrl,"TRUSTLISTFLAG", "de-vs",NULL); } if (!err) @@ -646,7 +655,7 @@ agent_marktrusted (ctrl_t ctrl, const char *name, const char *fpr, int flag) if (!fname) return gpg_error_from_syserror (); - if ((ec = access (fname, W_OK)) && ec != GPG_ERR_ENOENT) + if ((ec = gnupg_access (fname, W_OK)) && ec != GPG_ERR_ENOENT) { xfree (fname); return gpg_error (GPG_ERR_EPERM); -- cgit v1.2.3