dns-cert.c: Use constants for better readability.
[gnupg.git] / sm / keydb.c
index b12ba1d..86301b3 100644 (file)
@@ -1,11 +1,11 @@
 /* keydb.c - key database dispatcher
- * Copyright (C) 2001 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
  *
  * 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 2 of the License, or
+ * 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,
@@ -14,8 +14,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, write to the Free Software
- * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 #include <sys/stat.h>
 #include <unistd.h>
 
-#include "util.h"
-#include "options.h"
-#include "keybox.h"
-#include "keydb.h" 
+#include "gpgsm.h"
+#include "../kbx/keybox.h"
+#include "keydb.h"
 #include "i18n.h"
 
-static struct {
-  const char *homedir;
-  int dry_run;
-  int quiet;
-  int verbose;
-  int preserve_permissions;
-} keydbopt;
-
 static int active_handles;
 
 typedef enum {
@@ -57,6 +47,7 @@ struct resource_item {
   } u;
   void *token;
   int secret;
+  dotlock_t lockhandle;
 };
 
 static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
@@ -66,6 +57,7 @@ struct keydb_handle {
   int locked;
   int found;
   int current;
+  int is_ephemeral;
   int used; /* items in active */
   struct resource_item active[MAX_KEYDB_RESOURCES];
 };
@@ -77,181 +69,203 @@ static void unlock_all (KEYDB_HANDLE hd);
 
 /*
  * Register a resource (which currently may only be a keybox file).
- * The first keybox which is added by this function is
- * created if it does not exist.
- * Note: this function may be called before secure memory is
- * available.
+ * The first keybox which is added by this function is created if it
+ * does not exist.  If AUTO_CREATED is not NULL it will be set to true
+ * if the function has created a a new keybox.
  */
 int
