From nobody Wed May 14 11:37:56 2025 Delivered-To: importer@patchew.org Received-SPF: pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) client-ip=209.132.183.28; envelope-from=libvir-list-bounces@redhat.com; helo=mx1.redhat.com; Authentication-Results: mx.zohomail.com; spf=pass (zoho.com: domain of redhat.com designates 209.132.183.28 as permitted sender) smtp.mailfrom=libvir-list-bounces@redhat.com Return-Path: Received: from mx1.redhat.com (mx1.redhat.com [209.132.183.28]) by mx.zohomail.com with SMTPS id 1522965402005762.4277033301155; Thu, 5 Apr 2018 14:56:42 -0700 (PDT) Received: from smtp.corp.redhat.com (int-mx06.intmail.prod.int.phx2.redhat.com [10.5.11.16]) (using TLSv1.2 with cipher AECDH-AES256-SHA (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id 9C40D804F8; Thu, 5 Apr 2018 21:56:40 +0000 (UTC) Received: from colo-mx.corp.redhat.com (colo-mx01.intmail.prod.int.phx2.redhat.com [10.5.11.20]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 6748B82274; Thu, 5 Apr 2018 21:56:40 +0000 (UTC) Received: from lists01.pubmisc.prod.ext.phx2.redhat.com (lists01.pubmisc.prod.ext.phx2.redhat.com [10.5.19.33]) by colo-mx.corp.redhat.com (Postfix) with ESMTP id 1100A180BAD9; Thu, 5 Apr 2018 21:56:40 +0000 (UTC) Received: from smtp.corp.redhat.com (int-mx05.intmail.prod.int.phx2.redhat.com [10.5.11.15]) by lists01.pubmisc.prod.ext.phx2.redhat.com (8.13.8/8.13.8) with ESMTP id w35LuNgW021545 for ; Thu, 5 Apr 2018 17:56:23 -0400 Received: by smtp.corp.redhat.com (Postfix) id 189B07D90D; Thu, 5 Apr 2018 21:56:23 +0000 (UTC) Received: from mx1.redhat.com (ext-mx03.extmail.prod.ext.phx2.redhat.com [10.5.110.27]) by smtp.corp.redhat.com (Postfix) with ESMTPS id 1075E7D647 for ; Thu, 5 Apr 2018 21:56:23 +0000 (UTC) Received: from mx0a-001b2d01.pphosted.com (mx0a-001b2d01.pphosted.com [148.163.156.1]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mx1.redhat.com (Postfix) with ESMTPS id AA33083F43 for ; Thu, 5 Apr 2018 21:56:15 +0000 (UTC) Received: from pps.filterd (m0098410.ppops.net [127.0.0.1]) by mx0a-001b2d01.pphosted.com (8.16.0.22/8.16.0.22) with SMTP id w35LuCCH021134 for ; Thu, 5 Apr 2018 17:56:15 -0400 Received: from e34.co.us.ibm.com (e34.co.us.ibm.com [32.97.110.152]) by mx0a-001b2d01.pphosted.com with ESMTP id 2h5uk794w2-1 (version=TLSv1.2 cipher=AES256-SHA256 bits=256 verify=NOT) for ; Thu, 05 Apr 2018 17:56:14 -0400 Received: from localhost by e34.co.us.ibm.com with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted for from ; Thu, 5 Apr 2018 15:56:13 -0600 Received: from b03cxnp08028.gho.boulder.ibm.com (9.17.130.20) by e34.co.us.ibm.com (192.168.1.134) with IBM ESMTP SMTP Gateway: Authorized Use Only! Violators will be prosecuted; Thu, 5 Apr 2018 15:56:12 -0600 Received: from b03ledav002.gho.boulder.ibm.com (b03ledav002.gho.boulder.ibm.com [9.17.130.233]) by b03cxnp08028.gho.boulder.ibm.com (8.14.9/8.14.9/NCO v10.0) with ESMTP id w35LuBe010879308; Thu, 5 Apr 2018 14:56:11 -0700 Received: from b03ledav002.gho.boulder.ibm.com (unknown [127.0.0.1]) by IMSVA (Postfix) with ESMTP id A7511136043; Thu, 5 Apr 2018 15:56:11 -0600 (MDT) Received: from sbct-3.watson.ibm.com (unknown [9.47.158.153]) by b03ledav002.gho.boulder.ibm.com (Postfix) with ESMTP id 3A50013603A; Thu, 5 Apr 2018 15:56:11 -0600 (MDT) From: Stefan Berger To: libvir-list@redhat.com Date: Thu, 5 Apr 2018 17:56:02 -0400 In-Reply-To: <1522965366-836-1-git-send-email-stefanb@linux.vnet.ibm.com> References: <1522965366-836-1-git-send-email-stefanb@linux.vnet.ibm.com> X-TM-AS-GCONF: 00 x-cbid: 18040521-0016-0000-0000-0000087FC432 X-IBM-SpamModules-Scores: X-IBM-SpamModules-Versions: BY=3.00008810; HX=3.00000241; KW=3.00000007; PH=3.00000004; SC=3.00000256; SDB=6.01013642; UDB=6.00516697; IPR=6.00792903; MB=3.00020438; MTD=3.00000008; XFM=3.00000015; UTC=2018-04-05 21:56:13 X-IBM-AV-DETECTION: SAVI=unused REMOTE=unused XFE=unused x-cbparentid: 18040521-0017-0000-0000-00003E22F254 Message-Id: <1522965366-836-3-git-send-email-stefanb@linux.vnet.ibm.com> X-Proofpoint-Virus-Version: vendor=fsecure engine=2.50.10432:, , definitions=2018-04-05_09:, , signatures=0 X-Proofpoint-Spam-Details: rule=outbound_notspam policy=outbound score=0 priorityscore=1501 malwarescore=0 suspectscore=43 phishscore=0 bulkscore=0 spamscore=0 clxscore=1015 lowpriorityscore=0 impostorscore=0 adultscore=0 classifier=spam adjust=0 reason=mlx scancount=1 engine=8.0.1-1709140000 definitions=main-1804050222 X-Greylist: Sender passed SPF test, Sender IP whitelisted by DNSRBL, ACL 207 matched, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Thu, 05 Apr 2018 21:56:15 +0000 (UTC) X-Greylist: inspected by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Thu, 05 Apr 2018 21:56:15 +0000 (UTC) for IP:'148.163.156.1' DOMAIN:'mx0a-001b2d01.pphosted.com' HELO:'mx0a-001b2d01.pphosted.com' FROM:'stefanb@linux.vnet.ibm.com' RCPT:'' X-RedHat-Spam-Score: -1.71 (RCVD_IN_DNSWL_LOW, RCVD_IN_MSPIKE_H5, RCVD_IN_MSPIKE_WL) 148.163.156.1 mx0a-001b2d01.pphosted.com 148.163.156.1 mx0a-001b2d01.pphosted.com X-Scanned-By: MIMEDefang 2.78 on 10.5.110.27 X-Scanned-By: MIMEDefang 2.79 on 10.5.11.15 X-loop: libvir-list@redhat.com Subject: [libvirt] [PATCH 2/6] tpm: Add support for external swtpm TPM emulator X-BeenThere: libvir-list@redhat.com X-Mailman-Version: 2.1.12 Precedence: junk List-Id: Development discussions about the libvirt library & tools List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Sender: libvir-list-bounces@redhat.com Errors-To: libvir-list-bounces@redhat.com X-Scanned-By: MIMEDefang 2.79 on 10.5.11.16 X-Greylist: Sender IP whitelisted, not delayed by milter-greylist-4.5.16 (mx1.redhat.com [10.5.110.27]); Thu, 05 Apr 2018 21:56:41 +0000 (UTC) X-ZohoMail: RSF_0 Z_629925259 SPT_0 Content-Type: text/plain; charset="utf-8" This patch adds support for an external swtpm TPM emulator. The XML for this type of TPM looks as follows: The XML will currently only start a TPM 1.2. Upon the first start, libvirt will run `swtpm_setup`, which will simulate t= he manufacturing of a TPM and create certificates for it and write them into t= he NVRAM location of the emulated TPM. Then, libvirt will automatically start the swtpm TPM emulator using the `sw= tpm` executable. Once the VM terminates, libvirt uses the swtpm_ioctl executable to graceful= ly shut down the `swtpm` in case it is still running (QEMU did not send shutdo= wn) or clean up the socket file. The above mentioned executables must be found in the PATH. The executables can either be run as root or started as root and switch to the tss user. The requirement for the tss user comes through 'tcsd', which is used for the simulation of the manufacturing. Which user is used can be configured through qemu.conf. The swtpm writes out state into files. The state is kept in /var/lib/libvir= t/tpm: [root@localhost libvirt]# ls -lZ | grep tpm drwx--x--x. 7 root root unconfined_u:object_r:virt_var_lib_t:s0 4096 Apr 5= 16:22 tpm The directory /var/lib/libvirt/tpm maintains per-TPM state directories but also hosts the UnixIO socket of running swtpms, which QEMU uses for communi= cating with them. At this point only the socket file is labeled properly and made = accessible for QEMU, which runs under the qemu user: [root@localhost tpm]# ls -lZ total 4 drwx------. 2 tss tss system_u:object_r:virt_var_lib_t:s0 4096 A= pr 5 16:46 485d0004-a48f-436a-8457-8a3b73e28567 srw-------. 1 qemu qemu system_u:object_r:svirt_image_t:s0:c413,c430 0 A= pr 5 16:46 485d0004-a48f-436a-8457-8a3b73e28567.sock [root@localhost 485d0004-a48f-436a-8457-8a3b73e28567]# ls -lZ total 8 -rw-r--r--. 1 tss tss system_u:object_r:virt_var_lib_t:s0 3648 Apr 5 16:46= tpm-00.permall -rw-r--r--. 1 tss tss system_u:object_r:virt_var_lib_t:s0 2237 Apr 5 16:46= vtpm.log root@sbct-3 485d0004-a48f-436a-8457-8a3b73e28567]# ps auxZ | grep swtpm | g= rep -v grep system_u:system_r:virtd_t:s0-s0:c0.c1023 tss 18697 0.0 0.0 28172 3892 ? = Ss 16:46 0:00 /usr/bin/swtpm socket --daemon --ctrl type=3Dunixio,p= ath=3D/var/lib/libvirt/tpm/485d0004-a48f-436a-8457-8a3b73e28567.sock,mode= =3D0600 --tpmstate dir=3D/var/lib/libvirt/tpm/485d0004-a48f-436a-8457-8a3b7= 3e28567 --log file=3D/var/lib/libvirt/tpm/485d0004-a48f-436a-8457-8a3b73e28= 567/vtpm.log --runas 59 [root@sbct-3 485d0004-a48f-436a-8457-8a3b73e28567]# ps auxZ | grep qemu | g= rep tpm | grep -v grep system_u:system_r:svirt_t:s0:c413,c430 qemu 18702 2.5 0.0 3036052 48676 ? = Sl 16:46 0:08 /bin/qemu-system-x86_64 -name guest=3Dcentos7.0,debug= -threads=3Don -S -object secret,id=3DmasterKey0,format=3Draw,file=3D/var/li= b/libvirt/qemu/domain-6-centos7.0/master-key.aes -machine pc-i440fx-2.8,acc= el=3Dkvm,usb=3Doff,dump-guest-core=3Doff -cpu kvm64 -m 2048 -realtime mlock= =3Doff -smp 2,sockets=3D2,cores=3D1,threads=3D1 -uuid 485d0004-a48f-436a-84= 57-8a3b73e28567 [...] -tpmdev emulator,id=3Dtpm-tpm0,chardev=3Dchrtpm -char= dev socket,id=3Dchrtpm,path=3D/var/lib/libvirt/tpm/485d0004-a48f-436a-8457-= 8a3b73e28567.sock -device tpm-tis,tpmdev=3Dtpm-tpm0,id=3Dtpm0 -device usb-m= ouse,id=3Dinput0,bus=3Dusb.0,port=3D1 -vnc 127.0.0.1:0 -device cirrus-vga,i= d=3Dvideo0,bus=3Dpci.0,addr=3D0x2 -device virtio-balloon-pci,id=3Dballoon0,= bus=3Dpci.0,addr=3D0x6 -msg timestamp=3Don Signed-off-by: Stefan Berger --- docs/formatdomain.html.in | 30 ++ docs/schemas/domaincommon.rng | 5 + src/conf/domain_audit.c | 2 + src/conf/domain_conf.c | 51 ++- src/conf/domain_conf.h | 5 + src/libvirt_private.syms | 5 + src/qemu/Makefile.inc.am | 2 + src/qemu/libvirtd_qemu.aug | 3 + src/qemu/qemu.conf | 7 + src/qemu/qemu_capabilities.c | 5 + src/qemu/qemu_capabilities.h | 1 + src/qemu/qemu_cgroup.c | 1 + src/qemu/qemu_command.c | 52 ++- src/qemu/qemu_conf.c | 11 +- src/qemu/qemu_conf.h | 2 + src/qemu/qemu_domain.c | 2 + src/qemu/qemu_driver.c | 13 + src/qemu/qemu_extdevice.c | 195 ++++++++++ src/qemu/qemu_extdevice.h | 36 ++ src/qemu/qemu_process.c | 8 + src/qemu/test_libvirtd_qemu.aug.in | 1 + src/security/security_dac.c | 6 + src/security/security_selinux.c | 11 + src/util/virfile.c | 12 + src/util/virfile.h | 2 +- src/util/virtpm.c | 432 +++++++++++++++++= ++++ src/util/virtpm.h | 12 + tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml | 1 + tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml | 1 + tests/qemuxml2argvdata/tpm-emulator.args | 24 ++ tests/qemuxml2argvdata/tpm-emulator.xml | 30 ++ tests/qemuxml2argvmock.c | 2 + tests/qemuxml2argvtest.c | 17 + tests/qemuxml2xmloutdata/tpm-emulator.xml | 34 ++ tests/qemuxml2xmltest.c | 1 + 38 files changed, 1011 insertions(+), 14 deletions(-) create mode 100644 src/qemu/qemu_extdevice.c create mode 100644 src/qemu/qemu_extdevice.h create mode 100644 tests/qemuxml2argvdata/tpm-emulator.args create mode 100644 tests/qemuxml2argvdata/tpm-emulator.xml create mode 100644 tests/qemuxml2xmloutdata/tpm-emulator.xml diff --git a/docs/formatdomain.html.in b/docs/formatdomain.html.in index 16fc7db..bd6fedc 100644 --- a/docs/formatdomain.html.in +++ b/docs/formatdomain.html.in @@ -7621,6 +7621,26 @@ qemu-kvm -net nic,model=3D? /dev/null </devices> ... + +

+ The emulator device type gives access to a TPM emulator providing + TPM functionlity for each VM. QEMU talks to it over a UnixIO socket.= With + the emulator device type each guest gets its own private TPM. + 'emulator' since 4.x.y +

+

+ Example: usage of the TPM Emulator +

+
+  ...
+  <devices>
+    <tpm model=3D'tpm-tis'>
+      <backend type=3D'emulator'>
+      </backend>
+    </tpm>
+  </devices>
+  ...
+
model
@@ -7653,6 +7673,16 @@ qemu-kvm -net nic,model=3D? /dev/null

+
+
emulator
+
+

+ For this backend type the 'swtpm' TPM Emulator must be insta= lled on the + host. Libvirt will automatically start an independent TPM em= ulator + for each QEMU guest requesting access to it. +

+
+
=20 diff --git a/docs/schemas/domaincommon.rng b/docs/schemas/domaincommon.rng index be5c628..d628444 100644 --- a/docs/schemas/domaincommon.rng +++ b/docs/schemas/domaincommon.rng @@ -4134,6 +4134,11 @@ + + + emulator + + diff --git a/src/conf/domain_audit.c b/src/conf/domain_audit.c index 82868bc..25cccdd 100644 --- a/src/conf/domain_audit.c +++ b/src/conf/domain_audit.c @@ -586,6 +586,8 @@ virDomainAuditTPM(virDomainObjPtr vm, virDomainTPMDefPt= r tpm, "virt=3D%s resrc=3Ddev reason=3D%s %s uuid=3D%s %s", virt, reason, vmname, uuidstr, device); break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + break; case VIR_DOMAIN_TPM_TYPE_LAST: default: break; diff --git a/src/conf/domain_conf.c b/src/conf/domain_conf.c index 232174a..da14ef8 100644 --- a/src/conf/domain_conf.c +++ b/src/conf/domain_conf.c @@ -862,7 +862,8 @@ VIR_ENUM_IMPL(virDomainTPMModel, VIR_DOMAIN_TPM_MODEL_L= AST, "tpm-crb") =20 VIR_ENUM_IMPL(virDomainTPMBackend, VIR_DOMAIN_TPM_TYPE_LAST, - "passthrough") + "passthrough", + "emulator") =20 VIR_ENUM_IMPL(virDomainIOMMUModel, VIR_DOMAIN_IOMMU_MODEL_LAST, "intel") @@ -2588,6 +2589,29 @@ void virDomainHostdevDefClear(virDomainHostdevDefPtr= def) } } =20 +static void virDomainTPMDeleteAny(const virDomainDef *def) +{ + virTPMDeleteEmulatorStorage(def->uuid); +} + +void virDomainTPMDelete(virDomainDefPtr def) +{ + virDomainTPMDefPtr tpm =3D def->tpm; + + if (!tpm) + return; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + virTPMDeleteEmulatorStorage(def->uuid); + break; + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + case VIR_DOMAIN_TPM_TYPE_LAST: + /* nothing to do */ + break; + } +} + void virDomainTPMDefFree(virDomainTPMDefPtr def) { if (!def) @@ -2597,6 +2621,9 @@ void virDomainTPMDefFree(virDomainTPMDefPtr def) case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: VIR_FREE(def->data.passthrough.source.data.file.path); break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + VIR_FREE(def->data.emulator.source.data.nix.path); + break; case VIR_DOMAIN_TPM_TYPE_LAST: break; } @@ -12525,6 +12552,11 @@ virDomainSmartcardDefParseXML(virDomainXMLOptionPt= r xmlopt, * * * + * or like this: + * + * + * + * */ static virDomainTPMDefPtr virDomainTPMDefParseXML(virDomainXMLOptionPtr xmlopt, @@ -12591,6 +12623,8 @@ virDomainTPMDefParseXML(virDomainXMLOptionPtr xmlop= t, def->data.passthrough.source.type =3D VIR_DOMAIN_CHR_TYPE_DEV; path =3D NULL; break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + break; case VIR_DOMAIN_TPM_TYPE_LAST: goto error; } @@ -24760,24 +24794,32 @@ virDomainTPMDefFormat(virBufferPtr buf, virDomainTPMDefPtr def, unsigned int flags) { + bool did_nl =3D false; + virBufferAsprintf(buf, "\n", virDomainTPMModelTypeToString(def->model)); virBufferAdjustIndent(buf, 2); - virBufferAsprintf(buf, "\n", + virBufferAsprintf(buf, "type)); virBufferAdjustIndent(buf, 2); =20 switch (def->type) { case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + virBufferAddLit(buf, ">\n"); + did_nl =3D true; virBufferEscapeString(buf, "\n", def->data.passthrough.source.data.file.path); break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: case VIR_DOMAIN_TPM_TYPE_LAST: break; } =20 virBufferAdjustIndent(buf, -2); - virBufferAddLit(buf, "\n"); + if (did_nl) + virBufferAddLit(buf, "\n"); + else + virBufferAddLit(buf, "/>\n"); =20 virDomainDeviceInfoFormat(buf, &def->info, flags); =20 @@ -27548,6 +27590,9 @@ virDomainDeleteConfig(const char *configDir, goto cleanup; } =20 + /* in case it had a TPM, remove it */ + virDomainTPMDeleteAny(dom->def); + ret =3D 0; =20 cleanup: diff --git a/src/conf/domain_conf.h b/src/conf/domain_conf.h index 1724340..4ecc70d 100644 --- a/src/conf/domain_conf.h +++ b/src/conf/domain_conf.h @@ -1284,6 +1284,7 @@ typedef enum { =20 typedef enum { VIR_DOMAIN_TPM_TYPE_PASSTHROUGH, + VIR_DOMAIN_TPM_TYPE_EMULATOR, =20 VIR_DOMAIN_TPM_TYPE_LAST } virDomainTPMBackendType; @@ -1298,6 +1299,9 @@ struct _virDomainTPMDef { struct { virDomainChrSourceDef source; } passthrough; + struct { + virDomainChrSourceDef source; + } emulator; } data; }; =20 @@ -2810,6 +2814,7 @@ int virDomainDeviceAddressIsValid(virDomainDeviceInfo= Ptr info, int type); virDomainDeviceInfoPtr virDomainDeviceGetInfo(virDomainDeviceDefPtr device= ); void virDomainTPMDefFree(virDomainTPMDefPtr def); +void virDomainTPMDelete(virDomainDefPtr def); =20 typedef int (*virDomainDeviceInfoCallback)(virDomainDefPtr def, virDomainDeviceDefPtr dev, diff --git a/src/libvirt_private.syms b/src/libvirt_private.syms index 03fe3b3..e64bbef 100644 --- a/src/libvirt_private.syms +++ b/src/libvirt_private.syms @@ -556,6 +556,7 @@ virDomainTimerTrackTypeToString; virDomainTPMBackendTypeFromString; virDomainTPMBackendTypeToString; virDomainTPMDefFree; +virDomainTPMDelete; virDomainTPMModelTypeFromString; virDomainTPMModelTypeToString; virDomainUSBDeviceDefForeach; @@ -2971,6 +2972,10 @@ virTimeStringThenRaw; =20 # util/virtpm.h virTPMCreateCancelPath; +virTPMDeleteEmulatorStorage; +virTPMEmulatorBuildCommand; +virTPMStopEmulator; +virTPMTryConnect; =20 =20 # util/virtypedparam.h diff --git a/src/qemu/Makefile.inc.am b/src/qemu/Makefile.inc.am index 8ef290a..6c8daf8 100644 --- a/src/qemu/Makefile.inc.am +++ b/src/qemu/Makefile.inc.am @@ -19,6 +19,8 @@ QEMU_DRIVER_SOURCES =3D \ qemu/qemu_domain_address.h \ qemu/qemu_cgroup.c \ qemu/qemu_cgroup.h \ + qemu/qemu_extdevice.c \ + qemu/qemu_extdevice.h \ qemu/qemu_hostdev.c \ qemu/qemu_hostdev.h \ qemu/qemu_hotplug.c \ diff --git a/src/qemu/libvirtd_qemu.aug b/src/qemu/libvirtd_qemu.aug index c19bf3a..cc5d657 100644 --- a/src/qemu/libvirtd_qemu.aug +++ b/src/qemu/libvirtd_qemu.aug @@ -118,6 +118,8 @@ module Libvirtd_qemu =3D let vxhs_entry =3D bool_entry "vxhs_tls" | str_entry "vxhs_tls_x509_cert_dir" =20 + let swtpm_entry =3D str_entry "swtpm_user" + (* Each entry in the config is one of the following ... *) let entry =3D default_tls_entry | vnc_entry @@ -137,6 +139,7 @@ module Libvirtd_qemu =3D | gluster_debug_level_entry | memory_entry | vxhs_entry + | swtpm_entry =20 let comment =3D [ label "#comment" . del /#[ \t]*/ "# " . store /([^ \= t\n][^\n]*)?/ . del /\n/ "\n" ] let empty =3D [ label "#empty" . eol ] diff --git a/src/qemu/qemu.conf b/src/qemu/qemu.conf index 43dd561..f64ae68 100644 --- a/src/qemu/qemu.conf +++ b/src/qemu/qemu.conf @@ -775,3 +775,10 @@ # This directory is used for memoryBacking source if configured as file. # NOTE: big files will be stored here #memory_backing_dir =3D "/var/lib/libvirt/qemu/ram" + +# User for the swtpm TPM Emulator +# +# Default is 'tss'; this is the same user that tcsd (TrouSerS) installs +# and uses; alternative is 'root' +# +#swtpm_user =3D "tss" diff --git a/src/qemu/qemu_capabilities.c b/src/qemu/qemu_capabilities.c index 0952663..ce4db62 100644 --- a/src/qemu/qemu_capabilities.c +++ b/src/qemu/qemu_capabilities.c @@ -467,6 +467,7 @@ VIR_ENUM_IMPL(virQEMUCaps, QEMU_CAPS_LAST, "virtio-mouse-ccw", "virtio-tablet-ccw", "tpm-crb", + "tpm-emulator", ); =20 =20 @@ -3098,6 +3099,10 @@ static const struct tpmTypeToCaps virQEMUCapsTPMType= sToCaps[] =3D { .type =3D VIR_DOMAIN_TPM_TYPE_PASSTHROUGH, .caps =3D QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, }, + { + .type =3D VIR_DOMAIN_TPM_TYPE_EMULATOR, + .caps =3D QEMU_CAPS_DEVICE_TPM_EMULATOR, + }, }; =20 const struct tpmTypeToCaps virQEMUCapsTPMModelsToCaps[] =3D { diff --git a/src/qemu/qemu_capabilities.h b/src/qemu/qemu_capabilities.h index 604525a..0cc2882 100644 --- a/src/qemu/qemu_capabilities.h +++ b/src/qemu/qemu_capabilities.h @@ -451,6 +451,7 @@ typedef enum { QEMU_CAPS_DEVICE_VIRTIO_MOUSE_CCW, /* -device virtio-mouse-ccw */ QEMU_CAPS_DEVICE_VIRTIO_TABLET_CCW, /* -device virtio-tablet-ccw */ QEMU_CAPS_DEVICE_TPM_CRB, /* -device tpm-crb */ + QEMU_CAPS_DEVICE_TPM_EMULATOR, /* -tpmdev emulator */ =20 QEMU_CAPS_LAST /* this must always be the last item */ } virQEMUCapsFlags; diff --git a/src/qemu/qemu_cgroup.c b/src/qemu/qemu_cgroup.c index b604edb..bd4859c 100644 --- a/src/qemu/qemu_cgroup.c +++ b/src/qemu/qemu_cgroup.c @@ -238,6 +238,7 @@ qemuSetupTPMCgroup(virDomainObjPtr vm) case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: ret =3D qemuSetupChrSourceCgroup(vm, &dev->data.passthrough.source= ); break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: case VIR_DOMAIN_TPM_TYPE_LAST: break; } diff --git a/src/qemu/qemu_command.c b/src/qemu/qemu_command.c index 89fd08b..878a147 100644 --- a/src/qemu/qemu_command.c +++ b/src/qemu/qemu_command.c @@ -9614,21 +9614,33 @@ qemuBuildTPMDevStr(const virDomainDef *def, =20 =20 static char * -qemuBuildTPMBackendStr(const virDomainDef *def, +qemuBuildTPMBackendStr(virDomainDef *def, + virQEMUDriverPtr driver, virCommandPtr cmd, virQEMUCapsPtr qemuCaps, int *tpmfd, - int *cancelfd) + int *cancelfd, + char **chardev) { - const virDomainTPMDef *tpm =3D def->tpm; + virDomainTPMDef *tpm =3D def->tpm; virBuffer buf =3D VIR_BUFFER_INITIALIZER; - const char *type =3D virDomainTPMBackendTypeToString(tpm->type); + const char *type =3D NULL; char *cancel_path =3D NULL, *devset =3D NULL; const char *tpmdev; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); =20 *tpmfd =3D -1; *cancelfd =3D -1; =20 + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + type =3D virDomainTPMBackendTypeToString(tpm->type); + break; + case VIR_DOMAIN_TPM_TYPE_LAST: + goto error; + } + virBufferAsprintf(&buf, "%s,id=3Dtpm-%s", type, tpm->info.alias); =20 switch (tpm->type) { @@ -9679,6 +9691,17 @@ qemuBuildTPMBackendStr(const virDomainDef *def, VIR_FREE(cancel_path); =20 break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + if (!virQEMUCapsGet(qemuCaps, QEMU_CAPS_DEVICE_TPM_EMULATOR)) + goto no_support; + + virBufferAddLit(&buf, ",chardev=3Dchrtpm"); + + if (virAsprintf(chardev, "socket,id=3Dchrtpm,path=3D%s", + tpm->data.emulator.source.data.nix.path) < 0) + goto error; + + break; case VIR_DOMAIN_TPM_TYPE_LAST: goto error; } @@ -9686,6 +9709,8 @@ qemuBuildTPMBackendStr(const virDomainDef *def, if (virBufferCheckError(&buf) < 0) goto error; =20 + virObjectUnref(cfg); + return virBufferContentAndReset(&buf); =20 no_support: @@ -9699,16 +9724,19 @@ qemuBuildTPMBackendStr(const virDomainDef *def, VIR_FREE(cancel_path); =20 virBufferFreeAndReset(&buf); + virObjectUnref(cfg); return NULL; } =20 =20 static int -qemuBuildTPMCommandLine(virCommandPtr cmd, - const virDomainDef *def, +qemuBuildTPMCommandLine(virQEMUDriverPtr driver, + virCommandPtr cmd, + virDomainDef *def, virQEMUCapsPtr qemuCaps) { char *optstr; + char *chardev =3D NULL; int tpmfd =3D -1; int cancelfd =3D -1; char *fdset; @@ -9716,13 +9744,19 @@ qemuBuildTPMCommandLine(virCommandPtr cmd, if (!def->tpm) return 0; =20 - if (!(optstr =3D qemuBuildTPMBackendStr(def, cmd, qemuCaps, - &tpmfd, &cancelfd))) + if (!(optstr =3D qemuBuildTPMBackendStr(def, driver, cmd, qemuCaps, + &tpmfd, &cancelfd, + &chardev))) return -1; =20 virCommandAddArgList(cmd, "-tpmdev", optstr, NULL); VIR_FREE(optstr); =20 + if (chardev) { + virCommandAddArgList(cmd, "-chardev", chardev, NULL); + VIR_FREE(chardev); + } + if (tpmfd >=3D 0) { fdset =3D qemuVirCommandGetFDSet(cmd, tpmfd); if (!fdset) @@ -10151,7 +10185,7 @@ qemuBuildCommandLine(virQEMUDriverPtr driver, chardevStdioLogd) < 0) goto error; =20 - if (qemuBuildTPMCommandLine(cmd, def, qemuCaps) < 0) + if (qemuBuildTPMCommandLine(driver, cmd, def, qemuCaps) < 0) goto error; =20 if (qemuBuildInputCommandLine(cmd, def, qemuCaps) < 0) diff --git a/src/qemu/qemu_conf.c b/src/qemu/qemu_conf.c index 36cf3a2..a204105 100644 --- a/src/qemu/qemu_conf.c +++ b/src/qemu/qemu_conf.c @@ -336,6 +336,9 @@ virQEMUDriverConfigPtr virQEMUDriverConfigNew(bool priv= ileged) &cfg->nfirmwares) < 0) goto error; =20 + if (virGetUserID("tss", &cfg->swtpm_user) < 0) + cfg->swtpm_user =3D 0; /* root */ + return cfg; =20 error: @@ -475,7 +478,7 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr = cfg, int rv; size_t i, j; char *stdioHandler =3D NULL; - char *user =3D NULL, *group =3D NULL; + char *user =3D NULL, *group =3D NULL, *swtpm_user =3D NULL; char **controllers =3D NULL; char **hugetlbfs =3D NULL; char **nvram =3D NULL; @@ -912,6 +915,11 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr= cfg, if (virConfGetValueString(conf, "memory_backing_dir", &cfg->memoryBack= ingDir) < 0) goto cleanup; =20 + if (virConfGetValueString(conf, "swtpm_user", &swtpm_user) < 0) + goto cleanup; + if (swtpm_user && virGetUserID(swtpm_user, &cfg->swtpm_user) < 0) + goto cleanup; + ret =3D 0; =20 cleanup: @@ -922,6 +930,7 @@ int virQEMUDriverConfigLoadFile(virQEMUDriverConfigPtr = cfg, VIR_FREE(corestr); VIR_FREE(user); VIR_FREE(group); + VIR_FREE(swtpm_user); virConfFree(conf); return ret; } diff --git a/src/qemu/qemu_conf.h b/src/qemu/qemu_conf.h index e1ad546..6908c36 100644 --- a/src/qemu/qemu_conf.h +++ b/src/qemu/qemu_conf.h @@ -206,6 +206,8 @@ struct _virQEMUDriverConfig { =20 bool vxhsTLS; char *vxhsTLSx509certdir; + + uid_t swtpm_user; }; =20 /* Main driver state */ diff --git a/src/qemu/qemu_domain.c b/src/qemu/qemu_domain.c index 580e0f8..9b7f8ff 100644 --- a/src/qemu/qemu_domain.c +++ b/src/qemu/qemu_domain.c @@ -7088,6 +7088,7 @@ qemuDomainRemoveInactive(virQEMUDriverPtr driver, VIR_WARN("unable to remove snapshot directory %s", snapDir); VIR_FREE(snapDir); } + virDomainTPMDelete(vm->def); =20 virObjectRef(vm); =20 @@ -10280,6 +10281,7 @@ qemuDomainSetupTPM(virQEMUDriverConfigPtr cfg ATTRI= BUTE_UNUSED, return -1; break; =20 + case VIR_DOMAIN_TPM_TYPE_EMULATOR: case VIR_DOMAIN_TPM_TYPE_LAST: /* nada */ break; diff --git a/src/qemu/qemu_driver.c b/src/qemu/qemu_driver.c index 7bcc493..ef0d0c9 100644 --- a/src/qemu/qemu_driver.c +++ b/src/qemu/qemu_driver.c @@ -14365,6 +14365,19 @@ qemuDomainSnapshotPrepare(virDomainObjPtr vm, goto cleanup; } =20 + if (vm->def->tpm) { + switch (vm->def->tpm->type) { + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + virReportError(VIR_ERR_CONFIG_UNSUPPORTED, + _("attached '%s' TPM does not allow snapshots"), + virDomainTPMBackendTypeToString(vm->def->tpm->t= ype)); + goto cleanup; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + } + for (i =3D 0; i < def->ndisks; i++) { virDomainSnapshotDiskDefPtr disk =3D &def->disks[i]; virDomainDiskDefPtr dom_disk =3D vm->def->disks[i]; diff --git a/src/qemu/qemu_extdevice.c b/src/qemu/qemu_extdevice.c new file mode 100644 index 0000000..4f42c9b --- /dev/null +++ b/src/qemu/qemu_extdevice.c @@ -0,0 +1,195 @@ +/* + * qemu_extdevice.c: QEMU external devices support + * + * Copyright (C) 2014 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Stefan Berger + */ + +#include + +#include "qemu_extdevice.h" +#include "qemu_domain.h" + +#include "viralloc.h" +#include "virlog.h" +#include "virstring.h" +#include "virtime.h" +#include "virtpm.h" + +#define VIR_FROM_THIS VIR_FROM_QEMU + +VIR_LOG_INIT("qemu.qemu_extdevice") + +static int +qemuExtDeviceLogCommand(qemuDomainLogContextPtr logCtxt, + virCommandPtr cmd, + const char *info) +{ + int ret =3D -1; + char *timestamp =3D NULL; + char *logline =3D NULL; + int logFD; + + logFD =3D qemuDomainLogContextGetWriteFD(logCtxt); + + if ((timestamp =3D virTimeStringNow()) =3D=3D NULL) + goto cleanup; + + if (virAsprintf(&logline, "%s: Starting external device: %s\n", + timestamp, info) < 0) + goto cleanup; + + if (safewrite(logFD, logline, strlen(logline)) < 0) + goto cleanup; + + virCommandWriteArgLog(cmd, logFD); + + ret =3D 0; + + cleanup: + VIR_FREE(timestamp); + VIR_FREE(logline); + + return ret; +} + + +/* + * qemuExtTPMStartEmulator: + * + * @comm: virConnect pointer + * @driver: QEMU driver + * @vm: domain object + * + * Start the external TPM Emulator: + * - have the command line built + * - start the external TPM Emulator and sync with it before QEMU start + */ +static int +qemuExtTPMStartEmulator(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainLogContextPtr logCtxt) +{ + int ret =3D -1; + virCommandPtr cmd =3D NULL; + int exitstatus; + char *errbuf =3D NULL; + virQEMUDriverConfigPtr cfg =3D virQEMUDriverGetConfig(driver); + virDomainDefPtr def =3D vm->def; + unsigned char *vmuuid =3D def->uuid; + virDomainTPMDefPtr tpm =3D def->tpm; + + /* stop any left-over TPM emulator for this VM */ + virTPMStopEmulator(tpm, vmuuid, false); + + if (!(cmd =3D virTPMEmulatorBuildCommand(tpm, vmuuid, cfg->swtpm_user)= )) + goto cleanup; + + if (qemuExtDeviceLogCommand(logCtxt, cmd, "TPM Emulator") < 0) + goto cleanup; + + virCommandSetErrorBuffer(cmd, &errbuf); + + if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus !=3D 0) { + VIR_ERROR("Could not start 'swtpm'. exitstatus: %d\n" + "stderr: %s\n", exitstatus, errbuf); + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not start 'swtpm'. exitstatus: %d, " + "error: %s"), exitstatus, errbuf); + goto error; + } + + /* sync the startup of the swtpm's Unix socket with the start of QEMU = */ + if (virTPMTryConnect(tpm->data.emulator.source.data.nix.path, + 3 * 1000 * 1000) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not connect to the swtpm on '%s'"), + tpm->data.emulator.source.data.nix.path); + goto error; + } + + ret =3D 0; + + cleanup: + VIR_FREE(errbuf); + virCommandFree(cmd); + + virObjectUnref(cfg); + + return ret; + + error: + virTPMStopEmulator(tpm, vmuuid, false); + VIR_FREE(tpm->data.emulator.source.data.nix.path); + + goto cleanup; +} + + +static int +qemuExtTPMStart(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainLogContextPtr logCtxt) +{ + int ret =3D 0; + virDomainTPMDefPtr tpm =3D vm->def->tpm; + + switch (tpm->type) { + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + ret =3D qemuExtTPMStartEmulator(driver, vm, logCtxt); + break; + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + + return ret; +} + +static void +qemuExtTPMStop(virDomainObjPtr vm) +{ + switch (vm->def->tpm->type) { + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + virTPMStopEmulator(vm->def->tpm, vm->def->uuid, false); + break; + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } +} + +int +qemuExtDevicesStart(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainLogContextPtr logCtxt) +{ + int ret =3D 0; + + if (vm->def->tpm) + ret =3D qemuExtTPMStart(driver, vm, logCtxt); + + return ret; +} + +void +qemuExtDevicesStop(virDomainObjPtr vm) +{ + if (vm->def->tpm) + qemuExtTPMStop(vm); +} diff --git a/src/qemu/qemu_extdevice.h b/src/qemu/qemu_extdevice.h new file mode 100644 index 0000000..4dcaec3 --- /dev/null +++ b/src/qemu/qemu_extdevice.h @@ -0,0 +1,36 @@ +/* + * qemu_extdevice.h: QEMU external devices support + * + * Copyright (C) 2014 IBM Corporation + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library. If not, see + * . + * + * Author: Stefan Berger + */ +#ifndef __QEMU_EXTDEVICE_H__ +# define __QEMU_EXTDEVICE_H__ + +# include "qemu_conf.h" +# include "qemu_domain.h" + +int qemuExtDevicesStart(virQEMUDriverPtr driver, + virDomainObjPtr vm, + qemuDomainLogContextPtr logCtxt) + ATTRIBUTE_RETURN_CHECK; + +void qemuExtDevicesStop(virDomainObjPtr vm); + +#endif /* __QEMU_EXTDEVICE_H__ */ + diff --git a/src/qemu/qemu_process.c b/src/qemu/qemu_process.c index 1afb71f..26acfab 100644 --- a/src/qemu/qemu_process.c +++ b/src/qemu/qemu_process.c @@ -47,6 +47,7 @@ #include "qemu_migration.h" #include "qemu_interface.h" #include "qemu_security.h" +#include "qemu_extdevice.h" =20 #include "cpu/cpu.h" #include "datatypes.h" @@ -5952,6 +5953,9 @@ qemuProcessLaunch(virConnectPtr conn, goto cleanup; logfile =3D qemuDomainLogContextGetWriteFD(logCtxt); =20 + if (qemuExtDevicesStart(driver, vm, logCtxt) < 0) + goto cleanup; + VIR_DEBUG("Building emulator command line"); if (!(cmd =3D qemuBuildCommandLine(driver, qemuDomainLogContextGetManager(logCtx= t), @@ -6191,6 +6195,8 @@ qemuProcessLaunch(virConnectPtr conn, ret =3D 0; =20 cleanup: + if (ret) + qemuExtDevicesStop(vm); qemuDomainSecretDestroy(vm); virCommandFree(cmd); virObjectUnref(logCtxt); @@ -6557,6 +6563,8 @@ void qemuProcessStop(virQEMUDriverPtr driver, /* Clear network bandwidth */ virDomainClearNetBandwidth(vm); =20 + qemuExtDevicesStop(vm); + virDomainConfVMNWFilterTeardown(vm); =20 if (cfg->macFilter) { diff --git a/src/qemu/test_libvirtd_qemu.aug.in b/src/qemu/test_libvirtd_qe= mu.aug.in index 688e5b9..03bef74 100644 --- a/src/qemu/test_libvirtd_qemu.aug.in +++ b/src/qemu/test_libvirtd_qemu.aug.in @@ -100,3 +100,4 @@ module Test_libvirtd_qemu =3D { "1" =3D "mount" } } { "memory_backing_dir" =3D "/var/lib/libvirt/qemu/ram" } +{ "swtpm_user" =3D "tss" } diff --git a/src/security/security_dac.c b/src/security/security_dac.c index 663c8c9..351f6f4 100644 --- a/src/security/security_dac.c +++ b/src/security/security_dac.c @@ -1372,6 +1372,11 @@ virSecurityDACSetTPMFileLabel(virSecurityManagerPtr = mgr, &tpm->data.passthrough.source, false); break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + ret =3D virSecurityDACSetChardevLabel(mgr, def, + &tpm->data.emulator.source, + false); + break; case VIR_DOMAIN_TPM_TYPE_LAST: break; } @@ -1393,6 +1398,7 @@ virSecurityDACRestoreTPMFileLabel(virSecurityManagerP= tr mgr, &tpm->data.passthrough.sou= rce, false); break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: case VIR_DOMAIN_TPM_TYPE_LAST: break; } diff --git a/src/security/security_selinux.c b/src/security/security_selinu= x.c index c26cdac..cfc8311 100644 --- a/src/security/security_selinux.c +++ b/src/security/security_selinux.c @@ -1472,6 +1472,12 @@ virSecuritySELinuxSetTPMFileLabel(virSecurityManager= Ptr mgr, return -1; } break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + tpmdev =3D tpm->data.emulator.source.data.nix.path; + rc =3D virSecuritySELinuxSetFilecon(mgr, tpmdev, seclabel->imagela= bel); + if (rc < 0) + return -1; + break; case VIR_DOMAIN_TPM_TYPE_LAST: break; } @@ -1505,6 +1511,11 @@ virSecuritySELinuxRestoreTPMFileLabelInt(virSecurity= ManagerPtr mgr, VIR_FREE(cancel_path); } break; + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + tpmdev =3D tpm->data.emulator.source.data.nix.path; + if (tpmdev) + rc =3D virSecuritySELinuxRestoreFileLabel(mgr, tpmdev); + break; case VIR_DOMAIN_TPM_TYPE_LAST: break; } diff --git a/src/util/virfile.c b/src/util/virfile.c index 5e9bd20..101f071 100644 --- a/src/util/virfile.c +++ b/src/util/virfile.c @@ -38,6 +38,7 @@ #include #include #include +#include #if defined HAVE_MNTENT_H && defined HAVE_GETMNTENT_R # include #endif @@ -3031,6 +3032,17 @@ virFileMakeParentPath(const char *path) return ret; } =20 +static int +_virFileDeletePathCB(const char *fpath, const struct stat *sb ATTRIBUTE_UN= USED, + int typeflag ATTRIBUTE_UNUSED, struct FTW *ftwbuf ATT= RIBUTE_UNUSED) +{ + return remove(fpath); +} + +int virFileDeletePath(const char *path) +{ + return nftw(path, _virFileDeletePathCB, 64, FTW_DEPTH | FTW_PHYS); +} =20 /* Build up a fully qualified path for a config file to be * associated with a persistent guest or network */ diff --git a/src/util/virfile.h b/src/util/virfile.h index cd2a386..e7fa736 100644 --- a/src/util/virfile.h +++ b/src/util/virfile.h @@ -258,6 +258,7 @@ int virFileMakePath(const char *path) ATTRIBUTE_RETURN_= CHECK; int virFileMakePathWithMode(const char *path, mode_t mode) ATTRIBUTE_RETURN_CHECK; int virFileMakeParentPath(const char *path) ATTRIBUTE_RETURN_CHECK; +int virFileDeletePath(const char *path) ATTRIBUTE_RETURN_CHECK; =20 char *virFileBuildPath(const char *dir, const char *name, @@ -353,7 +354,6 @@ int virFileReadValueString(char **value, const char *fo= rmat, ...) =20 int virFileWaitForExists(const char *path, size_t ms, size_t tries); =20 - int virFileInData(int fd, int *inData, long long *length); diff --git a/src/util/virtpm.c b/src/util/virtpm.c index d5c10da..8a99876 100644 --- a/src/util/virtpm.c +++ b/src/util/virtpm.c @@ -22,16 +22,36 @@ =20 #include =20 +#include #include +#include +#include +#include =20 +#include "conf/domain_conf.h" +#include "viralloc.h" +#include "vircommand.h" #include "virstring.h" #include "virerror.h" #include "viralloc.h" #include "virfile.h" +#include "virkmod.h" +#include "virlog.h" #include "virtpm.h" +#include "virutil.h" +#include "configmake.h" =20 #define VIR_FROM_THIS VIR_FROM_NONE =20 +VIR_LOG_INIT("util.tpm") + +/* + * executables for the swtpm; to be found on the host + */ +static char *swtpm_path; +static char *swtpm_setup; +static char *swtpm_ioctl; + /** * virTPMCreateCancelPath: * @devpath: Path to the TPM device @@ -74,3 +94,415 @@ virTPMCreateCancelPath(const char *devpath) cleanup: return path; } + +/* + * virTPMEmulatorInit + * + * Initialize the Emulator functions by searching for necessary + * executables that we will use to start and setup the swtpm + */ +static int +virTPMEmulatorInit(void) +{ + if (!swtpm_path) { + swtpm_path =3D virFindFileInPath("swtpm"); + if (!swtpm_path) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find swtpm 'swtpm' in PATH")); + return -1; + } + if (!virFileIsExecutable(swtpm_path)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("TPM emulator %s is not an executable"), + swtpm_path); + VIR_FREE(swtpm_path); + return -1; + } + } + + if (!swtpm_setup) { + swtpm_setup =3D virFindFileInPath("swtpm_setup"); + if (!swtpm_setup) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find 'swtpm_setup' in PATH")); + return -1; + } + if (!virFileIsExecutable(swtpm_setup)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("'%s' is not an executable"), + swtpm_setup); + VIR_FREE(swtpm_setup); + return -1; + } + } + + if (!swtpm_ioctl) { + swtpm_ioctl =3D virFindFileInPath("swtpm_ioctl"); + if (!swtpm_ioctl) { + virReportError(VIR_ERR_INTERNAL_ERROR, "%s", + _("Could not find swtpm_ioctl in PATH")); + return -1; + } + if (!virFileIsExecutable(swtpm_ioctl)) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("swtpm_ioctl program %s is not an executable"= ), + swtpm_ioctl); + VIR_FREE(swtpm_ioctl); + return -1; + } + } + + return 0; +} + +/* + * virTPMCreateEmulatorStoragePath + * + * @vmuuid: The UUID of the VM for which to create the storage; + * may be NULL + * @suffix: A suffix to append to the storage path; this can be + * used to create a file path + * + * Create the swtpm's storage path + */ +static char * +virTPMCreateEmulatorStoragePath(const unsigned char *vmuuid, + const char *suffix) +{ + char *path =3D NULL; + char uuid[VIR_UUID_STRING_BUFLEN]; + + if (vmuuid) + virUUIDFormat(vmuuid, uuid); + else + uuid[0] =3D '\0'; + + if (virAsprintf(&path, + "%s/lib/libvirt/tpm/%s%s", + LOCALSTATEDIR, uuid, suffix) < 0) + virReportOOMError(); + + return path; +} + +/* + * virTPMEmulatorInitStorage + * + * Initialize the TPM Emulator storage by creating its root directory, + * which is typically found in /var/lib/libvirt/tpm. + * + */ +static int +virTPMEmulatorInitStorage(void) +{ + char *path =3D NULL; + int rc =3D 0; + + if (!(path =3D virTPMCreateEmulatorStoragePath(NULL, ""))) + return -1; + + if (virFileExists(path)) + goto cleanup; + + /* allow others to cd into this dir */ + if (virFileMakePathWithMode(path, 0711) < 0) { + virReportSystemError(errno, + _("Could not create TPM directory %s"), + path); + rc =3D -1; + } + + cleanup: + VIR_FREE(path); + + return rc; +} + +/* + * virTPMCreateEmulatorStorage + * + * @vmuuid: The UUID of the VM + * @created: a pointer to a bool that will be set to true if the + * storage was created because it did not exist yet + * @userid: The userid that needs to be able to access the directory + * + * Unless the storage path for the swtpm for the given VM + * already exists, create it and make it accessible for the given userid. + */ +static char * +virTPMCreateEmulatorStorage(const unsigned char *vmuuid, bool *created, + uid_t swtpm_user) +{ + char *path; + mode_t mode; + + if (virTPMEmulatorInitStorage() < 0) + return NULL; + + *created =3D false; + + if (!(path =3D virTPMCreateEmulatorStoragePath(vmuuid, ""))) + return NULL; + + if (virFileExists(path)) + goto exit; + + *created =3D true; + + mode =3D S_IRUSR | S_IWUSR | S_IXUSR; + + if (virDirCreate(path, mode, swtpm_user, swtpm_user, 0) < 0) { + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not create directory %s as uid %u"), + path, swtpm_user); + VIR_FREE(path); + } + + exit: + return path; +} + +void +virTPMDeleteEmulatorStorage(const unsigned char *vmuuid) +{ + char *path; + + if (!(path =3D virTPMCreateEmulatorStoragePath(vmuuid, ""))) + return; + + ignore_value(virFileDeletePath(path)); + VIR_FREE(path); +} + + +/* + * virTPMCreateEmulatorSocket: + * + * @uuid: the UUID of the VM + * + * Create the vTPM device name from the given parameters + */ +static char * +virTPMCreateEmulatorSocket(unsigned const char *vmuuid) +{ + return virTPMCreateEmulatorStoragePath(vmuuid, ".sock"); +} + +/* + * virTPMTryConnect + * + * @pathname: The device pathname to try to open() + * @timeout_ms: The time in ms to spend trying to connect + * + * Try to connect to the given device pathname using open(). + */ +int +virTPMTryConnect(const char *pathname, unsigned long timeout_ms) +{ + return virFileWaitForExists(pathname, 10, timeout_ms / 10); +} + +/* + * virTPMSetupEmulator + * + * @storagepath: path to the directory for TPM state + * @vmuuid: the UUID of the VM + * @userid: The userid to switch to when setting up the TPM; + * typically this should be 'tss' + * @logfile: The file to write the log into; it must be writable + * for the user given by userid or 'tss' + * + * Setup the external swtpm + */ +static int +virTPMSetupEmulator(const char *storagepath, const unsigned char *vmuuid, + uid_t swtpm_user, const char *logfile) +{ + virCommandPtr cmd =3D NULL; + int exitstatus; + int rc =3D 0; + char uuid[VIR_UUID_STRING_BUFLEN]; + + cmd =3D virCommandNew(swtpm_setup); + if (!cmd) { + rc =3D -1; + goto cleanup; + } + + virUUIDFormat(vmuuid, uuid); + + if (swtpm_user > 0) { + virCommandAddArg(cmd, "--runas"); + virCommandAddArgFormat(cmd, "%u", swtpm_user); + } + virCommandAddArgList(cmd, + "--tpm-state", storagepath, + "--vmid", uuid, + "--logfile", logfile, + "--createek", + "--create-ek-cert", + "--create-platform-cert", + "--lock-nvram", + "--not-overwrite", + NULL); + + virCommandClearCaps(cmd); + + if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus !=3D 0) { + /* copy the log to libvirt error since the log will be deleted */ + char *buffer =3D NULL; + ignore_value(virFileReadAllQuiet(logfile, 10240, &buffer)); + VIR_ERROR(_("Error setting up swtpm:\n%s"), buffer); + VIR_FREE(buffer); + + virReportError(VIR_ERR_INTERNAL_ERROR, + _("Could not run '%s'. exitstatus: %d; " + "please check the libvirt error log"), + swtpm_setup, exitstatus); + rc =3D -1; + } + virCommandFree(cmd); + + cleanup: + + return rc; +} + +/* + * virTPMBuildEmulatorCommand + * + * @tpm: TPM definition + * @vmuuid: The UUID of the VM + * @swtpm_user: The uid for the swtpm to run as (drop privileges to from r= oot) + * + * Create the virCommand use for starting the emulator + * Do some initializations on the way, such as creation of storage + * and emulator setup. + */ +virCommandPtr +virTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, const unsigned char *vm= uuid, + uid_t swtpm_user) +{ + virCommandPtr cmd =3D NULL; + char *storagepath =3D NULL; + char *logfile =3D NULL; + bool created =3D false; + + if (virTPMEmulatorInit() < 0) + return NULL; + + if (!(storagepath =3D virTPMCreateEmulatorStorage(vmuuid, &created, + swtpm_user))) + return NULL; + + /* create logfile in dir where user creating the state will have acces= s */ + if (!(logfile =3D virTPMCreateEmulatorStoragePath(vmuuid, "/vtpm.log")= )) + goto error; + + if (created && + virTPMSetupEmulator(storagepath, vmuuid, swtpm_user, logfile) < 0) + goto error; + + if (!(tpm->data.emulator.source.data.nix.path =3D + virTPMCreateEmulatorSocket(vmuuid))) + goto error; + + unlink(tpm->data.emulator.source.data.nix.path); + + tpm->data.emulator.source.type =3D VIR_DOMAIN_CHR_TYPE_UNIX; + + cmd =3D virCommandNew(swtpm_path); + if (!cmd) + goto error; + + virCommandClearCaps(cmd); + + virCommandAddArgList(cmd, "socket", "--daemon", "--ctrl", NULL); + virCommandAddArgFormat(cmd, "type=3Dunixio,path=3D%s,mode=3D0600", + tpm->data.emulator.source.data.nix.path); + + virCommandAddArg(cmd, "--tpmstate"); + virCommandAddArgFormat(cmd, "dir=3D%s", storagepath); + + virCommandAddArg(cmd, "--log"); + virCommandAddArgFormat(cmd, "file=3D%s", logfile); + + /* allow process to open logfile by root before dropping privileges */ + virCommandAllowCap(cmd, CAP_DAC_OVERRIDE); + + if (swtpm_user > 0) { + virCommandAddArg(cmd, "--runas"); + virCommandAddArgFormat(cmd, "%u", swtpm_user); + virCommandAllowCap(cmd, CAP_SETGID); + virCommandAllowCap(cmd, CAP_SETUID); + } + + VIR_FREE(storagepath); + VIR_FREE(logfile); + + return cmd; + + error: + if (created) + virTPMDeleteEmulatorStorage(vmuuid); + + VIR_FREE(tpm->data.emulator.source.data.nix.path); + VIR_FREE(storagepath); + VIR_FREE(logfile); + + virCommandFree(cmd); + + return NULL; +} + +/* + * virTPMStopEmulator + * @tpm: TPM definition + * @vmuuid: the UUID of the VM + * @verbose: whether to report errors + * + * Gracefully stop the swptm + */ +void +virTPMStopEmulator(virDomainTPMDefPtr tpm, const unsigned char *vmuuid, + bool verbose) +{ + virCommandPtr cmd; + int exitstatus; + char *pathname; + char *errbuf =3D NULL; + + (void)vmuuid; + if (virTPMEmulatorInit() < 0) + return; + + if (!(pathname =3D virTPMCreateEmulatorSocket(vmuuid))) + return; + + cmd =3D virCommandNew(swtpm_ioctl); + if (!cmd) { + VIR_FREE(pathname); + return; + } + + virCommandAddArgList(cmd, "--unix", pathname, "-s", NULL); + + virCommandSetErrorBuffer(cmd, &errbuf); + + if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus !=3D 0) { + if (verbose) + VIR_ERROR(_("Could not run swtpm_ioctl -s '%s'." + " existstatus: %d\nstderr: %s"), + swtpm_ioctl, exitstatus, errbuf); + } + + virCommandFree(cmd); + + /* clean up the socket */ + unlink(pathname); + VIR_FREE(pathname); + + VIR_FREE(tpm->data.emulator.source.data.nix.path); + tpm->data.emulator.source.type =3D 0; + VIR_FREE(errbuf); +} diff --git a/src/util/virtpm.h b/src/util/virtpm.h index b21fc05..424718b 100644 --- a/src/util/virtpm.h +++ b/src/util/virtpm.h @@ -22,6 +22,18 @@ #ifndef __VIR_TPM_H__ # define __VIR_TPM_H__ =20 +# include "vircommand.h" + +typedef struct _virDomainTPMDef virDomainTPMDef; +typedef virDomainTPMDef *virDomainTPMDefPtr; + char *virTPMCreateCancelPath(const char *devpath) ATTRIBUTE_NOINLINE; +virCommandPtr virTPMEmulatorBuildCommand(virDomainTPMDefPtr tpm, + const unsigned char *vmuuid, + uid_t swtpm_user) ATTRIBUTE_RETURN_CHECK; +void virTPMStopEmulator(virDomainTPMDefPtr tpm, const unsigned char *vmuui= d, + bool verbose); +void virTPMDeleteEmulatorStorage(const unsigned char *vmuuid); +int virTPMTryConnect(const char *pathname, unsigned long timeout_ms); =20 #endif /* __VIR_TPM_H__ */ diff --git a/tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml b/tests/qemuc= apabilitiesdata/caps_2.11.0.s390x.xml index 70a35ef..376f58a 100644 --- a/tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.11.0.s390x.xml @@ -150,6 +150,7 @@ + 2011000 0 342058 diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml b/tests/qem= ucapabilitiesdata/caps_2.12.0.aarch64.xml index ff48293..069e0ae 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.aarch64.xml @@ -187,6 +187,7 @@ + 2011090 0 342346 diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml b/tests/qemuc= apabilitiesdata/caps_2.12.0.ppc64.xml index ee7fb9e..46d2463 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.ppc64.xml @@ -185,6 +185,7 @@ + 2011090 0 419215 diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml b/tests/qemuc= apabilitiesdata/caps_2.12.0.s390x.xml index b5b6b5b..36ffd75 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.s390x.xml @@ -150,6 +150,7 @@ + 2011090 0 0 diff --git a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml b/tests/qemu= capabilitiesdata/caps_2.12.0.x86_64.xml index 39ee4f4..b2f06b3 100644 --- a/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml +++ b/tests/qemucapabilitiesdata/caps_2.12.0.x86_64.xml @@ -226,6 +226,7 @@ + 2011090 0 390060 diff --git a/tests/qemuxml2argvdata/tpm-emulator.args b/tests/qemuxml2argvd= ata/tpm-emulator.args new file mode 100644 index 0000000..9418c74 --- /dev/null +++ b/tests/qemuxml2argvdata/tpm-emulator.args @@ -0,0 +1,24 @@ +LC_ALL=3DC \ +PATH=3D/bin \ +HOME=3D/home/test \ +USER=3Dtest \ +LOGNAME=3Dtest \ +QEMU_AUDIO_DRV=3Dnone \ +/usr/bin/qemu-system-x86_64 \ +-name TPM-VM \ +-S \ +-M pc-0.12 \ +-m 2048 \ +-smp 1,sockets=3D1,cores=3D1,threads=3D1 \ +-uuid 11d7cd22-da89-3094-6212-079a48a309a1 \ +-nographic \ +-nodefaults \ +-chardev socket,id=3Dcharmonitor,path=3D/tmp/lib/domain--1-TPM-VM/monitor.= sock,\ +server,nowait \ +-mon chardev=3Dcharmonitor,id=3Dmonitor,mode=3Dreadline \ +-boot c \ +-usb \ +-tpmdev emulator,id=3Dtpm-tpm0,chardev=3Dchrtpm \ +-chardev socket,id=3Dchrtpm,path=3D/dev/test \ +-device tpm-tis,tpmdev=3Dtpm-tpm0,id=3Dtpm0 \ +-device virtio-balloon-pci,id=3Dballoon0,bus=3Dpci.0,addr=3D0x3 diff --git a/tests/qemuxml2argvdata/tpm-emulator.xml b/tests/qemuxml2argvda= ta/tpm-emulator.xml new file mode 100644 index 0000000..2f4e777 --- /dev/null +++ b/tests/qemuxml2argvdata/tpm-emulator.xml @@ -0,0 +1,30 @@ + + TPM-VM + 11d7cd22-da89-3094-6212-079a48a309a1 + 2097152 + 512288 + 1 + + hvm + + + + + + + + destroy + restart + destroy + + /usr/bin/qemu-system-x86_64 + + + + + + + + + + diff --git a/tests/qemuxml2argvmock.c b/tests/qemuxml2argvmock.c index 177b24e..2b970fe 100644 --- a/tests/qemuxml2argvmock.c +++ b/tests/qemuxml2argvmock.c @@ -161,6 +161,7 @@ virNetDevRunEthernetScript(const char *ifname ATTRIBUTE= _UNUSED, return 0; } =20 +#if 0 void virCommandPassFD(virCommandPtr cmd ATTRIBUTE_UNUSED, int fd ATTRIBUTE_UNUSED, @@ -168,6 +169,7 @@ virCommandPassFD(virCommandPtr cmd ATTRIBUTE_UNUSED, { /* nada */ } +#endif =20 uint8_t * virCryptoGenerateRandom(size_t nbytes) diff --git a/tests/qemuxml2argvtest.c b/tests/qemuxml2argvtest.c index 2992197..92846c3 100644 --- a/tests/qemuxml2argvtest.c +++ b/tests/qemuxml2argvtest.c @@ -497,6 +497,19 @@ testCompareXMLToArgv(const void *data) } } =20 + if (vm->def->tpm) { + switch (vm->def->tpm->type) { + case VIR_DOMAIN_TPM_TYPE_EMULATOR: + if (VIR_STRDUP(vm->def->tpm->data.emulator.source.data.file.pat= h, + "/dev/test") < 0) + goto cleanup; + break; + case VIR_DOMAIN_TPM_TYPE_PASSTHROUGH: + case VIR_DOMAIN_TPM_TYPE_LAST: + break; + } + } + if (!(cmd =3D qemuProcessCreatePretendCmd(&driver, vm, migrateURI, (flags & FLAG_FIPS), false, VIR_QEMU_PROCESS_START_COLD)))= { @@ -504,6 +517,7 @@ testCompareXMLToArgv(const void *data) goto ok; goto cleanup; } + if (flags & FLAG_EXPECT_FAILURE) { VIR_TEST_DEBUG("passed instead of expected failure"); goto cleanup; @@ -2139,6 +2153,9 @@ mymain(void) QEMU_CAPS_DEVICE_TPM_CRB); DO_TEST_PARSE_ERROR("tpm-no-backend-invalid", QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE= _TPM_TIS); + DO_TEST("tpm-emulator", + QEMU_CAPS_DEVICE_TPM_PASSTHROUGH, QEMU_CAPS_DEVICE_TPM_EMULATO= R, + QEMU_CAPS_DEVICE_TPM_TIS); =20 =20 DO_TEST_PARSE_ERROR("pci-domain-invalid", NONE); diff --git a/tests/qemuxml2xmloutdata/tpm-emulator.xml b/tests/qemuxml2xmlo= utdata/tpm-emulator.xml new file mode 100644 index 0000000..1f783bb --- /dev/null +++ b/tests/qemuxml2xmloutdata/tpm-emulator.xml @@ -0,0 +1,34 @@ + + TPM-VM + 11d7cd22-da89-3094-6212-079a48a309a1 + 2097152 + 512288 + 1 + + hvm + + + + + + + + destroy + restart + destroy + + /usr/bin/qemu-system-x86_64 + +
+ + + + + + + + +
+ + + diff --git a/tests/qemuxml2xmltest.c b/tests/qemuxml2xmltest.c index 0f56029..b3e7c8e 100644 --- a/tests/qemuxml2xmltest.c +++ b/tests/qemuxml2xmltest.c @@ -700,6 +700,7 @@ mymain(void) DO_TEST("usb-ich9-ehci-addr", NONE); DO_TEST("disk-copy_on_read", NONE); DO_TEST("tpm-passthrough", NONE); + DO_TEST("tpm-emulator", NONE); =20 DO_TEST("metadata", NONE); DO_TEST("metadata-duplicate", NONE); --=20 2.5.5 -- libvir-list mailing list libvir-list@redhat.com https://www.redhat.com/mailman/listinfo/libvir-list