summaryrefslogtreecommitdiffstats
path: root/g13
diff options
context:
space:
mode:
Diffstat (limited to 'g13')
-rw-r--r--g13/Makefile.am3
-rw-r--r--g13/backend.c41
-rw-r--r--g13/backend.h4
-rw-r--r--g13/be-dmcrypt.c35
-rw-r--r--g13/be-dmcrypt.h3
-rw-r--r--g13/call-syshelp.c86
-rw-r--r--g13/call-syshelp.h3
-rw-r--r--g13/g13-syshelp.h4
-rw-r--r--g13/g13.c42
-rw-r--r--g13/keyblob.c229
-rw-r--r--g13/keyblob.h9
-rw-r--r--g13/mount.c196
-rw-r--r--g13/mount.h2
-rw-r--r--g13/server.c55
-rw-r--r--g13/sh-cmd.c117
-rw-r--r--g13/sh-dmcrypt.c241
-rw-r--r--g13/suspend.c143
-rw-r--r--g13/suspend.h26
18 files changed, 1029 insertions, 210 deletions
diff --git a/g13/Makefile.am b/g13/Makefile.am
index 4244a747a..05963c850 100644
--- a/g13/Makefile.am
+++ b/g13/Makefile.am
@@ -35,11 +35,12 @@ AM_CFLAGS = $(LIBGCRYPT_CFLAGS) $(LIBASSUAN_CFLAGS) $(NPTH_CFLAGS)
g13_SOURCES = \
g13.c g13.h \
g13-common.c g13-common.h \
- keyblob.h \
+ keyblob.c keyblob.h \
g13tuple.c g13tuple.h \
server.c server.h \
create.c create.h \
mount.c mount.h \
+ suspend.c suspend.h \
mountinfo.c mountinfo.h \
call-syshelp.c call-syshelp.h \
runner.c runner.h \
diff --git a/g13/backend.c b/g13/backend.c
index bb7bfcc02..dd2176834 100644
--- a/g13/backend.c
+++ b/g13/backend.c
@@ -198,7 +198,7 @@ be_create_new_keys (int conttype, membuf_t *mb)
}
-/* Dispatcher to the backend's create function. */
+/* 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,
@@ -220,7 +220,7 @@ be_create_container (ctrl_t ctrl, int conttype,
}
-/* Dispatcher to the backend's mount function. */
+/* Dispatcher to the backend's mount function. */
gpg_error_t
be_mount_container (ctrl_t ctrl, int conttype,
const char *fname, const char *mountpoint,
@@ -238,3 +238,40 @@ be_mount_container (ctrl_t ctrl, int conttype,
return no_such_backend (conttype);
}
}
+
+
+/* Dispatcher to the backend's suspend function. */
+gpg_error_t
+be_suspend_container (ctrl_t ctrl, int conttype, const char *fname)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_suspend_container (ctrl, fname);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
+
+
+/* Dispatcher to the backend's resume function. */
+gpg_error_t
+be_resume_container (ctrl_t ctrl, int conttype, const char *fname,
+ tupledesc_t tuples)
+{
+ switch (conttype)
+ {
+ case CONTTYPE_ENCFS:
+ return gpg_error (GPG_ERR_NOT_SUPPORTED);
+
+ case CONTTYPE_DM_CRYPT:
+ return be_dmcrypt_resume_container (ctrl, fname, tuples);
+
+ default:
+ return no_such_backend (conttype);
+ }
+}
diff --git a/g13/backend.h b/g13/backend.h
index 0f391d0ab..66d9cd593 100644
--- a/g13/backend.h
+++ b/g13/backend.h
@@ -39,6 +39,10 @@ gpg_error_t be_mount_container (ctrl_t ctrl, int conttype,
const char *fname, const char *mountpoint,
tupledesc_t tuples,
unsigned int *r_id);
+gpg_error_t be_suspend_container (ctrl_t ctrl, int conttype,
+ const char *fname);
+gpg_error_t be_resume_container (ctrl_t ctrl, int conttype,
+ const char *fname, tupledesc_t tuples);
#endif /*G13_BACKEND_H*/
diff --git a/g13/be-dmcrypt.c b/g13/be-dmcrypt.c
index 325567633..e5e9b332a 100644
--- a/g13/be-dmcrypt.c
+++ b/g13/be-dmcrypt.c
@@ -62,3 +62,38 @@ be_dmcrypt_mount_container (ctrl_t ctrl,
leave:
return err;
}
+
+
+/* Suspend the container described by the filename FNAME. */
+gpg_error_t
+be_dmcrypt_suspend_container (ctrl_t ctrl, const char *fname)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_set_device (ctrl, fname);
+ if (err)
+ goto leave;
+
+ err = call_syshelp_run_suspend (ctrl, CONTTYPE_DM_CRYPT);
+
+ leave:
+ return err;
+}
+
+
+/* Resume the container described by the filename FNAME and the keyblob
+ * information in TUPLES. */
+gpg_error_t
+be_dmcrypt_resume_container (ctrl_t ctrl, const char *fname, tupledesc_t tuples)
+{
+ gpg_error_t err;
+
+ err = call_syshelp_set_device (ctrl, fname);
+ if (err)
+ goto leave;
+
+ err = call_syshelp_run_resume (ctrl, CONTTYPE_DM_CRYPT, tuples);
+
+ leave:
+ return err;
+}
diff --git a/g13/be-dmcrypt.h b/g13/be-dmcrypt.h
index 152186057..d74e09f7c 100644
--- a/g13/be-dmcrypt.h
+++ b/g13/be-dmcrypt.h
@@ -27,6 +27,9 @@ gpg_error_t be_dmcrypt_mount_container (ctrl_t ctrl,
const char *fname,
const char *mountpoint,
tupledesc_t tuples);
+gpg_error_t be_dmcrypt_suspend_container (ctrl_t ctrl, const char *fname);
+gpg_error_t be_dmcrypt_resume_container (ctrl_t ctrl, const char *fname,
+ tupledesc_t tuples);
#endif /*G13_BE_DMCRYPT_H*/
diff --git a/g13/call-syshelp.c b/g13/call-syshelp.c
index 0e69e9c11..bc93d2014 100644
--- a/g13/call-syshelp.c
+++ b/g13/call-syshelp.c
@@ -337,6 +337,7 @@ mount_status_cb (void *opaque, const char *line)
}
+/* Inquire callback for MOUNT and RESUME. */
static gpg_error_t
mount_inq_cb (void *opaque, const char *line)
{
@@ -363,9 +364,11 @@ mount_inq_cb (void *opaque, const char *line)
}
-/* 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. */
+/*
+ * 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)
@@ -406,3 +409,80 @@ call_syshelp_run_mount (ctrl_t ctrl, int conttype, const char *mountpoint,
leave:
return err;
}
+
+
+
+/*
+ * Run the SUSPEND command on the current device. CONTTYPES gives the
+ * requested content type for the new container.
+ */
+gpg_error_t
+call_syshelp_run_suspend (ctrl_t ctrl, int conttype)
+{
+ gpg_error_t err;
+ assuan_context_t ctx;
+
+ err = start_syshelp (ctrl, &ctx);
+ if (err)
+ goto leave;
+
+ if (conttype == CONTTYPE_DM_CRYPT)
+ {
+ err = assuan_transact (ctx, "SUSPEND dm-crypt",
+ NULL, NULL,
+ NULL, NULL,
+ NULL, NULL);
+ }
+ else
+ {
+ log_error ("invalid backend type %d given\n", conttype);
+ err = GPG_ERR_INTERNAL;
+ goto leave;
+ }
+
+ leave:
+ return err;
+}
+
+
+
+/* Run the RESUME command on the current device. CONTTYPES gives the
+ requested content type for the container. */
+gpg_error_t
+call_syshelp_run_resume (ctrl_t ctrl, int conttype, 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, "RESUME dm-crypt",
+ NULL, NULL,
+ mount_inq_cb, &parm,
+ NULL, NULL);
+ unref_tupledesc (tuples);
+ }
+ else
+ {
+ 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 60cc776a8..c2578f241 100644
--- a/g13/call-syshelp.h
+++ b/g13/call-syshelp.h
@@ -28,6 +28,9 @@ 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);
+gpg_error_t call_syshelp_run_suspend (ctrl_t ctrl, int conttype);
+gpg_error_t call_syshelp_run_resume (ctrl_t ctrl, int conttype,
+ tupledesc_t tuples);
#endif /*GNUPG_G13_CALL_SYSHELP_H*/
diff --git a/g13/g13-syshelp.h b/g13/g13-syshelp.h
index 087fff609..dae2bd05c 100644
--- a/g13/g13-syshelp.h
+++ b/g13/g13-syshelp.h
@@ -86,6 +86,10 @@ 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);
+gpg_error_t sh_dmcrypt_suspend_container (ctrl_t ctrl, const char *devname);
+gpg_error_t sh_dmcrypt_resume_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 b8a2dda6c..4489b2fc8 100644
--- a/g13/g13.c
+++ b/g13/g13.c
@@ -42,6 +42,7 @@
#include "runner.h"
#include "create.h"
#include "mount.h"
+#include "suspend.h"
#include "mountinfo.h"
#include "backend.h"
#include "call-syshelp.h"
@@ -58,6 +59,8 @@ enum cmd_and_opt_values {
aCreate,
aMount,
aUmount,
+ aSuspend,
+ aResume,
aServer,
oOptions,
@@ -109,6 +112,8 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aCreate, "create", N_("Create a new file system container")),
ARGPARSE_c (aMount, "mount", N_("Mount a file system container") ),
ARGPARSE_c (aUmount, "umount", N_("Unmount a file system container") ),
+ ARGPARSE_c (aSuspend, "suspend", N_("Suspend a file system container") ),
+ ARGPARSE_c (aResume, "resume", N_("Resume a file system container") ),
ARGPARSE_c (aServer, "server", N_("Run in server mode")),
ARGPARSE_c (aGPGConfList, "gpgconf-list", "@"),
@@ -490,7 +495,8 @@ main ( int argc, char **argv)
case aServer:
case aMount:
case aUmount:
- /* nokeysetup = 1; */
+ case aSuspend:
+ case aResume:
case aCreate:
set_cmd (&cmd, pargs.r_opt);
break;
@@ -770,6 +776,40 @@ main ( int argc, char **argv)
}
break;
+ case aUmount: /* Unmount a mounted container. */
+ {
+ if (argc != 1)
+ wrong_args ("--umount filename");
+ err = GPG_ERR_NOT_IMPLEMENTED;
+ log_error ("error unmounting container '%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ }
+ break;
+
+ case aSuspend: /* Suspend a container. */
+ {
+ /* Fixme: Should we add a suspend all container option? */
+ if (argc != 1)
+ wrong_args ("--suspend filename");
+ err = g13_suspend_container (&ctrl, argv[0]);
+ if (err)
+ log_error ("error suspending container '%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ }
+ break;
+
+ case aResume: /* Resume a suspended container. */
+ {
+ /* Fixme: Should we add a resume all container option? */
+ if (argc != 1)
+ wrong_args ("--resume filename");
+ err = g13_resume_container (&ctrl, argv[0]);
+ if (err)
+ log_error ("error resuming container '%s': %s <%s>\n",
+ *argv, gpg_strerror (err), gpg_strsource (err));
+ }
+ break;
+
default:
log_error (_("invalid command (there is no implicit command)\n"));
break;
diff --git a/g13/keyblob.c b/g13/keyblob.c
new file mode 100644
index 000000000..cad0c4f80
--- /dev/null
+++ b/g13/keyblob.c
@@ -0,0 +1,229 @@
+/* keyblob.c - Keyblob parser and builder.
+ * Copyright (C) 2009 Free Software Foundation, Inc.
+ * Copyright (C) 2015-2016 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 <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 "../common/sysutils.h"
+#include "../common/call-gpg.h"
+#include "host2net.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 = buf32_to_uint (packet+2);
+ 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 )
+ log_info ("Note: meta data copies in '%s' are ignored\n", filename);
+
+ len = buf32_to_uint (packet+20);
+
+ /* 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 prefix of the keyblob and do some basic parsing. On
+ success returns an open estream file at R_FP and the length of the
+ header at R_HEADERLEN. */
+static gpg_error_t
+read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen)
+{
+ gpg_error_t err;
+ estream_t fp;
+ unsigned char packet[32];
+
+ *r_fp = NULL;
+
+ 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));
+ es_fclose (fp);
+ return err;
+ }
+
+ err = parse_header (filename, packet, 32, r_headerlen);
+ if (err)
+ es_fclose (fp);
+ else
+ *r_fp = fp;
+
+ return err;
+}
+
+
+
+/*
+ * Test whether the container with name FILENAME is a suitable G13
+ * container. This function may even be called on a mounted
+ * container.
+ */
+gpg_error_t
+g13_is_container (ctrl_t ctrl, const char *filename)
+{
+ gpg_error_t err;
+ estream_t fp = NULL;
+ size_t dummy;
+
+ (void)ctrl;
+
+ /* Read just the prefix of the header. */
+ err = read_keyblob_prefix (filename, &fp, &dummy);
+ if (!err)
+ es_fclose (fp);
+ return err;
+}
+
+
+/*
+ * Read the keyblob at FILENAME. The caller should have acquired a
+ * lockfile and checked that the file exists.
+ */
+gpg_error_t
+g13_keyblob_read (const char *filename,
+ void **r_enckeyblob, size_t *r_enckeybloblen)
+{
+ gpg_error_t err;
+ estream_t fp = NULL;
+ size_t headerlen = 0;
+ size_t msglen;
+ void *msg = NULL;
+
+ *r_enckeyblob = NULL;
+ *r_enckeybloblen = 0;
+
+ err = read_keyblob_prefix (filename, &fp, &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.
+ */
+gpg_error_t
+g13_keyblob_decrypt (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, opt.gpg_program, opt.gpg_arguments,
+ enckeyblob, enckeybloblen,
+ r_keyblob, r_keybloblen);
+
+ return err;
+}
diff --git a/g13/keyblob.h b/g13/keyblob.h
index 7540e4bfa..3415e9aa1 100644
--- a/g13/keyblob.h
+++ b/g13/keyblob.h
@@ -152,5 +152,14 @@
possible to prepend a truecrypt container with our keyblob. */
+
+/*-- keyblob.c --*/
+gpg_error_t g13_is_container (ctrl_t ctrl, const char *filename);
+gpg_error_t g13_keyblob_read (const char *filename,
+ void **r_enckeyblob, size_t *r_enckeybloblen);
+gpg_error_t g13_keyblob_decrypt (ctrl_t ctrl,
+ const void *enckeyblob, size_t enckeybloblen,
+ void **r_keyblob, size_t *r_keybloblen);
+
#endif /*G13_KEYBLOB_H*/
diff --git a/g13/mount.c b/g13/mount.c
index c5c8f22b4..272cd77e0 100644
--- a/g13/mount.c
+++ b/g13/mount.c
@@ -33,178 +33,10 @@
#include "keyblob.h"
#include "backend.h"
#include "g13tuple.h"
-#include "../common/sysutils.h"
-#include "../common/call-gpg.h"
#include "mountinfo.h"
#include "runner.h"
#include "host2net.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 = buf32_to_uint (packet+2);
- 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 )
- log_info ("Note: meta data copies in '%s' are ignored\n", filename);
-
- len = buf32_to_uint (packet+20);
-
- /* 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 prefix of the keyblob and do some basic parsing. On
- success returns an open estream file at R_FP and the length of the
- header at R_HEADERLEN. */
-static gpg_error_t
-read_keyblob_prefix (const char *filename, estream_t *r_fp, size_t *r_headerlen)
-{
- gpg_error_t err;
- estream_t fp;
- unsigned char packet[32];
-
- *r_fp = NULL;
-
- 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));
- es_fclose (fp);
- return err;
- }
-
- err = parse_header (filename, packet, 32, r_headerlen);
- if (err)
- es_fclose (fp);
- else
- *r_fp = fp;
-
- return err;
-}
-
-
-/* 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 = NULL;
- size_t headerlen = 0;
- size_t msglen;
- void *msg = NULL;
-
- *r_enckeyblob = NULL;
- *r_enckeybloblen = 0;
-
- err = read_keyblob_prefix (filename, &fp, &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, opt.gpg_program, opt.gpg_arguments,
- enckeyblob, enckeybloblen,
- r_keyblob, r_keybloblen);
-
- return err;
-}
+#include "../common/sysutils.h"
/* Mount the container with name FILENAME at MOUNTPOINT. */
@@ -285,13 +117,13 @@ 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);
+ err = g13_keyblob_read (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);
+ err = g13_keyblob_decrypt (ctrl, enckeyblob, enckeybloblen,
+ &keyblob, &keybloblen);
if (err)
goto leave;
xfree (enckeyblob);
@@ -386,23 +218,3 @@ g13_umount_container (ctrl_t ctrl, const char *filename, const char *mountpoint)
return 0;
}
-
-
-/* Test whether the container with name FILENAME is a suitable G13
- container. This function may even be called on a mounted
- container. */
-gpg_error_t
-g13_is_container (ctrl_t ctrl, const char *filename)
-{
- gpg_error_t err;
- estream_t fp = NULL;
- size_t dummy;
-
- (void)ctrl;
-
- /* Read just the prefix of the header. */
- err = read_keyblob_prefix (filename, &fp, &dummy);
- if (!err)
- es_fclose (fp);
- return err;
-}
diff --git a/g13/mount.h b/g13/mount.h
index b2fe99e59..003768202 100644
--- a/g13/mount.h
+++ b/g13/mount.h
@@ -27,7 +27,5 @@ gpg_error_t g13_umount_container (ctrl_t ctrl,
const char *filename,
const char *mountpoint);
-gpg_error_t g13_is_container (ctrl_t ctrl, const char *filename);
-
#endif /*G13_MOUNT_H*/
diff --git a/g13/server.c b/g13/server.c
index 2f4acf595..60a69bc6e 100644
--- a/g13/server.c
+++ b/g13/server.c
@@ -30,8 +30,9 @@
#include "i18n.h"
#include "keyblob.h"
#include "server.h"
-#include "mount.h"
#include "create.h"
+#include "mount.h"
+#include "suspend.h"
/* The filepointer for status message used in non-server mode */
@@ -356,6 +357,56 @@ cmd_umount (assuan_context_t ctx, char *line)
}
+static const char hlp_suspend[] =
+ "SUSPEND\n"
+ "\n"
+ "Suspend the currently set device.";
+static gpg_error_t
+cmd_suspend (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+
+ line = skip_options (line);
+ if (*line)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+
+ /* Perform the suspend operation. */
+ err = g13_suspend_container (ctrl, ctrl->server_local->containername);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_resume[] =
+ "RESUME\n"
+ "\n"
+ "Resume the currently set device.";
+static gpg_error_t
+cmd_resume (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err;
+
+ line = skip_options (line);
+ if (*line)
+ {
+ err = gpg_error (GPG_ERR_ASS_SYNTAX);
+ goto leave;
+ }
+
+ /* Perform the suspend operation. */
+ err = g13_resume_container (ctrl, ctrl->server_local->containername);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
static const char hlp_recipient[] =
"RECIPIENT <userID>\n"
"\n"
@@ -543,6 +594,8 @@ register_commands (assuan_context_t ctx)
{ "OPEN", cmd_open, hlp_open },
{ "MOUNT", cmd_mount, hlp_mount},
{ "UMOUNT", cmd_umount, hlp_umount },
+ { "SUSPEND", cmd_suspend, hlp_suspend },
+ { "RESUME", cmd_resume, hlp_resume },
{ "RECIPIENT", cmd_recipient, hlp_recipient },
{ "SIGNER", cmd_signer, hlp_signer },
{ "CREATE", cmd_create, hlp_create },
diff --git a/g13/sh-cmd.c b/g13/sh-cmd.c
index 6ba4cd8c8..fe596f460 100644
--- a/g13/sh-cmd.c
+++ b/g13/sh-cmd.c
@@ -379,6 +379,121 @@ cmd_mount (assuan_context_t ctx, char *line)
}
+static const char hlp_suspend[] =
+ "SUSPEND <type>\n"
+ "\n"
+ "Suspend an encrypted partition and wipe the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_suspend (assuan_context_t ctx, char *line)
+{
+ ctrl_t ctrl = assuan_get_pointer (ctx);
+ gpg_error_t err = 0;
+
+ 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)
+ {
+ err = gpg_error (GPG_ERR_ENODEV);
+ assuan_set_error (ctx, err, "Partition is empty");
+ goto leave;
+ }
+ err = 0;
+
+ err = sh_dmcrypt_suspend_container (ctrl, ctrl->server_local->devicename);
+
+ leave:
+ return leave_cmd (ctx, err);
+}
+
+
+static const char hlp_resume[] =
+ "RESUME <type>\n"
+ "\n"
+ "Resume an encrypted partition and set the key.\n"
+ "<type> must be \"dm-crypt\" for now.";
+static gpg_error_t
+cmd_resume (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);
+
+ 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)
+ {
+ 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)
+ {
+ 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_resume_container (ctrl,
+ ctrl->server_local->devicename,
+ tuples);
+
+ leave:
+ xfree (tuples);
+ destroy_tupledesc (tuples);
+ return leave_cmd (ctx, err);
+}
+
+
static const char hlp_getinfo[] =
"GETINFO <what>\n"
"\n"
@@ -476,6 +591,8 @@ register_commands (assuan_context_t ctx, int fail_all)
{ "DEVICE", cmd_device, hlp_device },
{ "CREATE", cmd_create, hlp_create },
{ "MOUNT", cmd_mount, hlp_mount },
+ { "SUSPEND", cmd_suspend,hlp_suspend},
+ { "RESUME", cmd_resume, hlp_resume },
{ "INPUT", NULL },
{ "OUTPUT", NULL },
{ "GETINFO", cmd_getinfo, hlp_getinfo },
diff --git a/g13/sh-dmcrypt.c b/g13/sh-dmcrypt.c
index 9510a8173..e0cd2e1a1 100644
--- a/g13/sh-dmcrypt.c
+++ b/g13/sh-dmcrypt.c
@@ -72,10 +72,13 @@
#endif
-/* Check whether the block device DEVNAME is used by device mapper.
- Returns: 0 if the device is good and not yet used by DM. */
+/*
+ * Check whether the block device DEVNAME is used by device mapper.
+ * If EXPECT_BUSY is set no error message is printed if the device is
+ * busy. Returns: 0 if the device is good and not yet used by DM.
+ */
static gpg_error_t
-check_blockdev (const char *devname)
+check_blockdev (const char *devname, int expect_busy)
{
gpg_error_t err;
struct stat sb;
@@ -147,8 +150,10 @@ check_blockdev (const char *devname)
if (xmajor == devmajor && xminor == devminor)
{
- log_error ("device '%s' (%u:%u) already used by device mapper\n",
- devname, devmajor, devminor);
+ if (!expect_busy)
+ log_error ("device '%s' (%u:%u)"
+ " already in use by device mapper\n",
+ devname, devmajor, devminor);
err = gpg_error (GPG_ERR_EBUSY);
goto leave;
}
@@ -290,7 +295,7 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
}
/* Check that the device is not used by device mapper. */
- err = check_blockdev (devname);
+ err = check_blockdev (devname, 0);
if (err)
goto leave;
@@ -525,7 +530,7 @@ sh_dmcrypt_create_container (ctrl_t ctrl, const char *devname, estream_t devfp)
}
-/* Mount a DM-Crypt congtainer on device DEVNAME taking keys and other
+/* Mount a DM-Crypt container on device DEVNAME taking keys and other
* meta data from KEYBLOB. */
gpg_error_t
sh_dmcrypt_mount_container (ctrl_t ctrl, const char *devname,
@@ -549,7 +554,7 @@ sh_dmcrypt_mount_container (ctrl_t ctrl, const char *devname,
g13_syshelp_i_know_what_i_am_doing ();
/* Check that the device is not yet used by device mapper. */
- err = check_blockdev (devname);
+ err = check_blockdev (devname, 0);
if (err)
goto leave;
@@ -716,3 +721,223 @@ sh_dmcrypt_mount_container (ctrl_t ctrl, const char *devname,
xfree (result);
return err;
}
+
+
+/* Suspend a DM-Crypt container on device DEVNAME and wipe the keys. */
+gpg_error_t
+sh_dmcrypt_suspend_container (ctrl_t ctrl, const char *devname)
+{
+ gpg_error_t err;
+ char *targetname_abs = NULL;
+ const char *targetname;
+ char *result = NULL;
+
+ if (!ctrl->devti)
+ return gpg_error (GPG_ERR_INV_ARG);
+
+ g13_syshelp_i_know_what_i_am_doing ();
+
+ /* Check that the device is used by device mapper. */
+ err = check_blockdev (devname, 1);
+ if (gpg_err_code (err) != GPG_ERR_EBUSY)
+ {
+ log_error ("device '%s' is not used by the device mapper: %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Fixme: Check that this is really a g13 partition. */
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname_abs = strconcat ("/dev/mapper/",
+ "g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname_abs)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ targetname = strrchr (targetname_abs, '/');
+ if (!targetname)
+ BUG ();
+ targetname++;
+
+ /* Send the suspend command. */
+ {
+ const char *argv[3];
+
+ argv[0] = "suspend";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup suspend %s\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running \"dmsetup suspend %s\": %s\n",
+ targetname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ /* Send the wipe key command. */
+ {
+ const char *argv[5];
+
+ argv[0] = "message";
+ argv[1] = targetname;
+ argv[2] = "0";
+ argv[3] = "key wipe";
+ argv[4] = NULL;
+ log_debug ("now running \"dmsetup message %s 0 key wipe\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running \"dmsetup message %s 0 key wipe\": %s\n",
+ targetname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+
+ leave:
+ xfree (targetname_abs);
+ xfree (result);
+ return err;
+}
+
+
+/* Resume a DM-Crypt container on device DEVNAME taking keys and other
+ * meta data from KEYBLOB. */
+gpg_error_t
+sh_dmcrypt_resume_container (ctrl_t ctrl, const char *devname,
+ tupledesc_t keyblob)
+{
+ gpg_error_t err;
+ char *targetname_abs = NULL;
+ const char *targetname;
+ char hexkey[8+16*2+1]; /* 8 is used to prepend "key set ". */
+ char *table = NULL;
+ 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);
+
+ g13_syshelp_i_know_what_i_am_doing ();
+
+ /* Check that the device is used by device mapper. */
+ err = check_blockdev (devname, 1);
+ if (gpg_err_code (err) != GPG_ERR_EBUSY)
+ {
+ log_error ("device '%s' is not used by the device mapper: %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+
+ /* Device mapper needs a name for the device: Take it from the label
+ or use "0". */
+ targetname_abs = strconcat ("/dev/mapper/",
+ "g13-", ctrl->client.uname, "-",
+ ctrl->devti->label? ctrl->devti->label : "0",
+ NULL);
+ if (!targetname_abs)
+ {
+ err = gpg_error_from_syserror ();
+ goto leave;
+ }
+ targetname = strrchr (targetname_abs, '/');
+ if (!targetname)
+ BUG ();
+ targetname++;
+
+ /* 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;
+ }
+ strcpy (hexkey, "key set ");
+ bin2hex (s, 16, hexkey+8);
+
+ /* Send the key */
+ {
+ const char *argv[4];
+
+ argv[0] = "message";
+ argv[1] = targetname;
+ argv[2] = "0";
+ argv[3] = NULL;
+ log_debug ("now running \"dmsetup message %s 0 [key set]\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, hexkey, &result, NULL);
+ }
+ wipememory (hexkey, sizeof hexkey);
+ if (err)
+ {
+ log_error ("error running \"dmsetup message %s 0 [key set]\": %s\n",
+ devname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ /* Send the resume command. */
+ {
+ const char *argv[3];
+
+ argv[0] = "resume";
+ argv[1] = targetname;
+ argv[2] = NULL;
+ log_debug ("now running \"dmsetup resume %s\"\n", targetname);
+ err = gnupg_exec_tool ("/sbin/dmsetup", argv, NULL, &result, NULL);
+ }
+ if (err)
+ {
+ log_error ("error running \"dmsetup resume %s\": %s\n",
+ targetname, gpg_strerror (err));
+ goto leave;
+ }
+ if (result && *result)
+ log_debug ("dmsetup result: %s\n", result);
+ xfree (result);
+ result = NULL;
+
+ leave:
+ wipememory (hexkey, sizeof hexkey);
+ if (table)
+ {
+ wipememory (table, strlen (table));
+ xfree (table);
+ }
+ xfree (targetname_abs);
+ xfree (result);
+ return err;
+}
diff --git a/g13/suspend.c b/g13/suspend.c
new file mode 100644
index 000000000..0532c8bdb
--- /dev/null
+++ b/g13/suspend.c
@@ -0,0 +1,143 @@
+/* suspend.c - Suspend/Resume a crypto container
+ * Copyright (C) 2016 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 <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 "suspend.h"
+
+#include "keyblob.h"
+#include "backend.h"
+#include "g13tuple.h"
+
+
+
+/* Suspend the container with name FILENAME. */
+gpg_error_t
+g13_suspend_container (ctrl_t ctrl, const char *filename)
+{
+ gpg_error_t err;
+ int needs_syshelp;
+
+ /* A quick check to see whether the container exists. */
+ 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 (!needs_syshelp)
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ else
+ err = be_suspend_container (ctrl, CONTTYPE_DM_CRYPT, filename);
+
+ return err;
+}
+
+
+/* Resume the container with name FILENAME. */
+gpg_error_t
+g13_resume_container (ctrl_t ctrl, const char *filename)
+{
+ gpg_error_t err;
+ int needs_syshelp;
+ 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;
+ char *mountpoint_buffer = NULL;
+
+ /* A quick check to see whether the container exists. */
+ 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 (!needs_syshelp)
+ {
+ err = gpg_error (GPG_ERR_NOT_SUPPORTED);
+ goto leave;
+ }
+
+ /* 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 = g13_keyblob_read (filename, &enckeyblob, &enckeybloblen);
+ if (err)
+ goto leave;
+
+ /* Decrypt that keyblob and store it in a tuple descriptor. */
+ err = g13_keyblob_decrypt (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_tupledesc (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_resume_container (ctrl, conttype, filename, tuples);
+
+ leave:
+ destroy_tupledesc (tuples);
+ xfree (keyblob);
+ xfree (enckeyblob);
+ xfree (mountpoint_buffer);
+ return err;
+}
diff --git a/g13/suspend.h b/g13/suspend.h
new file mode 100644
index 000000000..91702eb71
--- /dev/null
+++ b/g13/suspend.h
@@ -0,0 +1,26 @@
+/* suspend.h - Suspend/Resume a crypto container.
+ * Copyright (C) 2016 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 <http://www.gnu.org/licenses/>.
+ */
+
+#ifndef G13_SUSPEND_H
+#define G13_SUSPEND_H
+
+gpg_error_t g13_suspend_container (ctrl_t ctrl, const char *filename);
+gpg_error_t g13_resume_container (ctrl_t ctrl, const char *filename);
+
+#endif /*G13_SUSPEND_H*/