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.
This commit is contained in:
Richard Jones
2010-07-22 11:00:59 +01:00
parent 2fd8c259d3
commit 945e569db6
6 changed files with 311 additions and 23 deletions

2
TODO
View File

@@ -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).

View File

@@ -33,6 +33,49 @@ optgroup_luks_available (void)
return prog_exists ("cryptsetup");
}
/* Callers must also call remove_temp (tempfile). */
static char *
write_key_to_temp (const char *key)
{
char *tempfile = strdup ("/tmp/luksXXXXXX");
if (!tempfile) {
reply_with_perror ("strdup");
return NULL;
}
int fd = mkstemp (tempfile);
if (fd == -1) {
reply_with_perror ("mkstemp");
goto error;
}
size_t len = strlen (key);
if (xwrite (fd, key, len) == -1) {
reply_with_perror ("write");
close (fd);
goto error;
}
if (close (fd) == -1) {
reply_with_perror ("close");
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)
@@ -49,26 +92,9 @@ luks_open (const char *device, const char *key, const char *mapname,
return -1;
}
char tempfile[] = "/tmp/luksXXXXXX";
int fd = mkstemp (tempfile);
if (fd == -1) {
reply_with_perror ("mkstemp");
char *tempfile = write_key_to_temp (key);
if (!tempfile)
return -1;
}
len = strlen (key);
if (xwrite (fd, key, len) == -1) {
reply_with_perror ("write");
close (fd);
unlink (tempfile);
return -1;
}
if (close (fd) == -1) {
reply_with_perror ("close");
unlink (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;
}

View File

@@ -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 \

88
regressions/test-luks.sh Executable file
View File

@@ -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

View File

@@ -1 +1 @@
259
263

View File

@@ -4916,6 +4916,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