From: Collin Walling <walling@linux.ibm.com>
To: Zhuoying Cai <zycai@linux.ibm.com>,
thuth@redhat.com, richard.henderson@linaro.org, david@redhat.com,
pbonzini@redhat.com
Cc: jjherne@linux.ibm.com, jrossi@linux.ibm.com,
fiuczy@linux.ibm.com, pasic@linux.ibm.com,
borntraeger@linux.ibm.com, farman@linux.ibm.com,
iii@linux.ibm.com, qemu-s390x@nongnu.org, qemu-devel@nongnu.org
Subject: Re: [PATCH v1 07/24] s390x/diag: Implement DIAG 320 subcode 2
Date: Wed, 16 Apr 2025 17:32:24 -0400 [thread overview]
Message-ID: <6af75e03-86f2-4742-80d9-05a2887f6039@linux.ibm.com> (raw)
In-Reply-To: <20250408155527.123341-8-zycai@linux.ibm.com>
On 4/8/25 11:55 AM, Zhuoying Cai wrote:
> DIAG 320 subcode 2 provides certificates that are in the
> certificate store.
>
> 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. VCB includes a common
> header followed by zero or more verification-certificate entries (VCEs).
>
Please add a comment stating that only x509 certificates in DER format
and SHA-256 hash type are recognized.
Further, I would further explain what this 320 SC 2 implementation is
doing either in this commit message or where appropriate as comments:
- DIAG 320 uses a one-origin index for cert entries
- Describe the data structures in some more detail (mainly the more
obscure fields)
- Describe why the VCB header is written to userspace last
- Describe which fields are taken from the certificate and stored in
the data structures
- State that these certificates are retrieved from your cert store impl
> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>
> ---
> include/hw/s390x/ipl/diag320.h | 59 +++++++++
> target/s390x/diag.c | 227 ++++++++++++++++++++++++++++++++-
> 2 files changed, 285 insertions(+), 1 deletion(-)
>
> diff --git a/include/hw/s390x/ipl/diag320.h b/include/hw/s390x/ipl/diag320.h
> index ded336df25..32b6914b3b 100644
> --- a/include/hw/s390x/ipl/diag320.h
> +++ b/include/hw/s390x/ipl/diag320.h
> @@ -14,15 +14,24 @@
>
> #define DIAG_320_SUBC_QUERY_ISM 0
> #define DIAG_320_SUBC_QUERY_VCSI 1
> +#define DIAG_320_SUBC_STORE_VC 2
>
> #define DIAG_320_RC_OK 0x0001
> #define DIAG_320_RC_NOMEM 0x0202
> +#define DIAG_320_RC_INVAL_VCB_LEN 0x0204
> +#define DIAG_320_RC_BAD_RANGE 0x0302
>
> #define VCSSB_MAX_LEN 128
> #define VCE_HEADER_LEN 128
> #define VCB_HEADER_LEN 64
>
> #define DIAG_320_ISM_QUERY_VCSI 0x4000000000000000
> +#define DIAG_320_ISM_STORE_VC 0x2000000000000000
> +
> +#define DIAG_320_VCE_FLAGS_VALID 0x80
> +#define DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING 0
> +#define DIAG_320_VCE_FORMAT_X509_DER 1
> +#define DIAG_320_VCE_HASHTYPE_SHA2_256 1
>
> struct VerificationCertificateStorageSizeBlock {
> uint32_t length;
> @@ -41,4 +50,54 @@ struct VerificationCertificateStorageSizeBlock {
> typedef struct VerificationCertificateStorageSizeBlock \
> VerificationCertificateStorageSizeBlock;
>
> +struct vcb_header {
> + uint32_t vcbinlen;
> + uint32_t reserved0;
> + uint16_t fvci;> + uint16_t lvci;
> + uint32_t reserved1;
> + uint32_t cstoken;
> + uint32_t reserved2[3];
> + uint32_t vcboutlen;
> + uint8_t reserved3[3];
> + uint8_t version;
> + uint16_t svcc;
> + uint16_t rvcc;
> + uint32_t reserved4[5];
> +} QEMU_PACKED;
> +typedef struct vcb_header vcb_header;
s/vcbinlen/in_len
s/vcboutlen/out_len
s/fvci/first_vc_index
s/lvci/last_vc_index
s/cstoken/cs_token, which I guess we don't make use of at all... does it
make sense to just remove it and combine the reserved fields?
s/svcc/stored_ct
s/rvcc/remain_ct
This will make things much easier to understand.
> +
> +struct VerficationCertificateBlock {
> + vcb_header vcb_hdr;
> + uint8_t vcb_buf[];
s/vcb_buf/vce_buf
What is the justification of having a separate header struct? Can't we
just put everything in the VerificationCertificateBlock? It would make
some of the accessing in the functions below a LOT cleaner.
> +} QEMU_PACKED;
> +typedef struct VerficationCertificateBlock VerficationCertificateBlock;
> +
> +struct vce_header {
> + uint32_t len;
> + uint8_t flags;
> + uint8_t keytype;
> + uint16_t certidx;
> + uint32_t name[16];
> + uint8_t format;
> + uint8_t reserved0;
> + uint16_t keyidlen;
> + uint8_t reserved1;
> + uint8_t hashtype;
> + uint16_t hashlen;
> + uint32_t reserved2;
> + uint32_t certlen;
> + uint32_t reserved3[2];
> + uint16_t hashoffset;
> + uint16_t certoffset;
> + uint32_t reserved4[7];
Use underscores to separate words in these fields to make them easier on
the eyes.
> +} QEMU_PACKED;
> +typedef struct vce_header vce_header;
> +
> +struct VerificationCertificateEntry {
> + vce_header vce_hdr;
> + uint8_t cert_data_buf[];
s/cert_data_buf/cert_buf
and same comments as above w.r.t. to putting all header fields in here
> +} QEMU_PACKED;
> +typedef struct VerificationCertificateEntry VerificationCertificateEntry;
> +
> #endif
> diff --git a/target/s390x/diag.c b/target/s390x/diag.c
> index cc639819ec..82e4dc9e1e 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/s390-virtio-ccw.h"
> #include "system/kvm.h"
> @@ -191,6 +192,94 @@ out:
> }
> }
>
> +#ifdef CONFIG_GNUTLS
> +static bool diag_320_is_cert_valid(gnutls_x509_crt_t cert)
> +{
> + time_t now;
> +
> + if (gnutls_x509_crt_get_version(cert) < 0) {
> + return false;
> + }
> +
> + now = time(0);
> + if (!((gnutls_x509_crt_get_activation_time(cert) < now) &&
> + (gnutls_x509_crt_get_expiration_time(cert) > now))) {
> + return false;
> + }
> +
> + return true;
> +}
qcrypto_tls_creds_check_cert_times may suffice for now.
> +#endif /* CONFIG_GNUTLS */
> +
> +static int diag_320_get_cert_info(VerificationCertificateEntry *vce,
> + S390IPLCertificate qcert, bool *is_valid,
> + unsigned char **key_id_data, void **hash_data)
> +{
> +#ifdef CONFIG_GNUTLS
> + unsigned int algo;
> + unsigned int bits;
> + int hash_type;
> + int rc;
> +
> + gnutls_x509_crt_t g_cert = NULL;
> + if (g_init_cert((uint8_t *)qcert.raw, qcert.size, &g_cert)) {
> + return -1;
> + }
> +
> + /* VCE flag (validity) */
> + *is_valid = diag_320_is_cert_valid(g_cert);
> +
> + /* key-type */
> + algo = gnutls_x509_crt_get_pk_algorithm(g_cert, &bits);
> + if (algo == GNUTLS_PK_RSA) {
> + vce->vce_hdr.keytype = DIAG_320_VCE_KEYTYPE_SELF_DESCRIBING;
> + }
> +
> + /* VC format */
> + if (qcert.format == GNUTLS_X509_FMT_DER) {
> + vce->vce_hdr.format = DIAG_320_VCE_FORMAT_X509_DER;
> + }
> +
> + /* key id and key id len */
> + *key_id_data = g_malloc0(qcert.key_id_size);
> + rc = gnutls_x509_crt_get_key_id(g_cert, GNUTLS_KEYID_USE_SHA256,
> + *key_id_data, &qcert.key_id_size);
> + if (rc < 0) {
> + error_report("Fail to retrieve certificate key ID");
> + goto out;
> + }
> + vce->vce_hdr.keyidlen = (uint16_t)qcert.key_id_size;
> +
> + /* hash type */
> + hash_type = gnutls_x509_crt_get_signature_algorithm(g_cert);
> + if (hash_type == GNUTLS_SIGN_RSA_SHA256) {
> + vce->vce_hdr.hashtype = DIAG_320_VCE_HASHTYPE_SHA2_256;
> + }
> +
> + /* hash and hash len */
> + *hash_data = g_malloc0(qcert.hash_size);
> + rc = gnutls_x509_crt_get_fingerprint(g_cert, GNUTLS_DIG_SHA256,
> + *hash_data, &qcert.hash_size);
> + if (rc < 0) {
> + error_report("Fail to retrieve certificate hash");
> + goto out;
> + }
> + vce->vce_hdr.hashlen = (uint16_t)qcert.hash_size;
> +
> + gnutls_x509_crt_deinit(g_cert);
> +
> + return 0;
> +out:
> + gnutls_x509_crt_deinit(g_cert);
> + g_free(*key_id_data);
> + g_free(*hash_data);
> +
> + return -1;
> +#else
> + return -1;
> +#endif /* CONFIG_GNUTLS */
As per Daniel's suggestion, let's move all gnutls functions to somewhere
in the crypto/* directory. There seems to be a file aptly titled
"x509-utils.c" that may be a good starting point to contribute to.
A lot of this function should get offloaded to new/existing qcrypto
functions, so I'll reserve my comments for now.
> +}
> +
> void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> {
> S390CPU *cpu = env_archcpu(env);
> @@ -211,7 +300,7 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
>
> switch (subcode) {
> case DIAG_320_SUBC_QUERY_ISM:
> - uint64_t ism = DIAG_320_ISM_QUERY_VCSI;
> + uint64_t ism = DIAG_320_ISM_QUERY_VCSI | DIAG_320_ISM_STORE_VC;
>
> if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, &ism,
> be64_to_cpu(sizeof(ism)))) {
> @@ -257,6 +346,142 @@ void handle_diag_320(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
> }
> rc = DIAG_320_RC_OK;
> break;
> + case DIAG_320_SUBC_STORE_VC:
> + VerficationCertificateBlock *vcb;
> + size_t vce_offset = VCB_HEADER_LEN;
> + size_t remaining_space;
> + size_t vce_hdr_offset;
> + int i;
> +
> + unsigned char *key_id_data = NULL;
> + void *hash_data = NULL;
> + bool is_valid = false;
> +
> + vcb = g_new0(VerficationCertificateBlock, 1);
> + if (s390_cpu_virt_mem_read(cpu, addr, (uint8_t)r1, vcb, sizeof(*vcb))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> +
> + if (vcb->vcb_hdr.vcbinlen % 4096 != 0) {
I think you want to use TARGET_PAGE_MASK here instead of the explicit 4096.
> + rc = DIAG_320_RC_INVAL_VCB_LEN;
> + g_free(vcb);
> + break;
> + }
> +
> + if (1 > vcb->vcb_hdr.fvci || vcb->vcb_hdr.fvci > vcb->vcb_hdr.lvci) {
This is no longer entirely correct for vc index 0. If first and last
indexes are both 0, no certificates are returned and rc should be OK.
We may not use this feature in this patch series, but we need to be
compliant.
Additionally, it's valid to have first == 0 and last > first. In this
case, index 0 is skipped since the DIAG 320 cert store uses a one-origin
index.
> + rc = DIAG_320_RC_BAD_RANGE;
> + g_free(vcb);
> + break;
> + }
> +
> + vcb->vcb_hdr.vcboutlen = VCB_HEADER_LEN;
> + vcb->vcb_hdr.version = 0;
> + vcb->vcb_hdr.svcc = 0;
> + vcb->vcb_hdr.rvcc = 0;
> +
g_new0 initalizes all fields to 0, so no need to do that here.
> + remaining_space = vcb->vcb_hdr.vcbinlen - VCB_HEADER_LEN;
> +
> + for (i = vcb->vcb_hdr.fvci - 1; i < vcb->vcb_hdr.lvci; i++) {
> + VerificationCertificateEntry vce;> +
S390IPLCertificate qcert;
> +
> + /*
> + * If cert index goes beyond the highest cert
> + * store index (count - 1), then exit early
> + */
> + if (i >= qcs->count) {
> + break;
> + }
May make sense to just toss this in with the loop condition and then
initialize qcert = qcs->certs[i]; above.
> +
> + qcert = qcs->certs[i];
> +
> + /*
> + * If there is no more space to store the cert,
> + * set the remaining verification cert count and
> + * break early.
> + */
> + if (remaining_space < qcert.size) {
> + vcb->vcb_hdr.rvcc = vcb->vcb_hdr.lvci - i;
> + break;
> + }
> +
> + /* Construct VCE */
> + vce.vce_hdr.len = VCE_HEADER_LEN;
> + vce.vce_hdr.certidx = i + 1;
> + vce.vce_hdr.certlen = qcert.size;
> +
> + strncpy((char *)vce.vce_hdr.name, (char *)qcert.vc_name, VC_NAME_LEN_BYTES);
> +
> + rc = diag_320_get_cert_info(&vce, qcert, &is_valid, &key_id_data, &hash_data);
> + if (rc) {
> + continue;
> + }
> +
> + vce.vce_hdr.len += vce.vce_hdr.keyidlen;
> + vce.vce_hdr.len += vce.vce_hdr.hashlen;
> + vce.vce_hdr.len += vce.vce_hdr.certlen;
> +
> + vce.vce_hdr.hashoffset = VCE_HEADER_LEN + vce.vce_hdr.keyidlen;
> + vce.vce_hdr.certoffset = VCE_HEADER_LEN + vce.vce_hdr.keyidlen +
> + vce.vce_hdr.hashlen;
> +
> + vce_hdr_offset = vce_offset;
> + vce_offset += VCE_HEADER_LEN;
> +
> + /* Write Key ID */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, key_id_data,
> + be16_to_cpu(vce.vce_hdr.keyidlen))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> + vce_offset += vce.vce_hdr.keyidlen;
> +
> + /* Write Hash key */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, hash_data,
> + be16_to_cpu(vce.vce_hdr.hashlen))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> + vce_offset += vce.vce_hdr.hashlen;
Leading space.
> +
> + /* Write VCE cert data */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_offset, (uint8_t)r1, qcert.raw,
> + be32_to_cpu(vce.vce_hdr.certlen))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> + vce_offset += qcert.size;
> +
> + /* The certificate is valid and VCE contains the certificate */
> + if (is_valid) {
> + vce.vce_hdr.flags |= DIAG_320_VCE_FLAGS_VALID;
> + }
> +
> + /* Write VCE Header */
> + if (s390_cpu_virt_mem_write(cpu, addr + vce_hdr_offset, (uint8_t)r1, &vce,
> + be32_to_cpu(VCE_HEADER_LEN))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }
> +
> + vcb->vcb_hdr.vcboutlen += vce.vce_hdr.len;
> + remaining_space -= vce.vce_hdr.len;
> + vcb->vcb_hdr.svcc++;
> +
> + g_free(key_id_data);
> + g_free(hash_data);
> + }
> +
> + /* Finally, write the header */
/* Write VCB Header */
> + if (s390_cpu_virt_mem_write(cpu, addr, (uint8_t)r1, vcb,
> + be32_to_cpu(VCB_HEADER_LEN))) {
> + s390_cpu_virt_mem_handle_exc(cpu, ra);
> + return;
> + }> + rc = DIAG_320_RC_OK;
> + g_free(vcb);
> + break;
> default:
> s390_program_interrupt(env, PGM_SPECIFICATION, ra);
> return;
--
Regards,
Collin
next prev parent reply other threads:[~2025-04-16 21:33 UTC|newest]
Thread overview: 54+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-04-08 15:55 [PATCH v1 00/24] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 01/24] Add -boot-certificates /path/dir:/path/file option in QEMU command line Zhuoying Cai
2025-04-11 10:44 ` Thomas Huth
2025-04-11 12:57 ` Daniel P. Berrangé
2025-04-11 13:33 ` Daniel P. Berrangé
2025-04-11 17:45 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 02/24] hw/s390x/ipl: Create certificate store Zhuoying Cai
2025-04-11 12:44 ` Thomas Huth
2025-04-11 13:02 ` Daniel P. Berrangé
2025-04-08 15:55 ` [PATCH v1 03/24] s390x: Guest support for Certificate Store Facility (CS) Zhuoying Cai
2025-04-11 13:28 ` Thomas Huth
2025-04-14 17:53 ` Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 04/24] s390x/diag: Introduce DIAG 320 for certificate store facility Zhuoying Cai
2025-04-11 13:43 ` Thomas Huth
2025-04-11 18:37 ` Collin Walling
2025-04-08 15:55 ` [PATCH v1 05/24] s390x/diag: Refactor address validation check from diag308_parm_check Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 06/24] s390x/diag: Implement DIAG 320 subcode 1 Zhuoying Cai
2025-04-11 13:57 ` Thomas Huth
2025-04-17 19:57 ` Collin Walling
2025-04-11 17:40 ` Farhan Ali
2025-04-08 15:55 ` [PATCH v1 07/24] s390x/diag: Implement DIAG 320 subcode 2 Zhuoying Cai
2025-04-16 21:32 ` Collin Walling [this message]
2025-04-08 15:55 ` [PATCH v1 08/24] s390x/diag: Introduce DIAG 508 for secure IPL operations Zhuoying Cai
2025-04-11 14:22 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 09/24] s390x/diag: Implement DIAG 508 subcode 2 for signature verification Zhuoying Cai
2025-04-11 14:38 ` Thomas Huth
2025-04-11 17:30 ` Collin Walling
2025-04-08 15:55 ` [PATCH v1 10/24] pc-bios/s390-ccw: Introduce IPL Information Report Block (IIRB) Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 11/24] pc-bios/s390-ccw: Define memory for IPLB and convert IPLB to pointers Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 12/24] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block Zhuoying Cai
2025-04-11 19:13 ` Farhan Ali
2025-04-08 15:55 ` [PATCH v1 13/24] hw/s390x/ipl: Set iplb->len to maximum length of " Zhuoying Cai
2025-04-11 14:46 ` Thomas Huth
2025-04-11 15:39 ` Jared Rossi
2025-04-08 15:55 ` [PATCH v1 14/24] s390x: Guest support for Secure-IPL Facility Zhuoying Cai
2025-04-17 4:58 ` Thomas Huth
2025-04-17 18:54 ` Collin Walling
2025-04-08 15:55 ` [PATCH v1 15/24] pc-bios/s390-ccw: Refactor zipl_run() Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 16/24] pc-bios/s390-ccw: Refactor zipl_load_segment function Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 17/24] pc-bios/s390-ccw: Add signature verification for secure boot in audit mode Zhuoying Cai
2025-04-13 23:57 ` Jared Rossi
2025-04-17 22:39 ` Collin Walling
2025-04-08 15:55 ` [PATCH v1 18/24] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF) Zhuoying Cai
2025-04-17 4:57 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 19/24] pc-bios/s390-ccw: Add additional security checks for secure boot Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 20/24] Add -secure-boot on|off option in QEMU command line Zhuoying Cai
2025-04-11 14:50 ` Thomas Huth
2025-04-08 15:55 ` [PATCH v1 21/24] hw/s390x/ipl: Set IPIB flags for secure IPL Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 22/24] pc-bios/s390-ccw: Handle true secure IPL mode Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 23/24] pc-bios/s390-ccw: Handle secure boot with multiple boot devices Zhuoying Cai
2025-04-08 15:55 ` [PATCH v1 24/24] hw/s390x/ipl: Handle secure boot without specifying a boot device Zhuoying Cai
2025-04-16 22:11 ` Collin Walling
2025-04-17 13:53 ` Jared Rossi
2025-04-17 14:13 ` Zhuoying Cai
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=6af75e03-86f2-4742-80d9-05a2887f6039@linux.ibm.com \
--to=walling@linux.ibm.com \
--cc=borntraeger@linux.ibm.com \
--cc=david@redhat.com \
--cc=farman@linux.ibm.com \
--cc=fiuczy@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=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).