From fd3935d721a43141254f86864b195a6a6ceedd77 Mon Sep 17 00:00:00 2001 From: Christian Hesse Date: Fri, 14 Mar 2014 14:30:16 +0100 Subject: replace scripts with a C program --- udev/Makefile | 15 ++++ udev/ykfde | 17 ---- udev/ykfde.c | 252 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 267 insertions(+), 17 deletions(-) create mode 100644 udev/Makefile delete mode 100755 udev/ykfde create mode 100644 udev/ykfde.c (limited to 'udev') 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include + +#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: -- cgit v1.2.3-54-g00ecf