Coverage Report

Created: 2022-12-08 06:09

/src/gnupg/kbx/keybox-update.c
Line
Count
Source (jump to first uncovered line)
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 <https://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
0
#define FILECOPY_INSERT 1
37
0
#define FILECOPY_DELETE 2
38
0
#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
create_tmp_file (const char *template,
74
                 char **r_bakfname, char **r_tmpfname, estream_t *r_fp)
75
0
{
76
0
  gpg_error_t err;
77
78
0
  err = keybox_tmp_names (template, 0, r_bakfname, r_tmpfname);
79
0
  if (!err)
80
0
    {
81
0
      *r_fp = es_fopen (*r_tmpfname, "wb");
82
0
      if (!*r_fp)
83
0
        {
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
0
        }
90
0
    }
91
92
0
  return err;
93
0
}
94
95
96
static int
97
rename_tmp_file (const char *bakfname, const char *tmpfname,
98
                 const char *fname, int secret )
99
0
{
100
0
  int rc=0;
101
0
  int block = 0;
102
103
  /* restrict the permissions for secret keyboxs */
104
0
#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
0
#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
0
  if (!secret)
123
0
    {
124
0
      block = 1;
125
0
      rc = gnupg_rename_file (fname, bakfname, &block);
126
0
      if (rc)
127
0
        goto leave;
128
0
    }
129
130
  /* Then rename the file. */
131
0
  rc = gnupg_rename_file (tmpfname, fname, NULL);
132
0
  if (block)
133
0
    {
134
0
      gnupg_unblock_all_signals ();
135
0
      block = 0;
136
0
    }
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
0
 leave:
150
0
  if (block)
151
0
    gnupg_unblock_all_signals ();
152
0
  return rc;
153
0
}
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
blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
162
               int secret, int for_openpgp, off_t start_offset)
