LCOV - code coverage report
Current view: top level - kbx - keybox-update.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 148 347 42.7 %
Date: 2016-09-12 12:29:17 Functions: 8 10 80.0 %

          Line data    Source code
       1             : /* keybox-update.c - keybox update operations
       2             :  * Copyright (C) 2001, 2003, 2004, 2012 Free Software Foundation, Inc.
       3             :  *
       4             :  * This file is part of GnuPG.
       5             :  *
       6             :  * GnuPG is free software; you can redistribute it and/or modify
       7             :  * it under the terms of the GNU General Public License as published by
       8             :  * the Free Software Foundation; either version 3 of the License, or
       9             :  * (at your option) any later version.
      10             :  *
      11             :  * GnuPG is distributed in the hope that it will be useful,
      12             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :  * GNU General Public License for more details.
      15             :  *
      16             :  * You should have received a copy of the GNU General Public License
      17             :  * along with this program; if not, see <http://www.gnu.org/licenses/>.
      18             :  */
      19             : 
      20             : #include <config.h>
      21             : #include <stdlib.h>
      22             : #include <stdio.h>
      23             : #include <string.h>
      24             : #include <errno.h>
      25             : #include <time.h>
      26             : #include <unistd.h>
      27             : #include <assert.h>
      28             : 
      29             : #include "keybox-defs.h"
      30             : #include "../common/sysutils.h"
      31             : #include "../common/host2net.h"
      32             : #include "../common/utilproto.h"
      33             : 
      34             : #define EXTSEP_S "."
      35             : 
      36             : #define FILECOPY_INSERT 1
      37             : #define FILECOPY_DELETE 2
      38             : #define FILECOPY_UPDATE 3
      39             : 
      40             : 
      41             : #if !defined(HAVE_FSEEKO) && !defined(fseeko)
      42             : 
      43             : #ifdef HAVE_LIMITS_H
      44             : # include <limits.h>
      45             : #endif
      46             : #ifndef LONG_MAX
      47             : # define LONG_MAX ((long) ((unsigned long) -1 >> 1))
      48             : #endif
      49             : #ifndef LONG_MIN
      50             : # define LONG_MIN (-1 - LONG_MAX)
      51             : #endif
      52             : 
      53             : /****************
      54             :  * A substitute for fseeko, for hosts that don't have it.
      55             :  */
      56             : static int
      57             : fseeko (FILE * stream, off_t newpos, int whence)
      58             : {
      59             :   while (newpos != (long) newpos)
      60             :     {
      61             :       long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
      62             :       if (fseek (stream, pos, whence) != 0)
      63             :         return -1;
      64             :       newpos -= pos;
      65             :       whence = SEEK_CUR;
      66             :     }
      67             :   return fseek (stream, (long) newpos, whence);
      68             : }
      69             : #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */
      70             : 
      71             : 
      72             : static int
      73          69 : create_tmp_file (const char *template,
      74             :                  char **r_bakfname, char **r_tmpfname, FILE **r_fp)
      75             : {
      76             :   gpg_error_t err;
      77             : 
      78          69 :   err = keybox_tmp_names (template, 0, r_bakfname, r_tmpfname);
      79          69 :   if (!err)
      80             :     {
      81          69 :       *r_fp = fopen (*r_tmpfname, "wb");
      82          69 :       if (!*r_fp)
      83             :         {
      84           0 :           err = gpg_error_from_syserror ();
      85           0 :           xfree (*r_tmpfname);
      86           0 :           *r_tmpfname = NULL;
      87           0 :           xfree (*r_bakfname);
      88           0 :           *r_bakfname = NULL;
      89             :         }
      90             :     }
      91             : 
      92          69 :   return err;
      93             : }
      94             : 
      95             : 
      96             : static int
      97          69 : rename_tmp_file (const char *bakfname, const char *tmpfname,
      98             :                  const char *fname, int secret )
      99             : {
     100          69 :   int rc=0;
     101          69 :   int block = 0;
     102             : 
     103             :   /* restrict the permissions for secret keyboxs */
     104             : #ifndef HAVE_DOSISH_SYSTEM
     105             : /*    if (secret && !opt.preserve_permissions) */
     106             : /*      { */
     107             : /*        if (chmod (tmpfname, S_IRUSR | S_IWUSR) )  */
     108             : /*          { */
     109             : /*            log_debug ("chmod of '%s' failed: %s\n", */
     110             : /*                       tmpfname, strerror(errno) ); */
     111             : /*            return KEYBOX_Write_File; */
     112             : /*      } */
     113             : /*      } */
     114             : #endif
     115             : 
     116             :   /* fixme: invalidate close caches (not used with stdio)*/
     117             : /*    iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)tmpfname ); */
     118             : /*    iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)bakfname ); */
     119             : /*    iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname ); */
     120             : 
     121             :   /* First make a backup file except for secret keyboxes. */
     122          69 :   if (!secret)
     123             :     {
     124          69 :       block = 1;
     125          69 :       rc = keybox_file_rename (fname, bakfname, &block);
     126          69 :       if (rc)
     127           0 :         goto leave;
     128             :     }
     129             : 
     130             :   /* Then rename the file. */
     131          69 :   rc = keybox_file_rename (tmpfname, fname, NULL);
     132          69 :   if (block)
     133             :     {
     134          69 :       gnupg_unblock_all_signals ();
     135          69 :       block = 0;
     136             :     }
     137             :   /* if (rc) */
     138             :   /*   { */
     139             :   /*     if (secret) */
     140             :   /*       { */
     141             :   /*         log_info ("WARNING: 2 files with confidential" */
     142             :   /*                   " information exists.\n"); */
     143             :   /*         log_info ("%s is the unchanged one\n", fname ); */
     144             :   /*         log_info ("%s is the new one\n", tmpfname ); */
     145             :   /*         log_info ("Please fix this possible security flaw\n"); */
     146             :   /*       } */
     147             :   /*   } */
     148             : 
     149             :  leave:
     150          69 :   if (block)
     151           0 :     gnupg_unblock_all_signals ();
     152          69 :   return rc;
     153             : }
     154             : 
     155             : 
     156             : 
     157             : /* Perform insert/delete/update operation.  MODE is one of
     158             :    FILECOPY_INSERT, FILECOPY_DELETE, FILECOPY_UPDATE.  FOR_OPENPGP
     159             :    indicates that this is called due to an OpenPGP keyblock change.  */
     160             : static int
     161          69 : blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
     162             :                int secret, int for_openpgp, off_t start_offset)
     163             : {
     164             :   FILE *fp, *newfp;
     165          69 :   int rc=0;
     166          69 :   char *bakfname = NULL;
     167          69 :   char *tmpfname = NULL;
     168             :   char buffer[4096];  /* (Must be at least 32 bytes) */
     169             :   int nread, nbytes;
     170             : 
     171             :   /* Open the source file. Because we do a rename, we have to check the
     172             :      permissions of the file */
     173          69 :   if (access (fname, W_OK))
     174           0 :     return gpg_error_from_syserror ();
     175             : 
     176          69 :   fp = fopen (fname, "rb");
     177          69 :   if (mode == FILECOPY_INSERT && !fp && errno == ENOENT)
     178             :     {
     179             :       /* Insert mode but file does not exist:
     180             :          Create a new keybox file. */
     181           0 :       newfp = fopen (fname, "wb");
     182           0 :       if (!newfp )
     183           0 :         return gpg_error_from_syserror ();
     184             : 
     185           0 :       rc = _keybox_write_header_blob (newfp, for_openpgp);
     186           0 :       if (rc)
     187             :         {
     188           0 :           fclose (newfp);
     189           0 :           return rc;
     190             :         }
     191             : 
     192           0 :       rc = _keybox_write_blob (blob, newfp);
     193           0 :       if (rc)
     194             :         {
     195           0 :           fclose (newfp);
     196           0 :           return rc;
     197             :         }
     198             : 
     199           0 :       if ( fclose (newfp) )
     200           0 :         return gpg_error_from_syserror ();
     201             : 
     202             : /*        if (chmod( fname, S_IRUSR | S_IWUSR )) */
     203             : /*          { */
     204             : /*            log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
     205             : /*            return KEYBOX_File_Error; */
     206             : /*          } */
     207           0 :       return 0; /* Ready. */
     208             :     }
     209             : 
     210          69 :   if (!fp)
     211             :     {
     212           0 :       rc = gpg_error_from_syserror ();
     213           0 :       goto leave;
     214             :     }
     215             : 
     216             :   /* Create the new file.  On success NEWFP is initialized.  */
     217          69 :   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
     218          69 :   if (rc)
     219             :     {
     220           0 :       fclose (fp);
     221           0 :       goto leave;
     222             :     }
     223             : 
     224             :   /* prepare for insert */
     225          69 :   if (mode == FILECOPY_INSERT)
     226             :     {
     227          67 :       int first_record = 1;
     228             : 
     229             :       /* Copy everything to the new file.  If this is for OpenPGP, we
     230             :          make sure that the openpgp flag is set in the header.  (We
     231             :          failsafe the blob type.) */
     232         630 :       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
     233             :         {
     234         496 :           if (first_record && for_openpgp
     235          64 :               && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
     236             :             {
     237          64 :               first_record = 0;
     238          64 :               buffer[7] |= 0x02; /* OpenPGP data may be available.  */
     239             :             }
     240             : 
     241         496 :           if (fwrite (buffer, nread, 1, newfp) != 1)
     242             :             {
     243           0 :               rc = gpg_error_from_syserror ();
     244           0 :               fclose (fp);
     245           0 :               fclose (newfp);
     246           0 :               goto leave;
     247             :             }
     248             :         }
     249          67 :       if (ferror (fp))
     250             :         {
     251           0 :           rc = gpg_error_from_syserror ();
     252           0 :           fclose (fp);
     253           0 :           fclose (newfp);
     254           0 :           goto leave;
     255             :         }
     256             :     }
     257             : 
     258             :   /* Prepare for delete or update. */
     259          69 :   if ( mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE )
     260             :     {
     261           2 :       off_t current = 0;
     262             : 
     263             :       /* Copy first part to the new file. */
     264          21 :       while ( current < start_offset )
     265             :         {
     266          17 :           nbytes = DIM(buffer);
     267          17 :           if (current + nbytes > start_offset)
     268           2 :               nbytes = start_offset - current;
     269          17 :           nread = fread (buffer, 1, nbytes, fp);
     270          17 :           if (!nread)
     271           0 :             break;
     272          17 :           current += nread;
     273             : 
     274          17 :           if (fwrite (buffer, nread, 1, newfp) != 1)
     275             :             {
     276           0 :               rc = gpg_error_from_syserror ();
     277           0 :               fclose (fp);
     278           0 :               fclose (newfp);
     279           0 :               goto leave;
     280             :             }
     281             :         }
     282           2 :       if (ferror (fp))
     283             :         {
     284           0 :           rc = gpg_error_from_syserror ();
     285           0 :           fclose (fp);
     286           0 :           fclose (newfp);
     287           0 :           goto leave;
     288             :         }
     289             : 
     290             :       /* Skip this blob. */
     291           2 :       rc = _keybox_read_blob (NULL, fp);
     292           2 :       if (rc)
     293             :         {
     294           0 :           fclose (fp);
     295           0 :           fclose (newfp);
     296           0 :           return rc;
     297             :         }
     298             :     }
     299             : 
     300             :   /* Do an insert or update. */
     301          69 :   if ( mode == FILECOPY_INSERT || mode == FILECOPY_UPDATE )
     302             :     {
     303          69 :       rc = _keybox_write_blob (blob, newfp);
     304          69 :       if (rc)
     305             :         {
     306           0 :           fclose (fp);
     307           0 :           fclose (newfp);
     308           0 :           return rc;
     309             :         }
     310             :     }
     311             : 
     312             :   /* Copy the rest of the packet for an delete or update. */
     313          69 :   if (mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE)
     314             :     {
     315          16 :       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
     316             :         {
     317          12 :           if (fwrite (buffer, nread, 1, newfp) != 1)
     318             :             {
     319           0 :               rc = gpg_error_from_syserror ();
     320           0 :               fclose (fp);
     321           0 :               fclose (newfp);
     322           0 :               goto leave;
     323             :             }
     324             :         }
     325           2 :       if (ferror (fp))
     326             :         {
     327           0 :           rc = gpg_error_from_syserror ();
     328           0 :           fclose (fp);
     329           0 :           fclose (newfp);
     330           0 :           goto leave;
     331             :         }
     332             :     }
     333             : 
     334             :   /* Close both files. */
     335          69 :   if (fclose(fp))
     336             :     {
     337           0 :       rc = gpg_error_from_syserror ();
     338           0 :       fclose (newfp);
     339           0 :       goto leave;
     340             :     }
     341          69 :   if (fclose(newfp))
     342             :     {
     343           0 :       rc = gpg_error_from_syserror ();
     344           0 :       goto leave;
     345             :     }
     346             : 
     347          69 :   rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
     348             : 
     349             :  leave:
     350          69 :   xfree(bakfname);
     351          69 :   xfree(tmpfname);
     352          69 :   return rc;
     353             : }
     354             : 
     355             : 
     356             : /* Insert the OpenPGP keyblock {IMAGE,IMAGELEN} into HD.  SIGSTATUS is
     357             :    a vector describing the status of the signatures; its first element
     358             :    gives the number of following elements.  */
     359             : gpg_error_t
     360          64 : keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen,
     361             :                         u32 *sigstatus)
     362             : {
     363             :   gpg_error_t err;
     364             :   const char *fname;
     365             :   KEYBOXBLOB blob;
     366             :   size_t nparsed;
     367             :   struct _keybox_openpgp_info info;
     368             : 
     369          64 :   if (!hd)
     370           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     371          64 :   if (!hd->kb)
     372           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     373          64 :   fname = hd->kb->fname;
     374          64 :   if (!fname)
     375           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     376             : 
     377             : 
     378             :   /* Close this one otherwise we will mess up the position for a next
     379             :      search.  Fixme: it would be better to adjust the position after
     380             :      the write operation.  */
     381          64 :   _keybox_close_file (hd);
     382             : 
     383          64 :   err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
     384          64 :   if (err)
     385           0 :     return err;
     386          64 :   assert (nparsed <= imagelen);
     387          64 :   err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
     388             :                                      sigstatus, hd->ephemeral);
     389          64 :   _keybox_destroy_openpgp_info (&info);
     390          64 :   if (!err)
     391             :     {
     392          64 :       err = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 1, 0);
     393          64 :       _keybox_release_blob (blob);
     394             :       /*    if (!rc && !hd->secret && kb_offtbl) */
     395             :       /*      { */
     396             :       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
     397             :       /*      } */
     398             :     }
     399          64 :   return err;
     400             : }
     401             : 
     402             : 
     403             : /* Update the current key at HD with the given OpenPGP keyblock in
     404             :    {IMAGE,IMAGELEN}.  */
     405             : gpg_error_t
     406           2 : keybox_update_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen)
     407             : {
     408             :   gpg_error_t err;
     409             :   const char *fname;
     410             :   off_t off;
     411             :   KEYBOXBLOB blob;
     412             :   size_t nparsed;
     413             :   struct _keybox_openpgp_info info;
     414             : 
     415           2 :   if (!hd || !image || !imagelen)
     416           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     417           2 :   if (!hd->found.blob)
     418           0 :     return gpg_error (GPG_ERR_NOTHING_FOUND);
     419           2 :   if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP)
     420           0 :     return gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
     421           2 :   fname = hd->kb->fname;
     422           2 :   if (!fname)
     423           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     424             : 
     425           2 :   off = _keybox_get_blob_fileoffset (hd->found.blob);
     426           2 :   if (off == (off_t)-1)
     427           0 :     return gpg_error (GPG_ERR_GENERAL);
     428             : 
     429             :   /* Close this the file so that we do no mess up the position for a
     430             :      next search.  */
     431           2 :   _keybox_close_file (hd);
     432             : 
     433             :   /* Build a new blob.  */
     434           2 :   err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
     435           2 :   if (err)
     436           0 :     return err;
     437           2 :   assert (nparsed <= imagelen);
     438           2 :   err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
     439             :                                      NULL, hd->ephemeral);
     440           2 :   _keybox_destroy_openpgp_info (&info);
     441             : 
     442             :   /* Update the keyblock.  */
     443           2 :   if (!err)
     444             :     {
     445           2 :       err = blob_filecopy (FILECOPY_UPDATE, fname, blob, hd->secret, 1, off);
     446           2 :       _keybox_release_blob (blob);
     447             :     }
     448           2 :   return err;
     449             : }
     450             : 
     451             : 
     452             : 
     453             : #ifdef KEYBOX_WITH_X509
     454             : int
     455           3 : keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
     456             :                     unsigned char *sha1_digest)
     457             : {
     458             :   int rc;
     459             :   const char *fname;
     460             :   KEYBOXBLOB blob;
     461             : 
     462           3 :   if (!hd)
     463           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     464           3 :   if (!hd->kb)
     465           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     466           3 :   fname = hd->kb->fname;
     467           3 :   if (!fname)
     468           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     469             : 
     470             :   /* Close this one otherwise we will mess up the position for a next
     471             :      search.  Fixme: it would be better to adjust the position after
     472             :      the write operation.  */
     473           3 :   _keybox_close_file (hd);
     474             : 
     475           3 :   rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
     476           3 :   if (!rc)
     477             :     {
     478           3 :       rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0, 0);
     479           3 :       _keybox_release_blob (blob);
     480             :       /*    if (!rc && !hd->secret && kb_offtbl) */
     481             :       /*      { */
     482             :       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
     483             :       /*      } */
     484             :     }
     485           3 :   return rc;
     486             : }
     487             : 
     488             : int
     489           0 : keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
     490             :                     unsigned char *sha1_digest)
     491             : {
     492             :   (void)hd;
     493             :   (void)cert;
     494             :   (void)sha1_digest;
     495           0 :   return -1;
     496             : }
     497             : 
     498             : 
     499             : #endif /*KEYBOX_WITH_X509*/
     500             : 
     501             : /* Note: We assume that the keybox has been locked before the current
     502             :    search was executed.  This is needed so that we can depend on the
     503             :    offset information of the flags. */
     504             : int
     505           0 : keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
     506             : {
     507             :   off_t off;
     508             :   const char *fname;
     509             :   FILE *fp;
     510             :   gpg_err_code_t ec;
     511             :   size_t flag_pos, flag_size;
     512             :   const unsigned char *buffer;
     513             :   size_t length;
     514             : 
     515             :   (void)idx;  /* Not yet used.  */
     516             : 
     517           0 :   if (!hd)
     518           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     519           0 :   if (!hd->found.blob)
     520           0 :     return gpg_error (GPG_ERR_NOTHING_FOUND);
     521           0 :   if (!hd->kb)
     522           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     523           0 :   if (!hd->found.blob)
     524           0 :     return gpg_error (GPG_ERR_NOTHING_FOUND);
     525           0 :   fname = hd->kb->fname;
     526           0 :   if (!fname)
     527           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     528             : 
     529           0 :   off = _keybox_get_blob_fileoffset (hd->found.blob);
     530           0 :   if (off == (off_t)-1)
     531           0 :     return gpg_error (GPG_ERR_GENERAL);
     532             : 
     533           0 :   buffer = _keybox_get_blob_image (hd->found.blob, &length);
     534           0 :   ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
     535           0 :   if (ec)
     536           0 :     return gpg_error (ec);
     537             : 
     538           0 :   off += flag_pos;
     539             : 
     540           0 :   _keybox_close_file (hd);
     541           0 :   fp = fopen (hd->kb->fname, "r+b");
     542           0 :   if (!fp)
     543           0 :     return gpg_error_from_syserror ();
     544             : 
     545           0 :   ec = 0;
     546           0 :   if (fseeko (fp, off, SEEK_SET))
     547           0 :     ec = gpg_err_code_from_syserror ();
     548             :   else
     549             :     {
     550             :       unsigned char tmp[4];
     551             : 
     552           0 :       tmp[0] = value >> 24;
     553           0 :       tmp[1] = value >> 16;
     554           0 :       tmp[2] = value >>  8;
     555           0 :       tmp[3] = value;
     556             : 
     557           0 :       switch (flag_size)
     558             :         {
     559             :         case 1:
     560             :         case 2:
     561             :         case 4:
     562           0 :           if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
     563           0 :             ec = gpg_err_code_from_syserror ();
     564           0 :           break;
     565             :         default:
     566           0 :           ec = GPG_ERR_BUG;
     567           0 :           break;
     568             :         }
     569             :     }
     570             : 
     571           0 :   if (fclose (fp))
     572             :     {
     573           0 :       if (!ec)
     574           0 :         ec = gpg_err_code_from_syserror ();
     575             :     }
     576             : 
     577           0 :   return gpg_error (ec);
     578             : }
     579             : 
     580             : 
     581             : 
     582             : int
     583           3 : keybox_delete (KEYBOX_HANDLE hd)
     584             : {
     585             :   off_t off;
     586             :   const char *fname;
     587             :   FILE *fp;
     588             :   int rc;
     589             : 
     590           3 :   if (!hd)
     591           0 :     return gpg_error (GPG_ERR_INV_VALUE);
     592           3 :   if (!hd->found.blob)
     593           0 :     return gpg_error (GPG_ERR_NOTHING_FOUND);
     594           3 :   if (!hd->kb)
     595           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     596           3 :   fname = hd->kb->fname;
     597           3 :   if (!fname)
     598           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     599             : 
     600           3 :   off = _keybox_get_blob_fileoffset (hd->found.blob);
     601           3 :   if (off == (off_t)-1)
     602           0 :     return gpg_error (GPG_ERR_GENERAL);
     603           3 :   off += 4;
     604             : 
     605           3 :   _keybox_close_file (hd);
     606           3 :   fp = fopen (hd->kb->fname, "r+b");
     607           3 :   if (!fp)
     608           0 :     return gpg_error_from_syserror ();
     609             : 
     610           3 :   if (fseeko (fp, off, SEEK_SET))
     611           0 :     rc = gpg_error_from_syserror ();
     612           3 :   else if (putc (0, fp) == EOF)
     613           0 :     rc = gpg_error_from_syserror ();
     614             :   else
     615           3 :     rc = 0;
     616             : 
     617           3 :   if (fclose (fp))
     618             :     {
     619           0 :       if (!rc)
     620           0 :         rc = gpg_error_from_syserror ();
     621             :     }
     622             : 
     623           3 :   return rc;
     624             : }
     625             : 
     626             : 
     627             : /* Compress the keybox file.  This should be run with the file
     628             :    locked. */
     629             : int
     630           3 : keybox_compress (KEYBOX_HANDLE hd)
     631             : {
     632             :   int read_rc, rc;
     633             :   const char *fname;
     634             :   FILE *fp, *newfp;
     635           3 :   char *bakfname = NULL;
     636           3 :   char *tmpfname = NULL;
     637             :   int first_blob;
     638           3 :   KEYBOXBLOB blob = NULL;
     639             :   u32 cut_time;
     640           3 :   int any_changes = 0;
     641             :   int skipped_deleted;
     642             : 
     643           3 :   if (!hd)
     644           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     645           3 :   if (!hd->kb)
     646           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     647           3 :   if (hd->secret)
     648           0 :     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
     649           3 :   fname = hd->kb->fname;
     650           3 :   if (!fname)
     651           0 :     return gpg_error (GPG_ERR_INV_HANDLE);
     652             : 
     653           3 :   _keybox_close_file (hd);
     654             : 
     655             :   /* Open the source file. Because we do a rename, we have to check the
     656             :      permissions of the file */
     657           3 :   if (access (fname, W_OK))
     658           0 :     return gpg_error_from_syserror ();
     659             : 
     660           3 :   fp = fopen (fname, "rb");
     661           3 :   if (!fp && errno == ENOENT)
     662           0 :     return 0; /* Ready. File has been deleted right after the access above. */
     663           3 :   if (!fp)
     664             :     {
     665           0 :       rc = gpg_error_from_syserror ();
     666           0 :       return rc;
     667             :     }
     668             : 
     669             :   /* A quick test to see if we need to compress the file at all.  We
     670             :      schedule a compress run after 3 hours. */
     671           3 :   if ( !_keybox_read_blob (&blob, fp) )
     672             :     {
     673             :       const unsigned char *buffer;
     674             :       size_t length;
     675             : 
     676           3 :       buffer = _keybox_get_blob_image (blob, &length);
     677           3 :       if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
     678             :         {
     679           3 :           u32 last_maint = buf32_to_u32 (buffer+20);
     680             : 
     681           3 :           if ( (last_maint + 3*3600) > time (NULL) )
     682             :             {
     683           3 :               fclose (fp);
     684           3 :               _keybox_release_blob (blob);
     685           3 :               return 0; /* Compress run not yet needed. */
     686             :             }
     687             :         }
     688           0 :       _keybox_release_blob (blob);
     689           0 :       fseek (fp, 0, SEEK_SET);
     690           0 :       clearerr (fp);
     691             :     }
     692             : 
     693             :   /* Create the new file. */
     694           0 :   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
     695           0 :   if (rc)
     696             :     {
     697           0 :       fclose (fp);
     698           0 :       return rc;;
     699             :     }
     700             : 
     701             : 
     702             :   /* Processing loop.  By reading using _keybox_read_blob we
     703             :      automagically skip any blobs flagged as deleted.  Thus what we
     704             :      only have to do is to check all ephemeral flagged blocks whether
     705             :      their time has come and write out all other blobs. */
     706           0 :   cut_time = time(NULL) - 86400;
     707           0 :   first_blob = 1;
     708           0 :   skipped_deleted = 0;
     709           0 :   for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
     710           0 :        _keybox_release_blob (blob), blob = NULL )
     711             :     {
     712             :       unsigned int blobflags;
     713             :       const unsigned char *buffer;
     714             :       size_t length, pos, size;
     715             :       u32 created_at;
     716             : 
     717           0 :       if (skipped_deleted)
     718           0 :         any_changes = 1;
     719           0 :       buffer = _keybox_get_blob_image (blob, &length);
     720           0 :       if (first_blob)
     721             :         {
     722           0 :           first_blob = 0;
     723           0 :           if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
     724             :             {
     725             :               /* Write out the blob with an updated maintenance time
     726             :                  stamp and if needed (ie. used by gpg) set the openpgp
     727             :                  flag.  */
     728           0 :               _keybox_update_header_blob (blob, hd->for_openpgp);
     729           0 :               rc = _keybox_write_blob (blob, newfp);
     730           0 :               if (rc)
     731           0 :                 break;
     732           0 :               continue;
     733             :             }
     734             : 
     735             :           /* The header blob is missing.  Insert it.  */
     736           0 :           rc = _keybox_write_header_blob (newfp, hd->for_openpgp);
     737           0 :           if (rc)
     738           0 :             break;
     739           0 :           any_changes = 1;
     740             :         }
     741           0 :       else if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
     742             :         {
     743             :           /* Oops: There is another header record - remove it. */
     744           0 :           any_changes = 1;
     745           0 :           continue;
     746             :         }
     747             : 
     748           0 :       if (_keybox_get_flag_location (buffer, length,
     749             :                                      KEYBOX_FLAG_BLOB, &pos, &size)
     750           0 :           || size != 2)
     751             :         {
     752           0 :           rc = gpg_error (GPG_ERR_BUG);
     753           0 :           break;
     754             :         }
     755           0 :       blobflags = buf16_to_uint (buffer+pos);
     756           0 :       if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
     757             :         {
     758             :           /* This is an ephemeral blob. */
     759           0 :           if (_keybox_get_flag_location (buffer, length,
     760             :                                          KEYBOX_FLAG_CREATED_AT, &pos, &size)
     761           0 :               || size != 4)
     762           0 :             created_at = 0; /* oops. */
     763             :           else
     764           0 :             created_at = buf32_to_u32 (buffer+pos);
     765             : 
     766           0 :           if (created_at && created_at < cut_time)
     767             :             {
     768           0 :               any_changes = 1;
     769           0 :               continue; /* Skip this blob. */
     770             :             }
     771             :         }
     772             : 
     773           0 :       rc = _keybox_write_blob (blob, newfp);
     774           0 :       if (rc)
     775           0 :         break;
     776             :     }
     777           0 :   if (skipped_deleted)
     778           0 :     any_changes = 1;
     779           0 :   _keybox_release_blob (blob); blob = NULL;
     780           0 :   if (!rc && read_rc == -1)
     781           0 :     rc = 0;
     782           0 :   else if (!rc)
     783           0 :     rc = read_rc;
     784             : 
     785             :   /* Close both files. */
     786           0 :   if (fclose(fp) && !rc)
     787           0 :     rc = gpg_error_from_syserror ();
     788           0 :   if (fclose(newfp) && !rc)
     789           0 :     rc = gpg_error_from_syserror ();
     790             : 
     791             :   /* Rename or remove the temporary file. */
     792           0 :   if (rc || !any_changes)
     793           0 :     gnupg_remove (tmpfname);
     794             :   else
     795           0 :     rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);
     796             : 
     797           0 :   xfree(bakfname);
     798           0 :   xfree(tmpfname);
     799           0 :   return rc;
     800             : }

Generated by: LCOV version 1.11