diff --git a/g13/Makefile.am b/g13/Makefile.am
index 34d5baf4b..4244a747a 100644
--- a/g13/Makefile.am
+++ b/g13/Makefile.am
@@ -45,7 +45,8 @@ g13_SOURCES = \
runner.c runner.h \
backend.c backend.h \
be-encfs.c be-encfs.h \
- be-truecrypt.c be-truecrypt.h
+ be-truecrypt.c be-truecrypt.h \
+ be-dmcrypt.c be-dmcrypt.h
g13_LDADD = $(libcommonpth) \
$(LIBGCRYPT_LIBS) $(LIBASSUAN_LIBS) $(NPTH_LIBS) \
diff --git a/g13/backend.c b/g13/backend.c
index b35887be3..bb7bfcc02 100644
--- a/g13/backend.c
+++ b/g13/backend.c
@@ -31,13 +31,15 @@
#include "backend.h"
#include "be-encfs.h"
#include "be-truecrypt.h"
+#include "be-dmcrypt.h"
+#include "call-syshelp.h"
-
+#define no_such_backend(a) _no_such_backend ((a), __func__)
static gpg_error_t
-no_such_backend (int conttype)
+_no_such_backend (int conttype, const char *func)
{
- log_error ("invalid backend %d given - this is most likely a bug\n",
- conttype);
+ log_error ("invalid backend %d given in %s - this is most likely a bug\n",
+ conttype, func);
return gpg_error (GPG_ERR_INTERNAL);
}
@@ -81,6 +83,7 @@ be_is_supported_conttype (int conttype)
switch (conttype)
{
case CONTTYPE_ENCFS:
+ case CONTTYPE_DM_CRYPT:
return 1;
default:
@@ -106,7 +109,7 @@ be_take_lock_for_create (ctrl_t ctrl, const char *fname, dotlock_t *r_lock)
if (ctrl->conttype == CONTTYPE_DM_CRYPT)
{
/* */
- err = gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ err = call_syshelp_set_device (ctrl, fname);
goto leave;
}
@@ -186,6 +189,9 @@ be_create_new_keys (int conttype, membuf_t *mb)
case CONTTYPE_TRUECRYPT:
return be_truecrypt_create_new_keys (mb);
+ case CONTTYPE_DM_CRYPT:
+ return 0;
+
default:
return no_such_backend (conttype);
}
@@ -205,6 +211,9 @@ be_create_container (ctrl_t ctrl, int conttype,
case CONTTYPE_ENCFS:
return be_encfs_create_container (ctrl, fname, tuples, r_id);
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_create_container (ctrl);
+
default:
return no_such_backend (conttype);
}
@@ -222,6 +231,9 @@ be_mount_container (ctrl_t ctrl, int conttype,
case CONTTYPE_ENCFS:
return be_encfs_mount_container (ctrl, fname, mountpoint, tuples, r_id);
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_mount_container (ctrl, fname, mountpoint, tuples);
+
default:
return no_such_backend (conttype);
}
diff --git a/g13/be-dmcrypt.c b/g13/be-dmcrypt.c
new file mode 100644
index 000000000..325567633
--- /dev/null
+++ b/g13/be-dmcrypt.c
@@ -0,0 +1,64 @@
+/* be-dmcrypt.c - The DM-Crypt based backend
+ * Copyright (C) 2015 Werner Koch
+ *
+ * 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 .
+ */
+
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "g13.h"
+#include "i18n.h"
+#include "keyblob.h"
+#include "call-syshelp.h"
+#include "be-dmcrypt.h"
+
+
+/* Create the container using the current device.
+ * information in TUPLES. */
+gpg_error_t
+be_dmcrypt_create_container (ctrl_t ctrl)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_run_create (ctrl, CONTTYPE_DM_CRYPT);
+
+ return err;
+}
+
+
+/* Mount the container described by the filename FNAME and the keyblob
+ * information in TUPLES. On success the runner id is stored at R_ID. */
+gpg_error_t
+be_dmcrypt_mount_container (ctrl_t ctrl,
+ const char *fname, const char *mountpoint,
+ tupledesc_t tuples)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_set_device (ctrl, fname);
+ if (err)
+ goto leave;
+
+ err = call_syshelp_run_mount (ctrl, CONTTYPE_DM_CRYPT, mountpoint, tuples);
+
+ leave:
+ return err;
+}
diff --git a/g13/be-dmcrypt.h b/g13/be-dmcrypt.h
new file mode 100644
index 000000000..152186057
--- /dev/null
+++ b/g13/be-dmcrypt.h
@@ -0,0 +1,32 @@
+/* be-dmcrypt.h - Public defs for the DM-Crypt based backend
+ * Copyright (C) 2015 Werner Koch
+ *
+ * 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 .
+ */
+
+#ifndef G13_BE_DMCRYPT_H
+#define G13_BE_DMCRYPT_H
+
+#include "backend.h"
+
+gpg_error_t be_dmcrypt_create_container (ctrl_t ctrl);
+gpg_error_t be_dmcrypt_mount_container (ctrl_t ctrl,
+ const char *fname,
+ const char *mountpoint,
+ tupledesc_t tuples);
+
+
+#endif /*G13_BE_DMCRYPT_H*/
diff --git a/g13/call-syshelp.c b/g13/call-syshelp.c
index 0e227ab16..0e69e9c11 100644
--- a/g13/call-syshelp.c
+++ b/g13/call-syshelp.c
@@ -44,21 +44,56 @@ struct call_syshelp_s
};
-/* Fork off the syshelp tool if this has not already been done. */
+/* Parameter used with the CREATE command. */
+struct create_parm_s
+{
+ assuan_context_t ctx;
+ ctrl_t ctrl;
+ membuf_t plaintext;
+ unsigned int expect_plaintext:1;
+ unsigned int got_plaintext:1;
+};
+
+
+/* Parameter used with the MOUNT command. */
+struct mount_parm_s
+{
+ assuan_context_t ctx;
+ ctrl_t ctrl;
+ const void *keyblob;
+ size_t keybloblen;
+};
+
+
+
+
+
+/* Fork off the syshelp tool if this has not already been done. On
+ success stores the current Assuan context for the syshelp tool at
+ R_CTX. */
static gpg_error_t
-start_syshelp (ctrl_t ctrl)
+start_syshelp (ctrl_t ctrl, assuan_context_t *r_ctx)
{
gpg_error_t err;
assuan_context_t ctx;
assuan_fd_t no_close_list[3];
int i;
- if (ctrl->syshelp_local->assctx)
+ *r_ctx = NULL;
+
+ if (ctrl->syshelp_local && (*r_ctx = ctrl->syshelp_local->assctx))
return 0; /* Already set. */
if (opt.verbose)
log_info ("starting a new syshelp\n");
+ if (!ctrl->syshelp_local)
+ {
+ ctrl->syshelp_local = xtrycalloc (1, sizeof *ctrl->syshelp_local);
+ if (!ctrl->syshelp_local)
+ return gpg_error_from_syserror ();
+ }
+
if (es_fflush (NULL))
{
err = gpg_error_from_syserror ();
@@ -79,38 +114,40 @@ start_syshelp (ctrl_t ctrl)
return err;
}
- /* Call userv to start g13-syshelp. This userv script needs tpo be
- installed under the name "gnupg-g13-syshelp":
-
- if ( glob service-user root
- )
- reset
- suppress-args
- execute /home/wk/b/gnupg/g13/g13-syshelp -v
- else
- error Nothing to do for this service-user
- fi
- quit
- */
+ /* Call userv to start g13-syshelp. This userv script needs to be
+ * installed under the name "gnupg-g13-syshelp":
+ *
+ * if ( glob service-user root
+ * )
+ * reset
+ * suppress-args
+ * execute /home/wk/b/gnupg/g13/g13-syshelp -v
+ * else
+ * error Nothing to do for this service-user
+ * fi
+ * quit
+ */
{
- const char *argv[3];
+ const char *argv[4];
argv[0] = "userv";
- argv[1] = "gnupg-g13-syshelp";
- argv[2] = NULL;
+ argv[1] = "root";
+ argv[2] = "gnupg-g13-syshelp";
+ argv[3] = NULL;
err = assuan_pipe_connect (ctx, "/usr/bin/userv", argv,
no_close_list, NULL, NULL, 0);
}
if (err)
{
- log_error ("can't connect to '%s' - : %s\n",
- "g13-syshelp", gpg_strerror (err));
+ log_error ("can't connect to '%s': %s %s\n",
+ "g13-syshelp", gpg_strerror (err), gpg_strsource (err));
log_info ("(is userv and its gnupg-g13-syshelp script installed?)\n");
assuan_release (ctx);
return err;
}
- ctrl->syshelp_local->assctx = ctx;
+
+ *r_ctx = ctrl->syshelp_local->assctx = ctx;
if (DBG_IPC)
log_debug ("connection to g13-syshelp established\n");
@@ -129,5 +166,243 @@ call_syshelp_release (ctrl_t ctrl)
{
assuan_release (ctrl->syshelp_local->assctx);
ctrl->syshelp_local->assctx = NULL;
+ xfree (ctrl->syshelp_local);
+ ctrl->syshelp_local = NULL;
}
}
+
+
+/* Send the DEVICE command to the syshelper. FNAME is the name of the
+ device. */
+gpg_error_t
+call_syshelp_set_device (ctrl_t ctrl, const char *fname)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ char *line = NULL;
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ line = xtryasprintf ("DEVICE %s", fname);
+ if (!line)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ err = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+
+ leave:
+ xfree (line);
+ return err;
+}
+
+
+
+static gpg_error_t
+create_status_cb (void *opaque, const char *line)
+{
+ struct create_parm_s *parm = opaque;
+
+ if (has_leading_keyword (line, "PLAINTEXT_FOLLOWS"))
+ parm->expect_plaintext = 1;
+
+ return 0;
+}
+
+
+static gpg_error_t
+create_data_cb (void *opaque, const void *data, size_t datalen)
+{
+ struct create_parm_s *parm = opaque;
+ gpg_error_t err = 0;
+
+ if (!parm->expect_plaintext)
+ {
+ log_error ("status line for data missing\n");
+ err = gpg_error (GPG_ERR_UNEXPECTED);
+ }
+ else if (data)
+ {
+ put_membuf (&parm->plaintext, data, datalen);
+ }
+ else
+ {
+ parm->expect_plaintext = 0;
+ parm->got_plaintext = 1;
+ }
+
+ return err;
+}
+
+
+static gpg_error_t
+create_inq_cb (void *opaque, const char *line)
+{
+ struct create_parm_s *parm = opaque;
+ gpg_error_t err;
+
+ if (has_leading_keyword (line, "ENCKEYBLOB"))
+ {
+ void *plaintext;
+ size_t plaintextlen;
+
+ if (!parm->got_plaintext)
+ err = gpg_error (GPG_ERR_UNEXPECTED);
+ else if (!(plaintext = get_membuf (&parm->plaintext, &plaintextlen)))
+ err = gpg_error_from_syserror ();
+ else
+ {
+ void *ciphertext;
+ size_t ciphertextlen;
+
+ log_printhex ("plain", plaintext, plaintextlen);
+ err = g13_encrypt_keyblob (parm->ctrl,
+ plaintext, plaintextlen,
+ &ciphertext, &ciphertextlen);
+ wipememory (plaintext, plaintextlen);
+ xfree (plaintext);
+ if (err)
+ log_error ("error encrypting keyblob: %s\n", gpg_strerror (err));
+ else
+ {
+ err = assuan_send_data (parm->ctx, ciphertext, ciphertextlen);
+ xfree (ciphertext);
+ if (err)
+ log_error ("sending ciphertext to g13-syshelp failed: %s\n",
+ gpg_strerror (err));
+ }
+ }
+ }
+ else
+ err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+ return err;
+}
+
+
+/* Run the CREATE command on the current device. CONTTYPES gives the
+ requested content type for the new container. */
+gpg_error_t
+call_syshelp_run_create (ctrl_t ctrl, int conttype)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct create_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ /* tty_get ("waiting for debugger"); */
+ /* tty_kill_prompt (); */
+
+ parm.ctx = ctx;
+ parm.ctrl = ctrl;
+ init_membuf (&parm.plaintext, 512);
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ err = assuan_transact (ctx, "CREATE dm-crypt",
+ create_data_cb, &parm,
+ create_inq_cb, &parm,
+ create_status_cb, &parm);
+ }
+ else
+ {
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ xfree (get_membuf (&parm.plaintext, NULL));
+ return err;
+}
+
+
+
+static gpg_error_t
+mount_status_cb (void *opaque, const char *line)
+{
+ struct mount_parm_s *parm = opaque;
+
+ /* Nothing right now. */
+ (void)parm;
+ (void)line;
+
+ return 0;
+}
+
+
+static gpg_error_t
+mount_inq_cb (void *opaque, const char *line)
+{
+ struct mount_parm_s *parm = opaque;
+ gpg_error_t err;
+
+ if (has_leading_keyword (line, "KEYBLOB"))
+ {
+ int setconfidential = !assuan_get_flag (parm->ctx, ASSUAN_CONFIDENTIAL);
+
+ if (setconfidential)
+ assuan_begin_confidential (parm->ctx);
+ err = assuan_send_data (parm->ctx, parm->keyblob, parm->keybloblen);
+ if (setconfidential)
+ assuan_end_confidential (parm->ctx);
+ if (err)
+ log_error ("sending keyblob to g13-syshelp failed: %s\n",
+ gpg_strerror (err));
+ }
+ else
+ err = gpg_error (GPG_ERR_ASS_UNKNOWN_INQUIRE);
+
+ return err;
+}
+
+
+/* Run the MOUNT command on the current device. CONTTYPES gives the
+ requested content type for the new container. MOUNTPOINT the
+ desired mount point or NULL for default. */
+gpg_error_t
+call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
+ tupledesc_t tuples)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+ struct mount_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ /* tty_get ("waiting for debugger"); */
+ /* tty_kill_prompt (); */
+
+ parm.ctx = ctx;
+ parm.ctrl = ctrl;
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ ref_tupledesc (tuples);
+ parm.keyblob = get_tupledesc_data (tuples, &parm.keybloblen);
+ err = assuan_transact (ctx, "MOUNT dm-crypt",
+ NULL, NULL,
+ mount_inq_cb, &parm,
+ mount_status_cb, &parm);
+ unref_tupledesc (tuples);
+ }
+ else
+ {
+ (void)mountpoint; /* Not used. */
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
diff --git a/g13/call-syshelp.h b/g13/call-syshelp.h
index c78d53b5c..60cc776a8 100644
--- a/g13/call-syshelp.h
+++ b/g13/call-syshelp.h
@@ -20,7 +20,14 @@
#ifndef GNUPG_G13_CALL_SYSHELP_H
#define GNUPG_G13_CALL_SYSHELP_H
+#include "g13tuple.h"
+
void call_syshelp_release (ctrl_t ctrl);
+gpg_error_t call_syshelp_set_device (ctrl_t ctrl, const char *fname);
+gpg_error_t call_syshelp_run_create (ctrl_t ctrl, int conttype);
+gpg_error_t call_syshelp_run_mount (ctrl_t ctrl, int conttype,
+ const char *mountpoint,
+ tupledesc_t tuples);
#endif /*GNUPG_G13_CALL_SYSHELP_H*/
diff --git a/g13/create.c b/g13/create.c
index fc2761877..0126f5b47 100644
--- a/g13/create.c
+++ b/g13/create.c
@@ -103,17 +103,16 @@ create_new_keyblob (ctrl_t ctrl, int is_detached,
CTRL the result is a single OpenPGP binary message, a single
special OpenPGP packet encapsulating a CMS message or a
concatenation of both with the CMS packet being the last. */
-static gpg_error_t
-encrypt_keyblob (ctrl_t ctrl, void *keyblob, size_t keybloblen,
- strlist_t keys,
- void **r_encblob, size_t *r_encbloblen)
+gpg_error_t
+g13_encrypt_keyblob (ctrl_t ctrl, void *keyblob, size_t keybloblen,
+ void **r_encblob, size_t *r_encbloblen)
{
gpg_error_t err;
/* FIXME: For now we only implement OpenPGP. */
err = gpg_encrypt_blob (ctrl, opt.gpg_program, opt.gpg_arguments,
keyblob, keybloblen,
- keys,
+ ctrl->recipients,
r_encblob, r_encbloblen);
return err;
@@ -219,11 +218,10 @@ write_keyblob (const char *filename,
/* Create a new container under the name FILENAME and intialize it
- using the current settings. KEYS is a list of public keys to which
- the container will be encrypted. If the file already exists an
- error is returned. */
+ using the current settings. If the file already exists an error is
+ returned. */
gpg_error_t
-g13_create_container (ctrl_t ctrl, const char *filename, strlist_t keys)
+g13_create_container (ctrl_t ctrl, const char *filename)
{
gpg_error_t err;
dotlock_t lock;
@@ -236,7 +234,7 @@ g13_create_container (ctrl_t ctrl, const char *filename, strlist_t keys)
tupledesc_t tuples = NULL;
unsigned int dummy_rid;
- if (!keys)
+ if (!ctrl->recipients)
return gpg_error (GPG_ERR_NO_PUBKEY);
err = be_take_lock_for_create (ctrl, filename, &lock);
@@ -259,29 +257,32 @@ g13_create_container (ctrl_t ctrl, const char *filename, strlist_t keys)
}
}
- /* Create a new keyblob. */
- err = create_new_keyblob (ctrl, !!detachedname, &keyblob, &keybloblen);
- if (err)
- goto leave;
+ if (ctrl->conttype != CONTTYPE_DM_CRYPT)
+ {
+ /* Create a new keyblob. */
+ err = create_new_keyblob (ctrl, !!detachedname, &keyblob, &keybloblen);
+ if (err)
+ goto leave;
- /* Encrypt that keyblob. */
- err = encrypt_keyblob (ctrl, keyblob, keybloblen, keys,
- &enckeyblob, &enckeybloblen);
- if (err)
- goto leave;
+ /* Encrypt that keyblob. */
+ err = g13_encrypt_keyblob (ctrl, keyblob, keybloblen,
+ &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); */
+ /* 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 (filename, enckeyblob, enckeybloblen);
- if (err)
- goto leave;
+ /* Write out the header, the encrypted keyblob and some padding. */
+ err = write_keyblob (filename, enckeyblob, enckeybloblen);
+ if (err)
+ goto leave;
+ }
/* Create and append the container. FIXME: We should pass the
estream object in addition to the filename, so that the backend
diff --git a/g13/create.h b/g13/create.h
index cc4ddfd08..ec4224c40 100644
--- a/g13/create.h
+++ b/g13/create.h
@@ -20,8 +20,10 @@
#ifndef G13_CREATE_H
#define G13_CREATE_H
-gpg_error_t g13_create_container (ctrl_t ctrl, const char *filename,
- strlist_t keys);
+gpg_error_t g13_encrypt_keyblob (ctrl_t ctrl,
+ void *keyblob, size_t keybloblen,
+ void **r_encblob, size_t *r_encbloblen);
+gpg_error_t g13_create_container (ctrl_t ctrl, const char *filename);
#endif /*G13_CREATE_H*/
diff --git a/g13/g13-syshelp.c b/g13/g13-syshelp.c
index c09a5e917..cbb5f8dbf 100644
--- a/g13/g13-syshelp.c
+++ b/g13/g13-syshelp.c
@@ -270,6 +270,10 @@ main ( int argc, char **argv)
strerror (errno));
opt.homedir = default_homedir ();
+ /* Fixme: We enable verbose mode here because there is currently no
+ way to do this when starting g13-syshelp. To fix that we should
+ add a g13-syshelp.conf file in /etc/gnupg. */
+ opt.verbose = 1;
/* First check whether we have a debug option on the commandline. */
orig_argc = argc;
diff --git a/g13/g13-syshelp.h b/g13/g13-syshelp.h
index 5a2055eb1..0503079cd 100644
--- a/g13/g13-syshelp.h
+++ b/g13/g13-syshelp.h
@@ -21,7 +21,7 @@
#define G13_SYSHELP_H
#include "g13-common.h"
-
+#include "g13tuple.h"
struct tab_item_s;
typedef struct tab_item_s *tab_item_t;
@@ -83,6 +83,8 @@ gpg_error_t sh_is_empty_partition (const char *name);
/*-- sh-dmcrypt.c --*/
gpg_error_t sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname,
estream_t devfp);
+gpg_error_t sh_dmcrypt_mount_container (ctrl_t ctrl, const char *devname,
+ tupledesc_t keyblob);
#endif /*G13_SYSHELP_H*/
diff --git a/g13/g13.c b/g13/g13.c
index d9d40982a..b8a2dda6c 100644
--- a/g13/g13.c
+++ b/g13/g13.c
@@ -459,7 +459,7 @@ main ( int argc, char **argv)
if (default_config)
{
if (parse_debug)
- log_info (_("NOTE: no default option file '%s'\n"), configname);
+ log_info (_("Note: no default option file '%s'\n"), configname);
}
else
{
@@ -625,6 +625,8 @@ main ( int argc, char **argv)
/* Now that we have the options parsed we need to update the default
control structure. */
g13_init_default_ctrl (&ctrl);
+ ctrl.recipients = recipients;
+ recipients = NULL;
if (nogreeting)
greeting = 0;
@@ -646,7 +648,7 @@ main ( int argc, char **argv)
for (i=0; i < argc; i++)
if (argv[i][0] == '-' && argv[i][1] == '-')
- log_info (_("NOTE: '%s' is not considered an option\n"), argv[i]);
+ log_info (_("Note: '%s' is not considered an option\n"), argv[i]);
}
@@ -698,7 +700,7 @@ main ( int argc, char **argv)
strlist_t sl;
int failed = 0;
- for (sl = recipients; sl; sl = sl->next)
+ for (sl = ctrl->recipients; sl; sl = sl->next)
if (check_encryption_key ())
failed = 1;
if (failed)
@@ -738,7 +740,7 @@ main ( int argc, char **argv)
log_error ("server exited with error: %s <%s>\n",
gpg_strerror (err), gpg_strsource (err));
else
- shutdown_pending++;
+ g13_request_shutdown ();
}
break;
@@ -747,12 +749,12 @@ main ( int argc, char **argv)
if (argc != 1)
wrong_args ("--create filename");
start_idle_task ();
- err = g13_create_container (&ctrl, argv[0], recipients);
+ 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
- shutdown_pending++;
+ g13_request_shutdown ();
}
break;
@@ -797,6 +799,16 @@ void
g13_deinit_default_ctrl (ctrl_t ctrl)
{
call_syshelp_release (ctrl);
+ FREE_STRLIST (ctrl->recipients);
+}
+
+
+/* Request a shutdown. This can be used when the process should
+ * finish instead of running the idle task. */
+void
+g13_request_shutdown (void)
+{
+ shutdown_pending++;
}
@@ -932,9 +944,11 @@ idle_task (void *dummy_arg)
}
if (ret <= 0)
- /* Interrupt or timeout. Will be handled when calculating the
- next timeout. */
- continue;
+ {
+ /* Interrupt or timeout. Will be handled when calculating the
+ next timeout. */
+ continue;
+ }
/* Here one would add processing of file descriptors. */
}
diff --git a/g13/g13.h b/g13/g13.h
index f6f88b96b..e69489069 100644
--- a/g13/g13.h
+++ b/g13/g13.h
@@ -47,11 +47,14 @@ struct server_control_s
/* Type of the current container. See the CONTTYPE_ constants. */
int conttype;
+ strlist_t recipients; /* List of recipients. */
+
};
/*-- g13.c --*/
void g13_init_default_ctrl (ctrl_t ctrl);
void g13_deinit_default_ctrl (ctrl_t ctrl);
+void g13_request_shutdown (void);
#endif /*G13_H*/
diff --git a/g13/g13tuple.c b/g13/g13tuple.c
index b3187fbfe..fc6644cb7 100644
--- a/g13/g13tuple.c
+++ b/g13/g13tuple.c
@@ -143,6 +143,18 @@ ref_tupledesc (tupledesc_t tupledesc)
}
+/* Return a pointer to the memory used to store the tuples. This is
+ * the data originally provided to create_tupledesc. It is higly
+ * recommended that the callers uses ref_tupledesc before calling this
+ * function and unref_tupledesc when the return data will not anymore
+ * be used. */
+const void *
+get_tupledesc_data (tupledesc_t tupledesc, size_t *r_datalen)
+{
+ *r_datalen = tupledesc->datalen;
+ return tupledesc->data;
+}
+
/* 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 found return NULL. For use by next_tuple, the last
diff --git a/g13/g13tuple.h b/g13/g13tuple.h
index 948e3475e..c9dfb47b7 100644
--- a/g13/g13tuple.h
+++ b/g13/g13tuple.h
@@ -36,6 +36,9 @@ 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);
+#define unref_tupledesc(a) destroy_tupledesc ((a))
+const void *get_tupledesc_data (tupledesc_t tupledesc, size_t *r_datalen);
+
const void *find_tuple (tupledesc_t tupledesc,
unsigned int tag, size_t *r_length);
gpg_error_t find_tuple_uint (tupledesc_t tupledesc, unsigned int tag,
diff --git a/g13/mount.c b/g13/mount.c
index bc54020eb..c5c8f22b4 100644
--- a/g13/mount.c
+++ b/g13/mount.c
@@ -70,11 +70,8 @@ parse_header (const char *filename,
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);
- }
+ if (packet[24] > 1 )
+ log_info ("Note: meta data copies in '%s' are ignored\n", filename);
len = buf32_to_uint (packet+20);
@@ -216,6 +213,7 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
{
gpg_error_t err;
dotlock_t lock;
+ int needs_syshelp;
void *enckeyblob = NULL;
size_t enckeybloblen;
void *keyblob = NULL;
@@ -231,6 +229,12 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
if (access (filename, R_OK))
return gpg_error_from_syserror ();
+ /* Decide whether we need to use the g13-syshelp because we can't
+ use lock files for them. This is most likely the case for device
+ files; thus we test for this. FIXME: The correct solution would
+ be to call g13-syshelp to match the file against the g13tab. */
+ needs_syshelp = !strncmp (filename, "/dev/", 5);
+
if (!mountpoint)
{
mountpoint_buffer = xtrystrdup ("/tmp/g13-XXXXXX");
@@ -247,21 +251,25 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
mountpoint = mountpoint_buffer;
}
- /* Try to take a lock. */
- lock = dotlock_create (filename, 0);
- if (!lock)
- {
- xfree (mountpoint_buffer);
- return gpg_error_from_syserror ();
- }
-
- if (dotlock_take (lock, 0))
- {
- err = gpg_error_from_syserror ();
- goto leave;
- }
+ err = 0;
+ if (needs_syshelp)
+ lock = NULL;
else
- err = 0;
+ {
+ /* Try to take a lock. */
+ lock = dotlock_create (filename, 0);
+ if (!lock)
+ {
+ xfree (mountpoint_buffer);
+ return gpg_error_from_syserror ();
+ }
+
+ if (dotlock_take (lock, 0))
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ }
/* Check again that the file exists. */
{
@@ -275,6 +283,8 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
}
/* Read the encrypted keyblob. */
+ /* Fixme: Should we move this to syshelp for dm-crypt or do we
+ assume that the encrypted device is world readable? */
err = read_keyblob (filename, &enckeyblob, &enckeybloblen);
if (err)
goto leave;
@@ -311,8 +321,15 @@ g13_mount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
goto leave;
}
err = be_mount_container (ctrl, conttype, filename, mountpoint, tuples, &rid);
- if (!err)
+ if (err)
+ ;
+ else if (conttype == CONTTYPE_DM_CRYPT)
+ g13_request_shutdown ();
+ else
{
+ /* Unless this is a DM-CRYPT mount we put it into our mounttable
+ so that we can manage the mounts ourselves. For dm-crypt we
+ do not keep a process to monitor he mounts (for now). */
err = mountinfo_add_mount (filename, mountpoint, conttype, rid,
!!mountpoint_buffer);
/* Fixme: What shall we do if this fails? Add a provisional
diff --git a/g13/server.c b/g13/server.c
index 07b74f8bf..2f4acf595 100644
--- a/g13/server.c
+++ b/g13/server.c
@@ -45,8 +45,6 @@ struct server_local_s
assuan_context_t assuan_ctx;
char *containername; /* Malloced active containername. */
-
- strlist_t recipients; /* List of recipients. */
};
@@ -194,7 +192,7 @@ reset_notify (assuan_context_t ctx, char *line)
xfree (ctrl->server_local->containername);
ctrl->server_local->containername = NULL;
- FREE_STRLIST (ctrl->server_local->recipients);
+ FREE_STRLIST (ctrl->recipients);
assuan_close_input_fd (ctx);
assuan_close_output_fd (ctx);
@@ -372,7 +370,7 @@ cmd_recipient (assuan_context_t ctx, char *line)
line = skip_options (line);
- if (!add_to_strlist_try (&ctrl->server_local->recipients, line))
+ if (!add_to_strlist_try (&ctrl->recipients, line))
err = gpg_error_from_syserror ();
return leave_cmd (ctx, err);
@@ -438,11 +436,11 @@ cmd_create (assuan_context_t ctx, char *line)
}
/* Create container. */
- err = g13_create_container (ctrl, line, ctrl->server_local->recipients);
+ err = g13_create_container (ctrl, line);
if (!err)
{
- FREE_STRLIST (ctrl->server_local->recipients);
+ FREE_STRLIST (ctrl->recipients);
/* Store the filename. */
ctrl->server_local->containername = xtrystrdup (line);
diff --git a/g13/sh-cmd.c b/g13/sh-cmd.c
index 8a3006cab..6ba4cd8c8 100644
--- a/g13/sh-cmd.c
+++ b/g13/sh-cmd.c
@@ -172,9 +172,15 @@ cmd_device (assuan_context_t ctx, char *line)
tab_item_t ti;
estream_t fp = NULL;
- /* strcpy (line, "/dev/sdb1"); /\* FIXME *\/ */
line = skip_options (line);
+/* # warning hardwired to /dev/sdb1 ! */
+/* if (strcmp (line, "/dev/sdb1")) */
+/* { */
+/* err = gpg_error (GPG_ERR_ENOENT); */
+/* goto leave; */
+/* } */
+
/* Always close an open device stream of this session. */
xfree (ctrl->server_local->devicename);
ctrl->server_local->devicename = NULL;
@@ -239,6 +245,81 @@ cmd_create (assuan_context_t ctx, char *line)
{
ctrl_t ctrl = assuan_get_pointer (ctx);
gpg_error_t err = 0;
+ estream_t fp = NULL;
+
+ line = skip_options (line);
+ if (strcmp (line, "dm-crypt"))
+ {
+ err = set_error (GPG_ERR_INV_ARG, "Type must be \"dm-crypt\"");
+ goto leave;
+ }
+
+ if (!ctrl->server_local->devicename
+ || !ctrl->server_local->devicefp
+ || !ctrl->devti)
+ {
+ err = set_error (GPG_ERR_ENOENT, "No device has been set");
+ goto leave;
+ }
+
+ err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (err)
+ {
+ if (gpg_err_code (err) == GPG_ERR_FALSE)
+ err = gpg_error (GPG_ERR_CONFLICT);
+ err = assuan_set_error (ctx, err, "Partition is not empty");
+ goto leave;
+ }
+
+ /* We need a writeable stream to create the container. */
+ fp = es_fopen (ctrl->server_local->devicename, "r+b");
+ if (!fp)
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error opening '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+ if (es_setvbuf (fp, NULL, _IONBF, 0))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error setting '%s' to _IONBF: %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err));
+ goto leave;
+ }
+
+ err = sh_dmcrypt_create_container (ctrl,
+ ctrl->server_local->devicename,
+ fp);
+ if (es_fclose (fp))
+ {
+ gpg_error_t err2 = gpg_error_from_syserror ();
+ log_error ("error closing '%s': %s\n",
+ ctrl->server_local->devicename, gpg_strerror (err2));
+ if (!err)
+ err = err2;
+ }
+ fp = NULL;
+
+ leave:
+ es_fclose (fp);
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_mount[] =
+ "MOUNT \n"
+ "\n"
+ "Mount an encrypted partition on the current device.\n"
+ " must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_mount (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+ unsigned char *keyblob = NULL;
+ size_t keybloblen;
+ tupledesc_t tuples = NULL;
line = skip_options (line);
@@ -257,25 +338,47 @@ cmd_create (assuan_context_t ctx, char *line)
}
err = sh_is_empty_partition (ctrl->server_local->devicename);
+ if (!err)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ /* We expect that the client already decrypted the keyblob.
+ * Eventually we should move reading of the keyblob to here and ask
+ * the client to decrypt it. */
+ assuan_begin_confidential (ctx);
+ err = assuan_inquire (ctx, "KEYBLOB",
+ &keyblob, &keybloblen, 4 * 1024);
+ assuan_end_confidential (ctx);
if (err)
{
- err = assuan_set_error (ctx, err, "Partition is not empty");
+ log_error (_("assuan_inquire failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+ 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 received\n");
goto leave;
}
- err = sh_dmcrypt_create_container (ctrl,
- ctrl->server_local->devicename,
- ctrl->server_local->devicefp);
-
-
-
+ err = sh_dmcrypt_mount_container (ctrl,
+ ctrl->server_local->devicename,
+ tuples);
leave:
+ xfree (tuples);
+ destroy_tupledesc (tuples);
return leave_cmd (ctx, err);
}
-
static const char hlp_getinfo[] =
"GETINFO \n"
"\n"
@@ -372,6 +475,7 @@ register_commands (assuan_context_t ctx, int fail_all)
} table[] = {
{ "DEVICE", cmd_device, hlp_device },
{ "CREATE", cmd_create, hlp_create },
+ { "MOUNT", cmd_mount, hlp_mount },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
diff --git a/g13/sh-dmcrypt.c b/g13/sh-dmcrypt.c
index 20eea2350..f0693b1e9 100644
--- a/g13/sh-dmcrypt.c
+++ b/g13/sh-dmcrypt.c
@@ -45,7 +45,7 @@
/* The length of the crypto setup area in sectors. 16 KiB is a nice
multiple of a modern block size and should be sufficient for all
- kind of extra public key encryption packet. */
+ kind of extra public key encryption packets. */
#define SETUP_AREA_SECTORS 32 /* 16 KiB */
/* The number of header block copies stored at the begin and end of
@@ -208,11 +208,14 @@ mk_setup_area_prefix (size_t *r_length)
}
+/* Create a new g13 styloe DM-Crypt container on devoce DEVNAME. */
gpg_error_t
sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
{
gpg_error_t err;
char *header_space;
+ size_t header_space_size, header_space_used;
+ size_t paddinglen;
char *targetname = NULL;
size_t nread;
char *p;
@@ -226,11 +229,14 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
size_t keyblob_len;
size_t n;
const char *s;
+ unsigned char *packet;
+ int copy;
if (!ctrl->devti)
return gpg_error (GPG_ERR_INV_ARG);
- header_space = xtrymalloc (HEADER_SECTORS * SECTOR_SIZE);
+ header_space_size = SETUP_AREA_SECTORS * SECTOR_SIZE;
+ header_space = xtrymalloc (header_space_size);
if (!header_space)
return gpg_error_from_syserror ();
@@ -248,7 +254,7 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
append_tuple (&keyblob, KEYBLOB_TAG_CREATED, tbuf, strlen (tbuf));
}
- /* Rewind out stream. */
+ /* Rewind device stream. */
if (es_fseeko (devfp, 0, SEEK_SET))
{
err = gpg_error_from_syserror ();
@@ -259,9 +265,9 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
es_clearerr (devfp);
/* Extra check that the device is empty. */
- if (es_read (devfp, header_space, HEADER_SECTORS * SECTOR_SIZE, &nread))
+ if (es_read (devfp, header_space, header_space_size, &nread))
err = gpg_error_from_syserror ();
- else if (nread != HEADER_SECTORS * SECTOR_SIZE)
+ else if (nread != header_space_size)
err = gpg_error (GPG_ERR_TOO_SHORT);
else
err = 0;
@@ -302,7 +308,10 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
err = gpg_error (GPG_ERR_TOO_SHORT);
goto leave;
}
+ append_tuple_uint (&keyblob, KEYBLOB_TAG_CONT_NSEC, nblocks);
nblocks -= HEADER_SECTORS + FOOTER_SECTORS;
+ append_tuple_uint (&keyblob, KEYBLOB_TAG_ENC_NSEC, nblocks);
+ append_tuple_uint (&keyblob, KEYBLOB_TAG_ENC_OFF, HEADER_SECTORS);
/* Device mapper needs a name for the device: Take it from the label
or use "0". */
@@ -349,6 +358,9 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
goto leave;
}
append_tuple (&keyblob, KEYBLOB_TAG_HDRCOPY, p, n);
+ assert (n < header_space_size);
+ memcpy (header_space, p, n);
+ header_space_used = n;
/* Turn the keyblob into a buffer and callback to encrypt it. */
keyblob_buf = get_membuf (&keyblob, &keyblob_len);
@@ -363,29 +375,133 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
log_error ("encrypting the keyblob failed: %s\n", gpg_strerror (err));
goto leave;
}
+ log_debug ("plain setuparea=%p %zu bytes\n", keyblob_buf, keyblob_len);
wipememory (keyblob_buf, keyblob_len);
xfree (keyblob_buf);
keyblob_buf = NULL;
- /* Create the container. */
- /* { */
- /* const char *argv[3]; */
+ log_debug ("encry setuparea=%p %zu bytes\n", p, n);
+ if (n >= header_space_size || (header_space_used + n) >= header_space_size)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ log_error ("setup area would overflow: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ memcpy (header_space + header_space_used, p, n);
+ header_space_used += n;
- /* argv[0] = "create"; */
- /* argv[1] = targetname; */
- /* argv[2] = NULL; */
- /* err = sh_exec_tool ("/sbin/dmsetup", argv, table, &result, NULL); */
- /* } */
- /* if (err) */
- /* { */
- /* log_error ("error running dmsetup for '%s': %s\n", */
- /* devname, gpg_strerror (err)); */
- /* goto leave; */
- /* } */
- /* log_debug ("dmsetup result: %s\n", result); */
+ /* Write the padding. */
+ packet = header_space + header_space_used;
+ paddinglen = header_space_size - header_space_used;
+ if (paddinglen < 16)
+ {
+ err = gpg_error (GPG_ERR_TOO_LARGE);
+ log_error ("setup area too short for padding: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ packet[0] = (0xc0|61); /* CTB for Private packet type 0x61. */
+ packet[1] = 0xff; /* 5 byte length packet, value 20. */
+ packet[2] = (paddinglen-6) >> 24;
+ packet[3] = (paddinglen-6) >> 16;
+ packet[4] = (paddinglen-6) >> 8;
+ packet[5] = (paddinglen-6);
+ packet += 6;
+ paddinglen -= 6;
+ header_space_used += 6;
+ for ( ;paddinglen >= 10;
+ paddinglen -= 10, packet += 10, header_space_used += 10)
+ memcpy (packet, "GnuPG/PAD", 10);
+ for ( ;paddinglen; paddinglen--, packet++, header_space_used++)
+ *packet = 0;
+
+ if (header_space_used != header_space_size)
+ BUG ();
+
+ /* Create the container. */
+ {
+ const char *argv[3];
+
+ argv[0] = "create";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup create %s\"\n", targetname);
+ log_debug (" with table='%s'\"\n", table);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, table, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running dmsetup for '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
/* Write the setup area. */
+ if (es_fseeko (devfp, 0, SEEK_SET))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error seeking to begin of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ es_clearerr (devfp);
+ for (copy = 0; copy < HEADER_SETUP_AREA_COPIES; copy++)
+ {
+ size_t nwritten;
+
+ if (es_write (devfp, header_space, header_space_size, &nwritten))
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ else if (nwritten != header_space_size)
+ {
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ break;
+ }
+ }
+ if (err)
+ {
+ log_error ("error writing header space copy %d of '%s': %s\n",
+ copy, devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ if (es_fseeko (devfp,
+ (- header_space_size * FOOTER_SETUP_AREA_COPIES), SEEK_END))
+ {
+ err = gpg_error_from_syserror ();
+ log_error ("error seeking to end of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ es_clearerr (devfp);
+
+ for (copy = 0; copy < FOOTER_SETUP_AREA_COPIES; copy++)
+ {
+ size_t nwritten;
+
+ if (es_write (devfp, header_space, header_space_size, &nwritten))
+ {
+ err = gpg_error_from_syserror ();
+ break;
+ }
+ else if (nwritten != header_space_size)
+ {
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ break;
+ }
+ }
+ if (!err && es_fflush (devfp))
+ err = gpg_error_from_syserror ();
+ if (err)
+ {
+ log_error ("error writing footer space copy %d of '%s': %s\n",
+ copy, devname, gpg_strerror (err));
+ goto leave;
+ }
leave:
wipememory (hexkey, sizeof hexkey);
@@ -405,3 +521,166 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
xfree (header_space);
return err;
}
+
+
+/* Mount a DM-Crypt congtainer on device DEVNAME taking keys and other
+ * meta data from KEYBLOB. */
+gpg_error_t
+sh_dmcrypt_mount_container (ctrl_t ctrl, const char *devname,
+ tupledesc_t keyblob)
+{
+ gpg_error_t err;
+ char *targetname = NULL;
+ char hexkey[16*2+1];
+ char *table = NULL;
+ unsigned long long nblocks, nblocks2;
+ char *result = NULL;
+ size_t n;
+ const char *s;
+ const char *algostr;
+ size_t algostrlen;
+
+ if (!ctrl->devti)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ /* Check that the device is not yet used by device mapper. */
+ err = check_blockdev (devname);
+ if (err)
+ goto leave;
+
+ /* Compute the number of blocks and compare them to the value
+ provided as meta data. */
+ err = sh_blockdev_getsz (devname, &nblocks);
+ if (err)
+ {
+ log_error ("error getting size of '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ err = find_tuple_uint (keyblob, KEYBLOB_TAG_CONT_NSEC, &nblocks2);
+ if (err)
+ {
+ log_error ("error getting size from keyblob: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+ if (nblocks != nblocks2)
+ {
+ log_error ("inconsistent size of container: expected==%llu got=%llu\n",
+ nblocks2, nblocks);
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+ if (nblocks <= HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS)
+ {
+ log_error ("device '%s' is too small (min=%d blocks)\n",
+ devname,
+ HEADER_SECTORS + MIN_ENCRYPTED_SPACE + FOOTER_SECTORS);
+ err = gpg_error (GPG_ERR_TOO_SHORT);
+ goto leave;
+ }
+ nblocks -= HEADER_SECTORS + FOOTER_SECTORS;
+ err = find_tuple_uint (keyblob, KEYBLOB_TAG_ENC_NSEC, &nblocks2);
+ if (err)
+ {
+ log_error ("error getting enc size from keyblob: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ if (nblocks != nblocks2)
+ {
+ log_error ("inconsistent size of enc data: expected==%llu got=%llu\n",
+ nblocks2, nblocks);
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+ /* Check that the offset is consistent. */
+ err = find_tuple_uint (keyblob, KEYBLOB_TAG_ENC_OFF, &nblocks2);
+ if (err)
+ {
+ log_error ("error getting enc offset from keyblob: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ if (nblocks2 != HEADER_SECTORS)
+ {
+ log_error ("inconsistent offset of enc data: expected==%llu got=%d\n",
+ nblocks2, HEADER_SECTORS);
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname = strconcat ("g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Get the algorithm string. */
+ algostr = find_tuple (keyblob, KEYBLOB_TAG_ALGOSTR, &algostrlen);
+ if (!algostr || algostrlen > 100)
+ {
+ log_error ("algo string not found in keyblob or too long\n");
+ err = gpg_error (GPG_ERR_INV_DATA);
+ goto leave;
+ }
+
+ /* Get the key. */
+ s = find_tuple (keyblob, KEYBLOB_TAG_ENCKEY, &n);
+ if (!s || n != 16)
+ {
+ if (!s)
+ log_error ("no key found in keyblob\n");
+ else
+ log_error ("unexpected size of key (%zu)\n", n);
+ err = gpg_error (GPG_ERR_INV_KEYLEN);
+ goto leave;
+ }
+ bin2hex (s, 16, hexkey);
+
+ /* Build dmcrypt table. */
+ table = es_bsprintf ("0 %llu crypt %.*s %s 0 %s %d",
+ nblocks, (int)algostrlen, algostr,
+ hexkey, devname, HEADER_SECTORS);
+ wipememory (hexkey, sizeof hexkey);
+ if (!table)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+
+ /* Load the table. */
+ {
+ const char *argv[3];
+
+ argv[0] = "create";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup create %s\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, table, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running dmsetup for '%s': %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+
+
+ leave:
+ wipememory (hexkey, sizeof hexkey);
+ if (table)
+ {
+ wipememory (table, strlen (table));
+ xfree (table);
+ }
+ xfree (targetname);
+ xfree (result);
+ return err;
+}