diff --git a/docs/libblockdev-sections.txt b/docs/libblockdev-sections.txt index 2a62a1b7f..c134c079b 100644 --- a/docs/libblockdev-sections.txt +++ b/docs/libblockdev-sections.txt @@ -743,6 +743,14 @@ bd_s390_zfcp_sanitize_lun_input bd_s390_zfcp_online bd_s390_zfcp_scsi_offline bd_s390_zfcp_offline +bd_s390_zkey_generate +BDS390ZkeyInfo +bd_s390_zkey_info_copy +bd_s390_zkey_info_free +bd_s390_zkey_list +bd_s390_zkey_remove +bd_s390_zkey_cryptsetup_setvp +bd_s390_zkey_cryptsetup_validate BDS390Tech BDS390TechMode bd_s390_is_tech_avail diff --git a/misc/libblockdev-tasks.yml b/misc/libblockdev-tasks.yml index aa077f85b..dc1b3a8e7 100644 --- a/misc/libblockdev-tasks.yml +++ b/misc/libblockdev-tasks.yml @@ -69,6 +69,14 @@ - xfsprogs when: ansible_facts['distribution'] == 'Fedora' and test_dependencies|bool +- name: Install s390utils (Fedora) + package: + state: present + name: + - s390utils + when: ansible_facts['distribution'] == 'Fedora' and ansible_facts['architecture'] == 's390x' and test_dependencies|bool + + ####### CentOS - name: Install basic build tools (CentOS) package: @@ -128,6 +136,13 @@ - xfsprogs when: ansible_facts['distribution'] == 'CentOS' and test_dependencies|bool +- name: Install s390utils (CentOS) + package: + state: present + name: + - s390utils + when: ansible_facts['distribution'] == 'CentOS' and ansible_facts['architecture'] == 's390x' and test_dependencies|bool + - name: Install pylint using pip (CentOS) pip: name: ['pylint'] diff --git a/src/lib/plugin_apis/s390.api b/src/lib/plugin_apis/s390.api index ec8242ae6..7b48517ed 100644 --- a/src/lib/plugin_apis/s390.api +++ b/src/lib/plugin_apis/s390.api @@ -15,16 +15,19 @@ typedef enum { BD_S390_ERROR_FORMAT_FAILED, BD_S390_ERROR_DASDFMT, BD_S390_ERROR_IO, + BD_S390_ERROR_ZKEY, } BDS390Error; typedef enum { BD_S390_TECH_DASD = 0, BD_S390_TECH_ZFCP, + BD_S390_TECH_PAES, } BDS390Tech; typedef enum { BD_S390_TECH_MODE_MODIFY = 1 << 0, BD_S390_TECH_MODE_QUERY = 1 << 1, + BD_S390_TECH_MODE_CREATE = 1 << 2, } BDS390TechMode; /** @@ -175,4 +178,189 @@ gboolean bd_s390_zfcp_scsi_offline(const gchar *devno, const gchar *wwpn, const */ gboolean bd_s390_zfcp_offline (const gchar *devno, const gchar *wwpn, const gchar *lun, GError **error); +/** + * bd_s390_zkey_generate: + * @name: name of the secure key to generate in the secure key repository + * @key_type: (nullable): type of the secure key to generate (e.g. "CCA-AESCIPHER") or %NULL for the zkey default + * @keybits: size of the key in bits or 0 for the zkey default + * @volumes: (nullable) (array zero-terminated=1): list of volumes to associate with the key, each given + * in the "volume:dmname" format, or %NULL + * @apqns: (nullable) (array zero-terminated=1): list of cryptographic adapters (APQNs) to associate with + * the key, each given in the "card.domain" format, or %NULL + * @sector_size: sector size in bytes to use with dm-crypt or 0 for the system default + * @dummy_passphrase: whether to generate and associate a dummy passphrase with the key + * @extra: (nullable) (array zero-terminated=1): extra options for the key generation (right now + * passed to the 'zkey' utility) + * @error: (out) (optional): place to store error (if any) + * + * Generates a new secure key for pervasive encryption using the 'zkey' utility and stores it + * in the secure key repository. The key is generated as an XTS key with the 'LUKS2' volume type. + * + * Returns: whether the secure key was successfully generated or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_CREATE + */ +gboolean bd_s390_zkey_generate (const gchar *name, const gchar *key_type, guint64 keybits, const gchar **volumes, const gchar **apqns, guint64 sector_size, gboolean dummy_passphrase, const BDExtraArg **extra, GError **error); + +#define BD_S390_TYPE_ZKEY_INFO (bd_s390_zkey_info_get_type ()) +GType bd_s390_zkey_info_get_type(); + +/** + * BDS390ZkeyInfo: + * @name: name of the secure key in the secure key repository; + * @description: user provided description of the key (empty if none was set); + * @secure_key_size: size of the secure key in bytes; + * @clear_key_size: size of the effective (clear) key in bits; + * @xts: whether the key is an XTS type key or not; + * @key_type: type of the secure key (e.g. "CCA-AESCIPHER"); + * @volumes: (array zero-terminated=1): volumes associated with the key, each in the "volume:dmname" format; + * @apqns: (array zero-terminated=1): cryptographic adapters (APQNs) associated with the key, each in the "card.domain" format; + * @key_file_name: full path to the file holding the secure key; + * @sector_size: sector size in bytes to use with dm-crypt or 0 for the system default; + * @volume_type: volume type the key is to be used with (e.g. "LUKS2"); + * @dummy_passphrase: (nullable): path to the dummy passphrase file associated with the key or %NULL if none is set; + */ +typedef struct BDS390ZkeyInfo { + gchar *name; + gchar *description; + guint64 secure_key_size; + guint64 clear_key_size; + gboolean xts; + gchar *key_type; + gchar **volumes; + gchar **apqns; + gchar *key_file_name; + guint64 sector_size; + gchar *volume_type; + gchar *dummy_passphrase; +} BDS390ZkeyInfo; + +/** + * bd_s390_zkey_info_free: (skip) + * @info: (nullable): %BDS390ZkeyInfo to free + * + * Frees @info. + */ +void bd_s390_zkey_info_free (BDS390ZkeyInfo *info) { + if (info == NULL) + return; + + g_free (info->name); + g_free (info->description); + g_free (info->key_type); + g_strfreev (info->volumes); + g_strfreev (info->apqns); + g_free (info->key_file_name); + g_free (info->volume_type); + g_free (info->dummy_passphrase); + g_free (info); +} + +/** + * bd_s390_zkey_info_copy: (skip) + * @info: (nullable): %BDS390ZkeyInfo to copy + * + * Creates a new copy of @info. + */ +BDS390ZkeyInfo* bd_s390_zkey_info_copy (BDS390ZkeyInfo *info) { + if (info == NULL) + return NULL; + + BDS390ZkeyInfo *new_info = g_new0 (BDS390ZkeyInfo, 1); + + new_info->name = g_strdup (info->name); + new_info->description = g_strdup (info->description); + new_info->secure_key_size = info->secure_key_size; + new_info->clear_key_size = info->clear_key_size; + new_info->xts = info->xts; + new_info->key_type = g_strdup (info->key_type); + new_info->volumes = g_strdupv (info->volumes); + new_info->apqns = g_strdupv (info->apqns); + new_info->key_file_name = g_strdup (info->key_file_name); + new_info->sector_size = info->sector_size; + new_info->volume_type = g_strdup (info->volume_type); + new_info->dummy_passphrase = g_strdup (info->dummy_passphrase); + + return new_info; +} + +GType bd_s390_zkey_info_get_type () { + static GType type = 0; + + if (G_UNLIKELY(type == 0)) { + type = g_boxed_type_register_static("BDS390ZkeyInfo", + (GBoxedCopyFunc) bd_s390_zkey_info_copy, + (GBoxedFreeFunc) bd_s390_zkey_info_free); + } + + return type; +} + +/** + * bd_s390_zkey_list: + * @name: (nullable): name of a single secure key to get information about or %NULL to list all keys + * @error: (out) (optional): place to store error (if any) + * + * Lists the secure keys stored in the secure key repository using the 'zkey' utility. If @name + * is given, only the information about the matching key is returned. + * + * Returns: (array zero-terminated=1) (transfer full): information about the secure keys in the + * repository (an empty list if there are none) or %NULL in case of error + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_QUERY + */ +BDS390ZkeyInfo** bd_s390_zkey_list (const gchar *name, GError **error); + +/** + * bd_s390_zkey_remove: + * @name: name of the secure key to remove from the secure key repository + * @error: (out) (optional): place to store error (if any) + * + * Removes the secure key @name from the secure key repository using the 'zkey' utility. + * + * Returns: whether the secure key was successfully removed or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_MODIFY + */ +gboolean bd_s390_zkey_remove (const gchar *name, GError **error); + +/** + * bd_s390_zkey_cryptsetup_setvp: + * @device: LUKS2 device to set the verification pattern on + * @key_file: path to a file containing the LUKS passphrase used to unlock a keyslot of @device + * @extra: (nullable) (array zero-terminated=1): extra options for setting the verification pattern + * (right now passed to the 'zkey-cryptsetup' utility) + * @error: (out) (optional): place to store error (if any) + * + * Sets the verification pattern of the secure key in the metadata of the LUKS2 volume @device + * using the 'zkey-cryptsetup' utility. The verification pattern is used to identify the secure + * key associated with the volume. Setting the verification pattern requires unlocking a keyslot + * of @device, so the LUKS passphrase needs to be provided in @key_file (this is a passphrase + * file, not the secure key file). + * + * Returns: whether the verification pattern was successfully set or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_MODIFY + */ +gboolean bd_s390_zkey_cryptsetup_setvp (const gchar *device, const gchar *key_file, const BDExtraArg **extra, GError **error); + +/** + * bd_s390_zkey_cryptsetup_validate: + * @device: LUKS2 device to validate + * @key_file: path to a file containing the LUKS passphrase used to unlock a keyslot of @device + * @extra: (nullable) (array zero-terminated=1): extra options for the validation + * (right now passed to the 'zkey-cryptsetup' utility) + * @error: (out) (optional): place to store error (if any) + * + * Validates that the LUKS2 volume @device is correctly set up for pervasive encryption using the + * 'zkey-cryptsetup' utility. Validation requires unlocking a keyslot of @device, so the LUKS + * passphrase needs to be provided in @key_file (this is a passphrase file, not the secure key + * file). + * + * Returns: whether the LUKS2 volume @device is correctly set up for pervasive encryption or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_QUERY + */ +gboolean bd_s390_zkey_cryptsetup_validate (const gchar *device, const gchar *key_file, const BDExtraArg **extra, GError **error); + #endif /* BD_S390_API */ diff --git a/src/plugins/s390.c b/src/plugins/s390.c index 97f831da8..12ed06c2f 100644 --- a/src/plugins/s390.c +++ b/src/plugins/s390.c @@ -53,11 +53,17 @@ static GMutex deps_check_lock; #define DEPS_DASDFMT 0 #define DEPS_DASDFMT_MASK (1 << DEPS_DASDFMT) -#define DEPS_LAST 1 +#define DEPS_ZKEY 1 +#define DEPS_ZKEY_MASK (1 << DEPS_ZKEY) +#define DEPS_ZKEY_CRYPTSETUP 2 +#define DEPS_ZKEY_CRYPTSETUP_MASK (1 << DEPS_ZKEY_CRYPTSETUP) +#define DEPS_LAST 3 static const UtilDep deps[DEPS_LAST] = { /* dasdfmt doesn't return version info */ {"dasdfmt", NULL, NULL, NULL}, + {"zkey", NULL, NULL, NULL}, + {"zkey-cryptsetup", NULL, NULL, NULL}, }; @@ -104,6 +110,15 @@ gboolean bd_s390_is_tech_avail (BDS390Tech tech, guint64 mode, GError **error) { return check_deps (&avail_deps, DEPS_DASDFMT_MASK, deps, DEPS_LAST, &deps_check_lock, error); else return TRUE; + case BD_S390_TECH_PAES: { + /* pervasive encryption support always requires the 'zkey' utility; querying and + modifying the pervasive encryption setup of an existing LUKS2 volume additionally + requires the 'zkey-cryptsetup' utility */ + guint req_deps = DEPS_ZKEY_MASK; + if (mode & (BD_S390_TECH_MODE_QUERY | BD_S390_TECH_MODE_MODIFY)) + req_deps |= DEPS_ZKEY_CRYPTSETUP_MASK; + return check_deps (&avail_deps, req_deps, deps, DEPS_LAST, &deps_check_lock, error); + } default: g_set_error_literal (error, BD_S390_ERROR, BD_S390_ERROR_TECH_UNAVAIL, "Unknown technology"); return FALSE; @@ -1055,3 +1070,395 @@ gboolean bd_s390_zfcp_offline (const gchar *devno, const gchar *wwpn, const gcha bd_utils_report_finished (progress_id, "Completed"); return TRUE; } + +/** + * bd_s390_zkey_generate: + * @name: name of the secure key to generate in the secure key repository + * @key_type: (nullable): type of the secure key to generate (e.g. "CCA-AESCIPHER") or %NULL for the zkey default + * @keybits: size of the key in bits or 0 for the zkey default + * @volumes: (nullable) (array zero-terminated=1): list of volumes to associate with the key, each given + * in the "volume:dmname" format, or %NULL + * @apqns: (nullable) (array zero-terminated=1): list of cryptographic adapters (APQNs) to associate with + * the key, each given in the "card.domain" format, or %NULL + * @sector_size: sector size in bytes to use with dm-crypt or 0 for the system default + * @dummy_passphrase: whether to generate and associate a dummy passphrase with the key + * @error: (out) (optional): place to store error (if any) + * + * Generates a new secure key for pervasive encryption using the 'zkey' utility and stores it + * in the secure key repository. The key is generated as an XTS key with the 'LUKS2' volume type. + * + * Returns: whether the secure key was successfully generated or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_CREATE + */ +gboolean bd_s390_zkey_generate (const gchar *name, const gchar *key_type, guint64 keybits, const gchar **volumes, const gchar **apqns, guint64 sector_size, gboolean dummy_passphrase, const BDExtraArg **extra, GError **error) { + gboolean success = FALSE; + GPtrArray *argv = NULL; + + if (!check_deps (&avail_deps, DEPS_ZKEY_MASK, deps, DEPS_LAST, &deps_check_lock, error)) + return FALSE; + + if (name == NULL || *name == '\0') { + g_set_error_literal (error, BD_S390_ERROR, BD_S390_ERROR_ZKEY, + "Key name must be specified"); + return FALSE; + } + + argv = g_ptr_array_new_with_free_func (g_free); + g_ptr_array_add (argv, g_strdup ("zkey")); + g_ptr_array_add (argv, g_strdup ("generate")); + g_ptr_array_add (argv, g_strdup ("--name")); + g_ptr_array_add (argv, g_strdup (name)); + + if (key_type != NULL) { + g_ptr_array_add (argv, g_strdup ("--key-type")); + g_ptr_array_add (argv, g_strdup (key_type)); + } + if (keybits != 0) { + g_ptr_array_add (argv, g_strdup ("--keybits")); + g_ptr_array_add (argv, g_strdup_printf ("%"G_GUINT64_FORMAT, keybits)); + } + if (volumes != NULL && *volumes != NULL) { + /* zkey expects a single comma-separated list */ + g_ptr_array_add (argv, g_strdup ("--volumes")); + g_ptr_array_add (argv, g_strjoinv (",", (gchar **) volumes)); + } + if (apqns != NULL && *apqns != NULL) { + /* zkey expects a single comma-separated list */ + g_ptr_array_add (argv, g_strdup ("--apqns")); + g_ptr_array_add (argv, g_strjoinv (",", (gchar **) apqns)); + } + if (sector_size != 0) { + g_ptr_array_add (argv, g_strdup ("--sector-size")); + g_ptr_array_add (argv, g_strdup_printf ("%"G_GUINT64_FORMAT, sector_size)); + } + if (dummy_passphrase) + g_ptr_array_add (argv, g_strdup ("--gen-dummy-passphrase")); + + /* pervasive encryption of LUKS2 volumes always uses XTS secure keys */ + g_ptr_array_add (argv, g_strdup ("--xts")); + g_ptr_array_add (argv, g_strdup ("--volume-type")); + g_ptr_array_add (argv, g_strdup ("LUKS2")); + + g_ptr_array_add (argv, NULL); + + success = bd_utils_exec_and_report_error ((const gchar **) argv->pdata, extra, error); + g_ptr_array_free (argv, TRUE); + + return success; +} + +/** + * bd_s390_zkey_info_free: (skip) + * @info: (nullable): %BDS390ZkeyInfo to free + * + * Frees @info. + */ +void bd_s390_zkey_info_free (BDS390ZkeyInfo *info) { + if (info == NULL) + return; + + g_free (info->name); + g_free (info->description); + g_free (info->key_type); + g_strfreev (info->volumes); + g_strfreev (info->apqns); + g_free (info->key_file_name); + g_free (info->volume_type); + g_free (info->dummy_passphrase); + g_free (info); +} + +/** + * bd_s390_zkey_info_copy: (skip) + * @info: (nullable): %BDS390ZkeyInfo to copy + * + * Creates a new copy of @info. + */ +BDS390ZkeyInfo* bd_s390_zkey_info_copy (BDS390ZkeyInfo *info) { + if (info == NULL) + return NULL; + + BDS390ZkeyInfo *new_info = g_new0 (BDS390ZkeyInfo, 1); + + new_info->name = g_strdup (info->name); + new_info->description = g_strdup (info->description); + new_info->secure_key_size = info->secure_key_size; + new_info->clear_key_size = info->clear_key_size; + new_info->xts = info->xts; + new_info->key_type = g_strdup (info->key_type); + new_info->volumes = g_strdupv (info->volumes); + new_info->apqns = g_strdupv (info->apqns); + new_info->key_file_name = g_strdup (info->key_file_name); + new_info->sector_size = info->sector_size; + new_info->volume_type = g_strdup (info->volume_type); + new_info->dummy_passphrase = g_strdup (info->dummy_passphrase); + + return new_info; +} + +/** + * bd_s390_zkey_list: + * @name: (nullable): name of a single secure key to get information about or %NULL to list all keys + * @error: (out) (optional): place to store error (if any) + * + * Lists the secure keys stored in the secure key repository using the 'zkey' utility. If @name + * is given, only the information about the matching key is returned. + * + * Returns: (array zero-terminated=1) (transfer full): information about the secure keys in the + * repository (an empty list if there are none) or %NULL in case of error + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_QUERY + */ +BDS390ZkeyInfo** bd_s390_zkey_list (const gchar *name, GError **error) { + const gchar *argv[5] = {"zkey", "list", NULL, NULL, NULL}; + guint next = 2; + gchar *output = NULL; + gchar *stderr_data = NULL; + gint status = 0; + gboolean success = FALSE; + GPtrArray *keys = NULL; + gchar **lines = NULL; + BDS390ZkeyInfo *cur_info = NULL; + GPtrArray *cur_list = NULL; /* accumulates a multi-line list value (Volumes/APQNs) */ + gchar ***cur_list_dest = NULL; /* struct member to store the finalized NULL-terminated array in */ + gint value_col = -1; /* column (0-based) where field values start, learned from field lines */ + + if (!check_deps (&avail_deps, DEPS_ZKEY_MASK, deps, DEPS_LAST, &deps_check_lock, error)) + return NULL; + + if (name != NULL && *name != '\0') { + argv[next++] = "--name"; + argv[next++] = name; + } + + /* not using bd_utils_exec_and_capture_output because it treats an empty output + (i.e. no keys in the repository) as an error */ + success = bd_utils_exec_and_capture_output_no_progress (argv, NULL, &output, &stderr_data, &status, error); + if (!success) { + g_free (output); + g_free (stderr_data); + return NULL; + } + if (status != 0) { + g_set_error (error, BD_S390_ERROR, BD_S390_ERROR_ZKEY, + "Failed to list secure keys: %s", stderr_data ? stderr_data : ""); + g_free (output); + g_free (stderr_data); + return NULL; + } + g_free (stderr_data); + + keys = g_ptr_array_new (); + + lines = g_strsplit (output ? output : "", "\n", -1); + g_free (output); + + for (gchar **line_p = lines; *line_p != NULL; line_p++) { + gchar *line = *line_p; + gsize indent = strspn (line, " \t"); + gchar *colon = NULL; + gchar *label = NULL; + gchar *value = NULL; + + /* A multi-value field (Volumes/APQNs) can span several lines: the first item + follows the label, any further items appear on continuation lines that carry + no label and are indented to the value column. Such continuation lines cannot + be recognized by the absence of a colon -- a volume is formatted as + "volume:dmname" and thus contains one -- so they are detected by indentation + instead: everything left of the value column is whitespace. */ + if (cur_list != NULL && value_col >= 0 && (gint) indent >= value_col) { + gchar *item = g_strstrip (g_strdup (line + indent)); + if (*item != '\0') + g_ptr_array_add (cur_list, item); + else + g_free (item); + continue; + } + + /* any other line terminates the current multi-value field */ + if (cur_list != NULL) { + g_ptr_array_add (cur_list, NULL); + *cur_list_dest = (gchar **) g_ptr_array_free (cur_list, FALSE); + cur_list = NULL; + cur_list_dest = NULL; + } + + /* split the line on the first ':' -- the label never contains a colon; lines + without a colon are separators or blank lines */ + colon = strchr (line, ':'); + if (colon == NULL) + continue; + + /* "label : value" -- remember where values start so the continuation lines of + the next multi-value field can be recognized */ + value_col = (gint) (colon - line) + 2; + + label = g_strndup (line, colon - line); + label = g_strstrip (label); + value = g_strdup (colon + 1); + value = g_strstrip (value); + + if (g_strcmp0 (label, "Key") == 0) { + /* the "Key" line starts a new key record */ + cur_info = g_new0 (BDS390ZkeyInfo, 1); + cur_info->name = g_strdup (value); + g_ptr_array_add (keys, cur_info); + } else if (cur_info == NULL) { + /* a field line before any "Key" line -- ignore it */ + } else if (g_strcmp0 (label, "Description") == 0) { + cur_info->description = g_strdup (value); + } else if (g_strcmp0 (label, "Secure key size") == 0) { + cur_info->secure_key_size = g_ascii_strtoull (value, NULL, 0); + } else if (g_strcmp0 (label, "Clear key size") == 0) { + cur_info->clear_key_size = g_ascii_strtoull (value, NULL, 0); + } else if (g_strcmp0 (label, "XTS type key") == 0) { + cur_info->xts = (g_ascii_strcasecmp (value, "Yes") == 0); + } else if (g_strcmp0 (label, "Key type") == 0) { + cur_info->key_type = g_strdup (value); + } else if (g_strcmp0 (label, "Volumes") == 0) { + /* the value continues on the following continuation lines (one volume each) */ + cur_list = g_ptr_array_new (); + cur_list_dest = &cur_info->volumes; + if (*value != '\0') + g_ptr_array_add (cur_list, g_strdup (value)); + } else if (g_strcmp0 (label, "APQNs") == 0) { + /* the value continues on the following continuation lines (one APQN each) */ + cur_list = g_ptr_array_new (); + cur_list_dest = &cur_info->apqns; + if (*value != '\0') + g_ptr_array_add (cur_list, g_strdup (value)); + } else if (g_strcmp0 (label, "Key file name") == 0) { + cur_info->key_file_name = g_strdup (value); + } else if (g_strcmp0 (label, "Sector size") == 0) { + /* "(system default)" is reported as 0 */ + cur_info->sector_size = g_ascii_strtoull (value, NULL, 0); + } else if (g_strcmp0 (label, "Volume type") == 0) { + cur_info->volume_type = g_strdup (value); + } else if (g_strcmp0 (label, "Dummy passphrase") == 0) { + /* "(none)" means no dummy passphrase is set, otherwise it is a path to the passphrase file */ + if (g_strcmp0 (value, "(none)") != 0) + cur_info->dummy_passphrase = g_strdup (value); + } + + g_free (label); + g_free (value); + } + + /* finalize a multi-value field left open at the end of the output */ + if (cur_list != NULL) { + g_ptr_array_add (cur_list, NULL); + *cur_list_dest = (gchar **) g_ptr_array_free (cur_list, FALSE); + cur_list = NULL; + cur_list_dest = NULL; + } + + g_strfreev (lines); + + g_ptr_array_add (keys, NULL); + return (BDS390ZkeyInfo **) g_ptr_array_free (keys, FALSE); +} + +/** + * bd_s390_zkey_remove: + * @name: name of the secure key to remove from the secure key repository + * @error: (out) (optional): place to store error (if any) + * + * Removes the secure key @name from the secure key repository using the 'zkey' utility. + * + * Returns: whether the secure key was successfully removed or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_MODIFY + */ +gboolean bd_s390_zkey_remove (const gchar *name, GError **error) { + /* --force suppresses the interactive y/n confirmation */ + const gchar *argv[6] = {"zkey", "remove", "--name", name, "--force", NULL}; + + if (!check_deps (&avail_deps, DEPS_ZKEY_MASK, deps, DEPS_LAST, &deps_check_lock, error)) + return FALSE; + + if (name == NULL || *name == '\0') { + g_set_error_literal (error, BD_S390_ERROR, BD_S390_ERROR_ZKEY, + "Key name must be specified"); + return FALSE; + } + + return bd_utils_exec_and_report_error (argv, NULL, error); +} + +/** + * bd_s390_zkey_cryptsetup_setvp: + * @device: LUKS2 device to set the verification pattern on + * @key_file: path to a file containing the LUKS passphrase used to unlock a keyslot of @device + * @extra: (nullable) (array zero-terminated=1): extra options for setting the verification pattern + * (right now passed to the 'zkey-cryptsetup' utility) + * @error: (out) (optional): place to store error (if any) + * + * Sets the verification pattern of the secure key in the metadata of the LUKS2 volume @device + * using the 'zkey-cryptsetup' utility. The verification pattern is used to identify the secure + * key associated with the volume. Setting the verification pattern requires unlocking a keyslot + * of @device, so the LUKS passphrase needs to be provided in @key_file (this is a passphrase + * file, not the secure key file). + * + * Returns: whether the verification pattern was successfully set or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_MODIFY + */ +gboolean bd_s390_zkey_cryptsetup_setvp (const gchar *device, const gchar *key_file, const BDExtraArg **extra, GError **error) { + const gchar *argv[6] = {"zkey-cryptsetup", "setvp", "--key-file", key_file, device, NULL}; + + if (!check_deps (&avail_deps, DEPS_ZKEY_CRYPTSETUP_MASK, deps, DEPS_LAST, &deps_check_lock, error)) + return FALSE; + + if (device == NULL || *device == '\0') { + g_set_error_literal (error, BD_S390_ERROR, BD_S390_ERROR_ZKEY, + "Device must be specified"); + return FALSE; + } + + if (key_file == NULL || *key_file == '\0') { + g_set_error_literal (error, BD_S390_ERROR, BD_S390_ERROR_ZKEY, + "Key file must be specified"); + return FALSE; + } + + return bd_utils_exec_and_report_error (argv, extra, error); +} + +/** + * bd_s390_zkey_cryptsetup_validate: + * @device: LUKS2 device to validate + * @key_file: path to a file containing the LUKS passphrase used to unlock a keyslot of @device + * @extra: (nullable) (array zero-terminated=1): extra options for the validation + * (right now passed to the 'zkey-cryptsetup' utility) + * @error: (out) (optional): place to store error (if any) + * + * Validates that the LUKS2 volume @device is correctly set up for pervasive encryption using the + * 'zkey-cryptsetup' utility. Validation requires unlocking a keyslot of @device, so the LUKS + * passphrase needs to be provided in @key_file (this is a passphrase file, not the secure key + * file). + * + * Returns: whether the LUKS2 volume @device is correctly set up for pervasive encryption or not + * + * Tech category: %BD_S390_TECH_PAES-%BD_S390_TECH_MODE_QUERY + */ +gboolean bd_s390_zkey_cryptsetup_validate (const gchar *device, const gchar *key_file, const BDExtraArg **extra, GError **error) { + const gchar *argv[6] = {"zkey-cryptsetup", "validate", "--key-file", key_file, device, NULL}; + + if (!check_deps (&avail_deps, DEPS_ZKEY_CRYPTSETUP_MASK, deps, DEPS_LAST, &deps_check_lock, error)) + return FALSE; + + if (device == NULL || *device == '\0') { + g_set_error_literal (error, BD_S390_ERROR, BD_S390_ERROR_ZKEY, + "Device must be specified"); + return FALSE; + } + + if (key_file == NULL || *key_file == '\0') { + g_set_error_literal (error, BD_S390_ERROR, BD_S390_ERROR_ZKEY, + "Key file must be specified"); + return FALSE; + } + + return bd_utils_exec_and_report_error (argv, extra, error); +} diff --git a/src/plugins/s390.h b/src/plugins/s390.h index 1053fe4d8..d95335095 100644 --- a/src/plugins/s390.h +++ b/src/plugins/s390.h @@ -12,18 +12,54 @@ typedef enum { BD_S390_ERROR_FORMAT_FAILED, BD_S390_ERROR_DASDFMT, BD_S390_ERROR_IO, + BD_S390_ERROR_ZKEY, } BDS390Error; typedef enum { BD_S390_TECH_DASD = 0, BD_S390_TECH_ZFCP, + BD_S390_TECH_PAES, } BDS390Tech; typedef enum { BD_S390_TECH_MODE_MODIFY = 1 << 0, BD_S390_TECH_MODE_QUERY = 1 << 1, + BD_S390_TECH_MODE_CREATE = 1 << 2, } BDS390TechMode; +/** + * BDS390ZkeyInfo: + * @name: name of the secure key in the secure key repository; + * @description: user provided description of the key (empty if none was set); + * @secure_key_size: size of the secure key in bytes; + * @clear_key_size: size of the effective (clear) key in bits; + * @xts: whether the key is an XTS type key or not; + * @key_type: type of the secure key (e.g. "CCA-AESCIPHER"); + * @volumes: (array zero-terminated=1): volumes associated with the key, each in the "volume:dmname" format; + * @apqns: (array zero-terminated=1): cryptographic adapters (APQNs) associated with the key, each in the "card.domain" format; + * @key_file_name: full path to the file holding the secure key; + * @sector_size: sector size in bytes to use with dm-crypt or 0 for the system default; + * @volume_type: volume type the key is to be used with (e.g. "LUKS2"); + * @dummy_passphrase: (nullable): path to the dummy passphrase file associated with the key or %NULL if none is set; + */ +typedef struct BDS390ZkeyInfo { + gchar *name; + gchar *description; + guint64 secure_key_size; + guint64 clear_key_size; + gboolean xts; + gchar *key_type; + gchar **volumes; + gchar **apqns; + gchar *key_file_name; + guint64 sector_size; + gchar *volume_type; + gchar *dummy_passphrase; +} BDS390ZkeyInfo; + +void bd_s390_zkey_info_free (BDS390ZkeyInfo *info); +BDS390ZkeyInfo* bd_s390_zkey_info_copy (BDS390ZkeyInfo *info); + /* * If using the plugin as a standalone library, the following functions should * be called to: @@ -51,4 +87,10 @@ gboolean bd_s390_zfcp_online (const gchar *devno, const gchar *wwpn, const gchar gboolean bd_s390_zfcp_scsi_offline(const gchar *devno, const gchar *wwpn, const gchar *lun, GError **error); gboolean bd_s390_zfcp_offline(const gchar *devno, const gchar *wwpn, const gchar *lun, GError **error); +gboolean bd_s390_zkey_generate (const gchar *name, const gchar *key_type, guint64 keybits, const gchar **volumes, const gchar **apqns, guint64 sector_size, gboolean dummy_passphrase, const BDExtraArg **extra, GError **error); +BDS390ZkeyInfo** bd_s390_zkey_list (const gchar *name, GError **error); +gboolean bd_s390_zkey_remove (const gchar *name, GError **error); +gboolean bd_s390_zkey_cryptsetup_setvp (const gchar *device, const gchar *key_file, const BDExtraArg **extra, GError **error); +gboolean bd_s390_zkey_cryptsetup_validate (const gchar *device, const gchar *key_file, const BDExtraArg **extra, GError **error); + #endif /* BD_S390 */ diff --git a/tests/Makefile.am b/tests/Makefile.am index a3ad561be..15c7bec00 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -55,6 +55,10 @@ if WITH_NVME PLUGINS += nvme endif +if WITH_S390 +PLUGINS += s390 +endif + if WITH_SMART PLUGINS += smart endif diff --git a/tests/fake_utils/zkey_list_multiple_apqns/zkey b/tests/fake_utils/zkey_list_multiple_apqns/zkey new file mode 100755 index 000000000..91f846338 --- /dev/null +++ b/tests/fake_utils/zkey_list_multiple_apqns/zkey @@ -0,0 +1,29 @@ +#!/bin/bash + +# fake 'zkey list' output with a single key associated with multiple APQNs (and a +# multi-line verification pattern); the extra APQNs are printed on continuation +# lines indented to the value column + +cat <