g13: Allow the use of a g13tab label for --mount.
[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 "mountinfo.h"
37 #include "runner.h"
38 #include "host2net.h"
39 #include "server.h"  /*(g13_keyblob_decrypt)*/
40 #include "../common/sysutils.h"
41 #include "call-syshelp.h"
42
43
44 /* Mount the container with name FILENAME at MOUNTPOINT.  */
45 gpg_error_t
46 g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
47 {
48   gpg_error_t err;
49   dotlock_t lock;
50   int needs_syshelp = 0;
51   void *enckeyblob = NULL;
52   size_t enckeybloblen;
53   void *keyblob = NULL;
54   size_t keybloblen;
55   tupledesc_t tuples = NULL;
56   size_t n;
57   const unsigned char *value;
58   int conttype;
59   unsigned int rid;
60   char *mountpoint_buffer = NULL;
61   char *blockdev_buffer = NULL;
62
63   /* Decide whether we need to use the g13-syshelp.  */
64   err = call_syshelp_find_device (ctrl, filename, &blockdev_buffer);
65   if (!err)
66     {
67       needs_syshelp = 1;
68       filename = blockdev_buffer;
69     }
70   else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
71     {
72       log_error ("error finding device '%s': %s <%s>\n",
73                  filename, gpg_strerror (err), gpg_strsource (err));
74       return err;
75     }
76   else
77     {
78       /* A quick check to see whether we can the container exists.  */
79       if (access (filename, R_OK))
80         return gpg_error_from_syserror ();
81     }
82
83   if (!mountpoint)
84     {
85       mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX");
86       if (!mountpoint_buffer)
87         return gpg_error_from_syserror ();
88       if (!gnupg_mkdtemp (mountpoint_buffer))
89         {
90           err = gpg_error_from_syserror ();
91           log_error (_("can't create directory '%s': %s\n"),
92                      "/tmp/g13-XXXXXX", gpg_strerror (err));
93           xfree (mountpoint_buffer);
94           return err;
95         }
96       mountpoint = mountpoint_buffer;
97     }
98
99   err = 0;
100   if (needs_syshelp)
101     lock = NULL;
102   else
103     {
104       /* Try to take a lock.  */
105       lock = dotlock_create (filename, 0);
106       if (!lock)
107         {
108           xfree (mountpoint_buffer);
109           return gpg_error_from_syserror ();
110         }
111
112       if (dotlock_take (lock, 0))
113         {
114           err = gpg_error_from_syserror ();
115           goto leave;
116         }
117     }
118
119   /* Check again that the file exists.  */
120   if (!needs_syshelp)
121     {
122       struct stat sb;
123
124       if (stat (filename, &sb))
125         {
126           err = gpg_error_from_syserror ();
127           goto leave;
128         }
129     }
130
131   /* Read the encrypted keyblob.  */
132   if (needs_syshelp)
133     {
134       err = call_syshelp_set_device (ctrl, filename);
135       if (err)
136         goto leave;
137       err = call_syshelp_get_keyblob (ctrl, &enckeyblob, &enckeybloblen);
138     }
139   else
140     err = g13_keyblob_read (filename, &enckeyblob, &enckeybloblen);
141   if (err)
142     goto leave;
143
144   /* Decrypt that keyblob and store it in a tuple descriptor.  */
145   err = g13_keyblob_decrypt (ctrl, enckeyblob, enckeybloblen,
146                              &keyblob, &keybloblen);
147   if (err)
148     goto leave;
149   xfree (enckeyblob);
150   enckeyblob = NULL;
151
152   err = create_tupledesc (&tuples, keyblob, keybloblen);
153   if (!err)
154     keyblob = NULL;
155   else
156     {
157       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
158         log_error ("unknown keyblob version\n");
159       goto leave;
160     }
161   if (opt.verbose)
162     dump_tupledesc (tuples);
163
164   value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
165   if (!value || n != 2)
166     conttype = 0;
167   else
168     conttype = (value[0] << 8 | value[1]);
169   if (!be_is_supported_conttype (conttype))
170     {
171       log_error ("content type %d is not supported\n", conttype);
172       err = gpg_error (GPG_ERR_NOT_SUPPORTED);
173       goto leave;
174     }
175   err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid);
176   if (err)
177     ;
178   else if (conttype == CONTTYPE_DM_CRYPT)
179     g13_request_shutdown ();
180   else
181     {
182       /* Unless this is a DM-CRYPT mount we put it into our mounttable
183          so that we can manage the mounts ourselves.  For dm-crypt we
184          do not keep a process to monitor he mounts (for now).  */
185       err = mountinfo_add_mount (filename, mountpoint, conttype, rid,
186                                  !!mountpoint_buffer);
187       /* Fixme: What shall we do if this fails?  Add a provisional
188          mountinfo entry first and remove it on error? */
189       if (!err)
190         {
191           char *tmp = percent_plus_escape (mountpoint);
192           if (!tmp)
193             err = gpg_error_from_syserror ();
194           else
195             {
196               g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL);
197               xfree (tmp);
198             }
199         }
200     }
201
202  leave:
203   destroy_tupledesc (tuples);
204   xfree (keyblob);
205   xfree (enckeyblob);
206   dotlock_destroy (lock);
207   xfree (mountpoint_buffer);
208   xfree (blockdev_buffer);
209   return err;
210 }
211
212
213 /* Unmount the container with name FILENAME or the one mounted at
214    MOUNTPOINT.  If both are given the FILENAME takes precedence.  */
215 gpg_error_t
216 g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
217 {
218   gpg_error_t err;
219   char *blockdev;
220
221   if (!filename && !mountpoint)
222     return gpg_error (GPG_ERR_ENOENT);
223
224   /* Decide whether we need to use the g13-syshelp.  */
225   err = call_syshelp_find_device (ctrl, filename, &blockdev);
226   if (!err)
227     {
228       /* Need to employ the syshelper to umount the file system.  */
229       /* FIXME: We should get the CONTTYPE from the blockdev.  */
230       err = be_umount_container (ctrl, CONTTYPE_DM_CRYPT, blockdev);
231       if (!err)
232         {
233           /* if (conttype == CONTTYPE_DM_CRYPT) */
234           g13_request_shutdown ();
235         }
236     }
237   else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
238     {
239       log_error ("error finding device '%s': %s <%s>\n",
240                  filename, gpg_strerror (err), gpg_strsource (err));
241     }
242   else
243     {
244       /* Not in g13tab - kill the runner process for this mount.  */
245       unsigned int rid;
246       runner_t runner;
247
248       err = mountinfo_find_mount (filename, mountpoint, &rid);
249       if (err)
250         return err;
251
252       runner = runner_find_by_rid (rid);
253       if (!runner)
254         {
255           log_error ("runner %u not found\n", rid);
256           return gpg_error (GPG_ERR_NOT_FOUND);
257         }
258
259       runner_cancel (runner);
260       runner_release (runner);
261     }
262
263   xfree (blockdev);
264   return err;
265 }