Commit cc27088d authored by Ondrej Kozina's avatar Ondrej Kozina Committed by Milan Broz

Add support for LUKS2 token export and import.

parent 97ab7e9c
......@@ -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,
the backup will also be written to it.
.PP
\fItoken\fR <add|remove> <device>
\fItoken\fR <add|remove|import|export> <device>
.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
description. Usually, the passphrase should be stored in \fIuser\fR or
\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
type from token slot specified by \-\-token\-id option.
\fB<options>\fR can be [\-\-header, \-\-token\-id, \-\-key-slot, \-\-key\-description,
\-\-disable\-locks, \-\-disable\-keyring].
Action \fIimport\fR can store arbitrary valid token json in LUKS2 header. It may be passed via
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
\fIconvert\fR <device> \-\-type <format>
.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
bypass the passphrases, see also option \-\-master\-key\-file.
.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"
.TP
.B "\-\-use\-urandom"
......
......@@ -28,6 +28,7 @@ static const char *opt_cipher = NULL;
static const char *opt_hash = NULL;
static int opt_verify_passphrase = 0;
static const char *opt_json_file = NULL;
static const char *opt_key_file = NULL;
static const char *opt_keyfile_stdin = NULL;
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, &params);
if (r < 0)
if (r < 0) {
log_err(_("Failed to add luks2-keyring token %d."), opt_token);
return r;
}
token = r;
......@@ -1972,23 +1975,88 @@ static int _token_add(struct crypt_device *cd)
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)
{
int add, r;
int r;
struct crypt_device *cd = NULL;
enum { ADD = 0, REMOVE, IMPORT, EXPORT } action;
if (!strcmp(action_argv[0], "add")) {
if (!opt_key_description) {
log_err(_("--key-description parameter is mandatory for token add action."));
return -EINVAL;
}
add = 1;
action = ADD;
} else if (!strcmp(action_argv[0], "remove")) {
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;
}
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;
}
add = 0;
action = EXPORT;
} else {
log_err(_("Invalid token operation %s."), action_argv[0]);
return -EINVAL;
......@@ -2002,12 +2070,23 @@ static int action_token(void)
return r;
}
r = add ? _token_add(cd) : crypt_token_json_set(cd, opt_token, NULL);
if (r < 0) {
if (add)
log_err(_("Failed to add keyring token %d."), opt_token);
else
log_err(_("Failed to remove token %d."), opt_token);
switch (action) {
case ADD: /* adds only luks2-keyring type */
r = _token_add(cd);
break;
case REMOVE:
/* 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);
......@@ -2046,7 +2125,7 @@ static struct action_type {
{ "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") },
{ "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 },
{ "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 },
{ "json-file", '\0', POPT_ARG_STRING, &opt_json_file, 0, N_("Read or write the json from or to a file"), NULL },
POPT_TABLEEND
};
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_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_wipe_all_signatures(const char *path);
......
......@@ -558,3 +558,119 @@ out:
blk_free(h);
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;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment