12
* GNU General Public License for more details.
13
*/
14
15
+#include <inttypes.h>
16
#include "qemu/osdep.h"
17
#include "cpu.h"
18
#include "s390x-internal.h"
621
}
622
}
623
624
+static bool diag_508_verify_sig(uint8_t *cert, size_t cert_size,
625
+ uint8_t *comp, size_t comp_size,
626
+ uint8_t *sig, size_t sig_size)
627
+{
628
+ g_autofree uint8_t *sig_pem = NULL;
629
+ size_t sig_size_pem;
630
+ int rc;
631
+
632
+ /*
633
+ * PKCS#7 signature with DER format
634
+ * Convert to PEM format for signature verification
635
+ *
636
+ * Ignore errors during qcrypto signature format conversion and verification
637
+ * Return false on any error, treating it as a verification failure
638
+ */
639
+ rc = qcrypto_pkcs7_convert_sig_pem(sig, sig_size, &sig_pem, &sig_size_pem, NULL);
640
+ if (rc < 0) {
641
+ return false;
642
+ }
643
+
644
+ rc = qcrypto_x509_verify_sig(cert, cert_size,
645
+ comp, comp_size,
646
+ sig_pem, sig_size_pem, NULL);
647
+ if (rc < 0) {
648
+ return false;
649
+ }
650
+
651
+ return true;
652
+}
653
+
654
+static int handle_diag508_sig_verif(CPUS390XState *env, uint64_t addr)
655
+{
656
+ int verified;
657
+ uint32_t svb_len;
658
+ uint64_t comp_len, comp_addr;
659
+ uint64_t sig_len, sig_addr;
660
+ g_autofree uint8_t *comp = NULL;
661
+ g_autofree uint8_t *sig = NULL;
662
+ g_autofree Diag508SigVerifBlock *svb = NULL;
663
+ size_t svb_size = sizeof(Diag508SigVerifBlock);
664
+ S390IPLCertificateStore *cs = s390_ipl_get_certificate_store();
665
+
666
+ if (!cs->count) {
667
+ return DIAG_508_RC_NO_CERTS;
668
+ }
669
+
670
+ svb = g_new0(Diag508SigVerifBlock, 1);
671
+ s390_ipl_read(env, addr, svb, svb_size);
672
+
673
+ svb_len = be32_to_cpu(svb->length);
674
+ if (svb_len != svb_size) {
675
+ return DIAG_508_RC_INVAL_LEN;
676
+ }
677
+
678
+ comp_len = be64_to_cpu(svb->comp_len);
679
+ comp_addr = be64_to_cpu(svb->comp_addr);
680
+ sig_len = be64_to_cpu(svb->sig_len);
681
+ sig_addr = be64_to_cpu(svb->sig_addr);
682
+
683
+ if (!comp_len || !comp_addr || comp_len > DIAG_508_MAX_COMP_LEN) {
684
+ if (comp_len > DIAG_508_MAX_COMP_LEN) {
685
+ warn_report("DIAG 0x508: component length %" PRIu64
686
+ " exceeds current maximum %u",
687
+ comp_len, DIAG_508_MAX_COMP_LEN);
688
+ }
689
+ return DIAG_508_RC_INVAL_COMP_DATA;
690
+ }
691
+
692
+ if (!sig_len || !sig_addr || sig_len > DIAG_508_MAX_SIG_LEN) {
693
+ if (sig_len > DIAG_508_MAX_SIG_LEN) {
694
+ warn_report("DIAG 0x508: signature length %" PRIu64
695
+ " exceeds current maximum %u",
696
+ sig_len, DIAG_508_MAX_SIG_LEN);
697
+ }
698
+ return DIAG_508_RC_INVAL_PKCS7_SIG;
699
+ }
700
+
701
+ comp = g_malloc0(comp_len);
702
+ s390_ipl_read(env, comp_addr, comp, comp_len);
703
+
704
+ sig = g_malloc0(sig_len);
705
+ s390_ipl_read(env, sig_addr, sig, sig_len);
706
+
707
+ for (int i = 0; i < cs->count; i++) {
708
+ verified = diag_508_verify_sig(cs->certs[i].raw,
709
+ cs->certs[i].size,
710
+ comp, comp_len,
711
+ sig, sig_len);
712
+ if (verified) {
713
+ svb->cert_store_index = i;
714
+ svb->cert_len = cpu_to_be64(cs->certs[i].der_size);
715
+ s390_ipl_write(env, addr, svb, svb_size);
716
+ return DIAG_508_RC_OK;
717
+ }
718
+ }
719
+
720
+ return DIAG_508_RC_FAIL_VERIF;
721
+}
722
+
723
+QEMU_BUILD_BUG_MSG(sizeof(Diag508SigVerifBlock) != 64,
724
+ "size of Diag508SigVerifBlock is wrong");
725
+
726
void handle_diag_508(CPUS390XState *env, uint64_t r1, uint64_t r3, uintptr_t ra)
727
{
728
uint64_t subcode = env->regs[r3];
729
+ uint64_t addr = env->regs[r1];
730
int rc;
731
732
if (env->psw.mask & PSW_MASK_PSTATE) {
741
742
switch (subcode) {
743
case DIAG_508_SUBC_QUERY_SUBC:
640
- rc = 0;
744
+ rc = DIAG_508_SUBC_SIG_VERIF;
745
+ break;
746
+ case DIAG_508_SUBC_SIG_VERIF:
747
+ if (!diag_parm_addr_valid(addr, sizeof(Diag508SigVerifBlock), true)) {
748
+ s390_program_interrupt(env, PGM_ADDRESSING, ra);
749
+ return;
750
+ }
751
+
752
+ rc = handle_diag508_sig_verif(env, addr);
753
break;
754
default:
755
s390_program_interrupt(env, PGM_SPECIFICATION, ra);