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/>.
28 #include "keybox-defs.h"
33 #if !defined(HAVE_FSEEKO) && !defined(fseeko)
39 # define LONG_MAX ((long) ((unsigned long) -1 >> 1))
42 # define LONG_MIN (-1 - LONG_MAX)
46 * A substitute for fseeko, for hosts that don't have it.
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)
59 return fseek (stream
, (long) newpos
, whence
);
61 #endif /* !defined(HAVE_FSEEKO) && !defined(fseeko) */
66 create_tmp_file (const char *template,
67 char **r_bakfname
, char **r_tmpfname
, FILE **r_fp
)
69 char *bakfname
, *tmpfname
;
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);
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);
92 gpg_error_t tmperr
= gpg_error_from_syserror ();
96 strcpy (tmpfname
,template);
97 strcpy (tmpfname
+ strlen (template)-4, EXTSEP_S
"tmp");
100 { /* File does not end with kbx; hmmm. */
101 bakfname
= xtrymalloc ( strlen (template) + 5);
103 return gpg_error_from_syserror ();
104 strcpy (stpcpy (bakfname
, template), EXTSEP_S
"bak");
106 tmpfname
= xtrymalloc ( strlen (template) + 5);
109 gpg_error_t tmperr
= gpg_error_from_syserror ();
113 strcpy (stpcpy (tmpfname
, template), EXTSEP_S
"tmp");
115 # else /* Posix file names */
116 bakfname
= xtrymalloc (strlen (template) + 2);
118 return gpg_error_from_syserror ();
119 strcpy (stpcpy (bakfname
,template),"~");
121 tmpfname
= xtrymalloc ( strlen (template) + 5);
124 gpg_error_t tmperr
= gpg_error_from_syserror ();
128 strcpy (stpcpy (tmpfname
,template), EXTSEP_S
"tmp");
129 # endif /* Posix filename */
131 *r_fp
= fopen (tmpfname
, "wb");
134 gpg_error_t tmperr
= gpg_error_from_syserror ();
140 *r_bakfname
= bakfname
;
141 *r_tmpfname
= tmpfname
;
147 rename_tmp_file (const char *bakfname
, const char *tmpfname
,
148 const char *fname
, int secret
)
152 /* restrict the permissions for secret keyboxs */
153 #ifndef HAVE_DOSISH_SYSTEM
154 /* if (secret && !opt.preserve_permissions) */
156 /* if (chmod (tmpfname, S_IRUSR | S_IWUSR) ) */
158 /* log_debug ("chmod of `%s' failed: %s\n", */
159 /* tmpfname, strerror(errno) ); */
160 /* return KEYBOX_Write_File; */
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. */
173 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
176 if (rename (fname
, bakfname
) )
178 return gpg_error_from_syserror ();
182 /* Then rename the file. */
183 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
186 if (rename (tmpfname
, fname
) )
188 rc
= gpg_error_from_syserror ();
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"); */
205 /* Perform insert/delete/update operation.
211 blob_filecopy (int mode
, const char *fname
, KEYBOXBLOB blob
,
212 int secret
, off_t start_offset
)
216 char *bakfname
= NULL
;
217 char *tmpfname
= NULL
;
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");
233 return gpg_error_from_syserror ();
235 rc
= _keybox_write_header_blob (newfp
);
239 rc
= _keybox_write_blob (blob
, newfp
);
243 if ( fclose (newfp
) )
244 return gpg_error_from_syserror ();
246 /* if (chmod( fname, S_IRUSR | S_IWUSR )) */
248 /* log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
249 /* return KEYBOX_File_Error; */
251 return 0; /* Ready. */
256 rc
= gpg_error_from_syserror ();
260 /* Create the new file. */
261 rc
= create_tmp_file (fname
, &bakfname
, &tmpfname
, &newfp
);
268 /* prepare for insert */
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 ();
282 rc
= gpg_error_from_syserror ();
287 /* Prepare for delete or update. */
288 if ( mode
== 2 || mode
== 3 )
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
);
303 if (fwrite (buffer
, nread
, 1, newfp
) != 1)
305 rc
= gpg_error_from_syserror ();
311 rc
= gpg_error_from_syserror ();
315 /* Skip this blob. */
316 rc
= _keybox_read_blob (NULL
, fp
);
321 /* Do an insert or update. */
322 if ( mode
== 1 || mode
== 3 )
324 rc
= _keybox_write_blob (blob
, newfp
);
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 ();
342 rc
= gpg_error_from_syserror ();
347 /* Close both files. */
350 rc
= gpg_error_from_syserror ();
356 rc
= gpg_error_from_syserror ();
360 rc
= rename_tmp_file (bakfname
, tmpfname
, fname
, secret
);
370 #ifdef KEYBOX_WITH_X509
372 keybox_insert_cert (KEYBOX_HANDLE hd
, ksba_cert_t cert
,
373 unsigned char *sha1_digest
)
380 return gpg_error (GPG_ERR_INV_HANDLE
);
382 return gpg_error (GPG_ERR_INV_HANDLE
);
383 fname
= hd
->kb
->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
);
395 rc
= blob_filecopy (1, fname
, blob
, hd
->secret
, 0);
396 _keybox_release_blob (blob
);
397 /* if (!rc && !hd->secret && kb_offtbl) */
399 /* update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
406 keybox_update_cert (KEYBOX_HANDLE hd
, ksba_cert_t cert
,
407 unsigned char *sha1_digest
)
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
)
428 size_t flag_pos
, flag_size
;
429 const unsigned char *buffer
;
432 (void)idx
; /* Not yet used. */
435 return gpg_error (GPG_ERR_INV_VALUE
);
437 return gpg_error (GPG_ERR_NOTHING_FOUND
);
439 return gpg_error (GPG_ERR_INV_HANDLE
);
441 return gpg_error (GPG_ERR_NOTHING_FOUND
);
442 fname
= hd
->kb
->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
);
453 return gpg_error (ec
);
457 _keybox_close_file (hd
);
458 fp
= fopen (hd
->kb
->fname
, "r+b");
460 return gpg_error_from_syserror ();
463 if (fseeko (fp
, off
, SEEK_SET
))
464 ec
= gpg_error_from_syserror ();
467 unsigned char tmp
[4];
469 tmp
[0] = value
>> 24;
470 tmp
[1] = value
>> 16;
479 if (fwrite (tmp
+4-flag_size
, flag_size
, 1, fp
) != 1)
480 ec
= gpg_err_code_from_syserror ();
491 ec
= gpg_err_code_from_syserror ();
494 return gpg_error (ec
);
500 keybox_delete (KEYBOX_HANDLE hd
)
508 return gpg_error (GPG_ERR_INV_VALUE
);
510 return gpg_error (GPG_ERR_NOTHING_FOUND
);
512 return gpg_error (GPG_ERR_INV_HANDLE
);
513 fname
= hd
->kb
->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
);
522 _keybox_close_file (hd
);
523 fp
= fopen (hd
->kb
->fname
, "r+b");
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 ();
537 rc
= gpg_error_from_syserror ();
544 /* Compress the keybox file. This should be run with the file
547 keybox_compress (KEYBOX_HANDLE hd
)
552 char *bakfname
= NULL
;
553 char *tmpfname
= NULL
;
555 KEYBOXBLOB blob
= NULL
;
561 return gpg_error (GPG_ERR_INV_HANDLE
);
563 return gpg_error (GPG_ERR_INV_HANDLE
);
565 return gpg_error (GPG_ERR_NOT_IMPLEMENTED
);
566 fname
= hd
->kb
->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. */
582 rc
= gpg_error_from_syserror ();
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
;
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
) )
602 _keybox_release_blob (blob
);
603 return 0; /* Compress run not yet needed. */
606 _keybox_release_blob (blob
);
610 /* Create the new file. */
611 rc
= create_tmp_file (fname
, &bakfname
, &tmpfname
, &newfp
);
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;
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
;
636 buffer
= _keybox_get_blob_image (blob
, &length
);
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
);
650 /* The header blob is missing. Insert it. */
651 rc
= _keybox_write_header_blob (newfp
);
656 else if (length
> 4 && buffer
[4] == BLOBTYPE_HEADER
)
658 /* Oops: There is another header record - remove it. */
663 if (_keybox_get_flag_location (buffer
, length
,
664 KEYBOX_FLAG_BLOB
, &pos
, &size
)
667 rc
= gpg_error (GPG_ERR_BUG
);
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
)
677 created_at
= 0; /* oops. */
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
)
685 continue; /* Skip this blob. */
689 rc
= _keybox_write_blob (blob
, newfp
);
695 _keybox_release_blob (blob
); blob
= NULL
;
696 if (!rc
&& read_rc
== -1)
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
)
711 rc
= rename_tmp_file (bakfname
, tmpfname
, fname
, hd
->secret
);