]> git.proxmox.com Git - swtpm.git/commitdiff
swtpm: introduce control channel for chardev TPM
authorStefan Berger <stefanb@us.ibm.com>
Fri, 27 Nov 2015 22:34:15 +0000 (17:34 -0500)
committerStefan Berger <stefanb@us.ibm.com>
Sat, 28 Nov 2015 03:13:37 +0000 (22:13 -0500)
Implement a control channel for the 'chardev' swtpm that implements
a minimal set of control messages for now.

Signed-off-by: Stefan Berger <stefanb@linux.vnet.ibm.com>
14 files changed:
dist/swtpm.spec
dist/swtpm.spec.in
include/swtpm/tpm_ioctl.h
man/man8/swtpm.8
man/man8/swtpm.pod
src/swtpm/Makefile.am
src/swtpm/common.c
src/swtpm/common.h
src/swtpm/ctrlchannel.c [new file with mode: 0644]
src/swtpm/ctrlchannel.h [new file with mode: 0644]
src/swtpm/logging.h
src/swtpm/swtpm_chardev.c
tests/Makefile.am
tests/test_ctrlchannel [new file with mode: 0644]

index e5ce1060eb35d0e727fdcd1cfcd9fd479adf414e..9aedcfcf5e3f3e4cb8999018915b0ed3529853e8 100644 (file)
@@ -22,7 +22,7 @@ BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root
 
 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
index cffb280b676c601456b17474397bcd6a5907673a..7ff2d62f5442e86661067d9f22618483b7d33abc 100644 (file)
@@ -22,7 +22,7 @@ BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root
 
 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
index 152e8a241c2894725adfefbd12d78c57fcc76c0e..66054389eda318210051c7be28026d067c177283 100644 (file)
@@ -214,4 +214,12 @@ enum {
     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 */
index d5a4e0b6ad059fc3b234a11145b3577bd3bdd5c4..9fb88f9be21b01af1f88ab0086ef5d91ed0326db 100644 (file)
@@ -219,6 +219,12 @@ Daemonize the process.
 .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.
index 7b58faefef441437e068ef5f01797abda4a24f9f..c8b30f739132f90bf02c8536a6d62edfc06a6f9f 100644 (file)
@@ -108,6 +108,13 @@ Daemonize the process.
 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.
index ad05ac5bcbbe2664041c0e9f1345d17af23d4ec1..fc1de664af887f30a2e61c4b54e83a0730e90a9e 100644 (file)
@@ -6,6 +6,7 @@
 
 noinst_HEADERS = \
        common.h \
+       ctrlchannel.h \
        key.h \
        logging.h \
        main.h \
@@ -23,6 +24,7 @@ lib_LTLIBRARIES = libswtpm_libtpms.la
 
 libswtpm_libtpms_la_SOURCES = \
        common.c \
+       ctrlchannel.c \
        key.c \
        logging.c \
        options.c \
index aeb08ceb109e22300af5ccce9cfc120bd83c19f2..d566a36a24e1854b548ec240f627eb979fd78f70 100644 (file)
 #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>
 
@@ -54,6 +58,7 @@
 #include "swtpm_nvfile.h"
 #include "pidfile.h"
 #include "tpmstate.h"
+#include "ctrlchannel.h"
 
 /* --log %s */
 static const OptionDesc logging_opt_desc[] = {
@@ -106,6 +111,26 @@ static const OptionDesc tpmstate_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.
@@ -432,3 +457,223 @@ handle_tpmstate_options(char *options)
 
     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;
+}
index 95031dca6cdfc7ea6fd56224bf6a46802773c69c..087da0bb06cc72b751c064d0b1e20f685879d60a 100644 (file)
@@ -42,6 +42,8 @@ int handle_key_options(char *options);
 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_ */
 
diff --git a/src/swtpm/ctrlchannel.c b/src/swtpm/ctrlchannel.c
new file mode 100644 (file)
index 0000000..2a904be
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * 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;
+}
diff --git a/src/swtpm/ctrlchannel.h b/src/swtpm/ctrlchannel.h
new file mode 100644 (file)
index 0000000..450727d
--- /dev/null
@@ -0,0 +1,49 @@
+/*
+ * 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_ */
index b88727c01d392e5c6e78633b0a83cb402507d11f..4b92cd296adfe6a5064e8589e3ddb032ac0a767d 100644 (file)
@@ -40,6 +40,8 @@
 
 #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, ...);
index 91c53919fb091b419857890752ce6532bd8c52cd..53ad422bdd5f84bd52c60b0048c9df0d254e82f5 100644 (file)
@@ -47,6 +47,7 @@
 #include <poll.h>
 #include <sys/stat.h>
 #include <fcntl.h>
+#include <sys/socket.h>
 
 #include <libtpms/tpm_error.h>
 #include <libtpms/tpm_library.h>
@@ -60,6 +61,7 @@
 #include "pidfile.h"
 #include "tpmlib.h"
 #include "utils.h"
+#include "ctrlchannel.h"
 
 /* local variables */
 static int notify_fd[2] = {-1, -1};
@@ -77,6 +79,7 @@ static struct libtpms_callbacks callbacks = {
 struct mainLoopParams {
     uint32_t flags;
     int fd;
+    struct ctrlchannel *cc;
 };
 
 #define MAIN_LOOP_FLAG_TERMINATE  (1 << 0)
@@ -105,6 +108,11 @@ static void usage(FILE *file, const char *prgname, const char *iface)
     "                 : 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"
@@ -143,6 +151,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i
     char *logdata = NULL;
     char *piddata = NULL;
     char *tpmstatedata = NULL;
+    char *ctrlchdata = NULL;
 #ifdef DEBUG
     time_t              start_time;
 #endif
@@ -155,6 +164,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i
         {"key"       , required_argument, 0, 'k'},
         {"pid"       , required_argument, 0, 'P'},
         {"tpmstate"  , required_argument, 0, 's'},
+        {"ctrl"      , required_argument, 0, 'C'},
         {NULL        , 0                , 0, 0  },
     };
 
@@ -185,6 +195,7 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i
             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");
@@ -221,6 +232,10 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i
             tpmstatedata = optarg;
             break;
 
+        case 'C':
+            ctrlchdata = optarg;
+            break;
+
         case 'h':
             usage(stdout, prgname, iface);
             exit(EXIT_SUCCESS);
@@ -239,7 +254,8 @@ int swtpm_chardev_main(int argc, char **argv, const char *prgname, const char *i
     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) {
@@ -329,6 +345,8 @@ static int mainLoop(struct mainLoopParams *mlp)
     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");
 
@@ -341,6 +359,9 @@ static int mainLoop(struct mainLoopParams *mlp)
         return rc;
     }
 
+    ctrlfd = ctrlchannel_get_fd(mlp->cc);
+    ctrlclntfd = -1;
+
     while (!terminate) {
 
         while (rc == 0) {
@@ -353,14 +374,33 @@ static int mainLoop(struct mainLoopParams *mlp)
                     .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;
 
index cb5a6f919cd09f71f8252a35ad7d36a8431115df..490fb32ae3ae4c13f4eb2c2a3a1a6cc6fbce0d43 100644 (file)
@@ -23,6 +23,7 @@ TESTS = \
        test_migration_key_2 \
        \
        test_commandline \
+       test_ctrlchannel \
        test_parameters \
        test_resume_volatile
 
diff --git a/tests/test_ctrlchannel b/tests/test_ctrlchannel
new file mode 100644 (file)
index 0000000..4cdbb78
--- /dev/null
@@ -0,0 +1,56 @@
+#!/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