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/>.
33 #include "../common/exechelp.h"
36 /* Command values used to run the encfs tool. */
45 /* An object to keep the private state of the encfs tool. It is
46 released by encfs_handler_cleanup. */
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
;
57 send_cmd_bin (runner_t runner
, const void *data
, size_t datalen
)
59 return runner_send_line (runner
, data
, datalen
);
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
));
73 run_umount_helper (const char *mountpoint
)
76 const char pgmname
[] = "/usr/bin/fusermount";
83 err
= gnupg_spawn_process_detached (pgmname
, args
, NULL
);
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. */
93 handle_status_line (runner_t runner
, const char *line
,
94 enum encfs_cmds cmd
, tupledesc_t tuples
)
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
);
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"))
130 value
= find_tuple (tuples
, KEYBLOB_TAG_ENCKEY
, &n
);
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
);
141 err
= gpg_error (GPG_ERR_NO_DATA
);
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
);
155 /* The main processing function as used by the runner. */
157 encfs_handler (void *opaque
, runner_t runner
, const char *status_line
)
159 encfs_parm_t parm
= opaque
;
162 if (!parm
|| !runner
)
163 return gpg_error (GPG_ERR_BUG
);
166 /* Runner requested internal flushing - nothing to do here. */
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
)
175 /* No more need for the tuples. */
176 destroy_tupledesc (parm
->tuples
);
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
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
188 runner_cancel (runner
);
196 /* Called by the runner to cleanup the private data. */
198 encfs_handler_cleanup (void *opaque
)
200 encfs_parm_t parm
= opaque
;
205 destroy_tupledesc (parm
->tuples
);
206 xfree (parm
->mountpoint
);
211 /* Run the encfs tool. */
213 run_encfs_tool (ctrl_t ctrl
, enum encfs_cmds cmd
,
214 const char *rawdir
, const char *mountpoint
, tupledesc_t tuples
)
218 runner_t runner
= NULL
;
219 int outbound
[2] = { -1, -1 };
220 int inbound
[2] = { -1, -1 };
222 const char *argv
[10];
223 pid_t pid
= (pid_t
)(-1);
228 parm
= xtrycalloc (1, sizeof *parm
);
231 err
= gpg_error_from_syserror ();
235 parm
->tuples
= ref_tupledesc (tuples
);
236 parm
->mountpoint
= xtrystrdup (mountpoint
);
237 if (!parm
->mountpoint
)
239 err
= gpg_error_from_syserror ();
244 static int namecounter
;
247 snprintf (buffer
, sizeof buffer
, "encfs-%d", ++namecounter
);
248 err
= runner_new (&runner
, buffer
);
253 err
= gnupg_create_inbound_pipe (inbound
);
255 err
= gnupg_create_outbound_pipe (outbound
);
258 log_error (_("error creating a pipe: %s\n"), gpg_strerror (err
));
262 pgmname
= "/usr/bin/encfs";
266 argv
[idx
++] = "--stdinpass";
267 argv
[idx
++] = "--annotate";
268 argv
[idx
++] = rawdir
;
269 argv
[idx
++] = mountpoint
;
271 assert (idx
<= DIM (argv
));
273 err
= gnupg_spawn_process_fd (pgmname
, argv
,
274 outbound
[0], -1, inbound
[1], &pid
);
277 log_error ("error spawning `%s': %s\n", pgmname
, gpg_strerror (err
));
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
);
297 log_info ("running `%s' in the background\n", pgmname
);
300 if (inbound
[0] != -1)
302 if (inbound
[1] != -1)
304 if (outbound
[0] != -1)
306 if (outbound
[1] != -1)
308 if (pid
!= (pid_t
)(-1))
310 gnupg_wait_process (pgmname
, pid
, NULL
);
312 runner_release (runner
);
313 encfs_handler_cleanup (parm
);
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
325 be_encfs_get_detached_name (const char *fname
, char **r_name
, int *r_isdir
)
329 if (!fname
|| !*fname
)
330 return gpg_error (GPG_ERR_INV_ARG
);
332 result
= strconcat (fname
, ".d", NULL
);
334 return gpg_error_from_syserror ();
341 /* Create a new session key and append it as a tuple to the memory
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
355 be_encfs_create_new_keys (membuf_t
*mb
)
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);
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
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 )
379 /* Need to get more random. */
380 gcry_randomize (buffer
+32, 8, GCRY_STRONG_RANDOM
);
383 buffer
[i
] = buffer
[32+j
];
389 append_tuple (mb
, KEYBLOB_TAG_ENCKEY
, buffer
, 32);
391 /* Free the temporary buffer. */
392 wipememory (buffer
, 32+8); /* A failsafe extra wiping. */
399 /* Create the container described by the filename FNAME and the keyblob
400 information in TUPLES. */
402 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 (!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. */
446 be_encfs_mount_container (ctrl_t ctrl
,
447 const char *fname
, const char *mountpoint
,
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
);