mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
zkey: Add 'zkey kms import' command to import keys from a KMS
Allow to import keys that exist in a key management system into the repository. The key together with its properties is imported. Signed-off-by: Ingo Franzki <ifranzki@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
This commit is contained in:
committed by
Jan Höppner
parent
bcce1e8d18
commit
cd8a733c82
358
zkey/keystore.c
358
zkey/keystore.c
@@ -1200,6 +1200,7 @@ struct volume_check {
|
||||
const char *name;
|
||||
const char *volume;
|
||||
bool set;
|
||||
bool nocheck;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1289,7 +1290,7 @@ static int _keystore_volume_check(const char *volume, bool remove, bool set,
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
if (remove || info->nocheck) {
|
||||
rc = 0;
|
||||
goto out;
|
||||
}
|
||||
@@ -1602,6 +1603,8 @@ static int _keystore_set_default_properties(struct properties *key_props)
|
||||
* key (optional, can be NULL)
|
||||
* @param[in] noapqncheck if true, the specified APQN(s) are not checked for
|
||||
* existence and type.
|
||||
* @param[i] novolscheck if true, the specified Volume(s) are not checked for
|
||||
* existence or duplicate use
|
||||
* @param[in] sector_size the sector size to use with dm-crypt. It must be power
|
||||
* of two and in range 512 - 4096 bytes. 0 means that
|
||||
* the sector size is not specified and the system
|
||||
@@ -1615,7 +1618,7 @@ static int _keystore_create_info_props(struct keystore *keystore,
|
||||
const char *name,
|
||||
const char *description,
|
||||
const char *volumes, const char *apqns,
|
||||
bool noapqncheck,
|
||||
bool noapqncheck, bool novolcheck,
|
||||
size_t sector_size,
|
||||
const char *volume_type,
|
||||
const char *key_type,
|
||||
@@ -1623,7 +1626,7 @@ static int _keystore_create_info_props(struct keystore *keystore,
|
||||
struct properties **props)
|
||||
{
|
||||
struct volume_check vol_check = { .keystore = keystore, .name = name,
|
||||
.set = 0 };
|
||||
.set = 0, .nocheck = novolcheck };
|
||||
struct apqn_check apqn_check = { .noonlinecheck = noapqncheck,
|
||||
.nomsg = 0,
|
||||
.cardtype = get_card_type_for_keytype(
|
||||
@@ -1747,7 +1750,7 @@ static int _keystore_create_info_file(struct keystore *keystore,
|
||||
int rc;
|
||||
|
||||
rc = _keystore_create_info_props(keystore, name, description, volumes,
|
||||
apqns, noapqncheck, sector_size,
|
||||
apqns, noapqncheck, false, sector_size,
|
||||
volume_type, key_type, kms,
|
||||
&key_props);
|
||||
if (rc != 0)
|
||||
@@ -1989,9 +1992,9 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name,
|
||||
pr_verbose(keystore, "APQNs for keytype %s: '%s'", key_type, apqns);
|
||||
|
||||
rc = _keystore_create_info_props(keystore, name, description, volumes,
|
||||
apqns, false, sector_size, volume_type,
|
||||
key_type, kms_info->plugin_name,
|
||||
&key_props);
|
||||
apqns, false, false, sector_size,
|
||||
volume_type, key_type,
|
||||
kms_info->plugin_name, &key_props);
|
||||
if (rc != 0)
|
||||
goto out_free_key_filenames;
|
||||
|
||||
@@ -2261,7 +2264,7 @@ int keystore_change_key(struct keystore *keystore, const char *name,
|
||||
long int sector_size, const char *volume_type)
|
||||
{
|
||||
struct volume_check vol_check = { .keystore = keystore, .name = name,
|
||||
.set = 0 };
|
||||
.set = 0, .nocheck = 0 };
|
||||
struct apqn_check apqn_check = { .noonlinecheck = noapqncheck,
|
||||
.nomsg = 0 };
|
||||
struct key_filenames file_names = { NULL, NULL, NULL };
|
||||
@@ -3427,8 +3430,8 @@ int keystore_reencipher_key(struct keystore *keystore, const char *name_filter,
|
||||
int keystore_copy_key(struct keystore *keystore, const char *name,
|
||||
const char *newname, const char *volumes, bool local)
|
||||
{
|
||||
struct volume_check vol_check = { .keystore = keystore,
|
||||
.name = newname, .set = 0 };
|
||||
struct volume_check vol_check = { .keystore = keystore, .name = newname,
|
||||
.set = 0, .nocheck = 0 };
|
||||
struct key_filenames file_names = { NULL, NULL, NULL };
|
||||
struct key_filenames new_names = { NULL, NULL, NULL };
|
||||
struct properties *key_prop = NULL;
|
||||
@@ -4887,6 +4890,341 @@ int keystore_msg_for_kms_key(struct keystore *keystore, const char *key_type,
|
||||
return msg_for_key.num_keys == 0 ? -ENOENT : 0;
|
||||
}
|
||||
|
||||
struct kms_import {
|
||||
struct keystore *keystore;
|
||||
bool batch_mode;
|
||||
bool novolcheck;
|
||||
unsigned long num_imported;
|
||||
unsigned long num_skipped;
|
||||
unsigned long num_failed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Callback used with the keystore_import_kms_keys() function. Called for each
|
||||
* key.
|
||||
*
|
||||
* @param[in] key1_id the key-ID of the key (1st key of an XTS key)
|
||||
* @param[in] key1_label the label of the key (1st key of an XTS key)
|
||||
* @param[in] key2_id the key-ID of the 2nd XTS key, NULL if not XTS
|
||||
* @param[in] key2_label the label of the 2nd XTS key, NULL if not XTS
|
||||
* @param[in] xts if true, this is an XTS key pair
|
||||
* @param[in] name the zkey name of the key
|
||||
* @param[in] key_type the type of the key (CCA-AESDATA, etc)
|
||||
* @param[in] key_bits the key size in bits
|
||||
* @param[in] description the description of the key (can be NULL)
|
||||
* @param[in] cipher the cipher of the key (can be NULL)
|
||||
* @param[in] iv_mode the IV-mode of the key (can be NULL)
|
||||
* @param[in] volumes the associated volumes of the key (can be NULL)
|
||||
* @param[in] volume_type the volume type of the volume (can be NULL)
|
||||
* @param[in] sector_size the sector size of the volume (0 means default)
|
||||
* @param[in] addl_info_argz an argz string containing additional KMS plugin
|
||||
* specific infos to be displayed, or NULL if none.
|
||||
* @param[in] addl_info_len length of the argz string in addl_info_argz
|
||||
* @param[in] private_data the private data pointer
|
||||
*
|
||||
* @returns 0 on success, or a negative errno in case of an error.
|
||||
*/
|
||||
static int _keystore_process_kms_import(const char *key1_id,
|
||||
const char *key1_label,
|
||||
const char *key2_id,
|
||||
const char *key2_label,
|
||||
bool xts, const char *name,
|
||||
const char *UNUSED(key_type),
|
||||
size_t UNUSED(key_bits),
|
||||
const char *description,
|
||||
const char *UNUSED(cipher),
|
||||
const char *UNUSED(iv_mode),
|
||||
const char *volumes,
|
||||
const char *volume_type,
|
||||
size_t sector_size,
|
||||
const char *UNUSED(addl_info_argz),
|
||||
size_t UNUSED(addl_info_len),
|
||||
void *private_data)
|
||||
{
|
||||
struct key_filenames file_names = { NULL, NULL, NULL };
|
||||
struct kms_import *import_data = private_data;
|
||||
u8 secure_key[2 * MAX_SECURE_KEY_SIZE];
|
||||
struct properties *key_props = NULL;
|
||||
char vp[VERIFICATION_PATTERN_LEN];
|
||||
const char *key_name = name;
|
||||
struct keystore *keystore;
|
||||
size_t alt_name_len = 0;
|
||||
size_t secure_key_size;
|
||||
bool fatal_err = false;
|
||||
char *alt_name = NULL;
|
||||
const char *key_type;
|
||||
char *apqns = NULL;
|
||||
int rc;
|
||||
|
||||
keystore = import_data->keystore;
|
||||
|
||||
rc = _keystore_get_key_filenames(keystore, key_name, &file_names);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
check_duplicate_key:
|
||||
rc = _keystore_ensure_keyfiles_not_exist(&file_names, key_name);
|
||||
if (rc == -EEXIST) {
|
||||
if (import_data->batch_mode) {
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
printf("%s: Do you want to enter an alternate name [y/N]? ",
|
||||
program_invocation_short_name);
|
||||
if (!prompt_for_yes(keystore->verbose)) {
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
prompt_alt_name:
|
||||
printf("%s: Alternate name: ", program_invocation_short_name);
|
||||
rc = getline(&alt_name, &alt_name_len, stdin);
|
||||
if (rc <= 1) {
|
||||
rc = 1;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (alt_name[strlen(alt_name) - 1] == '\n')
|
||||
alt_name[strlen(alt_name) - 1] = '\0';
|
||||
|
||||
key_name = alt_name;
|
||||
_keystore_free_key_filenames(&file_names);
|
||||
rc = _keystore_get_key_filenames(keystore, key_name,
|
||||
&file_names);
|
||||
if (rc != 0)
|
||||
goto prompt_alt_name;
|
||||
|
||||
goto check_duplicate_key;
|
||||
} else if (rc != 0) {
|
||||
goto out;
|
||||
}
|
||||
|
||||
secure_key_size = sizeof(secure_key);
|
||||
rc = import_kms_key(keystore->kms_info, key1_id, key2_id, xts, key_name,
|
||||
secure_key, &secure_key_size, keystore->verbose);
|
||||
if (rc != 0) {
|
||||
warnx("KMS plugin '%s' failed to import key '%s': %s",
|
||||
keystore->kms_info->plugin_name, key_name, strerror(-rc));
|
||||
print_last_kms_error(keystore->kms_info);
|
||||
if (rc == -ENOTSUP)
|
||||
fatal_err = true;
|
||||
goto out;
|
||||
}
|
||||
|
||||
key_type = get_key_type(secure_key, secure_key_size);
|
||||
if (key_type == NULL) {
|
||||
warnx("Key '%s' is not a valid secure key", key_name);
|
||||
free(secure_key);
|
||||
rc = -EINVAL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = get_kms_apqns_for_key_type(keystore->kms_info, key_type, true,
|
||||
&apqns, keystore->verbose);
|
||||
if (rc != 0) {
|
||||
if (rc == -ENOTSUP)
|
||||
warnx("Key-type not supported by the KMS plugin '%s'",
|
||||
keystore->kms_info->plugin_name);
|
||||
goto out;
|
||||
}
|
||||
|
||||
pr_verbose(keystore, "APQNs for keytype %s: '%s'", key_type, apqns);
|
||||
|
||||
rc = _keystore_create_info_props(keystore, key_name, description,
|
||||
volumes, apqns, false,
|
||||
import_data->novolcheck,
|
||||
sector_size, volume_type, key_type,
|
||||
keystore->kms_info->plugin_name,
|
||||
&key_props);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_ID :
|
||||
PROP_NAME_KMS_KEY_ID, key1_id);
|
||||
if (rc != 0) {
|
||||
pr_verbose(keystore, "Failed to set key id of key #1: %s",
|
||||
strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_LABEL :
|
||||
PROP_NAME_KMS_KEY_LABEL, key1_label);
|
||||
if (rc != 0) {
|
||||
pr_verbose(keystore, "Failed to set key label of key #1: %s",
|
||||
strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (xts) {
|
||||
rc = properties_set(key_props, PROP_NAME_KMS_XTS_KEY2_ID,
|
||||
key2_id);
|
||||
if (rc != 0) {
|
||||
pr_verbose(keystore, "Failed to set key id of key #2: "
|
||||
"%s", strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = properties_set(key_props, PROP_NAME_KMS_XTS_KEY2_LABEL,
|
||||
key2_label);
|
||||
if (rc != 0) {
|
||||
pr_verbose(keystore, "Failed to set key label of key "
|
||||
"#2: %s", strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
rc = write_secure_key(file_names.skey_filename, secure_key,
|
||||
secure_key_size, keystore->verbose);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
||||
rc = _keystore_set_file_permission(keystore, file_names.skey_filename);
|
||||
if (rc != 0)
|
||||
goto out_remove;
|
||||
|
||||
rc = generate_key_verification_pattern(secure_key, secure_key_size,
|
||||
vp, sizeof(vp),
|
||||
keystore->verbose);
|
||||
if (rc != 0) {
|
||||
warnx("Failed to generate the key verification pattern: %s",
|
||||
strerror(-rc));
|
||||
warnx("Make sure that kernel module 'paes_s390' is loaded and "
|
||||
"that the 'paes' cipher is available");
|
||||
fatal_err = true;
|
||||
goto out_remove;
|
||||
}
|
||||
|
||||
rc = properties_set(key_props, PROP_NAME_KEY_VP, vp);
|
||||
if (rc != 0) {
|
||||
pr_verbose(keystore, "Failed to set verification pattern of "
|
||||
"key: %s", strerror(-rc));
|
||||
|
||||
goto out_remove;
|
||||
}
|
||||
|
||||
rc = properties_save(key_props, file_names.info_filename, 1);
|
||||
if (rc != 0) {
|
||||
pr_verbose(keystore,
|
||||
"Key info file '%s' could not be written: %s",
|
||||
file_names.info_filename, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = _keystore_set_file_permission(keystore, file_names.info_filename);
|
||||
if (rc != 0) {
|
||||
remove(file_names.info_filename);
|
||||
goto out_remove;
|
||||
}
|
||||
|
||||
out_remove:
|
||||
if (rc != 0) {
|
||||
remove(file_names.skey_filename);
|
||||
remove(file_names.info_filename);
|
||||
}
|
||||
|
||||
out:
|
||||
if (rc == 0) {
|
||||
printf("Successfully imported key '%s'\n", key_name);
|
||||
import_data->num_imported++;
|
||||
} else if (rc < 0) {
|
||||
warnx("Failed to import key '%s': %s", key_name, strerror(-rc));
|
||||
import_data->num_failed++;
|
||||
} else {
|
||||
warnx("Skipping key '%s'", key_name);
|
||||
import_data->num_skipped++;
|
||||
}
|
||||
|
||||
_keystore_free_key_filenames(&file_names);
|
||||
if (alt_name != NULL)
|
||||
free(alt_name);
|
||||
if (apqns != NULL)
|
||||
free(apqns);
|
||||
if (key_props != NULL)
|
||||
properties_free(key_props);
|
||||
|
||||
return fatal_err ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports secure keys from the KMS and adds it to the key store
|
||||
*
|
||||
* @param[in] keystore the key store
|
||||
* @param[in] label_filter the KMS label filter. Can contain wild cards.
|
||||
* NULL means no name filter.
|
||||
* @param[in] name_filter the name filter. Can contain wild cards.
|
||||
* NULL means no name filter.
|
||||
* @param[in] volume_filter the volume filter. Can contain wild cards, and
|
||||
* mutliple volume filters separated by commas.
|
||||
* If the filter does not contain the ':dm-name'
|
||||
* part, then the volumes are matched without the
|
||||
* dm-name part. If the filter contains the
|
||||
* ':dm-name' part, then the filter is matched
|
||||
* including the dm-name part.
|
||||
* NULL means no volume filter.
|
||||
* @param[in] volume_type If not NULL, specifies the volume type.
|
||||
* @param[in] kms_options an array of KMS options specified, or NULL if no
|
||||
* KMS options have been specified
|
||||
* @param[in] num_kms_options the number of options in above array
|
||||
* @param[in] batch_mode if true, suppress alternate name prompts if a key
|
||||
* with an already existing name is to be imported.
|
||||
* @param[in] novolcheck if true, do not check the associated volumes for
|
||||
* existence and duplicate use
|
||||
*
|
||||
* @returns 0 for success or a negative errno in case of an error
|
||||
*/
|
||||
int keystore_import_kms_keys(struct keystore *keystore,
|
||||
const char *label_filter,
|
||||
const char *name_filter,
|
||||
const char *volume_filter,
|
||||
const char *volume_type,
|
||||
struct kms_option *kms_options,
|
||||
size_t num_kms_options,
|
||||
bool batch_mode, bool novolcheck)
|
||||
{
|
||||
struct kms_import import_data = { 0 };
|
||||
int rc;
|
||||
|
||||
util_assert(keystore != NULL, "Internal error: keystore is NULL");
|
||||
|
||||
if (keystore->kms_info->plugin_lib == NULL) {
|
||||
warnx("The repository is not bound to a KMS plugin");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (volume_type != NULL &&
|
||||
!_keystore_valid_volume_type(volume_type)) {
|
||||
warnx("Invalid volume-type specified");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
import_data.keystore = keystore;
|
||||
import_data.batch_mode = batch_mode;
|
||||
import_data.novolcheck = novolcheck;
|
||||
import_data.num_imported = 0;
|
||||
import_data.num_skipped = 0;
|
||||
import_data.num_failed = 0;
|
||||
|
||||
rc = process_kms_keys(keystore->kms_info, label_filter, name_filter,
|
||||
volume_filter, volume_type,
|
||||
kms_options, num_kms_options,
|
||||
_keystore_process_kms_import, &import_data,
|
||||
keystore->verbose);
|
||||
if (rc != 0) {
|
||||
pr_verbose(keystore, "Failed to import kms keys: %s",
|
||||
strerror(-rc));
|
||||
} else {
|
||||
printf("%lu keys imported, %lu keys skipped, %lu keys "
|
||||
"failed to import\n",
|
||||
import_data.num_imported, import_data.num_skipped,
|
||||
import_data.num_failed);
|
||||
if (import_data.num_failed > 0)
|
||||
rc = -EIO;
|
||||
}
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Frees a keystore object
|
||||
*
|
||||
|
||||
@@ -126,6 +126,15 @@ int keystore_kms_keys_unbind(struct keystore *keystore);
|
||||
int keystore_msg_for_kms_key(struct keystore *keystore, const char *key_type,
|
||||
const char *msg);
|
||||
|
||||
int keystore_import_kms_keys(struct keystore *keystore,
|
||||
const char *label_filter,
|
||||
const char *name_filter,
|
||||
const char *volume_filter,
|
||||
const char *volume_type,
|
||||
struct kms_option *kms_options,
|
||||
size_t num_kms_options,
|
||||
bool batch_mode, bool novolcheck);
|
||||
|
||||
void keystore_free(struct keystore *keystore);
|
||||
|
||||
|
||||
|
||||
126
zkey/kms.c
126
zkey/kms.c
@@ -2987,3 +2987,129 @@ int list_kms_keys(struct kms_info *kms_info, const char *label_filter,
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a KMS managed key.
|
||||
*
|
||||
* @param[in] kms_info information of the currently bound plugin.
|
||||
* @param[in] key1_id the id of the key to import (1st key for XTS)
|
||||
* @param[in] key2_id the id of the 2nd XTS key to import (NULL if not
|
||||
* XTS)
|
||||
* @param[in] xts if true, an XTS key is to be imported
|
||||
* @param[in] name the zkey-name under which the key is imported
|
||||
* @param[in] key_blob a buffer to return the key blob. The size of the
|
||||
* buffer is specified in key_blob_length
|
||||
* @param[out] key_blob_length on entry: the size of the key_blob buffer.
|
||||
* on exit: the size of the key blob returned.
|
||||
* @param[in] verbose if true, verbose messages are printed
|
||||
*
|
||||
* @returns 0 for success or a negative errno in case of an error.
|
||||
*/
|
||||
int import_kms_key(struct kms_info *kms_info, const char *key1_id,
|
||||
const char *key2_id, bool xts, const char *name,
|
||||
unsigned char *key_blob, size_t *key_blob_length,
|
||||
bool verbose)
|
||||
{
|
||||
size_t key_blob_size, key_blob_ofs, key_size = 0;
|
||||
struct kms_property kms_prop;
|
||||
char *sys_name = NULL;
|
||||
int rc = 0;
|
||||
|
||||
util_assert(kms_info != NULL, "Internal error: kms_info is NULL");
|
||||
util_assert(key1_id != NULL, "Internal error: key1_id is NULL");
|
||||
util_assert(xts == false || key2_id != NULL,
|
||||
"Internal error: key2_id is NULL");
|
||||
util_assert(key_blob != NULL, "Internal error: key_blob is NULL");
|
||||
util_assert(key_blob_length != NULL,
|
||||
"Internal error: key_blob_length is NULL");
|
||||
|
||||
if (kms_info->plugin_lib == NULL) {
|
||||
warnx("The repository is not bound to a KMS plugin");
|
||||
return -ENOENT;
|
||||
}
|
||||
|
||||
if (kms_info->funcs->kms_import_key == NULL ||
|
||||
kms_info->funcs->kms_set_key_properties == NULL) {
|
||||
pr_verbose(verbose, "The KMS plugin does not support to "
|
||||
"import keys");
|
||||
return -ENOTSUP;
|
||||
}
|
||||
|
||||
sys_name = _get_system_specific_prop_name(KMS_KEY_PROP_NAME);
|
||||
if (sys_name == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
key_blob_size = *key_blob_length;
|
||||
memset(key_blob, 0, key_blob_size);
|
||||
|
||||
rc = kms_info->funcs->kms_import_key(kms_info->handle, key1_id,
|
||||
key_blob, &key_blob_size);
|
||||
if (rc != 0) {
|
||||
pr_verbose(verbose, "KMS plugin failed to import key '%s': %s",
|
||||
key1_id, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (is_cca_aes_data_key(key_blob, key_blob_size))
|
||||
key_size = AESDATA_KEY_SIZE;
|
||||
else if (is_cca_aes_cipher_key(key_blob, key_blob_size))
|
||||
key_size = AESCIPHER_KEY_SIZE;
|
||||
else if (is_ep11_aes_key(key_blob, key_blob_size))
|
||||
key_size = EP11_KEY_SIZE;
|
||||
|
||||
if (key_size == 0 || key_blob_size > key_size) {
|
||||
pr_verbose(verbose, "Key '%s' has an unknown or unsupported "
|
||||
"key type", key1_id);
|
||||
rc = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
key_blob_ofs = key_size;
|
||||
|
||||
if (xts) {
|
||||
key_blob_size = key_size;
|
||||
rc = kms_info->funcs->kms_import_key(kms_info->handle, key2_id,
|
||||
key_blob + key_blob_ofs,
|
||||
&key_blob_size);
|
||||
if (rc != 0) {
|
||||
pr_verbose(verbose, "KMS plugin failed to import key #2"
|
||||
"'%s': %s", key2_id, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
key_blob_ofs += key_size;
|
||||
}
|
||||
|
||||
*key_blob_length = key_blob_ofs;
|
||||
|
||||
kms_prop.name = sys_name;
|
||||
kms_prop.value = name;
|
||||
|
||||
rc = kms_info->funcs->kms_set_key_properties(kms_info->handle, key1_id,
|
||||
&kms_prop, 1);
|
||||
if (rc != 0) {
|
||||
pr_verbose(verbose, "KMS plugin failed to set properties of "
|
||||
"key '%s': %s", key1_id, strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (xts) {
|
||||
rc = kms_info->funcs->kms_set_key_properties(kms_info->handle,
|
||||
key2_id,
|
||||
&kms_prop, 1);
|
||||
if (rc != 0) {
|
||||
pr_verbose(verbose, "KMS plugin failed to set "
|
||||
"properties of key #2 '%s': %s", key1_id,
|
||||
strerror(-rc));
|
||||
goto out;
|
||||
}
|
||||
}
|
||||
|
||||
out:
|
||||
if (sys_name != NULL)
|
||||
free(sys_name);
|
||||
if (rc != 0)
|
||||
*key_blob_length = 0;
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -110,4 +110,9 @@ int list_kms_keys(struct kms_info *kms_info, const char *label_filter,
|
||||
const char *volume_type, struct kms_option *kms_options,
|
||||
size_t num_kms_options, bool verbose);
|
||||
|
||||
int import_kms_key(struct kms_info *kms_info, const char *key1_id,
|
||||
const char *key2_id, bool xts, const char *name,
|
||||
unsigned char *key_blob, size_t *key_blob_length,
|
||||
bool verbose);
|
||||
|
||||
#endif
|
||||
|
||||
91
zkey/zkey.1
91
zkey/zkey.1
@@ -1065,6 +1065,50 @@ command displays the attributes of the secure keys, such as key label, key name,
|
||||
whether it is a secure key that can be used for the XTS cipher mode, the textual
|
||||
description, associated volumes, the volume type, and sector size.
|
||||
.
|
||||
.SS "Import secure keys managed by a key management system into the repository"
|
||||
.
|
||||
.B zkey kms
|
||||
.BR import | im
|
||||
.RB [ \-\-label | \-B
|
||||
.IR key-label ]
|
||||
.RB [ \-\-name | \-N
|
||||
.IR key-name ]
|
||||
.RB [ \-\-volumes | \-l
|
||||
.IR volume1[:dmname1][,volume2[:dmname2][,...]] ]
|
||||
.RB [ \-\-volume-type | \-t
|
||||
.IR type ]
|
||||
.RB [ \-\-batch\-mode | \-q ]
|
||||
.RB [ \-\-no\-volume\-check ]
|
||||
.RB [ KMS\-plugin\ specific\ options ]
|
||||
.RB [ \-\-verbose | \-V ]
|
||||
.
|
||||
.PP
|
||||
Use the
|
||||
.B kms import
|
||||
command to import secure keys managed by a key management system (KMS) into the
|
||||
secure key repository.
|
||||
You can filter the list of keys to be imported by key label, key name,
|
||||
associated volumes, and volume type. You can use wildcards for the key label,
|
||||
key name, and associated volumes. The device-mapper name of an associated
|
||||
volume can be omitted. If it is specified, then only those keys are listed that
|
||||
are associated with the specified volume and device-mapper name.
|
||||
.PP
|
||||
A key management system plugin may offer plugin specific options that can be
|
||||
specified with the \fBkms import\fP command. Use \fBkms import \-\-help\fP
|
||||
to display the plugin specific options and their meaning.
|
||||
.PP
|
||||
If a secure key with the same name as a key to be imported already exists in
|
||||
the repository, then you are prompted to enter an alternate name. You can skip
|
||||
the import of that key, or enter an alternate name. If option
|
||||
\fB\-\-batch\-mode\fP is specified, then already existing keys are skipped.
|
||||
.PP
|
||||
If a key to be imported is associated with one or multiple volumes, it is
|
||||
verified that the volumes are available, and are not already associated with
|
||||
another secure key in the repository. If one of the volumes or all of them are
|
||||
not available, or are already associated with another secure key, the import
|
||||
fails. Use option \fB\-\-no\-volume\-check\fP to omit the volume check, and
|
||||
import the keys even if the associated volume(s) do not exist.
|
||||
.
|
||||
.
|
||||
.
|
||||
.SH OPTIONS
|
||||
@@ -1756,7 +1800,52 @@ has been compiled with LUKS2 support enabled.
|
||||
.
|
||||
.
|
||||
.
|
||||
|
||||
.SS "Options for the kms import command"
|
||||
.TP
|
||||
.BR \-B ", " \-\-label\~\fIkey-label\fP
|
||||
Specifies the label of the secure key in the key management system (KMS).
|
||||
You can use wildcards to select multiple secure keys.
|
||||
When wildcards are used you must quote the value.
|
||||
Only keys with labels that match the pattern are imported.
|
||||
.TP
|
||||
.BR \-N ", " \-\-name\~\fIkey-name\fP
|
||||
Specifies the name of the secure key in the key management system (KMS).
|
||||
You can use wildcards to select multiple secure keys.
|
||||
When wildcards are used you must quote the value.
|
||||
Only keys with names that match the pattern are imported.
|
||||
.TP
|
||||
.BR \-l ", " \-\-volumes\~\fIvolume1[:dmname1][,volume2[:dmname2][,...]]\fP
|
||||
Specifies a comma-separated list of volumes (block devices) which are
|
||||
associated with the secure AES key in the key management system (KMS). Only
|
||||
those keys are imported, which are associated with the specified volumes.
|
||||
The volume association also contains the device-mapper name, separated by a
|
||||
colon, used with dm-crypt. You can omit the device-mapper name; if it is
|
||||
specified then only those keys are listed that are associated with the
|
||||
specified volume and device-mapper name. You can use wildcards to specify
|
||||
the volumes and device-mapper names.
|
||||
When wildcards are used you must quote the value.
|
||||
.TP
|
||||
.BR \-t ", " \-\-volume-type\~\fItype\fP
|
||||
Specifies the volume type of the associated volumes used with dm-crypt. Possible
|
||||
values are \fBplain\fP and \fBluks2\fP. Only keys with the specified volume
|
||||
type are imported.
|
||||
This option is only available if
|
||||
.B zkey
|
||||
has been compiled with LUKS2 support enabled.
|
||||
.TP
|
||||
.BR \-q ", " \-\-batch\-mode
|
||||
Suppress prompts to skip or to enter an anternate name, if a secure key with the
|
||||
same name as the secure key to be imported already exists in the repository.
|
||||
When this option is specified, then keys with an altready existing name are
|
||||
skipped.
|
||||
.TP
|
||||
.BR \-\-no\-volume\-check
|
||||
Do not check if the volume(s) associated with the to be imported secure key(s)
|
||||
are available, or are already associated with other secure keys in the
|
||||
repository.
|
||||
.
|
||||
.
|
||||
.
|
||||
.SS "General options"
|
||||
.TP
|
||||
.BR \-V ", " \-\-verbose
|
||||
|
||||
104
zkey/zkey.c
104
zkey/zkey.c
@@ -72,6 +72,7 @@ static struct zkey_globals {
|
||||
char *volumes;
|
||||
char *apqns;
|
||||
bool noapqncheck;
|
||||
bool novolcheck;
|
||||
long int sector_size;
|
||||
char *volume_type;
|
||||
char *newname;
|
||||
@@ -128,6 +129,7 @@ static struct zkey_globals {
|
||||
#define COMMAND_KMS_CONFIGURE "configure"
|
||||
#define COMMAND_KMS_REENCIPHER "reencipher"
|
||||
#define COMMAND_KMS_LIST "list"
|
||||
#define COMMAND_KMS_IMPORT "import"
|
||||
|
||||
#define OPT_COMMAND_PLACEHOLDER "PLACEHOLDER"
|
||||
|
||||
@@ -150,6 +152,7 @@ static struct zkey_globals {
|
||||
#define OPT_CRYPTSETUP_OPEN 260
|
||||
#define OPT_CRYPTSETUP_FORMAT 261
|
||||
#define OPT_NO_APQN_CHECK 262
|
||||
#define OPT_NO_VOLUME_CHECK 263
|
||||
|
||||
/*
|
||||
* Configuration of command line options
|
||||
@@ -961,6 +964,66 @@ static struct util_opt opt_vec[] = {
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_LIST,
|
||||
},
|
||||
#endif
|
||||
/***********************************************************/
|
||||
{
|
||||
.flags = UTIL_OPT_FLAG_SECTION,
|
||||
.desc = "OPTIONS",
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_IMPORT,
|
||||
},
|
||||
{
|
||||
.option = { "label", required_argument, NULL, 'B'},
|
||||
.argument = "LABEL",
|
||||
.desc = "Label of the secure AES keys as known by the KMS that "
|
||||
"are to be imported. You can use wildcards to select "
|
||||
"the keys to be imported.",
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_IMPORT,
|
||||
},
|
||||
{
|
||||
.option = { "name", required_argument, NULL, 'N'},
|
||||
.argument = "NAME",
|
||||
.desc = "Name of the secure AES keys as known by zkey that "
|
||||
"are to be imported. You can use wildcards to select "
|
||||
"the keys to be imported.",
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_IMPORT,
|
||||
},
|
||||
{
|
||||
.option = { "volumes", required_argument, NULL, 'l'},
|
||||
.argument = "VOLUME[:DMNAME][,...]",
|
||||
.desc = "Comma-separated pairs of volume and device-mapper "
|
||||
"names that are associated with the secure AES key in "
|
||||
"the KMS. Use this option to import all keys "
|
||||
"associated with specific volumes. The device-mapper "
|
||||
"name (DMNAME) is optional. If specified, only those "
|
||||
"keys are listed where both, the volume and the device-"
|
||||
"mapper name matches.",
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_IMPORT,
|
||||
},
|
||||
#ifdef HAVE_LUKS2_SUPPORT
|
||||
{
|
||||
.option = { "volume-type", required_argument, NULL, 't'},
|
||||
.argument = "type",
|
||||
.desc = "The type of the associated volume(s). Possible values "
|
||||
"are 'plain' and 'luks2'. Use this option to import "
|
||||
"all keys with the specified volumes type.",
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_IMPORT,
|
||||
},
|
||||
#endif
|
||||
{
|
||||
.option = {"batch-mode", 0, NULL, 'q'},
|
||||
.desc = "Suppresses alternate name questions. When importing a "
|
||||
"key with a name that already exists in the "
|
||||
"repository, do not prompt for an alternate name, but "
|
||||
"skip the import of the duplicate key.",
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_IMPORT,
|
||||
},
|
||||
{
|
||||
.option = {"no-volume-check", 0, NULL, OPT_NO_VOLUME_CHECK},
|
||||
.desc = "Do not check if the volume(s) associated with the "
|
||||
"secure key(s) to be imported are available, or are "
|
||||
"already associated with other secure keys.",
|
||||
.command = COMMAND_KMS " " COMMAND_KMS_IMPORT,
|
||||
.flags = UTIL_OPT_FLAG_NOSHORT,
|
||||
},
|
||||
/***********************************************************/
|
||||
OPT_PLACEHOLDER,
|
||||
OPT_PLACEHOLDER,
|
||||
@@ -1063,6 +1126,7 @@ static int command_kms_info(void);
|
||||
static int command_kms_configure(void);
|
||||
static int command_kms_reencipher(void);
|
||||
static int command_kms_list(void);
|
||||
static int command_kms_import(void);
|
||||
|
||||
static struct zkey_command zkey_kms_commands[] = {
|
||||
{
|
||||
@@ -1150,6 +1214,20 @@ static struct zkey_command zkey_kms_commands[] = {
|
||||
.need_kms_login = 1,
|
||||
.kms_plugin_opts_cmd = KMS_COMMAND_LIST,
|
||||
},
|
||||
{
|
||||
.command = COMMAND_KMS_IMPORT,
|
||||
.abbrev_len = 2,
|
||||
.function = command_kms_import,
|
||||
.short_desc = "Imports secure keys managed by a key management "
|
||||
"system",
|
||||
.long_desc = "Imports secure keys managed by a key management "
|
||||
"system (KMS) into the repository",
|
||||
.need_keystore = 1,
|
||||
.has_options = 1,
|
||||
.use_kms_plugin = 1,
|
||||
.need_kms_login = 1,
|
||||
.kms_plugin_opts_cmd = KMS_COMMAND_LIST_IMPORT,
|
||||
},
|
||||
{ .command = NULL }
|
||||
};
|
||||
|
||||
@@ -2531,6 +2609,29 @@ static int command_kms_list(void)
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/*
|
||||
* Command handler for 'kms import'.
|
||||
*
|
||||
* Imports secure keys managed by a KMS
|
||||
*/
|
||||
static int command_kms_import(void)
|
||||
{
|
||||
int rc;
|
||||
|
||||
if (g.kms_info.plugin_lib == NULL) {
|
||||
rc = -ENOENT;
|
||||
warnx("The repository is not bound to a KMS plugin");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
rc = keystore_import_kms_keys(g.keystore, g.label, g.name, g.volumes,
|
||||
g.volume_type, g.kms_options,
|
||||
g.num_kms_options, g.batch_mode,
|
||||
g.novolcheck);
|
||||
|
||||
return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the keystore. The keystore directory is either the
|
||||
* default directory or as specified in an environment variable
|
||||
@@ -2761,6 +2862,9 @@ int main(int argc, char *argv[])
|
||||
case OPT_NO_APQN_CHECK:
|
||||
g.noapqncheck = 1;
|
||||
break;
|
||||
case OPT_NO_VOLUME_CHECK:
|
||||
g.novolcheck = 1;
|
||||
break;
|
||||
case 'S':
|
||||
g.sector_size = strtol(optarg, &endp, 0);
|
||||
if (*optarg == '\0' || *endp != '\0' ||
|
||||
|
||||
Reference in New Issue
Block a user