163
0
{
164
0
  gpg_err_code_t ec;
165
0
  estream_t fp, newfp;
166
0
  int rc = 0;
167
0
  char *bakfname = NULL;
168
0
  char *tmpfname = NULL;
169
0
  char buffer[4096];  /* (Must be at least 32 bytes) */
170
0
  int nread, nbytes;
171
172
  /* Open the source file. Because we do a rename, we have to check the
173
     permissions of the file */
174
0
  if ((ec = gnupg_access (fname, W_OK)))
175
0
    return gpg_error (ec);
176
177
0
  fp = es_fopen (fname, "rb");
178
0
  if (mode == FILECOPY_INSERT && !fp && errno == ENOENT)
179
0
    {
180
      /* Insert mode but file does not exist:
181
         Create a new keybox file. */
182
0
      newfp = es_fopen (fname, "wb");
183
0
      if (!newfp )
184
0
        return gpg_error_from_syserror ();
185
186
0
      rc = _keybox_write_header_blob (newfp, for_openpgp);
187
0
      if (rc)
188
0
        {
189
0
          es_fclose (newfp);
190
0
          return rc;
191
0
        }
192
193
0
      rc = _keybox_write_blob (blob, newfp, NULL);
194
0
      if (rc)
195
0
        {
196
0
          es_fclose (newfp);
197
0
          return rc;
198
0
        }
199
200
0
      if ( es_fclose (newfp) )
201
0
        return gpg_error_from_syserror ();
202
203
/*        if (chmod( fname, S_IRUSR | S_IWUSR )) */
204
/*          { */
205
/*            log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
206
/*            return KEYBOX_File_Error; */
207
/*          } */
208
0
      return 0; /* Ready. */
209
0
    }
210
211
0
  if (!fp)
212
0
    {
213
0
      rc = gpg_error_from_syserror ();
214
0
      goto leave;
215
0
    }
216
217
  /* Create the new file.  On success NEWFP is initialized.  */
218
0
  rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
219
0
  if (rc)
220
0
    {
221
0
      es_fclose (fp);
222
0
      goto leave;
223
0
    }
224
225
  /* prepare for insert */
226
0
  if (mode == FILECOPY_INSERT)
227
0
    {
228
0
      int first_record = 1;
229
230
      /* Copy everything to the new file.  If this is for OpenPGP, we
231
         make sure that the openpgp flag is set in the header.  (We
232
         failsafe the blob type.) */
233
0
      while ( (nread = es_fread (buffer, 1, DIM(buffer), fp)) > 0 )
234
0
        {
235
0
          if (first_record && for_openpgp
236
0
              && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
237
0
            {
238
0
              first_record = 0;
239
0
              buffer[7] |= 0x02; /* OpenPGP data may be available.  */
240
0
            }
241
242
0
          if (es_fwrite (buffer, nread, 1, newfp) != 1)
243
0
            {
244
0
              rc = gpg_error_from_syserror ();
245
0
              es_fclose (fp);
246
0
              es_fclose (newfp);
247
0
              goto leave;
248
0
            }
249
0
        }
250
0
      if (es_ferror (fp))
251
0
        {
252
0
          rc = gpg_error_from_syserror ();
253
0
          es_fclose (fp);
254
0
          es_fclose (newfp);
255
0
          goto leave;
256
0
        }
257
0
    }
258
259
  /* Prepare for delete or update. */
260
0
  if ( mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE )
261
0
    {
262
0
      off_t current = 0;
263
264
      /* Copy first part to the new file. */
265
0
      while ( current < start_offset )
266
0
        {
267
0
          nbytes = DIM(buffer);
268
0
          if (current + nbytes > start_offset)
269
0
              nbytes = start_offset - current;
270
0
          nread = es_fread (buffer, 1, nbytes, fp);
271
0
          if (!nread)
272
0
            break;
273
0
          current += nread;
274
275
0
          if (es_fwrite (buffer, nread, 1, newfp) != 1)
276
0
            {
277
0
              rc = gpg_error_from_syserror ();
278
0
              es_fclose (fp);
279
0
              es_fclose (newfp);
280
0
              goto leave;
281
0
            }
282
0
        }
283
0
      if (es_ferror (fp))
284
0
        {
285
0
          rc = gpg_error_from_syserror ();
286
0
          es_fclose (fp);
287
0
          es_fclose (newfp);
288
0
          goto leave;
289
0
        }
290
291
      /* Skip this blob. */
292
0
      rc = _keybox_read_blob (NULL, fp, NULL);
293
0
      if (rc)
294
0
        {
295
0
          es_fclose (fp);
296
0
          es_fclose (newfp);
297
0
          goto leave;
298
0
        }
299
0
    }
300
301
  /* Do an insert or update. */
302
0
  if ( mode == FILECOPY_INSERT || mode == FILECOPY_UPDATE )
303
0
    {
304
0
      rc = _keybox_write_blob (blob, newfp, NULL);
305
0
      if (rc)
306
0
        {
307
0
          es_fclose (fp);
308
0
          es_fclose (newfp);
309
0
          goto leave;
310
0
        }
311
0
    }
312
313
  /* Copy the rest of the packet for an delete or update. */
314
0
  if (mode == FILECOPY_DELETE || mode == FILECOPY_UPDATE)
315
0
    {
316
0
      while ( (nread = es_fread (buffer, 1, DIM(buffer), fp)) > 0 )
317
0
        {
318
0
          if (es_fwrite (buffer, nread, 1, newfp) != 1)
319
0
            {
320
0
              rc = gpg_error_from_syserror ();
321
0
              es_fclose (fp);
322
0
              es_fclose (newfp);
323
0
              goto leave;
324
0
            }
325
0
        }
326
0
      if (es_ferror (fp))
327
0
        {
328
0
          rc = gpg_error_from_syserror ();
329
0
          es_fclose (fp);
330
0
          es_fclose (newfp);
331
0
          goto leave;
332
0
        }
333
0
    }
334
335
  /* Close both files. */
336
0
  if (es_fclose(fp))
337
0
    {
338
0
      rc = gpg_error_from_syserror ();
339
0
      es_fclose (newfp);
340
0
      goto leave;
341
0
    }
342
0
  if (es_fclose(newfp))
343
0
    {
344
0
      rc = gpg_error_from_syserror ();
345
0
      goto leave;
346
0
    }
347
348
0
  rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
349
350
0
 leave:
351
0
  xfree(bakfname);
352
0
  xfree(tmpfname);
353
0
  return rc;
354
0
}
355
356
357
/* Insert the OpenPGP keyblock {IMAGE,IMAGELEN} into HD. */
358
gpg_error_t
359
keybox_insert_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen)
360
0
{
361
0
  gpg_error_t err;
362
0
  const char *fname;
363
0
  KEYBOXBLOB blob;
364
0
  size_t nparsed;
365
0
  struct _keybox_openpgp_info info;
366
367
0
  if (!hd)
368
0
    return gpg_error (GPG_ERR_INV_HANDLE);
369
0
  if (!hd->kb)
370
0
    return gpg_error (GPG_ERR_INV_HANDLE);
371
0
  fname = hd->kb->fname;
372
0
  if (!fname)
373
0
    return gpg_error (GPG_ERR_INV_HANDLE);
374
375
376
  /* Close this one otherwise we will mess up the position for a next
377
     search.  Fixme: it would be better to adjust the position after
378
     the write operation.  */
379
0
  _keybox_close_file (hd);
380
381
0
  err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
382
0
  if (err)
383
0
    return err;
384
0
  assert (nparsed <= imagelen);
385
0
  err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
386
0
                                      hd->ephemeral);
