Some minor bug fixes, new test utilities and started support for other
[gnupg.git] / kbx / keybox-update.c
1 /* keybox-update.c - keybox update operations
2  *      Copyright (C) 2001, 2003 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 <unistd.h>
27
28 #include "keybox-defs.h"
29
30 #define EXTSEP_S "."
31
32
33 static int
34 create_tmp_file (const char *template,
35                  char **r_bakfname, char **r_tmpfname, FILE **r_fp)
36 {  
37   char *bakfname, *tmpfname;
38   
39   *r_bakfname = NULL;
40   *r_tmpfname = NULL;
41   
42 # ifdef USE_ONLY_8DOT3
43   /* Here is another Windoze bug?:
44    * you cant rename("pubring.kbx.tmp", "pubring.kbx");
45    * but        rename("pubring.kbx.tmp", "pubring.aaa");
46    * works.  So we replace .kbx by .bak or .tmp
47    */
48   if (strlen (template) > 4
49       && !strcmp (template+strlen(template)-4, EXTSEP_S "kbx") )
50     {
51       bakfname = xtrymalloc (strlen (template) + 1);
52       if (!bakfname)
53         return gpg_error (gpg_err_code_from_errno (errno));
54       strcpy (bakfname, template);
55       strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak");
56       
57       tmpfname = xtrymalloc (strlen (template) + 1);
58       if (!tmpfname)
59         {
60           gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
61           xfree (bakfname);
62           return tmperr;
63         }
64       strcpy (tmpfname,template);
65       strcpy (tmpfname + strlen (template)-4, EXTSEP_S "tmp");
66     }
67   else 
68     { /* file does not end with kbx; hmmm */
69       bakfname = xtrymalloc ( strlen (template) + 5);
70       if (!bakfname)
71         return gpg_error (gpg_err_code_from_errno (errno));
72       strcpy (stpcpy (bakfname, template), EXTSEP_S "bak");
73       
74       tmpfname = xtrymalloc ( strlen (template) + 5);
75       if (!tmpfname)
76         {
77           gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
78           xfree (bakfname);
79           return tmperr;
80         }
81       strcpy (stpcpy (tmpfname, template), EXTSEP_S "tmp");
82     }
83 # else /* Posix file names */
84   bakfname = xtrymalloc (strlen (template) + 2);
85   if (!bakfname)
86     return gpg_error (gpg_err_code_from_errno (errno));
87   strcpy (stpcpy (bakfname,template),"~");
88   
89   tmpfname = xtrymalloc ( strlen (template) + 5);
90   if (!tmpfname)
91     {
92       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
93       xfree (bakfname);
94       return tmperr;
95     }
96   strcpy (stpcpy (tmpfname,template), EXTSEP_S "tmp");
97 # endif /* Posix filename */
98
99   *r_fp = fopen (tmpfname, "wb");
100   if (!*r_fp)
101     {
102       gpg_error_t tmperr = gpg_error (gpg_err_code_from_errno (errno));
103       xfree (tmpfname);
104       xfree (bakfname);
105       return tmperr;
106     }
107   
108   *r_bakfname = bakfname;
109   *r_tmpfname = tmpfname;
110   return 0;
111 }
112
113
114 static int
115 rename_tmp_file (const char *bakfname, const char *tmpfname,
116                  const char *fname, int secret )
117 {
118   int rc=0;
119
120   /* restrict the permissions for secret keyboxs */
121 #ifndef HAVE_DOSISH_SYSTEM
122 /*    if (secret && !opt.preserve_permissions) */
123 /*      { */
124 /*        if (chmod (tmpfname, S_IRUSR | S_IWUSR) )  */
125 /*          { */
126 /*            log_debug ("chmod of `%s' failed: %s\n", */
127 /*                       tmpfname, strerror(errno) ); */
128 /*            return KEYBOX_Write_File; */
129 /*      } */
130 /*      } */
131 #endif
132
133   /* fixme: invalidate close caches (not used with stdio)*/
134 /*    iobuf_ioctl (NULL, 2, 0, (char*)tmpfname ); */
135 /*    iobuf_ioctl (NULL, 2, 0, (char*)bakfname ); */
136 /*    iobuf_ioctl (NULL, 2, 0, (char*)fname ); */
137
138   /* first make a backup file except for secret keyboxs */
139   if (!secret)
140     { 
141 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
142       remove (bakfname);
143 #endif
144       if (rename (fname, bakfname) )
145         {
146           return gpg_error (gpg_err_code_from_errno (errno));
147         }
148     }
149   
150   /* then rename the file */
151 #if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
152   remove (fname);
153 #endif
154   if (rename (tmpfname, fname) )
155     {
156       rc = gpg_error (gpg_err_code_from_errno (errno));
157       if (secret)
158         {
159 /*            log_info ("WARNING: 2 files with confidential" */
160 /*                       " information exists.\n"); */
161 /*            log_info ("%s is the unchanged one\n", fname ); */
162 /*            log_info ("%s is the new one\n", tmpfname ); */
163 /*            log_info ("Please fix this possible security flaw\n"); */
164         }
165       return rc;
166     }
167   
168   return 0;
169 }
170
171
172
173 /* Perform insert/delete/update operation.
174     mode 1 = insert
175          2 = delete
176          3 = update
177 */
178 static int
179 blob_filecopy (int mode, const char *fname, KEYBOXBLOB blob, 
180                int secret, off_t start_offset, unsigned int n_packets )
181 {
182   FILE *fp, *newfp;
183   int rc=0;
184   char *bakfname = NULL;
185   char *tmpfname = NULL;
186   char buffer[4096];
187   int nread, nbytes;
188
189   /* Open the source file. Because we do a rename, we have to check the 
190      permissions of the file */
191   if (access (fname, W_OK))
192     return gpg_error (gpg_err_code_from_errno (errno));
193
194   fp = fopen (fname, "rb");
195   if (mode == 1 && !fp && errno == ENOENT)
196     { /* insert mode but file does not exist: create a new keybox file */
197       newfp = fopen (fname, "wb");
198       if (!newfp )
199         {
200           return gpg_error (gpg_err_code_from_errno (errno));
201         }
202
203       rc = _keybox_write_blob (blob, newfp);
204       if (rc)
205         {
206           return rc;
207         }
208       if ( fclose (newfp) )
209         {
210           return gpg_error (gpg_err_code_from_errno (errno));
211         }
212
213 /*        if (chmod( fname, S_IRUSR | S_IWUSR )) */
214 /*          { */
215 /*            log_debug ("%s: chmod failed: %s\n", fname, strerror(errno) ); */
216 /*            return KEYBOX_File_Error; */
217 /*          } */
218       return 0; /* ready */
219     }
220
221   if (!fp)
222     {
223       rc = gpg_error (gpg_err_code_from_errno (errno));
224       goto leave;
225     }
226
227   /* create the new file */
228   rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
229   if (rc)
230     {
231       fclose(fp);
232       goto leave;
233     }
234   
235   /* prepare for insert */
236   if (mode == 1)
237     { 
238       /* copy everything to the new file */
239       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
240         {
241           if (fwrite (buffer, nread, 1, newfp) != 1)
242             {
243               rc = gpg_error (gpg_err_code_from_errno (errno));
244               goto leave;
245             }
246         }
247       if (ferror (fp))
248         {
249           rc = gpg_error (gpg_err_code_from_errno (errno));
250           goto leave;
251         }
252     }
253   
254   /* prepare for delete or update */
255   if ( mode == 2 || mode == 3 ) 
256     { 
257       off_t current = 0;
258       
259       /* copy first part to the new file */
260       while ( current < start_offset )
261         {
262           nbytes = DIM(buffer);
263           if (current + nbytes > start_offset)
264               nbytes = start_offset - current;
265           nread = fread (buffer, 1, nbytes, fp);
266           if (!fread)
267             break;
268           current += nread;
269           
270           if (fwrite (buffer, nread, 1, newfp) != 1)
271             {
272               rc = gpg_error (gpg_err_code_from_errno (errno));
273               goto leave;
274             }
275         }
276       if (ferror (fp))
277         {
278           rc = gpg_error (gpg_err_code_from_errno (errno));
279           goto leave;
280         }
281       
282       /* skip this blob */
283       rc = _keybox_read_blob (NULL, fp);
284       if (rc)
285         return rc;
286     }
287   
288   /* Do an insert or update */
289   if ( mode == 1 || mode == 3 )
290     { 
291       rc = _keybox_write_blob (blob, newfp);
292       if (rc)
293           return rc;
294     }
295   
296   /* copy the rest of the packet for an delete or update */
297   if (mode == 2 || mode == 3)
298     { 
299       while ( (nread = fread (buffer, 1, DIM(buffer), fp)) > 0 )
300         {
301           if (fwrite (buffer, nread, 1, newfp) != 1)
302             {
303               rc = gpg_error (gpg_err_code_from_errno (errno));
304               goto leave;
305             }
306         }
307       if (ferror (fp))
308         {
309           rc = gpg_error (gpg_err_code_from_errno (errno));
310           goto leave;
311         }
312     }
313     
314   /* close both files */
315   if (fclose(fp))
316     {
317       rc = gpg_error (gpg_err_code_from_errno (errno));
318       fclose (newfp);
319       goto leave;
320     }
321   if (fclose(newfp))
322     {
323       rc = gpg_error (gpg_err_code_from_errno (errno));
324       goto leave;
325     }
326
327   rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
328
329  leave:
330   xfree(bakfname);
331   xfree(tmpfname);
332   return rc;
333 }
334
335
336
337
338 #ifdef KEYBOX_WITH_X509 
339 int
340 keybox_insert_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
341                     unsigned char *sha1_digest)
342 {
343   int rc;
344   const char *fname;
345   KEYBOXBLOB blob;
346
347   if (!hd)
348     return gpg_error (GPG_ERR_INV_HANDLE); 
349   if (!hd->kb)
350     return gpg_error (GPG_ERR_INV_HANDLE); 
351   fname = hd->kb->fname;
352   if (!fname)
353     return gpg_error (GPG_ERR_INV_HANDLE); 
354
355   /* close this one otherwise we will mess up the position for a next
356      search.  Fixme: it would be better to adjust the position after
357      the write opertions.  */
358   if (hd->fp)
359     {
360       fclose (hd->fp);
361       hd->fp = NULL;
362     }
363
364   rc = _keybox_create_x509_blob (&blob, cert, sha1_digest, hd->ephemeral);
365   if (!rc)
366     {
367       rc = blob_filecopy (1, fname, blob, hd->secret, 0, 0 );
368       _keybox_release_blob (blob);
369       /*    if (!rc && !hd->secret && kb_offtbl) */
370       /*      { */
371       /*        update_offset_hash_table_from_kb (kb_offtbl, kb, 0); */
372       /*      } */
373     }
374   return rc;
375 }
376
377 int
378 keybox_update_cert (KEYBOX_HANDLE hd, ksba_cert_t cert,
379                     unsigned char *sha1_digest)
380 {
381   return -1;
382 }
383
384
385 #endif /*KEYBOX_WITH_X509*/
386
387
388 int
389 keybox_delete (KEYBOX_HANDLE hd)
390 {
391   off_t off;
392   const char *fname;
393   FILE *fp;
394   int rc;
395
396   if (!hd)
397     return gpg_error (GPG_ERR_INV_VALUE);
398   if (!hd->found.blob)
399     return gpg_error (GPG_ERR_NOTHING_FOUND);
400   if (!hd->kb)
401     return gpg_error (GPG_ERR_INV_HANDLE); 
402   fname = hd->kb->fname;
403   if (!fname)
404     return gpg_error (GPG_ERR_INV_HANDLE); 
405
406   off = _keybox_get_blob_fileoffset (hd->found.blob);
407   if (off == (off_t)-1)
408     return gpg_error (GPG_ERR_GENERAL);
409   off += 4;
410
411   if (hd->fp)
412     {
413       fclose (hd->fp);
414       hd->fp = NULL;
415     }
416   
417   fp = fopen (hd->kb->fname, "r+b");
418   if (!fp)
419     return gpg_error (gpg_err_code_from_errno (errno));
420
421   if (fseeko (fp, off, SEEK_SET))
422     rc = gpg_error (gpg_err_code_from_errno (errno));
423   else if (putc (0, fp) == EOF)
424     rc = gpg_error (gpg_err_code_from_errno (errno));
425   else
426     rc = 0;
427
428   if (fclose (fp))
429     {
430       if (!rc)
431         rc = gpg_error (gpg_err_code_from_errno (errno));
432     }
433
434   return rc;
435 }
436
437