home: hub: mkinitfs

Download patch

ref: e4af128b30855b2b29a27c2fd7580b62059bbe51
parent: 63d9cbed3d73f9a9d88927440c098d127ebe2c92
author: Natanael Copa <ncopa@alpinelinux.org>
date: Wed Nov 25 05:09:17 CST 2015

nlplug-findfs: fix cryptsetup race condition

We need run cryptsetup in parallel so that keyboard drivers are loaded
while waiting for password input.

But cryptsetup will recreate the device which means that the uevent for
new device node will first be added then changed and finally will it
create the /dev/mapper/* device node. We handle the first generated
uevent and while handling, the device node might have disappeared
causeing blkid not find any UUID, and the /dev/mapper/* does not yet
exist.

This means that we need to:
- handle uevents in parallel while waiting for password input
- block uevent handling while actually setting up the crypt device

So we use libcryptsetup and add a mutex while setting up the crypt
device.

--- a/Makefile
+++ b/Makefile
@@ -81,9 +81,11 @@
 BLKID_LIBS	:= $(shell $(PKGCONF) --libs blkid)
 LIBKMOD_CFLAGS	:= $(shell $(PKGCONF) --cflags libkmod)
 LIBKMOD_LIBS	:= $(shell $(PKGCONF) --libs libkmod)
+CRYPTSETUP_CFLAGS := $(shell $(PKGCONF) --cflags libcryptsetup)
+CRYPTSETUP_LIBS	:= $(shell $(PKGCONF) --libs libcryptsetup)
 
-CFLAGS		+= $(BLKID_CFLAGS) $(LIBKMOD_CFLAGS)
-LIBS		= $(BLKID_LIBS) $(LIBKMOD_LIBS)
+CFLAGS		+= $(BLKID_CFLAGS) $(LIBKMOD_CFLAGS) $(CRYPTSETUP_CFLAGS)
+LIBS		= $(BLKID_LIBS) $(LIBKMOD_LIBS) $(CRYPTSETUP_LIBS)
 
 %.o: %.c
 	$(CC) $(CFLAGS) -o $@ -c $<
--- a/nlplug-findfs.c
+++ b/nlplug-findfs.c
@@ -20,6 +20,7 @@
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
+#include <termios.h>
 #include <unistd.h>
 
 #include <sys/eventfd.h>
@@ -35,6 +36,7 @@
 
 #include <libkmod.h>
 #include <blkid.h>
+#include <libcryptsetup.h>
 
 #include "arg.h"
 
@@ -46,6 +48,7 @@
 #define FOUND_APKOVL	0x4
 
 #define TRIGGER_THREAD		0x1
+#define CRYPTSETUP_THREAD	0x2
 
 static int dodebug;
 static char *default_envp[2];
@@ -190,6 +193,7 @@
 	char *search_device;
 	char *crypt_device;
 	char *crypt_name;
+	char crypt_devnode[256];
 	char *subsystem_filter;
 	int modalias_count;
 	int fork_count;
@@ -196,6 +200,10 @@
 	char *bootrepos;
 	char *apkovls;
 	int timeout;
+	int efd;
+	unsigned running_threads;
+	pthread_t cryptsetup_tid;
+	pthread_mutex_t cryptsetup_mutex;
 };
 
 
@@ -323,14 +331,90 @@
 	spawn_command(&spawnmgr, lvm2_argv, 0);
 }
 
-static void start_cryptsetup(char *devnode, char *cryptdm)
+
+static int read_pass(char *pass, size_t pass_size)
 {
-	char *cryptsetup_argv[] = {
-		"/sbin/cryptsetup", "luksOpen",
-		devnode, cryptdm ? cryptdm : "crypdm", NULL
-	};
+	struct termios old_flags, new_flags;
+	int r;
+
+	tcgetattr(STDIN_FILENO, &old_flags);
+	new_flags = old_flags;
+	new_flags.c_lflag &= ~ECHO;
+	new_flags.c_lflag |= ECHONL;
+
+	r = tcsetattr(STDIN_FILENO, TCSANOW, &new_flags);
+	if (r < 0) {
+		warn("tcsetattr");
+		return r;
+	}
+
+	if (fgets(pass, pass_size, stdin) == NULL) {
+		warn("fgets");
+		return -1;
+	}
+	pass[strlen(pass) - 1] = '\0';
+
+	if (tcsetattr(STDIN_FILENO, TCSANOW, &old_flags) < 0) {
+		warn("tcsetattr");
+		return r;
+	}
+
+	return 0;
+}
+
+static void *cryptsetup_thread(void *data)
+{
+	struct ueventconf *c = (struct ueventconf *)data;
+	uint64_t ok = CRYPTSETUP_THREAD;
+	struct crypt_device *cd;
+	int r, passwd_tries = 5;
+
+	r = crypt_init(&cd, c->crypt_devnode);
+	if (r < 0) {
+		warnx("crypt_init(%s)", c->crypt_devnode);
+		goto notify_out;
+	}
+
+	r = crypt_load(cd , CRYPT_LUKS1, NULL);
+	if (r < 0) {
+		warnx("crypt_load(%s)", c->crypt_devnode);
+		goto free_out;
+	}
+
+	while (passwd_tries > 0) {
+		char pass[1024];
+
+		printf("Enter passphrase for %s: ", c->crypt_devnode);
+		fflush(stdout);
+
+		if (read_pass(pass, sizeof(pass)) < 0)
+			goto free_out;
+		passwd_tries--;
+
+		pthread_mutex_lock(&c->cryptsetup_mutex);
+		r = crypt_activate_by_passphrase(cd, c->crypt_name,
+						 CRYPT_ANY_SLOT,
+						 pass, strlen(pass), 0);
+		pthread_mutex_unlock(&c->cryptsetup_mutex);
+
+		if (r == 0)
+			break;
+		printf("No key available with this passphrase.\n");
+	}
+
+free_out:
+	crypt_free(cd);
+notify_out:
+	write(c->efd, &ok, sizeof(ok));
+	return NULL;
+}
+
+static void start_cryptsetup(struct ueventconf *conf)
+{
+	dbg("starting cryptsetup %s -> %s", conf->crypt_devnode, conf->crypt_name);
 	load_kmod("dm-crypt");
-	spawn_command(&spawnmgr, cryptsetup_argv, 0);
+	pthread_create(&conf->cryptsetup_tid, NULL, cryptsetup_thread, conf);
+	conf->running_threads |= CRYPTSETUP_THREAD;
 }
 
 static int is_mounted(const char *devnode) {
@@ -621,13 +705,18 @@
 
 			snprintf(ev->devnode, sizeof(ev->devnode), "/dev/%s",
 				 ev->devname);
+			pthread_mutex_lock(&conf->cryptsetup_mutex);
 			rc = searchdev(ev, conf->search_device,
 				       conf->bootrepos, conf->apkovls);
+			pthread_mutex_unlock(&conf->cryptsetup_mutex);
 			if (rc)
 				return rc;
 
-			if (searchdev(ev, conf->crypt_device, NULL, NULL))
-				start_cryptsetup(ev->devnode, conf->crypt_name);
+			if (searchdev(ev, conf->crypt_device, NULL, NULL)) {
+				strncpy(conf->crypt_devnode, ev->devnode,
+					sizeof(conf->crypt_devnode));
+				start_cryptsetup(conf);
+			}
 		}
 	}
 	return 0;
@@ -740,7 +829,6 @@
 	int event_count = 0;
 	size_t total_bytes = 0;
 	int found = 0;
-	unsigned int running_threads = 0;
 	char *program_argv[2] = {0,0};
 	pthread_t tid;
 	sigset_t sigchldmask;
@@ -792,6 +880,10 @@
 	if (argc > 0)
 		conf.search_device = argv[0];
 
+	r = pthread_mutex_init(&conf.cryptsetup_mutex, NULL);
+	if (r < 0)
+		err(1, "pthread_mutex_init");
+
 	initsignals();
 	sigemptyset(&sigchldmask);
 	sigaddset(&sigchldmask, SIGCHLD);
@@ -805,11 +897,12 @@
 
 	fds[2].fd = eventfd(0, EFD_CLOEXEC);
 	fds[2].events = POLLIN;
+	conf.efd = fds[2].fd;
 	pthread_create(&tid, NULL, trigger_thread, &fds[2].fd);
-	running_threads |= TRIGGER_THREAD;
+	conf.running_threads |= TRIGGER_THREAD;
 
 	while (1) {
-		r = poll(fds, numfds, (spawn_active(&spawnmgr) || running_threads) ? -1 : conf.timeout);
+		r = poll(fds, numfds, (spawn_active(&spawnmgr) || conf.running_threads) ? -1 : conf.timeout);
 		if (r == -1) {
 			if (errno == EINTR || errno == ERESTART)
 				continue;
@@ -891,15 +984,19 @@
 			uint64_t tmask = 0;
 			if (read(fds[2].fd, &tmask, sizeof(tmask)) < 0)
 				warn("eventfd");
-			dbg("terminating thread %x", tmask);
-			close(fds[2].fd);
-			fds[2].fd = -1;
-			fds[2].revents = 0;
-			numfds--;
-			running_threads &= ~tmask;
-			pthread_join(tid, NULL);
+			if (tmask & TRIGGER_THREAD) {
+				dbg("terminating trigger thread");
+				pthread_join(tid, NULL);
+			}
+			if (tmask & CRYPTSETUP_THREAD) {
+				dbg("terminating cryptsetup thread");
+				pthread_join(conf.cryptsetup_tid, NULL);
+			}
+			conf.running_threads &= ~tmask;
 		}
 	}
+	close(fds[2].fd);
+	pthread_mutex_destroy(&conf.cryptsetup_mutex);
 
 	dbg("modaliases: %i, forks: %i, events: %i, total bufsize: %zu",
 		conf.modalias_count,