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 <https://www.gnu.org/licenses/>.
33 #include "../common/sysutils.h"
34 #include "../common/exechelp.h"
37 /* Command values used to run the encfs tool. */
46 /* An object to keep the private state of the encfs tool. It is
47 released by encfs_handler_cleanup. */
50 enum encfs_cmds cmd; /* The current command. */
51 tupledesc_t tuples; /* NULL or the tuples object. */
52 char *mountpoint; /* The mountpoint. */
54 typedef struct encfs_parm_s *encfs_parm_t;
58 send_cmd_bin (runner_t runner, const void *data, size_t datalen)
60 return runner_send_line (runner, data, datalen);
65 send_cmd (runner_t runner, const char *string)
67 log_debug ("sending command -->%s<--\n", string);
68 return send_cmd_bin (runner, string, strlen (string));
74 run_umount_helper (const char *mountpoint)
77 const char pgmname[] = FUSERMOUNT;
84 err = gnupg_spawn_process_detached (pgmname, args, NULL);
86 log_error ("failed to run '%s': %s\n",
87 pgmname, gpg_strerror (err));
91 /* Handle one line of the encfs tool's output. This function is
92 allowed to modify the content of BUFFER. */
94 handle_status_line (runner_t runner, const char *line,
95 enum encfs_cmds cmd, tupledesc_t tuples)
99 /* Check that encfs understands our new options. */
100 if (!strncmp (line, "$STATUS$", 8))
102 for (line +=8; *line && spacep (line); line++)
104 log_info ("got status '%s'\n", line);
105 if (!strcmp (line, "fuse_main_start"))
107 /* Send a special error code back to let the caller know
108 that everything has been setup by encfs. */
109 err = gpg_error (GPG_ERR_UNFINISHED);
114 else if (!strncmp (line, "$PROMPT$", 8))
116 for (line +=8; *line && spacep (line); line++)
118 log_info ("got prompt '%s'\n", line);
119 if (!strcmp (line, "create_root_dir"))
120 err = send_cmd (runner, cmd == ENCFS_CMD_CREATE? "y":"n");
121 else if (!strcmp (line, "create_mount_point"))
122 err = send_cmd (runner, "y");
123 else if (!strcmp (line, "passwd")
124 || !strcmp (line, "new_passwd"))
131 value = find_tuple (tuples, KEYBLOB_TAG_ENCKEY, &n);
133 err = gpg_error (GPG_ERR_INV_SESSION_KEY);
134 else if ((err = send_cmd_bin (runner, value, n)))
136 if (gpg_err_code (err) == GPG_ERR_BUG
137 && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
138 err = gpg_error (GPG_ERR_INV_SESSION_KEY);
142 err = gpg_error (GPG_ERR_NO_DATA);
145 err = send_cmd (runner, ""); /* Default to send an empty line. */
147 else if (strstr (line, "encfs: unrecognized option '"))
148 err = gpg_error (GPG_ERR_INV_ENGINE);
156 /* The main processing function as used by the runner. */
158 encfs_handler (void *opaque, runner_t runner, const char *status_line)
160 encfs_parm_t parm = opaque;
163 if (!parm || !runner)
164 return gpg_error (GPG_ERR_BUG);
167 /* Runner requested internal flushing - nothing to do here. */
171 err = handle_status_line (runner, status_line, parm->cmd, parm->tuples);
172 if (gpg_err_code (err) == GPG_ERR_UNFINISHED
173 && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
176 /* No more need for the tuples. */
177 destroy_tupledesc (parm->tuples);
180 if (parm->cmd == ENCFS_CMD_CREATE)
182 /* The encfs tool keeps on running after creation of the
183 container. We don't want that and thus need to stop the
185 run_umount_helper (parm->mountpoint);
186 /* In case the umount helper does not work we try to kill
187 the engine. FIXME: We should figure out how to make
189 runner_cancel (runner);
197 /* Called by the runner to cleanup the private data. */
199 encfs_handler_cleanup (void *opaque)
201 encfs_parm_t parm = opaque;
206 destroy_tupledesc (parm->tuples);
207 xfree (parm->mountpoint);
212 /* Run the encfs tool. */
214 run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
215 const char *rawdir, const char *mountpoint, tupledesc_t tuples,
220 runner_t runner = NULL;
221 int outbound[2] = { -1, -1 };
222 int inbound[2] = { -1, -1 };
224 const char *argv[10];
225 pid_t pid = (pid_t)(-1);
230 parm = xtrycalloc (1, sizeof *parm);
233 err = gpg_error_from_syserror ();
237 parm->tuples = ref_tupledesc (tuples);
238 parm->mountpoint = xtrystrdup (mountpoint);
239 if (!parm->mountpoint)
241 err = gpg_error_from_syserror ();
245 err = runner_new (&runner, "encfs");
249 err = gnupg_create_inbound_pipe (inbound, NULL, 0);
251 err = gnupg_create_outbound_pipe (outbound, NULL, 0);
254 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
263 argv[idx++] = "--stdinpass";
264 argv[idx++] = "--annotate";
265 argv[idx++] = rawdir;
266 argv[idx++] = mountpoint;
268 assert (idx <= DIM (argv));
270 err = gnupg_spawn_process_fd (pgmname, argv,
271 outbound[0], -1, inbound[1], &pid);
274 log_error ("error spawning '%s': %s\n", pgmname, gpg_strerror (err));
277 close (outbound[0]); outbound[0] = -1;
278 close ( inbound[1]); inbound[1] = -1;
280 runner_set_fds (runner, inbound[0], outbound[1]);
281 inbound[0] = -1; /* Now owned by RUNNER. */
282 outbound[1] = -1; /* Now owned by RUNNER. */
284 runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm);
285 parm = NULL; /* Now owned by RUNNER. */
287 runner_set_pid (runner, pid);
288 pid = (pid_t)(-1); /* The process is now owned by RUNNER. */
290 err = runner_spawn (runner);
294 *r_id = runner_get_rid (runner);
295 log_info ("running '%s' in the background\n", pgmname);
298 if (inbound[0] != -1)
300 if (inbound[1] != -1)
302 if (outbound[0] != -1)
304 if (outbound[1] != -1)
306 if (pid != (pid_t)(-1))
308 gnupg_wait_process (pgmname, pid, 1, NULL);
309 gnupg_release_process (pid);
311 runner_release (runner);
312 encfs_handler_cleanup (parm);
320 /* See be_get_detached_name for a description. Note that the
321 dispatcher code makes sure that NULL is stored at R_NAME before
324 be_encfs_get_detached_name (const char *fname, char **r_name, int *r_isdir)
328 if (!fname || !*fname)
329 return gpg_error (GPG_ERR_INV_ARG);
331 result = strconcat (fname, ".d", NULL);
333 return gpg_error_from_syserror ();
340 /* Create a new session key and append it as a tuple to the memory
343 The EncFS daemon takes a passphrase from stdin and internally
344 mangles it by means of some KDF from OpenSSL. We want to store a
345 binary key but we need to make sure that certain characters are not
346 used because the EncFS utility reads it from stdin and obviously
347 acts on some of the characters. This we replace CR (in case of an
348 MSDOS version of EncFS), LF (the delimiter used by EncFS) and Nul
349 (because it is unlikely to work). We use 32 bytes (256 bit)
350 because that is sufficient for the largest cipher (AES-256) and in
351 addition gives enough margin for a possible entropy degradation by
354 be_encfs_create_new_keys (membuf_t *mb)
359 /* Allocate a buffer of 32 bytes plus 8 spare bytes we may need to
360 replace the unwanted values. */
361 buffer = xtrymalloc_secure (32+8);
363 return gpg_error_from_syserror ();
365 /* Randomize the buffer. STRONG random should be enough as it is a
366 good compromise between security and performance. The
367 anticipated usage of this tool is the quite often creation of new
368 containers and thus this should not deplete the system's entropy
370 gcry_randomize (buffer, 32+8, GCRY_STRONG_RANDOM);
371 for (i=j=0; i < 32; i++)
373 if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == 0 )
378 /* Need to get more random. */
379 gcry_randomize (buffer+32, 8, GCRY_STRONG_RANDOM);
382 buffer[i] = buffer[32+j];
388 append_tuple (mb, KEYBLOB_TAG_ENCKEY, buffer, 32);
390 /* Free the temporary buffer. */
391 wipememory (buffer, 32+8); /* A failsafe extra wiping. */
398 /* Create the container described by the filename FNAME and the keyblob
399 information in TUPLES. */
401 be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples,
406 char *containername = NULL;
407 char *mountpoint = NULL;
409 err = be_encfs_get_detached_name (fname, &containername, &dummy);
413 mountpoint = xtrystrdup ("/tmp/.#g13_XXXXXX");
416 err = gpg_error_from_syserror ();
419 if (!gnupg_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));
427 err = run_encfs_tool (ctrl, ENCFS_CMD_CREATE, containername, mountpoint,
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 ()));
437 xfree (containername);
443 /* Mount the container described by the filename FNAME and the keyblob
444 information in TUPLES. On success the runner id is stored at R_ID. */
446 be_encfs_mount_container (ctrl_t ctrl,
447 const char *fname, const char *mountpoint,
448 tupledesc_t tuples, unsigned int *r_id)
452 char *containername = NULL;
456 log_error ("the encfs backend requires an explicit mountpoint\n");
457 err = gpg_error (GPG_ERR_NOT_SUPPORTED);
461 err = be_encfs_get_detached_name (fname, &containername, &dummy);
465 err = run_encfs_tool (ctrl, ENCFS_CMD_MOUNT, containername, mountpoint,
469 xfree (containername);