gpg: Unfinished support for v5 signatures.
[gnupg.git] / kbx / keybox-update.c
index 6428bb2..580330f 100644 (file)
@@ -14,7 +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, see <http://www.gnu.org/licenses/>.
+ * along with this program; if not, see <https://www.gnu.org/licenses/>.
  */
 
 #include <config.h>
 
 #include "keybox-defs.h"
 #include "../common/sysutils.h"
+#include "../common/host2net.h"
+#include "../common/utilproto.h"
 
 #define EXTSEP_S "."
 
+#define FILECOPY_INSERT 1
+#define FILECOPY_DELETE 2
+#define FILECOPY_UPDATE 3
+
 
 #if !defined(HAVE_FSEEKO) && !defined(fseeko)
 
@@ -63,88 +69,27 @@ fseeko (FILE * stream, off_t newpos, int whence)
 #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */
 
 
-
 static int
 create_tmp_file (const char *template,
                  char **r_bakfname, char **r_tmpfname, FILE **r_fp)
 {
-  char *bakfname, *tmpfname;
-
-  *r_bakfname = NULL;
-  *r_tmpfname = NULL;
-
-# ifdef USE_ONLY_8DOT3
-  /* Here is another Windoze bug?:
-   * you cant rename("pubring.kbx.tmp", "pubring.kbx");
-   * but       rename("pubring.kbx.tmp", "pubring.aaa");
-   * works.  So we replace ".kbx" by ".kb_" or ".k__".  Note that we
-   * can't use ".bak" and ".tmp", because these suffixes are used by
-   * gpg and would lead to a sharing violation or data corruption.
-   */
-  if (strlen (template) > 4
-      && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") )
-    {
-      bakfname = xtrymalloc (strlen (template) + 1);
-      if (!bakfname)
-        return gpg_error_from_syserror ();
-      strcpy (bakfname, template);
-      strcpy (bakfname+strlen(template)-4, EXTSEP_S "kb_");
-
-      tmpfname = xtrymalloc (strlen (template) + 1);
-      if (!tmpfname)
-        {
-          gpg_error_t tmperr = gpg_error_from_syserror ();
-          xfree (bakfname);
-          return tmperr;
-        }
-      strcpy (tmpfname,template);
-      strcpy (tmpfname + strlen (template)-4, EXTSEP_S "k__");
-    }
-  else
-    { /* File does not end with kbx, thus we hope we are working on a
-         modern file system and appending a suffix works. */
-      bakfname = xtrymalloc ( strlen (template) + 5);
-      if (!bakfname)
-        return gpg_error_from_syserror ();
-      strcpy (stpcpy (bakfname, template), EXTSEP_S "kb_");
+  gpg_error_t err;
 
-      tmpfname = xtrymalloc ( strlen (template) + 5);
-      if (!tmpfname)
+  err = keybox_tmp_names (template, 0, r_bakfname, r_tmpfname);
+  if (!err)
+    {
+      *r_fp = fopen (*r_tmpfname, "wb");
+      if (!*r_fp)
         {
-          gpg_error_t tmperr = gpg_error_from_syserror ();
-          xfree (bakfname);
-          return tmperr;
+          err = gpg_error_from_syserror ();
+          xfree (*r_tmpfname);
+          *r_tmpfname = NULL;
+          xfree (*r_bakfname);
+          *r_bakfname = NULL;
         }
-      strcpy (stpcpy (tmpfname, template), EXTSEP_S "k__");
-    }
-# else /* Posix file names */
-  bakfname = xtrymalloc (strlen (template) + 2);
-  if (!bakfname)
-    return gpg_error_from_syserror ();
-  strcpy (stpcpy (bakfname,template),"~");
-
-  tmpfname = xtrymalloc ( strlen (template) + 5);
-  if (!tmpfname)
-    {
-      gpg_error_t tmperr = gpg_error_from_syserror ();
-      xfree (bakfname);
-      return tmperr;
     }
-  strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp");
-# endif /* Posix filename */
 
-  *r_fp = fopen (tmpfname, "wb");
-  if (!*r_fp)
-    {
-      gpg_error_t tmperr = gpg_error_from_syserror ();
-      xfree (tmpfname);
-      xfree (bakfname);
-      return tmperr;
-    }
-
-  *r_bakfname = bakfname;
-  *r_tmpfname = tmpfname;
-  return 0;
+  return err;
 }
 
 
@@ -153,6 +98,7 @@ rename_tmp_file (const char *bakfname, const char *tmpfname,
                  const char *fname, int secret )
 {
   int rc=0;
+  int block = 0;
 
   /* restrict the permissions for secret keyboxs */
 #ifndef HAVE_DOSISH_SYSTEM
@@ -175,52 +121,51 @@ rename_tmp_file (const char *bakfname, const char *tmpfname,
   /* First make a backup file except for secret keyboxes. */
   if (!secret)
     {
-#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
-      gnupg_remove (bakfname);
-#endif
-      if (rename (fname, bakfname) )
-        {
-          return gpg_error_from_syserror ();
-       }
+      block = 1;
+      rc = gnupg_rename_file (fname, bakfname, &block);
+      if (rc)
+        goto leave;
     }
 
   /* Then rename the file. */
-#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
-  gnupg_remove (fname);
-#endif
-  if (rename (tmpfname, fname) )
+  rc = gnupg_rename_file (tmpfname, fname, NULL);
+  if (block)
     {
-      rc = gpg_error_from_syserror ();
-      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;
+      gnupg_unblock_all_signals ();
+      block = 0;
     }
+  /* if (rc) */
+  /*   { */
+  /*     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 0;
+ leave:
+  if (block)
+    gnupg_unblock_all_signals ();
+  return rc;
 }
 
 
 
-/* Perform insert/delete/update operation.
-    mode 1 = insert
-        2 = delete
-        3 = update
-*/
+/* Perform insert/delete/update operation.  MODE is one of
+   FILECOPY_INSERT, FILECOPY_DELETE, FILECOPY_UPDATE.  FOR_OPENPGP
+   indicates that this is called due to an OpenPGP keyblock change.  */
 static int
 blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
-               int secret, off_t start_offset)
+               int secret, int for_openpgp, off_t start_offset)
 {
   FILE *fp, *newfp;
   int rc=0;
   char *bakfname = NULL;
   char *tmpfname = NULL;
-  char buffer[4096];
+  char buffer[4096];  /* (Must be at least 32 bytes) */
   int nread, nbytes;
 
   /* Open the source file. Because we do a rename, we have to check the
@@ -229,7 +174,7 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
     return gpg_error_from_syserror ();
 
   fp = fopen (fname, "rb");
-  if (mode == 1 && !fp && errno == ENOENT)
+  if (mode == FILECOPY_INSERT && !fp && errno == ENOENT)
     {
       /* Insert mode but file does not exist:
          Create a new keybox file. */
@@ -237,13 +182,19 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
       if (!newfp )
         return gpg_error_from_syserror ();
 
-      rc = _keybox_write_header_blob (newfp);
+      rc = _keybox_write_header_blob (newfp, for_openpgp);
       if (rc)
-        return rc;
+        {
+          fclose (newfp);
+          return rc;
+        }
 
       rc = _keybox_write_blob (blob, newfp);
       if (rc)
-        return rc;
+        {
+          fclose (newfp);
+          return rc;
+        }
 
       if ( fclose (newfp) )
         return gpg_error_from_syserror ();
@@ -262,35 +213,50 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
       goto leave;
     }
 
-  /* Create the new file. */
+  /* Create the new file.  On success NEWFP is initialized.  */
   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
   if (rc)
     {
-      fclose(fp);
+      fclose (fp);
       goto leave;
     }
 
   /* prepare for insert */
-  if (mode == 1)
+  if (mode == FILECOPY_INSERT)
     {
-      /* Copy everything to the new file. */
+      int first_record = 1;
+
+      /* Copy everything to the new file.  If this is for OpenPGP, we
+         make sure that the openpgp flag is set in the header.  (We
+         failsafe the blob type.) */
       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
         {
+          if (first_record && for_openpgp
+              && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
+            {
+              first_record = 0;
+              buffer[7] |= 0x02; /* OpenPGP data may be available.  */
+            }
+
           if (fwrite (buffer, nread, 1, newfp) != 1)
             {
               rc = gpg_error_from_syserror ();
+              fclose (fp);
+              fclose (newfp);
               goto leave;
             }
         }
       if (ferror (fp))
         {
           rc = gpg_error_from_syserror ();
+          fclose (fp);
+          fclose (newfp);
           goto leave;
         }
     }
 
   /* Prepare for delete or update. */
-  if ( mode == 2 || mode == 3 )
+  if ( mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE )
     {
       off_t current = 0;
 
@@ -308,43 +274,59 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
           if (fwrite (buffer, nread, 1, newfp) != 1)
             {
               rc = gpg_error_from_syserror ();
+              fclose (fp);
+              fclose (newfp);
               goto leave;
             }
         }
       if (ferror (fp))
         {
           rc = gpg_error_from_syserror ();
+          fclose (fp);
+          fclose (newfp);
           goto leave;
         }
 
       /* Skip this blob. */
-      rc = _keybox_read_blob (NULL, fp);
+      rc = _keybox_read_blob (NULL, fp, NULL);
       if (rc)
-        return rc;
+        {
+          fclose (fp);
+          fclose (newfp);
+          return rc;
+        }
     }
 
   /* Do an insert or update. */
-  if ( mode == 1 || mode == 3 )
+  if ( mode == FILECOPY_INSERT || mode == FILECOPY_UPDATE )
     {
       rc = _keybox_write_blob (blob, newfp);
       if (rc)
+        {
+          fclose (fp);
+          fclose (newfp);
           return rc;
+        }
     }
 
   /* Copy the rest of the packet for an delete or update. */
-  if (mode == 2 || mode == 3)
+  if (mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE)
     {
       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
         {
           if (fwrite (buffer, nread, 1, newfp) != 1)
             {
               rc = gpg_error_from_syserror ();
+              fclose (fp);
+              fclose (newfp);
               goto leave;
             }
         }
       if (ferror (fp))
         {
           rc = gpg_error_from_syserror ();
+          fclose (fp);
+          fclose (newfp);
           goto leave;
         }
     }
@@ -371,12 +353,9 @@ blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
 }
 
 
-/* Insert the OpenPGP keyblock {IMAGE,IMAGELEN} into HD.  SIGSTATUS is
-   a vector describing the status of the signatures; its first element
-   gives the number of following elements.  */
+/* Insert the OpenPGP keyblock {IMAGE,IMAGELEN} into HD. */
 gpg_error_t
-keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen,
-                        u32 *sigstatus)
+keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen)
 {
   gpg_error_t err;
   const char *fname;
@@ -403,11 +382,11 @@ keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen,
     return err;
   assert (nparsed <= imagelen);
   err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
-                                     sigstatus, hd->ephemeral);
+                                      hd->ephemeral);
   _keybox_destroy_openpgp_info (&info);
   if (!err)
     {
-      err = blob_filecopy (1, fname, blob, hd->secret, 0);
+      err = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 1, 0);
       _keybox_release_blob (blob);
       /*    if (!rc && !hd->secret && kb_offtbl) */
       /*      { */
