qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Zhuoying Cai <zycai@linux.ibm.com>
To: Collin Walling <walling@linux.ibm.com>,
	thuth@redhat.com, berrange@redhat.com,
	richard.henderson@linaro.org, david@redhat.com,
	pbonzini@redhat.com, jrossi@linux.ibm.com, qemu-s390x@nongnu.org,
	qemu-devel@nongnu.org
Cc: jjherne@linux.ibm.com, pasic@linux.ibm.com,
	borntraeger@linux.ibm.com, farman@linux.ibm.com,
	mjrosato@linux.ibm.com, iii@linux.ibm.com
Subject: Re: [PATCH v4 20/28] pc-bios/s390-ccw: Add signature verification for secure IPL in audit mode
Date: Mon, 21 Jul 2025 17:50:14 -0400	[thread overview]
Message-ID: <86deb470-54a6-460f-bf25-239d2256ea28@linux.ibm.com> (raw)
In-Reply-To: <eccbac31-7c1d-4b75-a284-a46ad98675db@linux.ibm.com>

On 7/11/25 6:50 PM, Collin Walling wrote:
> On 7/11/25 5:10 PM, Zhuoying Cai wrote:
>> Enable secure IPL in audit mode, which performs signature verification,
>> but any error does not terminate the boot process. Only warnings will be
>> logged to the console instead.
>>
>> Add a comp_len variable to store the length of a segment in
>> zipl_load_segment. comp_len variable is necessary to store the
>> calculated segment length and is used during signature verification.
>> Return the length on success, or a negative return code on failure.
>>
>> Secure IPL in audit mode requires at least one certificate provided in
>> the key store along with necessary facilities (Secure IPL Facility,
>> Certificate Store Facility and secure IPL extension support).
>>
>> Note: Secure IPL in audit mode is implemented for the SCSI scheme of
>> virtio-blk/virtio-scsi devices.
>>
>> Signed-off-by: Zhuoying Cai <zycai@linux.ibm.com>

[snip...]

