BuildRequires: automake autoconf bash coreutils libtool sed
BuildRequires: libtpms-devel fuse-devel glib2-devel gmp-devel
-BuildRequires: expect bash net-tools nss-devel
+BuildRequires: expect bash net-tools nss-devel socat
%if %{with_gnutls}
BuildRequires: gnutls >= 3.1.0 gnutls-devel
BuildRequires: libtasn1-devel libtasn1 kernel-modules-extra
BuildRequires: automake autoconf bash coreutils libtool sed
BuildRequires: libtpms-devel fuse-devel glib2-devel gmp-devel
-BuildRequires: expect bash net-tools nss-devel
+BuildRequires: expect bash net-tools nss-devel socat
%if %{with_gnutls}
BuildRequires: gnutls >= 3.1.0 gnutls-devel
BuildRequires: libtasn1-devel libtasn1 kernel-modules-extra
PTM_GET_CONFIG = _IOR('P', 14, ptm_getconfig),
};
+/*
+ * Comments used by the non-CUSE TPMs
+ */
+enum {
+ CMD_GET_CAPABILITY = 1,
+ CMD_INIT,
+};
+
#endif /* _TPM_IOCTL_H */
.IX Item "--pid file=<pidfile>"
This options allows to set the name of file where the process \s-1ID \s0(pid) of the \s-1TPM\s0
will be written into.
+.IP "\fB\-\-ctrl type=[unixio|tcp][,path=<path>,[port=<port>][,fd=<filedescriptor>] \fR" 4
+.IX Item "--ctrl type=[unixio|tcp][,path=<path>,[port=<port>][,fd=<filedescriptor>] "
+This option adds a control channel to the \s-1TPM.\s0 The control channel can either use a UnixIO socket with
+a given \fIpath\fR or \fIfiledescriptor\fR or it can use a \s-1TCP\s0 socket on the given \fIport\fR or \fIfiledescriptor\fR.
+.Sp
+The control channel enables out-of-band control of the \s-1TPM,\s0 such as resetting the \s-1TPM.\s0
.IP "\fB\-h|\-\-help\fR" 4
.IX Item "-h|--help"
Display usage info.
This options allows to set the name of file where the process ID (pid) of the TPM
will be written into.
+=item B<--ctrl type=[unixio|tcp][,path=E<lt>pathE<gt>,[port=E<lt>portE<gt>][,fd=E<lt>filedescriptorE<gt>] >
+
+This option adds a control channel to the TPM. The control channel can either use a UnixIO socket with
+a given I<path> or I<filedescriptor> or it can use a TCP socket on the given I<port> or I<filedescriptor>.
+
+The control channel enables out-of-band control of the TPM, such as resetting the TPM.
+
=item B<-h|--help>
Display usage info.
noinst_HEADERS = \
common.h \
+ ctrlchannel.h \
key.h \
logging.h \
main.h \
libswtpm_libtpms_la_SOURCES = \
common.c \
+ ctrlchannel.c \
key.c \
logging.c \
options.c \
#include <sys/types.h>
#include <pwd.h>
#include <stdlib.h>
+#include <sys/stat.h>
+#include <sys/un.h>
+#include <netinet/in.h>
+#include <sys/socket.h>
#include <libtpms/tpm_error.h>
#include "swtpm_nvfile.h"
#include "pidfile.h"
#include "tpmstate.h"
+#include "ctrlchannel.h"
/* --log %s */
static const OptionDesc logging_opt_desc[] = {
END_OPTION_DESC
};
+static const OptionDesc ctrl_opt_desc[] = {
+ {
+ .name = "type",
+ .type = OPT_TYPE_STRING,
+ },
+ {
+ .name = "path",
+ .type = OPT_TYPE_STRING,
+ },
+ {
+ .name = "port",
+ .type = OPT_TYPE_INT,
+ },
+ {
+ .name = "fd",
+ .type = OPT_TYPE_INT,
+ },
+ END_OPTION_DESC
+};
+
/*
* handle_log_options:
* Parse and act upon the parsed log options. Initialize the logging.
return 0;
}
+
+/*
+ * unixio_open_socket: Open a UnixIO socket and return file descriptor
+ *
+ * @path: UnixIO socket path
+ * @perm: UnixIO socket permissions
+ */
+static int unixio_open_socket(const char *path, mode_t perm)
+{
+ struct sockaddr_un su;
+ int fd = -1, n;
+ size_t len;
+
+ su.sun_family = AF_UNIX;
+ len = sizeof(su.sun_path);
+ n = snprintf(su.sun_path, len, "%s", path);
+ if (n < 0) {
+ fprintf(stderr, "Could not nsprintf path to UnixIO socket\n");
+ return -1;
+ }
+ if (n >= (int)len) {
+ fprintf(stderr, "Path for UnioIO socket is too long\n");
+ return -1;
+ }
+
+ unlink(su.sun_path);
+
+ fd = socket(AF_UNIX, SOCK_STREAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "Could not open UnixIO socket\n");
+ return -1;
+ }
+
+ len = strlen(su.sun_path) + sizeof(su.sun_family);
+ n = bind(fd, (struct sockaddr *)&su, len);
+ if (n < 0) {
+ fprintf(stderr, "Could not open UnixIO socket: %s\n",
+ strerror(errno));
+ goto error;
+ }
+
+ if (chmod(su.sun_path, perm) < 0) {
+ fprintf(stderr,
+ "Could not change permssions on UnixIO socket: %s\n",
+ strerror(errno));
+ goto error;
+ }
+
+ n = listen(fd, 1);
+ if (n < 0) {
+ fprintf(stderr, "Cannot listen on UnixIO socket: %s\n",
+ strerror(errno));
+ goto error;
+ }
+
+ return fd;
+
+error:
+ close(fd);
+
+ return -1;
+}
+
+/*
+ * tcp_open_socket: Open a TCP port and return the file descriptor
+ *
+ * @port: port number
+ */
+static int tcp_open_socket(unsigned short port)
+{
+ int fd = -1, n;
+ struct sockaddr_in si;
+
+ fd = socket(AF_INET, SOCK_STREAM, 0);
+ if (fd < 0) {
+ fprintf(stderr, "Could not open TCP socket\n");
+ return -1;
+ }
+
+ si.sin_family = AF_INET;
+ si.sin_addr.s_addr = htonl(INADDR_LOOPBACK);
+ si.sin_port = htons(port);
+
+ n = bind(fd, (struct sockaddr *)&si, sizeof(si));
+ if (n < 0) {
+ fprintf(stderr, "Could not open TCP socket: %s\n",
+ strerror(errno));
+ goto error;
+ }
+
+ n = listen(fd, 1);
+ if (n < 0) {
+ fprintf(stderr, "Cannot listen on TCP socket: %s\n",
+ strerror(errno));
+ goto error;
+ }
+
+ return fd;
+
+error:
+ close(fd);
+
+ return -1;
+}
+
+/*
+ * parse_ctrlchannel_options:
+ * Parse the 'ctrl' (control channel) options.
+ *
+ * @options: the control channel options to parse
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+static int parse_ctrlchannel_options(char *options, struct ctrlchannel **cc)
+{
+ OptionValues *ovs = NULL;
+ char *error = NULL;
+ const char *type, *path;
+ int fd, port;
+ struct stat stat;
+
+ ovs = options_parse(options, ctrl_opt_desc, &error);
+ if (!ovs) {
+ fprintf(stderr, "Error parsing ctrl options: %s\n", error);
+ goto error;
+ }
+
+ type = option_get_string(ovs, "type", NULL);
+ if (!type) {
+ fprintf(stderr, "Missing type parameter for control channel\n");
+ goto error;
+ }
+
+ if (!strcmp(type, "unixio")) {
+ path = option_get_string(ovs, "path", NULL);
+ fd = option_get_int(ovs, "fd", -1);
+ if (fd >= 0) {
+ if (fstat(fd, &stat) < 0 || !S_ISSOCK(stat.st_mode)) {
+ fprintf(stderr,
+ "Bad filedescriptor %d for UnixIO control channel\n",
+ fd);
+ goto error;
+ }
+
+ *cc = ctrlchannel_new(fd);
+ } else if (path) {
+ fd = unixio_open_socket(path, 0770);
+ if (fd < 0)
+ goto error;
+
+ *cc = ctrlchannel_new(fd);
+ } else {
+ fprintf(stderr,
+ "Missing path and fd options for UnixIO control channel\n");
+ goto error;
+ }
+ } else if (!strcmp(type, "tcp")) {
+ port = option_get_int(ovs, "port", -1);
+ fd = option_get_int(ovs, "fd", -1);
+ if (fd >= 0) {
+ if (fstat(fd, &stat) < 0 || !S_ISSOCK(stat.st_mode)) {
+ fprintf(stderr,
+ "Bad filedescriptor %d for TCP control channel\n", fd);
+ goto error;
+ }
+
+ *cc = ctrlchannel_new(fd);
+ } else if (port >= 0) {
+ if (port >= 0x10000) {
+ fprintf(stderr,
+ "TCP control channel port outside valid range\n");
+ goto error;
+ }
+
+ fd = tcp_open_socket(port);
+ if (fd < 0)
+ goto error;
+
+ *cc = ctrlchannel_new(fd);
+ } else {
+ fprintf(stderr,
+ "Missing port and fd options for TCP control channel\n");
+ goto error;
+ }
+ } else {
+ fprintf(stderr, "Unsupport control channel type: %s\n", type);
+ goto error;
+ }
+
+ if (*cc == NULL)
+ goto error;
+
+ option_values_free(ovs);
+
+ return 0;
+
+error:
+ option_values_free(ovs);
+
+ return -1;
+}
+
+/*
+ * handle_ctrlchannel_options:
+ * Parse and act upon the parsed 'ctrl' (control channel) options.
+ *
+ * @options: the control channel options to parse
+ *
+ * Returns 0 on success, -1 on failure.
+ */
+int handle_ctrlchannel_options(char *options, struct ctrlchannel **cc)
+{
+ if (!options)
+ return 0;
+
+ if (parse_ctrlchannel_options(options, cc) < 0)
+ return -1;
+
+ return 0;
+}
int handle_migration_key_options(char *options);
int handle_pid_options(char *options);
int handle_tpmstate_options(char *options);
+struct ctrlchannel;
+int handle_ctrlchannel_options(char *options, struct ctrlchannel **cc);
#endif /* _SWTPM_COMMON_H_ */
--- /dev/null
+/*
+ * ctrlchannel.c -- control channel implementation
+ *
+ * (c) Copyright IBM Corporation 2015.
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ *
+ * 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.
+ */
+
+#include "config.h"
+
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <stdint.h>
+#include <arpa/inet.h>
+
+#include <libtpms/tpm_library.h>
+
+#include "ctrlchannel.h"
+#include "logging.h"
+#include "tpm_ioctl.h"
+#include "tpmlib.h"
+
+struct ctrlchannel {
+ int fd;
+};
+
+struct ctrlchannel *ctrlchannel_new(int fd)
+{
+ struct ctrlchannel *cc = malloc(sizeof(struct ctrlchannel));
+
+ if (!cc) {
+ logprintf(STDERR_FILENO, "Out of memory");
+ return NULL;
+ }
+
+ cc->fd = fd;
+ return cc;
+}
+
+int ctrlchannel_get_fd(struct ctrlchannel *cc)
+{
+ if (!cc)
+ return -1;
+
+ return cc->fd;
+}
+
+int ctrlchannel_process_fd(int fd,
+ struct libtpms_callbacks *cbs)
+{
+ struct input {
+ uint32_t cmd;
+ uint8_t body[4092];
+ } input;
+ struct output {
+ uint8_t body[4096];
+ } output;
+ ssize_t n;
+ ptm_init *init_p;
+ ptm_cap *ptm_caps;
+ TPM_RESULT *res_p;
+ size_t out_len = 0;
+
+ if (fd < 0)
+ return -1;
+
+ n = read(fd, &input, sizeof(input));
+ if (n < 0) {
+ goto err_socket;
+ }
+ if ((size_t)n < sizeof(input.cmd)) {
+ goto err_bad_input;
+ }
+
+ n -= sizeof(input.cmd);
+
+ switch (ntohl(input.cmd)) {
+ case CMD_GET_CAPABILITY:
+ ptm_caps = (ptm_cap *)&output.body;
+ *ptm_caps = htonl(PTM_CAP_INIT);
+
+ out_len = sizeof(*ptm_caps);
+ break;
+
+ case CMD_INIT:
+ if (n != sizeof(ptm_init)) {
+ goto err_bad_input;
+ } else {
+ init_p = (ptm_init *)input.body;
+ res_p = (TPM_RESULT *)output.body;
+ out_len = sizeof(*res_p);
+
+ TPMLIB_Terminate();
+
+ *res_p = tpmlib_start(cbs, ntohl(init_p->u.req.init_flags));
+ if (*res_p) {
+ logprintf(STDERR_FILENO,
+ "Error: Could not initialize the TPM\n");
+ }
+ }
+ break;
+
+ default:
+ logprintf(STDERR_FILENO,
+ "Error: Unknown command\n");
+ }
+
+ n = write(fd, output.body, out_len);
+ if (n < 0) {
+ logprintf(STDERR_FILENO,
+ "Error: Could not send response: %s\n", strerror(errno));
+ } else if ((size_t)n != out_len) {
+ logprintf(STDERR_FILENO,
+ "Error: Could not send complete response\n");
+ }
+
+err_bad_input:
+ return fd;
+
+err_socket:
+ close(fd);
+
+ return -1;
+}
--- /dev/null
+/*
+ * ctrlchannel.h -- control channel implementation
+ *
+ * (c) Copyright IBM Corporation 2015.
+ *
+ * Author: Stefan Berger <stefanb@us.ibm.com>
+ *
+ * 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.
+ */
+
+#ifndef _SWTPM_CTRLCHANNEL_H_
+#define _SWTPM_CTRLCHANNEL_H_
+
+struct ctrlchannel;
+struct libtpms_callbacks;
+
+struct ctrlchannel *ctrlchannel_new(int fd);
+int ctrlchannel_get_fd(struct ctrlchannel *cc);
+int ctrlchannel_process_fd(int fd,
+ struct libtpms_callbacks *cbs);
+
+#endif /* _SWTPM_CTRLCHANNEL_H_ */
#include <unistd.h> /* STD???_FILENO */
+#include <unistd.h> /* STD???_FILENO */
+
int log_init(const char *filename);
int log_init_fd(int fd);
int logprintf(int fd, const char *format, ...);
#include <poll.h>
#include <sys/stat.h>
#include <fcntl.h>
+#include <sys/socket.h>
#include <libtpms/tpm_error.h>
#include <libtpms/tpm_library.h>
#include "pidfile.h"
#include "tpmlib.h"
#include "utils.h"
+#include "ctrlchannel.h"
/* local variables */
static int notify_fd[2] = {-1, -1};
struct mainLoopParams {
uint32_t flags;
int fd;
+ struct ctrlchannel *cc;
};
#define MAIN_LOOP_FLAG_TERMINATE (1 << 0)
" : use the given character device\n"
"-f|--fd <fd> : use the given character device file descriptor\n"
"-d|--daemon : daemonize the TPM\n"
+ "--ctrl type=[unixio|tcp][,path=<path>][,port=<port>][,fd=<filedescriptor]\n"
+ " : TPM control channel using either UnixIO or TCP sockets;\n"
+ " the path is only valid for Unixio channels; the port must\n"
+ " be given in case the type is TCP; the TCP socket is bound\n"
+ " to 127.0.0.1\n"
"--log file=<path>|fd=<filedescriptor>\n"
" : write the TPM's log into the given file rather than\n"
" to the console; provide '-' for path to avoid logging\n"
char *logdata = NULL;
char *piddata = NULL;
char *tpmstatedata = NULL;
+ char *ctrlchdata = NULL;
#ifdef DEBUG
time_t start_time;
#endif
{"key" , required_argument, 0, 'k'},
{"pid" , required_argument, 0, 'P'},
{"tpmstate" , required_argument, 0, 's'},
+ {"ctrl" , required_argument, 0, 'C'},
{NULL , 0 , 0, 0 },
};
if (mlp.fd >= 0)
continue;
+ errno = 0;
val = strtoul(optarg, &end_ptr, 10);
if (val != (unsigned int)val || errno || end_ptr[0] != '\0') {
fprintf(stderr, "Cannot parse character device file descriptor.\n");
tpmstatedata = optarg;
break;
+ case 'C':
+ ctrlchdata = optarg;
+ break;
+
case 'h':
usage(stdout, prgname, iface);
exit(EXIT_SUCCESS);
if (handle_log_options(logdata) < 0 ||
handle_key_options(keydata) < 0 ||
handle_pid_options(piddata) < 0 ||
- handle_tpmstate_options(tpmstatedata) < 0)
+ handle_tpmstate_options(tpmstatedata) < 0 ||
+ handle_ctrlchannel_options(ctrlchdata, &mlp.cc) < 0)
return EXIT_FAILURE;
if (daemonize) {
uint32_t rlength = 0; /* bytes in response buffer */
uint32_t rTotal = 0; /* total allocated bytes */
int n;
+ int ctrlfd;
+ int ctrlclntfd;
TPM_DEBUG("mainLoop:\n");
return rc;
}
+ ctrlfd = ctrlchannel_get_fd(mlp->cc);
+ ctrlclntfd = -1;
+
while (!terminate) {
while (rc == 0) {
.fd = notify_fd[0],
.events = POLLIN,
.revents = 0,
+ }, {
+ .fd = ctrlfd,
+ .events = POLLIN,
+ .revents = 0,
+ } , {
+ .fd = ctrlclntfd,
+ .events = POLLIN | POLLHUP,
+ .revents = 0,
}
};
- if (poll(pollfds, 2, -1) < 0 ||
+ if (poll(pollfds, 4, -1) < 0 ||
(pollfds[1].revents & POLLIN) != 0) {
break;
}
+ if (pollfds[2].revents & POLLIN)
+ ctrlclntfd = accept(ctrlfd, NULL, 0);
+
+ if (pollfds[3].revents & POLLIN)
+ ctrlclntfd = ctrlchannel_process_fd(ctrlclntfd, &callbacks);
+
+ if (pollfds[3].revents & POLLHUP) {
+ close(ctrlclntfd);
+ ctrlclntfd = -1;
+ }
+
if (!(pollfds[0].revents & POLLIN))
continue;
test_migration_key_2 \
\
test_commandline \
+ test_ctrlchannel \
test_parameters \
test_resume_volatile
--- /dev/null
+#!/bin/bash
+
+# For the license, see the LICENSE file in the root directory.
+
+DIR=$(dirname "$0")
+ROOT=${DIR}/..
+SWTPM=swtpm
+SWTPM_EXE=$ROOT/src/swtpm/$SWTPM
+TPMDIR=`mktemp -d`
+PID_FILE=$TPMDIR/${SWTPM}.pid
+SOCK_PATH=$TPMDIR/sock
+CMD_PATH=$TPMDIR/cmd
+RESP_PATH=$TPMDIR/resp
+
+trap "cleanup" SIGTERM EXIT
+
+function cleanup()
+{
+ rm -rf $TPMDIR
+ if [ -n "$PID" ]; then
+ kill -SIGTERM $PID 2>/dev/null
+ fi
+}
+
+# Test 1: test the control channel
+
+# use a pseudo terminal
+exec 100<>/dev/ptmx
+$SWTPM_EXE chardev --fd 100 --tpmstate dir=$TPMDIR --pid file=$PID_FILE --ctrl type=unixio,path=$SOCK_PATH &>/dev/null &
+sleep 0.5
+
+if [ ! -r $PID_FILE ]; then
+ echo "Error: Chardev TPM did not write pidfile."
+ exit 1
+fi
+
+PID="$(cat $PID_FILE)"
+
+
+# Get the capability bits: CMD_GET_CAPABILITY = 0x00 00 00 01
+echo -en '\x00\x00\x00\x01' > $CMD_PATH
+socat -x FILE:$CMD_PATH,rdonly UNIX-CONNECT:$SOCK_PATH 2>&1 | \
+ sed -n '/^ /p' | \
+ tail -n1 > $RESP_PATH
+res="$(cat $RESP_PATH)"
+exp=" 00 00 00 01 00 00 00 00"
+if [ "$res" != "$exp" ]; then
+ echo "Error: Unexpected response from CMD_GET_CAPABILITY:"
+ echo " actual : $res"
+ echo " expected: $exp"
+ exit 1
+fi
+
+echo "OK"
+
+exit 0