diff options
author | Werner Koch <[email protected]> | 2016-02-13 16:01:45 +0000 |
---|---|---|
committer | Werner Koch <[email protected]> | 2016-02-13 16:06:54 +0000 |
commit | b0e6ab1109d05fc664f46e17d721fe9b01d38115 (patch) | |
tree | 70cf20d908523ef099962ee90730fd053082b5cf /g13/sh-dmcrypt.c | |
parent | g13: Improve dump_keyblob. (diff) | |
download | gnupg-b0e6ab1109d05fc664f46e17d721fe9b01d38115.tar.gz gnupg-b0e6ab1109d05fc664f46e17d721fe9b01d38115.zip |
g13: Second chunk of code to support dm-crypt.
* g13/be-dmcrypt.c, g13/be-dmcrypt.h: New.
* g13/Makefile.am (g13_SOURCES): Add them.
* g13/backend.c: Include be-dmcrypt.h and call-syshelp.h.
(no_such_backend): Rename to _no_such_backend and provide replacement
macro.
(be_is_supported_conttype): Support DM-Crypt.
(be_take_lock_for_create): Call set_segvice for DM-Crypt.
(be_create_new_keys): Make it a dummy for DM-Crypt.
(be_create_container): Call be_dmcrypt_create_container.
(be_mount_container): call be_dmcrypt_mount_container.
* g13/g13-syshelp.c (main): Enable verbose mode.
* g13/g13tuple.c (get_tupledesc_data): New.
* g13/g13tuple.h (unref_tupledesc): New.
* g13/g13.h (server_control_): Add field "recipients".
* g13/g13.c (main): Fix setting of recipients via cmdline.
(g13_deinit_default_ctrl): Release recipients list.
(g13_request_shutdown): New. Replace all direct update of
shutdown_pending by calls this function.
* g13/server.c (server_local_s): Remove field recipients which is now
part of CTRL.
(reset_notify, cmd_recipient, cmd_create): Adjust for this change.
* g13/create.c (encrypt_keyblob): Rename to g13_encrypt_keyblob.
(g13_create_container): Support DM-Crypt.
* g13/mount.c (parse_header): Allow for meta data copies.
(g13_mount_container): Support DM-Crypt.
* g13/sh-cmd.c (cmd_create): Make it work.
(cmd_mount): New.
* g13/sh-dmcrypt.c (sh_dmcrypt_create_container): Make it work.
(sh_dmcrypt_mount_container): New.
--
With this patch we can now create an encrypted partition and partly
mount it (i.e. setup keys and create the mapped device). We do not yet
create a file system or mount that file system
Signed-off-by: Werner Koch <[email protected]>
Diffstat (limited to 'g13/sh-dmcrypt.c')
-rw-r--r-- | g13/sh-dmcrypt.c | 319 |
1 files changed, 299 insertions, 20 deletions
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; + 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; + + /* 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; */ - /* 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); */ + { + 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; +} |