libcryptsetup: add OPAL type and params

Signed-off-by: Luca Boccassi <bluca@debian.org>
Co-authored-by: Ondrej Kozina <okozina@redhat.com>
This commit is contained in:
Luca Boccassi
2022-11-27 23:23:47 +00:00
committed by Milan Broz
parent fc4151f77e
commit b9cc0129c9
19 changed files with 1872 additions and 48 deletions

View File

@@ -592,6 +592,27 @@ AM_CONDITIONAL(HAVE_BLKID, test "x$enable_blkid" = "xyes")
AM_CONDITIONAL(HAVE_BLKID_WIPE, test "x$enable_blkid_wipe" = "xyes")
AM_CONDITIONAL(HAVE_BLKID_STEP_BACK, test "x$enable_blkid_step_back" = "xyes")
AC_ARG_ENABLE([hw-opal],
AS_HELP_STRING([--disable-hw-opal], [disable use of hardware-backed OPAL for device encryption]),
[],
[enable_hw_opal=yes])
if test "x$enable_hw_opal" = "xyes"; then
have_opal=yes
AC_CHECK_DECLS([ OPAL_FL_SUM_SUPPORTED,
IOC_OPAL_GET_LR_STATUS,
IOC_OPAL_GET_GEOMETRY
],
[],
[have_opal=no],
[#include <linux/sed-opal.h>])
if test "x$have_opal" = "xyes"; then
AC_DEFINE([HAVE_HW_OPAL], 1, [Define to 1 to enable OPAL support.])
else
AC_MSG_WARN([Can not compile with OPAL support, kernel headers are too old, requires v6.4.])
fi
fi
dnl Magic for cryptsetup.static build.
if test "x$enable_static_cryptsetup" = "xyes"; then
saved_PKG_CONFIG=$PKG_CONFIG

View File

@@ -103,6 +103,8 @@ libcryptsetup_la_SOURCES = \
lib/luks2/luks2_token.c \
lib/luks2/luks2_internal.h \
lib/luks2/luks2.h \
lib/luks2/hw_opal/hw_opal.c \
lib/luks2/hw_opal/hw_opal.h \
lib/utils_blkid.c \
lib/utils_blkid.h \
lib/bitlk/bitlk.h \

View File

@@ -158,6 +158,7 @@ struct device *crypt_data_device(struct crypt_device *cd);
uint64_t crypt_get_metadata_size_bytes(struct crypt_device *cd);
uint64_t crypt_get_keyslots_size_bytes(struct crypt_device *cd);
uint64_t crypt_get_data_offset_sectors(struct crypt_device *cd);
int crypt_opal_supported(struct crypt_device *cd, struct device *opal_device);
int crypt_confirm(struct crypt_device *cd, const char *msg);
@@ -166,6 +167,7 @@ int crypt_dev_is_rotational(int major, int minor);
int crypt_dev_is_dax(int major, int minor);
int crypt_dev_is_partition(const char *dev_path);
char *crypt_get_partition_device(const char *dev_path, uint64_t offset, uint64_t size);
int crypt_dev_get_partition_number(const char *dev_path);
char *crypt_get_base_device(const char *dev_path);
uint64_t crypt_dev_partition_offset(const char *dev_path);
int lookup_by_disk_id(const char *dm_uuid);

View File

@@ -609,6 +609,18 @@ struct crypt_params_luks2 {
const char *label; /**< header label or @e NULL*/
const char *subsystem; /**< header subsystem label or @e NULL*/
};
/**
* Structure used as parameter for OPAL (HW encrypted) device type.
*
* @see crypt_format_luks2_opal
*
*/
struct crypt_params_hw_opal {
const char *admin_key; /**< admin key */
size_t admin_key_size; /**< admin key size in bytes */
size_t user_key_size; /**< user authority key size part in bytes */
};
/** @} */
/**
@@ -648,6 +660,34 @@ int crypt_format(struct crypt_device *cd,
size_t volume_key_size,
void *params);
/**
* Create (format) new LUKS2 crypt device over HW OPAL device but do not activate it.
*
* @pre @e cd contains initialized and not formatted device context (device type must @b not be set)
*
* @param cd crypt device handle
* @param cipher for SW encryption (e.g. "aes") or NULL for HW encryption only
* @param cipher_mode including IV specification (e.g. "xts-plain") or NULL for HW encryption only
* @param uuid requested UUID or @e NULL if it should be generated
* @param volume_key pre-generated volume key or @e NULL if it should be generated (only for LUKS2 SW encryption)
* @param volume_key_size size of volume key in bytes (only for SW encryption).
* @param params LUKS2 crypt type specific parameters (see @link crypt-type @endlink)
* @param opal_params OPAL specific parameters
*
* @returns @e 0 on success or negative errno value otherwise.
*
* @note Note that crypt_format_luks2_opal does not create LUKS keyslot.
* To create keyslot call any crypt_keyslot_add_* function.
*/
int crypt_format_luks2_opal(struct crypt_device *cd,
const char *cipher,
const char *cipher_mode,
const char *uuid,
const char *volume_keys,
size_t volume_keys_size,
struct crypt_params_luks2 *params,
struct crypt_params_hw_opal *opal_params);
/**
* Set format compatibility flags.
*

View File

@@ -165,3 +165,8 @@ CRYPTSETUP_2.6 {
crypt_keyslot_add_by_keyslot_context;
crypt_volume_key_get_by_keyslot_context;
} CRYPTSETUP_2.5;
CRYPTSETUP_2.7 {
global:
crypt_format_luks2_opal;
} CRYPTSETUP_2.6;

849
lib/luks2/hw_opal/hw_opal.c Normal file
View File

@@ -0,0 +1,849 @@
/*
* OPAL utilities
*
* Copyright (C) 2022-2023 Luca Boccassi <bluca@debian.org>
* 2023 Ondrej Kozina <okozina@redhat.com>
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This file 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <assert.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include "internal.h"
#include "libcryptsetup.h"
#include "luks2/hw_opal/hw_opal.h"
#if HAVE_HW_OPAL
#include <linux/sed-opal.h>
/* Error codes are defined in the specification:
* TCG_Storage_Architecture_Core_Spec_v2.01_r1.00
* Section 5.1.5: Method Status Codes
* Names and values from table 166 */
typedef enum OpalStatus {
OPAL_STATUS_SUCCESS,
OPAL_STATUS_NOT_AUTHORIZED,
OPAL_STATUS_OBSOLETE0, /* Undefined but possible return values are called 'obsolete' */
OPAL_STATUS_SP_BUSY,
OPAL_STATUS_SP_FAILED,
OPAL_STATUS_SP_DISABLED,
OPAL_STATUS_SP_FROZEN,
OPAL_STATUS_NO_SESSIONS_AVAILABLE,
OPAL_STATUS_UNIQUENESS_CONFLICT,
OPAL_STATUS_INSUFFICIENT_SPACE,
OPAL_STATUS_INSUFFICIENT_ROWS,
OPAL_STATUS_INVALID_PARAMETER,
OPAL_STATUS_OBSOLETE1,
OPAL_STATUS_OBSOLETE2,
OPAL_STATUS_TPER_MALFUNCTION,
OPAL_STATUS_TRANSACTION_FAILURE,
OPAL_STATUS_RESPONSE_OVERFLOW,
OPAL_STATUS_AUTHORITY_LOCKED_OUT,
OPAL_STATUS_FAIL = 0x3F, /* As defined by specification */
_OPAL_STATUS_MAX,
_OPAL_STATUS_INVALID = -EINVAL,
} OpalStatus;
static const char* const opal_status_table[_OPAL_STATUS_MAX] = {
[OPAL_STATUS_SUCCESS] = "success",
[OPAL_STATUS_NOT_AUTHORIZED] = "not authorized",
[OPAL_STATUS_OBSOLETE0] = "obsolete",
[OPAL_STATUS_SP_BUSY] = "SP busy",
[OPAL_STATUS_SP_FAILED] = "SP failed",
[OPAL_STATUS_SP_DISABLED] = "SP disabled",
[OPAL_STATUS_SP_FROZEN] = "SP frozen",
[OPAL_STATUS_NO_SESSIONS_AVAILABLE] = "no sessions available",
[OPAL_STATUS_UNIQUENESS_CONFLICT] = "uniqueness conflict",
[OPAL_STATUS_INSUFFICIENT_SPACE] = "insufficient space",
[OPAL_STATUS_INSUFFICIENT_ROWS] = "insufficient rows",
[OPAL_STATUS_INVALID_PARAMETER] = "invalid parameter",
[OPAL_STATUS_OBSOLETE1] = "obsolete",
[OPAL_STATUS_OBSOLETE2] = "obsolete",
[OPAL_STATUS_TPER_MALFUNCTION] = "TPer malfunction",
[OPAL_STATUS_TRANSACTION_FAILURE] = "transaction failure",
[OPAL_STATUS_RESPONSE_OVERFLOW] = "response overflow",
[OPAL_STATUS_AUTHORITY_LOCKED_OUT] = "authority locked out",
[OPAL_STATUS_FAIL] = "unknown failure",
};
static const char *opal_status_to_string(int t)
{
if (t < 0)
return strerror(-t);
if (t >= _OPAL_STATUS_MAX)
return "unknown error";
return opal_status_table[t];
}
static int opal_geometry_fd(int fd,
bool *ret_align,
uint32_t *ret_block_size,
uint64_t *ret_alignment_granularity_blocks,
uint64_t *ret_lowest_lba_blocks)
{
int r;
struct opal_geometry geo;
assert(fd >= 0);
r = ioctl(fd, IOC_OPAL_GET_GEOMETRY, &geo);
if (r != OPAL_STATUS_SUCCESS)
return r;
if (ret_align)
*ret_align = (geo.align == 1);
if (ret_block_size)
*ret_block_size = geo.logical_block_size;
if (ret_alignment_granularity_blocks)
*ret_alignment_granularity_blocks = geo.alignment_granularity;
if (ret_lowest_lba_blocks)
*ret_lowest_lba_blocks = geo.lowest_aligned_lba;
return r;
}
static int opal_range_check_attributes_fd(struct crypt_device *cd,
int fd,
uint32_t segment_number,
const struct volume_key *vk,
const uint64_t *check_offset_sectors,
const uint64_t *check_length_sectors,
bool *check_read_locked,
bool *check_write_locked)
{
int r;
struct opal_lr_status *lrs;
uint32_t opal_block_bytes;
uint64_t offset, length;
bool read_locked, write_locked;
assert(fd >= 0);
assert(cd);
assert(vk);
r = opal_geometry_fd(fd, NULL, &opal_block_bytes, NULL, NULL);
if (r != OPAL_STATUS_SUCCESS)
return -EINVAL;
lrs = crypt_safe_alloc(sizeof(*lrs));
if (!lrs) {
r = -ENOMEM;
goto out;
}
*lrs = (struct opal_lr_status) {
.session = {
.who = segment_number + 1,
.opal_key = {
.key_len = vk->keylength,
.lr = segment_number
}
}
};
memcpy(lrs->session.opal_key.key, vk->key, vk->keylength);
r = ioctl(fd, IOC_OPAL_GET_LR_STATUS, lrs);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to get locking range status on device '%s'.",
crypt_get_device_name(cd));
r = -EINVAL;
goto out;
}
r = 0;
offset = lrs->range_start * opal_block_bytes / SECTOR_SIZE;
if (check_offset_sectors && (offset != *check_offset_sectors)) {
log_err(cd, _("OPAL range %d offset %" PRIu64 " does not match expected values %" PRIu64 "."),
segment_number, offset, *check_offset_sectors);
r = -EINVAL;
}
length = lrs->range_length * opal_block_bytes / SECTOR_SIZE;
if (check_length_sectors && (length != *check_length_sectors)) {
log_err(cd, _("OPAL range %d length %" PRIu64" does not match device length %" PRIu64 "."),
segment_number, length, *check_length_sectors);
r = -EINVAL;
}
if (!lrs->RLE || !lrs->WLE) {
log_err(cd, _("OPAL range %d locking is disabled."), segment_number);
r = -EINVAL;
}
read_locked = (lrs->l_state == OPAL_LK);
write_locked = !!(lrs->l_state & (OPAL_RO | OPAL_LK));
if (check_read_locked && (read_locked != *check_read_locked)) {
log_dbg(cd, "OPAL range %d read lock is %slocked.",
segment_number, *check_read_locked ? "" : "not ");
log_err(cd, _("Unexpected OPAL range %d lock state."), segment_number);
r = -EINVAL;
}
if (check_write_locked && (write_locked != *check_write_locked)) {
log_dbg(cd, "OPAL range %d write lock is %slocked.",
segment_number, *check_write_locked ? "" : "not ");
log_err(cd, _("Unexpected OPAL range %d lock state."), segment_number);
r = -EINVAL;
}
out:
crypt_safe_free(lrs);
return r;
}
int opal_setup_ranges(struct crypt_device *cd,
struct device *dev,
const struct volume_key *vk,
uint64_t range_start,
uint64_t range_length,
uint32_t segment_number,
const void *admin_key,
size_t admin_key_len)
{
struct opal_lr_act *activate = NULL;
struct opal_session_info *user_session = NULL;
struct opal_lock_unlock *user_add_to_lr = NULL, *lock = NULL;
struct opal_new_pw *new_pw = NULL;
struct opal_user_lr_setup *setup = NULL;
int r, fd;
assert(cd);
assert(dev);
assert(vk);
assert(admin_key);
assert(vk->keylength <= OPAL_KEY_MAX);
if (admin_key_len > OPAL_KEY_MAX)
return -EINVAL;
fd = device_open(cd, dev, O_RDWR);
if (fd < 0)
return -EIO;
r = opal_enabled(cd, dev);
if (r < 0)
return r;
/* If OPAL has never been enabled, we need to take ownership and do basic setup first */
if (r == 0) {
activate = crypt_safe_alloc(sizeof(struct opal_lr_act));
if (!activate) {
r = -ENOMEM;
goto out;
}
*activate = (struct opal_lr_act) {
.key = {
.key_len = admin_key_len,
},
.num_lrs = 8,
/* A max of 9 segments are supported, enable them all as there's no reason not to
* (0 is whole-volume)
*/
.lr = { 1, 2, 3, 4, 5, 6, 7, 8 },
};
memcpy(activate->key.key, admin_key, admin_key_len);
r = ioctl(fd, IOC_OPAL_TAKE_OWNERSHIP, &activate->key);
if (r < 0) {
r = -ENOTSUP;
log_dbg(cd, "OPAL not supported on this kernel version, refusing.");
goto out;
}
if (r == OPAL_STATUS_NOT_AUTHORIZED) /* We'll try again with a different key. */ {
r = -EPERM;
log_dbg(cd, "Failed to take ownership of OPAL device '%s': permission denied",
crypt_get_device_name(cd));
goto out;
}
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to take ownership of OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
r = ioctl(fd, IOC_OPAL_ACTIVATE_LSP, activate);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to activate OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
} else {
/* If it is already enabled, wipe the locking range first */
user_session = crypt_safe_alloc(sizeof(struct opal_session_info));
if (!user_session) {
r = -ENOMEM;
goto out;
}
*user_session = (struct opal_session_info) {
.who = OPAL_ADMIN1,
.opal_key = {
.lr = segment_number,
.key_len = admin_key_len,
},
};
memcpy(user_session->opal_key.key, admin_key, admin_key_len);
r = ioctl(fd, IOC_OPAL_ERASE_LR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to reset (erase) OPAL locking range %u on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = ioctl(fd, IOC_OPAL_SECURE_ERASE_LR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
}
}
user_session = crypt_safe_alloc(sizeof(struct opal_session_info));
if (!user_session) {
r = -ENOMEM;
goto out;
}
*user_session = (struct opal_session_info) {
.who = segment_number + 1,
.opal_key = {
.key_len = admin_key_len,
},
};
memcpy(user_session->opal_key.key, admin_key, admin_key_len);
r = ioctl(fd, IOC_OPAL_ACTIVATE_USR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to activate OPAL user on device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
user_add_to_lr = crypt_safe_alloc(sizeof(struct opal_lock_unlock));
if (!user_add_to_lr) {
r = -ENOMEM;
goto out;
}
*user_add_to_lr = (struct opal_lock_unlock) {
.session = {
.who = segment_number + 1,
.opal_key = {
.lr = segment_number,
.key_len = admin_key_len,
},
},
.l_state = OPAL_RO,
};
memcpy(user_add_to_lr->session.opal_key.key, admin_key, admin_key_len);
r = ioctl(fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to add OPAL user to locking range %u (RO) on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
user_add_to_lr->l_state = OPAL_RW;
r = ioctl(fd, IOC_OPAL_ADD_USR_TO_LR, user_add_to_lr);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to add OPAL user to locking range %u (RW) on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
new_pw = crypt_safe_alloc(sizeof(struct opal_new_pw));
if (!new_pw) {
r = -ENOMEM;
goto out;
}
*new_pw = (struct opal_new_pw) {
.session = {
.who = OPAL_ADMIN1,
.opal_key = {
.lr = segment_number,
.key_len = admin_key_len,
},
},
.new_user_pw = {
.who = segment_number + 1,
.opal_key = {
.key_len = vk->keylength,
.lr = segment_number,
},
},
};
memcpy(new_pw->new_user_pw.opal_key.key, vk->key, vk->keylength);
memcpy(new_pw->session.opal_key.key, admin_key, admin_key_len);
log_dbg(cd, "User authority key length: %zu", vk->keylength);
r = ioctl(fd, IOC_OPAL_SET_PW, new_pw);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to set OPAL user password on device '%s': (%d) %s",
crypt_get_device_name(cd), r, opal_status_to_string(r));
r = -EINVAL;
goto out;
}
setup = crypt_safe_alloc(sizeof(struct opal_user_lr_setup));
if (!setup) {
r = -ENOMEM;
goto out;
}
*setup = (struct opal_user_lr_setup) {
.range_start = range_start,
.range_length = range_length,
/* Some drives do not enable Locking Ranges on setup. This have some
* interesting consequences: Lock command called later below will pass,
* but locking range will _not_ be locked at all.
*/
.RLE = 1,
.WLE = 1,
.session = {
.who = OPAL_ADMIN1,
.opal_key = {
.key_len = admin_key_len,
.lr = segment_number,
},
},
};
memcpy(setup->session.opal_key.key, admin_key, admin_key_len);
r = ioctl(fd, IOC_OPAL_LR_SETUP, setup);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to setup locking range of length %llu at offset %llu on OPAL device '%s': %s",
setup->range_length, setup->range_start, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
/* After setup an OPAL device is unlocked, but the expectation with cryptsetup is that it needs
* to be activated separately, so lock it immediately. */
lock = crypt_safe_alloc(sizeof(struct opal_lock_unlock));
if (!lock) {
r = -ENOMEM;
goto out;
}
*lock = (struct opal_lock_unlock) {
.l_state = OPAL_LK,
.session = {
.who = segment_number + 1,
.opal_key = {
.key_len = vk->keylength,
.lr = segment_number,
},
}
};
memcpy(lock->session.opal_key.key, vk->key, vk->keylength);
r = ioctl(fd, IOC_OPAL_LOCK_UNLOCK, lock);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to lock OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
/* Double check the locking range is locked and the ranges are set up as configured */
r = opal_range_check_attributes_fd(cd, fd, segment_number, vk, &range_start,
&range_length, &(bool) {true}, &(bool){true});
out:
crypt_safe_free(activate);
crypt_safe_free(user_session);
crypt_safe_free(user_add_to_lr);
crypt_safe_free(new_pw);
crypt_safe_free(setup);
crypt_safe_free(lock);
return r;
}
static int opal_lock_unlock(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const struct volume_key *vk,
bool lock)
{
struct opal_lock_unlock unlock = {
.l_state = lock ? OPAL_LK : OPAL_RW,
.session = {
.who = segment_number + 1,
.opal_key = {
.lr = segment_number,
},
},
};
int r, fd;
if (opal_supported(cd, dev) <= 0)
return -ENOTSUP;
if (!lock && !vk)
return -EINVAL;
fd = device_open(cd, dev, O_RDWR);
if (fd < 0)
return -EIO;
if (!lock) {
assert(vk->keylength <= OPAL_KEY_MAX);
unlock.session.opal_key.key_len = vk->keylength;
memcpy(unlock.session.opal_key.key, vk->key, vk->keylength);
}
r = ioctl(fd, IOC_OPAL_LOCK_UNLOCK, &unlock);
if (r < 0) {
r = -ENOTSUP;
log_dbg(cd, "OPAL not supported on this kernel version, refusing.");
goto out;
}
if (r == OPAL_STATUS_NOT_AUTHORIZED) /* We'll try again with a different key. */ {
r = -EPERM;
log_dbg(cd, "Failed to %slock OPAL device '%s': permission denied",
lock ? "" : "un", crypt_get_device_name(cd));
goto out;
}
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to %slock OPAL device '%s': %s",
lock ? "" : "un", crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
/* If we are unlocking, also tell the kernel to automatically unlock when resuming
* from suspend, otherwise the drive will be locked and everything will go up in flames.
* Also set the flag to allow locking without having to pass the key again.
* But do not error out if this fails, as the device will already be unlocked. */
if (!lock) {
unlock.flags = OPAL_SAVE_FOR_LOCK;
r = ioctl(fd, IOC_OPAL_SAVE, &unlock);
if (r != OPAL_STATUS_SUCCESS) {
log_std(cd, "Failed to prepare OPAL device '%s' for sleep resume, be aware before suspending: %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = 0;
}
}
out:
if (!lock)
crypt_safe_memzero(unlock.session.opal_key.key, unlock.session.opal_key.key_len);
return r;
}
int opal_lock(struct crypt_device *cd, struct device *dev, uint32_t segment_number)
{
return opal_lock_unlock(cd, dev, segment_number, NULL, /* lock= */ true);
}
int opal_unlock(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const struct volume_key *vk)
{
return opal_lock_unlock(cd, dev, segment_number, vk, /* lock= */ false);
}
int opal_factory_reset(struct crypt_device *cd,
struct device *dev,
const char *password,
size_t password_len)
{
struct opal_key reset = {
.key_len = password_len,
};
int r, fd;
assert(cd);
assert(dev);
assert(password);
if (password_len > OPAL_KEY_MAX)
return -EINVAL;
fd = device_open(cd, dev, O_RDWR);
if (fd < 0)
return -EIO;
memcpy(reset.key, password, password_len);
r = ioctl(fd, IOC_OPAL_PSID_REVERT_TPR, &reset);
if (r < 0) {
r = -ENOTSUP;
log_dbg(cd, "OPAL not supported on this kernel version, refusing.");
goto out;
}
if (r == OPAL_STATUS_NOT_AUTHORIZED) /* We'll try again with a different key. */ {
r = -EPERM;
log_dbg(cd, "Failed to reset OPAL device '%s', incorrect PSID?",
crypt_get_device_name(cd));
goto out;
}
if (r != OPAL_STATUS_SUCCESS) {
r = -EINVAL;
log_dbg(cd, "Failed to reset OPAL device '%s' with PSID: %s",
crypt_get_device_name(cd), opal_status_to_string(r));
goto out;
}
out:
crypt_safe_memzero(reset.key, reset.key_len);
return r;
}
int opal_reset_segment(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const char *password,
size_t password_len)
{
struct opal_session_info *user_session = NULL;
struct opal_user_lr_setup *setup = NULL;
int r, fd;
assert(cd);
assert(dev);
assert(password);
if (password_len > OPAL_KEY_MAX)
return -EINVAL;
if (opal_enabled(cd, dev) <= 0)
return -EINVAL;
user_session = crypt_safe_alloc(sizeof(struct opal_session_info));
if (!user_session)
return -ENOMEM;
*user_session = (struct opal_session_info) {
.who = OPAL_ADMIN1,
.opal_key = {
.lr = segment_number,
.key_len = password_len,
},
};
memcpy(user_session->opal_key.key, password, password_len);
fd = device_open(cd, dev, O_RDWR);
if (fd < 0) {
r = -EIO;
goto out;
}
r = ioctl(fd, IOC_OPAL_ERASE_LR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to reset (erase) OPAL locking range %u on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = ioctl(fd, IOC_OPAL_SECURE_ERASE_LR, user_session);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to reset (secure erase) OPAL locking range %u on device '%s': %s",
segment_number, crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
/* Unlike IOC_OPAL_ERASE_LR, IOC_OPAL_SECURE_ERASE_LR does not disable the locking range,
* we have to do that by hand.
*/
setup = crypt_safe_alloc(sizeof(struct opal_user_lr_setup));
if (!setup) {
r = -ENOMEM;
goto out;
}
*setup = (struct opal_user_lr_setup) {
.range_start = 0,
.range_length = 0,
.session = {
.who = OPAL_ADMIN1,
.opal_key = user_session->opal_key,
},
};
r = ioctl(fd, IOC_OPAL_LR_SETUP, setup);
if (r != OPAL_STATUS_SUCCESS) {
log_dbg(cd, "Failed to disable locking range on OPAL device '%s': %s",
crypt_get_device_name(cd), opal_status_to_string(r));
r = -EINVAL;
goto out;
}
}
out:
crypt_safe_free(user_session);
crypt_safe_free(setup);
return r;
}
static int opal_query_status(struct crypt_device *cd, struct device *dev, unsigned expected)
{
struct opal_status st = { };
int fd, r;
assert(cd);
assert(dev);
fd = device_open(cd, dev, O_RDWR);
if (fd < 0)
return -EIO;
r = ioctl(fd, IOC_OPAL_GET_STATUS, &st);
return r < 0 ? -EINVAL : (st.flags & expected) ? 1 : 0;
}
int opal_supported(struct crypt_device *cd, struct device *dev)
{
return opal_query_status(cd, dev, OPAL_FL_SUPPORTED|OPAL_FL_LOCKING_SUPPORTED);
}
int opal_enabled(struct crypt_device *cd, struct device *dev)
{
return opal_query_status(cd, dev, OPAL_FL_LOCKING_ENABLED);
}
int opal_geometry(struct crypt_device *cd,
struct device *dev,
bool *ret_align,
uint32_t *ret_block_size,
uint64_t *ret_alignment_granularity_blocks,
uint64_t *ret_lowest_lba_blocks)
{
int fd;
assert(cd);
assert(dev);
fd = device_open(cd, dev, O_RDWR);
if (fd < 0)
return -EIO;
return opal_geometry_fd(fd, ret_align, ret_block_size,
ret_alignment_granularity_blocks, ret_lowest_lba_blocks);
}
int opal_range_check_attributes(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const struct volume_key *vk,
const uint64_t *check_offset_sectors,
const uint64_t *check_length_sectors,
bool *check_read_locked,
bool *check_write_locked)
{
int fd;
assert(cd);
assert(dev);
assert(vk);
fd = device_open(cd, dev, O_RDWR);
if (fd < 0)
return -EIO;
return opal_range_check_attributes_fd(cd, fd, segment_number, vk,
check_offset_sectors, check_length_sectors, check_read_locked,
check_write_locked);
}
#else
int opal_setup_ranges(struct crypt_device *cd,
struct device *dev,
const struct volume_key *vk,
uint64_t range_start,
uint64_t range_length,
uint32_t segment_number,
const void *admin_key,
size_t admin_key_len)
{
return -ENOTSUP;
}
int opal_lock(struct crypt_device *cd, struct device *dev, uint32_t segment_number)
{
return -ENOTSUP;
}
int opal_unlock(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const struct volume_key *vk)
{
return -ENOTSUP;
}
int opal_supported(struct crypt_device *cd, struct device *dev)
{
return -ENOTSUP;
}
int opal_enabled(struct crypt_device *cd, struct device *dev)
{
return -ENOTSUP;
}
int opal_factory_reset(struct crypt_device *cd,
struct device *dev,
const char *password,
size_t password_len)
{
return -ENOTSUP;
}
int opal_reset_segment(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const char *password,
size_t password_len)
{
return -ENOTSUP;
}
int opal_geometry(struct crypt_device *cd,
struct device *dev,
bool *ret_align,
uint32_t *ret_block_size,
uint64_t *ret_alignment_granularity_blocks,
uint64_t *ret_lowest_lba_blocks)
{
return -ENOTSUP;
}
int opal_range_check_attributes(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const struct volume_key *vk,
const uint64_t *check_offset_sectors,
const uint64_t *check_length_sectors,
bool *check_read_locked,
bool *check_write_locked)
{
return -ENOTSUP;
}
#endif

View File

@@ -0,0 +1,66 @@
/*
* OPAL utilities
*
* Copyright (C) 2022-2023 Luca Boccassi <bluca@debian.org>
* 2023 Ondrej Kozina <okozina@redhat.com>
*
* This file is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This file 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this file; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*/
#ifndef _UTILS_OPAL
#define _UTILS_OPAL
#include "internal.h"
int opal_setup_ranges(struct crypt_device *cd,
struct device *dev,
const struct volume_key *vk,
uint64_t range_start,
uint64_t range_length,
uint32_t segment_number,
const void *admin_key,
size_t admin_key_len);
int opal_lock(struct crypt_device *cd, struct device *dev, uint32_t segment_number);
int opal_unlock(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const struct volume_key *vk);
int opal_supported(struct crypt_device *cd, struct device *dev);
int opal_enabled(struct crypt_device *cd, struct device *dev);
int opal_factory_reset(struct crypt_device *cd,
struct device *dev,
const char *password,
size_t password_len);
int opal_reset_segment(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const char *password,
size_t password_len);
int opal_geometry(struct crypt_device *cd,
struct device *dev,
bool *ret_align,
uint32_t *ret_block_size,
uint64_t *ret_alignment_granularity_blocks,
uint64_t *ret_lowest_lba_blocks);
int opal_range_check_attributes(struct crypt_device *cd,
struct device *dev,
uint32_t segment_number,
const struct volume_key *vk,
const uint64_t *check_offset_sectors,
const uint64_t *check_length_sectors,
bool *check_read_locked,
bool *check_write_locked);
#endif

View File

@@ -359,7 +359,8 @@ int LUKS2_digest_create(struct crypt_device *cd,
*/
int LUKS2_activate(struct crypt_device *cd,
const char *name,
struct volume_key *vk,
struct volume_key *crypt_key,
struct volume_key *opal_key,
uint32_t flags);
int LUKS2_activate_multi(struct crypt_device *cd,
@@ -384,7 +385,10 @@ int LUKS2_generate_hdr(
unsigned int sector_size,
uint64_t data_offset,
uint64_t metadata_size_bytes,
uint64_t keyslots_size_bytes);
uint64_t keyslots_size_bytes,
uint64_t device_size_bytes,
uint32_t opal_segment_number,
uint32_t opal_key_size);
int LUKS2_hdr_get_storage_params(struct crypt_device *cd,
uint64_t alignment_offset_bytes,
@@ -418,6 +422,12 @@ int LUKS2_keyslot_area(struct luks2_hdr *hdr,
uint64_t *length);
int LUKS2_keyslot_pbkdf(struct luks2_hdr *hdr, int keyslot, struct crypt_pbkdf_type *pbkdf);
int LUKS2_split_crypt_and_opal_keys(struct crypt_device *cd,
struct luks2_hdr *hdr,
const struct volume_key *vk,
struct volume_key **ret_crypt_key,
struct volume_key **ret_opal_key);
/*
* Permanent activation flags stored in header
*/

View File

@@ -296,6 +296,8 @@ uint64_t json_segment_get_iv_offset(json_object *jobj_segment);
uint64_t json_segment_get_size(json_object *jobj_segment, unsigned blockwise);
const char *json_segment_get_cipher(json_object *jobj_segment);
uint32_t json_segment_get_sector_size(json_object *jobj_segment);
int json_segment_get_opal_segment_id(json_object *jobj_segment, uint32_t *ret_opal_segment_id);
int json_segment_get_opal_key_size(json_object *jobj_segment, size_t *ret_key_size);
bool json_segment_is_backup(json_object *jobj_segment);
json_object *json_segments_get_segment(json_object *jobj_segments, int segment);
unsigned json_segments_count(json_object *jobj_segments);
@@ -305,6 +307,13 @@ json_object *json_segment_create_linear(uint64_t offset, const uint64_t *length,
json_object *json_segment_create_crypt(uint64_t offset, uint64_t iv_offset, const uint64_t *length,
const char *cipher, const char *integrity,
uint32_t sector_size, unsigned reencryption);
json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
uint32_t segment_number, uint32_t key_size);
json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *length,
uint32_t segment_number, uint32_t key_size,
uint64_t iv_offset, const char *cipher,
const char *integrity, uint32_t sector_size,
unsigned reencryption);
int json_segments_segment_in_reencrypt(json_object *jobj_segments);
bool json_segment_cmp(json_object *jobj_segment_1, json_object *jobj_segment_2);
bool json_segment_contains_flag(json_object *jobj_segment, const char *flag_str, size_t len);
@@ -349,6 +358,11 @@ int LUKS2_segment_is_type(struct luks2_hdr *hdr,
int segment,
const char *type);
bool LUKS2_segment_is_hw_opal(struct luks2_hdr *hdr, int segment);
int LUKS2_get_opal_segment_number(struct luks2_hdr *hdr, int segment,
uint32_t *ret_opal_segment_number);
int LUKS2_segment_by_type(struct luks2_hdr *hdr,
const char *type);
@@ -357,6 +371,8 @@ int LUKS2_last_segment_by_type(struct luks2_hdr *hdr,
int LUKS2_get_default_segment(struct luks2_hdr *hdr);
bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_new(struct luks2_hdr *hdr);
int LUKS2_reencrypt_digest_old(struct luks2_hdr *hdr);
int LUKS2_reencrypt_data_offset(struct luks2_hdr *hdr, bool blockwise);

View File

@@ -210,12 +210,17 @@ int LUKS2_generate_hdr(
unsigned int sector_size, /* in bytes */
uint64_t data_offset, /* in bytes */
uint64_t metadata_size_bytes,
uint64_t keyslots_size_bytes)
uint64_t keyslots_size_bytes,
uint64_t device_size_bytes,
uint32_t opal_segment_number,
uint32_t opal_key_size)
{
struct json_object *jobj_segment, *jobj_keyslots, *jobj_segments, *jobj_config;
uuid_t partitionUuid;
int r, digest;
assert(cipher_spec || (opal_key_size > 0 && device_size_bytes));
hdr->hdr_size = metadata_size_bytes;
log_dbg(cd, "Formatting LUKS2 with JSON metadata area %" PRIu64
@@ -284,7 +289,20 @@ int LUKS2_generate_hdr(
goto err;
}
jobj_segment = json_segment_create_crypt(data_offset, 0, NULL, cipher_spec, integrity, sector_size, 0);
if (!opal_key_size)
jobj_segment = json_segment_create_crypt(data_offset, 0,
NULL, cipher_spec,
integrity, sector_size,
0);
else if (opal_key_size && cipher_spec)
jobj_segment = json_segment_create_opal_crypt(data_offset, &device_size_bytes,
opal_segment_number, opal_key_size, 0,
cipher_spec, integrity,
sector_size, 0);
else
jobj_segment = json_segment_create_opal(data_offset, &device_size_bytes,
opal_segment_number, opal_key_size);
if (!jobj_segment) {
r = -EINVAL;
goto err;

View File

@@ -21,6 +21,7 @@
*/
#include "luks2_internal.h"
#include "luks2/hw_opal/hw_opal.h"
#include "../integrity/integrity.h"
#include <ctype.h>
#include <uuid/uuid.h>
@@ -783,6 +784,16 @@ static int hdr_validate_segments(struct crypt_device *cd, json_object *hdr_jobj)
if (!strcmp(json_object_get_string(jobj_type), "crypt") &&
hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
return 1;
/* opal */
if (!strncmp(json_object_get_string(jobj_type), "hw-opal", 7)) {
if (!json_contains(cd, val, key, "Segment", "opal_segment_number", json_type_int) ||
!json_contains(cd, val, key, "Segment", "opal_key_size", json_type_int))
return 1;
if (!strcmp(json_object_get_string(jobj_type), "hw-opal-crypt") &&
hdr_validate_crypt_segment(cd, val, key, jobj_digests, size))
return 1;
}
}
if (first_backup == 0) {
@@ -2084,6 +2095,13 @@ static void hdr_dump_segments(struct crypt_device *cd, json_object *hdr_jobj)
json_object_object_get_ex(jobj_segment, "type", &jobj1);
log_std(cd, " %s: %s\n", segment, json_object_get_string(jobj1));
if (!strncmp(json_object_get_string(jobj1), "hw-opal", 7)) {
json_object_object_get_ex(jobj_segment, "opal_segment_number", &jobj1);
log_std(cd, "\tsegment number: %" PRIu32 "\n", crypt_jobj_get_uint32(jobj1));
json_object_object_get_ex(jobj_segment, "opal_key_size", &jobj1);
log_std(cd, "\topal key size: %" PRIu32 "\n", crypt_jobj_get_uint32(jobj1));
}
json_object_object_get_ex(jobj_segment, "offset", &jobj1);
json_str_to_uint64(jobj1, &value);
log_std(cd, "\toffset: %" PRIu64 " [bytes]\n", value);
@@ -2592,10 +2610,14 @@ int LUKS2_activate_multi(struct crypt_device *cd,
int LUKS2_activate(struct crypt_device *cd,
const char *name,
struct volume_key *vk,
struct volume_key *crypt_key,
struct volume_key *opal_key,
uint32_t flags)
{
int r;
bool dynamic;
uint32_t opal_segment_number;
uint64_t range_offset_sectors, device_length_bytes;
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
struct crypt_dm_active_device dmdi = {}, dmd = {
.uuid = crypt_get_uuid(cd)
@@ -2610,13 +2632,55 @@ int LUKS2_activate(struct crypt_device *cd,
log_err(cd, _("No known cipher specification pattern detected in LUKS2 header."));
return -EINVAL;
}
r = dm_crypt_target_set(&dmd.segment, 0, dmd.size, crypt_data_device(cd),
vk, crypt_get_cipher_spec(cd), crypt_get_iv_offset(cd),
crypt_get_data_offset(cd), crypt_get_integrity(cd) ?: "none",
crypt_get_integrity_tag_size(cd), crypt_get_sector_size(cd));
if (r < 0)
if ((r = LUKS2_get_data_size(hdr, &device_length_bytes, &dynamic)))
return r;
if (!dynamic)
dmd.size = device_length_bytes / SECTOR_SIZE;
if (opal_key) {
r = crypt_opal_supported(cd, crypt_data_device(cd));
if (r < 0)
return r;
r = LUKS2_get_opal_segment_number(hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number);
if (r < 0)
return -EINVAL;
range_offset_sectors = crypt_get_data_offset(cd) + crypt_dev_partition_offset(device_path(crypt_data_device(cd)));
r = opal_range_check_attributes(cd, crypt_data_device(cd), opal_segment_number,
opal_key, &range_offset_sectors, &dmd.size,
NULL /* read locked */, NULL /* write locked */);
if (r < 0)
return r;
r = opal_unlock(cd, crypt_data_device(cd), opal_segment_number, opal_key);
if (r < 0)
return r;
}
/* FIXME: temporary workaround for dm-integrity */
if (crypt_get_integrity_tag_size(cd))
dmd.size = 0;
if (LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "crypt") ||
LUKS2_segment_is_type(hdr, CRYPT_DEFAULT_SEGMENT, "hw-opal-crypt")) {
r = dm_crypt_target_set(&dmd.segment, 0,
dmd.size, crypt_data_device(cd),
crypt_key, crypt_get_cipher_spec(cd),
crypt_get_iv_offset(cd), crypt_get_data_offset(cd),
crypt_get_integrity(cd) ?: "none",
crypt_get_integrity_tag_size(cd),
crypt_get_sector_size(cd));
} else
r = dm_linear_target_set(&dmd.segment, 0,
dmd.size, crypt_data_device(cd),
crypt_get_data_offset(cd));
if (r < 0)
goto out;
/* Add persistent activation flags */
if (!(flags & CRYPT_ACTIVATE_IGNORE_PERSISTENT))
LUKS2_config_get_flags(cd, hdr, &dmd.flags);
@@ -2626,17 +2690,19 @@ int LUKS2_activate(struct crypt_device *cd,
if (crypt_get_integrity_tag_size(cd)) {
if (!LUKS2_integrity_compatible(hdr)) {
log_err(cd, _("Unsupported device integrity configuration."));
return -EINVAL;
r = -EINVAL;
goto out;
}
if (dmd.flags & CRYPT_ACTIVATE_ALLOW_DISCARDS) {
log_err(cd, _("Discard/TRIM is not supported."));
return -EINVAL;
r = -EINVAL;
goto out;
}
r = INTEGRITY_create_dmd_device(cd, NULL, NULL, NULL, NULL, &dmdi, dmd.flags, 0);
if (r)
return r;
goto out;
dmdi.flags |= CRYPT_ACTIVATE_PRIVATE;
dmdi.uuid = dmd.uuid;
@@ -2649,6 +2715,9 @@ int LUKS2_activate(struct crypt_device *cd,
dm_targets_free(cd, &dmd);
dm_targets_free(cd, &dmdi);
out:
if (r < 0 && opal_key)
opal_lock(cd, crypt_data_device(cd), opal_segment_number);
return r;
}
@@ -2682,6 +2751,7 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
struct dm_target *tgt;
crypt_status_info ci;
struct crypt_dm_active_device dmdc;
uint32_t opal_segment_number;
char **dep, deps_uuid_prefix[40], *deps[MAX_DM_DEPS+1] = { 0 };
const char *namei = NULL;
struct crypt_lock_handle *reencrypt_lock = NULL;
@@ -2786,6 +2856,12 @@ int LUKS2_deactivate(struct crypt_device *cd, const char *name, struct luks2_hdr
r = ret;
}
if (!r && !LUKS2_get_opal_segment_number(hdr, CRYPT_DEFAULT_SEGMENT, &opal_segment_number)) {
r = opal_lock(cd, crypt_data_device(cd), opal_segment_number);
if (r)
log_err(cd, _("Failed to lock OPAL device %s."),
device_path(crypt_data_device(cd)));
}
out:
LUKS2_reencrypt_unlock(cd, reencrypt_lock);
dep = deps;
@@ -2899,3 +2975,58 @@ int json_object_copy(json_object *jobj_src, json_object **jobj_dst)
return *jobj_dst ? 0 : -1;
#endif
}
int LUKS2_split_crypt_and_opal_keys(struct crypt_device *cd,
struct luks2_hdr *hdr,
const struct volume_key *vk,
struct volume_key **ret_crypt_key,
struct volume_key **ret_opal_key)
{
int r;
uint32_t opal_segment_number;
size_t opal_user_key_size;
json_object *jobj_segment;
struct volume_key *opal_key, *crypt_key;
assert(vk);
assert(ret_crypt_key);
assert(ret_opal_key);
jobj_segment = LUKS2_get_segment_jobj(hdr, CRYPT_DEFAULT_SEGMENT);
if (!jobj_segment)
return -EINVAL;
r = json_segment_get_opal_segment_id(jobj_segment, &opal_segment_number);
if (r < 0)
return -EINVAL;
r = json_segment_get_opal_key_size(jobj_segment, &opal_user_key_size);
if (r < 0)
return -EINVAL;
if (vk->keylength < opal_user_key_size)
return -EINVAL;
/* OPAL SEGMENT only */
if (vk->keylength == opal_user_key_size) {
*ret_crypt_key = NULL;
*ret_opal_key = NULL;
return 0;
}
opal_key = crypt_alloc_volume_key(opal_user_key_size, vk->key);
if (!opal_key)
return -ENOMEM;
crypt_key = crypt_alloc_volume_key(vk->keylength - opal_user_key_size,
vk->key + opal_user_key_size);
if (!crypt_key) {
crypt_free_volume_key(opal_key);
return -ENOMEM;
}
*ret_opal_key = opal_key;
*ret_crypt_key = crypt_key;
return 0;
}

View File

@@ -132,6 +132,37 @@ uint32_t json_segment_get_sector_size(json_object *jobj_segment)
return i < 0 ? SECTOR_SIZE : i;
}
int json_segment_get_opal_segment_id(json_object *jobj_segment, uint32_t *ret_opal_segment_id)
{
json_object *jobj_segment_id;
assert(ret_opal_segment_id);
if (!json_object_object_get_ex(jobj_segment, "opal_segment_number", &jobj_segment_id))
return -EINVAL;
*ret_opal_segment_id = json_object_get_int(jobj_segment_id);
return 0;
}
int json_segment_get_opal_key_size(json_object *jobj_segment, size_t *ret_key_size)
{
json_object *jobj_key_size;
assert(ret_key_size);
if (!jobj_segment)
return -EINVAL;
if (!json_object_object_get_ex(jobj_segment, "opal_key_size", &jobj_key_size))
return -EINVAL;
*ret_key_size = json_object_get_int(jobj_key_size);
return 0;
}
static json_object *json_segment_get_flags(json_object *jobj_segment)
{
json_object *jobj;
@@ -307,6 +338,39 @@ json_object *json_segment_create_crypt(uint64_t offset,
return NULL;
}
json_object *json_segment_create_opal(uint64_t offset, const uint64_t *length,
uint32_t segment_number, uint32_t key_size)
{
json_object *jobj = _segment_create_generic("hw-opal", offset, length);
if (!jobj)
return NULL;
json_object_object_add(jobj, "opal_segment_number", json_object_new_int(segment_number));
json_object_object_add(jobj, "opal_key_size", json_object_new_int(key_size));
return jobj;
}
json_object *json_segment_create_opal_crypt(uint64_t offset, const uint64_t *length,
uint32_t segment_number, uint32_t key_size,
uint64_t iv_offset, const char *cipher,
const char *integrity, uint32_t sector_size,
unsigned reencryption)
{
json_object *jobj = _segment_create_generic("hw-opal-crypt", offset, length);
if (!jobj)
return NULL;
json_object_object_add(jobj, "opal_segment_number", json_object_new_int(segment_number));
json_object_object_add(jobj, "opal_key_size", json_object_new_int(key_size));
if (json_add_crypt_fields(jobj, iv_offset, cipher, integrity, sector_size, reencryption))
return jobj;
json_object_put(jobj);
return NULL;
}
uint64_t LUKS2_segment_offset(struct luks2_hdr *hdr, int segment, unsigned blockwise)
{
return json_segment_get_offset(LUKS2_get_segment_jobj(hdr, segment), blockwise);
@@ -342,6 +406,34 @@ int LUKS2_segment_is_type(struct luks2_hdr *hdr, int segment, const char *type)
return !strcmp(json_segment_type(LUKS2_get_segment_jobj(hdr, segment)) ?: "", type);
}
static bool json_segment_is_hw_opal(json_object *jobj_segment)
{
const char *type = json_segment_type(jobj_segment);
if (!type)
return false;
/* hw-opal, hw-opal-crypt */
return !strcmp(type, "hw-opal") || !strcmp(type, "hw-opal-crypt");
}
bool LUKS2_segment_is_hw_opal(struct luks2_hdr *hdr, int segment)
{
return json_segment_is_hw_opal(LUKS2_get_segment_jobj(hdr, segment));
}
int LUKS2_get_opal_segment_number(struct luks2_hdr *hdr, int segment, uint32_t *ret_opal_segment_number)
{
json_object *jobj_segment = LUKS2_get_segment_jobj(hdr, segment);
assert(ret_opal_segment_number);
if (!json_segment_is_hw_opal(jobj_segment))
return -ENOENT;
return json_segment_get_opal_segment_id(jobj_segment, ret_opal_segment_number);
}
int LUKS2_last_segment_by_type(struct luks2_hdr *hdr, const char *type)
{
json_object *jobj_segments;
@@ -473,3 +565,27 @@ bool json_segment_cmp(json_object *jobj_segment_1, json_object *jobj_segment_2)
return true;
}
bool LUKS2_segments_dynamic_size(struct luks2_hdr *hdr)
{
json_object *jobj_segments, *jobj_size;
assert(hdr);
jobj_segments = LUKS2_get_segments_jobj(hdr);
if (!jobj_segments)
return false;
json_object_object_foreach(jobj_segments, key, val) {
UNUSED(key);
if (json_segment_is_backup(val))
continue;
if (json_object_object_get_ex(val, "size", &jobj_size) &&
!strcmp(json_object_get_string(jobj_size), "dynamic"))
return true;
}
return false;
}

View File

@@ -764,7 +764,7 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd,
{
bool use_keyring;
int keyslot, r, segment;
struct volume_key *vk = NULL;
struct volume_key *p_crypt, *p_opal, *crypt_key = NULL, *opal_key = NULL, *vk = NULL;
if (flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY)
segment = CRYPT_ANY_SEGMENT;
@@ -779,23 +779,39 @@ int LUKS2_token_open_and_activate(struct crypt_device *cd,
keyslot = r;
if (!crypt_use_keyring_for_vk(cd))
if (LUKS2_segment_is_hw_opal(hdr, CRYPT_DEFAULT_SEGMENT)) {
r = LUKS2_split_crypt_and_opal_keys(cd, hdr, vk, &crypt_key, &opal_key);
if (r < 0) {
crypt_free_volume_key(vk);
return r;
}
p_crypt = crypt_key;
p_opal = opal_key ?: vk;
} else {
p_crypt = vk;
p_opal = NULL;
}
if (!crypt_use_keyring_for_vk(cd) || !p_crypt)
use_keyring = false;
else
use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) ||
(flags & CRYPT_ACTIVATE_KEYRING_KEY));
if (use_keyring) {
if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, vk, keyslot)))
if (!(r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd, hdr, p_crypt, keyslot)))
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
}
if (r >= 0 && name)
r = LUKS2_activate(cd, name, vk, flags);
r = LUKS2_activate(cd, name, p_crypt, p_opal, flags);
if (r < 0)
crypt_drop_keyring_key(cd, vk);
crypt_drop_keyring_key(cd, p_crypt);
crypt_free_volume_key(vk);
crypt_free_volume_key(crypt_key);
crypt_free_volume_key(opal_key);
return r < 0 ? r : keyslot;
}

View File

@@ -25,6 +25,7 @@ libcryptsetup_sources = files(
'luks1/af.c',
'luks1/keyencryption.c',
'luks1/keymanage.c',
'luks2/hw_opal/hw_opal.c',
'luks2/luks2_digest.c',
'luks2/luks2_digest_pbkdf2.c',
'luks2/luks2_disk_metadata.c',

View File

@@ -31,6 +31,7 @@
#include "libcryptsetup.h"
#include "luks1/luks.h"
#include "luks2/luks2.h"
#include "luks2/luks2_internal.h"
#include "loopaes/loopaes.h"
#include "verity/verity.h"
#include "tcrypt/tcrypt.h"
@@ -40,6 +41,7 @@
#include "utils_device_locking.h"
#include "internal.h"
#include "keyslot_context.h"
#include "luks2/hw_opal/hw_opal.h"
#define CRYPT_CD_UNRESTRICTED (1 << 0)
#define CRYPT_CD_QUIET (1 << 1)
@@ -241,6 +243,27 @@ uint64_t crypt_get_data_offset_sectors(struct crypt_device *cd)
return cd->data_offset;
}
int crypt_opal_supported(struct crypt_device *cd, struct device *opal_device)
{
int r;
assert(cd);
assert(opal_device);
r = opal_supported(cd, opal_device);
if (r <= 0) {
if (r == -ENOTSUP)
log_err(cd, _("cryptsetup library SED OPAL2 support is disabled."));
else
log_err(cd, _("Device %s does not support OPAL2 HW encryption."),
device_path(opal_device));
r = -EINVAL;
} else
r = 0;
return r;
}
int init_crypto(struct crypt_device *ctx)
{
struct utsname uts;
@@ -783,9 +806,12 @@ static int _crypt_load_luks2(struct crypt_device *cd, int reload, int repair)
if (r)
return r;
if (!reload && !(type = strdup(CRYPT_LUKS2))) {
r = -ENOMEM;
goto out;
if (!reload) {
type = strdup(CRYPT_LUKS2);
if (!type) {
r = -ENOMEM;
goto out;
}
}
if (verify_pbkdf_params(cd, &cd->pbkdf)) {
@@ -2052,7 +2078,8 @@ static int _crypt_format_luks2(struct crypt_device *cd,
integrity, uuid,
sector_size,
data_offset_bytes,
metadata_size_bytes, keyslots_size_bytes);
metadata_size_bytes, keyslots_size_bytes,
0, 0, 0);
if (r < 0)
goto out;
@@ -2133,6 +2160,409 @@ out:
return 0;
}
static int opal_topology_alignment(struct crypt_device *cd,
uint64_t partition_offset_sectors,
uint64_t data_offset_sectors,
uint64_t required_alignment_sectors,
uint64_t default_alignment_bytes,
uint64_t *ret_alignment_offset_bytes,
uint64_t *ret_alignment_bytes,
uint32_t *ret_opal_block_bytes,
uint64_t *ret_opal_alignment_granularity_blocks)
{
bool opal_align;
int r;
uint32_t opal_block_bytes;
uint64_t opal_alignment_granularity_blocks, opal_lowest_lba_blocks;
assert(cd);
assert(ret_alignment_offset_bytes);
assert(ret_alignment_bytes);
assert(ret_opal_block_bytes);
assert(ret_opal_alignment_granularity_blocks);
r = opal_geometry(cd, crypt_data_device(cd), &opal_align, &opal_block_bytes,
&opal_alignment_granularity_blocks, &opal_lowest_lba_blocks);
if (r) {
log_err(cd, _("Cannot get OPAL alignment parameters."));
return -EINVAL;
}
log_dbg(cd, "OPAL geometry: alignment: '%c', logical block size: %" PRIu32
", alignment granularity: %" PRIu64 ", lowest aligned LBA: %" PRIu64,
opal_align ? 'y' : 'n', opal_block_bytes, opal_alignment_granularity_blocks, opal_lowest_lba_blocks);
if (opal_block_bytes < SECTOR_SIZE || NOTPOW2(opal_block_bytes)) {
log_err(cd, _("Bogus OPAL2 logical block size."));
return -EINVAL;
}
if (data_offset_sectors &&
MISALIGNED(data_offset_sectors + partition_offset_sectors, opal_block_bytes / SECTOR_SIZE)) {
log_err(cd, _("Requested data offset is not compatible with OPAL block size."));
return -EINVAL;
}
/* Data offset has priority over data alignment parameter */
if (!data_offset_sectors &&
MISALIGNED(required_alignment_sectors, opal_block_bytes / SECTOR_SIZE)) {
log_err(cd, _("Requested data alignment is not compatible with OPAL alignment."));
return -EINVAL;
}
if (!opal_align) {
*ret_alignment_bytes = required_alignment_sectors ? (required_alignment_sectors * SECTOR_SIZE) : default_alignment_bytes;
*ret_alignment_offset_bytes = 0;
*ret_opal_block_bytes = opal_block_bytes;
*ret_opal_alignment_granularity_blocks = 1;
return 0;
}
if (data_offset_sectors) {
if (MISALIGNED((((data_offset_sectors + partition_offset_sectors) * SECTOR_SIZE) / opal_block_bytes) - opal_lowest_lba_blocks,
opal_alignment_granularity_blocks)) {
// FIXME: Add hint to user on how to fix it
log_err(cd, _("Data offset does not satisfy OPAL alignment requirements."));
return -EINVAL;
}
*ret_alignment_offset_bytes = 0;
*ret_alignment_bytes = 0;
*ret_opal_block_bytes = opal_block_bytes;
*ret_opal_alignment_granularity_blocks = opal_alignment_granularity_blocks;
return 0;
}
if (MISALIGNED(required_alignment_sectors * SECTOR_SIZE, opal_block_bytes * opal_alignment_granularity_blocks)) {
log_err(cd, _("Requested data alignment does not satisfy locking range alignment requirements."));
return -EINVAL;
}
if (required_alignment_sectors)
*ret_alignment_bytes = required_alignment_sectors * SECTOR_SIZE;
else
*ret_alignment_bytes = size_round_up(default_alignment_bytes, opal_block_bytes * opal_alignment_granularity_blocks);
/* data offset is not set, calculate proper aligment */
*ret_alignment_offset_bytes = (partition_offset_sectors * SECTOR_SIZE) % (opal_block_bytes * opal_alignment_granularity_blocks);
if (*ret_alignment_offset_bytes)
*ret_alignment_offset_bytes = opal_block_bytes * opal_alignment_granularity_blocks - *ret_alignment_offset_bytes;
if (*ret_alignment_offset_bytes)
log_dbg(cd, "Compensating misaligned partition offset by %" PRIu64 "bytes.",
*ret_alignment_offset_bytes);
*ret_alignment_offset_bytes += (opal_lowest_lba_blocks * opal_block_bytes);
*ret_opal_block_bytes = opal_block_bytes;
*ret_opal_alignment_granularity_blocks = opal_alignment_granularity_blocks;
log_dbg(cd, "OPAL alignment (%" PRIu32 "/%" PRIu64 "), offset = %" PRIu64 ". Required alignment is %" PRIu64 ".",
opal_block_bytes, opal_alignment_granularity_blocks, *ret_alignment_offset_bytes, *ret_alignment_bytes);
return 0;
}
int crypt_format_luks2_opal(struct crypt_device *cd,
const char *cipher,
const char *cipher_mode,
const char *uuid,
const char *volume_keys,
size_t volume_keys_size,
struct crypt_params_luks2 *params,
struct crypt_params_hw_opal *opal_params)
{
bool opal_range_reset = false, subsystem_overridden = false, sector_size_autodetect = cipher != NULL;
int r;
char cipher_spec[128];
const char *integrity = params ? params->integrity : NULL;
uint32_t sector_size, opal_block_bytes, opal_segment_number = 1; /* We'll use the partition number if available later */
uint64_t alignment_offset_bytes, data_offset_bytes, device_size_bytes, opal_alignment_granularity_blocks,
partition_offset_sectors, range_offset_blocks, range_length_blocks,
required_alignment_bytes, metadata_size_bytes, keyslots_size_bytes;
struct volume_key *user_key = NULL;
if (!cd || !params || !opal_params ||
!opal_params->admin_key || !opal_params->admin_key_size || !opal_params->user_key_size)
return -EINVAL;
if (cd->type) {
log_dbg(cd, "Context already formatted as %s.", cd->type);
return -EINVAL;
}
log_dbg(cd, "Formatting device %s as type LUKS2 with OPAL HW encryption.", mdata_device_path(cd) ?: "(none)");
if (volume_keys_size < opal_params->user_key_size)
return -EINVAL;
if (cipher && (volume_keys_size == opal_params->user_key_size))
return -EINVAL;
if (!crypt_metadata_device(cd)) {
log_err(cd, _("Can't format LUKS without device."));
return -EINVAL;
}
if (params->data_alignment &&
MISALIGNED(cd->data_offset, params->data_alignment)) {
log_err(cd, _("Requested data alignment is not compatible with data offset."));
return -EINVAL;
}
if (params->data_device) {
if (!cd->metadata_device)
cd->metadata_device = cd->device;
else
device_free(cd, cd->device);
cd->device = NULL;
if (device_alloc(cd, &cd->device, params->data_device) < 0)
return -ENOMEM;
}
r = crypt_opal_supported(cd, crypt_data_device(cd));
if (r < 0)
return r;
if (params->sector_size)
sector_size_autodetect = false;
partition_offset_sectors = crypt_dev_partition_offset(device_path(crypt_data_device(cd)));
if (!(cd->type = strdup(CRYPT_LUKS2)))
return -ENOMEM;
if (volume_keys)
cd->volume_key = crypt_alloc_volume_key(volume_keys_size, volume_keys);
else
cd->volume_key = crypt_generate_volume_key(cd, volume_keys_size);
if (!cd->volume_key) {
r = -ENOMEM;
goto out;
}
if (cipher) {
user_key = crypt_alloc_volume_key(opal_params->user_key_size, cd->volume_key->key);
if (!user_key) {
r = -ENOMEM;
goto out;
}
}
r = 0;
if (params->pbkdf)
r = crypt_set_pbkdf_type(cd, params->pbkdf);
else if (verify_pbkdf_params(cd, &cd->pbkdf))
r = init_pbkdf_type(cd, NULL, CRYPT_LUKS2);
if (r < 0)
goto out;
if (cd->metadata_device && !cd->data_offset)
/* For detached header the alignment is used directly as data offset */
cd->data_offset = params->data_alignment;
r = opal_topology_alignment(cd, partition_offset_sectors,
cd->data_offset, params->data_alignment,
DEFAULT_DISK_ALIGNMENT, &alignment_offset_bytes, &required_alignment_bytes,
&opal_block_bytes, &opal_alignment_granularity_blocks);
if (r < 0)
goto out;
if (sector_size_autodetect) {
sector_size = device_optimal_encryption_sector_size(cd, crypt_data_device(cd));
if ((opal_block_bytes * opal_alignment_granularity_blocks) > sector_size)
sector_size = opal_block_bytes * opal_alignment_granularity_blocks;
log_dbg(cd, "Auto-detected optimal encryption sector size for device %s is %d bytes.",
device_path(crypt_data_device(cd)), sector_size);
} else
sector_size = params->sector_size;
/* To ensure it is obvious and explicit that OPAL is being used, set the
* subsystem tag if the user hasn't passed one. */
if (!params->subsystem) {
params->subsystem = "HW-OPAL";
subsystem_overridden = true;
}
/* We need to give the drive a segment number - use the partition number if there is
* one, otherwise the first valid (1) number if it's a single-volume setup */
r = crypt_dev_get_partition_number(device_path(crypt_data_device(cd)));
if (r > 0)
opal_segment_number = r;
if (cipher) {
r = LUKS2_check_encryption_params(cd, cipher, cipher_mode, integrity,
volume_keys_size - opal_params->user_key_size,
params, &integrity);
if (r < 0)
goto out;
}
r = device_size(crypt_data_device(cd), &device_size_bytes);
if (r < 0)
goto out;
r = LUKS2_hdr_get_storage_params(cd, alignment_offset_bytes, required_alignment_bytes,
&metadata_size_bytes, &keyslots_size_bytes, &data_offset_bytes);
if (r < 0)
goto out;
r = -EINVAL;
if (device_size_bytes < data_offset_bytes && !cd->metadata_device) {
log_err(cd, _("Device %s is too small."), device_path(crypt_data_device(cd)));
goto out;
}
device_size_bytes -= data_offset_bytes;
if (MISALIGNED(device_size_bytes, opal_block_bytes * opal_alignment_granularity_blocks)) {
log_err(cd, _("Compensating device size by %" PRIu64 " sectors to align it with OPAL alignment granularity."),
(device_size_bytes % (opal_alignment_granularity_blocks * opal_block_bytes)) / SECTOR_SIZE);
device_size_bytes -= (device_size_bytes % (opal_block_bytes * opal_alignment_granularity_blocks));
}
if (!device_size_bytes)
goto out;
if (cipher) {
r = LUKS2_check_encryption_sector(cd, device_size_bytes, data_offset_bytes, sector_size,
sector_size_autodetect, integrity == NULL,
&sector_size);
if (r < 0)
goto out;
if (*cipher_mode != '\0')
r = snprintf(cipher_spec, sizeof(cipher_spec), "%s-%s", cipher, cipher_mode);
else
r = snprintf(cipher_spec, sizeof(cipher_spec), "%s", cipher);
if (r < 0 || (size_t)r >= sizeof(cipher_spec)) {
r = -EINVAL;
goto out;
}
}
r = LUKS2_generate_hdr(cd, &cd->u.luks2.hdr, cd->volume_key,
cipher ? cipher_spec : NULL, integrity, uuid,
sector_size,
data_offset_bytes,
metadata_size_bytes, keyslots_size_bytes,
device_size_bytes,
opal_segment_number,
opal_params->user_key_size);
if (r < 0)
goto out;
if (params->label || params->subsystem) {
r = LUKS2_hdr_labels(cd, &cd->u.luks2.hdr,
params->label, params->subsystem, 0);
if (r < 0)
goto out;
}
device_set_block_size(crypt_data_device(cd), sector_size);
r = LUKS2_wipe_header_areas(cd, &cd->u.luks2.hdr, cd->metadata_device != NULL);
if (r < 0) {
log_err(cd, _("Cannot wipe header on device %s."),
mdata_device_path(cd));
if (device_size_bytes < LUKS2_hdr_and_areas_size(&cd->u.luks2.hdr))
log_err(cd, _("Device %s is too small."), device_path(crypt_metadata_device(cd)));
goto out;
}
range_offset_blocks = (data_offset_bytes + partition_offset_sectors * SECTOR_SIZE) / opal_block_bytes;
range_length_blocks = device_size_bytes / opal_block_bytes;
r = opal_setup_ranges(cd, crypt_data_device(cd), user_key ?: cd->volume_key,
range_offset_blocks, range_length_blocks,
opal_segment_number, opal_params->admin_key, opal_params->admin_key_size);
if (r < 0) {
if (r == -EPERM)
log_err(cd, _("Incorrect OPAL Admin key."));
else
log_err(cd, _("Cannot setup OPAL segment."));
goto out;
}
opal_range_reset = true;
/* integrity metadata goes in unlocked OPAL locking range */
if (crypt_get_integrity_tag_size(cd)) {
r = opal_unlock(cd, crypt_data_device(cd), opal_segment_number, user_key ?: cd->volume_key);
if (r < 0)
goto out;
r = crypt_wipe_device(cd, crypt_data_device(cd), CRYPT_WIPE_ZERO,
crypt_get_data_offset(cd) * SECTOR_SIZE,
8 * SECTOR_SIZE, 8 * SECTOR_SIZE, NULL, NULL);
if (r < 0) {
if (r == -EBUSY)
log_err(cd, _("Cannot format device %s in use."),
data_device_path(cd));
else if (r == -EACCES) {
log_err(cd, _("Cannot format device %s, permission denied."),
data_device_path(cd));
r = -EINVAL;
} else
log_err(cd, _("Cannot wipe header on device %s."),
data_device_path(cd));
goto out;
}
r = INTEGRITY_format(cd, params->integrity_params, NULL, NULL, 0);
if (r)
log_err(cd, _("Cannot format integrity for device %s."),
data_device_path(cd));
if (r < 0)
goto out;
r = opal_lock(cd, crypt_data_device(cd), opal_segment_number);
if (r < 0)
goto out;
}
/* override sequence id check with format */
r = LUKS2_hdr_write_force(cd, &cd->u.luks2.hdr);
if (r < 0) {
if (r == -EBUSY)
log_err(cd, _("Cannot format device %s in use."),
mdata_device_path(cd));
else if (r == -EACCES) {
log_err(cd, _("Cannot format device %s, permission denied."),
mdata_device_path(cd));
r = -EINVAL;
} else
log_err(cd, _("Cannot format device %s."),
mdata_device_path(cd));
}
out:
crypt_free_volume_key(user_key);
if (subsystem_overridden)
params->subsystem = NULL;
if (r >= 0)
return 0;
if (opal_range_reset &&
(opal_reset_segment(cd, crypt_data_device(cd), opal_segment_number,
opal_params->admin_key, opal_params->admin_key_size) < 0))
log_err(cd, _("Locking range %d reset on device %s failed."),
opal_segment_number, device_path(crypt_data_device(cd)));
LUKS2_hdr_free(cd, &cd->u.luks2.hdr);
crypt_set_null_type(cd);
crypt_free_volume_key(cd->volume_key);
cd->volume_key = NULL;
return r;
}
static int _crypt_format_loopaes(struct crypt_device *cd,
const char *cipher,
const char *uuid,
@@ -3023,11 +3453,6 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
uint64_t old_size;
int r;
/*
* FIXME: Also with LUKS2 we must not allow resize when there's
* explicit size stored in metadata (length != "dynamic")
*/
/* Device context type must be initialized */
if (!cd || !cd->type || !name)
return -EINVAL;
@@ -3037,6 +3462,11 @@ int crypt_resize(struct crypt_device *cd, const char *name, uint64_t new_size)
return -ENOTSUP;
}
if (isLUKS2(cd->type) && !LUKS2_segments_dynamic_size(&cd->u.luks2.hdr)) {
log_err(cd, _("Can not resize LUKS2 device with static size."));
return -EINVAL;
}
log_dbg(cd, "Resizing device %s to %" PRIu64 " sectors.", name, new_size);
r = dm_query_device(cd, name, DM_ACTIVE_CRYPT_KEYSIZE | DM_ACTIVE_CRYPT_KEY |
@@ -4054,12 +4484,13 @@ int create_or_reload_device(struct crypt_device *cd, const char *name,
int r;
enum devcheck device_check;
struct dm_target *tgt;
uint64_t offset;
if (!type || !name || !single_segment(dmd))
return -EINVAL;
tgt = &dmd->segment;
if (tgt->type != DM_CRYPT && tgt->type != DM_INTEGRITY)
if (tgt->type != DM_CRYPT && tgt->type != DM_INTEGRITY && tgt->type != DM_LINEAR)
return -EINVAL;
/* drop CRYPT_ACTIVATE_REFRESH flag if any device is inactive */
@@ -4070,11 +4501,12 @@ int create_or_reload_device(struct crypt_device *cd, const char *name,
if (dmd->flags & CRYPT_ACTIVATE_REFRESH)
r = _reload_device(cd, name, dmd);
else {
if (tgt->type == DM_CRYPT) {
if (tgt->type == DM_CRYPT || tgt->type == DM_LINEAR) {
device_check = dmd->flags & CRYPT_ACTIVATE_SHARED ? DEV_OK : DEV_EXCL;
offset = tgt->type == DM_CRYPT ? tgt->u.crypt.offset : tgt->u.linear.offset;
r = device_block_adjust(cd, tgt->data_device, device_check,
tgt->u.crypt.offset, &dmd->size, &dmd->flags);
offset, &dmd->size, &dmd->flags);
if (!r) {
tgt->size = dmd->size;
r = dm_create_device(cd, name, type, dmd);
@@ -4137,7 +4569,7 @@ static int _open_and_activate(struct crypt_device *cd,
{
bool use_keyring;
int r;
struct volume_key *vk = NULL;
struct volume_key *p_crypt, *p_opal, *crypt_key = NULL, *opal_key = NULL, *vk = NULL;
r = LUKS2_keyslot_open(cd, keyslot,
(flags & CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY) ?
@@ -4147,7 +4579,21 @@ static int _open_and_activate(struct crypt_device *cd,
return r;
keyslot = r;
if (!crypt_use_keyring_for_vk(cd))
if (LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) {
r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr,
vk, &crypt_key,
&opal_key);
if (r < 0)
return r;
p_crypt = crypt_key;
p_opal = opal_key ?: vk;
} else {
p_crypt = vk;
p_opal = NULL;
}
if (!crypt_use_keyring_for_vk(cd) || !p_crypt)
use_keyring = false;
else
use_keyring = ((name && !crypt_is_cipher_null(crypt_get_cipher(cd))) ||
@@ -4155,18 +4601,20 @@ static int _open_and_activate(struct crypt_device *cd,
if (use_keyring) {
r = LUKS2_volume_key_load_in_keyring_by_keyslot(cd,
&cd->u.luks2.hdr, vk, keyslot);
&cd->u.luks2.hdr, p_crypt, keyslot);
if (r < 0)
goto out;
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
}
if (name)
r = LUKS2_activate(cd, name, vk, flags);
r = LUKS2_activate(cd, name, p_crypt, p_opal, flags);
out:
if (r < 0)
crypt_drop_keyring_key(cd, vk);
crypt_drop_keyring_key(cd, p_crypt);
crypt_free_volume_key(vk);
crypt_free_volume_key(crypt_key);
crypt_free_volume_key(opal_key);
return r < 0 ? r : keyslot;
}
@@ -4594,7 +5042,7 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
uint32_t flags)
{
bool use_keyring;
struct volume_key *vk = NULL;
struct volume_key *p_opal, *crypt_key = NULL, *opal_key = NULL, *vk = NULL, *p_crypt = NULL;
int r;
if (!cd ||
@@ -4669,7 +5117,23 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
if (r > 0)
r = 0;
if (!crypt_use_keyring_for_vk(cd))
if (LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) {
r = LUKS2_split_crypt_and_opal_keys(cd, &cd->u.luks2.hdr,
vk, &crypt_key,
&opal_key);
if (r < 0) {
crypt_free_volume_key(vk);
return r;
}
p_crypt = crypt_key;
p_opal = opal_key ?: vk;
} else {
p_crypt = vk;
p_opal = NULL;
}
if (!crypt_use_keyring_for_vk(cd) || !p_crypt)
use_keyring = false;
else
use_keyring = (name && !crypt_is_cipher_null(crypt_get_cipher(cd))) ||
@@ -4677,15 +5141,15 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
if (!r && use_keyring) {
r = LUKS2_key_description_by_segment(cd,
&cd->u.luks2.hdr, vk, CRYPT_DEFAULT_SEGMENT);
&cd->u.luks2.hdr, p_crypt, CRYPT_DEFAULT_SEGMENT);
if (!r)
r = crypt_volume_key_load_in_keyring(cd, vk);
r = crypt_volume_key_load_in_keyring(cd, p_crypt);
if (!r)
flags |= CRYPT_ACTIVATE_KEYRING_KEY;
}
if (!r && name)
r = LUKS2_activate(cd, name, vk, flags);
r = LUKS2_activate(cd, name, p_crypt, p_opal, flags);
} else if (isVERITY(cd->type)) {
r = crypt_activate_by_signed_key(cd, name, volume_key, volume_key_size, NULL, 0, flags);
} else if (isTCRYPT(cd->type)) {
@@ -4716,8 +5180,11 @@ int crypt_activate_by_volume_key(struct crypt_device *cd,
}
if (r < 0)
crypt_drop_keyring_key(cd, vk);
crypt_drop_keyring_key(cd, p_crypt);
crypt_free_volume_key(vk);
crypt_free_volume_key(crypt_key);
crypt_free_volume_key(opal_key);
return r;
}
@@ -4819,6 +5286,17 @@ int crypt_deactivate_by_name(struct crypt_device *cd, const char *name, uint32_t
cd = fake_cd;
}
if (flags & (CRYPT_DEACTIVATE_DEFERRED | CRYPT_DEACTIVATE_DEFERRED_CANCEL)) {
struct luks2_hdr *hdr = crypt_get_hdr(cd, CRYPT_LUKS2);
if (hdr) {
json_object *jobj = json_segments_get_segment(LUKS2_get_segments_jobj(hdr), 0);
if (jobj && !strcmp(json_segment_type(jobj), "hw-opal")) {
log_err(cd, _("OPAL does not support deferred deactivation."));
return -EINVAL;
}
}
}
/* skip holders detection and early abort when some flags raised */
if (flags & (CRYPT_DEACTIVATE_FORCE | CRYPT_DEACTIVATE_DEFERRED | CRYPT_DEACTIVATE_DEFERRED_CANCEL))
get_flags &= ~DM_ACTIVE_HOLDERS;
@@ -5565,6 +6043,12 @@ const char *crypt_keyslot_get_encryption(struct crypt_device *cd, int keyslot, s
return cd->u.luks2.keyslot_cipher;
}
if (LUKS2_segment_is_hw_opal(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT)) {
/* Fallback to default LUKS2 keyslot encryption */
*key_size = DEFAULT_LUKS2_KEYSLOT_KEYBITS / 8;
return DEFAULT_LUKS2_KEYSLOT_CIPHER;
}
/* Try to reuse volume encryption parameters */
cipher = LUKS2_get_cipher(&cd->u.luks2.hdr, CRYPT_DEFAULT_SEGMENT);
if (!LUKS2_keyslot_cipher_incompatible(cd, cipher)) {
@@ -5901,6 +6385,10 @@ int crypt_convert(struct crypt_device *cd,
/* Internal access function to header pointer */
void *crypt_get_hdr(struct crypt_device *cd, const char *type)
{
/* One type can be OPAL */
if (isLUKS2(type) && isLUKS2(cd->type))
return &cd->u.luks2.hdr;
/* If requested type differs, ignore it */
if (strcmp(cd->type, type))
return NULL;
@@ -5911,9 +6399,6 @@ void *crypt_get_hdr(struct crypt_device *cd, const char *type)
if (isLUKS1(cd->type))
return &cd->u.luks1.hdr;
if (isLUKS2(cd->type))
return &cd->u.luks2.hdr;
if (isLOOPAES(cd->type))
return &cd->u.loopaes;

View File

@@ -210,6 +210,24 @@ static int _path_get_uint64(const char *sysfs_path, uint64_t *value, const char
return _read_uint64(path, value);
}
int crypt_dev_get_partition_number(const char *dev_path)
{
uint64_t partno;
struct stat st;
if (stat(dev_path, &st) < 0)
return 0;
if (!S_ISBLK(st.st_mode))
return 0;
if (!_sysfs_get_uint64(major(st.st_rdev), minor(st.st_rdev),
&partno, "partition"))
return -EINVAL;
return (int)partno;
}
int crypt_dev_is_rotational(int major, int minor)
{
uint64_t val;

View File

@@ -635,6 +635,28 @@ if get_option('blkid')
endforeach
endif
have = get_option('hw-opal')
if have
if cc.has_header('linux/sed-opal.h')
foreach symbol : [
'OPAL_FL_SUM_SUPPORTED',
'IOC_OPAL_GET_LR_STATUS',
'IOC_OPAL_GET_GEOMETRY',
]
if not cc.has_header_symbol('linux/sed-opal.h', symbol)
have = false
warning('OPAL support disabled, linux/sed-opal.h does not define ' + symbol)
endif
endforeach
else
have = false
warning('OPAL support disabled, linux/sed-opal.h not found, requires kernel v6.4.')
endif
endif
conf.set10('HAVE_HW_OPAL', have, description: 'Define to 1 to enable OPAL support.')
# ==========================================================================
# Check compiler support for symver function attribute

View File

@@ -40,6 +40,7 @@ option('fuzzing-engine', type : 'string', description : 'specify LDFLAGS for lin
option('fuzz-targets', type : 'boolean', description : 'enable building fuzz targets', value : false)
option('gcrypt-pbkdf2', type : 'feature', description : 'enable internal gcrypt PBKDF2', value : 'auto')
option('gcrypt-argon2', type : 'feature', description : 'enable internal gcrypt Argon2', value : 'auto')
option('hw-opal', type : 'boolean', description : 'support LUKS2 extension for SED OPAL HW encryption', value : true)
option('integritysetup', type : 'boolean', description : 'integritysetup Support', value : true)
option('internal-sse-argon2', type : 'boolean', description : 'use internal SSE implementation of Argon2 PBKDF', value : false)
option('kernel_crypto', type : 'boolean', description : 'kernel userspace crypto (no benchmark and tcrypt)', value : true)

View File

@@ -435,8 +435,9 @@ int tools_write_mk(const char *file, const char *key, int keysize)
void tools_package_version(const char *name, bool use_pwlibs)
{
bool udev = false, blkid = false, keyring = false, fips = false;
bool kernel_capi = false, pwquality = false, passwdqc = false;
bool udev = false, blkid = false, keyring = false, fips = false,
kernel_capi = false, pwquality = false, passwdqc = false,
hw_opal = false;
#ifdef USE_UDEV
udev = true;
#endif
@@ -457,12 +458,16 @@ void tools_package_version(const char *name, bool use_pwlibs)
#elif defined(ENABLE_PASSWDQC)
passwdqc = true;
#endif
log_std("%s %s flags: %s%s%s%s%s%s%s\n", name, PACKAGE_VERSION,
#ifdef HAVE_HW_OPAL
hw_opal = true;
#endif
log_std("%s %s flags: %s%s%s%s%s%s%s%s\n", name, PACKAGE_VERSION,
udev ? "UDEV " : "",
blkid ? "BLKID " : "",
keyring ? "KEYRING " : "",
fips ? "FIPS " : "",
kernel_capi ? "KERNEL_CAPI " : "",
pwquality && use_pwlibs ? "PWQUALITY " : "",
passwdqc && use_pwlibs ? "PASSWDQC " : "");
passwdqc && use_pwlibs ? "PASSWDQC " : "",
hw_opal ? "HW_OPAL " : "");
}