g10: Fix another race condition for trustdb access.
[gnupg.git] / g10 / keydb.c
index fb4966c..17ddd5d 100644 (file)
@@ -23,7 +23,6 @@
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
-#include <assert.h>
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <unistd.h>
@@ -60,7 +59,10 @@ struct resource_item
 
 static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
 static int used_resources;
-static void *primary_keyring=NULL;
+
+/* A pointer used to check for the primary key database by comparing
+   to the struct resource_item's TOKEN.  */
+static void *primary_keydb;
 
 
 /* This is a simple cache used to return the last result of a
@@ -261,7 +263,7 @@ keyblock_cache_clear (struct keydb_handle *hd)
    the keyring or keybox will be created.
 
    Return 0 if it is okay to access the specified file.  */
-static int
+static gpg_error_t
 maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
 {
   dotlock_t lockhd = NULL;
@@ -269,6 +271,8 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
   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. */
@@ -347,11 +351,39 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
     }
 
   /* 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);
@@ -419,6 +451,8 @@ maybe_create_keyring_or_box (char *filename, int is_box, int force_create)
       dotlock_release (lockhd);
       dotlock_destroy (lockhd);
     }
+  xfree (bak_fname);
+  xfree (tmp_fname);
   return rc;
 }
 
@@ -533,7 +567,52 @@ keydb_search_desc_dump (struct keydb_search_desc *desc)
       return xasprintf ("Bad search mode (%d)", desc->mode);
     }
 }
+
+
 \f
+/* 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)
 {
@@ -547,7 +626,7 @@ keydb_add_resource (const char *url, unsigned int flags)
   int read_only = !!(flags&KEYDB_RESOURCE_FLAG_READONLY);
   int is_default = !!(flags&KEYDB_RESOURCE_FLAG_DEFAULT);
   int is_gpgvdef = !!(flags&KEYDB_RESOURCE_FLAG_GPGVDEF);
-  int rc = 0;
+  gpg_error_t err = 0;
   KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
   void *token;
 
@@ -568,7 +647,7 @@ keydb_add_resource (const char *url, unsigned int flags)
   else if (strchr (resname, ':'))
     {
       log_error ("invalid key resource URL '%s'\n", url );
-      rc = gpg_error (GPG_ERR_GENERAL);
+      err = gpg_error (GPG_ERR_GENERAL);
       goto leave;
     }
 #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
@@ -587,7 +666,7 @@ keydb_add_resource (const char *url, unsigned int flags)
           )
         filename = make_filename (resname, NULL);
       else
-        filename = make_filename (opt.homedir, resname, NULL);
+        filename = make_filename (gnupg_homedir (), resname, NULL);
     }
   else
     filename = xstrdup (resname);
@@ -663,22 +742,22 @@ keydb_add_resource (const char *url, unsigned int flags)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
       log_error ("unknown type of key resource '%s'\n", url );
-      rc = gpg_error (GPG_ERR_GENERAL);
+      err = gpg_error (GPG_ERR_GENERAL);
       goto leave;
 
     case KEYDB_RESOURCE_TYPE_KEYRING:
-      rc = maybe_create_keyring_or_box (filename, 0, create);
-      if (rc)
+      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)
-            rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+            err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
           else
             {
               if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
-                primary_keyring = token;
+                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;
@@ -691,26 +770,25 @@ keydb_add_resource (const char *url, unsigned int flags)
              However, we can still mark it as primary even if it was
              already registered.  */
           if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
-            primary_keyring = token;
+            primary_keydb = token;
         }
       break;
 
     case KEYDB_RESOURCE_TYPE_KEYBOX:
       {
-        rc = maybe_create_keyring_or_box (filename, 1, create);
-        if (rc)
+        err = maybe_create_keyring_or_box (filename, 1, create);
+        if (err)
           goto leave;
 
-        /* FIXME: How do we register a read-only keybox?  */
-        token = keybox_register_file (filename, 0);
-        if (token)
+        err = keybox_register_file (filename, 0, &token);
+        if (!err)
           {
             if (used_resources >= MAX_KEYDB_RESOURCES)
-              rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+              err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
             else
               {
-                /* if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY)) */
-                /*   primary_keyring = token; */
+                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;
@@ -721,32 +799,31 @@ keydb_add_resource (const char *url, unsigned int flags)
                 used_resources++;
               }
           }
-        else
+        else if (gpg_err_code (err) == GPG_ERR_EEXIST)
           {
             /* 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; */
+            if ((flags & KEYDB_RESOURCE_FLAG_PRIMARY))
+              primary_keydb = token;
           }
       }
       break;
 
       default:
        log_error ("resource type of '%s' not supported\n", url);
-       rc = gpg_error (GPG_ERR_GENERAL);
+       err = gpg_error (GPG_ERR_GENERAL);
        goto leave;
     }
 
   /* fixme: check directory permissions and print a warning */
 
  leave:
-  if (rc)
-    log_error (_("keyblock resource '%s': %s\n"), filename, gpg_strerror (rc));
+  if (err)
+    log_error (_("keyblock resource '%s': %s\n"), filename, gpg_strerror (err));
   else
     any_registered = 1;
   xfree (filename);
-  return rc;
+  return err;
 }
 
 
@@ -783,7 +860,7 @@ keydb_new (void)
   hd->saved_found = -1;
   hd->is_reset = 1;
 