-keydb_add_resource (const char *url, int force, int secret)
+keydb_add_resource (const char *url, int force, int secret, int *auto_created)
 {
-    static int any_secret, any_public;
-    const char *resname = url;
-    IOBUF iobuf = NULL;
-    char *filename = NULL;
-    int rc = 0;
-    KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
-    const char *created_fname = NULL;
-
-    /* Do we have an URL?
-     * gnupg-ring:filename  := this is a plain keybox
-     * filename := See what is is, but create as plain keybox.
-     */
-    if (strlen (resname) > 11) {
-       if (!strncmp( resname, "gnupg-ring:", 11) ) {
-           rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-           resname += 11;
+  static int any_secret, any_public;
+  const char *resname = url;
+  char *filename = NULL;
+  int rc = 0;
+  FILE *fp;
+  KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
+
+  if (auto_created)
+    *auto_created = 0;
+
+  /* Do we have an URL?
+     gnupg-kbx:filename := this is a plain keybox
+     filename := See what is is, but create as plain keybox.
+  */
+  if (strlen (resname) > 10)
+    {
+      if (!strncmp (resname, "gnupg-kbx:", 10) )
+        {
+          rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+          resname += 10;
        }
-      #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
-       else if (strchr (resname, ':')) {
-           log_error ("invalid key resource URL `%s'\n", url );
-           rc = G10ERR_GENERAL;
-           goto leave;
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
+      else if (strchr (resname, ':'))
+        {
+          log_error ("invalid key resource URL `%s'\n", url );
+          rc = gpg_error (GPG_ERR_GENERAL);
+          goto leave;
        }
-      #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
+#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
     }
 
-    if (*resname != DIRSEP_C ) { /* do tilde expansion etc */
-       if (strchr(resname, DIRSEP_C) )
-           filename = make_filename (resname, NULL);
-       else
-           filename = make_filename (keydbopt.homedir, resname, NULL);
+  if (*resname != DIRSEP_C )
+    { /* do tilde expansion etc */
+      if (strchr(resname, DIRSEP_C) )
+        filename = make_filename (resname, NULL);
+      else
+        filename = make_filename (opt.homedir, resname, NULL);
     }
-    else
-       filename = m_strdup (resname);
+  else
+    filename = xstrdup (resname);
 
-    if (!force)
-       force = secret? !any_secret : !any_public;
+  if (!force)
+    force = secret? !any_secret : !any_public;
 
-    /* see whether we can determine the filetype */
-    if (rt == KEYDB_RESOURCE_TYPE_NONE) {
-       FILE *fp = fopen( filename, "rb" );
-
-       if (fp) {
-           u32 magic;
-
-           if (fread( &magic, 4, 1, fp) == 1 ) {
-               if (magic == 0x13579ace || magic == 0xce9a5713)
-                   ; /* GDBM magic - no more support */
-               else
-                   rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-           }
-           else /* maybe empty: assume ring */
-               rt = KEYDB_RESOURCE_TYPE_KEYBOX;
-           fclose( fp );
-       }
-       else /* no file yet: create ring */
-           rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+  /* see whether we can determine the filetype */
+  if (rt == KEYDB_RESOURCE_TYPE_NONE)
+    {
+      FILE *fp2 = fopen( filename, "rb" );
+
+      if (fp2) {
+        u32 magic;
+
+        /* FIXME: check for the keybox magic */
+        if (fread( &magic, 4, 1, fp2) == 1 )
+          {
+            if (magic == 0x13579ace || magic == 0xce9a5713)
+              ; /* GDBM magic - no more support */
+            else
+              rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+          }
+        else /* maybe empty: assume ring */
+          rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+        fclose (fp2);
+      }
+      else /* no file yet: create ring */
+        rt = KEYDB_RESOURCE_TYPE_KEYBOX;
     }
 
-    switch (rt) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-       log_error ("unknown type of key resource `%s'\n", url );
-       rc = G10ERR_GENERAL;
-       goto leave;
-
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-       iobuf = iobuf_open (filename);
-       if (!iobuf && !force) {
-           rc = G10ERR_OPEN_FILE;
-           goto leave;
-       }
+  switch (rt)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      log_error ("unknown type of key resource `%s'\n", url );
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
+
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      fp = fopen (filename, "rb");
+      if (!fp && !force)
+        {
+          rc = gpg_error (gpg_err_code_from_errno (errno));
+          goto leave;
+        }
 
-       if (!iobuf) {
-           char *last_slash_in_filename;
-
-           last_slash_in_filename = strrchr (filename, DIRSEP_C);
-           *last_slash_in_filename = 0;
-
-           if (access(filename, F_OK)) {
-               /* on the first time we try to create the default homedir and
-                * in this case the process will be terminated, so that on the
-                * next invocation it can read the options file in on startup
-                */
-               try_make_homedir (filename);
-               rc = G10ERR_OPEN_FILE;
-               *last_slash_in_filename = DIRSEP_C;
-               goto leave;
+      if (!fp)
+        { /* no file */
+#if 0 /* no autocreate of the homedirectory yet */
+          {
+            char *last_slash_in_filename;
+
+            last_slash_in_filename = strrchr (filename, DIRSEP_C);
+            *last_slash_in_filename = 0;
+            if (access (filename, F_OK))
+              { /* on the first time we try to create the default
+                   homedir and in this case the process will be
+                   terminated, so that on the next invocation can
+                   read the options file in on startup */
+                try_make_homedir (filename);
+                rc = gpg_error (GPG_ERR_FILE_OPEN_ERROR);
+                *last_slash_in_filename = DIRSEP_C;
+                goto leave;
+              }
+            *last_slash_in_filename = DIRSEP_C;
+          }
+#endif
+          fp = fopen (filename, "w");
+          if (!fp)
+            {
+              rc = gpg_error (gpg_err_code_from_errno (errno));
+              log_error (_("error creating keybox `%s': %s\n"),
+                         filename, strerror(errno));
+              if (errno == ENOENT)
+                log_info (_("you may want to start the gpg-agent first\n"));
+              goto leave;
            }
 
-           *last_slash_in_filename = DIRSEP_C;
-
-           iobuf = iobuf_create (filename);
-           if (!iobuf) {
-               log_error ( _("error creating keybox `%s': %s\n"),
-                            filename, strerror(errno));
-               rc = G10ERR_OPEN_FILE;
-               goto leave;
-           }
-           else {
-             #ifndef HAVE_DOSISH_SYSTEM
-               if (secret && !keydbopt.preserve_permissionws) {
-                   if (chmod (filename, S_IRUSR | S_IWUSR) ) {
-                       log_error (_("changing permission of "
-                                     " `%s' failed: %s\n"),
-                                   filename, strerror(errno) );
-                       rc = G10ERR_WRITE_FILE;
-                       goto leave;
-                   }
-               }
-             #endif
-               if (!keydbopt.quiet)
-                    log_info (_("keybox `%s' created\n"), filename);
-                created_fname = filename;
-           }
+          if (!opt.quiet)
+            log_info (_("keybox `%s' created\n"), filename);
+          if (auto_created)
+            *auto_created = 1;
        }
-       iobuf_close (iobuf);
-       iobuf = NULL;
-        if (created_fname) /* must invalidate that ugly cache */
-            iobuf_ioctl (NULL, 2, 0, (char*)created_fname);
+       fclose (fp);
+       fp = NULL;
+        /* now register the file */
         {
-          void *token = keybox_register_filename (filename, secret);
+
+          void *token = keybox_register_file (filename, secret);
           if (!token)
             ; /* already registered - ignore it */
           else if (used_resources >= MAX_KEYDB_RESOURCES)
-              rc = G10ERR_RESOURCE_LIMIT;
-          else 
+            rc = 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);
+              if (!all_resources[used_resources].lockhandle)
+                log_fatal ( _("can't create lock for `%s'\n"), filename);
+
+              /* 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 (token, secret);
+
+                  if (kbxhd)
+                    {
+                      keybox_compress (kbxhd);
+                      keybox_release (kbxhd);
+                    }
+                  dotlock_release (all_resources[used_resources].lockhandle);
+                }
+
               used_resources++;
             }
         }
-       break;
 
-      default:
-       log_error ("resource type of `%s' not supported\n", url);
-       rc = G10ERR_GENERAL;
-       goto leave;
+
+       break;
+    default:
+      log_error ("resource type of `%s' not supported\n", url);
+      rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
+      goto leave;
     }
 
-    /* fixme: check directory permissions and print a warning */
+  /* fixme: check directory permissions and print a warning */
 
 leave:
-    if (rc)
-       log_error ("keyblock resource `%s': %s\n", filename, g10_errstr(rc));
-    else if (secret)
-       any_secret = 1;
-    else
-       any_public = 1;
-    m_free (filename);
-    return rc;
+ leave:
+  if (rc)
+    log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror(rc));
+  else if (secret)
+    any_secret = 1;
+  else
+    any_public = 1;
+  xfree (filename);
+  return rc;
 }
 
 
-
-
 KEYDB_HANDLE
 keydb_new (int secret)
 {
   KEYDB_HANDLE hd;
   int i, j;
-  
-  hd = m_alloc_clear (sizeof *hd);
+
+  hd = xcalloc (1, sizeof *hd);
   hd->found = -1;
-  
+
   assert (used_resources <= MAX_KEYDB_RESOURCES);
   for (i=j=0; i < used_resources; i++)
     {
@@ -265,142 +279,209 @@ keydb_new (int secret)
           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 (all_resources[i].token, secret);
-          if (!hd->active[j].u.kr) {
-            m_free (hd);
-            return NULL; /* fixme: release all previously allocated handles*/
-          }
+          if (!hd->active[j].u.kr)
+            {
+              xfree (hd);
+              return NULL; /* fixme: release all previously allocated handles*/
+            }
           j++;
           break;
         }
     }
   hd->used = j;
-  
+
   active_handles++;
   return hd;
 }
 
-void 
+void
 keydb_release (KEYDB_HANDLE hd)
 {
-    int i;
+  int i;
 
-    if (!hd)
-        return;
-    assert (active_handles > 0);
-    active_handles--;
+  if (!hd)
+    return;
+  assert (active_handles > 0);
+  active_handles--;
 
-    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_KEYBOX:
-            keybox_release (hd->active[i].u.kr);
-            break;
+  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_KEYBOX:
+          keybox_release (hd->active[i].u.kr);
+          break;
         }
     }
 
-    m_free (hd);
+    xfree (hd);
 }
 
 
-/*
- * Return the name of the current resource.  This is function first
- * looks for the last found found, then for the current search
- * position, and last returns the first available resource.  The
- * returned string is only valid as long as the handle exists.  This
- * function does only return NULL if no handle is specified, in all
- * other error cases an empty string is returned.
- */
+/* Return the name of the current resource.  This is function first
+   looks for the last found found, then for the current search
+   position, and last returns the first available resource.  The
+   returned string is only valid as long as the handle exists.  This
+   function does only return 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;
+  int idx;
+  const char *s = NULL;
 
-    if (!hd) 
-        return 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;
+  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_KEYBOX:
-        s = keybox_get_resource_name (hd->active[idx].u.kr);
-        break;
+  switch (hd->active[idx].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      s = NULL;
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      s = keybox_get_resource_name (hd->active[idx].u.kr);
+      break;
+    }
+
+  return s? s: "";
+}
+
+/* Switch the handle into ephemeral mode and return the orginal value. */
+int
+keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
+{
+  int i;
+
+  if (!hd)
+    return 0;
+
+  yes = !!yes;
+  if (hd->is_ephemeral != yes)
+    {
+      for (i=0; i < hd->used; i++)
+        {
+          switch (hd->active[i].type)
+            {
+            case KEYDB_RESOURCE_TYPE_NONE:
+              break;
+            case KEYDB_RESOURCE_TYPE_KEYBOX:
+              keybox_set_ephemeral (hd->active[i].u.kr, yes);
+              break;
+            }
+        }
     }
 
-    return s? s: "";
+  i = hd->is_ephemeral;
+  hd->is_ephemeral = yes;
+  return i;
 }
 
 
+/* If the keyring has not yet been locked, lock it now.  This
+   operation is required before any update operation; it is optional
+   for an insert operation.  The lock is released with
+   keydb_released. */
+gpg_error_t
+keydb_lock (KEYDB_HANDLE hd)
+{
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_HANDLE);
+  if (hd->locked)
+    return 0; /* Already locked. */
+  return lock_all (hd);
+}
+
 
-static int 
+\f
+static int
 lock_all (KEYDB_HANDLE hd)
 {
-    int i, rc = 0;
-
-    for (i=0; !rc && i < hd->used; i++) {
-        switch (hd->active[i].type) {
-          case KEYDB_RESOURCE_TYPE_NONE:
-            break;
-          case KEYDB_RESOURCE_TYPE_KEYBOX:
-            rc = keybox_lock (hd->active[i].u.kr, 1);
-            break;
+  int i, rc = 0;
+
+  /* Fixme: This locking scheme may lead to 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. */
+  for (i=0; i < hd->used; i++)
+    {
+      switch (hd->active[i].type)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          if (hd->active[i].lockhandle)
+            rc = dotlock_take (hd->active[i].lockhandle, -1);
+          break;
         }
+      if (rc)
+        break;
     }
 
-    if (rc) {
+    if (rc)
+      {
         /* revert the already set locks */
-        for (i--; i >= 0; i--) {
-            switch (hd->active[i].type) {
+        for (i--; i >= 0; i--)
+          {
+            switch (hd->active[i].type)
+              {
               case KEYDB_RESOURCE_TYPE_NONE:
                 break;
               case KEYDB_RESOURCE_TYPE_KEYBOX:
-                keybox_lock (hd->active[i].u.kr, 0);
+                if (hd->active[i].lockhandle)
+                  dotlock_release (hd->active[i].lockhandle);
                 break;
-            }
-        }
-    }
+              }
+          }
+      }
     else
-        hd->locked = 1;
+      hd->locked = 1;
 
-    return rc;
+    /* make_dotlock () does not yet guarantee that errno is set, thus
+       we can't rely on the error reason and will simply use
+       EACCES. */
+    return rc? gpg_error (GPG_ERR_EACCES) : 0;
 }
 
 static void
 unlock_all (KEYDB_HANDLE hd)
 {
-    int i;
+  int i;
 
-    if (!hd->locked)
-        return;
-
-    for (i=hd->used-1; i >= 0; i--) {
-        switch (hd->active[i].type) {
-          case KEYDB_RESOURCE_TYPE_NONE:
-            break;
-          case KEYDB_RESOURCE_TYPE_KEYBOX:
-            keybox_lock (hd->active[i].u.kr, 0);
-            break;
+  if (!hd->locked)
+    return;
+
+  for (i=hd->used-1; i >= 0; i--)
+    {
+      switch (hd->active[i].type)
+        {
+        case KEYDB_RESOURCE_TYPE_NONE:
+          break;
+        case KEYDB_RESOURCE_TYPE_KEYBOX:
+          if (hd->active[i].lockhandle)
+            dotlock_release (hd->active[i].lockhandle);
+          break;
         }
     }
-    hd->locked = 0;
+  hd->locked = 0;
 }
 
-
+\f
+#if 0
 /*
  * Return the last found keybox.  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 public key used to locate the keyblock or flag bit 1 set for
  * the user ID node.
  */
 int
@@ -411,7 +492,7 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
     if (!hd)
         return G10ERR_INV_ARG;
 
-    if ( hd->found < 0 || hd->found >= hd->used) 
+    if ( hd->found < 0 || hd->found >= hd->used)
         return -1; /* nothing found */
 
     switch (hd->active[hd->found].type) {
@@ -426,7 +507,7 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
     return rc;
 }
 
-/* 
+/*
  * update the current keyblock with KB
  */
 int
@@ -437,15 +518,14 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     if (!hd)
         return G10ERR_INV_ARG;
 
-    if ( hd->found < 0 || hd->found >= hd->used) 
+    if ( hd->found < 0 || hd->found >= hd->used)
         return -1; /* nothing found */
 
-    if( keydbopt.dry_run )
+    if( opt.dry_run )
        return 0;
 
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
+    if (!hd->locked)
+      return gpg_error (GPG_ERR_NOT_LOCKED);
 
     switch (hd->active[hd->found].type) {
       case KEYDB_RESOURCE_TYPE_NONE:
@@ -461,8 +541,8 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
 }
 
 
-/* 
- * Insert a new KB into one of the resources. 
+/*
+ * Insert a new KB into one of the resources.
  */
 int
 keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
@@ -470,15 +550,15 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     int rc = -1;
     int idx;
 
-    if (!hd) 
+    if (!hd)
         return G10ERR_INV_ARG;
 
-    if( keydbopt.dry_run )
+    if( opt.dry_run )
        return 0;
 
-    if ( hd->found >= 0 && hd->found < hd->used) 
+    if ( hd->found >= 0 && hd->found < hd->used)
         idx = hd->found;
-    else if ( hd->current >= 0 && hd->current < hd->used) 
+    else if ( hd->current >= 0 && hd->current < hd->used)
         idx = hd->current;
     else
         return G10ERR_GENERAL;
@@ -500,62 +580,240 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     return rc;
 }
 
+#endif /*disabled code*/
+
+
+\f
+/*
+  Return the last found object.  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
+keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert)
+{
+  int rc = 0;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
+    return -1; /* nothing found */
 
-/* 
- * The current keyblock will be deleted.
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      rc = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      rc = keybox_get_cert (hd->active[hd->found].u.kr, r_cert);
+      break;
+    }
+
+  return rc;
+}
+
+/* Return a flag of the last found object. WHICH is the flag requested;
+   it should be one of the KEYBOX_FLAG_ values.  If the operation is
+   successful, the flag value will be stored at the address given by
+   VALUE.  Return 0 on success or an error code. */
+gpg_error_t
+keydb_get_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int *value)
+{
+  int err = 0;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
+    return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      err = keybox_get_flags (hd->active[hd->found].u.kr, which, idx, value);
+      break;
+    }
+
+  return err;
+}
+
+/* Set a flag of the last found object. WHICH is the flag to be set; it
+   should be one of the KEYBOX_FLAG_ values.  If the operation is
+   successful, the flag value will be stored in the keybox.  Note,
+   that some flag values can't be updated and thus may return an
+   error, some other flag values may be masked out before an update.
+   Returns 0 on success or an error code. */
+gpg_error_t
+keydb_set_flags (KEYDB_HANDLE hd, int which, int idx, unsigned int value)
+{
+  int err = 0;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
+    return gpg_error (GPG_ERR_NOTHING_FOUND);
+
+  if (!hd->locked)
+    return gpg_error (GPG_ERR_NOT_LOCKED);
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      err = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      err = keybox_set_flags (hd->active[hd->found].u.kr, which, idx, value);
+      break;
+    }
+
+  return err;
+}
+
+/*
+ * Insert a new Certificate into one of the resources.
  */
 int
-keydb_delete_keyblock (KEYDB_HANDLE hd)
+keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
 {
-    int rc = -1;
+  int rc = -1;
+  int idx;
+  unsigned char digest[20];
 
-    if (!hd)
-        return G10ERR_INV_ARG;
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-    if ( hd->found < 0 || hd->found >= hd->used) 
-        return -1; /* nothing found */
+  if (opt.dry_run)
+    return 0;
 
-    if( keydbopt.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);
 
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
+  if (!hd->locked)
+    return gpg_error (GPG_ERR_NOT_LOCKED);
 
-    switch (hd->active[hd->found].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-        rc = keybox_delete_keyblock (hd->active[hd->found].u.kr);
-        break;
+  gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
+
+  switch (hd->active[idx].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      rc = gpg_error (GPG_ERR_GENERAL);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      rc = keybox_insert_cert (hd->active[idx].u.kr, cert, digest);
+      break;
     }
 
-    unlock_all (hd);
+  unlock_all (hd);
+  return rc;
+}
+
+
+
+/* Update the current keyblock with KB.  */
+int
+keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
+{
+  int rc = 0;
+  unsigned char digest[20];
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
+    return -1; /* nothing found */
+
+  if (opt.dry_run)
+    return 0;
+
+  rc = lock_all (hd);
+  if (rc)
     return rc;
+
+  gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      rc = gpg_error (GPG_ERR_GENERAL); /* oops */
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      rc = keybox_update_cert (hd->active[hd->found].u.kr, cert, digest);
+      break;
+    }
+
+  unlock_all (hd);
+  return rc;
+}
+
+
+/*
+ * The current keyblock or cert will be deleted.
+ */
+int
+keydb_delete (KEYDB_HANDLE hd, int unlock)
+{
+  int rc = -1;
+
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
+
+  if ( hd->found < 0 || hd->found >= hd->used)
+    return -1; /* nothing found */
+
+  if( opt.dry_run )
+    return 0;
+
+  if (!hd->locked)
+    return gpg_error (GPG_ERR_NOT_LOCKED);
+
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      rc = gpg_error (GPG_ERR_GENERAL);
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      rc = keybox_delete (hd->active[hd->found].u.kr);
+      break;
+    }
+
+  if (unlock)
+    unlock_all (hd);
+  return rc;
 }
 
