diff --git a/g10/keydb.c b/g10/keydb.c index 58a14a83d..03fadfd54 100644 --- a/g10/keydb.c +++ b/g10/keydb.c @@ -1,2092 +1,2089 @@ /* keydb.c - key database dispatcher * Copyright (C) 2001-2013 Free Software Foundation, Inc. * Coyrright (C) 2001-2015 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "options.h" #include "main.h" /*try_make_homedir ()*/ #include "packet.h" #include "keyring.h" #include "../kbx/keybox.h" #include "keydb.h" #include "../common/i18n.h" static int active_handles; typedef enum { KEYDB_RESOURCE_TYPE_NONE = 0, KEYDB_RESOURCE_TYPE_KEYRING, KEYDB_RESOURCE_TYPE_KEYBOX } KeydbResourceType; #define MAX_KEYDB_RESOURCES 40 struct resource_item { KeydbResourceType type; union { KEYRING_HANDLE kr; KEYBOX_HANDLE kb; } u; void *token; }; static struct resource_item all_resources[MAX_KEYDB_RESOURCES]; static int used_resources; /* A pointer used to check for the primary key database by comparing to the struct resource_item's TOKEN. */ static void *primary_keydb; /* Whether we have successfully registered any resource. */ static int any_registered; /* This is a simple cache used to return the last result of a successful fingerprint search. This works only for keybox resources because (due to lack of a copy_keyblock function) we need to store an image of the keyblock which is fortunately instantly available for keyboxes. */ enum keyblock_cache_states { KEYBLOCK_CACHE_EMPTY, KEYBLOCK_CACHE_PREPARED, KEYBLOCK_CACHE_FILLED }; struct keyblock_cache { enum keyblock_cache_states state; byte fpr[MAX_FINGERPRINT_LEN]; iobuf_t iobuf; /* Image of the keyblock. */ int pk_no; int uid_no; /* Offset of the record in the keybox. */ int resource; off_t offset; }; struct keydb_handle { /* When we locked all of the resources in ACTIVE (using keyring_lock / keybox_lock, as appropriate). */ int locked; /* If this flag is set a lock will only be released by * keydb_release. */ int keep_lock; /* The index into ACTIVE of the resources in which the last search result was found. Initially -1. */ int found; /* Initially -1 (invalid). This is used to save a search result and later restore it as the selected result. */ int saved_found; /* The number of skipped long blobs since the last search (keydb_search_reset). */ unsigned long skipped_long_blobs; /* If set, this disables the use of the keyblock cache. */ int no_caching; /* Whether the next search will be from the beginning of the database (and thus consider all records). */ int is_reset; /* The "file position." In our case, this is index of the current resource in ACTIVE. */ int current; /* The number of resources in ACTIVE. */ int used; /* Cache of the last found and parsed key block (only used for keyboxes, not keyrings). */ struct keyblock_cache keyblock_cache; /* Copy of ALL_RESOURCES when keydb_new is called. */ struct resource_item active[MAX_KEYDB_RESOURCES]; }; /* Looking up keys is expensive. To hide the cost, we cache whether keys exist in the key database. Then, if we know a key does not exist, we don't have to spend time looking it up. This particularly helps the --list-sigs and --check-sigs commands. The cache stores the results in a hash using separate chaining. Concretely: we use the LSB of the keyid to index the hash table and each bucket consists of a linked list of entries. An entry consists of the 64-bit key id. If a key id is not in the cache, then we don't know whether it is in the DB or not. To simplify the cache consistency protocol, we simply flush the whole cache whenever a key is inserted or updated. */ #define KID_NOT_FOUND_CACHE_BUCKETS 256 static struct kid_not_found_cache_bucket * kid_not_found_cache[KID_NOT_FOUND_CACHE_BUCKETS]; struct kid_not_found_cache_bucket { struct kid_not_found_cache_bucket *next; u32 kid[2]; }; struct { unsigned int count; /* The current number of entries in the hash table. */ unsigned int peak; /* The peak of COUNT. */ unsigned int flushes; /* The number of flushes. */ } kid_not_found_stats; struct { unsigned int handles; /* Number of handles created. */ unsigned int locks; /* Number of locks taken. */ unsigned int parse_keyblocks; /* Number of parse_keyblock_image calls. */ unsigned int get_keyblocks; /* Number of keydb_get_keyblock calls. */ unsigned int build_keyblocks; /* Number of build_keyblock_image calls. */ unsigned int update_keyblocks;/* Number of update_keyblock calls. */ unsigned int insert_keyblocks;/* Number of update_keyblock calls. */ unsigned int delete_keyblocks;/* Number of delete_keyblock calls. */ unsigned int search_resets; /* Number of keydb_search_reset calls. */ unsigned int found; /* Number of successful keydb_search calls. */ unsigned int found_cached; /* Ditto but from the cache. */ unsigned int notfound; /* Number of failed keydb_search calls. */ unsigned int notfound_cached; /* Ditto but from the cache. */ } keydb_stats; static int lock_all (KEYDB_HANDLE hd); static void unlock_all (KEYDB_HANDLE hd); /* Check whether the keyid KID is in key id is definitely not in the database. Returns: 0 - Indeterminate: the key id is not in the cache; we don't know whether the key is in the database or not. If you want a definitive answer, you'll need to perform a lookup. 1 - There is definitely no key with this key id in the database. We searched for a key with this key id previously, but we didn't find it in the database. */ static int kid_not_found_p (u32 *kid) { struct kid_not_found_cache_bucket *k; for (k = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) { if (DBG_CACHE) log_debug ("keydb: kid_not_found_p (%08lx%08lx) => not in DB\n", (ulong)kid[0], (ulong)kid[1]); return 1; } if (DBG_CACHE) log_debug ("keydb: kid_not_found_p (%08lx%08lx) => indeterminate\n", (ulong)kid[0], (ulong)kid[1]); return 0; } /* Insert the keyid KID into the kid_not_found_cache. FOUND is whether the key is in the key database or not. Note this function does not check whether the key id is already in the cache. As such, kid_not_found_p() should be called first. */ static void kid_not_found_insert (u32 *kid) { struct kid_not_found_cache_bucket *k; if (DBG_CACHE) log_debug ("keydb: kid_not_found_insert (%08lx%08lx)\n", (ulong)kid[0], (ulong)kid[1]); k = xmalloc (sizeof *k); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->next = kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS]; kid_not_found_cache[kid[0] % KID_NOT_FOUND_CACHE_BUCKETS] = k; kid_not_found_stats.count++; } /* Flush the kid not found cache. */ static void kid_not_found_flush (void) { struct kid_not_found_cache_bucket *k, *knext; int i; if (DBG_CACHE) log_debug ("keydb: kid_not_found_flush\n"); if (!kid_not_found_stats.count) return; for (i=0; i < DIM(kid_not_found_cache); i++) { for (k = kid_not_found_cache[i]; k; k = knext) { knext = k->next; xfree (k); } kid_not_found_cache[i] = NULL; } if (kid_not_found_stats.count > kid_not_found_stats.peak) kid_not_found_stats.peak = kid_not_found_stats.count; kid_not_found_stats.count = 0; kid_not_found_stats.flushes++; } static void keyblock_cache_clear (struct keydb_handle *hd) { hd->keyblock_cache.state = KEYBLOCK_CACHE_EMPTY; iobuf_close (hd->keyblock_cache.iobuf); hd->keyblock_cache.iobuf = NULL; hd->keyblock_cache.resource = -1; hd->keyblock_cache.offset = -1; } /* Handle the creation of a keyring or a keybox if it does not yet exist. Take into account that other processes might have the keyring/keybox already locked. This lock check does not work if the directory itself is not yet available. If IS_BOX is true the filename is expected to refer to a keybox. If FORCE_CREATE is true the keyring or keybox will be created. Return 0 if it is okay to access the specified file. */ static gpg_error_t maybe_create_keyring_or_box (char *filename, int is_box, int force_create) { dotlock_t lockhd = NULL; IOBUF iobuf; int rc; mode_t oldmask; char *last_slash_in_filename; char *bak_fname = NULL; char *tmp_fname = NULL; int save_slash; /* A quick test whether the filename already exists. */ if (!access (filename, F_OK)) return !access (filename, R_OK)? 0 : gpg_error (GPG_ERR_EACCES); /* If we don't want to create a new file at all, there is no need to go any further - bail out right here. */ if (!force_create) return gpg_error (GPG_ERR_ENOENT); /* First of all we try to create the home directory. Note, that we don't do any locking here because any sane application of gpg would create the home directory by itself and not rely on gpg's tricky auto-creation which is anyway only done for certain home directory name pattern. */ last_slash_in_filename = strrchr (filename, DIRSEP_C); #if HAVE_W32_SYSTEM { /* Windows may either have a slash or a backslash. Take care of it. */ char *p = strrchr (filename, '/'); if (!last_slash_in_filename || p > last_slash_in_filename) last_slash_in_filename = p; } #endif /*HAVE_W32_SYSTEM*/ if (!last_slash_in_filename) return gpg_error (GPG_ERR_ENOENT); /* No slash at all - should not happen though. */ save_slash = *last_slash_in_filename; *last_slash_in_filename = 0; if (access(filename, F_OK)) { static int tried; if (!tried) { tried = 1; try_make_homedir (filename); } if (access (filename, F_OK)) { rc = gpg_error_from_syserror (); *last_slash_in_filename = save_slash; goto leave; } } *last_slash_in_filename = save_slash; /* To avoid races with other instances of gpg trying to create or update the keyring (it is removed during an update for a short time), we do the next stuff in a locked state. */ lockhd = dotlock_create (filename, 0); if (!lockhd) { rc = gpg_error_from_syserror (); /* A reason for this to fail is that the directory is not writable. However, this whole locking stuff does not make sense if this is the case. An empty non-writable directory with no keyring is not really useful at all. */ if (opt.verbose) log_info ("can't allocate lock for '%s': %s\n", filename, gpg_strerror (rc)); if (!force_create) return gpg_error (GPG_ERR_ENOENT); /* Won't happen. */ else return rc; } if ( dotlock_take (lockhd, -1) ) { rc = gpg_error_from_syserror (); /* This is something bad. Probably a stale lockfile. */ log_info ("can't lock '%s': %s\n", filename, gpg_strerror (rc)); goto leave; } /* Now the real test while we are locked. */ /* Gpg either uses pubring.gpg or pubring.kbx and thus different * lock files. Now, when one gpg process is updating a pubring.gpg * and thus holding the corresponding lock, a second gpg process may * get to here at the time between the two rename operation used by * the first process to update pubring.gpg. The lock taken above * may not protect the second process if it tries to create a * pubring.kbx file which would be protected by a different lock * file. * * We can detect this case by checking that the two temporary files * used by the update code exist at the same time. In that case we * do not create a new file but act as if FORCE_CREATE has not been * given. Obviously there is a race between our two checks but the * worst thing is that we won't create a new file, which is better * than to accidentally creating one. */ rc = keybox_tmp_names (filename, is_box, &bak_fname, &tmp_fname); if (rc) goto leave; if (!access (filename, F_OK)) { rc = 0; /* Okay, we may access the file now. */ goto leave; } if (!access (bak_fname, F_OK) && !access (tmp_fname, F_OK)) { /* Very likely another process is updating a pubring.gpg and we should not create a pubring.kbx. */ rc = gpg_error (GPG_ERR_ENOENT); goto leave; } /* The file does not yet exist, create it now. */ oldmask = umask (077); if (is_secured_filename (filename)) { iobuf = NULL; gpg_err_set_errno (EPERM); } else iobuf = iobuf_create (filename, 0); umask (oldmask); if (!iobuf) { rc = gpg_error_from_syserror (); if (is_box) log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); else log_error (_("error creating keyring '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } iobuf_close (iobuf); /* Must invalidate that ugly cache */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, filename); /* Make sure that at least one record is in a new keybox file, so that the detection magic will work the next time it is used. */ if (is_box) { FILE *fp = fopen (filename, "wb"); if (!fp) rc = gpg_error_from_syserror (); else { rc = _keybox_write_header_blob (fp, 1); fclose (fp); } if (rc) { if (is_box) log_error (_("error creating keybox '%s': %s\n"), filename, gpg_strerror (rc)); else log_error (_("error creating keyring '%s': %s\n"), filename, gpg_strerror (rc)); goto leave; } } if (!opt.quiet) { if (is_box) log_info (_("keybox '%s' created\n"), filename); else log_info (_("keyring '%s' created\n"), filename); } rc = 0; leave: if (lockhd) { dotlock_release (lockhd); dotlock_destroy (lockhd); } xfree (bak_fname); xfree (tmp_fname); return rc; } /* Helper for keydb_add_resource. Opens FILENAME to figure out the resource type. Returns the specified file's likely type. If the file does not exist, returns KEYDB_RESOURCE_TYPE_NONE and sets *R_FOUND to 0. Otherwise, tries to figure out the file's type. This is either KEYDB_RESOURCE_TYPE_KEYBOX, KEYDB_RESOURCE_TYPE_KEYRING or KEYDB_RESOURCE_TYPE_KEYNONE. If the file is a keybox and it has the OpenPGP flag set, then R_OPENPGP is also set. */ static KeydbResourceType rt_from_file (const char *filename, int *r_found, int *r_openpgp) { u32 magic; unsigned char verbuf[4]; FILE *fp; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; *r_found = *r_openpgp = 0; fp = fopen (filename, "rb"); if (fp) { *r_found = 1; if (fread (&magic, 4, 1, fp) == 1 ) { if (magic == 0x13579ace || magic == 0xce9a5713) ; /* GDBM magic - not anymore supported. */ else if (fread (&verbuf, 4, 1, fp) == 1 && verbuf[0] == 1 && fread (&magic, 4, 1, fp) == 1 && !memcmp (&magic, "KBXf", 4)) { if ((verbuf[3] & 0x02)) *r_openpgp = 1; rt = KEYDB_RESOURCE_TYPE_KEYBOX; } else rt = KEYDB_RESOURCE_TYPE_KEYRING; } else /* Maybe empty: assume keyring. */ rt = KEYDB_RESOURCE_TYPE_KEYRING; fclose (fp); } return rt; } char * keydb_search_desc_dump (struct keydb_search_desc *desc) { char b[MAX_FORMATTED_FINGERPRINT_LEN + 1]; char fpr[2 * MAX_FINGERPRINT_LEN + 1]; switch (desc->mode) { case KEYDB_SEARCH_MODE_EXACT: return xasprintf ("EXACT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SUBSTR: return xasprintf ("SUBSTR: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAIL: return xasprintf ("MAIL: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILSUB: return xasprintf ("MAILSUB: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_MAILEND: return xasprintf ("MAILEND: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_WORDS: return xasprintf ("WORDS: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_SHORT_KID: return xasprintf ("SHORT_KID: '%s'", format_keyid (desc->u.kid, KF_SHORT, b, sizeof (b))); case KEYDB_SEARCH_MODE_LONG_KID: return xasprintf ("LONG_KID: '%s'", format_keyid (desc->u.kid, KF_LONG, b, sizeof (b))); case KEYDB_SEARCH_MODE_FPR16: bin2hex (desc->u.fpr, 16, fpr); return xasprintf ("FPR16: '%s'", format_hexfingerprint (fpr, b, sizeof (b))); case KEYDB_SEARCH_MODE_FPR20: bin2hex (desc->u.fpr, 20, fpr); return xasprintf ("FPR20: '%s'", format_hexfingerprint (fpr, b, sizeof (b))); case KEYDB_SEARCH_MODE_FPR: bin2hex (desc->u.fpr, 20, fpr); return xasprintf ("FPR: '%s'", format_hexfingerprint (fpr, b, sizeof (b))); case KEYDB_SEARCH_MODE_ISSUER: return xasprintf ("ISSUER: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_ISSUER_SN: return xasprintf ("ISSUER_SN: '%*s'", (int) (desc->snlen == -1 ? strlen (desc->sn) : desc->snlen), desc->sn); case KEYDB_SEARCH_MODE_SN: return xasprintf ("SN: '%*s'", (int) (desc->snlen == -1 ? strlen (desc->sn) : desc->snlen), desc->sn); case KEYDB_SEARCH_MODE_SUBJECT: return xasprintf ("SUBJECT: '%s'", desc->u.name); case KEYDB_SEARCH_MODE_KEYGRIP: return xasprintf ("KEYGRIP: %s", desc->u.grip); case KEYDB_SEARCH_MODE_FIRST: return xasprintf ("FIRST"); case KEYDB_SEARCH_MODE_NEXT: return xasprintf ("NEXT"); default: return xasprintf ("Bad search mode (%d)", desc->mode); } } /* Register a resource (keyring or keybox). The first keyring or * keybox that is added using this function is created if it does not * already exist and the KEYDB_RESOURCE_FLAG_READONLY is not set. * * FLAGS are a combination of the KEYDB_RESOURCE_FLAG_* constants. * * URL must have the following form: * * gnupg-ring:filename = plain keyring * gnupg-kbx:filename = keybox file * filename = check file's type (create as a plain keyring) * * Note: on systems with drive letters (Windows) invalid URLs (i.e., * those with an unrecognized part before the ':' such as "c:\...") * will silently be treated as bare filenames. On other systems, such * URLs will cause this function to return GPG_ERR_GENERAL. * * If KEYDB_RESOURCE_FLAG_DEFAULT is set, the resource is a keyring * and the file ends in ".gpg", then this function also checks if a * file with the same name, but the extension ".kbx" exists, is a * keybox and the OpenPGP flag is set. If so, this function opens * that resource instead. * * If the file is not found, KEYDB_RESOURCE_FLAG_GPGVDEF is set and * the URL ends in ".kbx", then this function will try opening the * same URL, but with the extension ".gpg". If that file is a keybox * with the OpenPGP flag set or it is a keyring, then we use that * instead. * * If the file is not found, KEYDB_RESOURCE_FLAG_DEFAULT is set, the * file should be created and the file's extension is ".gpg" then we * replace the extension with ".kbx". * * If the KEYDB_RESOURCE_FLAG_PRIMARY is set and the resource is a * keyring (not a keybox), then this resource is considered the * primary resource. This is used by keydb_locate_writable(). If * another primary keyring is set, then that keyring is considered the * primary. * * If KEYDB_RESOURCE_FLAG_READONLY is set and the resource is a * keyring (not a keybox), then the keyring is marked as read only and * operations just as keyring_insert_keyblock will return * GPG_ERR_ACCESS. */ gpg_error_t keydb_add_resource (const char *url, unsigned int flags) { /* The file named by the URL (i.e., without the prototype). */ const char *resname = url; char *filename = NULL; int create; int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY); int is_default = !!(flags&KEYDB_RESOURCE_FLAG_DEFAULT); int is_gpgvdef = !!(flags&KEYDB_RESOURCE_FLAG_GPGVDEF); gpg_error_t err = 0; KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE; void *token; /* Create the resource if it is the first registered one. */ create = (!read_only && !any_registered); if (strlen (resname) > 11 && !strncmp( resname, "gnupg-ring:", 11) ) { rt = KEYDB_RESOURCE_TYPE_KEYRING; resname += 11; } else if (strlen (resname) > 10 && !strncmp (resname, "gnupg-kbx:", 10) ) { rt = KEYDB_RESOURCE_TYPE_KEYBOX; resname += 10; } #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__) else if (strchr (resname, ':')) { log_error ("invalid key resource URL '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; } #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */ if (*resname != DIRSEP_C #ifdef HAVE_W32_SYSTEM && *resname != '/' /* Fixme: does not handle drive letters. */ #endif ) { /* Do tilde expansion etc. */ if (strchr (resname, DIRSEP_C) #ifdef HAVE_W32_SYSTEM || strchr (resname, '/') /* Windows also accepts this. */ #endif ) filename = make_filename (resname, NULL); else filename = make_filename (gnupg_homedir (), resname, NULL); } else filename = xstrdup (resname); /* See whether we can determine the filetype. */ if (rt == KEYDB_RESOURCE_TYPE_NONE) { int found, openpgp_flag; int pass = 0; size_t filenamelen; check_again: filenamelen = strlen (filename); rt = rt_from_file (filename, &found, &openpgp_flag); if (found) { /* The file exists and we have the resource type in RT. Now let us check whether in addition to the "pubring.gpg" a "pubring.kbx with openpgp keys exists. This is so that GPG 2.1 will use an existing "pubring.kbx" by default iff that file has been created or used by 2.1. This check is needed because after creation or use of the kbx file with 2.1 an older version of gpg may have created a new pubring.gpg for its own use. */ if (!pass && is_default && rt == KEYDB_RESOURCE_TYPE_KEYRING && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg")) { strcpy (filename+filenamelen-4, ".kbx"); if ((rt_from_file (filename, &found, &openpgp_flag) == KEYDB_RESOURCE_TYPE_KEYBOX) && found && openpgp_flag) rt = KEYDB_RESOURCE_TYPE_KEYBOX; else /* Restore filename */ strcpy (filename+filenamelen-4, ".gpg"); } } else if (!pass && is_gpgvdef && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".kbx")) { /* Not found but gpgv's default "trustedkeys.kbx" file has been requested. We did not found it so now check whether a "trustedkeys.gpg" file exists and use that instead. */ KeydbResourceType rttmp; strcpy (filename+filenamelen-4, ".gpg"); rttmp = rt_from_file (filename, &found, &openpgp_flag); if (found && ((rttmp == KEYDB_RESOURCE_TYPE_KEYBOX && openpgp_flag) || (rttmp == KEYDB_RESOURCE_TYPE_KEYRING))) rt = rttmp; else /* Restore filename */ strcpy (filename+filenamelen-4, ".kbx"); } else if (!pass && is_default && create && filenamelen > 4 && !strcmp (filename+filenamelen-4, ".gpg")) { /* The file does not exist, the default resource has been requested, the file shall be created, and the file has a ".gpg" suffix. Change the suffix to ".kbx" and try once more. This way we achieve that we open an existing ".gpg" keyring, but create a new keybox file with an ".kbx" suffix. */ strcpy (filename+filenamelen-4, ".kbx"); pass++; goto check_again; } else /* No file yet: create keybox. */ rt = KEYDB_RESOURCE_TYPE_KEYBOX; } switch (rt) { case KEYDB_RESOURCE_TYPE_NONE: log_error ("unknown type of key resource '%s'\n", url ); err = gpg_error (GPG_ERR_GENERAL); goto leave; case KEYDB_RESOURCE_TYPE_KEYRING: err = maybe_create_keyring_or_box (filename, 0, create); if (err) goto leave; if (keyring_register_filename (filename, read_only, &token)) { if (used_resources >= MAX_KEYDB_RESOURCES) err = gpg_error (GPG_ERR_RESOURCE_LIMIT); else { if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; all_resources[used_resources].type = rt; all_resources[used_resources].u.kr = NULL; /* Not used here */ all_resources[used_resources].token = token; used_resources++; } } else { /* This keyring was already registered, so ignore it. However, we can still mark it as primary even if it was already registered. */ if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; } break; case KEYDB_RESOURCE_TYPE_KEYBOX: { err = maybe_create_keyring_or_box (filename, 1, create); if (err) goto leave; err = keybox_register_file (filename, 0, &token); if (!err) { if (used_resources >= MAX_KEYDB_RESOURCES) err = gpg_error (GPG_ERR_RESOURCE_LIMIT); else { if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; all_resources[used_resources].type = rt; all_resources[used_resources].u.kb = NULL; /* Not used here */ all_resources[used_resources].token = token; /* FIXME: Do a compress run if needed and no other user is currently using the keybox. */ used_resources++; } } else if (gpg_err_code (err) == GPG_ERR_EEXIST) { /* Already registered. We will mark it as the primary key if requested. */ if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) primary_keydb = token; } } break; default: log_error ("resource type of '%s' not supported\n", url); err = gpg_error (GPG_ERR_GENERAL); goto leave; } /* fixme: check directory permissions and print a warning */ leave: if (err) { log_error (_("keyblock resource '%s': %s\n"), filename, gpg_strerror (err)); write_status_error ("add_keyblock_resource", err); } else any_registered = 1; xfree (filename); return err; } void keydb_dump_stats (void) { log_info ("keydb: handles=%u locks=%u parse=%u get=%u\n", keydb_stats.handles, keydb_stats.locks, keydb_stats.parse_keyblocks, keydb_stats.get_keyblocks); log_info (" build=%u update=%u insert=%u delete=%u\n", keydb_stats.build_keyblocks, keydb_stats.update_keyblocks, keydb_stats.insert_keyblocks, keydb_stats.delete_keyblocks); log_info (" reset=%u found=%u not=%u cache=%u not=%u\n", keydb_stats.search_resets, keydb_stats.found, keydb_stats.notfound, keydb_stats.found_cached, keydb_stats.notfound_cached); log_info ("kid_not_found_cache: count=%u peak=%u flushes=%u\n", kid_not_found_stats.count, kid_not_found_stats.peak, kid_not_found_stats.flushes); } /* Create a new database handle. A database handle is similar to a file handle: it contains a local file position. This is used when searching: subsequent searches resume where the previous search left off. To rewind the position, use keydb_search_reset(). This function returns NULL on error, sets ERRNO, and prints an error diagnostic. */ KEYDB_HANDLE keydb_new (void) { KEYDB_HANDLE hd; int i, j; int die = 0; int reterrno; if (DBG_CLOCK) log_clock ("keydb_new"); hd = xtrycalloc (1, sizeof *hd); if (!hd) goto leave; hd->found = -1; hd->saved_found = -1; hd->is_reset = 1; log_assert (used_resources <= MAX_KEYDB_RESOURCES); for (i=j=0; ! die && i < used_resources; i++) { switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYRING: hd->active[j].type = all_resources[i].type; hd->active[j].token = all_resources[i].token; hd->active[j].u.kr = keyring_new (all_resources[i].token); if (!hd->active[j].u.kr) { reterrno = errno; die = 1; } j++; break; case KEYDB_RESOURCE_TYPE_KEYBOX: hd->active[j].type = all_resources[i].type; hd->active[j].token = all_resources[i].token; hd->active[j].u.kb = keybox_new_openpgp (all_resources[i].token, 0); if (!hd->active[j].u.kb) { reterrno = errno; die = 1; } j++; break; } } hd->used = j; active_handles++; keydb_stats.handles++; if (die) { keydb_release (hd); gpg_err_set_errno (reterrno); hd = NULL; } leave: if (!hd) log_error (_("error opening key DB: %s\n"), gpg_strerror (gpg_error_from_syserror())); return hd; } void keydb_release (KEYDB_HANDLE hd) { int i; if (!hd) return; log_assert (active_handles > 0); active_handles--; hd->keep_lock = 0; unlock_all (hd); for (i=0; i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_release (hd->active[i].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_release (hd->active[i].u.kb); break; } } keyblock_cache_clear (hd); xfree (hd); } /* Take a lock on the files immediately and not only during insert or * update. This lock is released with keydb_release. */ gpg_error_t keydb_lock (KEYDB_HANDLE hd) { gpg_error_t err; if (!hd) return gpg_error (GPG_ERR_INV_ARG); err = lock_all (hd); if (!err) hd->keep_lock = 1; return err; } /* Set a flag on the handle to suppress use of cached results. This * is required for updating a keyring and for key listings. Fixme: * Using a new parameter for keydb_new might be a better solution. */ void keydb_disable_caching (KEYDB_HANDLE hd) { if (hd) hd->no_caching = 1; } /* Return the file name of the resource in which the current search * result was found or, if there is no search result, the filename of * the current resource (i.e., the resource that the file position * points to). Note: the filename is not necessarily the URL used to * open it! * * This function only returns NULL if no handle is specified, in all * other error cases an empty string is returned. */ const char * keydb_get_resource_name (KEYDB_HANDLE hd) { int idx; const char *s = NULL; if (!hd) return NULL; if ( hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if ( hd->current >= 0 && hd->current < hd->used) idx = hd->current; else idx = 0; switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: s = NULL; break; case KEYDB_RESOURCE_TYPE_KEYRING: s = keyring_get_resource_name (hd->active[idx].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: s = keybox_get_resource_name (hd->active[idx].u.kb); break; } return s? s: ""; } static int lock_all (KEYDB_HANDLE hd) { int i, rc = 0; /* Fixme: This locking scheme may lead to a deadlock if the resources are not added in the same order by all processes. We are currently only allowing one resource so it is not a problem. [Oops: Who claimed the latter] To fix this we need to use a lock file to protect lock_all. */ for (i=0; !rc && i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_lock (hd->active[i].u.kr, 1); break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_lock (hd->active[i].u.kb, 1); break; } } if (rc) { /* Revert the already taken locks. */ for (i--; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_lock (hd->active[i].u.kr, 0); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kb, 0); break; } } } else { hd->locked = 1; keydb_stats.locks++; } return rc; } static void unlock_all (KEYDB_HANDLE hd) { int i; if (!hd->locked || hd->keep_lock) return; for (i=hd->used-1; i >= 0; i--) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_lock (hd->active[i].u.kr, 0); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_lock (hd->active[i].u.kb, 0); break; } } hd->locked = 0; } /* Save the last found state and invalidate the current selection * (i.e., the entry selected by keydb_search() is invalidated and * something like keydb_get_keyblock() will return an error). This * does not change the file position. This makes it possible to do * something like: * * keydb_search (hd, ...); // Result 1. * keydb_push_found_state (hd); * keydb_search_reset (hd); * keydb_search (hd, ...); // Result 2. * keydb_pop_found_state (hd); * keydb_get_keyblock (hd, ...); // -> Result 1. * * Note: it is only possible to save a single save state at a time. * In other words, the save stack only has room for a single * instance of the state. */ void keydb_push_found_state (KEYDB_HANDLE hd) { if (!hd) return; if (hd->found < 0 || hd->found >= hd->used) { hd->saved_found = -1; return; } switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_push_found_state (hd->active[hd->found].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_push_found_state (hd->active[hd->found].u.kb); break; } hd->saved_found = hd->found; hd->found = -1; } /* Restore the previous save state. If the saved state is NULL or invalid, this is a NOP. */ void keydb_pop_found_state (KEYDB_HANDLE hd) { if (!hd) return; hd->found = hd->saved_found; hd->saved_found = -1; if (hd->found < 0 || hd->found >= hd->used) return; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: keyring_pop_found_state (hd->active[hd->found].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: keybox_pop_found_state (hd->active[hd->found].u.kb); break; } } static gpg_error_t parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no, kbnode_t *r_keyblock) { gpg_error_t err; struct parse_packet_ctx_s parsectx; PACKET *pkt; kbnode_t keyblock = NULL; kbnode_t node, *tail; int in_cert, save_mode; int pk_count, uid_count; *r_keyblock = NULL; pkt = xtrymalloc (sizeof *pkt); if (!pkt) return gpg_error_from_syserror (); init_packet (pkt); init_parse_packet (&parsectx, iobuf); save_mode = set_packet_list_mode (0); in_cert = 0; tail = NULL; pk_count = uid_count = 0; while ((err = parse_packet (&parsectx, pkt)) != -1) { if (gpg_err_code (err) == GPG_ERR_UNKNOWN_PACKET) { free_packet (pkt, &parsectx); init_packet (pkt); continue; } if (err) { log_error ("parse_keyblock_image: read error: %s\n", gpg_strerror (err)); err = gpg_error (GPG_ERR_INV_KEYRING); break; } /* Filter allowed packets. */ switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_SIGNATURE: case PKT_RING_TRUST: break; /* Allowed per RFC. */ default: - /* Note that can't allow ring trust packets here and some of - the other GPG specific packets don't make sense either. */ - log_error ("skipped packet of type %d in keybox\n", - (int)pkt->pkttype); + log_info ("skipped packet of type %d in keybox\n", (int)pkt->pkttype); free_packet(pkt, &parsectx); init_packet(pkt); continue; } /* Other sanity checks. */ if (!in_cert && pkt->pkttype != PKT_PUBLIC_KEY) { log_error ("parse_keyblock_image: first packet in a keybox blob " "is not a public key packet\n"); err = gpg_error (GPG_ERR_INV_KEYRING); break; } if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { log_error ("parse_keyblock_image: " "multiple keyblocks in a keybox blob\n"); err = gpg_error (GPG_ERR_INV_KEYRING); break; } in_cert = 1; node = new_kbnode (pkt); switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: if (++pk_count == pk_no) node->flag |= 1; break; case PKT_USER_ID: if (++uid_count == uid_no) node->flag |= 2; break; default: break; } if (!keyblock) keyblock = node; else *tail = node; tail = &node->next; pkt = xtrymalloc (sizeof *pkt); if (!pkt) { err = gpg_error_from_syserror (); break; } init_packet (pkt); } set_packet_list_mode (save_mode); if (err == -1 && keyblock) err = 0; /* Got the entire keyblock. */ if (err) release_kbnode (keyblock); else { *r_keyblock = keyblock; keydb_stats.parse_keyblocks++; } free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree (pkt); return err; } /* Return the keyblock last found by keydb_search() in *RET_KB. * * On success, the function returns 0 and the caller must free *RET_KB * using release_kbnode(). Otherwise, the function returns an error * code. * * The returned keyblock has the kbnode flag bit 0 set for the node * with the public key used to locate the keyblock or flag bit 1 set * for the user ID node. */ gpg_error_t keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb) { gpg_error_t err = 0; *ret_kb = NULL; if (!hd) return gpg_error (GPG_ERR_INV_ARG); if (DBG_CLOCK) log_clock ("keydb_get_keybock enter"); if (hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED) { err = iobuf_seek (hd->keyblock_cache.iobuf, 0); if (err) { log_error ("keydb_get_keyblock: failed to rewind iobuf for cache\n"); keyblock_cache_clear (hd); } else { err = parse_keyblock_image (hd->keyblock_cache.iobuf, hd->keyblock_cache.pk_no, hd->keyblock_cache.uid_no, ret_kb); if (err) keyblock_cache_clear (hd); if (DBG_CLOCK) log_clock (err? "keydb_get_keyblock leave (cached, failed)" : "keydb_get_keyblock leave (cached)"); return err; } } if (hd->found < 0 || hd->found >= hd->used) return gpg_error (GPG_ERR_VALUE_NOT_FOUND); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: err = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb); break; case KEYDB_RESOURCE_TYPE_KEYBOX: { iobuf_t iobuf; int pk_no, uid_no; err = keybox_get_keyblock (hd->active[hd->found].u.kb, &iobuf, &pk_no, &uid_no); if (!err) { err = parse_keyblock_image (iobuf, pk_no, uid_no, ret_kb); if (!err && hd->keyblock_cache.state == KEYBLOCK_CACHE_PREPARED) { hd->keyblock_cache.state = KEYBLOCK_CACHE_FILLED; hd->keyblock_cache.iobuf = iobuf; hd->keyblock_cache.pk_no = pk_no; hd->keyblock_cache.uid_no = uid_no; } else { iobuf_close (iobuf); } } } break; } if (hd->keyblock_cache.state != KEYBLOCK_CACHE_FILLED) keyblock_cache_clear (hd); if (!err) keydb_stats.get_keyblocks++; if (DBG_CLOCK) log_clock (err? "keydb_get_keyblock leave (failed)" : "keydb_get_keyblock leave"); return err; } /* Build a keyblock image from KEYBLOCK. Returns 0 on success and * only then stores a new iobuf object at R_IOBUF. */ static gpg_error_t build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf) { gpg_error_t err; iobuf_t iobuf; kbnode_t kbctx, node; *r_iobuf = NULL; iobuf = iobuf_temp (); for (kbctx = NULL; (node = walk_kbnode (keyblock, &kbctx, 0));) { /* Make sure to use only packets valid on a keyblock. */ switch (node->pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SIGNATURE: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_RING_TRUST: break; default: continue; } err = build_packet_and_meta (iobuf, node->pkt); if (err) { iobuf_close (iobuf); return err; } } keydb_stats.build_keyblocks++; *r_iobuf = iobuf; return 0; } /* Update the keyblock KB (i.e., extract the fingerprint and find the * corresponding keyblock in the keyring). * * This doesn't do anything if --dry-run was specified. * * Returns 0 on success. Otherwise, it returns an error code. Note: * if there isn't a keyblock in the keyring corresponding to KB, then * this function returns GPG_ERR_VALUE_NOT_FOUND. * * This function selects the matching record and modifies the current * file position to point to the record just after the selected entry. * Thus, if you do a subsequent search using HD, you should first do a * keydb_search_reset. Further, if the selected record is important, * you should use keydb_push_found_state and keydb_pop_found_state to * save and restore it. */ gpg_error_t keydb_update_keyblock (ctrl_t ctrl, KEYDB_HANDLE hd, kbnode_t kb) { gpg_error_t err; PKT_public_key *pk; KEYDB_SEARCH_DESC desc; size_t len; log_assert (kb); log_assert (kb->pkt->pkttype == PKT_PUBLIC_KEY); pk = kb->pkt->pkt.public_key; if (!hd) return gpg_error (GPG_ERR_INV_ARG); kid_not_found_flush (); keyblock_cache_clear (hd); if (opt.dry_run) return 0; err = lock_all (hd); if (err) return err; #ifdef USE_TOFU tofu_notice_key_changed (ctrl, kb); #endif memset (&desc, 0, sizeof (desc)); fingerprint_from_pk (pk, desc.u.fpr, &len); if (len == 20) desc.mode = KEYDB_SEARCH_MODE_FPR20; else log_bug ("%s: Unsupported key length: %zu\n", __func__, len); keydb_search_reset (hd); err = keydb_search (hd, &desc, 1, NULL); if (err) return gpg_error (GPG_ERR_VALUE_NOT_FOUND); log_assert (hd->found >= 0 && hd->found < hd->used); switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: err = keyring_update_keyblock (hd->active[hd->found].u.kr, kb); break; case KEYDB_RESOURCE_TYPE_KEYBOX: { iobuf_t iobuf; err = build_keyblock_image (kb, &iobuf); if (!err) { err = keybox_update_keyblock (hd->active[hd->found].u.kb, iobuf_get_temp_buffer (iobuf), iobuf_get_temp_length (iobuf)); iobuf_close (iobuf); } } break; } unlock_all (hd); if (!err) keydb_stats.update_keyblocks++; return err; } /* Insert a keyblock into one of the underlying keyrings or keyboxes. * * Be default, the keyring / keybox from which the last search result * came is used. If there was no previous search result (or * keydb_search_reset was called), then the keyring / keybox where the * next search would start is used (i.e., the current file position). * * Note: this doesn't do anything if --dry-run was specified. * * Returns 0 on success. Otherwise, it returns an error code. */ gpg_error_t keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb) { gpg_error_t err; int idx; if (!hd) return gpg_error (GPG_ERR_INV_ARG); kid_not_found_flush (); keyblock_cache_clear (hd); if (opt.dry_run) return 0; if (hd->found >= 0 && hd->found < hd->used) idx = hd->found; else if (hd->current >= 0 && hd->current < hd->used) idx = hd->current; else return gpg_error (GPG_ERR_GENERAL); err = lock_all (hd); if (err) return err; switch (hd->active[idx].type) { case KEYDB_RESOURCE_TYPE_NONE: err = gpg_error (GPG_ERR_GENERAL); /* oops */ break; case KEYDB_RESOURCE_TYPE_KEYRING: err = keyring_insert_keyblock (hd->active[idx].u.kr, kb); break; case KEYDB_RESOURCE_TYPE_KEYBOX: { /* We need to turn our kbnode_t list of packets into a proper keyblock first. This is required by the OpenPGP key parser included in the keybox code. Eventually we can change this kludge to have the caller pass the image. */ iobuf_t iobuf; err = build_keyblock_image (kb, &iobuf); if (!err) { err = keybox_insert_keyblock (hd->active[idx].u.kb, iobuf_get_temp_buffer (iobuf), iobuf_get_temp_length (iobuf)); iobuf_close (iobuf); } } break; } unlock_all (hd); if (!err) keydb_stats.insert_keyblocks++; return err; } /* Delete the currently selected keyblock. If you haven't done a * search yet on this database handle (or called keydb_search_reset), * then this will return an error. * * Returns 0 on success or an error code, if an error occurs. */ gpg_error_t keydb_delete_keyblock (KEYDB_HANDLE hd) { gpg_error_t rc; if (!hd) return gpg_error (GPG_ERR_INV_ARG); kid_not_found_flush (); keyblock_cache_clear (hd); if (hd->found < 0 || hd->found >= hd->used) return gpg_error (GPG_ERR_VALUE_NOT_FOUND); if (opt.dry_run) return 0; rc = lock_all (hd); if (rc) return rc; switch (hd->active[hd->found].type) { case KEYDB_RESOURCE_TYPE_NONE: rc = gpg_error (GPG_ERR_GENERAL); break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_delete_keyblock (hd->active[hd->found].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_delete (hd->active[hd->found].u.kb); break; } unlock_all (hd); if (!rc) keydb_stats.delete_keyblocks++; return rc; } /* A database may consists of multiple keyrings / key boxes. This * sets the "file position" to the start of the first keyring / key * box that is writable (i.e., doesn't have the read-only flag set). * * This first tries the primary keyring (the last keyring (not * keybox!) added using keydb_add_resource() and with * KEYDB_RESOURCE_FLAG_PRIMARY set). If that is not writable, then it * tries the keyrings / keyboxes in the order in which they were * added. */ gpg_error_t keydb_locate_writable (KEYDB_HANDLE hd) { gpg_error_t rc; if (!hd) return GPG_ERR_INV_ARG; rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) return rc; /* If we have a primary set, try that one first */ if (primary_keydb) { for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) { if(hd->active[hd->current].token == primary_keydb) { if(keyring_is_writable (hd->active[hd->current].token)) return 0; else break; } } rc = keydb_search_reset (hd); /* this does reset hd->current */ if (rc) return rc; } for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) { switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); break; case KEYDB_RESOURCE_TYPE_KEYRING: if (keyring_is_writable (hd->active[hd->current].token)) return 0; /* found (hd->current is set to it) */ break; case KEYDB_RESOURCE_TYPE_KEYBOX: if (keybox_is_writable (hd->active[hd->current].token)) return 0; /* found (hd->current is set to it) */ break; } } return gpg_error (GPG_ERR_NOT_FOUND); } /* Rebuild the on-disk caches of all key resources. */ void keydb_rebuild_caches (ctrl_t ctrl, int noisy) { int i, rc; for (i=0; i < used_resources; i++) { if (!keyring_is_writable (all_resources[i].token)) continue; switch (all_resources[i].type) { case KEYDB_RESOURCE_TYPE_NONE: /* ignore */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_rebuild_cache (ctrl, all_resources[i].token,noisy); if (rc) log_error (_("failed to rebuild keyring cache: %s\n"), gpg_strerror (rc)); break; case KEYDB_RESOURCE_TYPE_KEYBOX: /* N/A. */ break; } } } /* Return the number of skipped blocks (because they were to large to read from a keybox) since the last search reset. */ unsigned long keydb_get_skipped_counter (KEYDB_HANDLE hd) { return hd ? hd->skipped_long_blobs : 0; } /* 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). * * Returns 0 on success and an error code if an error occurred. * (Currently, this function always returns 0 if HD is valid.) */ gpg_error_t keydb_search_reset (KEYDB_HANDLE hd) { gpg_error_t rc = 0; int i; if (!hd) return gpg_error (GPG_ERR_INV_ARG); keyblock_cache_clear (hd); if (DBG_CLOCK) log_clock ("keydb_search_reset"); if (DBG_CACHE) log_debug ("keydb_search: reset (hd=%p)", hd); hd->skipped_long_blobs = 0; hd->current = 0; hd->found = -1; /* Now reset all resources. */ for (i=0; !rc && i < hd->used; i++) { switch (hd->active[i].type) { case KEYDB_RESOURCE_TYPE_NONE: break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_search_reset (hd->active[i].u.kr); break; case KEYDB_RESOURCE_TYPE_KEYBOX: rc = keybox_search_reset (hd->active[i].u.kb); break; } } hd->is_reset = 1; if (!rc) keydb_stats.search_resets++; return rc; } /* Search the database for keys matching the search description. If * the DB contains any legacy keys, these are silently ignored. * * DESC is an array of search terms with NDESC entries. The search * terms are or'd together. That is, the next entry in the DB that * matches any of the descriptions will be returned. * * Note: this function resumes searching where the last search left * off (i.e., at the current file position). If you want to search * from the start of the database, then you need to first call * keydb_search_reset(). * * If no key matches the search description, returns * GPG_ERR_NOT_FOUND. If there was a match, returns 0. If an error * occurred, returns an error code. * * The returned key is considered to be selected and the raw data can, * for instance, be returned by calling keydb_get_keyblock(). */ gpg_error_t keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t *descindex) { int i; gpg_error_t rc; int was_reset = hd->is_reset; /* If an entry is already in the cache, then don't add it again. */ int already_in_cache = 0; if (descindex) *descindex = 0; /* Make sure it is always set on return. */ if (!hd) return gpg_error (GPG_ERR_INV_ARG); if (!any_registered) { write_status_error ("keydb_search", gpg_error (GPG_ERR_KEYRING_OPEN)); return gpg_error (GPG_ERR_NOT_FOUND); } if (DBG_CLOCK) log_clock ("keydb_search enter"); if (DBG_LOOKUP) { log_debug ("%s: %zd search descriptions:\n", __func__, ndesc); for (i = 0; i < ndesc; i ++) { char *t = keydb_search_desc_dump (&desc[i]); log_debug ("%s %d: %s\n", __func__, i, t); xfree (t); } } if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID && (already_in_cache = kid_not_found_p (desc[0].u.kid)) == 1 ) { if (DBG_CLOCK) log_clock ("keydb_search leave (not found, cached)"); keydb_stats.notfound_cached++; return gpg_error (GPG_ERR_NOT_FOUND); } /* NB: If one of the exact search modes below is used in a loop to walk over all keys (with the same fingerprint) the caching must have been disabled for the handle. */ if (!hd->no_caching && ndesc == 1 && (desc[0].mode == KEYDB_SEARCH_MODE_FPR20 || desc[0].mode == KEYDB_SEARCH_MODE_FPR) && hd->keyblock_cache.state == KEYBLOCK_CACHE_FILLED && !memcmp (hd->keyblock_cache.fpr, desc[0].u.fpr, 20) /* Make sure the current file position occurs before the cached result to avoid an infinite loop. */ && (hd->current < hd->keyblock_cache.resource || (hd->current == hd->keyblock_cache.resource && (keybox_offset (hd->active[hd->current].u.kb) <= hd->keyblock_cache.offset)))) { /* (DESCINDEX is already set). */ if (DBG_CLOCK) log_clock ("keydb_search leave (cached)"); hd->current = hd->keyblock_cache.resource; /* HD->KEYBLOCK_CACHE.OFFSET is the last byte in the record. Seek just beyond that. */ keybox_seek (hd->active[hd->current].u.kb, hd->keyblock_cache.offset + 1); keydb_stats.found_cached++; return 0; } rc = -1; while ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) && hd->current >= 0 && hd->current < hd->used) { if (DBG_LOOKUP) log_debug ("%s: searching %s (resource %d of %d)\n", __func__, hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYRING ? "keyring" : (hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX ? "keybox" : "unknown type"), hd->current, hd->used); switch (hd->active[hd->current].type) { case KEYDB_RESOURCE_TYPE_NONE: BUG(); /* we should never see it here */ break; case KEYDB_RESOURCE_TYPE_KEYRING: rc = keyring_search (hd->active[hd->current].u.kr, desc, ndesc, descindex, 1); break; case KEYDB_RESOURCE_TYPE_KEYBOX: do rc = keybox_search (hd->active[hd->current].u.kb, desc, ndesc, KEYBOX_BLOBTYPE_PGP, descindex, &hd->skipped_long_blobs); while (rc == GPG_ERR_LEGACY_KEY); break; } if (DBG_LOOKUP) log_debug ("%s: searched %s (resource %d of %d) => %s\n", __func__, hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYRING ? "keyring" : (hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX ? "keybox" : "unknown type"), hd->current, hd->used, rc == -1 ? "EOF" : gpg_strerror (rc)); if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) { /* EOF -> switch to next resource */ hd->current++; } else if (!rc) hd->found = hd->current; } hd->is_reset = 0; rc = ((rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF) ? gpg_error (GPG_ERR_NOT_FOUND) : rc); keyblock_cache_clear (hd); if (!hd->no_caching && !rc && ndesc == 1 && (desc[0].mode == KEYDB_SEARCH_MODE_FPR20 || desc[0].mode == KEYDB_SEARCH_MODE_FPR) && hd->active[hd->current].type == KEYDB_RESOURCE_TYPE_KEYBOX) { hd->keyblock_cache.state = KEYBLOCK_CACHE_PREPARED; hd->keyblock_cache.resource = hd->current; /* The current offset is at the start of the next record. Since a record is at least 1 byte, we just use offset - 1, which is within the record. */ hd->keyblock_cache.offset = keybox_offset (hd->active[hd->current].u.kb) - 1; memcpy (hd->keyblock_cache.fpr, desc[0].u.fpr, 20); } if (gpg_err_code (rc) == GPG_ERR_NOT_FOUND && ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID && was_reset && !already_in_cache) kid_not_found_insert (desc[0].u.kid); if (DBG_CLOCK) log_clock (rc? "keydb_search leave (not found)" : "keydb_search leave (found)"); if (!rc) keydb_stats.found++; else keydb_stats.notfound++; return rc; } /* Return the first non-legacy key in the database. * * If you want the very first key in the database, you can directly * call keydb_search with the search description * KEYDB_SEARCH_MODE_FIRST. */ gpg_error_t keydb_search_first (KEYDB_HANDLE hd) { gpg_error_t err; KEYDB_SEARCH_DESC desc; err = keydb_search_reset (hd); if (err) return err; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; return keydb_search (hd, &desc, 1, NULL); } /* Return the next key (not the next matching key!). * * Unlike calling keydb_search with KEYDB_SEARCH_MODE_NEXT, this * function silently skips legacy keys. */ gpg_error_t keydb_search_next (KEYDB_HANDLE hd) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_NEXT; return keydb_search (hd, &desc, 1, NULL); } /* This is a convenience function for searching for keys with a long * key id. * * Note: this function resumes searching where the last search left * off. If you want to search the whole database, then you need to * first call keydb_search_reset(). */ gpg_error_t keydb_search_kid (KEYDB_HANDLE hd, u32 *kid) { KEYDB_SEARCH_DESC desc; memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_LONG_KID; desc.u.kid[0] = kid[0]; desc.u.kid[1] = kid[1]; return keydb_search (hd, &desc, 1, NULL); } /* This is a convenience function for searching for keys with a long * (20 byte) fingerprint. * * Note: this function resumes searching where the last search left * off. If you want to search the whole database, then you need to * first call keydb_search_reset(). */ gpg_error_t keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr) { KEYDB_SEARCH_DESC desc; 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, NULL); } diff --git a/g10/keyring.c b/g10/keyring.c index 50f1b824c..25ef50747 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -1,1741 +1,1741 @@ /* keyring.c - keyring file handling * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1997-2015 Werner Koch * * This file is part of GnuPG. * * GnuPG is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 3 of the License, or * (at your option) any later version. * * GnuPG is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, see . */ #include #include #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "keyring.h" #include "packet.h" #include "keydb.h" #include "options.h" #include "main.h" /*for check_key_signature()*/ #include "../common/i18n.h" #include "../kbx/keybox.h" typedef struct keyring_resource *KR_RESOURCE; struct keyring_resource { struct keyring_resource *next; int read_only; dotlock_t lockhd; int is_locked; int did_full_scan; char fname[1]; }; typedef struct keyring_resource const * CONST_KR_RESOURCE; static KR_RESOURCE kr_resources; struct keyring_handle { CONST_KR_RESOURCE resource; struct { CONST_KR_RESOURCE kr; IOBUF iobuf; int eof; int error; } current; struct { CONST_KR_RESOURCE kr; off_t offset; size_t pk_no; size_t uid_no; unsigned int n_packets; /*used for delete and update*/ } found, saved_found; struct { char *name; char *pattern; } word_match; }; /* The number of extant handles. */ static int active_handles; static int do_copy (int mode, const char *fname, KBNODE root, off_t start_offset, unsigned int n_packets ); /* We keep a cache of entries that we have entered in the DB. This includes not only public keys, but also subkeys. Note: we'd like to keep the offset of the items that are present, however, this doesn't work, because another concurrent GnuPG process could modify the keyring. */ struct key_present { struct key_present *next; u32 kid[2]; }; /* For the hash table, we use separate chaining with linked lists. This means that we have an array of N linked lists (buckets), which is indexed by KEYID[1] mod N. Elements present in the keyring will be on the list; elements not present in the keyring will not be on the list. Note: since the hash table stores both present and not present information, it cannot be used until we complete a full scan of the keyring. This is indicated by key_present_hash_ready. */ typedef struct key_present **key_present_hash_t; static key_present_hash_t key_present_hash; static int key_present_hash_ready; #define KEY_PRESENT_HASH_BUCKETS 2048 /* Allocate a new value for a key present hash table. */ static struct key_present * key_present_value_new (void) { struct key_present *k; k = xmalloc_clear (sizeof *k); return k; } /* Allocate a new key present hash table. */ static key_present_hash_t key_present_hash_new (void) { struct key_present **tbl; tbl = xmalloc_clear (KEY_PRESENT_HASH_BUCKETS * sizeof *tbl); return tbl; } /* Return whether the value described by KID if it is in the hash table. Otherwise, return NULL. */ static struct key_present * key_present_hash_lookup (key_present_hash_t tbl, u32 *kid) { struct key_present *k; for (k = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return k; return NULL; } /* Add the key to the hash table TBL if it is not already present. */ static void key_present_hash_update (key_present_hash_t tbl, u32 *kid) { struct key_present *k; for (k = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; k; k = k->next) { if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return; } k = key_present_value_new (); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->next = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))] = k; } /* Add all the keys (public and subkeys) present in the keyblock to the hash TBL. */ static void key_present_hash_update_from_kb (key_present_hash_t tbl, KBNODE node) { for (; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; keyid_from_pk (node->pkt->pkt.public_key, aki); key_present_hash_update (tbl, aki); } } } /* * Register a filename for plain keyring files. ptr is set to a * pointer to be used to create a handles etc, or the already-issued * pointer if it has already been registered. The function returns 1 * if a new keyring was registered. */ int keyring_register_filename (const char *fname, int read_only, void **ptr) { KR_RESOURCE kr; if (active_handles) /* There are open handles. */ BUG (); for (kr=kr_resources; kr; kr = kr->next) { if (same_file_p (kr->fname, fname)) { /* Already registered. */ if (read_only) kr->read_only = 1; *ptr=kr; return 0; } } kr = xmalloc (sizeof *kr + strlen (fname)); strcpy (kr->fname, fname); kr->read_only = read_only; kr->lockhd = NULL; kr->is_locked = 0; kr->did_full_scan = 0; /* keep a list of all issued pointers */ kr->next = kr_resources; kr_resources = kr; /* create the offset table the first time a function here is used */ if (!key_present_hash) key_present_hash = key_present_hash_new (); *ptr=kr; return 1; } int keyring_is_writable (void *token) { KR_RESOURCE r = token; return r? (r->read_only || !access (r->fname, W_OK)) : 0; } /* Create a new handle for the resource associated with TOKEN. On error NULL is returned and ERRNO is set. The returned handle must be released using keyring_release (). */ KEYRING_HANDLE keyring_new (void *token) { KEYRING_HANDLE hd; KR_RESOURCE resource = token; log_assert (resource); hd = xtrycalloc (1, sizeof *hd); if (!hd) return hd; hd->resource = resource; active_handles++; return hd; } void keyring_release (KEYRING_HANDLE hd) { if (!hd) return; log_assert (active_handles > 0); active_handles--; xfree (hd->word_match.name); xfree (hd->word_match.pattern); iobuf_close (hd->current.iobuf); xfree (hd); } /* Save the current found state in HD for later retrieval by keybox_pop_found_state. Only one state may be saved. */ void keyring_push_found_state (KEYRING_HANDLE hd) { hd->saved_found = hd->found; hd->found.kr = NULL; } /* Restore the saved found state in HD. */ void keyring_pop_found_state (KEYRING_HANDLE hd) { hd->found = hd->saved_found; hd->saved_found.kr = NULL; } const char * keyring_get_resource_name (KEYRING_HANDLE hd) { if (!hd || !hd->resource) return NULL; return hd->resource->fname; } /* * Lock the keyring with the given handle, or unlock if YES is false. * We ignore the handle and lock all registered files. */ int keyring_lock (KEYRING_HANDLE hd, int yes) { KR_RESOURCE kr; int rc = 0; (void)hd; if (yes) { /* first make sure the lock handles are created */ for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (!kr->lockhd) { kr->lockhd = dotlock_create (kr->fname, 0); if (!kr->lockhd) { log_info ("can't allocate lock for '%s'\n", kr->fname ); rc = GPG_ERR_GENERAL; } } } if (rc) return rc; /* and now set the locks */ for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (kr->is_locked) continue; #ifdef HAVE_W32_SYSTEM /* Under Windows we need to CloseHandle the file before we * try to lock it. This is because another process might * have taken the lock and is using keybox_file_rename to * rename the base file. How if our dotlock_take below is * waiting for the lock but we have the base file still * open, keybox_file_rename will never succeed as we are * in a deadlock. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)kr->fname); #endif /*HAVE_W32_SYSTEM*/ if (dotlock_take (kr->lockhd, -1) ) { log_info ("can't lock '%s'\n", kr->fname ); rc = GPG_ERR_GENERAL; } else kr->is_locked = 1; } } if (rc || !yes) { for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (!kr->is_locked) continue; if (dotlock_release (kr->lockhd)) log_info ("can't unlock '%s'\n", kr->fname ); else kr->is_locked = 0; } } return rc; } /* * Return the last found keyblock. Caller must free it. * The returned keyblock has the kbode 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. */ int keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) { PACKET *pkt; struct parse_packet_ctx_s parsectx; int rc; KBNODE keyblock = NULL, node, lastnode; IOBUF a; int in_cert = 0; int pk_no = 0; int uid_no = 0; int save_mode; if (ret_kb) *ret_kb = NULL; if (!hd->found.kr) return -1; /* no successful search */ a = iobuf_open (hd->found.kr->fname); if (!a) { log_error(_("can't open '%s'\n"), hd->found.kr->fname); return GPG_ERR_KEYRING_OPEN; } if (iobuf_seek (a, hd->found.offset) ) { log_error ("can't seek '%s'\n", hd->found.kr->fname); iobuf_close(a); return GPG_ERR_KEYRING_OPEN; } pkt = xmalloc (sizeof *pkt); init_packet (pkt); init_parse_packet (&parsectx, a); hd->found.n_packets = 0; lastnode = NULL; save_mode = set_packet_list_mode(0); while ((rc=parse_packet (&parsectx, pkt)) != -1) { hd->found.n_packets = parsectx.n_parsed_packets; if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) { free_packet (pkt, &parsectx); init_packet (pkt); continue; } if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) { if (in_cert) /* It is not this key that is problematic, but the following key. */ { rc = 0; hd->found.n_packets --; } else /* Upper layer needs to handle this. */ { } break; } if (rc) { log_error ("keyring_get_keyblock: read error: %s\n", gpg_strerror (rc) ); rc = GPG_ERR_INV_KEYRING; break; } /* Filter allowed packets. */ switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_SIGNATURE: break; /* Allowed per RFC. */ case PKT_RING_TRUST: case PKT_OLD_COMMENT: case PKT_COMMENT: case PKT_GPG_CONTROL: break; /* Allowed by us. */ default: - log_error ("skipped packet of type %d in keyring\n", - (int)pkt->pkttype); + log_info ("skipped packet of type %d in keyring\n", + (int)pkt->pkttype); free_packet(pkt, &parsectx); init_packet(pkt); continue; } if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { hd->found.n_packets--; /* fix counter */ break; /* ready */ } in_cert = 1; node = lastnode = new_kbnode (pkt); if (!keyblock) keyblock = node; else add_kbnode (keyblock, node); switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: if (++pk_no == hd->found.pk_no) node->flag |= 1; break; case PKT_USER_ID: if (++uid_no == hd->found.uid_no) node->flag |= 2; break; default: break; } pkt = xmalloc (sizeof *pkt); init_packet(pkt); } set_packet_list_mode(save_mode); if (rc == -1 && keyblock) rc = 0; /* got the entire keyblock */ if (rc || !ret_kb) release_kbnode (keyblock); else { *ret_kb = keyblock; } free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree (pkt); iobuf_close(a); /* Make sure that future search operations fail immediately when * we know that we are working on a invalid keyring */ if (gpg_err_code (rc) == GPG_ERR_INV_KEYRING) hd->current.error = rc; return rc; } int keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb) { int rc; if (!hd->found.kr) return -1; /* no successful prior search */ if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); if (!hd->found.n_packets) { /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); return rc; } if (!hd->found.n_packets) BUG (); } /* The open iobuf isn't needed anymore and in fact is a problem when it comes to renaming the keyring files on some operating systems, so close it here */ iobuf_close(hd->current.iobuf); hd->current.iobuf = NULL; /* do the update */ rc = do_copy (3, hd->found.kr->fname, kb, hd->found.offset, hd->found.n_packets ); if (!rc) { if (key_present_hash) { key_present_hash_update_from_kb (key_present_hash, kb); } /* better reset the found info */ hd->found.kr = NULL; hd->found.offset = 0; } return rc; } int keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb) { int rc; const char *fname; if (!hd) fname = NULL; else if (hd->found.kr) { fname = hd->found.kr->fname; if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); } else if (hd->current.kr) { fname = hd->current.kr->fname; if (hd->current.kr->read_only) return gpg_error (GPG_ERR_EACCES); } else fname = hd->resource? hd->resource->fname:NULL; if (!fname) return GPG_ERR_GENERAL; /* Close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position * after the write opertions. */ iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; /* do the insert */ rc = do_copy (1, fname, kb, 0, 0 ); if (!rc && key_present_hash) { key_present_hash_update_from_kb (key_present_hash, kb); } return rc; } int keyring_delete_keyblock (KEYRING_HANDLE hd) { int rc; if (!hd->found.kr) return -1; /* no successful prior search */ if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); if (!hd->found.n_packets) { /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); return rc; } if (!hd->found.n_packets) BUG (); } /* close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position * after the write opertions. */ iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; /* do the delete */ rc = do_copy (2, hd->found.kr->fname, NULL, hd->found.offset, hd->found.n_packets ); if (!rc) { /* better reset the found info */ hd->found.kr = NULL; hd->found.offset = 0; /* Delete is a rare operations, so we don't remove the keys * from the offset table */ } return rc; } /* * Start the next search on this handle right at the beginning */ int keyring_search_reset (KEYRING_HANDLE hd) { log_assert (hd); iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; hd->current.eof = 0; hd->current.error = 0; hd->found.kr = NULL; hd->found.offset = 0; if (hd->current.kr) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)hd->current.kr->fname); hd->current.kr = NULL; return 0; } static int prepare_search (KEYRING_HANDLE hd) { if (hd->current.error) { /* If the last key was a legacy key, we simply ignore the error so that we can easily use search_next. */ if (gpg_err_code (hd->current.error) == GPG_ERR_LEGACY_KEY) { if (DBG_LOOKUP) log_debug ("%s: last error was GPG_ERR_LEGACY_KEY, clearing\n", __func__); hd->current.error = 0; } else { if (DBG_LOOKUP) log_debug ("%s: returning last error: %s\n", __func__, gpg_strerror (hd->current.error)); return hd->current.error; /* still in error state */ } } if (hd->current.kr && !hd->current.eof) { if ( !hd->current.iobuf ) { if (DBG_LOOKUP) log_debug ("%s: missing iobuf!\n", __func__); return GPG_ERR_GENERAL; /* Position invalid after a modify. */ } return 0; /* okay */ } if (!hd->current.kr && hd->current.eof) { if (DBG_LOOKUP) log_debug ("%s: EOF!\n", __func__); return -1; /* still EOF */ } if (!hd->current.kr) { /* start search with first keyring */ hd->current.kr = hd->resource; if (!hd->current.kr) { if (DBG_LOOKUP) log_debug ("%s: keyring not available!\n", __func__); hd->current.eof = 1; return -1; /* keyring not available */ } log_assert (!hd->current.iobuf); } else { /* EOF */ if (DBG_LOOKUP) log_debug ("%s: EOF\n", __func__); iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; hd->current.kr = NULL; hd->current.eof = 1; return -1; } hd->current.eof = 0; hd->current.iobuf = iobuf_open (hd->current.kr->fname); if (!hd->current.iobuf) { hd->current.error = gpg_error_from_syserror (); log_error(_("can't open '%s'\n"), hd->current.kr->fname ); return hd->current.error; } return 0; } /* A map of the all characters valid used for word_match() * Valid characters are in this table converted to uppercase. * because the upper 128 bytes have special meaning, we assume * that they are all valid. * Note: We must use numerical values here in case that this program * will be converted to those little blue HAL9000s with their strange * EBCDIC character set (user ids are UTF-8). * wk 2000-04-13: Hmmm, does this really make sense, given the fact that * we can run gpg now on a S/390 running GNU/Linux, where the code * translation is done by the device drivers? */ static const byte word_match_chars[256] = { /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 40 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 58 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 68 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 78 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* a0 */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a8 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* b0 */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b8 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* c0 */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* c8 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* d0 */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* d8 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* e0 */ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e8 */ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* f0 */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f8 */ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; /**************** * Do a word match (original user id starts with a '+'). * The pattern is already tokenized to a more suitable format: * There are only the real words in it delimited by one space * and all converted to uppercase. * * Returns: 0 if all words match. * * Note: This algorithm is a straightforward one and not very * fast. It works for UTF-8 strings. The uidlen should * be removed but due to the fact that old versions of * pgp don't use UTF-8 we still use the length; this should * be fixed in parse-packet (and replace \0 by some special * UTF-8 encoding) */ static int word_match( const byte *uid, size_t uidlen, const byte *pattern ) { size_t wlen, n; const byte *p; const byte *s; for( s=pattern; *s; ) { do { /* skip leading delimiters */ while( uidlen && !word_match_chars[*uid] ) uid++, uidlen--; /* get length of the word */ n = uidlen; p = uid; while( n && word_match_chars[*p] ) p++, n--; wlen = p - uid; /* and compare against the current word from pattern */ for(n=0, p=uid; n < wlen && s[n] != ' ' && s[n] ; n++, p++ ) { if( word_match_chars[*p] != s[n] ) break; } if( n == wlen && (s[n] == ' ' || !s[n]) ) break; /* found */ uid += wlen; uidlen -= wlen; } while( uidlen ); if( !uidlen ) return -1; /* not found */ /* advance to next word in pattern */ for(; *s != ' ' && *s ; s++ ) ; if( *s ) s++ ; } return 0; /* found */ } /**************** * prepare word word_match; that is parse the name and * build the pattern. * caller has to free the returned pattern */ static char* prepare_word_match (const byte *name) { byte *pattern, *p; int c; /* the original length is always enough for the pattern */ p = pattern = xmalloc(strlen(name)+1); do { /* skip leading delimiters */ while( *name && !word_match_chars[*name] ) name++; /* copy as long as we don't have a delimiter and convert * to uppercase. * fixme: how can we handle utf8 uppercasing */ for( ; *name && (c=word_match_chars[*name]); name++ ) *p++ = c; *p++ = ' '; /* append pattern delimiter */ } while( *name ); p[-1] = 0; /* replace last pattern delimiter by EOS */ return pattern; } static int compare_name (int mode, const char *name, const char *uid, size_t uidlen) { int i; const char *s, *se; if (mode == KEYDB_SEARCH_MODE_EXACT) { for (i=0; name[i] && uidlen; i++, uidlen--) if (uid[i] != name[i]) break; if (!uidlen && !name[i]) return 0; /* found */ } else if (mode == KEYDB_SEARCH_MODE_SUBSTR) { if (ascii_memistr( uid, uidlen, name )) return 0; } else if ( mode == KEYDB_SEARCH_MODE_MAIL || mode == KEYDB_SEARCH_MODE_MAILSUB || mode == KEYDB_SEARCH_MODE_MAILEND) { int have_angles = 1; for (i=0, s= uid; i < uidlen && *s != '<'; s++, i++) ; if (i == uidlen) { /* The UID is a plain addr-spec (cf. RFC2822 section 4.3). */ have_angles = 0; s = uid; i = 0; } if (i < uidlen) { if (have_angles) { /* skip opening delim and one char and look for the closing one*/ s++; i++; for (se=s+1, i++; i < uidlen && *se != '>'; se++, i++) ; } else se = s + uidlen; if (i < uidlen) { i = se - s; if (mode == KEYDB_SEARCH_MODE_MAIL) { if( strlen(name)-2 == i && !ascii_memcasecmp( s, name+1, i) ) return 0; } else if (mode == KEYDB_SEARCH_MODE_MAILSUB) { if( ascii_memistr( s, i, name ) ) return 0; } else { /* email from end */ /* nyi */ } } } } else if (mode == KEYDB_SEARCH_MODE_WORDS) return word_match (uid, uidlen, name); else BUG(); return -1; /* not found */ } /* * Search through the keyring(s), starting at the current position, * for a keyblock which contains one of the keys described in the DESC array. */ int keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t *descindex, int ignore_legacy) { int rc; PACKET pkt; struct parse_packet_ctx_s parsectx; int save_mode; off_t offset, main_offset; size_t n; int need_uid, need_words, need_keyid, need_fpr, any_skip; int pk_no, uid_no; int initial_skip; int scanned_from_start; int use_key_present_hash; PKT_user_id *uid = NULL; PKT_public_key *pk = NULL; u32 aki[2]; /* figure out what information we need */ need_uid = need_words = need_keyid = need_fpr = any_skip = 0; for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: need_uid = 1; break; case KEYDB_SEARCH_MODE_WORDS: need_uid = 1; need_words = 1; break; case KEYDB_SEARCH_MODE_SHORT_KID: case KEYDB_SEARCH_MODE_LONG_KID: need_keyid = 1; break; case KEYDB_SEARCH_MODE_FPR16: case KEYDB_SEARCH_MODE_FPR20: case KEYDB_SEARCH_MODE_FPR: need_fpr = 1; break; case KEYDB_SEARCH_MODE_FIRST: /* always restart the search in this mode */ keyring_search_reset (hd); break; default: break; } if (desc[n].skipfnc) { any_skip = 1; need_keyid = 1; } } if (DBG_LOOKUP) log_debug ("%s: need_uid = %d; need_words = %d; need_keyid = %d; need_fpr = %d; any_skip = %d\n", __func__, need_uid, need_words, need_keyid, need_fpr, any_skip); rc = prepare_search (hd); if (rc) { if (DBG_LOOKUP) log_debug ("%s: prepare_search failed: %s (%d)\n", __func__, gpg_strerror (rc), gpg_err_code (rc)); return rc; } use_key_present_hash = !!key_present_hash; if (!use_key_present_hash) { if (DBG_LOOKUP) log_debug ("%s: no offset table.\n", __func__); } else if (!key_present_hash_ready) { if (DBG_LOOKUP) log_debug ("%s: initializing offset table. (need_keyid: %d => 1)\n", __func__, need_keyid); need_keyid = 1; } else if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID) { struct key_present *oi; if (DBG_LOOKUP) log_debug ("%s: look up by long key id, checking cache\n", __func__); oi = key_present_hash_lookup (key_present_hash, desc[0].u.kid); if (!oi) { /* We know that we don't have this key */ if (DBG_LOOKUP) log_debug ("%s: cache says not present\n", __func__); hd->found.kr = NULL; hd->current.eof = 1; return -1; } /* We could now create a positive search status and return. * However the problem is that another instance of gpg may * have changed the keyring so that the offsets are not valid * anymore - therefore we don't do it */ } if (need_words) { const char *name = NULL; log_debug ("word search mode does not yet work\n"); /* FIXME: here is a long standing bug in our function and in addition we just use the first search description */ for (n=0; n < ndesc && !name; n++) { if (desc[n].mode == KEYDB_SEARCH_MODE_WORDS) name = desc[n].u.name; } log_assert (name); if ( !hd->word_match.name || strcmp (hd->word_match.name, name) ) { /* name changed */ xfree (hd->word_match.name); xfree (hd->word_match.pattern); hd->word_match.name = xstrdup (name); hd->word_match.pattern = prepare_word_match (name); } /* name = hd->word_match.pattern; */ } init_packet(&pkt); save_mode = set_packet_list_mode(0); hd->found.kr = NULL; main_offset = 0; pk_no = uid_no = 0; initial_skip = 1; /* skip until we see the start of a keyblock */ scanned_from_start = iobuf_tell (hd->current.iobuf) == 0; if (DBG_LOOKUP) log_debug ("%s: %ssearching from start of resource.\n", __func__, scanned_from_start ? "" : "not "); init_parse_packet (&parsectx, hd->current.iobuf); while (1) { byte afp[MAX_FINGERPRINT_LEN]; size_t an; rc = search_packet (&parsectx, &pkt, &offset, need_uid); if (ignore_legacy && gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) { free_packet (&pkt, &parsectx); continue; } if (rc) break; if (pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_SECRET_KEY) { main_offset = offset; pk_no = uid_no = 0; initial_skip = 0; } if (initial_skip) { free_packet (&pkt, &parsectx); continue; } pk = NULL; uid = NULL; if ( pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_PUBLIC_SUBKEY || pkt.pkttype == PKT_SECRET_KEY || pkt.pkttype == PKT_SECRET_SUBKEY) { pk = pkt.pkt.public_key; ++pk_no; if (need_fpr) { fingerprint_from_pk (pk, afp, &an); while (an < 20) /* fill up to 20 bytes */ afp[an++] = 0; } if (need_keyid) keyid_from_pk (pk, aki); if (use_key_present_hash && !key_present_hash_ready && scanned_from_start) key_present_hash_update (key_present_hash, aki); } else if (pkt.pkttype == PKT_USER_ID) { uid = pkt.pkt.user_id; ++uid_no; } for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_NONE: BUG (); break; case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: case KEYDB_SEARCH_MODE_WORDS: if ( uid && !compare_name (desc[n].mode, desc[n].u.name, uid->name, uid->len)) goto found; break; case KEYDB_SEARCH_MODE_SHORT_KID: if (pk && desc[n].u.kid[1] == aki[1]) goto found; break; case KEYDB_SEARCH_MODE_LONG_KID: if (pk && desc[n].u.kid[0] == aki[0] && desc[n].u.kid[1] == aki[1]) goto found; break; case KEYDB_SEARCH_MODE_FPR16: if (pk && !memcmp (desc[n].u.fpr, afp, 16)) goto found; break; case KEYDB_SEARCH_MODE_FPR20: case KEYDB_SEARCH_MODE_FPR: if (pk && !memcmp (desc[n].u.fpr, afp, 20)) goto found; break; case KEYDB_SEARCH_MODE_FIRST: if (pk) goto found; break; case KEYDB_SEARCH_MODE_NEXT: if (pk) goto found; break; default: rc = GPG_ERR_INV_ARG; goto found; } } free_packet (&pkt, &parsectx); continue; found: if (rc) goto real_found; if (DBG_LOOKUP) log_debug ("%s: packet starting at offset %lld matched descriptor %zu\n" , __func__, (long long)offset, n); /* Record which desc we matched on. Note this value is only meaningful if this function returns with no errors. */ if(descindex) *descindex=n; for (n=any_skip?0:ndesc; n < ndesc; n++) { if (desc[n].skipfnc && desc[n].skipfnc (desc[n].skipfncvalue, aki, uid_no)) { if (DBG_LOOKUP) log_debug ("%s: skipping match: desc %zd's skip function returned TRUE\n", __func__, n); break; } } if (n == ndesc) goto real_found; free_packet (&pkt, &parsectx); } real_found: if (!rc) { if (DBG_LOOKUP) log_debug ("%s: returning success\n", __func__); hd->found.offset = main_offset; hd->found.kr = hd->current.kr; hd->found.pk_no = pk? pk_no : 0; hd->found.uid_no = uid? uid_no : 0; } else if (rc == -1) { if (DBG_LOOKUP) log_debug ("%s: no matches (EOF)\n", __func__); hd->current.eof = 1; /* if we scanned all keyrings, we are sure that * all known key IDs are in our offtbl, mark that. */ if (use_key_present_hash && !key_present_hash_ready && scanned_from_start) { KR_RESOURCE kr; /* First set the did_full_scan flag for this keyring. */ for (kr=kr_resources; kr; kr = kr->next) { if (hd->resource == kr) { kr->did_full_scan = 1; break; } } /* Then check whether all flags are set and if so, mark the offtbl ready */ for (kr=kr_resources; kr; kr = kr->next) { if (!kr->did_full_scan) break; } if (!kr) key_present_hash_ready = 1; } } else { if (DBG_LOOKUP) log_debug ("%s: error encountered during search: %s (%d)\n", __func__, gpg_strerror (rc), rc); hd->current.error = rc; } free_packet (&pkt, &parsectx); deinit_parse_packet (&parsectx); set_packet_list_mode(save_mode); return rc; } static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, IOBUF *r_fp) { gpg_error_t err; mode_t oldmask; err = keybox_tmp_names (template, 1, r_bakfname, r_tmpfname); if (err) return err; /* Create the temp file with limited access. Note that the umask call is not anymore needed because iobuf_create now takes care of it. However, it does not harm and thus we keep it. */ oldmask = umask (077); if (is_secured_filename (*r_tmpfname)) { *r_fp = NULL; gpg_err_set_errno (EPERM); } else *r_fp = iobuf_create (*r_tmpfname, 1); umask (oldmask); if (!*r_fp) { err = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), *r_tmpfname, gpg_strerror (err)); xfree (*r_tmpfname); *r_tmpfname = NULL; xfree (*r_bakfname); *r_bakfname = NULL; } return err; } static int rename_tmp_file (const char *bakfname, const char *tmpfname, const char *fname) { int rc = 0; int block = 0; /* Invalidate close caches. */ if (iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)tmpfname )) { rc = gpg_error_from_syserror (); goto fail; } iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)bakfname ); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname ); /* First make a backup file. */ block = 1; rc = gnupg_rename_file (fname, bakfname, &block); if (rc) goto fail; /* then rename the file */ rc = gnupg_rename_file (tmpfname, fname, NULL); if (block) { gnupg_unblock_all_signals (); block = 0; } if (rc) { register_secured_file (fname); goto fail; } /* Now make sure the file has the same permissions as the original */ #ifndef HAVE_DOSISH_SYSTEM { struct stat statbuf; statbuf.st_mode=S_IRUSR | S_IWUSR; if (!stat (bakfname, &statbuf) && !chmod (fname, statbuf.st_mode)) ; else log_error ("WARNING: unable to restore permissions to '%s': %s", fname, strerror(errno)); } #endif return 0; fail: if (block) gnupg_unblock_all_signals (); return rc; } static int write_keyblock (IOBUF fp, KBNODE keyblock) { KBNODE kbctx = NULL, node; int rc; while ( (node = walk_kbnode (keyblock, &kbctx, 0)) ) { if ( (rc = build_packet_and_meta (fp, node->pkt) )) { log_error ("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); return rc; } } return 0; } /* * Walk over all public keyrings, check the signatures and replace the * keyring with a new one where the signature cache is then updated. * This is only done for the public keyrings. */ int keyring_rebuild_cache (ctrl_t ctrl, void *token, int noisy) { KEYRING_HANDLE hd; KEYDB_SEARCH_DESC desc; KBNODE keyblock = NULL, node; const char *lastresname = NULL, *resname; IOBUF tmpfp = NULL; char *tmpfilename = NULL; char *bakfilename = NULL; int rc; ulong count = 0, sigcount = 0; hd = keyring_new (token); if (!hd) return gpg_error_from_syserror (); memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; rc=keyring_lock (hd, 1); if(rc) goto leave; for (;;) { rc = keyring_search (hd, &desc, 1, NULL, 1 /* ignore_legacy */); if (rc) break; /* ready. */ desc.mode = KEYDB_SEARCH_MODE_NEXT; resname = keyring_get_resource_name (hd); if (lastresname != resname ) { /* we have switched to a new keyring - commit changes */ if (tmpfp) { if (iobuf_close (tmpfp)) { rc = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", tmpfilename, strerror (errno)); goto leave; } /* because we have switched resources, we can be sure that * the original file is closed */ tmpfp = NULL; } /* Static analyzer note: BAKFILENAME is never NULL here because it is controlled by LASTRESNAME. */ rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, lastresname) : 0; xfree (tmpfilename); tmpfilename = NULL; xfree (bakfilename); bakfilename = NULL; if (rc) goto leave; lastresname = resname; if (noisy && !opt.quiet) log_info (_("caching keyring '%s'\n"), resname); rc = create_tmp_file (resname, &bakfilename, &tmpfilename, &tmpfp); if (rc) goto leave; } release_kbnode (keyblock); rc = keyring_get_keyblock (hd, &keyblock); if (rc) { if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; /* Skip legacy keys. */ log_error ("keyring_get_keyblock failed: %s\n", gpg_strerror (rc)); goto leave; } if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { /* We had a few reports about corrupted keyrings; if we have been called directly from the command line we delete such a keyblock instead of bailing out. */ log_error ("unexpected keyblock found (pkttype=%d)%s\n", keyblock->pkt->pkttype, noisy? " - deleted":""); if (noisy) continue; log_info ("Hint: backup your keys and try running '%s'\n", "gpg --rebuild-keydb-caches"); rc = gpg_error (GPG_ERR_INV_KEYRING); goto leave; } if (keyblock->pkt->pkt.public_key->version < 4) { /* We do not copy/cache v3 keys or any other unknown packets. It is better to remove them from the keyring. The code required to keep them in the keyring would be too complicated. Given that we do not touch the old secring.gpg a suitable backup for decryption of v3 stuff using an older gpg version will always be available. Note: This test is actually superfluous because we already acted upon GPG_ERR_LEGACY_KEY. */ } else { /* Check all signature to set the signature's cache flags. */ for (node=keyblock; node; node=node->next) { /* Note that this doesn't cache the result of a revocation issued by a designated revoker. This is because the pk in question does not carry the revkeys as we haven't merged the key and selfsigs. It is questionable whether this matters very much since there are very very few designated revoker revocation packets out there. */ if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig=node->pkt->pkt.signature; if(!opt.no_sig_cache && sig->flags.checked && sig->flags.valid && (openpgp_md_test_algo(sig->digest_algo) || openpgp_pk_test_algo(sig->pubkey_algo))) sig->flags.checked=sig->flags.valid=0; else check_key_signature (ctrl, keyblock, node, NULL); sigcount++; } } /* Write the keyblock to the temporary file. */ rc = write_keyblock (tmpfp, keyblock); if (rc) goto leave; if ( !(++count % 50) && noisy && !opt.quiet) log_info (ngettext("%lu keys cached so far (%lu signature)\n", "%lu keys cached so far (%lu signatures)\n", sigcount), count, sigcount); } } /* end main loop */ if (rc == -1) rc = 0; if (rc) { log_error ("keyring_search failed: %s\n", gpg_strerror (rc)); goto leave; } if (noisy || opt.verbose) { log_info (ngettext("%lu key cached", "%lu keys cached", count), count); log_printf (ngettext(" (%lu signature)\n", " (%lu signatures)\n", sigcount), sigcount); } if (tmpfp) { if (iobuf_close (tmpfp)) { rc = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", tmpfilename, strerror (errno)); goto leave; } /* because we have switched resources, we can be sure that * the original file is closed */ tmpfp = NULL; } rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, lastresname) : 0; xfree (tmpfilename); tmpfilename = NULL; xfree (bakfilename); bakfilename = NULL; leave: if (tmpfp) iobuf_cancel (tmpfp); xfree (tmpfilename); xfree (bakfilename); release_kbnode (keyblock); keyring_lock (hd, 0); keyring_release (hd); return rc; } /**************** * Perform insert/delete/update operation. * mode 1 = insert * 2 = delete * 3 = update */ static int do_copy (int mode, const char *fname, KBNODE root, off_t start_offset, unsigned int n_packets ) { IOBUF fp, newfp; int rc=0; char *bakfname = NULL; char *tmpfname = NULL; /* Open the source file. Because we do a rename, we have to check the permissions of the file */ if (access (fname, W_OK)) return gpg_error_from_syserror (); fp = iobuf_open (fname); if (mode == 1 && !fp && errno == ENOENT) { /* insert mode but file does not exist: create a new file */ KBNODE kbctx, node; mode_t oldmask; oldmask=umask(077); if (is_secured_filename (fname)) { newfp = NULL; gpg_err_set_errno (EPERM); } else newfp = iobuf_create (fname, 1); umask(oldmask); if( !newfp ) { rc = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), fname, strerror(errno)); return rc; } if( !opt.quiet ) log_info(_("%s: keyring created\n"), fname ); kbctx=NULL; while ( (node = walk_kbnode( root, &kbctx, 0 )) ) { if( (rc = build_packet( newfp, node->pkt )) ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); iobuf_cancel(newfp); return rc; } } if( iobuf_close(newfp) ) { rc = gpg_error_from_syserror (); log_error ("%s: close failed: %s\n", fname, strerror(errno)); return rc; } return 0; /* ready */ } if( !fp ) { rc = gpg_error_from_syserror (); log_error(_("can't open '%s': %s\n"), fname, strerror(errno) ); goto leave; } /* Create the new file. */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { iobuf_close(fp); goto leave; } if( mode == 1 ) { /* insert */ /* copy everything to the new file */ rc = copy_all_packets (fp, newfp); if( rc != -1 ) { log_error("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 2 || mode == 3 ) { /* delete or update */ /* copy first part to the new file */ rc = copy_some_packets( fp, newfp, start_offset ); if( rc ) { /* should never get EOF here */ log_error ("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } /* skip this keyblock */ log_assert( n_packets ); rc = skip_some_packets( fp, n_packets ); if( rc ) { log_error("%s: skipping %u packets failed: %s\n", fname, n_packets, gpg_strerror (rc)); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 1 || mode == 3 ) { /* insert or update */ rc = write_keyblock (newfp, root); if (rc) { iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 2 || mode == 3 ) { /* delete or update */ /* copy the rest */ rc = copy_all_packets( fp, newfp ); if( rc != -1 ) { log_error("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } /* close both files */ if( iobuf_close(fp) ) { rc = gpg_error_from_syserror (); log_error("%s: close failed: %s\n", fname, strerror(errno) ); goto leave; } if( iobuf_close(newfp) ) { rc = gpg_error_from_syserror (); log_error("%s: close failed: %s\n", tmpfname, strerror(errno) ); goto leave; } rc = rename_tmp_file (bakfname, tmpfname, fname); leave: xfree(bakfname); xfree(tmpfname); return rc; }