Home | History | Annotate | Line # | Download | only in dist
      1 /*
      2  * Copyright (C) 2014-2022 Yubico AB - See COPYING
      3  */
      4 
      5 #include <fido.h>
      6 #include <fido/es256.h>
      7 #include <fido/rs256.h>
      8 #include <fido/eddsa.h>
      9 
     10 #include <openssl/ec.h>
     11 #include <openssl/obj_mac.h>
     12 
     13 #include <inttypes.h>
     14 #include <limits.h>
     15 #include <stdlib.h>
     16 #include <fcntl.h>
     17 #include <sys/stat.h>
     18 #include <pwd.h>
     19 #include <errno.h>
     20 #include <unistd.h>
     21 #include <string.h>
     22 #include <arpa/inet.h>
     23 
     24 #include "b64.h"
     25 #include "debug.h"
     26 #include "util.h"
     27 
     28 #define SSH_MAX_SIZE 8192
     29 #define SSH_HEADER "-----BEGIN OPENSSH PRIVATE KEY-----\n"
     30 #define SSH_HEADER_LEN (sizeof(SSH_HEADER) - 1)
     31 #define SSH_TRAILER "-----END OPENSSH PRIVATE KEY-----\n"
     32 #define SSH_TRAILER_LEN (sizeof(SSH_TRAILER) - 1)
     33 #define SSH_AUTH_MAGIC "openssh-key-v1"
     34 #define SSH_AUTH_MAGIC_LEN (sizeof(SSH_AUTH_MAGIC)) // AUTH_MAGIC includes \0
     35 #define SSH_ES256 "sk-ecdsa-sha2-nistp256 (at) openssh.com"
     36 #define SSH_ES256_LEN (sizeof(SSH_ES256) - 1)
     37 #define SSH_ES256_POINT_LEN 65
     38 #define SSH_P256_NAME "nistp256"
     39 #define SSH_P256_NAME_LEN (sizeof(SSH_P256_NAME) - 1)
     40 #define SSH_EDDSA "sk-ssh-ed25519 (at) openssh.com"
     41 #define SSH_EDDSA_LEN (sizeof(SSH_EDDSA) - 1)
     42 #define SSH_EDDSA_POINT_LEN 32
     43 #define SSH_SK_USER_PRESENCE_REQD 0x01
     44 #define SSH_SK_USER_VERIFICATION_REQD 0x04
     45 #define SSH_SK_RESIDENT_KEY 0x20
     46 
     47 struct opts {
     48   fido_opt_t up;
     49   fido_opt_t uv;
     50   fido_opt_t pin;
     51 };
     52 
     53 struct pk {
     54   void *ptr;
     55   int type;
     56 };
     57 
     58 static int hex_decode(const char *ascii_hex, unsigned char **blob,
     59                       size_t *blob_len) {
     60   *blob = NULL;
     61   *blob_len = 0;
     62 
     63   if (ascii_hex == NULL || (strlen(ascii_hex) % 2) != 0)
     64     return (0);
     65 
     66   *blob_len = strlen(ascii_hex) / 2;
     67   *blob = calloc(1, *blob_len);
     68   if (*blob == NULL)
     69     return (0);
     70 
     71   for (size_t i = 0; i < *blob_len; i++) {
     72     unsigned int c;
     73     int n = -1;
     74     int r = sscanf(ascii_hex, "%02x%n", &c, &n);
     75     if (r != 1 || n != 2 || c > UCHAR_MAX) {
     76       free(*blob);
     77       *blob = NULL;
     78       *blob_len = 0;
     79       return (0);
     80     }
     81     (*blob)[i] = (unsigned char) c;
     82     ascii_hex += n;
     83   }
     84 
     85   return (1);
     86 }
     87 
     88 static char *normal_b64(const char *websafe_b64) {
     89   char *b64;
     90   char *p;
     91   size_t n;
     92 
     93   n = strlen(websafe_b64);
     94   if (n > SIZE_MAX - 3)
     95     return (NULL);
     96 
     97   b64 = calloc(1, n + 3);
     98   if (b64 == NULL)
     99     return (NULL);
    100 
    101   memcpy(b64, websafe_b64, n);
    102   p = b64;
    103 
    104   while ((p = strpbrk(p, "-_")) != NULL) {
    105     switch (*p) {
    106       case '-':
    107         *p++ = '+';
    108         break;
    109       case '_':
    110         *p++ = '/';
    111         break;
    112     }
    113   }
    114 
    115   switch (n % 4) {
    116     case 1:
    117       b64[n] = '=';
    118       break;
    119     case 2:
    120     case 3:
    121       b64[n] = '=';
    122       b64[n + 1] = '=';
    123       break;
    124   }
    125 
    126   return (b64);
    127 }
    128 
    129 static int translate_old_format_pubkey(es256_pk_t *es256_pk,
    130                                        const unsigned char *pk, size_t pk_len) {
    131   EC_KEY *ec = NULL;
    132   EC_POINT *q = NULL;
    133   const EC_GROUP *g = NULL;
    134   int r = FIDO_ERR_INTERNAL;
    135 
    136   if (es256_pk == NULL)
    137     goto fail;
    138 
    139   if ((ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
    140       (g = EC_KEY_get0_group(ec)) == NULL)
    141     goto fail;
    142 
    143   if ((q = EC_POINT_new(g)) == NULL ||
    144       !EC_POINT_oct2point(g, q, pk, pk_len, NULL) ||
    145       !EC_KEY_set_public_key(ec, q))
    146     goto fail;
    147 
    148   r = es256_pk_from_EC_KEY(es256_pk, ec);
    149 
    150 fail:
    151   if (ec != NULL)
    152     EC_KEY_free(ec);
    153   if (q != NULL)
    154     EC_POINT_free(q);
    155 
    156   return r;
    157 }
    158 
    159 static int is_resident(const char *kh) { return strcmp(kh, "*") == 0; }
    160 
    161 static void reset_device(device_t *device) {
    162   free(device->keyHandle);
    163   free(device->publicKey);
    164   free(device->coseType);
    165   free(device->attributes);
    166   memset(device, 0, sizeof(*device));
    167 }
    168 
    169 static int parse_native_credential(const cfg_t *cfg, char *s, device_t *cred) {
    170   const char *delim = ",";
    171   const char *kh, *pk, *type, *attr;
    172   char *saveptr = NULL;
    173 
    174   memset(cred, 0, sizeof(*cred));
    175 
    176   if ((kh = strtok_r(s, delim, &saveptr)) == NULL) {
    177     debug_dbg(cfg, "Missing key handle");
    178     goto fail;
    179   }
    180 
    181   if ((pk = strtok_r(NULL, delim, &saveptr)) == NULL) {
    182     debug_dbg(cfg, "Missing public key");
    183     goto fail;
    184   }
    185 
    186   if ((type = strtok_r(NULL, delim, &saveptr)) == NULL) {
    187     debug_dbg(cfg, "Old format, assume es256 and +presence");
    188     cred->old_format = 1;
    189     type = "es256";
    190     attr = "+presence";
    191   } else if ((attr = strtok_r(NULL, delim, &saveptr)) == NULL) {
    192     debug_dbg(cfg, "Empty attributes");
    193     attr = "";
    194   }
    195 
    196   cred->keyHandle = cred->old_format ? normal_b64(kh) : strdup(kh);
    197   if (cred->keyHandle == NULL || (cred->publicKey = strdup(pk)) == NULL ||
    198       (cred->coseType = strdup(type)) == NULL ||
    199       (cred->attributes = strdup(attr)) == NULL) {
    200     debug_dbg(cfg, "Unable to allocate memory for credential components");
    201     goto fail;
    202   }
    203 
    204   return 1;
    205 
    206 fail:
    207   reset_device(cred);
    208   return 0;
    209 }
    210 
    211 static int parse_native_format(const cfg_t *cfg, const char *username,
    212                                FILE *opwfile, device_t *devices,
    213                                unsigned *n_devs) {
    214 
    215   char *s_user, *s_credential;
    216   char *buf = NULL;
    217   size_t bufsiz = 0;
    218   ssize_t len;
    219   unsigned i;
    220   int r = 0;
    221 
    222   while ((len = getline(&buf, &bufsiz, opwfile)) != -1) {
    223     char *saveptr = NULL;
    224     if (len > 0 && buf[len - 1] == '\n')
    225       buf[len - 1] = '\0';
    226 
    227     debug_dbg(cfg, "Read %zu bytes", len);
    228 
    229     s_user = strtok_r(buf, ":", &saveptr);
    230     if (s_user && strcmp(username, s_user) == 0) {
    231       debug_dbg(cfg, "Matched user: %s", s_user);
    232 
    233       // only keep last line for this user
    234       for (i = 0; i < *n_devs; i++) {
    235         reset_device(&devices[i]);
    236       }
    237       *n_devs = 0;
    238 
    239       i = 0;
    240       while ((s_credential = strtok_r(NULL, ":", &saveptr))) {
    241         if ((*n_devs)++ > cfg->max_devs - 1) {
    242           *n_devs = cfg->max_devs;
    243           debug_dbg(cfg,
    244                     "Found more than %d devices, ignoring the remaining ones",
    245                     cfg->max_devs);
    246           break;
    247         }
    248 
    249         if (!parse_native_credential(cfg, s_credential, &devices[i])) {
    250           debug_dbg(cfg, "Failed to parse credential");
    251           goto fail;
    252         }
    253 
    254         debug_dbg(cfg, "KeyHandle for device number %u: %s", i + 1,
    255                   devices[i].keyHandle);
    256         debug_dbg(cfg, "publicKey for device number %u: %s", i + 1,
    257                   devices[i].publicKey);
    258         debug_dbg(cfg, "COSE type for device number %u: %s", i + 1,
    259                   devices[i].coseType);
    260         debug_dbg(cfg, "Attributes for device number %u: %s", i + 1,
    261                   devices[i].attributes);
    262         i++;
    263       }
    264     }
    265   }
    266 
    267   if (!feof(opwfile)) {
    268     debug_dbg(cfg, "authfile parsing ended before eof (%d)", errno);
    269     goto fail;
    270   }
    271 
    272   r = 1;
    273 fail:
    274   free(buf);
    275   return r;
    276 }
    277 
    278 static int load_ssh_key(const cfg_t *cfg, char **out, FILE *opwfile,
    279                         size_t opwfile_size) {
    280   size_t buf_size;
    281   char *buf = NULL;
    282   char *cp = NULL;
    283   int r = 0;
    284   int ch;
    285 
    286   *out = NULL;
    287 
    288   if (opwfile_size < SSH_HEADER_LEN + SSH_TRAILER_LEN) {
    289     debug_dbg(cfg, "Malformed SSH key (length)");
    290     goto fail;
    291   }
    292 
    293   buf_size = opwfile_size > SSH_MAX_SIZE ? SSH_MAX_SIZE : opwfile_size;
    294   if ((cp = buf = calloc(1, buf_size)) == NULL) {
    295     debug_dbg(cfg, "Failed to allocate buffer for SSH key");
    296     goto fail;
    297   }
    298 
    299   // NOTE(adma): +1 for \0
    300   if (fgets(buf, (int)(SSH_HEADER_LEN + 1), opwfile) == NULL ||
    301       strlen(buf) != SSH_HEADER_LEN ||
    302       strncmp(buf, SSH_HEADER, SSH_HEADER_LEN) != 0) {
    303     debug_dbg(cfg, "Malformed SSH key (header)");
    304     goto fail;
    305   }
    306 
    307   while (opwfile_size > 0 && buf_size > 1) {
    308     ch = fgetc(opwfile);
    309     if (ch == EOF) {
    310       debug_dbg(cfg, "Unexpected authfile termination");
    311       goto fail;
    312     }
    313 
    314     opwfile_size--;
    315 
    316     if (ch != '\n' && ch != '\r') {
    317       *cp = (char) ch;
    318       buf_size--;
    319       if (ch == '-') {
    320         // NOTE(adma): no +1 here since we already read one '-'
    321         if (buf_size < SSH_TRAILER_LEN ||
    322             fgets(cp + 1, (int)SSH_TRAILER_LEN, opwfile) == NULL ||
    323             strlen(cp) != SSH_TRAILER_LEN ||
    324             strncmp(cp, SSH_TRAILER, SSH_TRAILER_LEN) != 0) {
    325           debug_dbg(cfg, "Malformed SSH key (trailer)");
    326           goto fail;
    327         }
    328 
    329         r = 1;
    330         *(cp) = '\0';
    331         break;
    332       } else {
    333         cp++;
    334       }
    335     }
    336   }
    337 
    338 fail:
    339   if (r != 1) {
    340     free(buf);
    341     buf = NULL;
    342   }
    343 
    344   *out = buf;
    345 
    346   return r;
    347 }
    348 
    349 static int ssh_get(const unsigned char **buf, size_t *size, unsigned char *dst,
    350                    size_t len) {
    351   if (*size < len)
    352     return 0;
    353   if (dst != NULL)
    354     memcpy(dst, *buf, len);
    355   *buf += len;
    356   *size -= len;
    357   return 1;
    358 }
    359 
    360 static int ssh_get_u8(const unsigned char **buf, size_t *size, uint8_t *val) {
    361   return ssh_get(buf, size, val, sizeof(*val));
    362 }
    363 
    364 static int ssh_get_u32(const unsigned char **buf, size_t *size, uint32_t *val) {
    365   if (!ssh_get(buf, size, (unsigned char *) val, sizeof(*val)))
    366     return 0;
    367   if (val != NULL)
    368     *val = ntohl(*val);
    369   return 1;
    370 }
    371 
    372 static int ssh_get_string_ref(const unsigned char **buf, size_t *size,
    373                               const unsigned char **ref, size_t *lenp) {
    374   uint32_t len;
    375 
    376   if (!ssh_get_u32(buf, size, &len))
    377     return 0;
    378   if (!ssh_get(buf, size, NULL, len))
    379     return 0;
    380   if (ref != NULL)
    381     *ref = *buf - len;
    382   if (lenp != NULL)
    383     *lenp = len;
    384   return 1;
    385 }
    386 
    387 static int ssh_get_cstring(const unsigned char **buf, size_t *size, char **str,
    388                            size_t *lenp) {
    389   const unsigned char *ref;
    390   size_t len;
    391 
    392   if (!ssh_get_string_ref(buf, size, &ref, &len))
    393     return 0;
    394   if (str != NULL) {
    395     if (len > SIZE_MAX - 1 || (*str = calloc(1, len + 1)) == NULL)
    396       return 0;
    397     memcpy(*str, ref, len);
    398   }
    399   if (lenp != NULL)
    400     *lenp = len;
    401   return 1;
    402 }
    403 
    404 static int ssh_log_cstring(const cfg_t *cfg, const unsigned char **buf,
    405                            size_t *size, const char *name) {
    406   char *str = NULL;
    407   size_t len;
    408 
    409   (void) name; // silence compiler warnings if PAM_DEBUG disabled
    410 
    411   if (!ssh_get_cstring(buf, size, &str, &len)) {
    412     debug_dbg(cfg, "Malformed SSH key (%s)", name);
    413     return 0;
    414   }
    415   debug_dbg(cfg, "%s (%zu) \"%s\"", name, len, str);
    416 
    417   free(str);
    418   return 1;
    419 }
    420 
    421 static int ssh_get_attrs(const cfg_t *cfg, const unsigned char **buf,
    422                          size_t *size, char **attrs) {
    423   char tmp[32] = {0};
    424   uint8_t flags;
    425   int r;
    426 
    427   // flags
    428   if (!ssh_get_u8(buf, size, &flags)) {
    429     debug_dbg(cfg, "Malformed SSH key (flags)");
    430     return 0;
    431   }
    432   debug_dbg(cfg, "flags: %02x", flags);
    433 
    434   r = snprintf(tmp, sizeof(tmp), "%s%s",
    435                flags & SSH_SK_USER_PRESENCE_REQD ? "+presence" : "",
    436                flags & SSH_SK_USER_VERIFICATION_REQD ? "+verification" : "");
    437   if (r < 0 || (size_t) r >= sizeof(tmp)) {
    438     debug_dbg(cfg, "Unable to prepare flags");
    439     return 0;
    440   }
    441 
    442   if ((*attrs = strdup(tmp)) == NULL) {
    443     debug_dbg(cfg, "Unable to allocate attributes");
    444     return 0;
    445   }
    446 
    447   return 1;
    448 }
    449 
    450 static int ssh_get_pubkey(const cfg_t *cfg, const unsigned char **buf,
    451                           size_t *size, char **type_p, char **pubkey_p) {
    452   char *ssh_type = NULL;
    453   char *ssh_curve = NULL;
    454   const unsigned char *blob;
    455   size_t len;
    456   int type;
    457   size_t point_len;
    458   int ok = 0;
    459 
    460   *type_p = NULL;
    461   *pubkey_p = NULL;
    462 
    463   // key type
    464   if (!ssh_get_cstring(buf, size, &ssh_type, &len)) {
    465     debug_dbg(cfg, "Malformed SSH key (keytype)");
    466     goto err;
    467   }
    468 
    469   if (len == SSH_ES256_LEN && memcmp(ssh_type, SSH_ES256, SSH_ES256_LEN) == 0) {
    470     type = COSE_ES256;
    471     point_len = SSH_ES256_POINT_LEN;
    472   } else if (len == SSH_EDDSA_LEN &&
    473              memcmp(ssh_type, SSH_EDDSA, SSH_EDDSA_LEN) == 0) {
    474     type = COSE_EDDSA;
    475     point_len = SSH_EDDSA_POINT_LEN;
    476   } else {
    477     debug_dbg(cfg, "Unknown key type %s", ssh_type);
    478     goto err;
    479   }
    480 
    481   debug_dbg(cfg, "keytype (%zu) \"%s\"", len, ssh_type);
    482 
    483   if (type == COSE_ES256) {
    484     // curve name
    485     if (!ssh_get_cstring(buf, size, &ssh_curve, &len)) {
    486       debug_dbg(cfg, "Malformed SSH key (curvename)");
    487       goto err;
    488     }
    489 
    490     if (len == SSH_P256_NAME_LEN &&
    491         memcmp(ssh_curve, SSH_P256_NAME, SSH_P256_NAME_LEN) == 0) {
    492       debug_dbg(cfg, "curvename (%zu) \"%s\"", len, ssh_curve);
    493     } else {
    494       debug_dbg(cfg, "Unknown curve %s", ssh_curve);
    495       goto err;
    496     }
    497   }
    498 
    499   // point
    500   if (!ssh_get_string_ref(buf, size, &blob, &len)) {
    501     debug_dbg(cfg, "Malformed SSH key (point)");
    502     goto err;
    503   }
    504 
    505   if (len != point_len) {
    506     debug_dbg(cfg, "Invalid point length, should be %zu, found %zu", point_len,
    507               len);
    508     goto err;
    509   }
    510 
    511   if (type == COSE_ES256) {
    512     // Skip the initial '04'
    513     if (len < 1) {
    514       debug_dbg(cfg, "Failed to skip initial '04'");
    515       goto err;
    516     }
    517     blob++;
    518     len--;
    519   }
    520 
    521   if (!b64_encode(blob, len, pubkey_p)) {
    522     debug_dbg(cfg, "Unable to allocate public key");
    523     goto err;
    524   }
    525 
    526   if ((*type_p = strdup(cose_string(type))) == NULL) {
    527     debug_dbg(cfg, "Unable to allocate COSE type");
    528     goto err;
    529   }
    530 
    531   ok = 1;
    532 err:
    533   if (!ok) {
    534     free(*type_p);
    535     free(*pubkey_p);
    536     *type_p = NULL;
    537     *pubkey_p = NULL;
    538   }
    539   free(ssh_type);
    540   free(ssh_curve);
    541 
    542   return ok;
    543 }
    544 
    545 static int parse_ssh_format(const cfg_t *cfg, FILE *opwfile,
    546                             size_t opwfile_size, device_t *devices,
    547                             unsigned *n_devs) {
    548   char *b64 = NULL;
    549   const unsigned char *decoded;
    550   unsigned char *decoded_initial = NULL;
    551   size_t decoded_len;
    552   const unsigned char *blob;
    553   uint32_t check1, check2, tmp;
    554   size_t len;
    555   int r = 0;
    556 
    557   // The logic below is inspired by
    558   // how ssh parses its own keys. See sshkey.c
    559   reset_device(&devices[0]);
    560   *n_devs = 0;
    561 
    562   if (!load_ssh_key(cfg, &b64, opwfile, opwfile_size) ||
    563       !b64_decode(b64, (void **) &decoded_initial, &decoded_len)) {
    564     debug_dbg(cfg, "Unable to decode credential");
    565     goto out;
    566   }
    567 
    568   decoded = decoded_initial;
    569 
    570   // magic
    571   if (decoded_len < SSH_AUTH_MAGIC_LEN ||
    572       memcmp(decoded, SSH_AUTH_MAGIC, SSH_AUTH_MAGIC_LEN) != 0) {
    573     debug_dbg(cfg, "Malformed SSH key (magic)");
    574     goto out;
    575   }
    576 
    577   decoded += SSH_AUTH_MAGIC_LEN;
    578   decoded_len -= SSH_AUTH_MAGIC_LEN;
    579 
    580   if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "ciphername") ||
    581       !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfname") ||
    582       !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfoptions"))
    583     goto out;
    584 
    585   if (!ssh_get_u32(&decoded, &decoded_len, &tmp)) {
    586     debug_dbg(cfg, "Malformed SSH key (nkeys)");
    587     goto out;
    588   }
    589   debug_dbg(cfg, "nkeys: %" PRIu32, tmp);
    590   if (tmp != 1) {
    591     debug_dbg(cfg, "Multiple keys not supported");
    592     goto out;
    593   }
    594 
    595   // public_key (skip)
    596   if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
    597     debug_dbg(cfg, "Malformed SSH key (pubkey)");
    598     goto out;
    599   }
    600 
    601   // private key (consume length)
    602   if (!ssh_get_u32(&decoded, &decoded_len, &tmp) || decoded_len < tmp) {
    603     debug_dbg(cfg, "Malformed SSH key (pvtkey length)");
    604     goto out;
    605   }
    606 
    607   // check1, check2
    608   if (!ssh_get_u32(&decoded, &decoded_len, &check1) ||
    609       !ssh_get_u32(&decoded, &decoded_len, &check2)) {
    610     debug_dbg(cfg, "Malformed SSH key (check1, check2)");
    611     goto out;
    612   }
    613 
    614   debug_dbg(cfg, "check1: %" PRIu32, check1);
    615   debug_dbg(cfg, "check2: %" PRIu32, check2);
    616 
    617   if (check1 != check2) {
    618     debug_dbg(cfg, "Mismatched check values");
    619     goto out;
    620   }
    621 
    622   if (!ssh_get_pubkey(cfg, &decoded, &decoded_len, &devices[0].coseType,
    623                       &devices[0].publicKey) ||
    624       !ssh_log_cstring(cfg, &decoded, &decoded_len, "application") ||
    625       !ssh_get_attrs(cfg, &decoded, &decoded_len, &devices[0].attributes))
    626     goto out;
    627 
    628   // keyhandle
    629   if (!ssh_get_string_ref(&decoded, &decoded_len, &blob, &len) ||
    630       !b64_encode(blob, len, &devices[0].keyHandle)) {
    631     debug_dbg(cfg, "Malformed SSH key (keyhandle)");
    632     goto out;
    633   }
    634 
    635   debug_dbg(cfg, "KeyHandle for device number 1: %s", devices[0].keyHandle);
    636   debug_dbg(cfg, "publicKey for device number 1: %s", devices[0].publicKey);
    637   debug_dbg(cfg, "COSE type for device number 1: %s", devices[0].coseType);
    638   debug_dbg(cfg, "Attributes for device number 1: %s", devices[0].attributes);
    639 
    640   // reserved (skip)
    641   if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
    642     debug_dbg(cfg, "Malformed SSH key (reserved)");
    643     goto out;
    644   }
    645 
    646   // comment
    647   if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "comment"))
    648     goto out;
    649 
    650   // padding
    651   if (decoded_len >= 255) {
    652     debug_dbg(cfg, "Malformed SSH key (padding length)");
    653     goto out;
    654   }
    655 
    656   for (int i = 1; (unsigned) i <= decoded_len; i++) {
    657     if (decoded[i - 1] != i) {
    658       debug_dbg(cfg, "Malformed SSH key (padding)");
    659       goto out;
    660     }
    661   }
    662 
    663   *n_devs = 1;
    664   r = 1;
    665 
    666 out:
    667   if (r != 1) {
    668     reset_device(&devices[0]);
    669     *n_devs = 0;
    670   }
    671 
    672   free(decoded_initial);
    673   free(b64);
    674 
    675   return r;
    676 }
    677 
    678 int get_devices_from_authfile(const cfg_t *cfg, const char *username,
    679                               device_t *devices, unsigned *n_devs) {
    680 
    681   int r = PAM_AUTHINFO_UNAVAIL;
    682   int fd = -1;
    683   struct stat st;
    684   struct passwd *pw = NULL, pw_s;
    685   char buffer[BUFSIZE];
    686   int gpu_ret;
    687   FILE *opwfile = NULL;
    688   size_t opwfile_size;
    689   unsigned i;
    690 
    691   /* Ensure we never return uninitialized count. */
    692   *n_devs = 0;
    693 
    694   fd = open(cfg->auth_file, O_RDONLY | O_CLOEXEC | O_NOCTTY);
    695   if (fd < 0) {
    696     if (errno == ENOENT && cfg->nouserok) {
    697       r = PAM_IGNORE;
    698     }
    699     debug_dbg(cfg, "Cannot open authentication file: %s", strerror(errno));
    700     goto err;
    701   }
    702 
    703   if (fstat(fd, &st) < 0) {
    704     debug_dbg(cfg, "Cannot stat authentication file: %s", strerror(errno));
    705     goto err;
    706   }
    707 
    708   if (!S_ISREG(st.st_mode)) {
    709     debug_dbg(cfg, "Authentication file is not a regular file");
    710     goto err;
    711   }
    712 
    713   if ((st.st_mode & (S_IWGRP | S_IWOTH)) != 0) {
    714     debug_dbg(cfg, "Authentication file has insecure permissions");
    715     goto err;
    716   }
    717 
    718   opwfile_size = (size_t)st.st_size;
    719 
    720   gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw);
    721   if (gpu_ret != 0 || pw == NULL) {
    722     debug_dbg(cfg, "Unable to retrieve credentials for uid %u, (%s)", st.st_uid,
    723               strerror(errno));
    724     goto err;
    725   }
    726 
    727   if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) {
    728     if (strcmp(username, "root") != 0) {
    729       debug_dbg(cfg,
    730                 "The owner of the authentication file is neither %s nor root",
    731                 username);
    732     } else {
    733       debug_dbg(cfg, "The owner of the authentication file is not root");
    734     }
    735     goto err;
    736   }
    737 
    738   opwfile = fdopen(fd, "r");
    739   if (opwfile == NULL) {
    740     debug_dbg(cfg, "fdopen: %s", strerror(errno));
    741     goto err;
    742   } else {
    743     fd = -1; /* fd belongs to opwfile */
    744   }
    745 
    746   if (cfg->sshformat == 0) {
    747     if (parse_native_format(cfg, username, opwfile, devices, n_devs) != 1) {
    748       goto err;
    749     }
    750   } else {
    751     if (parse_ssh_format(cfg, opwfile, opwfile_size, devices, n_devs) != 1) {
    752       goto err;
    753     }
    754   }
    755 
    756   debug_dbg(cfg, "Found %d device(s) for user %s", *n_devs, username);
    757   r = PAM_SUCCESS;
    758 
    759 err:
    760   if (r != PAM_SUCCESS) {
    761     for (i = 0; i < *n_devs; i++) {
    762       reset_device(&devices[i]);
    763     }
    764     *n_devs = 0;
    765   } else if (*n_devs == 0) {
    766     r = cfg->nouserok ? PAM_IGNORE : PAM_USER_UNKNOWN;
    767   }
    768 
    769   if (opwfile)
    770     fclose(opwfile);
    771 
    772   if (fd != -1)
    773     close(fd);
    774 
    775   return r;
    776 }
    777 
    778 void free_devices(device_t *devices, const unsigned n_devs) {
    779   unsigned i;
    780 
    781   if (!devices)
    782     return;
    783 
    784   for (i = 0; i < n_devs; i++) {
    785     reset_device(&devices[i]);
    786   }
    787 
    788   free(devices);
    789   devices = NULL;
    790 }
    791 
    792 static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist,
    793                               size_t devlist_len, fido_assert_t *assert,
    794                               const int rk, fido_dev_t **authlist) {
    795   const fido_dev_info_t *di = NULL;
    796   fido_dev_t *dev = NULL;
    797   int r;
    798   size_t i;
    799   size_t j;
    800 
    801   debug_dbg(cfg, "Working with %zu authenticator(s)", devlist_len);
    802 
    803   for (i = 0, j = 0; i < devlist_len; i++) {
    804     debug_dbg(cfg, "Checking whether key exists in authenticator %zu", i);
    805 
    806     di = fido_dev_info_ptr(devlist, i);
    807     if (!di) {
    808       debug_dbg(cfg, "Unable to get device pointer");
    809       continue;
    810     }
    811 
    812     debug_dbg(cfg, "Authenticator path: %s", fido_dev_info_path(di));
    813 
    814     dev = fido_dev_new();
    815     if (!dev) {
    816       debug_dbg(cfg, "Unable to allocate device type");
    817       continue;
    818     }
    819 
    820     r = fido_dev_open(dev, fido_dev_info_path(di));
    821     if (r != FIDO_OK) {
    822       debug_dbg(cfg, "Failed to open authenticator: %s (%d)", fido_strerr(r),
    823                 r);
    824       fido_dev_free(&dev);
    825       continue;
    826     }
    827 
    828     if (rk || cfg->nodetect) {
    829       /* resident credential or nodetect: try all authenticators */
    830       authlist[j++] = dev;
    831     } else {
    832       r = fido_dev_get_assert(dev, assert, NULL);
    833       if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) ||
    834           (fido_dev_is_fido2(dev) && r == FIDO_OK)) {
    835         authlist[j++] = dev;
    836         debug_dbg(cfg, "Found key in authenticator %zu", i);
    837         return (1);
    838       }
    839       debug_dbg(cfg, "Key not found in authenticator %zu", i);
    840 
    841       fido_dev_close(dev);
    842       fido_dev_free(&dev);
    843     }
    844   }
    845 
    846   if (j != 0)
    847     return (1);
    848   else {
    849     debug_dbg(cfg, "Key not found");
    850     return (0);
    851   }
    852 }
    853 
    854 static void init_opts(struct opts *opts) {
    855   opts->up = FIDO_OPT_FALSE;
    856   opts->uv = FIDO_OPT_OMIT;
    857   opts->pin = FIDO_OPT_FALSE;
    858 }
    859 
    860 static void parse_opts(const cfg_t *cfg, const char *attr, struct opts *opts) {
    861   if (cfg->userpresence == 1 || strstr(attr, "+presence")) {
    862     opts->up = FIDO_OPT_TRUE;
    863   } else if (cfg->userpresence == 0) {
    864     opts->up = FIDO_OPT_FALSE;
    865   } else {
    866     opts->up = FIDO_OPT_OMIT;
    867   }
    868 
    869   if (cfg->userverification == 1 || strstr(attr, "+verification")) {
    870     opts->uv = FIDO_OPT_TRUE;
    871   } else if (cfg->userverification == 0)
    872     opts->uv = FIDO_OPT_FALSE;
    873   else {
    874     opts->uv = FIDO_OPT_OMIT;
    875   }
    876 
    877   if (cfg->pinverification == 1 || strstr(attr, "+pin")) {
    878     opts->pin = FIDO_OPT_TRUE;
    879   } else if (cfg->pinverification == 0) {
    880     opts->pin = FIDO_OPT_FALSE;
    881   } else {
    882     opts->pin = FIDO_OPT_OMIT;
    883   }
    884 }
    885 
    886 static int get_device_opts(fido_dev_t *dev, int *pin, int *uv) {
    887   fido_cbor_info_t *info = NULL;
    888   char *const *ptr;
    889   const bool *val;
    890   size_t len;
    891 
    892   *pin = *uv = -1; /* unsupported */
    893 
    894   if (fido_dev_is_fido2(dev)) {
    895     if ((info = fido_cbor_info_new()) == NULL ||
    896         fido_dev_get_cbor_info(dev, info) != FIDO_OK) {
    897       fido_cbor_info_free(&info);
    898       return 0;
    899     }
    900 
    901     ptr = fido_cbor_info_options_name_ptr(info);
    902     val = fido_cbor_info_options_value_ptr(info);
    903     len = fido_cbor_info_options_len(info);
    904     for (size_t i = 0; i < len; i++) {
    905       if (strcmp(ptr[i], "clientPin") == 0) {
    906         *pin = val[i];
    907       } else if (strcmp(ptr[i], "uv") == 0) {
    908         *uv = val[i];
    909       }
    910     }
    911   }
    912 
    913   fido_cbor_info_free(&info);
    914   return 1;
    915 }
    916 
    917 static int match_device_opts(fido_dev_t *dev, struct opts *opts) {
    918   int pin, uv;
    919 
    920   /* FIXME: fido_dev_{supports,has}_{pin,uv} (1.7.0) */
    921   if (!get_device_opts(dev, &pin, &uv)) {
    922     return -1;
    923   }
    924 
    925   if (opts->uv == FIDO_OPT_FALSE && uv < 0) {
    926     opts->uv = FIDO_OPT_OMIT;
    927   }
    928 
    929   if ((opts->pin == FIDO_OPT_TRUE && pin != 1) ||
    930       (opts->uv == FIDO_OPT_TRUE && uv != 1)) {
    931     return 0;
    932   }
    933 
    934   return 1;
    935 }
    936 
    937 static int set_opts(const cfg_t *cfg, const struct opts *opts,
    938                     fido_assert_t *assert) {
    939   if (fido_assert_set_up(assert, opts->up) != FIDO_OK) {
    940     debug_dbg(cfg, "Failed to set UP");
    941     return 0;
    942   }
    943   if (fido_assert_set_uv(assert, opts->uv) != FIDO_OK) {
    944     debug_dbg(cfg, "Failed to set UV");
    945     return 0;
    946   }
    947 
    948   return 1;
    949 }
    950 
    951 static int set_cdh(const cfg_t *cfg, fido_assert_t *assert) {
    952   unsigned char cdh[32];
    953   int r;
    954 
    955   if (!random_bytes(cdh, sizeof(cdh))) {
    956     debug_dbg(cfg, "Failed to generate challenge");
    957     return 0;
    958   }
    959 
    960   r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
    961   if (r != FIDO_OK) {
    962     debug_dbg(cfg, "Unable to set challenge: %s (%d)", fido_strerr(r), r);
    963     return 0;
    964   }
    965 
    966   return 1;
    967 }
    968 
    969 static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device,
    970                                      const struct opts *opts) {
    971   fido_assert_t *assert = NULL;
    972   unsigned char *buf = NULL;
    973   size_t buf_len;
    974   int ok = 0;
    975   int r;
    976 
    977   if ((assert = fido_assert_new()) == NULL) {
    978     debug_dbg(cfg, "Unable to allocate assertion");
    979     goto err;
    980   }
    981 
    982   if (device->old_format)
    983     r = fido_assert_set_rp(assert, cfg->appid);
    984   else
    985     r = fido_assert_set_rp(assert, cfg->origin);
    986 
    987   if (r != FIDO_OK) {
    988     debug_dbg(cfg, "Unable to set origin: %s (%d)", fido_strerr(r), r);
    989     goto err;
    990   }
    991 
    992   if (is_resident(device->keyHandle)) {
    993     debug_dbg(cfg, "Credential is resident");
    994   } else {
    995     debug_dbg(cfg, "Key handle: %s", device->keyHandle);
    996     if (!b64_decode(device->keyHandle, (void **) &buf, &buf_len)) {
    997       debug_dbg(cfg, "Failed to decode key handle");
    998       goto err;
    999     }
   1000 
   1001     r = fido_assert_allow_cred(assert, buf, buf_len);
   1002     if (r != FIDO_OK) {
   1003       debug_dbg(cfg, "Unable to set keyHandle: %s (%d)", fido_strerr(r), r);
   1004       goto err;
   1005     }
   1006   }
   1007 
   1008   if (!set_opts(cfg, opts, assert)) {
   1009     debug_dbg(cfg, "Failed to set assert options");
   1010     goto err;
   1011   }
   1012 
   1013   if (!set_cdh(cfg, assert)) {
   1014     debug_dbg(cfg, "Failed to set client data hash");
   1015     goto err;
   1016   }
   1017 
   1018   ok = 1;
   1019 
   1020 err:
   1021   if (!ok)
   1022     fido_assert_free(&assert);
   1023 
   1024   free(buf);
   1025 
   1026   return assert;
   1027 }
   1028 
   1029 static void reset_pk(struct pk *pk) {
   1030   if (pk->type == COSE_ES256) {
   1031     es256_pk_free((es256_pk_t **) &pk->ptr);
   1032   } else if (pk->type == COSE_RS256) {
   1033     rs256_pk_free((rs256_pk_t **) &pk->ptr);
   1034   } else if (pk->type == COSE_EDDSA) {
   1035     eddsa_pk_free((eddsa_pk_t **) &pk->ptr);
   1036   }
   1037   memset(pk, 0, sizeof(*pk));
   1038 }
   1039 
   1040 int cose_type(const char *str, int *type) {
   1041   if (strcasecmp(str, "es256") == 0) {
   1042     *type = COSE_ES256;
   1043   } else if (strcasecmp(str, "rs256") == 0) {
   1044     *type = COSE_RS256;
   1045   } else if (strcasecmp(str, "eddsa") == 0) {
   1046     *type = COSE_EDDSA;
   1047   } else {
   1048     *type = 0;
   1049     return 0;
   1050   }
   1051 
   1052   return 1;
   1053 }
   1054 
   1055 const char *cose_string(int type) {
   1056   switch (type) {
   1057     case COSE_ES256:
   1058       return "es256";
   1059     case COSE_RS256:
   1060       return "rs256";
   1061     case COSE_EDDSA:
   1062       return "eddsa";
   1063     default:
   1064       return "unknown";
   1065   }
   1066 }
   1067 
   1068 static int parse_pk(const cfg_t *cfg, int old, const char *type, const char *pk,
   1069                     struct pk *out) {
   1070   unsigned char *buf = NULL;
   1071   size_t buf_len;
   1072   int ok = 0;
   1073   int r;
   1074 
   1075   reset_pk(out);
   1076 
   1077   if (old) {
   1078     if (!hex_decode(pk, &buf, &buf_len)) {
   1079       debug_dbg(cfg, "Failed to decode public key");
   1080       goto err;
   1081     }
   1082   } else {
   1083     if (!b64_decode(pk, (void **) &buf, &buf_len)) {
   1084       debug_dbg(cfg, "Failed to decode public key");
   1085       goto err;
   1086     }
   1087   }
   1088 
   1089   if (!cose_type(type, &out->type)) {
   1090     debug_dbg(cfg, "Unknown COSE type '%s'", type);
   1091     goto err;
   1092   }
   1093 
   1094   // For backwards compatibility, failure to pack the public key is not
   1095   // returned as an error.  Instead, it is handled by fido_verify_assert().
   1096   if (out->type == COSE_ES256) {
   1097     if ((out->ptr = es256_pk_new()) == NULL) {
   1098       debug_dbg(cfg, "Failed to allocate ES256 public key");
   1099       goto err;
   1100     }
   1101     if (old) {
   1102       r = translate_old_format_pubkey(out->ptr, buf, buf_len);
   1103     } else {
   1104       r = es256_pk_from_ptr(out->ptr, buf, buf_len);
   1105     }
   1106     if (r != FIDO_OK) {
   1107       debug_dbg(cfg, "Failed to convert ES256 public key");
   1108     }
   1109   } else if (out->type == COSE_RS256) {
   1110     if ((out->ptr = rs256_pk_new()) == NULL) {
   1111       debug_dbg(cfg, "Failed to allocate RS256 public key");
   1112       goto err;
   1113     }
   1114     r = rs256_pk_from_ptr(out->ptr, buf, buf_len);
   1115     if (r != FIDO_OK) {
   1116       debug_dbg(cfg, "Failed to convert RS256 public key");
   1117     }
   1118   } else if (out->type == COSE_EDDSA) {
   1119     if ((out->ptr = eddsa_pk_new()) == NULL) {
   1120       debug_dbg(cfg, "Failed to allocate EDDSA public key");
   1121       goto err;
   1122     }
   1123     r = eddsa_pk_from_ptr(out->ptr, buf, buf_len);
   1124     if (r != FIDO_OK) {
   1125       debug_dbg(cfg, "Failed to convert EDDSA public key");
   1126     }
   1127   } else {
   1128     debug_dbg(cfg, "COSE type '%s' not handled", type);
   1129     goto err;
   1130   }
   1131 
   1132   ok = 1;
   1133 err:
   1134   free(buf);
   1135 
   1136   return ok;
   1137 }
   1138 
   1139 int do_authentication(const cfg_t *cfg, const device_t *devices,
   1140                       const unsigned n_devs, pam_handle_t *pamh) {
   1141   fido_assert_t *assert = NULL;
   1142   fido_dev_info_t *devlist = NULL;
   1143   fido_dev_t **authlist = NULL;
   1144   int cued = 0;
   1145   int r;
   1146   int retval = PAM_AUTH_ERR;
   1147   size_t ndevs = 0;
   1148   size_t ndevs_prev = 0;
   1149   unsigned i = 0;
   1150   struct opts opts;
   1151   struct pk pk;
   1152   char *pin = NULL;
   1153 
   1154   init_opts(&opts);
   1155 #ifndef WITH_FUZZING
   1156   fido_init(cfg->debug ? FIDO_DEBUG : 0);
   1157 #else
   1158   fido_init(0);
   1159 #endif
   1160   memset(&pk, 0, sizeof(pk));
   1161 
   1162   devlist = fido_dev_info_new(DEVLIST_LEN);
   1163   if (!devlist) {
   1164     debug_dbg(cfg, "Unable to allocate devlist");
   1165     goto out;
   1166   }
   1167 
   1168   r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
   1169   if (r != FIDO_OK) {
   1170     debug_dbg(cfg, "Unable to discover device(s), %s (%d)", fido_strerr(r), r);
   1171     goto out;
   1172   }
   1173 
   1174   ndevs_prev = ndevs;
   1175 
   1176   debug_dbg(cfg, "Device max index is %zu", ndevs);
   1177 
   1178   authlist = calloc(DEVLIST_LEN + 1, sizeof(fido_dev_t *));
   1179   if (!authlist) {
   1180     debug_dbg(cfg, "Unable to allocate authenticator list");
   1181     goto out;
   1182   }
   1183 
   1184   if (cfg->nodetect)
   1185     debug_dbg(cfg, "nodetect option specified, suitable key detection will be "
   1186                    "skipped");
   1187 
   1188   i = 0;
   1189   while (i < n_devs) {
   1190     debug_dbg(cfg, "Attempting authentication with device number %d", i + 1);
   1191 
   1192     init_opts(&opts); /* used during authenticator discovery */
   1193     assert = prepare_assert(cfg, &devices[i], &opts);
   1194     if (assert == NULL) {
   1195       debug_dbg(cfg, "Failed to prepare assert");
   1196       goto out;
   1197     }
   1198 
   1199     if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
   1200                   devices[i].publicKey, &pk)) {
   1201       debug_dbg(cfg, "Failed to parse public key");
   1202       goto out;
   1203     }
   1204 
   1205     if (get_authenticators(cfg, devlist, ndevs, assert,
   1206                            is_resident(devices[i].keyHandle), authlist)) {
   1207       for (size_t j = 0; authlist[j] != NULL; j++) {
   1208         /* options used during authentication */
   1209         parse_opts(cfg, devices[i].attributes, &opts);
   1210 
   1211         r = match_device_opts(authlist[j], &opts);
   1212         if (r != 1) {
   1213           debug_dbg(cfg, "%s, skipping authenticator",
   1214                     r < 0 ? "Failed to query supported options"
   1215                           : "Unsupported options");
   1216           continue;
   1217         }
   1218 
   1219         if (!set_opts(cfg, &opts, assert)) {
   1220           debug_dbg(cfg, "Failed to set assert options");
   1221           goto out;
   1222         }
   1223 
   1224         if (!set_cdh(cfg, assert)) {
   1225           debug_dbg(cfg, "Failed to reset client data hash");
   1226           goto out;
   1227         }
   1228 
   1229         if (opts.pin == FIDO_OPT_TRUE) {
   1230           pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: ");
   1231           if (pin == NULL) {
   1232             debug_dbg(cfg, "converse() returned NULL");
   1233             goto out;
   1234           }
   1235         }
   1236         if (opts.up == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) {
   1237           if (cfg->manual == 0 && cfg->cue && !cued) {
   1238             cued = 1;
   1239             converse(pamh, PAM_TEXT_INFO,
   1240                      cfg->cue_prompt != NULL ? cfg->cue_prompt : DEFAULT_CUE);
   1241           }
   1242         }
   1243         r = fido_dev_get_assert(authlist[j], assert, pin);
   1244         if (pin) {
   1245           explicit_bzero(pin, strlen(pin));
   1246           free(pin);
   1247           pin = NULL;
   1248         }
   1249         if (r == FIDO_OK) {
   1250           if (opts.pin == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) {
   1251             r = fido_assert_set_uv(assert, FIDO_OPT_TRUE);
   1252             if (r != FIDO_OK) {
   1253               debug_dbg(cfg, "Failed to set UV");
   1254               goto out;
   1255             }
   1256           }
   1257           r = fido_assert_verify(assert, 0, pk.type, pk.ptr);
   1258           if (r == FIDO_OK) {
   1259             retval = PAM_SUCCESS;
   1260             goto out;
   1261           }
   1262         }
   1263       }
   1264     } else {
   1265       debug_dbg(cfg, "Device for this keyhandle is not present");
   1266     }
   1267 
   1268     i++;
   1269 
   1270     fido_dev_info_free(&devlist, ndevs);
   1271 
   1272     devlist = fido_dev_info_new(DEVLIST_LEN);
   1273     if (!devlist) {
   1274       debug_dbg(cfg, "Unable to allocate devlist");
   1275       goto out;
   1276     }
   1277 
   1278     r = fido_dev_info_manifest(devlist, DEVLIST_LEN, &ndevs);
   1279     if (r != FIDO_OK) {
   1280       debug_dbg(cfg, "Unable to discover device(s), %s (%d)", fido_strerr(r),
   1281                 r);
   1282       goto out;
   1283     }
   1284 
   1285     if (ndevs > ndevs_prev) {
   1286       debug_dbg(cfg,
   1287                 "Devices max_index has changed: %zu (was %zu). Starting over",
   1288                 ndevs, ndevs_prev);
   1289       ndevs_prev = ndevs;
   1290       i = 0;
   1291     }
   1292 
   1293     for (size_t j = 0; authlist[j] != NULL; j++) {
   1294       fido_dev_close(authlist[j]);
   1295       fido_dev_free(&authlist[j]);
   1296     }
   1297 
   1298     fido_assert_free(&assert);
   1299   }
   1300 
   1301 out:
   1302   reset_pk(&pk);
   1303   fido_assert_free(&assert);
   1304   fido_dev_info_free(&devlist, ndevs);
   1305 
   1306   if (authlist) {
   1307     for (size_t j = 0; authlist[j] != NULL; j++) {
   1308       fido_dev_close(authlist[j]);
   1309       fido_dev_free(&authlist[j]);
   1310     }
   1311     free(authlist);
   1312   }
   1313 
   1314   return retval;
   1315 }
   1316 
   1317 #define MAX_PROMPT_LEN (1024)
   1318 
   1319 static int manual_get_assert(const cfg_t *cfg, const char *prompt,
   1320                              pam_handle_t *pamh, fido_assert_t *assert) {
   1321   char *b64_cdh = NULL;
   1322   char *b64_rpid = NULL;
   1323   char *b64_authdata = NULL;
   1324   char *b64_sig = NULL;
   1325   unsigned char *authdata = NULL;
   1326   unsigned char *sig = NULL;
   1327   size_t authdata_len;
   1328   size_t sig_len;
   1329   int r;
   1330   int ok = 0;
   1331 
   1332   b64_cdh = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
   1333   b64_rpid = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
   1334   b64_authdata = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
   1335   b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
   1336 
   1337   if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) {
   1338     debug_dbg(cfg, "Failed to decode authenticator data");
   1339     goto err;
   1340   }
   1341 
   1342   if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) {
   1343     debug_dbg(cfg, "Failed to decode signature");
   1344     goto err;
   1345   }
   1346 
   1347   r = fido_assert_set_count(assert, 1);
   1348   if (r != FIDO_OK) {
   1349     debug_dbg(cfg, "Failed to set signature count of assertion");
   1350     goto err;
   1351   }
   1352 
   1353   r = fido_assert_set_authdata(assert, 0, authdata, authdata_len);
   1354   if (r != FIDO_OK) {
   1355     debug_dbg(cfg, "Failed to set authdata of assertion");
   1356     goto err;
   1357   }
   1358 
   1359   r = fido_assert_set_sig(assert, 0, sig, sig_len);
   1360   if (r != FIDO_OK) {
   1361     debug_dbg(cfg, "Failed to set signature of assertion");
   1362     goto err;
   1363   }
   1364 
   1365   ok = 1;
   1366 err:
   1367   free(b64_cdh);
   1368   free(b64_rpid);
   1369   free(b64_authdata);
   1370   free(b64_sig);
   1371   free(authdata);
   1372   free(sig);
   1373 
   1374   return ok;
   1375 }
   1376 
   1377 int do_manual_authentication(const cfg_t *cfg, const device_t *devices,
   1378                              const unsigned n_devs, pam_handle_t *pamh) {
   1379   fido_assert_t **assert = NULL;
   1380   struct pk *pk = NULL;
   1381   char *b64_challenge = NULL;
   1382   char prompt[MAX_PROMPT_LEN];
   1383   char buf[MAX_PROMPT_LEN];
   1384   int retval = PAM_AUTH_ERR;
   1385   int n;
   1386   int r;
   1387   unsigned i = 0;
   1388   struct opts opts;
   1389 
   1390   init_opts(&opts);
   1391   assert = calloc(n_devs, sizeof(*assert));
   1392   if (assert == NULL)
   1393 	goto out;
   1394   pk = calloc(n_devs, sizeof(*pk));
   1395   if (pk == NULL)
   1396 	goto out;
   1397 
   1398 #ifndef WITH_FUZZING
   1399   fido_init(cfg->debug ? FIDO_DEBUG : 0);
   1400 #else
   1401   fido_init(0);
   1402 #endif
   1403 
   1404   for (i = 0; i < n_devs; ++i) {
   1405     /* options used during authentication */
   1406     parse_opts(cfg, devices[i].attributes, &opts);
   1407     assert[i] = prepare_assert(cfg, &devices[i], &opts);
   1408     if (assert[i] == NULL) {
   1409       debug_dbg(cfg, "Failed to prepare assert");
   1410       goto out;
   1411     }
   1412 
   1413     debug_dbg(cfg, "Attempting authentication with device number %d", i + 1);
   1414 
   1415     if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
   1416                   devices[i].publicKey, &pk[i])) {
   1417       debug_dbg(cfg, "Unable to parse public key %u", i);
   1418       goto out;
   1419     }
   1420 
   1421     if (!b64_encode(fido_assert_clientdata_hash_ptr(assert[i]),
   1422                     fido_assert_clientdata_hash_len(assert[i]),
   1423                     &b64_challenge)) {
   1424       debug_dbg(cfg, "Failed to encode challenge");
   1425       goto out;
   1426     }
   1427 
   1428     debug_dbg(cfg, "Challenge: %s", b64_challenge);
   1429 
   1430     n = snprintf(prompt, sizeof(prompt), "Challenge #%d:", i + 1);
   1431     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
   1432       debug_dbg(cfg, "Failed to print challenge prompt");
   1433       goto out;
   1434     }
   1435 
   1436     converse(pamh, PAM_TEXT_INFO, prompt);
   1437 
   1438     n = snprintf(buf, sizeof(buf), "%s\n%s\n%s", b64_challenge, cfg->origin,
   1439                  devices[i].keyHandle);
   1440     if (n <= 0 || (size_t) n >= sizeof(buf)) {
   1441       debug_dbg(cfg, "Failed to print fido2-assert input string");
   1442       goto out;
   1443     }
   1444 
   1445     converse(pamh, PAM_TEXT_INFO, buf);
   1446 
   1447     free(b64_challenge);
   1448     b64_challenge = NULL;
   1449   }
   1450 
   1451   converse(pamh, PAM_TEXT_INFO,
   1452            "Please pass the challenge(s) above to fido2-assert, and "
   1453            "paste the results in the prompt below.");
   1454 
   1455   for (i = 0; i < n_devs; ++i) {
   1456     n = snprintf(prompt, sizeof(prompt), "Response #%d: ", i + 1);
   1457     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
   1458       debug_dbg(cfg, "Failed to print response prompt");
   1459       goto out;
   1460     }
   1461 
   1462     if (!manual_get_assert(cfg, prompt, pamh, assert[i])) {
   1463       debug_dbg(cfg, "Failed to get assert %u", i);
   1464       goto out;
   1465     }
   1466 
   1467     r = fido_assert_verify(assert[i], 0, pk[i].type, pk[i].ptr);
   1468     if (r == FIDO_OK) {
   1469       retval = PAM_SUCCESS;
   1470       break;
   1471     }
   1472   }
   1473 
   1474 out:
   1475   for (i = 0; i < n_devs; i++) {
   1476     fido_assert_free(&assert[i]);
   1477     reset_pk(&pk[i]);
   1478   }
   1479   free(assert);
   1480   free(pk);
   1481   free(b64_challenge);
   1482 
   1483   return retval;
   1484 }
   1485 
   1486 static int _converse(pam_handle_t *pamh, int nargs,
   1487                      const struct pam_message **message,
   1488                      struct pam_response **response) {
   1489   struct pam_conv *conv;
   1490   int retval;
   1491 
   1492   retval = pam_get_item(pamh, PAM_CONV, (void *) &conv);
   1493 
   1494   if (retval != PAM_SUCCESS) {
   1495     return retval;
   1496   }
   1497 
   1498   return conv->conv(nargs, message, response, conv->appdata_ptr);
   1499 }
   1500 
   1501 char *converse(pam_handle_t *pamh, int echocode, const char *prompt) {
   1502   const struct pam_message msg = {.msg_style = echocode,
   1503                                   .msg = (char *) (uintptr_t) prompt};
   1504   const struct pam_message *msgs = &msg;
   1505   struct pam_response *resp = NULL;
   1506   int retval = _converse(pamh, 1, &msgs, &resp);
   1507   char *ret = NULL;
   1508 
   1509   if (retval != PAM_SUCCESS || resp == NULL || resp->resp == NULL ||
   1510       *resp->resp == '\000') {
   1511 
   1512     if (retval == PAM_SUCCESS && resp && resp->resp) {
   1513       ret = resp->resp;
   1514     }
   1515   } else {
   1516     ret = resp->resp;
   1517   }
   1518 
   1519   // Deallocate temporary storage.
   1520   if (resp) {
   1521     if (!ret) {
   1522       free(resp->resp);
   1523     }
   1524     free(resp);
   1525   }
   1526 
   1527   return ret;
   1528 }
   1529 
   1530 #ifndef RANDOM_DEV
   1531 #define RANDOM_DEV "/dev/urandom"
   1532 #endif
   1533 
   1534 int random_bytes(void *buf, size_t cnt) {
   1535   int fd;
   1536   ssize_t n;
   1537 
   1538   fd = open(RANDOM_DEV, O_RDONLY);
   1539   if (fd < 0)
   1540     return (0);
   1541 
   1542   n = read(fd, buf, cnt);
   1543   close(fd);
   1544   if (n < 0 || (size_t) n != cnt)
   1545     return (0);
   1546 
   1547   return (1);
   1548 }
   1549