+
 \f
 /*
  * Locate the default writable key resource, so that the next
  * operation (which is only relevant for inserts) will be done on this
- * resource.  
+ * resource.
  */
 int
 keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
 {
   int rc;
-  
+
+  (void)reserved;
+
   if (!hd)
-    return G10ERR_INV_ARG;
-  
+    return gpg_error (GPG_ERR_INV_VALUE);
+
   rc = keydb_search_reset (hd); /* this does reset hd->current */
   if (rc)
     return rc;
 
-  for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++) 
+  for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
     {
-      switch (hd->active[hd->current].type) 
+      switch (hd->active[hd->current].type)
         {
         case KEYDB_RESOURCE_TYPE_NONE:
           BUG();
@@ -566,7 +824,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
           break;
         }
     }
-  
+
   return -1;
 }
 
@@ -576,8 +834,8 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
 void
 keydb_rebuild_caches (void)
 {
-  int i, rc;
-  
+  int i;
+
   for (i=0; i < used_resources; i++)
     {
       if (all_resources[i].secret)
@@ -587,10 +845,10 @@ keydb_rebuild_caches (void)
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
-          rc = keybox_rebuild_cache (all_resources[i].token);
-          if (rc)
-            log_error (_("failed to rebuild keybox cache: %s\n"),
-                       g10_errstr (rc));
+/*            rc = keybox_rebuild_cache (all_resources[i].token); */
+/*            if (rc) */
+/*              log_error (_("failed to rebuild keybox cache: %s\n"), */
+/*                         g10_errstr (rc)); */
           break;
         }
     }
