Add kernel keyring functions for volume key.

Code is written by Ondrej Kozina.

This patch adds ability to store volume key in kernel keyring
(feature available in recent kernels) and avoid setting
key through dm-ioctl and avoiding key in table mapping.

Will be used in LUKS2.

Signed-off-by: Milan Broz <gmazyland@gmail.com>
This commit is contained in:
Milan Broz
2017-05-28 14:05:36 +02:00
parent 8a859391be
commit d891e00f63
9 changed files with 645 additions and 19 deletions

View File

@@ -37,11 +37,34 @@ PKG_PROG_PKG_CONFIG
AC_HEADER_DIRENT
AC_HEADER_STDC
AC_CHECK_HEADERS(fcntl.h malloc.h inttypes.h sys/ioctl.h sys/mman.h \
sys/sysmacros.h sys/statvfs.h ctype.h unistd.h locale.h byteswap.h endian.h)
sys/sysmacros.h sys/statvfs.h ctype.h unistd.h locale.h byteswap.h endian.h stdint.h)
AC_CHECK_HEADERS(uuid/uuid.h,,[AC_MSG_ERROR([You need the uuid library.])])
AC_CHECK_HEADER(libdevmapper.h,,[AC_MSG_ERROR([You need the device-mapper library.])])
AC_ARG_ENABLE(keyring, AS_HELP_STRING([--disable-keyring],[disable kernel keyring support and builtin kernel keyring token]),[], [enable_keyring=yes])
if test "x$enable_keyring" = "xyes"; then
AC_CHECK_HEADERS(linux/keyctl.h,,[AC_MSG_ERROR([You need Linux kernel headers with kernel keyring service compiled.])])
dnl ==========================================================================
dnl check whether kernel is compiled with kernel keyring service syscalls
AC_CHECK_DECL(__NR_add_key,,[AC_MSG_ERROR([The kernel is missing add_key syscall.])], [#include <syscall.h>])
AC_CHECK_DECL(__NR_keyctl,,[AC_MSG_ERROR([The kernel is missing keyctl syscall.])], [#include <syscall.h>])
AC_CHECK_DECL(__NR_request_key,,[AC_MSG_ERROR([The kernel is missing request_key syscall.])], [#include <syscall.h>])
dnl ==========================================================================
dnl check that key_serial_t hasn't been adopted yet in stdlib
AC_CHECK_TYPES([key_serial_t], [], [], [
AC_INCLUDES_DEFAULT
#ifdef HAVE_LINUX_KEYCTL_H
# include <linux/keyctl.h>
#endif
])
AC_DEFINE(KERNEL_KEYRING, 1, [Enable kernel keyring service support])
fi
AM_CONDITIONAL(KERNEL_KEYRING, test x$enable_keyring = xyes)
saved_LIBS=$LIBS
AC_CHECK_LIB(uuid, uuid_clear, ,[AC_MSG_ERROR([You need the uuid library.])])
AC_SUBST(UUID_LIBS, $LIBS)

View File

@@ -61,6 +61,8 @@ libcryptsetup_la_SOURCES = \
utils_fips.c \
utils_fips.h \
utils_device.c \
utils_keyring.c \
utils_keyring.h \
libdevmapper.c \
utils_dm.h \
volumekey.c \

View File

@@ -371,10 +371,21 @@ static void hex_key(char *hexkey, size_t key_size, const char *key)
sprintf(&hexkey[i * 2], "%02x", (unsigned char)key[i]);
}
/* get string length for key_size written in decimal system */
static size_t get_key_size_strlen(size_t key_size)
{
size_t ret = 1;
while ((key_size /= 10))
ret++;
return ret;
}
/* https://gitlab.com/cryptsetup/cryptsetup/wikis/DMCrypt */
static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t flags)
{
int r, max_size, null_cipher = 0, num_options = 0;
int r, max_size, null_cipher = 0, num_options = 0, keystr_len = 0;
char *params, *hexkey;
char features[256];
@@ -399,13 +410,24 @@ static char *get_dm_crypt_params(struct crypt_dm_active_device *dmd, uint32_t fl
if (!strncmp(dmd->u.crypt.cipher, "cipher_null-", 12))
null_cipher = 1;
hexkey = crypt_safe_alloc(null_cipher ? 2 : (dmd->u.crypt.vk->keylength * 2 + 1));
if (dmd->u.crypt.key_in_keyring) {
keystr_len = strlen(dmd->u.crypt.key_description) + get_key_size_strlen(dmd->u.crypt.vk->keylength) + 9;
hexkey = crypt_safe_alloc(keystr_len);
} else
hexkey = crypt_safe_alloc(null_cipher ? 2 : (dmd->u.crypt.vk->keylength * 2 + 1));
if (!hexkey)
return NULL;
if (null_cipher)
strncpy(hexkey, "-", 2);
else
else if (dmd->u.crypt.key_in_keyring) {
r = snprintf(hexkey, keystr_len, ":%zu:logon:%s", dmd->u.crypt.vk->keylength, dmd->u.crypt.key_description);
if (r < 0 || r >= keystr_len) {
params = NULL;
goto out;
}
} else
hex_key(hexkey, dmd->u.crypt.vk->keylength, dmd->u.crypt.vk->key);
max_size = strlen(hexkey) + strlen(dmd->u.crypt.cipher) +
@@ -1149,6 +1171,7 @@ static int _dm_query_crypt(uint32_t get_flags,
char *rcipher, *key_, *rdevice, *endp, buffer[3], *arg;
unsigned int i;
int r;
size_t key_size;
memset(dmd, 0, sizeof(*dmd));
dmd->target = DM_CRYPT;
@@ -1222,20 +1245,38 @@ static int _dm_query_crypt(uint32_t get_flags,
return -EINVAL;
}
if (key_[0] == ':')
dmd->u.crypt.key_in_keyring = 1;
if (get_flags & DM_ACTIVE_CRYPT_KEYSIZE) {
dmd->u.crypt.vk = crypt_alloc_volume_key(strlen(key_) / 2, NULL);
/* we will trust kernel the key_string is in expected format */
if (key_[0] == ':') {
if (sscanf(key_ + 1, "%zu", &key_size) != 1)
return -EINVAL;
} else
key_size = strlen(key_) / 2;
dmd->u.crypt.vk = crypt_alloc_volume_key(key_size, NULL);
if (!dmd->u.crypt.vk)
return -ENOMEM;
if (get_flags & DM_ACTIVE_CRYPT_KEY) {
buffer[2] = '\0';
for(i = 0; i < dmd->u.crypt.vk->keylength; i++) {
memcpy(buffer, &key_[i * 2], 2);
dmd->u.crypt.vk->key[i] = strtoul(buffer, &endp, 16);
if (endp != &buffer[2]) {
if (key_[0] == ':') {
dmd->u.crypt.key_description = strdup(strpbrk(key_ + 1, ":") + 1);
if (!dmd->u.crypt.key_description) {
crypt_free_volume_key(dmd->u.crypt.vk);
dmd->u.crypt.vk = NULL;
return -EINVAL;
return -ENOMEM;
}
} else {
buffer[2] = '\0';
for(i = 0; i < dmd->u.crypt.vk->keylength; i++) {
memcpy(buffer, &key_[i * 2], 2);
dmd->u.crypt.vk->key[i] = strtoul(buffer, &endp, 16);
if (endp != &buffer[2]) {
crypt_free_volume_key(dmd->u.crypt.vk);
dmd->u.crypt.vk = NULL;
return -EINVAL;
}
}
}
}
@@ -1685,10 +1726,10 @@ out:
}
int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
size_t key_size, const char *key)
size_t key_size, const char *key, unsigned key_in_keyring)
{
uint32_t dmt_flags;
int msg_size = key_size * 2 + 10; // key set <key>
int msg_size;
char *msg = NULL;
int r = -ENOTSUP;
@@ -1698,6 +1739,11 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
if (!(dmt_flags & DM_KEY_WIPE_SUPPORTED))
goto out;
if (key_in_keyring)
msg_size = strlen(key) + get_key_size_strlen(key_size) + 17;
else
msg_size = key_size * 2 + 10; // key set <key>
msg = crypt_safe_alloc(msg_size);
if (!msg) {
r = -ENOMEM;
@@ -1705,7 +1751,10 @@ int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
}
strcpy(msg, "key set ");
hex_key(&msg[8], key_size, key);
if (key_in_keyring)
snprintf(msg + 8, msg_size - 8, ":%zu:logon:%s", key_size, key);
else
hex_key(&msg[8], key_size, key);
if (!_dm_message(name, msg, dmt_flags) ||
!_dm_simple(DM_DEVICE_RESUME, name, 1)) {

View File

@@ -1814,7 +1814,7 @@ int crypt_resume_by_passphrase(struct crypt_device *cd,
&cd->u.luks1.hdr, &vk, cd);
if (r >= 0) {
keyslot = r;
r = dm_resume_and_reinstate_key(cd, name, vk->keylength, vk->key);
r = dm_resume_and_reinstate_key(cd, name, vk->keylength, vk->key, 0);
if (r == -ENOTSUP)
log_err(cd, _("Resume is not supported for device %s.\n"), name);
else if (r)
@@ -1871,7 +1871,7 @@ int crypt_resume_by_keyfile_offset(struct crypt_device *cd,
goto out;
keyslot = r;
r = dm_resume_and_reinstate_key(cd, name, vk->keylength, vk->key);
r = dm_resume_and_reinstate_key(cd, name, vk->keylength, vk->key, 0);
if (r)
log_err(cd, _("Error during resuming device %s.\n"), name);
out:

View File

@@ -75,6 +75,7 @@ struct crypt_dm_active_device {
struct {
const char *cipher;
const char *integrity;
char *key_description;
/* Active key for device */
struct volume_key *vk;
@@ -82,6 +83,8 @@ struct crypt_dm_active_device {
/* struct crypt_active_device */
uint64_t offset; /* offset in sectors */
uint64_t iv_offset; /* IV initilisation sector */
unsigned key_in_keyring:1; /* status detected key loaded via kernel keyring */
} crypt;
struct {
struct device *hash_device;
@@ -133,7 +136,7 @@ int dm_create_device(struct crypt_device *cd, const char *name,
int reload);
int dm_suspend_and_wipe_key(struct crypt_device *cd, const char *name);
int dm_resume_and_reinstate_key(struct crypt_device *cd, const char *name,
size_t key_size, const char *key);
size_t key_size, const char *key, unsigned key_in_keyring);
const char *dm_get_dir(void);

275
lib/utils_keyring.c Normal file
View File

@@ -0,0 +1,275 @@
/*
* kernel keyring utilities
*
* Copyright (C) 2016-2017, Red Hat, Inc. All rights reserved.
* Copyright (C) 2016, Ondrej Kozina. All rights reserved.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/syscall.h>
#ifndef HAVE_KEY_SERIAL_T
#define HAVE_KEY_SERIAL_T
#include <stdint.h>
typedef int32_t key_serial_t;
#endif
#include "internal.h"
#include "utils_keyring.h"
#ifdef KERNEL_KEYRING
#include <linux/keyctl.h>
/* request_key */
static key_serial_t request_key(const char *type,
const char *description,
const char *callout_info,
key_serial_t keyring)
{
return syscall(__NR_request_key, type, description, callout_info, keyring);
}
/* add_key */
static key_serial_t add_key(const char *type,
const char *description,
const void *payload,
size_t plen,
key_serial_t keyring)
{
return syscall(__NR_add_key, type, description, payload, plen, keyring);
}
/* keyctl_read */
static long keyctl_read(key_serial_t key, char *buffer, size_t buflen)
{
return syscall(__NR_keyctl, KEYCTL_READ, key, buffer, buflen);
}
/* keyctl_revoke */
static long keyctl_revoke(key_serial_t key)
{
return syscall(__NR_keyctl, KEYCTL_REVOKE, key);
}
/* keyctl_unlink */
static long keyctl_unlink(key_serial_t key, key_serial_t keyring)
{
return syscall(__NR_keyctl, KEYCTL_UNLINK, key, keyring);
}
#endif
int keyring_check(void)
{
#ifdef KERNEL_KEYRING
/* logon type key descriptions must be in format "prefix:description" */
return syscall(__NR_request_key, "logon", "dummy", NULL, 0) == -1l && errno != ENOSYS;
#else
return 0;
#endif
}
int keyring_add_key_in_thread_keyring(const char *key_desc, const void *key, size_t key_size)
{
#ifdef KERNEL_KEYRING
key_serial_t kid;
log_dbg("Loading key %s (%zu bytes) in thread keyring", key_desc, key_size);
kid = add_key("logon", key_desc, key, key_size, KEY_SPEC_THREAD_KEYRING);
if (kid < 0) {
switch (errno) {
case EINVAL:
log_dbg("add_key: the payload data is invalid.");
break;
case ENOMEM:
log_dbg("add_key: insufficient memory to create a key.");
break;
case EDQUOT:
log_dbg("add_key: quota would exceed.");
break;
}
return -errno;
}
return 0;
#else
return -EINVAL;
#endif
}
#ifdef KERNEL_KEYRING
static key_serial_t request_key_verbose(const char *type,
const char *key_desc,
const char *callout_info,
key_serial_t keyring)
{
key_serial_t kid;
/*
* Search for a key in this particular order (first found, first served):
*
* 1) thread keyring
* 2) process keyring
* 3) user keyring (if exists) or user session keyring (if exists)
*/
kid = request_key(type, key_desc, callout_info, keyring);
if (kid < 0) {
switch (errno) {
case EACCES:
log_dbg("request_key: The keyring wasn't available for modification by the user.");
break;
case EINTR:
log_dbg("request_key: The request was interrupted by a signal.");
break;
case EDQUOT:
log_dbg("request_key: The key quota for this user would be exceeded by creating this key or linking it to the keyring.");
break;
case EKEYEXPIRED:
log_dbg("request_key: An expired key was found, but no replacement could be obtained.");
break;
case EKEYREVOKED:
log_dbg("request_key: A revoked key was found, but no replacement could be obtained.");
break;
case ENOKEY:
log_dbg("request_key: No matching key was found.");
break;
/* NOTE following error codes are unreachable unless key generation is triggered */
case EKEYREJECTED:
log_dbg("request_key: The attempt to generate a new key was rejected.");
break;
case ENOMEM:
log_dbg("request_key: Insufficient memory to create a key.");
break;
}
}
return kid;
}
#endif
int keyring_get_passphrase(const char *key_desc,
char **passphrase,
size_t *passphrase_len)
{
#ifdef KERNEL_KEYRING
int err;
key_serial_t kid;
long ret;
char *buf = NULL;
size_t len = 0;
log_dbg("Looking for key described with '%s'.", key_desc);
do
kid = request_key_verbose("user", key_desc, NULL, 0);
while (kid < 0 && errno == EINTR);
if (kid < 0)
return -errno;
/* just get payload size */
ret = keyctl_read(kid, NULL, 0);
if (ret > 0) {
len = ret;
buf = malloc(len);
if (!buf)
return -ENOMEM;
/* retrieve actual payload data */
ret = keyctl_read(kid, buf, len);
}
if (ret < 0) {
err = errno;
crypt_memzero(buf, len);
free(buf);
switch (err) {
case ENOKEY:
log_dbg("keyctl_read: The key specified is invalid.");
break;
case EKEYEXPIRED:
log_dbg("keyctl_read: The key specified has expired.");
break;
case EKEYREVOKED:
log_dbg("keyctl_read: The key specified had been revoked.");
break;
case EACCES:
log_dbg("keyctl_read: The key exists, but is not readable by the calling process.");
break;
}
return -err;
}
*passphrase = buf;
*passphrase_len = len;
return 0;
#else
return -EINVAL;
#endif
}
int keyring_revoke_and_unlink_key(const char *key_desc)
{
#ifdef KERNEL_KEYRING
key_serial_t kid;
log_dbg("requesting keyring key %s for removal", key_desc);
do
kid = request_key_verbose("logon", key_desc, NULL, 0);
while (kid < 0 && errno == EINTR);
if (kid < 0)
return 0;
log_dbg("Revoking key %s", key_desc);
if (keyctl_revoke(kid)) {
switch (errno) {
case ENOKEY:
log_dbg ("keyctl_revoke: The specified key does not exist.");
break;
case EKEYREVOKED:
log_dbg("keyctl_revoke: The key has already been revoked.");
break;
case EACCES:
log_dbg("keyctl_revoke: The name key exists, but is not writable by the calling process.");
return -errno;
default:
log_dbg("keyctl_revoke: Unexpected errno: %d", errno);
}
}
/*
* best effort only. the key could have been linked
* in some other keyring and its payload is now
* revoked anyway.
*/
keyctl_unlink(kid, KEY_SPEC_THREAD_KEYRING);
keyctl_unlink(kid, KEY_SPEC_PROCESS_KEYRING);
keyctl_unlink(kid, KEY_SPEC_USER_KEYRING);
return 0;
#else
return -EINVAL;
#endif
}

38
lib/utils_keyring.h Normal file
View File

@@ -0,0 +1,38 @@
/*
* kernel keyring syscall wrappers
*
* Copyright (C) 2016, Red Hat, Inc. All rights reserved.
* Copyright (C) 2016, Ondrej Kozina. All rights reserved.
*
* 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _UTILS_KEYRING
#define _UTILS_KEYRING
int keyring_check(void);
int keyring_get_passphrase(const char *key_desc,
char **passphrase,
size_t *passphrase_len);
int keyring_add_key_in_thread_keyring(
const char *key_desc,
const void *key,
size_t key_size);
int keyring_revoke_and_unlink_key(const char *key_desc);
#endif

View File

@@ -7,7 +7,8 @@ TESTS = api-test \
password-hash-test \
tcrypt-compat-test \
luks1-compat-test \
device-test
device-test \
keyring-test
if VERITYSETUP
TESTS += verity-compat-test
@@ -37,6 +38,7 @@ EXTRA_DIST = compatimage.img.bz2 compatv10image.img.bz2 \
tcrypt-compat-test \
luks1-compat-test \
device-test \
keyring-test \
integrity-compat-test \
cryptsetup-valg-supps valg.sh valg-api.sh

234
tests/keyring-test Executable file
View File

@@ -0,0 +1,234 @@
#!/bin/bash
DEV_ZERO="dmtst-zero"
DEV_CRYPT="dmtst-crypt"
CIPHER="aes-xts-plain64"
TEST_KEYRING_NAME="dmtst_keyring"
USER_KEY_32_OK="dmtst:ukey_32_ok"
USER_KEY_32_WRONG="dmtst:ukey_32_wrong_size"
LOGON_KEY_32_OK="dmtst:lkey_32_ok"
LOGON_KEY_32_WRONG="dmtst:lkey_32_wrong_size"
PAYLOAD_32="bb21158c733229347bd4e681891e213d"
PAYLOAD_31="bb21158c733229347bd4e681891e213"
HEXKEY_32="bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a1a";
HEXKEY_32_BAD="bb21158c733229347bd4e68189XXXX3d94c685be6a5b84818afe7a78a6de7a1a"
HEXKEY_31="bb21158c733229347bd4e681891e213d94c685be6a5b84818afe7a78a6de7a"
function skip()
{
[ -n "$1" ] && echo "$1"
exit 0
}
function remove_mapping()
{
[ -b /dev/mapper/$DEV_CRYPT ] && dmsetup remove $DEV_CRYPT
[ -b /dev/mapper/$DEV_ZERO ] && dmsetup remove $DEV_ZERO
# unlink whole test keyring
[ -n "$TEST_KEYRING" ] && keyctl unlink $TEST_KEYRING "@s" >/dev/null
}
function fail()
{
[ -n "$1" ] && echo "$1"
echo "FAILED"
remove_mapping
exit 2
}
# $1 type
# $2 description
# $3 payload
# $4 keyring
function load_key()
{
keyctl add $@ >/dev/null
}
function dm_crypt_keyring_support()
{
local str
local major=0
local minor=0
str=$(dmsetup targets | grep crypt | sed -e 's/crypt[[:space:]]\+v\(.*\)/\1/g')
[ -z "$str" ] && fail "failed to parse dm-crypt version"
major=${str%%.*}
minor=${str#*.}
minor=${minor%.*}
[ $major -gt 1 ] && return 0
[ $major -lt 1 ] && return 1
[ $minor -ge 15 ]
}
which dmsetup >/dev/null || skip "Cannot find dmsetup, test skipped"
which keyctl >/dev/null || skip "Cannot find keyctl, test skipped"
modprobe dm-crypt || fail "dm-crypt failed to load"
dm_crypt_keyring_support || skip "dm-crypt doesn't support kernel keyring, test skipped."
keyctl new_session >/dev/null || fail "Cannot create new session keyring"
TEST_KEYRING=$(keyctl newring $TEST_KEYRING_NAME "@s" 2> /dev/null)
test -n "$TEST_KEYRING" || fail "Cannot create test keyring"
load_key logon $LOGON_KEY_32_OK $PAYLOAD_32 "$TEST_KEYRING" || fail "Cannot load 32 byte logon key type"
load_key user $USER_KEY_32_OK $PAYLOAD_32 "$TEST_KEYRING" || fail "Cannot load 32 byte user key type"
load_key logon $LOGON_KEY_32_WRONG $PAYLOAD_31 "$TEST_KEYRING" || fail "Cannot load 31 byte logon key type"
load_key user $USER_KEY_32_WRONG $PAYLOAD_31 "$TEST_KEYRING" || fail "Cannot load 31 byte user key type"
dmsetup create $DEV_ZERO --table "0 100 zero" || fail
echo "[1] Valid keyring keys"
# load logon type kernel key
KEY=":32:logon:$LOGON_KEY_32_OK"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" || fail
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $KEY 0" || fail
dmsetup remove $DEV_CRYPT || fail
# load user type kernel key
KEY=":32:user:$USER_KEY_32_OK"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" || fail
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $KEY 0" || fail
dmsetup remove $DEV_CRYPT || fail
# load logon type kernel key...
KEY=":32:logon:$LOGON_KEY_32_OK"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" || fail
dmsetup suspend $DEV_CRYPT || fail
dmsetup message $DEV_CRYPT 0 "key wipe" || fail
# ...replace the key with hexkey...
dmsetup message $DEV_CRYPT 0 "key set $HEXKEY_32" || fail
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $HEXKEY_32 0" || fail
dmsetup resume $DEV_CRYPT || fail
dmsetup suspend $DEV_CRYPT || fail
# ...and replace it again with user type kernel key...
dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER :32:user:$USER_KEY_32_OK 0" || fail
dmsetup message $DEV_CRYPT 0 "key set $HEXKEY_32" || fail
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $HEXKEY_32 0" || fail
dmsetup resume $DEV_CRYPT || fail
dmsetup remove $DEV_CRYPT || fail
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $HEXKEY_32 0 /dev/mapper/$DEV_ZERO 0" || fail
dmsetup suspend $DEV_CRYPT || fail
dmsetup message $DEV_CRYPT 0 "key wipe" || fail
dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
dmsetup resume $DEV_CRYPT || fail
dmsetup suspend $DEV_CRYPT || fail
dmsetup message $DEV_CRYPT 0 "key set :32:logon:$LOGON_KEY_32_OK" || fail
dmsetup resume $DEV_CRYPT || fail
dmsetup remove $DEV_CRYPT || fail
echo "[2] message ioctl"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $HEXKEY_32 0 /dev/mapper/$DEV_ZERO 0" || fail
dmsetup suspend $DEV_CRYPT || fail
dmsetup message $DEV_CRYPT 0 "key set :32:logon:$LOGON_KEY_32_WRONG" 2> /dev/null && fail
# old key should be intact and valid
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER $HEXKEY_32 0" || fail
dmsetup resume $DEV_CRYPT || fail
dmsetup suspend $DEV_CRYPT || fail
# now the key gets destroyed by invalid input
dmsetup message $DEV_CRYPT 0 "key set $HEXKEY_32_BAD" 2> /dev/null && fail
dmsetup resume $DEV_CRYPT 2> /dev/null && fail
# hmm... see the output. don't like it
# dmsetup table --showkeys $DEV_CRYPT
dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER :32:user:$USER_KEY_32_OK 0" || fail
dmsetup message $DEV_CRYPT 0 "key set :31:logon:$LOGON_KEY_32_OK" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set :" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set ::::" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set :0:logon:$LOGON_KEY_32_OK" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set :32" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set :32:" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set :32:logon" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set :32:logo" 2> /dev/null && fail
dmsetup message $DEV_CRYPT 0 "key set :32:logon:" 2> /dev/null && fail
dmsetup table --showkeys $DEV_CRYPT | grep -q "crypt $CIPHER :32:user:$USER_KEY_32_OK 0" || fail
dmsetup message $DEV_CRYPT 0 "key set :32:user:$USER_KEY_32_OK" || fail
dmsetup resume $DEV_CRYPT || fail
dmsetup remove $DEV_CRYPT || fail
echo "[3] bOrked keys"
# declare the key having 32 bytes but load key which has in fact 31 bytes only
KEY=":32:logon:$LOGON_KEY_32_WRONG"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted wrong key size"
# declare the key having 31 bytes (incompatible with cipher) and load key with 32 bytes in real
KEY=":31:logon:$LOGON_KEY_32_WRONG"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted wrong key size"
# declare the key being user type but try to load logon one
KEY=":32:user:$LOGON_KEY_32"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted key description for invalid key type"
# now the other way
KEY=":32:logon:$USER_KEY_32"
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $KEY 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted key description for invalid key type"
BORKED_KEYS=":\ 32:logon:$LOGON_KEY_32_OK
: 32:logon:$LOGON_KEY_32_OK
:+32:logon:$LOGON_KEY_32_OK
:-32:logon:$LOGON_KEY_32_OK
:32 :logon:$LOGON_KEY_32_OK
:32\ :logon:$LOGON_KEY_32_OK
:32_:logon:$LOGON_KEY_32_OK
:32+:logon:$LOGON_KEY_32_OK
:30+2:logon:$LOGON_KEY_32_OK
:32+0:logon:$LOGON_KEY_32_OK
:32: logon:$LOGON_KEY_32_OK
:32:\ logon:$LOGON_KEY_32_OK
:32:logonA:$LOGON_KEY_32_OK
:32:logo:$LOGON_KEY_32_OK
:32:llogon:$LOGON_KEY_32_OK
:32xlogon:$LOGON_KEY_32_OK
:32logon:$LOGON_KEY_32_OK
:32:logonx$LOGON_KEY_32_OK
:32:logon$LOGON_KEY_32_OK
: 32:user:$USER_KEY_32_OK
:\ 32:user:$USER_KEY_32_OK
:+32:user:$USER_KEY_32_OK
:-32:user:$USER_KEY_32_OK
:32 :user:$USER_KEY_32_OK
:32\ :user:$USER_KEY_32_OK
:32_:user:$USER_KEY_32_OK
:32+:user:$USER_KEY_32_OK
:30+2:user:$USER_KEY_32_OK
:32+0:user:$USER_KEY_32_OK
:32: user:$USER_KEY_32_OK
:32:\ user:$USER_KEY_32_OK
:32:userA:$USER_KEY_32_OK
:32:use:$USER_KEY_32_OK
:32:uuser:$USER_KEY_32_OK
:32xuser:$USER_KEY_32_OK
:32user:$USER_KEY_32_OK
:32:userx$USER_KEY_32_OK
:32:user$USER_KEY_32_OK
:32:userlogon:$USER_KEY_32_OK
:32:userlogon:$LOGON_KEY_32_OK
:32:logonuser:$USER_KEY_32_OK
:32:logonuser:$LOGON_KEY_32_OK
:32:logon:user:$USER_KEY_32_OK
:32:logon:user:$LOGON_KEY_32_OK
:32:user:logon:$USER_KEY_32_OK
:32:user:logon:$LOGON_KEY_32_OK"
# TODO: add tests with whitespace in key description (not possible with current libdevmapper)
IFS="
"
for key in $BORKED_KEYS; do
dmsetup create $DEV_CRYPT --table "0 100 crypt $CIPHER $key 0 /dev/mapper/$DEV_ZERO 0" 2> /dev/null && fail "dm-crypt accepted seriously borked key string"
done
remove_mapping