swtpm_setup: Get rid of 'c' code and support changing user in python

Get rid of the 'c' code that only changed the user and add for support of
the --runas option to change to a different user in the python part.

To get 'make distcheck' to work I needed to name the swtpm_setup python
script with the suffix .in so that it gets copied to the build directory
as swtpm_setup. We need to change execute permissions on this file after
copying.

Signed-off-by: Stefan Berger <stefanb@linux.ibm.com>
This commit is contained in:
Stefan Berger 2020-09-08 09:27:10 -04:00 committed by Stefan Berger
parent 094dba930f
commit 3064a72ff0
14 changed files with 56 additions and 344 deletions

1
.gitignore vendored
View File

@ -55,7 +55,6 @@ Makefile
/src/swtpm_cert/swtpm_cert
/src/swtpm_ioctl/swtpm_ioctl
/src/swtpm_setup/dist
/src/swtpm_setup/swtpm_setup
/src/swtpm_setup/py_swtpm_setup/swtpm_setup_conf.py
/test-driver
tests/*.log

View File

@ -563,9 +563,10 @@ AC_CONFIG_FILES([Makefile \
])
AC_CONFIG_FILES([samples/swtpm-localca],
[chmod 755 samples/swtpm-localca])
AC_CONFIG_FILES([src/swtpm_setup/swtpm_setup],
[chmod 755 src/swtpm_setup/swtpm_setup])
dnl for out-of-tree builds:
AC_CONFIG_LINKS([src/swtpm_setup/swtpm_setup.sh:src/swtpm_setup/swtpm_setup.sh \
src/swtpm_setup/py_swtpm_setup/__init__.py:src/swtpm_setup/py_swtpm_setup/__init__.py \
AC_CONFIG_LINKS([src/swtpm_setup/py_swtpm_setup/__init__.py:src/swtpm_setup/py_swtpm_setup/__init__.py \
src/swtpm_setup/py_swtpm_setup/swtpm.py:src/swtpm_setup/py_swtpm_setup/swtpm.py \
src/swtpm_setup/py_swtpm_setup/swtpm_utils.py:src/swtpm_setup/py_swtpm_setup/swtpm_utils.py \
src/swtpm_setup/py_swtpm_setup/swtpm_setup.py:src/swtpm_setup/py_swtpm_setup/swtpm_setup.py])

View File

@ -4,12 +4,7 @@
# For the license, see the LICENSE file in the root directory.
#
bin_PROGRAMS = \
swtpm_setup
swtpm_setup_SOURCES = swtpm_setup.c
dist_bin_SCRIPTS = swtpm_setup.sh
dist_bin_SCRIPTS = swtpm_setup
PY_SWTPM_SETUP_FILES = $(wildcard py_swtpm_setup/*.py)
@ -24,13 +19,6 @@ $(PY_PACKAGE): $(PY_SWTPM_SETUP_FILES)
all-local: $(PY_PACKAGE)
# for in-tree testing on Cygwin we need to copy swtpm_setup.exe
# for it to find swtpm_setup.sh
all:
@if test "$(host_os)" = "cygwin"; then \
$(CP) -f .libs/swtpm_setup.exe ./ ; \
fi
install-exec-local: $(PY_PACKAGE)
@if ! test $(findstring /usr, "$(DESTDIR)$(bindir)"); then \
echo "Warning: Not installing python package to $(DESTDIR)$(bindir)"; \

View File

@ -10,7 +10,8 @@ A tool for simulating the manufacturing of a TPM 1.2 or 2.0
# R0914: Too many local variables (21/15) (too-many-locals)
# R0101: Too many nested blocks (6/5) (too-many-nested-blocks)
# W0703: Catching too general exception Exception (broad-except)
# pylint: disable=W0703,R0913,R0914,R0912,R0101
# C0302: Too many lines in module (1032/1000) (too-many-lines)
# pylint: disable=W0703,R0913,R0914,R0912,R0101,C0302
#
# swtpm_setup.py
@ -28,6 +29,7 @@ import glob
import grp
import json
import os
import pwd
import re
import subprocess
import sys
@ -651,6 +653,41 @@ def print_capabilities(swtpm_prg_l):
return 0
def change_process_owner(user):
""" change the process owner to the given one """
if not user.isnumeric():
try:
passwd = pwd.getpwnam(user)
except KeyError:
logerr(LOGFILE, "Error: User '%s' does not exist.\n" % user)
return 1
try:
os.initgroups(passwd.pw_name, passwd.pw_gid)
except PermissionError as err:
logerr(LOGFILE, "Error: initgroups() failed: %s\n" % str(err))
return 1
gid = passwd.pw_gid
uid = passwd.pw_uid
else:
if int(user) > 0xffffffff:
logerr(LOGFILE, "Error: uid %s outside valid range.\n" % user)
gid = int(user)
uid = int(user)
try:
os.setgid(gid)
except PermissionError as err:
logerr(LOGFILE, "Error: setgid(%d) failed: %s\n" % (gid, str(err)))
return 1
try:
os.setuid(uid)
except PermissionError as err:
logerr(LOGFILE, "Error: setuid(%d) failed: %s\n" % (uid, str(err)))
return 1
return 0
# pylint: disable=R0915
def main():
@ -721,6 +758,7 @@ def main():
rsa_keysize_str = "%d" % DEFAULT_RSA_KEYSIZE
swtpm_keyopt = ""
fds_to_pass = []
runas = ""
for opt, arg in opts:
if opt in ['--tpm-state', '--tpmstate']:
@ -772,8 +810,7 @@ def main():
elif opt == '--cipher':
cipher = arg
elif opt == '--runas':
# ignored here
pass
runas = arg
elif opt == '--logfile':
LOGFILE = arg
elif opt == '--overwrite':
@ -816,6 +853,11 @@ def main():
ret = print_capabilities(swtpm_prg_l)
sys.exit(ret)
if runas:
ret = change_process_owner(runas)
if ret != 0:
sys.exit(1)
if not got_ownerpass:
flags |= SETUP_OWNERPASS_ZEROS_F
if not got_srkpass:

View File

@ -1,301 +0,0 @@
/*
* swtpm_setup.c
*
* Authors: Stefan Berger <stefanb@us.ibm.com>
*
* (c) Copyright IBM Corporation 2011,2014,2015.
*
* All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* Neither the names of the IBM Corporation nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <stdbool.h>
#include <string.h>
#include <errno.h>
#include <limits.h>
#include <libgen.h>
#include <sys/types.h>
#include <pwd.h>
#include <grp.h>
#include <fcntl.h>
#if defined __APPLE__
#include <sys/mount.h>
#include <mach-o/dyld.h>
#endif
/*
* Those parameters interpreted by swtpm_setup.sh that have an additional
* parameter.
*/
const char *one_arg_params[] = {
"--tpm-state",
"--tpmstate",
"--tpm",
"--ownerpass",
"--srkpass",
"--config",
"--vmid",
"--logfile",
"--keyfile",
"--pwdfile",
"--swtpm_ioctl",
"--pcr-banks",
"--tcsd-system-ps-file",
"--rsa-keysize",
NULL
};
/*
* Those parameters interpreted by swtpm_setup.sh that have a file descriptor
* parameter.
*/
const char *fd_arg_params[] = {
"--keyfile-fd",
"--pwdfile-fd",
NULL
};
/*
* Sanitize the file descriptor we pass to swtpm_setup.sh so that it can
* freely use any fds in the range [100..109], e.g. 'exec 100 ... '.
*/
static int move_reserved_fd(const char *fdstring, char **newfdstring)
{
char *endptr;
long fd;
int newfd;
errno = 0;
fd = strtol(fdstring, &endptr, 10);
if (fdstring == endptr || *endptr != '\0' || fd < 0 || errno != 0) {
fprintf(stderr, "Invalid file descriptor '%s'.\n", fdstring);
return -1;
}
/* reserve file descriptors 100 - 109 for swtpm_setup.sh to use */
if (fd >= 100 && fd <= 109) {
newfd = fcntl(fd, F_DUPFD, 3);
if (newfd < 0) {
fprintf(stderr, "F_DUPFD failed: %s\n", strerror(errno));
return -1;
}
if (newfd >= 100 && newfd <= 109) {
fprintf(stderr, "newfd is also in reserved range: %u\n", newfd);
return -1;
}
close(fd);
if (asprintf(newfdstring, "%u", newfd) < 0) {
fprintf(stderr, "Out of memory\n");
return -1;
}
return 1;
}
return 0;
}
static int change_process_owner(const char *user)
{
struct passwd *passwd;
long int uid, gid;
char *endptr = NULL;
uid = strtoul(user, &endptr, 10);
if (*endptr != '\0') {
/* a string */
passwd = getpwnam(user);
if (!passwd) {
fprintf(stderr, "Error: User '%s' does not exist.\n", user);
return -1;
}
if (initgroups(passwd->pw_name, passwd->pw_gid) < 0) {
fprintf(stderr, "Error: initgroups(%s, %d) failed.\n",
passwd->pw_name, passwd->pw_gid);
return -1;
}
gid = passwd->pw_gid;
uid = passwd->pw_uid;
} else {
/* an integer */
if ((unsigned long int)uid > UINT_MAX) {
fprintf(stderr, "Error: uid %s outside valid range.\n", user);
return -1;
}
gid = uid;
}
if (setgid(gid) < 0) {
fprintf(stderr, "Error: setgid(%ld) failed.\n", gid);
return -1;
}
if (setuid(uid) < 0) {
fprintf(stderr, "Error: setuid(%ld) failed.\n", uid);
return -1;
}
return 0;
}
int main(int argc, char *argv[])
{
const char *program = "swtpm_setup.sh";
char resolved_path[PATH_MAX];
char *dir;
char *path_program;
size_t length;
struct passwd *passwd = NULL;
int i = 1, j;
const char *userid = NULL;
bool change_user = true;
const char *p;
#if defined __APPLE__
char path[MAXPATHLEN];
uint32_t pathlen = sizeof(path);
#endif
char *newargv;
int rc;
while (i < argc) {
if (!strcmp("--runas", argv[i])) {
i++;
if (i == argc) {
fprintf(stderr, "Missing user argument for --runas\n");
exit(1);
}
userid = argv[i];
} else if (!strcmp("--help", argv[i]) || !strcmp("-h", argv[i])) {
change_user = false;
} else if (!strcmp("--version", argv[i])) {
change_user = false;
} else if (!strcmp("--print-capabilities", argv[i])) {
change_user = false;
}
for (j = 0; one_arg_params[j] != NULL; j++) {
if (!strcmp(one_arg_params[j], argv[i])) {
i++;
goto skip;
}
}
/* Ensure that no file descriptor overlaps with those reserved
* for free use by swtpm_setup.sh
*/
for (j = 0; fd_arg_params[j] != NULL; j++) {
if (!strcmp(fd_arg_params[j], argv[i]) &&
i + 1 < argc) {
i++;
rc = move_reserved_fd(argv[i], &newargv);
switch (rc) {
case 0:
/* nothing to do */
break;
case 1:
argv[i] = newargv;
break;
default:
return EXIT_FAILURE;
}
break;
}
}
skip:
i++;
}
#if defined __OpenBSD__ || defined __FreeBSD__ || defined __DragonFly__
p = getenv("_");
#elif defined __APPLE__
if (_NSGetExecutablePath(path, &pathlen) < 0) {
fprintf(stderr, "Could not get path of 'self'.");
return EXIT_FAILURE;
}
p = path;
#else
p = "/proc/self/exe";
#endif
if (!realpath(p, resolved_path)) {
fprintf(stderr, "Could not resolve path (%s) to executable: %s\n",
p, strerror(errno));
return EXIT_FAILURE;
}
dir = dirname(resolved_path);
if (!dir) {
fprintf(stderr, "Could not get directory from path '%s'.",
resolved_path);
return EXIT_FAILURE;
}
length = strlen(dir) + 1 + strlen(program) + 2;
path_program = malloc(length);
if (!path_program) {
fprintf(stderr, "Out of memory.\n");
goto exit_failure;
}
if (snprintf(path_program, length, "%s/%s", dir, program) >=
(int)length) {
fprintf(stderr, "Internal error writing string.\n");
goto exit_failure;
}
/*
* Unless we saw --runas, we will not attempt to switch the user.
*/
if (!userid)
change_user = false;
if (change_user && change_process_owner(userid))
goto exit_failure;
/*
* need to pass unmodified argv to swtpm_setup.sh
*/
execv(path_program, argv);
if (passwd) {
/* should never get here */
fprintf(stderr, "As user %s:", passwd->pw_name);
}
fprintf(stderr, "Could not execute '%s' : %s\n",
path_program, strerror(errno));
exit_failure:
free(path_program);
return EXIT_FAILURE;
}

