summaryrefslogtreecommitdiffstats
path: root/g13
diff options
context:
space:
mode:
authorWerner Koch <wk@gnupg.org>2009-10-13 21:17:24 +0200
committerWerner Koch <wk@gnupg.org>2009-10-13 21:17:24 +0200
commit536b6ab09fa3e17f955c8b55e8469f3265a1936f (patch)
treea06fba4fb448cc70de12a470d7dde7f22c3eaf8f /g13
parentReplace C99 style vararg macro which was anyway not correct. (diff)
downloadgnupg2-536b6ab09fa3e17f955c8b55e8469f3265a1936f.tar.xz
gnupg2-536b6ab09fa3e17f955c8b55e8469f3265a1936f.zip
Keep on hacking on g13. A simple --create and --mount does now work.
A hacked up encfs is required.
Diffstat (limited to 'g13')
-rw-r--r--g13/Makefile.am2
-rw-r--r--g13/backend.c50
-rw-r--r--g13/backend.h10
-rw-r--r--g13/be-encfs.c413
-rw-r--r--g13/be-encfs.h9
-rw-r--r--g13/call-gpg.c132
-rw-r--r--g13/call-gpg.h2
-rw-r--r--g13/create.c28
-rw-r--r--g13/create.h2
-rw-r--r--g13/g13.c127
-rw-r--r--g13/keyblob.h5
-rw-r--r--g13/mount.c303
-rw-r--r--g13/mount.h29
-rw-r--r--g13/runner.c444
-rw-r--r--g13/runner.h68
-rw-r--r--g13/utils.c129
-rw-r--r--g13/utils.h15
17 files changed, 1750 insertions, 18 deletions
diff --git a/g13/Makefile.am b/g13/Makefile.am
index 44f546eb7..2108f562a 100644
--- a/g13/Makefile.am
+++ b/g13/Makefile.am
@@ -31,7 +31,9 @@ g13_SOURCES = \
keyblob.h \
utils.c utils.h \
create.c create.h \
+ mount.c mount.h \
call-gpg.c call-gpg.h \
+ runner.c runner.h \
backend.c backend.h \
be-encfs.c be-encfs.h \
be-truecrypt.c be-truecrypt.h
diff --git a/g13/backend.c b/g13/backend.c
index a6f38719a..08aec324f 100644
--- a/g13/backend.c
+++ b/g13/backend.c
@@ -41,6 +41,22 @@ no_such_backend (int conttype)
}
+/* Return true if CONTTYPE is supported by us. */
+int
+be_is_supported_conttype (int conttype)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+
/* If the backend requires a separate file or directory for the
container, return its name by computing it from FNAME which gives
the g13 filename. The new file name is allocated and stored at
@@ -81,3 +97,37 @@ be_create_new_keys (int conttype, membuf_t *mb)
}
}
+
+/* Dispatcher to the backend's create function. */
+gpg_error_t
+be_create_container (ctrl_t ctrl, int conttype,
+ const char *fname, int fd, tupledesc_t tuples)
+{
+ (void)fd; /* Not yet used. */
+
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return be_encfs_create_container (ctrl, fname, tuples);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+/* Dispatcher to the backend's mount function. */
+gpg_error_t
+be_mount_container (ctrl_t ctrl, int conttype,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return be_encfs_mount_container (ctrl, fname, mountpoint, tuples);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
diff --git a/g13/backend.h b/g13/backend.h
index ffd03d3f5..7cdde9e4b 100644
--- a/g13/backend.h
+++ b/g13/backend.h
@@ -21,12 +21,20 @@
#define G13_BACKEND_H
#include "../common/membuf.h"
+#include "utils.h" /* For tupledesc_t */
-
+int be_is_supported_conttype (int conttype);
gpg_error_t be_get_detached_name (int conttype, const char *fname,
char **r_name, int *r_isdir);
gpg_error_t be_create_new_keys (int conttype, membuf_t *mb);
+gpg_error_t be_create_container (ctrl_t ctrl, int conttype,
+ const char *fname, int fd,
+ tupledesc_t tuples);
+gpg_error_t be_mount_container (ctrl_t ctrl, int conttype,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples);
+
#endif /*G13_BACKEND_H*/
diff --git a/g13/be-encfs.c b/g13/be-encfs.c
index 18030b80e..0f7ec73e6 100644
--- a/g13/be-encfs.c
+++ b/g13/be-encfs.c
@@ -23,12 +23,301 @@
#include <string.h>
#include <errno.h>
#include <unistd.h>
+#include <assert.h>
#include "g13.h"
#include "i18n.h"
#include "keyblob.h"
#include "be-encfs.h"
+#include "runner.h"
+#include "../common/exechelp.h"
+
+/* Command values used to run the encfs tool. */
+enum encfs_cmds
+ {
+ ENCFS_CMD_CREATE,
+ ENCFS_CMD_MOUNT,
+ ENCFS_CMD_UMOUNT
+ };
+
+
+/* An object to keep the private state of the encfs tool. It is
+ released by encfs_handler_cleanup. */
+struct encfs_parm_s
+{
+ enum encfs_cmds cmd; /* The current command. */
+ tupledesc_t tuples; /* NULL or the tuples object. */
+ char *mountpoint; /* The mountpoint. */
+};
+typedef struct encfs_parm_s *encfs_parm_t;
+
+
+static gpg_error_t
+send_cmd_bin (runner_t runner, const void *data, size_t datalen)
+{
+ return runner_send_line (runner, data, datalen);
+}
+
+
+static gpg_error_t
+send_cmd (runner_t runner, const char *string)
+{
+ log_debug ("sending command -->%s<--\n", string);
+ return send_cmd_bin (runner, string, strlen (string));
+}
+
+
+
+static void
+run_umount_helper (const char *mountpoint)
+{
+ gpg_error_t err;
+ const char pgmname[] = "/usr/bin/fusermount";
+ const char *args[3];
+
+ args[0] = "-u";
+ args[1] = mountpoint;
+ args[2] = NULL;
+
+ err = gnupg_spawn_process_detached (pgmname, args, NULL);
+ if (err)
+ log_error ("failed to run `%s': %s\n",
+ pgmname, gpg_strerror (err));
+}
+
+
+/* Handle one line of the encfs tool's output. This function is
+ allowed to modify the content of BUFFER. */
+static gpg_error_t
+handle_status_line (runner_t runner, const char *line,
+ enum encfs_cmds cmd, tupledesc_t tuples)
+{
+ gpg_error_t err;
+
+ /* Check that encfs understands our new options. */
+ if (!strncmp (line, "$STATUS$", 8))
+ {
+ for (line +=8; *line && spacep (line); line++)
+ ;
+ log_info ("got status `%s'\n", line);
+ if (!strcmp (line, "fuse_main_start"))
+ {
+ /* Send a special error code back to let the caller know
+ that everything has been setup by encfs. */
+ err = gpg_error (GPG_ERR_UNFINISHED);
+ }
+ else
+ err = 0;
+ }
+ else if (!strncmp (line, "$PROMPT$", 8))
+ {
+ for (line +=8; *line && spacep (line); line++)
+ ;
+ log_info ("got prompt `%s'\n", line);
+ if (!strcmp (line, "create_root_dir"))
+ err = send_cmd (runner, cmd == ENCFS_CMD_CREATE? "y":"n");
+ else if (!strcmp (line, "create_mount_point"))
+ err = send_cmd (runner, "y");
+ else if (!strcmp (line, "passwd")
+ || !strcmp (line, "new_passwd"))
+ {
+ if (tuples)
+ {
+ size_t n;
+ const void *value;
+
+ value = find_tuple (tuples, KEYBLOB_TAG_ENCKEY, &n);
+ if (!value)
+ err = gpg_error (GPG_ERR_INV_SESSION_KEY);
+ else if ((err = send_cmd_bin (runner, value, n)))
+ {
+ if (gpg_err_code (err) == GPG_ERR_BUG
+ && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+ err = gpg_error (GPG_ERR_INV_SESSION_KEY);
+ }
+ }
+ else
+ err = gpg_error (GPG_ERR_NO_DATA);
+ }
+ else
+ err = send_cmd (runner, ""); /* Default to send an empty line. */
+ }
+ else if (strstr (line, "encfs: unrecognized option '"))
+ err = gpg_error (GPG_ERR_INV_ENGINE);
+ else
+ err = 0;
+
+ return err;
+}
+
+
+/* The main processing function as used by the runner. */
+static gpg_error_t
+encfs_handler (void *opaque, runner_t runner, const char *status_line)
+{
+ encfs_parm_t parm = opaque;
+ gpg_error_t err;
+
+ if (!parm || !runner)
+ return gpg_error (GPG_ERR_BUG);
+ if (!status_line)
+ {
+ /* Runner requested internal flushing - nothing to do here. */
+ return 0;
+ }
+
+ err = handle_status_line (runner, status_line, parm->cmd, parm->tuples);
+ if (gpg_err_code (err) == GPG_ERR_UNFINISHED
+ && gpg_err_source (err) == GPG_ERR_SOURCE_DEFAULT)
+ {
+ err = 0;
+ /* No more need for the tuples. */
+ destroy_tupledesc (parm->tuples);
+ parm->tuples = NULL;
+
+ if (parm->cmd == ENCFS_CMD_CREATE)
+ {
+ /* The encfs tool keeps on running after creation of the
+ container. We don't want that and thus need to stop the
+ encfs process. */
+ run_umount_helper (parm->mountpoint);
+ /* In case the umount helper does not work we try to kill
+ the engine. FIXME: We should figure out how to make
+ fusermount work. */
+ runner_cancel (runner);
+ }
+ }
+
+ return err;
+}
+
+
+/* Called by the runner to cleanup the private data. */
+static void
+encfs_handler_cleanup (void *opaque)
+{
+ encfs_parm_t parm = opaque;
+
+ if (!parm)
+ return;
+
+ destroy_tupledesc (parm->tuples);
+ xfree (parm->mountpoint);
+ xfree (parm);
+}
+
+
+/* Run the encfs tool. */
+static gpg_error_t
+run_encfs_tool (ctrl_t ctrl, enum encfs_cmds cmd,
+ const char *rawdir, const char *mountpoint, tupledesc_t tuples)
+{
+ gpg_error_t err;
+ encfs_parm_t parm;
+ runner_t runner = NULL;
+ int outbound[2] = { -1, -1 };
+ int inbound[2] = { -1, -1 };
+ const char *pgmname;
+ const char *argv[10];
+ pid_t pid = (pid_t)(-1);
+ int idx;
+
+ (void)ctrl;
+
+ parm = xtrycalloc (1, sizeof *parm);
+ if (!parm)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ parm->cmd = cmd;
+ parm->tuples = ref_tupledesc (tuples);
+ parm->mountpoint = xtrystrdup (mountpoint);
+ if (!parm->mountpoint)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ {
+ static int namecounter;
+ char buffer[50];
+
+ snprintf (buffer, sizeof buffer, "encfs-%d", ++namecounter);
+ err = runner_new (&runner, buffer);
+ if (err)
+ goto leave;
+ }
+
+ err = gnupg_create_inbound_pipe (inbound);
+ if (!err)
+ err = gnupg_create_outbound_pipe (outbound);
+ if (err)
+ {
+ log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ pgmname = "/usr/bin/encfs";
+ idx = 0;
+ argv[idx++] = "-f";
+ argv[idx++] = "-v";
+ argv[idx++] = "--stdinpass";
+ argv[idx++] = "--annotate";
+ argv[idx++] = rawdir;
+ argv[idx++] = mountpoint;
+ argv[idx++] = NULL;
+ assert (idx <= DIM (argv));
+
+ err = gnupg_spawn_process_fd (pgmname, argv,
+ outbound[0], -1, inbound[1], &pid);
+ if (err)
+ {
+ log_error ("error spawning `%s': %s\n", pgmname, gpg_strerror (err));
+ goto leave;
+ }
+ close (outbound[0]); outbound[0] = -1;
+ close ( inbound[1]); inbound[1] = -1;
+
+ runner_set_fds (runner, inbound[0], outbound[1]);
+ inbound[0] = -1; /* Now owned by RUNNER. */
+ outbound[1] = -1; /* Now owned by RUNNER. */
+
+ runner_set_handler (runner, encfs_handler, encfs_handler_cleanup, parm);
+ parm = NULL; /* Now owned by RUNNER. */
+
+ runner_set_pid (runner, pid);
+ pid = (pid_t)(-1); /* The process is now owned by RUNNER. */
+
+ err = runner_spawn (runner);
+ if (err)
+ goto leave;
+
+ log_info ("running `%s' in the background\n", pgmname);
+
+ leave:
+ if (inbound[0] != -1)
+ close (inbound[0]);
+ if (inbound[1] != -1)
+ close (inbound[1]);
+ if (outbound[0] != -1)
+ close (outbound[0]);
+ if (outbound[1] != -1)
+ close (outbound[1]);
+ if (pid != (pid_t)(-1))
+ {
+ gnupg_wait_process (pgmname, pid, NULL);
+ }
+ runner_release (runner);
+ encfs_handler_cleanup (parm);
+ return err;
+}
+
+
+
+
+
/* See be_get_detached_name for a description. Note that the
dispatcher code makes sure that NULL is stored at R_NAME before
calling us. */
@@ -49,10 +338,134 @@ be_encfs_get_detached_name (const char *fname, char **r_name, int *r_isdir)
}
+/* Create a new session key and append it as a tuple to the memory
+ buffer MB.
+
+ The EncFS daemon takes a passphrase from stdin and internally
+ mangles it by means of some KDF from OpenSSL. We want to store a
+ binary key but we need to make sure that certain characters are not
+ used because the EncFS utility reads it from stdin and obviously
+ acts on some of the characters. This we replace CR (in case of an
+ MSDOS version of EncFS), LF (the delimiter used by EncFS) and Nul
+ (because it is unlikely to work). We use 32 bytes (256 bit)
+ because that is sufficient for the largest cipher (AES-256) and in
+ addition gives enough margin for a possible entropy degradation by
+ the KDF. */
gpg_error_t
be_encfs_create_new_keys (membuf_t *mb)
{
+ char *buffer;
+ int i, j;
+
+ /* Allocate a buffer of 32 bytes plus 8 spare bytes we may need to
+ replace the unwanted values. */
+ buffer = xtrymalloc_secure (32+8);
+ if (!buffer)
+ return gpg_error_from_syserror ();
+
+ /* Randomize the buffer. STRONG random should be enough as it is a
+ good compromise between security and performance. The
+ anticipated usage of this tool is the quite often creation of new
+ containers and thus this should not deplete the system's entropy
+ tool too much. */
+ gcry_randomize (buffer, 32+8, GCRY_STRONG_RANDOM);
+ for (i=j=0; i < 32; i++)
+ {
+ if (buffer[i] == '\r' || buffer[i] == '\n' || buffer[i] == 0 )
+ {
+ /* Replace. */
+ if (j == 8)
+ {
+ /* Need to get more random. */
+ gcry_randomize (buffer+32, 8, GCRY_STRONG_RANDOM);
+ j = 0;
+ }
+ buffer[i] = buffer[32+j];
+ j++;
+ }
+ }
+
+ /* Store the key. */
+ append_tuple (mb, KEYBLOB_TAG_ENCKEY, buffer, 32);
+
+ /* Free the temporary buffer. */
+ wipememory (buffer, 32+8); /* A failsafe extra wiping. */
+ xfree (buffer);
+
return 0;
}
+/* Create the container described by the filename FNAME and the keyblob
+ information in TUPLES. */
+gpg_error_t
+be_encfs_create_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples)
+{
+ gpg_error_t err;
+ int dummy;
+ char *containername = NULL;
+ char *mountpoint = NULL;
+
+ err = be_encfs_get_detached_name (fname, &containername, &dummy);
+ if (err)
+ goto leave;
+
+ mountpoint = xtrystrdup ("/tmp/.#g13_XXXXXX");
+ if (!mountpoint)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (!mkdtemp (mountpoint))
+ {
+ err = gpg_error_from_syserror ();
+ log_error (_("can't create directory `%s': %s\n"),
+ "/tmp/g13-XXXXXX", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = run_encfs_tool (ctrl, ENCFS_CMD_CREATE, containername, mountpoint,
+ tuples);
+
+ /* In any case remove the temporary mount point. */
+ if (rmdir (mountpoint))
+ log_error ("error removing temporary mount point `%s': %s\n",
+ mountpoint, gpg_strerror (gpg_error_from_syserror ()));
+
+
+ leave:
+ xfree (containername);
+ xfree (mountpoint);
+ return err;
+}
+
+
+/* Mount the container described by the filename FNAME and the keyblob
+ information in TUPLES. */
+gpg_error_t
+be_encfs_mount_container (ctrl_t ctrl,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples)
+{
+ gpg_error_t err;
+ int dummy;
+ char *containername = NULL;
+
+ if (!mountpoint)
+ {
+ log_error ("the encfs backend requires an explicit mountpoint\n");
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+
+ err = be_encfs_get_detached_name (fname, &containername, &dummy);
+ if (err)
+ goto leave;
+
+ err = run_encfs_tool (ctrl, ENCFS_CMD_MOUNT, containername, mountpoint,
+ tuples);
+
+ leave:
+ xfree (containername);
+ return err;
+}
diff --git a/g13/be-encfs.h b/g13/be-encfs.h
index 061385345..c6c8396c5 100644
--- a/g13/be-encfs.h
+++ b/g13/be-encfs.h
@@ -26,6 +26,15 @@ gpg_error_t be_encfs_get_detached_name (const char *fname,
char **r_name, int *r_isdir);
gpg_error_t be_encfs_create_new_keys (membuf_t *mb);
+gpg_error_t be_encfs_create_container (ctrl_t ctrl,
+ const char *fname,
+ tupledesc_t tuples);
+
+gpg_error_t be_encfs_mount_container (ctrl_t ctrl,
+ const char *fname,
+ const char *mountpoint,
+ tupledesc_t tuples);
+
#endif /*G13_BE_ENCFS_H*/
diff --git a/g13/call-gpg.c b/g13/call-gpg.c
index 2399058b0..dd519021e 100644
--- a/g13/call-gpg.c
+++ b/g13/call-gpg.c
@@ -43,7 +43,7 @@ start_gpg (ctrl_t ctrl, int input_fd, int output_fd, assuan_context_t *r_ctx)
gpg_error_t err;
assuan_context_t ctx = NULL;
const char *pgmname;
- const char *argv[6];
+ const char *argv[7];
int no_close_list[5];
int i;
char line[ASSUAN_LINELENGTH];
@@ -464,3 +464,133 @@ gpg_encrypt_blob (ctrl_t ctrl, const void *plain, size_t plainlen,
}
+
+/* Call GPG to decrypt a block of data.
+
+
+ */
+gpg_error_t
+gpg_decrypt_blob (ctrl_t ctrl, const void *ciph, size_t ciphlen,
+ void **r_plain, size_t *r_plainlen)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ int outbound_fds[2] = { -1, -1 };
+ int inbound_fds[2] = { -1, -1 };
+ pth_t writer_tid = NULL;
+ pth_t reader_tid = NULL;
+ gpg_error_t writer_err, reader_err;
+ membuf_t reader_mb;
+
+ *r_plain = NULL;
+ *r_plainlen = 0;
+
+ /* Init the memory buffer to receive the encrypted stuff. */
+ init_membuf_secure (&reader_mb, 1024);
+
+ /* Create two pipes. */
+ err = gnupg_create_outbound_pipe (outbound_fds);
+ if (!err)
+ err = gnupg_create_inbound_pipe (inbound_fds);
+ if (err)
+ {
+ log_error (_("error creating a pipe: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Start GPG and send the INPUT and OUTPUT commands. */
+ err = start_gpg (ctrl, outbound_fds[0], inbound_fds[1], &ctx);
+ if (err)
+ goto leave;
+ close (outbound_fds[0]); outbound_fds[0] = -1;
+ close (inbound_fds[1]); inbound_fds[1] = -1;
+
+ /* Start a writer thread to feed the INPUT command of the server. */
+ err = start_writer (outbound_fds[1], ciph, ciphlen,
+ &writer_tid, &writer_err);
+ if (err)
+ return err;
+ outbound_fds[1] = -1; /* The thread owns the FD now. */
+
+ /* Start a reader thread to eat from the OUTPUT command of the
+ server. */
+ err = start_reader (inbound_fds[0], &reader_mb,
+ &reader_tid, &reader_err);
+ if (err)
+ return err;
+ outbound_fds[0] = -1; /* The thread owns the FD now. */
+
+ /* Run the decryption. */
+ err = assuan_transact (ctx, "DECRYPT", NULL, NULL, NULL, NULL, NULL, NULL);
+ if (err)
+ {
+ log_error ("the engine's DECRYPT command failed: %s <%s>\n",
+ gpg_strerror (err), gpg_strsource (err));
+ goto leave;
+ }
+
+ /* Wait for reader and return the data. */
+ if (!pth_join (reader_tid, NULL))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("waiting for reader thread failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ reader_tid = NULL;
+ if (reader_err)
+ {
+ err = reader_err;
+ log_error ("read error in reader thread: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Wait for the writer to catch a writer error. */
+ if (!pth_join (writer_tid, NULL))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("waiting for writer thread failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ writer_tid = NULL;
+ if (writer_err)
+ {
+ err = writer_err;
+ log_error ("write error in writer thread: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Return the data. */
+ *r_plain = get_membuf (&reader_mb, r_plainlen);
+ if (!*r_plain)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error while storing the data in the reader thread: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+
+ leave:
+ if (reader_tid)
+ {
+ pth_cancel (reader_tid);
+ pth_join (reader_tid, NULL);
+ }
+ if (writer_tid)
+ {
+ pth_cancel (writer_tid);
+ pth_join (writer_tid, NULL);
+ }
+ if (outbound_fds[0] != -1)
+ close (outbound_fds[0]);
+ if (outbound_fds[1] != -1)
+ close (outbound_fds[1]);
+ if (inbound_fds[0] != -1)
+ close (inbound_fds[0]);
+ if (inbound_fds[1] != -1)
+ close (inbound_fds[1]);
+ release_gpg (ctx);
+ xfree (get_membuf (&reader_mb, NULL));
+ return err;
+}
+
+
diff --git a/g13/call-gpg.h b/g13/call-gpg.h
index 3e801be3b..ffc5f29b5 100644
--- a/g13/call-gpg.h
+++ b/g13/call-gpg.h
@@ -23,6 +23,8 @@
gpg_error_t gpg_encrypt_blob (ctrl_t ctrl,
const void *plain, size_t plainlen,
void **r_ciph, size_t *r_ciphlen);
+gpg_error_t gpg_decrypt_blob (ctrl_t ctrl, const void *ciph, size_t ciphlen,
+ void **r_plain, size_t *r_plainlen);
diff --git a/g13/create.c b/g13/create.c
index 0c6735b80..7f3a349b2 100644
--- a/g13/create.c
+++ b/g13/create.c
@@ -79,9 +79,9 @@ create_new_keyblob (ctrl_t ctrl, int is_detached,
if (err)
goto leave;
+ /* Just for testing. */
append_tuple (&mb, KEYBLOB_TAG_FILLER, "filler", 6);
-
*r_blob = get_membuf (&mb, r_bloblen);
if (!*r_blob)
{
@@ -122,7 +122,7 @@ encrypt_keyblob (ctrl_t ctrl, void *keyblob, size_t keybloblen,
appropriate header. This fucntion is called with a lock file in
place and after checking that the filename does not exists. */
static gpg_error_t
-write_keyblob (ctrl_t ctrl, const char *filename,
+write_keyblob (const char *filename,
const void *keyblob, size_t keybloblen)
{
gpg_error_t err;
@@ -152,7 +152,7 @@ write_keyblob (ctrl_t ctrl, const char *filename,
packet[4] = 0;
packet[5] = 26;
memcpy (packet+6, "GnuPG/G13", 10); /* Packet subtype. */
- packet[16] = 1; /* G13 packet format. */
+ packet[16] = 1; /* G13 packet format version. */
packet[17] = 0; /* Reserved. */
packet[18] = 0; /* Reserved. */
packet[19] = 0; /* OS Flag. */
@@ -202,7 +202,7 @@ write_keyblob (ctrl_t ctrl, const char *filename,
return err;
}
- return err;
+ return 0;
writeerr:
@@ -220,7 +220,7 @@ write_keyblob (ctrl_t ctrl, const char *filename,
using the current settings. If the file already exists an error is
returned. */
gpg_error_t
-create_new_container (ctrl_t ctrl, const char *filename)
+g13_create_container (ctrl_t ctrl, const char *filename)
{
gpg_error_t err;
dotlock_t lock;
@@ -230,6 +230,7 @@ create_new_container (ctrl_t ctrl, const char *filename)
size_t enckeybloblen;
char *detachedname = NULL;
int detachedisdir;
+ tupledesc_t tuples = NULL;
/* A quick check to see that no container with that name already
exists. */
@@ -286,17 +287,28 @@ create_new_container (ctrl_t ctrl, const char *filename)
&enckeyblob, &enckeybloblen);
if (err)
goto leave;
+
+ /* Put a copy of the keyblob into a tuple structure. */
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (err)
+ goto leave;
+ keyblob = NULL;
+ /* if (opt.verbose) */
+ /* dump_keyblob (tuples); */
/* Write out the header, the encrypted keyblob and some padding. */
- err = write_keyblob (ctrl, filename, enckeyblob, enckeybloblen);
+ err = write_keyblob (filename, enckeyblob, enckeybloblen);
if (err)
goto leave;
- /* Create and append the container. */
-
+ /* Create and append the container. FIXME: We should pass the
+ estream object in addition to the filename, so that the backend
+ can append the container to the g13 file. */
+ err = be_create_container (ctrl, ctrl->conttype, filename, -1, tuples);
leave:
+ destroy_tupledesc (tuples);
xfree (detachedname);
xfree (enckeyblob);
xfree (keyblob);
diff --git a/g13/create.h b/g13/create.h
index afe616b69..d533c0852 100644
--- a/g13/create.h
+++ b/g13/create.h
@@ -20,7 +20,7 @@
#ifndef G13_CREATE_H
#define G13_CREATE_H
-gpg_error_t create_new_container (ctrl_t ctrl, const char *filename);
+gpg_error_t g13_create_container (ctrl_t ctrl, const char *filename);
#endif /*G13_CREATE_H*/
diff --git a/g13/g13.c b/g13/g13.c
index d6a31673d..6b4d05a7f 100644
--- a/g13/g13.c
+++ b/g13/g13.c
@@ -35,8 +35,10 @@
#include "i18n.h"
#include "sysutils.h"
#include "gc-opt-flags.h"
-#include "create.h"
#include "keyblob.h"
+#include "./runner.h"
+#include "./create.h"
+#include "./mount.h"
enum cmd_and_opt_values {
@@ -84,6 +86,7 @@ enum cmd_and_opt_values {
oHomedir,
oWithColons,
oDryRun,
+ oNoDetach,
oRecipient,
@@ -111,6 +114,7 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_s_n (oVerbose, "verbose", N_("verbose")),
ARGPARSE_s_n (oQuiet, "quiet", N_("be somewhat more quiet")),
ARGPARSE_s_n (oNoTTY, "no-tty", N_("don't use the terminal at all")),
+ ARGPARSE_s_n (oNoDetach, "no-detach", N_("do not detach from the console")),
ARGPARSE_s_s (oLogFile, "log-file", N_("|FILE|write log output to FILE")),
ARGPARSE_s_n (oNoLogFile, "no-log-file", "@"),
ARGPARSE_s_i (oLoggerFD, "logger-fd", "@"),
@@ -329,6 +333,7 @@ main ( int argc, char **argv)
int nogreeting = 0;
int debug_wait = 0;
int use_random_seed = 1;
+ int nodetach = 0;
int nokeysetup = 0;
enum cmd_and_opt_values cmd = 0;
struct server_control_s ctrl;
@@ -499,6 +504,8 @@ main ( int argc, char **argv)
case oAuditLog: auditlog = pargs.r.ret_str; break;
+ case oNoDetach: nodetach = 1; break;
+
case oDebug: debug_value |= pargs.r.ret_ulong; break;
case oDebugAll: debug_value = ~0; break;
case oDebugNone: debug_value = 0; break;
@@ -677,16 +684,47 @@ main ( int argc, char **argv)
{
if (argc != 1)
wrong_args ("--create filename");
- err = create_new_container (&ctrl, argv[0]);
+ err = g13_create_container (&ctrl, argv[0]);
if (err)
log_error ("error creating a new container: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
+ else
+ {
+ unsigned int n;
+
+ while ((n = runner_get_threads ()))
+ {
+ log_info ("number of running threads: %u\n", n);
+ pth_sleep (5);
+ }
+ }
+ }
+ break;
+
+ case aMount: /* Mount a container. */
+ {
+ if (argc != 1 && argc != 2 )
+ wrong_args ("--mount filename [mountpoint]");
+ err = g13_mount_container (&ctrl, argv[0], argc == 2?argv[1]:NULL);
+ if (err)
+ log_error ("error mounting container `%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ else
+ {
+ unsigned int n;
+
+ while ((n = runner_get_threads ()))
+ {
+ log_info ("number of running threads: %u\n", n);
+ pth_sleep (5);
+ }
+ }
}
break;
default:
- log_error (_("invalid command (there is no implicit command)\n"));
- break;
+ log_error (_("invalid command (there is no implicit command)\n"));
+ break;
}
/* Print the audit result if needed. */
@@ -735,3 +773,84 @@ g13_init_default_ctrl (struct server_control_s *ctrl)
}
+/* static void */
+/* daemonize (int nodetach) */
+/* { */
+/* gnupg_fd_t fd; */
+/* gnupg_fd_t fd_ssh; */
+/* pid_t pid; */
+
+/* fflush (NULL); */
+/* #ifdef HAVE_W32_SYSTEM */
+/* pid = getpid (); */
+/* #else /\*!HAVE_W32_SYSTEM*\/ */
+/* pid = fork (); */
+/* if (pid == (pid_t)-1) */
+/* { */
+/* log_fatal ("fork failed: %s\n", strerror (errno) ); */
+/* g13_exit (1); */
+/* } */
+/* else if (pid) /\* We are the parent *\/ */
+/* { */
+/* /\* We need to clwanup our resources. An gcry_atfork might be */
+/* needed. *\/ */
+/* exit (0); */
+/* /\*NOTREACHED*\/ */
+/* } /\* End parent *\/ */
+
+/* /\* */
+/* This is the child */
+/* *\/ */
+
+/* /\* Detach from tty and put process into a new session *\/ */
+/* if (!nodetach ) */
+/* { */
+/* int i; */
+/* unsigned int oldflags; */
+
+/* /\* Close stdin, stdout and stderr unless it is the log stream *\/ */
+/* for (i=0; i <= 2; i++) */
+/* { */
+/* if (!log_test_fd (i) && i != fd ) */
+/* { */
+/* if ( ! close (i) */
+/* && open ("/dev/null", i? O_WRONLY : O_RDONLY) == -1) */
+/* { */
+/* log_error ("failed to open `%s': %s\n", */
+/* "/dev/null", strerror (errno)); */
+/* cleanup (); */
+/* exit (1); */
+/* } */
+/* } */
+/* } */
+/* if (setsid() == -1) */
+/* { */
+/* log_error ("setsid() failed: %s\n", strerror(errno) ); */
+/* cleanup (); */
+/* exit (1); */
+/* } */
+
+/* log_get_prefix (&oldflags); */
+/* log_set_prefix (NULL, oldflags | JNLIB_LOG_RUN_DETACHED); */
+/* opt.running_detached = 1; */
+/* } */
+
+/* if (chdir("/")) */
+/* { */
+/* log_error ("chdir to / failed: %s\n", strerror (errno)); */
+/* exit (1); */
+/* } */
+
+/* { */
+/* struct sigaction sa; */
+
+/* sa.sa_handler = SIG_IGN; */
+/* sigemptyset (&sa.sa_mask); */
+/* sa.sa_flags = 0; */
+/* sigaction (SIGPIPE, &sa, NULL); */
+/* } */
+/* #endif /\*!HAVE_W32_SYSTEM*\/ */
+
+/* log_info ("%s %s started\n", strusage(11), strusage(13) ); */
+/* handle_something (fd, opt.ssh_support ? fd_ssh : GNUPG_INVALID_FD); */
+/* } */
diff --git a/g13/keyblob.h b/g13/keyblob.h
index b52919e0c..a7701005d 100644
--- a/g13/keyblob.h
+++ b/g13/keyblob.h
@@ -63,8 +63,9 @@
#define KEYBLOB_TAG_BLOBVERSION 0
/* This tag is used to describe the version of the keyblob. It must
- be the first tag in a keyblob. Its value is a single byte giving
- the blob version. The current version is 1. */
+ be the first tag in a keyblob and may only occur once. Its value
+ is a single byte giving the blob version. The only defined version
+ is 1. */
#define KEYBLOB_TAG_CONTTYPE 1
/* This tag gives the type of the container. The value is a two byte
diff --git a/g13/mount.c b/g13/mount.c
new file mode 100644
index 000000000..85851e9a8
--- /dev/null
+++ b/g13/mount.c
@@ -0,0 +1,303 @@
+/* mount.c - Mount a crypto container
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <assert.h>
+
+#include "g13.h"
+#include "i18n.h"
+#include "mount.h"
+
+#include "keyblob.h"
+#include "backend.h"
+#include "utils.h"
+#include "call-gpg.h"
+#include "estream.h"
+
+
+/* Parse the header prefix and return the length of the entire header. */
+static gpg_error_t
+parse_header (const char *filename,
+ const unsigned char *packet, size_t packetlen,
+ size_t *r_headerlen)
+{
+ unsigned int len;
+
+ if (packetlen != 32)
+ return gpg_error (GPG_ERR_BUG);
+
+ len = ((packet[2] << 24) | (packet[3] << 16)
+ | (packet[4] << 8) | packet[5]);
+ if (packet[0] != (0xc0|61) || len < 26
+ || memcmp (packet+6, "GnuPG/G13", 10))
+ {
+ log_error ("file `%s' is not valid container\n", filename);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+ if (packet[16] != 1)
+ {
+ log_error ("unknown version %u of container `%s'\n",
+ (unsigned int)packet[16], filename);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+ if (packet[17] || packet[18]
+ || packet[26] || packet[27] || packet[28] || packet[29]
+ || packet[30] || packet[31])
+ log_info ("WARNING: unknown meta information in `%s'\n", filename);
+ if (packet[19])
+ log_info ("WARNING: OS flag is not supported in `%s'\n", filename);
+ if (packet[24] != 1 || packet[25] != 0)
+ {
+ log_error ("meta data copies in `%s' are not supported\n", filename);
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ }
+
+ len = ((packet[20] << 24) | (packet[21] << 16)
+ | (packet[22] << 8) | packet[23]);
+
+ /* Do a basic sanity check on the length. */
+ if (len < 32 || len > 1024*1024)
+ {
+ log_error ("bad length given in container `%s'\n", filename);
+ return gpg_error (GPG_ERR_INV_OBJ);
+ }
+
+ *r_headerlen = len;
+ return 0;
+}
+
+
+
+/* Read the keyblob at FILENAME. The caller should have acquired a
+ lockfile and checked that the file exists. */
+static gpg_error_t
+read_keyblob (const char *filename,
+ void **r_enckeyblob, size_t *r_enckeybloblen)
+{
+ gpg_error_t err;
+ estream_t fp;
+ unsigned char packet[32];
+ size_t headerlen, msglen;
+ void *msg = NULL;
+
+ *r_enckeyblob = NULL;
+ *r_enckeybloblen = 0;
+
+ fp = es_fopen (filename, "rb");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading `%s': %s\n",
+ filename, gpg_strerror (err));
+ return err;
+ }
+
+ /* Read the header. It is defined as 32 bytes thus we read it in one go. */
+ if (es_fread (packet, 32, 1, fp) != 1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading the header of `%s': %s\n",
+ filename, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = parse_header (filename, packet, 32, &headerlen);
+ if (err)
+ goto leave;
+
+ if (opt.verbose)
+ log_info ("header length of `%s' is %zu\n", filename, headerlen);
+
+ /* Read everything including the padding. We should eventually do a
+ regular OpenPGP parsing to detect the padding packet and pass
+ only the actual used OpenPGP data to the engine. This is in
+ particular required when supporting CMS which will be
+ encapsulated in an OpenPGP packet. */
+ assert (headerlen >= 32);
+ msglen = headerlen - 32;
+ if (!msglen)
+ {
+ err = gpg_error (GPG_ERR_NO_DATA);
+ goto leave;
+ }
+ msg = xtrymalloc (msglen);
+ if (!msglen)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ if (es_fread (msg, msglen, 1, fp) != 1)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading keyblob of `%s': %s\n",
+ filename, gpg_strerror (err));
+ goto leave;
+ }
+
+ *r_enckeyblob = msg;
+ msg = NULL;
+ *r_enckeybloblen = msglen;
+
+ leave:
+ xfree (msg);
+ es_fclose (fp);
+
+ return err;
+}
+
+
+
+
+/* Decrypt the keyblob (ENCKEYBLOB,ENCKEYBLOBLEN) and store the result at
+ (R_KEYBLOB, R_KEYBLOBLEN). Returns 0 on success or an error code.
+ On error R_KEYBLOB is set to NULL. */
+static gpg_error_t
+decrypt_keyblob (ctrl_t ctrl, const void *enckeyblob, size_t enckeybloblen,
+ void **r_keyblob, size_t *r_keybloblen)
+{
+ gpg_error_t err;
+
+ /* FIXME: For now we only implement OpenPGP. */
+ err = gpg_decrypt_blob (ctrl, enckeyblob, enckeybloblen,
+ r_keyblob, r_keybloblen);
+
+ return err;
+}
+
+
+static void
+dump_keyblob (tupledesc_t tuples)
+{
+ size_t n;
+ unsigned int tag;
+ const void *value;
+
+ log_info ("keyblob dump:\n");
+ tag = KEYBLOB_TAG_BLOBVERSION;
+ value = find_tuple (tuples, tag, &n);
+ while (value)
+ {
+ log_info (" tag: %-5u len: %-2u value: ", tag, (unsigned int)n);
+ if (tag == KEYBLOB_TAG_ENCKEY
+ || tag == KEYBLOB_TAG_MACKEY)
+ log_printf ("[confidential]\n");
+ else if (!n)
+ log_printf ("[none]\n");
+ else
+ log_printhex ("", value, n);
+ value = next_tuple (tuples, &tag, &n);
+ }
+}
+
+
+
+/* Mount the container with name FILENAME at MOUNTPOINT. */
+gpg_error_t
+g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
+{
+ gpg_error_t err;
+ dotlock_t lock;
+ void *enckeyblob = NULL;
+ size_t enckeybloblen;
+ void *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
+ size_t n;
+ const unsigned char *value;
+ int conttype;
+
+ /* A quick check to see whether the container exists. */
+ if (access (filename, R_OK))
+ return gpg_error_from_syserror ();
+
+ /* Try to take a lock. */
+ lock = create_dotlock (filename);
+ if (!lock)
+ return gpg_error_from_syserror ();
+
+ if (make_dotlock (lock, 0))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ else
+ err = 0;
+
+ /* Check again that the file exists. */
+ {
+ struct stat sb;
+
+ if (stat (filename, &sb))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
+
+ /* Read the encrypted keyblob. */
+ err = read_keyblob (filename, &enckeyblob, &enckeybloblen);
+ if (err)
+ goto leave;
+
+ /* Decrypt that keyblob and store it in a tuple descriptor. */
+ err = decrypt_keyblob (ctrl, enckeyblob, enckeybloblen,
+ &keyblob, &keybloblen);
+ if (err)
+ goto leave;
+ xfree (enckeyblob);
+ enckeyblob = NULL;
+
+ err = create_tupledesc (&tuples, keyblob, keybloblen);
+ if (!err)
+ keyblob = NULL;
+ else
+ {
+ if (gpg_err_code (err) == GPG_ERR_NOT_SUPPORTED)
+ log_error ("unknown keyblob version\n");
+ goto leave;
+ }
+ if (opt.verbose)
+ dump_keyblob (tuples);
+
+ value = find_tuple (tuples, KEYBLOB_TAG_CONTTYPE, &n);
+ if (!value || n != 2)
+ conttype = 0;
+ else
+ conttype = (value[0] << 8 | value[1]);
+ if (!be_is_supported_conttype (conttype))
+ {
+ log_error ("content type %d is not supported\n", conttype);
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+ err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples);
+
+ leave:
+ destroy_tupledesc (tuples);
+ xfree (keyblob);
+ xfree (enckeyblob);
+ destroy_dotlock (lock);
+ return err;
+}
diff --git a/g13/mount.h b/g13/mount.h
new file mode 100644
index 000000000..03b8264c9
--- /dev/null
+++ b/g13/mount.h
@@ -0,0 +1,29 @@
+/* mmount.h - Defs to mount a crypto container
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_MOUNT_H
+#define G13_MOUNT_H
+
+gpg_error_t g13_mount_container (ctrl_t ctrl,
+ const char *filename,
+ const char *mountpoint);
+
+
+#endif /*G13_MOUNT_H*/
+
diff --git a/g13/runner.c b/g13/runner.c
new file mode 100644
index 000000000..d88b69b98
--- /dev/null
+++ b/g13/runner.c
@@ -0,0 +1,444 @@
+/* runner.c - Run and watch the backend engines
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <assert.h>
+#include <pth.h>
+
+#include "g13.h"
+#include "i18n.h"
+#include "keyblob.h"
+#include "runner.h"
+#include "../common/exechelp.h"
+
+
+/* The runner object. */
+struct runner_s
+{
+ char *name; /* The name of this runner. */
+
+ int spawned; /* True if runner_spawn has been called. */
+ pth_t threadid; /* The TID of the runner thread. */
+
+ int cancel_flag; /* If set the thread should terminate itself. */
+
+ /* We use a reference counter to know when it is safe to remove the
+ object. Lackiong an explicit ref fucntion this counter will take
+ only these two values:
+
+ 1 = Thread not running or only the thread is still running.
+ 2 = Thread is running and someone is holding a reference. */
+ int refcount;
+
+ pid_t pid; /* PID of the backend's process (the engine). */
+ int in_fd; /* File descriptors to read from the engine. */
+ int out_fd; /* File descriptors to write to the engine. */
+ engine_handler_fnc_t handler; /* The handler functions. */
+ engine_handler_cleanup_fnc_t handler_cleanup;
+ void *handler_data; /* Private data of HANDLER and HANDLER_CLEANUP. */
+
+ /* Instead of IN_FD we use an estream. Note that the runner thread
+ may close the stream and set status_fp to NULL at any time. Thus
+ it won't be a good idea to use it while the runner thread is
+ running. */
+ estream_t status_fp;
+};
+
+
+/* Avariabale to track the number of active runner threads. */
+static unsigned int thread_count;
+
+
+
+/* Write NBYTES of BUF to file descriptor FD. */
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+ size_t nleft = nbytes;
+ int nwritten;
+
+ while (nleft > 0)
+ {
+ nwritten = pth_write (fd, buf, nleft);
+ if (nwritten < 0)
+ {
+ if (errno == EINTR)
+ nwritten = 0;
+ else
+ return -1;
+ }
+ nleft -= nwritten;
+ buf = (const char*)buf + nwritten;
+ }
+
+ return 0;
+}
+
+
+static int
+check_already_spawned (runner_t runner, const char *funcname)
+{
+ if (runner->spawned)
+ {
+ log_error ("BUG: runner already spawned - ignoring call to %s\n",
+ funcname);
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/* Return the number of active threads. */
+unsigned int
+runner_get_threads (void)
+{
+ return thread_count;
+}
+
+
+/* The public release function. */
+void
+runner_release (runner_t runner)
+{
+ if (!runner)
+ return;
+
+ if (!--runner->refcount)
+ return;
+
+ es_fclose (runner->status_fp);
+ if (runner->in_fd != -1)
+ close (runner->in_fd);
+ if (runner->out_fd != -1)
+ close (runner->out_fd);
+
+ /* Fixme: close the process. */
+
+ /* Tell the engine to release its data. */
+ if (runner->handler_cleanup)
+ runner->handler_cleanup (runner->handler_data);
+
+ if (runner->pid != (pid_t)(-1))
+ {
+ /* The process has not been cleaned up - do it now. */
+ gnupg_kill_process (runner->pid);
+ /* (Actually we should use the program name and not the
+ arbitrary NAME of the runner object. However it does not
+ matter because that information is only used for
+ diagnostics.) */
+ gnupg_wait_process (runner->name, runner->pid, NULL);
+ }
+
+ xfree (runner->name);
+ xfree (runner);
+}
+
+
+/* Create a new runner context. On success a new runner object is
+ stored at R_RUNNER. On failure NULL is stored at this address and
+ an error code returned. */
+gpg_error_t
+runner_new (runner_t *r_runner, const char *name)
+{
+ runner_t runner;
+
+ *r_runner = NULL;
+
+ runner = xtrycalloc (1, sizeof *runner);
+ if (!runner)
+ return gpg_error_from_syserror ();
+ runner->name = xtrystrdup (name? name: "[unknown]");
+ if (!runner->name)
+ {
+ xfree (runner);
+ return gpg_error_from_syserror ();
+ }
+ runner->refcount = 1;
+ runner->pid = (pid_t)(-1);
+ runner->in_fd = -1;
+ runner->out_fd = -1;
+
+
+ *r_runner = runner;
+ return 0;
+}
+
+
+/* A runner usually maintaines two file descriptors to control the
+ backend engine. This function is used to set these file
+ descriptors. The function takes ownership of these file
+ descriptors. IN_FD will be used to read from engine and OUT_FD to
+ send data to the engine. */
+void
+runner_set_fds (runner_t runner, int in_fd, int out_fd)
+{
+ if (check_already_spawned (runner, "runner_set_fds"))
+ return;
+
+ if (runner->in_fd != -1)
+ close (runner->in_fd);
+ if (runner->out_fd != -1)
+ close (runner->out_fd);
+ runner->in_fd = in_fd;
+ runner->out_fd = out_fd;
+}
+
+
+/* Set the PID of the backend engine. After this call the engine is
+ owned by the runner object. */
+void
+runner_set_pid (runner_t runner, pid_t pid)
+{
+ if (check_already_spawned (runner, "runner_set_fds"))
+ return;
+
+ runner->pid = pid;
+}
+
+
+/* Register the engine handler fucntions HANDLER and HANDLER_CLEANUP
+ and its private HANDLER_DATA with RUNNER. */
+void
+runner_set_handler (runner_t runner,
+ engine_handler_fnc_t handler,
+ engine_handler_cleanup_fnc_t handler_cleanup,
+ void *handler_data)
+{
+ if (check_already_spawned (runner, "runner_set_handler"))
+ return;
+
+ runner->handler = handler;
+ runner->handler_cleanup = handler_cleanup;
+ runner->handler_data = handler_data;
+}
+
+
+/* The thread spawned by runner_spawn. */
+static void *
+runner_thread (void *arg)
+{
+ runner_t runner = arg;
+ gpg_error_t err;
+
+ log_debug ("starting runner thread\n");
+ /* If a status_fp is available, the thread's main task is to read
+ from that stream and invoke the backend's handler function. This
+ is done on a line by line base and the line length is limited to
+ a reasonable value (about 1000 characters). Other work will
+ continue either due to an EOF of the stream or by demand of the
+ engine. */
+ if (runner->status_fp)
+ {
+ int c, cont_line;
+ unsigned int pos;
+ char buffer[1024];
+ estream_t fp = runner->status_fp;
+
+ pos = 0;
+ err = 0;
+ cont_line = 0;
+ while (!err && !runner->cancel_flag && (c=es_getc (fp)) != EOF)
+ {
+ buffer[pos++] = c;
+ if (pos >= sizeof buffer - 5 || c == '\n')
+ {
+ buffer[pos - (c == '\n')] = 0;
+ if (opt.verbose)
+ log_info ("%s%s: %s\n",
+ runner->name, cont_line? "(cont)":"", buffer);
+ /* We handle only complete lines and ignore any stuff we
+ possibly had to truncate. That is - at least for the
+ encfs engine - not an issue because our changes to
+ the tool make sure that only relatively short prompt
+ lines are of interest. */
+ if (!cont_line && runner->handler)
+ err = runner->handler (runner->handler_data,
+ runner, buffer);
+ pos = 0;
+ cont_line = (c != '\n');
+ }
+ }
+ if (!err && runner->cancel_flag)
+ log_debug ("runner thread noticed cancel flag\n");
+ else
+ log_debug ("runner thread saw EOF\n");
+ if (pos)
+ {
+ buffer[pos] = 0;
+ if (opt.verbose)
+ log_info ("%s%s: %s\n",
+ runner->name, cont_line? "(cont)":"", buffer);
+ if (!cont_line && !err && runner->handler)
+ err = runner->handler (runner->handler_data,
+ runner, buffer);
+ }
+ if (!err && es_ferror (fp))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error reading from %s: %s\n",
+ runner->name, gpg_strerror (err));
+ }
+
+ runner->status_fp = NULL;
+ es_fclose (fp);
+ log_debug ("runner thread closed status fp\n");
+ }
+
+ /* Now wait for the process to finish. */
+ if (!err && runner->pid != (pid_t)(-1))
+ {
+ int exitcode;
+
+ log_debug ("runner thread waiting ...\n");
+ err = gnupg_wait_process (runner->name, runner->pid, &exitcode);
+ runner->pid = (pid_t)(-1);
+ if (err)
+ log_error ("running `%s' failed (exitcode=%d): %s\n",
+ runner->name, exitcode, gpg_strerror (err));
+ log_debug ("runner thread waiting finished\n");
+ }
+
+ /* Get rid of the runner object (note: it is refcounted). */
+ log_debug ("runner thread releasing runner ...\n");
+ runner_release (runner);
+ log_debug ("runner thread runner released\n");
+ thread_count--;
+
+ return NULL;
+}
+
+
+/* Spawn a new thread to let RUNNER work as a coprocess. */
+gpg_error_t
+runner_spawn (runner_t runner)
+{
+ gpg_error_t err;
+ pth_attr_t tattr;
+ pth_t tid;
+
+ if (check_already_spawned (runner, "runner_spawn"))
+ return gpg_error (GPG_ERR_BUG);
+
+ /* In case we have an input fd, open it as an estream so that the
+ Pth scheduling will work. The stdio functions don't work with
+ Pth because they don't call the pth counterparts of read and
+ write unless linker tricks are used. */
+ if (runner->in_fd != -1)
+ {
+ estream_t fp;
+
+ fp = es_fdopen (runner->in_fd, "r");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("can't fdopen pipe for reading: %s\n", gpg_strerror (err));
+ return err;
+ }
+ runner->status_fp = fp;
+ runner->in_fd = -1; /* Now owned by status_fp. */
+ }
+
+ tattr = pth_attr_new ();
+ pth_attr_set (tattr, PTH_ATTR_JOINABLE, 1);
+ pth_attr_set (tattr, PTH_ATTR_STACK_SIZE, 256*1024);
+ pth_attr_set (tattr, PTH_ATTR_NAME, runner->name);
+
+ tid = pth_spawn (tattr, runner_thread, runner);
+ if (!tid)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error spawning runner thread: %s\n", gpg_strerror (err));
+ return err;
+ }
+ /* The scheduler has not yet kicked in, thus we can safely set the
+ spawned flag and the tid. */
+ thread_count++;
+ runner->spawned = 1;
+ runner->threadid = tid;
+ pth_attr_destroy (tattr);
+
+ /* The runner thread is now runnable. */
+
+
+
+ return 0;
+}
+
+
+/* Cancel a running thread. */
+void
+runner_cancel (runner_t runner)
+{
+ if (runner->spawned)
+ {
+ /* FIXME: This does only work if the thread emits status lines. We
+ need to change the trhead to wait on an event. */
+ runner->cancel_flag = 1;
+ /* For now we use the brutal way and kill the process. */
+ gnupg_kill_process (runner->pid);
+ }
+}
+
+
+/* Send a line of data down to the engine. This line may not contain
+ a binary Nul or a LF character. This function is used by the
+ engine's handler. */
+gpg_error_t
+runner_send_line (runner_t runner, const void *data, size_t datalen)
+{
+ gpg_error_t err = 0;
+
+ if (!runner->spawned)
+ {
+ log_error ("BUG: runner for %s not spawned\n", runner->name);
+ err = gpg_error (GPG_ERR_INTERNAL);
+ }
+ else if (runner->out_fd == -1)
+ {
+ log_error ("no output file descriptor for runner %s\n", runner->name);
+ err = gpg_error (GPG_ERR_EBADF);
+ }
+ else if (data && datalen)
+ {
+ if (memchr (data, '\n', datalen))
+ {
+ log_error ("LF detected in response data\n");
+ err = gpg_error (GPG_ERR_BUG);
+ }
+ else if (memchr (data, 0, datalen))
+ {
+ log_error ("Nul detected in response data\n");
+ err = gpg_error (GPG_ERR_BUG);
+ }
+ else if (writen (runner->out_fd, data, datalen))
+ err = gpg_error_from_syserror ();
+ }
+
+ if (!err)
+ if (writen (runner->out_fd, "\n", 1))
+ err = gpg_error_from_syserror ();
+
+ return err;
+}
diff --git a/g13/runner.h b/g13/runner.h
new file mode 100644
index 000000000..0152f22e4
--- /dev/null
+++ b/g13/runner.h
@@ -0,0 +1,68 @@
+/* runner.h - Run and watch the backend engines
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ *
+ * This file is part of GnuPG.
+ *
+ * GnuPG is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * GnuPG is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_RUNNER_H
+#define G13_RUNNER_H
+
+/* The runner object. */
+struct runner_s;
+typedef struct runner_s *runner_t;
+
+/* Prototypes for the handler functions provided by the engine. */
+typedef gpg_error_t (*engine_handler_fnc_t) (void *opaque,
+ runner_t runner,
+ const char *statusline);
+typedef void (*engine_handler_cleanup_fnc_t) (void *opaque);
+
+
+/* Return the number of active threads. */
+unsigned int runner_get_threads (void);
+
+/* Create a new runner object. */
+gpg_error_t runner_new (runner_t *r_runner, const char *name);
+
+/* Free a runner object. */
+void runner_release (runner_t runner);
+
+/* Functions to set properties of the runner. */
+void runner_set_fds (runner_t runner, int in_fd, int out_fd);
+
+void runner_set_pid (runner_t runner, pid_t pid);
+
+/* Register the handler functions with a runner. */
+void runner_set_handler (runner_t runner,
+ engine_handler_fnc_t handler,
+ engine_handler_cleanup_fnc_t handler_cleanup,
+ void *handler_data);
+
+/* Start the runner. */
+gpg_error_t runner_spawn (runner_t runner);
+
+/* Cancel a runner. */
+void runner_cancel (runner_t runner);
+
+/* Send data back to the engine. This function is used by the
+ engine's handler. */
+gpg_error_t runner_send_line (runner_t runner,
+ const void *data, size_t datalen);
+
+
+
+#endif /*G13_RUNNER_H*/
+
diff --git a/g13/utils.c b/g13/utils.c
index 15b4426ef..ef0c572a6 100644
--- a/g13/utils.c
+++ b/g13/utils.c
@@ -28,6 +28,17 @@
#include "utils.h"
+/* Definition of the tuple descriptor object. */
+struct tupledesc_s
+{
+ unsigned char *data; /* The tuple data. */
+ size_t datalen; /* The length of the data. */
+ size_t pos; /* The current position as used by next_tuple. */
+ int refcount; /* Number of references hold. */
+};
+
+
+
/* Append the TAG and the VALUE to the MEMBUF. There is no error
checking here; this is instead done while getting the value back
from the membuf. */
@@ -49,3 +60,121 @@ append_tuple (membuf_t *membuf, int tag, const void *value, size_t length)
put_membuf (membuf, value, length);
}
+
+/* Create a tuple object by moving the ownership of (DATA,DATALEN) to
+ a new object. Returns 0 on success and stores the new object at
+ R_TUPLEHD. The return object must be released using
+ destroy_tuples(). */
+gpg_error_t
+create_tupledesc (tupledesc_t *r_desc, void *data, size_t datalen)
+{
+ if (datalen < 5 || memcmp (data, "\x00\x00\x00\x01\x01", 5))
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ *r_desc = xtrymalloc (sizeof **r_desc);
+ if (!*r_desc)
+ return gpg_error_from_syserror ();
+ (*r_desc)->data = data;
+ (*r_desc)->datalen = datalen;
+ (*r_desc)->pos = 0;
+ (*r_desc)->refcount++;
+ return 0;
+}
+
+/* Unref a tuple descriptor and if the refcount is down to 0 release
+ its allocated storage. */
+void
+destroy_tupledesc (tupledesc_t tupledesc)
+{
+ if (!tupledesc)
+ return;
+
+ if (!--tupledesc->refcount)
+ {
+ xfree (tupledesc->data);
+ xfree (tupledesc);
+ }
+}
+
+
+tupledesc_t
+ref_tupledesc (tupledesc_t tupledesc)
+{
+ if (tupledesc)
+ tupledesc->refcount++;
+ return tupledesc;
+}
+
+
+/* Find the first tuple with tag TAG. On success return a pointer to
+ its value and store the length of the value at R_LENGTH. If no
+ tuple was return NULL. For future use by next_tupe, the last
+ position is stored in the descriptor. */
+const void *
+find_tuple (tupledesc_t tupledesc, unsigned int tag, size_t *r_length)
+{
+ const unsigned char *s;
+ const unsigned char *s_end; /* Points right behind the data. */
+ unsigned int t;
+ size_t n;
+
+ s = tupledesc->data;
+ if (!s)
+ return NULL;
+ s_end = s + tupledesc->datalen;
+ while (s < s_end)
+ {
+ if (s+3 >= s_end || s + 3 < s)
+ break;
+ t = s[0] << 8;
+ t |= s[1];
+ n = s[2] << 8;
+ n |= s[3];
+ s += 4;
+ if (s + n > s_end || s + n < s)
+ break;
+ if (t == tag)
+ {
+ tupledesc->pos = (s + n) - tupledesc->data;
+ *r_length = n;
+ return s;
+ }
+ s += n;
+ }
+ return NULL;
+}
+
+
+const void *
+next_tuple (tupledesc_t tupledesc, unsigned int *r_tag, size_t *r_length)
+{
+ const unsigned char *s;
+ const unsigned char *s_end; /* Points right behind the data. */
+ unsigned int t;
+ size_t n;
+
+ s = tupledesc->data;
+ if (!s)
+ return NULL;
+ s_end = s + tupledesc->datalen;
+ s += tupledesc->pos;
+ if (s < s_end
+ && !(s+3 >= s_end || s + 3 < s))
+ {
+ t = s[0] << 8;
+ t |= s[1];
+ n = s[2] << 8;
+ n |= s[3];
+ s += 4;
+ if (!(s + n > s_end || s + n < s))
+ {
+ tupledesc->pos = (s + n) - tupledesc->data;
+ *r_tag = t;
+ *r_length = n;
+ return s;
+ }
+ }
+
+ return NULL;
+}
+
diff --git a/g13/utils.h b/g13/utils.h
index c1104f759..ef718d60d 100644
--- a/g13/utils.h
+++ b/g13/utils.h
@@ -22,10 +22,23 @@
#include "../common/membuf.h"
-
+/* Append a new tuple to a memory buffer. */
void append_tuple (membuf_t *membuf,
int tag, const void *value, size_t length);
+/* The tuple descriptor object. */
+struct tupledesc_s;
+typedef struct tupledesc_s *tupledesc_t;
+
+gpg_error_t create_tupledesc (tupledesc_t *r_tupledesc,
+ void *data, size_t datalen);
+void destroy_tupledesc (tupledesc_t tupledesc);
+tupledesc_t ref_tupledesc (tupledesc_t tupledesc);
+const void *find_tuple (tupledesc_t tupledesc,
+ unsigned int tag, size_t *r_length);
+const void *next_tuple (tupledesc_t tupledesc,
+ unsigned int *r_tag, size_t *r_length);
+
#endif /*G13_UTILS_H*/