Keep on hacking on g13. A simple --create and --mount does now work.
[gnupg.git] / g13 / be-encfs.c
blob0f7ec73e6cf56627ebda23fd826eddd14ce4c043
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 argv[idx++] = "-v";
266 argv[idx++] = "--stdinpass";
267 argv[idx++] = "--annotate";
268 argv[idx++] = rawdir;
269 argv[idx++] = mountpoint;
270 argv[idx++] = NULL;
271 assert (idx <= DIM (argv));
273 err = gnupg_spawn_process_fd (pgmname, argv,
274 outbound[0], -1, inbound[1], &pid);
275 if (err)
277 log_error ("error spawning `%s': %s\n", pgmname, gpg_strerror (err));
278 goto leave;
280 close (outbound[0]); outbound[0] = -1;
281 close ( inbound[1]); inbound[1] = -1;
283 runner_set_fds (runner, inbound[0], outbound[1]);
284 inbound[0] = -1; /* Now owned by RUNNER. */
285 outbound[1] = -1; /* Now owned by RUNNER. */
287 runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm);
288 parm = NULL; /* Now owned by RUNNER. */
290 runner_set_pid (runner, pid);
291 pid = (pid_t)(-1); /* The process is now owned by RUNNER. */
293 err = runner_spawn (runner);
294 if (err)
295 goto leave;
297 log_info ("running `%s' in the background\n", pgmname);
299 leave:
300 if (inbound[0] != -1)
301 close (inbound[0]);
302 if (inbound[1] != -1)
303 close (inbound[1]);
304 if (outbound[0] != -1)
305 close (outbound[0]);
306 if (outbound[1] != -1)
307 close (outbound[1]);
308 if (pid != (pid_t)(-1))
310 gnupg_wait_process (pgmname, pid, NULL);
312 runner_release (runner);
313 encfs_handler_cleanup (parm);
314 return err;
321 /* See be_get_detached_name for a description. Note that the
322 dispatcher code makes sure that NULL is stored at R_NAME before
323 calling us. */
324 gpg_error_t
325 be_encfs_get_detached_name (const char *fname, char **r_name, int *r_isdir)
327 char *result;
329 if (!fname || !*fname)
330 return gpg_error (GPG_ERR_INV_ARG);
332 result = strconcat (fname, ".d", NULL);
333 if (!result)
334 return gpg_error_from_syserror ();
335 *r_name = result;
336 *r_isdir = 1;
337 return 0;
341 /* Create a new session key and append it as a tuple to the memory
342 buffer MB.
344 The EncFS daemon takes a passphrase from stdin and internally
345 mangles it by means of some KDF from OpenSSL. We want to store a
346 binary key but we need to make sure that certain characters are not
347 used because the EncFS utility reads it from stdin and obviously
348 acts on some of the characters. This we replace CR (in case of an
349 MSDOS version of EncFS), LF (the delimiter used by EncFS) and Nul
350 (because it is unlikely to work). We use 32 bytes (256 bit)
351 because that is sufficient for the largest cipher (AES-256) and in
352 addition gives enough margin for a possible entropy degradation by
353 the KDF. */
354 gpg_error_t
355 be_encfs_create_new_keys (membuf_t *mb)
357 char *buffer;
358 int i, j;
360 /* Allocate a buffer of 32 bytes plus 8 spare bytes we may need to
361 replace the unwanted values. */
362 buffer = xtrymalloc_secure (32+8);
363 if (!buffer)
364 return gpg_error_from_syserror ();
366 /* Randomize the buffer. STRONG random should be enough as it is a
367 good compromise between security and performance. The
368 anticipated usage of this tool is the quite often creation of new
369 containers and thus this should not deplete the system's entropy
370 tool too much. */
371 gcry_randomize (buffer, 32+8, GCRY_STRONG_RANDOM);
372 for (i=j=0; i < 32; i++)
374 if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == 0 )
376 /* Replace. */
377 if (j == 8)
379 /* Need to get more random. */
380 gcry_randomize (buffer+32, 8, GCRY_STRONG_RANDOM);
381 j = 0;
383 buffer[i] = buffer[32+j];
384 j++;
388 /* Store the key. */
389 append_tuple (mb, KEYBLOB_TAG_ENCKEY, buffer, 32);
391 /* Free the temporary buffer. */
392 wipememory (buffer, 32+8); /* A failsafe extra wiping. */
393 xfree (buffer);
395 return 0;
399 /* Create the container described by the filename FNAME and the keyblob
400 information in TUPLES. */
401 gpg_error_t
402 be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples)
404 gpg_error_t err;
405 int dummy;
406 char *containername = NULL;
407 char *mountpoint = NULL;
409 err = be_encfs_get_detached_name (fname, &containername, &dummy);
410 if (err)
411 goto leave;
413 mountpoint = xtrystrdup ("/tmp/.#g13_XXXXXX");
414 if (!mountpoint)
416 err = gpg_error_from_syserror ();
417 goto leave;
419 if (!mkdtemp (mountpoint))
421 err = gpg_error_from_syserror ();
422 log_error (_("can't create directory `%s': %s\n"),
423 "/tmp/g13-XXXXXX", gpg_strerror (err));
424 goto leave;
427 err = run_encfs_tool (ctrl, ENCFS_CMD_CREATE, containername, mountpoint,
428 tuples);
430 /* In any case remove the temporary mount point. */
431 if (rmdir (mountpoint))
432 log_error ("error removing temporary mount point `%s': %s\n",
433 mountpoint, gpg_strerror (gpg_error_from_syserror ()));
436 leave:
437 xfree (containername);
438 xfree (mountpoint);
439 return err;
443 /* Mount the container described by the filename FNAME and the keyblob
444 information in TUPLES. */
445 gpg_error_t
446 be_encfs_mount_container (ctrl_t ctrl,
447 const char *fname, const char *mountpoint,
448 tupledesc_t tuples)
450 gpg_error_t err;
451 int dummy;
452 char *containername = NULL;
454 if (!mountpoint)
456 log_error ("the encfs backend requires an explicit mountpoint\n");
457 err = gpg_error (GPG_ERR_NOT_SUPPORTED);
458 goto leave;
461 err = be_encfs_get_detached_name (fname, &containername, &dummy);
462 if (err)
463 goto leave;
465 err = run_encfs_tool (ctrl, ENCFS_CMD_MOUNT, containername, mountpoint,
466 tuples);
468 leave:
469 xfree (containername);
470 return err;