diff --git a/g10/Makefile.am b/g10/Makefile.am index 884b4749b..1404014e9 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -1,260 +1,266 @@ # Copyright (C) 1998, 1999, 2000, 2001, 2002, # 2003, 2006, 2010 Free Software Foundation, Inc. # # This file is part of GnuPG. # # GnuPG is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # GnuPG is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, see . ## Process this file with automake to produce Makefile.in EXTRA_DIST = distsigkey.gpg \ ChangeLog-2011 gpg-w32info.rc \ gpg.w32-manifest.in test.c t-keydb-keyring.kbx \ t-keydb-get-keyblock.gpg t-stutter-data.asc \ all-tests.scm AM_CPPFLAGS = include $(top_srcdir)/am/cmacros.am AM_CFLAGS = $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS) \ $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) needed_libs = ../kbx/libkeybox.a $(libcommon) # Because there are no program specific transform macros we need to # work around that to allow installing gpg as gpg2. gpg2_hack_list = gpg gpgv if USE_GPG2_HACK gpg2_hack_uninst = gpg2 gpgv2 use_gpg2_hack = yes else gpg2_hack_uninst = $(gpg2_hack_list) use_gpg2_hack = no endif # NB: We use noinst_ for gpg and gpgv so that we can install them with # the install-hook target under the name gpg2/gpgv2. noinst_PROGRAMS = gpg if !HAVE_W32CE_SYSTEM noinst_PROGRAMS += gpgv endif -if MAINTAINER_MODE -noinst_PROGRAMS += gpgcompose -endif +#if MAINTAINER_MODE +#noinst_PROGRAMS += gpgcompose +#endif noinst_PROGRAMS += $(module_tests) TESTS = $(module_tests) TESTS_ENVIRONMENT = \ abs_top_srcdir=$(abs_top_srcdir) if ENABLE_BZIP2_SUPPORT bzip2_source = compress-bz2.c else bzip2_source = endif if ENABLE_CARD_SUPPORT card_source = card-util.c else card_source = endif if NO_TRUST_MODELS trust_source = else trust_source = trustdb.c trustdb.h tdbdump.c tdbio.c tdbio.h endif if USE_TOFU tofu_source = tofu.h tofu.c gpgsql.c gpgsql.h else tofu_source = endif if HAVE_W32_SYSTEM resource_objs += gpg-w32info.o gpg-w32info.o : gpg.w32-manifest endif common_source = \ gpg.h \ dek.h \ build-packet.c \ compress.c \ $(bzip2_source) \ filter.h \ free-packet.c \ getkey.c \ - keydb.c keydb.h \ + keydb.h \ + keydb-private.h \ + call-keyboxd.c \ + keydb.c \ keyring.c keyring.h \ seskey.c \ kbnode.c \ main.h \ mainproc.c \ armor.c \ mdfilter.c \ textfilter.c \ progress.c \ misc.c \ rmd160.c rmd160.h \ options.h \ openfile.c \ keyid.c \ packet.h \ parse-packet.c \ cpr.c \ plaintext.c \ sig-check.c \ keylist.c \ pkglue.c pkglue.h \ objcache.c objcache.h \ ecdh.c gpg_sources = server.c \ $(common_source) \ pkclist.c \ skclist.c \ pubkey-enc.c \ passphrase.c \ decrypt.c \ decrypt-data.c \ cipher-cfb.c \ cipher-aead.c \ encrypt.c \ sign.c \ verify.c \ revoke.c \ dearmor.c \ import.c \ export.c \ migrate.c \ delkey.c \ keygen.c \ helptext.c \ keyserver.c \ keyserver-internal.h \ call-dirmngr.c call-dirmngr.h \ photoid.c photoid.h \ call-agent.c call-agent.h \ trust.c $(trust_source) $(tofu_source) \ $(card_source) \ exec.c exec.h \ key-clean.c key-clean.h \ key-check.c key-check.h gpg_SOURCES = gpg.c \ keyedit.c keyedit.h \ $(gpg_sources) -gpgcompose_SOURCES = gpgcompose.c $(gpg_sources) +#gpgcompose_SOURCES = gpgcompose.c $(gpg_sources) gpgv_SOURCES = gpgv.c \ $(common_source) \ verify.c #gpgd_SOURCES = gpgd.c \ # ks-proto.h \ # ks-proto.c \ # ks-db.c \ # ks-db.h \ # $(common_source) LDADD = $(needed_libs) ../common/libgpgrl.a \ $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) gpg_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(resource_objs) $(extra_sys_libs) gpg_LDFLAGS = $(extra_bin_ldflags) gpgv_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ - $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(resource_objs) $(extra_sys_libs) gpgv_LDFLAGS = $(extra_bin_ldflags) -gpgcompose_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ - $(LIBICONV) $(resource_objs) $(extra_sys_libs) -gpgcompose_LDFLAGS = $(extra_bin_ldflags) +#gpgcompose_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ +# $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ +# $(LIBICONV) $(resource_objs) $(extra_sys_libs) +#gpgcompose_LDFLAGS = $(extra_bin_ldflags) t_common_ldadd = module_tests = t-rmd160 t-keydb t-keydb-get-keyblock t-stutter t_rmd160_SOURCES = t-rmd160.c rmd160.c t_rmd160_LDADD = $(t_common_ldadd) t_keydb_SOURCES = t-keydb.c test-stubs.c $(common_source) -t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ +t_keydb_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(t_common_ldadd) t_keydb_get_keyblock_SOURCES = t-keydb-get-keyblock.c test-stubs.c \ $(common_source) -t_keydb_get_keyblock_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ +t_keydb_get_keyblock_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(t_common_ldadd) t_stutter_SOURCES = t-stutter.c test-stubs.c \ $(common_source) -t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) $(GPG_ERROR_LIBS) \ +t_stutter_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ + $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(t_common_ldadd) $(PROGRAMS): $(needed_libs) ../common/libgpgrl.a # NB: To install gpg and gpgv we use this -hook. This code has to # duplicate most of the automake generated install-binPROGRAMS target # so that directories are created and the transform feature works. install-exec-hook: @echo "running install-exec-hook"; \ echo " $(MKDIR_P) '$(DESTDIR)$(bindir)'"; \ $(MKDIR_P) "$(DESTDIR)$(bindir)"; \ for p in $(gpg2_hack_list); do \ echo "$$p$(EXEEXT) $$p$(EXEEXT)"; done | \ sed 's/$(EXEEXT)$$//' | \ while read p p1; do if test -f $$p \ ; then echo "$$p"; echo "$$p"; else :; fi; \ done | \ sed -e 'p;s,.*/,,;n;h' \ -e 's|.*|.|' \ -e 'p;x;s,.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/' | \ sed 'N;N;N;s,\n, ,g' | \ $(AWK) 'BEGIN { files["."] = ""; dirs["."] = 1 } \ { d=$$3; if (dirs[d] != 1) { print "d", d; dirs[d] = 1 } \ if ($$2 == $$4) files[d] = files[d] " " $$1; \ else { print "f", $$3 "/" $$4, $$1; } } \ END { for (d in files) print "f", d, files[d] }' | \ while read type dir files; do \ for f in $$files; do \ if test $(use_gpg2_hack) = yes ; \ then f2=`echo "$${f}" | sed 's/$(EXEEXT)$$//'`2$(EXEEXT); \ else f2="$${f}" ;\ fi ; \ echo "$(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) \ $${f} '$(DESTDIR)$(bindir)/$${f2}'"; \ $(INSTALL_PROGRAM_ENV) $(INSTALL_PROGRAM) \ $${f} "$(DESTDIR)$(bindir)/$${f2}"; \ done; \ done install-data-local: $(mkinstalldirs) $(DESTDIR)$(pkgdatadir) $(INSTALL_DATA) $(srcdir)/distsigkey.gpg \ $(DESTDIR)$(pkgdatadir)/distsigkey.gpg # NB: For uninstalling gpg and gpgv we use -local because there is # no need for a specific order the targets need to be run. uninstall-local: -@rm $(DESTDIR)$(pkgdatadir)/distsigkey.gpg -@files=`for p in $(gpg2_hack_uninst); do echo "$$p"; done | \ sed -e 'h;s,^.*/,,;s/$(EXEEXT)$$//;$(transform)' \ -e 's/$$/$(EXEEXT)/' \ `; \ echo " ( cd '$(DESTDIR)$(bindir)' && rm -f" $$files ")"; \ cd "$(DESTDIR)$(bindir)" && rm -f $$files diff --git a/g10/call-keyboxd.c b/g10/call-keyboxd.c new file mode 100644 index 000000000..f05a9e85f --- /dev/null +++ b/g10/call-keyboxd.c @@ -0,0 +1,842 @@ +/* call-keyboxd.c - Access to the keyboxd storage server + * Copyright (C) 2019 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include +#include +#include +#include +#include +#include +#include +#ifdef HAVE_LOCALE_H +# include +#endif + +#include "gpg.h" +#include +#include "../common/util.h" +#include "../common/membuf.h" +#include "options.h" +#include "../common/i18n.h" +#include "../common/asshelp.h" +#include "../common/status.h" +#include "keydb.h" + +#include "keydb-private.h" /* For struct keydb_handle_s */ + + +/* Data used to keep track of keybox daemon sessions. This allows us + * to use several sessions with the keyboxd and also to re-use already + * established sessions. Note that gpg.h defines the type + * keyboxd_local_t for this structure. */ +struct keyboxd_local_s +{ + /* Link to other keyboxd contexts which are used simultaneously. */ + struct keyboxd_local_s *next; + + /* The active Assuan context. */ + assuan_context_t ctx; + + /* This flag set while an operation is running on this context. */ + unsigned int is_active : 1; + + /* This flag is set to record that the standard per session init has + * been done. */ + unsigned int per_session_init_done : 1; +}; + + + + +/* Deinitialize all session resources pertaining to the keyboxd. */ +void +gpg_keyboxd_deinit_session_data (ctrl_t ctrl) +{ + keyboxd_local_t kbl; + + while ((kbl = ctrl->keyboxd_local)) + { + ctrl->keyboxd_local = kbl->next; + if (kbl->is_active) + log_error ("oops: trying to cleanup an active keyboxd context\n"); + else + assuan_release (kbl->ctx); + xfree (kbl); + } +} + + +/* Print a warning if the server's version number is less than our + version number. Returns an error code on a connection problem. */ +static gpg_error_t +warn_version_mismatch (assuan_context_t ctx, const char *servername) +{ + gpg_error_t err; + char *serverversion; + const char *myversion = strusage (13); + + err = get_assuan_server_version (ctx, 0, &serverversion); + if (err) + log_error (_("error getting version from '%s': %s\n"), + servername, gpg_strerror (err)); + else if (compare_version_strings (serverversion, myversion) < 0) + { + char *warn; + + warn = xtryasprintf (_("server '%s' is older than us (%s < %s)"), + servername, serverversion, myversion); + if (!warn) + err = gpg_error_from_syserror (); + else + { + log_info (_("WARNING: %s\n"), warn); + if (!opt.quiet) + { + log_info (_("Note: Outdated servers may lack important" + " security fixes.\n")); + log_info (_("Note: Use the command \"%s\" to restart them.\n"), + "gpgconf --kill all"); + } + + write_status_strings (STATUS_WARNING, "server_version_mismatch 0", + " ", warn, NULL); + xfree (warn); + } + } + xfree (serverversion); + return err; +} + + +/* Connect to the keybox daemon and launch it if necessary. Handle + * the server's initial greeting and set global options. Returns a + * new assuan context or an error. */ +static gpg_error_t +create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) +{ + gpg_error_t err; + assuan_context_t ctx; + + *r_ctx = NULL; + + err = start_new_keyboxd (&ctx, + GPG_ERR_SOURCE_DEFAULT, + opt.keyboxd_program, + opt.autostart, opt.verbose, DBG_IPC, + NULL, ctrl); + if (!opt.autostart && gpg_err_code (err) == GPG_ERR_NO_KEYBOXD) + { + static int shown; + + if (!shown) + { + shown = 1; + log_info (_("no keyboxd running in this session\n")); + } + } + else if (!err && !(err = warn_version_mismatch (ctx, KEYBOXD_NAME))) + { + /* Place to emit global options. */ + } + + if (err) + assuan_release (ctx); + else + *r_ctx = ctx; + + return err; +} + + +/* Get a context for accessing keyboxd. If no context is available a + * new one is created and if necessary keyboxd is started. On success + * an assuan context is stored at R_CTX. This context may only be + * released by means of close_context. Note that NULL is stored at + * R_CTX on error. */ +static gpg_error_t +open_context (ctrl_t ctrl, assuan_context_t *r_ctx) +{ + gpg_error_t err; + keyboxd_local_t kbl; + + *r_ctx = NULL; + for (;;) + { + for (kbl = ctrl->keyboxd_local; kbl && kbl->is_active; kbl = kbl->next) + ; + if (kbl) + { + /* Found an inactive keyboxd session - return that. */ + log_assert (!kbl->is_active); + + /* But first do the per session init if not yet done. */ + if (!kbl->per_session_init_done) + { + kbl->per_session_init_done = 1; + } + + kbl->is_active = 1; + + *r_ctx = kbl->ctx; + return 0; + } + + /* None found. Create a new session and retry. */ + kbl = xtrycalloc (1, sizeof *kbl); + if (!kbl) + return gpg_error_from_syserror (); + err = create_new_context (ctrl, &kbl->ctx); + if (err) + { + xfree (kbl); + return err; + } + + /* Although we are not yet using nPth in gpg we better prepare + * to be nPth thread safe. Thus we add it to the list and + * retry; this is easier than to employ a lock. */ + kbl->next = ctrl->keyboxd_local; + ctrl->keyboxd_local = kbl; + } +} + + +/* Close the assuan context CTX and return it to a pool of unused + * contexts. If CTX is NULL, the function does nothing. */ +static void +close_context (ctrl_t ctrl, assuan_context_t ctx) +{ + keyboxd_local_t kbl; + + if (!ctx) + return; + + for (kbl = ctrl->keyboxd_local; kbl; kbl = kbl->next) + { + if (kbl->ctx == ctx) + { + if (!kbl->is_active) + log_fatal ("closing inactive keyboxd context %p\n", ctx); + kbl->is_active = 0; + return; + } + } + log_fatal ("closing unknown keyboxd ctx %p\n", ctx); +} + + + +/* Create a new database handle. A database handle is similar to a + * file handle: it contains a local file position. This is used when + * searching: subsequent searches resume where the previous search + * left off. To rewind the position, use keydb_search_reset(). This + * function returns NULL on error, sets ERRNO, and prints an error + * diagnostic. Depending on --use-keyboxd either the old internal + * keydb code is used (keydb.c) or, if set, the processing is diverted + * to the keyboxd. */ +/* FIXME: We should change the interface to return a gpg_error_t. */ +KEYDB_HANDLE +keydb_new (ctrl_t ctrl) +{ + gpg_error_t err; + KEYDB_HANDLE hd; + + if (DBG_CLOCK) + log_clock ("keydb_new"); + + hd = xtrycalloc (1, sizeof *hd); + if (!hd) + { + err = gpg_error_from_syserror (); + goto leave; + } + + if (!opt.use_keyboxd) + { + err = internal_keydb_init (hd); + goto leave; + } + hd->use_keyboxd = 1; + hd->ctrl = ctrl; + hd->need_search_reset = 1; + + err = open_context (ctrl, &hd->assuan_context); + if (err) + goto leave; + + leave: + if (err) + { + int rc; + log_error (_("error opening key DB: %s\n"), gpg_strerror (err)); + xfree (hd); + hd = NULL; + if (!(rc = gpg_err_code_to_errno (err))) + rc = gpg_err_code_to_errno (GPG_ERR_EIO); + gpg_err_set_errno (rc); + } + return hd; +} + + +/* Release a keydb handle. */ +void +keydb_release (KEYDB_HANDLE hd) +{ + if (!hd) + return; + + if (!hd->use_keyboxd) + internal_keydb_deinit (hd); + else + { + close_context (hd->ctrl, hd->assuan_context); + } + xfree (hd); +} + + +/* Take a lock if we are not using the keyboxd. */ +gpg_error_t +keydb_lock (KEYDB_HANDLE hd) +{ + if (!hd) + return gpg_error (GPG_ERR_INV_ARG); + + if (!hd->use_keyboxd) + return internal_keydb_lock (hd); + + return 0; +} + + + +/* FIXME: This helper is duplicates code of partse_keyblock_image. */ +static gpg_error_t +keydb_get_keyblock_do_parse (iobuf_t iobuf, int pk_no, int uid_no, + kbnode_t *r_keyblock) +{ + gpg_error_t err; + struct parse_packet_ctx_s parsectx; + PACKET *pkt; + kbnode_t keyblock = NULL; + kbnode_t node, *tail; + int in_cert, save_mode; + int pk_count, uid_count; + + *r_keyblock = NULL; + + pkt = xtrymalloc (sizeof *pkt); + if (!pkt) + return gpg_error_from_syserror (); + init_packet (pkt); + init_parse_packet (&parsectx, iobuf); + save_mode = set_packet_list_mode (0); + in_cert = 0; + tail = NULL; + pk_count = uid_count = 0; + while ((err = parse_packet (&parsectx, pkt)) != -1) + { + if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET) + { + free_packet (pkt, &parsectx); + init_packet (pkt); + continue; + } + if (err) + { + es_fflush (es_stdout); + log_error ("parse_keyblock_image: read error: %s\n", + gpg_strerror (err)); + if (gpg_err_code (err) == GPG_ERR_INV_PACKET) + { + free_packet (pkt, &parsectx); + init_packet (pkt); + continue; + } + err = gpg_error (GPG_ERR_INV_KEYRING); + break; + } + + /* Filter allowed packets. */ + switch (pkt->pkttype) + { + case PKT_PUBLIC_KEY: + case PKT_PUBLIC_SUBKEY: + case PKT_SECRET_KEY: + case PKT_SECRET_SUBKEY: + case PKT_USER_ID: + case PKT_ATTRIBUTE: + case PKT_SIGNATURE: + case PKT_RING_TRUST: + break; /* Allowed per RFC. */ + + default: + log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype); + free_packet(pkt, &parsectx); + init_packet(pkt); + continue; + } + + /* Other sanity checks. */ + if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY) + { + log_error ("parse_keyblock_image: first packet in a keybox blob " + "is not a public key packet\n"); + err = gpg_error (GPG_ERR_INV_KEYRING); + break; + } + if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY + || pkt->pkttype == PKT_SECRET_KEY)) + { + log_error ("parse_keyblock_image: " + "multiple keyblocks in a keybox blob\n"); + err = gpg_error (GPG_ERR_INV_KEYRING); + break; + } + in_cert = 1; + + node = new_kbnode (pkt); + + switch (pkt->pkttype) + { + case PKT_PUBLIC_KEY: + case PKT_PUBLIC_SUBKEY: + case PKT_SECRET_KEY: + case PKT_SECRET_SUBKEY: + if (++pk_count == pk_no) + node->flag |= 1; + break; + + case PKT_USER_ID: + if (++uid_count == uid_no) + node->flag |= 2; + break; + + default: + break; + } + + if (!keyblock) + keyblock = node; + else + *tail = node; + tail = &node->next; + pkt = xtrymalloc (sizeof *pkt); + if (!pkt) + { + err = gpg_error_from_syserror (); + break; + } + init_packet (pkt); + } + set_packet_list_mode (save_mode); + + if (err == -1 && keyblock) + err = 0; /* Got the entire keyblock. */ + + if (err) + release_kbnode (keyblock); + else + { + *r_keyblock = keyblock; + } + free_packet (pkt, &parsectx); + deinit_parse_packet (&parsectx); + xfree (pkt); + return err; +} + + +/* Return the keyblock last found by keydb_search() in *RET_KB. + * + * On success, the function returns 0 and the caller must free *RET_KB + * using release_kbnode(). Otherwise, the function returns an error + * code. + * + * The returned keyblock has the kbnode flag bit 0 set for the node + * with the public key used to locate the keyblock or flag bit 1 set + * for the user ID node. */ +gpg_error_t +keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb) +{ + gpg_error_t err; + int pk_no, uid_no; + + *ret_kb = NULL; + + if (!hd) + return gpg_error (GPG_ERR_INV_ARG); + + if (DBG_CLOCK) + log_clock ("%s enter", __func__); + + if (!hd->use_keyboxd) + { + err = internal_keydb_get_keyblock (hd, ret_kb); + goto leave; + } + + if (!hd->search_result) + { + err = gpg_error (GPG_ERR_VALUE_NOT_FOUND); + goto leave; + } + + pk_no = uid_no = 0; /*FIXME: Get this from the keyboxd. */ + err = keydb_get_keyblock_do_parse (hd->search_result, pk_no, uid_no, ret_kb); + /* In contrast to the old code we close the iobuf here and thus this + * function may be called only once to get a keyblock. */ + iobuf_close (hd->search_result); + hd->search_result = NULL; + + + leave: + if (DBG_CLOCK) + log_clock ("%s leave%s", __func__, err? " (failed)":""); + return err; +} + + +/* Update the keyblock KB (i.e., extract the fingerprint and find the + * corresponding keyblock in the keyring). + * + * This doesn't do anything if --dry-run was specified. + * + * Returns 0 on success. Otherwise, it returns an error code. Note: + * if there isn't a keyblock in the keyring corresponding to KB, then + * this function returns GPG_ERR_VALUE_NOT_FOUND. + * + * This function selects the matching record and modifies the current + * file position to point to the record just after the selected entry. + * Thus, if you do a subsequent search using HD, you should first do a + * keydb_search_reset. Further, if the selected record is important, + * you should use keydb_push_found_state and keydb_pop_found_state to + * save and restore it. */ +gpg_error_t +keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) +{ + gpg_error_t err; + + log_assert (kb); + log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); + + if (!hd) + return gpg_error (GPG_ERR_INV_ARG); + + if (!hd->use_keyboxd) + { + err = internal_keydb_update_keyblock (ctrl, hd, kb); + goto leave; + } + + err = GPG_ERR_NOT_IMPLEMENTED; + + leave: + return err; +} + + +/* Insert a keyblock into one of the underlying keyrings or keyboxes. + * + * Be default, the keyring / keybox from which the last search result + * came is used. If there was no previous search result (or + * keydb_search_reset was called), then the keyring / keybox where the + * next search would start is used (i.e., the current file position). + * + * Note: this doesn't do anything if --dry-run was specified. + * + * Returns 0 on success. Otherwise, it returns an error code. */ +gpg_error_t +keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb) +{ + gpg_error_t err; + + if (!hd) + return gpg_error (GPG_ERR_INV_ARG); + + if (!hd->use_keyboxd) + { + err = internal_keydb_insert_keyblock (hd, kb); + goto leave; + } + + err = GPG_ERR_NOT_IMPLEMENTED; + + leave: + return err; +} + + +/* Delete the currently selected keyblock. If you haven't done a + * search yet on this database handle (or called keydb_search_reset), + * then this function returns an error. + * + * Returns 0 on success or an error code, if an error occured. */ +gpg_error_t +keydb_delete_keyblock (KEYDB_HANDLE hd) +{ + gpg_error_t err; + + if (!hd) + return gpg_error (GPG_ERR_INV_ARG); + + if (!hd->use_keyboxd) + { + err = internal_keydb_delete_keyblock (hd); + goto leave; + } + + err = GPG_ERR_NOT_IMPLEMENTED; + + leave: + return err; +} + + +/* Clears the current search result and resets the handle's position + * so that the next search starts at the beginning of the database. + * + * Returns 0 on success and an error code if an error occurred. */ +gpg_error_t +keydb_search_reset (KEYDB_HANDLE hd) +{ + gpg_error_t err; + + if (!hd) + return gpg_error (GPG_ERR_INV_ARG); + + if (DBG_CLOCK) + log_clock ("%s", __func__); + if (DBG_CACHE) + log_debug ("%s (hd=%p)", __func__, hd); + + if (!hd->use_keyboxd) + { + err = internal_keydb_search_reset (hd); + goto leave; + } + + /* All we need todo is to tell search that a reset is pending. Noet + * that keydb_new sets this flag as well. */ + hd->need_search_reset = 1; + err = 0; + + leave: + return err; +} + + +/* Search the database for keys matching the search description. If + * the DB contains any legacy keys, these are silently ignored. + * + * DESC is an array of search terms with NDESC entries. The search + * terms are or'd together. That is, the next entry in the DB that + * matches any of the descriptions will be returned. + * + * Note: this function resumes searching where the last search left + * off (i.e., at the current file position). If you want to search + * from the start of the database, then you need to first call + * keydb_search_reset(). + * + * If no key matches the search description, returns + * GPG_ERR_NOT_FOUND. If there was a match, returns 0. If an error + * occurred, returns an error code. + * + * The returned key is considered to be selected and the raw data can, + * for instance, be returned by calling keydb_get_keyblock(). */ +gpg_error_t +keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, + size_t ndesc, size_t *descindex) +{ + gpg_error_t err; + int i; + char line[ASSUAN_LINELENGTH]; + + + if (!hd) + return gpg_error (GPG_ERR_INV_ARG); + + if (descindex) + *descindex = 0; /* Make sure it is always set on return. */ + + if (DBG_CLOCK) + log_clock ("%s enter", __func__); + + if (DBG_LOOKUP) + { + log_debug ("%s: %zd search descriptions:\n", __func__, ndesc); + for (i = 0; i < ndesc; i ++) + { + char *t = keydb_search_desc_dump (&desc[i]); + log_debug ("%s %d: %s\n", __func__, i, t); + xfree (t); + } + } + + if (!hd->use_keyboxd) + { + err = internal_keydb_search (hd, desc, ndesc, descindex); + goto leave; + } + + + if (hd->search_result) + { + iobuf_close (hd->search_result); + hd->search_result = NULL; + } + + if (!hd->need_search_reset) + { + /* No reset requested thus continue the search. The keyboxd + * keeps the context of the search and thus the NEXT operates on + * the last search pattern. This is how we always used the + * keydb.c functions. In theory we were able to modify the + * search pattern between searches but that is not anymore + * supported by keyboxd and a cursory check does not show that + * we actually made used of that misfeature. */ + snprintf (line, sizeof line, "NEXT"); + goto do_search; + } + + hd->need_search_reset = 0; + + if (!ndesc) + { + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + /* FIXME: Implement --multi */ + switch (desc->mode) + { + case KEYDB_SEARCH_MODE_EXACT: + snprintf (line, sizeof line, "SEARCH =%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_SUBSTR: + snprintf (line, sizeof line, "SEARCH *%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_MAIL: + snprintf (line, sizeof line, "SEARCH <%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_MAILSUB: + snprintf (line, sizeof line, "SEARCH @%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_MAILEND: + snprintf (line, sizeof line, "SEARCH .%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_WORDS: + snprintf (line, sizeof line, "SEARCH +%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_SHORT_KID: + snprintf (line, sizeof line, "SEARCH 0x%08lX", + (ulong)desc->u.kid[1]); + break; + + case KEYDB_SEARCH_MODE_LONG_KID: + snprintf (line, sizeof line, "SEARCH 0x%08lX%08lX", + (ulong)desc->u.kid[0], (ulong)desc->u.kid[1]); + break; + + case KEYDB_SEARCH_MODE_FPR: + { + unsigned char hexfpr[MAX_FINGERPRINT_LEN * 2 + 1]; + log_assert (desc[0].fprlen <= MAX_FINGERPRINT_LEN); + bin2hex (desc[0].u.fpr, desc[0].fprlen, hexfpr); + snprintf (line, sizeof line, "SEARCH 0x%s", hexfpr); + } + break; + + case KEYDB_SEARCH_MODE_ISSUER: + snprintf (line, sizeof line, "SEARCH #/%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_ISSUER_SN: + case KEYDB_SEARCH_MODE_SN: + snprintf (line, sizeof line, "SEARCH #%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_SUBJECT: + snprintf (line, sizeof line, "SEARCH /%s", desc[0].u.name); + break; + + case KEYDB_SEARCH_MODE_KEYGRIP: + { + unsigned char hexgrip[KEYGRIP_LEN * 2 + 1]; + bin2hex (desc[0].u.grip, KEYGRIP_LEN, hexgrip); + snprintf (line, sizeof line, "SEARCH &%s", hexgrip); + } + break; + + case KEYDB_SEARCH_MODE_FIRST: + snprintf (line, sizeof line, "SEARCH"); + break; + + case KEYDB_SEARCH_MODE_NEXT: + log_debug ("%s: mode next - we should not get to here!\n", __func__); + snprintf (line, sizeof line, "NEXT"); + break; + + default: + err = gpg_error (GPG_ERR_INV_ARG); + goto leave; + } + + do_search: + { + membuf_t data; + void *buffer; + size_t len; + + init_membuf (&data, 8192); + err = assuan_transact (hd->assuan_context, line, + put_membuf_cb, &data, + NULL, NULL, + NULL, NULL); + if (err) + { + xfree (get_membuf (&data, &len)); + goto leave; + } + + buffer = get_membuf (&data, &len); + if (!buffer) + { + err = gpg_error_from_syserror (); + goto leave; + } + + /* Fixme: Avoid double allocation. */ + hd->search_result = iobuf_temp_with_content (buffer, len); + } + + + leave: + if (DBG_CLOCK) + log_clock ("%s leave (%sfound)", __func__, err? "not ":""); + return err; +} diff --git a/g10/delkey.c b/g10/delkey.c index b5ab47434..8a7144ace 100644 --- a/g10/delkey.c +++ b/g10/delkey.c @@ -1,379 +1,379 @@ /* delkey.c - delete keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004, * 2005, 2006 Free Software Foundation, Inc. * Copyright (C) 2014, 2019 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "../common/iobuf.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "trustdb.h" #include "filter.h" #include "../common/ttyio.h" #include "../common/i18n.h" #include "call-agent.h" /**************** * Delete a public or secret key from a keyring. * r_sec_avail will be set if a secret key is available and the public * key can't be deleted for that reason. */ static gpg_error_t do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, int *r_sec_avail) { gpg_error_t err; kbnode_t keyblock = NULL; kbnode_t node, kbctx; kbnode_t targetnode; KEYDB_HANDLE hd; PKT_public_key *pk = NULL; u32 keyid[2]; int okay=0; int yes; KEYDB_SEARCH_DESC desc; int exactmatch; /* True if key was found by fingerprint. */ int thiskeyonly; /* 0 = false, 1 = is primary key, 2 = is a subkey. */ *r_sec_avail = 0; - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) return gpg_error_from_syserror (); /* Search the userid. */ err = classify_user_id (username, &desc, 1); exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR); thiskeyonly = desc.exact; if (!err) err = keydb_search (hd, &desc, 1, NULL); if (err) { log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err)); write_status_text (STATUS_DELETE_PROBLEM, "1"); goto leave; } /* Read the keyblock. */ err = keydb_get_keyblock (hd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err) ); goto leave; } /* Get the keyid from the keyblock. */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if (!node) { log_error ("Oops; key not found anymore!\n"); err = gpg_error (GPG_ERR_GENERAL); goto leave; } /* If an operation only on a subkey is requested, find that subkey * now. */ if (thiskeyonly) { kbnode_t tmpnode; for (kbctx=NULL; (tmpnode = walk_kbnode (keyblock, &kbctx, 0)); ) { if (!(tmpnode->pkt->pkttype == PKT_PUBLIC_KEY || tmpnode->pkt->pkttype == PKT_PUBLIC_SUBKEY)) continue; if (exact_subkey_match_p (&desc, tmpnode)) break; } if (!tmpnode) { log_error ("Oops; requested subkey not found anymore!\n"); err = gpg_error (GPG_ERR_GENERAL); goto leave; } /* Set NODE to this specific subkey or primary key. */ thiskeyonly = node == tmpnode? 1 : 2; targetnode = tmpnode; } else targetnode = node; pk = targetnode->pkt->pkt.public_key; keyid_from_pk (pk, keyid); if (!secret && !force) { - if (have_secret_key_with_kid (keyid)) + if (have_secret_key_with_kid (ctrl, keyid)) { *r_sec_avail = 1; err = gpg_error (GPG_ERR_EOF); goto leave; } else err = 0; } - if (secret && !have_secret_key_with_kid (keyid)) + if (secret && !have_secret_key_with_kid (ctrl, keyid)) { err = gpg_error (GPG_ERR_NOT_FOUND); log_error (_("key \"%s\" not found\n"), username); write_status_text (STATUS_DELETE_PROBLEM, "1"); goto leave; } if (opt.batch && exactmatch) okay++; else if (opt.batch && secret) { log_error(_("can't do this in batch mode\n")); log_info (_("(unless you specify the key by fingerprint)\n")); } else if (opt.batch && opt.answer_yes) okay++; else if (opt.batch) { log_error(_("can't do this in batch mode without \"--yes\"\n")); log_info (_("(unless you specify the key by fingerprint)\n")); } else { print_key_info (ctrl, NULL, 0, pk, secret); tty_printf ("\n"); if (thiskeyonly == 1 && !secret) { /* We need to delete the entire public key despite the use * of the thiskeyonly request. */ tty_printf (_("Note: The public primary key and all its subkeys" " will be deleted.\n")); } else if (thiskeyonly == 2 && !secret) { tty_printf (_("Note: Only the shown public subkey" " will be deleted.\n")); } if (thiskeyonly == 1 && secret) { tty_printf (_("Note: Only the secret part of the shown primary" " key will be deleted.\n")); } else if (thiskeyonly == 2 && secret) { tty_printf (_("Note: Only the secret part of the shown subkey" " will be deleted.\n")); } if (thiskeyonly) tty_printf ("\n"); yes = cpr_get_answer_is_yes (secret? "delete_key.secret.okay": "delete_key.okay", _("Delete this key from the keyring? (y/N) ")); if (!cpr_enabled() && secret && yes) { /* I think it is not required to check a passphrase; if the * user is so stupid as to let others access his secret * keyring (and has no backup) - it is up him to read some * very basic texts about security. */ yes = cpr_get_answer_is_yes ("delete_key.secret.okay", _("This is a secret key! - really delete? (y/N) ")); } if (yes) okay++; } if (okay) { if (secret) { char *prompt; gpg_error_t firsterr = 0; char *hexgrip; setup_main_keyids (keyblock); for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (!(node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) continue; if (thiskeyonly && targetnode != node) continue; if (agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) continue; /* No secret key for that public (sub)key. */ prompt = gpg_format_keydesc (ctrl, node->pkt->pkt.public_key, FORMAT_KEYDESC_DELKEY, 1); err = hexkeygrip_from_pk (node->pkt->pkt.public_key, &hexgrip); /* NB: We require --yes to advise the agent not to * request a confirmation. The rationale for this extra * pre-caution is that since 2.1 the secret key may also * be used for other protocols and thus deleting it from * the gpg would also delete the key for other tools. */ if (!err && !opt.dry_run) err = agent_delete_key (NULL, hexgrip, prompt, opt.answer_yes); xfree (prompt); xfree (hexgrip); if (err) { if (gpg_err_code (err) == GPG_ERR_KEY_ON_CARD) write_status_text (STATUS_DELETE_PROBLEM, "1"); log_error (_("deleting secret %s failed: %s\n"), (node->pkt->pkttype == PKT_PUBLIC_KEY ? _("key"):_("subkey")), gpg_strerror (err)); if (!firsterr) firsterr = err; if (gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) { write_status_error ("delete_key.secret", err); break; } } } err = firsterr; if (firsterr) goto leave; } else if (thiskeyonly == 2) { int selected = 0; /* Delete the specified public subkey. */ for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (thiskeyonly && targetnode != node) continue; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { selected = targetnode == node; if (selected) delete_kbnode (node); } else if (selected && node->pkt->pkttype == PKT_SIGNATURE) delete_kbnode (node); else selected = 0; } commit_kbnode (&keyblock); err = keydb_update_keyblock (ctrl, hd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } } else { err = keydb_delete_keyblock (hd); if (err) { log_error (_("deleting keyblock failed: %s\n"), gpg_strerror (err)); goto leave; } } /* Note that the ownertrust being cleared will trigger a revalidation_mark(). This makes sense - only deleting keys that have ownertrust set should trigger this. */ if (!secret && pk && !opt.dry_run && thiskeyonly != 2 && clear_ownertrusts (ctrl, pk)) { if (opt.verbose) log_info (_("ownertrust information cleared\n")); } } leave: keydb_release (hd); release_kbnode (keyblock); return err; } /* * Delete a public or secret key from a keyring. */ gpg_error_t delete_keys (ctrl_t ctrl, strlist_t names, int secret, int allow_both) { gpg_error_t err; int avail; int force = (!allow_both && !secret && opt.expert); /* Force allows us to delete a public key even if a secret key exists. */ for ( ;names ; names=names->next ) { err = do_delete_key (ctrl, names->d, secret, force, &avail); if (err && avail) { if (allow_both) { err = do_delete_key (ctrl, names->d, 1, 0, &avail); if (!err) err = do_delete_key (ctrl, names->d, 0, 0, &avail); } else { log_error (_("there is a secret key for public key \"%s\"!\n"), names->d); log_info(_("use option \"--delete-secret-keys\" to delete" " it first.\n")); write_status_text (STATUS_DELETE_PROBLEM, "2"); return err; } } if (err) { log_error ("%s: delete key failed: %s\n", names->d, gpg_strerror (err)); return err; } } return 0; } diff --git a/g10/export.c b/g10/export.c index e8bf14cf5..15861cc0d 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1,2437 +1,2437 @@ /* export.c - Export keys in the OpenPGP defined format. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, * 2005, 2010 Free Software Foundation, Inc. * Copyright (C) 1998-2016 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "../common/i18n.h" #include "../common/membuf.h" #include "../common/host2net.h" #include "../common/zb32.h" #include "../common/recsel.h" #include "../common/mbox-util.h" #include "../common/init.h" #include "trustdb.h" #include "call-agent.h" #include "key-clean.h" /* An object to keep track of subkeys. */ struct subkey_list_s { struct subkey_list_s *next; u32 kid[2]; }; typedef struct subkey_list_s *subkey_list_t; /* An object to track statistics for export operations. */ struct export_stats_s { ulong count; /* Number of processed keys. */ ulong secret_count; /* Number of secret keys seen. */ ulong exported; /* Number of actual exported keys. */ }; /* A global variable to store the selector created from * --export-filter keep-uid=EXPR. * --export-filter drop-subkey=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; /* Local prototypes. */ static int do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options, export_stats_t stats); static int do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, kbnode_t *keyblock_out, unsigned int options, export_stats_t stats, int *any); static gpg_error_t print_pka_or_dane_records /**/ (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk, const void *data, size_t datalen, int print_pka, int print_dane); static void cleanup_export_globals (void) { recsel_release (export_keep_uid); export_keep_uid = NULL; recsel_release (export_drop_subkey); export_drop_subkey = NULL; } /* Option parser for export options. See parse_options for details. */ int parse_export_options(char *str,unsigned int *options,int noisy) { struct parse_options export_opts[]= { {"export-local-sigs",EXPORT_LOCAL_SIGS,NULL, N_("export signatures that are marked as local-only")}, {"export-attributes",EXPORT_ATTRIBUTES,NULL, N_("export attribute user IDs (generally photo IDs)")}, {"export-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL, N_("export revocation keys marked as \"sensitive\"")}, {"export-clean",EXPORT_CLEAN,NULL, N_("remove unusable parts from key during export")}, {"export-minimal",EXPORT_MINIMAL|EXPORT_CLEAN,NULL, N_("remove as much as possible from key during export")}, {"export-drop-uids", EXPORT_DROP_UIDS, NULL, N_("Do not export user id or attribute packets")}, {"export-pka", EXPORT_PKA_FORMAT, NULL, NULL }, {"export-dane", EXPORT_DANE_FORMAT, NULL, NULL }, {"backup", EXPORT_BACKUP, NULL, N_("use the GnuPG key backup format")}, {"export-backup", EXPORT_BACKUP, NULL, NULL }, /* Aliases for backward compatibility */ {"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL}, {"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL}, {"include-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,NULL}, /* dummy */ {"export-unusable-sigs",0,NULL,NULL}, {"export-clean-sigs",0,NULL,NULL}, {"export-clean-uids",0,NULL,NULL}, {NULL,0,NULL,NULL} /* add tags for include revoked and disabled? */ }; int rc; rc = parse_options (str, options, export_opts, noisy); if (!rc) return 0; /* Alter other options we want or don't want for restore. */ if ((*options & EXPORT_BACKUP)) { *options |= (EXPORT_LOCAL_SIGS | EXPORT_ATTRIBUTES | EXPORT_SENSITIVE_REVKEYS); *options &= ~(EXPORT_CLEAN | EXPORT_MINIMAL | EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT); } /* Dropping uids also means to drop attributes. */ if ((*options & EXPORT_DROP_UIDS)) *options &= ~(EXPORT_ATTRIBUTES); return rc; } /* Parse and set an export 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 called several * times all expressions for the same NAME are concatenated. * Supported filter names are: * * - keep-uid :: If the expression evaluates to true for a certain * user ID packet, that packet and all it dependencies * will be exported. The expression may use these * variables: * * - uid :: The entire user ID. * - mbox :: The mail box part of the user ID. * - primary :: Evaluate to true for the primary user ID. * * - drop-subkey :: If the expression evaluates to true for a subkey * packet that subkey and all it dependencies will be * remove from the keyblock. The expression may use these * variables: * * - secret :: 1 for a secret subkey, else 0. * - key_algo :: Public key algorithm id */ gpg_error_t parse_and_set_export_filter (const char *string) { gpg_error_t err; /* Auto register the cleanup function. */ register_mem_cleanup_func (cleanup_export_globals); if (!strncmp (string, "keep-uid=", 9)) 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 err = gpg_error (GPG_ERR_INV_NAME); return err; } /* Create a new export stats object initialized to zero. On error returns NULL and sets ERRNO. */ export_stats_t export_new_stats (void) { export_stats_t stats; return xtrycalloc (1, sizeof *stats); } /* Release an export stats object. */ void export_release_stats (export_stats_t stats) { xfree (stats); } /* Print export statistics using the status interface. */ void export_print_stats (export_stats_t stats) { if (!stats) return; if (is_status_enabled ()) { char buf[15*20]; snprintf (buf, sizeof buf, "%lu %lu %lu", stats->count, stats->secret_count, stats->exported ); write_status_text (STATUS_EXPORT_RES, buf); } } /* * Export public keys (to stdout or to --output FILE). * * Depending on opt.armor the output is armored. OPTIONS are defined * in main.h. If USERS is NULL, all keys will be exported. STATS is * either an export stats object for update or NULL. * * This function is the core of "gpg --export". */ int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats) { return do_export (ctrl, users, 0, options, stats); } /* * Export secret keys (to stdout or to --output FILE). * * Depending on opt.armor the output is armored. OPTIONS are defined * in main.h. If USERS is NULL, all secret keys will be exported. * STATS is either an export stats object for update or NULL. * * This function is the core of "gpg --export-secret-keys". */ int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats) { return do_export (ctrl, users, 1, options, stats); } /* * Export secret sub keys (to stdout or to --output FILE). * * This is the same as export_seckeys but replaces the primary key by * a stub key. Depending on opt.armor the output is armored. OPTIONS * are defined in main.h. If USERS is NULL, all secret subkeys will * be exported. STATS is either an export stats object for update or * NULL. * * This function is the core of "gpg --export-secret-subkeys". */ int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats) { return do_export (ctrl, users, 2, options, stats); } /* * Export a single key into a memory buffer. STATS is either an * export stats object for update or NULL. */ gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen) { gpg_error_t err; iobuf_t iobuf; int any; strlist_t helplist; *r_keyblock = NULL; *r_data = NULL; *r_datalen = 0; helplist = NULL; if (!add_to_strlist_try (&helplist, keyspec)) return gpg_error_from_syserror (); iobuf = iobuf_temp (); err = do_export_stream (ctrl, iobuf, helplist, 0, r_keyblock, options, stats, &any); if (!err && !any) err = gpg_error (GPG_ERR_NOT_FOUND); if (!err) { const void *src; size_t datalen; iobuf_flush_temp (iobuf); src = iobuf_get_temp_buffer (iobuf); datalen = iobuf_get_temp_length (iobuf); if (!datalen) err = gpg_error (GPG_ERR_NO_PUBKEY); else if (!(*r_data = xtrymalloc (datalen))) err = gpg_error_from_syserror (); else { memcpy (*r_data, src, datalen); *r_datalen = datalen; } } iobuf_close (iobuf); free_strlist (helplist); if (err && *r_keyblock) { release_kbnode (*r_keyblock); *r_keyblock = NULL; } return err; } /* Export the keys identified by the list of strings in USERS. If Secret is false public keys will be exported. With secret true secret keys will be exported; in this case 1 means the entire secret keyblock and 2 only the subkeys. OPTIONS are the export options to apply. */ static int do_export (ctrl_t ctrl, strlist_t users, int secret, unsigned int options, export_stats_t stats) { IOBUF out = NULL; int any, rc; armor_filter_context_t *afx = NULL; compress_filter_context_t zfx; memset( &zfx, 0, sizeof zfx); rc = open_outfile (-1, NULL, 0, !!secret, &out ); if (rc) return rc; if ( opt.armor && !(options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT)) ) { afx = new_armor_context (); afx->what = secret? 5 : 1; push_armor_filter (afx, out); } rc = do_export_stream (ctrl, out, users, secret, NULL, options, stats, &any); if ( rc || !any ) iobuf_cancel (out); else iobuf_close (out); release_armor_context (afx); return rc; } /* Release an entire subkey list. */ static void release_subkey_list (subkey_list_t list) { while (list) { subkey_list_t tmp = list->next;; xfree (list); list = tmp; } } /* Returns true if NODE is a subkey and contained in LIST. */ static int subkey_in_list_p (subkey_list_t list, KBNODE node) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { u32 kid[2]; keyid_from_pk (node->pkt->pkt.public_key, kid); for (; list; list = list->next) if (list->kid[0] == kid[0] && list->kid[1] == kid[1]) return 1; } return 0; } /* Allocate a new subkey list item from NODE. */ static subkey_list_t new_subkey_list_item (KBNODE node) { subkey_list_t list = xcalloc (1, sizeof *list); if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) keyid_from_pk (node->pkt->pkt.public_key, list->kid); return list; } /* Helper function to check whether the subkey at NODE actually matches the description at DESC. The function returns true if the key under question has been specified by an exact specification (keyID or fingerprint) and does match the one at NODE. It is assumed that the packet at NODE is either a public or secret subkey. */ int exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node) { u32 kid[2]; byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; int result = 0; switch(desc->mode) { case KEYDB_SEARCH_MODE_SHORT_KID: case KEYDB_SEARCH_MODE_LONG_KID: keyid_from_pk (node->pkt->pkt.public_key, kid); break; case KEYDB_SEARCH_MODE_FPR: fingerprint_from_pk (node->pkt->pkt.public_key, fpr, &fprlen); break; default: break; } switch(desc->mode) { case KEYDB_SEARCH_MODE_SHORT_KID: if (desc->u.kid[1] == kid[1]) result = 1; break; case KEYDB_SEARCH_MODE_LONG_KID: if (desc->u.kid[0] == kid[0] && desc->u.kid[1] == kid[1]) result = 1; break; case KEYDB_SEARCH_MODE_FPR: if (fprlen == desc->fprlen && !memcmp (desc->u.fpr, fpr, desc->fprlen)) result = 1; break; default: break; } return result; } /* Return an error if the key represented by the S-expression S_KEY * and the OpenPGP key represented by PK do not use the same curve. */ static gpg_error_t match_curve_skey_pk (gcry_sexp_t s_key, PKT_public_key *pk) { gcry_sexp_t curve = NULL; gcry_sexp_t flags = NULL; char *curve_str = NULL; char *flag; const char *oidstr = NULL; gcry_mpi_t curve_as_mpi = NULL; gpg_error_t err; int is_eddsa = 0; int idx = 0; if (!(pk->pubkey_algo==PUBKEY_ALGO_ECDH || pk->pubkey_algo==PUBKEY_ALGO_ECDSA || pk->pubkey_algo==PUBKEY_ALGO_EDDSA)) return gpg_error (GPG_ERR_PUBKEY_ALGO); curve = gcry_sexp_find_token (s_key, "curve", 0); if (!curve) { log_error ("no reported curve\n"); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } curve_str = gcry_sexp_nth_string (curve, 1); gcry_sexp_release (curve); curve = NULL; if (!curve_str) { log_error ("no curve name\n"); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } oidstr = openpgp_curve_to_oid (curve_str, NULL); if (!oidstr) { log_error ("no OID known for curve '%s'\n", curve_str); xfree (curve_str); return gpg_error (GPG_ERR_UNKNOWN_CURVE); } xfree (curve_str); err = openpgp_oid_from_str (oidstr, &curve_as_mpi); if (err) return err; if (gcry_mpi_cmp (pk->pkey[0], curve_as_mpi)) { log_error ("curves do not match\n"); gcry_mpi_release (curve_as_mpi); return gpg_error (GPG_ERR_INV_CURVE); } gcry_mpi_release (curve_as_mpi); flags = gcry_sexp_find_token (s_key, "flags", 0); if (flags) { for (idx = 1; idx < gcry_sexp_length (flags); idx++) { flag = gcry_sexp_nth_string (flags, idx); if (flag && (strcmp ("eddsa", flag) == 0)) is_eddsa = 1; gcry_free (flag); } } if (is_eddsa != (pk->pubkey_algo == PUBKEY_ALGO_EDDSA)) { log_error ("disagreement about EdDSA\n"); err = gpg_error (GPG_ERR_INV_CURVE); } return err; } /* Return a canonicalized public key algoithms. This is used to compare different flavors of algorithms (e.g. ELG and ELG_E are considered the same). */ static enum gcry_pk_algos canon_pk_algo (enum gcry_pk_algos algo) { switch (algo) { case GCRY_PK_RSA: case GCRY_PK_RSA_E: case GCRY_PK_RSA_S: return GCRY_PK_RSA; case GCRY_PK_ELG: case GCRY_PK_ELG_E: return GCRY_PK_ELG; case GCRY_PK_ECC: case GCRY_PK_ECDSA: case GCRY_PK_ECDH: return GCRY_PK_ECC; default: return algo; } } /* 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 cleartext_secret_key_to_openpgp (gcry_sexp_t s_key, PKT_public_key *pk) { gpg_error_t err; gcry_sexp_t top_list; gcry_sexp_t key = NULL; char *key_type = NULL; enum gcry_pk_algos pk_algo; struct seckey_info *ski; int idx, sec_start; gcry_mpi_t pub_params[10] = { NULL }; /* we look for a private-key, then the first element in it tells us the type */ top_list = gcry_sexp_find_token (s_key, "private-key", 0); if (!top_list) goto bad_seckey; /* ignore all S-expression after the first sublist -- we assume that they are comments or otherwise irrelevant to OpenPGP */ if (gcry_sexp_length(top_list) < 2) goto bad_seckey; key = gcry_sexp_nth (top_list, 1); if (!key) goto bad_seckey; key_type = gcry_sexp_nth_string(key, 0); pk_algo = gcry_pk_map_name (key_type); log_assert (!pk->seckey_info); pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!ski) { err = gpg_error_from_syserror (); goto leave; } switch (canon_pk_algo (pk_algo)) { case GCRY_PK_RSA: if (!is_RSA (pk->pubkey_algo)) goto bad_pubkey_algo; err = gcry_sexp_extract_param (key, NULL, "ne", &pub_params[0], &pub_params[1], NULL); for (idx=0; idx < 2 && !err; idx++) if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx])) err = gpg_error (GPG_ERR_BAD_PUBKEY); if (!err) { for (idx = 2; idx < 6 && !err; idx++) { gcry_mpi_release (pk->pkey[idx]); pk->pkey[idx] = NULL; } err = gcry_sexp_extract_param (key, NULL, "dpqu", &pk->pkey[2], &pk->pkey[3], &pk->pkey[4], &pk->pkey[5], NULL); } if (!err) { for (idx = 2; idx < 6; idx++) ski->csum += checksum_mpi (pk->pkey[idx]); } break; case GCRY_PK_DSA: if (!is_DSA (pk->pubkey_algo)) goto bad_pubkey_algo; err = gcry_sexp_extract_param (key, NULL, "pqgy", &pub_params[0], &pub_params[1], &pub_params[2], &pub_params[3], NULL); for (idx=0; idx < 4 && !err; idx++) if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx])) err = gpg_error (GPG_ERR_BAD_PUBKEY); if (!err) { gcry_mpi_release (pk->pkey[4]); pk->pkey[4] = NULL; err = gcry_sexp_extract_param (key, NULL, "x", &pk->pkey[4], NULL); } if (!err) ski->csum += checksum_mpi (pk->pkey[4]); break; case GCRY_PK_ELG: if (!is_ELGAMAL (pk->pubkey_algo)) goto bad_pubkey_algo; err = gcry_sexp_extract_param (key, NULL, "pgy", &pub_params[0], &pub_params[1], &pub_params[2], NULL); for (idx=0; idx < 3 && !err; idx++) if (gcry_mpi_cmp(pk->pkey[idx], pub_params[idx])) err = gpg_error (GPG_ERR_BAD_PUBKEY); if (!err) { gcry_mpi_release (pk->pkey[3]); pk->pkey[3] = NULL; err = gcry_sexp_extract_param (key, NULL, "x", &pk->pkey[3], NULL); } if (!err) ski->csum += checksum_mpi (pk->pkey[3]); break; case GCRY_PK_ECC: err = match_curve_skey_pk (key, pk); if (err) goto leave; if (!err) err = gcry_sexp_extract_param (key, NULL, "q", &pub_params[0], NULL); if (!err && (gcry_mpi_cmp(pk->pkey[1], pub_params[0]))) err = gpg_error (GPG_ERR_BAD_PUBKEY); sec_start = 2; if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) sec_start += 1; if (!err) { gcry_mpi_release (pk->pkey[sec_start]); pk->pkey[sec_start] = NULL; err = gcry_sexp_extract_param (key, NULL, "d", &pk->pkey[sec_start], NULL); } if (!err) ski->csum += checksum_mpi (pk->pkey[sec_start]); break; default: pk->seckey_info = NULL; xfree (ski); err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); break; } leave: gcry_sexp_release (top_list); gcry_sexp_release (key); gcry_free (key_type); for (idx=0; idx < DIM(pub_params); idx++) gcry_mpi_release (pub_params[idx]); return err; bad_pubkey_algo: err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; bad_seckey: err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; } /* Use the key transfer format given in S_PGP to create the secinfo structure in PK and change the parameter array in PK to include the secret parameters. */ static gpg_error_t transfer_format_to_openpgp (gcry_sexp_t s_pgp, PKT_public_key *pk) { gpg_error_t err; gcry_sexp_t top_list; gcry_sexp_t list = NULL; char *curve = NULL; const char *value; size_t valuelen; char *string; int idx; int is_v4, is_protected; enum gcry_pk_algos pk_algo; int protect_algo = 0; char iv[16]; int ivlen = 0; int s2k_mode = 0; int s2k_algo = 0; byte s2k_salt[8]; u32 s2k_count = 0; int is_ecdh = 0; size_t npkey, nskey; gcry_mpi_t skey[10]; /* We support up to 9 parameters. */ int skeyidx = 0; struct seckey_info *ski; /* gcry_log_debugsxp ("transferkey", s_pgp); */ top_list = gcry_sexp_find_token (s_pgp, "openpgp-private-key", 0); if (!top_list) goto bad_seckey; list = gcry_sexp_find_token (top_list, "version", 0); if (!list) goto bad_seckey; value = gcry_sexp_nth_data (list, 1, &valuelen); if (!value || valuelen != 1 || !(value[0] == '3' || value[0] == '4')) goto bad_seckey; is_v4 = (value[0] == '4'); gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "protection", 0); if (!list) goto bad_seckey; value = gcry_sexp_nth_data (list, 1, &valuelen); if (!value) goto bad_seckey; if (valuelen == 4 && !memcmp (value, "sha1", 4)) is_protected = 2; else if (valuelen == 3 && !memcmp (value, "sum", 3)) is_protected = 1; else if (valuelen == 4 && !memcmp (value, "none", 4)) is_protected = 0; else goto bad_seckey; if (is_protected) { string = gcry_sexp_nth_string (list, 2); if (!string) goto bad_seckey; protect_algo = gcry_cipher_map_name (string); xfree (string); value = gcry_sexp_nth_data (list, 3, &valuelen); if (!value || !valuelen || valuelen > sizeof iv) goto bad_seckey; memcpy (iv, value, valuelen); ivlen = valuelen; string = gcry_sexp_nth_string (list, 4); if (!string) goto bad_seckey; s2k_mode = strtol (string, NULL, 10); xfree (string); string = gcry_sexp_nth_string (list, 5); if (!string) goto bad_seckey; s2k_algo = gcry_md_map_name (string); xfree (string); value = gcry_sexp_nth_data (list, 6, &valuelen); if (!value || !valuelen || valuelen > sizeof s2k_salt) goto bad_seckey; memcpy (s2k_salt, value, valuelen); string = gcry_sexp_nth_string (list, 7); if (!string) goto bad_seckey; s2k_count = strtoul (string, NULL, 10); xfree (string); } /* Parse the gcrypt PK algo and check that it is okay. */ gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "algo", 0); if (!list) goto bad_seckey; string = gcry_sexp_nth_string (list, 1); if (!string) goto bad_seckey; 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) goto bad_seckey; /* Check that the pubkey algo matches the one from the public key. */ switch (canon_pk_algo (pk_algo)) { case GCRY_PK_RSA: if (!is_RSA (pk->pubkey_algo)) pk_algo = 0; /* Does not match. */ break; case GCRY_PK_DSA: if (!is_DSA (pk->pubkey_algo)) pk_algo = 0; /* Does not match. */ break; case GCRY_PK_ELG: if (!is_ELGAMAL (pk->pubkey_algo)) pk_algo = 0; /* Does not match. */ break; case GCRY_PK_ECC: if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA) ; else if (pk->pubkey_algo == PUBKEY_ALGO_ECDH) is_ecdh = 1; else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA) ; else pk_algo = 0; /* Does not match. */ /* For ECC we do not have the domain parameters thus fix our info. */ npkey = 1; nskey = 2; break; default: pk_algo = 0; /* Oops. */ break; } if (!pk_algo) { err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } /* This check has to go after the ecc adjustments. */ if (nskey > PUBKEY_MAX_NSKEY) goto bad_seckey; /* Parse the key parameters. */ gcry_sexp_release (list); list = gcry_sexp_find_token (top_list, "skey", 0); if (!list) goto bad_seckey; for (idx=0;;) { int is_enc; value = gcry_sexp_nth_data (list, ++idx, &valuelen); if (!value && skeyidx >= npkey) break; /* Ready. */ /* Check for too many parameters. Note that depending on the protection mode and version number we may see less than NSKEY (but at least NPKEY+1) parameters. */ if (idx >= 2*nskey) goto bad_seckey; if (skeyidx >= DIM (skey)-1) goto bad_seckey; if (!value || valuelen != 1 || !(value[0] == '_' || value[0] == 'e')) goto bad_seckey; is_enc = (value[0] == 'e'); value = gcry_sexp_nth_data (list, ++idx, &valuelen); if (!value || !valuelen) goto bad_seckey; if (is_enc) { void *p = xtrymalloc (valuelen); if (!p) goto outofmem; memcpy (p, value, valuelen); skey[skeyidx] = gcry_mpi_set_opaque (NULL, p, valuelen*8); if (!skey[skeyidx]) goto outofmem; } else { if (gcry_mpi_scan (skey + skeyidx, GCRYMPI_FMT_STD, value, valuelen, NULL)) goto bad_seckey; } skeyidx++; } skey[skeyidx++] = NULL; gcry_sexp_release (list); list = NULL; /* We have no need for the CSUM value thus we don't parse it. */ /* list = gcry_sexp_find_token (top_list, "csum", 0); */ /* if (list) */ /* { */ /* string = gcry_sexp_nth_string (list, 1); */ /* if (!string) */ /* goto bad_seckey; */ /* desired_csum = strtoul (string, NULL, 10); */ /* xfree (string); */ /* } */ /* else */ /* desired_csum = 0; */ /* gcry_sexp_release (list); list = NULL; */ /* Get the curve name if any, */ list = gcry_sexp_find_token (top_list, "curve", 0); if (list) { curve = gcry_sexp_nth_string (list, 1); gcry_sexp_release (list); list = NULL; } gcry_sexp_release (top_list); top_list = NULL; /* log_debug ("XXX is_v4=%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); */ /* log_printhex ("XXX iv", iv, ivlen); */ /* log_debug ("XXX ivlen=%d\n", ivlen); */ /* log_debug ("XXX s2k_mode=%d\n", s2k_mode); */ /* log_debug ("XXX s2k_algo=%d\n", s2k_algo); */ /* log_printhex ("XXX s2k_salt", s2k_salt, sizeof s2k_salt); */ /* log_debug ("XXX s2k_count=%lu\n", (unsigned long)s2k_count); */ /* for (idx=0; skey[idx]; idx++) */ /* { */ /* int is_enc = gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE); */ /* log_info ("XXX skey[%d]%s:", idx, is_enc? " (enc)":""); */ /* if (is_enc) */ /* { */ /* void *p; */ /* unsigned int nbits; */ /* p = gcry_mpi_get_opaque (skey[idx], &nbits); */ /* log_printhex (NULL, p, (nbits+7)/8); */ /* } */ /* else */ /* gcry_mpi_dump (skey[idx]); */ /* log_printf ("\n"); */ /* } */ if (!is_v4 || is_protected != 2 ) { /* We only support the v4 format and a SHA-1 checksum. */ err = gpg_error (GPG_ERR_NOT_IMPLEMENTED); goto leave; } /* We need to change the received parameters for ECC algorithms. The transfer format has the curve name and the parameters separate. We put them all into the SKEY array. */ if (canon_pk_algo (pk_algo) == GCRY_PK_ECC) { const char *oidstr; /* Assert that all required parameters are available. We also check that the array does not contain more parameters than needed (this was used by some beta versions of 2.1. */ if (!curve || !skey[0] || !skey[1] || skey[2]) { err = gpg_error (GPG_ERR_INTERNAL); goto leave; } oidstr = openpgp_curve_to_oid (curve, NULL); if (!oidstr) { log_error ("no OID known for curve '%s'\n", curve); err = gpg_error (GPG_ERR_UNKNOWN_CURVE); goto leave; } /* Put the curve's OID into the MPI array. This requires that we shift Q and D. For ECDH also insert the KDF parms. */ if (is_ecdh) { skey[4] = NULL; skey[3] = skey[1]; skey[2] = gcry_mpi_copy (pk->pkey[2]); } else { skey[3] = NULL; skey[2] = skey[1]; } skey[1] = skey[0]; skey[0] = NULL; err = openpgp_oid_from_str (oidstr, skey + 0); if (err) goto leave; /* Fixup the NPKEY and NSKEY to match OpenPGP reality. */ npkey = 2 + is_ecdh; nskey = 3 + is_ecdh; /* for (idx=0; skey[idx]; idx++) */ /* { */ /* log_info ("YYY skey[%d]:", idx); */ /* if (gcry_mpi_get_flag (skey[idx], GCRYMPI_FLAG_OPAQUE)) */ /* { */ /* void *p; */ /* unsigned int nbits; */ /* p = gcry_mpi_get_opaque (skey[idx], &nbits); */ /* log_printhex (NULL, p, (nbits+7)/8); */ /* } */ /* else */ /* gcry_mpi_dump (skey[idx]); */ /* log_printf ("\n"); */ /* } */ } /* Do some sanity checks. */ if (s2k_count > 255) { /* We expect an already encoded S2K count. */ err = gpg_error (GPG_ERR_INV_DATA); goto leave; } err = openpgp_cipher_test_algo (protect_algo); if (err) goto leave; err = openpgp_md_test_algo (s2k_algo); if (err) goto leave; /* Check that the public key parameters match. Note that since Libgcrypt 1.5 gcry_mpi_cmp handles opaque MPI correctly. */ for (idx=0; idx < npkey; idx++) if (gcry_mpi_cmp (pk->pkey[idx], skey[idx])) { err = gpg_error (GPG_ERR_BAD_PUBKEY); goto leave; } /* Check that the first secret key parameter in SKEY is encrypted and that there are no more secret key parameters. The latter is guaranteed by the v4 packet format. */ if (!gcry_mpi_get_flag (skey[npkey], GCRYMPI_FLAG_OPAQUE)) goto bad_seckey; if (npkey+1 < DIM (skey) && skey[npkey+1]) goto bad_seckey; /* Check that the secret key parameters in PK are all set to NULL. */ for (idx=npkey; idx < nskey; idx++) if (pk->pkey[idx]) goto bad_seckey; /* Now build the protection info. */ pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!ski) { err = gpg_error_from_syserror (); goto leave; } ski->is_protected = 1; ski->sha1chk = 1; ski->algo = protect_algo; ski->s2k.mode = s2k_mode; ski->s2k.hash_algo = s2k_algo; log_assert (sizeof ski->s2k.salt == sizeof s2k_salt); memcpy (ski->s2k.salt, s2k_salt, sizeof s2k_salt); ski->s2k.count = s2k_count; log_assert (ivlen <= sizeof ski->iv); memcpy (ski->iv, iv, ivlen); ski->ivlen = ivlen; /* Store the protected secret key parameter. */ pk->pkey[npkey] = skey[npkey]; skey[npkey] = NULL; /* That's it. */ leave: gcry_free (curve); gcry_sexp_release (list); gcry_sexp_release (top_list); for (idx=0; idx < skeyidx; idx++) gcry_mpi_release (skey[idx]); return err; bad_seckey: err = gpg_error (GPG_ERR_BAD_SECKEY); goto leave; outofmem: err = gpg_error (GPG_ERR_ENOMEM); goto leave; } /* Print an "EXPORTED" status line. PK is the primary public key. */ static void print_status_exported (PKT_public_key *pk) { char *hexfpr; if (!is_status_enabled ()) return; hexfpr = hexfingerprint (pk, NULL, 0); write_status_text (STATUS_EXPORTED, hexfpr? hexfpr : "[?]"); xfree (hexfpr); } /* * Receive a secret key from agent specified by HEXGRIP. * * Since the key data from the agent is encrypted, decrypt it using * CIPHERHD context. Then, parse the decrypted key data into transfer * format, and put secret parameters into PK. * * If CLEARTEXT is 0, store the secret key material * passphrase-protected. Otherwise, store secret key material in the * clear. * * CACHE_NONCE_ADDR is used to share nonce for multiple key retrievals. */ gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk) { 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; char *prompt; 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); xfree (prompt); if (err) goto unwraperror; if (wrappedkeylen < 24) { err = gpg_error (GPG_ERR_INV_LENGTH); goto unwraperror; } keylen = wrappedkeylen - 8; key = xtrymalloc_secure (keylen); if (!key) { err = gpg_error_from_syserror (); goto unwraperror; } err = gcry_cipher_decrypt (cipherhd, key, keylen, wrappedkey, wrappedkeylen); if (err) goto unwraperror; realkeylen = gcry_sexp_canon_len (key, keylen, NULL, &err); if (!realkeylen) goto unwraperror; /* Invalid csexp. */ err = gcry_sexp_sscan (&s_skey, NULL, key, realkeylen); if (!err) { if (cleartext) err = cleartext_secret_key_to_openpgp (s_skey, pk); else err = transfer_format_to_openpgp (s_skey, pk); gcry_sexp_release (s_skey); } unwraperror: xfree (key); xfree (wrappedkey); if (err) { log_error ("key %s: error receiving key from agent:" " %s%s\n", hexgrip, gpg_strerror (err), gpg_err_code (err) == GPG_ERR_FULLY_CANCELED? "":_(" - skipped")); } return err; } /* Write KEYBLOCK either to stdout or to the file set with the * --output option. This is a simplified version of do_export_stream * which supports only a few export options. */ gpg_error_t write_keyblock_to_output (kbnode_t keyblock, int with_armor, unsigned int options) { gpg_error_t err; const char *fname; iobuf_t out; kbnode_t node; armor_filter_context_t *afx = NULL; iobuf_t out_help = NULL; PKT_public_key *pk = NULL; fname = opt.outfile? opt.outfile : "-"; if (is_secured_filename (fname) ) return gpg_error (GPG_ERR_EPERM); out = iobuf_create (fname, 0); if (!out) { err = gpg_error_from_syserror (); log_error(_("can't create '%s': %s\n"), fname, gpg_strerror (err)); return err; } if (opt.verbose) log_info (_("writing to '%s'\n"), iobuf_get_fname_nonnull (out)); if ((options & (EXPORT_PKA_FORMAT|EXPORT_DANE_FORMAT))) { with_armor = 0; out_help = iobuf_temp (); } if (with_armor) { afx = new_armor_context (); afx->what = 1; push_armor_filter (afx, out); } for (node = keyblock; node; node = node->next) { if (is_deleted_kbnode (node)) continue; if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* Skip - they should not be here anyway. */ if (!pk && (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY)) pk = node->pkt->pkt.public_key; if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out_help? out_help : out, node->pkt); else err = build_packet (out_help? out_help : out, node->pkt); if (err) { log_error ("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (err) ); goto leave; } } err = 0; if (out_help && pk) { const void *data; size_t datalen; iobuf_flush_temp (out_help); data = iobuf_get_temp_buffer (out_help); datalen = iobuf_get_temp_length (out_help); err = print_pka_or_dane_records (out, keyblock, pk, data, datalen, (options & EXPORT_PKA_FORMAT), (options & EXPORT_DANE_FORMAT)); } leave: if (err) iobuf_cancel (out); else iobuf_close (out); iobuf_cancel (out_help); release_armor_context (afx); return err; } /* * Apply the keep-uid filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_keep_uid_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; struct impex_filter_parm_s parm; parm.ctrl = ctrl; for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_USER_ID) { parm.node = node; if (!recsel_select (selector, impex_filter_getval, &parm)) { /* log_debug ("keep-uid: deleting '%s'\n", */ /* node->pkt->pkt.user_id->name); */ /* The UID packet and all following packets up to the * next UID or a subkey. */ delete_kbnode (node); for (; node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ; node = node->next) delete_kbnode (node->next); } /* else */ /* log_debug ("keep-uid: keeping '%s'\n", */ /* node->pkt->pkt.user_id->name); */ } } } /* * Apply the drop-subkey filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_drop_subkey_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; struct impex_filter_parm_s parm; parm.ctrl = ctrl; for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { parm.node = node; if (recsel_select (selector, impex_filter_getval, &parm)) { /*log_debug ("drop-subkey: deleting a key\n");*/ /* The subkey packet and all following packets up to the * next subkey. */ delete_kbnode (node); for (; node->next && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ; node = node->next) delete_kbnode (node->next); } } } } /* Print DANE or PKA records for all user IDs in KEYBLOCK to OUT. The * data for the record is taken from (DATA,DATELEN). PK is the public * key packet with the primary key. */ static gpg_error_t print_pka_or_dane_records (iobuf_t out, kbnode_t keyblock, PKT_public_key *pk, const void *data, size_t datalen, int print_pka, int print_dane) { gpg_error_t err = 0; kbnode_t kbctx, node; PKT_user_id *uid; char *mbox = NULL; char hashbuf[32]; char *hash = NULL; char *domain; const char *s; unsigned int len; estream_t fp = NULL; char *hexdata = NULL; char *hexfpr; hexfpr = hexfingerprint (pk, NULL, 0); if (!hexfpr) { err = gpg_error_from_syserror (); goto leave; } hexdata = bin2hex (data, datalen, NULL); if (!hexdata) { err = gpg_error_from_syserror (); goto leave; } ascii_strlwr (hexdata); fp = es_fopenmem (0, "rw,samethread"); if (!fp) { err = gpg_error_from_syserror (); goto leave; } for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype != PKT_USER_ID) continue; uid = node->pkt->pkt.user_id; if (uid->flags.expired || uid->flags.revoked) continue; xfree (mbox); mbox = mailbox_from_userid (uid->name, 0); if (!mbox) continue; domain = strchr (mbox, '@'); *domain++ = 0; if (print_pka) { es_fprintf (fp, "$ORIGIN _pka.%s.\n; %s\n; ", domain, hexfpr); print_utf8_buffer (fp, uid->name, uid->len); es_putc ('\n', fp); gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); xfree (hash); hash = zb32_encode (hashbuf, 8*20); if (!hash) { err = gpg_error_from_syserror (); goto leave; } len = strlen (hexfpr)/2; es_fprintf (fp, "%s TYPE37 \\# %u 0006 0000 00 %02X %s\n\n", hash, 6 + len, len, hexfpr); } if (print_dane && hexdata) { es_fprintf (fp, "$ORIGIN _openpgpkey.%s.\n; %s\n; ", domain, hexfpr); print_utf8_buffer (fp, uid->name, uid->len); es_putc ('\n', fp); gcry_md_hash_buffer (GCRY_MD_SHA256, hashbuf, mbox, strlen (mbox)); xfree (hash); hash = bin2hex (hashbuf, 28, NULL); if (!hash) { err = gpg_error_from_syserror (); goto leave; } ascii_strlwr (hash); len = strlen (hexdata)/2; es_fprintf (fp, "%s TYPE61 \\# %u (\n", hash, len); for (s = hexdata; ;) { es_fprintf (fp, "\t%.64s\n", s); if (strlen (s) < 64) break; s += 64; } es_fputs ("\t)\n\n", fp); } } /* Make sure it is a string and write it. */ es_fputc (0, fp); { void *vp; if (es_fclose_snatch (fp, &vp, NULL)) { err = gpg_error_from_syserror (); goto leave; } fp = NULL; iobuf_writestr (out, vp); es_free (vp); } err = 0; leave: xfree (hash); xfree (mbox); es_fclose (fp); xfree (hexdata); xfree (hexfpr); return err; } /* Helper for do_export_stream which writes one keyblock to OUT. */ static gpg_error_t do_export_one_keyblock (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, iobuf_t out, int secret, unsigned int options, export_stats_t stats, int *any, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t descindex, gcry_cipher_hd_t cipherhd) { gpg_error_t err = gpg_error (GPG_ERR_NOT_FOUND); char *cache_nonce = NULL; subkey_list_t subkey_list = NULL; /* Track already processed subkeys. */ int skip_until_subkey = 0; int cleartext = 0; char *hexgrip = NULL; char *serialno = NULL; PKT_public_key *pk; u32 subkidbuf[2], *subkid; kbnode_t kbctx, node; /* NB: walk_kbnode skips packets marked as deleted. */ for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (skip_until_subkey) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) skip_until_subkey = 0; else continue; } /* We used to use comment packets, but not any longer. In * case we still have comments on a key, strip them here * before we call build_packet(). */ if (node->pkt->pkttype == PKT_COMMENT) continue; /* Skip ring trust packets - they should not be here anyway. */ if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* If exact is set, then we only export what was requested * (plus the primary key, if the user didn't specifically * request it). */ if (desc[descindex].exact && node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (!exact_subkey_match_p (desc+descindex, node)) { /* Before skipping this subkey, check whether any * other description wants an exact match on a * subkey and include that subkey into the output * too. Need to add this subkey to a list so that * it won't get processed a second time. * * So the first step here is to check that list and * skip in any case if the key is in that list. * * We need this whole mess because the import * function of GnuPG < 2.1 is not able to merge * secret keys and thus it is useless to output them * as two separate keys and have import merge them. */ if (subkey_in_list_p (subkey_list, node)) skip_until_subkey = 1; /* Already processed this one. */ else { size_t j; for (j=0; j < ndesc; j++) if (j != descindex && desc[j].exact && exact_subkey_match_p (desc+j, node)) break; if (!(j < ndesc)) skip_until_subkey = 1; /* No other one matching. */ } } if (skip_until_subkey) continue; /* Mark this one as processed. */ { subkey_list_t tmp = new_subkey_list_item (node); tmp->next = subkey_list; subkey_list = tmp; } } if (node->pkt->pkttype == PKT_SIGNATURE) { /* Do not export packets which are marked as not * exportable. */ if (!(options & EXPORT_LOCAL_SIGS) && !node->pkt->pkt.signature->flags.exportable) continue; /* not exportable */ /* Do not export packets with a "sensitive" revocation key * unless the user wants us to. Note that we do export * these when issuing the actual revocation (see revoke.c). */ if (!(options & EXPORT_SENSITIVE_REVKEYS) && node->pkt->pkt.signature->revkey) { int i; for (i = 0; i < node->pkt->pkt.signature->numrevkeys; i++) if ((node->pkt->pkt.signature->revkey[i].class & 0x40)) break; if (i < node->pkt->pkt.signature->numrevkeys) continue; } } /* Don't export user ids (and attributes)? This is not RFC-4880 * compliant but we allow it anyway. */ if ((options & EXPORT_DROP_UIDS) && node->pkt->pkttype == PKT_USER_ID) { /* Skip until we get to something that is not a user id (or * attrib) or a signature on it. */ while (kbctx->next && kbctx->next->pkt->pkttype == PKT_SIGNATURE) kbctx = kbctx->next; continue; } /* Don't export attribs? */ if (!(options & EXPORT_ATTRIBUTES) && node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data) { /* Skip until we get to something that is not an attrib or a * signature on an attrib. */ while (kbctx->next && kbctx->next->pkt->pkttype == PKT_SIGNATURE) kbctx = kbctx->next; continue; } if (secret && (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) { pk = node->pkt->pkt.public_key; if (node->pkt->pkttype == PKT_PUBLIC_KEY) subkid = NULL; else { keyid_from_pk (pk, subkidbuf); subkid = subkidbuf; } if (pk->seckey_info) { log_error ("key %s: oops: seckey_info already set" " - skipped\n", keystr_with_sub (keyid, subkid)); skip_until_subkey = 1; continue; } xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { log_error ("key %s: error computing keygrip: %s" " - skipped\n", keystr_with_sub (keyid, subkid), gpg_strerror (err)); skip_until_subkey = 1; err = 0; continue; } xfree (serialno); serialno = NULL; if (secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY) { /* We are asked not to export the secret parts of the * primary key. Make up an error code to create the * stub. */ err = GPG_ERR_NOT_FOUND; } else err = agent_get_keyinfo (ctrl, hexgrip, &serialno, &cleartext); if ((!err && serialno) && secret == 2 && node->pkt->pkttype == PKT_PUBLIC_KEY) { /* It does not make sense to export a key with its * primary key on card using a non-key stub. Thus we * skip those keys when used with --export-secret-subkeys. */ log_info (_("key %s: key material on-card - skipped\n"), keystr_with_sub (keyid, subkid)); skip_until_subkey = 1; } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND || (!err && serialno)) { /* Create a key stub. */ struct seckey_info *ski; const char *s; pk->seckey_info = ski = xtrycalloc (1, sizeof *ski); if (!ski) { err = gpg_error_from_syserror (); goto leave; } ski->is_protected = 1; if (err) ski->s2k.mode = 1001; /* GNU dummy (no secret key). */ else { ski->s2k.mode = 1002; /* GNU-divert-to-card. */ for (s=serialno; sizeof (ski->ivlen) && *s && s[1]; ski->ivlen++, s += 2) ski->iv[ski->ivlen] = xtoi_2 (s); } if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out, node->pkt); else err = build_packet (out, node->pkt); if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY) { stats->exported++; print_status_exported (node->pkt->pkt.public_key); } } else if (!err) { err = receive_seckey_from_agent (ctrl, cipherhd, cleartext, &cache_nonce, hexgrip, pk); if (err) { if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) goto leave; skip_until_subkey = 1; err = 0; } else { if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out, node->pkt); else err = build_packet (out, node->pkt); if (node->pkt->pkttype == PKT_PUBLIC_KEY) { stats->exported++; print_status_exported (node->pkt->pkt.public_key); } } } else { log_error ("key %s: error getting keyinfo from agent: %s" " - skipped\n", keystr_with_sub (keyid, subkid), gpg_strerror (err)); skip_until_subkey = 1; err = 0; } xfree (pk->seckey_info); pk->seckey_info = NULL; { int i; for (i = pubkey_get_npkey (pk->pubkey_algo); i < pubkey_get_nskey (pk->pubkey_algo); i++) { gcry_mpi_release (pk->pkey[i]); pk->pkey[i] = NULL; } } } else /* Not secret or common packets. */ { if ((options & EXPORT_BACKUP)) err = build_packet_and_meta (out, node->pkt); else err = build_packet (out, node->pkt); if (!err && node->pkt->pkttype == PKT_PUBLIC_KEY) { stats->exported++; print_status_exported (node->pkt->pkt.public_key); } } if (err) { log_error ("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (err)); goto leave; } if (!skip_until_subkey) *any = 1; } leave: release_subkey_list (subkey_list); xfree (serialno); xfree (hexgrip); xfree (cache_nonce); return err; } /* Export the keys identified by the list of strings in USERS to the stream OUT. If SECRET is false public keys will be exported. With secret true secret keys will be exported; in this case 1 means the entire secret keyblock and 2 only the subkeys. OPTIONS are the export options to apply. If KEYBLOCK_OUT is not NULL, AND the exit code is zero, a pointer to the first keyblock found and exported will be stored at this address; no other keyblocks are exported in this case. The caller must free the returned keyblock. If any key has been exported true is stored at ANY. */ static int do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, kbnode_t *keyblock_out, unsigned int options, export_stats_t stats, int *any) { gpg_error_t err = 0; PACKET pkt; kbnode_t keyblock = NULL; kbnode_t node; size_t ndesc, descindex; KEYDB_SEARCH_DESC *desc = NULL; KEYDB_HANDLE kdbhd; strlist_t sl; gcry_cipher_hd_t cipherhd = NULL; struct export_stats_s dummystats; iobuf_t out_help = NULL; if (!stats) stats = &dummystats; *any = 0; init_packet (&pkt); - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) return gpg_error_from_syserror (); /* For the PKA and DANE format open a helper iobuf and for DANE * enforce some options. */ if ((options & (EXPORT_PKA_FORMAT | EXPORT_DANE_FORMAT))) { out_help = iobuf_temp (); if ((options & EXPORT_DANE_FORMAT)) options |= EXPORT_MINIMAL | EXPORT_CLEAN; } if (!users) { ndesc = 1; desc = xcalloc (ndesc, sizeof *desc); desc[0].mode = KEYDB_SEARCH_MODE_FIRST; } else { for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) ; desc = xmalloc ( ndesc * sizeof *desc); for (ndesc=0, sl=users; sl; sl = sl->next) { if (!(err=classify_user_id (sl->d, desc+ndesc, 1))) ndesc++; else log_error (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (err)); } keydb_disable_caching (kdbhd); /* We are looping the search. */ /* It would be nice to see which of the given users did actually match one in the keyring. To implement this we need to have a found flag for each entry in desc. To set this flag we must check all those entries after a match to mark all matched one - currently we stop at the first match. To do this we need an extra flag to enable this feature. */ } #ifdef ENABLE_SELINUX_HACKS if (secret) { log_error (_("exporting secret keys not allowed\n")); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } #endif /* For secret key export we need to setup a decryption context. */ if (secret) { void *kek = NULL; size_t keklen; err = agent_keywrap_key (ctrl, 1, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Prepare a cipher context. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) { log_error ("error setting up an encryption context: %s\n", gpg_strerror (err)); goto leave; } xfree (kek); kek = NULL; } for (;;) { u32 keyid[2]; PKT_public_key *pk; err = keydb_search (kdbhd, desc, ndesc, &descindex); if (!users) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; if (err) break; /* Read the keyblock. */ release_kbnode (keyblock); keyblock = NULL; err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); goto leave; } node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("public key packet not found in keyblock - skipped\n"); continue; } stats->count++; setup_main_keyids (keyblock); /* gpg_format_keydesc needs it. */ pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); /* If a secret key export is required we need to check whether we have a secret key at all and if so create the seckey_info structure. */ if (secret) { if (agent_probe_any_secret_key (ctrl, keyblock)) continue; /* No secret key (neither primary nor subkey). */ /* No v3 keys with GNU mode 1001. */ if (secret == 2 && pk->version == 3) { log_info (_("key %s: PGP 2.x style key - skipped\n"), keystr (keyid)); continue; } /* The agent does not yet allow export of v3 packets. It is actually questionable whether we should allow them at all. */ if (pk->version == 3) { log_info ("key %s: PGP 2.x style key (v3) export " "not yet supported - skipped\n", keystr (keyid)); continue; } stats->secret_count++; } /* Always do the cleaning on the public key part if requested. * A designated revocation is never stripped, even with * export-minimal set. */ if ((options & EXPORT_CLEAN)) { merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&EXPORT_MINIMAL), NULL, NULL); clean_all_subkeys (ctrl, keyblock, opt.verbose, (options&EXPORT_MINIMAL)? KEY_CLEAN_ALL /**/ : KEY_CLEAN_AUTHENCR, NULL, NULL); commit_kbnode (&keyblock); } if (export_keep_uid) { commit_kbnode (&keyblock); apply_keep_uid_filter (ctrl, keyblock, export_keep_uid); commit_kbnode (&keyblock); } if (export_drop_subkey) { commit_kbnode (&keyblock); apply_drop_subkey_filter (ctrl, keyblock, export_drop_subkey); commit_kbnode (&keyblock); } /* 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 (err) break; if (keyblock_out) { *keyblock_out = keyblock; break; } if (out_help) { /* We want to write PKA or DANE records. OUT_HELP has the * keyblock and we print a record for each uid to OUT. */ const void *data; size_t datalen; iobuf_flush_temp (out_help); data = iobuf_get_temp_buffer (out_help); datalen = iobuf_get_temp_length (out_help); err = print_pka_or_dane_records (out, keyblock, pk, data, datalen, (options & EXPORT_PKA_FORMAT), (options & EXPORT_DANE_FORMAT)); if (err) goto leave; iobuf_close (out_help); out_help = iobuf_temp (); } } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; leave: iobuf_cancel (out_help); gcry_cipher_close (cipherhd); xfree(desc); keydb_release (kdbhd); if (err || !keyblock_out) release_kbnode( keyblock ); if( !*any ) log_info(_("WARNING: nothing exported\n")); return err; } static gpg_error_t key_to_sshblob (membuf_t *mb, const char *identifier, ...) { va_list arg_ptr; gpg_error_t err = 0; unsigned char nbuf[4]; unsigned char *buf; size_t buflen; gcry_mpi_t a; ulongtobuf (nbuf, (ulong)strlen (identifier)); put_membuf (mb, nbuf, 4); put_membuf_str (mb, identifier); if (!strncmp (identifier, "ecdsa-sha2-", 11)) { ulongtobuf (nbuf, (ulong)strlen (identifier+11)); put_membuf (mb, nbuf, 4); put_membuf_str (mb, identifier+11); } va_start (arg_ptr, identifier); while ((a = va_arg (arg_ptr, gcry_mpi_t))) { err = gcry_mpi_aprint (GCRYMPI_FMT_SSH, &buf, &buflen, a); if (err) break; if (!strcmp (identifier, "ssh-ed25519") && buflen > 5 && buf[4] == 0x40) { /* We need to strip our 0x40 prefix. */ put_membuf (mb, "\x00\x00\x00\x20", 4); put_membuf (mb, buf+5, buflen-5); } else put_membuf (mb, buf, buflen); gcry_free (buf); } va_end (arg_ptr); return err; } /* Export the key identified by USERID in the SSH public key format. The function exports the latest subkey with Authentication capability unless the '!' suffix is used to export a specific key. */ gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_SEARCH_DESC desc; u32 latest_date; u32 curtime = make_timestamp (); kbnode_t latest_key, node; PKT_public_key *pk; const char *identifier = NULL; membuf_t mb; estream_t fp = NULL; struct b64state b64_state; const char *fname = "-"; init_membuf (&mb, 4096); /* We need to know whether the key has been specified using the exact syntax ('!' suffix). Thus we need to run a classify_user_id on our own. */ err = classify_user_id (userid, &desc, 1); /* Get the public key. */ if (!err) { getkey_ctx_t getkeyctx; err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, &getkeyctx, NULL, userid, &keyblock, NULL, 0 /* Only usable keys or given exact. */); if (!err) { err = getkey_next (ctrl, getkeyctx, NULL, NULL); if (!err) err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); else if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY) err = 0; } getkey_end (ctrl, getkeyctx); } if (err) { log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err)); return err; } /* The finish_lookup code in getkey.c does not handle auth keys, thus we have to duplicate the code here to find the latest subkey. However, if the key has been found using an exact match ('!' notation) we use that key without any further checks and even allow the use of the primary key. */ latest_date = 0; latest_key = NULL; for (node = keyblock; node; node = node->next) { if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_PUBLIC_KEY) && node->pkt->pkt.public_key->flags.exact) { latest_key = node; break; } } if (!latest_key) { for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = node->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tchecking subkey %08lX\n", (ulong) keyid_from_pk (pk, NULL)); if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH)) { if (DBG_LOOKUP) log_debug ("\tsubkey not usable for authentication\n"); continue; } if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tsubkey not valid\n"); continue; } if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tsubkey has been revoked\n"); continue; } if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tsubkey has expired\n"); continue; } if (pk->timestamp > curtime && !opt.ignore_valid_from) { if (DBG_LOOKUP) log_debug ("\tsubkey not yet valid\n"); continue; } if (DBG_LOOKUP) log_debug ("\tsubkey might be fine\n"); /* In case a key has a timestamp of 0 set, we make sure that it is used. A better change would be to compare ">=" but that might also change the selected keys and is as such a more intrusive change. */ if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date)) { latest_date = pk->timestamp; latest_key = node; } } /* If no subkey was suitable check the primary key. */ if (!latest_key && (node = keyblock) && node->pkt->pkttype == PKT_PUBLIC_KEY) { pk = node->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tchecking primary key %08lX\n", (ulong) keyid_from_pk (pk, NULL)); if (!(pk->pubkey_usage & PUBKEY_USAGE_AUTH)) { if (DBG_LOOKUP) log_debug ("\tprimary key not usable for authentication\n"); } else if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tprimary key not valid\n"); } else if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tprimary key has been revoked\n"); } else if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tprimary key has expired\n"); } else if (pk->timestamp > curtime && !opt.ignore_valid_from) { if (DBG_LOOKUP) log_debug ("\tprimary key not yet valid\n"); } else { if (DBG_LOOKUP) log_debug ("\tprimary key is fine\n"); latest_date = pk->timestamp; latest_key = node; } } } if (!latest_key) { err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); log_error (_("key \"%s\" not found: %s\n"), userid, gpg_strerror (err)); goto leave; } pk = latest_key->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tusing key %08lX\n", (ulong) keyid_from_pk (pk, NULL)); switch (pk->pubkey_algo) { case PUBKEY_ALGO_DSA: identifier = "ssh-dss"; err = key_to_sshblob (&mb, identifier, pk->pkey[0], pk->pkey[1], pk->pkey[2], pk->pkey[3], NULL); break; case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_S: identifier = "ssh-rsa"; err = key_to_sshblob (&mb, identifier, pk->pkey[1], pk->pkey[0], NULL); break; case PUBKEY_ALGO_ECDSA: { char *curveoid; const char *curve; curveoid = openpgp_oid_to_str (pk->pkey[0]); if (!curveoid) err = gpg_error_from_syserror (); else if (!(curve = openpgp_oid_to_curve (curveoid, 0))) err = gpg_error (GPG_ERR_UNKNOWN_CURVE); else { if (!strcmp (curve, "nistp256")) identifier = "ecdsa-sha2-nistp256"; else if (!strcmp (curve, "nistp384")) identifier = "ecdsa-sha2-nistp384"; else if (!strcmp (curve, "nistp521")) identifier = "ecdsa-sha2-nistp521"; if (!identifier) err = gpg_error (GPG_ERR_UNKNOWN_CURVE); else err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL); } xfree (curveoid); } break; case PUBKEY_ALGO_EDDSA: if (!openpgp_oid_is_ed25519 (pk->pkey[0])) err = gpg_error (GPG_ERR_UNKNOWN_CURVE); else { identifier = "ssh-ed25519"; err = key_to_sshblob (&mb, identifier, pk->pkey[1], NULL); } break; case PUBKEY_ALGO_ELGAMAL_E: case PUBKEY_ALGO_ELGAMAL: err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); break; default: err = GPG_ERR_PUBKEY_ALGO; break; } if (!identifier) goto leave; if (opt.outfile && *opt.outfile && strcmp (opt.outfile, "-")) fp = es_fopen ((fname = opt.outfile), "w"); else fp = es_stdout; if (!fp) { err = gpg_error_from_syserror (); log_error (_("error creating '%s': %s\n"), fname, gpg_strerror (err)); goto leave; } es_fprintf (fp, "%s ", identifier); err = b64enc_start_es (&b64_state, fp, ""); if (!err) { void *blob; size_t bloblen; blob = get_membuf (&mb, &bloblen); if (blob) { err = b64enc_write (&b64_state, blob, bloblen); xfree (blob); if (err) goto leave; } err = b64enc_finish (&b64_state); } if (err) goto leave; es_fprintf (fp, " openpgp:0x%08lX\n", (ulong)keyid_from_pk (pk, NULL)); if (es_ferror (fp)) err = gpg_error_from_syserror (); else { if (es_fclose (fp)) err = gpg_error_from_syserror (); fp = NULL; } if (err) log_error (_("error writing '%s': %s\n"), fname, gpg_strerror (err)); leave: es_fclose (fp); xfree (get_membuf (&mb, NULL)); release_kbnode (keyblock); return err; } diff --git a/g10/getkey.c b/g10/getkey.c index 57617a0a9..2cc56cdbd 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -1,4133 +1,4135 @@ /* getkey.c - Get a key from the database * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2008, 2010 Free Software Foundation, Inc. * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "packet.h" #include "../common/iobuf.h" #include "keydb.h" #include "options.h" #include "main.h" #include "trustdb.h" #include "../common/i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "objcache.h" #include "../common/host2net.h" #include "../common/mbox-util.h" #include "../common/status.h" #define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE #define MAX_UID_CACHE_ENTRIES PK_UID_CACHE_SIZE #if MAX_PK_CACHE_ENTRIES < 2 #error We need the cache for key creation #endif /* Flags values returned by the lookup code. Note that the values are * directly used by the KEY_CONSIDERED status line. */ #define LOOKUP_NOT_SELECTED (1<<0) #define LOOKUP_ALL_SUBKEYS_EXPIRED (1<<1) /* or revoked */ /* A context object used by the lookup functions. */ struct getkey_ctx_s { /* Part of the search criteria: whether the search is an exact search or not. A search that is exact requires that a key or subkey meet all of the specified criteria. A search that is not exact allows selecting a different key or subkey from the keyblock that matched the criteria. Further, an exact search returns the key or subkey that matched whereas a non-exact search typically returns the primary key. See finish_lookup for details. */ int exact; /* Part of the search criteria: Whether the caller only wants keys with an available secret key. This is used by getkey_next to get the next result with the same initial criteria. */ int want_secret; /* Part of the search criteria: The type of the requested key. A mask of PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. If non-zero, then for a key to match, it must implement one of the required uses. */ int req_usage; /* The database handle. */ KEYDB_HANDLE kr_handle; /* Whether we should call xfree() on the context when the context is released using getkey_end()). */ int not_allocated; /* This variable is used as backing store for strings which have their address used in ITEMS. */ strlist_t extra_list; /* Hack to return the mechanism (AKL_foo) used to find the key. */ int found_via_akl; /* Part of the search criteria: The low-level search specification as passed to keydb_search. */ int nitems; /* This must be the last element in the structure. When we allocate the structure, we allocate it so that ITEMS can hold NITEMS. */ KEYDB_SEARCH_DESC items[1]; }; #if 0 static struct { int any; int okay_count; int nokey_count; int error_count; } lkup_stats[21]; #endif typedef struct keyid_list { struct keyid_list *next; byte fprlen; char fpr[MAX_FINGERPRINT_LEN]; u32 keyid[2]; } *keyid_list_t; #if MAX_PK_CACHE_ENTRIES typedef struct pk_cache_entry { struct pk_cache_entry *next; u32 keyid[2]; PKT_public_key *pk; } *pk_cache_entry_t; static pk_cache_entry_t pk_cache; static int pk_cache_entries; /* Number of entries in pk cache. */ static int pk_cache_disabled; #endif #if MAX_UID_CACHE_ENTRIES < 5 #error we really need the userid cache #endif static void merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock); static int lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret, kbnode_t *ret_keyblock, kbnode_t *ret_found_key); static kbnode_t finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, int want_secret, unsigned int *r_flags); static void print_status_key_considered (kbnode_t keyblock, unsigned int flags); #if 0 static void print_stats () { int i; for (i = 0; i < DIM (lkup_stats); i++) { if (lkup_stats[i].any) es_fprintf (es_stderr, "lookup stats: mode=%-2d ok=%-6d nokey=%-6d err=%-6d\n", i, lkup_stats[i].okay_count, lkup_stats[i].nokey_count, lkup_stats[i].error_count); } } #endif /* Cache a copy of a public key in the public key cache. PK is not * cached if caching is disabled (via getkey_disable_caches), if * PK->FLAGS.DONT_CACHE is set, we don't know how to derive a key id * from the public key (e.g., unsupported algorithm), or a key with * the key id is already in the cache. * * The public key packet is copied into the cache using * copy_public_key. Thus, any secret parts are not copied, for * instance. * * This cache is filled by get_pubkey and is read by get_pubkey and * get_pubkey_fast. */ void cache_public_key (PKT_public_key * pk) { #if MAX_PK_CACHE_ENTRIES pk_cache_entry_t ce, ce2; u32 keyid[2]; if (pk_cache_disabled) return; if (pk->flags.dont_cache) return; if (is_ELGAMAL (pk->pubkey_algo) || pk->pubkey_algo == PUBKEY_ALGO_DSA || pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH || is_RSA (pk->pubkey_algo)) { keyid_from_pk (pk, keyid); } else return; /* Don't know how to get the keyid. */ for (ce = pk_cache; ce; ce = ce->next) if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) { if (DBG_CACHE) log_debug ("cache_public_key: already in cache\n"); return; } if (pk_cache_entries >= MAX_PK_CACHE_ENTRIES) { int n; /* Remove the last 50% of the entries. */ for (ce = pk_cache, n = 0; ce && n < pk_cache_entries/2; n++) ce = ce->next; if (ce && ce != pk_cache && ce->next) { ce2 = ce->next; ce->next = NULL; ce = ce2; for (; ce; ce = ce2) { ce2 = ce->next; free_public_key (ce->pk); xfree (ce); pk_cache_entries--; } } log_assert (pk_cache_entries < MAX_PK_CACHE_ENTRIES); } pk_cache_entries++; ce = xmalloc (sizeof *ce); ce->next = pk_cache; pk_cache = ce; ce->pk = copy_public_key (NULL, pk); ce->keyid[0] = keyid[0]; ce->keyid[1] = keyid[1]; #endif } /* Return a const utf-8 string with the text "[User ID not found]". This function is required so that we don't need to switch gettext's encoding temporary. */ static const char * user_id_not_found_utf8 (void) { static char *text; if (!text) text = native_to_utf8 (_("[User ID not found]")); return text; } /* Disable and drop the public key cache (which is filled by cache_public_key and get_pubkey). Note: there is currently no way to re-enable this cache. */ void getkey_disable_caches () { #if MAX_PK_CACHE_ENTRIES { pk_cache_entry_t ce, ce2; for (ce = pk_cache; ce; ce = ce2) { ce2 = ce->next; free_public_key (ce->pk); xfree (ce); } pk_cache_disabled = 1; pk_cache_entries = 0; pk_cache = NULL; } #endif /* fixme: disable user id cache ? */ } /* Free a list of pubkey_t objects. */ void pubkeys_free (pubkey_t keys) { while (keys) { pubkey_t next = keys->next; xfree (keys->pk); release_kbnode (keys->keyblock); xfree (keys); keys = next; } } static void pk_from_block (PKT_public_key *pk, kbnode_t keyblock, kbnode_t found_key) { kbnode_t a = found_key ? found_key : keyblock; log_assert (a->pkt->pkttype == PKT_PUBLIC_KEY || a->pkt->pkttype == PKT_PUBLIC_SUBKEY); copy_public_key (pk, a->pkt->pkt.public_key); } /* Specialized version of get_pubkey which retrieves the key based on * information in SIG. In contrast to get_pubkey PK is required. */ gpg_error_t get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig) { const byte *fpr; size_t fprlen; /* First try the new ISSUER_FPR info. */ fpr = issuer_fpr_raw (sig, &fprlen); if (fpr && !get_pubkey_byfprint (ctrl, pk, NULL, fpr, fprlen)) return 0; /* Fallback to use the ISSUER_KEYID. */ return get_pubkey (ctrl, pk, sig->keyid); } /* Return the public key with the key id KEYID and store it at PK. * The resources in *PK should be released using * release_public_key_parts(). This function also stores a copy of * the public key in the user id cache (see cache_public_key). * * If PK is NULL, this function just stores the public key in the * cache and returns the usual return code. * * PK->REQ_USAGE (which is a mask of PUBKEY_USAGE_SIG, * PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT) is passed through to the * lookup function. If this is non-zero, only keys with the specified * usage will be returned. As such, it is essential that * PK->REQ_USAGE be correctly initialized! * * Returns 0 on success, GPG_ERR_NO_PUBKEY if there is no public key * with the specified key id, or another error code if an error * occurs. * * If the data was not read from the cache, then the self-signed data * has definitely been merged into the public key using * merge_selfsigs. */ int get_pubkey (ctrl_t ctrl, PKT_public_key * pk, u32 * keyid) { int internal = 0; int rc = 0; #if MAX_PK_CACHE_ENTRIES if (pk) { /* Try to get it from the cache. We don't do this when pk is NULL as it does not guarantee that the user IDs are cached. */ pk_cache_entry_t ce; for (ce = pk_cache; ce; ce = ce->next) { if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1]) /* XXX: We don't check PK->REQ_USAGE here, but if we don't read from the cache, we do check it! */ { copy_public_key (pk, ce->pk); return 0; } } } #endif /* More init stuff. */ if (!pk) { internal++; pk = xtrycalloc (1, sizeof *pk); if (!pk) { rc = gpg_error_from_syserror (); goto leave; } } /* Do a lookup. */ { struct getkey_ctx_s ctx; kbnode_t kb = NULL; kbnode_t found_key = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; if (ctrl && ctrl->cached_getkey_kdb) { ctx.kr_handle = ctrl->cached_getkey_kdb; ctrl->cached_getkey_kdb = NULL; keydb_search_reset (ctx.kr_handle); } else { - ctx.kr_handle = keydb_new (); + ctx.kr_handle = keydb_new (ctrl); if (!ctx.kr_handle) { rc = gpg_error_from_syserror (); goto leave; } } ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_usage = pk->req_usage; rc = lookup (ctrl, &ctx, 0, &kb, &found_key); if (!rc) { pk_from_block (pk, kb, found_key); } getkey_end (ctrl, &ctx); release_kbnode (kb); } if (!rc) goto leave; rc = GPG_ERR_NO_PUBKEY; leave: if (!rc) cache_public_key (pk); if (internal) free_public_key (pk); return rc; } /* Similar to get_pubkey, but it does not take PK->REQ_USAGE into * account nor does it merge in the self-signed data. This function * also only considers primary keys. It is intended to be used as a * quick check of the key to avoid recursion. It should only be used * in very certain cases. Like get_pubkey and unlike any of the other * lookup functions, this function also consults the user id cache * (see cache_public_key). * * Return the public key in *PK. The resources in *PK should be * released using release_public_key_parts(). */ int -get_pubkey_fast (PKT_public_key * pk, u32 * keyid) +get_pubkey_fast (ctrl_t ctrl, PKT_public_key * pk, u32 * keyid) { int rc = 0; KEYDB_HANDLE hd; KBNODE keyblock; u32 pkid[2]; log_assert (pk); #if MAX_PK_CACHE_ENTRIES { /* Try to get it from the cache */ pk_cache_entry_t ce; for (ce = pk_cache; ce; ce = ce->next) { if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] /* Only consider primary keys. */ && ce->pk->keyid[0] == ce->pk->main_keyid[0] && ce->pk->keyid[1] == ce->pk->main_keyid[1]) { if (pk) copy_public_key (pk, ce->pk); return 0; } } } #endif - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) return gpg_error_from_syserror (); rc = keydb_search_kid (hd, keyid); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { keydb_release (hd); return GPG_ERR_NO_PUBKEY; } rc = keydb_get_keyblock (hd, &keyblock); keydb_release (hd); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); return GPG_ERR_NO_PUBKEY; } log_assert (keyblock && keyblock->pkt && keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* We return the primary key. If KEYID matched a subkey, then we return an error. */ keyid_from_pk (keyblock->pkt->pkt.public_key, pkid); if (keyid[0] == pkid[0] && keyid[1] == pkid[1]) copy_public_key (pk, keyblock->pkt->pkt.public_key); else rc = GPG_ERR_NO_PUBKEY; release_kbnode (keyblock); /* Not caching key here since it won't have all of the fields properly set. */ return rc; } /* Return the entire keyblock used to create SIG. This is a * specialized version of get_pubkeyblock. * * FIXME: This is a hack because get_pubkey_for_sig was already called * and it could have used a cache to hold the key. */ kbnode_t get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig) { const byte *fpr; size_t fprlen; kbnode_t keyblock; /* First try the new ISSUER_FPR info. */ fpr = issuer_fpr_raw (sig, &fprlen); if (fpr && !get_pubkey_byfprint (ctrl, NULL, &keyblock, fpr, fprlen)) return keyblock; /* Fallback to use the ISSUER_KEYID. */ return get_pubkeyblock (ctrl, sig->keyid); } /* Return the key block for the key with key id KEYID or NULL, if an * error occurs. Use release_kbnode() to release the key block. * * The self-signed data has already been merged into the public key * using merge_selfsigs. */ kbnode_t get_pubkeyblock (ctrl_t ctrl, u32 * keyid) { struct getkey_ctx_s ctx; int rc = 0; KBNODE keyblock = NULL; memset (&ctx, 0, sizeof ctx); /* No need to set exact here because we want the entire block. */ ctx.not_allocated = 1; - ctx.kr_handle = keydb_new (); + ctx.kr_handle = keydb_new (ctrl); if (!ctx.kr_handle) return NULL; ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; rc = lookup (ctrl, &ctx, 0, &keyblock, NULL); getkey_end (ctrl, &ctx); return rc ? NULL : keyblock; } /* Return the public key with the key id KEYID iff the secret key is * available and store it at PK. The resources should be released * using release_public_key_parts(). * * Unlike other lookup functions, PK may not be NULL. PK->REQ_USAGE * is passed through to the lookup function and is a mask of * PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. Thus, it * must be valid! If this is non-zero, only keys with the specified * usage will be returned. * * Returns 0 on success. If a public key with the specified key id is * not found or a secret key is not available for that public key, an * error code is returned. Note: this function ignores legacy keys. * An error code is also return if an error occurs. * * The self-signed data has already been merged into the public key * using merge_selfsigs. */ gpg_error_t get_seckey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid) { gpg_error_t err; struct getkey_ctx_s ctx; kbnode_t keyblock = NULL; kbnode_t found_key = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; /* Use the key ID exactly as given. */ ctx.not_allocated = 1; - ctx.kr_handle = keydb_new (); + ctx.kr_handle = keydb_new (ctrl); if (!ctx.kr_handle) return gpg_error_from_syserror (); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx.items[0].u.kid[0] = keyid[0]; ctx.items[0].u.kid[1] = keyid[1]; ctx.req_usage = pk->req_usage; err = lookup (ctrl, &ctx, 1, &keyblock, &found_key); if (!err) { pk_from_block (pk, keyblock, found_key); } getkey_end (ctrl, &ctx); release_kbnode (keyblock); if (!err) { err = agent_probe_secret_key (/*ctrl*/NULL, pk); if (err) release_public_key_parts (pk); } return err; } /* Skip unusable keys. A key is unusable if it is revoked, expired or disabled or if the selected user id is revoked or expired. */ static int skip_unusable (void *opaque, u32 * keyid, int uid_no) { ctrl_t ctrl = opaque; int unusable = 0; KBNODE keyblock; PKT_public_key *pk; keyblock = get_pubkeyblock (ctrl, keyid); if (!keyblock) { log_error ("error checking usability status of %s\n", keystr (keyid)); goto leave; } pk = keyblock->pkt->pkt.public_key; /* Is the key revoked or expired? */ if (pk->flags.revoked || pk->has_expired) unusable = 1; /* Is the user ID in question revoked or expired? */ if (!unusable && uid_no) { KBNODE node; int uids_seen = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *user_id = node->pkt->pkt.user_id; uids_seen ++; if (uids_seen != uid_no) continue; if (user_id->flags.revoked || user_id->flags.expired) unusable = 1; break; } } /* If UID_NO is non-zero, then the keyblock better have at least that many UIDs. */ log_assert (uids_seen == uid_no); } if (!unusable) unusable = pk_is_disabled (pk); leave: release_kbnode (keyblock); return unusable; } /* Search for keys matching some criteria. If RETCTX is not NULL, then the constructed context is returned in *RETCTX so that getpubkey_next can be used to get subsequent results. In this case, getkey_end() must be used to free the search context. If RETCTX is not NULL, then RET_KDBHD must be NULL. If NAMELIST is not NULL, then a search query is constructed using classify_user_id on each of the strings in the list. (Recall: the database does an OR of the terms, not an AND.) If NAMELIST is NULL, then all results are returned. If PK is not NULL, the public key of the first result is returned in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is set, it is used to filter the search results. See the documentation for finish_lookup to understand exactly how this is used. Note: The self-signed data has already been merged into the public key using merge_selfsigs. Free *PK by calling release_public_key_parts (or, if PK was allocated using xfree, you can use free_public_key, which calls release_public_key_parts(PK) and then xfree(PK)). If WANT_SECRET is set, then only keys with an available secret key (either locally or via key registered on a smartcard) are returned. If INCLUDE_UNUSABLE is set, then unusable keys (see the documentation for skip_unusable for an exact definition) are skipped unless they are looked up by key id or by fingerprint. If RET_KB is not NULL, the keyblock is returned in *RET_KB. This should be freed using release_kbnode(). If RET_KDBHD is not NULL, then the new database handle used to conduct the search is returned in *RET_KDBHD. This can be used to get subsequent results using keydb_search_next. Note: in this case, no advanced filtering is done for subsequent results (e.g., WANT_SECRET and PK->REQ_USAGE are not respected). This function returns 0 on success. Otherwise, an error code is returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY (if want_secret is set) is returned if the key is not found. */ static int key_byname (ctrl_t ctrl, GETKEY_CTX *retctx, strlist_t namelist, PKT_public_key *pk, int want_secret, int include_unusable, KBNODE * ret_kb, KEYDB_HANDLE * ret_kdbhd) { int rc = 0; int n; strlist_t r; GETKEY_CTX ctx; KBNODE help_kb = NULL; KBNODE found_key = NULL; if (retctx) { /* Reset the returned context in case of error. */ log_assert (!ret_kdbhd); /* Not allowed because the handle is stored in the context. */ *retctx = NULL; } if (ret_kdbhd) *ret_kdbhd = NULL; if (!namelist) /* No search terms: iterate over the whole DB. */ { ctx = xmalloc_clear (sizeof *ctx); ctx->nitems = 1; ctx->items[0].mode = KEYDB_SEARCH_MODE_FIRST; if (!include_unusable) { ctx->items[0].skipfnc = skip_unusable; ctx->items[0].skipfncvalue = ctrl; } } else { /* Build the search context. */ for (n = 0, r = namelist; r; r = r->next) n++; /* CTX has space for a single search term at the end. Thus, we need to allocate sizeof *CTX plus (n - 1) sizeof CTX->ITEMS. */ ctx = xmalloc_clear (sizeof *ctx + (n - 1) * sizeof ctx->items); ctx->nitems = n; for (n = 0, r = namelist; r; r = r->next, n++) { gpg_error_t err; err = classify_user_id (r->d, &ctx->items[n], 1); if (ctx->items[n].exact) ctx->exact = 1; if (err) { xfree (ctx); return gpg_err_code (err); /* FIXME: remove gpg_err_code. */ } if (!include_unusable && ctx->items[n].mode != KEYDB_SEARCH_MODE_SHORT_KID && ctx->items[n].mode != KEYDB_SEARCH_MODE_LONG_KID && ctx->items[n].mode != KEYDB_SEARCH_MODE_FPR) { ctx->items[n].skipfnc = skip_unusable; ctx->items[n].skipfncvalue = ctrl; } } } ctx->want_secret = want_secret; - ctx->kr_handle = keydb_new (); + ctx->kr_handle = keydb_new (ctrl); if (!ctx->kr_handle) { rc = gpg_error_from_syserror (); getkey_end (ctrl, ctx); return rc; } if (!ret_kb) ret_kb = &help_kb; if (pk) { ctx->req_usage = pk->req_usage; } rc = lookup (ctrl, ctx, want_secret, ret_kb, &found_key); if (!rc && pk) { pk_from_block (pk, *ret_kb, found_key); } release_kbnode (help_kb); if (retctx) /* Caller wants the context. */ *retctx = ctx; else { if (ret_kdbhd) { *ret_kdbhd = ctx->kr_handle; ctx->kr_handle = NULL; } getkey_end (ctrl, ctx); } return rc; } /* Find a public key identified by NAME. * * If name appears to be a valid RFC822 mailbox (i.e., email address) * and auto key lookup is enabled (mode != GET_PUBKEY_NO_AKL), then * the specified auto key lookup methods (--auto-key-lookup) are used * to import the key into the local keyring. Otherwise, just the * local keyring is consulted. * * MODE can be one of: * GET_PUBKEY_NORMAL - The standard mode * GET_PUBKEY_NO_AKL - The auto key locate functionality is * disabled and only the local key ring is * considered. Note: the local key ring is * consulted even if local is not in the * auto-key-locate option list! * GET_PUBKEY_NO_LOCAL - Only the auto key locate functionaly is * used and no local search is done. * * If RETCTX is not NULL, then the constructed context is returned in * *RETCTX so that getpubkey_next can be used to get subsequent * results. In this case, getkey_end() must be used to free the * search context. If RETCTX is not NULL, then RET_KDBHD must be * NULL. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! PK->REQ_USAGE is * passed through to the lookup function and is a mask of * PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. If this * is non-zero, only keys with the specified usage will be returned. * Note: The self-signed data has already been merged into the public * key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * NAME is a string, which is turned into a search query using * classify_user_id. * * If RET_KEYBLOCK is not NULL, the keyblock is returned in * *RET_KEYBLOCK. This should be freed using release_kbnode(). * * If RET_KDBHD is not NULL, then the new database handle used to * conduct the search is returned in *RET_KDBHD. This can be used to * get subsequent results using keydb_search_next or to modify the * returned record. Note: in this case, no advanced filtering is done * for subsequent results (e.g., PK->REQ_USAGE is not respected). * Unlike RETCTX, this is always returned. * * If INCLUDE_UNUSABLE is set, then unusable keys (see the * documentation for skip_unusable for an exact definition) are * skipped unless they are looked up by key id or by fingerprint. * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY * (if want_secret is set) is returned if the key is not found. */ int get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, GETKEY_CTX * retctx, PKT_public_key * pk, const char *name, KBNODE * ret_keyblock, KEYDB_HANDLE * ret_kdbhd, int include_unusable) { int rc; strlist_t namelist = NULL; struct akl *akl; int is_mbox; int nodefault = 0; int anylocalfirst = 0; int mechanism_type = AKL_NODEFAULT; /* If RETCTX is not NULL, then RET_KDBHD must be NULL. */ log_assert (retctx == NULL || ret_kdbhd == NULL); if (retctx) *retctx = NULL; /* Does NAME appear to be a mailbox (mail address)? */ is_mbox = is_valid_mailbox (name); /* The auto-key-locate feature works as follows: there are a number * of methods to look up keys. By default, the local keyring is * tried first. Then, each method listed in the --auto-key-locate is * tried in the order it appears. * * This can be changed as follows: * * - if nodefault appears anywhere in the list of options, then * the local keyring is not tried first, or, * * - if local appears anywhere in the list of options, then the * local keyring is not tried first, but in the order in which * it was listed in the --auto-key-locate option. * * Note: we only save the search context in RETCTX if the local * method is the first method tried (either explicitly or * implicitly). */ if (mode == GET_PUBKEY_NO_LOCAL) nodefault = 1; /* Auto-key-locate but ignore "local". */ else if (mode != GET_PUBKEY_NO_AKL) { /* auto-key-locate is enabled. */ /* nodefault is true if "nodefault" or "local" appear. */ for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type == AKL_NODEFAULT || akl->type == AKL_LOCAL) { nodefault = 1; break; } /* anylocalfirst is true if "local" appears before any other search methods (except "nodefault"). */ for (akl = opt.auto_key_locate; akl; akl = akl->next) if (akl->type != AKL_NODEFAULT) { if (akl->type == AKL_LOCAL) anylocalfirst = 1; break; } } if (!nodefault) { /* "nodefault" didn't occur. Thus, "local" is implicitly the * first method to try. */ anylocalfirst = 1; } if (mode == GET_PUBKEY_NO_LOCAL) { /* Force using the AKL. If IS_MBOX is not set this is the final * error code. */ rc = GPG_ERR_NO_PUBKEY; } else if (nodefault && is_mbox) { /* Either "nodefault" or "local" (explicitly) appeared in the * auto key locate list and NAME appears to be an email address. * Don't try the local keyring. */ rc = GPG_ERR_NO_PUBKEY; } else { /* Either "nodefault" and "local" don't appear in the auto key * locate list (in which case we try the local keyring first) or * NAME does not appear to be an email address (in which case we * only try the local keyring). In this case, lookup NAME in * the local keyring. */ add_to_strlist (&namelist, name); rc = key_byname (ctrl, retctx, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); } /* If the requested name resembles a valid mailbox and automatic retrieval has been enabled, we try to import the key. */ if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY && mode != GET_PUBKEY_NO_AKL && is_mbox) { /* NAME wasn't present in the local keyring (or we didn't try * the local keyring). Since the auto key locate feature is * enabled and NAME appears to be an email address, try the auto * locate feature. */ for (akl = opt.auto_key_locate; akl; akl = akl->next) { unsigned char *fpr = NULL; size_t fpr_len; int did_akl_local = 0; int no_fingerprint = 0; const char *mechanism_string = "?"; mechanism_type = akl->type; switch (mechanism_type) { case AKL_NODEFAULT: /* This is a dummy mechanism. */ mechanism_string = ""; rc = GPG_ERR_NO_PUBKEY; break; case AKL_LOCAL: if (mode == GET_PUBKEY_NO_LOCAL) { mechanism_string = ""; rc = GPG_ERR_NO_PUBKEY; } else { mechanism_string = "Local"; did_akl_local = 1; if (retctx) { getkey_end (ctrl, *retctx); *retctx = NULL; } add_to_strlist (&namelist, name); rc = key_byname (ctrl, anylocalfirst ? retctx : NULL, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); } break; case AKL_CERT: mechanism_string = "DNS CERT"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_cert (ctrl, name, 0, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_PKA: mechanism_string = "PKA"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_pka (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_DANE: mechanism_string = "DANE"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_cert (ctrl, name, 1, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_WKD: mechanism_string = "WKD"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_wkd (ctrl, name, 0, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_LDAP: mechanism_string = "LDAP"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_ldap (ctrl, name, &fpr, &fpr_len); glo_ctrl.in_auto_key_retrieve--; break; case AKL_KEYSERVER: /* Strictly speaking, we don't need to only use a valid * mailbox for the getname search, but it helps cut down * on the problem of searching for something like "john" * and getting a whole lot of keys back. */ if (keyserver_any_configured (ctrl)) { mechanism_string = "keyserver"; glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, opt.keyserver); glo_ctrl.in_auto_key_retrieve--; } else { mechanism_string = "Unconfigured keyserver"; rc = GPG_ERR_NO_PUBKEY; } break; case AKL_SPEC: { struct keyserver_spec *keyserver; mechanism_string = akl->spec->uri; keyserver = keyserver_match (akl->spec); glo_ctrl.in_auto_key_retrieve++; rc = keyserver_import_name (ctrl, name, &fpr, &fpr_len, keyserver); glo_ctrl.in_auto_key_retrieve--; } break; } /* Use the fingerprint of the key that we actually fetched. * This helps prevent problems where the key that we fetched * doesn't have the same name that we used to fetch it. In * the case of CERT and PKA, this is an actual security * requirement as the URL might point to a key put in by an * attacker. By forcing the use of the fingerprint, we * won't use the attacker's key here. */ if (!rc && fpr) { char fpr_string[MAX_FINGERPRINT_LEN * 2 + 1]; log_assert (fpr_len <= MAX_FINGERPRINT_LEN); free_strlist (namelist); namelist = NULL; bin2hex (fpr, fpr_len, fpr_string); if (opt.verbose) log_info ("auto-key-locate found fingerprint %s\n", fpr_string); add_to_strlist (&namelist, fpr_string); } else if (!rc && !fpr && !did_akl_local) { /* The acquisition method said no failure occurred, but * it didn't return a fingerprint. That's a failure. */ no_fingerprint = 1; rc = GPG_ERR_NO_PUBKEY; } xfree (fpr); fpr = NULL; if (!rc && !did_akl_local) { /* There was no error and we didn't do a local lookup. * This means that we imported a key into the local * keyring. Try to read the imported key from the * keyring. */ if (retctx) { getkey_end (ctrl, *retctx); *retctx = NULL; } rc = key_byname (ctrl, anylocalfirst ? retctx : NULL, namelist, pk, 0, include_unusable, ret_keyblock, ret_kdbhd); } if (!rc) { /* Key found. */ if (opt.verbose) log_info (_("automatically retrieved '%s' via %s\n"), name, mechanism_string); break; } if ((gpg_err_code (rc) != GPG_ERR_NO_PUBKEY || opt.verbose || no_fingerprint) && *mechanism_string) log_info (_("error retrieving '%s' via %s: %s\n"), name, mechanism_string, no_fingerprint ? _("No fingerprint") : gpg_strerror (rc)); } } if (rc && retctx) { getkey_end (ctrl, *retctx); *retctx = NULL; } if (retctx && *retctx) { log_assert (!(*retctx)->extra_list); (*retctx)->extra_list = namelist; (*retctx)->found_via_akl = mechanism_type; } else free_strlist (namelist); return rc; } /* Comparison machinery for get_best_pubkey_byname. */ /* First we have a struct to cache computed information about the key * in question. */ struct pubkey_cmp_cookie { int valid; /* Is this cookie valid? */ PKT_public_key key; /* The key. */ PKT_user_id *uid; /* The matching UID packet. */ unsigned int validity; /* Computed validity of (KEY, UID). */ u32 creation_time; /* Creation time of the newest subkey capable of encryption. */ }; /* Then we have a series of helper functions. */ static int key_is_ok (const PKT_public_key *key) { return (! key->has_expired && ! key->flags.revoked && key->flags.valid && ! key->flags.disabled); } static int uid_is_ok (const PKT_public_key *key, const PKT_user_id *uid) { return key_is_ok (key) && ! uid->flags.revoked; } static int subkey_is_ok (const PKT_public_key *sub) { return ! sub->flags.revoked && sub->flags.valid && ! sub->flags.disabled; } /* Return true if KEYBLOCK has only expired encryption subkyes. Note * that the function returns false if the key has no encryption * subkeys at all or the subkeys are revoked. */ static int only_expired_enc_subkeys (kbnode_t keyblock) { kbnode_t node; PKT_public_key *sub; int any = 0; for (node = find_next_kbnode (keyblock, PKT_PUBLIC_SUBKEY); node; node = find_next_kbnode (node, PKT_PUBLIC_SUBKEY)) { sub = node->pkt->pkt.public_key; if (!(sub->pubkey_usage & PUBKEY_USAGE_ENC)) continue; if (!subkey_is_ok (sub)) continue; any = 1; if (!sub->has_expired) return 0; } return any? 1 : 0; } /* Finally this function compares a NEW key to the former candidate * OLD. Returns < 0 if the old key is worse, > 0 if the old key is * better, == 0 if it is a tie. */ static int pubkey_cmp (ctrl_t ctrl, const char *name, struct pubkey_cmp_cookie *old, struct pubkey_cmp_cookie *new, KBNODE new_keyblock) { kbnode_t n; new->creation_time = 0; for (n = find_next_kbnode (new_keyblock, PKT_PUBLIC_SUBKEY); n; n = find_next_kbnode (n, PKT_PUBLIC_SUBKEY)) { PKT_public_key *sub = n->pkt->pkt.public_key; if ((sub->pubkey_usage & PUBKEY_USAGE_ENC) == 0) continue; if (! subkey_is_ok (sub)) continue; if (sub->timestamp > new->creation_time) new->creation_time = sub->timestamp; } for (n = find_next_kbnode (new_keyblock, PKT_USER_ID); n; n = find_next_kbnode (n, PKT_USER_ID)) { PKT_user_id *uid = n->pkt->pkt.user_id; char *mbox = mailbox_from_userid (uid->name, 0); int match = mbox ? strcasecmp (name, mbox) == 0 : 0; xfree (mbox); if (! match) continue; new->uid = scopy_user_id (uid); new->validity = get_validity (ctrl, new_keyblock, &new->key, uid, NULL, 0) & TRUST_MASK; new->valid = 1; if (! old->valid) return -1; /* No OLD key. */ if (! uid_is_ok (&old->key, old->uid) && uid_is_ok (&new->key, uid)) return -1; /* Validity of the NEW key is better. */ if (old->validity < new->validity) return -1; /* Validity of the NEW key is better. */ if (old->validity == new->validity && uid_is_ok (&new->key, uid) && old->creation_time < new->creation_time) return -1; /* Both keys are of the same validity, but the NEW key is newer. */ } /* Stick with the OLD key. */ return 1; } /* This function works like get_pubkey_byname, but if the name * resembles a mail address, the results are ranked and only the best * result is returned. */ gpg_error_t get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, GETKEY_CTX *retctx, PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, int include_unusable) { gpg_error_t err; struct getkey_ctx_s *ctx = NULL; int is_mbox = is_valid_mailbox (name); int wkd_tried = 0; if (retctx) *retctx = NULL; start_over: if (ctx) /* Clear in case of a start over. */ { if (ret_keyblock) { release_kbnode (*ret_keyblock); *ret_keyblock = NULL; } getkey_end (ctrl, ctx); ctx = NULL; } err = get_pubkey_byname (ctrl, mode, &ctx, pk, name, ret_keyblock, NULL, include_unusable); if (err) { getkey_end (ctrl, ctx); return err; } /* If the keyblock was retrieved from the local database and the key * has expired, do further checks. However, we can do this only if * the caller requested a keyblock. */ if (is_mbox && ctx && ctx->found_via_akl == AKL_LOCAL && ret_keyblock) { u32 now = make_timestamp (); PKT_public_key *pk2 = (*ret_keyblock)->pkt->pkt.public_key; int found; /* If the key has expired and its origin was the WKD then try to * get a fresh key from the WKD. We also try this if the key * has any only expired encryption subkeys. In case we checked * for a fresh copy in the last 3 hours we won't do that again. * Unfortunately that does not yet work because KEYUPDATE is * only updated during import iff the key has actually changed * (see import.c:import_one). */ if (!wkd_tried && pk2->keyorg == KEYORG_WKD && (pk2->keyupdate + 3*3600) < now && (pk2->has_expired || only_expired_enc_subkeys (*ret_keyblock))) { if (opt.verbose) log_info (_("checking for a fresh copy of an expired key via %s\n"), "WKD"); wkd_tried = 1; glo_ctrl.in_auto_key_retrieve++; found = !keyserver_import_wkd (ctrl, name, 0, NULL, NULL); glo_ctrl.in_auto_key_retrieve--; if (found) goto start_over; } } if (is_mbox && ctx) { /* Rank results and return only the most relevant key. */ struct pubkey_cmp_cookie best = { 0 }; struct pubkey_cmp_cookie new = { 0 }; kbnode_t new_keyblock; while (getkey_next (ctrl, ctx, &new.key, &new_keyblock) == 0) { int diff = pubkey_cmp (ctrl, name, &best, &new, new_keyblock); release_kbnode (new_keyblock); if (diff < 0) { /* New key is better. */ release_public_key_parts (&best.key); free_user_id (best.uid); best = new; } else if (diff > 0) { /* Old key is better. */ release_public_key_parts (&new.key); free_user_id (new.uid); } else { /* A tie. Keep the old key. */ release_public_key_parts (&new.key); free_user_id (new.uid); } new.uid = NULL; } getkey_end (ctrl, ctx); ctx = NULL; free_user_id (best.uid); best.uid = NULL; if (best.valid) { if (retctx || ret_keyblock) { ctx = xtrycalloc (1, sizeof **retctx); if (! ctx) err = gpg_error_from_syserror (); else { - ctx->kr_handle = keydb_new (); + ctx->kr_handle = keydb_new (ctrl); if (! ctx->kr_handle) { err = gpg_error_from_syserror (); xfree (ctx); ctx = NULL; if (retctx) *retctx = NULL; } else { u32 *keyid = pk_keyid (&best.key); ctx->exact = 1; ctx->nitems = 1; ctx->items[0].mode = KEYDB_SEARCH_MODE_LONG_KID; ctx->items[0].u.kid[0] = keyid[0]; ctx->items[0].u.kid[1] = keyid[1]; if (ret_keyblock) { release_kbnode (*ret_keyblock); *ret_keyblock = NULL; err = getkey_next (ctrl, ctx, NULL, ret_keyblock); } } } } if (pk) *pk = best.key; else release_public_key_parts (&best.key); } } if (err && ctx) { getkey_end (ctrl, ctx); ctx = NULL; } if (retctx && ctx) *retctx = ctx; else getkey_end (ctrl, ctx); return err; } /* Get a public key from a file. * * PK is the buffer to store the key. The caller needs to make sure * that PK->REQ_USAGE is valid. PK->REQ_USAGE is passed through to * the lookup function and is a mask of PUBKEY_USAGE_SIG, * PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT. If this is non-zero, only * keys with the specified usage will be returned. * * FNAME is the file name. That file should contain exactly one * keyblock. * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY is returned if the key * is not found. * * The self-signed data has already been merged into the public key * using merge_selfsigs. The caller must release the content of PK by * calling release_public_key_parts (or, if PK was malloced, using * free_public_key). */ gpg_error_t get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname) { gpg_error_t err; kbnode_t keyblock; kbnode_t found_key; unsigned int infoflags; err = read_key_from_file (ctrl, fname, &keyblock); if (!err) { /* Warning: node flag bits 0 and 1 should be preserved by * merge_selfsigs. FIXME: Check whether this still holds. */ merge_selfsigs (ctrl, keyblock); found_key = finish_lookup (keyblock, pk->req_usage, 0, 0, &infoflags); print_status_key_considered (keyblock, infoflags); if (found_key) pk_from_block (pk, keyblock, found_key); else err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); } release_kbnode (keyblock); return err; } /* Lookup a key with the specified fingerprint. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: this function does an exact search and thus the * returned public key may be a subkey rather than the primary key. * Note: The self-signed data has already been merged into the public * key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If PK->REQ_USAGE is set, it is used to filter the search results. * (Thus, if PK is not NULL, PK->REQ_USAGE must be valid!!!) See the * documentation for finish_lookup to understand exactly how this is * used. * * If R_KEYBLOCK is not NULL, then the first result's keyblock is * returned in *R_KEYBLOCK. This should be freed using * release_kbnode(). * * FPRINT is a byte array whose contents is the fingerprint to use as * the search term. FPRINT_LEN specifies the length of the * fingerprint (in bytes). Currently, only 16, 20, and 32-byte * fingerprints are supported. * * FIXME: We should replace this with the _byname function. This can * be done by creating a userID conforming to the unified fingerprint * style. */ int get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock, const byte * fprint, size_t fprint_len) { int rc; if (r_keyblock) *r_keyblock = NULL; if (fprint_len == 32 || fprint_len == 20 || fprint_len == 16) { struct getkey_ctx_s ctx; KBNODE kb = NULL; KBNODE found_key = NULL; memset (&ctx, 0, sizeof ctx); ctx.exact = 1; ctx.not_allocated = 1; /* FIXME: We should get the handle from the cache like we do in * get_pubkey. */ - ctx.kr_handle = keydb_new (); + ctx.kr_handle = keydb_new (ctrl); if (!ctx.kr_handle) return gpg_error_from_syserror (); ctx.nitems = 1; ctx.items[0].mode = KEYDB_SEARCH_MODE_FPR; memcpy (ctx.items[0].u.fpr, fprint, fprint_len); ctx.items[0].fprlen = fprint_len; if (pk) ctx.req_usage = pk->req_usage; rc = lookup (ctrl, &ctx, 0, &kb, &found_key); if (!rc && pk) pk_from_block (pk, kb, found_key); if (!rc && r_keyblock) { *r_keyblock = kb; kb = NULL; } release_kbnode (kb); getkey_end (ctrl, &ctx); } else rc = GPG_ERR_GENERAL; /* Oops */ return rc; } /* This function is similar to get_pubkey_byfprint, but it doesn't * merge the self-signed data into the public key and subkeys or into * the user ids. It also doesn't add the key to the user id cache. * Further, this function ignores PK->REQ_USAGE. * * This function is intended to avoid recursion and, as such, should * only be used in very specific situations. * * Like get_pubkey_byfprint, PK may be NULL. In that case, this * function effectively just checks for the existence of the key. */ gpg_error_t -get_pubkey_byfprint_fast (PKT_public_key * pk, +get_pubkey_byfprint_fast (ctrl_t ctrl, PKT_public_key * pk, const byte * fprint, size_t fprint_len) { gpg_error_t err; KBNODE keyblock; - err = get_keyblock_byfprint_fast (&keyblock, NULL, fprint, fprint_len, 0); + err = get_keyblock_byfprint_fast (ctrl, + &keyblock, NULL, fprint, fprint_len, 0); if (!err) { if (pk) copy_public_key (pk, keyblock->pkt->pkt.public_key); release_kbnode (keyblock); } return err; } /* This function is similar to get_pubkey_byfprint_fast but returns a * keydb handle at R_HD and the keyblock at R_KEYBLOCK. R_KEYBLOCK or * R_HD may be NULL. If LOCK is set the handle has been opend in * locked mode and keydb_disable_caching () has been called. On error * R_KEYBLOCK is set to NULL but R_HD must be released by the caller; * it may have a value of NULL, though. This allows to do an insert * operation on a locked keydb handle. */ gpg_error_t -get_keyblock_byfprint_fast (kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd, +get_keyblock_byfprint_fast (ctrl_t ctrl, + kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd, const byte *fprint, size_t fprint_len, int lock) { gpg_error_t err; KEYDB_HANDLE hd; kbnode_t keyblock; byte fprbuf[MAX_FINGERPRINT_LEN]; int i; if (r_keyblock) *r_keyblock = NULL; if (r_hd) *r_hd = NULL; for (i = 0; i < MAX_FINGERPRINT_LEN && i < fprint_len; i++) fprbuf[i] = fprint[i]; - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) return gpg_error_from_syserror (); if (lock) { err = keydb_lock (hd); if (err) { /* If locking did not work, we better don't return a handle * at all - there was a reason that locking has been * requested. */ keydb_release (hd); return err; } keydb_disable_caching (hd); } /* Fo all other errors we return the handle. */ if (r_hd) *r_hd = hd; err = keydb_search_fpr (hd, fprbuf, fprint_len); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { if (!r_hd) keydb_release (hd); return gpg_error (GPG_ERR_NO_PUBKEY); } err = keydb_get_keyblock (hd, &keyblock); if (err) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (err)); if (!r_hd) keydb_release (hd); return gpg_error (GPG_ERR_NO_PUBKEY); } log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY); /* Not caching key here since it won't have all of the fields properly set. */ if (r_keyblock) *r_keyblock = keyblock; else release_kbnode (keyblock); if (!r_hd) keydb_release (hd); return 0; } const char * parse_def_secret_key (ctrl_t ctrl) { KEYDB_HANDLE hd = NULL; strlist_t t; static int warned; for (t = opt.def_secret_key; t; t = t->next) { gpg_error_t err; KEYDB_SEARCH_DESC desc; KBNODE kb; KBNODE node; err = classify_user_id (t->d, &desc, 1); if (err) { log_error (_("secret key \"%s\" not found: %s\n"), t->d, gpg_strerror (err)); if (!opt.quiet) log_info (_("(check argument of option '%s')\n"), "--default-key"); continue; } if (! hd) { - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) return NULL; } else keydb_search_reset (hd); err = keydb_search (hd, &desc, 1, NULL); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) continue; if (err) { log_error (_("key \"%s\" not found: %s\n"), t->d, gpg_strerror (err)); t = NULL; break; } err = keydb_get_keyblock (hd, &kb); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); continue; } merge_selfsigs (ctrl, kb); err = gpg_error (GPG_ERR_NO_SECKEY); node = kb; do { PKT_public_key *pk = node->pkt->pkt.public_key; /* Check that the key has the signing capability. */ if (! (pk->pubkey_usage & PUBKEY_USAGE_SIG)) continue; /* Check if the key is valid. */ if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("not using %s as default key, %s", keystr_from_pk (pk), "revoked"); continue; } if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("not using %s as default key, %s", keystr_from_pk (pk), "expired"); continue; } if (pk_is_disabled (pk)) { if (DBG_LOOKUP) log_debug ("not using %s as default key, %s", keystr_from_pk (pk), "disabled"); continue; } err = agent_probe_secret_key (ctrl, pk); if (! err) /* This is a valid key. */ break; } while ((node = find_next_kbnode (node, PKT_PUBLIC_SUBKEY))); release_kbnode (kb); if (err) { if (! warned && ! opt.quiet) { log_info (_("Warning: not using '%s' as default key: %s\n"), t->d, gpg_strerror (GPG_ERR_NO_SECKEY)); print_reported_error (err, GPG_ERR_NO_SECKEY); } } else { if (! warned && ! opt.quiet) log_info (_("using \"%s\" as default secret key for signing\n"), t->d); break; } } if (! warned && opt.def_secret_key && ! t) log_info (_("all values passed to '%s' ignored\n"), "--default-key"); warned = 1; if (hd) keydb_release (hd); if (t) return t->d; return NULL; } /* Look up a secret key. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is * set, it is used to filter the search results. See the * documentation for finish_lookup to understand exactly how this is * used. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If --default-key was set, then the specified key is looked up. (In * this case, the default key is returned even if it is considered * unusable. See the documentation for skip_unusable for exactly what * this means.) * * Otherwise, this initiates a DB scan that returns all keys that are * usable (see previous paragraph for exactly what usable means) and * for which a secret key is available. * * This function returns the first match. Additional results can be * returned using getkey_next. */ gpg_error_t get_seckey_default (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; strlist_t namelist = NULL; int include_unusable = 1; const char *def_secret_key = parse_def_secret_key (ctrl); if (def_secret_key) add_to_strlist (&namelist, def_secret_key); else include_unusable = 0; err = key_byname (ctrl, NULL, namelist, pk, 1, include_unusable, NULL, NULL); free_strlist (namelist); return err; } /* Search for keys matching some criteria. * * If RETCTX is not NULL, then the constructed context is returned in * *RETCTX so that getpubkey_next can be used to get subsequent * results. In this case, getkey_end() must be used to free the * search context. If RETCTX is not NULL, then RET_KDBHD must be * NULL. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is * set, it is used to filter the search results. See the * documentation for finish_lookup to understand exactly how this is * used. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If NAMES is not NULL, then a search query is constructed using * classify_user_id on each of the strings in the list. (Recall: the * database does an OR of the terms, not an AND.) If NAMES is * NULL, then all results are returned. * * If WANT_SECRET is set, then only keys with an available secret key * (either locally or via key registered on a smartcard) are returned. * * This function does not skip unusable keys (see the documentation * for skip_unusable for an exact definition). * * If RET_KEYBLOCK is not NULL, the keyblock is returned in * *RET_KEYBLOCK. This should be freed using release_kbnode(). * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY * (if want_secret is set) is returned if the key is not found. */ gpg_error_t getkey_bynames (ctrl_t ctrl, getkey_ctx_t *retctx, PKT_public_key *pk, strlist_t names, int want_secret, kbnode_t *ret_keyblock) { return key_byname (ctrl, retctx, names, pk, want_secret, 1, ret_keyblock, NULL); } /* Search for one key matching some criteria. * * If RETCTX is not NULL, then the constructed context is returned in * *RETCTX so that getpubkey_next can be used to get subsequent * results. In this case, getkey_end() must be used to free the * search context. If RETCTX is not NULL, then RET_KDBHD must be * NULL. * * If PK is not NULL, the public key of the first result is returned * in *PK. Note: PK->REQ_USAGE must be valid!!! If PK->REQ_USAGE is * set, it is used to filter the search results. See the * documentation for finish_lookup to understand exactly how this is * used. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xfree, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * If NAME is not NULL, then a search query is constructed using * classify_user_id on the string. In this case, even unusable keys * (see the documentation for skip_unusable for an exact definition of * unusable) are returned. Otherwise, if --default-key was set, then * that key is returned (even if it is unusable). If neither of these * conditions holds, then the first usable key is returned. * * If WANT_SECRET is set, then only keys with an available secret key * (either locally or via key registered on a smartcard) are returned. * * This function does not skip unusable keys (see the documentation * for skip_unusable for an exact definition). * * If RET_KEYBLOCK is not NULL, the keyblock is returned in * *RET_KEYBLOCK. This should be freed using release_kbnode(). * * This function returns 0 on success. Otherwise, an error code is * returned. In particular, GPG_ERR_NO_PUBKEY or GPG_ERR_NO_SECKEY * (if want_secret is set) is returned if the key is not found. * * FIXME: We also have the get_pubkey_byname function which has a * different semantic. Should be merged with this one. */ gpg_error_t getkey_byname (ctrl_t ctrl, getkey_ctx_t *retctx, PKT_public_key *pk, const char *name, int want_secret, kbnode_t *ret_keyblock) { gpg_error_t err; strlist_t namelist = NULL; int with_unusable = 1; const char *def_secret_key = NULL; if (want_secret && !name) def_secret_key = parse_def_secret_key (ctrl); if (want_secret && !name && def_secret_key) add_to_strlist (&namelist, def_secret_key); else if (name) add_to_strlist (&namelist, name); else with_unusable = 0; err = key_byname (ctrl, retctx, namelist, pk, want_secret, with_unusable, ret_keyblock, NULL); /* FIXME: Check that we really return GPG_ERR_NO_SECKEY if WANT_SECRET has been used. */ free_strlist (namelist); return err; } /* Return the next search result. * * If PK is not NULL, the public key of the next result is returned in * *PK. Note: The self-signed data has already been merged into the * public key using merge_selfsigs. Free *PK by calling * release_public_key_parts (or, if PK was allocated using xmalloc, you * can use free_public_key, which calls release_public_key_parts(PK) * and then xfree(PK)). * * RET_KEYBLOCK can be given as NULL; if it is not NULL it the entire * found keyblock is returned which must be released with * release_kbnode. If the function returns an error NULL is stored at * RET_KEYBLOCK. * * The self-signed data has already been merged into the public key * using merge_selfsigs. */ gpg_error_t getkey_next (ctrl_t ctrl, getkey_ctx_t ctx, PKT_public_key *pk, kbnode_t *ret_keyblock) { int rc; /* Fixme: Make sure this is proper gpg_error */ KBNODE keyblock = NULL; KBNODE found_key = NULL; /* We need to disable the caching so that for an exact key search we won't get the result back from the cache and thus end up in an endless loop. The endless loop can occur, because the cache is used without respecting the current file pointer! */ keydb_disable_caching (ctx->kr_handle); /* FOUND_KEY is only valid as long as RET_KEYBLOCK is. If the * caller wants PK, but not RET_KEYBLOCK, we need hand in our own * keyblock. */ if (pk && ret_keyblock == NULL) ret_keyblock = &keyblock; rc = lookup (ctrl, ctx, ctx->want_secret, ret_keyblock, pk ? &found_key : NULL); if (!rc && pk) { log_assert (found_key); pk_from_block (pk, NULL, found_key); release_kbnode (keyblock); } return rc; } /* Release any resources used by a key listing context. This must be * called on the context returned by, e.g., getkey_byname. */ void getkey_end (ctrl_t ctrl, getkey_ctx_t ctx) { if (ctx) { #ifdef HAVE_W32_SYSTEM /* FIXME: This creates a big regression for Windows because the * keyring is only released after the global ctrl is released. * So if an operation does a getkey and then tries to modify the * keyring it will fail on Windows with a sharing violation. We * need to modify all keyring write operations to also take the * ctrl and close the cached_getkey_kdb handle to make writing * work. See: GnuPG-bug-id: 3097 */ (void)ctrl; keydb_release (ctx->kr_handle); #else /*!HAVE_W32_SYSTEM*/ if (ctrl && !ctrl->cached_getkey_kdb) ctrl->cached_getkey_kdb = ctx->kr_handle; else keydb_release (ctx->kr_handle); #endif /*!HAVE_W32_SYSTEM*/ free_strlist (ctx->extra_list); if (!ctx->not_allocated) xfree (ctx); } } /************************************************ ************* Merging stuff ******************** ************************************************/ /* Set the mainkey_id fields for all keys in KEYBLOCK. This is * usually done by merge_selfsigs but at some places we only need the * main_kid not a full merge. The function also guarantees that all * pk->keyids are computed. */ void setup_main_keyids (kbnode_t keyblock) { u32 kid[2], mainkid[2]; kbnode_t kbctx, node; PKT_public_key *pk; if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) BUG (); pk = keyblock->pkt->pkt.public_key; keyid_from_pk (pk, mainkid); for (kbctx=NULL; (node = walk_kbnode (keyblock, &kbctx, 0)); ) { if (!(node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) continue; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, kid); /* Make sure pk->keyid is set. */ if (!pk->main_keyid[0] && !pk->main_keyid[1]) { pk->main_keyid[0] = mainkid[0]; pk->main_keyid[1] = mainkid[1]; } } } /* KEYBLOCK corresponds to a public key block. This function merges * much of the information from the self-signed data into the public * key, public subkey and user id data structures. If you use the * high-level search API (e.g., get_pubkey) for looking up key blocks, * then you don't need to call this function. This function is * useful, however, if you change the keyblock, e.g., by adding or * removing a self-signed data packet. */ void merge_keys_and_selfsig (ctrl_t ctrl, kbnode_t keyblock) { if (!keyblock) ; else if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) merge_selfsigs (ctrl, keyblock); else log_debug ("FIXME: merging secret key blocks is not anymore available\n"); } static int parse_key_usage (PKT_signature * sig) { int key_usage = 0; const byte *p; size_t n; byte flags; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_FLAGS, &n); if (p && n) { /* First octet of the keyflags. */ flags = *p; if (flags & 1) { key_usage |= PUBKEY_USAGE_CERT; flags &= ~1; } if (flags & 2) { key_usage |= PUBKEY_USAGE_SIG; flags &= ~2; } /* We do not distinguish between encrypting communications and encrypting storage. */ if (flags & (0x04 | 0x08)) { key_usage |= PUBKEY_USAGE_ENC; flags &= ~(0x04 | 0x08); } if (flags & 0x20) { key_usage |= PUBKEY_USAGE_AUTH; flags &= ~0x20; } if (flags) key_usage |= PUBKEY_USAGE_UNKNOWN; if (!key_usage) key_usage |= PUBKEY_USAGE_NONE; } else if (p) /* Key flags of length zero. */ key_usage |= PUBKEY_USAGE_NONE; /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a capability that we do not handle. This serves to distinguish between a zero key usage which we handle as the default capabilities for that algorithm, and a usage that we do not handle. Likewise we use PUBKEY_USAGE_NONE to indicate that key_flags have been given but they do not specify any usage. */ return key_usage; } /* Apply information from SIGNODE (which is the valid self-signature * associated with that UID) to the UIDNODE: * - weather the UID has been revoked * - assumed creation date of the UID * - temporary store the keyflags here * - temporary store the key expiration time here * - mark whether the primary user ID flag hat been set. * - store the preferences */ static void fixup_uidnode (KBNODE uidnode, KBNODE signode, u32 keycreated) { PKT_user_id *uid = uidnode->pkt->pkt.user_id; PKT_signature *sig = signode->pkt->pkt.signature; const byte *p, *sym, *aead, *hash, *zip; size_t n, nsym, naead, nhash, nzip; sig->flags.chosen_selfsig = 1;/* We chose this one. */ uid->created = 0; /* Not created == invalid. */ if (IS_UID_REV (sig)) { uid->flags.revoked = 1; return; /* Has been revoked. */ } else uid->flags.revoked = 0; uid->expiredate = sig->expiredate; if (sig->flags.expired) { uid->flags.expired = 1; return; /* Has expired. */ } else uid->flags.expired = 0; uid->created = sig->timestamp; /* This one is okay. */ uid->selfsigversion = sig->version; /* If we got this far, it's not expired :) */ uid->flags.expired = 0; /* Store the key flags in the helper variable for later processing. */ uid->help_key_usage = parse_key_usage (sig); /* Ditto for the key expiration. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (p && buf32_to_u32 (p)) uid->help_key_expire = keycreated + buf32_to_u32 (p); else uid->help_key_expire = 0; /* Set the primary user ID flag - we will later wipe out some * of them to only have one in our keyblock. */ uid->flags.primary = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (p && *p) uid->flags.primary = 2; /* We could also query this from the unhashed area if it is not in * the hased area and then later try to decide which is the better * there should be no security problem with this. * For now we only look at the hashed one. */ /* Now build the preferences list. These must come from the hashed section so nobody can modify the ciphers a key is willing to accept. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM, &n); sym = p; nsym = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD, &n); aead = p; naead = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH, &n); hash = p; nhash = p ? n : 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR, &n); zip = p; nzip = p ? n : 0; if (uid->prefs) xfree (uid->prefs); n = nsym + naead + nhash + nzip; if (!n) uid->prefs = NULL; else { uid->prefs = xmalloc (sizeof (*uid->prefs) * (n + 1)); n = 0; for (; nsym; nsym--, n++) { uid->prefs[n].type = PREFTYPE_SYM; uid->prefs[n].value = *sym++; } for (; naead; naead--, n++) { uid->prefs[n].type = PREFTYPE_AEAD; uid->prefs[n].value = *aead++; } for (; nhash; nhash--, n++) { uid->prefs[n].type = PREFTYPE_HASH; uid->prefs[n].value = *hash++; } for (; nzip; nzip--, n++) { uid->prefs[n].type = PREFTYPE_ZIP; uid->prefs[n].value = *zip++; } uid->prefs[n].type = PREFTYPE_NONE; /* End of list marker */ uid->prefs[n].value = 0; } /* See whether we have the MDC feature. */ uid->flags.mdc = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); if (p && n && (p[0] & 0x01)) uid->flags.mdc = 1; /* See whether we have the AEAD feature. */ uid->flags.aead = 0; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n); if (p && n && (p[0] & 0x02)) uid->flags.aead = 1; /* And the keyserver modify flag. */ uid->flags.ks_modify = 1; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n); if (p && n && (p[0] & 0x80)) uid->flags.ks_modify = 0; } static void sig_to_revoke_info (PKT_signature * sig, struct revoke_info *rinfo) { rinfo->date = sig->timestamp; rinfo->algo = sig->pubkey_algo; rinfo->keyid[0] = sig->keyid[0]; rinfo->keyid[1] = sig->keyid[1]; } /* Given a keyblock, parse the key block and extract various pieces of * information and save them with the primary key packet and the user * id packets. For instance, some information is stored in signature * packets. We find the latest such valid packet (since the user can * change that information) and copy its contents into the * PKT_public_key. * * Note that R_REVOKED may be set to 0, 1 or 2. * * This function fills in the following fields in the primary key's * keyblock: * * main_keyid (computed) * revkey / numrevkeys (derived from self signed key data) * flags.valid (whether we have at least 1 self-sig) * flags.maybe_revoked (whether a designed revoked the key, but * we are missing the key to check the sig) * selfsigversion (highest version of any valid self-sig) * pubkey_usage (derived from most recent self-sig or most * recent user id) * has_expired (various sources) * expiredate (various sources) * * See the documentation for fixup_uidnode for how the user id packets * are modified. In addition to that the primary user id's is_primary * field is set to 1 and the other user id's is_primary are set to 0. */ static void merge_selfsigs_main (ctrl_t ctrl, kbnode_t keyblock, int *r_revoked, struct revoke_info *rinfo) { PKT_public_key *pk = NULL; KBNODE k; u32 kid[2]; u32 sigdate, uiddate, uiddate2; KBNODE signode, uidnode, uidnode2; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; int key_expire_seen = 0; byte sigversion = 0; *r_revoked = 0; memset (rinfo, 0, sizeof (*rinfo)); /* Section 11.1 of RFC 4880 determines the order of packets within a * message. There are three sections, which must occur in the * following order: the public key, the user ids and user attributes * and the subkeys. Within each section, each primary packet (e.g., * a user id packet) is followed by one or more signature packets, * which modify that packet. */ /* According to Section 11.1 of RFC 4880, the public key must be the first packet. Note that parse_keyblock_image ensures that the first packet is the public key. */ if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) BUG (); pk = keyblock->pkt->pkt.public_key; keytimestamp = pk->timestamp; keyid_from_pk (pk, kid); pk->main_keyid[0] = kid[0]; pk->main_keyid[1] = kid[1]; if (pk->version < 4) { /* Before v4 the key packet itself contains the expiration date * and there was no way to change it, so we start with the one * from the key packet. */ key_expire = pk->max_expiredate; key_expire_seen = 1; } /* First pass: * * - Find the latest direct key self-signature. We assume that the * newest one overrides all others. * * - Determine whether the key has been revoked. * * - Gather all revocation keys (unlike other data, we don't just * take them from the latest self-signed packet). * * - Determine max (sig[...]->version). */ /* Reset this in case this key was already merged. */ xfree (pk->revkey); pk->revkey = NULL; pk->numrevkeys = 0; signode = NULL; sigdate = 0; /* Helper variable to find the latest signature. */ /* According to Section 11.1 of RFC 4880, the public key comes first * and is immediately followed by any signature packets that modify * it. */ for (k = keyblock; k && k->pkt->pkttype != PKT_USER_ID && k->pkt->pkttype != PKT_ATTRIBUTE && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1]) { /* Self sig. */ if (check_key_signature (ctrl, keyblock, k, NULL)) ; /* Signature did not verify. */ else if (IS_KEY_REV (sig)) { /* Key has been revoked - there is no way to * override such a revocation, so we theoretically * can stop now. We should not cope with expiration * times for revocations here because we have to * assume that an attacker can generate all kinds of * signatures. However due to the fact that the key * has been revoked it does not harm either and by * continuing we gather some more info on that * key. */ *r_revoked = 1; sig_to_revoke_info (sig, rinfo); } else if (IS_KEY_SIG (sig)) { /* Add the indicated revocations keys from all * signatures not just the latest. We do this * because you need multiple 1F sigs to properly * handle revocation keys (PGP does it this way, and * a revocation key could be sensitive and hence in * a different signature). */ if (sig->revkey) { int i; pk->revkey = xrealloc (pk->revkey, sizeof (struct revocation_key) * (pk->numrevkeys + sig->numrevkeys)); for (i = 0; i < sig->numrevkeys; i++, pk->numrevkeys++) { pk->revkey[pk->numrevkeys].class = sig->revkey[i].class; pk->revkey[pk->numrevkeys].algid = sig->revkey[i].algid; pk->revkey[pk->numrevkeys].fprlen = sig->revkey[i].fprlen; memcpy (pk->revkey[pk->numrevkeys].fpr, sig->revkey[i].fpr, sig->revkey[i].fprlen); memset (pk->revkey[pk->numrevkeys].fpr + sig->revkey[i].fprlen, 0, sizeof (sig->revkey[i].fpr) - sig->revkey[i].fprlen); } } if (sig->timestamp >= sigdate) { /* This is the latest signature so far. */ if (sig->flags.expired) ; /* Signature has expired - ignore it. */ else { sigdate = sig->timestamp; signode = k; if (sig->version > sigversion) sigversion = sig->version; } } } } } } /* Remove dupes from the revocation keys. */ if (pk->revkey) { int i, j, x, changed = 0; for (i = 0; i < pk->numrevkeys; i++) { for (j = i + 1; j < pk->numrevkeys; j++) { if (memcmp (&pk->revkey[i], &pk->revkey[j], sizeof (struct revocation_key)) == 0) { /* remove j */ for (x = j; x < pk->numrevkeys - 1; x++) pk->revkey[x] = pk->revkey[x + 1]; pk->numrevkeys--; j--; changed = 1; } } } if (changed) pk->revkey = xrealloc (pk->revkey, pk->numrevkeys * sizeof (struct revocation_key)); } /* SIGNODE is the 1F signature packet with the latest creation time. * Extract some information from it. */ if (signode) { /* Some information from a direct key signature take precedence * over the same information given in UID sigs. */ PKT_signature *sig = signode->pkt->pkt.signature; const byte *p; key_usage = parse_key_usage (sig); p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (p && buf32_to_u32 (p)) { key_expire = keytimestamp + buf32_to_u32 (p); key_expire_seen = 1; } /* Mark that key as valid: One direct key signature should * render a key as valid. */ pk->flags.valid = 1; } /* Pass 1.5: Look for key revocation signatures that were not made * by the key (i.e. did a revocation key issue a revocation for * us?). Only bother to do this if there is a revocation key in the * first place and we're not revoked already. */ if (!*r_revoked && pk->revkey) for (k = keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = k->pkt->pkt.signature; if (IS_KEY_REV (sig) && (sig->keyid[0] != kid[0] || sig->keyid[1] != kid[1])) { int rc = check_revocation_keys (ctrl, pk, sig); if (rc == 0) { *r_revoked = 2; sig_to_revoke_info (sig, rinfo); /* Don't continue checking since we can't be any * more revoked than this. */ break; } else if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY) pk->flags.maybe_revoked = 1; /* A failure here means the sig did not verify, was * not issued by a revocation key, or a revocation * key loop was broken. If a revocation key isn't * findable, however, the key might be revoked and * we don't know it. */ /* Fixme: In the future handle subkey and cert * revocations? PGP doesn't, but it's in 2440. */ } } } /* Second pass: Look at the self-signature of all user IDs. */ /* According to RFC 4880 section 11.1, user id and attribute packets * are in the second section, after the public key packet and before * the subkey packets. */ signode = uidnode = NULL; sigdate = 0; /* Helper variable to find the latest signature in one UID. */ for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID || k->pkt->pkttype == PKT_ATTRIBUTE) { /* New user id packet. */ /* Apply the data from the most recent self-signed packet to * the preceding user id packet. */ if (uidnode && signode) { fixup_uidnode (uidnode, signode, keytimestamp); pk->flags.valid = 1; } /* Clear SIGNODE. The only relevant self-signed data for * UIDNODE follows it. */ if (k->pkt->pkttype == PKT_USER_ID) uidnode = k; else uidnode = NULL; signode = NULL; sigdate = 0; } else if (k->pkt->pkttype == PKT_SIGNATURE && uidnode) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1]) { if (check_key_signature (ctrl, keyblock, k, NULL)) ; /* signature did not verify */ else if ((IS_UID_SIG (sig) || IS_UID_REV (sig)) && sig->timestamp >= sigdate) { /* Note: we allow invalidation of cert revocations * by a newer signature. An attacker can't use this * because a key should be revoked with a key revocation. * The reason why we have to allow for that is that at * one time an email address may become invalid but later * the same email address may become valid again (hired, * fired, hired again). */ sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig = 0; if (sig->version > sigversion) sigversion = sig->version; } } } } if (uidnode && signode) { fixup_uidnode (uidnode, signode, keytimestamp); pk->flags.valid = 1; } /* If the key isn't valid yet, and we have * --allow-non-selfsigned-uid set, then force it valid. */ if (!pk->flags.valid && opt.allow_non_selfsigned_uid) { if (opt.verbose) log_info (_("Invalid key %s made valid by" " --allow-non-selfsigned-uid\n"), keystr_from_pk (pk)); pk->flags.valid = 1; } /* The key STILL isn't valid, so try and find an ultimately * trusted signature. */ if (!pk->flags.valid) { uidnode = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) uidnode = k; else if (k->pkt->pkttype == PKT_SIGNATURE && uidnode) { PKT_signature *sig = k->pkt->pkt.signature; if (sig->keyid[0] != kid[0] || sig->keyid[1] != kid[1]) { PKT_public_key *ultimate_pk; ultimate_pk = xmalloc_clear (sizeof (*ultimate_pk)); /* We don't want to use the full get_pubkey to avoid * infinite recursion in certain cases. There is no * reason to check that an ultimately trusted key is * still valid - if it has been revoked the user * should also remove the ultimate trust flag. */ - if (get_pubkey_fast (ultimate_pk, sig->keyid) == 0 + if (get_pubkey_fast (ctrl, ultimate_pk, sig->keyid) == 0 && check_key_signature2 (ctrl, keyblock, k, ultimate_pk, NULL, NULL, NULL, NULL) == 0 && get_ownertrust (ctrl, ultimate_pk) == TRUST_ULTIMATE) { free_public_key (ultimate_pk); pk->flags.valid = 1; break; } free_public_key (ultimate_pk); } } } } /* Record the highest selfsig version so we know if this is a v3 key * through and through, or a v3 key with a v4 selfsig somewhere. * This is useful in a few places to know if the key must be treated * as PGP2-style or OpenPGP-style. Note that a selfsig revocation * with a higher version number will also raise this value. This is * okay since such a revocation must be issued by the user (i.e. it * cannot be issued by someone else to modify the key behavior.) */ pk->selfsigversion = sigversion; /* Now that we had a look at all user IDs we can now get some * information from those user IDs. */ if (!key_usage) { /* Find the latest user ID with key flags set. */ uiddate = 0; /* Helper to find the latest user ID. */ for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->help_key_usage && uid->created > uiddate) { key_usage = uid->help_key_usage; uiddate = uid->created; } } } } if (!key_usage) { /* No key flags at all: get it from the algo. */ key_usage = openpgp_pk_algo_usage (pk->pubkey_algo); } else { /* Check that the usage matches the usage as given by the algo. */ int x = openpgp_pk_algo_usage (pk->pubkey_algo); if (x) /* Mask it down to the actual allowed usage. */ key_usage &= x; } /* Whatever happens, it's a primary key, so it can certify. */ pk->pubkey_usage = key_usage | PUBKEY_USAGE_CERT; if (!key_expire_seen) { /* Find the latest valid user ID with a key expiration set * Note, that this may be a different one from the above because * some user IDs may have no expiration date set. */ uiddate = 0; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->help_key_expire && uid->created > uiddate) { key_expire = uid->help_key_expire; uiddate = uid->created; } } } } /* Currently only v3 keys have a maximum expiration date, but I'll * bet v5 keys get this feature again. */ if (key_expire == 0 || (pk->max_expiredate && key_expire > pk->max_expiredate)) key_expire = pk->max_expiredate; pk->has_expired = key_expire >= curtime ? 0 : key_expire; pk->expiredate = key_expire; /* Fixme: we should see how to get rid of the expiretime fields but * this needs changes at other places too. */ /* And now find the real primary user ID and delete all others. */ uiddate = uiddate2 = 0; uidnode = uidnode2 = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if (uid->flags.primary) { if (uid->created > uiddate) { uiddate = uid->created; uidnode = k; } else if (uid->created == uiddate && uidnode) { /* The dates are equal, so we need to do a different * (and arbitrary) comparison. This should rarely, * if ever, happen. It's good to try and guarantee * that two different GnuPG users with two different * keyrings at least pick the same primary. */ if (cmp_user_ids (uid, uidnode->pkt->pkt.user_id) > 0) uidnode = k; } } else { if (uid->created > uiddate2) { uiddate2 = uid->created; uidnode2 = k; } else if (uid->created == uiddate2 && uidnode2) { if (cmp_user_ids (uid, uidnode2->pkt->pkt.user_id) > 0) uidnode2 = k; } } } } if (uidnode) { for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { PKT_user_id *uid = k->pkt->pkt.user_id; if (k != uidnode) uid->flags.primary = 0; } } } else if (uidnode2) { /* None is flagged primary - use the latest user ID we have, * and disambiguate with the arbitrary packet comparison. */ uidnode2->pkt->pkt.user_id->flags.primary = 1; } else { /* None of our uids were self-signed, so pick the one that * sorts first to be the primary. This is the best we can do * here since there are no self sigs to date the uids. */ uidnode = NULL; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data) { if (!uidnode) { uidnode = k; uidnode->pkt->pkt.user_id->flags.primary = 1; continue; } else { if (cmp_user_ids (k->pkt->pkt.user_id, uidnode->pkt->pkt.user_id) > 0) { uidnode->pkt->pkt.user_id->flags.primary = 0; uidnode = k; uidnode->pkt->pkt.user_id->flags.primary = 1; } else { /* just to be safe: */ k->pkt->pkt.user_id->flags.primary = 0; } } } } } } /* Convert a buffer to a signature. Useful for 0x19 embedded sigs. * Caller must free the signature when they are done. */ static PKT_signature * buf_to_sig (const byte * buf, size_t len) { PKT_signature *sig = xmalloc_clear (sizeof (PKT_signature)); IOBUF iobuf = iobuf_temp_with_content (buf, len); int save_mode = set_packet_list_mode (0); if (parse_signature (iobuf, PKT_SIGNATURE, len, sig) != 0) { free_seckey_enc (sig); sig = NULL; } set_packet_list_mode (save_mode); iobuf_close (iobuf); return sig; } /* Use the self-signed data to fill in various fields in subkeys. * * KEYBLOCK is the whole keyblock. SUBNODE is the subkey to fill in. * * Sets the following fields on the subkey: * * main_keyid * flags.valid if the subkey has a valid self-sig binding * flags.revoked * flags.backsig * pubkey_usage * has_expired * expired_date * * On this subkey's most revent valid self-signed packet, the * following field is set: * * flags.chosen_selfsig */ static void merge_selfsigs_subkey (ctrl_t ctrl, kbnode_t keyblock, kbnode_t subnode) { PKT_public_key *mainpk = NULL, *subpk = NULL; PKT_signature *sig; KBNODE k; u32 mainkid[2]; u32 sigdate = 0; KBNODE signode; u32 curtime = make_timestamp (); unsigned int key_usage = 0; u32 keytimestamp = 0; u32 key_expire = 0; const byte *p; if (subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY) BUG (); mainpk = keyblock->pkt->pkt.public_key; if (mainpk->version < 4) return;/* (actually this should never happen) */ keyid_from_pk (mainpk, mainkid); subpk = subnode->pkt->pkt.public_key; keytimestamp = subpk->timestamp; subpk->flags.valid = 0; subpk->flags.exact = 0; subpk->main_keyid[0] = mainpk->main_keyid[0]; subpk->main_keyid[1] = mainpk->main_keyid[1]; /* Find the latest key binding self-signature. */ signode = NULL; sigdate = 0; /* Helper to find the latest signature. */ for (k = subnode->next; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_SIGNATURE) { sig = k->pkt->pkt.signature; if (sig->keyid[0] == mainkid[0] && sig->keyid[1] == mainkid[1]) { if (check_key_signature (ctrl, keyblock, k, NULL)) ; /* Signature did not verify. */ else if (IS_SUBKEY_REV (sig)) { /* Note that this means that the date on a * revocation sig does not matter - even if the * binding sig is dated after the revocation sig, * the subkey is still marked as revoked. This * seems ok, as it is just as easy to make new * subkeys rather than re-sign old ones as the * problem is in the distribution. Plus, PGP (7) * does this the same way. */ subpk->flags.revoked = 1; sig_to_revoke_info (sig, &subpk->revoked); /* Although we could stop now, we continue to * figure out other information like the old expiration * time. */ } else if (IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate) { if (sig->flags.expired) ; /* Signature has expired - ignore it. */ else { sigdate = sig->timestamp; signode = k; signode->pkt->pkt.signature->flags.chosen_selfsig = 0; } } } } } /* No valid key binding. */ if (!signode) return; sig = signode->pkt->pkt.signature; sig->flags.chosen_selfsig = 1; /* So we know which selfsig we chose later. */ key_usage = parse_key_usage (sig); if (!key_usage) { /* No key flags at all: get it from the algo. */ key_usage = openpgp_pk_algo_usage (subpk->pubkey_algo); } else { /* Check that the usage matches the usage as given by the algo. */ int x = openpgp_pk_algo_usage (subpk->pubkey_algo); if (x) /* Mask it down to the actual allowed usage. */ key_usage &= x; } subpk->pubkey_usage = key_usage; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (p && buf32_to_u32 (p)) key_expire = keytimestamp + buf32_to_u32 (p); else key_expire = 0; subpk->has_expired = key_expire >= curtime ? 0 : key_expire; subpk->expiredate = key_expire; /* Algo doesn't exist. */ if (openpgp_pk_test_algo (subpk->pubkey_algo)) return; subpk->flags.valid = 1; /* Find the most recent 0x19 embedded signature on our self-sig. */ if (!subpk->flags.backsig) { int seq = 0; size_t n; PKT_signature *backsig = NULL; sigdate = 0; /* We do this while() since there may be other embedded * signatures in the future. We only want 0x19 here. */ while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_SIGNATURE, &n, &seq, NULL))) if (n > 3 && ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19))) { PKT_signature *tempsig = buf_to_sig (p, n); if (tempsig) { if (tempsig->timestamp > sigdate) { if (backsig) free_seckey_enc (backsig); backsig = tempsig; sigdate = backsig->timestamp; } else free_seckey_enc (tempsig); } } seq = 0; /* It is safe to have this in the unhashed area since the 0x19 * is located on the selfsig for convenience, not security. */ while ((p = enum_sig_subpkt (sig->unhashed, SIGSUBPKT_SIGNATURE, &n, &seq, NULL))) if (n > 3 && ((p[0] == 3 && p[2] == 0x19) || (p[0] == 4 && p[1] == 0x19))) { PKT_signature *tempsig = buf_to_sig (p, n); if (tempsig) { if (tempsig->timestamp > sigdate) { if (backsig) free_seckey_enc (backsig); backsig = tempsig; sigdate = backsig->timestamp; } else free_seckey_enc (tempsig); } } if (backsig) { /* At this point, backsig contains the most recent 0x19 sig. * Let's see if it is good. */ /* 2==valid, 1==invalid, 0==didn't check */ if (check_backsig (mainpk, subpk, backsig) == 0) subpk->flags.backsig = 2; else subpk->flags.backsig = 1; free_seckey_enc (backsig); } } } /* Merge information from the self-signatures with the public key, * subkeys and user ids to make using them more easy. * * See documentation for merge_selfsigs_main, merge_selfsigs_subkey * and fixup_uidnode for exactly which fields are updated. */ static void merge_selfsigs (ctrl_t ctrl, kbnode_t keyblock) { KBNODE k; int revoked; struct revoke_info rinfo; PKT_public_key *main_pk; prefitem_t *prefs; unsigned int mdc_feature; unsigned int aead_feature; if (keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { if (keyblock->pkt->pkttype == PKT_SECRET_KEY) { log_error ("expected public key but found secret key " "- must stop\n"); /* We better exit here because a public key is expected at * other places too. FIXME: Figure this out earlier and * don't get to here at all */ g10_exit (1); } BUG (); } merge_selfsigs_main (ctrl, keyblock, &revoked, &rinfo); /* Now merge in the data from each of the subkeys. */ for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { merge_selfsigs_subkey (ctrl, keyblock, k); } } main_pk = keyblock->pkt->pkt.public_key; if (revoked || main_pk->has_expired || !main_pk->flags.valid) { /* If the primary key is revoked, expired, or invalid we * better set the appropriate flags on that key and all * subkeys. */ for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = k->pkt->pkt.public_key; if (!main_pk->flags.valid) pk->flags.valid = 0; if (revoked && !pk->flags.revoked) { pk->flags.revoked = revoked; memcpy (&pk->revoked, &rinfo, sizeof (rinfo)); } if (main_pk->has_expired) { pk->has_expired = main_pk->has_expired; if (!pk->expiredate || pk->expiredate > main_pk->expiredate) pk->expiredate = main_pk->expiredate; } } } return; } /* Set the preference list of all keys to those of the primary real * user ID. Note: we use these preferences when we don't know by * which user ID the key has been selected. * fixme: we should keep atoms of commonly used preferences or * use reference counting to optimize the preference lists storage. * FIXME: it might be better to use the intersection of * all preferences. * Do a similar thing for the MDC feature flag. */ prefs = NULL; mdc_feature = aead_feature = 0; for (k = keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) { if (k->pkt->pkttype == PKT_USER_ID && !k->pkt->pkt.user_id->attrib_data && k->pkt->pkt.user_id->flags.primary) { prefs = k->pkt->pkt.user_id->prefs; mdc_feature = k->pkt->pkt.user_id->flags.mdc; aead_feature = k->pkt->pkt.user_id->flags.aead; break; } } for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk = k->pkt->pkt.public_key; if (pk->prefs) xfree (pk->prefs); pk->prefs = copy_prefs (prefs); pk->flags.mdc = mdc_feature; pk->flags.aead = aead_feature; } } } /* See whether the key satisfies any additional requirements specified * in CTX. If so, return the node of an appropriate key or subkey. * Otherwise, return NULL if there was no appropriate key. * * Note that we do not return a reference, i.e. the result must not be * freed using 'release_kbnode'. * * In case the primary key is not required, select a suitable subkey. * We need the primary key if PUBKEY_USAGE_CERT is set in REQ_USAGE or * we are in PGP7 mode and PUBKEY_USAGE_SIG is set in * REQ_USAGE. * * If any of PUBKEY_USAGE_SIG, PUBKEY_USAGE_ENC and PUBKEY_USAGE_CERT * are set in REQ_USAGE, we filter by the key's function. Concretely, * if PUBKEY_USAGE_SIG and PUBKEY_USAGE_CERT are set, then we only * return a key if it is (at least) either a signing or a * certification key. * * If REQ_USAGE is set, then we reject any keys that are not good * (i.e., valid, not revoked, not expired, etc.). This allows the * getkey functions to be used for plain key listings. * * Sets the matched key's user id field (pk->user_id) to the user id * that matched the low-level search criteria or NULL. * * If R_FLAGS is not NULL set certain flags for more detailed error * reporting. Used flags are: * * - LOOKUP_ALL_SUBKEYS_EXPIRED :: All Subkeys are expired or have * been revoked. * - LOOKUP_NOT_SELECTED :: No suitable key found * * This function needs to handle several different cases: * * 1. No requested usage and no primary key requested * Examples for this case are that we have a keyID to be used * for decryption or verification. * 2. No usage but primary key requested * This is the case for all functions which work on an * entire keyblock, e.g. for editing or listing * 3. Usage and primary key requested * FIXME * 4. Usage but no primary key requested * FIXME * */ static kbnode_t finish_lookup (kbnode_t keyblock, unsigned int req_usage, int want_exact, int want_secret, unsigned int *r_flags) { kbnode_t k; /* If WANT_EXACT is set, the key or subkey that actually matched the low-level search criteria. */ kbnode_t foundk = NULL; /* The user id (if any) that matched the low-level search criteria. */ PKT_user_id *foundu = NULL; u32 latest_date; kbnode_t latest_key; PKT_public_key *pk; int req_prim; u32 curtime = make_timestamp (); if (r_flags) *r_flags = 0; #define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT) req_usage &= USAGE_MASK; /* Request the primary if we're certifying another key, and also if * signing data while --pgp7 is on since pgp 7 do * not understand signatures made by a signing subkey. PGP 8 does. */ req_prim = ((req_usage & PUBKEY_USAGE_CERT) || (PGP7 && (req_usage & PUBKEY_USAGE_SIG))); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* For an exact match mark the primary or subkey that matched the low-level search criteria. */ if (want_exact) { for (k = keyblock; k; k = k->next) { if ((k->flag & 1)) { log_assert (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY); foundk = k; pk = k->pkt->pkt.public_key; pk->flags.exact = 1; break; } } } /* Get the user id that matched that low-level search criteria. */ for (k = keyblock; k; k = k->next) { if ((k->flag & 2)) { log_assert (k->pkt->pkttype == PKT_USER_ID); foundu = k->pkt->pkt.user_id; break; } } if (DBG_LOOKUP) log_debug ("finish_lookup: checking key %08lX (%s)(req_usage=%x)\n", (ulong) keyid_from_pk (keyblock->pkt->pkt.public_key, NULL), foundk ? "one" : "all", req_usage); if (!req_usage) { latest_key = foundk ? foundk : keyblock; goto found; } latest_date = 0; latest_key = NULL; /* Set LATEST_KEY to the latest (the one with the most recent * timestamp) good (valid, not revoked, not expired, etc.) subkey. * * Don't bother if we are only looking for a primary key or we need * an exact match and the exact match is not a subkey. */ if (req_prim || (foundk && foundk->pkt->pkttype != PKT_PUBLIC_SUBKEY)) ; else { kbnode_t nextk; int n_subkeys = 0; int n_revoked_or_expired = 0; /* Either start a loop or check just this one subkey. */ for (k = foundk ? foundk : keyblock; k; k = nextk) { if (foundk) { /* If FOUNDK is not NULL, then only consider that exact key, i.e., don't iterate. */ nextk = NULL; } else nextk = k->next; if (k->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = k->pkt->pkt.public_key; if (DBG_LOOKUP) log_debug ("\tchecking subkey %08lX\n", (ulong) keyid_from_pk (pk, NULL)); if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tsubkey not valid\n"); continue; } if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) { if (DBG_LOOKUP) log_debug ("\tusage does not match: want=%x have=%x\n", req_usage, pk->pubkey_usage); continue; } n_subkeys++; if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tsubkey has been revoked\n"); n_revoked_or_expired++; continue; } if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tsubkey has expired\n"); n_revoked_or_expired++; continue; } if (pk->timestamp > curtime && !opt.ignore_valid_from) { if (DBG_LOOKUP) log_debug ("\tsubkey not yet valid\n"); continue; } if (want_secret && agent_probe_secret_key (NULL, pk)) { if (DBG_LOOKUP) log_debug ("\tno secret key\n"); continue; } if (DBG_LOOKUP) log_debug ("\tsubkey might be fine\n"); /* In case a key has a timestamp of 0 set, we make sure that it is used. A better change would be to compare ">=" but that might also change the selected keys and is as such a more intrusive change. */ if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date)) { latest_date = pk->timestamp; latest_key = k; } } if (n_subkeys == n_revoked_or_expired && r_flags) *r_flags |= LOOKUP_ALL_SUBKEYS_EXPIRED; } /* Check if the primary key is ok (valid, not revoke, not expire, * matches requested usage) if: * * - we didn't find an appropriate subkey and we're not doing an * exact search, * * - we're doing an exact match and the exact match was the * primary key, or, * * - we're just considering the primary key. */ if ((!latest_key && !want_exact) || foundk == keyblock || req_prim) { if (DBG_LOOKUP && !foundk && !req_prim) log_debug ("\tno suitable subkeys found - trying primary\n"); pk = keyblock->pkt->pkt.public_key; if (!pk->flags.valid) { if (DBG_LOOKUP) log_debug ("\tprimary key not valid\n"); } else if (!((pk->pubkey_usage & USAGE_MASK) & req_usage)) { if (DBG_LOOKUP) log_debug ("\tprimary key usage does not match: " "want=%x have=%x\n", req_usage, pk->pubkey_usage); } else if (pk->flags.revoked) { if (DBG_LOOKUP) log_debug ("\tprimary key has been revoked\n"); } else if (pk->has_expired) { if (DBG_LOOKUP) log_debug ("\tprimary key has expired\n"); } else /* Okay. */ { if (DBG_LOOKUP) log_debug ("\tprimary key may be used\n"); latest_key = keyblock; } } if (!latest_key) { if (DBG_LOOKUP) log_debug ("\tno suitable key found - giving up\n"); if (r_flags) *r_flags |= LOOKUP_NOT_SELECTED; return NULL; /* Not found. */ } found: if (DBG_LOOKUP) log_debug ("\tusing key %08lX\n", (ulong) keyid_from_pk (latest_key->pkt->pkt.public_key, NULL)); if (latest_key) { pk = latest_key->pkt->pkt.public_key; free_user_id (pk->user_id); pk->user_id = scopy_user_id (foundu); } if (latest_key != keyblock && opt.verbose) { char *tempkeystr = xstrdup (keystr_from_pk (latest_key->pkt->pkt.public_key)); log_info (_("using subkey %s instead of primary key %s\n"), tempkeystr, keystr_from_pk (keyblock->pkt->pkt.public_key)); xfree (tempkeystr); } cache_put_keyblock (keyblock); return latest_key ? latest_key : keyblock; /* Found. */ } /* Print a KEY_CONSIDERED status line. */ static void print_status_key_considered (kbnode_t keyblock, unsigned int flags) { char hexfpr[2*MAX_FINGERPRINT_LEN + 1]; kbnode_t node; char flagbuf[20]; if (!is_status_enabled ()) return; for (node=keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) break; if (!node) { log_error ("%s: keyblock w/o primary key\n", __func__); return; } hexfingerprint (node->pkt->pkt.public_key, hexfpr, sizeof hexfpr); snprintf (flagbuf, sizeof flagbuf, " %u", flags); write_status_strings (STATUS_KEY_CONSIDERED, hexfpr, flagbuf, NULL); } /* A high-level function to lookup keys. * * This function builds on top of the low-level keydb API. It first * searches the database using the description stored in CTX->ITEMS, * then it filters the results using CTX and, finally, if WANT_SECRET * is set, it ignores any keys for which no secret key is available. * * Unlike the low-level search functions, this function also merges * all of the self-signed data into the keys, subkeys and user id * packets (see the merge_selfsigs for details). * * On success the key's keyblock is stored at *RET_KEYBLOCK, and the * specific subkey is stored at *RET_FOUND_KEY. Note that we do not * return a reference in *RET_FOUND_KEY, i.e. the result must not be * freed using 'release_kbnode', and it is only valid until * *RET_KEYBLOCK is deallocated. Therefore, if RET_FOUND_KEY is not * NULL, then RET_KEYBLOCK must not be NULL. */ static int lookup (ctrl_t ctrl, getkey_ctx_t ctx, int want_secret, kbnode_t *ret_keyblock, kbnode_t *ret_found_key) { int rc; int no_suitable_key = 0; KBNODE keyblock = NULL; KBNODE found_key = NULL; unsigned int infoflags; log_assert (ret_found_key == NULL || ret_keyblock != NULL); if (ret_keyblock) *ret_keyblock = NULL; for (;;) { rc = keydb_search (ctx->kr_handle, ctx->items, ctx->nitems, NULL); if (rc) break; /* If we are iterating over the entire database, then we need to * change from KEYDB_SEARCH_MODE_FIRST, which does an implicit * reset, to KEYDB_SEARCH_MODE_NEXT, which gets the next record. */ if (ctx->nitems && ctx->items->mode == KEYDB_SEARCH_MODE_FIRST) ctx->items->mode = KEYDB_SEARCH_MODE_NEXT; rc = keydb_get_keyblock (ctx->kr_handle, &keyblock); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto skip; } if (want_secret) { rc = agent_probe_any_secret_key (NULL, keyblock); if (gpg_err_code(rc) == GPG_ERR_NO_SECKEY) goto skip; /* No secret key available. */ if (rc) goto found; /* Unexpected error. */ } /* Warning: node flag bits 0 and 1 should be preserved by * merge_selfsigs. */ merge_selfsigs (ctrl, keyblock); found_key = finish_lookup (keyblock, ctx->req_usage, ctx->exact, want_secret, &infoflags); print_status_key_considered (keyblock, infoflags); if (found_key) { no_suitable_key = 0; goto found; } else { no_suitable_key = 1; } skip: /* Release resources and continue search. */ release_kbnode (keyblock); keyblock = NULL; /* The keyblock cache ignores the current "file position". * Thus, if we request the next result and the cache matches * (and it will since it is what we just looked for), we'll get * the same entry back! We can avoid this infinite loop by * disabling the cache. */ keydb_disable_caching (ctx->kr_handle); } found: if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search failed: %s\n", gpg_strerror (rc)); if (!rc) { if (ret_keyblock) { *ret_keyblock = keyblock; /* Return the keyblock. */ keyblock = NULL; } } else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && no_suitable_key) rc = want_secret? GPG_ERR_UNUSABLE_SECKEY : GPG_ERR_UNUSABLE_PUBKEY; else if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = want_secret? GPG_ERR_NO_SECKEY : GPG_ERR_NO_PUBKEY; release_kbnode (keyblock); if (ret_found_key) { if (! rc) *ret_found_key = found_key; else *ret_found_key = NULL; } return rc; } gpg_error_t get_seckey_default_or_card (ctrl_t ctrl, PKT_public_key *pk, const byte *fpr_card, size_t fpr_len) { gpg_error_t err; strlist_t namelist = NULL; const char *def_secret_key = parse_def_secret_key (ctrl); if (def_secret_key) add_to_strlist (&namelist, def_secret_key); else if (fpr_card) return get_pubkey_byfprint (ctrl, pk, NULL, fpr_card, fpr_len); if (!fpr_card || (def_secret_key && def_secret_key[strlen (def_secret_key)-1] == '!')) err = key_byname (ctrl, NULL, namelist, pk, 1, 0, NULL, NULL); else { /* Default key is specified and card key is also available. */ kbnode_t k, keyblock = NULL; err = key_byname (ctrl, NULL, namelist, pk, 1, 0, &keyblock, NULL); if (!err) for (k = keyblock; k; k = k->next) { PKT_public_key *pk_candidate; char fpr[MAX_FINGERPRINT_LEN]; if (k->pkt->pkttype != PKT_PUBLIC_KEY &&k->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk_candidate = k->pkt->pkt.public_key; if (!pk_candidate->flags.valid) continue; if (!((pk_candidate->pubkey_usage & USAGE_MASK) & pk->req_usage)) continue; fingerprint_from_pk (pk_candidate, fpr, NULL); if (!memcmp (fpr_card, fpr, fpr_len)) { release_public_key_parts (pk); copy_public_key (pk, pk_candidate); break; } } release_kbnode (keyblock); } free_strlist (namelist); return err; } /********************************************* *********** User ID printing helpers ******* *********************************************/ /* Return a string with a printable representation of the user_id. * this string must be freed by xfree. If R_NOUID is not NULL it is * set to true if a user id was not found; otherwise to false. */ static char * get_user_id_string (ctrl_t ctrl, u32 * keyid, int mode) { char *name; unsigned int namelen; char *p; log_assert (mode != 2); name = cache_get_uid_bykid (keyid, &namelen); if (!name) { /* Get it so that the cache will be filled. */ if (!get_pubkey (ctrl, NULL, keyid)) name = cache_get_uid_bykid (keyid, &namelen); } if (name) { if (mode) p = xasprintf ("%08lX%08lX %.*s", (ulong) keyid[0], (ulong) keyid[1], namelen, name); else p = xasprintf ("%s %.*s", keystr (keyid), namelen, name); xfree (name); } else { if (mode) p = xasprintf ("%08lX%08lX [?]", (ulong) keyid[0], (ulong) keyid[1]); else p = xasprintf ("%s [?]", keystr (keyid)); } return p; } char * get_user_id_string_native (ctrl_t ctrl, u32 * keyid) { char *p = get_user_id_string (ctrl, keyid, 0); char *p2 = utf8_to_native (p, strlen (p), 0); xfree (p); return p2; } char * get_long_user_id_string (ctrl_t ctrl, u32 * keyid) { return get_user_id_string (ctrl, keyid, 1); } /* Please try to use get_user_byfpr instead of this one. */ char * get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid) { char *name; unsigned int namelen; if (r_nouid) *r_nouid = 0; name = cache_get_uid_bykid (keyid, &namelen); if (!name) { /* Get it so that the cache will be filled. */ if (!get_pubkey (ctrl, NULL, keyid)) name = cache_get_uid_bykid (keyid, &namelen); } if (!name) { name = xstrdup (user_id_not_found_utf8 ()); namelen = strlen (name); if (r_nouid) *r_nouid = 1; } if (rn && name) *rn = namelen; return name; } /* Please try to use get_user_id_byfpr_native instead of this one. */ char * get_user_id_native (ctrl_t ctrl, u32 *keyid) { size_t rn; char *p = get_user_id (ctrl, keyid, &rn, NULL); char *p2 = utf8_to_native (p, rn, 0); xfree (p); return p2; } /* Return the user id for a key designated by its fingerprint, FPR, which must be MAX_FINGERPRINT_LEN bytes in size. Note: the returned string, which must be freed using xfree, may not be NUL terminated. To determine the length of the string, you must use *RN. */ static char * get_user_id_byfpr (ctrl_t ctrl, const byte *fpr, size_t fprlen, size_t *rn) { char *name; name = cache_get_uid_byfpr (fpr, fprlen, rn); if (!name) { /* Get it so that the cache will be filled. */ if (!get_pubkey_byfprint (ctrl, NULL, NULL, fpr, fprlen)) name = cache_get_uid_byfpr (fpr, fprlen, rn); } if (!name) { name = xstrdup (user_id_not_found_utf8 ()); *rn = strlen (name); } return name; } /* Like get_user_id_byfpr, but convert the string to the native encoding. The returned string needs to be freed. Unlike get_user_id_byfpr, the returned string is NUL terminated. */ char * get_user_id_byfpr_native (ctrl_t ctrl, const byte *fpr, size_t fprlen) { size_t rn; char *p = get_user_id_byfpr (ctrl, fpr, fprlen, &rn); char *p2 = utf8_to_native (p, rn, 0); xfree (p); return p2; } /* Return the database handle used by this context. The context still owns the handle. */ KEYDB_HANDLE get_ctx_handle (GETKEY_CTX ctx) { return ctx->kr_handle; } static void free_akl (struct akl *akl) { if (! akl) return; if (akl->spec) free_keyserver_spec (akl->spec); xfree (akl); } void release_akl (void) { while (opt.auto_key_locate) { struct akl *akl2 = opt.auto_key_locate; opt.auto_key_locate = opt.auto_key_locate->next; free_akl (akl2); } } /* Returns false on error. */ int parse_auto_key_locate (const char *options_arg) { char *tok; char *options, *options_buf; options = options_buf = xstrdup (options_arg); while ((tok = optsep (&options))) { struct akl *akl, *check, *last = NULL; int dupe = 0; if (tok[0] == '\0') continue; akl = xmalloc_clear (sizeof (*akl)); if (ascii_strcasecmp (tok, "clear") == 0) { xfree (akl); free_akl (opt.auto_key_locate); opt.auto_key_locate = NULL; continue; } else if (ascii_strcasecmp (tok, "nodefault") == 0) akl->type = AKL_NODEFAULT; else if (ascii_strcasecmp (tok, "local") == 0) akl->type = AKL_LOCAL; else if (ascii_strcasecmp (tok, "ldap") == 0) akl->type = AKL_LDAP; else if (ascii_strcasecmp (tok, "keyserver") == 0) akl->type = AKL_KEYSERVER; else if (ascii_strcasecmp (tok, "cert") == 0) akl->type = AKL_CERT; else if (ascii_strcasecmp (tok, "pka") == 0) akl->type = AKL_PKA; else if (ascii_strcasecmp (tok, "dane") == 0) akl->type = AKL_DANE; else if (ascii_strcasecmp (tok, "wkd") == 0) akl->type = AKL_WKD; else if ((akl->spec = parse_keyserver_uri (tok, 1))) akl->type = AKL_SPEC; else { free_akl (akl); xfree (options_buf); return 0; } /* We must maintain the order the user gave us */ for (check = opt.auto_key_locate; check; last = check, check = check->next) { /* Check for duplicates */ if (check->type == akl->type && (akl->type != AKL_SPEC || (akl->type == AKL_SPEC && strcmp (check->spec->uri, akl->spec->uri) == 0))) { dupe = 1; free_akl (akl); break; } } if (!dupe) { if (last) last->next = akl; else opt.auto_key_locate = akl; } } xfree (options_buf); return 1; } /* The list of key origins. */ static struct { const char *name; int origin; } key_origin_list[] = { { "self", KEYORG_SELF }, { "file", KEYORG_FILE }, { "url", KEYORG_URL }, { "wkd", KEYORG_WKD }, { "dane", KEYORG_DANE }, { "ks-pref", KEYORG_KS_PREF }, { "ks", KEYORG_KS }, { "unknown", KEYORG_UNKNOWN } }; /* Parse the argument for --key-origin. Return false on error. */ int parse_key_origin (char *string) { int i; char *comma; comma = strchr (string, ','); if (comma) *comma = 0; if (!ascii_strcasecmp (string, "help")) { log_info (_("valid values for option '%s':\n"), "--key-origin"); for (i=0; i < DIM (key_origin_list); i++) log_info (" %s\n", key_origin_list[i].name); g10_exit (1); } for (i=0; i < DIM (key_origin_list); i++) if (!ascii_strcasecmp (string, key_origin_list[i].name)) { opt.key_origin = key_origin_list[i].origin; xfree (opt.key_origin_url); opt.key_origin_url = NULL; if (comma && comma[1]) { opt.key_origin_url = xstrdup (comma+1); trim_spaces (opt.key_origin_url); } return 1; } if (comma) *comma = ','; return 0; } /* Return a string or "?" for the key ORIGIN. */ const char * key_origin_string (int origin) { int i; for (i=0; i < DIM (key_origin_list); i++) if (key_origin_list[i].origin == origin) return key_origin_list[i].name; return "?"; } /* Returns true if a secret key is available for the public key with key id KEYID; returns false if not. This function ignores legacy keys. Note: this is just a fast check and does not tell us whether the secret key is valid; this check merely indicates whether there is some secret key with the specified key id. */ int -have_secret_key_with_kid (u32 *keyid) +have_secret_key_with_kid (ctrl_t ctrl, u32 *keyid) { gpg_error_t err; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; kbnode_t keyblock; kbnode_t node; int result = 0; - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) return 0; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0] = keyid[0]; desc.u.kid[1] = keyid[1]; while (!result) { err = keydb_search (kdbhd, &desc, 1, NULL); if (err) break; err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); break; } for (node = keyblock; node; node = node->next) { /* Bit 0 of the flags is set if the search found the key using that key or subkey. Note: a search will only ever match a single key or subkey. */ if ((node->flag & 1)) { log_assert (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY); if (!agent_probe_secret_key (NULL, node->pkt->pkt.public_key)) result = 1; /* Secret key available. */ else result = 0; break; } } release_kbnode (keyblock); } keydb_release (kdbhd); return result; } diff --git a/g10/gpg.c b/g10/gpg.c index 0bbe72394..145796cbc 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -1,5588 +1,5599 @@ /* gpg.c - The GnuPG utility (main for gpg) * Copyright (C) 1998-2011 Free Software Foundation, Inc. * Copyright (C) 1997-2017 Werner Koch * Copyright (C) 2015-2017 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #ifdef HAVE_STAT #include /* for stat() */ #endif #include #ifdef HAVE_W32_SYSTEM # ifdef HAVE_WINSOCK2_H # include # endif # include #endif #define INCLUDED_BY_MAIN_MODULE 1 #include "gpg.h" #include #include "../common/iobuf.h" #include "../common/util.h" #include "packet.h" #include "../common/membuf.h" #include "main.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "filter.h" #include "../common/ttyio.h" #include "../common/i18n.h" #include "../common/sysutils.h" #include "../common/status.h" #include "keyserver-internal.h" #include "exec.h" #include "../common/gc-opt-flags.h" #include "../common/asshelp.h" #include "call-dirmngr.h" #include "tofu.h" #include "objcache.h" #include "../common/init.h" #include "../common/mbox-util.h" #include "../common/shareddefs.h" #include "../common/compliance.h" #if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__) #define MY_O_BINARY O_BINARY #ifndef S_IRGRP # define S_IRGRP 0 # define S_IWGRP 0 #endif #else #define MY_O_BINARY 0 #endif #ifdef __MINGW32__ int _dowildcard = -1; #endif enum cmd_and_opt_values { aNull = 0, oArmor = 'a', aDetachedSign = 'b', aSym = 'c', aDecrypt = 'd', aEncr = 'e', oRecipientFile = 'f', oHiddenRecipientFile = 'F', oInteractive = 'i', aListKeys = 'k', oDryRun = 'n', oOutput = 'o', oQuiet = 'q', oRecipient = 'r', oHiddenRecipient = 'R', aSign = 's', oTextmodeShort= 't', oLocalUser = 'u', oVerbose = 'v', oCompress = 'z', oSetNotation = 'N', aListSecretKeys = 'K', oBatch = 500, oMaxOutput, oInputSizeHint, oChunkSize, oSigNotation, oCertNotation, oShowNotation, oNoShowNotation, oKnownNotation, aEncrFiles, aEncrSym, aDecryptFiles, aClearsign, aStore, aQuickKeygen, aFullKeygen, aKeygen, aSignEncr, aSignEncrSym, aSignSym, aSignKey, aLSignKey, aQuickSignKey, aQuickLSignKey, aQuickAddUid, aQuickAddKey, aQuickRevUid, aQuickSetExpire, aQuickSetPrimaryUid, aListConfig, aListGcryptConfig, aGPGConfList, aGPGConfTest, aListPackets, aEditKey, aDeleteKeys, aDeleteSecretKeys, aDeleteSecretAndPublicKeys, aImport, aFastImport, aVerify, aVerifyFiles, aListSigs, aSendKeys, aRecvKeys, aLocateKeys, aLocateExtKeys, aSearchKeys, aRefreshKeys, aFetchKeys, aShowKeys, aExport, aExportSecret, aExportSecretSub, aExportSshKey, aCheckKeys, aGenRevoke, aDesigRevoke, aPrimegen, aPrintMD, aPrintMDs, aCheckTrustDB, aUpdateTrustDB, aFixTrustDB, aListTrustDB, aListTrustPath, aExportOwnerTrust, aImportOwnerTrust, aDeArmor, aEnArmor, aGenRandom, aRebuildKeydbCaches, aCardStatus, aCardEdit, aChangePIN, aPasswd, aServer, aTOFUPolicy, oMimemode, oTextmode, oNoTextmode, oExpert, oNoExpert, oDefSigExpire, oAskSigExpire, oNoAskSigExpire, oDefCertExpire, oAskCertExpire, oNoAskCertExpire, oDefCertLevel, oMinCertLevel, oAskCertLevel, oNoAskCertLevel, oFingerprint, oWithFingerprint, oWithSubkeyFingerprint, oWithICAOSpelling, oWithKeygrip, oWithKeyScreening, oWithSecret, oWithWKDHash, oWithColons, oWithKeyData, oWithKeyOrigin, oWithTofuInfo, oWithSigList, oWithSigCheck, oAnswerYes, oAnswerNo, oKeyring, oPrimaryKeyring, oSecretKeyring, oShowKeyring, oDefaultKey, oDefRecipient, oDefRecipientSelf, oNoDefRecipient, oTrySecretKey, oOptions, oDebug, oDebugLevel, oDebugAll, oDebugIOLBF, oDebugSetIobufSize, oDebugAllowLargeChunks, oStatusFD, oStatusFile, oAttributeFD, oAttributeFile, oEmitVersion, oNoEmitVersion, oCompletesNeeded, oMarginalsNeeded, oMaxCertDepth, oLoadExtension, oCompliance, oGnuPG, oRFC2440, oRFC4880, oRFC4880bis, oOpenPGP, oPGP7, oPGP8, oDE_VS, oRFC2440Text, oNoRFC2440Text, oCipherAlgo, oAEADAlgo, oDigestAlgo, oCertDigestAlgo, oCompressAlgo, oCompressLevel, oBZ2CompressLevel, oBZ2DecompressLowmem, oPassphrase, oPassphraseFD, oPassphraseFile, oPassphraseRepeat, oPinentryMode, oCommandFD, oCommandFile, oQuickRandom, oNoVerbose, oTrustDBName, oNoSecmemWarn, oRequireSecmem, oNoRequireSecmem, oNoPermissionWarn, oNoArmor, oNoDefKeyring, oNoKeyring, oNoGreeting, oNoTTY, oNoOptions, oNoBatch, oHomedir, oSkipVerify, oSkipHiddenRecipients, oNoSkipHiddenRecipients, oAlwaysTrust, oTrustModel, oForceOwnertrust, oSetFilename, oForYourEyesOnly, oNoForYourEyesOnly, oSetPolicyURL, oSigPolicyURL, oCertPolicyURL, oShowPolicyURL, oNoShowPolicyURL, oSigKeyserverURL, oUseEmbeddedFilename, oNoUseEmbeddedFilename, oComment, oDefaultComment, oNoComments, oThrowKeyids, oNoThrowKeyids, oShowPhotos, oNoShowPhotos, oPhotoViewer, oForceAEAD, oS2KMode, oS2KDigest, oS2KCipher, oS2KCount, oDisplayCharset, oNotDashEscaped, oEscapeFrom, oNoEscapeFrom, oLockOnce, oLockMultiple, oLockNever, oKeyServer, oKeyServerOptions, oImportOptions, oImportFilter, oExportOptions, oExportFilter, oListOptions, oVerifyOptions, oTempDir, oExecPath, oEncryptTo, oHiddenEncryptTo, oNoEncryptTo, oEncryptToDefaultKey, oLoggerFD, oLoggerFile, oUtf8Strings, oNoUtf8Strings, oDisableCipherAlgo, oDisablePubkeyAlgo, oAllowNonSelfsignedUID, oNoAllowNonSelfsignedUID, oAllowFreeformUID, oNoAllowFreeformUID, oAllowSecretKeyImport, oEnableSpecialFilenames, oNoLiteral, oSetFilesize, oHonorHttpProxy, oFastListMode, oListOnly, oIgnoreTimeConflict, oIgnoreValidFrom, oIgnoreCrcError, oIgnoreMDCError, oShowSessionKey, oOverrideSessionKey, oOverrideSessionKeyFD, oNoRandomSeedFile, oAutoKeyRetrieve, oNoAutoKeyRetrieve, oUseAgent, oNoUseAgent, oGpgAgentInfo, + oUseKeyboxd, oMergeOnly, oTryAllSecrets, oTrustedKey, oNoExpensiveTrustChecks, oFixedListMode, oLegacyListMode, oNoSigCache, oAutoCheckTrustDB, oNoAutoCheckTrustDB, oPreservePermissions, oDefaultPreferenceList, oDefaultKeyserverURL, oPersonalCipherPreferences, oPersonalAEADPreferences, oPersonalDigestPreferences, oPersonalCompressPreferences, oAgentProgram, + oKeyboxdProgram, oDirmngrProgram, oDisableDirmngr, oDisplay, oTTYname, oTTYtype, oLCctype, oLCmessages, oXauthority, oGroup, oUnGroup, oNoGroups, oStrict, oNoStrict, oMangleDosFilenames, oNoMangleDosFilenames, oEnableProgressFilter, oMultifile, oKeyidFormat, oExitOnStatusWriteError, oLimitCardInsertTries, oReaderPort, octapiDriver, opcscDriver, oDisableCCID, oRequireCrossCert, oNoRequireCrossCert, oAutoKeyLocate, oNoAutoKeyLocate, oEnableLargeRSA, oDisableLargeRSA, oEnableDSA2, oDisableDSA2, oAllowWeakDigestAlgos, oFakedSystemTime, oNoAutostart, oPrintPKARecords, oPrintDANERecords, oTOFUDefaultPolicy, oTOFUDBFormat, oDefaultNewKeyAlgo, oWeakDigest, oUnwrap, oOnlySignTextIDs, oDisableSignerUID, oSender, oKeyOrigin, oRequestOrigin, oNoSymkeyCache, oNoop }; static ARGPARSE_OPTS opts[] = { ARGPARSE_group (300, N_("@Commands:\n ")), ARGPARSE_c (aSign, "sign", N_("make a signature")), ARGPARSE_c (aClearsign, "clear-sign", N_("make a clear text signature")), ARGPARSE_c (aClearsign, "clearsign", "@"), ARGPARSE_c (aDetachedSign, "detach-sign", N_("make a detached signature")), ARGPARSE_c (aEncr, "encrypt", N_("encrypt data")), ARGPARSE_c (aEncrFiles, "encrypt-files", "@"), ARGPARSE_c (aSym, "symmetric", N_("encryption only with symmetric cipher")), ARGPARSE_c (aStore, "store", "@"), ARGPARSE_c (aDecrypt, "decrypt", N_("decrypt data (default)")), ARGPARSE_c (aDecryptFiles, "decrypt-files", "@"), ARGPARSE_c (aVerify, "verify" , N_("verify a signature")), ARGPARSE_c (aVerifyFiles, "verify-files" , "@" ), ARGPARSE_c (aListKeys, "list-keys", N_("list keys")), ARGPARSE_c (aListKeys, "list-public-keys", "@" ), ARGPARSE_c (aListSigs, "list-signatures", N_("list keys and signatures")), ARGPARSE_c (aListSigs, "list-sigs", "@"), ARGPARSE_c (aCheckKeys, "check-signatures", N_("list and check key signatures")), ARGPARSE_c (aCheckKeys, "check-sigs", "@"), ARGPARSE_c (oFingerprint, "fingerprint", N_("list keys and fingerprints")), ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")), ARGPARSE_c (aKeygen, "generate-key", N_("generate a new key pair")), ARGPARSE_c (aKeygen, "gen-key", "@"), ARGPARSE_c (aQuickKeygen, "quick-generate-key" , N_("quickly generate a new key pair")), ARGPARSE_c (aQuickKeygen, "quick-gen-key", "@"), ARGPARSE_c (aQuickAddUid, "quick-add-uid", N_("quickly add a new user-id")), ARGPARSE_c (aQuickAddUid, "quick-adduid", "@"), ARGPARSE_c (aQuickAddKey, "quick-add-key", "@"), ARGPARSE_c (aQuickAddKey, "quick-addkey", "@"), ARGPARSE_c (aQuickRevUid, "quick-revoke-uid", N_("quickly revoke a user-id")), ARGPARSE_c (aQuickRevUid, "quick-revuid", "@"), ARGPARSE_c (aQuickSetExpire, "quick-set-expire", N_("quickly set a new expiration date")), ARGPARSE_c (aQuickSetPrimaryUid, "quick-set-primary-uid", "@"), ARGPARSE_c (aFullKeygen, "full-generate-key" , N_("full featured key pair generation")), ARGPARSE_c (aFullKeygen, "full-gen-key", "@"), ARGPARSE_c (aGenRevoke, "generate-revocation", N_("generate a revocation certificate")), ARGPARSE_c (aGenRevoke, "gen-revoke", "@"), ARGPARSE_c (aDeleteKeys,"delete-keys", N_("remove keys from the public keyring")), ARGPARSE_c (aDeleteSecretKeys, "delete-secret-keys", N_("remove keys from the secret keyring")), ARGPARSE_c (aQuickSignKey, "quick-sign-key" , N_("quickly sign a key")), ARGPARSE_c (aQuickLSignKey, "quick-lsign-key", N_("quickly sign a key locally")), ARGPARSE_c (aSignKey, "sign-key" ,N_("sign a key")), ARGPARSE_c (aLSignKey, "lsign-key" ,N_("sign a key locally")), ARGPARSE_c (aEditKey, "edit-key" ,N_("sign or edit a key")), ARGPARSE_c (aEditKey, "key-edit" ,"@"), ARGPARSE_c (aPasswd, "change-passphrase", N_("change a passphrase")), ARGPARSE_c (aPasswd, "passwd", "@"), ARGPARSE_c (aDesigRevoke, "generate-designated-revocation", "@"), ARGPARSE_c (aDesigRevoke, "desig-revoke","@" ), ARGPARSE_c (aExport, "export" , N_("export keys") ), ARGPARSE_c (aSendKeys, "send-keys" , N_("export keys to a keyserver") ), ARGPARSE_c (aRecvKeys, "receive-keys" , N_("import keys from a keyserver") ), ARGPARSE_c (aRecvKeys, "recv-keys" , "@"), ARGPARSE_c (aSearchKeys, "search-keys" , N_("search for keys on a keyserver") ), ARGPARSE_c (aRefreshKeys, "refresh-keys", N_("update all keys from a keyserver")), ARGPARSE_c (aLocateKeys, "locate-keys", "@"), ARGPARSE_c (aLocateExtKeys, "locate-external-keys", "@"), ARGPARSE_c (aFetchKeys, "fetch-keys" , "@" ), ARGPARSE_c (aShowKeys, "show-keys" , "@" ), ARGPARSE_c (aExportSecret, "export-secret-keys" , "@" ), ARGPARSE_c (aExportSecretSub, "export-secret-subkeys" , "@" ), ARGPARSE_c (aExportSshKey, "export-ssh-key", "@" ), ARGPARSE_c (aImport, "import", N_("import/merge keys")), ARGPARSE_c (aFastImport, "fast-import", "@"), #ifdef ENABLE_CARD_SUPPORT ARGPARSE_c (aCardStatus, "card-status", N_("print the card status")), ARGPARSE_c (aCardEdit, "edit-card", N_("change data on a card")), ARGPARSE_c (aCardEdit, "card-edit", "@"), ARGPARSE_c (aChangePIN, "change-pin", N_("change a card's PIN")), #endif ARGPARSE_c (aListConfig, "list-config", "@"), ARGPARSE_c (aListGcryptConfig, "list-gcrypt-config", "@"), ARGPARSE_c (aGPGConfList, "gpgconf-list", "@" ), ARGPARSE_c (aGPGConfTest, "gpgconf-test", "@" ), ARGPARSE_c (aListPackets, "list-packets","@"), #ifndef NO_TRUST_MODELS ARGPARSE_c (aExportOwnerTrust, "export-ownertrust", "@"), ARGPARSE_c (aImportOwnerTrust, "import-ownertrust", "@"), ARGPARSE_c (aUpdateTrustDB,"update-trustdb", N_("update the trust database")), ARGPARSE_c (aCheckTrustDB, "check-trustdb", "@"), ARGPARSE_c (aFixTrustDB, "fix-trustdb", "@"), #endif ARGPARSE_c (aDeArmor, "dearmor", "@"), ARGPARSE_c (aDeArmor, "dearmour", "@"), ARGPARSE_c (aEnArmor, "enarmor", "@"), ARGPARSE_c (aEnArmor, "enarmour", "@"), ARGPARSE_c (aPrintMD, "print-md", N_("print message digests")), ARGPARSE_c (aPrimegen, "gen-prime", "@" ), ARGPARSE_c (aGenRandom,"gen-random", "@" ), ARGPARSE_c (aServer, "server", N_("run in server mode")), ARGPARSE_c (aTOFUPolicy, "tofu-policy", N_("|VALUE|set the TOFU policy for a key")), ARGPARSE_group (301, N_("@\nOptions:\n ")), ARGPARSE_s_n (oArmor, "armor", N_("create ascii armored output")), ARGPARSE_s_n (oArmor, "armour", "@"), ARGPARSE_s_s (oRecipient, "recipient", N_("|USER-ID|encrypt for USER-ID")), ARGPARSE_s_s (oHiddenRecipient, "hidden-recipient", "@"), ARGPARSE_s_s (oRecipientFile, "recipient-file", "@"), ARGPARSE_s_s (oHiddenRecipientFile, "hidden-recipient-file", "@"), ARGPARSE_s_s (oRecipient, "remote-user", "@"), /* (old option name) */ ARGPARSE_s_s (oDefRecipient, "default-recipient", "@"), ARGPARSE_s_n (oDefRecipientSelf, "default-recipient-self", "@"), ARGPARSE_s_n (oNoDefRecipient, "no-default-recipient", "@"), ARGPARSE_s_s (oTempDir, "temp-directory", "@"), ARGPARSE_s_s (oExecPath, "exec-path", "@"), ARGPARSE_s_s (oEncryptTo, "encrypt-to", "@"), ARGPARSE_s_n (oNoEncryptTo, "no-encrypt-to", "@"), ARGPARSE_s_s (oHiddenEncryptTo, "hidden-encrypt-to", "@"), ARGPARSE_s_n (oEncryptToDefaultKey, "encrypt-to-default-key", "@"), ARGPARSE_s_s (oLocalUser, "local-user", N_("|USER-ID|use USER-ID to sign or decrypt")), ARGPARSE_s_s (oSender, "sender", "@"), ARGPARSE_s_s (oTrySecretKey, "try-secret-key", "@"), ARGPARSE_s_i (oCompress, NULL, N_("|N|set compress level to N (0 disables)")), ARGPARSE_s_i (oCompressLevel, "compress-level", "@"), ARGPARSE_s_i (oBZ2CompressLevel, "bzip2-compress-level", "@"), ARGPARSE_s_n (oBZ2DecompressLowmem, "bzip2-decompress-lowmem", "@"), ARGPARSE_s_n (oMimemode, "mimemode", "@"), ARGPARSE_s_n (oTextmodeShort, NULL, "@"), ARGPARSE_s_n (oTextmode, "textmode", N_("use canonical text mode")), ARGPARSE_s_n (oNoTextmode, "no-textmode", "@"), ARGPARSE_s_n (oExpert, "expert", "@"), ARGPARSE_s_n (oNoExpert, "no-expert", "@"), ARGPARSE_s_s (oDefSigExpire, "default-sig-expire", "@"), ARGPARSE_s_n (oAskSigExpire, "ask-sig-expire", "@"), ARGPARSE_s_n (oNoAskSigExpire, "no-ask-sig-expire", "@"), ARGPARSE_s_s (oDefCertExpire, "default-cert-expire", "@"), ARGPARSE_s_n (oAskCertExpire, "ask-cert-expire", "@"), ARGPARSE_s_n (oNoAskCertExpire, "no-ask-cert-expire", "@"), ARGPARSE_s_i (oDefCertLevel, "default-cert-level", "@"), ARGPARSE_s_i (oMinCertLevel, "min-cert-level", "@"), ARGPARSE_s_n (oAskCertLevel, "ask-cert-level", "@"), ARGPARSE_s_n (oNoAskCertLevel, "no-ask-cert-level", "@"), ARGPARSE_s_s (oOutput, "output", N_("|FILE|write output to FILE")), ARGPARSE_p_u (oMaxOutput, "max-output", "@"), ARGPARSE_s_s (oInputSizeHint, "input-size-hint", "@"), ARGPARSE_s_i (oChunkSize, "chunk-size", "@"), ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")), ARGPARSE_s_n (oQuiet, "quiet", "@"), ARGPARSE_s_n (oNoTTY, "no-tty", "@"), ARGPARSE_s_n (oForceAEAD, "force-aead", "@"), ARGPARSE_s_n (oDisableSignerUID, "disable-signer-uid", "@"), ARGPARSE_s_n (oDryRun, "dry-run", N_("do not make any changes")), ARGPARSE_s_n (oInteractive, "interactive", N_("prompt before overwriting")), ARGPARSE_s_n (oBatch, "batch", "@"), ARGPARSE_s_n (oAnswerYes, "yes", "@"), ARGPARSE_s_n (oAnswerNo, "no", "@"), ARGPARSE_s_s (oKeyring, "keyring", "@"), ARGPARSE_s_s (oPrimaryKeyring, "primary-keyring", "@"), ARGPARSE_s_s (oSecretKeyring, "secret-keyring", "@"), ARGPARSE_s_n (oShowKeyring, "show-keyring", "@"), ARGPARSE_s_s (oDefaultKey, "default-key", "@"), ARGPARSE_s_s (oKeyServer, "keyserver", "@"), ARGPARSE_s_s (oKeyServerOptions, "keyserver-options", "@"), ARGPARSE_s_s (oKeyOrigin, "key-origin", "@"), ARGPARSE_s_s (oImportOptions, "import-options", "@"), ARGPARSE_s_s (oImportFilter, "import-filter", "@"), ARGPARSE_s_s (oExportOptions, "export-options", "@"), ARGPARSE_s_s (oExportFilter, "export-filter", "@"), ARGPARSE_s_s (oListOptions, "list-options", "@"), ARGPARSE_s_s (oVerifyOptions, "verify-options", "@"), ARGPARSE_s_s (oDisplayCharset, "display-charset", "@"), ARGPARSE_s_s (oDisplayCharset, "charset", "@"), ARGPARSE_s_s (oOptions, "options", "@"), ARGPARSE_s_s (oDebug, "debug", "@"), ARGPARSE_s_s (oDebugLevel, "debug-level", "@"), ARGPARSE_s_n (oDebugAll, "debug-all", "@"), ARGPARSE_s_n (oDebugIOLBF, "debug-iolbf", "@"), ARGPARSE_s_u (oDebugSetIobufSize, "debug-set-iobuf-size", "@"), ARGPARSE_s_u (oDebugAllowLargeChunks, "debug-allow-large-chunks", "@"), ARGPARSE_s_i (oStatusFD, "status-fd", "@"), ARGPARSE_s_s (oStatusFile, "status-file", "@"), ARGPARSE_s_i (oAttributeFD, "attribute-fd", "@"), ARGPARSE_s_s (oAttributeFile, "attribute-file", "@"), ARGPARSE_s_i (oCompletesNeeded, "completes-needed", "@"), ARGPARSE_s_i (oMarginalsNeeded, "marginals-needed", "@"), ARGPARSE_s_i (oMaxCertDepth, "max-cert-depth", "@" ), ARGPARSE_s_s (oTrustedKey, "trusted-key", "@"), ARGPARSE_s_s (oLoadExtension, "load-extension", "@"), /* Dummy. */ ARGPARSE_s_s (oCompliance, "compliance", "@"), ARGPARSE_s_n (oGnuPG, "gnupg", "@"), ARGPARSE_s_n (oGnuPG, "no-pgp2", "@"), ARGPARSE_s_n (oGnuPG, "no-pgp6", "@"), ARGPARSE_s_n (oGnuPG, "no-pgp7", "@"), ARGPARSE_s_n (oGnuPG, "no-pgp8", "@"), ARGPARSE_s_n (oRFC2440, "rfc2440", "@"), ARGPARSE_s_n (oRFC4880, "rfc4880", "@"), ARGPARSE_s_n (oRFC4880bis, "rfc4880bis", "@"), ARGPARSE_s_n (oOpenPGP, "openpgp", N_("use strict OpenPGP behavior")), ARGPARSE_s_n (oPGP7, "pgp6", "@"), ARGPARSE_s_n (oPGP7, "pgp7", "@"), ARGPARSE_s_n (oPGP8, "pgp8", "@"), ARGPARSE_s_n (oRFC2440Text, "rfc2440-text", "@"), ARGPARSE_s_n (oNoRFC2440Text, "no-rfc2440-text", "@"), ARGPARSE_s_i (oS2KMode, "s2k-mode", "@"), ARGPARSE_s_s (oS2KDigest, "s2k-digest-algo", "@"), ARGPARSE_s_s (oS2KCipher, "s2k-cipher-algo", "@"), ARGPARSE_s_i (oS2KCount, "s2k-count", "@"), ARGPARSE_s_s (oCipherAlgo, "cipher-algo", "@"), ARGPARSE_s_s (oAEADAlgo, "aead-algo", "@"), ARGPARSE_s_s (oDigestAlgo, "digest-algo", "@"), ARGPARSE_s_s (oCertDigestAlgo, "cert-digest-algo", "@"), ARGPARSE_s_s (oCompressAlgo,"compress-algo", "@"), ARGPARSE_s_s (oCompressAlgo, "compression-algo", "@"), /* Alias */ ARGPARSE_s_n (oThrowKeyids, "throw-keyids", "@"), ARGPARSE_s_n (oNoThrowKeyids, "no-throw-keyids", "@"), ARGPARSE_s_n (oShowPhotos, "show-photos", "@"), ARGPARSE_s_n (oNoShowPhotos, "no-show-photos", "@"), ARGPARSE_s_s (oPhotoViewer, "photo-viewer", "@"), ARGPARSE_s_s (oSetNotation, "set-notation", "@"), ARGPARSE_s_s (oSigNotation, "sig-notation", "@"), ARGPARSE_s_s (oCertNotation, "cert-notation", "@"), ARGPARSE_s_s (oKnownNotation, "known-notation", "@"), ARGPARSE_group (302, N_( "@\n(See the man page for a complete listing of all commands and options)\n" )), ARGPARSE_group (303, N_("@\nExamples:\n\n" " -se -r Bob [file] sign and encrypt for user Bob\n" " --clear-sign [file] make a clear text signature\n" " --detach-sign [file] make a detached signature\n" " --list-keys [names] show keys\n" " --fingerprint [names] show fingerprints\n")), /* More hidden commands and options. */ ARGPARSE_c (aPrintMDs, "print-mds", "@"), /* old */ #ifndef NO_TRUST_MODELS ARGPARSE_c (aListTrustDB, "list-trustdb", "@"), #endif /* Not yet used: ARGPARSE_c (aListTrustPath, "list-trust-path", "@"), */ ARGPARSE_c (aDeleteSecretAndPublicKeys, "delete-secret-and-public-keys", "@"), ARGPARSE_c (aRebuildKeydbCaches, "rebuild-keydb-caches", "@"), ARGPARSE_s_s (oPassphrase, "passphrase", "@"), ARGPARSE_s_i (oPassphraseFD, "passphrase-fd", "@"), ARGPARSE_s_s (oPassphraseFile, "passphrase-file", "@"), ARGPARSE_s_i (oPassphraseRepeat,"passphrase-repeat", "@"), ARGPARSE_s_s (oPinentryMode, "pinentry-mode", "@"), ARGPARSE_s_s (oRequestOrigin, "request-origin", "@"), ARGPARSE_s_i (oCommandFD, "command-fd", "@"), ARGPARSE_s_s (oCommandFile, "command-file", "@"), ARGPARSE_s_n (oQuickRandom, "debug-quick-random", "@"), ARGPARSE_s_n (oNoVerbose, "no-verbose", "@"), #ifndef NO_TRUST_MODELS ARGPARSE_s_s (oTrustDBName, "trustdb-name", "@"), ARGPARSE_s_n (oAutoCheckTrustDB, "auto-check-trustdb", "@"), ARGPARSE_s_n (oNoAutoCheckTrustDB, "no-auto-check-trustdb", "@"), ARGPARSE_s_s (oForceOwnertrust, "force-ownertrust", "@"), #endif ARGPARSE_s_n (oNoSecmemWarn, "no-secmem-warning", "@"), ARGPARSE_s_n (oRequireSecmem, "require-secmem", "@"), ARGPARSE_s_n (oNoRequireSecmem, "no-require-secmem", "@"), ARGPARSE_s_n (oNoPermissionWarn, "no-permission-warning", "@"), ARGPARSE_s_n (oNoArmor, "no-armor", "@"), ARGPARSE_s_n (oNoArmor, "no-armour", "@"), ARGPARSE_s_n (oNoDefKeyring, "no-default-keyring", "@"), ARGPARSE_s_n (oNoKeyring, "no-keyring", "@"), ARGPARSE_s_n (oNoGreeting, "no-greeting", "@"), ARGPARSE_s_n (oNoOptions, "no-options", "@"), ARGPARSE_s_s (oHomedir, "homedir", "@"), ARGPARSE_s_n (oNoBatch, "no-batch", "@"), ARGPARSE_s_n (oWithColons, "with-colons", "@"), ARGPARSE_s_n (oWithTofuInfo,"with-tofu-info", "@"), ARGPARSE_s_n (oWithKeyData,"with-key-data", "@"), ARGPARSE_s_n (oWithSigList,"with-sig-list", "@"), ARGPARSE_s_n (oWithSigCheck,"with-sig-check", "@"), ARGPARSE_c (aListKeys, "list-key", "@"), /* alias */ ARGPARSE_c (aListSigs, "list-sig", "@"), /* alias */ ARGPARSE_c (aCheckKeys, "check-sig", "@"), /* alias */ ARGPARSE_c (aShowKeys, "show-key", "@"), /* alias */ ARGPARSE_s_n (oSkipVerify, "skip-verify", "@"), ARGPARSE_s_n (oSkipHiddenRecipients, "skip-hidden-recipients", "@"), ARGPARSE_s_n (oNoSkipHiddenRecipients, "no-skip-hidden-recipients", "@"), ARGPARSE_s_i (oDefCertLevel, "default-cert-check-level", "@"), /* old */ #ifndef NO_TRUST_MODELS ARGPARSE_s_n (oAlwaysTrust, "always-trust", "@"), #endif ARGPARSE_s_s (oTrustModel, "trust-model", "@"), ARGPARSE_s_s (oTOFUDefaultPolicy, "tofu-default-policy", "@"), ARGPARSE_s_s (oSetFilename, "set-filename", "@"), ARGPARSE_s_n (oForYourEyesOnly, "for-your-eyes-only", "@"), ARGPARSE_s_n (oNoForYourEyesOnly, "no-for-your-eyes-only", "@"), ARGPARSE_s_s (oSetPolicyURL, "set-policy-url", "@"), ARGPARSE_s_s (oSigPolicyURL, "sig-policy-url", "@"), ARGPARSE_s_s (oCertPolicyURL, "cert-policy-url", "@"), ARGPARSE_s_n (oShowPolicyURL, "show-policy-url", "@"), ARGPARSE_s_n (oNoShowPolicyURL, "no-show-policy-url", "@"), ARGPARSE_s_s (oSigKeyserverURL, "sig-keyserver-url", "@"), ARGPARSE_s_n (oShowNotation, "show-notation", "@"), ARGPARSE_s_n (oNoShowNotation, "no-show-notation", "@"), ARGPARSE_s_s (oComment, "comment", "@"), ARGPARSE_s_n (oDefaultComment, "default-comment", "@"), ARGPARSE_s_n (oNoComments, "no-comments", "@"), ARGPARSE_s_n (oEmitVersion, "emit-version", "@"), ARGPARSE_s_n (oNoEmitVersion, "no-emit-version", "@"), ARGPARSE_s_n (oNoEmitVersion, "no-version", "@"), /* alias */ ARGPARSE_s_n (oNotDashEscaped, "not-dash-escaped", "@"), ARGPARSE_s_n (oEscapeFrom, "escape-from-lines", "@"), ARGPARSE_s_n (oNoEscapeFrom, "no-escape-from-lines", "@"), ARGPARSE_s_n (oLockOnce, "lock-once", "@"), ARGPARSE_s_n (oLockMultiple, "lock-multiple", "@"), ARGPARSE_s_n (oLockNever, "lock-never", "@"), ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"), ARGPARSE_s_s (oLoggerFile, "log-file", "@"), ARGPARSE_s_s (oLoggerFile, "logger-file", "@"), /* 1.4 compatibility. */ ARGPARSE_s_n (oUseEmbeddedFilename, "use-embedded-filename", "@"), ARGPARSE_s_n (oNoUseEmbeddedFilename, "no-use-embedded-filename", "@"), ARGPARSE_s_n (oUtf8Strings, "utf8-strings", "@"), ARGPARSE_s_n (oNoUtf8Strings, "no-utf8-strings", "@"), ARGPARSE_s_n (oWithFingerprint, "with-fingerprint", "@"), ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprint", "@"), ARGPARSE_s_n (oWithSubkeyFingerprint, "with-subkey-fingerprints", "@"), ARGPARSE_s_n (oWithICAOSpelling, "with-icao-spelling", "@"), ARGPARSE_s_n (oWithKeygrip, "with-keygrip", "@"), ARGPARSE_s_n (oWithKeyScreening,"with-key-screening", "@"), ARGPARSE_s_n (oWithSecret, "with-secret", "@"), ARGPARSE_s_n (oWithWKDHash, "with-wkd-hash", "@"), ARGPARSE_s_n (oWithKeyOrigin, "with-key-origin", "@"), ARGPARSE_s_s (oDisableCipherAlgo, "disable-cipher-algo", "@"), ARGPARSE_s_s (oDisablePubkeyAlgo, "disable-pubkey-algo", "@"), ARGPARSE_s_n (oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", "@"), ARGPARSE_s_n (oNoAllowNonSelfsignedUID, "no-allow-non-selfsigned-uid", "@"), ARGPARSE_s_n (oAllowFreeformUID, "allow-freeform-uid", "@"), ARGPARSE_s_n (oNoAllowFreeformUID, "no-allow-freeform-uid", "@"), ARGPARSE_s_n (oNoLiteral, "no-literal", "@"), ARGPARSE_p_u (oSetFilesize, "set-filesize", "@"), ARGPARSE_s_n (oFastListMode, "fast-list-mode", "@"), ARGPARSE_s_n (oFixedListMode, "fixed-list-mode", "@"), ARGPARSE_s_n (oLegacyListMode, "legacy-list-mode", "@"), ARGPARSE_s_n (oListOnly, "list-only", "@"), ARGPARSE_s_n (oPrintPKARecords, "print-pka-records", "@"), ARGPARSE_s_n (oPrintDANERecords, "print-dane-records", "@"), ARGPARSE_s_n (oIgnoreTimeConflict, "ignore-time-conflict", "@"), ARGPARSE_s_n (oIgnoreValidFrom, "ignore-valid-from", "@"), ARGPARSE_s_n (oIgnoreCrcError, "ignore-crc-error", "@"), ARGPARSE_s_n (oIgnoreMDCError, "ignore-mdc-error", "@"), ARGPARSE_s_n (oShowSessionKey, "show-session-key", "@"), ARGPARSE_s_s (oOverrideSessionKey, "override-session-key", "@"), ARGPARSE_s_i (oOverrideSessionKeyFD, "override-session-key-fd", "@"), ARGPARSE_s_n (oNoRandomSeedFile, "no-random-seed-file", "@"), ARGPARSE_s_n (oAutoKeyRetrieve, "auto-key-retrieve", "@"), ARGPARSE_s_n (oNoAutoKeyRetrieve, "no-auto-key-retrieve", "@"), ARGPARSE_s_n (oNoSigCache, "no-sig-cache", "@"), ARGPARSE_s_n (oMergeOnly, "merge-only", "@" ), ARGPARSE_s_n (oAllowSecretKeyImport, "allow-secret-key-import", "@"), ARGPARSE_s_n (oTryAllSecrets, "try-all-secrets", "@"), ARGPARSE_s_n (oEnableSpecialFilenames, "enable-special-filenames", "@"), ARGPARSE_s_n (oNoExpensiveTrustChecks, "no-expensive-trust-checks", "@"), ARGPARSE_s_n (oPreservePermissions, "preserve-permissions", "@"), ARGPARSE_s_s (oDefaultPreferenceList, "default-preference-list", "@"), ARGPARSE_s_s (oDefaultKeyserverURL, "default-keyserver-url", "@"), ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-preferences","@"), ARGPARSE_s_s (oPersonalAEADPreferences, "personal-aead-preferences","@"), ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-preferences","@"), ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-preferences", "@"), ARGPARSE_s_s (oFakedSystemTime, "faked-system-time", "@"), ARGPARSE_s_s (oWeakDigest, "weak-digest","@"), ARGPARSE_s_n (oUnwrap, "unwrap", "@"), ARGPARSE_s_n (oOnlySignTextIDs, "only-sign-text-ids", "@"), /* Aliases. I constantly mistype these, and assume other people do as well. */ ARGPARSE_s_s (oPersonalCipherPreferences, "personal-cipher-prefs", "@"), ARGPARSE_s_s (oPersonalAEADPreferences, "personal-aead-prefs", "@"), ARGPARSE_s_s (oPersonalDigestPreferences, "personal-digest-prefs", "@"), ARGPARSE_s_s (oPersonalCompressPreferences, "personal-compress-prefs", "@"), ARGPARSE_s_s (oAgentProgram, "agent-program", "@"), + ARGPARSE_s_s (oKeyboxdProgram, "keyboxd-program", "@"), ARGPARSE_s_s (oDirmngrProgram, "dirmngr-program", "@"), ARGPARSE_s_n (oDisableDirmngr, "disable-dirmngr", "@"), ARGPARSE_s_s (oDisplay, "display", "@"), ARGPARSE_s_s (oTTYname, "ttyname", "@"), ARGPARSE_s_s (oTTYtype, "ttytype", "@"), ARGPARSE_s_s (oLCctype, "lc-ctype", "@"), ARGPARSE_s_s (oLCmessages, "lc-messages","@"), ARGPARSE_s_s (oXauthority, "xauthority", "@"), ARGPARSE_s_s (oGroup, "group", "@"), ARGPARSE_s_s (oUnGroup, "ungroup", "@"), ARGPARSE_s_n (oNoGroups, "no-groups", "@"), ARGPARSE_s_n (oStrict, "strict", "@"), ARGPARSE_s_n (oNoStrict, "no-strict", "@"), ARGPARSE_s_n (oMangleDosFilenames, "mangle-dos-filenames", "@"), ARGPARSE_s_n (oNoMangleDosFilenames, "no-mangle-dos-filenames", "@"), ARGPARSE_s_n (oEnableProgressFilter, "enable-progress-filter", "@"), ARGPARSE_s_n (oMultifile, "multifile", "@"), ARGPARSE_s_s (oKeyidFormat, "keyid-format", "@"), ARGPARSE_s_n (oExitOnStatusWriteError, "exit-on-status-write-error", "@"), ARGPARSE_s_i (oLimitCardInsertTries, "limit-card-insert-tries", "@"), ARGPARSE_s_n (oEnableLargeRSA, "enable-large-rsa", "@"), ARGPARSE_s_n (oDisableLargeRSA, "disable-large-rsa", "@"), ARGPARSE_s_n (oEnableDSA2, "enable-dsa2", "@"), ARGPARSE_s_n (oDisableDSA2, "disable-dsa2", "@"), ARGPARSE_s_n (oAllowWeakDigestAlgos, "allow-weak-digest-algos", "@"), ARGPARSE_s_s (oDefaultNewKeyAlgo, "default-new-key-algo", "@"), /* These two are aliases to help users of the PGP command line product use gpg with minimal pain. Many commands are common already as they seem to have borrowed commands from us. Now I'm returning the favor. */ ARGPARSE_s_s (oLocalUser, "sign-with", "@"), ARGPARSE_s_s (oRecipient, "user", "@"), ARGPARSE_s_n (oRequireCrossCert, "require-backsigs", "@"), ARGPARSE_s_n (oRequireCrossCert, "require-cross-certification", "@"), ARGPARSE_s_n (oNoRequireCrossCert, "no-require-backsigs", "@"), ARGPARSE_s_n (oNoRequireCrossCert, "no-require-cross-certification", "@"), /* New options. Fixme: Should go more to the top. */ ARGPARSE_s_s (oAutoKeyLocate, "auto-key-locate", "@"), ARGPARSE_s_n (oNoAutoKeyLocate, "no-auto-key-locate", "@"), ARGPARSE_s_n (oNoAutostart, "no-autostart", "@"), ARGPARSE_s_n (oNoSymkeyCache, "no-symkey-cache", "@"), + ARGPARSE_s_n (oUseKeyboxd, "use-keyboxd", "@"), /* Dummy options with warnings. */ ARGPARSE_s_n (oUseAgent, "use-agent", "@"), ARGPARSE_s_n (oNoUseAgent, "no-use-agent", "@"), ARGPARSE_s_s (oGpgAgentInfo, "gpg-agent-info", "@"), ARGPARSE_s_s (oReaderPort, "reader-port", "@"), ARGPARSE_s_s (octapiDriver, "ctapi-driver", "@"), ARGPARSE_s_s (opcscDriver, "pcsc-driver", "@"), ARGPARSE_s_n (oDisableCCID, "disable-ccid", "@"), ARGPARSE_s_n (oHonorHttpProxy, "honor-http-proxy", "@"), ARGPARSE_s_s (oTOFUDBFormat, "tofu-db-format", "@"), /* Dummy options. */ ARGPARSE_s_n (oNoop, "sk-comments", "@"), ARGPARSE_s_n (oNoop, "no-sk-comments", "@"), ARGPARSE_s_n (oNoop, "compress-keys", "@"), ARGPARSE_s_n (oNoop, "compress-sigs", "@"), ARGPARSE_s_n (oNoop, "force-v3-sigs", "@"), ARGPARSE_s_n (oNoop, "no-force-v3-sigs", "@"), ARGPARSE_s_n (oNoop, "force-v4-certs", "@"), ARGPARSE_s_n (oNoop, "no-force-v4-certs", "@"), ARGPARSE_s_n (oNoop, "no-mdc-warning", "@"), ARGPARSE_s_n (oNoop, "force-mdc", "@"), ARGPARSE_s_n (oNoop, "no-force-mdc", "@"), ARGPARSE_s_n (oNoop, "disable-mdc", "@"), ARGPARSE_s_n (oNoop, "no-disable-mdc", "@"), ARGPARSE_s_n (oNoop, "allow-multisig-verification", "@"), ARGPARSE_s_n (oNoop, "allow-multiple-messages", "@"), ARGPARSE_s_n (oNoop, "no-allow-multiple-messages", "@"), ARGPARSE_end () }; /* The list of supported debug flags. */ static struct debug_flags_s debug_flags [] = { { DBG_PACKET_VALUE , "packet" }, { DBG_MPI_VALUE , "mpi" }, { DBG_CRYPTO_VALUE , "crypto" }, { DBG_FILTER_VALUE , "filter" }, { DBG_IOBUF_VALUE , "iobuf" }, { DBG_MEMORY_VALUE , "memory" }, { DBG_CACHE_VALUE , "cache" }, { DBG_MEMSTAT_VALUE, "memstat" }, { DBG_TRUST_VALUE , "trust" }, { DBG_HASHING_VALUE, "hashing" }, { DBG_IPC_VALUE , "ipc" }, { DBG_CLOCK_VALUE , "clock" }, { DBG_LOOKUP_VALUE , "lookup" }, { DBG_EXTPROG_VALUE, "extprog" }, { 0, NULL } }; #ifdef ENABLE_SELINUX_HACKS #define ALWAYS_ADD_KEYRINGS 1 #else #define ALWAYS_ADD_KEYRINGS 0 #endif int g10_errors_seen = 0; static int utf8_strings = 0; static int maybe_setuid = 1; static unsigned int opt_set_iobuf_size; static unsigned int opt_set_iobuf_size_used; static char *build_list( const char *text, char letter, const char *(*mapf)(int), int (*chkf)(int) ); static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ); static void print_mds( const char *fname, int algo ); static void add_notation_data( const char *string, int which ); static void add_policy_url( const char *string, int which ); static void add_keyserver_url( const char *string, int which ); static void emergency_cleanup (void); static void read_sessionkey_from_fd (int fd); static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) { const char *s; char *result; if (maybe_setuid) { gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */ maybe_setuid = 0; } s = getfnc (NULL); result = xmalloc (strlen (libname) + 1 + strlen (s) + 1); strcpy (stpcpy (stpcpy (result, libname), " "), s); return result; } static int build_list_pk_test_algo (int algo) { /* Show only one "RSA" string. If RSA_E or RSA_S is available RSA is also available. */ if (algo == PUBKEY_ALGO_RSA_E || algo == PUBKEY_ALGO_RSA_S) return GPG_ERR_DIGEST_ALGO; return openpgp_pk_test_algo (algo); } static const char * build_list_pk_algo_name (int algo) { return openpgp_pk_algo_name (algo); } static int build_list_cipher_test_algo (int algo) { return openpgp_cipher_test_algo (algo); } static const char * build_list_cipher_algo_name (int algo) { return openpgp_cipher_algo_name (algo); } static int build_list_aead_test_algo (int algo) { return openpgp_aead_test_algo (algo); } static const char * build_list_aead_algo_name (int algo) { return openpgp_aead_algo_name (algo); } static int build_list_md_test_algo (int algo) { /* By default we do not accept MD5 based signatures. To avoid confusion we do not announce support for it either. */ if (algo == DIGEST_ALGO_MD5) return GPG_ERR_DIGEST_ALGO; return openpgp_md_test_algo (algo); } static const char * build_list_md_algo_name (int algo) { return openpgp_md_algo_name (algo); } static const char * my_strusage( int level ) { static char *digests, *pubkeys, *ciphers, *zips, *aeads, *ver_gcry; const char *p; switch( level ) { case 11: p = "@GPG@ (@GNUPG@)"; break; case 13: p = VERSION; break; case 17: p = PRINTABLE_OS_NAME; break; case 19: p = _("Please report bugs to <@EMAIL@>.\n"); break; case 20: if (!ver_gcry) ver_gcry = make_libversion ("libgcrypt", gcry_check_version); p = ver_gcry; break; #ifdef IS_DEVELOPMENT_VERSION case 25: p="NOTE: THIS IS A DEVELOPMENT VERSION!"; break; case 26: p="It is only intended for test purposes and should NOT be"; break; case 27: p="used in a production environment or with production keys!"; break; #endif case 1: case 40: p = _("Usage: @GPG@ [options] [files] (-h for help)"); break; case 41: p = _("Syntax: @GPG@ [options] [files]\n" "Sign, check, encrypt or decrypt\n" "Default operation depends on the input data\n"); break; case 31: p = "\nHome: "; break; #ifndef __riscos__ case 32: p = gnupg_homedir (); break; #else /* __riscos__ */ case 32: p = make_filename(gnupg_homedir (), NULL); break; #endif /* __riscos__ */ case 33: p = _("\nSupported algorithms:\n"); break; case 34: if (!pubkeys) pubkeys = build_list (_("Pubkey: "), 1, build_list_pk_algo_name, build_list_pk_test_algo ); p = pubkeys; break; case 35: if( !ciphers ) ciphers = build_list(_("Cipher: "), 'S', build_list_cipher_algo_name, build_list_cipher_test_algo ); p = ciphers; break; case 36: if (!aeads) aeads = build_list ("AEAD: ", 'A', build_list_aead_algo_name, build_list_aead_test_algo); p = aeads; break; case 37: if( !digests ) digests = build_list(_("Hash: "), 'H', build_list_md_algo_name, build_list_md_test_algo ); p = digests; break; case 38: if( !zips ) zips = build_list(_("Compression: "),'Z', compress_algo_to_string, check_compress_algo); p = zips; break; default: p = NULL; } return p; } static char * build_list (const char *text, char letter, const char * (*mapf)(int), int (*chkf)(int)) { membuf_t mb; int indent; int i, j, len; int limit; const char *s; char *string; if (maybe_setuid) gcry_control (GCRYCTL_INIT_SECMEM, 0, 0); /* Drop setuid. */ indent = utf8_charcount (text, -1); len = 0; init_membuf (&mb, 512); limit = (letter == 'A')? 4 : 110; for (i=0; i <= limit; i++ ) { if (!chkf (i) && (s = mapf (i))) { if (mb.len - len > 60) { put_membuf_str (&mb, ",\n"); len = mb.len; for (j=0; j < indent; j++) put_membuf_str (&mb, " "); } else if (mb.len) put_membuf_str (&mb, ", "); else put_membuf_str (&mb, text); put_membuf_str (&mb, s); if (opt.verbose && letter) { char num[20]; if (letter == 1) snprintf (num, sizeof num, " (%d)", i); else snprintf (num, sizeof num, " (%c%d)", letter, i); put_membuf_str (&mb, num); } } } if (mb.len) put_membuf_str (&mb, "\n"); put_membuf (&mb, "", 1); string = get_membuf (&mb, NULL); return xrealloc (string, strlen (string)+1); } static void wrong_args( const char *text) { es_fprintf (es_stderr, _("usage: %s [options] %s\n"), GPG_NAME, text); log_inc_errorcount (); g10_exit(2); } static char * make_username( const char *string ) { char *p; if( utf8_strings ) p = xstrdup(string); else p = native_to_utf8( string ); return p; } static void set_opt_session_env (const char *name, const char *value) { gpg_error_t err; err = session_env_setenv (opt.session_env, name, value); if (err) log_fatal ("error setting session environment: %s\n", gpg_strerror (err)); } /* Setup the debugging. With a LEVEL of NULL only the active debug flags are propagated to the subsystems. With LEVEL set, a specific set of debug flags is set; thus overriding all flags already set. */ static void set_debug (const char *level) { int numok = (level && digitp (level)); int numlvl = numok? atoi (level) : 0; if (!level) ; else if (!strcmp (level, "none") || (numok && numlvl < 1)) opt.debug = 0; else if (!strcmp (level, "basic") || (numok && numlvl <= 2)) opt.debug = DBG_MEMSTAT_VALUE; else if (!strcmp (level, "advanced") || (numok && numlvl <= 5)) opt.debug = DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE; else if (!strcmp (level, "expert") || (numok && numlvl <= 8)) opt.debug = (DBG_MEMSTAT_VALUE|DBG_TRUST_VALUE|DBG_EXTPROG_VALUE |DBG_CACHE_VALUE|DBG_LOOKUP|DBG_FILTER_VALUE|DBG_PACKET_VALUE); else if (!strcmp (level, "guru") || numok) { opt.debug = ~0; /* Unless the "guru" string has been used we don't want to allow hashing debugging. The rationale is that people tend to select the highest debug value and would then clutter their disk with debug files which may reveal confidential data. */ if (numok) opt.debug &= ~(DBG_HASHING_VALUE); } else { log_error (_("invalid debug-level '%s' given\n"), level); g10_exit (2); } if ((opt.debug & DBG_MEMORY_VALUE)) memory_debug_mode = 1; if ((opt.debug & DBG_MEMSTAT_VALUE)) memory_stat_debug_mode = 1; if (DBG_MPI) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 2); if (DBG_CRYPTO) gcry_control (GCRYCTL_SET_DEBUG_FLAGS, 1); if ((opt.debug & DBG_IOBUF_VALUE)) iobuf_debug_mode = 1; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); if (opt.debug) parse_debug_flag (NULL, &opt.debug, debug_flags); if (opt_set_iobuf_size || opt_set_iobuf_size_used) log_debug ("iobuf buffer size is %uk\n", iobuf_set_buffer_size (opt_set_iobuf_size)); } /* We set the screen dimensions for UI purposes. Do not allow screens smaller than 80x24 for the sake of simplicity. */ static void set_screen_dimensions(void) { #ifndef HAVE_W32_SYSTEM char *str; str=getenv("COLUMNS"); if(str) opt.screen_columns=atoi(str); str=getenv("LINES"); if(str) opt.screen_lines=atoi(str); #endif if(opt.screen_columns<80 || opt.screen_columns>255) opt.screen_columns=80; if(opt.screen_lines<24 || opt.screen_lines>255) opt.screen_lines=24; } /* Helper to open a file FNAME either for reading or writing to be used with --status-file etc functions. Not generally useful but it avoids the riscos specific functions and well some Windows people might like it too. Prints an error message and returns -1 on error. On success the file descriptor is returned. */ static int open_info_file (const char *fname, int for_write, int binary) { #ifdef __riscos__ return riscos_fdopenfile (fname, for_write); #elif defined (ENABLE_SELINUX_HACKS) /* We can't allow these even when testing for a secured filename because files to be secured might not yet been secured. This is similar to the option file but in that case it is unlikely that sensitive information may be retrieved by means of error messages. */ (void)fname; (void)for_write; (void)binary; return -1; #else int fd; if (binary) binary = MY_O_BINARY; /* if (is_secured_filename (fname)) */ /* { */ /* fd = -1; */ /* gpg_err_set_errno (EPERM); */ /* } */ /* else */ /* { */ do { if (for_write) fd = open (fname, O_CREAT | O_TRUNC | O_WRONLY | binary, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP); else fd = open (fname, O_RDONLY | binary); } while (fd == -1 && errno == EINTR); /* } */ if ( fd == -1) log_error ( for_write? _("can't create '%s': %s\n") : _("can't open '%s': %s\n"), fname, strerror(errno)); return fd; #endif } static void set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd ) { enum cmd_and_opt_values cmd = *ret_cmd; if( !cmd || cmd == new_cmd ) cmd = new_cmd; else if( cmd == aSign && new_cmd == aEncr ) cmd = aSignEncr; else if( cmd == aEncr && new_cmd == aSign ) cmd = aSignEncr; else if( cmd == aSign && new_cmd == aSym ) cmd = aSignSym; else if( cmd == aSym && new_cmd == aSign ) cmd = aSignSym; else if( cmd == aSym && new_cmd == aEncr ) cmd = aEncrSym; else if( cmd == aEncr && new_cmd == aSym ) cmd = aEncrSym; else if (cmd == aSignEncr && new_cmd == aSym) cmd = aSignEncrSym; else if (cmd == aSignSym && new_cmd == aEncr) cmd = aSignEncrSym; else if (cmd == aEncrSym && new_cmd == aSign) cmd = aSignEncrSym; else if( ( cmd == aSign && new_cmd == aClearsign ) || ( cmd == aClearsign && new_cmd == aSign ) ) cmd = aClearsign; else { log_error(_("conflicting commands\n")); g10_exit(2); } *ret_cmd = cmd; } static void add_group(char *string) { char *name,*value; struct groupitem *item; /* Break off the group name */ name=strsep(&string,"="); if(string==NULL) { log_error(_("no = sign found in group definition '%s'\n"),name); return; } trim_trailing_ws(name,strlen(name)); /* Does this group already exist? */ for(item=opt.grouplist;item;item=item->next) if(strcasecmp(item->name,name)==0) break; if(!item) { item=xmalloc(sizeof(struct groupitem)); item->name=name; item->next=opt.grouplist; item->values=NULL; opt.grouplist=item; } /* Break apart the values */ while ((value= strsep(&string," \t"))) { if (*value) add_to_strlist2(&item->values,value,utf8_strings); } } static void rm_group(char *name) { struct groupitem *item,*last=NULL; trim_trailing_ws(name,strlen(name)); for(item=opt.grouplist;item;last=item,item=item->next) { if(strcasecmp(item->name,name)==0) { if(last) last->next=item->next; else opt.grouplist=item->next; free_strlist(item->values); xfree(item); break; } } } /* We need to check three things. 0) The homedir. It must be x00, a directory, and owned by the user. 1) The options/gpg.conf file. Okay unless it or its containing directory is group or other writable or not owned by us. Disable exec in this case. 2) Extensions. Same as #1. Returns true if the item is unsafe. */ static int check_permissions (const char *path, int item) { #if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM) static int homedir_cache=-1; char *tmppath,*dir; struct stat statbuf,dirbuf; int homedir=0,ret=0,checkonly=0; int perm=0,own=0,enc_dir_perm=0,enc_dir_own=0; if(opt.no_perm_warn) return 0; log_assert(item==0 || item==1 || item==2); /* extensions may attach a path */ if(item==2 && path[0]!=DIRSEP_C) { if(strchr(path,DIRSEP_C)) tmppath=make_filename(path,NULL); else tmppath=make_filename(gnupg_libdir (),path,NULL); } else tmppath=xstrdup(path); /* If the item is located in the homedir, but isn't the homedir, don't continue if we already checked the homedir itself. This is to avoid user confusion with an extra options file warning which could be rectified if the homedir itself had proper permissions. */ if(item!=0 && homedir_cache>-1 && !ascii_strncasecmp (gnupg_homedir (), tmppath, strlen (gnupg_homedir ()))) { ret=homedir_cache; goto end; } /* It's okay if the file or directory doesn't exist */ if(stat(tmppath,&statbuf)!=0) { ret=0; goto end; } /* Now check the enclosing directory. Theoretically, we could walk this test up to the root directory /, but for the sake of sanity, I'm stopping at one level down. */ dir=make_dirname(tmppath); if(stat(dir,&dirbuf)!=0 || !S_ISDIR(dirbuf.st_mode)) { /* Weird error */ ret=1; goto end; } xfree(dir); /* Assume failure */ ret=1; if(item==0) { /* The homedir must be x00, a directory, and owned by the user. */ if(S_ISDIR(statbuf.st_mode)) { if(statbuf.st_uid==getuid()) { if((statbuf.st_mode & (S_IRWXG|S_IRWXO))==0) ret=0; else perm=1; } else own=1; homedir_cache=ret; } } else if(item==1 || item==2) { /* The options or extension file. Okay unless it or its containing directory is group or other writable or not owned by us or root. */ if(S_ISREG(statbuf.st_mode)) { if(statbuf.st_uid==getuid() || statbuf.st_uid==0) { if((statbuf.st_mode & (S_IWGRP|S_IWOTH))==0) { /* it's not writable, so make sure the enclosing directory is also not writable */ if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0) { if((dirbuf.st_mode & (S_IWGRP|S_IWOTH))==0) ret=0; else enc_dir_perm=1; } else enc_dir_own=1; } else { /* it's writable, so the enclosing directory had better not let people get to it. */ if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0) { if((dirbuf.st_mode & (S_IRWXG|S_IRWXO))==0) ret=0; else perm=enc_dir_perm=1; /* unclear which one to fix! */ } else enc_dir_own=1; } } else own=1; } } else BUG(); if(!checkonly) { if(own) { if(item==0) log_info(_("WARNING: unsafe ownership on" " homedir '%s'\n"),tmppath); else if(item==1) log_info(_("WARNING: unsafe ownership on" " configuration file '%s'\n"),tmppath); else log_info(_("WARNING: unsafe ownership on" " extension '%s'\n"),tmppath); } if(perm) { if(item==0) log_info(_("WARNING: unsafe permissions on" " homedir '%s'\n"),tmppath); else if(item==1) log_info(_("WARNING: unsafe permissions on" " configuration file '%s'\n"),tmppath); else log_info(_("WARNING: unsafe permissions on" " extension '%s'\n"),tmppath); } if(enc_dir_own) { if(item==0) log_info(_("WARNING: unsafe enclosing directory ownership on" " homedir '%s'\n"),tmppath); else if(item==1) log_info(_("WARNING: unsafe enclosing directory ownership on" " configuration file '%s'\n"),tmppath); else log_info(_("WARNING: unsafe enclosing directory ownership on" " extension '%s'\n"),tmppath); } if(enc_dir_perm) { if(item==0) log_info(_("WARNING: unsafe enclosing directory permissions on" " homedir '%s'\n"),tmppath); else if(item==1) log_info(_("WARNING: unsafe enclosing directory permissions on" " configuration file '%s'\n"),tmppath); else log_info(_("WARNING: unsafe enclosing directory permissions on" " extension '%s'\n"),tmppath); } } end: xfree(tmppath); if(homedir) homedir_cache=ret; return ret; #else /*!(HAVE_STAT && !HAVE_DOSISH_SYSTEM)*/ (void)path; (void)item; return 0; #endif /*!(HAVE_STAT && !HAVE_DOSISH_SYSTEM)*/ } /* Print the OpenPGP defined algo numbers. */ static void print_algo_numbers(int (*checker)(int)) { int i,first=1; for(i=0;i<=110;i++) { if(!checker(i)) { if(first) first=0; else es_printf (";"); es_printf ("%d",i); } } } static void print_algo_names(int (*checker)(int),const char *(*mapper)(int)) { int i,first=1; for(i=0;i<=110;i++) { if(!checker(i)) { if(first) first=0; else es_printf (";"); es_printf ("%s",mapper(i)); } } } /* In the future, we can do all sorts of interesting configuration output here. For now, just give "group" as the Enigmail folks need it, and pubkey, cipher, hash, and compress as they may be useful for frontends. */ static void list_config(char *items) { int show_all = !items; char *name = NULL; const char *s; struct groupitem *giter; int first, iter; if(!opt.with_colons) return; while(show_all || (name=strsep(&items," "))) { int any=0; if(show_all || ascii_strcasecmp(name,"group")==0) { for (giter = opt.grouplist; giter; giter = giter->next) { strlist_t sl; es_fprintf (es_stdout, "cfg:group:"); es_write_sanitized (es_stdout, giter->name, strlen(giter->name), ":", NULL); es_putc (':', es_stdout); for(sl=giter->values; sl; sl=sl->next) { es_write_sanitized (es_stdout, sl->d, strlen (sl->d), ":;", NULL); if(sl->next) es_printf(";"); } es_printf("\n"); } any=1; } if(show_all || ascii_strcasecmp(name,"version")==0) { es_printf("cfg:version:"); es_write_sanitized (es_stdout, VERSION, strlen(VERSION), ":", NULL); es_printf ("\n"); any=1; } if(show_all || ascii_strcasecmp(name,"pubkey")==0) { es_printf ("cfg:pubkey:"); print_algo_numbers (build_list_pk_test_algo); es_printf ("\n"); any=1; } if(show_all || ascii_strcasecmp(name,"pubkeyname")==0) { es_printf ("cfg:pubkeyname:"); print_algo_names (build_list_pk_test_algo, build_list_pk_algo_name); es_printf ("\n"); any=1; } if(show_all || ascii_strcasecmp(name,"cipher")==0) { es_printf ("cfg:cipher:"); print_algo_numbers (build_list_cipher_test_algo); es_printf ("\n"); any=1; } if (show_all || !ascii_strcasecmp (name,"ciphername")) { es_printf ("cfg:ciphername:"); print_algo_names (build_list_cipher_test_algo, build_list_cipher_algo_name); es_printf ("\n"); any = 1; } if(show_all || ascii_strcasecmp(name,"digest")==0 || ascii_strcasecmp(name,"hash")==0) { es_printf ("cfg:digest:"); print_algo_numbers (build_list_md_test_algo); es_printf ("\n"); any=1; } if (show_all || !ascii_strcasecmp(name,"digestname") || !ascii_strcasecmp(name,"hashname")) { es_printf ("cfg:digestname:"); print_algo_names (build_list_md_test_algo, build_list_md_algo_name); es_printf ("\n"); any=1; } if(show_all || ascii_strcasecmp(name,"compress")==0) { es_printf ("cfg:compress:"); print_algo_numbers(check_compress_algo); es_printf ("\n"); any=1; } if(show_all || ascii_strcasecmp (name, "compressname") == 0) { es_printf ("cfg:compressname:"); print_algo_names (check_compress_algo, compress_algo_to_string); es_printf ("\n"); any=1; } if (show_all || !ascii_strcasecmp(name,"ccid-reader-id")) { /* We ignore this for GnuPG 1.4 backward compatibility. */ any=1; } if (show_all || !ascii_strcasecmp (name,"curve")) { es_printf ("cfg:curve:"); for (iter=0, first=1; (s = openpgp_enum_curves (&iter)); first=0) es_printf ("%s%s", first?"":";", s); es_printf ("\n"); any=1; } /* Curve OIDs are rarely useful and thus only printed if requested. */ if (name && !ascii_strcasecmp (name,"curveoid")) { es_printf ("cfg:curveoid:"); for (iter=0, first=1; (s = openpgp_enum_curves (&iter)); first = 0) { s = openpgp_curve_to_oid (s, NULL); es_printf ("%s%s", first?"":";", s? s:"[?]"); } es_printf ("\n"); any=1; } if(show_all) break; if(!any) log_error(_("unknown configuration item '%s'\n"),name); } } /* List options and default values in the GPG Conf format. This is a new tool distributed with gnupg 1.9.x but we also want some limited support in older gpg versions. The output is the name of the configuration file and a list of options available for editing by gpgconf. */ static void gpgconf_list (const char *configfile) { char *configfile_esc = percent_escape (configfile, NULL); es_printf ("%s-%s.conf:%lu:\"%s\n", GPGCONF_NAME, GPG_NAME, GC_OPT_FLAG_DEFAULT, configfile_esc ? configfile_esc : "/dev/null"); es_printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("default-key:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("encrypt-to:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("try-secret-key:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("auto-key-locate:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("auto-key-retrieve:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("log-file:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("debug-level:%lu:\"none:\n", GC_OPT_FLAG_DEFAULT); es_printf ("group:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("compliance:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, "gnupg"); es_printf ("default-new-key-algo:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("trust-model:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("disable-dirmngr:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("max-cert-depth:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("completes-needed:%lu:\n", GC_OPT_FLAG_NONE); es_printf ("marginals-needed:%lu:\n", GC_OPT_FLAG_NONE); /* The next one is an info only item and should match the macros at the top of keygen.c */ es_printf ("default_pubkey_algo:%lu:\"%s:\n", GC_OPT_FLAG_DEFAULT, get_default_pubkey_algo ()); xfree (configfile_esc); } static int parse_subpacket_list(char *list) { char *tok; byte subpackets[128],i; int count=0; if(!list) { /* No arguments means all subpackets */ memset(subpackets+1,1,sizeof(subpackets)-1); count=127; } else { memset(subpackets,0,sizeof(subpackets)); /* Merge with earlier copy */ if(opt.show_subpackets) { byte *in; for(in=opt.show_subpackets;*in;in++) { if(*in>127 || *in<1) BUG(); if(!subpackets[*in]) count++; subpackets[*in]=1; } } while((tok=strsep(&list," ,"))) { if(!*tok) continue; i=atoi(tok); if(i>127 || i<1) return 0; if(!subpackets[i]) count++; subpackets[i]=1; } } xfree(opt.show_subpackets); opt.show_subpackets=xmalloc(count+1); opt.show_subpackets[count--]=0; for(i=1;i<128 && count>=0;i++) if(subpackets[i]) opt.show_subpackets[count--]=i; return 1; } static int parse_list_options(char *str) { char *subpackets=""; /* something that isn't NULL */ struct parse_options lopts[]= { {"show-photos",LIST_SHOW_PHOTOS,NULL, N_("display photo IDs during key listings")}, {"show-usage",LIST_SHOW_USAGE,NULL, N_("show key usage information during key listings")}, {"show-policy-urls",LIST_SHOW_POLICY_URLS,NULL, N_("show policy URLs during signature listings")}, {"show-notations",LIST_SHOW_NOTATIONS,NULL, N_("show all notations during signature listings")}, {"show-std-notations",LIST_SHOW_STD_NOTATIONS,NULL, N_("show IETF standard notations during signature listings")}, {"show-standard-notations",LIST_SHOW_STD_NOTATIONS,NULL, NULL}, {"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL, N_("show user-supplied notations during signature listings")}, {"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL, N_("show preferred keyserver URLs during signature listings")}, {"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL, N_("show user ID validity during key listings")}, {"show-unusable-uids",LIST_SHOW_UNUSABLE_UIDS,NULL, N_("show revoked and expired user IDs in key listings")}, {"show-unusable-subkeys",LIST_SHOW_UNUSABLE_SUBKEYS,NULL, N_("show revoked and expired subkeys in key listings")}, {"show-keyring",LIST_SHOW_KEYRING,NULL, N_("show the keyring name in key listings")}, {"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL, N_("show expiration dates during signature listings")}, {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL, NULL}, {"show-only-fpr-mbox",LIST_SHOW_ONLY_FPR_MBOX, NULL, NULL}, {NULL,0,NULL,NULL} }; /* C99 allows for non-constant initializers, but we'd like to compile everywhere, so fill in the show-sig-subpackets argument here. Note that if the parse_options array changes, we'll have to change the subscript here. */ lopts[13].value=&subpackets; if(parse_options(str,&opt.list_options,lopts,1)) { if(opt.list_options&LIST_SHOW_SIG_SUBPACKETS) { /* Unset so users can pass multiple lists in. */ opt.list_options&=~LIST_SHOW_SIG_SUBPACKETS; if(!parse_subpacket_list(subpackets)) return 0; } else if(subpackets==NULL && opt.show_subpackets) { /* User did 'no-show-subpackets' */ xfree(opt.show_subpackets); opt.show_subpackets=NULL; } return 1; } else return 0; } /* Collapses argc/argv into a single string that must be freed */ static char * collapse_args(int argc,char *argv[]) { char *str=NULL; int i,first=1,len=0; for(i=0;imagic = SERVER_CONTROL_MAGIC; } /* This function is called to deinitialize a control object. It is not deallocated. */ static void gpg_deinit_default_ctrl (ctrl_t ctrl) { #ifdef USE_TOFU tofu_closedbs (ctrl); #endif gpg_dirmngr_deinit_session_data (ctrl); keydb_release (ctrl->cached_getkey_kdb); } char * get_default_configname (void) { char *configname = NULL; char *name = xstrdup (GPG_NAME EXTSEP_S "conf-" SAFE_VERSION); char *ver = &name[strlen (GPG_NAME EXTSEP_S "conf-")]; do { if (configname) { char *tok; xfree (configname); configname = NULL; if ((tok = strrchr (ver, SAFE_VERSION_DASH))) *tok='\0'; else if ((tok = strrchr (ver, SAFE_VERSION_DOT))) *tok='\0'; else break; } configname = make_filename (gnupg_homedir (), name, NULL); } while (access (configname, R_OK)); xfree(name); if (! configname) configname = make_filename (gnupg_homedir (), GPG_NAME EXTSEP_S "conf", NULL); if (! access (configname, R_OK)) { /* Print a warning when both config files are present. */ char *p = make_filename (gnupg_homedir (), "options", NULL); if (! access (p, R_OK)) log_info (_("Note: old default options file '%s' ignored\n"), p); xfree (p); } else { /* Use the old default only if it exists. */ char *p = make_filename (gnupg_homedir (), "options", NULL); if (!access (p, R_OK)) { xfree (configname); configname = p; } else xfree (p); } return configname; } int main (int argc, char **argv) { ARGPARSE_ARGS pargs; IOBUF a; int rc=0; int orig_argc; char **orig_argv; const char *fname; char *username; int may_coredump; strlist_t sl; strlist_t remusr = NULL; strlist_t locusr = NULL; strlist_t nrings = NULL; armor_filter_context_t *afx = NULL; int detached_sig = 0; FILE *configfp = NULL; char *configname = NULL; char *save_configname = NULL; char *default_configname = NULL; unsigned configlineno; int parse_debug = 0; int default_config = 1; int default_keyring = 1; int greeting = 0; int nogreeting = 0; char *logfile = NULL; int use_random_seed = 1; enum cmd_and_opt_values cmd = 0; const char *debug_level = NULL; #ifndef NO_TRUST_MODELS const char *trustdb_name = NULL; #endif /*!NO_TRUST_MODELS*/ char *def_cipher_string = NULL; char *def_aead_string = NULL; char *def_digest_string = NULL; char *compress_algo_string = NULL; char *cert_digest_string = NULL; char *s2k_cipher_string = NULL; char *s2k_digest_string = NULL; char *pers_cipher_list = NULL; char *pers_aead_list = NULL; char *pers_digest_list = NULL; char *pers_compress_list = NULL; int eyes_only=0; int multifile=0; int pwfd = -1; int ovrseskeyfd = -1; int fpr_maybe_cmd = 0; /* --fingerprint maybe a command. */ int any_explicit_recipient = 0; int default_akl = 1; int require_secmem = 0; int got_secmem = 0; struct assuan_malloc_hooks malloc_hooks; ctrl_t ctrl; static int print_dane_records; static int print_pka_records; static int allow_large_chunks; #ifdef __riscos__ opt.lock_once = 1; #endif /* __riscos__ */ /* Please note that we may running SUID(ROOT), so be very CAREFUL when adding any stuff between here and the call to secmem_init() somewhere after the option parsing. */ early_system_init (); gnupg_reopen_std (GPG_NAME); trap_unaligned (); gnupg_rl_initialize (); set_strusage (my_strusage); gcry_control (GCRYCTL_SUSPEND_SECMEM_WARN); log_set_prefix (GPG_NAME, GPGRT_LOG_WITH_PREFIX); /* Make sure that our subsystems are ready. */ i18n_init(); init_common_subsystems (&argc, &argv); /* Use our own logging handler for Libcgrypt. */ setup_libgcrypt_logging (); /* Put random number into secure memory */ gcry_control (GCRYCTL_USE_SECURE_RNDPOOL); may_coredump = disable_core_dumps(); gnupg_init_signals (0, emergency_cleanup); dotlock_create (NULL, 0); /* Register lock file cleanup. */ /* Tell the compliance module who we are. */ gnupg_initialize_compliance (GNUPG_MODULE_NAME_GPG); opt.autostart = 1; opt.session_env = session_env_new (); if (!opt.session_env) log_fatal ("error allocating session environment block: %s\n", strerror (errno)); opt.command_fd = -1; /* no command fd */ opt.compress_level = -1; /* defaults to standard compress level */ opt.bz2_compress_level = -1; /* defaults to standard compress level */ /* note: if you change these lines, look at oOpenPGP */ opt.def_cipher_algo = 0; opt.def_aead_algo = 0; opt.def_digest_algo = 0; opt.cert_digest_algo = 0; opt.compress_algo = -1; /* defaults to DEFAULT_COMPRESS_ALGO */ opt.s2k_mode = 3; /* iterated+salted */ opt.s2k_count = 0; /* Auto-calibrate when needed. */ opt.s2k_cipher_algo = DEFAULT_CIPHER_ALGO; opt.completes_needed = 1; opt.marginals_needed = 3; opt.max_cert_depth = 5; opt.escape_from = 1; opt.flags.require_cross_cert = 1; opt.import_options = IMPORT_REPAIR_KEYS; opt.export_options = EXPORT_ATTRIBUTES; opt.keyserver_options.import_options = (IMPORT_REPAIR_KEYS | IMPORT_REPAIR_PKS_SUBKEY_BUG | IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN); opt.keyserver_options.export_options = EXPORT_ATTRIBUTES; opt.keyserver_options.options = KEYSERVER_HONOR_PKA_RECORD; opt.verify_options = (LIST_SHOW_UID_VALIDITY | VERIFY_SHOW_POLICY_URLS | VERIFY_SHOW_STD_NOTATIONS | VERIFY_SHOW_KEYSERVER_URLS); opt.list_options = (LIST_SHOW_UID_VALIDITY | LIST_SHOW_USAGE); #ifdef NO_TRUST_MODELS opt.trust_model = TM_ALWAYS; #else opt.trust_model = TM_AUTO; #endif opt.tofu_default_policy = TOFU_POLICY_AUTO; opt.mangle_dos_filenames = 0; opt.min_cert_level = 2; set_screen_dimensions (); opt.keyid_format = KF_NONE; opt.def_sig_expire = "0"; opt.def_cert_expire = "0"; gnupg_set_homedir (NULL); opt.passphrase_repeat = 1; opt.emit_version = 0; opt.weak_digests = NULL; opt.compliance = CO_GNUPG; opt.flags.rfc4880bis = 1; /* Check whether we have a config file on the command line. */ orig_argc = argc; orig_argv = argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= (ARGPARSE_FLAG_KEEP | ARGPARSE_FLAG_NOVERSION); while( arg_parse( &pargs, opts) ) { if( pargs.r_opt == oDebug || pargs.r_opt == oDebugAll ) parse_debug++; else if (pargs.r_opt == oDebugIOLBF) es_setvbuf (es_stdout, NULL, _IOLBF, 0); else if( pargs.r_opt == oOptions ) { /* yes there is one, so we do not try the default one, but * read the option file when it is encountered at the commandline */ default_config = 0; } else if( pargs.r_opt == oNoOptions ) { default_config = 0; /* --no-options */ opt.no_homedir_creation = 1; } else if( pargs.r_opt == oHomedir ) gnupg_set_homedir (pargs.r.ret_str); else if( pargs.r_opt == oNoPermissionWarn ) opt.no_perm_warn=1; else if (pargs.r_opt == oStrict ) { /* Not used */ } else if (pargs.r_opt == oNoStrict ) { /* Not used */ } } #ifdef HAVE_DOSISH_SYSTEM if ( strchr (gnupg_homedir (), '\\') ) { char *d, *buf = xmalloc (strlen (gnupg_homedir ())+1); const char *s; for (d=buf, s = gnupg_homedir (); *s; s++) { *d++ = *s == '\\'? '/': *s; #ifdef HAVE_W32_SYSTEM if (s[1] && IsDBCSLeadByte (*s)) *d++ = *++s; #endif } *d = 0; gnupg_set_homedir (buf); } #endif /* Initialize the secure memory. */ if (!gcry_control (GCRYCTL_INIT_SECMEM, SECMEM_BUFFER_SIZE, 0)) got_secmem = 1; #if defined(HAVE_GETUID) && defined(HAVE_GETEUID) /* There should be no way to get to this spot while still carrying setuid privs. Just in case, bomb out if we are. */ if ( getuid () != geteuid () ) BUG (); #endif maybe_setuid = 0; /* Okay, we are now working under our real uid */ /* malloc hooks go here ... */ malloc_hooks.malloc = gcry_malloc; malloc_hooks.realloc = gcry_realloc; malloc_hooks.free = gcry_free; assuan_set_malloc_hooks (&malloc_hooks); assuan_set_gpg_err_source (GPG_ERR_SOURCE_DEFAULT); setup_libassuan_logging (&opt.debug, NULL); /* Set default options which require that malloc stuff is ready. */ additional_weak_digest ("MD5"); parse_auto_key_locate ("local,wkd"); /* Try for a version specific config file first */ default_configname = get_default_configname (); if (default_config) configname = xstrdup (default_configname); argc = orig_argc; argv = orig_argv; pargs.argc = &argc; pargs.argv = &argv; pargs.flags= ARGPARSE_FLAG_KEEP; /* By this point we have a homedir, and cannot change it. */ check_permissions (gnupg_homedir (), 0); next_pass: if( configname ) { if(check_permissions(configname,1)) { /* If any options file is unsafe, then disable any external programs for keyserver calls or photo IDs. Since the external program to call is set in the options file, a unsafe options file can lead to an arbitrary program being run. */ opt.exec_disable=1; } configlineno = 0; configfp = fopen( configname, "r" ); if (configfp && is_secured_file (fileno (configfp))) { fclose (configfp); configfp = NULL; gpg_err_set_errno (EPERM); } if( !configfp ) { if( default_config ) { if( parse_debug ) log_info(_("Note: no default option file '%s'\n"), configname ); } else { log_error(_("option file '%s': %s\n"), configname, strerror(errno) ); g10_exit(2); } xfree(configname); configname = NULL; } if( parse_debug && configname ) log_info(_("reading options from '%s'\n"), configname ); default_config = 0; } while( optfile_parse( configfp, configname, &configlineno, &pargs, opts) ) { switch( pargs.r_opt ) { case aListConfig: case aListGcryptConfig: case aGPGConfList: case aGPGConfTest: set_cmd (&cmd, pargs.r_opt); /* Do not register a keyring for these commands. */ default_keyring = -1; break; case aCheckKeys: case aListPackets: case aImport: case aFastImport: case aSendKeys: case aRecvKeys: case aSearchKeys: case aRefreshKeys: case aFetchKeys: case aExport: #ifdef ENABLE_CARD_SUPPORT case aCardStatus: case aCardEdit: case aChangePIN: #endif /* ENABLE_CARD_SUPPORT*/ case aListKeys: case aLocateKeys: case aLocateExtKeys: case aListSigs: case aExportSecret: case aExportSecretSub: case aExportSshKey: case aSym: case aClearsign: case aGenRevoke: case aDesigRevoke: case aPrimegen: case aGenRandom: case aPrintMD: case aPrintMDs: case aListTrustDB: case aCheckTrustDB: case aUpdateTrustDB: case aFixTrustDB: case aListTrustPath: case aDeArmor: case aEnArmor: case aSign: case aQuickSignKey: case aQuickLSignKey: case aSignKey: case aLSignKey: case aStore: case aQuickKeygen: case aQuickAddUid: case aQuickAddKey: case aQuickRevUid: case aQuickSetExpire: case aQuickSetPrimaryUid: case aExportOwnerTrust: case aImportOwnerTrust: case aRebuildKeydbCaches: set_cmd (&cmd, pargs.r_opt); break; case aKeygen: case aFullKeygen: case aEditKey: case aDeleteSecretKeys: case aDeleteSecretAndPublicKeys: case aDeleteKeys: case aPasswd: set_cmd (&cmd, pargs.r_opt); greeting=1; break; case aShowKeys: set_cmd (&cmd, pargs.r_opt); opt.import_options |= IMPORT_SHOW; opt.import_options |= IMPORT_DRY_RUN; opt.import_options &= ~IMPORT_REPAIR_KEYS; opt.list_options |= LIST_SHOW_UNUSABLE_UIDS; opt.list_options |= LIST_SHOW_UNUSABLE_SUBKEYS; opt.list_options |= LIST_SHOW_NOTATIONS; opt.list_options |= LIST_SHOW_POLICY_URLS; break; case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break; case aDecryptFiles: multifile=1; /* fall through */ case aDecrypt: set_cmd( &cmd, aDecrypt); break; case aEncrFiles: multifile=1; /* fall through */ case aEncr: set_cmd( &cmd, aEncr); break; case aVerifyFiles: multifile=1; /* fall through */ case aVerify: set_cmd( &cmd, aVerify); break; case aServer: set_cmd (&cmd, pargs.r_opt); opt.batch = 1; break; case aTOFUPolicy: set_cmd (&cmd, pargs.r_opt); break; case oArmor: opt.armor = 1; opt.no_armor=0; break; case oOutput: opt.outfile = pargs.r.ret_str; break; case oMaxOutput: opt.max_output = pargs.r.ret_ulong; break; case oInputSizeHint: opt.input_size_hint = string_to_u64 (pargs.r.ret_str); break; case oChunkSize: opt.chunk_size = pargs.r.ret_int; break; case oQuiet: opt.quiet = 1; break; case oNoTTY: tty_no_terminal(1); break; case oDryRun: opt.dry_run = 1; break; case oInteractive: opt.interactive = 1; break; case oVerbose: opt.verbose++; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); opt.list_options|=LIST_SHOW_UNUSABLE_UIDS; opt.list_options|=LIST_SHOW_UNUSABLE_SUBKEYS; break; case oBatch: opt.batch = 1; nogreeting = 1; break; case oUseAgent: /* Dummy. */ break; case oNoUseAgent: obsolete_option (configname, configlineno, "no-use-agent"); break; case oGpgAgentInfo: obsolete_option (configname, configlineno, "gpg-agent-info"); break; + + case oUseKeyboxd: + opt.use_keyboxd = 1; + break; + case oReaderPort: obsolete_scdaemon_option (configname, configlineno, "reader-port"); break; case octapiDriver: obsolete_scdaemon_option (configname, configlineno, "ctapi-driver"); break; case opcscDriver: obsolete_scdaemon_option (configname, configlineno, "pcsc-driver"); break; case oDisableCCID: obsolete_scdaemon_option (configname, configlineno, "disable-ccid"); break; case oHonorHttpProxy: obsolete_option (configname, configlineno, "honor-http-proxy"); break; case oAnswerYes: opt.answer_yes = 1; break; case oAnswerNo: opt.answer_no = 1; break; case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break; case oPrimaryKeyring: sl = append_to_strlist (&nrings, pargs.r.ret_str); sl->flags = KEYDB_RESOURCE_FLAG_PRIMARY; break; case oShowKeyring: deprecated_warning(configname,configlineno,"--show-keyring", "--list-options ","show-keyring"); opt.list_options|=LIST_SHOW_KEYRING; break; case oDebug: if (parse_debug_flag (pargs.r.ret_str, &opt.debug, debug_flags)) { pargs.r_opt = ARGPARSE_INVALID_ARG; pargs.err = ARGPARSE_PRINT_ERROR; } break; case oDebugAll: opt.debug = ~0; break; case oDebugLevel: debug_level = pargs.r.ret_str; break; case oDebugIOLBF: break; /* Already set in pre-parse step. */ case oDebugSetIobufSize: opt_set_iobuf_size = pargs.r.ret_ulong; opt_set_iobuf_size_used = 1; break; case oDebugAllowLargeChunks: allow_large_chunks = 1; break; case oStatusFD: set_status_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) ); break; case oStatusFile: set_status_fd ( open_info_file (pargs.r.ret_str, 1, 0) ); break; case oAttributeFD: set_attrib_fd ( translate_sys2libc_fd_int (pargs.r.ret_int, 1) ); break; case oAttributeFile: set_attrib_fd ( open_info_file (pargs.r.ret_str, 1, 1) ); break; case oLoggerFD: log_set_fd (translate_sys2libc_fd_int (pargs.r.ret_int, 1)); break; case oLoggerFile: logfile = pargs.r.ret_str; break; case oWithFingerprint: opt.with_fingerprint = 1; opt.fingerprint++; break; case oWithSubkeyFingerprint: opt.with_subkey_fingerprint = 1; break; case oWithICAOSpelling: opt.with_icao_spelling = 1; break; case oFingerprint: opt.fingerprint++; fpr_maybe_cmd = 1; break; case oWithKeygrip: opt.with_keygrip = 1; break; case oWithKeyScreening: opt.with_key_screening = 1; break; case oWithSecret: opt.with_secret = 1; break; case oWithWKDHash: opt.with_wkd_hash = 1; break; case oWithKeyOrigin: opt.with_key_origin = 1; break; case oSecretKeyring: /* Ignore this old option. */ break; case oOptions: /* config files may not be nested (silently ignore them) */ if( !configfp ) { xfree(configname); configname = xstrdup(pargs.r.ret_str); goto next_pass; } break; case oNoArmor: opt.no_armor=1; opt.armor=0; break; case oNoDefKeyring: if (default_keyring > 0) default_keyring = 0; break; case oNoKeyring: default_keyring = -1; break; case oNoGreeting: nogreeting = 1; break; case oNoVerbose: opt.verbose = 0; gcry_control (GCRYCTL_SET_VERBOSITY, (int)opt.verbose); opt.list_sigs=0; break; case oQuickRandom: gcry_control (GCRYCTL_ENABLE_QUICK_RANDOM, 0); break; case oEmitVersion: opt.emit_version++; break; case oNoEmitVersion: opt.emit_version=0; break; case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break; case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break; case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break; #ifndef NO_TRUST_MODELS case oTrustDBName: trustdb_name = pargs.r.ret_str; break; #endif /*!NO_TRUST_MODELS*/ case oDefaultKey: sl = add_to_strlist (&opt.def_secret_key, pargs.r.ret_str); sl->flags = (pargs.r_opt << PK_LIST_SHIFT); if (configfp) sl->flags |= PK_LIST_CONFIG; break; case oDefRecipient: if( *pargs.r.ret_str ) { xfree (opt.def_recipient); opt.def_recipient = make_username(pargs.r.ret_str); } break; case oDefRecipientSelf: xfree(opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 1; break; case oNoDefRecipient: xfree(opt.def_recipient); opt.def_recipient = NULL; opt.def_recipient_self = 0; break; case oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */ case oHomedir: break; case oNoBatch: opt.batch = 0; break; case oWithTofuInfo: opt.with_tofu_info = 1; break; case oWithKeyData: opt.with_key_data=1; /*FALLTHRU*/ case oWithColons: opt.with_colons=':'; break; case oWithSigCheck: opt.check_sigs = 1; /*FALLTHRU*/ case oWithSigList: opt.list_sigs = 1; break; case oSkipVerify: opt.skip_verify=1; break; case oSkipHiddenRecipients: opt.skip_hidden_recipients = 1; break; case oNoSkipHiddenRecipients: opt.skip_hidden_recipients = 0; break; case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break; #ifndef NO_TRUST_MODELS /* There are many programs (like mutt) that call gpg with --always-trust so keep this option around for a long time. */ case oAlwaysTrust: opt.trust_model=TM_ALWAYS; break; case oTrustModel: parse_trust_model(pargs.r.ret_str); break; #endif /*!NO_TRUST_MODELS*/ case oTOFUDefaultPolicy: opt.tofu_default_policy = parse_tofu_policy (pargs.r.ret_str); break; case oTOFUDBFormat: obsolete_option (configname, configlineno, "tofu-db-format"); break; case oForceOwnertrust: log_info(_("Note: %s is not for normal use!\n"), "--force-ownertrust"); opt.force_ownertrust=string_to_trust_value(pargs.r.ret_str); if(opt.force_ownertrust==-1) { log_error("invalid ownertrust '%s'\n",pargs.r.ret_str); opt.force_ownertrust=0; } break; case oLoadExtension: /* Dummy so that gpg 1.4 conf files can work. Should eventually be removed. */ break; case oCompliance: { int compliance = gnupg_parse_compliance_option (pargs.r.ret_str, compliance_options, DIM (compliance_options), opt.quiet); if (compliance < 0) g10_exit (1); set_compliance_option (compliance); } break; case oOpenPGP: case oRFC2440: case oRFC4880: case oRFC4880bis: case oPGP7: case oPGP8: case oGnuPG: set_compliance_option (pargs.r_opt); break; case oRFC2440Text: opt.rfc2440_text=1; break; case oNoRFC2440Text: opt.rfc2440_text=0; break; case oSetFilename: if(utf8_strings) opt.set_filename = pargs.r.ret_str; else opt.set_filename = native_to_utf8(pargs.r.ret_str); break; case oForYourEyesOnly: eyes_only = 1; break; case oNoForYourEyesOnly: eyes_only = 0; break; case oSetPolicyURL: add_policy_url(pargs.r.ret_str,0); add_policy_url(pargs.r.ret_str,1); break; case oSigPolicyURL: add_policy_url(pargs.r.ret_str,0); break; case oCertPolicyURL: add_policy_url(pargs.r.ret_str,1); break; case oShowPolicyURL: deprecated_warning(configname,configlineno,"--show-policy-url", "--list-options ","show-policy-urls"); deprecated_warning(configname,configlineno,"--show-policy-url", "--verify-options ","show-policy-urls"); opt.list_options|=LIST_SHOW_POLICY_URLS; opt.verify_options|=VERIFY_SHOW_POLICY_URLS; break; case oNoShowPolicyURL: deprecated_warning(configname,configlineno,"--no-show-policy-url", "--list-options ","no-show-policy-urls"); deprecated_warning(configname,configlineno,"--no-show-policy-url", "--verify-options ","no-show-policy-urls"); opt.list_options&=~LIST_SHOW_POLICY_URLS; opt.verify_options&=~VERIFY_SHOW_POLICY_URLS; break; case oSigKeyserverURL: add_keyserver_url(pargs.r.ret_str,0); break; case oUseEmbeddedFilename: opt.flags.use_embedded_filename=1; break; case oNoUseEmbeddedFilename: opt.flags.use_embedded_filename=0; break; case oComment: if(pargs.r.ret_str[0]) append_to_strlist(&opt.comments,pargs.r.ret_str); break; case oDefaultComment: deprecated_warning(configname,configlineno, "--default-comment","--no-comments",""); /* fall through */ case oNoComments: free_strlist(opt.comments); opt.comments=NULL; break; case oThrowKeyids: opt.throw_keyids = 1; break; case oNoThrowKeyids: opt.throw_keyids = 0; break; case oShowPhotos: deprecated_warning(configname,configlineno,"--show-photos", "--list-options ","show-photos"); deprecated_warning(configname,configlineno,"--show-photos", "--verify-options ","show-photos"); opt.list_options|=LIST_SHOW_PHOTOS; opt.verify_options|=VERIFY_SHOW_PHOTOS; break; case oNoShowPhotos: deprecated_warning(configname,configlineno,"--no-show-photos", "--list-options ","no-show-photos"); deprecated_warning(configname,configlineno,"--no-show-photos", "--verify-options ","no-show-photos"); opt.list_options&=~LIST_SHOW_PHOTOS; opt.verify_options&=~VERIFY_SHOW_PHOTOS; break; case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break; case oForceAEAD: opt.force_aead = 1; break; case oDisableSignerUID: opt.flags.disable_signer_uid = 1; break; case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break; case oS2KDigest: s2k_digest_string = xstrdup(pargs.r.ret_str); break; case oS2KCipher: s2k_cipher_string = xstrdup(pargs.r.ret_str); break; case oS2KCount: if (pargs.r.ret_int) opt.s2k_count = encode_s2k_iterations (pargs.r.ret_int); else opt.s2k_count = 0; /* Auto-calibrate when needed. */ break; case oRecipient: case oHiddenRecipient: case oRecipientFile: case oHiddenRecipientFile: /* Store the recipient. Note that we also store the * option as private data in the flags. This is achieved * by shifting the option value to the left so to keep * enough space for the flags. */ sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); sl->flags = (pargs.r_opt << PK_LIST_SHIFT); if (configfp) sl->flags |= PK_LIST_CONFIG; if (pargs.r_opt == oHiddenRecipient || pargs.r_opt == oHiddenRecipientFile) sl->flags |= PK_LIST_HIDDEN; if (pargs.r_opt == oRecipientFile || pargs.r_opt == oHiddenRecipientFile) sl->flags |= PK_LIST_FROM_FILE; any_explicit_recipient = 1; break; case oEncryptTo: case oHiddenEncryptTo: /* Store an additional recipient. */ sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings ); sl->flags = ((pargs.r_opt << PK_LIST_SHIFT) | PK_LIST_ENCRYPT_TO); if (configfp) sl->flags |= PK_LIST_CONFIG; if (pargs.r_opt == oHiddenEncryptTo) sl->flags |= PK_LIST_HIDDEN; break; case oNoEncryptTo: opt.no_encrypt_to = 1; break; case oEncryptToDefaultKey: opt.encrypt_to_default_key = configfp ? 2 : 1; break; case oTrySecretKey: add_to_strlist2 (&opt.secret_keys_to_try, pargs.r.ret_str, utf8_strings); break; case oMimemode: opt.mimemode = opt.textmode = 1; break; case oTextmodeShort: opt.textmode = 2; break; case oTextmode: opt.textmode=1; break; case oNoTextmode: opt.textmode=opt.mimemode=0; break; case oExpert: opt.expert = 1; break; case oNoExpert: opt.expert = 0; break; case oDefSigExpire: if(*pargs.r.ret_str!='\0') { if(parse_expire_string(pargs.r.ret_str)==(u32)-1) log_error(_("'%s' is not a valid signature expiration\n"), pargs.r.ret_str); else opt.def_sig_expire=pargs.r.ret_str; } break; case oAskSigExpire: opt.ask_sig_expire = 1; break; case oNoAskSigExpire: opt.ask_sig_expire = 0; break; case oDefCertExpire: if(*pargs.r.ret_str!='\0') { if(parse_expire_string(pargs.r.ret_str)==(u32)-1) log_error(_("'%s' is not a valid signature expiration\n"), pargs.r.ret_str); else opt.def_cert_expire=pargs.r.ret_str; } break; case oAskCertExpire: opt.ask_cert_expire = 1; break; case oNoAskCertExpire: opt.ask_cert_expire = 0; break; case oDefCertLevel: opt.def_cert_level=pargs.r.ret_int; break; case oMinCertLevel: opt.min_cert_level=pargs.r.ret_int; break; case oAskCertLevel: opt.ask_cert_level = 1; break; case oNoAskCertLevel: opt.ask_cert_level = 0; break; case oLocalUser: /* store the local users */ sl = add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings ); sl->flags = (pargs.r_opt << PK_LIST_SHIFT); if (configfp) sl->flags |= PK_LIST_CONFIG; break; case oSender: { char *mbox = mailbox_from_userid (pargs.r.ret_str, 0); if (!mbox) log_error (_("\"%s\" is not a proper mail address\n"), pargs.r.ret_str); else { add_to_strlist (&opt.sender_list, mbox); xfree (mbox); } } break; case oCompress: /* this is the -z command line option */ opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int; break; case oCompressLevel: opt.compress_level = pargs.r.ret_int; break; case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break; case oBZ2DecompressLowmem: opt.bz2_decompress_lowmem=1; break; case oPassphrase: set_passphrase_from_string(pargs.r.ret_str); break; case oPassphraseFD: pwfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0); break; case oPassphraseFile: pwfd = open_info_file (pargs.r.ret_str, 0, 1); break; case oPassphraseRepeat: opt.passphrase_repeat = pargs.r.ret_int; break; case oPinentryMode: opt.pinentry_mode = parse_pinentry_mode (pargs.r.ret_str); if (opt.pinentry_mode == -1) log_error (_("invalid pinentry mode '%s'\n"), pargs.r.ret_str); break; case oRequestOrigin: opt.request_origin = parse_request_origin (pargs.r.ret_str); if (opt.request_origin == -1) log_error (_("invalid request origin '%s'\n"), pargs.r.ret_str); break; case oCommandFD: opt.command_fd = translate_sys2libc_fd_int (pargs.r.ret_int, 0); if (! gnupg_fd_valid (opt.command_fd)) log_error ("command-fd is invalid: %s\n", strerror (errno)); break; case oCommandFile: opt.command_fd = open_info_file (pargs.r.ret_str, 0, 1); break; case oCipherAlgo: def_cipher_string = xstrdup(pargs.r.ret_str); break; case oAEADAlgo: def_aead_string = xstrdup (pargs.r.ret_str); break; case oDigestAlgo: def_digest_string = xstrdup(pargs.r.ret_str); break; case oCompressAlgo: /* If it is all digits, stick a Z in front of it for later. This is for backwards compatibility with versions that took the compress algorithm number. */ { char *pt=pargs.r.ret_str; while(*pt) { if (!isascii (*pt) || !isdigit (*pt)) break; pt++; } if(*pt=='\0') { compress_algo_string=xmalloc(strlen(pargs.r.ret_str)+2); strcpy(compress_algo_string,"Z"); strcat(compress_algo_string,pargs.r.ret_str); } else compress_algo_string = xstrdup(pargs.r.ret_str); } break; case oCertDigestAlgo: cert_digest_string = xstrdup(pargs.r.ret_str); break; case oNoSecmemWarn: gcry_control (GCRYCTL_DISABLE_SECMEM_WARN); break; case oRequireSecmem: require_secmem=1; break; case oNoRequireSecmem: require_secmem=0; break; case oNoPermissionWarn: opt.no_perm_warn=1; break; case oDisplayCharset: if( set_native_charset( pargs.r.ret_str ) ) log_error(_("'%s' is not a valid character set\n"), pargs.r.ret_str); break; case oNotDashEscaped: opt.not_dash_escaped = 1; break; case oEscapeFrom: opt.escape_from = 1; break; case oNoEscapeFrom: opt.escape_from = 0; break; case oLockOnce: opt.lock_once = 1; break; case oLockNever: dotlock_disable (); break; case oLockMultiple: #ifndef __riscos__ opt.lock_once = 0; #else /* __riscos__ */ riscos_not_implemented("lock-multiple"); #endif /* __riscos__ */ break; case oKeyServer: { keyserver_spec_t keyserver; keyserver = parse_keyserver_uri (pargs.r.ret_str, 0); if (!keyserver) log_error (_("could not parse keyserver URL\n")); else { /* We only support a single keyserver. Later ones override earlier ones. (Since we parse the config file first and then the command line arguments, the command line takes precedence.) */ if (opt.keyserver) free_keyserver_spec (opt.keyserver); opt.keyserver = keyserver; } } break; case oKeyServerOptions: if(!parse_keyserver_options(pargs.r.ret_str)) { if(configname) log_error(_("%s:%d: invalid keyserver options\n"), configname,configlineno); else log_error(_("invalid keyserver options\n")); } break; case oImportOptions: if(!parse_import_options(pargs.r.ret_str,&opt.import_options,1)) { if(configname) log_error(_("%s:%d: invalid import options\n"), configname,configlineno); else log_error(_("invalid import options\n")); } break; case oImportFilter: rc = parse_and_set_import_filter (pargs.r.ret_str); if (rc) log_error (_("invalid filter option: %s\n"), gpg_strerror (rc)); break; case oExportOptions: if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1)) { if(configname) log_error(_("%s:%d: invalid export options\n"), configname,configlineno); else log_error(_("invalid export options\n")); } break; case oExportFilter: rc = parse_and_set_export_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)) { if(configname) log_error(_("%s:%d: invalid list options\n"), configname,configlineno); else log_error(_("invalid list options\n")); } break; case oVerifyOptions: { struct parse_options vopts[]= { {"show-photos",VERIFY_SHOW_PHOTOS,NULL, N_("display photo IDs during signature verification")}, {"show-policy-urls",VERIFY_SHOW_POLICY_URLS,NULL, N_("show policy URLs during signature verification")}, {"show-notations",VERIFY_SHOW_NOTATIONS,NULL, N_("show all notations during signature verification")}, {"show-std-notations",VERIFY_SHOW_STD_NOTATIONS,NULL, N_("show IETF standard notations during signature verification")}, {"show-standard-notations",VERIFY_SHOW_STD_NOTATIONS,NULL, NULL}, {"show-user-notations",VERIFY_SHOW_USER_NOTATIONS,NULL, N_("show user-supplied notations during signature verification")}, {"show-keyserver-urls",VERIFY_SHOW_KEYSERVER_URLS,NULL, N_("show preferred keyserver URLs during signature verification")}, {"show-uid-validity",VERIFY_SHOW_UID_VALIDITY,NULL, N_("show user ID validity during signature verification")}, {"show-unusable-uids",VERIFY_SHOW_UNUSABLE_UIDS,NULL, N_("show revoked and expired user IDs in signature verification")}, {"show-primary-uid-only",VERIFY_SHOW_PRIMARY_UID_ONLY,NULL, N_("show only the primary user ID in signature verification")}, {"pka-lookups",VERIFY_PKA_LOOKUPS,NULL, N_("validate signatures with PKA data")}, {"pka-trust-increase",VERIFY_PKA_TRUST_INCREASE,NULL, N_("elevate the trust of signatures with valid PKA data")}, {NULL,0,NULL,NULL} }; if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts,1)) { if(configname) log_error(_("%s:%d: invalid verify options\n"), configname,configlineno); else log_error(_("invalid verify options\n")); } } break; case oTempDir: opt.temp_dir=pargs.r.ret_str; break; case oExecPath: if(set_exec_path(pargs.r.ret_str)) log_error(_("unable to set exec-path to %s\n"),pargs.r.ret_str); else opt.exec_path_set=1; break; case oSetNotation: add_notation_data( pargs.r.ret_str, 0 ); add_notation_data( pargs.r.ret_str, 1 ); break; case oSigNotation: add_notation_data( pargs.r.ret_str, 0 ); break; case oCertNotation: add_notation_data( pargs.r.ret_str, 1 ); break; case oKnownNotation: register_known_notation (pargs.r.ret_str); break; case oShowNotation: deprecated_warning(configname,configlineno,"--show-notation", "--list-options ","show-notations"); deprecated_warning(configname,configlineno,"--show-notation", "--verify-options ","show-notations"); opt.list_options|=LIST_SHOW_NOTATIONS; opt.verify_options|=VERIFY_SHOW_NOTATIONS; break; case oNoShowNotation: deprecated_warning(configname,configlineno,"--no-show-notation", "--list-options ","no-show-notations"); deprecated_warning(configname,configlineno,"--no-show-notation", "--verify-options ","no-show-notations"); opt.list_options&=~LIST_SHOW_NOTATIONS; opt.verify_options&=~VERIFY_SHOW_NOTATIONS; break; case oUtf8Strings: utf8_strings = 1; break; case oNoUtf8Strings: utf8_strings = 0; break; case oDisableCipherAlgo: { int algo = string_to_cipher_algo (pargs.r.ret_str); gcry_cipher_ctl (NULL, GCRYCTL_DISABLE_ALGO, &algo, sizeof algo); } break; case oDisablePubkeyAlgo: { int algo = gcry_pk_map_name (pargs.r.ret_str); gcry_pk_ctl (GCRYCTL_DISABLE_ALGO, &algo, sizeof algo); } break; case oNoSigCache: opt.no_sig_cache = 1; break; case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break; case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break; case oAllowFreeformUID: opt.allow_freeform_uid = 1; break; case oNoAllowFreeformUID: opt.allow_freeform_uid = 0; break; case oNoLiteral: opt.no_literal = 1; break; case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break; case oFastListMode: opt.fast_list_mode = 1; break; case oFixedListMode: /* Dummy */ break; case oLegacyListMode: opt.legacy_list_mode = 1; break; case oPrintPKARecords: print_pka_records = 1; break; case oPrintDANERecords: print_dane_records = 1; break; case oListOnly: opt.list_only=1; break; case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break; case oIgnoreValidFrom: opt.ignore_valid_from = 1; break; case oIgnoreCrcError: opt.ignore_crc_error = 1; break; case oIgnoreMDCError: opt.ignore_mdc_error = 1; break; case oNoRandomSeedFile: use_random_seed = 0; break; case oAutoKeyRetrieve: opt.keyserver_options.options |= KEYSERVER_AUTO_KEY_RETRIEVE; break; case oNoAutoKeyRetrieve: opt.keyserver_options.options &= ~KEYSERVER_AUTO_KEY_RETRIEVE; break; case oShowSessionKey: opt.show_session_key = 1; break; case oOverrideSessionKey: opt.override_session_key = pargs.r.ret_str; break; case oOverrideSessionKeyFD: ovrseskeyfd = translate_sys2libc_fd_int (pargs.r.ret_int, 0); break; case oMergeOnly: deprecated_warning(configname,configlineno,"--merge-only", "--import-options ","merge-only"); opt.import_options|=IMPORT_MERGE_ONLY; break; case oAllowSecretKeyImport: /* obsolete */ break; case oTryAllSecrets: opt.try_all_secrets = 1; break; case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break; case oEnableSpecialFilenames: enable_special_filenames (); break; case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break; case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break; case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break; case oPreservePermissions: opt.preserve_permissions=1; break; case oDefaultPreferenceList: opt.def_preference_list = pargs.r.ret_str; break; case oDefaultKeyserverURL: { keyserver_spec_t keyserver; keyserver = parse_keyserver_uri (pargs.r.ret_str,1 ); if (!keyserver) log_error (_("could not parse keyserver URL\n")); else free_keyserver_spec (keyserver); opt.def_keyserver_url = pargs.r.ret_str; } break; case oPersonalCipherPreferences: pers_cipher_list=pargs.r.ret_str; break; case oPersonalAEADPreferences: pers_aead_list = pargs.r.ret_str; break; case oPersonalDigestPreferences: pers_digest_list=pargs.r.ret_str; break; case oPersonalCompressPreferences: pers_compress_list=pargs.r.ret_str; break; case oAgentProgram: opt.agent_program = pargs.r.ret_str; break; + case oKeyboxdProgram: opt.keyboxd_program = pargs.r.ret_str; break; case oDirmngrProgram: opt.dirmngr_program = pargs.r.ret_str; break; case oDisableDirmngr: opt.disable_dirmngr = 1; break; case oWeakDigest: additional_weak_digest(pargs.r.ret_str); break; case oUnwrap: opt.unwrap_encryption = 1; break; case oOnlySignTextIDs: opt.only_sign_text_ids = 1; break; case oDisplay: set_opt_session_env ("DISPLAY", pargs.r.ret_str); break; case oTTYname: set_opt_session_env ("GPG_TTY", pargs.r.ret_str); break; case oTTYtype: set_opt_session_env ("TERM", pargs.r.ret_str); break; case oXauthority: set_opt_session_env ("XAUTHORITY", pargs.r.ret_str); break; case oLCctype: opt.lc_ctype = pargs.r.ret_str; break; case oLCmessages: opt.lc_messages = pargs.r.ret_str; break; case oGroup: add_group(pargs.r.ret_str); break; case oUnGroup: rm_group(pargs.r.ret_str); break; case oNoGroups: while(opt.grouplist) { struct groupitem *iter=opt.grouplist; free_strlist(iter->values); opt.grouplist=opt.grouplist->next; xfree(iter); } break; case oStrict: case oNoStrict: /* Not used */ break; case oMangleDosFilenames: opt.mangle_dos_filenames = 1; break; case oNoMangleDosFilenames: opt.mangle_dos_filenames = 0; break; case oEnableProgressFilter: opt.enable_progress_filter = 1; break; case oMultifile: multifile=1; break; case oKeyidFormat: if(ascii_strcasecmp(pargs.r.ret_str,"short")==0) opt.keyid_format=KF_SHORT; else if(ascii_strcasecmp(pargs.r.ret_str,"long")==0) opt.keyid_format=KF_LONG; else if(ascii_strcasecmp(pargs.r.ret_str,"0xshort")==0) opt.keyid_format=KF_0xSHORT; else if(ascii_strcasecmp(pargs.r.ret_str,"0xlong")==0) opt.keyid_format=KF_0xLONG; else if(ascii_strcasecmp(pargs.r.ret_str,"none")==0) opt.keyid_format = KF_NONE; else log_error("unknown keyid-format '%s'\n",pargs.r.ret_str); break; case oExitOnStatusWriteError: opt.exit_on_status_write_error = 1; break; case oLimitCardInsertTries: opt.limit_card_insert_tries = pargs.r.ret_int; break; case oRequireCrossCert: opt.flags.require_cross_cert=1; break; case oNoRequireCrossCert: opt.flags.require_cross_cert=0; break; case oAutoKeyLocate: if (default_akl) { /* This is the first time --auto-key-locate is seen. * We need to reset the default akl. */ default_akl = 0; release_akl(); } if(!parse_auto_key_locate(pargs.r.ret_str)) { if(configname) log_error(_("%s:%d: invalid auto-key-locate list\n"), configname,configlineno); else log_error(_("invalid auto-key-locate list\n")); } break; case oNoAutoKeyLocate: release_akl(); break; case oKeyOrigin: if(!parse_key_origin (pargs.r.ret_str)) log_error (_("invalid argument for option \"%.50s\"\n"), "--key-origin"); break; case oEnableLargeRSA: #if SECMEM_BUFFER_SIZE >= 65536 opt.flags.large_rsa=1; #else if (configname) log_info("%s:%d: WARNING: gpg not built with large secure " "memory buffer. Ignoring enable-large-rsa\n", configname,configlineno); else log_info("WARNING: gpg not built with large secure " "memory buffer. Ignoring --enable-large-rsa\n"); #endif /* SECMEM_BUFFER_SIZE >= 65536 */ break; case oDisableLargeRSA: opt.flags.large_rsa=0; break; case oEnableDSA2: opt.flags.dsa2=1; break; case oDisableDSA2: opt.flags.dsa2=0; break; case oAllowWeakDigestAlgos: opt.flags.allow_weak_digest_algos = 1; break; case oFakedSystemTime: { size_t len = strlen (pargs.r.ret_str); int freeze = 0; time_t faked_time; if (len > 0 && pargs.r.ret_str[len-1] == '!') { freeze = 1; pargs.r.ret_str[len-1] = '\0'; } faked_time = isotime2epoch (pargs.r.ret_str); if (faked_time == (time_t)(-1)) faked_time = (time_t)strtoul (pargs.r.ret_str, NULL, 10); gnupg_set_time (faked_time, freeze); } break; case oNoAutostart: opt.autostart = 0; break; case oNoSymkeyCache: opt.no_symkey_cache = 1; break; case oDefaultNewKeyAlgo: opt.def_new_key_algo = pargs.r.ret_str; break; case oNoop: break; default: if (configfp) pargs.err = ARGPARSE_PRINT_WARNING; else { pargs.err = ARGPARSE_PRINT_ERROR; /* The argparse function calls a plain exit and thus * we need to print a status here. */ write_status_failure ("option-parser", gpg_error(GPG_ERR_GENERAL)); } break; } } if (configfp) { fclose( configfp ); configfp = NULL; /* Remember the first config file name. */ if (!save_configname) save_configname = configname; else xfree(configname); configname = NULL; goto next_pass; } xfree(configname); configname = NULL; if (log_get_errorcount (0)) { write_status_failure ("option-parser", gpg_error(GPG_ERR_GENERAL)); g10_exit(2); } /* The command --gpgconf-list is pretty simple and may be called directly after the option parsing. */ if (cmd == aGPGConfList) { gpgconf_list (save_configname ? save_configname : default_configname); g10_exit (0); } xfree (save_configname); xfree (default_configname); if (print_dane_records) log_error ("invalid option \"%s\"; use \"%s\" instead\n", "--print-dane-records", "--export-options export-dane"); if (print_pka_records) log_error ("invalid option \"%s\"; use \"%s\" instead\n", "--print-pks-records", "--export-options export-pka"); if (log_get_errorcount (0)) { write_status_failure ("option-checking", gpg_error(GPG_ERR_GENERAL)); g10_exit(2); } if( nogreeting ) greeting = 0; if( greeting ) { es_fprintf (es_stderr, "%s %s; %s\n", strusage(11), strusage(13), strusage(14) ); es_fprintf (es_stderr, "%s\n", strusage(15) ); } #ifdef IS_DEVELOPMENT_VERSION if (!opt.batch) { const char *s; if((s=strusage(25))) log_info("%s\n",s); if((s=strusage(26))) log_info("%s\n",s); if((s=strusage(27))) log_info("%s\n",s); } #endif /* FIXME: We should use logging to a file only in server mode; however we have not yet implemetyed that. Thus we try to get away with --batch as indication for logging to file required. */ if (logfile && opt.batch) { log_set_file (logfile); log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID); } if (opt.verbose > 2) log_info ("using character set '%s'\n", get_native_charset ()); if( may_coredump && !opt.quiet ) log_info(_("WARNING: program may create a core file!\n")); if (opt.flags.rfc4880bis) log_info ("Note: RFC4880bis features are enabled.\n"); else { opt.mimemode = 0; /* This will use text mode instead. */ } if (eyes_only) { if (opt.set_filename) log_info(_("WARNING: %s overrides %s\n"), "--for-your-eyes-only","--set-filename"); opt.set_filename="_CONSOLE"; } if (opt.no_literal) { log_info(_("Note: %s is not for normal use!\n"), "--no-literal"); if (opt.textmode) log_error(_("%s not allowed with %s!\n"), "--textmode", "--no-literal" ); if (opt.set_filename) log_error(_("%s makes no sense with %s!\n"), eyes_only?"--for-your-eyes-only":"--set-filename", "--no-literal" ); } if (opt.set_filesize) log_info(_("Note: %s is not for normal use!\n"), "--set-filesize"); if( opt.batch ) tty_batchmode( 1 ); if (gnupg_faked_time_p ()) { gnupg_isotime_t tbuf; log_info (_("WARNING: running with faked system time: ")); gnupg_get_isotime (tbuf); dump_isotime (tbuf); log_printf ("\n"); } /* Print a warning if an argument looks like an option. */ if (!opt.quiet && !(pargs.flags & ARGPARSE_FLAG_STOP_SEEN)) { int i; for (i=0; i < argc; i++) if (argv[i][0] == '-' && argv[i][1] == '-') log_info (_("Note: '%s' is not considered an option\n"), argv[i]); } gcry_control (GCRYCTL_RESUME_SECMEM_WARN); if(require_secmem && !got_secmem) { log_info(_("will not run with insecure memory due to %s\n"), "--require-secmem"); write_status_failure ("option-checking", gpg_error(GPG_ERR_GENERAL)); g10_exit(2); } set_debug (debug_level); if (DBG_CLOCK) log_clock ("start"); /* Do these after the switch(), so they can override settings. */ if (PGP7) { /* That does not anymore work because we have no more support for v3 signatures. */ opt.escape_from=1; opt.ask_sig_expire=0; } else if(PGP8) { opt.escape_from=1; } if( def_cipher_string ) { opt.def_cipher_algo = string_to_cipher_algo (def_cipher_string); xfree(def_cipher_string); def_cipher_string = NULL; if ( openpgp_cipher_test_algo (opt.def_cipher_algo) ) log_error(_("selected cipher algorithm is invalid\n")); } if (def_aead_string) { opt.def_aead_algo = string_to_aead_algo (def_aead_string); xfree (def_aead_string); def_aead_string = NULL; if (openpgp_aead_test_algo (opt.def_aead_algo)) log_error(_("selected AEAD algorithm is invalid\n")); } if( def_digest_string ) { opt.def_digest_algo = string_to_digest_algo (def_digest_string); xfree(def_digest_string); def_digest_string = NULL; if ( openpgp_md_test_algo (opt.def_digest_algo) ) log_error(_("selected digest algorithm is invalid\n")); } if( compress_algo_string ) { opt.compress_algo = string_to_compress_algo(compress_algo_string); xfree(compress_algo_string); compress_algo_string = NULL; if( check_compress_algo(opt.compress_algo) ) log_error(_("selected compression algorithm is invalid\n")); } if( cert_digest_string ) { opt.cert_digest_algo = string_to_digest_algo (cert_digest_string); xfree(cert_digest_string); cert_digest_string = NULL; if (openpgp_md_test_algo(opt.cert_digest_algo)) log_error(_("selected certification digest algorithm is invalid\n")); } if( s2k_cipher_string ) { opt.s2k_cipher_algo = string_to_cipher_algo (s2k_cipher_string); xfree(s2k_cipher_string); s2k_cipher_string = NULL; if (openpgp_cipher_test_algo (opt.s2k_cipher_algo)) log_error(_("selected cipher algorithm is invalid\n")); } if( s2k_digest_string ) { opt.s2k_digest_algo = string_to_digest_algo (s2k_digest_string); xfree(s2k_digest_string); s2k_digest_string = NULL; if (openpgp_md_test_algo(opt.s2k_digest_algo)) log_error(_("selected digest algorithm is invalid\n")); } if( opt.completes_needed < 1 ) log_error(_("completes-needed must be greater than 0\n")); if( opt.marginals_needed < 2 ) log_error(_("marginals-needed must be greater than 1\n")); if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 ) log_error(_("max-cert-depth must be in the range from 1 to 255\n")); if(opt.def_cert_level<0 || opt.def_cert_level>3) log_error(_("invalid default-cert-level; must be 0, 1, 2, or 3\n")); if( opt.min_cert_level < 1 || opt.min_cert_level > 3 ) log_error(_("invalid min-cert-level; must be 1, 2, or 3\n")); switch( opt.s2k_mode ) { case 0: log_info(_("Note: simple S2K mode (0) is strongly discouraged\n")); break; case 1: case 3: break; default: log_error(_("invalid S2K mode; must be 0, 1 or 3\n")); } /* This isn't actually needed, but does serve to error out if the string is invalid. */ if(opt.def_preference_list && keygen_set_std_prefs(opt.def_preference_list,0)) log_error(_("invalid default preferences\n")); if(pers_cipher_list && keygen_set_std_prefs(pers_cipher_list,PREFTYPE_SYM)) log_error(_("invalid personal cipher preferences\n")); if (pers_aead_list && keygen_set_std_prefs (pers_aead_list, PREFTYPE_AEAD)) log_error(_("invalid personal AEAD preferences\n")); if(pers_digest_list && keygen_set_std_prefs(pers_digest_list,PREFTYPE_HASH)) log_error(_("invalid personal digest preferences\n")); if(pers_compress_list && keygen_set_std_prefs(pers_compress_list,PREFTYPE_ZIP)) log_error(_("invalid personal compress preferences\n")); /* Check chunk size. Please fix also the man page if you change * the default. The limits are given by the specs. */ if (!opt.chunk_size) opt.chunk_size = 27; /* Default to the suggested max of 128 MiB. */ else if (opt.chunk_size < 6) { opt.chunk_size = 6; log_info (_("chunk size invalid - using %d\n"), opt.chunk_size); } else if (opt.chunk_size > (allow_large_chunks? 62 : 27)) { opt.chunk_size = (allow_large_chunks? 62 : 27); log_info (_("chunk size invalid - using %d\n"), opt.chunk_size); } /* We don't support all possible commands with multifile yet */ if(multifile) { char *cmdname; switch(cmd) { case aSign: cmdname="--sign"; break; case aSignEncr: cmdname="--sign --encrypt"; break; case aClearsign: cmdname="--clear-sign"; break; case aDetachedSign: cmdname="--detach-sign"; break; case aSym: cmdname="--symmetric"; break; case aEncrSym: cmdname="--symmetric --encrypt"; break; case aStore: cmdname="--store"; break; default: cmdname=NULL; break; } if(cmdname) log_error(_("%s does not yet work with %s\n"),cmdname,"--multifile"); } if( log_get_errorcount(0) ) { write_status_failure ("option-postprocessing", gpg_error(GPG_ERR_GENERAL)); g10_exit (2); } if(opt.compress_level==0) opt.compress_algo=COMPRESS_ALGO_NONE; /* Check our chosen algorithms against the list of legal algorithms. */ if(!GNUPG && !opt.flags.rfc4880bis) { const char *badalg=NULL; preftype_t badtype=PREFTYPE_NONE; if(opt.def_cipher_algo && !algo_available(PREFTYPE_SYM,opt.def_cipher_algo,NULL)) { badalg = openpgp_cipher_algo_name (opt.def_cipher_algo); badtype = PREFTYPE_SYM; } else if(opt.def_aead_algo && !algo_available(PREFTYPE_AEAD, opt.def_aead_algo, NULL)) { badalg = openpgp_aead_algo_name (opt.def_aead_algo); badtype = PREFTYPE_AEAD; } else if(opt.def_digest_algo && !algo_available(PREFTYPE_HASH,opt.def_digest_algo,NULL)) { badalg = gcry_md_algo_name (opt.def_digest_algo); badtype = PREFTYPE_HASH; } else if(opt.cert_digest_algo && !algo_available(PREFTYPE_HASH,opt.cert_digest_algo,NULL)) { badalg = gcry_md_algo_name (opt.cert_digest_algo); badtype = PREFTYPE_HASH; } else if(opt.compress_algo!=-1 && !algo_available(PREFTYPE_ZIP,opt.compress_algo,NULL)) { badalg = compress_algo_to_string(opt.compress_algo); badtype = PREFTYPE_ZIP; } if(badalg) { switch(badtype) { case PREFTYPE_SYM: log_info (_("cipher algorithm '%s'" " may not be used in %s mode\n"), badalg, gnupg_compliance_option_string (opt.compliance)); break; case PREFTYPE_AEAD: log_info (_("AEAD algorithm '%s'" " may not be used in %s mode\n"), badalg, gnupg_compliance_option_string (opt.compliance)); break; case PREFTYPE_HASH: log_info (_("digest algorithm '%s'" " may not be used in %s mode\n"), badalg, gnupg_compliance_option_string (opt.compliance)); break; case PREFTYPE_ZIP: log_info (_("compression algorithm '%s'" " may not be used in %s mode\n"), badalg, gnupg_compliance_option_string (opt.compliance)); break; default: BUG(); } compliance_failure(); } } /* Check our chosen algorithms against the list of allowed * algorithms in the current compliance mode, and fail hard if it * is not. This is us being nice to the user informing her early * that the chosen algorithms are not available. We also check * and enforce this right before the actual operation. */ /* FIXME: We also need to check the AEAD algo. */ if (opt.def_cipher_algo && ! gnupg_cipher_is_allowed (opt.compliance, cmd == aEncr || cmd == aSignEncr || cmd == aEncrSym || cmd == aSym || cmd == aSignSym || cmd == aSignEncrSym, opt.def_cipher_algo, GCRY_CIPHER_MODE_NONE)) log_error (_("cipher algorithm '%s' may not be used in %s mode\n"), openpgp_cipher_algo_name (opt.def_cipher_algo), gnupg_compliance_option_string (opt.compliance)); if (opt.def_digest_algo && ! gnupg_digest_is_allowed (opt.compliance, cmd == aSign || cmd == aSignEncr || cmd == aSignEncrSym || cmd == aSignSym || cmd == aClearsign, opt.def_digest_algo)) log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (opt.def_digest_algo), gnupg_compliance_option_string (opt.compliance)); /* Fail hard. */ if (log_get_errorcount (0)) { write_status_failure ("option-checking", gpg_error(GPG_ERR_GENERAL)); g10_exit (2); } /* Set the random seed file. */ if( use_random_seed ) { char *p = make_filename (gnupg_homedir (), "random_seed", NULL ); gcry_control (GCRYCTL_SET_RANDOM_SEED_FILE, p); if (!access (p, F_OK)) register_secured_file (p); xfree(p); } /* If there is no command but the --fingerprint is given, default to the --list-keys command. */ if (!cmd && fpr_maybe_cmd) { set_cmd (&cmd, aListKeys); } if( opt.verbose > 1 ) set_packet_list_mode(1); /* Add the keyrings, but not for some special commands. We always * need to add the keyrings if we are running under SELinux, this * is so that the rings are added to the list of secured files. - * We do not add any keyring if --no-keyring has been used. */ - if (default_keyring >= 0 + * We do not add any keyring if --no-keyring or --use-keyboxd has + * been used. */ + if (!opt.use_keyboxd + && default_keyring >= 0 && (ALWAYS_ADD_KEYRINGS || (cmd != aDeArmor && cmd != aEnArmor && cmd != aGPGConfTest))) { if (!nrings || default_keyring > 0) /* Add default ring. */ keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG, KEYDB_RESOURCE_FLAG_DEFAULT); for (sl = nrings; sl; sl = sl->next ) keydb_add_resource (sl->d, sl->flags); } FREE_STRLIST(nrings); + /* In loopback mode, never ask for the password multiple times. */ if (opt.pinentry_mode == PINENTRY_MODE_LOOPBACK) - /* In loopback mode, never ask for the password multiple - times. */ { opt.passphrase_repeat = 0; } if (cmd == aGPGConfTest) g10_exit(0); if (pwfd != -1) /* Read the passphrase now. */ read_passphrase_from_fd (pwfd); if (ovrseskeyfd != -1 ) /* Read the sessionkey now. */ read_sessionkey_from_fd (ovrseskeyfd); fname = argc? *argv : NULL; if(fname && utf8_strings) opt.flags.utf8_filename=1; ctrl = xcalloc (1, sizeof *ctrl); gpg_init_default_ctrl (ctrl); #ifndef NO_TRUST_MODELS switch (cmd) { case aPrimegen: case aPrintMD: case aPrintMDs: case aGenRandom: case aDeArmor: case aEnArmor: case aListConfig: case aListGcryptConfig: break; case aFixTrustDB: case aExportOwnerTrust: rc = setup_trustdb (0, trustdb_name); break; case aListTrustDB: rc = setup_trustdb (argc? 1:0, trustdb_name); break; case aKeygen: case aFullKeygen: case aQuickKeygen: rc = setup_trustdb (1, trustdb_name); break; default: /* If we are using TM_ALWAYS, we do not need to create the trustdb. */ rc = setup_trustdb (opt.trust_model != TM_ALWAYS, trustdb_name); break; } if (rc) log_error (_("failed to initialize the TrustDB: %s\n"), gpg_strerror (rc)); #endif /*!NO_TRUST_MODELS*/ switch (cmd) { case aStore: case aSym: case aSign: case aSignSym: case aClearsign: if (!opt.quiet && any_explicit_recipient) log_info (_("WARNING: recipients (-r) given " "without using public key encryption\n")); break; default: break; } /* Check for certain command whether we need to migrate a secring.gpg to the gpg-agent. */ switch (cmd) { case aListSecretKeys: case aSign: case aSignEncr: case aSignEncrSym: case aSignSym: case aClearsign: case aDecrypt: case aSignKey: case aLSignKey: case aEditKey: case aPasswd: case aDeleteSecretKeys: case aDeleteSecretAndPublicKeys: case aQuickKeygen: case aQuickAddUid: case aQuickAddKey: case aQuickRevUid: case aQuickSetPrimaryUid: case aFullKeygen: case aKeygen: case aImport: case aExportSecret: case aExportSecretSub: case aGenRevoke: case aDesigRevoke: case aCardEdit: case aChangePIN: migrate_secring (ctrl); break; case aListKeys: if (opt.with_secret) migrate_secring (ctrl); break; default: break; } /* The command dispatcher. */ switch( cmd ) { case aServer: gpg_server (ctrl); break; case aStore: /* only store the file */ if( argc > 1 ) wrong_args("--store [filename]"); if( (rc = encrypt_store(fname)) ) { write_status_failure ("store", rc); log_error ("storing '%s' failed: %s\n", print_fname_stdin(fname),gpg_strerror (rc) ); } break; case aSym: /* encrypt the given file only with the symmetric cipher */ if( argc > 1 ) wrong_args("--symmetric [filename]"); if( (rc = encrypt_symmetric(fname)) ) { write_status_failure ("symencrypt", rc); log_error (_("symmetric encryption of '%s' failed: %s\n"), print_fname_stdin(fname),gpg_strerror (rc) ); } break; case aEncr: /* encrypt the given file */ if(multifile) encrypt_crypt_files (ctrl, argc, argv, remusr); else { if( argc > 1 ) wrong_args("--encrypt [filename]"); if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 0, NULL, -1)) ) { write_status_failure ("encrypt", rc); log_error("%s: encryption failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) ); } } break; case aEncrSym: /* This works with PGP 8 in the sense that it acts just like a symmetric message. It doesn't work at all with 2 or 6. It might work with 7, but alas, I don't have a copy to test with right now. */ if( argc > 1 ) wrong_args("--symmetric --encrypt [filename]"); else if(opt.s2k_mode==0) log_error(_("you cannot use --symmetric --encrypt" " with --s2k-mode 0\n")); else if (PGP7) log_error(_("you cannot use --symmetric --encrypt" " in %s mode\n"), gnupg_compliance_option_string (opt.compliance)); else { if( (rc = encrypt_crypt (ctrl, -1, fname, remusr, 1, NULL, -1)) ) { write_status_failure ("encrypt", rc); log_error ("%s: encryption failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) ); } } break; case aSign: /* sign the given file */ sl = NULL; if( detached_sig ) { /* sign all files */ for( ; argc; argc--, argv++ ) add_to_strlist( &sl, *argv ); } else { if( argc > 1 ) wrong_args("--sign [filename]"); if( argc ) { sl = xmalloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } } if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 0, NULL, NULL))) { write_status_failure ("sign", rc); log_error ("signing failed: %s\n", gpg_strerror (rc) ); } free_strlist(sl); break; case aSignEncr: /* sign and encrypt the given file */ if( argc > 1 ) wrong_args("--sign --encrypt [filename]"); if( argc ) { sl = xmalloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } else sl = NULL; if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 1, remusr, NULL))) { write_status_failure ("sign-encrypt", rc); log_error("%s: sign+encrypt failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) ); } free_strlist(sl); break; case aSignEncrSym: /* sign and encrypt the given file */ if( argc > 1 ) wrong_args("--symmetric --sign --encrypt [filename]"); else if(opt.s2k_mode==0) log_error(_("you cannot use --symmetric --sign --encrypt" " with --s2k-mode 0\n")); else if (PGP7) log_error(_("you cannot use --symmetric --sign --encrypt" " in %s mode\n"), gnupg_compliance_option_string (opt.compliance)); else { if( argc ) { sl = xmalloc_clear( sizeof *sl + strlen(fname)); strcpy(sl->d, fname); } else sl = NULL; if ((rc = sign_file (ctrl, sl, detached_sig, locusr, 2, remusr, NULL))) { write_status_failure ("sign-encrypt", rc); log_error("%s: symmetric+sign+encrypt failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) ); } free_strlist(sl); } break; case aSignSym: /* sign and conventionally encrypt the given file */ if (argc > 1) wrong_args("--sign --symmetric [filename]"); rc = sign_symencrypt_file (ctrl, fname, locusr); if (rc) { write_status_failure ("sign-symencrypt", rc); log_error("%s: sign+symmetric failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) ); } break; case aClearsign: /* make a clearsig */ if( argc > 1 ) wrong_args("--clear-sign [filename]"); if( (rc = clearsign_file (ctrl, fname, locusr, NULL)) ) { write_status_failure ("sign", rc); log_error("%s: clear-sign failed: %s\n", print_fname_stdin(fname), gpg_strerror (rc) ); } break; case aVerify: if (multifile) { if ((rc = verify_files (ctrl, argc, argv))) log_error("verify files failed: %s\n", gpg_strerror (rc) ); } else { if ((rc = verify_signatures (ctrl, argc, argv))) log_error("verify signatures failed: %s\n", gpg_strerror (rc) ); } if (rc) write_status_failure ("verify", rc); break; case aDecrypt: if (multifile) decrypt_messages (ctrl, argc, argv); else { if( argc > 1 ) wrong_args("--decrypt [filename]"); if( (rc = decrypt_message (ctrl, fname) )) { write_status_failure ("decrypt", rc); log_error("decrypt_message failed: %s\n", gpg_strerror (rc) ); } } break; case aQuickSignKey: case aQuickLSignKey: { const char *fpr; if (argc < 1) wrong_args ("--quick-[l]sign-key fingerprint [userids]"); fpr = *argv++; argc--; sl = NULL; for( ; argc; argc--, argv++) append_to_strlist2 (&sl, *argv, utf8_strings); keyedit_quick_sign (ctrl, fpr, sl, locusr, (cmd == aQuickLSignKey)); free_strlist (sl); } break; case aSignKey: if( argc != 1 ) wrong_args("--sign-key user-id"); /* fall through */ case aLSignKey: if( argc != 1 ) wrong_args("--lsign-key user-id"); /* fall through */ sl=NULL; if(cmd==aSignKey) append_to_strlist(&sl,"sign"); else if(cmd==aLSignKey) append_to_strlist(&sl,"lsign"); else BUG(); append_to_strlist( &sl, "save" ); username = make_username( fname ); keyedit_menu (ctrl, username, locusr, sl, 0, 0 ); xfree(username); free_strlist(sl); break; case aEditKey: /* Edit a key signature */ if( !argc ) wrong_args("--edit-key user-id [commands]"); username = make_username( fname ); if( argc > 1 ) { sl = NULL; for( argc--, argv++ ; argc; argc--, argv++ ) append_to_strlist( &sl, *argv ); keyedit_menu (ctrl, username, locusr, sl, 0, 1 ); free_strlist(sl); } else keyedit_menu (ctrl, username, locusr, NULL, 0, 1 ); xfree(username); break; case aPasswd: if (argc != 1) wrong_args("--change-passphrase "); else { username = make_username (fname); keyedit_passwd (ctrl, username); xfree (username); } break; case aDeleteKeys: case aDeleteSecretKeys: case aDeleteSecretAndPublicKeys: sl = NULL; /* I'm adding these in reverse order as add_to_strlist2 reverses them again, and it's easier to understand in the proper order :) */ for( ; argc; argc-- ) add_to_strlist2( &sl, argv[argc-1], utf8_strings ); delete_keys (ctrl, sl, cmd==aDeleteSecretKeys, cmd==aDeleteSecretAndPublicKeys); free_strlist(sl); break; case aCheckKeys: opt.check_sigs = 1; /* fall through */ case aListSigs: opt.list_sigs = 1; /* fall through */ case aListKeys: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist2( &sl, *argv, utf8_strings ); public_key_list (ctrl, sl, 0, 0); free_strlist(sl); break; case aListSecretKeys: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist2( &sl, *argv, utf8_strings ); secret_key_list (ctrl, sl); free_strlist(sl); break; case aLocateKeys: case aLocateExtKeys: sl = NULL; for (; argc; argc--, argv++) add_to_strlist2( &sl, *argv, utf8_strings ); public_key_list (ctrl, sl, 1, cmd == aLocateExtKeys); free_strlist (sl); break; case aQuickKeygen: { const char *x_algo, *x_usage, *x_expire; if (argc < 1 || argc > 4) wrong_args("--quick-generate-key USER-ID [ALGO [USAGE [EXPIRE]]]"); username = make_username (fname); argv++, argc--; x_algo = ""; x_usage = ""; x_expire = ""; if (argc) { x_algo = *argv++; argc--; if (argc) { x_usage = *argv++; argc--; if (argc) { x_expire = *argv++; argc--; } } } quick_generate_keypair (ctrl, username, x_algo, x_usage, x_expire); xfree (username); } break; case aKeygen: /* generate a key */ if( opt.batch ) { if( argc > 1 ) wrong_args("--generate-key [parameterfile]"); generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0); } else { if (opt.command_fd != -1 && argc) { if( argc > 1 ) wrong_args("--generate-key [parameterfile]"); opt.batch = 1; generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0); } else if (argc) wrong_args ("--generate-key"); else generate_keypair (ctrl, 0, NULL, NULL, 0); } break; case aFullKeygen: /* Generate a key with all options. */ if (opt.batch) { if (argc > 1) wrong_args ("--full-generate-key [parameterfile]"); generate_keypair (ctrl, 1, argc? *argv : NULL, NULL, 0); } else { if (argc) wrong_args("--full-generate-key"); generate_keypair (ctrl, 1, NULL, NULL, 0); } break; case aQuickAddUid: { const char *uid, *newuid; if (argc != 2) wrong_args ("--quick-add-uid USER-ID NEW-USER-ID"); uid = *argv++; argc--; newuid = *argv++; argc--; keyedit_quick_adduid (ctrl, uid, newuid); } break; case aQuickAddKey: { const char *x_fpr, *x_algo, *x_usage, *x_expire; if (argc < 1 || argc > 4) wrong_args ("--quick-add-key FINGERPRINT [ALGO [USAGE [EXPIRE]]]"); x_fpr = *argv++; argc--; x_algo = ""; x_usage = ""; x_expire = ""; if (argc) { x_algo = *argv++; argc--; if (argc) { x_usage = *argv++; argc--; if (argc) { x_expire = *argv++; argc--; } } } keyedit_quick_addkey (ctrl, x_fpr, x_algo, x_usage, x_expire); } break; case aQuickRevUid: { const char *uid, *uidtorev; if (argc != 2) wrong_args ("--quick-revoke-uid USER-ID USER-ID-TO-REVOKE"); uid = *argv++; argc--; uidtorev = *argv++; argc--; keyedit_quick_revuid (ctrl, uid, uidtorev); } break; case aQuickSetExpire: { const char *x_fpr, *x_expire; if (argc < 2) wrong_args ("--quick-set-exipre FINGERPRINT EXPIRE [SUBKEY-FPRS]"); x_fpr = *argv++; argc--; x_expire = *argv++; argc--; keyedit_quick_set_expire (ctrl, x_fpr, x_expire, argv); } break; case aQuickSetPrimaryUid: { const char *uid, *primaryuid; if (argc != 2) wrong_args ("--quick-set-primary-uid USER-ID PRIMARY-USER-ID"); uid = *argv++; argc--; primaryuid = *argv++; argc--; keyedit_quick_set_primary (ctrl, uid, primaryuid); } break; case aFastImport: opt.import_options |= IMPORT_FAST; /* fall through */ case aImport: case aShowKeys: import_keys (ctrl, argc? argv:NULL, argc, NULL, opt.import_options, opt.key_origin, opt.key_origin_url); break; /* TODO: There are a number of command that use this same "make strlist, call function, report error, free strlist" pattern. Join them together here and avoid all that duplicated code. */ case aExport: case aSendKeys: case aRecvKeys: sl = NULL; for( ; argc; argc--, argv++ ) append_to_strlist2( &sl, *argv, utf8_strings ); if( cmd == aSendKeys ) rc = keyserver_export (ctrl, sl ); else if( cmd == aRecvKeys ) rc = keyserver_import (ctrl, sl ); else { export_stats_t stats = export_new_stats (); rc = export_pubkeys (ctrl, sl, opt.export_options, stats); export_print_stats (stats); export_release_stats (stats); } if(rc) { if(cmd==aSendKeys) { write_status_failure ("send-keys", rc); log_error(_("keyserver send failed: %s\n"),gpg_strerror (rc)); } else if(cmd==aRecvKeys) { write_status_failure ("recv-keys", rc); log_error (_("keyserver receive failed: %s\n"), gpg_strerror (rc)); } else { write_status_failure ("export", rc); log_error (_("key export failed: %s\n"), gpg_strerror (rc)); } } free_strlist(sl); break; case aExportSshKey: if (argc != 1) wrong_args ("--export-ssh-key "); rc = export_ssh_key (ctrl, argv[0]); if (rc) { write_status_failure ("export-ssh-key", rc); log_error (_("export as ssh key failed: %s\n"), gpg_strerror (rc)); } break; case aSearchKeys: sl = NULL; for (; argc; argc--, argv++) append_to_strlist2 (&sl, *argv, utf8_strings); rc = keyserver_search (ctrl, sl); if (rc) { write_status_failure ("search-keys", rc); log_error (_("keyserver search failed: %s\n"), gpg_strerror (rc)); } free_strlist (sl); break; case aRefreshKeys: sl = NULL; for( ; argc; argc--, argv++ ) append_to_strlist2( &sl, *argv, utf8_strings ); rc = keyserver_refresh (ctrl, sl); if(rc) { write_status_failure ("refresh-keys", rc); log_error (_("keyserver refresh failed: %s\n"),gpg_strerror (rc)); } free_strlist(sl); break; case aFetchKeys: sl = NULL; for( ; argc; argc--, argv++ ) append_to_strlist2( &sl, *argv, utf8_strings ); rc = keyserver_fetch (ctrl, sl, opt.key_origin); if(rc) { write_status_failure ("fetch-keys", rc); log_error ("key fetch failed: %s\n",gpg_strerror (rc)); } free_strlist(sl); break; case aExportSecret: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist2( &sl, *argv, utf8_strings ); { export_stats_t stats = export_new_stats (); export_seckeys (ctrl, sl, opt.export_options, stats); export_print_stats (stats); export_release_stats (stats); } free_strlist(sl); break; case aExportSecretSub: sl = NULL; for( ; argc; argc--, argv++ ) add_to_strlist2( &sl, *argv, utf8_strings ); { export_stats_t stats = export_new_stats (); export_secsubkeys (ctrl, sl, opt.export_options, stats); export_print_stats (stats); export_release_stats (stats); } free_strlist(sl); break; case aGenRevoke: if( argc != 1 ) wrong_args("--generate-revocation user-id"); username = make_username(*argv); gen_revoke (ctrl, username ); xfree( username ); break; case aDesigRevoke: if (argc != 1) wrong_args ("--generate-designated-revocation user-id"); username = make_username (*argv); gen_desig_revoke (ctrl, username, locusr); xfree (username); break; case aDeArmor: if( argc > 1 ) wrong_args("--dearmor [file]"); rc = dearmor_file( argc? *argv: NULL ); if( rc ) { write_status_failure ("dearmor", rc); log_error (_("dearmoring failed: %s\n"), gpg_strerror (rc)); } break; case aEnArmor: if( argc > 1 ) wrong_args("--enarmor [file]"); rc = enarmor_file( argc? *argv: NULL ); if( rc ) { write_status_failure ("enarmor", rc); log_error (_("enarmoring failed: %s\n"), gpg_strerror (rc)); } break; case aPrimegen: #if 0 /*FIXME*/ { int mode = argc < 2 ? 0 : atoi(*argv); if( mode == 1 && argc == 2 ) { mpi_print (es_stdout, generate_public_prime( atoi(argv[1]) ), 1); } else if( mode == 2 && argc == 3 ) { mpi_print (es_stdout, generate_elg_prime( 0, atoi(argv[1]), atoi(argv[2]), NULL,NULL ), 1); } else if( mode == 3 && argc == 3 ) { MPI *factors; mpi_print (es_stdout, generate_elg_prime( 1, atoi(argv[1]), atoi(argv[2]), NULL,&factors ), 1); es_putc ('\n', es_stdout); mpi_print (es_stdout, factors[0], 1 ); /* print q */ } else if( mode == 4 && argc == 3 ) { MPI g = mpi_alloc(1); mpi_print (es_stdout, generate_elg_prime( 0, atoi(argv[1]), atoi(argv[2]), g, NULL ), 1); es_putc ('\n', es_stdout); mpi_print (es_stdout, g, 1 ); mpi_free (g); } else wrong_args("--gen-prime mode bits [qbits] "); es_putc ('\n', es_stdout); } #endif wrong_args("--gen-prime not yet supported "); break; case aGenRandom: { int level = argc ? atoi(*argv):0; int count = argc > 1 ? atoi(argv[1]): 0; int endless = !count; if( argc < 1 || argc > 2 || level < 0 || level > 2 || count < 0 ) wrong_args("--gen-random 0|1|2 [count]"); while( endless || count ) { byte *p; /* We need a multiple of 3, so that in case of armored output we get a correct string. No linefolding is done, as it is best to leave this to other tools */ size_t n = !endless && count < 99? count : 99; p = gcry_random_bytes (n, level); #ifdef HAVE_DOSISH_SYSTEM setmode ( fileno(stdout), O_BINARY ); #endif if (opt.armor) { char *tmp = make_radix64_string (p, n); es_fputs (tmp, es_stdout); xfree (tmp); if (n%3 == 1) es_putc ('=', es_stdout); if (n%3) es_putc ('=', es_stdout); } else { es_fwrite( p, n, 1, es_stdout ); } xfree(p); if( !endless ) count -= n; } if (opt.armor) es_putc ('\n', es_stdout); } break; case aPrintMD: if( argc < 1) wrong_args("--print-md algo [files]"); { int all_algos = (**argv=='*' && !(*argv)[1]); int algo = all_algos? 0 : gcry_md_map_name (*argv); if( !algo && !all_algos ) log_error(_("invalid hash algorithm '%s'\n"), *argv ); else { argc--; argv++; if( !argc ) print_mds(NULL, algo); else { for(; argc; argc--, argv++ ) print_mds(*argv, algo); } } } break; case aPrintMDs: /* old option */ if( !argc ) print_mds(NULL,0); else { for(; argc; argc--, argv++ ) print_mds(*argv,0); } break; #ifndef NO_TRUST_MODELS case aListTrustDB: if( !argc ) list_trustdb (ctrl, es_stdout, NULL); else { for( ; argc; argc--, argv++ ) list_trustdb (ctrl, es_stdout, *argv ); } break; case aUpdateTrustDB: if( argc ) wrong_args("--update-trustdb"); update_trustdb (ctrl); break; case aCheckTrustDB: /* Old versions allowed for arguments - ignore them */ check_trustdb (ctrl); break; case aFixTrustDB: how_to_fix_the_trustdb (); break; case aListTrustPath: if( !argc ) wrong_args("--list-trust-path "); for( ; argc; argc--, argv++ ) { username = make_username( *argv ); list_trust_path( username ); xfree(username); } break; case aExportOwnerTrust: if( argc ) wrong_args("--export-ownertrust"); export_ownertrust (ctrl); break; case aImportOwnerTrust: if( argc > 1 ) wrong_args("--import-ownertrust [file]"); import_ownertrust (ctrl, argc? *argv:NULL ); break; #endif /*!NO_TRUST_MODELS*/ case aRebuildKeydbCaches: if (argc) wrong_args ("--rebuild-keydb-caches"); keydb_rebuild_caches (ctrl, 1); break; #ifdef ENABLE_CARD_SUPPORT case aCardStatus: if (argc == 0) card_status (ctrl, es_stdout, NULL); else if (argc == 1) card_status (ctrl, es_stdout, *argv); else wrong_args ("--card-status [serialno]"); break; case aCardEdit: if (argc) { sl = NULL; for (argc--, argv++ ; argc; argc--, argv++) append_to_strlist (&sl, *argv); card_edit (ctrl, sl); free_strlist (sl); } else card_edit (ctrl, NULL); break; case aChangePIN: if (!argc) change_pin (0,1); else if (argc == 1) change_pin (atoi (*argv),1); else wrong_args ("--change-pin [no]"); break; #endif /* ENABLE_CARD_SUPPORT*/ case aListConfig: { char *str=collapse_args(argc,argv); list_config(str); xfree(str); } break; case aListGcryptConfig: /* Fixme: It would be nice to integrate that with --list-config but unfortunately there is no way yet to have libgcrypt print it to an estream for further parsing. */ gcry_control (GCRYCTL_PRINT_CONFIG, stdout); break; case aTOFUPolicy: #ifdef USE_TOFU { int policy; int i; KEYDB_HANDLE hd; if (argc < 2) wrong_args ("--tofu-policy POLICY KEYID [KEYID...]"); policy = parse_tofu_policy (argv[0]); - hd = keydb_new (); + hd = keydb_new (ctrl); if (! hd) { write_status_failure ("tofu-driver", gpg_error(GPG_ERR_GENERAL)); g10_exit (1); } tofu_begin_batch_update (ctrl); for (i = 1; i < argc; i ++) { KEYDB_SEARCH_DESC desc; kbnode_t kb; rc = classify_user_id (argv[i], &desc, 0); if (rc) { log_error (_("error parsing key specification '%s': %s\n"), argv[i], gpg_strerror (rc)); write_status_failure ("tofu-driver", rc); g10_exit (1); } if (! (desc.mode == KEYDB_SEARCH_MODE_SHORT_KID || desc.mode == KEYDB_SEARCH_MODE_LONG_KID || desc.mode == KEYDB_SEARCH_MODE_FPR || desc.mode == KEYDB_SEARCH_MODE_KEYGRIP)) { log_error (_("'%s' does not appear to be a valid" " key ID, fingerprint or keygrip\n"), argv[i]); write_status_failure ("tofu-driver", gpg_error(GPG_ERR_GENERAL)); g10_exit (1); } rc = keydb_search_reset (hd); if (rc) { /* This should not happen, thus no need to tranalate the string. */ log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc)); write_status_failure ("tofu-driver", rc); g10_exit (1); } rc = keydb_search (hd, &desc, 1, NULL); if (rc) { log_error (_("key \"%s\" not found: %s\n"), argv[i], gpg_strerror (rc)); write_status_failure ("tofu-driver", rc); g10_exit (1); } rc = keydb_get_keyblock (hd, &kb); if (rc) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc)); write_status_failure ("tofu-driver", rc); g10_exit (1); } merge_keys_and_selfsig (ctrl, kb); if (tofu_set_policy (ctrl, kb, policy)) { write_status_failure ("tofu-driver", rc); g10_exit (1); } release_kbnode (kb); } tofu_end_batch_update (ctrl); keydb_release (hd); } #endif /*USE_TOFU*/ break; default: if (!opt.quiet) log_info (_("WARNING: no command supplied." " Trying to guess what you mean ...\n")); /*FALLTHRU*/ case aListPackets: if( argc > 1 ) wrong_args("[filename]"); /* Issue some output for the unix newbie */ if (!fname && !opt.outfile && gnupg_isatty (fileno (stdin)) && gnupg_isatty (fileno (stdout)) && gnupg_isatty (fileno (stderr))) log_info(_("Go ahead and type your message ...\n")); a = iobuf_open(fname); if (a && is_secured_file (iobuf_get_fd (a))) { iobuf_close (a); a = NULL; gpg_err_set_errno (EPERM); } if( !a ) log_error(_("can't open '%s'\n"), print_fname_stdin(fname)); else { if( !opt.no_armor ) { if( use_armor_filter( a ) ) { afx = new_armor_context (); push_armor_filter (afx, a); } } if( cmd == aListPackets ) { opt.list_packets=1; set_packet_list_mode(1); } rc = proc_packets (ctrl, NULL, a ); if( rc ) { write_status_failure ("-", rc); log_error ("processing message failed: %s\n", gpg_strerror (rc)); } iobuf_close(a); } break; } /* cleanup */ gpg_deinit_default_ctrl (ctrl); xfree (ctrl); release_armor_context (afx); FREE_STRLIST(remusr); FREE_STRLIST(locusr); g10_exit(0); return 8; /*NEVER REACHED*/ } /* Note: This function is used by signal handlers!. */ static void emergency_cleanup (void) { gcry_control (GCRYCTL_TERM_SECMEM ); } void g10_exit( int rc ) { /* If we had an error but not printed an error message, do it now. * Note that write_status_failure will never print a second failure * status line. */ if (rc) write_status_failure ("gpg-exit", gpg_error (GPG_ERR_GENERAL)); gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); if (DBG_CLOCK) log_clock ("stop"); if ( (opt.debug & DBG_MEMSTAT_VALUE) ) { keydb_dump_stats (); sig_check_dump_stats (); objcache_dump_stats (); gcry_control (GCRYCTL_DUMP_MEMORY_STATS); gcry_control (GCRYCTL_DUMP_RANDOM_STATS); } if (opt.debug) gcry_control (GCRYCTL_DUMP_SECMEM_STATS ); gnupg_block_all_signals (); emergency_cleanup (); rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; exit (rc); } /* Pretty-print hex hashes. This assumes at least an 80-character display, but there are a few other similar assumptions in the display code. */ static void print_hex (gcry_md_hd_t md, int algo, const char *fname) { int i,n,count,indent=0; const byte *p; if (fname) indent = es_printf("%s: ",fname); if (indent>40) { es_printf ("\n"); indent=0; } if (algo==DIGEST_ALGO_RMD160) indent += es_printf("RMD160 = "); else if (algo>0) indent += es_printf("%6s = ", gcry_md_algo_name (algo)); else algo = abs(algo); count = indent; p = gcry_md_read (md, algo); n = gcry_md_get_algo_dlen (algo); count += es_printf ("%02X",*p++); for(i=1;i79) { es_printf ("\n%*s",indent," "); count = indent; } else count += es_printf(" "); if (!(i%8)) count += es_printf(" "); } else if (n==20) { if(!(i%2)) { if(count+4>79) { es_printf ("\n%*s",indent," "); count=indent; } else count += es_printf(" "); } if (!(i%10)) count += es_printf(" "); } else { if(!(i%4)) { if (count+8>79) { es_printf ("\n%*s",indent," "); count=indent; } else count += es_printf(" "); } } count += es_printf("%02X",*p); } es_printf ("\n"); } static void print_hashline( gcry_md_hd_t md, int algo, const char *fname ) { int i, n; const byte *p; if ( fname ) { for (p = fname; *p; p++ ) { if ( *p <= 32 || *p > 127 || *p == ':' || *p == '%' ) es_printf ("%%%02X", *p ); else es_putc (*p, es_stdout); } } es_putc (':', es_stdout); es_printf ("%d:", algo); p = gcry_md_read (md, algo); n = gcry_md_get_algo_dlen (algo); for(i=0; i < n ; i++, p++ ) es_printf ("%02X", *p); es_fputs (":\n", es_stdout); } static void print_mds( const char *fname, int algo ) { estream_t fp; char buf[1024]; size_t n; gcry_md_hd_t md; if (!fname) { fp = es_stdin; es_set_binary (fp); } else { fp = es_fopen (fname, "rb" ); if (fp && is_secured_file (es_fileno (fp))) { es_fclose (fp); fp = NULL; gpg_err_set_errno (EPERM); } } if (!fp) { log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) ); return; } gcry_md_open (&md, 0, 0); if (algo) gcry_md_enable (md, algo); else { if (!gcry_md_test_algo (GCRY_MD_MD5)) gcry_md_enable (md, GCRY_MD_MD5); gcry_md_enable (md, GCRY_MD_SHA1); if (!gcry_md_test_algo (GCRY_MD_RMD160)) gcry_md_enable (md, GCRY_MD_RMD160); if (!gcry_md_test_algo (GCRY_MD_SHA224)) gcry_md_enable (md, GCRY_MD_SHA224); if (!gcry_md_test_algo (GCRY_MD_SHA256)) gcry_md_enable (md, GCRY_MD_SHA256); if (!gcry_md_test_algo (GCRY_MD_SHA384)) gcry_md_enable (md, GCRY_MD_SHA384); if (!gcry_md_test_algo (GCRY_MD_SHA512)) gcry_md_enable (md, GCRY_MD_SHA512); } while ((n=es_fread (buf, 1, DIM(buf), fp))) gcry_md_write (md, buf, n); if (es_ferror(fp)) log_error ("%s: %s\n", fname?fname:"[stdin]", strerror(errno)); else { gcry_md_final (md); if (opt.with_colons) { if ( algo ) print_hashline (md, algo, fname); else { if (!gcry_md_test_algo (GCRY_MD_MD5)) print_hashline( md, GCRY_MD_MD5, fname ); print_hashline( md, GCRY_MD_SHA1, fname ); if (!gcry_md_test_algo (GCRY_MD_RMD160)) print_hashline( md, GCRY_MD_RMD160, fname ); if (!gcry_md_test_algo (GCRY_MD_SHA224)) print_hashline (md, GCRY_MD_SHA224, fname); if (!gcry_md_test_algo (GCRY_MD_SHA256)) print_hashline( md, GCRY_MD_SHA256, fname ); if (!gcry_md_test_algo (GCRY_MD_SHA384)) print_hashline ( md, GCRY_MD_SHA384, fname ); if (!gcry_md_test_algo (GCRY_MD_SHA512)) print_hashline ( md, GCRY_MD_SHA512, fname ); } } else { if (algo) print_hex (md, -algo, fname); else { if (!gcry_md_test_algo (GCRY_MD_MD5)) print_hex (md, GCRY_MD_MD5, fname); print_hex (md, GCRY_MD_SHA1, fname ); if (!gcry_md_test_algo (GCRY_MD_RMD160)) print_hex (md, GCRY_MD_RMD160, fname ); if (!gcry_md_test_algo (GCRY_MD_SHA224)) print_hex (md, GCRY_MD_SHA224, fname); if (!gcry_md_test_algo (GCRY_MD_SHA256)) print_hex (md, GCRY_MD_SHA256, fname ); if (!gcry_md_test_algo (GCRY_MD_SHA384)) print_hex (md, GCRY_MD_SHA384, fname ); if (!gcry_md_test_algo (GCRY_MD_SHA512)) print_hex (md, GCRY_MD_SHA512, fname ); } } } gcry_md_close (md); if (fp != es_stdin) es_fclose (fp); } /**************** * Check the supplied name,value string and add it to the notation * data to be used for signatures. which==0 for sig notations, and 1 * for cert notations. */ static void add_notation_data( const char *string, int which ) { struct notation *notation; notation=string_to_notation(string,utf8_strings); if(notation) { if(which) { notation->next=opt.cert_notations; opt.cert_notations=notation; } else { notation->next=opt.sig_notations; opt.sig_notations=notation; } } } static void add_policy_url( const char *string, int which ) { unsigned int i,critical=0; strlist_t sl; if(*string=='!') { string++; critical=1; } for(i=0;iflags |= 1; } static void add_keyserver_url( const char *string, int which ) { unsigned int i,critical=0; strlist_t sl; if(*string=='!') { string++; critical=1; } for(i=0;iflags |= 1; } static void read_sessionkey_from_fd (int fd) { int i, len; char *line; if (! gnupg_fd_valid (fd)) log_fatal ("override-session-key-fd is invalid: %s\n", strerror (errno)); for (line = NULL, i = len = 100; ; i++ ) { if (i >= len-1 ) { char *tmp = line; len += 100; line = xmalloc_secure (len); if (tmp) { memcpy (line, tmp, i); xfree (tmp); } else i=0; } if (read (fd, line + i, 1) != 1 || line[i] == '\n') break; } line[i] = 0; log_debug ("seskey: %s\n", line); gpgrt_annotate_leaked_object (line); opt.override_session_key = line; } diff --git a/g10/gpg.h b/g10/gpg.h index 28a77b6df..adb919d7d 100644 --- a/g10/gpg.h +++ b/g10/gpg.h @@ -1,111 +1,117 @@ /* gpg.h - top level include file for gpg etc. * Copyright (C) 2003, 2006, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef GNUPG_G10_GPG_H #define GNUPG_G10_GPG_H /* Note, that this file should be the first one after the system header files. This is required to set the error source to the correct value and may be of advantage if we ever have to do special things. */ #ifdef HAVE_W32_SYSTEM # define WIN32_LEAN_AND_MEAN 1 #endif #ifdef GPG_ERR_SOURCE_DEFAULT #error GPG_ERR_SOURCE_DEFAULT already defined #endif #define GPG_ERR_SOURCE_DEFAULT GPG_ERR_SOURCE_GPG #define map_assuan_err(a) \ map_assuan_err_with_source (GPG_ERR_SOURCE_DEFAULT, (a)) #include #include /* Number of bits we accept when reading or writing MPIs. */ #define MAX_EXTERN_MPI_BITS 16384 /* The maximum length of a binary fingerprints. This is used to * provide a static buffer and will be increased if we need to support * longer fingerprints. Warning: At some places we have some * assumption on a 20 byte fingerprint. * Watch out for FIXME(fingerprint) */ #define MAX_FINGERPRINT_LEN 32 /* The maximum length of a formatted fingerprint as returned by * format_hexfingerprint(). */ #define MAX_FORMATTED_FINGERPRINT_LEN 59 /* Forward declarations. */ /* Object used to keep state locally to server.c . */ struct server_local_s; +/* Object used to keep state locally to call-keyboxd.c . */ +struct keyboxd_local_s; +typedef struct keyboxd_local_s *keyboxd_local_t; + /* Object used to keep state locally to call-dirmngr.c . */ struct dirmngr_local_s; typedef struct dirmngr_local_s *dirmngr_local_t; /* Object used to describe a keyblock node. */ -typedef struct kbnode_struct *KBNODE; /* Deprecated use kbnode_t. */ -typedef struct kbnode_struct *kbnode_t; +typedef struct kbnode_struct *KBNODE; /* Deprecated use kbnode_t. */typedef struct kbnode_struct *kbnode_t; /* The handle for keydb operations. */ -typedef struct keydb_handle *KEYDB_HANDLE; +typedef struct keydb_handle_s *KEYDB_HANDLE; /* TOFU database meta object. */ struct tofu_dbs_s; typedef struct tofu_dbs_s *tofu_dbs_t; #if SIZEOF_UNSIGNED_LONG == 8 # define SERVER_CONTROL_MAGIC 0x53616c696e676572 #else # define SERVER_CONTROL_MAGIC 0x53616c69 #endif /* Session control object. This object is passed to most functions to convey the status of a session. Note that the defaults are set by gpg_init_default_ctrl(). */ struct server_control_s { /* Always has the value SERVER_CONTROL_MAGIC. */ unsigned long magic; /* Local data for server.c */ struct server_local_s *server_local; /* Local data for call-dirmngr.c */ dirmngr_local_t dirmngr_local; + /* Local data for call-keyboxd.c */ + keyboxd_local_t keyboxd_local; + /* Local data for tofu.c */ struct { tofu_dbs_t dbs; int batch_updated_wanted; } tofu; /* This is used to cache a key data base handle. */ KEYDB_HANDLE cached_getkey_kdb; }; #endif /*GNUPG_G10_GPG_H*/ diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index 7b7e1dc9a..43cecb90e 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -1,3112 +1,3112 @@ /* gpgcompose.c - Maintainer tool to create OpenPGP messages by hand. * Copyright (C) 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include "gpg.h" #include "packet.h" #include "keydb.h" #include "main.h" #include "options.h" #include "call-agent.h" static int do_debug; #define debug(fmt, ...) \ do { if (do_debug) log_debug (fmt, ##__VA_ARGS__); } while (0) /* --encryption, for instance, adds a filter in front of out. There is an operator (--encryption-pop) to end this. We use the following infrastructure to make it easy to pop the state. */ struct filter { void *func; void *context; int pkttype; int partial_block_mode; struct filter *next; }; /* Hack to ass CTRL to some functions. */ static ctrl_t global_ctrl; static struct filter *filters; static void filter_push (iobuf_t out, void *func, void *context, int type, int partial_block_mode) { gpg_error_t err; struct filter *f = xmalloc_clear (sizeof (*f)); f->next = filters; f->func = func; f->context = context; f->pkttype = type; f->partial_block_mode = partial_block_mode; filters = f; err = iobuf_push_filter (out, func, context); if (err) log_fatal ("Adding filter: %s\n", gpg_strerror (err)); } static void filter_pop (iobuf_t out, int expected_type) { gpg_error_t err; struct filter *f = filters; log_assert (f); if (f->pkttype != expected_type) log_fatal ("Attempted to pop a %s container, " "but current container is a %s container.\n", pkttype_str (f->pkttype), pkttype_str (expected_type)); if (f->pkttype == PKT_ENCRYPTED) { err = iobuf_pop_filter (out, f->func, f->context); if (err) log_fatal ("Popping encryption filter: %s\n", gpg_strerror (err)); } else log_fatal ("FILTERS appears to be corrupted.\n"); if (f->partial_block_mode) iobuf_set_partial_body_length_mode (out, 0); filters = f->next; xfree (f); } /* Return if CIPHER_ID is a valid cipher. */ static int valid_cipher (int cipher_id) { return (cipher_id == CIPHER_ALGO_IDEA || cipher_id == CIPHER_ALGO_3DES || cipher_id == CIPHER_ALGO_CAST5 || cipher_id == CIPHER_ALGO_BLOWFISH || cipher_id == CIPHER_ALGO_AES || cipher_id == CIPHER_ALGO_AES192 || cipher_id == CIPHER_ALGO_AES256 || cipher_id == CIPHER_ALGO_TWOFISH || cipher_id == CIPHER_ALGO_CAMELLIA128 || cipher_id == CIPHER_ALGO_CAMELLIA192 || cipher_id == CIPHER_ALGO_CAMELLIA256); } /* Parse a session key encoded as a string of the form x:HEXDIGITS where x is the algorithm id. (This is the format emitted by gpg --show-session-key.) */ struct session_key { int algo; int keylen; char *key; }; static struct session_key parse_session_key (const char *option, char *p, int require_algo) { char *tail; struct session_key sk; memset (&sk, 0, sizeof (sk)); /* Check for the optional "cipher-id:" at the start of the string. */ errno = 0; sk.algo = strtol (p, &tail, 10); if (! errno && tail && *tail == ':') { if (! valid_cipher (sk.algo)) log_info ("%s: %d is not a known cipher (but using anyways)\n", option, sk.algo); p = tail + 1; } else if (require_algo) log_fatal ("%s: Session key must have the form algo:HEXCHARACTERS.\n", option); else sk.algo = 0; /* Ignore a leading 0x. */ if (p[0] == '0' && p[1] == 'x') p += 2; if (strlen (p) % 2 != 0) log_fatal ("%s: session key must consist of an even number of hexadecimal characters.\n", option); sk.keylen = strlen (p) / 2; sk.key = xmalloc (sk.keylen); if (hex2bin (p, sk.key, sk.keylen) == -1) log_fatal ("%s: Session key must only contain hexadecimal characters\n", option); return sk; } /* A callback. OPTION_STR is the option that was matched. ARGC is the number of arguments following the option and ARGV are those arguments. (Thus, argv[0] is the first string following the option and argv[-1] is the option.) COOKIE is the opaque value passed to process_options. */ typedef int (*option_prcessor_t) (const char *option_str, int argc, char *argv[], void *cookie); struct option { /* The option that this matches. This must start with "--" or be the empty string. The empty string matches bare arguments. */ const char *option; /* The function to call to process this option. */ option_prcessor_t func; /* Documentation. */ const char *help; }; /* Merge two lists of options. Note: this makes a shallow copy! The caller must xfree() the result. */ static struct option * merge_options (struct option a[], struct option b[]) { int i, j; struct option *c; for (i = 0; a[i].option; i ++) ; for (j = 0; b[j].option; j ++) ; c = xmalloc ((i + j + 1) * sizeof (struct option)); memcpy (c, a, i * sizeof (struct option)); memcpy (&c[i], b, j * sizeof (struct option)); c[i + j].option = NULL; if (a[i].help && b[j].help) c[i + j].help = xasprintf ("%s\n\n%s", a[i].help, b[j].help); else if (a[i].help) c[i + j].help = a[i].help; else if (b[j].help) c[i + j].help = b[j].help; return c; } /* Returns whether ARG is an option. All options start with --. */ static int is_option (const char *arg) { return arg[0] == '-' && arg[1] == '-'; } /* OPTIONS is a NULL terminated array of struct option:s. Finds the entry that is the same as ARG. Returns -1 if no entry is found. The empty string option matches bare arguments. */ static int match_option (const struct option options[], const char *arg) { int i; int bare_arg = ! is_option (arg); for (i = 0; options[i].option; i ++) if ((! bare_arg && strcmp (options[i].option, arg) == 0) /* Non-options match the empty string. */ || (bare_arg && options[i].option[0] == '\0')) return i; return -1; } static void show_help (struct option options[]) { int i; int max_length = 0; int space; for (i = 0; options[i].option; i ++) { const char *option = options[i].option[0] ? options[i].option : "ARG"; int l = strlen (option); if (l > max_length) max_length = l; } space = 72 - (max_length + 2); if (space < 40) space = 40; for (i = 0; ; i ++) { const char *option = options[i].option; const char *help = options[i].help; int l; int j; char *tmp; char *formatted; char *p; char *newline; if (! option && ! help) break; if (option) { const char *o = option[0] ? option : "ARG"; l = strlen (o); fprintf (stdout, "%s", o); } if (! help) { fputc ('\n', stdout); continue; } if (option) for (j = l; j < max_length + 2; j ++) fputc (' ', stdout); #define BOLD_START "\033[1m" #define NORMAL_RESTORE "\033[0m" #define BOLD(x) BOLD_START x NORMAL_RESTORE if (! option || options[i].func) tmp = (char *) help; else tmp = xasprintf ("%s " BOLD("(Unimplemented.)"), help); if (! option) space = 72; formatted = format_text (tmp, space, space + 4); if (!formatted) abort (); if (tmp != help) xfree (tmp); if (! option) { printf ("\n%s\n", formatted); break; } for (p = formatted; p && *p; p = (*newline == '\0') ? newline : newline + 1) { newline = strchr (p, '\n'); if (! newline) newline = &p[strlen (p)]; l = (size_t) newline - (size_t) p; if (p != formatted) for (j = 0; j < max_length + 2; j ++) fputc (' ', stdout); fwrite (p, l, 1, stdout); fputc ('\n', stdout); } xfree (formatted); } } /* Return value is number of consumed argv elements. */ static int process_options (const char *parent_option, struct option break_options[], struct option local_options[], void *lcookie, struct option global_options[], void *gcookie, int argc, char *argv[]) { int i; for (i = 0; i < argc; i ++) { int j; struct option *option; void *cookie; int bare_arg; option_prcessor_t func; int consumed; if (break_options) { j = match_option (break_options, argv[i]); if (j != -1) /* Match. Break out. */ return i; } j = match_option (local_options, argv[i]); if (j == -1) { if (global_options) j = match_option (global_options, argv[i]); if (j == -1) { if (strcmp (argv[i], "--help") == 0) { if (! global_options) show_help (local_options); else { struct option *combined = merge_options (local_options, global_options); show_help (combined); xfree (combined); } g10_exit (0); } if (parent_option) log_fatal ("%s: Unknown option: %s\n", parent_option, argv[i]); else log_fatal ("Unknown option: %s\n", argv[i]); } option = &global_options[j]; cookie = gcookie; } else { option = &local_options[j]; cookie = lcookie; } bare_arg = strcmp (option->option, "") == 0; func = option->func; if (! func) { if (bare_arg) log_fatal ("Bare arguments unimplemented.\n"); else log_fatal ("Unimplemented option: %s\n", option->option); } consumed = func (bare_arg ? parent_option : argv[i], argc - i - !bare_arg, &argv[i + !bare_arg], cookie); i += consumed; if (bare_arg) i --; } return i; } /* The keys, subkeys, user ids and user attributes in the order that they were added. */ PACKET components[20]; /* The number of components. */ int ncomponents; static int add_component (int pkttype, void *component) { int i = ncomponents ++; log_assert (i < sizeof (components) / sizeof (components[0])); log_assert (pkttype == PKT_PUBLIC_KEY || pkttype == PKT_PUBLIC_SUBKEY || pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY || pkttype == PKT_USER_ID || pkttype == PKT_ATTRIBUTE); components[i].pkttype = pkttype; components[i].pkt.generic = component; return i; } static void dump_component (PACKET *pkt) { struct kbnode_struct kbnode; if (! do_debug) return; memset (&kbnode, 0, sizeof (kbnode)); kbnode.pkt = pkt; dump_kbnode (&kbnode); } /* Returns the first primary key in COMPONENTS or NULL if there is none. */ static PKT_public_key * primary_key (void) { int i; for (i = 0; i < ncomponents; i ++) if (components[i].pkttype == PKT_PUBLIC_KEY) return components[i].pkt.public_key; return NULL; } /* The last session key (updated when adding a SK-ESK, PK-ESK or SED packet. */ static DEK session_key; static int user_id (const char *option, int argc, char *argv[], void *cookie); static int public_key (const char *option, int argc, char *argv[], void *cookie); static int sk_esk (const char *option, int argc, char *argv[], void *cookie); static int pk_esk (const char *option, int argc, char *argv[], void *cookie); static int encrypted (const char *option, int argc, char *argv[], void *cookie); static int encrypted_pop (const char *option, int argc, char *argv[], void *cookie); static int literal (const char *option, int argc, char *argv[], void *cookie); static int signature (const char *option, int argc, char *argv[], void *cookie); static int copy (const char *option, int argc, char *argv[], void *cookie); static struct option major_options[] = { { "--user-id", user_id, "Create a user id packet." }, { "--public-key", public_key, "Create a public key packet." }, { "--private-key", NULL, "Create a private key packet." }, { "--public-subkey", public_key, "Create a subkey packet." }, { "--private-subkey", NULL, "Create a private subkey packet." }, { "--sk-esk", sk_esk, "Create a symmetric-key encrypted session key packet." }, { "--pk-esk", pk_esk, "Create a public-key encrypted session key packet." }, { "--encrypted", encrypted, "Create a symmetrically encrypted data packet." }, { "--encrypted-mdc", encrypted, "Create a symmetrically encrypted and integrity protected data packet." }, { "--encrypted-pop", encrypted_pop, "Pop the most recent encryption container started by either" " --encrypted or --encrypted-mdc." }, { "--compressed", NULL, "Create a compressed data packet." }, { "--literal", literal, "Create a literal (plaintext) data packet." }, { "--signature", signature, "Create a signature packet." }, { "--onepass-sig", NULL, "Create a one-pass signature packet." }, { "--copy", copy, "Copy the specified file." }, { NULL, NULL, "To get more information about a given command, use:\n\n" " $ gpgcompose --command --help to list a command's options."}, }; static struct option global_options[] = { { NULL, NULL, NULL }, }; /* Make our lives easier and use a static limit for the user name. 10k is way more than enough anyways... */ const int user_id_max_len = 10 * 1024; static int user_id_name (const char *option, int argc, char *argv[], void *cookie) { PKT_user_id *uid = cookie; int l; if (argc == 0) log_fatal ("Usage: %s USER_ID\n", option); if (uid->len) log_fatal ("Attempt to set user id multiple times.\n"); l = strlen (argv[0]); if (l > user_id_max_len) log_fatal ("user id too long (max: %d)\n", user_id_max_len); memcpy (uid->name, argv[0], l); uid->name[l] = 0; uid->len = l; return 1; } static struct option user_id_options[] = { { "", user_id_name, "Set the user id. This is usually in the format " "\"Name (comment) \"" }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --user-id \"USERID\" | " GPG_NAME " --list-packets" } }; static int user_id (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; PKT_user_id *uid = xmalloc_clear (sizeof (*uid) + user_id_max_len); int c = add_component (PKT_USER_ID, uid); int processed; processed = process_options (option, major_options, user_id_options, uid, global_options, NULL, argc, argv); if (! uid->len) log_fatal ("%s: user id not given", option); err = build_packet (out, &components[c]); if (err) log_fatal ("Serializing user id packet: %s\n", gpg_strerror (err)); debug ("Wrote user id packet:\n"); dump_component (&components[c]); return processed; } static int pk_search_terms (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC desc; kbnode_t kb; PKT_public_key *pk = cookie; PKT_public_key *pk_ref; int i; if (argc == 0) log_fatal ("Usage: %s KEYID\n", option); if (pk->pubkey_algo) log_fatal ("%s: multiple keys provided\n", option); err = classify_user_id (argv[0], &desc, 0); if (err) log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err)); - hd = keydb_new (); + hd = keydb_new (ctrl); err = keydb_search (hd, &desc, 1, NULL); if (err) log_fatal ("looking up '%s': %s\n", argv[0], gpg_strerror (err)); err = keydb_get_keyblock (hd, &kb); if (err) log_fatal ("retrieving keyblock for '%s': %s\n", argv[0], gpg_strerror (err)); keydb_release (hd); pk_ref = kb->pkt->pkt.public_key; /* Copy the timestamp (if not already set), algo and public key parameters. */ if (! pk->timestamp) pk->timestamp = pk_ref->timestamp; pk->pubkey_algo = pk_ref->pubkey_algo; for (i = 0; i < pubkey_get_npkey (pk->pubkey_algo); i ++) pk->pkey[i] = gcry_mpi_copy (pk_ref->pkey[i]); release_kbnode (kb); return 1; } static int pk_timestamp (const char *option, int argc, char *argv[], void *cookie) { PKT_public_key *pk = cookie; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s TIMESTAMP\n", option); errno = 0; pk->timestamp = parse_timestamp (argv[0], &tail); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); return 1; } #define TIMESTAMP_HELP \ "Either as seconds since the epoch or as an ISO 8601 formatted " \ "string (yyyymmddThhmmss, where the T is a literal)." static struct option pk_options[] = { { "--timestamp", pk_timestamp, "The creation time. " TIMESTAMP_HELP }, { "", pk_search_terms, "The key to copy the creation time and public key parameters from." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --public-key $KEYID --user-id \"USERID\" \\\n" " | " GPG_NAME " --list-packets" } }; static int public_key (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; iobuf_t out = cookie; PKT_public_key *pk; int c; int processed; int t = (strcmp (option, "--public-key") == 0 ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY); (void) option; pk = xmalloc_clear (sizeof (*pk)); pk->version = 4; c = add_component (t, pk); processed = process_options (option, major_options, pk_options, pk, global_options, NULL, argc, argv); if (! pk->pubkey_algo) log_fatal ("%s: key to extract public key parameters from not given", option); /* Clear the keyid in case we updated one of the relevant fields after accessing it. */ pk->keyid[0] = pk->keyid[1] = 0; err = build_packet (out, &components[c]); if (err) log_fatal ("serializing %s packet: %s\n", t == PKT_PUBLIC_KEY ? "public key" : "subkey", gpg_strerror (err)); debug ("Wrote %s packet:\n", t == PKT_PUBLIC_KEY ? "public key" : "subkey"); dump_component (&components[c]); return processed; } struct signinfo { /* Key with which to sign. */ kbnode_t issuer_kb; PKT_public_key *issuer_pk; /* Overrides the issuer's key id. */ u32 issuer_keyid[2]; /* Sets the issuer's keyid to the primary key's key id. */ int issuer_keyid_self; /* Key to sign. */ PKT_public_key *pk; /* Subkey to sign. */ PKT_public_key *sk; /* User id to sign. */ PKT_user_id *uid; int class; int digest_algo; u32 timestamp; u32 key_expiration; byte *cipher_algorithms; int cipher_algorithms_len; byte *digest_algorithms; int digest_algorithms_len; byte *compress_algorithms; int compress_algorithms_len; u32 expiration; int exportable_set; int exportable; int revocable_set; int revocable; int trust_level_set; byte trust_args[2]; char *trust_scope; struct revocation_key *revocation_key; int nrevocation_keys; struct notation *notations; byte *key_server_preferences; int key_server_preferences_len; char *key_server; int primary_user_id_set; int primary_user_id; char *policy_uri; byte *key_flags; int key_flags_len; char *signers_user_id; byte reason_for_revocation_code; char *reason_for_revocation; byte *features; int features_len; /* Whether to corrupt the signature. */ int corrupt; }; static int sig_issuer (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; KEYDB_HANDLE hd; KEYDB_SEARCH_DESC desc; struct signinfo *si = cookie; if (argc == 0) log_fatal ("Usage: %s KEYID\n", option); if (si->issuer_pk) log_fatal ("%s: multiple keys provided\n", option); err = classify_user_id (argv[0], &desc, 0); if (err) log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err)); - hd = keydb_new (); + hd = keydb_new (ctrl); err = keydb_search (hd, &desc, 1, NULL); if (err) log_fatal ("looking up '%s': %s\n", argv[0], gpg_strerror (err)); err = keydb_get_keyblock (hd, &si->issuer_kb); if (err) log_fatal ("retrieving keyblock for '%s': %s\n", argv[0], gpg_strerror (err)); keydb_release (hd); si->issuer_pk = si->issuer_kb->pkt->pkt.public_key; return 1; } static int sig_issuer_keyid (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; KEYDB_SEARCH_DESC desc; struct signinfo *si = cookie; if (argc == 0) log_fatal ("Usage: %s KEYID|self\n", option); if (si->issuer_keyid[0] || si->issuer_keyid[1] || si->issuer_keyid_self) log_fatal ("%s given multiple times.\n", option); if (strcasecmp (argv[0], "self") == 0) { si->issuer_keyid_self = 1; return 1; } err = classify_user_id (argv[0], &desc, 0); if (err) log_fatal ("search terms '%s': %s\n", argv[0], gpg_strerror (err)); if (desc.mode != KEYDB_SEARCH_MODE_LONG_KID) log_fatal ("%s is not a valid long key id.\n", argv[0]); keyid_copy (si->issuer_keyid, desc.u.kid); return 1; } static int sig_pk (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s COMPONENT_INDEX\n", option); errno = 0; i = strtoul (argv[0], &tail, 10); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); if (i >= ncomponents) log_fatal ("%d: No such component (have %d components so far)\n", i, ncomponents); if (! (components[i].pkttype == PKT_PUBLIC_KEY || components[i].pkttype == PKT_PUBLIC_SUBKEY)) log_fatal ("Component %d is not a public key or a subkey.", i); if (strcmp (option, "--pk") == 0) { if (si->pk) log_fatal ("%s already given.\n", option); si->pk = components[i].pkt.public_key; } else if (strcmp (option, "--sk") == 0) { if (si->sk) log_fatal ("%s already given.\n", option); si->sk = components[i].pkt.public_key; } else log_fatal ("Cannot handle %s\n", option); return 1; } static int sig_user_id (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s COMPONENT_INDEX\n", option); if (si->uid) log_fatal ("%s already given.\n", option); errno = 0; i = strtoul (argv[0], &tail, 10); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); if (i >= ncomponents) log_fatal ("%d: No such component (have %d components so far)\n", i, ncomponents); if (! (components[i].pkttype != PKT_USER_ID || components[i].pkttype == PKT_ATTRIBUTE)) log_fatal ("Component %d is not a public key or a subkey.", i); si->uid = components[i].pkt.user_id; return 1; } static int sig_class (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s CLASS\n", option); errno = 0; i = strtoul (argv[0], &tail, 0); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); si->class = i; return 1; } static int sig_digest (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s DIGEST_ALGO\n", option); errno = 0; i = strtoul (argv[0], &tail, 10); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); si->digest_algo = i; return 1; } static int sig_timestamp (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s TIMESTAMP\n", option); errno = 0; si->timestamp = parse_timestamp (argv[0], &tail); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); return 1; } static int sig_expiration (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int is_expiration = strcmp (option, "--expiration") == 0; u32 *i = is_expiration ? &si->expiration : &si->key_expiration; if (! is_expiration) log_assert (strcmp (option, "--key-expiration") == 0); if (argc == 0) log_fatal ("Usage: %s DURATION\n", option); *i = parse_expire_string (argv[0]); if (*i == (u32)-1) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); return 1; } static int sig_int_list (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int nvalues = 1; char *values = xmalloc (nvalues * sizeof (values[0])); char *tail = argv[0]; int i; byte **a; int *n; if (argc == 0) log_fatal ("Usage: %s VALUE[,VALUE...]\n", option); for (i = 0; tail && *tail; i ++) { int v; char *old_tail = tail; errno = 0; v = strtol (tail, &tail, 0); if (errno || old_tail == tail || (tail && !(*tail == ',' || *tail == 0))) log_fatal ("Invalid value passed to %s (%s). " "Expected a list of comma separated numbers\n", option, argv[0]); if (! (0 <= v && v <= 255)) log_fatal ("%s: %d is out of range (Expected: 0-255)\n", option, v); if (i == nvalues) { nvalues *= 2; values = xrealloc (values, nvalues * sizeof (values[0])); } values[i] = v; if (*tail == ',') tail ++; else log_assert (*tail == 0); } if (strcmp ("--cipher-algos", option) == 0) { a = &si->cipher_algorithms; n = &si->cipher_algorithms_len; } else if (strcmp ("--digest-algos", option) == 0) { a = &si->digest_algorithms; n = &si->digest_algorithms_len; } else if (strcmp ("--compress-algos", option) == 0) { a = &si->compress_algorithms; n = &si->compress_algorithms_len; } else log_fatal ("Cannot handle %s\n", option); if (*a) log_fatal ("Option %s given multiple times.\n", option); *a = values; *n = i; return 1; } static int sig_flag (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int range[2] = {0, 255}; char *tail; int v; if (strcmp (option, "--primary-user-id") == 0) range[1] = 1; if (argc <= 1) { if (range[0] == 0 && range[1] == 1) log_fatal ("Usage: %s 0|1\n", option); else log_fatal ("Usage: %s %d-%d\n", option, range[0], range[1]); } errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || !(range[0] <= v && v <= range[1])) log_fatal ("Invalid value passed to %s (%s). Expected %d-%d\n", option, argv[0], range[0], range[1]); if (strcmp (option, "--exportable") == 0) { si->exportable_set = 1; si->exportable = v; } else if (strcmp (option, "--revocable") == 0) { si->revocable_set = 1; si->revocable = v; } else if (strcmp (option, "--primary-user-id") == 0) { si->primary_user_id_set = 1; si->primary_user_id = v; } else log_fatal ("Cannot handle %s\n", option); return 1; } static int sig_trust_level (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int i; char *tail; if (argc <= 1) log_fatal ("Usage: %s DEPTH TRUST_AMOUNT\n", option); for (i = 0; i < sizeof (si->trust_args) / sizeof (si->trust_args[0]); i ++) { int v; errno = 0; v = strtol (argv[i], &tail, 0); if (errno || (tail && *tail) || !(0 <= v && v <= 255)) log_fatal ("Invalid value passed to %s (%s). Expected 0-255\n", option, argv[i]); si->trust_args[i] = v; } si->trust_level_set = 1; return 2; } static int sig_string_arg (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; char *p = argv[0]; char **s; if (argc == 0) log_fatal ("Usage: %s STRING\n", option); if (strcmp (option, "--trust-scope") == 0) s = &si->trust_scope; else if (strcmp (option, "--key-server") == 0) s = &si->key_server; else if (strcmp (option, "--signers-user-id") == 0) s = &si->signers_user_id; else if (strcmp (option, "--policy-uri") == 0) s = &si->policy_uri; else log_fatal ("Cannot handle %s\n", option); if (*s) log_fatal ("%s already given.\n", option); *s = xstrdup (p); return 1; } static int sig_revocation_key (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; struct signinfo *si = cookie; int v; char *tail; PKT_public_key pk; struct revocation_key *revkey; if (argc < 2) log_fatal ("Usage: %s CLASS KEYID\n", option); memset (&pk, 0, sizeof (pk)); errno = 0; v = strtol (argv[0], &tail, 16); if (errno || (tail && *tail) || !(0 <= v && v <= 255)) log_fatal ("%s: Invalid class value (%s). Expected 0-255\n", option, argv[0]); pk.req_usage = PUBKEY_USAGE_SIG; err = get_pubkey_byname (NULL, GET_PUBKEY_NO_AKL, NULL, &pk, argv[1], NULL, NULL, 1); if (err) log_fatal ("looking up key %s: %s\n", argv[1], gpg_strerror (err)); si->nrevocation_keys ++; si->revocation_key = xrealloc (si->revocation_key, si->nrevocation_keys * sizeof (*si->revocation_key)); revkey = &si->revocation_key[si->nrevocation_keys - 1]; revkey->class = v; revkey->algid = pk.pubkey_algo; fingerprint_from_pk (&pk, revkey->fpr, NULL); release_public_key_parts (&pk); return 2; } static int sig_notation (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int is_blob = strcmp (option, "--notation") != 0; struct notation *notation; char *p = argv[0]; int p_free = 0; char *data; int data_size; int data_len; if (argc == 0) log_fatal ("Usage: %s [!<]name=value\n", option); if ((p[0] == '!' && p[1] == '<') || p[0] == '<') /* Read from a file. */ { char *filename = NULL; iobuf_t in; int prefix; if (p[0] == '<') p ++; else { /* Remove the '<', which string_to_notation does not understand, and preserve the '!'. */ p = xstrdup (&p[1]); p_free = 1; p[0] = '!'; } filename = strchr (p, '='); if (! filename) log_fatal ("No value specified. Usage: %s [!<]name=value\n", option); filename ++; prefix = (size_t) filename - (size_t) p; errno = 0; in = iobuf_open (filename); if (! in) log_fatal ("Opening '%s': %s\n", filename, errno ? strerror (errno): "unknown error"); /* A notation can be at most about a few dozen bytes short of 64k. Since this is relatively small, we just allocate that much instead of trying to dynamically size a buffer. */ data_size = 64 * 1024; data = xmalloc (data_size); log_assert (prefix <= data_size); memcpy (data, p, prefix); data_len = iobuf_read (in, &data[prefix], data_size - prefix - 1); if (data_len == -1) /* EOF => 0 bytes read. */ data_len = 0; if (data_len == data_size - prefix - 1) /* Technically, we should do another read and check for EOF, but what's one byte more or less? */ log_fatal ("Notation data doesn't fit in the packet.\n"); iobuf_close (in); /* NUL terminate it. */ data[prefix + data_len] = 0; if (p_free) xfree (p); p = data; p_free = 1; data = &p[prefix]; if (is_blob) p[prefix - 1] = 0; } else if (is_blob) { data = strchr (p, '='); if (! data) { data = p; data_len = 0; } else { p = xstrdup (p); p_free = 1; data = strchr (p, '='); log_assert (data); /* NUL terminate the name. */ *data = 0; data ++; data_len = strlen (data); } } if (is_blob) notation = blob_to_notation (p, data, data_len); else notation = string_to_notation (p, 1); if (! notation) log_fatal ("creating notation: an unknown error occurred.\n"); notation->next = si->notations; si->notations = notation; if (p_free) xfree (p); return 1; } static int sig_big_endian_arg (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; char *p = argv[0]; int i; int l; char *bytes; if (argc == 0) log_fatal ("Usage: %s HEXDIGITS\n", option); /* Skip a leading "0x". */ if (p[0] == '0' && p[1] == 'x') p += 2; for (i = 0; i < strlen (p); i ++) if (!hexdigitp (&p[i])) log_fatal ("%s: argument ('%s') must consist of hex digits.\n", option, p); if (strlen (p) % 2 != 0) log_fatal ("%s: argument ('%s') must contain an even number of hex digits.\n", option, p); l = strlen (p) / 2; bytes = xmalloc (l); hex2bin (p, bytes, l); if (strcmp (option, "--key-server-preferences") == 0) { if (si->key_server_preferences) log_fatal ("%s given multiple times.\n", option); si->key_server_preferences = bytes; si->key_server_preferences_len = l; } else if (strcmp (option, "--key-flags") == 0) { if (si->key_flags) log_fatal ("%s given multiple times.\n", option); si->key_flags = bytes; si->key_flags_len = l; } else if (strcmp (option, "--features") == 0) { if (si->features) log_fatal ("%s given multiple times.\n", option); si->features = bytes; si->features_len = l; } else log_fatal ("Cannot handle %s\n", option); return 1; } static int sig_reason_for_revocation (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; int v; char *tail; if (argc < 2) log_fatal ("Usage: %s REASON_CODE REASON_STRING\n", option); errno = 0; v = strtol (argv[0], &tail, 16); if (errno || (tail && *tail) || !(0 <= v && v <= 255)) log_fatal ("%s: Invalid reason code (%s). Expected 0-255\n", option, argv[0]); if (si->reason_for_revocation) log_fatal ("%s given multiple times.\n", option); si->reason_for_revocation_code = v; si->reason_for_revocation = xstrdup (argv[1]); return 2; } static int sig_corrupt (const char *option, int argc, char *argv[], void *cookie) { struct signinfo *si = cookie; (void) option; (void) argc; (void) argv; (void) cookie; si->corrupt = 1; return 0; } static struct option sig_options[] = { { "--issuer", sig_issuer, "The key to use to generate the signature."}, { "--issuer-keyid", sig_issuer_keyid, "Set the issuer's key id. This is useful for creating a " "self-signature. As a special case, the value \"self\" refers " "to the primary key's key id. " "(RFC 4880, Section 5.2.3.5)" }, { "--pk", sig_pk, "The primary keyas an index into the components (keys and uids) " "created so far where the first component has the index 0." }, { "--sk", sig_pk, "The subkey as an index into the components (keys and uids) created " "so far where the first component has the index 0. Only needed for " "0x18, 0x19, and 0x28 signatures." }, { "--user-id", sig_user_id, "The user id as an index into the components (keys and uids) created " "so far where the first component has the index 0. Only needed for " "0x10-0x13 and 0x30 signatures." }, { "--class", sig_class, "The signature's class. Valid values are " "0x10-0x13 (user id and primary-key certification), " "0x18 (subkey binding), " "0x19 (primary key binding), " "0x1f (direct primary key signature), " "0x20 (key revocation), " "0x28 (subkey revocation), and " "0x30 (certification revocation)." }, { "--digest", sig_digest, "The digest algorithm" }, { "--timestamp", sig_timestamp, "The signature's creation time. " TIMESTAMP_HELP " 0 means now. " "(RFC 4880, Section 5.2.3.4)" }, { "--key-expiration", sig_expiration, "The number of days until the associated key expires. To specify " "seconds, prefix the value with \"seconds=\". It is also possible " "to use 'y', 'm' and 'w' as simple multipliers. For instance, 2y " "means 2 years, etc. " "(RFC 4880, Section 5.2.3.6)" }, { "--cipher-algos", sig_int_list, "A comma separated list of the preferred cipher algorithms (identified by " "their number, see RFC 4880, Section 9). " "(RFC 4880, Section 5.2.3.7)" }, { "--digest-algos", sig_int_list, "A comma separated list of the preferred algorithms (identified by " "their number, see RFC 4880, Section 9). " "(RFC 4880, Section 5.2.3.8)" }, { "--compress-algos", sig_int_list, "A comma separated list of the preferred algorithms (identified by " "their number, see RFC 4880, Section 9)." "(RFC 4880, Section 5.2.3.9)" }, { "--expiration", sig_expiration, "The number of days until the signature expires. To specify seconds, " "prefix the value with \"seconds=\". It is also possible to use 'y', " "'m' and 'w' as simple multipliers. For instance, 2y means 2 years, " "etc. " "(RFC 4880, Section 5.2.3.10)" }, { "--exportable", sig_flag, "Mark this signature as exportable (1) or local (0). " "(RFC 4880, Section 5.2.3.11)" }, { "--revocable", sig_flag, "Mark this signature as revocable (1, revocations are ignored) " "or non-revocable (0). " "(RFC 4880, Section 5.2.3.12)" }, { "--trust-level", sig_trust_level, "Set the trust level. This takes two integer arguments (0-255): " "the trusted-introducer level and the degree of trust. " "(RFC 4880, Section 5.2.3.13.)" }, { "--trust-scope", sig_string_arg, "A regular expression that limits the scope of --trust-level. " "(RFC 4880, Section 5.2.3.14.)" }, { "--revocation-key", sig_revocation_key, "Specify a designated revoker. Takes two arguments: the class " "(normally 0x80 or 0xC0 (sensitive)) and the key id of the " "designatured revoker. May be given multiple times. " "(RFC 4880, Section 5.2.3.15)" }, { "--notation", sig_notation, "Add a human-readable notation of the form \"[!<]name=value\" where " "\"!\" means that the critical flag should be set and \"<\" means " "that VALUE is a file to read the data from. " "(RFC 4880, Section 5.2.3.16)" }, { "--notation-binary", sig_notation, "Add a binary notation of the form \"[!<]name=value\" where " "\"!\" means that the critical flag should be set and \"<\" means " "that VALUE is a file to read the data from. " "(RFC 4880, Section 5.2.3.16)" }, { "--key-server-preferences", sig_big_endian_arg, "Big-endian number encoding the keyserver preferences. " "(RFC 4880, Section 5.2.3.17)" }, { "--key-server", sig_string_arg, "The preferred keyserver. (RFC 4880, Section 5.2.3.18)" }, { "--primary-user-id", sig_flag, "Sets the primary user id flag. (RFC 4880, Section 5.2.3.19)" }, { "--policy-uri", sig_string_arg, "URI of a document that describes the issuer's signing policy. " "(RFC 4880, Section 5.2.3.20)" }, { "--key-flags", sig_big_endian_arg, "Big-endian number encoding the key flags. " "(RFC 4880, Section 5.2.3.21)" }, { "--signers-user-id", sig_string_arg, "The user id (as a string) responsible for the signing. " "(RFC 4880, Section 5.2.3.22)" }, { "--reason-for-revocation", sig_reason_for_revocation, "Takes two arguments: a reason for revocation code and a " "user-provided string. " "(RFC 4880, Section 5.2.3.23)" }, { "--features", sig_big_endian_arg, "Big-endian number encoding the feature flags. " "(RFC 4880, Section 5.2.3.24)" }, { "--signature-target", NULL, "Takes three arguments: the target signature's public key algorithm " " (as an integer), the hash algorithm (as an integer) and the hash " " (as a hexadecimal string). " "(RFC 4880, Section 5.2.3.25)" }, { "--embedded-signature", NULL, "An embedded signature. This must be immediately followed by a " "signature packet (created using --signature ...) or a filename " "containing the packet." "(RFC 4880, Section 5.2.3.26)" }, { "--hashed", NULL, "The following attributes will be placed in the hashed area of " "the signature. (This is the default and it reset at the end of" "each signature.)" }, { "--unhashed", NULL, "The following attributes will be placed in the unhashed area of " "the signature (and thus not integrity protected)." }, { "--corrupt", sig_corrupt, "Corrupt the signature." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --public-key $KEYID --user-id USERID \\\n" " --signature --class 0x10 --issuer $KEYID --issuer-keyid self \\\n" " | " GPG_NAME " --list-packets"} }; static int mksubpkt_callback (PKT_signature *sig, void *cookie) { struct signinfo *si = cookie; int i; if (si->key_expiration) { char buf[4]; buf[0] = (si->key_expiration >> 24) & 0xff; buf[1] = (si->key_expiration >> 16) & 0xff; buf[2] = (si->key_expiration >> 8) & 0xff; buf[3] = si->key_expiration & 0xff; build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4); } if (si->cipher_algorithms) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, si->cipher_algorithms, si->cipher_algorithms_len); if (si->digest_algorithms) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, si->digest_algorithms, si->digest_algorithms_len); if (si->compress_algorithms) build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, si->compress_algorithms, si->compress_algorithms_len); if (si->exportable_set) { char buf = si->exportable; build_sig_subpkt (sig, SIGSUBPKT_EXPORTABLE, &buf, 1); } if (si->trust_level_set) build_sig_subpkt (sig, SIGSUBPKT_TRUST, si->trust_args, sizeof (si->trust_args)); if (si->trust_scope) build_sig_subpkt (sig, SIGSUBPKT_REGEXP, si->trust_scope, strlen (si->trust_scope)); for (i = 0; i < si->nrevocation_keys; i ++) { struct revocation_key *revkey = &si->revocation_key[i]; gpg_error_t err = keygen_add_revkey (sig, revkey); if (err) { u32 keyid[2]; keyid_from_fingerprint (global_ctrl, revkey->fpr, 20, keyid); log_fatal ("adding revocation key %s: %s\n", keystr (keyid), gpg_strerror (err)); } } /* keygen_add_revkey sets revocable=0 so be sure to do this after adding the rev keys. */ if (si->revocable_set) { char buf = si->revocable; build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, &buf, 1); } keygen_add_notations (sig, si->notations); if (si->key_server_preferences) build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, si->key_server_preferences, si->key_server_preferences_len); if (si->key_server) build_sig_subpkt (sig, SIGSUBPKT_PREF_KS, si->key_server, strlen (si->key_server)); if (si->primary_user_id_set) { char buf = si->primary_user_id; build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, &buf, 1); } if (si->policy_uri) build_sig_subpkt (sig, SIGSUBPKT_POLICY, si->policy_uri, strlen (si->policy_uri)); if (si->key_flags) build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, si->key_flags, si->key_flags_len); if (si->signers_user_id) build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, si->signers_user_id, strlen (si->signers_user_id)); if (si->reason_for_revocation) { int len = 1 + strlen (si->reason_for_revocation); char *buf; buf = xmalloc (len); buf[0] = si->reason_for_revocation_code; memcpy (&buf[1], si->reason_for_revocation, len - 1); build_sig_subpkt (sig, SIGSUBPKT_REVOC_REASON, buf, len); xfree (buf); } if (si->features) build_sig_subpkt (sig, SIGSUBPKT_FEATURES, si->features, si->features_len); return 0; } static int signature (const char *option, int argc, char *argv[], void *cookie) { gpg_error_t err; iobuf_t out = cookie; struct signinfo si; int processed; PKT_public_key *pk; PKT_signature *sig; PACKET pkt; u32 keyid_orig[2], keyid[2]; (void) option; memset (&si, 0, sizeof (si)); memset (&pkt, 0, sizeof (pkt)); processed = process_options (option, major_options, sig_options, &si, global_options, NULL, argc, argv); if (ncomponents) { int pkttype = components[ncomponents - 1].pkttype; if (pkttype == PKT_PUBLIC_KEY) { if (! si.class) /* Direct key sig. */ si.class = 0x1F; } else if (pkttype == PKT_PUBLIC_SUBKEY) { if (! si.sk) si.sk = components[ncomponents - 1].pkt.public_key; if (! si.class) /* Subkey binding sig. */ si.class = 0x18; } else if (pkttype == PKT_USER_ID) { if (! si.uid) si.uid = components[ncomponents - 1].pkt.user_id; if (! si.class) /* Certification of a user id and public key packet. */ si.class = 0x10; } } pk = NULL; if (! si.pk || ! si.issuer_pk) /* No primary key specified. Default to the first one that we find. */ { int i; for (i = 0; i < ncomponents; i ++) if (components[i].pkttype == PKT_PUBLIC_KEY) { pk = components[i].pkt.public_key; break; } } if (! si.pk) { if (! pk) log_fatal ("%s: no primary key given and no primary key available", "--pk"); si.pk = pk; } if (! si.issuer_pk) { if (! pk) log_fatal ("%s: no issuer key given and no primary key available", "--issuer"); si.issuer_pk = pk; } if (si.class == 0x18 || si.class == 0x19 || si.class == 0x28) /* Requires the primary key and a subkey. */ { if (! si.sk) log_fatal ("sig class 0x%x requires a subkey (--sk)\n", si.class); } else if (si.class == 0x10 || si.class == 0x11 || si.class == 0x12 || si.class == 0x13 || si.class == 0x30) /* Requires the primary key and a user id. */ { if (! si.uid) log_fatal ("sig class 0x%x requires a uid (--uid)\n", si.class); } else if (si.class == 0x1F || si.class == 0x20) /* Just requires the primary key. */ ; else log_fatal ("Unsupported signature class: 0x%x\n", si.class); sig = xmalloc_clear (sizeof (*sig)); /* Save SI.ISSUER_PK->KEYID. */ keyid_copy (keyid_orig, pk_keyid (si.issuer_pk)); if (si.issuer_keyid[0] || si.issuer_keyid[1]) keyid_copy (si.issuer_pk->keyid, si.issuer_keyid); else if (si.issuer_keyid_self) { PKT_public_key *pripk = primary_key(); if (! pripk) log_fatal ("--issuer-keyid self given, but no primary key available.\n"); keyid_copy (si.issuer_pk->keyid, pk_keyid (pripk)); } /* The reuse of core gpg stuff by this tool is questionable when it * requires adding extra code to the actual gpg code. It does not * make sense to pass an extra parameter and in particular not given * that gpg already has opt.cert_digest_algo to override it. */ if (si.digest_algo) log_info ("note: digest algo can't be passed to make_keysig_packet\n"); /* Changing the issuer's key id is fragile. Check to make sure make_keysig_packet didn't recompute the keyid. */ keyid_copy (keyid, si.issuer_pk->keyid); err = make_keysig_packet (global_ctrl, &sig, si.pk, si.uid, si.sk, si.issuer_pk, si.class, si.timestamp, si.expiration, mksubpkt_callback, &si, NULL); log_assert (keyid_cmp (keyid, si.issuer_pk->keyid) == 0); if (err) log_fatal ("Generating signature: %s\n", gpg_strerror (err)); /* Restore SI.PK->KEYID. */ keyid_copy (si.issuer_pk->keyid, keyid_orig); if (si.corrupt) { /* Set the top 32-bits to 0xBAD0DEAD. */ int bits = gcry_mpi_get_nbits (sig->data[0]); gcry_mpi_t x = gcry_mpi_new (0); gcry_mpi_add_ui (x, x, 0xBAD0DEAD); gcry_mpi_lshift (x, x, bits > 32 ? bits - 32 : bits); gcry_mpi_clear_highbit (sig->data[0], bits > 32 ? bits - 32 : 0); gcry_mpi_add (sig->data[0], sig->data[0], x); gcry_mpi_release (x); } pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; err = build_packet (out, &pkt); if (err) log_fatal ("serializing public key packet: %s\n", gpg_strerror (err)); debug ("Wrote signature packet:\n"); dump_component (&pkt); free_seckey_enc (sig); release_kbnode (si.issuer_kb); xfree (si.revocation_key); return processed; } struct sk_esk_info { /* The cipher used for encrypting the session key (when a session key is used). */ int cipher; /* The cipher used for encryping the SED packet. */ int sed_cipher; /* S2K related data. */ int hash; int mode; int mode_set; byte salt[8]; int salt_set; int iterations; /* If applying the S2K function to the passphrase is the session key or if it is the decryption key for the session key. */ int s2k_is_session_key; /* Generate a new, random session key. */ int new_session_key; /* The unencrypted session key. */ int session_key_len; char *session_key; char *password; }; static int sk_esk_cipher (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "integer|IDEA|3DES|CAST5|BLOWFISH|AES|AES192|AES256|CAMELLIA128|CAMELLIA192|CAMELLIA256"; int cipher; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (strcasecmp (argv[0], "IDEA") == 0) cipher = CIPHER_ALGO_IDEA; else if (strcasecmp (argv[0], "3DES") == 0) cipher = CIPHER_ALGO_3DES; else if (strcasecmp (argv[0], "CAST5") == 0) cipher = CIPHER_ALGO_CAST5; else if (strcasecmp (argv[0], "BLOWFISH") == 0) cipher = CIPHER_ALGO_BLOWFISH; else if (strcasecmp (argv[0], "AES") == 0) cipher = CIPHER_ALGO_AES; else if (strcasecmp (argv[0], "AES192") == 0) cipher = CIPHER_ALGO_AES192; else if (strcasecmp (argv[0], "TWOFISH") == 0) cipher = CIPHER_ALGO_TWOFISH; else if (strcasecmp (argv[0], "CAMELLIA128") == 0) cipher = CIPHER_ALGO_CAMELLIA128; else if (strcasecmp (argv[0], "CAMELLIA192") == 0) cipher = CIPHER_ALGO_CAMELLIA192; else if (strcasecmp (argv[0], "CAMELLIA256") == 0) cipher = CIPHER_ALGO_CAMELLIA256; else { char *tail; int v; errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || ! valid_cipher (v)) log_fatal ("Invalid or unsupported value. Usage: %s %s\n", option, usage); cipher = v; } if (strcmp (option, "--cipher") == 0) { if (si->cipher) log_fatal ("%s given multiple times.", option); si->cipher = cipher; } else if (strcmp (option, "--sed-cipher") == 0) { if (si->sed_cipher) log_fatal ("%s given multiple times.", option); si->sed_cipher = cipher; } return 1; } static int sk_esk_mode (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "integer|simple|salted|iterated"; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->mode) log_fatal ("%s given multiple times.", option); if (strcasecmp (argv[0], "simple") == 0) si->mode = 0; else if (strcasecmp (argv[0], "salted") == 0) si->mode = 1; else if (strcasecmp (argv[0], "iterated") == 0) si->mode = 3; else { char *tail; int v; errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || ! (v == 0 || v == 1 || v == 3)) log_fatal ("Invalid or unsupported value. Usage: %s %s\n", option, usage); si->mode = v; } si->mode_set = 1; return 1; } static int sk_esk_hash_algorithm (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "integer|MD5|SHA1|RMD160|SHA256|SHA384|SHA512|SHA224"; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->hash) log_fatal ("%s given multiple times.", option); if (strcasecmp (argv[0], "MD5") == 0) si->hash = DIGEST_ALGO_MD5; else if (strcasecmp (argv[0], "SHA1") == 0) si->hash = DIGEST_ALGO_SHA1; else if (strcasecmp (argv[0], "RMD160") == 0) si->hash = DIGEST_ALGO_RMD160; else if (strcasecmp (argv[0], "SHA256") == 0) si->hash = DIGEST_ALGO_SHA256; else if (strcasecmp (argv[0], "SHA384") == 0) si->hash = DIGEST_ALGO_SHA384; else if (strcasecmp (argv[0], "SHA512") == 0) si->hash = DIGEST_ALGO_SHA512; else if (strcasecmp (argv[0], "SHA224") == 0) si->hash = DIGEST_ALGO_SHA224; else { char *tail; int v; errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || ! (v == DIGEST_ALGO_MD5 || v == DIGEST_ALGO_SHA1 || v == DIGEST_ALGO_RMD160 || v == DIGEST_ALGO_SHA256 || v == DIGEST_ALGO_SHA384 || v == DIGEST_ALGO_SHA512 || v == DIGEST_ALGO_SHA224)) log_fatal ("Invalid or unsupported value. Usage: %s %s\n", option, usage); si->hash = v; } return 1; } static int sk_esk_salt (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "16-HEX-CHARACTERS"; char *p = argv[0]; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->salt_set) log_fatal ("%s given multiple times.", option); if (p[0] == '0' && p[1] == 'x') p += 2; if (strlen (p) != 16) log_fatal ("%s: Salt must be exactly 16 hexadecimal characters (have: %zd)\n", option, strlen (p)); if (hex2bin (p, si->salt, sizeof (si->salt)) == -1) log_fatal ("%s: Salt must only contain hexadecimal characters\n", option); si->salt_set = 1; return 1; } static int sk_esk_iterations (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "ITERATION-COUNT"; char *tail; int v; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || v < 0) log_fatal ("%s: Non-negative integer expected.\n", option); si->iterations = v; return 1; } static int sk_esk_session_key (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "HEX-CHARACTERS|auto|none"; char *p = argv[0]; struct session_key sk; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (si->session_key || si->s2k_is_session_key || si->new_session_key) log_fatal ("%s given multiple times.", option); if (strcasecmp (p, "none") == 0) { si->s2k_is_session_key = 1; return 1; } if (strcasecmp (p, "new") == 0) { si->new_session_key = 1; return 1; } if (strcasecmp (p, "auto") == 0) return 1; sk = parse_session_key (option, p, 0); if (si->session_key) log_fatal ("%s given multiple times.", option); if (sk.algo) si->sed_cipher = sk.algo; si->session_key_len = sk.keylen; si->session_key = sk.key; return 1; } static int sk_esk_password (const char *option, int argc, char *argv[], void *cookie) { struct sk_esk_info *si = cookie; char *usage = "PASSWORD"; if (argc == 0) log_fatal ("Usage: --sk-esk %s\n", usage); if (si->password) log_fatal ("%s given multiple times.", option); si->password = xstrdup (argv[0]); return 1; } static struct option sk_esk_options[] = { { "--cipher", sk_esk_cipher, "The encryption algorithm for encrypting the session key. " "One of IDEA, 3DES, CAST5, BLOWFISH, AES (default), AES192, " "AES256, TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256." }, { "--sed-cipher", sk_esk_cipher, "The encryption algorithm for encrypting the SED packet. " "One of IDEA, 3DES, CAST5, BLOWFISH, AES, AES192, " "AES256 (default), TWOFISH, CAMELLIA128, CAMELLIA192, or CAMELLIA256." }, { "--mode", sk_esk_mode, "The S2K mode. Either one of the strings \"simple\", \"salted\" " "or \"iterated\" or an integer." }, { "--hash", sk_esk_hash_algorithm, "The hash algorithm to used to derive the key. One of " "MD5, SHA1 (default), RMD160, SHA256, SHA384, SHA512, or SHA224." }, { "--salt", sk_esk_salt, "The S2K salt encoded as 16 hexadecimal characters. One needed " "if the S2K function is in salted or iterated mode." }, { "--iterations", sk_esk_iterations, "The iteration count. If not provided, a reasonable value is chosen. " "Note: due to the encoding scheme, not every value is valid. For " "convenience, the provided value will be rounded appropriately. " "Only needed if the S2K function is in iterated mode." }, { "--session-key", sk_esk_session_key, "The session key to be encrypted by the S2K function as a hexadecimal " "string. If this is \"new\", then a new session key is generated." "If this is \"auto\", then either the last session key is " "used, if the was none, one is generated. If this is \"none\", then " "the session key is the result of applying the S2K algorithms to the " "password. The session key may be prefaced with an integer and a colon " "to indicate the cipher to use for the SED packet (making --sed-cipher " "unnecessary and allowing the direct use of the result of " "\"" GPG_NAME " --show-session-key\")." }, { "", sk_esk_password, "The password." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --sk-esk foobar --encrypted \\\n" " --literal --value foo | " GPG_NAME " --list-packets" } }; static int sk_esk (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; int processed; struct sk_esk_info si; DEK sesdek; DEK s2kdek; PKT_symkey_enc *ske; PACKET pkt; memset (&si, 0, sizeof (si)); processed = process_options (option, major_options, sk_esk_options, &si, global_options, NULL, argc, argv); if (! si.password) log_fatal ("%s: missing password. Usage: %s PASSWORD", option, option); /* Fill in defaults, if appropriate. */ if (! si.cipher) si.cipher = CIPHER_ALGO_AES; if (! si.sed_cipher) si.sed_cipher = CIPHER_ALGO_AES256; if (! si.hash) si.hash = DIGEST_ALGO_SHA1; if (! si.mode_set) /* Salted and iterated. */ si.mode = 3; if (si.mode != 0 && ! si.salt_set) /* Generate a salt. */ gcry_randomize (si.salt, 8, GCRY_STRONG_RANDOM); if (si.mode == 0) { if (si.iterations) log_info ("%s: --iterations provided, but not used for mode=0\n", option); si.iterations = 0; } else if (! si.iterations) si.iterations = 10000; memset (&sesdek, 0, sizeof (sesdek)); /* The session key is used to encrypt the SED packet. */ sesdek.algo = si.sed_cipher; if (si.session_key) /* Copy the unencrypted session key into SESDEK. */ { sesdek.keylen = openpgp_cipher_get_algo_keylen (sesdek.algo); if (sesdek.keylen != si.session_key_len) log_fatal ("%s: Cipher algorithm requires a %d byte session key, but provided session key is %d bytes.", option, sesdek.keylen, si.session_key_len); log_assert (sesdek.keylen <= sizeof (sesdek.key)); memcpy (sesdek.key, si.session_key, sesdek.keylen); } else if (! si.s2k_is_session_key || si.new_session_key) /* We need a session key, but one wasn't provided. Generate it. */ make_session_key (&sesdek); /* The encrypted session key needs 1 + SESDEK.KEYLEN bytes of space. */ ske = xmalloc_clear (sizeof (*ske) + sesdek.keylen); ske->version = 4; ske->cipher_algo = si.cipher; ske->s2k.mode = si.mode; ske->s2k.hash_algo = si.hash; log_assert (sizeof (si.salt) == sizeof (ske->s2k.salt)); memcpy (ske->s2k.salt, si.salt, sizeof (ske->s2k.salt)); if (! si.s2k_is_session_key) { if (!si.iterations) ske->s2k.count = encode_s2k_iterations (agent_get_s2k_count ()); else ske->s2k.count = encode_s2k_iterations (si.iterations); } /* Derive the symmetric key that is either the session key or the key used to encrypt the session key. */ memset (&s2kdek, 0, sizeof (s2kdek)); s2kdek.algo = si.cipher; s2kdek.keylen = openpgp_cipher_get_algo_keylen (s2kdek.algo); err = gcry_kdf_derive (si.password, strlen (si.password), ske->s2k.mode == 3 ? GCRY_KDF_ITERSALTED_S2K : ske->s2k.mode == 1 ? GCRY_KDF_SALTED_S2K : GCRY_KDF_SIMPLE_S2K, ske->s2k.hash_algo, ske->s2k.salt, 8, S2K_DECODE_COUNT (ske->s2k.count), /* The size of the desired key and its buffer. */ s2kdek.keylen, s2kdek.key); if (err) log_fatal ("gcry_kdf_derive failed: %s", gpg_strerror (err)); if (si.s2k_is_session_key) { ske->seskeylen = 0; session_key = s2kdek; } else /* Encrypt the session key using the s2k specifier. */ { DEK *sesdekp = &sesdek; void *enckey; size_t enckeylen; /* Now encrypt the session key (or rather, the algorithm used to encrypt the SKESK plus the session key) using S2KDEK. */ err = encrypt_seskey (&s2kdek, 0, &sesdekp, &enckey, &enckeylen); if (err) log_fatal ("encrypt_seskey failed: %s\n", gpg_strerror (err)); if (enckeylen - 1 > sesdek.keylen) log_fatal ("key size is too big: %zu\n", enckeylen); else { ske->seskeylen = (byte)enckeylen; memcpy (ske->seskey, enckey, enckeylen); } /* Save the session key for later. */ session_key = sesdek; xfree (enckey); } pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = ske; err = build_packet (out, &pkt); if (err) log_fatal ("Serializing sym-key encrypted packet: %s\n", gpg_strerror (err)); debug ("Wrote sym-key encrypted packet:\n"); dump_component (&pkt); xfree (si.session_key); xfree (si.password); xfree (ske); return processed; } struct pk_esk_info { int session_key_set; int new_session_key; int sed_cipher; int session_key_len; char *session_key; int throw_keyid; char *keyid; }; static int pk_esk_session_key (const char *option, int argc, char *argv[], void *cookie) { struct pk_esk_info *pi = cookie; char *usage = "HEX-CHARACTERS|auto|none"; char *p = argv[0]; struct session_key sk; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (pi->session_key_set) log_fatal ("%s given multiple times.", option); pi->session_key_set = 1; if (strcasecmp (p, "new") == 0) { pi->new_session_key = 1; return 1; } if (strcasecmp (p, "auto") == 0) return 1; sk = parse_session_key (option, p, 0); if (pi->session_key) log_fatal ("%s given multiple times.", option); if (sk.algo) pi->sed_cipher = sk.algo; pi->session_key_len = sk.keylen; pi->session_key = sk.key; return 1; } static int pk_esk_throw_keyid (const char *option, int argc, char *argv[], void *cookie) { struct pk_esk_info *pi = cookie; (void) option; (void) argc; (void) argv; pi->throw_keyid = 1; return 0; } static int pk_esk_keyid (const char *option, int argc, char *argv[], void *cookie) { struct pk_esk_info *pi = cookie; char *usage = "KEYID"; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (pi->keyid) log_fatal ("Multiple key ids given, but only one is allowed."); pi->keyid = xstrdup (argv[0]); return 1; } static struct option pk_esk_options[] = { { "--session-key", pk_esk_session_key, "The session key to be encrypted by the S2K function as a hexadecimal " "string. If this is not given or is \"auto\", then the current " "session key is used. If there is no session key or this is \"new\", " "then a new session key is generated. The session key may be " "prefaced with an integer and a colon to indicate the cipher to use " "for the SED packet (making --sed-cipher unnecessary and allowing the " "direct use of the result of \"" GPG_NAME " --show-session-key\")." }, { "--throw-keyid", pk_esk_throw_keyid, "Throw the keyid." }, { "", pk_esk_keyid, "The key id." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --pk-esk $KEYID --encrypted --literal --value foo \\\n" " | " GPG_NAME " --list-packets"} }; static int pk_esk (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; int processed; struct pk_esk_info pi; PKT_public_key pk; memset (&pi, 0, sizeof (pi)); processed = process_options (option, major_options, pk_esk_options, &pi, global_options, NULL, argc, argv); if (! pi.keyid) log_fatal ("%s: missing keyid. Usage: %s KEYID", option, option); memset (&pk, 0, sizeof (pk)); pk.req_usage = PUBKEY_USAGE_ENC; err = get_pubkey_byname (NULL, GET_PUBKEY_NO_AKL, NULL, &pk, pi.keyid, NULL, NULL, 1); if (err) log_fatal ("%s: looking up key %s: %s\n", option, pi.keyid, gpg_strerror (err)); if (pi.sed_cipher) /* Have a session key. */ { session_key.algo = pi.sed_cipher; session_key.keylen = pi.session_key_len; log_assert (session_key.keylen <= sizeof (session_key.key)); memcpy (session_key.key, pi.session_key, session_key.keylen); } if (pi.new_session_key || ! session_key.algo) { if (! pi.new_session_key) /* Default to AES256. */ session_key.algo = CIPHER_ALGO_AES256; make_session_key (&session_key); } err = write_pubkey_enc (global_ctrl, &pk, pi.throw_keyid, &session_key, out); if (err) log_fatal ("%s: writing pk_esk packet for %s: %s\n", option, pi.keyid, gpg_strerror (err)); debug ("Wrote pk_esk packet for %s\n", pi.keyid); xfree (pi.keyid); xfree (pi.session_key); return processed; } struct encinfo { int saw_session_key; }; static int encrypted_session_key (const char *option, int argc, char *argv[], void *cookie) { struct encinfo *ei = cookie; char *usage = "HEX-CHARACTERS|auto"; char *p = argv[0]; struct session_key sk; if (argc == 0) log_fatal ("Usage: %s %s\n", option, usage); if (ei->saw_session_key) log_fatal ("%s given multiple times.", option); ei->saw_session_key = 1; if (strcasecmp (p, "auto") == 0) return 1; sk = parse_session_key (option, p, 1); session_key.algo = sk.algo; log_assert (sk.keylen <= sizeof (session_key.key)); memcpy (session_key.key, sk.key, sk.keylen); xfree (sk.key); return 1; } static struct option encrypted_options[] = { { "--session-key", encrypted_session_key, "The session key to be encrypted by the S2K function as a hexadecimal " "string. If this is not given or is \"auto\", then the last session key " "is used. If there was none, then an error is raised. The session key " "must be prefaced with an integer and a colon to indicate the cipher " "to use (this is format used by \"" GPG_NAME " --show-session-key\")." }, { NULL, NULL, "After creating the packet, this command clears the current " "session key.\n\n" "Example: nested encryption packets:\n\n" " $ gpgcompose --sk-esk foo --encrypted-mdc \\\n" " --sk-esk bar --encrypted-mdc \\\n" " --literal --value 123 --encrypted-pop --encrypted-pop | " GPG_NAME" -d" } }; static int encrypted (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; int processed; struct encinfo ei; PKT_encrypted e; cipher_filter_context_t *cfx; memset (&ei, 0, sizeof (ei)); processed = process_options (option, major_options, encrypted_options, &ei, global_options, NULL, argc, argv); if (! session_key.algo) log_fatal ("%s: no session key configured\n" " (use e.g. --sk-esk PASSWORD or --pk-esk KEYID).\n", option); memset (&e, 0, sizeof (e)); /* We only need to set E->LEN, E->EXTRALEN (if E->LEN is not 0), and E->NEW_CTB. */ e.len = 0; e.new_ctb = 1; /* Register the cipher filter. */ cfx = xmalloc_clear (sizeof (*cfx)); /* Copy the session key. */ cfx->dek = xmalloc (sizeof (*cfx->dek)); *cfx->dek = session_key; if (do_debug) { char *buf; buf = xmalloc (2 * session_key.keylen + 1); debug ("session key: algo: %d; keylen: %d; key: %s\n", session_key.algo, session_key.keylen, bin2hex (session_key.key, session_key.keylen, buf)); xfree (buf); } if (strcmp (option, "--encrypted-mdc") == 0) cfx->dek->use_mdc = 1; else if (strcmp (option, "--encrypted") == 0) cfx->dek->use_mdc = 0; else log_fatal ("%s: option not handled by this function!\n", option); cfx->datalen = 0; filter_push (out, cipher_filter_cfb, cfx, PKT_ENCRYPTED, cfx->datalen == 0); debug ("Wrote encrypted packet:\n"); /* Clear the current session key. */ memset (&session_key, 0, sizeof (session_key)); return processed; } static struct option encrypted_pop_options[] = { { NULL, NULL, "Example:\n\n" " $ gpgcompose --sk-esk PASSWORD \\\n" " --encrypted-mdc \\\n" " --literal --value foo \\\n" " --encrypted-pop | " GPG_NAME " --list-packets" } }; static int encrypted_pop (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; int processed; processed = process_options (option, major_options, encrypted_pop_options, NULL, global_options, NULL, argc, argv); /* We only support a single option, --help, which causes the program * to exit. */ log_assert (processed == 0); filter_pop (out, PKT_ENCRYPTED); debug ("Popped encryption container.\n"); return processed; } struct data { int file; union { char *data; char *filename; }; struct data *next; }; /* This must be the first member of the struct to be able to use add_value! */ struct datahead { struct data *head; struct data **last_next; }; static int add_value (const char *option, int argc, char *argv[], void *cookie) { struct datahead *dh = cookie; struct data *d = xmalloc_clear (sizeof (struct data)); d->file = strcmp ("--file", option) == 0; if (! d->file) log_assert (strcmp ("--value", option) == 0); if (argc == 0) { if (d->file) log_fatal ("Usage: %s FILENAME\n", option); else log_fatal ("Usage: %s STRING\n", option); } if (! dh->last_next) /* First time through. Initialize DH->LAST_NEXT. */ { log_assert (! dh->head); dh->last_next = &dh->head; } if (d->file) d->filename = argv[0]; else d->data = argv[0]; /* Append it. */ *dh->last_next = d; dh->last_next = &d->next; return 1; } struct litinfo { /* This must be the first element for add_value to work! */ struct datahead data; int timestamp_set; u32 timestamp; char mode; int partial_body_length_encoding; char *name; }; static int literal_timestamp (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; char *tail = NULL; if (argc == 0) log_fatal ("Usage: %s TIMESTAMP\n", option); errno = 0; li->timestamp = parse_timestamp (argv[0], &tail); if (errno || (tail && *tail)) log_fatal ("Invalid value passed to %s (%s)\n", option, argv[0]); li->timestamp_set = 1; return 1; } static int literal_mode (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; if (argc == 0 || ! (strcmp (argv[0], "b") == 0 || strcmp (argv[0], "t") == 0 || strcmp (argv[0], "u") == 0)) log_fatal ("Usage: %s [btu]\n", option); li->mode = argv[0][0]; return 1; } static int literal_partial_body_length (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; char *tail; int v; int range[2] = {0, 1}; if (argc <= 1) log_fatal ("Usage: %s [0|1]\n", option); errno = 0; v = strtol (argv[0], &tail, 0); if (errno || (tail && *tail) || !(range[0] <= v && v <= range[1])) log_fatal ("Invalid value passed to %s (%s). Expected %d-%d\n", option, argv[0], range[0], range[1]); li->partial_body_length_encoding = v; return 1; } static int literal_name (const char *option, int argc, char *argv[], void *cookie) { struct litinfo *li = cookie; if (argc <= 0) log_fatal ("Usage: %s NAME\n", option); if (strlen (argv[0]) > 255) log_fatal ("%s: name is too long (%zd > 255 characters).\n", option, strlen (argv[0])); li->name = argv[0]; return 1; } static struct option literal_options[] = { { "--value", add_value, "A string to store in the literal packet." }, { "--file", add_value, "A file to copy into the literal packet." }, { "--timestamp", literal_timestamp, "The literal packet's time stamp. This defaults to the current time." }, { "--mode", literal_mode, "The content's mode (normally 'b' (default), 't' or 'u')." }, { "--partial-body-length", literal_partial_body_length, "Force partial body length encoding." }, { "--name", literal_name, "The literal's name." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --literal --value foobar | " GPG_NAME " -d"} }; static int literal (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; gpg_error_t err; int processed; struct litinfo li; PKT_plaintext *pt; PACKET pkt; struct data *data; memset (&li, 0, sizeof (li)); processed = process_options (option, major_options, literal_options, &li, global_options, NULL, argc, argv); if (! li.data.head) log_fatal ("%s: no data provided (use --value or --file)", option); pt = xmalloc_clear (sizeof (*pt) + (li.name ? strlen (li.name) : 0)); pt->new_ctb = 1; if (li.timestamp_set) pt->timestamp = li.timestamp; else /* Default to the current time. */ pt->timestamp = make_timestamp (); pt->mode = li.mode; if (! pt->mode) /* Default to binary. */ pt->mode = 'b'; if (li.name) { strcpy (pt->name, li.name); pt->namelen = strlen (pt->name); } pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; if (! li.partial_body_length_encoding) /* Compute the amount of data. */ { pt->len = 0; for (data = li.data.head; data; data = data->next) { if (data->file) { iobuf_t in; int overflow; off_t off; in = iobuf_open (data->filename); if (! in) /* An error opening the file. We do error handling below so just break here. */ { pt->len = 0; break; } off = iobuf_get_filelength (in, &overflow); iobuf_close (in); if (overflow || off == 0) /* Length is unknown or there was an error (unfortunately, iobuf_get_filelength doesn't distinguish between 0 length files and an error!). Fall back to partial body mode. */ { pt->len = 0; break; } pt->len += off; } else pt->len += strlen (data->data); } } err = build_packet (out, &pkt); if (err) log_fatal ("Serializing literal packet: %s\n", gpg_strerror (err)); /* Write out the data. */ for (data = li.data.head; data; data = data->next) { if (data->file) { iobuf_t in; errno = 0; in = iobuf_open (data->filename); if (! in) log_fatal ("Opening '%s': %s\n", data->filename, errno ? strerror (errno): "unknown error"); iobuf_copy (out, in); if (iobuf_error (in)) log_fatal ("Reading from %s: %s\n", data->filename, gpg_strerror (iobuf_error (in))); if (iobuf_error (out)) log_fatal ("Writing literal data from %s: %s\n", data->filename, gpg_strerror (iobuf_error (out))); iobuf_close (in); } else { err = iobuf_write (out, data->data, strlen (data->data)); if (err) log_fatal ("Writing literal data: %s\n", gpg_strerror (err)); } } if (! pt->len) { /* Disable partial body length mode. */ log_assert (pt->new_ctb == 1); iobuf_set_partial_body_length_mode (out, 0); } debug ("Wrote literal packet:\n"); dump_component (&pkt); while (li.data.head) { data = li.data.head->next; xfree (li.data.head); li.data.head = data; } xfree (pt); return processed; } static int copy_file (const char *option, int argc, char *argv[], void *cookie) { char **filep = cookie; if (argc == 0) log_fatal ("Usage: %s FILENAME\n", option); *filep = argv[0]; return 1; } static struct option copy_options[] = { { "", copy_file, "Copy the specified file to stdout." }, { NULL, NULL, "Example:\n\n" " $ gpgcompose --copy /etc/hostname\n\n" "This is particularly useful when combined with gpgsplit." } }; static int copy (const char *option, int argc, char *argv[], void *cookie) { iobuf_t out = cookie; char *file = NULL; iobuf_t in; int processed; processed = process_options (option, major_options, copy_options, &file, global_options, NULL, argc, argv); if (! file) log_fatal ("Usage: %s FILE\n", option); errno = 0; in = iobuf_open (file); if (! in) log_fatal ("Error opening %s: %s.\n", file, errno ? strerror (errno): "unknown error"); iobuf_copy (out, in); if (iobuf_error (out)) log_fatal ("Copying data to destination: %s\n", gpg_strerror (iobuf_error (out))); if (iobuf_error (in)) log_fatal ("Reading data from %s: %s\n", argv[0], gpg_strerror (iobuf_error (in))); iobuf_close (in); return processed; } int main (int argc, char *argv[]) { const char *filename = "-"; iobuf_t out; int preprocessed = 1; int processed; ctrl_t ctrl; opt.ignore_time_conflict = 1; /* Allow notations in the IETF space, for instance. */ opt.expert = 1; global_ctrl = ctrl = xcalloc (1, sizeof *ctrl); keydb_add_resource ("pubring" EXTSEP_S GPGEXT_GPG, KEYDB_RESOURCE_FLAG_DEFAULT); if (argc == 1) /* Nothing to do. */ return 0; if (strcmp (argv[1], "--output") == 0 || strcmp (argv[1], "-o") == 0) { filename = argv[2]; log_info ("Writing to %s\n", filename); preprocessed += 2; } out = iobuf_create (filename, 0); if (! out) log_fatal ("Failed to open stdout for writing\n"); processed = process_options (NULL, NULL, major_options, out, global_options, NULL, argc - preprocessed, &argv[preprocessed]); if (processed != argc - preprocessed) log_fatal ("Didn't process %d options.\n", argc - preprocessed - processed); iobuf_close (out); return 0; } /* Stubs duplicated from gpg.c. */ int g10_errors_seen = 0; /* Note: This function is used by signal handlers!. */ static void emergency_cleanup (void) { gcry_control (GCRYCTL_TERM_SECMEM ); } void g10_exit( int rc ) { gcry_control (GCRYCTL_UPDATE_RANDOM_SEED_FILE); emergency_cleanup (); rc = rc? rc : log_get_errorcount(0)? 2 : g10_errors_seen? 1 : 0; exit (rc); } void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check) { (void) ctrl; (void) username; (void) locusr; (void) commands; (void) quiet; (void) seckey_check; } void show_basic_key_info (ctrl_t ctrl, KBNODE keyblock, int made_from_sec) { (void)ctrl; (void)keyblock; (void)made_from_sec; } int keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, int rc, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int is_selfsig, int print_without_key, int extended) { (void) ctrl; (void) fp; (void) rc; (void) keyblock; (void) node; (void) inv_sigs; (void) no_key; (void) oth_err; (void) is_selfsig; (void) print_without_key; (void) extended; return 0; } diff --git a/g10/import.c b/g10/import.c index c32dbf059..cab36e0b2 100644 --- a/g10/import.c +++ b/g10/import.c @@ -1,4534 +1,4535 @@ /* import.c - import a key into our key storage. * Copyright (C) 1998-2007, 2010-2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2016, 2017, 2019 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "../common/util.h" #include "trustdb.h" #include "main.h" #include "../common/i18n.h" #include "../common/ttyio.h" #include "../common/recsel.h" #include "keyserver-internal.h" #include "call-agent.h" #include "../common/membuf.h" #include "../common/init.h" #include "../common/mbox-util.h" #include "key-check.h" #include "key-clean.h" struct import_stats_s { ulong count; ulong no_user_id; ulong imported; ulong n_uids; ulong n_sigs; ulong n_subk; ulong unchanged; ulong n_revoc; ulong secret_read; ulong secret_imported; ulong secret_dups; ulong skipped_new_keys; ulong not_imported; ulong n_sigs_cleaned; ulong n_uids_cleaned; ulong v3keys; /* Number of V3 keys seen. */ }; /* Node flag to indicate that a user ID or a subkey has a * valid self-signature. */ #define NODE_GOOD_SELFSIG 1 /* Node flag to indicate that a user ID or subkey has * an invalid self-signature. */ #define NODE_BAD_SELFSIG 2 /* Node flag to indicate that the node shall be deleted. */ #define NODE_DELETION_MARK 4 /* A node flag used to temporary mark a node. */ #define NODE_FLAG_A 8 /* A flag used by transfer_secret_keys. */ #define NODE_TRANSFER_SECKEY 16 /* An object and a global instance to store selectors created from * --import-filter keep-uid=EXPR. * --import-filter drop-sig=EXPR. * * FIXME: We should put this into the CTRL object but that requires a * lot more changes right now. For now we use save and restore * function to temporary change them. */ /* Definition of the import filters. */ struct import_filter_s { recsel_expr_t keep_uid; recsel_expr_t drop_sig; }; /* The current instance. */ struct import_filter_s import_filter; static int import (ctrl_t ctrl, IOBUF inp, const char* fname, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url); static int read_block (IOBUF a, unsigned int options, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys); static void revocation_present (ctrl_t ctrl, kbnode_t keyblock); static gpg_error_t import_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, int origin, const char *url, int *r_valid); static gpg_error_t import_matching_seckeys ( ctrl_t ctrl, kbnode_t seckeys, const byte *mainfpr, size_t mainfprlen, struct import_stats_s *stats, int batch); static gpg_error_t import_secret_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, int batch, unsigned int options, int for_migration, import_screener_t screener, void *screener_arg, kbnode_t *r_secattic); static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, struct import_stats_s *stats); static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self); static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, unsigned int options); static int any_uid_left (kbnode_t keyblock); static int remove_all_uids (kbnode_t *keyblock); static void remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid); static int merge_blocks (ctrl_t ctrl, unsigned int options, kbnode_t keyblock_orig, kbnode_t keyblock, u32 *keyid, u32 curtime, int origin, const char *url, int *n_uids, int *n_sigs, int *n_subk ); static gpg_error_t append_new_uid (unsigned int options, kbnode_t keyblock, kbnode_t node, u32 curtime, int origin, const char *url, int *n_sigs); static int append_key (kbnode_t keyblock, kbnode_t node, int *n_sigs); static int merge_sigs (kbnode_t dst, kbnode_t src, int *n_sigs); static int merge_keysigs (kbnode_t dst, kbnode_t src, int *n_sigs); static void release_import_filter (import_filter_t filt) { recsel_release (filt->keep_uid); filt->keep_uid = NULL; recsel_release (filt->drop_sig); filt->drop_sig = NULL; } static void cleanup_import_globals (void) { release_import_filter (&import_filter); } int parse_import_options(char *str,unsigned int *options,int noisy) { struct parse_options import_opts[]= { {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL, N_("import signatures that are marked as local-only")}, {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL, N_("repair damage from the pks keyserver during import")}, {"keep-ownertrust", IMPORT_KEEP_OWNERTTRUST, NULL, N_("do not clear the ownertrust values during import")}, {"fast-import",IMPORT_FAST,NULL, N_("do not update the trustdb after import")}, {"import-show",IMPORT_SHOW,NULL, N_("show key during import")}, {"merge-only",IMPORT_MERGE_ONLY,NULL, N_("only accept updates to existing keys")}, {"import-clean",IMPORT_CLEAN,NULL, N_("remove unusable parts from key after import")}, {"import-minimal",IMPORT_MINIMAL|IMPORT_CLEAN,NULL, N_("remove as much as possible from key after import")}, {"import-drop-uids", IMPORT_DROP_UIDS, NULL, N_("do not import user id or attribute packets")}, {"self-sigs-only", IMPORT_SELF_SIGS_ONLY, NULL, N_("ignore key-signatures which are not self-signatures")}, {"import-export", IMPORT_EXPORT, NULL, N_("run import filters and export key immediately")}, {"restore", IMPORT_RESTORE, NULL, N_("assume the GnuPG key backup format")}, {"import-restore", IMPORT_RESTORE, NULL, NULL}, {"repair-keys", IMPORT_REPAIR_KEYS, NULL, N_("repair keys on import")}, /* No description to avoid string change: Fixme for 2.3 */ {"show-only", (IMPORT_SHOW | IMPORT_DRY_RUN), NULL, NULL}, /* Aliases for backward compatibility */ {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL}, {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL}, /* dummy */ {"import-unusable-sigs",0,NULL,NULL}, {"import-clean-sigs",0,NULL,NULL}, {"import-clean-uids",0,NULL,NULL}, {"convert-sk-to-pk",0, NULL,NULL}, /* Not anymore needed due to the new design. */ {NULL,0,NULL,NULL} }; int rc; rc = parse_options (str, options, import_opts, noisy); if (rc && (*options & IMPORT_RESTORE)) { /* Alter other options we want or don't want for restore. */ *options |= (IMPORT_LOCAL_SIGS | IMPORT_KEEP_OWNERTTRUST); *options &= ~(IMPORT_MINIMAL | IMPORT_CLEAN | IMPORT_REPAIR_PKS_SUBKEY_BUG | IMPORT_MERGE_ONLY); } return rc; } /* Parse and set an import 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: * * - keep-uid :: If the expression evaluates to true for a certain * user ID packet, that packet and all it dependencies * will be imported. The expression may use these * variables: * * - uid :: The entire user ID. * - mbox :: The mail box part of the user ID. * - primary :: Evaluate to true for the primary user ID. */ gpg_error_t parse_and_set_import_filter (const char *string) { gpg_error_t err; /* Auto register the cleanup function. */ register_mem_cleanup_func (cleanup_import_globals); if (!strncmp (string, "keep-uid=", 9)) err = recsel_parse_expr (&import_filter.keep_uid, string+9); else if (!strncmp (string, "drop-sig=", 9)) err = recsel_parse_expr (&import_filter.drop_sig, string+9); else err = gpg_error (GPG_ERR_INV_NAME); return err; } /* Save the current import filters, return them, and clear the current * filters. Returns NULL on error and sets ERRNO. */ import_filter_t save_and_clear_import_filter (void) { import_filter_t filt; filt = xtrycalloc (1, sizeof *filt); if (!filt) return NULL; *filt = import_filter; memset (&import_filter, 0, sizeof import_filter); return filt; } /* Release the current import filters and restore them from NEWFILT. * Ownership of NEWFILT is moved to this function. */ void restore_import_filter (import_filter_t filt) { if (filt) { release_import_filter (&import_filter); import_filter = *filt; xfree (filt); } } import_stats_t import_new_stats_handle (void) { return xmalloc_clear ( sizeof (struct import_stats_s) ); } void import_release_stats_handle (import_stats_t p) { xfree (p); } /* Read a key from a file. Only the first key in the file is * considered and stored at R_KEYBLOCK. FNAME is the name of the * file. */ gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock) { gpg_error_t err; iobuf_t inp; PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; u32 keyid[2]; int v3keys; /* Dummy */ int non_self; /* Dummy */ (void)ctrl; *r_keyblock = NULL; inp = iobuf_open (fname); if (!inp) err = gpg_error_from_syserror (); else if (is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; err = gpg_error (GPG_ERR_EPERM); } else err = 0; if (err) { log_error (_("can't open '%s': %s\n"), iobuf_is_pipe_filename (fname)? "[stdin]": fname, gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_ENOENT) err = gpg_error (GPG_ERR_NO_PUBKEY); goto leave; } /* Push the armor filter. */ { armor_filter_context_t *afx; afx = new_armor_context (); afx->only_keyblocks = 1; push_armor_filter (afx, inp); release_armor_context (afx); } /* Read the first non-v3 keyblock. */ while (!(err = read_block (inp, 0, &pending_pkt, &keyblock, &v3keys))) { if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) break; log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype); release_kbnode (keyblock); keyblock = NULL; } if (err) { if (gpg_err_code (err) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), iobuf_is_pipe_filename (fname)? "[stdin]": fname, gpg_strerror (err)); goto leave; } keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); if (!find_next_kbnode (keyblock, PKT_USER_ID)) { err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; } collapse_uids (&keyblock); clear_kbnode_flags (keyblock); if (chk_self_sigs (ctrl, keyblock, keyid, &non_self)) { err = gpg_error (GPG_ERR_INV_KEYRING); goto leave; } if (!delete_inv_parts (ctrl, keyblock, keyid, 0) ) { err = gpg_error (GPG_ERR_NO_USER_ID); goto leave; } *r_keyblock = keyblock; keyblock = NULL; leave: if (inp) { iobuf_close (inp); /* Must invalidate that ugly cache to actually close the file. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); } release_kbnode (keyblock); /* FIXME: Do we need to free PENDING_PKT ? */ return err; } /* * Import the public keys from the given filename. Input may be armored. * This function rejects all keys which are not validly self signed on at * least one userid. Only user ids which are self signed will be imported. * Other signatures are not checked. * * Actually this function does a merge. It works like this: * * - get the keyblock * - check self-signatures and remove all userids and their signatures * without/invalid self-signatures. * - reject the keyblock, if we have no valid userid. * - See whether we have this key already in one of our pubrings. * If not, simply add it to the default keyring. * - Compare the key and the self-signatures of the new and the one in * our keyring. If they are different something weird is going on; * ask what to do. * - See whether we have only non-self-signature on one user id; if not * ask the user what to do. * - compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * (consider looking at the timestamp and use the newest?) * - Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * - Proceed with next signature. * * Key revocation certificates have special handling. */ static gpg_error_t import_keys_internal (ctrl_t ctrl, iobuf_t inp, char **fnames, int nnames, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url) { int i; gpg_error_t err = 0; struct import_stats_s *stats = stats_handle; if (!stats) stats = import_new_stats_handle (); if (inp) { err = import (ctrl, inp, "[stream]", stats, fpr, fpr_len, options, screener, screener_arg, origin, url); } else { if (!fnames && !nnames) nnames = 1; /* Ohh what a ugly hack to jump into the loop */ for (i=0; i < nnames; i++) { const char *fname = fnames? fnames[i] : NULL; IOBUF inp2 = iobuf_open(fname); if (!fname) fname = "[stdin]"; if (inp2 && is_secured_file (iobuf_get_fd (inp2))) { iobuf_close (inp2); inp2 = NULL; gpg_err_set_errno (EPERM); } if (!inp2) log_error (_("can't open '%s': %s\n"), fname, strerror (errno)); else { err = import (ctrl, inp2, fname, stats, fpr, fpr_len, options, screener, screener_arg, origin, url); iobuf_close (inp2); /* Must invalidate that ugly cache to actually close it. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); if (err) log_error ("import from '%s' failed: %s\n", fname, gpg_strerror (err) ); } if (!fname) break; } } if (!stats_handle) { if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN)) != (IMPORT_SHOW | IMPORT_DRY_RUN)) import_print_stats (stats); import_release_stats_handle (stats); } /* If no fast import and the trustdb is dirty (i.e. we added a key or userID that had something other than a selfsig, a signature that was other than a selfsig, or any revocation), then update/check the trustdb if the user specified by setting interactive or by not setting no-auto-check-trustdb */ if (!(options & IMPORT_FAST)) check_or_update_trustdb (ctrl); return err; } void import_keys (ctrl_t ctrl, char **fnames, int nnames, import_stats_t stats_handle, unsigned int options, int origin, const char *url) { import_keys_internal (ctrl, NULL, fnames, nnames, stats_handle, NULL, NULL, options, NULL, NULL, origin, url); } gpg_error_t import_keys_es_stream (ctrl_t ctrl, estream_t fp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url) { gpg_error_t err; iobuf_t inp; inp = iobuf_esopen (fp, "rb", 1); if (!inp) { err = gpg_error_from_syserror (); log_error ("iobuf_esopen failed: %s\n", gpg_strerror (err)); return err; } err = import_keys_internal (ctrl, inp, NULL, 0, stats_handle, fpr, fpr_len, options, screener, screener_arg, origin, url); iobuf_close (inp); return err; } static int import (ctrl_t ctrl, IOBUF inp, const char* fname,struct import_stats_s *stats, unsigned char **fpr,size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url) { PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; /* Need to initialize because gcc can't grasp the return semantics of read_block. */ kbnode_t secattic = NULL; /* Kludge for PGP desktop percularity */ int rc = 0; int v3keys; getkey_disable_caches (); if (!opt.no_armor) /* Armored reading is not disabled. */ { armor_filter_context_t *afx; afx = new_armor_context (); afx->only_keyblocks = 1; push_armor_filter (afx, inp); release_armor_context (afx); } while (!(rc = read_block (inp, options, &pending_pkt, &keyblock, &v3keys))) { stats->v3keys += v3keys; if (keyblock->pkt->pkttype == PKT_PUBLIC_KEY) { rc = import_one (ctrl, keyblock, stats, fpr, fpr_len, options, 0, 0, screener, screener_arg, origin, url, NULL); if (secattic) { byte tmpfpr[MAX_FINGERPRINT_LEN]; size_t tmpfprlen; if (!rc && !(opt.dry_run || (options & IMPORT_DRY_RUN))) { /* Kudge for PGP desktop - see below. */ fingerprint_from_pk (keyblock->pkt->pkt.public_key, tmpfpr, &tmpfprlen); rc = import_matching_seckeys (ctrl, secattic, tmpfpr, tmpfprlen, stats, opt.batch); } release_kbnode (secattic); secattic = NULL; } } else if (keyblock->pkt->pkttype == PKT_SECRET_KEY) { release_kbnode (secattic); secattic = NULL; rc = import_secret_one (ctrl, keyblock, stats, opt.batch, options, 0, screener, screener_arg, &secattic); keyblock = NULL; /* Ownership was transferred. */ if (secattic) { if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY) rc = 0; /* Try import after the next pubkey. */ /* The attic is a workaround for the peculiar PGP * Desktop method of exporting a secret key: The * exported file is the concatenation of two armored * keyblocks; first the private one and then the public * one. The strange thing is that the secret one has no * binding signatures at all and thus we have not * imported it. The attic stores that secret keys and * we try to import it once after the very next public * keyblock. */ } } else if (keyblock->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (keyblock->pkt->pkt.signature) ) { release_kbnode (secattic); secattic = NULL; rc = import_revoke_cert (ctrl, keyblock, options, stats); } else { release_kbnode (secattic); secattic = NULL; log_info (_("skipping block of type %d\n"), keyblock->pkt->pkttype); } release_kbnode (keyblock); /* fixme: we should increment the not imported counter but this does only make sense if we keep on going despite of errors. For now we do this only if the imported key is too large. */ if (gpg_err_code (rc) == GPG_ERR_TOO_LARGE && gpg_err_source (rc) == GPG_ERR_SOURCE_KEYBOX) { stats->not_imported++; } else if (rc) break; if (!(++stats->count % 100) && !opt.quiet) log_info (_("%lu keys processed so far\n"), stats->count ); if (origin == KEYORG_WKD && stats->count >= 5) { /* We limit the number of keys _received_ from the WKD to 5. * In fact there should be only one key but some sites want * to store a few expired keys there also. gpg's key * selection will later figure out which key to use. Note * that for WKD we always return the fingerprint of the * first imported key. */ log_info ("import from WKD stopped after %d keys\n", 5); break; } } stats->v3keys += v3keys; if (rc == -1) rc = 0; else if (rc && gpg_err_code (rc) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (rc)); release_kbnode (secattic); /* When read_block loop was stopped by error, we have PENDING_PKT left. */ if (pending_pkt) { free_packet (pending_pkt, NULL); xfree (pending_pkt); } return rc; } /* Helper to migrate secring.gpg to GnuPG 2.1. */ gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname) { gpg_error_t err; iobuf_t inp; PACKET *pending_pkt = NULL; kbnode_t keyblock = NULL; /* Need to initialize because gcc can't grasp the return semantics of read_block. */ struct import_stats_s *stats; int v3keys; inp = iobuf_open (fname); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { err = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), fname, gpg_strerror (err)); return err; } getkey_disable_caches(); stats = import_new_stats_handle (); while (!(err = read_block (inp, 0, &pending_pkt, &keyblock, &v3keys))) { if (keyblock->pkt->pkttype == PKT_SECRET_KEY) { err = import_secret_one (ctrl, keyblock, stats, 1, 0, 1, NULL, NULL, NULL); keyblock = NULL; /* Ownership was transferred. */ } release_kbnode (keyblock); if (err) break; } import_release_stats_handle (stats); if (err == -1) err = 0; else if (err && gpg_err_code (err) != GPG_ERR_INV_KEYRING) log_error (_("error reading '%s': %s\n"), fname, gpg_strerror (err)); else if (err) log_error ("import from '%s' failed: %s\n", fname, gpg_strerror (err)); iobuf_close (inp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); return err; } void import_print_stats (import_stats_t stats) { if (!opt.quiet) { log_info(_("Total number processed: %lu\n"), stats->count + stats->v3keys); if (stats->v3keys) log_info(_(" skipped PGP-2 keys: %lu\n"), stats->v3keys); if (stats->skipped_new_keys ) log_info(_(" skipped new keys: %lu\n"), stats->skipped_new_keys ); if (stats->no_user_id ) log_info(_(" w/o user IDs: %lu\n"), stats->no_user_id ); if (stats->imported) { log_info(_(" imported: %lu"), stats->imported ); log_printf ("\n"); } if (stats->unchanged ) log_info(_(" unchanged: %lu\n"), stats->unchanged ); if (stats->n_uids ) log_info(_(" new user IDs: %lu\n"), stats->n_uids ); if (stats->n_subk ) log_info(_(" new subkeys: %lu\n"), stats->n_subk ); if (stats->n_sigs ) log_info(_(" new signatures: %lu\n"), stats->n_sigs ); if (stats->n_revoc ) log_info(_(" new key revocations: %lu\n"), stats->n_revoc ); if (stats->secret_read ) log_info(_(" secret keys read: %lu\n"), stats->secret_read ); if (stats->secret_imported ) log_info(_(" secret keys imported: %lu\n"), stats->secret_imported ); if (stats->secret_dups ) log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups ); if (stats->not_imported ) log_info(_(" not imported: %lu\n"), stats->not_imported ); if (stats->n_sigs_cleaned) log_info(_(" signatures cleaned: %lu\n"),stats->n_sigs_cleaned); if (stats->n_uids_cleaned) log_info(_(" user IDs cleaned: %lu\n"),stats->n_uids_cleaned); } if (is_status_enabled ()) { char buf[15*20]; snprintf (buf, sizeof buf, "%lu %lu %lu 0 %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu", stats->count + stats->v3keys, stats->no_user_id, stats->imported, stats->unchanged, stats->n_uids, stats->n_subk, stats->n_sigs, stats->n_revoc, stats->secret_read, stats->secret_imported, stats->secret_dups, stats->skipped_new_keys, stats->not_imported, stats->v3keys ); write_status_text (STATUS_IMPORT_RES, buf); } } /* Return true if PKTTYPE is valid in a keyblock. */ static int valid_keyblock_packet (int pkttype) { switch (pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_SIGNATURE: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_RING_TRUST: return 1; default: return 0; } } /* Read the next keyblock from stream A. Meta data (ring trust * packets) are only considered if OPTIONS has the IMPORT_RESTORE flag * set. PENDING_PKT should be initialized to NULL and not changed by * the caller. * * Returns 0 for okay, -1 no more blocks, or any other errorcode. The * integer at R_V3KEY counts the number of unsupported v3 keyblocks. */ static int read_block( IOBUF a, unsigned int options, PACKET **pending_pkt, kbnode_t *ret_root, int *r_v3keys) { int rc; struct parse_packet_ctx_s parsectx; PACKET *pkt; kbnode_t root = NULL; kbnode_t lastnode = NULL; int in_cert, in_v3key, skip_sigs; u32 keyid[2]; int got_keyid = 0; unsigned int dropped_nonselfsigs = 0; *r_v3keys = 0; if (*pending_pkt) { root = lastnode = new_kbnode( *pending_pkt ); *pending_pkt = NULL; log_assert (root->pkt->pkttype == PKT_PUBLIC_KEY || root->pkt->pkttype == PKT_SECRET_KEY); in_cert = 1; keyid_from_pk (root->pkt->pkt.public_key, keyid); got_keyid = 1; } else in_cert = 0; pkt = xmalloc (sizeof *pkt); init_packet (pkt); init_parse_packet (&parsectx, a); if (!(options & IMPORT_RESTORE)) parsectx.skip_meta = 1; in_v3key = 0; skip_sigs = 0; while ((rc=parse_packet (&parsectx, pkt)) != -1) { if (rc && (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY))) { in_v3key = 1; ++*r_v3keys; free_packet (pkt, &parsectx); init_packet (pkt); continue; } else if (rc ) /* (ignore errors) */ { skip_sigs = 0; if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) ; /* Do not show a diagnostic. */ else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET && (pkt->pkttype == PKT_USER_ID || pkt->pkttype == PKT_ATTRIBUTE)) { /* This indicates a too large user id or attribute * packet. We skip this packet and all following * signatures. Sure, this won't allow to repair a * garbled keyring in case one of the signatures belong * to another user id. However, this better mitigates * DoS using inserted user ids. */ skip_sigs = 1; } else if (gpg_err_code (rc) == GPG_ERR_INV_PACKET && (pkt->pkttype == PKT_OLD_COMMENT || pkt->pkttype == PKT_COMMENT)) ; /* Ignore too large comment packets. */ else { log_error("read_block: read error: %s\n", gpg_strerror (rc) ); rc = GPG_ERR_INV_KEYRING; goto ready; } free_packet (pkt, &parsectx); init_packet(pkt); continue; } if (skip_sigs) { if (pkt->pkttype == PKT_SIGNATURE) { free_packet (pkt, &parsectx); init_packet (pkt); continue; } skip_sigs = 0; } if (in_v3key && !(pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { free_packet (pkt, &parsectx); init_packet(pkt); continue; } in_v3key = 0; if (!root && pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (pkt->pkt.signature) ) { /* This is a revocation certificate which is handled in a * special way. */ root = new_kbnode( pkt ); pkt = NULL; goto ready; } /* Make a linked list of all packets. */ switch (pkt->pkttype) { case PKT_COMPRESSED: if (check_compress_algo (pkt->pkt.compressed->algorithm)) { rc = GPG_ERR_COMPR_ALGO; goto ready; } else { compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx ); pkt->pkt.compressed->buf = NULL; if (push_compress_filter2 (a, cfx, pkt->pkt.compressed->algorithm, 1)) xfree (cfx); /* e.g. in case of compression_algo NONE. */ } free_packet (pkt, &parsectx); init_packet(pkt); break; case PKT_RING_TRUST: /* Skip those packets unless we are in restore mode. */ if ((opt.import_options & IMPORT_RESTORE)) goto x_default; free_packet (pkt, &parsectx); init_packet(pkt); break; case PKT_SIGNATURE: if (!in_cert) goto x_default; if (!(options & IMPORT_SELF_SIGS_ONLY)) goto x_default; log_assert (got_keyid); if (pkt->pkt.signature->keyid[0] == keyid[0] && pkt->pkt.signature->keyid[1] == keyid[1]) { /* This is likely a self-signature. We import this one. * Eventually we should use the ISSUER_FPR to compare * self-signatures, but that will work only for v5 keys * which are currently not even deployed. * Note that we do not do any crypto verify here because * that would defeat this very mitigation of DoS by * importing a key with a huge amount of faked * key-signatures. A verification will be done later in * the processing anyway. Here we want a cheap an early * way to drop non-self-signatures. */ goto x_default; } /* Skip this signature. */ dropped_nonselfsigs++; free_packet (pkt, &parsectx); init_packet(pkt); break; case PKT_PUBLIC_KEY: case PKT_SECRET_KEY: if (!got_keyid) { keyid_from_pk (pkt->pkt.public_key, keyid); got_keyid = 1; } if (in_cert) /* Store this packet. */ { *pending_pkt = pkt; pkt = NULL; goto ready; } in_cert = 1; goto x_default; default: x_default: if (in_cert && valid_keyblock_packet (pkt->pkttype)) { if (!root ) root = lastnode = new_kbnode (pkt); else { lastnode->next = new_kbnode (pkt); lastnode = lastnode->next; } pkt = xmalloc (sizeof *pkt); } else free_packet (pkt, &parsectx); init_packet(pkt); break; } } ready: if (rc == -1 && root ) rc = 0; if (rc ) release_kbnode( root ); else *ret_root = root; free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree( pkt ); if (!rc && dropped_nonselfsigs && opt.verbose) log_info ("key %s: number of dropped non-self-signatures: %u\n", keystr (keyid), dropped_nonselfsigs); return rc; } /* Walk through the subkeys on a pk to find if we have the PKS disease: multiple subkeys with their binding sigs stripped, and the sig for the first subkey placed after the last subkey. That is, instead of "pk uid sig sub1 bind1 sub2 bind2 sub3 bind3" we have "pk uid sig sub1 sub2 sub3 bind1". We can't do anything about sub2 and sub3, as they are already lost, but we can try and rescue sub1 by reordering the keyblock so that it reads "pk uid sig sub1 bind1 sub2 sub3". Returns TRUE if the keyblock was modified. */ static int fix_pks_corruption (ctrl_t ctrl, kbnode_t keyblock) { int changed = 0; int keycount = 0; kbnode_t node; kbnode_t last = NULL; kbnode_t sknode=NULL; /* First determine if we have the problem at all. Look for 2 or more subkeys in a row, followed by a single binding sig. */ for (node=keyblock; node; last=node, node=node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { keycount++; if(!sknode) sknode=node; } else if (node->pkt->pkttype == PKT_SIGNATURE && IS_SUBKEY_SIG (node->pkt->pkt.signature) && keycount >= 2 && !node->next) { /* We might have the problem, as this key has two subkeys in a row without any intervening packets. */ /* Sanity check */ if (!last) break; /* Temporarily attach node to sknode. */ node->next = sknode->next; sknode->next = node; last->next = NULL; /* Note we aren't checking whether this binding sig is a selfsig. This is not necessary here as the subkey and binding sig will be rejected later if that is the case. */ if (check_key_signature (ctrl, keyblock,node,NULL)) { /* Not a match, so undo the changes. */ sknode->next = node->next; last->next = node; node->next = NULL; break; } else { /* Mark it good so we don't need to check it again */ sknode->flag |= NODE_GOOD_SELFSIG; changed = 1; break; } } else keycount = 0; } return changed; } /* Versions of GnuPG before 1.4.11 and 2.0.16 allowed to import bogus direct key signatures. A side effect of this was that a later import of the same good direct key signatures was not possible because the cmp_signature check in merge_blocks considered them equal. Although direct key signatures are now checked during import, there might still be bogus signatures sitting in a keyring. We need to detect and delete them before doing a merge. This function returns the number of removed sigs. */ static int fix_bad_direct_key_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid) { gpg_error_t err; kbnode_t node; int count = 0; for (node = keyblock->next; node; node=node->next) { if (node->pkt->pkttype == PKT_USER_ID) break; if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (node->pkt->pkt.signature)) { err = check_key_signature (ctrl, keyblock, node, NULL); if (err && gpg_err_code (err) != GPG_ERR_PUBKEY_ALGO ) { /* If we don't know the error, we can't decide; this is not a problem because cmp_signature can't compare the signature either. */ log_info ("key %s: invalid direct key signature removed\n", keystr (keyid)); delete_kbnode (node); count++; } } } return count; } static void print_import_ok (PKT_public_key *pk, unsigned int reason) { byte array[MAX_FINGERPRINT_LEN], *s; char buf[MAX_FINGERPRINT_LEN*2+30], *p; size_t i, n; snprintf (buf, sizeof buf, "%u ", reason); p = buf + strlen (buf); fingerprint_from_pk (pk, array, &n); s = array; for (i=0; i < n ; i++, s++, p += 2) sprintf (p, "%02X", *s); write_status_text (STATUS_IMPORT_OK, buf); } static void print_import_check (PKT_public_key * pk, PKT_user_id * id) { byte hexfpr[2*MAX_FINGERPRINT_LEN+1]; u32 keyid[2]; keyid_from_pk (pk, keyid); hexfingerprint (pk, hexfpr, sizeof hexfpr); write_status_printf (STATUS_IMPORT_CHECK, "%08X%08X %s %s", keyid[0], keyid[1], hexfpr, id->name); } static void check_prefs_warning(PKT_public_key *pk) { log_info(_("WARNING: key %s contains preferences for unavailable\n" "algorithms on these user IDs:\n"), keystr_from_pk(pk)); } static void check_prefs (ctrl_t ctrl, kbnode_t keyblock) { kbnode_t node; PKT_public_key *pk; int problem=0; merge_keys_and_selfsig (ctrl, keyblock); pk=keyblock->pkt->pkt.public_key; for(node=keyblock;node;node=node->next) { if(node->pkt->pkttype==PKT_USER_ID && node->pkt->pkt.user_id->created && node->pkt->pkt.user_id->prefs) { PKT_user_id *uid = node->pkt->pkt.user_id; prefitem_t *prefs = uid->prefs; char *user = utf8_to_native(uid->name,strlen(uid->name),0); for(;prefs->type;prefs++) { char num[10]; /* prefs->value is a byte, so we're over safe here */ sprintf(num,"%u",prefs->value); if(prefs->type==PREFTYPE_SYM) { if (openpgp_cipher_test_algo (prefs->value)) { const char *algo = (openpgp_cipher_test_algo (prefs->value) ? num : openpgp_cipher_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for cipher" " algorithm %s\n"), user, algo); problem=1; } } else if(prefs->type==PREFTYPE_AEAD) { if (openpgp_aead_test_algo (prefs->value)) { /* FIXME: The test below is wrong. We should * check if ...algo_name yields a "?" and * only in that case use NUM. */ const char *algo = (openpgp_aead_test_algo (prefs->value) ? num : openpgp_aead_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for AEAD" " algorithm %s\n"), user, algo); problem=1; } } else if(prefs->type==PREFTYPE_HASH) { if(openpgp_md_test_algo(prefs->value)) { const char *algo = (gcry_md_test_algo (prefs->value) ? num : gcry_md_algo_name (prefs->value)); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for digest" " algorithm %s\n"), user, algo); problem=1; } } else if(prefs->type==PREFTYPE_ZIP) { if(check_compress_algo (prefs->value)) { const char *algo=compress_algo_to_string(prefs->value); if(!problem) check_prefs_warning(pk); log_info(_(" \"%s\": preference for compression" " algorithm %s\n"),user,algo?algo:num); problem=1; } } } xfree(user); } } if(problem) { log_info(_("it is strongly suggested that you update" " your preferences and\n")); log_info(_("re-distribute this key to avoid potential algorithm" " mismatch problems\n")); if(!opt.batch) { strlist_t sl = NULL; strlist_t locusr = NULL; size_t fprlen=0; byte fpr[MAX_FINGERPRINT_LEN], *p; char username[(MAX_FINGERPRINT_LEN*2)+1]; unsigned int i; p = fingerprint_from_pk (pk,fpr,&fprlen); for(i=0;ictrl; kbnode_t node = parm->node; static char numbuf[20]; const char *result; log_assert (ctrl && ctrl->magic == SERVER_CONTROL_MAGIC); if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_ATTRIBUTE) { PKT_user_id *uid = node->pkt->pkt.user_id; if (!strcmp (propname, "uid")) result = uid->name; else if (!strcmp (propname, "mbox")) { if (!uid->mbox) { uid->mbox = mailbox_from_userid (uid->name, 0); } result = uid->mbox; } else if (!strcmp (propname, "primary")) { result = uid->flags.primary? "1":"0"; } else if (!strcmp (propname, "expired")) { result = uid->flags.expired? "1":"0"; } else if (!strcmp (propname, "revoked")) { result = uid->flags.revoked? "1":"0"; } else result = NULL; } else if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (!strcmp (propname, "sig_created")) { snprintf (numbuf, sizeof numbuf, "%lu", (ulong)sig->timestamp); result = numbuf; } else if (!strcmp (propname, "sig_created_d")) { result = datestr_from_sig (sig); } else if (!strcmp (propname, "sig_algo")) { snprintf (numbuf, sizeof numbuf, "%d", sig->pubkey_algo); result = numbuf; } else if (!strcmp (propname, "sig_digest_algo")) { snprintf (numbuf, sizeof numbuf, "%d", sig->digest_algo); result = numbuf; } else if (!strcmp (propname, "expired")) { result = sig->flags.expired? "1":"0"; } 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) { PKT_public_key *pk = node->pkt->pkt.public_key; if (!strcmp (propname, "secret")) { result = (node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY)? "1":"0"; } else if (!strcmp (propname, "key_algo")) { snprintf (numbuf, sizeof numbuf, "%d", pk->pubkey_algo); result = numbuf; } else if (!strcmp (propname, "key_created")) { snprintf (numbuf, sizeof numbuf, "%lu", (ulong)pk->timestamp); result = numbuf; } else if (!strcmp (propname, "key_created_d")) { result = datestr_from_pk (pk); } else if (!strcmp (propname, "expired")) { result = pk->has_expired? "1":"0"; } else if (!strcmp (propname, "revoked")) { result = pk->flags.revoked? "1":"0"; } else if (!strcmp (propname, "disabled")) { result = pk_is_disabled (pk)? "1":"0"; } else if (!strcmp (propname, "usage")) { snprintf (numbuf, sizeof numbuf, "%s%s%s%s%s", (pk->pubkey_usage & PUBKEY_USAGE_ENC)?"e":"", (pk->pubkey_usage & PUBKEY_USAGE_SIG)?"s":"", (pk->pubkey_usage & PUBKEY_USAGE_CERT)?"c":"", (pk->pubkey_usage & PUBKEY_USAGE_AUTH)?"a":"", (pk->pubkey_usage & PUBKEY_USAGE_UNKNOWN)?"?":""); result = numbuf; } else result = NULL; } else result = NULL; return result; } /* * Apply the keep-uid filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_keep_uid_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; struct impex_filter_parm_s parm; parm.ctrl = ctrl; for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_USER_ID) { parm.node = node; if (!recsel_select (selector, impex_filter_getval, &parm)) { /* log_debug ("keep-uid: deleting '%s'\n", */ /* node->pkt->pkt.user_id->name); */ /* The UID packet and all following packets up to the * next UID or a subkey. */ delete_kbnode (node); for (; node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ; node = node->next) delete_kbnode (node->next); } /* else */ /* log_debug ("keep-uid: keeping '%s'\n", */ /* node->pkt->pkt.user_id->name); */ } } } /* * Apply the drop-sig filter to the keyblock. The deleted nodes are * marked and thus the caller should call commit_kbnode afterwards. * KEYBLOCK must not have any blocks marked as deleted. */ static void apply_drop_sig_filter (ctrl_t ctrl, kbnode_t keyblock, recsel_expr_t selector) { kbnode_t node; int active = 0; u32 main_keyid[2]; PKT_signature *sig; struct impex_filter_parm_s parm; parm.ctrl = ctrl; keyid_from_pk (keyblock->pkt->pkt.public_key, main_keyid); /* Loop over all signatures for user id and attribute packets which * are not self signatures. */ for (node = keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) break; /* ready. */ if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_ATTRIBUTE) active = 1; if (!active) continue; if (node->pkt->pkttype != PKT_SIGNATURE) continue; sig = node->pkt->pkt.signature; if (main_keyid[0] == sig->keyid[0] || main_keyid[1] == sig->keyid[1]) continue; /* Skip self-signatures. */ if (IS_UID_SIG(sig) || IS_UID_REV(sig)) { parm.node = node; if (recsel_select (selector, impex_filter_getval, &parm)) delete_kbnode (node); } } } /* Insert a key origin into a public key packet. */ static gpg_error_t insert_key_origin_pk (PKT_public_key *pk, u32 curtime, int origin, const char *url) { if (origin == KEYORG_WKD || origin == KEYORG_DANE) { /* For WKD and DANE we insert origin information also for the * key but we don't record the URL because we have have no use * for that: An update using a keyserver has higher precedence * and will thus update this origin info. For refresh using WKD * or DANE we need to go via the User ID anyway. Recall that we * are only inserting a new key. */ pk->keyorg = origin; pk->keyupdate = curtime; } else if (origin == KEYORG_KS && url) { /* If the key was retrieved from a keyserver using a fingerprint * request we add the meta information. Note that the use of a * fingerprint needs to be enforced by the caller of the import * function. This is commonly triggered by verifying a modern * signature which has an Issuer Fingerprint signature * subpacket. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } else if (origin == KEYORG_FILE) { pk->keyorg = origin; pk->keyupdate = curtime; } else if (origin == KEYORG_URL) { pk->keyorg = origin; pk->keyupdate = curtime; if (url) { xfree (pk->updateurl); pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } return 0; } /* Insert a key origin into a user id packet. */ static gpg_error_t insert_key_origin_uid (PKT_user_id *uid, u32 curtime, int origin, const char *url) { if (origin == KEYORG_WKD || origin == KEYORG_DANE) { /* We insert origin information on a UID only when we received * them via the Web Key Directory or a DANE record. The key we * receive here from the WKD has been filtered to contain only * the user ID as looked up in the WKD. For a DANE origin we * this should also be the case. Thus we will see here only one * user id. */ uid->keyorg = origin; uid->keyupdate = curtime; if (url) { xfree (uid->updateurl); uid->updateurl = xtrystrdup (url); if (!uid->updateurl) return gpg_error_from_syserror (); } } else if (origin == KEYORG_KS && url) { /* If the key was retrieved from a keyserver using a fingerprint * request we mark that also in the user ID. However we do not * store the keyserver URL in the UID. A later update (merge) * from a more trusted source will replace this info. */ uid->keyorg = origin; uid->keyupdate = curtime; } else if (origin == KEYORG_FILE) { uid->keyorg = origin; uid->keyupdate = curtime; } else if (origin == KEYORG_URL) { uid->keyorg = origin; uid->keyupdate = curtime; } return 0; } /* Apply meta data to KEYBLOCK. This sets the origin of the key to * ORIGIN and the updateurl to URL. Note that this function is only * used for a new key, that is not when we are merging keys. */ static gpg_error_t insert_key_origin (kbnode_t keyblock, int origin, const char *url) { gpg_error_t err; kbnode_t node; u32 curtime = make_timestamp (); for (node = keyblock; node; node = node->next) { if (is_deleted_kbnode (node)) ; else if (node->pkt->pkttype == PKT_PUBLIC_KEY) { err = insert_key_origin_pk (node->pkt->pkt.public_key, curtime, origin, url); if (err) return err; } else if (node->pkt->pkttype == PKT_USER_ID) { err = insert_key_origin_uid (node->pkt->pkt.user_id, curtime, origin, url); if (err) return err; } } return 0; } /* Update meta data on KEYBLOCK. This updates the key origin on the * public key according to ORIGIN and URL. The UIDs are already * updated when this function is called. */ static gpg_error_t update_key_origin (kbnode_t keyblock, u32 curtime, int origin, const char *url) { PKT_public_key *pk; log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); pk = keyblock->pkt->pkt.public_key; if (pk->keyupdate > curtime) ; /* Don't do it for a time warp. */ else if (origin == KEYORG_WKD || origin == KEYORG_DANE) { /* We only update the origin info if they either have never been * set or are the origin was the same as the new one. If this * is WKD we also update the UID to show from which user id this * was updated. */ if (!pk->keyorg || pk->keyorg == KEYORG_WKD || pk->keyorg == KEYORG_DANE) { pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; if (origin == KEYORG_WKD && url) { pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } } else if (origin == KEYORG_KS) { /* All updates from a keyserver are considered to have the * freshed key. Thus we always set the new key origin. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; if (url) { pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } else if (origin == KEYORG_FILE) { /* Updates from a file are considered to be fresh. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; } else if (origin == KEYORG_URL) { /* Updates from a URL are considered to be fresh. */ pk->keyorg = origin; pk->keyupdate = curtime; xfree (pk->updateurl); pk->updateurl = NULL; if (url) { pk->updateurl = xtrystrdup (url); if (!pk->updateurl) return gpg_error_from_syserror (); } } return 0; } /* * Try to import one keyblock. Return an error only in serious cases, * but never for an invalid keyblock. It uses log_error to increase * the internal errorcount, so that invalid input can be detected by * programs which called gpg. If SILENT is no messages are printed - * even most error messages are suppressed. ORIGIN is the origin of * the key (0 for unknown) and URL the corresponding URL. FROM_SK * indicates that the key has been made from a secret key. If R_SAVED * is not NULL a boolean will be stored indicating whether the keyblock * has valid parts. */ static gpg_error_t import_one_real (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, int origin, const char *url, int *r_valid) { gpg_error_t err = 0; PKT_public_key *pk; kbnode_t node, uidnode; kbnode_t keyblock_orig = NULL; byte fpr2[MAX_FINGERPRINT_LEN]; size_t fpr2len; u32 keyid[2]; int new_key = 0; int mod_key = 0; int same_key = 0; int non_self = 0; size_t an; char pkstrbuf[PUBKEY_STRING_SIZE]; int merge_keys_done = 0; int any_filter = 0; KEYDB_HANDLE hd = NULL; if (r_valid) *r_valid = 0; /* If show-only is active we don't won't any extra output. */ if ((options & (IMPORT_SHOW | IMPORT_DRY_RUN))) silent = 1; /* Get the key and print some info about it. */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if (!node ) BUG(); pk = node->pkt->pkt.public_key; fingerprint_from_pk (pk, fpr2, &fpr2len); for (an = fpr2len; an < MAX_FINGERPRINT_LEN; an++) fpr2[an] = 0; keyid_from_pk( pk, keyid ); uidnode = find_next_kbnode( keyblock, PKT_USER_ID ); if (opt.verbose && !opt.interactive && !silent && !from_sk) { /* Note that we do not print this info in FROM_SK mode * because import_secret_one already printed that. */ log_info ("pub %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk(pk) ); if (uidnode) print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len ); log_printf ("\n"); } /* Unless import-drop-uids has been requested we don't allow import * of a key without UIDs. */ if (!uidnode && !(options & IMPORT_DROP_UIDS)) { if (!silent) log_error( _("key %s: no user ID\n"), keystr_from_pk(pk)); return 0; } if (screener && screener (keyblock, screener_arg)) { log_error (_("key %s: %s\n"), keystr_from_pk (pk), _("rejected by import screener")); return 0; } if (opt.interactive && !silent) { if (is_status_enabled()) print_import_check (pk, uidnode->pkt->pkt.user_id); merge_keys_and_selfsig (ctrl, keyblock); tty_printf ("\n"); show_basic_key_info (ctrl, keyblock, from_sk); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("import.okay", "Do you want to import this key? (y/N) ")) return 0; } /* Remove all non-self-sigs if requested. Noe that this is a NOP if * that option has been globally set but we may also be called * latter with the already parsed keyblock and a locally changed * option. This is why we need to remove them here as well. */ if ((options & IMPORT_SELF_SIGS_ONLY)) remove_all_non_self_sigs (&keyblock, keyid); /* Remove or collapse the user ids. */ if ((options & IMPORT_DROP_UIDS)) remove_all_uids (&keyblock); else collapse_uids (&keyblock); /* Clean the key that we're about to import, to cut down on things that we have to clean later. This has no practical impact on the end result, but does result in less logging which might confuse the user. */ if ((options & IMPORT_CLEAN)) { merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), NULL, NULL); clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, NULL, NULL); } clear_kbnode_flags( keyblock ); if ((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption (ctrl, keyblock) && opt.verbose) log_info (_("key %s: PKS subkey corruption repaired\n"), keystr_from_pk(pk)); if ((options & IMPORT_REPAIR_KEYS)) key_check_all_keysigs (ctrl, 1, keyblock, 0, 0); if (chk_self_sigs (ctrl, keyblock, keyid, &non_self)) return 0; /* Invalid keyblock - error already printed. */ /* If we allow such a thing, mark unsigned uids as valid */ if (opt.allow_non_selfsigned_uid) { for (node=keyblock; node; node = node->next ) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & NODE_GOOD_SELFSIG) && !(node->flag & NODE_BAD_SELFSIG) ) { char *user=utf8_to_native(node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len,0); /* Fake a good signature status for the user id. */ node->flag |= NODE_GOOD_SELFSIG; log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"), keystr_from_pk(pk),user); xfree(user); } } /* Delete invalid parts and without the drop option bail out if * there are no user ids. */ if (!delete_inv_parts (ctrl, keyblock, keyid, options) && !(options & IMPORT_DROP_UIDS) ) { if (!silent) { log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk)); if (!opt.quiet ) log_info(_("this may be caused by a missing self-signature\n")); } stats->no_user_id++; return 0; } /* Get rid of deleted nodes. */ commit_kbnode (&keyblock); /* Apply import filter. */ if (import_filter.keep_uid) { apply_keep_uid_filter (ctrl, keyblock, import_filter.keep_uid); commit_kbnode (&keyblock); any_filter = 1; } if (import_filter.drop_sig) { apply_drop_sig_filter (ctrl, keyblock, import_filter.drop_sig); commit_kbnode (&keyblock); any_filter = 1; } /* If we ran any filter we need to check that at least one user id * is left in the keyring. Note that we do not use log_error in * this case. */ if (any_filter && !any_uid_left (keyblock)) { if (!opt.quiet ) log_info ( _("key %s: no valid user IDs\n"), keystr_from_pk (pk)); stats->no_user_id++; return 0; } /* The keyblock is valid and ready for real import. */ if (r_valid) *r_valid = 1; /* Show the key in the form it is merged or inserted. We skip this * if "import-export" is also active without --armor or the output * file has explicily been given. */ if ((options & IMPORT_SHOW) && !((options & IMPORT_EXPORT) && !opt.armor && !opt.outfile)) { merge_keys_and_selfsig (ctrl, keyblock); merge_keys_done = 1; /* Note that we do not want to show the validity because the key * has not yet imported. */ list_keyblock_direct (ctrl, keyblock, from_sk, 0, opt.fingerprint || opt.with_fingerprint, 1); es_fflush (es_stdout); } /* Write the keyblock to the output and do not actually import. */ if ((options & IMPORT_EXPORT)) { if (!merge_keys_done) { merge_keys_and_selfsig (ctrl, keyblock); merge_keys_done = 1; } err = write_keyblock_to_output (keyblock, opt.armor, opt.export_options); goto leave; } if (opt.dry_run || (options & IMPORT_DRY_RUN)) goto leave; /* Do we have this key already in one of our pubrings ? */ - err = get_keyblock_byfprint_fast (&keyblock_orig, &hd, + err = get_keyblock_byfprint_fast (ctrl, &keyblock_orig, &hd, fpr2, fpr2len, 1/*locked*/); if ((err && gpg_err_code (err) != GPG_ERR_NO_PUBKEY && gpg_err_code (err) != GPG_ERR_UNUSABLE_PUBKEY) || !hd) { /* The !hd above is to catch a misbehaving function which * returns NO_PUBKEY for failing to allocate a handle. */ if (!silent) log_error (_("key %s: public key not found: %s\n"), keystr(keyid), gpg_strerror (err)); } else if (err && (opt.import_options&IMPORT_MERGE_ONLY) ) { if (opt.verbose && !silent ) log_info( _("key %s: new key - skipped\n"), keystr(keyid)); err = 0; stats->skipped_new_keys++; } else if (err) /* Insert this key. */ { /* Note: ERR can only be NO_PUBKEY or UNUSABLE_PUBKEY. */ int n_sigs_cleaned, n_uids_cleaned; err = keydb_locate_writable (hd); if (err) { log_error (_("no writable keyring found: %s\n"), gpg_strerror (err)); err = gpg_error (GPG_ERR_GENERAL); goto leave; } if (opt.verbose > 1 ) log_info (_("writing to '%s'\n"), keydb_get_resource_name (hd) ); if ((options & IMPORT_CLEAN)) { merge_keys_and_selfsig (ctrl, keyblock); clean_all_uids (ctrl, keyblock, opt.verbose, (options&IMPORT_MINIMAL), &n_uids_cleaned,&n_sigs_cleaned); clean_all_subkeys (ctrl, keyblock, opt.verbose, KEY_CLEAN_NONE, NULL, NULL); } /* Unless we are in restore mode apply meta data to the * keyblock. Note that this will never change the first packet * and thus the address of KEYBLOCK won't change. */ if ( !(options & IMPORT_RESTORE) ) { err = insert_key_origin (keyblock, origin, url); if (err) { log_error ("insert_key_origin failed: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_GENERAL); goto leave; } } err = keydb_insert_keyblock (hd, keyblock ); if (err) log_error (_("error writing keyring '%s': %s\n"), keydb_get_resource_name (hd), gpg_strerror (err)); else if (!(opt.import_options & IMPORT_KEEP_OWNERTTRUST)) { /* This should not be possible since we delete the ownertrust when a key is deleted, but it can happen if the keyring and trustdb are out of sync. It can also be made to happen with the trusted-key command and by importing and locally exported key. */ clear_ownertrusts (ctrl, pk); if (non_self) revalidation_mark (ctrl); } /* Release the handle and thus unlock the keyring asap. */ keydb_release (hd); hd = NULL; /* We are ready. */ if (!err && !opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len); log_info (_("key %s: public key \"%s\" imported\n"), keystr(keyid), p); xfree(p); } if (!err && is_status_enabled()) { char *us = get_long_user_id_string (ctrl, keyid); write_status_text( STATUS_IMPORTED, us ); xfree(us); print_import_ok (pk, 1); } if (!err) { stats->imported++; new_key = 1; } } else /* Key already exists - merge. */ { int n_uids, n_sigs, n_subk, n_sigs_cleaned, n_uids_cleaned; u32 curtime = make_timestamp (); /* Compare the original against the new key; just to be sure nothing * weird is going on */ if (cmp_public_keys (keyblock_orig->pkt->pkt.public_key, pk)) { if (!silent) log_error( _("key %s: doesn't match our copy\n"),keystr(keyid)); goto leave; } /* Make sure the original direct key sigs are all sane. */ n_sigs_cleaned = fix_bad_direct_key_sigs (ctrl, keyblock_orig, keyid); if (n_sigs_cleaned) commit_kbnode (&keyblock_orig); /* Try to merge KEYBLOCK into KEYBLOCK_ORIG. */ clear_kbnode_flags( keyblock_orig ); clear_kbnode_flags( keyblock ); n_uids = n_sigs = n_subk = n_uids_cleaned = 0; err = merge_blocks (ctrl, options, keyblock_orig, keyblock, keyid, curtime, origin, url, &n_uids, &n_sigs, &n_subk ); if (err) goto leave; if ((options & IMPORT_CLEAN)) { merge_keys_and_selfsig (ctrl, keyblock_orig); clean_all_uids (ctrl, keyblock_orig, opt.verbose, (options&IMPORT_MINIMAL), &n_uids_cleaned,&n_sigs_cleaned); clean_all_subkeys (ctrl, keyblock_orig, opt.verbose, KEY_CLEAN_NONE, NULL, NULL); } if (n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) { /* Unless we are in restore mode apply meta data to the * keyblock. Note that this will never change the first packet * and thus the address of KEYBLOCK won't change. */ if ( !(options & IMPORT_RESTORE) ) { err = update_key_origin (keyblock_orig, curtime, origin, url); if (err) { log_error ("update_key_origin failed: %s\n", gpg_strerror (err)); goto leave; } } mod_key = 1; /* KEYBLOCK_ORIG has been updated; write */ err = keydb_update_keyblock (ctrl, hd, keyblock_orig); if (err) log_error (_("error writing keyring '%s': %s\n"), keydb_get_resource_name (hd), gpg_strerror (err)); else if (non_self) revalidation_mark (ctrl); /* Release the handle and thus unlock the keyring asap. */ keydb_release (hd); hd = NULL; /* We are ready. Print and update stats if we got no error. * An error here comes from writing the keyblock and thus * very likely means that no update happened. */ if (!err && !opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len); if (n_uids == 1 ) log_info( _("key %s: \"%s\" 1 new user ID\n"), keystr(keyid),p); else if (n_uids ) log_info( _("key %s: \"%s\" %d new user IDs\n"), keystr(keyid),p,n_uids); if (n_sigs == 1 ) log_info( _("key %s: \"%s\" 1 new signature\n"), keystr(keyid), p); else if (n_sigs ) log_info( _("key %s: \"%s\" %d new signatures\n"), keystr(keyid), p, n_sigs ); if (n_subk == 1 ) log_info( _("key %s: \"%s\" 1 new subkey\n"), keystr(keyid), p); else if (n_subk ) log_info( _("key %s: \"%s\" %d new subkeys\n"), keystr(keyid), p, n_subk ); if (n_sigs_cleaned==1) log_info(_("key %s: \"%s\" %d signature cleaned\n"), keystr(keyid),p,n_sigs_cleaned); else if (n_sigs_cleaned) log_info(_("key %s: \"%s\" %d signatures cleaned\n"), keystr(keyid),p,n_sigs_cleaned); if (n_uids_cleaned==1) log_info(_("key %s: \"%s\" %d user ID cleaned\n"), keystr(keyid),p,n_uids_cleaned); else if (n_uids_cleaned) log_info(_("key %s: \"%s\" %d user IDs cleaned\n"), keystr(keyid),p,n_uids_cleaned); xfree(p); } if (!err) { stats->n_uids +=n_uids; stats->n_sigs +=n_sigs; stats->n_subk +=n_subk; stats->n_sigs_cleaned +=n_sigs_cleaned; stats->n_uids_cleaned +=n_uids_cleaned; if (is_status_enabled () && !silent) print_import_ok (pk, ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0))); } } else { /* Release the handle and thus unlock the keyring asap. */ keydb_release (hd); hd = NULL; /* FIXME: We do not track the time we last checked a key for * updates. To do this we would need to rewrite even the * keys which have no changes. Adding this would be useful * for the automatic update of expired keys via the WKD in * case the WKD still carries the expired key. See * get_best_pubkey_byname. */ same_key = 1; if (is_status_enabled ()) print_import_ok (pk, 0); if (!opt.quiet && !silent) { char *p = get_user_id_byfpr_native (ctrl, fpr2, fpr2len); log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p); xfree(p); } stats->unchanged++; } } leave: keydb_release (hd); if (mod_key || new_key || same_key) { /* A little explanation for this: we fill in the fingerprint when importing keys as it can be useful to know the fingerprint in certain keyserver-related cases (a keyserver asked for a particular name, but the key doesn't have that name). However, in cases where we're importing more than one key at a time, we cannot know which key to fingerprint. In these cases, rather than guessing, we do not fingerprinting at all, and we must hope the user ID on the keys are useful. Note that we need to do this for new keys, merged keys and even for unchanged keys. This is required because for example the --auto-key-locate feature may import an already imported key and needs to know the fingerprint of the key in all cases. */ if (fpr) { /* Note that we need to compare against 0 here because COUNT gets only incremented after returning from this function. */ if (!stats->count) { xfree (*fpr); *fpr = fingerprint_from_pk (pk, NULL, fpr_len); } else if (origin != KEYORG_WKD) { xfree (*fpr); *fpr = NULL; } } } /* Now that the key is definitely incorporated into the keydb, we need to check if a designated revocation is present or if the prefs are not rational so we can warn the user. */ if (mod_key) { revocation_present (ctrl, keyblock_orig); - if (!from_sk && have_secret_key_with_kid (keyid)) + if (!from_sk && have_secret_key_with_kid (ctrl, keyid)) check_prefs (ctrl, keyblock_orig); } else if (new_key) { revocation_present (ctrl, keyblock); - if (!from_sk && have_secret_key_with_kid (keyid)) + if (!from_sk && have_secret_key_with_kid (ctrl, keyid)) check_prefs (ctrl, keyblock); } release_kbnode( keyblock_orig ); return err; } /* Wrapper around import_one_real to retry the import in some cases. */ static gpg_error_t import_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, unsigned char **fpr, size_t *fpr_len, unsigned int options, int from_sk, int silent, import_screener_t screener, void *screener_arg, int origin, const char *url, int *r_valid) { gpg_error_t err; err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, from_sk, silent, screener, screener_arg, origin, url, r_valid); if (gpg_err_code (err) == GPG_ERR_TOO_LARGE && gpg_err_source (err) == GPG_ERR_SOURCE_KEYBOX && ((options & (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN)) != (IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN))) { /* We hit the maximum image length. Ask the wrapper to do * everything again but this time with some extra options. */ u32 keyid[2]; keyid_from_pk (keyblock->pkt->pkt.public_key, keyid); log_info ("key %s: keyblock too large, retrying with self-sigs-only\n", keystr (keyid)); options |= IMPORT_SELF_SIGS_ONLY | IMPORT_CLEAN; err = import_one_real (ctrl, keyblock, stats, fpr, fpr_len, options, from_sk, silent, screener, screener_arg, origin, url, r_valid); } return err; } /* Transfer all the secret keys in SEC_KEYBLOCK to the gpg-agent. The * function prints diagnostics and returns an error code. If BATCH is * true the secret keys are stored by gpg-agent in the transfer format * (i.e. no re-protection and aksing for passphrases). If ONLY_MARKED * is set, only those nodes with flag NODE_TRANSFER_SECKEY are * processed. */ gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch, int force, int only_marked) { gpg_error_t err = 0; void *kek = NULL; size_t keklen; kbnode_t ctx = NULL; kbnode_t node; PKT_public_key *main_pk, *pk; struct seckey_info *ski; int nskey; membuf_t mbuf; int i, j; void *format_args[2*PUBKEY_MAX_NSKEY]; gcry_sexp_t skey, prot, tmpsexp; gcry_sexp_t curve = NULL; unsigned char *transferkey = NULL; size_t transferkeylen; gcry_cipher_hd_t cipherhd = NULL; unsigned char *wrappedkey = NULL; size_t wrappedkeylen; char *cache_nonce = NULL; int stub_key_skipped = 0; /* Get the current KEK. */ err = agent_keywrap_key (ctrl, 0, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } /* Prepare a cipher context. */ err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) goto leave; xfree (kek); kek = NULL; /* Note: We need to use walk_kbnode so that we skip nodes which are * marked as deleted. */ main_pk = NULL; while ((node = walk_kbnode (sec_keyblock, &ctx, 0))) { if (node->pkt->pkttype != PKT_SECRET_KEY && node->pkt->pkttype != PKT_SECRET_SUBKEY) continue; if (only_marked && !(node->flag & NODE_TRANSFER_SECKEY)) continue; pk = node->pkt->pkt.public_key; if (!main_pk) main_pk = pk; /* Make sure the keyids are available. */ keyid_from_pk (pk, NULL); if (node->pkt->pkttype == PKT_SECRET_KEY) { pk->main_keyid[0] = pk->keyid[0]; pk->main_keyid[1] = pk->keyid[1]; } else { pk->main_keyid[0] = main_pk->keyid[0]; pk->main_keyid[1] = main_pk->keyid[1]; } ski = pk->seckey_info; if (!ski) BUG (); if (stats) { stats->count++; stats->secret_read++; } /* We ignore stub keys. The way we handle them in other parts of the code is by asking the agent whether any secret key is available for a given keyblock and then concluding that we have a secret key; all secret (sub)keys of the keyblock the agent does not know of are then stub keys. This works also for card stub keys. The learn command or the card-status command may be used to check with the agent whether a card has been inserted and a stub key is in turn generated by the agent. */ if (ski->s2k.mode == 1001 || ski->s2k.mode == 1002) { stub_key_skipped = 1; continue; } /* Convert our internal secret key object into an S-expression. */ nskey = pubkey_get_nskey (pk->pubkey_algo); if (!nskey || nskey > PUBKEY_MAX_NSKEY) { err = gpg_error (GPG_ERR_BAD_SECKEY); log_error ("internal error: %s\n", gpg_strerror (err)); goto leave; } init_membuf (&mbuf, 50); put_membuf_str (&mbuf, "(skey"); if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { /* The ECC case. */ char *curvestr = openpgp_oid_to_str (pk->pkey[0]); if (!curvestr) err = gpg_error_from_syserror (); else { const char *curvename = openpgp_oid_to_curve (curvestr, 1); gcry_sexp_release (curve); err = gcry_sexp_build (&curve, NULL, "(curve %s)", curvename?curvename:curvestr); xfree (curvestr); if (!err) { j = 0; /* Append the public key element Q. */ put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + 1; /* Append the secret key element D. For ECDH we skip PKEY[2] because this holds the KEK which is not needed by gpg-agent. */ i = pk->pubkey_algo == PUBKEY_ALGO_ECDH? 3 : 2; if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) put_membuf_str (&mbuf, " e %m"); else put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i; } } } else { /* Standard case for the old (non-ECC) algorithms. */ for (i=j=0; i < nskey; i++) { if (!pk->pkey[i]) continue; /* Protected keys only have NPKEY+1 elements. */ if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER1)) put_membuf_str (&mbuf, " e %m"); else put_membuf_str (&mbuf, " _ %m"); format_args[j++] = pk->pkey + i; } } put_membuf_str (&mbuf, ")"); put_membuf (&mbuf, "", 1); if (err) xfree (get_membuf (&mbuf, NULL)); else { char *format = get_membuf (&mbuf, NULL); if (!format) err = gpg_error_from_syserror (); else err = gcry_sexp_build_array (&skey, NULL, format, format_args); xfree (format); } if (err) { log_error ("error building skey array: %s\n", gpg_strerror (err)); goto leave; } if (ski->is_protected) { char countbuf[35]; /* FIXME: Support AEAD */ /* Note that the IVLEN may be zero if we are working on a dummy key. We can't express that in an S-expression and thus we send dummy data for the IV. */ snprintf (countbuf, sizeof countbuf, "%lu", (unsigned long)ski->s2k.count); err = gcry_sexp_build (&prot, NULL, " (protection %s %s %b %d %s %b %s)\n", ski->sha1chk? "sha1":"sum", openpgp_cipher_algo_name (ski->algo), ski->ivlen? (int)ski->ivlen:1, ski->ivlen? ski->iv: (const unsigned char*)"X", ski->s2k.mode, openpgp_md_algo_name (ski->s2k.hash_algo), (int)sizeof (ski->s2k.salt), ski->s2k.salt, countbuf); } else err = gcry_sexp_build (&prot, NULL, " (protection none)\n"); tmpsexp = NULL; xfree (transferkey); transferkey = NULL; if (!err) err = gcry_sexp_build (&tmpsexp, NULL, "(openpgp-private-key\n" " (version %d)\n" " (algo %s)\n" " %S%S\n" " (csum %d)\n" " %S)\n", pk->version, openpgp_pk_algo_name (pk->pubkey_algo), curve, skey, (int)(unsigned long)ski->csum, prot); gcry_sexp_release (skey); gcry_sexp_release (prot); if (!err) err = make_canon_sexp_pad (tmpsexp, 1, &transferkey, &transferkeylen); gcry_sexp_release (tmpsexp); if (err) { log_error ("error building transfer key: %s\n", gpg_strerror (err)); goto leave; } /* Wrap the key. */ wrappedkeylen = transferkeylen + 8; xfree (wrappedkey); wrappedkey = xtrymalloc (wrappedkeylen); if (!wrappedkey) err = gpg_error_from_syserror (); else err = gcry_cipher_encrypt (cipherhd, wrappedkey, wrappedkeylen, transferkey, transferkeylen); if (err) goto leave; xfree (transferkey); transferkey = NULL; /* Send the wrapped key to the agent. */ { char *desc = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_IMPORT, 1); err = agent_import_key (ctrl, desc, &cache_nonce, wrappedkey, wrappedkeylen, batch, force, pk->keyid, pk->main_keyid, pk->pubkey_algo); xfree (desc); } if (!err) { if (opt.verbose) log_info (_("key %s: secret key imported\n"), keystr_from_pk_with_sub (main_pk, pk)); if (stats) stats->secret_imported++; } else if ( gpg_err_code (err) == GPG_ERR_EEXIST ) { if (opt.verbose) log_info (_("key %s: secret key already exists\n"), keystr_from_pk_with_sub (main_pk, pk)); err = 0; if (stats) stats->secret_dups++; } else { log_error (_("key %s: error sending to agent: %s\n"), keystr_from_pk_with_sub (main_pk, pk), gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) break; /* Don't try the other subkeys. */ } } if (!err && stub_key_skipped) /* We need to notify user how to migrate stub keys. */ err = gpg_error (GPG_ERR_NOT_PROCESSED); leave: gcry_sexp_release (curve); xfree (cache_nonce); xfree (wrappedkey); xfree (transferkey); gcry_cipher_close (cipherhd); xfree (kek); return err; } /* Walk a secret keyblock and produce a public keyblock out of it. * Returns a new node or NULL on error. Modifies the tag field of the * nodes. */ static kbnode_t sec_to_pub_keyblock (kbnode_t sec_keyblock) { kbnode_t pub_keyblock = NULL; kbnode_t ctx = NULL; kbnode_t secnode, pubnode; kbnode_t lastnode = NULL; unsigned int tag = 0; /* Set a tag to all nodes. */ for (secnode = sec_keyblock; secnode; secnode = secnode->next) secnode->tag = ++tag; /* Copy. */ while ((secnode = walk_kbnode (sec_keyblock, &ctx, 0))) { if (secnode->pkt->pkttype == PKT_SECRET_KEY || secnode->pkt->pkttype == PKT_SECRET_SUBKEY) { /* Make a public key. */ PACKET *pkt; PKT_public_key *pk; pkt = xtrycalloc (1, sizeof *pkt); pk = pkt? copy_public_key (NULL, secnode->pkt->pkt.public_key): NULL; if (!pk) { xfree (pkt); release_kbnode (pub_keyblock); return NULL; } if (secnode->pkt->pkttype == PKT_SECRET_KEY) pkt->pkttype = PKT_PUBLIC_KEY; else pkt->pkttype = PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; pubnode = new_kbnode (pkt); } else { pubnode = clone_kbnode (secnode); } pubnode->tag = secnode->tag; if (!pub_keyblock) pub_keyblock = lastnode = pubnode; else { lastnode->next = pubnode; lastnode = pubnode; } } return pub_keyblock; } /* Delete all notes in the keyblock at R_KEYBLOCK which are not in * PUB_KEYBLOCK. Modifies the tags of both keyblock's nodes. */ static gpg_error_t resync_sec_with_pub_keyblock (kbnode_t *r_keyblock, kbnode_t pub_keyblock, kbnode_t *r_removedsecs) { kbnode_t sec_keyblock = *r_keyblock; kbnode_t node, prevnode; unsigned int *taglist; unsigned int ntaglist, n; kbnode_t attic = NULL; kbnode_t *attic_head = &attic; /* Collect all tags in an array for faster searching. */ for (ntaglist = 0, node = pub_keyblock; node; node = node->next) ntaglist++; taglist = xtrycalloc (ntaglist, sizeof *taglist); if (!taglist) return gpg_error_from_syserror (); for (ntaglist = 0, node = pub_keyblock; node; node = node->next) taglist[ntaglist++] = node->tag; /* Walks over the secret keyblock and delete all nodes which are not * in the tag list. Those nodes have been deleted in the * pub_keyblock. Sequential search is a bit lazy and could be * optimized by sorting and bsearch; however secret keyrings are * short and there are easier ways to DoS the import. */ again: for (prevnode=NULL, node=sec_keyblock; node; prevnode=node, node=node->next) { for (n=0; n < ntaglist; n++) if (taglist[n] == node->tag) break; if (n == ntaglist) /* Not in public keyblock. */ { if (node->pkt->pkttype == PKT_SECRET_KEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { if (!prevnode) sec_keyblock = node->next; else prevnode->next = node->next; node->next = NULL; *attic_head = node; attic_head = &node->next; goto again; /* That's lame; I know. */ } else delete_kbnode (node); } } xfree (taglist); /* Commit the as deleted marked nodes and return the possibly * modified keyblock and a list of removed secret key nodes. */ commit_kbnode (&sec_keyblock); *r_keyblock = sec_keyblock; *r_removedsecs = attic; return 0; } /* Helper for import_secret_one. */ static gpg_error_t do_transfer (ctrl_t ctrl, kbnode_t keyblock, PKT_public_key *pk, struct import_stats_s *stats, int batch, int only_marked) { gpg_error_t err; struct import_stats_s subkey_stats = {0}; err = transfer_secret_keys (ctrl, &subkey_stats, keyblock, batch, 0, only_marked); if (gpg_err_code (err) == GPG_ERR_NOT_PROCESSED) { /* TRANSLATORS: For a smartcard, each private key on host has a * reference (stub) to a smartcard and actual private key data * is stored on the card. A single smartcard can have up to * three private key data. Importing private key stub is always * skipped in 2.1, and it returns GPG_ERR_NOT_PROCESSED. * Instead, user should be suggested to run 'gpg --card-status', * then, references to a card will be automatically created * again. */ log_info (_("To migrate '%s', with each smartcard, " "run: %s\n"), "secring.gpg", "gpg --card-status"); err = 0; } if (!err) { int status = 16; if (!opt.quiet) log_info (_("key %s: secret key imported\n"), keystr_from_pk (pk)); if (subkey_stats.secret_imported) { status |= 1; stats->secret_imported += 1; } if (subkey_stats.secret_dups) stats->secret_dups += 1; if (is_status_enabled ()) print_import_ok (pk, status); } return err; } /* If the secret keys (main or subkey) in SECKEYS have a corresponding * public key in the public key described by (FPR,FPRLEN) import these * parts. */ static gpg_error_t import_matching_seckeys (ctrl_t ctrl, kbnode_t seckeys, const byte *mainfpr, size_t mainfprlen, struct import_stats_s *stats, int batch) { gpg_error_t err; kbnode_t pub_keyblock = NULL; kbnode_t node; struct { byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; } *fprlist = NULL; size_t n, nfprlist; byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; PKT_public_key *pk; /* Get the entire public key block from our keystore and put all its * fingerprints into an array. */ err = get_pubkey_byfprint (ctrl, NULL, &pub_keyblock, mainfpr, mainfprlen); if (err) goto leave; log_assert (pub_keyblock && pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); pk = pub_keyblock->pkt->pkt.public_key; for (nfprlist = 0, node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) nfprlist++; log_assert (nfprlist); fprlist = xtrycalloc (nfprlist, sizeof *fprlist); if (!fprlist) { err = gpg_error_from_syserror (); goto leave; } for (n = 0, node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { fingerprint_from_pk (node->pkt->pkt.public_key, fprlist[n].fpr, &fprlist[n].fprlen); n++; } log_assert (n == nfprlist); /* for (n=0; n < nfprlist; n++) */ /* log_printhex (fprlist[n].fpr, fprlist[n].fprlen, "pubkey %zu:", n); */ /* Mark all secret keys which have a matching public key part in * PUB_KEYBLOCK. */ for (node = seckeys; node; node = node->next) { if (node->pkt->pkttype != PKT_SECRET_KEY && node->pkt->pkttype != PKT_SECRET_SUBKEY) continue; /* Should not happen. */ fingerprint_from_pk (node->pkt->pkt.public_key, fpr, &fprlen); node->flag &= ~NODE_TRANSFER_SECKEY; for (n=0; n < nfprlist; n++) if (fprlist[n].fprlen == fprlen && !memcmp (fprlist[n].fpr,fpr,fprlen)) { node->flag |= NODE_TRANSFER_SECKEY; /* log_debug ("found matching seckey\n"); */ break; } } /* Transfer all marked keys. */ err = do_transfer (ctrl, seckeys, pk, stats, batch, 1); leave: xfree (fprlist); release_kbnode (pub_keyblock); return err; } /* Import function for a single secret keyblock. Handling is simpler * than for public keys. We allow secret key importing only when * allow is true, this is so that a secret key can not be imported * accidentally and thereby tampering with the trust calculation. * * Ownership of KEYBLOCK is transferred to this function! * * If R_SECATTIC is not null the last special sec_keyblock is stored * there. */ static gpg_error_t import_secret_one (ctrl_t ctrl, kbnode_t keyblock, struct import_stats_s *stats, int batch, unsigned int options, int for_migration, import_screener_t screener, void *screener_arg, kbnode_t *r_secattic) { PKT_public_key *pk; struct seckey_info *ski; kbnode_t node, uidnode; u32 keyid[2]; gpg_error_t err = 0; int nr_prev; kbnode_t pub_keyblock; kbnode_t attic = NULL; byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; char pkstrbuf[PUBKEY_STRING_SIZE]; /* Get the key and print some info about it */ node = find_kbnode (keyblock, PKT_SECRET_KEY); if (!node) BUG (); pk = node->pkt->pkt.public_key; fingerprint_from_pk (pk, fpr, &fprlen); keyid_from_pk (pk, keyid); uidnode = find_next_kbnode (keyblock, PKT_USER_ID); if (screener && screener (keyblock, screener_arg)) { log_error (_("secret key %s: %s\n"), keystr_from_pk (pk), _("rejected by import screener")); release_kbnode (keyblock); return 0; } if (opt.verbose && !for_migration) { log_info ("sec %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk), datestr_from_pk (pk)); if (uidnode) print_utf8_buffer (log_get_stream (), uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len); log_printf ("\n"); } stats->secret_read++; if ((options & IMPORT_NO_SECKEY)) { if (!for_migration) log_error (_("importing secret keys not allowed\n")); release_kbnode (keyblock); return 0; } if (!uidnode) { if (!for_migration) log_error( _("key %s: no user ID\n"), keystr_from_pk (pk)); release_kbnode (keyblock); return 0; } ski = pk->seckey_info; if (!ski) { /* Actually an internal error. */ log_error ("key %s: secret key info missing\n", keystr_from_pk (pk)); release_kbnode (keyblock); return 0; } /* A quick check to not import keys with an invalid protection cipher algorithm (only checks the primary key, though). */ if (ski->algo > 110) { if (!for_migration) log_error (_("key %s: secret key with invalid cipher %d" " - skipped\n"), keystr_from_pk (pk), ski->algo); release_kbnode (keyblock); return 0; } #ifdef ENABLE_SELINUX_HACKS if (1) { /* We don't allow importing secret keys because that may be used to put a secret key into the keyring and the user might later be tricked into signing stuff with that key. */ log_error (_("importing secret keys not allowed\n")); release_kbnode (keyblock); return 0; } #endif clear_kbnode_flags (keyblock); nr_prev = stats->skipped_new_keys; /* Make a public key out of the key. */ pub_keyblock = sec_to_pub_keyblock (keyblock); if (!pub_keyblock) { err = gpg_error_from_syserror (); log_error ("key %s: failed to create public key from secret key\n", keystr_from_pk (pk)); } else { int valid; /* Note that this outputs an IMPORT_OK status message for the public key block, and below we will output another one for the secret keys. FIXME? */ import_one (ctrl, pub_keyblock, stats, NULL, NULL, options, 1, for_migration, screener, screener_arg, 0, NULL, &valid); /* The secret keyblock may not have nodes which are deleted in * the public keyblock. Otherwise we would import just the * secret key without having the public key. That would be * surprising and clutters our private-keys-v1.d. */ err = resync_sec_with_pub_keyblock (&keyblock, pub_keyblock, &attic); if (err) goto leave; if (!valid) { /* If the block was not valid the primary key is left in the * original keyblock because we require that for the first * node. Move it to ATTIC. */ if (keyblock && keyblock->pkt->pkttype == PKT_SECRET_KEY) { node = keyblock; keyblock = node->next; node->next = NULL; if (attic) { node->next = attic; attic = node; } else attic = node; } /* Try to import the secret key iff we have a public key. */ if (attic && !(opt.dry_run || (options & IMPORT_DRY_RUN))) err = import_matching_seckeys (ctrl, attic, fpr, fprlen, stats, batch); else err = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } /* log_debug ("attic is:\n"); */ /* dump_kbnode (attic); */ /* Proceed with the valid parts of PUBKEYBLOCK. */ /* At least we cancel the secret key import when the public key import was skipped due to MERGE_ONLY option and a new key. */ if (!(opt.dry_run || (options & IMPORT_DRY_RUN)) && stats->skipped_new_keys <= nr_prev) { /* Read the keyblock again to get the effects of a merge for * the public key. */ err = get_pubkey_byfprint (ctrl, NULL, &node, fpr, fprlen); if (err || !node) log_error ("key %s: failed to re-lookup public key: %s\n", keystr_from_pk (pk), gpg_strerror (err)); else { err = do_transfer (ctrl, keyblock, pk, stats, batch, 0); if (!err) check_prefs (ctrl, node); release_kbnode (node); if (!err && attic) { /* Try to import invalid subkeys. This can be the * case if the primary secret key was imported due * to --allow-non-selfsigned-uid. */ err = import_matching_seckeys (ctrl, attic, fpr, fprlen, stats, batch); } } } } leave: release_kbnode (keyblock); release_kbnode (pub_keyblock); if (r_secattic) *r_secattic = attic; else release_kbnode (attic); return err; } /* Return the recocation reason from signature SIG. If no revocation * reason is availabale 0 is returned, in other cases the reason * (0..255). If R_REASON is not NULL a malloced textual * representation of the code is stored there. If R_COMMENT is not * NULL the comment from the reason is stored there and its length at * R_COMMENTLEN. Note that the value at R_COMMENT is not filtered but * user supplied data in UTF8; thus it needs to be escaped for display * purposes. Both return values are either NULL or a malloced * string/buffer. */ int get_revocation_reason (PKT_signature *sig, char **r_reason, char **r_comment, size_t *r_commentlen) { int reason_seq = 0; size_t reason_n; const byte *reason_p; char reason_code_buf[20]; const char *reason_text = NULL; int reason_code = 0; if (r_reason) *r_reason = NULL; if (r_comment) *r_comment = NULL; /* Skip over empty reason packets. */ while ((reason_p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REVOC_REASON, &reason_n, &reason_seq, NULL)) && !reason_n) ; if (reason_p) { reason_code = *reason_p; reason_n--; reason_p++; switch (reason_code) { case 0x00: reason_text = _("No reason specified"); break; case 0x01: reason_text = _("Key is superseded"); break; case 0x02: reason_text = _("Key has been compromised"); break; case 0x03: reason_text = _("Key is no longer used"); break; case 0x20: reason_text = _("User ID is no longer valid"); break; default: snprintf (reason_code_buf, sizeof reason_code_buf, "code=%02x", reason_code); reason_text = reason_code_buf; break; } if (r_reason) *r_reason = xstrdup (reason_text); if (r_comment && reason_n) { *r_comment = xmalloc (reason_n); memcpy (*r_comment, reason_p, reason_n); *r_commentlen = reason_n; } } return reason_code; } /* List the recocation signature as a "rvs" record. SIGRC shows the * character from the signature verification or 0 if no public key was * found. */ static void list_standalone_revocation (ctrl_t ctrl, PKT_signature *sig, int sigrc) { char *siguid = NULL; size_t siguidlen = 0; char *issuer_fpr = NULL; int reason_code = 0; char *reason_text = NULL; char *reason_comment = NULL; size_t reason_commentlen; if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) { int nouid; siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid); if (nouid) sigrc = '?'; } reason_code = get_revocation_reason (sig, &reason_text, &reason_comment, &reason_commentlen); if (opt.with_colons) { es_fputs ("rvs:", es_stdout); if (sigrc) es_putc (sigrc, es_stdout); es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:::", sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], colon_datestr_from_sig (sig), colon_expirestr_from_sig (sig)); if (siguid) es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); es_fprintf (es_stdout, ":%02x%c", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); if (reason_text) es_fprintf (es_stdout, ",%02x", reason_code); es_fputs ("::", es_stdout); if ((issuer_fpr = issuer_fpr_string (sig))) es_fputs (issuer_fpr, es_stdout); es_fprintf (es_stdout, ":::%d:", sig->digest_algo); if (reason_comment) { es_fputs ("::::", es_stdout); es_write_sanitized (es_stdout, reason_comment, reason_commentlen, ":", NULL); es_putc (':', es_stdout); } es_putc ('\n', es_stdout); if (opt.show_subpackets) print_subpackets_colon (sig); } else /* Human readable. */ { es_fputs ("rvs", es_stdout); es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", sigrc, (sig->sig_class - 0x10 > 0 && sig->sig_class - 0x10 < 4) ? '0' + sig->sig_class - 0x10 : ' ', sig->flags.exportable ? ' ' : 'L', sig->flags.revocable ? ' ' : 'R', sig->flags.policy_url ? 'P' : ' ', sig->flags.notation ? 'N' : ' ', sig->flags.expired ? 'X' : ' ', (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > 0) ? '0' + sig->trust_depth : ' ', keystr (sig->keyid), datestr_from_sig (sig)); if (siguid) { es_fprintf (es_stdout, " "); print_utf8_buffer (es_stdout, siguid, siguidlen); } es_putc ('\n', es_stdout); if (sig->flags.policy_url && (opt.list_options & LIST_SHOW_POLICY_URLS)) show_policy_url (sig, 3, 0); if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) show_notation (sig, 3, 0, ((opt.list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + ((opt.list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0)); if (sig->flags.pref_ks && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 3, 0); if (reason_text) { es_fprintf (es_stdout, " %s%s\n", _("reason for revocation: "), reason_text); if (reason_comment) { const byte *s, *s_lf; size_t n, n_lf; s = reason_comment; n = reason_commentlen; s_lf = NULL; do { /* We don't want any empty lines, so we skip them. */ for (;n && *s == '\n'; s++, n--) ; if (n) { s_lf = memchr (s, '\n', n); n_lf = s_lf? s_lf - s : n; es_fprintf (es_stdout, " %s", _("revocation comment: ")); es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); es_putc ('\n', es_stdout); s += n_lf; n -= n_lf; } } while (s_lf); } } } es_fflush (es_stdout); xfree (reason_text); xfree (reason_comment); xfree (siguid); xfree (issuer_fpr); } /**************** * Import a revocation certificate; this is a single signature packet. */ static int import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, struct import_stats_s *stats) { PKT_public_key *pk = NULL; kbnode_t onode; kbnode_t keyblock = NULL; KEYDB_HANDLE hd = NULL; u32 keyid[2]; int rc = 0; int sigrc = 0; int silent; /* No error output for --show-keys. */ silent = (options & (IMPORT_SHOW | IMPORT_DRY_RUN)); log_assert (!node->next ); log_assert (node->pkt->pkttype == PKT_SIGNATURE ); log_assert (IS_KEY_REV (node->pkt->pkt.signature)); keyid[0] = node->pkt->pkt.signature->keyid[0]; keyid[1] = node->pkt->pkt.signature->keyid[1]; pk = xmalloc_clear( sizeof *pk ); rc = get_pubkey (ctrl, pk, keyid ); if (gpg_err_code (rc) == GPG_ERR_NO_PUBKEY ) { if (!silent) log_error (_("key %s: no public key -" " can't apply revocation certificate\n"), keystr(keyid)); rc = 0; goto leave; } else if (rc ) { log_error (_("key %s: public key not found: %s\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } /* Read the original keyblock. */ - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) { rc = gpg_error_from_syserror (); goto leave; } { byte afp[MAX_FINGERPRINT_LEN]; size_t an; fingerprint_from_pk (pk, afp, &an); rc = keydb_search_fpr (hd, afp, an); } if (rc) { log_error (_("key %s: can't locate original keyblock: %s\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } rc = keydb_get_keyblock (hd, &keyblock ); if (rc) { log_error (_("key %s: can't read original keyblock: %s\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } /* it is okay, that node is not in keyblock because * check_key_signature works fine for sig_class 0x20 (KEY_REV) in * this special case. SIGRC is only used for IMPORT_SHOW. */ rc = check_key_signature (ctrl, keyblock, node, NULL); switch (gpg_err_code (rc)) { case 0: sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: sigrc = '?'; break; case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } if (rc ) { if (!silent) log_error (_("key %s: invalid revocation certificate" ": %s - rejected\n"), keystr(keyid), gpg_strerror (rc)); goto leave; } /* check whether we already have this */ for(onode=keyblock->next; onode; onode=onode->next ) { if (onode->pkt->pkttype == PKT_USER_ID ) break; else if (onode->pkt->pkttype == PKT_SIGNATURE && !cmp_signatures(node->pkt->pkt.signature, onode->pkt->pkt.signature)) { rc = 0; goto leave; /* yes, we already know about it */ } } /* insert it */ insert_kbnode( keyblock, clone_kbnode(node), 0 ); /* and write the keyblock back unless in dry run mode. */ if (!(opt.dry_run || (options & IMPORT_DRY_RUN))) { rc = keydb_update_keyblock (ctrl, hd, keyblock ); if (rc) log_error (_("error writing keyring '%s': %s\n"), keydb_get_resource_name (hd), gpg_strerror (rc) ); keydb_release (hd); hd = NULL; /* we are ready */ if (!opt.quiet ) { char *p=get_user_id_native (ctrl, keyid); log_info( _("key %s: \"%s\" revocation certificate imported\n"), keystr(keyid),p); xfree(p); } /* If the key we just revoked was ultimately trusted, remove its * ultimate trust. This doesn't stop the user from putting the * ultimate trust back, but is a reasonable solution for now. */ if (get_ownertrust (ctrl, pk) == TRUST_ULTIMATE) clear_ownertrusts (ctrl, pk); revalidation_mark (ctrl); } stats->n_revoc++; leave: if ((options & IMPORT_SHOW)) list_standalone_revocation (ctrl, node->pkt->pkt.signature, sigrc); keydb_release (hd); release_kbnode( keyblock ); free_public_key( pk ); return rc; } /* Loop over the KEYBLOCK and check all self signatures. KEYID is the * keyid of the primary key for reporting purposes. On return the * following bits in the node flags are set: * * - NODE_GOOD_SELFSIG :: User ID or subkey has a self-signature * - NODE_BAD_SELFSIG :: Used ID or subkey has an invalid self-signature * - NODE_DELETION_MARK :: This node shall be deleted * * NON_SELF is set to true if there are any sigs other than self-sigs * in this keyblock. * * Returns 0 on success or -1 (but not an error code) if the keyblock * is invalid. */ static int chk_self_sigs (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, int *non_self) { kbnode_t knode = NULL; /* The node of the current subkey. */ PKT_public_key *subpk = NULL; /* and its packet. */ kbnode_t bsnode = NULL; /* Subkey binding signature node. */ u32 bsdate = 0; /* Timestamp of that node. */ kbnode_t rsnode = NULL; /* Subkey recocation signature node. */ u32 rsdate = 0; /* Timestamp of that node. */ PKT_signature *sig; int rc; kbnode_t n; for (n=keyblock; (n = find_next_kbnode (n, 0)); ) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) { knode = n; subpk = knode->pkt->pkt.public_key; bsdate = 0; rsdate = 0; bsnode = NULL; rsnode = NULL; continue; } if ( n->pkt->pkttype != PKT_SIGNATURE ) continue; sig = n->pkt->pkt.signature; if ( keyid[0] != sig->keyid[0] || keyid[1] != sig->keyid[1] ) { *non_self = 1; continue; } /* This just caches the sigs for later use. That way we import a fully-cached key which speeds things up. */ if (!opt.no_sig_cache) check_key_signature (ctrl, keyblock, n, NULL); if ( IS_UID_SIG(sig) || IS_UID_REV(sig) ) { kbnode_t unode = find_prev_kbnode( keyblock, n, PKT_USER_ID ); if ( !unode ) { log_error( _("key %s: no user ID for signature\n"), keystr(keyid)); return -1; /* The complete keyblock is invalid. */ } /* If it hasn't been marked valid yet, keep trying. */ if (!(unode->flag & NODE_GOOD_SELFSIG)) { rc = check_key_signature (ctrl, keyblock, n, NULL); if ( rc ) { if ( opt.verbose ) { char *p = utf8_to_native (unode->pkt->pkt.user_id->name, strlen (unode->pkt->pkt.user_id->name),0); log_info (gpg_err_code(rc) == GPG_ERR_PUBKEY_ALGO ? _("key %s: unsupported public key " "algorithm on user ID \"%s\"\n"): _("key %s: invalid self-signature " "on user ID \"%s\"\n"), keystr (keyid),p); xfree (p); } } else unode->flag |= NODE_GOOD_SELFSIG; } } else if (IS_KEY_SIG (sig)) { rc = check_key_signature (ctrl, keyblock, n, NULL); if ( rc ) { if (opt.verbose) log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? _("key %s: unsupported public key algorithm\n"): _("key %s: invalid direct key signature\n"), keystr (keyid)); n->flag |= NODE_DELETION_MARK; } } else if ( IS_SUBKEY_SIG (sig) ) { /* Note that this works based solely on the timestamps like the rest of gpg. If the standard gets revocation targets, this may need to be revised. */ if ( !knode ) { if (opt.verbose) log_info (_("key %s: no subkey for key binding\n"), keystr (keyid)); n->flag |= NODE_DELETION_MARK; } else { rc = check_key_signature (ctrl, keyblock, n, NULL); if ( rc ) { if (opt.verbose) { keyid_from_pk (subpk, NULL); log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? _("key %s: unsupported public key" " algorithm\n"): _("key %s: invalid subkey binding\n"), keystr_with_sub (keyid, subpk->keyid)); } n->flag |= NODE_DELETION_MARK; } else { /* It's valid, so is it newer? */ if (sig->timestamp >= bsdate) { knode->flag |= NODE_GOOD_SELFSIG; /* Subkey is valid. */ if (bsnode) { /* Delete the last binding sig since this one is newer */ bsnode->flag |= NODE_DELETION_MARK; if (opt.verbose) { keyid_from_pk (subpk, NULL); log_info (_("key %s: removed multiple subkey" " binding\n"), keystr_with_sub (keyid, subpk->keyid)); } } bsnode = n; bsdate = sig->timestamp; } else n->flag |= NODE_DELETION_MARK; /* older */ } } } else if ( IS_SUBKEY_REV (sig) ) { /* We don't actually mark the subkey as revoked right now, so just check that the revocation sig is the most recent valid one. Note that we don't care if the binding sig is newer than the revocation sig. See the comment in getkey.c:merge_selfsigs_subkey for more. */ if ( !knode ) { if (opt.verbose) log_info (_("key %s: no subkey for key revocation\n"), keystr(keyid)); n->flag |= NODE_DELETION_MARK; } else { rc = check_key_signature (ctrl, keyblock, n, NULL); if ( rc ) { if(opt.verbose) log_info (gpg_err_code (rc) == GPG_ERR_PUBKEY_ALGO ? _("key %s: unsupported public" " key algorithm\n"): _("key %s: invalid subkey revocation\n"), keystr(keyid)); n->flag |= NODE_DELETION_MARK; } else { /* It's valid, so is it newer? */ if (sig->timestamp >= rsdate) { if (rsnode) { /* Delete the last revocation sig since this one is newer. */ rsnode->flag |= NODE_DELETION_MARK; if (opt.verbose) log_info (_("key %s: removed multiple subkey" " revocation\n"),keystr(keyid)); } rsnode = n; rsdate = sig->timestamp; } else n->flag |= NODE_DELETION_MARK; /* older */ } } } } return 0; } /* Delete all parts which are invalid and those signatures whose * public key algorithm is not available in this implementation; but * consider RSA as valid, because parse/build_packets knows about it. * * Returns: True if at least one valid user-id is left over. */ static int delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, unsigned int options) { kbnode_t node; int nvalid=0, uid_seen=0, subkey_seen=0; PKT_public_key *pk; for (node=keyblock->next; node; node = node->next ) { if (node->pkt->pkttype == PKT_USER_ID) { uid_seen = 1; if ((node->flag & NODE_BAD_SELFSIG) || !(node->flag & NODE_GOOD_SELFSIG)) { if (opt.verbose ) { char *p=utf8_to_native(node->pkt->pkt.user_id->name, node->pkt->pkt.user_id->len,0); log_info( _("key %s: skipped user ID \"%s\"\n"), keystr(keyid),p); xfree(p); } delete_kbnode( node ); /* the user-id */ /* and all following packets up to the next user-id */ while (node->next && node->next->pkt->pkttype != PKT_USER_ID && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ){ delete_kbnode( node->next ); node = node->next; } } else nvalid++; } else if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY ) { if ((node->flag & NODE_BAD_SELFSIG) || !(node->flag & NODE_GOOD_SELFSIG)) { if (opt.verbose ) { pk = node->pkt->pkt.public_key; keyid_from_pk (pk, NULL); log_info (_("key %s: skipped subkey\n"), keystr_with_sub (keyid, pk->keyid)); } delete_kbnode( node ); /* the subkey */ /* and all following signature packets */ while (node->next && node->next->pkt->pkttype == PKT_SIGNATURE ) { delete_kbnode( node->next ); node = node->next; } } else subkey_seen = 1; } else if (node->pkt->pkttype == PKT_SIGNATURE && openpgp_pk_test_algo (node->pkt->pkt.signature->pubkey_algo) && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA ) { delete_kbnode( node ); /* build_packet() can't handle this */ } else if (node->pkt->pkttype == PKT_SIGNATURE && !node->pkt->pkt.signature->flags.exportable && !(options&IMPORT_LOCAL_SIGS) - && !have_secret_key_with_kid (node->pkt->pkt.signature->keyid)) + && !have_secret_key_with_kid (ctrl, + node->pkt->pkt.signature->keyid)) { /* here we violate the rfc a bit by still allowing * to import non-exportable signature when we have the * the secret key used to create this signature - it * seems that this makes sense */ if(opt.verbose) log_info( _("key %s: non exportable signature" " (class 0x%02X) - skipped\n"), keystr(keyid), node->pkt->pkt.signature->sig_class ); delete_kbnode( node ); } else if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (node->pkt->pkt.signature)) { if (uid_seen ) { if(opt.verbose) log_info( _("key %s: revocation certificate" " at wrong place - skipped\n"),keystr(keyid)); delete_kbnode( node ); } else { /* If the revocation cert is from a different key than the one we're working on don't check it - it's probably from a revocation key and won't be verifiable with this key anyway. */ if(node->pkt->pkt.signature->keyid[0]==keyid[0] && node->pkt->pkt.signature->keyid[1]==keyid[1]) { int rc = check_key_signature (ctrl, keyblock, node, NULL); if (rc ) { if(opt.verbose) log_info( _("key %s: invalid revocation" " certificate: %s - skipped\n"), keystr(keyid), gpg_strerror (rc)); delete_kbnode( node ); } } } } else if (node->pkt->pkttype == PKT_SIGNATURE && (IS_SUBKEY_SIG (node->pkt->pkt.signature) || IS_SUBKEY_REV (node->pkt->pkt.signature)) && !subkey_seen ) { if(opt.verbose) log_info( _("key %s: subkey signature" " in wrong place - skipped\n"), keystr(keyid)); delete_kbnode( node ); } else if (node->pkt->pkttype == PKT_SIGNATURE && !IS_CERT(node->pkt->pkt.signature)) { if(opt.verbose) log_info(_("key %s: unexpected signature class (0x%02X) -" " skipped\n"),keystr(keyid), node->pkt->pkt.signature->sig_class); delete_kbnode(node); } else if ((node->flag & NODE_DELETION_MARK)) delete_kbnode( node ); } /* note: because keyblock is the public key, it is never marked * for deletion and so keyblock cannot change */ commit_kbnode( &keyblock ); return nvalid; } /* This function returns true if any UID is left in the keyring. */ static int any_uid_left (kbnode_t keyblock) { kbnode_t node; for (node=keyblock->next; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) return 1; return 0; } /* Delete all user ids from KEYBLOCK. * Returns: True if the keyblock has changed. */ static int remove_all_uids (kbnode_t *keyblock) { kbnode_t node; int any = 0; for (node = *keyblock; node; node = node->next) { if (is_deleted_kbnode (node)) continue; if (node->pkt->pkttype != PKT_USER_ID) continue; /* We are at the first user id. Delete everything up to the * first subkey. */ for (; node; node = node->next) { if (is_deleted_kbnode (node)) continue; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) break; delete_kbnode (node); any = 1; } break; /* All done. */ } commit_kbnode (keyblock); return any; } /* Delete all non-self-sigs from KEYBLOCK. * Returns: True if the keyblock has changed. */ static void remove_all_non_self_sigs (kbnode_t *keyblock, u32 *keyid) { kbnode_t node; unsigned int dropped = 0; for (node = *keyblock; node; node = node->next) { if (is_deleted_kbnode (node)) continue; if (node->pkt->pkttype != PKT_SIGNATURE) continue; if (node->pkt->pkt.signature->keyid[0] == keyid[0] && node->pkt->pkt.signature->keyid[1] == keyid[1]) continue; delete_kbnode (node); dropped++; } if (dropped) commit_kbnode (keyblock); if (dropped && opt.verbose) log_info ("key %s: number of dropped non-self-signatures: %u\n", keystr (keyid), dropped); } /* * It may happen that the imported keyblock has duplicated user IDs. * We check this here and collapse those user IDs together with their * sigs into one. * Returns: True if the keyblock has changed. */ int collapse_uids (kbnode_t *keyblock) { kbnode_t uid1; int any=0; for(uid1=*keyblock;uid1;uid1=uid1->next) { kbnode_t uid2; if(is_deleted_kbnode(uid1)) continue; if(uid1->pkt->pkttype!=PKT_USER_ID) continue; for(uid2=uid1->next;uid2;uid2=uid2->next) { if(is_deleted_kbnode(uid2)) continue; if(uid2->pkt->pkttype!=PKT_USER_ID) continue; if(cmp_user_ids(uid1->pkt->pkt.user_id, uid2->pkt->pkt.user_id)==0) { /* We have a duplicated uid */ kbnode_t sig1,last; any=1; /* Now take uid2's signatures, and attach them to uid1 */ for(last=uid2;last->next;last=last->next) { if(is_deleted_kbnode(last)) continue; if(last->next->pkt->pkttype==PKT_USER_ID || last->next->pkt->pkttype==PKT_PUBLIC_SUBKEY || last->next->pkt->pkttype==PKT_SECRET_SUBKEY) break; } /* Snip out uid2 */ (find_prev_kbnode(*keyblock,uid2,0))->next=last->next; /* Now put uid2 in place as part of uid1 */ last->next=uid1->next; uid1->next=uid2; delete_kbnode(uid2); /* Now dedupe uid1 */ for(sig1=uid1->next;sig1;sig1=sig1->next) { kbnode_t sig2; if(is_deleted_kbnode(sig1)) continue; if(sig1->pkt->pkttype==PKT_USER_ID || sig1->pkt->pkttype==PKT_PUBLIC_SUBKEY || sig1->pkt->pkttype==PKT_SECRET_SUBKEY) break; if(sig1->pkt->pkttype!=PKT_SIGNATURE) continue; for(sig2=sig1->next,last=sig1;sig2;last=sig2,sig2=sig2->next) { if(is_deleted_kbnode(sig2)) continue; if(sig2->pkt->pkttype==PKT_USER_ID || sig2->pkt->pkttype==PKT_PUBLIC_SUBKEY || sig2->pkt->pkttype==PKT_SECRET_SUBKEY) break; if(sig2->pkt->pkttype!=PKT_SIGNATURE) continue; if(cmp_signatures(sig1->pkt->pkt.signature, sig2->pkt->pkt.signature)==0) { /* We have a match, so delete the second signature */ delete_kbnode(sig2); sig2=last; } } } } } } commit_kbnode(keyblock); if(any && !opt.quiet) { const char *key="???"; if ((uid1 = find_kbnode (*keyblock, PKT_PUBLIC_KEY)) ) key = keystr_from_pk (uid1->pkt->pkt.public_key); else if ((uid1 = find_kbnode( *keyblock, PKT_SECRET_KEY)) ) key = keystr_from_pk (uid1->pkt->pkt.public_key); log_info (_("key %s: duplicated user ID detected - merged\n"), key); } return any; } /* Check for a 0x20 revocation from a revocation key that is not present. This may be called without the benefit of merge_xxxx so you can't rely on pk->revkey and friends. */ static void revocation_present (ctrl_t ctrl, kbnode_t keyblock) { kbnode_t onode, inode; PKT_public_key *pk = keyblock->pkt->pkt.public_key; for(onode=keyblock->next;onode;onode=onode->next) { /* If we reach user IDs, we're done. */ if(onode->pkt->pkttype==PKT_USER_ID) break; if (onode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (onode->pkt->pkt.signature) && onode->pkt->pkt.signature->revkey) { int idx; PKT_signature *sig=onode->pkt->pkt.signature; for(idx=0;idxnumrevkeys;idx++) { u32 keyid[2]; keyid_from_fingerprint (ctrl, sig->revkey[idx].fpr, sig->revkey[idx].fprlen, keyid); for(inode=keyblock->next;inode;inode=inode->next) { /* If we reach user IDs, we're done. */ if(inode->pkt->pkttype==PKT_USER_ID) break; if (inode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (inode->pkt->pkt.signature) && inode->pkt->pkt.signature->keyid[0]==keyid[0] && inode->pkt->pkt.signature->keyid[1]==keyid[1]) { /* Okay, we have a revocation key, and a * revocation issued by it. Do we have the key * itself? */ gpg_error_t err; - err = get_pubkey_byfprint_fast (NULL, + err = get_pubkey_byfprint_fast (ctrl, NULL, sig->revkey[idx].fpr, sig->revkey[idx].fprlen); if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY || gpg_err_code (err) == GPG_ERR_UNUSABLE_PUBKEY) { char *tempkeystr = xstrdup (keystr_from_pk (pk)); /* No, so try and get it */ if ((opt.keyserver_options.options & KEYSERVER_AUTO_KEY_RETRIEVE) && keyserver_any_configured (ctrl)) { log_info(_("WARNING: key %s may be revoked:" " fetching revocation key %s\n"), tempkeystr,keystr(keyid)); keyserver_import_fprint (ctrl, sig->revkey[idx].fpr, sig->revkey[idx].fprlen, opt.keyserver, 0); /* Do we have it now? */ - err = get_pubkey_byfprint_fast (NULL, + err = get_pubkey_byfprint_fast (ctrl, NULL, sig->revkey[idx].fpr, sig->revkey[idx].fprlen); } if (gpg_err_code (err) == GPG_ERR_NO_PUBKEY || gpg_err_code (err) == GPG_ERR_UNUSABLE_PUBKEY) log_info(_("WARNING: key %s may be revoked:" " revocation key %s not present.\n"), tempkeystr,keystr(keyid)); xfree(tempkeystr); } } } } } } } /* * compare and merge the blocks * * o compare the signatures: If we already have this signature, check * that they compare okay; if not, issue a warning and ask the user. * o Simply add the signature. Can't verify here because we may not have * the signature's public key yet; verification is done when putting it * into the trustdb, which is done automagically as soon as this pubkey * is used. * Note: We indicate newly inserted packets with NODE_FLAG_A. */ static int merge_blocks (ctrl_t ctrl, unsigned int options, kbnode_t keyblock_orig, kbnode_t keyblock, u32 *keyid, u32 curtime, int origin, const char *url, int *n_uids, int *n_sigs, int *n_subk ) { kbnode_t onode, node; int rc, found; /* 1st: handle revocation certificates */ for (node=keyblock->next; node; node=node->next ) { if (node->pkt->pkttype == PKT_USER_ID ) break; else if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (node->pkt->pkt.signature)) { /* check whether we already have this */ found = 0; for (onode=keyblock_orig->next; onode; onode=onode->next) { if (onode->pkt->pkttype == PKT_USER_ID ) break; else if (onode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_REV (onode->pkt->pkt.signature) && !cmp_signatures(onode->pkt->pkt.signature, node->pkt->pkt.signature)) { found = 1; break; } } if (!found) { kbnode_t n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= NODE_FLAG_A; ++*n_sigs; if(!opt.quiet) { char *p = get_user_id_native (ctrl, keyid); log_info(_("key %s: \"%s\" revocation" " certificate added\n"), keystr(keyid),p); xfree(p); } } } } /* 2nd: merge in any direct key (0x1F) sigs */ for(node=keyblock->next; node; node=node->next) { if (node->pkt->pkttype == PKT_USER_ID ) break; else if (node->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (node->pkt->pkt.signature)) { /* check whether we already have this */ found = 0; for (onode=keyblock_orig->next; onode; onode=onode->next) { if (onode->pkt->pkttype == PKT_USER_ID) break; else if (onode->pkt->pkttype == PKT_SIGNATURE && IS_KEY_SIG (onode->pkt->pkt.signature) && !cmp_signatures(onode->pkt->pkt.signature, node->pkt->pkt.signature)) { found = 1; break; } } if (!found ) { kbnode_t n2 = clone_kbnode(node); insert_kbnode( keyblock_orig, n2, 0 ); n2->flag |= NODE_FLAG_A; ++*n_sigs; if(!opt.quiet) log_info( _("key %s: direct key signature added\n"), keystr(keyid)); } } } /* 3rd: try to merge new certificates in */ for (onode=keyblock_orig->next; onode; onode=onode->next) { if (!(onode->flag & NODE_FLAG_A) && onode->pkt->pkttype == PKT_USER_ID) { /* find the user id in the imported keyblock */ for (node=keyblock->next; node; node=node->next) if (node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if (node ) /* found: merge */ { rc = merge_sigs (onode, node, n_sigs); if (rc ) return rc; } } } /* 4th: add new user-ids */ for (node=keyblock->next; node; node=node->next) { if (node->pkt->pkttype == PKT_USER_ID) { /* do we have this in the original keyblock */ for (onode=keyblock_orig->next; onode; onode=onode->next ) if (onode->pkt->pkttype == PKT_USER_ID && !cmp_user_ids( onode->pkt->pkt.user_id, node->pkt->pkt.user_id ) ) break; if (!onode ) /* this is a new user id: append */ { rc = append_new_uid (options, keyblock_orig, node, curtime, origin, url, n_sigs); if (rc ) return rc; ++*n_uids; } } } /* 5th: add new subkeys */ for (node=keyblock->next; node; node=node->next) { onode = NULL; if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { /* do we have this in the original keyblock? */ for(onode=keyblock_orig->next; onode; onode=onode->next) if (onode->pkt->pkttype == PKT_PUBLIC_SUBKEY && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key)) break; if (!onode ) /* This is a new subkey: append. */ { rc = append_key (keyblock_orig, node, n_sigs); if (rc) return rc; ++*n_subk; } } else if (node->pkt->pkttype == PKT_SECRET_SUBKEY) { /* do we have this in the original keyblock? */ for (onode=keyblock_orig->next; onode; onode=onode->next ) if (onode->pkt->pkttype == PKT_SECRET_SUBKEY && !cmp_public_keys (onode->pkt->pkt.public_key, node->pkt->pkt.public_key) ) break; if (!onode ) /* This is a new subkey: append. */ { rc = append_key (keyblock_orig, node, n_sigs); if (rc ) return rc; ++*n_subk; } } } /* 6th: merge subkey certificates */ for (onode=keyblock_orig->next; onode; onode=onode->next) { if (!(onode->flag & NODE_FLAG_A) && (onode->pkt->pkttype == PKT_PUBLIC_SUBKEY || onode->pkt->pkttype == PKT_SECRET_SUBKEY)) { /* find the subkey in the imported keyblock */ for(node=keyblock->next; node; node=node->next) { if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) && !cmp_public_keys( onode->pkt->pkt.public_key, node->pkt->pkt.public_key ) ) break; } if (node) /* Found: merge. */ { rc = merge_keysigs( onode, node, n_sigs); if (rc ) return rc; } } } return 0; } /* Helper function for merge_blocks. * * Append the new userid starting with NODE and all signatures to * KEYBLOCK. ORIGIN and URL conveys the usual key origin info. The * integer at N_SIGS is updated with the number of new signatures. */ static gpg_error_t append_new_uid (unsigned int options, kbnode_t keyblock, kbnode_t node, u32 curtime, int origin, const char *url, int *n_sigs) { gpg_error_t err; kbnode_t n; kbnode_t n_where = NULL; log_assert (node->pkt->pkttype == PKT_USER_ID); /* Find the right position for the new user id and its signatures. */ for (n = keyblock; n; n_where = n, n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_SECRET_SUBKEY ) break; } if (!n) n_where = NULL; /* and append/insert */ while (node) { /* we add a clone to the original keyblock, because this * one is released first. */ n = clone_kbnode(node); if (n->pkt->pkttype == PKT_USER_ID && !(options & IMPORT_RESTORE) ) { err = insert_key_origin_uid (n->pkt->pkt.user_id, curtime, origin, url); if (err) return err; } if (n_where) { insert_kbnode( n_where, n, 0 ); n_where = n; } else add_kbnode( keyblock, n ); n->flag |= NODE_FLAG_A; node->flag |= NODE_FLAG_A; if (n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if (node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } /* Helper function for merge_blocks * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_USER_ID. * (how should we handle comment packets here?) */ static int merge_sigs (kbnode_t dst, kbnode_t src, int *n_sigs) { kbnode_t n, n2; int found = 0; log_assert (dst->pkt->pkttype == PKT_USER_ID); log_assert (src->pkt->pkttype == PKT_USER_ID); for (n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next) { if (n->pkt->pkttype != PKT_SIGNATURE ) continue; if (IS_SUBKEY_SIG (n->pkt->pkt.signature) || IS_SUBKEY_REV (n->pkt->pkt.signature) ) continue; /* skip signatures which are only valid on subkeys */ found = 0; for (n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next) if (!cmp_signatures(n->pkt->pkt.signature,n2->pkt->pkt.signature)) { found++; break; } if (!found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= NODE_FLAG_A; n->flag |= NODE_FLAG_A; ++*n_sigs; } } return 0; } /* Helper function for merge_blocks * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY. */ static int merge_keysigs (kbnode_t dst, kbnode_t src, int *n_sigs) { kbnode_t n, n2; int found = 0; log_assert (dst->pkt->pkttype == PKT_PUBLIC_SUBKEY || dst->pkt->pkttype == PKT_SECRET_SUBKEY); for (n=src->next; n ; n = n->next) { if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY || n->pkt->pkttype == PKT_PUBLIC_KEY ) break; if (n->pkt->pkttype != PKT_SIGNATURE ) continue; found = 0; for (n2=dst->next; n2; n2 = n2->next) { if (n2->pkt->pkttype == PKT_PUBLIC_SUBKEY || n2->pkt->pkttype == PKT_PUBLIC_KEY ) break; if (n2->pkt->pkttype == PKT_SIGNATURE && (n->pkt->pkt.signature->keyid[0] == n2->pkt->pkt.signature->keyid[0]) && (n->pkt->pkt.signature->keyid[1] == n2->pkt->pkt.signature->keyid[1]) && (n->pkt->pkt.signature->timestamp <= n2->pkt->pkt.signature->timestamp) && (n->pkt->pkt.signature->sig_class == n2->pkt->pkt.signature->sig_class)) { found++; break; } } if (!found ) { /* This signature is new or newer, append N to DST. * We add a clone to the original keyblock, because this * one is released first */ n2 = clone_kbnode(n); insert_kbnode( dst, n2, PKT_SIGNATURE ); n2->flag |= NODE_FLAG_A; n->flag |= NODE_FLAG_A; ++*n_sigs; } } return 0; } /* Helper function for merge_blocks. * Append the subkey starting with NODE and all signatures to KEYBLOCK. * Mark all new and copied packets by setting flag bit 0. */ static int append_key (kbnode_t keyblock, kbnode_t node, int *n_sigs) { kbnode_t n; log_assert (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY); while (node) { /* we add a clone to the original keyblock, because this * one is released first */ n = clone_kbnode(node); add_kbnode( keyblock, n ); n->flag |= NODE_FLAG_A; node->flag |= NODE_FLAG_A; if (n->pkt->pkttype == PKT_SIGNATURE ) ++*n_sigs; node = node->next; if (node && node->pkt->pkttype != PKT_SIGNATURE ) break; } return 0; } diff --git a/g10/keydb-private.h b/g10/keydb-private.h new file mode 100644 index 000000000..c5315b991 --- /dev/null +++ b/g10/keydb-private.h @@ -0,0 +1,176 @@ +/* keydb-private.h - Common definitions for keydb.c and call-keyboxd.c + * Copyright (C) 2019 g10 Code GmbH + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see . + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef G10_KEYDB_PRIVATE_H +#define G10_KEYDB_PRIVATE_H + +#include +#include "../common/membuf.h" + + +/* Ugly forward declarations. */ +struct keyring_handle; +typedef struct keyring_handle *KEYRING_HANDLE; +struct keybox_handle; +typedef struct keybox_handle *KEYBOX_HANDLE; + + +/* This is for keydb.c and only used in non-keyboxd mode. */ +#define MAX_KEYDB_RESOURCES 40 + +/* This is for keydb.c and only used in non-keyboxd mode. */ +typedef enum + { + KEYDB_RESOURCE_TYPE_NONE = 0, + KEYDB_RESOURCE_TYPE_KEYRING, + KEYDB_RESOURCE_TYPE_KEYBOX + } KeydbResourceType; + +/* This is for keydb.c and only used in non-keyboxd mode. */ +struct resource_item +{ + KeydbResourceType type; + union { + KEYRING_HANDLE kr; + KEYBOX_HANDLE kb; + } u; + void *token; +}; + + +/* This is a simple cache used to return the last result of a + * successful fingerprint search. This works only for keybox + * resources because (due to lack of a copy_keyblock function) we need + * to store an image of the keyblock which is fortunately instantly + * available for keyboxes. Only used in non-keyboxd mode. */ +enum keyblock_cache_states { + KEYBLOCK_CACHE_EMPTY, + KEYBLOCK_CACHE_PREPARED, + KEYBLOCK_CACHE_FILLED +}; + +struct keyblock_cache { + enum keyblock_cache_states state; + byte fpr[MAX_FINGERPRINT_LEN]; + byte fprlen; + iobuf_t iobuf; /* Image of the keyblock. */ + int pk_no; + int uid_no; + /* Offset of the record in the keybox. */ + int resource; + off_t offset; +}; + + +/* The definition of the KEYDB_HANDLE as used internally by keydb.c and + * the newer call-keyboxd. */ +struct keydb_handle_s +{ + /* Flag set if this handles pertains to call-keyboxd.c. */ + int use_keyboxd; + + /* BEGIN USE_KEYBOXD */ + /* (These fields are only valid if USE_KEYBOXD is set.) */ + + /* A shallow pointer with the CTRL used to create this handle. */ + ctrl_t ctrl; + + /* The context used to communicate with the keyboxd. */ + assuan_context_t assuan_context; + + /* I/O buffer with the last search result or NULL. */ + iobuf_t search_result; + + /* Flag indicating that a search reset is required. */ + unsigned int need_search_reset : 1; + + /* END USE_KEYBOXD */ + + /* BEGIN !USE_KEYBOXD */ + /* (The remaining fields are only valid if USE_KEYBOXD is cleared.) */ + + /* When we locked all of the resources in ACTIVE (using keyring_lock + * / keybox_lock, as appropriate). */ + int locked; + + /* If this flag is set a lock will only be released by + * keydb_release. */ + int keep_lock; + + /* The index into ACTIVE of the resources in which the last search + result was found. Initially -1. */ + int found; + + /* Initially -1 (invalid). This is used to save a search result and + later restore it as the selected result. */ + int saved_found; + + /* The number of skipped long blobs since the last search + (keydb_search_reset). */ + unsigned long skipped_long_blobs; + + /* If set, this disables the use of the keyblock cache. */ + int no_caching; + + /* Whether the next search will be from the beginning of the + database (and thus consider all records). */ + int is_reset; + + /* The "file position." In our case, this is index of the current + resource in ACTIVE. */ + int current; + + /* The number of resources in ACTIVE. */ + int used; + + /* Cache of the last found and parsed key block (only used for + keyboxes, not keyrings). */ + struct keyblock_cache keyblock_cache; + + /* Copy of ALL_RESOURCES when keydb_new is called. */ + struct resource_item active[MAX_KEYDB_RESOURCES]; + + /* END !USE_KEYBOXD */ +}; + + +/*-- keydb.c --*/ + +/* These are the functions call-keyboxd diverts to if the keyboxd is + * not used. */ + +gpg_error_t internal_keydb_init (KEYDB_HANDLE hd); +void internal_keydb_deinit (KEYDB_HANDLE hd); +gpg_error_t internal_keydb_lock (KEYDB_HANDLE hd); + +gpg_error_t internal_keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb); +gpg_error_t internal_keydb_update_keyblock (ctrl_t ctrl, + KEYDB_HANDLE hd, kbnode_t kb); +gpg_error_t internal_keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb); +gpg_error_t internal_keydb_delete_keyblock (KEYDB_HANDLE hd); +gpg_error_t internal_keydb_search_reset (KEYDB_HANDLE hd); +gpg_error_t internal_keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, + size_t ndesc, size_t *descindex); + + + + + +#endif /*G10_KEYDB_PRIVATE_H*/ diff --git a/g10/keydb.c b/g10/keydb.c index d3a2709bc..6ca239475 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -1,2101 +1,1973 @@ /* keydb.c - key database dispatcher * Copyright (C) 2001-2013 Free Software Foundation, Inc. * Copyright (C) 2001-2015 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "options.h" #include "main.h" /*try_make_homedir ()*/ #include "packet.h" #include "keyring.h" #include "../kbx/keybox.h" #include "keydb.h" #include "../common/i18n.h" -static int active_handles; +#include "keydb-private.h" /* For struct keydb_handle_s */ -typedef enum - { - KEYDB_RESOURCE_TYPE_NONE = 0, - KEYDB_RESOURCE_TYPE_KEYRING, - KEYDB_RESOURCE_TYPE_KEYBOX - } KeydbResourceType; -#define MAX_KEYDB_RESOURCES 40 +static int active_handles; -struct resource_item -{ - KeydbResourceType type; - union { - KEYRING_HANDLE kr; - KEYBOX_HANDLE kb; - } u; - void *token; -}; static struct resource_item all_resources[MAX_KEYDB_RESOURCES]; static int used_resources; /* A pointer used to check for the primary key database by comparing to the struct resource_item's TOKEN. */ static void *primary_keydb; /* Whether we have successfully registered any resource. */ static int any_registered; -/* This is a simple cache used to return the last result of a - successful fingerprint search. This works only for keybox resources - because (due to lack of a copy_keyblock function) we need to store - an image of the keyblock which is fortunately instantly available - for keyboxes. */ -enum keyblock_cache_states { - KEYBLOCK_CACHE_EMPTY, - KEYBLOCK_CACHE_PREPARED, - KEYBLOCK_CACHE_FILLED -}; - -struct keyblock_cache { - enum keyblock_cache_states state; - byte fpr[MAX_FINGERPRINT_LEN]; - byte fprlen; - iobuf_t iobuf; /* Image of the keyblock. */ - int pk_no; - int uid_no; - /* Offset of the record in the keybox. */ - int resource; - off_t offset; -}; - - -struct keydb_handle -{ - /* When we locked all of the resources in ACTIVE (using keyring_lock - / keybox_lock, as appropriate). */ - int locked; - - /* If this flag is set a lock will only be released by - * keydb_release. */ - int keep_lock; - - /* The index into ACTIVE of the resources in which the last search - result was found. Initially -1. */ - int found; - - /* Initially -1 (invalid). This is used to save a search result and - later restore it as the selected result. */ - int saved_found; - - /* The number of skipped long blobs since the last search - (keydb_search_reset). */ - unsigned long skipped_long_blobs; - - /* If set, this disables the use of the keyblock cache. */ - int no_caching; - - /* Whether the next search will be from the beginning of the - database (and thus consider all records). */ - int is_reset; - - /* The "file position." In our case, this is index of the current - resource in ACTIVE. */ - int current; - - /* The number of resources in ACTIVE. */ - int used; - - /* Cache of the last found and parsed key block (only used for - keyboxes, not keyrings). */ - struct keyblock_cache keyblock_cache; - - /* Copy of ALL_RESOURCES when keydb_new is called. */ - struct resource_item active[MAX_KEYDB_RESOURCES]; -}; - /* Looking up keys is expensive. To hide the cost, we cache whether keys exist in the key database. Then, if we know a key does not exist, we don't have to spend time looking it up. This particularly helps the --list-sigs and --check-sigs commands. The cache stores the results in a hash using separate chaining. Concretely: we use the LSB of the keyid to index the hash table and each bucket consists of a linked list of entries. An entry consists of the 64-bit key id. If a key id is not in the cache, then we don't know whether it is in the DB or not. To simplify the cache consistency protocol, we simply flush the whole cache whenever a key is inserted or updated. */ #define KID_NOT_FOUND_CACHE_BUCKETS 256 static struct kid_not_found_cache_bucket * kid_not_found_cache[KID_NOT_FOUND_CACHE_BUCKETS]; struct kid_not_found_cache_bucket { struct kid_not_found_cache_bucket *next; u32 kid[2]; }; struct { unsigned int count; /* The current number of entries in the hash table. */ unsigned int peak; /* The peak of COUNT. */ unsigned int flushes; /* The number of flushes. */ } kid_not_found_stats; struct { unsigned int handles; /* Number of handles created. */ unsigned int locks; /* Number of locks taken. */ unsigned int parse_keyblocks; /* Number of parse_keyblock_image calls. */ unsigned int get_keyblocks; /* Number of keydb_get_keyblock calls. */ unsigned int build_keyblocks; /* Number of build_keyblock_image calls. */ unsigned int update_keyblocks;/* Number of update_keyblock calls. */ unsigned int insert_keyblocks;/* Number of update_keyblock calls. */ unsigned int delete_keyblocks;/* Number of delete_keyblock calls. */ unsigned int search_resets; /* Number of keydb_search_reset calls. */ unsigned int found; /* Number of successful keydb_search calls. */ unsigned int found_cached; /* Ditto but from the cache. */ unsigned int notfound; /* Number of failed keydb_search calls. */ unsigned int notfound_cached; /* Ditto but from the cache. */ } keydb_stats; static int lock_all (KEYDB_HANDLE hd); static void unlock_all (KEYDB_HANDLE hd); /* Check whether the keyid KID is in key id is definitely not in the database. Returns: 0 - Indeterminate: the key id is not in the cache; we don't know whether the key is in the database or not. If you want a definitive answer, you'll need to perform a lookup. 1 - There is definitely no key with this key id in the database. We searched for a key with this key id previously, but we didn't find it in the database. */ static int kid_not_found_p (u32 *kid) { struct kid_not_found_cache_bucket *k; for (k = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) { if (DBG_CACHE) log_debug ("keydb: kid_not_found_p (%08lx%08lx) => not in DB\n", (ulong)kid[0], (ulong)kid[1]); return 1; } if (DBG_CACHE) log_debug ("keydb: kid_not_found_p (%08lx%08lx) => indeterminate\n", (ulong)kid[0], (ulong)kid[1]); return 0; } /* Insert the keyid KID into the kid_not_found_cache. FOUND is whether the key is in the key database or not. Note this function does not check whether the key id is already in the cache. As such, kid_not_found_p() should be called first. */ static void kid_not_found_insert (u32 *kid) { struct kid_not_found_cache_bucket *k; if (DBG_CACHE) log_debug ("keydb: kid_not_found_insert (%08lx%08lx)\n", (ulong)kid[0], (ulong)kid[1]); k = xmalloc (sizeof *k); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->next = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS] = k; kid_not_found_stats.count++; } /* Flush the kid not found cache. */ static void kid_not_found_flush (void) { struct kid_not_found_cache_bucket *k, *knext; int i; if (DBG_CACHE) log_debug ("keydb: kid_not_found_flush\n"); if (!kid_not_found_stats.count) return; for (i=0; i < DIM(kid_not_found_cache); i++) { for (k = kid_not_found_cache[i]; k; k = knext) { knext = k->next; xfree (k); } kid_not_found_cache[i] = NULL; } if (kid_not_found_stats.count > kid_not_found_stats.peak) kid_not_found_stats.peak = kid_not_found_stats.count; kid_not_found_stats.count = 0; kid_not_found_stats.flushes++; } static void -keyblock_cache_clear (struct keydb_handle *hd) +keyblock_cache_clear (struct keydb_handle_s *hd) { hd->keyblock_cache.state = KEYBLOCK_CACHE_EMPTY; iobuf_close (hd->keyblock_cache.iobuf); hd->keyblock_cache.iobuf = NULL; hd->keyblock_cache.resource = -1; hd->keyblock_cache.offset = -1; } /* Handle the creation of a keyring or a keybox if it does not yet exist. Take into account that other processes might have the keyring/keybox already locked. This lock check does not work if the directory itself is not yet available. If IS_BOX is true the filename is expected to refer to a keybox. If FORCE_CREATE is true the keyring or keybox will be created. Return 0 if it is okay to access the specified file. */ static gpg_error_t maybe_create_keyring_or_box (char *filename, int is_box, int force_create) { dotlock_t lockhd = NULL; IOBUF iobuf; int rc; mode_t oldmask; char *last_slash_in_filename; char *bak_fname = NULL; char *tmp_fname = NULL; int save_slash; /* A quick test whether the filename already exists. */ if (!access (filename, F_OK)) return !access (filename, R_OK)? 0 : gpg_error (GPG_ERR_EACCES); /* If we don't want to create a new file at all, there is no need to go any further - bail out right here. */ if (!force_create) return gpg_error (GPG_ERR_ENOENT); /* First of all we try to create the home directory. Note, that we don't do any locking here because any sane application of gpg would create the home directory by itself and not rely on gpg's tricky auto-creation which is anyway only done for certain home directory name pattern. */ last_slash_in_filename = strrchr (filename, DIRSEP_C); #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *p = strrchr (filename, '/'); if (!last_slash_in_filename || p > last_slash_in_filename) last_slash_in_filename = p; } #endif /*HAVE_W32_SYSTEM*/ if (!last_slash_in_filename) return gpg_error (GPG_ERR_ENOENT); /* No slash at all - should not happen though. */ save_slash = *last_slash_in_filename; *last_slash_in_filename = 0; if (access(filename, F_OK)) { static int tried; if (!tried) { tried = 1; try_make_homedir (filename); } if (access (filename, F_OK)) { rc = gpg_error_from_syserror (); *last_slash_in_filename = save_slash; goto leave; } } *last_slash_in_filename = save_slash; /* To avoid races with other instances of gpg trying to create or update the keyring (it is removed during an update for a short time), we do the next stuff in a locked state. */ lockhd = dotlock_create (filename, 0); if (!lockhd) { rc = gpg_error_from_syserror (); /* A reason for this to fail is that the directory is not writable. However, this whole locking stuff does not make sense if this is the case. An empty non-writable directory with no keyring is not really useful at all. */ if (opt.verbose) log_info ("can't allocate lock for '%s': %s\n", filename, gpg_strerror (rc)); if (!force_create) return gpg_error (GPG_ERR_ENOENT); /* Won't happen. */ else return rc; } if ( dotlock_take (lockhd, -1) ) { rc = gpg_error_from_syserror (); /* This is something bad. Probably a stale lockfile. */ log_info ("can't lock '%s': %s\n", filename, gpg_strerror (rc)); goto leave; } /* Now the real test while we are locked. */ /* Gpg either uses pubring.gpg or pubring.kbx and thus different * lock files. Now, when one gpg process is updating a pubring.gpg * and thus holding the corresponding lock, a second gpg process may * get to here at the time between the two rename operation used by * the first process to update pubring.gpg. The lock taken above * may not protect the second process if it tries to create a * pubring.kbx file which would be protected by a different lock * file. * * We can detect this case by checking that the two temporary files * used by the update code exist at the same time. In that case we * do not create a new file but act as if FORCE_CREATE has not been * given. Obviously there is a race between our two checks but the * worst thing is that we won't create a new file, which is better * than to accidentally creating one. */ rc = keybox_tmp_names (filename, is_box, &bak_fname, &tmp_fname); if (rc) goto leave; if (!access (filename, F_OK)) { rc = 0; /* Okay, we may access the file now. */ goto leave; } if (!access (bak_fname, F_OK) && !access (tmp_fname, F_OK)) { /* Very likely another process is updating a pubring.gpg and we should not create a pubring.kbx. */ rc = gpg_error (GPG_ERR_ENOENT); goto leave; } /* The file does not yet exist, create it now. */ oldmask = umask (077); if (is_secured_filename (filename)) { iobuf = NULL; gpg_err_set_errno (EPERM); } else iobuf = iobuf_create (filename, 0); umask (oldmask); if (!iobuf) { rc = gpg_error_from_syserror (); if (is_box) log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); else log_error (_("error creating keyring '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } iobuf_close (iobuf); /* Must invalidate that ugly cache */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, filename); /* Make sure that at least one record is in a new keybox file, so that the detection magic will work the next time it is used. */ if (is_box) { FILE *fp = fopen (filename, "wb"); if (!fp) rc = gpg_error_from_syserror (); else { rc = _keybox_write_header_blob (fp, NULL, 1); fclose (fp); } if (rc) { if (is_box) log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); else log_error (_("error creating keyring '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } } if (!opt.quiet) { if (is_box) log_info (_("keybox '%s' created\n"), filename); else log_info (_("keyring '%s' created\n"), filename); } rc = 0; leave: if (lockhd) { dotlock_release (lockhd); dotlock_destroy (lockhd); } xfree (bak_fname); xfree (tmp_fname); return rc; } /* Helper for keydb_add_resource. Opens FILENAME to figure out the resource type. Returns the specified file's likely type. If the file does not exist, returns KEYDB_RESOURCE_TYPE_NONE and sets *R_FOUND to 0. Otherwise, tries to figure out the file's type. This is either KEYDB_RESOURCE_TYPE_KEYBOX, KEYDB_RESOURCE_TYPE_KEYRING or KEYDB_RESOURCE_TYPE_KEYNONE. If the file is a keybox and it has the OpenPGP flag set, then R_OPENPGP is also set. */ static KeydbResourceType rt_from_file (const char *filename, int *r_found, int *r_openpgp) { u32 magic; unsigned char verbuf[4]; FILE *fp; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; *r_found = *r_openpgp = 0; fp = fopen (filename, "rb"); if (fp) { *r_found = 1; if (fread (&magic, 4, 1, fp) == 1 ) { if (magic == 0x13579ace || magic == 0xce9a5713) ; /* GDBM magic - not anymore supported. */ else if (fread (&verbuf, 4, 1, fp) == 1 && verbuf[0] == 1 && fread (&magic, 4, 1, fp) == 1 && !memcmp (&magic, "KBXf", 4)) { if ((verbuf[3] & 0x02)) *r_openpgp = 1; rt = KEYDB_RESOURCE_TYPE_KEYBOX; } else rt = KEYDB_RESOURCE_TYPE_KEYRING; } else /* Maybe empty: assume keyring. */ rt = KEYDB_RESOURCE_TYPE_KEYRING; fclose (fp); } return rt; } char * keydb_search_desc_dump (struct keydb_search_desc *desc) { char b[MAX_FORMATTED_FINGERPRINT_LEN + 1]; char fpr[2 * MAX_FINGERPRINT_LEN + 1]; switch (desc->mode) { case KEYDB_SEARCH_MODE_EXACT: return xasprintf ("EXACT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SUBSTR: return xasprintf ("SUBSTR: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAIL: return xasprintf ("MAIL: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILSUB: return xasprintf ("MAILSUB: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILEND: return xasprintf ("MAILEND: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_WORDS: return xasprintf ("WORDS: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SHORT_KID: return xasprintf ("SHORT_KID: '%s'", format_keyid (desc->u.kid, KF_SHORT, b, sizeof (b))); case KEYDB_SEARCH_MODE_LONG_KID: return xasprintf ("LONG_KID: '%s'", format_keyid (desc->u.kid, KF_LONG, b, sizeof (b))); case KEYDB_SEARCH_MODE_FPR: bin2hex (desc->u.fpr, desc->fprlen, fpr); return xasprintf ("FPR%02d: '%s'", desc->fprlen, format_hexfingerprint (fpr, b, sizeof (b))); case KEYDB_SEARCH_MODE_ISSUER: return xasprintf ("ISSUER: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_ISSUER_SN: return xasprintf ("ISSUER_SN: '%*s'", (int) (desc->snlen == -1 ? strlen (desc->sn) : desc->snlen), desc->sn); case KEYDB_SEARCH_MODE_SN: return xasprintf ("SN: '%*s'", (int) (desc->snlen == -1 ? strlen (desc->sn) : desc->snlen), desc->sn); case KEYDB_SEARCH_MODE_SUBJECT: return xasprintf ("SUBJECT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_KEYGRIP: return xasprintf ("KEYGRIP: %s", desc->u.grip); case KEYDB_SEARCH_MODE_FIRST: return xasprintf ("FIRST"); case KEYDB_SEARCH_MODE_NEXT: return xasprintf ("NEXT"); default: return xasprintf ("Bad search mode (%d)", desc->mode); } } /* Register a resource (keyring or keybox). The first keyring or * keybox that is added using this function is created if it does not * already exist and the KEYDB_RESOURCE_FLAG_READONLY is not set. * * FLAGS are a combination of the KEYDB_RESOURCE_FLAG_* constants. * * URL must have the following form: * * gnupg-ring:filename = plain keyring * gnupg-kbx:filename = keybox file * filename = check file's type (create as a plain keyring) * * Note: on systems with drive letters (Windows) invalid URLs (i.e., * those with an unrecognized part before the ':' such as "c:\...") * will silently be treated as bare filenames. On other systems, such * URLs will cause this function to return GPG_ERR_GENERAL. * * If KEYDB_RESOURCE_FLAG_DEFAULT is set, the resource is a keyring * and the file ends in ".gpg", then this function also checks if a * file with the same name, but the extension ".kbx" exists, is a * keybox and the OpenPGP flag is set. If so, this function opens * that resource instead. * * If the file is not found, KEYDB_RESOURCE_FLAG_GPGVDEF is set and * the URL ends in ".kbx", then this function will try opening the * same URL, but with the extension ".gpg". If that file is a keybox * with the OpenPGP flag set or it is a keyring, then we use that * instead. * * If the file is not found, KEYDB_RESOURCE_FLAG_DEFAULT is set, the * file should be created and the file's extension is ".gpg" then we * replace the extension with ".kbx". * * If the KEYDB_RESOURCE_FLAG_PRIMARY is set and the resource is a * keyring (not a keybox), then this resource is considered the * primary resource. This is used by keydb_locate_writable(). If * another primary keyring is set, then that keyring is considered the * primary. * * If KEYDB_RESOURCE_FLAG_READONLY is set and the resource is a * keyring (not a keybox), then the keyring is marked as read only and * operations just as keyring_insert_keyblock will return * GPG_ERR_ACCESS. */ gpg_error_t keydb_add_resource (const char *url, unsigned int flags) { /* The file named by the URL (i.e., without the prototype). */ const char *resname = url; char *filename = NULL; int create; int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY); int is_default = !!(flags&KEYDB_RESOURCE_FLAG_DEFAULT); int is_gpgvdef = !!(flags&KEYDB_RESOURCE_FLAG_GPGVDEF); gpg_error_t err = 0; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; void *token; /* Create the resource if it is the first registered one. */ create = (!read_only && !any_registered); if (strlen (resname) > 11 && !strncmp( resname, "gnupg-ring:", 11) ) { rt = KEYDB_RESOURCE_TYPE_KEYRING; resname += 11; } else if (strlen (resname) > 10 && !strncmp (resname, "gnupg-kbx:", 10) ) { rt = KEYDB_RESOURCE_TYPE_KEYBOX; resname += 10; } #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) else if (strchr (resname, ':')) { log_error ("invalid key resource URL '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ if (*resname != DIRSEP_C #ifdef HAVE_W32_SYSTEM && *resname != '/' /* Fixme: does not handle drive letters. */ #endif ) { /* Do tilde expansion etc. */ if (strchr (resname, DIRSEP_C) #ifdef HAVE_W32_SYSTEM || strchr (resname, '/') /* Windows also accepts this. */ #endif ) filename = make_filename (resname, NULL); else filename = make_filename (gnupg_homedir (), resname, NULL); } else filename = xstrdup (resname); /* See whether we can determine the filetype. */ if (rt == KEYDB_RESOURCE_TYPE_NONE) { int found, openpgp_flag; int pass = 0; size_t filenamelen; check_again: filenamelen = strlen (filename); rt = rt_from_file (filename, &found, &openpgp_flag); if (found) { /* The file exists and we have the resource type in RT. Now let us check whether in addition to the "pubring.gpg" a "pubring.kbx with openpgp keys exists. This is so that GPG 2.1 will use an existing "pubring.kbx" by default iff that file has been created or used by 2.1. This check is needed because after creation or use of the kbx file with 2.1 an older version of gpg may have created a new pubring.gpg for its own use. */ if (!pass && is_default && rt == KEYDB_RESOURCE_TYPE_KEYRING && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg")) { strcpy (filename+filenamelen-4, ".kbx"); if ((rt_from_file (filename, &found, &openpgp_flag) == KEYDB_RESOURCE_TYPE_KEYBOX) && found && openpgp_flag) rt = KEYDB_RESOURCE_TYPE_KEYBOX; else /* Restore filename */ strcpy (filename+filenamelen-4, ".gpg"); } } else if (!pass && is_gpgvdef && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".kbx")) { /* Not found but gpgv's default "trustedkeys.kbx" file has been requested. We did not found it so now check whether a "trustedkeys.gpg" file exists and use that instead. */ KeydbResourceType rttmp; strcpy (filename+filenamelen-4, ".gpg"); rttmp = rt_from_file (filename, &found, &openpgp_flag); if (found && ((rttmp == KEYDB_RESOURCE_TYPE_KEYBOX && openpgp_flag) || (rttmp == KEYDB_RESOURCE_TYPE_KEYRING))) rt = rttmp; else /* Restore filename */ strcpy (filename+filenamelen-4, ".kbx"); } else if (!pass && is_default && create && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg")) { /* The file does not exist, the default resource has been requested, the file shall be created, and the file has a ".gpg" suffix. Change the suffix to ".kbx" and try once more. This way we achieve that we open an existing ".gpg" keyring, but create a new keybox file with an ".kbx" suffix. */ strcpy (filename+filenamelen-4, ".kbx"); pass++; goto check_again; } else /* No file yet: create keybox. */ rt = KEYDB_RESOURCE_TYPE_KEYBOX; } switch (rt) { case KEYDB_RESOURCE_TYPE_NONE: log_error ("unknown type of key resource '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; case KEYDB_RESOURCE_TYPE_KEYRING: err = maybe_create_keyring_or_box (filename, 0, create); if (err) goto leave; if (keyring_register_filename (filename, read_only, &token)) { if (used_resources >= MAX_KEYDB_RESOURCES) err = gpg_error (GPG_ERR_RESOURCE_LIMIT); else { if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; all_resources[used_resources].type = rt; all_resources[used_resources].u.kr = NULL; /* Not used here */ all_resources[used_resources].token = token; used_resources++; } } else { /* This keyring was already registered, so ignore it. However, we can still mark it as primary even if it was already registered. */ if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; } break; case KEYDB_RESOURCE_TYPE_KEYBOX: { err = maybe_create_keyring_or_box (filename, 1, create); if (err) goto leave; err = keybox_register_file (filename, 0, &token); if (!err) { if (used_resources >= MAX_KEYDB_RESOURCES) err = gpg_error (GPG_ERR_RESOURCE_LIMIT); else { if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; all_resources[used_resources].type = rt; all_resources[used_resources].u.kb = NULL; /* Not used here */ all_resources[used_resources].token = token; /* FIXME: Do a compress run if needed and no other user is currently using the keybox. */ used_resources++; } } else if (gpg_err_code (err) == GPG_ERR_EEXIST) { /* Already registered. We will mark it as the primary key if requested. */ if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; } } break; default: log_error ("resource type of '%s' not supported\n", url); err = gpg_error (GPG_ERR_GENERAL); goto leave; } /* fixme: check directory permissions and print a warning */ leave: if (err) { log_error (_("keyblock resource '%s': %s\n"), filename, gpg_strerror (err)); write_status_error ("add_keyblock_resource", err); } else any_registered = 1; xfree (filename); return err; } void keydb_dump_stats (void) { log_info ("keydb: handles=%u locks=%u parse=%u get=%u\n", keydb_stats.handles, keydb_stats.locks, keydb_stats.parse_keyblocks, keydb_stats.get_keyblocks); log_info (" build=%u update=%u insert=%u delete=%u\n", keydb_stats.build_keyblocks, keydb_stats.update_keyblocks, keydb_stats.insert_keyblocks, keydb_stats.delete_keyblocks); log_info (" reset=%u found=%u not=%u cache=%u not=%u\n", keydb_stats.search_resets, keydb_stats.found, keydb_stats.notfound, keydb_stats.found_cached, keydb_stats.notfound_cached); log_info ("kid_not_found_cache: count=%u peak=%u flushes=%u\n", kid_not_found_stats.count, kid_not_found_stats.peak, kid_not_found_stats.flushes); } -/* Create a new database handle. A database handle is similar to a - file handle: it contains a local file position. This is used when - searching: subsequent searches resume where the previous search - left off. To rewind the position, use keydb_search_reset(). This - function returns NULL on error, sets ERRNO, and prints an error - diagnostic. */ -KEYDB_HANDLE -keydb_new (void) +/* keydb_new diverts to here in non-keyboxd mode. HD is just the + * calloced structure with the handle type intialized. */ +gpg_error_t +internal_keydb_init (KEYDB_HANDLE hd) { - KEYDB_HANDLE hd; + gpg_error_t err = 0; int i, j; int die = 0; int reterrno; - if (DBG_CLOCK) - log_clock ("keydb_new"); - - hd = xtrycalloc (1, sizeof *hd); - if (!hd) - goto leave; + log_assert (!hd->use_keyboxd); hd->found = -1; hd->saved_found = -1; hd->is_reset = 1; log_assert (used_resources <= MAX_KEYDB_RESOURCES); for (i=j=0; ! die && i < used_resources; i++) { switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYRING: hd->active[j].type = all_resources[i].type; hd->active[j].token = all_resources[i].token; hd->active[j].u.kr = keyring_new (all_resources[i].token); if (!hd->active[j].u.kr) { reterrno = errno; die = 1; } j++; break; case KEYDB_RESOURCE_TYPE_KEYBOX: hd->active[j].type = all_resources[i].type; hd->active[j].token = all_resources[i].token; hd->active[j].u.kb = keybox_new_openpgp (all_resources[i].token, 0); if (!hd->active[j].u.kb) { reterrno = errno; die = 1; } j++; break; } } hd->used = j; active_handles++; keydb_stats.handles++; if (die) - { - keydb_release (hd); - gpg_err_set_errno (reterrno); - hd = NULL; - } + err = gpg_error_from_errno (reterrno); - leave: - if (!hd) - log_error (_("error opening key DB: %s\n"), - gpg_strerror (gpg_error_from_syserror())); - - return hd; + return err; } +/* Free all non-keyboxd resources owned by the database handle. + * keydb_release diverts to here. */ void -keydb_release (KEYDB_HANDLE hd) +internal_keydb_deinit (KEYDB_HANDLE hd) { int i; - if (!hd) - return; + log_assert (!hd->use_keyboxd); + log_assert (active_handles > 0); active_handles--; hd->keep_lock = 0; unlock_all (hd); for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_release (hd->active[i].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_release (hd->active[i].u.kb); break; } } keyblock_cache_clear (hd); - xfree (hd); } /* Take a lock on the files immediately and not only during insert or * update. This lock is released with keydb_release. */ gpg_error_t -keydb_lock (KEYDB_HANDLE hd) +internal_keydb_lock (KEYDB_HANDLE hd) { gpg_error_t err; - if (!hd) - return gpg_error (GPG_ERR_INV_ARG); + log_assert (!hd->use_keyboxd); err = lock_all (hd); if (!err) hd->keep_lock = 1; return err; } /* Set a flag on the handle to suppress use of cached results. This * is required for updating a keyring and for key listings. Fixme: * Using a new parameter for keydb_new might be a better solution. */ void keydb_disable_caching (KEYDB_HANDLE hd) { - if (hd) + if (hd && !hd->use_keyboxd) hd->no_caching = 1; } /* Return the file name of the resource in which the current search * result was found or, if there is no search result, the filename of * the current resource (i.e., the resource that the file position * points to). Note: the filename is not necessarily the URL used to * open it! * * This function only returns NULL if no handle is specified, in all * other error cases an empty string is returned. */ const char * keydb_get_resource_name (KEYDB_HANDLE hd) { int idx; const char *s = NULL; if (!hd) return NULL; + if (!hd->use_keyboxd) + return "[keyboxd]"; + if ( hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else idx = 0; switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: s = NULL; break; case KEYDB_RESOURCE_TYPE_KEYRING: s = keyring_get_resource_name (hd->active[idx].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: s = keybox_get_resource_name (hd->active[idx].u.kb); break; } return s? s: ""; } static int lock_all (KEYDB_HANDLE hd) { int i, rc = 0; /* Fixme: This locking scheme may lead to a deadlock if the resources are not added in the same order by all processes. We are currently only allowing one resource so it is not a problem. [Oops: Who claimed the latter] To fix this we need to use a lock file to protect lock_all. */ for (i=0; !rc && i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_lock (hd->active[i].u.kr, 1); break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_lock (hd->active[i].u.kb, 1, -1); break; } } if (rc) { /* Revert the already taken locks. */ for (i--; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_lock (hd->active[i].u.kr, 0); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kb, 0, 0); break; } } } else { hd->locked = 1; keydb_stats.locks++; } return rc; } static void unlock_all (KEYDB_HANDLE hd) { int i; if (!hd->locked || hd->keep_lock) return; for (i=hd->used-1; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_lock (hd->active[i].u.kr, 0); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kb, 0, 0); break; } } hd->locked = 0; } /* Save the last found state and invalidate the current selection * (i.e., the entry selected by keydb_search() is invalidated and * something like keydb_get_keyblock() will return an error). This * does not change the file position. This makes it possible to do * something like: * * keydb_search (hd, ...); // Result 1. * keydb_push_found_state (hd); * keydb_search_reset (hd); * keydb_search (hd, ...); // Result 2. * keydb_pop_found_state (hd); * keydb_get_keyblock (hd, ...); // -> Result 1. * * Note: it is only possible to save a single save state at a time. * In other words, the save stack only has room for a single * instance of the state. */ +/* FIXME(keyboxd): This function is used only at one place - see how + * we can avoid it. */ void keydb_push_found_state (KEYDB_HANDLE hd) { if (!hd) return; if (hd->found < 0 || hd->found >= hd->used) { hd->saved_found = -1; return; } switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_push_found_state (hd->active[hd->found].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_push_found_state (hd->active[hd->found].u.kb); break; } hd->saved_found = hd->found; hd->found = -1; } /* Restore the previous save state. If the saved state is NULL or invalid, this is a NOP. */ +/* FIXME(keyboxd): This function is used only at one place - see how + * we can avoid it. */ void keydb_pop_found_state (KEYDB_HANDLE hd) { if (!hd) return; hd->found = hd->saved_found; hd->saved_found = -1; if (hd->found < 0 || hd->found >= hd->used) return; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_pop_found_state (hd->active[hd->found].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_pop_found_state (hd->active[hd->found].u.kb); break; } } static gpg_error_t parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, kbnode_t *r_keyblock) { gpg_error_t err; struct parse_packet_ctx_s parsectx; PACKET *pkt; kbnode_t keyblock = NULL; kbnode_t node, *tail; int in_cert, save_mode; int pk_count, uid_count; *r_keyblock = NULL; pkt = xtrymalloc (sizeof *pkt); if (!pkt) return gpg_error_from_syserror (); init_packet (pkt); init_parse_packet (&parsectx, iobuf); save_mode = set_packet_list_mode (0); in_cert = 0; tail = NULL; pk_count = uid_count = 0; while ((err = parse_packet (&parsectx, pkt)) != -1) { if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET) { free_packet (pkt, &parsectx); init_packet (pkt); continue; } if (err) { es_fflush (es_stdout); log_error ("parse_keyblock_image: read error: %s\n", gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_INV_PACKET) { free_packet (pkt, &parsectx); init_packet (pkt); continue; } err = gpg_error (GPG_ERR_INV_KEYRING); break; } /* Filter allowed packets. */ switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_SIGNATURE: case PKT_RING_TRUST: break; /* Allowed per RFC. */ default: log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype); free_packet(pkt, &parsectx); init_packet(pkt); continue; } /* Other sanity checks. */ if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY) { log_error ("parse_keyblock_image: first packet in a keybox blob " "is not a public key packet\n"); err = gpg_error (GPG_ERR_INV_KEYRING); break; } if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { log_error ("parse_keyblock_image: " "multiple keyblocks in a keybox blob\n"); err = gpg_error (GPG_ERR_INV_KEYRING); break; } in_cert = 1; node = new_kbnode (pkt); switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: if (++pk_count == pk_no) node->flag |= 1; break; case PKT_USER_ID: if (++uid_count == uid_no) node->flag |= 2; break; default: break; } if (!keyblock) keyblock = node; else *tail = node; tail = &node->next; pkt = xtrymalloc (sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); break; } init_packet (pkt); } set_packet_list_mode (save_mode); if (err == -1 && keyblock) err = 0; /* Got the entire keyblock. */ if (err) release_kbnode (keyblock); else { *r_keyblock = keyblock; keydb_stats.parse_keyblocks++; } free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree (pkt); return err; } /* Return the keyblock last found by keydb_search() in *RET_KB. + * keydb_get_keyblock divert to here in the non-keyboxd mode. * * On success, the function returns 0 and the caller must free *RET_KB * using release_kbnode(). Otherwise, the function returns an error * code. * * The returned keyblock has the kbnode flag bit 0 set for the node * with the public key used to locate the keyblock or flag bit 1 set * for the user ID node. */ gpg_error_t -keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb) +internal_keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb) { gpg_error_t err = 0; - *ret_kb = NULL; - - if (!hd) - return gpg_error (GPG_ERR_INV_ARG); - - if (DBG_CLOCK) - log_clock ("keydb_get_keybock enter"); + log_assert (!hd->use_keyboxd); if (hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED) { err = iobuf_seek (hd->keyblock_cache.iobuf, 0); if (err) { log_error ("keydb_get_keyblock: failed to rewind iobuf for cache\n"); keyblock_cache_clear (hd); } else { err = parse_keyblock_image (hd->keyblock_cache.iobuf, hd->keyblock_cache.pk_no, hd->keyblock_cache.uid_no, ret_kb); if (err) keyblock_cache_clear (hd); if (DBG_CLOCK) - log_clock (err? "keydb_get_keyblock leave (cached, failed)" - : "keydb_get_keyblock leave (cached)"); + log_clock ("%s leave (cached mode)", __func__); return err; } } if (hd->found < 0 || hd->found >= hd->used) return gpg_error (GPG_ERR_VALUE_NOT_FOUND); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: err = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb); break; case KEYDB_RESOURCE_TYPE_KEYBOX: { iobuf_t iobuf; int pk_no, uid_no; err = keybox_get_keyblock (hd->active[hd->found].u.kb, &iobuf, &pk_no, &uid_no); if (!err) { err = parse_keyblock_image (iobuf, pk_no, uid_no, ret_kb); if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED) { hd->keyblock_cache.state = KEYBLOCK_CACHE_FILLED; hd->keyblock_cache.iobuf = iobuf; hd->keyblock_cache.pk_no = pk_no; hd->keyblock_cache.uid_no = uid_no; } else { iobuf_close (iobuf); } } } break; } if (hd->keyblock_cache.state != KEYBLOCK_CACHE_FILLED) keyblock_cache_clear (hd); if (!err) keydb_stats.get_keyblocks++; - if (DBG_CLOCK) - log_clock (err? "keydb_get_keyblock leave (failed)" - : "keydb_get_keyblock leave"); return err; } /* Build a keyblock image from KEYBLOCK. Returns 0 on success and * only then stores a new iobuf object at R_IOBUF. */ static gpg_error_t build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf) { gpg_error_t err; iobuf_t iobuf; kbnode_t kbctx, node; *r_iobuf = NULL; iobuf = iobuf_temp (); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { /* Make sure to use only packets valid on a keyblock. */ switch (node->pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SIGNATURE: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_RING_TRUST: break; default: continue; } err = build_packet_and_meta (iobuf, node->pkt); if (err) { iobuf_close (iobuf); return err; } } keydb_stats.build_keyblocks++; *r_iobuf = iobuf; return 0; } /* Update the keyblock KB (i.e., extract the fingerprint and find the * corresponding keyblock in the keyring). + * keydb_update_keyblock diverts to here in the non-keyboxd mode. * * This doesn't do anything if --dry-run was specified. * * Returns 0 on success. Otherwise, it returns an error code. Note: * if there isn't a keyblock in the keyring corresponding to KB, then * this function returns GPG_ERR_VALUE_NOT_FOUND. * * This function selects the matching record and modifies the current * file position to point to the record just after the selected entry. * Thus, if you do a subsequent search using HD, you should first do a * keydb_search_reset. Further, if the selected record is important, * you should use keydb_push_found_state and keydb_pop_found_state to * save and restore it. */ gpg_error_t -keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) +internal_keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) { gpg_error_t err; PKT_public_key *pk; KEYDB_SEARCH_DESC desc; size_t len; - log_assert (kb); - log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); + log_assert (!hd->use_keyboxd); pk = kb->pkt->pkt.public_key; - if (!hd) - return gpg_error (GPG_ERR_INV_ARG); - kid_not_found_flush (); keyblock_cache_clear (hd); if (opt.dry_run) return 0; err = lock_all (hd); if (err) return err; #ifdef USE_TOFU tofu_notice_key_changed (ctrl, kb); #endif memset (&desc, 0, sizeof (desc)); fingerprint_from_pk (pk, desc.u.fpr, &len); if (len == 20 || len == 32) { desc.mode = KEYDB_SEARCH_MODE_FPR; desc.fprlen = len; } else log_bug ("%s: Unsupported key length: %zu\n", __func__, len); keydb_search_reset (hd); err = keydb_search (hd, &desc, 1, NULL); if (err) return gpg_error (GPG_ERR_VALUE_NOT_FOUND); log_assert (hd->found >= 0 && hd->found < hd->used); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: err = keyring_update_keyblock (hd->active[hd->found].u.kr, kb); break; case KEYDB_RESOURCE_TYPE_KEYBOX: { iobuf_t iobuf; err = build_keyblock_image (kb, &iobuf); if (!err) { err = keybox_update_keyblock (hd->active[hd->found].u.kb, iobuf_get_temp_buffer (iobuf), iobuf_get_temp_length (iobuf)); iobuf_close (iobuf); } } break; } unlock_all (hd); if (!err) keydb_stats.update_keyblocks++; return err; } /* Insert a keyblock into one of the underlying keyrings or keyboxes. + * keydb_insert_keyblock diverts to here in the non-keyboxd mode. * * Be default, the keyring / keybox from which the last search result * came is used. If there was no previous search result (or * keydb_search_reset was called), then the keyring / keybox where the * next search would start is used (i.e., the current file position). * * Note: this doesn't do anything if --dry-run was specified. * * Returns 0 on success. Otherwise, it returns an error code. */ gpg_error_t -keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb) +internal_keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb) { gpg_error_t err; int idx; - if (!hd) - return gpg_error (GPG_ERR_INV_ARG); + log_assert (!hd->use_keyboxd); kid_not_found_flush (); keyblock_cache_clear (hd); if (opt.dry_run) return 0; if (hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if (hd->current >= 0 && hd->current < hd->used) idx = hd->current; else return gpg_error (GPG_ERR_GENERAL); err = lock_all (hd); if (err) return err; switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: err = keyring_insert_keyblock (hd->active[idx].u.kr, kb); break; case KEYDB_RESOURCE_TYPE_KEYBOX: { /* We need to turn our kbnode_t list of packets into a proper keyblock first. This is required by the OpenPGP key parser included in the keybox code. Eventually we can change this kludge to have the caller pass the image. */ iobuf_t iobuf; err = build_keyblock_image (kb, &iobuf); if (!err) { err = keybox_insert_keyblock (hd->active[idx].u.kb, iobuf_get_temp_buffer (iobuf), iobuf_get_temp_length (iobuf)); iobuf_close (iobuf); } } break; } unlock_all (hd); if (!err) keydb_stats.insert_keyblocks++; return err; } /* Delete the currently selected keyblock. If you haven't done a * search yet on this database handle (or called keydb_search_reset), * then this will return an error. * * Returns 0 on success or an error code, if an error occurs. */ gpg_error_t -keydb_delete_keyblock (KEYDB_HANDLE hd) +internal_keydb_delete_keyblock (KEYDB_HANDLE hd) { gpg_error_t rc; - if (!hd) - return gpg_error (GPG_ERR_INV_ARG); + log_assert (!hd->use_keyboxd); kid_not_found_flush (); keyblock_cache_clear (hd); if (hd->found < 0 || hd->found >= hd->used) return gpg_error (GPG_ERR_VALUE_NOT_FOUND); if (opt.dry_run) return 0; rc = lock_all (hd); if (rc) return rc; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: rc = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_delete_keyblock (hd->active[hd->found].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_delete (hd->active[hd->found].u.kb); break; } unlock_all (hd); if (!rc) keydb_stats.delete_keyblocks++; return rc; } /* A database may consists of multiple keyrings / key boxes. This * sets the "file position" to the start of the first keyring / key * box that is writable (i.e., doesn't have the read-only flag set). * * This first tries the primary keyring (the last keyring (not * keybox!) added using keydb_add_resource() and with * KEYDB_RESOURCE_FLAG_PRIMARY set). If that is not writable, then it * tries the keyrings / keyboxes in the order in which they were * added. */ gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd) { gpg_error_t rc; if (!hd) return GPG_ERR_INV_ARG; + if (hd->use_keyboxd) + return 0; /* No need for this here. */ + rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) return rc; /* If we have a primary set, try that one first */ if (primary_keydb) { for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) { if(hd->active[hd->current].token == primary_keydb) { if(keyring_is_writable (hd->active[hd->current].token)) return 0; else break; } } rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) return rc; } for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) { switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); break; case KEYDB_RESOURCE_TYPE_KEYRING: if (keyring_is_writable (hd->active[hd->current].token)) return 0; /* found (hd->current is set to it) */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: if (keybox_is_writable (hd->active[hd->current].token)) return 0; /* found (hd->current is set to it) */ break; } } return gpg_error (GPG_ERR_NOT_FOUND); } /* Rebuild the on-disk caches of all key resources. */ void keydb_rebuild_caches (ctrl_t ctrl, int noisy) { int i, rc; + if (opt.use_keyboxd) + return; /* No need for this here. */ + for (i=0; i < used_resources; i++) { if (!keyring_is_writable (all_resources[i].token)) continue; switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_rebuild_cache (ctrl, all_resources[i].token,noisy); if (rc) log_error (_("failed to rebuild keyring cache: %s\n"), gpg_strerror (rc)); break; case KEYDB_RESOURCE_TYPE_KEYBOX: /* N/A. */ break; } } } -/* Return the number of skipped blocks (because they were to large to +/* Return the number of skipped blocks (because they were too large to read from a keybox) since the last search reset. */ unsigned long keydb_get_skipped_counter (KEYDB_HANDLE hd) { - return hd ? hd->skipped_long_blobs : 0; + /*FIXME(keyboxd): Do we need this? */ + return hd && !hd->use_keyboxd? hd->skipped_long_blobs : 0; } /* Clears the current search result and resets the handle's position * so that the next search starts at the beginning of the database * (the start of the first resource). + * keydb_search_reset diverts to here in the non-keyboxd mode. * * Returns 0 on success and an error code if an error occurred. * (Currently, this function always returns 0 if HD is valid.) */ gpg_error_t -keydb_search_reset (KEYDB_HANDLE hd) +internal_keydb_search_reset (KEYDB_HANDLE hd) { gpg_error_t rc = 0; int i; - if (!hd) - return gpg_error (GPG_ERR_INV_ARG); + log_assert (!hd->use_keyboxd); keyblock_cache_clear (hd); - if (DBG_CLOCK) - log_clock ("keydb_search_reset"); - - if (DBG_CACHE) - log_debug ("keydb_search: reset (hd=%p)", hd); - hd->skipped_long_blobs = 0; hd->current = 0; hd->found = -1; /* Now reset all resources. */ for (i=0; !rc && i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_search_reset (hd->active[i].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_search_reset (hd->active[i].u.kb); break; } } hd->is_reset = 1; if (!rc) keydb_stats.search_resets++; return rc; } /* Search the database for keys matching the search description. If * the DB contains any legacy keys, these are silently ignored. + * keydb_search diverts to here in the non-keyboxd mode. * * DESC is an array of search terms with NDESC entries. The search * terms are or'd together. That is, the next entry in the DB that * matches any of the descriptions will be returned. * * Note: this function resumes searching where the last search left * off (i.e., at the current file position). If you want to search * from the start of the database, then you need to first call * keydb_search_reset(). * * If no key matches the search description, returns * GPG_ERR_NOT_FOUND. If there was a match, returns 0. If an error * occurred, returns an error code. * * The returned key is considered to be selected and the raw data can, * for instance, be returned by calling keydb_get_keyblock(). */ gpg_error_t -keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, - size_t ndesc, size_t *descindex) +internal_keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, + size_t ndesc, size_t *descindex) { - int i; gpg_error_t rc; int was_reset = hd->is_reset; /* If an entry is already in the cache, then don't add it again. */ int already_in_cache = 0; int fprlen; - if (descindex) - *descindex = 0; /* Make sure it is always set on return. */ - - if (!hd) - return gpg_error (GPG_ERR_INV_ARG); + log_assert (!hd->use_keyboxd); if (!any_registered) { write_status_error ("keydb_search", gpg_error (GPG_ERR_KEYRING_OPEN)); return gpg_error (GPG_ERR_NOT_FOUND); } - if (DBG_CLOCK) - log_clock ("keydb_search enter"); - - if (DBG_LOOKUP) - { - log_debug ("%s: %zd search descriptions:\n", __func__, ndesc); - for (i = 0; i < ndesc; i ++) - { - char *t = keydb_search_desc_dump (&desc[i]); - log_debug ("%s %d: %s\n", __func__, i, t); - xfree (t); - } - } - - if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID && (already_in_cache = kid_not_found_p (desc[0].u.kid)) == 1 ) { if (DBG_CLOCK) - log_clock ("keydb_search leave (not found, cached)"); + log_clock ("%s leave (not found, cached)", __func__); keydb_stats.notfound_cached++; return gpg_error (GPG_ERR_NOT_FOUND); } /* NB: If one of the exact search modes below is used in a loop to walk over all keys (with the same fingerprint) the caching must have been disabled for the handle. */ if (desc[0].mode == KEYDB_SEARCH_MODE_FPR) fprlen = desc[0].fprlen; else fprlen = 0; if (!hd->no_caching && ndesc == 1 && fprlen && hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED && hd->keyblock_cache.fprlen == fprlen && !memcmp (hd->keyblock_cache.fpr, desc[0].u.fpr, fprlen) /* Make sure the current file position occurs before the cached result to avoid an infinite loop. */ && (hd->current < hd->keyblock_cache.resource || (hd->current == hd->keyblock_cache.resource && (keybox_offset (hd->active[hd->current].u.kb) <= hd->keyblock_cache.offset)))) { /* (DESCINDEX is already set). */ if (DBG_CLOCK) - log_clock ("keydb_search leave (cached)"); + log_clock ("%s leave (cached)", __func__); hd->current = hd->keyblock_cache.resource; /* HD->KEYBLOCK_CACHE.OFFSET is the last byte in the record. Seek just beyond that. */ keybox_seek (hd->active[hd->current].u.kb, hd->keyblock_cache.offset + 1); keydb_stats.found_cached++; return 0; } rc = -1; while ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) && hd->current >= 0 && hd->current < hd->used) { if (DBG_LOOKUP) log_debug ("%s: searching %s (resource %d of %d)\n", __func__, hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYRING ? "keyring" : (hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX ? "keybox" : "unknown type"), hd->current, hd->used); switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); /* we should never see it here */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_search (hd->active[hd->current].u.kr, desc, ndesc, descindex, 1); break; case KEYDB_RESOURCE_TYPE_KEYBOX: do rc = keybox_search (hd->active[hd->current].u.kb, desc, ndesc, KEYBOX_BLOBTYPE_PGP, descindex, &hd->skipped_long_blobs); while (rc == GPG_ERR_LEGACY_KEY); break; } if (DBG_LOOKUP) log_debug ("%s: searched %s (resource %d of %d) => %s\n", __func__, hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYRING ? "keyring" : (hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX ? "keybox" : "unknown type"), hd->current, hd->used, rc == -1 ? "EOF" : gpg_strerror (rc)); if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) { /* EOF -> switch to next resource */ hd->current++; } else if (!rc) hd->found = hd->current; } hd->is_reset = 0; rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) ? gpg_error (GPG_ERR_NOT_FOUND) : rc); keyblock_cache_clear (hd); if (!hd->no_caching && !rc && ndesc == 1 && fprlen && hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX) { hd->keyblock_cache.state = KEYBLOCK_CACHE_PREPARED; hd->keyblock_cache.resource = hd->current; /* The current offset is at the start of the next record. Since a record is at least 1 byte, we just use offset - 1, which is within the record. */ hd->keyblock_cache.offset = keybox_offset (hd->active[hd->current].u.kb) - 1; memcpy (hd->keyblock_cache.fpr, desc[0].u.fpr, fprlen); hd->keyblock_cache.fprlen = fprlen; } if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID && was_reset && !already_in_cache) kid_not_found_insert (desc[0].u.kid); - if (DBG_CLOCK) - log_clock (rc? "keydb_search leave (not found)" - : "keydb_search leave (found)"); if (!rc) keydb_stats.found++; else keydb_stats.notfound++; return rc; } /* Return the first non-legacy key in the database. * * If you want the very first key in the database, you can directly * call keydb_search with the search description * KEYDB_SEARCH_MODE_FIRST. */ gpg_error_t keydb_search_first (KEYDB_HANDLE hd) { gpg_error_t err; KEYDB_SEARCH_DESC desc; err = keydb_search_reset (hd); if (err) return err; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; return keydb_search (hd, &desc, 1, NULL); } /* Return the next key (not the next matching key!). * * Unlike calling keydb_search with KEYDB_SEARCH_MODE_NEXT, this * function silently skips legacy keys. */ gpg_error_t keydb_search_next (KEYDB_HANDLE hd) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_NEXT; return keydb_search (hd, &desc, 1, NULL); } /* This is a convenience function for searching for keys with a long * key id. * * Note: this function resumes searching where the last search left * off. If you want to search the whole database, then you need to * first call keydb_search_reset(). */ gpg_error_t keydb_search_kid (KEYDB_HANDLE hd, u32 *kid) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0] = kid[0]; desc.u.kid[1] = kid[1]; return keydb_search (hd, &desc, 1, NULL); } /* This is a convenience function for searching for keys with a long * (20 byte) fingerprint. * * Note: this function resumes searching where the last search left * off. If you want to search the whole database, then you need to * first call keydb_search_reset(). */ gpg_error_t keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr, size_t fprlen) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FPR; memcpy (desc.u.fpr, fpr, fprlen); desc.fprlen = fprlen; return keydb_search (hd, &desc, 1, NULL); } diff --git a/g10/keydb.h b/g10/keydb.h index 6ad8dce4c..25304ed25 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -1,556 +1,559 @@ /* keydb.h - Key database * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, * 2006, 2010 Free Software Foundation, Inc. * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef G10_KEYDB_H #define G10_KEYDB_H #include "../common/types.h" #include "../common/util.h" #include "packet.h" /* What qualifies as a certification (key-signature in contrast to a * data signature)? Note that a back signature is special and can be * made by key and data signatures capable subkeys.) */ #define IS_CERT(s) (IS_KEY_SIG(s) || IS_UID_SIG(s) || IS_SUBKEY_SIG(s) \ || IS_KEY_REV(s) || IS_UID_REV(s) || IS_SUBKEY_REV(s)) #define IS_SIG(s) (!IS_CERT(s)) #define IS_KEY_SIG(s) ((s)->sig_class == 0x1f) #define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10) #define IS_SUBKEY_SIG(s) ((s)->sig_class == 0x18) #define IS_BACK_SIG(s) ((s)->sig_class == 0x19) #define IS_KEY_REV(s) ((s)->sig_class == 0x20) #define IS_UID_REV(s) ((s)->sig_class == 0x30) #define IS_SUBKEY_REV(s) ((s)->sig_class == 0x28) struct getkey_ctx_s; typedef struct getkey_ctx_s *GETKEY_CTX; typedef struct getkey_ctx_s *getkey_ctx_t; /**************** * A Keyblock is all packets which form an entire certificate; * i.e. the public key, certificate, trust packets, user ids, * signatures, and subkey. * * This structure is also used to bind arbitrary packets together. */ struct kbnode_struct { kbnode_t next; PACKET *pkt; int flag; /* Local use during keyblock processing (not cloned).*/ unsigned int tag; /* Ditto. */ int private_flag; }; #define is_deleted_kbnode(a) ((a)->private_flag & 1) #define is_cloned_kbnode(a) ((a)->private_flag & 2) /* * A structure to store key identification as well as some stuff * needed for key validation. */ struct key_item { struct key_item *next; unsigned int ownertrust,min_ownertrust; byte trust_depth; byte trust_value; char *trust_regexp; u32 kid[2]; }; /* Bit flags used with build_pk_list. */ enum { PK_LIST_ENCRYPT_TO = 1, /* This is an encrypt-to recipient. */ PK_LIST_HIDDEN = 2, /* This is a hidden recipient. */ PK_LIST_CONFIG = 4, /* Specified via config file. */ PK_LIST_FROM_FILE = 8 /* Take key from file with that name. */ }; /* To store private data in the flags the private data must be left * shifted by this value. */ enum { PK_LIST_SHIFT = 4 }; /* Structure to hold a couple of public key certificates. */ typedef struct pk_list *PK_LIST; /* Deprecated. */ typedef struct pk_list *pk_list_t; struct pk_list { PK_LIST next; PKT_public_key *pk; int flags; /* See PK_LIST_ constants. */ }; /* Structure to hold a list of secret key certificates. */ typedef struct sk_list *SK_LIST; struct sk_list { SK_LIST next; PKT_public_key *pk; int mark; /* not used */ }; /* structure to collect all information which can be used to * identify a public key */ typedef struct pubkey_find_info *PUBKEY_FIND_INFO; struct pubkey_find_info { u32 keyid[2]; unsigned nbits; byte pubkey_algo; byte fingerprint[MAX_FINGERPRINT_LEN]; char userid[1]; }; /* Helper type for preference functions. */ union pref_hint { int digest_length; }; /* Constants to describe from where a key was fetched or updated. */ enum { KEYORG_UNKNOWN = 0, KEYORG_KS = 1, /* Public keyserver. */ KEYORG_KS_PREF = 2, /* Preferred keysrver. */ KEYORG_DANE = 3, /* OpenPGP DANE. */ KEYORG_WKD = 4, /* Web Key Directory. */ KEYORG_URL = 5, /* Trusted URL. */ KEYORG_FILE = 6, /* Trusted file. */ KEYORG_SELF = 7 /* We generated it. */ }; /* * Check whether the signature SIG is in the klist K. */ static inline struct key_item * is_in_klist (struct key_item *k, PKT_signature *sig) { for (; k; k = k->next) { if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1]) return k; } return NULL; } +/*-- call-keyboxd.c --*/ + +/* Create a new database handle. Returns NULL on error, sets ERRNO, + * and prints an error diagnostic. */ +KEYDB_HANDLE keydb_new (ctrl_t ctrl); + +/* Release a keydb handle. */ +void keydb_release (KEYDB_HANDLE hd); + +/* Take a lock if we are not using the keyboxd. */ +gpg_error_t keydb_lock (KEYDB_HANDLE hd); + +/* Return the keyblock last found by keydb_search. */ +gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb); + +/* Update the keyblock KB. */ +gpg_error_t keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb); + +/* Insert a keyblock into one of the storage system. */ +gpg_error_t keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb); + +/* Delete the currently selected keyblock. */ +gpg_error_t keydb_delete_keyblock (KEYDB_HANDLE hd); + +/* Clears the current search result and resets the handle's position. */ +gpg_error_t keydb_search_reset (KEYDB_HANDLE hd); + +/* Search the database for keys matching the search description. */ +gpg_error_t keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, + size_t ndesc, size_t *descindex); + + /*-- keydb.c --*/ #define KEYDB_RESOURCE_FLAG_PRIMARY 2 /* The primary resource. */ #define KEYDB_RESOURCE_FLAG_DEFAULT 4 /* The default one. */ #define KEYDB_RESOURCE_FLAG_READONLY 8 /* Open in read only mode. */ #define KEYDB_RESOURCE_FLAG_GPGVDEF 16 /* Default file for gpgv. */ /* Format a search term for debugging output. The caller must free the result. */ char *keydb_search_desc_dump (struct keydb_search_desc *desc); /* Register a resource (keyring or keybox). */ gpg_error_t keydb_add_resource (const char *url, unsigned int flags); /* Dump some statistics to the log. */ void keydb_dump_stats (void); -/* Create a new database handle. Returns NULL on error, sets ERRNO, - and prints an error diagnostic. */ -KEYDB_HANDLE keydb_new (void); - -/* Free all resources owned by the database handle. */ -void keydb_release (KEYDB_HANDLE hd); - -/* Take a lock on the files immediately and not only during insert or - * update. This lock is released with keydb_release. */ -gpg_error_t keydb_lock (KEYDB_HANDLE hd); - /* Set a flag on the handle to suppress use of cached results. This is required for updating a keyring and for key listings. Fixme: Using a new parameter for keydb_new might be a better solution. */ void keydb_disable_caching (KEYDB_HANDLE hd); /* Save the last found state and invalidate the current selection. */ void keydb_push_found_state (KEYDB_HANDLE hd); /* Restore the previous save state. */ void keydb_pop_found_state (KEYDB_HANDLE hd); /* Return the file name of the resource. */ const char *keydb_get_resource_name (KEYDB_HANDLE hd); -/* Return the keyblock last found by keydb_search. */ -gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb); - -/* Update the keyblock KB. */ -gpg_error_t keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb); - -/* Insert a keyblock into one of the underlying keyrings or keyboxes. */ -gpg_error_t keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb); - -/* Delete the currently selected keyblock. */ -gpg_error_t keydb_delete_keyblock (KEYDB_HANDLE hd); - /* Find the first writable resource. */ gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd); /* Rebuild the on-disk caches of all key resources. */ void keydb_rebuild_caches (ctrl_t ctrl, int noisy); /* Return the number of skipped blocks (because they were to large to read from a keybox) since the last search reset. */ unsigned long keydb_get_skipped_counter (KEYDB_HANDLE hd); -/* Clears the current search result and resets the handle's position. */ -gpg_error_t keydb_search_reset (KEYDB_HANDLE hd); - -/* Search the database for keys matching the search description. */ -gpg_error_t keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, - size_t ndesc, size_t *descindex); - /* Return the first non-legacy key in the database. */ gpg_error_t keydb_search_first (KEYDB_HANDLE hd); /* Return the next key (not the next matching key!). */ gpg_error_t keydb_search_next (KEYDB_HANDLE hd); /* This is a convenience function for searching for keys with a long key id. */ gpg_error_t keydb_search_kid (KEYDB_HANDLE hd, u32 *kid); /* This is a convenience function for searching for keys by * fingerprint. */ gpg_error_t keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr, size_t fprlen); /*-- pkclist.c --*/ void show_revocation_reason (ctrl_t ctrl, PKT_public_key *pk, int mode ); int check_signatures_trust (ctrl_t ctrl, PKT_signature *sig); void release_pk_list (PK_LIST pk_list); int build_pk_list (ctrl_t ctrl, strlist_t rcpts, PK_LIST *ret_pk_list); gpg_error_t find_and_check_key (ctrl_t ctrl, const char *name, unsigned int use, int mark_hidden, int from_file, pk_list_t *pk_list_addr); int algo_available( preftype_t preftype, int algo, const union pref_hint *hint ); int select_algo_from_prefs( PK_LIST pk_list, int preftype, int request, const union pref_hint *hint); int select_mdc_from_pklist (PK_LIST pk_list); aead_algo_t select_aead_from_pklist (pk_list_t pk_list); void warn_missing_aead_from_pklist (PK_LIST pk_list); void warn_missing_aes_from_pklist (PK_LIST pk_list); /*-- skclist.c --*/ int random_is_faked (void); void release_sk_list( SK_LIST sk_list ); gpg_error_t build_sk_list (ctrl_t ctrl, strlist_t locusr, SK_LIST *ret_sk_list, unsigned use); /*-- passphrase.h --*/ int have_static_passphrase(void); const char *get_static_passphrase (void); void set_passphrase_from_string(const char *pass); void read_passphrase_from_fd( int fd ); void passphrase_clear_cache (const char *cacheid); DEK *passphrase_to_dek_ext(u32 *keyid, int pubkey_algo, int cipher_algo, STRING2KEY *s2k, int mode, const char *tryagain_text, const char *custdesc, const char *custprompt, int *canceled); DEK *passphrase_to_dek (int cipher_algo, STRING2KEY *s2k, int create, int nocache, const char *tryagain_text, int *canceled); void set_next_passphrase( const char *s ); char *get_last_passphrase(void); void next_to_last_passphrase(void); void emit_status_need_passphrase (ctrl_t ctrl, u32 *keyid, u32 *mainkeyid, int pubkey_algo); #define FORMAT_KEYDESC_NORMAL 0 #define FORMAT_KEYDESC_IMPORT 1 #define FORMAT_KEYDESC_EXPORT 2 #define FORMAT_KEYDESC_DELKEY 3 char *gpg_format_keydesc (ctrl_t ctrl, PKT_public_key *pk, int mode, int escaped); /*-- getkey.c --*/ /* Cache a copy of a public key in the public key cache. */ void cache_public_key( PKT_public_key *pk ); /* Disable and drop the public key cache. */ void getkey_disable_caches(void); /* Return the public key used for signature SIG and store it at PK. */ gpg_error_t get_pubkey_for_sig (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig); /* Return the public key with the key id KEYID and store it at PK. */ int get_pubkey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid); /* Similar to get_pubkey, but it does not take PK->REQ_USAGE into account nor does it merge in the self-signed data. This function also only considers primary keys. */ -int get_pubkey_fast (PKT_public_key *pk, u32 *keyid); +int get_pubkey_fast (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid); /* Return the entire keyblock used to create SIG. This is a * specialized version of get_pubkeyblock. */ kbnode_t get_pubkeyblock_for_sig (ctrl_t ctrl, PKT_signature *sig); /* Return the key block for the key with KEYID. */ kbnode_t get_pubkeyblock (ctrl_t ctrl, u32 *keyid); /* A list used by get_pubkeys to gather all of the matches. */ struct pubkey_s { struct pubkey_s *next; /* The key to use (either the public key or the subkey). */ PKT_public_key *pk; kbnode_t keyblock; }; typedef struct pubkey_s *pubkey_t; /* Free a list of public keys. */ void pubkeys_free (pubkey_t keys); /* Mode flags for get_pubkey_byname. */ enum get_pubkey_modes { GET_PUBKEY_NORMAL = 0, GET_PUBKEY_NO_AKL = 1, GET_PUBKEY_NO_LOCAL = 2 }; /* Find a public key identified by NAME. */ int get_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, GETKEY_CTX *retctx, PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd, int include_unusable); /* Likewise, but only return the best match if NAME resembles a mail * address. */ gpg_error_t get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, GETKEY_CTX *retctx, PKT_public_key *pk, const char *name, KBNODE *ret_keyblock, int include_unusable); /* Get a public key directly from file FNAME. */ gpg_error_t get_pubkey_fromfile (ctrl_t ctrl, PKT_public_key *pk, const char *fname); /* Return the public key with the key id KEYID iff the secret key is * available and store it at PK. */ gpg_error_t get_seckey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid); /* Lookup a key with the specified fingerprint. */ int get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock, const byte *fprint, size_t fprint_len); /* This function is similar to get_pubkey_byfprint, but it doesn't merge the self-signed data into the public key and subkeys or into the user ids. */ -gpg_error_t get_pubkey_byfprint_fast (PKT_public_key *pk, +gpg_error_t get_pubkey_byfprint_fast (ctrl_t ctrl, PKT_public_key *pk, const byte *fprint, size_t fprint_len); /* This function is similar to get_pubkey_byfprint, but it doesn't merge the self-signed data into the public key and subkeys or into the user ids. */ -gpg_error_t get_keyblock_byfprint_fast (kbnode_t *r_keyblock, +gpg_error_t get_keyblock_byfprint_fast (ctrl_t ctrl, + kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd, const byte *fprint, size_t fprint_len, int lock); /* Returns true if a secret key is available for the public key with key id KEYID. */ -int have_secret_key_with_kid (u32 *keyid); +int have_secret_key_with_kid (ctrl_t ctrl, u32 *keyid); /* Parse the --default-key parameter. Returns the last key (in terms of when the option is given) that is available. */ const char *parse_def_secret_key (ctrl_t ctrl); /* Look up a secret key. */ gpg_error_t get_seckey_default (ctrl_t ctrl, PKT_public_key *pk); gpg_error_t get_seckey_default_or_card (ctrl_t ctrl, PKT_public_key *pk, const byte *fpr, size_t fpr_len); /* Search for keys matching some criteria. */ gpg_error_t getkey_bynames (ctrl_t ctrl, getkey_ctx_t *retctx, PKT_public_key *pk, strlist_t names, int want_secret, kbnode_t *ret_keyblock); /* Search for one key matching some criteria. */ gpg_error_t getkey_byname (ctrl_t ctrl, getkey_ctx_t *retctx, PKT_public_key *pk, const char *name, int want_secret, kbnode_t *ret_keyblock); /* Return the next search result. */ gpg_error_t getkey_next (ctrl_t ctrl, getkey_ctx_t ctx, PKT_public_key *pk, kbnode_t *ret_keyblock); /* Release any resources used by a key listing context. */ void getkey_end (ctrl_t ctrl, getkey_ctx_t ctx); /* Return the database handle used by this context. The context still owns the handle. */ KEYDB_HANDLE get_ctx_handle(GETKEY_CTX ctx); /* Enumerate some secret keys. */ gpg_error_t enum_secret_keys (ctrl_t ctrl, void **context, PKT_public_key *pk); /* Set the mainkey_id fields for all keys in KEYBLOCK. */ void setup_main_keyids (kbnode_t keyblock); /* This function merges information from the self-signed data into the data structures. */ void merge_keys_and_selfsig (ctrl_t ctrl, kbnode_t keyblock); char *get_user_id_string_native (ctrl_t ctrl, u32 *keyid); char *get_long_user_id_string (ctrl_t ctrl, u32 *keyid); char *get_user_id (ctrl_t ctrl, u32 *keyid, size_t *rn, int *r_nouid); char *get_user_id_native (ctrl_t ctrl, u32 *keyid); char *get_user_id_byfpr_native (ctrl_t ctrl, const byte *fpr, size_t fprlen); void release_akl(void); int parse_auto_key_locate(const char *options); int parse_key_origin (char *string); const char *key_origin_string (int origin); /*-- keyid.c --*/ int pubkey_letter( int algo ); char *pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize); #define PUBKEY_STRING_SIZE 32 u32 v3_keyid (gcry_mpi_t a, u32 *ki); void hash_public_key( gcry_md_hd_t md, PKT_public_key *pk ); char *format_keyid (u32 *keyid, int format, char *buffer, int len); /* Return PK's keyid. The memory is owned by PK. */ u32 *pk_keyid (PKT_public_key *pk); /* Return the keyid of the primary key associated with PK. The memory is owned by PK. */ u32 *pk_main_keyid (PKT_public_key *pk); /* Order A and B. If A < B then return -1, if A == B then return 0, and if A > B then return 1. */ static int GPGRT_ATTR_UNUSED keyid_cmp (const u32 *a, const u32 *b) { if (a[0] < b[0]) return -1; if (a[0] > b[0]) return 1; if (a[1] < b[1]) return -1; if (a[1] > b[1]) return 1; return 0; } /* Return whether PK is a primary key. */ static int GPGRT_ATTR_UNUSED pk_is_primary (PKT_public_key *pk) { return keyid_cmp (pk_keyid (pk), pk_main_keyid (pk)) == 0; } /* Copy the keyid in SRC to DEST and return DEST. */ u32 *keyid_copy (u32 *dest, const u32 *src); size_t keystrlen(void); const char *keystr(u32 *keyid); const char *keystr_with_sub (u32 *main_kid, u32 *sub_kid); const char *keystr_from_pk(PKT_public_key *pk); const char *keystr_from_pk_with_sub (PKT_public_key *main_pk, PKT_public_key *sub_pk); /* Return PK's key id as a string using the default format. PK owns the storage. */ const char *pk_keyid_str (PKT_public_key *pk); const char *keystr_from_desc(KEYDB_SEARCH_DESC *desc); u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid ); u32 keyid_from_sig (PKT_signature *sig, u32 *keyid ); u32 keyid_from_fingerprint (ctrl_t ctrl, const byte *fprint, size_t fprint_len, u32 *keyid); byte *namehash_from_uid(PKT_user_id *uid); unsigned nbits_from_pk( PKT_public_key *pk ); /* Convert an UTC TIMESTAMP into an UTC yyyy-mm-dd string. Return * that string. The caller should pass a buffer with at least a size * of MK_DATESTR_SIZE. */ char *mk_datestr (char *buffer, size_t bufsize, u32 timestamp); #define MK_DATESTR_SIZE 11 const char *datestr_from_pk( PKT_public_key *pk ); const char *datestr_from_sig( PKT_signature *sig ); const char *expirestr_from_pk( PKT_public_key *pk ); const char *expirestr_from_sig( PKT_signature *sig ); const char *revokestr_from_pk( PKT_public_key *pk ); const char *usagestr_from_pk (PKT_public_key *pk, int fill); const char *colon_strtime (u32 t); const char *colon_datestr_from_pk (PKT_public_key *pk); const char *colon_datestr_from_sig (PKT_signature *sig); const char *colon_expirestr_from_sig (PKT_signature *sig); byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len ); char *hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen); char *format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen); gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array); gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip); /*-- kbnode.c --*/ KBNODE new_kbnode( PACKET *pkt ); KBNODE clone_kbnode( KBNODE node ); void release_kbnode( KBNODE n ); void delete_kbnode( KBNODE node ); void add_kbnode( KBNODE root, KBNODE node ); void insert_kbnode( KBNODE root, KBNODE node, int pkttype ); void move_kbnode( KBNODE *root, KBNODE node, KBNODE where ); void remove_kbnode( KBNODE *root, KBNODE node ); KBNODE find_prev_kbnode( KBNODE root, KBNODE node, int pkttype ); KBNODE find_next_kbnode( KBNODE node, int pkttype ); KBNODE find_kbnode( KBNODE node, int pkttype ); KBNODE walk_kbnode( KBNODE root, KBNODE *context, int all ); void clear_kbnode_flags( KBNODE n ); int commit_kbnode( KBNODE *root ); void dump_kbnode( KBNODE node ); #endif /*G10_KEYDB_H*/ diff --git a/g10/keyedit.c b/g10/keyedit.c index 1bf5de9b2..9238d5d53 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -1,6277 +1,6277 @@ /* keyedit.c - Edit properties of a key * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1998-2017 Werner Koch * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #ifdef HAVE_LIBREADLINE # define GNUPG_LIBREADLINE_H_INCLUDED # include #endif #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "../common/iobuf.h" #include "keydb.h" #include "photoid.h" #include "../common/util.h" #include "main.h" #include "trustdb.h" #include "filter.h" #include "../common/ttyio.h" #include "../common/status.h" #include "../common/i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "../common/host2net.h" #include "tofu.h" #include "key-check.h" #include "key-clean.h" #include "keyedit.h" static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose); static void show_names (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, PKT_public_key * pk, unsigned int flag, int with_prefs); static void show_key_with_all_names (ctrl_t ctrl, estream_t fp, KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs, int nowarn); static void show_key_and_fingerprint (ctrl_t ctrl, kbnode_t keyblock, int with_subkeys); static void show_key_and_grip (kbnode_t keyblock); static void subkey_expire_warning (kbnode_t keyblock); static int menu_adduid (ctrl_t ctrl, kbnode_t keyblock, int photo, const char *photo_name, const char *uidstr); static void menu_deluid (KBNODE pub_keyblock); static int menu_delsig (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, int self_only); static void menu_delkey (KBNODE pub_keyblock); static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive); static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended, u32 newexpiration); static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock); static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_set_keyserver_url (ctrl_t ctrl, const char *url, kbnode_t pub_keyblock); static int menu_set_notation (ctrl_t ctrl, const char *string, kbnode_t pub_keyblock); static int menu_select_uid (KBNODE keyblock, int idx); static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash); static int menu_select_key (KBNODE keyblock, int idx, char *p); static int count_uids (KBNODE keyblock); static int count_uids_with_flag (KBNODE keyblock, unsigned flag); static int count_keys_with_flag (KBNODE keyblock, unsigned flag); static int count_selected_uids (KBNODE keyblock); static int real_uids_left (KBNODE keyblock); static int count_selected_keys (KBNODE keyblock); static int menu_revsig (ctrl_t ctrl, kbnode_t keyblock); static int menu_revuid (ctrl_t ctrl, kbnode_t keyblock); static int core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, const struct revocation_reason_info *reason, int *modified); static int menu_revkey (ctrl_t ctrl, kbnode_t pub_keyblock); static int menu_revsubkey (ctrl_t ctrl, kbnode_t pub_keyblock); #ifndef NO_TRUST_MODELS static int enable_disable_key (ctrl_t ctrl, kbnode_t keyblock, int disable); #endif /*!NO_TRUST_MODELS*/ static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock); static int update_trust = 0; #define CONTROL_D ('D' - 'A' + 1) struct sign_attrib { int non_exportable, non_revocable; struct revocation_reason_info *reason; byte trust_depth, trust_value; char *trust_regexp; }; /* TODO: Fix duplicated code between here and the check-sigs/list-sigs code in keylist.c. */ static int print_and_check_one_sig_colon (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int *is_selfsig, int print_without_key) { PKT_signature *sig = node->pkt->pkt.signature; int rc, sigrc; /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keylist.c:list_keyblock_print */ rc = check_key_signature (ctrl, keyblock, node, is_selfsig); switch (gpg_err_code (rc)) { case 0: node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR); sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: node->flag = NODFLG_BADSIG; sigrc = '-'; if (inv_sigs) ++ * inv_sigs; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if (no_key) ++ * no_key; break; default: node->flag = NODFLG_SIGERR; sigrc = '%'; if (oth_err) ++ * oth_err; break; } if (sigrc != '?' || print_without_key) { es_printf ("sig:%c::%d:%08lX%08lX:%lu:%lu:", sigrc, sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], (ulong) sig->timestamp, (ulong) sig->expiredate); if (sig->trust_depth || sig->trust_value) es_printf ("%d %d", sig->trust_depth, sig->trust_value); es_printf (":"); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_printf ("::%02x%c\n", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); if (opt.show_subpackets) print_subpackets_colon (sig); } return (sigrc == '!'); } /* * Print information about a signature (rc is its status), check it * and return true if the signature is okay. NODE must be a signature * packet. With EXTENDED set all possible signature list options will * always be printed. */ int keyedit_print_one_sig (ctrl_t ctrl, estream_t fp, int rc, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int is_selfsig, int print_without_key, int extended) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; int is_rev = sig->sig_class == 0x30; /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keylist.c:list_keyblock_print */ switch (gpg_err_code (rc)) { case 0: node->flag &= ~(NODFLG_BADSIG | NODFLG_NOKEY | NODFLG_SIGERR); sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: node->flag = NODFLG_BADSIG; sigrc = '-'; if (inv_sigs) ++ * inv_sigs; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: node->flag = NODFLG_NOKEY; sigrc = '?'; if (no_key) ++ * no_key; break; default: node->flag = NODFLG_SIGERR; sigrc = '%'; if (oth_err) ++ * oth_err; break; } if (sigrc != '?' || print_without_key) { tty_fprintf (fp, "%s%c%c %c%c%c%c%c%c %s %s", is_rev ? "rev" : "sig", sigrc, (sig->sig_class - 0x10 > 0 && sig->sig_class - 0x10 < 4) ? '0' + sig->sig_class - 0x10 : ' ', sig->flags.exportable ? ' ' : 'L', sig->flags.revocable ? ' ' : 'R', sig->flags.policy_url ? 'P' : ' ', sig->flags.notation ? 'N' : ' ', sig->flags.expired ? 'X' : ' ', (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > 0) ? '0' + sig->trust_depth : ' ', keystr (sig->keyid), datestr_from_sig (sig)); if ((opt.list_options & LIST_SHOW_SIG_EXPIRE) || extended ) tty_fprintf (fp, " %s", expirestr_from_sig (sig)); tty_fprintf (fp, " "); if (sigrc == '%') tty_fprintf (fp, "[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; else if (is_selfsig) { tty_fprintf (fp, is_rev ? _("[revocation]") : _("[self-signature]")); if (extended && sig->flags.chosen_selfsig) tty_fprintf (fp, "*"); } else { size_t n; char *p = get_user_id (ctrl, sig->keyid, &n, NULL); tty_print_utf8_string2 (fp, p, n, opt.screen_columns - keystrlen () - 26 - ((opt. list_options & LIST_SHOW_SIG_EXPIRE) ? 11 : 0)); xfree (p); } if (fp == log_get_stream ()) log_printf ("\n"); else tty_fprintf (fp, "\n"); if (sig->flags.policy_url && ((opt.list_options & LIST_SHOW_POLICY_URLS) || extended)) show_policy_url (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0)); if (sig->flags.notation && ((opt.list_options & LIST_SHOW_NOTATIONS) || extended)) show_notation (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0), ((opt. list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + ((opt. list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0)); if (sig->flags.pref_ks && ((opt.list_options & LIST_SHOW_KEYSERVER_URLS) || extended)) show_keyserver_url (sig, 3, (!fp? -1 : fp == log_get_stream ()? 1 : 0)); if (extended) { PKT_public_key *pk = keyblock->pkt->pkt.public_key; const unsigned char *s; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (s && *s) tty_fprintf (fp, " [primary]\n"); s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL); if (s && buf32_to_u32 (s)) tty_fprintf (fp, " [expires: %s]\n", isotimestamp (pk->timestamp + buf32_to_u32 (s))); } } return (sigrc == '!'); } static int print_and_check_one_sig (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node, int *inv_sigs, int *no_key, int *oth_err, int *is_selfsig, int print_without_key, int extended) { int rc; rc = check_key_signature (ctrl, keyblock, node, is_selfsig); return keyedit_print_one_sig (ctrl, NULL, rc, keyblock, node, inv_sigs, no_key, oth_err, *is_selfsig, print_without_key, extended); } static int sign_mk_attrib (PKT_signature * sig, void *opaque) { struct sign_attrib *attrib = opaque; byte buf[8]; if (attrib->non_exportable) { buf[0] = 0; /* not exportable */ build_sig_subpkt (sig, SIGSUBPKT_EXPORTABLE, buf, 1); } if (attrib->non_revocable) { buf[0] = 0; /* not revocable */ build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); } if (attrib->reason) revocation_reason_build_cb (sig, attrib->reason); if (attrib->trust_depth) { /* Not critical. If someone doesn't understand trust sigs, this can still be a valid regular signature. */ buf[0] = attrib->trust_depth; buf[1] = attrib->trust_value; build_sig_subpkt (sig, SIGSUBPKT_TRUST, buf, 2); /* Critical. If someone doesn't understands regexps, this whole sig should be invalid. Note the +1 for the length - regexps are null terminated. */ if (attrib->trust_regexp) build_sig_subpkt (sig, SIGSUBPKT_FLAG_CRITICAL | SIGSUBPKT_REGEXP, attrib->trust_regexp, strlen (attrib->trust_regexp) + 1); } return 0; } static void trustsig_prompt (byte * trust_value, byte * trust_depth, char **regexp) { char *p; *trust_value = 0; *trust_depth = 0; *regexp = NULL; /* Same string as pkclist.c:do_edit_ownertrust */ tty_printf (_ ("Please decide how far you trust this user to correctly verify" " other users' keys\n(by looking at passports, checking" " fingerprints from different sources, etc.)\n")); tty_printf ("\n"); tty_printf (_(" %d = I trust marginally\n"), 1); tty_printf (_(" %d = I trust fully\n"), 2); tty_printf ("\n"); while (*trust_value == 0) { p = cpr_get ("trustsig_prompt.trust_value", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); /* 60 and 120 are as per RFC2440 */ if (p[0] == '1' && !p[1]) *trust_value = 60; else if (p[0] == '2' && !p[1]) *trust_value = 120; xfree (p); } tty_printf ("\n"); tty_printf (_("Please enter the depth of this trust signature.\n" "A depth greater than 1 allows the key you are" " signing to make\n" "trust signatures on your behalf.\n")); tty_printf ("\n"); while (*trust_depth == 0) { p = cpr_get ("trustsig_prompt.trust_depth", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); *trust_depth = atoi (p); xfree (p); } tty_printf ("\n"); tty_printf (_("Please enter a domain to restrict this signature, " "or enter for none.\n")); tty_printf ("\n"); p = cpr_get ("trustsig_prompt.trust_regexp", _("Your selection? ")); trim_spaces (p); cpr_kill_prompt (); if (strlen (p) > 0) { char *q = p; int regexplen = 100, ind; *regexp = xmalloc (regexplen); /* Now mangle the domain the user entered into a regexp. To do this, \-escape everything that isn't alphanumeric, and attach "<[^>]+[@.]" to the front, and ">$" to the end. */ strcpy (*regexp, "<[^>]+[@.]"); ind = strlen (*regexp); while (*q) { if (!((*q >= 'A' && *q <= 'Z') || (*q >= 'a' && *q <= 'z') || (*q >= '0' && *q <= '9'))) (*regexp)[ind++] = '\\'; (*regexp)[ind++] = *q; if ((regexplen - ind) < 3) { regexplen += 100; *regexp = xrealloc (*regexp, regexplen); } q++; } (*regexp)[ind] = '\0'; strcat (*regexp, ">$"); } xfree (p); tty_printf ("\n"); } /* * Loop over all LOCUSR and sign the uids after asking. If no * user id is marked, all user ids will be signed; if some user_ids * are marked only those will be signed. If QUICK is true the * function won't ask the user and use sensible defaults. */ static int sign_uids (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, strlist_t locusr, int *ret_modified, int local, int nonrevocable, int trust, int interactive, int quick) { int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; PKT_public_key *pk = NULL; KBNODE node, uidnode; PKT_public_key *primary_pk = NULL; int select_all = !count_selected_uids (keyblock) || interactive; /* Build a list of all signators. * * We use the CERT flag to request the primary which must always * be one which is capable of signing keys. I can't see a reason * why to sign keys using a subkey. Implementation of USAGE_CERT * is just a hack in getkey.c and does not mean that a subkey * marked as certification capable will be used. */ rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT); if (rc) goto leave; /* Loop over all signators. */ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { u32 sk_keyid[2], pk_keyid[2]; char *p, *trust_regexp = NULL; int class = 0, selfsig = 0; u32 duration = 0, timestamp = 0; byte trust_depth = 0, trust_value = 0; pk = sk_rover->pk; keyid_from_pk (pk, sk_keyid); /* Set mark A for all selected user ids. */ for (node = keyblock; node; node = node->next) { if (select_all || (node->flag & NODFLG_SELUID)) node->flag |= NODFLG_MARK_A; else node->flag &= ~NODFLG_MARK_A; } /* Reset mark for uids which are already signed. */ uidnode = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { primary_pk = node->pkt->pkt.public_key; keyid_from_pk (primary_pk, pk_keyid); /* Is this a self-sig? */ if (pk_keyid[0] == sk_keyid[0] && pk_keyid[1] == sk_keyid[1]) selfsig = 1; } else if (node->pkt->pkttype == PKT_USER_ID) { uidnode = (node->flag & NODFLG_MARK_A) ? node : NULL; if (uidnode) { int yesreally = 0; char *user; user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); if (opt.only_sign_text_ids && uidnode->pkt->pkt.user_id->attribs) { tty_fprintf (fp, _("Skipping user ID \"%s\"," " which is not a text ID.\n"), user); uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (uidnode->pkt->pkt.user_id->flags.revoked) { tty_fprintf (fp, _("User ID \"%s\" is revoked."), user); if (selfsig) tty_fprintf (fp, "\n"); else if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.revoke_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } else if (uidnode->pkt->pkt.user_id->flags.expired) { tty_fprintf (fp, _("User ID \"%s\" is expired."), user); if (selfsig) tty_fprintf (fp, "\n"); else if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.expire_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } else if (!uidnode->pkt->pkt.user_id->created && !selfsig) { tty_fprintf (fp, _("User ID \"%s\" is not self-signed."), user); if (opt.expert && !quick) { tty_fprintf (fp, "\n"); /* No, so remove the mark and continue */ if (!cpr_get_answer_is_yes ("sign_uid.nosig_okay", _("Are you sure you " "still want to sign " "it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } else if (interactive) yesreally = 1; } else { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; tty_fprintf (fp, _(" Unable to sign.\n")); } } if (uidnode && interactive && !yesreally && !quick) { tty_fprintf (fp, _("User ID \"%s\" is signable. "), user); if (!cpr_get_answer_is_yes ("sign_uid.sign_okay", _("Sign it? (y/N) "))) { uidnode->flag &= ~NODFLG_MARK_A; uidnode = NULL; } } xfree (user); } } else if (uidnode && node->pkt->pkttype == PKT_SIGNATURE && (node->pkt->pkt.signature->sig_class & ~3) == 0x10) { if (sk_keyid[0] == node->pkt->pkt.signature->keyid[0] && sk_keyid[1] == node->pkt->pkt.signature->keyid[1]) { char buf[50]; char *user; user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); /* It's a v3 self-sig. Make it into a v4 self-sig? */ if (node->pkt->pkt.signature->version < 4 && selfsig && !quick) { tty_fprintf (fp, _("The self-signature on \"%s\"\n" "is a PGP 2.x-style signature.\n"), user); /* Note that the regular PGP2 warning below still applies if there are no v4 sigs on this key at all. */ if (opt.expert) if (cpr_get_answer_is_yes ("sign_uid.v4_promote_okay", _("Do you want to promote " "it to an OpenPGP self-" "signature? (y/N) "))) { node->flag |= NODFLG_DELSIG; xfree (user); continue; } } /* Is the current signature expired? */ if (node->pkt->pkt.signature->flags.expired) { tty_fprintf (fp, _("Your current signature on \"%s\"\n" "has expired.\n"), user); if (quick || cpr_get_answer_is_yes ("sign_uid.replace_expired_okay", _("Do you want to issue a " "new signature to replace " "the expired one? (y/N) "))) { /* Mark these for later deletion. We don't want to delete them here, just in case the replacement signature doesn't happen for some reason. We only delete these after the replacement is already in place. */ node->flag |= NODFLG_DELSIG; xfree (user); continue; } } if (!node->pkt->pkt.signature->flags.exportable && !local) { /* It's a local sig, and we want to make a exportable sig. */ tty_fprintf (fp, _("Your current signature on \"%s\"\n" "is a local signature.\n"), user); if (quick || cpr_get_answer_is_yes ("sign_uid.local_promote_okay", _("Do you want to promote " "it to a full exportable " "signature? (y/N) "))) { /* Mark these for later deletion. We don't want to delete them here, just in case the replacement signature doesn't happen for some reason. We only delete these after the replacement is already in place. */ node->flag |= NODFLG_DELSIG; xfree (user); continue; } } /* Fixme: see whether there is a revocation in which * case we should allow signing it again. */ if (!node->pkt->pkt.signature->flags.exportable && local) tty_fprintf ( fp, _("\"%s\" was already locally signed by key %s\n"), user, keystr_from_pk (pk)); else tty_fprintf (fp, _("\"%s\" was already signed by key %s\n"), user, keystr_from_pk (pk)); if (opt.expert && !quick && cpr_get_answer_is_yes ("sign_uid.dupe_okay", _("Do you want to sign it " "again anyway? (y/N) "))) { /* Don't delete the old sig here since this is an --expert thing. */ xfree (user); continue; } snprintf (buf, sizeof buf, "%08lX%08lX", (ulong) pk->keyid[0], (ulong) pk->keyid[1]); write_status_text (STATUS_ALREADY_SIGNED, buf); uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */ xfree (user); } } } /* Check whether any uids are left for signing. */ if (!count_uids_with_flag (keyblock, NODFLG_MARK_A)) { tty_fprintf (fp, _("Nothing to sign with key %s\n"), keystr_from_pk (pk)); continue; } /* Ask whether we really should sign these user id(s). */ tty_fprintf (fp, "\n"); show_key_with_all_names (ctrl, fp, keyblock, 1, 0, 1, 0, 0, 0); tty_fprintf (fp, "\n"); if (primary_pk->expiredate && !selfsig) { /* Static analyzer note: A claim that PRIMARY_PK might be NULL is not correct because it set from the public key packet which is always the first packet in a keyblock and parsed in the above loop over the keyblock. In case the keyblock has no packets at all and thus the loop was not entered the above count_uids_with_flag would have detected this case. */ u32 now = make_timestamp (); if (primary_pk->expiredate <= now) { tty_fprintf (fp, _("This key has expired!")); if (opt.expert && !quick) { tty_fprintf (fp, " "); if (!cpr_get_answer_is_yes ("sign_uid.expired_okay", _("Are you sure you still " "want to sign it? (y/N) "))) continue; } else { tty_fprintf (fp, _(" Unable to sign.\n")); continue; } } else { tty_fprintf (fp, _("This key is due to expire on %s.\n"), expirestr_from_pk (primary_pk)); if (opt.ask_cert_expire && !quick) { char *answer = cpr_get ("sign_uid.expire", _("Do you want your signature to " "expire at the same time? (Y/n) ")); if (answer_is_yes_no_default (answer, 1)) { /* This fixes the signature timestamp we're going to make as now. This is so the expiration date is exactly correct, and not a few seconds off (due to the time it takes to answer the questions, enter the passphrase, etc). */ timestamp = now; duration = primary_pk->expiredate - now; } cpr_kill_prompt (); xfree (answer); } } } /* Only ask for duration if we haven't already set it to match the expiration of the pk */ if (!duration && !selfsig) { if (opt.ask_cert_expire && !quick) duration = ask_expire_interval (1, opt.def_cert_expire); else duration = parse_expire_string (opt.def_cert_expire); } if (selfsig) ; else { if (opt.batch || !opt.ask_cert_level || quick) class = 0x10 + opt.def_cert_level; else { char *answer; tty_fprintf (fp, _("How carefully have you verified the key you are " "about to sign actually belongs\nto the person " "named above? If you don't know what to " "answer, enter \"0\".\n")); tty_fprintf (fp, "\n"); tty_fprintf (fp, _(" (0) I will not answer.%s\n"), opt.def_cert_level == 0 ? " (default)" : ""); tty_fprintf (fp, _(" (1) I have not checked at all.%s\n"), opt.def_cert_level == 1 ? " (default)" : ""); tty_fprintf (fp, _(" (2) I have done casual checking.%s\n"), opt.def_cert_level == 2 ? " (default)" : ""); tty_fprintf (fp, _(" (3) I have done very careful checking.%s\n"), opt.def_cert_level == 3 ? " (default)" : ""); tty_fprintf (fp, "\n"); while (class == 0) { answer = cpr_get ("sign_uid.class", _("Your selection? " "(enter '?' for more information): ")); if (answer[0] == '\0') class = 0x10 + opt.def_cert_level; /* Default */ else if (ascii_strcasecmp (answer, "0") == 0) class = 0x10; /* Generic */ else if (ascii_strcasecmp (answer, "1") == 0) class = 0x11; /* Persona */ else if (ascii_strcasecmp (answer, "2") == 0) class = 0x12; /* Casual */ else if (ascii_strcasecmp (answer, "3") == 0) class = 0x13; /* Positive */ else tty_fprintf (fp, _("Invalid selection.\n")); xfree (answer); } } if (trust && !quick) trustsig_prompt (&trust_value, &trust_depth, &trust_regexp); } if (!quick) { p = get_user_id_native (ctrl, sk_keyid); tty_fprintf (fp, _("Are you sure that you want to sign this key with your\n" "key \"%s\" (%s)\n"), p, keystr_from_pk (pk)); xfree (p); } if (selfsig) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("This will be a self-signature.\n")); if (local) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("WARNING: the signature will not be marked " "as non-exportable.\n")); } if (nonrevocable) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("WARNING: the signature will not be marked " "as non-revocable.\n")); } } else { if (local) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("The signature will be marked as non-exportable.\n")); } if (nonrevocable) { tty_fprintf (fp, "\n"); tty_fprintf (fp, _("The signature will be marked as non-revocable.\n")); } switch (class) { case 0x11: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have not checked this key at all.\n")); break; case 0x12: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have checked this key casually.\n")); break; case 0x13: tty_fprintf (fp, "\n"); tty_fprintf (fp, _("I have checked this key very carefully.\n")); break; } } tty_fprintf (fp, "\n"); if (opt.batch && opt.answer_yes) ; else if (quick) ; else if (!cpr_get_answer_is_yes ("sign_uid.okay", _("Really sign? (y/N) "))) continue; /* Now we can sign the user ids. */ reloop: /* (Must use this, because we are modifying the list.) */ primary_pk = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) primary_pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_MARK_A)) { PACKET *pkt; PKT_signature *sig; struct sign_attrib attrib; log_assert (primary_pk); memset (&attrib, 0, sizeof attrib); attrib.non_exportable = local; attrib.non_revocable = nonrevocable; attrib.trust_depth = trust_depth; attrib.trust_value = trust_value; attrib.trust_regexp = trust_regexp; node->flag &= ~NODFLG_MARK_A; /* We force creation of a v4 signature for local * signatures, otherwise we would not generate the * subpacket with v3 keys and the signature becomes * exportable. */ if (selfsig) rc = make_keysig_packet (ctrl, &sig, primary_pk, node->pkt->pkt.user_id, NULL, pk, 0x13, 0, 0, keygen_add_std_prefs, primary_pk, NULL); else rc = make_keysig_packet (ctrl, &sig, primary_pk, node->pkt->pkt.user_id, NULL, pk, class, timestamp, duration, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); goto leave; } *ret_modified = 1; /* We changed the keyblock. */ update_trust = 1; pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), PKT_SIGNATURE); goto reloop; } } /* Delete any sigs that got promoted */ for (node = keyblock; node; node = node->next) if (node->flag & NODFLG_DELSIG) delete_kbnode (node); } /* End loop over signators. */ leave: release_sk_list (sk_list); return rc; } /* * Change the passphrase of the primary and all secondary keys. Note * that it is common to use only one passphrase for the primary and * all subkeys. However, this is now (since GnuPG 2.1) all up to the * gpg-agent. Returns 0 on success or an error code. */ static gpg_error_t change_passphrase (ctrl_t ctrl, kbnode_t keyblock) { gpg_error_t err; kbnode_t node; PKT_public_key *pk; int any; u32 keyid[2], subid[2]; char *hexgrip = NULL; char *cache_nonce = NULL; char *passwd_nonce = NULL; node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; public key missing!\n"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } pk = node->pkt->pkt.public_key; keyid_from_pk (pk, keyid); /* Check whether it is likely that we will be able to change the passphrase for any subkey. */ for (any = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { char *serialno; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, subid); xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; err = agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); if (!err && serialno) ; /* Key on card. */ else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) ; /* Maybe stub key. */ else if (!err) any = 1; /* Key is known. */ else log_error ("key %s: error getting keyinfo from agent: %s\n", keystr_with_sub (keyid, subid), gpg_strerror (err)); xfree (serialno); } } err = 0; if (!any) { tty_printf (_("Key has only stub or on-card key items - " "no passphrase to change.\n")); goto leave; } /* Change the passphrase for all keys. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { char *desc; pk = node->pkt->pkt.public_key; keyid_from_pk (pk, subid); xfree (hexgrip); err = hexkeygrip_from_pk (pk, &hexgrip); if (err) goto leave; /* Note that when using --dry-run we don't change the * passphrase but merely verify the current passphrase. */ desc = gpg_format_keydesc (ctrl, pk, FORMAT_KEYDESC_NORMAL, 1); err = agent_passwd (ctrl, hexgrip, desc, !!opt.dry_run, &cache_nonce, &passwd_nonce); xfree (desc); if (err) log_log ((gpg_err_code (err) == GPG_ERR_CANCELED || gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) ? GPGRT_LOGLVL_INFO : GPGRT_LOGLVL_ERROR, _("key %s: error changing passphrase: %s\n"), keystr_with_sub (keyid, subid), gpg_strerror (err)); if (gpg_err_code (err) == GPG_ERR_FULLY_CANCELED) break; } } leave: xfree (hexgrip); xfree (cache_nonce); xfree (passwd_nonce); return err; } /* Fix various problems in the keyblock. Returns true if the keyblock was changed. Note that a pointer to the keyblock must be given and the function may change it (i.e. replacing the first node). */ static int fix_keyblock (ctrl_t ctrl, kbnode_t *keyblockp) { int changed = 0; if (collapse_uids (keyblockp)) changed++; if (key_check_all_keysigs (ctrl, 1, *keyblockp, 0, 1)) changed++; reorder_keyblock (*keyblockp); /* If we modified the keyblock, make sure the flags are right. */ if (changed) merge_keys_and_selfsig (ctrl, *keyblockp); return changed; } static int parse_sign_type (const char *str, int *localsig, int *nonrevokesig, int *trustsig) { const char *p = str; while (*p) { if (ascii_strncasecmp (p, "l", 1) == 0) { *localsig = 1; p++; } else if (ascii_strncasecmp (p, "nr", 2) == 0) { *nonrevokesig = 1; p += 2; } else if (ascii_strncasecmp (p, "t", 1) == 0) { *trustsig = 1; p++; } else return 0; } return 1; } /* * Menu driven key editor. If seckey_check is true, then a secret key * that matches username will be looked for. If it is false, not all * commands will be available. * * Note: to keep track of certain selections we use node->mark MARKBIT_xxxx. */ /* Need an SK for this command */ #define KEYEDIT_NEED_SK 1 /* Need an SUB KEY for this command */ #define KEYEDIT_NEED_SUBSK 2 /* Match the tail of the string */ #define KEYEDIT_TAIL_MATCH 8 enum cmdids { cmdNONE = 0, cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN, cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG, cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY, cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF, cmdEXPIRE, cmdCHANGEUSAGE, cmdBACKSIGN, #ifndef NO_TRUST_MODELS cmdENABLEKEY, cmdDISABLEKEY, #endif /*!NO_TRUST_MODELS*/ cmdSHOWPREF, cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST, cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP }; static struct { const char *name; enum cmdids id; int flags; const char *desc; } cmds[] = { { "quit", cmdQUIT, 0, N_("quit this menu")}, { "q", cmdQUIT, 0, NULL}, { "save", cmdSAVE, 0, N_("save and quit")}, { "help", cmdHELP, 0, N_("show this help")}, { "?", cmdHELP, 0, NULL}, { "fpr", cmdFPR, 0, N_("show key fingerprint")}, { "grip", cmdGRIP, 0, N_("show the keygrip")}, { "list", cmdLIST, 0, N_("list key and user IDs")}, { "l", cmdLIST, 0, NULL}, { "uid", cmdSELUID, 0, N_("select user ID N")}, { "key", cmdSELKEY, 0, N_("select subkey N")}, { "check", cmdCHECK, 0, N_("check signatures")}, { "c", cmdCHECK, 0, NULL}, { "change-usage", cmdCHANGEUSAGE, KEYEDIT_NEED_SK, NULL}, { "cross-certify", cmdBACKSIGN, KEYEDIT_NEED_SK, NULL}, { "backsign", cmdBACKSIGN, KEYEDIT_NEED_SK, NULL}, { "sign", cmdSIGN, KEYEDIT_TAIL_MATCH, N_("sign selected user IDs [* see below for related commands]")}, { "s", cmdSIGN, 0, NULL}, /* "lsign" and friends will never match since "sign" comes first and it is a tail match. They are just here so they show up in the help menu. */ { "lsign", cmdNOP, 0, N_("sign selected user IDs locally")}, { "tsign", cmdNOP, 0, N_("sign selected user IDs with a trust signature")}, { "nrsign", cmdNOP, 0, N_("sign selected user IDs with a non-revocable signature")}, { "debug", cmdDEBUG, 0, NULL}, { "adduid", cmdADDUID, KEYEDIT_NEED_SK, N_("add a user ID")}, { "addphoto", cmdADDPHOTO, KEYEDIT_NEED_SK, N_("add a photo ID")}, { "deluid", cmdDELUID, 0, N_("delete selected user IDs")}, /* delphoto is really deluid in disguise */ { "delphoto", cmdDELUID, 0, NULL}, { "addkey", cmdADDKEY, KEYEDIT_NEED_SK, N_("add a subkey")}, #ifdef ENABLE_CARD_SUPPORT { "addcardkey", cmdADDCARDKEY, KEYEDIT_NEED_SK, N_("add a key to a smartcard")}, { "keytocard", cmdKEYTOCARD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, N_("move a key to a smartcard")}, { "bkuptocard", cmdBKUPTOCARD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, N_("move a backup key to a smartcard")}, #endif /*ENABLE_CARD_SUPPORT */ { "delkey", cmdDELKEY, 0, N_("delete selected subkeys")}, { "addrevoker", cmdADDREVOKER, KEYEDIT_NEED_SK, N_("add a revocation key")}, { "delsig", cmdDELSIG, 0, N_("delete signatures from the selected user IDs")}, { "expire", cmdEXPIRE, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, N_("change the expiration date for the key or selected subkeys")}, { "primary", cmdPRIMARY, KEYEDIT_NEED_SK, N_("flag the selected user ID as primary")}, { "toggle", cmdTOGGLE, KEYEDIT_NEED_SK, NULL}, /* Dummy command. */ { "t", cmdTOGGLE, KEYEDIT_NEED_SK, NULL}, { "pref", cmdPREF, 0, N_("list preferences (expert)")}, { "showpref", cmdSHOWPREF, 0, N_("list preferences (verbose)")}, { "setpref", cmdSETPREF, KEYEDIT_NEED_SK, N_("set preference list for the selected user IDs")}, { "updpref", cmdSETPREF, KEYEDIT_NEED_SK, NULL}, { "keyserver", cmdPREFKS, KEYEDIT_NEED_SK, N_("set the preferred keyserver URL for the selected user IDs")}, { "notation", cmdNOTATION, KEYEDIT_NEED_SK, N_("set a notation for the selected user IDs")}, { "passwd", cmdPASSWD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, N_("change the passphrase")}, { "password", cmdPASSWD, KEYEDIT_NEED_SK | KEYEDIT_NEED_SUBSK, NULL}, #ifndef NO_TRUST_MODELS { "trust", cmdTRUST, 0, N_("change the ownertrust")}, #endif /*!NO_TRUST_MODELS*/ { "revsig", cmdREVSIG, 0, N_("revoke signatures on the selected user IDs")}, { "revuid", cmdREVUID, KEYEDIT_NEED_SK, N_("revoke selected user IDs")}, { "revphoto", cmdREVUID, KEYEDIT_NEED_SK, NULL}, { "revkey", cmdREVKEY, KEYEDIT_NEED_SK, N_("revoke key or selected subkeys")}, #ifndef NO_TRUST_MODELS { "enable", cmdENABLEKEY, 0, N_("enable key")}, { "disable", cmdDISABLEKEY, 0, N_("disable key")}, #endif /*!NO_TRUST_MODELS*/ { "showphoto", cmdSHOWPHOTO, 0, N_("show selected photo IDs")}, { "clean", cmdCLEAN, 0, N_("compact unusable user IDs and remove unusable signatures from key")}, { "minimize", cmdMINIMIZE, 0, N_("compact unusable user IDs and remove all signatures from key")}, { NULL, cmdNONE, 0, NULL} }; #ifdef HAVE_LIBREADLINE /* These two functions are used by readline for command completion. */ static char * command_generator (const char *text, int state) { static int list_index, len; const char *name; /* If this is a new word to complete, initialize now. This includes saving the length of TEXT for efficiency, and initializing the index variable to 0. */ if (!state) { list_index = 0; len = strlen (text); } /* Return the next partial match */ while ((name = cmds[list_index].name)) { /* Only complete commands that have help text */ if (cmds[list_index++].desc && strncmp (name, text, len) == 0) return strdup (name); } return NULL; } static char ** keyedit_completion (const char *text, int start, int end) { /* If we are at the start of a line, we try and command-complete. If not, just do nothing for now. */ (void) end; if (start == 0) return rl_completion_matches (text, command_generator); rl_attempted_completion_over = 1; return NULL; } #endif /* HAVE_LIBREADLINE */ /* Main function of the menu driven key editor. */ void keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr, strlist_t commands, int quiet, int seckey_check) { enum cmdids cmd = 0; gpg_error_t err = 0; KBNODE keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int have_seckey = 0; int have_anyseckey = 0; char *answer = NULL; int redisplay = 1; int modified = 0; int sec_shadowing = 0; int run_subkey_warnings = 0; int have_commands = !!commands; if (opt.command_fd != -1) ; else if (opt.batch && !have_commands) { log_error (_("can't do this in batch mode\n")); goto leave; } #ifdef HAVE_W32_SYSTEM /* Due to Windows peculiarities we need to make sure that the trustdb stale check is done before we open another file (i.e. by searching for a key). In theory we could make sure that the files are closed after use but the open/close caches inhibits that and flushing the cache right before the stale check is not easy to implement. Thus we take the easy way out and run the stale check as early as possible. Note, that for non- W32 platforms it is run indirectly trough a call to get_validity (). */ check_trustdb_stale (ctrl); #endif /* Get the public key */ err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, NULL, NULL, username, &keyblock, &kdbhd, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err)); goto leave; } if (fix_keyblock (ctrl, &keyblock)) modified++; /* See whether we have a matching secret key. */ if (seckey_check) { have_anyseckey = !agent_probe_any_secret_key (ctrl, keyblock); if (have_anyseckey && !agent_probe_secret_key (ctrl, keyblock->pkt->pkt.public_key)) { /* The primary key is also available. */ have_seckey = 1; } if (have_seckey && !quiet) tty_printf (_("Secret key is available.\n")); else if (have_anyseckey && !quiet) tty_printf (_("Secret subkeys are available.\n")); } /* Main command loop. */ for (;;) { int i, arg_number, photo; const char *arg_string = ""; char *p; PKT_public_key *pk = keyblock->pkt->pkt.public_key; tty_printf ("\n"); if (redisplay && !quiet) { /* Show using flags: with_revoker, with_subkeys. */ show_key_with_all_names (ctrl, NULL, keyblock, 0, 1, 0, 1, 0, 0); tty_printf ("\n"); redisplay = 0; } if (run_subkey_warnings) { run_subkey_warnings = 0; if (!count_selected_keys (keyblock)) subkey_expire_warning (keyblock); } do { xfree (answer); if (have_commands) { if (commands) { answer = xstrdup (commands->d); commands = commands->next; } else if (opt.batch) { answer = xstrdup ("quit"); } else have_commands = 0; } if (!have_commands) { #ifdef HAVE_LIBREADLINE tty_enable_completion (keyedit_completion); #endif answer = cpr_get_no_help ("keyedit.prompt", GPG_NAME "> "); cpr_kill_prompt (); tty_disable_completion (); } trim_spaces (answer); } while (*answer == '#'); arg_number = 0; /* Here is the init which egcc complains about. */ photo = 0; /* Same here. */ if (!*answer) cmd = cmdLIST; else if (*answer == CONTROL_D) cmd = cmdQUIT; else if (digitp (answer)) { cmd = cmdSELUID; arg_number = atoi (answer); } else { if ((p = strchr (answer, ' '))) { *p++ = 0; trim_spaces (answer); trim_spaces (p); arg_number = atoi (p); arg_string = p; } for (i = 0; cmds[i].name; i++) { if (cmds[i].flags & KEYEDIT_TAIL_MATCH) { size_t l = strlen (cmds[i].name); size_t a = strlen (answer); if (a >= l) { if (!ascii_strcasecmp (&answer[a - l], cmds[i].name)) { answer[a - l] = '\0'; break; } } } else if (!ascii_strcasecmp (answer, cmds[i].name)) break; } if ((cmds[i].flags & (KEYEDIT_NEED_SK|KEYEDIT_NEED_SUBSK)) && !(((cmds[i].flags & KEYEDIT_NEED_SK) && have_seckey) || ((cmds[i].flags & KEYEDIT_NEED_SUBSK) && have_anyseckey))) { tty_printf (_("Need the secret key to do this.\n")); cmd = cmdNOP; } else cmd = cmds[i].id; } /* Dispatch the command. */ switch (cmd) { case cmdHELP: for (i = 0; cmds[i].name; i++) { if ((cmds[i].flags & (KEYEDIT_NEED_SK|KEYEDIT_NEED_SUBSK)) && !(((cmds[i].flags & KEYEDIT_NEED_SK) && have_seckey) ||((cmds[i].flags&KEYEDIT_NEED_SUBSK)&&have_anyseckey))) ; /* Skip those item if we do not have the secret key. */ else if (cmds[i].desc) tty_printf ("%-11s %s\n", cmds[i].name, _(cmds[i].desc)); } tty_printf ("\n"); tty_printf (_("* The 'sign' command may be prefixed with an 'l' for local " "signatures (lsign),\n" " a 't' for trust signatures (tsign), an 'nr' for " "non-revocable signatures\n" " (nrsign), or any combination thereof (ltsign, " "tnrsign, etc.).\n")); break; case cmdLIST: redisplay = 1; break; case cmdFPR: show_key_and_fingerprint (ctrl, keyblock, (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1)))); break; case cmdGRIP: show_key_and_grip (keyblock); break; case cmdSELUID: if (strlen (arg_string) == NAMEHASH_LEN * 2) redisplay = menu_select_uid_namehash (keyblock, arg_string); else { if (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1))) arg_number = -1; /* Select all. */ redisplay = menu_select_uid (keyblock, arg_number); } break; case cmdSELKEY: { if (*arg_string == '*' && (!arg_string[1] || spacep (arg_string + 1))) arg_number = -1; /* Select all. */ if (menu_select_key (keyblock, arg_number, p)) redisplay = 1; } break; case cmdCHECK: if (key_check_all_keysigs (ctrl, -1, keyblock, count_selected_uids (keyblock), !strcmp (arg_string, "selfsig"))) modified = 1; break; case cmdSIGN: { int localsig = 0, nonrevokesig = 0, trustsig = 0, interactive = 0; if (pk->flags.revoked) { tty_printf (_("Key is revoked.")); if (opt.expert) { tty_printf (" "); if (!cpr_get_answer_is_yes ("keyedit.sign_revoked.okay", _("Are you sure you still want to sign it? (y/N) "))) break; } else { tty_printf (_(" Unable to sign.\n")); break; } } if (count_uids (keyblock) > 1 && !count_selected_uids (keyblock)) { int result; if (opt.only_sign_text_ids) result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", _("Really sign all text user IDs? (y/N) ")); else result = cpr_get_answer_is_yes ("keyedit.sign_all.okay", _("Really sign all user IDs? (y/N) ")); if (! result) { if (opt.interactive) interactive = 1; else { tty_printf (_("Hint: Select the user IDs to sign\n")); have_commands = 0; break; } } } /* What sort of signing are we doing? */ if (!parse_sign_type (answer, &localsig, &nonrevokesig, &trustsig)) { tty_printf (_("Unknown signature type '%s'\n"), answer); break; } sign_uids (ctrl, NULL, keyblock, locusr, &modified, localsig, nonrevokesig, trustsig, interactive, 0); } break; case cmdDEBUG: dump_kbnode (keyblock); break; case cmdTOGGLE: /* The toggle command is a leftover from old gpg versions where we worked with a secret and a public keyring. It is not necessary anymore but we keep this command for the sake of scripts using it. */ redisplay = 1; break; case cmdADDPHOTO: if (RFC2440) { tty_printf (_("This command is not allowed while in %s mode.\n"), gnupg_compliance_option_string (opt.compliance)); break; } photo = 1; /* fall through */ case cmdADDUID: if (menu_adduid (ctrl, keyblock, photo, arg_string, NULL)) { update_trust = 1; redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } break; case cmdDELUID: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (real_uids_left (keyblock) < 1) tty_printf (_("You can't delete the last user ID!\n")); else if (cpr_get_answer_is_yes ("keyedit.remove.uid.okay", n1 > 1 ? _("Really remove all selected user IDs? (y/N) ") : _("Really remove this user ID? (y/N) "))) { menu_deluid (keyblock); redisplay = 1; modified = 1; } } break; case cmdDELSIG: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (menu_delsig (ctrl, keyblock)) { /* No redisplay here, because it may scroll away some * of the status output of this command. */ modified = 1; } } break; case cmdADDKEY: if (!generate_subkeypair (ctrl, keyblock, NULL, NULL, NULL)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } break; #ifdef ENABLE_CARD_SUPPORT case cmdADDCARDKEY: if (!card_generate_subkey (ctrl, keyblock)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } break; case cmdKEYTOCARD: { KBNODE node = NULL; switch (count_selected_keys (keyblock)) { case 0: if (cpr_get_answer_is_yes ("keyedit.keytocard.use_primary", /* TRANSLATORS: Please take care: This is about moving the key and not about removing it. */ _("Really move the primary key? (y/N) "))) node = keyblock; break; case 1: for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && node->flag & NODFLG_SELKEY) break; } break; default: tty_printf (_("You must select exactly one key.\n")); break; } if (node) { PKT_public_key *xxpk = node->pkt->pkt.public_key; if (card_store_subkey (node, xxpk ? xxpk->pubkey_usage : 0)) { redisplay = 1; sec_shadowing = 1; } } } break; case cmdBKUPTOCARD: { /* Ask for a filename, check whether this is really a backup key as generated by the card generation, parse that key and store it on card. */ KBNODE node; char *fname; PACKET *pkt; IOBUF a; struct parse_packet_ctx_s parsectx; if (!*arg_string) { tty_printf (_("Command expects a filename argument\n")); break; } if (*arg_string == DIRSEP_C) fname = xstrdup (arg_string); else if (*arg_string == '~') fname = make_filename (arg_string, NULL); else fname = make_filename (gnupg_homedir (), arg_string, NULL); /* Open that file. */ a = iobuf_open (fname); if (a && is_secured_file (iobuf_get_fd (a))) { iobuf_close (a); a = NULL; gpg_err_set_errno (EPERM); } if (!a) { tty_printf (_("Can't open '%s': %s\n"), fname, strerror (errno)); xfree (fname); break; } /* Parse and check that file. */ pkt = xmalloc (sizeof *pkt); init_packet (pkt); init_parse_packet (&parsectx, a); err = parse_packet (&parsectx, pkt); deinit_parse_packet (&parsectx); iobuf_close (a); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char *) fname); if (!err && pkt->pkttype != PKT_SECRET_KEY && pkt->pkttype != PKT_SECRET_SUBKEY) err = GPG_ERR_NO_SECKEY; if (err) { tty_printf (_("Error reading backup key from '%s': %s\n"), fname, gpg_strerror (err)); xfree (fname); free_packet (pkt, NULL); xfree (pkt); break; } xfree (fname); node = new_kbnode (pkt); /* Transfer it to gpg-agent which handles secret keys. */ err = transfer_secret_keys (ctrl, NULL, node, 1, 1, 0); /* Treat the pkt as a public key. */ pkt->pkttype = PKT_PUBLIC_KEY; /* Ask gpg-agent to store the secret key to card. */ if (card_store_subkey (node, 0)) { redisplay = 1; sec_shadowing = 1; } release_kbnode (node); } break; #endif /* ENABLE_CARD_SUPPORT */ case cmdDELKEY: { int n1; if (!(n1 = count_selected_keys (keyblock))) { tty_printf (_("You must select at least one key.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "key"); } else if (!cpr_get_answer_is_yes ("keyedit.remove.subkey.okay", n1 > 1 ? _("Do you really want to delete the " "selected keys? (y/N) ") : _("Do you really want to delete this key? (y/N) "))) ; else { menu_delkey (keyblock); redisplay = 1; modified = 1; } } break; case cmdADDREVOKER: { int sensitive = 0; if (ascii_strcasecmp (arg_string, "sensitive") == 0) sensitive = 1; if (menu_addrevoker (ctrl, keyblock, sensitive)) { redisplay = 1; modified = 1; merge_keys_and_selfsig (ctrl, keyblock); } } break; case cmdREVUID: { int n1; if (!(n1 = count_selected_uids (keyblock))) { tty_printf (_("You must select at least one user ID.\n")); if (!opt.expert) tty_printf (_("(Use the '%s' command.)\n"), "uid"); } else if (cpr_get_answer_is_yes ("keyedit.revoke.uid.okay", n1 > 1 ? _("Really revoke all selected user IDs? (y/N) ") : _("Really revoke this user ID? (y/N) "))) { if (menu_revuid (ctrl, keyblock)) { modified = 1; redisplay = 1; } } } break; case cmdREVKEY: { int n1; if (!(n1 = count_selected_keys (keyblock))) { if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay", _("Do you really want to revoke" " the entire key? (y/N) "))) { if (menu_revkey (ctrl, keyblock)) modified = 1; redisplay = 1; } } else if (cpr_get_answer_is_yes ("keyedit.revoke.subkey.okay", n1 > 1 ? _("Do you really want to revoke" " the selected subkeys? (y/N) ") : _("Do you really want to revoke" " this subkey? (y/N) "))) { if (menu_revsubkey (ctrl, keyblock)) modified = 1; redisplay = 1; } if (modified) merge_keys_and_selfsig (ctrl, keyblock); } break; case cmdEXPIRE: if (gpg_err_code (menu_expire (ctrl, keyblock, 0, 0)) == GPG_ERR_TRUE) { merge_keys_and_selfsig (ctrl, keyblock); run_subkey_warnings = 1; modified = 1; redisplay = 1; } break; case cmdCHANGEUSAGE: if (menu_changeusage (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdBACKSIGN: if (menu_backsign (ctrl, keyblock)) { modified = 1; redisplay = 1; } break; case cmdPRIMARY: if (menu_set_primary_uid (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdPASSWD: change_passphrase (ctrl, keyblock); break; #ifndef NO_TRUST_MODELS case cmdTRUST: if (opt.trust_model == TM_EXTERNAL) { tty_printf (_("Owner trust may not be set while " "using a user provided trust database\n")); break; } show_key_with_all_names (ctrl, NULL, keyblock, 0, 0, 0, 1, 0, 0); tty_printf ("\n"); if (edit_ownertrust (ctrl, find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt. public_key, 1)) { redisplay = 1; /* No real need to set update_trust here as edit_ownertrust() calls revalidation_mark() anyway. */ update_trust = 1; } break; #endif /*!NO_TRUST_MODELS*/ case cmdPREF: { int count = count_selected_uids (keyblock); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key, count ? NODFLG_SELUID : 0, 1); } break; case cmdSHOWPREF: { int count = count_selected_uids (keyblock); log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); show_names (ctrl, NULL, keyblock, keyblock->pkt->pkt.public_key, count ? NODFLG_SELUID : 0, 2); } break; case cmdSETPREF: { PKT_user_id *tempuid; keygen_set_std_prefs (!*arg_string ? "default" : arg_string, 0); tempuid = keygen_get_std_prefs (); tty_printf (_("Set preference list to:\n")); show_prefs (tempuid, NULL, 1); free_user_id (tempuid); if (cpr_get_answer_is_yes ("keyedit.setpref.okay", count_selected_uids (keyblock) ? _("Really update the preferences" " for the selected user IDs? (y/N) ") : _("Really update the preferences? (y/N) "))) { if (menu_set_preferences (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } } } break; case cmdPREFKS: if (menu_set_keyserver_url (ctrl, *arg_string ? arg_string : NULL, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdNOTATION: if (menu_set_notation (ctrl, *arg_string ? arg_string : NULL, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); modified = 1; redisplay = 1; } break; case cmdNOP: break; case cmdREVSIG: if (menu_revsig (ctrl, keyblock)) { redisplay = 1; modified = 1; } break; #ifndef NO_TRUST_MODELS case cmdENABLEKEY: case cmdDISABLEKEY: if (enable_disable_key (ctrl, keyblock, cmd == cmdDISABLEKEY)) { redisplay = 1; modified = 1; } break; #endif /*!NO_TRUST_MODELS*/ case cmdSHOWPHOTO: menu_showphoto (ctrl, keyblock); break; case cmdCLEAN: if (menu_clean (ctrl, keyblock, 0)) redisplay = modified = 1; break; case cmdMINIMIZE: if (menu_clean (ctrl, keyblock, 1)) redisplay = modified = 1; break; case cmdQUIT: if (have_commands) goto leave; if (!modified && !sec_shadowing) goto leave; if (!cpr_get_answer_is_yes ("keyedit.save.okay", _("Save changes? (y/N) "))) { if (cpr_enabled () || cpr_get_answer_is_yes ("keyedit.cancel.okay", _("Quit without saving? (y/N) "))) goto leave; break; } /* fall through */ case cmdSAVE: if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); break; } } if (sec_shadowing) { err = agent_scd_learn (NULL, 1); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); break; } } if (!modified && !sec_shadowing) tty_printf (_("Key not changed so no update needed.\n")); if (update_trust) { revalidation_mark (ctrl); update_trust = 0; } goto leave; case cmdINVCMD: default: tty_printf ("\n"); tty_printf (_("Invalid command (try \"help\")\n")); break; } } /* End of the main command loop. */ leave: release_kbnode (keyblock); keydb_release (kdbhd); xfree (answer); } /* Change the passphrase of the secret key identified by USERNAME. */ void keyedit_passwd (ctrl_t ctrl, const char *username) { gpg_error_t err; PKT_public_key *pk; kbnode_t keyblock = NULL; pk = xtrycalloc (1, sizeof *pk); if (!pk) { err = gpg_error_from_syserror (); goto leave; } err = getkey_byname (ctrl, NULL, pk, username, 1, &keyblock); if (err) goto leave; err = change_passphrase (ctrl, keyblock); leave: release_kbnode (keyblock); free_public_key (pk); if (err) { log_info ("error changing the passphrase for '%s': %s\n", username, gpg_strerror (err)); write_status_error ("keyedit.passwd", err); } else write_status_text (STATUS_SUCCESS, "keyedit.passwd"); } /* Helper for quick commands to find the keyblock for USERNAME. * Returns on success the key database handle at R_KDBHD and the * keyblock at R_KEYBLOCK. */ static gpg_error_t quick_find_keyblock (ctrl_t ctrl, const char *username, KEYDB_HANDLE *r_kdbhd, kbnode_t *r_keyblock) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; KEYDB_SEARCH_DESC desc; kbnode_t node; *r_kdbhd = NULL; *r_keyblock = NULL; /* Search the key; we don't want the whole getkey stuff here. */ - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) { /* Note that keydb_new has already used log_error. */ err = gpg_error_from_syserror (); goto leave; } err = classify_user_id (username, &desc, 1); if (!err) err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) { err = keydb_get_keyblock (kdbhd, &keyblock); if (err) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (err)); goto leave; } /* Now with the keyblock retrieved, search again to detect an ambiguous specification. We need to save the found state so that we can do an update later. */ keydb_push_found_state (kdbhd); err = keydb_search (kdbhd, &desc, 1, NULL); if (!err) err = gpg_error (GPG_ERR_AMBIGUOUS_NAME); else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = 0; keydb_pop_found_state (kdbhd); if (!err) { /* We require the secret primary key to set the primary UID. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); log_assert (node); err = agent_probe_secret_key (ctrl, node->pkt->pkt.public_key); } } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) err = gpg_error (GPG_ERR_NO_PUBKEY); if (err) { log_error (_("key \"%s\" not found: %s\n"), username, gpg_strerror (err)); goto leave; } fix_keyblock (ctrl, &keyblock); merge_keys_and_selfsig (ctrl, keyblock); *r_keyblock = keyblock; keyblock = NULL; *r_kdbhd = kdbhd; kdbhd = NULL; leave: release_kbnode (keyblock); keydb_release (kdbhd); return err; } /* Unattended adding of a new keyid. USERNAME specifies the key. NEWUID is the new user id to add to the key. */ void keyedit_quick_adduid (ctrl_t ctrl, const char *username, const char *newuid) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; char *uidstring = NULL; uidstring = xstrdup (newuid); trim_spaces (uidstring); if (!*uidstring) { log_error ("%s\n", gpg_strerror (GPG_ERR_INV_USER_ID)); goto leave; } #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* Search the key; we don't want the whole getkey stuff here. */ err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); if (err) goto leave; if (menu_adduid (ctrl, keyblock, 0, NULL, uidstring)) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } if (update_trust) revalidation_mark (ctrl); } leave: xfree (uidstring); release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended revocation of a keyid. USERNAME specifies the key. UIDTOREV is the user id revoke from the key. */ void keyedit_quick_revuid (ctrl_t ctrl, const char *username, const char *uidtorev) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; kbnode_t node; int modified = 0; size_t revlen; size_t valid_uids; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* Search the key; we don't want the whole getkey stuff here. */ err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); if (err) goto leave; /* Too make sure that we do not revoke the last valid UID, we first count how many valid UIDs there are. */ valid_uids = 0; for (node = keyblock; node; node = node->next) valid_uids += (node->pkt->pkttype == PKT_USER_ID && !node->pkt->pkt.user_id->flags.revoked && !node->pkt->pkt.user_id->flags.expired); /* Find the right UID. */ revlen = strlen (uidtorev); for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && revlen == node->pkt->pkt.user_id->len && !memcmp (node->pkt->pkt.user_id->name, uidtorev, revlen)) { struct revocation_reason_info *reason; /* Make sure that we do not revoke the last valid UID. */ if (valid_uids == 1 && ! node->pkt->pkt.user_id->flags.revoked && ! node->pkt->pkt.user_id->flags.expired) { log_error (_("cannot revoke the last valid user ID.\n")); err = gpg_error (GPG_ERR_INV_USER_ID); goto leave; } reason = get_default_uid_revocation_reason (); err = core_revuid (ctrl, keyblock, node, reason, &modified); release_revocation_reason_info (reason); if (err) goto leave; err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } revalidation_mark (ctrl); goto leave; } } err = gpg_error (GPG_ERR_NO_USER_ID); leave: if (err) { log_error (_("revoking the user ID failed: %s\n"), gpg_strerror (err)); write_status_error ("keyedit.revoke.uid", err); } release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended setting of the primary uid. USERNAME specifies the key. PRIMARYUID is the user id which shall be primary. */ void keyedit_quick_set_primary (ctrl_t ctrl, const char *username, const char *primaryuid) { gpg_error_t err; KEYDB_HANDLE kdbhd = NULL; kbnode_t keyblock = NULL; kbnode_t node; size_t primaryuidlen; int any; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif err = quick_find_keyblock (ctrl, username, &kdbhd, &keyblock); if (err) goto leave; /* Find and mark the UID - we mark only the first valid one. */ primaryuidlen = strlen (primaryuid); any = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && !any && !node->pkt->pkt.user_id->flags.revoked && !node->pkt->pkt.user_id->flags.expired && primaryuidlen == node->pkt->pkt.user_id->len && !memcmp (node->pkt->pkt.user_id->name, primaryuid, primaryuidlen)) { node->flag |= NODFLG_SELUID; any = 1; } else node->flag &= ~NODFLG_SELUID; } if (!any) err = gpg_error (GPG_ERR_NO_USER_ID); else if (menu_set_primary_uid (ctrl, keyblock)) { merge_keys_and_selfsig (ctrl, keyblock); err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } revalidation_mark (ctrl); } else err = gpg_error (GPG_ERR_GENERAL); if (err) log_error (_("setting the primary user ID failed: %s\n"), gpg_strerror (err)); leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Find a keyblock by fingerprint because only this uniquely * identifies a key and may thus be used to select a key for * unattended subkey creation os key signing. */ static gpg_error_t find_by_primary_fpr (ctrl_t ctrl, const char *fpr, kbnode_t *r_keyblock, KEYDB_HANDLE *r_kdbhd) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; KEYDB_SEARCH_DESC desc; byte fprbin[MAX_FINGERPRINT_LEN]; size_t fprlen; *r_keyblock = NULL; *r_kdbhd = NULL; if (classify_user_id (fpr, &desc, 1) || desc.mode != KEYDB_SEARCH_MODE_FPR) { log_error (_("\"%s\" is not a fingerprint\n"), fpr); err = gpg_error (GPG_ERR_INV_NAME); goto leave; } err = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, NULL, NULL, fpr, &keyblock, &kdbhd, 1); if (err) { log_error (_("key \"%s\" not found: %s\n"), fpr, gpg_strerror (err)); goto leave; } /* Check that the primary fingerprint has been given. */ fingerprint_from_pk (keyblock->pkt->pkt.public_key, fprbin, &fprlen); if (desc.mode == KEYDB_SEARCH_MODE_FPR && fprlen == desc.fprlen && !memcmp (fprbin, desc.u.fpr, fprlen)) ; else { log_error (_("\"%s\" is not the primary fingerprint\n"), fpr); err = gpg_error (GPG_ERR_INV_NAME); goto leave; } *r_keyblock = keyblock; keyblock = NULL; *r_kdbhd = kdbhd; kdbhd = NULL; err = 0; leave: release_kbnode (keyblock); keydb_release (kdbhd); return err; } /* Unattended key signing function. If the key specifified by FPR is available and FPR is the primary fingerprint all user ids of the key are signed using the default signing key. If UIDS is an empty list all usable UIDs are signed, if it is not empty, only those user ids matching one of the entries of the list are signed. With LOCAL being true the signatures are marked as non-exportable. */ void keyedit_quick_sign (ctrl_t ctrl, const char *fpr, strlist_t uids, strlist_t locusr, int local) { gpg_error_t err; kbnode_t keyblock = NULL; KEYDB_HANDLE kdbhd = NULL; int modified = 0; PKT_public_key *pk; kbnode_t node; strlist_t sl; int any; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a key and may thus be used to select a key for unattended key signing. */ if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) goto leave; if (fix_keyblock (ctrl, &keyblock)) modified++; /* Give some info in verbose. */ if (opt.verbose) { show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 1/*with_revoker*/, 1/*with_fingerprint*/, 0, 0, 1); es_fflush (es_stdout); } pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), _(" Unable to sign.\n")); goto leave; } /* Set the flags according to the UIDS list. Fixme: We may want to use classify_user_id along with dedicated compare functions so that we match the same way as in the key lookup. */ any = 0; menu_select_uid (keyblock, 0); /* Better clear the flags first. */ for (sl=uids; sl; sl = sl->next) { const char *name = sl->d; int count = 0; sl->flags &= ~(1|2); /* Clear flags used for error reporting. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; if (uid->attrib_data) ; else if (*name == '=' && strlen (name+1) == uid->len && !memcmp (uid->name, name + 1, uid->len)) { /* Exact match - we don't do a check for ambiguity * in this case. */ node->flag |= NODFLG_SELUID; if (any != -1) { sl->flags |= 1; /* Report as found. */ any = 1; } } else if (ascii_memistr (uid->name, uid->len, *name == '*'? name+1:name)) { node->flag |= NODFLG_SELUID; if (any != -1) { sl->flags |= 1; /* Report as found. */ any = 1; } count++; } } } if (count > 1) { any = -1; /* Force failure at end. */ sl->flags |= 2; /* Report as ambiguous. */ } } /* Check whether all given user ids were found. */ for (sl=uids; sl; sl = sl->next) if (!(sl->flags & 1)) any = -1; /* That user id was not found. */ /* Print an error if there was a problem with the user ids. */ if (uids && any < 1) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); es_fflush (es_stdout); for (sl=uids; sl; sl = sl->next) { if ((sl->flags & 2)) log_info (_("Invalid user ID '%s': %s\n"), sl->d, gpg_strerror (GPG_ERR_AMBIGUOUS_NAME)); else if (!(sl->flags & 1)) log_info (_("Invalid user ID '%s': %s\n"), sl->d, gpg_strerror (GPG_ERR_NOT_FOUND)); } log_error ("%s %s", _("No matching user IDs."), _("Nothing to sign.\n")); goto leave; } /* Sign. */ sign_uids (ctrl, es_stdout, keyblock, locusr, &modified, local, 0, 0, 0, 1); es_fflush (es_stdout); if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } } else log_info (_("Key not changed so no update needed.\n")); if (update_trust) revalidation_mark (ctrl); leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended subkey creation function. * */ void keyedit_quick_addkey (ctrl_t ctrl, const char *fpr, const char *algostr, const char *usagestr, const char *expirestr) { gpg_error_t err; kbnode_t keyblock; KEYDB_HANDLE kdbhd; int modified = 0; PKT_public_key *pk; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a * key and may thus be used to select a key for unattended subkey * creation. */ if (find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd)) goto leave; if (fix_keyblock (ctrl, &keyblock)) modified++; pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), "\n"); goto leave; } /* Create the subkey. Note that the called function already prints * an error message. */ if (!generate_subkeypair (ctrl, keyblock, algostr, usagestr, expirestr)) modified = 1; es_fflush (es_stdout); /* Store. */ if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } } else log_info (_("Key not changed so no update needed.\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); } /* Unattended expiration setting function for the main key. If * SUBKEYFPRS is not NULL and SUBKEYSFPRS[0] is neither NULL, it is * expected to be an array of fingerprints for subkeys to change. It * may also be an array which just one item "*" to indicate that all * keys shall be set to that expiration date. */ void keyedit_quick_set_expire (ctrl_t ctrl, const char *fpr, const char *expirestr, char **subkeyfprs) { gpg_error_t err; kbnode_t keyblock, node; KEYDB_HANDLE kdbhd; int modified = 0; PKT_public_key *pk; u32 expire; int primary_only = 0; int idx; #ifdef HAVE_W32_SYSTEM /* See keyedit_menu for why we need this. */ check_trustdb_stale (ctrl); #endif /* We require a fingerprint because only this uniquely identifies a * key and may thus be used to select a key for unattended * expiration setting. */ err = find_by_primary_fpr (ctrl, fpr, &keyblock, &kdbhd); if (err) goto leave; if (fix_keyblock (ctrl, &keyblock)) modified++; pk = keyblock->pkt->pkt.public_key; if (pk->flags.revoked) { if (!opt.verbose) show_key_with_all_names (ctrl, es_stdout, keyblock, 0, 0, 0, 0, 0, 1); log_error ("%s%s", _("Key is revoked."), "\n"); err = gpg_error (GPG_ERR_CERT_REVOKED); goto leave; } expire = parse_expire_string (expirestr); if (expire == (u32)-1 ) { log_error (_("'%s' is not a valid expiration time\n"), expirestr); err = gpg_error (GPG_ERR_INV_VALUE); goto leave; } if (expire) expire += make_timestamp (); /* Check whether a subkey's expiration time shall be changed or the * expiration time of all keys. */ if (!subkeyfprs || !subkeyfprs[0]) primary_only = 1; else if ( !strcmp (subkeyfprs[0], "*") && !subkeyfprs[1]) { /* Change all subkeys keys which have not been revoked and are * not yet expired. */ merge_keys_and_selfsig (ctrl, keyblock); for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (pk = node->pkt->pkt.public_key) && !pk->flags.revoked && !pk->has_expired) node->flag |= NODFLG_SELKEY; } } else { /* Change specified subkeys. */ KEYDB_SEARCH_DESC desc; byte fprbin[MAX_FINGERPRINT_LEN]; size_t fprlen; err = 0; merge_keys_and_selfsig (ctrl, keyblock); for (idx=0; subkeyfprs[idx]; idx++) { int any = 0; /* Parse the fingerprint. */ if (classify_user_id (subkeyfprs[idx], &desc, 1) || desc.mode != KEYDB_SEARCH_MODE_FPR) { log_error (_("\"%s\" is not a proper fingerprint\n"), subkeyfprs[idx] ); if (!err) err = gpg_error (GPG_ERR_INV_NAME); continue; } /* Set the flag for the matching non revoked subkey. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (pk = node->pkt->pkt.public_key) && !pk->flags.revoked ) { fingerprint_from_pk (pk, fprbin, &fprlen); if (fprlen == 20 && !memcmp (fprbin, desc.u.fpr, 20)) { node->flag |= NODFLG_SELKEY; any = 1; } } } if (!any) { log_error (_("subkey \"%s\" not found\n"), subkeyfprs[idx]); if (!err) err = gpg_error (GPG_ERR_NOT_FOUND); } } if (err) goto leave; } /* Set the new expiration date. */ err = menu_expire (ctrl, keyblock, primary_only? 1 : 2, expire); if (gpg_err_code (err) == GPG_ERR_TRUE) modified = 1; else if (err) goto leave; es_fflush (es_stdout); /* Store. */ if (modified) { err = keydb_update_keyblock (ctrl, kdbhd, keyblock); if (err) { log_error (_("update failed: %s\n"), gpg_strerror (err)); goto leave; } if (update_trust) revalidation_mark (ctrl); } else log_info (_("Key not changed so no update needed.\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); if (err) write_status_error ("set_expire", err); } static void tty_print_notations (int indent, PKT_signature * sig) { int first = 1; struct notation *notation, *nd; if (indent < 0) { first = 0; indent = -indent; } notation = sig_to_notation (sig); for (nd = notation; nd; nd = nd->next) { if (!first) tty_printf ("%*s", indent, ""); else first = 0; tty_print_utf8_string (nd->name, strlen (nd->name)); tty_printf ("="); tty_print_utf8_string (nd->value, strlen (nd->value)); tty_printf ("\n"); } free_notation (notation); } /* * Show preferences of a public keyblock. */ static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose) { const prefitem_t fake = { 0, 0 }; const prefitem_t *prefs; int i; if (!uid) return; if (uid->prefs) prefs = uid->prefs; else if (verbose) prefs = &fake; else return; if (verbose) { int any, des_seen = 0, sha1_seen = 0, uncomp_seen = 0; tty_printf (" "); tty_printf (_("Cipher: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_SYM) { if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (!openpgp_cipher_test_algo (prefs[i].value) && prefs[i].value < 100) tty_printf ("%s", openpgp_cipher_algo_name (prefs[i].value)); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == CIPHER_ALGO_3DES) des_seen = 1; } } if (!des_seen) { if (any) tty_printf (", "); tty_printf ("%s", openpgp_cipher_algo_name (CIPHER_ALGO_3DES)); } tty_printf ("\n "); tty_printf (_("AEAD: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_AEAD) { if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (!openpgp_aead_test_algo (prefs[i].value) && prefs[i].value < 100) tty_printf ("%s", openpgp_aead_algo_name (prefs[i].value)); else tty_printf ("[%d]", prefs[i].value); } } tty_printf ("\n "); tty_printf (_("Digest: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_HASH) { if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (!gcry_md_test_algo (prefs[i].value) && prefs[i].value < 100) tty_printf ("%s", gcry_md_algo_name (prefs[i].value)); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == DIGEST_ALGO_SHA1) sha1_seen = 1; } } if (!sha1_seen) { if (any) tty_printf (", "); tty_printf ("%s", gcry_md_algo_name (DIGEST_ALGO_SHA1)); } tty_printf ("\n "); tty_printf (_("Compression: ")); for (i = any = 0; prefs[i].type; i++) { if (prefs[i].type == PREFTYPE_ZIP) { const char *s = compress_algo_to_string (prefs[i].value); if (any) tty_printf (", "); any = 1; /* We don't want to display strings for experimental algos */ if (s && prefs[i].value < 100) tty_printf ("%s", s); else tty_printf ("[%d]", prefs[i].value); if (prefs[i].value == COMPRESS_ALGO_NONE) uncomp_seen = 1; } } if (!uncomp_seen) { if (any) tty_printf (", "); else { tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_ZIP)); tty_printf (", "); } tty_printf ("%s", compress_algo_to_string (COMPRESS_ALGO_NONE)); } if (uid->flags.mdc || uid->flags.aead || !uid->flags.ks_modify) { tty_printf ("\n "); tty_printf (_("Features: ")); any = 0; if (uid->flags.mdc) { tty_printf ("MDC"); any = 1; } if (!uid->flags.aead) { if (any) tty_printf (", "); tty_printf ("AEAD"); } if (!uid->flags.ks_modify) { if (any) tty_printf (", "); tty_printf (_("Keyserver no-modify")); } } tty_printf ("\n"); if (selfsig) { const byte *pref_ks; size_t pref_ks_len; pref_ks = parse_sig_subpkt (selfsig->hashed, SIGSUBPKT_PREF_KS, &pref_ks_len); if (pref_ks && pref_ks_len) { tty_printf (" "); tty_printf (_("Preferred keyserver: ")); tty_print_utf8_string (pref_ks, pref_ks_len); tty_printf ("\n"); } if (selfsig->flags.notation) { tty_printf (" "); tty_printf (_("Notations: ")); tty_print_notations (5 + strlen (_("Notations: ")), selfsig); } } } else { tty_printf (" "); for (i = 0; prefs[i].type; i++) { tty_printf (" %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' : prefs[i].type == PREFTYPE_AEAD ? 'A' : prefs[i].type == PREFTYPE_HASH ? 'H' : prefs[i].type == PREFTYPE_ZIP ? 'Z' : '?', prefs[i].value); } if (uid->flags.mdc) tty_printf (" [mdc]"); if (uid->flags.aead) tty_printf (" [aead]"); if (!uid->flags.ks_modify) tty_printf (" [no-ks-modify]"); tty_printf ("\n"); } } /* This is the version of show_key_with_all_names used when opt.with_colons is used. It prints all available data in a easy to parse format and does not translate utf8 */ static void show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock) { KBNODE node; int i, j, ulti_hack = 0; byte pk_version = 0; PKT_public_key *primary = NULL; int have_seckey; if (!fp) fp = es_stdout; /* the keys */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)) { PKT_public_key *pk = node->pkt->pkt.public_key; u32 keyid[2]; if (node->pkt->pkttype == PKT_PUBLIC_KEY) { pk_version = pk->version; primary = pk; } keyid_from_pk (pk, keyid); have_seckey = !agent_probe_secret_key (ctrl, pk); if (node->pkt->pkttype == PKT_PUBLIC_KEY) es_fputs (have_seckey? "sec:" : "pub:", fp); else es_fputs (have_seckey? "ssb:" : "sub:", fp); if (!pk->flags.valid) es_putc ('i', fp); else if (pk->flags.revoked) es_putc ('r', fp); else if (pk->has_expired) es_putc ('e', fp); else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks)) { int trust = get_validity_info (ctrl, keyblock, pk, NULL); if (trust == 'u') ulti_hack = 1; es_putc (trust, fp); } es_fprintf (fp, ":%u:%d:%08lX%08lX:%lu:%lu::", nbits_from_pk (pk), pk->pubkey_algo, (ulong) keyid[0], (ulong) keyid[1], (ulong) pk->timestamp, (ulong) pk->expiredate); if (node->pkt->pkttype == PKT_PUBLIC_KEY && !(opt.fast_list_mode || opt.no_expensive_trust_checks)) es_putc (get_ownertrust_info (ctrl, pk, 0), fp); es_putc (':', fp); es_putc (':', fp); es_putc (':', fp); /* Print capabilities. */ if ((pk->pubkey_usage & PUBKEY_USAGE_ENC)) es_putc ('e', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_SIG)) es_putc ('s', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_CERT)) es_putc ('c', fp); if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) es_putc ('a', fp); es_putc ('\n', fp); print_fingerprint (ctrl, fp, pk, 0); print_revokers (fp, pk); } } /* the user ids */ i = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; if (uid->attrib_data) es_fputs ("uat:", fp); else es_fputs ("uid:", fp); if (uid->flags.revoked) es_fputs ("r::::::::", fp); else if (uid->flags.expired) es_fputs ("e::::::::", fp); else if (opt.fast_list_mode || opt.no_expensive_trust_checks) es_fputs ("::::::::", fp); else { int uid_validity; if (primary && !ulti_hack) uid_validity = get_validity_info (ctrl, keyblock, primary, uid); else uid_validity = 'u'; es_fprintf (fp, "%c::::::::", uid_validity); } if (uid->attrib_data) es_fprintf (fp, "%u %lu", uid->numattribs, uid->attrib_len); else es_write_sanitized (fp, uid->name, uid->len, ":", NULL); es_putc (':', fp); /* signature class */ es_putc (':', fp); /* capabilities */ es_putc (':', fp); /* preferences */ if (pk_version > 3 || uid->selfsigversion > 3) { const prefitem_t *prefs = uid->prefs; for (j = 0; prefs && prefs[j].type; j++) { if (j) es_putc (' ', fp); es_fprintf (fp, "%c%d", prefs[j].type == PREFTYPE_SYM ? 'S' : prefs[j].type == PREFTYPE_HASH ? 'H' : prefs[j].type == PREFTYPE_ZIP ? 'Z' : '?', prefs[j].value); } if (uid->flags.mdc) es_fputs (",mdc", fp); if (uid->flags.aead) es_fputs (",aead", fp); if (!uid->flags.ks_modify) es_fputs (",no-ks-modify", fp); } es_putc (':', fp); /* flags */ es_fprintf (fp, "%d,", i); if (uid->flags.primary) es_putc ('p', fp); if (uid->flags.revoked) es_putc ('r', fp); if (uid->flags.expired) es_putc ('e', fp); if ((node->flag & NODFLG_SELUID)) es_putc ('s', fp); if ((node->flag & NODFLG_MARK_A)) es_putc ('m', fp); es_putc (':', fp); if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP) { #ifdef USE_TOFU enum tofu_policy policy; if (! tofu_get_policy (ctrl, primary, uid, &policy) && policy != TOFU_POLICY_NONE) es_fprintf (fp, "%s", tofu_policy_str (policy)); #endif /*USE_TOFU*/ } es_putc (':', fp); es_putc ('\n', fp); } } } static void show_names (ctrl_t ctrl, estream_t fp, kbnode_t keyblock, PKT_public_key * pk, unsigned int flag, int with_prefs) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && !is_deleted_kbnode (node)) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; if (!flag || (flag && (node->flag & flag))) { if (!(flag & NODFLG_MARK_A) && pk) tty_fprintf (fp, "%s ", uid_trust_string_fixed (ctrl, pk, uid)); if (flag & NODFLG_MARK_A) tty_fprintf (fp, " "); else if (node->flag & NODFLG_SELUID) tty_fprintf (fp, "(%d)* ", i); else if (uid->flags.primary) tty_fprintf (fp, "(%d). ", i); else tty_fprintf (fp, "(%d) ", i); tty_print_utf8_string2 (fp, uid->name, uid->len, 0); tty_fprintf (fp, "\n"); if (with_prefs && pk) { if (pk->version > 3 || uid->selfsigversion > 3) { PKT_signature *selfsig = NULL; KBNODE signode; for (signode = node->next; signode && signode->pkt->pkttype == PKT_SIGNATURE; signode = signode->next) { if (signode->pkt->pkt.signature-> flags.chosen_selfsig) { selfsig = signode->pkt->pkt.signature; break; } } show_prefs (uid, selfsig, with_prefs == 2); } else tty_fprintf (fp, _("There are no preferences on a" " PGP 2.x-style user ID.\n")); } } } } } /* * Display the key a the user ids, if only_marked is true, do only so * for user ids with mark A flag set and do not display the index * number. If FP is not NULL print to the given stream and not to the * tty (ignored in with-colons mode). */ static void show_key_with_all_names (ctrl_t ctrl, estream_t fp, KBNODE keyblock, int only_marked, int with_revoker, int with_fpr, int with_subkeys, int with_prefs, int nowarn) { gpg_error_t err; kbnode_t node; int i; int do_warn = 0; int have_seckey = 0; char *serialno = NULL; PKT_public_key *primary = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; if (opt.with_colons) { show_key_with_all_names_colon (ctrl, fp, keyblock); return; } /* the keys */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY && !is_deleted_kbnode (node))) { PKT_public_key *pk = node->pkt->pkt.public_key; const char *otrust = "err"; const char *trust = "err"; if (node->pkt->pkttype == PKT_PUBLIC_KEY) { /* do it here, so that debug messages don't clutter the * output */ static int did_warn = 0; trust = get_validity_string (ctrl, pk, NULL); otrust = get_ownertrust_string (ctrl, pk, 0); /* Show a warning once */ if (!did_warn && (get_validity (ctrl, keyblock, pk, NULL, NULL, 0) & TRUST_FLAG_PENDING_CHECK)) { did_warn = 1; do_warn = 1; } primary = pk; } if (pk->flags.revoked) { char *user = get_user_id_string_native (ctrl, pk->revoked.keyid); tty_fprintf (fp, _("The following key was revoked on" " %s by %s key %s\n"), revokestr_from_pk (pk), gcry_pk_algo_name (pk->revoked.algo), user); xfree (user); } if (with_revoker) { if (!pk->revkey && pk->numrevkeys) BUG (); else for (i = 0; i < pk->numrevkeys; i++) { u32 r_keyid[2]; char *user; const char *algo; algo = gcry_pk_algo_name (pk->revkey[i].algid); keyid_from_fingerprint (ctrl, pk->revkey[i].fpr, pk->revkey[i].fprlen, r_keyid); user = get_user_id_string_native (ctrl, r_keyid); tty_fprintf (fp, _("This key may be revoked by %s key %s"), algo ? algo : "?", user); if (pk->revkey[i].class & 0x40) { tty_fprintf (fp, " "); tty_fprintf (fp, _("(sensitive)")); } tty_fprintf (fp, "\n"); xfree (user); } } keyid_from_pk (pk, NULL); xfree (serialno); serialno = NULL; { char *hexgrip; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { log_error ("error computing a keygrip: %s\n", gpg_strerror (err)); have_seckey = 0; } else have_seckey = !agent_get_keyinfo (ctrl, hexgrip, &serialno, NULL); xfree (hexgrip); } tty_fprintf (fp, "%s%c %s/%s", node->pkt->pkttype == PKT_PUBLIC_KEY && have_seckey? "sec" : node->pkt->pkttype == PKT_PUBLIC_KEY ? "pub" : have_seckey ? "ssb" : "sub", (node->flag & NODFLG_SELKEY) ? '*' : ' ', pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (pk->keyid)); if (opt.legacy_list_mode) tty_fprintf (fp, " "); else tty_fprintf (fp, "\n "); tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk)); tty_fprintf (fp, " "); if (pk->flags.revoked) tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk)); else if (pk->has_expired) tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk)); else tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, " "); tty_fprintf (fp, _("usage: %s"), usagestr_from_pk (pk, 1)); tty_fprintf (fp, "\n"); if (serialno) { /* The agent told us that a secret key is available and that it has been stored on a card. */ tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "", _("card-no: ")); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ tty_fprintf (fp, "%.*s %.*s\n", 4, serialno+16, 8, serialno+20); } else tty_fprintf (fp, "%s\n", serialno); } else if (pk->seckey_info && pk->seckey_info->is_protected && pk->seckey_info->s2k.mode == 1002) { /* FIXME: Check whether this code path is still used. */ tty_fprintf (fp, "%*s%s", opt.legacy_list_mode? 21:5, "", _("card-no: ")); if (pk->seckey_info->ivlen == 16 && !memcmp (pk->seckey_info->iv, "\xD2\x76\x00\x01\x24\x01", 6)) { /* This is an OpenPGP card. */ for (i = 8; i < 14; i++) { if (i == 10) tty_fprintf (fp, " "); tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]); } } else { /* Unknown card: Print all. */ for (i = 0; i < pk->seckey_info->ivlen; i++) tty_fprintf (fp, "%02X", pk->seckey_info->iv[i]); } tty_fprintf (fp, "\n"); } if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) { if (opt.trust_model != TM_ALWAYS) { tty_fprintf (fp, "%*s", opt.legacy_list_mode? ((int) keystrlen () + 13):5, ""); /* Ownertrust is only meaningful for the PGP or classic trust models, or PGP combined with TOFU */ if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP) { int width = 14 - strlen (otrust); if (width <= 0) width = 1; tty_fprintf (fp, _("trust: %s"), otrust); tty_fprintf (fp, "%*s", width, ""); } tty_fprintf (fp, _("validity: %s"), trust); tty_fprintf (fp, "\n"); } if (node->pkt->pkttype == PKT_PUBLIC_KEY && (get_ownertrust (ctrl, pk) & TRUST_FLAG_DISABLED)) { tty_fprintf (fp, "*** "); tty_fprintf (fp, _("This key has been disabled")); tty_fprintf (fp, "\n"); } } if ((node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) && with_fpr) { print_fingerprint (ctrl, fp, pk, 2); tty_fprintf (fp, "\n"); } } } show_names (ctrl, fp, keyblock, primary, only_marked ? NODFLG_MARK_A : 0, with_prefs); if (do_warn && !nowarn) tty_fprintf (fp, _("Please note that the shown key validity" " is not necessarily correct\n" "unless you restart the program.\n")); xfree (serialno); } /* Display basic key information. This function is suitable to show * information on the key without any dependencies on the trustdb or * any other internal GnuPG stuff. KEYBLOCK may either be a public or * a secret key. This function may be called with KEYBLOCK containing * secret keys and thus the printing of "pub" vs. "sec" does only * depend on the packet type and not by checking with gpg-agent. If * PRINT_SEC ist set "sec" is printed instead of "pub". */ void show_basic_key_info (ctrl_t ctrl, kbnode_t keyblock, int print_sec) { KBNODE node; int i; char pkstrbuf[PUBKEY_STRING_SIZE]; /* The primary key */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_SECRET_KEY) { PKT_public_key *pk = node->pkt->pkt.public_key; const char *tag; if (node->pkt->pkttype == PKT_SECRET_KEY || print_sec) tag = "sec"; else tag = "pub"; /* Note, we use the same format string as in other show functions to make the translation job easier. */ tty_printf ("%s %s/%s ", tag, pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk)); tty_printf (_("created: %s"), datestr_from_pk (pk)); tty_printf (" "); tty_printf (_("expires: %s"), expirestr_from_pk (pk)); tty_printf ("\n"); print_fingerprint (ctrl, NULL, pk, 3); tty_printf ("\n"); } } /* The user IDs. */ for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; ++i; tty_printf (" "); if (uid->flags.revoked) tty_printf ("[%s] ", _("revoked")); else if (uid->flags.expired) tty_printf ("[%s] ", _("expired")); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); } } } static void show_key_and_fingerprint (ctrl_t ctrl, kbnode_t keyblock, int with_subkeys) { kbnode_t node; PKT_public_key *pk = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { pk = node->pkt->pkt.public_key; tty_printf ("pub %s/%s %s ", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk)); } else if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_print_utf8_string (uid->name, uid->len); break; } } tty_printf ("\n"); if (pk) print_fingerprint (ctrl, NULL, pk, 2); if (with_subkeys) { for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = node->pkt->pkt.public_key; tty_printf ("sub %s/%s %s [%s]\n", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk), usagestr_from_pk (pk, 0)); print_fingerprint (ctrl, NULL, pk, 4); } } } } /* Show a listing of the primary and its subkeys along with their keygrips. */ static void show_key_and_grip (kbnode_t keyblock) { kbnode_t node; PKT_public_key *pk = NULL; char pkstrbuf[PUBKEY_STRING_SIZE]; char *hexgrip; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = node->pkt->pkt.public_key; tty_printf ("%s %s/%s %s [%s]\n", node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub", pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk(pk), datestr_from_pk (pk), usagestr_from_pk (pk, 0)); if (!hexkeygrip_from_pk (pk, &hexgrip)) { tty_printf (" Keygrip: %s\n", hexgrip); xfree (hexgrip); } } } } /* Show a warning if no uids on the key have the primary uid flag set. */ static void no_primary_warning (KBNODE keyblock) { KBNODE node; int have_primary = 0, uid_count = 0; /* TODO: if we ever start behaving differently with a primary or non-primary attribute ID, we will need to check for attributes here as well. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data == NULL) { uid_count++; if (node->pkt->pkt.user_id->flags.primary == 2) { have_primary = 1; break; } } } if (uid_count > 1 && !have_primary) log_info (_ ("WARNING: no user ID has been marked as primary. This command" " may\n cause a different user ID to become" " the assumed primary.\n")); } /* Print a warning if the latest encryption subkey expires soon. This function is called after the expire data of the primary key has been changed. */ static void subkey_expire_warning (kbnode_t keyblock) { u32 curtime = make_timestamp (); kbnode_t node; PKT_public_key *pk; /* u32 mainexpire = 0; */ u32 subexpire = 0; u32 latest_date = 0; for (node = keyblock; node; node = node->next) { /* if (node->pkt->pkttype == PKT_PUBLIC_KEY) */ /* { */ /* pk = node->pkt->pkt.public_key; */ /* mainexpire = pk->expiredate; */ /* } */ if (node->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = node->pkt->pkt.public_key; if (!pk->flags.valid) continue; if (pk->flags.revoked) continue; if (pk->timestamp > curtime) continue; /* Ignore future keys. */ if (!(pk->pubkey_usage & PUBKEY_USAGE_ENC)) continue; /* Not an encryption key. */ if (pk->timestamp > latest_date || (!pk->timestamp && !latest_date)) { latest_date = pk->timestamp; subexpire = pk->expiredate; } } if (!subexpire) return; /* No valid subkey with an expiration time. */ if (curtime + (10*86400) > subexpire) { log_info (_("WARNING: Your encryption subkey expires soon.\n")); log_info (_("You may want to change its expiration date too.\n")); } } /* * Ask for a new user id, add the self-signature, and update the * keyblock. If UIDSTRING is not NULL the user ID is generated * unattended using that string. UIDSTRING is expected to be utf-8 * encoded and white space trimmed. Returns true if there is a new * user id. */ static int menu_adduid (ctrl_t ctrl, kbnode_t pub_keyblock, int photo, const char *photo_name, const char *uidstring) { PKT_user_id *uid; PKT_public_key *pk = NULL; PKT_signature *sig = NULL; PACKET *pkt; KBNODE node; KBNODE pub_where = NULL; gpg_error_t err; if (photo && uidstring) return 0; /* Not allowed. */ for (node = pub_keyblock; node; pub_where = node, node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; } if (!node) /* No subkey. */ pub_where = NULL; log_assert (pk); if (photo) { int hasattrib = 0; for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->attrib_data != NULL) { hasattrib = 1; break; } /* It is legal but bad for compatibility to add a photo ID to a v3 key as it means that PGP2 will not be able to use that key anymore. Also, PGP may not expect a photo on a v3 key. Don't bother to ask this if the key already has a photo - any damage has already been done at that point. -dms */ if (pk->version == 3 && !hasattrib) { if (opt.expert) { tty_printf (_("WARNING: This is a PGP2-style key. " "Adding a photo ID may cause some versions\n" " of PGP to reject this key.\n")); if (!cpr_get_answer_is_yes ("keyedit.v3_photo.okay", _("Are you sure you still want " "to add it? (y/N) "))) return 0; } else { tty_printf (_("You may not add a photo ID to " "a PGP2-style key.\n")); return 0; } } uid = generate_photo_id (ctrl, pk, photo_name); } else uid = generate_user_id (pub_keyblock, uidstring); if (!uid) { if (uidstring) { write_status_error ("adduid", gpg_error (304)); log_error ("%s", _("Such a user ID already exists on this key!\n")); } return 0; } err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x13, 0, 0, keygen_add_std_prefs, pk, NULL); if (err) { write_status_error ("keysig", err); log_error ("signing failed: %s\n", gpg_strerror (err)); free_user_id (uid); return 0; } /* Insert/append to public keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = uid; node = new_kbnode (pkt); if (pub_where) insert_kbnode (pub_where, node, 0); else add_kbnode (pub_keyblock, node); pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; if (pub_where) insert_kbnode (node, new_kbnode (pkt), 0); else add_kbnode (pub_keyblock, new_kbnode (pkt)); return 1; } /* * Remove all selected userids from the keyring */ static void menu_deluid (KBNODE pub_keyblock) { KBNODE node; int selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { selected = node->flag & NODFLG_SELUID; if (selected) { /* Only cause a trust update if we delete a non-revoked user id */ if (!node->pkt->pkt.user_id->flags.revoked) update_trust = 1; delete_kbnode (node); } } else if (selected && node->pkt->pkttype == PKT_SIGNATURE) delete_kbnode (node); else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) selected = 0; } commit_kbnode (&pub_keyblock); } static int menu_delsig (ctrl_t ctrl, kbnode_t pub_keyblock) { KBNODE node; PKT_user_id *uid = NULL; int changed = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { uid = (node->flag & NODFLG_SELUID) ? node->pkt->pkt.user_id : NULL; } else if (uid && node->pkt->pkttype == PKT_SIGNATURE) { int okay, valid, selfsig, inv_sig, no_key, other_err; tty_printf ("uid "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); okay = inv_sig = no_key = other_err = 0; if (opt.with_colons) valid = print_and_check_one_sig_colon (ctrl, pub_keyblock, node, &inv_sig, &no_key, &other_err, &selfsig, 1); else valid = print_and_check_one_sig (ctrl, pub_keyblock, node, &inv_sig, &no_key, &other_err, &selfsig, 1, 0); if (valid) { okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.valid", _("Delete this good signature? (y/N/q)")); /* Only update trust if we delete a good signature. The other two cases do not affect trust. */ if (okay) update_trust = 1; } else if (inv_sig || other_err) okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.invalid", _("Delete this invalid signature? (y/N/q)")); else if (no_key) okay = cpr_get_answer_yes_no_quit ("keyedit.delsig.unknown", _("Delete this unknown signature? (y/N/q)")); if (okay == -1) break; if (okay && selfsig && !cpr_get_answer_is_yes ("keyedit.delsig.selfsig", _("Really delete this self-signature? (y/N)"))) okay = 0; if (okay) { delete_kbnode (node); changed++; } } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) uid = NULL; } if (changed) { commit_kbnode (&pub_keyblock); tty_printf (ngettext("Deleted %d signature.\n", "Deleted %d signatures.\n", changed), changed); } else tty_printf (_("Nothing deleted.\n")); return changed; } static int menu_clean (ctrl_t ctrl, kbnode_t keyblock, int self_only) { KBNODE uidnode; int modified = 0, select_all = !count_selected_uids (keyblock); for (uidnode = keyblock->next; uidnode && uidnode->pkt->pkttype != PKT_PUBLIC_SUBKEY; uidnode = uidnode->next) { if (uidnode->pkt->pkttype == PKT_USER_ID && (uidnode->flag & NODFLG_SELUID || select_all)) { int uids = 0, sigs = 0; char *user = utf8_to_native (uidnode->pkt->pkt.user_id->name, uidnode->pkt->pkt.user_id->len, 0); clean_one_uid (ctrl, keyblock, uidnode, opt.verbose, self_only, &uids, &sigs); if (uids) { const char *reason; if (uidnode->pkt->pkt.user_id->flags.revoked) reason = _("revoked"); else if (uidnode->pkt->pkt.user_id->flags.expired) reason = _("expired"); else reason = _("invalid"); tty_printf (_("User ID \"%s\" compacted: %s\n"), user, reason); modified = 1; } else if (sigs) { tty_printf (ngettext("User ID \"%s\": %d signature removed\n", "User ID \"%s\": %d signatures removed\n", sigs), user, sigs); modified = 1; } else { tty_printf (self_only == 1 ? _("User ID \"%s\": already minimized\n") : _("User ID \"%s\": already clean\n"), user); } xfree (user); } } return modified; } /* * Remove some of the secondary keys */ static void menu_delkey (KBNODE pub_keyblock) { KBNODE node; int selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { selected = node->flag & NODFLG_SELKEY; if (selected) delete_kbnode (node); } else if (selected && node->pkt->pkttype == PKT_SIGNATURE) delete_kbnode (node); else selected = 0; } commit_kbnode (&pub_keyblock); /* No need to set update_trust here since signing keys are no longer used to certify other keys, so there is no change in trust when revoking/removing them. */ } /* * Ask for a new revoker, create the self-signature and put it into * the keyblock. Returns true if there is a new revoker. */ static int menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive) { PKT_public_key *pk = NULL; PKT_public_key *revoker_pk = NULL; PKT_signature *sig = NULL; PACKET *pkt; struct revocation_key revkey; size_t fprlen; int rc; log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); pk = pub_keyblock->pkt->pkt.public_key; if (pk->numrevkeys == 0 && pk->version == 3) { /* It is legal but bad for compatibility to add a revoker to a v3 key as it means that PGP2 will not be able to use that key anymore. Also, PGP may not expect a revoker on a v3 key. Don't bother to ask this if the key already has a revoker - any damage has already been done at that point. -dms */ if (opt.expert) { tty_printf (_("WARNING: This is a PGP 2.x-style key. " "Adding a designated revoker may cause\n" " some versions of PGP to reject this key.\n")); if (!cpr_get_answer_is_yes ("keyedit.v3_revoker.okay", _("Are you sure you still want " "to add it? (y/N) "))) return 0; } else { tty_printf (_("You may not add a designated revoker to " "a PGP 2.x-style key.\n")); return 0; } } for (;;) { char *answer; free_public_key (revoker_pk); revoker_pk = xmalloc_clear (sizeof (*revoker_pk)); tty_printf ("\n"); answer = cpr_get_utf8 ("keyedit.add_revoker", _("Enter the user ID of the designated revoker: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); goto fail; } /* Note that I'm requesting CERT here, which usually implies primary keys only, but some casual testing shows that PGP and GnuPG both can handle a designated revocation from a subkey. */ revoker_pk->req_usage = PUBKEY_USAGE_CERT; rc = get_pubkey_byname (ctrl, GET_PUBKEY_NO_AKL, NULL, revoker_pk, answer, NULL, NULL, 1); if (rc) { log_error (_("key \"%s\" not found: %s\n"), answer, gpg_strerror (rc)); xfree (answer); continue; } xfree (answer); fingerprint_from_pk (revoker_pk, revkey.fpr, &fprlen); if (fprlen != 20 && fprlen != 32) { log_error (_("cannot appoint a PGP 2.x style key as a " "designated revoker\n")); continue; } revkey.fprlen = fprlen; revkey.class = 0x80; if (sensitive) revkey.class |= 0x40; revkey.algid = revoker_pk->pubkey_algo; if (cmp_public_keys (revoker_pk, pk) == 0) { /* This actually causes no harm (after all, a key that designates itself as a revoker is the same as a regular key), but it's easy enough to check. */ log_error (_("you cannot appoint a key as its own " "designated revoker\n")); continue; } keyid_from_pk (pk, NULL); /* Does this revkey already exist? */ if (!pk->revkey && pk->numrevkeys) BUG (); else { int i; for (i = 0; i < pk->numrevkeys; i++) { if (memcmp (&pk->revkey[i], &revkey, sizeof (struct revocation_key)) == 0) { char buf[50]; log_error (_("this key has already been designated " "as a revoker\n")); format_keyid (pk_keyid (pk), KF_LONG, buf, sizeof (buf)); write_status_text (STATUS_ALREADY_SIGNED, buf); break; } } if (i < pk->numrevkeys) continue; } print_key_info (ctrl, NULL, 0, revoker_pk, 0); print_fingerprint (ctrl, NULL, revoker_pk, 2); tty_printf ("\n"); tty_printf (_("WARNING: appointing a key as a designated revoker " "cannot be undone!\n")); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("keyedit.add_revoker.okay", _("Are you sure you want to appoint this " "key as a designated revoker? (y/N) "))) continue; free_public_key (revoker_pk); revoker_pk = NULL; break; } rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, 0x1F, 0, 0, keygen_add_revkey, &revkey, NULL); if (rc) { write_status_error ("keysig", rc); log_error ("signing failed: %s\n", gpg_strerror (rc)); goto fail; } /* Insert into public keyblock. */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (pub_keyblock, new_kbnode (pkt), PKT_SIGNATURE); return 1; fail: if (sig) free_seckey_enc (sig); free_public_key (revoker_pk); return 0; } /* With FORCE_MAINKEY cleared this function handles the interactive * menu option "expire". With UNATTENDED set to 1 this function only * sets the expiration date of the primary key to NEWEXPIRATION and * avoid all interactivity; with a value of 2 only the flagged subkeys * are set to NEWEXPIRATION. Returns 0 if nothing was done, * GPG_ERR_TRUE if the key was modified, or any other error code. */ static gpg_error_t menu_expire (ctrl_t ctrl, kbnode_t pub_keyblock, int unattended, u32 newexpiration) { int signumber, rc; u32 expiredate; int only_mainkey; /* Set if only the mainkey is to be updated. */ PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; kbnode_t node; u32 keyid[2]; if (unattended) { only_mainkey = (unattended == 1); expiredate = newexpiration; } else { int n1; only_mainkey = 0; n1 = count_selected_keys (pub_keyblock); if (n1 > 1) { if (!cpr_get_answer_is_yes ("keyedit.expire_multiple_subkeys.okay", _("Are you sure you want to change the" " expiration time for multiple subkeys? (y/N) "))) return gpg_error (GPG_ERR_CANCELED);; } else if (n1) tty_printf (_("Changing expiration time for a subkey.\n")); else { tty_printf (_("Changing expiration time for the primary key.\n")); only_mainkey = 1; no_primary_warning (pub_keyblock); } expiredate = ask_expiredate (); } /* Now we can actually change the self-signature(s) */ main_pk = sub_pk = NULL; uid = NULL; signumber = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); main_pk->expiredate = expiredate; } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if ((node->flag & NODFLG_SELKEY) && unattended != 1) { /* The flag is set and we do not want to set the * expiration date only for the main key. */ sub_pk = node->pkt->pkt.public_key; sub_pk->expiredate = expiredate; } else sub_pk = NULL; } else if (node->pkt->pkttype == PKT_USER_ID) uid = node->pkt->pkt.user_id; else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE && (only_mainkey || sub_pk)) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ((only_mainkey && uid && uid->created && (sig->sig_class & ~3) == 0x10) || (!only_mainkey && sig->sig_class == 0x18)) && sig->flags.chosen_selfsig) { /* This is a self-signature which is to be replaced. */ PKT_signature *newsig; PACKET *newpkt; signumber++; if ((only_mainkey && main_pk->version < 4) || (!only_mainkey && sub_pk->version < 4)) { log_info (_("You can't change the expiration date of a v3 key\n")); return gpg_error (GPG_ERR_LEGACY_KEY); } if (only_mainkey) rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_key_expire, main_pk); else rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, NULL, sub_pk, main_pk, keygen_add_key_expire, sub_pk); if (rc) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (rc)); if (gpg_err_code (rc) == GPG_ERR_TRUE) rc = GPG_ERR_GENERAL; return rc; } /* Replace the packet. */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; } } } update_trust = 1; return gpg_error (GPG_ERR_TRUE); } /* Change the capability of a selected key. This command should only * be used to rectify badly created keys and as such is not suggested * for general use. */ static int menu_changeusage (ctrl_t ctrl, kbnode_t keyblock) { int n1, rc; int mainkey = 0; PKT_public_key *main_pk, *sub_pk; PKT_user_id *uid; kbnode_t node; u32 keyid[2]; n1 = count_selected_keys (keyblock); if (n1 > 1) { tty_printf (_("You must select exactly one key.\n")); return 0; } else if (n1) tty_printf (_("Changing usage of a subkey.\n")); else { tty_printf (_("Changing usage of the primary key.\n")); mainkey = 1; } /* Now we can actually change the self-signature(s) */ main_pk = sub_pk = NULL; uid = NULL; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (node->flag & NODFLG_SELKEY) sub_pk = node->pkt->pkt.public_key; else sub_pk = NULL; } else if (node->pkt->pkttype == PKT_USER_ID) uid = node->pkt->pkt.user_id; else if (main_pk && node->pkt->pkttype == PKT_SIGNATURE && (mainkey || sub_pk)) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && ((mainkey && uid && uid->created && (sig->sig_class & ~3) == 0x10) || (!mainkey && sig->sig_class == 0x18)) && sig->flags.chosen_selfsig) { /* This is the self-signature which is to be replaced. */ PKT_signature *newsig; PACKET *newpkt; if ((mainkey && main_pk->version < 4) || (!mainkey && sub_pk->version < 4)) { /* Note: This won't happen because we don't support * v3 keys anymore. */ log_info ("You can't change the capabilities of a v3 key\n"); return 0; } if (mainkey) main_pk->pubkey_usage = ask_key_flags (main_pk->pubkey_algo, 0, main_pk->pubkey_usage); else sub_pk->pubkey_usage = ask_key_flags (sub_pk->pubkey_algo, 1, sub_pk->pubkey_usage); if (mainkey) rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_key_flags, main_pk); else rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, NULL, sub_pk, main_pk, keygen_add_key_flags, sub_pk); if (rc) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* Replace the packet. */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; sub_pk = NULL; break; } } } return 1; } static int menu_backsign (ctrl_t ctrl, kbnode_t pub_keyblock) { int rc, modified = 0; PKT_public_key *main_pk; KBNODE node; u32 timestamp; log_assert (pub_keyblock->pkt->pkttype == PKT_PUBLIC_KEY); merge_keys_and_selfsig (ctrl, pub_keyblock); main_pk = pub_keyblock->pkt->pkt.public_key; keyid_from_pk (main_pk, NULL); /* We use the same timestamp for all backsigs so that we don't reveal information about the used machine. */ timestamp = make_timestamp (); for (node = pub_keyblock; node; node = node->next) { PKT_public_key *sub_pk = NULL; KBNODE node2, sig_pk = NULL /*,sig_sk = NULL*/; /* char *passphrase; */ /* Find a signing subkey with no backsig */ if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { if (node->pkt->pkt.public_key->pubkey_usage & PUBKEY_USAGE_SIG) { if (node->pkt->pkt.public_key->flags.backsig) tty_printf (_ ("signing subkey %s is already cross-certified\n"), keystr_from_pk (node->pkt->pkt.public_key)); else sub_pk = node->pkt->pkt.public_key; } else tty_printf (_("subkey %s does not sign and so does" " not need to be cross-certified\n"), keystr_from_pk (node->pkt->pkt.public_key)); } if (!sub_pk) continue; /* Find the selected selfsig on this subkey */ for (node2 = node->next; node2 && node2->pkt->pkttype == PKT_SIGNATURE; node2 = node2->next) if (node2->pkt->pkt.signature->version >= 4 && node2->pkt->pkt.signature->flags.chosen_selfsig) { sig_pk = node2; break; } if (!sig_pk) continue; /* Find the secret subkey that matches the public subkey */ log_debug ("FIXME: Check whether a secret subkey is available.\n"); /* if (!sub_sk) */ /* { */ /* tty_printf (_("no secret subkey for public subkey %s - ignoring\n"), */ /* keystr_from_pk (sub_pk)); */ /* continue; */ /* } */ /* Now we can get to work. */ rc = make_backsig (ctrl, sig_pk->pkt->pkt.signature, main_pk, sub_pk, sub_pk, timestamp, NULL); if (!rc) { PKT_signature *newsig; PACKET *newpkt; rc = update_keysig_packet (ctrl, &newsig, sig_pk->pkt->pkt.signature, main_pk, NULL, sub_pk, main_pk, NULL, NULL); if (!rc) { /* Put the new sig into place on the pubkey */ newpkt = xmalloc_clear (sizeof (*newpkt)); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (sig_pk->pkt, NULL); xfree (sig_pk->pkt); sig_pk->pkt = newpkt; modified = 1; } else { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); break; } } else { log_error ("make_backsig failed: %s\n", gpg_strerror (rc)); break; } } return modified; } static int change_primary_uid_cb (PKT_signature * sig, void *opaque) { byte buf[1]; /* first clear all primary uid flags so that we are sure none are * lingering around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID); /* if opaque is set,we want to set the primary id */ if (opaque) { buf[0] = 1; build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, buf, 1); } return 0; } /* * Set the primary uid flag for the selected UID. We will also reset * all other primary uid flags. For this to work we have to update * all the signature timestamps. If we would do this with the current * time, we lose quite a lot of information, so we use a kludge to * do this: Just increment the timestamp by one second which is * sufficient to updated a signature during import. */ static int menu_set_primary_uid (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected; int attribute = 0; int modified = 0; if (count_selected_uids (pub_keyblock) != 1) { tty_printf (_("Please select exactly one user ID.\n")); return 0; } main_pk = NULL; uid = NULL; selected = 0; /* Is our selected uid an attribute packet? */ for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && node->flag & NODFLG_SELUID) attribute = (node->pkt->pkt.user_id->attrib_data != NULL); for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* No more user ids expected - ready. */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = node->flag & NODFLG_SELUID; } else if (main_pk && uid && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && attribute == (uid->attrib_data != NULL) && sig->flags.chosen_selfsig) { if (sig->version < 4) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); xfree (user); } else { /* This is a selfsignature which is to be replaced. We can just ignore v3 signatures because they are not able to carry the primary ID flag. We also ignore self-sigs on user IDs that are not of the same type that we are making primary. That is, if we are making a user ID primary, we alter user IDs. If we are making an attribute packet primary, we alter attribute packets. */ /* FIXME: We must make sure that we only have one self-signature per user ID here (not counting revocations) */ PKT_signature *newsig; PACKET *newpkt; const byte *p; int action; /* See whether this signature has the primary UID flag. */ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL); if (!p) p = parse_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID, NULL); if (p && *p) /* yes */ action = selected ? 0 : -1; else /* no */ action = selected ? 1 : 0; if (action) { int rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, change_primary_uid_cb, action > 0 ? "x" : NULL); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; } } } } } return modified; } /* * Set preferences to new values for the selected user IDs */ static int menu_set_preferences (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; no_primary_warning (pub_keyblock); select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* No more user-ids expected - ready. */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { if (sig->version < 4) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); xfree (user); } else { /* This is a selfsignature which is to be replaced * We have to ignore v3 signatures because they are * not able to carry the preferences. */ PKT_signature *newsig; PACKET *newpkt; int rc; rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_upd_std_prefs, NULL); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; } } } } return modified; } static int menu_set_keyserver_url (ctrl_t ctrl, const char *url, kbnode_t pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; char *answer, *uri; no_primary_warning (pub_keyblock); if (url) answer = xstrdup (url); else { answer = cpr_get_utf8 ("keyedit.add_keyserver", _("Enter your preferred keyserver URL: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); return 0; } } if (ascii_strcasecmp (answer, "none") == 0) uri = NULL; else { struct keyserver_spec *keyserver = NULL; /* Sanity check the format */ keyserver = parse_keyserver_uri (answer, 1); xfree (answer); if (!keyserver) { log_info (_("could not parse keyserver URL\n")); return 0; } uri = xstrdup (keyserver->uri); free_keyserver_spec (keyserver); } select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); if (sig->version < 4) log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); else { /* This is a selfsignature which is to be replaced * We have to ignore v3 signatures because they are * not able to carry the subpacket. */ PKT_signature *newsig; PACKET *newpkt; int rc; const byte *p; size_t plen; p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &plen); if (p && plen) { tty_printf ("Current preferred keyserver for user" " ID \"%s\": ", user); tty_print_utf8_string (p, plen); tty_printf ("\n"); if (!cpr_get_answer_is_yes ("keyedit.confirm_keyserver", uri ? _("Are you sure you want to replace it? (y/N) ") : _("Are you sure you want to delete it? (y/N) "))) continue; } else if (uri == NULL) { /* There is no current keyserver URL, so there is no point in trying to un-set it. */ continue; } rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_keyserver_url, uri); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); xfree (uri); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; } xfree (user); } } } xfree (uri); return modified; } static int menu_set_notation (ctrl_t ctrl, const char *string, KBNODE pub_keyblock) { PKT_public_key *main_pk; PKT_user_id *uid; KBNODE node; u32 keyid[2]; int selected, select_all; int modified = 0; char *answer; struct notation *notation; no_primary_warning (pub_keyblock); if (string) answer = xstrdup (string); else { answer = cpr_get_utf8 ("keyedit.add_notation", _("Enter the notation: ")); if (answer[0] == '\0' || answer[0] == CONTROL_D) { xfree (answer); return 0; } } if (!ascii_strcasecmp (answer, "none") || !ascii_strcasecmp (answer, "-")) notation = NULL; /* Delete them all. */ else { notation = string_to_notation (answer, 0); if (!notation) { xfree (answer); return 0; } } xfree (answer); select_all = !count_selected_uids (pub_keyblock); /* Now we can actually change the self signature(s) */ main_pk = NULL; uid = NULL; selected = 0; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) break; /* ready */ if (node->pkt->pkttype == PKT_PUBLIC_KEY) { main_pk = node->pkt->pkt.public_key; keyid_from_pk (main_pk, keyid); } else if (node->pkt->pkttype == PKT_USER_ID) { uid = node->pkt->pkt.user_id; selected = select_all || (node->flag & NODFLG_SELUID); } else if (main_pk && uid && selected && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; if (keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] && (uid && (sig->sig_class & ~3) == 0x10) && sig->flags.chosen_selfsig) { char *user = utf8_to_native (uid->name, strlen (uid->name), 0); if (sig->version < 4) log_info (_("skipping v3 self-signature on user ID \"%s\"\n"), user); else { PKT_signature *newsig; PACKET *newpkt; int rc, skip = 0, addonly = 1; if (sig->flags.notation) { tty_printf ("Current notations for user ID \"%s\":\n", user); tty_print_notations (-9, sig); } else { tty_printf ("No notations on user ID \"%s\"\n", user); if (notation == NULL) { /* There are no current notations, so there is no point in trying to un-set them. */ continue; } } if (notation) { struct notation *n; int deleting = 0; notation->next = sig_to_notation (sig); for (n = notation->next; n; n = n->next) if (strcmp (n->name, notation->name) == 0) { if (notation->value) { if (strcmp (n->value, notation->value) == 0) { if (notation->flags.ignore) { /* Value match with a delete flag. */ n->flags.ignore = 1; deleting = 1; } else { /* Adding the same notation twice, so don't add it at all. */ skip = 1; tty_printf ("Skipping notation:" " %s=%s\n", notation->name, notation->value); break; } } } else { /* No value, so it means delete. */ n->flags.ignore = 1; deleting = 1; } if (n->flags.ignore) { tty_printf ("Removing notation: %s=%s\n", n->name, n->value); addonly = 0; } } if (!notation->flags.ignore && !skip) tty_printf ("Adding notation: %s=%s\n", notation->name, notation->value); /* We tried to delete, but had no matches. */ if (notation->flags.ignore && !deleting) continue; } else { tty_printf ("Removing all notations\n"); addonly = 0; } if (skip || (!addonly && !cpr_get_answer_is_yes ("keyedit.confirm_notation", _("Proceed? (y/N) ")))) continue; rc = update_keysig_packet (ctrl, &newsig, sig, main_pk, uid, NULL, main_pk, keygen_add_notations, notation); if (rc) { log_error ("update_keysig_packet failed: %s\n", gpg_strerror (rc)); free_notation (notation); xfree (user); return 0; } /* replace the packet */ newpkt = xmalloc_clear (sizeof *newpkt); newpkt->pkttype = PKT_SIGNATURE; newpkt->pkt.signature = newsig; free_packet (node->pkt, NULL); xfree (node->pkt); node->pkt = newpkt; modified = 1; if (notation) { /* Snip off the notation list from the sig */ free_notation (notation->next); notation->next = NULL; } xfree (user); } } } } free_notation (notation); return modified; } /* * Select one user id or remove all selection if IDX is 0 or select * all if IDX is -1. Returns: True if the selection changed. */ static int menu_select_uid (KBNODE keyblock, int idx) { KBNODE node; int i; if (idx == -1) /* Select all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) node->flag |= NODFLG_SELUID; return 1; } else if (idx) /* Toggle. */ { for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) if (++i == idx) break; } if (!node) { tty_printf (_("No user ID with index %d\n"), idx); return 0; } for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { if (++i == idx) { if ((node->flag & NODFLG_SELUID)) node->flag &= ~NODFLG_SELUID; else node->flag |= NODFLG_SELUID; } } } } else /* Unselect all */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) node->flag &= ~NODFLG_SELUID; } return 1; } /* Search in the keyblock for a uid that matches namehash */ static int menu_select_uid_namehash (KBNODE keyblock, const char *namehash) { byte hash[NAMEHASH_LEN]; KBNODE node; int i; log_assert (strlen (namehash) == NAMEHASH_LEN * 2); for (i = 0; i < NAMEHASH_LEN; i++) hash[i] = hextobyte (&namehash[i * 2]); for (node = keyblock->next; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { namehash_from_uid (node->pkt->pkt.user_id); if (memcmp (node->pkt->pkt.user_id->namehash, hash, NAMEHASH_LEN) == 0) { if (node->flag & NODFLG_SELUID) node->flag &= ~NODFLG_SELUID; else node->flag |= NODFLG_SELUID; break; } } } if (!node) { tty_printf (_("No user ID with hash %s\n"), namehash); return 0; } return 1; } /* * Select secondary keys * Returns: True if the selection changed. */ static int menu_select_key (KBNODE keyblock, int idx, char *p) { KBNODE node; int i, j; int is_hex_digits; is_hex_digits = p && strlen (p) >= 8; if (is_hex_digits) { /* Skip initial spaces. */ while (spacep (p)) p ++; /* If the id starts with 0x accept and ignore it. */ if (p[0] == '0' && p[1] == 'x') p += 2; for (i = 0, j = 0; p[i]; i ++) if (hexdigitp (&p[i])) { p[j] = toupper (p[i]); j ++; } else if (spacep (&p[i])) /* Skip spaces. */ { } else { is_hex_digits = 0; break; } if (is_hex_digits) /* In case we skipped some spaces, add a new NUL terminator. */ { p[j] = 0; /* If we skipped some spaces, make sure that we still have at least 8 characters. */ is_hex_digits = (/* Short keyid. */ strlen (p) == 8 /* Long keyid. */ || strlen (p) == 16 /* Fingerprints are (currently) 32 or 40 characters. */ || strlen (p) >= 32); } } if (is_hex_digits) { int found_one = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { int match = 0; if (strlen (p) == 8 || strlen (p) == 16) { u32 kid[2]; char kid_str[17]; keyid_from_pk (node->pkt->pkt.public_key, kid); format_keyid (kid, strlen (p) == 8 ? KF_SHORT : KF_LONG, kid_str, sizeof (kid_str)); if (strcmp (p, kid_str) == 0) match = 1; } else { char fp[2*MAX_FINGERPRINT_LEN + 1]; hexfingerprint (node->pkt->pkt.public_key, fp, sizeof (fp)); if (strcmp (fp, p) == 0) match = 1; } if (match) { if ((node->flag & NODFLG_SELKEY)) node->flag &= ~NODFLG_SELKEY; else node->flag |= NODFLG_SELKEY; found_one = 1; } } if (found_one) return 1; tty_printf (_("No subkey with key ID '%s'.\n"), p); return 0; } if (idx == -1) /* Select all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) node->flag |= NODFLG_SELKEY; } else if (idx) /* Toggle selection. */ { for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) if (++i == idx) break; } if (!node) { tty_printf (_("No subkey with index %d\n"), idx); return 0; } for (i = 0, node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) if (++i == idx) { if ((node->flag & NODFLG_SELKEY)) node->flag &= ~NODFLG_SELKEY; else node->flag |= NODFLG_SELKEY; } } } else /* Unselect all. */ { for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) node->flag &= ~NODFLG_SELKEY; } return 1; } static int count_uids_with_flag (KBNODE keyblock, unsigned flag) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && (node->flag & flag)) i++; return i; } static int count_keys_with_flag (KBNODE keyblock, unsigned flag) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if ((node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) && (node->flag & flag)) i++; return i; } static int count_uids (KBNODE keyblock) { KBNODE node; int i = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID) i++; return i; } /* * Returns true if there is at least one selected user id */ static int count_selected_uids (KBNODE keyblock) { return count_uids_with_flag (keyblock, NODFLG_SELUID); } static int count_selected_keys (KBNODE keyblock) { return count_keys_with_flag (keyblock, NODFLG_SELKEY); } /* Returns how many real (i.e. not attribute) uids are unmarked. */ static int real_uids_left (KBNODE keyblock) { KBNODE node; int real = 0; for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & NODFLG_SELUID) && !node->pkt->pkt.user_id->attrib_data) real++; return real; } /* * Ask whether the signature should be revoked. If the user commits this, * flag bit MARK_A is set on the signature and the user ID. */ static void ask_revoke_sig (ctrl_t ctrl, kbnode_t keyblock, kbnode_t node) { int doit = 0; PKT_user_id *uid; PKT_signature *sig = node->pkt->pkt.signature; KBNODE unode = find_prev_kbnode (keyblock, node, PKT_USER_ID); if (!unode) { log_error ("Oops: no user ID for signature\n"); return; } uid = unode->pkt->pkt.user_id; if (opt.with_colons) { if (uid->attrib_data) printf ("uat:::::::::%u %lu", uid->numattribs, uid->attrib_len); else { es_printf ("uid:::::::::"); es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL); } es_printf ("\n"); print_and_check_one_sig_colon (ctrl, keyblock, node, NULL, NULL, NULL, NULL, 1); } else { char *p = utf8_to_native (unode->pkt->pkt.user_id->name, unode->pkt->pkt.user_id->len, 0); tty_printf (_("user ID: \"%s\"\n"), p); xfree (p); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), sig->flags.exportable ? "" : _(" (non-exportable)"), ""); } if (sig->flags.expired) { tty_printf (_("This signature expired on %s.\n"), expirestr_from_sig (sig)); /* Use a different question so we can have different help text */ doit = cpr_get_answer_is_yes ("ask_revoke_sig.expired", _("Are you sure you still want to revoke it? (y/N) ")); } else doit = cpr_get_answer_is_yes ("ask_revoke_sig.one", _("Create a revocation certificate for this signature? (y/N) ")); if (doit) { node->flag |= NODFLG_MARK_A; unode->flag |= NODFLG_MARK_A; } } /* * Display all user ids of the current public key together with signatures * done by one of our keys. Then walk over all this sigs and ask the user * whether he wants to revoke this signature. * Return: True when the keyblock has changed. */ static int menu_revsig (ctrl_t ctrl, kbnode_t keyblock) { PKT_signature *sig; PKT_public_key *primary_pk; KBNODE node; int changed = 0; int rc, any, skip = 1, all = !count_selected_uids (keyblock); struct revocation_reason_info *reason = NULL; log_assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY); /* First check whether we have any signatures at all. */ any = 0; for (node = keyblock; node; node = node->next) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if (node->pkt->pkttype == PKT_USER_ID) { if (node->flag & NODFLG_SELUID || all) skip = 0; else skip = 1; } else if (!skip && node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), - have_secret_key_with_kid (sig->keyid))) + have_secret_key_with_kid (ctrl, sig->keyid))) { if ((sig->sig_class & ~3) == 0x10) { any = 1; break; } } } if (!any) { tty_printf (_("Not signed by you.\n")); return 0; } /* FIXME: detect duplicates here */ tty_printf (_("You have signed these user IDs on key %s:\n"), keystr_from_pk (keyblock->pkt->pkt.public_key)); for (node = keyblock; node; node = node->next) { node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A); if (node->pkt->pkttype == PKT_USER_ID) { if (node->flag & NODFLG_SELUID || all) { PKT_user_id *uid = node->pkt->pkt.user_id; /* Hmmm: Should we show only UIDs with a signature? */ tty_printf (" "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); skip = 0; } else skip = 1; } else if (!skip && node->pkt->pkttype == PKT_SIGNATURE && ((sig = node->pkt->pkt.signature), - have_secret_key_with_kid (sig->keyid))) + have_secret_key_with_kid (ctrl, sig->keyid))) { if ((sig->sig_class & ~3) == 0x10) { tty_printf (" "); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), sig->flags.exportable ? "" : _(" (non-exportable)"), sig->flags.revocable ? "" : _(" (non-revocable)")); if (sig->flags.revocable) node->flag |= NODFLG_SELSIG; } else if (sig->sig_class == 0x30) { tty_printf (" "); tty_printf (_("revoked by your key %s on %s\n"), keystr (sig->keyid), datestr_from_sig (sig)); } } } tty_printf ("\n"); /* ask */ for (node = keyblock; node; node = node->next) { if (!(node->flag & NODFLG_SELSIG)) continue; ask_revoke_sig (ctrl, keyblock, node); } /* present selected */ any = 0; for (node = keyblock; node; node = node->next) { if (!(node->flag & NODFLG_MARK_A)) continue; if (!any) { any = 1; tty_printf (_("You are about to revoke these signatures:\n")); } if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; tty_printf (" "); tty_print_utf8_string (uid->name, uid->len); tty_printf ("\n"); } else if (node->pkt->pkttype == PKT_SIGNATURE) { sig = node->pkt->pkt.signature; tty_printf (" "); tty_printf (_("signed by your key %s on %s%s%s\n"), keystr (sig->keyid), datestr_from_sig (sig), "", sig->flags.exportable ? "" : _(" (non-exportable)")); } } if (!any) return 0; /* none selected */ if (!cpr_get_answer_is_yes ("ask_revoke_sig.okay", _("Really create the revocation certificates? (y/N) "))) return 0; /* forget it */ reason = ask_revocation_reason (0, 1, 0); if (!reason) { /* user decided to cancel */ return 0; } /* now we can sign the user ids */ reloop: /* (must use this, because we are modifying the list) */ primary_pk = keyblock->pkt->pkt.public_key; for (node = keyblock; node; node = node->next) { KBNODE unode; PACKET *pkt; struct sign_attrib attrib; PKT_public_key *signerkey; if (!(node->flag & NODFLG_MARK_A) || node->pkt->pkttype != PKT_SIGNATURE) continue; unode = find_prev_kbnode (keyblock, node, PKT_USER_ID); log_assert (unode); /* we already checked this */ memset (&attrib, 0, sizeof attrib); attrib.reason = reason; attrib.non_exportable = !node->pkt->pkt.signature->flags.exportable; node->flag &= ~NODFLG_MARK_A; signerkey = xmalloc_secure_clear (sizeof *signerkey); if (get_seckey (ctrl, signerkey, node->pkt->pkt.signature->keyid)) { log_info (_("no secret key\n")); free_public_key (signerkey); continue; } rc = make_keysig_packet (ctrl, &sig, primary_pk, unode->pkt->pkt.user_id, NULL, signerkey, 0x30, 0, 0, sign_mk_attrib, &attrib, NULL); free_public_key (signerkey); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); release_revocation_reason_info (reason); return changed; } changed = 1; /* we changed the keyblock */ update_trust = 1; /* Are we revoking our own uid? */ if (primary_pk->keyid[0] == sig->keyid[0] && primary_pk->keyid[1] == sig->keyid[1]) unode->pkt->pkt.user_id->flags.revoked = 1; pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (unode, new_kbnode (pkt), 0); goto reloop; } release_revocation_reason_info (reason); return changed; } /* return 0 if revocation of NODE (which must be a User ID) was successful, non-zero if there was an error. *modified will be set to 1 if a change was made. */ static int core_revuid (ctrl_t ctrl, kbnode_t keyblock, KBNODE node, const struct revocation_reason_info *reason, int *modified) { PKT_public_key *pk = keyblock->pkt->pkt.public_key; gpg_error_t rc; if (node->pkt->pkttype != PKT_USER_ID) { rc = gpg_error (GPG_ERR_NO_USER_ID); write_status_error ("keysig", rc); log_error (_("tried to revoke a non-user ID: %s\n"), gpg_strerror (rc)); return 1; } else { PKT_user_id *uid = node->pkt->pkt.user_id; if (uid->flags.revoked) { char *user = utf8_to_native (uid->name, uid->len, 0); log_info (_("user ID \"%s\" is already revoked\n"), user); xfree (user); } else { PACKET *pkt; PKT_signature *sig; struct sign_attrib attrib; u32 timestamp = make_timestamp (); if (uid->created >= timestamp) { /* Okay, this is a problem. The user ID selfsig was created in the future, so we need to warn the user and set our revocation timestamp one second after that so everything comes out clean. */ log_info (_("WARNING: a user ID signature is dated %d" " seconds in the future\n"), uid->created - timestamp); timestamp = uid->created + 1; } memset (&attrib, 0, sizeof attrib); /* should not need to cast away const here; but revocation_reason_build_cb needs to take a non-const void* in order to meet the function signutare for the mksubpkt argument to make_keysig_packet */ attrib.reason = (struct revocation_reason_info *)reason; rc = make_keysig_packet (ctrl, &sig, pk, uid, NULL, pk, 0x30, timestamp, 0, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); return 1; } else { pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), 0); #ifndef NO_TRUST_MODELS /* If the trustdb has an entry for this key+uid then the trustdb needs an update. */ if (!update_trust && ((get_validity (ctrl, keyblock, pk, uid, NULL, 0) & TRUST_MASK) >= TRUST_UNDEFINED)) update_trust = 1; #endif /*!NO_TRUST_MODELS*/ node->pkt->pkt.user_id->flags.revoked = 1; if (modified) *modified = 1; } } return 0; } } /* Revoke a user ID (i.e. revoke a user ID selfsig). Return true if keyblock changed. */ static int menu_revuid (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; KBNODE node; int changed = 0; int rc; struct revocation_reason_info *reason = NULL; size_t valid_uids; /* Note that this is correct as per the RFCs, but nevertheless somewhat meaningless in the real world. 1991 did define the 0x30 sig class, but PGP 2.x did not actually implement it, so it would probably be safe to use v4 revocations everywhere. -ds */ for (node = pub_keyblock; node; node = node->next) if (pk->version > 3 || (node->pkt->pkttype == PKT_USER_ID && node->pkt->pkt.user_id->selfsigversion > 3)) { if ((reason = ask_revocation_reason (0, 1, 4))) break; else goto leave; } /* Too make sure that we do not revoke the last valid UID, we first count how many valid UIDs there are. */ valid_uids = 0; for (node = pub_keyblock; node; node = node->next) valid_uids += node->pkt->pkttype == PKT_USER_ID && ! node->pkt->pkt.user_id->flags.revoked && ! node->pkt->pkt.user_id->flags.expired; reloop: /* (better this way because we are modifying the keyring) */ for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_SELUID)) { int modified = 0; /* Make sure that we do not revoke the last valid UID. */ if (valid_uids == 1 && ! node->pkt->pkt.user_id->flags.revoked && ! node->pkt->pkt.user_id->flags.expired) { log_error (_("Cannot revoke the last valid user ID.\n")); goto leave; } rc = core_revuid (ctrl, pub_keyblock, node, reason, &modified); if (rc) goto leave; if (modified) { node->flag &= ~NODFLG_SELUID; changed = 1; goto reloop; } } if (changed) commit_kbnode (&pub_keyblock); leave: release_revocation_reason_info (reason); return changed; } /* * Revoke the whole key. */ static int menu_revkey (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key; int rc, changed = 0; struct revocation_reason_info *reason; PACKET *pkt; PKT_signature *sig; if (pk->flags.revoked) { tty_printf (_("Key %s is already revoked.\n"), keystr_from_pk (pk)); return 0; } reason = ask_revocation_reason (1, 0, 0); /* user decided to cancel */ if (!reason) return 0; rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk, 0x20, 0, 0, revocation_reason_build_cb, reason, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); goto scram; } changed = 1; /* we changed the keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (pub_keyblock, new_kbnode (pkt), 0); commit_kbnode (&pub_keyblock); update_trust = 1; scram: release_revocation_reason_info (reason); return changed; } static int menu_revsubkey (ctrl_t ctrl, kbnode_t pub_keyblock) { PKT_public_key *mainpk; KBNODE node; int changed = 0; int rc; struct revocation_reason_info *reason = NULL; reason = ask_revocation_reason (1, 0, 0); if (!reason) return 0; /* User decided to cancel. */ reloop: /* (better this way because we are modifying the keyring) */ mainpk = pub_keyblock->pkt->pkt.public_key; for (node = pub_keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY && (node->flag & NODFLG_SELKEY)) { PACKET *pkt; PKT_signature *sig; PKT_public_key *subpk = node->pkt->pkt.public_key; struct sign_attrib attrib; if (subpk->flags.revoked) { tty_printf (_("Subkey %s is already revoked.\n"), keystr_from_pk (subpk)); continue; } memset (&attrib, 0, sizeof attrib); attrib.reason = reason; node->flag &= ~NODFLG_SELKEY; rc = make_keysig_packet (ctrl, &sig, mainpk, NULL, subpk, mainpk, 0x28, 0, 0, sign_mk_attrib, &attrib, NULL); if (rc) { write_status_error ("keysig", rc); log_error (_("signing failed: %s\n"), gpg_strerror (rc)); release_revocation_reason_info (reason); return changed; } changed = 1; /* we changed the keyblock */ pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; insert_kbnode (node, new_kbnode (pkt), 0); goto reloop; } } commit_kbnode (&pub_keyblock); /* No need to set update_trust here since signing keys no longer are used to certify other keys, so there is no change in trust when revoking/removing them */ release_revocation_reason_info (reason); return changed; } /* Note that update_ownertrust is going to mark the trustdb dirty when enabling or disabling a key. This is arguably sub-optimal as disabled keys are still counted in the web of trust, but perhaps not worth adding extra complexity to change. -ds */ #ifndef NO_TRUST_MODELS static int enable_disable_key (ctrl_t ctrl, kbnode_t keyblock, int disable) { PKT_public_key *pk = find_kbnode (keyblock, PKT_PUBLIC_KEY)->pkt->pkt.public_key; unsigned int trust, newtrust; trust = newtrust = get_ownertrust (ctrl, pk); newtrust &= ~TRUST_FLAG_DISABLED; if (disable) newtrust |= TRUST_FLAG_DISABLED; if (trust == newtrust) return 0; /* already in that state */ update_ownertrust (ctrl, pk, newtrust); return 0; } #endif /*!NO_TRUST_MODELS*/ static void menu_showphoto (ctrl_t ctrl, kbnode_t keyblock) { KBNODE node; int select_all = !count_selected_uids (keyblock); int count = 0; PKT_public_key *pk = NULL; /* Look for the public key first. We have to be really, really, explicit as to which photo this is, and what key it is a UID on since people may want to sign it. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY) pk = node->pkt->pkt.public_key; else if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; count++; if ((select_all || (node->flag & NODFLG_SELUID)) && uid->attribs != NULL) { int i; for (i = 0; i < uid->numattribs; i++) { byte type; u32 size; if (uid->attribs[i].type == ATTRIB_IMAGE && parse_image_header (&uid->attribs[i], &type, &size)) { tty_printf (_("Displaying %s photo ID of size %ld for " "key %s (uid %d)\n"), image_type_to_string (type, 1), (ulong) size, keystr_from_pk (pk), count); show_photos (ctrl, &uid->attribs[i], 1, pk, uid); } } } } } } diff --git a/g10/keygen.c b/g10/keygen.c index d9037d29d..5aeda36ce 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -1,5777 +1,5777 @@ /* keygen.c - Generate a key pair * Copyright (C) 1998-2007, 2009-2011 Free Software Foundation, Inc. * Copyright (C) 2014, 2015, 2016, 2017, 2018 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "main.h" #include "packet.h" #include "../common/ttyio.h" #include "options.h" #include "keydb.h" #include "trustdb.h" #include "../common/status.h" #include "../common/i18n.h" #include "keyserver-internal.h" #include "call-agent.h" #include "pkglue.h" #include "../common/shareddefs.h" #include "../common/host2net.h" #include "../common/mbox-util.h" /* The default algorithms. If you change them, you should ensure the value is inside the bounds enforced by ask_keysize and gen_xxx. See also get_keysize_range which encodes the allowed ranges. */ #define DEFAULT_STD_KEY_PARAM "rsa3072/cert,sign+rsa3072/encr" #define FUTURE_STD_KEY_PARAM "ed25519/cert,sign+cv25519/encr" /* When generating keys using the streamlined key generation dialog, use this as a default expiration interval. */ const char *default_expiration_interval = "2y"; /* Flag bits used during key generation. */ #define KEYGEN_FLAG_NO_PROTECTION 1 #define KEYGEN_FLAG_TRANSIENT_KEY 2 #define KEYGEN_FLAG_CREATE_V5_KEY 4 /* Maximum number of supported algorithm preferences. */ #define MAX_PREFS 30 enum para_name { pKEYTYPE, pKEYLENGTH, pKEYCURVE, pKEYUSAGE, pSUBKEYTYPE, pSUBKEYLENGTH, pSUBKEYCURVE, pSUBKEYUSAGE, pAUTHKEYTYPE, pNAMEREAL, pNAMEEMAIL, pNAMECOMMENT, pPREFERENCES, pREVOKER, pUSERID, pCREATIONDATE, pKEYCREATIONDATE, /* Same in seconds since epoch. */ pEXPIREDATE, pKEYEXPIRE, /* in n seconds */ pSUBKEYEXPIRE, /* in n seconds */ pPASSPHRASE, pSERIALNO, pCARDBACKUPKEY, pHANDLE, pKEYSERVER, pKEYGRIP, pSUBKEYGRIP, pVERSION, /* Desired version of the key packet. */ pSUBVERSION, /* Ditto for the subpacket. */ }; struct para_data_s { struct para_data_s *next; int lnr; enum para_name key; union { u32 expire; u32 creation; unsigned int usage; struct revocation_key revkey; char value[1]; } u; }; struct output_control_s { int lnr; int dryrun; unsigned int keygen_flags; int use_files; struct { char *fname; char *newfname; IOBUF stream; armor_filter_context_t *afx; } pub; }; struct opaque_data_usage_and_pk { unsigned int usage; PKT_public_key *pk; }; /* FIXME: These globals vars are ugly. And using MAX_PREFS even for * aeads is useless, given that we don't expects more than a very few * algorithms. */ static int prefs_initialized = 0; static byte sym_prefs[MAX_PREFS]; static int nsym_prefs; static byte hash_prefs[MAX_PREFS]; static int nhash_prefs; static byte zip_prefs[MAX_PREFS]; static int nzip_prefs; static byte aead_prefs[MAX_PREFS]; static int naead_prefs; static int mdc_available; static int ks_modify; static int aead_available; static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, const char *algostr, const char *usagestr, const char *expirestr, int *r_algo, unsigned int *r_usage, u32 *r_expire, unsigned int *r_nbits, const char **r_curve, int *r_version); static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, struct output_control_s *outctrl, int card ); static int write_keyblock (iobuf_t out, kbnode_t node); static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval, int keygen_flags); static unsigned int get_keysize_range (int algo, unsigned int *min, unsigned int *max); /* Return the algo string for a default new key. */ const char * get_default_pubkey_algo (void) { if (opt.def_new_key_algo) { if (*opt.def_new_key_algo && !strchr (opt.def_new_key_algo, ':')) return opt.def_new_key_algo; /* To avoid checking that option every time we delay that until * here. The only thing we really need to make sure is that * there is no colon in the string so that the --gpgconf-list * command won't mess up its output. */ log_info (_("invalid value for option '%s'\n"), "--default-new-key-algo"); } return DEFAULT_STD_KEY_PARAM; } static void print_status_key_created (int letter, PKT_public_key *pk, const char *handle) { byte array[MAX_FINGERPRINT_LEN], *s; char *buf, *p; size_t i, n; if (!handle) handle = ""; buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1); p = buf; if (letter || pk) { *p++ = letter; if (pk) { *p++ = ' '; fingerprint_from_pk (pk, array, &n); s = array; /* Fixme: Use bin2hex */ for (i=0; i < n ; i++, s++, p += 2) snprintf (p, 3, "%02X", *s); } } if (*handle) { *p++ = ' '; for (i=0; handle[i] && i < 100; i++) *p++ = isspace ((unsigned int)handle[i])? '_':handle[i]; } *p = 0; write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED, buf); xfree (buf); } static void print_status_key_not_created (const char *handle) { print_status_key_created (0, NULL, handle); } static gpg_error_t write_uid (kbnode_t root, const char *s) { PACKET *pkt = xmalloc_clear (sizeof *pkt); size_t n = strlen (s); if (n > MAX_UID_PACKET_LENGTH - 10) return gpg_error (GPG_ERR_INV_USER_ID); pkt->pkttype = PKT_USER_ID; pkt->pkt.user_id = xmalloc_clear (sizeof *pkt->pkt.user_id + n); pkt->pkt.user_id->len = n; pkt->pkt.user_id->ref = 1; strcpy (pkt->pkt.user_id->name, s); add_kbnode (root, new_kbnode (pkt)); return 0; } static void do_add_key_flags (PKT_signature *sig, unsigned int use) { byte buf[1]; buf[0] = 0; /* The spec says that all primary keys MUST be able to certify. */ if(sig->sig_class!=0x18) buf[0] |= 0x01; if (use & PUBKEY_USAGE_SIG) buf[0] |= 0x02; if (use & PUBKEY_USAGE_ENC) buf[0] |= 0x04 | 0x08; if (use & PUBKEY_USAGE_AUTH) buf[0] |= 0x20; build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1); } int keygen_add_key_expire (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; byte buf[8]; u32 u; if (pk->expiredate) { if (pk->expiredate > pk->timestamp) u = pk->expiredate - pk->timestamp; else u = 1; buf[0] = (u >> 24) & 0xff; buf[1] = (u >> 16) & 0xff; buf[2] = (u >> 8) & 0xff; buf[3] = u & 0xff; build_sig_subpkt (sig, SIGSUBPKT_KEY_EXPIRE, buf, 4); } else { /* Make sure we don't leave a key expiration subpacket lying around */ delete_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE); } return 0; } /* Add the key usage (i.e. key flags) in SIG from the public keys * pubkey_usage field. OPAQUE has the public key. */ int keygen_add_key_flags (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; do_add_key_flags (sig, pk->pubkey_usage); return 0; } static int keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque) { struct opaque_data_usage_and_pk *oduap = opaque; do_add_key_flags (sig, oduap->usage); return keygen_add_key_expire (sig, oduap->pk); } static int set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf) { int i; for (i=0; i < *nbuf; i++ ) if (buf[i] == val) { log_info (_("preference '%s' duplicated\n"), item); return -1; } if (*nbuf >= MAX_PREFS) { if(type==1) log_info(_("too many cipher preferences\n")); else if(type==2) log_info(_("too many digest preferences\n")); else if(type==3) log_info(_("too many compression preferences\n")); else if(type==4) log_info(_("too many AEAD preferences\n")); else BUG(); return -1; } buf[(*nbuf)++] = val; return 0; } /* * Parse the supplied string and use it to set the standard * preferences. The string may be in a form like the one printed by * "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual * cipher/hash/compress names. Use NULL to set the default * preferences. Returns: 0 = okay */ int keygen_set_std_prefs (const char *string,int personal) { byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS], aead[MAX_PREFS]; int nsym=0, nhash=0, nzip=0, naead=0, val, rc=0; int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */ char dummy_string[25*4+1]; /* Enough for 25 items. */ if (!string || !ascii_strcasecmp (string, "default")) { if (opt.def_preference_list) string=opt.def_preference_list; else { int any_compress = 0; dummy_string[0]='\0'; /* The rationale why we use the order AES256,192,128 is for compatibility reasons with PGP. If gpg would define AES128 first, we would get the somewhat confusing situation: gpg -r pgpkey -r gpgkey ---gives--> AES256 gpg -r gpgkey -r pgpkey ---gives--> AES Note that by using --personal-cipher-preferences it is possible to prefer AES128. */ /* Make sure we do not add more than 15 items here, as we could overflow the size of dummy_string. We currently have at most 12. */ if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES256) ) strcat(dummy_string,"S9 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES192) ) strcat(dummy_string,"S8 "); if ( !openpgp_cipher_test_algo (CIPHER_ALGO_AES) ) strcat(dummy_string,"S7 "); strcat(dummy_string,"S2 "); /* 3DES */ if (opt.flags.rfc4880bis && !openpgp_aead_test_algo (AEAD_ALGO_OCB)) strcat(dummy_string,"A2 "); if (opt.flags.rfc4880bis && !openpgp_aead_test_algo (AEAD_ALGO_EAX)) strcat(dummy_string,"A1 "); if (personal) { /* The default internal hash algo order is: * SHA-256, SHA-384, SHA-512, SHA-224, SHA-1. */ if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) strcat (dummy_string, "H8 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) strcat (dummy_string, "H9 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) strcat (dummy_string, "H10 "); } else { /* The default advertised hash algo order is: * SHA-512, SHA-384, SHA-256, SHA-224, SHA-1. */ if (!openpgp_md_test_algo (DIGEST_ALGO_SHA512)) strcat (dummy_string, "H10 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA384)) strcat (dummy_string, "H9 "); if (!openpgp_md_test_algo (DIGEST_ALGO_SHA256)) strcat (dummy_string, "H8 "); } if (!openpgp_md_test_algo (DIGEST_ALGO_SHA224)) strcat (dummy_string, "H11 "); strcat (dummy_string, "H2 "); /* SHA-1 */ if(!check_compress_algo(COMPRESS_ALGO_ZLIB)) { strcat(dummy_string,"Z2 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_BZIP2)) { strcat(dummy_string,"Z3 "); any_compress = 1; } if(!check_compress_algo(COMPRESS_ALGO_ZIP)) { strcat(dummy_string,"Z1 "); any_compress = 1; } /* In case we have no compress algo at all, declare that we prefer no compression. */ if (!any_compress) strcat(dummy_string,"Z0 "); /* Remove the trailing space. */ if (*dummy_string && dummy_string[strlen (dummy_string)-1] == ' ') dummy_string[strlen (dummy_string)-1] = 0; string=dummy_string; } } else if (!ascii_strcasecmp (string, "none")) string = ""; if(strlen(string)) { char *prefstringbuf; char *tok, *prefstring; /* We need a writable string. */ prefstring = prefstringbuf = xstrdup (string); while((tok=strsep(&prefstring," ,"))) { if((val=string_to_cipher_algo (tok))) { if(set_one_pref(val,1,tok,sym,&nsym)) rc=-1; } else if((val=string_to_digest_algo (tok))) { if(set_one_pref(val,2,tok,hash,&nhash)) rc=-1; } else if((val=string_to_compress_algo(tok))>-1) { if(set_one_pref(val,3,tok,zip,&nzip)) rc=-1; } else if ((val=string_to_aead_algo (tok))) { if (set_one_pref (val, 4, tok, aead, &naead)) rc = -1; } else if (ascii_strcasecmp(tok,"mdc")==0) mdc=1; else if (ascii_strcasecmp(tok,"no-mdc")==0) mdc=0; else if (ascii_strcasecmp(tok,"ks-modify")==0) modify=1; else if (ascii_strcasecmp(tok,"no-ks-modify")==0) modify=0; else { log_info (_("invalid item '%s' in preference string\n"),tok); rc=-1; } } xfree (prefstringbuf); } if(!rc) { if(personal) { if(personal==PREFTYPE_SYM) { xfree(opt.personal_cipher_prefs); if(nsym==0) opt.personal_cipher_prefs=NULL; else { int i; opt.personal_cipher_prefs= xmalloc(sizeof(prefitem_t *)*(nsym+1)); for (i=0; iref=1; uid->prefs = xmalloc ((sizeof(prefitem_t *)* (nsym_prefs+naead_prefs+nhash_prefs+nzip_prefs+1))); for(i=0;iprefs[j].type=PREFTYPE_SYM; uid->prefs[j].value=sym_prefs[i]; } for (i=0; i < naead_prefs; i++, j++) { uid->prefs[j].type = PREFTYPE_AEAD; uid->prefs[j].value = aead_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_HASH; uid->prefs[j].value=hash_prefs[i]; } for(i=0;iprefs[j].type=PREFTYPE_ZIP; uid->prefs[j].value=zip_prefs[i]; } uid->prefs[j].type=PREFTYPE_NONE; uid->prefs[j].value=0; uid->flags.mdc = mdc_available; uid->flags.aead = aead_available; uid->flags.ks_modify = ks_modify; return uid; } static void add_feature_mdc (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x01; /* MDC feature */ else buf[0] &= ~0x01; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_feature_aead (PKT_signature *sig, int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); if (s && n && ((enabled && (s[0] & 0x02)) || (!enabled && !(s[0] & 0x02)))) return; /* Already set or cleared */ if (!s || !n) { /* Create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if (enabled) buf[0] |= 0x02; /* AEAD supported */ else buf[0] &= ~0x02; /* Are there any bits set? */ for (i=0; i < n; i++) if (buf[i]) break; if (i == n) delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_feature_v5 (PKT_signature *sig, int enabled) { const byte *s; size_t n; int i; char *buf; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n ); if (s && n && ((enabled && (s[0] & 0x04)) || (!enabled && !(s[0] & 0x04)))) return; /* Already set or cleared */ if (!s || !n) { /* Create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if (enabled) buf[0] |= 0x04; /* v5 key supported */ else buf[0] &= ~0x04; /* Are there any bits set? */ for (i=0; i < n; i++) if (buf[i]) break; if (i == n) delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES); else build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n); xfree (buf); } static void add_keyserver_modify (PKT_signature *sig,int enabled) { const byte *s; size_t n; int i; char *buf; /* The keyserver modify flag is a negative flag (i.e. no-modify) */ enabled=!enabled; s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n ); /* Already set or cleared */ if (s && n && ((enabled && (s[0] & 0x80)) || (!enabled && !(s[0] & 0x80)))) return; if (!s || !n) { /* create a new one */ n = 1; buf = xmalloc_clear (n); } else { buf = xmalloc (n); memcpy (buf, s, n); } if(enabled) buf[0] |= 0x80; /* no-modify flag */ else buf[0] &= ~0x80; /* Are there any bits set? */ for(i=0;ihashed, SIGSUBPKT_KS_FLAGS); else build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, buf, n); xfree (buf); } int keygen_upd_std_prefs (PKT_signature *sig, void *opaque) { (void)opaque; if (!prefs_initialized) keygen_set_std_prefs (NULL, 0); if (nsym_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM); } if (naead_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_AEAD, aead_prefs, naead_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_AEAD); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_AEAD); } if (nhash_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH); } if (nzip_prefs) build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs); else { delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR); delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR); } /* Make sure that the MDC feature flag is set if needed. */ add_feature_mdc (sig,mdc_available); add_feature_aead (sig, aead_available); add_feature_v5 (sig, opt.flags.rfc4880bis); add_keyserver_modify (sig,ks_modify); keygen_add_keyserver_url(sig,NULL); return 0; } /**************** * Add preference to the self signature packet. * This is only called for packets with version > 3. */ int keygen_add_std_prefs (PKT_signature *sig, void *opaque) { PKT_public_key *pk = opaque; do_add_key_flags (sig, pk->pubkey_usage); keygen_add_key_expire (sig, opaque ); keygen_upd_std_prefs (sig, opaque); keygen_add_keyserver_url (sig,NULL); return 0; } int keygen_add_keyserver_url(PKT_signature *sig, void *opaque) { const char *url=opaque; if(!url) url=opt.def_keyserver_url; if(url) build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url)); else delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS); return 0; } int keygen_add_notations(PKT_signature *sig,void *opaque) { struct notation *notation; /* We always start clean */ delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION); delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION); sig->flags.notation=0; for(notation=opaque;notation;notation=notation->next) if(!notation->flags.ignore) { unsigned char *buf; unsigned int n1,n2; n1=strlen(notation->name); if(notation->altvalue) n2=strlen(notation->altvalue); else if(notation->bdat) n2=notation->blen; else n2=strlen(notation->value); buf = xmalloc( 8 + n1 + n2 ); /* human readable or not */ buf[0] = notation->bdat?0:0x80; buf[1] = buf[2] = buf[3] = 0; buf[4] = n1 >> 8; buf[5] = n1; buf[6] = n2 >> 8; buf[7] = n2; memcpy(buf+8, notation->name, n1 ); if(notation->altvalue) memcpy(buf+8+n1, notation->altvalue, n2 ); else if(notation->bdat) memcpy(buf+8+n1, notation->bdat, n2 ); else memcpy(buf+8+n1, notation->value, n2 ); build_sig_subpkt( sig, SIGSUBPKT_NOTATION | (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0), buf, 8+n1+n2 ); xfree(buf); } return 0; } int keygen_add_revkey (PKT_signature *sig, void *opaque) { struct revocation_key *revkey = opaque; byte buf[2+MAX_FINGERPRINT_LEN]; log_assert (revkey->fprlen <= MAX_FINGERPRINT_LEN); buf[0] = revkey->class; buf[1] = revkey->algid; memcpy (buf + 2, revkey->fpr, revkey->fprlen); memset (buf + 2 + revkey->fprlen, 0, sizeof (revkey->fpr) - revkey->fprlen); build_sig_subpkt (sig, SIGSUBPKT_REV_KEY, buf, 2+revkey->fprlen); /* All sigs with revocation keys set are nonrevocable. */ sig->flags.revocable = 0; buf[0] = 0; build_sig_subpkt (sig, SIGSUBPKT_REVOCABLE, buf, 1); parse_revkeys (sig); return 0; } /* Create a back-signature. If TIMESTAMP is not NULL, use it for the signature creation time. */ gpg_error_t make_backsig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PKT_signature *backsig; cache_public_key (sub_pk); err = make_keysig_packet (ctrl, &backsig, pk, NULL, sub_pk, sub_psk, 0x19, timestamp, 0, NULL, NULL, cache_nonce); if (err) log_error ("make_keysig_packet failed for backsig: %s\n", gpg_strerror (err)); else { /* Get it into a binary packed form. */ IOBUF backsig_out = iobuf_temp(); PACKET backsig_pkt; init_packet (&backsig_pkt); backsig_pkt.pkttype = PKT_SIGNATURE; backsig_pkt.pkt.signature = backsig; err = build_packet (backsig_out, &backsig_pkt); free_packet (&backsig_pkt, NULL); if (err) log_error ("build_packet failed for backsig: %s\n", gpg_strerror (err)); else { size_t pktlen = 0; byte *buf = iobuf_get_temp_buffer (backsig_out); /* Remove the packet header. */ if(buf[0]&0x40) { if (buf[1] < 192) { pktlen = buf[1]; buf += 2; } else if(buf[1] < 224) { pktlen = (buf[1]-192)*256; pktlen += buf[2]+192; buf += 3; } else if (buf[1] == 255) { pktlen = buf32_to_size_t (buf+2); buf += 6; } else BUG (); } else { int mark = 1; switch (buf[0]&3) { case 3: BUG (); break; case 2: pktlen = (size_t)buf[mark++] << 24; pktlen |= buf[mark++] << 16; /* fall through */ case 1: pktlen |= buf[mark++] << 8; /* fall through */ case 0: pktlen |= buf[mark++]; } buf += mark; } /* Now make the binary blob into a subpacket. */ build_sig_subpkt (sig, SIGSUBPKT_SIGNATURE, buf, pktlen); iobuf_close (backsig_out); } } return err; } /* Write a direct key signature to the first key in ROOT using the key PSK. REVKEY is describes the direct key signature and TIMESTAMP is the timestamp to set on the signature. */ static gpg_error_t write_direct_sig (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, struct revocation_key *revkey, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing direct signature\n")); /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG (); pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (ctrl, &sig, pk, NULL,NULL, psk, 0x1F, timestamp, 0, keygen_add_revkey, revkey, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err) ); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write a self-signature to the first user id in ROOT using the key PSK. USE and TIMESTAMP give the extra data we need for the signature. */ static gpg_error_t write_selfsigs (ctrl_t ctrl, kbnode_t root, PKT_public_key *psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; PKT_user_id *uid; KBNODE node; PKT_public_key *pk; if (opt.verbose) log_info (_("writing self signature\n")); /* Get the uid packet from the list. */ node = find_kbnode (root, PKT_USER_ID); if (!node) BUG(); /* No user id packet in tree. */ uid = node->pkt->pkt.user_id; /* Get the pk packet from the pub_tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pk = node->pkt->pkt.public_key; /* The usage has not yet been set - do it now. */ pk->pubkey_usage = use; /* We have to cache the key, so that the verification of the signature creation is able to retrieve the public key. */ cache_public_key (pk); /* Make the signature. */ err = make_keysig_packet (ctrl, &sig, pk, uid, NULL, psk, 0x13, timestamp, 0, keygen_add_std_prefs, pk, cache_nonce); if (err) { log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err)); return err; } pkt = xmalloc_clear (sizeof *pkt); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt)); return err; } /* Write the key binding signature. If TIMESTAMP is not NULL use the signature creation time. PRI_PSK is the key use for signing. SUB_PSK is a key used to create a back-signature; that one is only used if USE has the PUBKEY_USAGE_SIG capability. */ static int write_keybinding (ctrl_t ctrl, kbnode_t root, PKT_public_key *pri_psk, PKT_public_key *sub_psk, unsigned int use, u32 timestamp, const char *cache_nonce) { gpg_error_t err; PACKET *pkt; PKT_signature *sig; KBNODE node; PKT_public_key *pri_pk, *sub_pk; struct opaque_data_usage_and_pk oduap; if (opt.verbose) log_info(_("writing key binding signature\n")); /* Get the primary pk packet from the tree. */ node = find_kbnode (root, PKT_PUBLIC_KEY); if (!node) BUG(); pri_pk = node->pkt->pkt.public_key; /* We have to cache the key, so that the verification of the * signature creation is able to retrieve the public key. */ cache_public_key (pri_pk); /* Find the last subkey. */ sub_pk = NULL; for (node = root; node; node = node->next ) { if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; } if (!sub_pk) BUG(); /* Make the signature. */ oduap.usage = use; oduap.pk = sub_pk; err = make_keysig_packet (ctrl, &sig, pri_pk, NULL, sub_pk, pri_psk, 0x18, timestamp, 0, keygen_add_key_flags_and_expire, &oduap, cache_nonce); if (err) { log_error ("make_keysig_packeto failed: %s\n", gpg_strerror (err)); return err; } /* Make a backsig. */ if (use & PUBKEY_USAGE_SIG) { err = make_backsig (ctrl, sig, pri_pk, sub_pk, sub_psk, timestamp, cache_nonce); if (err) return err; } pkt = xmalloc_clear ( sizeof *pkt ); pkt->pkttype = PKT_SIGNATURE; pkt->pkt.signature = sig; add_kbnode (root, new_kbnode (pkt) ); return err; } static gpg_error_t ecckey_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, int algo) { gpg_error_t err; gcry_sexp_t list, l2; char *curve = NULL; int i; const char *oidstr; unsigned int nbits; array[0] = NULL; array[1] = NULL; array[2] = NULL; list = gcry_sexp_find_token (sexp, "public-key", 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); l2 = gcry_sexp_find_token (list, "curve", 0); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } curve = gcry_sexp_nth_string (l2, 1); if (!curve) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } gcry_sexp_release (l2); oidstr = openpgp_curve_to_oid (curve, &nbits); if (!oidstr) { /* That can't happen because we used one of the curves gpg_curve_to_oid knows about. */ err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } err = openpgp_oid_from_str (oidstr, &array[0]); if (err) goto leave; l2 = gcry_sexp_find_token (list, "q", 0); if (!l2) { err = gpg_error (GPG_ERR_NO_OBJ); goto leave; } array[1] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[1]) { err = gpg_error (GPG_ERR_INV_OBJ); goto leave; } gcry_sexp_release (list); if (algo == PUBKEY_ALGO_ECDH) { array[2] = pk_ecdh_default_params (nbits); if (!array[2]) { err = gpg_error_from_syserror (); goto leave; } } leave: xfree (curve); if (err) { for (i=0; i < 3; i++) { gcry_mpi_release (array[i]); array[i] = NULL; } } return err; } /* Extract key parameters from SEXP and store them in ARRAY. ELEMS is a string where each character denotes a parameter name. TOPNAME is the name of the top element above the elements. */ static int key_from_sexp (gcry_mpi_t *array, gcry_sexp_t sexp, const char *topname, const char *elems) { gcry_sexp_t list, l2; const char *s; int i, idx; int rc = 0; list = gcry_sexp_find_token (sexp, topname, 0); if (!list) return gpg_error (GPG_ERR_INV_OBJ); l2 = gcry_sexp_cadr (list); gcry_sexp_release (list); list = l2; if (!list) return gpg_error (GPG_ERR_NO_OBJ); for (idx=0,s=elems; *s; s++, idx++) { l2 = gcry_sexp_find_token (list, s, 1); if (!l2) { rc = gpg_error (GPG_ERR_NO_OBJ); /* required parameter not found */ goto leave; } array[idx] = gcry_sexp_nth_mpi (l2, 1, GCRYMPI_FMT_USG); gcry_sexp_release (l2); if (!array[idx]) { rc = gpg_error (GPG_ERR_INV_OBJ); /* required parameter invalid */ goto leave; } } gcry_sexp_release (list); leave: if (rc) { for (i=0; itimestamp = timestamp; pk->version = (keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); free_public_key (pk); return err; } gcry_sexp_release (s_key); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; } /* Common code for the key generation function gen_xxx. */ static int common_gen (const char *keyparms, int algo, const char *algoelem, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr) { int err; PACKET *pkt; PKT_public_key *pk; gcry_sexp_t s_key; err = agent_genkey (NULL, cache_nonce_addr, passwd_nonce_addr, keyparms, !!(keygen_flags & KEYGEN_FLAG_NO_PROTECTION), passphrase, &s_key); if (err) { log_error ("agent_genkey failed: %s\n", gpg_strerror (err) ); return err; } pk = xtrycalloc (1, sizeof *pk); if (!pk) { err = gpg_error_from_syserror (); gcry_sexp_release (s_key); return err; } pk->timestamp = timestamp; pk->version = (keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, algo); else err = key_from_sexp (pk->pkey, s_key, "public-key", algoelem); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); gcry_sexp_release (s_key); free_public_key (pk); return err; } gcry_sexp_release (s_key); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); free_public_key (pk); return err; } pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; } /* * Generate an Elgamal key. */ static int gen_elg (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr) { int err; char *keyparms; char nbitsstr[35]; log_assert (is_ELGAMAL (algo)); if (nbits < 1024) { nbits = 2048; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > 4096) { nbits = 4096; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } /* Note that we use transient-key only if no-protection has also been enabled. */ snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(%s(nbits %zu:%s)%s))", algo == GCRY_PK_ELG_E ? "openpgp-elg" : algo == GCRY_PK_ELG ? "elg" : "x-oops" , strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "pgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); xfree (keyparms); } return err; } /* * Generate an DSA key */ static gpg_error_t gen_dsa (unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr) { int err; unsigned int qbits; char *keyparms; char nbitsstr[35]; char qbitsstr[35]; if (nbits < 768) { nbits = 2048; log_info(_("keysize invalid; using %u bits\n"), nbits ); } else if ( nbits > 3072 ) { nbits = 3072; log_info(_("keysize invalid; using %u bits\n"), nbits ); } if( (nbits % 64) ) { nbits = ((nbits + 63) / 64) * 64; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* To comply with FIPS rules we round up to the next value unless in expert mode. */ if (!opt.expert && nbits > 1024 && (nbits % 1024)) { nbits = ((nbits + 1023) / 1024) * 1024; log_info(_("keysize rounded up to %u bits\n"), nbits ); } /* Figure out a q size based on the key size. FIPS 180-3 says: L = 1024, N = 160 L = 2048, N = 224 L = 2048, N = 256 L = 3072, N = 256 2048/256 is an odd pair since there is also a 2048/224 and 3072/256. Matching sizes is not a very exact science. We'll do 256 qbits for nbits over 2047, 224 for nbits over 1024 but less than 2048, and 160 for 1024 (DSA1). */ if (nbits > 2047) qbits = 256; else if ( nbits > 1024) qbits = 224; else qbits = 160; if (qbits != 160 ) log_info (_("WARNING: some OpenPGP programs can't" " handle a DSA key with this digest size\n")); snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); snprintf (qbitsstr, sizeof qbitsstr, "%u", qbits); keyparms = xtryasprintf ("(genkey(dsa(nbits %zu:%s)(qbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, strlen (qbitsstr), qbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, PUBKEY_ALGO_DSA, "pqgy", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); xfree (keyparms); } return err; } /* * Generate an ECC key */ static gpg_error_t gen_ecc (int algo, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr) { gpg_error_t err; char *keyparms; log_assert (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH); if (!curve || !*curve) return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* Map the displayed short forms of some curves to their canonical * names. */ if (!ascii_strcasecmp (curve, "cv25519")) curve = "Curve25519"; else if (!ascii_strcasecmp (curve, "ed25519")) curve = "Ed25519"; /* Note that we use the "comp" flag with EdDSA to request the use of a 0x40 compression prefix octet. */ if (algo == PUBKEY_ALGO_EDDSA) keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags eddsa comp%s)))", strlen (curve), curve, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); else if (algo == PUBKEY_ALGO_ECDH && !strcmp (curve, "Curve25519")) keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags djb-tweak comp%s)))", strlen (curve), curve, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); else keyparms = xtryasprintf ("(genkey(ecc(curve %zu:%s)(flags nocomp%s)))", strlen (curve), curve, (((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? " transient-key" : "")); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); xfree (keyparms); } return err; } /* * Generate an RSA key. */ static int gen_rsa (int algo, unsigned int nbits, KBNODE pub_root, u32 timestamp, u32 expireval, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr) { int err; char *keyparms; char nbitsstr[35]; const unsigned maxsize = (opt.flags.large_rsa ? 8192 : 4096); log_assert (is_RSA(algo)); if (!nbits) nbits = get_keysize_range (algo, NULL, NULL); if (nbits < 1024) { nbits = 3072; log_info (_("keysize invalid; using %u bits\n"), nbits ); } else if (nbits > maxsize) { nbits = maxsize; log_info (_("keysize invalid; using %u bits\n"), nbits ); } if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; log_info (_("keysize rounded up to %u bits\n"), nbits ); } snprintf (nbitsstr, sizeof nbitsstr, "%u", nbits); keyparms = xtryasprintf ("(genkey(rsa(nbits %zu:%s)%s))", strlen (nbitsstr), nbitsstr, ((keygen_flags & KEYGEN_FLAG_TRANSIENT_KEY) && (keygen_flags & KEYGEN_FLAG_NO_PROTECTION))? "(transient-key)" : "" ); if (!keyparms) err = gpg_error_from_syserror (); else { err = common_gen (keyparms, algo, "ne", pub_root, timestamp, expireval, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); xfree (keyparms); } return err; } /**************** * check valid days: * return 0 on error or the multiplier */ static int check_valid_days( const char *s ) { if( !digitp(s) ) return 0; for( s++; *s; s++) if( !digitp(s) ) break; if( !*s ) return 1; if( s[1] ) return 0; /* e.g. "2323wc" */ if( *s == 'd' || *s == 'D' ) return 1; if( *s == 'w' || *s == 'W' ) return 7; if( *s == 'm' || *s == 'M' ) return 30; if( *s == 'y' || *s == 'Y' ) return 365; return 0; } static void print_key_flags(int flags) { if(flags&PUBKEY_USAGE_SIG) tty_printf("%s ",_("Sign")); if(flags&PUBKEY_USAGE_CERT) tty_printf("%s ",_("Certify")); if(flags&PUBKEY_USAGE_ENC) tty_printf("%s ",_("Encrypt")); if(flags&PUBKEY_USAGE_AUTH) tty_printf("%s ",_("Authenticate")); } /* Ask for the key flags and return them. CURRENT gives the current * usage which should normally be given as 0. MASK gives the allowed * flags. */ unsigned int ask_key_flags_with_mask (int algo, int subkey, unsigned int current, unsigned int mask) { /* TRANSLATORS: Please use only plain ASCII characters for the * translation. If this is not possible use single digits. The * string needs to 8 bytes long. Here is a description of the * functions: * * s = Toggle signing capability * e = Toggle encryption capability * a = Toggle authentication capability * q = Finish */ const char *togglers = _("SsEeAaQq"); char *answer = NULL; const char *s; unsigned int possible; if ( strlen(togglers) != 8 ) { tty_printf ("NOTE: Bad translation at %s:%d. " "Please report.\n", __FILE__, __LINE__); togglers = "11223300"; } /* Mask the possible usage flags. This is for example used for a * card based key. */ possible = (openpgp_pk_algo_usage (algo) & mask); /* However, only primary keys may certify. */ if (subkey) possible &= ~PUBKEY_USAGE_CERT; /* Preload the current set with the possible set, without * authentication if CURRENT is 0. If CURRENT is non-zero we mask * with all possible usages. */ if (current) current &= possible; else current = (possible&~PUBKEY_USAGE_AUTH); for (;;) { tty_printf("\n"); tty_printf(_("Possible actions for this %s key: "), (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA) ? "ECDSA/EdDSA" : openpgp_pk_algo_name (algo)); print_key_flags(possible); tty_printf("\n"); tty_printf(_("Current allowed actions: ")); print_key_flags(current); tty_printf("\n\n"); if(possible&PUBKEY_USAGE_SIG) tty_printf(_(" (%c) Toggle the sign capability\n"), togglers[0]); if(possible&PUBKEY_USAGE_ENC) tty_printf(_(" (%c) Toggle the encrypt capability\n"), togglers[2]); if(possible&PUBKEY_USAGE_AUTH) tty_printf(_(" (%c) Toggle the authenticate capability\n"), togglers[4]); tty_printf(_(" (%c) Finished\n"),togglers[6]); tty_printf("\n"); xfree(answer); answer = cpr_get("keygen.flags",_("Your selection? ")); cpr_kill_prompt(); if (*answer == '=') { /* Hack to allow direct entry of the capabilities. */ current = 0; for (s=answer+1; *s; s++) { if ((*s == 's' || *s == 'S') && (possible&PUBKEY_USAGE_SIG)) current |= PUBKEY_USAGE_SIG; else if ((*s == 'e' || *s == 'E') && (possible&PUBKEY_USAGE_ENC)) current |= PUBKEY_USAGE_ENC; else if ((*s == 'a' || *s == 'A') && (possible&PUBKEY_USAGE_AUTH)) current |= PUBKEY_USAGE_AUTH; else if (!subkey && *s == 'c') { /* Accept 'c' for the primary key because USAGE_CERT will be set anyway. This is for folks who want to experiment with a cert-only primary key. */ current |= PUBKEY_USAGE_CERT; } } break; } else if (strlen(answer)>1) tty_printf(_("Invalid selection.\n")); else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7]) break; else if((*answer==togglers[0] || *answer==togglers[1]) && possible&PUBKEY_USAGE_SIG) { if(current&PUBKEY_USAGE_SIG) current&=~PUBKEY_USAGE_SIG; else current|=PUBKEY_USAGE_SIG; } else if((*answer==togglers[2] || *answer==togglers[3]) && possible&PUBKEY_USAGE_ENC) { if(current&PUBKEY_USAGE_ENC) current&=~PUBKEY_USAGE_ENC; else current|=PUBKEY_USAGE_ENC; } else if((*answer==togglers[4] || *answer==togglers[5]) && possible&PUBKEY_USAGE_AUTH) { if(current&PUBKEY_USAGE_AUTH) current&=~PUBKEY_USAGE_AUTH; else current|=PUBKEY_USAGE_AUTH; } else tty_printf(_("Invalid selection.\n")); } xfree(answer); return current; } unsigned int ask_key_flags (int algo, int subkey, unsigned int current) { return ask_key_flags_with_mask (algo, subkey, current, ~0); } /* Check whether we have a key for the key with HEXGRIP. Returns 0 if there is no such key or the OpenPGP algo number for the key. */ static int check_keygrip (ctrl_t ctrl, const char *hexgrip) { gpg_error_t err; unsigned char *public; size_t publiclen; int algo; if (hexgrip[0] == '&') hexgrip++; err = agent_readkey (ctrl, 0, hexgrip, &public); if (err) return 0; publiclen = gcry_sexp_canon_len (public, 0, NULL, NULL); algo = get_pk_algo_from_canon_sexp (public, publiclen); xfree (public); return map_pk_gcry_to_openpgp (algo); } /* Ask for an algorithm. The function returns the algorithm id to * create. If ADDMODE is false the function won't show an option to * create the primary and subkey combined and won't set R_USAGE * either. If a combined algorithm has been selected, the subkey * algorithm is stored at R_SUBKEY_ALGO. If R_KEYGRIP is given, the * user has the choice to enter the keygrip of an existing key. That * keygrip is then stored at this address. The caller needs to free * it. */ static int ask_algo (ctrl_t ctrl, int addmode, int *r_subkey_algo, unsigned int *r_usage, char **r_keygrip) { gpg_error_t err; char *keygrip = NULL; char *answer = NULL; int algo; int dummy_algo; char *p; if (!r_subkey_algo) r_subkey_algo = &dummy_algo; tty_printf (_("Please select what kind of key you want:\n")); #if GPG_USE_RSA if (!addmode) tty_printf (_(" (%d) RSA and RSA (default)\n"), 1 ); #endif if (!addmode && opt.compliance != CO_DE_VS) tty_printf (_(" (%d) DSA and Elgamal\n"), 2 ); if (opt.compliance != CO_DE_VS) tty_printf (_(" (%d) DSA (sign only)\n"), 3 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (sign only)\n"), 4 ); #endif if (addmode) { if (opt.compliance != CO_DE_VS) tty_printf (_(" (%d) Elgamal (encrypt only)\n"), 5 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (encrypt only)\n"), 6 ); #endif } if (opt.expert) { if (opt.compliance != CO_DE_VS) tty_printf (_(" (%d) DSA (set your own capabilities)\n"), 7 ); #if GPG_USE_RSA tty_printf (_(" (%d) RSA (set your own capabilities)\n"), 8 ); #endif } #if GPG_USE_ECDSA || GPG_USE_ECDH || GPG_USE_EDDSA if (opt.expert && !addmode) tty_printf (_(" (%d) ECC and ECC\n"), 9 ); if (opt.expert) tty_printf (_(" (%d) ECC (sign only)\n"), 10 ); if (opt.expert) tty_printf (_(" (%d) ECC (set your own capabilities)\n"), 11 ); if (opt.expert && addmode) tty_printf (_(" (%d) ECC (encrypt only)\n"), 12 ); #endif if (opt.expert && r_keygrip) tty_printf (_(" (%d) Existing key\n"), 13 ); if (r_keygrip) tty_printf (_(" (%d) Existing key from card\n"), 14 ); for (;;) { *r_usage = 0; *r_subkey_algo = 0; xfree (answer); answer = cpr_get ("keygen.algo", _("Your selection? ")); cpr_kill_prompt (); algo = *answer? atoi (answer) : 1; if (opt.compliance == CO_DE_VS && (algo == 2 || algo == 3 || algo == 5 || algo == 7)) { tty_printf (_("Invalid selection.\n")); } else if ((algo == 1 || !strcmp (answer, "rsa+rsa")) && !addmode) { algo = PUBKEY_ALGO_RSA; *r_subkey_algo = PUBKEY_ALGO_RSA; break; } else if ((algo == 2 || !strcmp (answer, "dsa+elg")) && !addmode) { algo = PUBKEY_ALGO_DSA; *r_subkey_algo = PUBKEY_ALGO_ELGAMAL_E; break; } else if (algo == 3 || !strcmp (answer, "dsa")) { algo = PUBKEY_ALGO_DSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if (algo == 4 || !strcmp (answer, "rsa/s")) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if ((algo == 5 || !strcmp (answer, "elg")) && addmode) { algo = PUBKEY_ALGO_ELGAMAL_E; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 6 || !strcmp (answer, "rsa/e")) && addmode) { algo = PUBKEY_ALGO_RSA; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 7 || !strcmp (answer, "dsa/*")) && opt.expert) { algo = PUBKEY_ALGO_DSA; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 8 || !strcmp (answer, "rsa/*")) && opt.expert) { algo = PUBKEY_ALGO_RSA; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 9 || !strcmp (answer, "ecc+ecc")) && opt.expert && !addmode) { algo = PUBKEY_ALGO_ECDSA; *r_subkey_algo = PUBKEY_ALGO_ECDH; break; } else if ((algo == 10 || !strcmp (answer, "ecc/s")) && opt.expert) { algo = PUBKEY_ALGO_ECDSA; *r_usage = PUBKEY_USAGE_SIG; break; } else if ((algo == 11 || !strcmp (answer, "ecc/*")) && opt.expert) { algo = PUBKEY_ALGO_ECDSA; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 12 || !strcmp (answer, "ecc/e")) && opt.expert && addmode) { algo = PUBKEY_ALGO_ECDH; *r_usage = PUBKEY_USAGE_ENC; break; } else if ((algo == 13 || !strcmp (answer, "keygrip")) && opt.expert && r_keygrip) { for (;;) { xfree (answer); answer = tty_get (_("Enter the keygrip: ")); tty_kill_prompt (); trim_spaces (answer); if (!*answer) { xfree (answer); answer = NULL; continue; } if (strlen (answer) != 40 && !(answer[0] == '&' && strlen (answer+1) == 40)) tty_printf (_("Not a valid keygrip (expecting 40 hex digits)\n")); else if (!(algo = check_keygrip (ctrl, answer)) ) tty_printf (_("No key with this keygrip\n")); else break; /* Okay. */ } xfree (keygrip); keygrip = answer; answer = NULL; *r_usage = ask_key_flags (algo, addmode, 0); break; } else if ((algo == 14 || !strcmp (answer, "cardkey")) && r_keygrip) { char *serialno; strlist_t keypairlist, sl; int count, selection; err = agent_scd_serialno (&serialno, NULL); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto ask_again; } tty_printf (_("Serial number of the card: %s\n"), serialno); xfree (serialno); err = agent_scd_keypairinfo (ctrl, NULL, &keypairlist); if (err) { tty_printf (_("error reading the card: %s\n"), gpg_strerror (err)); goto ask_again; } do { tty_printf (_("Available keys:\n")); for (count=1,sl=keypairlist; sl; sl = sl->next, count++) { gcry_sexp_t s_pkey; char *algostr = NULL; enum gcry_pk_algos algoid = 0; const char *keyref; int any = 0; keyref = strchr (sl->d, ' '); if (keyref) { keyref++; if (!agent_scd_readkey (keyref, &s_pkey)) { algostr = pubkey_algo_string (s_pkey, &algoid); gcry_sexp_release (s_pkey); } } /* We use the flags also encode the algo for use * below. We need to tweak the algo in case * GCRY_PK_ECC is returned becuase pubkey_algo_string * is not aware of the OpenPGP algo mapping. * FIXME: This is an ugly hack. */ sl->flags &= 0xff; if (algoid == GCRY_PK_ECC && algostr && !strncmp (algostr, "nistp", 5) && !(sl->flags & GCRY_PK_USAGE_ENCR)) sl->flags |= (PUBKEY_ALGO_ECDSA << 8); else sl->flags |= (map_pk_gcry_to_openpgp (algoid) << 8); tty_printf (" (%d) %s %s", count, sl->d, algostr); if ((sl->flags & GCRY_PK_USAGE_CERT)) { tty_printf ("%scert", any?",":" ("); any = 1; } if ((sl->flags & GCRY_PK_USAGE_SIGN)) { tty_printf ("%ssign", any?",":" ("); any = 1; } if ((sl->flags & GCRY_PK_USAGE_AUTH)) { tty_printf ("%sauth", any?",":" ("); any = 1; } if ((sl->flags & GCRY_PK_USAGE_ENCR)) { tty_printf ("%sencr", any?",":" ("); any = 1; } tty_printf ("%s\n", any?")":""); xfree (algostr); } xfree (answer); answer = cpr_get ("keygen.cardkey", _("Your selection? ")); cpr_kill_prompt (); trim_spaces (answer); selection = atoi (answer); } while (!(selection > 0 && selection < count)); for (count=1,sl=keypairlist; sl; sl = sl->next, count++) if (count == selection) break; if (!sl) { /* Just in case COUNT is zero (no keys). */ free_strlist (keypairlist); goto ask_again; } xfree (keygrip); keygrip = xstrdup (sl->d); if ((p = strchr (keygrip, ' '))) *p = 0; algo = (sl->flags >>8); if (opt.expert) *r_usage = ask_key_flags_with_mask (algo, addmode, (sl->flags & 0xff), (sl->flags & 0xff)); else { *r_usage = (sl->flags & 0xff); if (addmode) *r_usage &= ~GCRY_PK_USAGE_CERT; } free_strlist (keypairlist); break; } else tty_printf (_("Invalid selection.\n")); ask_again: ; } xfree(answer); if (r_keygrip) *r_keygrip = keygrip; return algo; } static unsigned int get_keysize_range (int algo, unsigned int *min, unsigned int *max) { unsigned int def; unsigned int dummy1, dummy2; if (!min) min = &dummy1; if (!max) max = &dummy2; switch(algo) { case PUBKEY_ALGO_DSA: *min = opt.expert? 768 : 1024; *max=3072; def=2048; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_ECDH: *min=256; *max=521; def=256; break; case PUBKEY_ALGO_EDDSA: *min=255; *max=441; def=255; break; default: *min = opt.compliance == CO_DE_VS ? 2048: 1024; *max = 4096; def = 3072; break; } return def; } /* Return a fixed up keysize depending on ALGO. */ static unsigned int fixup_keysize (unsigned int nbits, int algo, int silent) { if (algo == PUBKEY_ALGO_DSA && (nbits % 64)) { nbits = ((nbits + 63) / 64) * 64; if (!silent) tty_printf (_("rounded up to %u bits\n"), nbits); } else if (algo == PUBKEY_ALGO_EDDSA) { if (nbits != 255 && nbits != 441) { if (nbits < 256) nbits = 255; else nbits = 441; if (!silent) tty_printf (_("rounded to %u bits\n"), nbits); } } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) { if (nbits != 256 && nbits != 384 && nbits != 521) { if (nbits < 256) nbits = 256; else if (nbits < 384) nbits = 384; else nbits = 521; if (!silent) tty_printf (_("rounded to %u bits\n"), nbits); } } else if ((nbits % 32)) { nbits = ((nbits + 31) / 32) * 32; if (!silent) tty_printf (_("rounded up to %u bits\n"), nbits ); } return nbits; } /* Ask for the key size. ALGO is the algorithm. If PRIMARY_KEYSIZE is not 0, the function asks for the size of the encryption subkey. */ static unsigned ask_keysize (int algo, unsigned int primary_keysize) { unsigned int nbits; unsigned int min, def, max; int for_subkey = !!primary_keysize; int autocomp = 0; def = get_keysize_range (algo, &min, &max); if (primary_keysize && !opt.expert) { /* Deduce the subkey size from the primary key size. */ if (algo == PUBKEY_ALGO_DSA && primary_keysize > 3072) nbits = 3072; /* For performance reasons we don't support more than 3072 bit DSA. However we won't see this case anyway because DSA can't be used as an encryption subkey ;-). */ else nbits = primary_keysize; autocomp = 1; goto leave; } tty_printf(_("%s keys may be between %u and %u bits long.\n"), openpgp_pk_algo_name (algo), min, max); for (;;) { char *prompt, *answer; if (for_subkey) prompt = xasprintf (_("What keysize do you want " "for the subkey? (%u) "), def); else prompt = xasprintf (_("What keysize do you want? (%u) "), def); answer = cpr_get ("keygen.size", prompt); cpr_kill_prompt (); nbits = *answer? atoi (answer): def; xfree(prompt); xfree(answer); if(nbitsmax) tty_printf(_("%s keysizes must be in the range %u-%u\n"), openpgp_pk_algo_name (algo), min, max); else break; } tty_printf (_("Requested keysize is %u bits\n"), nbits); leave: nbits = fixup_keysize (nbits, algo, autocomp); return nbits; } /* Ask for the curve. ALGO is the selected algorithm which this function may adjust. Returns a const string of the name of the curve. */ const char * ask_curve (int *algo, int *subkey_algo, const char *current) { /* NB: We always use a complete algo list so that we have stable numbers in the menu regardless on how Gpg was configured. */ struct { const char *name; const char* eddsa_curve; /* Corresponding EdDSA curve. */ const char *pretty_name; unsigned int supported : 1; /* Supported by gpg. */ unsigned int de_vs : 1; /* Allowed in CO_DE_VS. */ unsigned int expert_only : 1; /* Only with --expert */ unsigned int available : 1; /* Available in Libycrypt (runtime checked) */ } curves[] = { #if GPG_USE_ECDSA || GPG_USE_ECDH # define MY_USE_ECDSADH 1 #else # define MY_USE_ECDSADH 0 #endif { "Curve25519", "Ed25519", "Curve 25519", !!GPG_USE_EDDSA, 0, 0, 0 }, { "Curve448", "Ed448", "Curve 448", 0/*reserved*/ , 0, 1, 0 }, { "NIST P-256", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, { "NIST P-384", NULL, NULL, MY_USE_ECDSADH, 0, 0, 0 }, { "NIST P-521", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, { "brainpoolP256r1", NULL, "Brainpool P-256", MY_USE_ECDSADH, 1, 1, 0 }, { "brainpoolP384r1", NULL, "Brainpool P-384", MY_USE_ECDSADH, 1, 1, 0 }, { "brainpoolP512r1", NULL, "Brainpool P-512", MY_USE_ECDSADH, 1, 1, 0 }, { "secp256k1", NULL, NULL, MY_USE_ECDSADH, 0, 1, 0 }, }; #undef MY_USE_ECDSADH int idx; char *answer; const char *result = NULL; gcry_sexp_t keyparms; tty_printf (_("Please select which elliptic curve you want:\n")); keyparms = NULL; for (idx=0; idx < DIM(curves); idx++) { int rc; curves[idx].available = 0; if (!curves[idx].supported) continue; if (opt.compliance==CO_DE_VS) { if (!curves[idx].de_vs) continue; /* Not allowed. */ } else if (!opt.expert && curves[idx].expert_only) continue; /* We need to switch from the ECDH name of the curve to the EDDSA name of the curve if we want a signing key. */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", curves[idx].eddsa_curve? curves[idx].eddsa_curve /**/ : curves[idx].name); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; if (subkey_algo && curves[idx].eddsa_curve) { /* Both Curve 25519 (or 448) keys are to be created. Check that Libgcrypt also supports the real Curve25519 (or 448). */ gcry_sexp_release (keyparms); rc = gcry_sexp_build (&keyparms, NULL, "(public-key(ecc(curve %s)))", curves[idx].name); if (rc) continue; if (!gcry_pk_get_curve (keyparms, 0, NULL)) continue; } curves[idx].available = 1; tty_printf (" (%d) %s\n", idx + 1, curves[idx].pretty_name? curves[idx].pretty_name:curves[idx].name); } gcry_sexp_release (keyparms); for (;;) { answer = cpr_get ("keygen.curve", _("Your selection? ")); cpr_kill_prompt (); idx = *answer? atoi (answer) : 1; if (!*answer && current) { xfree(answer); return NULL; } else if (*answer && !idx) { /* See whether the user entered the name of the curve. */ for (idx=0; idx < DIM(curves); idx++) { if (!opt.expert && curves[idx].expert_only) continue; if (!stricmp (curves[idx].name, answer) || (curves[idx].pretty_name && !stricmp (curves[idx].pretty_name, answer))) break; } if (idx == DIM(curves)) idx = -1; } else idx--; xfree(answer); answer = NULL; if (idx < 0 || idx >= DIM (curves) || !curves[idx].available) tty_printf (_("Invalid selection.\n")); else { /* If the user selected a signing algorithm and Curve25519 we need to set the algo to EdDSA and update the curve name. If switching away from EdDSA, we need to set the algo back to ECDSA. */ if (*algo == PUBKEY_ALGO_ECDSA || *algo == PUBKEY_ALGO_EDDSA) { if (curves[idx].eddsa_curve) { if (subkey_algo && *subkey_algo == PUBKEY_ALGO_ECDSA) *subkey_algo = PUBKEY_ALGO_EDDSA; *algo = PUBKEY_ALGO_EDDSA; result = curves[idx].eddsa_curve; } else { if (subkey_algo && *subkey_algo == PUBKEY_ALGO_EDDSA) *subkey_algo = PUBKEY_ALGO_ECDSA; *algo = PUBKEY_ALGO_ECDSA; result = curves[idx].name; } } else result = curves[idx].name; break; } } if (!result) result = curves[0].name; return result; } /**************** * Parse an expire string and return its value in seconds. * Returns (u32)-1 on error. * This isn't perfect since scan_isodatestr returns unix time, and * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset. * Because of this, we only permit setting expirations up to 2106, but * OpenPGP could theoretically allow up to 2242. I think we'll all * just cope for the next few years until we get a 64-bit time_t or * similar. */ u32 parse_expire_string( const char *string ) { int mult; u32 seconds; u32 abs_date = 0; u32 curtime = make_timestamp (); time_t tt; if (!string || !*string || !strcmp (string, "none") || !strcmp (string, "never") || !strcmp (string, "-")) seconds = 0; else if (!strncmp (string, "seconds=", 8)) seconds = atoi (string+8); else if ((abs_date = scan_isodatestr(string)) && (abs_date+86400/2) > curtime) seconds = (abs_date+86400/2) - curtime; else if ((tt = isotime2epoch (string)) != (time_t)(-1)) seconds = (u32)tt - curtime; else if ((mult = check_valid_days (string))) seconds = atoi (string) * 86400L * mult; else seconds = (u32)(-1); return seconds; } /* Parse a Creation-Date string which is either "1986-04-26" or "19860426T042640". Returns 0 on error. */ static u32 parse_creation_string (const char *string) { u32 seconds; if (!*string) seconds = 0; else if ( !strncmp (string, "seconds=", 8) ) seconds = atoi (string+8); else if ( !(seconds = scan_isodatestr (string))) { time_t tmp = isotime2epoch (string); seconds = (tmp == (time_t)(-1))? 0 : tmp; } return seconds; } /* object == 0 for a key, and 1 for a sig */ u32 ask_expire_interval(int object,const char *def_expire) { u32 interval; char *answer; switch(object) { case 0: if(def_expire) BUG(); tty_printf(_("Please specify how long the key should be valid.\n" " 0 = key does not expire\n" " = key expires in n days\n" " w = key expires in n weeks\n" " m = key expires in n months\n" " y = key expires in n years\n")); break; case 1: if(!def_expire) BUG(); tty_printf(_("Please specify how long the signature should be valid.\n" " 0 = signature does not expire\n" " = signature expires in n days\n" " w = signature expires in n weeks\n" " m = signature expires in n months\n" " y = signature expires in n years\n")); break; default: BUG(); } /* Note: The elgamal subkey for DSA has no expiration date because * it must be signed with the DSA key and this one has the expiration * date */ answer = NULL; for(;;) { u32 curtime; xfree(answer); if(object==0) answer = cpr_get("keygen.valid",_("Key is valid for? (0) ")); else { char *prompt; prompt = xasprintf (_("Signature is valid for? (%s) "), def_expire); answer = cpr_get("siggen.valid",prompt); xfree(prompt); if(*answer=='\0') answer=xstrdup(def_expire); } cpr_kill_prompt(); trim_spaces(answer); curtime = make_timestamp (); interval = parse_expire_string( answer ); if( interval == (u32)-1 ) { tty_printf(_("invalid value\n")); continue; } if( !interval ) { tty_printf((object==0) ? _("Key does not expire at all\n") : _("Signature does not expire at all\n")); } else { tty_printf(object==0 ? _("Key expires at %s\n") : _("Signature expires at %s\n"), asctimestamp((ulong)(curtime + interval) ) ); #if SIZEOF_TIME_T <= 4 && !defined (HAVE_UNSIGNED_TIME_T) if ( (time_t)((ulong)(curtime+interval)) < 0 ) tty_printf (_("Your system can't display dates beyond 2038.\n" "However, it will be correctly handled up to" " 2106.\n")); else #endif /*SIZEOF_TIME_T*/ if ( (time_t)((unsigned long)(curtime+interval)) < curtime ) { tty_printf (_("invalid value\n")); continue; } } if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay", _("Is this correct? (y/N) ")) ) break; } xfree(answer); return interval; } u32 ask_expiredate() { u32 x = ask_expire_interval(0,NULL); return x? make_timestamp() + x : 0; } static PKT_user_id * uid_from_string (const char *string) { size_t n; PKT_user_id *uid; n = strlen (string); uid = xmalloc_clear (sizeof *uid + n); uid->len = n; strcpy (uid->name, string); uid->ref = 1; return uid; } /* Return true if the user id UID already exists in the keyblock. */ static int uid_already_in_keyblock (kbnode_t keyblock, const char *uid) { PKT_user_id *uidpkt = uid_from_string (uid); kbnode_t node; int result = 0; for (node=keyblock; node && !result; node=node->next) if (!is_deleted_kbnode (node) && node->pkt->pkttype == PKT_USER_ID && !cmp_user_ids (uidpkt, node->pkt->pkt.user_id)) result = 1; free_user_id (uidpkt); return result; } /* Ask for a user ID. With a MODE of 1 an extra help prompt is printed for use during a new key creation. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. IF FULL is not set some prompts are not shown. */ static char * ask_user_id (int mode, int full, KBNODE keyblock) { char *answer; char *aname, *acomment, *amail, *uid; if ( !mode ) { /* TRANSLATORS: This is the new string telling the user what gpg is now going to do (i.e. ask for the parts of the user ID). Note that if you do not translate this string, a different string will be used, which might still have a correct translation. */ const char *s1 = N_("\n" "GnuPG needs to construct a user ID to identify your key.\n" "\n"); const char *s2 = _(s1); if (!strcmp (s1, s2)) { /* There is no translation for the string thus we to use the old info text. gettext has no way to tell whether a translation is actually available, thus we need to to compare again. */ /* TRANSLATORS: This string is in general not anymore used but you should keep your existing translation. In case the new string is not translated this old string will be used. */ const char *s3 = N_("\n" "You need a user ID to identify your key; " "the software constructs the user ID\n" "from the Real Name, Comment and Email Address in this form:\n" " \"Heinrich Heine (Der Dichter) \"\n\n"); const char *s4 = _(s3); if (strcmp (s3, s4)) s2 = s3; /* A translation exists - use it. */ } tty_printf ("%s", s2) ; } uid = aname = acomment = amail = NULL; for(;;) { char *p; int fail=0; if( !aname ) { for(;;) { xfree(aname); aname = cpr_get("keygen.name",_("Real name: ")); trim_spaces(aname); cpr_kill_prompt(); if( opt.allow_freeform_uid ) break; if( strpbrk( aname, "<>" ) ) { tty_printf(_("Invalid character in name\n")); tty_printf(_("The characters '%s' and '%s' may not " "appear in name\n"), "<", ">"); } else if( digitp(aname) ) tty_printf(_("Name may not start with a digit\n")); else if (*aname && strlen (aname) < 5) { tty_printf(_("Name must be at least 5 characters long\n")); /* However, we allow an empty name. */ } else break; } } if( !amail ) { for(;;) { xfree(amail); amail = cpr_get("keygen.email",_("Email address: ")); trim_spaces(amail); cpr_kill_prompt(); if( !*amail || opt.allow_freeform_uid ) break; /* no email address is okay */ else if ( !is_valid_mailbox (amail) ) tty_printf(_("Not a valid email address\n")); else break; } } if (!acomment) { if (full) { for(;;) { xfree(acomment); acomment = cpr_get("keygen.comment",_("Comment: ")); trim_spaces(acomment); cpr_kill_prompt(); if( !*acomment ) break; /* no comment is okay */ else if( strpbrk( acomment, "()" ) ) tty_printf(_("Invalid character in comment\n")); else break; } } else { xfree (acomment); acomment = xstrdup (""); } } xfree(uid); uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10); if (!*aname && *amail && !*acomment && !random_is_faked ()) { /* Empty name and comment but with mail address. Use simplified form with only the non-angle-bracketed mail address. */ p = stpcpy (p, amail); } else { p = stpcpy (p, aname ); if (*acomment) p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")"); if (*amail) p = stpcpy(stpcpy(stpcpy(p," <"), amail),">"); } /* Append a warning if the RNG is switched into fake mode. */ if ( random_is_faked () ) strcpy(p, " (insecure!)" ); /* print a note in case that UTF8 mapping has to be done */ for(p=uid; *p; p++ ) { if( *p & 0x80 ) { tty_printf(_("You are using the '%s' character set.\n"), get_native_charset() ); break; } } tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid); if( !*amail && !opt.allow_freeform_uid && (strchr( aname, '@' ) || strchr( acomment, '@'))) { fail = 1; tty_printf(_("Please don't put the email address " "into the real name or the comment\n") ); } if (!fail && keyblock) { if (uid_already_in_keyblock (keyblock, uid)) { tty_printf (_("Such a user ID already exists on this key!\n")); fail = 1; } } for(;;) { /* TRANSLATORS: These are the allowed answers in lower and uppercase. Below you will find the matching string which should be translated accordingly and the letter changed to match the one in the answer string. n = Change name c = Change comment e = Change email o = Okay (ready, continue) q = Quit */ const char *ansstr = _("NnCcEeOoQq"); if( strlen(ansstr) != 10 ) BUG(); if( cpr_enabled() ) { answer = xstrdup (ansstr + (fail?8:6)); answer[1] = 0; } else if (full) { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") : _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } else { answer = cpr_get("keygen.userid.cmd", fail? _("Change (N)ame, (E)mail, or (Q)uit? ") : _("Change (N)ame, (E)mail, or (O)kay/(Q)uit? ")); cpr_kill_prompt(); } if( strlen(answer) > 1 ) ; else if( *answer == ansstr[0] || *answer == ansstr[1] ) { xfree(aname); aname = NULL; break; } else if( *answer == ansstr[2] || *answer == ansstr[3] ) { xfree(acomment); acomment = NULL; break; } else if( *answer == ansstr[4] || *answer == ansstr[5] ) { xfree(amail); amail = NULL; break; } else if( *answer == ansstr[6] || *answer == ansstr[7] ) { if( fail ) { tty_printf(_("Please correct the error first\n")); } else { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; break; } } else if( *answer == ansstr[8] || *answer == ansstr[9] ) { xfree(aname); aname = NULL; xfree(acomment); acomment = NULL; xfree(amail); amail = NULL; xfree(uid); uid = NULL; break; } xfree(answer); } xfree(answer); if (!amail && !acomment) break; xfree(uid); uid = NULL; } if( uid ) { char *p = native_to_utf8( uid ); xfree( uid ); uid = p; } return uid; } /* Basic key generation. Here we divert to the actual generation routines based on the requested algorithm. */ static int do_create (int algo, unsigned int nbits, const char *curve, kbnode_t pub_root, u32 timestamp, u32 expiredate, int is_subkey, int keygen_flags, const char *passphrase, char **cache_nonce_addr, char **passwd_nonce_addr) { gpg_error_t err; /* Fixme: The entropy collecting message should be moved to a libgcrypt progress handler. */ if (!opt.batch) tty_printf (_( "We need to generate a lot of random bytes. It is a good idea to perform\n" "some other action (type on the keyboard, move the mouse, utilize the\n" "disks) during the prime generation; this gives the random number\n" "generator a better chance to gain enough entropy.\n") ); if (algo == PUBKEY_ALGO_ELGAMAL_E) err = gen_elg (algo, nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); else if (algo == PUBKEY_ALGO_DSA) err = gen_dsa (nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) err = gen_ecc (algo, curve, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); else if (algo == PUBKEY_ALGO_RSA) err = gen_rsa (algo, nbits, pub_root, timestamp, expiredate, is_subkey, keygen_flags, passphrase, cache_nonce_addr, passwd_nonce_addr); else BUG(); return err; } /* Generate a new user id packet or return NULL if canceled. If KEYBLOCK is not NULL the function prevents the creation of an already existing user ID. If UIDSTR is not NULL the user is not asked but UIDSTR is used to create the user id packet; if the user id already exists NULL is returned. UIDSTR is expected to be utf-8 encoded and should have already been checked for a valid length etc. */ PKT_user_id * generate_user_id (KBNODE keyblock, const char *uidstr) { PKT_user_id *uid; char *p; if (uidstr) { if (uid_already_in_keyblock (keyblock, uidstr)) return NULL; /* Already exists. */ uid = uid_from_string (uidstr); } else { p = ask_user_id (1, 1, keyblock); if (!p) return NULL; /* Canceled. */ uid = uid_from_string (p); xfree (p); } return uid; } /* Helper for parse_key_parameter_string for one part of the * specification string; i.e. ALGO/FLAGS. If STRING is NULL or empty * success is returned. On error an error code is returned. Note * that STRING may be modified by this function. NULL may be passed * for any parameter. FOR_SUBKEY shall be true if this is used as a * subkey. If CLEAR_CERT is set a default CERT usage will be cleared; * this is useful if for example the default algorithm is used for a * subkey. If R_KEYVERSION is not NULL it will receive the version of * the key; this is currently 4 but can be changed with the flag "v5" * to create a v5 key. */ static gpg_error_t parse_key_parameter_part (char *string, int for_subkey, int clear_cert, int *r_algo, unsigned int *r_size, unsigned int *r_keyuse, char const **r_curve, int *r_keyversion) { char *flags; int algo; char *endp; const char *curve = NULL; int ecdh_or_ecdsa = 0; unsigned int size; int keyuse; int keyversion = 4; int i; const char *s; if (!string || !*string) return 0; /* Success. */ flags = strchr (string, '/'); if (flags) *flags++ = 0; algo = 0; if (strlen (string) >= 3 && (digitp (string+3) || !string[3])) { if (!ascii_memcasecmp (string, "rsa", 3)) algo = PUBKEY_ALGO_RSA; else if (!ascii_memcasecmp (string, "dsa", 3)) algo = PUBKEY_ALGO_DSA; else if (!ascii_memcasecmp (string, "elg", 3)) algo = PUBKEY_ALGO_ELGAMAL_E; } if (algo) { if (!string[3]) size = get_keysize_range (algo, NULL, NULL); else { size = strtoul (string+3, &endp, 10); if (size < 512 || size > 16384 || *endp) return gpg_error (GPG_ERR_INV_VALUE); } } else if ((curve = openpgp_is_curve_supported (string, &algo, &size))) { if (!algo) { algo = PUBKEY_ALGO_ECDH; /* Default ECC algorithm. */ ecdh_or_ecdsa = 1; /* We may need to switch the algo. */ } } else return gpg_error (GPG_ERR_UNKNOWN_CURVE); /* Parse the flags. */ keyuse = 0; if (flags) { char **tokens = NULL; tokens = strtokenize (flags, ","); if (!tokens) return gpg_error_from_syserror (); for (i=0; (s = tokens[i]); i++) { if (!*s) ; else if (!ascii_strcasecmp (s, "sign")) keyuse |= PUBKEY_USAGE_SIG; else if (!ascii_strcasecmp (s, "encrypt") || !ascii_strcasecmp (s, "encr")) keyuse |= PUBKEY_USAGE_ENC; else if (!ascii_strcasecmp (s, "auth")) keyuse |= PUBKEY_USAGE_AUTH; else if (!ascii_strcasecmp (s, "cert")) keyuse |= PUBKEY_USAGE_CERT; else if (!ascii_strcasecmp (s, "ecdsa")) { if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) algo = PUBKEY_ALGO_ECDSA; else { xfree (tokens); return gpg_error (GPG_ERR_INV_FLAG); } ecdh_or_ecdsa = 0; } else if (!ascii_strcasecmp (s, "ecdh")) { if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ECDSA) algo = PUBKEY_ALGO_ECDH; else { xfree (tokens); return gpg_error (GPG_ERR_INV_FLAG); } ecdh_or_ecdsa = 0; } else if (!ascii_strcasecmp (s, "eddsa")) { /* Not required but we allow it for consistency. */ if (algo == PUBKEY_ALGO_EDDSA) ; else { xfree (tokens); return gpg_error (GPG_ERR_INV_FLAG); } } else if (!ascii_strcasecmp (s, "v5")) { if (opt.flags.rfc4880bis) keyversion = 5; } else if (!ascii_strcasecmp (s, "v4")) keyversion = 4; else { xfree (tokens); return gpg_error (GPG_ERR_UNKNOWN_FLAG); } } xfree (tokens); } /* If not yet decided switch between ecdh and ecdsa. */ if (ecdh_or_ecdsa && keyuse) algo = (keyuse & PUBKEY_USAGE_ENC)? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; else if (ecdh_or_ecdsa) algo = for_subkey? PUBKEY_ALGO_ECDH : PUBKEY_ALGO_ECDSA; /* Set or fix key usage. */ if (!keyuse) { if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_DSA) keyuse = PUBKEY_USAGE_SIG; else if (algo == PUBKEY_ALGO_RSA) keyuse = for_subkey? PUBKEY_USAGE_ENC : PUBKEY_USAGE_SIG; else keyuse = PUBKEY_USAGE_ENC; } else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_DSA) { keyuse &= ~PUBKEY_USAGE_ENC; /* Forbid encryption. */ } else if (algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_ELGAMAL_E) { keyuse = PUBKEY_USAGE_ENC; /* Allow only encryption. */ } /* Make sure a primary key can certify. */ if (!for_subkey) keyuse |= PUBKEY_USAGE_CERT; /* But if requested remove th cert usage. */ if (clear_cert) keyuse &= ~PUBKEY_USAGE_CERT; /* Check that usage is actually possible. */ if (/**/((keyuse & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) && !pubkey_get_nsig (algo)) || ((keyuse & PUBKEY_USAGE_ENC) && !pubkey_get_nenc (algo)) || (for_subkey && (keyuse & PUBKEY_USAGE_CERT))) return gpg_error (GPG_ERR_WRONG_KEY_USAGE); /* Return values. */ if (r_algo) *r_algo = algo; if (r_size) { unsigned int min, def, max; /* Make sure the keysize is in the allowed range. */ def = get_keysize_range (algo, &min, &max); if (!size) size = def; else if (size < min) size = min; else if (size > max) size = max; *r_size = fixup_keysize (size, algo, 1); } if (r_keyuse) *r_keyuse = keyuse; if (r_curve) *r_curve = curve; if (r_keyversion) *r_keyversion = keyversion; return 0; } /* Parse and return the standard key generation parameter. * The string is expected to be in this format: * * ALGO[/FLAGS][+SUBALGO[/FLAGS]] * * Here ALGO is a string in the same format as printed by the * keylisting. For example: * * rsa3072 := RSA with 3072 bit. * dsa2048 := DSA with 2048 bit. * elg2048 := Elgamal with 2048 bit. * ed25519 := EDDSA using curve Ed25519. * cv25519 := ECDH using curve Curve25519. * nistp256:= ECDSA or ECDH using curve NIST P-256 * * All strings with an unknown prefix are considered an elliptic * curve. Curves which have no implicit algorithm require that FLAGS * is given to select whether ECDSA or ECDH is used; this can eoither * be done using an algorithm keyword or usage keywords. * * FLAGS is a comma delimited string of keywords: * * cert := Allow usage Certify * sign := Allow usage Sign * encr := Allow usage Encrypt * auth := Allow usage Authentication * encrypt := Alias for "encr" * ecdsa := Use algorithm ECDSA. * eddsa := Use algorithm EdDSA. * ecdh := Use algorithm ECDH. * v5 := Create version 5 key (requires option --rfc4880bis) * * There are several defaults and fallbacks depending on the * algorithm. PART can be used to select which part of STRING is * used: * -1 := Both parts * 0 := Only the part of the primary key * 1 := If there is one part parse that one, if there are * two parts parse the part which best matches the * SUGGESTED_USE or in case that can't be evaluated the second part. * Always return using the args for the primary key (R_ALGO,....). * */ gpg_error_t parse_key_parameter_string (const char *string, int part, unsigned int suggested_use, int *r_algo, unsigned int *r_size, unsigned int *r_keyuse, char const **r_curve, int *r_version, int *r_subalgo, unsigned int *r_subsize, unsigned int *r_subkeyuse, char const **r_subcurve, int *r_subversion) { gpg_error_t err = 0; char *primary, *secondary; if (r_algo) *r_algo = 0; if (r_size) *r_size = 0; if (r_keyuse) *r_keyuse = 0; if (r_curve) *r_curve = NULL; if (r_version) *r_version = 4; if (r_subalgo) *r_subalgo = 0; if (r_subsize) *r_subsize = 0; if (r_subkeyuse) *r_subkeyuse = 0; if (r_subcurve) *r_subcurve = NULL; if (r_subversion) *r_subversion = 4; if (!string || !*string || !ascii_strcasecmp (string, "default") || !strcmp (string, "-")) string = get_default_pubkey_algo (); else if (!ascii_strcasecmp (string, "future-default") || !ascii_strcasecmp (string, "futuredefault")) string = FUTURE_STD_KEY_PARAM; primary = xstrdup (string); secondary = strchr (primary, '+'); if (secondary) *secondary++ = 0; if (part == -1 || part == 0) { err = parse_key_parameter_part (primary, 0, 0, r_algo, r_size, r_keyuse, r_curve, r_version); if (!err && part == -1) err = parse_key_parameter_part (secondary, 1, 0, r_subalgo, r_subsize, r_subkeyuse, r_subcurve, r_subversion); } else if (part == 1) { /* If we have SECONDARY, use that part. If there is only one * part consider this to be the subkey algo. In case a * SUGGESTED_USE has been given and the usage of the secondary * part does not match SUGGESTED_USE try again using the primary * part. Note that when falling back to the primary key we need * to force clearing the cert usage. */ if (secondary) { err = parse_key_parameter_part (secondary, 1, 0, r_algo, r_size, r_keyuse, r_curve, r_version); if (!err && suggested_use && r_keyuse && !(suggested_use & *r_keyuse)) err = parse_key_parameter_part (primary, 1, 1 /*(clear cert)*/, r_algo, r_size, r_keyuse, r_curve, r_version); } else err = parse_key_parameter_part (primary, 1, 0, r_algo, r_size, r_keyuse, r_curve, r_version); } xfree (primary); return err; } /* Append R to the linked list PARA. */ static void append_to_parameter (struct para_data_s *para, struct para_data_s *r) { log_assert (para); while (para->next) para = para->next; para->next = r; } /* Release the parameter list R. */ static void release_parameter_list (struct para_data_s *r) { struct para_data_s *r2; for (; r ; r = r2) { r2 = r->next; if (r->key == pPASSPHRASE && *r->u.value) wipememory (r->u.value, strlen (r->u.value)); xfree (r); } } static struct para_data_s * get_parameter( struct para_data_s *para, enum para_name key ) { struct para_data_s *r; for( r = para; r && r->key != key; r = r->next ) ; return r; } static const char * get_parameter_value( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return (r && *r->u.value)? r->u.value : NULL; } /* This is similar to get_parameter_value but also returns the empty string. This is required so that quick_generate_keypair can use an empty Passphrase to specify no-protection. */ static const char * get_parameter_passphrase (struct para_data_s *para) { struct para_data_s *r = get_parameter (para, pPASSPHRASE); return r ? r->u.value : NULL; } static int get_parameter_algo( struct para_data_s *para, enum para_name key, int *r_default) { int i; struct para_data_s *r = get_parameter( para, key ); if (r_default) *r_default = 0; if (!r) return -1; /* Note that we need to handle the ECC algorithms specified as strings directly because Libgcrypt folds them all to ECC. */ if (!ascii_strcasecmp (r->u.value, "default")) { /* Note: If you change this default algo, remember to change it * also in gpg.c:gpgconf_list. */ /* FIXME: We only allow the algo here and have a separate thing * for the curve etc. That is a ugly but demanded for backward * compatibility with the batch key generation. It would be * better to make full use of parse_key_parameter_string. */ parse_key_parameter_string (NULL, 0, 0, &i, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL); if (r_default) *r_default = 1; } else if (digitp (r->u.value)) i = atoi( r->u.value ); else if (!strcmp (r->u.value, "ELG-E") || !strcmp (r->u.value, "ELG")) i = PUBKEY_ALGO_ELGAMAL_E; else if (!ascii_strcasecmp (r->u.value, "EdDSA")) i = PUBKEY_ALGO_EDDSA; else if (!ascii_strcasecmp (r->u.value, "ECDSA")) i = PUBKEY_ALGO_ECDSA; else if (!ascii_strcasecmp (r->u.value, "ECDH")) i = PUBKEY_ALGO_ECDH; else i = map_pk_gcry_to_openpgp (gcry_pk_map_name (r->u.value)); if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S) i = 0; /* we don't want to allow generation of these algorithms */ return i; } /* Parse a usage string. The usage keywords "auth", "sign", "encr" * may be delimited by space, tab, or comma. On error -1 is returned * instead of the usage flags. */ static int parse_usagestr (const char *usagestr) { gpg_error_t err; char **tokens = NULL; const char *s; int i; unsigned int use = 0; tokens = strtokenize (usagestr, " \t,"); if (!tokens) { err = gpg_error_from_syserror (); log_error ("strtokenize failed: %s\n", gpg_strerror (err)); return -1; } for (i=0; (s = tokens[i]); i++) { if (!*s) ; else if (!ascii_strcasecmp (s, "sign")) use |= PUBKEY_USAGE_SIG; else if (!ascii_strcasecmp (s, "encrypt") || !ascii_strcasecmp (s, "encr")) use |= PUBKEY_USAGE_ENC; else if (!ascii_strcasecmp (s, "auth")) use |= PUBKEY_USAGE_AUTH; else if (!ascii_strcasecmp (s, "cert")) use |= PUBKEY_USAGE_CERT; else { xfree (tokens); return -1; /* error */ } } xfree (tokens); return use; } /* * Parse the usage parameter and set the keyflags. Returns -1 on * error, 0 for no usage given or 1 for usage available. */ static int parse_parameter_usage (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); int i; if (!r) return 0; /* none (this is an optional parameter)*/ i = parse_usagestr (r->u.value); if (i == -1) { log_error ("%s:%d: invalid usage list\n", fname, r->lnr ); return -1; /* error */ } r->u.usage = i; return 1; } static int parse_revocation_key (const char *fname, struct para_data_s *para, enum para_name key) { struct para_data_s *r = get_parameter( para, key ); struct revocation_key revkey; char *pn; int i; if( !r ) return 0; /* none (this is an optional parameter) */ pn = r->u.value; revkey.class=0x80; revkey.algid=atoi(pn); if(!revkey.algid) goto fail; /* Skip to the fpr */ while(*pn && *pn!=':') pn++; if(*pn!=':') goto fail; pn++; for(i=0;iu.revkey,&revkey,sizeof(struct revocation_key)); return 0; fail: log_error("%s:%d: invalid revocation key\n", fname, r->lnr ); return -1; /* error */ } static u32 get_parameter_u32( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); if( !r ) return 0; if( r->key == pKEYCREATIONDATE ) return r->u.creation; if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE ) return r->u.expire; if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE ) return r->u.usage; return (unsigned int)strtoul( r->u.value, NULL, 10 ); } static unsigned int get_parameter_uint( struct para_data_s *para, enum para_name key ) { return get_parameter_u32( para, key ); } static struct revocation_key * get_parameter_revkey( struct para_data_s *para, enum para_name key ) { struct para_data_s *r = get_parameter( para, key ); return r? &r->u.revkey : NULL; } static int proc_parameter_file (ctrl_t ctrl, struct para_data_s *para, const char *fname, struct output_control_s *outctrl, int card ) { struct para_data_s *r; const char *s1, *s2, *s3; size_t n; char *p; int is_default = 0; int have_user_id = 0; int err, algo; /* Check that we have all required parameters. */ r = get_parameter( para, pKEYTYPE ); if(r) { algo = get_parameter_algo (para, pKEYTYPE, &is_default); if (openpgp_pk_test_algo2 (algo, PUBKEY_USAGE_SIG)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } } else { log_error ("%s: no Key-Type specified\n",fname); return -1; } err = parse_parameter_usage (fname, para, pKEYUSAGE); if (!err) { /* Default to algo capabilities if key-usage is not provided and no default algorithm has been requested. */ r = xmalloc_clear(sizeof(*r)); r->key = pKEYUSAGE; r->u.usage = (is_default ? (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG) : openpgp_pk_algo_usage(algo)); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Key-Usage not allowed for algo %d\n", fname, r->lnr, algo); return -1; } } is_default = 0; r = get_parameter( para, pSUBKEYTYPE ); if(r) { algo = get_parameter_algo (para, pSUBKEYTYPE, &is_default); if (openpgp_pk_test_algo (algo)) { log_error ("%s:%d: invalid algorithm\n", fname, r->lnr ); return -1; } err = parse_parameter_usage (fname, para, pSUBKEYUSAGE); if (!err) { /* Default to algo capabilities if subkey-usage is not provided */ r = xmalloc_clear (sizeof(*r)); r->key = pSUBKEYUSAGE; r->u.usage = (is_default ? PUBKEY_USAGE_ENC : openpgp_pk_algo_usage (algo)); append_to_parameter (para, r); } else if (err == -1) return -1; else { r = get_parameter (para, pSUBKEYUSAGE); if (r && (r->u.usage & ~openpgp_pk_algo_usage (algo))) { log_error ("%s:%d: specified Subkey-Usage not allowed" " for algo %d\n", fname, r->lnr, algo); return -1; } } } if( get_parameter_value( para, pUSERID ) ) have_user_id=1; else { /* create the formatted user ID */ s1 = get_parameter_value( para, pNAMEREAL ); s2 = get_parameter_value( para, pNAMECOMMENT ); s3 = get_parameter_value( para, pNAMEEMAIL ); if( s1 || s2 || s3 ) { n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0); r = xmalloc_clear( sizeof *r + n + 20 ); r->key = pUSERID; p = r->u.value; if( s1 ) p = stpcpy(p, s1 ); if( s2 ) p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")"); if( s3 ) { /* If we have only the email part, do not add the space * and the angle brackets. */ if (*r->u.value) p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">"); else p = stpcpy (p, s3); } append_to_parameter (para, r); have_user_id=1; } } if(!have_user_id) { log_error("%s: no User-ID specified\n",fname); return -1; } /* Set preferences, if any. */ keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0); /* Set keyserver, if any. */ s1=get_parameter_value( para, pKEYSERVER ); if(s1) { struct keyserver_spec *spec; spec = parse_keyserver_uri (s1, 1); if(spec) { free_keyserver_spec(spec); opt.def_keyserver_url=s1; } else { r = get_parameter (para, pKEYSERVER); log_error("%s:%d: invalid keyserver url\n", fname, r->lnr ); return -1; } } /* Set revoker, if any. */ if (parse_revocation_key (fname, para, pREVOKER)) return -1; /* Make KEYCREATIONDATE from Creation-Date. */ r = get_parameter (para, pCREATIONDATE); if (r && *r->u.value) { u32 seconds; seconds = parse_creation_string (r->u.value); if (!seconds) { log_error ("%s:%d: invalid creation date\n", fname, r->lnr ); return -1; } r->u.creation = seconds; r->key = pKEYCREATIONDATE; /* Change that entry. */ } /* Make KEYEXPIRE from Expire-Date. */ r = get_parameter( para, pEXPIREDATE ); if( r && *r->u.value ) { u32 seconds; seconds = parse_expire_string( r->u.value ); if( seconds == (u32)-1 ) { log_error("%s:%d: invalid expire date\n", fname, r->lnr ); return -1; } r->u.expire = seconds; r->key = pKEYEXPIRE; /* change hat entry */ /* also set it for the subkey */ r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYEXPIRE; r->u.expire = seconds; append_to_parameter (para, r); } do_generate_keypair (ctrl, para, outctrl, card ); return 0; } /**************** * Kludge to allow non interactive key generation controlled * by a parameter file. * Note, that string parameters are expected to be in UTF-8 */ static void read_parameter_file (ctrl_t ctrl, const char *fname ) { static struct { const char *name; enum para_name key; } keywords[] = { { "Key-Type", pKEYTYPE}, { "Key-Length", pKEYLENGTH }, { "Key-Curve", pKEYCURVE }, { "Key-Usage", pKEYUSAGE }, { "Subkey-Type", pSUBKEYTYPE }, { "Subkey-Length", pSUBKEYLENGTH }, { "Subkey-Curve", pSUBKEYCURVE }, { "Subkey-Usage", pSUBKEYUSAGE }, { "Name-Real", pNAMEREAL }, { "Name-Email", pNAMEEMAIL }, { "Name-Comment", pNAMECOMMENT }, { "Expire-Date", pEXPIREDATE }, { "Creation-Date", pCREATIONDATE }, { "Passphrase", pPASSPHRASE }, { "Preferences", pPREFERENCES }, { "Revoker", pREVOKER }, { "Handle", pHANDLE }, { "Keyserver", pKEYSERVER }, { "Keygrip", pKEYGRIP }, { "Key-Grip", pKEYGRIP }, { "Subkey-grip", pSUBKEYGRIP }, { "Key-Version", pVERSION }, { "Subkey-Version", pSUBVERSION }, { NULL, 0 } }; IOBUF fp; byte *line; unsigned int maxlen, nline; char *p; int lnr; const char *err = NULL; struct para_data_s *para, *r; int i; struct output_control_s outctrl; memset( &outctrl, 0, sizeof( outctrl ) ); outctrl.pub.afx = new_armor_context (); if( !fname || !*fname) fname = "-"; fp = iobuf_open (fname); if (fp && is_secured_file (iobuf_get_fd (fp))) { iobuf_close (fp); fp = NULL; gpg_err_set_errno (EPERM); } if (!fp) { log_error (_("can't open '%s': %s\n"), fname, strerror(errno) ); return; } iobuf_ioctl (fp, IOBUF_IOCTL_NO_CACHE, 1, NULL); lnr = 0; err = NULL; para = NULL; maxlen = 1024; line = NULL; while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) { char *keyword, *value; lnr++; if( !maxlen ) { err = "line too long"; break; } for( p = line; isspace(*(byte*)p); p++ ) ; if( !*p || *p == '#' ) continue; keyword = p; if( *keyword == '%' ) { for( ; !isspace(*(byte*)p); p++ ) ; if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; value = p; trim_trailing_ws( value, strlen(value) ); if( !ascii_strcasecmp( keyword, "%echo" ) ) log_info("%s\n", value ); else if( !ascii_strcasecmp( keyword, "%dry-run" ) ) outctrl.dryrun = 1; else if( !ascii_strcasecmp( keyword, "%ask-passphrase" ) ) ; /* Dummy for backward compatibility. */ else if( !ascii_strcasecmp( keyword, "%no-ask-passphrase" ) ) ; /* Dummy for backward compatibility. */ else if( !ascii_strcasecmp( keyword, "%no-protection" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_NO_PROTECTION; else if( !ascii_strcasecmp( keyword, "%transient-key" ) ) outctrl.keygen_flags |= KEYGEN_FLAG_TRANSIENT_KEY; else if( !ascii_strcasecmp( keyword, "%commit" ) ) { outctrl.lnr = lnr; if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else if( !ascii_strcasecmp( keyword, "%pubring" ) ) { if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) ) ; /* still the same file - ignore it */ else { xfree( outctrl.pub.newfname ); outctrl.pub.newfname = xstrdup( value ); outctrl.use_files = 1; } } else if( !ascii_strcasecmp( keyword, "%secring" ) ) { /* Ignore this command. */ } else log_info("skipping control '%s' (%s)\n", keyword, value ); continue; } if( !(p = strchr( p, ':' )) || p == keyword ) { err = "missing colon"; break; } if( *p ) *p++ = 0; for( ; isspace(*(byte*)p); p++ ) ; if( !*p ) { err = "missing argument"; break; } value = p; trim_trailing_ws( value, strlen(value) ); for(i=0; keywords[i].name; i++ ) { if( !ascii_strcasecmp( keywords[i].name, keyword ) ) break; } if( !keywords[i].name ) { err = "unknown keyword"; break; } if( keywords[i].key != pKEYTYPE && !para ) { err = "parameter block does not start with \"Key-Type\""; break; } if( keywords[i].key == pKEYTYPE && para ) { outctrl.lnr = lnr; if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); release_parameter_list( para ); para = NULL; } else { for( r = para; r; r = r->next ) { if( r->key == keywords[i].key ) break; } if( r ) { err = "duplicate keyword"; break; } } if (!opt.flags.rfc4880bis && (keywords[i].key == pVERSION || keywords[i].key == pSUBVERSION)) ; /* Ignore version unless --rfc4880bis is active. */ else { r = xmalloc_clear( sizeof *r + strlen( value ) ); r->lnr = lnr; r->key = keywords[i].key; strcpy( r->u.value, value ); r->next = para; para = r; } } if( err ) log_error("%s:%d: %s\n", fname, lnr, err ); else if( iobuf_error (fp) ) { log_error("%s:%d: read error\n", fname, lnr); } else if( para ) { outctrl.lnr = lnr; if (proc_parameter_file (ctrl, para, fname, &outctrl, 0 )) print_status_key_not_created (get_parameter_value (para, pHANDLE)); } if( outctrl.use_files ) { /* close open streams */ iobuf_close( outctrl.pub.stream ); /* Must invalidate that ugly cache to actually close it. */ if (outctrl.pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl.pub.fname); xfree( outctrl.pub.fname ); xfree( outctrl.pub.newfname ); } xfree (line); release_parameter_list( para ); iobuf_close (fp); release_armor_context (outctrl.pub.afx); } /* Helper for quick_generate_keypair. */ static struct para_data_s * quickgen_set_para (struct para_data_s *para, int for_subkey, int algo, int nbits, const char *curve, unsigned int use, int version) { struct para_data_s *r; r = xmalloc_clear (sizeof *r + 30); r->key = for_subkey? pSUBKEYUSAGE : pKEYUSAGE; if (use) snprintf (r->u.value, 30, "%s%s%s%s", (use & PUBKEY_USAGE_ENC)? "encr " : "", (use & PUBKEY_USAGE_SIG)? "sign " : "", (use & PUBKEY_USAGE_AUTH)? "auth " : "", (use & PUBKEY_USAGE_CERT)? "cert " : ""); else strcpy (r->u.value, for_subkey ? "encr" : "sign"); r->next = para; para = r; r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYTYPE : pKEYTYPE; snprintf (r->u.value, 20, "%d", algo); r->next = para; para = r; if (curve) { r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = for_subkey? pSUBKEYCURVE : pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } else { r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBKEYLENGTH : pKEYLENGTH; sprintf (r->u.value, "%u", nbits); r->next = para; para = r; } if (opt.flags.rfc4880bis) { r = xmalloc_clear (sizeof *r + 20); r->key = for_subkey? pSUBVERSION : pVERSION; snprintf (r->u.value, 20, "%d", version); r->next = para; para = r; } return para; } /* * Unattended generation of a standard key. */ void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, const char *usagestr, const char *expirestr) { gpg_error_t err; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; int use_tty; memset (&outctrl, 0, sizeof outctrl); use_tty = (!opt.batch && !opt.answer_yes && !*algostr && !*usagestr && !*expirestr && !cpr_enabled () && gnupg_isatty (fileno (stdin)) && gnupg_isatty (fileno (stdout)) && gnupg_isatty (fileno (stderr))); r = xmalloc_clear (sizeof *r + strlen (uid)); r->key = pUSERID; strcpy (r->u.value, uid); r->next = para; para = r; uid = trim_spaces (r->u.value); if (!*uid || (!opt.allow_freeform_uid && !is_valid_user_id (uid))) { log_error (_("Key generation failed: %s\n"), gpg_strerror (GPG_ERR_INV_USER_ID)); goto leave; } /* If gpg is directly used on the console ask whether a key with the given user id shall really be created. */ if (use_tty) { tty_printf (_("About to create a key for:\n \"%s\"\n\n"), uid); if (!cpr_get_answer_is_yes_def ("quick_keygen.okay", _("Continue? (Y/n) "), 1)) goto leave; } /* Check whether such a user ID already exists. */ { KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_EXACT; desc.u.name = uid; - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) goto leave; err = keydb_search (kdbhd, &desc, 1, NULL); keydb_release (kdbhd); if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { log_info (_("A key for \"%s\" already exists\n"), uid); if (opt.answer_yes) ; else if (!use_tty || !cpr_get_answer_is_yes_def ("quick_keygen.force", _("Create anyway? (y/N) "), 0)) { write_status_error ("genkey", gpg_error (304)); log_inc_errorcount (); /* we used log_info */ goto leave; } log_info (_("creating anyway\n")); } } if (!*expirestr || strcmp (expirestr, "-") == 0) expirestr = default_expiration_interval; if ((!*algostr || !ascii_strcasecmp (algostr, "default") || !ascii_strcasecmp (algostr, "future-default") || !ascii_strcasecmp (algostr, "futuredefault")) && (!*usagestr || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-"))) { /* Use default key parameters. */ int algo, subalgo, version, subversion; unsigned int size, subsize; unsigned int keyuse, subkeyuse; const char *curve, *subcurve; err = parse_key_parameter_string (algostr, -1, 0, &algo, &size, &keyuse, &curve, &version, &subalgo, &subsize, &subkeyuse, &subcurve, &subversion); if (err) { log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); goto leave; } para = quickgen_set_para (para, 0, algo, size, curve, keyuse, version); if (subalgo) para = quickgen_set_para (para, 1, subalgo, subsize, subcurve, subkeyuse, subversion); if (*expirestr) { u32 expire; expire = parse_expire_string (expirestr); if (expire == (u32)-1 ) { err = gpg_error (GPG_ERR_INV_VALUE); log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); goto leave; } r = xmalloc_clear (sizeof *r + 20); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; } } else { /* Extended unattended mode. Creates only the primary key. */ int algo, version; unsigned int use; u32 expire; unsigned int nbits; const char *curve; err = parse_algo_usage_expire (ctrl, 0, algostr, usagestr, expirestr, &algo, &use, &expire, &nbits, &curve, &version); if (err) { log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); goto leave; } para = quickgen_set_para (para, 0, algo, nbits, curve, use, version); r = xmalloc_clear (sizeof *r + 20); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; } /* If the pinentry loopback mode is not and we have a static passphrase (i.e. set with --passphrase{,-fd,-file} while in batch mode), we use that passphrase for the new key. */ if (opt.pinentry_mode != PINENTRY_MODE_LOOPBACK && have_static_passphrase ()) { const char *s = get_static_passphrase (); r = xmalloc_clear (sizeof *r + strlen (s)); r->key = pPASSPHRASE; strcpy (r->u.value, s); r->next = para; para = r; } proc_parameter_file (ctrl, para, "[internal]", &outctrl, 0); leave: release_parameter_list (para); } /* * Generate a keypair (fname is only used in batch mode) If * CARD_SERIALNO is not NULL the function will create the keys on an * OpenPGP Card. If CARD_BACKUP_KEY has been set and CARD_SERIALNO is * NOT NULL, the encryption key for the card is generated on the host, * imported to the card and a backup file created by gpg-agent. If * FULL is not set only the basic prompts are used (except for batch * mode). */ void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key) { gpg_error_t err; unsigned int nbits; char *uid = NULL; int algo; unsigned int use; int both = 0; u32 expire; struct para_data_s *para = NULL; struct para_data_s *r; struct output_control_s outctrl; #ifndef ENABLE_CARD_SUPPORT (void)card_backup_key; #endif memset( &outctrl, 0, sizeof( outctrl ) ); if (opt.batch && card_serialno) { /* We don't yet support unattended key generation with a card * serial number. */ log_error (_("can't do this in batch mode\n")); print_further_info ("key generation with card serial number"); return; } if (opt.batch) { read_parameter_file (ctrl, fname); return; } if (card_serialno) { #ifdef ENABLE_CARD_SUPPORT struct agent_card_info_s info; memset (&info, 0, sizeof (info)); err = agent_scd_getattr ("KEY-ATTR", &info); if (err) { log_error (_("error getting current key info: %s\n"), gpg_strerror (err)); return; } r = xcalloc (1, sizeof *r + strlen (card_serialno) ); r->key = pSERIALNO; strcpy( r->u.value, card_serialno); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", info.key_attr[0].algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy (r->u.value, "sign"); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", info.key_attr[1].algo ); r->next = para; para = r; r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy (r->u.value, "encrypt"); r->next = para; para = r; if (info.key_attr[1].algo == PUBKEY_ALGO_RSA) { r = xcalloc (1, sizeof *r + 20 ); r->key = pSUBKEYLENGTH; sprintf( r->u.value, "%u", info.key_attr[1].nbits); r->next = para; para = r; } else if (info.key_attr[1].algo == PUBKEY_ALGO_ECDSA || info.key_attr[1].algo == PUBKEY_ALGO_EDDSA || info.key_attr[1].algo == PUBKEY_ALGO_ECDH) { r = xcalloc (1, sizeof *r + strlen (info.key_attr[1].curve)); r->key = pSUBKEYCURVE; strcpy (r->u.value, info.key_attr[1].curve); r->next = para; para = r; } r = xcalloc (1, sizeof *r + 20 ); r->key = pAUTHKEYTYPE; sprintf( r->u.value, "%d", info.key_attr[2].algo ); r->next = para; para = r; if (card_backup_key) { r = xcalloc (1, sizeof *r + 1); r->key = pCARDBACKUPKEY; strcpy (r->u.value, "1"); r->next = para; para = r; } #endif /*ENABLE_CARD_SUPPORT*/ } else if (full) /* Full featured key generation. */ { int subkey_algo; char *key_from_hexgrip = NULL; algo = ask_algo (ctrl, 0, &subkey_algo, &use, &key_from_hexgrip); if (key_from_hexgrip) { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; if (use) { r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", (use & PUBKEY_USAGE_ENC)? "encrypt ":"", (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 40 ); r->key = pKEYGRIP; strcpy (r->u.value, key_from_hexgrip); r->next = para; para = r; xfree (key_from_hexgrip); } else { const char *curve = NULL; if (subkey_algo) { /* Create primary and subkey at once. */ both = 1; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, &subkey_algo, NULL); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = 0; r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } else { r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo); r->next = para; para = r; nbits = ask_keysize (algo, 0); r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYUSAGE; strcpy( r->u.value, "sign" ); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYTYPE; sprintf( r->u.value, "%d", subkey_algo); r->next = para; para = r; r = xmalloc_clear( sizeof *r + 20 ); r->key = pSUBKEYUSAGE; strcpy( r->u.value, "encrypt" ); r->next = para; para = r; if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { if (algo == PUBKEY_ALGO_EDDSA && subkey_algo == PUBKEY_ALGO_ECDH) { /* Need to switch to a different curve for the encryption key. */ curve = "Curve25519"; } r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pSUBKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } } else /* Create only a single key. */ { /* For ECC we need to ask for the curve before storing the algo because ask_curve may change the algo. */ if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { curve = ask_curve (&algo, NULL, NULL); r = xmalloc_clear (sizeof *r + strlen (curve)); r->key = pKEYCURVE; strcpy (r->u.value, curve); r->next = para; para = r; } r = xmalloc_clear( sizeof *r + 20 ); r->key = pKEYTYPE; sprintf( r->u.value, "%d", algo ); r->next = para; para = r; if (use) { r = xmalloc_clear( sizeof *r + 25 ); r->key = pKEYUSAGE; sprintf( r->u.value, "%s%s%s", (use & PUBKEY_USAGE_SIG)? "sign ":"", (use & PUBKEY_USAGE_ENC)? "encrypt ":"", (use & PUBKEY_USAGE_AUTH)? "auth":"" ); r->next = para; para = r; } nbits = 0; } if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) { /* The curve has already been set. */ } else { nbits = ask_keysize (both? subkey_algo : algo, nbits); r = xmalloc_clear( sizeof *r + 20 ); r->key = both? pSUBKEYLENGTH : pKEYLENGTH; sprintf( r->u.value, "%u", nbits); r->next = para; para = r; } } } else /* Default key generation. */ { int subalgo, version, subversion; unsigned int size, subsize; unsigned int keyuse, subkeyuse; const char *curve, *subcurve; tty_printf ( _("Note: Use \"%s %s\"" " for a full featured key generation dialog.\n"), #if USE_GPG2_HACK GPG_NAME "2" #else GPG_NAME #endif , "--full-generate-key" ); err = parse_key_parameter_string (NULL, -1, 0, &algo, &size, &keyuse, &curve, &version, &subalgo, &subsize, &subkeyuse, &subcurve, &subversion); if (err) { log_error (_("Key generation failed: %s\n"), gpg_strerror (err)); return; } para = quickgen_set_para (para, 0, algo, size, curve, keyuse, version); if (subalgo) para = quickgen_set_para (para, 1, subalgo, subsize, subcurve, subkeyuse, subversion); } expire = full? ask_expire_interval (0, NULL) : parse_expire_string (default_expiration_interval); r = xcalloc (1, sizeof *r + 20); r->key = pKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; r = xcalloc (1, sizeof *r + 20); r->key = pSUBKEYEXPIRE; r->u.expire = expire; r->next = para; para = r; uid = ask_user_id (0, full, NULL); if (!uid) { log_error(_("Key generation canceled.\n")); release_parameter_list( para ); return; } r = xcalloc (1, sizeof *r + strlen (uid)); r->key = pUSERID; strcpy (r->u.value, uid); r->next = para; para = r; proc_parameter_file (ctrl, para, "[internal]", &outctrl, !!card_serialno); release_parameter_list (para); } /* Create and delete a dummy packet to start off a list of kbnodes. */ static void start_tree(KBNODE *tree) { PACKET *pkt; pkt=xmalloc_clear(sizeof(*pkt)); pkt->pkttype=PKT_NONE; *tree=new_kbnode(pkt); delete_kbnode(*tree); } /* Write the *protected* secret key to the file. */ static gpg_error_t card_write_key_to_backup_file (PKT_public_key *sk, const char *backup_dir) { gpg_error_t err = 0; int rc; char keyid_buffer[2 * 8 + 1]; char name_buffer[50]; char *fname; IOBUF fp; mode_t oldmask; PACKET *pkt = NULL; format_keyid (pk_keyid (sk), KF_LONG, keyid_buffer, sizeof (keyid_buffer)); snprintf (name_buffer, sizeof name_buffer, "sk_%s.gpg", keyid_buffer); fname = make_filename (backup_dir, name_buffer, NULL); /* Note that the umask call is not anymore needed because iobuf_create now takes care of it. However, it does not harm and thus we keep it. */ oldmask = umask (077); if (is_secured_filename (fname)) { fp = NULL; gpg_err_set_errno (EPERM); } else fp = iobuf_create (fname, 1); umask (oldmask); if (!fp) { err = gpg_error_from_syserror (); log_error (_("can't create backup file '%s': %s\n"), fname, strerror (errno) ); goto leave; } pkt = xcalloc (1, sizeof *pkt); pkt->pkttype = PKT_SECRET_KEY; pkt->pkt.secret_key = sk; rc = build_packet (fp, pkt); if (rc) { log_error ("build packet failed: %s\n", gpg_strerror (rc)); iobuf_cancel (fp); } else { char *fprbuf; iobuf_close (fp); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname); log_info (_("Note: backup of card key saved to '%s'\n"), fname); fprbuf = hexfingerprint (sk, NULL, 0); if (!fprbuf) { err = gpg_error_from_syserror (); goto leave; } write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED, fprbuf, fname, strlen (fname), 0); xfree (fprbuf); } leave: xfree (pkt); xfree (fname); return err; } /* Store key to card and make a backup file in OpenPGP format. */ static gpg_error_t card_store_key_with_backup (ctrl_t ctrl, PKT_public_key *sub_psk, const char *backup_dir) { PKT_public_key *sk; gnupg_isotime_t timestamp; gpg_error_t err; char *hexgrip; int rc; struct agent_card_info_s info; gcry_cipher_hd_t cipherhd = NULL; char *cache_nonce = NULL; void *kek = NULL; size_t keklen; sk = copy_public_key (NULL, sub_psk); if (!sk) return gpg_error_from_syserror (); epoch2isotime (timestamp, (time_t)sk->timestamp); err = hexkeygrip_from_pk (sk, &hexgrip); if (err) return err; memset(&info, 0, sizeof (info)); rc = agent_scd_getattr ("SERIALNO", &info); if (rc) return (gpg_error_t)rc; rc = agent_keytocard (hexgrip, 2, 1, info.serialno, timestamp); xfree (info.serialno); if (rc) { err = (gpg_error_t)rc; goto leave; } err = agent_keywrap_key (ctrl, 1, &kek, &keklen); if (err) { log_error ("error getting the KEK: %s\n", gpg_strerror (err)); goto leave; } err = gcry_cipher_open (&cipherhd, GCRY_CIPHER_AES128, GCRY_CIPHER_MODE_AESWRAP, 0); if (!err) err = gcry_cipher_setkey (cipherhd, kek, keklen); if (err) { log_error ("error setting up an encryption context: %s\n", gpg_strerror (err)); goto leave; } err = receive_seckey_from_agent (ctrl, cipherhd, 0, &cache_nonce, hexgrip, sk); if (err) { log_error ("error getting secret key from agent: %s\n", gpg_strerror (err)); goto leave; } err = card_write_key_to_backup_file (sk, backup_dir); if (err) log_error ("writing card key to backup file: %s\n", gpg_strerror (err)); else /* Remove secret key data in agent side. */ agent_scd_learn (NULL, 1); leave: xfree (cache_nonce); gcry_cipher_close (cipherhd); xfree (kek); xfree (hexgrip); free_public_key (sk); return err; } static void do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, struct output_control_s *outctrl, int card) { gpg_error_t err; KBNODE pub_root = NULL; const char *s; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; struct revocation_key *revkey; int did_sub = 0; u32 timestamp; char *cache_nonce = NULL; int algo; u32 expire; const char *key_from_hexgrip = NULL; unsigned int keygen_flags; if (outctrl->dryrun) { log_info("dry-run mode - key generation skipped\n"); return; } if ( outctrl->use_files ) { if ( outctrl->pub.newfname ) { iobuf_close(outctrl->pub.stream); outctrl->pub.stream = NULL; if (outctrl->pub.fname) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)outctrl->pub.fname); xfree( outctrl->pub.fname ); outctrl->pub.fname = outctrl->pub.newfname; outctrl->pub.newfname = NULL; if (is_secured_filename (outctrl->pub.fname) ) { outctrl->pub.stream = NULL; gpg_err_set_errno (EPERM); } else outctrl->pub.stream = iobuf_create (outctrl->pub.fname, 0); if (!outctrl->pub.stream) { log_error(_("can't create '%s': %s\n"), outctrl->pub.newfname, strerror(errno) ); return; } if (opt.armor) { outctrl->pub.afx->what = 1; push_armor_filter (outctrl->pub.afx, outctrl->pub.stream); } } log_assert( outctrl->pub.stream ); if (opt.verbose) log_info (_("writing public key to '%s'\n"), outctrl->pub.fname ); } /* We create the packets as a tree of kbnodes. Because the structure we create is known in advance we simply generate a linked list. The first packet is a dummy packet which we flag as deleted. The very first packet must always be a KEY packet. */ start_tree (&pub_root); timestamp = get_parameter_u32 (para, pKEYCREATIONDATE); if (!timestamp) timestamp = make_timestamp (); /* Note that, depending on the backend (i.e. the used scdaemon version), the card key generation may update TIMESTAMP for each key. Thus we need to pass TIMESTAMP to all signing function to make sure that the binding signature is done using the timestamp of the corresponding (sub)key and not that of the primary key. An alternative implementation could tell the signing function the node of the subkey but that is more work than just to pass the current timestamp. */ algo = get_parameter_algo( para, pKEYTYPE, NULL ); expire = get_parameter_u32( para, pKEYEXPIRE ); key_from_hexgrip = get_parameter_value (para, pKEYGRIP); keygen_flags = outctrl->keygen_flags; if (get_parameter_uint (para, pVERSION) == 5) keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; if (key_from_hexgrip) err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, pub_root, timestamp, expire, 0, keygen_flags); else if (!card) err = do_create (algo, get_parameter_uint( para, pKEYLENGTH ), get_parameter_value (para, pKEYCURVE), pub_root, timestamp, expire, 0, keygen_flags, get_parameter_passphrase (para), &cache_nonce, NULL); else err = gen_card_key (1, algo, 1, pub_root, ×tamp, expire, keygen_flags); /* Get the pointer to the generated public key packet. */ if (!err) { pri_psk = pub_root->next->pkt->pkt.public_key; log_assert (pri_psk); /* Make sure a few fields are correctly set up before going further. */ pri_psk->flags.primary = 1; keyid_from_pk (pri_psk, NULL); /* We don't use pk_keyid to get keyid, because it also asserts that main_keyid is set! */ keyid_copy (pri_psk->main_keyid, pri_psk->keyid); } if (!err && (revkey = get_parameter_revkey (para, pREVOKER))) err = write_direct_sig (ctrl, pub_root, pri_psk, revkey, timestamp, cache_nonce); if (!err && (s = get_parameter_value (para, pUSERID))) { err = write_uid (pub_root, s ); if (!err) err = write_selfsigs (ctrl, pub_root, pri_psk, get_parameter_uint (para, pKEYUSAGE), timestamp, cache_nonce); } /* Write the auth key to the card before the encryption key. This is a partial workaround for a PGP bug (as of this writing, all versions including 8.1), that causes it to try and encrypt to the most recent subkey regardless of whether that subkey is actually an encryption type. In this case, the auth key is an RSA key so it succeeds. */ if (!err && card && get_parameter (para, pAUTHKEYTYPE)) { err = gen_card_key (3, get_parameter_algo( para, pAUTHKEYTYPE, NULL ), 0, pub_root, ×tamp, expire, keygen_flags); if (!err) err = write_keybinding (ctrl, pub_root, pri_psk, NULL, PUBKEY_USAGE_AUTH, timestamp, cache_nonce); } if (!err && get_parameter (para, pSUBKEYTYPE)) { int subkey_algo = get_parameter_algo (para, pSUBKEYTYPE, NULL); s = NULL; key_from_hexgrip = get_parameter_value (para, pSUBKEYGRIP); keygen_flags = outctrl->keygen_flags; if (get_parameter_uint (para, pSUBVERSION) == 5) keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; if (key_from_hexgrip) err = do_create_from_keygrip (ctrl, subkey_algo, key_from_hexgrip, pub_root, timestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, keygen_flags); else if (!card || (s = get_parameter_value (para, pCARDBACKUPKEY))) { err = do_create (subkey_algo, get_parameter_uint (para, pSUBKEYLENGTH), get_parameter_value (para, pSUBKEYCURVE), pub_root, timestamp, get_parameter_u32 (para, pSUBKEYEXPIRE), 1, s? KEYGEN_FLAG_NO_PROTECTION : keygen_flags, get_parameter_passphrase (para), &cache_nonce, NULL); /* Get the pointer to the generated public subkey packet. */ if (!err) { kbnode_t node; for (node = pub_root; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; log_assert (sub_psk); if (s) err = card_store_key_with_backup (ctrl, sub_psk, gnupg_homedir ()); } } else { err = gen_card_key (2, subkey_algo, 0, pub_root, ×tamp, expire, keygen_flags); } if (!err) err = write_keybinding (ctrl, pub_root, pri_psk, sub_psk, get_parameter_uint (para, pSUBKEYUSAGE), timestamp, cache_nonce); did_sub = 1; } if (!err && outctrl->use_files) /* Direct write to specified files. */ { err = write_keyblock (outctrl->pub.stream, pub_root); if (err) log_error ("can't write public key: %s\n", gpg_strerror (err)); } else if (!err) /* Write to the standard keyrings. */ { KEYDB_HANDLE pub_hd; - pub_hd = keydb_new (); + pub_hd = keydb_new (ctrl); if (!pub_hd) err = gpg_error_from_syserror (); else { err = keydb_locate_writable (pub_hd); if (err) log_error (_("no writable public keyring found: %s\n"), gpg_strerror (err)); } if (!err && opt.verbose) { log_info (_("writing public key to '%s'\n"), keydb_get_resource_name (pub_hd)); } if (!err) { err = keydb_insert_keyblock (pub_hd, pub_root); if (err) log_error (_("error writing public keyring '%s': %s\n"), keydb_get_resource_name (pub_hd), gpg_strerror (err)); } keydb_release (pub_hd); if (!err) { int no_enc_rsa; PKT_public_key *pk; no_enc_rsa = ((get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_RSA) && get_parameter_uint (para, pKEYUSAGE) && !((get_parameter_uint (para, pKEYUSAGE) & PUBKEY_USAGE_ENC)) ); pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; keyid_from_pk (pk, pk->main_keyid); register_trusted_keyid (pk->main_keyid); update_ownertrust (ctrl, pk, ((get_ownertrust (ctrl, pk) & ~TRUST_MASK) | TRUST_ULTIMATE )); gen_standard_revoke (ctrl, pk, cache_nonce); /* Get rid of the first empty packet. */ commit_kbnode (&pub_root); if (!opt.batch) { tty_printf (_("public and secret key created and signed.\n") ); tty_printf ("\n"); merge_keys_and_selfsig (ctrl, pub_root); list_keyblock_direct (ctrl, pub_root, 0, 1, opt.fingerprint || opt.with_fingerprint, 1); } if (!opt.batch && (get_parameter_algo (para, pKEYTYPE, NULL) == PUBKEY_ALGO_DSA || no_enc_rsa ) && !get_parameter (para, pSUBKEYTYPE) ) { tty_printf(_("Note that this key cannot be used for " "encryption. You may want to use\n" "the command \"--edit-key\" to generate a " "subkey for this purpose.\n") ); } } } if (err) { if (opt.batch) log_error ("key generation failed: %s\n", gpg_strerror (err) ); else tty_printf (_("Key generation failed: %s\n"), gpg_strerror (err) ); write_status_error (card? "card_key_generate":"key_generate", err); print_status_key_not_created ( get_parameter_value (para, pHANDLE) ); } else { PKT_public_key *pk = find_kbnode (pub_root, PKT_PUBLIC_KEY)->pkt->pkt.public_key; print_status_key_created (did_sub? 'B':'P', pk, get_parameter_value (para, pHANDLE)); } release_kbnode (pub_root); xfree (cache_nonce); } static gpg_error_t parse_algo_usage_expire (ctrl_t ctrl, int for_subkey, const char *algostr, const char *usagestr, const char *expirestr, int *r_algo, unsigned int *r_usage, u32 *r_expire, unsigned int *r_nbits, const char **r_curve, int *r_version) { gpg_error_t err; int algo; unsigned int use, nbits; u32 expire; int wantuse; int version = 4; const char *curve = NULL; *r_curve = NULL; nbits = 0; /* Parse the algo string. */ if (algostr && *algostr == '&' && strlen (algostr) == 41) { /* Take algo from existing key. */ algo = check_keygrip (ctrl, algostr+1); /* FIXME: We need the curve name as well. */ return gpg_error (GPG_ERR_NOT_IMPLEMENTED); } err = parse_key_parameter_string (algostr, for_subkey? 1 : 0, usagestr? parse_usagestr (usagestr):0, &algo, &nbits, &use, &curve, &version, NULL, NULL, NULL, NULL, NULL); if (err) return err; /* Parse the usage string. */ if (!usagestr || !*usagestr || !ascii_strcasecmp (usagestr, "default") || !strcmp (usagestr, "-")) ; /* Keep usage from parse_key_parameter_string. */ else if ((wantuse = parse_usagestr (usagestr)) != -1) use = wantuse; else return gpg_error (GPG_ERR_INV_VALUE); /* Make sure a primary key has the CERT usage. */ if (!for_subkey) use |= PUBKEY_USAGE_CERT; /* Check that usage is possible. NB: We have the same check in * parse_key_parameter_string but need it here again in case the * separate usage value has been given. */ if (/**/((use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH|PUBKEY_USAGE_CERT)) && !pubkey_get_nsig (algo)) || ((use & PUBKEY_USAGE_ENC) && !pubkey_get_nenc (algo)) || (for_subkey && (use & PUBKEY_USAGE_CERT))) return gpg_error (GPG_ERR_WRONG_KEY_USAGE); /* Parse the expire string. */ expire = parse_expire_string (expirestr); if (expire == (u32)-1 ) return gpg_error (GPG_ERR_INV_VALUE); if (curve) *r_curve = curve; *r_algo = algo; *r_usage = use; *r_expire = expire; *r_nbits = nbits; *r_version = version; return 0; } /* Add a new subkey to an existing key. Returns 0 if a new key has been generated and put into the keyblocks. If any of ALGOSTR, USAGESTR, or EXPIRESTR is NULL interactive mode is used. */ gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, const char *usagestr, const char *expirestr) { gpg_error_t err = 0; int interactive; kbnode_t node; PKT_public_key *pri_psk = NULL; PKT_public_key *sub_psk = NULL; int algo; unsigned int use; u32 expire; unsigned int nbits = 0; const char *curve = NULL; u32 cur_time; char *key_from_hexgrip = NULL; char *hexgrip = NULL; char *serialno = NULL; char *cache_nonce = NULL; char *passwd_nonce = NULL; int keygen_flags = 0; interactive = (!algostr || !usagestr || !expirestr); /* Break out the primary key. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; primary key missing in keyblock!\n"); err = gpg_error (GPG_ERR_BUG); goto leave; } pri_psk = node->pkt->pkt.public_key; cur_time = make_timestamp (); if (pri_psk->timestamp > cur_time) { ulong d = pri_psk->timestamp - cur_time; log_info ( d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_psk->version < 4) { log_info (_("Note: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_CONFLICT); goto leave; } err = hexkeygrip_from_pk (pri_psk, &hexgrip); if (err) goto leave; if (agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) { if (interactive) tty_printf (_("Secret parts of primary key are not available.\n")); else log_info ( _("Secret parts of primary key are not available.\n")); err = gpg_error (GPG_ERR_NO_SECKEY); goto leave; } if (serialno) { if (interactive) tty_printf (_("Secret parts of primary key are stored on-card.\n")); else log_info ( _("Secret parts of primary key are stored on-card.\n")); } if (interactive) { algo = ask_algo (ctrl, 1, NULL, &use, &key_from_hexgrip); log_assert (algo); if (key_from_hexgrip) nbits = 0; else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH) curve = ask_curve (&algo, NULL, NULL); else nbits = ask_keysize (algo, 0); expire = ask_expire_interval (0, NULL); if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } } else /* Unattended mode. */ { int version; err = parse_algo_usage_expire (ctrl, 1, algostr, usagestr, expirestr, &algo, &use, &expire, &nbits, &curve, &version); if (err) goto leave; if (version == 5) keygen_flags |= KEYGEN_FLAG_CREATE_V5_KEY; } /* Verify the passphrase now so that we get a cache item for the * primary key passphrase. The agent also returns a passphrase * nonce, which we can use to set the passphrase for the subkey to * that of the primary key. */ { char *desc = gpg_format_keydesc (ctrl, pri_psk, FORMAT_KEYDESC_NORMAL, 1); err = agent_passwd (ctrl, hexgrip, desc, 1 /*=verify*/, &cache_nonce, &passwd_nonce); xfree (desc); if (gpg_err_code (err) == GPG_ERR_NOT_IMPLEMENTED && gpg_err_source (err) == GPG_ERR_SOURCE_GPGAGENT) err = 0; /* Very likely that the key is on a card. */ if (err) goto leave; } /* Start creation. */ if (key_from_hexgrip) { err = do_create_from_keygrip (ctrl, algo, key_from_hexgrip, keyblock, cur_time, expire, 1, keygen_flags); } else { const char *passwd; /* If the pinentry loopback mode is not and we have a static passphrase (i.e. set with --passphrase{,-fd,-file} while in batch mode), we use that passphrase for the new subkey. */ if (opt.pinentry_mode != PINENTRY_MODE_LOOPBACK && have_static_passphrase ()) passwd = get_static_passphrase (); else passwd = NULL; err = do_create (algo, nbits, curve, keyblock, cur_time, expire, 1, keygen_flags, passwd, &cache_nonce, &passwd_nonce); } if (err) goto leave; /* Get the pointer to the generated public subkey packet. */ for (node = keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_psk = node->pkt->pkt.public_key; /* Write the binding signature. */ err = write_keybinding (ctrl, keyblock, pri_psk, sub_psk, use, cur_time, cache_nonce); if (err) goto leave; print_status_key_created ('S', sub_psk, NULL); leave: xfree (key_from_hexgrip); xfree (hexgrip); xfree (serialno); xfree (cache_nonce); xfree (passwd_nonce); if (err) log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); return err; } #ifdef ENABLE_CARD_SUPPORT /* Generate a subkey on a card. */ gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int keyno, const char *serialno) { gpg_error_t err = 0; kbnode_t node; PKT_public_key *pri_pk = NULL; unsigned int use; u32 expire; u32 cur_time; struct para_data_s *para = NULL; PKT_public_key *sub_pk = NULL; int algo; struct agent_card_info_s info; int keygen_flags = 0; /* FIXME!!! */ log_assert (keyno >= 1 && keyno <= 3); memset (&info, 0, sizeof (info)); err = agent_scd_getattr ("KEY-ATTR", &info); if (err) { log_error (_("error getting current key info: %s\n"), gpg_strerror (err)); return err; } algo = info.key_attr[keyno-1].algo; para = xtrycalloc (1, sizeof *para + strlen (serialno) ); if (!para) { err = gpg_error_from_syserror (); goto leave; } para->key = pSERIALNO; strcpy (para->u.value, serialno); /* Break out the primary secret key */ node = find_kbnode (pub_keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; public key lost!\n"); err = gpg_error (GPG_ERR_INTERNAL); goto leave; } pri_pk = node->pkt->pkt.public_key; cur_time = make_timestamp(); if (pri_pk->timestamp > cur_time) { ulong d = pri_pk->timestamp - cur_time; log_info (d==1 ? _("key has been created %lu second " "in future (time warp or clock problem)\n") : _("key has been created %lu seconds " "in future (time warp or clock problem)\n"), d ); if (!opt.ignore_time_conflict) { err = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } } if (pri_pk->version < 4) { log_info (_("Note: creating subkeys for v3 keys " "is not OpenPGP compliant\n")); err = gpg_error (GPG_ERR_NOT_SUPPORTED); goto leave; } expire = ask_expire_interval (0, NULL); if (keyno == 1) use = PUBKEY_USAGE_SIG; else if (keyno == 2) use = PUBKEY_USAGE_ENC; else use = PUBKEY_USAGE_AUTH; if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay", _("Really create? (y/N) "))) { err = gpg_error (GPG_ERR_CANCELED); goto leave; } /* Note, that depending on the backend, the card key generation may update CUR_TIME. */ err = gen_card_key (keyno, algo, 0, pub_keyblock, &cur_time, expire, keygen_flags); /* Get the pointer to the generated public subkey packet. */ if (!err) { for (node = pub_keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) sub_pk = node->pkt->pkt.public_key; log_assert (sub_pk); err = write_keybinding (ctrl, pub_keyblock, pri_pk, sub_pk, use, cur_time, NULL); } leave: if (err) log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); else print_status_key_created ('S', sub_pk, NULL); release_parameter_list (para); return err; } #endif /* !ENABLE_CARD_SUPPORT */ /* * Write a keyblock to an output stream */ static int write_keyblock( IOBUF out, KBNODE node ) { for( ; node ; node = node->next ) { if(!is_deleted_kbnode(node)) { int rc = build_packet( out, node->pkt ); if( rc ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); return rc; } } } return 0; } /* Note that timestamp is an in/out arg. * FIXME: Does not yet support v5 keys. */ static gpg_error_t gen_card_key (int keyno, int algo, int is_primary, kbnode_t pub_root, u32 *timestamp, u32 expireval, int keygen_flags) { #ifdef ENABLE_CARD_SUPPORT gpg_error_t err; PACKET *pkt; PKT_public_key *pk; char keyid[10]; unsigned char *public; gcry_sexp_t s_key; snprintf (keyid, DIM(keyid), "OPENPGP.%d", keyno); pk = xtrycalloc (1, sizeof *pk ); if (!pk) return gpg_error_from_syserror (); pkt = xtrycalloc (1, sizeof *pkt); if (!pkt) { xfree (pk); return gpg_error_from_syserror (); } /* Note: SCD knows the serialnumber, thus there is no point in passing it. */ err = agent_scd_genkey (keyno, 1, timestamp); /* The code below is not used because we force creation of * the a card key (3rd arg). * if (gpg_err_code (rc) == GPG_ERR_EEXIST) * { * tty_printf ("\n"); * log_error ("WARNING: key does already exists!\n"); * tty_printf ("\n"); * if ( cpr_get_answer_is_yes( "keygen.card.replace_key", * _("Replace existing key? "))) * rc = agent_scd_genkey (keyno, 1, timestamp); * } */ if (err) { log_error ("key generation failed: %s\n", gpg_strerror (err)); xfree (pkt); xfree (pk); return err; } /* Send the READKEY command so that the agent creates a shadow key for card key. We need to do that now so that we are able to create the self-signatures. */ err = agent_readkey (NULL, 1, keyid, &public); if (err) return err; err = gcry_sexp_sscan (&s_key, NULL, public, gcry_sexp_canon_len (public, 0, NULL, NULL)); xfree (public); if (err) return err; if (algo == PUBKEY_ALGO_RSA) err = key_from_sexp (pk->pkey, s_key, "public-key", "ne"); else if (algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_EDDSA || algo == PUBKEY_ALGO_ECDH ) err = ecckey_from_sexp (pk->pkey, s_key, algo); else err = gpg_error (GPG_ERR_PUBKEY_ALGO); gcry_sexp_release (s_key); if (err) { log_error ("key_from_sexp failed: %s\n", gpg_strerror (err) ); free_public_key (pk); return err; } pk->timestamp = *timestamp; pk->version = (keygen_flags & KEYGEN_FLAG_CREATE_V5_KEY)? 5 : 4; if (expireval) pk->expiredate = pk->timestamp + expireval; pk->pubkey_algo = algo; pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY; pkt->pkt.public_key = pk; add_kbnode (pub_root, new_kbnode (pkt)); return 0; #else (void)keyno; (void)is_primary; (void)pub_root; (void)timestamp; (void)expireval; return gpg_error (GPG_ERR_NOT_SUPPORTED); #endif /*!ENABLE_CARD_SUPPORT*/ } diff --git a/g10/keylist.c b/g10/keylist.c index 1274775d2..5f5fe9d34 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -1,2215 +1,2215 @@ /* keylist.c - Print information about OpenPGP keys * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2008, 2010, 2012 Free Software Foundation, Inc. * Copyright (C) 2013, 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #ifdef HAVE_DOSISH_SYSTEM # include /* for setmode() */ #endif #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "photoid.h" #include "../common/util.h" #include "../common/ttyio.h" #include "trustdb.h" #include "main.h" #include "../common/i18n.h" #include "../common/status.h" #include "call-agent.h" #include "../common/mbox-util.h" #include "../common/zb32.h" #include "tofu.h" #include "../common/compliance.h" #include "../common/pkscreening.h" static void list_all (ctrl_t, int, int); static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret); static void locate_one (ctrl_t ctrl, strlist_t names, int no_local); static void print_card_serialno (const char *serialno); struct keylist_context { int check_sigs; /* If set signatures shall be verified. */ int good_sigs; /* Counter used if CHECK_SIGS is set. */ int inv_sigs; /* Counter used if CHECK_SIGS is set. */ int no_key; /* Counter used if CHECK_SIGS is set. */ int oth_err; /* Counter used if CHECK_SIGS is set. */ 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); /* The stream used to write attribute packets to. */ static estream_t attrib_fp; /* Release resources from a keylist context. */ static void keylist_context_release (struct keylist_context *listctx) { (void)listctx; /* Nothing to release. */ } /* 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 * keyring. */ void public_key_list (ctrl_t ctrl, strlist_t list, int locate_mode, int no_local) { #ifndef NO_TRUST_MODELS if (opt.with_colons) { byte trust_model, marginals, completes, cert_depth, min_cert_level; ulong created, nextcheck; read_trust_options (ctrl, &trust_model, &created, &nextcheck, &marginals, &completes, &cert_depth, &min_cert_level); es_fprintf (es_stdout, "tru:"); if (nextcheck && nextcheck <= make_timestamp ()) es_fprintf (es_stdout, "o"); if (trust_model != opt.trust_model) es_fprintf (es_stdout, "t"); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP) { if (marginals != opt.marginals_needed) es_fprintf (es_stdout, "m"); if (completes != opt.completes_needed) es_fprintf (es_stdout, "c"); if (cert_depth != opt.max_cert_depth) es_fprintf (es_stdout, "d"); if (min_cert_level != opt.min_cert_level) es_fprintf (es_stdout, "l"); } es_fprintf (es_stdout, ":%d:%lu:%lu", trust_model, created, nextcheck); /* Only show marginals, completes, and cert_depth in the classic or PGP trust models since they are not meaningful otherwise. */ if (trust_model == TM_PGP || trust_model == TM_CLASSIC) es_fprintf (es_stdout, ":%d:%d:%d", marginals, completes, cert_depth); es_fprintf (es_stdout, "\n"); } #endif /*!NO_TRUST_MODELS*/ /* We need to do the stale check right here because it might need to update the keyring while we already have the keyring open. This is very bad for W32 because of a sharing violation. For real OSes it might lead to false results if we are later listing a keyring which is associated with the inode of a deleted file. */ check_trustdb_stale (ctrl); #ifdef USE_TOFU tofu_begin_batch_update (ctrl); #endif if (locate_mode) locate_one (ctrl, list, no_local); else if (!list) list_all (ctrl, 0, opt.with_secret); else list_one (ctrl, list, 0, opt.with_secret); #ifdef USE_TOFU tofu_end_batch_update (ctrl); #endif } void secret_key_list (ctrl_t ctrl, strlist_t list) { (void)ctrl; check_trustdb_stale (ctrl); if (!list) list_all (ctrl, 1, 0); else /* List by user id */ list_one (ctrl, list, 1, 0); } /* Helper for print_key_info and print_key_info_log. */ static char * format_key_info (ctrl_t ctrl, PKT_public_key *pk, int secret) { u32 keyid[2]; char *p; char pkstrbuf[PUBKEY_STRING_SIZE]; char *result; keyid_from_pk (pk, keyid); /* If the pk was chosen by a particular user ID, that is the one to print. */ if (pk->user_id) p = utf8_to_native (pk->user_id->name, pk->user_id->len, 0); else p = get_user_id_native (ctrl, keyid); result = xtryasprintf ("%s %s/%s %s %s", secret? (pk->flags.primary? "sec":"ssb") /* */ : (pk->flags.primary? "pub":"sub"), pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr (keyid), datestr_from_pk (pk), p); xfree (p); return result; } /* Print basic information about a public or secret key. With FP * passed as NULL, the tty output interface is used, otherwise output * is directed to the given stream. INDENT gives the requested * indentation; if that is a negative value indentation is suppressed * for the first line. SECRET tells that the PK has a secret part. * FIXME: This is similar in use to print_key_line and thus both * functions should eventually be united. */ void print_key_info (ctrl_t ctrl, estream_t fp, int indent, PKT_public_key *pk, int secret) { int indentabs = indent >= 0? indent : -indent; char *info; /* Note: Negative values for INDENT are not yet needed. */ info = format_key_info (ctrl, pk, secret); if (!fp && indent >= 0) tty_printf ("\n"); /* (Backward compatibility to old code) */ tty_fprintf (fp, "%*s%s\n", indentabs, "", info? info : "[Ooops - out of core]"); xfree (info); } /* Same as print_key_info put print using the log functions at * LOGLEVEL. */ void print_key_info_log (ctrl_t ctrl, int loglevel, int indent, PKT_public_key *pk, int secret) { int indentabs = indent >= 0? indent : -indent; char *info; info = format_key_info (ctrl, pk, secret); log_log (loglevel, "%*s%s\n", indentabs, "", info? info : "[Ooops - out of core]"); xfree (info); } /* Print basic information of a secret key including the card serial number information. */ #ifdef ENABLE_CARD_SUPPORT void print_card_key_info (estream_t fp, kbnode_t keyblock) { kbnode_t node; char *hexgrip; char *serialno; int s2k_char; char pkstrbuf[PUBKEY_STRING_SIZE]; int indent; for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { int rc; PKT_public_key *pk = node->pkt->pkt.public_key; serialno = NULL; rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) { log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); s2k_char = '?'; } else if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) s2k_char = serialno? '>':' '; else s2k_char = '#'; /* Key not found. */ tty_fprintf (fp, "%s%c %s/%s %n", node->pkt->pkttype == PKT_PUBLIC_KEY ? "sec" : "ssb", s2k_char, pubkey_string (pk, pkstrbuf, sizeof pkstrbuf), keystr_from_pk (pk), &indent); tty_fprintf (fp, _("created: %s"), datestr_from_pk (pk)); tty_fprintf (fp, " "); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); if (serialno) { tty_fprintf (fp, "\n%*s%s", indent, "", _("card-no: ")); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ tty_fprintf (fp, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else tty_fprintf (fp, "%s", serialno); } tty_fprintf (fp, "\n"); xfree (hexgrip); xfree (serialno); } } } #endif /*ENABLE_CARD_SUPPORT*/ /* Flags = 0x01 hashed 0x02 critical. */ static void status_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { char status[40]; /* Don't print these. */ if (len > 256) return; snprintf (status, sizeof status, "%d %u %u ", type, flags, (unsigned int) len); write_status_text_and_buffer (STATUS_SIG_SUBPACKET, status, buf, len, 0); } /* Print a policy URL. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. */ void show_policy_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_POLICY, &len, &seq, &crit))) { if (mode != 2) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (crit) str = _("Critical signature policy: "); else str = _("Signature policy: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (fp, "%s", str); tty_print_utf8_string2 (fp, p, len, 0); tty_fprintf (fp, "\n"); } if (mode > 0) write_status_buffer (STATUS_POLICY_URL, p, len, 0); } } /* Print a keyserver URL. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. */ void show_keyserver_url (PKT_signature * sig, int indent, int mode) { const byte *p; size_t len; int seq = 0, crit; estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; while ((p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_KS, &len, &seq, &crit))) { if (mode != 2) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (crit) str = _("Critical preferred keyserver: "); else str = _("Preferred keyserver: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (fp, "%s", str); tty_print_utf8_string2 (fp, p, len, 0); tty_fprintf (fp, "\n"); } if (mode > 0) status_one_subpacket (SIGSUBPKT_PREF_KS, len, (crit ? 0x02 : 0) | 0x01, p); } } /* Print notation data. Allowed values for MODE are: * -1 - print to the TTY * 0 - print to stdout. * 1 - use log_info and emit status messages. * 2 - emit only status messages. * * Defined bits in WHICH: * 1 - standard notations * 2 - user notations */ void show_notation (PKT_signature * sig, int indent, int mode, int which) { estream_t fp = mode < 0? NULL : mode ? log_get_stream () : es_stdout; notation_t nd, notations; if (which == 0) which = 3; notations = sig_to_notation (sig); /* There may be multiple notations in the same sig. */ for (nd = notations; nd; nd = nd->next) { if (mode != 2) { int has_at = !!strchr (nd->name, '@'); if ((which & 1 && !has_at) || (which & 2 && has_at)) { const char *str; tty_fprintf (fp, "%*s", indent, ""); if (nd->flags.critical) str = _("Critical signature notation: "); else str = _("Signature notation: "); if (mode > 0) log_info ("%s", str); else tty_fprintf (fp, "%s", str); /* This is all UTF8 */ tty_print_utf8_string2 (fp, nd->name, strlen (nd->name), 0); tty_fprintf (fp, "="); tty_print_utf8_string2 (fp, nd->value, strlen (nd->value), 0); /* (We need to use log_printf so that the next call to a log function does not insert an extra LF.) */ if (mode > 0) log_printf ("\n"); else tty_fprintf (fp, "\n"); } } if (mode > 0) { write_status_buffer (STATUS_NOTATION_NAME, nd->name, strlen (nd->name), 0); if (nd->flags.critical || nd->flags.human) write_status_text (STATUS_NOTATION_FLAGS, nd->flags.critical && nd->flags.human? "1 1" : nd->flags.critical? "1 0" : "0 1"); write_status_buffer (STATUS_NOTATION_DATA, nd->value, strlen (nd->value), 50); } } free_notation (notations); } static void print_signature_stats (struct keylist_context *s) { if (!s->check_sigs) return; /* Signature checking was not requested. */ /* Better flush stdout so that the stats are always printed after * the output. */ es_fflush (es_stdout); if (s->good_sigs) log_info (ngettext("%d good signature\n", "%d good signatures\n", s->good_sigs), s->good_sigs); if (s->inv_sigs) log_info (ngettext("%d bad signature\n", "%d bad signatures\n", s->inv_sigs), s->inv_sigs); if (s->no_key) log_info (ngettext("%d signature not checked due to a missing key\n", "%d signatures not checked due to missing keys\n", s->no_key), s->no_key); if (s->oth_err) log_info (ngettext("%d signature not checked due to an error\n", "%d signatures not checked due to errors\n", s->oth_err), s->oth_err); } /* List all keys. If SECRET is true only secret keys are listed. If MARK_SECRET is true secret keys are indicated in a public key listing. */ static void list_all (ctrl_t ctrl, int secret, int mark_secret) { KEYDB_HANDLE hd; KBNODE keyblock = NULL; int rc = 0; int any_secret; const char *lastresname, *resname; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) rc = gpg_error_from_syserror (); else rc = keydb_search_first (hd); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_first failed: %s\n", gpg_strerror (rc)); goto leave; } lastresname = NULL; do { rc = keydb_get_keyblock (hd, &keyblock); if (rc) { if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; /* Skip legacy keys. */ log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto leave; } if (secret || mark_secret) any_secret = !agent_probe_any_secret_key (NULL, keyblock); else any_secret = 0; if (secret && !any_secret) ; /* Secret key listing requested but this isn't one. */ else { if (!opt.with_colons && !(opt.list_options & LIST_SHOW_ONLY_FPR_MBOX)) { resname = keydb_get_resource_name (hd); if (lastresname != resname) { int i; es_fprintf (es_stdout, "%s\n", resname); for (i = strlen (resname); i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); lastresname = resname; } } merge_keys_and_selfsig (ctrl, keyblock); list_keyblock (ctrl, keyblock, secret, any_secret, opt.fingerprint, &listctx); } release_kbnode (keyblock); keyblock = NULL; } while (!(rc = keydb_search_next (hd))); es_fflush (es_stdout); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); if (keydb_get_skipped_counter (hd)) log_info (ngettext("Warning: %lu key skipped due to its large size\n", "Warning: %lu keys skipped due to their large sizes\n", keydb_get_skipped_counter (hd)), keydb_get_skipped_counter (hd)); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); leave: keylist_context_release (&listctx); release_kbnode (keyblock); keydb_release (hd); } static void list_one (ctrl_t ctrl, strlist_t names, int secret, int mark_secret) { int rc = 0; KBNODE keyblock = NULL; GETKEY_CTX ctx; const char *resname; const char *keyring_str = _("Keyring"); int i; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (!secret && opt.check_sigs) listctx.check_sigs = 1; /* fixme: using the bynames function has the disadvantage that we * don't know whether one of the names given was not found. OTOH, * this function has the advantage to list the names in the * sequence as defined by the keyDB and does not duplicate * outputs. A solution could be do test whether all given have * been listed (this needs a way to use the keyDB search * functions) or to have the search function return indicators for * found names. Yet another way is to use the keydb search * facilities directly. */ rc = getkey_bynames (ctrl, &ctx, NULL, names, secret, &keyblock); if (rc) { log_error ("error reading key: %s\n", gpg_strerror (rc)); getkey_end (ctrl, ctx); write_status_error ("keylist.getkey", rc); return; } do { if ((opt.list_options & LIST_SHOW_KEYRING) && !opt.with_colons) { resname = keydb_get_resource_name (get_ctx_handle (ctx)); es_fprintf (es_stdout, "%s: %s\n", keyring_str, resname); for (i = strlen (resname) + strlen (keyring_str) + 2; i; i--) es_putc ('-', es_stdout); es_putc ('\n', es_stdout); } list_keyblock (ctrl, keyblock, secret, mark_secret, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (!getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void locate_one (ctrl_t ctrl, strlist_t names, int no_local) { int rc = 0; strlist_t sl; GETKEY_CTX ctx = NULL; KBNODE keyblock = NULL; struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); if (opt.check_sigs) listctx.check_sigs = 1; for (sl = names; sl; sl = sl->next) { rc = get_best_pubkey_byname (ctrl, no_local? GET_PUBKEY_NO_LOCAL /* */: GET_PUBKEY_NORMAL, &ctx, NULL, sl->d, &keyblock, 1); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NO_PUBKEY) log_error ("error reading key: %s\n", gpg_strerror (rc)); else if (opt.verbose) log_info (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (rc)); } else { do { list_keyblock (ctrl, keyblock, 0, 0, opt.fingerprint, &listctx); release_kbnode (keyblock); } while (ctx && !getkey_next (ctrl, ctx, NULL, &keyblock)); getkey_end (ctrl, ctx); ctx = NULL; } } if (opt.check_sigs && !opt.with_colons) print_signature_stats (&listctx); keylist_context_release (&listctx); } static void print_key_data (PKT_public_key * pk) { int n = pk ? pubkey_get_npkey (pk->pubkey_algo) : 0; int i; for (i = 0; i < n; i++) { es_fprintf (es_stdout, "pkd:%d:%u:", i, mpi_get_nbits (pk->pkey[i])); mpi_print (es_stdout, pk->pkey[i], 1); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } } /* Various public key screenings. (Right now just ROCA). With * COLON_MODE set the output is formatted for use in the compliance * field of a colon listing. */ static void print_pk_screening (PKT_public_key *pk, int colon_mode) { gpg_error_t err; if (is_RSA (pk->pubkey_algo) && pubkey_get_npkey (pk->pubkey_algo)) { err = screen_key_for_roca (pk->pkey[0]); if (!err) ; else if (gpg_err_code (err) == GPG_ERR_TRUE) { if (colon_mode) es_fprintf (es_stdout, colon_mode > 1? " %d":"%d", 6001); else es_fprintf (es_stdout, " Screening: ROCA vulnerability detected\n"); } else if (!colon_mode) es_fprintf (es_stdout, " Screening: [ROCA check failed: %s]\n", gpg_strerror (err)); } } static void print_capabilities (ctrl_t ctrl, PKT_public_key *pk, KBNODE keyblock) { unsigned int use = pk->pubkey_usage; int c_printed = 0; if (use & PUBKEY_USAGE_ENC) es_putc ('e', es_stdout); if (use & PUBKEY_USAGE_SIG) { es_putc ('s', es_stdout); if (pk->flags.primary) { es_putc ('c', es_stdout); /* The PUBKEY_USAGE_CERT flag was introduced later and we used to always print 'c' for a primary key. To avoid any regression here we better track whether we printed 'c' already. */ c_printed = 1; } } if ((use & PUBKEY_USAGE_CERT) && !c_printed) es_putc ('c', es_stdout); if ((use & PUBKEY_USAGE_AUTH)) es_putc ('a', es_stdout); if ((use & PUBKEY_USAGE_UNKNOWN)) es_putc ('?', es_stdout); if (keyblock) { /* Figure out the usable capabilities. */ KBNODE k; int enc = 0, sign = 0, cert = 0, auth = 0, disabled = 0; for (k = keyblock; k; k = k->next) { if (k->pkt->pkttype == PKT_PUBLIC_KEY || k->pkt->pkttype == PKT_PUBLIC_SUBKEY) { pk = k->pkt->pkt.public_key; if (pk->flags.primary) disabled = pk_is_disabled (pk); if (pk->flags.valid && !pk->flags.revoked && !pk->has_expired) { if (pk->pubkey_usage & PUBKEY_USAGE_ENC) enc = 1; if (pk->pubkey_usage & PUBKEY_USAGE_SIG) { sign = 1; if (pk->flags.primary) cert = 1; } if (pk->pubkey_usage & PUBKEY_USAGE_CERT) cert = 1; if ((pk->pubkey_usage & PUBKEY_USAGE_AUTH)) auth = 1; } } } if (enc) es_putc ('E', es_stdout); if (sign) es_putc ('S', es_stdout); if (cert) es_putc ('C', es_stdout); if (auth) es_putc ('A', es_stdout); if (disabled) es_putc ('D', es_stdout); } es_putc (':', es_stdout); } /* FLAGS: 0x01 hashed 0x02 critical */ static void print_one_subpacket (sigsubpkttype_t type, size_t len, int flags, const byte * buf) { size_t i; es_fprintf (es_stdout, "spk:%d:%u:%u:", type, flags, (unsigned int) len); for (i = 0; i < len; i++) { /* printable ascii other than : and % */ if (buf[i] >= 32 && buf[i] <= 126 && buf[i] != ':' && buf[i] != '%') es_fprintf (es_stdout, "%c", buf[i]); else es_fprintf (es_stdout, "%%%02X", buf[i]); } es_fprintf (es_stdout, "\n"); } void print_subpackets_colon (PKT_signature * sig) { byte *i; log_assert (opt.show_subpackets); for (i = opt.show_subpackets; *i; i++) { const byte *p; size_t len; int seq, crit; seq = 0; while ((p = enum_sig_subpkt (sig->hashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x01 | (crit ? 0x02 : 0), p); seq = 0; while ((p = enum_sig_subpkt (sig->unhashed, *i, &len, &seq, &crit))) print_one_subpacket (*i, len, 0x00 | (crit ? 0x02 : 0), p); } } void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk) { int i; if (!attrib_fp) return; for (i = 0; i < uid->numattribs; i++) { if (is_status_enabled ()) { byte array[MAX_FINGERPRINT_LEN], *p; char buf[(MAX_FINGERPRINT_LEN * 2) + 90]; size_t j, n; if (!pk) BUG (); fingerprint_from_pk (pk, array, &n); p = array; for (j = 0; j < n; j++, p++) sprintf (buf + 2 * j, "%02X", *p); sprintf (buf + strlen (buf), " %lu %u %u %u %lu %lu %u", (ulong) uid->attribs[i].len, uid->attribs[i].type, i + 1, uid->numattribs, (ulong) uid->created, (ulong) uid->expiredate, ((uid->flags.primary ? 0x01 : 0) | (uid->flags.revoked ? 0x02 : 0) | (uid->flags.expired ? 0x04 : 0))); write_status_text (STATUS_ATTRIBUTE, buf); } es_fwrite (uid->attribs[i].data, uid->attribs[i].len, 1, attrib_fp); es_fflush (attrib_fp); } } static void list_keyblock_print (ctrl_t ctrl, kbnode_t keyblock, int secret, int fpr, struct keylist_context *listctx) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; int skip_sigs = 0; char *hexgrip = NULL; char *serialno = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { /* Encode some info about the secret key in SECRET. */ if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = 2; /* Key not found. */ } if (!listctx->no_validity) check_trustdb_stale (ctrl); /* Print the "pub" line and in KF_NONE mode the fingerprint. */ print_key_line (ctrl, es_stdout, pk, secret); if (fpr) print_fingerprint (ctrl, NULL, pk, 0); if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (serialno) print_card_serialno (serialno); if (opt.with_key_data) print_key_data (pk); if (opt.with_key_screening) print_pk_screening (pk, 0); if (opt.with_key_origin && (pk->keyorg || pk->keyupdate || pk->updateurl)) { char updatestr[MK_DATESTR_SIZE]; es_fprintf (es_stdout, " origin=%s last=%s %s", key_origin_string (pk->keyorg), mk_datestr (updatestr, sizeof updatestr, pk->keyupdate), pk->updateurl? "url=":""); if (pk->updateurl) print_utf8_string (es_stdout, pk->updateurl); es_putc ('\n', es_stdout); } for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int indent; int kl = opt.keyid_format == KF_NONE? 10 : keystrlen (); if ((uid->flags.expired || uid->flags.revoked) && !(opt.list_options & LIST_SHOW_UNUSABLE_UIDS)) { skip_sigs = 1; continue; } else skip_sigs = 0; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if ((uid->flags.revoked || uid->flags.expired) || ((opt.list_options & LIST_SHOW_UID_VALIDITY) && !listctx->no_validity)) { const char *validity; validity = uid_trust_string_fixed (ctrl, pk, uid); indent = ((kl + (opt.legacy_list_mode? 9:11)) - atoi (uid_trust_string_fixed (ctrl, NULL, NULL))); if (indent < 0 || indent > 40) indent = 0; es_fprintf (es_stdout, "uid%*s%s ", indent, "", validity); } else { indent = kl + (opt.legacy_list_mode? 10:12); es_fprintf (es_stdout, "uid%*s", indent, ""); } print_utf8_buffer (es_stdout, uid->name, uid->len); es_putc ('\n', es_stdout); if (opt.with_wkd_hash) { char *mbox, *hash, *p; char hashbuf[32]; mbox = mailbox_from_userid (uid->name, 0); if (mbox && (p = strchr (mbox, '@'))) { *p++ = 0; gcry_md_hash_buffer (GCRY_MD_SHA1, hashbuf, mbox, strlen (mbox)); hash = zb32_encode (hashbuf, 8*20); if (hash) { es_fprintf (es_stdout, " %*s%s@%s\n", indent, "", hash, p); xfree (hash); } } xfree (mbox); } if (opt.with_key_origin && (uid->keyorg || uid->keyupdate || uid->updateurl)) { char updatestr[MK_DATESTR_SIZE]; es_fprintf (es_stdout, " %*sorigin=%s last=%s %s", indent, "", key_origin_string (uid->keyorg), mk_datestr (updatestr, sizeof updatestr, uid->keyupdate), uid->updateurl? "url=":""); if (uid->updateurl) print_utf8_string (es_stdout, uid->updateurl); es_putc ('\n', es_stdout); } if ((opt.list_options & LIST_SHOW_PHOTOS) && uid->attribs != NULL) show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid); } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { PKT_public_key *pk2 = node->pkt->pkt.public_key; if ((pk2->flags.revoked || pk2->has_expired) && !(opt.list_options & LIST_SHOW_UNUSABLE_SUBKEYS)) { skip_sigs = 1; continue; } else skip_sigs = 0; xfree (serialno); serialno = NULL; xfree (hexgrip); hexgrip = NULL; if (secret || opt.with_keygrip) { rc = hexkeygrip_from_pk (pk2, &hexgrip); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); } if (secret) { if (!agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) secret = serialno? 3 : 1; else secret = 2; /* Key not found. */ } /* Print the "sub" line. */ print_key_line (ctrl, es_stdout, pk2, secret); if (fpr > 1 || opt.with_subkey_fingerprint) { print_fingerprint (ctrl, NULL, pk2, 0); if (serialno) print_card_serialno (serialno); } if (opt.with_keygrip && hexgrip) es_fprintf (es_stdout, " Keygrip = %s\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); if (opt.with_key_screening) print_pk_screening (pk2, 0); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE && !skip_sigs) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc; char *sigstr; char *reason_text = NULL; char *reason_comment = NULL; size_t reason_commentlen; if (listctx->check_sigs) { rc = check_key_signature (ctrl, keyblock, node, NULL); switch (gpg_err_code (rc)) { case 0: listctx->good_sigs++; sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: listctx->inv_sigs++; sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: listctx->no_key++; continue; default: listctx->oth_err++; sigrc = '%'; break; } /* TODO: Make sure a cached sig record here still has the pk that issued it. See also keyedit.c:print_and_check_one_sig */ } else { rc = 0; sigrc = ' '; } if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) { sigstr = "rev"; get_revocation_reason (sig, &reason_text, &reason_comment, &reason_commentlen); } else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig " "[unexpected signature class 0x%02x]\n", sig->sig_class); continue; } es_fputs (sigstr, es_stdout); es_fprintf (es_stdout, "%c%c %c%c%c%c%c%c %s %s", sigrc, (sig->sig_class - 0x10 > 0 && sig->sig_class - 0x10 < 4) ? '0' + sig->sig_class - 0x10 : ' ', sig->flags.exportable ? ' ' : 'L', sig->flags.revocable ? ' ' : 'R', sig->flags.policy_url ? 'P' : ' ', sig->flags.notation ? 'N' : ' ', sig->flags.expired ? 'X' : ' ', (sig->trust_depth > 9) ? 'T' : (sig->trust_depth > 0) ? '0' + sig->trust_depth : ' ', keystr (sig->keyid), datestr_from_sig (sig)); if (opt.list_options & LIST_SHOW_SIG_EXPIRE) es_fprintf (es_stdout, " %s", expirestr_from_sig (sig)); es_fprintf (es_stdout, " "); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (sigrc == '?') ; else if (!opt.fast_list_mode) { size_t n; char *p = get_user_id (ctrl, sig->keyid, &n, NULL); print_utf8_buffer (es_stdout, p, n); xfree (p); } es_putc ('\n', es_stdout); if (sig->flags.policy_url && (opt.list_options & LIST_SHOW_POLICY_URLS)) show_policy_url (sig, 3, 0); if (sig->flags.notation && (opt.list_options & LIST_SHOW_NOTATIONS)) show_notation (sig, 3, 0, ((opt. list_options & LIST_SHOW_STD_NOTATIONS) ? 1 : 0) + ((opt. list_options & LIST_SHOW_USER_NOTATIONS) ? 2 : 0)); if (sig->flags.pref_ks && (opt.list_options & LIST_SHOW_KEYSERVER_URLS)) show_keyserver_url (sig, 3, 0); if (reason_text) { es_fprintf (es_stdout, " %s%s\n", _("reason for revocation: "), reason_text); if (reason_comment) { const byte *s, *s_lf; size_t n, n_lf; s = reason_comment; n = reason_commentlen; s_lf = NULL; do { /* We don't want any empty lines, so we skip them. */ for (;n && *s == '\n'; s++, n--) ; if (n) { s_lf = memchr (s, '\n', n); n_lf = s_lf? s_lf - s : n; es_fprintf (es_stdout, " %s", _("revocation comment: ")); es_write_sanitized (es_stdout, s, n_lf, NULL, NULL); es_putc ('\n', es_stdout); s += n_lf; n -= n_lf; } } while (s_lf); } } xfree (reason_text); xfree (reason_comment); /* fixme: check or list other sigs here */ } } es_putc ('\n', es_stdout); xfree (serialno); xfree (hexgrip); } /* Do a simple key listing printing only the fingerprint and the mail * address of valid keys. */ static void list_keyblock_simple (ctrl_t ctrl, kbnode_t keyblock) { gpg_err_code_t ec; kbnode_t kbctx; kbnode_t node; char hexfpr[2*MAX_FINGERPRINT_LEN+1]; char *mbox; (void)ctrl; node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } hexfingerprint (node->pkt->pkt.public_key, hexfpr, sizeof hexfpr); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; if (uid->attrib_data) continue; if (uid->flags.expired || uid->flags.revoked) continue; mbox = mailbox_from_userid (uid->name, 0); if (!mbox) { ec = gpg_err_code_from_syserror (); if (ec != GPG_ERR_EINVAL) log_error ("error getting mailbox from user-id: %s\n", gpg_strerror (ec)); continue; } es_fprintf (es_stdout, "%s %s\n", hexfpr, mbox); xfree (mbox); } } } void print_revokers (estream_t fp, PKT_public_key * pk) { /* print the revoker record */ if (!pk->revkey && pk->numrevkeys) BUG (); else { int i, j; for (i = 0; i < pk->numrevkeys; i++) { byte *p; es_fprintf (fp, "rvk:::%d::::::", pk->revkey[i].algid); p = pk->revkey[i].fpr; for (j = 0; j < pk->revkey[i].fprlen; j++, p++) es_fprintf (fp, "%02X", *p); es_fprintf (fp, ":%02x%s:\n", pk->revkey[i].class, (pk->revkey[i].class & 0x40) ? "s" : ""); } } } /* Print the compliance flags to field 18. PK is the public key. * KEYLENGTH is the length of the key in bits and CURVENAME is either * NULL or the name of the curve. The latter two args are here * merely because the caller has already computed them. */ static void print_compliance_flags (PKT_public_key *pk, unsigned int keylength, const char *curvename) { int any = 0; if (!keylength) keylength = nbits_from_pk (pk); if (pk->version == 5) { es_fputs (gnupg_status_compliance_flag (CO_GNUPG), es_stdout); any++; } if (gnupg_pk_is_compliant (CO_DE_VS, pk->pubkey_algo, pk->pkey, keylength, curvename)) { es_fprintf (es_stdout, any ? " %s" : "%s", gnupg_status_compliance_flag (CO_DE_VS)); any++; } if (opt.with_key_screening) print_pk_screening (pk, 1+any); } /* List a key in colon mode. If SECRET is true this is a secret key record (i.e. requested via --list-secret-key). If HAS_SECRET a secret key is available even if SECRET is not set. */ static void list_keyblock_colon (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret) { int rc; KBNODE kbctx; KBNODE node; PKT_public_key *pk; u32 keyid[2]; int trustletter = 0; int trustletter_print; int ownertrust_print; int ulti_hack = 0; int i; char *hexgrip_buffer = NULL; const char *hexgrip = NULL; char *serialno = NULL; int stubkey; unsigned int keylength; char *curve = NULL; const char *curvename = NULL; /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) { log_error ("Oops; key lost!\n"); dump_kbnode (keyblock); return; } pk = node->pkt->pkt.public_key; if (secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); /* In the error case we print an empty string so that we have a * "grp" record for each and subkey - even if it is empty. This * may help to prevent sync problems. */ hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret || has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk, keyid); if (!pk->flags.valid) trustletter_print = 'i'; else if (pk->flags.revoked) trustletter_print = 'r'; else if (pk->has_expired) trustletter_print = 'e'; else if (opt.fast_list_mode || opt.no_expensive_trust_checks) trustletter_print = 0; else { trustletter = get_validity_info (ctrl, keyblock, pk, NULL); if (trustletter == 'u') ulti_hack = 1; trustletter_print = trustletter; } if (!opt.fast_list_mode && !opt.no_expensive_trust_checks) ownertrust_print = get_ownertrust_info (ctrl, pk, 0); else ownertrust_print = 0; keylength = nbits_from_pk (pk); es_fputs (secret? "sec:":"pub:", es_stdout); if (trustletter_print) es_putc (trustletter_print, es_stdout); es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s::", keylength, pk->pubkey_algo, (ulong) keyid[0], (ulong) keyid[1], colon_datestr_from_pk (pk), colon_strtime (pk->expiredate)); if (ownertrust_print) es_putc (ownertrust_print, es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); es_putc (':', es_stdout); print_capabilities (ctrl, pk, keyblock); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA || pk->pubkey_algo == PUBKEY_ALGO_EDDSA || pk->pubkey_algo == PUBKEY_ALGO_ECDH) { curve = openpgp_oid_to_str (pk->pkey[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; es_fputs (curvename, es_stdout); } es_putc (':', es_stdout); /* End of field 17. */ print_compliance_flags (pk, keylength, curvename); es_putc (':', es_stdout); /* End of field 18 (compliance). */ if (pk->keyupdate) es_fputs (colon_strtime (pk->keyupdate), es_stdout); es_putc (':', es_stdout); /* End of field 19 (last_update). */ es_fprintf (es_stdout, "%d%s", pk->keyorg, pk->updateurl? " ":""); if (pk->updateurl) es_write_sanitized (es_stdout, pk->updateurl, strlen (pk->updateurl), ":", NULL); es_putc (':', es_stdout); /* End of field 20 (origin). */ es_putc ('\n', es_stdout); print_revokers (es_stdout, pk); print_fingerprint (ctrl, NULL, pk, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; int uid_validity; if (attrib_fp && uid->attrib_data != NULL) dump_attribs (uid, pk); if (uid->flags.revoked) uid_validity = 'r'; else if (uid->flags.expired) uid_validity = 'e'; else if (opt.no_expensive_trust_checks) uid_validity = 0; else if (ulti_hack) uid_validity = 'u'; else uid_validity = get_validity_info (ctrl, keyblock, pk, uid); es_fputs (uid->attrib_data? "uat:":"uid:", es_stdout); if (uid_validity) es_putc (uid_validity, es_stdout); es_fputs ("::::", es_stdout); es_fprintf (es_stdout, "%s:", colon_strtime (uid->created)); es_fprintf (es_stdout, "%s:", colon_strtime (uid->expiredate)); namehash_from_uid (uid); for (i = 0; i < 20; i++) es_fprintf (es_stdout, "%02X", uid->namehash[i]); es_fprintf (es_stdout, "::"); if (uid->attrib_data) es_fprintf (es_stdout, "%u %lu", uid->numattribs, uid->attrib_len); else es_write_sanitized (es_stdout, uid->name, uid->len, ":", NULL); es_fputs (":::::::::", es_stdout); if (uid->keyupdate) es_fputs (colon_strtime (uid->keyupdate), es_stdout); es_putc (':', es_stdout); /* End of field 19 (last_update). */ es_fprintf (es_stdout, "%d%s", uid->keyorg, uid->updateurl? " ":""); if (uid->updateurl) es_write_sanitized (es_stdout, uid->updateurl, strlen (uid->updateurl), ":", NULL); es_putc (':', es_stdout); /* End of field 20 (origin). */ es_putc ('\n', es_stdout); #ifdef USE_TOFU if (!uid->attrib_data && opt.with_tofu_info && (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP)) { /* Print a "tfs" record. */ tofu_write_tfs_record (ctrl, es_stdout, pk, uid->name); } #endif /*USE_TOFU*/ } else if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 keyid2[2]; PKT_public_key *pk2; int need_hexgrip = !!hexgrip; pk2 = node->pkt->pkt.public_key; xfree (hexgrip_buffer); hexgrip_buffer = NULL; hexgrip = NULL; xfree (serialno); serialno = NULL; if (need_hexgrip || secret || has_secret || opt.with_keygrip || opt.with_key_data) { rc = hexkeygrip_from_pk (pk2, &hexgrip_buffer); if (rc) log_error ("error computing a keygrip: %s\n", gpg_strerror (rc)); hexgrip = hexgrip_buffer? hexgrip_buffer : ""; } stubkey = 0; if ((secret||has_secret) && agent_get_keyinfo (NULL, hexgrip, &serialno, NULL)) stubkey = 1; /* Key not found. */ keyid_from_pk (pk2, keyid2); es_fputs (secret? "ssb:":"sub:", es_stdout); if (!pk2->flags.valid) es_putc ('i', es_stdout); else if (pk2->flags.revoked) es_putc ('r', es_stdout); else if (pk2->has_expired) es_putc ('e', es_stdout); else if (opt.fast_list_mode || opt.no_expensive_trust_checks) ; else { /* TRUSTLETTER should always be defined here. */ if (trustletter) es_fprintf (es_stdout, "%c", trustletter); } keylength = nbits_from_pk (pk2); es_fprintf (es_stdout, ":%u:%d:%08lX%08lX:%s:%s:::::", keylength, pk2->pubkey_algo, (ulong) keyid2[0], (ulong) keyid2[1], colon_datestr_from_pk (pk2), colon_strtime (pk2->expiredate)); print_capabilities (ctrl, pk2, NULL); es_putc (':', es_stdout); /* End of field 13. */ es_putc (':', es_stdout); /* End of field 14. */ if (secret || has_secret) { if (stubkey) es_putc ('#', es_stdout); else if (serialno) es_fputs (serialno, es_stdout); else if (has_secret) es_putc ('+', es_stdout); } es_putc (':', es_stdout); /* End of field 15. */ es_putc (':', es_stdout); /* End of field 16. */ if (pk2->pubkey_algo == PUBKEY_ALGO_ECDSA || pk2->pubkey_algo == PUBKEY_ALGO_EDDSA || pk2->pubkey_algo == PUBKEY_ALGO_ECDH) { xfree (curve); curve = openpgp_oid_to_str (pk2->pkey[0]); curvename = openpgp_oid_to_curve (curve, 0); if (!curvename) curvename = curve; es_fputs (curvename, es_stdout); } es_putc (':', es_stdout); /* End of field 17. */ print_compliance_flags (pk2, keylength, curvename); es_putc (':', es_stdout); /* End of field 18. */ es_putc ('\n', es_stdout); print_fingerprint (ctrl, NULL, pk2, 0); if (hexgrip) es_fprintf (es_stdout, "grp:::::::::%s:\n", hexgrip); if (opt.with_key_data) print_key_data (pk2); } else if (opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig = node->pkt->pkt.signature; int sigrc, fprokay = 0; char *sigstr; size_t fplen; byte fparray[MAX_FINGERPRINT_LEN]; char *siguid; size_t siguidlen; char *issuer_fpr = NULL; char *reason_text = NULL; char *reason_comment = NULL; size_t reason_commentlen; int reason_code; if (sig->sig_class == 0x20 || sig->sig_class == 0x28 || sig->sig_class == 0x30) { sigstr = "rev"; reason_code = get_revocation_reason (sig, &reason_text, &reason_comment, &reason_commentlen); } else if ((sig->sig_class & ~3) == 0x10) sigstr = "sig"; else if (sig->sig_class == 0x18) sigstr = "sig"; else if (sig->sig_class == 0x1F) sigstr = "sig"; else { es_fprintf (es_stdout, "sig::::::::::%02x%c:\n", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); continue; } if (opt.check_sigs) { PKT_public_key *signer_pk = NULL; es_fflush (es_stdout); if (opt.no_sig_cache) signer_pk = xmalloc_clear (sizeof (PKT_public_key)); rc = check_key_signature2 (ctrl, keyblock, node, NULL, signer_pk, NULL, NULL, NULL); switch (gpg_err_code (rc)) { case 0: sigrc = '!'; break; case GPG_ERR_BAD_SIGNATURE: sigrc = '-'; break; case GPG_ERR_NO_PUBKEY: case GPG_ERR_UNUSABLE_PUBKEY: sigrc = '?'; break; default: sigrc = '%'; break; } if (opt.no_sig_cache) { if (!rc) { fingerprint_from_pk (signer_pk, fparray, &fplen); fprokay = 1; } free_public_key (signer_pk); } } else { rc = 0; sigrc = ' '; /* Note the fix-up below in --list-sigs mode. */ } if (sigrc != '%' && sigrc != '?' && !opt.fast_list_mode) { int nouid; siguid = get_user_id (ctrl, sig->keyid, &siguidlen, &nouid); if (!opt.check_sigs && nouid) sigrc = '?'; /* No key in local keyring. */ } else { siguid = NULL; siguidlen = 0; } es_fputs (sigstr, es_stdout); es_putc (':', es_stdout); if (sigrc != ' ') es_putc (sigrc, es_stdout); es_fprintf (es_stdout, "::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo, (ulong) sig->keyid[0], (ulong) sig->keyid[1], colon_datestr_from_sig (sig), colon_expirestr_from_sig (sig)); if (sig->trust_depth || sig->trust_value) es_fprintf (es_stdout, "%d %d", sig->trust_depth, sig->trust_value); es_fprintf (es_stdout, ":"); if (sig->trust_regexp) es_write_sanitized (es_stdout, sig->trust_regexp, strlen (sig->trust_regexp), ":", NULL); es_fprintf (es_stdout, ":"); if (sigrc == '%') es_fprintf (es_stdout, "[%s] ", gpg_strerror (rc)); else if (siguid) es_write_sanitized (es_stdout, siguid, siguidlen, ":", NULL); es_fprintf (es_stdout, ":%02x%c", sig->sig_class, sig->flags.exportable ? 'x' : 'l'); if (reason_text) es_fprintf (es_stdout, ",%02x", reason_code); es_fputs ("::", es_stdout); if (opt.no_sig_cache && opt.check_sigs && fprokay) { for (i = 0; i < fplen; i++) es_fprintf (es_stdout, "%02X", fparray[i]); } else if ((issuer_fpr = issuer_fpr_string (sig))) es_fputs (issuer_fpr, es_stdout); es_fprintf (es_stdout, ":::%d:", sig->digest_algo); if (reason_comment) { es_fputs ("::::", es_stdout); es_write_sanitized (es_stdout, reason_comment, reason_commentlen, ":", NULL); es_putc (':', es_stdout); } es_putc ('\n', es_stdout); if (opt.show_subpackets) print_subpackets_colon (sig); /* fixme: check or list other sigs here */ xfree (reason_text); xfree (reason_comment); xfree (siguid); xfree (issuer_fpr); } } xfree (curve); xfree (hexgrip_buffer); xfree (serialno); } /* * Reorder the keyblock so that the primary user ID (and not attribute * packet) comes first. Fixme: Replace this by a generic sort * function. */ static void do_reorder_keyblock (KBNODE keyblock, int attr) { KBNODE primary = NULL, primary0 = NULL, primary2 = NULL; KBNODE last, node; for (node = keyblock; node; primary0 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && ((attr && node->pkt->pkt.user_id->attrib_data) || (!attr && !node->pkt->pkt.user_id->attrib_data)) && node->pkt->pkt.user_id->flags.primary) { primary = primary2 = node; for (node = node->next; node; primary2 = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID || node->pkt->pkttype == PKT_PUBLIC_SUBKEY || node->pkt->pkttype == PKT_SECRET_SUBKEY) { break; } } break; } } if (!primary) return; /* No primary key flag found (should not happen). */ for (last = NULL, node = keyblock; node; last = node, node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) break; } log_assert (node); log_assert (last); /* The user ID is never the first packet. */ log_assert (primary0); /* Ditto (this is the node before primary). */ if (node == primary) return; /* Already the first one. */ last->next = primary; primary0->next = primary2->next; primary2->next = node; } void reorder_keyblock (KBNODE keyblock) { do_reorder_keyblock (keyblock, 1); do_reorder_keyblock (keyblock, 0); } static void list_keyblock (ctrl_t ctrl, KBNODE keyblock, int secret, int has_secret, int fpr, struct keylist_context *listctx) { reorder_keyblock (keyblock); if (opt.with_colons) list_keyblock_colon (ctrl, keyblock, secret, has_secret); else if ((opt.list_options & LIST_SHOW_ONLY_FPR_MBOX)) { if (!listctx->no_validity) check_trustdb_stale (ctrl); list_keyblock_simple (ctrl, keyblock); } else list_keyblock_print (ctrl, keyblock, secret, fpr, listctx); if (secret) es_fflush (es_stdout); } /* Public function used by keygen to list a keyblock. If NO_VALIDITY * is set the validity of a key is never shown. */ void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity) { struct keylist_context listctx; memset (&listctx, 0, sizeof (listctx)); listctx.no_validity = !!no_validity; list_keyblock (ctrl, keyblock, secret, has_secret, fpr, &listctx); keylist_context_release (&listctx); } /* Print an hex digit in ICAO spelling. */ static void print_icao_hexdigit (estream_t fp, int c) { static const char *list[16] = { "Zero", "One", "Two", "Three", "Four", "Five", "Six", "Seven", "Eight", "Niner", "Alfa", "Bravo", "Charlie", "Delta", "Echo", "Foxtrot" }; tty_fprintf (fp, "%s", list[c&15]); } /* * Function to print the finperprint. * mode 0: as used in key listings, opt.with_colons is honored * 1: print using log_info () * 2: direct use of tty * 3: direct use of tty but only primary key. * 4: direct use of tty but only subkey. * 10: Same as 0 but with_colons etc is ignored. * 20: Same as 0 but using a compact format. * * Modes 1 and 2 will try and print both subkey and primary key * fingerprints. A MODE with bit 7 set is used internally. If * OVERRIDE_FP is not NULL that stream will be used in 0 instead * of es_stdout or instead of the TTY in modes 2 and 3. */ void print_fingerprint (ctrl_t ctrl, estream_t override_fp, PKT_public_key *pk, int mode) { char hexfpr[2*MAX_FINGERPRINT_LEN+1]; char *p; size_t i; estream_t fp; const char *text; int primary = 0; int with_colons = opt.with_colons; int with_icao = opt.with_icao_spelling; int compact = 0; if (mode == 10) { mode = 0; with_colons = 0; with_icao = 0; } else if (mode == 20) { mode = 0; with_colons = 0; compact = 1; } if (!opt.fingerprint && !opt.with_fingerprint && opt.with_subkey_fingerprint) compact = 1; if (pk->main_keyid[0] == pk->keyid[0] && pk->main_keyid[1] == pk->keyid[1]) primary = 1; /* Just to be safe */ if ((mode & 0x80) && !primary) { log_error ("primary key is not really primary!\n"); return; } mode &= ~0x80; if (!primary && (mode == 1 || mode == 2)) { PKT_public_key *primary_pk = xmalloc_clear (sizeof (*primary_pk)); get_pubkey (ctrl, primary_pk, pk->main_keyid); print_fingerprint (ctrl, override_fp, primary_pk, (mode | 0x80)); free_public_key (primary_pk); } if (mode == 1) { fp = log_get_stream (); if (primary) text = _("Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 2) { fp = override_fp; /* Use tty or given stream. */ if (primary) /* TRANSLATORS: this should fit into 24 bytes so that the * fingerprint data is properly aligned with the user ID */ text = _(" Primary key fingerprint:"); else text = _(" Subkey fingerprint:"); } else if (mode == 3) { fp = override_fp; /* Use tty or given stream. */ text = _(" Key fingerprint ="); } else if (mode == 4) { fp = override_fp; /* Use tty or given stream. */ text = _(" Subkey fingerprint:"); } else { fp = override_fp? override_fp : es_stdout; if (opt.keyid_format == KF_NONE) { text = " "; /* To indent ICAO spelling. */ compact = 1; } else text = _(" Key fingerprint ="); } hexfingerprint (pk, hexfpr, sizeof hexfpr); if (with_colons && !mode) { es_fprintf (fp, "fpr:::::::::%s:", hexfpr); } else if (compact && !opt.fingerprint && !opt.with_fingerprint) { tty_fprintf (fp, "%*s%s", 6, "", hexfpr); } else { char fmtfpr[MAX_FORMATTED_FINGERPRINT_LEN + 1]; format_hexfingerprint (hexfpr, fmtfpr, sizeof fmtfpr); if (compact) tty_fprintf (fp, "%*s%s", 6, "", fmtfpr); else tty_fprintf (fp, "%s %s", text, fmtfpr); } tty_fprintf (fp, "\n"); if (!with_colons && with_icao) { ; tty_fprintf (fp, "%*s\"", (int)strlen(text)+1, ""); for (i = 0, p = hexfpr; *p; i++, p++) { if (!i) ; else if (!(i%8)) tty_fprintf (fp, "\n%*s ", (int)strlen(text)+1, ""); else if (!(i%4)) tty_fprintf (fp, " "); else tty_fprintf (fp, " "); print_icao_hexdigit (fp, xtoi_1 (p)); } tty_fprintf (fp, "\"\n"); } } /* Print the serial number of an OpenPGP card if available. */ static void print_card_serialno (const char *serialno) { if (!serialno) return; if (opt.with_colons) return; /* Handled elsewhere. */ es_fputs (_(" Card serial no. ="), es_stdout); es_putc (' ', es_stdout); if (strlen (serialno) == 32 && !strncmp (serialno, "D27600012401", 12)) { /* This is an OpenPGP card. Print the relevant part. */ /* Example: D2760001240101010001000003470000 */ /* xxxxyyyyyyyy */ es_fprintf (es_stdout, "%.*s %.*s", 4, serialno+16, 8, serialno+20); } else es_fputs (serialno, es_stdout); es_putc ('\n', es_stdout); } /* Print a public or secret (sub)key line. Example: * * pub dsa2048 2007-12-31 [SC] [expires: 2018-12-31] * 80615870F5BAD690333686D0F2AD85AC1E42B367 * * pub rsa2048 2017-12-31 [SC] [expires: 2028-12-31] * 80615870F5BAD690333686D0F2AD85AC1E42B3671122334455 * * Some global options may result in a different output format. If * SECRET is set, "sec" or "ssb" is used instead of "pub" or "sub" and * depending on the value a flag character is shown: * * 1 := ' ' Regular secret key * 2 := '#' Stub secret key * 3 := '>' Secret key is on a token. */ void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret) { char pkstrbuf[PUBKEY_STRING_SIZE]; tty_fprintf (fp, "%s%c %s", pk->flags.primary? (secret? "sec":"pub") /**/ : (secret? "ssb":"sub"), secret == 2? '#' : secret == 3? '>' : ' ', pubkey_string (pk, pkstrbuf, sizeof pkstrbuf)); if (opt.keyid_format != KF_NONE) tty_fprintf (fp, "/%s", keystr_from_pk (pk)); tty_fprintf (fp, " %s", datestr_from_pk (pk)); if (pk->flags.primary && !(openpgp_pk_algo_usage (pk->pubkey_algo) & (PUBKEY_USAGE_CERT| PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH))) { /* A primary key which is really not capable to sign. */ tty_fprintf (fp, " [INVALID_ALGO]"); } else if ((opt.list_options & LIST_SHOW_USAGE)) { tty_fprintf (fp, " [%s]", usagestr_from_pk (pk, 0)); } if (pk->flags.revoked) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("revoked: %s"), revokestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->has_expired) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expired: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } else if (pk->expiredate) { tty_fprintf (fp, " ["); tty_fprintf (fp, _("expires: %s"), expirestr_from_pk (pk)); tty_fprintf (fp, "]"); } #if 0 /* I need to think about this some more. It's easy enough to include, but it looks sort of confusing in the listing... */ if (opt.list_options & LIST_SHOW_VALIDITY) { int validity = get_validity (ctrl, pk, NULL, NULL, 0); tty_fprintf (fp, " [%s]", trust_value_to_string (validity)); } #endif if (pk->pubkey_algo >= 100) tty_fprintf (fp, " [experimental algorithm %d]", pk->pubkey_algo); tty_fprintf (fp, "\n"); /* if the user hasn't explicitly asked for human-readable fingerprints, show compact fpr of primary key: */ if (pk->flags.primary && !opt.fingerprint && !opt.with_fingerprint) print_fingerprint (ctrl, fp, pk, 20); } void set_attrib_fd (int fd) { static int last_fd = -1; if (fd != -1 && last_fd == fd) return; /* Fixme: Do we need to check for the log stream here? */ if (attrib_fp && attrib_fp != log_get_stream ()) es_fclose (attrib_fp); attrib_fp = NULL; if (fd == -1) return; if (! gnupg_fd_valid (fd)) log_fatal ("attribute-fd is invalid: %s\n", strerror (errno)); #ifdef HAVE_DOSISH_SYSTEM setmode (fd, O_BINARY); #endif if (fd == 1) attrib_fp = es_stdout; else if (fd == 2) attrib_fp = es_stderr; else attrib_fp = es_fdopen (fd, "wb"); if (!attrib_fp) { log_fatal ("can't open fd %d for attribute output: %s\n", fd, strerror (errno)); } last_fd = fd; } diff --git a/g10/keyserver.c b/g10/keyserver.c index b07afb128..030bc9297 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1,2164 +1,2164 @@ /* keyserver.c - generic keyserver code * Copyright (C) 2001, 2002, 2003, 2004, 2005, 2006, 2007, 2008, * 2009, 2011, 2012 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "gpg.h" #include "../common/iobuf.h" #include "filter.h" #include "keydb.h" #include "../common/status.h" #include "exec.h" #include "main.h" #include "../common/i18n.h" #include "../common/ttyio.h" #include "options.h" #include "packet.h" #include "trustdb.h" #include "keyserver-internal.h" #include "../common/util.h" #include "../common/membuf.h" #include "../common/mbox-util.h" #include "call-dirmngr.h" #ifdef HAVE_W32_SYSTEM /* It seems Vista doesn't grok X_OK and so fails access() tests. Previous versions interpreted X_OK as F_OK anyway, so we'll just use F_OK directly. */ #undef X_OK #define X_OK F_OK #endif /* HAVE_W32_SYSTEM */ struct keyrec { KEYDB_SEARCH_DESC desc; u32 createtime,expiretime; int size,flags; byte type; IOBUF uidbuf; unsigned int lines; }; /* Parameters for the search line handler. */ struct search_line_handler_parm_s { ctrl_t ctrl; /* The session control structure. */ char *searchstr_disp; /* Native encoded search string or NULL. */ KEYDB_SEARCH_DESC *desc; /* Array with search descriptions. */ int count; /* Number of keys we are currently prepared to handle. This is the size of the DESC array. If it is too small, it will grow safely. */ int validcount; /* Enable the "Key x-y of z" messages. */ int nkeys; /* Number of processed records. */ int any_lines; /* At least one line has been processed. */ unsigned int numlines; /* Counter for displayed lines. */ int eof_seen; /* EOF encountered. */ int not_found; /* Set if no keys have been found. */ }; enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH}; static struct parse_options keyserver_opts[]= { /* some of these options are not real - just for the help message */ {"max-cert-size",0,NULL,NULL}, /* MUST be the first in this array! */ {"http-proxy", KEYSERVER_HTTP_PROXY, NULL, /* MUST be the second! */ N_("override proxy options set for dirmngr")}, {"include-revoked",0,NULL,N_("include revoked keys in search results")}, {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")}, {"timeout", KEYSERVER_TIMEOUT, NULL, N_("override timeout options set for dirmngr")}, {"refresh-add-fake-v3-keyids",KEYSERVER_ADD_FAKE_V3,NULL, NULL}, {"auto-key-retrieve",KEYSERVER_AUTO_KEY_RETRIEVE,NULL, N_("automatically retrieve keys when verifying signatures")}, {"honor-keyserver-url",KEYSERVER_HONOR_KEYSERVER_URL,NULL, N_("honor the preferred keyserver URL set on the key")}, {"honor-pka-record",KEYSERVER_HONOR_PKA_RECORD,NULL, N_("honor the PKA record set on a key when retrieving keys")}, {NULL,0,NULL,NULL} }; static gpg_error_t keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, struct keyserver_spec *override_keyserver, int quick, unsigned char **r_fpr, size_t *r_fprlen); static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs); /* Reasonable guess. The commonly used test key simon.josefsson.org is larger than 32k, thus we need at least this value. */ #define DEFAULT_MAX_CERT_SIZE 65536 static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE; static void warn_kshelper_option(char *option, int noisy) { char *p; if ((p=strchr (option, '='))) *p = 0; if (!strcmp (option, "ca-cert-file")) log_info ("keyserver option '%s' is obsolete; please use " "'%s' in dirmngr.conf\n", "ca-cert-file", "hkp-cacert"); else if (!strcmp (option, "check-cert") || !strcmp (option, "broken-http-proxy")) log_info ("keyserver option '%s' is obsolete\n", option); else if (noisy || opt.verbose) log_info ("keyserver option '%s' is unknown\n", option); } /* Called from main to parse the args for --keyserver-options. */ int parse_keyserver_options(char *options) { int ret=1; char *tok; char *max_cert=NULL; keyserver_opts[0].value=&max_cert; keyserver_opts[1].value=&opt.keyserver_options.http_proxy; while((tok=optsep(&options))) { if(tok[0]=='\0') continue; /* We accept quite a few possible options here - some options to handle specially, the keyserver_options list, and import and export options that pertain to keyserver operations. */ if (!parse_options (tok,&opt.keyserver_options.options, keyserver_opts,0) && !parse_import_options(tok,&opt.keyserver_options.import_options,0) && !parse_export_options(tok,&opt.keyserver_options.export_options,0)) { /* All of the standard options have failed, so the option was destined for a keyserver plugin as used by GnuPG < 2.1 */ warn_kshelper_option (tok, 1); } } if(max_cert) { max_cert_size=strtoul(max_cert,(char **)NULL,10); if(max_cert_size==0) max_cert_size=DEFAULT_MAX_CERT_SIZE; } return ret; } void free_keyserver_spec(struct keyserver_spec *keyserver) { xfree(keyserver->uri); xfree(keyserver->scheme); xfree(keyserver->auth); xfree(keyserver->host); xfree(keyserver->port); xfree(keyserver->path); xfree(keyserver->opaque); free_strlist(keyserver->options); xfree(keyserver); } /* Return 0 for match */ static int cmp_keyserver_spec(struct keyserver_spec *one,struct keyserver_spec *two) { if(ascii_strcasecmp(one->scheme,two->scheme)==0) { if(one->host && two->host && ascii_strcasecmp(one->host,two->host)==0) { if((one->port && two->port && ascii_strcasecmp(one->port,two->port)==0) || (!one->port && !two->port)) return 0; } else if(one->opaque && two->opaque && ascii_strcasecmp(one->opaque,two->opaque)==0) return 0; } return 1; } /* Try and match one of our keyservers. If we can, return that. If we can't, return our input. */ struct keyserver_spec * keyserver_match(struct keyserver_spec *spec) { struct keyserver_spec *ks; for(ks=opt.keyserver;ks;ks=ks->next) if(cmp_keyserver_spec(spec,ks)==0) return ks; return spec; } /* TODO: once we cut over to an all-curl world, we don't need this parser any longer so it can be removed, or at least moved to keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */ keyserver_spec_t parse_keyserver_uri (const char *string,int require_scheme) { int assume_hkp=0; struct keyserver_spec *keyserver; const char *idx; int count; char *uri, *duped_uri, *options; log_assert (string); keyserver=xmalloc_clear(sizeof(struct keyserver_spec)); duped_uri = uri = xstrdup (string); options=strchr(uri,' '); if(options) { char *tok; *options='\0'; options++; while((tok=optsep(&options))) warn_kshelper_option (tok, 0); } /* Get the scheme */ for(idx=uri,count=0;*idx && *idx!=':';idx++) { count++; /* Do we see the start of an RFC-2732 ipv6 address here? If so, there clearly isn't a scheme so get out early. */ if(*idx=='[') { /* Was the '[' the first thing in the string? If not, we have a mangled scheme with a [ in it so fail. */ if(count==1) break; else goto fail; } } if(count==0) goto fail; if(*idx=='\0' || *idx=='[') { if(require_scheme) return NULL; /* Assume HKP if there is no scheme */ assume_hkp=1; keyserver->scheme=xstrdup("hkp"); keyserver->uri=xmalloc(strlen(keyserver->scheme)+3+strlen(uri)+1); strcpy(keyserver->uri,keyserver->scheme); strcat(keyserver->uri,"://"); strcat(keyserver->uri,uri); } else { int i; keyserver->uri=xstrdup(uri); keyserver->scheme=xmalloc(count+1); /* Force to lowercase */ for(i=0;ischeme[i]=ascii_tolower(uri[i]); keyserver->scheme[i]='\0'; /* Skip past the scheme and colon */ uri+=count+1; } if(ascii_strcasecmp(keyserver->scheme,"x-broken-hkp")==0) { log_info ("keyserver option '%s' is obsolete\n", "x-broken-hkp"); } else if(ascii_strcasecmp(keyserver->scheme,"x-hkp")==0) { /* Canonicalize this to "hkp" so it works with both the internal and external keyserver interface. */ xfree(keyserver->scheme); keyserver->scheme=xstrdup("hkp"); } if (uri[0]=='/' && uri[1]=='/' && uri[2] == '/') { /* Three slashes means network path with a default host name. This is a hack because it does not crok all possible combinations. We should better replace all code by the parser from http.c. */ keyserver->path = xstrdup (uri+2); } else if(assume_hkp || (uri[0]=='/' && uri[1]=='/')) { /* Two slashes means network path. */ /* Skip over the "//", if any */ if(!assume_hkp) uri+=2; /* Do we have userinfo auth data present? */ for(idx=uri,count=0;*idx && *idx!='@' && *idx!='/';idx++) count++; /* We found a @ before the slash, so that means everything before the @ is auth data. */ if(*idx=='@') { if(count==0) goto fail; keyserver->auth=xmalloc(count+1); strncpy(keyserver->auth,uri,count); keyserver->auth[count]='\0'; uri+=count+1; } /* Is it an RFC-2732 ipv6 [literal address] ? */ if(*uri=='[') { for(idx=uri+1,count=1;*idx && ((isascii (*idx) && isxdigit(*idx)) || *idx==':' || *idx=='.');idx++) count++; /* Is the ipv6 literal address terminated? */ if(*idx==']') count++; else goto fail; } else for(idx=uri,count=0;*idx && *idx!=':' && *idx!='/';idx++) count++; if(count==0) goto fail; keyserver->host=xmalloc(count+1); strncpy(keyserver->host,uri,count); keyserver->host[count]='\0'; /* Skip past the host */ uri+=count; if(*uri==':') { /* It would seem to be reasonable to limit the range of the ports to values between 1-65535, but RFC 1738 and 1808 imply there is no limit. Of course, the real world has limits. */ for(idx=uri+1,count=0;*idx && *idx!='/';idx++) { count++; /* Ports are digits only */ if(!digitp(idx)) goto fail; } keyserver->port=xmalloc(count+1); strncpy(keyserver->port,uri+1,count); keyserver->port[count]='\0'; /* Skip past the colon and port number */ uri+=1+count; } /* Everything else is the path */ if(*uri) keyserver->path=xstrdup(uri); else keyserver->path=xstrdup("/"); if(keyserver->path[1]) keyserver->flags.direct_uri=1; } else if(uri[0]!='/') { /* No slash means opaque. Just record the opaque blob and get out. */ keyserver->opaque=xstrdup(uri); } else { /* One slash means absolute path. We don't need to support that yet. */ goto fail; } xfree (duped_uri); return keyserver; fail: free_keyserver_spec(keyserver); xfree (duped_uri); return NULL; } struct keyserver_spec * parse_preferred_keyserver(PKT_signature *sig) { struct keyserver_spec *spec=NULL; const byte *p; size_t plen; p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&plen); if(p && plen) { byte *dupe=xmalloc(plen+1); memcpy(dupe,p,plen); dupe[plen]='\0'; spec = parse_keyserver_uri (dupe, 1); xfree(dupe); } return spec; } static void print_keyrec (ctrl_t ctrl, int number,struct keyrec *keyrec) { iobuf_writebyte(keyrec->uidbuf,0); iobuf_flush_temp(keyrec->uidbuf); es_printf ("(%d)\t%s ", number, iobuf_get_temp_buffer (keyrec->uidbuf)); if (keyrec->size>0) es_printf ("%d bit ", keyrec->size); if(keyrec->type) { const char *str; str = openpgp_pk_algo_name (keyrec->type); if (str && strcmp (str, "?")) es_printf ("%s ",str); else es_printf ("unknown "); } switch(keyrec->desc.mode) { /* If the keyserver helper gave us a short keyid, we have no choice but to use it. Do check --keyid-format to add a 0x if needed. */ case KEYDB_SEARCH_MODE_SHORT_KID: es_printf ("key %s%08lX", (opt.keyid_format==KF_0xSHORT || opt.keyid_format==KF_0xLONG)?"0x":"", (ulong)keyrec->desc.u.kid[1]); break; /* However, if it gave us a long keyid, we can honor --keyid-format via keystr(). */ case KEYDB_SEARCH_MODE_LONG_KID: es_printf ("key %s",keystr(keyrec->desc.u.kid)); break; case KEYDB_SEARCH_MODE_FPR: { u32 kid[2]; keyid_from_fingerprint (ctrl, keyrec->desc.u.fpr, keyrec->desc.fprlen, kid); es_printf("key %s",keystr(kid)); } break; default: BUG(); break; } if(keyrec->createtime>0) { es_printf (", "); es_printf (_("created: %s"), strtimestamp(keyrec->createtime)); } if(keyrec->expiretime>0) { es_printf (", "); es_printf (_("expires: %s"), strtimestamp(keyrec->expiretime)); } if (keyrec->flags&1) es_printf (" (%s)", _("revoked")); if(keyrec->flags&2) es_printf (" (%s)", _("disabled")); if(keyrec->flags&4) es_printf (" (%s)", _("expired")); es_printf ("\n"); } /* Returns a keyrec (which must be freed) once a key is complete, and NULL otherwise. Call with a NULL keystring once key parsing is complete to return any unfinished keys. */ static struct keyrec * parse_keyrec(char *keystring) { /* FIXME: Remove the static and put the data into the parms we use for the caller anyway. */ static struct keyrec *work=NULL; struct keyrec *ret=NULL; char *record; int i; if(keystring==NULL) { if(work==NULL) return NULL; else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE) { xfree(work); return NULL; } else { ret=work; work=NULL; return ret; } } if(work==NULL) { work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } trim_trailing_ws (keystring, strlen (keystring)); if((record=strsep(&keystring,":"))==NULL) return ret; if(ascii_strcasecmp("pub",record)==0) { char *tok; gpg_error_t err; if(work->desc.mode) { ret=work; work=xmalloc_clear(sizeof(struct keyrec)); work->uidbuf=iobuf_temp(); } if((tok=strsep(&keystring,":"))==NULL) return ret; err = classify_user_id (tok, &work->desc, 1); if (err || (work->desc.mode != KEYDB_SEARCH_MODE_SHORT_KID && work->desc.mode != KEYDB_SEARCH_MODE_LONG_KID && work->desc.mode != KEYDB_SEARCH_MODE_FPR)) { work->desc.mode=KEYDB_SEARCH_MODE_NONE; return ret; } /* Note all items after this are optional. This allows us to have a pub line as simple as pub:keyid and nothing else. */ work->lines++; if((tok=strsep(&keystring,":"))==NULL) return ret; work->type=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; work->size=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; if(atoi(tok)<=0) work->createtime=0; else work->createtime=atoi(tok); if((tok=strsep(&keystring,":"))==NULL) return ret; if(atoi(tok)<=0) work->expiretime=0; else { work->expiretime=atoi(tok); /* Force the 'e' flag on if this key is expired. */ if(work->expiretime<=make_timestamp()) work->flags|=4; } if((tok=strsep(&keystring,":"))==NULL) return ret; while(*tok) switch(*tok++) { case 'r': case 'R': work->flags|=1; break; case 'd': case 'D': work->flags|=2; break; case 'e': case 'E': work->flags|=4; break; } } else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode) { char *userid,*tok,*decoded; if((tok=strsep(&keystring,":"))==NULL) return ret; if(strlen(tok)==0) return ret; userid=tok; /* By definition, de-%-encoding is always smaller than the original string so we can decode in place. */ i=0; while(*tok) if(tok[0]=='%' && tok[1] && tok[2]) { int c; userid[i] = (c=hextobyte(&tok[1])) == -1 ? '?' : c; i++; tok+=3; } else userid[i++]=*tok++; /* We don't care about the other info provided in the uid: line since no keyserver supports marking userids with timestamps or revoked/expired/disabled yet. */ /* No need to check for control characters, as utf8_to_native does this for us. */ decoded=utf8_to_native(userid,i,0); if(strlen(decoded)>opt.screen_columns-10) decoded[opt.screen_columns-10]='\0'; iobuf_writestr(work->uidbuf,decoded); xfree(decoded); iobuf_writestr(work->uidbuf,"\n\t"); work->lines++; } /* Ignore any records other than "pri" and "uid" for easy future growth. */ return ret; } /* Show a prompt and allow the user to select keys for retrieval. */ static gpg_error_t show_prompt (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int numdesc, int count, const char *search) { gpg_error_t err; char *answer = NULL; es_fflush (es_stdout); if (count && opt.command_fd == -1) { static int from = 1; tty_printf ("Keys %d-%d of %d for \"%s\". ", from, numdesc, count, search); from = numdesc + 1; } again: err = 0; xfree (answer); answer = cpr_get_no_help ("keysearch.prompt", _("Enter number(s), N)ext, or Q)uit > ")); /* control-d */ if (answer[0]=='\x04') { tty_printf ("Q\n"); answer[0] = 'q'; } if (answer[0]=='q' || answer[0]=='Q') err = gpg_error (GPG_ERR_CANCELED); else if (atoi (answer) >= 1 && atoi (answer) <= numdesc) { char *split = answer; char *num; int numarray[50]; int numidx = 0; int idx; while ((num = strsep (&split, " ,"))) if (atoi (num) >= 1 && atoi (num) <= numdesc) { if (numidx >= DIM (numarray)) { tty_printf ("Too many keys selected\n"); goto again; } numarray[numidx++] = atoi (num); } if (!numidx) goto again; { KEYDB_SEARCH_DESC *selarray; selarray = xtrymalloc (numidx * sizeof *selarray); if (!selarray) { err = gpg_error_from_syserror (); goto leave; } for (idx = 0; idx < numidx; idx++) selarray[idx] = desc[numarray[idx]-1]; err = keyserver_get (ctrl, selarray, numidx, NULL, 0, NULL, NULL); xfree (selarray); } } leave: xfree (answer); return err; } /* This is a callback used by call-dirmngr.c to process the result of KS_SEARCH command. If SPECIAL is 0, LINE is the actual data line received with all escaping removed and guaranteed to be exactly one line with stripped LF; an EOF is indicated by LINE passed as NULL. If special is 1, the line contains the source of the information (usually an URL). LINE may be modified after return. */ static gpg_error_t search_line_handler (void *opaque, int special, char *line) { struct search_line_handler_parm_s *parm = opaque; gpg_error_t err = 0; struct keyrec *keyrec; if (special == 1) { log_info ("data source: %s\n", line); return 0; } else if (special) { log_debug ("unknown value %d for special search callback", special); return 0; } if (parm->eof_seen && line) { log_debug ("ooops: unexpected data after EOF\n"); line = NULL; } /* Print the received line. */ if (opt.with_colons && line) { es_printf ("%s\n", line); } /* Look for an info: line. The only current info: values defined are the version and key count. */ if (line && !parm->any_lines && !ascii_strncasecmp ("info:", line, 5)) { char *str = line + 5; char *tok; if ((tok = strsep (&str, ":"))) { int version; if (sscanf (tok, "%d", &version) !=1 ) version = 1; if (version !=1 ) { log_error (_("invalid keyserver protocol " "(us %d!=handler %d)\n"), 1, version); return gpg_error (GPG_ERR_UNSUPPORTED_PROTOCOL); } } if ((tok = strsep (&str, ":")) && sscanf (tok, "%d", &parm->count) == 1) { if (!parm->count) parm->not_found = 1;/* Server indicated that no items follow. */ else if (parm->count < 0) parm->count = 10; /* Bad value - assume something reasonable. */ else parm->validcount = 1; /* COUNT seems to be okay. */ } parm->any_lines = 1; return 0; /* Line processing finished. */ } again: if (line) keyrec = parse_keyrec (line); else { /* Received EOF - flush data */ parm->eof_seen = 1; keyrec = parse_keyrec (NULL); if (!keyrec) { if (!parm->nkeys) parm->not_found = 1; /* No keys at all. */ else { if (parm->nkeys != parm->count) parm->validcount = 0; if (!(opt.with_colons && opt.batch)) { err = show_prompt (parm->ctrl, parm->desc, parm->nkeys, parm->validcount? parm->count : 0, parm->searchstr_disp); return err; } } } } /* Save the key in the key array. */ if (keyrec) { /* Allocate or enlarge the key array if needed. */ if (!parm->desc) { if (parm->count < 1) { parm->count = 10; parm->validcount = 0; } parm->desc = xtrymalloc (parm->count * sizeof *parm->desc); if (!parm->desc) { err = gpg_error_from_syserror (); iobuf_close (keyrec->uidbuf); xfree (keyrec); return err; } } else if (parm->nkeys == parm->count) { /* Keyserver sent more keys than claimed in the info: line. */ KEYDB_SEARCH_DESC *tmp; int newcount = parm->count + 10; tmp = xtryrealloc (parm->desc, newcount * sizeof *parm->desc); if (!tmp) { err = gpg_error_from_syserror (); iobuf_close (keyrec->uidbuf); xfree (keyrec); return err; } parm->count = newcount; parm->desc = tmp; parm->validcount = 0; } parm->desc[parm->nkeys] = keyrec->desc; if (!opt.with_colons) { /* SCREEN_LINES - 1 for the prompt. */ if (parm->numlines + keyrec->lines > opt.screen_lines - 1) { err = show_prompt (parm->ctrl, parm->desc, parm->nkeys, parm->validcount ? parm->count:0, parm->searchstr_disp); if (err) return err; parm->numlines = 0; } print_keyrec (parm->ctrl, parm->nkeys+1, keyrec); } parm->numlines += keyrec->lines; iobuf_close (keyrec->uidbuf); xfree (keyrec); parm->any_lines = 1; parm->nkeys++; /* If we are here due to a flush after the EOF, run again for the last prompt. Fixme: Make this code better readable. */ if (parm->eof_seen) goto again; } return 0; } int keyserver_export (ctrl_t ctrl, strlist_t users) { gpg_error_t err; strlist_t sl=NULL; KEYDB_SEARCH_DESC desc; int rc=0; /* Weed out descriptors that we don't support sending */ for(;users;users=users->next) { err = classify_user_id (users->d, &desc, 1); if (err || (desc.mode != KEYDB_SEARCH_MODE_SHORT_KID && desc.mode != KEYDB_SEARCH_MODE_LONG_KID && desc.mode != KEYDB_SEARCH_MODE_FPR)) { log_error(_("\"%s\" not a key ID: skipping\n"),users->d); continue; } else append_to_strlist(&sl,users->d); } if(sl) { rc = keyserver_put (ctrl, sl); free_strlist(sl); } return rc; } /* Structure to convey the arg to keyserver_retrieval_screener. */ struct ks_retrieval_screener_arg_s { KEYDB_SEARCH_DESC *desc; int ndesc; }; /* Check whether a key matches the search description. The function returns 0 if the key shall be imported. */ static gpg_error_t keyserver_retrieval_screener (kbnode_t keyblock, void *opaque) { struct ks_retrieval_screener_arg_s *arg = opaque; KEYDB_SEARCH_DESC *desc = arg->desc; int ndesc = arg->ndesc; kbnode_t node; PKT_public_key *pk; int n; u32 keyid[2]; byte fpr[MAX_FINGERPRINT_LEN]; size_t fpr_len = 0; /* Secret keys are not expected from a keyserver. We do not care about secret subkeys because the import code takes care of skipping them. Not allowing an import of a public key with a secret subkey would make it too easy to inhibit the downloading of a public key. Recall that keyservers do only limited checks. */ node = find_kbnode (keyblock, PKT_SECRET_KEY); if (node) return gpg_error (GPG_ERR_GENERAL); /* Do not import. */ if (!ndesc) return 0; /* Okay if no description given. */ /* Loop over all key packets. */ for (node = keyblock; node; node = node->next) { if (node->pkt->pkttype != PKT_PUBLIC_KEY && node->pkt->pkttype != PKT_PUBLIC_SUBKEY) continue; pk = node->pkt->pkt.public_key; fingerprint_from_pk (pk, fpr, &fpr_len); keyid_from_pk (pk, keyid); /* Compare requested and returned fingerprints if available. */ for (n = 0; n < ndesc; n++) { if (desc[n].mode == KEYDB_SEARCH_MODE_FPR) { if (fpr_len == desc[n].fprlen && !memcmp (fpr, desc[n].u.fpr, 32)) return 0; } else if (desc[n].mode == KEYDB_SEARCH_MODE_LONG_KID) { if (keyid[0] == desc[n].u.kid[0] && keyid[1] == desc[n].u.kid[1]) return 0; } else if (desc[n].mode == KEYDB_SEARCH_MODE_SHORT_KID) { if (keyid[1] == desc[n].u.kid[1]) return 0; } else /* No keyid or fingerprint - can't check. */ return 0; /* allow import. */ } } return gpg_error (GPG_ERR_GENERAL); } int keyserver_import (ctrl_t ctrl, strlist_t users) { gpg_error_t err; KEYDB_SEARCH_DESC *desc; int num=100,count=0; int rc=0; /* Build a list of key ids */ desc=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); for(;users;users=users->next) { err = classify_user_id (users->d, &desc[count], 1); if (err || (desc[count].mode != KEYDB_SEARCH_MODE_SHORT_KID && desc[count].mode != KEYDB_SEARCH_MODE_LONG_KID && desc[count].mode != KEYDB_SEARCH_MODE_FPR)) { log_error (_("\"%s\" not a key ID: skipping\n"), users->d); continue; } count++; if(count==num) { num+=100; desc=xrealloc(desc,sizeof(KEYDB_SEARCH_DESC)*num); } } if(count>0) rc = keyserver_get (ctrl, desc, count, NULL, 0, NULL, NULL); xfree(desc); return rc; } /* Return true if any keyserver has been configured. */ int keyserver_any_configured (ctrl_t ctrl) { return !gpg_dirmngr_ks_list (ctrl, NULL); } /* Import all keys that exactly match NAME */ int keyserver_import_name (ctrl_t ctrl, const char *name, unsigned char **fpr, size_t *fprlen, struct keyserver_spec *keyserver) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_EXACT; desc.u.name = name; return keyserver_get (ctrl, &desc, 1, keyserver, 0, fpr, fprlen); } int keyserver_import_fprint (ctrl_t ctrl, const byte *fprint,size_t fprint_len, struct keyserver_spec *keyserver, int quick) { KEYDB_SEARCH_DESC desc; memset(&desc,0,sizeof(desc)); if (fprint_len == 16 || fprint_len == 20 || fprint_len == 32) desc.mode = KEYDB_SEARCH_MODE_FPR; else return -1; memcpy(desc.u.fpr,fprint,fprint_len); desc.fprlen = fprint_len; /* TODO: Warn here if the fingerprint we got doesn't match the one we asked for? */ return keyserver_get (ctrl, &desc, 1, keyserver, quick, NULL, NULL); } int keyserver_import_keyid (ctrl_t ctrl, u32 *keyid,struct keyserver_spec *keyserver, int quick) { KEYDB_SEARCH_DESC desc; memset(&desc,0,sizeof(desc)); desc.mode=KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0]=keyid[0]; desc.u.kid[1]=keyid[1]; return keyserver_get (ctrl, &desc, 1, keyserver, quick, NULL, NULL); } /* code mostly stolen from do_export_stream */ static int keyidlist (ctrl_t ctrl, strlist_t users, KEYDB_SEARCH_DESC **klist, int *count, int fakev3) { int rc = 0; int num = 100; kbnode_t keyblock = NULL; kbnode_t node; KEYDB_HANDLE kdbhd; int ndesc; KEYDB_SEARCH_DESC *desc = NULL; strlist_t sl; *count=0; *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) { rc = gpg_error_from_syserror (); goto leave; } keydb_disable_caching (kdbhd); /* We are looping the search. */ if(!users) { ndesc = 1; desc = xmalloc_clear ( ndesc * sizeof *desc); desc[0].mode = KEYDB_SEARCH_MODE_FIRST; } else { for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++) ; desc = xmalloc ( ndesc * sizeof *desc); for (ndesc=0, sl=users; sl; sl = sl->next) { gpg_error_t err; if (!(err = classify_user_id (sl->d, desc+ndesc, 1))) ndesc++; else log_error (_("key \"%s\" not found: %s\n"), sl->d, gpg_strerror (err)); } } for (;;) { rc = keydb_search (kdbhd, desc, ndesc, NULL); if (rc) break; /* ready. */ if (!users) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; /* read the keyblock */ rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); goto leave; } if((node=find_kbnode(keyblock,PKT_PUBLIC_KEY))) { /* This is to work around a bug in some keyservers (pksd and OKS) that calculate v4 RSA keyids as if they were v3 RSA. The answer is to refresh both the correct v4 keyid (e.g. 99242560) and the fake v3 keyid (e.g. 68FDDBC7). This only happens for key refresh using the HKP scheme and if the refresh-add-fake-v3-keyids keyserver option is set. */ if(fakev3 && is_RSA(node->pkt->pkt.public_key->pubkey_algo) && node->pkt->pkt.public_key->version>=4) { (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; v3_keyid (node->pkt->pkt.public_key->pkey[0], (*klist)[*count].u.kid); (*count)++; if(*count==num) { num+=100; *klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); } } /* v4 keys get full fingerprints. v3 keys get long keyids. This is because it's easy to calculate any sort of keyid from a v4 fingerprint, but not a v3 fingerprint. */ if (node->pkt->pkt.public_key->version < 4) { (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID; keyid_from_pk(node->pkt->pkt.public_key, (*klist)[*count].u.kid); } else { size_t fprlen; fingerprint_from_pk (node->pkt->pkt.public_key, (*klist)[*count].u.fpr, &fprlen); (*klist)[*count].mode = KEYDB_SEARCH_MODE_FPR; (*klist)[*count].fprlen = fprlen; } /* This is a little hackish, using the skipfncvalue as a void* pointer to the keyserver spec, but we don't need the skipfnc here, and it saves having an additional field for this (which would be wasted space most of the time). */ (*klist)[*count].skipfncvalue=NULL; /* Are we honoring preferred keyservers? */ if(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL) { PKT_user_id *uid=NULL; PKT_signature *sig=NULL; merge_keys_and_selfsig (ctrl, keyblock); for(node=node->next;node;node=node->next) { if(node->pkt->pkttype==PKT_USER_ID && node->pkt->pkt.user_id->flags.primary) uid=node->pkt->pkt.user_id; else if(node->pkt->pkttype==PKT_SIGNATURE && node->pkt->pkt.signature-> flags.chosen_selfsig && uid) { sig=node->pkt->pkt.signature; break; } } /* Try and parse the keyserver URL. If it doesn't work, then we end up writing NULL which indicates we are the same as any other key. */ if(sig) (*klist)[*count].skipfncvalue=parse_preferred_keyserver(sig); } (*count)++; if(*count==num) { num+=100; *klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num); } } } if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) rc = 0; leave: if(rc) { xfree(*klist); *klist = NULL; } xfree(desc); keydb_release(kdbhd); release_kbnode(keyblock); return rc; } /* Note this is different than the original HKP refresh. It allows usernames to refresh only part of the keyring. */ gpg_error_t keyserver_refresh (ctrl_t ctrl, strlist_t users) { gpg_error_t err; int count, numdesc; int fakev3 = 0; KEYDB_SEARCH_DESC *desc; unsigned int options=opt.keyserver_options.import_options; /* We switch merge-only on during a refresh, as 'refresh' should never import new keys, even if their keyids match. */ opt.keyserver_options.import_options|=IMPORT_MERGE_ONLY; /* Similarly, we switch on fast-import, since refresh may make multiple import sets (due to preferred keyserver URLs). We don't want each set to rebuild the trustdb. Instead we do it once at the end here. */ opt.keyserver_options.import_options|=IMPORT_FAST; /* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO scheme, then enable fake v3 keyid generation. Note that this works only with a keyserver configured. gpg.conf (i.e. opt.keyserver); however that method of configuring a keyserver is deprecated and in any case it is questionable whether we should keep on supporting these ancient and broken keyservers. */ if((opt.keyserver_options.options&KEYSERVER_ADD_FAKE_V3) && opt.keyserver && (ascii_strcasecmp(opt.keyserver->scheme,"hkp")==0 || ascii_strcasecmp(opt.keyserver->scheme,"mailto")==0)) fakev3=1; err = keyidlist (ctrl, users, &desc, &numdesc, fakev3); if (err) return err; count=numdesc; if(count>0) { int i; /* Try to handle preferred keyserver keys first */ for(i=0;iuri); /* We use the keyserver structure we parsed out before. Note that a preferred keyserver without a scheme:// will be interpreted as hkp:// */ err = keyserver_get (ctrl, &desc[i], 1, keyserver, 0, NULL, NULL); if (err) log_info(_("WARNING: unable to refresh key %s" " via %s: %s\n"),keystr_from_desc(&desc[i]), keyserver->uri,gpg_strerror (err)); else { /* We got it, so mark it as NONE so we don't try and get it again from the regular keyserver. */ desc[i].mode=KEYDB_SEARCH_MODE_NONE; count--; } free_keyserver_spec(keyserver); } } } if(count>0) { char *tmpuri; err = gpg_dirmngr_ks_list (ctrl, &tmpuri); if (!err) { if (!opt.quiet) { log_info (ngettext("refreshing %d key from %s\n", "refreshing %d keys from %s\n", count), count, tmpuri); } xfree (tmpuri); err = keyserver_get (ctrl, desc, numdesc, NULL, 0, NULL, NULL); } } xfree(desc); opt.keyserver_options.import_options=options; /* If the original options didn't have fast import, and the trustdb is dirty, rebuild. */ if(!(opt.keyserver_options.import_options&IMPORT_FAST)) check_or_update_trustdb (ctrl); return err; } /* Search for keys on the keyservers. The patterns are given in the string list TOKENS. */ gpg_error_t keyserver_search (ctrl_t ctrl, strlist_t tokens) { gpg_error_t err; char *searchstr; struct search_line_handler_parm_s parm; memset (&parm, 0, sizeof parm); if (!tokens) return 0; /* Return success if no patterns are given. */ /* Write global options */ /* for(temp=opt.keyserver_options.other;temp;temp=temp->next) */ /* es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ /* Write per-keyserver options */ /* for(temp=keyserver->options;temp;temp=temp->next) */ /* es_fprintf(spawn->tochild,"OPTION %s\n",temp->d); */ { membuf_t mb; strlist_t item; init_membuf (&mb, 1024); for (item = tokens; item; item = item->next) { if (item != tokens) put_membuf (&mb, " ", 1); put_membuf_str (&mb, item->d); } put_membuf (&mb, "", 1); /* Append Nul. */ searchstr = get_membuf (&mb, NULL); if (!searchstr) { err = gpg_error_from_syserror (); goto leave; } } /* FIXME: Enable the next line */ /* log_info (_("searching for \"%s\" from %s\n"), searchstr, keyserver->uri); */ parm.ctrl = ctrl; if (searchstr) parm.searchstr_disp = utf8_to_native (searchstr, strlen (searchstr), 0); err = gpg_dirmngr_ks_search (ctrl, searchstr, search_line_handler, &parm); if (parm.not_found || gpg_err_code (err) == GPG_ERR_NO_DATA) { if (parm.searchstr_disp) log_info (_("key \"%s\" not found on keyserver\n"), parm.searchstr_disp); else log_info (_("key not found on keyserver\n")); } if (gpg_err_code (err) == GPG_ERR_NO_DATA) err = gpg_error (GPG_ERR_NOT_FOUND); else if (err) log_error ("error searching keyserver: %s\n", gpg_strerror (err)); /* switch(ret) */ /* { */ /* case KEYSERVER_SCHEME_NOT_FOUND: */ /* log_error(_("no handler for keyserver scheme '%s'\n"), */ /* opt.keyserver->scheme); */ /* break; */ /* case KEYSERVER_NOT_SUPPORTED: */ /* log_error(_("action '%s' not supported with keyserver " */ /* "scheme '%s'\n"), "search", opt.keyserver->scheme); */ /* break; */ /* case KEYSERVER_TIMEOUT: */ /* log_error(_("keyserver timed out\n")); */ /* break; */ /* case KEYSERVER_INTERNAL_ERROR: */ /* default: */ /* log_error(_("keyserver internal error\n")); */ /* break; */ /* } */ /* return gpg_error (GPG_ERR_KEYSERVER); */ leave: xfree (parm.desc); xfree (parm.searchstr_disp); xfree(searchstr); return err; } /* Helper for keyserver_get. Here we only receive a chunk of the description to be processed in one batch. This is required due to the limited number of patterns the dirmngr interface (KS_GET) can grok and to limit the amount of temporary required memory. */ static gpg_error_t keyserver_get_chunk (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, int *r_ndesc_used, import_stats_t stats_handle, struct keyserver_spec *override_keyserver, int quick, unsigned char **r_fpr, size_t *r_fprlen) { gpg_error_t err = 0; char **pattern; int idx, npat, npat_fpr; estream_t datastream; char *source = NULL; size_t linelen; /* Estimated linelen for KS_GET. */ size_t n; int only_fprs; #define MAX_KS_GET_LINELEN 950 /* Somewhat lower than the real limit. */ *r_ndesc_used = 0; /* Create an array filled with a search pattern for each key. The array is delimited by a NULL entry. */ pattern = xtrycalloc (ndesc+1, sizeof *pattern); if (!pattern) return gpg_error_from_syserror (); /* Note that we break the loop as soon as our estimation of the to be used line length reaches the limit. But we do this only if we have processed at least one search requests so that an overlong single request will be rejected only later by gpg_dirmngr_ks_get but we are sure that R_NDESC_USED has been updated. This avoids a possible indefinite loop. */ linelen = 17; /* "KS_GET --quick --" */ for (npat=npat_fpr=0, idx=0; idx < ndesc; idx++) { int quiet = 0; if (desc[idx].mode == KEYDB_SEARCH_MODE_FPR) { n = 1+2+2*desc[idx].fprlen; if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = xtrymalloc (n); if (!pattern[npat]) err = gpg_error_from_syserror (); else { strcpy (pattern[npat], "0x"); bin2hex (desc[idx].u.fpr, desc[idx].fprlen, pattern[npat]+2); npat++; if (desc[idx].fprlen == 20 || desc[idx].fprlen == 32) npat_fpr++; } } else if(desc[idx].mode == KEYDB_SEARCH_MODE_LONG_KID) { n = 1+2+16; if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = xtryasprintf ("0x%08lX%08lX", (ulong)desc[idx].u.kid[0], (ulong)desc[idx].u.kid[1]); if (!pattern[npat]) err = gpg_error_from_syserror (); else npat++; } else if(desc[idx].mode == KEYDB_SEARCH_MODE_SHORT_KID) { n = 1+2+8; if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = xtryasprintf ("0x%08lX", (ulong)desc[idx].u.kid[1]); if (!pattern[npat]) err = gpg_error_from_syserror (); else npat++; } else if(desc[idx].mode == KEYDB_SEARCH_MODE_EXACT) { /* The Dirmngr also uses classify_user_id to detect the type of the search string. By adding the '=' prefix we force Dirmngr's KS_GET to consider this an exact search string. (In gpg 1.4 and gpg 2.0 the keyserver helpers used the KS_GETNAME command to indicate this.) */ n = 1+1+strlen (desc[idx].u.name); if (idx && linelen + n > MAX_KS_GET_LINELEN) break; /* Declare end of this chunk. */ linelen += n; pattern[npat] = strconcat ("=", desc[idx].u.name, NULL); if (!pattern[npat]) err = gpg_error_from_syserror (); else { npat++; quiet = 1; } } else if (desc[idx].mode == KEYDB_SEARCH_MODE_NONE) continue; else BUG(); if (err) { for (idx=0; idx < npat; idx++) xfree (pattern[idx]); xfree (pattern); return err; } if (!quiet && override_keyserver) { if (override_keyserver->host) log_info (_("requesting key %s from %s server %s\n"), keystr_from_desc (&desc[idx]), override_keyserver->scheme, override_keyserver->host); else log_info (_("requesting key %s from %s\n"), keystr_from_desc (&desc[idx]), override_keyserver->uri); } } /* Remember how many of the search items were considered. Note that this is different from NPAT. */ *r_ndesc_used = idx; only_fprs = (npat && npat == npat_fpr); err = gpg_dirmngr_ks_get (ctrl, pattern, override_keyserver, quick, &datastream, &source); for (idx=0; idx < npat; idx++) xfree (pattern[idx]); xfree (pattern); if (opt.verbose && source) log_info ("data source: %s\n", source); if (!err) { struct ks_retrieval_screener_arg_s screenerarg; /* FIXME: Check whether this comment should be moved to dirmngr. Slurp up all the key data. In the future, it might be nice to look for KEY foo OUTOFBAND and FAILED indicators. It's harmless to ignore them, but ignoring them does make gpg complain about "no valid OpenPGP data found". One way to do this could be to continue parsing this line-by-line and make a temp iobuf for each key. Note that we don't allow the import of secret keys from a keyserver. Keyservers should never accept or send them but we better protect against rogue keyservers. */ screenerarg.desc = desc; screenerarg.ndesc = *r_ndesc_used; import_keys_es_stream (ctrl, datastream, stats_handle, r_fpr, r_fprlen, (opt.keyserver_options.import_options | IMPORT_NO_SECKEY), keyserver_retrieval_screener, &screenerarg, only_fprs? KEYORG_KS : 0, source); } es_fclose (datastream); xfree (source); return err; } /* Retrieve a key from a keyserver. The search pattern are in (DESC,NDESC). Allowed search modes are keyid, fingerprint, and exact searches. OVERRIDE_KEYSERVER gives an optional override keyserver. If (R_FPR,R_FPRLEN) are not NULL, they may return the fingerprint of a single imported key. If QUICK is set, dirmngr is advised to use a shorter timeout. */ static gpg_error_t keyserver_get (ctrl_t ctrl, KEYDB_SEARCH_DESC *desc, int ndesc, struct keyserver_spec *override_keyserver, int quick, unsigned char **r_fpr, size_t *r_fprlen) { gpg_error_t err; import_stats_t stats_handle; int ndesc_used; int any_good = 0; stats_handle = import_new_stats_handle(); for (;;) { err = keyserver_get_chunk (ctrl, desc, ndesc, &ndesc_used, stats_handle, override_keyserver, quick, r_fpr, r_fprlen); if (!err) any_good = 1; if (err || ndesc_used >= ndesc) break; /* Error or all processed. */ /* Prepare for the next chunk. */ desc += ndesc_used; ndesc -= ndesc_used; } if (any_good) import_print_stats (stats_handle); import_release_stats_handle (stats_handle); return err; } /* Send all keys specified by KEYSPECS to the configured keyserver. */ static gpg_error_t keyserver_put (ctrl_t ctrl, strlist_t keyspecs) { gpg_error_t err; strlist_t kspec; char *ksurl; if (!keyspecs) return 0; /* Return success if the list is empty. */ if (gpg_dirmngr_ks_list (ctrl, &ksurl)) { log_error (_("no keyserver known\n")); return gpg_error (GPG_ERR_NO_KEYSERVER); } for (kspec = keyspecs; kspec; kspec = kspec->next) { void *data; size_t datalen; kbnode_t keyblock; err = export_pubkey_buffer (ctrl, kspec->d, opt.keyserver_options.export_options, NULL, &keyblock, &data, &datalen); if (err) log_error (_("skipped \"%s\": %s\n"), kspec->d, gpg_strerror (err)); else { log_info (_("sending key %s to %s\n"), keystr (keyblock->pkt->pkt.public_key->keyid), ksurl?ksurl:"[?]"); err = gpg_dirmngr_ks_put (ctrl, data, datalen, keyblock); release_kbnode (keyblock); xfree (data); if (err) { write_status_error ("keyserver_send", err); log_error (_("keyserver send failed: %s\n"), gpg_strerror (err)); } } } xfree (ksurl); return err; } /* Loop over all URLs in STRLIST and fetch the key at that URL. Note that the fetch operation ignores the configured keyservers and instead directly retrieves the keys. */ int keyserver_fetch (ctrl_t ctrl, strlist_t urilist, int origin) { gpg_error_t err; strlist_t sl; estream_t datastream; unsigned int save_options = opt.keyserver_options.import_options; /* Switch on fast-import, since fetch can handle more than one import and we don't want each set to rebuild the trustdb. Instead we do it once at the end. */ opt.keyserver_options.import_options |= IMPORT_FAST; for (sl=urilist; sl; sl=sl->next) { if (!opt.quiet) log_info (_("requesting key from '%s'\n"), sl->d); err = gpg_dirmngr_ks_fetch (ctrl, sl->d, &datastream); if (!err) { import_stats_t stats_handle; stats_handle = import_new_stats_handle(); import_keys_es_stream (ctrl, datastream, stats_handle, NULL, NULL, opt.keyserver_options.import_options, NULL, NULL, origin, sl->d); import_print_stats (stats_handle); import_release_stats_handle (stats_handle); } else log_info (_("WARNING: unable to fetch URI %s: %s\n"), sl->d, gpg_strerror (err)); es_fclose (datastream); } opt.keyserver_options.import_options = save_options; /* If the original options didn't have fast import, and the trustdb is dirty, rebuild. */ if (!(opt.keyserver_options.import_options&IMPORT_FAST)) check_or_update_trustdb (ctrl); return 0; } /* Import key in a CERT or pointed to by a CERT. In DANE_MODE fetch the certificate using the DANE method. */ int keyserver_import_cert (ctrl_t ctrl, const char *name, int dane_mode, unsigned char **fpr,size_t *fpr_len) { gpg_error_t err; char *look,*url; estream_t key; look = xstrdup(name); if (!dane_mode) { char *domain = strrchr (look,'@'); if (domain) *domain='.'; } err = gpg_dirmngr_dns_cert (ctrl, look, dane_mode? NULL : "*", &key, fpr, fpr_len, &url); if (err) ; else if (key) { int armor_status=opt.no_armor; import_filter_t save_filt; /* CERTs and DANE records are always in binary format */ opt.no_armor=1; if (dane_mode) { save_filt = save_and_clear_import_filter (); if (!save_filt) err = gpg_error_from_syserror (); else { char *filtstr = es_bsprintf ("keep-uid=mbox = %s", look); err = filtstr? 0 : gpg_error_from_syserror (); if (!err) err = parse_and_set_import_filter (filtstr); xfree (filtstr); if (!err) err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, IMPORT_NO_SECKEY, NULL, NULL, KEYORG_DANE, NULL); restore_import_filter (save_filt); } } else { err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, (opt.keyserver_options.import_options | IMPORT_NO_SECKEY), NULL, NULL, 0, NULL); } opt.no_armor=armor_status; es_fclose (key); key = NULL; } else if (*fpr) { /* We only consider the IPGP type if a fingerprint was provided. This lets us select the right key regardless of what a URL points to, or get the key from a keyserver. */ if(url) { struct keyserver_spec *spec; spec = parse_keyserver_uri (url, 1); if(spec) { err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, spec, 0); free_keyserver_spec(spec); } } else if (keyserver_any_configured (ctrl)) { /* If only a fingerprint is provided, try and fetch it from the configured keyserver. */ err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, opt.keyserver, 0); } else log_info(_("no keyserver known\n")); /* Give a better string here? "CERT fingerprint for \"%s\" found, but no keyserver" " known (use option --keyserver)\n" ? */ } xfree(url); xfree(look); return err; } /* Import key pointed to by a PKA record. Return the requested fingerprint in fpr. */ gpg_error_t keyserver_import_pka (ctrl_t ctrl, const char *name, unsigned char **fpr, size_t *fpr_len) { gpg_error_t err; char *url; err = gpg_dirmngr_get_pka (ctrl, name, fpr, fpr_len, &url); if (url && *url && fpr && fpr_len) { /* An URL is available. Lookup the key. */ struct keyserver_spec *spec; spec = parse_keyserver_uri (url, 1); if (spec) { err = keyserver_import_fprint (ctrl, *fpr, *fpr_len, spec, 0); free_keyserver_spec (spec); } } xfree (url); if (err) { xfree(*fpr); *fpr = NULL; *fpr_len = 0; } return err; } /* Import a key using the Web Key Directory protocol. */ gpg_error_t keyserver_import_wkd (ctrl_t ctrl, const char *name, int quick, unsigned char **fpr, size_t *fpr_len) { gpg_error_t err; char *mbox; estream_t key; char *url = NULL; /* We want to work on the mbox. That is what dirmngr will do anyway * and we need the mbox for the import filter anyway. */ mbox = mailbox_from_userid (name, 0); if (!mbox) { err = gpg_error_from_syserror (); if (gpg_err_code (err) == GPG_ERR_EINVAL) err = gpg_error (GPG_ERR_INV_USER_ID); return err; } err = gpg_dirmngr_wkd_get (ctrl, mbox, quick, &key, &url); if (err) ; else if (key) { int armor_status = opt.no_armor; import_filter_t save_filt; /* Keys returned via WKD are in binary format. However, we * relax that requirement and allow also for armored data. */ opt.no_armor = 0; save_filt = save_and_clear_import_filter (); if (!save_filt) err = gpg_error_from_syserror (); else { char *filtstr = es_bsprintf ("keep-uid=mbox = %s", mbox); err = filtstr? 0 : gpg_error_from_syserror (); if (!err) err = parse_and_set_import_filter (filtstr); xfree (filtstr); if (!err) err = import_keys_es_stream (ctrl, key, NULL, fpr, fpr_len, IMPORT_NO_SECKEY, NULL, NULL, KEYORG_WKD, url); } restore_import_filter (save_filt); opt.no_armor = armor_status; es_fclose (key); key = NULL; } xfree (url); xfree (mbox); return err; } /* Import a key by name using LDAP */ int keyserver_import_ldap (ctrl_t ctrl, const char *name, unsigned char **fpr, size_t *fprlen) { (void)ctrl; (void)name; (void)fpr; (void)fprlen; return gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/ #if 0 char *domain; struct keyserver_spec *keyserver; strlist_t list=NULL; int rc,hostlen=1; struct srventry *srvlist=NULL; int srvcount,i; char srvname[MAXDNAME]; /* Parse out the domain */ domain=strrchr(name,'@'); if(!domain) return GPG_ERR_GENERAL; domain++; keyserver=xmalloc_clear(sizeof(struct keyserver_spec)); keyserver->scheme=xstrdup("ldap"); keyserver->host=xmalloc(1); keyserver->host[0]='\0'; snprintf(srvname,MAXDNAME,"_pgpkey-ldap._tcp.%s",domain); FIXME("network related - move to dirmngr or drop the code"); srvcount=getsrv(srvname,&srvlist); for(i=0;ihost=xrealloc(keyserver->host,hostlen); strcat(keyserver->host,srvlist[i].target); if(srvlist[i].port!=389) { char port[7]; hostlen+=6; /* a colon, plus 5 digits (unsigned 16-bit value) */ keyserver->host=xrealloc(keyserver->host,hostlen); snprintf(port,7,":%u",srvlist[i].port); strcat(keyserver->host,port); } strcat(keyserver->host," "); } free(srvlist); /* If all else fails, do the PGP Universal trick of ldap://keys.(domain) */ hostlen+=5+strlen(domain); keyserver->host=xrealloc(keyserver->host,hostlen); strcat(keyserver->host,"keys."); strcat(keyserver->host,domain); append_to_strlist(&list,name); rc = gpg_error (GPG_ERR_NOT_IMPLEMENTED); /*FIXME*/ /* keyserver_work (ctrl, KS_GETNAME, list, NULL, */ /* 0, fpr, fpr_len, keyserver); */ free_strlist(list); free_keyserver_spec(keyserver); return rc; #endif } diff --git a/g10/main.h b/g10/main.h index f45b03909..981315a4a 100644 --- a/g10/main.h +++ b/g10/main.h @@ -1,519 +1,519 @@ /* main.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef G10_MAIN_H #define G10_MAIN_H #include "../common/types.h" #include "../common/iobuf.h" #include "keydb.h" #include "keyedit.h" #include "../common/util.h" /* It could be argued that the default cipher should be 3DES rather than AES128, and the default compression should be 0 (i.e. uncompressed) rather than 1 (zip). However, the real world issues of speed and size come into play here. */ #if GPG_USE_AES256 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES256 #elif GPG_USE_AES128 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_AES #elif GPG_USE_CAST5 # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5 #else # define DEFAULT_CIPHER_ALGO CIPHER_ALGO_3DES #endif /* We will start using OCB mode by default only if the yet to be * released libgcrypt 1.9 is used. */ #if GCRYPT_VERSION_NUMBER < 0x010900 # define DEFAULT_AEAD_ALGO AEAD_ALGO_OCB #else # define DEFAULT_AEAD_ALGO AEAD_ALGO_EAX #endif #define DEFAULT_DIGEST_ALGO ((GNUPG)? DIGEST_ALGO_SHA256:DIGEST_ALGO_SHA1) #define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1 #ifdef HAVE_ZIP # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP #else # define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_NONE #endif #define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO) /* Various data objects. */ typedef struct { ctrl_t ctrl; int header_okay; PK_LIST pk_list; DEK *symkey_dek; STRING2KEY *symkey_s2k; cipher_filter_context_t cfx; } encrypt_filter_context_t; struct groupitem { char *name; strlist_t values; struct groupitem *next; }; struct weakhash { enum gcry_md_algos algo; int rejection_shown; struct weakhash *next; }; /*-- gpg.c --*/ extern int g10_errors_seen; #if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 ) void g10_exit(int rc) __attribute__ ((noreturn)); #else void g10_exit(int rc); #endif void print_pubkey_algo_note (pubkey_algo_t algo); void print_cipher_algo_note (cipher_algo_t algo); void print_digest_algo_note (digest_algo_t algo); void print_digest_rejected_note (enum gcry_md_algos algo); void print_reported_error (gpg_error_t err, gpg_err_code_t skip_if_ec); void print_further_info (const char *format, ...) GPGRT_ATTR_PRINTF(1,2); void additional_weak_digest (const char* digestname); /*-- armor.c --*/ char *make_radix64_string( const byte *data, size_t len ); /*-- misc.c --*/ void trap_unaligned(void); void register_secured_file (const char *fname); void unregister_secured_file (const char *fname); int is_secured_file (int fd); int is_secured_filename (const char *fname); u16 checksum_u16( unsigned n ); u16 checksum( byte *p, unsigned n ); u16 checksum_mpi( gcry_mpi_t a ); u32 buffer_to_u32( const byte *buffer ); const byte *get_session_marker( size_t *rlen ); enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo); #define openpgp_cipher_open(_a,_b,_c,_d) \ gcry_cipher_open((_a),map_cipher_openpgp_to_gcry((_b)),(_c),(_d)) #define openpgp_cipher_get_algo_keylen(_a) \ gcry_cipher_get_algo_keylen(map_cipher_openpgp_to_gcry((_a))) #define openpgp_cipher_get_algo_blklen(_a) \ gcry_cipher_get_algo_blklen(map_cipher_openpgp_to_gcry((_a))) int openpgp_cipher_blocklen (cipher_algo_t algo); int openpgp_cipher_test_algo(cipher_algo_t algo); const char *openpgp_cipher_algo_name (cipher_algo_t algo); gpg_error_t openpgp_aead_test_algo (aead_algo_t algo); const char *openpgp_aead_algo_name (aead_algo_t algo); gpg_error_t openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode, unsigned int *r_noncelen); pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo); int openpgp_pk_test_algo (pubkey_algo_t algo); int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use); int openpgp_pk_algo_usage ( int algo ); const char *openpgp_pk_algo_name (pubkey_algo_t algo); enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo); int openpgp_md_test_algo (digest_algo_t algo); const char *openpgp_md_algo_name (int algo); struct expando_args { PKT_public_key *pk; PKT_public_key *pksk; byte imagetype; int validity_info; const char *validity_string; const byte *namehash; }; -char *pct_expando(const char *string,struct expando_args *args); +char *pct_expando (ctrl_t ctrl, const char *string,struct expando_args *args); void deprecated_warning(const char *configname,unsigned int configlineno, const char *option,const char *repl1,const char *repl2); void deprecated_command (const char *name); void obsolete_scdaemon_option (const char *configname, unsigned int configlineno, const char *name); int string_to_cipher_algo (const char *string); aead_algo_t string_to_aead_algo (const char *string); int string_to_digest_algo (const char *string); const char *compress_algo_to_string(int algo); int string_to_compress_algo(const char *string); int check_compress_algo(int algo); int default_cipher_algo(void); aead_algo_t default_aead_algo(void); int default_compress_algo(void); void compliance_failure(void); struct parse_options { char *name; unsigned int bit; char **value; char *help; }; char *optsep(char **stringp); char *argsplit(char *string); int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy); const char *get_libexecdir (void); int path_access(const char *file,int mode); int pubkey_get_npkey (pubkey_algo_t algo); int pubkey_get_nskey (pubkey_algo_t algo); int pubkey_get_nsig (pubkey_algo_t algo); int pubkey_get_nenc (pubkey_algo_t algo); /* Temporary helpers. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *pkey ); int mpi_print (estream_t stream, gcry_mpi_t a, int mode); unsigned int ecdsa_qbits_from_Q (unsigned int qbits); /*-- cpr.c --*/ void set_status_fd ( int fd ); int is_status_enabled ( void ); void write_status ( int no ); void write_status_error (const char *where, gpg_error_t err); void write_status_errcode (const char *where, int errcode); void write_status_failure (const char *where, gpg_error_t err); void write_status_text ( int no, const char *text ); void write_status_printf (int no, const char *format, ...) GPGRT_ATTR_PRINTF(2,3); void write_status_strings (int no, const char *text, ...) GPGRT_ATTR_SENTINEL(0); void write_status_buffer ( int no, const char *buffer, size_t len, int wrap ); void write_status_text_and_buffer ( int no, const char *text, const char *buffer, size_t len, int wrap ); void write_status_begin_signing (gcry_md_hd_t md); int cpr_enabled(void); char *cpr_get( const char *keyword, const char *prompt ); char *cpr_get_no_help( const char *keyword, const char *prompt ); char *cpr_get_utf8( const char *keyword, const char *prompt ); char *cpr_get_hidden( const char *keyword, const char *prompt ); void cpr_kill_prompt(void); int cpr_get_answer_is_yes_def (const char *keyword, const char *prompt, int def_yes); int cpr_get_answer_is_yes( const char *keyword, const char *prompt ); int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt ); int cpr_get_answer_okay_cancel (const char *keyword, const char *prompt, int def_answer); /*-- helptext.c --*/ void display_online_help( const char *keyword ); /*-- encode.c --*/ int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek); gpg_error_t encrypt_seskey (DEK *dek, aead_algo_t aead_algo, DEK **r_seskey, void **r_enckey, size_t *r_enckeylen); aead_algo_t use_aead (pk_list_t pk_list, int algo); int use_mdc (pk_list_t pk_list,int algo); int encrypt_symmetric (const char *filename ); int encrypt_store (const char *filename ); int encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename, strlist_t remusr, int use_symkey, pk_list_t provided_keys, int outputfd); void encrypt_crypt_files (ctrl_t ctrl, int nfiles, char **files, strlist_t remusr); int encrypt_filter (void *opaque, int control, iobuf_t a, byte *buf, size_t *ret_len); int write_pubkey_enc (ctrl_t ctrl, PKT_public_key *pk, int throw_keyid, DEK *dek, iobuf_t out); /*-- sign.c --*/ int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int do_encrypt, strlist_t remusr, const char *outfile ); int clearsign_file (ctrl_t ctrl, const char *fname, strlist_t locusr, const char *outfile); int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr); /*-- sig-check.c --*/ void sig_check_dump_stats (void); /* SIG is a revocation signature. Check if any of PK's designated revokers generated it. If so, return 0. Note: this function (correctly) doesn't care if the designated revoker is revoked. */ int check_revocation_keys (ctrl_t ctrl, PKT_public_key *pk, PKT_signature *sig); /* Check that the backsig BACKSIG from the subkey SUB_PK to its primary key MAIN_PK is valid. */ int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk, PKT_signature *backsig); /* Check that the signature SIG over a key (e.g., a key binding or a key revocation) is valid. (To check signatures over data, use check_signature.) */ int check_key_signature (ctrl_t ctrl, kbnode_t root, kbnode_t sig, int *is_selfsig ); /* Like check_key_signature, but with the ability to specify some additional parameters and get back additional information. See the documentation for the implementation for details. */ int check_key_signature2 (ctrl_t ctrl, kbnode_t root, kbnode_t node, PKT_public_key *check_pk, PKT_public_key *ret_pk, int *is_selfsig, u32 *r_expiredate, int *r_expired); /* Returns whether SIGNER generated the signature SIG over the packet PACKET, which is a key, subkey or uid, and comes from the key block KB. If SIGNER is NULL, it is looked up based on the information in SIG. If not NULL, sets *IS_SELFSIG to indicate whether the signature is a self-signature and *RET_PK to a copy of the signer's key. */ gpg_error_t check_signature_over_key_or_uid (ctrl_t ctrl, PKT_public_key *signer, PKT_signature *sig, KBNODE kb, PACKET *packet, int *is_selfsig, PKT_public_key *ret_pk); /*-- delkey.c --*/ gpg_error_t delete_keys (ctrl_t ctrl, strlist_t names, int secret, int allow_both); /*-- keygen.c --*/ const char *get_default_pubkey_algo (void); u32 parse_expire_string(const char *string); u32 ask_expire_interval(int object,const char *def_expire); u32 ask_expiredate(void); unsigned int ask_key_flags (int algo, int subkey, unsigned int current); const char *ask_curve (int *algo, int *subkey_algo, const char *current); void quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, const char *usagestr, const char *expirestr); void generate_keypair (ctrl_t ctrl, int full, const char *fname, const char *card_serialno, int card_backup_key); int keygen_set_std_prefs (const char *string,int personal); PKT_user_id *keygen_get_std_prefs (void); int keygen_add_key_expire( PKT_signature *sig, void *opaque ); int keygen_add_key_flags (PKT_signature *sig, void *opaque); int keygen_add_std_prefs( PKT_signature *sig, void *opaque ); int keygen_upd_std_prefs( PKT_signature *sig, void *opaque ); int keygen_add_keyserver_url(PKT_signature *sig, void *opaque); int keygen_add_notations(PKT_signature *sig,void *opaque); int keygen_add_revkey(PKT_signature *sig, void *opaque); gpg_error_t make_backsig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pk, PKT_public_key *sub_pk, PKT_public_key *sub_psk, u32 timestamp, const char *cache_nonce); gpg_error_t generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock, const char *algostr, const char *usagestr, const char *expirestr); #ifdef ENABLE_CARD_SUPPORT gpg_error_t generate_card_subkeypair (ctrl_t ctrl, kbnode_t pub_keyblock, int keyno, const char *serialno); #endif /*-- openfile.c --*/ int overwrite_filep( const char *fname ); char *make_outfile_name( const char *iname ); char *ask_outfile_name( const char *name, size_t namelen ); int open_outfile (int out_fd, const char *iname, int mode, int restrictedperm, iobuf_t *a); char *get_matching_datafile (const char *sigfilename); iobuf_t open_sigfile (const char *sigfilename, progress_filter_context_t *pfx); void try_make_homedir( const char *fname ); char *get_openpgp_revocdir (const char *home); /*-- seskey.c --*/ void make_session_key( DEK *dek ); gcry_mpi_t encode_session_key( int openpgp_pk_algo, DEK *dek, unsigned nbits ); gcry_mpi_t encode_md_value (PKT_public_key *pk, gcry_md_hd_t md, int hash_algo ); /*-- import.c --*/ struct import_stats_s; typedef struct import_stats_s *import_stats_t; struct import_filter_s; typedef struct import_filter_s *import_filter_t; typedef gpg_error_t (*import_screener_t)(kbnode_t keyblock, void *arg); int parse_import_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_import_filter (const char *string); import_filter_t save_and_clear_import_filter (void); void restore_import_filter (import_filter_t filt); gpg_error_t read_key_from_file (ctrl_t ctrl, const char *fname, kbnode_t *r_keyblock); void import_keys (ctrl_t ctrl, char **fnames, int nnames, import_stats_t stats_hd, unsigned int options, int origin, const char *url); gpg_error_t import_keys_es_stream (ctrl_t ctrl, estream_t fp, import_stats_t stats_handle, unsigned char **fpr, size_t *fpr_len, unsigned int options, import_screener_t screener, void *screener_arg, int origin, const char *url); gpg_error_t import_old_secring (ctrl_t ctrl, const char *fname); import_stats_t import_new_stats_handle (void); void import_release_stats_handle (import_stats_t hd); void import_print_stats (import_stats_t hd); /* Communication for impex_filter_getval */ struct impex_filter_parm_s { ctrl_t ctrl; kbnode_t node; }; const char *impex_filter_getval (void *cookie, const char *propname); gpg_error_t transfer_secret_keys (ctrl_t ctrl, struct import_stats_s *stats, kbnode_t sec_keyblock, int batch, int force, int only_marked); int collapse_uids( KBNODE *keyblock ); int get_revocation_reason (PKT_signature *sig, char **r_reason, char **r_comment, size_t *r_commentlen); /*-- export.c --*/ struct export_stats_s; typedef struct export_stats_s *export_stats_t; export_stats_t export_new_stats (void); void export_release_stats (export_stats_t stats); void export_print_stats (export_stats_t stats); int parse_export_options(char *str,unsigned int *options,int noisy); gpg_error_t parse_and_set_export_filter (const char *string); int exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, kbnode_t node); int export_pubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_seckeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); int export_secsubkeys (ctrl_t ctrl, strlist_t users, unsigned int options, export_stats_t stats); gpg_error_t export_pubkey_buffer (ctrl_t ctrl, const char *keyspec, unsigned int options, export_stats_t stats, kbnode_t *r_keyblock, void **r_data, size_t *r_datalen); gpg_error_t receive_seckey_from_agent (ctrl_t ctrl, gcry_cipher_hd_t cipherhd, int cleartext, char **cache_nonce_addr, const char *hexgrip, PKT_public_key *pk); gpg_error_t write_keyblock_to_output (kbnode_t keyblock, int with_armor, unsigned int options); gpg_error_t export_ssh_key (ctrl_t ctrl, const char *userid); /*-- dearmor.c --*/ int dearmor_file( const char *fname ); int enarmor_file( const char *fname ); /*-- revoke.c --*/ struct revocation_reason_info; int gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce); int gen_revoke (ctrl_t ctrl, const char *uname); int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr); int revocation_reason_build_cb( PKT_signature *sig, void *opaque ); struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ); struct revocation_reason_info * get_default_uid_revocation_reason(void); void release_revocation_reason_info( struct revocation_reason_info *reason ); /*-- keylist.c --*/ 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 ); void print_subpackets_colon(PKT_signature *sig); void reorder_keyblock (KBNODE keyblock); void list_keyblock_direct (ctrl_t ctrl, kbnode_t keyblock, int secret, int has_secret, int fpr, int no_validity); void print_fingerprint (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int mode); void print_revokers (estream_t fp, PKT_public_key *pk); void show_policy_url(PKT_signature *sig,int indent,int mode); void show_keyserver_url(PKT_signature *sig,int indent,int mode); void show_notation(PKT_signature *sig,int indent,int mode,int which); void dump_attribs (const PKT_user_id *uid, PKT_public_key *pk); void set_attrib_fd(int fd); void print_key_info (ctrl_t ctrl, estream_t fp, int indent, PKT_public_key *pk, int secret); void print_key_info_log (ctrl_t ctrl, int loglevel, int indent, PKT_public_key *pk, int secret); void print_card_key_info (estream_t fp, KBNODE keyblock); void print_key_line (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, int secret); /*-- verify.c --*/ void print_file_status( int status, const char *name, int what ); int verify_signatures (ctrl_t ctrl, int nfiles, char **files ); int verify_files (ctrl_t ctrl, int nfiles, char **files ); int gpg_verify (ctrl_t ctrl, int sig_fd, int data_fd, estream_t out_fp); /*-- decrypt.c --*/ int decrypt_message (ctrl_t ctrl, const char *filename ); gpg_error_t decrypt_message_fd (ctrl_t ctrl, int input_fd, int output_fd); void decrypt_messages (ctrl_t ctrl, int nfiles, char *files[]); /*-- plaintext.c --*/ int hash_datafiles( gcry_md_hd_t md, gcry_md_hd_t md2, strlist_t files, const char *sigfilename, int textmode); int hash_datafile_by_fd ( gcry_md_hd_t md, gcry_md_hd_t md2, int data_fd, int textmode ); PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf); /*-- server.c --*/ int gpg_server (ctrl_t); gpg_error_t gpg_proxy_pinentry_notify (ctrl_t ctrl, const unsigned char *line); #ifdef ENABLE_CARD_SUPPORT /*-- card-util.c --*/ void change_pin (int no, int allow_admin); void card_status (ctrl_t ctrl, estream_t fp, const char *serialno); void card_edit (ctrl_t ctrl, strlist_t commands); gpg_error_t card_generate_subkey (ctrl_t ctrl, kbnode_t pub_keyblock); int card_store_subkey (KBNODE node, int use); #endif /*-- migrate.c --*/ void migrate_secring (ctrl_t ctrl); #endif /*G10_MAIN_H*/ diff --git a/g10/misc.c b/g10/misc.c index 0541d2b77..c2d5bbb8e 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -1,1888 +1,1888 @@ /* misc.c - miscellaneous functions * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2009, 2010 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2 #include #include #endif #ifdef HAVE_SETRLIMIT #include #include #include #endif #ifdef ENABLE_SELINUX_HACKS #include #endif #ifdef HAVE_W32_SYSTEM #include #include #ifdef HAVE_WINSOCK2_H # define WIN32_LEAN_AND_MEAN 1 # include #endif #include #include #ifndef CSIDL_APPDATA #define CSIDL_APPDATA 0x001a #endif #ifndef CSIDL_LOCAL_APPDATA #define CSIDL_LOCAL_APPDATA 0x001c #endif #ifndef CSIDL_FLAG_CREATE #define CSIDL_FLAG_CREATE 0x8000 #endif #endif /*HAVE_W32_SYSTEM*/ #include "gpg.h" #ifdef HAVE_W32_SYSTEM # include "../common/status.h" #endif /*HAVE_W32_SYSTEM*/ #include "../common/util.h" #include "main.h" #include "photoid.h" #include "options.h" #include "call-agent.h" #include "../common/i18n.h" #include "../common/zb32.h" /* FIXME: Libgcrypt 1.9 will support EAX. Until we kame this a * requirement we hardwire the enum used for EAX. */ #define MY_GCRY_CIPHER_MODE_EAX 14 #ifdef ENABLE_SELINUX_HACKS /* A object and a global variable to keep track of files marked as secured. */ struct secured_file_item { struct secured_file_item *next; ino_t ino; dev_t dev; }; static struct secured_file_item *secured_files; #endif /*ENABLE_SELINUX_HACKS*/ /* For the sake of SELinux we want to restrict access through gpg to certain files we keep under our own control. This function registers such a file and is_secured_file may then be used to check whether a file has ben registered as secured. */ void register_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; /* Note that we stop immediately if something goes wrong here. */ if (stat (fname, &buf)) log_fatal (_("fstat of '%s' failed in %s: %s\n"), fname, "register_secured_file", strerror (errno)); /* log_debug ("registering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return; /* Already registered. */ } sf = xmalloc (sizeof *sf); sf->ino = buf.st_ino; sf->dev = buf.st_dev; sf->next = secured_files; secured_files = sf; #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Remove a file registered as secure. */ void unregister_secured_file (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf, *sfprev; if (stat (fname, &buf)) { log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "unregister_secured_file", strerror (errno)); return; } /* log_debug ("unregistering '%s' i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) { if (sfprev) sfprev->next = sf->next; else secured_files = sf->next; xfree (sf); return; } } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ } /* Return true if FD is corresponds to a secured file. Using -1 for FS is allowed and will return false. */ int is_secured_file (int fd) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (fd == -1) return 0; /* No file descriptor so it can't be secured either. */ /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (fstat (fd, &buf)) { log_error (_("fstat(%d) failed in %s: %s\n"), fd, "is_secured_file", strerror (errno)); return 1; } /* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fd; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } /* Return true if FNAME is corresponds to a secured file. Using NULL, "" or "-" for FS is allowed and will return false. This function is used before creating a file, thus it won't fail if the file does not exist. */ int is_secured_filename (const char *fname) { #ifdef ENABLE_SELINUX_HACKS struct stat buf; struct secured_file_item *sf; if (iobuf_is_pipe_filename (fname) || !*fname) return 0; /* Note that we print out a error here and claim that a file is secure if something went wrong. */ if (stat (fname, &buf)) { if (errno == ENOENT || errno == EPERM || errno == EACCES) return 0; log_error (_("fstat of '%s' failed in %s: %s\n"), fname, "is_secured_filename", strerror (errno)); return 1; } /* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */ /* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */ for (sf=secured_files; sf; sf = sf->next) { if (sf->ino == buf.st_ino && sf->dev == buf.st_dev) return 1; /* Yes. */ } #else /*!ENABLE_SELINUX_HACKS*/ (void)fname; #endif /*!ENABLE_SELINUX_HACKS*/ return 0; /* No. */ } u16 checksum_u16( unsigned n ) { u16 a; a = (n >> 8) & 0xff; a += n & 0xff; return a; } u16 checksum( byte *p, unsigned n ) { u16 a; for(a=0; n; n-- ) a += *p++; return a; } u16 checksum_mpi (gcry_mpi_t a) { u16 csum; byte *buffer; size_t nbytes; if ( gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, a) ) BUG (); /* Fixme: For numbers not in secure memory we should use a stack * based buffer and only allocate a larger one if mpi_print returns * an error. */ buffer = (gcry_is_secure(a)? gcry_xmalloc_secure (nbytes) : gcry_xmalloc (nbytes)); if ( gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, NULL, a) ) BUG (); csum = checksum (buffer, nbytes); xfree (buffer); return csum; } void print_pubkey_algo_note (pubkey_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental public key algorithm %s\n"), openpgp_pk_algo_name (algo)); } } else if (algo == PUBKEY_ALGO_ELGAMAL) { es_fflush (es_stdout); log_info (_("WARNING: Elgamal sign+encrypt keys are deprecated\n")); } } void print_cipher_algo_note (cipher_algo_t algo) { if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental cipher algorithm %s\n"), openpgp_cipher_algo_name (algo)); } } } void print_digest_algo_note (digest_algo_t algo) { const enum gcry_md_algos galgo = map_md_openpgp_to_gcry (algo); const struct weakhash *weak; if(algo >= 100 && algo <= 110) { static int warn=0; if(!warn) { warn=1; es_fflush (es_stdout); log_info (_("WARNING: using experimental digest algorithm %s\n"), gcry_md_algo_name (galgo)); } } else for (weak = opt.weak_digests; weak != NULL; weak = weak->next) if (weak->algo == galgo) { es_fflush (es_stdout); log_info (_("WARNING: digest algorithm %s is deprecated\n"), gcry_md_algo_name (galgo)); } } void print_digest_rejected_note (enum gcry_md_algos algo) { struct weakhash* weak; int show = 1; for (weak = opt.weak_digests; weak; weak = weak->next) if (weak->algo == algo) { if (weak->rejection_shown) show = 0; else weak->rejection_shown = 1; break; } if (show) { es_fflush (es_stdout); log_info (_("Note: signatures using the %s algorithm are rejected\n"), gcry_md_algo_name(algo)); } } /* Print a message * "(reported error: %s)\n * in verbose mode to further explain an error. If the error code has * the value IGNORE_EC no message is printed. A message is also not * printed if ERR is 0. */ void print_reported_error (gpg_error_t err, gpg_err_code_t ignore_ec) { if (!opt.verbose) return; if (!gpg_err_code (err)) ; else if (gpg_err_code (err) == ignore_ec) ; else if (gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT) log_info (_("(reported error: %s)\n"), gpg_strerror (err)); else log_info (_("(reported error: %s <%s>)\n"), gpg_strerror (err), gpg_strsource (err)); } /* Print a message * "(further info: %s)\n * in verbose mode to further explain an error. That message is * intended to help debug a problem and should not be translated. */ void print_further_info (const char *format, ...) { va_list arg_ptr; if (!opt.verbose) return; log_info (_("(further info: ")); va_start (arg_ptr, format); log_logv (GPGRT_LOGLVL_CONT, format, arg_ptr); va_end (arg_ptr); log_printf (")\n"); } /* Map OpenPGP algo numbers to those used by Libgcrypt. We need to do this for algorithms we implemented in Libgcrypt after they become part of OpenPGP. */ enum gcry_cipher_algos map_cipher_openpgp_to_gcry (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_NONE: return GCRY_CIPHER_NONE; #ifdef GPG_USE_IDEA case CIPHER_ALGO_IDEA: return GCRY_CIPHER_IDEA; #else case CIPHER_ALGO_IDEA: return 0; #endif case CIPHER_ALGO_3DES: return GCRY_CIPHER_3DES; #ifdef GPG_USE_CAST5 case CIPHER_ALGO_CAST5: return GCRY_CIPHER_CAST5; #else case CIPHER_ALGO_CAST5: return 0; #endif #ifdef GPG_USE_BLOWFISH case CIPHER_ALGO_BLOWFISH: return GCRY_CIPHER_BLOWFISH; #else case CIPHER_ALGO_BLOWFISH: return 0; #endif #ifdef GPG_USE_AES128 case CIPHER_ALGO_AES: return GCRY_CIPHER_AES; #else case CIPHER_ALGO_AES: return 0; #endif #ifdef GPG_USE_AES192 case CIPHER_ALGO_AES192: return GCRY_CIPHER_AES192; #else case CIPHER_ALGO_AES192: return 0; #endif #ifdef GPG_USE_AES256 case CIPHER_ALGO_AES256: return GCRY_CIPHER_AES256; #else case CIPHER_ALGO_AES256: return 0; #endif #ifdef GPG_USE_TWOFISH case CIPHER_ALGO_TWOFISH: return GCRY_CIPHER_TWOFISH; #else case CIPHER_ALGO_TWOFISH: return 0; #endif #ifdef GPG_USE_CAMELLIA128 case CIPHER_ALGO_CAMELLIA128: return GCRY_CIPHER_CAMELLIA128; #else case CIPHER_ALGO_CAMELLIA128: return 0; #endif #ifdef GPG_USE_CAMELLIA192 case CIPHER_ALGO_CAMELLIA192: return GCRY_CIPHER_CAMELLIA192; #else case CIPHER_ALGO_CAMELLIA192: return 0; #endif #ifdef GPG_USE_CAMELLIA256 case CIPHER_ALGO_CAMELLIA256: return GCRY_CIPHER_CAMELLIA256; #else case CIPHER_ALGO_CAMELLIA256: return 0; #endif default: return 0; } } /* The inverse function of above. */ static cipher_algo_t map_cipher_gcry_to_openpgp (enum gcry_cipher_algos algo) { switch (algo) { case GCRY_CIPHER_NONE: return CIPHER_ALGO_NONE; case GCRY_CIPHER_IDEA: return CIPHER_ALGO_IDEA; case GCRY_CIPHER_3DES: return CIPHER_ALGO_3DES; case GCRY_CIPHER_CAST5: return CIPHER_ALGO_CAST5; case GCRY_CIPHER_BLOWFISH: return CIPHER_ALGO_BLOWFISH; case GCRY_CIPHER_AES: return CIPHER_ALGO_AES; case GCRY_CIPHER_AES192: return CIPHER_ALGO_AES192; case GCRY_CIPHER_AES256: return CIPHER_ALGO_AES256; case GCRY_CIPHER_TWOFISH: return CIPHER_ALGO_TWOFISH; case GCRY_CIPHER_CAMELLIA128: return CIPHER_ALGO_CAMELLIA128; case GCRY_CIPHER_CAMELLIA192: return CIPHER_ALGO_CAMELLIA192; case GCRY_CIPHER_CAMELLIA256: return CIPHER_ALGO_CAMELLIA256; default: return 0; } } /* Map Gcrypt public key algorithm numbers to those used by OpenPGP. FIXME: This mapping is used at only two places - we should get rid of it. */ pubkey_algo_t map_pk_gcry_to_openpgp (enum gcry_pk_algos algo) { switch (algo) { case GCRY_PK_EDDSA: return PUBKEY_ALGO_EDDSA; case GCRY_PK_ECDSA: return PUBKEY_ALGO_ECDSA; case GCRY_PK_ECDH: return PUBKEY_ALGO_ECDH; default: return algo < 110 ? (pubkey_algo_t)algo : 0; } } /* Return the block length of an OpenPGP cipher algorithm. */ int openpgp_cipher_blocklen (cipher_algo_t algo) { /* We use the numbers from OpenPGP to be sure that we get the right block length. This is so that the packet parsing code works even for unknown algorithms (for which we assume 8 due to tradition). NOTE: If you change the returned blocklen above 16, check the callers because they may use a fixed size buffer of that size. */ switch (algo) { case CIPHER_ALGO_AES: case CIPHER_ALGO_AES192: case CIPHER_ALGO_AES256: case CIPHER_ALGO_TWOFISH: case CIPHER_ALGO_CAMELLIA128: case CIPHER_ALGO_CAMELLIA192: case CIPHER_ALGO_CAMELLIA256: return 16; default: return 8; } } /**************** * Wrapper around the libgcrypt function with additional checks on * the OpenPGP constraints for the algo ID. */ int openpgp_cipher_test_algo (cipher_algo_t algo) { enum gcry_cipher_algos ga; ga = map_cipher_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_CIPHER_ALGO); return gcry_cipher_test_algo (ga); } /* Map the OpenPGP cipher algorithm whose ID is contained in ALGORITHM to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_cipher_algo_name (cipher_algo_t algo) { switch (algo) { case CIPHER_ALGO_IDEA: return "IDEA"; case CIPHER_ALGO_3DES: return "3DES"; case CIPHER_ALGO_CAST5: return "CAST5"; case CIPHER_ALGO_BLOWFISH: return "BLOWFISH"; case CIPHER_ALGO_AES: return "AES"; case CIPHER_ALGO_AES192: return "AES192"; case CIPHER_ALGO_AES256: return "AES256"; case CIPHER_ALGO_TWOFISH: return "TWOFISH"; case CIPHER_ALGO_CAMELLIA128: return "CAMELLIA128"; case CIPHER_ALGO_CAMELLIA192: return "CAMELLIA192"; case CIPHER_ALGO_CAMELLIA256: return "CAMELLIA256"; case CIPHER_ALGO_NONE: default: return "?"; } } /* Return 0 if ALGO is supported. Return an error if not. */ gpg_error_t openpgp_aead_test_algo (aead_algo_t algo) { /* FIXME: We currently have no easy way to test whether libgcrypt * implements a mode. The only way we can do this is to open a * cipher context with that mode and close it immediately. That is * a bit costly. So we look at the libgcrypt version and assume * nothing has been patched out. */ switch (algo) { case AEAD_ALGO_NONE: break; case AEAD_ALGO_EAX: #if GCRYPT_VERSION_NUMBER < 0x010900 break; #else return 0; #endif case AEAD_ALGO_OCB: return 0; } return gpg_error (GPG_ERR_INV_CIPHER_MODE); } /* Map the OpenPGP AEAD algorithm with ID ALGO to a string * representation of the algorithm name. For unknown algorithm IDs * this function returns "?". */ const char * openpgp_aead_algo_name (aead_algo_t algo) { switch (algo) { case AEAD_ALGO_NONE: break; case AEAD_ALGO_EAX: return "EAX"; case AEAD_ALGO_OCB: return "OCB"; } return "?"; } /* Return information for the AEAD algorithm ALGO. The corresponding * Libgcrypt ciphermode is stored at R_MODE and the required number of * octets for the nonce at R_NONCELEN. On error and error code is * returned. Note that the taglen is always 128 bits. */ gpg_error_t openpgp_aead_algo_info (aead_algo_t algo, enum gcry_cipher_modes *r_mode, unsigned int *r_noncelen) { switch (algo) { case AEAD_ALGO_OCB: *r_mode = GCRY_CIPHER_MODE_OCB; *r_noncelen = 15; break; case AEAD_ALGO_EAX: *r_mode = MY_GCRY_CIPHER_MODE_EAX; *r_noncelen = 16; break; default: log_error ("unsupported AEAD algo %d\n", algo); return gpg_error (GPG_ERR_INV_CIPHER_MODE); } return 0; } /* Return 0 if ALGO is a supported OpenPGP public key algorithm. */ int openpgp_pk_test_algo (pubkey_algo_t algo) { return openpgp_pk_test_algo2 (algo, 0); } /* Return 0 if ALGO is a supported OpenPGP public key algorithm and allows the usage USE. */ int openpgp_pk_test_algo2 (pubkey_algo_t algo, unsigned int use) { enum gcry_pk_algos ga = 0; size_t use_buf = use; switch (algo) { #ifdef GPG_USE_RSA case PUBKEY_ALGO_RSA: ga = GCRY_PK_RSA; break; case PUBKEY_ALGO_RSA_E: ga = GCRY_PK_RSA_E; break; case PUBKEY_ALGO_RSA_S: ga = GCRY_PK_RSA_S; break; #else case PUBKEY_ALGO_RSA: break; case PUBKEY_ALGO_RSA_E: break; case PUBKEY_ALGO_RSA_S: break; #endif case PUBKEY_ALGO_ELGAMAL_E: ga = GCRY_PK_ELG; break; case PUBKEY_ALGO_DSA: ga = GCRY_PK_DSA; break; #ifdef GPG_USE_ECDH case PUBKEY_ALGO_ECDH: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDH: break; #endif #ifdef GPG_USE_ECDSA case PUBKEY_ALGO_ECDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_ECDSA: break; #endif #ifdef GPG_USE_EDDSA case PUBKEY_ALGO_EDDSA: ga = GCRY_PK_ECC; break; #else case PUBKEY_ALGO_EDDSA: break; #endif case PUBKEY_ALGO_ELGAMAL: /* Don't allow type 20 keys unless in rfc2440 mode. */ if (RFC2440) ga = GCRY_PK_ELG; break; default: break; } if (!ga) return gpg_error (GPG_ERR_PUBKEY_ALGO); /* Elgamal in OpenPGP used to support signing and Libgcrypt still * does. However, we removed the signing capability from gpg ages * ago. This function should reflect this so that errors are thrown * early and not only when we try to sign using Elgamal. */ if (ga == GCRY_PK_ELG && (use & (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG))) return gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO); /* Now check whether Libgcrypt has support for the algorithm. */ return gcry_pk_algo_info (ga, GCRYCTL_TEST_ALGO, NULL, &use_buf); } int openpgp_pk_algo_usage ( int algo ) { int use = 0; /* They are hardwired in gpg 1.0. */ switch ( algo ) { case PUBKEY_ALGO_RSA: use = (PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH); break; case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_ECDH: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_RSA_S: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG; break; case PUBKEY_ALGO_ELGAMAL: if (RFC2440) use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_ELGAMAL_E: use = PUBKEY_USAGE_ENC; break; case PUBKEY_ALGO_DSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; break; case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH; default: break; } return use; } /* Map the OpenPGP pubkey algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_pk_algo_name (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return "RSA"; case PUBKEY_ALGO_ELGAMAL: case PUBKEY_ALGO_ELGAMAL_E: return "ELG"; case PUBKEY_ALGO_DSA: return "DSA"; case PUBKEY_ALGO_ECDH: return "ECDH"; case PUBKEY_ALGO_ECDSA: return "ECDSA"; case PUBKEY_ALGO_EDDSA: return "EDDSA"; default: return "?"; } } /* Explicit mapping of OpenPGP digest algos to Libgcrypt. */ /* FIXME: We do not yes use it everywhere. */ enum gcry_md_algos map_md_openpgp_to_gcry (digest_algo_t algo) { switch (algo) { #ifdef GPG_USE_MD5 case DIGEST_ALGO_MD5: return GCRY_MD_MD5; #else case DIGEST_ALGO_MD5: return 0; #endif case DIGEST_ALGO_SHA1: return GCRY_MD_SHA1; #ifdef GPG_USE_RMD160 case DIGEST_ALGO_RMD160: return GCRY_MD_RMD160; #else case DIGEST_ALGO_RMD160: return 0; #endif #ifdef GPG_USE_SHA224 case DIGEST_ALGO_SHA224: return GCRY_MD_SHA224; #else case DIGEST_ALGO_SHA224: return 0; #endif case DIGEST_ALGO_SHA256: return GCRY_MD_SHA256; #ifdef GPG_USE_SHA384 case DIGEST_ALGO_SHA384: return GCRY_MD_SHA384; #else case DIGEST_ALGO_SHA384: return 0; #endif #ifdef GPG_USE_SHA512 case DIGEST_ALGO_SHA512: return GCRY_MD_SHA512; #else case DIGEST_ALGO_SHA512: return 0; #endif default: return 0; } } /* Return 0 if ALGO is suitable and implemented OpenPGP hash algorithm. */ int openpgp_md_test_algo (digest_algo_t algo) { enum gcry_md_algos ga; ga = map_md_openpgp_to_gcry (algo); if (!ga) return gpg_error (GPG_ERR_DIGEST_ALGO); return gcry_md_test_algo (ga); } /* Map the OpenPGP digest algorithm whose ID is contained in ALGO to a string representation of the algorithm name. For unknown algorithm IDs this function returns "?". */ const char * openpgp_md_algo_name (int algo) { switch (algo) { case DIGEST_ALGO_MD5: return "MD5"; case DIGEST_ALGO_SHA1: return "SHA1"; case DIGEST_ALGO_RMD160: return "RIPEMD160"; case DIGEST_ALGO_SHA256: return "SHA256"; case DIGEST_ALGO_SHA384: return "SHA384"; case DIGEST_ALGO_SHA512: return "SHA512"; case DIGEST_ALGO_SHA224: return "SHA224"; } return "?"; } static unsigned long get_signature_count (PKT_public_key *pk) { #ifdef ENABLE_CARD_SUPPORT struct agent_card_info_s info; (void)pk; if (!agent_scd_getattr ("SIG-COUNTER",&info)) return info.sig_counter; else return 0; #else (void)pk; return 0; #endif } /* Expand %-strings. Returns a string which must be xfreed. Returns NULL if the string cannot be expanded (too large). */ char * -pct_expando(const char *string,struct expando_args *args) +pct_expando (ctrl_t ctrl, const char *string,struct expando_args *args) { const char *ch=string; int idx=0,maxlen=0,done=0; u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0}; char *ret=NULL; if(args->pk) keyid_from_pk(args->pk,pk_keyid); if(args->pksk) keyid_from_pk (args->pksk, sk_keyid); /* This is used so that %k works in photoid command strings in --list-secret-keys (which of course has a sk, but no pk). */ if(!args->pk && args->pksk) keyid_from_pk (args->pksk, pk_keyid); while(*ch!='\0') { if(!done) { /* 8192 is way bigger than we'll need here */ if(maxlen>=8192) goto fail; maxlen+=1024; ret=xrealloc(ret,maxlen); } done=0; if(*ch=='%') { switch(*(ch+1)) { case 's': /* short key id */ if(idx+8namehash) { char *tmp = zb32_encode (args->namehash, 8*20); if (tmp) { if (idx + strlen (tmp) < maxlen) { strcpy (ret+idx, tmp); idx += strlen (tmp); } xfree (tmp); done = 1; } } break; case 'c': /* signature count from card, if any. */ if(idx+10pksk)); idx+=strlen(&ret[idx]); done=1; } break; case 'f': /* Fingerprint of key being signed */ case 'p': /* Fingerprint of the primary key making the signature. */ case 'g': /* Fingerprint of the key making the signature. */ { byte array[MAX_FINGERPRINT_LEN]; size_t len; int i; if ((*(ch+1))=='f' && args->pk) fingerprint_from_pk (args->pk, array, &len); else if ((*(ch+1))=='p' && args->pksk) { if(args->pksk->flags.primary) fingerprint_from_pk (args->pksk, array, &len); else if (args->pksk->main_keyid[0] || args->pksk->main_keyid[1]) { /* Not the primary key: Find the fingerprint of the primary key. */ PKT_public_key *pk= xmalloc_clear(sizeof(PKT_public_key)); - if (!get_pubkey_fast (pk,args->pksk->main_keyid)) + if (!get_pubkey_fast (ctrl, pk,args->pksk->main_keyid)) fingerprint_from_pk (pk, array, &len); else memset (array, 0, (len=MAX_FINGERPRINT_LEN)); free_public_key (pk); } else /* Oops: info about the primary key missing. */ memset(array,0,(len=MAX_FINGERPRINT_LEN)); } else if((*(ch+1))=='g' && args->pksk) fingerprint_from_pk (args->pksk, array, &len); else memset(array,0,(len=MAX_FINGERPRINT_LEN)); if(idx+(len*2)validity_info && idx+1validity_info; ret[idx]='\0'; done=1; } break; /* The text string types */ case 't': case 'T': case 'V': { const char *str=NULL; switch(*(ch+1)) { case 't': /* e.g. "jpg" */ str=image_type_to_string(args->imagetype,0); break; case 'T': /* e.g. "image/jpeg" */ str=image_type_to_string(args->imagetype,2); break; case 'V': /* e.g. "full", "expired", etc. */ str=args->validity_string; break; } if(str && idx+strlen(str) 2) result = 0; } else result = 0; return result; } /* * Wrapper around gcry_md_map_name to provide a fallback using the * "Hn" syntax as used by the preference strings. */ int string_to_digest_algo (const char *string) { int val; /* FIXME: We should make use of our wrapper function and not assume that there is a 1 to 1 mapping between OpenPGP and Libgcrypt. */ val = gcry_md_map_name (string); if (!val && string && (string[0]=='H' || string[0]=='h')) { char *endptr; string++; val = strtol (string, &endptr, 10); if (!*string || *endptr || openpgp_md_test_algo (val)) val = 0; } return val; } const char * compress_algo_to_string(int algo) { const char *s=NULL; switch(algo) { case COMPRESS_ALGO_NONE: s=_("Uncompressed"); break; case COMPRESS_ALGO_ZIP: s="ZIP"; break; case COMPRESS_ALGO_ZLIB: s="ZLIB"; break; #ifdef HAVE_BZIP2 case COMPRESS_ALGO_BZIP2: s="BZIP2"; break; #endif } return s; } int string_to_compress_algo(const char *string) { /* TRANSLATORS: See doc/TRANSLATE about this string. */ if(match_multistr(_("uncompressed|none"),string)) return 0; else if(ascii_strcasecmp(string,"uncompressed")==0) return 0; else if(ascii_strcasecmp(string,"none")==0) return 0; else if(ascii_strcasecmp(string,"zip")==0) return 1; else if(ascii_strcasecmp(string,"zlib")==0) return 2; #ifdef HAVE_BZIP2 else if(ascii_strcasecmp(string,"bzip2")==0) return 3; #endif else if(ascii_strcasecmp(string,"z0")==0) return 0; else if(ascii_strcasecmp(string,"z1")==0) return 1; else if(ascii_strcasecmp(string,"z2")==0) return 2; #ifdef HAVE_BZIP2 else if(ascii_strcasecmp(string,"z3")==0) return 3; #endif else return -1; } int check_compress_algo(int algo) { switch (algo) { case 0: return 0; #ifdef HAVE_ZIP case 1: case 2: return 0; #endif #ifdef HAVE_BZIP2 case 3: return 0; #endif default: return GPG_ERR_COMPR_ALGO; } } int default_cipher_algo(void) { if(opt.def_cipher_algo) return opt.def_cipher_algo; else if(opt.personal_cipher_prefs) return opt.personal_cipher_prefs[0].value; else return opt.s2k_cipher_algo; } aead_algo_t default_aead_algo(void) { if(opt.def_aead_algo) return opt.def_aead_algo; else if(opt.personal_aead_prefs) return opt.personal_aead_prefs[0].value; else return DEFAULT_AEAD_ALGO; } /* There is no default_digest_algo function, but see sign.c:hash_for() */ int default_compress_algo(void) { if(opt.compress_algo!=-1) return opt.compress_algo; else if(opt.personal_compress_prefs) return opt.personal_compress_prefs[0].value; else return DEFAULT_COMPRESS_ALGO; } void compliance_failure(void) { char *ver="???"; switch(opt.compliance) { case CO_GNUPG: ver="GnuPG"; break; case CO_RFC4880: ver="OpenPGP"; break; case CO_RFC2440: ver="OpenPGP (older)"; break; case CO_PGP7: ver="PGP 7.x"; break; case CO_PGP8: ver="PGP 8.x"; break; case CO_DE_VS: ver="DE-VS applications"; break; } log_info(_("this message may not be usable by %s\n"),ver); opt.compliance=CO_GNUPG; } /* Break a string into successive option pieces. Accepts single word options and key=value argument options. */ char * optsep(char **stringp) { char *tok,*end; tok=*stringp; if(tok) { end=strpbrk(tok," ,="); if(end) { int sawequals=0; char *ptr=end; /* what we need to do now is scan along starting with *end, If the next character we see (ignoring spaces) is an = sign, then there is an argument. */ while(*ptr) { if(*ptr=='=') sawequals=1; else if(*ptr!=' ') break; ptr++; } /* There is an argument, so grab that too. At this point, ptr points to the first character of the argument. */ if(sawequals) { /* Is it a quoted argument? */ if(*ptr=='"') { ptr++; end=strchr(ptr,'"'); if(end) end++; } else end=strpbrk(ptr," ,"); } if(end && *end) { *end='\0'; *stringp=end+1; } else *stringp=NULL; } else *stringp=NULL; } return tok; } /* Breaks an option value into key and value. Returns NULL if there is no value. Note that "string" is modified to remove the =value part. */ char * argsplit(char *string) { char *equals,*arg=NULL; equals=strchr(string,'='); if(equals) { char *quote,*space; *equals='\0'; arg=equals+1; /* Quoted arg? */ quote=strchr(arg,'"'); if(quote) { arg=quote+1; quote=strchr(arg,'"'); if(quote) *quote='\0'; } else { size_t spaces; /* Trim leading spaces off of the arg */ spaces=strspn(arg," "); arg+=spaces; } /* Trim tailing spaces off of the tag */ space=strchr(string,' '); if(space) *space='\0'; } return arg; } /* Return the length of the initial token, leaving off any argument. */ static size_t optlen(const char *s) { char *end=strpbrk(s," ="); if(end) return end-s; else return strlen(s); } /* Note: This function returns true on success. */ int parse_options(char *str,unsigned int *options, struct parse_options *opts,int noisy) { char *tok; if (str && !strcmp (str, "help")) { int i,maxlen=0; /* Figure out the longest option name so we can line these up neatly. */ for(i=0;opts[i].name;i++) if(opts[i].help && maxlen='A' && file[0]<='Z') || (file[0]>='a' && file[0]<='z')) && file[1]==':') #else || file[0]=='/' #endif ) return access(file,mode); else { /* At least as large as, but most often larger than we need. */ char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1); char *split,*item,*path=xstrdup(envpath); split=path; while((item=strsep(&split,PATHSEP_S))) { strcpy(buffer,item); strcat(buffer,"/"); strcat(buffer,file); ret=access(buffer,mode); if(ret==0) break; } xfree(path); xfree(buffer); } return ret; } /* Return the number of public key parameters as used by OpenPGP. */ int pubkey_get_npkey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 2; case PUBKEY_ALGO_ELGAMAL_E: return 3; case PUBKEY_ALGO_DSA: return 4; case PUBKEY_ALGO_ECDH: return 3; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 3; case PUBKEY_ALGO_EDDSA: return 2; default: return 0; } } /* Return the number of secret key parameters as used by OpenPGP. */ int pubkey_get_nskey (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 6; case PUBKEY_ALGO_ELGAMAL_E: return 4; case PUBKEY_ALGO_DSA: return 5; case PUBKEY_ALGO_ECDH: return 4; case PUBKEY_ALGO_ECDSA: return 3; case PUBKEY_ALGO_ELGAMAL: return 4; case PUBKEY_ALGO_EDDSA: return 3; default: return 0; } } /* Temporary helper. */ int pubkey_get_nsig (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 0; case PUBKEY_ALGO_DSA: return 2; case PUBKEY_ALGO_ECDH: return 0; case PUBKEY_ALGO_ECDSA: return 2; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 2; default: return 0; } } /* Temporary helper. */ int pubkey_get_nenc (pubkey_algo_t algo) { switch (algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: return 1; case PUBKEY_ALGO_ELGAMAL_E: return 2; case PUBKEY_ALGO_DSA: return 0; case PUBKEY_ALGO_ECDH: return 2; case PUBKEY_ALGO_ECDSA: return 0; case PUBKEY_ALGO_ELGAMAL: return 2; case PUBKEY_ALGO_EDDSA: return 0; default: return 0; } } /* Temporary helper. */ unsigned int pubkey_nbits( int algo, gcry_mpi_t *key ) { int rc, nbits; gcry_sexp_t sexp; if (algo == PUBKEY_ALGO_DSA && key[0] && key[1] && key[2] && key[3]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", key[0], key[1], key[2], key[3] ); } else if ((algo == PUBKEY_ALGO_ELGAMAL || algo == PUBKEY_ALGO_ELGAMAL_E) && key[0] && key[1] && key[2]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", key[0], key[1], key[2] ); } else if (is_RSA (algo) && key[0] && key[1]) { rc = gcry_sexp_build (&sexp, NULL, "(public-key(rsa(n%m)(e%m)))", key[0], key[1] ); } else if ((algo == PUBKEY_ALGO_ECDSA || algo == PUBKEY_ALGO_ECDH || algo == PUBKEY_ALGO_EDDSA) && key[0] && key[1]) { char *curve = openpgp_oid_to_str (key[0]); if (!curve) rc = gpg_error_from_syserror (); else { rc = gcry_sexp_build (&sexp, NULL, "(public-key(ecc(curve%s)(q%m)))", curve, key[1]); xfree (curve); } } else return 0; if (rc) BUG (); nbits = gcry_pk_get_nbits (sexp); gcry_sexp_release (sexp); return nbits; } int mpi_print (estream_t fp, gcry_mpi_t a, int mode) { int n = 0; size_t nwritten; if (!a) return es_fprintf (fp, "[MPI_NULL]"); if (!mode) { unsigned int n1; n1 = gcry_mpi_get_nbits(a); n += es_fprintf (fp, "[%u bits]", n1); } else if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) { unsigned int nbits; unsigned char *p = gcry_mpi_get_opaque (a, &nbits); if (!p) n += es_fprintf (fp, "[invalid opaque value]"); else { if (!es_write_hexstring (fp, p, (nbits + 7)/8, 0, &nwritten)) n += nwritten; } } else { unsigned char *buffer; size_t buflen; if (gcry_mpi_aprint (GCRYMPI_FMT_USG, &buffer, &buflen, a)) BUG (); if (!es_write_hexstring (fp, buffer, buflen, 0, &nwritten)) n += nwritten; gcry_free (buffer); } return n; } /* pkey[1] or skey[1] is Q for ECDSA, which is an uncompressed point, i.e. 04 */ unsigned int ecdsa_qbits_from_Q (unsigned int qbits) { if ((qbits%8) > 3) { log_error (_("ECDSA public key is expected to be in SEC encoding " "multiple of 8 bits\n")); return 0; } qbits -= qbits%8; qbits /= 2; return qbits; } /* Ignore signatures and certifications made over certain digest * algorithms by default, MD5 is considered weak. This allows users * to deprecate support for other algorithms as well. */ void additional_weak_digest (const char* digestname) { struct weakhash *weak = NULL; const enum gcry_md_algos algo = string_to_digest_algo(digestname); if (algo == GCRY_MD_NONE) { log_error (_("unknown weak digest '%s'\n"), digestname); return; } /* Check to ensure it's not already present. */ for (weak = opt.weak_digests; weak; weak = weak->next) if (algo == weak->algo) return; /* Add it to the head of the list. */ weak = xmalloc(sizeof(*weak)); weak->algo = algo; weak->rejection_shown = 0; weak->next = opt.weak_digests; opt.weak_digests = weak; } diff --git a/g10/options.h b/g10/options.h index 234929b15..bfa2cc31e 100644 --- a/g10/options.h +++ b/g10/options.h @@ -1,414 +1,417 @@ /* options.h * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2010, 2011 Free Software Foundation, Inc. * Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #ifndef G10_OPTIONS_H #define G10_OPTIONS_H #include #include "../common/types.h" #include #include "main.h" #include "packet.h" #include "tofu.h" #include "../common/session-env.h" #include "../common/compliance.h" #ifndef EXTERN_UNLESS_MAIN_MODULE /* Norcraft can't cope with common symbols */ #if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE) #define EXTERN_UNLESS_MAIN_MODULE extern #else #define EXTERN_UNLESS_MAIN_MODULE #endif #endif /* Declaration of a keyserver spec type. The definition is found in ../common/keyserver.h. */ struct keyserver_spec; typedef struct keyserver_spec *keyserver_spec_t; /* Global options for GPG. */ EXTERN_UNLESS_MAIN_MODULE struct { int verbose; int quiet; unsigned debug; int armor; char *outfile; estream_t outfp; /* Hack, sometimes used in place of outfile. */ off_t max_output; /* If > 0 a hint with the expected number of input data bytes. This * is not necessary an exact number but intended to be used for * progress info and to decide on how to allocate buffers. */ uint64_t input_size_hint; /* The AEAD chunk size expressed as a power of 2. */ int chunk_size; int dry_run; int autostart; int list_only; int mimemode; int textmode; int expert; const char *def_sig_expire; int ask_sig_expire; const char *def_cert_expire; int ask_cert_expire; int batch; /* run in batch mode */ int answer_yes; /* answer yes on most questions */ int answer_no; /* answer no on most questions */ int check_sigs; /* check key signatures */ int with_colons; int with_key_data; int with_icao_spelling; /* Print ICAO spelling with fingerprints. */ int with_fingerprint; /* Option --with-fingerprint active. */ int with_subkey_fingerprint; /* Option --with-subkey-fingerprint active. */ int with_keygrip; /* Option --with-keygrip active. */ int with_key_screening;/* Option --with-key-screening active. */ int with_tofu_info; /* Option --with-tofu_info active. */ int with_secret; /* Option --with-secret active. */ int with_wkd_hash; /* Option --with-wkd-hash. */ int with_key_origin; /* Option --with-key-origin. */ int fingerprint; /* list fingerprints */ int list_sigs; /* list signatures */ int no_armor; int list_packets; /* Option --list-packets active. */ int def_cipher_algo; int def_aead_algo; int force_mdc; int disable_mdc; int force_aead; int def_digest_algo; int cert_digest_algo; int compress_algo; int compress_level; int bz2_compress_level; int bz2_decompress_lowmem; strlist_t def_secret_key; char *def_recipient; int def_recipient_self; strlist_t secret_keys_to_try; /* A list of mail addresses (addr-spec) provided by the user with * the option --sender. */ strlist_t sender_list; int def_cert_level; int min_cert_level; int ask_cert_level; int emit_version; /* 0 = none, 1 = major only, 2 = major and minor, 3 = full version, 4 = full version plus OS string. */ int marginals_needed; int completes_needed; int max_cert_depth; const char *agent_program; + const char *keyboxd_program; const char *dirmngr_program; int disable_dirmngr; const char *def_new_key_algo; /* Options to be passed to the gpg-agent */ session_env_t session_env; char *lc_ctype; char *lc_messages; int skip_verify; int skip_hidden_recipients; /* TM_CLASSIC must be zero to accommodate trustdbsg generated before we started storing the trust model inside the trustdb. */ enum { TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2, TM_ALWAYS, TM_DIRECT, TM_AUTO, TM_TOFU, TM_TOFU_PGP } trust_model; enum tofu_policy tofu_default_policy; int force_ownertrust; enum gnupg_compliance_mode compliance; enum { KF_DEFAULT, KF_NONE, KF_SHORT, KF_LONG, KF_0xSHORT, KF_0xLONG } keyid_format; const char *set_filename; strlist_t comments; int throw_keyids; const char *photo_viewer; int s2k_mode; int s2k_digest_algo; int s2k_cipher_algo; unsigned char s2k_count; /* This is the encoded form, not the raw count */ int not_dash_escaped; int escape_from; int lock_once; keyserver_spec_t keyserver; /* The list of configured keyservers. */ struct { unsigned int options; unsigned int import_options; unsigned int export_options; char *http_proxy; } keyserver_options; int exec_disable; int exec_path_set; unsigned int import_options; unsigned int export_options; unsigned int list_options; unsigned int verify_options; const char *def_preference_list; const char *def_keyserver_url; prefitem_t *personal_cipher_prefs; prefitem_t *personal_aead_prefs; prefitem_t *personal_digest_prefs; prefitem_t *personal_compress_prefs; struct weakhash *weak_digests; int no_perm_warn; char *temp_dir; int no_encrypt_to; int encrypt_to_default_key; int interactive; struct notation *sig_notations; struct notation *cert_notations; strlist_t sig_policy_url; strlist_t cert_policy_url; strlist_t sig_keyserver_url; strlist_t cert_subpackets; strlist_t sig_subpackets; int allow_non_selfsigned_uid; int allow_freeform_uid; int no_literal; ulong set_filesize; int fast_list_mode; int legacy_list_mode; int ignore_time_conflict; int ignore_valid_from; int ignore_crc_error; int ignore_mdc_error; int command_fd; const char *override_session_key; int show_session_key; const char *gpg_agent_info; int try_all_secrets; int no_expensive_trust_checks; int no_sig_cache; int no_auto_check_trustdb; int preserve_permissions; int no_homedir_creation; struct groupitem *grouplist; int mangle_dos_filenames; int enable_progress_filter; unsigned int screen_columns; unsigned int screen_lines; byte *show_subpackets; int rfc2440_text; /* If true, let write failures on the status-fd exit the process. */ int exit_on_status_write_error; /* If > 0, limit the number of card insertion prompts to this value. */ int limit_card_insert_tries; struct { /* If set, require an 0x19 backsig to be present on signatures made by signing subkeys. If not set, a missing backsig is not an error (but an invalid backsig still is). */ unsigned int require_cross_cert:1; unsigned int use_embedded_filename:1; unsigned int utf8_filename:1; unsigned int dsa2:1; unsigned int allow_weak_digest_algos:1; unsigned int large_rsa:1; unsigned int disable_signer_uid:1; /* Flag to enable experimental features from RFC4880bis. */ unsigned int rfc4880bis:1; /* Hack: --output is not given but OUTFILE was temporary set to "-". */ unsigned int dummy_outfile:1; } flags; /* Linked list of ways to find a key if the key isn't on the local keyring. */ struct akl { enum { AKL_NODEFAULT, AKL_LOCAL, AKL_CERT, AKL_PKA, AKL_DANE, AKL_WKD, AKL_LDAP, AKL_KEYSERVER, AKL_SPEC } type; keyserver_spec_t spec; struct akl *next; } *auto_key_locate; /* The value of --key-origin. See parse_key_origin(). */ int key_origin; char *key_origin_url; int passphrase_repeat; int pinentry_mode; int request_origin; int unwrap_encryption; int only_sign_text_ids; int no_symkey_cache; /* Disable the cache used for --symmetric. */ + + int use_keyboxd; /* Use the external keyboxd as storage backend. */ } opt; /* CTRL is used to keep some global variables we currently can't avoid. Future concurrent versions of gpg will put it into a per request structure CTRL. */ EXTERN_UNLESS_MAIN_MODULE struct { int in_auto_key_retrieve; /* True if we are doing an auto_key_retrieve. */ /* Hack to store the last error. We currently need it because the proc_packet machinery is not able to reliabale return error codes. Thus for the --server purposes we store some of the error codes here. FIXME! */ gpg_error_t lasterr; } glo_ctrl; #define DBG_PACKET_VALUE 1 /* debug packet reading/writing */ #define DBG_MPI_VALUE 2 /* debug mpi details */ #define DBG_CRYPTO_VALUE 4 /* debug crypto handling */ /* (may reveal sensitive data) */ #define DBG_FILTER_VALUE 8 /* debug internal filter handling */ #define DBG_IOBUF_VALUE 16 /* debug iobuf stuff */ #define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */ #define DBG_CACHE_VALUE 64 /* debug the caching */ #define DBG_MEMSTAT_VALUE 128 /* show memory statistics */ #define DBG_TRUST_VALUE 256 /* debug the trustdb */ #define DBG_HASHING_VALUE 512 /* debug hashing operations */ #define DBG_IPC_VALUE 1024 /* debug assuan communication */ #define DBG_CLOCK_VALUE 4096 #define DBG_LOOKUP_VALUE 8192 /* debug the key lookup */ #define DBG_EXTPROG_VALUE 16384 /* debug external program calls */ /* Tests for the debugging flags. */ #define DBG_PACKET (opt.debug & DBG_PACKET_VALUE) #define DBG_MPI (opt.debug & DBG_MPI_VALUE) #define DBG_CRYPTO (opt.debug & DBG_CRYPTO_VALUE) #define DBG_FILTER (opt.debug & DBG_FILTER_VALUE) #define DBG_CACHE (opt.debug & DBG_CACHE_VALUE) #define DBG_TRUST (opt.debug & DBG_TRUST_VALUE) #define DBG_HASHING (opt.debug & DBG_HASHING_VALUE) #define DBG_IPC (opt.debug & DBG_IPC_VALUE) #define DBG_CLOCK (opt.debug & DBG_CLOCK_VALUE) #define DBG_LOOKUP (opt.debug & DBG_LOOKUP_VALUE) #define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE) /* FIXME: We need to check why we did not put this into opt. */ #define DBG_MEMORY memory_debug_mode #define DBG_MEMSTAT memory_stat_debug_mode EXTERN_UNLESS_MAIN_MODULE int memory_debug_mode; EXTERN_UNLESS_MAIN_MODULE int memory_stat_debug_mode; /* Compatibility flags. */ #define GNUPG (opt.compliance==CO_GNUPG || opt.compliance==CO_DE_VS) #define RFC2440 (opt.compliance==CO_RFC2440) #define RFC4880 (opt.compliance==CO_RFC4880) #define PGP7 (opt.compliance==CO_PGP7) #define PGP8 (opt.compliance==CO_PGP8) #define PGPX (PGP7 || PGP8) /* Various option flags. Note that there should be no common string names between the IMPORT_ and EXPORT_ flags as they can be mixed in the keyserver-options option. */ #define IMPORT_LOCAL_SIGS (1<<0) #define IMPORT_REPAIR_PKS_SUBKEY_BUG (1<<1) #define IMPORT_FAST (1<<2) #define IMPORT_SHOW (1<<3) #define IMPORT_MERGE_ONLY (1<<4) #define IMPORT_MINIMAL (1<<5) #define IMPORT_CLEAN (1<<6) #define IMPORT_NO_SECKEY (1<<7) #define IMPORT_KEEP_OWNERTTRUST (1<<8) #define IMPORT_EXPORT (1<<9) #define IMPORT_RESTORE (1<<10) #define IMPORT_REPAIR_KEYS (1<<11) #define IMPORT_DRY_RUN (1<<12) #define IMPORT_DROP_UIDS (1<<13) #define IMPORT_SELF_SIGS_ONLY (1<<14) #define EXPORT_LOCAL_SIGS (1<<0) #define EXPORT_ATTRIBUTES (1<<1) #define EXPORT_SENSITIVE_REVKEYS (1<<2) #define EXPORT_RESET_SUBKEY_PASSWD (1<<3) #define EXPORT_MINIMAL (1<<4) #define EXPORT_CLEAN (1<<5) #define EXPORT_PKA_FORMAT (1<<6) #define EXPORT_DANE_FORMAT (1<<7) #define EXPORT_BACKUP (1<<10) #define EXPORT_DROP_UIDS (1<<13) #define LIST_SHOW_PHOTOS (1<<0) #define LIST_SHOW_POLICY_URLS (1<<1) #define LIST_SHOW_STD_NOTATIONS (1<<2) #define LIST_SHOW_USER_NOTATIONS (1<<3) #define LIST_SHOW_NOTATIONS (LIST_SHOW_STD_NOTATIONS|LIST_SHOW_USER_NOTATIONS) #define LIST_SHOW_KEYSERVER_URLS (1<<4) #define LIST_SHOW_UID_VALIDITY (1<<5) #define LIST_SHOW_UNUSABLE_UIDS (1<<6) #define LIST_SHOW_UNUSABLE_SUBKEYS (1<<7) #define LIST_SHOW_KEYRING (1<<8) #define LIST_SHOW_SIG_EXPIRE (1<<9) #define LIST_SHOW_SIG_SUBPACKETS (1<<10) #define LIST_SHOW_USAGE (1<<11) #define LIST_SHOW_ONLY_FPR_MBOX (1<<12) #define VERIFY_SHOW_PHOTOS (1<<0) #define VERIFY_SHOW_POLICY_URLS (1<<1) #define VERIFY_SHOW_STD_NOTATIONS (1<<2) #define VERIFY_SHOW_USER_NOTATIONS (1<<3) #define VERIFY_SHOW_NOTATIONS (VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_USER_NOTATIONS) #define VERIFY_SHOW_KEYSERVER_URLS (1<<4) #define VERIFY_SHOW_UID_VALIDITY (1<<5) #define VERIFY_SHOW_UNUSABLE_UIDS (1<<6) #define VERIFY_PKA_LOOKUPS (1<<7) #define VERIFY_PKA_TRUST_INCREASE (1<<8) #define VERIFY_SHOW_PRIMARY_UID_ONLY (1<<9) #define KEYSERVER_HTTP_PROXY (1<<0) #define KEYSERVER_TIMEOUT (1<<1) #define KEYSERVER_ADD_FAKE_V3 (1<<2) #define KEYSERVER_AUTO_KEY_RETRIEVE (1<<3) #define KEYSERVER_HONOR_KEYSERVER_URL (1<<4) #define KEYSERVER_HONOR_PKA_RECORD (1<<5) #endif /*G10_OPTIONS_H*/ diff --git a/g10/photoid.c b/g10/photoid.c index f9720d329..9bb4ab58f 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -1,392 +1,392 @@ /* photoid.c - photo ID handling code * Copyright (C) 2001, 2002, 2005, 2006, 2008, 2011 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #ifdef _WIN32 # ifdef HAVE_WINSOCK2_H # include # endif # include # ifndef VER_PLATFORM_WIN32_WINDOWS # define VER_PLATFORM_WIN32_WINDOWS 1 # endif #endif #include "gpg.h" #include "../common/util.h" #include "packet.h" #include "../common/status.h" #include "exec.h" #include "keydb.h" #include "../common/i18n.h" #include "../common/iobuf.h" #include "options.h" #include "main.h" #include "photoid.h" #include "../common/ttyio.h" #include "trustdb.h" /* Generate a new photo id packet, or return NULL if canceled. FIXME: Should we add a duplicates check similar to generate_user_id? */ PKT_user_id * generate_photo_id (ctrl_t ctrl, PKT_public_key *pk,const char *photo_name) { PKT_user_id *uid; int error=1,i; unsigned int len; char *filename; byte *photo=NULL; byte header[16]; IOBUF file; int overflow; header[0]=0x10; /* little side of photo header length */ header[1]=0; /* big side of photo header length */ header[2]=1; /* 1 == version of photo header */ header[3]=1; /* 1 == JPEG */ for(i=4;i<16;i++) /* The reserved bytes */ header[i]=0; #define EXTRA_UID_NAME_SPACE 71 uid=xmalloc_clear(sizeof(*uid)+71); if(photo_name && *photo_name) filename=make_filename(photo_name,(void *)NULL); else { tty_printf(_("\nPick an image to use for your photo ID." " The image must be a JPEG file.\n" "Remember that the image is stored within your public key." " If you use a\n" "very large picture, your key will become very large" " as well!\n" "Keeping the image close to 240x288 is a good size" " to use.\n")); filename=NULL; } while(photo==NULL) { if(filename==NULL) { char *tempname; tty_printf("\n"); tty_enable_completion(NULL); tempname=cpr_get("photoid.jpeg.add", _("Enter JPEG filename for photo ID: ")); tty_disable_completion(); filename=make_filename(tempname,(void *)NULL); xfree(tempname); if(strlen(filename)==0) goto scram; } file=iobuf_open(filename); if (file && is_secured_file (iobuf_get_fd (file))) { iobuf_close (file); file = NULL; gpg_err_set_errno (EPERM); } if(!file) { log_error(_("unable to open JPEG file '%s': %s\n"), filename,strerror(errno)); xfree(filename); filename=NULL; continue; } len=iobuf_get_filelength(file, &overflow); if(len>6144 || overflow) { tty_printf( _("This JPEG is really large (%d bytes) !\n"),len); if(!cpr_get_answer_is_yes("photoid.jpeg.size", _("Are you sure you want to use it? (y/N) "))) { iobuf_close(file); xfree(filename); filename=NULL; continue; } } photo=xmalloc(len); iobuf_read(file,photo,len); iobuf_close(file); /* Is it a JPEG? */ if(photo[0]!=0xFF || photo[1]!=0xD8) { log_error(_("'%s' is not a JPEG file\n"),filename); xfree(photo); photo=NULL; xfree(filename); filename=NULL; continue; } /* Build the packet */ build_attribute_subpkt(uid,1,photo,len,header,16); parse_attribute_subpkts(uid); make_attribute_uidname(uid, EXTRA_UID_NAME_SPACE); /* Showing the photo is not safe when noninteractive since the "user" may not be able to dismiss a viewer window! */ if(opt.command_fd==-1) { show_photos (ctrl, uid->attribs, uid->numattribs, pk, uid); switch(cpr_get_answer_yes_no_quit("photoid.jpeg.okay", _("Is this photo correct (y/N/q)? "))) { case -1: goto scram; case 0: free_attributes(uid); xfree(photo); photo=NULL; xfree(filename); filename=NULL; continue; } } } error=0; uid->ref=1; scram: xfree(filename); xfree(photo); if(error) { free_attributes(uid); xfree(uid); return NULL; } return uid; } /* Returns 0 for error, 1 for valid */ int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len) { u16 headerlen; if(attr->len<3) return 0; /* For historical reasons (i.e. "oops!"), the header length is little endian. */ headerlen=(attr->data[1]<<8) | attr->data[0]; if(headerlen>attr->len) return 0; if(type && attr->len>=4) { if(attr->data[2]==1) /* header version 1 */ *type=attr->data[3]; else *type=0; } *len=attr->len-headerlen; if(*len==0) return 0; return 1; } /* style==0 for extension, 1 for name, 2 for MIME type. Remember that the "name" style string could be used in a user ID name field, so make sure it is not too big (see parse-packet.c:parse_attribute). Extensions should be 3 characters long for the best cross-platform compatibility. */ char *image_type_to_string(byte type,int style) { char *string; switch(type) { case 1: /* jpeg */ if(style==0) string="jpg"; else if(style==1) string="jpeg"; else string="image/jpeg"; break; default: if(style==0) string="bin"; else if(style==1) string="unknown"; else string="image/x-unknown"; break; } return string; } #if !defined(FIXED_PHOTO_VIEWER) && !defined(DISABLE_PHOTO_VIEWER) static const char * get_default_photo_command(void) { #if defined(_WIN32) OSVERSIONINFO osvi; memset(&osvi,0,sizeof(osvi)); osvi.dwOSVersionInfoSize=sizeof(osvi); GetVersionEx(&osvi); if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS) return "start /w %i"; else return "!ShellExecute 400 %i"; #elif defined(__APPLE__) /* OS X. This really needs more than just __APPLE__. */ return "open %I"; #elif defined(__riscos__) return "Filer_Run %I"; #else if (!path_access ("xloadimage", X_OK)) return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin"; else if (!path_access ("display",X_OK)) return "display -title 'KeyID 0x%k' %i"; else if (getuid () && !path_access ("xdg-open", X_OK)) return "xdg-open %i"; else return "/bin/true"; #endif } #endif void show_photos (ctrl_t ctrl, const struct user_attribute *attrs, int count, PKT_public_key *pk, PKT_user_id *uid) { #ifdef DISABLE_PHOTO_VIEWER (void)attrs; (void)count; (void)pk; (void)uid; #else /*!DISABLE_PHOTO_VIEWER*/ int i; struct expando_args args; u32 len; u32 kid[2]={0,0}; memset (&args, 0, sizeof(args)); args.pk = pk; args.validity_info = get_validity_info (ctrl, NULL, pk, uid); args.validity_string = get_validity_string (ctrl, pk, uid); namehash_from_uid (uid); args.namehash = uid->namehash; if (pk) keyid_from_pk (pk, kid); es_fflush (es_stdout); for(i=0;itempfile_in, image_type_to_string(args.imagetype,2)); #endif xfree(name); fwrite(&attrs[i].data[offset],attrs[i].len-offset,1,spawn->tochild); if(exec_read(spawn)!=0) { exec_finish(spawn); goto fail; } if(exec_finish(spawn)!=0) goto fail; } return; fail: log_error(_("unable to display photo ID!\n")); #endif /*!DISABLE_PHOTO_VIEWER*/ } diff --git a/g10/revoke.c b/g10/revoke.c index 0a93c31f9..0e39eca61 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -1,900 +1,900 @@ /* revoke.c - Create recovation certificates. * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, * 2004 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "../common/ttyio.h" #include "../common/i18n.h" #include "call-agent.h" struct revocation_reason_info { int code; char *desc; }; int revocation_reason_build_cb( PKT_signature *sig, void *opaque ) { struct revocation_reason_info *reason = opaque; char *ud = NULL; byte *buffer; size_t buflen = 1; if(!reason) return 0; if( reason->desc ) { ud = native_to_utf8( reason->desc ); buflen += strlen(ud); } buffer = xmalloc( buflen ); *buffer = reason->code; if( ud ) { memcpy(buffer+1, ud, strlen(ud) ); xfree( ud ); } build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen ); xfree( buffer ); return 0; } /* Outputs a minimal pk (as defined by 2440) from a keyblock. A minimal pk consists of the public key packet and a user ID. We try and pick a user ID that has a uid signature, and include it if possible. */ static int export_minimal_pk(IOBUF out,KBNODE keyblock, PKT_signature *revsig,PKT_signature *revkey) { KBNODE node; PACKET pkt; PKT_user_id *uid=NULL; PKT_signature *selfsig=NULL; u32 keyid[2]; int rc; node=find_kbnode(keyblock,PKT_PUBLIC_KEY); if(!node) { log_error("key incomplete\n"); return GPG_ERR_GENERAL; } keyid_from_pk(node->pkt->pkt.public_key,keyid); pkt=*node->pkt; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } init_packet(&pkt); pkt.pkttype=PKT_SIGNATURE; /* the revocation itself, if any. 2440 likes this to come first. */ if(revsig) { pkt.pkt.signature=revsig; rc=build_packet(out,&pkt); if(rc) { log_error("build_packet failed: %s\n", gpg_strerror (rc) ); return rc; } } /* If a revkey in a 1F sig is present, include it too */ if(revkey) { pkt.pkt.signature=revkey; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } } while(!selfsig) { KBNODE signode; node=find_next_kbnode(node,PKT_USER_ID); if(!node) { /* We're out of user IDs - none were self-signed. */ if(uid) break; else { log_error(_("key %s has no user IDs\n"),keystr(keyid)); return GPG_ERR_GENERAL; } } if(node->pkt->pkt.user_id->attrib_data) continue; uid=node->pkt->pkt.user_id; signode=node; while((signode=find_next_kbnode(signode,PKT_SIGNATURE))) { if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && keyid[1]==signode->pkt->pkt.signature->keyid[1] && IS_UID_SIG(signode->pkt->pkt.signature)) { selfsig=signode->pkt->pkt.signature; break; } } } pkt.pkttype=PKT_USER_ID; pkt.pkt.user_id=uid; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } if(selfsig) { pkt.pkttype=PKT_SIGNATURE; pkt.pkt.signature=selfsig; rc=build_packet(out,&pkt); if(rc) { log_error(_("build_packet failed: %s\n"), gpg_strerror (rc) ); return rc; } } return 0; } /**************** * Generate a revocation certificate for UNAME via a designated revoker */ int gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) { int rc = 0; armor_filter_context_t *afx; PKT_public_key *pk = NULL; PKT_public_key *pk2 = NULL; PKT_signature *sig = NULL; IOBUF out = NULL; struct revocation_reason_info *reason = NULL; KEYDB_HANDLE kdbhd; KEYDB_SEARCH_DESC desc; KBNODE keyblock=NULL,node; u32 keyid[2]; int i,any=0; SK_LIST sk_list=NULL; if( opt.batch ) { log_error(_("can't do this in batch mode\n")); return GPG_ERR_GENERAL; } afx = new_armor_context (); - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) { rc = gpg_error_from_syserror (); goto leave; } rc = classify_user_id (uname, &desc, 1); if (!rc) rc = keydb_search (kdbhd, &desc, 1, NULL); if (rc) { log_error (_("key \"%s\" not found: %s\n"),uname, gpg_strerror (rc)); goto leave; } rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); goto leave; } /* To parse the revkeys */ merge_keys_and_selfsig (ctrl, keyblock); /* get the key from the keyblock */ node = find_kbnode( keyblock, PKT_PUBLIC_KEY ); if( !node ) BUG (); pk=node->pkt->pkt.public_key; keyid_from_pk(pk,keyid); if(locusr) { rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_CERT); if(rc) goto leave; } /* Are we a designated revoker for this key? */ if(!pk->revkey && pk->numrevkeys) BUG(); for(i=0;inumrevkeys;i++) { SK_LIST list; free_public_key (pk2); pk2 = NULL; if(sk_list) { for(list=sk_list;list;list=list->next) { byte fpr[MAX_FINGERPRINT_LEN]; size_t fprlen; fingerprint_from_pk (list->pk, fpr, &fprlen); /* Don't get involved with keys that don't have a v4 * or v5 fingerprint */ if (fprlen != 20 && fprlen != 32) continue; if (!memcmp(fpr,pk->revkey[i].fpr, fprlen)) break; } if (list) pk2 = copy_public_key (NULL, list->pk); else continue; } else { pk2 = xmalloc_clear (sizeof *pk2); rc = get_pubkey_byfprint (ctrl, pk2, NULL, pk->revkey[i].fpr, pk->revkey[i].fprlen); } /* We have the revocation key. */ if(!rc) { PKT_signature *revkey = NULL; any = 1; print_key_info (ctrl, NULL, 0, pk, 0); tty_printf ("\n"); tty_printf (_("To be revoked by:\n")); print_key_info (ctrl, NULL, 0, pk2, 1); if(pk->revkey[i].class&0x40) tty_printf(_("(This is a sensitive revocation key)\n")); tty_printf("\n"); rc = agent_probe_secret_key (ctrl, pk2); if (rc) { tty_printf (_("Secret key is not available.\n")); continue; } if( !cpr_get_answer_is_yes("gen_desig_revoke.okay", _("Create a designated revocation certificate for this key? (y/N) "))) continue; /* get the reason for the revocation (this is always v4) */ reason = ask_revocation_reason( 1, 0, 1 ); if( !reason ) continue; if( !opt.armor ) tty_printf(_("ASCII armored output forced.\n")); if( (rc = open_outfile (-1, NULL, 0, 1, &out )) ) goto leave; afx->what = 1; afx->hdrlines = "Comment: A designated revocation certificate" " should follow\n"; push_armor_filter (afx, out); /* create it */ rc = make_keysig_packet (ctrl, &sig, pk, NULL, NULL, pk2, 0x20, 0, 0, revocation_reason_build_cb, reason, NULL); if( rc ) { log_error(_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); goto leave; } /* Spit out a minimal pk as well, since otherwise there is no way to know which key to attach this revocation to. Also include the direct key signature that contains this revocation key. We're allowed to include sensitive revocation keys along with a revocation, as this may be the only time the recipient has seen it. Note that this means that if we have multiple different sensitive revocation keys in a given direct key signature, we're going to include them all here. This is annoying, but the good outweighs the bad, since without including this a sensitive revoker can't really do their job. People should not include multiple sensitive revocation keys in one signature: 2440 says "Note that it may be appropriate to isolate this subpacket within a separate signature so that it is not combined with other subpackets that need to be exported." -dms */ while(!revkey) { KBNODE signode; signode=find_next_kbnode(node,PKT_SIGNATURE); if(!signode) break; node=signode; if(keyid[0]==signode->pkt->pkt.signature->keyid[0] && keyid[1]==signode->pkt->pkt.signature->keyid[1] && IS_KEY_SIG(signode->pkt->pkt.signature)) { int j; for(j=0;jpkt->pkt.signature->numrevkeys;j++) { if (pk->revkey[i].class == signode->pkt->pkt.signature->revkey[j].class && pk->revkey[i].algid == signode->pkt->pkt.signature->revkey[j].algid && pk->revkey[i].fprlen == signode->pkt->pkt.signature->revkey[j].fprlen && !memcmp (pk->revkey[i].fpr, signode->pkt->pkt.signature->revkey[j].fpr, pk->revkey[i].fprlen)) { revkey = signode->pkt->pkt.signature; break; } } } } if(!revkey) BUG(); rc=export_minimal_pk(out,keyblock,sig,revkey); if(rc) goto leave; /* and issue a usage notice */ tty_printf(_("Revocation certificate created.\n")); break; } } if(!any) log_error(_("no revocation keys found for \"%s\"\n"),uname); leave: free_public_key (pk); free_public_key (pk2); if( sig ) free_seckey_enc( sig ); release_sk_list(sk_list); if( rc ) iobuf_cancel(out); else iobuf_close(out); release_revocation_reason_info( reason ); release_armor_context (afx); return rc; } /* Common core to create the revocation. FILENAME may be NULL to write to stdout or the filename given by --output. REASON describes the revocation reason. PSK is the public primary key - we expect that a corresponding secret key is available. KEYBLOCK is the entire KEYBLOCK which is used in PGP mode to write a minimal key and not just the naked revocation signature; it may be NULL. If LEADINTEXT is not NULL, it is written right before the (armored) output.*/ static int create_revocation (ctrl_t ctrl, const char *filename, struct revocation_reason_info *reason, PKT_public_key *psk, kbnode_t keyblock, const char *leadintext, int suffix, const char *cache_nonce) { int rc; iobuf_t out = NULL; armor_filter_context_t *afx; PKT_signature *sig = NULL; PACKET pkt; afx = new_armor_context (); if ((rc = open_outfile (-1, filename, suffix, 1, &out))) goto leave; if (leadintext ) iobuf_writestr (out, leadintext); afx->what = 1; afx->hdrlines = "Comment: This is a revocation certificate\n"; push_armor_filter (afx, out); rc = make_keysig_packet (ctrl, &sig, psk, NULL, NULL, psk, 0x20, 0, 0, revocation_reason_build_cb, reason, cache_nonce); if (rc) { log_error (_("make_keysig_packet failed: %s\n"), gpg_strerror (rc)); goto leave; } if (keyblock && (PGP7 || PGP8)) { /* Use a minimal pk for PGPx mode, since PGP can't import bare revocation certificates. */ rc = export_minimal_pk (out, keyblock, sig, NULL); if (rc) goto leave; } else { init_packet (&pkt); pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; rc = build_packet (out, &pkt); if (rc) { log_error (_("build_packet failed: %s\n"), gpg_strerror (rc)); goto leave; } } leave: if (sig) free_seckey_enc (sig); if (rc) iobuf_cancel (out); else iobuf_close (out); release_armor_context (afx); return rc; } /* This function is used to generate a standard revocation certificate by gpg's interactive key generation function. The certificate is stored at a dedicated place in a slightly modified form to avoid an accidental import. PSK is the primary key; a corresponding secret key must be available. CACHE_NONCE is optional but can be used to help gpg-agent to avoid an extra passphrase prompt. */ int gen_standard_revoke (ctrl_t ctrl, PKT_public_key *psk, const char *cache_nonce) { int rc; estream_t memfp; struct revocation_reason_info reason; char *dir, *tmpstr, *fname; void *leadin; size_t len; u32 keyid[2]; int kl; char *orig_codeset; char *old_outfile; dir = get_openpgp_revocdir (gnupg_homedir ()); tmpstr = hexfingerprint (psk, NULL, 0); if (!tmpstr) { rc = gpg_error_from_syserror (); xfree (dir); return rc; } fname = strconcat (dir, DIRSEP_S, tmpstr, NULL); if (!fname) { rc = gpg_error_from_syserror (); xfree (tmpstr); xfree (dir); return rc; } xfree (tmpstr); xfree (dir); keyid_from_pk (psk, keyid); memfp = es_fopenmem (0, "r+"); if (!memfp) log_fatal ("error creating memory stream\n"); orig_codeset = i18n_switchto_utf8 (); es_fprintf (memfp, "%s\n\n", _("This is a revocation certificate for the OpenPGP key:")); print_key_line (ctrl, memfp, psk, 0); if (opt.keyid_format != KF_NONE) print_fingerprint (ctrl, memfp, psk, 3); kl = opt.keyid_format == KF_NONE? 0 : keystrlen (); tmpstr = get_user_id (ctrl, keyid, &len, NULL); es_fprintf (memfp, "uid%*s%.*s\n\n", kl + 10, "", (int)len, tmpstr); xfree (tmpstr); es_fprintf (memfp, "%s\n\n%s\n\n%s\n\n:", _("A revocation certificate is a kind of \"kill switch\" to publicly\n" "declare that a key shall not anymore be used. It is not possible\n" "to retract such a revocation certificate once it has been published."), _("Use it to revoke this key in case of a compromise or loss of\n" "the secret key. However, if the secret key is still accessible,\n" "it is better to generate a new revocation certificate and give\n" "a reason for the revocation. For details see the description of\n" "of the gpg command \"--generate-revocation\" in the " "GnuPG manual."), _("To avoid an accidental use of this file, a colon has been inserted\n" "before the 5 dashes below. Remove this colon with a text editor\n" "before importing and publishing this revocation certificate.")); es_putc (0, memfp); i18n_switchback (orig_codeset); if (es_fclose_snatch (memfp, &leadin, NULL)) log_fatal ("error snatching memory stream\n"); reason.code = 0x00; /* No particular reason. */ reason.desc = NULL; old_outfile = opt.outfile; opt.outfile = NULL; rc = create_revocation (ctrl, fname, &reason, psk, NULL, leadin, 3, cache_nonce); opt.outfile = old_outfile; if (!rc && !opt.quiet) log_info (_("revocation certificate stored as '%s.rev'\n"), fname); xfree (leadin); xfree (fname); return rc; } /**************** * Generate a revocation certificate for UNAME */ int gen_revoke (ctrl_t ctrl, const char *uname) { int rc = 0; PKT_public_key *psk; u32 keyid[2]; kbnode_t keyblock = NULL; kbnode_t node; KEYDB_HANDLE kdbhd; struct revocation_reason_info *reason = NULL; KEYDB_SEARCH_DESC desc; if( opt.batch ) { log_error(_("can't do this in batch mode\n")); return GPG_ERR_GENERAL; } /* Search the userid; we don't want the whole getkey stuff here. */ - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) { rc = gpg_error_from_syserror (); goto leave; } rc = classify_user_id (uname, &desc, 1); if (!rc) rc = keydb_search (kdbhd, &desc, 1, NULL); if (rc) { if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) log_error (_("secret key \"%s\" not found\n"), uname); else log_error (_("secret key \"%s\" not found: %s\n"), uname, gpg_strerror (rc)); goto leave; } rc = keydb_get_keyblock (kdbhd, &keyblock ); if (rc) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc) ); goto leave; } rc = keydb_search (kdbhd, &desc, 1, NULL); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { /* Not ambiguous. */ } else if (rc == 0) { /* Ambiguous. */ /* TRANSLATORS: The %s prints a key specification which for example has been given at the command line. Several lines lines with secret key infos are printed after this message. */ log_error (_("'%s' matches multiple secret keys:\n"), uname); print_key_info_log (ctrl, GPGRT_LOGLVL_ERROR, 2, keyblock->pkt->pkt.public_key, 1); release_kbnode (keyblock); rc = keydb_get_keyblock (kdbhd, &keyblock); while (! rc) { print_key_info_log (ctrl, GPGRT_LOGLVL_INFO, 2, keyblock->pkt->pkt.public_key, 1); release_kbnode (keyblock); keyblock = NULL; rc = keydb_search (kdbhd, &desc, 1, NULL); if (! rc) rc = keydb_get_keyblock (kdbhd, &keyblock); } rc = GPG_ERR_AMBIGUOUS_NAME; goto leave; } else { log_error (_("error searching the keyring: %s\n"), gpg_strerror (rc)); goto leave; } /* Get the keyid from the keyblock. */ node = find_kbnode (keyblock, PKT_PUBLIC_KEY); if (!node) BUG (); psk = node->pkt->pkt.public_key; rc = agent_probe_secret_key (NULL, psk); if (rc) { log_error (_("secret key \"%s\" not found: %s\n"), uname, gpg_strerror (rc)); goto leave; } keyid_from_pk (psk, keyid ); print_key_info (ctrl, NULL, 0, psk, 1); tty_printf("\n"); if (!cpr_get_answer_is_yes ("gen_revoke.okay", _("Create a revocation certificate for this key? (y/N) "))) { rc = 0; goto leave; } /* Get the reason for the revocation. */ reason = ask_revocation_reason (1, 0, 1); if (!reason) { /* User decided to cancel. */ rc = 0; goto leave; } if (!opt.armor) tty_printf (_("ASCII armored output forced.\n")); rc = create_revocation (ctrl, NULL, reason, psk, keyblock, NULL, 0, NULL); if (rc) goto leave; /* and issue a usage notice */ tty_printf (_( "Revocation certificate created.\n\n" "Please move it to a medium which you can hide away; if Mallory gets\n" "access to this certificate he can use it to make your key unusable.\n" "It is smart to print this certificate and store it away, just in case\n" "your media become unreadable. But have some caution: The print system of\n" "your machine might store the data and make it available to others!\n")); leave: release_kbnode (keyblock); keydb_release (kdbhd); release_revocation_reason_info( reason ); return rc; } struct revocation_reason_info * ask_revocation_reason( int key_rev, int cert_rev, int hint ) { int code=-1; char *description = NULL; struct revocation_reason_info *reason; const char *text_0 = _("No reason specified"); const char *text_1 = _("Key has been compromised"); const char *text_2 = _("Key is superseded"); const char *text_3 = _("Key is no longer used"); const char *text_4 = _("User ID is no longer valid"); const char *code_text = NULL; do { code=-1; xfree(description); description = NULL; tty_printf(_("Please select the reason for the revocation:\n")); tty_printf( " 0 = %s\n", text_0 ); if( key_rev ) tty_printf(" 1 = %s\n", text_1 ); if( key_rev ) tty_printf(" 2 = %s\n", text_2 ); if( key_rev ) tty_printf(" 3 = %s\n", text_3 ); if( cert_rev ) tty_printf(" 4 = %s\n", text_4 ); tty_printf( " Q = %s\n", _("Cancel") ); if( hint ) tty_printf(_("(Probably you want to select %d here)\n"), hint ); while(code==-1) { int n; char *answer = cpr_get("ask_revocation_reason.code", _("Your decision? ")); trim_spaces( answer ); cpr_kill_prompt(); if( *answer == 'q' || *answer == 'Q') return NULL; /* cancel */ if( hint && !*answer ) n = hint; else if(!digitp( answer ) ) n = -1; else n = atoi(answer); xfree(answer); if( n == 0 ) { code = 0x00; /* no particular reason */ code_text = text_0; } else if( key_rev && n == 1 ) { code = 0x02; /* key has been compromised */ code_text = text_1; } else if( key_rev && n == 2 ) { code = 0x01; /* key is superseded */ code_text = text_2; } else if( key_rev && n == 3 ) { code = 0x03; /* key is no longer used */ code_text = text_3; } else if( cert_rev && n == 4 ) { code = 0x20; /* uid is no longer valid */ code_text = text_4; } else tty_printf(_("Invalid selection.\n")); } tty_printf(_("Enter an optional description; " "end it with an empty line:\n") ); for(;;) { char *answer = cpr_get("ask_revocation_reason.text", "> " ); trim_trailing_ws( answer, strlen(answer) ); cpr_kill_prompt(); if( !*answer ) { xfree(answer); break; } { char *p = make_printable_string( answer, strlen(answer), 0 ); xfree(answer); answer = p; } if( !description ) description = xstrdup(answer); else { char *p = xmalloc( strlen(description) + strlen(answer) + 2 ); strcpy(stpcpy(stpcpy( p, description),"\n"),answer); xfree(description); description = p; } xfree(answer); } tty_printf(_("Reason for revocation: %s\n"), code_text ); if( !description ) tty_printf(_("(No description given)\n") ); else tty_printf("%s\n", description ); } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay", _("Is this okay? (y/N) ")) ); reason = xmalloc( sizeof *reason ); reason->code = code; reason->desc = description; return reason; } struct revocation_reason_info * get_default_uid_revocation_reason(void) { struct revocation_reason_info *reason; reason = xmalloc( sizeof *reason ); reason->code = 0x20; /* uid is no longer valid */ reason->desc = strdup(""); /* no text */ return reason; } void release_revocation_reason_info( struct revocation_reason_info *reason ) { if( reason ) { xfree( reason->desc ); xfree( reason ); } } diff --git a/g10/sign.c b/g10/sign.c index d71580639..8c70b0a2e 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -1,1796 +1,1796 @@ /* sign.c - sign data * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, * 2007, 2010, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include "gpg.h" #include "options.h" #include "packet.h" #include "../common/status.h" #include "../common/iobuf.h" #include "keydb.h" #include "../common/util.h" #include "main.h" #include "filter.h" #include "../common/ttyio.h" #include "trustdb.h" #include "../common/status.h" #include "../common/i18n.h" #include "pkglue.h" #include "../common/sysutils.h" #include "call-agent.h" #include "../common/mbox-util.h" #include "../common/compliance.h" #ifdef HAVE_DOSISH_SYSTEM #define LF "\r\n" #else #define LF "\n" #endif /* Hack */ static int recipient_digest_algo; /* A type for the extra data we hash into v5 signature packets. */ struct pt_extra_hash_data_s { unsigned char mode; u32 timestamp; unsigned char namelen; char name[1]; }; typedef struct pt_extra_hash_data_s *pt_extra_hash_data_t; /* * Create notations and other stuff. It is assumed that the strings in * STRLIST are already checked to contain only printable data and have * a valid NAME=VALUE format. */ static void -mk_notation_policy_etc (PKT_signature *sig, +mk_notation_policy_etc (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pk, PKT_public_key *pksk) { const char *string; char *p = NULL; strlist_t pu = NULL; struct notation *nd = NULL; struct expando_args args; log_assert (sig->version >= 4); memset (&args, 0, sizeof(args)); args.pk = pk; args.pksk = pksk; /* Notation data. */ if (IS_SIG(sig) && opt.sig_notations) nd = opt.sig_notations; else if (IS_CERT(sig) && opt.cert_notations) nd = opt.cert_notations; if (nd) { struct notation *item; for (item = nd; item; item = item->next) { - item->altvalue = pct_expando (item->value,&args); + item->altvalue = pct_expando (ctrl, item->value,&args); if (!item->altvalue) log_error (_("WARNING: unable to %%-expand notation " "(too large). Using unexpanded.\n")); } keygen_add_notations (sig, nd); for (item = nd; item; item = item->next) { xfree (item->altvalue); item->altvalue = NULL; } } /* Set policy URL. */ if (IS_SIG(sig) && opt.sig_policy_url) pu = opt.sig_policy_url; else if (IS_CERT(sig) && opt.cert_policy_url) pu = opt.cert_policy_url; for (; pu; pu = pu->next) { string = pu->d; - p = pct_expando (string, &args); + p = pct_expando (ctrl, string, &args); if (!p) { log_error(_("WARNING: unable to %%-expand policy URL " "(too large). Using unexpanded.\n")); p = xstrdup(string); } build_sig_subpkt (sig, (SIGSUBPKT_POLICY | ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)), p, strlen (p)); xfree (p); } /* Preferred keyserver URL. */ if (IS_SIG(sig) && opt.sig_keyserver_url) pu = opt.sig_keyserver_url; for (; pu; pu = pu->next) { string = pu->d; - p = pct_expando (string, &args); + p = pct_expando (ctrl, string, &args); if (!p) { log_error (_("WARNING: unable to %%-expand preferred keyserver URL" " (too large). Using unexpanded.\n")); p = xstrdup (string); } build_sig_subpkt (sig, (SIGSUBPKT_PREF_KS | ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0)), p, strlen (p)); xfree (p); } /* Set signer's user id. */ if (IS_SIG (sig) && !opt.flags.disable_signer_uid) { char *mbox; /* For now we use the uid which was used to locate the key. */ if (pksk->user_id && (mbox = mailbox_from_userid (pksk->user_id->name, 0))) { if (DBG_LOOKUP) log_debug ("setting Signer's UID to '%s'\n", mbox); build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, mbox, strlen (mbox)); xfree (mbox); } else if (opt.sender_list) { /* If a list of --sender was given we scan that list and use * the first one matching a user id of the current key. */ /* FIXME: We need to get the list of user ids for the PKSK * packet. That requires either a function to look it up * again or we need to extend the key packet struct to link * to the primary key which in turn could link to the user * ids. Too much of a change right now. Let's take just * one from the supplied list and hope that the caller * passed a matching one. */ build_sig_subpkt (sig, SIGSUBPKT_SIGNERS_UID, opt.sender_list->d, strlen (opt.sender_list->d)); } } } /* * Helper to hash a user ID packet. */ static void hash_uid (gcry_md_hd_t md, int sigversion, const PKT_user_id *uid) { byte buf[5]; (void)sigversion; if (uid->attrib_data) { buf[0] = 0xd1; /* Indicates an attribute packet. */ buf[1] = uid->attrib_len >> 24; /* Always use 4 length bytes. */ buf[2] = uid->attrib_len >> 16; buf[3] = uid->attrib_len >> 8; buf[4] = uid->attrib_len; } else { buf[0] = 0xb4; /* Indicates a userid packet. */ buf[1] = uid->len >> 24; /* Always use 4 length bytes. */ buf[2] = uid->len >> 16; buf[3] = uid->len >> 8; buf[4] = uid->len; } gcry_md_write( md, buf, 5 ); if (uid->attrib_data) gcry_md_write (md, uid->attrib_data, uid->attrib_len ); else gcry_md_write (md, uid->name, uid->len ); } /* * Helper to hash some parts from the signature. EXTRAHASH gives the * extra data to be hashed into v5 signatures; it may by NULL for * detached signatures. */ static void hash_sigversion_to_magic (gcry_md_hd_t md, const PKT_signature *sig, pt_extra_hash_data_t extrahash) { byte buf[10]; int i; size_t n; gcry_md_putc (md, sig->version); gcry_md_putc (md, sig->sig_class); gcry_md_putc (md, sig->pubkey_algo); gcry_md_putc (md, sig->digest_algo); if (sig->hashed) { n = sig->hashed->len; gcry_md_putc (md, (n >> 8) ); gcry_md_putc (md, n ); gcry_md_write (md, sig->hashed->data, n ); n += 6; } else { gcry_md_putc (md, 0); /* Always hash the length of the subpacket. */ gcry_md_putc (md, 0); n = 6; } /* Hash data from the literal data packet. */ if (sig->version >= 5 && (sig->sig_class == 0x00 || sig->sig_class == 0x01)) { /* - One octet content format * - File name (one octet length followed by the name) * - Four octet timestamp */ if (extrahash) { buf[0] = extrahash->mode; buf[1] = extrahash->namelen; gcry_md_write (md, buf, 2); if (extrahash->namelen) gcry_md_write (md, extrahash->name, extrahash->namelen); buf[0] = extrahash->timestamp >> 24; buf[1] = extrahash->timestamp >> 16; buf[2] = extrahash->timestamp >> 8; buf[3] = extrahash->timestamp; gcry_md_write (md, buf, 4); } else /* Detached signatures */ { memset (buf, 0, 6); gcry_md_write (md, buf, 6); } } /* Add some magic. */ i = 0; buf[i++] = sig->version; buf[i++] = 0xff; if (sig->version >= 5) { /* Note: We don't hashed any data larger than 2^32 and thus we * can always use 0 here. See also note below. */ buf[i++] = 0; buf[i++] = 0; buf[i++] = 0; buf[i++] = 0; } buf[i++] = n >> 24; /* (n is only 16 bit, so this is always 0) */ buf[i++] = n >> 16; buf[i++] = n >> 8; buf[i++] = n; gcry_md_write (md, buf, i); } /* Perform the sign operation. If CACHE_NONCE is given the agent is advised to use that cached passphrase for the key. */ static int do_sign (ctrl_t ctrl, PKT_public_key *pksk, PKT_signature *sig, gcry_md_hd_t md, int mdalgo, const char *cache_nonce) { gpg_error_t err; byte *dp; char *hexgrip; if (pksk->timestamp > sig->timestamp ) { ulong d = pksk->timestamp - sig->timestamp; log_info (ngettext("key %s was created %lu second" " in the future (time warp or clock problem)\n", "key %s was created %lu seconds" " in the future (time warp or clock problem)\n", d), keystr_from_pk (pksk), d); if (!opt.ignore_time_conflict) return gpg_error (GPG_ERR_TIME_CONFLICT); } print_pubkey_algo_note (pksk->pubkey_algo); if (!mdalgo) mdalgo = gcry_md_get_algo (md); /* Check compliance. */ if (! gnupg_digest_is_allowed (opt.compliance, 1, mdalgo)) { log_error (_("digest algorithm '%s' may not be used in %s mode\n"), gcry_md_algo_name (mdalgo), gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_DIGEST_ALGO); goto leave; } if (! gnupg_pk_is_allowed (opt.compliance, PK_USE_SIGNING, pksk->pubkey_algo, pksk->pkey, nbits_from_pk (pksk), NULL)) { log_error (_("key %s may not be used for signing in %s mode\n"), keystr_from_pk (pksk), gnupg_compliance_option_string (opt.compliance)); err = gpg_error (GPG_ERR_PUBKEY_ALGO); goto leave; } if (!gnupg_rng_is_compliant (opt.compliance)) { err = gpg_error (GPG_ERR_FORBIDDEN); log_error (_("%s is not compliant with %s mode\n"), "RNG", gnupg_compliance_option_string (opt.compliance)); write_status_error ("random-compliance", err); goto leave; } print_digest_algo_note (mdalgo); dp = gcry_md_read (md, mdalgo); sig->digest_algo = mdalgo; sig->digest_start[0] = dp[0]; sig->digest_start[1] = dp[1]; mpi_release (sig->data[0]); sig->data[0] = NULL; mpi_release (sig->data[1]); sig->data[1] = NULL; err = hexkeygrip_from_pk (pksk, &hexgrip); if (!err) { char *desc; gcry_sexp_t s_sigval; desc = gpg_format_keydesc (ctrl, pksk, FORMAT_KEYDESC_NORMAL, 1); err = agent_pksign (NULL/*ctrl*/, cache_nonce, hexgrip, desc, pksk->keyid, pksk->main_keyid, pksk->pubkey_algo, dp, gcry_md_get_algo_dlen (mdalgo), mdalgo, &s_sigval); xfree (desc); if (err) ; else if (pksk->pubkey_algo == GCRY_PK_RSA || pksk->pubkey_algo == GCRY_PK_RSA_S) sig->data[0] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG); else if (openpgp_oid_is_ed25519 (pksk->pkey[0])) { sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_OPAQUE); sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_OPAQUE); } else { sig->data[0] = get_mpi_from_sexp (s_sigval, "r", GCRYMPI_FMT_USG); sig->data[1] = get_mpi_from_sexp (s_sigval, "s", GCRYMPI_FMT_USG); } gcry_sexp_release (s_sigval); } xfree (hexgrip); leave: if (err) log_error (_("signing failed: %s\n"), gpg_strerror (err)); else { if (opt.verbose) { char *ustr = get_user_id_string_native (ctrl, sig->keyid); log_info (_("%s/%s signature from: \"%s\"\n"), openpgp_pk_algo_name (pksk->pubkey_algo), openpgp_md_algo_name (sig->digest_algo), ustr); xfree (ustr); } } return err; } static int complete_sig (ctrl_t ctrl, PKT_signature *sig, PKT_public_key *pksk, gcry_md_hd_t md, const char *cache_nonce) { int rc; /* if (!(rc = check_secret_key (pksk, 0))) */ rc = do_sign (ctrl, pksk, sig, md, 0, cache_nonce); return rc; } /* Return true if the key seems to be on a version 1 OpenPGP card. This works by asking the agent and may fail if the card has not yet been used with the agent. */ static int openpgp_card_v1_p (PKT_public_key *pk) { gpg_error_t err; int result; /* Shortcut if we are not using RSA: The v1 cards only support RSA thus there is no point in looking any further. */ if (!is_RSA (pk->pubkey_algo)) return 0; if (!pk->flags.serialno_valid) { char *hexgrip; err = hexkeygrip_from_pk (pk, &hexgrip); if (err) { log_error ("error computing a keygrip: %s\n", gpg_strerror (err)); return 0; /* Ooops. */ } xfree (pk->serialno); agent_get_keyinfo (NULL, hexgrip, &pk->serialno, NULL); xfree (hexgrip); pk->flags.serialno_valid = 1; } if (!pk->serialno) result = 0; /* Error from a past agent_get_keyinfo or no card. */ else { /* The version number of the card is included in the serialno. */ result = !strncmp (pk->serialno, "D2760001240101", 14); } return result; } static int match_dsa_hash (unsigned int qbytes) { if (qbytes <= 20) return DIGEST_ALGO_SHA1; if (qbytes <= 28) return DIGEST_ALGO_SHA224; if (qbytes <= 32) return DIGEST_ALGO_SHA256; if (qbytes <= 48) return DIGEST_ALGO_SHA384; if (qbytes <= 66 ) /* 66 corresponds to 521 (64 to 512) */ return DIGEST_ALGO_SHA512; return DEFAULT_DIGEST_ALGO; /* DEFAULT_DIGEST_ALGO will certainly fail, but it's the best wrong answer we have if a digest larger than 512 bits is requested. */ } /* First try --digest-algo. If that isn't set, see if the recipient has a preferred algorithm (which is also filtered through --personal-digest-prefs). If we're making a signature without a particular recipient (i.e. signing, rather than signing+encrypting) then take the first algorithm in --personal-digest-prefs that is usable for the pubkey algorithm. If --personal-digest-prefs isn't set, then take the OpenPGP default (i.e. SHA-1). Note that Ed25519+EdDSA takes an input of arbitrary length and thus we don't enforce any particular algorithm like we do for standard ECDSA. However, we use SHA256 as the default algorithm. Possible improvement: Use the highest-ranked usable algorithm from the signing key prefs either before or after using the personal list? */ static int hash_for (PKT_public_key *pk) { if (opt.def_digest_algo) { return opt.def_digest_algo; } else if (recipient_digest_algo) { return recipient_digest_algo; } else if (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && openpgp_oid_is_ed25519 (pk->pkey[0])) { if (opt.personal_digest_prefs) return opt.personal_digest_prefs[0].value; else return DIGEST_ALGO_SHA256; } else if (pk->pubkey_algo == PUBKEY_ALGO_DSA || pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { unsigned int qbytes = gcry_mpi_get_nbits (pk->pkey[1]); if (pk->pubkey_algo == PUBKEY_ALGO_ECDSA) qbytes = ecdsa_qbits_from_Q (qbytes); qbytes = qbytes/8; /* It's a DSA key, so find a hash that is the same size as q or larger. If q is 160, assume it is an old DSA key and use a 160-bit hash unless --enable-dsa2 is set, in which case act like a new DSA key that just happens to have a 160-bit q (i.e. allow truncation). If q is not 160, by definition it must be a new DSA key. */ if (opt.personal_digest_prefs) { prefitem_t *prefs; if (qbytes != 20 || opt.flags.dsa2) { for (prefs=opt.personal_digest_prefs; prefs->type; prefs++) if (gcry_md_get_algo_dlen (prefs->value) >= qbytes) return prefs->value; } else { for (prefs=opt.personal_digest_prefs; prefs->type; prefs++) if (gcry_md_get_algo_dlen (prefs->value) == qbytes) return prefs->value; } } return match_dsa_hash(qbytes); } else if (openpgp_card_v1_p (pk)) { /* The sk lives on a smartcard, and old smartcards only handle SHA-1 and RIPEMD/160. Newer smartcards (v2.0) don't have this restriction anymore. Fortunately the serial number encodes the version of the card and thus we know that this key is on a v1 card. */ if(opt.personal_digest_prefs) { prefitem_t *prefs; for (prefs=opt.personal_digest_prefs;prefs->type;prefs++) if (prefs->value==DIGEST_ALGO_SHA1 || prefs->value==DIGEST_ALGO_RMD160) return prefs->value; } return DIGEST_ALGO_SHA1; } else if (opt.personal_digest_prefs) { /* It's not DSA, so we can use whatever the first hash algorithm is in the pref list */ return opt.personal_digest_prefs[0].value; } else return DEFAULT_DIGEST_ALGO; } static void print_status_sig_created (PKT_public_key *pk, PKT_signature *sig, int what) { byte array[MAX_FINGERPRINT_LEN]; char buf[100+MAX_FINGERPRINT_LEN*2]; size_t n; snprintf (buf, sizeof buf - 2*MAX_FINGERPRINT_LEN, "%c %d %d %02x %lu ", what, sig->pubkey_algo, sig->digest_algo, sig->sig_class, (ulong)sig->timestamp ); fingerprint_from_pk (pk, array, &n); bin2hex (array, n, buf + strlen (buf)); write_status_text( STATUS_SIG_CREATED, buf ); } /* * Loop over the secret certificates in SK_LIST and build the one pass * signature packets. OpenPGP says that the data should be bracket by * the onepass-sig and signature-packet; so we build these onepass * packet here in reverse order. */ static int write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass ) { int skcount; SK_LIST sk_rover; for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next) skcount++; for (; skcount; skcount--) { PKT_public_key *pk; PKT_onepass_sig *ops; PACKET pkt; int i, rc; for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) if (++i == skcount) break; pk = sk_rover->pk; ops = xmalloc_clear (sizeof *ops); ops->sig_class = sigclass; ops->digest_algo = hash_for (pk); ops->pubkey_algo = pk->pubkey_algo; keyid_from_pk (pk, ops->keyid); ops->last = (skcount == 1); init_packet (&pkt); pkt.pkttype = PKT_ONEPASS_SIG; pkt.pkt.onepass_sig = ops; rc = build_packet (out, &pkt); free_packet (&pkt, NULL); if (rc) { log_error ("build onepass_sig packet failed: %s\n", gpg_strerror (rc)); return rc; } } return 0; } /* * Helper to write the plaintext (literal data) packet. At * R_EXTRAHASH a malloced object with the with the extra data hashed * into v5 signatures is stored. */ static int write_plaintext_packet (iobuf_t out, iobuf_t inp, const char *fname, int ptmode, pt_extra_hash_data_t *r_extrahash) { PKT_plaintext *pt = NULL; u32 filesize; int rc = 0; if (!opt.no_literal) pt = setup_plaintext_name (fname, inp); /* Try to calculate the length of the data. */ if ( !iobuf_is_pipe_filename (fname) && *fname) { off_t tmpsize; int overflow; if (!(tmpsize = iobuf_get_filelength (inp, &overflow)) && !overflow && opt.verbose) log_info (_("WARNING: '%s' is an empty file\n"), fname); /* We can't encode the length of very large files because * OpenPGP uses only 32 bit for file sizes. So if the size of a * file is larger than 2^32 minus some bytes for packet headers, * we switch to partial length encoding. */ if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536)) filesize = tmpsize; else filesize = 0; /* Because the text_filter modifies the length of the * data, it is not possible to know the used length * without a double read of the file - to avoid that * we simple use partial length packets. */ if (ptmode == 't' || ptmode == 'u' || ptmode == 'm') filesize = 0; } else filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */ if (!opt.no_literal) { PACKET pkt; /* Note that PT has been initialized above in no_literal mode. */ pt->timestamp = make_timestamp (); pt->mode = ptmode; pt->len = filesize; pt->new_ctb = !pt->len; pt->buf = inp; init_packet (&pkt); pkt.pkttype = PKT_PLAINTEXT; pkt.pkt.plaintext = pt; /*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/ if ((rc = build_packet (out, &pkt))) log_error ("build_packet(PLAINTEXT) failed: %s\n", gpg_strerror (rc) ); *r_extrahash = xtrymalloc (sizeof **r_extrahash + pt->namelen); if (!*r_extrahash) rc = gpg_error_from_syserror (); else { (*r_extrahash)->mode = pt->mode; (*r_extrahash)->timestamp = pt->timestamp; (*r_extrahash)->namelen = pt->namelen; /* Note that the last byte of NAME won't be initialized * because we don't need it. */ memcpy ((*r_extrahash)->name, pt->name, pt->namelen); } pt->buf = NULL; free_packet (&pkt, NULL); } else { byte copy_buffer[4096]; int bytes_copied; *r_extrahash = xtrymalloc (sizeof **r_extrahash); if (!*r_extrahash) { rc = gpg_error_from_syserror (); goto leave; } /* FIXME: We need to parse INP to get the to be hashed data from * it. */ (*r_extrahash)->mode = 0; (*r_extrahash)->timestamp = 0; (*r_extrahash)->namelen = 0; while ((bytes_copied = iobuf_read (inp, copy_buffer, 4096)) != -1) if ((rc = iobuf_write (out, copy_buffer, bytes_copied))) { log_error ("copying input to output failed: %s\n", gpg_strerror (rc)); break; } wipememory (copy_buffer, 4096); /* burn buffer */ } leave: return rc; } /* * Write the signatures from the SK_LIST to OUT. HASH must be a * non-finalized hash which will not be changes here. EXTRAHASH is * either NULL or the extra data tro be hashed into v5 signatures. */ static int write_signature_packets (ctrl_t ctrl, SK_LIST sk_list, IOBUF out, gcry_md_hd_t hash, pt_extra_hash_data_t extrahash, int sigclass, u32 timestamp, u32 duration, int status_letter, const char *cache_nonce) { SK_LIST sk_rover; /* Loop over the certificates with secret keys. */ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { PKT_public_key *pk; PKT_signature *sig; gcry_md_hd_t md; int rc; pk = sk_rover->pk; /* Build the signature packet. */ sig = xtrycalloc (1, sizeof *sig); if (!sig) return gpg_error_from_syserror (); if (pk->version >= 5) sig->version = 5; /* Required for v5 keys. */ else sig->version = 4; /* Required. */ keyid_from_pk (pk, sig->keyid); sig->digest_algo = hash_for (pk); sig->pubkey_algo = pk->pubkey_algo; if (timestamp) sig->timestamp = timestamp; else sig->timestamp = make_timestamp(); if (duration) sig->expiredate = sig->timestamp + duration; sig->sig_class = sigclass; if (gcry_md_copy (&md, hash)) BUG (); build_sig_subpkt_from_sig (sig, pk); - mk_notation_policy_etc (sig, NULL, pk); + mk_notation_policy_etc (ctrl, sig, NULL, pk); hash_sigversion_to_magic (md, sig, extrahash); gcry_md_final (md); rc = do_sign (ctrl, pk, sig, md, hash_for (pk), cache_nonce); gcry_md_close (md); if (!rc) { /* Write the packet. */ PACKET pkt; init_packet (&pkt); pkt.pkttype = PKT_SIGNATURE; pkt.pkt.signature = sig; rc = build_packet (out, &pkt); if (!rc && is_status_enabled()) print_status_sig_created (pk, sig, status_letter); free_packet (&pkt, NULL); if (rc) log_error ("build signature packet failed: %s\n", gpg_strerror (rc)); } else free_seckey_enc (sig); if (rc) return rc; } return 0; } /* * Sign the files whose names are in FILENAME. * If DETACHED has the value true, * make a detached signature. If FILENAMES->d is NULL read from stdin * and ignore the detached mode. Sign the file with all secret keys * which can be taken from LOCUSR, if this is NULL, use the default one * If ENCRYPTFLAG is true, use REMUSER (or ask if it is NULL) to encrypt the * signed data for these users. * If OUTFILE is not NULL; this file is used for output and the function * does not ask for overwrite permission; output is then always * uncompressed, non-armored and in binary mode. */ int sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr, int encryptflag, strlist_t remusr, const char *outfile ) { const char *fname; armor_filter_context_t *afx; compress_filter_context_t zfx; md_filter_context_t mfx; text_filter_context_t tfx; progress_filter_context_t *pfx; encrypt_filter_context_t efx; iobuf_t inp = NULL; iobuf_t out = NULL; PACKET pkt; int rc = 0; PK_LIST pk_list = NULL; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; int multifile = 0; u32 duration=0; pt_extra_hash_data_t extrahash = NULL; pfx = new_progress_context (); afx = new_armor_context (); memset (&zfx, 0, sizeof zfx); memset (&mfx, 0, sizeof mfx); memset (&efx, 0, sizeof efx); efx.ctrl = ctrl; init_packet (&pkt); if (filenames) { fname = filenames->d; multifile = !!filenames->next; } else fname = NULL; if (fname && filenames->next && (!detached || encryptflag)) log_bug ("multiple files can only be detached signed"); if (encryptflag == 2 && (rc = setup_symkey (&efx.symkey_s2k, &efx.symkey_dek))) goto leave; if (opt.ask_sig_expire && !opt.batch) duration = ask_expire_interval(1,opt.def_sig_expire); else duration = parse_expire_string(opt.def_sig_expire); /* Note: In the old non-agent version the following call used to * unprotect the secret key. This is now done on demand by the agent. */ if ((rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG ))) goto leave; if (encryptflag && (rc = build_pk_list (ctrl, remusr, &pk_list))) goto leave; /* Prepare iobufs. */ if (multifile) /* have list of filenames */ inp = NULL; /* we do it later */ else { inp = iobuf_open(fname); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { rc = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), fname? fname: "[stdin]", strerror (errno)); goto leave; } handle_progress (pfx, inp, fname); } if (outfile) { if (is_secured_filename (outfile)) { out = NULL; gpg_err_set_errno (EPERM); } else out = iobuf_create (outfile, 0); if (!out) { rc = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), outfile, gpg_strerror (rc)); goto leave; } else if (opt.verbose) log_info (_("writing to '%s'\n"), outfile); } else if ((rc = open_outfile (-1, fname, opt.armor? 1 : detached? 2 : 0, 0, &out))) { goto leave; } /* Prepare to calculate the MD over the input. */ if (opt.textmode && !outfile && !multifile) { memset (&tfx, 0, sizeof tfx); iobuf_push_filter (inp, text_filter, &tfx); } if (gcry_md_open (&mfx.md, 0, 0)) BUG (); if (DBG_HASHING) gcry_md_debug (mfx.md, "sign"); /* If we're encrypting and signing, it is reasonable to pick the * hash algorithm to use out of the recipient key prefs. This is * best effort only, as in a DSA2 and smartcard world there are * cases where we cannot please everyone with a single hash (DSA2 * wants >160 and smartcards want =160). In the future this could * be more complex with different hashes for each sk, but the * current design requires a single hash for all SKs. */ if (pk_list) { if (opt.def_digest_algo) { if (!opt.expert && select_algo_from_prefs (pk_list,PREFTYPE_HASH, opt.def_digest_algo, NULL) != opt.def_digest_algo) { log_info (_("WARNING: forcing digest algorithm %s (%d)" " violates recipient preferences\n"), gcry_md_algo_name (opt.def_digest_algo), opt.def_digest_algo); } } else { int algo; int smartcard=0; union pref_hint hint; hint.digest_length = 0; /* Of course, if the recipient asks for something * unreasonable (like the wrong hash for a DSA key) then * don't do it. Check all sk's - if any are DSA or live * on a smartcard, then the hash has restrictions and we * may not be able to give the recipient what they want. * For DSA, pass a hint for the largest q we have. Note * that this means that a q>160 key will override a q=160 * key and force the use of truncation for the q=160 key. * The alternative would be to ignore the recipient prefs * completely and get a different hash for each DSA key in * hash_for(). The override behavior here is more or less * reasonable as it is under the control of the user which * keys they sign with for a given message and the fact * that the message with multiple signatures won't be * usable on an implementation that doesn't understand * DSA2 anyway. */ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) { if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_DSA || sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) { int temp_hashlen = gcry_mpi_get_nbits (sk_rover->pk->pkey[1]); if (sk_rover->pk->pubkey_algo == PUBKEY_ALGO_ECDSA) temp_hashlen = ecdsa_qbits_from_Q (temp_hashlen); temp_hashlen = (temp_hashlen+7)/8; /* Pick a hash that is large enough for our largest Q */ if (hint.digest_length < temp_hashlen) hint.digest_length = temp_hashlen; } /* FIXME: need to check gpg-agent for this. */ /* else if (sk_rover->pk->is_protected */ /* && sk_rover->pk->protect.s2k.mode == 1002) */ /* smartcard = 1; */ } /* Current smartcards only do 160-bit hashes. If we have * to have a >160-bit hash, then we can't use the * recipient prefs as we'd need both =160 and >160 at the * same time and recipient prefs currently require a * single hash for all signatures. All this may well have * to change as the cards add algorithms. */ if ((!smartcard || (smartcard && hint.digest_length==20)) && ((algo = select_algo_from_prefs (pk_list, PREFTYPE_HASH, -1, &hint)) > 0)) { recipient_digest_algo = algo; } } } for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); if (!multifile) iobuf_push_filter (inp, md_filter, &mfx); if (detached && !encryptflag) afx->what = 2; if (opt.armor && !outfile) push_armor_filter (afx, out); if (encryptflag) { efx.pk_list = pk_list; /* fixme: set efx.cfx.datalen if known */ iobuf_push_filter (out, encrypt_filter, &efx); } if (opt.compress_algo && !outfile && !detached) { int compr_algo = opt.compress_algo; /* If not forced by user */ if (compr_algo==-1) { /* If we're not encrypting, then select_algo_from_prefs * will fail and we'll end up with the default. If we are * encrypting, select_algo_from_prefs cannot fail since * there is an assumed preference for uncompressed data. * Still, if it did fail, we'll also end up with the * default. */ if ((compr_algo = select_algo_from_prefs (pk_list, PREFTYPE_ZIP, -1, NULL)) == -1) { compr_algo = default_compress_algo(); } } else if (!opt.expert && pk_list && select_algo_from_prefs (pk_list, PREFTYPE_ZIP, compr_algo, NULL) != compr_algo) { log_info (_("WARNING: forcing compression algorithm %s (%d)" " violates recipient preferences\n"), compress_algo_to_string (compr_algo), compr_algo); } /* Algo 0 means no compression. */ if (compr_algo) push_compress_filter (out, &zfx, compr_algo); } /* Write the one-pass signature packets if needed */ if (!detached) { rc = write_onepass_sig_packets (sk_list, out, opt.textmode && !outfile ? 0x01:0x00); if (rc) goto leave; } write_status_begin_signing (mfx.md); /* Setup the inner packet. */ if (detached) { if (multifile) { strlist_t sl; if (opt.verbose) log_info (_("signing:") ); /* Must walk reverse trough this list. */ for (sl = strlist_last(filenames); sl; sl = strlist_prev( filenames, sl)) { inp = iobuf_open (sl->d); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { rc = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), sl->d, gpg_strerror (rc)); goto leave; } handle_progress (pfx, inp, sl->d); if (opt.verbose) log_printf (" '%s'", sl->d ); if (opt.textmode) { memset (&tfx, 0, sizeof tfx); iobuf_push_filter (inp, text_filter, &tfx); } iobuf_push_filter (inp, md_filter, &mfx); while (iobuf_get (inp) != -1) ; iobuf_close (inp); inp = NULL; } if (opt.verbose) log_printf ("\n"); } else { /* Read, so that the filter can calculate the digest. */ while (iobuf_get(inp) != -1) ; } } else { rc = write_plaintext_packet (out, inp, fname, (opt.textmode && !outfile) ? (opt.mimemode? 'm' : 't') : 'b', &extrahash); } /* Catch errors from above. */ if (rc) goto leave; /* Write the signatures. */ rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, opt.textmode && !outfile? 0x01 : 0x00, 0, duration, detached ? 'D':'S', NULL); if (rc) goto leave; leave: if (rc) iobuf_cancel (out); else { iobuf_close (out); if (encryptflag) write_status (STATUS_END_ENCRYPTION); } iobuf_close (inp); gcry_md_close (mfx.md); release_sk_list (sk_list); release_pk_list (pk_list); recipient_digest_algo = 0; release_progress_context (pfx); release_armor_context (afx); xfree (extrahash); return rc; } /* * Make a clear signature. Note that opt.armor is not needed. */ int clearsign_file (ctrl_t ctrl, const char *fname, strlist_t locusr, const char *outfile) { armor_filter_context_t *afx; progress_filter_context_t *pfx; gcry_md_hd_t textmd = NULL; iobuf_t inp = NULL; iobuf_t out = NULL; PACKET pkt; int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; u32 duration = 0; pfx = new_progress_context (); afx = new_armor_context (); init_packet( &pkt ); if (opt.ask_sig_expire && !opt.batch) duration = ask_expire_interval (1, opt.def_sig_expire); else duration = parse_expire_string (opt.def_sig_expire); /* Note: In the old non-agent version the following call used to * unprotect the secret key. This is now done on demand by the agent. */ if ((rc=build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG))) goto leave; /* Prepare iobufs. */ inp = iobuf_open (fname); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { rc = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), fname? fname: "[stdin]", gpg_strerror (rc)); goto leave; } handle_progress (pfx, inp, fname); if (outfile) { if (is_secured_filename (outfile)) { outfile = NULL; gpg_err_set_errno (EPERM); } else out = iobuf_create (outfile, 0); if (!out) { rc = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), outfile, gpg_strerror (rc)); goto leave; } else if (opt.verbose) log_info (_("writing to '%s'\n"), outfile); } else if ((rc = open_outfile (-1, fname, 1, 0, &out))) { goto leave; } iobuf_writestr (out, "-----BEGIN PGP SIGNED MESSAGE-----" LF); { const char *s; int any = 0; byte hashs_seen[256]; memset (hashs_seen, 0, sizeof hashs_seen); iobuf_writestr (out, "Hash: " ); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) { int i = hash_for (sk_rover->pk); if (!hashs_seen[ i & 0xff ]) { s = gcry_md_algo_name (i); if (s) { hashs_seen[ i & 0xff ] = 1; if (any) iobuf_put (out, ','); iobuf_writestr (out, s); any = 1; } } } log_assert (any); iobuf_writestr (out, LF); } if (opt.not_dash_escaped) iobuf_writestr (out, "NotDashEscaped: You need "GPG_NAME " to verify this message" LF); iobuf_writestr (out, LF ); if (gcry_md_open (&textmd, 0, 0)) BUG (); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) gcry_md_enable (textmd, hash_for(sk_rover->pk)); if (DBG_HASHING) gcry_md_debug (textmd, "clearsign"); copy_clearsig_text (out, inp, textmd, !opt.not_dash_escaped, opt.escape_from); /* fixme: check for read errors */ /* Now write the armor. */ afx->what = 2; push_armor_filter (afx, out); /* Write the signatures. */ rc = write_signature_packets (ctrl, sk_list, out, textmd, NULL, 0x01, 0, duration, 'C', NULL); if (rc) goto leave; leave: if (rc) iobuf_cancel (out); else iobuf_close (out); iobuf_close (inp); gcry_md_close (textmd); release_sk_list (sk_list); release_progress_context (pfx); release_armor_context (afx); return rc; } /* * Sign and conventionally encrypt the given file. * FIXME: Far too much code is duplicated - revamp the whole file. */ int sign_symencrypt_file (ctrl_t ctrl, const char *fname, strlist_t locusr) { armor_filter_context_t *afx; progress_filter_context_t *pfx; compress_filter_context_t zfx; md_filter_context_t mfx; text_filter_context_t tfx; cipher_filter_context_t cfx; iobuf_t inp = NULL; iobuf_t out = NULL; PACKET pkt; STRING2KEY *s2k = NULL; int rc = 0; SK_LIST sk_list = NULL; SK_LIST sk_rover = NULL; int algo; u32 duration = 0; int canceled; pt_extra_hash_data_t extrahash = NULL; pfx = new_progress_context (); afx = new_armor_context (); memset (&zfx, 0, sizeof zfx); memset (&mfx, 0, sizeof mfx); memset (&tfx, 0, sizeof tfx); memset (&cfx, 0, sizeof cfx); init_packet (&pkt); if (opt.ask_sig_expire && !opt.batch) duration = ask_expire_interval (1, opt.def_sig_expire); else duration = parse_expire_string (opt.def_sig_expire); /* Note: In the old non-agent version the following call used to * unprotect the secret key. This is now done on demand by the agent. */ rc = build_sk_list (ctrl, locusr, &sk_list, PUBKEY_USAGE_SIG); if (rc) goto leave; /* Prepare iobufs. */ inp = iobuf_open (fname); if (inp && is_secured_file (iobuf_get_fd (inp))) { iobuf_close (inp); inp = NULL; gpg_err_set_errno (EPERM); } if (!inp) { rc = gpg_error_from_syserror (); log_error (_("can't open '%s': %s\n"), fname? fname: "[stdin]", gpg_strerror (rc)); goto leave; } handle_progress (pfx, inp, fname); /* Prepare key. */ s2k = xmalloc_clear (sizeof *s2k); s2k->mode = opt.s2k_mode; s2k->hash_algo = S2K_DIGEST_ALGO; algo = default_cipher_algo (); cfx.dek = passphrase_to_dek (algo, s2k, 1, 1, NULL, &canceled); if (!cfx.dek || !cfx.dek->keylen) { rc = gpg_error (canceled?GPG_ERR_CANCELED:GPG_ERR_BAD_PASSPHRASE); log_error (_("error creating passphrase: %s\n"), gpg_strerror (rc)); goto leave; } cfx.dek->use_aead = use_aead (NULL, cfx.dek->algo); if (!cfx.dek->use_aead) cfx.dek->use_mdc = !!use_mdc (NULL, cfx.dek->algo); if (!opt.quiet || !opt.batch) log_info (_("%s.%s encryption will be used\n"), openpgp_cipher_algo_name (algo), cfx.dek->use_aead? openpgp_aead_algo_name (cfx.dek->use_aead) /**/ : "CFB"); /* Now create the outfile. */ rc = open_outfile (-1, fname, opt.armor? 1:0, 0, &out); if (rc) goto leave; /* Prepare to calculate the MD over the input. */ if (opt.textmode) iobuf_push_filter (inp, text_filter, &tfx); if (gcry_md_open (&mfx.md, 0, 0)) BUG (); if (DBG_HASHING) gcry_md_debug (mfx.md, "symc-sign"); for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) gcry_md_enable (mfx.md, hash_for (sk_rover->pk)); iobuf_push_filter (inp, md_filter, &mfx); /* Push armor output filter */ if (opt.armor) push_armor_filter (afx, out); /* Write the symmetric key packet */ /* (current filters: armor)*/ { PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc ); enc->version = 4; enc->cipher_algo = cfx.dek->algo; enc->s2k = *s2k; pkt.pkttype = PKT_SYMKEY_ENC; pkt.pkt.symkey_enc = enc; if ((rc = build_packet (out, &pkt))) log_error ("build symkey packet failed: %s\n", gpg_strerror (rc)); xfree (enc); } /* Push the encryption filter */ iobuf_push_filter (out, cfx.dek->use_aead? cipher_filter_aead /**/ : cipher_filter_cfb, &cfx); /* Push the compress filter */ if (default_compress_algo()) { if (cfx.dek && (cfx.dek->use_mdc || cfx.dek->use_aead)) zfx.new_ctb = 1; push_compress_filter (out, &zfx,default_compress_algo() ); } /* Write the one-pass signature packets */ /* (current filters: zip - encrypt - armor) */ rc = write_onepass_sig_packets (sk_list, out, opt.textmode? 0x01:0x00); if (rc) goto leave; write_status_begin_signing (mfx.md); /* Pipe data through all filters; i.e. write the signed stuff. */ /* (current filters: zip - encrypt - armor) */ rc = write_plaintext_packet (out, inp, fname, opt.textmode ? (opt.mimemode?'m':'t'):'b', &extrahash); if (rc) goto leave; /* Write the signatures. */ /* (current filters: zip - encrypt - armor) */ rc = write_signature_packets (ctrl, sk_list, out, mfx.md, extrahash, opt.textmode? 0x01 : 0x00, 0, duration, 'S', NULL); if (rc) goto leave; leave: if (rc) iobuf_cancel (out); else { iobuf_close (out); write_status (STATUS_END_ENCRYPTION); } iobuf_close (inp); release_sk_list (sk_list); gcry_md_close (mfx.md); xfree (cfx.dek); xfree (s2k); release_progress_context (pfx); release_armor_context (afx); xfree (extrahash); return rc; } /* * Create a v4 signature in *RET_SIG. * * PK is the primary key to sign (required for all sigs) * UID is the user id to sign (required for 0x10..0x13, 0x30) * SUBPK is subkey to sign (required for 0x18, 0x19, 0x28) * * PKSK is the signing key * * SIGCLASS is the type of signature to create. * * DIGEST_ALGO is the digest algorithm. If it is 0 the function * selects an appropriate one. * * TIMESTAMP is the timestamp to use for the signature. 0 means "now" * * DURATION is the amount of time (in seconds) until the signature * expires. * * This function creates the following subpackets: issuer, created, * and expire (if duration is not 0). Additional subpackets can be * added using MKSUBPKT, which is called after these subpackets are * added and before the signature is generated. OPAQUE is passed to * MKSUBPKT. */ int make_keysig_packet (ctrl_t ctrl, PKT_signature **ret_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, PKT_public_key *pksk, int sigclass, u32 timestamp, u32 duration, int (*mksubpkt)(PKT_signature *, void *), void *opaque, const char *cache_nonce) { PKT_signature *sig; int rc = 0; int sigversion; int digest_algo; gcry_md_hd_t md; log_assert ((sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F || sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x30 || sigclass == 0x28 ); if (pksk->version >= 5) sigversion = 5; else sigversion = 4; /* Select the digest algo to use. */ if (opt.cert_digest_algo) /* Forceful override by the user. */ digest_algo = opt.cert_digest_algo; else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA) /* Meet DSA requirements. */ digest_algo = match_dsa_hash (gcry_mpi_get_nbits (pksk->pkey[1])/8); else if (pksk->pubkey_algo == PUBKEY_ALGO_ECDSA /* Meet ECDSA requirements. */ || pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) { if (openpgp_oid_is_ed25519 (pksk->pkey[0])) digest_algo = DIGEST_ALGO_SHA256; else digest_algo = match_dsa_hash (ecdsa_qbits_from_Q (gcry_mpi_get_nbits (pksk->pkey[1]))/8); } else /* Use the default. */ digest_algo = DEFAULT_DIGEST_ALGO; if (gcry_md_open (&md, digest_algo, 0)) BUG (); /* Hash the public key certificate. */ hash_public_key (md, pk); if (sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28) { /* Hash the subkey binding/backsig/revocation. */ hash_public_key (md, subpk); } else if (sigclass != 0x1F && sigclass != 0x20) { /* Hash the user id. */ hash_uid (md, sigversion, uid); } /* Make the signature packet. */ sig = xmalloc_clear (sizeof *sig); sig->version = sigversion; sig->flags.exportable = 1; sig->flags.revocable = 1; keyid_from_pk (pksk, sig->keyid); sig->pubkey_algo = pksk->pubkey_algo; sig->digest_algo = digest_algo; sig->timestamp = timestamp? timestamp : make_timestamp (); if (duration) sig->expiredate = sig->timestamp + duration; sig->sig_class = sigclass; build_sig_subpkt_from_sig (sig, pksk); - mk_notation_policy_etc (sig, pk, pksk); + mk_notation_policy_etc (ctrl, sig, pk, pksk); /* Crucial that the call to mksubpkt comes LAST before the calls * to finalize the sig as that makes it possible for the mksubpkt * function to get a reliable pointer to the subpacket area. */ if (mksubpkt) rc = (*mksubpkt)(sig, opaque); if (!rc) { hash_sigversion_to_magic (md, sig, NULL); gcry_md_final (md); rc = complete_sig (ctrl, sig, pksk, md, cache_nonce); } gcry_md_close (md); if (rc) free_seckey_enc (sig); else *ret_sig = sig; return rc; } /* * Create a new signature packet based on an existing one. * Only user ID signatures are supported for now. * PK is the public key to work on. * PKSK is the key used to make the signature. * * TODO: Merge this with make_keysig_packet. */ gpg_error_t update_keysig_packet (ctrl_t ctrl, PKT_signature **ret_sig, PKT_signature *orig_sig, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *subpk, PKT_public_key *pksk, int (*mksubpkt)(PKT_signature *, void *), void *opaque) { PKT_signature *sig; gpg_error_t rc = 0; int digest_algo; gcry_md_hd_t md; if ((!orig_sig || !pk || !pksk) || (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid) || (orig_sig->sig_class == 0x18 && !subpk)) return GPG_ERR_GENERAL; /* Either use the override digest algo or in the normal case the * original digest algorithm. However, iff the original digest * algorithms is SHA-1 and we are in gnupg or de-vs compliance mode * we switch to SHA-256 (done by the macro). */ if (opt.cert_digest_algo) digest_algo = opt.cert_digest_algo; else if (pksk->pubkey_algo == PUBKEY_ALGO_DSA || pksk->pubkey_algo == PUBKEY_ALGO_ECDSA || pksk->pubkey_algo == PUBKEY_ALGO_EDDSA) digest_algo = orig_sig->digest_algo; else if (orig_sig->digest_algo == DIGEST_ALGO_SHA1 || orig_sig->digest_algo == DIGEST_ALGO_RMD160) digest_algo = DEFAULT_DIGEST_ALGO; else digest_algo = orig_sig->digest_algo; if (gcry_md_open (&md, digest_algo, 0)) BUG (); /* Hash the public key certificate and the user id. */ hash_public_key (md, pk); if (orig_sig->sig_class == 0x18) hash_public_key (md, subpk); else hash_uid (md, orig_sig->version, uid); /* Create a new signature packet. */ sig = copy_signature (NULL, orig_sig); sig->digest_algo = digest_algo; /* We need to create a new timestamp so that new sig expiration * calculations are done correctly... */ sig->timestamp = make_timestamp(); /* ... but we won't make a timestamp earlier than the existing * one. */ { int tmout = 0; while (sig->timestamp <= orig_sig->timestamp) { if (++tmout > 5 && !opt.ignore_time_conflict) { rc = gpg_error (GPG_ERR_TIME_CONFLICT); goto leave; } gnupg_sleep (1); sig->timestamp = make_timestamp(); } } /* Note that already expired sigs will remain expired (with a * duration of 1) since build-packet.c:build_sig_subpkt_from_sig * detects this case. */ /* Put the updated timestamp into the sig. Note that this will * automagically lower any sig expiration dates to correctly * correspond to the differences in the timestamps (i.e. the * duration will shrink). */ build_sig_subpkt_from_sig (sig, pksk); if (mksubpkt) rc = (*mksubpkt)(sig, opaque); if (!rc) { hash_sigversion_to_magic (md, sig, NULL); gcry_md_final (md); rc = complete_sig (ctrl, sig, pksk, md, NULL); } leave: gcry_md_close (md); if (rc) free_seckey_enc (sig); else *ret_sig = sig; return rc; } diff --git a/g10/t-keydb-get-keyblock.c b/g10/t-keydb-get-keyblock.c index 167a9bbee..90ce6e9a6 100644 --- a/g10/t-keydb-get-keyblock.c +++ b/g10/t-keydb-get-keyblock.c @@ -1,65 +1,69 @@ /* t-keydb-get-keyblock.c - Tests for keydb.c. * Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "test.c" #include "keydb.h" + static void do_test (int argc, char *argv[]) { + ctrl_t ctrl; char *fname; int rc; KEYDB_HANDLE hd1; KEYDB_SEARCH_DESC desc1; KBNODE kb1; (void) argc; (void) argv; + ctrl = xcalloc (1, sizeof *ctrl); /* t-keydb-get-keyblock.gpg contains two keys: a modern key followed by a legacy key. If we get the keyblock for the modern key, we shouldn't get - */ fname = prepend_srcdir ("t-keydb-get-keyblock.gpg"); rc = keydb_add_resource (fname, 0); test_free (fname); if (rc) ABORT ("Failed to open keyring."); - hd1 = keydb_new (); + hd1 = keydb_new (ctrl); if (!hd1) ABORT (""); rc = classify_user_id ("8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367", &desc1, 0); if (rc) ABORT ("Failed to convert fingerprint for 1E42B367"); rc = keydb_search (hd1, &desc1, 1, NULL); if (rc) ABORT ("Failed to lookup key associated with 1E42B367"); rc = keydb_get_keyblock (hd1, &kb1); TEST_P ("", ! rc); keydb_release (hd1); release_kbnode (kb1); + xfree (ctrl); } diff --git a/g10/t-keydb.c b/g10/t-keydb.c index 5eb8d0154..4c78dac48 100644 --- a/g10/t-keydb.c +++ b/g10/t-keydb.c @@ -1,104 +1,107 @@ /* t-keydb.c - Tests for keydb.c. * Copyright (C) 2015 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include "test.c" #include "keydb.h" static void do_test (int argc, char *argv[]) { int rc; + ctrl_t ctrl; KEYDB_HANDLE hd1, hd2; KEYDB_SEARCH_DESC desc1, desc2; KBNODE kb1, kb2, p; char *uid1; char *uid2; char *fname; (void) argc; (void) argv; + ctrl = xcalloc (1, sizeof *ctrl); fname = prepend_srcdir ("t-keydb-keyring.kbx"); rc = keydb_add_resource (fname, 0); test_free (fname); if (rc) ABORT ("Failed to open keyring."); - hd1 = keydb_new (); + hd1 = keydb_new (ctrl); if (!hd1) ABORT (""); - hd2 = keydb_new (); + hd2 = keydb_new (ctrl); if (!hd2) ABORT (""); rc = classify_user_id ("2689 5E25 E844 6D44 A26D 8FAF 2F79 98F3 DBFC 6AD9", &desc1, 0); if (rc) ABORT ("Failed to convert fingerprint for DBFC6AD9"); rc = keydb_search (hd1, &desc1, 1, NULL); if (rc) ABORT ("Failed to lookup key associated with DBFC6AD9"); classify_user_id ("8061 5870 F5BA D690 3336 86D0 F2AD 85AC 1E42 B367", &desc2, 0); if (rc) ABORT ("Failed to convert fingerprint for 1E42B367"); rc = keydb_search (hd2, &desc2, 1, NULL); if (rc) ABORT ("Failed to lookup key associated with 1E42B367"); rc = keydb_get_keyblock (hd2, &kb2); if (rc) ABORT ("Failed to get keyblock for 1E42B367"); rc = keydb_get_keyblock (hd1, &kb1); if (rc) ABORT ("Failed to get keyblock for DBFC6AD9"); p = kb1; while (p && p->pkt->pkttype != PKT_USER_ID) p = p->next; if (! p) ABORT ("DBFC6AD9 has no user id packet"); uid1 = p->pkt->pkt.user_id->name; p = kb2; while (p && p->pkt->pkttype != PKT_USER_ID) p = p->next; if (! p) ABORT ("1E42B367 has no user id packet"); uid2 = p->pkt->pkt.user_id->name; if (verbose) { printf ("user id for DBFC6AD9: %s\n", uid1); printf ("user id for 1E42B367: %s\n", uid2); } TEST_P ("cache consistency", strcmp (uid1, uid2) != 0); release_kbnode (kb1); release_kbnode (kb2); keydb_release (hd1); keydb_release (hd2); + xfree (ctrl); } diff --git a/g10/tofu.c b/g10/tofu.c index e78da15c1..2eda1ff30 100644 --- a/g10/tofu.c +++ b/g10/tofu.c @@ -1,4037 +1,4037 @@ /* tofu.c - TOFU trust model. * Copyright (C) 2015, 2016 g10 Code GmbH * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ /* TODO: - Format the fingerprints nicely when printing (similar to gpg --list-keys) */ #include #include #include #include #include #include #include "gpg.h" #include "../common/types.h" #include "../common/logging.h" #include "../common/stringhelp.h" #include "options.h" #include "../common/mbox-util.h" #include "../common/i18n.h" #include "../common/ttyio.h" #include "trustdb.h" #include "../common/mkdir_p.h" #include "gpgsql.h" #include "../common/status.h" #include "tofu.h" #define CONTROL_L ('L' - 'A' + 1) /* Number of days with signed / ecnrypted messages required to * indicate that enough history is available for basic trust. */ #define BASIC_TRUST_THRESHOLD 4 /* Number of days with signed / encrypted messages required to * indicate that a lot of history is available. */ #define FULL_TRUST_THRESHOLD 21 /* A struct with data pertaining to the tofu DB. There is one such struct per session and it is cached in session's ctrl structure. To initialize this or get the current singleton, call opendbs(). There is no need to explicitly release it; cleanup is done when the CTRL object is released. */ struct tofu_dbs_s { sqlite3 *db; char *want_lock_file; time_t want_lock_file_ctime; struct { sqlite3_stmt *savepoint_batch; sqlite3_stmt *savepoint_batch_commit; sqlite3_stmt *record_binding_get_old_policy; sqlite3_stmt *record_binding_update; sqlite3_stmt *get_policy_select_policy_and_conflict; sqlite3_stmt *get_trust_bindings_with_this_email; sqlite3_stmt *get_trust_gather_other_user_ids; sqlite3_stmt *get_trust_gather_signature_stats; sqlite3_stmt *get_trust_gather_encryption_stats; sqlite3_stmt *register_already_seen; sqlite3_stmt *register_signature; sqlite3_stmt *register_encryption; } s; int in_batch_transaction; int in_transaction; time_t batch_update_started; }; #define STRINGIFY(s) STRINGIFY2(s) #define STRINGIFY2(s) #s /* The grouping parameters when collecting signature statistics. */ /* If a message is signed a couple of hours in the future, just assume some clock skew. */ #define TIME_AGO_FUTURE_IGNORE (2 * 60 * 60) /* Days. */ #define TIME_AGO_UNIT_SMALL (24 * 60 * 60) #define TIME_AGO_SMALL_THRESHOLD (7 * TIME_AGO_UNIT_SMALL) /* Months. */ #define TIME_AGO_UNIT_MEDIUM (30 * 24 * 60 * 60) #define TIME_AGO_MEDIUM_THRESHOLD (2 * TIME_AGO_UNIT_MEDIUM) /* Years. */ #define TIME_AGO_UNIT_LARGE (365 * 24 * 60 * 60) #define TIME_AGO_LARGE_THRESHOLD (2 * TIME_AGO_UNIT_LARGE) /* Local prototypes. */ static gpg_error_t end_transaction (ctrl_t ctrl, int only_batch); static char *email_from_user_id (const char *user_id); static int show_statistics (tofu_dbs_t dbs, const char *fingerprint, const char *email, enum tofu_policy policy, estream_t outfp, int only_status_fd, time_t now); const char * tofu_policy_str (enum tofu_policy policy) { switch (policy) { case TOFU_POLICY_NONE: return "none"; case TOFU_POLICY_AUTO: return "auto"; case TOFU_POLICY_GOOD: return "good"; case TOFU_POLICY_UNKNOWN: return "unknown"; case TOFU_POLICY_BAD: return "bad"; case TOFU_POLICY_ASK: return "ask"; default: return "???"; } } /* Convert a binding policy (e.g., TOFU_POLICY_BAD) to a trust level (e.g., TRUST_BAD) in light of the current configuration. */ int tofu_policy_to_trust_level (enum tofu_policy policy) { if (policy == TOFU_POLICY_AUTO) /* If POLICY is AUTO, fallback to OPT.TOFU_DEFAULT_POLICY. */ policy = opt.tofu_default_policy; switch (policy) { case TOFU_POLICY_AUTO: /* If POLICY and OPT.TOFU_DEFAULT_POLICY are both AUTO, default to marginal trust. */ return TRUST_MARGINAL; case TOFU_POLICY_GOOD: return TRUST_FULLY; case TOFU_POLICY_UNKNOWN: return TRUST_UNKNOWN; case TOFU_POLICY_BAD: return TRUST_NEVER; case TOFU_POLICY_ASK: return TRUST_UNKNOWN; default: log_bug ("Bad value for trust policy: %d\n", opt.tofu_default_policy); return 0; } } /* Start a transaction on DB. If ONLY_BATCH is set, then this will start a batch transaction if we haven't started a batch transaction and one has been requested. */ static gpg_error_t begin_transaction (ctrl_t ctrl, int only_batch) { tofu_dbs_t dbs = ctrl->tofu.dbs; int rc; char *err = NULL; log_assert (dbs); /* If we've been in batch update mode for a while (on average, more * than 500 ms), to prevent starving other gpg processes, we drop * and retake the batch lock. * * Note: gnupg_get_time has a one second resolution, if we wanted a * higher resolution, we could use npth_clock_gettime. */ if (/* No real transactions. */ dbs->in_transaction == 0 /* There is an open batch transaction. */ && dbs->in_batch_transaction /* And some time has gone by since it was started. */ && dbs->batch_update_started != gnupg_get_time ()) { struct stat statbuf; /* If we are in a batch update, then batch updates better have been enabled. */ log_assert (ctrl->tofu.batch_updated_wanted); /* Check if another process wants to run. (We just ignore any * stat failure. A waiter might have to wait a bit longer, but * otherwise there should be no impact.) */ if (stat (dbs->want_lock_file, &statbuf) == 0 && statbuf.st_ctime != dbs->want_lock_file_ctime) { end_transaction (ctrl, 2); /* Yield to allow another process a chance to run. Note: * testing suggests that anything less than a 100ms tends to * not result in the other process getting the lock. */ gnupg_usleep (100000); } else dbs->batch_update_started = gnupg_get_time (); } if (/* We don't have an open batch transaction. */ !dbs->in_batch_transaction && (/* Batch mode is enabled or we are starting a new transaction. */ ctrl->tofu.batch_updated_wanted || dbs->in_transaction == 0)) { struct stat statbuf; /* We are in batch mode, but we don't have an open batch * transaction. Since the batch save point must be the outer * save point, it must be taken before the inner save point. */ log_assert (dbs->in_transaction == 0); rc = gpgsql_stepx (dbs->db, &dbs->s.savepoint_batch, NULL, NULL, &err, "begin immediate transaction;", GPGSQL_ARG_END); if (rc) { log_error (_("error beginning transaction on TOFU database: %s\n"), err); sqlite3_free (err); return gpg_error (GPG_ERR_GENERAL); } dbs->in_batch_transaction = 1; dbs->batch_update_started = gnupg_get_time (); if (stat (dbs->want_lock_file, &statbuf) == 0) dbs->want_lock_file_ctime = statbuf.st_ctime; } if (only_batch) return 0; log_assert (dbs->in_transaction >= 0); dbs->in_transaction ++; rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err, "savepoint inner%d;", dbs->in_transaction); if (rc) { log_error (_("error beginning transaction on TOFU database: %s\n"), err); sqlite3_free (err); return gpg_error (GPG_ERR_GENERAL); } return 0; } /* Commit a transaction. If ONLY_BATCH is 1, then this only ends the * batch transaction if we have left batch mode. If ONLY_BATCH is 2, * this commits any open batch transaction even if we are still in * batch mode. */ static gpg_error_t end_transaction (ctrl_t ctrl, int only_batch) { tofu_dbs_t dbs = ctrl->tofu.dbs; int rc; char *err = NULL; if (only_batch || (! only_batch && dbs->in_transaction == 1)) { if (!dbs) return 0; /* Shortcut to allow for easier cleanup code. */ /* If we are releasing the batch transaction, then we better not be in a normal transaction. */ if (only_batch) log_assert (dbs->in_transaction == 0); if (/* Batch mode disabled? */ (!ctrl->tofu.batch_updated_wanted || only_batch == 2) /* But, we still have an open batch transaction? */ && dbs->in_batch_transaction) { /* The batch transaction is still in open, but we've left * batch mode. */ dbs->in_batch_transaction = 0; dbs->in_transaction = 0; rc = gpgsql_stepx (dbs->db, &dbs->s.savepoint_batch_commit, NULL, NULL, &err, "commit transaction;", GPGSQL_ARG_END); if (rc) { log_error (_("error committing transaction on TOFU database: %s\n"), err); sqlite3_free (err); return gpg_error (GPG_ERR_GENERAL); } return 0; } if (only_batch) return 0; } log_assert (dbs); log_assert (dbs->in_transaction > 0); rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err, "release inner%d;", dbs->in_transaction); dbs->in_transaction --; if (rc) { log_error (_("error committing transaction on TOFU database: %s\n"), err); sqlite3_free (err); return gpg_error (GPG_ERR_GENERAL); } return 0; } static gpg_error_t rollback_transaction (ctrl_t ctrl) { tofu_dbs_t dbs = ctrl->tofu.dbs; int rc; char *err = NULL; log_assert (dbs); log_assert (dbs->in_transaction > 0); /* Be careful to not undo any progress made by closed transactions in batch mode. */ rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &err, "rollback to inner%d;", dbs->in_transaction); dbs->in_transaction --; if (rc) { log_error (_("error rolling back transaction on TOFU database: %s\n"), err); sqlite3_free (err); return gpg_error (GPG_ERR_GENERAL); } return 0; } void tofu_begin_batch_update (ctrl_t ctrl) { ctrl->tofu.batch_updated_wanted ++; } void tofu_end_batch_update (ctrl_t ctrl) { log_assert (ctrl->tofu.batch_updated_wanted > 0); ctrl->tofu.batch_updated_wanted --; end_transaction (ctrl, 1); } /* Suspend any extant batch transaction (it is safe to call this even no batch transaction has been started). Note: you cannot suspend a batch transaction if you are in a normal transaction. The batch transaction can be resumed explicitly by calling tofu_resume_batch_transaction or implicitly by starting a normal transaction. */ static void tofu_suspend_batch_transaction (ctrl_t ctrl) { end_transaction (ctrl, 2); } /* Resume a batch transaction if there is no extant batch transaction and one has been requested using tofu_begin_batch_transaction. */ static void tofu_resume_batch_transaction (ctrl_t ctrl) { begin_transaction (ctrl, 1); } /* Wrapper around strtol which prints a warning in case of a * conversion error. On success the converted value is stored at * R_VALUE and 0 is returned; on error FALLBACK is stored at R_VALUE * and an error code is returned. */ static gpg_error_t string_to_long (long *r_value, const char *string, long fallback, int line) { gpg_error_t err; char *tail = NULL; gpg_err_set_errno (0); *r_value = strtol (string, &tail, 0); if (errno || !(!strcmp (tail, ".0") || !*tail)) { err = errno? gpg_error_from_errno (errno) : gpg_error (GPG_ERR_BAD_DATA); log_debug ("%s:%d: strtol failed for TOFU DB data; returned string" " (string='%.10s%s'; tail='%.10s%s'): %s\n", __FILE__, line, string, string && strlen(string) > 10 ? "..." : "", tail, tail && strlen(tail) > 10 ? "..." : "", gpg_strerror (err)); *r_value = fallback; } else err = 0; return err; } /* Wrapper around strtoul which prints a warning in case of a * conversion error. On success the converted value is stored at * R_VALUE and 0 is returned; on error FALLBACK is stored at R_VALUE * and an error code is returned. */ static gpg_error_t string_to_ulong (unsigned long *r_value, const char *string, unsigned long fallback, int line) { gpg_error_t err; char *tail = NULL; gpg_err_set_errno (0); *r_value = strtoul (string, &tail, 0); if (errno || !(!strcmp (tail, ".0") || !*tail)) { err = errno? gpg_error_from_errno (errno) : gpg_error (GPG_ERR_BAD_DATA); log_debug ("%s:%d: strtoul failed for TOFU DB data; returned string" " (string='%.10s%s'; tail='%.10s%s'): %s\n", __FILE__, line, string, string && strlen(string) > 10 ? "..." : "", tail, tail && strlen(tail) > 10 ? "..." : "", gpg_strerror (err)); *r_value = fallback; } else err = 0; return err; } /* Collect results of a select count (*) ...; style query. Aborts if the argument is not a valid integer (or real of the form X.0). */ static int get_single_unsigned_long_cb (void *cookie, int argc, char **argv, char **azColName) { unsigned long int *count = cookie; (void) azColName; log_assert (argc == 1); if (string_to_ulong (count, argv[0], 0, __LINE__)) return 1; /* Abort. */ return 0; } static int get_single_unsigned_long_cb2 (void *cookie, int argc, char **argv, char **azColName, sqlite3_stmt *stmt) { (void) stmt; return get_single_unsigned_long_cb (cookie, argc, argv, azColName); } /* We expect a single integer column whose name is "version". COOKIE must point to an int. This function always aborts. On error or a if the version is bad, sets *VERSION to -1. */ static int version_check_cb (void *cookie, int argc, char **argv, char **azColName) { int *version = cookie; if (argc != 1 || strcmp (azColName[0], "version") != 0) { *version = -1; return 1; } if (strcmp (argv[0], "1") == 0) *version = 1; else { log_error (_("unsupported TOFU database version: %s\n"), argv[0]); *version = -1; } /* Don't run again. */ return 1; } static int check_utks (sqlite3 *db) { int rc; char *err = NULL; struct key_item *utks; struct key_item *ki; int utk_count; char *utks_string = NULL; char keyid_str[16+1]; long utks_unchanged = 0; /* An early version of the v1 format did not include the list of * known ultimately trusted keys. * * This list is used to detect when the set of ultimately trusted * keys changes. We need to detect this to invalidate the effective * policy, which can change if an ultimately trusted key is added or * removed. */ rc = sqlite3_exec (db, "create table if not exists ultimately_trusted_keys" " (keyid);\n", NULL, NULL, &err); if (rc) { log_error ("error creating 'ultimately_trusted_keys' TOFU table: %s\n", err); sqlite3_free (err); goto out; } utks = tdb_utks (); for (ki = utks, utk_count = 0; ki; ki = ki->next, utk_count ++) ; if (utk_count) { /* Build a list of keyids of the form "XXX","YYY","ZZZ". */ int len = (1 + 16 + 1 + 1) * utk_count; int o = 0; utks_string = xmalloc (len); *utks_string = 0; for (ki = utks, utk_count = 0; ki; ki = ki->next, utk_count ++) { utks_string[o ++] = '\''; format_keyid (ki->kid, KF_LONG, keyid_str, sizeof (keyid_str)); memcpy (&utks_string[o], keyid_str, 16); o += 16; utks_string[o ++] = '\''; utks_string[o ++] = ','; } utks_string[o - 1] = 0; log_assert (o == len); } rc = gpgsql_exec_printf (db, get_single_unsigned_long_cb, &utks_unchanged, &err, "select" /* Removed UTKs? (Known UTKs in current UTKs.) */ " ((select count(*) from ultimately_trusted_keys" " where (keyid in (%s))) == %d)" " and" /* New UTKs? */ " ((select count(*) from ultimately_trusted_keys" " where keyid not in (%s)) == 0);", utks_string ? utks_string : "", utk_count, utks_string ? utks_string : ""); xfree (utks_string); if (rc) { log_error (_("TOFU DB error")); print_further_info ("checking if ultimately trusted keys changed: %s", err); sqlite3_free (err); goto out; } if (utks_unchanged) goto out; if (DBG_TRUST) log_debug ("TOFU: ultimately trusted keys changed.\n"); /* Given that the set of ultimately trusted keys * changed, clear any cached policies. */ rc = gpgsql_exec_printf (db, NULL, NULL, &err, "update bindings set effective_policy = %d;", TOFU_POLICY_NONE); if (rc) { log_error (_("TOFU DB error")); print_further_info ("clearing cached policies: %s", err); sqlite3_free (err); goto out; } /* Now, update the UTK table. */ rc = sqlite3_exec (db, "drop table ultimately_trusted_keys;", NULL, NULL, &err); if (rc) { log_error (_("TOFU DB error")); print_further_info ("dropping ultimately_trusted_keys: %s", err); sqlite3_free (err); goto out; } rc = sqlite3_exec (db, "create table if not exists" " ultimately_trusted_keys (keyid);\n", NULL, NULL, &err); if (rc) { log_error (_("TOFU DB error")); print_further_info ("creating ultimately_trusted_keys: %s", err); sqlite3_free (err); goto out; } for (ki = utks; ki; ki = ki->next) { format_keyid (ki->kid, KF_LONG, keyid_str, sizeof (keyid_str)); rc = gpgsql_exec_printf (db, NULL, NULL, &err, "insert into ultimately_trusted_keys values ('%s');", keyid_str); if (rc) { log_error (_("TOFU DB error")); print_further_info ("updating ultimately_trusted_keys: %s", err); sqlite3_free (err); goto out; } } out: return rc; } /* If the DB is new, initialize it. Otherwise, check the DB's version. Return 0 if the database is okay and 1 otherwise. */ static int initdb (sqlite3 *db) { char *err = NULL; int rc; unsigned long int count; int version = -1; rc = sqlite3_exec (db, "begin transaction;", NULL, NULL, &err); if (rc) { log_error (_("error beginning transaction on TOFU database: %s\n"), err); sqlite3_free (err); return 1; } /* If the DB has no tables, then assume this is a new DB that needs to be initialized. */ rc = sqlite3_exec (db, "select count(*) from sqlite_master where type='table';", get_single_unsigned_long_cb, &count, &err); if (rc) { log_error (_("error reading TOFU database: %s\n"), err); print_further_info ("query available tables"); sqlite3_free (err); goto out; } else if (count != 0) /* Assume that the DB is already initialized. Make sure the version is okay. */ { rc = sqlite3_exec (db, "select version from version;", version_check_cb, &version, &err); if (rc == SQLITE_ABORT && version == 1) /* Happy, happy, joy, joy. */ { sqlite3_free (err); rc = 0; goto out; } else if (rc == SQLITE_ABORT && version == -1) /* Unsupported version. */ { /* An error message was already displayed. */ sqlite3_free (err); goto out; } else if (rc) /* Some error. */ { log_error (_("error determining TOFU database's version: %s\n"), err); sqlite3_free (err); goto out; } else { /* Unexpected success. This can only happen if there are no rows. (select returned 0, but expected ABORT.) */ log_error (_("error determining TOFU database's version: %s\n"), gpg_strerror (GPG_ERR_NO_DATA)); rc = 1; goto out; } } /* Create the version table. */ rc = sqlite3_exec (db, "create table version (version INTEGER);", NULL, NULL, &err); if (rc) { log_error (_("error initializing TOFU database: %s\n"), err); print_further_info ("create version"); sqlite3_free (err); goto out; } /* Initialize the version table, which contains a single integer value. */ rc = sqlite3_exec (db, "insert into version values (1);", NULL, NULL, &err); if (rc) { log_error (_("error initializing TOFU database: %s\n"), err); print_further_info ("insert version"); sqlite3_free (err); goto out; } /* The list of bindings and auxiliary data. * * OID is a unique ID identifying this binding (and used by the * signatures table, see below). Note: OIDs will never be * reused. * * FINGERPRINT: The key's fingerprint. * * EMAIL: The normalized email address. * * USER_ID: The unmodified user id from which EMAIL was extracted. * * TIME: The time this binding was first observed. * * POLICY: The trust policy (TOFU_POLICY_BAD, etc. as an integer). * * CONFLICT is either NULL or a fingerprint. Assume that we have * a binding <0xdeadbeef, foo@example.com> and then we observe * <0xbaddecaf, foo@example.com>. There two bindings conflict * (they have the same email address). When we observe the * latter binding, we warn the user about the conflict and ask * for a policy decision about the new binding. We also change * the old binding's policy to ask if it was auto. So that we * know why this occurred, we also set conflict to 0xbaddecaf. */ rc = gpgsql_exec_printf (db, NULL, NULL, &err, "create table bindings\n" " (oid INTEGER PRIMARY KEY AUTOINCREMENT,\n" " fingerprint TEXT, email TEXT, user_id TEXT, time INTEGER,\n" " policy INTEGER CHECK (policy in (%d, %d, %d, %d, %d)),\n" " conflict STRING,\n" " unique (fingerprint, email));\n" "create index bindings_fingerprint_email\n" " on bindings (fingerprint, email);\n" "create index bindings_email on bindings (email);\n", TOFU_POLICY_AUTO, TOFU_POLICY_GOOD, TOFU_POLICY_UNKNOWN, TOFU_POLICY_BAD, TOFU_POLICY_ASK); if (rc) { log_error (_("error initializing TOFU database: %s\n"), err); print_further_info ("create bindings"); sqlite3_free (err); goto out; } /* The signatures that we have observed. * * BINDING refers to a record in the bindings table, which * describes the binding (i.e., this is a foreign key that * references bindings.oid). * * SIG_DIGEST is the digest stored in the signature. * * SIG_TIME is the timestamp stored in the signature. * * ORIGIN is a free-form string that describes who fed this * signature to GnuPG (e.g., email:claws). * * TIME is the time this signature was registered. */ rc = sqlite3_exec (db, "create table signatures " " (binding INTEGER NOT NULL, sig_digest TEXT," " origin TEXT, sig_time INTEGER, time INTEGER," " primary key (binding, sig_digest, origin));", NULL, NULL, &err); if (rc) { log_error (_("error initializing TOFU database: %s\n"), err); print_further_info ("create signatures"); sqlite3_free (err); goto out; } out: if (! rc) { /* Early version of the v1 format did not include the encryption table. Add it. */ rc = sqlite3_exec (db, "create table if not exists encryptions" " (binding INTEGER NOT NULL," " time INTEGER);" "create index if not exists encryptions_binding" " on encryptions (binding);\n", NULL, NULL, &err); if (rc) { log_error ("error creating 'encryptions' TOFU table: %s\n", err); sqlite3_free (err); } } if (! rc) { /* The effective policy for a binding. If a key is ultimately * trusted, then the effective policy of all of its bindings is * good. Likewise if a key is signed by an ultimately trusted * key, etc. If the effective policy is NONE, then we need to * recompute the effective policy. Otherwise, the effective * policy is considered to be up to date, i.e., effective_policy * is a cache of the computed policy. */ rc = gpgsql_exec_printf (db, NULL, NULL, &err, "alter table bindings" " add column effective_policy INTEGER" " DEFAULT %d" " CHECK (effective_policy in (%d, %d, %d, %d, %d, %d));", TOFU_POLICY_NONE, TOFU_POLICY_NONE, TOFU_POLICY_AUTO, TOFU_POLICY_GOOD, TOFU_POLICY_UNKNOWN, TOFU_POLICY_BAD, TOFU_POLICY_ASK); if (rc) { if (rc == SQLITE_ERROR) /* Almost certainly "duplicate column name", which we can * safely ignore. */ rc = 0; else log_error ("adding column effective_policy to bindings DB: %s\n", err); sqlite3_free (err); } } if (! rc) rc = check_utks (db); if (rc) { rc = sqlite3_exec (db, "rollback;", NULL, NULL, &err); if (rc) { log_error (_("error rolling back transaction on TOFU database: %s\n"), err); sqlite3_free (err); } return 1; } else { rc = sqlite3_exec (db, "end transaction;", NULL, NULL, &err); if (rc) { log_error (_("error committing transaction on TOFU database: %s\n"), err); sqlite3_free (err); return 1; } return 0; } } static int busy_handler (void *cookie, int call_count) { ctrl_t ctrl = cookie; tofu_dbs_t dbs = ctrl->tofu.dbs; (void) call_count; /* Update the want-lock-file time stamp (specifically, the ctime) so * that the current owner knows that we (well, someone) want the * lock. */ if (dbs) { /* Note: we don't fail if we can't create the lock file: this * process will have to wait a bit longer, but otherwise nothing * horrible should happen. */ estream_t fp; fp = es_fopen (dbs->want_lock_file, "w"); if (! fp) log_debug ("TOFU: Error opening '%s': %s\n", dbs->want_lock_file, strerror (errno)); else es_fclose (fp); } /* Call again. */ return 1; } /* Create a new DB handle. Returns NULL on error. */ /* FIXME: Change to return an error code for better reporting by the caller. */ static tofu_dbs_t opendbs (ctrl_t ctrl) { char *filename; sqlite3 *db; int rc; if (!ctrl->tofu.dbs) { filename = make_filename (gnupg_homedir (), "tofu.db", NULL); rc = sqlite3_open (filename, &db); if (rc) { log_error (_("error opening TOFU database '%s': %s\n"), filename, sqlite3_errmsg (db)); /* Even if an error occurs, DB is guaranteed to be valid. */ sqlite3_close (db); db = NULL; } /* If a DB is locked wait up to 5 seconds for the lock to be cleared before failing. */ if (db) { sqlite3_busy_timeout (db, 5 * 1000); sqlite3_busy_handler (db, busy_handler, ctrl); } if (db && initdb (db)) { sqlite3_close (db); db = NULL; } if (db) { ctrl->tofu.dbs = xmalloc_clear (sizeof *ctrl->tofu.dbs); ctrl->tofu.dbs->db = db; ctrl->tofu.dbs->want_lock_file = xasprintf ("%s-want-lock", filename); } xfree (filename); } else log_assert (ctrl->tofu.dbs->db); return ctrl->tofu.dbs; } /* Release all of the resources associated with the DB handle. */ void tofu_closedbs (ctrl_t ctrl) { tofu_dbs_t dbs; sqlite3_stmt **statements; dbs = ctrl->tofu.dbs; if (!dbs) return; /* Not initialized. */ log_assert (dbs->in_transaction == 0); end_transaction (ctrl, 2); /* Arghh, that is a surprising use of the struct. */ for (statements = (void *) &dbs->s; (void *) statements < (void *) &(&dbs->s)[1]; statements ++) sqlite3_finalize (*statements); sqlite3_close (dbs->db); xfree (dbs->want_lock_file); xfree (dbs); ctrl->tofu.dbs = NULL; } /* Collect results of a select min (foo) ...; style query. Aborts if the argument is not a valid integer (or real of the form X.0). */ static int get_single_long_cb (void *cookie, int argc, char **argv, char **azColName) { long *count = cookie; (void) azColName; log_assert (argc == 1); if (string_to_long (count, argv[0], 0, __LINE__)) return 1; /* Abort. */ return 0; } static int get_single_long_cb2 (void *cookie, int argc, char **argv, char **azColName, sqlite3_stmt *stmt) { (void) stmt; return get_single_long_cb (cookie, argc, argv, azColName); } /* Record (or update) a trust policy about a (possibly new) binding. If SHOW_OLD is set, the binding's old policy is displayed. */ static gpg_error_t record_binding (tofu_dbs_t dbs, const char *fingerprint, const char *email, const char *user_id, enum tofu_policy policy, enum tofu_policy effective_policy, const char *conflict, int set_conflict, int show_old, time_t now) { char *fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0); gpg_error_t rc; char *err = NULL; if (! (policy == TOFU_POLICY_AUTO || policy == TOFU_POLICY_GOOD || policy == TOFU_POLICY_UNKNOWN || policy == TOFU_POLICY_BAD || policy == TOFU_POLICY_ASK)) log_bug ("%s: Bad value for policy (%d)!\n", __func__, policy); if (DBG_TRUST || show_old) { /* Get the old policy. Since this is just for informational * purposes, there is no need to start a transaction or to die * if there is a failure. */ /* policy_old needs to be a long and not an enum tofu_policy, because we pass it by reference to get_single_long_cb2, which expects a long. */ long policy_old = TOFU_POLICY_NONE; rc = gpgsql_stepx (dbs->db, &dbs->s.record_binding_get_old_policy, get_single_long_cb2, &policy_old, &err, "select policy from bindings where fingerprint = ? and email = ?", GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, GPGSQL_ARG_END); if (rc) { log_debug ("TOFU: Error reading from binding database" " (reading policy for ): %s\n", fingerprint, email, err); sqlite3_free (err); } if (policy_old != TOFU_POLICY_NONE) (show_old ? log_info : log_debug) ("Changing TOFU trust policy for binding" " from %s to %s.\n", fingerprint, show_old ? user_id : email, tofu_policy_str (policy_old), tofu_policy_str (policy)); else (show_old ? log_info : log_debug) ("Setting TOFU trust policy for new binding" " to %s.\n", fingerprint, show_old ? user_id : email, tofu_policy_str (policy)); } if (opt.dry_run) { log_info ("TOFU database update skipped due to --dry-run\n"); rc = 0; goto leave; } rc = gpgsql_stepx (dbs->db, &dbs->s.record_binding_update, NULL, NULL, &err, "insert or replace into bindings\n" " (oid, fingerprint, email, user_id, time," " policy, conflict, effective_policy)\n" " values (\n" /* If we don't explicitly reuse the OID, then SQLite will * reallocate a new one. We just need to search for the OID * based on the fingerprint and email since they are unique. */ " (select oid from bindings where fingerprint = ? and email = ?),\n" " ?, ?, ?, ?, ?," /* If SET_CONFLICT is 0, then preserve conflict's current value. */ " case ?" " when 0 then" " (select conflict from bindings where fingerprint = ? and email = ?)" " else ?" " end," " ?);", /* oid subquery. */ GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, /* values 2 through 6. */ GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, GPGSQL_ARG_STRING, user_id, GPGSQL_ARG_LONG_LONG, (long long) now, GPGSQL_ARG_INT, (int) policy, /* conflict subquery. */ GPGSQL_ARG_INT, set_conflict ? 1 : 0, GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, GPGSQL_ARG_STRING, conflict ? conflict : "", GPGSQL_ARG_INT, (int) effective_policy, GPGSQL_ARG_END); if (rc) { log_error (_("error updating TOFU database: %s\n"), err); print_further_info (" insert bindings = %s", fingerprint, email, tofu_policy_str (policy)); sqlite3_free (err); goto leave; } leave: xfree (fingerprint_pp); return rc; } /* Collect the strings returned by a query in a simple string list. Any NULL values are converted to the empty string. If a result has 3 rows and each row contains two columns, then the results are added to the list as follows (the value is parentheses is the 1-based index in the final list): row 1, col 2 (6) row 1, col 1 (5) row 2, col 2 (4) row 2, col 1 (3) row 3, col 2 (2) row 3, col 1 (1) This is because add_to_strlist pushes the results onto the front of the list. The end result is that the rows are backwards, but the columns are in the expected order. */ static int strings_collect_cb (void *cookie, int argc, char **argv, char **azColName) { int i; strlist_t *strlist = cookie; (void) azColName; for (i = argc - 1; i >= 0; i --) add_to_strlist (strlist, argv[i] ? argv[i] : ""); return 0; } static int strings_collect_cb2 (void *cookie, int argc, char **argv, char **azColName, sqlite3_stmt *stmt) { (void) stmt; return strings_collect_cb (cookie, argc, argv, azColName); } /* Auxiliary data structure to collect statistics about signatures. */ struct signature_stats { struct signature_stats *next; /* The user-assigned policy for this binding. */ enum tofu_policy policy; /* How long ago the signature was created (rounded to a multiple of TIME_AGO_UNIT_SMALL, etc.). */ long time_ago; /* Number of signatures during this time. */ unsigned long count; /* If the corresponding key/user id has been expired / revoked. */ int is_expired; int is_revoked; /* The key that generated this signature. */ char fingerprint[1]; }; static void signature_stats_free (struct signature_stats *stats) { while (stats) { struct signature_stats *next = stats->next; xfree (stats); stats = next; } } static void signature_stats_prepend (struct signature_stats **statsp, const char *fingerprint, enum tofu_policy policy, long time_ago, unsigned long count) { struct signature_stats *stats = xmalloc_clear (sizeof (*stats) + strlen (fingerprint)); stats->next = *statsp; *statsp = stats; strcpy (stats->fingerprint, fingerprint); stats->policy = policy; stats->time_ago = time_ago; stats->count = count; } /* Process rows that contain the four columns: . */ static int signature_stats_collect_cb (void *cookie, int argc, char **argv, char **azColName, sqlite3_stmt *stmt) { struct signature_stats **statsp = cookie; int i = 0; enum tofu_policy policy; long time_ago; unsigned long count; long along; (void) azColName; (void) stmt; i ++; if (string_to_long (&along, argv[i], 0, __LINE__)) return 1; /* Abort */ policy = along; i ++; if (! argv[i]) time_ago = 0; else { if (string_to_long (&time_ago, argv[i], 0, __LINE__)) return 1; /* Abort. */ } i ++; /* If time_ago is NULL, then we had no messages, but we still have a single row, which count(*) turns into 1. */ if (! argv[i - 1]) count = 0; else { if (string_to_ulong (&count, argv[i], 0, __LINE__)) return 1; /* Abort */ } i ++; log_assert (argc == i); signature_stats_prepend (statsp, argv[0], policy, time_ago, count); return 0; } /* Format the first part of a conflict message and return that as a * malloced string. Returns NULL on error. */ static char * format_conflict_msg_part1 (int policy, strlist_t conflict_set, const char *email) { estream_t fp; char *fingerprint; char *tmpstr, *text; log_assert (conflict_set); fingerprint = conflict_set->d; fp = es_fopenmem (0, "rw,samethread"); if (!fp) log_fatal ("error creating memory stream: %s\n", gpg_strerror (gpg_error_from_syserror())); if (policy == TOFU_POLICY_NONE) { es_fprintf (fp, _("This is the first time the email address \"%s\" is " "being used with key %s."), email, fingerprint); es_fputs (" ", fp); } else if (policy == TOFU_POLICY_ASK && conflict_set->next) { int conflicts = strlist_length (conflict_set); es_fprintf (fp, ngettext("The email address \"%s\" is associated with %d key!", "The email address \"%s\" is associated with %d keys!", conflicts), email, conflicts); if (opt.verbose) es_fprintf (fp, _(" Since this binding's policy was 'auto', it has been " "changed to 'ask'.")); es_fputs (" ", fp); } es_fprintf (fp, _("Please indicate whether this email address should" " be associated with key %s or whether you think someone" " is impersonating \"%s\"."), fingerprint, email); es_fputc ('\n', fp); es_fputc (0, fp); if (es_fclose_snatch (fp, (void **)&tmpstr, NULL)) log_fatal ("error snatching memory stream\n"); text = format_text (tmpstr, 72, 80); es_free (tmpstr); return text; } /* Return 1 if A signed B and B signed A. */ static int cross_sigs (const char *email, kbnode_t a, kbnode_t b) { int i; PKT_public_key *a_pk = a->pkt->pkt.public_key; PKT_public_key *b_pk = b->pkt->pkt.public_key; char a_keyid[33]; char b_keyid[33]; if (DBG_TRUST) { format_keyid (pk_main_keyid (a_pk), KF_LONG, a_keyid, sizeof (a_keyid)); format_keyid (pk_main_keyid (b_pk), KF_LONG, b_keyid, sizeof (b_keyid)); } for (i = 0; i < 2; i ++) { /* See if SIGNER signed SIGNEE. */ kbnode_t signer = i == 0 ? a : b; kbnode_t signee = i == 0 ? b : a; PKT_public_key *signer_pk = signer->pkt->pkt.public_key; u32 *signer_kid = pk_main_keyid (signer_pk); kbnode_t n; int saw_email = 0; /* Iterate over SIGNEE's keyblock and see if there is a valid signature from SIGNER. */ for (n = signee; n; n = n->next) { PKT_signature *sig; if (n->pkt->pkttype == PKT_USER_ID) { if (saw_email) /* We're done: we've processed all signatures on the user id. */ break; else { /* See if this is the matching user id. */ PKT_user_id *user_id = n->pkt->pkt.user_id; char *email2 = email_from_user_id (user_id->name); if (strcmp (email, email2) == 0) saw_email = 1; xfree (email2); } } if (! saw_email) continue; if (n->pkt->pkttype != PKT_SIGNATURE) continue; sig = n->pkt->pkt.signature; if (! (sig->sig_class == 0x10 || sig->sig_class == 0x11 || sig->sig_class == 0x12 || sig->sig_class == 0x13)) /* Not a signature over a user id. */ continue; /* SIG is on SIGNEE's keyblock. If SIG was generated by the signer, then it's a match. */ if (keyid_cmp (sig->keyid, signer_kid) == 0) /* Match! */ break; } if (! n) /* We didn't find a signature from signer over signee. */ { if (DBG_TRUST) log_debug ("No cross sig between %s and %s\n", a_keyid, b_keyid); return 0; } } /* A signed B and B signed A. */ if (DBG_TRUST) log_debug ("Cross sig between %s and %s\n", a_keyid, b_keyid); return 1; } /* Return whether the key was signed by an ultimately trusted key. */ static int signed_by_utk (const char *email, kbnode_t a) { kbnode_t n; int saw_email = 0; for (n = a; n; n = n->next) { PKT_signature *sig; if (n->pkt->pkttype == PKT_USER_ID) { if (saw_email) /* We're done: we've processed all signatures on the user id. */ break; else { /* See if this is the matching user id. */ PKT_user_id *user_id = n->pkt->pkt.user_id; char *email2 = email_from_user_id (user_id->name); if (strcmp (email, email2) == 0) saw_email = 1; xfree (email2); } } if (! saw_email) continue; if (n->pkt->pkttype != PKT_SIGNATURE) continue; sig = n->pkt->pkt.signature; if (! (sig->sig_class == 0x10 || sig->sig_class == 0x11 || sig->sig_class == 0x12 || sig->sig_class == 0x13)) /* Not a signature over a user id. */ continue; /* SIG is on SIGNEE's keyblock. If SIG was generated by the signer, then it's a match. */ if (tdb_keyid_is_utk (sig->keyid)) { /* Match! */ if (DBG_TRUST) log_debug ("TOFU: %s is signed by an ultimately trusted key.\n", pk_keyid_str (a->pkt->pkt.public_key)); return 1; } } if (DBG_TRUST) log_debug ("TOFU: %s is NOT signed by an ultimately trusted key.\n", pk_keyid_str (a->pkt->pkt.public_key)); return 0; } enum { BINDING_NEW = 1 << 0, BINDING_CONFLICT = 1 << 1, BINDING_EXPIRED = 1 << 2, BINDING_REVOKED = 1 << 3 }; /* Ask the user about the binding. There are three ways we could end * up here: * * - This is a new binding and there is a conflict * (policy == TOFU_POLICY_NONE && conflict_set_count > 1), * * - This is a new binding and opt.tofu_default_policy is set to * ask. (policy == TOFU_POLICY_NONE && opt.tofu_default_policy == * TOFU_POLICY_ASK), or, * * - The policy is ask (the user deferred last time) (policy == * TOFU_POLICY_ASK). * * Note: this function must not be called while in a transaction! * * CONFLICT_SET includes all of the conflicting bindings * with FINGERPRINT first. FLAGS is a bit-wise or of * BINDING_NEW, etc. */ static void ask_about_binding (ctrl_t ctrl, enum tofu_policy *policy, int *trust_level, strlist_t conflict_set, const char *fingerprint, const char *email, const char *user_id, time_t now) { tofu_dbs_t dbs; strlist_t iter; int conflict_set_count = strlist_length (conflict_set); char *sqerr = NULL; int rc; estream_t fp; strlist_t other_user_ids = NULL; struct signature_stats *stats = NULL; struct signature_stats *stats_iter = NULL; char *prompt = NULL; const char *choices; dbs = ctrl->tofu.dbs; log_assert (dbs); log_assert (dbs->in_transaction == 0); fp = es_fopenmem (0, "rw,samethread"); if (!fp) log_fatal ("error creating memory stream: %s\n", gpg_strerror (gpg_error_from_syserror())); { char *text = format_conflict_msg_part1 (*policy, conflict_set, email); if (!text) /* FIXME: Return the error all the way up. */ log_fatal ("format failed: %s\n", gpg_strerror (gpg_error_from_syserror())); es_fputs (text, fp); es_fputc ('\n', fp); xfree (text); } begin_transaction (ctrl, 0); /* Find other user ids associated with this key and whether the * bindings are marked as good or bad. */ rc = gpgsql_stepx (dbs->db, &dbs->s.get_trust_gather_other_user_ids, strings_collect_cb2, &other_user_ids, &sqerr, "select user_id, policy from bindings where fingerprint = ?;", GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_END); if (rc) { log_error (_("error gathering other user IDs: %s\n"), sqerr); sqlite3_free (sqerr); sqerr = NULL; rc = gpg_error (GPG_ERR_GENERAL); } if (other_user_ids) { strlist_t strlist_iter; es_fprintf (fp, _("This key's user IDs:\n")); for (strlist_iter = other_user_ids; strlist_iter; strlist_iter = strlist_iter->next) { char *other_user_id = strlist_iter->d; char *other_thing; enum tofu_policy other_policy; log_assert (strlist_iter->next); strlist_iter = strlist_iter->next; other_thing = strlist_iter->d; other_policy = atoi (other_thing); es_fprintf (fp, " %s (", other_user_id); es_fprintf (fp, _("policy: %s"), tofu_policy_str (other_policy)); es_fprintf (fp, ")\n"); } es_fprintf (fp, "\n"); free_strlist (other_user_ids); } /* Get the stats for all the keys in CONFLICT_SET. */ strlist_rev (&conflict_set); for (iter = conflict_set; iter && ! rc; iter = iter->next) { #define STATS_SQL(table, time, sign) \ "select fingerprint, policy, time_ago, count(*)\n" \ " from\n" \ " (select bindings.*,\n" \ " "sign" case\n" \ " when delta ISNULL then 1\n" \ /* From the future (but if its just a couple of hours in the \ * future don't turn it into a warning)? Or should we use \ * small, medium or large units? (Note: whatever we do, we \ * keep the value in seconds. Then when we group, everything \ * that rounds to the same number of seconds is grouped.) */ \ " when delta < -("STRINGIFY (TIME_AGO_FUTURE_IGNORE)") then 2\n" \ " when delta < ("STRINGIFY (TIME_AGO_SMALL_THRESHOLD)")\n" \ " then 3\n" \ " when delta < ("STRINGIFY (TIME_AGO_MEDIUM_THRESHOLD)")\n" \ " then 4\n" \ " when delta < ("STRINGIFY (TIME_AGO_LARGE_THRESHOLD)")\n" \ " then 5\n" \ " else 6\n" \ " end time_ago,\n" \ " delta time_ago_raw\n" \ " from bindings\n" \ " left join\n" \ " (select *,\n" \ " cast(? - " time " as real) delta\n" \ " from " table ") ss\n" \ " on ss.binding = bindings.oid)\n" \ " where email = ? and fingerprint = ?\n" \ " group by time_ago\n" \ /* Make sure the current key is first. */ \ " order by time_ago desc;\n" /* Use the time when we saw the signature, not when the signature was created as that can be forged. */ rc = gpgsql_stepx (dbs->db, &dbs->s.get_trust_gather_signature_stats, signature_stats_collect_cb, &stats, &sqerr, STATS_SQL ("signatures", "time", ""), GPGSQL_ARG_LONG_LONG, (long long) now, GPGSQL_ARG_STRING, email, GPGSQL_ARG_STRING, iter->d, GPGSQL_ARG_END); if (rc) { rc = gpg_error (GPG_ERR_GENERAL); break; } if (!stats || strcmp (iter->d, stats->fingerprint) != 0) /* No stats for this binding. Add a dummy entry. */ signature_stats_prepend (&stats, iter->d, TOFU_POLICY_AUTO, 1, 1); rc = gpgsql_stepx (dbs->db, &dbs->s.get_trust_gather_encryption_stats, signature_stats_collect_cb, &stats, &sqerr, STATS_SQL ("encryptions", "time", "-"), GPGSQL_ARG_LONG_LONG, (long long) now, GPGSQL_ARG_STRING, email, GPGSQL_ARG_STRING, iter->d, GPGSQL_ARG_END); if (rc) { rc = gpg_error (GPG_ERR_GENERAL); break; } #undef STATS_SQL if (!stats || strcmp (iter->d, stats->fingerprint) != 0 || stats->time_ago > 0) /* No stats for this binding. Add a dummy entry. */ signature_stats_prepend (&stats, iter->d, TOFU_POLICY_AUTO, -1, 1); } end_transaction (ctrl, 0); strlist_rev (&conflict_set); if (rc) { strlist_t strlist_iter; log_error (_("error gathering signature stats: %s\n"), sqerr); sqlite3_free (sqerr); sqerr = NULL; es_fprintf (fp, ngettext("The email address \"%s\" is" " associated with %d key:\n", "The email address \"%s\" is" " associated with %d keys:\n", conflict_set_count), email, conflict_set_count); for (strlist_iter = conflict_set; strlist_iter; strlist_iter = strlist_iter->next) es_fprintf (fp, " %s\n", strlist_iter->d); } else { char *key = NULL; strlist_t binding; int seen_in_past = 0; int encrypted = 1; es_fprintf (fp, _("Statistics for keys" " with the email address \"%s\":\n"), email); for (stats_iter = stats; stats_iter; stats_iter = stats_iter->next) { #if 0 log_debug ("%s: time_ago: %ld; count: %ld\n", stats_iter->fingerprint, stats_iter->time_ago, stats_iter->count); #endif if (stats_iter->time_ago > 0 && encrypted) { /* We've change from the encrypted stats to the verified * stats. Reset SEEN_IN_PAST. */ encrypted = 0; seen_in_past = 0; } if (! key || strcmp (key, stats_iter->fingerprint)) { int this_key; char *key_pp; key = stats_iter->fingerprint; this_key = strcmp (key, fingerprint) == 0; key_pp = format_hexfingerprint (key, NULL, 0); es_fprintf (fp, " %s (", key_pp); /* Find the associated binding. */ for (binding = conflict_set; binding; binding = binding->next) if (strcmp (key, binding->d) == 0) break; log_assert (binding); if ((binding->flags & BINDING_REVOKED)) { es_fprintf (fp, _("revoked")); es_fprintf (fp, ", "); } else if ((binding->flags & BINDING_EXPIRED)) { es_fprintf (fp, _("expired")); es_fprintf (fp, ", "); } if (this_key) es_fprintf (fp, _("this key")); else es_fprintf (fp, _("policy: %s"), tofu_policy_str (stats_iter->policy)); es_fputs ("):\n", fp); xfree (key_pp); seen_in_past = 0; show_statistics (dbs, stats_iter->fingerprint, email, TOFU_POLICY_ASK, NULL, 1, now); } if (labs(stats_iter->time_ago) == 1) { /* The 1 in this case is the NULL entry. */ log_assert (stats_iter->count == 1); stats_iter->count = 0; } seen_in_past += stats_iter->count; es_fputs (" ", fp); if (!stats_iter->count) { if (stats_iter->time_ago > 0) es_fprintf (fp, ngettext("Verified %d message.", "Verified %d messages.", seen_in_past), seen_in_past); else es_fprintf (fp, ngettext("Encrypted %d message.", "Encrypted %d messages.", seen_in_past), seen_in_past); } else if (labs(stats_iter->time_ago) == 2) { if (stats_iter->time_ago > 0) es_fprintf (fp, ngettext("Verified %d message in the future.", "Verified %d messages in the future.", seen_in_past), seen_in_past); else es_fprintf (fp, ngettext("Encrypted %d message in the future.", "Encrypted %d messages in the future.", seen_in_past), seen_in_past); /* Reset it. */ seen_in_past = 0; } else { if (labs(stats_iter->time_ago) == 3) { int days = 1 + stats_iter->time_ago / TIME_AGO_UNIT_SMALL; if (stats_iter->time_ago > 0) es_fprintf (fp, ngettext("Messages verified over the past %d day: %d.", "Messages verified over the past %d days: %d.", days), days, seen_in_past); else es_fprintf (fp, ngettext("Messages encrypted over the past %d day: %d.", "Messages encrypted over the past %d days: %d.", days), days, seen_in_past); } else if (labs(stats_iter->time_ago) == 4) { int months = 1 + stats_iter->time_ago / TIME_AGO_UNIT_MEDIUM; if (stats_iter->time_ago > 0) es_fprintf (fp, ngettext("Messages verified over the past %d month: %d.", "Messages verified over the past %d months: %d.", months), months, seen_in_past); else es_fprintf (fp, ngettext("Messages encrypted over the past %d month: %d.", "Messages encrypted over the past %d months: %d.", months), months, seen_in_past); } else if (labs(stats_iter->time_ago) == 5) { int years = 1 + stats_iter->time_ago / TIME_AGO_UNIT_LARGE; if (stats_iter->time_ago > 0) es_fprintf (fp, ngettext("Messages verified over the past %d year: %d.", "Messages verified over the past %d years: %d.", years), years, seen_in_past); else es_fprintf (fp, ngettext("Messages encrypted over the past %d year: %d.", "Messages encrypted over the past %d years: %d.", years), years, seen_in_past); } else if (labs(stats_iter->time_ago) == 6) { if (stats_iter->time_ago > 0) es_fprintf (fp, _("Messages verified in the past: %d."), seen_in_past); else es_fprintf (fp, _("Messages encrypted in the past: %d."), seen_in_past); } else log_assert (! "Broken SQL.\n"); } es_fputs ("\n", fp); } } if (conflict_set_count > 1 || (conflict_set->flags & BINDING_CONFLICT)) { /* This is a conflict. */ /* TRANSLATORS: Please translate the text found in the source * file below. We don't directly internationalize that text so * that we can tweak it without breaking translations. */ const char *text = _("TOFU detected a binding conflict"); char *textbuf; if (!strcmp (text, "TOFU detected a binding conflict")) { /* No translation. Use the English text. */ text = "Normally, an email address is associated with a single key. " "However, people sometimes generate a new key if " "their key is too old or they think it might be compromised. " "Alternatively, a new key may indicate a man-in-the-middle " "attack! Before accepting this association, you should talk to or " "call the person to make sure this new key is legitimate."; } textbuf = format_text (text, 72, 80); es_fprintf (fp, "\n%s\n", textbuf? textbuf : "[OUT OF CORE!]"); xfree (textbuf); } es_fputc ('\n', fp); /* Add a NUL terminator. */ es_fputc (0, fp); if (es_fclose_snatch (fp, (void **) &prompt, NULL)) log_fatal ("error snatching memory stream\n"); /* I think showing the large message once is sufficient. If we * would move it right before the cpr_get many lines will scroll * away and the user might not realize that he merely entered a * wrong choice (because he does not see that either). As a small * benefit we allow C-L to redisplay everything. */ tty_printf ("%s", prompt); /* Suspend any transaction: it could take a while until the user responds. */ tofu_suspend_batch_transaction (ctrl); while (1) { char *response; /* TRANSLATORS: Two letters (normally the lower and upper case * version of the hotkey) for each of the five choices. If * there is only one choice in your language, repeat it. */ choices = _("gG" "aA" "uU" "rR" "bB"); if (strlen (choices) != 10) log_bug ("Bad TOFU conflict translation! Please report."); response = cpr_get ("tofu.conflict", _("(G)ood, (A)ccept once, (U)nknown, (R)eject once, (B)ad? ")); trim_spaces (response); cpr_kill_prompt (); if (*response == CONTROL_L) tty_printf ("%s", prompt); else if (!response[0]) /* Default to unknown. Don't save it. */ { tty_printf (_("Defaulting to unknown.\n")); *policy = TOFU_POLICY_UNKNOWN; break; } else if (!response[1]) { char *choice = strchr (choices, *response); if (choice) { int c = ((size_t) choice - (size_t) choices) / 2; switch (c) { case 0: /* Good. */ *policy = TOFU_POLICY_GOOD; *trust_level = tofu_policy_to_trust_level (*policy); break; case 1: /* Accept once. */ *policy = TOFU_POLICY_ASK; *trust_level = tofu_policy_to_trust_level (TOFU_POLICY_GOOD); break; case 2: /* Unknown. */ *policy = TOFU_POLICY_UNKNOWN; *trust_level = tofu_policy_to_trust_level (*policy); break; case 3: /* Reject once. */ *policy = TOFU_POLICY_ASK; *trust_level = tofu_policy_to_trust_level (TOFU_POLICY_BAD); break; case 4: /* Bad. */ *policy = TOFU_POLICY_BAD; *trust_level = tofu_policy_to_trust_level (*policy); break; default: log_bug ("c should be between 0 and 4 but it is %d!", c); } if (record_binding (dbs, fingerprint, email, user_id, *policy, TOFU_POLICY_NONE, NULL, 0, 0, now)) { /* If there's an error registering the * binding, don't save the signature. */ *trust_level = _tofu_GET_TRUST_ERROR; } break; } } xfree (response); } tofu_resume_batch_transaction (ctrl); xfree (prompt); signature_stats_free (stats); } /* Return the set of keys that conflict with the binding (including the binding itself, which will be first in the list). For each returned key also sets BINDING_NEW, etc. */ static strlist_t build_conflict_set (ctrl_t ctrl, tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint, const char *email) { gpg_error_t rc; char *sqerr; strlist_t conflict_set = NULL; int conflict_set_count; strlist_t iter; kbnode_t *kb_all; KEYDB_HANDLE hd; int i; /* Get the fingerprints of any bindings that share the email address * and whether the bindings have a known conflict. * * Note: if the binding in question is in the DB, it will also be * returned. Thus, if the result set is empty, then is a new binding. */ rc = gpgsql_stepx (dbs->db, &dbs->s.get_trust_bindings_with_this_email, strings_collect_cb2, &conflict_set, &sqerr, "select" /* A binding should only appear once, but try not to break in the * case of corruption. */ " fingerprint || case sum(conflict NOTNULL) when 0 then '' else '!' end" " from bindings where email = ?" " group by fingerprint" /* Make sure the current key comes first in the result list (if it is present). */ " order by fingerprint = ? asc, fingerprint desc;", GPGSQL_ARG_STRING, email, GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_END); if (rc) { log_error (_("error reading TOFU database: %s\n"), sqerr); print_further_info ("listing fingerprints"); sqlite3_free (sqerr); rc = gpg_error (GPG_ERR_GENERAL); return NULL; } /* Set BINDING_CONFLICT if the binding has a known conflict. This * allows us to distinguish between bindings where the user * explicitly set the policy to ask and bindings where we set the * policy to ask due to a conflict. */ for (iter = conflict_set; iter; iter = iter->next) { /* Fixme: Why the check against N+1? */ int l = strlen (iter->d); if (!(l == 2 * 20 || l == 2 * 20 + 1 || l == 2 * 32 || l == 2 * 32 + 1)) { log_error (_("TOFU db corruption detected.\n")); print_further_info ("fingerprint '%s' is %d characters long", iter->d, l); } if (l >= 1 && iter->d[l - 1] == '!') { iter->flags |= BINDING_CONFLICT; /* Remove the !. */ iter->d[l - 1] = 0; } } /* If the current binding has not yet been recorded, add it to the * list. (The order by above ensures that if it is present, it will * be first.) */ if (! (conflict_set && strcmp (conflict_set->d, fingerprint) == 0)) { add_to_strlist (&conflict_set, fingerprint); conflict_set->flags |= BINDING_NEW; } conflict_set_count = strlist_length (conflict_set); /* Eliminate false conflicts. */ if (conflict_set_count == 1) /* We only have a single key. There are no false conflicts to eliminate. But, we do need to set the flags. */ { if (pk->has_expired) conflict_set->flags |= BINDING_EXPIRED; if (pk->flags.revoked) conflict_set->flags |= BINDING_REVOKED; return conflict_set; } /* If two keys have cross signatures, then they are controlled by * the same person and thus are not in conflict. */ kb_all = xcalloc (sizeof (kb_all[0]), conflict_set_count); - hd = keydb_new (); + hd = keydb_new (ctrl); for (i = 0, iter = conflict_set; i < conflict_set_count; i ++, iter = iter->next) { char *fp = iter->d; KEYDB_SEARCH_DESC desc; kbnode_t kb; PKT_public_key *binding_pk; kbnode_t n; int found_user_id; rc = keydb_search_reset (hd); if (rc) { log_error ("resetting keydb failed: %s\n", gpg_strerror (rc)); continue; } rc = classify_user_id (fp, &desc, 0); if (rc) { log_error (_("error parsing key specification '%s': %s\n"), fp, gpg_strerror (rc)); continue; } rc = keydb_search (hd, &desc, 1, NULL); if (rc) { /* Note: it is entirely possible that we don't have the key corresponding to an entry in the TOFU DB. This can happen if we merge two TOFU DBs, but not the key rings. */ log_info (_("key \"%s\" not found: %s\n"), fp, gpg_strerror (rc)); continue; } rc = keydb_get_keyblock (hd, &kb); if (rc) { log_error (_("error reading keyblock: %s\n"), gpg_strerror (rc)); print_further_info ("fingerprint: %s", fp); continue; } merge_keys_and_selfsig (ctrl, kb); log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); kb_all[i] = kb; /* Since we have the key block, use this opportunity to figure * out if the binding is expired or revoked. */ binding_pk = kb->pkt->pkt.public_key; /* The binding is always expired/revoked if the key is * expired/revoked. */ if (binding_pk->has_expired) iter->flags |= BINDING_EXPIRED; if (binding_pk->flags.revoked) iter->flags |= BINDING_REVOKED; /* The binding is also expired/revoked if the user id is * expired/revoked. */ n = kb; found_user_id = 0; while ((n = find_next_kbnode (n, PKT_USER_ID)) && ! found_user_id) { PKT_user_id *user_id2 = n->pkt->pkt.user_id; char *email2; if (user_id2->attrib_data) continue; email2 = email_from_user_id (user_id2->name); if (strcmp (email, email2) == 0) { found_user_id = 1; if (user_id2->flags.revoked) iter->flags |= BINDING_REVOKED; if (user_id2->flags.expired) iter->flags |= BINDING_EXPIRED; } xfree (email2); } if (! found_user_id) { log_info (_("TOFU db corruption detected.\n")); print_further_info ("user id '%s' not on key block '%s'", email, fingerprint); } } keydb_release (hd); /* Now that we have the key blocks, check for cross sigs. */ { int j; strlist_t *prevp; strlist_t iter_next; int *die; log_assert (conflict_set_count > 0); die = xtrycalloc (conflict_set_count, sizeof *die); if (!die) { /*err = gpg_error_from_syserror ();*/ xoutofcore (); /* Fixme: Let the function return an error. */ } for (i = 0; i < conflict_set_count; i ++) { /* Look for cross sigs between this key (i == 0) or a key * that has cross sigs with i == 0 (i.e., transitively) */ if (! (i == 0 || die[i])) continue; for (j = i + 1; j < conflict_set_count; j ++) /* Be careful: we might not have a key block for a key. */ if (kb_all[i] && kb_all[j] && cross_sigs (email, kb_all[i], kb_all[j])) die[j] = 1; } /* Free unconflicting bindings (and all of the key blocks). */ for (iter = conflict_set, prevp = &conflict_set, i = 0; iter; iter = iter_next, i ++) { iter_next = iter->next; release_kbnode (kb_all[i]); if (die[i]) { *prevp = iter_next; iter->next = NULL; free_strlist (iter); conflict_set_count --; } else { prevp = &iter->next; } } /* We shouldn't have removed the head. */ log_assert (conflict_set); log_assert (conflict_set_count >= 1); xfree (die); } xfree (kb_all); if (DBG_TRUST) { log_debug ("binding conflicts:\n", fingerprint, email); for (iter = conflict_set; iter; iter = iter->next) { log_debug (" %s:%s%s%s%s\n", iter->d, (iter->flags & BINDING_NEW) ? " new" : "", (iter->flags & BINDING_CONFLICT) ? " known_conflict" : "", (iter->flags & BINDING_EXPIRED) ? " expired" : "", (iter->flags & BINDING_REVOKED) ? " revoked" : ""); } } return conflict_set; } /* Return the effective policy for the binding * (email has already been normalized). Returns * _tofu_GET_POLICY_ERROR if an error occurs. Returns any conflict * information in *CONFLICT_SETP if CONFLICT_SETP is not NULL and the * returned policy is TOFU_POLICY_ASK (consequently, if there is a * conflict, but the user set the policy to good *CONFLICT_SETP will * empty). Note: as per build_conflict_set, which is used to build * the conflict information, the conflict information includes the * current user id as the first element of the linked list. * * This function registers the binding in the bindings table if it has * not yet been registered. */ static enum tofu_policy get_policy (ctrl_t ctrl, tofu_dbs_t dbs, PKT_public_key *pk, const char *fingerprint, const char *user_id, const char *email, strlist_t *conflict_setp, time_t now) { int rc; char *err = NULL; strlist_t results = NULL; enum tofu_policy policy = _tofu_GET_POLICY_ERROR; enum tofu_policy effective_policy_orig = TOFU_POLICY_NONE; enum tofu_policy effective_policy = _tofu_GET_POLICY_ERROR; long along; char *conflict_orig = NULL; char *conflict = NULL; strlist_t conflict_set = NULL; int conflict_set_count; /* Check if the binding is known (TOFU_POLICY_NONE cannot appear in the DB. Thus, if POLICY is still TOFU_POLICY_NONE after executing the query, then the result set was empty.) */ rc = gpgsql_stepx (dbs->db, &dbs->s.get_policy_select_policy_and_conflict, strings_collect_cb2, &results, &err, "select policy, conflict, effective_policy from bindings\n" " where fingerprint = ? and email = ?", GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, GPGSQL_ARG_END); if (rc) { log_error (_("error reading TOFU database: %s\n"), err); print_further_info ("reading the policy"); sqlite3_free (err); rc = gpg_error (GPG_ERR_GENERAL); goto out; } if (strlist_length (results) == 0) { /* No results. Use the defaults. */ policy = TOFU_POLICY_NONE; effective_policy = TOFU_POLICY_NONE; } else if (strlist_length (results) == 3) { /* Parse and sanity check the results. */ if (string_to_long (&along, results->d, 0, __LINE__)) { log_error (_("error reading TOFU database: %s\n"), gpg_strerror (GPG_ERR_BAD_DATA)); print_further_info ("bad value for policy: %s", results->d); goto out; } policy = along; if (! (policy == TOFU_POLICY_AUTO || policy == TOFU_POLICY_GOOD || policy == TOFU_POLICY_UNKNOWN || policy == TOFU_POLICY_BAD || policy == TOFU_POLICY_ASK)) { log_error (_("error reading TOFU database: %s\n"), gpg_strerror (GPG_ERR_DB_CORRUPTED)); print_further_info ("invalid value for policy (%d)", policy); effective_policy = _tofu_GET_POLICY_ERROR; goto out; } if (*results->next->d) conflict = xstrdup (results->next->d); if (string_to_long (&along, results->next->next->d, 0, __LINE__)) { log_error (_("error reading TOFU database: %s\n"), gpg_strerror (GPG_ERR_BAD_DATA)); print_further_info ("bad value for effective policy: %s", results->next->next->d); goto out; } effective_policy = along; if (! (effective_policy == TOFU_POLICY_NONE || effective_policy == TOFU_POLICY_AUTO || effective_policy == TOFU_POLICY_GOOD || effective_policy == TOFU_POLICY_UNKNOWN || effective_policy == TOFU_POLICY_BAD || effective_policy == TOFU_POLICY_ASK)) { log_error (_("error reading TOFU database: %s\n"), gpg_strerror (GPG_ERR_DB_CORRUPTED)); print_further_info ("invalid value for effective_policy (%d)", effective_policy); effective_policy = _tofu_GET_POLICY_ERROR; goto out; } } else { /* The result has the wrong form. */ log_error (_("error reading TOFU database: %s\n"), gpg_strerror (GPG_ERR_BAD_DATA)); print_further_info ("reading policy: expected 3 columns, got %d\n", strlist_length (results)); goto out; } /* Save the effective policy and conflict so we know if we changed * them. */ effective_policy_orig = effective_policy; conflict_orig = conflict; /* Unless there is a conflict, if the effective policy is cached, * just return it. The reason we don't do this when there is a * conflict is because of the following scenario: assume A and B * conflict and B has signed A's key. Now, later we import A's * signature on B. We need to recheck A, but the signature was on * B, i.e., when B changes, we invalidate B's effective policy, but * we also need to invalidate A's effective policy. Instead, we * assume that conflicts are rare and don't optimize for them, which * would complicate the code. */ if (effective_policy != TOFU_POLICY_NONE && !conflict) goto out; /* If the user explicitly set the policy, then respect that. */ if (policy != TOFU_POLICY_AUTO && policy != TOFU_POLICY_NONE) { effective_policy = policy; goto out; } /* Unless proven wrong, assume the effective policy is 'auto'. */ effective_policy = TOFU_POLICY_AUTO; /* See if the key is ultimately trusted. */ { u32 kid[2]; keyid_from_pk (pk, kid); if (tdb_keyid_is_utk (kid)) { effective_policy = TOFU_POLICY_GOOD; goto out; } } /* See if the key is signed by an ultimately trusted key. */ { int fingerprint_raw_len = strlen (fingerprint) / 2; char fingerprint_raw[MAX_FINGERPRINT_LEN]; int len = 0; /* FIXME(fingerprint) */ if (fingerprint_raw_len != 20 /*sizeof fingerprint_raw */ || ((len = hex2bin (fingerprint, fingerprint_raw, fingerprint_raw_len)) != strlen (fingerprint))) { if (DBG_TRUST) log_debug ("TOFU: Bad fingerprint: %s (len: %zu, parsed: %d)\n", fingerprint, strlen (fingerprint), len); } else { int lookup_err; kbnode_t kb; lookup_err = get_pubkey_byfprint (ctrl, NULL, &kb, fingerprint_raw, fingerprint_raw_len); if (lookup_err) { if (DBG_TRUST) log_debug ("TOFU: Looking up %s: %s\n", fingerprint, gpg_strerror (lookup_err)); } else { int is_signed_by_utk = signed_by_utk (email, kb); release_kbnode (kb); if (is_signed_by_utk) { effective_policy = TOFU_POLICY_GOOD; goto out; } } } } /* Check for any conflicts / see if a previously discovered conflict * disappeared. The latter can happen if the conflicting bindings * are now cross signed, for instance. */ conflict_set = build_conflict_set (ctrl, dbs, pk, fingerprint, email); conflict_set_count = strlist_length (conflict_set); if (conflict_set_count == 0) { /* build_conflict_set should always at least return the current binding. Something went wrong. */ effective_policy = _tofu_GET_POLICY_ERROR; goto out; } if (conflict_set_count == 1 && (conflict_set->flags & BINDING_NEW)) { /* We've never observed a binding with this email address and we * have a default policy, which is not to ask the user. */ /* If we've seen this binding, then we've seen this email and * policy couldn't possibly be TOFU_POLICY_NONE. */ log_assert (policy == TOFU_POLICY_NONE); if (DBG_TRUST) log_debug ("TOFU: New binding , no conflict.\n", fingerprint, email); effective_policy = TOFU_POLICY_AUTO; goto out; } if (conflict_set_count == 1 && (conflict_set->flags & BINDING_CONFLICT)) { /* No known conflicts now, but there was a conflict. This means * at some point, there was a conflict and we changed this * binding's policy to ask and set the conflicting key. The * conflict can go away if there is not a cross sig between the * two keys. In this case, just silently clear the conflict and * reset the policy to auto. */ if (DBG_TRUST) log_debug ("TOFU: binding had a conflict, but it's been resolved (probably via cross sig).\n", fingerprint, email); effective_policy = TOFU_POLICY_AUTO; conflict = NULL; goto out; } if (conflict_set_count == 1) { /* No conflicts and never marked as conflicting. */ log_assert (!conflict); effective_policy = TOFU_POLICY_AUTO; goto out; } /* There is a conflicting key. */ log_assert (conflict_set_count > 1); effective_policy = TOFU_POLICY_ASK; conflict = xstrdup (conflict_set->next->d); out: log_assert (policy == _tofu_GET_POLICY_ERROR || policy == TOFU_POLICY_NONE || policy == TOFU_POLICY_AUTO || policy == TOFU_POLICY_GOOD || policy == TOFU_POLICY_UNKNOWN || policy == TOFU_POLICY_BAD || policy == TOFU_POLICY_ASK); /* Everything but NONE. */ log_assert (effective_policy == _tofu_GET_POLICY_ERROR || effective_policy == TOFU_POLICY_AUTO || effective_policy == TOFU_POLICY_GOOD || effective_policy == TOFU_POLICY_UNKNOWN || effective_policy == TOFU_POLICY_BAD || effective_policy == TOFU_POLICY_ASK); if (effective_policy != TOFU_POLICY_ASK && conflict) conflict = NULL; /* If we don't have a record of this binding, its effective policy * changed, or conflict changed, update the DB. */ if (effective_policy != _tofu_GET_POLICY_ERROR && (/* New binding. */ policy == TOFU_POLICY_NONE /* effective_policy changed. */ || effective_policy != effective_policy_orig /* conflict changed. */ || (conflict != conflict_orig && (!conflict || !conflict_orig || strcmp (conflict, conflict_orig) != 0)))) { if (record_binding (dbs, fingerprint, email, user_id, policy == TOFU_POLICY_NONE ? TOFU_POLICY_AUTO : policy, effective_policy, conflict, 1, 0, now) != 0) log_error ("error setting TOFU binding's policy" " to %s\n", tofu_policy_str (policy)); } /* If the caller wants the set of conflicts, return it. */ if (effective_policy == TOFU_POLICY_ASK && conflict_setp) { if (! conflict_set) conflict_set = build_conflict_set (ctrl, dbs, pk, fingerprint, email); *conflict_setp = conflict_set; } else { free_strlist (conflict_set); if (conflict_setp) *conflict_setp = NULL; } xfree (conflict_orig); if (conflict != conflict_orig) xfree (conflict); free_strlist (results); return effective_policy; } /* Return the trust level (TRUST_NEVER, etc.) for the binding * (email is already normalized). If no policy * is registered, returns TOFU_POLICY_NONE. If an error occurs, * returns _tofu_GET_TRUST_ERROR. * * PK is the public key object for FINGERPRINT. * * USER_ID is the unadulterated user id. * * If MAY_ASK is set, then we may interact with the user. This is * necessary if there is a conflict or the binding's policy is * TOFU_POLICY_ASK. In the case of a conflict, we set the new * conflicting binding's policy to TOFU_POLICY_ASK. In either case, * we return TRUST_UNDEFINED. Note: if MAY_ASK is set, then this * function must not be called while in a transaction! */ static enum tofu_policy get_trust (ctrl_t ctrl, PKT_public_key *pk, const char *fingerprint, const char *email, const char *user_id, int may_ask, enum tofu_policy *policyp, strlist_t *conflict_setp, time_t now) { tofu_dbs_t dbs = ctrl->tofu.dbs; int in_transaction = 0; enum tofu_policy policy; int rc; char *sqerr = NULL; strlist_t conflict_set = NULL; int trust_level = TRUST_UNKNOWN; strlist_t iter; log_assert (dbs); if (may_ask) log_assert (dbs->in_transaction == 0); if (opt.batch) may_ask = 0; log_assert (pk_is_primary (pk)); /* Make sure _tofu_GET_TRUST_ERROR isn't equal to any of the trust levels. */ log_assert (_tofu_GET_TRUST_ERROR != TRUST_UNKNOWN && _tofu_GET_TRUST_ERROR != TRUST_EXPIRED && _tofu_GET_TRUST_ERROR != TRUST_UNDEFINED && _tofu_GET_TRUST_ERROR != TRUST_NEVER && _tofu_GET_TRUST_ERROR != TRUST_MARGINAL && _tofu_GET_TRUST_ERROR != TRUST_FULLY && _tofu_GET_TRUST_ERROR != TRUST_ULTIMATE); begin_transaction (ctrl, 0); in_transaction = 1; /* We need to call get_policy even if the key is ultimately trusted * to make sure the binding has been registered. */ policy = get_policy (ctrl, dbs, pk, fingerprint, user_id, email, &conflict_set, now); if (policy == TOFU_POLICY_ASK) /* The conflict set should always contain at least one element: * the current key. */ log_assert (conflict_set); else /* If the policy is not TOFU_POLICY_ASK, then conflict_set will be * NULL. */ log_assert (! conflict_set); /* If the key is ultimately trusted, there is nothing to do. */ { u32 kid[2]; keyid_from_pk (pk, kid); if (tdb_keyid_is_utk (kid)) { trust_level = TRUST_ULTIMATE; policy = TOFU_POLICY_GOOD; goto out; } } if (policy == TOFU_POLICY_AUTO) { policy = opt.tofu_default_policy; if (DBG_TRUST) log_debug ("TOFU: binding 's policy is" " auto (default: %s).\n", fingerprint, email, tofu_policy_str (opt.tofu_default_policy)); if (policy == TOFU_POLICY_ASK) /* The default policy is ASK, but there is no conflict (policy * was 'auto'). In this case, we need to make sure the * conflict set includes at least the current user id. */ { add_to_strlist (&conflict_set, fingerprint); } } switch (policy) { case TOFU_POLICY_AUTO: case TOFU_POLICY_GOOD: case TOFU_POLICY_UNKNOWN: case TOFU_POLICY_BAD: /* The saved judgement is auto -> auto, good, unknown or bad. * We don't need to ask the user anything. */ if (DBG_TRUST) log_debug ("TOFU: Known binding 's policy: %s\n", fingerprint, email, tofu_policy_str (policy)); trust_level = tofu_policy_to_trust_level (policy); goto out; case TOFU_POLICY_ASK: /* We need to ask the user what to do. */ break; case _tofu_GET_POLICY_ERROR: trust_level = _tofu_GET_TRUST_ERROR; goto out; default: log_bug ("%s: Impossible value for policy (%d)\n", __func__, policy); } /* We get here if: * * 1. The saved policy is auto and the default policy is ask * (get_policy() == TOFU_POLICY_AUTO * && opt.tofu_default_policy == TOFU_POLICY_ASK) * * 2. The saved policy is ask (either last time the user selected * accept once or reject once or there was a conflict and this * binding's policy was changed from auto to ask) * (policy == TOFU_POLICY_ASK). */ log_assert (policy == TOFU_POLICY_ASK); if (may_ask) { /* We can't be in a normal transaction in ask_about_binding. */ end_transaction (ctrl, 0); in_transaction = 0; /* If we get here, we need to ask the user about the binding. */ ask_about_binding (ctrl, &policy, &trust_level, conflict_set, fingerprint, email, user_id, now); } else { trust_level = TRUST_UNDEFINED; } /* Mark any conflicting bindings that have an automatic policy as * now requiring confirmation. Note: we do this after we ask for * confirmation so that when the current policy is printed, it is * correct. */ if (! in_transaction) { begin_transaction (ctrl, 0); in_transaction = 1; } /* The conflict set should always contain at least one element: * the current key. */ log_assert (conflict_set); for (iter = conflict_set->next; iter; iter = iter->next) { /* We don't immediately set the effective policy to 'ask, because */ rc = gpgsql_exec_printf (dbs->db, NULL, NULL, &sqerr, "update bindings set effective_policy = %d, conflict = %Q" " where email = %Q and fingerprint = %Q and effective_policy != %d;", TOFU_POLICY_NONE, fingerprint, email, iter->d, TOFU_POLICY_ASK); if (rc) { log_error (_("error changing TOFU policy: %s\n"), sqerr); print_further_info ("binding: ", fingerprint, user_id); sqlite3_free (sqerr); sqerr = NULL; rc = gpg_error (GPG_ERR_GENERAL); } else if (DBG_TRUST) log_debug ("Set %s to conflict with %s\n", iter->d, fingerprint); } out: if (in_transaction) end_transaction (ctrl, 0); if (policyp) *policyp = policy; if (conflict_setp) *conflict_setp = conflict_set; else free_strlist (conflict_set); return trust_level; } /* Return a malloced string of the form * "7~months" * The caller should replace all '~' in the returned string by a space * and also free the returned string. * * This is actually a bad hack which may not work correctly with all * languages. */ static char * time_ago_str (long long int t) { /* It would be nice to use a macro to do this, but gettext works on the unpreprocessed code. */ #define MIN_SECS (60) #define HOUR_SECS (60 * MIN_SECS) #define DAY_SECS (24 * HOUR_SECS) #define WEEK_SECS (7 * DAY_SECS) #define MONTH_SECS (30 * DAY_SECS) #define YEAR_SECS (365 * DAY_SECS) if (t > 2 * YEAR_SECS) { long long int c = t / YEAR_SECS; return xtryasprintf (ngettext("%lld~year", "%lld~years", c), c); } if (t > 2 * MONTH_SECS) { long long int c = t / MONTH_SECS; return xtryasprintf (ngettext("%lld~month", "%lld~months", c), c); } if (t > 2 * WEEK_SECS) { long long int c = t / WEEK_SECS; return xtryasprintf (ngettext("%lld~week", "%lld~weeks", c), c); } if (t > 2 * DAY_SECS) { long long int c = t / DAY_SECS; return xtryasprintf (ngettext("%lld~day", "%lld~days", c), c); } if (t > 2 * HOUR_SECS) { long long int c = t / HOUR_SECS; return xtryasprintf (ngettext("%lld~hour", "%lld~hours", c), c); } if (t > 2 * MIN_SECS) { long long int c = t / MIN_SECS; return xtryasprintf (ngettext("%lld~minute", "%lld~minutes", c), c); } return xtryasprintf (ngettext("%lld~second", "%lld~seconds", t), t); } /* If FP is NULL, write TOFU_STATS status line. If FP is not NULL * write a "tfs" record to that stream. */ static void write_stats_status (estream_t fp, enum tofu_policy policy, unsigned long signature_count, unsigned long signature_first_seen, unsigned long signature_most_recent, unsigned long signature_days, unsigned long encryption_count, unsigned long encryption_first_done, unsigned long encryption_most_recent, unsigned long encryption_days) { int summary; int validity; unsigned long days_sq; /* Use the euclidean distance (m = sqrt(a^2 + b^2)) rather then the sum of the magnitudes (m = a + b) to ensure a balance between verified signatures and encrypted messages. */ days_sq = signature_days * signature_days + encryption_days * encryption_days; if (days_sq < 1) validity = 1; /* Key without history. */ else if (days_sq < (2 * BASIC_TRUST_THRESHOLD) * (2 * BASIC_TRUST_THRESHOLD)) validity = 2; /* Key with too little history. */ else if (days_sq < (2 * FULL_TRUST_THRESHOLD) * (2 * FULL_TRUST_THRESHOLD)) validity = 3; /* Key with enough history for basic trust. */ else validity = 4; /* Key with a lot of history. */ if (policy == TOFU_POLICY_ASK) summary = 0; /* Key requires attention. */ else summary = validity; if (fp) { es_fprintf (fp, "tfs:1:%d:%lu:%lu:%s:%lu:%lu:%lu:%lu:%d:%lu:%lu:\n", summary, signature_count, encryption_count, tofu_policy_str (policy), signature_first_seen, signature_most_recent, encryption_first_done, encryption_most_recent, validity, signature_days, encryption_days); } else { write_status_printf (STATUS_TOFU_STATS, "%d %lu %lu %s %lu %lu %lu %lu %d %lu %lu", summary, signature_count, encryption_count, tofu_policy_str (policy), signature_first_seen, signature_most_recent, encryption_first_done, encryption_most_recent, validity, signature_days, encryption_days); } } /* Note: If OUTFP is not NULL, this function merely prints a "tfs" record * to OUTFP. * * POLICY is the key's policy (as returned by get_policy). * * Returns 0 if ONLY_STATUS_FD is set. Otherwise, returns whether * the caller should call show_warning after iterating over all user * ids. */ static int show_statistics (tofu_dbs_t dbs, const char *fingerprint, const char *email, enum tofu_policy policy, estream_t outfp, int only_status_fd, time_t now) { char *fingerprint_pp; int rc; strlist_t strlist = NULL; char *err = NULL; unsigned long signature_first_seen = 0; unsigned long signature_most_recent = 0; unsigned long signature_count = 0; unsigned long signature_days = 0; unsigned long encryption_first_done = 0; unsigned long encryption_most_recent = 0; unsigned long encryption_count = 0; unsigned long encryption_days = 0; int show_warning = 0; if (only_status_fd && ! is_status_enabled ()) return 0; fingerprint_pp = format_hexfingerprint (fingerprint, NULL, 0); /* Get the signature stats. */ rc = gpgsql_exec_printf (dbs->db, strings_collect_cb, &strlist, &err, "select count (*), coalesce (min (signatures.time), 0),\n" " coalesce (max (signatures.time), 0)\n" " from signatures\n" " left join bindings on signatures.binding = bindings.oid\n" " where fingerprint = %Q and email = %Q;", fingerprint, email); if (rc) { log_error (_("error reading TOFU database: %s\n"), err); print_further_info ("getting signature statistics"); sqlite3_free (err); rc = gpg_error (GPG_ERR_GENERAL); goto out; } rc = gpgsql_exec_printf (dbs->db, strings_collect_cb, &strlist, &err, "select count (*) from\n" " (select round(signatures.time / (24 * 60 * 60)) day\n" " from signatures\n" " left join bindings on signatures.binding = bindings.oid\n" " where fingerprint = %Q and email = %Q\n" " group by day);", fingerprint, email); if (rc) { log_error (_("error reading TOFU database: %s\n"), err); print_further_info ("getting signature statistics (by day)"); sqlite3_free (err); rc = gpg_error (GPG_ERR_GENERAL); goto out; } if (strlist) { /* We expect exactly 4 elements. */ log_assert (strlist->next); log_assert (strlist->next->next); log_assert (strlist->next->next->next); log_assert (! strlist->next->next->next->next); string_to_ulong (&signature_days, strlist->d, -1, __LINE__); string_to_ulong (&signature_count, strlist->next->d, -1, __LINE__); string_to_ulong (&signature_first_seen, strlist->next->next->d, -1, __LINE__); string_to_ulong (&signature_most_recent, strlist->next->next->next->d, -1, __LINE__); free_strlist (strlist); strlist = NULL; } /* Get the encryption stats. */ rc = gpgsql_exec_printf (dbs->db, strings_collect_cb, &strlist, &err, "select count (*), coalesce (min (encryptions.time), 0),\n" " coalesce (max (encryptions.time), 0)\n" " from encryptions\n" " left join bindings on encryptions.binding = bindings.oid\n" " where fingerprint = %Q and email = %Q;", fingerprint, email); if (rc) { log_error (_("error reading TOFU database: %s\n"), err); print_further_info ("getting encryption statistics"); sqlite3_free (err); rc = gpg_error (GPG_ERR_GENERAL); goto out; } rc = gpgsql_exec_printf (dbs->db, strings_collect_cb, &strlist, &err, "select count (*) from\n" " (select round(encryptions.time / (24 * 60 * 60)) day\n" " from encryptions\n" " left join bindings on encryptions.binding = bindings.oid\n" " where fingerprint = %Q and email = %Q\n" " group by day);", fingerprint, email); if (rc) { log_error (_("error reading TOFU database: %s\n"), err); print_further_info ("getting encryption statistics (by day)"); sqlite3_free (err); rc = gpg_error (GPG_ERR_GENERAL); goto out; } if (strlist) { /* We expect exactly 4 elements. */ log_assert (strlist->next); log_assert (strlist->next->next); log_assert (strlist->next->next->next); log_assert (! strlist->next->next->next->next); string_to_ulong (&encryption_days, strlist->d, -1, __LINE__); string_to_ulong (&encryption_count, strlist->next->d, -1, __LINE__); string_to_ulong (&encryption_first_done, strlist->next->next->d, -1, __LINE__); string_to_ulong (&encryption_most_recent, strlist->next->next->next->d, -1, __LINE__); free_strlist (strlist); strlist = NULL; } if (!outfp) write_status_text_and_buffer (STATUS_TOFU_USER, fingerprint, email, strlen (email), 0); write_stats_status (outfp, policy, signature_count, signature_first_seen, signature_most_recent, signature_days, encryption_count, encryption_first_done, encryption_most_recent, encryption_days); if (!outfp && !only_status_fd) { estream_t fp; char *msg; fp = es_fopenmem (0, "rw,samethread"); if (! fp) log_fatal ("error creating memory stream: %s\n", gpg_strerror (gpg_error_from_syserror())); if (signature_count == 0 && encryption_count == 0) { es_fprintf (fp, _("%s: Verified 0~signatures and encrypted 0~messages."), email); } else { if (signature_count == 0) es_fprintf (fp, _("%s: Verified 0 signatures."), email); else { /* Note: Translation not possible with that wording. */ char *ago_str = time_ago_str (now - signature_first_seen); es_fprintf (fp, "%s: Verified %ld~signatures in the past %s.", email, signature_count, ago_str); xfree (ago_str); } es_fputs (" ", fp); if (encryption_count == 0) es_fprintf (fp, _("Encrypted 0 messages.")); else { char *ago_str = time_ago_str (now - encryption_first_done); /* Note: Translation not possible with this kind of * composition. */ es_fprintf (fp, "Encrypted %ld~messages in the past %s.", encryption_count, ago_str); xfree (ago_str); } } if (opt.verbose) { es_fputs (" ", fp); es_fprintf (fp, _("(policy: %s)"), tofu_policy_str (policy)); } es_fputs ("\n", fp); { char *tmpmsg, *p; es_fputc (0, fp); if (es_fclose_snatch (fp, (void **) &tmpmsg, NULL)) log_fatal ("error snatching memory stream\n"); msg = format_text (tmpmsg, 72, 80); if (!msg) /* FIXME: Return the error all the way up. */ log_fatal ("format failed: %s\n", gpg_strerror (gpg_error_from_syserror())); es_free (tmpmsg); /* Print a status line but suppress the trailing LF. * Spaces are not percent escaped. */ if (*msg) write_status_buffer (STATUS_TOFU_STATS_LONG, msg, strlen (msg)-1, -1); /* Remove the non-breaking space markers. */ for (p=msg; *p; p++) if (*p == '~') *p = ' '; } log_string (GPGRT_LOGLVL_INFO, msg); xfree (msg); if (policy == TOFU_POLICY_AUTO) { if (signature_count == 0) log_info (_("Warning: we have yet to see" " a message signed using this key and user id!\n")); else if (signature_count == 1) log_info (_("Warning: we've only seen one message" " signed using this key and user id!\n")); if (encryption_count == 0) log_info (_("Warning: you have yet to encrypt" " a message to this key!\n")); else if (encryption_count == 1) log_info (_("Warning: you have only encrypted" " one message to this key!\n")); /* Cf. write_stats_status */ if ((encryption_count * encryption_count + signature_count * signature_count) < ((2 * BASIC_TRUST_THRESHOLD) * (2 * BASIC_TRUST_THRESHOLD))) show_warning = 1; } } out: xfree (fingerprint_pp); return show_warning; } static void show_warning (const char *fingerprint, strlist_t user_id_list) { char *set_policy_command; char *text; char *tmpmsg; set_policy_command = xasprintf ("gpg --tofu-policy bad %s", fingerprint); tmpmsg = xasprintf (ngettext ("Warning: if you think you've seen more signatures " "by this key and user id, then this key might be a " "forgery! Carefully examine the email address for small " "variations. If the key is suspect, then use\n" " %s\n" "to mark it as being bad.\n", "Warning: if you think you've seen more signatures " "by this key and these user ids, then this key might be a " "forgery! Carefully examine the email addresses for small " "variations. If the key is suspect, then use\n" " %s\n" "to mark it as being bad.\n", strlist_length (user_id_list)), set_policy_command); text = format_text (tmpmsg, 72, 80); if (!text) /* FIXME: Return the error all the way up. */ log_fatal ("format failed: %s\n", gpg_strerror (gpg_error_from_syserror())); xfree (tmpmsg); log_string (GPGRT_LOGLVL_INFO, text); xfree (text); es_free (set_policy_command); } /* Extract the email address from a user id and normalize it. If the user id doesn't contain an email address, then we use the whole user_id and normalize that. The returned string must be freed. */ static char * email_from_user_id (const char *user_id) { char *email = mailbox_from_userid (user_id, 0); if (! email) { /* Hmm, no email address was provided or we are out of core. Just take the lower-case version of the whole user id. It could be a hostname, for instance. */ email = ascii_strlwr (xstrdup (user_id)); } return email; } /* Register the signature with the bindings , for each USER_ID in USER_ID_LIST. The fingerprint is taken from the primary key packet PK. SIG_DIGEST_BIN is the binary representation of the message's digest. SIG_DIGEST_BIN_LEN is its length. SIG_TIME is the time that the signature was generated. ORIGIN is a free-formed string describing the origin of the signature. If this was from an email and the Claws MUA was used, then this should be something like: "email:claws". If this is NULL, the default is simply "unknown". If MAY_ASK is 1, then this function may interact with the user. This is necessary if there is a conflict or the binding's policy is TOFU_POLICY_ASK. This function returns 0 on success and an error code if an error occurred. */ gpg_error_t tofu_register_signature (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list, const byte *sig_digest_bin, int sig_digest_bin_len, time_t sig_time, const char *origin) { time_t now = gnupg_get_time (); gpg_error_t rc; tofu_dbs_t dbs; char *fingerprint = NULL; strlist_t user_id; char *email = NULL; char *sqlerr = NULL; char *sig_digest = NULL; unsigned long c; dbs = opendbs (ctrl); if (! dbs) { rc = gpg_error (GPG_ERR_GENERAL); log_error (_("error opening TOFU database: %s\n"), gpg_strerror (rc)); return rc; } /* We do a query and then an insert. Make sure they are atomic by wrapping them in a transaction. */ rc = begin_transaction (ctrl, 0); if (rc) return rc; log_assert (pk_is_primary (pk)); sig_digest = make_radix64_string (sig_digest_bin, sig_digest_bin_len); if (!sig_digest) { rc = gpg_error_from_syserror (); goto leave; } fingerprint = hexfingerprint (pk, NULL, 0); if (!fingerprint) { rc = gpg_error_from_syserror (); goto leave; } if (! origin) origin = "unknown"; /* The default origin is simply "unknown". */ for (user_id = user_id_list; user_id; user_id = user_id->next) { email = email_from_user_id (user_id->d); if (DBG_TRUST) log_debug ("TOFU: Registering signature %s with binding" " \n", sig_digest, fingerprint, email); /* Make sure the binding exists and record any TOFU conflicts. */ if (get_trust (ctrl, pk, fingerprint, email, user_id->d, 0, NULL, NULL, now) == _tofu_GET_TRUST_ERROR) { rc = gpg_error (GPG_ERR_GENERAL); xfree (email); break; } /* If we've already seen this signature before, then don't add it again. */ rc = gpgsql_stepx (dbs->db, &dbs->s.register_already_seen, get_single_unsigned_long_cb2, &c, &sqlerr, "select count (*)\n" " from signatures left join bindings\n" " on signatures.binding = bindings.oid\n" " where fingerprint = ? and email = ? and sig_time = ?\n" " and sig_digest = ?", GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, GPGSQL_ARG_LONG_LONG, (long long) sig_time, GPGSQL_ARG_STRING, sig_digest, GPGSQL_ARG_END); if (rc) { log_error (_("error reading TOFU database: %s\n"), sqlerr); print_further_info ("checking existence"); sqlite3_free (sqlerr); rc = gpg_error (GPG_ERR_GENERAL); } else if (c > 1) /* Duplicates! This should not happen. In particular, because is the primary key! */ log_debug ("SIGNATURES DB contains duplicate records" " ." " Please report.\n", fingerprint, email, (unsigned long) sig_time, sig_digest, origin); else if (c == 1) { if (DBG_TRUST) log_debug ("Already observed the signature and binding" " \n", fingerprint, email, (unsigned long) sig_time, sig_digest, origin); } else if (opt.dry_run) { log_info ("TOFU database update skipped due to --dry-run\n"); } else /* This is the first time that we've seen this signature and binding. Record it. */ { if (DBG_TRUST) log_debug ("TOFU: Saving signature" " \n", fingerprint, email, sig_digest); log_assert (c == 0); rc = gpgsql_stepx (dbs->db, &dbs->s.register_signature, NULL, NULL, &sqlerr, "insert into signatures\n" " (binding, sig_digest, origin, sig_time, time)\n" " values\n" " ((select oid from bindings\n" " where fingerprint = ? and email = ?),\n" " ?, ?, ?, ?);", GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, GPGSQL_ARG_STRING, sig_digest, GPGSQL_ARG_STRING, origin, GPGSQL_ARG_LONG_LONG, (long long) sig_time, GPGSQL_ARG_LONG_LONG, (long long) now, GPGSQL_ARG_END); if (rc) { log_error (_("error updating TOFU database: %s\n"), sqlerr); print_further_info ("insert signatures"); sqlite3_free (sqlerr); rc = gpg_error (GPG_ERR_GENERAL); } } xfree (email); if (rc) break; } leave: if (rc) rollback_transaction (ctrl); else rc = end_transaction (ctrl, 0); xfree (fingerprint); xfree (sig_digest); return rc; } gpg_error_t tofu_register_encryption (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list, int may_ask) { time_t now = gnupg_get_time (); gpg_error_t rc = 0; tofu_dbs_t dbs; kbnode_t kb = NULL; int free_user_id_list = 0; char *fingerprint = NULL; strlist_t user_id; char *sqlerr = NULL; int in_batch = 0; dbs = opendbs (ctrl); if (! dbs) { rc = gpg_error (GPG_ERR_GENERAL); log_error (_("error opening TOFU database: %s\n"), gpg_strerror (rc)); return rc; } if (/* We need the key block to find the primary key. */ ! pk_is_primary (pk) /* We need the key block to find all user ids. */ || ! user_id_list) kb = get_pubkeyblock (ctrl, pk->keyid); /* Make sure PK is a primary key. */ if (! pk_is_primary (pk)) pk = kb->pkt->pkt.public_key; if (! user_id_list) { /* Use all non-revoked user ids. Do use expired user ids. */ kbnode_t n = kb; while ((n = find_next_kbnode (n, PKT_USER_ID))) { PKT_user_id *uid = n->pkt->pkt.user_id; if (uid->flags.revoked) continue; add_to_strlist (&user_id_list, uid->name); } free_user_id_list = 1; if (! user_id_list) log_info (_("WARNING: Encrypting to %s, which has no " "non-revoked user ids\n"), keystr (pk->keyid)); } fingerprint = hexfingerprint (pk, NULL, 0); if (!fingerprint) { rc = gpg_error_from_syserror (); goto leave; } tofu_begin_batch_update (ctrl); in_batch = 1; tofu_resume_batch_transaction (ctrl); for (user_id = user_id_list; user_id; user_id = user_id->next) { char *email = email_from_user_id (user_id->d); strlist_t conflict_set = NULL; enum tofu_policy policy; /* Make sure the binding exists and that we recognize any conflicts. */ int tl = get_trust (ctrl, pk, fingerprint, email, user_id->d, may_ask, &policy, &conflict_set, now); if (tl == _tofu_GET_TRUST_ERROR) { /* An error. */ rc = gpg_error (GPG_ERR_GENERAL); xfree (email); goto leave; } /* If there is a conflict and MAY_ASK is true, we need to show * the TOFU statistics for the current binding and the * conflicting bindings. But, if we are not in batch mode, then * they have already been printed (this is required to make sure * the information is available to the caller before cpr_get is * called). */ if (policy == TOFU_POLICY_ASK && may_ask && opt.batch) { strlist_t iter; /* The conflict set should contain at least the current * key. */ log_assert (conflict_set); for (iter = conflict_set; iter; iter = iter->next) show_statistics (dbs, iter->d, email, TOFU_POLICY_ASK, NULL, 1, now); } free_strlist (conflict_set); rc = gpgsql_stepx (dbs->db, &dbs->s.register_encryption, NULL, NULL, &sqlerr, "insert into encryptions\n" " (binding, time)\n" " values\n" " ((select oid from bindings\n" " where fingerprint = ? and email = ?),\n" " ?);", GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_STRING, email, GPGSQL_ARG_LONG_LONG, (long long) now, GPGSQL_ARG_END); if (rc) { log_error (_("error updating TOFU database: %s\n"), sqlerr); print_further_info ("insert encryption"); sqlite3_free (sqlerr); rc = gpg_error (GPG_ERR_GENERAL); } xfree (email); } leave: if (in_batch) tofu_end_batch_update (ctrl); release_kbnode (kb); if (free_user_id_list) free_strlist (user_id_list); xfree (fingerprint); return rc; } /* Combine a trust level returned from the TOFU trust model with a trust level returned by the PGP trust model. This is primarily of interest when the trust model is tofu+pgp (TM_TOFU_PGP). This function ors together the upper bits (the values not covered by TRUST_MASK, i.e., TRUST_FLAG_REVOKED, etc.). */ int tofu_wot_trust_combine (int tofu_base, int wot_base) { int tofu = tofu_base & TRUST_MASK; int wot = wot_base & TRUST_MASK; int upper = (tofu_base & ~TRUST_MASK) | (wot_base & ~TRUST_MASK); log_assert (tofu == TRUST_UNKNOWN || tofu == TRUST_EXPIRED || tofu == TRUST_UNDEFINED || tofu == TRUST_NEVER || tofu == TRUST_MARGINAL || tofu == TRUST_FULLY || tofu == TRUST_ULTIMATE); log_assert (wot == TRUST_UNKNOWN || wot == TRUST_EXPIRED || wot == TRUST_UNDEFINED || wot == TRUST_NEVER || wot == TRUST_MARGINAL || wot == TRUST_FULLY || wot == TRUST_ULTIMATE); /* We first consider negative trust policys. These trump positive trust policies. */ if (tofu == TRUST_NEVER || wot == TRUST_NEVER) /* TRUST_NEVER trumps everything else. */ return upper | TRUST_NEVER; if (tofu == TRUST_EXPIRED || wot == TRUST_EXPIRED) /* TRUST_EXPIRED trumps everything but TRUST_NEVER. */ return upper | TRUST_EXPIRED; /* Now we only have positive or neutral trust policies. We take the max. */ if (tofu == TRUST_ULTIMATE) return upper | TRUST_ULTIMATE | TRUST_FLAG_TOFU_BASED; if (wot == TRUST_ULTIMATE) return upper | TRUST_ULTIMATE; if (tofu == TRUST_FULLY) return upper | TRUST_FULLY | TRUST_FLAG_TOFU_BASED; if (wot == TRUST_FULLY) return upper | TRUST_FULLY; if (tofu == TRUST_MARGINAL) return upper | TRUST_MARGINAL | TRUST_FLAG_TOFU_BASED; if (wot == TRUST_MARGINAL) return upper | TRUST_MARGINAL; if (tofu == TRUST_UNDEFINED) return upper | TRUST_UNDEFINED | TRUST_FLAG_TOFU_BASED; if (wot == TRUST_UNDEFINED) return upper | TRUST_UNDEFINED; return upper | TRUST_UNKNOWN; } /* Write a "tfs" record for a --with-colons listing. */ gpg_error_t tofu_write_tfs_record (ctrl_t ctrl, estream_t fp, PKT_public_key *pk, const char *user_id) { time_t now = gnupg_get_time (); gpg_error_t err = 0; tofu_dbs_t dbs; char *fingerprint; char *email = NULL; enum tofu_policy policy; if (!*user_id) return 0; /* No TOFU stats possible for an empty ID. */ dbs = opendbs (ctrl); if (!dbs) { err = gpg_error (GPG_ERR_GENERAL); log_error (_("error opening TOFU database: %s\n"), gpg_strerror (err)); return err; } fingerprint = hexfingerprint (pk, NULL, 0); if (!fingerprint) { err = gpg_error_from_syserror (); goto leave; } email = email_from_user_id (user_id); policy = get_policy (ctrl, dbs, pk, fingerprint, user_id, email, NULL, now); show_statistics (dbs, fingerprint, email, policy, fp, 0, now); leave: xfree (email); xfree (fingerprint); return err; } /* Return the validity (TRUST_NEVER, etc.) of the bindings , for each USER_ID in USER_ID_LIST. If USER_ID_LIST->FLAG is set, then the id is considered to be expired. PK is the primary key packet. If MAY_ASK is 1 and the policy is TOFU_POLICY_ASK, then the user will be prompted to choose a policy. If MAY_ASK is 0 and the policy is TOFU_POLICY_ASK, then TRUST_UNKNOWN is returned. Returns TRUST_UNDEFINED if an error occurs. Fixme: eturn an error code */ int tofu_get_validity (ctrl_t ctrl, PKT_public_key *pk, strlist_t user_id_list, int may_ask) { time_t now = gnupg_get_time (); tofu_dbs_t dbs; char *fingerprint = NULL; strlist_t user_id; int trust_level = TRUST_UNKNOWN; int bindings = 0; int bindings_valid = 0; int need_warning = 0; int had_conflict = 0; dbs = opendbs (ctrl); if (! dbs) { log_error (_("error opening TOFU database: %s\n"), gpg_strerror (GPG_ERR_GENERAL)); return TRUST_UNDEFINED; } fingerprint = hexfingerprint (pk, NULL, 0); if (!fingerprint) log_fatal ("%s: malloc failed\n", __func__); tofu_begin_batch_update (ctrl); /* Start the batch transaction now. */ tofu_resume_batch_transaction (ctrl); for (user_id = user_id_list; user_id; user_id = user_id->next, bindings ++) { char *email = email_from_user_id (user_id->d); strlist_t conflict_set = NULL; enum tofu_policy policy; /* Always call get_trust to make sure the binding is registered. */ int tl = get_trust (ctrl, pk, fingerprint, email, user_id->d, may_ask, &policy, &conflict_set, now); if (tl == _tofu_GET_TRUST_ERROR) { /* An error. */ trust_level = TRUST_UNDEFINED; xfree (email); goto die; } if (DBG_TRUST) log_debug ("TOFU: validity for : %s%s.\n", fingerprint, email, trust_value_to_string (tl), user_id->flags ? " (but expired)" : ""); if (user_id->flags) tl = TRUST_EXPIRED; if (tl != TRUST_EXPIRED) bindings_valid ++; if (may_ask && tl != TRUST_ULTIMATE && tl != TRUST_EXPIRED) { /* If policy is ask, then we already printed out the * conflict information in ask_about_binding or will do so * in a moment. */ if (policy != TOFU_POLICY_ASK) need_warning |= show_statistics (dbs, fingerprint, email, policy, NULL, 0, now); /* If there is a conflict and MAY_ASK is true, we need to * show the TOFU statistics for the current binding and the * conflicting bindings. But, if we are not in batch mode, * then they have already been printed (this is required to * make sure the information is available to the caller * before cpr_get is called). */ if (policy == TOFU_POLICY_ASK && opt.batch) { strlist_t iter; /* The conflict set should contain at least the current * key. */ log_assert (conflict_set); had_conflict = 1; for (iter = conflict_set; iter; iter = iter->next) show_statistics (dbs, iter->d, email, TOFU_POLICY_ASK, NULL, 1, now); } } free_strlist (conflict_set); if (tl == TRUST_NEVER) trust_level = TRUST_NEVER; else if (tl == TRUST_EXPIRED) /* Ignore expired bindings in the trust calculation. */ ; else if (tl > trust_level) { /* The expected values: */ log_assert (tl == TRUST_UNKNOWN || tl == TRUST_UNDEFINED || tl == TRUST_MARGINAL || tl == TRUST_FULLY || tl == TRUST_ULTIMATE); /* We assume the following ordering: */ log_assert (TRUST_UNKNOWN < TRUST_UNDEFINED); log_assert (TRUST_UNDEFINED < TRUST_MARGINAL); log_assert (TRUST_MARGINAL < TRUST_FULLY); log_assert (TRUST_FULLY < TRUST_ULTIMATE); trust_level = tl; } xfree (email); } if (need_warning && ! had_conflict) show_warning (fingerprint, user_id_list); die: tofu_end_batch_update (ctrl); xfree (fingerprint); if (bindings_valid == 0) { if (DBG_TRUST) log_debug ("no (of %d) valid bindings." " Can't get TOFU validity for this set of user ids.\n", bindings); return TRUST_NEVER; } return trust_level; } /* Set the policy for all non-revoked user ids in the keyblock KB to POLICY. If no key is available with the specified key id, then this function returns GPG_ERR_NO_PUBKEY. Returns 0 on success and an error code otherwise. */ gpg_error_t tofu_set_policy (ctrl_t ctrl, kbnode_t kb, enum tofu_policy policy) { gpg_error_t err = 0; time_t now = gnupg_get_time (); tofu_dbs_t dbs; PKT_public_key *pk; char *fingerprint = NULL; log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); pk = kb->pkt->pkt.public_key; dbs = opendbs (ctrl); if (! dbs) { log_error (_("error opening TOFU database: %s\n"), gpg_strerror (GPG_ERR_GENERAL)); return gpg_error (GPG_ERR_GENERAL); } if (DBG_TRUST) log_debug ("Setting TOFU policy for %s to %s\n", keystr (pk->keyid), tofu_policy_str (policy)); if (! pk_is_primary (pk)) log_bug ("%s: Passed a subkey, but expecting a primary key.\n", __func__); fingerprint = hexfingerprint (pk, NULL, 0); if (!fingerprint) return gpg_error_from_syserror (); begin_transaction (ctrl, 0); for (; kb; kb = kb->next) { PKT_user_id *user_id; char *email; if (kb->pkt->pkttype != PKT_USER_ID) continue; user_id = kb->pkt->pkt.user_id; if (user_id->flags.revoked) /* Skip revoked user ids. (Don't skip expired user ids, the expiry can be changed.) */ continue; email = email_from_user_id (user_id->name); err = record_binding (dbs, fingerprint, email, user_id->name, policy, TOFU_POLICY_NONE, NULL, 0, 1, now); if (err) { log_error ("error setting policy for key %s, user id \"%s\": %s", fingerprint, email, gpg_strerror (err)); xfree (email); break; } xfree (email); } if (err) rollback_transaction (ctrl); else end_transaction (ctrl, 0); xfree (fingerprint); return err; } /* Return the TOFU policy for the specified binding in *POLICY. If no policy has been set for the binding, sets *POLICY to TOFU_POLICY_NONE. PK is a primary public key and USER_ID is a user id. Returns 0 on success and an error code otherwise. */ gpg_error_t tofu_get_policy (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *user_id, enum tofu_policy *policy) { time_t now = gnupg_get_time (); tofu_dbs_t dbs; char *fingerprint; char *email; /* Make sure PK is a primary key. */ log_assert (pk_is_primary (pk)); dbs = opendbs (ctrl); if (! dbs) { log_error (_("error opening TOFU database: %s\n"), gpg_strerror (GPG_ERR_GENERAL)); return gpg_error (GPG_ERR_GENERAL); } fingerprint = hexfingerprint (pk, NULL, 0); if (!fingerprint) return gpg_error_from_syserror (); email = email_from_user_id (user_id->name); *policy = get_policy (ctrl, dbs, pk, fingerprint, user_id->name, email, NULL, now); xfree (email); xfree (fingerprint); if (*policy == _tofu_GET_POLICY_ERROR) return gpg_error (GPG_ERR_GENERAL); return 0; } gpg_error_t tofu_notice_key_changed (ctrl_t ctrl, kbnode_t kb) { tofu_dbs_t dbs; PKT_public_key *pk; char *fingerprint; char *sqlerr = NULL; int rc; /* Make sure PK is a primary key. */ setup_main_keyids (kb); pk = kb->pkt->pkt.public_key; log_assert (pk_is_primary (pk)); dbs = opendbs (ctrl); if (! dbs) { log_error (_("error opening TOFU database: %s\n"), gpg_strerror (GPG_ERR_GENERAL)); return gpg_error (GPG_ERR_GENERAL); } fingerprint = hexfingerprint (pk, NULL, 0); if (!fingerprint) return gpg_error_from_syserror (); rc = gpgsql_stepx (dbs->db, NULL, NULL, NULL, &sqlerr, "update bindings set effective_policy = ?" " where fingerprint = ?;", GPGSQL_ARG_INT, (int) TOFU_POLICY_NONE, GPGSQL_ARG_STRING, fingerprint, GPGSQL_ARG_END); xfree (fingerprint); if (rc == _tofu_GET_POLICY_ERROR) return gpg_error (GPG_ERR_GENERAL); return 0; } diff --git a/g10/trustdb.c b/g10/trustdb.c index a230a6c03..352aecede 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -1,2258 +1,2258 @@ /* trustdb.c * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007, * 2008, 2012 Free Software Foundation, Inc. * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #ifndef DISABLE_REGEX #include #include #endif /* !DISABLE_REGEX */ #include "gpg.h" #include "../common/status.h" #include "../common/iobuf.h" #include "keydb.h" #include "../common/util.h" #include "options.h" #include "packet.h" #include "main.h" #include "../common/mbox-util.h" #include "../common/i18n.h" #include "tdbio.h" #include "trustdb.h" #include "tofu.h" #include "key-clean.h" typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */ /* * Structure to keep track of keys, this is used as an array wherre * the item right after the last one has a keyblock set to NULL. * Maybe we can drop this thing and replace it by key_item */ struct key_array { KBNODE keyblock; }; /* Control information for the trust DB. */ static struct { int init; int level; char *dbname; int no_trustdb; } trustdb_args; /* Some globals. */ static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */ static struct key_item *utk_list; /* all ultimately trusted keys */ static int pending_check_trustdb; static int validate_keys (ctrl_t ctrl, int interactive); /********************************************** ************* some helpers ******************* **********************************************/ static struct key_item * new_key_item (void) { struct key_item *k; k = xmalloc_clear (sizeof *k); return k; } static void release_key_items (struct key_item *k) { struct key_item *k2; for (; k; k = k2) { k2 = k->next; xfree (k->trust_regexp); xfree (k); } } #define KEY_HASH_TABLE_SIZE 1024 /* * For fast keylook up we need a hash table. Each byte of a KeyID * should be distributed equally over the 256 possible values (except * for v3 keyIDs but we consider them as not important here). So we * can just use 10 bits to index a table of KEY_HASH_TABLE_SIZE key items. * Possible optimization: Do not use key_items but other hash_table when the * duplicates lists get too large. */ static KeyHashTable new_key_hash_table (void) { struct key_item **tbl; tbl = xmalloc_clear (KEY_HASH_TABLE_SIZE * sizeof *tbl); return tbl; } static void release_key_hash_table (KeyHashTable tbl) { int i; if (!tbl) return; for (i=0; i < KEY_HASH_TABLE_SIZE; i++) release_key_items (tbl[i]); xfree (tbl); } /* * Returns: True if the keyID is in the given hash table */ static int test_key_hash_table (KeyHashTable tbl, u32 *kid) { struct key_item *k; for (k = tbl[(kid[1] % KEY_HASH_TABLE_SIZE)]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return 1; return 0; } /* * Add a new key to the hash table. The key is identified by its key ID. */ static void add_key_hash_table (KeyHashTable tbl, u32 *kid) { int i = kid[1] % KEY_HASH_TABLE_SIZE; struct key_item *k, *kk; for (k = tbl[i]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return; /* already in table */ kk = new_key_item (); kk->kid[0] = kid[0]; kk->kid[1] = kid[1]; kk->next = tbl[i]; tbl[i] = kk; } /* * Release a key_array */ static void release_key_array ( struct key_array *keys ) { struct key_array *k; if (keys) { for (k=keys; k->keyblock; k++) release_kbnode (k->keyblock); xfree (keys); } } /********************************************* ********** Initialization ***************** *********************************************/ /* * Used to register extra ultimately trusted keys - this has to be done * before initializing the validation module. * FIXME: Should be replaced by a function to add those keys to the trustdb. */ void tdb_register_trusted_keyid (u32 *keyid) { struct key_item *k; k = new_key_item (); k->kid[0] = keyid[0]; k->kid[1] = keyid[1]; k->next = user_utk_list; user_utk_list = k; } void tdb_register_trusted_key( const char *string ) { gpg_error_t err; KEYDB_SEARCH_DESC desc; err = classify_user_id (string, &desc, 1); if (err || desc.mode != KEYDB_SEARCH_MODE_LONG_KID ) { log_error(_("'%s' is not a valid long keyID\n"), string ); return; } register_trusted_keyid(desc.u.kid); } /* * Helper to add a key to the global list of ultimately trusted keys. * Returns: true = inserted, false = already in list. */ static int add_utk (u32 *kid) { struct key_item *k; if (tdb_keyid_is_utk (kid)) return 0; k = new_key_item (); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->ownertrust = TRUST_ULTIMATE; k->next = utk_list; utk_list = k; if( opt.verbose > 1 ) log_info(_("key %s: accepted as trusted key\n"), keystr(kid)); return 1; } /**************** * Verify that all our secret keys are usable and put them into the utk_list. */ static void verify_own_keys (ctrl_t ctrl) { TRUSTREC rec; ulong recnum; int rc; struct key_item *k; if (utk_list) return; /* scan the trustdb to find all ultimately trusted keys */ for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { if ( rec.rectype == RECTYPE_TRUST && (rec.r.trust.ownertrust & TRUST_MASK) == TRUST_ULTIMATE) { byte *fpr = rec.r.trust.fingerprint; int fprlen; u32 kid[2]; /* Problem: We do only use fingerprints in the trustdb but * we need the keyID here to indetify the key; we can only * use that ugly hack to distinguish between 16 and 20 * butes fpr - it does not work always so we better change * the whole validation code to only work with * fingerprints */ fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20; keyid_from_fingerprint (ctrl, fpr, fprlen, kid); if (!add_utk (kid)) log_info(_("key %s occurs more than once in the trustdb\n"), keystr(kid)); } } /* Put any --trusted-key keys into the trustdb */ for (k = user_utk_list; k; k = k->next) { if ( add_utk (k->kid) ) { /* not yet in trustDB as ultimately trusted */ PKT_public_key pk; memset (&pk, 0, sizeof pk); rc = get_pubkey (ctrl, &pk, k->kid); if (rc) log_info(_("key %s: no public key for trusted key - skipped\n"), keystr(k->kid)); else { tdb_update_ownertrust (ctrl, &pk, ((tdb_get_ownertrust (ctrl, &pk, 0) & ~TRUST_MASK) | TRUST_ULTIMATE )); release_public_key_parts (&pk); } log_info (_("key %s marked as ultimately trusted\n"),keystr(k->kid)); } } /* release the helper table table */ release_key_items (user_utk_list); user_utk_list = NULL; return; } /* Returns whether KID is on the list of ultimately trusted keys. */ int tdb_keyid_is_utk (u32 *kid) { struct key_item *k; for (k = utk_list; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return 1; return 0; } /* Return the list of ultimately trusted keys. */ struct key_item * tdb_utks (void) { return utk_list; } /********************************************* *********** TrustDB stuff ******************* *********************************************/ /* * Read a record but die if it does not exist */ static void read_record (ulong recno, TRUSTREC *rec, int rectype ) { int rc = tdbio_read_record (recno, rec, rectype); if (rc) { log_error(_("trust record %lu, req type %d: read failed: %s\n"), recno, rec->rectype, gpg_strerror (rc) ); tdbio_invalid(); } if (rectype != rec->rectype) { log_error(_("trust record %lu is not of requested type %d\n"), rec->recnum, rectype); tdbio_invalid(); } } /* * Write a record and die on error */ static void write_record (ctrl_t ctrl, TRUSTREC *rec) { int rc = tdbio_write_record (ctrl, rec); if (rc) { log_error(_("trust record %lu, type %d: write failed: %s\n"), rec->recnum, rec->rectype, gpg_strerror (rc) ); tdbio_invalid(); } } /* * sync the TrustDb and die on error */ static void do_sync(void) { int rc = tdbio_sync (); if(rc) { log_error (_("trustdb: sync failed: %s\n"), gpg_strerror (rc) ); g10_exit(2); } } const char * trust_model_string (int model) { switch (model) { case TM_CLASSIC: return "classic"; case TM_PGP: return "pgp"; case TM_EXTERNAL: return "external"; case TM_TOFU: return "tofu"; case TM_TOFU_PGP: return "tofu+pgp"; case TM_ALWAYS: return "always"; case TM_DIRECT: return "direct"; default: return "unknown"; } } /**************** * Perform some checks over the trustdb * level 0: only open the db * 1: used for initial program startup */ int setup_trustdb( int level, const char *dbname ) { /* just store the args */ if( trustdb_args.init ) return 0; trustdb_args.level = level; trustdb_args.dbname = dbname? xstrdup(dbname): NULL; return 0; } void how_to_fix_the_trustdb () { const char *name = trustdb_args.dbname; if (!name) name = "trustdb.gpg"; log_info (_("You may try to re-create the trustdb using the commands:\n")); log_info (" cd %s\n", default_homedir ()); log_info (" %s --export-ownertrust > otrust.tmp\n", GPG_NAME); #ifdef HAVE_W32_SYSTEM log_info (" del %s\n", name); #else log_info (" rm %s\n", name); #endif log_info (" %s --import-ownertrust < otrust.tmp\n", GPG_NAME); log_info (_("If that does not work, please consult the manual\n")); } /* Initialize the trustdb. With NO_CREATE set a missing trustdb is * not an error and the function won't terminate the process on error; * in that case 0 is returned if there is a trustdb or an error code * if no trustdb is available. */ gpg_error_t init_trustdb (ctrl_t ctrl, int no_create) { int level = trustdb_args.level; const char* dbname = trustdb_args.dbname; if( trustdb_args.init ) return 0; trustdb_args.init = 1; if(level==0 || level==1) { int rc = tdbio_set_dbname (ctrl, dbname, (!no_create && level), &trustdb_args.no_trustdb); if (no_create && trustdb_args.no_trustdb) { /* No trustdb found and the caller asked us not to create * it. Return an error and set the initialization state * back so that we always test for an existing trustdb. */ trustdb_args.init = 0; return gpg_error (GPG_ERR_ENOENT); } if (rc) log_fatal("can't init trustdb: %s\n", gpg_strerror (rc) ); } else BUG(); if(opt.trust_model==TM_AUTO) { /* Try and set the trust model off of whatever the trustdb says it is. */ opt.trust_model=tdbio_read_model(); /* Sanity check this ;) */ if(opt.trust_model != TM_CLASSIC && opt.trust_model != TM_PGP && opt.trust_model != TM_TOFU_PGP && opt.trust_model != TM_TOFU && opt.trust_model != TM_EXTERNAL) { log_info(_("unable to use unknown trust model (%d) - " "assuming %s trust model\n"),opt.trust_model,"pgp"); opt.trust_model = TM_PGP; } if(opt.verbose) log_info(_("using %s trust model\n"), trust_model_string (opt.trust_model)); } if (opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC || opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP) { /* Verify the list of ultimately trusted keys and move the --trusted-keys list there as well. */ if(level==1) verify_own_keys (ctrl); if(!tdbio_db_matches_options()) pending_check_trustdb=1; } return 0; } /* Check whether we have a trust database, initializing it if necessary if the trust model is not 'always trust'. Returns true if we do have a usable trust database. */ int have_trustdb (ctrl_t ctrl) { return !init_trustdb (ctrl, opt.trust_model == TM_ALWAYS); } /**************** * Recreate the WoT but do not ask for new ownertrusts. Special * feature: In batch mode and without a forced yes, this is only done * when a check is due. This can be used to run the check from a crontab */ void check_trustdb (ctrl_t ctrl) { init_trustdb (ctrl, 0); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU) { if (opt.batch && !opt.answer_yes) { ulong scheduled; scheduled = tdbio_read_nextcheck (); if (!scheduled) { log_info (_("no need for a trustdb check\n")); return; } if (scheduled > make_timestamp ()) { log_info (_("next trustdb check due at %s\n"), strtimestamp (scheduled)); return; } } validate_keys (ctrl, 0); } else log_info (_("no need for a trustdb check with '%s' trust model\n"), trust_model_string(opt.trust_model)); } /* * Recreate the WoT. */ void update_trustdb (ctrl_t ctrl) { init_trustdb (ctrl, 0); if (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU) validate_keys (ctrl, 1); else log_info (_("no need for a trustdb update with '%s' trust model\n"), trust_model_string(opt.trust_model)); } void tdb_revalidation_mark (ctrl_t ctrl) { init_trustdb (ctrl, 0); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return; /* We simply set the time for the next check to 1 (far back in 1970) so that a --update-trustdb will be scheduled. */ if (tdbio_write_nextcheck (ctrl, 1)) do_sync (); pending_check_trustdb = 1; } int trustdb_pending_check(void) { return pending_check_trustdb; } /* If the trustdb is dirty, and we're interactive, update it. Otherwise, check it unless no-auto-check-trustdb is set. */ void tdb_check_or_update (ctrl_t ctrl) { if (trustdb_pending_check ()) { if (opt.interactive) update_trustdb (ctrl); else if (!opt.no_auto_check_trustdb) check_trustdb (ctrl); } } void read_trust_options (ctrl_t ctrl, byte *trust_model, ulong *created, ulong *nextcheck, byte *marginals, byte *completes, byte *cert_depth, byte *min_cert_level) { TRUSTREC opts; init_trustdb (ctrl, 0); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) memset (&opts, 0, sizeof opts); else read_record (0, &opts, RECTYPE_VER); if(trust_model) *trust_model=opts.r.ver.trust_model; if(created) *created=opts.r.ver.created; if(nextcheck) *nextcheck=opts.r.ver.nextcheck; if(marginals) *marginals=opts.r.ver.marginals; if(completes) *completes=opts.r.ver.completes; if(cert_depth) *cert_depth=opts.r.ver.cert_depth; if(min_cert_level) *min_cert_level=opts.r.ver.min_cert_level; } /*********************************************** *********** Ownertrust et al. **************** ***********************************************/ static int read_trust_record (ctrl_t ctrl, PKT_public_key *pk, TRUSTREC *rec) { int rc; init_trustdb (ctrl, 0); rc = tdbio_search_trust_bypk (ctrl, pk, rec); if (rc) { if (gpg_err_code (rc) != GPG_ERR_NOT_FOUND) log_error ("trustdb: searching trust record failed: %s\n", gpg_strerror (rc)); return rc; } if (rec->rectype != RECTYPE_TRUST) { log_error ("trustdb: record %lu is not a trust record\n", rec->recnum); return GPG_ERR_TRUSTDB; } return 0; } /* * Return the assigned ownertrust value for the given public key. The * key should be the primary key. If NO_CREATE is set a missing * trustdb will not be created. This comes for example handy when we * want to print status lines (DECRYPTION_KEY) which carry ownertrust * values but we usually use --always-trust. */ unsigned int tdb_get_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int no_create) { TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; /* If the caller asked not to create a trustdb we call init_trustdb * directly and allow it to fail with an error code for a * non-existing trustdb. */ if (no_create && init_trustdb (ctrl, 1)) return TRUST_UNKNOWN; err = read_trust_record (ctrl, pk, &rec); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) return TRUST_UNKNOWN; /* no record yet */ if (err) { tdbio_invalid (); return TRUST_UNKNOWN; /* actually never reached */ } return rec.r.trust.ownertrust; } unsigned int tdb_get_min_ownertrust (ctrl_t ctrl, PKT_public_key *pk, int no_create) { TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; /* If the caller asked not to create a trustdb we call init_trustdb * directly and allow it to fail with an error code for a * non-existing trustdb. */ if (no_create && init_trustdb (ctrl, 1)) return TRUST_UNKNOWN; err = read_trust_record (ctrl, pk, &rec); if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) return TRUST_UNKNOWN; /* no record yet */ if (err) { tdbio_invalid (); return TRUST_UNKNOWN; /* actually never reached */ } return rec.r.trust.min_ownertrust; } /* * Set the trust value of the given public key to the new value. * The key should be a primary one. */ void tdb_update_ownertrust (ctrl_t ctrl, PKT_public_key *pk, unsigned int new_trust ) { TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return; err = read_trust_record (ctrl, pk, &rec); if (!err) { if (DBG_TRUST) log_debug ("update ownertrust from %u to %u\n", (unsigned int)rec.r.trust.ownertrust, new_trust ); if (rec.r.trust.ownertrust != new_trust) { rec.r.trust.ownertrust = new_trust; write_record (ctrl, &rec); tdb_revalidation_mark (ctrl); do_sync (); } } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* no record yet - create a new one */ size_t dummy; if (DBG_TRUST) log_debug ("insert ownertrust %u\n", new_trust ); memset (&rec, 0, sizeof rec); rec.recnum = tdbio_new_recnum (ctrl); rec.rectype = RECTYPE_TRUST; fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy); rec.r.trust.ownertrust = new_trust; write_record (ctrl, &rec); tdb_revalidation_mark (ctrl); do_sync (); } else { tdbio_invalid (); } } static void update_min_ownertrust (ctrl_t ctrl, u32 *kid, unsigned int new_trust) { PKT_public_key *pk; TRUSTREC rec; gpg_error_t err; if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return; pk = xmalloc_clear (sizeof *pk); err = get_pubkey (ctrl, pk, kid); if (err) { log_error (_("public key %s not found: %s\n"), keystr (kid), gpg_strerror (err)); xfree (pk); return; } err = read_trust_record (ctrl, pk, &rec); if (!err) { if (DBG_TRUST) log_debug ("key %08lX%08lX: update min_ownertrust from %u to %u\n", (ulong)kid[0],(ulong)kid[1], (unsigned int)rec.r.trust.min_ownertrust, new_trust ); if (rec.r.trust.min_ownertrust != new_trust) { rec.r.trust.min_ownertrust = new_trust; write_record (ctrl, &rec); tdb_revalidation_mark (ctrl); do_sync (); } } else if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* no record yet - create a new one */ size_t dummy; if (DBG_TRUST) log_debug ("insert min_ownertrust %u\n", new_trust ); memset (&rec, 0, sizeof rec); rec.recnum = tdbio_new_recnum (ctrl); rec.rectype = RECTYPE_TRUST; fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy); rec.r.trust.min_ownertrust = new_trust; write_record (ctrl, &rec); tdb_revalidation_mark (ctrl); do_sync (); } else { tdbio_invalid (); } free_public_key (pk); } /* * Clear the ownertrust and min_ownertrust values. * * Return: True if a change actually happened. */ int tdb_clear_ownertrusts (ctrl_t ctrl, PKT_public_key *pk) { TRUSTREC rec; gpg_error_t err; init_trustdb (ctrl, 0); if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return 0; err = read_trust_record (ctrl, pk, &rec); if (!err) { if (DBG_TRUST) { log_debug ("clearing ownertrust (old value %u)\n", (unsigned int)rec.r.trust.ownertrust); log_debug ("clearing min_ownertrust (old value %u)\n", (unsigned int)rec.r.trust.min_ownertrust); } if (rec.r.trust.ownertrust || rec.r.trust.min_ownertrust) { rec.r.trust.ownertrust = 0; rec.r.trust.min_ownertrust = 0; write_record (ctrl, &rec); tdb_revalidation_mark (ctrl); do_sync (); return 1; } } else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); } return 0; } /* * Note: Caller has to do a sync */ static void update_validity (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid, int depth, int validity) { TRUSTREC trec, vrec; gpg_error_t err; ulong recno; namehash_from_uid(uid); err = read_trust_record (ctrl, pk, &trec); if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); return; } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* No record yet - create a new one. */ size_t dummy; memset (&trec, 0, sizeof trec); trec.recnum = tdbio_new_recnum (ctrl); trec.rectype = RECTYPE_TRUST; fingerprint_from_pk (pk, trec.r.trust.fingerprint, &dummy); trec.r.trust.ownertrust = 0; } /* locate an existing one */ recno = trec.r.trust.validlist; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if ( !memcmp (vrec.r.valid.namehash, uid->namehash, 20) ) break; recno = vrec.r.valid.next; } if (!recno) /* insert a new validity record */ { memset (&vrec, 0, sizeof vrec); vrec.recnum = tdbio_new_recnum (ctrl); vrec.rectype = RECTYPE_VALID; memcpy (vrec.r.valid.namehash, uid->namehash, 20); vrec.r.valid.next = trec.r.trust.validlist; trec.r.trust.validlist = vrec.recnum; } vrec.r.valid.validity = validity; vrec.r.valid.full_count = uid->help_full_count; vrec.r.valid.marginal_count = uid->help_marginal_count; write_record (ctrl, &vrec); trec.r.trust.depth = depth; write_record (ctrl, &trec); } /*********************************************** ********* Query trustdb values ************** ***********************************************/ /* Return true if key is disabled. Note that this is usually used via the pk_is_disabled macro. */ int tdb_cache_disabled_value (ctrl_t ctrl, PKT_public_key *pk) { gpg_error_t err; TRUSTREC trec; int disabled = 0; if (pk->flags.disabled_valid) return pk->flags.disabled; init_trustdb (ctrl, 0); if (trustdb_args.no_trustdb) return 0; /* No trustdb => not disabled. */ err = read_trust_record (ctrl, pk, &trec); if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); goto leave; } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* No record found, so assume not disabled. */ goto leave; } if ((trec.r.trust.ownertrust & TRUST_FLAG_DISABLED)) disabled = 1; /* Cache it for later so we don't need to look at the trustdb every time */ pk->flags.disabled = disabled; pk->flags.disabled_valid = 1; leave: return disabled; } void tdb_check_trustdb_stale (ctrl_t ctrl) { static int did_nextcheck=0; init_trustdb (ctrl, 0); if (trustdb_args.no_trustdb) return; /* No trustdb => can't be stale. */ if (!did_nextcheck && (opt.trust_model == TM_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_TOFU)) { ulong scheduled; did_nextcheck = 1; scheduled = tdbio_read_nextcheck (); if ((scheduled && scheduled <= make_timestamp ()) || pending_check_trustdb) { if (opt.no_auto_check_trustdb) { pending_check_trustdb = 1; if (!opt.quiet) log_info (_("please do a --check-trustdb\n")); } else { if (!opt.quiet) log_info (_("checking the trustdb\n")); validate_keys (ctrl, 0); } } } } /* * Return the validity information for KB/PK (at least one of them * must be non-NULL). This is the core of get_validity. If SIG is * not NULL, then the trust is being evaluated in the context of the * provided signature. This is used by the TOFU code to record * statistics. */ unsigned int tdb_get_validity_core (ctrl_t ctrl, kbnode_t kb, PKT_public_key *pk, PKT_user_id *uid, PKT_public_key *main_pk, PKT_signature *sig, int may_ask) { TRUSTREC trec, vrec; gpg_error_t err = 0; ulong recno; #ifdef USE_TOFU unsigned int tofu_validity = TRUST_UNKNOWN; int free_kb = 0; #endif unsigned int validity = TRUST_UNKNOWN; if (kb && pk) log_assert (keyid_cmp (pk_main_keyid (pk), pk_main_keyid (kb->pkt->pkt.public_key)) == 0); if (! pk) { log_assert (kb); pk = kb->pkt->pkt.public_key; } #ifndef USE_TOFU (void)sig; (void)may_ask; #endif init_trustdb (ctrl, 0); /* If we have no trustdb (which also means it has not been created) and the trust-model is always, we don't know the validity - return immediately. If we won't do that the tdbio code would try to open the trustdb and run into a fatal error. */ if (trustdb_args.no_trustdb && opt.trust_model == TM_ALWAYS) return TRUST_UNKNOWN; check_trustdb_stale (ctrl); if(opt.trust_model==TM_DIRECT) { /* Note that this happens BEFORE any user ID stuff is checked. The direct trust model applies to keys as a whole. */ validity = tdb_get_ownertrust (ctrl, main_pk, 0); goto leave; } #ifdef USE_TOFU if (opt.trust_model == TM_TOFU || opt.trust_model == TM_TOFU_PGP) { kbnode_t n = NULL; strlist_t user_id_list = NULL; int done = 0; /* If the caller didn't supply a user id then use all uids. */ if (! uid) { if (! kb) { kb = get_pubkeyblock (ctrl, main_pk->keyid); free_kb = 1; } n = kb; } if (DBG_TRUST && sig && sig->signers_uid) log_debug ("TOFU: only considering user id: '%s'\n", sig->signers_uid); while (!done && (uid || (n = find_next_kbnode (n, PKT_USER_ID)))) { PKT_user_id *user_id; int expired = 0; if (uid) { user_id = uid; /* If the caller specified a user id, then we only process the specified user id and are done after the first iteration. */ done = 1; } else user_id = n->pkt->pkt.user_id; if (user_id->attrib_data) /* Skip user attributes. */ continue; if (sig && sig->signers_uid) /* Make sure the UID matches. */ { char *email = mailbox_from_userid (user_id->name, 0); if (!email || !*email || strcmp (sig->signers_uid, email) != 0) { if (DBG_TRUST) log_debug ("TOFU: skipping user id '%s', which does" " not match the signer's email ('%s')\n", email, sig->signers_uid); xfree (email); continue; } xfree (email); } /* If the user id is revoked or expired, then skip it. */ if (user_id->flags.revoked || user_id->flags.expired) { if (DBG_TRUST) { char *s; if (user_id->flags.revoked && user_id->flags.expired) s = "revoked and expired"; else if (user_id->flags.revoked) s = "revoked"; else s = "expire"; log_debug ("TOFU: Ignoring %s user id (%s)\n", s, user_id->name); } if (user_id->flags.revoked) continue; expired = 1; } add_to_strlist (&user_id_list, user_id->name); user_id_list->flags = expired; } /* Process the user ids in the order they appear in the key block. */ strlist_rev (&user_id_list); /* It only makes sense to observe any signature before getting the validity. This is because if the current signature results in a conflict, then we damn well want to take that into account. */ if (sig) { err = tofu_register_signature (ctrl, main_pk, user_id_list, sig->digest, sig->digest_len, sig->timestamp, "unknown"); if (err) { log_error ("TOFU: error registering signature: %s\n", gpg_strerror (err)); tofu_validity = TRUST_UNKNOWN; } } if (! err) tofu_validity = tofu_get_validity (ctrl, main_pk, user_id_list, may_ask); free_strlist (user_id_list); if (free_kb) release_kbnode (kb); } #endif /*USE_TOFU*/ if (opt.trust_model == TM_TOFU_PGP || opt.trust_model == TM_CLASSIC || opt.trust_model == TM_PGP) { err = read_trust_record (ctrl, main_pk, &trec); if (err && gpg_err_code (err) != GPG_ERR_NOT_FOUND) { tdbio_invalid (); return 0; } if (gpg_err_code (err) == GPG_ERR_NOT_FOUND) { /* No record found. */ validity = TRUST_UNKNOWN; goto leave; } /* Loop over all user IDs */ recno = trec.r.trust.validlist; validity = 0; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if(uid) { /* If a user ID is given we return the validity for that user ID ONLY. If the namehash is not found, then there is no validity at all (i.e. the user ID wasn't signed). */ if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0) { validity=(vrec.r.valid.validity & TRUST_MASK); break; } } else { /* If no user ID is given, we take the maximum validity over all user IDs */ if (validity < (vrec.r.valid.validity & TRUST_MASK)) validity = (vrec.r.valid.validity & TRUST_MASK); } recno = vrec.r.valid.next; } if ((trec.r.trust.ownertrust & TRUST_FLAG_DISABLED)) { validity |= TRUST_FLAG_DISABLED; pk->flags.disabled = 1; } else pk->flags.disabled = 0; pk->flags.disabled_valid = 1; } leave: #ifdef USE_TOFU validity = tofu_wot_trust_combine (tofu_validity, validity); #else /*!USE_TOFU*/ validity &= TRUST_MASK; if (validity == TRUST_NEVER) /* TRUST_NEVER trumps everything else. */ validity |= TRUST_NEVER; if (validity == TRUST_EXPIRED) /* TRUST_EXPIRED trumps everything but TRUST_NEVER. */ validity |= TRUST_EXPIRED; #endif /*!USE_TOFU*/ if (opt.trust_model != TM_TOFU && pending_check_trustdb) validity |= TRUST_FLAG_PENDING_CHECK; return validity; } static void get_validity_counts (ctrl_t ctrl, PKT_public_key *pk, PKT_user_id *uid) { TRUSTREC trec, vrec; ulong recno; if(pk==NULL || uid==NULL) BUG(); namehash_from_uid(uid); uid->help_marginal_count=uid->help_full_count=0; init_trustdb (ctrl, 0); if(read_trust_record (ctrl, pk, &trec)) return; /* loop over all user IDs */ recno = trec.r.trust.validlist; while (recno) { read_record (recno, &vrec, RECTYPE_VALID); if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0) { uid->help_marginal_count=vrec.r.valid.marginal_count; uid->help_full_count=vrec.r.valid.full_count; /* es_printf("Fetched marginal %d, full %d\n",uid->help_marginal_count,uid->help_full_count); */ break; } recno = vrec.r.valid.next; } } void list_trust_path( const char *username ) { (void)username; } /**************** * Enumerate all keys, which are needed to build all trust paths for * the given key. This function does not return the key itself or * the ultimate key (the last point in cerificate chain). Only * certificate chains which ends up at an ultimately trusted key * are listed. If ownertrust or validity is not NULL, the corresponding * value for the returned LID is also returned in these variable(s). * * 1) create a void pointer and initialize it to NULL * 2) pass this void pointer by reference to this function. * Set lid to the key you want to enumerate and pass it by reference. * 3) call this function as long as it does not return -1 * to indicate EOF. LID does contain the next key used to build the web * 4) Always call this function a last time with LID set to NULL, * so that it can free its context. * * Returns: -1 on EOF or the level of the returned LID */ int enum_cert_paths( void **context, ulong *lid, unsigned *ownertrust, unsigned *validity ) { (void)context; (void)lid; (void)ownertrust; (void)validity; return -1; } /**************** * Print the current path */ void enum_cert_paths_print (void **context, FILE *fp, int refresh, ulong selected_lid) { (void)context; (void)fp; (void)refresh; (void)selected_lid; } /**************************************** *********** NEW NEW NEW **************** ****************************************/ static int ask_ownertrust (ctrl_t ctrl, u32 *kid, int minimum) { PKT_public_key *pk; int rc; int ot; pk = xmalloc_clear (sizeof *pk); rc = get_pubkey (ctrl, pk, kid); if (rc) { log_error (_("public key %s not found: %s\n"), keystr(kid), gpg_strerror (rc) ); return TRUST_UNKNOWN; } if(opt.force_ownertrust) { log_info("force trust for key %s to %s\n", keystr(kid),trust_value_to_string(opt.force_ownertrust)); tdb_update_ownertrust (ctrl, pk, opt.force_ownertrust); ot=opt.force_ownertrust; } else { ot=edit_ownertrust (ctrl, pk, 0); if(ot>0) ot = tdb_get_ownertrust (ctrl, pk, 0); else if(ot==0) ot = minimum?minimum:TRUST_UNDEFINED; else ot = -1; /* quit */ } free_public_key( pk ); return ot; } static void mark_keyblock_seen (KeyHashTable tbl, KBNODE node) { for ( ;node; node = node->next ) if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; keyid_from_pk (node->pkt->pkt.public_key, aki); add_key_hash_table (tbl, aki); } } static void dump_key_array (int depth, struct key_array *keys) { struct key_array *kar; for (kar=keys; kar->keyblock; kar++) { KBNODE node = kar->keyblock; u32 kid[2]; keyid_from_pk(node->pkt->pkt.public_key, kid); es_printf ("%d:%08lX%08lX:K::%c::::\n", depth, (ulong)kid[0], (ulong)kid[1], '?'); for (; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { int len = node->pkt->pkt.user_id->len; if (len > 30) len = 30; es_printf ("%d:%08lX%08lX:U:::%c:::", depth, (ulong)kid[0], (ulong)kid[1], (node->flag & 4)? 'f': (node->flag & 2)? 'm': (node->flag & 1)? 'q':'-'); es_write_sanitized (es_stdout, node->pkt->pkt.user_id->name, len, ":", NULL); es_putc (':', es_stdout); es_putc ('\n', es_stdout); } } } } static void store_validation_status (ctrl_t ctrl, int depth, kbnode_t keyblock, KeyHashTable stored) { KBNODE node; int status; int any = 0; for (node=keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) { PKT_user_id *uid = node->pkt->pkt.user_id; if (node->flag & 4) status = TRUST_FULLY; else if (node->flag & 2) status = TRUST_MARGINAL; else if (node->flag & 1) status = TRUST_UNDEFINED; else status = 0; if (status) { update_validity (ctrl, keyblock->pkt->pkt.public_key, uid, depth, status); mark_keyblock_seen(stored,keyblock); any = 1; } } } if (any) do_sync (); } /* Returns a sanitized copy of the regexp (which might be "", but not NULL). */ #ifndef DISABLE_REGEX /* Operator characters except '.' and backslash. See regex(7) on BSD. */ #define REGEXP_OPERATOR_CHARS "^[$()|*+?{" static char * sanitize_regexp(const char *old) { size_t start=0,len=strlen(old),idx=0; int escaped=0,standard_bracket=0; char *new=xmalloc((len*2)+1); /* enough to \-escape everything if we have to */ /* There are basically two commonly-used regexps here. GPG and most versions of PGP use "<[^>]+[@.]example\.com>$" and PGP (9) command line uses "example.com" (i.e. whatever the user specifies, and we can't expect users know to use "\." instead of "."). So here are the rules: we're allowed to start with "<[^>]+[@.]" and end with ">$" or start and end with nothing. In between, the only legal regex character is ".", and everything else gets escaped. Part of the gotcha here is that some regex packages allow more than RFC-4880 requires. For example, 4880 has no "{}" operator, but GNU regex does. Commenting removes these operators from consideration. A possible future enhancement is to use commenting to effectively back off a given regex to the Henry Spencer syntax in 4880. -dshaw */ /* Are we bracketed between "<[^>]+[@.]" and ">$" ? */ if(len>=12 && strncmp(old,"<[^>]+[@.]",10)==0 && old[len-2]=='>' && old[len-1]=='$') { strcpy(new,"<[^>]+[@.]"); idx=strlen(new); standard_bracket=1; start+=10; len-=2; } /* Walk the remaining characters and ensure that everything that is left is not an operational regex character. */ for(;start$", then it was escaping the ">" and is fine. If the regexp actually ended with the bare "\", then it's an illegal regexp and regcomp should kick it out. */ if(standard_bracket) strcat(new,">$"); return new; } #endif /*!DISABLE_REGEX*/ /* Used by validate_one_keyblock to confirm a regexp within a trust signature. Returns 1 for match, and 0 for no match or regex error. */ static int check_regexp(const char *expr,const char *string) { #ifdef DISABLE_REGEX (void)expr; (void)string; /* When DISABLE_REGEX is defined, assume all regexps do not match. */ return 0; #else int ret; char *regexp; regexp=sanitize_regexp(expr); #ifdef __riscos__ ret=riscos_check_regexp(expr, string, DBG_TRUST); #else { regex_t pat; ret=regcomp(&pat,regexp,REG_ICASE|REG_NOSUB|REG_EXTENDED); if(ret==0) { ret=regexec(&pat,string,0,NULL,0); regfree(&pat); } ret=(ret==0); } #endif if(DBG_TRUST) log_debug("regexp '%s' ('%s') on '%s': %s\n", regexp,expr,string,ret?"YES":"NO"); xfree(regexp); return ret; #endif } /* * Return true if the key is signed by one of the keys in the given * key ID list. User IDs with a valid signature are marked by node * flags as follows: * flag bit 0: There is at least one signature * 1: There is marginal confidence that this is a legitimate uid * 2: There is full confidence that this is a legitimate uid. * 8: Used for internal purposes. * 9: Ditto (in mark_usable_uid_certs()) * 10: Ditto (ditto) * This function assumes that all kbnode flags are cleared on entry. */ static int validate_one_keyblock (ctrl_t ctrl, kbnode_t kb, struct key_item *klist, u32 curtime, u32 *next_expire) { struct key_item *kr; KBNODE node, uidnode=NULL; PKT_user_id *uid=NULL; PKT_public_key *pk = kb->pkt->pkt.public_key; u32 main_kid[2]; int issigned=0, any_signed = 0; keyid_from_pk(pk, main_kid); for (node=kb; node; node = node->next) { /* A bit of discussion here: is it better for the web of trust to be built among only self-signed uids? On the one hand, a self-signed uid is a statement that the key owner definitely intended that uid to be there, but on the other hand, a signed (but not self-signed) uid does carry trust, of a sort, even if it is a statement being made by people other than the key owner "through" the uids on the key owner's key. I'm going with the latter. However, if the user ID was explicitly revoked, or passively allowed to expire, that should stop validity through the user ID until it is resigned. -dshaw */ if (node->pkt->pkttype == PKT_USER_ID && !node->pkt->pkt.user_id->flags.revoked && !node->pkt->pkt.user_id->flags.expired) { if (uidnode && issigned) { if (uid->help_full_count >= opt.completes_needed || uid->help_marginal_count >= opt.marginals_needed ) uidnode->flag |= 4; else if (uid->help_full_count || uid->help_marginal_count) uidnode->flag |= 2; uidnode->flag |= 1; any_signed = 1; } uidnode = node; uid=uidnode->pkt->pkt.user_id; /* If the selfsig is going to expire... */ if(uid->expiredate && uid->expiredate<*next_expire) *next_expire = uid->expiredate; issigned = 0; get_validity_counts (ctrl, pk, uid); mark_usable_uid_certs (ctrl, kb, uidnode, main_kid, klist, curtime, next_expire); } else if (node->pkt->pkttype == PKT_SIGNATURE && (node->flag & (1<<8)) && uid) { /* Note that we are only seeing unrevoked sigs here */ PKT_signature *sig = node->pkt->pkt.signature; kr = is_in_klist (klist, sig); /* If the trust_regexp does not match, it's as if the sig did not exist. This is safe for non-trust sigs as well since we don't accept a regexp on the sig unless it's a trust sig. */ if (kr && (!kr->trust_regexp || !(opt.trust_model == TM_PGP || opt.trust_model == TM_TOFU_PGP) || (uidnode && check_regexp(kr->trust_regexp, uidnode->pkt->pkt.user_id->name)))) { /* Are we part of a trust sig chain? We always favor the latest trust sig, rather than the greater or lesser trust sig or value. I could make a decent argument for any of these cases, but this seems to be what PGP does, and I'd like to be compatible. -dms */ if ((opt.trust_model == TM_PGP || opt.trust_model == TM_TOFU_PGP) && sig->trust_depth && pk->trust_timestamp <= sig->timestamp) { unsigned char depth; /* If the depth on the signature is less than the chain currently has, then use the signature depth so we don't increase the depth beyond what the signer wanted. If the depth on the signature is more than the chain currently has, then use the chain depth so we use as much of the signature depth as the chain will permit. An ultimately trusted signature can restart the depth to whatever level it likes. */ if (sig->trust_depth < kr->trust_depth || kr->ownertrust == TRUST_ULTIMATE) depth = sig->trust_depth; else depth = kr->trust_depth; if (depth) { if(DBG_TRUST) log_debug ("trust sig on %s, sig depth is %d," " kr depth is %d\n", uidnode->pkt->pkt.user_id->name, sig->trust_depth, kr->trust_depth); /* If we got here, we know that: this is a trust sig. it's a newer trust sig than any previous trust sig on this key (not uid). it is legal in that it was either generated by an ultimate key, or a key that was part of a trust chain, and the depth does not violate the original trust sig. if there is a regexp attached, it matched successfully. */ if (DBG_TRUST) log_debug ("replacing trust value %d with %d and " "depth %d with %d\n", pk->trust_value,sig->trust_value, pk->trust_depth,depth); pk->trust_value = sig->trust_value; pk->trust_depth = depth-1; /* If the trust sig contains a regexp, record it on the pk for the next round. */ if (sig->trust_regexp) pk->trust_regexp = sig->trust_regexp; } } if (kr->ownertrust == TRUST_ULTIMATE) uid->help_full_count = opt.completes_needed; else if (kr->ownertrust == TRUST_FULLY) uid->help_full_count++; else if (kr->ownertrust == TRUST_MARGINAL) uid->help_marginal_count++; issigned = 1; } } } if (uidnode && issigned) { if (uid->help_full_count >= opt.completes_needed || uid->help_marginal_count >= opt.marginals_needed ) uidnode->flag |= 4; else if (uid->help_full_count || uid->help_marginal_count) uidnode->flag |= 2; uidnode->flag |= 1; any_signed = 1; } return any_signed; } static int search_skipfnc (void *opaque, u32 *kid, int dummy_uid_no) { (void)dummy_uid_no; return test_key_hash_table ((KeyHashTable)opaque, kid); } /* * Scan all keys and return a key_array of all suitable keys from * kllist. The caller has to pass keydb handle so that we don't use * to create our own. Returns either a key_array or NULL in case of * an error. No results found are indicated by an empty array. * Caller hast to release the returned array. */ static struct key_array * validate_key_list (ctrl_t ctrl, KEYDB_HANDLE hd, KeyHashTable full_trust, struct key_item *klist, u32 curtime, u32 *next_expire) { KBNODE keyblock = NULL; struct key_array *keys = NULL; size_t nkeys, maxkeys; int rc; KEYDB_SEARCH_DESC desc; maxkeys = 1000; keys = xmalloc ((maxkeys+1) * sizeof *keys); nkeys = 0; rc = keydb_search_reset (hd); if (rc) { log_error ("keydb_search_reset failed: %s\n", gpg_strerror (rc)); xfree (keys); return NULL; } memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; desc.skipfnc = search_skipfnc; desc.skipfncvalue = full_trust; rc = keydb_search (hd, &desc, 1, NULL); if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND) { keys[nkeys].keyblock = NULL; return keys; } if (rc) { log_error ("keydb_search(first) failed: %s\n", gpg_strerror (rc)); goto die; } desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */ do { PKT_public_key *pk; rc = keydb_get_keyblock (hd, &keyblock); if (rc) { log_error ("keydb_get_keyblock failed: %s\n", gpg_strerror (rc)); goto die; } if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { log_debug ("ooops: invalid pkttype %d encountered\n", keyblock->pkt->pkttype); dump_kbnode (keyblock); release_kbnode(keyblock); continue; } /* prepare the keyblock for further processing */ merge_keys_and_selfsig (ctrl, keyblock); clear_kbnode_flags (keyblock); pk = keyblock->pkt->pkt.public_key; if (pk->has_expired || pk->flags.revoked) { /* it does not make sense to look further at those keys */ mark_keyblock_seen (full_trust, keyblock); } else if (validate_one_keyblock (ctrl, keyblock, klist, curtime, next_expire)) { KBNODE node; if (pk->expiredate && pk->expiredate >= curtime && pk->expiredate < *next_expire) *next_expire = pk->expiredate; if (nkeys == maxkeys) { maxkeys += 1000; keys = xrealloc (keys, (maxkeys+1) * sizeof *keys); } keys[nkeys++].keyblock = keyblock; /* Optimization - if all uids are fully trusted, then we never need to consider this key as a candidate again. */ for (node=keyblock; node; node = node->next) if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & 4)) break; if(node==NULL) mark_keyblock_seen (full_trust, keyblock); keyblock = NULL; } release_kbnode (keyblock); keyblock = NULL; } while (!(rc = keydb_search (hd, &desc, 1, NULL))); if (rc && gpg_err_code (rc) != GPG_ERR_NOT_FOUND) { log_error ("keydb_search_next failed: %s\n", gpg_strerror (rc)); goto die; } keys[nkeys].keyblock = NULL; return keys; die: keys[nkeys].keyblock = NULL; release_key_array (keys); return NULL; } /* Caller must sync */ static void reset_trust_records (ctrl_t ctrl) { TRUSTREC rec; ulong recnum; int count = 0, nreset = 0; for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ ) { if(rec.rectype==RECTYPE_TRUST) { count++; if(rec.r.trust.min_ownertrust) { rec.r.trust.min_ownertrust=0; write_record (ctrl, &rec); } } else if(rec.rectype==RECTYPE_VALID && ((rec.r.valid.validity&TRUST_MASK) || rec.r.valid.marginal_count || rec.r.valid.full_count)) { rec.r.valid.validity &= ~TRUST_MASK; rec.r.valid.marginal_count=rec.r.valid.full_count=0; nreset++; write_record (ctrl, &rec); } } if (opt.verbose) { log_info (ngettext("%d key processed", "%d keys processed", count), count); log_printf (ngettext(" (%d validity count cleared)\n", " (%d validity counts cleared)\n", nreset), nreset); } } /* * Run the key validation procedure. * * This works this way: * Step 1: Find all ultimately trusted keys (UTK). * mark them all as seen and put them into klist. * Step 2: loop max_cert_times * Step 3: if OWNERTRUST of any key in klist is undefined * ask user to assign ownertrust * Step 4: Loop over all keys in the keyDB which are not marked seen * Step 5: if key is revoked or expired * mark key as seen * continue loop at Step 4 * Step 6: For each user ID of that key signed by a key in klist * Calculate validity by counting trusted signatures. * Set validity of user ID * Step 7: If any signed user ID was found * mark key as seen * End Loop * Step 8: Build a new klist from all fully trusted keys from step 6 * End Loop * Ready * */ static int validate_keys (ctrl_t ctrl, int interactive) { int rc = 0; int quit=0; struct key_item *klist = NULL; struct key_item *k; struct key_array *keys = NULL; struct key_array *kar; KEYDB_HANDLE kdb = NULL; KBNODE node; int depth; int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate; KeyHashTable stored,used,full_trust; u32 start_time, next_expire; /* Make sure we have all sigs cached. TODO: This is going to require some architectural re-thinking, as it is agonizingly slow. Perhaps combine this with reset_trust_records(), or only check the caches on keys that are actually involved in the web of trust. */ keydb_rebuild_caches (ctrl, 0); - kdb = keydb_new (); + kdb = keydb_new (ctrl); if (!kdb) return gpg_error_from_syserror (); start_time = make_timestamp (); next_expire = 0xffffffff; /* set next expire to the year 2106 */ stored = new_key_hash_table (); used = new_key_hash_table (); full_trust = new_key_hash_table (); reset_trust_records (ctrl); /* Fixme: Instead of always building a UTK list, we could just build it * here when needed */ if (!utk_list) { if (!opt.quiet) log_info (_("no ultimately trusted keys found\n")); goto leave; } /* mark all UTKs as used and fully_trusted and set validity to ultimate */ for (k=utk_list; k; k = k->next) { KBNODE keyblock; PKT_public_key *pk; keyblock = get_pubkeyblock (ctrl, k->kid); if (!keyblock) { log_error (_("public key of ultimately" " trusted key %s not found\n"), keystr(k->kid)); continue; } mark_keyblock_seen (used, keyblock); mark_keyblock_seen (stored, keyblock); mark_keyblock_seen (full_trust, keyblock); pk = keyblock->pkt->pkt.public_key; for (node=keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID) update_validity (ctrl, pk, node->pkt->pkt.user_id, 0, TRUST_ULTIMATE); } if ( pk->expiredate && pk->expiredate >= start_time && pk->expiredate < next_expire) next_expire = pk->expiredate; release_kbnode (keyblock); do_sync (); } if (opt.trust_model == TM_TOFU) /* In the TOFU trust model, we only need to save the ultimately trusted keys. */ goto leave; klist = utk_list; if (!opt.quiet) log_info ("marginals needed: %d completes needed: %d trust model: %s\n", opt.marginals_needed, opt.completes_needed, trust_model_string (opt.trust_model)); for (depth=0; depth < opt.max_cert_depth; depth++) { int valids=0,key_count; /* See whether we should assign ownertrust values to the keys in klist. */ ot_unknown = ot_undefined = ot_never = 0; ot_marginal = ot_full = ot_ultimate = 0; for (k=klist; k; k = k->next) { int min=0; /* 120 and 60 are as per RFC2440 */ if(k->trust_value>=120) min=TRUST_FULLY; else if(k->trust_value>=60) min=TRUST_MARGINAL; if(min!=k->min_ownertrust) update_min_ownertrust (ctrl, k->kid,min); if (interactive && k->ownertrust == TRUST_UNKNOWN) { k->ownertrust = ask_ownertrust (ctrl, k->kid,min); if (k->ownertrust == (unsigned int)(-1)) { quit=1; goto leave; } } /* This can happen during transition from an old trustdb before trust sigs. It can also happen if a user uses two different versions of GnuPG or changes the --trust-model setting. */ if(k->ownertrustkid[0],(ulong)k->kid[1], trust_value_to_string(k->ownertrust), trust_value_to_string(min)); k->ownertrust=min; } if (k->ownertrust == TRUST_UNKNOWN) ot_unknown++; else if (k->ownertrust == TRUST_UNDEFINED) ot_undefined++; else if (k->ownertrust == TRUST_NEVER) ot_never++; else if (k->ownertrust == TRUST_MARGINAL) ot_marginal++; else if (k->ownertrust == TRUST_FULLY) ot_full++; else if (k->ownertrust == TRUST_ULTIMATE) ot_ultimate++; valids++; } /* Find all keys which are signed by a key in kdlist */ keys = validate_key_list (ctrl, kdb, full_trust, klist, start_time, &next_expire); if (!keys) { log_error ("validate_key_list failed\n"); rc = GPG_ERR_GENERAL; goto leave; } for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++) ; /* Store the calculated valididation status somewhere */ if (opt.verbose > 1 && DBG_TRUST) dump_key_array (depth, keys); for (kar=keys; kar->keyblock; kar++) store_validation_status (ctrl, depth, kar->keyblock, stored); if (!opt.quiet) log_info (_("depth: %d valid: %3d signed: %3d" " trust: %d-, %dq, %dn, %dm, %df, %du\n"), depth, valids, key_count, ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate ); /* Build a new kdlist from all fully valid keys in KEYS */ if (klist != utk_list) release_key_items (klist); klist = NULL; for (kar=keys; kar->keyblock; kar++) { for (node=kar->keyblock; node; node = node->next) { if (node->pkt->pkttype == PKT_USER_ID && (node->flag & 4)) { u32 kid[2]; /* have we used this key already? */ keyid_from_pk (kar->keyblock->pkt->pkt.public_key, kid); if(test_key_hash_table(used,kid)==0) { /* Normally we add both the primary and subkey ids to the hash via mark_keyblock_seen, but since we aren't using this hash as a skipfnc, that doesn't matter here. */ add_key_hash_table (used,kid); k = new_key_item (); k->kid[0]=kid[0]; k->kid[1]=kid[1]; k->ownertrust = (tdb_get_ownertrust (ctrl, kar->keyblock->pkt->pkt.public_key, 0) & TRUST_MASK); k->min_ownertrust = tdb_get_min_ownertrust (ctrl, kar->keyblock->pkt->pkt.public_key, 0); k->trust_depth= kar->keyblock->pkt->pkt.public_key->trust_depth; k->trust_value= kar->keyblock->pkt->pkt.public_key->trust_value; if(kar->keyblock->pkt->pkt.public_key->trust_regexp) k->trust_regexp= xstrdup(kar->keyblock->pkt-> pkt.public_key->trust_regexp); k->next = klist; klist = k; break; } } } } release_key_array (keys); keys = NULL; if (!klist) break; /* no need to dive in deeper */ } leave: keydb_release (kdb); release_key_array (keys); if (klist != utk_list) release_key_items (klist); release_key_hash_table (full_trust); release_key_hash_table (used); release_key_hash_table (stored); if (!rc && !quit) /* mark trustDB as checked */ { int rc2; if (next_expire == 0xffffffff || next_expire < start_time ) tdbio_write_nextcheck (ctrl, 0); else { tdbio_write_nextcheck (ctrl, next_expire); if (!opt.quiet) log_info (_("next trustdb check due at %s\n"), strtimestamp (next_expire)); } rc2 = tdbio_update_version_record (ctrl); if (rc2) { log_error (_("unable to update trustdb version record: " "write failed: %s\n"), gpg_strerror (rc2)); tdbio_invalid (); } do_sync (); pending_check_trustdb = 0; } return rc; }