387
0
  _keybox_destroy_openpgp_info (&info);
388
0
  if (!err)
389
0
    {
390
0
      err = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 1, 0);
391
0
      _keybox_release_blob (blob);
392
      /*    if (!rc && !hd->secret && kb_offtbl) */
393
      /*      { */
394
      /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
395
      /*      } */
396
0
    }
397
0
  return err;
398
0
}
399
400
401
/* Update the current key at HD with the given OpenPGP keyblock in
402
   {IMAGE,IMAGELEN}.  */
403
gpg_error_t
404
keybox_update_keyblock (KEYBOX_HANDLE hd, const void *image, size_t imagelen)
405
0
{
406
0
  gpg_error_t err;
407
0
  const char *fname;
408
0
  off_t off;
409
0
  KEYBOXBLOB blob;
410
0
  size_t nparsed;
411
0
  struct _keybox_openpgp_info info;
412
413
0
  if (!hd || !image || !imagelen)
414
0
    return gpg_error (GPG_ERR_INV_VALUE);
415
0
  if (!hd->found.blob)
416
0
    return gpg_error (GPG_ERR_NOTHING_FOUND);
417
0
  if (blob_get_type (hd->found.blob) != KEYBOX_BLOBTYPE_PGP)
418
0
    return gpg_error (GPG_ERR_WRONG_BLOB_TYPE);
419
0
  fname = hd->kb->fname;
420
0
  if (!fname)
421
0
    return gpg_error (GPG_ERR_INV_HANDLE);
422
423
0
  off = _keybox_get_blob_fileoffset (hd->found.blob);
424
0
  if (off == (off_t)-1)
425
0
    return gpg_error (GPG_ERR_GENERAL);
426
427
  /* Close the file so that we do no mess up the position for a
428
     next search.  */
429
0
  _keybox_close_file (hd);
430
431
  /* Build a new blob.  */
432
0
  err = _keybox_parse_openpgp (image, imagelen, &nparsed, &info);
433
0
  if (err)
434
0
    return err;
435
0
  assert (nparsed <= imagelen);
436
0
  err = _keybox_create_openpgp_blob (&blob, &info, image, imagelen,
437
0
                                     hd->ephemeral);
438
0
  _keybox_destroy_openpgp_info (&info);
439
440
  /* Update the keyblock.  */
441
0
  if (!err)
442
0
    {
443
0
      err = blob_filecopy (FILECOPY_UPDATE, fname, blob, hd->secret, 1, off);
444
0
      _keybox_release_blob (blob);
445
0
    }
446
0
  return err;
447
0
}
448
449
450
451
#ifdef KEYBOX_WITH_X509
452
int
453
keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
454
                    unsigned char *sha1_digest)
