Skip to content

Commit

Permalink
CP-40444: Add swtpm-wrapper to the repository
Browse files Browse the repository at this point in the history
Relevant messages from its changelog:

CP-35104: Add swtpm-wrapper for proper logging

CP-35054: Start SWTPM daemon deprivilideged

CP-40195: Only call swtpm_setup for manufacture
swtpm_setup is run as root and handles guest data on subsequent boots
after TPM manufacture. Avoid running it completely after the initial
manufacture to avoid any possible security issues.
The '.lock' will only be present after running swtpm_setup so run chown
conditionally. At the same time, handle errors from chown rather than
silently ignoring them.

CA-369317: Don't ignore errors from swtpm_setup

Signed-off-by: Pau Ruiz Safont <[email protected]>
  • Loading branch information
psafont committed Aug 26, 2022
1 parent ed51796 commit 3ecb5bd
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 0 deletions.
1 change: 1 addition & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ install: build doc sdk doc-json
install -D ./ocaml/xenopsd/scripts/common.py $(DESTDIR)/$(XENOPSD_LIBEXECDIR)/common.py
install -D ./ocaml/xenopsd/scripts/igmp_query_injector.py $(DESTDIR)/$(XENOPSD_LIBEXECDIR)/igmp_query_injector.py
install -D ./ocaml/xenopsd/scripts/qemu-wrapper $(DESTDIR)/$(QEMU_WRAPPER_DIR)/qemu-wrapper
install -D ./ocaml/xenopsd/scripts/swtpm-wrapper $(DESTDIR)/$(QEMU_WRAPPER_DIR)/swtpm-wrapper
DESTDIR=$(DESTDIR) SBINDIR=$(SBINDIR) QEMU_WRAPPER_DIR=$(QEMU_WRAPPER_DIR) XENOPSD_LIBEXECDIR=$(XENOPSD_LIBEXECDIR) ETCDIR=$(ETCDIR) ./ocaml/xenopsd/scripts/make-custom-xenopsd.conf
# squeezed
install -D _build/install/default/bin/squeezed $(DESTDIR)/$(SBINDIR)/squeezed
Expand Down
168 changes: 168 additions & 0 deletions ocaml/xenopsd/scripts/swtpm-wrapper
Original file line number Diff line number Diff line change
@@ -0,0 +1,168 @@
#! /usr/bin/python
#
# Copyright (C) 2022 Citrix Systems R&D Ltd.
#
# This program 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; version 2.1 only. with the special
# exception on linking described in file LICENSE.
#
# This program 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.

from __future__ import print_function
import os
import stat
import sys
import pwd
import grp
import subprocess
import ctypes
import ctypes.util
from resource import getrlimit, RLIMIT_CORE, RLIMIT_FSIZE, setrlimit


STATE_FILE = 'tpm2-00.permall'

CLONE_NEWNS = 0x00020000 # mount namespace
CLONE_NEWNET = 0x40000000 # network namespace
CLONE_NEWIPC = 0x08000000 # IPC namespace

# Set cgroup_slice to the name of the cgroup slice the swtpm process
# should live in.
# - None means leave in the same slice as the parent process.
# - '' means move it into the default slice.
# - 'system.slice' means move it into the system slice, etc.
# If the nominated slice does not already exist, the process will be
# left in its parent's slice.
cgroup_slice = ''

def unshare(flags):
libc = ctypes.CDLL(ctypes.util.find_library('c'), use_errno=True)
unshare_prototype = ctypes.CFUNCTYPE(ctypes.c_int, ctypes.c_int, use_errno=True)
unshare = unshare_prototype(('unshare', libc))
ret = unshare(flags)
if ret < 0:
raise OSError(ctypes.get_errno(), os.strerror(ctypes.get_errno()))

def enable_core_dumps():

limit = 64 * 1024 * 1024
oldlimits = getrlimit(RLIMIT_CORE)
hardlimit = oldlimits[1]
if limit > hardlimit:
hardlimit = limit
setrlimit(RLIMIT_CORE, (limit, hardlimit))
return limit

