g13: Fix double free bug.
[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   /* A quick check to see whether the container exists.  */
64   if (access (filename, F_OK))
65     return gpg_error_from_syserror ();
66
67   /* Decide whether we need to use the g13-syshelp because we can't
68      use lock files for them.  This is most likely the case for device
69      files; thus we test for this.  FIXME: The correct solution would
70      be to call g13-syshelp to match the file against the g13tab.  */
71   err = call_syshelp_find_device (ctrl, filename, &blockdev_buffer);
72   if (!err)
73     {
74       needs_syshelp = 1;
75       filename = blockdev_buffer;
76     }
77   else if (gpg_err_code (err) != GPG_ERR_NOT_FOUND)
78     {
79       log_error ("error finding device '%s': %s <%s>\n",
80                  filename, gpg_strerror (err), gpg_strsource (err));
81       return err;
82     }
83
84   if (!mountpoint)
85     {
86       mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX");
87       if (!mountpoint_buffer)
88         return gpg_error_from_syserror ();
89       if (!gnupg_mkdtemp (mountpoint_buffer))
90         {
91           err = gpg_error_from_syserror ();
92           log_error (_("can't create directory '%s': %s\n"),
93                      "/tmp/g13-XXXXXX", gpg_strerror (err));
94           xfree (mountpoint_buffer);
95           return err;
96         }
97       mountpoint = mountpoint_buffer;
98     }
99
100   err = 0;
101   if (needs_syshelp)
102     lock = NULL;
103   else
104     {
105       /* Try to take a lock.  */
106       lock = dotlock_create (filename, 0);
107       if (!lock)
108         {
109           xfree (mountpoint_buffer);
110           return gpg_error_from_syserror ();
111         }
112
113       if (dotlock_take (lock, 0))
114         {
115           err = gpg_error_from_syserror ();
116           goto leave;
117         }
118     }
119
120   /* Check again that the file exists.  */
121   if (!needs_syshelp)
122     {
123       struct stat sb;
124
125       if (stat (filename, &sb))
126         {
127           err = gpg_error_from_syserror ();
128           goto leave;
129         }
130     }
131
132   /* Read the encrypted keyblob.  */
133   if (needs_syshelp)
134     {
135       err = call_syshelp_set_device (ctrl, filename);
136       if (err)
137         goto leave;
138       err = call_syshelp_get_keyblob (ctrl, &enckeyblob, &enckeybloblen);
139     }
140   else
141     err = g13_keyblob_read (filename, &enckeyblob, &enckeybloblen);
142   if (err)
143     goto leave;
144
145   /* Decrypt that keyblob and store it in a tuple descriptor.  */
146   err = g13_keyblob_decrypt (ctrl, enckeyblob, enckeybloblen,
147                              &keyblob, &keybloblen);
148   if (err)
149     goto leave;
150   xfree (enckeyblob);
151   enckeyblob = NULL;
152
153   err = create_tupledesc (&tuples, keyblob, keybloblen);
154   if (!err)
155     keyblob = NULL;
156   else
157     {
158       if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
159         log_error ("unknown keyblob version\n");
160       goto leave;
161     }
162   if (opt.verbose)
163     dump_tupledesc (tuples);
164
165   value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
166   if (!value || n != 2)
167     conttype = 0;
168   else
169     conttype = (value[0] << 8 | value[1]);
170   if (!be_is_supported_conttype (conttype))
171     {
172       log_error ("content type %d is not supported\n", conttype);
173       err = gpg_error (GPG_ERR_NOT_SUPPORTED);
174       goto leave;
175     }
176   err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid);
177   if (err)
178     ;
179   else if (conttype == CONTTYPE_DM_CRYPT)
180     g13_request_shutdown ();
181   else
182     {
183       /* Unless this is a DM-CRYPT mount we put it into our mounttable
184          so that we can manage the mounts ourselves.  For dm-crypt we
185          do not keep a process to monitor he mounts (for now).  */
186       err = mountinfo_add_mount (filename, mountpoint, conttype, rid,
187                                  !!mountpoint_buffer);
188       /* Fixme: What shall we do if this fails?  Add a provisional
189          mountinfo entry first and remove it on error? */
190       if (!err)
191         {
192           char *tmp = percent_plus_escape (mountpoint);
193           if (!tmp)
194             err = gpg_error_from_syserror ();
195           else
196             {
197               g13_status (ctrl, STATUS_MOUNTPOINT, tmp, NULL);
198               xfree (tmp);
199             }
200         }
201     }
202
203  leave:
204   destroy_tupledesc (tuples);
205   xfree (keyblob);
206   xfree (enckeyblob);
207   dotlock_destroy (lock);
208   xfree (mountpoint_buffer);
209   xfree (blockdev_buffer);
210   return err;
211 }
212
213
214 /* Unmount the container with name FILENAME or the one mounted at
215    MOUNTPOINT.  If both are given the FILENAME takes precedence.  */
216 gpg_error_t
217 g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
218 {
219   gpg_error_t err;
220   unsigned int rid;
221   runner_t runner;
222
223   (void)ctrl;
224
225   if (!filename && !mountpoint)
226     return gpg_error (GPG_ERR_ENOENT);
227
228   err = mountinfo_find_mount (filename, mountpoint, &rid);
229   if (err)
230     return err;
231
232   runner = runner_find_by_rid (rid);
233   if (!runner)
234     {
235       log_error ("runner %u not found\n", rid);
236       return gpg_error (GPG_ERR_NOT_FOUND);
237     }
238
239   runner_cancel (runner);
240   runner_release (runner);
241
242   return 0;
243 }