Fix last change.
[gnupg.git] / kbx / keybox-update.c
blobc29e316666732bdd3111eecfe4b77e4b9a393e37
1 /* keybox-update.c - keybox update operations
2 * Copyright (C) 2001, 2003, 2004 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
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>
28 #include "keybox-defs.h"
30 #define EXTSEP_S "."
33 #if !defined(HAVE_FSEEKO) && !defined(fseeko)
35 #ifdef HAVE_LIMITS_H
36 # include <limits.h>
37 #endif
38 #ifndef LONG_MAX
39 # define LONG_MAX ((long) ((unsigned long) -1 >> 1))
40 #endif
41 #ifndef LONG_MIN
42 # define LONG_MIN (-1 - LONG_MAX)
43 #endif
45 /****************
46 * A substitute for fseeko, for hosts that don't have it.
48 static int
49 fseeko (FILE * stream, off_t newpos, int whence)
51 while (newpos != (long) newpos)
53 long pos = newpos < 0 ? LONG_MIN : LONG_MAX;
54 if (fseek (stream, pos, whence) != 0)
55 return -1;
56 newpos -= pos;
57 whence = SEEK_CUR;
59 return fseek (stream, (long) newpos, whence);
61 #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */
65 static int
66 create_tmp_file (const char *template,
67 char **r_bakfname, char **r_tmpfname, FILE **r_fp)
69 char *bakfname, *tmpfname;
71 *r_bakfname = NULL;
72 *r_tmpfname = NULL;
74 # ifdef USE_ONLY_8DOT3
75 /* Here is another Windoze bug?:
76 * you cant rename("pubring.kbx.tmp", "pubring.kbx");
77 * but rename("pubring.kbx.tmp", "pubring.aaa");
78 * works. So we replace .kbx by .bak or .tmp
80 if (strlen (template) > 4
81 && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") )
83 bakfname = xtrymalloc (strlen (template) + 1);
84 if (!bakfname)
85 return gpg_error_from_syserror ();
86 strcpy (bakfname, template);
87 strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak");
89 tmpfname = xtrymalloc (strlen (template) + 1);
90 if (!tmpfname)
92 gpg_error_t tmperr = gpg_error_from_syserror ();
93 xfree (bakfname);
94 return tmperr;
96 strcpy (tmpfname,template);
97 strcpy (tmpfname + strlen (template)-4, EXTSEP_S "tmp");
99 else
100 { /* File does not end with kbx; hmmm. */
101 bakfname = xtrymalloc ( strlen (template) + 5);
102 if (!bakfname)
103 return gpg_error_from_syserror ();
104 strcpy (stpcpy (bakfname, template), EXTSEP_S "bak");
106 tmpfname = xtrymalloc ( strlen (template) + 5);
107 if (!tmpfname)
109 gpg_error_t tmperr = gpg_error_from_syserror ();
110 xfree (bakfname);
111 return tmperr;
113 strcpy (stpcpy (tmpfname, template), EXTSEP_S "tmp");
115 # else /* Posix file names */
116 bakfname = xtrymalloc (strlen (template) + 2);
117 if (!bakfname)
118 return gpg_error_from_syserror ();
119 strcpy (stpcpy (bakfname,template),"~");
121 tmpfname = xtrymalloc ( strlen (template) + 5);
122 if (!tmpfname)
124 gpg_error_t tmperr = gpg_error_from_syserror ();
125 xfree (bakfname);
126 return tmperr;
128 strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp");
129 # endif /* Posix filename */
131 *r_fp = fopen (tmpfname, "wb");
132 if (!*r_fp)
134 gpg_error_t tmperr = gpg_error_from_syserror ();
135 xfree (tmpfname);
136 xfree (bakfname);
137 return tmperr;
140 *r_bakfname = bakfname;
141 *r_tmpfname = tmpfname;
142 return 0;
146 static int
147 rename_tmp_file (const char *bakfname, const char *tmpfname,
148 const char *fname, int secret )
150 int rc=0;
152 /* restrict the permissions for secret keyboxs */
153 #ifndef HAVE_DOSISH_SYSTEM
154 /* if (secret && !opt.preserve_permissions) */
155 /* { */
156 /* if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) */
157 /* { */
158 /* log_debug ("chmod of `%s' failed: %s\n", */
159 /* tmpfname, strerror(errno) ); */
160 /* return KEYBOX_Write_File; */
161 /* } */
162 /* } */
163 #endif
165 /* fixme: invalidate close caches (not used with stdio)*/
166 /* iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ); */
167 /* iobuf_ioctl (NULL, 2, 0, (char*)bakfname ); */
168 /* iobuf_ioctl (NULL, 2, 0, (char*)fname ); */
170 /* First make a backup file except for secret keyboxes. */
171 if (!secret)
173 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
174 remove (bakfname);
175 #endif
176 if (rename (fname, bakfname) )
178 return gpg_error_from_syserror ();
182 /* Then rename the file. */
183 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
184 remove (fname);
185 #endif
186 if (rename (tmpfname, fname) )
188 rc = gpg_error_from_syserror ();
189 if (secret)
191 /* log_info ("WARNING: 2 files with confidential" */
192 /* " information exists.\n"); */
193 /* log_info ("%s is the unchanged one\n", fname ); */
194 /* log_info ("%s is the new one\n", tmpfname ); */
195 /* log_info ("Please fix this possible security flaw\n"); */
197 return rc;
200 return 0;
205 /* Perform insert/delete/update operation.
206 mode 1 = insert
207 2 = delete
208 3 = update
210 static int
211 blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob,
212 int secret, off_t start_offset)
214 FILE *fp, *newfp;
215 int rc=0;
216 char *bakfname = NULL;
217 char *tmpfname = NULL;
218 char buffer[4096];
219 int nread, nbytes;
221 /* Open the source file. Because we do a rename, we have to check the
222 permissions of the file */
223 if (access (fname, W_OK))
224 return gpg_error_from_syserror ();
226 fp = fopen (fname, "rb");
227 if (mode == 1 && !fp && errno == ENOENT)
229 /* Insert mode but file does not exist:
230 Create a new keybox file. */
231 newfp = fopen (fname, "wb");
232 if (!newfp )
233 return gpg_error_from_syserror ();
235 rc = _keybox_write_header_blob (newfp);
236 if (rc)
237 return rc;
239 rc = _keybox_write_blob (blob, newfp);
240 if (rc)
241 return rc;
243 if ( fclose (newfp) )
244 return gpg_error_from_syserror ();
246 /* if (chmod( fname, S_IRUSR | S_IWUSR )) */
247 /* { */
248 /* log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
249 /* return KEYBOX_File_Error; */
250 /* } */
251 return 0; /* Ready. */
254 if (!fp)
256 rc = gpg_error_from_syserror ();
257 goto leave;
260 /* Create the new file. */
261 rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
262 if (rc)
264 fclose(fp);
265 goto leave;
268 /* prepare for insert */
269 if (mode == 1)
271 /* Copy everything to the new file. */
272 while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
274 if (fwrite (buffer, nread, 1, newfp) != 1)
276 rc = gpg_error_from_syserror ();
277 goto leave;
280 if (ferror (fp))
282 rc = gpg_error_from_syserror ();
283 goto leave;
287 /* Prepare for delete or update. */
288 if ( mode == 2 || mode == 3 )
290 off_t current = 0;
292 /* Copy first part to the new file. */
293 while ( current < start_offset )
295 nbytes = DIM(buffer);
296 if (current + nbytes > start_offset)
297 nbytes = start_offset - current;
298 nread = fread (buffer, 1, nbytes, fp);
299 if (!nread)
300 break;
301 current += nread;
303 if (fwrite (buffer, nread, 1, newfp) != 1)
305 rc = gpg_error_from_syserror ();
306 goto leave;
309 if (ferror (fp))
311 rc = gpg_error_from_syserror ();
312 goto leave;
315 /* Skip this blob. */
316 rc = _keybox_read_blob (NULL, fp);
317 if (rc)
318 return rc;
321 /* Do an insert or update. */
322 if ( mode == 1 || mode == 3 )
324 rc = _keybox_write_blob (blob, newfp);
325 if (rc)
326 return rc;
329 /* Copy the rest of the packet for an delete or update. */
330 if (mode == 2 || mode == 3)
332 while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
334 if (fwrite (buffer, nread, 1, newfp) != 1)
336 rc = gpg_error_from_syserror ();
337 goto leave;
340 if (ferror (fp))
342 rc = gpg_error_from_syserror ();
343 goto leave;
347 /* Close both files. */
348 if (fclose(fp))
350 rc = gpg_error_from_syserror ();
351 fclose (newfp);
352 goto leave;
354 if (fclose(newfp))
356 rc = gpg_error_from_syserror ();
357 goto leave;
360 rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
362 leave:
363 xfree(bakfname);
364 xfree(tmpfname);
365 return rc;
370 #ifdef KEYBOX_WITH_X509
372 keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
373 unsigned char *sha1_digest)
375 int rc;
376 const char *fname;
377 KEYBOXBLOB blob;
379 if (!hd)
380 return gpg_error (GPG_ERR_INV_HANDLE);
381 if (!hd->kb)
382 return gpg_error (GPG_ERR_INV_HANDLE);
383 fname = hd->kb->fname;
384 if (!fname)
385 return gpg_error (GPG_ERR_INV_HANDLE);
387 /* Close this one otherwise we will mess up the position for a next
388 search. Fixme: it would be better to adjust the position after
389 the write operation. */
390 _keybox_close_file (hd);
392 rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
393 if (!rc)
395 rc = blob_filecopy (1, fname, blob, hd->secret, 0);
396 _keybox_release_blob (blob);
397 /* if (!rc && !hd->secret && kb_offtbl) */
398 /* { */
399 /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
400 /* } */
402 return rc;
406 keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
407 unsigned char *sha1_digest)
409 (void)hd;
410 (void)cert;
411 (void)sha1_digest;
412 return -1;
416 #endif /*KEYBOX_WITH_X509*/
418 /* Note: We assume that the keybox has been locked before the current
419 search was executed. This is needed so that we can depend on the
420 offset information of the flags. */
422 keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
424 off_t off;
425 const char *fname;
426 FILE *fp;
427 gpg_err_code_t ec;
428 size_t flag_pos, flag_size;
429 const unsigned char *buffer;
430 size_t length;
432 (void)idx; /* Not yet used. */
434 if (!hd)
435 return gpg_error (GPG_ERR_INV_VALUE);
436 if (!hd->found.blob)
437 return gpg_error (GPG_ERR_NOTHING_FOUND);
438 if (!hd->kb)
439 return gpg_error (GPG_ERR_INV_HANDLE);
440 if (!hd->found.blob)
441 return gpg_error (GPG_ERR_NOTHING_FOUND);
442 fname = hd->kb->fname;
443 if (!fname)
444 return gpg_error (GPG_ERR_INV_HANDLE);
446 off = _keybox_get_blob_fileoffset (hd->found.blob);
447 if (off == (off_t)-1)
448 return gpg_error (GPG_ERR_GENERAL);
450 buffer = _keybox_get_blob_image (hd->found.blob, &length);
451 ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
452 if (ec)
453 return gpg_error (ec);
455 off += flag_pos;
457 _keybox_close_file (hd);
458 fp = fopen (hd->kb->fname, "r+b");
459 if (!fp)
460 return gpg_error_from_syserror ();
462 ec = 0;
463 if (fseeko (fp, off, SEEK_SET))
464 ec = gpg_error_from_syserror ();
465 else
467 unsigned char tmp[4];
469 tmp[0] = value >> 24;
470 tmp[1] = value >> 16;
471 tmp[2] = value >> 8;
472 tmp[3] = value;
474 switch (flag_size)
476 case 1:
477 case 2:
478 case 4:
479 if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
480 ec = gpg_err_code_from_syserror ();
481 break;
482 default:
483 ec = GPG_ERR_BUG;
484 break;
488 if (fclose (fp))
490 if (!ec)
491 ec = gpg_err_code_from_syserror ();
494 return gpg_error (ec);
500 keybox_delete (KEYBOX_HANDLE hd)
502 off_t off;
503 const char *fname;
504 FILE *fp;
505 int rc;
507 if (!hd)
508 return gpg_error (GPG_ERR_INV_VALUE);
509 if (!hd->found.blob)
510 return gpg_error (GPG_ERR_NOTHING_FOUND);
511 if (!hd->kb)
512 return gpg_error (GPG_ERR_INV_HANDLE);
513 fname = hd->kb->fname;
514 if (!fname)
515 return gpg_error (GPG_ERR_INV_HANDLE);
517 off = _keybox_get_blob_fileoffset (hd->found.blob);
518 if (off == (off_t)-1)
519 return gpg_error (GPG_ERR_GENERAL);
520 off += 4;
522 _keybox_close_file (hd);
523 fp = fopen (hd->kb->fname, "r+b");
524 if (!fp)
525 return gpg_error_from_syserror ();
527 if (fseeko (fp, off, SEEK_SET))
528 rc = gpg_error_from_syserror ();
529 else if (putc (0, fp) == EOF)
530 rc = gpg_error_from_syserror ();
531 else
532 rc = 0;
534 if (fclose (fp))
536 if (!rc)
537 rc = gpg_error_from_syserror ();
540 return rc;
544 /* Compress the keybox file. This should be run with the file
545 locked. */
547 keybox_compress (KEYBOX_HANDLE hd)
549 int read_rc, rc;
550 const char *fname;
551 FILE *fp, *newfp;
552 char *bakfname = NULL;
553 char *tmpfname = NULL;
554 int first_blob;
555 KEYBOXBLOB blob = NULL;
556 u32 cut_time;
557 int any_changes = 0;
558 int skipped_deleted;
560 if (!hd)
561 return gpg_error (GPG_ERR_INV_HANDLE);
562 if (!hd->kb)
563 return gpg_error (GPG_ERR_INV_HANDLE);
564 if (hd->secret)
565 return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
566 fname = hd->kb->fname;
567 if (!fname)
568 return gpg_error (GPG_ERR_INV_HANDLE);
570 _keybox_close_file (hd);
572 /* Open the source file. Because we do a rename, we have to check the
573 permissions of the file */
574 if (access (fname, W_OK))
575 return gpg_error_from_syserror ();
577 fp = fopen (fname, "rb");
578 if (!fp && errno == ENOENT)
579 return 0; /* Ready. File has been deleted right after the access above. */
580 if (!fp)
582 rc = gpg_error_from_syserror ();
583 return rc;
586 /* A quick test to see if we need to compress the file at all. We
587 schedule a compress run after 3 hours. */
588 if ( !_keybox_read_blob (&blob, fp) )
590 const unsigned char *buffer;
591 size_t length;
593 buffer = _keybox_get_blob_image (blob, &length);
594 if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
596 u32 last_maint = ((buffer[20] << 24) | (buffer[20+1] << 16)
597 | (buffer[20+2] << 8) | (buffer[20+3]));
599 if ( (last_maint + 3*3600) > time (NULL) )
601 fclose (fp);
602 _keybox_release_blob (blob);
603 return 0; /* Compress run not yet needed. */
606 _keybox_release_blob (blob);
607 rewind (fp);
610 /* Create the new file. */
611 rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
612 if (rc)
614 fclose(fp);
615 return rc;;
619 /* Processing loop. By reading using _keybox_read_blob we
620 automagically skip any blobs flagged as deleted. Thus what we
621 only have to do is to check all ephemeral flagged blocks whether
622 their time has come and write out all other blobs. */
623 cut_time = time(NULL) - 86400;
624 first_blob = 1;
625 skipped_deleted = 0;
626 for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
627 _keybox_release_blob (blob), blob = NULL )
629 unsigned int blobflags;
630 const unsigned char *buffer;
631 size_t length, pos, size;
632 u32 created_at;
634 if (skipped_deleted)
635 any_changes = 1;
636 buffer = _keybox_get_blob_image (blob, &length);
637 if (first_blob)
639 first_blob = 0;
640 if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
642 /* Write out the blob with an updated maintenance time stamp. */
643 _keybox_update_header_blob (blob);
644 rc = _keybox_write_blob (blob, newfp);
645 if (rc)
646 break;
647 continue;
650 /* The header blob is missing. Insert it. */
651 rc = _keybox_write_header_blob (newfp);
652 if (rc)
653 break;
654 any_changes = 1;
656 else if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
658 /* Oops: There is another header record - remove it. */
659 any_changes = 1;
660 continue;
663 if (_keybox_get_flag_location (buffer, length,
664 KEYBOX_FLAG_BLOB, &pos, &size)
665 || size != 2)
667 rc = gpg_error (GPG_ERR_BUG);
668 break;
670 blobflags = ((buffer[pos] << 8) | (buffer[pos+1]));
671 if ((blobflags & KEYBOX_FLAG_BLOB_EPHEMERAL))
673 /* This is an ephemeral blob. */
674 if (_keybox_get_flag_location (buffer, length,
675 KEYBOX_FLAG_CREATED_AT, &pos, &size)
676 || size != 4)
677 created_at = 0; /* oops. */
678 else
679 created_at = ((buffer[pos] << 24) | (buffer[pos+1] << 16)
680 | (buffer[pos+2] << 8) | (buffer[pos+3]));
682 if (created_at && created_at < cut_time)
684 any_changes = 1;
685 continue; /* Skip this blob. */
689 rc = _keybox_write_blob (blob, newfp);
690 if (rc)
691 break;
693 if (skipped_deleted)
694 any_changes = 1;
695 _keybox_release_blob (blob); blob = NULL;
696 if (!rc && read_rc == -1)
697 rc = 0;
698 else if (!rc)
699 rc = read_rc;
701 /* Close both files. */
702 if (fclose(fp) && !rc)
703 rc = gpg_error_from_syserror ();
704 if (fclose(newfp) && !rc)
705 rc = gpg_error_from_syserror ();
707 /* Rename or remove the temporary file. */
708 if (rc || !any_changes)
709 remove (tmpfname);
710 else
711 rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);
713 xfree(bakfname);
714 xfree(tmpfname);
715 return rc;