Fixed an email/DN bug.
[gnupg.git] / sm / keydb.c
index fe65565..1fbf9b6 100644 (file)
@@ -1,11 +1,11 @@
 /* keydb.c - key database dispatcher
- * Copyright (C) 2001, 2003 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>
@@ -33,8 +32,6 @@
 #include "keydb.h" 
 #include "i18n.h"
 
-#define DIRSEP_C '/'
-
 static int active_handles;
 
 typedef enum {
@@ -72,13 +69,12 @@ 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;
@@ -88,6 +84,9 @@ keydb_add_resource (const char *url, int force, int secret)
   KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
   const char *created_fname = NULL;
 
+  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.
@@ -188,12 +187,16 @@ keydb_add_resource (const char *url, int force, int secret)
               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;
            }
 
           if (!opt.quiet)
             log_info (_("keybox `%s' created\n"), filename);
           created_fname = filename;
+          if (auto_created)
+            *auto_created = 1;
        }
        fclose (fp);
        fp = NULL;
@@ -216,10 +219,25 @@ keydb_add_resource (const char *url, int force, int secret)
                 = create_dotlock (filename);
               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 (!make_dotlock (all_resources[used_resources].lockhandle, 0))
+                {
+                  KEYBOX_HANDLE kbxhd = keybox_new (token, secret);
+                  
+                  if (kbxhd)
+                    {
+                      keybox_compress (kbxhd);
+                      keybox_release (kbxhd);
+                    }
+                  release_dotlock (all_resources[used_resources].lockhandle);
+                }
                   
               used_resources++;
             }
         }
+
+
        break;
     default:
       log_error ("resource type of `%s' not supported\n", url);
@@ -373,6 +391,21 @@ keydb_set_ephemeral (KEYDB_HANDLE hd, int yes)
 }
 
 
+/* If the keyring has not yet been locked, lock it now.  This
+   operation is required before any update opeations; it is optionaly
+   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);
+}
+
+
 \f
 static int 
 lock_all (KEYDB_HANDLE hd)
@@ -380,8 +413,8 @@ lock_all (KEYDB_HANDLE hd)
   int i, rc = 0;
 
   /* Fixme: This locking scheme may lead to deadlock if the resources
-     are not added in the same sequence by all processes.  We are
-     cuurently only allowing one resource so it is not a problem. */
+     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) 
@@ -416,7 +449,10 @@ lock_all (KEYDB_HANDLE hd)
     else
       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
@@ -490,9 +526,8 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     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:
@@ -552,12 +587,12 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
 
 \f
 /*
-  Return the last found keybox.  Caller must free it.  The returned
+  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, KsbaCert *r_cert)
+keydb_get_cert (KEYDB_HANDLE hd, ksba_cert_t *r_cert)
 {
   int rc = 0;
 
@@ -580,15 +615,76 @@ keydb_get_cert (KEYDB_HANDLE hd, KsbaCert *r_cert)
   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_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
+keydb_insert_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
 {
   int rc = -1;
   int idx;
-  char digest[20];
+  unsigned char digest[20];
   
   if (!hd) 
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -603,9 +699,8 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
   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);
 
   gpgsm_get_fingerprint (cert, GCRY_MD_SHA1, digest, NULL); /* kludge*/
 
@@ -625,12 +720,12 @@ keydb_insert_cert (KEYDB_HANDLE hd, KsbaCert cert)
 
 
 
-/* update the current keyblock with KB */
+/* Update the current keyblock with KB.  */
 int
-keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
+keydb_update_cert (KEYDB_HANDLE hd, ksba_cert_t cert)
 {
   int rc = 0;
-  char digest[20];
+  unsigned char digest[20];
   
   if (!hd)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -666,7 +761,7 @@ keydb_update_cert (KEYDB_HANDLE hd, KsbaCert cert)
  * The current keyblock or cert will be deleted.
  */
 int
-keydb_delete (KEYDB_HANDLE hd)
+keydb_delete (KEYDB_HANDLE hd, int unlock)
 {
   int rc = -1;
   
@@ -679,9 +774,8 @@ keydb_delete (KEYDB_HANDLE hd)
   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)
     {
@@ -693,7 +787,8 @@ keydb_delete (KEYDB_HANDLE hd)
       break;
     }
 
-  unlock_all (hd);
+  if (unlock)
+    unlock_all (hd);
   return rc;
 }
 
