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

git-svn-id: https://cryptsetup.googlecode.com/svn/trunk@559 36d66b0a-2a48-0410-832c-cd162a569da5
This commit is contained in:
Milan Broz
2011-07-01 16:38:58 +00:00
parent 5b8fb6f135
commit d44d07c9eb
12 changed files with 199 additions and 46 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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") &&

View File

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