@@ -423,10 +402,47 @@ keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen,
 gpg_error_t
 keybox_update_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen)
 {
-  (void)hd;
-  (void)image;
-  (void)imagelen;
-  return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+  gpg_error_t err;
+  const char *fname;
+  off_t off;
+  KEYBOXBLOB blob;
+  size_t nparsed;
+  struct _keybox_openpgp_info info;
+
+  if (!hd || !image || !imagelen)
+    return gpg_error (GPG_ERR_INV_VALUE);
+  if (!hd->found.blob)
+    return gpg_error (GPG_ERR_NOTHING_FOUND);
+  if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP)
+    return gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
+  fname = hd->kb->fname;
+  if (!fname)
+    return gpg_error (GPG_ERR_INV_HANDLE);
+
+  off = _keybox_get_blob_fileoffset (hd->found.blob);
+  if (off == (off_t)-1)
+    return gpg_error (GPG_ERR_GENERAL);
+
+  /* Close this the file so that we do no mess up the position for a
+     next search.  */
+  _keybox_close_file (hd);
+
+  /* Build a new blob.  */
+  err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
+  if (err)
+    return err;
+  assert (nparsed <= imagelen);
+  err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
+                                     hd->ephemeral);
+  _keybox_destroy_openpgp_info (&info);
+
+  /* Update the keyblock.  */
+  if (!err)
+    {
+      err = blob_filecopy (FILECOPY_UPDATE, fname, blob, hd->secret, 1, off);
+      _keybox_release_blob (blob);
+    }
+  return err;
 }
 
 
