1
0
Fork 0

Merging upstream version 2.8.

Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
Daniel Baumann 2025-02-16 12:26:13 +01:00
parent 60735e10df
commit 2b9f904876
Signed by: daniel
GPG key ID: FBB4F0E80A80222F
407 changed files with 2341 additions and 926 deletions

4
plugins/sed/meson.build Normal file
View file

@ -0,0 +1,4 @@
sources += [
'plugins/sed/sed.c',
'plugins/sed/sedopal_cmd.c',
]

178
plugins/sed/sed.c Normal file
View file

@ -0,0 +1,178 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <fcntl.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <inttypes.h>
#include <linux/fs.h>
#include <sys/stat.h>
#include "common.h"
#include "nvme.h"
#include "libnvme.h"
#include "nvme-print.h"
#include "sedopal_cmd.h"
#include <linux/sed-opal.h>
#define CREATE_CMD
#include "sed.h"
OPT_ARGS(no_opts) = {
OPT_END()
};
OPT_ARGS(key_opts) = {
OPT_FLAG("ask-key", 'k', &sedopal_ask_key,
"prompt for SED authentication key"),
OPT_END()
};
OPT_ARGS(revert_opts) = {
OPT_FLAG("destructive", 'e', &sedopal_destructive_revert,
"destructive revert"),
OPT_FLAG("psid", 'p', &sedopal_psid_revert, "PSID revert"),
OPT_END()
};
/*
* Open the NVMe device specified on the command line. It must be the
* NVMe block device (e.g. /dev/nvme0n1).
*/
static int sed_opal_open_device(struct nvme_dev **dev, int argc, char **argv,
const char *desc, struct argconfig_commandline_options *opts)
{
int err;
err = parse_and_open(dev, argc, argv, desc, opts);
if (err)
return err;
if (!S_ISBLK((*dev)->direct.stat.st_mode)) {
fprintf(stderr,
"ERROR : The NVMe block device must be specified\n");
err = -EINVAL;
dev_close(*dev);
}
return err;
}
static int sed_opal_discover(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{
int err;
const char *desc = "Query SED device and display locking features";
struct nvme_dev *dev;
err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
if (err)
return err;
err = sedopal_cmd_discover(dev->direct.fd);
dev_close(dev);
return err;
}
static int sed_opal_initialize(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{
int err;
const char *desc = "Initialize a SED device for locking";
struct nvme_dev *dev;
err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
if (err)
return err;
err = sedopal_cmd_initialize(dev->direct.fd);
if (err != 0)
fprintf(stderr, "initialize: SED error - %s\n",
sedopal_error_to_text(err));
dev_close(dev);
return err;
}
static int sed_opal_revert(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{
int err;
const char *desc = "Revert a SED device from locking state";
struct nvme_dev *dev;
err = sed_opal_open_device(&dev, argc, argv, desc, revert_opts);
if (err)
return err;
err = sedopal_cmd_revert(dev->direct.fd);
if (err != 0)
fprintf(stderr, "revert: SED error - %s\n",
sedopal_error_to_text(err));
dev_close(dev);
return err;
}
static int sed_opal_lock(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{
int err;
const char *desc = "Lock a SED device";
struct nvme_dev *dev;
err = sed_opal_open_device(&dev, argc, argv, desc, key_opts);
if (err)
return err;
err = sedopal_cmd_lock(dev->direct.fd);
if (err != 0)
fprintf(stderr, "lock: SED error - %s\n",
sedopal_error_to_text(err));
dev_close(dev);
return err;
}
static int sed_opal_unlock(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{
int err;
const char *desc = "Unlock a SED device";
struct nvme_dev *dev;
err = sed_opal_open_device(&dev, argc, argv, desc, key_opts);
if (err)
return err;
err = sedopal_cmd_unlock(dev->direct.fd);
if (err != 0)
fprintf(stderr, "unlock: SED error - %s\n",
sedopal_error_to_text(err));
dev_close(dev);
return err;
}
static int sed_opal_password(int argc, char **argv, struct command *cmd,
struct plugin *plugin)
{
int err;
const char *desc = "Change the locking password of a SED device";
struct nvme_dev *dev;
err = sed_opal_open_device(&dev, argc, argv, desc, no_opts);
if (err)
return err;
err = sedopal_cmd_password(dev->direct.fd);
if (err != 0)
fprintf(stderr, "password: SED error - %s\n",
sedopal_error_to_text(err));
dev_close(dev);
return err;
}

19
plugins/sed/sed.h Normal file
View file

@ -0,0 +1,19 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#undef CMD_INC_FILE
#define CMD_INC_FILE plugins/sed/sed
#include "cmd.h"
#include <linux/sed-opal.h>
PLUGIN(NAME("sed", "SED Opal Command Set", NVME_VERSION),
COMMAND_LIST(
ENTRY("discover", "Discover SED Opal Locking Features", sed_opal_discover, "1")
ENTRY("initialize", "Initialize a SED Opal Device for locking", sed_opal_initialize)
ENTRY("revert", "Revert a SED Opal Device from locking", sed_opal_revert)
ENTRY("lock", "Lock a SED Opal Device", sed_opal_lock)
ENTRY("unlock", "Unlock a SED Opal Device", sed_opal_unlock)
ENTRY("password", "Change the SED Opal Device password", sed_opal_password)
)
);
#include "define_cmd.h"

512
plugins/sed/sedopal_cmd.c Normal file
View file

@ -0,0 +1,512 @@
// SPDX-License-Identifier: GPL-2.0-or-later
#include <ctype.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/ioctl.h>
#include <sys/mount.h>
#include <linux/sed-opal.h>
#include "sedopal_spec.h"
#include "sedopal_cmd.h"
/*
* ask user for key rather than obtaining it from kernel keyring
*/
bool sedopal_ask_key;
/*
* initiate dialog to ask for and confirm new password
*/
bool sedopal_ask_new_key;
/*
* perform a destructive drive revert
*/
bool sedopal_destructive_revert;
/*
* perform a PSID drive revert
*/
bool sedopal_psid_revert;
/*
* Map method status codes to error text
*/
static const char * const sedopal_errors[] = {
[SED_STATUS_SUCCESS] = "Success",
[SED_STATUS_NOT_AUTHORIZED] = "Host Not Authorized",
[SED_STATUS_OBSOLETE_1] = "Obsolete",
[SED_STATUS_SP_BUSY] = "SP Session Busy",
[SED_STATUS_SP_FAILED] = "SP Failed",
[SED_STATUS_SP_DISABLED] = "SP Disabled",
[SED_STATUS_SP_FROZEN] = "SP Frozen",
[SED_STATUS_NO_SESSIONS_AVAILABLE] = "No Sessions Available",
[SED_STATUS_UNIQUENESS_CONFLICT] = "Uniqueness Conflict",
[SED_STATUS_INSUFFICIENT_SPACE] = "Insufficient Space",
[SED_STATUS_INSUFFICIENT_ROWS] = "Insufficient Rows",
[SED_STATUS_OBSOLETE_2] = "Obsolete",
[SED_STATUS_INVALID_PARAMETER] = "Invalid Parameter",
[SED_STATUS_OBSOLETE_3] = "Obsolete",
[SED_STATUS_OBSOLETE_4] = "Obsolete",
[SED_STATUS_TPER_MALFUNCTION] = "TPER Malfunction",
[SED_STATUS_TRANSACTION_FAILURE] = "Transaction Failure",
[SED_STATUS_RESPONSE_OVERFLOW] = "Response Overflow",
[SED_STATUS_AUTHORITY_LOCKED_OUT] = "Authority Locked Out",
};
const char *sedopal_error_to_text(int code)
{
if (code == SED_STATUS_FAIL)
return "Failed";
if (code == SED_STATUS_NO_METHOD_STATUS)
return "Method returned no status";
if (code < SED_STATUS_SUCCESS ||
code > SED_STATUS_AUTHORITY_LOCKED_OUT)
return("Unknown Error");
return sedopal_errors[code];
}
/*
* Read a user entered password and do some basic validity checks.
*/
char *sedopal_get_password(char *prompt)
{
char *pass;
int len;
pass = getpass(prompt);
if (pass == NULL)
return NULL;
len = strlen(pass);
if (len < SEDOPAL_MIN_PASSWORD_LEN)
return NULL;
if (len > SEDOPAL_MAX_PASSWORD_LEN)
return NULL;
return pass;
}
/*
* Initialize a SED Opal key. The key can either specify that the actual
* key should be looked up in the kernel keyring, or it should be
* populated in the key by prompting the user.
*/
int sedopal_set_key(struct opal_key *key)
{
#if !HAVE_KEY_TYPE
/*
* If key_type isn't avaialable, force key prompt
*/
sedopal_ask_key = true;
#endif
if (sedopal_ask_key) {
char *pass;
char *prompt;
/*
* set proper prompt
*/
if (sedopal_ask_new_key)
prompt = SEDOPAL_NEW_PW_PROMPT;
else {
if (sedopal_psid_revert)
prompt = SEDOPAL_PSID_PROMPT;
else
prompt = SEDOPAL_CURRENT_PW_PROMPT;
}
pass = sedopal_get_password(prompt);
if (pass == NULL)
return -EINVAL;
#if HAVE_KEY_TYPE
key->key_type = OPAL_INCLUDED;
#endif
key->key_len = strlen(pass);
memcpy(key->key, pass, key->key_len);
/*
* If getting a new key, ask for it to be re-entered
* and verify the two entries are the same.
*/
if (sedopal_ask_new_key) {
pass = sedopal_get_password(SEDOPAL_REENTER_PW_PROMPT);
if (strncmp((char *)key->key, pass, key->key_len)) {
fprintf(stderr,
"Error: passwords don't match\n");
return -EINVAL;
}
}
} else {
#if HAVE_KEY_TYPE
key->key_type = OPAL_KEYRING;
#endif
key->key_len = 0;
}
key->lr = 0;
return 0;
}
/*
* Prepare a drive for SED Opal locking.
*/
int sedopal_cmd_initialize(int fd)
{
int rc;
struct opal_key key;
struct opal_lr_act lr_act = {};
struct opal_user_lr_setup lr_setup = {};
sedopal_ask_key = true;
rc = sedopal_set_key(&key);
if (rc != 0)
return rc;
/*
* take ownership of the device
*/
rc = ioctl(fd, IOC_OPAL_TAKE_OWNERSHIP, &key);
if (rc != 0) {
fprintf(stderr,
"Error: failed to take device ownership - %d\n", rc);
return rc;
}
/*
* activate lsp
*/
lr_act.num_lrs = 1;
lr_act.sum = false;
lr_act.key = key;
rc = ioctl(fd, IOC_OPAL_ACTIVATE_LSP, &lr_act);
if (rc != 0) {
fprintf(stderr, "Error: failed to activate LSP - %d\n", rc);
return rc;
}
/*
* setup global locking range
*/
lr_setup.range_start = 0;
lr_setup.range_length = 0;
lr_setup.RLE = true;
lr_setup.WLE = true;
lr_setup.session.opal_key = key;
lr_setup.session.sum = 0;
lr_setup.session.who = OPAL_ADMIN1;
rc = ioctl(fd, IOC_OPAL_LR_SETUP, &lr_setup);
if (rc != 0) {
fprintf(stderr,
"Error: failed to setup locking range - %d\n", rc);
return rc;
}
return rc;
}
/*
* Lock a SED Opal drive
*/
int sedopal_cmd_lock(int fd)
{
return sedopal_lock_unlock(fd, OPAL_LK);
}
/*
* Unlock a SED Opal drive
*/
int sedopal_cmd_unlock(int fd)
{
return sedopal_lock_unlock(fd, OPAL_RW);
}
/*
* Prepare and issue an ioctl to lock/unlock a drive
*/
int sedopal_lock_unlock(int fd, int lock_state)
{
int rc;
struct opal_lock_unlock opal_lu = {};
rc = sedopal_set_key(&opal_lu.session.opal_key);
if (rc != 0)
return rc;
opal_lu.session.sum = 0;
opal_lu.session.who = OPAL_ADMIN1;
opal_lu.l_state = lock_state;
rc = ioctl(fd, IOC_OPAL_LOCK_UNLOCK, &opal_lu);
if (rc != 0)
fprintf(stderr,
"Error: failed locking or unlocking - %d\n", rc);
/*
* If the unlock was successful, force a re-read of the
* partition table.
*/
if (rc == 0) {
rc = ioctl(fd, BLKRRPART, 0);
if (rc != 0)
fprintf(stderr,
"Error: failed re-reading partition\n");
}
return rc;
}
/*
* Confirm a destructive drive so that data is inadvertently erased
*/
static bool sedopal_confirm_revert(void)
{
int rc;
char ans;
bool confirmed = false;
/*
* verify that destructive revert is really the intention
*/
fprintf(stdout,
"Destructive revert erases drive data. Continue (y/n)? ");
rc = fscanf(stdin, " %c", &ans);
if ((rc == 1) && (ans == 'y' || ans == 'Y')) {
fprintf(stdout, "Are you sure (y/n)? ");
rc = fscanf(stdin, " %c", &ans);
if ((rc == 1) && (ans == 'y' || ans == 'Y'))
confirmed = true;
}
return confirmed;
}
/*
* perform a destructive drive revert
*/
static int sedopal_revert_destructive(int fd)
{
struct opal_key key;
int rc;
if (!sedopal_confirm_revert()) {
fprintf(stderr, "Aborting destructive revert\n");
return -1;
}
/*
* for destructive revert, require that key is provided
*/
sedopal_ask_key = true;
rc = sedopal_set_key(&key);
if (rc == 0)
rc = ioctl(fd, IOC_OPAL_REVERT_TPR, &key);
return rc;
}
/*
* perform a PSID drive revert
*/
static int sedopal_revert_psid(int fd)
{
#ifdef IOC_OPAL_PSID_REVERT_TPR
struct opal_key key;
int rc;
if (!sedopal_confirm_revert()) {
fprintf(stderr, "Aborting PSID revert\n");
return -1;
}
rc = sedopal_set_key(&key);
if (rc == 0) {
rc = ioctl(fd, IOC_OPAL_PSID_REVERT_TPR, &key);
if (rc != 0)
fprintf(stderr, "PSID_REVERT_TPR rc %d\n", rc);
}
return rc;
#else
fprintf(stderr, "ERROR : PSID revert is not supported\n");
return -EOPNOTSUPP;
#endif /* IOC_OPAL_PSID_REVERT_TPR */
}
/*
* revert a drive from the provisioned state to a state where locking
* is disabled.
*/
int sedopal_cmd_revert(int fd)
{
int rc;
/*
* for revert, require that key/PSID is provided
*/
sedopal_ask_key = true;
if (sedopal_psid_revert) {
rc = sedopal_revert_psid(fd);
} else if (sedopal_destructive_revert) {
rc = sedopal_revert_destructive(fd);
} else {
#ifdef IOC_OPAL_REVERT_LSP
struct opal_revert_lsp revert_lsp;
rc = sedopal_set_key(&revert_lsp.key);
if (rc != 0)
return rc;
revert_lsp.options = OPAL_PRESERVE;
revert_lsp.__pad = 0;
rc = ioctl(fd, IOC_OPAL_REVERT_LSP, &revert_lsp);
#else
rc = -EOPNOTSUPP;
#endif
}
if (rc != 0)
fprintf(stderr, "Error: failed reverting drive - %d\n", rc);
return rc;
}
/*
* Change the password of a drive. The existing password must be
* provided and the new password is confirmed by re-entry.
*/
int sedopal_cmd_password(int fd)
{
int rc;
struct opal_new_pw new_pw = {};
new_pw.new_user_pw.who = OPAL_ADMIN1;
new_pw.new_user_pw.opal_key.lr = 0;
new_pw.session.who = OPAL_ADMIN1;
new_pw.session.sum = 0;
new_pw.session.opal_key.lr = 0;
/*
* get current key
*/
sedopal_ask_key = true;
if (sedopal_set_key(&new_pw.session.opal_key) != 0)
return -EINVAL;
/*
* get new key
*/
sedopal_ask_new_key = true;
if (sedopal_set_key(&new_pw.new_user_pw.opal_key) != 0)
return -EINVAL;
rc = ioctl(fd, IOC_OPAL_SET_PW, &new_pw);
if (rc != 0)
fprintf(stderr, "Error: failed setting password - %d\n", rc);
return rc;
}
/*
* Print the state of locking features.
*/
void sedopal_print_locking_features(uint8_t features)
{
printf("Locking Features:\n");
printf("\tLocking Supported: %s\n",
(features & OPAL_FEATURE_LOCKING_SUPPORTED) ? "Yes" : "No");
printf("\tLocking Feature Enabled: %s\n",
(features & OPAL_FEATURE_LOCKING_ENABLED) ? "Yes" : "No");
printf("\tLocked: %s\n",
(features & OPAL_FEATURE_LOCKED) ? "Yes" : "No");
}
/*
* Query a drive to determine if it's SED Opal capable and
* it's current locking status.
*/
int sedopal_cmd_discover(int fd)
{
#ifdef IOC_OPAL_DISCOVERY
int rc;
bool sedopal_locking_supported = false;
struct opal_discovery discover;
struct level_0_discovery_header *dh;
struct level_0_discovery_features *feat;
struct level_0_discovery_features *feat_end;
uint16_t code;
uint8_t locking_flags;
char buf[4096];
discover.data = (__u64)buf;
discover.size = sizeof(buf);
rc = ioctl(fd, IOC_OPAL_DISCOVERY, &discover);
if (rc < 0) {
fprintf(stderr, "Error: ioctl IOC_OPAL_DISCOVERY failed\n");
return rc;
}
/*
* The returned buffer contains a level 0 discovery header
* folowed by an array of level 0 feature records.
*
* TCG Opal Specification v2.0.2 section 3.1.1
*/
dh = (struct level_0_discovery_header *)buf;
feat = (struct level_0_discovery_features *)(dh + 1);
feat_end = (struct level_0_discovery_features *)
(buf + be32toh(dh->parameter_length));
/*
* iterate through all the features that were returned
*/
while (feat < feat_end) {
code = be16toh(feat->code);
switch (code) {
case OPAL_FEATURE_CODE_LOCKING:
locking_flags = feat->feature;
break;
case OPAL_FEATURE_CODE_OPALV2:
sedopal_locking_supported = true;
break;
default:
break;
}
feat++;
}
rc = 0;
if (!sedopal_locking_supported) {
fprintf(stderr, "Error: device does not support SED Opal\n");
rc = -1;
} else
sedopal_print_locking_features(locking_flags);
return rc;
#else /* IOC_OPAL_DISCOVERY */
fprintf(stderr, "ERROR : NVMe device discovery is not supported\n");
return -EOPNOTSUPP;
#endif
}

55
plugins/sed/sedopal_cmd.h Normal file
View file

@ -0,0 +1,55 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _SED_OPAL_CMD_H
#define _SED_OPAL_CMD_H
#define SEDOPAL_CURRENT_PW_PROMPT "Password: "
#define SEDOPAL_NEW_PW_PROMPT "New Password: "
#define SEDOPAL_REENTER_PW_PROMPT "Re-enter New Password: "
#define SEDOPAL_PSID_PROMPT "PSID: "
#define SEDOPAL_MIN_PASSWORD_LEN 8
#define SEDOPAL_MAX_PASSWORD_LEN 32
#define NVME_DEV_PATH "/dev/nvme"
extern bool sedopal_ask_key;
extern bool sedopal_ask_new_key;
extern bool sedopal_destructive_revert;
extern bool sedopal_psid_revert;
/*
* Sub-commands supported by the sedopal command
*/
enum sedopal_cmds {
SEDOPAL_CMD_NOT_SPECIFIED = -1,
SEDOPAL_CMD_INITIALIZE = 0,
SEDOPAL_CMD_LOCK = 1,
SEDOPAL_CMD_UNLOCK = 2,
SEDOPAL_CMD_REVERT = 3,
SEDOPAL_CMD_PASSWORD = 4,
SEDOPAL_CMD_DISCOVER = 5,
};
struct cmd_table {
int (*cmd_handler)(int fd);
};
/*
* command handlers
*/
int sedopal_cmd_initialize(int fd);
int sedopal_cmd_lock(int fd);
int sedopal_cmd_unlock(int fd);
int sedopal_cmd_revert(int fd);
int sedopal_cmd_password(int fd);
int sedopal_cmd_discover(int fd);
/*
* utility functions
*/
int sedopal_open_nvme_device(char *device);
int sedopal_lock_unlock(int fd, int lock_state);
const char *sedopal_error_to_text(int code);
#endif /* _SED_OPAL_CMD_H */

View file

@ -0,0 +1,71 @@
/* SPDX-License-Identifier: GPL-2.0-or-later */
#ifndef _SED_OPAL_SPEC_H
#define _SED_OPAL_SPEC_H
/*
* TCP Storage Architecture Core Specification Version 2.01
* section 5.1.5 Method Status Codes
*/
enum sed_status_codes {
SED_STATUS_SUCCESS = 0x00,
SED_STATUS_NOT_AUTHORIZED = 0x01,
SED_STATUS_OBSOLETE_1 = 0x02,
SED_STATUS_SP_BUSY = 0x03,
SED_STATUS_SP_FAILED = 0x04,
SED_STATUS_SP_DISABLED = 0x05,
SED_STATUS_SP_FROZEN = 0x06,
SED_STATUS_NO_SESSIONS_AVAILABLE = 0x07,
SED_STATUS_UNIQUENESS_CONFLICT = 0x08,
SED_STATUS_INSUFFICIENT_SPACE = 0x09,
SED_STATUS_INSUFFICIENT_ROWS = 0x0A,
SED_STATUS_OBSOLETE_2 = 0x0B,
SED_STATUS_INVALID_PARAMETER = 0x0C,
SED_STATUS_OBSOLETE_3 = 0x0D,
SED_STATUS_OBSOLETE_4 = 0x0E,
SED_STATUS_TPER_MALFUNCTION = 0x0F,
SED_STATUS_TRANSACTION_FAILURE = 0x10,
SED_STATUS_RESPONSE_OVERFLOW = 0x11,
SED_STATUS_AUTHORITY_LOCKED_OUT = 0x12,
SED_STATUS_FAIL = 0x3F,
SED_STATUS_NO_METHOD_STATUS = 0x89,
};
/*
* Definitions from TCG Opal Specification v2.0.2
*/
/*
* level 0 feature codes - section 3.1.1
*/
#define OPAL_FEATURE_CODE_LOCKING 0x0002
#define OPAL_FEATURE_CODE_OPALV2 0x0203
/* locking features */
#define OPAL_FEATURE_LOCKING_SUPPORTED 0x01
#define OPAL_FEATURE_LOCKING_ENABLED 0x02
#define OPAL_FEATURE_LOCKED 0x04
/*
* discovery header as specified in section 3.1.1.1
*/
struct level_0_discovery_header {
uint32_t parameter_length;
uint32_t revision;
uint64_t reserved;
uint8_t vendor_specific[32];
};
/*
* level 0 features as specified in section 3.1.1.3
*/
struct level_0_discovery_features {
uint16_t code;
uint8_t version;
uint8_t length;
uint8_t feature;
uint8_t reserved[11];
};
#endif /* _SED_OPAL_SPEC_H */