455
{
456
  int rc;
457
  const char *fname;
458
  KEYBOXBLOB blob;
459
460
  if (!hd)
461
    return gpg_error (GPG_ERR_INV_HANDLE);
462
  if (!hd->kb)
463
    return gpg_error (GPG_ERR_INV_HANDLE);
464
  fname = hd->kb->fname;
465
  if (!fname)
466
    return gpg_error (GPG_ERR_INV_HANDLE);
467
468
  /* Close this one otherwise we will mess up the position for a next
469
     search.  Fixme: it would be better to adjust the position after
470
     the write operation.  */
471
  _keybox_close_file (hd);
472
473
  rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
474
  if (!rc)
475
    {
476
      rc = blob_filecopy (FILECOPY_INSERT, fname, blob, hd->secret, 0, 0);
477
      _keybox_release_blob (blob);
478
      /*    if (!rc && !hd->secret && kb_offtbl) */
479
      /*      { */
480
      /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
481
      /*      } */
482
    }
483
  return rc;
484
}
485
486
int
487
keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
488
                    unsigned char *sha1_digest)
489
{
490
  (void)hd;
491
  (void)cert;
492
  (void)sha1_digest;
493
  return -1;
494
}
495
496
497
#endif /*KEYBOX_WITH_X509*/
498
499
/* Note: We assume that the keybox has been locked before the current
500
   search was executed.  This is needed so that we can depend on the
501
   offset information of the flags. */
