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";
267 argv
[idx
++] = "--stdinpass";
268 argv
[idx
++] = "--annotate";
269 argv
[idx
++] = rawdir
;
270 argv
[idx
++] = mountpoint
;
272 assert (idx
<= DIM (argv
));
274 err
= gnupg_spawn_process_fd (pgmname
, argv
,
275 outbound
[0], -1, inbound
[1], &pid
);
278 log_error ("error spawning `%s': %s\n", pgmname
, gpg_strerror (err
));
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
);
298 log_info ("running `%s' in the background\n", pgmname
);
301 if (inbound
[0] != -1)
303 if (inbound
[1] != -1)
305 if (outbound
[0] != -1)
307 if (outbound
[1] != -1)
309 if (pid
!= (pid_t
)(-1))
311 gnupg_wait_process (pgmname
, pid
, NULL
);
313 runner_release (runner
);
314 encfs_handler_cleanup (parm
);
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
326 be_encfs_get_detached_name (const char *fname
, char **r_name
, int *r_isdir
)
330 if (!fname
|| !*fname
)
331 return gpg_error (GPG_ERR_INV_ARG
);
333 result
= strconcat (fname
, ".d", NULL
);
335 return gpg_error_from_syserror ();
342 /* Create a new session key and append it as a tuple to the memory
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
356 be_encfs_create_new_keys (membuf_t
*mb
)
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);
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
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 )
380 /* Need to get more random. */
381 gcry_randomize (buffer
+32, 8, GCRY_STRONG_RANDOM
);
384 buffer
[i
] = buffer
[32+j
];
390 append_tuple (mb
, KEYBLOB_TAG_ENCKEY
, buffer
, 32);
392 /* Free the temporary buffer. */
393 wipememory (buffer
, 32+8); /* A failsafe extra wiping. */
400 /* Create the container described by the filename FNAME and the keyblob
401 information in TUPLES. */
403 be_encfs_create_container (ctrl_t ctrl
, const char *fname
, tupledesc_t tuples
)
407 char *containername
= NULL
;
408 char *mountpoint
= NULL
;
410 err
= be_encfs_get_detached_name (fname
, &containername
, &dummy
);
414 mountpoint
= xtrystrdup ("/tmp/.#g13_XXXXXX");
417 err
= gpg_error_from_syserror ();
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
));
428 err
= run_encfs_tool (ctrl
, ENCFS_CMD_CREATE
, containername
, mountpoint
,
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 ()));
438 xfree (containername
);
444 /* Mount the container described by the filename FNAME and the keyblob
445 information in TUPLES. */
447 be_encfs_mount_container (ctrl_t ctrl
,
448 const char *fname
, const char *mountpoint
,
453 char *containername
= NULL
;
457 log_error ("the encfs backend requires an explicit mountpoint\n");
458 err
= gpg_error (GPG_ERR_NOT_SUPPORTED
);
462 err
= be_encfs_get_detached_name (fname
, &containername
, &dummy
);
466 err
= run_encfs_tool (ctrl
, ENCFS_CMD_MOUNT
, containername
, mountpoint
,
470 xfree (containername
);