The keybox gets now compressed after 3 hours and ephemeral
[gnupg.git] / kbx / keybox-update.c
1 /* keybox-update.c - keybox update operations
2  *      Copyright (C) 2001, 2003, 2004 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 2 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, write to the Free Software
18  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
19  */
20
21 #include <config.h>
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <errno.h>
26 #include <time.h>
27 #include <unistd.h>
28
29 #include "keybox-defs.h"
30
31 #define EXTSEP_S "."
32
33
34 static int
35 create_tmp_file (const char *template,
36                  char **r_bakfname, char **r_tmpfname, FILE **r_fp)
37 {  
38   char *bakfname, *tmpfname;
39   
40   *r_bakfname = NULL;
41   *r_tmpfname = NULL;
42   
43 # ifdef USE_ONLY_8DOT3
44   /* Here is another Windoze bug?:
45    * you cant rename("pubring.kbx.tmp", "pubring.kbx");
46    * but        rename("pubring.kbx.tmp", "pubring.aaa");
47    * works.  So we replace .kbx by .bak or .tmp
48    */
49   if (strlen (template) > 4
50       && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") )
51     {
52       bakfname = xtrymalloc (strlen (template) + 1);
53       if (!bakfname)
54         return gpg_error (gpg_err_code_from_errno (errno));
55       strcpy (bakfname, template);
56       strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak");
57       
58       tmpfname = xtrymalloc (strlen (template) + 1);
59       if (!tmpfname)
60         {
61           gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
62           xfree (bakfname);
63           return tmperr;
64         }
65       strcpy (tmpfname,template);
66       strcpy (tmpfname + strlen (template)-4, EXTSEP_S "tmp");
67     }
68   else 
69     { /* file does not end with kbx; hmmm */
70       bakfname = xtrymalloc ( strlen (template) + 5);
71       if (!bakfname)
72         return gpg_error (gpg_err_code_from_errno (errno));
73       strcpy (stpcpy (bakfname, template), EXTSEP_S "bak");
74       
75       tmpfname = xtrymalloc ( strlen (template) + 5);
76       if (!tmpfname)
77         {
78           gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
79           xfree (bakfname);
80           return tmperr;
81         }
82       strcpy (stpcpy (tmpfname, template), EXTSEP_S "tmp");
83     }
84 # else /* Posix file names */
85   bakfname = xtrymalloc (strlen (template) + 2);
86   if (!bakfname)
87     return gpg_error (gpg_err_code_from_errno (errno));
88   strcpy (stpcpy (bakfname,template),"~");
89   
90   tmpfname = xtrymalloc ( strlen (template) + 5);
91   if (!tmpfname)
92     {
93       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
94       xfree (bakfname);
95       return tmperr;
96     }
97   strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp");
98 # endif /* Posix filename */
99
100   *r_fp = fopen (tmpfname, "wb");
101   if (!*r_fp)
102     {
103       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
104       xfree (tmpfname);
105       xfree (bakfname);
106       return tmperr;
107     }
108   
109   *r_bakfname = bakfname;
110   *r_tmpfname = tmpfname;
111   return 0;
112 }
113
114
115 static int
116 rename_tmp_file (const char *bakfname, const char *tmpfname,
117                  const char *fname, int secret )
118 {
119   int rc=0;
120
121   /* restrict the permissions for secret keyboxs */
122 #ifndef HAVE_DOSISH_SYSTEM
123 /*    if (secret && !opt.preserve_permissions) */
124 /*      { */
125 /*        if (chmod (tmpfname, S_IRUSR | S_IWUSR) )  */
126 /*          { */
127 /*            log_debug ("chmod of `%s' failed: %s\n", */
128 /*                       tmpfname, strerror(errno) ); */
129 /*            return KEYBOX_Write_File; */
130 /*      } */
131 /*      } */
132 #endif
133
134   /* fixme: invalidate close caches (not used with stdio)*/
135 /*    iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ); */
136 /*    iobuf_ioctl (NULL, 2, 0, (char*)bakfname ); */
137 /*    iobuf_ioctl (NULL, 2, 0, (char*)fname ); */
138
139   /* first make a backup file except for secret keyboxs */
140   if (!secret)
141     { 
142 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
143       remove (bakfname);
144 #endif
145       if (rename (fname, bakfname) )
146         {
147           return gpg_error (gpg_err_code_from_errno (errno));
148         }
149     }
150   
151   /* then rename the file */
152 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
153   remove (fname);
154 #endif
155   if (rename (tmpfname, fname) )
156     {
157       rc = gpg_error (gpg_err_code_from_errno (errno));
158       if (secret)
159         {
160 /*            log_info ("WARNING: 2 files with confidential" */
161 /*                       " information exists.\n"); */
162 /*            log_info ("%s is the unchanged one\n", fname ); */
163 /*            log_info ("%s is the new one\n", tmpfname ); */
164 /*            log_info ("Please fix this possible security flaw\n"); */
165         }
166       return rc;
167     }
168   
169   return 0;
170 }
171
172
173
174 /* Perform insert/delete/update operation.
175     mode 1 = insert
176          2 = delete
177          3 = update
178 */
179 static int
180 blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, 
181                int secret, off_t start_offset, unsigned int n_packets )
182 {
183   FILE *fp, *newfp;
184   int rc=0;
185   char *bakfname = NULL;
186   char *tmpfname = NULL;
187   char buffer[4096];
188   int nread, nbytes;
189
190   /* Open the source file. Because we do a rename, we have to check the 
191      permissions of the file */
192   if (access (fname, W_OK))
193     return gpg_error (gpg_err_code_from_errno (errno));
194
195   fp = fopen (fname, "rb");
196   if (mode == 1 && !fp && errno == ENOENT)
197     { 
198       /* Insert mode but file does not exist:
199          Create a new keybox file. */
200       newfp = fopen (fname, "wb");
201       if (!newfp )
202         return gpg_error (gpg_err_code_from_errno (errno));
203
204       rc = _keybox_write_header_blob (newfp);
205       if (rc)
206         return rc;
207
208       rc = _keybox_write_blob (blob, newfp);
209       if (rc)
210         return rc;
211
212       if ( fclose (newfp) )
213         return gpg_error (gpg_err_code_from_errno (errno));
214
215 /*        if (chmod( fname, S_IRUSR | S_IWUSR )) */
216 /*          { */
217 /*            log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
218 /*            return KEYBOX_File_Error; */
219 /*          } */
220       return 0; /* Ready. */
221     }
222
223   if (!fp)
224     {
225       rc = gpg_error (gpg_err_code_from_errno (errno));
226       goto leave;
227     }
228
229   /* Create the new file. */
230   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
231   if (rc)
232     {
233       fclose(fp);
234       goto leave;
235     }
236   
237   /* prepare for insert */
238   if (mode == 1)
239     { 
240       /* Copy everything to the new file. */
241       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
242         {
243           if (fwrite (buffer, nread, 1, newfp) != 1)
244             {
245               rc = gpg_error (gpg_err_code_from_errno (errno));
246               goto leave;
247             }
248         }
249       if (ferror (fp))
250         {
251           rc = gpg_error (gpg_err_code_from_errno (errno));
252           goto leave;
253         }
254     }
255   
256   /* Prepare for delete or update. */
257   if ( mode == 2 || mode == 3 ) 
258     { 
259       off_t current = 0;
260       
261       /* Copy first part to the new file. */
262       while ( current < start_offset )
263         {
264           nbytes = DIM(buffer);
265           if (current + nbytes > start_offset)
266               nbytes = start_offset - current;
267           nread = fread (buffer, 1, nbytes, fp);
268           if (!nread)
269             break;
270           current += nread;
271           
272           if (fwrite (buffer, nread, 1, newfp) != 1)
273             {
274               rc = gpg_error (gpg_err_code_from_errno (errno));
275               goto leave;
276             }
277         }
278       if (ferror (fp))
279         {
280           rc = gpg_error (gpg_err_code_from_errno (errno));
281           goto leave;
282         }
283       
284       /* Skip this blob. */
285       rc = _keybox_read_blob (NULL, fp);
286       if (rc)
287         return rc;
288     }
289   
290   /* Do an insert or update. */
291   if ( mode == 1 || mode == 3 )
292     { 
293       rc = _keybox_write_blob (blob, newfp);
294       if (rc)
295           return rc;
296     }
297   
298   /* Copy the rest of the packet for an delete or update. */
299   if (mode == 2 || mode == 3)
300     { 
301       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
302         {
303           if (fwrite (buffer, nread, 1, newfp) != 1)
304             {
305               rc = gpg_error (gpg_err_code_from_errno (errno));
306               goto leave;
307             }
308         }
309       if (ferror (fp))
310         {
311           rc = gpg_error (gpg_err_code_from_errno (errno));
312           goto leave;
313         }
314     }
315     
316   /* Close both files. */
317   if (fclose(fp))
318     {
319       rc = gpg_error (gpg_err_code_from_errno (errno));
320       fclose (newfp);
321       goto leave;
322     }
323   if (fclose(newfp))
324     {
325       rc = gpg_error (gpg_err_code_from_errno (errno));
326       goto leave;
327     }
328
329   rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
330
331  leave:
332   xfree(bakfname);
333   xfree(tmpfname);
334   return rc;
335 }
336
337
338
339 #ifdef KEYBOX_WITH_X509 
340 int
341 keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
342                     unsigned char *sha1_digest)
343 {
344   int rc;
345   const char *fname;
346   KEYBOXBLOB blob;
347
348   if (!hd)
349     return gpg_error (GPG_ERR_INV_HANDLE); 
350   if (!hd->kb)
351     return gpg_error (GPG_ERR_INV_HANDLE); 
352   fname = hd->kb->fname;
353   if (!fname)
354     return gpg_error (GPG_ERR_INV_HANDLE); 
355
356   /* Close this one otherwise we will mess up the position for a next
357      search.  Fixme: it would be better to adjust the position after
358      the write opertions.  */
359   if (hd->fp)
360     {
361       fclose (hd->fp);
362       hd->fp = NULL;
363     }
364
365   rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
366   if (!rc)
367     {
368       rc = blob_filecopy (1, fname, blob, hd->secret, 0, 0 );
369       _keybox_release_blob (blob);
370       /*    if (!rc && !hd->secret && kb_offtbl) */
371       /*      { */
372       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
373       /*      } */
374     }
375   return rc;
376 }
377
378 int
379 keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
380                     unsigned char *sha1_digest)
381 {
382   return -1;
383 }
384
385
386 #endif /*KEYBOX_WITH_X509*/
387
388 /* Note: We assume that the keybox has been locked before the current
389    search was executed.  This is needed so that we can depend on the
390    offset information of the flags. */
391 int
392 keybox_set_flags (KEYBOX_HANDLE hd, int what, int idx, unsigned int value)
393 {
394   off_t off;
395   const char *fname;
396   FILE *fp;
397   gpg_err_code_t ec;
398   size_t flag_pos, flag_size;
399   const unsigned char *buffer;
400   size_t length;
401
402   if (!hd)
403     return gpg_error (GPG_ERR_INV_VALUE);
404   if (!hd->found.blob)
405     return gpg_error (GPG_ERR_NOTHING_FOUND);
406   if (!hd->kb)
407     return gpg_error (GPG_ERR_INV_HANDLE); 
408   if (!hd->found.blob)
409     return gpg_error (GPG_ERR_NOTHING_FOUND);
410   fname = hd->kb->fname;
411   if (!fname)
412     return gpg_error (GPG_ERR_INV_HANDLE); 
413
414   off = _keybox_get_blob_fileoffset (hd->found.blob);
415   if (off == (off_t)-1)
416     return gpg_error (GPG_ERR_GENERAL);
417
418   buffer = _keybox_get_blob_image (hd->found.blob, &length);
419   ec = _keybox_get_flag_location (buffer, length, what, &flag_pos, &flag_size);
420   if (ec)
421     return gpg_error (ec);
422
423   off += flag_pos;
424
425   if (hd->fp)
426     {
427       fclose (hd->fp);
428       hd->fp = NULL;
429     }
430   fp = fopen (hd->kb->fname, "r+b");
431   if (!fp)
432     return gpg_error (gpg_err_code_from_errno (errno));
433
434   ec = 0;
435   if (fseeko (fp, off, SEEK_SET))
436     ec = gpg_error (gpg_err_code_from_errno (errno));
437   else
438     {
439       unsigned char tmp[4];
440
441       tmp[0] = value >> 24;
442       tmp[1] = value >> 16;
443       tmp[2] = value >>  8;
444       tmp[3] = value;
445
446       switch (flag_size)
447         {
448         case 1: 
449         case 2:
450         case 4:
451           if (fwrite (tmp+4-flag_size, flag_size, 1, fp) != 1)
452             ec = gpg_err_code_from_errno (errno);
453           break;
454         default:
455           ec = GPG_ERR_BUG;
456           break;
457         }
458     }
459
460   if (fclose (fp))
461     {
462       if (!ec)
463         ec = gpg_err_code_from_errno (errno);
464     }
465
466   return gpg_error (ec);
467 }
468
469
470
471 int
472 keybox_delete (KEYBOX_HANDLE hd)
473 {
474   off_t off;
475   const char *fname;
476   FILE *fp;
477   int rc;
478
479   if (!hd)
480     return gpg_error (GPG_ERR_INV_VALUE);
481   if (!hd->found.blob)
482     return gpg_error (GPG_ERR_NOTHING_FOUND);
483   if (!hd->kb)
484     return gpg_error (GPG_ERR_INV_HANDLE); 
485   fname = hd->kb->fname;
486   if (!fname)
487     return gpg_error (GPG_ERR_INV_HANDLE); 
488
489   off = _keybox_get_blob_fileoffset (hd->found.blob);
490   if (off == (off_t)-1)
491     return gpg_error (GPG_ERR_GENERAL);
492   off += 4;
493
494   if (hd->fp)
495     {
496       fclose (hd->fp);
497       hd->fp = NULL;
498     }
499   
500   fp = fopen (hd->kb->fname, "r+b");
501   if (!fp)
502     return gpg_error (gpg_err_code_from_errno (errno));
503
504   if (fseeko (fp, off, SEEK_SET))
505     rc = gpg_error (gpg_err_code_from_errno (errno));
506   else if (putc (0, fp) == EOF)
507     rc = gpg_error (gpg_err_code_from_errno (errno));
508   else
509     rc = 0;
510
511   if (fclose (fp))
512     {
513       if (!rc)
514         rc = gpg_error (gpg_err_code_from_errno (errno));
515     }
516
517   return rc;
518 }
519
520
521 /* Compress the keybox file.  This should be run with the file
522    locked. */
523 int
524 keybox_compress (KEYBOX_HANDLE hd)
525 {
526   int read_rc, rc;
527   const char *fname;
528   FILE *fp, *newfp;
529   char *bakfname = NULL;
530   char *tmpfname = NULL;
531   int first_blob;
532   KEYBOXBLOB blob = NULL;
533   u32 cut_time;
534   int any_changes = 0;
535   int skipped_deleted;
536
537   if (!hd)
538     return gpg_error (GPG_ERR_INV_HANDLE); 
539   if (!hd->kb)
540     return gpg_error (GPG_ERR_INV_HANDLE); 
541   if (hd->secret)
542     return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
543   fname = hd->kb->fname;
544   if (!fname)
545     return gpg_error (GPG_ERR_INV_HANDLE); 
546
547   if (hd->fp)
548     {
549       fclose (hd->fp);
550       hd->fp = NULL;
551     }
552
553   /* Open the source file. Because we do a rename, we have to check the 
554      permissions of the file */
555   if (access (fname, W_OK))
556     return gpg_error (gpg_err_code_from_errno (errno));
557
558   fp = fopen (fname, "rb");
559   if (!fp && errno == ENOENT)
560     return 0; /* Ready. File has been deleted right after the access above. */
561   if (!fp)
562     {
563       rc = gpg_error (gpg_err_code_from_errno (errno));
564       return rc;
565     }
566
567   /* A quick test to see if we need to compress the file at all.  We
568      schedule a compress run after 3 hours. */
569   if ( !_keybox_read_blob (&blob, fp) )
570     {
571       const unsigned char *buffer;
572       size_t length;
573
574       buffer = _keybox_get_blob_image (blob, &length);
575       if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
576         {
577           u32 last_maint = ((buffer[20] << 24) | (buffer[20+1] << 16)
578                             | (buffer[20+2] << 8) | (buffer[20+3]));
579           
580           if ( (last_maint + 3*3600) > time (NULL) )
581             {
582               fclose (fp);
583               _keybox_release_blob (blob);
584               return 0; /* Compress run not yet needed. */
585             }
586         }
587       _keybox_release_blob (blob);
588       rewind (fp);
589     }
590
591   /* Create the new file. */
592   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
593   if (rc)
594     {
595       fclose(fp);
596       return rc;;
597     }
598
599   
600   /* Processing loop.  By reading using _keybox_read_blob we
601      automagically skip and blobs flagged as deleted.  Thus what we
602      only have to do is to check all ephemeral flagged blocks whether
603      their time has come and write out all other blobs. */
604   cut_time = time(NULL) - 86400;
605   first_blob = 1;
606   skipped_deleted = 0;
607   for (rc=0; !(read_rc = _keybox_read_blob2 (&blob, fp, &skipped_deleted));
608        _keybox_release_blob (blob), blob = NULL )
609     {
610       unsigned int blobflags;
611       const unsigned char *buffer;
612       size_t length, pos, size;
613       u32 created_at;
614
615       if (skipped_deleted)
616         any_changes = 1;
617       buffer = _keybox_get_blob_image (blob, &length);
618       if (first_blob)
619         {
620           first_blob = 0;
621           if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
622             {
623               /* Write out the blob with an updated maintenance time stamp. */
624               _keybox_update_header_blob (blob);
625               rc = _keybox_write_blob (blob, newfp);
626               if (rc)
627                 break;
628               continue;
629             }
630
631           /* The header blob is missing.  Insert it.  */
632           rc = _keybox_write_header_blob (newfp);
633           if (rc)
634             break;
635           any_changes = 1;
636         }
637       else if (length > 4 && buffer[4] == BLOBTYPE_HEADER)
638         {
639           /* Oops: There is another header record - remove it. */
640           any_changes = 1;
641           continue;
642         }
643
644       if (_keybox_get_flag_location (buffer, length, 
645                                      KEYBOX_FLAG_BLOB, &pos, &size)
646           || size != 2)
647         {
648           rc = gpg_error (GPG_ERR_BUG);
649           break;
650         }
651       blobflags = ((buffer[pos] << 8) | (buffer[pos+1]));
652       if ((blobflags & 2))
653         {
654           /* This is an ephemeral blob. */
655           if (_keybox_get_flag_location (buffer, length, 
656                                          KEYBOX_FLAG_CREATED_AT, &pos, &size)
657               || size != 4)
658             created_at = 0; /* oops. */
659           else
660             created_at = ((buffer[pos] << 24) | (buffer[pos+1] << 16)
661                           | (buffer[pos+2] << 8) | (buffer[pos+3]));
662
663           if (created_at && created_at < cut_time)
664             {
665               any_changes = 1;
666               continue; /* Skip this blob. */
667             }
668         }
669
670       rc = _keybox_write_blob (blob, newfp);
671       if (rc)
672         break;
673     }
674   if (skipped_deleted)
675     any_changes = 1;
676   _keybox_release_blob (blob); blob = NULL;
677   if (!rc && read_rc == -1)
678     rc = 0;
679   else if (!rc)
680     rc = read_rc;
681
682   /* Close both files. */
683   if (fclose(fp) && !rc)
684     rc = gpg_error (gpg_err_code_from_errno (errno));
685   if (fclose(newfp) && !rc)
686     rc = gpg_error (gpg_err_code_from_errno (errno));
687
688   /* Rename or remove the temporary file. */
689   if (rc || !any_changes)
690     remove (tmpfname);
691   else
692     rc = rename_tmp_file (bakfname, tmpfname, fname, hd->secret);
693
694   xfree(bakfname);
695   xfree(tmpfname);
696   return rc;
697 }
698