Fixed yesterday's W32 fix.
[gnupg.git] / g10 / keydb.c
index b64f38c..3360f63 100644 (file)
@@ -1,11 +1,12 @@
 /* keydb.c - key database dispatcher
- * Copyright (C) 2001, 2002, 2003 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005, 
+ *               2008 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 +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, 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>
@@ -71,6 +71,136 @@ static int lock_all (KEYDB_HANDLE hd);
 static void unlock_all (KEYDB_HANDLE hd);
 
 
+/* Handle the creation of a keyring if it does not yet exist.  Take
+   into acount that other processes might have the keyring already
+   locked.  This lock check does not work if the directory itself is
+   not yet available. */
+static int
+maybe_create_keyring (char *filename, int force)
+{
+  DOTLOCK lockhd = NULL;
+  IOBUF iobuf;
+  int rc;
+  mode_t oldmask;
+  char *last_slash_in_filename;
+  int save_slash;
+
+  /* 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 keyring (it is removed during an update for a short
+     time), we do the next stuff in a locked state. */
+  lockhd = create_dotlock (filename);
+  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 ( make_dotlock (lockhd, -1) )
+    {
+      /* This is something bad.  Probably a stale lockfile.  */
+      log_info ("can't lock `%s'\n", filename );
+      rc = G10ERR_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);
+  if (is_secured_filename (filename))
+    {
+      iobuf = NULL;
+      errno = EPERM;
+    }
+  else
+    iobuf = iobuf_create (filename);
+  umask (oldmask);
+  if (!iobuf) 
+    {
+      rc = gpg_error_from_syserror ();
+      log_error ( _("error creating keyring `%s': %s\n"),
+                  filename, strerror(errno));
+      goto leave;
+    }
+
+  if (!opt.quiet)
+    log_info (_("keyring `%s' created\n"), filename);
+
+  iobuf_close (iobuf);
+  /* Must invalidate that ugly cache */
+  iobuf_ioctl (NULL, 2, 0, filename);
+  rc = 0;
+
+ leave:
+  if (lockhd)
+    {
+      release_dotlock (lockhd);
+      destroy_dotlock (lockhd);
+    }
+  return rc;
+}
+
+
 /*
  * Register a resource (which currently may only be a keyring file).
  * The first keyring which is added by this function is
@@ -78,14 +208,14 @@ static void unlock_all (KEYDB_HANDLE hd);
  * Note: this function may be called before secure memory is
  * available.
  * Flag 1 == force
- * Flag 2 == default
+ * Flag 2 == mark resource as primary
+ * Flag 4 == This is a default resources
  */
 int
 keydb_add_resource (const char *url, int flags, int secret)
 {
     static int any_secret, any_public;
     const char *resname = url;
-    iobuf_t iobuf = NULL;
     char *filename = NULL;
     int force=(flags&1);
     int rc = 0;
@@ -104,7 +234,7 @@ keydb_add_resource (const char *url, int flags, int secret)
 #if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
        else if (strchr (resname, ':')) {
            log_error ("invalid key resource URL `%s'\n", url );
-           rc = GPG_ERR_GENERAL;
+           rc = G10ERR_GENERAL;
            goto leave;
        }
 #endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
@@ -146,65 +276,18 @@ keydb_add_resource (const char *url, int flags, int secret)
     switch (rt) {
       case KEYDB_RESOURCE_TYPE_NONE:
        log_error ("unknown type of key resource `%s'\n", url );
-       rc = GPG_ERR_GENERAL;
+       rc = G10ERR_GENERAL;
        goto leave;
 
       case KEYDB_RESOURCE_TYPE_KEYRING:
-        if (access(filename, F_OK))
-          { /* file does not exist */
-           mode_t oldmask;
-           char *last_slash_in_filename;
-
-            if (!force) 
-              {
-                rc = gpg_error_from_errno (errno);
-                goto leave;
-              }
-
-           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 check again. */
-                static int tried;
-                
-                if (!tried)
-                  {
-                    tried = 1;
-                    try_make_homedir (filename);
-                  }
-               if (access (filename, F_OK))
-                  {
-                    rc = gpg_error_from_errno (errno);
-                    *last_slash_in_filename = DIRSEP_C;
-                    goto leave;
-                  }
-              }
-           *last_slash_in_filename = DIRSEP_C;
-
-           oldmask=umask(077);
-           iobuf = iobuf_create (filename);
-           umask(oldmask);
-           if (!iobuf) 
-              {
-               log_error ( _("error creating keyring `%s': %s\n"),
-                            filename, strerror(errno));
-                rc = gpg_error_from_errno (errno);
-               goto leave;
-              }
-
-            if (!opt.quiet)
-              log_info (_("keyring `%s' created\n"), filename);
-            iobuf_close (iobuf);
-            iobuf = NULL;
-            /* must invalidate that ugly cache */
-            iobuf_ioctl (NULL, 2, 0, (char*)filename);
-          } /* end file creation */
+        rc = maybe_create_keyring (filename, force);
+        if (rc)
+          goto leave;
 
         if(keyring_register_filename (filename, secret, &token))
          {
            if (used_resources >= MAX_KEYDB_RESOURCES)
-             rc = GPG_ERR_RESOURCE_LIMIT;
+             rc = G10ERR_RESOURCE_LIMIT;
            else 
              {
                if(flags&2)
@@ -228,7 +311,7 @@ keydb_add_resource (const char *url, int flags, int secret)
 
       default:
        log_error ("resource type of `%s' not supported\n", url);
-       rc = GPG_ERR_GENERAL;
+       rc = G10ERR_GENERAL;
        goto leave;
     }
 
@@ -236,7 +319,18 @@ keydb_add_resource (const char *url, int flags, int secret)
 
   leave:
     if (rc)
-       log_error ("keyblock resource `%s': %s\n", filename, gpg_strerror (rc));
+      {
+        /* Secret keyrings are not required in all cases.  To avoid
+           having gpg return failure we use log_info here if the
+           rewsource is a secret one and marked as default
+           resource.  */
+        if ((flags&4) && secret)
+          log_info (_("keyblock resource `%s': %s\n"),
+                    filename, g10_errstr(rc));
+        else
+          log_error (_("keyblock resource `%s': %s\n"),
+                     filename, g10_errstr(rc));
+      }
     else if (secret)
        any_secret = 1;
     else
@@ -254,7 +348,7 @@ keydb_new (int secret)
   KEYDB_HANDLE hd;
   int i, j;
   
-  hd = xcalloc (1,sizeof *hd);
+  hd = xmalloc_clear (sizeof *hd);
   hd->found = -1;
   
   assert (used_resources <= MAX_KEYDB_RESOURCES);
@@ -414,14 +508,14 @@ keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
     int rc = 0;
 
     if (!hd)
-        return GPG_ERR_INV_ARG;
+        return G10ERR_INV_ARG;
 
     if ( hd->found < 0 || hd->found >= hd->used) 
         return -1; /* nothing found */
 
     switch (hd->active[hd->found].type) {
       case KEYDB_RESOURCE_TYPE_NONE:
-        rc = GPG_ERR_GENERAL; /* oops */
+        rc = G10ERR_GENERAL; /* oops */
         break;
       case KEYDB_RESOURCE_TYPE_KEYRING:
         rc = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
@@ -440,7 +534,7 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     int rc = 0;
 
     if (!hd)
-        return GPG_ERR_INV_ARG;
+        return G10ERR_INV_ARG;
 
     if ( hd->found < 0 || hd->found >= hd->used) 
         return -1; /* nothing found */
@@ -454,7 +548,7 @@ keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
 
     switch (hd->active[hd->found].type) {
       case KEYDB_RESOURCE_TYPE_NONE:
-        rc = GPG_ERR_GENERAL; /* oops */
+        rc = G10ERR_GENERAL; /* oops */
         break;
       case KEYDB_RESOURCE_TYPE_KEYRING:
         rc = keyring_update_keyblock (hd->active[hd->found].u.kr, kb);
@@ -476,7 +570,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     int idx;
 
     if (!hd) 
-        return GPG_ERR_INV_ARG;
+        return G10ERR_INV_ARG;
 
     if( opt.dry_run )
        return 0;
@@ -486,7 +580,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
     else if ( hd->current >= 0 && hd->current < hd->used) 
         idx = hd->current;
     else
-        return GPG_ERR_GENERAL;
+        return G10ERR_GENERAL;
 
     rc = lock_all (hd);
     if (rc)
@@ -494,7 +588,7 @@ keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
 
     switch (hd->active[idx].type) {
       case KEYDB_RESOURCE_TYPE_NONE:
-        rc = GPG_ERR_GENERAL; /* oops */
+        rc = G10ERR_GENERAL; /* oops */
         break;
       case KEYDB_RESOURCE_TYPE_KEYRING:
         rc = keyring_insert_keyblock (hd->active[idx].u.kr, kb);
@@ -515,7 +609,7 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
     int rc = -1;
 
     if (!hd)
-        return GPG_ERR_INV_ARG;
+        return G10ERR_INV_ARG;
 
     if ( hd->found < 0 || hd->found >= hd->used) 
         return -1; /* nothing found */
@@ -529,7 +623,7 @@ keydb_delete_keyblock (KEYDB_HANDLE hd)
 
     switch (hd->active[hd->found].type) {
       case KEYDB_RESOURCE_TYPE_NONE:
-        rc = GPG_ERR_GENERAL; /* oops */
+        rc = G10ERR_GENERAL; /* oops */
         break;
       case KEYDB_RESOURCE_TYPE_KEYRING:
         rc = keyring_delete_keyblock (hd->active[hd->found].u.kr);
@@ -552,7 +646,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
   int rc;
   
   if (!hd)
-    return GPG_ERR_INV_ARG;
+    return G10ERR_INV_ARG;
   
   rc = keydb_search_reset (hd); /* this does reset hd->current */
   if (rc)
@@ -598,7 +692,7 @@ keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
  * Rebuild the caches of all key resources.
  */
 void
-keydb_rebuild_caches (void)
+keydb_rebuild_caches (int noisy)
 {
   int i, rc;
   
@@ -611,10 +705,10 @@ keydb_rebuild_caches (void)
         case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
           break;
         case KEYDB_RESOURCE_TYPE_KEYRING:
-          rc = keyring_rebuild_cache (all_resources[i].token);
+          rc = keyring_rebuild_cache (all_resources[i].token,noisy);
           if (rc)
             log_error (_("failed to rebuild keyring cache: %s\n"),
-                       gpg_strerror (rc));
+                       g10_errstr (rc));
           break;
         }
     }
@@ -631,7 +725,7 @@ keydb_search_reset (KEYDB_HANDLE hd)
     int i, rc = 0;
 
     if (!hd)
-        return GPG_ERR_INV_ARG;
+        return G10ERR_INV_ARG;
 
     hd->current = 0; 
     hd->found = -1;
@@ -660,7 +754,7 @@ keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
     int rc = -1;
 
     if (!hd)
-        return GPG_ERR_INV_ARG;
+        return G10ERR_INV_ARG;
 
     while (rc == -1 && hd->current >= 0 && hd->current < hd->used) {
         switch (hd->active[hd->current].type) {