Skip to content

Commit

Permalink
block: sed-opal: keyring support for SED keys
Browse files Browse the repository at this point in the history
Extend the SED block driver so it can alternatively
obtain a key from a sed-opal kernel keyring. The SED
ioctls will indicate the source of the key, either
directly in the ioctl data or from the keyring.

This allows the use of SED commands in scripts such as
udev scripts so that drives may be automatically unlocked
as they become available.

Signed-off-by: Greg Joyce <gjoyce@linux.vnet.ibm.com>
Reviewed-by: Jonathan Derrick <jonathan.derrick@linux.dev>
Acked-by: Jarkko Sakkinen <jarkko@kernel.org>
Link: https://lore.kernel.org/r/20230721211534.3437070-4-gjoyce@linux.vnet.ibm.com
Signed-off-by: Jens Axboe <axboe@kernel.dk>
  • Loading branch information
Greg Joyce authored and Jens Axboe committed Aug 22, 2023
1 parent 5c82efc commit 3bfeb61
Show file tree
Hide file tree
Showing 4 changed files with 184 additions and 3 deletions.
2 changes: 2 additions & 0 deletions block/Kconfig
Original file line number Diff line number Diff line change
Expand Up @@ -184,6 +184,8 @@ config BLK_DEBUG_FS_ZONED

config BLK_SED_OPAL
bool "Logic for interfacing with Opal enabled SEDs"
depends on KEYS
select PSERIES_PLPKS if PPC_PSERIES
help
Builds Logic for interfacing with Opal enabled controllers.
Enabling this option enables users to setup/unlock/lock
Expand Down
174 changes: 172 additions & 2 deletions block/sed-opal.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@
#include <linux/sed-opal.h>
#include <linux/string.h>
#include <linux/kdev_t.h>
#include <linux/key.h>
#include <linux/key-type.h>
#include <keys/user-type.h>

#include "opal_proto.h"

Expand All @@ -29,6 +32,8 @@
/* Number of bytes needed by cmd_finalize. */
#define CMD_FINALIZE_BYTES_NEEDED 7

static struct key *sed_opal_keyring;

struct opal_step {
int (*fn)(struct opal_dev *dev, void *data);
void *data;
Expand Down Expand Up @@ -269,6 +274,101 @@ static void print_buffer(const u8 *ptr, u32 length)
#endif
}

/*
* Allocate/update a SED Opal key and add it to the SED Opal keyring.
*/
static int update_sed_opal_key(const char *desc, u_char *key_data, int keylen)
{
key_ref_t kr;

if (!sed_opal_keyring)
return -ENOKEY;

kr = key_create_or_update(make_key_ref(sed_opal_keyring, true), "user",
desc, (const void *)key_data, keylen,
KEY_USR_VIEW | KEY_USR_SEARCH | KEY_USR_WRITE,
KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_BUILT_IN |
KEY_ALLOC_BYPASS_RESTRICTION);
if (IS_ERR(kr)) {
pr_err("Error adding SED key (%ld)\n", PTR_ERR(kr));
return PTR_ERR(kr);
}

return 0;
}

/*
* Read a SED Opal key from the SED Opal keyring.
*/
static int read_sed_opal_key(const char *key_name, u_char *buffer, int buflen)
{
int ret;
key_ref_t kref;
struct key *key;

if (!sed_opal_keyring)
return -ENOKEY;

kref = keyring_search(make_key_ref(sed_opal_keyring, true),
&key_type_user, key_name, true);

if (IS_ERR(kref))
ret = PTR_ERR(kref);

key = key_ref_to_ptr(kref);
down_read(&key->sem);
ret = key_validate(key);
if (ret == 0) {
if (buflen > key->datalen)
buflen = key->datalen;

ret = key->type->read(key, (char *)buffer, buflen);
}
up_read(&key->sem);

key_ref_put(kref);

return ret;
}

