diff options
Diffstat (limited to 'g10/keydb.c')
-rw-r--r-- | g10/keydb.c | 705 |
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); } |