From d0d5fe32f849b38ecbf1ec55a3cb01ad33b905a8 Mon Sep 17 00:00:00 2001 From: eabdullin Date: Fri, 4 Oct 2024 06:22:18 +0000 Subject: [PATCH] import CS s390utils-2.33.1-2.el9 --- .gitignore | 4 +- .s390utils.metadata | 4 +- SOURCES/s390utils-2.29.0-rhel.patch | 5719 --------------------------- SOURCES/s390utils-2.33.1-rhel.patch | 311 ++ SPECS/s390utils.spec | 34 +- 5 files changed, 341 insertions(+), 5731 deletions(-) delete mode 100644 SOURCES/s390utils-2.29.0-rhel.patch create mode 100644 SOURCES/s390utils-2.33.1-rhel.patch diff --git a/.gitignore b/.gitignore index 63d61f1..a0c064b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,2 @@ -SOURCES/s390-tools-2.29.0-rust-vendor.tar.xz -SOURCES/s390-tools-2.29.0.tar.gz +SOURCES/s390-tools-2.33.1-rust-vendor.tar.xz +SOURCES/s390-tools-2.33.1.tar.gz diff --git a/.s390utils.metadata b/.s390utils.metadata index c873ba0..48d9507 100644 --- a/.s390utils.metadata +++ b/.s390utils.metadata @@ -1,2 +1,2 @@ -1dcae3e55c2d4d945d0b5c61a12671468aa5f7ef SOURCES/s390-tools-2.29.0-rust-vendor.tar.xz -e10ffbde7f3fcf4438fdfdd83051ad68518e7be5 SOURCES/s390-tools-2.29.0.tar.gz +16b3df1ec7da8a40845ca3c81e96467f3c8beece SOURCES/s390-tools-2.33.1-rust-vendor.tar.xz +cd6394f5a08a3803100d5b7983b3bdf2544b4394 SOURCES/s390-tools-2.33.1.tar.gz diff --git a/SOURCES/s390utils-2.29.0-rhel.patch b/SOURCES/s390utils-2.29.0-rhel.patch deleted file mode 100644 index cee3cc7..0000000 --- a/SOURCES/s390utils-2.29.0-rhel.patch +++ /dev/null @@ -1,5719 +0,0 @@ -From a32824922cb273703bacd44e6a29cbc33ae48cf5 Mon Sep 17 00:00:00 2001 -From: Ingo Franzki -Date: Fri, 21 Jul 2023 14:06:18 +0200 -Subject: [PATCH 01/13] zkey: Support EP11 AES keys with prepended header to - retain EP11 session (RHEL-11440) - -The pkey kernel module supports two key blob formats for EP11 AES keys. -The first one (PKEY_TYPE_EP11) contains a 16 bytes header that overlays -the first 32 bytes of the key blob which usually contain the ID of the -EP11 session to which the key is bound. For zkey/dm-crypt that session -ID used to be all zeros. The second blob format (PKEY_TYPE_EP11_AES) -prepends the 16 bytes header to the blob, an thus does not overlay the -blob. This format can be used for key blobs that are session-bound, i.e. -have a non-zero session ID in the first 32 bytes. - -Change zkey to generate EP11 keys using the new format (i.e. pkey type -PKEY_TYPE_EP11_AES), but existing key blobs using the old format can -still be used. - -Signed-off-by: Ingo Franzki -Reviewed-by: Joerg Schmidbauer -Signed-off-by: Steffen Eiden -(cherry picked from commit 1b044b8a40ab59e4f5ffe66e3ad81499b0beccce) ---- - zkey/ep11.c | 48 +++++++++----- - zkey/keystore.c | 4 +- - zkey/kmip/zkey-kmip.c | 76 ++++++++++++++++++---- - zkey/kms.c | 9 ++- - zkey/pkey.c | 141 +++++++++++++++++++++++++++++++++++++++-- - zkey/pkey.h | 45 +++++++++---- - zkey/zkey-cryptsetup.c | 15 ++++- - zkey/zkey.c | 8 ++- - 8 files changed, 295 insertions(+), 51 deletions(-) - -diff --git a/zkey/ep11.c b/zkey/ep11.c -index 8359929..df8b57d 100644 ---- a/zkey/ep11.c -+++ b/zkey/ep11.c -@@ -365,8 +365,9 @@ int select_ep11_apqn_by_mkvp(struct ep11_lib *ep11, u8 *mkvp, - * @param[in] target the target handle to use for the re-encipher operation - * @param[in] card the card that corresponds to the target handle - * @param[in] domain the domain that corresponds to the target handle -- * @param[in/out] ep11key the EP11 key token to reencipher. The re-enciphered -- * secure key will be returned in this buffer. -+ * @param[in/out] ep11key_blob the EP11 key token to reencipher. The -+ * re-enciphered secure key will be returned in this -+ * buffer. - * @param[in] ep11key_size the size of the secure key - * @param[in] verbose if true, verbose messages are printed - * -@@ -374,21 +375,29 @@ int select_ep11_apqn_by_mkvp(struct ep11_lib *ep11, u8 *mkvp, - */ - static int ep11_adm_reencrypt(struct ep11_lib *ep11, target_t target, - unsigned int card, unsigned int domain, -- struct ep11keytoken *ep11key, -+ u8 *ep11key_blob, - unsigned int ep11key_size, bool verbose) - { -+ struct ep11kblob_header *hdr = (struct ep11kblob_header *)ep11key_blob; -+ struct ep11keytoken *ep11key; - CK_BYTE resp[MAX_BLOBSIZE]; - CK_BYTE req[MAX_BLOBSIZE]; -- char ep11_token_header[sizeof(ep11key->head)]; -+ char ep11_token_header[sizeof(ep11key->head)] = { 0 }; - struct XCPadmresp lrb; - struct XCPadmresp rb; -+ bool with_header; - size_t resp_len; - size_t blob_len; - long req_len; - CK_RV rv; - int rc; - -- blob_len = ep11key->head.length; -+ with_header = is_ep11_aes_key_with_header(ep11key_blob, ep11key_size); -+ ep11key = (struct ep11keytoken *)(with_header ? -+ ep11key_blob + sizeof(struct ep11kblob_header) : -+ ep11key_blob); -+ blob_len = with_header ? hdr->len - sizeof(struct ep11kblob_header) : -+ ep11key->head.len; - if (blob_len > ep11key_size) { - pr_verbose(verbose, "Blob length larger than secure key size"); - return -EINVAL; -@@ -397,9 +406,14 @@ static int ep11_adm_reencrypt(struct ep11_lib *ep11, target_t target, - rb.domain = domain; - lrb.domain = domain; - -- /* The token header is an overlay over the (all zero) session field */ -- memcpy(ep11_token_header, ep11key, sizeof(ep11_token_header)); -- memset(ep11key->session, 0, sizeof(ep11key->session)); -+ if (!with_header) { -+ /* -+ * The token header is an overlay over the (all zero) session -+ * field -+ */ -+ memcpy(ep11_token_header, ep11key, sizeof(ep11_token_header)); -+ memset(ep11key->session, 0, sizeof(ep11key->session)); -+ } - - resp_len = sizeof(resp); - req_len = ep11->dll_xcpa_cmdblock(req, sizeof(req), XCP_ADM_REENCRYPT, -@@ -446,7 +460,8 @@ static int ep11_adm_reencrypt(struct ep11_lib *ep11, target_t target, - } - - memcpy(ep11key, lrb.payload, blob_len); -- memcpy(ep11key, ep11_token_header, sizeof(ep11_token_header)); -+ if (!with_header) -+ memcpy(ep11key, ep11_token_header, sizeof(ep11_token_header)); - - return 0; - } -@@ -469,7 +484,6 @@ int reencipher_ep11_key(struct ep11_lib *ep11, target_t target, - unsigned int card, unsigned int domain, u8 *secure_key, - unsigned int secure_key_size, bool verbose) - { -- struct ep11keytoken *ep11key = (struct ep11keytoken *)secure_key; - CK_IBM_DOMAIN_INFO dinf; - CK_ULONG dinf_len = sizeof(dinf); - CK_RV rv; -@@ -493,17 +507,21 @@ int reencipher_ep11_key(struct ep11_lib *ep11, target_t target, - return -ENODEV; - } - -- rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key, -+ rc = ep11_adm_reencrypt(ep11, target, card, domain, secure_key, - secure_key_size, verbose); - if (rc != 0) - return rc; - - if (is_xts_key(secure_key, secure_key_size)) { -- secure_key += EP11_KEY_SIZE; -- secure_key_size -= EP11_KEY_SIZE; -- ep11key = (struct ep11keytoken *)secure_key; -+ if (is_ep11_aes_key_with_header(secure_key, secure_key_size)) { -+ secure_key += EP11_AES_KEY_SIZE; -+ secure_key_size -= EP11_AES_KEY_SIZE; -+ } else { -+ secure_key += EP11_KEY_SIZE; -+ secure_key_size -= EP11_KEY_SIZE; -+ } - -- rc = ep11_adm_reencrypt(ep11, target, card, domain, ep11key, -+ rc = ep11_adm_reencrypt(ep11, target, card, domain, secure_key, - secure_key_size, verbose); - if (rc != 0) - return rc; -diff --git a/zkey/keystore.c b/zkey/keystore.c -index 4efa2e4..c0a7037 100644 ---- a/zkey/keystore.c -+++ b/zkey/keystore.c -@@ -3398,7 +3398,9 @@ static int _keystore_perform_reencipher(struct keystore *keystore, - "CURRENT master key", name); - if (!selected && - !is_ep11_aes_key(secure_key, -- secure_key_size)) -+ secure_key_size) && -+ !is_ep11_aes_key_with_header(secure_key, -+ secure_key_size)) - print_msg_for_cca_envvars( - "secure AES key"); - } -diff --git a/zkey/kmip/zkey-kmip.c b/zkey/kmip/zkey-kmip.c -index a00c5dd..e7b7c73 100644 ---- a/zkey/kmip/zkey-kmip.c -+++ b/zkey/kmip/zkey-kmip.c -@@ -5278,9 +5278,11 @@ static int _ep11_unwrap_key_rsa(struct plugin_handle *ph, - m_UnwrapKey_t dll_m_UnwrapKey; - const unsigned char *key_blob; - struct ep11keytoken *ep11key; -+ struct ep11kblob_header *hdr; - CK_MECHANISM mech = { 0 }; - CK_BYTE csum[7] = { 0 }; - CK_BBOOL ck_true = true; -+ int pkey_fd, rc; - CK_RV rv; - - CK_ATTRIBUTE template[] = { -@@ -5306,7 +5308,8 @@ static int _ep11_unwrap_key_rsa(struct plugin_handle *ph, - pr_verbose(&ph->pd, "Wrap hashing algorithm: %d", - ph->profile->wrap_hashing_algo); - -- if (*unwrapped_key_len < sizeof(struct ep11keytoken)) { -+ if (*unwrapped_key_len < sizeof(struct ep11kblob_header) + -+ sizeof(struct ep11keytoken)) { - _set_error(ph, "Key buffer is too small"); - return -EINVAL; - } -@@ -5381,19 +5384,68 @@ static int _ep11_unwrap_key_rsa(struct plugin_handle *ph, - 256 * 256 * csum[csum_len - 3] + - 256 * 256 * 256 * csum[csum_len - 4]; - -- /* Setup the EP11 token header */ -- ep11key = (struct ep11keytoken *)unwrapped_key; -- memset(&ep11key->session, 0, sizeof(ep11key->session)); -- ep11key->head.type = TOKEN_TYPE_NON_CCA; -- ep11key->head.length = *unwrapped_key_len; -- ep11key->head.version = TOKEN_VERSION_EP11_AES; -- ep11key->head.keybitlen = bit_len; -- -- pr_verbose(&ph->pd, "unwrapped bit length: %u", -- ep11key->head.keybitlen); -+ /* Prepend and setup the EP11 token header */ -+ hdr = (struct ep11kblob_header *)unwrapped_key; -+ ep11key = (struct ep11keytoken *) -+ (unwrapped_key + sizeof(struct ep11kblob_header)); -+ memmove(ep11key, unwrapped_key, *unwrapped_key_len); -+ *unwrapped_key_len += sizeof(struct ep11kblob_header); -+ memset(hdr, 0, sizeof(struct ep11kblob_header)); -+ hdr->type = TOKEN_TYPE_NON_CCA; -+ hdr->hver = 0; -+ hdr->len = *unwrapped_key_len; -+ hdr->version = TOKEN_VERSION_EP11_AES_WITH_HEADER; -+ hdr->bitlen = bit_len; -+ -+ pr_verbose(&ph->pd, "unwrapped bit length: %u", hdr->bitlen); - - /* return full length, blob is already zero padded */ -- *unwrapped_key_len = sizeof(struct ep11keytoken); -+ *unwrapped_key_len = -+ sizeof(struct ep11kblob_header) + sizeof(struct ep11keytoken); -+ -+ /* -+ * Check if the pkey module supports keys of type -+ * TOKEN_VERSION_EP11_AES_WITH_HEADER, older kernels may not support -+ * such keys. If it does not support such keys, convert the key to -+ * TOKEN_VERSION_EP11_AES type, if its session field is all zero -+ * (i.e. the key is not session bound). -+ */ -+ pkey_fd = open_pkey_device(ph->pd.verbose); -+ if (pkey_fd < 0) { -+ _set_error(ph, "Failed to open pkey device"); -+ return -EIO; -+ } -+ -+ rc = validate_secure_key(pkey_fd, unwrapped_key, *unwrapped_key_len, -+ NULL, NULL, NULL, ph->pd.verbose); -+ close(pkey_fd); -+ if (rc == -EINVAL || rc == -ENODEV) { -+ pr_verbose(&ph->pd, "The pkey kernel module does not support " -+ "PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11"); -+ -+ if (is_ep11_key_session_bound(unwrapped_key, -+ *unwrapped_key_len)) { -+ _set_error(ph, "The unwrapped key is session bound. " -+ "Kernel support is required for such keys"); -+ return -EIO; -+ } -+ -+ key_blob_len = hdr->len; -+ *unwrapped_key_len -= sizeof(struct ep11kblob_header); -+ memmove(unwrapped_key, -+ unwrapped_key + sizeof(struct ep11kblob_header), -+ *unwrapped_key_len); -+ ep11key = (struct ep11keytoken *)unwrapped_key; -+ memset(&ep11key->session, 0, sizeof(ep11key->session)); -+ ep11key->head.type = TOKEN_TYPE_NON_CCA; -+ ep11key->head.len = key_blob_len - -+ sizeof(struct ep11kblob_header); -+ ep11key->head.version = TOKEN_VERSION_EP11_AES; -+ ep11key->head.bitlen = bit_len; -+ } else if (rc != 0) { -+ _set_error(ph, "Failed to validate unwrapped key"); -+ return rc; -+ } - - return 0; - } -diff --git a/zkey/kms.c b/zkey/kms.c -index 9892a9e..2e33b22 100644 ---- a/zkey/kms.c -+++ b/zkey/kms.c -@@ -2175,7 +2175,7 @@ int generate_kms_key(struct kms_info *kms_info, const char *name, - else if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) - key_size = AESCIPHER_KEY_SIZE; - else if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) -- key_size = EP11_KEY_SIZE; -+ key_size = EP11_AES_KEY_SIZE; - else - return -ENOTSUP; - -@@ -2248,6 +2248,9 @@ int generate_kms_key(struct kms_info *kms_info, const char *name, - if (verbose) - util_hexdump_grp(stderr, NULL, key_blob, 4, key_blob_size, 0); - -+ if (is_ep11_aes_key(key_blob, key_blob_size)) -+ key_size = EP11_KEY_SIZE; -+ - /* Save ID and label of 1st key */ - rc = properties_set(key_props, xts ? PROP_NAME_KMS_XTS_KEY1_ID : - PROP_NAME_KMS_KEY_ID, key1_id); -@@ -3132,6 +3135,8 @@ int import_kms_key(struct kms_info *kms_info, const char *key1_id, - key_size = AESCIPHER_KEY_SIZE; - else if (is_ep11_aes_key(key_blob, key_blob_size)) - key_size = EP11_KEY_SIZE; -+ else if (is_ep11_aes_key_with_header(key_blob, key_blob_size)) -+ key_size = EP11_AES_KEY_SIZE; - - if (key_size == 0 || key_blob_size > key_size) { - pr_verbose(verbose, "Key '%s' has an unknown or unsupported " -@@ -3366,6 +3371,8 @@ int refresh_kms_key(struct kms_info *kms_info, struct properties *key_props, - key_size = AESCIPHER_KEY_SIZE; - else if (is_ep11_aes_key(key_blob, key_blob_size)) - key_size = EP11_KEY_SIZE; -+ else if (is_ep11_aes_key_with_header(key_blob, key_blob_size)) -+ key_size = EP11_AES_KEY_SIZE; - - if (key_size == 0 || key_blob_size > key_size) { - pr_verbose(verbose, "Key '%s' has an unknown or unsupported " -diff --git a/zkey/pkey.c b/zkey/pkey.c -index e013e06..2582088 100644 ---- a/zkey/pkey.c -+++ b/zkey/pkey.c -@@ -858,7 +858,7 @@ static enum pkey_key_type key_type_to_pkey_type(const char *key_type) - if (strcasecmp(key_type, KEY_TYPE_CCA_AESCIPHER) == 0) - return PKEY_TYPE_CCA_CIPHER; - if (strcasecmp(key_type, KEY_TYPE_EP11_AES) == 0) -- return PKEY_TYPE_EP11; -+ return PKEY_TYPE_EP11_AES; - - return 0; - } -@@ -879,6 +879,8 @@ static size_t key_size_for_type(enum pkey_key_type type) - return AESCIPHER_KEY_SIZE; - case PKEY_TYPE_EP11: - return EP11_KEY_SIZE; -+ case PKEY_TYPE_EP11_AES: -+ return EP11_AES_KEY_SIZE; - default: - return 0; - } -@@ -924,6 +926,7 @@ int generate_secure_key_random(int pkey_fd, const char *keyfile, - return -ENOTSUP; - } - -+retry: - genseck2.size = keybits_to_keysize(keybits); - if (genseck2.size == 0) { - warnx("Invalid value for '--keybits'/'-c': '%lu'", keybits); -@@ -957,10 +960,33 @@ int generate_secure_key_random(int pkey_fd, const char *keyfile, - genseck2.keylen = size; - - rc = pkey_genseck2(pkey_fd, &genseck2, verbose); -+ if (rc == -EINVAL && genseck2.type == PKEY_TYPE_EP11_AES) { -+ /* -+ * Older kernels may not support gensek2 with key type -+ * PKEY_TYPE_EP11_AES, retry with PKEY_TYPE_EP11. -+ */ -+ pr_verbose(verbose, -+ "ioctl PKEY_GENSECK2 does not support " -+ "PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11"); -+ -+ genseck2.type = PKEY_TYPE_EP11; -+ free(genseck2.apqns); -+ genseck2.apqns = NULL; -+ genseck2.apqn_entries = 0; -+ free(secure_key); -+ goto retry; -+ } - if (rc != 0) { - warnx("Failed to generate a secure key: %s", strerror(-rc)); - goto out; - } -+ if (rc == 0 && genseck2.type == PKEY_TYPE_EP11) { -+ if (is_ep11_key_session_bound(secure_key, size)) { -+ warnx("The generated key is session bound. Kernel " -+ "support is required for such keys"); -+ goto out; -+ } -+ } - - if (xts) { - free(genseck2.apqns); -@@ -1062,6 +1088,7 @@ int generate_secure_key_clear(int pkey_fd, const char *keyfile, - return -ENOTSUP; - } - -+retry: - clr2seck2.size = keybits_to_keysize(HALF_KEYSIZE_FOR_XTS( - clear_key_size * 8, xts)); - if (clr2seck2.size == 0) { -@@ -1096,10 +1123,33 @@ int generate_secure_key_clear(int pkey_fd, const char *keyfile, - clr2seck2.keylen = size; - - rc = pkey_clr2seck2(pkey_fd, &clr2seck2, verbose); -+ if (rc == -EINVAL && clr2seck2.type == PKEY_TYPE_EP11_AES) { -+ /* -+ * Older kernels may not support clr2seck2 with key type -+ * PKEY_TYPE_EP11_AES, retry with PKEY_TYPE_EP11. -+ */ -+ pr_verbose(verbose, -+ "ioctl PKEY_CLR2SECK2 does not support " -+ "PKEY_TYPE_EP11_AES, fall back to PKEY_TYPE_EP11"); -+ -+ clr2seck2.type = PKEY_TYPE_EP11; -+ free(clr2seck2.apqns); -+ clr2seck2.apqns = NULL; -+ clr2seck2.apqn_entries = 0; -+ free(secure_key); -+ goto retry; -+ } - if (rc != 0) { - warnx("Failed to generate a secure key: %s", strerror(-rc)); - goto out; - } -+ if (rc == 0 && clr2seck2.type == PKEY_TYPE_EP11) { -+ if (is_ep11_key_session_bound(secure_key, size)) { -+ warnx("The generated key is session bound. Kernel " -+ "support is required for such keys"); -+ goto out; -+ } -+ } - - if (xts) { - free(clr2seck2.apqns); -@@ -1486,6 +1536,8 @@ int get_master_key_verification_pattern(const u8 *key, size_t key_size, - struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; - struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; - struct ep11keytoken *ep11key = (struct ep11keytoken *)key; -+ struct ep11keytoken *ep11key2 = -+ (struct ep11keytoken *)(key + sizeof(struct ep11kblob_header)); - - util_assert(key != NULL, "Internal error: secure_key is NULL"); - util_assert(mkvp != NULL, "Internal error: mkvp is NULL"); -@@ -1497,6 +1549,8 @@ int get_master_key_verification_pattern(const u8 *key, size_t key_size, - memcpy(mkvp, &cipherkey->kvp, sizeof(cipherkey->kvp)); - else if (is_ep11_aes_key(key, key_size)) - memcpy(mkvp, &ep11key->wkvp, sizeof(ep11key->wkvp)); -+ else if (is_ep11_aes_key_with_header(key, key_size)) -+ memcpy(mkvp, &ep11key2->wkvp, sizeof(ep11key2->wkvp)); - else - return -EINVAL; - -@@ -1593,9 +1647,43 @@ bool is_ep11_aes_key(const u8 *key, size_t key_size) - - if (ep11key->head.type != TOKEN_TYPE_NON_CCA) - return false; -+ if (ep11key->head.hver != 0) -+ return false; - if (ep11key->head.version != TOKEN_VERSION_EP11_AES) - return false; -- if (ep11key->head.length > key_size) -+ if (ep11key->head.len > key_size) -+ return false; -+ -+ if (ep11key->version != 0x1234) -+ return false; -+ -+ return true; -+} -+ -+/** -+ * Check if the specified key is a EP11 AES key token with external header. -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns true if the key is an EP11 AES token with external header type -+ */ -+bool is_ep11_aes_key_with_header(const u8 *key, size_t key_size) -+{ -+ struct ep11kblob_header *header = (struct ep11kblob_header *)key; -+ struct ep11keytoken *ep11key = -+ (struct ep11keytoken *)(key + sizeof(struct ep11kblob_header)); -+ -+ if (key == NULL || key_size < EP11_AES_KEY_SIZE) -+ return false; -+ -+ if (header->type != TOKEN_TYPE_NON_CCA) -+ return false; -+ if (header->hver != 0) -+ return false; -+ if (header->version != TOKEN_VERSION_EP11_AES_WITH_HEADER) -+ return false; -+ if (header->len > key_size) - return false; - - if (ep11key->version != 0x1234) -@@ -1604,6 +1692,33 @@ bool is_ep11_aes_key(const u8 *key, size_t key_size) - return true; - } - -+/** -+ * Check if the specified EP11 AES key is session bound. -+ * -+ * @param[in] key the secure key token -+ * @param[in] key_size the size of the secure key -+ * -+ * @returns true if the key is an EP11 AES token type -+ */ -+bool is_ep11_key_session_bound(const u8 *key, size_t key_size) -+{ -+ struct ep11keytoken *ep11key; -+ -+ if (is_ep11_aes_key(key, key_size)) { -+ ep11key = (struct ep11keytoken *)key; -+ return memcmp(ep11key->session + sizeof(ep11key->head), -+ ZERO_SESSION, sizeof(ep11key->session) - -+ sizeof(ep11key->head)) != 0; -+ } else if (is_ep11_aes_key_with_header(key, key_size)) { -+ ep11key = (struct ep11keytoken *) -+ (key + sizeof(struct ep11kblob_header)); -+ return memcmp(ep11key->session, ZERO_SESSION, -+ sizeof(ep11key->session)) != 0; -+ } else { -+ return false; -+ } -+} -+ - /** - * Check if the specified key is an XTS type key - * -@@ -1629,6 +1744,11 @@ bool is_xts_key(const u8 *key, size_t key_size) - is_ep11_aes_key(key + EP11_KEY_SIZE, - key_size - EP11_KEY_SIZE)) - return true; -+ } else if (is_ep11_aes_key_with_header(key, key_size)) { -+ if (key_size == 2 * EP11_AES_KEY_SIZE && -+ is_ep11_aes_key_with_header(key + EP11_AES_KEY_SIZE, -+ key_size - EP11_AES_KEY_SIZE)) -+ return true; - } - - return false; -@@ -1650,6 +1770,7 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) - struct aesdatakeytoken *datakey = (struct aesdatakeytoken *)key; - struct aescipherkeytoken *cipherkey = (struct aescipherkeytoken *)key; - struct ep11keytoken *ep11key = (struct ep11keytoken *)key; -+ struct ep11kblob_header *hdr = (struct ep11kblob_header *)key; - - util_assert(bitsize != NULL, "Internal error: bitsize is NULL"); - -@@ -1672,10 +1793,17 @@ int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize) - *bitsize += cipherkey->pl - 384; - } - } else if (is_ep11_aes_key(key, key_size)) { -- *bitsize = ep11key->head.keybitlen; -+ *bitsize = ep11key->head.bitlen; - if (key_size == 2 * EP11_KEY_SIZE) { - ep11key = (struct ep11keytoken *)(key + EP11_KEY_SIZE); -- *bitsize += ep11key->head.keybitlen; -+ *bitsize += ep11key->head.bitlen; -+ } -+ } else if (is_ep11_aes_key_with_header(key, key_size)) { -+ *bitsize = hdr->bitlen; -+ if (key_size == 2 * EP11_AES_KEY_SIZE) { -+ hdr = (struct ep11kblob_header *) -+ (key + EP11_AES_KEY_SIZE); -+ *bitsize += hdr->bitlen; - } - } else { - return -EINVAL; -@@ -1700,6 +1828,8 @@ const char *get_key_type(const u8 *key, size_t key_size) - return KEY_TYPE_CCA_AESCIPHER; - if (is_ep11_aes_key(key, key_size)) - return KEY_TYPE_EP11_AES; -+ if (is_ep11_aes_key_with_header(key, key_size)) -+ return KEY_TYPE_EP11_AES; - return NULL; - } - -@@ -2016,7 +2146,8 @@ int reencipher_secure_key(struct ext_lib *lib, u8 *secure_key, - return rc; - } - -- if (is_ep11_aes_key(secure_key, secure_key_size)) { -+ if (is_ep11_aes_key(secure_key, secure_key_size) || -+ is_ep11_aes_key_with_header(secure_key, secure_key_size)) { - /* EP11 secure key: need the EP11 host library */ - if (lib->ep11->lib_ep11 == NULL) { - rc = load_ep11_library(lib->ep11, verbose); -diff --git a/zkey/pkey.h b/zkey/pkey.h -index 5a5bc3c..3b57c5f 100644 ---- a/zkey/pkey.h -+++ b/zkey/pkey.h -@@ -39,6 +39,8 @@ struct tokenheader { - #define TOKEN_VERSION_PROTECTED_KEY 0x01 - #define TOKEN_VERSION_CLEAR_KEY 0x02 - #define TOKEN_VERSION_EP11_AES 0x03 -+#define TOKEN_VERSION_EP11_AES_WITH_HEADER 0x06 -+#define TOKEN_VERSION_EP11_ECC_WITH_HEADER 0x07 - - struct aesdatakeytoken { - u8 type; /* TOKEN_TYPE_INTERNAL (0x01) for internal key token */ -@@ -89,17 +91,20 @@ struct aescipherkeytoken { - u8 varpart[80]; /* variable part */ - } __packed; - -+struct ep11kblob_header { -+ u8 type; /* always 0x00 */ -+ u8 hver; /* header version, currently needs to be 0x00 */ -+ u16 len; /* total length in bytes (including this header) */ -+ u8 version; /* PKEY_TYPE_EP11_AES or PKEY_TYPE_EP11_ECC */ -+ u8 res0; /* unused */ -+ u16 bitlen; /* clear key bit len, 0 for unknown */ -+ u8 res1[8]; /* unused */ -+} __packed; -+ - struct ep11keytoken { - union { - u8 session[32]; -- struct { -- u8 type; /* TOKEN_TYPE_NON_CCA (0x00) */ -- u8 res0; /* unused */ -- u16 length; /* length of token */ -- u8 version; /* TOKEN_VERSION_EP11_AES (0x03) */ -- u8 res1; /* unused */ -- u16 keybitlen; /* clear key bit len, 0 for unknown */ -- } head; -+ struct ep11kblob_header head; - }; - u8 wkvp[16]; /* wrapping key verification pattern */ - u64 attr; /* boolean key attributes */ -@@ -111,18 +116,29 @@ struct ep11keytoken { - u8 padding[64]; - } __packed; - -+#define ZERO_SESSION \ -+ "\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0" -+ - #define AESDATA_KEY_SIZE sizeof(struct aesdatakeytoken) - #define AESCIPHER_KEY_SIZE sizeof(struct aescipherkeytoken) - #define EP11_KEY_SIZE sizeof(struct ep11keytoken) -+#define EP11_AES_KEY_SIZE (sizeof(struct ep11kblob_header) + \ -+ sizeof(struct ep11keytoken)) - - /* MAX/MIN from zt_common.h produces warnings for variable length arrays */ - #define _MIN(a, b) ((a) < (b) ? (a) : (b)) - #define _MAX(a, b) ((a) > (b) ? (a) : (b)) - --#define MAX_SECURE_KEY_SIZE _MAX(EP11_KEY_SIZE, \ -- _MAX(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE)) --#define MIN_SECURE_KEY_SIZE _MIN(EP11_KEY_SIZE, \ -- _MIN(AESDATA_KEY_SIZE, AESCIPHER_KEY_SIZE)) -+#define MAX_SECURE_KEY_SIZE _MAX( \ -+ _MAX(EP11_KEY_SIZE, \ -+ EP11_AES_KEY_SIZE), \ -+ _MAX(AESDATA_KEY_SIZE, \ -+ AESCIPHER_KEY_SIZE)) -+#define MIN_SECURE_KEY_SIZE _MIN( \ -+ _MIN(EP11_KEY_SIZE, \ -+ EP11_AES_KEY_SIZE), \ -+ _MIN(AESDATA_KEY_SIZE, \ -+ AESCIPHER_KEY_SIZE)) - - struct pkey_seckey { - u8 seckey[AESDATA_KEY_SIZE]; /* the secure key blob */ -@@ -175,6 +191,9 @@ enum pkey_key_type { - PKEY_TYPE_CCA_DATA = (u32) 1, - PKEY_TYPE_CCA_CIPHER = (u32) 2, - PKEY_TYPE_EP11 = (u32) 3, -+ PKEY_TYPE_CCA_ECC = (u32) 0x1f, -+ PKEY_TYPE_EP11_AES = (u32) 6, -+ PKEY_TYPE_EP11_ECC = (u32) 7, - }; - - enum pkey_key_size { -@@ -321,6 +340,8 @@ int get_master_key_verification_pattern(const u8 *key, size_t key_size, - bool is_cca_aes_data_key(const u8 *key, size_t key_size); - bool is_cca_aes_cipher_key(const u8 *key, size_t key_size); - bool is_ep11_aes_key(const u8 *key, size_t key_size); -+bool is_ep11_aes_key_with_header(const u8 *key, size_t key_size); -+bool is_ep11_key_session_bound(const u8 *key, size_t key_size); - bool is_xts_key(const u8 *key, size_t key_size); - int get_key_bit_size(const u8 *key, size_t key_size, size_t *bitsize); - const char *get_key_type(const u8 *key, size_t key_size); -diff --git a/zkey/zkey-cryptsetup.c b/zkey/zkey-cryptsetup.c -index fae78c7..8b55f7d 100644 ---- a/zkey/zkey-cryptsetup.c -+++ b/zkey/zkey-cryptsetup.c -@@ -1673,7 +1673,10 @@ static int reencipher_prepare(int token) - warnx("Failed to re-encipher the secure volume " - "key for device '%s'\n", g.pos_arg); - if (!selected && -- !is_ep11_aes_key((u8 *)key, securekeysize)) -+ !is_ep11_aes_key((u8 *)key, -+ securekeysize) && -+ !is_ep11_aes_key_with_header((u8 *)key, -+ securekeysize)) - print_msg_for_cca_envvars( - "secure AES volume key"); - rc = -EINVAL; -@@ -1696,7 +1699,10 @@ static int reencipher_prepare(int token) - warnx("Failed to re-encipher the secure volume " - "key for device '%s'\n", g.pos_arg); - if (!selected && -- !is_ep11_aes_key((u8 *)key, securekeysize)) -+ !is_ep11_aes_key((u8 *)key, -+ securekeysize) && -+ !is_ep11_aes_key_with_header((u8 *)key, -+ securekeysize)) - print_msg_for_cca_envvars( - "secure AES volume key"); - rc = -EINVAL; -@@ -1836,7 +1842,10 @@ static int reencipher_complete(int token) - warnx("Failed to re-encipher the secure volume " - "key for device '%s'\n", g.pos_arg); - if (!selected && -- !is_ep11_aes_key((u8 *)key, securekeysize)) -+ !is_ep11_aes_key((u8 *)key, -+ securekeysize) && -+ !is_ep11_aes_key_with_header((u8 *)key, -+ securekeysize)) - print_msg_for_cca_envvars( - "secure AES volume key"); - rc = -EINVAL; -diff --git a/zkey/zkey.c b/zkey/zkey.c -index 3000290..843e554 100644 ---- a/zkey/zkey.c -+++ b/zkey/zkey.c -@@ -1968,7 +1968,9 @@ static int command_reencipher_file(void) - "master key has failed\n"); - if (!selected && - !is_ep11_aes_key(secure_key, -- secure_key_size)) -+ secure_key_size) && -+ !is_ep11_aes_key_with_header(secure_key, -+ secure_key_size)) - print_msg_for_cca_envvars( - "secure AES key"); - } -@@ -1993,7 +1995,9 @@ static int command_reencipher_file(void) - "master key has failed\n"); - if (!selected && - !is_ep11_aes_key(secure_key, -- secure_key_size)) -+ secure_key_size) && -+ !is_ep11_aes_key_with_header(secure_key, -+ secure_key_size)) - print_msg_for_cca_envvars( - "secure AES key"); - } --- -2.43.0 - - -From df0819ca69dbef1f99321f51cd9c4d33c6374992 Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Mon, 7 Aug 2023 16:56:54 +0200 -Subject: [PATCH 02/13] rust/Makefile: Fix use of Cargoflags for 'make clean' -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Reviewed-by: Marc Hartmayer -Signed-off-by: Steffen Eiden -Signed-off-by: Jan Höppner -(cherry picked from commit ee669294650a2f96585357447fa5e93794e5b48f) ---- - rust/Makefile | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/rust/Makefile b/rust/Makefile -index cf2fda7..420bafd 100644 ---- a/rust/Makefile -+++ b/rust/Makefile -@@ -68,7 +68,7 @@ clean: - $(foreach target,$(CARGO_TARGETS),\ - $(CARGO_CLEAN) --manifest-path=$(target)/Cargo.toml ${ALL_CARGOFLAGS} ;) - $(foreach target,$(PV_TARGETS),\ -- $(CARGO_CLEAN) --manifest-path=$(target)/Cargo.toml ${CARGOFLAGS} ;) -+ $(CARGO_CLEAN) --manifest-path=$(target)/Cargo.toml ${ALL_CARGOFLAGS} ;) - $(RM) -- .check-dep-pvtools .detect-openssl.dep.c .check-cargo - - rust-test: .check-cargo .no-cross-compile --- -2.43.0 - - -From b6ce8c7fc10c225c0b1d59af32edd323f5817ab7 Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Mon, 7 Aug 2023 16:56:55 +0200 -Subject: [PATCH 03/13] rust/README.md: Fix some typos -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Reviewed-by: Marc Hartmayer -Signed-off-by: Steffen Eiden -Signed-off-by: Jan Höppner -(cherry picked from commit d5f806390012b1fe0afb52eb9d78a883cc5b4cdf) ---- - rust/README.md | 14 +++++++------- - 1 file changed, 7 insertions(+), 7 deletions(-) - -diff --git a/rust/README.md b/rust/README.md -index 2622bba..61b0af8 100644 ---- a/rust/README.md -+++ b/rust/README.md -@@ -7,21 +7,21 @@ https://www.rust-lang.org/learn/get-started - ## Building rust code - ### s390-tools build system - If `cargo` is installed a simple `make` should do the job. Note that, --compiling rust programs take significaltly longer than C code. To closely --monitor the prgress use `make V=1` By default release builds are made. -+compiling rust programs take significantly longer than C code. To closely -+monitor the progress use `make V=1` By default release builds are made. - - With `make CARGOFLAGS=` one can pass additional flags to cargo. - With `make HAVE_CARGO=0` one can turn of any compilation that requires cargo. - With `make CARGO=<...>` one can set the cargo binary - - ### cargo --If you need to run cargo directly, cd to each project you want to build and -+If you need to run cargo directly, `cd` to each project you want to build and - issue your cargo commands. Do **NOT** forget to specify `--release` if you are - building tools for a release. The s390-tools expect the environment variable - `S390_TOOLS_RELEASE` to be present at build time. This is the version string the - rust tools provide. - --Tpp: You can use `make version` to get the version string. -+Tip: You can use `make version` to get the version string. - - ## Internal Libraries - * __utils__ _Library for rust tools that bundles common stuff for the 390-tools_ -@@ -43,7 +43,7 @@ Exiting tools may be rewritten in Rust. - - ### What (third-party) crates can be used for s390-tools? - A huge list of libraries are made available through Rusts' ecosystem and is one --of many upsides. However, just like with Coding Style Guidlines, it is -+of many upsides. However, just like with Coding Style Guidelines, it is - important to limit the usage of those libraries so that within a project, - everyone is on the same page and that code written in Rust uses similar - approaches. It makes it easier for code review and maintainability in general. -@@ -98,8 +98,8 @@ use utils::release_string; - fn print_version() { - println!( - "{} version {}\nCopyright IBM Corp. 2023", -- env!("CARGO_PKG_NAME"), // collapes into the crates name -- release_string!() // this (very likely) collapes into a compile time constant -+ env!("CARGO_PKG_NAME"), // collapses into the crates name -+ release_string!() // this (very likely) collapses into a compile time constant - ); - } - ``` --- -2.43.0 - - -From 883d28afea6ea18b1001ebf9e3d921d86be9c593 Mon Sep 17 00:00:00 2001 -From: Marc Hartmayer -Date: Mon, 4 Sep 2023 14:18:50 +0200 -Subject: [PATCH 04/13] rust/**/*.rs: fix `cargo clippy` findings -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Automatically fixed by the command `cargo clippy --fix` and `cargo fmt`. - -Reviewed-by: Steffen Eiden -Signed-off-by: Marc Hartmayer -Signed-off-by: Jan Höppner -(cherry picked from commit 3849b2959414feae3e984d4cae0a0cfcb529006d) ---- - rust/pv/src/brcb.rs | 4 +- - rust/pv/src/crypto.rs | 2 +- - rust/pv/src/req.rs | 2 +- - rust/pv/src/uvsecret/guest_secret.rs | 2 +- - rust/pv/src/uvsecret/secret_list.rs | 2 +- - rust/pv/src/verify/helper.rs | 10 ++-- - rust/pv/src/verify/test.rs | 72 ++++++++++++---------------- - rust/pv/tests/add_secret_request.rs | 17 ++----- - 8 files changed, 47 insertions(+), 64 deletions(-) - -diff --git a/rust/pv/src/brcb.rs b/rust/pv/src/brcb.rs -index 4bf5f16..9ef58e2 100644 ---- a/rust/pv/src/brcb.rs -+++ b/rust/pv/src/brcb.rs -@@ -205,7 +205,7 @@ mod tests { - #[test] - fn from_se_image_hdr() { - let bin_hdr = get_test_asset!("exp/secure_guest.hdr"); -- let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(bin_hdr.clone())).unwrap(); -+ let hdr_tags = BootHdrTags::from_se_image(&mut Cursor::new(*bin_hdr)).unwrap(); - assert_eq!(hdr_tags, EXP_HDR); - } - -@@ -220,7 +220,7 @@ mod tests { - )); - - // mess up magic -- let mut bin_hdr_copy = bin_hdr.clone(); -+ let mut bin_hdr_copy = *bin_hdr; - bin_hdr_copy.swap(0, 1); - assert!(matches!( - BootHdrTags::from_se_image(&mut Cursor::new(bin_hdr_copy)), -diff --git a/rust/pv/src/crypto.rs b/rust/pv/src/crypto.rs -index 910419e..8132e9d 100644 ---- a/rust/pv/src/crypto.rs -+++ b/rust/pv/src/crypto.rs -@@ -315,7 +315,7 @@ mod tests { - ]; - - let res = encrypt_aes( -- &&SymKey::Aes256(aes_gcm_key.into()), -+ &SymKey::Aes256(aes_gcm_key.into()), - &aes_gcm_iv, - &aes_gcm_plain, - ) -diff --git a/rust/pv/src/req.rs b/rust/pv/src/req.rs -index cda448b..b9acd73 100644 ---- a/rust/pv/src/req.rs -+++ b/rust/pv/src/req.rs -@@ -451,7 +451,7 @@ mod tests { - let ks = vec![ - Keyslot::new(host_key.clone()), - Keyslot::new(host_key.clone()), -- Keyslot::new(host_key.clone()), -+ Keyslot::new(host_key), - ]; - let mut aad = Vec::::new(); - ks.iter().for_each(|ks| aad.push(Aad::Ks(ks))); -diff --git a/rust/pv/src/uvsecret/guest_secret.rs b/rust/pv/src/uvsecret/guest_secret.rs -index 8ad4fa5..8c44421 100644 ---- a/rust/pv/src/uvsecret/guest_secret.rs -+++ b/rust/pv/src/uvsecret/guest_secret.rs -@@ -105,7 +105,7 @@ mod test { - 0xef, 0xc7, 0x3c, 0x62, - ]; - let name = "association secret".to_string(); -- let secret = GuestSecret::association("association secret", secret_value.clone()).unwrap(); -+ let secret = GuestSecret::association("association secret", secret_value).unwrap(); - let exp = GuestSecret::Association { - name, - id: exp_id, -diff --git a/rust/pv/src/uvsecret/secret_list.rs b/rust/pv/src/uvsecret/secret_list.rs -index 724702a..6943bd3 100644 ---- a/rust/pv/src/uvsecret/secret_list.rs -+++ b/rust/pv/src/uvsecret/secret_list.rs -@@ -117,7 +117,7 @@ impl Display for SecretEntry { - writeln!(f, "{} {}:", self.index, stype_str(self.stype.get()))?; - write!(f, " ")?; - for b in self.id { -- write!(f, "{:02x}", b)?; -+ write!(f, "{b:02x}")?; - } - Ok(()) - } -diff --git a/rust/pv/src/verify/helper.rs b/rust/pv/src/verify/helper.rs -index 89f92b8..a2f313b 100644 ---- a/rust/pv/src/verify/helper.rs -+++ b/rust/pv/src/verify/helper.rs -@@ -484,20 +484,20 @@ mod test { - let ibm_wrong_subj = load_gen_cert("ibm_wrong_subject.crt"); - let no_sign_crt = load_gen_cert("inter_ca.crt"); - -- assert!(super::get_ibm_z_sign_key(&vec!(ibm_crt.clone())).is_ok()); -+ assert!(super::get_ibm_z_sign_key(&[ibm_crt.clone()]).is_ok()); - assert!(matches!( -- super::get_ibm_z_sign_key(&vec!(ibm_crt.clone(), ibm_crt.clone())), -+ super::get_ibm_z_sign_key(&[ibm_crt.clone(), ibm_crt.clone()]), - Err(Error::HkdVerify(ManyIbmSignKeys)) - )); - assert!(matches!( -- super::get_ibm_z_sign_key(&vec!(ibm_wrong_subj)), -+ super::get_ibm_z_sign_key(&[ibm_wrong_subj]), - Err(Error::HkdVerify(NoIbmSignKey)) - )); - assert!(matches!( -- super::get_ibm_z_sign_key(&vec!(no_sign_crt.clone())), -+ super::get_ibm_z_sign_key(&[no_sign_crt.clone()]), - Err(Error::HkdVerify(NoIbmSignKey)) - )); -- assert!(super::get_ibm_z_sign_key(&vec!(ibm_crt.clone(), no_sign_crt.clone())).is_ok(),); -+ assert!(super::get_ibm_z_sign_key(&[ibm_crt, no_sign_crt]).is_ok(),); - } - - #[test] -diff --git a/rust/pv/src/verify/test.rs b/rust/pv/src/verify/test.rs -index 8a6258d..1c0d2b5 100644 ---- a/rust/pv/src/verify/test.rs -+++ b/rust/pv/src/verify/test.rs -@@ -26,7 +26,7 @@ fn verify_sign_error(exp_raw: libc::c_int, obs: Error) { - } - fn verify_sign_error_slice(exp_raw: &[libc::c_int], obs: Error) { - if exp_raw -- .into_iter() -+ .iter() - .filter(|e| match &obs { - Error::HkdVerify(ty) => match ty { - IbmSignInvalid(err, _d) => &&err.as_raw() == e, -@@ -51,7 +51,7 @@ fn store_setup() { - let ibm_str = get_cert_asset_path_string("ibm.crt"); - let inter_str = get_cert_asset_path_string("inter.crt"); - -- let store = helper::store_setup(&None, &vec![], &vec![ibm_str, inter_str]); -+ let store = helper::store_setup(&None, &[], &[ibm_str, inter_str]); - assert!(store.is_ok()); - } - -@@ -63,7 +63,7 @@ fn verify_chain_online() { - - let mock_inter = mock_endpt("inter_ca.crl"); - -- let mut store = helper::store_setup(&Some(root_crt), &vec![], &vec![]).unwrap(); -+ let mut store = helper::store_setup(&Some(root_crt), &[], &[]).unwrap(); - download_crls_into_store(&mut store, slice::from_ref(&ibm_crt)).unwrap(); - let store = store.build(); - -@@ -71,8 +71,8 @@ fn verify_chain_online() { - - let mut sk = Stack::::new().unwrap(); - sk.push(inter_crt).unwrap(); -- verify_chain(&store, &sk, &vec![ibm_crt.clone()]).unwrap(); -- assert!(verify_chain(&store, &sk, &vec!(ibm_crt)).is_ok()); -+ verify_chain(&store, &sk, &[ibm_crt.clone()]).unwrap(); -+ assert!(verify_chain(&store, &sk, &[ibm_crt]).is_ok()); - } - - #[test] -@@ -82,13 +82,13 @@ fn verify_chain_offline() { - let inter_crt = load_gen_cert("inter_ca.crt"); - let root_crt = get_cert_asset_path_string("root_ca.chained.crt"); - -- let store = helper::store_setup(&Some(root_crt), &vec![inter_crl], &vec![]) -+ let store = helper::store_setup(&Some(root_crt), &[inter_crl], &[]) - .unwrap() - .build(); - - let mut sk = Stack::::new().unwrap(); - sk.push(inter_crt).unwrap(); -- assert!(verify_chain(&store, &sk, &vec![ibm_crt]).is_ok()); -+ assert!(verify_chain(&store, &sk, &[ibm_crt]).is_ok()); - } - - #[test] -@@ -107,8 +107,8 @@ fn verify_online() { - let inter_crl = get_cert_asset_path_string("inter_ca.crl"); - let ibm_crl = get_cert_asset_path_string("ibm.crl"); - let verifier = CertVerifier::new( -- &vec![ibm_crt, inter_crt], -- &vec![ibm_crl, inter_crl], -+ &[ibm_crt, inter_crt], -+ &[ibm_crl, inter_crl], - &Some(root_crt), - false, - ) -@@ -148,8 +148,8 @@ fn verify_offline() { - let hkd = load_gen_cert("host.crt"); - - let verifier = CertVerifier::new( -- &vec![ibm_crt, inter_crt], -- &vec![ibm_crl, inter_crl], -+ &[ibm_crt, inter_crt], -+ &[ibm_crl, inter_crl], - &Some(root_crt), - true, - ) -@@ -186,25 +186,20 @@ fn verifier_new() { - let ibm_rev_crt = get_cert_asset_path_string("ibm_rev.crt"); - - // To many signing keys -- let verifier = CertVerifier::new( -- &vec![ibm_crt.clone(), ibm_rev_crt.clone()], -- &vec![], -- &None, -- true, -- ); -+ let verifier = CertVerifier::new(&[ibm_crt.clone(), ibm_rev_crt.clone()], &[], &None, true); - assert!(matches!(verifier, Err(Error::HkdVerify(ManyIbmSignKeys)))); - - // no CRL for each X509 - let verifier = CertVerifier::new( -- &vec![inter_crt.clone(), ibm_crt.clone()], -- &vec![inter_crl.clone()], -- &Some(root_crt.clone()), -+ &[inter_crt.clone(), ibm_crt.clone()], -+ &[inter_crl.clone()], -+ &Some(root_crt), - false, - ); - verify_sign_error(3, verifier.unwrap_err()); - let verifier = CertVerifier::new( -- &vec![inter_crt.clone(), ibm_crt.clone()], -- &vec![], -+ &[inter_crt.clone(), ibm_crt.clone()], -+ &[], - &Some(root_chn_crt.clone()), - false, - ); -@@ -212,8 +207,8 @@ fn verifier_new() { - - // wrong intermediate (or ibm key) - let verifier = CertVerifier::new( -- &vec![inter_fake_crt, ibm_crt.clone()], -- &vec![inter_fake_crl], -+ &[inter_fake_crt, ibm_crt.clone()], -+ &[inter_fake_crl], - &Some(root_chn_crt.clone()), - true, - ); -@@ -222,8 +217,8 @@ fn verifier_new() { - - //wrong root ca - let verifier = CertVerifier::new( -- &vec![inter_crt.clone(), ibm_crt.clone()], -- &vec![inter_crl.clone()], -+ &[inter_crt.clone(), ibm_crt.clone()], -+ &[inter_crl.clone()], - &None, - true, - ); -@@ -231,33 +226,28 @@ fn verifier_new() { - - //correct signing key + intermediate cert - let _verifier = CertVerifier::new( -- &vec![inter_crt.clone(), ibm_crt.clone()], -- &vec![inter_crl.clone()], -+ &[inter_crt.clone(), ibm_crt.clone()], -+ &[inter_crl.clone()], - &Some(root_chn_crt.clone()), - false, - ) - .unwrap(); - - // no intermediate key -- let verifier = CertVerifier::new( -- &vec![ibm_crt.clone()], -- &vec![], -- &Some(root_chn_crt.clone()), -- false, -- ); -+ let verifier = CertVerifier::new(&[ibm_crt], &[], &Some(root_chn_crt.clone()), false); - verify_sign_error(20, verifier.unwrap_err()); - - //Ibm Sign outdated - let verifier = CertVerifier::new( -- &vec![inter_crt.clone(), ibm_early_crt.clone()], -- &vec![inter_crl.clone()], -+ &[inter_crt.clone(), ibm_early_crt], -+ &[inter_crl.clone()], - &Some(root_chn_crt.clone()), - false, - ); - assert!(matches!(verifier, Err(Error::HkdVerify(NoIbmSignKey)))); - let verifier = CertVerifier::new( -- &vec![inter_crt.clone(), ibm_late_crt.clone()], -- &vec![inter_crl.clone()], -+ &[inter_crt.clone(), ibm_late_crt], -+ &[inter_crl.clone()], - &Some(root_chn_crt.clone()), - false, - ); -@@ -265,9 +255,9 @@ fn verifier_new() { - - // revoked - let verifier = CertVerifier::new( -- &vec![inter_crt.clone(), ibm_rev_crt.clone()], -- &vec![inter_crl.clone()], -- &Some(root_chn_crt.clone()), -+ &[inter_crt, ibm_rev_crt], -+ &[inter_crl], -+ &Some(root_chn_crt), - false, - ); - verify_sign_error(23, verifier.unwrap_err()); -diff --git a/rust/pv/tests/add_secret_request.rs b/rust/pv/tests/add_secret_request.rs -index 2b3e861..bf491c6 100644 ---- a/rust/pv/tests/add_secret_request.rs -+++ b/rust/pv/tests/add_secret_request.rs -@@ -18,7 +18,7 @@ use pv::{ - const TAGS: BootHdrTags = BootHdrTags::new([1; 64], [2; 64], [3; 64], [4; 16]); - const CUID: ConfigUid = [0x42u8; 16]; - const ASSOC_SECRET: [u8; 32] = [0x11; 32]; --const ASSOC_ID: &'static str = "add_secret_request"; -+const ASSOC_ID: &str = "add_secret_request"; - - fn create_asrcb( - guest_secret: GuestSecret, -@@ -38,7 +38,7 @@ fn create_asrcb( - }; - - asrcb.add_hostkey(hkd); -- Ok(asrcb.encrypt(ctx)?) -+ asrcb.encrypt(ctx) - } - - fn get_crypto() -> (PKey, ReqEncrCtx) { -@@ -63,17 +63,10 @@ where - { - let (host_key, ctx) = get_crypto(); - let cuid = match cuid { -- true => Some(CUID.into()), -+ true => Some(CUID), - false => None, - }; -- create_asrcb( -- guest_secret, -- ext_secret.into(), -- flags, -- cuid.into(), -- host_key, -- &ctx, -- ) -+ create_asrcb(guest_secret, ext_secret.into(), flags, cuid, host_key, &ctx) - } - - fn association() -> GuestSecret { -@@ -156,7 +149,7 @@ fn null_none_default_cuid_seven() { - let mut asrcb = - AddSecretRequest::new(AddSecretVersion::One, GuestSecret::Null, TAGS, no_flag()); - (0..7).for_each(|_| asrcb.add_hostkey(hkd.clone())); -- asrcb.set_cuid(CUID.into()); -+ asrcb.set_cuid(CUID); - let asrcb = asrcb.encrypt(&ctx).unwrap(); - - let exp = get_test_asset!("exp/asrcb/null_none_default_cuid_seven"); --- -2.43.0 - - -From 4c8072cebe9add441c42e62663d4089d14d32389 Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Wed, 25 Oct 2023 15:26:14 +0200 -Subject: [PATCH 05/13] rust/pv: fix Invalid write of size 1 - -Fix a valgrind finding. Fix an invalid read/write of one byte after the -actual struct to clear. Not fixing this may result in a illegal write or -memory corruption of the program. Fortunately, for the actual only user, -pvsecret this is not the case. - -Fixes: c6f621d0 ("rust: Add library for pv tools") -Signed-off-by: Steffen Eiden -(cherry picked from commit 71b93d55effae6a74bbbbafe7057e1f97d692a74) ---- - rust/pv/src/secret.rs | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/rust/pv/src/secret.rs b/rust/pv/src/secret.rs -index cdef9ef..88287c8 100644 ---- a/rust/pv/src/secret.rs -+++ b/rust/pv/src/secret.rs -@@ -34,8 +34,8 @@ impl Zeroize for Vec { - // * Vec allocated at least capacity elements continuously - // * dst points always to a valid location - unsafe { -+ std::ptr::write_volatile(dst, 0); - dst = dst.add(1); -- std::ptr::write_volatile(dst, 0) - } - } - std::sync::atomic::compiler_fence(std::sync::atomic::Ordering::SeqCst); --- -2.43.0 - - -From 49eabe2d13ea3909f4c522fefaf8db998c7ab888 Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Wed, 4 Oct 2023 10:59:34 +0200 -Subject: [PATCH 06/13] rust: Create workspace - -A workspaces simplifies the build and packaging process significantly. -All build artifacts and binaries are now built in a single location -(e.g., rust/target/release/*), and a unified dependency resolution is -used. Hence one Cargo.lock for all crates at rust/Cargo.lock. - -Closes: https://github.com/ibm-s390-linux/s390-tools/issues/156 -Reviewed-by: Marc Hartmayer -Signed-off-by: Steffen Eiden -(cherry picked from commit 32b68a5fad652589c85eae098e799563b88864b5) ---- - rust/Cargo.toml | 15 ++++++++++ - rust/Makefile | 43 +++++++++++---------------- - rust/README.md | 20 +++++++++++-- - rust/pv/Cargo.toml | 8 ++--- - rust/pv/openssl_extensions/Cargo.toml | 4 +-- - rust/pvsecret/Cargo.toml | 8 ++--- - rust/utils/Cargo.toml | 4 +-- - 7 files changed, 59 insertions(+), 43 deletions(-) - create mode 100644 rust/Cargo.toml - -diff --git a/rust/Cargo.toml b/rust/Cargo.toml -new file mode 100644 -index 0000000..65a70a9 ---- /dev/null -+++ b/rust/Cargo.toml -@@ -0,0 +1,15 @@ -+[workspace] -+members = [ -+ "pv", -+ "pvsecret", -+ "utils", -+] -+resolver = "2" -+ -+[workspace.package] -+edition = "2021" -+license = "MIT" -+ -+[profile.release] -+lto = true -+panic = "abort" -diff --git a/rust/Makefile b/rust/Makefile -index 420bafd..e4e9885 100644 ---- a/rust/Makefile -+++ b/rust/Makefile -@@ -15,16 +15,17 @@ ifneq (${HAVE_CARGO},0) - - BUILD_TARGETS = $(CARGO_TARGETS) - INSTALL_TARGETS := install-rust-tools install-man -- CARGO_TEST_TARGETS = $(addsuffix, _test, $(CARGO_TARGETS)) -+ CARGO_TEST_TARGETS = $(addsuffix .test, $(CARGO_TARGETS)) - - ifneq (${HAVE_OPENSSL},0) - ifneq (${HAVE_LIBCURL},0) - PV_TARGETS := pvsecret - -- PV_BUILD_TARGETS = $(PV_TARGETS) -- CARGO_TEST_TARGETS += $(addsuffix, _test, $(PV_TARGETS)) pv -+ PV_BUILD_TARGETS := $(PV_TARGETS) -+ CARGO_TEST_TARGETS += $(addsuffix .test,pv $(PV_TARGETS)) - endif #LIBCURL - endif #OPENSSL -+ TEST_TARGETS := $(addsuffix _build,$(CARGO_TEST_TARGETS)) - endif #CARGO - - BUILD_TARGETS += $(PV_BUILD_TARGETS) -@@ -39,18 +40,15 @@ endif - $(BUILD_TARGETS) rust-test: CC = $(CC_SILENT) - $(BUILD_TARGETS) rust-test: AR = $(AR_SILENT) - --$(CARGO_TARGETS): .check-cargo .no-cross-compile -- $(CARGO_BUILD) --manifest-path=$@/Cargo.toml $(ALL_CARGOFLAGS) --.PHONY: $(CARGO_TARGETS) -+$(PV_TARGETS): .check-dep-pvtools -+$(PV_TARGETS) $(CARGO_TARGETS): .check-cargo .no-cross-compile -+ $(CARGO_BUILD) --bin $@ $(ALL_CARGOFLAGS) -+.PHONY: $(PV_TARGETS) $(CARGO_TARGETS) - -- --$(CARGO_TEST_TARGETS): .check-cargo .no-cross-compile -- $(CARGO_TEST) --manifest-path=$@/Cargo.toml --all-features $(CARGOFLAGS) --.PHONY: $(CARGO_TEST_TARGETS) -- --$(PV_TARGETS): .check-cargo .no-cross-compile .check-dep-pvtools -- $(CARGO_BUILD) --manifest-path=$@/Cargo.toml $(ALL_CARGOFLAGS) --.PHONY: $(PV_TARGETS) -+$(TEST_TARGETS): ALL_CARGOFLAGS += --no-run -+$(CARGO_TEST_TARGETS) $(TEST_TARGETS): .check-cargo .no-cross-compile -+ $(CARGO_TEST) --package $(basename $@) --all-features $(ALL_CARGOFLAGS) -+.PHONY: $(TEST_TARGETS) $(CARGO_TEST_TARGETS) - - skip-build: - echo " SKIP rust-tools due to unresolved dependencies" -@@ -65,22 +63,17 @@ print-rust-targets: - echo $(BUILD_TARGETS) - - clean: -- $(foreach target,$(CARGO_TARGETS),\ -- $(CARGO_CLEAN) --manifest-path=$(target)/Cargo.toml ${ALL_CARGOFLAGS} ;) -- $(foreach target,$(PV_TARGETS),\ -- $(CARGO_CLEAN) --manifest-path=$(target)/Cargo.toml ${ALL_CARGOFLAGS} ;) -+ $(CARGO_CLEAN) ${ALL_CARGOFLAGS} - $(RM) -- .check-dep-pvtools .detect-openssl.dep.c .check-cargo - --rust-test: .check-cargo .no-cross-compile -- $(foreach target,$(CARGO_TEST_TARGETS),\ -- $(CARGO_TEST) --manifest-path=$(target)/Cargo.toml --all-features ${ALL_CARGOFLAGS} ;) -+rust-test: $(CARGO_TEST_TARGETS) - - install-rust-tools: $(BUILD_TARGETS) - $(INSTALL) -d -m 755 $(DESTDIR)$(USRBINDIR) - $(foreach target,$(CARGO_TARGETS),\ -- $(INSTALL) $(target)/target/release/$(target) $(DESTDIR)$(USRBINDIR);) -+ $(INSTALL) target/release/$(target) $(DESTDIR)$(USRBINDIR);) - $(foreach target,$(PV_TARGETS),\ -- $(INSTALL) $(target)/target/release/$(target) $(DESTDIR)$(USRBINDIR);) -+ $(INSTALL) target/release/$(target) $(DESTDIR)$(USRBINDIR);) - - install-man: - $(foreach target,$(CARGO_TARGETS),\ -@@ -118,13 +111,13 @@ endif - - .check-dep-pvtools: .detect-openssl.dep.c - $(call check_dep, \ -- "$(BIN_PROGRAM)", \ -+ "Rust-pv", \ - $^, \ - "openssl-devel / libssl-dev version >= 1.1.1", \ - "HAVE_OPENSSL=0", \ - "-I.") - $(call check_dep, \ -- "$(BIN_PROGRAM)", \ -+ "Rust-pv", \ - "curl/curl.h", \ - "libcurl-devel", \ - "HAVE_LIBCURL=0") -diff --git a/rust/README.md b/rust/README.md -index 61b0af8..16603bf 100644 ---- a/rust/README.md -+++ b/rust/README.md -@@ -81,11 +81,27 @@ is a start, but can change over time. - Dependencies used by the crates listed above can be used, too. - - ### Add new tool --To add a new tool issue `cargo new ` in the `rust` directory. -+To add a new tool issue `cargo new $TOOLNAME` in the `rust` directory. - - Add the tool to the _s390-tools_ build system: - ```Makefile --CARGO_TARGETS := TOOLNAME -+CARGO_TARGETS := $TOOLNAME -+``` -+Add the library to the _s390-tools_ test list: -+```Makefile -+CARGO_TEST_TARGETS := $LIBNAME -+``` -+ -+Add the tool/library to the cargo workspace: -+```toml -+[workspace] -+members = [ -+ "pv", -+ "pvsecret", -+ "$TOOLNAME", -+ "$LIBNAME" -+ "utils", -+] - ``` - - ### Versions -diff --git a/rust/pv/Cargo.toml b/rust/pv/Cargo.toml -index 689d624..825386a 100644 ---- a/rust/pv/Cargo.toml -+++ b/rust/pv/Cargo.toml -@@ -1,8 +1,8 @@ - [package] - name = "pv" - version = "0.9.0" --edition = "2021" --license = "MIT" -+edition.workspace = true -+license.workspace = true - - [dependencies] - libc = "0.2" -@@ -30,7 +30,3 @@ lazy_static = "1" - default = [] - request = ["dep:openssl", "dep:curl", "dep:openssl_extensions", "dep:serde", "dep:clap"] - uvsecret = ["dep:byteorder", "dep:serde"] -- --[profile.release] --lto = true --panic = "abort" # release builds now do not clean up stack after panics. .1 Mb -diff --git a/rust/pv/openssl_extensions/Cargo.toml b/rust/pv/openssl_extensions/Cargo.toml -index 17d4c0e..90907ce 100644 ---- a/rust/pv/openssl_extensions/Cargo.toml -+++ b/rust/pv/openssl_extensions/Cargo.toml -@@ -1,8 +1,8 @@ - [package] - name = "openssl_extensions" - version = "0.1.0" --edition = "2021" --license = "MIT" -+edition.workspace = true -+license.workspace = true - - [dependencies] - foreign-types = "0.3" -diff --git a/rust/pvsecret/Cargo.toml b/rust/pvsecret/Cargo.toml -index da4deab..d1e75b1 100644 ---- a/rust/pvsecret/Cargo.toml -+++ b/rust/pvsecret/Cargo.toml -@@ -1,8 +1,8 @@ - [package] - name = "pvsecret" - version = "0.9.0" --edition = "2021" --license = "MIT" -+edition.workspace = true -+license.workspace = true - - [dependencies] - anyhow = { version = "1", features = ["std"] } -@@ -12,7 +12,3 @@ serde_yaml = "0.9" - - pv = { path = "../pv", features = ["uvsecret", "request"] } - utils = { path = "../utils" } -- --[profile.release] --lto = true --panic = "abort" # release builds now do not clean up stack after panics. .1 Mb -diff --git a/rust/utils/Cargo.toml b/rust/utils/Cargo.toml -index 30bbbc8..215381b 100644 ---- a/rust/utils/Cargo.toml -+++ b/rust/utils/Cargo.toml -@@ -1,5 +1,5 @@ - [package] - name = "utils" - version = "0.1.0" --edition = "2021" --license = "MIT" -+edition.workspace = true -+license.workspace = true --- -2.43.0 - - -From be47ce72f4ee7dc7ed2dafb9b89079b0c2b154fa Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Wed, 4 Oct 2023 11:08:20 +0200 -Subject: [PATCH 07/13] rust: Update dependency files - -With the last patch introducing the rust workspace the location of -Cargo.lock has changed. Therefore, remove all crate level lock-files and -add rust/Cargo.lock as the only lock-file. - -Steps to reproduce: -``` -cd rust -mv pvsecret/Cargo.lock . -cargo build -cargo update -p openssl -cargo update -p curl-sys -cargo update -p rustix - -``` - -While at it update some dependencies to get fixes for security issues. - -Reviewed-by: Marc Hartmayer -Signed-off-by: Steffen Eiden -(cherry picked from commit d1b61c37fa78c95d0512b417aa21fbc66bddcd4b) ---- - rust/.gitignore | 3 - - rust/{pvsecret => }/Cargo.lock | 222 +++++++++++++++++++++++++++++++-- - 2 files changed, 210 insertions(+), 15 deletions(-) - rename rust/{pvsecret => }/Cargo.lock (77%) - -diff --git a/rust/.gitignore b/rust/.gitignore -index 4760f82..f9f3955 100644 ---- a/rust/.gitignore -+++ b/rust/.gitignore -@@ -9,6 +9,3 @@ target/ - # Generated during make build can be removed at any point - .check-dep-pvtools - .check-cargo -- --# Ignore lock files by default --Cargo.lock -diff --git a/rust/pvsecret/Cargo.lock b/rust/Cargo.lock -similarity index 77% -rename from rust/pvsecret/Cargo.lock -rename to rust/Cargo.lock -index 1db32c2..f7d1cf0 100644 ---- a/rust/pvsecret/Cargo.lock -+++ b/rust/Cargo.lock -@@ -2,6 +2,15 @@ - # It is not intended for manual editing. - version = 3 - -+[[package]] -+name = "aho-corasick" -+version = "1.1.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "b2969dcb958b36655471fc61f7e416fa76033bdd4bfed0678d8fee1e2d07a1f0" -+dependencies = [ -+ "memchr", -+] -+ - [[package]] - name = "anstream" - version = "0.3.2" -@@ -57,6 +66,16 @@ version = "1.0.71" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "9c7d0618f0e0b7e8ff11427422b64564d5fb0be1940354bfe2e0529b18a9d9b8" - -+[[package]] -+name = "assert-json-diff" -+version = "2.0.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "47e4f2b81832e72834d7518d8487a0396a28cc408186a2e8854c0f98011faf12" -+dependencies = [ -+ "serde", -+ "serde_json", -+] -+ - [[package]] - name = "autocfg" - version = "1.1.0" -@@ -69,6 +88,12 @@ version = "1.3.2" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" - -+[[package]] -+name = "bitflags" -+version = "2.4.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "327762f6e5a765692301e5bb513e0d9fef63be86bbc14528052b1cd3e6f03e07" -+ - [[package]] - name = "byteorder" - version = "1.4.3" -@@ -106,7 +131,7 @@ checksum = "c1458a1df40e1e2afebb7ab60ce55c1fa8f431146205aa5f4887e0b111c27636" - dependencies = [ - "anstream", - "anstyle", -- "bitflags", -+ "bitflags 1.3.2", - "clap_lex", - "strsim", - "terminal_size", -@@ -153,9 +178,9 @@ dependencies = [ - - [[package]] - name = "curl-sys" --version = "0.4.63+curl-8.1.2" -+version = "0.4.68+curl-8.4.0" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "aeb0fef7046022a1e2ad67a004978f0e3cacb9e3123dc62ce768f92197b771dc" -+checksum = "b4a0d18d88360e374b16b2273c832b5e57258ffc1d4aa4f96b108e0738d5752f" - dependencies = [ - "cc", - "libc", -@@ -163,7 +188,7 @@ dependencies = [ - "openssl-sys", - "pkg-config", - "vcpkg", -- "winapi", -+ "windows-sys 0.48.0", - ] - - [[package]] -@@ -202,6 +227,26 @@ version = "0.1.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "00b0228411908ca8685dba7fc2cdd70ec9990a6e753e89b6ac91a84c40fbaf4b" - -+[[package]] -+name = "form_urlencoded" -+version = "1.2.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "a62bc1cf6f830c2ec14a513a9fb124d0a213a629668a4186f329db21fe045652" -+dependencies = [ -+ "percent-encoding", -+] -+ -+[[package]] -+name = "getrandom" -+version = "0.2.10" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "be4136b2a15dd319360be1c07d9933517ccf0be8f16bf62a3bee4f0d618df427" -+dependencies = [ -+ "cfg-if", -+ "libc", -+ "wasi", -+] -+ - [[package]] - name = "hashbrown" - version = "0.12.3" -@@ -220,6 +265,12 @@ version = "0.3.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -+[[package]] -+name = "httparse" -+version = "1.8.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" -+ - [[package]] - name = "indexmap" - version = "1.9.3" -@@ -259,6 +310,12 @@ version = "1.0.6" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "453ad9f582a441959e5f0d088b02ce04cfe8d51a8eaf077f12ac6d3e94164ca6" - -+[[package]] -+name = "lazy_static" -+version = "1.4.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" -+ - [[package]] - name = "libc" - version = "0.2.146" -@@ -289,6 +346,29 @@ version = "0.4.19" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "b06a4cde4c0f271a446782e3eff8de789548ce57dbc8eca9292c27f4a42004b4" - -+[[package]] -+name = "memchr" -+version = "2.6.4" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" -+ -+[[package]] -+name = "mockito" -+version = "0.31.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "80f9fece9bd97ab74339fe19f4bcaf52b76dcc18e5364c7977c1838f76b38de9" -+dependencies = [ -+ "assert-json-diff", -+ "httparse", -+ "lazy_static", -+ "log", -+ "rand", -+ "regex", -+ "serde_json", -+ "serde_urlencoded", -+ "similar", -+] -+ - [[package]] - name = "once_cell" - version = "1.18.0" -@@ -297,11 +377,11 @@ checksum = "dd8b5dd2ae5ed71462c540258bedcb51965123ad7e7ccf4b9a8cafaa4a63576d" - - [[package]] - name = "openssl" --version = "0.10.54" -+version = "0.10.60" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "69b3f656a17a6cbc115b5c7a40c616947d213ba182135b014d6051b73ab6f019" -+checksum = "79a4c6c3a2b158f7f8f2a2fc5a969fa3a068df6fc9dbb4a43845436e3af7c800" - dependencies = [ -- "bitflags", -+ "bitflags 2.4.1", - "cfg-if", - "foreign-types", - "libc", -@@ -329,9 +409,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" - - [[package]] - name = "openssl-sys" --version = "0.9.88" -+version = "0.9.96" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "c2ce0f250f34a308dcfdbb351f511359857d4ed2134ba715a4eadd46e1ffd617" -+checksum = "3812c071ba60da8b5677cc12bcb1d42989a65553772897a7e0355545a819838f" - dependencies = [ - "cc", - "libc", -@@ -350,12 +430,24 @@ dependencies = [ - "openssl-sys", - ] - -+[[package]] -+name = "percent-encoding" -+version = "2.3.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" -+ - [[package]] - name = "pkg-config" - version = "0.3.27" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "26072860ba924cbfa98ea39c8c19b4dd6a4a25423dbdf219c1eca91aa0cf6964" - -+[[package]] -+name = "ppv-lite86" -+version = "0.2.17" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "5b40af805b3121feab8a3c29f04d8ad262fa8e0561883e7653e024ae4479e6de" -+ - [[package]] - name = "proc-macro2" - version = "1.0.60" -@@ -373,11 +465,14 @@ dependencies = [ - "cfg-if", - "clap", - "curl", -+ "lazy_static", - "libc", - "log", -+ "mockito", - "openssl", - "openssl_extensions", - "serde", -+ "serde_test", - "thiserror", - "zerocopy", - ] -@@ -403,13 +498,72 @@ dependencies = [ - "proc-macro2", - ] - -+[[package]] -+name = "rand" -+version = "0.8.5" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" -+dependencies = [ -+ "libc", -+ "rand_chacha", -+ "rand_core", -+] -+ -+[[package]] -+name = "rand_chacha" -+version = "0.3.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e6c10a63a0fa32252be49d21e7709d4d4baf8d231c2dbce1eaa8141b9b127d88" -+dependencies = [ -+ "ppv-lite86", -+ "rand_core", -+] -+ -+[[package]] -+name = "rand_core" -+version = "0.6.4" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" -+dependencies = [ -+ "getrandom", -+] -+ -+[[package]] -+name = "regex" -+version = "1.10.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "380b951a9c5e80ddfd6136919eef32310721aa4aacd4889a8d39124b026ab343" -+dependencies = [ -+ "aho-corasick", -+ "memchr", -+ "regex-automata", -+ "regex-syntax", -+] -+ -+[[package]] -+name = "regex-automata" -+version = "0.4.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "5f804c7828047e88b2d32e2d7fe5a105da8ee3264f01902f796c8e067dc2483f" -+dependencies = [ -+ "aho-corasick", -+ "memchr", -+ "regex-syntax", -+] -+ -+[[package]] -+name = "regex-syntax" -+version = "0.8.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" -+ - [[package]] - name = "rustix" --version = "0.37.20" -+version = "0.37.27" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "b96e891d04aa506a6d1f318d2771bcb1c7dfda84e126660ace067c9b474bb2c0" -+checksum = "fea8ca367a3a01fe35e6943c400addf443c0f57670e6ec51196f71a4b8762dd2" - dependencies = [ -- "bitflags", -+ "bitflags 1.3.2", - "errno", - "io-lifetimes", - "libc", -@@ -452,6 +606,38 @@ dependencies = [ - "syn 2.0.18", - ] - -+[[package]] -+name = "serde_json" -+version = "1.0.99" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "46266871c240a00b8f503b877622fe33430b3c7d963bdc0f2adc511e54a1eae3" -+dependencies = [ -+ "itoa", -+ "ryu", -+ "serde", -+] -+ -+[[package]] -+name = "serde_test" -+version = "1.0.176" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "5a2f49ace1498612d14f7e0b8245519584db8299541dfe31a06374a828d620ab" -+dependencies = [ -+ "serde", -+] -+ -+[[package]] -+name = "serde_urlencoded" -+version = "0.7.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d3491c14715ca2294c4d6a88f15e84739788c1d030eed8c110436aafdaa2f3fd" -+dependencies = [ -+ "form_urlencoded", -+ "itoa", -+ "ryu", -+ "serde", -+] -+ - [[package]] - name = "serde_yaml" - version = "0.9.21" -@@ -465,6 +651,12 @@ dependencies = [ - "unsafe-libyaml", - ] - -+[[package]] -+name = "similar" -+version = "2.3.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" -+ - [[package]] - name = "socket2" - version = "0.4.9" -@@ -561,6 +753,12 @@ version = "0.2.15" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -+[[package]] -+name = "wasi" -+version = "0.11.0+wasi-snapshot-preview1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" -+ - [[package]] - name = "winapi" - version = "0.3.9" --- -2.43.0 - - -From c25115c0d605c9c79efd8e17d4917a35603c0766 Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Tue, 21 Nov 2023 13:27:21 +0100 -Subject: [PATCH 08/13] rust: Sanitize minimal dependencies - -The crate dependencies were a bit to slack. Due to the rust dependency -resolver's strategy of always selecting the latest version this never -lead to any issues. - -This has no impact on the workspaces Cargo.lock - -Reviewed-by: Marc Hartmayer -Signed-off-by: Steffen Eiden -(cherry picked from commit 9019c6864a4d39f940994b3858cc331983c605ee) ---- - rust/pv/Cargo.toml | 16 ++++++++-------- - rust/pv/openssl_extensions/Cargo.toml | 10 +++++----- - rust/pvsecret/Cargo.toml | 4 ++-- - 3 files changed, 15 insertions(+), 15 deletions(-) - -diff --git a/rust/pv/Cargo.toml b/rust/pv/Cargo.toml -index 825386a..22d5fd3 100644 ---- a/rust/pv/Cargo.toml -+++ b/rust/pv/Cargo.toml -@@ -5,26 +5,26 @@ edition.workspace = true - license.workspace = true - - [dependencies] --libc = "0.2" --log = { version = "0.4", features = ["std", "release_max_level_debug"] } --thiserror = "1" -+libc = "0.2.49" -+log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } -+thiserror = "1.0.33" - zerocopy = "0.6" - cfg-if = "1.0.0" - - # dependencies for request feature - clap = { version ="4", features = ["derive", "wrap_help"], optional = true } --curl = { version ="0.4", optional = true } --openssl = {version = "0.10", optional = true } -+curl = { version ="0.4.7", optional = true } -+openssl = {version = "0.10.49", optional = true } - openssl_extensions = { path = "openssl_extensions", optional = true } --serde = { version = "1", features = ["derive"], optional = true } -+serde = { version = "1.0.139", features = ["derive"], optional = true } - - # misc optional dependencies --byteorder = {version = "1", optional = true } -+byteorder = {version = "1.3", optional = true } - - [dev-dependencies] - mockito = {version = "0.31", default-features = false } - serde_test = "1" --lazy_static = "1" -+lazy_static = "1.1" - - [features] - default = [] -diff --git a/rust/pv/openssl_extensions/Cargo.toml b/rust/pv/openssl_extensions/Cargo.toml -index 90907ce..7bdeece 100644 ---- a/rust/pv/openssl_extensions/Cargo.toml -+++ b/rust/pv/openssl_extensions/Cargo.toml -@@ -5,8 +5,8 @@ edition.workspace = true - license.workspace = true - - [dependencies] --foreign-types = "0.3" --libc = {version = "0.2", features = [ "extra_traits"] } --log = { version = "0.4", features = ["std", "release_max_level_debug"] } --openssl = "0.10" --openssl-sys = "0.9" -+foreign-types = "0.3.1" -+libc = {version = "0.2.49", features = [ "extra_traits"] } -+log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } -+openssl = "0.10.49" -+openssl-sys = "0.9.85" -diff --git a/rust/pvsecret/Cargo.toml b/rust/pvsecret/Cargo.toml -index d1e75b1..e236c00 100644 ---- a/rust/pvsecret/Cargo.toml -+++ b/rust/pvsecret/Cargo.toml -@@ -5,9 +5,9 @@ edition.workspace = true - license.workspace = true - - [dependencies] --anyhow = { version = "1", features = ["std"] } -+anyhow = { version = "1.0.70", features = ["std"] } - clap = { version ="4", features = ["derive", "wrap_help"]} --log = { version = "0.4", features = ["std", "release_max_level_debug"] } -+log = { version = "0.4.6", features = ["std", "release_max_level_debug"] } - serde_yaml = "0.9" - - pv = { path = "../pv", features = ["uvsecret", "request"] } --- -2.43.0 - - -From b6009c80b112ad85ca2aa649126b913af5af253c Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Wed, 29 Nov 2023 17:06:50 +0100 -Subject: [PATCH 09/13] rust: Use default panic behaviour - -Reviewed-by: Marc Hartmayer -Signed-off-by: Steffen Eiden -(cherry picked from commit ae0cbf00b11bfe195c9e6e3b4168a3dccacde983) ---- - rust/Cargo.toml | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/rust/Cargo.toml b/rust/Cargo.toml -index 65a70a9..7ba1faa 100644 ---- a/rust/Cargo.toml -+++ b/rust/Cargo.toml -@@ -12,4 +12,3 @@ license = "MIT" - - [profile.release] - lto = true --panic = "abort" --- -2.43.0 - - -From c4e48d060b7d92d7c6cd150728ecb55b301afa62 Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Thu, 30 Nov 2023 16:02:16 +0100 -Subject: [PATCH 10/13] rust/pv: Update mockito to version 1 - -Signed-off-by: Steffen Eiden -(cherry picked from commit 21662d38e68b58bad033cdb1fca99987dd07cf78) ---- - rust/Cargo.lock | 468 ++++++++++++++++++++++++++++++++++++- - rust/pv/Cargo.toml | 2 +- - rust/pv/src/verify/test.rs | 4 +- - 3 files changed, 465 insertions(+), 9 deletions(-) - -diff --git a/rust/Cargo.lock b/rust/Cargo.lock -index f7d1cf0..067be05 100644 ---- a/rust/Cargo.lock -+++ b/rust/Cargo.lock -@@ -2,6 +2,21 @@ - # It is not intended for manual editing. - version = 3 - -+[[package]] -+name = "addr2line" -+version = "0.21.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -+dependencies = [ -+ "gimli", -+] -+ -+[[package]] -+name = "adler" -+version = "1.0.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -+ - [[package]] - name = "aho-corasick" - version = "1.1.2" -@@ -82,6 +97,21 @@ version = "1.1.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa" - -+[[package]] -+name = "backtrace" -+version = "0.3.69" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "2089b7e3f35b9dd2d0ed921ead4f6d318c27680d4a5bd167b3ee120edb105837" -+dependencies = [ -+ "addr2line", -+ "cc", -+ "cfg-if", -+ "libc", -+ "miniz_oxide", -+ "object", -+ "rustc-demangle", -+] -+ - [[package]] - name = "bitflags" - version = "1.3.2" -@@ -100,6 +130,12 @@ version = "1.4.3" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" - -+[[package]] -+name = "bytes" -+version = "1.5.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "a2bd12c1caf447e69cd4528f47f94d203fd2582878ecb9e9465484c4148a8223" -+ - [[package]] - name = "cc" - version = "1.0.79" -@@ -172,7 +208,7 @@ dependencies = [ - "openssl-probe", - "openssl-sys", - "schannel", -- "socket2", -+ "socket2 0.4.9", - "winapi", - ] - -@@ -191,6 +227,12 @@ dependencies = [ - "windows-sys 0.48.0", - ] - -+[[package]] -+name = "equivalent" -+version = "1.0.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" -+ - [[package]] - name = "errno" - version = "0.3.1" -@@ -212,6 +254,12 @@ dependencies = [ - "libc", - ] - -+[[package]] -+name = "fnv" -+version = "1.0.7" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" -+ - [[package]] - name = "foreign-types" - version = "0.3.2" -@@ -236,6 +284,95 @@ dependencies = [ - "percent-encoding", - ] - -+[[package]] -+name = "futures" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "da0290714b38af9b4a7b094b8a37086d1b4e61f2df9122c3cad2577669145335" -+dependencies = [ -+ "futures-channel", -+ "futures-core", -+ "futures-executor", -+ "futures-io", -+ "futures-sink", -+ "futures-task", -+ "futures-util", -+] -+ -+[[package]] -+name = "futures-channel" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "ff4dd66668b557604244583e3e1e1eada8c5c2e96a6d0d6653ede395b78bbacb" -+dependencies = [ -+ "futures-core", -+ "futures-sink", -+] -+ -+[[package]] -+name = "futures-core" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "eb1d22c66e66d9d72e1758f0bd7d4fd0bee04cad842ee34587d68c07e45d088c" -+ -+[[package]] -+name = "futures-executor" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "0f4fb8693db0cf099eadcca0efe2a5a22e4550f98ed16aba6c48700da29597bc" -+dependencies = [ -+ "futures-core", -+ "futures-task", -+ "futures-util", -+] -+ -+[[package]] -+name = "futures-io" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8bf34a163b5c4c52d0478a4d757da8fb65cabef42ba90515efee0f6f9fa45aaa" -+ -+[[package]] -+name = "futures-macro" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "53b153fd91e4b0147f4aced87be237c98248656bb01050b96bf3ee89220a8ddb" -+dependencies = [ -+ "proc-macro2", -+ "quote", -+ "syn 2.0.18", -+] -+ -+[[package]] -+name = "futures-sink" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e36d3378ee38c2a36ad710c5d30c2911d752cb941c00c72dbabfb786a7970817" -+ -+[[package]] -+name = "futures-task" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "efd193069b0ddadc69c46389b740bbccdd97203899b48d09c5f7969591d6bae2" -+ -+[[package]] -+name = "futures-util" -+version = "0.3.29" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "a19526d624e703a3179b3d322efec918b6246ea0fa51d41124525f00f1cc8104" -+dependencies = [ -+ "futures-channel", -+ "futures-core", -+ "futures-io", -+ "futures-macro", -+ "futures-sink", -+ "futures-task", -+ "memchr", -+ "pin-project-lite", -+ "pin-utils", -+ "slab", -+] -+ - [[package]] - name = "getrandom" - version = "0.2.10" -@@ -247,12 +384,43 @@ dependencies = [ - "wasi", - ] - -+[[package]] -+name = "gimli" -+version = "0.28.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" -+ -+[[package]] -+name = "h2" -+version = "0.3.22" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4d6250322ef6e60f93f9a2162799302cd6f68f79f6e5d85c8c16f14d1d958178" -+dependencies = [ -+ "bytes", -+ "fnv", -+ "futures-core", -+ "futures-sink", -+ "futures-util", -+ "http", -+ "indexmap 2.1.0", -+ "slab", -+ "tokio", -+ "tokio-util", -+ "tracing", -+] -+ - [[package]] - name = "hashbrown" - version = "0.12.3" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" - -+[[package]] -+name = "hashbrown" -+version = "0.14.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604" -+ - [[package]] - name = "heck" - version = "0.4.1" -@@ -265,12 +433,64 @@ version = "0.3.1" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "fed44880c466736ef9a5c5b5facefb5ed0785676d0c02d612db14e54f0d84286" - -+[[package]] -+name = "http" -+version = "0.2.11" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8947b1a6fad4393052c7ba1f4cd97bed3e953a95c79c92ad9b051a04611d9fbb" -+dependencies = [ -+ "bytes", -+ "fnv", -+ "itoa", -+] -+ -+[[package]] -+name = "http-body" -+version = "0.4.5" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d5f38f16d184e36f2408a55281cd658ecbd3ca05cce6d6510a176eca393e26d1" -+dependencies = [ -+ "bytes", -+ "http", -+ "pin-project-lite", -+] -+ - [[package]] - name = "httparse" - version = "1.8.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "d897f394bad6a705d5f4104762e116a75639e470d80901eed05a860a95cb1904" - -+[[package]] -+name = "httpdate" -+version = "1.0.3" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" -+ -+[[package]] -+name = "hyper" -+version = "0.14.27" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "ffb1cfd654a8219eaef89881fdb3bb3b1cdc5fa75ded05d6933b2b382e395468" -+dependencies = [ -+ "bytes", -+ "futures-channel", -+ "futures-core", -+ "futures-util", -+ "h2", -+ "http", -+ "http-body", -+ "httparse", -+ "httpdate", -+ "itoa", -+ "pin-project-lite", -+ "socket2 0.4.9", -+ "tokio", -+ "tower-service", -+ "tracing", -+ "want", -+] -+ - [[package]] - name = "indexmap" - version = "1.9.3" -@@ -278,7 +498,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "bd070e393353796e801d209ad339e89596eb4c8d430d18ede6a1cced8fafbd99" - dependencies = [ - "autocfg", -- "hashbrown", -+ "hashbrown 0.12.3", -+] -+ -+[[package]] -+name = "indexmap" -+version = "2.1.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d530e1a18b1cb4c484e6e34556a0d948706958449fca0cab753d649f2bce3d1f" -+dependencies = [ -+ "equivalent", -+ "hashbrown 0.14.3", - ] - - [[package]] -@@ -340,6 +570,16 @@ version = "0.3.8" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "ef53942eb7bf7ff43a617b3e2c1c4a5ecf5944a7c1bc12d7ee39bbb15e5c1519" - -+[[package]] -+name = "lock_api" -+version = "0.4.11" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "3c168f8615b12bc01f9c17e2eb0cc07dcae1940121185446edc3744920e8ef45" -+dependencies = [ -+ "autocfg", -+ "scopeguard", -+] -+ - [[package]] - name = "log" - version = "0.4.19" -@@ -352,21 +592,61 @@ version = "2.6.4" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167" - -+[[package]] -+name = "miniz_oxide" -+version = "0.7.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7" -+dependencies = [ -+ "adler", -+] -+ -+[[package]] -+name = "mio" -+version = "0.8.8" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "927a765cd3fc26206e66b296465fa9d3e5ab003e651c1b3c060e7956d96b19d2" -+dependencies = [ -+ "libc", -+ "wasi", -+ "windows-sys 0.48.0", -+] -+ - [[package]] - name = "mockito" --version = "0.31.1" -+version = "1.2.0" - source = "registry+https://github.com/rust-lang/crates.io-index" --checksum = "80f9fece9bd97ab74339fe19f4bcaf52b76dcc18e5364c7977c1838f76b38de9" -+checksum = "f8d3038e23466858569c2d30a537f691fa0d53b51626630ae08262943e3bbb8b" - dependencies = [ - "assert-json-diff", -- "httparse", -- "lazy_static", -+ "futures", -+ "hyper", - "log", - "rand", - "regex", - "serde_json", - "serde_urlencoded", - "similar", -+ "tokio", -+] -+ -+[[package]] -+name = "num_cpus" -+version = "1.16.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -+dependencies = [ -+ "hermit-abi", -+ "libc", -+] -+ -+[[package]] -+name = "object" -+version = "0.32.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0" -+dependencies = [ -+ "memchr", - ] - - [[package]] -@@ -430,12 +710,47 @@ dependencies = [ - "openssl-sys", - ] - -+[[package]] -+name = "parking_lot" -+version = "0.12.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "3742b2c103b9f06bc9fff0a37ff4912935851bee6d36f3c02bcc755bcfec228f" -+dependencies = [ -+ "lock_api", -+ "parking_lot_core", -+] -+ -+[[package]] -+name = "parking_lot_core" -+version = "0.9.9" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4c42a9226546d68acdd9c0a280d17ce19bfe27a46bf68784e4066115788d008e" -+dependencies = [ -+ "cfg-if", -+ "libc", -+ "redox_syscall", -+ "smallvec", -+ "windows-targets", -+] -+ - [[package]] - name = "percent-encoding" - version = "2.3.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "9b2a4787296e9989611394c33f193f676704af1686e70b8f8033ab5ba9a35a94" - -+[[package]] -+name = "pin-project-lite" -+version = "0.2.13" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58" -+ -+[[package]] -+name = "pin-utils" -+version = "0.1.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -+ - [[package]] - name = "pkg-config" - version = "0.3.27" -@@ -528,6 +843,15 @@ dependencies = [ - "getrandom", - ] - -+[[package]] -+name = "redox_syscall" -+version = "0.4.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4722d768eff46b75989dd134e5c353f0d6296e5aaa3132e776cbdb56be7731aa" -+dependencies = [ -+ "bitflags 1.3.2", -+] -+ - [[package]] - name = "regex" - version = "1.10.2" -@@ -557,6 +881,12 @@ version = "0.8.2" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "c08c74e62047bb2de4ff487b251e4a92e24f48745648451635cec7d591162d9f" - -+[[package]] -+name = "rustc-demangle" -+version = "0.1.23" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d626bb9dae77e28219937af045c257c28bfd3f69333c512553507f5f9798cb76" -+ - [[package]] - name = "rustix" - version = "0.37.27" -@@ -586,6 +916,12 @@ dependencies = [ - "windows-sys 0.42.0", - ] - -+[[package]] -+name = "scopeguard" -+version = "1.2.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" -+ - [[package]] - name = "serde" - version = "1.0.164" -@@ -644,19 +980,43 @@ version = "0.9.21" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "d9d684e3ec7de3bf5466b32bd75303ac16f0736426e5a4e0d6e489559ce1249c" - dependencies = [ -- "indexmap", -+ "indexmap 1.9.3", - "itoa", - "ryu", - "serde", - "unsafe-libyaml", - ] - -+[[package]] -+name = "signal-hook-registry" -+version = "1.4.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "d8229b473baa5980ac72ef434c4415e70c4b5e71b423043adb4ba059f89c99a1" -+dependencies = [ -+ "libc", -+] -+ - [[package]] - name = "similar" - version = "2.3.0" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "2aeaf503862c419d66959f5d7ca015337d864e9c49485d771b732e2a20453597" - -+[[package]] -+name = "slab" -+version = "0.4.9" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "8f92a496fb766b417c996b9c5e57daf2f7ad3b0bebe1ccfca4856390e3d3bb67" -+dependencies = [ -+ "autocfg", -+] -+ -+[[package]] -+name = "smallvec" -+version = "1.11.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4dccd0940a2dcdf68d092b8cbab7dc0ad8fa938bf95787e1b916b0e3d0e8e970" -+ - [[package]] - name = "socket2" - version = "0.4.9" -@@ -667,6 +1027,16 @@ dependencies = [ - "winapi", - ] - -+[[package]] -+name = "socket2" -+version = "0.5.4" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4031e820eb552adee9295814c0ced9e5cf38ddf1e8b7d566d6de8e2538ea989e" -+dependencies = [ -+ "libc", -+ "windows-sys 0.48.0", -+] -+ - [[package]] - name = "strsim" - version = "0.10.0" -@@ -725,6 +1095,81 @@ dependencies = [ - "syn 2.0.18", - ] - -+[[package]] -+name = "tokio" -+version = "1.33.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "4f38200e3ef7995e5ef13baec2f432a6da0aa9ac495b2c0e8f3b7eec2c92d653" -+dependencies = [ -+ "backtrace", -+ "bytes", -+ "libc", -+ "mio", -+ "num_cpus", -+ "parking_lot", -+ "pin-project-lite", -+ "signal-hook-registry", -+ "socket2 0.5.4", -+ "tokio-macros", -+ "windows-sys 0.48.0", -+] -+ -+[[package]] -+name = "tokio-macros" -+version = "2.1.0" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "630bdcf245f78637c13ec01ffae6187cca34625e8c63150d424b59e55af2675e" -+dependencies = [ -+ "proc-macro2", -+ "quote", -+ "syn 2.0.18", -+] -+ -+[[package]] -+name = "tokio-util" -+version = "0.7.10" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "5419f34732d9eb6ee4c3578b7989078579b7f039cbbb9ca2c4da015749371e15" -+dependencies = [ -+ "bytes", -+ "futures-core", -+ "futures-sink", -+ "pin-project-lite", -+ "tokio", -+ "tracing", -+] -+ -+[[package]] -+name = "tower-service" -+version = "0.3.2" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" -+ -+[[package]] -+name = "tracing" -+version = "0.1.40" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "c3523ab5a71916ccf420eebdf5521fcef02141234bbc0b8a49f2fdc4544364ef" -+dependencies = [ -+ "pin-project-lite", -+ "tracing-core", -+] -+ -+[[package]] -+name = "tracing-core" -+version = "0.1.32" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "c06d3da6113f116aaee68e4d601191614c9053067f9ab7f6edbcb161237daa54" -+dependencies = [ -+ "once_cell", -+] -+ -+[[package]] -+name = "try-lock" -+version = "0.2.4" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "3528ecfd12c466c6f163363caf2d02a71161dd5e1cc6ae7b34207ea2d42d81ed" -+ - [[package]] - name = "unicode-ident" - version = "1.0.9" -@@ -753,6 +1198,15 @@ version = "0.2.15" - source = "registry+https://github.com/rust-lang/crates.io-index" - checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" - -+[[package]] -+name = "want" -+version = "0.3.1" -+source = "registry+https://github.com/rust-lang/crates.io-index" -+checksum = "bfa7760aed19e106de2c7c0b581b509f2f25d3dacaf737cb82ac61bc6d760b0e" -+dependencies = [ -+ "try-lock", -+] -+ - [[package]] - name = "wasi" - version = "0.11.0+wasi-snapshot-preview1" -diff --git a/rust/pv/Cargo.toml b/rust/pv/Cargo.toml -index 22d5fd3..ff84526 100644 ---- a/rust/pv/Cargo.toml -+++ b/rust/pv/Cargo.toml -@@ -22,7 +22,7 @@ serde = { version = "1.0.139", features = ["derive"], optional = true } - byteorder = {version = "1.3", optional = true } - - [dev-dependencies] --mockito = {version = "0.31", default-features = false } -+mockito = {version = "1", default-features = false } - serde_test = "1" - lazy_static = "1.1" - -diff --git a/rust/pv/src/verify/test.rs b/rust/pv/src/verify/test.rs -index 1c0d2b5..5ca2e71 100644 ---- a/rust/pv/src/verify/test.rs -+++ b/rust/pv/src/verify/test.rs -@@ -14,7 +14,9 @@ use crate::test_utils::*; - pub fn mock_endpt(res: &str) -> mockito::Mock { - let res_path = get_cert_asset_path(res); - -- mockito::mock("GET", format!("/crl/{res}").as_str()) -+ let mut server = mockito::Server::new(); -+ server -+ .mock("GET", format!("/crl/{res}").as_str()) - .with_header("content-type", "application/pkix-crl") - .with_body_from_file(res_path) - .create() --- -2.43.0 - - -From 66783f1901dcaca6f567ad13b05acc7dbe412ff0 Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Wed, 20 Dec 2023 13:31:18 +0100 -Subject: [PATCH 11/13] rust/Makefile: Fix CC/AR variables for TEST_TARGETS -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: Steffen Eiden -Signed-off-by: Jan Höppner -(cherry picked from commit 6fd02279da20acba882426496e3b87e556bdeabc) ---- - rust/Makefile | 4 ++-- - 1 file changed, 2 insertions(+), 2 deletions(-) - -diff --git a/rust/Makefile b/rust/Makefile -index e4e9885..fa3cf04 100644 ---- a/rust/Makefile -+++ b/rust/Makefile -@@ -37,8 +37,8 @@ endif - - # the cc crate uses these variables to compile c code. It does not open a shell - # to call the compiler, so no echo etc. allowed here, just a path to a program --$(BUILD_TARGETS) rust-test: CC = $(CC_SILENT) --$(BUILD_TARGETS) rust-test: AR = $(AR_SILENT) -+$(BUILD_TARGETS) $(TEST_TARGETS) rust-test: CC = $(CC_SILENT) -+$(BUILD_TARGETS) $(TEST_TARGETS) rust-test: AR = $(AR_SILENT) - - $(PV_TARGETS): .check-dep-pvtools - $(PV_TARGETS) $(CARGO_TARGETS): .check-cargo .no-cross-compile --- -2.43.0 - - -From d54a8aa4d7b77338fd5511d895eadbb074b6024a Mon Sep 17 00:00:00 2001 -From: Steffen Eiden -Date: Fri, 15 Dec 2023 11:30:14 +0100 -Subject: [PATCH 12/13] rust/pv: Provide access for SecretList members -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Adds getter for SecretList and SecretEntry. -Adds enum to represent secret types. -Add Iterator functionality for SecretList. - -While at it, make the datatype of the capacity of the list transparent -for users. - -Signed-off-by: Steffen Eiden -Signed-off-by: Jan Höppner -(cherry picked from commit 0764460eafdfd91f78bbed5ac818f19d60e14b70) ---- - rust/pv/src/lib.rs | 2 +- - rust/pv/src/uvsecret/secret_list.rs | 170 ++++++++++++++++++++++++---- - 2 files changed, 151 insertions(+), 21 deletions(-) - -diff --git a/rust/pv/src/lib.rs b/rust/pv/src/lib.rs -index d72ac93..ed89140 100644 ---- a/rust/pv/src/lib.rs -+++ b/rust/pv/src/lib.rs -@@ -70,7 +70,7 @@ pub mod uv { - }; - #[cfg(feature = "uvsecret")] - pub use crate::uvsecret::{ -- secret_list::SecretList, -+ secret_list::{ListableSecretType, SecretEntry, SecretList}, - uvc::{AddCmd, ListCmd, LockCmd}, - }; - } -diff --git a/rust/pv/src/uvsecret/secret_list.rs b/rust/pv/src/uvsecret/secret_list.rs -index 6943bd3..72a05b2 100644 ---- a/rust/pv/src/uvsecret/secret_list.rs -+++ b/rust/pv/src/uvsecret/secret_list.rs -@@ -2,13 +2,14 @@ - // - // Copyright IBM Corp. 2023 - --use crate::{misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result}; -+use crate::{assert_size, misc::to_u16, uv::ListCmd, uvdevice::UvCmd, Error, Result}; - use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt}; - use serde::{Serialize, Serializer}; --use std::usize; - use std::{ - fmt::Display, - io::{Cursor, Read, Seek, Write}, -+ slice::Iter, -+ vec::IntoIter, - }; - use zerocopy::{AsBytes, FromBytes, U16, U32}; - -@@ -19,16 +20,79 @@ use super::ser_gsid; - /// Requires the `uvsecret` feature. - #[derive(Debug, PartialEq, Eq, Serialize)] - pub struct SecretList { -- total_num_secrets: u16, -+ total_num_secrets: usize, - secrets: Vec, - } - -+impl<'a> IntoIterator for &'a SecretList { -+ type Item = &'a SecretEntry; -+ type IntoIter = Iter<'a, SecretEntry>; -+ -+ fn into_iter(self) -> Self::IntoIter { -+ self.iter() -+ } -+} -+ -+impl IntoIterator for SecretList { -+ type Item = SecretEntry; -+ type IntoIter = IntoIter; -+ -+ fn into_iter(self) -> Self::IntoIter { -+ self.secrets.into_iter() -+ } -+} -+ -+impl FromIterator for SecretList { -+ fn from_iter>(iter: T) -> Self { -+ let secrets: Vec<_> = iter.into_iter().collect(); -+ let total_num_secrets = secrets.len() as u16; -+ Self::new(total_num_secrets, secrets) -+ } -+} -+ - impl SecretList { -+ #[doc(hidden)] -+ /// For testing purposes. -+ pub fn new(total_num_secrets: u16, secrets: Vec) -> Self { -+ Self { -+ total_num_secrets: total_num_secrets as usize, -+ secrets, -+ } -+ } -+ -+ /// Returns an iterator over the slice. -+ /// -+ /// The iterator yields all secret entries from start to end. -+ pub fn iter(&self) -> Iter<'_, SecretEntry> { -+ self.secrets.iter() -+ } -+ -+ /// Returns the length of this [`SecretList`]. -+ pub fn len(&self) -> usize { -+ self.secrets.len() -+ } -+ -+ /// Check for is_empty of this [`SecretList`]. -+ pub fn is_empty(&self) -> bool { -+ self.secrets.is_empty() -+ } -+ -+ /// Reports the number of secrets stored in UV -+ /// -+ /// This number may be not equal to the provided number of [`SecretEntry`] -+ pub fn total_num_secrets(&self) -> usize { -+ self.total_num_secrets -+ } -+ - /// Encodes the list in the same binary format the UV would do - pub fn encode(&self, w: &mut T) -> Result<()> { - let num_s = to_u16(self.secrets.len()).ok_or(Error::ManySecrets)?; - w.write_u16::(num_s)?; -- w.write_u16::(self.total_num_secrets)?; -+ w.write_u16::( -+ self.total_num_secrets -+ .try_into() -+ .map_err(|_| Error::ManySecrets)?, -+ )?; - w.write_all(&[0u8; 12])?; - for secret in &self.secrets { - w.write_all(secret.as_bytes())?; -@@ -39,10 +103,10 @@ impl SecretList { - /// Decodes the list from the binary format of the UV into this internal representation - pub fn decode(r: &mut R) -> std::io::Result { - let num_s = r.read_u16::()?; -- let total_num_secrets = r.read_u16::()?; -+ let total_num_secrets = r.read_u16::()? as usize; - let mut v: Vec = Vec::with_capacity(num_s as usize); - r.seek(std::io::SeekFrom::Current(12))?; //skip reserved bytes -- let mut buf = [0u8; SECRET_ENTRY_SIZE]; -+ let mut buf = [0u8; SecretEntry::STRUCT_SIZE]; - for _ in 0..num_s { - r.read_exact(&mut buf)?; - //cannot fail. buffer has the same size as the secret entry -@@ -84,9 +148,56 @@ fn ser_u16(v: &U16, ser: S) -> Result - ser.serialize_u16(v.get()) - } - -+/// Secret types that can appear in a [`SecretList`] -+#[non_exhaustive] -+#[derive(PartialEq, Eq)] -+pub enum ListableSecretType { -+ /// Association Secret -+ Association, -+ /// Invalid secret type, that should never appear in a list -+ /// -+ /// 0 is reserved -+ /// 1 is Null secret, with no id and not listable -+ Invalid(u16), -+ /// Unknown secret type -+ Unknown(u16), -+} -+impl ListableSecretType { -+ const ASSOCIATION: u16 = 0x0002; -+} -+ -+impl Display for ListableSecretType { -+ fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -+ match self { -+ Self::Association => write!(f, "Association"), -+ Self::Invalid(n) => write!(f, "Invalid({n})"), -+ Self::Unknown(n) => write!(f, "Unknown({n})"), -+ } -+ } -+} -+ -+impl From> for ListableSecretType { -+ fn from(value: U16) -> Self { -+ match value.get() { -+ 0x0000 => Self::Invalid(0), -+ 0x0001 => Self::Invalid(1), -+ Self::ASSOCIATION => ListableSecretType::Association, -+ n => Self::Unknown(n), -+ } -+ } -+} -+ -+impl From for U16 { -+ fn from(value: ListableSecretType) -> Self { -+ match value { -+ ListableSecretType::Association => ListableSecretType::ASSOCIATION, -+ ListableSecretType::Invalid(n) | ListableSecretType::Unknown(n) => n, -+ } -+ .into() -+ } -+} -+ - /// A secret in a [`SecretList`] --/// --/// Fields are in big endian - #[repr(C)] - #[derive(Debug, PartialEq, Eq, AsBytes, FromBytes, Serialize)] - pub struct SecretEntry { -@@ -101,20 +212,43 @@ pub struct SecretEntry { - #[serde(serialize_with = "ser_gsid")] - id: [u8; 32], - } --const SECRET_ENTRY_SIZE: usize = 0x30; -+assert_size!(SecretEntry, SecretEntry::STRUCT_SIZE); -+ -+impl SecretEntry { -+ const STRUCT_SIZE: usize = 0x30; -+ -+ #[doc(hidden)] -+ /// For testing purposes. -+ pub fn new(index: u16, stype: ListableSecretType, id: [u8; 32], secret_len: u32) -> Self { -+ Self { -+ index: index.into(), -+ stype: stype.into(), -+ len: secret_len.into(), -+ res_8: 0, -+ id, -+ } -+ } - --fn stype_str(stype: u16) -> String { -- match stype { -- // should never match (not incl in list), but here for completeness -- 1 => "Null".to_string(), -- 2 => "Association".to_string(), -- n => format!("Unknown {n}"), -+ /// Returns the index of this [`SecretEntry`]. -+ pub fn index(&self) -> u16 { -+ self.index.get() -+ } -+ -+ /// Returns the secret type of this [`SecretEntry`]. -+ pub fn stype(&self) -> ListableSecretType { -+ self.stype.into() -+ } -+ -+ /// Returns a reference to the id of this [`SecretEntry`]. -+ pub fn id(&self) -> &[u8] { -+ &self.id - } - } - - impl Display for SecretEntry { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { -- writeln!(f, "{} {}:", self.index, stype_str(self.stype.get()))?; -+ let stype: ListableSecretType = self.stype.into(); -+ writeln!(f, "{} {}:", self.index, stype)?; - write!(f, " ")?; - for b in self.id { - write!(f, "{b:02x}")?; -@@ -128,10 +262,6 @@ mod test { - use super::*; - use std::io::{BufReader, BufWriter, Cursor}; - -- #[test] -- fn secret_entry_size() { -- assert_eq!(::std::mem::size_of::(), SECRET_ENTRY_SIZE); -- } - #[test] - fn dump_secret_entry() { - const EXP: &[u8] = &[ --- -2.43.0 - - -From e75bbd754e5912d34c0aedfe35ccedd54ca850be Mon Sep 17 00:00:00 2001 -From: Harald Freudenberger -Date: Fri, 1 Dec 2023 12:10:20 +0100 -Subject: [PATCH 13/13] rust/pvapconfig: Introduce new tool pvapconfig -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -pvapconfig is a new tool for automatically configuring the APQNs -within an Secure Execution KVM guest with AP pass-through support. -Based on a given AP configuration it tries to find a matching -APQN and bind and associate it with the correct secret. - -Signed-off-by: Harald Freudenberger -Reviewed-by: Ingo Franzki -Reviewed-by: Holger Dengler -Reviewed-by: Steffen Eiden -Signed-off-by: Jan Höppner -(cherry picked from commit 94a38ebc3aec9ee954f1ae9a4a0a09ea7f29f11c) ---- - rust/Cargo.lock | 16 + - rust/Cargo.toml | 1 + - rust/Makefile | 2 +- - rust/pvapconfig/Cargo.toml | 19 + - rust/pvapconfig/README.md | 9 + - rust/pvapconfig/man/pvapconfig.1 | 174 +++++++ - rust/pvapconfig/src/ap.rs | 817 +++++++++++++++++++++++++++++++ - rust/pvapconfig/src/cli.rs | 63 +++ - rust/pvapconfig/src/config.rs | 391 +++++++++++++++ - rust/pvapconfig/src/helper.rs | 272 ++++++++++ - rust/pvapconfig/src/main.rs | 668 +++++++++++++++++++++++++ - rust/pvapconfig/src/uv.rs | 105 ++++ - 12 files changed, 2536 insertions(+), 1 deletion(-) - create mode 100644 rust/pvapconfig/Cargo.toml - create mode 100644 rust/pvapconfig/README.md - create mode 100644 rust/pvapconfig/man/pvapconfig.1 - create mode 100644 rust/pvapconfig/src/ap.rs - create mode 100644 rust/pvapconfig/src/cli.rs - create mode 100644 rust/pvapconfig/src/config.rs - create mode 100644 rust/pvapconfig/src/helper.rs - create mode 100644 rust/pvapconfig/src/main.rs - create mode 100644 rust/pvapconfig/src/uv.rs - -diff --git a/rust/Cargo.lock b/rust/Cargo.lock -index 067be05..3a6c423 100644 ---- a/rust/Cargo.lock -+++ b/rust/Cargo.lock -@@ -792,6 +792,22 @@ dependencies = [ - "zerocopy", - ] - -+[[package]] -+name = "pvapconfig" -+version = "0.9.0" -+dependencies = [ -+ "clap", -+ "lazy_static", -+ "openssl", -+ "openssl-sys", -+ "pv", -+ "rand", -+ "regex", -+ "serde", -+ "serde_yaml", -+ "utils", -+] -+ - [[package]] - name = "pvsecret" - version = "0.9.0" -diff --git a/rust/Cargo.toml b/rust/Cargo.toml -index 7ba1faa..f358dbd 100644 ---- a/rust/Cargo.toml -+++ b/rust/Cargo.toml -@@ -1,6 +1,7 @@ - [workspace] - members = [ - "pv", -+ "pvapconfig", - "pvsecret", - "utils", - ] -diff --git a/rust/Makefile b/rust/Makefile -index fa3cf04..818694d 100644 ---- a/rust/Makefile -+++ b/rust/Makefile -@@ -19,7 +19,7 @@ ifneq (${HAVE_CARGO},0) - - ifneq (${HAVE_OPENSSL},0) - ifneq (${HAVE_LIBCURL},0) -- PV_TARGETS := pvsecret -+ PV_TARGETS := pvsecret pvapconfig - - PV_BUILD_TARGETS := $(PV_TARGETS) - CARGO_TEST_TARGETS += $(addsuffix .test,pv $(PV_TARGETS)) -diff --git a/rust/pvapconfig/Cargo.toml b/rust/pvapconfig/Cargo.toml -new file mode 100644 -index 0000000..2a69468 ---- /dev/null -+++ b/rust/pvapconfig/Cargo.toml -@@ -0,0 +1,19 @@ -+[package] -+name = "pvapconfig" -+description = "A tool to configure the AP resources inside a SE guest based on UV secrets and an AP config file." -+authors = ["Harald Freudenberger "] -+version = "0.9.0" -+edition.workspace = true -+license.workspace = true -+ -+[dependencies] -+clap = { version ="4.1", features = ["derive", "wrap_help"]} -+lazy_static = "1.1" -+openssl = { version = "0.10" } -+openssl-sys = { version = "0.9" } -+pv = { path = "../pv", features = ["uvsecret", "request"] } -+rand = "0.8" -+regex = "1" -+serde = { version = "1.0", features = ["derive"] } -+serde_yaml = "0.9" -+utils = { path = "../utils" } -diff --git a/rust/pvapconfig/README.md b/rust/pvapconfig/README.md -new file mode 100644 -index 0000000..d30cb76 ---- /dev/null -+++ b/rust/pvapconfig/README.md -@@ -0,0 +1,9 @@ -+ -+# pvapconfig -+## Description -+**pvapconfig** is used to automatically set up the AP configuration -+within an IBM Secure Execution guest. -diff --git a/rust/pvapconfig/man/pvapconfig.1 b/rust/pvapconfig/man/pvapconfig.1 -new file mode 100644 -index 0000000..73ce8ce ---- /dev/null -+++ b/rust/pvapconfig/man/pvapconfig.1 -@@ -0,0 +1,174 @@ -+.\" pvapconfig.1 -+.\" -+.\" Copyright 2023 IBM Corp. -+.\" s390-tools is free software; you can redistribute it and/or modify -+.\" it under the terms of the MIT license. See LICENSE for details. -+.\" -+.\" use -+.\" groff -man -Tutf8 pvapconfig.1 -+.\" or -+.\" nroff -man pvapconfig.1 -+.\" to process this source -+.\" -+.TH PVAPCONFIG 1 "DEC 2023" "s390-tools" -+.SH NAME -+pvapconfig \- automatic configure APQNs within an SE KVM guest. -+.SH SYNOPSIS -+.TP 9 -+.B pvapconfig [OPTIONS] -+.SH DESCRIPTION -+pvapconfig is a tool for automatically configuring the APQNs within an -+Secure Execution KVM guest with AP pass-through support. Based on a -+given AP configuration it tries to find matching APQNs and binds and -+associates them with the given secret(s). -+ -+Here is a description of pvapconfig's process: -+.TP 3 -+1. Check AP bus: Support for AP bus needs to be available and the AP -+bus needs to support APSB. APSB is only available within an KVM SE -+guest with AP pass-through support. -+.TP 3 -+2. Check Ultravisor: UV support needs to be available and the UV needs -+to support the -+.I list secrets -+feature. -+.TP 3 -+3. Read in and validate the AP configuration file. By default if not -+overwritten by the --config option the AP configuration is read from -+.I /etc/pvapconfig.yaml -+and syntactically verified. See section CONFIGFILE for details about -+the syntax and semantic of the configuration file. -+.TP 3 -+4. Fetch the list of association secrets from the UV. Actually the -+index of the secret and the secret id for each entry is collected. The -+secret value is NOT fetched as it is NOT accessible but only usable -+within the UV firmware. -+.TP 3 -+5. Gather all APQNs available within this KVM SE guest. Collect -+information about each APQN like online states, crypto card serial -+numbers and master key verification patterns (MKVP). -+.TP 3 -+6. Go through all AP config entries. For each AP config entry try to -+find an APQN which is already configured (bound/associated) to -+satisfy this config entry. If such a match is found, the AP config -+entry is assumed to be fulfilled and marked as done. -+.TP 3 -+7. All remaining APQNs which do not already satisfy an AP config entry -+are now examined for their bind and association state and maybe reset -+to unbound state. -+.TP 3 -+8. Go through all AP config entries which are still not -+fulfilled. For each such AP config entry try to search for an APQN -+which would match to this entry and then prepare this APQN (bind, -+maybe associate). If successful, mark the AP config entry as done. -+.TP 3 -+9. Evaluation of the applied AP config entries. Applied means the AP -+config entry has been fulfilled either in step 6 or in step 8. With -+the strict option given ALL AP config entries need to apply otherwise -+an error message is printed and pvapconfig returns with exit failure. -+If the strict option is not given, it is enough to satisfy at least one -+AP config entry from the configuration and pvapconfig will return -+successfully. -+.SH OPTIONS -+.TP 8 -+.B -c, --config -+Use as the AP config file for pvapconfig. If pvapconfig -+is run without this option the default configuration file -+/etc/pvapconfig.yaml is used. -+.TP 8 -+.B -h, --help -+Print pvapconfig usage information and exit. -+.TP 8 -+.B -n, --dry-run -+Do not bind, unbind or associate APQNs but only process the -+configuration and the available APQNs and secrets and simulate the -+bind, unbind or associate action on the chosen APQN. Use it together -+with the verbose option to see which actions pvapconfig would do if -+unleashed. -+.TP 8 -+.B -s, --strict -+All AP config entries need to be satisfied to have pvapconfig -+terminate with success. Without this option one applied AP config -+entry is enough to meet the expectations. -+.TP 8 -+.B -v, --verbose -+Print out informational messages about what pvapconfig is actually -+doing. -+.TP 8 -+.B -V, --version -+Print version information and exit. -+.SH CONFIGFILE -+The pvapconfig yaml configuration file consists of a list of AP config -+entries. Each entry may hold this information: -+.TP 2 -+- mode: AP queue mode information, required, either "EP11" or "Accel". -+.TP 2 -+- mkvp: AP queue Master Key Verification Pattern (MKVP), required for -+EP11, hex string optional prepented with 0x. The MKVP hex string value -+may hold either 16 bytes (32 hex characters) or 32 bytes (64 hex -+characters) but only the leftmost 16 bytes hold MKVP information and -+thus the rest is ignored. -+.TP 2 -+- serialnr: Crypto Card Serial Number, string, optional for EP11, -+ignored for Accel. As this is a real ASCII string uppercase and -+lowercase character(s) count different. -+.TP 2 -+- mingen: Card Minimal Generation, string "CEX4", "CEX5", "CEX6", -+"CEX7" or "CEX8" for Accelerator, string "CEC8" for EP11, optional. If -+given specifies the minimal accepted Crypto card generation. -+.TP 2 -+- secretid: Secret id, hex string with optional 0x prepented, required -+for EP11, ignored for Accel. Details see the following text. -+.TP 2 -+- name: ASCII string, optional, but see details below. -+.TP 2 -+- description: Description of this AP config entry, string, ignored, -+ just for convenience for the reader or editor of the configuration. -+.PP -+The secret id uniquely identifies an association secret. However, it -+is a clumsy hex string of 64 characters which represent the readable -+sha256 value over the secret's name. So pvapconfig can use the name -+instead and calculate the secret id from the name. So the rule is: -+.TP 2 -+- If name and secretid is given, the secretid needs to match to the -+sha256 hash over the given name for this AP config entry. -+.TP 2 -+- If only name is given then the secretid is calculated with a sha256 -+ hash over the given name. -+.TP 2 -+- If only the secretid is given, there is nothing more to do but -+verify that the value is a hex string with 64 characters. -+.SH LOCKING -+Pvapconfig needs to have a consistent view of the AP resources -+during lifetime. There must not run multiple instances of pvapconfig -+or any manipulations of the AP resources in parallel. To prevent the -+execution of multiple pvapconfig instances the lock file -+/run/lock/pvapconfig.lock is established. A second instance of -+pvapconfig will detect this lock file and terminated with an error -+message. If for any reason this file still exists as a leftover from a -+previous pvapconfig crash for example, it needs to get removed by -+hand. The lock file contains the process id of the pvapconfig process -+which created this file. -+.SH RETURN VALUE -+.TP 8 -+.B 0 - Successful termination. -+At least one AP config entry has been applied or at least one APQN has -+been found in a state matching to one AP config entry. If strict -+option is given, ALL AP config entries have been applied. An AP config -+entry is applied either by configuring the APQN accordingly or an APQN -+has been found which already fulfills the constrains. -+.RE -+.TP 8 -+.B 1 - Failure. -+Either some kind of failure happened during processing the -+configuration or the configuration could not get applied -+successful. In all cases pvapconfig prints out a message to standard -+error with details about the failure. Also pvapconfig does NOT reset -+the APQNs to the state found at the startup when failing to apply the -+configuration. -+.SH NOTES -+For more information and details see the IBM documentation about -+Confidential Computing "Introducing IBM Secure Execution for Linux" -+available at https://www.ibm.com/docs/. -+.SH SEE ALSO -+\fBpvsecret\fR(1), \fBlszcrypt\fR(8), \fBchzcrypt\fR(8) -diff --git a/rust/pvapconfig/src/ap.rs b/rust/pvapconfig/src/ap.rs -new file mode 100644 -index 0000000..0b88235 ---- /dev/null -+++ b/rust/pvapconfig/src/ap.rs -@@ -0,0 +1,817 @@ -+// SPDX-License-Identifier: MIT -+// -+// Copyright IBM Corp. 2023 -+// -+//! AP support functions for pvapconfig -+// -+ -+use crate::helper::*; -+use regex::Regex; -+use std::fmt; -+use std::path::Path; -+use std::slice::Iter; -+use std::thread; -+use std::time; -+ -+const PATH_SYS_BUS_AP: &str = "/sys/bus/ap"; -+const PATH_SYS_BUS_AP_FEATURES: &str = "/sys/bus/ap/features"; -+const PATH_SYS_BUS_AP_BINDINGS: &str = "/sys/bus/ap/bindings"; -+const PATH_SYS_DEVICES_AP: &str = "/sys/devices/ap"; -+ -+const RE_CARD_DIR: &str = r"^card([[:xdigit:]]{2})$"; -+const RE_QUEUE_DIR: &str = r"^([[:xdigit:]]{2})\.([[:xdigit:]]{4})$"; -+const RE_CARD_TYPE: &str = r"^CEX([3-8])([ACP])$"; -+const RE_EP11_MKVP: &str = r"WK\s+CUR:\s+(\S+)\s+(\S+)"; -+const RE_CCA_AES_MKVP: &str = r"AES\s+CUR:\s+(\S+)\s+(\S+)"; -+const RE_CCA_APKA_MKVP: &str = r"APKA\s+CUR:\s+(\S+)\s+(\S+)"; -+ -+const SYS_BUS_AP_BINDINGS_POLL_MS: u64 = 500; -+ -+const SYS_BUS_AP_BIND_POLL_MS: u64 = 500; -+const SYS_BUS_AP_BIND_TIMEOUT_MS: u64 = 10000; -+ -+const SYS_BUS_AP_ASSOC_POLL_MS: u64 = 500; -+const SYS_BUS_AP_ASSOC_TIMEOUT_MS: u64 = 10000; -+ -+/// Check if AP bus support is available. -+/// Returns Result with Ok(()) or Err(failurestring). -+pub fn check_ap_bus_support() -> Result<(), String> { -+ if !Path::new(PATH_SYS_BUS_AP).is_dir() { -+ return Err(format!( -+ "AP bus support missing (path {PATH_SYS_BUS_AP} is invalid)." -+ )); -+ } -+ Ok(()) -+} -+ -+/// Check if AP bus supports APSB. -+/// -+/// When APSB support is available returns Result -+/// with Ok(()) or otherwise Err(failurestring). -+pub fn ap_bus_has_apsb_support() -> Result<(), String> { -+ if !Path::new(PATH_SYS_BUS_AP_FEATURES).is_file() { -+ return Err(format!( -+ "AP bus features support missing (file {PATH_SYS_BUS_AP_FEATURES} does not exist)." -+ )); -+ } -+ let features = sysfs_read_string(PATH_SYS_BUS_AP_FEATURES).map_err(|err| { -+ format!("Failure reading AP bus features from {PATH_SYS_BUS_AP_FEATURES} ({err:?}).") -+ })?; -+ match features.find("APSB") { -+ Some(_) => Ok(()), -+ None => Err("Missing AP bus feature APSB (SE AP pass-through not enabled ?).".to_string()), -+ } -+} -+ -+/// Wait for AP bus set up all it's devices. -+/// -+/// This function loops until the AP bus reports that -+/// - all AP queue devices have been constructed -+/// - and all AP device have been bound to a device driver. -+/// This may take some time and even loop forever if there -+/// is something wrong with the kernel modules setup. -+/// Returns true when AP bus bindings are complete, -+/// otherwise false and a message is printed. -+/// When AP bus binding complete is not immediately reached -+/// and this function needs to loop, about every 5 seconds -+/// a message is printed "Waiting for ...". -+pub fn wait_for_ap_bus_bindings_complete() -> bool { -+ let mut counter = 0; -+ loop { -+ match sysfs_read_string(PATH_SYS_BUS_AP_BINDINGS) { -+ Ok(s) => { -+ if s.contains("complete") { -+ return true; -+ } -+ } -+ Err(err) => { -+ eprintln!( -+ "Failure reading AP bus bindings from {} ({:?}).", -+ PATH_SYS_BUS_AP_BINDINGS, err -+ ); -+ return false; -+ } -+ } -+ thread::sleep(time::Duration::from_millis(SYS_BUS_AP_BINDINGS_POLL_MS)); -+ counter += 1; -+ if counter % 10 == 0 { -+ println!("Waiting for AP bus bindings complete."); -+ } -+ } -+} -+ -+#[derive(Debug, Clone, PartialEq, Eq)] -+pub enum ApqnMode { -+ Accel, -+ Ep11, -+ Cca, -+} -+ -+#[derive(Debug, Clone)] -+pub struct ApqnInfoAccel { -+ // empty -+} -+ -+#[derive(Debug, Clone)] -+pub struct ApqnInfoEp11 { -+ pub serialnr: String, -+ pub mkvp: String, // may be an empty string if no WK set -+} -+ -+#[derive(Debug, Clone)] -+pub struct ApqnInfoCca { -+ pub serialnr: String, -+ pub mkvp_aes: String, // may be an empty string if no MK set -+ pub mkvp_apka: String, // may be an empty string if no MK set -+} -+ -+#[derive(Debug, Clone)] -+pub enum ApqnInfo { -+ Accel(ApqnInfoAccel), -+ Ep11(ApqnInfoEp11), -+ Cca(ApqnInfoCca), -+} -+ -+impl ApqnInfo { -+ fn accel_info(_carddir: &str, _queuedir: &str) -> Result { -+ Ok(ApqnInfo::Accel(ApqnInfoAccel {})) -+ } -+ -+ fn cca_info(carddir: &str, queuedir: &str) -> Result { -+ let serialnr = match sysfs_read_string(&format!("{carddir}/serialnr")) { -+ Ok(r) => r, -+ Err(err) => { -+ return Err(format!( -+ "Failure reading serialnr from {carddir}/serialnr: {:?}.", -+ err -+ )) -+ } -+ }; -+ let mkvps = match sysfs_read_string(&format!("{carddir}/{queuedir}/mkvps")) { -+ Ok(r) => r, -+ Err(err) => { -+ return Err(format!( -+ "Failure reading mkvps from {carddir}/{queuedir}/mkvps: {:?}.", -+ err -+ )) -+ } -+ }; -+ let mut aes_mkvp = String::new(); -+ let re_cca_aes_mkvp = Regex::new(RE_CCA_AES_MKVP).unwrap(); -+ if !re_cca_aes_mkvp.is_match(&mkvps) { -+ return Err(format!( -+ "APQN {} failure parsing mkvps string '{}'.", -+ queuedir, mkvps -+ )); -+ } else { -+ let caps = re_cca_aes_mkvp.captures(&mkvps).unwrap(); -+ let valid = caps.get(1).unwrap().as_str().to_lowercase(); -+ if valid != "valid" { -+ eprintln!( -+ "Warning: APQN {} has no valid AES master key set.", -+ queuedir -+ ); -+ } else { -+ aes_mkvp = caps.get(2).unwrap().as_str().to_lowercase(); -+ if aes_mkvp.starts_with("0x") { -+ aes_mkvp = String::from(&aes_mkvp[2..]); -+ } -+ } -+ } -+ let mut apka_mkvp = String::new(); -+ let re_cca_apka_mkvp = Regex::new(RE_CCA_APKA_MKVP).unwrap(); -+ if !re_cca_apka_mkvp.is_match(&mkvps) { -+ return Err(format!( -+ "APQN {} failure parsing mkvps string '{}'.", -+ queuedir, mkvps -+ )); -+ } else { -+ let caps = re_cca_apka_mkvp.captures(&mkvps).unwrap(); -+ let valid = caps.get(1).unwrap().as_str().to_lowercase(); -+ if valid != "valid" { -+ eprintln!( -+ "Warning: APQN {} has no valid APKA master key set.", -+ queuedir -+ ); -+ } else { -+ apka_mkvp = caps.get(2).unwrap().as_str().to_lowercase(); -+ if apka_mkvp.starts_with("0x") { -+ apka_mkvp = String::from(&apka_mkvp[2..]); -+ } -+ } -+ } -+ Ok(ApqnInfo::Cca(ApqnInfoCca { -+ serialnr, -+ mkvp_aes: aes_mkvp, -+ mkvp_apka: apka_mkvp, -+ })) -+ } -+ -+ fn ep11_info(carddir: &str, queuedir: &str) -> Result { -+ let serialnr = match sysfs_read_string(&format!("{carddir}/serialnr")) { -+ Ok(r) => r, -+ Err(err) => { -+ return Err(format!( -+ "Failure reading serialnr from {carddir}/serialnr: {:?}.", -+ err -+ )) -+ } -+ }; -+ let mkvps = match sysfs_read_string(&format!("{carddir}/{queuedir}/mkvps")) { -+ Ok(r) => r, -+ Err(err) => { -+ return Err(format!( -+ "Failure reading mkvps from {carddir}/{queuedir}/mkvps: {:?}.", -+ err -+ )) -+ } -+ }; -+ let mut mkvp = String::new(); -+ let re_ep11_mkvp = Regex::new(RE_EP11_MKVP).unwrap(); -+ if !re_ep11_mkvp.is_match(&mkvps) { -+ return Err(format!( -+ "APQN {} failure parsing mkvps string '{}'.", -+ queuedir, mkvps -+ )); -+ } else { -+ let caps = re_ep11_mkvp.captures(&mkvps).unwrap(); -+ let valid = caps.get(1).unwrap().as_str().to_lowercase(); -+ if valid != "valid" { -+ eprintln!("Warning: APQN {} has no valid wrapping key set.", queuedir); -+ } else { -+ mkvp = caps.get(2).unwrap().as_str().to_lowercase(); -+ if mkvp.starts_with("0x") { -+ mkvp = String::from(&mkvp[2..]); -+ } -+ if mkvp.len() > 32 { -+ mkvp = String::from(&mkvp[..32]) -+ } -+ } -+ } -+ Ok(ApqnInfo::Ep11(ApqnInfoEp11 { serialnr, mkvp })) -+ } -+ -+ fn info(mode: &ApqnMode, carddir: &str, queuedir: &str) -> Result { -+ match mode { -+ ApqnMode::Accel => ApqnInfo::accel_info(carddir, queuedir), -+ ApqnMode::Cca => ApqnInfo::cca_info(carddir, queuedir), -+ ApqnMode::Ep11 => ApqnInfo::ep11_info(carddir, queuedir), -+ } -+ } -+} -+ -+#[derive(Debug, Clone)] -+pub struct Apqn { -+ pub name: String, -+ pub card: u32, -+ pub domain: u32, -+ pub gen: u32, -+ pub mode: ApqnMode, -+ pub info: Option, -+} -+ -+impl fmt::Display for Apqn { -+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { -+ write!(f, "({},{})", self.card, self.domain) -+ } -+} -+ -+impl Apqn { -+ pub fn bind_state(&self) -> Result { -+ get_apqn_bind_state(self.card, self.domain) -+ } -+ -+ pub fn set_bind_state(&self, state: BindState) -> Result<(), String> { -+ set_apqn_bind_state(self.card, self.domain, state) -+ } -+ -+ pub fn associate_state(&self) -> Result { -+ get_apqn_associate_state(self.card, self.domain) -+ } -+ -+ pub fn set_associate_state(&self, state: AssocState) -> Result<(), String> { -+ set_apqn_associate_state(self.card, self.domain, state) -+ } -+} -+ -+/// Wrapper object around Vector of Apqns -+pub struct ApqnList(Vec); -+ -+impl ApqnList { -+ #[cfg(test)] // only used in test code -+ pub fn from_apqn_vec(apqns: Vec) -> ApqnList { -+ ApqnList(apqns) -+ } -+ -+ #[cfg(test)] // only used in test code -+ pub fn to_apqn_vec(&self) -> Vec { -+ self.0.clone() -+ } -+ -+ pub fn iter(&self) -> Iter<'_, Apqn> { -+ self.0.iter() -+ } -+ -+ pub fn len(&self) -> usize { -+ self.0.len() -+ } -+ -+ pub fn is_empty(&self) -> bool { -+ self.0.is_empty() -+ } -+ -+ /// Scan AP bus devices in sysfs and construct the Apqnlist. -+ /// -+ /// The list is a vector of struct Apqn for each APQN found in sysfs -+ /// which is online and the card type matches to the regular expression -+ /// RE_CARD_TYPE. -+ /// On success a vector of struct Apqn is returned. This list may be -+ /// empty if there are no APQNs available or do not match to the conditions. -+ /// On failure None is returned. -+ /// Fatal errors which should never happened like unable to compile a -+ /// static regular expression will result in calling panic. -+ /// # Panics -+ /// Panics if the compilation of a static regular expression fails. -+ pub fn gather_apqns() -> Option { -+ let mut apqns: Vec = Vec::new(); -+ let re_card_type = Regex::new(RE_CARD_TYPE).unwrap(); -+ let re_queue_dir = Regex::new(RE_QUEUE_DIR).unwrap(); -+ let card_dirs = -+ match sysfs_get_list_of_subdirs_matching_regex(PATH_SYS_DEVICES_AP, RE_CARD_DIR) { -+ Ok(r) => r, -+ Err(err) => { -+ eprintln!( -+ "Failure reading AP devices {} ({:?}).", -+ PATH_SYS_DEVICES_AP, err -+ ); -+ return None; -+ } -+ }; -+ for dir in card_dirs { -+ let path = format!("{PATH_SYS_DEVICES_AP}/{dir}"); -+ let card_type = match sysfs_read_string(&format!("{path}/type")) { -+ Ok(r) => r, -+ Err(err) => { -+ eprintln!("Failure reading card type from {} ({:?}).", path, err); -+ return None; -+ } -+ }; -+ if !re_card_type.is_match(&card_type) { -+ eprintln!("Failure parsing card type string '{}'.", card_type); -+ return None; -+ } -+ let caps = re_card_type.captures(&card_type).unwrap(); -+ let gen = caps.get(1).unwrap().as_str().parse::().unwrap(); -+ let mode = match caps.get(2).unwrap().as_str().parse::().unwrap() { -+ 'A' => ApqnMode::Accel, -+ 'C' => ApqnMode::Cca, -+ 'P' => ApqnMode::Ep11, -+ _ => panic!("Code inconsistence between regex RE_CARD_TYPE and evaluation code."), -+ }; -+ if pv::misc::pv_guest_bit_set() { -+ // the UV blocks requests to CCA cards within SE guest with -+ // AP pass-through support. However, filter out CCA cards as these -+ // cards cause hangs during information gathering. -+ if mode == ApqnMode::Cca { -+ continue; -+ } -+ } -+ let queue_dirs = match sysfs_get_list_of_subdirs_matching_regex(&path, RE_QUEUE_DIR) { -+ Ok(r) => r, -+ Err(err) => { -+ eprintln!( -+ "Failure reading AP queue directories in {} ({:?}).", -+ path, err -+ ); -+ return None; -+ } -+ }; -+ for queue_dir in queue_dirs { -+ let _online = match sysfs_read_i32(&format!("{path}/{queue_dir}/online")) { -+ Ok(1) => true, -+ _ => continue, -+ }; -+ let caps = re_queue_dir.captures(&queue_dir).unwrap(); -+ let cardstr = caps.get(1).unwrap().as_str(); -+ let card = u32::from_str_radix(cardstr, 16).unwrap(); -+ let domstr = caps.get(2).unwrap().as_str(); -+ let dom = u32::from_str_radix(domstr, 16).unwrap(); -+ // For the mpvk and serialnr to fetch from the APQN within a SE -+ // guest the APQN needs to be bound to the guest. So if the APQN -+ // is not bound, temporarily bind it here until the info has -+ // been retrieved. -+ let mut tempbound = false; -+ if pv::misc::pv_guest_bit_set() { -+ let cbs = match get_apqn_bind_state(card, dom) { -+ Ok(bs) => bs, -+ Err(err) => { -+ eprintln!( -+ "Error: Failure reading APQN ({},{}) bind state: {}", -+ card, dom, err -+ ); -+ BindState::NotSupported -+ } -+ }; -+ if cbs == BindState::Unbound { -+ let r = set_apqn_bind_state(card, dom, BindState::Bound); -+ if r.is_err() { -+ eprintln!( -+ "Warning: Failure to temp. bind APQN ({},{}): {}", -+ card, -+ dom, -+ r.unwrap_err() -+ ); -+ continue; -+ } else { -+ tempbound = true; -+ } -+ }; -+ }; -+ let info = match ApqnInfo::info(&mode, &path, &queue_dir) { -+ Err(err) => { -+ // print the error but continue with info set to None -+ eprintln!( -+ "Warning: Failure to gather info for APQN ({},{}): {}", -+ card, dom, err -+ ); -+ None -+ } -+ Ok(i) => Some(i), -+ }; -+ if tempbound { -+ let r = set_apqn_bind_state(card, dom, BindState::Unbound); -+ if r.is_err() { -+ eprintln!( -+ "Warning: Failure to unbind temp. bound APQN ({},{}): {}", -+ card, -+ dom, -+ r.unwrap_err() -+ ); -+ } -+ }; -+ apqns.push(Apqn { -+ name: queue_dir.clone(), -+ card, -+ domain: dom, -+ gen, -+ mode: mode.clone(), -+ info, -+ }); -+ } -+ } -+ Some(ApqnList(apqns)) -+ } -+ -+ /// Sort this Apqnlist by card generation: -+ /// newest generation first, older generations last. -+ pub fn sort_by_gen(&mut self) { -+ self.0.sort_unstable_by(|a, b| b.gen.cmp(&a.gen)); -+ } -+ -+ /// Check MK restriction -+ /// -+ /// Within one card there must not exist 2 APQNs with same -+ /// MK setup. This rule only applies to EP11 cards. -+ /// Returns true if this check passed, -+ /// otherwise false and a message is printed. -+ pub fn check_mk_restriction(&self) -> bool { -+ for a1 in self.0.iter() { -+ for a2 in self.0.iter() { -+ if a1.card == a2.card -+ && a1.domain < a2.domain -+ && a1.mode == ApqnMode::Ep11 -+ && a1.info.is_some() -+ && a2.info.is_some() -+ { -+ let i1 = match a1.info.as_ref().unwrap() { -+ ApqnInfo::Ep11(i) => i, -+ _ => continue, -+ }; -+ let i2 = match a2.info.as_ref().unwrap() { -+ ApqnInfo::Ep11(i) => i, -+ _ => continue, -+ }; -+ if i1.mkvp.is_empty() || i2.mkvp.is_empty() { -+ continue; -+ } -+ if i1.mkvp == i2.mkvp { -+ eprintln!("APQN {} and APQN {} have same MPVK", a1, a2); -+ return false; -+ } -+ } -+ } -+ } -+ true -+ } -+} -+ -+#[derive(PartialEq, Eq)] -+pub enum BindState { -+ Bound, -+ Unbound, -+ NotSupported, -+} -+ -+/// Query bind state for this APQN. -+/// -+/// Returns a BindState enum as defined above or on failure -+/// an error string. Does NOT print any error messages. -+pub fn get_apqn_bind_state(card: u32, dom: u32) -> Result { -+ let path = format!( -+ "{}/card{:02x}/{:02x}.{:04x}/se_bind", -+ PATH_SYS_DEVICES_AP, card, card, dom -+ ); -+ match sysfs_read_string(&path) { -+ Err(err) => Err(format!( -+ "Failure reading se_bind attribute for APQN({},{}): {:?}.", -+ card, dom, err -+ )), -+ Ok(str) => match str.as_str() { -+ "bound" => Ok(BindState::Bound), -+ "unbound" => Ok(BindState::Unbound), -+ "-" => Ok(BindState::NotSupported), -+ _ => Err(format!("Unknown bind state '{str}'.")), -+ }, -+ } -+} -+ -+/// Bind or unbind an APQN. -+/// -+/// The action is determined by the BindState given in. -+/// But of course only Bound and Unbound is supported - otherwise -+/// this function panics! -+/// The function actively loops over the bind state until -+/// the requested bind state is reached or a timeout has -+/// occurred (SYS_BUS_AP_BIND_TIMEOUT_MS). -+/// On success () is returned, on failure an error string -+/// is returned. Does NOT print any error messages. -+/// # Panics -+/// Panics if a desired bind state other than Bund or Unbound is given. -+pub fn set_apqn_bind_state(card: u32, dom: u32, state: BindState) -> Result<(), String> { -+ let path = format!( -+ "{}/card{:02x}/{:02x}.{:04x}/se_bind", -+ PATH_SYS_DEVICES_AP, card, card, dom -+ ); -+ let r = match state { -+ BindState::Bound => sysfs_write_i32(&path, 1), -+ BindState::Unbound => sysfs_write_i32(&path, 0), -+ _ => panic!("set_apqn_bind_state called with invalid BindState."), -+ }; -+ if r.is_err() { -+ return Err(format!( -+ "Failure writing se_bind attribute for APQN({},{}): {:?}.", -+ card, -+ dom, -+ r.unwrap_err() -+ )); -+ } -+ let mut ms: u64 = 0; -+ loop { -+ thread::sleep(time::Duration::from_millis(SYS_BUS_AP_BIND_POLL_MS)); -+ ms += SYS_BUS_AP_BIND_POLL_MS; -+ if ms >= SYS_BUS_AP_BIND_TIMEOUT_MS { -+ break Err(format!( -+ "Timeout setting APQN({},{}) bind state.", -+ card, dom -+ )); -+ } -+ let newstate = match get_apqn_bind_state(card, dom) { -+ Err(err) => return Err(err), -+ Ok(s) => s, -+ }; -+ if newstate == state { -+ return Ok(()); -+ } -+ } -+} -+ -+#[derive(PartialEq, Eq)] -+pub enum AssocState { -+ Associated(u16), -+ AssociationPending, -+ Unassociated, -+ NotSupported, -+} -+ -+/// Query association state for this APQN. -+/// -+/// Returns an AssocState enum as defined above or on failure -+/// an error string. Does NOT print any error messages. -+pub fn get_apqn_associate_state(card: u32, dom: u32) -> Result { -+ let path = format!( -+ "{}/card{:02x}/{:02x}.{:04x}/se_associate", -+ PATH_SYS_DEVICES_AP, card, card, dom -+ ); -+ match sysfs_read_string(&path) { -+ Err(err) => Err(format!( -+ "Failure reading se_associate attribute for APQN({},{}: {:?}", -+ card, dom, err -+ )), -+ Ok(str) => { -+ if let Some(prefix) = str.strip_prefix("associated ") { -+ let value = &prefix.parse::(); -+ match value { -+ Ok(v) => Ok(AssocState::Associated(*v)), -+ Err(_) => Err(format!("Invalid association index in '{str}'.")), -+ } -+ } else { -+ match str.as_str() { -+ "association pending" => Ok(AssocState::AssociationPending), -+ "unassociated" => Ok(AssocState::Unassociated), -+ "-" => Ok(AssocState::NotSupported), -+ _ => Err(format!("Unknown association state '{str}'.")), -+ } -+ } -+ } -+ } -+} -+ -+fn set_apqn_associate_state_associate(card: u32, dom: u32, idx: u16) -> Result<(), String> { -+ let path = format!( -+ "{}/card{:02x}/{:02x}.{:04x}/se_associate", -+ PATH_SYS_DEVICES_AP, card, card, dom -+ ); -+ let r = sysfs_write_i32(&path, idx as i32); -+ if r.is_err() { -+ return Err(format!( -+ "Failure writing se_associate attribute for APQN({},{}): {:?}.", -+ card, -+ dom, -+ r.unwrap_err() -+ )); -+ } -+ let mut ms: u64 = 0; -+ loop { -+ thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS)); -+ ms += SYS_BUS_AP_ASSOC_POLL_MS; -+ if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS { -+ break Err(format!( -+ "Timeout setting APQN({},{}) association idx {} state.", -+ card, dom, idx -+ )); -+ } -+ let newstate = match get_apqn_associate_state(card, dom) { -+ Err(err) => return Err(err), -+ Ok(s) => s, -+ }; -+ if let AssocState::Associated(i) = newstate { -+ if idx == i { -+ return Ok(()); -+ } else { -+ return Err(format!( -+ "Failure: APQN({},{}) is associated with {} but it should be {}.", -+ card, dom, i, idx -+ )); -+ } -+ } -+ } -+} -+ -+fn set_apqn_associate_state_unbind(card: u32, dom: u32) -> Result<(), String> { -+ let bindpath = format!( -+ "{}/card{:02x}/{:02x}.{:04x}/se_bind", -+ PATH_SYS_DEVICES_AP, card, card, dom -+ ); -+ let r = sysfs_write_i32(&bindpath, 0); -+ if r.is_err() { -+ return Err(format!( -+ "Failure writing se_bind attribute for APQN({},{}): {:?}.", -+ card, -+ dom, -+ r.unwrap_err() -+ )); -+ } -+ let mut ms: u64 = 0; -+ loop { -+ thread::sleep(time::Duration::from_millis(SYS_BUS_AP_ASSOC_POLL_MS)); -+ ms += SYS_BUS_AP_ASSOC_POLL_MS; -+ if ms >= SYS_BUS_AP_ASSOC_TIMEOUT_MS { -+ break Err(format!( -+ "Timeout setting APQN({},{}) association unbind state.", -+ card, dom -+ )); -+ } -+ let newstate = match get_apqn_associate_state(card, dom) { -+ Err(err) => return Err(err), -+ Ok(s) => s, -+ }; -+ if newstate == AssocState::Unassociated { -+ return Ok(()); -+ } -+ } -+} -+ -+/// Associate or Unassociate an APQN. -+/// -+/// The action is determined by the AssocState given in. -+/// But of course only Associated and Unassociated is supported -+/// otherwise this function panics! -+/// The function actively loops over the association state until -+/// the requested state is reached or a timeout has -+/// occurred (SYS_BUS_AP_ASSOC_TIMEOUT_MS). -+/// The unassociate is in fact a unbind. So the code triggers -+/// an unbind and then loops over the sysfs se_associate until -+/// "unassociated" is reached. -+/// On success () is returned, on failure an error string -+/// is returned. Does NOT print any error messages. -+/// # Panics -+/// Panics if a desired bind state other than Associated or -+/// Unassociated is given. -+pub fn set_apqn_associate_state(card: u32, dom: u32, state: AssocState) -> Result<(), String> { -+ match state { -+ AssocState::Associated(idx) => set_apqn_associate_state_associate(card, dom, idx), -+ AssocState::Unassociated => set_apqn_associate_state_unbind(card, dom), -+ _ => panic!("set_apqn_associate_state called with invalid AssocState."), -+ } -+} -+ -+#[cfg(test)] -+mod tests { -+ -+ use super::*; -+ -+ // These tests assume, there is an AP bus available -+ // Also for each APQN which is online, it is assumed -+ // to have a valid master key set up (for Ep11 and CCA). -+ -+ #[test] -+ fn test_check_ap_bus_support() { -+ if Path::new(PATH_SYS_BUS_AP).is_dir() { -+ assert!(check_ap_bus_support().is_ok()); -+ } else { -+ assert!(check_ap_bus_support().is_err()); -+ } -+ } -+ #[test] -+ fn test_check_ap_bus_apsb_support() { -+ if Path::new(PATH_SYS_BUS_AP).is_dir() { -+ // if we are inside a secure execution guest the -+ // apsb check should succeed. Outside an SE guest -+ // the check should fail. -+ if pv::misc::pv_guest_bit_set() { -+ assert!(ap_bus_has_apsb_support().is_ok()); -+ } else { -+ assert!(ap_bus_has_apsb_support().is_err()); -+ } -+ } else { -+ assert!(ap_bus_has_apsb_support().is_err()); -+ } -+ } -+ #[test] -+ fn test_wait_for_ap_bus_bindings_complete() { -+ let r = wait_for_ap_bus_bindings_complete(); -+ if Path::new(PATH_SYS_BUS_AP).is_dir() { -+ assert!(r); -+ } else { -+ assert!(!r); -+ } -+ } -+ #[test] -+ fn test_gather_apqns() { -+ let r = ApqnList::gather_apqns(); -+ if Path::new(PATH_SYS_BUS_AP).is_dir() { -+ assert!(r.is_some()); -+ // fail if no entries found -+ let l = r.unwrap(); -+ let v = l.to_apqn_vec(); -+ for a in v { -+ match a.mode { -+ ApqnMode::Accel => { -+ // fail if no ApqnInfo is attached -+ assert!(a.info.is_some()); -+ } -+ ApqnMode::Ep11 => { -+ // fail if no ApqnInfo is attached -+ assert!(a.info.is_some()); -+ let info = a.info.unwrap(); -+ let i = match &info { -+ ApqnInfo::Ep11(i) => i, -+ _ => panic!("ApqnInfo attached onto Ep11 APQN is NOT ApqnInfoEp11 ?!?"), -+ }; -+ // fail if no serialnr -+ assert!(i.serialnr.len() > 0); -+ // mkvp is either empty (no WK set) or has exact 32 characters -+ assert!(i.mkvp.is_empty() || i.mkvp.len() == 32); -+ } -+ ApqnMode::Cca => { -+ // fail if no ApqnInfo is attached -+ assert!(a.info.is_some()); -+ let info = a.info.unwrap(); -+ let i = match &info { -+ ApqnInfo::Cca(i) => i, -+ _ => panic!("ApqnInfo attached onto Cca APQN is NOT ApqnInfoCca ?!?"), -+ }; -+ // fail if no serialnr -+ assert!(i.serialnr.len() > 0); -+ // aes mkvp is either empty (no MK set) or exact 16 characters -+ assert!(i.mkvp_aes.is_empty() || i.mkvp_aes.len() == 16); -+ // apka mkvp is either empty (no MK set) or exact 16 characters -+ assert!(i.mkvp_apka.is_empty() || i.mkvp_apka.len() == 16); -+ } -+ } -+ } -+ } else { -+ assert!(r.is_none()); -+ } -+ } -+} -diff --git a/rust/pvapconfig/src/cli.rs b/rust/pvapconfig/src/cli.rs -new file mode 100644 -index 0000000..e7fd283 ---- /dev/null -+++ b/rust/pvapconfig/src/cli.rs -@@ -0,0 +1,63 @@ -+// SPDX-License-Identifier: MIT -+// -+// Copyright IBM Corp. 2023 -+// -+//! Command line interface for pvapconfig -+// -+ -+use clap::Parser; -+use lazy_static::lazy_static; -+ -+/// The default pvapconfig config file -+pub const PATH_DEFAULT_CONFIG_FILE: &str = "/etc/pvapconfig.yaml"; -+ -+#[derive(Parser, Clone)] -+pub struct Cli { -+ /// Provide a custom config file (overwrites default /etc/pvapconfig.yaml). -+ #[arg(short, long, value_name = "FILE")] -+ pub config: Option, -+ -+ /// Dry run: display the actions but don't actually perform them on the APQNs. -+ #[arg(short = 'n', long = "dry-run")] -+ pub dryrun: bool, -+ -+ /// Enforce strict match: All config entries need to be fullfilled. -+ /// -+ /// By default it is enough to successfully apply at least one config entry. -+ /// With the strict flag enabled, all config entries within a config file -+ /// need to be applied successful. -+ #[arg(long = "strict")] -+ pub strict: bool, -+ -+ /// Provide more detailed output. -+ #[arg(short, long)] -+ pub verbose: bool, -+ -+ /// Print version information and exit. -+ #[arg(short = 'V', long)] -+ pub version: bool, -+} -+ -+lazy_static! { -+ pub static ref ARGS: Cli = Cli::parse(); -+} -+ -+impl Cli { -+ /// verbose returns true if the verbose command line option -+ /// was given, otherwise false is returned. -+ pub fn verbose(&self) -> bool { -+ self.verbose -+ } -+ -+ /// dryrun returns true if the dry-run command line option -+ /// was given, otherwise false is returned. -+ pub fn dryrun(&self) -> bool { -+ self.dryrun -+ } -+ -+ /// strict returns true if the strict flag was given, otherwise -+ /// false is returned. -+ pub fn strict(&self) -> bool { -+ self.strict -+ } -+} -diff --git a/rust/pvapconfig/src/config.rs b/rust/pvapconfig/src/config.rs -new file mode 100644 -index 0000000..28d2ac7 ---- /dev/null -+++ b/rust/pvapconfig/src/config.rs -@@ -0,0 +1,391 @@ -+// SPDX-License-Identifier: MIT -+// -+// Copyright IBM Corp. 2023 -+// -+//! Functions around handling the pvapconfig configuration file -+// -+ -+use openssl::sha::sha256; -+use regex::Regex; -+use serde::{Deserialize, Serialize}; -+use serde_yaml::{self}; -+use std::fs::File; -+use std::slice::Iter; -+ -+pub const STR_MODE_EP11: &str = "ep11"; -+pub const STR_MODE_ACCEL: &str = "accel"; -+ -+const RE_EP11_MKVP_32: &str = r"^(0x)?([[:xdigit:]]{32})$"; -+const RE_EP11_MKVP_64: &str = r"^(0x)?([[:xdigit:]]{64})$"; -+const RE_SERIALNR: &str = r"^(\S{16})$"; -+const RE_EP11_GEN: &str = r"^cex(8)$"; -+const RE_ACCEL_GEN: &str = r"^cex([4-8])$"; -+const RE_SECRETID: &str = r"^(0x)?([[:xdigit:]]{64})$"; -+ -+#[derive(Debug, Serialize, Deserialize, Default, Clone)] -+#[serde(default, deny_unknown_fields)] -+pub struct ApConfigEntry { -+ pub name: String, // name and description are unmodified from the config file -+ pub description: String, // accel after validation ep11 after validation -+ pub mode: String, // "accel" "ep11" -+ pub mkvp: String, // empty 32 hex lowercase characters -+ pub serialnr: String, // empty empty or 16 non-whitespace characters -+ pub mingen: String, // empty or "cex4"..."cex8" empty or "cex8" -+ pub secretid: String, // empty 64 hex lowercase characters -+} -+ -+impl ApConfigEntry { -+ fn validate_secretid(&mut self) -> Result<(), String> { -+ // either secret id or name may be given -+ if self.secretid.is_empty() && self.name.is_empty() { -+ return Err("Neither secretid nor name given.".to_string()); -+ } -+ // if name is given, calculate sha256 digest for this name -+ // test for the hash calculated here can be done with openssl: -+ // echo -n "Hello" >in.bin; openssl dgst -sha256 -binary -out out.bin in.bin; hexdump -C out.bin -+ if self.name.is_empty() { -+ return Ok(()); -+ } -+ let hash = sha256(self.name.as_bytes()); -+ let hashstr = crate::helper::u8_to_hexstring(&hash); -+ // if there is a secretid given, this must match to the hash -+ if !self.secretid.is_empty() { -+ if self.secretid != hashstr { -+ return Err("Mismatch between sha256(name) and secretid.".to_string()); -+ } -+ } else { -+ self.secretid = hashstr; -+ } -+ Ok(()) -+ } -+ -+ /// # Panics -+ /// Panics if the compilation of a static regular expression fails. -+ fn validate_ep11_entry(&mut self) -> Result<(), String> { -+ // mkvp is required -+ let mut mkvp = self.mkvp.trim().to_lowercase(); -+ if mkvp.is_empty() { -+ return Err("Mkvp value missing.".to_string()); -+ } -+ // either 64 hex or 32 hex -+ if Regex::new(RE_EP11_MKVP_64).unwrap().is_match(&mkvp) { -+ // need to cut away the last 32 hex characters -+ mkvp = String::from(&mkvp[..mkvp.len() - 32]) -+ } else if Regex::new(RE_EP11_MKVP_32).unwrap().is_match(&mkvp) { -+ // nothing to do here -+ } else { -+ return Err(format!("Mkvp value '{}' is not valid.", &self.mkvp)); -+ } -+ self.mkvp = match mkvp.strip_prefix("0x") { -+ Some(rest) => String::from(rest), -+ None => mkvp, -+ }; -+ // serialnr is optional -+ let serialnr = self.serialnr.trim().to_string(); -+ if !serialnr.is_empty() && !Regex::new(RE_SERIALNR).unwrap().is_match(&serialnr) { -+ return Err(format!("Serialnr value '{}' is not valid.", &self.serialnr)); -+ } -+ self.serialnr = serialnr; -+ // mingen is optional, but if given only CEX8 is valid -+ let mingen = self.mingen.trim().to_lowercase(); -+ if !mingen.is_empty() && !Regex::new(RE_EP11_GEN).unwrap().is_match(&mingen) { -+ return Err(format!("Mingen value '{}' is not valid.", &self.mingen)); -+ } -+ self.mingen = mingen; -+ // secretid or name is required -+ let secretid = self.secretid.trim().to_lowercase(); -+ if !secretid.is_empty() && !Regex::new(RE_SECRETID).unwrap().is_match(&secretid) { -+ return Err(format!("Secretid value '{}' is not valid.", &self.secretid)); -+ } -+ self.secretid = match secretid.strip_prefix("0x") { -+ Some(rest) => String::from(rest), -+ None => secretid, -+ }; -+ // name is optional, ignored here -+ // description is optional, ignored here -+ // but the secretid needs some more validation -+ self.validate_secretid() -+ } -+ -+ /// # Panics -+ /// Panics if the compilation of a static regular expression fails. -+ fn validate_accel_entry(&mut self) -> Result<(), String> { -+ // mkvp is ignored -+ self.mkvp.clear(); -+ // serialnr is ignored -+ self.serialnr.clear(); -+ // mingen is optional, but if given must match to CEX4..CEX8 -+ let mingen = self.mingen.trim().to_lowercase(); -+ if !mingen.is_empty() && !Regex::new(RE_ACCEL_GEN).unwrap().is_match(&mingen) { -+ return Err(format!("Mingen value '{}' is not valid.", &self.mingen)); -+ } -+ self.mingen = mingen; -+ // secretid is ignored -+ self.secretid.clear(); -+ // name is optional, ignored here -+ // description is optional, ignored here -+ Ok(()) -+ } -+ -+ fn validate(&mut self) -> Result<(), String> { -+ // trim name -+ self.name = self.name.trim().to_string(); -+ // mode is always required -+ let mode = self.mode.trim().to_lowercase(); -+ match mode.as_str() { -+ STR_MODE_EP11 => { -+ self.mode = mode; -+ self.validate_ep11_entry()?; -+ } -+ STR_MODE_ACCEL => { -+ self.mode = mode; -+ self.validate_accel_entry()?; -+ } -+ _ => return Err(format!("Unknown or invalid mode '{}'.", mode)), -+ } -+ Ok(()) -+ } -+} -+ -+/// Wrapper object around Vector of ApConfigEntry -+pub struct ApConfigList(Vec); -+ -+impl ApConfigList { -+ #[cfg(test)] // only used in test code -+ pub fn from_apconfigentry_vec(apconfigs: Vec) -> ApConfigList { -+ ApConfigList(apconfigs) -+ } -+ -+ pub fn iter(&self) -> Iter<'_, ApConfigEntry> { -+ self.0.iter() -+ } -+ -+ pub fn len(&self) -> usize { -+ self.0.len() -+ } -+ -+ pub fn is_empty(&self) -> bool { -+ self.0.is_empty() -+ } -+ -+ fn read_yaml_file(fname: &str) -> Result, String> { -+ let file = match File::open(fname) { -+ Ok(f) => f, -+ Err(err) => { -+ return Err(format!( -+ "Failure to open AP config file {}: {:?}", -+ fname, err -+ )) -+ } -+ }; -+ match serde_yaml::from_reader(file) { -+ Ok(cfg) => Ok(cfg), -+ Err(err) => Err(format!( -+ "Failure parsing AP config file {}: {:?}", -+ fname, err -+ )), -+ } -+ } -+ -+ fn validate(config: &mut [ApConfigEntry]) -> Result<(), String> { -+ for (i, entry) in config.iter_mut().enumerate() { -+ let ename = if !entry.name.trim().is_empty() { -+ format!("AP config entry {} '{}'", i, entry.name.trim()) -+ } else { -+ format!("AP config entry {}", i) -+ }; -+ if let Err(err) = &entry.validate() { -+ return Err(format!("{}: {}", ename, err)); -+ } -+ } -+ Ok(()) -+ } -+ -+ /// Read in and validate the yaml configuration from a file. -+ /// Returns a Result with Ok(ApConfigList) on success -+ /// or an Err(errorstring) on failure. -+ pub fn read_and_validate_yaml_file(fname: &str) -> Result { -+ let mut apconfig = ApConfigList::read_yaml_file(fname)?; -+ ApConfigList::validate(&mut apconfig)?; -+ Ok(ApConfigList(apconfig)) -+ } -+} -+ -+#[cfg(test)] -+mod tests { -+ -+ use super::*; -+ use std::env; -+ use std::fs; -+ use std::io::Write; -+ -+ const GOOD_CONFIGS: [&str; 8] = [ -+ "# good test 1 -+- name: my Accelerator -+ mode: AcCel -+ mingen: Cex7\n", -+ "# good test 2 -+- name: my Accelerator 2 -+ description: Accelerator entry with description -+ mode: Accel\n", -+ "# good test 3 -+- name: my EP11 APQN 1 -+ mode: Ep11 -+ mkvp: 0xDB3C3B3C3F097DD55EC7EB0E7FDBCB93 -+ serialnr: 93AADFK719460083 -+ secretid: 0xBC9d46c052BC3574454C5715757274629a283767ed237922cfb8651c0e77320A\n", -+ "# good test 4 -+- name: my EP11 APQN 2 -+ mode: EP11 -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 -+ serialnr: 93aaDHzu42082261 -+ secretid: 0x2ca853f959fc5ce5f1888cb48dae39514a27bb66520ac85f6073a7f678d262c0\n", -+ "# good test 5 -+- name: my EP11 APQN 3 -+ mode: EP11 -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93db3c3b3c3f097dd55ec7eb0e7fdbcb93 -+ serialnr: 93aaDHzu42082261 -+ secretid: 0xd146c9ae77cdff25fa87a5b3487587dc29a4e391b315c98570e8fa2e2ec91454\n", -+ "# no name but secretid given -+- mode: EP11 -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 -+ secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", -+ "# no secretid but name given -+- mode: EP11 -+ name: My-EP11-AP-config -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93\n", -+ "# secretid and name given -+- mode: EP11 -+ name: My-EP11-AP-config -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 -+ secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", -+ ]; -+ -+ const BAD_CONFIGS: [&str; 12] = [ -+ "# mode missing -+- name: bad test 1 -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 -+ secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", -+ "# invalid mode -+- name: bad test 2 -+ mode: CCA -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93 -+ secretid: 0x0767668dd22f23fa675c4641e04bb4e991f443be4df13ce3896b8eeca59fcc10\n", -+ "# Accelerator with wrong CEX3 -+- name: bad test 3 -+ mode: Accel -+ mingen: Cex3\n", -+ "# Accelerator with wrong CEX9 -+- name: bad test 4 -+ mode: Accel -+ mingen: CEX9\n", -+ "# EP11 with mkvp missing -+- name: bad test 5 -+ mode: EP11 -+ serialnr: 93AADHZU42082261\n", -+ "# EP11 with non hex mkvp -+- name: bad test 6 -+ mode: EP11 -+ mkvp: 0xabcdefghijklmnopqqponmlkjihgfedcba -+ serialnr: 93AADHZU42082261\n", -+ "# EP11 with mkvp too big -+- name: bad test 7 -+ mode: EP11 -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb93aa -+ serialnr: 93AADHZU42082261\n", -+ "# EP11 with mkvp too small -+- name: bad test 8 -+ mode: EP11 -+ mkvp: 0xdb3c3b3c3f097dd55ec7eb0e7fdbcb -+ serialnr: 93AADHZU42082261\n", -+ "# EP11 with invalid CEXx -+- name: bad test 9 -+ mode: EP11 -+ mingen: CEX7 -+ mkvp: 0x00112233445566778899aabbccddeeff -+ serialnr: 93AADHZU42082261\n", -+ "# EP11 with invalid Serialnr -+- name: bad test 10 -+ mode: EP11 -+ mkvp: 0x00112233445566778899aabbccddeeff -+ serialnr: 93AADHZU4208226\n", -+ "# EP11 with invalid Serialnr -+- name: bad test 11 -+ mode: EP11 -+ mkvp: 0x00112233445566778899aabbccddeeff -+ serialnr: 93AAD ZU42082261\n", -+ "# EP11 with sha256(name) != secretid -+- name: bad test 12 -+ mode: EP11 -+ mkvp: 0x00112233445566778899aabbccddeeff -+ serialnr: AABBCCDDEEFFGGHH -+ secretid: 0x2ca853f959fc5ce5f1888cb48dae39514a27bb66520ac85f6073a7f678d262c0\n", -+ ]; -+ -+ const BAD_DESERIALIZE: [&str; 2] = [ -+ "/*\ntotal nonsense\n */\n", -+ "# wrong/unknown field -+- name: de-serialize failure 1 -+ type: EP11\n", -+ ]; -+ -+ fn write_yaml_config_to_temp_file(content: &str) -> Result { -+ let dir = env::temp_dir(); -+ let rnd = rand::random::(); -+ let fname = format!("{}/config-test-{}.yaml", dir.to_str().unwrap(), rnd); -+ let mut f = match File::create(&fname) { -+ Ok(f) => f, -+ Err(_) => return Err(format!("Failure creating temp file '{fname}'.")), -+ }; -+ match f.write_all(content.as_bytes()) { -+ Ok(_) => Ok(fname), -+ Err(_) => { -+ fs::remove_file(&fname).ok(); -+ Err(format!("Failure writing to temp file '{fname}'.")) -+ } -+ } -+ } -+ -+ #[test] -+ fn test_good_yaml() { -+ for yaml in GOOD_CONFIGS { -+ let f = write_yaml_config_to_temp_file(yaml).unwrap(); -+ let config = ApConfigList::read_and_validate_yaml_file(&f).unwrap(); -+ assert!(config.len() > 0); -+ fs::remove_file(&f).ok(); -+ } -+ } -+ #[test] -+ fn test_bad_yaml() { -+ for yaml in BAD_CONFIGS { -+ let f = write_yaml_config_to_temp_file(yaml).unwrap(); -+ let r = ApConfigList::read_and_validate_yaml_file(&f); -+ assert!(r.is_err()); -+ fs::remove_file(&f).ok(); -+ } -+ } -+ #[test] -+ fn test_invalid_deserizalize() { -+ for yaml in BAD_DESERIALIZE { -+ let f = write_yaml_config_to_temp_file(yaml).unwrap(); -+ let r = ApConfigList::read_and_validate_yaml_file(&f); -+ assert!(r.is_err()); -+ fs::remove_file(&f).ok(); -+ } -+ } -+ #[test] -+ fn test_sha256() { -+ assert!( -+ crate::helper::u8_to_hexstring(&sha256("Hello".as_bytes())) -+ == "185f8db32271fe25f561a6fc938b2e264306ec304eda518007d1764826381969" -+ ); -+ assert!( -+ crate::helper::u8_to_hexstring(&sha256("SECRET1".as_bytes())) -+ == "03153249db7ce46b0330ffb1a760b59710531af08ec4d7f8424a6870fae49360" -+ ); -+ assert!( -+ crate::helper::u8_to_hexstring(&sha256("SECRET2".as_bytes())) -+ == "258499e710e0bd3bb878d6bac7e478b30f3f3e72566989f638c4143d14f6c0b6" -+ ); -+ } -+} -diff --git a/rust/pvapconfig/src/helper.rs b/rust/pvapconfig/src/helper.rs -new file mode 100644 -index 0000000..c4670ce ---- /dev/null -+++ b/rust/pvapconfig/src/helper.rs -@@ -0,0 +1,272 @@ -+// SPDX-License-Identifier: MIT -+// -+// Copyright IBM Corp. 2023 -+// -+//! Collection of helper functions for pvapconfig -+// -+ -+use regex::Regex; -+use std::error::Error; -+use std::fs; -+use std::fs::{File, OpenOptions}; -+use std::io::{Read, Write}; -+use std::path::PathBuf; -+ -+pub const PATH_PVAPCONFIG_LOCK: &str = "/run/lock/pvapconfig.lock"; -+ -+/// Convert u8 slice to (lowercase) hex string -+pub fn u8_to_hexstring(slice: &[u8]) -> String { -+ let s = String::with_capacity(2 * slice.len()); -+ slice.iter().fold(s, |acc, e| acc + &format!("{e:02x}")) -+} -+ -+/// Convert hexstring to u8 vector -+/// The hexstring may contain whitespaces which are ignored. -+/// If there are other characters in there or if the number -+/// of hex characters is uneven panic() is called. -+/// # Panics -+/// Panics if the given string contains characters other than -+/// hex digits and whitespace. Panics if the number of hex digits -+/// is not even. -+#[cfg(test)] // currently only used in test code -+pub fn hexstring_to_u8(hex: &str) -> Vec { -+ let mut s = String::new(); -+ for c in hex.chars() { -+ if c.is_ascii_hexdigit() { -+ s.push(c); -+ } else if c.is_whitespace() { -+ // ignore -+ } else { -+ panic!("Invalid character '{c}'"); -+ } -+ } -+ if s.len() % 2 == 1 { -+ panic!("Uneven # of hex characters in '{s}'"); -+ } -+ let mut hex_bytes = s.as_bytes().iter().map_while(|b| match b { -+ b'0'..=b'9' => Some(b - b'0'), -+ b'a'..=b'f' => Some(b - b'a' + 10), -+ b'A'..=b'F' => Some(b - b'A' + 10), -+ _ => None, -+ }); -+ let mut bytes = Vec::with_capacity(s.len()); -+ while let (Some(h), Some(l)) = (hex_bytes.next(), hex_bytes.next()) { -+ bytes.push(h << 4 | l) -+ } -+ bytes -+} -+ -+/// Read sysfs file into string -+pub fn sysfs_read_string(fname: &str) -> Result> { -+ let mut file = File::open(fname)?; -+ let mut content = String::new(); -+ file.read_to_string(&mut content)?; -+ let trimmed_content = String::from(content.trim()); -+ Ok(trimmed_content) -+} -+ -+/// Write string into sysfs file -+pub fn sysfs_write_string(fname: &str, value: &str) -> Result<(), Box> { -+ let mut file = OpenOptions::new().write(true).open(fname)?; -+ file.write_all(value.as_bytes())?; -+ Ok(()) -+} -+ -+/// Read sysfs file content and parse as i32 value -+pub fn sysfs_read_i32(fname: &str) -> Result> { -+ let content = sysfs_read_string(fname)?; -+ Ok(content.parse::()?) -+} -+ -+/// Write an i32 value into a sysfs file -+pub fn sysfs_write_i32(fname: &str, value: i32) -> Result<(), Box> { -+ return sysfs_write_string(fname, &value.to_string()); -+} -+ -+/// For a given (sysfs) directory construct a list of all subdirs -+/// and give it back as a vector of strings. If there is no subdir, -+/// the vector is empty. -+pub fn sysfs_get_list_of_subdirs(dname: &str) -> Result, Box> { -+ let mut v: Vec = Vec::new(); -+ let entries = fs::read_dir(dname)?; -+ for entry in entries.flatten() { -+ let file_type = match entry.file_type() { -+ Ok(ft) => ft, -+ _ => continue, -+ }; -+ if !file_type.is_dir() { -+ continue; -+ } -+ let fname = match entry.file_name().into_string() { -+ Ok(s) => s, -+ _ => continue, -+ }; -+ v.push(fname); -+ } -+ Ok(v) -+} -+ -+/// For a given (sysfs) directory construct a list of all subdirs which -+/// match to the given regular expression and give the list back as a -+/// vector of strings. If there is no subdir, the vector is empty. -+pub fn sysfs_get_list_of_subdirs_matching_regex( -+ dname: &str, -+ regex: &str, -+) -> Result, Box> { -+ let mut v: Vec = Vec::new(); -+ let re = Regex::new(regex)?; -+ let entries = sysfs_get_list_of_subdirs(dname)?; -+ for entry in entries { -+ if re.is_match(&entry) { -+ v.push(entry); -+ } -+ } -+ Ok(v) -+} -+ -+/// LockFile for inter-process locking -+/// -+/// Simple class for process locking for pvapconfig. -+/// The lock concept is simple: The existence of a file is used as the -+/// locking indicator. If the file exists something is locked, if it does -+/// not exist something is not locked. In the lock file the PID of the -+/// process created the file ("owning this file") is written in. -+/// With the ProcessLock object leaving scope the associated lock file -+/// is automatically deleted. It is assumed that the creation of a file -+/// is an atomic operation - that's true for most filesystems but may -+/// cause problems with network based filesystems. -+/// Example: -+/// ``` -+/// let lock = LockFile::lock("/var/lock/process.lock"); -+/// assert!(lock.is_ok()); -+/// let lock2 = LockFile::lock("/var/lock/process.lock"); -+/// assert!(lock2.is_err()); -+/// drop(lock); -+/// let lock3 = LockFile::lock("/var/lock/process.lock"); -+/// assert!(lock3.is_ok()); -+/// ``` -+#[derive(Debug)] -+pub struct LockFile { -+ lockfile: PathBuf, -+} -+ -+impl LockFile { -+ /// Try to establish the lock file. -+ /// Upon success the given file is fresh created and has the pid of this -+ /// process written in. The function returns a new LockFile object -+ /// which has implemented the Drop Trait. So with this object going out -+ /// of scope the lock file is deleted. If establishing the lock file -+ /// fails for any reason (for example the file already exists), the -+ /// function fails with returning an Error string. This function does -+ /// NOT panic if establishing the lock file fails for any reason. If -+ /// the lock file could be esablished but writing in the PID fails, a -+ /// warning is printed but the function continues with returning a -+ /// LockFile object. -+ pub fn try_lock(fname: &str) -> Result { -+ let lockfile = PathBuf::from(fname); -+ let mut file = match OpenOptions::new() -+ .write(true) -+ .create_new(true) -+ .open(&lockfile) -+ { -+ Err(err) => { -+ return Err(format!( -+ "Failure trying to create lock file {fname}: {err:?}." -+ )) -+ } -+ Ok(f) => f, -+ }; -+ let _ = file -+ .write(format!("{}", std::process::id()).as_bytes()) -+ .map_err(|err| { -+ println!("Warning: could not write PID into lockfile {fname}: {err:?}.") -+ }); -+ Ok(LockFile { lockfile }) -+ } -+} -+ -+impl Drop for LockFile { -+ fn drop(&mut self) { -+ let _ = fs::remove_file(&self.lockfile).map_err(|err| { -+ println!( -+ "Warning: could not remove lockfile {}: {err:?}.", -+ self.lockfile.display() -+ ) -+ }); -+ } -+} -+ -+#[cfg(test)] -+mod tests { -+ -+ use super::*; -+ -+ // Only very simple tests -+ -+ const TEST_BYTES: [u8; 8] = [0x01, 0x23, 0x45, 0x67, 0x89, 0xab, 0xcd, 0xef]; -+ const TEST_HEXSTR: &str = "0123456789abcdef"; -+ -+ #[test] -+ fn test_u8_to_hexstring() { -+ let str = u8_to_hexstring(&TEST_BYTES); -+ assert!(str == TEST_HEXSTR); -+ } -+ #[test] -+ fn test_hexstring_to_u8() { -+ let bytes = hexstring_to_u8(TEST_HEXSTR); -+ assert!(bytes.as_slice() == TEST_BYTES); -+ } -+ #[test] -+ fn test_sysfs_read_string() { -+ let r = sysfs_read_string("/proc/cpuinfo"); -+ assert!(r.is_ok()); -+ } -+ #[test] -+ fn test_sysfs_read_i32() { -+ let r = sysfs_read_i32("/proc/sys/kernel/random/entropy_avail"); -+ assert!(r.is_ok()); -+ } -+ #[test] -+ fn test_sysfs_get_list_of_subdirs() { -+ let r = sysfs_get_list_of_subdirs("/proc/self"); -+ assert!(r.is_ok()); -+ let v = r.unwrap(); -+ assert!(!v.is_empty()); -+ } -+ #[test] -+ fn test_sysfs_get_list_of_subdirs_matching_regex() { -+ let r = sysfs_get_list_of_subdirs_matching_regex("/proc/self", "fd.*"); -+ assert!(r.is_ok()); -+ let v = r.unwrap(); -+ assert!(!v.is_empty()); -+ for e in v { -+ assert!(e.strip_prefix("fd").is_some()); -+ } -+ } -+ #[test] -+ fn test_sysfs_write_i32() { -+ const TEST_PATH: &str = "/tmp/test_sysfs_write_i32"; -+ let mut file = File::create(TEST_PATH).unwrap(); -+ let _ = file.write_all(b"XYZ"); -+ drop(file); -+ let r = sysfs_read_i32(TEST_PATH); -+ assert!(r.is_err()); -+ let r = sysfs_write_i32(TEST_PATH, 999); -+ assert!(r.is_ok()); -+ let r = sysfs_read_i32(TEST_PATH); -+ assert!(r.is_ok()); -+ let v = r.unwrap(); -+ assert!(v == 999); -+ let _ = fs::remove_file(TEST_PATH); -+ } -+ #[test] -+ fn test_lockfile() { -+ let r1 = LockFile::try_lock(PATH_PVAPCONFIG_LOCK); -+ assert!(r1.is_ok()); -+ let r2 = LockFile::try_lock(PATH_PVAPCONFIG_LOCK); -+ assert!(r2.is_err()); -+ drop(r1); -+ let r3 = LockFile::try_lock(PATH_PVAPCONFIG_LOCK); -+ assert!(r3.is_ok()); -+ } -+} -diff --git a/rust/pvapconfig/src/main.rs b/rust/pvapconfig/src/main.rs -new file mode 100644 -index 0000000..07899cd ---- /dev/null -+++ b/rust/pvapconfig/src/main.rs -@@ -0,0 +1,668 @@ -+// SPDX-License-Identifier: MIT -+// -+// Copyright IBM Corp. 2023 -+// -+//! pvapconfig - Tool to automatically set up the AP configuration -+//! within an IBM Secure Execution guest. -+// -+ -+mod ap; -+mod cli; -+mod config; -+mod helper; -+mod uv; -+ -+use ap::{Apqn, ApqnList}; -+use cli::ARGS; -+use config::{ApConfigEntry, ApConfigList}; -+use helper::{LockFile, PATH_PVAPCONFIG_LOCK}; -+use pv::uv::{ListableSecretType, SecretList}; -+use std::process::ExitCode; -+use utils::release_string; -+ -+/// Simple macro for -+/// if Cli::verbose() { -+/// print!(...); -+/// } -+macro_rules! info { -+ ($($arg:tt)*) => {{ -+ if ARGS.verbose() { -+ print!($($arg)*); -+ } -+ }}; -+} -+ -+/// Simple macro for the main function only -+/// Does a eprintln of the arguments and then -+/// return with exit failure. -+macro_rules! println_and_exit_failure { -+ ($($arg:tt)*) => {{ -+ eprintln!($($arg)*); -+ return ExitCode::FAILURE; -+ }}; -+} -+ -+/// Simple macro for the main function only -+/// Check if given object has is_err() true and -+/// then eprintln the unwrapped error and -+/// returns with exit failure. -+macro_rules! on_error_print_and_exit { -+ ($r:expr) => { -+ if $r.is_err() { -+ eprintln!("{}", $r.unwrap_err()); -+ return ExitCode::FAILURE; -+ } -+ }; -+} -+ -+fn main() -> ExitCode { -+ // handle version option -+ if cli::ARGS.version { -+ println!( -+ "{} version {}\nCopyright IBM Corp. 2023", -+ env!("CARGO_PKG_NAME"), -+ release_string!() -+ ); -+ return ExitCode::SUCCESS; -+ } -+ -+ // make sure only one pvapconfig instance is running -+ let r = LockFile::try_lock(PATH_PVAPCONFIG_LOCK); -+ on_error_print_and_exit!(r); -+ let _lockfile = r.unwrap(); -+ -+ // AP bus check -+ info!("Checking AP bus support and facilities...\n"); -+ let r = ap::check_ap_bus_support(); -+ on_error_print_and_exit!(r); -+ let r = ap::ap_bus_has_apsb_support(); -+ on_error_print_and_exit!(r); -+ info!("AP bus support and facilities are ok.\n"); -+ -+ // UV check -+ info!("Checking UV support and environment...\n"); -+ if !pv::misc::pv_guest_bit_set() { -+ println_and_exit_failure!("Failure: this is not a SE guest."); -+ } -+ let r = uv::has_list_secrets_facility(); -+ on_error_print_and_exit!(r); -+ info!("UV support and environment is ok.\n"); -+ -+ // read configuration -+ let configfile: &str = match &cli::ARGS.config { -+ Some(f) => f, -+ _ => cli::PATH_DEFAULT_CONFIG_FILE, -+ }; -+ info!( -+ "Reading AP configuration entries from file '{}'...\n", -+ configfile -+ ); -+ let apconfig: ApConfigList = match ApConfigList::read_and_validate_yaml_file(configfile) { -+ Ok(apcfg) => apcfg, -+ Err(err) => println_and_exit_failure!("{}", err), -+ }; -+ if apconfig.is_empty() { -+ println!( -+ "No AP configuration entries in config file '{}': Nothing to do.", -+ configfile -+ ); -+ return ExitCode::SUCCESS; -+ } -+ info!("Found {} AP configuration entries.\n", apconfig.len()); -+ -+ // get list of secrets from UV -+ info!("Fetching list of secrets from UV...\n"); -+ let secrets: SecretList = match uv::gather_secrets() { -+ Err(e) => println_and_exit_failure!("{}", e), -+ Ok(los) => los, -+ }; -+ info!("Fetched {} Secret entries from UV.\n", secrets.len()); -+ -+ // Warning if no UV secrets given but AP config entries require it -+ let non_accel_apc = apconfig -+ .iter() -+ .filter(|apc| apc.mode != config::STR_MODE_ACCEL) -+ .count(); -+ if non_accel_apc > 0 && secrets.is_empty() { -+ println!( -+ "Warning: No UV Secrets given but at least one AP config entry requires a Secret." -+ ); -+ } -+ -+ info!("Waiting for AP bus bindings complete...\n"); -+ if !ap::wait_for_ap_bus_bindings_complete() { -+ return ExitCode::FAILURE; -+ } -+ info!("Fetching list of available APQNs...\n"); -+ let mut apqns: ApqnList = match ApqnList::gather_apqns() { -+ Some(l) => l, -+ None => return ExitCode::FAILURE, -+ }; -+ if apqns.is_empty() { -+ info!("List of available APQNs is empty: So there's nothing to do.\n"); -+ return ExitCode::SUCCESS; -+ } -+ info!("Found {} APQNs.\n", apqns.len()); -+ // check MK restriction -+ if !apqns.check_mk_restriction() { -+ return ExitCode::FAILURE; -+ } -+ -+ // now the real work -+ info!("Applying AP configuration...\n"); -+ let n = match do_ap_config(&mut apqns, &secrets, &apconfig, false) { -+ Err(e) => println_and_exit_failure!("{}", e), -+ Ok(n) => n, -+ }; -+ -+ if n == 0 { -+ println_and_exit_failure!( -+ "None out of {} AP config entries could be applied.", -+ apconfig.len() -+ ); -+ } else if ARGS.strict() && n != apconfig.len() { -+ println_and_exit_failure!( -+ "Strict flag given and only {} out of {} AP config entries have been applied.", -+ n, -+ apconfig.len() -+ ); -+ } -+ -+ info!( -+ "Successfully applied {} out of {} AP config entries.\n", -+ n, -+ apconfig.len() -+ ); -+ -+ ExitCode::SUCCESS -+} -+ -+/// The real worker function -+/// -+/// This is the real algorithm which is trying to apply the -+/// AP configuration read from the config file to the existing -+/// APQNs with the info from the list of secrets from the UV. -+/// Returns the nr of AP config entries which are fullfilled -+/// after the function ended. -+/// apqns needs to be mutable as the function does a resort -+/// but content stays the same. -+fn do_ap_config( -+ apqns: &mut ApqnList, -+ secrets: &SecretList, -+ apconfig: &ApConfigList, -+ fntest: bool, -+) -> Result { -+ let mut resolved_entries = 0; -+ let mut apconfig_done = vec![false; apconfig.len()]; -+ let mut apqn_done = vec![false; apqns.len()]; -+ -+ // Preparation: Sort APQNs by generation. -+ // All the following steps iterate through the list -+ // of APQNs. So by sorting the APQNs starting with -+ // highest card generation down to the older card -+ // generations we prefer newer card generations over -+ // older card generations. -+ apqns.sort_by_gen(); -+ -+ // Step 1: -+ // Go through all AP config entries and try to find an APQN -+ // which already matches to this entry. If such an APQN is -+ // found mark the AP config entry as done, and mark the APQN -+ // as used so that entry and APQN will get skipped over in -+ // the next steps. -+ -+ for (ci, apc) in apconfig.iter().enumerate() { -+ let cistr = if !apc.name.is_empty() { -+ format!("#{} '{}'", ci + 1, apc.name) -+ } else { -+ format!("#{}", ci + 1) -+ }; -+ for (ai, apqn) in apqns.iter().enumerate() { -+ if apqn_done[ai] { -+ continue; -+ } -+ if !config_and_apqn_match(apc, apqn) { -+ continue; -+ } -+ if fntest { -+ continue; -+ } -+ match apqn.mode { -+ ap::ApqnMode::Accel => { -+ // check bind state of this APQN -+ let bind_state_ok = match apqn.bind_state() { -+ Err(err) => { -+ eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}"); -+ false -+ } -+ Ok(ap::BindState::Bound) => true, -+ Ok(_) => false, -+ }; -+ if !bind_state_ok { -+ continue; -+ } -+ // This APQN matches to the current AP config entry and is already bound. -+ // So this AP config entry is satisfied: mark this config enty as done -+ // and mark this APQN as used. -+ info!("Accelerator APQN {apqn} already satisfies AP config entry {cistr}.\n"); -+ apconfig_done[ci] = true; -+ apqn_done[ai] = true; -+ resolved_entries += 1; -+ break; -+ } -+ ap::ApqnMode::Ep11 => { -+ // check association state of this APQN -+ let (assoc_state_ok, assoc_idx) = match apqn.associate_state() { -+ Err(err) => { -+ eprintln!( -+ "Warning: Failure reading APQN {apqn} associate state: {err}" -+ ); -+ (false, 0) -+ } -+ Ok(ap::AssocState::Associated(idx)) => (true, idx), -+ Ok(_) => (false, 0), -+ }; -+ if !assoc_state_ok { -+ continue; -+ } -+ // check association index -+ let r = secrets.iter().find(|&se| { -+ se.stype() == ListableSecretType::Association -+ && se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE -+ && se.index() == assoc_idx -+ && helper::u8_to_hexstring(se.id()) == apc.secretid -+ }); -+ if r.is_none() { -+ continue; -+ } -+ // This APQN matches to the current AP config entry and is already -+ // associated with the right secret id. So this AP config entry is -+ // satisfied: mark this config enty as done and mark this APQN as used. -+ info!("EP11 APQN {apqn} already satisfies AP config entry {cistr}.\n"); -+ apconfig_done[ci] = true; -+ apqn_done[ai] = true; -+ resolved_entries += 1; -+ break; -+ } -+ _ => { -+ // (currently) unknown/unsupported APQN mode -+ } -+ } -+ } -+ } -+ -+ // Step 2: -+ // All APQNs NOT marked as done are now examined for their bind -+ // and association state and maybe reset to "unbound". -+ -+ for (ai, apqn) in apqns.iter().enumerate() { -+ if apqn_done[ai] || fntest { -+ continue; -+ } -+ match apqn.bind_state() { -+ Err(err) => eprintln!("Warning: Failure reading APQN {apqn} bind state: {err}"), -+ Ok(ap::BindState::Bound) => { -+ info!("Unbind APQN {apqn} as this bind/associate does not match to any AP config entry.\n"); -+ if !ARGS.dryrun() { -+ if let Err(err) = apqn.set_bind_state(ap::BindState::Unbound) { -+ return Err(format!("Failure unbinding APQN {apqn}: {err}")); -+ } -+ } -+ } -+ Ok(_) => {} -+ }; -+ } -+ -+ // Step 3: -+ // Go through all remaining AP config entries and try to fullfill each -+ // by searching for an APQN which would match to this config entry and -+ // then prepare this APQN (bind, maybe associate). -+ for (ci, apc) in apconfig.iter().enumerate() { -+ let cistr = if !apc.name.is_empty() { -+ format!("#{} '{}'", ci + 1, apc.name) -+ } else { -+ format!("#{}", ci + 1) -+ }; -+ if apconfig_done[ci] { -+ continue; -+ } -+ for (ai, apqn) in apqns.iter().enumerate() { -+ if apqn_done[ai] { -+ continue; -+ } -+ if !config_and_apqn_match(apc, apqn) { -+ continue; -+ } -+ match apqn.mode { -+ ap::ApqnMode::Accel => { -+ // try to bind this accelerator APQN -+ if ARGS.verbose() || fntest { -+ println!("Bind APQN {apqn} to match to AP config entry {cistr}."); -+ } -+ if !(ARGS.dryrun() || fntest) { -+ if let Err(err) = apqn.set_bind_state(ap::BindState::Bound) { -+ // bind failed, unbind/reset this apqn, return with failure -+ let _ = apqn.set_bind_state(ap::BindState::Unbound); -+ return Err(format!("Failure binding APQN {apqn}: {err}")); -+ } -+ } -+ apconfig_done[ci] = true; -+ apqn_done[ai] = true; -+ resolved_entries += 1; -+ break; -+ } -+ ap::ApqnMode::Ep11 => { -+ // EP11 needs bind and associate, but before doing this let's -+ // check out which secret index to use with the associate -+ let se = match secrets.iter().find(|&se| { -+ se.stype() == ListableSecretType::Association -+ && se.id().len() == uv::AP_ASSOC_SECRET_ID_SIZE -+ && helper::u8_to_hexstring(se.id()) == apc.secretid -+ }) { -+ None => { -+ eprintln!("Warning: Secret id '{}' from config entry {} not found in UV secrets list.", -+ apc.secretid, cistr); -+ break; -+ } -+ Some(se) => se, -+ }; -+ // try to bind -+ if ARGS.verbose() || fntest { -+ println!( -+ "Bind APQN {apqn} to match to AP config entry {cistr} (step 1/2)." -+ ); -+ } -+ if !(ARGS.dryrun() || fntest) { -+ if let Err(err) = apqn.set_bind_state(ap::BindState::Bound) { -+ // bind failed, unbind/reset this apqn, return with failure -+ let _ = apqn.set_bind_state(ap::BindState::Unbound); -+ return Err(format!("Failure binding APQN {}: {}", apqn, err)); -+ } -+ } -+ // try to associate -+ if ARGS.verbose() || fntest { -+ println!( -+ "Associate APQN {} with uv secrets index {} to match AP config entry {} (step 2/2).", -+ apqn, se.index(), cistr -+ ); -+ } -+ if !(ARGS.dryrun() || fntest) { -+ let apas = ap::AssocState::Associated(se.index()); -+ apqn.set_associate_state(apas) -+ .map_err(|err| format!("Failure associating APQN {apqn}: {err}"))?; -+ } -+ apconfig_done[ci] = true; -+ apqn_done[ai] = true; -+ resolved_entries += 1; -+ break; -+ } -+ _ => { -+ // (currently) unknown/unsupported APQN mode -+ } -+ } -+ } -+ } -+ -+ Ok(resolved_entries) -+} -+ -+/// # Panics -+/// Panics if mingen for an accelerator has not a number as the 4th character. -+/// Panics if mingen for an ep11 has not a number as the 4th character. -+/// Please note this can not happen, as mingen is already checked via RE -+/// during storing the value into mingen. -+fn config_and_apqn_match(apc: &ApConfigEntry, apqn: &Apqn) -> bool { -+ if apc.mode == config::STR_MODE_ACCEL && apqn.mode == ap::ApqnMode::Accel { -+ // config and apqn are accelerators -+ // maybe check mingen -+ if !apc.mingen.is_empty() { -+ let mingen = &apc.mingen[3..].parse::().unwrap(); -+ if mingen < &apqn.gen { -+ return false; -+ } -+ } -+ return true; -+ } else if apc.mode == config::STR_MODE_EP11 && apqn.mode == ap::ApqnMode::Ep11 { -+ // config and apqn are ep11 -+ let info = match &apqn.info { -+ Some(ap::ApqnInfo::Ep11(i)) => i, -+ _ => return false, -+ }; -+ // maybe check mingen -+ if !apc.mingen.is_empty() { -+ let mingen = &apc.mingen[3..].parse::().unwrap(); -+ if mingen < &apqn.gen { -+ return false; -+ } -+ } -+ // maybe check serialnr -+ if !apc.serialnr.is_empty() && apc.serialnr != info.serialnr { -+ return false; -+ } -+ // check mkvp, currently an ep11 config entry must state an mkvp value -+ // whereas an ep11 info from an APQN may have an empty mkvp value to -+ // indicate that there is no WK set on this APQN. -+ if apc.mkvp != info.mkvp { -+ return false; -+ } -+ return true; -+ } -+ false -+} -+ -+#[cfg(test)] -+mod tests { -+ -+ use super::*; -+ use helper::hexstring_to_u8; -+ use pv::uv::SecretEntry; -+ -+ // This is more or less only a test for the do_ap_config() function -+ // However, this is THE main functionality of the whole application. -+ -+ fn make_test_apqns() -> Vec { -+ let mut v = Vec::new(); -+ v.push(ap::Apqn { -+ name: String::from("10.0007"), -+ card: 16, -+ domain: 7, -+ gen: 8, -+ mode: ap::ApqnMode::Accel, -+ info: Option::Some(ap::ApqnInfo::Accel(ap::ApqnInfoAccel {})), -+ }); -+ v.push(ap::Apqn { -+ name: String::from("11.0008"), -+ card: 17, -+ domain: 8, -+ gen: 8, -+ mode: ap::ApqnMode::Ep11, -+ info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 { -+ serialnr: String::from("93AADFK719460083"), -+ mkvp: String::from("db3c3b3c3f097dd55ec7eb0e7fdbcb93"), -+ })), -+ }); -+ v.push(ap::Apqn { -+ name: String::from("12.0009"), -+ card: 18, -+ domain: 9, -+ gen: 8, -+ mode: ap::ApqnMode::Ep11, -+ info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 { -+ serialnr: String::from("93AADHZU42082261"), -+ mkvp: String::from("4a27bb66520ac85f6073a7f678d262c0"), -+ })), -+ }); -+ v.push(ap::Apqn { -+ name: String::from("12.000a"), -+ card: 18, -+ domain: 10, -+ gen: 8, -+ mode: ap::ApqnMode::Ep11, -+ info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 { -+ serialnr: String::from("93AADHZU42082261"), -+ mkvp: String::from("383d2a9ab781f35343554c5b3d9337cd"), -+ })), -+ }); -+ v.push(ap::Apqn { -+ name: String::from("13.000d"), -+ card: 19, -+ domain: 13, -+ gen: 8, -+ mode: ap::ApqnMode::Ep11, -+ info: Option::Some(ap::ApqnInfo::Ep11(ap::ApqnInfoEp11 { -+ serialnr: String::from("87HU397G150TZGR"), -+ mkvp: String::new(), -+ })), -+ }); -+ v.push(ap::Apqn { -+ name: String::from("13.000f"), -+ card: 19, -+ domain: 15, -+ gen: 8, -+ mode: ap::ApqnMode::Ep11, -+ info: Option::None, -+ }); -+ return v; -+ } -+ -+ fn make_assoc_secretentry(idx: u16, hexidstr: &str) -> SecretEntry { -+ let id = hexstring_to_u8(hexidstr); -+ let idlen: u32 = id.len().try_into().unwrap(); -+ let idarray = <&[u8; 32]>::try_from(id.as_slice()).unwrap(); -+ SecretEntry::new(idx, ListableSecretType::Association, *idarray, idlen) -+ } -+ -+ fn make_test_secrets() -> Vec { -+ let mut v: Vec = Vec::new(); -+ v.push(make_assoc_secretentry( -+ 33, -+ "3333333333333333333333333333333333333333333333333333333333333333", -+ )); -+ v.push(make_assoc_secretentry( -+ 13, -+ "bc9d46c052bc3574454c5715757274629a283767ed237922cfb8651c0e77320a", -+ )); -+ v.push(make_assoc_secretentry( -+ 44, -+ "4444444444444444444444444444444444444444444444444444444444444444", -+ )); -+ v.push(make_assoc_secretentry( -+ 15, -+ "06cdbbac76a595b481110d108154bc05ebbf900a0f16e36a24045998934fb1e9", -+ )); -+ v.push(make_assoc_secretentry( -+ 17, -+ "6831af07f8c8e7309a3ace9f3b5554d34e3eaa4a27a08fdee469e367c3fa3e9e", -+ )); -+ return v; -+ } -+ -+ fn make_test_apconfigs() -> Vec { -+ let mut v: Vec = Vec::new(); -+ v.push(config::ApConfigEntry { -+ name: String::from("test_1"), -+ description: String::from("test_1"), -+ mode: String::from("accel"), -+ mkvp: String::from(""), -+ serialnr: String::from(""), -+ mingen: String::from("cex8"), -+ secretid: String::from(""), -+ }); -+ v.push(config::ApConfigEntry { -+ name: String::from("test_2"), -+ description: String::from("test_2"), -+ mode: String::from("ep11"), -+ mkvp: String::from("db3c3b3c3f097dd55ec7eb0e7fdbcb93"), -+ serialnr: String::from("93AADFK719460083"), -+ mingen: String::from("cex8"), -+ secretid: String::from( -+ "bc9d46c052bc3574454c5715757274629a283767ed237922cfb8651c0e77320a", -+ ), -+ }); -+ v.push(config::ApConfigEntry { -+ name: String::from("test_3"), -+ description: String::from("test_3"), -+ mode: String::from("ep11"), -+ mkvp: String::from("4a27bb66520ac85f6073a7f678d262c0"), -+ serialnr: String::from(""), -+ mingen: String::from("cex8"), -+ secretid: String::from( -+ "06cdbbac76a595b481110d108154bc05ebbf900a0f16e36a24045998934fb1e9", -+ ), -+ }); -+ v.push(config::ApConfigEntry { -+ name: String::from("test_4"), -+ description: String::from("test_4"), -+ mode: String::from("ep11"), -+ mkvp: String::from("8be1eaf5c44e2fa8b18804551b604b1b"), -+ serialnr: String::from(""), -+ mingen: String::from("cex8"), -+ secretid: String::from( -+ "6831af07f8c8e7309a3ace9f3b5554d34e3eaa4a27a08fdee469e367c3fa3e9e", -+ ), -+ }); -+ return v; -+ } -+ -+ #[test] -+ fn test_do_ap_config_invocation_1() { -+ let test_apqns = make_test_apqns(); -+ let mut apqns: Vec = Vec::new(); -+ apqns.push(test_apqns[0].clone()); -+ let secrets: Vec = Vec::new(); -+ let secretlist = SecretList::new(secrets.len() as u16, secrets); -+ let test_apconfigs = make_test_apconfigs(); -+ let mut apconfig: Vec = Vec::new(); -+ apconfig.push(test_apconfigs[0].clone()); -+ let apcfglist = ApConfigList::from_apconfigentry_vec(apconfig); -+ let mut apqnlist = ApqnList::from_apqn_vec(apqns); -+ let r = do_ap_config(&mut apqnlist, &secretlist, &apcfglist, true); -+ assert!(r.is_ok()); -+ let n = r.unwrap(); -+ assert!(n == 1); -+ } -+ -+ #[test] -+ fn test_do_ap_config_invocation_2() { -+ let test_apqns = make_test_apqns(); -+ let mut apqns: Vec = Vec::new(); -+ apqns.push(test_apqns[1].clone()); -+ let mut secrets = make_test_secrets(); -+ while secrets.len() > 2 { -+ secrets.pop(); -+ } -+ let secretlist = SecretList::new(secrets.len() as u16, secrets); -+ let test_apconfigs = make_test_apconfigs(); -+ let mut apconfig: Vec = Vec::new(); -+ apconfig.push(test_apconfigs[1].clone()); -+ let apcfglist = ApConfigList::from_apconfigentry_vec(apconfig); -+ let mut apqnlist = ApqnList::from_apqn_vec(apqns); -+ let r = do_ap_config(&mut apqnlist, &secretlist, &apcfglist, true); -+ assert!(r.is_ok()); -+ let n = r.unwrap(); -+ assert!(n == 1); -+ } -+ -+ #[test] -+ fn test_do_ap_config_invocation_3() { -+ let test_apqns = make_test_apqns(); -+ let mut apqns: Vec = Vec::new(); -+ for a in test_apqns.iter() { -+ apqns.push(a.clone()); -+ } -+ apqns.reverse(); -+ let secrets = make_test_secrets(); -+ let secretlist = SecretList::new(secrets.len() as u16, secrets); -+ let test_apconfigs = make_test_apconfigs(); -+ let mut apconfig: Vec = Vec::new(); -+ for c in test_apconfigs.iter() { -+ apconfig.push(c.clone()); -+ } -+ let apcfglist = ApConfigList::from_apconfigentry_vec(apconfig); -+ let mut apqnlist = ApqnList::from_apqn_vec(apqns); -+ let r = do_ap_config(&mut apqnlist, &secretlist, &apcfglist, true); -+ assert!(r.is_ok()); -+ let n = r.unwrap(); -+ assert!(n == 3, "n = {} != 3", n); -+ } -+} -diff --git a/rust/pvapconfig/src/uv.rs b/rust/pvapconfig/src/uv.rs -new file mode 100644 -index 0000000..2f98bd5 ---- /dev/null -+++ b/rust/pvapconfig/src/uv.rs -@@ -0,0 +1,105 @@ -+// SPDX-License-Identifier: MIT -+// -+// Copyright IBM Corp. 2023 -+// -+//! UV related functions for pvapconfig -+// -+ -+use pv::uv::{ListCmd, SecretList, UvDevice, UvcSuccess}; -+use regex::Regex; -+use std::path::Path; -+ -+/// The byte size of association secret of type 2 in struct SecretEntry -+pub const AP_ASSOC_SECRET_ID_SIZE: usize = 32; -+ -+const PATH_SYS_FW_UV_FACILITIES: &str = "/sys/firmware/uv/query/facilities"; -+ -+const RE_UV_FACILITIES: &str = r"^(0x)?([[:xdigit:]]+)"; -+const RE_UV_FAC_BIT_LIST_SECRETS: u32 = 30; -+ -+/// Check UV facilities to offer the 'list secrets' call. -+/// Returns a Result with Ok(()) if the 'list secrets' feature -+/// is available, otherwise an Err(reasonstring) is returned where -+/// the string denotes a hint which can be displayed. -+/// # Panics -+/// Panics if the compilation of a static regular expression fails. -+/// Panics if RE_UV_FACILITIES does not match. -+pub fn has_list_secrets_facility() -> Result<(), String> { -+ if !Path::new(PATH_SYS_FW_UV_FACILITIES).is_file() { -+ return Err(format!( -+ "UV facilities sysfs attribute not found (file {} does not exist).", -+ PATH_SYS_FW_UV_FACILITIES -+ )); -+ } -+ let facstr = match crate::helper::sysfs_read_string(PATH_SYS_FW_UV_FACILITIES) { -+ Ok(s) => s, -+ Err(err) => { -+ return Err(format!( -+ "Failure reading UV facilities from {PATH_SYS_FW_UV_FACILITIES} ({:?}).", -+ err -+ )) -+ } -+ }; -+ let re_uv_facilities = Regex::new(RE_UV_FACILITIES).unwrap(); -+ if !re_uv_facilities.is_match(&facstr) { -+ Err(format!("Failure parsing UV facilities entry '{facstr}'.")) -+ } else { -+ let caps = re_uv_facilities.captures(&facstr).unwrap(); -+ let fachex = caps.get(2).unwrap().as_str(); -+ let i: usize = RE_UV_FAC_BIT_LIST_SECRETS as usize / 4; -+ if i >= fachex.len() { -+ return Err(format!("Failure parsing UV facilities entry '{fachex}'.")); -+ } -+ let nibble = u32::from_str_radix(&fachex[i..i + 1], 16).unwrap(); -+ const THEBIT: u32 = 1 << (3 - (RE_UV_FAC_BIT_LIST_SECRETS % 4)); -+ if nibble & THEBIT == 0 { -+ return Err("The 'list secret' feature is missing on this UV.".to_string()); -+ } -+ Ok(()) -+ } -+} -+ -+/// Fetch the list of secrets from the UV. -+/// Returns Err(errorstring) on error or -+/// Ok(SecretList) on success. -+/// The list may be empty if the UV doesn't have any secrets stored. -+pub fn gather_secrets() -> Result { -+ let uv = match UvDevice::open() { -+ Err(e) => return Err(format!("Failed to open UV device: {:?}.", e)), -+ Ok(u) => u, -+ }; -+ let mut cmd = ListCmd::default(); -+ match uv.send_cmd(&mut cmd).map_err(|e| format!("{e:?}"))? { -+ UvcSuccess::RC_SUCCESS => (), -+ UvcSuccess::RC_MORE_DATA => println!("Warning: There is more data available than expected"), -+ }; -+ cmd.try_into().map_err(|e| format!("{e:?}")) -+} -+ -+#[cfg(test)] -+mod tests { -+ -+ use super::*; -+ -+ // As the name says: check for list secrets feature bit in UV facilities. -+ #[test] -+ fn test_has_list_secrets_facility() { -+ let r = has_list_secrets_facility(); -+ if pv::misc::pv_guest_bit_set() { -+ assert!(r.is_ok()); -+ } else { -+ assert!(r.is_err()); -+ } -+ } -+ -+ // Simple invocation of the list_secrets function. Should not fail -+ #[test] -+ fn test_list_secrets() { -+ let r = gather_secrets(); -+ if pv::misc::pv_guest_bit_set() { -+ assert!(r.is_ok()); -+ } else { -+ assert!(r.is_err()); -+ } -+ } -+} --- -2.43.0 - diff --git a/SOURCES/s390utils-2.33.1-rhel.patch b/SOURCES/s390utils-2.33.1-rhel.patch new file mode 100644 index 0000000..c97b193 --- /dev/null +++ b/SOURCES/s390utils-2.33.1-rhel.patch @@ -0,0 +1,311 @@ +From ab26b43985f44b71abf40f8d0e50bfbd0808f8b9 Mon Sep 17 00:00:00 2001 +From: =?UTF-8?q?Jan=20H=C3=B6ppner?= +Date: Fri, 14 Jun 2024 14:25:40 +0200 +Subject: [PATCH 1/2] s390-tools: Replace 'which' with built-in 'command -v' +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +There are still a few scripts using the 'which' command to determine +either the full path or the mere existence of an executable. +Some of these scripts might run in minimal environments where 'which' is +not available due to dependency restriction. 'which' is also considered +unreliable for historical implementation details. + +Use the POSIX defined [1] built-in 'command -v' instead to reduce +package dependencies and improve reliability. + +[1] https://pubs.opengroup.org/onlinepubs/9699919799/ +Reviewed-by: Benjamin Block +Signed-off-by: Jan Höppner +Signed-off-by: Steffen Eiden +(cherry picked from commit 0b0960254e8c2b556cefa97f83651a92f54a5e42) +--- + iucvterm/doc/ts-shell/iucvconn_on_login | 2 +- + netboot/mk-pxelinux-ramfs | 2 +- + ziomon/ziomon | 3 +-- + 3 files changed, 3 insertions(+), 4 deletions(-) + +diff --git a/iucvterm/doc/ts-shell/iucvconn_on_login b/iucvterm/doc/ts-shell/iucvconn_on_login +index 860c4cc..92010df 100755 +--- a/iucvterm/doc/ts-shell/iucvconn_on_login ++++ b/iucvterm/doc/ts-shell/iucvconn_on_login +@@ -15,7 +15,7 @@ + prog_name=`basename $0` + guest_name=${USER:-`whoami 2>/dev/null`} + terminal=lnxhvc0 +-iucvconn=`which iucvconn 2>/dev/null` ++iucvconn=`command -v iucvconn 2>/dev/null` + + __error() { + printf "$prog_name: $@\n" >&2 +diff --git a/netboot/mk-pxelinux-ramfs b/netboot/mk-pxelinux-ramfs +index 64ebde1..07f8b07 100755 +--- a/netboot/mk-pxelinux-ramfs ++++ b/netboot/mk-pxelinux-ramfs +@@ -127,7 +127,7 @@ echo "$cmd: Copying kexec" + # Install both binary and required shared libraries + OLDPATH=$PATH + PATH=$OLDPATH:/sbin:/usr/sbin +-kexec_bin=$(which kexec) ++kexec_bin=$(command -v kexec) + kexec_sos=$(sharedobjs $kexec_bin) + PATH=$OLDPATH + +diff --git a/ziomon/ziomon b/ziomon/ziomon +index fd6248a..d1545cc 100755 +--- a/ziomon/ziomon ++++ b/ziomon/ziomon +@@ -667,8 +667,7 @@ function check_free_space_mileage() { + + + function check_blktrace() { +- which blktrace >/dev/null 2>&1; +- if [ $? -ne 0 ]; then ++ if ! command -v blktrace >/dev/null 2>&1; then + echo "$WRP_TOOLNAME: Could not find blktrace. Please make sure that the blktrace package is installed and matches the level in the documentation."; + exit 1; + fi +-- +2.45.2 + + +From 9770d8bff4b2ef6f01af1c273d1eac121b672a85 Mon Sep 17 00:00:00 2001 +From: Marc Hartmayer +Date: Thu, 6 Jun 2024 15:07:45 +0000 +Subject: [PATCH 2/2] Revert "zipl/src: Fix problems when target parameters are + specified by user" + +The commit fb0b6263d1a9 ("zipl/src: Fix problems when target parameters +are specified by user") breaks the case where the user has to provide +all target attributes via the zipl command line, because the target +device cannot be used to retrieve those attributes. + +$ zipl -V --blsdir /tmp/tmp.xHmFUdgBCi/boot//loader/entries/ --config /tmp/tmp.dSv9MJ3svs +Looking for components in '/lib/s390-tools' +Using config file '/tmp/tmp.dSv9MJ3svs' (from command line) +Using BLS config file '/tmp/tmp.xHmFUdgBCi/boot//loader/entries//50_normal.conf' +Using BLS config file '/tmp/tmp.xHmFUdgBCi/boot//loader/entries//45_normal_swiotlb.conf' +Using BLS config file '/tmp/tmp.xHmFUdgBCi/boot//loader/entries//40_pv.conf' +Using BLS config file '/tmp/tmp.xHmFUdgBCi/boot//loader/entries//30_pv_reboot.conf' +Secure boot support: yes +Target device information + Device..........................: 2b:00 + Device name.....................: nbd0 *) + Device driver name..............: nbd + Type............................: disk device + Disk layout.....................: SCSI disk layout *) + Geometry - start................: 2048 *) + File system block size..........: 4096 + Physical block size.............: 512 *) + Device size in physical blocks..: 2095071 + *) Data provided by user. +Building bootmap in '/tmp/tmp.xHmFUdgBCi/boot/' +Building menu 'zipl-automatic-menu' +Adding #1: IPL section 'Normal Guest' (default) + initial ramdisk...: /tmp/tmp.xHmFUdgBCi/boot//ramdisk-s390x + kernel image......: /tmp/tmp.xHmFUdgBCi/boot//vmlinux-s390x +zIPL environment block content: +Error: Could not add image file '/tmp/tmp.xHmFUdgBCi/boot//vmlinux-s390x': Could not get disk geometry + +This reverts commit fb0b6263d1a9a497760a21ccb178748ec5ccf955. + +Acked-by: Eduard Shishkin +Signed-off-by: Marc Hartmayer +Signed-off-by: Steffen Eiden +(cherry picked from commit 63ff07ba38dda99e2661a097d05266555c727a2e) +--- + zipl/include/job.h | 1 - + zipl/src/bootmap.c | 40 +++++++++++++++++++--------------------- + zipl/src/disk.c | 3 --- + zipl/src/job.c | 3 ++- + 4 files changed, 21 insertions(+), 26 deletions(-) + +diff --git a/zipl/include/job.h b/zipl/include/job.h +index e46788b..597d400 100644 +--- a/zipl/include/job.h ++++ b/zipl/include/job.h +@@ -152,7 +152,6 @@ static inline int target_parameters_are_set(struct job_target_data *td) + + int job_get(int argc, char* argv[], struct job_data** data); + void job_free(struct job_data* job); +-void free_target_data(struct job_target_data *td); + int type_from_target(char *target, disk_type_t *type); + int check_job_dump_images(struct job_dump_data* dump, char* name); + int check_job_images_ngdump(struct job_dump_data* dump, char* name); +diff --git a/zipl/src/bootmap.c b/zipl/src/bootmap.c +index 82b77d0..f6f5bac 100644 +--- a/zipl/src/bootmap.c ++++ b/zipl/src/bootmap.c +@@ -304,6 +304,7 @@ static int add_component_file_range(struct install_set *bis, + address_t load_address, + size_t trailer, void *component, + int add_files, ++ struct job_target_data *target, + int comp_id, int menu_idx, + int program_table_id) + { +@@ -338,15 +339,8 @@ static int add_component_file_range(struct install_set *bis, + return -1; + } + } else { +- /* +- * Make sure that file is on target device. +- * For this, retrieve info of the underlying disk without +- * any user hints +- */ +- struct job_target_data tmp = {.source = source_unknown}; +- +- rc = disk_get_info_from_file(filename, &tmp, &file_info); +- free_target_data(&tmp); ++ /* Make sure file is on correct device */ ++ rc = disk_get_info_from_file(filename, target, &file_info); + if (rc) + return -1; + if (file_info->device != bis->info->device) { +@@ -383,11 +377,12 @@ write_segment_table: + static int add_component_file(struct install_set *bis, const char *filename, + address_t load_address, size_t trailer, + void *component, int add_files, +- int comp_id, int menu_idx, int program_table_id) ++ struct job_target_data *target, int comp_id, ++ int menu_idx, int program_table_id) + { + return add_component_file_range(bis, filename, NULL, load_address, + trailer, component, add_files, +- comp_id, menu_idx, ++ target, comp_id, menu_idx, + program_table_id); + } + +@@ -549,7 +544,8 @@ static int add_ipl_program(struct install_set *bis, char *filename, + bool add_envblk, struct job_envblk_data *envblk, + struct job_ipl_data *ipl, disk_blockptr_t *program, + int verbose, int add_files, component_header_type type, +- int is_secure, int menu_idx, int program_table_id) ++ struct job_target_data *target, int is_secure, ++ int menu_idx, int program_table_id) + { + struct signature_header sig_head; + size_t ramdisk_size, image_size; +@@ -671,7 +667,7 @@ static int add_ipl_program(struct install_set *bis, char *filename, + /* Add stage 3 loader to bootmap */ + rc = add_component_file(bis, ZIPL_STAGE3_PATH, STAGE3_LOAD_ADDRESS, + signature_size, VOID_ADD(table, offset), 1, +- COMPONENT_ID_LOADER, menu_idx, ++ target, COMPONENT_ID_LOADER, menu_idx, + program_table_id); + if (rc) { + error_text("Could not add internal loader file '%s'", +@@ -749,7 +745,7 @@ static int add_ipl_program(struct install_set *bis, char *filename, + + rc = add_component_file(bis, ipl->common.image, ipl->common.image_addr, + signature_size, VOID_ADD(table, offset), +- add_files, COMPONENT_ID_KERNEL_IMAGE, ++ add_files, target, COMPONENT_ID_KERNEL_IMAGE, + menu_idx, program_table_id); + if (rc) { + error_text("Could not add image file '%s'", ipl->common.image); +@@ -809,7 +805,7 @@ static int add_ipl_program(struct install_set *bis, char *filename, + ipl->common.ramdisk_addr, + signature_size, + VOID_ADD(table, offset), +- add_files, COMPONENT_ID_RAMDISK, ++ add_files, target, COMPONENT_ID_RAMDISK, + menu_idx, program_table_id); + if (rc) { + error_text("Could not add ramdisk '%s'", +@@ -865,7 +861,7 @@ static int add_ipl_program(struct install_set *bis, char *filename, + rc = add_component_file_range(bis, filename, ®, + ipl->envblk_addr, 0, + VOID_ADD(table, offset), +- 0, ++ 0, target, + COMPONENT_ID_ENVBLK, + menu_idx, + program_table_id); +@@ -897,6 +893,7 @@ static int add_segment_program(struct install_set *bis, + struct job_segment_data *segment, + disk_blockptr_t *program, int verbose, + int add_files, component_header_type type, ++ struct job_target_data *target, + int program_table_id) + { + void *table; +@@ -916,7 +913,7 @@ static int add_segment_program(struct install_set *bis, + printf(" segment file......: %s\n", segment->segment); + + rc = add_component_file(bis, segment->segment, segment->segment_addr, 0, +- VOID_ADD(table, offset), add_files, ++ VOID_ADD(table, offset), add_files, target, + COMPONENT_ID_SEGMENT_FILE, 0 /* menu_idx */, + program_table_id); + if (rc) { +@@ -999,7 +996,7 @@ static int add_dump_program(struct install_set *bis, struct job_data *job, + ipl.common.parmline = dump->common.parmline; + ipl.common.parm_addr = dump->common.parm_addr; + return add_ipl_program(bis, NULL, false, NULL, &ipl, program, +- verbose, 1, type, SECURE_BOOT_DISABLED, ++ verbose, 1, type, target, SECURE_BOOT_DISABLED, + 0 /* menu_idx */, program_table_id); + } + +@@ -1044,7 +1041,8 @@ static int build_program_table(struct job_data *job, + true, &job->envblk, &job->data.ipl, + &table[0], verbose || job->command_line, + job->add_files, component_header, +- job->is_secure, 0, program_table_id); ++ &job->target, job->is_secure, 0, ++ program_table_id); + break; + case job_segment: + if (bis->print_details) { +@@ -1057,7 +1055,7 @@ static int build_program_table(struct job_data *job, + rc = add_segment_program(bis, &job->data.segment, &table[0], + verbose || job->command_line, + job->add_files, COMPONENT_HEADER_IPL, +- program_table_id); ++ &job->target, program_table_id); + break; + case job_dump_partition: + /* Only useful for a partition dump that uses a dump kernel*/ +@@ -1116,7 +1114,7 @@ static int build_program_table(struct job_data *job, + &table[job->data.menu.entry[i].pos], + verbose || job->command_line, + job->add_files, component_header, +- is_secure, i, ++ &job->target, is_secure, i, + program_table_id); + break; + case job_print_usage: +diff --git a/zipl/src/disk.c b/zipl/src/disk.c +index b4bd49a..38a637c 100644 +--- a/zipl/src/disk.c ++++ b/zipl/src/disk.c +@@ -528,9 +528,6 @@ static int disk_set_info_complete(struct job_target_data *td, + * config file, or special "target options" of zipl tool. + * If target parameters were specified by user, then the step 1 above + * is skipped. +- +- * To exclude any user assumptions about the DEVICE, this function +- * should be called with TD pointing to a zeroed structure. + * + * DEVICE: logical, or physical device, optionally formated with a + * file system. +diff --git a/zipl/src/job.c b/zipl/src/job.c +index 2066402..27f0181 100644 +--- a/zipl/src/job.c ++++ b/zipl/src/job.c +@@ -370,7 +370,8 @@ get_command_line(int argc, char* argv[], struct command_line* line) + } + + +-void free_target_data(struct job_target_data *data) ++static void ++free_target_data(struct job_target_data* data) + { + free(data->bootmap_dir); + free(data->targetbase); +-- +2.45.2 + diff --git a/SPECS/s390utils.spec b/SPECS/s390utils.spec index b004d71..aca2152 100644 --- a/SPECS/s390utils.spec +++ b/SPECS/s390utils.spec @@ -14,8 +14,8 @@ Name: s390utils Summary: Utilities and daemons for IBM z Systems -Version: 2.29.0 -Release: 3%{?dist} +Version: 2.33.1 +Release: 2%{?dist} Epoch: 2 License: MIT URL: https://github.com/ibm-s390-linux/s390-tools @@ -117,11 +117,11 @@ be used together with the zSeries (s390) Linux kernel and device drivers. %setup -q -n s390-tools-%{version} # Fedora/RHEL changes -%patch0 -p1 -b .zipl-invert-script-options -%patch1 -p1 -b .blscfg-rpm-nvr-sort +%patch -P 0 -p1 -b .zipl-invert-script-options +%patch -P 1 -p1 -b .blscfg-rpm-nvr-sort # upstream fixes/updates -%patch100 -p1 +%patch -P 100 -p1 # remove --strip from install find . -name Makefile | xargs sed -i 's/$(INSTALL) -s/$(INSTALL)/g' @@ -273,7 +273,7 @@ touch %{buildroot}%{_sysconfdir}/zipl.conf %{_mandir}/man1/pvsecret-create.1* %{_mandir}/man1/pvsecret-list.1* %{_mandir}/man1/pvsecret-lock.1* -%{_mandir}/man1/pvsecret-version.1* +%{_mandir}/man1/pvsecret-verify.1* %{_mandir}/man1/pvsecret.1* %endif %dir %{_datadir}/s390-tools @@ -331,7 +331,10 @@ This package provides minimal set of tools needed to system to boot. /lib/s390-tools/cpictl /lib/s390-tools/stage3.bin /lib/s390-tools/zdev_id +/lib/s390-tools/zdev-from-dasd_mod.dasd /lib/s390-tools/zdev-root-update +/lib/s390-tools/zdev-to-dasd_mod.dasd +/lib/s390-tools/zdev-to-rd.znet /lib/s390-tools/zipl.conf %ghost %config(noreplace) %{_sysconfdir}/zipl.conf %config(noreplace) %{_sysconfdir}/ziplenv @@ -549,6 +552,7 @@ getent group zkeyadm > /dev/null || groupadd -r zkeyadm %{_sbindir}/chccwdev %{_sbindir}/chchp %{_sbindir}/chcpumf +%{_sbindir}/chpstat %{_sbindir}/chshut %{_sbindir}/chzcrypt %{_sbindir}/dasdstat @@ -563,8 +567,9 @@ getent group zkeyadm > /dev/null || groupadd -r zkeyadm %{_sbindir}/lscss %{_sbindir}/lsdasd %{_sbindir}/lshwc -%{_sbindir}/lsqeth %{_sbindir}/lsluns +%{_sbindir}/lsqeth +%{_sbindir}/lspai %{_sbindir}/lsreipl %{_sbindir}/lsscm %{_sbindir}/lsshut @@ -641,7 +646,7 @@ getent group zkeyadm > /dev/null || groupadd -r zkeyadm %{_mandir}/man1/pvsecret-create.1* %{_mandir}/man1/pvsecret-list.1* %{_mandir}/man1/pvsecret-lock.1* -%{_mandir}/man1/pvsecret-version.1* +%{_mandir}/man1/pvsecret-verify.1* %{_mandir}/man1/pvsecret.1* %endif %{_mandir}/man1/zkey.1* @@ -653,6 +658,7 @@ getent group zkeyadm > /dev/null || groupadd -r zkeyadm %{_mandir}/man8/chccwdev.8* %{_mandir}/man8/chchp.8* %{_mandir}/man8/chcpumf.8* +%{_mandir}/man8/chpstat.8* %{_mandir}/man8/chshut.8* %{_mandir}/man8/chzcrypt.8* %{_mandir}/man8/dasdstat.8* @@ -668,6 +674,7 @@ getent group zkeyadm > /dev/null || groupadd -r zkeyadm %{_mandir}/man8/lsdasd.8* %{_mandir}/man8/lshwc.8* %{_mandir}/man8/lsluns.8* +%{_mandir}/man8/lspai.8* %{_mandir}/man8/lsqeth.8* %{_mandir}/man8/lsreipl.8* %{_mandir}/man8/lsscm.8* @@ -1059,6 +1066,17 @@ User-space development files for the s390/s390x architecture. %changelog +* Thu Jul 25 2024 Dan Horák - 2:2.33.1-2 +- avoid usage of the which tool (RHEL-38488) +- Related: RHEL-38488 + +* Fri Jun 14 2024 Dan Horák - 2:2.33.1-1 +- rebased to 2.33.1 (RHEL-23704) +- dbginfo.sh: missing data of new ROCE cards (RHEL-24109) +- SE-tooling: New IBM host-key subject locality (RHEL-30273) +- Fix disk type detection when running under QEMU (RHEL-40358) +- Resolves: RHEL-23704 RHEL-24109 RHEL-30273 RHEL-40358 + * Mon Jan 29 2024 Dan Horák - 2:2.29.0-3 - add s390utils-se-data as a noarch subpackage with Secure Execution data files - Resolves: RHEL-10567