Richard W.M. Jones
2010-Jul-22 11:27 UTC
[Libguestfs] [PATCH] New APIs: Support for creating LUKS and managing keys.
-- Richard Jones, Virtualization Group, Red Hat http://people.redhat.com/~rjones virt-top is 'top' for virtual machines. Tiny program with many powerful monitoring features, net stats, disk stats, logging, etc. http://et.redhat.com/~rjones/virt-top -------------- next part -------------->From ce2052408c16633b2403fc55f658338c40b03c15 Mon Sep 17 00:00:00 2001From: Richard Jones <rjones at redhat.com> Date: Thu, 22 Jul 2010 11:00:59 +0100 Subject: [PATCH] New APIs: Support for creating LUKS and managing keys. This commit adds four APIs for creating new LUKS devices and key management. These are: luks_format Format a LUKS device with the default cipher. luks_format_cipher Format with a chosen cipher. luks_add_key Add another key to an existing device. luks_kill_slot Delete a key from an existing device. This enables all the significant functionality of the cryptsetup luks* commands. Note that you can obtain the UUID of a LUKS device already by using vfs-uuid. This also includes a regression test covering all the LUKS functions. --- TODO | 2 - daemon/luks.c | 204 +++++++++++++++++++++++++++++++++++++++++----- regressions/Makefile.am | 1 + regressions/test-luks.sh | 88 ++++++++++++++++++++ src/MAX_PROC_NR | 2 +- src/generator.ml | 37 ++++++++ 6 files changed, 311 insertions(+), 23 deletions(-) create mode 100755 regressions/test-luks.sh diff --git a/TODO b/TODO index d0196c8..5bce5d9 100644 --- a/TODO +++ b/TODO @@ -365,7 +365,5 @@ groups. If it contains, eg., partitions, you cannot access them. We would like to add: - An easier way to use this from guestfish. - - Ability to create LUKS devices. - - Ability to change LUKS keys on existing devices. - Direct access to the /dev/mapper device (eg. if it contains anything apart from VGs). diff --git a/daemon/luks.c b/daemon/luks.c index f5a0b9d..07aebdd 100644 --- a/daemon/luks.c +++ b/daemon/luks.c @@ -33,43 +33,69 @@ optgroup_luks_available (void) return prog_exists ("cryptsetup"); } -static int -luks_open (const char *device, const char *key, const char *mapname, - int readonly) +/* Callers must also call remove_temp (tempfile). */ +static char * +write_key_to_temp (const char *key) { - /* Sanity check: /dev/mapper/mapname must not exist already. Note - * that the device-mapper control device (/dev/mapper/control) is - * always there, so you can't ever have mapname == "control". - */ - size_t len = strlen (mapname); - char devmapper[len+32]; - snprintf (devmapper, len+32, "/dev/mapper/%s", mapname); - if (access (devmapper, F_OK) == 0) { - reply_with_error ("%s: device already exists", devmapper); - return -1; + char *tempfile = strdup ("/tmp/luksXXXXXX"); + if (!tempfile) { + reply_with_perror ("strdup"); + return NULL; } - char tempfile[] = "/tmp/luksXXXXXX"; int fd = mkstemp (tempfile); if (fd == -1) { reply_with_perror ("mkstemp"); - return -1; + goto error; } - len = strlen (key); + size_t len = strlen (key); if (xwrite (fd, key, len) == -1) { reply_with_perror ("write"); close (fd); - unlink (tempfile); - return -1; + goto error; } if (close (fd) == -1) { reply_with_perror ("close"); - unlink (tempfile); + goto error; + } + + return tempfile; + + error: + unlink (tempfile); + free (tempfile); + return NULL; +} + +static void +remove_temp (char *tempfile) +{ + unlink (tempfile); + free (tempfile); +} + +static int +luks_open (const char *device, const char *key, const char *mapname, + int readonly) +{ + /* Sanity check: /dev/mapper/mapname must not exist already. Note + * that the device-mapper control device (/dev/mapper/control) is + * always there, so you can't ever have mapname == "control". + */ + size_t len = strlen (mapname); + char devmapper[len+32]; + snprintf (devmapper, len+32, "/dev/mapper/%s", mapname); + if (access (devmapper, F_OK) == 0) { + reply_with_error ("%s: device already exists", devmapper); return -1; } + char *tempfile = write_key_to_temp (key); + if (!tempfile) + return -1; + const char *argv[16]; size_t i = 0; @@ -84,7 +110,7 @@ luks_open (const char *device, const char *key, const char *mapname, char *err; int r = commandv (NULL, &err, (const char * const *) argv); - unlink (tempfile); + remove_temp (tempfile); if (r == -1) { reply_with_error ("%s", err); @@ -136,3 +162,141 @@ do_luks_close (const char *device) return 0; } + +static int +luks_format (const char *device, const char *key, int keyslot, + const char *cipher) +{ + char *tempfile = write_key_to_temp (key); + if (!tempfile) + return -1; + + const char *argv[16]; + char keyslot_s[16]; + size_t i = 0; + + argv[i++] = "cryptsetup"; + argv[i++] = "-q"; + if (cipher) { + argv[i++] = "--cipher"; + argv[i++] = cipher; + } + argv[i++] = "--key-slot"; + snprintf (keyslot_s, sizeof keyslot_s, "%d", keyslot); + argv[i++] = keyslot_s; + argv[i++] = "luksFormat"; + argv[i++] = device; + argv[i++] = tempfile; + argv[i++] = NULL; + + char *err; + int r = commandv (NULL, &err, (const char * const *) argv); + remove_temp (tempfile); + + if (r == -1) { + reply_with_error ("%s", err); + free (err); + return -1; + } + + free (err); + + udev_settle (); + + return 0; +} + +int +do_luks_format (const char *device, const char *key, int keyslot) +{ + return luks_format (device, key, keyslot, NULL); +} + +int +do_luks_format_cipher (const char *device, const char *key, int keyslot, + const char *cipher) +{ + return luks_format (device, key, keyslot, cipher); +} + +int +do_luks_add_key (const char *device, const char *key, const char *newkey, + int keyslot) +{ + char *keyfile = write_key_to_temp (key); + if (!keyfile) + return -1; + + char *newkeyfile = write_key_to_temp (newkey); + if (!newkeyfile) { + remove_temp (keyfile); + return -1; + } + + const char *argv[16]; + char keyslot_s[16]; + size_t i = 0; + + argv[i++] = "cryptsetup"; + argv[i++] = "-q"; + argv[i++] = "-d"; + argv[i++] = keyfile; + argv[i++] = "--key-slot"; + snprintf (keyslot_s, sizeof keyslot_s, "%d", keyslot); + argv[i++] = keyslot_s; + argv[i++] = "luksAddKey"; + argv[i++] = device; + argv[i++] = newkeyfile; + argv[i++] = NULL; + + char *err; + int r = commandv (NULL, &err, (const char * const *) argv); + remove_temp (keyfile); + remove_temp (newkeyfile); + + if (r == -1) { + reply_with_error ("%s", err); + free (err); + return -1; + } + + free (err); + + return 0; +} + +int +do_luks_kill_slot (const char *device, const char *key, int keyslot) +{ + char *tempfile = write_key_to_temp (key); + if (!tempfile) + return -1; + + const char *argv[16]; + char keyslot_s[16]; + size_t i = 0; + + argv[i++] = "cryptsetup"; + argv[i++] = "-q"; + argv[i++] = "-d"; + argv[i++] = tempfile; + argv[i++] = "luksKillSlot"; + argv[i++] = device; + snprintf (keyslot_s, sizeof keyslot_s, "%d", keyslot); + argv[i++] = keyslot_s; + argv[i++] = NULL; + + char *err; + int r = commandv (NULL, &err, (const char * const *) argv); + remove_temp (tempfile); + + if (r == -1) { + reply_with_error ("%s", err); + free (err); + return -1; + } + + free (err); + + return 0; +} diff --git a/regressions/Makefile.am b/regressions/Makefile.am index ff00321..e0218bc 100644 --- a/regressions/Makefile.am +++ b/regressions/Makefile.am @@ -34,6 +34,7 @@ TESTS = \ test-cancellation-download-librarycancels.sh \ test-cancellation-upload-daemoncancels.sh \ test-find0.sh \ + test-luks.sh \ test-lvm-filtering.sh \ test-lvm-mapping.pl \ test-noexec-stack.pl \ diff --git a/regressions/test-luks.sh b/regressions/test-luks.sh new file mode 100755 index 0000000..fe42d87 --- /dev/null +++ b/regressions/test-luks.sh @@ -0,0 +1,88 @@ +#!/bin/bash - +# libguestfs +# Copyright (C) 2010 Red Hat Inc. +# +# This program 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 2 of the License, or +# (at your option) any later version. +# +# This program 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, write to the Free Software +# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + +# Test LUKS device creation, opening, key slots. + +set -e + +rm -f test1.img + +../fish/guestfish --keys-from-stdin <<EOF +sparse test1.img 1G +run +part-disk /dev/sda mbr + +# Create LUKS device with key "key0" in slot 0. +luks-format /dev/sda1 0 +key0 + +# Open the device. +luks-open /dev/sda1 lukstest +key0 + +# Put some LVM structures on the encrypted device. +pvcreate /dev/mapper/lukstest +vgcreate VG /dev/mapper/lukstest +lvcreate LV1 VG 64 +lvcreate LV2 VG 64 +vg-activate-all false + +# Close the device. +luks-close /dev/mapper/lukstest + +# Add keys in other slots. +luks-add-key /dev/sda1 1 +key0 +key1 +luks-add-key /dev/sda1 2 +key1 +key2 +luks-add-key /dev/sda1 3 +key2 +key3 + +# Check we can open the device with one of the new keys. +luks-open /dev/sda1 lukstest +key1 +luks-close /dev/mapper/lukstest +luks-open /dev/sda1 lukstest +key3 +luks-close /dev/mapper/lukstest + +# Remove a key. +luks-kill-slot /dev/sda1 1 +key0 + +# This is expected to fail. +-luks-open /dev/sda1 lukstest +key1 + +# Replace a key slot. +luks-kill-slot /dev/sda1 3 +key2 +luks-add-key /dev/sda1 3 +key2 +newkey3 + +luks-open /dev/sda1 lukstest +newkey3 +luks-close /dev/mapper/lukstest + +EOF + +rm -f test1.img diff --git a/src/MAX_PROC_NR b/src/MAX_PROC_NR index 98ecf58..175b6c5 100644 --- a/src/MAX_PROC_NR +++ b/src/MAX_PROC_NR @@ -1 +1 @@ -259 +263 diff --git a/src/generator.ml b/src/generator.ml index 69f561c..3af673d 100755 --- a/src/generator.ml +++ b/src/generator.ml @@ -4918,6 +4918,43 @@ C<device> parameter must be the name of the LUKS mapping device (ie. C</dev/mapper/mapname>) and I<not> the name of the underlying block device."); + ("luks_format", (RErr, [Device "device"; Key "key"; Int "keyslot"]), 260, [Optional "luks"; DangerWillRobinson], + [], + "format a block device as a LUKS encrypted device", + "\ +This command erases existing data on C<device> and formats +the device as a LUKS encrypted device. C<key> is the +initial key, which is added to key slot C<slot>. (LUKS +supports 8 key slots, numbered 0-7)."); + + ("luks_format_cipher", (RErr, [Device "device"; Key "key"; Int "keyslot"; String "cipher"]), 261, [Optional "luks"; DangerWillRobinson], + [], + "format a block device as a LUKS encrypted device", + "\ +This command is the same as C<guestfs_luks_format> but +it also allows you to set the C<cipher> used."); + + ("luks_add_key", (RErr, [Device "device"; Key "key"; Key "newkey"; Int "keyslot"]), 262, [Optional "luks"], + [], + "add a key on a LUKS encrypted device", + "\ +This command adds a new key on LUKS device C<device>. +C<key> is any existing key, and is used to access the device. +C<newkey> is the new key to add. C<keyslot> is the key slot +that will be replaced. + +Note that if C<keyslot> already contains a key, then this +command will fail. You have to use C<guestfs_luks_kill_slot> +first to remove that key."); + + ("luks_kill_slot", (RErr, [Device "device"; Key "key"; Int "keyslot"]), 263, [Optional "luks"], + [], + "remove a key from a LUKS encrypted device", + "\ +This command deletes the key in key slot C<keyslot> from the +encrypted LUKS device C<device>. C<key> must be one of the +I<other> keys."); + ] let all_functions = non_daemon_functions @ daemon_functions -- 1.7.1
Apparently Analagous Threads
- [PATCH 0/5] Add progress notification to upload APIs
- [ANNOUNCE] libguestfs 1.14 released - tools for managing virtual machines and disk images
- Re: [PATCH 0/2] added icat and fls0 APIs for deleted files recovery
- Re: [PATCH 5/7] perl: Properly decode node names, value keys from UTF-8
- [PATCH 0/2] Support for LUKS-encrypted Linux guests