diff options
Diffstat (limited to 'g13')
-rw-r--r-- | g13/Makefile.am | 3 | ||||
-rw-r--r-- | g13/backend.c | 41 | ||||
-rw-r--r-- | g13/backend.h | 4 | ||||
-rw-r--r-- | g13/be-dmcrypt.c | 35 | ||||
-rw-r--r-- | g13/be-dmcrypt.h | 3 | ||||
-rw-r--r-- | g13/call-syshelp.c | 86 | ||||
-rw-r--r-- | g13/call-syshelp.h | 3 | ||||
-rw-r--r-- | g13/g13-syshelp.h | 4 | ||||
-rw-r--r-- | g13/g13.c | 42 | ||||
-rw-r--r-- | g13/keyblob.c | 229 | ||||
-rw-r--r-- | g13/keyblob.h | 9 | ||||
-rw-r--r-- | g13/mount.c | 196 | ||||
-rw-r--r-- | g13/mount.h | 2 | ||||
-rw-r--r-- | g13/server.c | 55 | ||||
-rw-r--r-- | g13/sh-cmd.c | 117 | ||||
-rw-r--r-- | g13/sh-dmcrypt.c | 241 | ||||
-rw-r--r-- | g13/suspend.c | 143 | ||||
-rw-r--r-- | g13/suspend.h | 26 |
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*/ @@ -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*/ |