[systemd-devel] [PATCH] add keyscript support to cryptsetup

David Härdeman david at hardeman.nu
Thu Jun 28 15:56:04 PDT 2012


Debian's cryptsetup package supports the keyscript= option in /etc/crypttab

This patch is a first attempt at implementing support for the same option
in systemd. It is not at exact feature parity yet (environment variables
are missing and relative paths are not supported), but it's a start.

I'm not sure if the (somewhat complicated dance) with fds is considered
acceptable or if I should use something else?
---
 src/cryptsetup/cryptsetup.c |  133 ++++++++++++++++++++++++++++++++++++++-----
 1 file changed, 119 insertions(+), 14 deletions(-)

diff --git a/src/cryptsetup/cryptsetup.c b/src/cryptsetup/cryptsetup.c
index b26fcca..3547a38 100644
--- a/src/cryptsetup/cryptsetup.c
+++ b/src/cryptsetup/cryptsetup.c
@@ -19,6 +19,11 @@
   along with systemd; If not, see <http://www.gnu.org/licenses/>.
 ***/
 
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <fcntl.h>
 #include <string.h>
 #include <errno.h>
 #include <sys/mman.h>
@@ -42,6 +47,7 @@ static unsigned opt_tries = 0;
 static bool opt_readonly = false;
 static bool opt_verify = false;
 static bool opt_discards = false;
+static char *opt_keyscript = NULL;
 static usec_t opt_timeout = DEFAULT_TIMEOUT_USEC;
 
 /* Options Debian's crypttab knows we don't:
@@ -53,9 +59,86 @@ static usec_t opt_timeout = DEFAULT_TIMEOUT_USEC;
     checkargs=
     noearly=
     loud=
-    keyscript=
 */
 
+#define PIPE_READ	0
+#define PIPE_WRITE	1
+
+static int crypt_activate_by_keyscript(struct crypt_device *cd, const char *name,
+				       const char *keyscript, const char *key_file,
+				       size_t keyfile_size, uint32_t flags)
+{
+	int pipefd[2];
+	int k;
+	pid_t pid;
+
+	if (pipe(pipefd)) {
+		log_error("Failed to call pipe: %m");
+		return -errno;
+	}
+
+	/* exec the keyscript and temporarily set its stdout as stdin */
+        pid = fork();
+	if (pid < 0) {
+		k = -errno;
+		log_error("Failed to fork: %m");
+		close(pipefd[PIPE_READ]);
+		close(pipefd[PIPE_WRITE]);
+		return k;
+
+	} else if (pid > 0) {
+		int tmpfd;
+
+		close(pipefd[PIPE_WRITE]);
+		tmpfd = dup(STDIN_FILENO);
+		if (tmpfd < 0) {
+			k = -errno;
+			close(pipefd[PIPE_READ]);
+			waitpid(pid, NULL, 0);
+			return k;
+		}
+
+		if (dup2(pipefd[PIPE_READ], STDIN_FILENO) < 0) {
+			k = -errno;
+			close(pipefd[PIPE_READ]);
+			waitpid(pid, NULL, 0);
+			return k;
+		}
+
+		close(pipefd[PIPE_READ]);
+
+		k = crypt_activate_by_keyfile(cd, name, CRYPT_ANY_SLOT, "-", keyfile_size, flags);
+
+		waitpid(pid, NULL, 0);
+
+		dup2(tmpfd, STDIN_FILENO);
+		close(tmpfd);
+
+	} else if (pid == 0) {
+		const char *args[3];
+
+		close(pipefd[PIPE_READ]);
+		if (dup2(pipefd[PIPE_WRITE], STDOUT_FILENO) < 0)
+			_exit(EXIT_FAILURE);
+		close(pipefd[PIPE_WRITE]);
+
+		close(STDIN_FILENO);
+		if (open("/dev/null", O_RDWR) < 0)
+			_exit(EXIT_FAILURE);
+		if (dup2(STDIN_FILENO, STDERR_FILENO) < 0)
+			_exit(EXIT_FAILURE);
+
+		args[0] = keyscript;
+		args[1] = key_file;
+		args[2] = NULL;
+
+		execv(args[0], (char **)args);
+		_exit(EXIT_FAILURE);
+	}
+
+	return k;
+}
+
 static int parse_one_option(const char *option) {
         assert(option);
 
@@ -88,6 +171,22 @@ static int parse_one_option(const char *option) {
                 free(opt_hash);
                 opt_hash = t;
 
+	} else if (startswith(option, "keyscript=")) {
+		char *t;
+
+		if (!option[10] || !path_is_absolute(option+10)) {
+			log_error("keyscript= path is invalid, ignoring.");
+			free(opt_keyscript);
+			opt_keyscript = NULL;
+			return 0;
+		}
+
+		if (!(t = strdup(option+10)))
+			return -ENOMEM;
+
+		free(opt_keyscript);
+		opt_keyscript = t;
+
         } else if (startswith(option, "tries=")) {
 
                 if (safe_atou(option+6, &opt_tries) < 0) {
@@ -263,20 +362,23 @@ int main(int argc, char *argv[]) {
                         goto finish;
                 }
 
-                if (argc >= 5 &&
-                    argv[4][0] &&
-                    !streq(argv[4], "-") &&
-                    !streq(argv[4], "none")) {
-
-                        if (!path_is_absolute(argv[4]))
-                                log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
-                        else
-                                key_file = argv[4];
-                }
-
+		/* Options are parsed first as they can influence later parsing */
                 if (argc >= 6 && argv[5][0] && !streq(argv[5], "-"))
                         parse_options(argv[5]);
 
+		if (argc >= 5) {
+			if (opt_keyscript)
+				key_file = argv[4]; /* used as arg to keyscript */
+			else if (argv[4][0] &&
+				 !streq(argv[4], "-") &&
+				 !streq(argv[4], "none"))
+				/* do nothing */;
+			else if (!path_is_absolute(argv[4]))
+				log_error("Password file path %s is not absolute. Ignoring.", argv[4]);
+			else
+				key_file = argv[4];
+		}
+
                 /* A delicious drop of snake oil */
                 mlockall(MCL_FUTURE);
 
@@ -461,8 +563,10 @@ int main(int argc, char *argv[]) {
                                  crypt_get_volume_key_size(cd)*8,
                                  argv[3]);
 
-                        if (key_file)
-                                k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, keyfile_size, flags);
+			if (opt_keyscript)
+				k = crypt_activate_by_keyscript(cd, argv[2], opt_keyscript, key_file, keyfile_size, flags);
+			else if (key_file)
+				k = crypt_activate_by_keyfile(cd, argv[2], CRYPT_ANY_SLOT, key_file, keyfile_size, flags);
                         else {
                                 char **p;
 
@@ -522,6 +626,7 @@ finish:
         if (cd)
                 crypt_free(cd);
 
+	free(opt_keyscript);
         free(opt_cipher);
         free(opt_hash);
 



More information about the systemd-devel mailing list