-  assert (used_resources <= MAX_KEYDB_RESOURCES);
+  log_assert (used_resources <= MAX_KEYDB_RESOURCES);
   for (i=j=0; ! die && i < used_resources; i++)
     {
       switch (all_resources[i].type)
@@ -841,7 +918,7 @@ keydb_release (KEYDB_HANDLE hd)
 
   if (!hd)
     return;
-  assert (active_handles > 0);
+  log_assert (active_handles > 0);
   active_handles--;
 
   unlock_all (hd);
@@ -864,6 +941,9 @@ keydb_release (KEYDB_HANDLE hd)
 }
 
 
+/* Set a flag on the handle to suppress use of cached results.  This
+ * is required for updating a keyring and for key listings.  Fixme:
+ * Using a new parameter for keydb_new might be a better solution.  */
 void
 keydb_disable_caching (KEYDB_HANDLE hd)
 {
@@ -872,6 +952,14 @@ keydb_disable_caching (KEYDB_HANDLE hd)
 }
 
 
+/* 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)
 {
@@ -946,7 +1034,7 @@ lock_all (KEYDB_HANDLE hd)
               keyring_lock (hd->active[i].u.kr, 0);
               break;
             case KEYDB_RESOURCE_TYPE_KEYBOX:
-              rc = keybox_lock (hd->active[i].u.kb, 0);
+              keybox_lock (hd->active[i].u.kb, 0);
               break;
             }
         }
@@ -985,6 +1073,22 @@ unlock_all (KEYDB_HANDLE hd)
 
 
 \f
+/* 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 the save stack only has room for a single
+ * instance of the state.  */
 void
 keydb_push_found_state (KEYDB_HANDLE hd)
 {
@@ -1014,6 +1118,8 @@ keydb_push_found_state (KEYDB_HANDLE hd)
 }
 
 
+/* Restore the previous save state.  If the saved state is NULL or
+   invalid, this is a NOP.  */
 void
 keydb_pop_found_state (KEYDB_HANDLE hd)
 {
@@ -1204,6 +1310,15 @@ parse_keyblock_image (iobuf_t iobuf, int pk_no, int uid_no,
 }
 
 
+/* 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)
 {
@@ -1382,16 +1497,32 @@ build_keyblock_image (kbnode_t keyblock, iobuf_t *r_iobuf, u32 **r_sigstatus)
 }
 
 
+/* 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 (KEYDB_HANDLE hd, kbnode_t kb)
 {
   gpg_error_t err;
-  PKT_public_key *pk = kb->pkt->pkt.public_key;
+  PKT_public_key *pk;
   KEYDB_SEARCH_DESC desc;
   size_t len;
 
-  assert (kb);
-  assert (kb->pkt->pkttype == PKT_PUBLIC_KEY);
+  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);
@@ -1411,13 +1542,13 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
   if (len == 20)
     desc.mode = KEYDB_SEARCH_MODE_FPR20;
   else
-    log_bug ("%s: Unsupported key length: %zd\n", __func__, len);
+    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);
-  assert (hd->found >= 0 && hd->found < hd->used);
+  log_assert (hd->found >= 0 && hd->found < hd->used);
 
   switch (hd->active[hd->found].type)
     {
@@ -1448,6 +1579,16 @@ keydb_update_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 }
 
 
+/* 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)
 {
@@ -1509,6 +1650,11 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, kbnode_t kb)
 }
 
 
+/* 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)
 {
@@ -1549,6 +1695,15 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
 
 
 \f
+/* 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)
 {
@@ -1562,11 +1717,11 @@ keydb_locate_writable (KEYDB_HANDLE hd)
     return rc;
 
   /* If we have a primary set, try that one first */
-  if (primary_keyring)
+  if (primary_keydb)
     {
       for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
        {
-         if(hd->active[hd->current].token==primary_keyring)
+         if(hd->active[hd->current].token == primary_keydb)
            {
              if(keyring_is_writable (hd->active[hd->current].token))
                return 0;
@@ -1601,6 +1756,8 @@ keydb_locate_writable (KEYDB_HANDLE hd)
   return gpg_error (GPG_ERR_NOT_FOUND);
 }
 
+
+/* Rebuild the on-disk caches of all key resources.  */
 void
 keydb_rebuild_caches (int noisy)
 {
@@ -1628,6 +1785,8 @@ keydb_rebuild_caches (int noisy)
 }
 
 
+/* 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)
 {
@@ -1635,6 +1794,12 @@ keydb_get_skipped_counter (KEYDB_HANDLE hd)
 }
 
 
+/* 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)
 {
@@ -1675,6 +1840,24 @@ keydb_search_reset (KEYDB_HANDLE hd)
 }
 
 
+/* Search the database for keys matching the search description.  If
+ * the DB contains any legacy keys, these are silently ignored.
+ *
+ * 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)
@@ -1826,6 +2009,11 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
 }
 
 
+/* 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)
 {
@@ -1842,6 +2030,10 @@ keydb_search_first (KEYDB_HANDLE hd)
 }
 
 
+/* 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)
 {
@@ -1852,6 +2044,13 @@ keydb_search_next (KEYDB_HANDLE hd)
   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)
 {
@@ -1864,6 +2063,13 @@ keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
   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)
 {