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