summaryrefslogtreecommitdiffstats
path: root/udev
diff options
context:
space:
mode:
Diffstat (limited to 'udev')
-rw-r--r--udev/Makefile15
-rwxr-xr-xudev/ykfde17
-rw-r--r--udev/ykfde.c252
3 files changed, 267 insertions, 17 deletions
diff --git a/udev/Makefile b/udev/Makefile
new file mode 100644
index 0000000..ff4ed5a
--- /dev/null
+++ b/udev/Makefile
@@ -0,0 +1,15 @@
+CC := gcc
+INSTALL := install
+RM := rm
+
+all: ykfde
+
+ykfde: ykfde.c
+ $(CC) $(FLAGS) -lykpers-1 -lyubikey -liniparser $(LDFLAGS) -o ykfde ykfde.c
+
+install: ykfde
+ $(INSTALL) -D -m0644 20-ykfde.rules $(DESTDIR)/usr/lib/initcpio/udev/20-ykfde.rules
+ $(INSTALL) -D -m0755 ykfde $(DESTDIR)/usr/lib/udev/ykfde
+
+clean:
+ $(RM) -f ykfde
diff --git a/udev/ykfde b/udev/ykfde
deleted file mode 100755
index 4d3af57..0000000
--- a/udev/ykfde
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/sh
-
-source /etc/ykfde.conf
-
-# Looks like Yubikey is reset after challenge response, triggering
-# yet another add event. Ignore if the file exists.
-[ -s /crypto_keyfile.bin ] && exit 0
-
-# write the response to keyfile
-ykchalresp -${YKFDE_SLOT:-2} "$(cat /ykfde-challenge)" 2>/dev/null | tr -d '\n' > /crypto_keyfile.bin
-
-# if the systemd unit was faster try to answer password agent
-for REQUEST in $(ls -1 /run/systemd/ask-password/ask.* || \
- inotifywait --quiet --format %w%f --event MOVED_TO --timeout 2 /run/systemd/ask-password/); do
- grep -q '^Message=Please enter passphrase for disk' ${REQUEST} || exit 1
- /usr/lib/systemd/systemd-reply-password 1 $(grep '^Socket=' ${REQUEST} | cut -d= -f2) < /crypto_keyfile.bin
-done
diff --git a/udev/ykfde.c b/udev/ykfde.c
new file mode 100644
index 0000000..8e92ba9
--- /dev/null
+++ b/udev/ykfde.c
@@ -0,0 +1,252 @@
+/*
+ * (C) 2014 by Christian Hesse <mail@eworm.de>
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU General Public License, incorporated herein by reference.
+ *
+ * compile with:
+ * $ gcc -o yk_test yk_test.c -lykpers-1 -lyubikey -liniparser
+ *
+ * test woth:
+ * $ systemd-ask-password --no-tty "Please enter passphrase for disk foobar..."
+ */
+
+#include <dirent.h>
+#include <errno.h>
+#include <stddef.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/inotify.h>
+#include <sys/poll.h>
+#include <sys/signalfd.h>
+#include <sys/socket.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <yubikey.h>
+#include <ykpers-1/ykdef.h>
+#include <ykpers-1/ykcore.h>
+
+#include <iniparser.h>
+
+#define EVENT_SIZE (sizeof (struct inotify_event))
+#define EVENT_BUF_LEN (1024 * (EVENT_SIZE + 16))
+
+#define ASK_PATH "/run/systemd/ask-password/"
+#define ASK_MESSAGE "Please enter passphrase for disk"
+
+#define CONFIGFILE "/etc/ykfde.conf"
+#define CHALLENGEFILE "/ykfde-challenge"
+
+static int send_on_socket(int fd, const char *socket_name, const void *packet, size_t size) {
+ union {
+ struct sockaddr sa;
+ struct sockaddr_un un;
+ } sa = {
+ .un.sun_family = AF_UNIX,
+ };
+
+ strncpy(sa.un.sun_path, socket_name, sizeof(sa.un.sun_path));
+
+ if (sendto(fd, packet, size, MSG_NOSIGNAL, &sa.sa, offsetof(struct sockaddr_un, sun_path) + strlen(socket_name)) < 0) {
+ perror("sendto() failed");
+ return EXIT_FAILURE;
+ }
+
+ return EXIT_SUCCESS;
+}
+
+static int try_answer(char * ask_file, char * response) {
+ int8_t ret = EXIT_FAILURE;
+ dictionary * ini;
+ char * ask_message, * ask_socket;
+ int fd_askpass;
+
+ if ((ini = iniparser_load(ask_file)) == NULL)
+ perror("cannot parse file");
+
+ ask_message = iniparser_getstring(ini, "Ask:Message", NULL);
+
+ if (strncmp(ask_message, ASK_MESSAGE, strlen(ASK_MESSAGE)) != 0)
+ goto out1;
+
+ ask_socket = iniparser_getstring(ini, "Ask:Socket", NULL);
+
+ if ((fd_askpass = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
+ perror("socket() failed");
+ goto out1;
+ }
+
+ if (send_on_socket(fd_askpass, ask_socket, response, strlen(response)) < 0) {
+ perror("send_on_socket() failed");
+ goto out2;
+ }
+
+ ret = EXIT_SUCCESS;
+
+out2:
+ close(fd_askpass);
+
+out1:
+ iniparser_freedict(ini);
+
+ return ret;
+}
+
+int main(int argc, char **argv) {
+ int8_t ret = EXIT_FAILURE;
+ /* Yubikey */
+ YK_KEY * yk;
+ uint8_t slot = SLOT_CHAL_HMAC2;
+ unsigned char response[64];
+ unsigned char response_hex[(SHA1_MAX_BLOCK_SIZE * 2) + 1];
+ char response_askpass[(SHA1_MAX_BLOCK_SIZE * 2) + 2];
+ /* iniparser */
+ dictionary * ini;
+ /* read challenge */
+ size_t fsize;
+ char * challenge;
+ FILE * challengefile;
+ /* read dir */
+ DIR * dir;
+ struct dirent * ent;
+ /* inotify */
+ struct inotify_event * event;
+ int fd_inotify, watch, length, i = 0;
+ char buffer[EVENT_BUF_LEN];
+
+ /* check if challenge file exists */
+ if (access(CHALLENGEFILE, R_OK) == -1)
+ goto out10;
+
+ /* read challenge from file */
+ if ((challengefile = fopen(CHALLENGEFILE, "r")) == NULL) {
+ perror("Failed opening challenge file for reading");
+ goto out10;
+ }
+ fseek(challengefile, 0, SEEK_END);
+ fsize = ftell(challengefile);
+ fseek(challengefile, 0, SEEK_SET);
+
+ if ((challenge = malloc(fsize + 1)) == NULL) {
+ perror("malloc() failed");
+ goto out20;
+ }
+
+ if ((fread(challenge, fsize, 1, challengefile)) != 1) {
+ perror("Failed reading challenge from file");
+ goto out30;
+ }
+ challenge[fsize] = 0;
+
+ /* try to read config file */
+ if ((ini = iniparser_load(CONFIGFILE)) != NULL) {
+ slot = iniparser_getint(ini, "general:Slot", slot);
+
+ switch (slot) {
+ case '1':
+ slot = SLOT_CHAL_HMAC1;
+ break;
+ default: /* slot 2 is default */
+ slot = SLOT_CHAL_HMAC2;
+ break;
+ }
+
+ iniparser_freedict(ini);
+ }
+
+ if (!yk_init()) {
+ perror("yk_init() failed");
+ goto out30;
+ }
+
+ if ((yk = yk_open_first_key()) == NULL) {
+ perror("yk_open_first_key() failed");
+ goto out40;
+ }
+
+ memset(response, 0, sizeof(response));
+
+ if (!yk_challenge_response(yk, slot, 0, strlen(challenge), (unsigned char *)challenge, sizeof(response), response)) {
+ perror("yk_challenge_response() failed");
+ goto out50;
+ }
+ yubikey_hex_encode((char *)response_hex, (char *)response, 20);
+
+ sprintf(response_askpass, "+%s", response_hex);
+
+ /* change to directory so we do not have to assemble complete path */
+ if (chdir(ASK_PATH) != 0) {
+ perror("chdir() failed");
+ goto out50;
+ }
+
+ /* is the request already there? */
+ if ((dir = opendir(ASK_PATH)) != NULL) {
+ while ((ent = readdir(dir)) != NULL) {
+ if (strncmp(ent->d_name, "ask.", 4) == 0) {
+ if ((ret = try_answer(ent->d_name, response_askpass)) == EXIT_SUCCESS)
+ goto out60;
+ }
+ }
+ } else {
+ perror ("opendir() failed");
+ goto out50;
+ }
+
+ /* creating the INOTIFY instance */
+ if ((fd_inotify = inotify_init()) < 0) {
+ perror("inotify_init() failed");
+ goto out60;
+ }
+
+ /* adding ASK_PATH directory into watch list */
+ watch = inotify_add_watch(fd_inotify, ASK_PATH, IN_MOVED_TO);
+
+ /* read to determine the event change happens. Actually this read blocks until the change event occurs */
+ if ((length = read(fd_inotify, buffer, EVENT_BUF_LEN)) < 0) {
+ perror("read() failed");
+ goto out70;
+ }
+
+ /*actually read return the list of change events happens. Here, read the change event one by one and process it accordingly.*/
+ while (i < length) {
+ event = (struct inotify_event *)&buffer[i];
+ if (event->len > 0)
+ if ((ret = try_answer(event->name, response_askpass)) == EXIT_SUCCESS)
+ goto out70;
+ i += EVENT_SIZE + event->len;
+ }
+
+out70:
+ inotify_rm_watch(fd_inotify, watch);
+ close(fd_inotify);
+
+out60:
+ closedir(dir);
+
+out50:
+ if (!yk_close_key(yk))
+ perror("yk_close_key() failed");
+
+out40:
+ if (!yk_release())
+ perror("yk_release() failed");
+
+out30:
+ if (challenge != NULL)
+ free(challenge);
+
+out20:
+ fclose(challengefile);
+ unlink(CHALLENGEFILE);
+
+out10:
+ return ret;
+}
+
+// vim: set syntax=c: