aboutsummaryrefslogtreecommitdiffstats
path: root/g10/keydb.c
diff options
context:
space:
mode:
Diffstat (limited to 'g10/keydb.c')
-rw-r--r--g10/keydb.c705
1 files changed, 630 insertions, 75 deletions
diff --git a/g10/keydb.c b/g10/keydb.c
index 75c036cf5..79ab5afc6 100644
--- a/g10/keydb.c
+++ b/g10/keydb.c
@@ -1,6 +1,6 @@
/* keydb.c - key database dispatcher
* Copyright (C) 2001, 2002, 2003, 2004, 2005,
- * 2008, 2009, 2011 Free Software Foundation, Inc.
+ * 2008, 2009, 2011, 2013 Free Software Foundation, Inc.
*
* This file is part of GnuPG.
*
@@ -34,6 +34,7 @@
#include "main.h" /*try_make_homedir ()*/
#include "packet.h"
#include "keyring.h"
+#include "../kbx/keybox.h"
#include "keydb.h"
#include "i18n.h"
@@ -42,7 +43,8 @@ static int active_handles;
typedef enum
{
KEYDB_RESOURCE_TYPE_NONE = 0,
- KEYDB_RESOURCE_TYPE_KEYRING
+ KEYDB_RESOURCE_TYPE_KEYRING,
+ KEYDB_RESOURCE_TYPE_KEYBOX
} KeydbResourceType;
#define MAX_KEYDB_RESOURCES 40
@@ -51,6 +53,7 @@ struct resource_item
KeydbResourceType type;
union {
KEYRING_HANDLE kr;
+ KEYBOX_HANDLE kb;
} u;
void *token;
};
@@ -69,16 +72,48 @@ struct keydb_handle
};
+/* This is a simple cache used to return the last result of a
+ successful long kid search. This works only for keybox resources
+ because (due to lack of a copy_keyblock function) we need to store
+ an image of the keyblock which is fortunately instantly available
+ for keyboxes. */
+enum keyblock_cache_states {
+ KEYBLOCK_CACHE_EMPTY,
+ KEYBLOCK_CACHE_PREPARED,
+ KEYBLOCK_CACHE_FILLED
+};
+
+struct {
+ enum keyblock_cache_states state;
+ u32 kid[2];
+ iobuf_t iobuf; /* Image of the keyblock. */
+ u32 *sigstatus;
+ int pk_no;
+ int uid_no;
+} keyblock_cache;
+
+
static int lock_all (KEYDB_HANDLE hd);
static void unlock_all (KEYDB_HANDLE hd);
-/* Handle the creation of a keyring if it does not yet exist. Take
- into acount that other processes might have the keyring already
- locked. This lock check does not work if the directory itself is
- not yet available. */
+static void
+keyblock_cache_clear (void)
+{
+ keyblock_cache.state = KEYBLOCK_CACHE_EMPTY;
+ xfree (keyblock_cache.sigstatus);
+ keyblock_cache.sigstatus = NULL;
+ iobuf_close (keyblock_cache.iobuf);
+ keyblock_cache.iobuf = NULL;
+}
+
+
+/* Handle the creation of a keyring or a keybox if it does not yet
+ exist. Take into acount that other processes might have the
+ keyring/keybox already locked. This lock check does not work if
+ the directory itself is not yet available. */
static int
-maybe_create_keyring (char *filename, int force)
+maybe_create_keyring_or_box (char *filename, int is_box, int force)
{
dotlock_t lockhd = NULL;
IOBUF iobuf;
@@ -139,29 +174,31 @@ maybe_create_keyring (char *filename, int force)
lockhd = dotlock_create (filename, 0);
if (!lockhd)
{
+ rc = gpg_error_from_syserror ();
/* A reason for this to fail is that the directory is not
writable. However, this whole locking stuff does not make
sense if this is the case. An empty non-writable directory
with no keyring is not really useful at all. */
if (opt.verbose)
- log_info ("can't allocate lock for '%s'\n", filename );
+ log_info ("can't allocate lock for '%s': %s\n",
+ filename, gpg_strerror (rc));
if (!force)
return gpg_error (GPG_ERR_ENOENT);
else
- return gpg_error (GPG_ERR_GENERAL);
+ return rc;
}
if ( dotlock_take (lockhd, -1) )
{
+ rc = gpg_error_from_syserror ();
/* This is something bad. Probably a stale lockfile. */
- log_info ("can't lock '%s'\n", filename );
- rc = G10ERR_GENERAL;
+ log_info ("can't lock '%s': %s\n", filename, gpg_strerror (rc));
goto leave;
}
/* Now the real test while we are locked. */
- if (!access(filename, F_OK))
+ if (!access (filename, F_OK))
{
rc = 0; /* Okay, we may access the file now. */
goto leave;
@@ -180,17 +217,51 @@ maybe_create_keyring (char *filename, int force)
if (!iobuf)
{
rc = gpg_error_from_syserror ();
- log_error ( _("error creating keyring '%s': %s\n"),
- filename, strerror(errno));
+ 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)
- log_info (_("keyring '%s' created\n"), filename);
-
iobuf_close (iobuf);
/* Must invalidate that ugly cache */
iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, filename);
+
+ /* Make sure that at least one record is in a new keybox file, so
+ that the detection magic will work the next time it is used. */
+ if (is_box)
+ {
+ FILE *fp = fopen (filename, "w");
+ if (!fp)
+ rc = gpg_error_from_syserror ();
+ else
+ {
+ rc = _keybox_write_header_blob (fp);
+ fclose (fp);
+ }
+ if (rc)
+ {
+ if (is_box)
+ log_error (_("error creating keybox '%s': %s\n"),
+ filename, gpg_strerror (rc));
+ else
+ log_error (_("error creating keyring '%s': %s\n"),
+ filename, gpg_strerror (rc));
+ goto leave;
+ }
+ }
+
+ if (!opt.quiet)
+ {
+ if (is_box)
+ log_info (_("keybox '%s' created\n"), filename);
+ else
+ log_info (_("keyring '%s' created\n"), filename);
+ }
+
rc = 0;
leave:
@@ -204,51 +275,49 @@ maybe_create_keyring (char *filename, int force)
/*
- * Register a resource (which currently may only be a keyring file).
- * The first keyring which is added by this function is
- * created if it does not exist.
- * Note: this function may be called before secure memory is
- * available.
- * Flag 1 - Force.
- * Flag 2 - Mark resource as primary.
- * Flag 4 - This is a default resources.
- * Flag 8 - Open as read-only.
+ * Register a resource (keyring or aeybox). The first keyring or
+ * keybox which is added by this function is created if it does not
+ * exist. FLAGS are a combination of the KEYDB_RESOURCE_FLAG_
+ * constants as defined in keydb.h.
*/
gpg_error_t
-keydb_add_resource (const char *url, int flags)
+keydb_add_resource (const char *url, unsigned int flags)
{
- static int any_public;
+ static int any_registered;
const char *resname = url;
char *filename = NULL;
- int force = (flags&1);
- int read_only = !!(flags&8);
+ int create;
+ int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY);
int rc = 0;
KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
void *token;
- if (read_only)
- force = 0;
+ /* Create the resource if it is the first registered one. */
+ create = (!read_only && !any_registered);
/* Do we have an URL?
- * gnupg-ring:filename := this is a plain keyring
+ * gnupg-ring:filename := this is a plain keyring.
+ * gnupg-kbx:filename := this is a keybox file.
* filename := See what is is, but create as plain keyring.
*/
- if (strlen (resname) > 11)
+ if (strlen (resname) > 11 && !strncmp( resname, "gnupg-ring:", 11) )
{
- if (!strncmp( resname, "gnupg-ring:", 11) )
- {
- rt = KEYDB_RESOURCE_TYPE_KEYRING;
- resname += 11;
- }
+ rt = KEYDB_RESOURCE_TYPE_KEYRING;
+ resname += 11;
+ }
+ else if (strlen (resname) > 10 && !strncmp (resname, "gnupg-kbx:", 10) )
+ {
+ rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+ resname += 10;
+ }
#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
- else if (strchr (resname, ':'))
- {
- log_error ("invalid key resource URL '%s'\n", url );
- rc = gpg_error (GPG_ERR_GENERAL);
- goto leave;
- }
-#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
+ else if (strchr (resname, ':'))
+ {
+ log_error ("invalid key resource URL '%s'\n", url );
+ rc = gpg_error (GPG_ERR_GENERAL);
+ goto leave;
}
+#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
if (*resname != DIRSEP_C )
{
@@ -261,9 +330,6 @@ keydb_add_resource (const char *url, int flags)
else
filename = xstrdup (resname);
- if (!force && !read_only)
- force = !any_public;
-
/* See whether we can determine the filetype. */
if (rt == KEYDB_RESOURCE_TYPE_NONE)
{
@@ -273,20 +339,25 @@ keydb_add_resource (const char *url, int flags)
{
u32 magic;
- if (fread( &magic, 4, 1, fp) == 1 )
+ if (fread (&magic, 4, 1, fp) == 1 )
{
if (magic == 0x13579ace || magic == 0xce9a5713)
; /* GDBM magic - not anymore supported. */
+ else if (fread (&magic, 4, 1, fp) == 1
+ && !memcmp (&magic, "\x01", 1)
+ && fread (&magic, 4, 1, fp) == 1
+ && !memcmp (&magic, "KBXf", 4))
+ rt = KEYDB_RESOURCE_TYPE_KEYBOX;
else
rt = KEYDB_RESOURCE_TYPE_KEYRING;
}
else /* Maybe empty: assume keyring. */
rt = KEYDB_RESOURCE_TYPE_KEYRING;
- fclose( fp );
+ fclose (fp);
}
- else /* No file yet: create keyring. */
- rt = KEYDB_RESOURCE_TYPE_KEYRING;
+ else /* No file yet: create keybox. */
+ rt = KEYDB_RESOURCE_TYPE_KEYBOX;
}
switch (rt)
@@ -297,7 +368,7 @@ keydb_add_resource (const char *url, int flags)
goto leave;
case KEYDB_RESOURCE_TYPE_KEYRING:
- rc = maybe_create_keyring (filename, force);
+ rc = maybe_create_keyring_or_box (filename, create, 0);
if (rc)
goto leave;
@@ -307,7 +378,7 @@ keydb_add_resource (const char *url, int flags)
rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
else
{
- if (flags&2)
+ if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
primary_keyring = token;
all_resources[used_resources].type = rt;
all_resources[used_resources].u.kr = NULL; /* Not used here */
@@ -320,24 +391,61 @@ keydb_add_resource (const char *url, int flags)
/* This keyring was already registered, so ignore it.
However, we can still mark it as primary even if it was
already registered. */
- if (flags&2)
+ if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
primary_keyring = token;
}
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ {
+ rc = maybe_create_keyring_or_box (filename, create, 1);
+ if (rc)
+ goto leave;
+
+ /* FIXME: How do we register a read-only keybox? */
+ token = keybox_register_file (filename, 0);
+ if (token)
+ {
+ if (used_resources >= MAX_KEYDB_RESOURCES)
+ rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+ else
+ {
+ /* if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) */
+ /* primary_keyring = token; */
+ all_resources[used_resources].type = rt;
+ all_resources[used_resources].u.kb = NULL; /* Not used here */
+ all_resources[used_resources].token = token;
+
+ /* FIXME: Do a compress run if needed and no other
+ user is currently using the keybox. */
+
+ used_resources++;
+ }
+ }
+ else
+ {
+ /* Already registered. We will mark it as the primary key
+ if requested. */
+ /* FIXME: How to do that? Change the keybox interface? */
+ /* if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) */
+ /* primary_keyring = token; */
+ }
+ }
+ break;
+
default:
log_error ("resource type of '%s' not supported\n", url);
rc = gpg_error (GPG_ERR_GENERAL);
goto leave;
}
- /* fixme: check directory permissions and print a warning */
+ /* fixme: check directory permissions and print a warning */
leave:
if (rc)
log_error (_("keyblock resource '%s': %s\n"), filename, gpg_strerror (rc));
else
- any_public = 1;
+ any_registered = 1;
xfree (filename);
return rc;
}
@@ -351,6 +459,9 @@ keydb_new (void)
KEYDB_HANDLE hd;
int i, j;
+ if (DBG_CLOCK)
+ log_clock ("keydb_new");
+
hd = xmalloc_clear (sizeof *hd);
hd->found = -1;
@@ -371,6 +482,17 @@ keydb_new (void)
}
j++;
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ hd->active[j].type = all_resources[i].type;
+ hd->active[j].token = all_resources[i].token;
+ hd->active[j].u.kb = keybox_new (all_resources[i].token, 0);
+ if (!hd->active[j].u.kb)
+ {
+ xfree (hd);
+ return NULL; /* fixme: release all previously allocated handles*/
+ }
+ j++;
+ break;
}
}
hd->used = j;
@@ -399,6 +521,9 @@ keydb_release (KEYDB_HANDLE hd)
case KEYDB_RESOURCE_TYPE_KEYRING:
keyring_release (hd->active[i].u.kr);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ keybox_release (hd->active[i].u.kb);
+ break;
}
}
@@ -438,6 +563,9 @@ keydb_get_resource_name (KEYDB_HANDLE hd)
case KEYDB_RESOURCE_TYPE_KEYRING:
s = keyring_get_resource_name (hd->active[idx].u.kr);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ s = keybox_get_resource_name (hd->active[idx].u.kb);
+ break;
}
return s? s: "";
@@ -450,6 +578,13 @@ lock_all (KEYDB_HANDLE hd)
{
int i, rc = 0;
+ /* Fixme: This locking scheme may lead to a deadlock if the resources
+ are not added in the same order by all processes. We are
+ currently only allowing one resource so it is not a problem.
+ [Oops: Who claimed the latter]
+
+ To fix this we need to use a lock file to protect lock_all. */
+
for (i=0; !rc && i < hd->used; i++)
{
switch (hd->active[i].type)
@@ -459,12 +594,15 @@ lock_all (KEYDB_HANDLE hd)
case KEYDB_RESOURCE_TYPE_KEYRING:
rc = keyring_lock (hd->active[i].u.kr, 1);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_lock (hd->active[i].u.kb, 1);
+ break;
}
}
if (rc)
{
- /* Revert the already set locks. */
+ /* Revert the already taken locks. */
for (i--; i >= 0; i--)
{
switch (hd->active[i].type)
@@ -474,6 +612,9 @@ lock_all (KEYDB_HANDLE hd)
case KEYDB_RESOURCE_TYPE_KEYRING:
keyring_lock (hd->active[i].u.kr, 0);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_lock (hd->active[i].u.kb, 0);
+ break;
}
}
}
@@ -501,12 +642,170 @@ unlock_all (KEYDB_HANDLE hd)
case KEYDB_RESOURCE_TYPE_KEYRING:
keyring_lock (hd->active[i].u.kr, 0);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ keybox_lock (hd->active[i].u.kb, 0);
+ break;
}
}
hd->locked = 0;
}
+static gpg_error_t
+parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
+ const u32 *sigstatus, kbnode_t *r_keyblock)
+{
+ gpg_error_t err;
+ PACKET *pkt;
+ kbnode_t keyblock = NULL;
+ kbnode_t node, *tail;
+ int in_cert, save_mode;
+ u32 n_sigs;
+ int pk_count, uid_count;
+
+ *r_keyblock = NULL;
+
+ pkt = xtrymalloc (sizeof *pkt);
+ if (!pkt)
+ return gpg_error_from_syserror ();
+ init_packet (pkt);
+ save_mode = set_packet_list_mode (0);
+ in_cert = 0;
+ n_sigs = 0;
+ tail = NULL;
+ pk_count = uid_count = 0;
+ while ((err = parse_packet (iobuf, pkt)) != -1)
+ {
+ if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET)
+ {
+ free_packet (pkt);
+ init_packet (pkt);
+ continue;
+ }
+ if (err)
+ {
+ log_error ("parse_keyblock_image: read error: %s\n",
+ gpg_strerror (err));
+ err = gpg_error (GPG_ERR_INV_KEYRING);
+ break;
+ }
+ if (pkt->pkttype == PKT_COMPRESSED)
+ {
+ log_error ("skipped compressed packet in keybox blob\n");
+ free_packet(pkt);
+ init_packet(pkt);
+ continue;
+ }
+ if (pkt->pkttype == PKT_RING_TRUST)
+ {
+ log_info ("skipped ring trust packet in keybox blob\n");
+ free_packet(pkt);
+ init_packet(pkt);
+ continue;
+ }
+
+ if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY)
+ {
+ log_error ("parse_keyblock_image: first packet in a keybox blob "
+ "is not a public key packet\n");
+ err = gpg_error (GPG_ERR_INV_KEYRING);
+ break;
+ }
+ if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY
+ || pkt->pkttype == PKT_SECRET_KEY))
+ {
+ log_error ("parse_keyblock_image: "
+ "multiple keyblocks in a keybox blob\n");
+ err = gpg_error (GPG_ERR_INV_KEYRING);
+ break;
+ }
+ in_cert = 1;
+
+ if (pkt->pkttype == PKT_SIGNATURE && sigstatus)
+ {
+ PKT_signature *sig = pkt->pkt.signature;
+
+ n_sigs++;
+ if (n_sigs > sigstatus[0])
+ {
+ log_error ("parse_keyblock_image: "
+ "more signatures than found in the meta data\n");
+ err = gpg_error (GPG_ERR_INV_KEYRING);
+ break;
+
+ }
+ if (sigstatus[n_sigs])
+ {
+ sig->flags.checked = 1;
+ if (sigstatus[n_sigs] == 1 )
+ ; /* missing key */
+ else if (sigstatus[n_sigs] == 2 )
+ ; /* bad signature */
+ else if (sigstatus[n_sigs] < 0x10000000)
+ ; /* bad flag */
+ else
+ {
+ sig->flags.valid = 1;
+ /* Fixme: Shall we set the expired flag here? */
+ }
+ }
+ }
+
+ node = new_kbnode (pkt);
+
+ switch (pkt->pkttype)
+ {
+ case PKT_PUBLIC_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_SECRET_KEY:
+ case PKT_SECRET_SUBKEY:
+ if (++pk_count == pk_no)
+ node->flag |= 1;
+ break;
+
+ case PKT_USER_ID:
+ if (++uid_count == uid_no)
+ node->flag |= 2;
+ break;
+
+ default:
+ break;
+ }
+
+ if (!keyblock)
+ keyblock = node;
+ else
+ *tail = node;
+ tail = &node->next;
+ pkt = xtrymalloc (sizeof *pkt);
+ if (!pkt)
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ init_packet (pkt);
+ }
+ set_packet_list_mode (save_mode);
+
+ if (err == -1 && keyblock)
+ err = 0; /* Got the entire keyblock. */
+
+ if (!err && sigstatus && n_sigs != sigstatus[0])
+ {
+ log_error ("parse_keyblock_image: signature count does not match\n");
+ err = gpg_error (GPG_ERR_INV_KEYRING);
+ }
+
+ if (err)
+ release_kbnode (keyblock);
+ else
+ *r_keyblock = keyblock;
+ free_packet (pkt);
+ xfree (pkt);
+ return err;
+}
+
+
/*
* Return the last found keyring. Caller must free it.
* The returned keyblock has the kbode flag bit 0 set for the node with
@@ -518,9 +817,24 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
{
gpg_error_t err = 0;
+ *ret_kb = NULL;
+
if (!hd)
return gpg_error (GPG_ERR_INV_ARG);
+ if (keyblock_cache.state == KEYBLOCK_CACHE_FILLED)
+ {
+ iobuf_seek (keyblock_cache.iobuf, 0);
+ err = parse_keyblock_image (keyblock_cache.iobuf,
+ keyblock_cache.pk_no,
+ keyblock_cache.uid_no,
+ keyblock_cache.sigstatus,
+ ret_kb);
+ if (err)
+ keyblock_cache_clear ();
+ return err;
+ }
+
if (hd->found < 0 || hd->found >= hd->used)
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
@@ -532,11 +846,125 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
case KEYDB_RESOURCE_TYPE_KEYRING:
err = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ {
+ iobuf_t iobuf;
+ u32 *sigstatus;
+ int pk_no, uid_no;
+
+ err = keybox_get_keyblock (hd->active[hd->found].u.kb,
+ &iobuf, &pk_no, &uid_no, &sigstatus);
+ if (!err)
+ {
+ err = parse_keyblock_image (iobuf, pk_no, uid_no, sigstatus,
+ ret_kb);
+ if (!err && keyblock_cache.state == KEYBLOCK_CACHE_PREPARED)
+ {
+ keyblock_cache.state = KEYBLOCK_CACHE_FILLED;
+ keyblock_cache.sigstatus = sigstatus;
+ keyblock_cache.iobuf = iobuf;
+ keyblock_cache.pk_no = pk_no;
+ keyblock_cache.uid_no = uid_no;
+ }
+ else
+ {
+ xfree (sigstatus);
+ iobuf_close (iobuf);
+ }
+ }
+ }
+ break;
}
+ if (keyblock_cache.state != KEYBLOCK_CACHE_FILLED)
+ keyblock_cache_clear ();
+
return err;
}
+
+/* Build a keyblock image from KEYBLOCK. Returns 0 on success and
+ only then stores a new iobuf object at R_IOBUF and a signature
+ status vecotor at R_SIGSTATUS. */
+static gpg_error_t
+build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
+{
+ gpg_error_t err;
+ iobuf_t iobuf;
+ kbnode_t kbctx, node;
+ u32 n_sigs;
+ u32 *sigstatus;
+
+ *r_iobuf = NULL;
+ *r_sigstatus = NULL;
+
+ /* Allocate a vector for the signature cache. This is an array of
+ u32 values with the first value giving the number of elements to
+ follow and each element descriping the cache status of the
+ signature. */
+ for (kbctx = NULL, n_sigs = 0; (node = walk_kbnode (keyblock, &kbctx, 0));)
+ if (node->pkt->pkttype == PKT_SIGNATURE)
+ n_sigs++;
+ sigstatus = xtrycalloc (1+n_sigs, sizeof *sigstatus);
+ if (!sigstatus)
+ return gpg_error_from_syserror ();
+
+ iobuf = iobuf_temp ();
+ for (kbctx = NULL, n_sigs = 0; (node = walk_kbnode (keyblock, &kbctx, 0));)
+ {
+ /* Make sure to use only packets valid on a keyblock. */
+ switch (node->pkt->pkttype)
+ {
+ case PKT_PUBLIC_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_SIGNATURE:
+ case PKT_USER_ID:
+ case PKT_ATTRIBUTE:
+ /* Note that we don't want the ring trust packets. They are
+ not useful. */
+ break;
+ default:
+ continue;
+ }
+
+ err = build_packet (iobuf, node->pkt);
+ if (err)
+ {
+ iobuf_close (iobuf);
+ return err;
+ }
+
+ /* Build signature status vector. */
+ if (node->pkt->pkttype == PKT_SIGNATURE)
+ {
+ PKT_signature *sig = node->pkt->pkt.signature;
+
+ n_sigs++;
+ /* Fixme: Detect tye "missing key" status. */
+ if (sig->flags.checked)
+ {
+ if (sig->flags.valid)
+ {
+ if (!sig->expiredate)
+ sigstatus[n_sigs] = 0xffffffff;
+ else if (sig->expiredate < 0x1000000)
+ sigstatus[n_sigs] = 0x10000000;
+ else
+ sigstatus[n_sigs] = sig->expiredate;
+ }
+ else
+ sigstatus[n_sigs] = 0x00000002; /* Bad signature. */
+ }
+ }
+ }
+ sigstatus[0] = n_sigs;
+
+ *r_iobuf = iobuf;
+ *r_sigstatus = sigstatus;
+ return 0;
+}
+
+
/*
* Update the current keyblock with the keyblock KB
*/
@@ -548,6 +976,8 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
if (!hd)
return gpg_error (GPG_ERR_INV_ARG);
+ keyblock_cache_clear ();
+
if (hd->found < 0 || hd->found >= hd->used)
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
@@ -566,6 +996,12 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
case KEYDB_RESOURCE_TYPE_KEYRING:
rc = keyring_update_keyblock (hd->active[hd->found].u.kr, kb);
break;
+ /* case KEYDB_RESOURCE_TYPE_KEYRING: */
+ /* rc = build_keyblock (kb, &image, &imagelen); */
+ /* if (!rc) */
+ /* rc = keybox_update_keyblock (hd->active[hd->found].u.kb, */
+ /* image, imagelen); */
+ /* break; */
}
unlock_all (hd);
@@ -579,12 +1015,14 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
gpg_error_t
keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
{
- int rc;
+ gpg_error_t err;
int idx;
if (!hd)
return gpg_error (GPG_ERR_INV_ARG);
+ keyblock_cache_clear ();
+
if (opt.dry_run)
return 0;
@@ -595,22 +1033,42 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
else
return gpg_error (GPG_ERR_GENERAL);
- rc = lock_all (hd);
- if (rc)
- return rc;
+ err = lock_all (hd);
+ if (err)
+ return err;
switch (hd->active[idx].type)
{
case KEYDB_RESOURCE_TYPE_NONE:
- rc = gpg_error (GPG_ERR_GENERAL); /* oops */
+ err = gpg_error (GPG_ERR_GENERAL); /* oops */
break;
case KEYDB_RESOURCE_TYPE_KEYRING:
- rc = keyring_insert_keyblock (hd->active[idx].u.kr, kb);
+ 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;
+ u32 *sigstatus;
+
+ err = build_keyblock_image (kb, &iobuf, &sigstatus);
+ if (!err)
+ {
+ err = keybox_insert_keyblock (hd->active[idx].u.kb,
+ iobuf_get_temp_buffer (iobuf),
+ iobuf_get_temp_length (iobuf),
+ sigstatus);
+ xfree (sigstatus);
+ iobuf_close (iobuf);
+ }
+ }
break;
}
unlock_all (hd);
- return rc;
+ return err;
}
@@ -625,6 +1083,8 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
if (!hd)
return gpg_error (GPG_ERR_INV_ARG);
+ keyblock_cache_clear ();
+
if (hd->found < 0 || hd->found >= hd->used)
return gpg_error (GPG_ERR_VALUE_NOT_FOUND);
@@ -643,6 +1103,9 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
case KEYDB_RESOURCE_TYPE_KEYRING:
rc = keyring_delete_keyblock (hd->active[hd->found].u.kr);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_delete (hd->active[hd->found].u.kb);
+ break;
}
unlock_all (hd);
@@ -700,6 +1163,10 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
if (keyring_is_writable (hd->active[hd->current].token))
return 0; /* found (hd->current is set to it) */
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ if (keybox_is_writable (hd->active[hd->current].token))
+ return 0; /* found (hd->current is set to it) */
+ break;
}
}
@@ -714,6 +1181,8 @@ keydb_rebuild_caches (int noisy)
{
int i, rc;
+ keyblock_cache_clear ();
+
for (i=0; i < used_resources; i++)
{
if (!keyring_is_writable (all_resources[i].token))
@@ -746,6 +1215,11 @@ keydb_search_reset (KEYDB_HANDLE hd)
if (!hd)
return gpg_error (GPG_ERR_INV_ARG);
+ keyblock_cache_clear ();
+
+ if (DBG_CLOCK)
+ log_clock ("keydb_search_reset");
+
hd->current = 0;
hd->found = -1;
/* Now reset all resources. */
@@ -758,12 +1232,61 @@ keydb_search_reset (KEYDB_HANDLE hd)
case KEYDB_RESOURCE_TYPE_KEYRING:
rc = keyring_search_reset (hd->active[i].u.kr);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_search_reset (hd->active[i].u.kb);
+ break;
}
}
return rc;
}
+static void
+dump_search_desc (const char *text, KEYDB_SEARCH_DESC *desc, size_t ndesc)
+{
+ int n;
+ const char *s;
+
+ for (n=0; n < ndesc; n++)
+ {
+ switch (desc[n].mode)
+ {
+ case KEYDB_SEARCH_MODE_NONE: s = "none"; break;
+ case KEYDB_SEARCH_MODE_EXACT: s = "exact"; break;
+ case KEYDB_SEARCH_MODE_SUBSTR: s = "substr"; break;
+ case KEYDB_SEARCH_MODE_MAIL: s = "mail"; break;
+ case KEYDB_SEARCH_MODE_MAILSUB: s = "mailsub"; break;
+ case KEYDB_SEARCH_MODE_MAILEND: s = "mailend"; break;
+ case KEYDB_SEARCH_MODE_WORDS: s = "words"; break;
+ case KEYDB_SEARCH_MODE_SHORT_KID: s = "short_kid"; break;
+ case KEYDB_SEARCH_MODE_LONG_KID: s = "long_kid"; break;
+ case KEYDB_SEARCH_MODE_FPR16: s = "fpr16"; break;
+ case KEYDB_SEARCH_MODE_FPR20: s = "fpr20"; break;
+ case KEYDB_SEARCH_MODE_FPR: s = "fpr"; break;
+ case KEYDB_SEARCH_MODE_ISSUER: s = "issuer"; break;
+ case KEYDB_SEARCH_MODE_ISSUER_SN: s = "issuer_sn"; break;
+ case KEYDB_SEARCH_MODE_SN: s = "sn"; break;
+ case KEYDB_SEARCH_MODE_SUBJECT: s = "subject"; break;
+ case KEYDB_SEARCH_MODE_KEYGRIP: s = "keygrip"; break;
+ case KEYDB_SEARCH_MODE_FIRST: s = "first"; break;
+ case KEYDB_SEARCH_MODE_NEXT: s = "next"; break;
+ default: s = "?"; break;
+ }
+ if (!n)
+ log_debug ("%s: mode=%s", text, s);
+ else
+ log_debug ("%*s mode=%s", (int)strlen (text), "", s);
+ if (desc[n].mode == KEYDB_SEARCH_MODE_LONG_KID)
+ log_printf (" %08lX%08lX", (unsigned long)desc[n].u.kid[0],
+ (unsigned long)desc[n].u.kid[1]);
+ else if (desc[n].mode == KEYDB_SEARCH_MODE_SHORT_KID)
+ log_printf (" %08lX", (unsigned long)desc[n].u.kid[1]);
+ else if (desc[n].mode == KEYDB_SEARCH_MODE_SUBSTR)
+ log_printf (" '%s'", desc[n].u.name);
+ }
+}
+
+
/*
* Search through all keydb resources, starting at the current
* position, for a keyblock which contains one of the keys described
@@ -771,14 +1294,30 @@ keydb_search_reset (KEYDB_HANDLE hd)
* keyring was found.
*/
gpg_error_t
-keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
- size_t ndesc, size_t *descindex)
+keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+ size_t ndesc, size_t *descindex)
{
gpg_error_t rc;
if (!hd)
return gpg_error (GPG_ERR_INV_ARG);
+ if (DBG_CLOCK)
+ log_clock ("keydb_search enter");
+
+ if (DBG_CACHE)
+ dump_search_desc ("keydb_search", desc, ndesc);
+
+ if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID
+ && keyblock_cache.state == KEYBLOCK_CACHE_FILLED
+ && keyblock_cache.kid[0] == desc[0].u.kid[0]
+ && keyblock_cache.kid[1] == desc[0].u.kid[1])
+ {
+ if (DBG_CLOCK)
+ log_clock ("keydb_search leave (cached)");
+ return 0;
+ }
+
rc = -1;
while ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
&& hd->current >= 0 && hd->current < hd->used)
@@ -792,6 +1331,9 @@ keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
rc = keyring_search (hd->active[hd->current].u.kr, desc,
ndesc, descindex);
break;
+ case KEYDB_RESOURCE_TYPE_KEYBOX:
+ rc = keybox_search (hd->active[hd->current].u.kb, desc, ndesc);
+ break;
}
if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
{
@@ -802,9 +1344,22 @@ keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
hd->found = hd->current;
}
- return ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
- ? gpg_error (GPG_ERR_NOT_FOUND)
- : rc);
+ rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
+ ? gpg_error (GPG_ERR_NOT_FOUND)
+ : rc);
+
+ keyblock_cache_clear ();
+ if (!rc && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID)
+ {
+ keyblock_cache.state = KEYBLOCK_CACHE_PREPARED;
+ keyblock_cache.kid[0] = desc[0].u.kid[0];
+ keyblock_cache.kid[1] = desc[0].u.kid[1];
+ }
+
+ if (DBG_CLOCK)
+ log_clock (rc? "keydb_search leave (not found)"
+ : "keydb_search leave (found)");
+ return rc;
}
@@ -815,7 +1370,7 @@ keydb_search_first (KEYDB_HANDLE hd)
memset (&desc, 0, sizeof desc);
desc.mode = KEYDB_SEARCH_MODE_FIRST;
- return keydb_search (hd, &desc, 1);
+ return keydb_search (hd, &desc, 1, NULL);
}
gpg_error_t
@@ -825,7 +1380,7 @@ keydb_search_next (KEYDB_HANDLE hd)
memset (&desc, 0, sizeof desc);
desc.mode = KEYDB_SEARCH_MODE_NEXT;
- return keydb_search (hd, &desc, 1);
+ return keydb_search (hd, &desc, 1, NULL);
}
gpg_error_t
@@ -837,7 +1392,7 @@ keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
desc.mode = KEYDB_SEARCH_MODE_LONG_KID;
desc.u.kid[0] = kid[0];
desc.u.kid[1] = kid[1];
- return keydb_search (hd, &desc, 1);
+ return keydb_search (hd, &desc, 1, NULL);
}
gpg_error_t
@@ -848,5 +1403,5 @@ keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
memset (&desc, 0, sizeof desc);
desc.mode = KEYDB_SEARCH_MODE_FPR;
memcpy (desc.u.fpr, fpr, MAX_FINGERPRINT_LEN);
- return keydb_search (hd, &desc, 1);
+ return keydb_search (hd, &desc, 1, NULL);
}