>> +static int zipl_run_secure(ComponentEntry *entry, uint8_t *tmp_sec)
>> +{
>> +    IplDeviceComponentList comps;
>> +    IplSignatureCertificateList certs;
>> +    uint64_t *cert = NULL;
>> +    int cert_index = 0;
>> +    int comp_index = 0;
>> +    uint64_t comp_addr;
>> +    int comp_len;
>> +    bool have_sig;
>> +    uint32_t sig_len;
>> +    uint64_t cert_len = -1;
>> +    uint8_t cert_idx = -1;
>> +    bool verified;
>> +    uint32_t certs_len;
>> +    /*
>> +     * Store indices of cert entry that have already used for signature verification
>> +     * to prevent allocating the same certificate multiple times.
>> +     * cert_table index: index of certificate from qemu cert store used for verification
>> +     * cert_table value: index of cert entry in cert list that contains the certificate
>> +     */
>> +    int cert_table[MAX_CERTIFICATES] = { [0 ... MAX_CERTIFICATES - 1] = -1};
>> +    int signed_count = 0;
>> +
>> +    if (!zipl_secure_ipl_supported()) {
>> +        return -1;
>> +    }
>> +
>> +    zipl_secure_init_lists(&comps, &certs);
>> +    certs_len = zipl_secure_get_certs_length();
>> +    cert = malloc(certs_len);
>> +
>> +    have_sig = false;
> 
> You can get rid of this boolean and simply use sig_len as the indicator
> that there were nonzero bytes read when a signature entry was detected
> in the previous loop.  Where you set have_sig to false below, you can
> set sig_len to 0 to reset it.
> 
>> +    while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
>> +           entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
>> +
> 
> I'll be honest, I'm not a big fan of neither the design of this while
> loop nor the one in zipl_run_normal.  I'd prefer something like:
> 
> while (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
> 
>     // sanity checking
> 
>     switch (entry->component_type) {
>     case ZIPL_COMP_ENTRY_SIGNATURE:
>         ...
>         break;
>     case ZIPL_COMP_ENTRY_LOAD:
>         ...
>         break;
>     default:
>         // Unrecognized entry type, return error
>     }
> 
>     entry++;
> }
> 
> The above makes it clear that we're loading in segments until the exec
> entry is found, and the handling for each recognized component type is
> clearly labeled and organized.
> 
> I won't speak for making this change to the zipl_run_normal function
> yet, as it may introduce too many changes in this patch series.
> 
>> +        if (entry->component_type == ZIPL_COMP_ENTRY_SIGNATURE) {
>> +            /* There should never be two signatures in a row */
>> +            if (have_sig) {
>> +                return -1;
>> +            }
>> +
>> +            sig_len = zipl_handle_sig_entry(entry);
>> +            if (sig_len < 0) {
>> +                return -1;
>> +            }
>> +
>> +            have_sig = true;
>> +        } else {
>> +            comp_addr = entry->compdat.load_addr;
>> +            comp_len = zipl_load_segment(entry, comp_addr);
>> +            if (comp_len < 0) {
>> +                return -1;
>> +            }
>> +
>> +            if (have_sig) {
> 
> If you use my idea to re-write this loop, you'll be able to reduce one
> level of indentation of the code that follows by checking the inverse
> condition:
> 
> if (!have_sig) { // or if (!sig_len)
>     break;
> 
>> +                verified = verify_signature(comp_len, comp_addr,
>> +                                            sig_len, (uint64_t)sig_sec,
>> +                                            &cert_len, &cert_idx);
>> +
>> +                if (verified) {
>> +                    cert_index = handle_certificate(cert_table, &cert,
>> +                                                    cert_len, cert_idx,
>> +                                                    &certs, cert_index);
>> +                    if (cert_index == -1) {
>> +                        return -1;
>> +                    }
>> +
>> +                    puts("Verified component");
>> +                    zipl_secure_comp_list_add(&comps, comp_index, cert_table[cert_idx],
>> +                                              comp_addr, comp_len,
>> +                                              S390_IPL_COMPONENT_FLAG_SC |
>> +                                              S390_IPL_COMPONENT_FLAG_CSV);
>> +                } else {
>> +                    zipl_secure_comp_list_add(&comps, comp_index, -1,
>> +                                              comp_addr, comp_len,
>> +                                              S390_IPL_COMPONENT_FLAG_SC);
>> +                    zipl_secure_print("Could not verify component");
>> +                }
>> +
>> +                comp_index++;
>> +                signed_count += 1;
>> +                /* After a signature is used another new one can be accepted */
>> +                have_sig = false;
>> +            }
>> +        }
>> +
>> +        entry++;
>> +
>> +        if ((uint8_t *)(&entry[1]) > tmp_sec + MAX_SECTOR_SIZE) {
>> +            puts("Wrong entry value");
>> +            return -EINVAL;
>> +        }
> 
> Can someone who is more informed than I am of the IPL process please
> explain to me what is the purpose of the above check?  Why does it check
> if the next entry, the one which isn't going to be inspected/loaded, is
> within the bounds of tmp_sec?  This has been here since this file's
> inception and I can't find any documentation or mention that supports it.
> 
> This code precludes any of the secure IPL changes.
> 
> Was this actually meant to be entry[0] to ensure the actual entry we
> want to work on is not outside the bounds of tmp_sec?  Or perhaps it was
> meant to be done before the increment to entry?
> 
>> +    }
>> +
>> +    if (entry->component_type != ZIPL_COMP_ENTRY_EXEC) {
>> +        puts("No EXEC entry");
>> +        return -EINVAL;
>> +    }
>> +
>> +    if (signed_count == 0) {
>> +        zipl_secure_print("Secure boot is on, but components are not signed");
>> +    }
> 
> Shouldn't this be handled within the loop?  If it gets to a LOAD type
> without a signature, audit mode should report and move on.  Secure mode
> should report and abort on the spot.  Then you can get rid of signed_count.
> 
> I'll get back to reviewing patch 22 wrt to this variable's usage later on...
> 

This is handled after the loop because the number of LOAD components can
vary, and not all of them require signatures. We count the signed
components once all have been processed to verify whether components,
such as the stage3 bootloader and kernel, are signed for secure IPL.

The signed_count is also used in a later patch for the SCLAB check,
which allows only one signed component when the Single Component flag is
set.

>> +
>> +    if (zipl_secure_update_iirb(&comps, &certs)) {
>> +        zipl_secure_print("Failed to write IPL Information Report Block");
>> +    }
>> +    write_reset_psw(entry->compdat.load_psw);
>> +
>> +    return 0;
>> +}
>> +
>>  static int zipl_run_normal(ComponentEntry *entry, uint8_t *tmp_sec)
>>  {
>>      while (entry->component_type == ZIPL_COMP_ENTRY_LOAD ||
>> @@ -735,8 +893,17 @@ static int zipl_run(ScsiBlockPtr *pte)
>>      /* Load image(s) into RAM */
>>      entry = (ComponentEntry *)(&header[1]);
>>  
>> -    if (zipl_run_normal(entry, tmp_sec)) {
>> -        return -1;
>> +    switch (boot_mode) {
>> +    case ZIPL_SECURE_AUDIT_MODE:
>> +        if (zipl_run_secure(entry, tmp_sec)) {
>> +            return -1;
>> +        }
>> +        break;
>> +    case ZIPL_NORMAL_MODE:
>> +        if (zipl_run_normal(entry, tmp_sec)) {
>> +            return -1;
>> +        }
>> +        break;
>>      }
>>  
>>      /* should not return */
>> @@ -1095,17 +1262,35 @@ static int zipl_load_vscsi(void)
>>   * IPL starts here
>>   */
>>  
>> +ZiplBootMode zipl_mode(uint8_t hdr_flags)
>> +{
>> +    bool sipl_set = hdr_flags & DIAG308_IPIB_FLAGS_SIPL;
>> +    bool iplir_set = hdr_flags & DIAG308_IPIB_FLAGS_IPLIR;
>> +
>> +    if (!sipl_set && iplir_set) {
>> +        return ZIPL_SECURE_AUDIT_MODE;
>> +    }
>> +
>> +    return ZIPL_NORMAL_MODE;
>> +}
>> +
>>  void zipl_load(void)
>>  {
>>      VDev *vdev = virtio_get_device();
>>  
>>      if (vdev->is_cdrom) {
>> +        if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
>> +            panic("Secure boot from ISO image is not supported!");
>> +        }
>>          ipl_iso_el_torito();
>>          puts("Failed to IPL this ISO image!");
>>          return;
>>      }
>>  
>>      if (virtio_get_device_type() == VIRTIO_ID_NET) {
>> +        if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
>> +            panic("Virtio net boot device does not support secure boot!");
>> +        }
>>          netmain();
>>          puts("Failed to IPL from this network!");
>>          return;
>> @@ -1116,6 +1301,10 @@ void zipl_load(void)
>>          return;
>>      }
>>  
>> +    if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
>> +        panic("ECKD boot device does not support secure boot!");
>> +    }
>> +
>>      switch (virtio_get_device_type()) {
>>      case VIRTIO_ID_BLOCK:
>>          zipl_load_vblk();
>> diff --git a/pc-bios/s390-ccw/bootmap.h b/pc-bios/s390-ccw/bootmap.h
>> index 95943441d3..e48823a835 100644
>> --- a/pc-bios/s390-ccw/bootmap.h
>> +++ b/pc-bios/s390-ccw/bootmap.h
>> @@ -88,9 +88,18 @@ typedef struct BootMapTable {
>>      BootMapPointer entry[];
>>  } __attribute__ ((packed)) BootMapTable;
>>  
>> +#define DER_SIGNATURE_FORMAT 1
>> +
>> +typedef struct SignatureInformation {
>> +    uint8_t format;
>> +    uint8_t reserved[3];
>> +    uint32_t sig_len;
>> +} __attribute__((packed)) SignatureInformation;
>> +
>>  typedef union ComponentEntryData {
>>      uint64_t load_psw;
>>      uint64_t load_addr;
>> +    SignatureInformation sig_info;
>>  } ComponentEntryData;
>>  
>>  typedef struct ComponentEntry {
>> diff --git a/pc-bios/s390-ccw/main.c b/pc-bios/s390-ccw/main.c
>> index c9328f1c51..38962da1dd 100644
>> --- a/pc-bios/s390-ccw/main.c
>> +++ b/pc-bios/s390-ccw/main.c
>> @@ -28,6 +28,7 @@ IplParameterBlock *iplb;
>>  bool have_iplb;
>>  static uint16_t cutype;
>>  LowCore *lowcore; /* Yes, this *is* a pointer to address 0 */
>> +ZiplBootMode boot_mode;
>>  
>>  #define LOADPARM_PROMPT "PROMPT  "
>>  #define LOADPARM_EMPTY  "        "
>> @@ -272,9 +273,17 @@ static int virtio_setup(void)
>>  
>>  static void ipl_boot_device(void)
>>  {
>> +    if (boot_mode == 0) {
>> +        boot_mode = zipl_mode(iplb->hdr_flags);
>> +    }
>> +
>>      switch (cutype) {
>>      case CU_TYPE_DASD_3990:
>>      case CU_TYPE_DASD_2107:
>> +        if (boot_mode == ZIPL_SECURE_AUDIT_MODE) {
>> +            panic("Passthrough (vfio) device does not support secure boot!");
>> +        }
>> +
>>          dasd_ipl(blk_schid, cutype);
>>          break;
>>      case CU_TYPE_VIRTIO:
>> diff --git a/pc-bios/s390-ccw/s390-ccw.h b/pc-bios/s390-ccw/s390-ccw.h
>> index 6cdce3e5e5..648f407dc5 100644
>> --- a/pc-bios/s390-ccw/s390-ccw.h
>> +++ b/pc-bios/s390-ccw/s390-ccw.h
>> @@ -39,6 +39,9 @@ typedef unsigned long long u64;
>>  #define MIN_NON_ZERO(a, b) ((a) == 0 ? (b) : \
>>                              ((b) == 0 ? (a) : (MIN(a, b))))
>>  #endif
>> +#ifndef ROUND_UP
>> +#define ROUND_UP(n, d) (((n) + (d) - 1) & -(0 ? (n) : (d)))
>> +#endif
>>  
>>  #define ARRAY_SIZE(a) (sizeof(a) / sizeof((a)[0]))
>>  
>> @@ -64,6 +67,8 @@ void sclp_print(const char *string);
>>  void sclp_set_write_mask(uint32_t receive_mask, uint32_t send_mask);
>>  void sclp_setup(void);
>>  void sclp_get_loadparm_ascii(char *loadparm);
>> +bool sclp_is_diag320_on(void);
>> +bool sclp_is_sipl_on(void);
>>  int sclp_read(char *str, size_t count);
>>  
>>  /* virtio.c */
>> @@ -76,6 +81,15 @@ int virtio_read(unsigned long sector, void *load_addr);
>>  /* bootmap.c */
>>  void zipl_load(void);
>>  
>> +typedef enum ZiplBootMode {
>> +    ZIPL_NORMAL_MODE = 1,
>> +    ZIPL_SECURE_AUDIT_MODE = 2,
>> +} ZiplBootMode;
> 
> These should be ZIPL_BOOT_MODE_*
> 
> Also, is there a reason why the list starts at 1 and not defaulting to
> the implicit 0?
> 

boot_mode is a global variable defined in pc-bios/s390-ccw/main.c, and
it defaults to 0, which indicates that the boot mode hasn’t been
determined yet.

We start the list at 1 to reserve 0 as the implicit “undefined” or “not
yet set” value. The actual boot mode is only set later when boot_mode == 0:
    if (boot_mode == 0) {
        boot_mode = zipl_mode(iplb->hdr_flags);
    }
This allows us to distinguish between an unset state and valid boot modes.

>> +
>> +extern ZiplBootMode boot_mode;
>> +
>> +ZiplBootMode zipl_mode(uint8_t hdr_flags);
>> +
>>  /* jump2ipl.c */
>>  void write_reset_psw(uint64_t psw);
>>  int jump_to_IPL_code(uint64_t address);
>> diff --git a/pc-bios/s390-ccw/sclp.c b/pc-bios/s390-ccw/sclp.c
>> index 4a07de018d..0b03c3164f 100644
>> --- a/pc-bios/s390-ccw/sclp.c
>> +++ b/pc-bios/s390-ccw/sclp.c
>> @@ -113,6 +113,50 @@ void sclp_get_loadparm_ascii(char *loadparm)
>>      }
>>  }
>>  

[snip...]






  parent reply	other threads:[~2025-07-21 21:54 UTC|newest]

Thread overview: 62+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-07-11 21:10 [PATCH v4 00/28] Secure IPL Support for SCSI Scheme of virtio-blk/virtio-scsi Devices Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 01/28] Add boot-certificates to s390-ccw-virtio machine type option Zhuoying Cai
2025-07-22 15:56   ` Daniel P. Berrangé
2025-07-11 21:10 ` [PATCH v4 02/28] crypto/x509-utils: Add helper functions for certificate store Zhuoying Cai
2025-07-22 16:06   ` Daniel P. Berrangé
2025-07-11 21:10 ` [PATCH v4 03/28] hw/s390x/ipl: Create " Zhuoying Cai
2025-07-22 16:14   ` Daniel P. Berrangé
2025-07-11 21:10 ` [PATCH v4 04/28] s390x: Guest support for Certificate Store Facility (CS) Zhuoying Cai
2025-07-21 21:30   ` Collin Walling
2025-07-23 20:15     ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 05/28] s390x/diag: Introduce DIAG 320 for certificate store facility Zhuoying Cai
2025-07-21 21:26   ` Collin Walling
2025-07-21 21:39     ` Collin Walling
2025-07-22 21:08       ` Collin Walling
2025-07-23 17:50     ` Eric Farman
2025-07-11 21:10 ` [PATCH v4 06/28] s390x/diag: Refactor address validation check from diag308_parm_check Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 07/28] s390x/diag: Implement DIAG 320 subcode 1 Zhuoying Cai
2025-07-23 20:17   ` Eric Farman
2025-07-28 22:01     ` Zhuoying Cai
2025-07-23 22:15   ` Collin Walling
2025-07-23 22:20     ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 08/28] crypto/x509-utils: Add helper functions for DIAG 320 subcode 2 Zhuoying Cai
2025-07-22 16:20   ` Daniel P. Berrangé
2025-07-11 21:10 ` [PATCH v4 09/28] s390x/diag: Implement " Zhuoying Cai
2025-07-22 16:23   ` Daniel P. Berrangé
2025-07-28 20:59   ` Collin Walling
2025-07-29 18:18     ` Zhuoying Cai
2025-07-29 18:56       ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 10/28] s390x/diag: Introduce DIAG 508 for secure IPL operations Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 11/28] crypto/x509-utils: Add helper functions for DIAG 508 subcode 1 Zhuoying Cai
2025-07-22 16:26   ` Daniel P. Berrangé
2025-07-11 21:10 ` [PATCH v4 12/28] s390x/diag: Implement DIAG 508 subcode 1 for signature verification Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 13/28] pc-bios/s390-ccw: Introduce IPL Information Report Block (IIRB) Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 14/28] pc-bios/s390-ccw: Define memory for IPLB and convert IPLB to pointers Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 15/28] hw/s390x/ipl: Add IPIB flags to IPL Parameter Block Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 16/28] hw/s390x/ipl: Set iplb->len to maximum length of " Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 17/28] s390x: Guest support for Secure-IPL Facility Zhuoying Cai
2025-07-14 20:33   ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 18/28] pc-bios/s390-ccw: Refactor zipl_run() Zhuoying Cai
2025-07-14 20:53   ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 19/28] pc-bios/s390-ccw: Refactor zipl_load_segment function Zhuoying Cai
2025-07-14 22:10   ` Collin Walling
2025-07-15 15:59     ` Zhuoying Cai
2025-07-15 21:48       ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 20/28] pc-bios/s390-ccw: Add signature verification for secure IPL in audit mode Zhuoying Cai
2025-07-11 22:50   ` Collin Walling
2025-07-14 14:54     ` Jared Rossi
2025-07-14 15:34       ` Thomas Huth
2025-07-14 17:46         ` Collin Walling
2025-07-21 21:50     ` Zhuoying Cai [this message]
2025-07-28 21:05       ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 21/28] s390x: Guest support for Secure-IPL Code Loading Attributes Facility (SCLAF) Zhuoying Cai
2025-07-14 22:13   ` Collin Walling
2025-07-11 21:10 ` [PATCH v4 22/28] pc-bios/s390-ccw: Add additional security checks for secure boot Zhuoying Cai
2025-07-11 21:10 ` [PATCH v4 23/28] Add secure-boot to s390-ccw-virtio machine type option Zhuoying Cai
2025-07-11 21:11 ` [PATCH v4 24/28] hw/s390x/ipl: Set IPIB flags for secure IPL Zhuoying Cai
2025-07-11 21:11 ` [PATCH v4 25/28] pc-bios/s390-ccw: Handle true secure IPL mode Zhuoying Cai
2025-07-11 21:11 ` [PATCH v4 26/28] pc-bios/s390-ccw: Handle secure boot with multiple boot devices Zhuoying Cai
2025-07-22 16:28   ` Daniel P. Berrangé
2025-07-11 21:11 ` [PATCH v4 27/28] hw/s390x/ipl: Handle secure boot without specifying a boot device Zhuoying Cai
2025-07-11 21:11 ` [PATCH v4 28/28] docs: Add secure IPL documentation Zhuoying Cai
2025-07-21 19:13   ` 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=86deb470-54a6-460f-bf25-239d2256ea28@linux.ibm.com \
    --to=zycai@linux.ibm.com \
    --cc=berrange@redhat.com \
    --cc=borntraeger@linux.ibm.com \
    --cc=david@redhat.com \
    --cc=farman@linux.ibm.com \
    --cc=iii@linux.ibm.com \
    --cc=jjherne@linux.ibm.com \
    --cc=jrossi@linux.ibm.com \
    --cc=mjrosato@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 \
    /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).