@@ -598,106 +856,400 @@ keydb_rebuild_caches (void)
 
 
 
-/* 
+/*
  * Start the next search on this handle right at the beginning
  */
-int 
+int
 keydb_search_reset (KEYDB_HANDLE hd)
 {
-    int i, rc = 0;
+  int i, rc = 0;
 
-    if (!hd)
-        return G10ERR_INV_ARG;
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-    hd->current = 0; 
-    hd->found = -1;
-    /* and 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_KEYBOX:
-            rc = keybox_search_reset (hd->active[i].u.kr);
-            break;
+  hd->current = 0;
+  hd->found = -1;
+  /* and 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_KEYBOX:
+          rc = keybox_search_reset (hd->active[i].u.kr);
+          break;
         }
     }
-    return rc; 
+  return rc; /* fixme: we need to map error codes or share them with
+                all modules*/
 }
 
-
-/* 
+/*
  * Search through all keydb resources, starting at the current position,
  * for a keyblock which contains one of the keys described in the DESC array.
  */
-int 
+int
 keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
 {
-    int rc = -1;
+  int rc = -1;
 
-    if (!hd)
-        return G10ERR_INV_ARG;
+  if (!hd)
+    return gpg_error (GPG_ERR_INV_VALUE);
 
-    while (rc == -1 && hd->current >= 0 && 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_KEYBOX:
-            rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc);
-            break;
+  while (rc == -1 && hd->current >= 0 && 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_KEYBOX:
+          rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc);
+          break;
         }