7
src/swtpm_setup/swtpm_setup.in Executable file
View File

@ -0,0 +1,7 @@
#!/usr/bin/env python3
""" Launcher for swtpm_setup
"""
from py_swtpm_setup.swtpm_setup import main
main()

View File

@ -1,10 +0,0 @@
#!/usr/bin/env bash
python3 -c "
import sys;
from py_swtpm_setup.swtpm_setup import main
sys.argv.pop(0)
sys.argv[0]='$0'
main()" \
- "$@"

View File

@ -6,8 +6,6 @@
ROOT=${abs_top_builddir:-$(pwd)/..}
TESTDIR=${abs_top_testdir:-$(dirname "$0")}
# We need to be able to find swtpm_setup.py and swtpm_setup.py needs to find swtpm
export PYTHONPATH=$ROOT/src/swtpm_setup
PATH=$ROOT/src/swtpm:$PATH
[ "${SWTPM_IFACE}" == "cuse" ] && source ${TESTDIR}/test_cuse

View File

@ -6,8 +6,6 @@
ROOT=${abs_top_builddir:-$(pwd)/..}
TESTDIR=${abs_top_testdir:-$(dirname "$0")}
# We need to be able to find swtpm_setup.py and swtpm_setup.py needs to find swtpm
export PYTHONPATH=$ROOT/src/swtpm_setup
PATH=$ROOT/src/swtpm:$PATH
[ "${SWTPM_IFACE}" == "cuse" ] && source ${TESTDIR}/test_cuse

