gpg: Remove option --no-sig-create-check.
[gnupg.git] / sm / keydb.c
index 35343f3..b3363c4 100644 (file)
@@ -1,5 +1,6 @@
 /* keydb.c - key database dispatcher
  * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2014 g10 Code GmbH
  *
  * This file is part of GnuPG.
  *
@@ -56,6 +57,7 @@ static int used_resources;
 struct keydb_handle {
   int locked;
   int found;
+  int saved_found;
   int current;
   int is_ephemeral;
   int used; /* items in active */
@@ -67,11 +69,173 @@ static int lock_all (KEYDB_HANDLE hd);
 static void unlock_all (KEYDB_HANDLE hd);
 
 
+static void
+try_make_homedir (const char *fname)
+{
+  const char *defhome = standard_homedir ();
+
+  /* Create the directory only if the supplied directory name is the
+     same as the default one.  This way we avoid to create arbitrary
+     directories when a non-default home directory is used.  To cope
+     with HOME, we do compare only the suffix if we see that the
+     default homedir does start with a tilde.  */
+  if ( opt.dry_run || opt.no_homedir_creation )
+    return;
+
+  if (
+#ifdef HAVE_W32_SYSTEM
+      ( !compare_filenames (fname, defhome) )
+#else
+      ( *defhome == '~'
+        && (strlen(fname) >= strlen (defhome+1)
+            && !strcmp(fname+strlen(fname)-strlen(defhome+1), defhome+1 ) ))
+      || (*defhome != '~'  && !compare_filenames( fname, defhome ) )
+#endif
+      )
+    {
+      if (gnupg_mkdir (fname, "-rwx"))
+        log_info (_("can't create directory '%s': %s\n"),
+                  fname, strerror(errno) );
+      else if (!opt.quiet )
+        log_info (_("directory '%s' created\n"), fname);
+    }
+}
+
+
+/* Handle the creation of a keybox if it does not yet exist.  Take
+   into acount that other processes might have the keybox already
+   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
+maybe_create_keybox (char *filename, int force, int *r_created)
+{
+  dotlock_t lockhd = NULL;
+  FILE *fp;
+  int rc;
+  mode_t oldmask;
+  char *last_slash_in_filename;
+  int save_slash;
+
+  if (r_created)
+    *r_created = 0;
+
+  /* A quick test whether the filename already exists. */
+  if (!access (filename, F_OK))
+    return 0;
+
+  /* If we don't want to create a new file at all, there is no need to
+     go any further - bail out right here.  */
+  if (!force)
+    return gpg_error (GPG_ERR_ENOENT);
+
+  /* First of all we try to create the home directory.  Note, that we
+     don't do any locking here because any sane application of gpg
+     would create the home directory by itself and not rely on gpg's
+     tricky auto-creation which is anyway only done for some home
+     directory name patterns. */
+  last_slash_in_filename = strrchr (filename, DIRSEP_C);
+#if HAVE_W32_SYSTEM
+  {
+    /* Windows may either have a slash or a backslash.  Take care of it.  */
+    char *p = strrchr (filename, '/');
+    if (!last_slash_in_filename || p > last_slash_in_filename)
+      last_slash_in_filename = p;
+  }
+#endif /*HAVE_W32_SYSTEM*/
+  if (!last_slash_in_filename)
+    return gpg_error (GPG_ERR_ENOENT);  /* No slash at all - should
+                                           not happen though.  */
+  save_slash = *last_slash_in_filename;
+  *last_slash_in_filename = 0;
+  if (access(filename, F_OK))
+    {
+      static int tried;
+
+      if (!tried)
+        {
+          tried = 1;
+          try_make_homedir (filename);
+        }
+      if (access (filename, F_OK))
+        {
+          rc = gpg_error_from_syserror ();
+          *last_slash_in_filename = save_slash;
+          goto leave;
+        }
+    }
+  *last_slash_in_filename = save_slash;
+
+  /* To avoid races with other instances of gpg trying to create or
+     update the keybox (it is removed during an update for a short
+     time), we do the next stuff in a locked state. */
+  lockhd = dotlock_create (filename, 0);
+  if (!lockhd)
+    {
+      /* A reason for this to fail is that the directory is not
+         writable. However, this whole locking stuff does not make
+         sense if this is the case. An empty non-writable directory
+         with no keyring is not really useful at all. */
+      if (opt.verbose)
+        log_info ("can't allocate lock for '%s'\n", filename );
+
+      if (!force)
+        return gpg_error (GPG_ERR_ENOENT);
+      else
+        return gpg_error (GPG_ERR_GENERAL);
+    }
+
+  if ( dotlock_take (lockhd, -1) )
+    {
+      /* This is something bad.  Probably a stale lockfile.  */
+      log_info ("can't lock '%s'\n", filename);
+      rc = gpg_error (GPG_ERR_GENERAL);
+      goto leave;
+    }
+
+  /* Now the real test while we are locked. */
+  if (!access(filename, F_OK))
+    {
+      rc = 0;  /* Okay, we may access the file now.  */
+      goto leave;
+    }
+
+  /* The file does not yet exist, create it now. */
+  oldmask = umask (077);
+  fp = fopen (filename, "w");
+  if (!fp)
+    {
+      rc = gpg_error_from_syserror ();
+      umask (oldmask);
+      log_error (_("error creating keybox '%s': %s\n"),
+                 filename, gpg_strerror (rc));
+      goto leave;
+    }
+  umask (oldmask);
+
+  if (!opt.quiet)
+    log_info (_("keybox '%s' created\n"), filename);
+  if (r_created)
+    *r_created = 1;
+
+  fclose (fp);
+  rc = 0;
+
+ leave:
+  if (lockhd)
+    {
+      dotlock_release (lockhd);
+      dotlock_destroy (lockhd);
+    }
+  return rc;
+}
+
+
 /*
  * 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.  If AUTO_CREATED is not NULL it will be set to true
- * if the function has created a new keybox.
+ * if the function has created a new keybox.
  */
 int
 keydb_add_resource (const char *url, int force, int secret, int *auto_created)
