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