static int opal_get_key(struct opal_dev *dev, struct opal_key *key)
{
int ret = 0;

switch (key->key_type) {
case OPAL_INCLUDED:
/* the key is ready to use */
break;
case OPAL_KEYRING:
/* the key is in the keyring */
ret = read_sed_opal_key(OPAL_AUTH_KEY, key->key, OPAL_KEY_MAX);
if (ret > 0) {
if (ret > U8_MAX) {
ret = -ENOSPC;
goto error;
}
key->key_len = ret;
key->key_type = OPAL_INCLUDED;
}
break;
default:
ret = -EINVAL;
break;
}
if (ret < 0)
goto error;

/* must have a PEK by now or it's an error */
if (key->key_type != OPAL_INCLUDED || key->key_len == 0) {
ret = -EINVAL;
goto error;
}
return 0;
error:
pr_debug("Error getting password: %d\n", ret);
return ret;
}

static bool check_tper(const void *data)
{
const struct d0_tper_features *tper = data;
Expand Down Expand Up @@ -2459,6 +2559,9 @@ static int opal_secure_erase_locking_range(struct opal_dev *dev,
};
int ret;

ret = opal_get_key(dev, &opal_session->opal_key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, erase_steps, ARRAY_SIZE(erase_steps));
Expand Down Expand Up @@ -2492,6 +2595,9 @@ static int opal_revertlsp(struct opal_dev *dev, struct opal_revert_lsp *rev)
};
int ret;

ret = opal_get_key(dev, &rev->key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, steps, ARRAY_SIZE(steps));
Expand All @@ -2510,6 +2616,9 @@ static int opal_erase_locking_range(struct opal_dev *dev,
};
int ret;

ret = opal_get_key(dev, &opal_session->opal_key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, erase_steps, ARRAY_SIZE(erase_steps));
Expand Down Expand Up @@ -2538,6 +2647,9 @@ static int opal_enable_disable_shadow_mbr(struct opal_dev *dev,
opal_mbr->enable_disable != OPAL_MBR_DISABLE)
return -EINVAL;

ret = opal_get_key(dev, &opal_mbr->key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, mbr_steps, ARRAY_SIZE(mbr_steps));
Expand All @@ -2563,6 +2675,9 @@ static int opal_set_mbr_done(struct opal_dev *dev,
mbr_done->done_flag != OPAL_MBR_NOT_DONE)
return -EINVAL;

