g13: Second chunk of code to support dm-crypt.
[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 "g13tuple.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 )
74     log_info ("Note: meta data copies in '%s' are ignored\n", filename);
75
76   len = buf32_to_uint (packet+20);
77
78   /* Do a basic sanity check on the length.  */
79   if (len < 32 || len > 1024*1024)
80     {
81       log_error ("bad length given in container '%s'\n", filename);
82       return gpg_error (GPG_ERR_INV_OBJ);
83     }
84
85   *r_headerlen = len;
86   return 0;
87 }
88
89
90 /* Read the prefix of the keyblob and do some basic parsing.  On
91    success returns an open estream file at R_FP and the length of the
92    header at R_HEADERLEN.  */
93 static gpg_error_t
94 read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen)
95 {
96   gpg_error_t err;
97   estream_t fp;
98   unsigned char packet[32];
99
100   *r_fp = NULL;
101
102   fp = es_fopen (filename, "rb");
103   if (!fp)
104     {
105       err = gpg_error_from_syserror ();
106       log_error ("error reading '%s': %s\n", filename, gpg_strerror (err));
107       return err;
108     }
109
110   /* Read the header.  It is defined as 32 bytes thus we read it in one go.  */
111   if (es_fread (packet, 32, 1, fp) != 1)
112     {
113       err = gpg_error_from_syserror ();
114       log_error ("error reading the header of '%s': %s\n",
115                  filename, gpg_strerror (err));
116       es_fclose (fp);
117       return err;
118     }
119
120   err = parse_header (filename, packet, 32, r_headerlen);
121   if (err)
122     es_fclose (fp);
123   else
124     *r_fp = fp;
125
126   return err;
127 }
128
129
130 /* Read the keyblob at FILENAME.  The caller should have acquired a
131    lockfile and checked that the file exists.  */
132 static gpg_error_t
133 read_keyblob (const char *filename,
134               void **r_enckeyblob, size_t *r_enckeybloblen)
135 {
136   gpg_error_t err;
137   estream_t fp = NULL;
138   size_t headerlen = 0;
139   size_t msglen;
140   void *msg = NULL;
141
142   *r_enckeyblob = NULL;
143   *r_enckeybloblen = 0;
144
145   err = read_keyblob_prefix (filename, &fp, &headerlen);
146   if (err)
147     goto leave;
148
149   if (opt.verbose)
150     log_info ("header length of '%s' is %zu\n", filename, headerlen);
151
152   /* Read everything including the padding.  We should eventually do a
153      regular OpenPGP parsing to detect the padding packet and pass
154      only the actual used OpenPGP data to the engine.  This is in
155      particular required when supporting CMS which will be
156      encapsulated in an OpenPGP packet.  */
157   assert (headerlen >= 32);
158   msglen = headerlen - 32;
159   if (!msglen)
160     {
161       err = gpg_error (GPG_ERR_NO_DATA);
162       goto leave;
163     }
164   msg = xtrymalloc (msglen);
165   if (!msglen)
166     {
167       err = gpg_error_from_syserror ();
168       goto leave;
169     }
170   if (es_fread (msg, msglen, 1, fp) != 1)
171     {
172       err = gpg_error_from_syserror ();
173       log_error ("error reading keyblob of '%s': %s\n",
174                  filename, gpg_strerror (err));
175       goto leave;
176     }
177
178   *r_enckeyblob = msg;
179   msg = NULL;
180   *r_enckeybloblen = msglen;
181
182  leave:
183   xfree (msg);
184   es_fclose (fp);
185
186   return err;
187 }
188
189
190
191
192 /* Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result at
193    (R_KEYBLOB, R_KEYBLOBLEN).  Returns 0 on success or an error code.
194    On error R_KEYBLOB is set to NULL.  */
195 static gpg_error_t
196 decrypt_keyblob (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen,
197                  void **r_keyblob, size_t *r_keybloblen)
198 {
199   gpg_error_t err;
200
201   /* FIXME:  For now we only implement OpenPGP.  */
202   err = gpg_decrypt_blob (ctrl, opt.gpg_program, opt.gpg_arguments,
203                           enckeyblob, enckeybloblen,
204                           r_keyblob, r_keybloblen);
205
206   return err;
207 }
208
209
210 /* Mount the container with name FILENAME at MOUNTPOINT.  */
211 gpg_error_t
212 g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
213 {
214   gpg_error_t err;
215   dotlock_t lock;
216   int needs_syshelp;
217   void *enckeyblob = NULL;
218   size_t enckeybloblen;
219   void *keyblob = NULL;
220   size_t keybloblen;
221   tupledesc_t tuples = NULL;
222   size_t n;
223   const unsigned char *value;
224   int conttype;
225   unsigned int rid;
226   char *mountpoint_buffer = NULL;
227
228   /* A quick check to see whether the container exists.  */
229   if (access (filename, R_OK))
230     return gpg_error_from_syserror ();
231
232   /* Decide whether we need to use the g13-syshelp because we can't
233      use lock files for them.  This is most likely the case for device
234      files; thus we test for this.  FIXME: The correct solution would
235      be to call g13-syshelp to match the file against the g13tab.  */
236   needs_syshelp = !strncmp (filename, "/dev/", 5);
237
238   if (!mountpoint)
239     {
240       mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX");
241       if (!mountpoint_buffer)
242         return gpg_error_from_syserror ();
243       if (!gnupg_mkdtemp (mountpoint_buffer))
244         {
245           err = gpg_error_from_syserror ();
246           log_error (_("can't create directory '%s': %s\n"),
247                      "/tmp/g13-XXXXXX", gpg_strerror (err));
248           xfree (mountpoint_buffer);
249           return err;
250         }
251       mountpoint = mountpoint_buffer;
252     }
253
254   err = 0;
255   if (needs_syshelp)
256     lock = NULL;
257   else
258     {
259       /* Try to take a lock.  */
260       lock = dotlock_create (filename, 0);
261       if (!lock)
262         {
263           xfree (mountpoint_buffer);
264           return gpg_error_from_syserror ();
265         }
266
267       if (dotlock_take (lock, 0))
268         {
269           err = gpg_error_from_syserror ();
270           goto leave;
271         }
272     }
273
274   /* Check again that the file exists.  */
275   {
276     struct stat sb;
277
278     if (stat (filename, &sb))
279       {
280         err = gpg_error_from_syserror ();
281         goto leave;
282       }
283   }
284
285   /* Read the encrypted keyblob.  */
286   /* Fixme: Should we move this to syshelp for dm-crypt or do we
287      assume that the encrypted device is world readable?  */
288   err = read_keyblob (filename, &enckeyblob, &enckeybloblen);
289   if (err)
290     goto leave;
291
292   /* Decrypt that keyblob and store it in a tuple descriptor.  */
293   err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen,
294                          &keyblob, &keybloblen);
295   if (err)
296     goto leave;
297   xfree (enckeyblob);
298   enckeyblob = NULL;
299
300   err = create_tupledesc (&tuples, keyblob, keybloblen);
301   if (!err)
302     keyblob = NULL;
303   else
304     {
305       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
306         log_error ("unknown keyblob version\n");
307       goto leave;
308     }
309   if (opt.verbose)
310     dump_tupledesc (tuples);
311
312   value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
313   if (!value || n != 2)
314     conttype = 0;
315   else
316     conttype = (value[0] << 8 | value[1]);
317   if (!be_is_supported_conttype (conttype))
318     {
319       log_error ("content type %d is not supported\n", conttype);
320       err = gpg_error (GPG_ERR_NOT_SUPPORTED);
321       goto leave;
322     }
323   err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid);
324   if (err)
325     ;
326   else if (conttype == CONTTYPE_DM_CRYPT)
327     g13_request_shutdown ();
328   else
329     {
330       /* Unless this is a DM-CRYPT mount we put it into our mounttable
331          so that we can manage the mounts ourselves.  For dm-crypt we
332          do not keep a process to monitor he mounts (for now).  */
333       err = mountinfo_add_mount (filename, mountpoint, conttype, rid,
334                                  !!mountpoint_buffer);
335       /* Fixme: What shall we do if this fails?  Add a provisional
336          mountinfo entry first and remove it on error? */
337       if (!err)
338         {
339           char *tmp = percent_plus_escape (mountpoint);
340           if (!tmp)
341             err = gpg_error_from_syserror ();
342           else
343             {
344               g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL);
345               xfree (tmp);
346             }
347         }
348     }
349
350  leave:
351   destroy_tupledesc (tuples);
352   xfree (keyblob);
353   xfree (enckeyblob);
354   dotlock_destroy (lock);
355   xfree (mountpoint_buffer);
356   return err;
357 }
358
359
360 /* Unmount the container with name FILENAME or the one mounted at
361    MOUNTPOINT.  If both are given the FILENAME takes precedence.  */
362 gpg_error_t
363 g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
364 {
365   gpg_error_t err;
366   unsigned int rid;
367   runner_t runner;
368
369   (void)ctrl;
370
371   if (!filename && !mountpoint)
372     return gpg_error (GPG_ERR_ENOENT);
373   err = mountinfo_find_mount (filename, mountpoint, &rid);
374   if (err)
375     return err;
376
377   runner = runner_find_by_rid (rid);
378   if (!runner)
379     {
380       log_error ("runner %u not found\n", rid);
381       return gpg_error (GPG_ERR_NOT_FOUND);
382     }
383
384   runner_cancel (runner);
385   runner_release (runner);
386
387   return 0;
388 }
389
390
391 /* Test whether the container with name FILENAME is a suitable G13
392    container.  This function may even be called on a mounted
393    container.  */
394 gpg_error_t
395 g13_is_container (ctrl_t ctrl, const char *filename)
396 {
397   gpg_error_t err;
398   estream_t fp = NULL;
399   size_t dummy;
400
401   (void)ctrl;
402
403   /* Read just the prefix of the header.  */
404   err = read_keyblob_prefix (filename, &fp, &dummy);
405   if (!err)
406     es_fclose (fp);
407   return err;
408 }