502
int
503
keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
504
0
{
505
0
  off_t off;
506
0
  const char *fname;
507
0
  estream_t fp;
508
0
  gpg_err_code_t ec;
509
0
  size_t flag_pos, flag_size;
510
0
  const unsigned char *buffer;
511
0
  size_t length;
512
513
0
  (void)idx;  /* Not yet used.  */
514
515
0
  if (!hd)
516
0
    return gpg_error (GPG_ERR_INV_VALUE);
517
0
  if (!hd->found.blob)
518
0
    return gpg_error (GPG_ERR_NOTHING_FOUND);
519
0
  if (!hd->kb)
520
0
    return gpg_error (GPG_ERR_INV_HANDLE);
521
0
  if (!hd->found.blob)
522
0
    return gpg_error (GPG_ERR_NOTHING_FOUND);
523
0
  fname = hd->kb->fname;
524
0
  if (!fname)
525
0
    return gpg_error (GPG_ERR_INV_HANDLE);
526
527
0
  off = _keybox_get_blob_fileoffset (hd->found.blob);
528
0
  if (off == (off_t)-1)
529
0
    return gpg_error (GPG_ERR_GENERAL);
530
531
0
  buffer = _keybox_get_blob_image (hd->found.blob, &length);
532
0
  ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
533
0
  if (ec)
534
0
    return gpg_error (ec);
535
536
0
  off += flag_pos;
537
538
0
  _keybox_close_file (hd);
539
0
  fp = es_fopen (hd->kb->fname, "r+b");
540
0
  if (!fp)
541
0
    return gpg_error_from_syserror ();
542
543
0
  ec = 0;
544
0
  if (es_fseeko (fp, off, SEEK_SET))
545
0
    ec = gpg_err_code_from_syserror ();
546
0
  else
547
0
    {
548
0
      unsigned char tmp[4];
549
550
0
      tmp[0] = value >> 24;
551
0
      tmp[1] = value >> 16;
552
0
      tmp[2] = value >>  8;
553
0
      tmp[3] = value;
554
555
0
      switch (flag_size)
556
0
        {
557
0
        case 1:
558
0
        case 2:
559
0
        case 4:
560
0
          if (es_fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
561
0
            ec = gpg_err_code_from_syserror ();
562
0
          break;
563
0
        default:
564
0
          ec = GPG_ERR_BUG;
565
0
          break;
566
0
        }
567
0
    }
568
569
0
  if (es_fclose (fp))
570
0
    {
571
0
      if (!ec)
572
0
        ec = gpg_err_code_from_syserror ();
573
0
    }
574
575
0
  return gpg_error (ec);
576
0
}
577
578
579
580
int
581
keybox_delete (KEYBOX_HANDLE hd)
582
0
{
583
0
  off_t off;
584
0
  const char *fname;
585
0
  estream_t fp;
586
0
  int rc;
587
588
0
  if (!hd)
589
0
    return gpg_error (GPG_ERR_INV_VALUE);
590
0
  if (!hd->found.blob)
591
0
    return gpg_error (GPG_ERR_NOTHING_FOUND);
592
0
  if (!hd->kb)
593
0
    return gpg_error (GPG_ERR_INV_HANDLE);
594
0
  fname = hd->kb->fname;
595
0
  if (!fname)
596
0
    return gpg_error (GPG_ERR_INV_HANDLE);
597
598
0
  off = _keybox_get_blob_fileoffset (hd->found.blob);
599
0
  if (off == (off_t)-1)
600
0
    return gpg_error (GPG_ERR_GENERAL);
601
0
  off += 4;
602
603
0
  _keybox_close_file (hd);
604
0
  fp = es_fopen (hd->kb->fname, "r+b");
605
0
  if (!fp)
606
0
    return gpg_error_from_syserror ();
607
608
0
  if (es_fseeko (fp, off, SEEK_SET))
609
0
    rc = gpg_error_from_syserror ();
610
0
  else if (es_fputc (0, fp) == EOF)
611
0
    rc = gpg_error_from_syserror ();
612
0
  else
613
0
    rc = 0;
614
615
0
  if (es_fclose (fp))
616
0
    {
617
0
      if (!rc)
618
0
        rc = gpg_error_from_syserror ();
619
0
    }
620
621
0
  return rc;
622
0
}
623
624
625
/* Compress the keybox file.  This should be run with the file
626
   locked. */
627
int
628
keybox_compress (KEYBOX_HANDLE hd)
629
1
{
630
1
  gpg_err_code_t ec;
631
1
  int read_rc, rc;
632
1
  const char *fname;
633
1
  estream_t fp, newfp;
634
1
  char *bakfname = NULL;
635
1
  char *tmpfname = NULL;
636
1
  int first_blob;
637
1
  KEYBOXBLOB blob = NULL;
638
1
  u32 cut_time;
639
1
  int any_changes = 0;
640
1
  int skipped_deleted;
641
642
1
  if (!hd)
643
0
    return gpg_error (GPG_ERR_INV_HANDLE);
644
1
  if (!hd->kb)
645
0
    return gpg_error (GPG_ERR_INV_HANDLE);
646
1
  if (hd->secret)
647
0
    return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
648
1
  fname = hd->kb->fname;
649
1
  if (!fname)
650
0
    return gpg_error (GPG_ERR_INV_HANDLE);
651
652
1
  _keybox_close_file (hd);
653
654
  /* Open the source file. Because we do a rename, we have to check the
655
     permissions of the file */
656
1
  if ((ec = gnupg_access (fname, W_OK)))
657
0
    return gpg_error (ec);
658
659
1
  fp = es_fopen (fname, "rb");
660
1
  if (!fp && errno == ENOENT)
661
0
    return 0; /* Ready. File has been deleted right after the access above. */
662
1
  if (!fp)
663
0
    {
664
0
      rc = gpg_error_from_syserror ();
665
0
      return rc;
666
0
    }
667
668
  /* A quick test to see if we need to compress the file at all.  We
669
     schedule a compress run after 3 hours. */
670
1
  if ( !_keybox_read_blob (&blob, fp, NULL) )
671
1
    {
672
1
      const unsigned char *buffer;
673
1
      size_t length;
674
675
1
      buffer = _keybox_get_blob_image (blob, &length);
676
1
      if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
677
1
        {
678
1
          u32 last_maint = buf32_to_u32 (buffer+20);
679
680
1
          if ( (last_maint + 3*3600) > make_timestamp () )
681
1
            {
682
1
              es_fclose (fp);
683
1
              _keybox_release_blob (blob);
684
1
              return 0; /* Compress run not yet needed. */
685
1
            }
686
1
        }
687
0
      _keybox_release_blob (blob);
688
0
      es_fseek (fp, 0, SEEK_SET);
689
0
      es_clearerr (fp);
690
0
    }
691
692
  /* Create the new file. */
693
0
  rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
694
0
  if (rc)
695
0
    {
696
0
      es_fclose (fp);
697
0
      return rc;;
698
0
    }
699
700
701
  /* Processing loop.  By reading using _keybox_read_blob we
702
     automagically skip any blobs flagged as deleted.  Thus what we
703
     only have to do is to check all ephemeral flagged blocks whether
704
     their time has come and write out all other blobs. */
705
0
  cut_time = make_timestamp () - 86400;
706
0
  first_blob = 1;
707
0
  skipped_deleted = 0;
708
0
  for (rc=0; !(read_rc = _keybox_read_blob (&blob, fp, &skipped_deleted));
709
0
       _keybox_release_blob (blob), blob = NULL )
710
0
    {
711
0
      unsigned int blobflags;
712
0
      const unsigned char *buffer;
713
0
      size_t length, pos, size;
714
0
      u32 created_at;
715
716
0
      if (skipped_deleted)
717
0
        any_changes = 1;
718
0
      buffer = _keybox_get_blob_image (blob, &length);
719
0
      if (first_blob)
720
0
        {
721
0
          first_blob = 0;
722
0
          if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
723
0
            {
724
              /* Write out the blob with an updated maintenance time
725
                 stamp and if needed (ie. used by gpg) set the openpgp
726
                 flag.  */
727
0
              _keybox_update_header_blob (blob, hd->for_openpgp);
728
0
              rc = _keybox_write_blob (blob, newfp, NULL);
729
0
              if (rc)
730
0
                break;
731
0
              continue;
732
0
            }
733
734
          /* The header blob is missing.  Insert it.  */
735
0
          rc = _keybox_write_header_blob (newfp, hd->for_openpgp);
736
0
          if (rc)
737
0
            break;
738
0
          any_changes = 1;
739
0
        }
740
0
      else if (length > 4 && buffer[4] == KEYBOX_BLOBTYPE_HEADER)
741
0
        {
742
          /* Oops: There is another header record - remove it. */
743
0
          any_changes = 1;
744
0
          continue;
745
0
        }
746
747
0
      if (_keybox_get_flag_location (buffer, length,
748
0
                                     KEYBOX_FLAG_BLOB, &pos, &size)
749
0
          || size != 2)
750
0
        {
751
0
          rc = gpg_error (GPG_ERR_BUG);
752
0
          break;
753
0
        }
754
0
      blobflags = buf16_to_uint (buffer+pos);
755
0
      if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
756
0
        {
757
          /* This is an ephemeral blob. */
758
0
          if (_keybox_get_flag_location (buffer, length,
759
0
                                         KEYBOX_FLAG_CREATED_AT, &pos, &size)
760
0
              || size != 4)
761
0
            created_at = 0; /* oops. */
762
0
          else
763
0
            created_at = buf32_to_u32 (buffer+pos);
764
765
0
          if (created_at && created_at < cut_time)
766
0
            {
767
0
              any_changes = 1;
768
0
              continue; /* Skip this blob. */
769
0
            }
770
0
        }
771
772
0
      rc = _keybox_write_blob (blob, newfp, NULL);
773
0
      if (rc)
774
0
        break;
775
0
    }
776
0
  if (skipped_deleted)
777
0
    any_changes = 1;
778
0
  _keybox_release_blob (blob); blob = NULL;
779
0
  if (!rc && read_rc == -1)
780
0
    rc = 0;
781
0
  else if (!rc)
782
0
    rc = read_rc;
783
784
  /* Close both files. */
785
0
  if (es_fclose(fp) && !rc)
786
0
    rc = gpg_error_from_syserror ();
787
0
  if (es_fclose(newfp) && !rc)
788
0
    rc = gpg_error_from_syserror ();
789
790
  /* Rename or remove the temporary file. */
791
0
  if (rc || !any_changes)
792
0
    gnupg_remove (tmpfname);
793
0
  else
794
0
    rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);
795
796
0
  xfree(bakfname);
797
0
  xfree(tmpfname);
798
0
  return rc;
799
0
}