mount does now work in server and standalone mode.
[gnupg.git] / g13 / be-encfs.c
blobde3209a910c76129ce0f894a47a8d843b489dedc
1 /* be-encfs.c - The EncFS based backend
2 * Copyright (C) 2009 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
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.
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.
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/>.
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 <assert.h>
28 #include "g13.h"
29 #include "i18n.h"
30 #include "keyblob.h"
31 #include "be-encfs.h"
32 #include "runner.h"
33 #include "../common/exechelp.h"
36 /* Command values used to run the encfs tool. */
37 enum encfs_cmds
39 ENCFS_CMD_CREATE,
40 ENCFS_CMD_MOUNT,
41 ENCFS_CMD_UMOUNT
45 /* An object to keep the private state of the encfs tool. It is
46 released by encfs_handler_cleanup. */
47 struct encfs_parm_s
49 enum encfs_cmds cmd; /* The current command. */
50 tupledesc_t tuples; /* NULL or the tuples object. */
51 char *mountpoint; /* The mountpoint. */
53 typedef struct encfs_parm_s *encfs_parm_t;
56 static gpg_error_t
57 send_cmd_bin (runner_t runner, const void *data, size_t datalen)
59 return runner_send_line (runner, data, datalen);
63 static gpg_error_t
64 send_cmd (runner_t runner, const char *string)
66 log_debug ("sending command -->%s<--\n", string);
67 return send_cmd_bin (runner, string, strlen (string));
72 static void
73 run_umount_helper (const char *mountpoint)
75 gpg_error_t err;
76 const char pgmname[] = "/usr/bin/fusermount";
77 const char *args[3];
79 args[0] = "-u";
80 args[1] = mountpoint;
81 args[2] = NULL;
83 err = gnupg_spawn_process_detached (pgmname, args, NULL);
84 if (err)
85 log_error ("failed to run `%s': %s\n",
86 pgmname, gpg_strerror (err));
90 /* Handle one line of the encfs tool's output. This function is
91 allowed to modify the content of BUFFER. */
92 static gpg_error_t
93 handle_status_line (runner_t runner, const char *line,
94 enum encfs_cmds cmd, tupledesc_t tuples)
96 gpg_error_t err;
98 /* Check that encfs understands our new options. */
99 if (!strncmp (line, "$STATUS$", 8))
101 for (line +=8; *line && spacep (line); line++)
103 log_info ("got status `%s'\n", line);
104 if (!strcmp (line, "fuse_main_start"))
106 /* Send a special error code back to let the caller know
107 that everything has been setup by encfs. */
108 err = gpg_error (GPG_ERR_UNFINISHED);
110 else
111 err = 0;
113 else if (!strncmp (line, "$PROMPT$", 8))
115 for (line +=8; *line && spacep (line); line++)
117 log_info ("got prompt `%s'\n", line);
118 if (!strcmp (line, "create_root_dir"))
119 err = send_cmd (runner, cmd == ENCFS_CMD_CREATE? "y":"n");
120 else if (!strcmp (line, "create_mount_point"))
121 err = send_cmd (runner, "y");
122 else if (!strcmp (line, "passwd")
123 || !strcmp (line, "new_passwd"))
125 if (tuples)
127 size_t n;
128 const void *value;
130 value = find_tuple (tuples, KEYBLOB_TAG_ENCKEY, &n);
131 if (!value)
132 err = gpg_error (GPG_ERR_INV_SESSION_KEY);
133 else if ((err = send_cmd_bin (runner, value, n)))
135 if (gpg_err_code (err) == GPG_ERR_BUG
136 && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
137 err = gpg_error (GPG_ERR_INV_SESSION_KEY);
140 else
141 err = gpg_error (GPG_ERR_NO_DATA);
143 else
144 err = send_cmd (runner, ""); /* Default to send an empty line. */
146 else if (strstr (line, "encfs: unrecognized option '"))
147 err = gpg_error (GPG_ERR_INV_ENGINE);
148 else
149 err = 0;
151 return err;
155 /* The main processing function as used by the runner. */
156 static gpg_error_t
157 encfs_handler (void *opaque, runner_t runner, const char *status_line)
159 encfs_parm_t parm = opaque;
160 gpg_error_t err;
162 if (!parm || !runner)
163 return gpg_error (GPG_ERR_BUG);
164 if (!status_line)
166 /* Runner requested internal flushing - nothing to do here. */
167 return 0;
170 err = handle_status_line (runner, status_line, parm->cmd, parm->tuples);
171 if (gpg_err_code (err) == GPG_ERR_UNFINISHED
172 && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
174 err = 0;
175 /* No more need for the tuples. */
176 destroy_tupledesc (parm->tuples);
177 parm->tuples = NULL;
179 if (parm->cmd == ENCFS_CMD_CREATE)
181 /* The encfs tool keeps on running after creation of the
182 container. We don't want that and thus need to stop the
183 encfs process. */
184 run_umount_helper (parm->mountpoint);
185 /* In case the umount helper does not work we try to kill
186 the engine. FIXME: We should figure out how to make
187 fusermount work. */
188 runner_cancel (runner);
192 return err;
196 /* Called by the runner to cleanup the private data. */
197 static void
198 encfs_handler_cleanup (void *opaque)
200 encfs_parm_t parm = opaque;
202 if (!parm)
203 return;
205 destroy_tupledesc (parm->tuples);
206 xfree (parm->mountpoint);
207 xfree (parm);
211 /* Run the encfs tool. */
212 static gpg_error_t
213 run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
214 const char *rawdir, const char *mountpoint, tupledesc_t tuples)
216 gpg_error_t err;
217 encfs_parm_t parm;
218 runner_t runner = NULL;
219 int outbound[2] = { -1, -1 };
220 int inbound[2] = { -1, -1 };
221 const char *pgmname;
222 const char *argv[10];
223 pid_t pid = (pid_t)(-1);
224 int idx;
226 (void)ctrl;
228 parm = xtrycalloc (1, sizeof *parm);
229 if (!parm)
231 err = gpg_error_from_syserror ();
232 goto leave;
234 parm->cmd = cmd;
235 parm->tuples = ref_tupledesc (tuples);
236 parm->mountpoint = xtrystrdup (mountpoint);
237 if (!parm->mountpoint)
239 err = gpg_error_from_syserror ();
240 goto leave;
244 static int namecounter;
245 char buffer[50];
247 snprintf (buffer, sizeof buffer, "encfs-%d", ++namecounter);
248 err = runner_new (&runner, buffer);
249 if (err)
250 goto leave;
253 err = gnupg_create_inbound_pipe (inbound);
254 if (!err)
255 err = gnupg_create_outbound_pipe (outbound);
256 if (err)
258 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
259 goto leave;
262 pgmname = "/usr/bin/encfs";
263 idx = 0;
264 argv[idx++] = "-f";
265 if (opt.verbose)
266 argv[idx++] = "-v";
267 argv[idx++] = "--stdinpass";
268 argv[idx++] = "--annotate";
269 argv[idx++] = rawdir;
270 argv[idx++] = mountpoint;
271 argv[idx++] = NULL;
272 assert (idx <= DIM (argv));
274 err = gnupg_spawn_process_fd (pgmname, argv,
275 outbound[0], -1, inbound[1], &pid);
276 if (err)
278 log_error ("error spawning `%s': %s\n", pgmname, gpg_strerror (err));
279 goto leave;
281 close (outbound[0]); outbound[0] = -1;
282 close ( inbound[1]); inbound[1] = -1;
284 runner_set_fds (runner, inbound[0], outbound[1]);
285 inbound[0] = -1; /* Now owned by RUNNER. */
286 outbound[1] = -1; /* Now owned by RUNNER. */
288 runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm);
289 parm = NULL; /* Now owned by RUNNER. */
291 runner_set_pid (runner, pid);
292 pid = (pid_t)(-1); /* The process is now owned by RUNNER. */
294 err = runner_spawn (runner);
295 if (err)
296 goto leave;
298 log_info ("running `%s' in the background\n", pgmname);
300 leave:
301 if (inbound[0] != -1)
302 close (inbound[0]);
303 if (inbound[1] != -1)
304 close (inbound[1]);
305 if (outbound[0] != -1)
306 close (outbound[0]);
307 if (outbound[1] != -1)
308 close (outbound[1]);
309 if (pid != (pid_t)(-1))
311 gnupg_wait_process (pgmname, pid, NULL);
313 runner_release (runner);
314 encfs_handler_cleanup (parm);
315 return err;
322 /* See be_get_detached_name for a description. Note that the
323 dispatcher code makes sure that NULL is stored at R_NAME before
324 calling us. */
325 gpg_error_t
326 be_encfs_get_detached_name (const char *fname, char **r_name, int *r_isdir)
328 char *result;
330 if (!fname || !*fname)
331 return gpg_error (GPG_ERR_INV_ARG);
333 result = strconcat (fname, ".d", NULL);
334 if (!result)
335 return gpg_error_from_syserror ();
336 *r_name = result;
337 *r_isdir = 1;
338 return 0;
342 /* Create a new session key and append it as a tuple to the memory
343 buffer MB.
345 The EncFS daemon takes a passphrase from stdin and internally
346 mangles it by means of some KDF from OpenSSL. We want to store a
347 binary key but we need to make sure that certain characters are not
348 used because the EncFS utility reads it from stdin and obviously
349 acts on some of the characters. This we replace CR (in case of an
350 MSDOS version of EncFS), LF (the delimiter used by EncFS) and Nul
351 (because it is unlikely to work). We use 32 bytes (256 bit)
352 because that is sufficient for the largest cipher (AES-256) and in
353 addition gives enough margin for a possible entropy degradation by
354 the KDF. */
355 gpg_error_t
356 be_encfs_create_new_keys (membuf_t *mb)
358 char *buffer;
359 int i, j;
361 /* Allocate a buffer of 32 bytes plus 8 spare bytes we may need to
362 replace the unwanted values. */
363 buffer = xtrymalloc_secure (32+8);
364 if (!buffer)
365 return gpg_error_from_syserror ();
367 /* Randomize the buffer. STRONG random should be enough as it is a
368 good compromise between security and performance. The
369 anticipated usage of this tool is the quite often creation of new
370 containers and thus this should not deplete the system's entropy
371 tool too much. */
372 gcry_randomize (buffer, 32+8, GCRY_STRONG_RANDOM);
373 for (i=j=0; i < 32; i++)
375 if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == 0 )
377 /* Replace. */
378 if (j == 8)
380 /* Need to get more random. */
381 gcry_randomize (buffer+32, 8, GCRY_STRONG_RANDOM);
382 j = 0;
384 buffer[i] = buffer[32+j];
385 j++;
389 /* Store the key. */
390 append_tuple (mb, KEYBLOB_TAG_ENCKEY, buffer, 32);
392 /* Free the temporary buffer. */
393 wipememory (buffer, 32+8); /* A failsafe extra wiping. */
394 xfree (buffer);
396 return 0;
400 /* Create the container described by the filename FNAME and the keyblob
401 information in TUPLES. */
402 gpg_error_t
403 be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples)
405 gpg_error_t err;
406 int dummy;
407 char *containername = NULL;
408 char *mountpoint = NULL;
410 err = be_encfs_get_detached_name (fname, &containername, &dummy);
411 if (err)
412 goto leave;
414 mountpoint = xtrystrdup ("/tmp/.#g13_XXXXXX");
415 if (!mountpoint)
417 err = gpg_error_from_syserror ();
418 goto leave;
420 if (!mkdtemp (mountpoint))
422 err = gpg_error_from_syserror ();
423 log_error (_("can't create directory `%s': %s\n"),
424 "/tmp/g13-XXXXXX", gpg_strerror (err));
425 goto leave;
428 err = run_encfs_tool (ctrl, ENCFS_CMD_CREATE, containername, mountpoint,
429 tuples);
431 /* In any case remove the temporary mount point. */
432 if (rmdir (mountpoint))
433 log_error ("error removing temporary mount point `%s': %s\n",
434 mountpoint, gpg_strerror (gpg_error_from_syserror ()));
437 leave:
438 xfree (containername);
439 xfree (mountpoint);
440 return err;
444 /* Mount the container described by the filename FNAME and the keyblob
445 information in TUPLES. */
446 gpg_error_t
447 be_encfs_mount_container (ctrl_t ctrl,
448 const char *fname, const char *mountpoint,
449 tupledesc_t tuples)
451 gpg_error_t err;
452 int dummy;
453 char *containername = NULL;
455 if (!mountpoint)
457 log_error ("the encfs backend requires an explicit mountpoint\n");
458 err = gpg_error (GPG_ERR_NOT_SUPPORTED);
459 goto leave;
462 err = be_encfs_get_detached_name (fname, &containername, &dummy);
463 if (err)
464 goto leave;
466 err = run_encfs_tool (ctrl, ENCFS_CMD_MOUNT, containername, mountpoint,
467 tuples);
469 leave:
470 xfree (containername);
471 return err;