diff --git a/zkey/keystore.c b/zkey/keystore.c index 038e2fcb..7d6bb893 100644 --- a/zkey/keystore.c +++ b/zkey/keystore.c @@ -40,12 +40,16 @@ struct key_filenames { char *skey_filename; char *info_filename; char *renc_filename; + char *pass_filename; }; #define FILE_EXTENSION_LEN 5 #define SKEY_FILE_EXTENSION ".skey" #define INFO_FILE_EXTENSION ".info" #define RENC_FILE_EXTENSION ".renc" +#define PASS_FILE_EXTENSION ".pass" + +#define DUMMY_PASSPHRASE_LEN 16 #define LOCK_FILE_NAME ".lock" @@ -76,6 +80,7 @@ struct key_filenames { #define REC_VOLUME_TYPE "Volume type" #define REC_KMS "KMS" #define REC_KMS_KEY_LABEL "KMS key label" +#define REC_PASSPHRASE_FILE "Dummy passphrase" #define pr_verbose(keystore, fmt...) do { \ if (keystore->verbose) \ @@ -110,6 +115,8 @@ static int _keystore_get_key_filenames(struct keystore *keystore, name, INFO_FILE_EXTENSION); util_asprintf(&names->renc_filename, "%s/%s%s", keystore->directory, name, RENC_FILE_EXTENSION); + util_asprintf(&names->pass_filename, "%s/%s%s", keystore->directory, + name, PASS_FILE_EXTENSION); pr_verbose(keystore, "File names for key '%s': '%s' and '%s'", name, names->skey_filename, names->info_filename); @@ -128,6 +135,18 @@ static int _keystore_reencipher_key_exists(struct key_filenames *file_names) return util_path_is_reg_file("%s", file_names->renc_filename); } +/** + * Checks if the .pass file exists. + * + * @param[in] file_names names of the files + * + * @returns 1 if the file exist, 0 if the file do not exist + */ +static int _keystore_passphrase_file_exists(struct key_filenames *file_names) +{ + return util_path_is_reg_file("%s", file_names->pass_filename); +} + /** * Checks if both, the .skey and the .info (and .renc) files exist. * @@ -146,7 +165,8 @@ static int _keystore_exists_keyfiles(struct key_filenames *file_names) if (rc_skey && rc_info) return 1; if (!rc_skey && !rc_info && - _keystore_reencipher_key_exists(file_names) == 0) + _keystore_reencipher_key_exists(file_names) == 0 && + _keystore_passphrase_file_exists(file_names) == 0) return 0; return -1; } @@ -220,6 +240,8 @@ static void _keystore_free_key_filenames(struct key_filenames *names) free(names->info_filename); if (names->renc_filename) free(names->renc_filename); + if (names->pass_filename) + free(names->pass_filename); } /** @@ -985,7 +1007,7 @@ static int _keystore_process_filtered(struct keystore *keystore, process_key_t process_func, void *process_private) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; char **apqn_filter_list = NULL; char **vol_filter_list = NULL; struct properties *key_props; @@ -1593,6 +1615,129 @@ static int _keystore_set_default_properties(struct properties *key_props) return 0; } +/** + * Generate, Set or remove a dummy LUKS2 passphrase of a key. + * + * @param[in] keystore the key store + * @param[in] name the name of the key + * @param[in] file_names the file names of the key + * @param[in] properties the properties of the key + * @param[in] prompt if true, prompt for removal (if passphrase exists) + * + * @returns 0 on success, or a negative errno value on error + */ +static int _keystore_remove_passphrase(struct keystore *keystore, + const char *name, + const struct key_filenames *filenames, + struct properties *properties, + bool prompt) +{ + int rc; + + if (_keystore_passphrase_file_exists((struct key_filenames *)filenames) + && prompt) { + util_print_indented("ATTENTION: When you remove the LUKS2 " + "dummy passphrase of a key, you might no " + "longer be able to open the LUKS2 volumes " + "associated with the key, unless you still " + "know a passphrase of one of the key slots " + "of these volumes!", 0); + _keystore_msg_for_volumes("The following volumes are encrypted " + "with this key:", properties, NULL); + printf("%s: Remove passphrase for key '%s' [y/N]? ", + program_invocation_short_name, name); + if (!prompt_for_yes(keystore->verbose)) { + warnx("Operation aborted"); + return -ECANCELED; + } + } + + rc = remove(filenames->pass_filename); + if (rc != 0 && errno != ENOENT) { + rc = -errno; + warnx("Failed to remove file '%s': %s", + filenames->pass_filename, strerror(-rc)); + return rc; + } + + return 0; +} + +/** + * Generate, Set or remove a dummy LUKS2 passphrase of a key. + * + * @param[in] keystore the key store + * @param[in] name the name of the key + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2. If NULKL, the passphrase is generated by + * random. + * @param[in] file_names the file names of the key + * @param[in] properties the properties of the key + * @param[in] prompt if true, prompt for change, if passphrase exists + * already + * + * @returns 0 on success, or a negative errno value on error + */ +static int _keystore_set_passphrase(struct keystore *keystore, + const char *name, + const char *passphrase_file, + const struct key_filenames *filenames, + struct properties *properties, + bool prompt) +{ + char *volume_type; + int rc; + + if (_keystore_passphrase_file_exists((struct key_filenames *)filenames) + && prompt) { + warnx("There is already a LUKS2 dummy passphrase associated " + "with key '%s'.", name); + util_print_indented("To change a dummy passphrase of a key, " + "first remove the currently associated " + "passphrase with command 'zkey change " + "--name --remove-dummy-passphrase' " + "and then set the new dummy passphrase for " + "the key.", 0); + return -EEXIST; + } + + volume_type = _keystore_get_volume_type(properties); + if (volume_type == NULL) { + pr_verbose(keystore, "No volume type available"); + return -EINVAL; + } + if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) != 0) { + warnx("The LUKS2 dummy passphrase can only be set for keys " + "with a volume type of LUKS2."); + free(volume_type); + return -EINVAL; + } + free(volume_type); + + if (passphrase_file != NULL) { + rc = copy_file(passphrase_file, filenames->pass_filename, 0); + if (rc != 0) { + warnx("Failed to copy the passphrase phase '%s': %s", + passphrase_file, strerror(-rc)); + return rc; + } + } else { + rc = copy_file("/dev/urandom", filenames->pass_filename, + DUMMY_PASSPHRASE_LEN); + if (rc != 0) { + warnx("Failed to generate the dummy passphrase: %s", + strerror(-rc)); + return rc; + } + } + + rc = _keystore_set_file_permission(keystore, filenames->pass_filename); + if (rc != 0) + return rc; + + return 0; +} + /** * Creates the key properties for a key * @@ -1615,6 +1760,8 @@ static int _keystore_set_default_properties(struct properties *key_props) * @param[in] key_type the type of the key * @param[in] kms the name of the KMS plugin, or NULL if no KMS is bound * @param[out] props the properties object is allocated and returned + * + * @returns 0 on success, or a negative errno value on error */ static int _keystore_create_info_props(struct keystore *keystore, const char *name, @@ -1721,7 +1868,7 @@ out: * * @param[in] keystore the key store * @param[in] name the name of the key - * @param[in] info_filename the file name of the key info file + * @param[in] filenames the file names of the key files * @param[in] description textual description of the key (optional, can be NULL) * @param[in] volumes a comma separated list of volumes associated with this * key (optional, can be NULL) @@ -1735,7 +1882,12 @@ out: * default is used. * @param[in] volume_type the type of volume * @param[in] key_type the type of the key + * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2 (optional, can be NULL) * @param[in] kms the name of the KMS plugin, or NULL if no KMS is bound + * + * @returns 0 on success, or a negative errno value on error */ static int _keystore_create_info_file(struct keystore *keystore, const char *name, @@ -1746,6 +1898,8 @@ static int _keystore_create_info_file(struct keystore *keystore, size_t sector_size, const char *volume_type, const char *key_type, + bool gen_passphrase, + const char *passphrase_file, const char *kms) { struct properties *key_props = NULL; @@ -1758,12 +1912,24 @@ static int _keystore_create_info_file(struct keystore *keystore, if (rc != 0) return rc; + if (gen_passphrase || passphrase_file != NULL) { + rc = _keystore_set_passphrase(keystore, name, gen_passphrase ? + NULL : passphrase_file, + filenames, key_props, true); + if (rc != 0) { + pr_verbose(keystore, "Failed to set the passphrase: %s", + strerror(-rc)); + goto out; + } + } + rc = _keystore_ensure_vp_exists(keystore, filenames, key_props); 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"); + remove(filenames->pass_filename); goto out; } @@ -1772,12 +1938,14 @@ static int _keystore_create_info_file(struct keystore *keystore, pr_verbose(keystore, "Key info file '%s' could not be written: %s", filenames->info_filename, strerror(-rc)); + remove(filenames->pass_filename); goto out; } rc = _keystore_set_file_permission(keystore, filenames->info_filename); if (rc != 0) { remove(filenames->info_filename); + remove(filenames->pass_filename); goto out; } @@ -1809,6 +1977,9 @@ out: * if NULL, the secure key is generated by random. * @param[in] volume_type the type of volume * @param[in] key_type the type of the key + * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2 (optional, can be NULL) * @param[in] pkey_fd the file descriptor of /dev/pkey * * @returns 0 for success or a negative errno in case of an error @@ -1818,9 +1989,10 @@ int keystore_generate_key(struct keystore *keystore, const char *name, const char *apqns, bool noapqncheck, size_t sector_size, size_t keybits, bool xts, const char *clear_key_file, const char *volume_type, - const char *key_type, int pkey_fd) + const char *key_type, bool gen_passphrase, + const char *passphrase_file, int pkey_fd) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; char **apqn_list = NULL; int rc; @@ -1880,7 +2052,8 @@ int keystore_generate_key(struct keystore *keystore, const char *name, rc = _keystore_create_info_file(keystore, name, &file_names, description, volumes, apqns, noapqncheck, sector_size, volume_type, - key_type, NULL); + key_type, gen_passphrase, + passphrase_file, NULL); if (rc != 0) goto out_free_props; @@ -1921,6 +2094,9 @@ out_free_key_filenames: * @param[in] xts if true, an XTS key is generated * @param[in] volume_type the type of volume * @param[in] key_type the type of the key (can be NULL) + * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2 (optional, can be NULL) * @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 @@ -1931,10 +2107,11 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name, const char *description, const char *volumes, size_t sector_size, size_t keybits, bool xts, const char *volume_type, const char *key_type, + bool gen_passphrase, const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; struct kms_info *kms_info; char *apqns = NULL; @@ -2000,8 +2177,21 @@ int keystore_generate_key_kms(struct keystore *keystore, const char *name, if (rc != 0) goto out_free_key_filenames; + if (gen_passphrase || passphrase_file != NULL) { + rc = _keystore_set_passphrase(keystore, name, gen_passphrase ? + NULL : passphrase_file, + &file_names, key_props, true); + if (rc != 0) { + pr_verbose(keystore, "Failed to set the passphrase: %s", + strerror(-rc)); + goto out_free_key_filenames; + } + } + rc = generate_kms_key(kms_info, name, key_type, key_props, xts, keybits, file_names.skey_filename, + _keystore_passphrase_file_exists(&file_names) ? + file_names.pass_filename : NULL, kms_options, num_kms_options, keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to generate key '%s': %s", @@ -2048,8 +2238,10 @@ out_del_info_file: out_free_props: if (key_props != NULL) properties_free(key_props); - if (rc != 0) + if (rc != 0) { remove(file_names.skey_filename); + remove(file_names.pass_filename); + } out_free_key_filenames: _keystore_free_key_filenames(&file_names); if (apqns != NULL) @@ -2081,6 +2273,9 @@ out_free_key_filenames: * default is used. * @param[in] import_file The name of a secure key containing the key to import * @param[in] volume_type the type of volume + * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2 (optional, can be NULL) * @param[in] lib the external library struct * * @returns 0 for success or a negative errno in case of an error @@ -2089,9 +2284,10 @@ int keystore_import_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, const char *import_file, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, struct ext_lib *lib) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; size_t secure_key_size; const char *key_type; @@ -2206,7 +2402,8 @@ int keystore_import_key(struct keystore *keystore, const char *name, rc = _keystore_create_info_file(keystore, name, &file_names, description, volumes, apqns, noapqncheck, sector_size, volume_type, - key_type, NULL); + key_type, gen_passphrase, + passphrase_file, NULL); if (rc != 0) goto out_free_props; @@ -2256,23 +2453,33 @@ out_free_key_filenames: * not be changed. * @param[in] volume_type the type of volume. If NULL then the volume type is * not changed. - * * + * @param[in] gen_passphrase if true, generate a (dummy) passphrase for LUKS2 + * @param[in] passphrase_file the file name of a file containing a passphrase + * for LUKS2 (optional, can be NULL) + * @param[in] remove_passphrase if true, remove the (dummy) passphrase + * @param[in] quiet if true no confirmation prompt is shown when removing + * a (dummy) passphrase + * * @returns 0 for success or a negative errno in case of an error * */ int keystore_change_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, - long int sector_size, const char *volume_type) + long int sector_size, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, + bool remove_passphrase, bool quiet) { struct volume_check vol_check = { .keystore = keystore, .name = name, .set = 0, .nocheck = 0 }; struct apqn_check apqn_check = { .noonlinecheck = noapqncheck, .nomsg = 0 }; - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; + const char **passphrase_upd = NULL; char *upd_volume_type = NULL; char *apqns_prop, *key_type; + const char *null_ptr = NULL; char *upd_volumes = NULL; size_t secure_key_size; u8 mkvp[MKVP_LENGTH]; @@ -2401,6 +2608,38 @@ int keystore_change_key(struct keystore *keystore, const char *name, upd_volume_type = properties_get(key_props, PROP_NAME_VOLUME_TYPE); + + /* Remove dummy passphrase if change to PLAIN volume type */ + if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) != 0 && + _keystore_passphrase_file_exists(&file_names)) { + rc = _keystore_remove_passphrase(keystore, name, + &file_names, key_props, + false); + if (rc != 0) + goto out; + + passphrase_upd = &null_ptr; + } + } + + if (gen_passphrase || passphrase_file != NULL) { + rc = _keystore_set_passphrase(keystore, name, gen_passphrase ? + NULL : passphrase_file, + &file_names, key_props, true); + if (rc != 0) + goto out; + + passphrase_upd = (const char **)&file_names.pass_filename; + } + + if (remove_passphrase) { + if (_keystore_passphrase_file_exists(&file_names)) + passphrase_upd = &null_ptr; + + rc = _keystore_remove_passphrase(keystore, name, &file_names, + key_props, !quiet); + if (rc != 0) + goto out; } if (kms_bound) { @@ -2412,7 +2651,7 @@ int keystore_change_key(struct keystore *keystore, const char *name, description, upd_volumes, upd_volume_type, sector_size >= 0 ? sect_size : NULL, - keystore->verbose); + passphrase_upd, keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to set key properties " "for key '%s': %s", @@ -2441,6 +2680,8 @@ int keystore_change_key(struct keystore *keystore, const char *name, pr_verbose(keystore, "Successfully changed key '%s'", name); out: + if (rc != 0 && passphrase_upd != NULL && *passphrase_upd != NULL) + remove(*passphrase_upd); _keystore_free_key_filenames(&file_names); if (key_props != NULL) properties_free(key_props); @@ -2467,10 +2708,11 @@ out: int keystore_rename_key(struct keystore *keystore, const char *name, const char *newname) { - struct key_filenames file_names = { NULL, NULL, NULL }; - struct key_filenames new_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; + struct key_filenames new_names = { 0 }; struct properties *key_props = NULL; bool reenc_exists = false; + bool pass_exists = false; char *msg; int rc; @@ -2516,6 +2758,16 @@ int keystore_rename_key(struct keystore *keystore, const char *name, goto out_rename_info; } } + if (_keystore_passphrase_file_exists(&file_names)) { + pass_exists = true; + if (rename(file_names.pass_filename, + new_names.pass_filename) != 0) { + rc = -errno; + pr_verbose(keystore, "Failed to rename '%s': %s", + file_names.pass_filename, strerror(-rc)); + goto out_rename_info; + } + } key_props = properties_new(); rc = properties_load(key_props, new_names.info_filename, 1); @@ -2531,7 +2783,7 @@ int keystore_rename_key(struct keystore *keystore, const char *name, rc = set_kms_key_properties(keystore->kms_info, key_props, newname, NULL, NULL, NULL, NULL, - keystore->verbose); + NULL, keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to set key properties " "for key '%s': %s", @@ -2549,6 +2801,16 @@ int keystore_rename_key(struct keystore *keystore, const char *name, _keystore_msg_for_volumes(msg, key_props, VOLUME_TYPE_PLAIN); free(msg); + if (_keystore_passphrase_file_exists(&new_names)) { + util_asprintf(&msg, "The following volumes are associated with " + "the renamed key '%s'. You should adjust the " + "corresponding crypttab entries to use the new " + "dummy passphrase file name '%s'.", newname, + new_names.pass_filename); + _keystore_msg_for_volumes(msg, key_props, VOLUME_TYPE_LUKS2); + free(msg); + } + pr_verbose(keystore, "Successfully renamed key '%s' to '%s'", name, newname); @@ -2557,6 +2819,8 @@ int keystore_rename_key(struct keystore *keystore, const char *name, out_rename_info: if (reenc_exists) rename(file_names.renc_filename, new_names.renc_filename); + if (pass_exists) + rename(file_names.pass_filename, new_names.pass_filename); rename(new_names.info_filename, file_names.info_filename); out_rename_skey: @@ -2613,6 +2877,8 @@ static struct util_rec *_keystore_setup_record(bool validation) util_rec_def(rec, REC_KMS, UTIL_REC_ALIGN_LEFT, 54, REC_KMS); util_rec_def(rec, REC_KMS_KEY_LABEL, UTIL_REC_ALIGN_LEFT, 54, REC_KMS_KEY_LABEL); + util_rec_def(rec, REC_PASSPHRASE_FILE, UTIL_REC_ALIGN_LEFT, 54, + REC_PASSPHRASE_FILE); util_rec_def(rec, REC_CREATION_TIME, UTIL_REC_ALIGN_LEFT, 54, REC_CREATION_TIME); util_rec_def(rec, REC_CHANGE_TIME, UTIL_REC_ALIGN_LEFT, 54, @@ -2629,7 +2895,8 @@ static void _keystore_print_record(struct util_rec *rec, bool validation, const char *skey_filename, size_t secure_key_size, bool is_xts, size_t clear_key_bitsize, bool valid, - bool is_old_mk, bool reenc_pending, u8 *mkvp) + bool is_old_mk, bool reenc_pending, u8 *mkvp, + const char *pass_filename) { char temp_vp[VERIFICATION_PATTERN_LEN + 2]; char *kms_xts_key1_label = NULL; @@ -2767,6 +3034,10 @@ static void _keystore_print_record(struct util_rec *rec, label_argz_len); else util_rec_set(rec, REC_KMS_KEY_LABEL, "(local)"); + if (pass_filename != NULL) + util_rec_set(rec, REC_PASSPHRASE_FILE, pass_filename); + else + util_rec_set(rec, REC_PASSPHRASE_FILE, "(none)"); util_rec_set(rec, REC_CREATION_TIME, creation); util_rec_set(rec, REC_CHANGE_TIME, change != NULL ? change : "(never)"); @@ -2966,7 +3237,9 @@ static int _keystore_process_validate(struct keystore *keystore, is_xts_key(secure_key, secure_key_size), clear_key_bitsize, valid, is_old_mk, _keystore_reencipher_key_exists(file_names), - mkvp); + mkvp, + _keystore_passphrase_file_exists(file_names) ? + file_names->pass_filename : NULL); if (valid && is_old_mk) { util_print_indented("WARNING: The secure key is currently " @@ -3434,8 +3707,8 @@ int keystore_copy_key(struct keystore *keystore, const char *name, { 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 key_filenames file_names = { 0 }; + struct key_filenames new_names = { 0 }; struct properties *key_prop = NULL; size_t secure_key_size; bool kms_bound = false; @@ -3536,7 +3809,6 @@ int keystore_copy_key(struct keystore *keystore, const char *name, pr_verbose(keystore, "Key info file '%s' could not be written: %s", new_names.info_filename, strerror(-rc)); - remove(new_names.skey_filename); goto out; } @@ -3544,6 +3816,23 @@ int keystore_copy_key(struct keystore *keystore, const char *name, if (rc != 0) goto out; + if (_keystore_passphrase_file_exists(&file_names)) { + rc = copy_file(file_names.skey_filename, + new_names.pass_filename, 0); + if (rc != 0) { + pr_verbose(keystore, + "Passphrase file '%s' could not be copied: " + "%s", new_names.pass_filename, + strerror(-rc)); + goto out; + } + + rc = _keystore_set_file_permission(keystore, + file_names.pass_filename); + if (rc != 0) + goto out; + } + pr_verbose(keystore, "Successfully copied key '%s' to '%s'", name, newname); @@ -3551,6 +3840,7 @@ out: if (rc != 0) { remove(new_names.skey_filename); remove(new_names.info_filename); + remove(new_names.pass_filename); } _keystore_free_key_filenames(&file_names); @@ -3576,7 +3866,7 @@ out: int keystore_export_key(struct keystore *keystore, const char *name, const char *export_file) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; size_t secure_key_size; u8 *secure_key; int rc; @@ -3675,7 +3965,7 @@ int keystore_remove_key(struct keystore *keystore, const char *name, bool quiet, struct kms_option *kms_options, size_t num_kms_options) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; struct properties *key_props = NULL; int rc; @@ -3739,6 +4029,13 @@ int keystore_remove_key(struct keystore *keystore, const char *name, file_names.renc_filename, strerror(-rc)); } } + if (_keystore_passphrase_file_exists(&file_names)) { + if (remove(file_names.pass_filename) != 0) { + rc = -errno; + pr_verbose(keystore, "Failed to remove '%s': %s", + file_names.pass_filename, strerror(-rc)); + } + } pr_verbose(keystore, "Successfully removed key '%s'", name); out: @@ -3794,7 +4091,9 @@ static int _keystore_display_key(struct keystore *keystore, is_xts_key(secure_key, secure_key_size), clear_key_bitsize, 0, 0, _keystore_reencipher_key_exists(file_names), - NULL); + NULL, + _keystore_passphrase_file_exists(file_names) ? + file_names->pass_filename : NULL); out: free(secure_key); @@ -3910,6 +4209,7 @@ struct crypt_info { size_t key_file_size, size_t sector_size, const char *volume_type, + const char *passphrase_file, struct crypt_info *info); }; @@ -3925,6 +4225,7 @@ struct crypt_info { * @param[in] key_file_size the size of the key file in bytes * @param[in] sector_size the sector size in bytes or 0 if not specified * @param[in] volume_type the volume type + * @param[in] passphrase_file the passphrase file name (can be NULL) * @param[in] info processing info * * @returns 0 if successful, a negative errno value otherwise @@ -3937,6 +4238,7 @@ static int _keystore_process_cryptsetup(struct keystore *keystore, size_t key_file_size, size_t sector_size, const char *volume_type, + const char *passphrase_file, struct crypt_info *info) { char *keyfile_opt = NULL, *offset_opt = NULL; @@ -3957,6 +4259,9 @@ static int _keystore_process_cryptsetup(struct keystore *keystore, if (info->keyfile_size > 0) util_asprintf(&size_opt, "--keyfile-size %lu ", info->keyfile_size); + } else if (passphrase_file != NULL) { + util_asprintf(&keyfile_opt, "--key-file '%s' ", + passphrase_file); } if (info->tries > 0) util_asprintf(&tries_opt, "--tries %lu ", info->tries); @@ -4069,6 +4374,7 @@ static int _keystore_process_cryptsetup(struct keystore *keystore, * @param[in] key_file_size the size of the key file in bytes * @param[in] sector_size the sector size in bytes or 0 if not specified * @param[in] volume_type the volume type + * @param[in] passphrase_file the passphrase file name (can be NULL) * @param[in] info processing info (not used here) * * @returns 0 if successful, a negative errno value otherwise @@ -4082,6 +4388,7 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), size_t key_file_size, size_t sector_size, const char *volume_type, + const char *passphrase_file, struct crypt_info *info) { char temp[1000]; @@ -4105,14 +4412,19 @@ static int _keystore_process_crypttab(struct keystore *UNUSED(keystore), dmname, volume, key_file_name, cipher_spec, key_file_size * 8, sector_size > 0 ? temp : ""); } else if (strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { - printf("%s\t%s\t%s\tluks", dmname, volume, - info->keyfile != NULL ? info->keyfile : "none"); if (info->keyfile != NULL) { + printf("%s\t%s\t%s\tluks", dmname, volume, + info->keyfile); if (info->keyfile_offset > 0) printf(",keyfile-offset=%lu", info->keyfile_offset); if (info->keyfile_size > 0) printf(",keyfile-size=%lu", info->keyfile_size); + } else if (passphrase_file != NULL) { + printf("%s\t%s\t%s\tluks", dmname, volume, + passphrase_file); + } else { + printf("%s\t%s\tnone\tluks", dmname, volume); } if (info->tries > 0) printf(",tries=%lu", info->tries); @@ -4237,7 +4549,10 @@ static int _keystore_process_crypt(struct keystore *keystore, rc = info->process_func(keystore, vol, dmname, cipher_spec, file_names->skey_filename, secure_key_size, sector_size, - volume_type, info); + volume_type, + _keystore_passphrase_file_exists(file_names) ? + file_names->pass_filename : NULL, + info); if (rc != 0) break; } @@ -4403,7 +4718,7 @@ int keystore_convert_key(struct keystore *keystore, const char *name, const char *key_type, bool noapqncheck, bool quiet, int pkey_fd, struct ext_lib *lib) { - struct key_filenames file_names = { NULL, NULL, NULL }; + struct key_filenames file_names = { 0 }; u8 output_key[2 * MAX_SECURE_KEY_SIZE]; struct properties *properties = NULL; int rc, min_level, selected = 1; @@ -4920,6 +5235,7 @@ struct kms_import { * @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] passphrase the passphrase of the key (can be NULL) * @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 @@ -4940,12 +5256,13 @@ static int _keystore_process_kms_import(const char *key1_id, const char *volumes, const char *volume_type, size_t sector_size, + const char *passphrase, 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; + struct key_filenames file_names = { 0 }; u8 secure_key[2 * MAX_SECURE_KEY_SIZE]; struct properties *key_props = NULL; char vp[VERIFICATION_PATTERN_LEN]; @@ -5042,6 +5359,23 @@ prompt_alt_name: if (rc != 0) goto out; + if (passphrase != NULL && volume_type != NULL && + strcasecmp(volume_type, VOLUME_TYPE_LUKS2) == 0) { + rc = store_passphrase_from_base64(passphrase, + file_names.pass_filename, + keystore->verbose); + if (rc != 0) { + pr_verbose(keystore, "Failed to parse passphrase: %s", + strerror(-rc)); + goto out; + } + + rc = _keystore_set_file_permission(keystore, + file_names.pass_filename); + if (rc != 0) + goto out_remove; + } + rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_ID : PROP_NAME_KMS_KEY_ID, key1_id); if (rc != 0) { @@ -5267,7 +5601,9 @@ static int _keystore_refresh_kms_key(struct keystore *keystore, rc = refresh_kms_key(keystore->kms_info, properties, &description, &cipher, &iv_mode, &volumes, &volume_type, §or_size, - file_names->skey_filename, keystore->verbose); + file_names->skey_filename, + file_names->pass_filename, + keystore->verbose); if (rc != 0) { warnx("KMS plugin '%s' failed to refresh key '%s': %s", keystore->kms_info->plugin_name, name, strerror(-rc)); @@ -5277,6 +5613,17 @@ static int _keystore_refresh_kms_key(struct keystore *keystore, goto out; } + rc = _keystore_set_file_permission(keystore, file_names->skey_filename); + if (rc != 0) + goto out; + + if (_keystore_passphrase_file_exists(file_names)) { + rc = _keystore_set_file_permission(keystore, + file_names->pass_filename); + if (rc != 0) + goto out; + } + if (!refresh_data->refresh_properties) goto save_props; @@ -5345,6 +5692,10 @@ save_props: goto out; } + rc = _keystore_set_file_permission(keystore, file_names->info_filename); + if (rc != 0) + goto out; + out: if (rc == 0) { printf("Successfully refreshed key '%s'\n", name); diff --git a/zkey/keystore.h b/zkey/keystore.h index 944cc91d..1443b5df 100644 --- a/zkey/keystore.h +++ b/zkey/keystore.h @@ -54,12 +54,14 @@ int keystore_generate_key(struct keystore *keystore, const char *name, const char *apqns, bool noapqncheck, size_t sector_size, size_t keybits, bool xts, const char *clear_key_file, const char *volume_type, - const char *key_type, int pkey_fd); + const char *key_type, bool gen_passphrase, + const char *passphrase_file, int pkey_fd); int keystore_generate_key_kms(struct keystore *keystore, const char *name, const char *description, const char *volumes, size_t sector_size, size_t keybits, bool xts, const char *volume_type, const char *key_type, + bool gen_passphrase, const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options); @@ -67,12 +69,15 @@ int keystore_import_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, size_t sector_size, const char *import_file, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, struct ext_lib *lib); int keystore_change_key(struct keystore *keystore, const char *name, const char *description, const char *volumes, const char *apqns, bool noapqncheck, - long int sector_size, const char *volume_type); + long int sector_size, const char *volume_type, + bool gen_passphrase, const char *passphrase_file, + bool remove_passphrase, bool quiet); int keystore_rename_key(struct keystore *keystore, const char *name, const char *newname); diff --git a/zkey/kms.c b/zkey/kms.c index 93643c40..ab1113d1 100644 --- a/zkey/kms.c +++ b/zkey/kms.c @@ -64,6 +64,7 @@ #define KMS_KEY_PROP_XTS_KEY2_ID "xts-key2-id" #define KMS_KEY_PROP_XTS_KEY1_LABEL "xts-key1-label" #define KMS_KEY_PROP_XTS_KEY2_LABEL "xts-key2-label" +#define KMS_KEY_PROP_PASSPHRASE "dummy-passphrase-base64" #define KMS_REC_LABEL "Key label" #define KMS_REC_NAME "Name" @@ -2120,6 +2121,8 @@ static char *_get_system_specific_prop_name(const char *prop_name) * @param[in] keybits the key bit size (e.g. 128, 196, 256, 0 to use the * plugin's default) * @param[in] filename the file name to store the key in + * @param[in] passphrase_file the file name containing the LUKS2 passphrase, + * or NULL if no passphrase is set * @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 @@ -2131,6 +2134,7 @@ static char *_get_system_specific_prop_name(const char *prop_name) int generate_kms_key(struct kms_info *kms_info, const char *name, const char *key_type, struct properties *key_props, bool xts, size_t keybits, const char *filename, + const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options, bool verbose) { @@ -2140,12 +2144,13 @@ int generate_kms_key(struct kms_info *kms_info, const char *name, char key2_label[KMS_KEY_LABEL_SIZE + 1] = { 0 }; char key1_id[KMS_KEY_ID_SIZE + 1] = { 0 }; char key2_id[KMS_KEY_ID_SIZE + 1] = { 0 }; - struct kms_property kms_props[12]; + struct kms_property kms_props[13]; int xts_mode_prop = -1, rc = 0; size_t key_size, key_blob_size; enum kms_key_mode key_mode; size_t num_kms_props = 0; char *sys_volumes = NULL; + char *passphrase = NULL; util_assert(kms_info != NULL, "Internal error: kms_info is NULL"); util_assert(name != NULL, "Internal error: name is NULL"); @@ -2209,6 +2214,19 @@ int generate_kms_key(struct kms_info *kms_info, const char *name, "XTS-KEY-1"); } + if (passphrase_file != NULL) { + passphrase = read_passphrase_as_base64(passphrase_file, + verbose); + if (passphrase == NULL) { + pr_verbose(verbose, "Failed to read passphrase from " + "file '%s'", passphrase_file); + goto out; + } + + ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_PASSPHRASE, + passphrase); + } + key_mode = xts ? KMS_KEY_MODE_XTS_1 : KMS_KEY_MODE_NON_XTS; key_blob_size = key_size; @@ -2333,6 +2351,8 @@ out: free(sector_size); if (sys_volumes != NULL) free(sys_volumes); + if (passphrase != NULL) + free(passphrase); return rc; } @@ -2348,20 +2368,28 @@ out: * @param[in] volumes the volumes of the key (can be NULL) * @param[in] vol_type the volume type of the key (can be NULL) * @param[in] sector_size the sector_size of the key (can be NULL) + * @param[in] passphrase_file an address to a character string containing the + * file name containing the LUKS2 passphrase, + * or NULL if no passphrase is set. If + * passphrase_file itself is NULL, then no change in + * the passphrase property. + * @param[in] verbose if true, verbose messages are printed * * @returns 0 for success or a negative errno in case of an error. */ - int set_kms_key_properties(struct kms_info *kms_info, struct properties *key_props, const char *name, const char *description, const char *volumes, const char *vol_type, - const char *sector_size, bool verbose) + const char *sector_size, + const char **passphrase_file, + bool verbose) { char *key1_id = NULL, *key2_id = NULL; - struct kms_property kms_props[10]; + struct kms_property kms_props[11]; size_t num_kms_props = 0; char *sys_volumes = NULL; + char *passphrase = NULL; char *sys_name = NULL; bool xts = false; @@ -2416,6 +2444,22 @@ int set_kms_key_properties(struct kms_info *kms_info, if (sector_size != NULL) ADD_KMS_PROPS(kms_props, num_kms_props, KMS_KEY_PROP_SECTOR_SIZE, sector_size); + if (passphrase_file != NULL) { + /* *passphrase_file is NULL to remove the propoerty */ + if (*passphrase_file != NULL) { + passphrase = + read_passphrase_as_base64(*passphrase_file, + verbose); + if (passphrase == NULL) { + pr_verbose(verbose, "Failed to read passphrase" + " from file '%s'", *passphrase_file); + goto out; + } + } + + ADD_KMS_PROPS(kms_props, num_kms_props, + KMS_KEY_PROP_PASSPHRASE, passphrase); + } if (num_kms_props == 0) goto out; @@ -2450,6 +2494,8 @@ out: free(key1_id); if (key2_id != NULL) free(key2_id); + if (passphrase != NULL) + free(passphrase); return rc; } @@ -2642,6 +2688,7 @@ static int _process_kms_keys_cb(const char *key_id, const char *key_label, const char *name, *volumes, *cipher, *iv_mode, *description; const char *xts_key2_id = NULL, *xts_key2_label = NULL; const char *xts_key, *volume_type, *temp; + const char *passphrase; size_t sector_size = 0; bool xts = false; @@ -2697,6 +2744,8 @@ static int _process_kms_keys_cb(const char *key_id, const char *key_label, KMS_KEY_PROP_SECTOR_SIZE); if (temp != NULL) sscanf(temp, "%lu", §or_size); + passphrase = _find_property(properties, num_properties, + KMS_KEY_PROP_PASSPHRASE); if (process_data->label_filter != NULL) { if (fnmatch(process_data->label_filter, key_label, @@ -2730,6 +2779,7 @@ static int _process_kms_keys_cb(const char *key_id, const char *key_label, xts ? key_bits * 2 : key_bits, description, cipher, iv_mode, volumes, volume_type, sector_size, + passphrase, addl_info_argz, addl_info_len, process_data->private_data); } @@ -2867,6 +2917,7 @@ static int _list_kms_keys_cb(const char *UNUSED(key1_id), const char *UNUSED(cipher), const char *UNUSED(iv_mode), const char *volumes, const char *volume_type, size_t sector_size, + const char *UNUSED(passphrase), const char *addl_info_argz, size_t addl_info_len, void *private_data) { @@ -3141,6 +3192,7 @@ out: * @param[out] volume_type on return: the volume_type property * @param[out] sector_size on return: the sector_size property * @param[in] filename the file name to store the refreshed key blob in + * @param[in] passphrase_file the file name to store the dummy passphras in * @param[in] verbose if true, verbose messages are printed * * @returns 0 for success or a negative errno in case of an error. @@ -3148,7 +3200,8 @@ out: int refresh_kms_key(struct kms_info *kms_info, struct properties *key_props, char **description, char **cipher, char **iv_mode, char **volumes, char **volume_type, ssize_t *sector_size, - const char *filename, bool verbose) + const char *filename, const char *passphrase_file, + bool verbose) { struct kms_property *properties = NULL; u8 key_blob[2 * MAX_SECURE_KEY_SIZE]; @@ -3241,6 +3294,29 @@ int refresh_kms_key(struct kms_info *kms_info, struct properties *key_props, if (str != NULL) sscanf(str, "%lu", (long unsigned int *)sector_size); } + if (passphrase_file != NULL && volume_type != NULL && + *volume_type != NULL && strcasecmp(*volume_type, "luks2") == 0) { + str = _find_property(properties, num_properties, + KMS_KEY_PROP_PASSPHRASE); + if (str != NULL) { + rc = store_passphrase_from_base64(str, passphrase_file, + verbose); + if (rc != 0) { + pr_verbose(verbose, + "Failed to parse passphrase: %s", + strerror(-rc)); + goto out; + } + } else { + rc = remove(passphrase_file); + if (rc != 0 && errno != ENOENT) { + pr_verbose(verbose, + "Failed to remove passphrase_file: " + "%s", strerror(errno)); + goto out; + } + } + } key_blob_size = sizeof(key_blob); memset(key_blob, 0, key_blob_size); diff --git a/zkey/kms.h b/zkey/kms.h index b415df45..6ce8b11a 100644 --- a/zkey/kms.h +++ b/zkey/kms.h @@ -74,6 +74,7 @@ int get_kms_apqns_for_key_type(struct kms_info *kms_info, const char *key_type, int generate_kms_key(struct kms_info *kms_info, const char *name, const char *key_type, struct properties *key_props, bool xts, size_t keybits, const char *filename, + const char *passphrase_file, struct kms_option *kms_options, size_t num_kms_options, bool verbose); @@ -81,7 +82,9 @@ int set_kms_key_properties(struct kms_info *kms_info, struct properties *key_props, const char *name, const char *description, const char *volumes, const char *vol_type, - const char *sector_size, bool verbose); + const char *sector_size, + const char **passphrase_file, + bool verbose); int remove_kms_key(struct kms_info *kms_info, struct properties *key_props, struct kms_option *kms_options, size_t num_kms_options, @@ -94,6 +97,7 @@ typedef int (*kms_process_callback)(const char *key1_id, const char *key1_label, const char *description, const char *cipher, const char *iv_mode, const char *volumes, const char *volume_type, size_t sector_size, + const char *passphrase, const char *addl_info_argz, size_t addl_info_len, void *private_data); @@ -118,6 +122,7 @@ int import_kms_key(struct kms_info *kms_info, const char *key1_id, int refresh_kms_key(struct kms_info *kms_info, struct properties *key_props, char **description, char **cipher, char **iv_mode, char **volumes, char **volume_type, ssize_t *sector_size, - const char *filename, bool verbose); + const char *filename, const char *passphrase_file, + bool verbose); #endif diff --git a/zkey/utils.c b/zkey/utils.c index 39b35533..8ae93340 100644 --- a/zkey/utils.c +++ b/zkey/utils.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -25,7 +26,8 @@ #include "lib/util_rec.h" #include "lib/util_base.h" - #include +#include +#include #include "utils.h" #include "properties.h" @@ -1128,6 +1130,7 @@ bool prompt_for_yes(bool verbose) { char str[20]; + fflush(stdout); if (fgets(str, sizeof(str), stdin) == NULL) return false; @@ -1174,3 +1177,199 @@ char *printable_mkvp(enum card_type cardtype, u8 *mkvp) return mkvp_print_buf; } + +/* + * Copy the contents of one file into another file. If num_bytes is zero, + * then all content until EOF of the input file is copied. Otherwise only up to + * num_bytes is copied. + * + * @param[in] in_file_name the file name of the input file + * @param[in] out_file_name the file name of the output file + * @param[in] num_bytes the number of bytes to copy, or 0 to copy until + * EOF of the input file + * + * @returns zero for success, or a negative error in case of an error + */ +int copy_file(const char *in_file_name, const char *out_file_name, + size_t num_bytes) +{ + FILE *fp_in = NULL, *fp_out = NULL; + size_t num = 0, len; + char buff[1024]; + int rc = 0; + + fp_in = fopen(in_file_name, "r"); + if (fp_in == NULL) { + rc = -errno; + warnx("Failed to open '%s': %s", in_file_name, strerror(-rc)); + goto out; + } + + fp_out = fopen(out_file_name, "w"); + if (fp_out == NULL) { + rc = -errno; + warnx("Failed to open '%s': %s", out_file_name, strerror(-rc)); + goto out; + } + + while (!feof(fp_in) && (num_bytes == 0 || num < num_bytes)) { + len = fread(buff, 1, num_bytes == 0 ? sizeof(buff) : + MIN(num_bytes - num, sizeof(buff)), fp_in); + if (ferror(fp_in)) { + rc = -EIO; + warnx("Failed to read from '%s': %s", in_file_name, + strerror(-rc)); + break; + } + + if (len == 0) + break; + + if (fwrite(buff, len, 1, fp_out) != 1) { + rc = -errno; + warnx("Failed to write to '%s': %s", out_file_name, + strerror(-rc)); + break; + } + + num += len; + } + +out: + if (fp_in != NULL) + fclose(fp_in); + if (fp_out != NULL) + fclose(fp_out); + + return rc; +} + +/** + * Reads the passphrase from the specified file and returns the passphrase as + * an base64 encoded string. The returned string must be freed by the caller. + * + * @param[in] filename the file name of the file containing the passphrase + * @param[in] verbose if true, additional error messages are printed. + * + * @returns an allocated string + */ +char *read_passphrase_as_base64(const char *filename, bool verbose) +{ + unsigned char *ret = NULL, *buf = NULL; + int outlen, len; + struct stat sb; + FILE *fp; + + if (stat(filename, &sb) != 0) { + pr_verbose(verbose, "stat on file '%s' failed: %s", filename, + strerror(errno)); + return NULL; + } + + if (sb.st_size == 0) { + pr_verbose(verbose, "File '%s' is empty", filename); + return NULL; + } + + fp = fopen(filename, "r"); + if (fp == NULL) { + pr_verbose(verbose, "Open of file '%s' failed: %s", filename, + strerror(errno)); + return NULL; + } + + buf = malloc(sb.st_size); + if (buf == NULL) { + pr_verbose(verbose, "Malloc failed"); + goto out; + } + + if (fread(buf, sb.st_size, 1, fp) != 1) { + pr_verbose(verbose, "Reading file '%s' failed: %s", filename, + strerror(errno)); + goto out; + } + + outlen = (sb.st_size / 3) * 4; + if (sb.st_size % 3 > 0) + outlen += 4; + + ret = malloc(outlen + 1); + if (ret == NULL) { + pr_verbose(verbose, "Malloc failed"); + goto out; + } + + len = EVP_EncodeBlock(ret, buf, sb.st_size); + if (len != outlen) { + pr_verbose(verbose, "EVP_EncodeBlock failed"); + free(ret); + ret = NULL; + goto out; + } + + ret[outlen] = '\0'; + +out: + free(buf); + fclose(fp); + + return (char *)ret; +} + +/** + * Stores the passphrase into the specified file. Decodes the base64 string into + * bytes. + * + * @param[in] b64_string the passphrase as a base64 string + * @param[in] filename the file name of the file containing the passphrase + * @param[in] verbose if true, additional error messages are printed. + * + * @returns 0 for success or a negative errno in case of an error. + */ +int store_passphrase_from_base64(const char *b64_string, const char *filename, + bool verbose) +{ + size_t len, outlen, rawlen, i; + unsigned char *buf; + FILE *fp = NULL; + int rc = 0; + + len = strlen(b64_string); + rawlen = outlen = (len / 4) * 3; + for (i = len - 1; b64_string[i] == '='; i--, rawlen--) + ; + + buf = malloc(outlen); + if (buf == NULL) { + pr_verbose(verbose, "Malloc failed"); + return -ENOMEM; + } + + fp = fopen(filename, "w"); + if (fp == NULL) { + pr_verbose(verbose, "Open of file '%s' failed: %s", filename, + strerror(errno)); + rc = -EIO; + goto out; + } + + len = EVP_DecodeBlock(buf, (unsigned char *)b64_string, len); + if (len != outlen) { + pr_verbose(verbose, "EVP_DecodeBlock failed"); + goto out; + } + + if (fwrite(buf, rawlen, 1, fp) != 1) { + pr_verbose(verbose, "Writing file '%s' failed: %s", filename, + strerror(errno)); + goto out; + } + +out: + if (fp != NULL) + fclose(fp); + free(buf); + return rc; +} + diff --git a/zkey/utils.h b/zkey/utils.h index a39f367a..9334ea88 100644 --- a/zkey/utils.h +++ b/zkey/utils.h @@ -77,4 +77,11 @@ bool prompt_for_yes(bool verbose); char *printable_mkvp(enum card_type cardtype, u8 *mkvp); +int copy_file(const char *in_file_name, const char *out_file_name, + size_t num_bytes); + +char *read_passphrase_as_base64(const char *filename, bool verbose); +int store_passphrase_from_base64(const char *hex_string, const char *filename, + bool verbose); + #endif diff --git a/zkey/zkey.1 b/zkey/zkey.1 index 0b398c2d..ddffdd27 100644 --- a/zkey/zkey.1 +++ b/zkey/zkey.1 @@ -126,6 +126,9 @@ key repository. .RB [ \-\-key-type | \-K .IR type ] .RB [ \-\-local | \-L ] +.RB [ \-\-gen\-dummy\-passphrase ] +.RB [ \-\-set\-dummy\-passphrase +.IR passphrase\-file ] .RB [ KMS\-plugin\ specific\ options ] .RB [ \-\-verbose | \-V ] .PP @@ -379,6 +382,9 @@ EP11-AES to be installed. For the supported environments and downloads, see: .IR bytes ] .RB [ \-\-volume-type | \-t .IR type ] +.RB [ \-\-gen\-dummy\-passphrase ] +.RB [ \-\-set\-dummy\-passphrase +.IR passphrase\-file ] .RB [ \-\-verbose | \-V ] . .PP @@ -515,6 +521,11 @@ because the secure key is contained in the LUKS2 header. .IR bytes ] .RB [ \-\-volume-type | \-t .IR type ] +.RB [ \-\-gen\-dummy\-passphrase ] +.RB [ \-\-set\-dummy\-passphrase +.IR passphrase\-file ] +.RB [ \-\-remove\-dummy\-passphrase ] +.RB [ \-\-force | \-F ] .RB [ \-\-verbose | \-V ] . .PP @@ -661,7 +672,7 @@ option to generate crypttab entries for the specified volume type only. For LUKS2 volumes, a passphrase is required. You are prompted for the passphrase during system startup when crypttab is evaluated, unless option .B \-\-key\-file -is specified. Option +is specified, or a dummy passphrase is associated with the secure key. Option .B \-\-tries specifies how often a passphrase can be re-entered. When option .B \-\-key\-file @@ -734,7 +745,7 @@ LUKS key slots is of less relevance. For LUKS2 volumes, a passphrase is required. You are prompted for the passphrase when running the generated commands, unless option .B \-\-key\-file -is specified. Option +is specified, or a dummy passphrase is associated with the secure key. Option .B \-\-tries specifies how often a passphrase can be re-entered. When option .B \-\-key\-file @@ -1236,6 +1247,26 @@ system plugin (KMS plugin) is bound to the secure key repository. If the repository is bound to a key management system plugin, then keys are generated using the key management system by default. .TP +.BR \-\-gen\-dummy\-passphrase +Generate a dummy passphrase randomly and associate it with the secure AES key +used to encrypt LUKS2 volume(s). The LUKS2 passphrase is of less or no relevance +for the security of the volume(s), when an secure AES key is used to encrypt the +volume(s), and can therefore be stored insecurely inside the secure key +repository. If for a certain usage the passphrase is of relevance for +security, then do not use this option. This option can only be specified for +keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP +Set a dummy passphrase that is read from the specified file and associate it +with the secure AES key used to encrypt LUKS2 volume(s). The LUKS2 passphrase +is of less or no relevance for the security of the volume(s), when an secure +AES key is used to encrypt the volume(s), and can therefore be stored insecurely +inside the secure key repository. If for a certain usage the passphrase is of +relevance for security, then do not use this option. This option can only be +specified for keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. +.TP .B KMS-plugin specific options A key management system plugin may offer and even require plugin specific options that can be specified with the generate command when the secure key @@ -1372,6 +1403,26 @@ This option is only available if has been compiled with LUKS2 support enabled. If LUKS2 support is not enabled, the default volume type is \fBplain\fP. This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-gen\-dummy\-passphrase +Generate a dummy passphrase randomly and associate it with the secure AES key +used to encrypt LUKS2 volume(s). The LUKS2 passphrase is of less or no relevance +for the security of the volume(s), when an secure AES key is used to encrypt the +volume(s), and can therefore be stored insecurely inside the secure key +repository. If for a certain usage the passphrase is of relevance for +security, then do not use this option. This option can only be specified for +keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP +Set a dummy passphrase that is read from the specified file and associate it +with the secure AES key used to encrypt LUKS2 volume(s). The LUKS2 passphrase +is of less or no relevance for the security of the volume(s), when an secure +AES key is used to encrypt the volume(s), and can therefore be stored insecurely +inside the secure key repository. If for a certain usage the passphrase is of +relevance for security, then do not use this option. This option can only be +specified for keys with a volume type of \fBluks2\fP. +This option is only used for secure keys contained in the secure key repository. . . . @@ -1523,6 +1574,47 @@ This option is only available if .B zkey has been compiled with LUKS2 support enabled. This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-gen\-dummy\-passphrase +Generate a dummy passphrase randomly and associate it with the secure AES key +used to encrypt LUKS2 volume(s). The LUKS2 passphrase is of less or no relevance +for the security of the volume(s), when an secure AES key is used to encrypt the +volume(s), and can therefore be stored insecurely inside the secure key +repository. If for a certain usage the passphrase is of relevance for +security, then do not use this option. This option can only be specified for +keys with a volume type of \fBluks2\fP. When there is already a dummy +passphrase associated with the key, you muts first remove the dummy passphrase +with option \fB\-\-remove\-dummy\-passphrase\fP before you can associate a new +dummy passphrase. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-set\-dummy\-passphrase\~\fIpassphrase\-file\fP +Set a dummy passphrase that is read from the specified file and associate it +with the secure AES key used to encrypt LUKS2 volume(s). The LUKS2 passphrase +is of less or no relevance for the security of the volume(s), when an secure +AES key is used to encrypt the volume(s), and can therefore be stored insecurely +inside the secure key repository. If for a certain usage the passphrase is of +relevance for security, then do not use this option. This option can only be +specified for keys with a volume type of \fBluks2\fP. When there is already a +dummy passphrase associated with the key, you muts first remove the dummy +passphrase with option \fB\-\-remove\-dummy\-passphrase\fP before you can +associate a new dummy passphrase. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-\-remove\-dummy\-passphrase +Remove the associated dummy passphrase used with LUKS2 volume(s). +The user is prompted to confirm the removal of the associated dummy passphrase. +Use the \fB\-\-force\fP option to remove the associated dummy passphrase without +prompting for a confirmation. +This option is only used for secure keys contained in the secure key repository. +.TP +.BR \-F ", " \-\-force\fP +The user is prompted to confirm the removal of the associated dummy passphrase. +Use this option to remove the associated dummy passphrase without prompting for +a confirmation. +This option is only used for secure keys contained in the secure key repository, +and can only be specified together with option +\fB\-\-remove\-dummy\-passphrase\fP. . . . @@ -1590,8 +1682,9 @@ has been compiled with LUKS2 support enabled. This option is only used for secure keys contained in the secure key repository. .TP .BR \-\-key\-file\~\fIfile\-name\fP -Reads the passphrase from the specified file. If this option is omitted, then -you are prompted to enter the passphrase interactively during system startup. +Reads the passphrase from the specified file. If this option is omitted, and +no dummy passphrase is associated with the secure key, then you are prompted to +enter the passphrase interactively during system startup. This option is passed to the generated crypttab entries for LUKS2 volumes, and is only available if .B zkey @@ -1677,10 +1770,11 @@ option, and is only available if has been compiled with LUKS2 support enabled. .TP .BR \-\-key\-file\~\fIfile\-name\fP -Reads the passphrase from the specified file. If this option is omitted, -or if the file\-name is \fI-\fP (a dash), then you are prompted to enter the -passphrase interactively. This option is passed to the generated command(s) -for LUKS2 volumes, and is only available if +Reads the passphrase from the specified file. If this option is omitted, and +no dummy passphrase is associated with the secure key, or if the file\-name is +\fI-\fP (a dash), then you are prompted to enter the passphrase interactively. +This option is passed to the generated command(s) for LUKS2 volumes, and is +only available if .B zkey has been compiled with LUKS2 support enabled. .TP diff --git a/zkey/zkey.c b/zkey/zkey.c index e22d7ead..c882da22 100644 --- a/zkey/zkey.c +++ b/zkey/zkey.c @@ -79,6 +79,9 @@ static struct zkey_globals { char *key_type; char *label; bool local; + bool gen_passphrase; + char *passphrase_file; + bool remove_passphrase; bool kms_bound; bool run; bool batch_mode; @@ -156,6 +159,9 @@ static struct zkey_globals { #define OPT_NO_APQN_CHECK 262 #define OPT_NO_VOLUME_CHECK 263 #define OPT_REFRESH_PROPERTIES 264 +#define OPT_GEN_DUMMY_PASSPHRASE 265 +#define OPT_SET_DUMMY_PASSPHRASE 266 +#define OPT_REMOVE_DUMMY_PASSPHRASE 267 /* * Configuration of command line options @@ -271,6 +277,31 @@ static struct util_opt opt_vec[] = { "generated by the KMS per default.", .command = COMMAND_GENERATE, }, + { + .option = { "gen-dummy-passphrase", 0, NULL, + OPT_GEN_DUMMY_PASSPHRASE}, + .desc = "Generate a dummy passphrase and associate it with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_GENERATE, + }, + { + .option = { "set-dummy-passphrase", required_argument, NULL, + OPT_SET_DUMMY_PASSPHRASE}, + .argument = "passphrase-file", + .desc = "Set a dummy passphrase to be associated with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_GENERATE, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -429,6 +460,31 @@ static struct util_opt opt_vec[] = { .command = COMMAND_IMPORT, }, #endif + { + .option = { "gen-dummy-passphrase", 0, NULL, + OPT_GEN_DUMMY_PASSPHRASE}, + .desc = "Generate a dummy passphrase and associate it with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_IMPORT, + }, + { + .option = { "set-dummy-passphrase", required_argument, NULL, + OPT_SET_DUMMY_PASSPHRASE}, + .argument = "passphrase-file", + .desc = "Set a dummy passphrase to be associated with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_IMPORT, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -593,6 +649,45 @@ static struct util_opt opt_vec[] = { .command = COMMAND_CHANGE, }, #endif + { + .option = { "gen-dummy-passphrase", 0, NULL, + OPT_GEN_DUMMY_PASSPHRASE}, + .desc = "Generate a dummy passphrase and associate it with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_CHANGE, + }, + { + .option = { "set-dummy-passphrase", required_argument, NULL, + OPT_SET_DUMMY_PASSPHRASE}, + .argument = "passphrase-file", + .desc = "Set a dummy passphrase to be associated with the " + "secure AES key used to encrypt LUKS2 volume(s). The " + "LUKS2 passphrase is of less or no relevance for the " + "security of the volume(s), when an secure AES key is " + "used to encrypt the volume(s), and can therefore be " + "stored insecurely inside the secure key repository.", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_CHANGE, + }, + { + .option = { "remove-dummy-passphrase", 0, NULL, + OPT_REMOVE_DUMMY_PASSPHRASE}, + .desc = "Remove an associated dummy passphrase used with LUKS2 " + "volume(s).", + .flags = UTIL_OPT_FLAG_NOSHORT, + .command = COMMAND_CHANGE, + }, + { + .option = {"force", 0, NULL, 'F'}, + .desc = "Do not prompt for a confirmation when removing an " + "associated dummy passphrase", + .command = COMMAND_CHANGE, + }, /***********************************************************/ { .flags = UTIL_OPT_FLAG_SECTION, @@ -1631,6 +1726,14 @@ static int command_generate_repository(void) if (g.sector_size < 0) g.sector_size = 0; + if (g.gen_passphrase && g.passphrase_file != NULL) { + warnx("Either '--gen-dummy-passphrase' or " + "'--set-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.kms_info.plugin_lib != NULL && !g.local) { if (g.apqns != NULL) { warnx("Option '--apqns|-a' is not valid for " @@ -1655,6 +1758,8 @@ static int command_generate_repository(void) g.description, g.volumes, g.sector_size, g.keybits, g.xts, g.volume_type, g.key_type, + g.gen_passphrase, + g.passphrase_file, g.kms_options, g.num_kms_options); goto out; @@ -1666,7 +1771,8 @@ static int command_generate_repository(void) rc = keystore_generate_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, g.keybits, g.xts, g.clearkeyfile, - g.volume_type, g.key_type, g.pkey_fd); + g.volume_type, g.key_type, g.gen_passphrase, + g.passphrase_file, g.pkey_fd); out: return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; @@ -1729,6 +1835,18 @@ static int command_generate(void) util_prg_print_parse_error(); return EXIT_FAILURE; } + if (g.gen_passphrase) { + warnx("Option '--gen-dummy-passphrase' is not valid " + "for generating a key outside of the repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.passphrase_file != NULL) { + warnx("Option '--sen-dummy-passphrase' is not valid " + "for generating a key outside of the repository"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } rc = cross_check_apqns(NULL, NULL, get_min_card_level_for_keytype(g.key_type), @@ -2111,10 +2229,19 @@ static int command_import(void) util_prg_print_parse_error(); return EXIT_FAILURE; } + if (g.gen_passphrase && g.passphrase_file != NULL) { + warnx("Either '--gen-dummy-passphrase' or " + "'--set-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + rc = keystore_import_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, - g.pos_arg, g.volume_type, &g.lib); + g.pos_arg, g.volume_type, g.gen_passphrase, + g.passphrase_file, &g.lib); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -2200,10 +2327,39 @@ static int command_change(void) util_prg_print_parse_error(); return EXIT_FAILURE; } + if (g.gen_passphrase && g.passphrase_file != NULL) { + warnx("Either '--gen-dummy-passphrase' or " + "'--set-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.gen_passphrase && g.remove_passphrase) { + warnx("Either '--gen-dummy-passphrase' or " + "'--remove-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.passphrase_file != NULL && g.remove_passphrase) { + warnx("Either '--set-dummy-passphrase' or " + "'--remove-dummy-passphrase' can be specified, but not " + "both"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } + if (g.force && !g.remove_passphrase) { + warnx("Option '--force|-F' is only valid together with " + "the '--remove-dummy-passphrase' option"); + util_prg_print_parse_error(); + return EXIT_FAILURE; + } rc = keystore_change_key(g.keystore, g.name, g.description, g.volumes, g.apqns, g.noapqncheck, g.sector_size, - g.volume_type); + g.volume_type, g.gen_passphrase, + g.passphrase_file, g.remove_passphrase, + g.force); return rc != 0 ? EXIT_FAILURE : EXIT_SUCCESS; } @@ -3056,6 +3212,15 @@ int main(int argc, char *argv[]) case 'P': g.refresh_properties = 1; break; + case OPT_GEN_DUMMY_PASSPHRASE: + g.gen_passphrase = 1; + break; + case OPT_SET_DUMMY_PASSPHRASE: + g.passphrase_file = optarg; + break; + case OPT_REMOVE_DUMMY_PASSPHRASE: + g.remove_passphrase = 1; + break; case 'h': print_help(command, sub_command); return EXIT_SUCCESS;