2003-06-05 07:14:21 +00:00
|
|
|
|
/* keydb.c - key database dispatcher
|
2015-05-08 15:51:11 +02:00
|
|
|
|
* Copyright (C) 2001-2013 Free Software Foundation, Inc.
|
2019-05-14 13:36:08 +02:00
|
|
|
|
* Copyright (C) 2001-2015 Werner Koch
|
2024-08-09 09:31:54 +02:00
|
|
|
|
* Copyright (C) 2019,2024 g10 Code GmbH
|
2003-06-05 07:14:21 +00:00
|
|
|
|
*
|
|
|
|
|
* 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
|
2007-07-04 19:49:40 +00:00
|
|
|
|
* the Free Software Foundation; either version 3 of the License, or
|
2003-06-05 07:14:21 +00:00
|
|
|
|
* (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
|
2016-11-05 12:02:19 +01:00
|
|
|
|
* along with this program; if not, see <https://www.gnu.org/licenses/>.
|
2024-08-09 09:31:54 +02:00
|
|
|
|
* SPDX-License-Identifier: GPL-3.0-or-later
|
2003-06-05 07:14:21 +00:00
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include <config.h>
|
|
|
|
|
#include <stdio.h>
|
|
|
|
|
#include <stdlib.h>
|
|
|
|
|
#include <string.h>
|
|
|
|
|
#include <errno.h>
|
|
|
|
|
#include <sys/types.h>
|
|
|
|
|
#include <sys/stat.h>
|
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
2003-06-18 19:56:13 +00:00
|
|
|
|
#include "gpg.h"
|
2017-03-07 20:21:23 +09:00
|
|
|
|
#include "../common/util.h"
|
2020-10-20 10:43:55 +02:00
|
|
|
|
#include "../common/sysutils.h"
|
2003-06-05 07:14:21 +00:00
|
|
|
|
#include "options.h"
|
|
|
|
|
#include "main.h" /*try_make_homedir ()*/
|
|
|
|
|
#include "packet.h"
|
|
|
|
|
#include "keyring.h"
|
2012-12-27 15:04:29 +01:00
|
|
|
|
#include "../kbx/keybox.h"
|
2011-02-04 12:57:53 +01:00
|
|
|
|
#include "keydb.h"
|
2017-03-07 20:21:23 +09:00
|
|
|
|
#include "../common/i18n.h"
|
2023-04-04 16:39:59 +02:00
|
|
|
|
#include "../common/comopt.h"
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
#include "keydb-private.h" /* For struct keydb_handle_s */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
static int active_handles;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
|
|
|
|
|
static int used_resources;
|
2016-01-13 09:29:39 +01:00
|
|
|
|
|
|
|
|
|
/* A pointer used to check for the primary key database by comparing
|
|
|
|
|
to the struct resource_item's TOKEN. */
|
|
|
|
|
static void *primary_keydb;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2016-11-10 17:01:19 +01:00
|
|
|
|
/* Whether we have successfully registered any resource. */
|
|
|
|
|
static int any_registered;
|
2015-08-31 13:57:07 +02:00
|
|
|
|
|
2015-08-26 12:22:24 +02:00
|
|
|
|
/* Looking up keys is expensive. To hide the cost, we cache whether
|
|
|
|
|
keys exist in the key database. Then, if we know a key does not
|
|
|
|
|
exist, we don't have to spend time looking it up. This
|
|
|
|
|
particularly helps the --list-sigs and --check-sigs commands.
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-08-26 12:22:24 +02:00
|
|
|
|
The cache stores the results in a hash using separate chaining.
|
|
|
|
|
Concretely: we use the LSB of the keyid to index the hash table and
|
|
|
|
|
each bucket consists of a linked list of entries. An entry
|
|
|
|
|
consists of the 64-bit key id. If a key id is not in the cache,
|
|
|
|
|
then we don't know whether it is in the DB or not.
|
|
|
|
|
|
|
|
|
|
To simplify the cache consistency protocol, we simply flush the
|
|
|
|
|
whole cache whenever a key is inserted or updated. */
|
|
|
|
|
|
|
|
|
|
#define KID_NOT_FOUND_CACHE_BUCKETS 256
|
|
|
|
|
static struct kid_not_found_cache_bucket *
|
|
|
|
|
kid_not_found_cache[KID_NOT_FOUND_CACHE_BUCKETS];
|
|
|
|
|
|
|
|
|
|
struct kid_not_found_cache_bucket
|
2015-06-20 15:03:32 +02:00
|
|
|
|
{
|
2015-08-26 12:22:24 +02:00
|
|
|
|
struct kid_not_found_cache_bucket *next;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
u32 kid[2];
|
2015-08-26 12:22:24 +02:00
|
|
|
|
};
|
2015-06-20 15:03:32 +02:00
|
|
|
|
|
2017-03-31 20:03:43 +02:00
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
unsigned int count; /* The current number of entries in the hash table. */
|
|
|
|
|
unsigned int peak; /* The peak of COUNT. */
|
|
|
|
|
unsigned int flushes; /* The number of flushes. */
|
|
|
|
|
} kid_not_found_stats;
|
|
|
|
|
|
|
|
|
|
struct
|
|
|
|
|
{
|
|
|
|
|
unsigned int handles; /* Number of handles created. */
|
|
|
|
|
unsigned int locks; /* Number of locks taken. */
|
|
|
|
|
unsigned int parse_keyblocks; /* Number of parse_keyblock_image calls. */
|
|
|
|
|
unsigned int get_keyblocks; /* Number of keydb_get_keyblock calls. */
|
|
|
|
|
unsigned int build_keyblocks; /* Number of build_keyblock_image calls. */
|
|
|
|
|
unsigned int update_keyblocks;/* Number of update_keyblock calls. */
|
|
|
|
|
unsigned int insert_keyblocks;/* Number of update_keyblock calls. */
|
|
|
|
|
unsigned int delete_keyblocks;/* Number of delete_keyblock calls. */
|
|
|
|
|
unsigned int search_resets; /* Number of keydb_search_reset calls. */
|
|
|
|
|
unsigned int found; /* Number of successful keydb_search calls. */
|
|
|
|
|
unsigned int found_cached; /* Ditto but from the cache. */
|
|
|
|
|
unsigned int notfound; /* Number of failed keydb_search calls. */
|
|
|
|
|
unsigned int notfound_cached; /* Ditto but from the cache. */
|
|
|
|
|
} keydb_stats;
|
|
|
|
|
|
2015-06-20 15:03:32 +02:00
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
static int lock_all (KEYDB_HANDLE hd);
|
|
|
|
|
static void unlock_all (KEYDB_HANDLE hd);
|
|
|
|
|
|
|
|
|
|
|
2015-11-16 12:41:46 +01:00
|
|
|
|
/* Check whether the keyid KID is in key id is definitely not in the
|
2015-08-26 12:22:24 +02:00
|
|
|
|
database.
|
2015-06-22 15:15:39 +02:00
|
|
|
|
|
|
|
|
|
Returns:
|
2015-08-26 12:22:24 +02:00
|
|
|
|
|
|
|
|
|
0 - Indeterminate: the key id is not in the cache; we don't know
|
|
|
|
|
whether the key is in the database or not. If you want a
|
|
|
|
|
definitive answer, you'll need to perform a lookup.
|
|
|
|
|
|
|
|
|
|
1 - There is definitely no key with this key id in the database.
|
|
|
|
|
We searched for a key with this key id previously, but we
|
|
|
|
|
didn't find it in the database. */
|
2015-06-20 15:03:32 +02:00
|
|
|
|
static int
|
|
|
|
|
kid_not_found_p (u32 *kid)
|
|
|
|
|
{
|
2015-08-26 12:22:24 +02:00
|
|
|
|
struct kid_not_found_cache_bucket *k;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
|
2015-08-26 12:22:24 +02:00
|
|
|
|
for (k = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; k; k = k->next)
|
2015-06-20 15:03:32 +02:00
|
|
|
|
if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
|
2015-06-22 15:15:39 +02:00
|
|
|
|
{
|
|
|
|
|
if (DBG_CACHE)
|
2015-08-26 12:22:24 +02:00
|
|
|
|
log_debug ("keydb: kid_not_found_p (%08lx%08lx) => not in DB\n",
|
|
|
|
|
(ulong)kid[0], (ulong)kid[1]);
|
|
|
|
|
return 1;
|
2015-06-22 15:15:39 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (DBG_CACHE)
|
2015-08-26 12:22:24 +02:00
|
|
|
|
log_debug ("keydb: kid_not_found_p (%08lx%08lx) => indeterminate\n",
|
2015-06-22 15:15:39 +02:00
|
|
|
|
(ulong)kid[0], (ulong)kid[1]);
|
2015-06-20 15:03:32 +02:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-08-26 12:22:24 +02:00
|
|
|
|
/* Insert the keyid KID into the kid_not_found_cache. FOUND is whether
|
|
|
|
|
the key is in the key database or not.
|
|
|
|
|
|
|
|
|
|
Note this function does not check whether the key id is already in
|
|
|
|
|
the cache. As such, kid_not_found_p() should be called first. */
|
2015-06-20 15:03:32 +02:00
|
|
|
|
static void
|
2015-08-26 12:22:24 +02:00
|
|
|
|
kid_not_found_insert (u32 *kid)
|
2015-06-20 15:03:32 +02:00
|
|
|
|
{
|
2015-08-26 12:22:24 +02:00
|
|
|
|
struct kid_not_found_cache_bucket *k;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
|
2015-06-22 15:15:39 +02:00
|
|
|
|
if (DBG_CACHE)
|
2015-08-26 12:22:24 +02:00
|
|
|
|
log_debug ("keydb: kid_not_found_insert (%08lx%08lx)\n",
|
|
|
|
|
(ulong)kid[0], (ulong)kid[1]);
|
2015-06-20 15:03:32 +02:00
|
|
|
|
k = xmalloc (sizeof *k);
|
|
|
|
|
k->kid[0] = kid[0];
|
|
|
|
|
k->kid[1] = kid[1];
|
2015-08-26 12:22:24 +02:00
|
|
|
|
k->next = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS];
|
|
|
|
|
kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS] = k;
|
2017-03-31 20:03:43 +02:00
|
|
|
|
kid_not_found_stats.count++;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-08-31 11:14:21 +02:00
|
|
|
|
/* Flush the kid not found cache. */
|
2015-06-20 15:03:32 +02:00
|
|
|
|
static void
|
|
|
|
|
kid_not_found_flush (void)
|
|
|
|
|
{
|
2015-08-26 12:22:24 +02:00
|
|
|
|
struct kid_not_found_cache_bucket *k, *knext;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
int i;
|
|
|
|
|
|
2015-06-22 15:15:39 +02:00
|
|
|
|
if (DBG_CACHE)
|
|
|
|
|
log_debug ("keydb: kid_not_found_flush\n");
|
|
|
|
|
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (!kid_not_found_stats.count)
|
2015-06-20 15:03:32 +02:00
|
|
|
|
return;
|
|
|
|
|
|
2015-08-26 12:22:24 +02:00
|
|
|
|
for (i=0; i < DIM(kid_not_found_cache); i++)
|
2015-06-20 15:03:32 +02:00
|
|
|
|
{
|
2015-08-26 12:22:24 +02:00
|
|
|
|
for (k = kid_not_found_cache[i]; k; k = knext)
|
2015-06-20 15:03:32 +02:00
|
|
|
|
{
|
|
|
|
|
knext = k->next;
|
|
|
|
|
xfree (k);
|
|
|
|
|
}
|
2015-08-26 12:22:24 +02:00
|
|
|
|
kid_not_found_cache[i] = NULL;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
}
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (kid_not_found_stats.count > kid_not_found_stats.peak)
|
|
|
|
|
kid_not_found_stats.peak = kid_not_found_stats.count;
|
|
|
|
|
kid_not_found_stats.count = 0;
|
|
|
|
|
kid_not_found_stats.flushes++;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2013-01-08 14:44:49 +01:00
|
|
|
|
static void
|
2019-09-09 14:34:09 +02:00
|
|
|
|
keyblock_cache_clear (struct keydb_handle_s *hd)
|
2013-01-08 14:44:49 +01:00
|
|
|
|
{
|
2015-08-31 13:57:07 +02:00
|
|
|
|
hd->keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
|
|
|
|
|
iobuf_close (hd->keyblock_cache.iobuf);
|
|
|
|
|
hd->keyblock_cache.iobuf = NULL;
|
2015-12-15 12:21:30 +01:00
|
|
|
|
hd->keyblock_cache.resource = -1;
|
|
|
|
|
hd->keyblock_cache.offset = -1;
|
2013-01-08 14:44:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2012-12-27 15:04:29 +01:00
|
|
|
|
/* Handle the creation of a keyring or a keybox if it does not yet
|
2014-09-18 16:00:34 +02:00
|
|
|
|
exist. Take into account that other processes might have the
|
2012-12-27 15:04:29 +01:00
|
|
|
|
keyring/keybox already locked. This lock check does not work if
|
2015-08-26 12:22:24 +02:00
|
|
|
|
the directory itself is not yet available. If IS_BOX is true the
|
|
|
|
|
filename is expected to refer to a keybox. If FORCE_CREATE is true
|
2015-08-31 11:14:21 +02:00
|
|
|
|
the keyring or keybox will be created.
|
|
|
|
|
|
|
|
|
|
Return 0 if it is okay to access the specified file. */
|
2016-01-13 09:29:39 +01:00
|
|
|
|
static gpg_error_t
|
2014-09-18 16:00:34 +02:00
|
|
|
|
maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
|
2006-04-19 11:26:11 +00:00
|
|
|
|
{
|
2020-10-20 10:43:55 +02:00
|
|
|
|
gpg_err_code_t ec;
|
2009-09-23 10:28:41 +00:00
|
|
|
|
dotlock_t lockhd = NULL;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
IOBUF iobuf;
|
|
|
|
|
int rc;
|
|
|
|
|
mode_t oldmask;
|
|
|
|
|
char *last_slash_in_filename;
|
2016-01-14 18:29:26 +01:00
|
|
|
|
char *bak_fname = NULL;
|
|
|
|
|
char *tmp_fname = NULL;
|
2008-01-29 16:04:57 +00:00
|
|
|
|
int save_slash;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
|
|
|
|
/* A quick test whether the filename already exists. */
|
2020-10-20 10:43:55 +02:00
|
|
|
|
if (!gnupg_access (filename, F_OK))
|
|
|
|
|
return !gnupg_access (filename, R_OK)? 0 : gpg_error (GPG_ERR_EACCES);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
|
|
|
|
/* If we don't want to create a new file at all, there is no need to
|
|
|
|
|
go any further - bail out right here. */
|
2014-09-18 16:00:34 +02:00
|
|
|
|
if (!force_create)
|
2006-04-19 11:26:11 +00:00
|
|
|
|
return gpg_error (GPG_ERR_ENOENT);
|
|
|
|
|
|
|
|
|
|
/* First of all we try to create the home directory. Note, that we
|
|
|
|
|
don't do any locking here because any sane application of gpg
|
|
|
|
|
would create the home directory by itself and not rely on gpg's
|
2014-09-18 16:00:34 +02:00
|
|
|
|
tricky auto-creation which is anyway only done for certain home
|
|
|
|
|
directory name pattern. */
|
2006-04-19 11:26:11 +00:00
|
|
|
|
last_slash_in_filename = strrchr (filename, DIRSEP_C);
|
2008-01-29 16:04:57 +00:00
|
|
|
|
#if HAVE_W32_SYSTEM
|
|
|
|
|
{
|
|
|
|
|
/* Windows may either have a slash or a backslash. Take care of it. */
|
|
|
|
|
char *p = strrchr (filename, '/');
|
|
|
|
|
if (!last_slash_in_filename || p > last_slash_in_filename)
|
|
|
|
|
last_slash_in_filename = p;
|
|
|
|
|
}
|
|
|
|
|
#endif /*HAVE_W32_SYSTEM*/
|
|
|
|
|
if (!last_slash_in_filename)
|
|
|
|
|
return gpg_error (GPG_ERR_ENOENT); /* No slash at all - should
|
|
|
|
|
not happen though. */
|
|
|
|
|
save_slash = *last_slash_in_filename;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
*last_slash_in_filename = 0;
|
2020-10-26 12:39:26 +01:00
|
|
|
|
if (gnupg_access(filename, F_OK))
|
2011-02-04 12:57:53 +01:00
|
|
|
|
{
|
2006-04-19 11:26:11 +00:00
|
|
|
|
static int tried;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
if (!tried)
|
|
|
|
|
{
|
|
|
|
|
tried = 1;
|
|
|
|
|
try_make_homedir (filename);
|
|
|
|
|
}
|
2020-10-20 10:43:55 +02:00
|
|
|
|
if ((ec = gnupg_access (filename, F_OK)))
|
2006-04-19 11:26:11 +00:00
|
|
|
|
{
|
2020-10-20 10:43:55 +02:00
|
|
|
|
rc = gpg_error (ec);
|
2008-01-30 14:26:57 +00:00
|
|
|
|
*last_slash_in_filename = save_slash;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2023-04-04 16:39:59 +02:00
|
|
|
|
|
|
|
|
|
*last_slash_in_filename = save_slash;
|
|
|
|
|
|
|
|
|
|
if (!opt.use_keyboxd
|
|
|
|
|
&& !parse_comopt (GNUPG_MODULE_NAME_GPG, 0)
|
|
|
|
|
&& comopt.use_keyboxd)
|
|
|
|
|
{
|
|
|
|
|
/* The above try_make_homedir created a new default hoemdir
|
|
|
|
|
* and also wrote a new common.conf. Thus we now see that
|
|
|
|
|
* use-keyboxd has been set. Let's set this option and
|
|
|
|
|
* return a dedicated error code. */
|
|
|
|
|
opt.use_keyboxd = comopt.use_keyboxd;
|
|
|
|
|
rc = gpg_error (GPG_ERR_TRUE);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2006-04-19 11:26:11 +00:00
|
|
|
|
}
|
2023-04-04 16:39:59 +02:00
|
|
|
|
else
|
|
|
|
|
*last_slash_in_filename = save_slash;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
|
|
|
|
/* To avoid races with other instances of gpg trying to create or
|
|
|
|
|
update the keyring (it is removed during an update for a short
|
|
|
|
|
time), we do the next stuff in a locked state. */
|
2011-09-28 15:41:58 +02:00
|
|
|
|
lockhd = dotlock_create (filename, 0);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
if (!lockhd)
|
|
|
|
|
{
|
2012-12-27 15:04:29 +01:00
|
|
|
|
rc = gpg_error_from_syserror ();
|
2006-04-19 11:26:11 +00:00
|
|
|
|
/* A reason for this to fail is that the directory is not
|
|
|
|
|
writable. However, this whole locking stuff does not make
|
|
|
|
|
sense if this is the case. An empty non-writable directory
|
|
|
|
|
with no keyring is not really useful at all. */
|
|
|
|
|
if (opt.verbose)
|
2012-12-27 15:04:29 +01:00
|
|
|
|
log_info ("can't allocate lock for '%s': %s\n",
|
|
|
|
|
filename, gpg_strerror (rc));
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
2014-09-18 16:00:34 +02:00
|
|
|
|
if (!force_create)
|
|
|
|
|
return gpg_error (GPG_ERR_ENOENT); /* Won't happen. */
|
2006-04-19 11:26:11 +00:00
|
|
|
|
else
|
2012-12-27 15:04:29 +01:00
|
|
|
|
return rc;
|
2006-04-19 11:26:11 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-09-23 14:43:58 +02:00
|
|
|
|
if ( dotlock_take (lockhd, -1) )
|
2006-04-19 11:26:11 +00:00
|
|
|
|
{
|
2012-12-27 15:04:29 +01:00
|
|
|
|
rc = gpg_error_from_syserror ();
|
2006-04-19 11:26:11 +00:00
|
|
|
|
/* This is something bad. Probably a stale lockfile. */
|
2012-12-27 15:04:29 +01:00
|
|
|
|
log_info ("can't lock '%s': %s\n", filename, gpg_strerror (rc));
|
2006-04-19 11:26:11 +00:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Now the real test while we are locked. */
|
2016-01-14 18:29:26 +01:00
|
|
|
|
|
|
|
|
|
/* Gpg either uses pubring.gpg or pubring.kbx and thus different
|
|
|
|
|
* lock files. Now, when one gpg process is updating a pubring.gpg
|
|
|
|
|
* and thus holding the corresponding lock, a second gpg process may
|
|
|
|
|
* get to here at the time between the two rename operation used by
|
|
|
|
|
* the first process to update pubring.gpg. The lock taken above
|
|
|
|
|
* may not protect the second process if it tries to create a
|
|
|
|
|
* pubring.kbx file which would be protected by a different lock
|
|
|
|
|
* file.
|
|
|
|
|
*
|
|
|
|
|
* We can detect this case by checking that the two temporary files
|
|
|
|
|
* used by the update code exist at the same time. In that case we
|
|
|
|
|
* do not create a new file but act as if FORCE_CREATE has not been
|
|
|
|
|
* given. Obviously there is a race between our two checks but the
|
|
|
|
|
* worst thing is that we won't create a new file, which is better
|
|
|
|
|
* than to accidentally creating one. */
|
|
|
|
|
rc = keybox_tmp_names (filename, is_box, &bak_fname, &tmp_fname);
|
|
|
|
|
if (rc)
|
|
|
|
|
goto leave;
|
|
|
|
|
|
2020-10-20 10:43:55 +02:00
|
|
|
|
if (!gnupg_access (filename, F_OK))
|
2006-04-19 11:26:11 +00:00
|
|
|
|
{
|
|
|
|
|
rc = 0; /* Okay, we may access the file now. */
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
2020-10-20 10:43:55 +02:00
|
|
|
|
if (!gnupg_access (bak_fname, F_OK) && !gnupg_access (tmp_fname, F_OK))
|
2016-01-14 18:29:26 +01:00
|
|
|
|
{
|
|
|
|
|
/* Very likely another process is updating a pubring.gpg and we
|
|
|
|
|
should not create a pubring.kbx. */
|
|
|
|
|
rc = gpg_error (GPG_ERR_ENOENT);
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
|
|
|
|
|
/* The file does not yet exist, create it now. */
|
|
|
|
|
oldmask = umask (077);
|
|
|
|
|
if (is_secured_filename (filename))
|
|
|
|
|
{
|
|
|
|
|
iobuf = NULL;
|
2010-04-01 13:24:55 +00:00
|
|
|
|
gpg_err_set_errno (EPERM);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
2014-06-25 20:25:28 +02:00
|
|
|
|
iobuf = iobuf_create (filename, 0);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
umask (oldmask);
|
2011-02-04 12:57:53 +01:00
|
|
|
|
if (!iobuf)
|
2006-04-19 11:26:11 +00:00
|
|
|
|
{
|
2006-09-14 16:50:33 +00:00
|
|
|
|
rc = gpg_error_from_syserror ();
|
2012-12-27 15:04:29 +01:00
|
|
|
|
if (is_box)
|
|
|
|
|
log_error (_("error creating keybox '%s': %s\n"),
|
|
|
|
|
filename, gpg_strerror (rc));
|
|
|
|
|
else
|
|
|
|
|
log_error (_("error creating keyring '%s': %s\n"),
|
|
|
|
|
filename, gpg_strerror (rc));
|
2006-04-19 11:26:11 +00:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
iobuf_close (iobuf);
|
|
|
|
|
/* Must invalidate that ugly cache */
|
2010-03-08 17:05:37 +00:00
|
|
|
|
iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, filename);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
|
|
|
|
|
/* Make sure that at least one record is in a new keybox file, so
|
|
|
|
|
that the detection magic will work the next time it is used. */
|
|
|
|
|
if (is_box)
|
|
|
|
|
{
|
2020-10-20 11:52:16 +02:00
|
|
|
|
estream_t fp = es_fopen (filename, "wb");
|
2012-12-27 15:04:29 +01:00
|
|
|
|
if (!fp)
|
|
|
|
|
rc = gpg_error_from_syserror ();
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-10-20 11:52:16 +02:00
|
|
|
|
rc = _keybox_write_header_blob (fp, 1);
|
|
|
|
|
es_fclose (fp);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
}
|
|
|
|
|
if (rc)
|
|
|
|
|
{
|
|
|
|
|
if (is_box)
|
|
|
|
|
log_error (_("error creating keybox '%s': %s\n"),
|
|
|
|
|
filename, gpg_strerror (rc));
|
|
|
|
|
else
|
|
|
|
|
log_error (_("error creating keyring '%s': %s\n"),
|
|
|
|
|
filename, gpg_strerror (rc));
|
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!opt.quiet)
|
|
|
|
|
{
|
|
|
|
|
if (is_box)
|
|
|
|
|
log_info (_("keybox '%s' created\n"), filename);
|
|
|
|
|
else
|
|
|
|
|
log_info (_("keyring '%s' created\n"), filename);
|
|
|
|
|
}
|
|
|
|
|
|
2006-04-19 11:26:11 +00:00
|
|
|
|
rc = 0;
|
|
|
|
|
|
|
|
|
|
leave:
|
|
|
|
|
if (lockhd)
|
|
|
|
|
{
|
2011-09-23 14:43:58 +02:00
|
|
|
|
dotlock_release (lockhd);
|
|
|
|
|
dotlock_destroy (lockhd);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
}
|
2016-01-14 18:29:26 +01:00
|
|
|
|
xfree (bak_fname);
|
|
|
|
|
xfree (tmp_fname);
|
2006-04-19 11:26:11 +00:00
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-08-31 11:14:21 +02:00
|
|
|
|
/* Helper for keydb_add_resource. Opens FILENAME to figure out the
|
|
|
|
|
resource type.
|
|
|
|
|
|
|
|
|
|
Returns the specified file's likely type. If the file does not
|
|
|
|
|
exist, returns KEYDB_RESOURCE_TYPE_NONE and sets *R_FOUND to 0.
|
|
|
|
|
Otherwise, tries to figure out the file's type. This is either
|
|
|
|
|
KEYDB_RESOURCE_TYPE_KEYBOX, KEYDB_RESOURCE_TYPE_KEYRING or
|
|
|
|
|
KEYDB_RESOURCE_TYPE_KEYNONE. If the file is a keybox and it has
|
|
|
|
|
the OpenPGP flag set, then R_OPENPGP is also set. */
|
2014-10-09 19:10:32 +02:00
|
|
|
|
static KeydbResourceType
|
|
|
|
|
rt_from_file (const char *filename, int *r_found, int *r_openpgp)
|
|
|
|
|
{
|
|
|
|
|
u32 magic;
|
|
|
|
|
unsigned char verbuf[4];
|
2020-11-11 15:22:51 +01:00
|
|
|
|
estream_t fp;
|
2014-10-09 19:10:32 +02:00
|
|
|
|
KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
|
|
|
|
|
|
|
|
|
|
*r_found = *r_openpgp = 0;
|
2020-11-11 15:22:51 +01:00
|
|
|
|
fp = es_fopen (filename, "rb");
|
2014-10-09 19:10:32 +02:00
|
|
|
|
if (fp)
|
|
|
|
|
{
|
|
|
|
|
*r_found = 1;
|
|
|
|
|
|
2020-11-11 15:22:51 +01:00
|
|
|
|
if (es_fread (&magic, 4, 1, fp) == 1 )
|
2014-10-09 19:10:32 +02:00
|
|
|
|
{
|
|
|
|
|
if (magic == 0x13579ace || magic == 0xce9a5713)
|
|
|
|
|
; /* GDBM magic - not anymore supported. */
|
2020-11-11 15:22:51 +01:00
|
|
|
|
else if (es_fread (&verbuf, 4, 1, fp) == 1
|
2014-10-09 19:10:32 +02:00
|
|
|
|
&& verbuf[0] == 1
|
2020-11-11 15:22:51 +01:00
|
|
|
|
&& es_fread (&magic, 4, 1, fp) == 1
|
2014-10-09 19:10:32 +02:00
|
|
|
|
&& !memcmp (&magic, "KBXf", 4))
|
|
|
|
|
{
|
|
|
|
|
if ((verbuf[3] & 0x02))
|
|
|
|
|
*r_openpgp = 1;
|
|
|
|
|
rt = KEYDB_RESOURCE_TYPE_KEYBOX;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
rt = KEYDB_RESOURCE_TYPE_KEYRING;
|
|
|
|
|
}
|
|
|
|
|
else /* Maybe empty: assume keyring. */
|
|
|
|
|
rt = KEYDB_RESOURCE_TYPE_KEYRING;
|
|
|
|
|
|
2020-11-11 15:22:51 +01:00
|
|
|
|
es_fclose (fp);
|
2014-10-09 19:10:32 +02:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return rt;
|
|
|
|
|
}
|
2015-11-17 11:36:38 +01:00
|
|
|
|
|
|
|
|
|
char *
|
|
|
|
|
keydb_search_desc_dump (struct keydb_search_desc *desc)
|
|
|
|
|
{
|
|
|
|
|
char b[MAX_FORMATTED_FINGERPRINT_LEN + 1];
|
2015-12-11 14:40:23 +01:00
|
|
|
|
char fpr[2 * MAX_FINGERPRINT_LEN + 1];
|
2014-10-09 19:10:32 +02:00
|
|
|
|
|
2019-11-28 09:39:35 +01:00
|
|
|
|
#if MAX_FINGERPRINT_LEN < UBID_LEN || MAX_FINGERPRINT_LEN < KEYGRIP_LEN
|
|
|
|
|
#error MAX_FINGERPRINT_LEN is shorter than KEYGRIP or UBID length.
|
2019-09-27 10:05:07 +02:00
|
|
|
|
#endif
|
|
|
|
|
|
2015-11-17 11:36:38 +01:00
|
|
|
|
switch (desc->mode)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_SEARCH_MODE_EXACT:
|
|
|
|
|
return xasprintf ("EXACT: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_SUBSTR:
|
|
|
|
|
return xasprintf ("SUBSTR: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_MAIL:
|
|
|
|
|
return xasprintf ("MAIL: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_MAILSUB:
|
|
|
|
|
return xasprintf ("MAILSUB: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_MAILEND:
|
|
|
|
|
return xasprintf ("MAILEND: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_WORDS:
|
|
|
|
|
return xasprintf ("WORDS: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_SHORT_KID:
|
|
|
|
|
return xasprintf ("SHORT_KID: '%s'",
|
|
|
|
|
format_keyid (desc->u.kid, KF_SHORT, b, sizeof (b)));
|
|
|
|
|
case KEYDB_SEARCH_MODE_LONG_KID:
|
|
|
|
|
return xasprintf ("LONG_KID: '%s'",
|
|
|
|
|
format_keyid (desc->u.kid, KF_LONG, b, sizeof (b)));
|
2019-03-14 08:54:59 +01:00
|
|
|
|
case KEYDB_SEARCH_MODE_FPR:
|
|
|
|
|
bin2hex (desc->u.fpr, desc->fprlen, fpr);
|
2019-03-14 14:55:06 +01:00
|
|
|
|
return xasprintf ("FPR%02d: '%s'", desc->fprlen,
|
2015-11-17 11:36:38 +01:00
|
|
|
|
format_hexfingerprint (fpr, b, sizeof (b)));
|
|
|
|
|
case KEYDB_SEARCH_MODE_ISSUER:
|
|
|
|
|
return xasprintf ("ISSUER: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_ISSUER_SN:
|
2020-09-08 10:05:08 +02:00
|
|
|
|
return xasprintf ("ISSUER_SN: '#%.*s/%s'",
|
|
|
|
|
(int)desc->snlen,desc->sn, desc->u.name);
|
2015-11-17 11:36:38 +01:00
|
|
|
|
case KEYDB_SEARCH_MODE_SN:
|
2020-09-08 10:05:08 +02:00
|
|
|
|
return xasprintf ("SN: '%.*s'",
|
|
|
|
|
(int)desc->snlen, desc->sn);
|
2015-11-17 11:36:38 +01:00
|
|
|
|
case KEYDB_SEARCH_MODE_SUBJECT:
|
|
|
|
|
return xasprintf ("SUBJECT: '%s'", desc->u.name);
|
|
|
|
|
case KEYDB_SEARCH_MODE_KEYGRIP:
|
2019-11-28 09:39:35 +01:00
|
|
|
|
bin2hex (desc[0].u.grip, KEYGRIP_LEN, fpr);
|
2019-09-27 10:05:07 +02:00
|
|
|
|
return xasprintf ("KEYGRIP: %s", fpr);
|
|
|
|
|
case KEYDB_SEARCH_MODE_UBID:
|
2019-11-28 09:39:35 +01:00
|
|
|
|
bin2hex (desc[0].u.ubid, UBID_LEN, fpr);
|
2019-09-27 10:05:07 +02:00
|
|
|
|
return xasprintf ("UBID: %s", fpr);
|
2015-11-17 11:36:38 +01:00
|
|
|
|
case KEYDB_SEARCH_MODE_FIRST:
|
|
|
|
|
return xasprintf ("FIRST");
|
|
|
|
|
case KEYDB_SEARCH_MODE_NEXT:
|
|
|
|
|
return xasprintf ("NEXT");
|
|
|
|
|
default:
|
|
|
|
|
return xasprintf ("Bad search mode (%d)", desc->mode);
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-11 11:41:49 +01:00
|
|
|
|
|
|
|
|
|
|
2015-11-17 11:36:38 +01:00
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Register a resource (keyring or keybox). The first keyring or
|
|
|
|
|
* keybox that is added using this function is created if it does not
|
|
|
|
|
* already exist and the KEYDB_RESOURCE_FLAG_READONLY is not set.
|
|
|
|
|
*
|
|
|
|
|
* FLAGS are a combination of the KEYDB_RESOURCE_FLAG_* constants.
|
|
|
|
|
*
|
|
|
|
|
* URL must have the following form:
|
|
|
|
|
*
|
|
|
|
|
* gnupg-ring:filename = plain keyring
|
|
|
|
|
* gnupg-kbx:filename = keybox file
|
|
|
|
|
* filename = check file's type (create as a plain keyring)
|
|
|
|
|
*
|
|
|
|
|
* Note: on systems with drive letters (Windows) invalid URLs (i.e.,
|
|
|
|
|
* those with an unrecognized part before the ':' such as "c:\...")
|
|
|
|
|
* will silently be treated as bare filenames. On other systems, such
|
|
|
|
|
* URLs will cause this function to return GPG_ERR_GENERAL.
|
|
|
|
|
*
|
|
|
|
|
* If KEYDB_RESOURCE_FLAG_DEFAULT is set, the resource is a keyring
|
|
|
|
|
* and the file ends in ".gpg", then this function also checks if a
|
|
|
|
|
* file with the same name, but the extension ".kbx" exists, is a
|
|
|
|
|
* keybox and the OpenPGP flag is set. If so, this function opens
|
|
|
|
|
* that resource instead.
|
|
|
|
|
*
|
|
|
|
|
* If the file is not found, KEYDB_RESOURCE_FLAG_GPGVDEF is set and
|
|
|
|
|
* the URL ends in ".kbx", then this function will try opening the
|
|
|
|
|
* same URL, but with the extension ".gpg". If that file is a keybox
|
|
|
|
|
* with the OpenPGP flag set or it is a keyring, then we use that
|
|
|
|
|
* instead.
|
|
|
|
|
*
|
|
|
|
|
* If the file is not found, KEYDB_RESOURCE_FLAG_DEFAULT is set, the
|
|
|
|
|
* file should be created and the file's extension is ".gpg" then we
|
|
|
|
|
* replace the extension with ".kbx".
|
|
|
|
|
*
|
|
|
|
|
* If the KEYDB_RESOURCE_FLAG_PRIMARY is set and the resource is a
|
|
|
|
|
* keyring (not a keybox), then this resource is considered the
|
|
|
|
|
* primary resource. This is used by keydb_locate_writable(). If
|
|
|
|
|
* another primary keyring is set, then that keyring is considered the
|
|
|
|
|
* primary.
|
|
|
|
|
*
|
|
|
|
|
* If KEYDB_RESOURCE_FLAG_READONLY is set and the resource is a
|
|
|
|
|
* keyring (not a keybox), then the keyring is marked as read only and
|
|
|
|
|
* operations just as keyring_insert_keyblock will return
|
2023-04-04 16:39:59 +02:00
|
|
|
|
* GPG_ERR_ACCESS.
|
|
|
|
|
*/
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2012-12-27 15:04:29 +01:00
|
|
|
|
keydb_add_resource (const char *url, unsigned int flags)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2015-08-31 11:14:21 +02:00
|
|
|
|
/* The file named by the URL (i.e., without the prototype). */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
const char *resname = url;
|
2015-08-31 11:14:21 +02:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
char *filename = NULL;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
int create;
|
|
|
|
|
int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY);
|
2014-09-18 16:00:34 +02:00
|
|
|
|
int is_default = !!(flags&KEYDB_RESOURCE_FLAG_DEFAULT);
|
2015-08-07 15:53:56 +02:00
|
|
|
|
int is_gpgvdef = !!(flags&KEYDB_RESOURCE_FLAG_GPGVDEF);
|
2016-01-13 09:29:39 +01:00
|
|
|
|
gpg_error_t err = 0;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
|
|
|
|
|
void *token;
|
|
|
|
|
|
2012-12-27 15:04:29 +01:00
|
|
|
|
/* Create the resource if it is the first registered one. */
|
|
|
|
|
create = (!read_only && !any_registered);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2012-12-27 15:04:29 +01:00
|
|
|
|
if (strlen (resname) > 11 && !strncmp( resname, "gnupg-ring:", 11) )
|
2011-04-29 15:07:11 +02:00
|
|
|
|
{
|
2012-12-27 15:04:29 +01:00
|
|
|
|
rt = KEYDB_RESOURCE_TYPE_KEYRING;
|
|
|
|
|
resname += 11;
|
|
|
|
|
}
|
|
|
|
|
else if (strlen (resname) > 10 && !strncmp (resname, "gnupg-kbx:", 10) )
|
|
|
|
|
{
|
|
|
|
|
rt = KEYDB_RESOURCE_TYPE_KEYBOX;
|
|
|
|
|
resname += 10;
|
|
|
|
|
}
|
2003-06-05 07:14:21 +00:00
|
|
|
|
#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
|
2012-12-27 15:04:29 +01:00
|
|
|
|
else if (strchr (resname, ':'))
|
|
|
|
|
{
|
|
|
|
|
log_error ("invalid key resource URL '%s'\n", url );
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_GENERAL);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
goto leave;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2012-12-27 15:04:29 +01:00
|
|
|
|
#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-03-24 13:30:57 +01:00
|
|
|
|
if (*resname != DIRSEP_C
|
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
&& *resname != '/' /* Fixme: does not handle drive letters. */
|
|
|
|
|
#endif
|
|
|
|
|
)
|
2011-04-29 15:07:11 +02:00
|
|
|
|
{
|
|
|
|
|
/* Do tilde expansion etc. */
|
2015-03-24 13:30:57 +01:00
|
|
|
|
if (strchr (resname, DIRSEP_C)
|
|
|
|
|
#ifdef HAVE_W32_SYSTEM
|
|
|
|
|
|| strchr (resname, '/') /* Windows also accepts this. */
|
|
|
|
|
#endif
|
|
|
|
|
)
|
2011-04-29 15:07:11 +02:00
|
|
|
|
filename = make_filename (resname, NULL);
|
|
|
|
|
else
|
2016-06-07 10:59:46 +02:00
|
|
|
|
filename = make_filename (gnupg_homedir (), resname, NULL);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2011-04-29 15:07:11 +02:00
|
|
|
|
else
|
|
|
|
|
filename = xstrdup (resname);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
/* See whether we can determine the filetype. */
|
|
|
|
|
if (rt == KEYDB_RESOURCE_TYPE_NONE)
|
|
|
|
|
{
|
2014-10-09 19:10:32 +02:00
|
|
|
|
int found, openpgp_flag;
|
2014-09-18 16:00:34 +02:00
|
|
|
|
int pass = 0;
|
|
|
|
|
size_t filenamelen;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2014-09-18 16:00:34 +02:00
|
|
|
|
check_again:
|
|
|
|
|
filenamelen = strlen (filename);
|
2014-10-09 19:10:32 +02:00
|
|
|
|
rt = rt_from_file (filename, &found, &openpgp_flag);
|
|
|
|
|
if (found)
|
2011-04-29 15:07:11 +02:00
|
|
|
|
{
|
2014-10-09 19:10:32 +02:00
|
|
|
|
/* The file exists and we have the resource type in RT.
|
|
|
|
|
|
|
|
|
|
Now let us check whether in addition to the "pubring.gpg"
|
|
|
|
|
a "pubring.kbx with openpgp keys exists. This is so that
|
|
|
|
|
GPG 2.1 will use an existing "pubring.kbx" by default iff
|
|
|
|
|
that file has been created or used by 2.1. This check is
|
|
|
|
|
needed because after creation or use of the kbx file with
|
|
|
|
|
2.1 an older version of gpg may have created a new
|
|
|
|
|
pubring.gpg for its own use. */
|
|
|
|
|
if (!pass && is_default && rt == KEYDB_RESOURCE_TYPE_KEYRING
|
|
|
|
|
&& filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg"))
|
2011-04-29 15:07:11 +02:00
|
|
|
|
{
|
2014-10-09 19:10:32 +02:00
|
|
|
|
strcpy (filename+filenamelen-4, ".kbx");
|
|
|
|
|
if ((rt_from_file (filename, &found, &openpgp_flag)
|
|
|
|
|
== KEYDB_RESOURCE_TYPE_KEYBOX) && found && openpgp_flag)
|
2012-12-27 15:04:29 +01:00
|
|
|
|
rt = KEYDB_RESOURCE_TYPE_KEYBOX;
|
2014-10-09 19:10:32 +02:00
|
|
|
|
else /* Restore filename */
|
|
|
|
|
strcpy (filename+filenamelen-4, ".gpg");
|
|
|
|
|
}
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2015-08-07 15:53:56 +02:00
|
|
|
|
else if (!pass && is_gpgvdef
|
|
|
|
|
&& filenamelen > 4 && !strcmp (filename+filenamelen-4, ".kbx"))
|
|
|
|
|
{
|
|
|
|
|
/* Not found but gpgv's default "trustedkeys.kbx" file has
|
|
|
|
|
been requested. We did not found it so now check whether
|
|
|
|
|
a "trustedkeys.gpg" file exists and use that instead. */
|
|
|
|
|
KeydbResourceType rttmp;
|
|
|
|
|
|
|
|
|
|
strcpy (filename+filenamelen-4, ".gpg");
|
|
|
|
|
rttmp = rt_from_file (filename, &found, &openpgp_flag);
|
|
|
|
|
if (found
|
|
|
|
|
&& ((rttmp == KEYDB_RESOURCE_TYPE_KEYBOX && openpgp_flag)
|
|
|
|
|
|| (rttmp == KEYDB_RESOURCE_TYPE_KEYRING)))
|
|
|
|
|
rt = rttmp;
|
|
|
|
|
else /* Restore filename */
|
|
|
|
|
strcpy (filename+filenamelen-4, ".kbx");
|
|
|
|
|
}
|
2014-09-18 16:00:34 +02:00
|
|
|
|
else if (!pass
|
|
|
|
|
&& is_default && create
|
|
|
|
|
&& filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg"))
|
|
|
|
|
{
|
|
|
|
|
/* The file does not exist, the default resource has been
|
|
|
|
|
requested, the file shall be created, and the file has a
|
|
|
|
|
".gpg" suffix. Change the suffix to ".kbx" and try once
|
|
|
|
|
more. This way we achieve that we open an existing
|
|
|
|
|
".gpg" keyring, but create a new keybox file with an
|
|
|
|
|
".kbx" suffix. */
|
|
|
|
|
strcpy (filename+filenamelen-4, ".kbx");
|
|
|
|
|
pass++;
|
|
|
|
|
goto check_again;
|
|
|
|
|
}
|
2012-12-27 15:04:29 +01:00
|
|
|
|
else /* No file yet: create keybox. */
|
|
|
|
|
rt = KEYDB_RESOURCE_TYPE_KEYBOX;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
switch (rt)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("unknown type of key resource '%s'\n", url );
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_GENERAL);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
goto leave;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = maybe_create_keyring_or_box (filename, 0, create);
|
|
|
|
|
if (err)
|
2011-04-29 15:07:11 +02:00
|
|
|
|
goto leave;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (keyring_register_filename (filename, read_only, &token))
|
|
|
|
|
{
|
|
|
|
|
if (used_resources >= MAX_KEYDB_RESOURCES)
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
2012-12-27 15:04:29 +01:00
|
|
|
|
if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
|
2016-01-13 09:29:39 +01:00
|
|
|
|
primary_keydb = token;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
all_resources[used_resources].type = rt;
|
|
|
|
|
all_resources[used_resources].u.kr = NULL; /* Not used here */
|
|
|
|
|
all_resources[used_resources].token = token;
|
|
|
|
|
used_resources++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/* This keyring was already registered, so ignore it.
|
|
|
|
|
However, we can still mark it as primary even if it was
|
|
|
|
|
already registered. */
|
2012-12-27 15:04:29 +01:00
|
|
|
|
if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
|
2016-01-13 09:29:39 +01:00
|
|
|
|
primary_keydb = token;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
}
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
{
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = maybe_create_keyring_or_box (filename, 1, create);
|
|
|
|
|
if (err)
|
2012-12-27 15:04:29 +01:00
|
|
|
|
goto leave;
|
|
|
|
|
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = keybox_register_file (filename, 0, &token);
|
|
|
|
|
if (!err)
|
2012-12-27 15:04:29 +01:00
|
|
|
|
{
|
|
|
|
|
if (used_resources >= MAX_KEYDB_RESOURCES)
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
else
|
|
|
|
|
{
|
2016-01-13 09:29:39 +01:00
|
|
|
|
if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
|
|
|
|
|
primary_keydb = token;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
all_resources[used_resources].type = rt;
|
|
|
|
|
all_resources[used_resources].u.kb = NULL; /* Not used here */
|
|
|
|
|
all_resources[used_resources].token = token;
|
|
|
|
|
|
2023-12-29 10:57:26 +09:00
|
|
|
|
if (!(flags & KEYDB_RESOURCE_FLAG_READONLY))
|
2019-08-23 15:51:13 +02:00
|
|
|
|
{
|
2023-12-29 10:57:26 +09:00
|
|
|
|
KEYBOX_HANDLE kbxhd;
|
|
|
|
|
|
|
|
|
|
/* Do a compress run if needed and no other user is
|
|
|
|
|
* currently using the keybox. */
|
|
|
|
|
kbxhd = keybox_new_openpgp (token, 0);
|
|
|
|
|
if (kbxhd)
|
2019-08-23 15:51:13 +02:00
|
|
|
|
{
|
2023-12-29 10:57:26 +09:00
|
|
|
|
if (!keybox_lock (kbxhd, 1, 0))
|
|
|
|
|
{
|
|
|
|
|
keybox_compress (kbxhd);
|
|
|
|
|
keybox_lock (kbxhd, 0, 0);
|
|
|
|
|
}
|
2019-08-23 15:51:13 +02:00
|
|
|
|
|
2023-12-29 10:57:26 +09:00
|
|
|
|
keybox_release (kbxhd);
|
|
|
|
|
}
|
2019-08-23 15:51:13 +02:00
|
|
|
|
}
|
2012-12-27 15:04:29 +01:00
|
|
|
|
used_resources++;
|
|
|
|
|
}
|
|
|
|
|
}
|
2016-01-13 09:29:39 +01:00
|
|
|
|
else if (gpg_err_code (err) == GPG_ERR_EEXIST)
|
2012-12-27 15:04:29 +01:00
|
|
|
|
{
|
|
|
|
|
/* Already registered. We will mark it as the primary key
|
|
|
|
|
if requested. */
|
2016-01-13 09:29:39 +01:00
|
|
|
|
if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
|
|
|
|
|
primary_keydb = token;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
default:
|
2012-06-05 19:29:22 +02:00
|
|
|
|
log_error ("resource type of '%s' not supported\n", url);
|
2016-01-13 09:29:39 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_GENERAL);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
goto leave;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-27 15:04:29 +01:00
|
|
|
|
/* fixme: check directory permissions and print a warning */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
leave:
|
2016-01-13 09:29:39 +01:00
|
|
|
|
if (err)
|
2016-11-10 17:01:19 +01:00
|
|
|
|
{
|
2023-04-04 16:39:59 +02:00
|
|
|
|
if (gpg_err_code (err) != GPG_ERR_TRUE)
|
|
|
|
|
{
|
|
|
|
|
log_error (_("keyblock resource '%s': %s\n"),
|
|
|
|
|
filename, gpg_strerror (err));
|
|
|
|
|
write_status_error ("add_keyblock_resource", err);
|
|
|
|
|
}
|
2016-11-10 17:01:19 +01:00
|
|
|
|
}
|
2011-04-29 15:07:11 +02:00
|
|
|
|
else
|
2012-12-27 15:04:29 +01:00
|
|
|
|
any_registered = 1;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
xfree (filename);
|
2016-01-13 09:29:39 +01:00
|
|
|
|
return err;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-06-20 15:03:32 +02:00
|
|
|
|
void
|
|
|
|
|
keydb_dump_stats (void)
|
|
|
|
|
{
|
2017-03-31 20:03:43 +02:00
|
|
|
|
log_info ("keydb: handles=%u locks=%u parse=%u get=%u\n",
|
|
|
|
|
keydb_stats.handles,
|
|
|
|
|
keydb_stats.locks,
|
|
|
|
|
keydb_stats.parse_keyblocks,
|
|
|
|
|
keydb_stats.get_keyblocks);
|
|
|
|
|
log_info (" build=%u update=%u insert=%u delete=%u\n",
|
|
|
|
|
keydb_stats.build_keyblocks,
|
|
|
|
|
keydb_stats.update_keyblocks,
|
|
|
|
|
keydb_stats.insert_keyblocks,
|
|
|
|
|
keydb_stats.delete_keyblocks);
|
|
|
|
|
log_info (" reset=%u found=%u not=%u cache=%u not=%u\n",
|
|
|
|
|
keydb_stats.search_resets,
|
|
|
|
|
keydb_stats.found,
|
|
|
|
|
keydb_stats.notfound,
|
|
|
|
|
keydb_stats.found_cached,
|
|
|
|
|
keydb_stats.notfound_cached);
|
|
|
|
|
log_info ("kid_not_found_cache: count=%u peak=%u flushes=%u\n",
|
|
|
|
|
kid_not_found_stats.count,
|
|
|
|
|
kid_not_found_stats.peak,
|
|
|
|
|
kid_not_found_stats.flushes);
|
2015-06-20 15:03:32 +02:00
|
|
|
|
}
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
/* keydb_new diverts to here in non-keyboxd mode. HD is just the
|
Spelling cleanup.
No functional changes, just fixing minor spelling issues.
---
Most of these were identified from the command line by running:
codespell \
--ignore-words-list fpr,stati,keyserver,keyservers,asign,cas,iff,ifset \
--skip '*.po,ChangeLog*,help.*.txt,*.jpg,*.eps,*.pdf,*.png,*.gpg,*.asc' \
doc g13 g10 kbx agent artwork scd tests tools am common dirmngr sm \
NEWS README README.maint TODO
Signed-off-by: Daniel Kahn Gillmor <dkg@fifthhorseman.net>
2020-02-18 09:34:42 -05:00
|
|
|
|
* calloced structure with the handle type initialized. */
|
2019-09-09 14:34:09 +02:00
|
|
|
|
gpg_error_t
|
|
|
|
|
internal_keydb_init (KEYDB_HANDLE hd)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2019-09-09 14:34:09 +02:00
|
|
|
|
gpg_error_t err = 0;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
int i, j;
|
2015-08-31 11:22:14 +02:00
|
|
|
|
int die = 0;
|
2015-12-03 12:18:32 +01:00
|
|
|
|
int reterrno;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
hd->found = -1;
|
2015-05-08 15:51:11 +02:00
|
|
|
|
hd->saved_found = -1;
|
2015-08-31 09:47:58 +02:00
|
|
|
|
hd->is_reset = 1;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2016-04-29 11:05:24 +02:00
|
|
|
|
log_assert (used_resources <= MAX_KEYDB_RESOURCES);
|
2015-08-31 11:22:14 +02:00
|
|
|
|
for (i=j=0; ! die && i < used_resources; i++)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
|
|
|
|
switch (all_resources[i].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
hd->active[j].type = all_resources[i].type;
|
|
|
|
|
hd->active[j].token = all_resources[i].token;
|
2010-04-23 11:36:59 +00:00
|
|
|
|
hd->active[j].u.kr = keyring_new (all_resources[i].token);
|
2015-08-31 11:22:14 +02:00
|
|
|
|
if (!hd->active[j].u.kr)
|
2015-12-03 12:18:32 +01:00
|
|
|
|
{
|
|
|
|
|
reterrno = errno;
|
|
|
|
|
die = 1;
|
|
|
|
|
}
|
2003-06-05 07:14:21 +00:00
|
|
|
|
j++;
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
hd->active[j].type = all_resources[i].type;
|
|
|
|
|
hd->active[j].token = all_resources[i].token;
|
2014-10-09 19:10:32 +02:00
|
|
|
|
hd->active[j].u.kb = keybox_new_openpgp (all_resources[i].token, 0);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
if (!hd->active[j].u.kb)
|
2015-12-03 12:18:32 +01:00
|
|
|
|
{
|
|
|
|
|
reterrno = errno;
|
|
|
|
|
die = 1;
|
|
|
|
|
}
|
2012-12-27 15:04:29 +01:00
|
|
|
|
j++;
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
hd->used = j;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
active_handles++;
|
2017-03-31 20:03:43 +02:00
|
|
|
|
keydb_stats.handles++;
|
2015-08-31 11:22:14 +02:00
|
|
|
|
|
|
|
|
|
if (die)
|
2019-09-09 14:34:09 +02:00
|
|
|
|
err = gpg_error_from_errno (reterrno);
|
2015-12-03 12:18:32 +01:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
return err;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2014-10-13 14:01:29 +02:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
/* Free all non-keyboxd resources owned by the database handle.
|
|
|
|
|
* keydb_release diverts to here. */
|
2011-02-04 12:57:53 +01:00
|
|
|
|
void
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_deinit (KEYDB_HANDLE hd)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
int i;
|
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
|
|
|
|
|
2016-04-29 11:05:24 +02:00
|
|
|
|
log_assert (active_handles > 0);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
active_handles--;
|
|
|
|
|
|
2017-10-18 18:28:52 +02:00
|
|
|
|
hd->keep_lock = 0;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
unlock_all (hd);
|
|
|
|
|
for (i=0; i < hd->used; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (hd->active[i].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
keyring_release (hd->active[i].u.kr);
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
keybox_release (hd->active[i].u.kb);
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2016-06-30 17:09:59 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2017-10-18 18:28:52 +02:00
|
|
|
|
/* Take a lock on the files immediately and not only during insert or
|
|
|
|
|
* update. This lock is released with keydb_release. */
|
|
|
|
|
gpg_error_t
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_lock (KEYDB_HANDLE hd)
|
2017-10-18 18:28:52 +02:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2017-10-18 18:28:52 +02:00
|
|
|
|
|
|
|
|
|
err = lock_all (hd);
|
|
|
|
|
if (!err)
|
|
|
|
|
hd->keep_lock = 1;
|
|
|
|
|
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* 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. */
|
2014-10-13 14:01:29 +02:00
|
|
|
|
void
|
|
|
|
|
keydb_disable_caching (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
2019-09-09 14:34:09 +02:00
|
|
|
|
if (hd && !hd->use_keyboxd)
|
2014-10-13 14:01:29 +02:00
|
|
|
|
hd->no_caching = 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Return the file name of the resource in which the current search
|
|
|
|
|
* result was found or, if there is no search result, the filename of
|
|
|
|
|
* the current resource (i.e., the resource that the file position
|
|
|
|
|
* points to). Note: the filename is not necessarily the URL used to
|
|
|
|
|
* open it!
|
|
|
|
|
*
|
|
|
|
|
* This function only returns NULL if no handle is specified, in all
|
|
|
|
|
* other error cases an empty string is returned. */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
const char *
|
|
|
|
|
keydb_get_resource_name (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
int idx;
|
|
|
|
|
const char *s = NULL;
|
|
|
|
|
|
|
|
|
|
if (!hd)
|
|
|
|
|
return NULL;
|
|
|
|
|
|
2020-01-21 11:17:18 +01:00
|
|
|
|
if (hd->use_keyboxd)
|
2019-09-09 14:34:09 +02:00
|
|
|
|
return "[keyboxd]";
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if ( hd->found >= 0 && hd->found < hd->used)
|
|
|
|
|
idx = hd->found;
|
|
|
|
|
else if ( hd->current >= 0 && hd->current < hd->used)
|
|
|
|
|
idx = hd->current;
|
|
|
|
|
else
|
|
|
|
|
idx = 0;
|
|
|
|
|
|
|
|
|
|
switch (hd->active[idx].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
s = NULL;
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
s = keyring_get_resource_name (hd->active[idx].u.kr);
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
s = keybox_get_resource_name (hd->active[idx].u.kb);
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
return s? s: "";
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
static int
|
2003-06-05 07:14:21 +00:00
|
|
|
|
lock_all (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
int i, rc = 0;
|
|
|
|
|
|
2012-12-27 15:04:29 +01:00
|
|
|
|
/* Fixme: This locking scheme may lead to a deadlock if the resources
|
|
|
|
|
are not added in the same order by all processes. We are
|
|
|
|
|
currently only allowing one resource so it is not a problem.
|
|
|
|
|
[Oops: Who claimed the latter]
|
|
|
|
|
|
|
|
|
|
To fix this we need to use a lock file to protect lock_all. */
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
for (i=0; !rc && i < hd->used; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (hd->active[i].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
rc = keyring_lock (hd->active[i].u.kr, 1);
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
2019-05-14 13:36:08 +02:00
|
|
|
|
rc = keybox_lock (hd->active[i].u.kb, 1, -1);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (rc)
|
|
|
|
|
{
|
2012-12-27 15:04:29 +01:00
|
|
|
|
/* Revert the already taken locks. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
for (i--; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
switch (hd->active[i].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
keyring_lock (hd->active[i].u.kr, 0);
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
2019-05-14 13:36:08 +02:00
|
|
|
|
keybox_lock (hd->active[i].u.kb, 0, 0);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-29 15:07:11 +02:00
|
|
|
|
else
|
2017-03-31 20:03:43 +02:00
|
|
|
|
{
|
|
|
|
|
hd->locked = 1;
|
|
|
|
|
keydb_stats.locks++;
|
|
|
|
|
}
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
static void
|
|
|
|
|
unlock_all (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
int i;
|
|
|
|
|
|
2017-10-18 18:28:52 +02:00
|
|
|
|
if (!hd->locked || hd->keep_lock)
|
2011-04-29 15:07:11 +02:00
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
for (i=hd->used-1; i >= 0; i--)
|
|
|
|
|
{
|
|
|
|
|
switch (hd->active[i].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
keyring_lock (hd->active[i].u.kr, 0);
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
2019-05-14 13:36:08 +02:00
|
|
|
|
keybox_lock (hd->active[i].u.kb, 0, 0);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-29 15:07:11 +02:00
|
|
|
|
hd->locked = 0;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2015-05-08 15:51:11 +02:00
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Save the last found state and invalidate the current selection
|
|
|
|
|
* (i.e., the entry selected by keydb_search() is invalidated and
|
|
|
|
|
* something like keydb_get_keyblock() will return an error). This
|
|
|
|
|
* does not change the file position. This makes it possible to do
|
|
|
|
|
* something like:
|
|
|
|
|
*
|
|
|
|
|
* keydb_search (hd, ...); // Result 1.
|
|
|
|
|
* keydb_push_found_state (hd);
|
|
|
|
|
* keydb_search_reset (hd);
|
|
|
|
|
* keydb_search (hd, ...); // Result 2.
|
|
|
|
|
* keydb_pop_found_state (hd);
|
|
|
|
|
* keydb_get_keyblock (hd, ...); // -> Result 1.
|
|
|
|
|
*
|
|
|
|
|
* Note: it is only possible to save a single save state at a time.
|
2017-02-20 16:19:50 -05:00
|
|
|
|
* In other words, the save stack only has room for a single
|
2016-01-11 11:41:49 +01:00
|
|
|
|
* instance of the state. */
|
2019-09-09 14:34:09 +02:00
|
|
|
|
/* FIXME(keyboxd): This function is used only at one place - see how
|
|
|
|
|
* we can avoid it. */
|
2015-05-08 15:51:11 +02:00
|
|
|
|
void
|
|
|
|
|
keydb_push_found_state (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
|
|
|
|
if (!hd)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
if (hd->found < 0 || hd->found >= hd->used)
|
|
|
|
|
{
|
|
|
|
|
hd->saved_found = -1;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
switch (hd->active[hd->found].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
keyring_push_found_state (hd->active[hd->found].u.kr);
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
keybox_push_found_state (hd->active[hd->found].u.kb);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
hd->saved_found = hd->found;
|
|
|
|
|
hd->found = -1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Restore the previous save state. If the saved state is NULL or
|
|
|
|
|
invalid, this is a NOP. */
|
2019-09-09 14:34:09 +02:00
|
|
|
|
/* FIXME(keyboxd): This function is used only at one place - see how
|
|
|
|
|
* we can avoid it. */
|
2015-05-08 15:51:11 +02:00
|
|
|
|
void
|
|
|
|
|
keydb_pop_found_state (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
|
|
|
|
if (!hd)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
hd->found = hd->saved_found;
|
|
|
|
|
hd->saved_found = -1;
|
|
|
|
|
if (hd->found < 0 || hd->found >= hd->used)
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
switch (hd->active[hd->found].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
keyring_pop_found_state (hd->active[hd->found].u.kr);
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
keybox_pop_found_state (hd->active[hd->found].u.kb);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2020-09-02 15:05:44 +02:00
|
|
|
|
/* Parse the keyblock in IOBUF and return at R_KEYBLOCK. */
|
|
|
|
|
gpg_error_t
|
|
|
|
|
keydb_parse_keyblock (iobuf_t iobuf, int pk_no, int uid_no,
|
2017-03-30 15:18:45 +02:00
|
|
|
|
kbnode_t *r_keyblock)
|
2012-12-28 14:03:16 +01:00
|
|
|
|
{
|
|
|
|
|
gpg_error_t err;
|
2017-03-29 10:02:40 +02:00
|
|
|
|
struct parse_packet_ctx_s parsectx;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
PACKET *pkt;
|
|
|
|
|
kbnode_t keyblock = NULL;
|
2012-12-28 17:17:56 +01:00
|
|
|
|
kbnode_t node, *tail;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
int in_cert, save_mode;
|
2013-01-07 21:14:52 +01:00
|
|
|
|
int pk_count, uid_count;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
|
|
|
|
|
*r_keyblock = NULL;
|
|
|
|
|
|
|
|
|
|
pkt = xtrymalloc (sizeof *pkt);
|
|
|
|
|
if (!pkt)
|
|
|
|
|
return gpg_error_from_syserror ();
|
|
|
|
|
init_packet (pkt);
|
2017-03-29 10:02:40 +02:00
|
|
|
|
init_parse_packet (&parsectx, iobuf);
|
2012-12-28 14:03:16 +01:00
|
|
|
|
save_mode = set_packet_list_mode (0);
|
|
|
|
|
in_cert = 0;
|
2012-12-28 17:17:56 +01:00
|
|
|
|
tail = NULL;
|
2013-01-07 21:14:52 +01:00
|
|
|
|
pk_count = uid_count = 0;
|
2017-03-29 10:02:40 +02:00
|
|
|
|
while ((err = parse_packet (&parsectx, pkt)) != -1)
|
2012-12-28 14:03:16 +01:00
|
|
|
|
{
|
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET)
|
|
|
|
|
{
|
2017-03-29 11:57:40 +02:00
|
|
|
|
free_packet (pkt, &parsectx);
|
2012-12-28 14:03:16 +01:00
|
|
|
|
init_packet (pkt);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
if (err)
|
|
|
|
|
{
|
2019-05-21 17:27:42 +02:00
|
|
|
|
es_fflush (es_stdout);
|
2012-12-28 14:03:16 +01:00
|
|
|
|
log_error ("parse_keyblock_image: read error: %s\n",
|
|
|
|
|
gpg_strerror (err));
|
2019-05-21 17:27:42 +02:00
|
|
|
|
if (gpg_err_code (err) == GPG_ERR_INV_PACKET)
|
|
|
|
|
{
|
|
|
|
|
free_packet (pkt, &parsectx);
|
|
|
|
|
init_packet (pkt);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2012-12-28 14:03:16 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_KEYRING);
|
|
|
|
|
break;
|
|
|
|
|
}
|
2015-02-09 15:46:00 +01:00
|
|
|
|
|
|
|
|
|
/* Filter allowed packets. */
|
|
|
|
|
switch (pkt->pkttype)
|
2012-12-28 14:03:16 +01:00
|
|
|
|
{
|
2015-02-09 15:46:00 +01:00
|
|
|
|
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:
|
gpg: Revamp reading and writing of ring trust packets.
* g10/parse-packet.c (parse_trust): Rename to ...
(parse_ring_trust): this. Change args and implement new ring trust
packet format.
(parse): Add special ring trust packet handling.
* g10/packet.h (PKT_user_id): New fields KEYUPDATE, UPDATEURL, and
KEYSRC.
(PKT_public_key): Ditto.
(RING_TRUST_SIG, RING_TRUST_KEY, RING_TRUST_UID): New consts.
(PKT_ring_trust): New.
(struct packet_struct): Remove member RING_TRUST.
(strcu parse_packet_ctx_s): Add field SKIP_META.
(init_parse_packet): Init SKIPT_META.
* g10/free-packet.c (release_public_key_parts): Free UDPATEURL.
(free_user_id): Ditto.
* g10/mainproc.c (list_node): Remove printing of non-documented "rtv"
lines.
* g10/build-packet.c (build_packet_and_meta): New.
(do_ring_trust): New.
* g10/export.c (write_keyblock_to_output): Use build_packet_and_meta
in backup mode.
(do_export_one_keyblock): Ditto.
* g10/import.c (read_block): Add arg WITH_META. Skip ring trust
packets if that ism not set.
(import): Call read_block WITH_META in restore mode.
* g10/keydb.h (KEYSRC_UNKNOWN, KEYSRC_FILE, KEYSRC_KS, KEYSRC_PREF_KS)
(KEYSRC_WKD, KEYSRC_WKD_SD, KEYSRC_DANE): New constants. They are not
yet used, though.
* g10/keydb.c (parse_keyblock_image): Allow ring trust packets.
(build_keyblock_image): Ditto. Use build_packet_and_meta.
* g10/keyring.c (keyring_get_keyblock): Remove specila treatment of
ring trust packets.
(write_keyblock): Use build_packet_and_meta. Remove special treatment
of ring trust packets and initialization of the signature caches.
--
This patch introduced the framework to store meta data for keys and
user ids in the keyrings/keyboxes. Ring trust packets are
implementation defined and have always been used in gpg to cache the
signature verification status.
Ring trust packets are only exported with the export option "backup"
and only imported with the import option "restore".
The new code uses a cleaner way to handle the ring trust packets: When
the parser reads a ring trust packet and the previously read packet
matches the type of that ring trust packet, the information is stored
in that previously read packet (signature, user id, or primary key)
and the next packet is read immediately. Thus only the parser sees
the ring trust packets. Ring trust packets are written by using the
new function build_packet_and_meta instead of build_packet. That
function writes a ring trust packet when the needed information is
available.
As a side-effect of this patch the signature status cache works again
and "gpg --check-sigs" is thus much faster.
Signed-off-by: Werner Koch <wk@gnupg.org>
2017-03-30 09:07:02 +02:00
|
|
|
|
case PKT_RING_TRUST:
|
2015-02-09 15:46:00 +01:00
|
|
|
|
break; /* Allowed per RFC. */
|
|
|
|
|
|
|
|
|
|
default:
|
2018-02-22 14:23:01 +01:00
|
|
|
|
log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype);
|
2017-03-29 11:57:40 +02:00
|
|
|
|
free_packet(pkt, &parsectx);
|
2012-12-28 14:03:16 +01:00
|
|
|
|
init_packet(pkt);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-09 15:46:00 +01:00
|
|
|
|
/* Other sanity checks. */
|
2012-12-28 14:03:16 +01:00
|
|
|
|
if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY)
|
|
|
|
|
{
|
2012-12-28 17:17:56 +01:00
|
|
|
|
log_error ("parse_keyblock_image: first packet in a keybox blob "
|
|
|
|
|
"is not a public key packet\n");
|
2012-12-28 14:03:16 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_KEYRING);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY
|
|
|
|
|
|| pkt->pkttype == PKT_SECRET_KEY))
|
|
|
|
|
{
|
2012-12-28 17:17:56 +01:00
|
|
|
|
log_error ("parse_keyblock_image: "
|
|
|
|
|
"multiple keyblocks in a keybox blob\n");
|
2012-12-28 14:03:16 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_INV_KEYRING);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
in_cert = 1;
|
|
|
|
|
|
|
|
|
|
node = new_kbnode (pkt);
|
2013-01-07 21:14:52 +01:00
|
|
|
|
|
|
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2012-12-28 14:03:16 +01:00
|
|
|
|
if (!keyblock)
|
|
|
|
|
keyblock = node;
|
|
|
|
|
else
|
2012-12-28 17:17:56 +01:00
|
|
|
|
*tail = node;
|
|
|
|
|
tail = &node->next;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
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
|
2017-03-31 20:03:43 +02:00
|
|
|
|
{
|
|
|
|
|
*r_keyblock = keyblock;
|
|
|
|
|
keydb_stats.parse_keyblocks++;
|
|
|
|
|
}
|
2017-03-29 11:57:40 +02:00
|
|
|
|
free_packet (pkt, &parsectx);
|
|
|
|
|
deinit_parse_packet (&parsectx);
|
2012-12-28 14:03:16 +01:00
|
|
|
|
xfree (pkt);
|
|
|
|
|
return err;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Return the keyblock last found by keydb_search() in *RET_KB.
|
2019-09-09 14:34:09 +02:00
|
|
|
|
* keydb_get_keyblock divert to here in the non-keyboxd mode.
|
2016-01-11 11:41:49 +01:00
|
|
|
|
*
|
|
|
|
|
* 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. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t err = 0;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2015-06-19 16:59:46 +02:00
|
|
|
|
|
2015-08-31 13:57:07 +02:00
|
|
|
|
if (hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
|
2013-01-08 14:44:49 +01:00
|
|
|
|
{
|
2015-08-31 13:57:07 +02:00
|
|
|
|
err = iobuf_seek (hd->keyblock_cache.iobuf, 0);
|
2013-01-08 14:44:49 +01:00
|
|
|
|
if (err)
|
2015-09-02 10:33:26 +02:00
|
|
|
|
{
|
|
|
|
|
log_error ("keydb_get_keyblock: failed to rewind iobuf for cache\n");
|
2015-08-31 13:57:07 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2015-09-02 10:33:26 +02:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
2020-09-02 15:05:44 +02:00
|
|
|
|
err = keydb_parse_keyblock (hd->keyblock_cache.iobuf,
|
2015-08-31 13:57:07 +02:00
|
|
|
|
hd->keyblock_cache.pk_no,
|
|
|
|
|
hd->keyblock_cache.uid_no,
|
2015-09-02 10:33:26 +02:00
|
|
|
|
ret_kb);
|
|
|
|
|
if (err)
|
2015-08-31 13:57:07 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2015-09-02 10:33:26 +02:00
|
|
|
|
if (DBG_CLOCK)
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_clock ("%s leave (cached mode)", __func__);
|
2015-09-02 10:33:26 +02:00
|
|
|
|
return err;
|
|
|
|
|
}
|
2013-01-08 14:44:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (hd->found < 0 || hd->found >= hd->used)
|
|
|
|
|
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
switch (hd->active[hd->found].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
err = gpg_error (GPG_ERR_GENERAL); /* oops */
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
err = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
|
|
|
|
|
break;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
{
|
|
|
|
|
iobuf_t iobuf;
|
2013-01-07 21:14:52 +01:00
|
|
|
|
int pk_no, uid_no;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
|
2012-12-28 17:17:56 +01:00
|
|
|
|
err = keybox_get_keyblock (hd->active[hd->found].u.kb,
|
2017-03-30 15:18:45 +02:00
|
|
|
|
&iobuf, &pk_no, &uid_no);
|
2012-12-28 14:03:16 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
{
|
2020-09-02 15:05:44 +02:00
|
|
|
|
err = keydb_parse_keyblock (iobuf, pk_no, uid_no, ret_kb);
|
2015-08-31 13:57:07 +02:00
|
|
|
|
if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
|
2013-01-08 14:44:49 +01:00
|
|
|
|
{
|
2015-08-31 13:57:07 +02:00
|
|
|
|
hd->keyblock_cache.state = KEYBLOCK_CACHE_FILLED;
|
|
|
|
|
hd->keyblock_cache.iobuf = iobuf;
|
|
|
|
|
hd->keyblock_cache.pk_no = pk_no;
|
|
|
|
|
hd->keyblock_cache.uid_no = uid_no;
|
2013-01-08 14:44:49 +01:00
|
|
|
|
}
|
2016-06-30 17:09:59 +02:00
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
iobuf_close (iobuf);
|
|
|
|
|
}
|
2012-12-28 14:03:16 +01:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-31 13:57:07 +02:00
|
|
|
|
if (hd->keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
|
|
|
|
|
keyblock_cache_clear (hd);
|
2013-01-08 14:44:49 +01:00
|
|
|
|
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
keydb_stats.get_keyblocks++;
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
return err;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2012-12-28 14:03:16 +01:00
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Update the keyblock KB (i.e., extract the fingerprint and find the
|
|
|
|
|
* corresponding keyblock in the keyring).
|
2019-09-09 14:34:09 +02:00
|
|
|
|
* keydb_update_keyblock diverts to here in the non-keyboxd mode.
|
2016-01-11 11:41:49 +01:00
|
|
|
|
*
|
|
|
|
|
* 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. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2013-11-15 15:54:31 +01:00
|
|
|
|
gpg_error_t err;
|
2015-12-23 15:16:02 +01:00
|
|
|
|
PKT_public_key *pk;
|
2015-12-15 20:05:20 +01:00
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
|
|
|
|
size_t len;
|
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2015-12-23 15:16:02 +01:00
|
|
|
|
pk = kb->pkt->pkt.public_key;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-06-20 15:03:32 +02:00
|
|
|
|
kid_not_found_flush ();
|
2015-08-31 13:57:07 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2013-01-08 14:44:49 +01:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (opt.dry_run)
|
|
|
|
|
return 0;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2013-11-15 15:54:31 +01:00
|
|
|
|
err = lock_all (hd);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
g10: Cache the effective policy. Recompute it when required.
* g10/tofu.c (initdb): Add column effective_policy to the bindings
table.
(record_binding): New parameters effective_policy and set_conflict.
Save the effective policy. If SET_CONFLICT is set, then set conflict
according to CONFLICT. Otherwise, preserve the current value of
conflict. Update callers.
(get_trust): Don't compute the effective policy here...
(get_policy): ... do it here, if it was not cached. Take new
parameters, PK, the public key, and NOW, the time that the operation
started. Update callers.
(show_statistics): New parameter PK. Pass it to get_policy. Update
callers.
(tofu_notice_key_changed): New function.
* g10/gpgv.c (tofu_notice_key_changed): New stub.
* g10/import.c (import_revoke_cert): Take additional argument CTRL.
Pass it to keydb_update_keyblock.
* g10/keydb.c (keydb_update_keyblock): Take additional argument CTRL.
Update callers.
[USE_TOFU]: Call tofu_notice_key_changed.
* g10/test-stubs.c (tofu_notice_key_changed): New stub.
* tests/openpgp/tofu.scm: Assume that manually setting a binding's
policy to auto does not cause the tofu engine to forget about any
conflict.
--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
We now store the computed policy in the tofu DB (in the
effective_policy column of the bindings table) to avoid computing it
every time, which is expensive. Further, policy is never overridden
in case of a conflict. Instead, we detect a conflict if CONFLICT is
not empty.
This change is backwards compatible to existing DBs. The only minor
incompatibility is that unresolved conflicts won't be automatically
resolved in case we import a direct signature, or cross signatures.
2016-11-21 22:47:30 +01:00
|
|
|
|
#ifdef USE_TOFU
|
|
|
|
|
tofu_notice_key_changed (ctrl, kb);
|
2024-12-04 11:04:41 +01:00
|
|
|
|
#else
|
|
|
|
|
(void)ctrl;
|
g10: Cache the effective policy. Recompute it when required.
* g10/tofu.c (initdb): Add column effective_policy to the bindings
table.
(record_binding): New parameters effective_policy and set_conflict.
Save the effective policy. If SET_CONFLICT is set, then set conflict
according to CONFLICT. Otherwise, preserve the current value of
conflict. Update callers.
(get_trust): Don't compute the effective policy here...
(get_policy): ... do it here, if it was not cached. Take new
parameters, PK, the public key, and NOW, the time that the operation
started. Update callers.
(show_statistics): New parameter PK. Pass it to get_policy. Update
callers.
(tofu_notice_key_changed): New function.
* g10/gpgv.c (tofu_notice_key_changed): New stub.
* g10/import.c (import_revoke_cert): Take additional argument CTRL.
Pass it to keydb_update_keyblock.
* g10/keydb.c (keydb_update_keyblock): Take additional argument CTRL.
Update callers.
[USE_TOFU]: Call tofu_notice_key_changed.
* g10/test-stubs.c (tofu_notice_key_changed): New stub.
* tests/openpgp/tofu.scm: Assume that manually setting a binding's
policy to auto does not cause the tofu engine to forget about any
conflict.
--
Signed-off-by: Neal H. Walfield <neal@g10code.com>
We now store the computed policy in the tofu DB (in the
effective_policy column of the bindings table) to avoid computing it
every time, which is expensive. Further, policy is never overridden
in case of a conflict. Instead, we detect a conflict if CONFLICT is
not empty.
This change is backwards compatible to existing DBs. The only minor
incompatibility is that unresolved conflicts won't be automatically
resolved in case we import a direct signature, or cross signatures.
2016-11-21 22:47:30 +01:00
|
|
|
|
#endif
|
|
|
|
|
|
2015-12-15 20:05:20 +01:00
|
|
|
|
memset (&desc, 0, sizeof (desc));
|
|
|
|
|
fingerprint_from_pk (pk, desc.u.fpr, &len);
|
2019-03-14 14:55:06 +01:00
|
|
|
|
if (len == 20 || len == 32)
|
|
|
|
|
{
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_FPR;
|
|
|
|
|
desc.fprlen = len;
|
|
|
|
|
}
|
2015-12-15 20:05:20 +01:00
|
|
|
|
else
|
2015-12-23 15:16:02 +01:00
|
|
|
|
log_bug ("%s: Unsupported key length: %zu\n", __func__, len);
|
2015-12-15 20:05:20 +01:00
|
|
|
|
|
|
|
|
|
keydb_search_reset (hd);
|
|
|
|
|
err = keydb_search (hd, &desc, 1, NULL);
|
|
|
|
|
if (err)
|
|
|
|
|
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
|
2016-04-29 11:05:24 +02:00
|
|
|
|
log_assert (hd->found >= 0 && hd->found < hd->used);
|
2015-12-15 20:05:20 +01:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
switch (hd->active[hd->found].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
2013-11-15 15:54:31 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_GENERAL); /* oops */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
2013-11-15 15:54:31 +01:00
|
|
|
|
err = keyring_update_keyblock (hd->active[hd->found].u.kr, kb);
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
{
|
|
|
|
|
iobuf_t iobuf;
|
|
|
|
|
|
2017-03-30 15:18:45 +02:00
|
|
|
|
err = build_keyblock_image (kb, &iobuf);
|
2013-11-15 15:54:31 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
{
|
2019-11-27 11:58:47 +01:00
|
|
|
|
keydb_stats.build_keyblocks++;
|
2013-11-15 15:54:31 +01:00
|
|
|
|
err = keybox_update_keyblock (hd->active[hd->found].u.kb,
|
|
|
|
|
iobuf_get_temp_buffer (iobuf),
|
|
|
|
|
iobuf_get_temp_length (iobuf));
|
|
|
|
|
iobuf_close (iobuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-29 15:07:11 +02:00
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
unlock_all (hd);
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
keydb_stats.update_keyblocks++;
|
2013-11-15 15:54:31 +01:00
|
|
|
|
return err;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Insert a keyblock into one of the underlying keyrings or keyboxes.
|
2019-09-09 14:34:09 +02:00
|
|
|
|
* keydb_insert_keyblock diverts to here in the non-keyboxd mode.
|
2016-01-11 11:41:49 +01:00
|
|
|
|
*
|
|
|
|
|
* 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. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2012-12-28 14:03:16 +01:00
|
|
|
|
gpg_error_t err;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
int idx;
|
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2015-06-20 15:03:32 +02:00
|
|
|
|
kid_not_found_flush ();
|
2015-08-31 13:57:07 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2013-01-08 14:44:49 +01:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (opt.dry_run)
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
if (hd->found >= 0 && hd->found < hd->used)
|
|
|
|
|
idx = hd->found;
|
|
|
|
|
else if (hd->current >= 0 && hd->current < hd->used)
|
|
|
|
|
idx = hd->current;
|
|
|
|
|
else
|
|
|
|
|
return gpg_error (GPG_ERR_GENERAL);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2012-12-28 14:03:16 +01:00
|
|
|
|
err = lock_all (hd);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
|
|
|
|
switch (hd->active[idx].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
2012-12-28 14:03:16 +01:00
|
|
|
|
err = gpg_error (GPG_ERR_GENERAL); /* oops */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
2012-12-28 14:03:16 +01:00
|
|
|
|
err = keyring_insert_keyblock (hd->active[idx].u.kr, kb);
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
{ /* We need to turn our kbnode_t list of packets into a proper
|
|
|
|
|
keyblock first. This is required by the OpenPGP key parser
|
|
|
|
|
included in the keybox code. Eventually we can change this
|
|
|
|
|
kludge to have the caller pass the image. */
|
|
|
|
|
iobuf_t iobuf;
|
|
|
|
|
|
2017-03-30 15:18:45 +02:00
|
|
|
|
err = build_keyblock_image (kb, &iobuf);
|
2012-12-28 14:03:16 +01:00
|
|
|
|
if (!err)
|
|
|
|
|
{
|
2019-11-27 11:58:47 +01:00
|
|
|
|
keydb_stats.build_keyblocks++;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
err = keybox_insert_keyblock (hd->active[idx].u.kb,
|
|
|
|
|
iobuf_get_temp_buffer (iobuf),
|
2017-03-30 15:18:45 +02:00
|
|
|
|
iobuf_get_temp_length (iobuf));
|
2012-12-28 14:03:16 +01:00
|
|
|
|
iobuf_close (iobuf);
|
|
|
|
|
}
|
|
|
|
|
}
|
2011-04-29 15:07:11 +02:00
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
unlock_all (hd);
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (!err)
|
|
|
|
|
keydb_stats.insert_keyblocks++;
|
2012-12-28 14:03:16 +01:00
|
|
|
|
return err;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Delete the currently selected keyblock. If you haven't done a
|
|
|
|
|
* search yet on this database handle (or called keydb_search_reset),
|
|
|
|
|
* then this will return an error.
|
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success or an error code, if an error occurs. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_delete_keyblock (KEYDB_HANDLE hd)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-06-20 15:03:32 +02:00
|
|
|
|
kid_not_found_flush ();
|
2015-08-31 13:57:07 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2013-01-08 14:44:49 +01:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (hd->found < 0 || hd->found >= hd->used)
|
|
|
|
|
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (opt.dry_run)
|
|
|
|
|
return 0;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
rc = lock_all (hd);
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
switch (hd->active[hd->found].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
rc = gpg_error (GPG_ERR_GENERAL);
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
rc = keyring_delete_keyblock (hd->active[hd->found].u.kr);
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
rc = keybox_delete (hd->active[hd->found].u.kb);
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
unlock_all (hd);
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (!rc)
|
|
|
|
|
keydb_stats.delete_keyblocks++;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* A database may consists of multiple keyrings / key boxes. This
|
|
|
|
|
* sets the "file position" to the start of the first keyring / key
|
|
|
|
|
* box that is writable (i.e., doesn't have the read-only flag set).
|
|
|
|
|
*
|
|
|
|
|
* This first tries the primary keyring (the last keyring (not
|
|
|
|
|
* keybox!) added using keydb_add_resource() and with
|
|
|
|
|
* KEYDB_RESOURCE_FLAG_PRIMARY set). If that is not writable, then it
|
|
|
|
|
* tries the keyrings / keyboxes in the order in which they were
|
|
|
|
|
* added. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2015-08-28 16:22:59 +02:00
|
|
|
|
keydb_locate_writable (KEYDB_HANDLE hd)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t rc;
|
2008-10-20 13:53:23 +00:00
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
if (!hd)
|
2015-01-22 12:06:11 +01:00
|
|
|
|
return GPG_ERR_INV_ARG;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
if (hd->use_keyboxd)
|
|
|
|
|
return 0; /* No need for this here. */
|
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
rc = keydb_search_reset (hd); /* this does reset hd->current */
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
|
|
|
|
|
/* If we have a primary set, try that one first */
|
2016-01-13 09:29:39 +01:00
|
|
|
|
if (primary_keydb)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
|
|
|
|
for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
|
|
|
|
|
{
|
2016-01-13 09:29:39 +01:00
|
|
|
|
if(hd->active[hd->current].token == primary_keydb)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
|
|
|
|
if(keyring_is_writable (hd->active[hd->current].token))
|
|
|
|
|
return 0;
|
|
|
|
|
else
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rc = keydb_search_reset (hd); /* this does reset hd->current */
|
|
|
|
|
if (rc)
|
|
|
|
|
return rc;
|
|
|
|
|
}
|
|
|
|
|
|
2011-02-04 12:57:53 +01:00
|
|
|
|
for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-02-04 12:57:53 +01:00
|
|
|
|
switch (hd->active[hd->current].type)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
BUG();
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
if (keyring_is_writable (hd->active[hd->current].token))
|
|
|
|
|
return 0; /* found (hd->current is set to it) */
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
if (keybox_is_writable (hd->active[hd->current].token))
|
|
|
|
|
return 0; /* found (hd->current is set to it) */
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
return gpg_error (GPG_ERR_NOT_FOUND);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
|
|
|
|
|
/* Rebuild the on-disk caches of all key resources. */
|
2003-06-05 07:14:21 +00:00
|
|
|
|
void
|
2017-03-31 20:03:52 +02:00
|
|
|
|
keydb_rebuild_caches (ctrl_t ctrl, int noisy)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
|
|
|
|
int i, rc;
|
2011-02-04 12:57:53 +01:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
if (opt.use_keyboxd)
|
|
|
|
|
return; /* No need for this here. */
|
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
for (i=0; i < used_resources; i++)
|
|
|
|
|
{
|
2009-04-03 10:34:22 +00:00
|
|
|
|
if (!keyring_is_writable (all_resources[i].token))
|
|
|
|
|
continue;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
switch (all_resources[i].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
2017-03-31 20:03:52 +02:00
|
|
|
|
rc = keyring_rebuild_cache (ctrl, all_resources[i].token,noisy);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
if (rc)
|
|
|
|
|
log_error (_("failed to rebuild keyring cache: %s\n"),
|
2015-01-22 12:06:11 +01:00
|
|
|
|
gpg_strerror (rc));
|
2003-06-05 07:14:21 +00:00
|
|
|
|
break;
|
2013-11-15 15:54:31 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
/* N/A. */
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
/* Return the number of skipped blocks (because they were too large to
|
2016-01-11 11:41:49 +01:00
|
|
|
|
read from a keybox) since the last search reset. */
|
2014-10-09 21:01:49 +02:00
|
|
|
|
unsigned long
|
|
|
|
|
keydb_get_skipped_counter (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
2019-09-09 14:34:09 +02:00
|
|
|
|
/*FIXME(keyboxd): Do we need this? */
|
|
|
|
|
return hd && !hd->use_keyboxd? hd->skipped_long_blobs : 0;
|
2014-10-09 21:01:49 +02:00
|
|
|
|
}
|
|
|
|
|
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* 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).
|
2019-09-09 14:34:09 +02:00
|
|
|
|
* keydb_search_reset diverts to here in the non-keyboxd mode.
|
2016-01-11 11:41:49 +01:00
|
|
|
|
*
|
|
|
|
|
* Returns 0 on success and an error code if an error occurred.
|
|
|
|
|
* (Currently, this function always returns 0 if HD is valid.) */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_search_reset (KEYDB_HANDLE hd)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t rc = 0;
|
|
|
|
|
int i;
|
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2015-08-31 13:57:07 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2013-01-08 14:44:49 +01:00
|
|
|
|
|
2014-10-09 21:01:49 +02:00
|
|
|
|
hd->skipped_long_blobs = 0;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
hd->current = 0;
|
|
|
|
|
hd->found = -1;
|
|
|
|
|
/* Now reset all resources. */
|
|
|
|
|
for (i=0; !rc && i < hd->used; i++)
|
|
|
|
|
{
|
|
|
|
|
switch (hd->active[i].type)
|
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
rc = keyring_search_reset (hd->active[i].u.kr);
|
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
|
|
|
|
rc = keybox_search_reset (hd->active[i].u.kb);
|
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
2015-08-31 09:47:58 +02:00
|
|
|
|
hd->is_reset = 1;
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (!rc)
|
|
|
|
|
keydb_stats.search_resets++;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Search the database for keys matching the search description. If
|
|
|
|
|
* the DB contains any legacy keys, these are silently ignored.
|
2019-09-09 14:34:09 +02:00
|
|
|
|
* keydb_search diverts to here in the non-keyboxd mode.
|
2016-01-11 11:41:49 +01:00
|
|
|
|
*
|
|
|
|
|
* 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(). */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2019-09-09 14:34:09 +02:00
|
|
|
|
internal_keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
|
|
|
|
|
size_t ndesc, size_t *descindex)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t rc;
|
2015-08-31 09:47:58 +02:00
|
|
|
|
int was_reset = hd->is_reset;
|
2015-08-31 11:14:21 +02:00
|
|
|
|
/* If an entry is already in the cache, then don't add it again. */
|
2015-08-26 12:22:24 +02:00
|
|
|
|
int already_in_cache = 0;
|
2019-03-14 08:54:59 +01:00
|
|
|
|
int fprlen;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_assert (!hd->use_keyboxd);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2016-11-10 17:01:19 +01:00
|
|
|
|
if (!any_registered)
|
|
|
|
|
{
|
|
|
|
|
write_status_error ("keydb_search", gpg_error (GPG_ERR_KEYRING_OPEN));
|
|
|
|
|
return gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
|
2015-06-20 15:03:32 +02:00
|
|
|
|
if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
|
2015-08-26 12:22:24 +02:00
|
|
|
|
&& (already_in_cache = kid_not_found_p (desc[0].u.kid)) == 1 )
|
2015-06-20 15:03:32 +02:00
|
|
|
|
{
|
|
|
|
|
if (DBG_CLOCK)
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_clock ("%s leave (not found, cached)", __func__);
|
2017-03-31 20:03:43 +02:00
|
|
|
|
keydb_stats.notfound_cached++;
|
2015-06-20 15:03:32 +02:00
|
|
|
|
return gpg_error (GPG_ERR_NOT_FOUND);
|
|
|
|
|
}
|
|
|
|
|
|
2014-12-01 11:54:51 +01:00
|
|
|
|
/* NB: If one of the exact search modes below is used in a loop to
|
|
|
|
|
walk over all keys (with the same fingerprint) the caching must
|
|
|
|
|
have been disabled for the handle. */
|
2019-03-14 14:55:06 +01:00
|
|
|
|
if (desc[0].mode == KEYDB_SEARCH_MODE_FPR)
|
2019-03-14 08:54:59 +01:00
|
|
|
|
fprlen = desc[0].fprlen;
|
|
|
|
|
else
|
|
|
|
|
fprlen = 0;
|
|
|
|
|
|
2014-10-13 14:01:29 +02:00
|
|
|
|
if (!hd->no_caching
|
|
|
|
|
&& ndesc == 1
|
2019-03-14 08:54:59 +01:00
|
|
|
|
&& fprlen
|
|
|
|
|
&& hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED
|
|
|
|
|
&& hd->keyblock_cache.fprlen == fprlen
|
|
|
|
|
&& !memcmp (hd->keyblock_cache.fpr, desc[0].u.fpr, fprlen)
|
2015-12-15 12:21:30 +01:00
|
|
|
|
/* Make sure the current file position occurs before the cached
|
|
|
|
|
result to avoid an infinite loop. */
|
|
|
|
|
&& (hd->current < hd->keyblock_cache.resource
|
|
|
|
|
|| (hd->current == hd->keyblock_cache.resource
|
|
|
|
|
&& (keybox_offset (hd->active[hd->current].u.kb)
|
|
|
|
|
<= hd->keyblock_cache.offset))))
|
2013-01-08 14:44:49 +01:00
|
|
|
|
{
|
2014-05-14 16:32:49 +02:00
|
|
|
|
/* (DESCINDEX is already set). */
|
2013-01-08 14:44:49 +01:00
|
|
|
|
if (DBG_CLOCK)
|
2019-09-09 14:34:09 +02:00
|
|
|
|
log_clock ("%s leave (cached)", __func__);
|
2015-12-15 13:09:27 +01:00
|
|
|
|
|
|
|
|
|
hd->current = hd->keyblock_cache.resource;
|
|
|
|
|
/* HD->KEYBLOCK_CACHE.OFFSET is the last byte in the record.
|
|
|
|
|
Seek just beyond that. */
|
2019-03-14 08:54:59 +01:00
|
|
|
|
keybox_seek (hd->active[hd->current].u.kb, hd->keyblock_cache.offset + 1);
|
2017-03-31 20:03:43 +02:00
|
|
|
|
keydb_stats.found_cached++;
|
2013-01-08 14:44:49 +01:00
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
rc = -1;
|
|
|
|
|
while ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
|
|
|
|
|
&& hd->current >= 0 && hd->current < hd->used)
|
|
|
|
|
{
|
2024-08-09 09:31:54 +02:00
|
|
|
|
if (DBG_KEYDB)
|
2015-11-16 20:55:34 +01:00
|
|
|
|
log_debug ("%s: searching %s (resource %d of %d)\n",
|
|
|
|
|
__func__,
|
|
|
|
|
hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYRING
|
|
|
|
|
? "keyring"
|
|
|
|
|
: (hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX
|
|
|
|
|
? "keybox" : "unknown type"),
|
|
|
|
|
hd->current, hd->used);
|
|
|
|
|
|
|
|
|
|
switch (hd->active[hd->current].type)
|
2011-04-29 15:07:11 +02:00
|
|
|
|
{
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_NONE:
|
|
|
|
|
BUG(); /* we should never see it here */
|
|
|
|
|
break;
|
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYRING:
|
|
|
|
|
rc = keyring_search (hd->active[hd->current].u.kr, desc,
|
2015-11-17 14:38:03 +01:00
|
|
|
|
ndesc, descindex, 1);
|
2011-04-29 15:07:11 +02:00
|
|
|
|
break;
|
2012-12-27 15:04:29 +01:00
|
|
|
|
case KEYDB_RESOURCE_TYPE_KEYBOX:
|
2015-11-17 14:38:03 +01:00
|
|
|
|
do
|
|
|
|
|
rc = keybox_search (hd->active[hd->current].u.kb, desc,
|
|
|
|
|
ndesc, KEYBOX_BLOBTYPE_PGP,
|
|
|
|
|
descindex, &hd->skipped_long_blobs);
|
|
|
|
|
while (rc == GPG_ERR_LEGACY_KEY);
|
2012-12-27 15:04:29 +01:00
|
|
|
|
break;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2015-11-16 20:55:34 +01:00
|
|
|
|
|
2024-08-09 09:31:54 +02:00
|
|
|
|
if (DBG_KEYDB)
|
2015-11-16 20:55:34 +01:00
|
|
|
|
log_debug ("%s: searched %s (resource %d of %d) => %s\n",
|
|
|
|
|
__func__,
|
|
|
|
|
hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYRING
|
|
|
|
|
? "keyring"
|
|
|
|
|
: (hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX
|
|
|
|
|
? "keybox" : "unknown type"),
|
|
|
|
|
hd->current, hd->used,
|
|
|
|
|
rc == -1 ? "EOF" : gpg_strerror (rc));
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
|
|
|
|
|
{
|
|
|
|
|
/* EOF -> switch to next resource */
|
|
|
|
|
hd->current++;
|
|
|
|
|
}
|
|
|
|
|
else if (!rc)
|
|
|
|
|
hd->found = hd->current;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
2015-08-31 09:47:58 +02:00
|
|
|
|
hd->is_reset = 0;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2013-01-08 14:44:49 +01:00
|
|
|
|
rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
|
|
|
|
|
? gpg_error (GPG_ERR_NOT_FOUND)
|
|
|
|
|
: rc);
|
|
|
|
|
|
2015-08-31 13:57:07 +02:00
|
|
|
|
keyblock_cache_clear (hd);
|
2014-10-13 14:01:29 +02:00
|
|
|
|
if (!hd->no_caching
|
|
|
|
|
&& !rc
|
2019-03-14 08:54:59 +01:00
|
|
|
|
&& ndesc == 1
|
|
|
|
|
&& fprlen
|
2015-10-29 09:52:56 +01:00
|
|
|
|
&& hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX)
|
2013-01-08 14:44:49 +01:00
|
|
|
|
{
|
2015-08-31 13:57:07 +02:00
|
|
|
|
hd->keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
|
2015-12-15 12:21:30 +01:00
|
|
|
|
hd->keyblock_cache.resource = hd->current;
|
|
|
|
|
/* The current offset is at the start of the next record. Since
|
|
|
|
|
a record is at least 1 byte, we just use offset - 1, which is
|
|
|
|
|
within the record. */
|
|
|
|
|
hd->keyblock_cache.offset
|
|
|
|
|
= keybox_offset (hd->active[hd->current].u.kb) - 1;
|
2019-03-14 08:54:59 +01:00
|
|
|
|
memcpy (hd->keyblock_cache.fpr, desc[0].u.fpr, fprlen);
|
|
|
|
|
hd->keyblock_cache.fprlen = fprlen;
|
2013-01-08 14:44:49 +01:00
|
|
|
|
}
|
|
|
|
|
|
2015-08-26 12:22:24 +02:00
|
|
|
|
if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND
|
2019-03-14 08:54:59 +01:00
|
|
|
|
&& ndesc == 1
|
|
|
|
|
&& desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
|
|
|
|
|
&& was_reset
|
2015-08-26 12:22:24 +02:00
|
|
|
|
&& !already_in_cache)
|
|
|
|
|
kid_not_found_insert (desc[0].u.kid);
|
2015-06-20 15:03:32 +02:00
|
|
|
|
|
2017-03-31 20:03:43 +02:00
|
|
|
|
if (!rc)
|
|
|
|
|
keydb_stats.found++;
|
|
|
|
|
else
|
|
|
|
|
keydb_stats.notfound++;
|
2013-01-08 14:44:49 +01:00
|
|
|
|
return rc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Return the first non-legacy key in the database.
|
|
|
|
|
*
|
|
|
|
|
* If you want the very first key in the database, you can directly
|
|
|
|
|
* call keydb_search with the search description
|
|
|
|
|
* KEYDB_SEARCH_MODE_FIRST. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2003-06-05 07:14:21 +00:00
|
|
|
|
keydb_search_first (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
2015-01-22 16:36:28 +01:00
|
|
|
|
gpg_error_t err;
|
2011-04-29 15:07:11 +02:00
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-08-31 09:22:23 +02:00
|
|
|
|
err = keydb_search_reset (hd);
|
|
|
|
|
if (err)
|
|
|
|
|
return err;
|
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
memset (&desc, 0, sizeof desc);
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_FIRST;
|
2015-11-17 14:38:03 +01:00
|
|
|
|
return keydb_search (hd, &desc, 1, NULL);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2015-01-22 16:36:28 +01:00
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
/* Return the next key (not the next matching key!).
|
|
|
|
|
*
|
|
|
|
|
* Unlike calling keydb_search with KEYDB_SEARCH_MODE_NEXT, this
|
|
|
|
|
* function silently skips legacy keys. */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2003-06-05 07:14:21 +00:00
|
|
|
|
keydb_search_next (KEYDB_HANDLE hd)
|
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2015-11-17 14:38:03 +01:00
|
|
|
|
memset (&desc, 0, sizeof desc);
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_NEXT;
|
|
|
|
|
return keydb_search (hd, &desc, 1, NULL);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
|
|
|
|
|
/* This is a convenience function for searching for keys with a long
|
|
|
|
|
* key id.
|
|
|
|
|
*
|
|
|
|
|
* Note: this function resumes searching where the last search left
|
|
|
|
|
* off. If you want to search the whole database, then you need to
|
|
|
|
|
* first call keydb_search_reset(). */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2003-06-05 07:14:21 +00:00
|
|
|
|
keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
|
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
memset (&desc, 0, sizeof desc);
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_LONG_KID;
|
|
|
|
|
desc.u.kid[0] = kid[0];
|
|
|
|
|
desc.u.kid[1] = kid[1];
|
2013-01-08 09:43:21 +01:00
|
|
|
|
return keydb_search (hd, &desc, 1, NULL);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|
|
|
|
|
|
2016-01-11 11:41:49 +01:00
|
|
|
|
|
|
|
|
|
/* This is a convenience function for searching for keys with a long
|
|
|
|
|
* (20 byte) fingerprint.
|
|
|
|
|
*
|
|
|
|
|
* Note: this function resumes searching where the last search left
|
|
|
|
|
* off. If you want to search the whole database, then you need to
|
|
|
|
|
* first call keydb_search_reset(). */
|
2011-04-29 15:07:11 +02:00
|
|
|
|
gpg_error_t
|
2019-03-14 08:54:59 +01:00
|
|
|
|
keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr, size_t fprlen)
|
2003-06-05 07:14:21 +00:00
|
|
|
|
{
|
2011-04-29 15:07:11 +02:00
|
|
|
|
KEYDB_SEARCH_DESC desc;
|
2003-06-05 07:14:21 +00:00
|
|
|
|
|
2011-04-29 15:07:11 +02:00
|
|
|
|
memset (&desc, 0, sizeof desc);
|
|
|
|
|
desc.mode = KEYDB_SEARCH_MODE_FPR;
|
2019-03-14 08:54:59 +01:00
|
|
|
|
memcpy (desc.u.fpr, fpr, fprlen);
|
|
|
|
|
desc.fprlen = fprlen;
|
2015-11-17 14:38:03 +01:00
|
|
|
|
return keydb_search (hd, &desc, 1, NULL);
|
2003-06-05 07:14:21 +00:00
|
|
|
|
}
|