[ima-evm-utils 4/5] evmctl-allow-launching-daemon

Vivek Goyal vgoyal at redhat.com
Fri Sep 6 19:38:23 UTC 2013


Launch evmctl as daemon so that clients can connect to it and request
file signing. This can be useful when signing needs to happen using
an crypto card and client does not know the pin of the card. Machine
owner can setup evmctl daemon, set pin and later clients can request
file signing without knowing pin.

I have taken lot of daemon and client code from pesign project (written
by peter jones).

Signed-off-by: Vivek Goyal <vgoyal at redhat.com>
---
 src/daemon.h |  83 +++++++
 src/evmctl.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
 2 files changed, 829 insertions(+), 30 deletions(-)
 create mode 100644 src/daemon.h

diff --git a/src/daemon.h b/src/daemon.h
new file mode 100644
index 0000000..0a29ae6
--- /dev/null
+++ b/src/daemon.h
@@ -0,0 +1,83 @@
+/*
+ * Copyright 2013 Red Hat, Inc.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author(s): Vivek Goyal <vgoyal at redhat.com>
+ */
+#ifndef DAEMON_H
+#define DAEMON_H 1
+
+/* Daemon related stuff */
+#define SOCKPATH	"/var/run/evmctl/socket"
+#define PIDFILE		"/var/run/evmctld.pid"
+#define EVMCTLD_VERSION	0xb4d052dc
+
+extern int daemonize(void);
+
+typedef struct {
+	uint32_t version;
+	uint32_t command;
+	uint32_t size;
+} evmctld_msghdr;
+
+typedef struct  {
+	uint32_t rc;
+	uint8_t errmsg[];
+} evmctld_cmd_response;
+
+typedef struct {
+	uint32_t size;
+	uint8_t value[];
+} evmctld_string;
+
+typedef enum {
+	CMD_KILL_DAEMON,
+	CMD_SET_PIN,
+	CMD_IMA_SIGN_ATTACHED,
+	CMD_IMA_SIGN_DETACHED,
+	CMD_RESPONSE,
+	CMD_LIST_END
+} evmctld_cmd;
+
+static inline uint32_t
+__attribute__ ((unused))
+evmctld_string_size(char *buffer)
+{
+	evmctld_string *s;
+	return sizeof(s->size) + (buffer ? strlen(buffer) : 0) + 1;
+}
+
+static inline void
+__attribute__ ((unused))
+evmctld_string_set(evmctld_string *str, char *value)
+{
+	str->size = (value ? strlen(value) : 0) + 1;
+	if (value)
+		strcpy((char *)str->value, value);
+	else
+		str->value[0] = '\0';
+}
+
+static inline evmctld_string *
+__attribute__ ((unused))
+evmctld_string_next(evmctld_string *str)
+{
+	char *buffer = (char *)str;
+	buffer += sizeof(str->size) + str->size;
+	return (evmctld_string *)buffer;
+}
+
+
+#endif /* DAEMON_H */
diff --git a/src/evmctl.c b/src/evmctl.c
index 2205d1e..6638410 100644
--- a/src/evmctl.c
+++ b/src/evmctl.c
@@ -26,6 +26,7 @@
  *	 IMA/EVM control program
  */
 
+#define _GNU_SOURCE
 #include <sys/types.h>
 #include <sys/stat.h>
 #include <sys/ioctl.h>
@@ -48,6 +49,11 @@
 #include <stdbool.h>
 #include <libp11.h>
 
+#include <sys/socket.h>
+#include <sys/un.h>
+#include <sys/prctl.h>
+#include <poll.h>
+
 #include <openssl/sha.h>
 #include <openssl/rsa.h>
 #include <openssl/pem.h>
@@ -57,15 +63,9 @@
 #include <openssl/err.h>
 #include <openssl/x509.h>
 
-#define USE_FPRINTF
+#include "daemon.h"
 
-#ifdef USE_FPRINTF
-#define do_log(level, fmt, args...)	({ if (level <= verbose) fprintf(stderr, fmt, ##args); })
-#define do_log_dump(level, p, len)	({ if (level <= verbose) do_dump(stderr, p, len); })
-#else
-#define do_log(level, fmt, args...)	syslog(level, fmt, ##args)
-#define do_log_dump(p, len)
-#endif
+#define USE_FPRINTF
 
 #ifdef DEBUG
 #define log_debug(fmt, args...)		do_log(LOG_DEBUG, "%s:%d " fmt, __func__ , __LINE__ , ##args)
@@ -77,6 +77,8 @@
 
 #define log_dump(p, len)		do_log_dump(LOG_INFO, p, len)
 #define log_info(fmt, args...)		do_log(LOG_INFO, fmt, ##args)
+#define log_warn(fmt, args...)		do_log(LOG_WARNING, fmt, ##args)
+#define log_notice(fmt, args...)	do_log(LOG_NOTICE, fmt, ##args)
 #define log_err(fmt, args...)		do_log(LOG_ERR, fmt, ##args)
 #define log_errno(fmt, args...)		do_log(LOG_ERR, fmt ": errno: %s (%d)\n", ##args, strerror(errno), errno)
 
@@ -91,6 +93,10 @@
 #define FS_IOC32_GETFLAGS	_IOR('f', 1, int)
 #define FS_IOC32_SETFLAGS	_IOW('f', 2, int)
 
+static int should_exit = 0;
+static int socket_desc;
+static bool daemon_mode = false;
+
 struct h_misc {
 	unsigned long ino;
 	uint32_t generation;
@@ -252,6 +258,14 @@ struct command {
 	char *msg;		/* extra info message */
 };
 
+/* When in daemon mode, this keep info about client context */
+struct client_context {
+	bool memlock;		/* Client wants to memlock file */
+	char *token_label;
+	char *key;
+	char *file;
+};
+
 static int verbose = LOG_INFO - 1;
 static int g_argc;
 static char **g_argv;
@@ -287,6 +301,26 @@ static verify_hash_fn_t verify_hash;
 struct command cmds[];
 static void print_usage(struct command *cmd);
 
+static void do_log(int level, const char *fmt, ...)
+{
+	va_list ap;
+
+	va_start(ap, fmt);
+	if (daemon_mode == true) {
+		vsyslog(level, fmt, ap);
+		va_end(ap);
+		return;
+	}
+
+#ifdef USE_FPRINTF
+	if (level <= verbose)
+		vfprintf(stderr, fmt, ap);
+#else
+	vsyslog(level, fmt, ap);
+#endif
+	va_end(ap);
+}
+
 static void do_dump(FILE *fp, const void *ptr, int len)
 {
 	int i;
@@ -297,8 +331,26 @@ static void do_dump(FILE *fp, const void *ptr, int len)
 	fprintf(fp, "\n");
 }
 
+static void do_log_dump(int level, const void *p, int len)
+{
+#ifdef USE_FPRINTF
+	if (level > verbose)
+		return;
+
+	/* May be we can dump to syslog in daemon mode ? */
+	if (daemon_mode == true)
+		return;
+
+	do_dump(stderr, p, len);
+#endif
+}
+
 static void dump(const void *ptr, int len)
 {
+	/* May be we can dump to syslog in daemon mode ? */
+	if (daemon_mode == true)
+		return;
+
 	do_dump(stdout, ptr, len);
 }
 
@@ -1133,10 +1185,10 @@ static int parse_token_label(char *tk_label, char *key, char **newkey)
 static void unload_engine(ENGINE *e)
 {
 	/* Free up functional reference */
-	if (e != NULL)
+	if (e != NULL) {
 		ENGINE_finish(e);
-
-	ENGINE_cleanup();
+		ENGINE_cleanup();
+	}
 }
 
 static ENGINE *load_engine(void)
@@ -1271,7 +1323,8 @@ static int add_memlock_info(unsigned char *ptr)
 	return sizeof(struct memlock_hdr);
 }
 
-static int sign_ima(const char *file, const char *key)
+static int sign_ima(const char *file, const char *key,
+					struct client_context *cc)
 {
 	unsigned char hash[64];
 	unsigned char sig[1024] = "\x03";
@@ -1299,7 +1352,7 @@ static int sign_ima(const char *file, const char *key)
 		return 0;
 	}
 
