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