@@ -456,7 +472,7 @@ keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
   rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
   if (!rc)
     {
-      rc = blob_filecopy (1, fname, blob, hd->secret, 0);
+      rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0, 0);
       _keybox_release_blob (blob);
       /*    if (!rc && !hd->secret && kb_offtbl) */
       /*      { */
@@ -525,7 +541,7 @@ keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
 
   ec = 0;
   if (fseeko (fp, off, SEEK_SET))
-    ec = gpg_error_from_syserror ();
+    ec = gpg_err_code_from_syserror ();
   else
     {
       unsigned char tmp[4];
@@ -649,16 +665,15 @@ keybox_compress (KEYBOX_HANDLE hd)
 
   /* A quick test to see if we need to compress the file at all.  We
      schedule a compress run after 3 hours. */
-  if ( !_keybox_read_blob (&blob, fp) )
+  if ( !_keybox_read_blob (&blob, fp, NULL) )
     {
       const unsigned char *buffer;
       size_t length;
 
       buffer = _keybox_get_blob_image (blob, &length);
-      if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
+      if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
         {
-          u32 last_maint = ((buffer[20] << 24) | (buffer[20+1] << 16)
-                            | (buffer[20+2] << 8) | (buffer[20+3]));
+          u32 last_maint = buf32_to_u32 (buffer+20);
 
           if ( (last_maint + 3*3600) > time (NULL) )
             {
@@ -676,7 +691,7 @@ keybox_compress (KEYBOX_HANDLE hd)
   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
   if (rc)
     {
-      fclose(fp);
+      fclose (fp);
       return rc;;
     }
 
@@ -688,7 +703,7 @@ keybox_compress (KEYBOX_HANDLE hd)
   cut_time = time(NULL) - 86400;
   first_blob = 1;
   skipped_deleted = 0;
-  for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
+  for (rc=0; !(read_rc = _keybox_read_blob (&blob, fp, &skipped_deleted));
        _keybox_release_blob (blob), blob = NULL )
     {
       unsigned int blobflags;
@@ -702,10 +717,12 @@ keybox_compress (KEYBOX_HANDLE hd)
       if (first_blob)
         {
           first_blob = 0;
-          if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
+          if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
             {
-              /* Write out the blob with an updated maintenance time stamp. */
-              _keybox_update_header_blob (blob);
+              /* Write out the blob with an updated maintenance time
+                 stamp and if needed (ie. used by gpg) set the openpgp
+                 flag.  */
+              _keybox_update_header_blob (blob, hd->for_openpgp);
               rc = _keybox_write_blob (blob, newfp);
               if (rc)
                 break;
@@ -713,12 +730,12 @@ keybox_compress (KEYBOX_HANDLE hd)
             }
 
           /* The header blob is missing.  Insert it.  */
-          rc = _keybox_write_header_blob (newfp);
+          rc = _keybox_write_header_blob (newfp, hd->for_openpgp);
           if (rc)
             break;
           any_changes = 1;
         }
-      else if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
+      else if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
         {
           /* Oops: There is another header record - remove it. */
           any_changes = 1;
@@ -732,7 +749,7 @@ keybox_compress (KEYBOX_HANDLE hd)
           rc = gpg_error (GPG_ERR_BUG);
           break;
         }
-      blobflags = ((buffer[pos] << 8) | (buffer[pos+1]));
+      blobflags = buf16_to_uint (buffer+pos);
       if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
         {
           /* This is an ephemeral blob. */
@@ -741,8 +758,7 @@ keybox_compress (KEYBOX_HANDLE hd)
               || size != 4)
             created_at = 0; /* oops. */
           else
-            created_at = ((buffer[pos] << 24) | (buffer[pos+1] << 16)
-                          | (buffer[pos+2] << 8) | (buffer[pos+3]));
+            created_at = buf32_to_u32 (buffer+pos);
 
           if (created_at && created_at < cut_time)
             {