common: Turn assertions into expressions.
[gnupg.git] / sm / keydb.c
index 974625d..44dd9ca 100644 (file)
@@ -15,7 +15,7 @@
  * 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 <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
@@ -47,13 +47,16 @@ struct resource_item {
     KEYBOX_HANDLE kr;
   } u;
   void *token;
-  int secret;
   dotlock_t lockhandle;
 };
 
 static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
 static int used_resources;
 
+/* Whether we have successfully registered any resource.  */
+static int any_registered;
+
+
 struct keydb_handle {
   int locked;
   int found;
@@ -107,7 +110,7 @@ try_make_homedir (const char *fname)
    locked.  This lock check does not work if the directory itself is
    not yet available.  If R_CREATED is not NULL it will be set to true
    if the function created a new keybox.  */
-static int
+static gpg_error_t
 maybe_create_keybox (char *filename, int force, int *r_created)
 {
   dotlock_t lockhd = NULL;
@@ -122,7 +125,7 @@ maybe_create_keybox (char *filename, int force, int *r_created)
 
   /* A quick test whether the filename already exists. */
   if (!access (filename, F_OK))
-    return 0;
+    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.  */
@@ -213,6 +216,18 @@ maybe_create_keybox (char *filename, int force, int *r_created)
     }
   umask (oldmask);
 
+  /* Make sure that at least one record is in a new keybox file, so
+     that the detection magic for OpenPGP keyboxes works the next time
+     it is used.  */
+  rc = _keybox_write_header_blob (fp, 0);
+  if (rc)
+    {
+      fclose (fp);
+      log_error (_("error creating keybox '%s': %s\n"),
+                 filename, gpg_strerror (rc));
+      goto leave;
+    }
+
   if (!opt.quiet)
     log_info (_("keybox '%s' created\n"), filename);
   if (r_created)
@@ -237,13 +252,12 @@ maybe_create_keybox (char *filename, int force, int *r_created)
  * does not exist.  If AUTO_CREATED is not NULL it will be set to true
  * if the function has created a new keybox.
  */
-int
-keydb_add_resource (const char *url, int force, int secret, int *auto_created)
+gpg_error_t
+keydb_add_resource (ctrl_t ctrl, const char *url, int force, int *auto_created)
 {
-  static int any_secret, any_public;
   const char *resname = url;
   char *filename = NULL;
-  int rc = 0;
+  gpg_error_t err = 0;
   KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
 
   if (auto_created)
@@ -264,7 +278,7 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
       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__ */
@@ -275,13 +289,13 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
       if (strchr(resname, DIRSEP_C) )
         filename = make_filename (resname, NULL);
       else
-        filename = make_filename (opt.homedir, resname, NULL);
+        filename = make_filename (gnupg_homedir (), resname, NULL);
     }
   else
     filename = xstrdup (resname);
 
   if (!force)
-    force = secret? !any_secret : !any_public;
+    force = !any_registered;
 
   /* see whether we can determine the filetype */
   if (rt == KEYDB_RESOURCE_TYPE_NONE)
@@ -312,26 +326,29 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
     {
     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_KEYBOX:
-      rc = maybe_create_keybox (filename, force, auto_created);
-      if (rc)
+      err = maybe_create_keybox (filename, force, auto_created);
+      if (err)
         goto leave;
       /* Now register the file */
       {
-        void *token = keybox_register_file (filename, secret);
-        if (!token)
-          ; /* already registered - ignore it */
+        void *token;
+
+        err = keybox_register_file (filename, 0, &token);
+        if (gpg_err_code (err) == GPG_ERR_EEXIST)
+          ; /* Already registered - ignore.  */
+        else if (err)
+          ; /* Other error.  */
         else if (used_resources >= MAX_KEYDB_RESOURCES)
-          rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+          err = gpg_error (GPG_ERR_RESOURCE_LIMIT);
         else
           {
             all_resources[used_resources].type = rt;
             all_resources[used_resources].u.kr = NULL; /* Not used here */
             all_resources[used_resources].token = token;
-            all_resources[used_resources].secret = secret;
 
             all_resources[used_resources].lockhandle
               = dotlock_create (filename, 0);
@@ -341,7 +358,7 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
             /* Do a compress run if needed and the file is not locked. */
             if (!dotlock_take (all_resources[used_resources].lockhandle, 0))
               {
-                KEYBOX_HANDLE kbxhd = keybox_new_x509 (token, secret);
+                KEYBOX_HANDLE kbxhd = keybox_new_x509 (token, 0);
 
                 if (kbxhd)
                   {
@@ -358,26 +375,28 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
 
     default:
       log_error ("resource type of '%s' not supported\n", url);
-      rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+      err = gpg_error (GPG_ERR_NOT_SUPPORTED);
       goto leave;
     }
 
   /* fixme: check directory permissions and print a warning */
 
  leave:
-  if (rc)
-    log_error ("keyblock resource '%s': %s\n", filename, gpg_strerror(rc));
-  else if (secret)
-    any_secret = 1;
+  if (err)
+    {
+      log_error ("keyblock resource '%s': %s\n", filename, gpg_strerror (err));
+      gpgsm_status_with_error (ctrl, STATUS_ERROR,
+                               "add_keyblock_resource", err);
+    }
   else
-    any_public = 1;
+    any_registered = 1;
   xfree (filename);
-  return rc;
+  return err;
 }
 
 
 KEYDB_HANDLE
-keydb_new (int secret)
+keydb_new (void)
 {
   KEYDB_HANDLE hd;
   int i, j;
@@ -389,8 +408,6 @@ keydb_new (int secret)
   assert (used_resources <= MAX_KEYDB_RESOURCES);
   for (i=j=0; i < used_resources; i++)
     {
-      if (!all_resources[i].secret != !secret)
-        continue;
       switch (all_resources[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
@@ -398,9 +415,8 @@ keydb_new (int secret)
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           hd->active[j].type   = all_resources[i].type;
           hd->active[j].token  = all_resources[i].token;
-          hd->active[j].secret = all_resources[i].secret;
           hd->active[j].lockhandle = all_resources[i].lockhandle;
-          hd->active[j].u.kr = keybox_new_x509 (all_resources[i].token, secret);
+          hd->active[j].u.kr = keybox_new_x509 (all_resources[i].token, 0);
           if (!hd->active[j].u.kr)
             {
               xfree (hd);
@@ -478,7 +494,7 @@ keydb_get_resource_name (KEYDB_HANDLE hd)
   return s? s: "";
 }
 
-/* Switch the handle into ephemeral mode and return the orginal value. */
+/* Switch the handle into ephemeral mode and return the original value. */
 int
 keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
 {
@@ -903,8 +919,6 @@ keydb_rebuild_caches (void)
 
   for (i=0; i < used_resources; i++)
     {
-      if (all_resources[i].secret)
-        continue;
       switch (all_resources[i].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
@@ -924,10 +938,11 @@ keydb_rebuild_caches (void)
 /*
  * Start the next search on this handle right at the beginning
  */
-int
+gpg_error_t
 keydb_search_reset (KEYDB_HANDLE hd)
 {
-  int i, rc = 0;
+  int i;
+  gpg_error_t rc = 0;
 
   if (!hd)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -946,8 +961,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
           break;
         }
     }
-  return rc; /* fixme: we need to map error codes or share them with
-                all modules*/
+  return rc;
 }
 
 /*
@@ -955,7 +969,8 @@ keydb_search_reset (KEYDB_HANDLE hd)
  * for a keyblock which contains one of the keys described in the DESC array.
  */
 int
-keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
+keydb_search (ctrl_t ctrl, KEYDB_HANDLE hd,
+              KEYDB_SEARCH_DESC *desc, size_t ndesc)
 {
   int rc = -1;
   unsigned long skipped;
@@ -963,6 +978,13 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
   if (!hd)
     return gpg_error (GPG_ERR_INV_VALUE);
 
+  if (!any_registered)
+    {
+      gpgsm_status_with_error (ctrl, STATUS_ERROR, "keydb_search",
+                               gpg_error (GPG_ERR_KEYRING_OPEN));
+      return gpg_error (GPG_ERR_NOT_FOUND);
+    }
+
   while (rc == -1 && hd->current >= 0 && hd->current < hd->used)
     {
       switch (hd->active[hd->current].type)
@@ -976,8 +998,10 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
                               NULL, &skipped);
           break;
         }
-      if (rc == -1) /* EOF -> switch to next resource */
-        hd->current++;
+      if (rc == -1 || gpg_err_code (rc) == GPG_ERR_EOF)
+        { /* EOF -> switch to next resource */
+          hd->current++;
+        }
       else if (!rc)
         hd->found = hd->current;
     }
@@ -987,27 +1011,27 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
 
 
 int
-keydb_search_first (KEYDB_HANDLE hd)
+keydb_search_first (ctrl_t ctrl, KEYDB_HANDLE hd)
 {
   KEYDB_SEARCH_DESC desc;
 
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_FIRST;
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_next (KEYDB_HANDLE hd)
+keydb_search_next (ctrl_t ctrl, KEYDB_HANDLE hd)
 {
   KEYDB_SEARCH_DESC desc;
 
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_NEXT;
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
+keydb_search_kid (ctrl_t ctrl, KEYDB_HANDLE hd, u32 *kid)
 {
   KEYDB_SEARCH_DESC desc;
 
@@ -1017,22 +1041,22 @@ keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
   desc.mode = KEYDB_SEARCH_MODE_LONG_KID;
   desc.u.kid[0] = kid[0];
   desc.u.kid[1] = kid[1];
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
+keydb_search_fpr (ctrl_t ctrl, 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, 20);
-  return keydb_search (hd, &desc, 1);
+  return keydb_search (ctrl, hd, &desc, 1);
 }
 
 int
-keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
+keydb_search_issuer (ctrl_t ctrl, KEYDB_HANDLE hd, const char *issuer)
 {
   KEYDB_SEARCH_DESC desc;
   int rc;
@@ -1040,12 +1064,12 @@ keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_ISSUER;
   desc.u.name = issuer;
-  rc = keydb_search (hd, &desc, 1);
+  rc = keydb_search (ctrl, hd, &desc, 1);
   return rc;
 }
 
 int
-keydb_search_issuer_sn (KEYDB_HANDLE hd,
+keydb_search_issuer_sn (ctrl_t ctrl, KEYDB_HANDLE hd,
                         const char *issuer, ksba_const_sexp_t serial)
 {
   KEYDB_SEARCH_DESC desc;
@@ -1064,12 +1088,12 @@ keydb_search_issuer_sn (KEYDB_HANDLE hd,
     return gpg_error (GPG_ERR_INV_VALUE);
   desc.sn = s+1;
   desc.u.name = issuer;
-  rc = keydb_search (hd, &desc, 1);
+  rc = keydb_search (ctrl, hd, &desc, 1);
   return rc;
 }
 
 int
-keydb_search_subject (KEYDB_HANDLE hd, const char *name)
+keydb_search_subject (ctrl_t ctrl, KEYDB_HANDLE hd, const char *name)
 {
   KEYDB_SEARCH_DESC desc;
   int rc;
@@ -1077,7 +1101,7 @@ keydb_search_subject (KEYDB_HANDLE hd, const char *name)
   memset (&desc, 0, sizeof desc);
   desc.mode = KEYDB_SEARCH_MODE_SUBJECT;
   desc.u.name = name;
-  rc = keydb_search (hd, &desc, 1);
+  rc = keydb_search (ctrl, hd, &desc, 1);
   return rc;
 }
 
@@ -1088,7 +1112,7 @@ keydb_search_subject (KEYDB_HANDLE hd, const char *name)
    If EXISTED is not NULL it will be set to true if the certificate
    was already in the DB. */
 int
-keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
+keydb_store_cert (ctrl_t ctrl, ksba_cert_t cert, int ephemeral, int *existed)
 {
   KEYDB_HANDLE kh;
   int rc;
@@ -1103,21 +1127,22 @@ keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
       return gpg_error (GPG_ERR_GENERAL);
     }
 
-  kh = keydb_new (0);
+  kh = keydb_new ();
   if (!kh)
     {
       log_error (_("failed to allocate keyDB handle\n"));
       return gpg_error (GPG_ERR_ENOMEM);;
     }
 
-  if (ephemeral)
-    keydb_set_ephemeral (kh, 1);
+  /* Set the ephemeral flag so that the search looks at all
+     records.  */
+  keydb_set_ephemeral (kh, 1);
 
   rc = lock_all (kh);
   if (rc)
     return rc;
 
-  rc = keydb_search_fpr (kh, fpr);
+  rc = keydb_search_fpr (ctrl, kh, fpr);
   if (rc != -1)
     {
       keydb_release (kh);
@@ -1125,6 +1150,19 @@ keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
         {
           if (existed)
             *existed = 1;
+          if (!ephemeral)
+            {
+              /* Remove ephemeral flags from existing certificate to "store"
+                 it permanently. */
+              rc = keydb_set_cert_flags (ctrl, cert, 1, KEYBOX_FLAG_BLOB, 0,
+                                         KEYBOX_FLAG_BLOB_EPHEMERAL, 0);
+              if (rc)
+                {
+                  log_error ("clearing ephemeral flag failed: %s\n",
+                             gpg_strerror (rc));
+                  return rc;
+                }
+            }
           return 0; /* okay */
         }
       log_error (_("problem looking for existing certificate: %s\n"),
@@ -1132,6 +1170,10 @@ keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
       return rc;
     }
 
+  /* Reset the ephemeral flag if not requested.  */
+  if (!ephemeral)
+    keydb_set_ephemeral (kh, 0);
+
   rc = keydb_locate_writable (kh, 0);
   if (rc)
     {
@@ -1156,7 +1198,7 @@ keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
    transaction by locating the certificate in the DB and updating the
    flags. */
 gpg_error_t
-keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
+keydb_set_cert_flags (ctrl_t ctrl, ksba_cert_t cert, int ephemeral,
                       int which, int idx,
                       unsigned int mask, unsigned int value)
 {
@@ -1171,7 +1213,7 @@ keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
       return gpg_error (GPG_ERR_GENERAL);
     }
 
-  kh = keydb_new (0);
+  kh = keydb_new ();
   if (!kh)
     {
       log_error (_("failed to allocate keyDB handle\n"));
@@ -1189,7 +1231,7 @@ keydb_set_cert_flags (ksba_cert_t cert, int ephemeral,
       return err;
     }
 
-  err = keydb_search_fpr (kh, fpr);
+  err = keydb_search_fpr (ctrl, kh, fpr);
   if (err)
     {
       if (err == -1)
@@ -1242,7 +1284,7 @@ keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
 
   (void)ctrl;
 
-  hd = keydb_new (0);
+  hd = keydb_new ();
   if (!hd)
     {
       log_error ("keydb_new failed\n");
@@ -1273,11 +1315,7 @@ keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
         {
           rc = classify_user_id (sl->d, desc+ndesc, 0);
           if (rc)
-            {
-              log_error ("key '%s' not found: %s\n",
-                         sl->d, gpg_strerror (rc));
-              rc = 0;
-            }
+            log_error ("key '%s' not found: %s\n", sl->d, gpg_strerror (rc));
           else
             ndesc++;
         }
@@ -1290,7 +1328,7 @@ keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
       goto leave;
     }
 
-  while (!(rc = keydb_search (hd, desc, ndesc)))
+  while (!(rc = keydb_search (ctrl, hd, desc, ndesc)))
     {
       if (!names)
         desc[0].mode = KEYDB_SEARCH_MODE_NEXT;