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