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