summaryrefslogtreecommitdiffstats
path: root/udev
diff options
context:
space:
mode:
Diffstat (limited to 'udev')
-rw-r--r--udev/20-ykfde.rules2
-rw-r--r--udev/Makefile18
-rw-r--r--udev/ykfde.c418
3 files changed, 1 insertions, 437 deletions
diff --git a/udev/20-ykfde.rules b/udev/20-ykfde.rules
index 2bd67e2..6973819 100644
--- a/udev/20-ykfde.rules
+++ b/udev/20-ykfde.rules
@@ -25,4 +25,4 @@
ACTION=="add", SUBSYSTEM=="usb", ENV{DEVTYPE}=="usb_device", \
ATTRS{idVendor}=="1050", \
ATTRS{idProduct}=="0010|0110|0111|0114|0116|0401|0403|0405|0407|0410", \
- RUN+="/usr/lib/udev/ykfde"
+ RUN+="/usr/lib/ykfde/worker"
diff --git a/udev/Makefile b/udev/Makefile
deleted file mode 100644
index 3a63ec4..0000000
--- a/udev/Makefile
+++ /dev/null
@@ -1,18 +0,0 @@
-# commands
-CC := gcc
-INSTALL := install
-RM := rm
-# flags
-CFLAGS += -std=c11 -O2 -fPIC -Wall -Werror
-LDFLAGS += -Wl,-z,now -Wl,-z,relro -pie
-
-all: ykfde
-
-ykfde: ykfde.c ../config.h
- $(CC) $(CFLAGS) -liniparser -lkeyutils -lykpers-1 -lyubikey $(LDFLAGS) -o ykfde ykfde.c
-
-install: ykfde
- $(INSTALL) -D -m0755 ykfde $(DESTDIR)/usr/lib/udev/ykfde
-
-clean:
- $(RM) -f ykfde
diff --git a/udev/ykfde.c b/udev/ykfde.c
deleted file mode 100644
index ef0cefc..0000000
--- a/udev/ykfde.c
+++ /dev/null
@@ -1,418 +0,0 @@
-/*
- * (C) 2014-2017 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 ykfde ykfde.c -liniparser -lkeyutils -lykpers-1 -lyubikey
- *
- * test with:
- * $ systemd-ask-password --no-tty "Please enter passphrase for disk foobar..."
- */
-
-#include <dirent.h>
-#include <errno.h>
-#include <fcntl.h>
-#include <stddef.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/poll.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <iniparser.h>
-
-#include <keyutils.h>
-
-#include <yubikey.h>
-#include <ykpers-1/ykdef.h>
-#include <ykpers-1/ykcore.h>
-
-#include "../config.h"
-
-/* Yubikey supports write of 64 byte challenge to slot,
- * returns HMAC-SHA1 response.
- *
- * Lengths are defined in ykpers-1/ykdef.h:
- * SHA1_MAX_BLOCK_SIZE 64
- * SHA1_DIGEST_SIZE 20
- *
- * For passphrase we use hex encoded digest, that is
- * twice the length of binary digest. */
-#define CHALLENGELEN SHA1_MAX_BLOCK_SIZE
-#define RESPONSELEN SHA1_MAX_BLOCK_SIZE
-#define PASSPHRASELEN SHA1_DIGEST_SIZE * 2
-
-#define ASK_PATH "/run/systemd/ask-password/"
-#define ASK_MESSAGE "Please enter passphrase for disk"
-
-/*** send_on_socket ***/
-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;
-}
-
-/*** yk_open_and_check ***/
-static YK_KEY * yk_open_and_check(const unsigned int expected, unsigned int * serial) {
- YK_KEY * yk;
-
- if ((yk = yk_open_first_key()) == NULL) {
- if (errno != EAGAIN)
- perror("yk_open_first_key() failed");
- goto out1;
- }
-
- if (serial != NULL) {
- /* read the serial number from key */
- if (yk_get_serial(yk, 0, 0, serial) == 0) {
- perror("yk_get_serial() failed");
- goto out2;
- }
-
- if (expected > 0 && expected != *serial) {
- fprintf(stderr, "Opened Yubikey with unexpected serial number (%d != %d)...\n", expected, *serial);
- goto out2;
- }
- }
-
- return yk;
-
-out2:
- /* close Yubikey */
- if (yk_close_key(yk) == 0)
- perror("yk_close_key() failed");
-
-out1:
- return NULL;
-}
-
-/*** read_challenge ***/
-static int read_challenge(const unsigned int serial, char * challenge) {
- int rc = EXIT_FAILURE;
- char challengefilename[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 1];
- int challengefile;
-
- snprintf(challengefilename, sizeof(challengefilename), CHALLENGEDIR "/challenge-%d", serial);
-
- /* check if challenge file exists */
- if (access(challengefilename, R_OK) == -1) {
- goto out1;
- }
-
- /* read challenge from file */
- if ((challengefile = open(challengefilename, O_RDONLY)) < 0) {
- perror("Failed opening challenge file for reading");
- goto out1;
- }
-
- if (read(challengefile, challenge, CHALLENGELEN) < 0) {
- perror("Failed reading challenge from file");
- goto out2;
- }
-
- rc = EXIT_SUCCESS;
-
-out2:
- close(challengefile);
-
-out1:
- return rc;
-}
-
-/*** get_second_factor ***/
-static char * get_second_factor(void) {
- key_serial_t key;
- void * payload = NULL;
-
- /* get second factor from key store
- * If this fails it is not critical... possibly we just do not
- * use second factor. */
- key = keyctl_search(KEY_SPEC_USER_KEYRING, "user", "ykfde-2f", 0);
-
- if (key > 0) {
- /* if we have a key id we have a key - so this should succeed */
- if (keyctl_read_alloc(key, &payload) < 0) {
- perror("Failed reading payload from key");
- return NULL;
- }
-
- return payload;
- }
-
- return NULL;
-}
-
-/*** get_response ***/
-static int get_response(const unsigned int serial, uint8_t slot, char * challenge, char * passphrase) {
- YK_KEY * yk;
- char response[RESPONSELEN];
- char * second_factor;
- size_t second_factor_len;
- /* iniparser */
- dictionary * ini;
- char section_ykslot[10 /* unsigned int in char */ + 1 + sizeof(CONFYKSLOT) + 1];
-
- memset(response, 0, RESPONSELEN);
-
- if ((second_factor = get_second_factor()) != NULL) {
- /* we replace part of the challenge with the second factor */
- second_factor_len = strlen(second_factor);
- memcpy(challenge, second_factor, second_factor_len < CHALLENGELEN / 2 ?
- second_factor_len : CHALLENGELEN / 2);
- memset(second_factor, 0, second_factor_len);
- free(second_factor);
- }
-
- /* try to read config file
- * If anything here fails we do not care... slot 2 is the default. */
- if ((ini = iniparser_load(CONFIGFILE)) != NULL) {
- /* first try the general setting */
- slot = iniparser_getint(ini, "general:" CONFYKSLOT, slot);
-
- sprintf(section_ykslot, "%d:" CONFYKSLOT, serial);
-
- /* then probe for setting with serial number */
- slot = iniparser_getint(ini, section_ykslot, slot);
-
- switch (slot) {
- case 1:
- case SLOT_CHAL_HMAC1:
- slot = SLOT_CHAL_HMAC1;
- break;
- case 2:
- case SLOT_CHAL_HMAC2:
- default:
- slot = SLOT_CHAL_HMAC2;
- break;
- }
-
- iniparser_freedict(ini);
- }
-
- /* open Yubikey and check serial */
- if ((yk = yk_open_and_check(serial, NULL)) == NULL) {
- fprintf(stderr, "yk_open_and_check() failed\n");
- goto out1;
- }
-
- /* do challenge/response and encode to hex */
- if (yk_challenge_response(yk, slot, true,
- CHALLENGELEN, (unsigned char *) challenge,
- RESPONSELEN, (unsigned char *) response) == 0) {
- perror("yk_challenge_response() failed");
- goto out2;
- }
-
- yubikey_hex_encode((char *) passphrase, (char *) response, SHA1_DIGEST_SIZE);
-
-out2:
- /* close Yubikey */
- if (yk_close_key(yk) == 0)
- perror("yk_close_key() failed");
-
-out1:
- memset(response, 0, RESPONSELEN);
-
- return EXIT_SUCCESS;
-}
-
-/*** add_keyring ***/
-static int add_keyring(const char * passphrase) {
- key_serial_t key;
-
- /* add key to kernel key store
- * Put it into session keyring first, set permissions and
- * move it to user keyring. */
- if ((key = add_key("user", "cryptsetup", passphrase,
- PASSPHRASELEN, KEY_SPEC_SESSION_KEYRING)) < 0) {
- perror("add_key() failed");
- return -1;
- }
-
- if (keyctl_set_timeout(key, 150) < 0) {
- perror("keyctl_set_timeout() failed");
- return -1;
- }
-
- if (keyctl_setperm(key, KEY_POS_ALL|KEY_USR_ALL) < 0) {
- perror("keyctl_setperm() failed");
- return -1;
- }
-
- if (keyctl_link(key, KEY_SPEC_USER_KEYRING) < 0) {
- perror("keyctl_link() failed");
- return -1;
- }
-
- if (keyctl_unlink(key, KEY_SPEC_SESSION_KEYRING) < 0) {
- perror("keyctl_unlink() failed");
- return -1;
- }
-
- return EXIT_SUCCESS;
-}
-
-/*** answer_askpass ***/
-static int answer_askpass(const char * ask_file, const char * passphrase) {
- int rc = EXIT_FAILURE, fd_askpass;
- const char * ask_message, * ask_socket;
- /* iniparser */
- dictionary * ini;
-
- if ((ini = iniparser_load(ask_file)) == NULL) {
- perror("cannot parse file");
- goto out1;
- }
-
- ask_message = iniparser_getstring(ini, "Ask:Message", NULL);
-
- if (strncmp(ask_message, ASK_MESSAGE, strlen(ASK_MESSAGE)) != 0)
- goto out2;
-
- if ((ask_socket = iniparser_getstring(ini, "Ask:Socket", NULL)) == NULL) {
- perror("Could not get socket name");
- goto out2;
- }
-
- if ((fd_askpass = socket(AF_UNIX, SOCK_DGRAM|SOCK_CLOEXEC|SOCK_NONBLOCK, 0)) < 0) {
- perror("socket() failed");
- goto out2;
- }
-
- if (send_on_socket(fd_askpass, ask_socket, passphrase, PASSPHRASELEN + 1) < 0) {
- perror("send_on_socket() failed");
- goto out3;
- }
-
- rc = EXIT_SUCCESS;
-
-out3:
- close(fd_askpass);
-
-out2:
- iniparser_freedict(ini);
-
-out1:
- return rc;
-}
-
-/*** walk_askpass ***/
-static int walk_askpass(const char * passphrase) {
- int rc = EXIT_FAILURE;
- DIR * dir;
- struct dirent * ent;
-
- /* change to directory so we do not have to assemble complete/absolute path */
- if (chdir(ASK_PATH) != 0) {
- perror("chdir() failed");
- return rc;
- }
-
- /* 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 ((rc = answer_askpass(ent->d_name, passphrase)) == EXIT_SUCCESS)
- goto out;
- }
- }
- } else {
- perror ("opendir() failed");
- return EXIT_FAILURE;
- }
-
- rc = EXIT_SUCCESS;
-
-out:
- closedir(dir);
-
- return rc;
-}
-
-/*** main ***/
-int main(int argc, char **argv) {
- int8_t rc = EXIT_FAILURE;
- /* Yubikey */
- YK_KEY * yk;
- uint8_t slot = SLOT_CHAL_HMAC2;
- unsigned int serial = 0;
- /* challenge and passphrase */
- char challenge[CHALLENGELEN + 1];
- char passphrase[PASSPHRASELEN + 2];
-
-#ifdef DEBUG
- /* reopening stderr to /dev/console may help debugging... */
- FILE * tmp = freopen("/dev/console", "w", stderr);
- (void) tmp;
-#endif
-
- /* initialize static memory */
- memset(challenge, 0, CHALLENGELEN + 1);
- memset(passphrase, 0, PASSPHRASELEN + 2);
-
- *passphrase = '+';
-
- /* init and open first Yubikey */
- if (yk_init() == 0) {
- perror("yk_init() failed");
- goto out10;
- }
-
- /* open Yubikey and get serial */
- if ((yk = yk_open_and_check(0, &serial)) == NULL) {
- if (errno == EAGAIN)
- rc = EXIT_SUCCESS;
- goto out30;
- }
-
- /* close Yubikey */
- if (yk_close_key(yk) == 0) {
- perror("yk_close_key() failed");
- goto out30;
- }
-
- if ((rc = read_challenge(serial, challenge)) < 0)
- goto out30;
-
- if ((rc = get_response(serial, slot, challenge, passphrase + 1)) < 0)
- goto out30;
-
- if ((rc = add_keyring(passphrase + 1)) < 0)
- goto out30;
-
- if ((rc = walk_askpass(passphrase)) < 0)
- goto out30;
-
-out30:
- /* release Yubikey */
- if (yk_release() == 0)
- perror("yk_release() failed");
-
-out10:
- /* wipe challenge from memory */
- memset(challenge, 0, CHALLENGELEN + 1);
- memset(passphrase, 0, PASSPHRASELEN + 2);
-
- return rc;
-}
-
-// vim: set syntax=c: