# Add ability to connect to UNIX socket and make it the default.
# Drop Solaris privileges for security.
# Add -h and --help options.
# Update the event log file during startup and shutdown.
# This change was implemented in-house and is suitable for upstream use.
#
--- src/tcsd/svrside.c	2014-04-24 11:05:44.000000000 -0700
+++ src/tcsd/svrside.c	2016-04-26 15:49:54.940330002 -0700
@@ -28,6 +28,16 @@
 #include <errno.h>
 #include <getopt.h>
 #include <sys/select.h>
+
+#ifdef SOLARIS
+#include <priv.h>
+#include <fcntl.h>
+#endif
+#ifndef HAVE_DAEMON
+#include <fcntl.h>
+#include <syslog.h>
+#endif
+
 #include "trousers/tss.h"
 #include "trousers_types.h"
 #include "tcs_tsp.h"
@@ -54,6 +64,204 @@
 #define MAX_IP_PROTO 2
 #define INVALID_ADDR_STR "<Invalid client address>"
 
+#ifdef SOLARIS
+extern int get_device_fd(void);
+
+#define TPM_IOCTL_GETEVTABLE    1
+struct tpm_evtable_ioblk {
+	uint32_t	buflen;
+	caddr_t		buf;
+};
+
+static int
+store_eventlog(char *filename, struct tpm_evtable_ioblk *evlog)
+{
+	int fd;
+	unsigned int bytes = 0;
+
+	fd = open(filename, O_WRONLY | O_TRUNC | O_CREAT, 0600);
+	if (fd == -1) {
+		LogError("Error opening logfile %s: %s", filename,
+		    strerror(errno));
+		return (-1);
+	}
+	while (bytes < evlog->buflen) {
+		int n;
+		n = write(fd, evlog->buf, evlog->buflen - bytes);
+		if (n == -1 && errno != EAGAIN) {
+			LogError("Error writing logfile %s: %s",
+			    filename, strerror(errno));
+			close(fd);
+			return (-1);
+		}
+		if (n != -1)
+			bytes += n;
+	}
+	close(fd);
+
+	return (0);
+}
+
+static int
+get_event_log_from_kernel(void)
+{
+	int fd = get_device_fd();
+	struct tpm_evtable_ioblk ioblk;
+
+	if (fd == -1)
+		return (-1);
+
+	(void) memset(&ioblk, 0, sizeof (ioblk));
+	if (ioctl(fd, TPM_IOCTL_GETEVTABLE, &ioblk)) {
+		LogDebug("Cannot get event log from kernel: %s",
+		    strerror(errno));
+		return (-1);
+	}
+	if (ioblk.buflen == 0)
+		return (0);
+
+	ioblk.buf = calloc(1, ioblk.buflen);
+	if (ioblk.buf == NULL) {
+		return (-1);
+	}
+	if (ioctl(fd, TPM_IOCTL_GETEVTABLE, &ioblk)) {
+		free(ioblk.buf);
+		LogDebug("Cannot get event log from kernel: %s",
+		    strerror(errno));
+		return (-1);
+	}
+
+	return (store_eventlog(tcsd_options.firmware_log_file, &ioblk));
+}
+
+/*
+ * For Solaris, make the tcsd privilege aware and drop
+ * risky privileges if they are not needed.
+ */
+static int
+drop_privs(void)
+{
+	priv_set_t *myprivs;
+	int rv;
+
+	/*
+	 * Drop unneeded privs such as fork/exec.
+	 *
+	 * Get "basic" privs and remove the ones we don't want.
+	 */
+	if ((myprivs = priv_str_to_set("basic", ",", NULL)) == NULL) {
+		LogError("priv_str_to_set failed: %s", strerror(errno));
+		return (1);
+	} else {
+		(void) priv_delset(myprivs, PRIV_PROC_EXEC);
+		(void) priv_delset(myprivs, PRIV_PROC_FORK);
+		(void) priv_delset(myprivs, PRIV_FILE_LINK_ANY);
+		(void) priv_delset(myprivs, PRIV_PROC_INFO);
+		(void) priv_delset(myprivs, PRIV_PROC_SESSION);
+		(void) priv_delset(myprivs, PRIV_PROC_SETID);
+
+		/* for auditing */
+		(void) priv_addset(myprivs, PRIV_PROC_AUDIT);
+
+		if ((rv = setppriv(PRIV_SET, PRIV_PERMITTED, myprivs)))
+			return (rv);
+		if ((rv = setppriv(PRIV_SET, PRIV_LIMIT, myprivs)))
+			return (rv);
+		if ((rv = setppriv(PRIV_SET, PRIV_INHERITABLE, myprivs)))
+			return (rv);
+
+		(void) priv_freeset(myprivs);
+	}
+	return (0);
+}
+#endif /* SOLARIS */
+
+#ifndef HAVE_DAEMON
+static int
+daemon(int nochdir, int noclose) {
+	int rv, fd;
+
+	if (!noclose) {
+		closelog();
+		closefrom(0);
+	}
+
+	switch (fork()) {
+		case -1: /* failure: parent process */
+			return (-1);
+		case 0: /* success: child process */
+			break;
+		default: /* success: parent process */
+		exit (0);
+	}
+
+	/* Create a new SID for the child process */
+	if (setsid() == -1)
+		return (-1);
+	/* Prevent cwd from being left open and unremovable */
+	if (!nochdir)
+		(void) chdir("/");
+	(void) umask(0);
+	return (0);
+}
+#endif /* !HAVE_DAEMON */
+
+
+static int
+setup_unix_socket(struct srv_sock_info ssi[])
+{
+	struct sockaddr_un serv_addr;
+	int sd, opt;
+
+	ssi->sd = -1;
+
+	// Initialization of UNIX socket.
+	sd = socket(PF_UNIX, SOCK_STREAM, 0);
+	if (sd < 0) {
+		LogWarn("Failed UNIX socket: %s", strerror(errno));
+		goto err;
+	}
+
+	memset(&serv_addr, 0, sizeof (serv_addr));
+	serv_addr.sun_family = AF_UNIX;
+	strncpy(serv_addr.sun_path, TCSD_DEFAULT_SOCKET,
+	    sizeof(serv_addr.sun_path));
+	/* Remove previous stale server socket, if any */
+	(void) unlink(TCSD_DEFAULT_SOCKET);
+
+	LogDebug("Connecting to UNIX Domain socket %s",
+	    TCSD_DEFAULT_SOCKET);
+
+
+	if (bind(sd, (struct sockaddr *) &serv_addr, sizeof (serv_addr)) < 0) {
+		LogWarn("Failed UNIX socket bind: %s", strerror(errno));
+		goto err;
+	}
+
+	if (listen(sd, TCSD_MAX_SOCKETS_QUEUED) < 0) {
+		LogWarn("Failed UNIX socket listen: %s", strerror(errno));
+		goto err;
+	}
+
+	if (chmod(serv_addr.sun_path, 0666) < 0) {
+		LogError("Failed chmod %s: %s", serv_addr.sun_path,
+		    strerror(errno));
+		return (-1);
+	}
+
+	ssi->domain = AF_UNIX;
+	ssi->sd = sd;
+	ssi->addr_len = sizeof (serv_addr);
+
+	return (0);
+
+ err:
+	if (sd != -1)
+		close(sd);
+
+	return (-1);
+}
+
 static void close_server_socks(struct srv_sock_info *socks_info)
 {
 	int i, rv;
@@ -83,6 +291,10 @@
 	auth_mgr_final();
 	(void)req_mgr_final();
 	conf_file_final(&tcsd_options);
+#ifdef SOLARIS
+	/* Not fatal if this fails */
+	(void) get_event_log_from_kernel();
+#endif
 	EVENT_LOG_final();
 }
 
