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