-        if (rc == -1) /* EOF -> switch to next resource */
-            hd->current++; 
-        else if (!rc)
-            hd->found = hd->current;
+      if (rc == -1) /* EOF -> switch to next resource */
+        hd->current++;
+      else if (!rc)
+        hd->found = hd->current;
     }
 
-    return rc; 
+  return rc;
 }
 
 
 int
 keydb_search_first (KEYDB_HANDLE hd)
 {
-    KEYDB_SEARCH_DESC desc;
+  KEYDB_SEARCH_DESC desc;
 
-    memset (&desc, 0, sizeof desc);
-    desc.mode = KEYDB_SEARCH_MODE_FIRST;
-    return keydb_search (hd, &desc, 1);
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_FIRST;
+  return keydb_search (hd, &desc, 1);
 }
 
 int
 keydb_search_next (KEYDB_HANDLE hd)
 {
-    KEYDB_SEARCH_DESC desc;
+  KEYDB_SEARCH_DESC desc;
 
-    memset (&desc, 0, sizeof desc);
-    desc.mode = KEYDB_SEARCH_MODE_NEXT;
-    return keydb_search (hd, &desc, 1);
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_NEXT;
+  return keydb_search (hd, &desc, 1);
 }
 
 int
 keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
 {
-    KEYDB_SEARCH_DESC desc;
+  KEYDB_SEARCH_DESC desc;
+
+  (void)kid;
 
-    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);
+  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);
 }
 
 int
 keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
 {
-    KEYDB_SEARCH_DESC desc;
+  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);
+}
+
+int
+keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
+{
+  KEYDB_SEARCH_DESC desc;
+  int rc;
+
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_ISSUER;
+  desc.u.name = issuer;
+  rc = keydb_search (hd, &desc, 1);
+  return rc;
+}
+
+int
+keydb_search_issuer_sn (KEYDB_HANDLE hd,
+                        const char *issuer, ksba_const_sexp_t serial)
+{
+  KEYDB_SEARCH_DESC desc;
+  int rc;
+  const unsigned char *s;
+
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_ISSUER_SN;
+  s = serial;
+  if (*s !='(')
+    return gpg_error (GPG_ERR_INV_VALUE);
+  s++;
+  for (desc.snlen = 0; digitp (s); s++)
+    desc.snlen = 10*desc.snlen + atoi_1 (s);
+  if (*s !=':')
+    return gpg_error (GPG_ERR_INV_VALUE);
+  desc.sn = s+1;
+  desc.u.name = issuer;
+  rc = keydb_search (hd, &desc, 1);
+  return rc;
+}
+
+int
+keydb_search_subject (KEYDB_HANDLE hd, const char *name)
+{
+  KEYDB_SEARCH_DESC desc;
+  int rc;
+
+  memset (&desc, 0, sizeof desc);
+  desc.mode = KEYDB_SEARCH_MODE_SUBJECT;
+  desc.u.name = name;
+  rc = keydb_search (hd, &desc, 1);
+  return rc;
+}
+
+
+\f
+/* Store the certificate in the key DB but make sure that it does not
+   already exists.  We do this simply by comparing the fingerprint.
+   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_HANDLE kh;
+  int rc;
+  unsigned char fpr[20];
+
+  if (existed)
+    *existed = 0;
+
+  if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+    {
+      log_error (_("failed to get the fingerprint\n"));
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  kh = keydb_new (0);
+  if (!kh)
+    {
+      log_error (_("failed to allocate keyDB handle\n"));
+      return gpg_error (GPG_ERR_ENOMEM);;
+    }
+
+  if (ephemeral)
+    keydb_set_ephemeral (kh, 1);
+
+  rc = lock_all (kh);
+  if (rc)
+    return rc;
+
+  rc = keydb_search_fpr (kh, fpr);
+  if (rc != -1)
+    {
+      keydb_release (kh);
+      if (!rc)
+        {
+          if (existed)
+            *existed = 1;
+          return 0; /* okay */
+        }
+      log_error (_("problem looking for existing certificate: %s\n"),
+                 gpg_strerror (rc));
+      return rc;
+    }
 
