diff options
-rw-r--r-- | ChangeLog | 5 | ||||
-rw-r--r-- | lib/internal.h | 9 | ||||
-rw-r--r-- | lib/libcryptsetup.h | 2 | ||||
-rw-r--r-- | lib/libdevmapper.c | 28 | ||||
-rw-r--r-- | lib/loopaes/loopaes.c | 2 | ||||
-rw-r--r-- | lib/setup.c | 43 | ||||
-rw-r--r-- | lib/utils.c | 25 | ||||
-rw-r--r-- | lib/utils_devpath.c | 77 | ||||
-rw-r--r-- | lib/utils_dm.h | 3 | ||||
-rw-r--r-- | man/cryptsetup.8 | 8 | ||||
-rw-r--r-- | src/cryptsetup.c | 29 | ||||
-rwxr-xr-x | tests/compat-test | 14 |
12 files changed, 199 insertions, 46 deletions
@@ -1,3 +1,8 @@ +2011-07-01 Milan Broz <mbroz@redhat.com> + * Add --shared option for creating non-overlapping crypt segments. + * Add shared flag to libcryptsetup api. + * Fix plain crypt format parameters to include size option (API change). + 2011-06-08 Milan Broz <mbroz@redhat.com> * Fix return code for status command when device doesn't exists. diff --git a/lib/internal.h b/lib/internal.h index 07e7db2..1803462 100644 --- a/lib/internal.h +++ b/lib/internal.h @@ -47,6 +47,7 @@ void set_error(const char *fmt, ...); const char *get_error(void); char *crypt_lookup_dev(const char *dev_id); +int crypt_sysfs_check_crypt_segment(const char *device, uint64_t offset, uint64_t size); int sector_size_for_device(const char *device); int device_read_ahead(const char *dev, uint32_t *read_ahead); @@ -54,13 +55,11 @@ ssize_t write_blockwise(int fd, void *buf, size_t count); ssize_t read_blockwise(int fd, void *_buf, size_t count); ssize_t write_lseek_blockwise(int fd, char *buf, size_t count, off_t offset); int device_ready(struct crypt_device *cd, const char *device, int mode); -int get_device_infos(const char *device, - int open_exclusive, - int *readonly, - uint64_t *size); + +enum devcheck { DEV_OK = 0, DEV_EXCL = 1, DEV_SHARED = 2 }; int device_check_and_adjust(struct crypt_device *cd, const char *device, - int open_exclusive, + enum devcheck device_check, uint64_t *size, uint64_t *offset, int *read_only); diff --git a/lib/libcryptsetup.h b/lib/libcryptsetup.h index 08c45fe..5d6128a 100644 --- a/lib/libcryptsetup.h +++ b/lib/libcryptsetup.h @@ -161,6 +161,7 @@ struct crypt_params_plain { const char *hash; /* password hash function */ uint64_t offset; /* offset in sectors */ uint64_t skip; /* IV initilisation sector */ + uint64_t size; /* size of mapped device or 0 for autodetection */ }; struct crypt_params_luks1 { @@ -375,6 +376,7 @@ int crypt_keyslot_destroy(struct crypt_device *cd, int keyslot); */ #define CRYPT_ACTIVATE_READONLY (1 << 0) #define CRYPT_ACTIVATE_NO_UUID (1 << 1) +#define CRYPT_ACTIVATE_SHARED (1 << 2) /** * Active device runtime attributes diff --git a/lib/libdevmapper.c b/lib/libdevmapper.c index 34b1767..aa61c25 100644 --- a/lib/libdevmapper.c +++ b/lib/libdevmapper.c @@ -206,7 +206,7 @@ void dm_exit(void) } /* Return path to DM device */ -char *dm_device_path(int major, int minor) +char *dm_device_path(const char *prefix, int major, int minor) { struct dm_task *dmt; const char *name; @@ -222,7 +222,7 @@ char *dm_device_path(int major, int minor) return NULL; } - if (snprintf(path, sizeof(path), "/dev/mapper/%s", name) < 0) + if (snprintf(path, sizeof(path), "%s%s", prefix ?: "", name) < 0) path[0] = '\0'; dm_task_destroy(dmt); @@ -764,3 +764,27 @@ int dm_is_dm_kernel_name(const char *name) { return strncmp(name, "dm-", 3) ? 0 : 1; } + +int dm_check_segment(const char *name, uint64_t offset, uint64_t size) +{ + uint64_t seg_size, seg_offset; + int r; + + log_dbg("Checking segments for device %s.", name); + + r = dm_query_device(name, NULL, &seg_size, NULL, &seg_offset, + NULL, NULL, NULL, NULL, NULL, NULL); + if (r < 0) + return r; + + if (offset >= (seg_offset + seg_size) || (offset + size) <= seg_offset) + r = 0; + else + r = -EBUSY; + + log_dbg("seg: %" PRIu64 " - %" PRIu64 ", new %" PRIu64 " - %" PRIu64 "%s", + seg_offset, seg_offset + seg_size, offset, offset + size, + r ? " (overlapping)" : " (ok)"); + + return r; +} diff --git a/lib/loopaes/loopaes.c b/lib/loopaes/loopaes.c index 7fdd56c..e6fe827 100644 --- a/lib/loopaes/loopaes.c +++ b/lib/loopaes/loopaes.c @@ -200,7 +200,7 @@ int LOOPAES_activate(struct crypt_device *cd, device = crypt_get_device_name(cd); read_only = flags & CRYPT_ACTIVATE_READONLY; - r = device_check_and_adjust(cd, device, 1, &size, &offset, &read_only); + r = device_check_and_adjust(cd, device, DEV_EXCL, &size, &offset, &read_only); if (r) return r; diff --git a/lib/setup.c b/lib/setup.c index 4da7b81..550928d 100644 --- a/lib/setup.c +++ b/lib/setup.c @@ -357,17 +357,26 @@ static int create_device_helper(struct crypt_device *cd, uint64_t skip, uint64_t offset, const char *uuid, - int read_only, + uint32_t activation_flags, int reload) { crypt_status_info ci; char *dm_cipher = NULL; char *processed_key = NULL; - int r; + enum devcheck device_check; + int r, read_only; if (!name) return -EINVAL; + read_only = activation_flags & CRYPT_ACTIVATE_READONLY ? 1 : 0; + if (reload) + device_check = DEV_OK; + else if (activation_flags & CRYPT_ACTIVATE_SHARED) + device_check = DEV_SHARED; + else + device_check = DEV_EXCL; + ci = crypt_status(cd, name); if (ci == CRYPT_INVALID) return -EINVAL; @@ -385,7 +394,7 @@ static int create_device_helper(struct crypt_device *cd, return -EINVAL; } - r = device_check_and_adjust(cd, cd->device, !reload, &size, &offset, &read_only); + r = device_check_and_adjust(cd, cd->device, device_check, &size, &offset, &read_only); if (r) return r; @@ -424,7 +433,7 @@ static int open_from_hdr_and_vk(struct crypt_device *cd, read_only = flags & CRYPT_ACTIVATE_READONLY; no_uuid = flags & CRYPT_ACTIVATE_NO_UUID; - r = device_check_and_adjust(cd, cd->device, 1, &size, &offset, &read_only); + r = device_check_and_adjust(cd, cd->device, DEV_EXCL, &size, &offset, &read_only); if (r) return r; @@ -625,8 +634,12 @@ static int crypt_create_and_update_device(struct crypt_options *options, int upd struct crypt_device *cd = NULL; char *passphrase = NULL; size_t passphrase_size = 0; + uint32_t activation_flags; int r; + activation_flags = options->flags & CRYPT_FLAG_READONLY ? + CRYPT_ACTIVATE_READONLY : 0; + r = _crypt_init(&cd, CRYPT_PLAIN, options, 0, 1); if (r) return r; @@ -639,8 +652,7 @@ static int crypt_create_and_update_device(struct crypt_options *options, int upd options->cipher, NULL, options->key_file, passphrase, passphrase_size, options->key_size, options->size, options->skip, - options->offset, NULL, options->flags & CRYPT_FLAG_READONLY, - update); + options->offset, NULL, activation_flags, update); crypt_safe_free(passphrase); crypt_free(cd); @@ -697,7 +709,7 @@ int crypt_resize_device(struct crypt_options *options) goto out; size = options->size; - r = device_check_and_adjust(cd, device, 0, &size, &offset, &read_only); + r = device_check_and_adjust(cd, device, DEV_OK, &size, &offset, &read_only); if (r) goto out; @@ -1226,6 +1238,7 @@ static int _crypt_format_plain(struct crypt_device *cd, cd->plain_hdr.offset = params ? params->offset : 0; cd->plain_hdr.skip = params ? params->skip : 0; + cd->plain_hdr.size = params ? params->size : 0; if (!cd->plain_cipher || !cd->plain_cipher_mode) return -ENOMEM; @@ -1425,7 +1438,7 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size) goto out; } - r = device_check_and_adjust(cd, device, 0, &new_size, &offset, &read_only); + r = device_check_and_adjust(cd, device, DEV_OK, &new_size, &offset, &read_only); if (r) goto out; @@ -1945,10 +1958,9 @@ int crypt_activate_by_passphrase(struct crypt_device *cd, r = create_device_helper(cd, name, cd->plain_hdr.hash, cd->plain_cipher, cd->plain_cipher_mode, NULL, passphrase, passphrase_size, - cd->volume_key->keylength, 0, + cd->volume_key->keylength, cd->plain_hdr.size, cd->plain_hdr.skip, cd->plain_hdr.offset, - cd->plain_uuid, - flags & CRYPT_ACTIVATE_READONLY, 0); + cd->plain_uuid, flags, 0); keyslot = 0; } else if (isLUKS(cd->type)) { /* provided passphrase, do not retry */ @@ -2011,10 +2023,9 @@ int crypt_activate_by_keyfile(struct crypt_device *cd, r = create_device_helper(cd, name, cd->plain_hdr.hash, cd->plain_cipher, cd->plain_cipher_mode, NULL, passphrase_read, passphrase_size_read, - cd->volume_key->keylength, 0, + cd->volume_key->keylength, cd->plain_hdr.size, cd->plain_hdr.skip, cd->plain_hdr.offset, - cd->plain_uuid, - flags & CRYPT_ACTIVATE_READONLY, 0); + cd->plain_uuid, flags, 0); } else if (isLUKS(cd->type)) { r = key_from_file(cd, _("Enter passphrase: "), &passphrase_read, &passphrase_size_read, keyfile, keyfile_size); @@ -2079,8 +2090,8 @@ int crypt_activate_by_volume_key(struct crypt_device *cd, return create_device_helper(cd, name, NULL, cd->plain_cipher, cd->plain_cipher_mode, NULL, volume_key, volume_key_size, - cd->volume_key->keylength, 0, cd->plain_hdr.skip, - cd->plain_hdr.offset, cd->plain_uuid, flags & CRYPT_ACTIVATE_READONLY, 0); + cd->volume_key->keylength, cd->plain_hdr.size, cd->plain_hdr.skip, + cd->plain_hdr.offset, cd->plain_uuid, flags, 0); } if (!isLUKS(cd->type)) { diff --git a/lib/utils.c b/lib/utils.c index 9c555d1..19fff5d 100644 --- a/lib/utils.c +++ b/lib/utils.c @@ -336,10 +336,8 @@ int device_ready(struct crypt_device *cd, const char *device, int mode) return r; } -int get_device_infos(const char *device, - int open_exclusive, - int *readonly, - uint64_t *size) +static int get_device_infos(const char *device, enum devcheck device_check, + int *readonly, uint64_t *size) { struct stat st; unsigned long size_small; @@ -353,7 +351,7 @@ int get_device_infos(const char *device, return -EINVAL; /* never wipe header on mounted device */ - if (open_exclusive && S_ISBLK(st.st_mode)) + if (device_check == DEV_EXCL && S_ISBLK(st.st_mode)) flags |= O_EXCL; /* Try to open read-write to check whether it is a read-only device */ @@ -363,7 +361,7 @@ int get_device_infos(const char *device, fd = open(device, O_RDONLY | flags); } - if (fd == -1 && open_exclusive && errno == EBUSY) + if (fd == -1 && device_check == DEV_EXCL && errno == EBUSY) return -EBUSY; if (fd == -1) @@ -396,7 +394,7 @@ out: int device_check_and_adjust(struct crypt_device *cd, const char *device, - int open_exclusive, + enum devcheck device_check, uint64_t *size, uint64_t *offset, int *read_only) @@ -407,7 +405,7 @@ int device_check_and_adjust(struct crypt_device *cd, if (!device) return -ENOTBLK; - r = get_device_infos(device, open_exclusive, &real_readonly, &real_size); + r = get_device_infos(device, device_check, &real_readonly, &real_size); if (r < 0) { if (r == -EBUSY) log_err(cd, _("Cannot use device %s which is in use " @@ -432,6 +430,17 @@ int device_check_and_adjust(struct crypt_device *cd, *size -= *offset; } + if (device_check == DEV_SHARED) { + log_dbg("Checking crypt segments for device %s.", device); + r = crypt_sysfs_check_crypt_segment(device, *offset, *size); + if (r < 0) { + log_err(cd, "Cannot use device %s (crypt segments " + "overlaps or in use by another device).\n", + device); + return r; + } + } + if (real_readonly) *read_only = 1; diff --git a/lib/utils_devpath.c b/lib/utils_devpath.c index f78a36e..2844f35 100644 --- a/lib/utils_devpath.c +++ b/lib/utils_devpath.c @@ -24,11 +24,14 @@ #include <stdlib.h> #include <unistd.h> #include <dirent.h> +#include <fcntl.h> +#include <errno.h> #include <sys/stat.h> #include <sys/types.h> #include "utils_dm.h" char *crypt_lookup_dev(const char *dev_id); +int crypt_sysfs_check_crypt_segment(const char *device, uint64_t offset, uint64_t size); static char *__lookup_dev(char *path, dev_t dev, int dir_level, const int max_level) { @@ -146,7 +149,7 @@ char *crypt_lookup_dev(const char *dev_id) devname++; if (dm_is_dm_kernel_name(devname)) - devpath = dm_device_path(major, minor); + devpath = dm_device_path("/dev/mapper/", major, minor); else if (snprintf(path, sizeof(path), "/dev/%s", devname) > 0) devpath = strdup(path); @@ -163,3 +166,75 @@ char *crypt_lookup_dev(const char *dev_id) return devpath; } + +static int crypt_sysfs_get_major_minor(const char *kname, int *major, int *minor) +{ + char path[PATH_MAX], tmp[64]; + int fd, r = 0; + + if (snprintf(path, sizeof(path), "/sys/block/%s/dev", kname) < 0) + return 0; + + if ((fd = open(path, O_RDONLY)) < 0) + return 0; + r = read(fd, tmp, sizeof(tmp)); + close(fd); + + if (r <= 0 || sscanf(tmp, "%d:%d", major, minor) != 2) + return 0; + + return 1; +} + +static int crypt_sysfs_get_holders_dir(const char *device, char *path, int size) +{ + struct stat st; + + if (stat(device, &st) < 0 || !S_ISBLK(st.st_mode)) + return 0; + + if (snprintf(path, size, "/sys/dev/block/%d:%d/holders", + major(st.st_rdev), minor(st.st_rdev)) < 0) + return 0; + + return 1; +} + +int crypt_sysfs_check_crypt_segment(const char *device, uint64_t offset, uint64_t size) +{ + DIR *dir; + struct dirent *d; + char path[PATH_MAX], *dmname; + int major, minor, r = 0; + + if (!crypt_sysfs_get_holders_dir(device, path, sizeof(path))) + return -EINVAL; + + if (!(dir = opendir(path))) + return -EINVAL; + + while (!r && (d = readdir(dir))) { + if (!strcmp(d->d_name, ".") || !strcmp(d->d_name, "..")) + continue; + + if (!dm_is_dm_kernel_name(d->d_name)) { + r = -EBUSY; + break; + } + + if (!crypt_sysfs_get_major_minor(d->d_name, &major, &minor)) { + r = -EINVAL; + break; + } + + if (!(dmname = dm_device_path(NULL, major, minor))) { + r = -EINVAL; + break; + } + r = dm_check_segment(dmname, offset, size); + free(dmname); + } + closedir(dir); + + return r; +} diff --git a/lib/utils_dm.h b/lib/utils_dm.h index 585df44..59b29a8 100644 --- a/lib/utils_dm.h +++ b/lib/utils_dm.h @@ -38,8 +38,9 @@ int dm_suspend_and_wipe_key(const char *name); int dm_resume_and_reinstate_key(const char *name, size_t key_size, const char *key); -char *dm_device_path(int major, int minor); +char *dm_device_path(const char *prefix, int major, int minor); int dm_is_dm_device(int major, int minor); int dm_is_dm_kernel_name(const char *name); +int dm_check_segment(const char *name, uint64_t offset, uint64_t size); #endif /* _UTILS_DM_H */ diff --git a/man/cryptsetup.8 b/man/cryptsetup.8 index 18ca603..fd8c329 100644 --- a/man/cryptsetup.8 +++ b/man/cryptsetup.8 @@ -14,7 +14,7 @@ For basic (plain) dm-crypt mappings, there are four operations. creates a mapping with <name> backed by device <device>. \fB<options>\fR can be [\-\-hash, \-\-cipher, \-\-verify-passphrase, -\-\-key-file, \-\-key-size, \-\-offset, \-\-skip, \-\-size, \-\-readonly] +\-\-key-file, \-\-key-size, \-\-offset, \-\-skip, \-\-size, \-\-readonly, \-\-shared] .PP \fIremove\fR <name> .IP @@ -319,6 +319,12 @@ This option is only relevant for \fIcreate\fR and \fIloopaesOpen\fR action. .B "\-\-readonly" set up a read-only mapping. .TP +.B "\-\-shared" +create another non-overlapping mapping to one common ciphertext device, +e.g. to create hidden device inside another encrypted device. +This option is only relevant for \fIcreate\fR action. +Use \-\-offset, \-\-size and \-\-skip to specify mapped area. +.TP .B "\-\-iter-time, \-i" The number of milliseconds to spend with PBKDF2 password processing. This option is only relevant to the LUKS operations as diff --git a/src/cryptsetup.c b/src/cryptsetup.c index 21182f0..f9e719d 100644 --- a/src/cryptsetup.c +++ b/src/cryptsetup.c @@ -61,6 +61,7 @@ static int opt_align_payload = 0; static int opt_random = 0; static int opt_urandom = 0; static int opt_dump_master_key = 0; +static int opt_shared = 0; static const char **action_argv; static int action_argc; @@ -224,10 +225,12 @@ static int action_create(int arg __attribute__((unused))) .hash = opt_hash ?: DEFAULT_PLAIN_HASH, .skip = opt_skip, .offset = opt_offset, + .size = opt_size, }; char *password = NULL; size_t passwordLen; size_t key_size = (opt_key_size ?: DEFAULT_PLAIN_KEYBITS) / 8; + uint32_t activate_flags = 0; int r; if (params.hash && !strcmp(params.hash, "plain")) @@ -262,11 +265,17 @@ static int action_create(int arg __attribute__((unused))) if (r < 0) goto out; + if (opt_readonly) + activate_flags |= CRYPT_ACTIVATE_READONLY; + + if (opt_shared) + activate_flags |= CRYPT_ACTIVATE_SHARED; + if (opt_key_file) /* With hashing, read the whole keyfile */ r = crypt_activate_by_keyfile(cd, action_argv[0], CRYPT_ANY_SLOT, opt_key_file, params.hash ? 0 : key_size, - opt_readonly ? CRYPT_ACTIVATE_READONLY : 0); + activate_flags); else { r = crypt_get_key(_("Enter passphrase: "), &password, &passwordLen, opt_keyfile_size, @@ -277,17 +286,8 @@ static int action_create(int arg __attribute__((unused))) goto out; r = crypt_activate_by_passphrase(cd, action_argv[0], - CRYPT_ANY_SLOT, password, passwordLen, - opt_readonly ? CRYPT_ACTIVATE_READONLY : 0); + CRYPT_ANY_SLOT, password, passwordLen, activate_flags); } - - /* FIXME: workaround, new api missing format parameter for size. - * Properly fix it after bumping library version, - * add start_offset and size into "PLAIN" format specifiers. - */ - if (r >= 0 && opt_size) - r = crypt_resize(cd, action_argv[0], opt_size); - out: crypt_free(cd); crypt_safe_free(password); @@ -1137,6 +1137,7 @@ int main(int argc, const char **argv) { "header-backup-file",'\0', POPT_ARG_STRING, &opt_header_backup_file, 0, N_("File with LUKS header and keyslots backup."), NULL }, { "use-random", '\0', POPT_ARG_NONE, &opt_random, 0, N_("Use /dev/random for generating volume key."), NULL }, { "use-urandom", '\0', POPT_ARG_NONE, &opt_urandom, 0, N_("Use /dev/urandom for generating volume key."), NULL }, + { "shared", '\0', POPT_ARG_NONE, &opt_shared, 0, N_("Share device with another non-overlapping crypt segment."), NULL }, { "uuid", '\0', POPT_ARG_STRING, &opt_uuid, 0, N_("UUID for device to use."), NULL }, POPT_TABLEEND }; @@ -1218,6 +1219,12 @@ int main(int argc, const char **argv) /* FIXME: rewrite this from scratch */ + if (opt_shared && strcmp(aname, "create")) { + usage(popt_context, EXIT_FAILURE, + _("Option --shared is allowed only for create operation.\n"), + poptGetInvocationName(popt_context)); + } + if (opt_key_size && strcmp(aname, "luksFormat") && strcmp(aname, "create") && diff --git a/tests/compat-test b/tests/compat-test index fdd8e49..291c1bf 100755 --- a/tests/compat-test +++ b/tests/compat-test @@ -4,6 +4,7 @@ CRYPTSETUP=../src/cryptsetup DEV_NAME=dummy DEV_NAME2=dummy2 +DEV_NAME3=dummy3 ORIG_IMG=luks-test-orig IMG=luks-test KEY1=key1 @@ -24,6 +25,7 @@ LOOPDEV=$(losetup -f 2>/dev/null) function remove_mapping() { + [ -b /dev/mapper/$DEV_NAME3 ] && dmsetup remove $DEV_NAME3 [ -b /dev/mapper/$DEV_NAME2 ] && dmsetup remove $DEV_NAME2 [ -b /dev/mapper/$DEV_NAME ] && dmsetup remove $DEV_NAME losetup -d $LOOPDEV >/dev/null 2>&1 @@ -355,5 +357,17 @@ echo "key0" | $CRYPTSETUP luksRemoveKey $LOOPDEV -l 2 2>/dev/null && fail echo "key01" | $CRYPTSETUP luksRemoveKey $LOOPDEV -d 4 2>/dev/null && fail echo -e "key0\n" | $CRYPTSETUP luksRemoveKey $LOOPDEV -d- -l 4 || fail +prepare "[25] Create non-overlapping segments" wipe +echo "key0" | $CRYPTSETUP create $DEV_NAME $LOOPDEV --offset 0 --size 256 || fail +echo "key0" | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --offset 512 --size 256 2>/dev/null && fail +echo "key0" | $CRYPTSETUP create $DEV_NAME2 $LOOPDEV --offset 512 --size 256 --shared || fail +echo "key0" | $CRYPTSETUP create $DEV_NAME3 $LOOPDEV --offset 255 --size 256 --shared 2>/dev/null && fail +echo "key0" | $CRYPTSETUP create $DEV_NAME3 $LOOPDEV --offset 256 --size 257 --shared 2>/dev/null && fail +echo "key0" | $CRYPTSETUP create $DEV_NAME3 $LOOPDEV --offset 256 --size 1024 --shared 2>/dev/null && fail +echo "key0" | $CRYPTSETUP create $DEV_NAME3 $LOOPDEV --offset 256 --size 256 --shared || fail +$CRYPTSETUP -q remove $DEV_NAME3 || fail +$CRYPTSETUP -q remove $DEV_NAME2 || fail +$CRYPTSETUP -q remove $DEV_NAME || fail + remove_mapping exit 0 |