From d8a6ef13b36eebd23a697625cd6f962a064349d3 Mon Sep 17 00:00:00 2001 From: Frank Qin Date: Sat, 29 Apr 2023 23:16:44 -0700 Subject: [PATCH 1/7] authtok: add support for decrypting an auth token Add support for storing an encrypted auth token in u2f_keys. The auth token is passed to PAM and may be used by other PAM modules. The encryption key is derived using the FIDO2 hmac-secret extension. --- Makefile.am | 1 + authtok.c | 261 ++++++++++++++++++++++++++++++++++++++++++ authtok.h | 20 ++++ pam-u2f.c | 3 + pamu2fcfg/Makefile.am | 1 + pamu2fcfg/pamu2fcfg.c | 153 ++++++++++++++++++++++--- util.c | 50 +++++++- util.h | 2 + 8 files changed, 469 insertions(+), 22 deletions(-) create mode 100644 authtok.c create mode 100644 authtok.h diff --git a/Makefile.am b/Makefile.am index 16302c7e..97d02f5b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -26,6 +26,7 @@ libmodule_la_SOURCES += drop_privs.h libmodule_la_SOURCES += expand.c libmodule_la_SOURCES += explicit_bzero.c libmodule_la_SOURCES += util.c util.h +libmodule_la_SOURCES += authtok.c authtok.h libmodule_la_LIBADD = -lpam $(LIBFIDO2_LIBS) $(LIBCRYPTO_LIBS) pampluginexecdir = $(PAMDIR) diff --git a/authtok.c b/authtok.c new file mode 100644 index 00000000..605c1af1 --- /dev/null +++ b/authtok.c @@ -0,0 +1,261 @@ +#include +#include +#include +#include + +#include "authtok.h" +#include "b64.h" + +static int +encrypt_authtok(const unsigned char *plaintext, size_t plaintext_len, + const unsigned char *key, /* 32 bytes */ + unsigned char *tag, /* 16 bytes */ + unsigned char *ciphertext /* equal to plaintext_len */ +) { + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *cipher = NULL; + int len; + int retval = 0; + unsigned char iv[12] = {0}; + + if (!(ctx = EVP_CIPHER_CTX_new())) { + goto err; + } + if (!(cipher = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL))) { + goto err; + } + if (!EVP_EncryptInit_ex2(ctx, cipher, key, iv, NULL)) { + goto err; + } + if (plaintext_len > INT_MAX) { + goto err; + } + if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, + (int) plaintext_len)) { + goto err; + } + if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) { + goto err; + } + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_GET_TAG, 16, (void *) tag)) { + goto err; + } + retval = 1; + +err: + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(cipher); + return retval; +} + +static int +decrypt_authtok(const unsigned char *ciphertext, size_t ciphertext_len, + const unsigned char *key, /* 32 bytes */ + const unsigned char *tag, /* 16 bytes */ + unsigned char *plaintext /* equal to ciphertext_len */ +) { + EVP_CIPHER_CTX *ctx = NULL; + EVP_CIPHER *cipher = NULL; + int len; + int retval = 0; + unsigned char iv[12] = {0}; + + if (!(ctx = EVP_CIPHER_CTX_new())) { + goto err; + } + if (!(cipher = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL))) { + goto err; + } + if (!EVP_DecryptInit_ex2(ctx, cipher, key, iv, NULL)) { + goto err; + } + if (ciphertext_len > INT_MAX) { + goto err; + } + if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, + (int) ciphertext_len)) { + goto err; + } + if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_AEAD_SET_TAG, 16, (void *) tag)) { + goto err; + } + if (!EVP_DecryptFinal_ex(ctx, plaintext + len, &len)) { + goto err; + } + retval = 1; + +err: + EVP_CIPHER_CTX_free(ctx); + EVP_CIPHER_free(cipher); + return retval; +} + +int get_authtok(const fido_assert_t *assert, const char *enc_authtok, + char **authtok, size_t *authtok_len) { + unsigned char *buf = NULL; + size_t buf_len; + const unsigned char *key; + size_t key_len; + size_t stmt_count; + int ok = 0; + + *authtok = NULL; + + if (!b64_decode(enc_authtok, (void **) &buf, &buf_len) || + buf_len <= HMAC_SALT_SIZE + AEAD_TAG_SIZE) { + goto err; + } + + if ((stmt_count = fido_assert_count(assert)) != 1) { + goto err; + } + + if ((key = fido_assert_hmac_secret_ptr(assert, 0)) == NULL) { + goto err; + } + if ((key_len = fido_assert_hmac_secret_len(assert, 0)) != HMAC_SECRET_SIZE) { + goto err; + } + + *authtok_len = buf_len - HMAC_SALT_SIZE - AEAD_TAG_SIZE; + if (!(*authtok = malloc(*authtok_len + 1))) { + goto err; + } + if (!decrypt_authtok(buf + HMAC_SALT_SIZE, + buf_len - HMAC_SALT_SIZE - AEAD_TAG_SIZE, key, + buf + buf_len - AEAD_TAG_SIZE, + (unsigned char *) *authtok)) { + goto err; + } + (*authtok)[*authtok_len] = '\0'; + + ok = 1; + +err: + free(buf); + if (!ok) { + if (*authtok) { + explicit_bzero(*authtok, *authtok_len); + free(*authtok); + *authtok = NULL; + *authtok_len = 0; + } + } + return ok; +} + +static int get_hmac_secret(fido_dev_t *dev, fido_cred_t *cred, fido_opt_t uv, + const char *pin, const unsigned char *salt, + unsigned char *secret) { + fido_assert_t *assert = NULL; + unsigned char cdh[32] = {0}; + const unsigned char *kh = NULL; + const unsigned char *hmac_secret; + size_t kh_len; + size_t hmac_secret_len; + const char *id; + int retval = 0; + + if ((assert = fido_assert_new()) == NULL) { + goto err; + } + + if (!(id = fido_cred_rp_id(cred))) { + goto err; + } + + if (fido_assert_set_rp(assert, id) != FIDO_OK) { + goto err; + } + + if ((kh = fido_cred_id_ptr(cred)) == NULL) { + goto err; + } + + if ((kh_len = fido_cred_id_len(cred)) == 0) { + goto err; + } + + if (fido_assert_allow_cred(assert, kh, kh_len) != FIDO_OK) { + goto err; + } + + if (fido_assert_set_clientdata_hash(assert, cdh, 32) != FIDO_OK) { + goto err; + } + + if (fido_assert_set_uv(assert, uv) != FIDO_OK) { + goto err; + } + + if (fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET) != FIDO_OK) { + goto err; + } + + if (fido_assert_set_hmac_salt(assert, salt, HMAC_SALT_SIZE) != FIDO_OK) { + goto err; + } + + if (fido_dev_get_assert(dev, assert, pin) != FIDO_OK) { + goto err; + } + + if (fido_assert_count(assert) != 1) { + goto err; + } + + if ((hmac_secret = fido_assert_hmac_secret_ptr(assert, 0)) == NULL) { + goto err; + } + + if ((hmac_secret_len = fido_assert_hmac_secret_len(assert, 0)) != + HMAC_SECRET_SIZE) { + goto err; + } + + memcpy(secret, hmac_secret, hmac_secret_len); + retval = 1; + +err: + fido_assert_free(&assert); + return retval; +} + +int generate_encrypted_authtok(fido_dev_t *dev, fido_cred_t *cred, + const char *authtok, fido_opt_t uv, + const char *pin, unsigned char **enc_authtok, + size_t *enc_authtok_len) { + size_t authtok_len; + unsigned char key[32]; + int ok = 0; + + authtok_len = strlen(authtok); + *enc_authtok_len = HMAC_SALT_SIZE + authtok_len + AEAD_TAG_SIZE; + if ((*enc_authtok = malloc(*enc_authtok_len)) == NULL) { + goto err; + } + if (!random_bytes(*enc_authtok, HMAC_SALT_SIZE)) { + goto err; + } + if (!get_hmac_secret(dev, cred, uv, pin, *enc_authtok, key)) { + goto err; + } + if (!encrypt_authtok((unsigned char *) authtok, authtok_len, key, + *enc_authtok + HMAC_SALT_SIZE + authtok_len, + *enc_authtok + HMAC_SALT_SIZE)) { + goto err; + } + ok = 1; + +err: + explicit_bzero(key, sizeof(key)); + if (!ok) { + if (*enc_authtok) { + explicit_bzero(*enc_authtok, *enc_authtok_len); + free(*enc_authtok); + *enc_authtok = NULL; + *enc_authtok_len = 0; + } + } + return ok; +} diff --git a/authtok.h b/authtok.h new file mode 100644 index 00000000..801541eb --- /dev/null +++ b/authtok.h @@ -0,0 +1,20 @@ +#ifndef AUTHTOK_H +#define AUTHTOK_H + +#include + +#include "util.h" + +#define HMAC_SALT_SIZE 32 +#define HMAC_SECRET_SIZE 32 +#define AEAD_TAG_SIZE 16 + +int get_authtok(const fido_assert_t *assert, const char *enc_authtok, + char **authtok, size_t *authtok_len); + +int generate_encrypted_authtok(fido_dev_t *dev, fido_cred_t *cred, + const char *authtok, fido_opt_t uv, + const char *pin, unsigned char **enc_authtok, + size_t *enc_authtok_len); + +#endif /* UTIL_H */ diff --git a/pam-u2f.c b/pam-u2f.c index 28c76d44..2bbfad53 100644 --- a/pam-u2f.c +++ b/pam-u2f.c @@ -65,6 +65,8 @@ static void parse_cfg(int flags, int argc, const char **argv, cfg_t *cfg) { cfg->nodetect = 1; } else if (strcmp(argv[i], "expand") == 0) { cfg->expand = 1; + } else if (strcmp(argv[i], "allowauthtok") == 0) { + cfg->allowauthtok = 1; } else if (strncmp(argv[i], "userpresence=", 13) == 0) { sscanf(argv[i], "userpresence=%d", &cfg->userpresence); } else if (strncmp(argv[i], "userverification=", 17) == 0) { @@ -111,6 +113,7 @@ static void parse_cfg(int flags, int argc, const char **argv, cfg_t *cfg) { debug_dbg(cfg, "alwaysok=%d", cfg->alwaysok); debug_dbg(cfg, "sshformat=%d", cfg->sshformat); debug_dbg(cfg, "expand=%d", cfg->expand); + debug_dbg(cfg, "allowauthtok=%d", cfg->allowauthtok); debug_dbg(cfg, "authfile=%s", cfg->auth_file ? cfg->auth_file : "(null)"); debug_dbg(cfg, "authpending_file=%s", cfg->authpending_file ? cfg->authpending_file : "(null)"); diff --git a/pamu2fcfg/Makefile.am b/pamu2fcfg/Makefile.am index 7cb1b9fe..6fb8be1f 100644 --- a/pamu2fcfg/Makefile.am +++ b/pamu2fcfg/Makefile.am @@ -9,4 +9,5 @@ pamu2fcfg_SOURCES = pamu2fcfg.c pamu2fcfg_SOURCES += readpassphrase.c _readpassphrase.h pamu2fcfg_SOURCES += strlcpy.c openbsd-compat.h pamu2fcfg_SOURCES += ../util.c ../b64.c ../explicit_bzero.c +pamu2fcfg_SOURCES += ../authtok.c ../authtok.h pamu2fcfg_LDADD = $(LIBFIDO2_LIBS) $(LIBCRYPTO_LIBS) diff --git a/pamu2fcfg/pamu2fcfg.c b/pamu2fcfg/pamu2fcfg.c index 74d6ab45..5c7d4583 100644 --- a/pamu2fcfg/pamu2fcfg.c +++ b/pamu2fcfg/pamu2fcfg.c @@ -27,6 +27,7 @@ #include "b64.h" #include "util.h" +#include "authtok.h" #include "openbsd-compat.h" @@ -46,6 +47,10 @@ struct args { int debug; int verbose; int nouser; + int authtok; + int pam_userpresence; + int pam_userverification; + int pam_pinverification; }; static fido_cred_t *prepare_cred(const struct args *const args) { @@ -160,6 +165,14 @@ static fido_cred_t *prepare_cred(const struct args *const args) { goto err; } + if (args->authtok) { + if ((r = fido_cred_set_extensions(cred, FIDO_EXT_HMAC_SECRET)) != FIDO_OK) { + fprintf(stderr, "error: fido_cred_set_extensions (%d) %s\n", r, + fido_strerr(r)); + goto err; + } + } + ok = 0; err: @@ -171,22 +184,27 @@ static fido_cred_t *prepare_cred(const struct args *const args) { } static int make_cred(const struct args *args, const char *path, fido_dev_t *dev, - fido_cred_t *cred, int devopts) { + fido_cred_t *cred, int devopts, + unsigned char **enc_authtok, size_t *enc_authtok_len) { char prompt[BUFSIZE]; - char pin[BUFSIZE]; + char authtok[BUFSIZE]; + char *pin = NULL; + fido_opt_t uv; + int use_pin; int n; int r; + int retval = -1; if (path == NULL || dev == NULL || cred == NULL) { fprintf(stderr, "%s: args\n", __func__); - return -1; + goto err; } /* Some form of UV required; built-in UV is available. */ if (args->user_verification || (devopts & (UV_SET | UV_NOT_REQD)) == UV_SET) { if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) { fprintf(stderr, "error: fido_cred_set_uv: %s (%d)\n", fido_strerr(r), r); - return -1; + goto err; } } @@ -203,24 +221,73 @@ static int make_cred(const struct args *args, const char *path, fido_dev_t *dev, r == FIDO_ERR_PIN_BLOCKED)) { n = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path); if (n < 0 || (size_t) n >= sizeof(prompt)) { - fprintf(stderr, "error: snprintf prompt"); - return -1; + fprintf(stderr, "error: snprintf prompt\n"); + goto err; } - if (!readpassphrase(prompt, pin, sizeof(pin), RPP_ECHO_OFF)) { - fprintf(stderr, "error: failed to read pin"); - explicit_bzero(pin, sizeof(pin)); - return -1; + if ((pin = malloc(BUFSIZE)) == NULL) { + fprintf(stderr, "error: malloc\n"); + goto err; + } + if (!readpassphrase(prompt, pin, BUFSIZE, RPP_ECHO_OFF)) { + fprintf(stderr, "error: failed to read pin\n"); + goto err; } r = fido_dev_make_cred(dev, cred, pin); } - explicit_bzero(pin, sizeof(pin)); if (r != FIDO_OK) { fprintf(stderr, "error: fido_dev_make_cred (%d) %s\n", r, fido_strerr(r)); - return -1; + goto err; } - return 0; + if (args->authtok) { + if (!readpassphrase("Enter auth token: ", authtok, sizeof(authtok), + RPP_ECHO_OFF)) { + fprintf(stderr, "error: failed to read auth token\n"); + goto err; + } + + if (args->pam_userpresence == 0 && args->no_user_presence) { + fprintf(stderr, "error: user presence is required for auth token\n"); + goto err; + } + + if (args->pam_userverification == 1 || args->user_verification == 1) { + uv = FIDO_OPT_TRUE; + } else if (args->pam_userverification == 0) + uv = FIDO_OPT_FALSE; + else { + uv = FIDO_OPT_OMIT; + } + + if (args->pam_pinverification == 1 || args->pin_verification == 1) { + if (pin == NULL) { + fprintf(stderr, + "error: pin verification is required but no pin is found\n"); + goto err; + } + use_pin = 1; + } else { + use_pin = 0; + } + + if (!generate_encrypted_authtok(dev, cred, authtok, uv, + use_pin ? pin : NULL, enc_authtok, + enc_authtok_len)) { + fprintf(stderr, "error: failed to generate encrypted auth token\n"); + goto err; + } + } + + retval = 0; + +err: + explicit_bzero(authtok, BUFSIZE); + if (pin) { + explicit_bzero(pin, BUFSIZE); + free(pin); + } + return retval; } static int verify_cred(const fido_cred_t *const cred) { @@ -248,12 +315,15 @@ static int verify_cred(const fido_cred_t *const cred) { } static int print_authfile_line(const struct args *const args, - const fido_cred_t *const cred) { + const fido_cred_t *const cred, + const unsigned char *enc_authtok, + size_t enc_authtok_len) { const unsigned char *kh = NULL; const unsigned char *pk = NULL; const char *user = NULL; char *b64_kh = NULL; char *b64_pk = NULL; + char *b64_enc_authtok = NULL; size_t kh_len; size_t pk_len; int ok = -1; @@ -296,17 +366,26 @@ static int print_authfile_line(const struct args *const args, printf("%s", user); } - printf(":%s,%s,%s,%s%s%s", args->resident ? "*" : b64_kh, b64_pk, + if (args->authtok) { + if (!b64_encode(enc_authtok, enc_authtok_len, &b64_enc_authtok)) { + fprintf(stderr, "error: failed to encode PAM auth token\n"); + goto err; + } + } + + printf(":%s,%s,%s,%s%s%s,%s", args->resident ? "*" : b64_kh, b64_pk, cose_string(fido_cred_type(cred)), !args->no_user_presence ? "+presence" : "", args->user_verification ? "+verification" : "", - args->pin_verification ? "+pin" : ""); + args->pin_verification ? "+pin" : "", + args->authtok ? b64_enc_authtok : "*"); ok = 0; err: free(b64_kh); free(b64_pk); + free(b64_enc_authtok); return ok; } @@ -369,6 +448,8 @@ static void parse_args(int argc, char *argv[], struct args *args) { { "verbose", no_argument, NULL, 'v' }, { "username", required_argument, NULL, 'u' }, { "nouser", no_argument, NULL, 'n' }, + { "authtok", no_argument, NULL, 'a' }, + { "pam-arguments", required_argument, NULL, 'p' }, { 0, 0, 0, 0 } }; const char *usage = @@ -395,11 +476,21 @@ static void parse_args(int argc, char *argv[], struct args *args) { " defaults to the current user name\n" " -n, --nouser Print only registration information (key handle,\n" " public key, and options), useful for appending\n" +" -a, --authtok Read a PAM auth token (usually a password) that\n" +" should be decrypted during authentication\n" +" -p, --pam-arguments The arguments passed to the pam module, used for\n" +" encryption key generation when --authtok is set\n" "\n" "Report bugs at <" PACKAGE_BUGREPORT ">.\n"; /* clang-format on */ + char *saveptr = NULL; + char *arg; - while ((c = getopt_long(argc, argv, "ho:i:t:rPNVdvu:n", options, NULL)) != + args->pam_userpresence = -1; + args->pam_userverification = -1; + args->pam_pinverification = -1; + + while ((c = getopt_long(argc, argv, "ho:i:t:rPNVdvu:nap:", options, NULL)) != -1) { switch (c) { case 'h': @@ -438,6 +529,22 @@ static void parse_args(int argc, char *argv[], struct args *args) { case 'n': args->nouser = 1; break; + case 'a': + args->authtok = 1; + break; + case 'p': + arg = strtok_r(optarg, " ", &saveptr); + while (arg != NULL) { + if (strncmp(arg, "userpresence=", 13) == 0) { + sscanf(arg, "userpresence=%d", &args->pam_userpresence); + } else if (strncmp(arg, "userverification=", 17) == 0) { + sscanf(arg, "userverification=%d", &args->pam_userverification); + } else if (strncmp(arg, "pinverification=", 16) == 0) { + sscanf(arg, "pinverification=%d", &args->pam_pinverification); + } + arg = strtok_r(NULL, " ", &saveptr); + } + break; case OPT_VERSION: printf("pamu2fcfg " PACKAGE_VERSION "\n"); exit(EXIT_SUCCESS); @@ -463,6 +570,8 @@ int main(int argc, char *argv[]) { size_t ndevs = 0; int devopts = 0; int r; + unsigned char *enc_authtok = NULL; + size_t enc_authtok_len; parse_args(argc, argv, &args); fido_init(args.debug ? FIDO_DEBUG : 0); @@ -555,8 +664,10 @@ int main(int argc, char *argv[]) { if ((cred = prepare_cred(&args)) == NULL) goto err; - if (make_cred(&args, path, dev, cred, devopts) != 0 || - verify_cred(cred) != 0 || print_authfile_line(&args, cred) != 0) + if (make_cred(&args, path, dev, cred, devopts, &enc_authtok, + &enc_authtok_len) != 0 || + verify_cred(cred) != 0 || + print_authfile_line(&args, cred, enc_authtok, enc_authtok_len) != 0) goto err; exit_code = EXIT_SUCCESS; @@ -568,5 +679,9 @@ int main(int argc, char *argv[]) { fido_cred_free(&cred); fido_dev_free(&dev); + if (enc_authtok) { + free(enc_authtok); + } + exit(exit_code); } diff --git a/util.c b/util.c index f3ea20f8..8001c173 100644 --- a/util.c +++ b/util.c @@ -24,6 +24,7 @@ #include "b64.h" #include "debug.h" #include "util.h" +#include "authtok.h" #define SSH_MAX_SIZE 8192 #define SSH_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\n" @@ -163,12 +164,13 @@ static void reset_device(device_t *device) { free(device->publicKey); free(device->coseType); free(device->attributes); + free(device->enc_authtok); memset(device, 0, sizeof(*device)); } static int parse_native_credential(const cfg_t *cfg, char *s, device_t *cred) { const char *delim = ","; - const char *kh, *pk, *type, *attr; + const char *kh, *pk, *type, *attr, *enc_authtok; char *saveptr = NULL; memset(cred, 0, sizeof(*cred)); @@ -191,12 +193,16 @@ static int parse_native_credential(const cfg_t *cfg, char *s, device_t *cred) { } else if ((attr = strtok_r(NULL, delim, &saveptr)) == NULL) { debug_dbg(cfg, "Empty attributes"); attr = ""; + } else if ((enc_authtok = strtok_r(NULL, delim, &saveptr)) == NULL) { + debug_dbg(cfg, "Missing encrypted auth token"); + enc_authtok = "*"; } cred->keyHandle = cred->old_format ? normal_b64(kh) : strdup(kh); if (cred->keyHandle == NULL || (cred->publicKey = strdup(pk)) == NULL || (cred->coseType = strdup(type)) == NULL || - (cred->attributes = strdup(attr)) == NULL) { + (cred->attributes = strdup(attr)) == NULL || + (cred->enc_authtok = strdup(enc_authtok)) == NULL) { debug_dbg(cfg, "Unable to allocate memory for credential components"); goto fail; } @@ -259,6 +265,8 @@ static int parse_native_format(const cfg_t *cfg, const char *username, devices[i].coseType); debug_dbg(cfg, "Attributes for device number %u: %s", i + 1, devices[i].attributes); + debug_dbg(cfg, "Encrypted auth token for device number %u: %s", i + 1, + devices[i].enc_authtok); i++; } } @@ -827,7 +835,8 @@ static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist, } else { r = fido_dev_get_assert(dev, assert, NULL); if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) || - (fido_dev_is_fido2(dev) && r == FIDO_OK)) { + (fido_dev_is_fido2(dev) && + (r == FIDO_OK || r == FIDO_ERR_UP_REQUIRED))) { authlist[j++] = dev; debug_dbg(cfg, "Found key in authenticator %zu", i); return (1); @@ -967,6 +976,8 @@ static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device, fido_assert_t *assert = NULL; unsigned char *buf = NULL; size_t buf_len; + unsigned char *enc_authtok = NULL; + size_t enc_authtok_len; int ok = 0; int r; @@ -1011,6 +1022,26 @@ static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device, goto err; } + if (cfg->allowauthtok && strcmp(device->enc_authtok, "*") != 0) { + if (!b64_decode(device->enc_authtok, (void **) &enc_authtok, + &enc_authtok_len) || + enc_authtok_len < HMAC_SALT_SIZE + AEAD_TAG_SIZE) { + debug_dbg(cfg, "Failed to decode encrypted auth token"); + goto err; + } + + if (fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET) != FIDO_OK) { + debug_dbg(cfg, "Failed to set extensions"); + goto err; + } + + if (fido_assert_set_hmac_salt(assert, enc_authtok, HMAC_SALT_SIZE) != + FIDO_OK) { + debug_dbg(cfg, "Failed to set hmac salt"); + goto err; + } + } + ok = 1; err: @@ -1018,6 +1049,7 @@ static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device, fido_assert_free(&assert); free(buf); + free(enc_authtok); return assert; } @@ -1146,6 +1178,8 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, struct opts opts; struct pk pk; char *pin = NULL; + char *authtok; + size_t authtok_len; init_opts(&opts); #ifndef WITH_FUZZING @@ -1254,6 +1288,16 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, } r = fido_assert_verify(assert, 0, pk.type, pk.ptr); if (r == FIDO_OK) { + if (cfg->allowauthtok && strcmp(devices[i].enc_authtok, "*") != 0) { + if (get_authtok(assert, devices[i].enc_authtok, &authtok, + &authtok_len)) { + pam_set_item(pamh, PAM_AUTHTOK, authtok); + explicit_bzero(authtok, authtok_len); + free(authtok); + } else { + debug_dbg(cfg, "Failed to decrypt auth token"); + } + } retval = 1; goto out; } diff --git a/util.h b/util.h index cb8572b4..f8b5e911 100644 --- a/util.h +++ b/util.h @@ -36,6 +36,7 @@ typedef struct { int pinverification; int sshformat; int expand; + int allowauthtok; const char *auth_file; const char *authpending_file; const char *origin; @@ -50,6 +51,7 @@ typedef struct { char *keyHandle; char *coseType; char *attributes; + char *enc_authtok; int old_format; } device_t; From 3be18829fa99eb95706e0addd494f7b3dca5b9ce Mon Sep 17 00:00:00 2001 From: Frank Qin Date: Sun, 14 May 2023 17:12:17 -0700 Subject: [PATCH 2/7] authtok: fix uninitialized and unused variables --- authtok.c | 6 ++---- fuzz/fuzz_format_parsers.c | 2 ++ pamu2fcfg/pamu2fcfg.c | 2 +- util.c | 2 ++ 4 files changed, 7 insertions(+), 5 deletions(-) diff --git a/authtok.c b/authtok.c index 605c1af1..f7d29b91 100644 --- a/authtok.c +++ b/authtok.c @@ -95,8 +95,6 @@ int get_authtok(const fido_assert_t *assert, const char *enc_authtok, unsigned char *buf = NULL; size_t buf_len; const unsigned char *key; - size_t key_len; - size_t stmt_count; int ok = 0; *authtok = NULL; @@ -106,14 +104,14 @@ int get_authtok(const fido_assert_t *assert, const char *enc_authtok, goto err; } - if ((stmt_count = fido_assert_count(assert)) != 1) { + if (fido_assert_count(assert) != 1) { goto err; } if ((key = fido_assert_hmac_secret_ptr(assert, 0)) == NULL) { goto err; } - if ((key_len = fido_assert_hmac_secret_len(assert, 0)) != HMAC_SECRET_SIZE) { + if (fido_assert_hmac_secret_len(assert, 0) != HMAC_SECRET_SIZE) { goto err; } diff --git a/fuzz/fuzz_format_parsers.c b/fuzz/fuzz_format_parsers.c index 81e11b38..c45bbe88 100644 --- a/fuzz/fuzz_format_parsers.c +++ b/fuzz/fuzz_format_parsers.c @@ -19,10 +19,12 @@ static void cleanup(device_t *devs, unsigned int n_devs) { free(devs[i].publicKey); free(devs[i].coseType); free(devs[i].attributes); + free(devs[i].enc_authtok); devs[i].keyHandle = NULL; devs[i].publicKey = NULL; devs[i].coseType = NULL; devs[i].attributes = NULL; + devs[i].enc_authtok = NULL; } } diff --git a/pamu2fcfg/pamu2fcfg.c b/pamu2fcfg/pamu2fcfg.c index 5c7d4583..a5941c8e 100644 --- a/pamu2fcfg/pamu2fcfg.c +++ b/pamu2fcfg/pamu2fcfg.c @@ -571,7 +571,7 @@ int main(int argc, char *argv[]) { int devopts = 0; int r; unsigned char *enc_authtok = NULL; - size_t enc_authtok_len; + size_t enc_authtok_len = 0; parse_args(argc, argv, &args); fido_init(args.debug ? FIDO_DEBUG : 0); diff --git a/util.c b/util.c index 8001c173..ef473695 100644 --- a/util.c +++ b/util.c @@ -190,9 +190,11 @@ static int parse_native_credential(const cfg_t *cfg, char *s, device_t *cred) { cred->old_format = 1; type = "es256"; attr = "+presence"; + enc_authtok = "*"; } else if ((attr = strtok_r(NULL, delim, &saveptr)) == NULL) { debug_dbg(cfg, "Empty attributes"); attr = ""; + enc_authtok = "*"; } else if ((enc_authtok = strtok_r(NULL, delim, &saveptr)) == NULL) { debug_dbg(cfg, "Missing encrypted auth token"); enc_authtok = "*"; From f74618f5e955bce5f23fd2ba52e02d13ba324bd4 Mon Sep 17 00:00:00 2001 From: Frank Qin Date: Sat, 17 Jun 2023 18:34:18 -0700 Subject: [PATCH 3/7] authtok: fix some issues with auth token support --- authtok.c | 27 +++++++-------- pamu2fcfg/pamu2fcfg.c | 35 +++++++++++++------ util.c | 81 +++++++++++++++++++++++++------------------ 3 files changed, 83 insertions(+), 60 deletions(-) diff --git a/authtok.c b/authtok.c index f7d29b91..f9410923 100644 --- a/authtok.c +++ b/authtok.c @@ -1,7 +1,5 @@ #include -#include #include -#include #include "authtok.h" #include "b64.h" @@ -13,7 +11,6 @@ encrypt_authtok(const unsigned char *plaintext, size_t plaintext_len, unsigned char *ciphertext /* equal to plaintext_len */ ) { EVP_CIPHER_CTX *ctx = NULL; - EVP_CIPHER *cipher = NULL; int len; int retval = 0; unsigned char iv[12] = {0}; @@ -21,10 +18,7 @@ encrypt_authtok(const unsigned char *plaintext, size_t plaintext_len, if (!(ctx = EVP_CIPHER_CTX_new())) { goto err; } - if (!(cipher = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL))) { - goto err; - } - if (!EVP_EncryptInit_ex2(ctx, cipher, key, iv, NULL)) { + if (!EVP_EncryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv)) { goto err; } if (plaintext_len > INT_MAX) { @@ -44,7 +38,6 @@ encrypt_authtok(const unsigned char *plaintext, size_t plaintext_len, err: EVP_CIPHER_CTX_free(ctx); - EVP_CIPHER_free(cipher); return retval; } @@ -55,7 +48,6 @@ decrypt_authtok(const unsigned char *ciphertext, size_t ciphertext_len, unsigned char *plaintext /* equal to ciphertext_len */ ) { EVP_CIPHER_CTX *ctx = NULL; - EVP_CIPHER *cipher = NULL; int len; int retval = 0; unsigned char iv[12] = {0}; @@ -63,10 +55,7 @@ decrypt_authtok(const unsigned char *ciphertext, size_t ciphertext_len, if (!(ctx = EVP_CIPHER_CTX_new())) { goto err; } - if (!(cipher = EVP_CIPHER_fetch(NULL, "AES-256-GCM", NULL))) { - goto err; - } - if (!EVP_DecryptInit_ex2(ctx, cipher, key, iv, NULL)) { + if (!EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), NULL, key, iv)) { goto err; } if (ciphertext_len > INT_MAX) { @@ -86,7 +75,6 @@ decrypt_authtok(const unsigned char *ciphertext, size_t ciphertext_len, err: EVP_CIPHER_CTX_free(ctx); - EVP_CIPHER_free(cipher); return retval; } @@ -146,7 +134,7 @@ static int get_hmac_secret(fido_dev_t *dev, fido_cred_t *cred, fido_opt_t uv, const char *pin, const unsigned char *salt, unsigned char *secret) { fido_assert_t *assert = NULL; - unsigned char cdh[32] = {0}; + unsigned char cdh[32]; const unsigned char *kh = NULL; const unsigned char *hmac_secret; size_t kh_len; @@ -154,6 +142,10 @@ static int get_hmac_secret(fido_dev_t *dev, fido_cred_t *cred, fido_opt_t uv, const char *id; int retval = 0; + if (!random_bytes(cdh, sizeof(cdh))) { + goto err; + } + if ((assert = fido_assert_new()) == NULL) { goto err; } @@ -202,6 +194,11 @@ static int get_hmac_secret(fido_dev_t *dev, fido_cred_t *cred, fido_opt_t uv, goto err; } + if (fido_assert_verify(assert, 0, fido_cred_type(cred), + fido_cred_pubkey_ptr(cred)) != FIDO_OK) { + goto err; + } + if ((hmac_secret = fido_assert_hmac_secret_ptr(assert, 0)) == NULL) { goto err; } diff --git a/pamu2fcfg/pamu2fcfg.c b/pamu2fcfg/pamu2fcfg.c index a5941c8e..a4c41e77 100644 --- a/pamu2fcfg/pamu2fcfg.c +++ b/pamu2fcfg/pamu2fcfg.c @@ -247,24 +247,32 @@ static int make_cred(const struct args *args, const char *path, fido_dev_t *dev, goto err; } - if (args->pam_userpresence == 0 && args->no_user_presence) { - fprintf(stderr, "error: user presence is required for auth token\n"); - goto err; - } - - if (args->pam_userverification == 1 || args->user_verification == 1) { + if (args->pam_userverification || args->user_verification) { uv = FIDO_OPT_TRUE; - } else if (args->pam_userverification == 0) + } else if (!args->pam_userverification) uv = FIDO_OPT_FALSE; else { uv = FIDO_OPT_OMIT; } - if (args->pam_pinverification == 1 || args->pin_verification == 1) { + if (args->pam_pinverification || args->pin_verification) { + /* Even if we didn't use a pin to make the credential, if eventually PAM + * requires a pin during authentication, we must use a pin to generate the + * HMAC secret. */ if (pin == NULL) { - fprintf(stderr, - "error: pin verification is required but no pin is found\n"); - goto err; + n = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path); + if (n < 0 || (size_t) n >= sizeof(prompt)) { + fprintf(stderr, "error: snprintf prompt\n"); + goto err; + } + if ((pin = malloc(BUFSIZE)) == NULL) { + fprintf(stderr, "error: malloc\n"); + goto err; + } + if (!readpassphrase(prompt, pin, BUFSIZE, RPP_ECHO_OFF)) { + fprintf(stderr, "error: failed to read pin\n"); + goto err; + } } use_pin = 1; } else { @@ -574,6 +582,11 @@ int main(int argc, char *argv[]) { size_t enc_authtok_len = 0; parse_args(argc, argv, &args); + if (args.authtok && args.no_user_presence) { + fprintf(stderr, "error: user presence is required for auth token\n"); + goto err; + } + fido_init(args.debug ? FIDO_DEBUG : 0); devlist = fido_dev_info_new(64); diff --git a/util.c b/util.c index ef473695..65e7682f 100644 --- a/util.c +++ b/util.c @@ -171,31 +171,31 @@ static void reset_device(device_t *device) { static int parse_native_credential(const cfg_t *cfg, char *s, device_t *cred) { const char *delim = ","; const char *kh, *pk, *type, *attr, *enc_authtok; - char *saveptr = NULL; + char *saveptr = s; memset(cred, 0, sizeof(*cred)); - if ((kh = strtok_r(s, delim, &saveptr)) == NULL) { + if ((kh = strsep(&saveptr, delim)) == NULL) { debug_dbg(cfg, "Missing key handle"); goto fail; } - if ((pk = strtok_r(NULL, delim, &saveptr)) == NULL) { + if ((pk = strsep(&saveptr, delim)) == NULL) { debug_dbg(cfg, "Missing public key"); goto fail; } - if ((type = strtok_r(NULL, delim, &saveptr)) == NULL) { + if ((type = strsep(&saveptr, delim)) == NULL) { debug_dbg(cfg, "Old format, assume es256 and +presence"); cred->old_format = 1; type = "es256"; attr = "+presence"; enc_authtok = "*"; - } else if ((attr = strtok_r(NULL, delim, &saveptr)) == NULL) { + } else if ((attr = strsep(&saveptr, delim)) == NULL) { debug_dbg(cfg, "Empty attributes"); attr = ""; enc_authtok = "*"; - } else if ((enc_authtok = strtok_r(NULL, delim, &saveptr)) == NULL) { + } else if ((enc_authtok = strsep(&saveptr, delim)) == NULL) { debug_dbg(cfg, "Missing encrypted auth token"); enc_authtok = "*"; } @@ -228,13 +228,13 @@ static int parse_native_format(const cfg_t *cfg, const char *username, int r = 0; while ((len = getline(&buf, &bufsiz, opwfile)) != -1) { - char *saveptr = NULL; + char *saveptr = buf; if (len > 0 && buf[len - 1] == '\n') buf[len - 1] = '\0'; debug_dbg(cfg, "Read %zu bytes", len); - s_user = strtok_r(buf, ":", &saveptr); + s_user = strsep(&saveptr, ":"); if (s_user && strcmp(username, s_user) == 0) { debug_dbg(cfg, "Matched user: %s", s_user); @@ -245,7 +245,7 @@ static int parse_native_format(const cfg_t *cfg, const char *username, *n_devs = 0; i = 0; - while ((s_credential = strtok_r(NULL, ":", &saveptr))) { + while ((s_credential = strsep(&saveptr, ":"))) { if ((*n_devs)++ > cfg->max_devs - 1) { *n_devs = cfg->max_devs; debug_dbg(cfg, @@ -837,8 +837,7 @@ static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist, } else { r = fido_dev_get_assert(dev, assert, NULL); if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) || - (fido_dev_is_fido2(dev) && - (r == FIDO_OK || r == FIDO_ERR_UP_REQUIRED))) { + (fido_dev_is_fido2(dev) && r == FIDO_OK)) { authlist[j++] = dev; debug_dbg(cfg, "Found key in authenticator %zu", i); return (1); @@ -973,13 +972,41 @@ static int set_cdh(const cfg_t *cfg, fido_assert_t *assert) { return 1; } +static int set_hmac_secret(const cfg_t *cfg, fido_assert_t *assert, + const char *b64_enc_authtok) { + unsigned char *enc_authtok = NULL; + size_t enc_authtok_len; + int ok = 0; + + if (!b64_decode(b64_enc_authtok, (void **) &enc_authtok, &enc_authtok_len) || + enc_authtok_len < HMAC_SALT_SIZE + AEAD_TAG_SIZE) { + debug_dbg(cfg, "Failed to decode encrypted auth token"); + goto err; + } + + if (fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET) != FIDO_OK) { + debug_dbg(cfg, "Failed to set extensions"); + goto err; + } + + if (fido_assert_set_hmac_salt(assert, enc_authtok, HMAC_SALT_SIZE) != + FIDO_OK) { + debug_dbg(cfg, "Failed to set hmac salt"); + goto err; + } + + ok = 1; + +err: + free(enc_authtok); + return ok; +} + static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device, const struct opts *opts) { fido_assert_t *assert = NULL; unsigned char *buf = NULL; size_t buf_len; - unsigned char *enc_authtok = NULL; - size_t enc_authtok_len; int ok = 0; int r; @@ -1024,26 +1051,6 @@ static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device, goto err; } - if (cfg->allowauthtok && strcmp(device->enc_authtok, "*") != 0) { - if (!b64_decode(device->enc_authtok, (void **) &enc_authtok, - &enc_authtok_len) || - enc_authtok_len < HMAC_SALT_SIZE + AEAD_TAG_SIZE) { - debug_dbg(cfg, "Failed to decode encrypted auth token"); - goto err; - } - - if (fido_assert_set_extensions(assert, FIDO_EXT_HMAC_SECRET) != FIDO_OK) { - debug_dbg(cfg, "Failed to set extensions"); - goto err; - } - - if (fido_assert_set_hmac_salt(assert, enc_authtok, HMAC_SALT_SIZE) != - FIDO_OK) { - debug_dbg(cfg, "Failed to set hmac salt"); - goto err; - } - } - ok = 1; err: @@ -1051,7 +1058,6 @@ static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device, fido_assert_free(&assert); free(buf); - free(enc_authtok); return assert; } @@ -1260,6 +1266,13 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, goto out; } + if (cfg->allowauthtok && strcmp(devices[i].enc_authtok, "*") != 0) { + if (!set_hmac_secret(cfg, assert, devices[i].enc_authtok)) { + debug_dbg(cfg, "Failed to set hmac secret"); + goto out; + } + } + if (opts.pin == FIDO_OPT_TRUE) { pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: "); if (pin == NULL) { From 808f5aa6913eaaebf9d9d45fe4fe6d2cded0059a Mon Sep 17 00:00:00 2001 From: Frank Qin Date: Sun, 18 Jun 2023 16:18:26 -0700 Subject: [PATCH 4/7] authtok: include required header file --- authtok.c | 1 + 1 file changed, 1 insertion(+) diff --git a/authtok.c b/authtok.c index f9410923..9c44ef38 100644 --- a/authtok.c +++ b/authtok.c @@ -1,3 +1,4 @@ +#include #include #include From d30f019ba38857f40eacb82ac174c2d1ce43e56e Mon Sep 17 00:00:00 2001 From: Frank Qin Date: Sun, 9 Jul 2023 15:15:00 -0700 Subject: [PATCH 5/7] authtok: skip hmac secret if no PIN or UV --- util.c | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/util.c b/util.c index 65e7682f..46921888 100644 --- a/util.c +++ b/util.c @@ -1188,6 +1188,7 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, char *pin = NULL; char *authtok; size_t authtok_len; + int enable_authtok; init_opts(&opts); #ifndef WITH_FUZZING @@ -1266,7 +1267,17 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, goto out; } + enable_authtok = 0; if (cfg->allowauthtok && strcmp(devices[i].enc_authtok, "*") != 0) { + if (opts.pin == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) { + enable_authtok = 1; + } else { + debug_dbg(cfg, "Skipping auth token decryption because neither PIN " + "nor UV is set"); + } + } + + if (enable_authtok) { if (!set_hmac_secret(cfg, assert, devices[i].enc_authtok)) { debug_dbg(cfg, "Failed to set hmac secret"); goto out; @@ -1303,7 +1314,7 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, } r = fido_assert_verify(assert, 0, pk.type, pk.ptr); if (r == FIDO_OK) { - if (cfg->allowauthtok && strcmp(devices[i].enc_authtok, "*") != 0) { + if (enable_authtok) { if (get_authtok(assert, devices[i].enc_authtok, &authtok, &authtok_len)) { pam_set_item(pamh, PAM_AUTHTOK, authtok); From 66df754ca8af81b96d3e1a59cfa37d39be794d75 Mon Sep 17 00:00:00 2001 From: Frank Qin Date: Sun, 9 Jul 2023 16:10:25 -0700 Subject: [PATCH 6/7] authtok: simplify make_cred in pamu2fcfg --- pamu2fcfg/pamu2fcfg.c | 89 ++++++++++++++++++------------------------- 1 file changed, 38 insertions(+), 51 deletions(-) diff --git a/pamu2fcfg/pamu2fcfg.c b/pamu2fcfg/pamu2fcfg.c index a4c41e77..04b613d7 100644 --- a/pamu2fcfg/pamu2fcfg.c +++ b/pamu2fcfg/pamu2fcfg.c @@ -200,8 +200,23 @@ static int make_cred(const struct args *args, const char *path, fido_dev_t *dev, goto err; } + /* Calculate the form of verification we need */ + if (args->authtok) { + if (args->pam_userverification == 1 || args->user_verification) { + uv = FIDO_OPT_TRUE; + } else if (args->pam_userverification == 0) + uv = FIDO_OPT_FALSE; + else { + uv = FIDO_OPT_OMIT; + } + use_pin = args->pam_pinverification == 1 || args->pin_verification; + } else { + uv = args->user_verification ? FIDO_OPT_TRUE : FIDO_OPT_FALSE; + use_pin = args->pin_verification; + } + /* Some form of UV required; built-in UV is available. */ - if (args->user_verification || (devopts & (UV_SET | UV_NOT_REQD)) == UV_SET) { + if (uv == FIDO_OPT_TRUE || (devopts & (UV_SET | UV_NOT_REQD)) == UV_SET) { if ((r = fido_cred_set_uv(cred, FIDO_OPT_TRUE)) != FIDO_OK) { fprintf(stderr, "error: fido_cred_set_uv: %s (%d)\n", fido_strerr(r), r); goto err; @@ -209,30 +224,35 @@ static int make_cred(const struct args *args, const char *path, fido_dev_t *dev, } /* Let built-in UV have precedence over PIN. No UV also handled here. */ - if (args->user_verification || !args->pin_verification) { + if (uv == FIDO_OPT_TRUE || !use_pin) { r = fido_dev_make_cred(dev, cred, NULL); } else { r = FIDO_ERR_PIN_REQUIRED; } /* Some form of UV required; built-in UV failed or is not available. */ - if ((devopts & PIN_SET) && - (r == FIDO_ERR_PIN_REQUIRED || r == FIDO_ERR_UV_BLOCKED || - r == FIDO_ERR_PIN_BLOCKED)) { - n = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path); - if (n < 0 || (size_t) n >= sizeof(prompt)) { - fprintf(stderr, "error: snprintf prompt\n"); - goto err; - } - if ((pin = malloc(BUFSIZE)) == NULL) { - fprintf(stderr, "error: malloc\n"); - goto err; - } - if (!readpassphrase(prompt, pin, BUFSIZE, RPP_ECHO_OFF)) { - fprintf(stderr, "error: failed to read pin\n"); + if (r == FIDO_ERR_PIN_REQUIRED || r == FIDO_ERR_UV_BLOCKED || + r == FIDO_ERR_PIN_BLOCKED) { + if (devopts & PIN_SET) { + n = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path); + if (n < 0 || (size_t) n >= sizeof(prompt)) { + fprintf(stderr, "error: snprintf prompt\n"); + goto err; + } + if ((pin = malloc(BUFSIZE)) == NULL) { + fprintf(stderr, "error: malloc\n"); + goto err; + } + if (!readpassphrase(prompt, pin, BUFSIZE, RPP_ECHO_OFF)) { + fprintf(stderr, "error: failed to read pin\n"); + goto err; + } + r = fido_dev_make_cred(dev, cred, pin); + } else { + fprintf(stderr, "error: pin verification is required but no pin is set " + "on the authenticator"); goto err; } - r = fido_dev_make_cred(dev, cred, pin); } if (r != FIDO_OK) { @@ -247,40 +267,7 @@ static int make_cred(const struct args *args, const char *path, fido_dev_t *dev, goto err; } - if (args->pam_userverification || args->user_verification) { - uv = FIDO_OPT_TRUE; - } else if (!args->pam_userverification) - uv = FIDO_OPT_FALSE; - else { - uv = FIDO_OPT_OMIT; - } - - if (args->pam_pinverification || args->pin_verification) { - /* Even if we didn't use a pin to make the credential, if eventually PAM - * requires a pin during authentication, we must use a pin to generate the - * HMAC secret. */ - if (pin == NULL) { - n = snprintf(prompt, sizeof(prompt), "Enter PIN for %s: ", path); - if (n < 0 || (size_t) n >= sizeof(prompt)) { - fprintf(stderr, "error: snprintf prompt\n"); - goto err; - } - if ((pin = malloc(BUFSIZE)) == NULL) { - fprintf(stderr, "error: malloc\n"); - goto err; - } - if (!readpassphrase(prompt, pin, BUFSIZE, RPP_ECHO_OFF)) { - fprintf(stderr, "error: failed to read pin\n"); - goto err; - } - } - use_pin = 1; - } else { - use_pin = 0; - } - - if (!generate_encrypted_authtok(dev, cred, authtok, uv, - use_pin ? pin : NULL, enc_authtok, + if (!generate_encrypted_authtok(dev, cred, authtok, uv, pin, enc_authtok, enc_authtok_len)) { fprintf(stderr, "error: failed to generate encrypted auth token\n"); goto err; From bb9394e31289fd42ec964e58ba254e8ba71e19e4 Mon Sep 17 00:00:00 2001 From: Frank Qin Date: Sun, 19 May 2024 14:42:32 -0700 Subject: [PATCH 7/7] authtok: add more debug logging Debug log whether authtok is enabled and when token descryption is successful. https://github.com/Yubico/pam-u2f/pull/294#issuecomment-2116846227 --- util.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/util.c b/util.c index 46921888..955c6322 100644 --- a/util.c +++ b/util.c @@ -1277,6 +1277,7 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, } } + debug_dbg(cfg, "enable_authtok=%d", enable_authtok); if (enable_authtok) { if (!set_hmac_secret(cfg, assert, devices[i].enc_authtok)) { debug_dbg(cfg, "Failed to set hmac secret"); @@ -1320,6 +1321,7 @@ int do_authentication(const cfg_t *cfg, const device_t *devices, pam_set_item(pamh, PAM_AUTHTOK, authtok); explicit_bzero(authtok, authtok_len); free(authtok); + debug_dbg(cfg, "Successfully decrypted auth token"); } else { debug_dbg(cfg, "Failed to decrypt auth token"); }