[libvirt] [PATCH 2/6] tpm: Add support for external swtpm TPM emulator
Daniel P. Berrangé
berrange at redhat.com
Fri Apr 6 14:12:39 UTC 2018
On Fri, Apr 06, 2018 at 07:23:49AM -0400, Stefan Berger wrote:
> On 04/06/2018 04:26 AM, Daniel P. Berrangé wrote:
> > On Thu, Apr 05, 2018 at 05:56:02PM -0400, Stefan Berger wrote:
> > > This patch adds support for an external swtpm TPM emulator. The XML for
> > > this type of TPM looks as follows:
> > >
> > > <tpm model='tpm-tis'>
> > > <backend type='emulator'/>
> > > </tpm>
> > > + if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 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;
> > > + }
> > Ewww, this is really not nice to deal with startup races.
> >
> > You're using the --daemon flag to swtpm, so swtpm should make sure
> > that it doesn't daemonize itself & let the parent exit, until the
> > UNIX socket is actually ready to access connections. Can we just
> > get swtpm fixed in this way.
>
> It does that actually already and the socket file is there once it
> daemonizes itself.
If that's the case, why do you need this TryConnect busy waiting loop
at all ? It should be immediately available.
> > > + cmd = virCommandNew(swtpm_path);
> > > + if (!cmd)
> > > + goto error;
> > > +
> > > + virCommandClearCaps(cmd);
> > > +
> > > + virCommandAddArgList(cmd, "socket", "--daemon", "--ctrl", NULL);
> > > + virCommandAddArgFormat(cmd, "type=unixio,path=%s,mode=0600",
> > > + tpm->data.emulator.source.data.nix.path);
> > > +
> > > + virCommandAddArg(cmd, "--tpmstate");
> > > + virCommandAddArgFormat(cmd, "dir=%s", storagepath);
> > > +
> > > + virCommandAddArg(cmd, "--log");
> > > + virCommandAddArgFormat(cmd, "file=%s", logfile);
> > > +
> > > + /* allow process to open logfile by root before dropping privileges */
> > > + virCommandAllowCap(cmd, CAP_DAC_OVERRIDE);
> > Why can't we get have the log file be owned by the user that
> > swtpm runs as, instead of root ?
>
> I would have to look at this particular capability again. I initially wanted
> to put the swtpm's log file also into /var/log/libvirt/qemu. It works nice
> of course when running swtpm as 'root' but not so much when running it as
> 'tss':
>
> root at localhost tmp]$ sudo ls -l /var/log/libvirt/ | grep qemu
> drwx------. 2 root root 20480 Apr 5 16:14 qemu
Yeah the logs are owned by root these days, because they're not written by
qemu itself, instead virtlogd owns it.
> So where do we put the swtpm's log files? /var/log/libvirt/swtpm?
Yeah, probably best to have a separate directory
> Iirc the CAP_DAC_OVERRIDE became necessary when swtpm tries to append to the
> log that 'tss' owns but now libvirt runs it as 'root'. I think that was the
> reason I added it. One way to solve this would be to chown() the files
> before starting swtpm. Is that the solution?
I'd think a separate dir is best
>
>
> >
> > > + if (swtpm_user > 0) {
> > > + virCommandAddArg(cmd, "--runas");
> > > + virCommandAddArgFormat(cmd, "%u", swtpm_user);
> > > + virCommandAllowCap(cmd, CAP_SETGID);
> > > + virCommandAllowCap(cmd, CAP_SETUID);
> > > + }
> > Then we could tell virCommand to set the UID/GID before it even
> > executes this, and not use -runas, thus avoiding the need to
> > allow theses capabilities.
>
> And the directory it writes the logs in would have to have ownership of
> either root:root or tss:root (or tss:tss), depending on how libvirt is
> currently configured? Again chown() the dir before starting?
>
> >
> >
> > > +/*
> > > + * 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 = NULL;
> > > +
> > > + (void)vmuuid;
> > > + if (virTPMEmulatorInit() < 0)
> > > + return;
> > > +
> > > + if (!(pathname = virTPMCreateEmulatorSocket(vmuuid)))
> > > + return;
> > > +
> > > + cmd = virCommandNew(swtpm_ioctl);
> > > + if (!cmd) {
> > > + VIR_FREE(pathname);
> > > + return;
> > > + }
> > > +
> > > + virCommandAddArgList(cmd, "--unix", pathname, "-s", NULL);
> > > +
> > > + virCommandSetErrorBuffer(cmd, &errbuf);
> > > +
> > > + if (virCommandRun(cmd, &exitstatus) < 0 || exitstatus != 0) {
> > > + if (verbose)
> > > + VIR_ERROR(_("Could not run swtpm_ioctl -s '%s'."
> > > + " existstatus: %d\nstderr: %s"),
> > > + swtpm_ioctl, exitstatus, errbuf);
> > > + }
> > > +
> > > + virCommandFree(cmd);
> > I would feel better if we just directly killed the process - with
> > this approach if something goes wrong with swtpm it may never
> > respond to this request and stay running.
>
> swtpm can write a pidfile. I am only adding this later in this series.
> Problem is with --daemon libvirt doesn't know the pid of the swtpm anymore.
The other option is to not use --daemon, and let libvirt write the pid
file, but that introduces the race with socket path creation again
which is not good.
Regards,
Daniel
--
|: https://berrange.com -o- https://www.flickr.com/photos/dberrange :|
|: https://libvirt.org -o- https://fstop138.berrange.com :|
|: https://entangle-photo.org -o- https://www.instagram.com/dberrange :|
More information about the libvir-list
mailing list