aboutsummaryrefslogtreecommitdiffstats
path: root/bin
diff options
context:
space:
mode:
authorGravatar Christian Hesse <mail@eworm.de>2016-01-16 02:13:22 +0100
committerGravatar Christian Hesse <mail@eworm.de>2016-01-16 02:26:21 +0100
commit40a0f31f1838d4774ebd960640bfb230dc562ea1 (patch)
treed2997424c305efd41a210432f1fad8aa9f6d88ee /bin
parentf4c501575f6c6119acc8177406612bdfafab3cb3 (diff)
downloadmkinitcpio-ykfde-40a0f31f1838d4774ebd960640bfb230dc562ea1.tar.gz
mkinitcpio-ykfde-40a0f31f1838d4774ebd960640bfb230dc562ea1.tar.zst
We have support for second factor. Yeah!
Diffstat (limited to 'bin')
-rw-r--r--bin/Makefile6
-rw-r--r--bin/ykfde.c230
2 files changed, 169 insertions, 67 deletions
diff --git a/bin/Makefile b/bin/Makefile
index 6e77de8..25faea9 100644
--- a/bin/Makefile
+++ b/bin/Makefile
@@ -3,12 +3,12 @@ CC := gcc
INSTALL := install
RM := rm
# flags
-CFLAGS += -std=c11 -O2 -fpic -pie -Wall -Werror
+CFLAGS += -std=gnu11 -O2 -fpic -pie -Wall -Werror
all: ykfde ykfde-cpio
-ykfde: ykfde.c ../config.h
- $(CC) $(CFLAGS) -lykpers-1 -lyubikey -liniparser -lcryptsetup $(LDFLAGS) -o ykfde ykfde.c
+ykfde: ykfde.c ../config.h ../version.h
+ $(CC) $(CFLAGS) -lcryptsetup -liniparser -lkeyutils -lykpers-1 -lyubikey $(LDFLAGS) -o ykfde ykfde.c
ykfde-cpio: ykfde-cpio.c ../config.h
$(CC) $(CFLAGS) -larchive $(LDFLAGS) -o ykfde-cpio ykfde-cpio.c
diff --git a/bin/ykfde.c b/bin/ykfde.c
index 075a7a2..cace8b2 100644
--- a/bin/ykfde.c
+++ b/bin/ykfde.c
@@ -5,17 +5,11 @@
* of the GNU General Public License, incorporated herein by reference.
*
* compile with:
- * $ gcc -o ykfde ykfde.c -lykpers-1 -lyubikey -lcryptsetup -liniparser
+ * $ gcc -o ykfde ykfde.c -lcryptsetup -liniparser -lkeyutils -lykpers-1 -lyubikey
*/
-#ifndef _XOPEN_SOURCE
-# define _XOPEN_SOURCE
-# ifndef _XOPEN_SOURCE_EXTENDED
-# define _XOPEN_SOURCE_EXTENDED
-# endif
-#endif
-
#include <fcntl.h>
+#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
@@ -24,6 +18,8 @@
#include <iniparser.h>
+#include <keyutils.h>
+
#include <yubikey.h>
#include <ykpers-1/ykdef.h>
#include <ykpers-1/ykcore.h>
@@ -31,18 +27,40 @@
#include <libcryptsetup.h>
#include "../config.h"
+#include "../version.h"
-/* challenge is 64 byte,
- * HMAC-SHA1 response is 40 byte */
-#define CHALLENGELEN 64
+#define PROGNAME "ykfde"
+
+/* 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
+const static char optstring[] = "hn:s:V";
+const static struct option options_long[] = {
+ /* name has_arg flag val */
+ { "help", no_argument, NULL, 'h' },
+ { "2nd-factor", required_argument, NULL, 's' },
+ { "new-2nd-factor", required_argument, NULL, 'n' },
+ { "version", no_argument, NULL, 'V' },
+ { 0, 0, 0, 0 }
+};
+
int main(int argc, char **argv) {
+ unsigned int version = 0, help = 0;
char challenge_old[CHALLENGELEN + 1],
+ challenge_old_no_2f[CHALLENGELEN + 1],
challenge_new[CHALLENGELEN + 1],
- repose_old[RESPONSELEN],
- repose_new[RESPONSELEN],
+ response_old[RESPONSELEN],
+ response_new[RESPONSELEN],
passphrase_old[PASSPHRASELEN + 1],
passphrase_new[PASSPHRASELEN + 1];
char challengefilename[sizeof(CHALLENGEDIR) + 11 /* "/challenge-" */ + 10 /* unsigned int in char */ + 1],
@@ -54,9 +72,15 @@ int main(int argc, char **argv) {
/* cryptsetup */
const char * device_name;
int8_t luks_slot = -1;
- struct crypt_device *cd;
- crypt_status_info cs;
- crypt_keyslot_info ck;
+ struct crypt_device *cryptdevice;
+ crypt_status_info cryptstatus;
+ crypt_keyslot_info cryptkeyslot;
+ /* keyutils */
+ int second_factor = 1;
+ key_serial_t key;
+ void * payload = NULL;
+ char * new_2nd_factor = NULL;
+ size_t plen = 0;
/* yubikey */
YK_KEY * yk;
uint8_t yk_slot = SLOT_CHAL_HMAC2;
@@ -64,16 +88,47 @@ int main(int argc, char **argv) {
/* iniparser */
dictionary * ini;
char section_ykslot[10 /* unsigned int in char */ + 1 + sizeof(CONFYKSLOT) + 1];
- char section_luksslot[10 /* unsigned int in char */ + 1 + sizeof(CONFLUKSSLOT) + 1];
+ char section_luksslot[10 + 1 + sizeof(CONFLUKSSLOT) + 1];
+
+ /* get command line options */
+ while ((i = getopt_long(argc, argv, optstring, options_long, NULL)) != -1)
+ switch (i) {
+ case 'h':
+ help++;
+ break;
+ case 'n':
+ new_2nd_factor = strdup(optarg);
+ memset(optarg, '*', strlen(optarg));
+ break;
+ case 's':
+ payload = strdup(optarg);
+ memset(optarg, '*', strlen(optarg));
+ break;
+ case 'V':
+ version++;
+ break;
+ }
+
+ if (version > 0)
+ printf("%s: %s v%s (compiled: " __DATE__ ", " __TIME__ ")\n", argv[0], PROGNAME, VERSION);
+
+ if (help > 0)
+ fprintf(stderr, "usage: %s [-h] [-n <new-2nd-factor>] [-s <2nd-factor>] [-V]\n", argv[0]);
+
+ if (version > 0 || help > 0)
+ return EXIT_SUCCESS;
+
/* initialize random seed */
gettimeofday(&tv, NULL);
srand(tv.tv_usec * tv.tv_sec);
+ /* initialize static buffers */
memset(challenge_old, 0, CHALLENGELEN + 1);
+ memset(challenge_old_no_2f, 0, CHALLENGELEN + 1);
memset(challenge_new, 0, CHALLENGELEN + 1);
- memset(repose_old, 0, RESPONSELEN);
- memset(repose_new, 0, RESPONSELEN);
+ memset(response_old, 0, RESPONSELEN);
+ memset(response_new, 0, RESPONSELEN);
memset(passphrase_old, 0, PASSPHRASELEN + 1);
memset(passphrase_new, 0, PASSPHRASELEN + 1);
@@ -130,30 +185,32 @@ int main(int argc, char **argv) {
luks_slot = iniparser_getint(ini, section_luksslot, luks_slot);
if (luks_slot < 0) {
rc = EXIT_FAILURE;
- fprintf(stderr, "Please set LUKS key slot for Yubikey with serial %d!\n", serial);
- printf("Add something like this to " CONFIGFILE ":\n\n[%d]\nluks slot = 1\n", serial);
+ fprintf(stderr, "Please set LUKS key slot for Yubikey with serial %d!\n"
+ "Add something like this to " CONFIGFILE ":\n\n"
+ "[%d]\nluks slot = 1\n", serial, serial);
goto out40;
}
+ if (payload == NULL) {
+ /* get second factor from key store */
+ if ((key = request_key("user", "ykfde-2f", NULL, 0)) < 0)
+ fprintf(stderr, "Failed requesting key. That's ok if you do not use\n"
+ "second factor. Give it manually if required.\n");
+
+ if (key > -1) {
+ /* if we have a key id we have a key - so this should succeed */
+ if ((rc = keyctl_read_alloc(key, &payload)) < 0) {
+ perror("Failed reading payload from key");
+ goto out40;
+ }
+ } else
+ payload = strdup("");
+ }
+
/* get random number and limit to printable ASCII character (32 to 126) */
for(i = 0; i < CHALLENGELEN; i++)
challenge_new[i] = (rand() % (126 - 32)) + 32;
- /* do challenge/response and encode to hex */
- if ((rc = yk_challenge_response(yk, yk_slot, true,
- CHALLENGELEN, (unsigned char *)challenge_new,
- RESPONSELEN, (unsigned char *)repose_new)) < 0) {
- perror("yk_challenge_response() failed");
- goto out40;
- }
- yubikey_hex_encode((char *)passphrase_new, (char *)repose_new, 20);
-
- /* initialize crypt device */
- if ((rc = crypt_init_by_name(&cd, device_name)) < 0) {
- fprintf(stderr, "Device %s failed to initialize.\n", device_name);
- goto out40;
- }
-
/* these are the filenames for challenge
* we need this for reading and writing */
sprintf(challengefilename, CHALLENGEDIR "/challenge-%d", serial);
@@ -162,29 +219,53 @@ int main(int argc, char **argv) {
/* write new challenge to file */
if ((rc = challengefiletmp = mkstemp(challengefiletmpname)) < 0) {
fprintf(stderr, "Could not open file %s for writing.\n", challengefiletmpname);
- goto out50;
+ goto out40;
}
if ((rc = write(challengefiletmp, challenge_new, CHALLENGELEN)) < 0) {
fprintf(stderr, "Failed to write challenge to file.\n");
- goto out60;
+ goto out50;
}
challengefiletmp = close(challengefiletmp);
+ /* now that the new challenge has been written to file...
+ * add second factor to new challenge */
+ if (second_factor) {
+ const char * tmp = new_2nd_factor ? new_2nd_factor : payload;
+ plen = strlen(tmp);
+ memcpy(challenge_new, tmp, plen < CHALLENGELEN / 2 ? plen : CHALLENGELEN / 2);
+ }
+
+ /* do challenge/response and encode to hex */
+ if ((rc = yk_challenge_response(yk, yk_slot, true,
+ CHALLENGELEN, (unsigned char *) challenge_new,
+ RESPONSELEN, (unsigned char *) response_new)) < 0) {
+ perror("yk_challenge_response() failed");
+ goto out50;
+ }
+ yubikey_hex_encode((char *) passphrase_new, (char *) response_new, SHA1_DIGEST_SIZE);
+
/* get status of crypt device
* We expect this to be active (or busy). It is the actual root device, no? */
- cs = crypt_status(cd, device_name);
- if (cs != CRYPT_ACTIVE && cs != CRYPT_BUSY) {
+ cryptstatus = crypt_status(cryptdevice, device_name);
+ if (cryptstatus != CRYPT_ACTIVE && cryptstatus != CRYPT_BUSY) {
rc = EXIT_FAILURE;
fprintf(stderr, "Device %s is invalid or inactive.\n", device_name);
goto out60;
}
- ck = crypt_keyslot_status(cd, luks_slot);
- if (ck == CRYPT_SLOT_INVALID) {
+ /* initialize crypt device */
+ if ((rc = crypt_init_by_name(&cryptdevice, device_name)) < 0) {
+ fprintf(stderr, "Device %s failed to initialize.\n", device_name);
+ goto out60;
+ }
+
+ cryptkeyslot = crypt_keyslot_status(cryptdevice, luks_slot);
+
+ if (cryptkeyslot == CRYPT_SLOT_INVALID) {
rc = EXIT_FAILURE;
fprintf(stderr, "Key slot %d is invalid.\n", luks_slot);
goto out60;
- } else if (ck == CRYPT_SLOT_ACTIVE || ck == CRYPT_SLOT_ACTIVE_LAST) {
+ } else if (cryptkeyslot == CRYPT_SLOT_ACTIVE || cryptkeyslot == CRYPT_SLOT_ACTIVE_LAST) {
/* read challenge from file */
if ((rc = challengefile = open(challengefilename, O_RDONLY)) < 0) {
perror("Failed opening challenge file for reading");
@@ -199,20 +280,38 @@ int main(int argc, char **argv) {
challengefile = close(challengefile);
/* finished reading challenge */
- /* do challenge/response and encode to hex */
- if ((rc = yk_challenge_response(yk, yk_slot, true,
- CHALLENGELEN, (unsigned char *)challenge_old,
- RESPONSELEN, (unsigned char *)repose_old)) < 0) {
- perror("yk_challenge_response() failed");
- goto out60;
+ /* create a copy for second run in loop */
+ memcpy(challenge_old_no_2f, challenge_old, CHALLENGELEN);
+
+ /* copy the second factor */
+ if (second_factor) {
+ plen = strlen(payload);
+ memcpy(challenge_old, payload, plen < CHALLENGELEN / 2 ? plen : CHALLENGELEN / 2);
}
- yubikey_hex_encode((char *)passphrase_old, (char *)repose_old, 20);
- if ((rc = crypt_keyslot_change_by_passphrase(cd, luks_slot, luks_slot,
- passphrase_old, PASSPHRASELEN,
- passphrase_new, PASSPHRASELEN)) < 0) {
- fprintf(stderr, "Could not update passphrase for key slot %d.\n", luks_slot);
- goto out60;
+ /* try old with and without 2nd factor */
+ for (uint8_t i = 0; i < 1 + second_factor; i++) {
+ /* do challenge/response and encode to hex */
+ if ((rc = yk_challenge_response(yk, yk_slot, true,
+ CHALLENGELEN, (unsigned char *) challenge_old,
+ RESPONSELEN, (unsigned char *) response_old)) < 0) {
+ perror("yk_challenge_response() failed");
+ goto out60;
+ }
+ yubikey_hex_encode((char *) passphrase_old, (char *) response_old, SHA1_DIGEST_SIZE);
+
+ if ((rc = crypt_keyslot_change_by_passphrase(cryptdevice, luks_slot, luks_slot,
+ passphrase_old, PASSPHRASELEN,
+ passphrase_new, PASSPHRASELEN)) < 0) {
+ fprintf(stderr, "Could not update passphrase for key slot %d on %s try.\n",
+ luks_slot, i ? "second" : "first");
+ if (!second_factor || i > 0)
+ goto out60;
+ } else
+ break;
+
+ /* copy back... */
+ memcpy(challenge_old, challenge_old_no_2f, CHALLENGELEN);
}
if ((rc = unlink(challengefilename)) < 0) {
@@ -220,7 +319,7 @@ int main(int argc, char **argv) {
goto out60;
}
} else { /* ck == CRYPT_SLOT_INACTIVE */
- if ((rc = crypt_keyslot_add_by_passphrase(cd, luks_slot, NULL, 0,
+ if ((rc = crypt_keyslot_add_by_passphrase(cryptdevice, luks_slot, NULL, 0,
passphrase_new, PASSPHRASELEN)) < 0) {
fprintf(stderr, "Could add passphrase for key slot %d.\n", luks_slot);
goto out60;
@@ -235,18 +334,18 @@ int main(int argc, char **argv) {
rc = EXIT_SUCCESS;
out60:
+ /* free crypt context */
+ crypt_free(cryptdevice);
+
+out50:
/* close the challenge file */
if (challengefile)
close(challengefile);
if (challengefiletmp)
close(challengefiletmp);
- if (access(challengefiletmpname, F_OK) == 0 )
+ if (access(challengefiletmpname, F_OK) == 0)
unlink(challengefiletmpname);
-out50:
- /* free crypt context */
- crypt_free(cd);
-
out40:
/* close Yubikey */
if (!yk_close_key(yk))
@@ -261,17 +360,20 @@ out20:
/* free iniparser dictionary */
iniparser_freedict(ini);
-
out10:
/* wipe response (cleartext password!) from memory */
/* This is statically allocated and always save to wipe! */
memset(challenge_old, 0, CHALLENGELEN + 1);
+ memset(challenge_old_no_2f, 0, CHALLENGELEN + 1);
memset(challenge_new, 0, CHALLENGELEN + 1);
- memset(repose_old, 0, RESPONSELEN);
- memset(repose_new, 0, RESPONSELEN);
+ memset(response_old, 0, RESPONSELEN);
+ memset(response_new, 0, RESPONSELEN);
memset(passphrase_old, 0, PASSPHRASELEN + 1);
memset(passphrase_new, 0, PASSPHRASELEN + 1);
+ free(new_2nd_factor);
+ free(payload);
+
return rc;
}