def prepare_exec():
"""Set up the execution environment for SWTPM."""

if cgroup_slice is not None:
# Move to nominated cgroup slice
print("Moving to cgroup slice '%s'" % cgroup_slice)
try:
# Note the default slice uses /sys/fs/cgroup/cpu/tasks but
# other.slice uses /sys/fs/cgroup/cpu/other.slice/tasks.
g = open("/sys/fs/cgroup/cpu/%s/tasks" % cgroup_slice, 'w')
g.write(str(os.getpid()))
g.close()
except IOError as e:
print("Warning: writing pid to '%s' tasks file: %s" \
% (cgroup_slice, e))

core_dump_limit = enable_core_dumps()
print("core dump limit: %d" % core_dump_limit)

limit = 256 * 1024
setrlimit(RLIMIT_FSIZE, (limit, limit))

flags = CLONE_NEWNS | CLONE_NEWIPC | CLONE_NEWNET
unshare(flags)

sys.stdout.flush()
sys.stderr.flush()

def main(argv):
print("Arguments: %s" % " ".join(argv[1:]))

if len(argv) < 3:
return

domid = int(argv[1])
tpm_dir = argv[2]
tpm_path = tpm_dir
depriv = True

n= 3
while n < len(argv):
if argv[n] == "-priv":
depriv = False
continue
n += 1

tpm_env = dict(os.environ)
tpm_env["LD_LIBRARY_PATH"] = "/usr/lib:"

if not os.path.exists(os.path.join(tpm_dir, STATE_FILE)):
# Initial manufacture

tpm_exe = '/usr/bin/swtpm_setup'
tpm_args = ["swtpm_setup", "--tpm2", "--tpm-state", tpm_dir, "--createek", "--create-ek-cert", "--create-platform-cert", "--lock-nvram", "--not-overwrite"]
subprocess.check_call(tpm_args, executable=tpm_exe, env=tpm_env)

tpm_exe = '/usr/bin/swtpm'
uid = pwd.getpwnam('swtpm_base').pw_uid + domid
tpm_args = []

if depriv:
tpm_args = ["--chroot", tpm_dir,
"--runas", str(uid)]
try:
dev_dir = os.path.join(tpm_dir, "dev")
if not os.path.isdir(dev_dir):
os.mkdir(dev_dir)

urandom = os.path.join(dev_dir, "urandom")
if not os.path.exists(urandom):
os.mknod(urandom, 0666 | stat.S_IFCHR, os.makedev(1, 9))

os.chown(tpm_dir, uid, uid)
if os.path.exists(os.path.join(tpm_dir, ".lock")):
os.chown(os.path.join(tpm_dir, ".lock"), uid, uid)
os.chown(os.path.join(tpm_dir, STATE_FILE), uid, uid)

except OSError as error:
print(error)
return

tpm_path = '/'

swtpm_sock = os.path.join(tpm_path, "swtpm-sock")
swtpm_pid = os.path.join(tpm_path, "swtpm-%d.pid" % domid)

tpm_args = ["swtpm-%d" % domid, "socket",
"--tpm2",
"--tpmstate", "dir=%s" % tpm_path,
"--ctrl", "type=unixio,path=%s" % swtpm_sock,
"--log", "level=1",
"--pid", "file=%s" % swtpm_pid,
"-t"] + tpm_args

swtpm = subprocess.Popen(tpm_args,executable=tpm_exe, preexec_fn=prepare_exec(), env=tpm_env, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
print("Exec: %s %s" % (tpm_exe, " ".join(tpm_args)))

sys.stdout.flush()
sys.stderr.flush()

# Redirect output from SWTPM to logger
os.dup2(swtpm.stdout.fileno(), 0)
swtpm.stdout.close()

os.execvp('logger', ['logger', '-p', 'daemon.info', '-t',
'swtpm-%d[%d]' % (domid, swtpm.pid)])

if __name__ == '__main__':
raise SystemExit(main(sys.argv))

0 comments on commit 3ecb5bd

Please sign in to comment.