@@ -199,6 +411,10 @@
 		(void)req_mgr_final();
 		return result;
 	}
+#ifdef SOLARIS
+	/* Not fatal if this fails */
+	(void) get_event_log_from_kernel();
+#endif
 
 	result = owner_evict_init();
 	if (result != TSS_SUCCESS) {
@@ -352,6 +568,13 @@
 	int i=0;
 
 	ssi[0].sd = ssi[1].sd = -1;
+
+	// By default, use UNIX socket
+	if (tcsd_options.port == 0) {
+		return (setup_unix_socket(&ssi[0]));
+	}
+
+	// Use TCP/IP socket
 	// Only enqueue sockets successfully bound or that weren't disabled.
 	if (tcsd_options.disable_ipv4) {
 		LogWarn("IPv4 support disabled by configuration option");
@@ -380,11 +603,17 @@
 
 	if (getnameinfo((struct sockaddr *)client_addr, socklen, buf,
 						sizeof(buf), NULL, 0, 0) != 0) {
-		LogWarn("Could not retrieve client address info");
-		return NULL;
-	} else {
-		return strdup(buf);
+		if (tcsd_options.port == 0) { /* UNIX socket (always local) */
+			if (gethostname(buf, sizeof (buf)) != 0) {
+				strcpy(buf, "localhost");
+			}
+		} else {
+			LogWarn("Could not retrieve client address info");
+			return NULL;
+		}
 	}
+
+	return (strdup(buf));
 }
 
 void prepare_for_select(struct srv_sock_info *socks_info, int *num_fds,
@@ -490,6 +719,14 @@
 		}
 	}
 
+#ifdef SOLARIS
+	/* For Solaris, drop privileges for security. */
+	if ((rv = drop_privs())) {
+		LogError("drop_privs failed: %s", strerror(errno));
+		return (rv);
+	}
+#endif /* SOLARIS */
+
 	LogInfo("%s: TCSD up and running.", PACKAGE_STRING);
 
 	sigemptyset(&sigmask);
