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