Introduce a new <boot-certs> element inside <os> for s390x domains,
holding one or more <path> children pointing to X.509 certificates
used by the firmware during Secure IPL.

Example:
  <os>
    <type arch='s390x' machine='s390-ccw-virtio-11.1'>hvm</type>
    <boot-certs>
      <path>/path/to/cert.pem</path>
      <path>/path/to/cert2.pem</path>
    </boot-certs>
  </os>

Only the xml/conf layer is added here (parsing, formatting, and schema
validation). The QEMU command-line translation of these paths onto:

  -machine s390-ccw-virtio,...,boot-certs.<n>.path=<path>

is handled by QEMU itself and is not part of this patch.

The <boot-certs> element is intentionally omitted from migratable XML.
Certificate paths are host-local and will not exist on the destination
host. The guest XML on the destination needs to be configured with
<boot-certs> referencing certificate paths that are valid on the
destination host.

Signed-off-by: Zhuoying Cai <[email protected]>
---
 src/conf/domain_conf.c            | 69 +++++++++++++++++++++++++++++++
 src/conf/domain_conf.h            |  2 +
 src/conf/schemas/domaincommon.rng |  9 ++++
 3 files changed, 80 insertions(+)

diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c
index 94a1f8bd8e..1dfb7b251a 100644
--- a/src/conf/domain_conf.c
+++ b/src/conf/domain_conf.c
@@ -4112,6 +4112,9 @@ virDomainOSDefClear(virDomainOSDef *os)
     for (i = 0; i < os->nacpiTables; i++)
         virDomainOSACPITableDefFree(os->acpiTables[i]);
     g_free(os->acpiTables);
+    for (i = 0; i < os->nbootCerts; i++)
+        g_free(os->bootCertPaths[i]);
+    g_free(os->bootCertPaths);
     virDomainLoaderDefFree(os->loader);
     virDomainVarstoreDefFree(os->varstore);
     g_free(os->bootloader);
@@ -18753,6 +18756,58 @@ virDomainDefParseBootAcpiOptions(virDomainDef *def,
 }
 
 
+static int
+virDomainDefParseBootCertPaths(virDomainDef *def,
+                               xmlXPathContextPtr ctxt)
+{
+    int n;
+    g_autofree xmlNodePtr *nodes = NULL;
+    size_t ncerts = 0;
+    char **certs = NULL;
+    size_t i;
+
+    if (!virXPathNode("./os/boot-certs", ctxt))
+        return 0;
+
+    if ((n = virXPathNodeSet("./os/boot-certs/path", ctxt, &nodes)) < 0)
+        return -1;
+
+    if (n == 0) {
+        virReportError(VIR_ERR_XML_ERROR, "%s",
+                       _("missing <path> element in <boot-certs>"));
+        return -1;
+    }
+
+    certs = g_new0(char *, n);
+    for (i = 0; i < n; i++) {
+        g_autofree char *path = virXMLNodeContentString(nodes[i]);
+
+        if (!path)
+            goto error;
+
+        if (virStringIsEmpty(path)) {
+            virReportError(VIR_ERR_XML_ERROR, "%s",
+                           _("Paths for boot certificates must be provided"));
+            goto error;
+        }
+
+        certs[ncerts] = virFileSanitizePath(path);
+        ncerts++;
+    }
+
+    def->os.nbootCerts = ncerts;
+    def->os.bootCertPaths = certs;
+
+    return 0;
+
+ error:
+    for (i = 0; i < ncerts; i++)
+        g_free(certs[i]);
+    g_free(certs);
+    return -1;
+}
+
+
 static int
 virDomainDefParseBootOptions(virDomainDef *def,
                              xmlXPathContextPtr ctxt,
@@ -18781,6 +18836,9 @@ virDomainDefParseBootOptions(virDomainDef *def,
         if (virDomainDefParseBootAcpiOptions(def, ctxt) < 0)
             return -1;
 
+        if (virDomainDefParseBootCertPaths(def, ctxt) < 0)
+            return -1;
+
         if (virDomainDefParseBootXML(ctxt, def) < 0)
             return -1;
 
@@ -30222,6 +30280,17 @@ virDomainDefFormatInternalSetRootName(virDomainDef 
*def,
         virBufferAddLit(buf, "</acpi>\n");
     }
 
+    if (def->os.nbootCerts && !migratable) {
+        virBufferAddLit(buf, "<boot-certs>\n");
+        virBufferAdjustIndent(buf, 2);
+        for (i = 0; i < def->os.nbootCerts; i++) {
+            virBufferEscapeString(buf, "<path>%s</path>\n",
+                                  def->os.bootCertPaths[i]);
+        }
+        virBufferAdjustIndent(buf, -2);
+        virBufferAddLit(buf, "</boot-certs>\n");
+    }
+
     if (!def->os.bootloader) {
         for (n = 0; n < def->os.nBootDevs; n++) {
             const char *boottype =
diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h
index eadb5a49a4..c8bc5d6604 100644
--- a/src/conf/domain_conf.h
+++ b/src/conf/domain_conf.h
@@ -2604,6 +2604,8 @@ struct _virDomainOSDef {
     char *root;
     size_t nacpiTables;
     virDomainOSACPITableDef **acpiTables;
+    size_t nbootCerts;
+    char **bootCertPaths;
     virDomainLoaderDef *loader;
     virDomainVarstoreDef *varstore;
     char *bootloader;
diff --git a/src/conf/schemas/domaincommon.rng 
b/src/conf/schemas/domaincommon.rng
index d1b10c1c5f..db2365f592 100644
--- a/src/conf/schemas/domaincommon.rng
+++ b/src/conf/schemas/domaincommon.rng
@@ -315,6 +315,15 @@
               </oneOrMore>
             </element>
           </optional>
+          <optional>
+            <element name="boot-certs">
+              <oneOrMore>
+                <element name="path">
+                  <ref name="absFilePath"/>
+                </element>
+              </oneOrMore>
+            </element>
+          </optional>
           <optional>
             <element name="loader">
               <optional>
-- 
2.56.0

Reply via email to