-	if (memlock) {
+	if (memlock || (cc && cc->memlock)) {
 		memlock_len = add_memlock_info(sig + len);
 		len += memlock_len;
 	}
@@ -1321,6 +1374,7 @@ static int sign_ima(const char *file, const char *key)
 static int cmd_sign_ima(struct command *cmd)
 {
 	char *key, *file = g_argv[optind++];
+	int rc;
 
 	if (!file) {
 		log_err("Parameters missing\n");
@@ -1330,7 +1384,23 @@ static int cmd_sign_ima(struct command *cmd)
 
 	key = keyfile ? : "/etc/keys/privkey_evm.pem";
 
-	return sign_ima(file, key);
+	if (token_label) {
+		char *temp_key = strdup(keyfile);
+		rc = parse_token_label(token_label, temp_key, &keyfile);
+		free(temp_key);
+		if (rc)
+			return -1;
+	}
+
+	if (engine_id) {
+		engine = load_engine();
+		if (engine == NULL) {
+			log_err("Failed to load engine\n");
+			return -1;
+		}
+	}
+
+	return sign_ima(file, key, NULL);
 
 }
 
@@ -1348,7 +1418,7 @@ static int cmd_sign_evm(struct command *cmd)
 	key = keyfile ? : "/etc/keys/privkey_evm.pem";
 
 	if (digsig) {
-		err = sign_ima(file, key);
+		err = sign_ima(file, key, NULL);
 		if (err)
 			return err;
 	}
@@ -1420,6 +1490,665 @@ out:
 	return ret;
 }
 
+static void write_pid_file(int pid)
+{
+	int fd = open(PIDFILE, O_WRONLY|O_CREAT|O_TRUNC, 0644);
+	if (fd < 0) {
+err:
+		fprintf(stderr, "couldn't open pidfile: %m\n");
+		exit(1);
+	}
+	char *pidstr = NULL;
+	int rc = asprintf(&pidstr, "%d\n", pid);
+	if (rc < 0)
+		goto err;
+
+	rc = write(fd, pidstr, strlen(pidstr)+1);
+	if (rc < 0)
+		goto err;
+
+	free(pidstr);
+	close(fd);
+}
+
+static void check_socket(void)
+{
+	errno = 0;
+	int rc = access(SOCKPATH, R_OK);
+	if (rc == 0) {
+		struct sockaddr_un addr_un = {
+			.sun_family = AF_UNIX,
+			.sun_path = SOCKPATH,
+		};
+
+		int sd = socket(AF_UNIX, SOCK_SEQPACKET, 0);
+		if (sd < 0) {
+			fprintf(stderr, "unable to create socket: %m");
+			exit(1);
+		}
+
+		socklen_t len = strlen(addr_un.sun_path) +
+				sizeof(addr_un.sun_family);
+
+		rc = connect(sd, (struct sockaddr *)&addr_un, len);
+		if (rc < 0) {
+			close(sd);
+			unlink(SOCKPATH);
+			return;
+		}
+
+		struct sockaddr_un remote;
+		socklen_t size = sizeof(remote);
+		rc = getpeername(sd, &remote, &size);
+		if (rc < 0) {
+			close(sd);
+			return;
+		} else {
+			fprintf(stderr, "already running");
+			exit(1);
+		}
+	} else {
+		/* It could be something other than EEXIST, but it really
+		 * doesn't matter since the daemon isn't running.  Blindly
+		 * remove it. */
+		unlink(SOCKPATH);
+	}
+}
+
+static void daemon_setup_std_descriptors(void)
+{
+	int rc;
+
+	/* Handle stdio, stdout and stderr */
+	int fd = open("/dev/zero", O_RDONLY);
+	if (fd < 0) {
+		log_err("could not open /dev/zero: %m");
+		exit(1);
+	}
+	close(STDIN_FILENO);
+
+	rc = dup2(fd, STDIN_FILENO);
+	if (rc < 0) {
+		log_err("could not set up standard input: %m");
+		exit(1);
+	}
+	close(fd);
+
+	fd = open("/dev/null", O_WRONLY);
+	if (fd < 0) {
+		log_err("could not open /dev/null: %m");
+		exit(1);
+	}
+	close(STDOUT_FILENO);
+
+	rc = dup2(fd, STDOUT_FILENO);
+	if (rc < 0) {
+		log_err("could not set up standard output: %m");
+		exit(1);
+	}
+	close(STDERR_FILENO);
+
+	rc = dup2(fd, STDERR_FILENO);
+	if (rc < 0) {
+		log_err("could not set up standard error: %m");
+		exit(1);
+	}
+	close(fd);
+}
+
+static void quit_handler(int signal)
+{
+	should_exit = 1;
+}
+
+static int set_up_socket(void)
+{
+	int sd = socket(AF_UNIX, SOCK_STREAM, 0);
+	if (sd < 0) {
+		log_err("unable to create socket: %m");
+		exit(1);
+	}
+
+	int one = 1;
+	int rc = setsockopt(sd, SOL_SOCKET, SO_PASSCRED, &one, sizeof(one));
+	if (rc < 0) {
+		log_err("unable to set socket options: %m");
+		exit(1);
+	}
+
+	struct sockaddr_un addr_un = {
+		.sun_family = AF_UNIX,
+		.sun_path = SOCKPATH,
+	};
+
+	rc = bind(sd, &addr_un, sizeof(addr_un));
+	if (rc < 0) {
+		log_err("unable to bind to \"%s\": %m", addr_un.sun_path);
+		exit(1);
+	}
+	rc = chmod(SOCKPATH, 0660);
+	if (rc < 0) {
+		log_err("could not set permissions for \"%s\": %m", SOCKPATH);
+		exit(1);
+	}
+
+	rc = listen(sd, 5);
+	if (rc < 0) {
+		log_err("unable to listen on socket: %m");
+		exit(1);
+	}
+
+	socket_desc = sd;
+	return 0;
+}
+
+static void send_response(struct pollfd *pollfd, int rc, char *response)
+{
+	struct msghdr msg;
+	struct iovec iov;
+	ssize_t n;
+	int msglen = response ? strlen(response) + 1 : 0;
+
+	iov.iov_len = sizeof(evmctld_msghdr) + sizeof(evmctld_cmd_response)
+			+ msglen;
+
+	void *buffer = calloc(1, iov.iov_len);
+	if (!buffer) {
+		log_err("could not allocate memory: %m");
+		exit(1);
+	}
+
+	iov.iov_base = buffer;
+
+	evmctld_msghdr *em = buffer;
+	evmctld_cmd_response *resp = (evmctld_cmd_response *)((uint8_t *)em +
+					offsetof(evmctld_msghdr, size) +
+					sizeof (em->size));
+
+	em->version = EVMCTLD_VERSION;
+	em->command = CMD_RESPONSE;
+	em->size = sizeof(resp->rc) + msglen;
+
+	memset(&msg, '\0', sizeof(msg));
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	resp->rc = rc;
+
+	if (response)
+		memcpy(resp->errmsg, response, msglen);
+
+	n = sendmsg(pollfd->fd, &msg, 0);
+	if (n < 0)
+		log_warn("could not send response to client: %m");
+
+	free(buffer);
+}
+
+static void handle_kill_daemon(struct pollfd *pollfd, socklen_t size)
+{
+        should_exit = 1;
+}
+
+static void free_client_context(struct client_context *cc)
+{
+	if (cc == NULL)
+		return;
+
+	if (cc->key)
+		free(cc->key);
+	if (cc->token_label)
+		free(cc->token_label);
+	free(cc);
+}
+
+static void handle_ima_sign(struct pollfd *pollfd, socklen_t size)
+{
+	struct msghdr msg;
+	struct iovec iov;
+	ssize_t n;
+	evmctld_string *ekey = NULL, *etoken_label = NULL, *efile = NULL, *etemp;
+	bool ememlock = false;
+	char *key, *temp;
+	int rc;
+	struct client_context *cc;
+
+	/* Allocate client context */
+	cc = calloc(sizeof(struct client_context), 1);
+	if (!cc)
+		goto oom;
+
+	char *buffer = malloc(size);
+	if (!buffer) {
+oom:
+		log_err("unable to allocate memory: %m");
+		exit(1);
+	}
+
+	memset(&msg, '\0', sizeof(msg));
+
+	iov.iov_base = buffer;
+	iov.iov_len = size;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);
+
+	etemp = (evmctld_string *)buffer;
+	if (n < sizeof(etemp->size)) {
+malformed:
+		log_err("sign: invalid data\n");
+		log_err("possible exploit attempt. closing.\n");
+		close(pollfd->fd);
+		return;
+	}
+
+	n -= sizeof(etemp->size);
+	if (n < etemp->size)
+		goto malformed;
+	n -= etemp->size;
+
+	if (etemp->value[etemp->size - 1] != '\0')
+		goto malformed;
+
+	ekey = etemp;
+	log_info("sign: key=%s.\n", (char *)ekey->value);
+
+next_string:
+	etemp = evmctld_string_next(etemp);
+	n -= sizeof(etemp->size);
+	if (n < etemp->size)
+		goto malformed;
+	n -= etemp->size;
+
+	if (etemp->value[etemp->size - 1] != '\0')
+		goto malformed;
+
+	log_info("sign: etemp->value=%s", (char *)etemp->value);
+
+	if (etemp->size > 15 &&
+	    !strncmp((char *)etemp->value, "--token_label ", 14)) {
+		if (etoken_label != NULL)
+			goto malformed;
+		etoken_label = etemp;
+		temp = (char *)etemp->value + strlen("--token_label ");
+		cc->token_label = strdup(temp);
+		if (!cc->token_label)
+			goto oom;
+	} else if (etemp->size > 9 &&
+		   !strncmp((char *)etemp->value, "--memlock", 9)) {
+		if (ememlock != false)
+			goto malformed;
+		ememlock = true;
+		cc->memlock = true;
+	} else {
+		efile = etemp;
+		cc->file = strdup((char *)efile->value);
+		if (!cc->file)
+			goto oom;
+	}
+
+	/* file to be signed is last string */
+	if (!efile && n > 0)
+		goto next_string;
+
+	if (n != 0)
+		goto malformed;
+
+	if (!efile) {
+		log_info("Did not find efile\n");
+		goto malformed;
+	}
+
+	log_info("sign: key=%s efile=%s, memlock=%d token_label=%s\n",
+			ekey->value, efile->value, ememlock,
+			etoken_label ? etoken_label->value : NULL);
+
+	if (sigfile)
+		log_notice("ima signing detached");
+	else
+		log_notice("ima signing attached");
+
+	/* Strip --key from key */
+	if (strncmp((char *)ekey->value, "--key ", 6)) {
+		/* key is not prefixed with --key. Bad message */
+		goto malformed;
+	}
+
+	key = (char *)ekey->value + strlen("--key ");
+
+	/* Convert token label to slot and modify key accordingly */
+	if (cc->token_label) {
+		rc = parse_token_label(cc->token_label, key, &cc->key);
+		if (rc) {
+			log_notice("Failed to parse token label");
+			send_response(pollfd, rc, "Failed to parse token label");
+			goto out;
+		}
+	} else  {
+		cc->key = strdup(key);
+		if (!cc->key)
+			goto oom;
+	}
+
+	rc = sign_ima((char *)cc->file, cc->key, cc);
+	if (!rc) {
+		log_notice("Signing successful");
+		send_response(pollfd, rc, "Signing successful");
+	} else {
+		log_notice("Signing Failed");
+		send_response(pollfd, rc, "Signing Failed");
+	}
+
+out:
+	free(buffer);
+	free_client_context(cc);
+}
+
+static void handle_ima_sign_detached(struct pollfd *pollfd, socklen_t size)
+{
+	/* emulate passing -f */
+	sigfile = 1;
+	xattr = 0;
+	handle_ima_sign(pollfd, size);
+}
+
+static void handle_ima_sign_attached(struct pollfd *pollfd, socklen_t size)
+{
+	handle_ima_sign(pollfd, size);
+}
+
+static void handle_set_pin(struct pollfd *pollfd, socklen_t size)
+{
+	struct msghdr msg;
+	struct iovec iov;
+	ssize_t n;
+
+	char *buffer = malloc(size);
+	if (!buffer) {
+oom:
+		log_err("unable to allocate memory: %m");
+		exit(1);
+	}
+
+	memset(&msg, '\0', sizeof(msg));
+
+	iov.iov_base = buffer;
+	iov.iov_len = size;
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);
+
+	evmctld_string *ep = (evmctld_string *)buffer;
+	if (n < sizeof(ep->size)) {
+malformed:
+		log_err("set-pin: invalid data\n");
+		log_err("possible exploit attempt. closing.\n");
+		close(pollfd->fd);
+		return;
+	}
+
+	n -= sizeof(ep->size);
+	if (n < ep->size)
+		goto malformed;
+	n -= ep->size;
+
+	if (ep->value[ep->size - 1] != '\0')
+		goto malformed;
+
+	if (n != 0)
+		goto malformed;
+
+	log_notice("setting pin");
+
+	char *pin = (char *)ep->value;
+	if (!pin)
+		goto oom;
+
+	if (engine) {
+		ENGINE_ctrl_cmd_string(engine, "PIN", pin, 0);
+		log_notice("set pin done.");
+		send_response(pollfd, 0, "set pin done");
+	} else {
+		log_notice("No engine loaded. set pin failed.\n");
+		send_response(pollfd, 1, "No engine loaded. set pin failed.");
+	}
+
+	free(buffer);
+}
+
+static void handle_invalid_input(evmctld_cmd cmd, struct pollfd *pollfd,
+			socklen_t size)
+{
+		log_err("got unexpected command 0x%x", cmd);
+		log_err("possible exploit attempt");
+}
+
+typedef void (*cmd_handler)(struct pollfd *pollfd, socklen_t size);
+
+typedef struct {
+	evmctld_cmd cmd;
+	cmd_handler func;
+} cmd_table_t;
+
+cmd_table_t cmd_table[] = {
+		{ CMD_KILL_DAEMON, handle_kill_daemon },
+		{ CMD_SET_PIN, handle_set_pin },
+		{ CMD_IMA_SIGN_ATTACHED, handle_ima_sign_attached },
+		{ CMD_IMA_SIGN_DETACHED, handle_ima_sign_detached },
+		{ CMD_RESPONSE, NULL },
+		{ CMD_LIST_END, NULL }
+	};
+
+static int handle_event(struct pollfd *pollfd)
+{
+	struct msghdr msg;
+	struct iovec iov;
+	ssize_t n;
+	evmctld_msghdr em;
+	int i;
+
+	msg.msg_name = NULL;
+	msg.msg_namelen = 0;
+
+	iov.iov_base = &em;
+	iov.iov_len = sizeof(em);
+	msg.msg_iov = &iov;
+	msg.msg_iovlen = 1;
+
+	char control[1024];
+	msg.msg_controllen = 1024;
+	msg.msg_control = control;
+
+	n = recvmsg(pollfd->fd, &msg, MSG_WAITALL);
+	if (n < 0) {
+		log_warn("recvmsg failed: %m");
+		return n;
+	}
+
+	/* if recvmsg returned 0, we're not going to get any valid data. */
+	/* This *probably* means we were hung up on. */
+	if (n == 0)
+		return n;
+
+	if (n < sizeof (em)) {
+		log_err("got message with invalid size %zu", n);
+		log_err("possible exploit attempt.  closing.");
+		close(pollfd->fd);
+		return -1;
+	}
+
+	if (em.version != EVMCTLD_VERSION) {
+		log_err("got version %d, expected version %d",
+			em.version, EVMCTLD_VERSION);
+		log_err("possible exploit attempt.  closing.");
+		close(pollfd->fd);
+		return -1;
+	}
+
+	for (i = 0; cmd_table[i].cmd != CMD_LIST_END; i++) {
+		if (cmd_table[i].cmd == em.command) {
+			if (cmd_table[i].func == NULL) {
+				handle_invalid_input(em.command, pollfd,
+							em.size);
+				close(pollfd->fd);
+			}
+			cmd_table[i].func(pollfd, em.size);
+			return 0;
+		}
+	}
+
+	handle_invalid_input(em.command, pollfd, em.size);
+	close(pollfd->fd);
+	return 0;
+}
+
+static void do_shutdown(int nsockets, struct pollfd *pollfds)
+{
+	int i;
+
+	unlink(SOCKPATH);
+	unlink(PIDFILE);
+
+	log_notice("evmctld exiting (pid %d)", getpid());
+
+	for (i = 0; i < nsockets; i++)
+		close(pollfds[i].fd);
+	free(pollfds);
+}
+
+static int handle_events(void)
+{
+	int rc;
+	int nsockets = 1;
+	int i, j;
+
+	struct pollfd *pollfds = calloc(1, sizeof(struct pollfd));
+
+	if (!pollfds) {
+		log_err("could not allocate memory: %m");
+		exit(1);
+	}
+
+	pollfds[0].fd = socket_desc;
+	pollfds[0].events = POLLIN|POLLPRI|POLLHUP;
+
+	while (1) {
+		if (should_exit != 0) {
+shutdown:
+			do_shutdown(nsockets, pollfds);
+			return 0;
+		}
+		rc = ppoll(pollfds, nsockets, NULL, NULL);
+		if (should_exit != 0)
+			goto shutdown;
+		if (rc < 0) {
+			log_warn("ppoll: %m");
+			continue;
+		}
+
+		if (pollfds[0].revents & POLLIN) {
+			nsockets++;
+			struct pollfd *newpollfds = realloc(pollfds,
+				nsockets * sizeof(struct pollfd));
+
+			if (!newpollfds) {
+				log_err("could not allocate memory: %m");
+				exit(1);
+			}
+			pollfds = newpollfds;
+
+			struct sockaddr_un remote;
+			socklen_t len = sizeof(remote);
+			pollfds[nsockets-1].fd = accept(pollfds[0].fd, &remote,
+							&len);
+			pollfds[nsockets-1].events = POLLIN|POLLPRI|POLLHUP;
+			pollfds[nsockets-1].revents = pollfds[0].revents;
+		}
+
+		for (i = 1; i < nsockets; i++) {
+			if (pollfds[i].revents & (POLLHUP|POLLNVAL)) {
+				close(pollfds[i].fd);
+				if (i == nsockets-1) {
+					nsockets--;
+					continue;
+				}
+				for (j = i; j < nsockets - 1; j++) {
+					pollfds[j].fd = pollfds[j+1].fd;
+					pollfds[j].events =
+						pollfds[j].events;
+					pollfds[j].revents =
+						pollfds[j].revents;
+				}
+				nsockets--;
+				i--;
+				continue;
+			}
+
+			if (pollfds[i].revents & (POLLIN|POLLPRI))
+				handle_event(&pollfds[i]);
+		}
+	}
+	return 0;
+}
+
+static int cmd_daemonize(struct command *cmd)
+{
+	pid_t pid;
+	int rc;
+
+	if (getuid() != 0) {
+		fprintf(stderr, "evmctl daemon must be started as root");
+		exit(1);
+	}
+
+	check_socket();
+
+	openlog("evmctld", LOG_PID, LOG_DAEMON);
+
+	if ((pid = fork())) {
+		sleep(2);
+		return 0;
+	}
+
+	daemon_mode = true;
+
+	write_pid_file(getpid());
+
+	/* Load engine */
+	if (engine_id) {
+		engine = load_engine();
+		if (engine == NULL) {
+			log_err("Failed to load engine\n");
+			exit(1);
+		}
+	}
+
+	log_notice("evmctld starting(pid %d)", getpid());
+
+	/* Handle stdio, stdout and stderr */
+	daemon_setup_std_descriptors();
+	prctl(PR_SET_NAME, "evmctld", 0, 0, 0);
+	setsid();
+
+	struct sigaction sa = {
+		.sa_handler = quit_handler,
+	};
+	sigaction(SIGQUIT, &sa, NULL);
+	sigaction(SIGINT, &sa, NULL);
+	sigaction(SIGTERM, &sa, NULL);
+
+	chdir("/");
+
+	set_up_socket();
+
+	/* TODO: Handling related to password functions */
+
+	rc = handle_events();
+	return 0;
+}
+
 static int cmd_ima_sig_import(struct command *cmd)
 {
 	char *rawsigfile, *file, *key;
@@ -1895,7 +2624,7 @@ static int cmd_hmac_evm(struct command *cmd)
 	key = keyfile ? : "/etc/keys/privkey_evm.pem";
 
 	if (digsig) {
-		err = sign_ima(file, key);
+		err = sign_ima(file, key, NULL);
 		if (err)
 			return err;
 	}
@@ -2012,6 +2741,7 @@ struct command cmds[] = {
 	{"hmac", cmd_hmac_evm, 0, "[--imahash | --imasig ] file", "Sign file metadata with HMAC using symmetric key (for testing purpose).\n"},
 #endif
 	{"ima_sig_import", cmd_ima_sig_import, 0, "[--x509] [-a hashalgo] [-k pubkey] signature file", "Make IMA signature from externally signed raw signature blob.\n"},
+	{"daemonize", cmd_daemonize, 0, "", "Start evmctl daemon for signing\n"},
 	{0, 0, 0, NULL}
 };
 
@@ -2132,20 +2862,6 @@ int main(int argc, char *argv[])
 		goto out;
 	}
 
-	if (token_label) {
-		char *temp_key = strdup(keyfile);
-		err = parse_token_label(token_label, temp_key, &keyfile);
-		free(temp_key);
-		if (err)
-			goto out;
-	}
-
-	if (engine_id) {
-		engine = load_engine();
-		if (engine == NULL)
-			goto out;
-	}
-
 	err = call_command(cmds, argv[optind++]);
 
 	if (err) {
-- 
1.8.3.1



More information about the kernel mailing list