@@ -80,9 +244,7 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
   const char *resname = url;
   char *filename = NULL;
   int rc = 0;
-  FILE *fp;
   KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
-  const char *created_fname = NULL;
 
   if (auto_created)
     *auto_created = 0;
@@ -101,7 +263,7 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
 #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
       else if (strchr (resname, ':'))
         {
-          log_error ("invalid key resource URL `%s'\n", url );
+          log_error ("invalid key resource URL '%s'\n", url );
           rc = gpg_error (GPG_ERR_GENERAL);
           goto leave;
        }
@@ -124,123 +286,78 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
   /* see whether we can determine the filetype */
   if (rt == KEYDB_RESOURCE_TYPE_NONE)
     {
-      FILE *fp2 = fopen( filename, "rb" );
+      FILE *fp = fopen( filename, "rb" );
 
-      if (fp2) {
-        u32 magic;
+      if (fp)
+        {
+          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 */
+          /* FIXME: check for the keybox 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 keybox */
+            rt = KEYDB_RESOURCE_TYPE_KEYBOX;
+          fclose (fp);
+        }
+      else /* no file yet: create keybox */
         rt = KEYDB_RESOURCE_TYPE_KEYBOX;
     }
 
   switch (rt)
     {
     case KEYDB_RESOURCE_TYPE_NONE:
-      log_error ("unknown type of key resource `%s'\n", url );
+      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 (!fp)
-        { /* no file */
-#if 0 /* no autocreate of the homedirectory yet */
+      rc = maybe_create_keybox (filename, force, auto_created);
+      if (rc)
+        goto leave;
+      /* Now register the file */
+      {
+        void *token = keybox_register_file (filename, secret);
+        if (!token)
+          ; /* already registered - ignore it */
+        else if (used_resources >= MAX_KEYDB_RESOURCES)
+          rc = gpg_error (GPG_ERR_RESOURCE_LIMIT);
+        else
           {
-            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;
+            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_x509 (token, secret);
+
+                if (kbxhd)
+                  {
+                    keybox_compress (kbxhd);
+                    keybox_release (kbxhd);
+                  }
+                dotlock_release (all_resources[used_resources].lockhandle);
               }
-            *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;
-           }
-
-          if (!opt.quiet)
-            log_info (_("keybox `%s' created\n"), filename);
-          created_fname = filename;
-          if (auto_created)
-            *auto_created = 1;
-       }
-       fclose (fp);
-       fp = NULL;
-        /* now register the file */
-        {
-
-          void *token = keybox_register_file (filename, secret);
-          if (!token)
-            ; /* already registered - ignore it */
-          else if (used_resources >= MAX_KEYDB_RESOURCES)
-            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
-                = 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++;
-            }
-        }
 
+            used_resources++;
+          }
+      }
+      break;
 
-       break;
     default:
-      log_error ("resource type of `%s' not supported\n", url);
+      log_error ("resource type of '%s' not supported\n", url);
       rc = gpg_error (GPG_ERR_NOT_SUPPORTED);
       goto leave;
     }
@@ -249,7 +366,7 @@ keydb_add_resource (const char *url, int force, int secret, int *auto_created)
 
  leave:
   if (rc)
-    log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror(rc));
+    log_error ("keyblock resource '%s': %s\n", filename, gpg_strerror(rc));
   else if (secret)
     any_secret = 1;
   else
@@ -267,6 +384,7 @@ keydb_new (int secret)
 
   hd = xcalloc (1, sizeof *hd);
   hd->found = -1;
+  hd->saved_found = -1;
 
   assert (used_resources <= MAX_KEYDB_RESOURCES);
   for (i=j=0; i < used_resources; i++)
@@ -282,7 +400,7 @@ keydb_new (int secret)
           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);
+          hd->active[j].u.kr = keybox_new_x509 (all_resources[i].token, secret);
           if (!hd->active[j].u.kr)
             {
               xfree (hd);
@@ -423,7 +541,7 @@ lock_all (KEYDB_HANDLE hd)
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           if (hd->active[i].lockhandle)
-            rc = make_dotlock (hd->active[i].lockhandle, -1);
+            rc = dotlock_take (hd->active[i].lockhandle, -1);
           break;
         }
       if (rc)