-    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);
+  rc = keydb_locate_writable (kh, 0);
+  if (rc)
+    {
+      log_error (_("error finding writable keyDB: %s\n"), gpg_strerror (rc));
+      keydb_release (kh);
+      return rc;
+    }
+
+  rc = keydb_insert_cert (kh, cert);
+  if (rc)
+    {
+      log_error (_("error storing certificate: %s\n"), gpg_strerror (rc));
+      keydb_release (kh);
+      return rc;
+    }
+  keydb_release (kh);
+  return 0;
 }
 
 
+/* This is basically keydb_set_flags but it implements a complete
+   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,
+                      int which, int idx,
+                      unsigned int mask, unsigned int value)
+{
+  KEYDB_HANDLE kh;
+  gpg_error_t err;
+  unsigned char fpr[20];
+  unsigned int old_value;
+
+  if (!gpgsm_get_fingerprint (cert, 0, fpr, NULL))
+    {
+      log_error (_("failed to get the fingerprint\n"));
+      return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  kh = keydb_new (0);
+  if (!kh)
+    {
+      log_error (_("failed to allocate keyDB handle\n"));
+      return gpg_error (GPG_ERR_ENOMEM);;
+    }
+
+  if (ephemeral)
+    keydb_set_ephemeral (kh, 1);
+
+  err = keydb_lock (kh);
+  if (err)
+    {
+      log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+      keydb_release (kh);
+      return err;
+    }
+
+  err = keydb_search_fpr (kh, fpr);
+  if (err)
+    {
+      if (err == -1)
+        err = gpg_error (GPG_ERR_NOT_FOUND);
+      else
+        log_error (_("problem re-searching certificate: %s\n"),
+                   gpg_strerror (err));
+      keydb_release (kh);
+      return err;
+    }
 
+  err = keydb_get_flags (kh, which, idx, &old_value);
+  if (err)
+    {
+      log_error (_("error getting stored flags: %s\n"), gpg_strerror (err));
+      keydb_release (kh);
+      return err;
+    }
+
+  value = ((old_value & ~mask) | (value & mask));
+
+  if (value != old_value)
+    {
+      err = keydb_set_flags (kh, which, idx, value);
+      if (err)
+        {
+          log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+          keydb_release (kh);
+          return err;
+        }
+    }
+
+  keydb_release (kh);
+  return 0;
+}
+
+
+/* Reset all the certificate flags we have stored with the certificates
+   for performance reasons. */
+void
+keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
+{
+  gpg_error_t err;
+  KEYDB_HANDLE hd = NULL;
+  KEYDB_SEARCH_DESC *desc = NULL;
+  int ndesc;
+  strlist_t sl;
+  int rc=0;
+  unsigned int old_value, value;
+
+  (void)ctrl;
+
+  hd = keydb_new (0);
+  if (!hd)
+    {
+      log_error ("keydb_new failed\n");
+      goto leave;
+    }
+
+  if (!names)
+    ndesc = 1;
+  else
+    {
+      for (sl=names, ndesc=0; sl; sl = sl->next, ndesc++)
+        ;
+    }
+
+  desc = xtrycalloc (ndesc, sizeof *desc);
+  if (!ndesc)
+    {
+      log_error ("allocating memory failed: %s\n",
+                 gpg_strerror (out_of_core ()));
+      goto leave;
+    }
+
+  if (!names)
+    desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+  else
+    {
+      for (ndesc=0, sl=names; sl; sl = sl->next)
+        {
+          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;
+            }
+          else
+            ndesc++;
+        }
+    }
+
+  err = keydb_lock (hd);
+  if (err)
+    {
+      log_error (_("error locking keybox: %s\n"), gpg_strerror (err));
+      goto leave;
+    }
+
+  while (!(rc = keydb_search (hd, desc, ndesc)))
+    {
+      if (!names)
+        desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+      err = keydb_get_flags (hd, KEYBOX_FLAG_VALIDITY, 0, &old_value);
+      if (err)
+        {
+          log_error (_("error getting stored flags: %s\n"),
+                     gpg_strerror (err));
+          goto leave;
+        }
+
+      value = (old_value & ~VALIDITY_REVOKED);
+      if (value != old_value)
+        {
+          err = keydb_set_flags (hd, KEYBOX_FLAG_VALIDITY, 0, value);
+          if (err)
+            {
+              log_error (_("error storing flags: %s\n"), gpg_strerror (err));
+              goto leave;
+            }
+        }
+    }
+  if (rc && rc != -1)
+    log_error ("keydb_search failed: %s\n", gpg_strerror (rc));
+
+ leave:
+  xfree (desc);
+  keydb_release (hd);
+}