Fixed a bunch of little bugs as reported by Fabian Keil.
[gnupg.git] / g10 / keyring.c
index bd577a6..6b3c489 100644 (file)
@@ -1,11 +1,11 @@
 /* keyring.c - keyring file handling
 /* keyring.c - keyring file handling
- * Copyright (C) 2001, 2004 Free Software Foundation, Inc.
+ * Copyright (C) 2001, 2004, 2009 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
  *
  * 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,
  * (at your option) any later version.
  *
  * GnuPG is distributed in the hope that it will be useful,
@@ -14,9 +14,7 @@
  * GNU General Public License for more details.
  *
  * You should have received a copy of the GNU General Public License
  * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
- * USA.
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
  */
 
 #include <config.h>
@@ -52,9 +50,11 @@ typedef struct off_item **OffsetHashTable;
 
 
 typedef struct keyring_name *KR_NAME;
 
 
 typedef struct keyring_name *KR_NAME;
-struct keyring_name {
+struct keyring_name 
+{
   struct keyring_name *next;
   int secret;
   struct keyring_name *next;
   int secret;
+  int readonly;
   DOTLOCK lockhd;
   int is_locked;
   int did_full_scan;
   DOTLOCK lockhd;
   int is_locked;
   int did_full_scan;
@@ -160,6 +160,8 @@ update_offset_hash_table (OffsetHashTable tbl, u32 *kid, off_t off)
 {
   struct off_item *k;
 
 {
   struct off_item *k;
 
+  (void)off;
+
   for (k = tbl[(kid[1] & 0x07ff)]; k; k = k->next)
     {
       if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) 
   for (k = tbl[(kid[1] & 0x07ff)]; k; k = k->next)
     {
       if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) 
@@ -199,7 +201,8 @@ update_offset_hash_table_from_kb (OffsetHashTable tbl, KBNODE node, off_t off)
  * if a new keyring was registered.
 */
 int
  * if a new keyring was registered.
 */
 int
-keyring_register_filename (const char *fname, int secret, void **ptr)
+keyring_register_filename (const char *fname, int secret, int readonly, 
+                           void **ptr)
 {
     KR_NAME kr;
 
 {
     KR_NAME kr;
 
@@ -208,10 +211,13 @@ keyring_register_filename (const char *fname, int secret, void **ptr)
 
     for (kr=kr_names; kr; kr = kr->next)
       {
 
     for (kr=kr_names; kr; kr = kr->next)
       {
-        if ( !compare_filenames (kr->fname, fname) )
+        if (same_file_p (kr->fname, fname))
          {
          {
+            /* Already registered. */
+            if (readonly)
+              kr->readonly = 1;
             *ptr=kr;
             *ptr=kr;
-           return 0; /* already registered */
+           return 0; 
          }
       }
 
          }
       }
 
@@ -221,6 +227,7 @@ keyring_register_filename (const char *fname, int secret, void **ptr)
     kr = xmalloc (sizeof *kr + strlen (fname));
     strcpy (kr->fname, fname);
     kr->secret = !!secret;
     kr = xmalloc (sizeof *kr + strlen (fname));
     strcpy (kr->fname, fname);
     kr->secret = !!secret;
+    kr->readonly = readonly;
     kr->lockhd = NULL;
     kr->is_locked = 0;
     kr->did_full_scan = 0;
     kr->lockhd = NULL;
     kr->is_locked = 0;
     kr->did_full_scan = 0;
@@ -242,7 +249,7 @@ keyring_is_writable (void *token)
 {
   KR_NAME r = token;
 
 {
   KR_NAME r = token;
 
-  return r? !access (r->fname, W_OK) : 0;
+  return r? (r->readonly || !access (r->fname, W_OK)) : 0;
 }
     
 
 }
     
 
@@ -290,7 +297,7 @@ keyring_get_resource_name (KEYRING_HANDLE hd)
 
 
 /*
 
 
 /*
- * Lock the keyring with the given handle, or unlok if yes is false.
+ * Lock the keyring with the given handle, or unlock if YES is false.
  * We ignore the handle and lock all registered files.
  */
 int 
  * We ignore the handle and lock all registered files.
  */
 int 
@@ -299,6 +306,8 @@ keyring_lock (KEYRING_HANDLE hd, int yes)
     KR_NAME kr;
     int rc = 0;
 
     KR_NAME kr;
     int rc = 0;
 
+    (void)hd;
+
     if (yes) {
         /* first make sure the lock handles are created */
         for (kr=kr_names; kr; kr = kr->next) {
     if (yes) {
         /* first make sure the lock handles are created */
         for (kr=kr_names; kr; kr = kr->next) {
@@ -417,42 +426,52 @@ keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb)
         }
 
         in_cert = 1;
         }
 
         in_cert = 1;
-        if (pkt->pkttype == PKT_RING_TRUST) {
+        if (pkt->pkttype == PKT_RING_TRUST) 
+          {
             /*(this code is duplicated after the loop)*/
             if ( lastnode 
                  && lastnode->pkt->pkttype == PKT_SIGNATURE
                  && (pkt->pkt.ring_trust->sigcache & 1) ) {
             /*(this code is duplicated after the loop)*/
             if ( lastnode 
                  && lastnode->pkt->pkttype == PKT_SIGNATURE
                  && (pkt->pkt.ring_trust->sigcache & 1) ) {
-                /* this is a ring trust packet with a checked signature 
+                /* This is a ring trust packet with a checked signature 
                  * status cache following directly a signature paket.
                  * status cache following directly a signature paket.
-                 * Set the cache status into that signature packet */
+                 * Set the cache status into that signature packet */
                 PKT_signature *sig = lastnode->pkt->pkt.signature;
                 
                 sig->flags.checked = 1;
                 sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2);
             }
                 PKT_signature *sig = lastnode->pkt->pkt.signature;
                 
                 sig->flags.checked = 1;
                 sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2);
             }
-            /* reset lastnode, so that we set the cache status only from
-             * the ring trust packet immediately folling a signature */
+            /* Reset LASTNODE, so that we set the cache status only from
+             * the ring trust packet immediately following a signature. */
             lastnode = NULL;
             lastnode = NULL;
-        }
-        else {
-            node = lastnode = new_kbnode (pkt);
-            if (!keyblock)
-                keyblock = node;
-            else
-                add_kbnode (keyblock, node);
-
-            if ( pkt->pkttype == PKT_PUBLIC_KEY
-                 || pkt->pkttype == PKT_PUBLIC_SUBKEY
-                 || pkt->pkttype == PKT_SECRET_KEY
-                 || pkt->pkttype == PKT_SECRET_SUBKEY) {
-                if (++pk_no == hd->found.pk_no)
-                    node->flag |= 1;
-            }
-            else if ( pkt->pkttype == PKT_USER_ID) {
-                if (++uid_no == hd->found.uid_no)
-                    node->flag |= 2;
-            }
-        }
+           free_packet(pkt);
+           init_packet(pkt);
+           continue;
+          }
+
+
+        node = lastnode = new_kbnode (pkt);
+        if (!keyblock)
+          keyblock = node;
+        else
+          add_kbnode (keyblock, node);
+        switch (pkt->pkttype)
+          {
+          case PKT_PUBLIC_KEY:
+          case PKT_PUBLIC_SUBKEY:
+          case PKT_SECRET_KEY:
+          case PKT_SECRET_SUBKEY:
+            if (++pk_no == hd->found.pk_no)
+              node->flag |= 1;
+            break;
+
+          case PKT_USER_ID:
+            if (++uid_no == hd->found.uid_no)
+              node->flag |= 2;
+            break;
+            
+          default:
+            break;
+          }
 
         pkt = xmalloc (sizeof *pkt);
         init_packet(pkt);
 
         pkt = xmalloc (sizeof *pkt);
         init_packet(pkt);
@@ -497,6 +516,9 @@ keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb)
     if (!hd->found.kr)
         return -1; /* no successful prior search */
 
     if (!hd->found.kr)
         return -1; /* no successful prior search */
 
+    if (hd->found.kr->readonly)
+      return gpg_error (GPG_ERR_EACCES);
+
     if (!hd->found.n_packets) {
         /* need to know the number of packets - do a dummy get_keyblock*/
         rc = keyring_get_keyblock (hd, NULL);
     if (!hd->found.n_packets) {
         /* need to know the number of packets - do a dummy get_keyblock*/
         rc = keyring_get_keyblock (hd, NULL);
@@ -538,16 +560,24 @@ keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb)
     if (!hd)
         fname = NULL;
     else if (hd->found.kr)
     if (!hd)
         fname = NULL;
     else if (hd->found.kr)
+      {
         fname = hd->found.kr->fname;
         fname = hd->found.kr->fname;
+        if (hd->found.kr->readonly)
+          return gpg_error (GPG_ERR_EACCES);
+      }
     else if (hd->current.kr)
     else if (hd->current.kr)
+      {
         fname = hd->current.kr->fname;
         fname = hd->current.kr->fname;
+        if (hd->current.kr->readonly)
+          return gpg_error (GPG_ERR_EACCES);
+      }
     else 
         fname = hd->resource? hd->resource->fname:NULL;
 
     if (!fname)
         return G10ERR_GENERAL; 
 
     else 
         fname = hd->resource? hd->resource->fname:NULL;
 
     if (!fname)
         return G10ERR_GENERAL; 
 
-    /* close this one otherwise we will lose the position for
+    /* Close this one otherwise we will lose the position for
      * a next search.  Fixme: it would be better to adjust the position
      * after the write opertions.
      */
      * a next search.  Fixme: it would be better to adjust the position
      * after the write opertions.
      */
@@ -573,6 +603,9 @@ keyring_delete_keyblock (KEYRING_HANDLE hd)
     if (!hd->found.kr)
         return -1; /* no successful prior search */
 
     if (!hd->found.kr)
         return -1; /* no successful prior search */
 
+    if (hd->found.kr->readonly)
+      return gpg_error (GPG_ERR_EACCES);
+
     if (!hd->found.n_packets) {
         /* need to know the number of packets - do a dummy get_keyblock*/
         rc = keyring_get_keyblock (hd, NULL);
     if (!hd->found.n_packets) {
         /* need to know the number of packets - do a dummy get_keyblock*/
         rc = keyring_get_keyblock (hd, NULL);
@@ -661,7 +694,7 @@ prepare_search (KEYRING_HANDLE hd)
     hd->current.iobuf = iobuf_open (hd->current.kr->fname);
     if (!hd->current.iobuf)
       {
     hd->current.iobuf = iobuf_open (hd->current.kr->fname);
     if (!hd->current.iobuf)
       {
-        hd->current.error = gpg_error_from_errno (errno);
+        hd->current.error = gpg_error_from_syserror ();
         log_error(_("can't open `%s'\n"), hd->current.kr->fname );
         return hd->current.error;
       }
         log_error(_("can't open `%s'\n"), hd->current.kr->fname );
         return hd->current.error;
       }
@@ -964,7 +997,7 @@ keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc,
           hd->word_match.name = xstrdup (name);
           hd->word_match.pattern = prepare_word_match (name);
         }
           hd->word_match.name = xstrdup (name);
           hd->word_match.pattern = prepare_word_match (name);
         }
-      name = hd->word_match.pattern;
+      /*  name = hd->word_match.pattern; */
     }
 
   init_packet(&pkt);
     }
 
   init_packet(&pkt);
@@ -1200,7 +1233,7 @@ create_tmp_file (const char *template,
     umask(oldmask);
     if (!*r_fp)
       {
     umask(oldmask);
     if (!*r_fp)
       {
-        int rc = gpg_error_from_errno (errno);
+        int rc = gpg_error_from_syserror ();
        log_error(_("can't create `%s': %s\n"), tmpfname, strerror(errno) );
         xfree (tmpfname);
         xfree (bakfname);
        log_error(_("can't create `%s': %s\n"), tmpfname, strerror(errno) );
         xfree (tmpfname);
         xfree (bakfname);
@@ -1217,10 +1250,23 @@ static int
 rename_tmp_file (const char *bakfname, const char *tmpfname,
                  const char *fname, int secret )
 {
 rename_tmp_file (const char *bakfname, const char *tmpfname,
                  const char *fname, int secret )
 {
-  int rc=0;
+  int rc = 0;
+
+  /* It's a secret keyring, so let's force a fsync just to be safe on
+     filesystems that may not sync data and metadata together
+     (e.g. ext4). */
+  if (secret && iobuf_ioctl (NULL, 4, 0, (char*)tmpfname))
+    {
+      rc = gpg_error_from_syserror ();
+      goto fail;
+    }
 
 
-  /* invalidate close caches*/
-  iobuf_ioctl (NULL, 2, 0, (char*)tmpfname );
+  /* Invalidate close caches.  */
+  if (iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ))
+    {
+      rc = gpg_error_from_syserror ();
+      goto fail;
+    }
   iobuf_ioctl (NULL, 2, 0, (char*)bakfname );
   iobuf_ioctl (NULL, 2, 0, (char*)fname );
 
   iobuf_ioctl (NULL, 2, 0, (char*)bakfname );
   iobuf_ioctl (NULL, 2, 0, (char*)fname );
 
@@ -1232,7 +1278,7 @@ rename_tmp_file (const char *bakfname, const char *tmpfname,
 #endif
       if (rename (fname, bakfname) )
         {
 #endif
       if (rename (fname, bakfname) )
         {
-          rc = gpg_error_from_errno (errno);
+          rc = gpg_error_from_syserror ();
           log_error ("renaming `%s' to `%s' failed: %s\n",
                      fname, bakfname, strerror(errno) );
           return rc;
           log_error ("renaming `%s' to `%s' failed: %s\n",
                      fname, bakfname, strerror(errno) );
           return rc;
@@ -1247,19 +1293,11 @@ rename_tmp_file (const char *bakfname, const char *tmpfname,
     unregister_secured_file (fname);
   if (rename (tmpfname, fname) )
     {
     unregister_secured_file (fname);
   if (rename (tmpfname, fname) )
     {
-      rc = gpg_error_from_errno (errno);
+      rc = gpg_error_from_syserror ();
       log_error (_("renaming `%s' to `%s' failed: %s\n"),
                  tmpfname, fname, strerror(errno) );
       register_secured_file (fname);
       log_error (_("renaming `%s' to `%s' failed: %s\n"),
                  tmpfname, fname, strerror(errno) );
       register_secured_file (fname);
-      if (secret)
-        {
-          log_info(_("WARNING: 2 files with confidential"
-                     " information exists.\n"));
-          log_info(_("%s is the unchanged one\n"), fname );
-          log_info(_("%s is the new one\n"), tmpfname );
-          log_info(_("Please fix this possible security flaw\n"));
-       }
-      return rc;
+      goto fail;
     }
 
   /* Now make sure the file has the same permissions as the original */
     }
 
   /* Now make sure the file has the same permissions as the original */
@@ -1270,17 +1308,27 @@ rename_tmp_file (const char *bakfname, const char *tmpfname,
 
     statbuf.st_mode=S_IRUSR | S_IWUSR;
 
 
     statbuf.st_mode=S_IRUSR | S_IWUSR;
 
-    if(((secret && !opt.preserve_permissions) ||
-       (stat(bakfname,&statbuf)==0)) &&
-       (chmod(fname,statbuf.st_mode)==0))
+    if (((secret && !opt.preserve_permissions)
+         || !stat (bakfname,&statbuf)) 
+        && !chmod (fname,statbuf.st_mode))
       ;
     else
       ;
     else
-      log_error("WARNING: unable to restore permissions to `%s': %s",
-               fname,strerror(errno));
+      log_error ("WARNING: unable to restore permissions to `%s': %s",
+                 fname, strerror(errno));
   }
 #endif
 
   return 0;
   }
 #endif
 
   return 0;
+
+ fail:
+  if (secret)
+    {
+      log_info(_("WARNING: 2 files with confidential information exists.\n"));
+      log_info(_("%s is the unchanged one\n"), fname );
+      log_info(_("%s is the new one\n"), tmpfname );
+      log_info(_("Please fix this possible security flaw\n"));
+    }
+  return rc;
 }
 
 
 }
 
 
@@ -1317,7 +1365,7 @@ write_keyblock (IOBUF fp, KBNODE keyblock)
           iobuf_put (fp, 0);    /* unused */
           if (iobuf_put (fp, cacheval)) 
             {
           iobuf_put (fp, 0);    /* unused */
           if (iobuf_put (fp, cacheval)) 
             {
-              rc = gpg_error_from_errno (errno);
+              rc = gpg_error_from_syserror ();
               log_error ("writing sigcache packet failed\n");
               return rc;
             }
               log_error ("writing sigcache packet failed\n");
               return rc;
             }
@@ -1362,7 +1410,7 @@ keyring_rebuild_cache (void *token,int noisy)
             {
               if (iobuf_close (tmpfp))
                 {
             {
               if (iobuf_close (tmpfp))
                 {
-                  rc = gpg_error_from_errno (errno);
+                  rc = gpg_error_from_syserror ();
                   log_error ("error closing `%s': %s\n",
                              tmpfilename, strerror (errno));
                   goto leave;
                   log_error ("error closing `%s': %s\n",
                              tmpfilename, strerror (errno));
                   goto leave;
@@ -1442,7 +1490,7 @@ keyring_rebuild_cache (void *token,int noisy)
     {
       if (iobuf_close (tmpfp))
         {
     {
       if (iobuf_close (tmpfp))
         {
-          rc = gpg_error_from_errno (errno);
+          rc = gpg_error_from_syserror ();
           log_error ("error closing `%s': %s\n",
                      tmpfilename, strerror (errno));
           goto leave;
           log_error ("error closing `%s': %s\n",
                      tmpfilename, strerror (errno));
           goto leave;
@@ -1486,7 +1534,7 @@ do_copy (int mode, const char *fname, KBNODE root, int secret,
     /* Open the source file. Because we do a rename, we have to check the 
        permissions of the file */
     if (access (fname, W_OK))
     /* Open the source file. Because we do a rename, we have to check the 
        permissions of the file */
     if (access (fname, W_OK))
-      return gpg_error_from_errno (errno);
+      return gpg_error_from_syserror ();
 
     fp = iobuf_open (fname);
     if (mode == 1 && !fp && errno == ENOENT) { 
 
     fp = iobuf_open (fname);
     if (mode == 1 && !fp && errno == ENOENT) { 
@@ -1504,7 +1552,7 @@ do_copy (int mode, const char *fname, KBNODE root, int secret,
        umask(oldmask);
        if( !newfp )
          {
        umask(oldmask);
        if( !newfp )
          {
-            rc = gpg_error_from_errno (errno);
+            rc = gpg_error_from_syserror ();
            log_error (_("can't create `%s': %s\n"), fname, strerror(errno));
            return rc;
          }
            log_error (_("can't create `%s': %s\n"), fname, strerror(errno));
            return rc;
          }
@@ -1521,7 +1569,7 @@ do_copy (int mode, const char *fname, KBNODE root, int secret,
            }
        }
        if( iobuf_close(newfp) ) {
            }
        }
        if( iobuf_close(newfp) ) {
-            rc = gpg_error_from_errno (errno);
+            rc = gpg_error_from_syserror ();
            log_error ("%s: close failed: %s\n", fname, strerror(errno));
            return rc;
        }
            log_error ("%s: close failed: %s\n", fname, strerror(errno));
            return rc;
        }
@@ -1530,7 +1578,7 @@ do_copy (int mode, const char *fname, KBNODE root, int secret,
 
     if( !fp )
       {
 
     if( !fp )
       {
-        rc = gpg_error_from_errno (errno);
+        rc = gpg_error_from_syserror ();
        log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
        goto leave;
       }
        log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
        goto leave;
       }
@@ -1613,12 +1661,12 @@ do_copy (int mode, const char *fname, KBNODE root, int secret,
 
     /* close both files */
     if( iobuf_close(fp) ) {
 
     /* close both files */
     if( iobuf_close(fp) ) {
-        rc = gpg_error_from_errno (errno);
+        rc = gpg_error_from_syserror ();
        log_error("%s: close failed: %s\n", fname, strerror(errno) );
        goto leave;
     }
     if( iobuf_close(newfp) ) {
        log_error("%s: close failed: %s\n", fname, strerror(errno) );
        goto leave;
     }
     if( iobuf_close(newfp) ) {
-        rc = gpg_error_from_errno (errno);
+        rc = gpg_error_from_syserror ();
        log_error("%s: close failed: %s\n", tmpfname, strerror(errno) );
        goto leave;
     }
        log_error("%s: close failed: %s\n", tmpfname, strerror(errno) );
        goto leave;
     }