@@ -441,7 +559,7 @@ lock_all (KEYDB_HANDLE hd)
                 break;
               case KEYDB_RESOURCE_TYPE_KEYBOX:
                 if (hd->active[i].lockhandle)
-                  release_dotlock (hd->active[i].lockhandle);
+                  dotlock_release (hd->active[i].lockhandle);
                 break;
               }
           }
@@ -471,119 +589,64 @@ unlock_all (KEYDB_HANDLE hd)
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
           if (hd->active[i].lockhandle)
-            release_dotlock (hd->active[i].lockhandle);
+            dotlock_release (hd->active[i].lockhandle);
           break;
         }
     }
   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 user ID node.
- */
-int
-keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
+/* Push the last found state if any.  */
+void
+keydb_push_found_state (KEYDB_HANDLE hd)
 {
-    int rc = 0;
-
-    if (!hd)
-        return G10ERR_INV_ARG;
-
-    if ( hd->found < 0 || hd->found >= hd->used)
-        return -1; /* nothing found */
+  if (!hd)
+    return;
 
-    switch (hd->active[hd->found].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-        rc = keybox_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
-        break;
+  if (hd->found < 0 || hd->found >= hd->used)
+    {
+      hd->saved_found = -1;
+      return;
     }
 
-    return rc;
-}
-
-/*
- * update the current keyblock with KB
- */
-int
-keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
-{
-    int rc = 0;
-
-    if (!hd)
-        return G10ERR_INV_ARG;
-
-    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 = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-        rc = keybox_update_keyblock (hd->active[hd->found].u.kr, kb);
-        break;
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      keybox_push_found_state (hd->active[hd->found].u.kr);
+      break;
     }
 
-    unlock_all (hd);
-    return rc;
+  hd->saved_found = hd->found;
+  hd->found = -1;
 }
 
 
-/*
- * Insert a new KB into one of the resources.
- */
-int
-keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
+/* Pop the last found state.  */
+void
+keydb_pop_found_state (KEYDB_HANDLE hd)
 {
-    int rc = -1;
-    int idx;
-
-    if (!hd)
-        return G10ERR_INV_ARG;
-
-    if( opt.dry_run )
-       return 0;
-
-    if ( hd->found >= 0 && hd->found < hd->used)
-        idx = hd->found;
-    else if ( hd->current >= 0 && hd->current < hd->used)
-        idx = hd->current;
-    else
-        return G10ERR_GENERAL;
+  if (!hd)
+    return;
 
-    rc = lock_all (hd);
-    if (rc)
-        return rc;
+  hd->found = hd->saved_found;
+  hd->saved_found = -1;
+  if (hd->found < 0 || hd->found >= hd->used)
+    return;
 
-    switch (hd->active[idx].type) {
-      case KEYDB_RESOURCE_TYPE_NONE:
-        rc = G10ERR_GENERAL; /* oops */
-        break;
-      case KEYDB_RESOURCE_TYPE_KEYBOX:
-        rc = keybox_insert_keyblock (hd->active[idx].u.kr, kb);
-        break;
+  switch (hd->active[hd->found].type)
+    {
+    case KEYDB_RESOURCE_TYPE_NONE:
+      break;
+    case KEYDB_RESOURCE_TYPE_KEYBOX:
+      keybox_pop_found_state (hd->active[hd->found].u.kr);
+      break;
     }
-
-    unlock_all (hd);
-    return rc;
 }
 
-#endif /*disabled code*/
-
 
 \f
 /*
@@ -895,6 +958,7 @@ int
 keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
 {
   int rc = -1;
+  unsigned long skipped;
 
   if (!hd)
     return gpg_error (GPG_ERR_INV_VALUE);
@@ -907,7 +971,9 @@ keydb_search (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc)
           BUG(); /* we should never see it here */
           break;
         case KEYDB_RESOURCE_TYPE_KEYBOX:
-          rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc);
+          rc = keybox_search (hd->active[hd->current].u.kr, desc, ndesc,
+                              KEYBOX_BLOBTYPE_X509,
+                              NULL, &skipped);
           break;
         }
       if (rc == -1) /* EOF -> switch to next resource */
@@ -1044,8 +1110,9 @@ keydb_store_cert (ksba_cert_t cert, int ephemeral, int *existed)
       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)
@@ -1059,6 +1126,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 (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"),
@@ -1066,6 +1146,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)
     {
@@ -1205,10 +1289,10 @@ keydb_clear_some_cert_flags (ctrl_t ctrl, strlist_t names)
     {
       for (ndesc=0, sl=names; sl; sl = sl->next)
         {
-          rc = classify_user_id (sl->d, desc+ndesc);
+          rc = classify_user_id (sl->d, desc+ndesc, 0);
           if (rc)
             {
-              log_error ("key `%s' not found: %s\n",
+              log_error ("key '%s' not found: %s\n",
                          sl->d, gpg_strerror (rc));
               rc = 0;
             }