@@ -881,7 +976,7 @@ keydb_search_issuer (KEYDB_HANDLE hd, const char *issuer)
 
 int
 keydb_search_issuer_sn (KEYDB_HANDLE hd,
-                        const char *issuer, KsbaConstSexp serial)
+                        const char *issuer, ksba_const_sexp_t serial)
 {
   KEYDB_SEARCH_DESC desc;
   int rc;
@@ -918,32 +1013,6 @@ keydb_search_subject (KEYDB_HANDLE hd, const char *name)
 
 
 static int
-hextobyte (const unsigned char *s)
-{
-  int c;
-
-  if( *s >= '0' && *s <= '9' )
-    c = 16 * (*s - '0');
-  else if ( *s >= 'A' && *s <= 'F' )
-    c = 16 * (10 + *s - 'A');
-  else if ( *s >= 'a' && *s <= 'f' )
-    c = 16 * (10 + *s - 'a');
-  else
-    return -1;
-  s++;
-  if ( *s >= '0' && *s <= '9' )
-    c += *s - '0';
-  else if ( *s >= 'A' && *s <= 'F' )
-    c += 10 + *s - 'A';
-  else if ( *s >= 'a' && *s <= 'f' )
-    c += 10 + *s - 'a';
-  else
-    return -1;
-  return c;
-}
-
-
-static int
 classify_user_id (const char *name, 
                   KEYDB_SEARCH_DESC *desc,
                   int *force_exact )
@@ -957,7 +1026,7 @@ classify_user_id (const char *name,
    * we set it to the correct value right at the end of this function */
   memset (desc, 0, sizeof *desc);
   *force_exact = 0;
-  /* skip leading spaces.  Fixme: what about trailing white space? */
+  /* Skip leading spaces.  Fixme: what about trailing white space? */
   for(s = name; *s && spacep (s); s++ )
     ;
 
@@ -1030,7 +1099,7 @@ classify_user_id (const char *name,
                 if (!strchr("01234567890abcdefABCDEF", *si))
                   return 0; /* invalid digit in serial number*/
               }
-            desc->sn = s;
+            desc->sn = (const unsigned char*)s;
             desc->snlen = -1;
             if (!*si)
               mode = KEYDB_SEARCH_MODE_SN;
@@ -1069,7 +1138,15 @@ classify_user_id (const char *name,
         mode = KEYDB_SEARCH_MODE_FPR;
       } 
       break;
-           
+
+    case '&': /* Keygrip*/
+      {  
+        if (hex2bin (s+1, desc->u.grip, 20) < 0)
+          return 0; /* Invalid. */
+        mode = KEYDB_SEARCH_MODE_KEYGRIP;
+      } 
+      break;
+
     default:
       if (s[0] == '0' && s[1] == 'x')
         {
@@ -1219,7 +1296,7 @@ keydb_classify_name (const char *name, KEYDB_SEARCH_DESC *desc)
    If EXISTED is not NULL it will be set to true if the certificate
    was already in the DB. */
 int
-keydb_store_cert (KsbaCert cert, int ephemeral, int *existed)
+keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
 {
   KEYDB_HANDLE kh;
   int rc;
@@ -1244,6 +1321,10 @@ keydb_store_cert (KsbaCert cert, int ephemeral, int *existed)
   if (ephemeral)
     keydb_set_ephemeral (kh, 1);
   
+  rc = lock_all (kh);
+  if (rc)
+    return rc;
+
   rc = keydb_search_fpr (kh, fpr);
   if (rc != -1)
     {
@@ -1279,4 +1360,172 @@ keydb_store_cert (KsbaCert cert, int ephemeral, int *existed)
 }
 
 
+/* 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;
+  
+  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 = keydb_classify_name (sl->d, desc+ndesc);
+          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);
+}
+