View File

@ -6,8 +6,6 @@ ROOT=${abs_top_builddir:-$(dirname "$0")/..}
TESTDIR=${abs_top_testdir:=$(dirname "$0")}
SRCDIR=${abs_top_srcdir:-$(dirname "$0")/..}
# We need to be able to find swtpm_setup.py and swtpm_setup.py needs to find swtpm
export PYTHONPATH=$ROOT/src/swtpm_setup
PATH=$ROOT/src/swtpm:$PATH
PARAMETERS=(

View File

@ -18,8 +18,6 @@ ROOT=${abs_top_builddir:-$(dirname "$0")/..}
TESTDIR=${abs_top_testdir:=$(dirname "$0")}
SRCDIR=${abs_top_srcdir:-$(dirname "$0")/..}
# We need to be able to find swtpm_setup.py and swtpm_setup.py needs to find swtpm
export PYTHONPATH=$ROOT/src/swtpm_setup
PATH=$ROOT/src/swtpm:$PATH
source ${abs_top_builddir:-$(dirname "$0")/..}/tests/test_config

View File

@ -7,8 +7,6 @@ ROOT=${abs_top_builddir:-$(dirname "$0")/..}
TESTDIR=${abs_top_testdir:=$(dirname "$0")}
SRCDIR=${abs_top_srcdir:-$(dirname "$0")/..}
# We need to be able to find swtpm_setup.py and swtpm_setup.py needs to find swtpm
export PYTHONPATH=$ROOT/src/swtpm_setup
PATH=$ROOT/src/swtpm:$PATH
SWTPM_SETUP=${ROOT}/src/swtpm_setup/swtpm_setup

View File

@ -6,8 +6,6 @@ ROOT=${abs_top_builddir:-$(dirname "$0")/..}
TESTDIR=${abs_top_testdir:-$(dirname "$0")}
SRCDIR=${abs_top_srcdir:-$(dirname "$0")/..}
# We need to be able to find swtpm_setup.py and swtpm_setup.py needs to find swtpm
export PYTHONPATH=$ROOT/src/swtpm_setup
PATH=$ROOT/src/swtpm:$PATH
source ${abs_top_builddir:-$(dirname "$0")/..}/tests/test_config

View File

@ -6,8 +6,6 @@ TOPBUILD=${abs_top_builddir:-$(dirname "$0")/..}
TOPSRC=${abs_top_srcdir:-$(dirname "$0")/..}
TESTDIR=${abs_top_testdir:-$(dirname "$0")}
# We need to be able to find swtpm_setup.py and swtpm_setup.py needs to find swtpm
export PYTHONPATH=${TOPBUILD}/src/swtpm_setup
PATH=${TOPBUILD}/src/swtpm:$PATH
SWTPM_SETUP=${TOPBUILD}/src/swtpm_setup/swtpm_setup