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