aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNatanael Copa <ncopa@alpinelinux.org>2015-11-25 11:09:17 +0100
committerNatanael Copa <ncopa@alpinelinux.org>2015-11-25 10:57:51 +0000
commite4af128b30855b2b29a27c2fd7580b62059bbe51 (patch)
tree8e43f89143ed1b5adb1285fa65e6fc8336f03389
parent63d9cbed3d73f9a9d88927440c098d127ebe2c92 (diff)
downloadmkinitfs-e4af128b30855b2b29a27c2fd7580b62059bbe51.tar.bz2
mkinitfs-e4af128b30855b2b29a27c2fd7580b62059bbe51.tar.xz
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.
-rw-r--r--Makefile6
-rw-r--r--nlplug-findfs.c133
2 files changed, 119 insertions, 20 deletions
diff --git a/Makefile b/Makefile
index a62c294..11c5546 100644
--- a/Makefile
+++ b/Makefile
@@ -81,9 +81,11 @@ BLKID_CFLAGS := $(shell $(PKGCONF) --cflags blkid)
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 $<
diff --git a/nlplug-findfs.c b/nlplug-findfs.c
index bdfeaaa..2413f4b 100644
--- 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,12 +193,17 @@ struct ueventconf {
char *search_device;
char *crypt_device;
char *crypt_name;
+ char crypt_devnode[256];
char *subsystem_filter;
int modalias_count;
int fork_count;
char *bootrepos;
char *apkovls;
int timeout;
+ int efd;
+ unsigned running_threads;
+ pthread_t cryptsetup_tid;
+ pthread_mutex_t cryptsetup_mutex;
};
@@ -323,14 +331,90 @@ static void start_lvm2(char *devnode)
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 @@ static int dispatch_uevent(struct uevent *ev, struct ueventconf *conf)
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 main(int argc, char *argv[])
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 @@ int main(int argc, char *argv[])
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 @@ int main(int argc, char *argv[])
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 @@ int main(int argc, char *argv[])
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,