From 1f980d23af8b818ed8246ec6bf13b9c61b963ec0 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 6 Aug 2019 14:59:37 +0200 Subject: kbx: Allow writing using a estream. * kbx/keybox-file.c (_keybox_write_header_blob): New optional arg stream. Change callers. Signed-off-by: Werner Koch --- g10/keydb.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'g10') diff --git a/g10/keydb.c b/g10/keydb.c index a7691bbe2..d3a2709bc 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -448,7 +448,7 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create) rc = gpg_error_from_syserror (); else { - rc = _keybox_write_header_blob (fp, 1); + rc = _keybox_write_header_blob (fp, NULL, 1); fclose (fp); } if (rc) -- cgit v1.2.3 From aba82684fe14289cf62b4694bc398f3a274b4762 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 9 Sep 2019 14:34:09 +0200 Subject: gpg: New option --use-keyboxd. * g10/gpg.c (oUseKeyboxd,oKeyboxdProgram): New consts. (opts): New options --use-keyboxd and --keyboxd-program. (main): Implement them. * g10/keydb.c: Move some defs out to ... * g10/keydb-private.h: new file. * g10/keydb.c: prefix function names with "internal" and move original functions to ... * g10/call-keyboxd.c: new file. Divert to the internal fucntion if --use-keyboxd is used. Add a CTRL arg to most fucntions and change all callers. * g10/Makefile.am (common_source): Add new files. (noinst_PROGRAMS): Do bot build gpgcompose. -- Note that this is just the framework with only a basic implementation of searching via keyboxd. Signed-off-by: Werner Koch --- g10/Makefile.am | 32 +- g10/call-keyboxd.c | 842 +++++++++++++++++++++++++++++++++++++++++++++ g10/delkey.c | 6 +- g10/export.c | 2 +- g10/getkey.c | 34 +- g10/gpg.c | 21 +- g10/gpg.h | 12 +- g10/gpgcompose.c | 4 +- g10/import.c | 15 +- g10/keydb-private.h | 176 ++++++++++ g10/keydb.c | 240 +++---------- g10/keydb.h | 71 ++-- g10/keyedit.c | 6 +- g10/keygen.c | 4 +- g10/keylist.c | 2 +- g10/keyserver.c | 2 +- g10/main.h | 2 +- g10/misc.c | 4 +- g10/options.h | 3 + g10/photoid.c | 2 +- g10/revoke.c | 4 +- g10/sign.c | 12 +- g10/t-keydb-get-keyblock.c | 6 +- g10/t-keydb.c | 7 +- g10/tofu.c | 2 +- g10/trustdb.c | 2 +- 26 files changed, 1221 insertions(+), 292 deletions(-) create mode 100644 g10/call-keyboxd.c create mode 100644 g10/keydb-private.h (limited to 'g10') diff --git a/g10/Makefile.am b/g10/Makefile.am index 884b4749b..1404014e9 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -50,9 +50,9 @@ 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 = \ @@ -99,7 +99,10 @@ common_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 \ @@ -160,7 +163,7 @@ 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 @@ -179,29 +182,32 @@ gpg_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ $(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) 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 @@ -65,7 +65,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, *r_sec_avail = 0; - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) return gpg_error_from_syserror (); @@ -131,7 +131,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, 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); @@ -141,7 +141,7 @@ do_delete_key (ctrl_t ctrl, const char *username, int secret, int force, 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); diff --git a/g10/export.c b/g10/export.c index e8bf14cf5..15861cc0d 100644 --- a/g10/export.c +++ b/g10/export.c @@ -1877,7 +1877,7 @@ do_export_stream (ctrl_t ctrl, iobuf_t out, strlist_t users, int secret, stats = &dummystats; *any = 0; init_packet (&pkt); - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) return gpg_error_from_syserror (); diff --git a/g10/getkey.c b/g10/getkey.c index 57617a0a9..2cc56cdbd 100644 --- a/g10/getkey.c +++ b/g10/getkey.c @@ -403,7 +403,7 @@ get_pubkey (ctrl_t ctrl, PKT_public_key * pk, u32 * keyid) } else { - ctx.kr_handle = keydb_new (); + ctx.kr_handle = keydb_new (ctrl); if (!ctx.kr_handle) { rc = gpg_error_from_syserror (); @@ -448,7 +448,7 @@ leave: * 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; @@ -476,7 +476,7 @@ get_pubkey_fast (PKT_public_key * pk, u32 * keyid) } #endif - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) return gpg_error_from_syserror (); rc = keydb_search_kid (hd, keyid); @@ -550,7 +550,7 @@ get_pubkeyblock (ctrl_t ctrl, u32 * keyid) 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; @@ -592,7 +592,7 @@ get_seckey (ctrl_t ctrl, PKT_public_key *pk, u32 *keyid) 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; @@ -793,7 +793,7 @@ key_byname (ctrl_t ctrl, GETKEY_CTX *retctx, strlist_t namelist, } 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 (); @@ -1428,7 +1428,7 @@ get_best_pubkey_byname (ctrl_t ctrl, enum get_pubkey_modes mode, 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 (); @@ -1574,7 +1574,7 @@ get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock, 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 (); @@ -1612,13 +1612,14 @@ get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock, * 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) @@ -1638,7 +1639,8 @@ get_pubkey_byfprint_fast (PKT_public_key * pk, * 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; @@ -1655,7 +1657,7 @@ get_keyblock_byfprint_fast (kbnode_t *r_keyblock, KEYDB_HANDLE *r_hd, 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 (); @@ -1737,7 +1739,7 @@ parse_def_secret_key (ctrl_t ctrl) if (! hd) { - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) return NULL; } @@ -2712,7 +2714,7 @@ merge_selfsigs_main (ctrl_t ctrl, kbnode_t keyblock, int *r_revoked, * 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 @@ -4078,7 +4080,7 @@ key_origin_string (int origin) 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; @@ -4087,7 +4089,7 @@ have_secret_key_with_kid (u32 *keyid) kbnode_t node; int result = 0; - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) return 0; memset (&desc, 0, sizeof desc); diff --git a/g10/gpg.c b/g10/gpg.c index 0bbe72394..145796cbc 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -361,6 +361,7 @@ enum cmd_and_opt_values oUseAgent, oNoUseAgent, oGpgAgentInfo, + oUseKeyboxd, oMergeOnly, oTryAllSecrets, oTrustedKey, @@ -378,6 +379,7 @@ enum cmd_and_opt_values oPersonalDigestPreferences, oPersonalCompressPreferences, oAgentProgram, + oKeyboxdProgram, oDirmngrProgram, oDisableDirmngr, oDisplay, @@ -849,6 +851,7 @@ static ARGPARSE_OPTS opts[] = { 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", "@"), @@ -895,6 +898,7 @@ static ARGPARSE_OPTS opts[] = { 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", "@"), @@ -2734,6 +2738,11 @@ main (int argc, char **argv) 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; @@ -3491,6 +3500,7 @@ main (int argc, char **argv) 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: @@ -4105,8 +4115,10 @@ main (int argc, char **argv) /* 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))) { @@ -4118,9 +4130,8 @@ main (int argc, char **argv) } 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; } @@ -5064,7 +5075,7 @@ main (int argc, char **argv) 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)); diff --git a/g10/gpg.h b/g10/gpg.h index 28a77b6df..adb919d7d 100644 --- a/g10/gpg.h +++ b/g10/gpg.h @@ -60,16 +60,19 @@ /* 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; @@ -96,6 +99,9 @@ struct server_control_s /* 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; diff --git a/g10/gpgcompose.c b/g10/gpgcompose.c index 7b7e1dc9a..43cecb90e 100644 --- a/g10/gpgcompose.c +++ b/g10/gpgcompose.c @@ -614,7 +614,7 @@ pk_search_terms (const char *option, int argc, char *argv[], void *cookie) 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) @@ -810,7 +810,7 @@ sig_issuer (const char *option, int argc, char *argv[], void *cookie) 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) diff --git a/g10/import.c b/g10/import.c index c32dbf059..cab36e0b2 100644 --- a/g10/import.c +++ b/g10/import.c @@ -2022,7 +2022,7 @@ import_one_real (ctrl_t ctrl, 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 @@ -2310,13 +2310,13 @@ import_one_real (ctrl_t ctrl, 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); } @@ -3372,7 +3372,7 @@ import_revoke_cert (ctrl_t ctrl, kbnode_t node, unsigned int options, } /* Read the original keyblock. */ - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) { rc = gpg_error_from_syserror (); @@ -3768,7 +3768,8 @@ delete_inv_parts (ctrl_t ctrl, kbnode_t keyblock, u32 *keyid, 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 @@ -4089,7 +4090,7 @@ revocation_present (ctrl_t ctrl, kbnode_t keyblock) * 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 @@ -4111,7 +4112,7 @@ revocation_present (ctrl_t ctrl, kbnode_t keyblock) 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); } 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 @@ -37,25 +37,10 @@ #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; @@ -67,74 +52,6 @@ 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 @@ -273,7 +190,7 @@ kid_not_found_flush (void) 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); @@ -875,26 +792,17 @@ keydb_dump_stats (void) } -/* 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; @@ -936,28 +844,21 @@ keydb_new (void) 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--; @@ -979,19 +880,17 @@ keydb_release (KEYDB_HANDLE hd) } 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) @@ -1007,7 +906,7 @@ keydb_lock (KEYDB_HANDLE hd) void keydb_disable_caching (KEYDB_HANDLE hd) { - if (hd) + if (hd && !hd->use_keyboxd) hd->no_caching = 1; } @@ -1029,6 +928,9 @@ keydb_get_resource_name (KEYDB_HANDLE hd) 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) @@ -1152,6 +1054,8 @@ unlock_all (KEYDB_HANDLE hd) * 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) { @@ -1183,6 +1087,8 @@ keydb_push_found_state (KEYDB_HANDLE hd) /* 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) { @@ -1347,6 +1253,7 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, /* 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 @@ -1356,17 +1263,11 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, * 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) { @@ -1385,8 +1286,7 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *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; } } @@ -1434,9 +1334,6 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb) if (!err) keydb_stats.get_keyblocks++; - if (DBG_CLOCK) - log_clock (err? "keydb_get_keyblock leave (failed)" - : "keydb_get_keyblock leave"); return err; } @@ -1485,6 +1382,7 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf) /* 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. * @@ -1499,20 +1397,16 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf) * 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); @@ -1575,6 +1469,7 @@ keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) /* 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 @@ -1585,13 +1480,12 @@ keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) * * 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); @@ -1650,12 +1544,11 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb) * * 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); @@ -1708,6 +1601,9 @@ keydb_locate_writable (KEYDB_HANDLE hd) 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; @@ -1759,6 +1655,9 @@ 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)) @@ -1781,38 +1680,33 @@ keydb_rebuild_caches (ctrl_t ctrl, int noisy) } -/* 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; @@ -1840,6 +1734,7 @@ keydb_search_reset (KEYDB_HANDLE hd) /* 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 @@ -1857,21 +1752,16 @@ keydb_search_reset (KEYDB_HANDLE hd) * 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) { @@ -1879,26 +1769,11 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, 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); } @@ -1926,7 +1801,7 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, { /* (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. @@ -2016,9 +1891,6 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, && !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 diff --git a/g10/keydb.h b/g10/keydb.h index 6ad8dce4c..25304ed25 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -163,6 +163,38 @@ is_in_klist (struct key_item *k, PKT_signature *sig) } +/*-- 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 --*/ @@ -181,17 +213,6 @@ 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. */ @@ -206,18 +227,6 @@ 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); @@ -228,13 +237,6 @@ void keydb_rebuild_caches (ctrl_t ctrl, int noisy); 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); @@ -323,7 +325,7 @@ 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. */ @@ -383,13 +385,14 @@ int get_pubkey_byfprint (ctrl_t ctrl, PKT_public_key *pk, kbnode_t *r_keyblock, /* 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); @@ -397,7 +400,7 @@ gpg_error_t get_keyblock_byfprint_fast (kbnode_t *r_keyblock, /* 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. */ diff --git a/g10/keyedit.c b/g10/keyedit.c index 1bf5de9b2..9238d5d53 100644 --- a/g10/keyedit.c +++ b/g10/keyedit.c @@ -2289,7 +2289,7 @@ quick_find_keyblock (ctrl_t ctrl, const char *username, *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. */ @@ -5761,7 +5761,7 @@ menu_revsig (ctrl_t ctrl, kbnode_t keyblock) } 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) { @@ -5800,7 +5800,7 @@ menu_revsig (ctrl_t ctrl, kbnode_t keyblock) } 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) { diff --git a/g10/keygen.c b/g10/keygen.c index d9037d29d..5aeda36ce 100644 --- a/g10/keygen.c +++ b/g10/keygen.c @@ -4332,7 +4332,7 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid, const char *algostr, desc.mode = KEYDB_SEARCH_MODE_EXACT; desc.u.name = uid; - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) goto leave; @@ -5184,7 +5184,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para, { KEYDB_HANDLE pub_hd; - pub_hd = keydb_new (); + pub_hd = keydb_new (ctrl); if (!pub_hd) err = gpg_error_from_syserror (); else diff --git a/g10/keylist.c b/g10/keylist.c index 1274775d2..5f5fe9d34 100644 --- a/g10/keylist.c +++ b/g10/keylist.c @@ -527,7 +527,7 @@ list_all (ctrl_t ctrl, int secret, int mark_secret) if (opt.check_sigs) listctx.check_sigs = 1; - hd = keydb_new (); + hd = keydb_new (ctrl); if (!hd) rc = gpg_error_from_syserror (); else diff --git a/g10/keyserver.c b/g10/keyserver.c index b07afb128..030bc9297 100644 --- a/g10/keyserver.c +++ b/g10/keyserver.c @@ -1196,7 +1196,7 @@ keyidlist (ctrl_t ctrl, strlist_t users, KEYDB_SEARCH_DESC **klist, *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num); - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) { rc = gpg_error_from_syserror (); diff --git a/g10/main.h b/g10/main.h index f45b03909..981315a4a 100644 --- a/g10/main.h +++ b/g10/main.h @@ -157,7 +157,7 @@ struct expando_args 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); diff --git a/g10/misc.c b/g10/misc.c index 0541d2b77..c2d5bbb8e 100644 --- a/g10/misc.c +++ b/g10/misc.c @@ -895,7 +895,7 @@ get_signature_count (PKT_public_key *pk) /* 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; @@ -1017,7 +1017,7 @@ pct_expando(const char *string,struct expando_args *args) 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)); diff --git a/g10/options.h b/g10/options.h index 234929b15..bfa2cc31e 100644 --- a/g10/options.h +++ b/g10/options.h @@ -126,6 +126,7 @@ struct int completes_needed; int max_cert_depth; const char *agent_program; + const char *keyboxd_program; const char *dirmngr_program; int disable_dirmngr; @@ -284,6 +285,8 @@ struct 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 diff --git a/g10/photoid.c b/g10/photoid.c index f9720d329..9bb4ab58f 100644 --- a/g10/photoid.c +++ b/g10/photoid.c @@ -338,7 +338,7 @@ show_photos (ctrl_t ctrl, const struct user_attribute *attrs, int count, #endif /* make command grow */ - command=pct_expando(opt.photo_viewer,&args); + command=pct_expando (ctrl, opt.photo_viewer,&args); if(!command) goto fail; diff --git a/g10/revoke.c b/g10/revoke.c index 0a93c31f9..0e39eca61 100644 --- a/g10/revoke.c +++ b/g10/revoke.c @@ -217,7 +217,7 @@ gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr) afx = new_armor_context (); - kdbhd = keydb_new (); + kdbhd = keydb_new (ctrl); if (!kdbhd) { rc = gpg_error_from_syserror (); @@ -641,7 +641,7 @@ gen_revoke (ctrl_t ctrl, const char *uname) } /* 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 (); diff --git a/g10/sign.c b/g10/sign.c index d71580639..8c70b0a2e 100644 --- a/g10/sign.c +++ b/g10/sign.c @@ -70,7 +70,7 @@ typedef struct pt_extra_hash_data_s *pt_extra_hash_data_t; * 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; @@ -97,7 +97,7 @@ mk_notation_policy_etc (PKT_signature *sig, 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")); @@ -122,7 +122,7 @@ mk_notation_policy_etc (PKT_signature *sig, { string = pu->d; - p = pct_expando (string, &args); + p = pct_expando (ctrl, string, &args); if (!p) { log_error(_("WARNING: unable to %%-expand policy URL " @@ -145,7 +145,7 @@ mk_notation_policy_etc (PKT_signature *sig, { 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" @@ -834,7 +834,7 @@ write_signature_packets (ctrl_t ctrl, 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); @@ -1660,7 +1660,7 @@ make_keysig_packet (ctrl_t ctrl, 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 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 @@ -21,9 +21,11 @@ #include "keydb.h" + static void do_test (int argc, char *argv[]) { + ctrl_t ctrl; char *fname; int rc; KEYDB_HANDLE hd1; @@ -33,6 +35,7 @@ do_test (int argc, char *argv[]) (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 @@ -44,7 +47,7 @@ do_test (int argc, char *argv[]) if (rc) ABORT ("Failed to open keyring."); - hd1 = keydb_new (); + hd1 = keydb_new (ctrl); if (!hd1) ABORT (""); @@ -62,4 +65,5 @@ do_test (int argc, char *argv[]) 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 @@ -25,6 +25,7 @@ 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; @@ -35,16 +36,17 @@ do_test (int argc, char *argv[]) (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 (""); @@ -101,4 +103,5 @@ do_test (int argc, char *argv[]) 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 @@ -2131,7 +2131,7 @@ build_conflict_set (ctrl_t ctrl, tofu_dbs_t dbs, /* 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) diff --git a/g10/trustdb.c b/g10/trustdb.c index a230a6c03..352aecede 100644 --- a/g10/trustdb.c +++ b/g10/trustdb.c @@ -2015,7 +2015,7 @@ validate_keys (ctrl_t ctrl, int interactive) trust. */ keydb_rebuild_caches (ctrl, 0); - kdb = keydb_new (); + kdb = keydb_new (ctrl); if (!kdb) return gpg_error_from_syserror (); -- cgit v1.2.3 From 2f0fdab8aabdf408495163ef99b2d4d111f74692 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 10 Sep 2019 15:45:58 +0200 Subject: common: Allow a readlimit for iobuf_esopen. * common/iobuf.c (file_es_filter_ctx_t): Add fields use_readlimit and readlimit. (file_es_filter): Implement them. (iobuf_esopen): Add new arg readlimit. * g10/decrypt-data.c (decrypt_data): Adjust for change. * g10/import.c (import_keys_es_stream): Ditto. -- This comes handy for (length,datablob) style streams. Signed-off-by: Werner Koch --- common/iobuf.c | 37 +++++++++++++++++++++++++++++++++++-- common/iobuf.h | 6 ++++-- g10/decrypt-data.c | 2 +- g10/import.c | 2 +- 4 files changed, 41 insertions(+), 6 deletions(-) (limited to 'g10') diff --git a/common/iobuf.c b/common/iobuf.c index 05944255f..db5d062cd 100644 --- a/common/iobuf.c +++ b/common/iobuf.c @@ -106,6 +106,8 @@ typedef struct int keep_open; int no_cache; int eof_seen; + int use_readlimit; /* Take care of the readlimit. */ + size_t readlimit; /* Number of bytes left to read. */ int print_only_name; /* Flags indicating that fname is not a real file. */ char fname[1]; /* Name of the file. */ } file_es_filter_ctx_t; @@ -635,6 +637,34 @@ file_es_filter (void *opaque, int control, iobuf_t chain, byte * buf, rc = -1; *ret_len = 0; } + else if (a->use_readlimit) + { + nbytes = 0; + if (!a->readlimit) + { /* eof */ + a->eof_seen = 1; + rc = -1; + } + else + { + if (size > a->readlimit) + size = a->readlimit; + rc = es_read (f, buf, size, &nbytes); + if (rc == -1) + { /* error */ + rc = gpg_error_from_syserror (); + log_error ("%s: read error: %s\n", a->fname,strerror (errno)); + } + else if (!nbytes) + { /* eof */ + a->eof_seen = 1; + rc = -1; + } + else + a->readlimit -= nbytes; + } + *ret_len = nbytes; + } else { nbytes = 0; @@ -1412,7 +1442,8 @@ iobuf_fdopen_nc (int fd, const char *mode) iobuf_t -iobuf_esopen (estream_t estream, const char *mode, int keep_open) +iobuf_esopen (estream_t estream, const char *mode, int keep_open, + size_t readlimit) { iobuf_t a; file_es_filter_ctx_t *fcx; @@ -1424,7 +1455,9 @@ iobuf_esopen (estream_t estream, const char *mode, int keep_open) fcx->fp = estream; fcx->print_only_name = 1; fcx->keep_open = keep_open; - sprintf (fcx->fname, "[fd %p]", estream); + fcx->readlimit = readlimit; + fcx->use_readlimit = !!readlimit; + snprintf (fcx->fname, 30, "[fd %p]", estream); a->filter = file_es_filter; a->filter_ov = fcx; file_es_filter (fcx, IOBUFCTRL_INIT, NULL, NULL, &len); diff --git a/common/iobuf.h b/common/iobuf.h index 16156383c..9c9650c61 100644 --- a/common/iobuf.h +++ b/common/iobuf.h @@ -328,8 +328,10 @@ iobuf_t iobuf_fdopen_nc (int fd, const char *mode); letter 'w', creates an output filter. Otherwise, creates an input filter. If KEEP_OPEN is TRUE, then the stream is not closed when the filter is destroyed. Otherwise, the stream is closed when the - filter is destroyed. */ -iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open); + filter is destroyed. If READLIMIT is not 0 this gives a limit on + the number of bytes to read from estream. */ +iobuf_t iobuf_esopen (estream_t estream, const char *mode, int keep_open, + size_t readlimit); /* Create a filter using an existing socket. On Windows creates a special socket filter. On non-Windows systems simply, this simply diff --git a/g10/decrypt-data.c b/g10/decrypt-data.c index c73d5fb45..5fd458845 100644 --- a/g10/decrypt-data.c +++ b/g10/decrypt-data.c @@ -475,7 +475,7 @@ decrypt_data (ctrl_t ctrl, void *procctx, PKT_encrypted *ed, DEK *dek) rc = get_output_file ("", 0, ed->buf, &filename, &fp); if (! rc) { - iobuf_t output = iobuf_esopen (fp, "w", 0); + iobuf_t output = iobuf_esopen (fp, "w", 0, 0); armor_filter_context_t *afx = NULL; if (opt.armor) diff --git a/g10/import.c b/g10/import.c index cab36e0b2..17afaa6f6 100644 --- a/g10/import.c +++ b/g10/import.c @@ -550,7 +550,7 @@ import_keys_es_stream (ctrl_t ctrl, estream_t fp, gpg_error_t err; iobuf_t inp; - inp = iobuf_esopen (fp, "rb", 1); + inp = iobuf_esopen (fp, "rb", 1, 0); if (!inp) { err = gpg_error_from_syserror (); -- cgit v1.2.3 From ce9906b008c94c2aa4ac770a981d1e1e0b8aea47 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Tue, 10 Sep 2019 16:05:54 +0200 Subject: gpg: First rough implementation of keyboxd access for key lookup. * g10/Makefile.am: Add nPth flags. * g10/gpg.c: Include npth.h. (gpg_deinit_default_ctrl): Deinit call-keyboxd local data. (main): Init nPth. * g10/keydb-private.h (struct keydb_handle_s): Add field 'kbl' and remove the search result and the assuan context. * g10/call-keyboxd.c (struct keyboxd_local_s): Add more fields. (lock_datastream, unlock_datastream): New. (gpg_keyboxd_deinit_session_data): Adjust for changed data structures. (prepare_data_pipe): New. (open_context): Return kbl instead of an Assuan context. Init mutexes etc. (close_context): Merge into ... (keydb_release): here. Adjust for changed data structures. (datastream_thread): New. (keydb_get_keyblock): Implement datastream stuff. (keydb_search): Ditto. * common/asshelp.c (wait_for_sock): Add arg connect_flags. (start_new_service): Set FDPASSING flag for the keyboxd. -- This code as a lot of rough edges, in particular it relies on a well behaving keyboxd. We need to add code to shutdown the datastream reader thread in case of errors and to properly get it up again. We also need to make really sure that both threads run in lockstep so that the datastream thread is only active while we are sending a command to the keyboxd. We should also see whether we can depend nPth initialization on the --use-keyboxd option to avoid any problems with nPth. And we need to test on Windows. Signed-off-by: Werner Koch --- common/asshelp.c | 22 +-- g10/Makefile.am | 14 +- g10/call-keyboxd.c | 420 ++++++++++++++++++++++++++++++++++++++++++---------- g10/gpg.c | 14 +- g10/keydb-private.h | 11 +- g10/keydb.h | 3 + 6 files changed, 380 insertions(+), 104 deletions(-) (limited to 'g10') diff --git a/common/asshelp.c b/common/asshelp.c index a5724fad5..174933a83 100644 --- a/common/asshelp.c +++ b/common/asshelp.c @@ -310,14 +310,15 @@ unlock_spawning (lock_spawn_t *lock, const char *name) } -/* Helper to start a service. - * SECS gives the number of seconds to wait. SOCKNAME is the name of - * the socket to connect. VERBOSE is the usual verbose flag. CTX is - * the assuan context. DID_SUCCESS_MSG will be set to 1 if a success - * messages has been printed. +/* Helper to start a service. SECS gives the number of seconds to + * wait. SOCKNAME is the name of the socket to connect. VERBOSE is + * the usual verbose flag. CTX is the assuan context. CONNECT_FLAGS + * are the assuan connect flags. DID_SUCCESS_MSG will be set to 1 if + * a success messages has been printed. */ static gpg_error_t wait_for_sock (int secs, int module_name_id, const char *sockname, + unsigned int connect_flags, int verbose, assuan_context_t ctx, int *did_success_msg) { gpg_error_t err = 0; @@ -353,7 +354,7 @@ wait_for_sock (int secs, int module_name_id, const char *sockname, } gnupg_usleep (next_sleep_us); elapsed_us += next_sleep_us; - err = assuan_socket_connect (ctx, sockname, 0, 0); + err = assuan_socket_connect (ctx, sockname, 0, connect_flags); if (!err) { if (verbose) @@ -403,6 +404,7 @@ start_new_service (assuan_context_t *r_ctx, const char *status_start_line; int no_service_err; int seconds_to_wait; + unsigned int connect_flags = 0; const char *argv[6]; *r_ctx = NULL; @@ -439,6 +441,7 @@ start_new_service (assuan_context_t *r_ctx, status_start_line = "starting_keyboxd ? 0 0"; no_service_err = GPG_ERR_NO_KEYBOXD; seconds_to_wait = SECS_TO_WAIT_FOR_KEYBOXD; + connect_flags |= ASSUAN_SOCKET_CONNECT_FDPASSING; break; default: err = gpg_error (GPG_ERR_INV_ARG); @@ -446,7 +449,7 @@ start_new_service (assuan_context_t *r_ctx, return err; } - err = assuan_socket_connect (ctx, sockname, 0, 0); + err = assuan_socket_connect (ctx, sockname, 0, connect_flags); if (err && autostart) { char *abs_homedir; @@ -522,7 +525,7 @@ start_new_service (assuan_context_t *r_ctx, argv[i++] = NULL; if (!(err = lock_spawning (&lock, gnupg_homedir (), lock_name, verbose)) - && assuan_socket_connect (ctx, sockname, 0, 0)) + && assuan_socket_connect (ctx, sockname, 0, connect_flags)) { err = gnupg_spawn_process_detached (program? program : program_name, argv, NULL); @@ -532,7 +535,8 @@ start_new_service (assuan_context_t *r_ctx, gpg_strerror (err)); else err = wait_for_sock (seconds_to_wait, module_name_id, - sockname, verbose, ctx, &did_success_msg); + sockname, connect_flags, + verbose, ctx, &did_success_msg); } unlock_spawning (&lock, lock_name); diff --git a/g10/Makefile.am b/g10/Makefile.am index 1404014e9..fd2cd21b9 100644 --- a/g10/Makefile.am +++ b/g10/Makefile.am @@ -29,9 +29,9 @@ AM_CPPFLAGS = include $(top_srcdir)/am/cmacros.am AM_CFLAGS = $(SQLITE3_CFLAGS) $(LIBGCRYPT_CFLAGS) \ - $(LIBASSUAN_CFLAGS) $(GPG_ERROR_CFLAGS) + $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS) $(GPG_ERROR_CFLAGS) -needed_libs = ../kbx/libkeybox.a $(libcommon) +needed_libs = ../kbx/libkeybox.a $(libcommonpth) # Because there are no program specific transform macros we need to # work around that to allow installing gpg as gpg2. @@ -178,11 +178,11 @@ gpgv_SOURCES = gpgv.c \ LDADD = $(needed_libs) ../common/libgpgrl.a \ $(ZLIBS) $(LIBINTL) $(CAPLIBS) $(NETLIBS) gpg_LDADD = $(LDADD) $(SQLITE3_LIBS) $(LIBGCRYPT_LIBS) $(LIBREADLINE) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(resource_objs) $(extra_sys_libs) gpg_LDFLAGS = $(extra_bin_ldflags) gpgv_LDADD = $(LDADD) $(LIBGCRYPT_LIBS) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(resource_objs) $(extra_sys_libs) gpgv_LDFLAGS = $(extra_bin_ldflags) @@ -197,17 +197,17 @@ 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) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(NPTH_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) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(NPTH_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) \ - $(LIBASSUAN_LIBS) $(GPG_ERROR_LIBS) \ + $(LIBASSUAN_LIBS) $(NPTH_LIBS) $(GPG_ERROR_LIBS) \ $(LIBICONV) $(t_common_ldadd) diff --git a/g10/call-keyboxd.c b/g10/call-keyboxd.c index f05a9e85f..97f84c03d 100644 --- a/g10/call-keyboxd.c +++ b/g10/call-keyboxd.c @@ -28,6 +28,7 @@ #ifdef HAVE_LOCALE_H # include #endif +#include #include "gpg.h" #include @@ -36,6 +37,8 @@ #include "options.h" #include "../common/i18n.h" #include "../common/asshelp.h" +#include "../common/host2net.h" +#include "../common/exechelp.h" #include "../common/status.h" #include "keydb.h" @@ -54,17 +57,63 @@ struct keyboxd_local_s /* The active Assuan context. */ assuan_context_t ctx; + /* This object is used if fd-passing is used to convey the + * keyblocks. */ + struct { + /* NULL or a stream used to receive data. */ + estream_t fp; + + /* Condition variable to sync the datastream with the command. */ + npth_mutex_t mutex; + npth_cond_t cond; + + /* The found keyblock or the parsing error. */ + kbnode_t found_keyblock; + gpg_error_t found_err; + } datastream; + + /* I/O buffer with the last search result or NULL. Used if + * D-lines are used to convey the keyblocks. */ + iobuf_t search_result; + /* 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; + + /* Flag indicating that a search reset is required. */ + unsigned int need_search_reset : 1; }; +/* Local prototypes. */ +static void *datastream_thread (void *arg); + + +static void +lock_datastream (keyboxd_local_t kbl) +{ + int rc = npth_mutex_lock (&kbl->datastream.mutex); + if (rc) + log_fatal ("%s: failed to acquire mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_errno (rc))); +} + + +static void +unlock_datastream (keyboxd_local_t kbl) +{ + int rc = npth_mutex_unlock (&kbl->datastream.mutex); + if (rc) + log_fatal ("%s: failed to release mutex: %s\n", __func__, + gpg_strerror (gpg_error_from_errno (rc))); +} + + /* Deinitialize all session resources pertaining to the keyboxd. */ void gpg_keyboxd_deinit_session_data (ctrl_t ctrl) @@ -77,7 +126,12 @@ gpg_keyboxd_deinit_session_data (ctrl_t ctrl) if (kbl->is_active) log_error ("oops: trying to cleanup an active keyboxd context\n"); else - assuan_release (kbl->ctx); + { + es_fclose (kbl->datastream.fp); + kbl->datastream.fp = NULL; + assuan_release (kbl->ctx); + kbl->ctx = NULL; + } xfree (kbl); } } @@ -165,18 +219,86 @@ create_new_context (ctrl_t ctrl, assuan_context_t *r_ctx) } + +/* Setup the pipe used for receiving data from the keyboxd. Store the + * info on KBL. */ +static gpg_error_t +prepare_data_pipe (keyboxd_local_t kbl) +{ + gpg_error_t err; + int rc; + int inpipe[2]; + estream_t infp; + npth_t thread; + npth_attr_t tattr; + + err = gnupg_create_inbound_pipe (inpipe, &infp, 0); + if (err) + { + log_error ("error creating inbound pipe: %s\n", gpg_strerror (err)); + return err; /* That should not happen. */ + } + + err = assuan_sendfd (kbl->ctx, INT2FD (inpipe[1])); + if (err) + { + log_error ("sending sending fd %d to keyboxd: %s <%s>\n", + inpipe[1], gpg_strerror (err), gpg_strsource (err)); + es_fclose (infp); + close (inpipe[1]); + return 0; /* Server may not support fd-passing. */ + } + + err = assuan_transact (kbl->ctx, "OUTPUT FD", + NULL, NULL, NULL, NULL, NULL, NULL); + if (err) + { + log_info ("keyboxd does not accept our fd: %s <%s>\n", + gpg_strerror (err), gpg_strsource (err)); + es_fclose (infp); + return 0; + } + + kbl->datastream.fp = infp; + kbl->datastream.found_keyblock = NULL; + kbl->datastream.found_err = 0; + + rc = npth_attr_init (&tattr); + if (rc) + { + err = gpg_error_from_errno (rc); + log_error ("error preparing thread for keyboxd: %s\n",gpg_strerror (err)); + es_fclose (infp); + kbl->datastream.fp = NULL; + return err; + } + npth_attr_setdetachstate (&tattr, NPTH_CREATE_DETACHED); + rc = npth_create (&thread, &tattr, datastream_thread, kbl); + if (rc) + { + err = gpg_error_from_errno (rc); + log_error ("error spawning thread for keyboxd: %s\n", gpg_strerror (err)); + npth_attr_destroy (&tattr); + es_fclose (infp); + kbl->datastream.fp = NULL; + return err; + } + + return 0; +} + + /* 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. */ + * new one is created and if necessary keyboxd is started. R_KBL + * receives a pointer to the local context object. */ static gpg_error_t -open_context (ctrl_t ctrl, assuan_context_t *r_ctx) +open_context (ctrl_t ctrl, keyboxd_local_t *r_kbl) { gpg_error_t err; + int rc; keyboxd_local_t kbl; - *r_ctx = NULL; + *r_kbl = NULL; for (;;) { for (kbl = ctrl->keyboxd_local; kbl && kbl->is_active; kbl = kbl->next) @@ -189,12 +311,16 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx) /* But first do the per session init if not yet done. */ if (!kbl->per_session_init_done) { + err = prepare_data_pipe (kbl); + if (err) + return err; kbl->per_session_init_done = 1; } kbl->is_active = 1; + kbl->need_search_reset = 1; - *r_ctx = kbl->ctx; + *r_kbl = kbl; return 0; } @@ -202,43 +328,40 @@ open_context (ctrl_t ctrl, assuan_context_t *r_ctx) kbl = xtrycalloc (1, sizeof *kbl); if (!kbl) return gpg_error_from_syserror (); + + rc = npth_mutex_init (&kbl->datastream.mutex, NULL); + if (rc) + { + err = gpg_error_from_errno (rc); + log_error ("error initializing mutex: %s\n", gpg_strerror (err)); + xfree (kbl); + return err; + } + rc = npth_cond_init (&kbl->datastream.cond, NULL); + if (rc) + { + err = gpg_error_from_errno (rc); + log_error ("error initializing condition: %s\n", gpg_strerror (err)); + npth_mutex_destroy (&kbl->datastream.mutex); + xfree (kbl); + return err; + } + err = create_new_context (ctrl, &kbl->ctx); if (err) { + npth_cond_destroy (&kbl->datastream.cond); + npth_mutex_destroy (&kbl->datastream.mutex); 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. */ + /* For thread-saftey 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); + /*NOTREACHED*/ } @@ -275,11 +398,8 @@ keydb_new (ctrl_t ctrl) } hd->use_keyboxd = 1; hd->ctrl = ctrl; - hd->need_search_reset = 1; - err = open_context (ctrl, &hd->assuan_context); - if (err) - goto leave; + err = open_context (ctrl, &hd->kbl); leave: if (err) @@ -300,14 +420,25 @@ keydb_new (ctrl_t ctrl) void keydb_release (KEYDB_HANDLE hd) { + keyboxd_local_t kbl; + if (!hd) return; + if (DBG_CLOCK) + log_clock ("keydb_release"); if (!hd->use_keyboxd) internal_keydb_deinit (hd); else { - close_context (hd->ctrl, hd->assuan_context); + kbl = hd->kbl; + if (DBG_CLOCK) + log_clock ("close_context (found)"); + if (!kbl->is_active) + log_fatal ("closing inactive keyboxd context %p\n", kbl); + kbl->is_active = 0; + hd->kbl = NULL; + hd->ctrl = NULL; } xfree (hd); } @@ -465,6 +596,86 @@ keydb_get_keyblock_do_parse (iobuf_t iobuf, int pk_no, int uid_no, } +/* The thread used to read from the data stream. This is running as + * long as the connection and its datastream exists. */ +static void * +datastream_thread (void *arg) +{ + keyboxd_local_t kbl = arg; + gpg_error_t err; + int rc; + unsigned char lenbuf[4]; + size_t nread, datalen; + iobuf_t iobuf; + int pk_no, uid_no; + kbnode_t keyblock, tmpkeyblock; + + + log_debug ("Datastream_thread started\n"); + while (kbl->datastream.fp) + { + /* log_debug ("Datastream_thread waiting ...\n"); */ + if (es_read (kbl->datastream.fp, lenbuf, 4, &nread)) + { + err = gpg_error_from_syserror (); + if (gpg_err_code (err) == GPG_ERR_EAGAIN) + continue; + log_error ("error reading data length from keyboxd: %s\n", + gpg_strerror (err)); + gnupg_sleep (1); + continue; + } + if (nread != 4) + { + err = gpg_error (GPG_ERR_EIO); + log_error ("error reading data length from keyboxd: %s\n", + "short read"); + continue; + } + + datalen = buf32_to_size_t (lenbuf); + /* log_debug ("keyboxd announced %zu bytes\n", datalen); */ + + iobuf = iobuf_esopen (kbl->datastream.fp, "rb", 1, datalen); + pk_no = uid_no = 0; /* FIXME: Get this from the keyboxd. */ + err = keydb_get_keyblock_do_parse (iobuf, pk_no, uid_no, &keyblock); + iobuf_close (iobuf); + if (!err) + { + /* log_debug ("parsing datastream succeeded\n"); */ + + /* Thread-safe assignment to the result var: */ + tmpkeyblock = kbl->datastream.found_keyblock; + kbl->datastream.found_keyblock = keyblock; + release_kbnode (tmpkeyblock); + } + else + { + /* log_debug ("parsing datastream failed: %s <%s>\n", */ + /* gpg_strerror (err), gpg_strsource (err)); */ + tmpkeyblock = kbl->datastream.found_keyblock; + kbl->datastream.found_keyblock = NULL; + kbl->datastream.found_err = err; + release_kbnode (tmpkeyblock); + } + + /* Tell the main thread. */ + lock_datastream (kbl); + rc = npth_cond_signal (&kbl->datastream.cond); + if (rc) + { + err = gpg_error_from_errno (rc); + log_error ("%s: signaling condition failed: %s\n", + __func__, gpg_strerror (err)); + } + unlock_datastream (kbl); + } + log_debug ("Datastream_thread finished\n"); + + return NULL; +} + + /* Return the keyblock last found by keydb_search() in *RET_KB. * * On success, the function returns 0 and the caller must free *RET_KB @@ -494,20 +705,28 @@ keydb_get_keyblock (KEYDB_HANDLE hd, kbnode_t *ret_kb) goto leave; } - if (!hd->search_result) + if (hd->kbl->search_result) + { + pk_no = uid_no = 0; /*FIXME: Get this from the keyboxd. */ + err = keydb_get_keyblock_do_parse (hd->kbl->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->kbl->search_result); + hd->kbl->search_result = NULL; + } + else if (hd->kbl->datastream.found_keyblock) + { + *ret_kb = hd->kbl->datastream.found_keyblock; + hd->kbl->datastream.found_keyblock = NULL; + err = 0; + } + else { 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)":""); @@ -636,7 +855,7 @@ keydb_search_reset (KEYDB_HANDLE hd) /* 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; + hd->kbl->need_search_reset = 1; err = 0; leave: @@ -697,14 +916,20 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, goto leave; } - - if (hd->search_result) + /* Clear the result objects. */ + if (hd->kbl->search_result) { - iobuf_close (hd->search_result); - hd->search_result = NULL; + iobuf_close (hd->kbl->search_result); + hd->kbl->search_result = NULL; + } + if (hd->kbl->datastream.found_keyblock) + { + release_kbnode (hd->kbl->datastream.found_keyblock); + hd->kbl->datastream.found_keyblock = NULL; } - if (!hd->need_search_reset) + /* Check whether this is a NEXT search. */ + if (!hd->kbl->need_search_reset) { /* No reset requested thus continue the search. The keyboxd * keeps the context of the search and thus the NEXT operates on @@ -717,7 +942,7 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, goto do_search; } - hd->need_search_reset = 0; + hd->kbl->need_search_reset = 0; if (!ndesc) { @@ -807,31 +1032,68 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, } 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; - } + if (hd->kbl->datastream.fp) + { + /* log_debug ("Sending command '%s'\n", line); */ + err = assuan_transact (hd->kbl->ctx, line, + NULL, NULL, + NULL, NULL, + NULL, NULL); + if (err) + { + /* log_debug ("Finished command with error: %s\n", gpg_strerror (err)); */ + /* Fixme: On unexpected errors we need a way to cancek the + * data stream. Probly it will be best to closeand reopen + * it. */ + } + else + { + int rc; - buffer = get_membuf (&data, &len); - if (!buffer) - { - err = gpg_error_from_syserror (); - goto leave; - } + /* log_debug ("Finished command .. telling data stream\n"); */ + lock_datastream (hd->kbl); + if (!hd->kbl->datastream.found_keyblock) + { + /* log_debug ("%s: waiting on datastream_cond ...\n", __func__); */ + rc = npth_cond_wait (&hd->kbl->datastream.cond, + &hd->kbl->datastream.mutex); + /* log_debug ("%s: waiting on datastream.cond done\n", __func__); */ + if (rc) + { + err = gpg_error_from_errno (rc); + log_error ("%s: waiting on condition failed: %s\n", + __func__, gpg_strerror (err)); + } + } + unlock_datastream (hd->kbl); + } + } + else /* Slower D-line version if fd-passing was not successful. */ + { + membuf_t data; + void *buffer; + size_t len; + + init_membuf (&data, 8192); + err = assuan_transact (hd->kbl->ctx, 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); + hd->kbl->search_result = iobuf_temp_with_content (buffer, len); + xfree (buffer); } diff --git a/g10/gpg.c b/g10/gpg.c index 145796cbc..821d3959b 100644 --- a/g10/gpg.c +++ b/g10/gpg.c @@ -36,6 +36,7 @@ # endif # include #endif +#include #define INCLUDED_BY_MAIN_MODULE 1 #include "gpg.h" @@ -979,6 +980,9 @@ static void add_keyserver_url( const char *string, int which ); static void emergency_cleanup (void); static void read_sessionkey_from_fd (int fd); +/* NPth wrapper function definitions. */ +ASSUAN_SYSTEM_NPTH_IMPL; + static char * make_libversion (const char *libname, const char *(*getfnc)(const char*)) @@ -2246,6 +2250,7 @@ gpg_deinit_default_ctrl (ctrl_t ctrl) gpg_dirmngr_deinit_session_data (ctrl); keydb_release (ctrl->cached_getkey_kdb); + gpg_keyboxd_deinit_session_data (ctrl); } @@ -3736,6 +3741,11 @@ main (int argc, char **argv) } #endif + /* Init threading which is used by some helper functions. */ + npth_init (); + assuan_set_system_hooks (ASSUAN_SYSTEM_NPTH); + gpgrt_set_syscall_clamp (npth_unprotect, npth_protect); + /* 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 @@ -3743,7 +3753,9 @@ main (int argc, char **argv) if (logfile && opt.batch) { log_set_file (logfile); - log_set_prefix (NULL, GPGRT_LOG_WITH_PREFIX | GPGRT_LOG_WITH_TIME | GPGRT_LOG_WITH_PID); + log_set_prefix (NULL, (GPGRT_LOG_WITH_PREFIX + | GPGRT_LOG_WITH_TIME + | GPGRT_LOG_WITH_PID )); } if (opt.verbose > 2) diff --git a/g10/keydb-private.h b/g10/keydb-private.h index c5315b991..efef82289 100644 --- a/g10/keydb-private.h +++ b/g10/keydb-private.h @@ -92,14 +92,9 @@ struct keydb_handle_s /* 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; + /* Connection info which also keep the local state. (This is points + * into the CTRL->keybox_local list.) */ + keyboxd_local_t kbl; /* END USE_KEYBOXD */ diff --git a/g10/keydb.h b/g10/keydb.h index 25304ed25..cc6f05956 100644 --- a/g10/keydb.h +++ b/g10/keydb.h @@ -165,6 +165,9 @@ is_in_klist (struct key_item *k, PKT_signature *sig) /*-- call-keyboxd.c --*/ +/* Release all open contexts to the keyboxd. */ +void gpg_keyboxd_deinit_session_data (ctrl_t ctrl); + /* Create a new database handle. Returns NULL on error, sets ERRNO, * and prints an error diagnostic. */ KEYDB_HANDLE keydb_new (ctrl_t ctrl); -- cgit v1.2.3 From d38f877bd88c348644742f79bfb4909dd3af493b Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Wed, 25 Sep 2019 16:21:30 +0200 Subject: doc: Minor doc updates and a typo fix. -- --- doc/DETAILS | 12 ++++++------ g10/objcache.c | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'g10') diff --git a/doc/DETAILS b/doc/DETAILS index 3fa651e12..ed5cadec6 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1141,12 +1141,12 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: Status codes are also used between the components of the GnuPG system via the Assuan S lines. Some of them are documented here: -*** PUBKEY_TYPE - The type of the public key in the following D-lines or communicated - via a pipe. is the value of =enum pubkey_types=. - - - +*** PUBKEY_INFO + The type of the public key in the following D-lines or + communicated via a pipe. is the value of =enum pubkey_types= + and the Unique Blob ID which is a SHA-1 digest the entire + blob here formatted in hex.. The consumer of this status line + should be prepared to see a of up to 64 characters. * Format of the --attribute-fd output diff --git a/g10/objcache.c b/g10/objcache.c index adb0717d7..a90b4d9d8 100644 --- a/g10/objcache.c +++ b/g10/objcache.c @@ -30,8 +30,8 @@ #include "options.h" #include "objcache.h" -/* Note that max value for uid_items is actually a the threshold when - * we start to look for ietms which can be removed. */ +/* Note that max value for uid_items is actually the threshold when + * we start to look for items which can be removed. */ #define NO_OF_UID_ITEM_BUCKETS 107 #define MAX_UID_ITEMS_PER_BUCKET 20 -- cgit v1.2.3 From 4be79b5abeae82b9840e6aa93874f743e13c6df7 Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 27 Sep 2019 10:05:07 +0200 Subject: kbx,gpg: Allow lookup using a UBID. * common/userids.c (classify_user_id): Detect UBIDs. * kbx/backend-cache.c (blob_table_put): Store the public key type. (be_cache_search): Add search mode for UBIDs. * kbx/backend.h (struct db_request_part_s): Add cache.seqno_ubid. * g10/keydb.c (keydb_search_desc_dump): Fix printing of keygrip. Add ubid printing. * g10/call-keyboxd.c (keydb_search): Support search by UBID. Signed-off-by: Werner Koch --- common/userids.c | 14 ++++++++++++++ doc/DETAILS | 9 +++++---- g10/call-keyboxd.c | 8 ++++++++ g10/keydb.c | 10 +++++++++- kbx/backend-cache.c | 27 +++++++++++++++++++++++++-- kbx/backend-kbx.c | 2 ++ kbx/backend.h | 1 + 7 files changed, 64 insertions(+), 7 deletions(-) (limited to 'g10') diff --git a/common/userids.c b/common/userids.c index 55bd85546..eb714a9af 100644 --- a/common/userids.c +++ b/common/userids.c @@ -65,6 +65,9 @@ * (note that you can't search for these characters). Compare * is not case sensitive. * - If the userid starts with a '&' a 40 hex digits keygrip is expected. + * - If the userid starts with a '^' followed by 40 hex digits it describes + * a Unique-Blob-ID (UBID) which is the hash of keyblob or certificate as + * stored in the database. This is used in the IPC of the keyboxd. */ gpg_error_t @@ -251,6 +254,17 @@ classify_user_id (const char *name, KEYDB_SEARCH_DESC *desc, int openpgp_hack) } break; + case '^': /* UBID */ + { + if (hex2bin (s+1, desc->u.ubid, 20) < 0) + { + rc = gpg_error (GPG_ERR_INV_USER_ID); /* Invalid. */ + goto out; + } + mode = KEYDB_SEARCH_MODE_UBID; + } + break; + default: if (s[0] == '0' && s[1] == 'x') { diff --git a/doc/DETAILS b/doc/DETAILS index ed5cadec6..0610108f4 100644 --- a/doc/DETAILS +++ b/doc/DETAILS @@ -1144,10 +1144,11 @@ pkd:0:1024:B665B1435F4C2 .... FF26ABB: *** PUBKEY_INFO The type of the public key in the following D-lines or communicated via a pipe. is the value of =enum pubkey_types= - and the Unique Blob ID which is a SHA-1 digest the entire - blob here formatted in hex.. The consumer of this status line - should be prepared to see a of up to 64 characters. - + and the Unique Blob ID (UBID) which is a SHA-1 digest the + entire blob here formatted in hex. The consumer of this status + line should be prepared to see a of up to 64 characters. + Note that the keyboxd SEARCH command can be used to lookup the + public key using the prefixed with a caret (^). * Format of the --attribute-fd output diff --git a/g10/call-keyboxd.c b/g10/call-keyboxd.c index 97f84c03d..88ad07817 100644 --- a/g10/call-keyboxd.c +++ b/g10/call-keyboxd.c @@ -1017,6 +1017,14 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, } break; + case KEYDB_SEARCH_MODE_UBID: + { + unsigned char hexubid[20 * 2 + 1]; + bin2hex (desc[0].u.grip, 20, hexubid); + snprintf (line, sizeof line, "SEARCH ^%s", hexubid); + } + break; + case KEYDB_SEARCH_MODE_FIRST: snprintf (line, sizeof line, "SEARCH"); break; diff --git a/g10/keydb.c b/g10/keydb.c index 6ca239475..39ec5442e 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -456,6 +456,10 @@ keydb_search_desc_dump (struct keydb_search_desc *desc) char b[MAX_FORMATTED_FINGERPRINT_LEN + 1]; char fpr[2 * MAX_FINGERPRINT_LEN + 1]; +#if MAX_FINGERPRINT_LEN < 20 +#error MAX_FINGERPRINT_LEN shorter than GRIP and UBID length/ +#endif + switch (desc->mode) { case KEYDB_SEARCH_MODE_EXACT: @@ -495,7 +499,11 @@ keydb_search_desc_dump (struct keydb_search_desc *desc) case KEYDB_SEARCH_MODE_SUBJECT: return xasprintf ("SUBJECT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_KEYGRIP: - return xasprintf ("KEYGRIP: %s", desc->u.grip); + bin2hex (desc[0].u.grip, 20, fpr); + return xasprintf ("KEYGRIP: %s", fpr); + case KEYDB_SEARCH_MODE_UBID: + bin2hex (desc[0].u.ubid, 20, fpr); + return xasprintf ("UBID: %s", fpr); case KEYDB_SEARCH_MODE_FIRST: return xasprintf ("FIRST"); case KEYDB_SEARCH_MODE_NEXT: diff --git a/kbx/backend-cache.c b/kbx/backend-cache.c index d5b46b50a..10a6f6bd9 100644 --- a/kbx/backend-cache.c +++ b/kbx/backend-cache.c @@ -207,7 +207,7 @@ compare_blobs (const void *arg_a, const void *arg_b) /* Put the blob (BLOBDATA, BLOBDATALEN) into the cache using UBID as * the index. If it is already in the cache nothing happens. */ static void -blob_table_put (const unsigned char *ubid, +blob_table_put (const unsigned char *ubid, enum pubkey_types pktype, const void *blobdata, unsigned int blobdatalen) { unsigned int hash; @@ -335,6 +335,7 @@ blob_table_put (const unsigned char *ubid, b = blob_attic; blob_attic = b->next; b->next = NULL; + b->pktype = pktype; b->data = blobdatacopy; b->datalen = blobdatalen; memcpy (b->ubid, ubid, 20); @@ -932,6 +933,7 @@ be_cache_search (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request, reqpart->cache_seqno.fpr = 0; reqpart->cache_seqno.kid = 0; reqpart->cache_seqno.grip = 0; + reqpart->cache_seqno.ubid = 0; err = 0; goto leave; } @@ -992,6 +994,27 @@ be_cache_search (ctrl_t ctrl, backend_handle_t backend_hd, db_request_t request, /* ki = query_by_grip (desc[n].u.fpr, desc[n].fprlen); */ /* break; */ + case KEYDB_SEARCH_MODE_UBID: + /* This is the quite special UBID mode: If this is + * encountered in the search list we will return just this + * one and obviously look only into the blob cache. */ + if (reqpart->cache_seqno.ubid) + err = gpg_error (GPG_ERR_NOT_FOUND); + else + { + b = blob_table_get (desc[n].u.ubid); + if (b) + { + err = be_return_pubkey (ctrl, b->data, b->datalen, + b->pktype, desc[n].u.ubid); + blob_unref (b); + reqpart->cache_seqno.ubid++; + } + else + err = gpg_error (GPG_ERR_EOF); + } + goto leave; + default: ki = NULL; break; @@ -1123,7 +1146,7 @@ be_cache_pubkey (ctrl_t ctrl, const unsigned char *ubid, return; } - blob_table_put (ubid, blob, bloblen); + blob_table_put (ubid, pubkey_type, blob, bloblen); kinfo = &info.primary; key_table_put (kinfo->fpr, kinfo->fprlen, ubid, 0); diff --git a/kbx/backend-kbx.c b/kbx/backend-kbx.c index e58b74a3b..851f2dadf 100644 --- a/kbx/backend-kbx.c +++ b/kbx/backend-kbx.c @@ -302,6 +302,8 @@ be_kbx_seek (ctrl_t ctrl, backend_handle_t backend_hd, unsigned long skipped_long_blobs; KEYDB_SEARCH_DESC desc; + (void)ctrl; + log_assert (backend_hd && backend_hd->db_type == DB_TYPE_KBX); log_assert (request); diff --git a/kbx/backend.h b/kbx/backend.h index 8b389d35c..675ec213d 100644 --- a/kbx/backend.h +++ b/kbx/backend.h @@ -61,6 +61,7 @@ struct db_request_part_s unsigned int fpr; unsigned int kid; unsigned int grip; + unsigned int ubid; } cache_seqno; }; typedef struct db_request_part_s *db_request_part_t; -- cgit v1.2.3