g13: Re-factor high level create code.
[gnupg.git] / g13 / mount.c
1 /* mount.c - Mount a crypto container
2  * Copyright (C) 2009 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 <stdio.h>
22 #include <stdlib.h>
23 #include <string.h>
24 #include <errno.h>
25 #include <unistd.h>
26 #include <sys/stat.h>
27 #include <assert.h>
28
29 #include "g13.h"
30 #include "i18n.h"
31 #include "mount.h"
32
33 #include "keyblob.h"
34 #include "backend.h"
35 #include "utils.h"
36 #include "../common/sysutils.h"
37 #include "../common/call-gpg.h"
38 #include "mountinfo.h"
39 #include "runner.h"
40 #include "host2net.h"
41
42
43 /* Parse the header prefix and return the length of the entire header.  */
44 static gpg_error_t
45 parse_header (const char *filename,
46               const unsigned char *packet, size_t packetlen,
47               size_t *r_headerlen)
48 {
49   unsigned int len;
50
51   if (packetlen != 32)
52     return gpg_error (GPG_ERR_BUG);
53
54   len = buf32_to_uint (packet+2);
55   if (packet[0] != (0xc0|61) || len < 26
56       || memcmp (packet+6, "GnuPG/G13", 10))
57     {
58       log_error ("file '%s' is not valid container\n", filename);
59       return gpg_error (GPG_ERR_INV_OBJ);
60     }
61   if (packet[16] != 1)
62     {
63       log_error ("unknown version %u of container '%s'\n",
64                  (unsigned int)packet[16], filename);
65       return gpg_error (GPG_ERR_INV_OBJ);
66     }
67   if (packet[17] || packet[18]
68       || packet[26] || packet[27] || packet[28] || packet[29]
69       || packet[30] || packet[31])
70     log_info ("WARNING: unknown meta information in '%s'\n", filename);
71   if (packet[19])
72     log_info ("WARNING: OS flag is not supported in '%s'\n", filename);
73   if (packet[24] != 1 || packet[25] != 0)
74     {
75       log_error ("meta data copies in '%s' are not supported\n", filename);
76       return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
77     }
78
79   len = buf32_to_uint (packet+20);
80
81   /* Do a basic sanity check on the length.  */
82   if (len < 32 || len > 1024*1024)
83     {
84       log_error ("bad length given in container '%s'\n", filename);
85       return gpg_error (GPG_ERR_INV_OBJ);
86     }
87
88   *r_headerlen = len;
89   return 0;
90 }
91
92
93 /* Read the prefix of the keyblob and do some basic parsing.  On
94    success returns an open estream file at R_FP and the length of the
95    header at R_HEADERLEN.  */
96 static gpg_error_t
97 read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen)
98 {
99   gpg_error_t err;
100   estream_t fp;
101   unsigned char packet[32];
102
103   *r_fp = NULL;
104
105   fp = es_fopen (filename, "rb");
106   if (!fp)
107     {
108       err = gpg_error_from_syserror ();
109       log_error ("error reading '%s': %s\n", filename, gpg_strerror (err));
110       return err;
111     }
112
113   /* Read the header.  It is defined as 32 bytes thus we read it in one go.  */
114   if (es_fread (packet, 32, 1, fp) != 1)
115     {
116       err = gpg_error_from_syserror ();
117       log_error ("error reading the header of '%s': %s\n",
118                  filename, gpg_strerror (err));
119       es_fclose (fp);
120       return err;
121     }
122
123   err = parse_header (filename, packet, 32, r_headerlen);
124   if (err)
125     es_fclose (fp);
126   else
127     *r_fp = fp;
128
129   return err;
130 }
131
132
133 /* Read the keyblob at FILENAME.  The caller should have acquired a
134    lockfile and checked that the file exists.  */
135 static gpg_error_t
136 read_keyblob (const char *filename,
137               void **r_enckeyblob, size_t *r_enckeybloblen)
138 {
139   gpg_error_t err;
140   estream_t fp = NULL;
141   size_t headerlen = 0;
142   size_t msglen;
143   void *msg = NULL;
144
145   *r_enckeyblob = NULL;
146   *r_enckeybloblen = 0;
147
148   err = read_keyblob_prefix (filename, &fp, &headerlen);
149   if (err)
150     goto leave;
151
152   if (opt.verbose)
153     log_info ("header length of '%s' is %zu\n", filename, headerlen);
154
155   /* Read everything including the padding.  We should eventually do a
156      regular OpenPGP parsing to detect the padding packet and pass
157      only the actual used OpenPGP data to the engine.  This is in
158      particular required when supporting CMS which will be
159      encapsulated in an OpenPGP packet.  */
160   assert (headerlen >= 32);
161   msglen = headerlen - 32;
162   if (!msglen)
163     {
164       err = gpg_error (GPG_ERR_NO_DATA);
165       goto leave;
166     }
167   msg = xtrymalloc (msglen);
168   if (!msglen)
169     {
170       err = gpg_error_from_syserror ();
171       goto leave;
172     }
173   if (es_fread (msg, msglen, 1, fp) != 1)
174     {
175       err = gpg_error_from_syserror ();
176       log_error ("error reading keyblob of '%s': %s\n",
177                  filename, gpg_strerror (err));
178       goto leave;
179     }
180
181   *r_enckeyblob = msg;
182   msg = NULL;
183   *r_enckeybloblen = msglen;
184
185  leave:
186   xfree (msg);
187   es_fclose (fp);
188
189   return err;
190 }
191
192
193
194
195 /* Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result at
196    (R_KEYBLOB, R_KEYBLOBLEN).  Returns 0 on success or an error code.
197    On error R_KEYBLOB is set to NULL.  */
198 static gpg_error_t
199 decrypt_keyblob (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen,
200                  void **r_keyblob, size_t *r_keybloblen)
201 {
202   gpg_error_t err;
203
204   /* FIXME:  For now we only implement OpenPGP.  */
205   err = gpg_decrypt_blob (ctrl, opt.gpg_program, opt.gpg_arguments,
206                           enckeyblob, enckeybloblen,
207                           r_keyblob, r_keybloblen);
208
209   return err;
210 }
211
212
213 static void
214 dump_keyblob (tupledesc_t tuples)
215 {
216   size_t n;
217   unsigned int tag;
218   const void *value;
219
220   log_info ("keyblob dump:\n");
221   tag = KEYBLOB_TAG_BLOBVERSION;
222   value = find_tuple (tuples, tag, &n);
223   while (value)
224     {
225       log_info ("   tag: %-5u len: %-2u value: ", tag, (unsigned int)n);
226       if (tag == KEYBLOB_TAG_ENCKEY
227           ||  tag == KEYBLOB_TAG_MACKEY)
228         log_printf ("[confidential]\n");
229       else if (!n)
230         log_printf ("[none]\n");
231       else
232         log_printhex ("", value, n);
233       value = next_tuple (tuples, &tag, &n);
234     }
235 }
236
237
238
239 /* Mount the container with name FILENAME at MOUNTPOINT.  */
240 gpg_error_t
241 g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
242 {
243   gpg_error_t err;
244   dotlock_t lock;
245   void *enckeyblob = NULL;
246   size_t enckeybloblen;
247   void *keyblob = NULL;
248   size_t keybloblen;
249   tupledesc_t tuples = NULL;
250   size_t n;
251   const unsigned char *value;
252   int conttype;
253   unsigned int rid;
254   char *mountpoint_buffer = NULL;
255
256   /* A quick check to see whether the container exists.  */
257   if (access (filename, R_OK))
258     return gpg_error_from_syserror ();
259
260   if (!mountpoint)
261     {
262       mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX");
263       if (!mountpoint_buffer)
264         return gpg_error_from_syserror ();
265       if (!gnupg_mkdtemp (mountpoint_buffer))
266         {
267           err = gpg_error_from_syserror ();
268           log_error (_("can't create directory '%s': %s\n"),
269                      "/tmp/g13-XXXXXX", gpg_strerror (err));
270           xfree (mountpoint_buffer);
271           return err;
272         }
273       mountpoint = mountpoint_buffer;
274     }
275
276   /* Try to take a lock.  */
277   lock = dotlock_create (filename, 0);
278   if (!lock)
279     {
280       xfree (mountpoint_buffer);
281       return gpg_error_from_syserror ();
282     }
283
284   if (dotlock_take (lock, 0))
285     {
286       err = gpg_error_from_syserror ();
287       goto leave;
288     }
289   else
290     err = 0;
291
292   /* Check again that the file exists.  */
293   {
294     struct stat sb;
295
296     if (stat (filename, &sb))
297       {
298         err = gpg_error_from_syserror ();
299         goto leave;
300       }
301   }
302
303   /* Read the encrypted keyblob.  */
304   err = read_keyblob (filename, &enckeyblob, &enckeybloblen);
305   if (err)
306     goto leave;
307
308   /* Decrypt that keyblob and store it in a tuple descriptor.  */
309   err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen,
310                          &keyblob, &keybloblen);
311   if (err)
312     goto leave;
313   xfree (enckeyblob);
314   enckeyblob = NULL;
315
316   err = create_tupledesc (&tuples, keyblob, keybloblen);
317   if (!err)
318     keyblob = NULL;
319   else
320     {
321       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
322         log_error ("unknown keyblob version\n");
323       goto leave;
324     }
325   if (opt.verbose)
326     dump_keyblob (tuples);
327
328   value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
329   if (!value || n != 2)
330     conttype = 0;
331   else
332     conttype = (value[0] << 8 | value[1]);
333   if (!be_is_supported_conttype (conttype))
334     {
335       log_error ("content type %d is not supported\n", conttype);
336       err = gpg_error (GPG_ERR_NOT_SUPPORTED);
337       goto leave;
338     }
339   err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid);
340   if (!err)
341     {
342       err = mountinfo_add_mount (filename, mountpoint, conttype, rid,
343                                  !!mountpoint_buffer);
344       /* Fixme: What shall we do if this fails?  Add a provisional
345          mountinfo entry first and remove it on error? */
346       if (!err)
347         {
348           char *tmp = percent_plus_escape (mountpoint);
349           if (!tmp)
350             err = gpg_error_from_syserror ();
351           else
352             {
353               g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL);
354               xfree (tmp);
355             }
356         }
357     }
358
359  leave:
360   destroy_tupledesc (tuples);
361   xfree (keyblob);
362   xfree (enckeyblob);
363   dotlock_destroy (lock);
364   xfree (mountpoint_buffer);
365   return err;
366 }
367
368
369 /* Unmount the container with name FILENAME or the one mounted at
370    MOUNTPOINT.  If both are given the FILENAME takes precedence.  */
371 gpg_error_t
372 g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
373 {
374   gpg_error_t err;
375   unsigned int rid;
376   runner_t runner;
377
378   (void)ctrl;
379
380   if (!filename && !mountpoint)
381     return gpg_error (GPG_ERR_ENOENT);
382   err = mountinfo_find_mount (filename, mountpoint, &rid);
383   if (err)
384     return err;
385
386   runner = runner_find_by_rid (rid);
387   if (!runner)
388     {
389       log_error ("runner %u not found\n", rid);
390       return gpg_error (GPG_ERR_NOT_FOUND);
391     }
392
393   runner_cancel (runner);
394   runner_release (runner);
395
396   return 0;
397 }
398
399
400 /* Test whether the container with name FILENAME is a suitable G13
401    container.  This function may even be called on a mounted
402    container.  */
403 gpg_error_t
404 g13_is_container (ctrl_t ctrl, const char *filename)
405 {
406   gpg_error_t err;
407   estream_t fp = NULL;
408   size_t dummy;
409
410   (void)ctrl;
411
412   /* Read just the prefix of the header.  */
413   err = read_keyblob_prefix (filename, &fp, &dummy);
414   if (!err)
415     es_fclose (fp);
416   return err;
417 }