mirror of
https://gitlab.com/cryptsetup/cryptsetup.git
synced 2025-12-14 20:30:04 +01:00
Add support for LUKS2 token export and import.
This commit is contained in:
committed by
Milan Broz
parent
97ab7e9c65
commit
cc27088df9
@@ -457,9 +457,9 @@ of the LUKS header already on the device and of the header backup
|
|||||||
match. Alternatively, if there is no LUKS header on the device,
|
match. Alternatively, if there is no LUKS header on the device,
|
||||||
the backup will also be written to it.
|
the backup will also be written to it.
|
||||||
.PP
|
.PP
|
||||||
\fItoken\fR <add|remove> <device>
|
\fItoken\fR <add|remove|import|export> <device>
|
||||||
.IP
|
.IP
|
||||||
Adds a new keyring token to enable auto-activation of the device.
|
Action \fIadd\fR creates new keyring token to enable auto-activation of the device.
|
||||||
For the auto-activation, the passphrase must be stored in keyring with the specified
|
For the auto-activation, the passphrase must be stored in keyring with the specified
|
||||||
description. Usually, the passphrase should be stored in \fIuser\fR or
|
description. Usually, the passphrase should be stored in \fIuser\fR or
|
||||||
\fIuser-session\fR keyring.
|
\fIuser-session\fR keyring.
|
||||||
@@ -475,8 +475,15 @@ To remove existing token, specify the token ID which should be removed with
|
|||||||
\fBWARNING:\fR The action \fItoken remove\fR removes any token type, not just \fIkeyring\fR
|
\fBWARNING:\fR The action \fItoken remove\fR removes any token type, not just \fIkeyring\fR
|
||||||
type from token slot specified by \-\-token\-id option.
|
type from token slot specified by \-\-token\-id option.
|
||||||
|
|
||||||
\fB<options>\fR can be [\-\-header, \-\-token\-id, \-\-key-slot, \-\-key\-description,
|
Action \fIimport\fR can store arbitrary valid token json in LUKS2 header. It may be passed via
|
||||||
\-\-disable\-locks, \-\-disable\-keyring].
|
standard input or via file passed in \-\-json\-file option. If you specify \-\-key\-slot then
|
||||||
|
successfuly imported token is also assigned to the key slot.
|
||||||
|
|
||||||
|
Action \fIexport\fR writes requested token json to a file passed with \-\-json\-file or
|
||||||
|
to standard output.
|
||||||
|
|
||||||
|
\fB<options>\fR can be [\-\-header, \-\-token\-id, \-\-key\-slot, \-\-key\-description,
|
||||||
|
\-\-disable\-locks, \-\-disable\-keyring, \-\-json\-file].
|
||||||
.PP
|
.PP
|
||||||
\fIconvert\fR <device> \-\-type <format>
|
\fIconvert\fR <device> \-\-type <format>
|
||||||
.IP
|
.IP
|
||||||
@@ -826,6 +833,11 @@ For \fIluksDump\fR this option includes the master key in the displayed
|
|||||||
information. Use with care, as the master key can be used to
|
information. Use with care, as the master key can be used to
|
||||||
bypass the passphrases, see also option \-\-master\-key\-file.
|
bypass the passphrases, see also option \-\-master\-key\-file.
|
||||||
.TP
|
.TP
|
||||||
|
.B "\-\-json\-file"
|
||||||
|
Read token json from a file or write token to it. See \fItoken\fR action for more
|
||||||
|
information. \-\-json\-file=- reads json from standard input or writes it to
|
||||||
|
standard output respectively.
|
||||||
|
.TP
|
||||||
.B "\-\-use\-random"
|
.B "\-\-use\-random"
|
||||||
.TP
|
.TP
|
||||||
.B "\-\-use\-urandom"
|
.B "\-\-use\-urandom"
|
||||||
|
|||||||
104
src/cryptsetup.c
104
src/cryptsetup.c
@@ -28,6 +28,7 @@ static const char *opt_cipher = NULL;
|
|||||||
static const char *opt_hash = NULL;
|
static const char *opt_hash = NULL;
|
||||||
static int opt_verify_passphrase = 0;
|
static int opt_verify_passphrase = 0;
|
||||||
|
|
||||||
|
static const char *opt_json_file = NULL;
|
||||||
static const char *opt_key_file = NULL;
|
static const char *opt_key_file = NULL;
|
||||||
static const char *opt_keyfile_stdin = NULL;
|
static const char *opt_keyfile_stdin = NULL;
|
||||||
static int opt_keyfiles_count = 0;
|
static int opt_keyfiles_count = 0;
|
||||||
@@ -1958,8 +1959,10 @@ static int _token_add(struct crypt_device *cd)
|
|||||||
}
|
}
|
||||||
|
|
||||||
r = crypt_token_luks2_keyring_set(cd, opt_token, ¶ms);
|
r = crypt_token_luks2_keyring_set(cd, opt_token, ¶ms);
|
||||||
if (r < 0)
|
if (r < 0) {
|
||||||
|
log_err(_("Failed to add luks2-keyring token %d."), opt_token);
|
||||||
return r;
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
token = r;
|
token = r;
|
||||||
|
|
||||||
@@ -1972,23 +1975,88 @@ static int _token_add(struct crypt_device *cd)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int _token_import(struct crypt_device *cd)
|
||||||
|
{
|
||||||
|
char *json;
|
||||||
|
size_t json_length;
|
||||||
|
crypt_token_info token_info;
|
||||||
|
int r, token;
|
||||||
|
|
||||||
|
if (opt_token != CRYPT_ANY_TOKEN) {
|
||||||
|
token_info = crypt_token_status(cd, opt_token, NULL);
|
||||||
|
if (token_info < CRYPT_TOKEN_INACTIVE) {
|
||||||
|
log_err(_("Token %d is invalid."), opt_token);
|
||||||
|
return -EINVAL;
|
||||||
|
} else if (token_info > CRYPT_TOKEN_INACTIVE) {
|
||||||
|
log_err(_("Token %d in use."), opt_token);
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
r = tools_read_json_file(cd, opt_json_file, &json, &json_length);
|
||||||
|
if (r)
|
||||||
|
return r;
|
||||||
|
|
||||||
|
r = crypt_token_json_set(cd, opt_token, json);
|
||||||
|
free(json);
|
||||||
|
if (r < 0) {
|
||||||
|
log_err(_("Failed to import token from file."));
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
token = r;
|
||||||
|
|
||||||
|
if (opt_key_slot != CRYPT_ANY_SLOT) {
|
||||||
|
r = crypt_token_assign_keyslot(cd, token, opt_key_slot);
|
||||||
|
if (r < 0) {
|
||||||
|
log_err(_("Failed to assign token %d to keyslot %d."), token, opt_key_slot);
|
||||||
|
(void) crypt_token_json_set(cd, token, NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int _token_export(struct crypt_device *cd)
|
||||||
|
{
|
||||||
|
const char *json;
|
||||||
|
int r;
|
||||||
|
|
||||||
|
r = crypt_token_json_get(cd, opt_token, &json);
|
||||||
|
if (r < 0) {
|
||||||
|
log_err(_("Failed to get token %d for export."), opt_token);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
return tools_write_json_file(cd, opt_json_file, json);
|
||||||
|
}
|
||||||
|
|
||||||
static int action_token(void)
|
static int action_token(void)
|
||||||
{
|
{
|
||||||
int add, r;
|
int r;
|
||||||
struct crypt_device *cd = NULL;
|
struct crypt_device *cd = NULL;
|
||||||
|
enum { ADD = 0, REMOVE, IMPORT, EXPORT } action;
|
||||||
|
|
||||||
if (!strcmp(action_argv[0], "add")) {
|
if (!strcmp(action_argv[0], "add")) {
|
||||||
if (!opt_key_description) {
|
if (!opt_key_description) {
|
||||||
log_err(_("--key-description parameter is mandatory for token add action."));
|
log_err(_("--key-description parameter is mandatory for token add action."));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
add = 1;
|
action = ADD;
|
||||||
} else if (!strcmp(action_argv[0], "remove")) {
|
} else if (!strcmp(action_argv[0], "remove")) {
|
||||||
if (opt_token == CRYPT_ANY_TOKEN) {
|
if (opt_token == CRYPT_ANY_TOKEN) {
|
||||||
log_err(_("Missing --token option specifying token for removal."));
|
log_err(_("Action requires specific token. Use --token-id parameter."));
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
}
|
}
|
||||||
add = 0;
|
action = REMOVE;
|
||||||
|
} else if (!strcmp(action_argv[0], "import")) {
|
||||||
|
action = IMPORT;
|
||||||
|
} else if (!strcmp(action_argv[0], "export")) {
|
||||||
|
if (opt_token == CRYPT_ANY_TOKEN) {
|
||||||
|
log_err(_("Action requires specific token. Use --token-id parameter."));
|
||||||
|
return -EINVAL;
|
||||||
|
}
|
||||||
|
action = EXPORT;
|
||||||
} else {
|
} else {
|
||||||
log_err(_("Invalid token operation %s."), action_argv[0]);
|
log_err(_("Invalid token operation %s."), action_argv[0]);
|
||||||
return -EINVAL;
|
return -EINVAL;
|
||||||
@@ -2002,12 +2070,23 @@ static int action_token(void)
|
|||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
r = add ? _token_add(cd) : crypt_token_json_set(cd, opt_token, NULL);
|
switch (action) {
|
||||||
if (r < 0) {
|
case ADD: /* adds only luks2-keyring type */
|
||||||
if (add)
|
r = _token_add(cd);
|
||||||
log_err(_("Failed to add keyring token %d."), opt_token);
|
break;
|
||||||
else
|
case REMOVE:
|
||||||
log_err(_("Failed to remove token %d."), opt_token);
|
/* FIXME: add prompt here? a) for all types, b) external only? */
|
||||||
|
r = crypt_token_json_set(cd, opt_token, NULL);
|
||||||
|
break;
|
||||||
|
case IMPORT:
|
||||||
|
r = _token_import(cd);
|
||||||
|
break;
|
||||||
|
case EXPORT:
|
||||||
|
r = _token_export(cd);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
log_dbg("Internal token action error.");
|
||||||
|
r = EINVAL;
|
||||||
}
|
}
|
||||||
|
|
||||||
crypt_free(cd);
|
crypt_free(cd);
|
||||||
@@ -2046,7 +2125,7 @@ static struct action_type {
|
|||||||
{ "luksResume", action_luksResume, 1, 1, N_("<device>"), N_("Resume suspended LUKS device") },
|
{ "luksResume", action_luksResume, 1, 1, N_("<device>"), N_("Resume suspended LUKS device") },
|
||||||
{ "luksHeaderBackup", action_luksBackup,1,1, N_("<device>"), N_("Backup LUKS device header and keyslots") },
|
{ "luksHeaderBackup", action_luksBackup,1,1, N_("<device>"), N_("Backup LUKS device header and keyslots") },
|
||||||
{ "luksHeaderRestore",action_luksRestore,1,1,N_("<device>"), N_("Restore LUKS device header and keyslots") },
|
{ "luksHeaderRestore",action_luksRestore,1,1,N_("<device>"), N_("Restore LUKS device header and keyslots") },
|
||||||
{ "token", action_token, 2, 0, N_("<add|remove> <device>"), N_("Add or remove keyring token") },
|
{ "token", action_token, 2, 0, N_("<add|remove|import|export> <device>"), N_("Manipulate LUKS2 tokens") },
|
||||||
{}
|
{}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -2215,6 +2294,7 @@ int main(int argc, const char **argv)
|
|||||||
{ "label", '\0', POPT_ARG_STRING, &opt_label, 0, N_("Set label for the LUKS2 device"), NULL },
|
{ "label", '\0', POPT_ARG_STRING, &opt_label, 0, N_("Set label for the LUKS2 device"), NULL },
|
||||||
{ "subsystem", '\0', POPT_ARG_STRING, &opt_subsystem, 0, N_("Set subsystem label for the LUKS2 device"), NULL },
|
{ "subsystem", '\0', POPT_ARG_STRING, &opt_subsystem, 0, N_("Set subsystem label for the LUKS2 device"), NULL },
|
||||||
{ "unbound", '\0', POPT_ARG_NONE, &opt_unbound, 0, N_("Create unbound (no assigned data segment) LUKS2 keyslot"), NULL },
|
{ "unbound", '\0', POPT_ARG_NONE, &opt_unbound, 0, N_("Create unbound (no assigned data segment) LUKS2 keyslot"), NULL },
|
||||||
|
{ "json-file", '\0', POPT_ARG_STRING, &opt_json_file, 0, N_("Read or write the json from or to a file"), NULL },
|
||||||
POPT_TABLEEND
|
POPT_TABLEEND
|
||||||
};
|
};
|
||||||
poptContext popt_context;
|
poptContext popt_context;
|
||||||
|
|||||||
@@ -103,6 +103,9 @@ int tools_wipe_progress(uint64_t size, uint64_t offset, void *usrptr);
|
|||||||
int tools_read_mk(const char *file, char **key, int keysize);
|
int tools_read_mk(const char *file, char **key, int keysize);
|
||||||
int tools_write_mk(const char *file, const char *key, int keysize);
|
int tools_write_mk(const char *file, const char *key, int keysize);
|
||||||
|
|
||||||
|
int tools_read_json_file(struct crypt_device *cd, const char *file, char **json, size_t *json_size);
|
||||||
|
int tools_write_json_file(struct crypt_device *cd, const char *file, const char *json);
|
||||||
|
|
||||||
int tools_detect_signatures(const char *device, int ignore_luks, size_t *count);
|
int tools_detect_signatures(const char *device, int ignore_luks, size_t *count);
|
||||||
int tools_wipe_all_signatures(const char *path);
|
int tools_wipe_all_signatures(const char *path);
|
||||||
|
|
||||||
|
|||||||
@@ -558,3 +558,119 @@ out:
|
|||||||
blk_free(h);
|
blk_free(h);
|
||||||
return r;
|
return r;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* FIXME: 4MiBs is max LUKS2 mda length (including binary header).
|
||||||
|
* In future, read max allowed JSON size from config section.
|
||||||
|
*/
|
||||||
|
#define LUKS2_MAX_MDA_SIZE 0x400000
|
||||||
|
int tools_read_json_file(struct crypt_device *cd, const char *file, char **json, size_t *json_size)
|
||||||
|
{
|
||||||
|
ssize_t ret;
|
||||||
|
int fd, block, r;
|
||||||
|
void *buf = NULL;
|
||||||
|
|
||||||
|
block = tools_signals_blocked();
|
||||||
|
if (block)
|
||||||
|
set_int_block(0);
|
||||||
|
|
||||||
|
if (tools_is_stdin(file)) {
|
||||||
|
fd = STDIN_FILENO;
|
||||||
|
log_dbg("STDIN descriptor JSON read requested.");
|
||||||
|
} else {
|
||||||
|
log_dbg("File descriptor JSON read requested.");
|
||||||
|
fd = open(file, O_RDONLY);
|
||||||
|
if (fd < 0) {
|
||||||
|
log_err(_("Failed to open file %s in read-only mode."), file);
|
||||||
|
r = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = malloc(LUKS2_MAX_MDA_SIZE);
|
||||||
|
if (!buf) {
|
||||||
|
r = -ENOMEM;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isatty(fd) && !opt_batch_mode)
|
||||||
|
log_std(_("Provide valid LUKS2 token JSON:\n"));
|
||||||
|
|
||||||
|
/* we expect JSON (string) */
|
||||||
|
r = 0;
|
||||||
|
ret = read_buffer_intr(fd, buf, LUKS2_MAX_MDA_SIZE - 1, &quit);
|
||||||
|
if (ret < 0) {
|
||||||
|
r = -EIO;
|
||||||
|
log_err(_("Failed to read JSON file."));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
check_signal(&r);
|
||||||
|
if (r) {
|
||||||
|
log_err(_("\nRead interrupted."));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
*json_size = (size_t)ret;
|
||||||
|
*json = buf;
|
||||||
|
*(*json + ret) = '\0';
|
||||||
|
out:
|
||||||
|
if (block && !quit)
|
||||||
|
set_int_block(1);
|
||||||
|
if (fd != STDIN_FILENO)
|
||||||
|
close(fd);
|
||||||
|
if (r && buf) {
|
||||||
|
memset(buf, 0, LUKS2_MAX_MDA_SIZE);
|
||||||
|
free(buf);
|
||||||
|
}
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|
||||||
|
int tools_write_json_file(struct crypt_device *cd, const char *file, const char *json)
|
||||||
|
{
|
||||||
|
int block, fd, r;
|
||||||
|
size_t json_len;
|
||||||
|
ssize_t ret;
|
||||||
|
|
||||||
|
if (!json || !(json_len = strlen(json)) || json_len >= LUKS2_MAX_MDA_SIZE)
|
||||||
|
return -EINVAL;
|
||||||
|
|
||||||
|
block = tools_signals_blocked();
|
||||||
|
if (block)
|
||||||
|
set_int_block(0);
|
||||||
|
|
||||||
|
if (tools_is_stdin(file)) {
|
||||||
|
fd = STDOUT_FILENO;
|
||||||
|
log_dbg("STDOUT descriptor JSON write requested.");
|
||||||
|
} else {
|
||||||
|
log_dbg("File descriptor JSON write requested.");
|
||||||
|
fd = open(file, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (fd < 0) {
|
||||||
|
log_err(_("Failed to open file %s in write mode."), file ?: "");
|
||||||
|
r = -EINVAL;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
r = 0;
|
||||||
|
ret = write_buffer_intr(fd, json, json_len, &quit);
|
||||||
|
check_signal(&r);
|
||||||
|
if (r) {
|
||||||
|
log_err(_("\nWrite interrupted."));
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
if (ret < 0 || (size_t)ret != json_len) {
|
||||||
|
log_err(_("Failed to write JSON file."));
|
||||||
|
r = -EIO;
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (isatty(fd))
|
||||||
|
(void) write_buffer_intr(fd, "\n", 1, &quit);
|
||||||
|
out:
|
||||||
|
if (block && !quit)
|
||||||
|
set_int_block(1);
|
||||||
|
if (fd != STDOUT_FILENO)
|
||||||
|
close(fd);
|
||||||
|
return r;
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user