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; +}