From: "Daniel P. Berrangé" <berrange@redhat.com>
To: Zhuoying Cai <zycai@linux.ibm.com>
Cc: thuth@redhat.com, richard.henderson@linaro.org, david@redhat.com,
pbonzini@redhat.com, walling@linux.ibm.com,
jjherne@linux.ibm.com, jrossi@linux.ibm.com, pasic@linux.ibm.com,
borntraeger@linux.ibm.com, farman@linux.ibm.com,
iii@linux.ibm.com, eblake@redhat.com, armbru@redhat.com,
qemu-s390x@nongnu.org, qemu-devel@nongnu.org
Subject: Re: [PATCH v3 09/28] s390x/diag: Implement DIAG 320 subcode 2
Date: Fri, 6 Jun 2025 11:51:25 +0100 [thread overview]
Message-ID: <aELILYtDSoJ--0AP@redhat.com> (raw)
In-Reply-To: <20250604215657.528142-10-zycai@linux.ibm.com>
On Wed, Jun 04, 2025 at 05:56:37PM -0400, Zhuoying Cai wrote:
> DIAG 320 subcode 2 provides verification-certificates (VCs) that are in the
> certificate store. Only X509 certificates in DER format and SHA-256 hash
> type are recognized.
>
> The subcode value is denoted by setting the second-left-most bit
> of an 8-byte field.
>
> The Verification Certificate Block (VCB) contains the output data
> when the operation completes successfully. It includes a common
> header followed by zero or more Verification Certificate Entries (VCEs),
> depending on the VCB input length and the VC range (from the first VC
> index to the last VC index) in the certificate store.
>
> Each VCE contains information about a certificate retrieved from
> the S390IPLCertificateStore, such as the certificate name, key type,
> key ID length, hash length, and the raw certificate data.
> The key ID and hash are extracted from the raw certificate by the crypto API.
>
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> include/hw/s390x/ipl/diag320.h | 47 ++++++
> target/s390x/diag.c | 262 ++++++++++++++++++++++++++++++++-
> 2 files changed, 308 insertions(+), 1 deletion(-)
>
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index 1f7d0cb2f6..c8518dc5be 100644
> --- a/target/s390x/diag.c
> +++ b/target/s390x/diag.c
> @@ -17,6 +17,7 @@
> #include "s390x-internal.h"
> #include "hw/watchdog/wdt_diag288.h"
> #include "system/cpus.h"
> +#include "hw/s390x/cert-store.h"
> #include "hw/s390x/ipl.h"
> #include "hw/s390x/ipl/diag320.h"
> #include "hw/s390x/s390-virtio-ccw.h"
> @@ -24,6 +25,7 @@
> #include "kvm/kvm_s390x.h"
> #include "target/s390x/kvm/pv.h"
> #include "qemu/error-report.h"
> +#include "crypto/x509-utils.h"
>
>
> int handle_diag_288(CPUS390XState *env, uint64_t r1, uint64_t r3)
> @@ -191,8 +193,260 @@ out:
> }
> }
>
> +static int diag_320_is_cert_valid(S390IPLCertificate qcert, Error **errp)
> +{
> + int version;
> + int rc;
> +
> + version = qcrypto_get_x509_cert_version((uint8_t *)qcert.raw, qcert.size, errp);
Why not change 'qcert.raw' to be 'uint8_t *' to begin with if
all the APIs it is used with expect that ?
> + if (version < 0) {
> + return version == -ENOTSUP ? -ENOTSUP : -1;
> + }
This method shouldn't be returning errnos, only '-1'
> +
> + rc = qcrypto_check_x509_cert_times((uint8_t *)qcert.raw, qcert.size, errp);
> + if (rc) {
> + return -1;
> + }
> +
> + return 0;
> +}
> +
> +static int diag_320_get_cert_info(VCEntry *vce, S390IPLCertificate qcert, int *is_valid,
> + unsigned char **key_id_data, void **hash_data)
> +{
> + int algo;
> + int rc;
> + Error *err = NULL;
> +
> + /* VCE flag (validity) */
> + *is_valid = diag_320_is_cert_valid(qcert, &err);
> + /* return early if GNUTLS is not enabled */
> + if (*is_valid == -ENOTSUP) {
> + error_report("GNUTLS is not supported");
> + return -1;
> + }
> + /* reset err for the next caller to avoid assert failure */
> + err = NULL;
This leaks memory. There is also no reason why this should
treat missing GNUTLS as different from any other errors.
All fatal errors must be turned into failures of this
method.
> +
> + /* key-type */
> + algo = qcrypto_get_x509_pk_algorithm((uint8_t *)qcert.raw, qcert.size, &err);
> + if (algo == QCRYPTO_PK_ALGO_RSA) {
> + vce->key_type = DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
> + }
> + err = NULL;
Again leaking memory and failing at error propagation.
> +
> + /* VC format */
> + if (qcert.format == QCRYPTO_CERT_FMT_DER) {
> + vce->format = DIAG_320_VCE_FORMAT_X509_DER;
> + }
> +
> + /* key id and key id len */
> + *key_id_data = g_malloc0(qcert.key_id_size);
This can be removed if qcrypto_get_x509_cert_key_id is changed
to allocate the correct size itself.
> + rc = qcrypto_get_x509_cert_key_id((uint8_t *)qcert.raw, qcert.size,
> + QCRYPTO_KEYID_FLAGS_SHA256,
> + *key_id_data, &qcert.key_id_size, &err);
> + if (rc < 0) {
> + error_report_err(err);
> + error_report("Fail to retrieve certificate key ID");
The second error_report() is redundant
> + goto out;
> + }
> + vce->keyid_len = cpu_to_be16(qcert.key_id_size);
> +
> + /* hash type */
> + if (qcert.hash_type == QCRYPTO_SIG_ALGO_RSA_SHA256) {
> + vce->hash_type = DIAG_320_VCE_HASHTYPE_SHA2_256;
> + }
> +
> + /* hash and hash len */
> + *hash_data = g_malloc0(qcert.hash_size);
> + rc = qcrypto_get_x509_cert_fingerprint((uint8_t *)qcert.raw, qcert.size,
> + QCRYPTO_HASH_ALGO_SHA256,
> + *hash_data, &qcert.hash_size, &err);
> + if (rc < 0) {
> + error_report_err(err);
> + error_report("Fail to retrieve certificate hash");
Also redundant.
> + goto out;
> + }
> + vce->hash_len = cpu_to_be16(qcert.hash_size);
> +
> + return 0;
> +
> +out:
> + g_free(*key_id_data);
> + g_free(*hash_data);
> +
> + *key_id_data = NULL;
> + *hash_data = NULL;
g_clear_pointer(key_id_data, g_free) does free & clear in one operation
> +
> + return -1;
> +}
> +
> +static VCEntry *build_vce(S390IPLCertificate qcert, uint32_t vce_len, int idx,
> + size_t keyid_buf_size, size_t hash_buf_size)
> +{
> + VCEntry *vce = NULL;
g_autofree
> + unsigned char *key_id_data = NULL;
g_autofree
> + void *hash_data = NULL;
g_autofree
> + int is_valid = -1;
> + int rc;
> +
> + /*
> + * Construct VCE
> + * Unused area following the VCE field contains zeros.
> + */
> + vce = g_malloc0(vce_len);
> +
> + rc = diag_320_get_cert_info(vce, qcert, &is_valid, &key_id_data, &hash_data);
> + if (rc) {
> + g_free(vce);
Redundant if using g_autofree
> + return NULL;
> + }
> +
> + vce->len = cpu_to_be32(vce_len);
> + vce->cert_idx = cpu_to_be16(idx + 1);
> + vce->cert_len = cpu_to_be32(qcert.size);
> +
> + strncpy((char *)vce->name, (char *)qcert.vc_name, VC_NAME_LEN_BYTES);
> +
> + /* VCE field offset is also word aligned */
> + vce->hash_offset = cpu_to_be16(VCE_HEADER_LEN + keyid_buf_size);
> + vce->cert_offset = cpu_to_be16(VCE_HEADER_LEN + keyid_buf_size + hash_buf_size);
> +
> + /* Write Key ID */
> + memcpy(vce->cert_buf, key_id_data, qcert.key_id_size);
> + /* Write Hash key */
> + memcpy(vce->cert_buf + keyid_buf_size, hash_data, qcert.hash_size);
> + /* Write VCE cert data */
> + memcpy(vce->cert_buf + keyid_buf_size + hash_buf_size, qcert.raw, qcert.size);
> +
> + /* The certificate is valid and VCE contains the certificate */
> + if (is_valid == 0) {
> + vce->flags |= DIAG_320_VCE_FLAGS_VALID;
> + }
> +
> + g_free(key_id_data);
> + g_free(hash_data);
> +
> + key_id_data = NULL;
> + hash_data = NULL;
Redundant with g_autofree
> +
> + return vce;
Use g_steal_pointer(&vce) with g_autofree.
> +}
> +
> +static int handle_diag320_store_vc(S390CPU *cpu, uint64_t addr, uint64_t r1, uintptr_t ra,
> + S390IPLCertificateStore *qcs)
> +{
> + VCBlock *vcb;
g_autofree VCBlock *vcb = NULL;
> + size_t vce_offset;
> + size_t remaining_space;
> + size_t keyid_buf_size;
> + size_t hash_buf_size;
> + size_t cert_buf_size;
> + uint32_t vce_len;
> + uint16_t first_vc_index;
> + uint16_t last_vc_index;
> + uint32_t in_len;
> +
> + vcb = g_new0(VCBlock, 1);
> + if (s390_cpu_virt_mem_read(cpu, addr, r1, vcb, sizeof(*vcb))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
Fails to free 'vcb', hence the best practice use of g_autofree
> + return -1;
> + }
> +
> + in_len = be32_to_cpu(vcb->in_len);
> + first_vc_index = be16_to_cpu(vcb->first_vc_index);
> + last_vc_index = be16_to_cpu(vcb->last_vc_index);
> +
> + if (in_len % TARGET_PAGE_SIZE != 0) {
> + g_free(vcb);
> + return DIAG_320_RC_INVAL_VCB_LEN;
> + }
> +
> + if (first_vc_index > last_vc_index) {
> + g_free(vcb);
> + return DIAG_320_RC_BAD_RANGE;
> + }
> +
> + if (first_vc_index == 0) {
> + /*
> + * Zero is a valid index for the first and last VC index.
> + * Zero index results in the VCB header and zero certificates returned.
> + */
> + if (last_vc_index == 0) {
> + goto out;
> + }
> +
> + /* DIAG320 certificate store remains a one origin for cert entries */
> + vcb->first_vc_index = 1;
> + }
> +
> + vce_offset = VCB_HEADER_LEN;
> + vcb->out_len = VCB_HEADER_LEN;
> + remaining_space = in_len - VCB_HEADER_LEN;
> +
> + for (int i = first_vc_index - 1; i < last_vc_index && i < qcs->count; i++) {
> + VCEntry *vce;
> + S390IPLCertificate qcert = qcs->certs[i];
> + /*
> + * Each VCE is word aligned.
> + * Each variable length field within the VCE is also word aligned.
> + */
> + keyid_buf_size = ROUND_UP(qcert.key_id_size, 4);
> + hash_buf_size = ROUND_UP(qcert.hash_size, 4);
> + cert_buf_size = ROUND_UP(qcert.size, 4);
> + vce_len = VCE_HEADER_LEN + cert_buf_size + keyid_buf_size + hash_buf_size;
> +
> + /*
> + * If there is no more space to store the cert,
> + * set the remaining verification cert count and
> + * break early.
> + */
> + if (remaining_space < vce_len) {
> + vcb->remain_ct = cpu_to_be16(last_vc_index - i);
> + break;
> + }
> +
> + vce = build_vce(qcert, vce_len, i, keyid_buf_size, hash_buf_size);
> + if (vce == NULL) {
> + continue;
> + }
> +
> + /* Write VCE */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, r1, vce, vce_len)) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return -1;
> + }
> +
> + vce_offset += vce_len;
> + vcb->out_len += vce_len;
> + remaining_space -= vce_len;
> + vcb->stored_ct++;
> +
> + g_free(vce);
> + }
> +
> + vcb->out_len = cpu_to_be32(vcb->out_len);
> + vcb->stored_ct = cpu_to_be16(vcb->stored_ct);
> +
> +out:
> + /*
> + * Write VCB header
> + * All VCEs have been populated with the latest information
> + * and write VCB header last.
> + */
> + if (s390_cpu_virt_mem_write(cpu, addr, r1, vcb, VCB_HEADER_LEN)) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
Fails to free 'vcb'
> + return -1;
> + }
> +
> + g_free(vcb);
> + return DIAG_320_RC_OK;
> +}
> +
With regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
next prev parent reply other threads:[~2025-06-06 10:52 UTC|newest]
Thread overview: 51+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-04 21:56 [PATCH v3 00/28] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 01/28] Add boot-certificates to s390-ccw-virtio machine type option Zhuoying Cai
2025-06-06 14:00 ` Daniel P. Berrangé
2025-06-20 15:45 ` Zhuoying Cai
2025-06-24 15:03 ` Jared Rossi
2025-06-30 19:31 ` Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 02/28] crypto/x509-utils: Add helper functions for certificate store Zhuoying Cai
2025-06-06 10:03 ` Daniel P. Berrangé
2025-06-06 10:14 ` Daniel P. Berrangé
2025-06-17 10:58 ` Markus Armbruster
2025-06-17 14:57 ` Zhuoying Cai
2025-06-18 5:57 ` Markus Armbruster
2025-06-18 15:34 ` Zhuoying Cai
2025-06-23 6:15 ` Markus Armbruster
2025-06-23 8:00 ` Daniel P. Berrangé
2025-06-23 9:22 ` Markus Armbruster
2025-06-04 21:56 ` [PATCH v3 03/28] hw/s390x/ipl: Create " Zhuoying Cai
2025-06-06 10:31 ` Daniel P. Berrangé
2025-06-30 19:56 ` Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 04/28] s390x: Guest support for Certificate Store Facility (CS) Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 05/28] s390x/diag: Introduce DIAG 320 for certificate store facility Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 06/28] s390x/diag: Refactor address validation check from diag308_parm_check Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 07/28] s390x/diag: Implement DIAG 320 subcode 1 Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 08/28] crypto/x509-utils: Add helper functions for DIAG 320 subcode 2 Zhuoying Cai
2025-06-06 10:40 ` Daniel P. Berrangé
2025-06-06 10:43 ` Daniel P. Berrangé
2025-06-04 21:56 ` [PATCH v3 09/28] s390x/diag: Implement " Zhuoying Cai
2025-06-06 10:51 ` Daniel P. Berrangé [this message]
2025-06-04 21:56 ` [PATCH v3 10/28] s390x/diag: Introduce DIAG 508 for secure IPL operations Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 11/28] crypto: Add helper functions for DIAG 508 subcode 1 Zhuoying Cai
2025-06-06 10:56 ` Daniel P. Berrangé
2025-06-04 21:56 ` [PATCH v3 12/28] s390x/diag: Implement DIAG 508 subcode 1 for signature verification Zhuoying Cai
2025-06-06 10:58 ` Daniel P. Berrangé
2025-06-04 21:56 ` [PATCH v3 13/28] pc-bios/s390-ccw: Introduce IPL Information Report Block (IIRB) Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 14/28] pc-bios/s390-ccw: Define memory for IPLB and convert IPLB to pointers Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 15/28] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 16/28] hw/s390x/ipl: Set iplb->len to maximum length of " Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 17/28] s390x: Guest support for Secure-IPL Facility Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 18/28] pc-bios/s390-ccw: Refactor zipl_run() Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 19/28] pc-bios/s390-ccw: Refactor zipl_load_segment function Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 20/28] pc-bios/s390-ccw: Add signature verification for secure IPL in audit mode Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 21/28] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF) Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 22/28] pc-bios/s390-ccw: Add additional security checks for secure boot Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 23/28] Add secure-boot to s390-ccw-virtio machine type option Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 24/28] hw/s390x/ipl: Set IPIB flags for secure IPL Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 25/28] pc-bios/s390-ccw: Handle true secure IPL mode Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 26/28] pc-bios/s390-ccw: Handle secure boot with multiple boot devices Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 27/28] hw/s390x/ipl: Handle secure boot without specifying a boot device Zhuoying Cai
2025-06-04 21:56 ` [PATCH v3 28/28] docs: Add secure IPL documentation Zhuoying Cai
2025-06-06 11:04 ` Daniel P. Berrangé
2025-06-17 21:29 ` Collin Walling
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=aELILYtDSoJ--0AP@redhat.com \
--to=berrange@redhat.com \
--cc=armbru@redhat.com \
--cc=borntraeger@linux.ibm.com \
--cc=david@redhat.com \
--cc=eblake@redhat.com \
--cc=farman@linux.ibm.com \
--cc=iii@linux.ibm.com \
--cc=jjherne@linux.ibm.com \
--cc=jrossi@linux.ibm.com \
--cc=pasic@linux.ibm.com \
--cc=pbonzini@redhat.com \
--cc=qemu-devel@nongnu.org \
--cc=qemu-s390x@nongnu.org \
--cc=richard.henderson@linaro.org \
--cc=thuth@redhat.com \
--cc=walling@linux.ibm.com \
--cc=zycai@linux.ibm.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).