ret = opal_get_key(dev, &mbr_done->key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, mbr_steps, ARRAY_SIZE(mbr_steps));
Expand All @@ -2584,6 +2699,9 @@ static int opal_write_shadow_mbr(struct opal_dev *dev,
if (info->size == 0)
return 0;

ret = opal_get_key(dev, &info->key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, mbr_steps, ARRAY_SIZE(mbr_steps));
Expand Down Expand Up @@ -2641,6 +2759,9 @@ static int opal_add_user_to_lr(struct opal_dev *dev,
return -EINVAL;
}

ret = opal_get_key(dev, &lk_unlk->session.opal_key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, steps, ARRAY_SIZE(steps));
Expand All @@ -2663,6 +2784,10 @@ static int opal_reverttper(struct opal_dev *dev, struct opal_key *opal, bool psi

int ret;

ret = opal_get_key(dev, opal);

if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
if (psid)
Expand Down Expand Up @@ -2763,6 +2888,9 @@ static int opal_lock_unlock(struct opal_dev *dev,
if (lk_unlk->session.who > OPAL_USER9)
return -EINVAL;

ret = opal_get_key(dev, &lk_unlk->session.opal_key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
opal_lock_check_for_saved_key(dev, lk_unlk);
ret = __opal_lock_unlock(dev, lk_unlk);
Expand All @@ -2786,6 +2914,9 @@ static int opal_take_ownership(struct opal_dev *dev, struct opal_key *opal)
if (!dev)
return -ENODEV;

ret = opal_get_key(dev, opal);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, owner_steps, ARRAY_SIZE(owner_steps));
Expand All @@ -2808,6 +2939,9 @@ static int opal_activate_lsp(struct opal_dev *dev,
if (!opal_lr_act->num_lrs || opal_lr_act->num_lrs > OPAL_MAX_LRS)
return -EINVAL;

ret = opal_get_key(dev, &opal_lr_act->key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, active_steps, ARRAY_SIZE(active_steps));
Expand All @@ -2826,6 +2960,9 @@ static int opal_setup_locking_range(struct opal_dev *dev,
};
int ret;

ret = opal_get_key(dev, &opal_lrs->session.opal_key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, lr_steps, ARRAY_SIZE(lr_steps));
Expand Down Expand Up @@ -2879,6 +3016,14 @@ static int opal_set_new_pw(struct opal_dev *dev, struct opal_new_pw *opal_pw)
ret = execute_steps(dev, pw_steps, ARRAY_SIZE(pw_steps));
mutex_unlock(&dev->dev_lock);

if (ret)
return ret;

/* update keyring with new password */
ret = update_sed_opal_key(OPAL_AUTH_KEY,
opal_pw->new_user_pw.opal_key.key,
opal_pw->new_user_pw.opal_key.key_len);

return ret;
}

Expand All @@ -2899,6 +3044,9 @@ static int opal_activate_user(struct opal_dev *dev,
return -EINVAL;
}

ret = opal_get_key(dev, &opal_session->opal_key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);
ret = execute_steps(dev, act_steps, ARRAY_SIZE(act_steps));
Expand Down Expand Up @@ -2985,6 +3133,9 @@ static int opal_generic_read_write_table(struct opal_dev *dev,
{
int ret, bit_set;

ret = opal_get_key(dev, &rw_tbl->key);
if (ret)
return ret;
mutex_lock(&dev->dev_lock);
setup_opal_dev(dev);

Expand Down Expand Up @@ -3053,9 +3204,9 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
if (!capable(CAP_SYS_ADMIN))
return -EACCES;
if (!dev)
return -ENOTSUPP;
return -EOPNOTSUPP;
if (!(dev->flags & OPAL_FL_SUPPORTED))
return -ENOTSUPP;
return -EOPNOTSUPP;

if (cmd & IOC_IN) {
p = memdup_user(arg, _IOC_SIZE(cmd));
Expand Down Expand Up @@ -3137,3 +3288,22 @@ int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *arg)
return ret;
}
EXPORT_SYMBOL_GPL(sed_ioctl);

static int __init sed_opal_init(void)
{
struct key *kr;

kr = keyring_alloc(".sed_opal",
GLOBAL_ROOT_UID, GLOBAL_ROOT_GID, current_cred(),
(KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW |
KEY_USR_READ | KEY_USR_SEARCH | KEY_USR_WRITE,
KEY_ALLOC_NOT_IN_QUOTA,
NULL, NULL);
if (IS_ERR(kr))
return PTR_ERR(kr);

sed_opal_keyring = kr;

return 0;
}
late_initcall(sed_opal_init);
3 changes: 3 additions & 0 deletions include/linux/sed-opal.h
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ bool opal_unlock_from_suspend(struct opal_dev *dev);
struct opal_dev *init_opal_dev(void *data, sec_send_recv *send_recv);
int sed_ioctl(struct opal_dev *dev, unsigned int cmd, void __user *ioctl_ptr);

#define OPAL_AUTH_KEY "opal-boot-pin"
#define OPAL_AUTH_KEY_PREV "opal-boot-pin-prev"

static inline bool is_sed_ioctl(unsigned int cmd)
{
switch (cmd) {
Expand Down
8 changes: 7 additions & 1 deletion include/uapi/linux/sed-opal.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,16 @@ enum opal_lock_flags {
OPAL_SAVE_FOR_LOCK = 0x01,
};

enum opal_key_type {
OPAL_INCLUDED = 0, /* key[] is the key */
OPAL_KEYRING, /* key is in keyring */
};

struct opal_key {
__u8 lr;
__u8 key_len;
__u8 __align[6];
__u8 key_type;
__u8 __align[5];
__u8 key[OPAL_KEY_MAX];
};

Expand Down

0 comments on commit 3bfeb61

Please sign in to comment.