xref: /netbsd-src/external/bsd/pam-u2f/dist/util.c (revision d16b7486a53dcb8072b60ec6fcb4373a2d0c27b7)
1 /*
2  * Copyright (C) 2014-2019 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 <stdarg.h>
19 #include <syslog.h>
20 #include <pwd.h>
21 #include <errno.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <arpa/inet.h>
25 
26 #include "b64.h"
27 #include "util.h"
28 
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@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@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_format(const cfg_t *cfg, const char *username,
170                                char *buf, FILE *opwfile, device_t *devices,
171                                unsigned *n_devs) {
172 
173   char *s_user, *s_credential;
174   const char *s_token;
175   unsigned i;
176 
177   while (fgets(buf, (int) (DEVSIZE * (cfg->max_devs - 1)), opwfile)) {
178     char *saveptr = NULL;
179     size_t len = strlen(buf);
180     if (len > 0 && buf[len - 1] == '\n')
181       buf[len - 1] = '\0';
182 
183     if (cfg->debug)
184       D(cfg->debug_file, "Authorization line: %s", buf);
185 
186     s_user = strtok_r(buf, ":", &saveptr);
187     if (s_user && strcmp(username, s_user) == 0) {
188       if (cfg->debug)
189         D(cfg->debug_file, "Matched user: %s", s_user);
190 
191       // only keep last line for this user
192       for (i = 0; i < *n_devs; i++) {
193         reset_device(&devices[i]);
194       }
195       *n_devs = 0;
196 
197       i = 0;
198       while ((s_credential = strtok_r(NULL, ":", &saveptr)) != NULL) {
199         // s_credential is the whole line now
200         char *credsaveptr = NULL;
201 
202         if ((*n_devs)++ > cfg->max_devs - 1) {
203           *n_devs = cfg->max_devs;
204           if (cfg->debug) {
205             D(cfg->debug_file,
206               "Found more than %d devices, ignoring the remaining ones",
207               cfg->max_devs);
208           }
209           break;
210         }
211 
212         reset_device(&devices[i]);
213 
214         s_token = strtok_r(s_credential, ",", &credsaveptr);
215 
216         if (!s_token) {
217           if (cfg->debug) {
218             D(cfg->debug_file,
219               "Unable to retrieve keyHandle for device %d", i + 1);
220           }
221           return -1;
222         }
223 
224         if (cfg->debug) {
225           D(cfg->debug_file, "KeyHandle for device number %d: %s", i + 1,
226             s_token);
227         }
228 
229         devices[i].keyHandle = strdup(s_token);
230 
231         if (!devices[i].keyHandle) {
232           if (cfg->debug) {
233             D(cfg->debug_file,
234               "Unable to allocate memory for keyHandle number %d", i);
235           }
236           return -1;
237         }
238 
239         if (is_resident(devices[i].keyHandle) && cfg->debug) {
240           D(cfg->debug_file, "Credential is resident");
241         }
242 
243         s_token = strtok_r(NULL, ",", &credsaveptr);
244 
245         if (!s_token) {
246           if (cfg->debug) {
247             D(cfg->debug_file, "Unable to retrieve publicKey number %d", i + 1);
248           }
249           return -1;
250         }
251 
252         if (cfg->debug) {
253           D(cfg->debug_file, "publicKey for device number %d: %s", i + 1,
254             s_token);
255         }
256 
257         devices[i].publicKey = strdup(s_token);
258 
259         if (!devices[i].publicKey) {
260           if (cfg->debug) {
261             D(cfg->debug_file,
262               "Unable to allocate memory for publicKey number %d", i);
263           }
264           return -1;
265         }
266 
267         s_token = strtok_r(NULL, ",", &credsaveptr);
268 
269         if (!s_token) {
270           if (cfg->debug) {
271             D(cfg->debug_file, "Unable to retrieve COSE type %d", i + 1);
272             D(cfg->debug_file, "Assuming ES256 (backwards compatibility)");
273           }
274           devices[i].old_format = 1;
275           devices[i].coseType = strdup("es256");
276         } else {
277           if (cfg->debug) {
278             D(cfg->debug_file, "COSE type for device number %d: %s", i + 1,
279               s_token);
280           }
281           devices[i].coseType = strdup(s_token);
282         }
283 
284         if (!devices[i].coseType) {
285           if (cfg->debug) {
286             D(cfg->debug_file,
287               "Unable to allocate memory for COSE type number %d", i);
288           }
289           return -1;
290         }
291 
292         s_token = strtok_r(NULL, ",", &credsaveptr);
293 
294         if (devices[i].old_format == 1) {
295           if (cfg->debug) {
296             D(cfg->debug_file, "Old format for device %d, no attributes",
297               i + 1);
298             D(cfg->debug_file, "Assuming 'presence' (backwards compatibility)");
299           }
300           s_token = "+presence";
301         } else if (!s_token) {
302           s_token = "";
303         }
304 
305         if (cfg->debug) {
306           D(cfg->debug_file, "Attributes for device number %d: %s", i + 1,
307             s_token);
308         }
309         devices[i].attributes = strdup(s_token);
310 
311         if (!devices[i].attributes) {
312           if (cfg->debug) {
313             D(cfg->debug_file,
314               "Unable to allocate memory for attributes number %d", i);
315           }
316           return -1;
317         }
318 
319         if (devices[i].old_format) {
320           char *websafe_b64 = devices[i].keyHandle;
321           devices[i].keyHandle = normal_b64(websafe_b64);
322           free(websafe_b64);
323           if (!devices[i].keyHandle) {
324             if (cfg->debug) {
325               D(cfg->debug_file,
326                 "Unable to allocate memory for keyHandle number %d", i);
327             }
328             return -1;
329           }
330         }
331 
332         i++;
333       }
334     }
335   }
336 
337   return 1;
338 }
339 
340 static int load_ssh_key(const cfg_t *cfg, char *buf, size_t buf_size,
341                         FILE *opwfile, size_t opwfile_size) {
342   char *cp = buf;
343   int ch;
344 
345   if (opwfile_size > buf_size ||
346       opwfile_size < SSH_HEADER_LEN + SSH_TRAILER_LEN) {
347     if (cfg->debug) {
348       D(cfg->debug_file, "Malformed SSH key (length)");
349     }
350     return 0;
351   }
352 
353   // NOTE(adma): +1 for \0
354   if (fgets(buf, (int)(SSH_HEADER_LEN + 1), opwfile) == NULL ||
355       strlen(buf) != SSH_HEADER_LEN ||
356       strncmp(buf, SSH_HEADER, SSH_HEADER_LEN) != 0) {
357     if (cfg->debug) {
358       D(cfg->debug_file, "Malformed SSH key (header)");
359     }
360     return 0;
361   }
362 
363   while (opwfile_size > 0 && buf_size > 1) {
364     ch = fgetc(opwfile);
365     if (ch == EOF) {
366       if (cfg->debug) {
367         D(cfg->debug_file, "Unexpected authfile termination");
368       }
369       return 0;
370     }
371 
372     opwfile_size--;
373     buf_size--;
374 
375     if (ch != '\n' && ch != '\r') {
376       *cp = (char) ch;
377       if (ch == '-') {
378         // NOTE(adma): no +1 here since we already read one '-'
379         if (buf_size < SSH_TRAILER_LEN ||
380             fgets(cp + 1, (int)SSH_TRAILER_LEN, opwfile) == NULL ||
381             strlen(cp) != SSH_TRAILER_LEN ||
382             strncmp(cp, SSH_TRAILER, SSH_TRAILER_LEN) != 0) {
383           if (cfg->debug) {
384             D(cfg->debug_file, "Malformed SSH key (trailer)");
385           }
386           return 0;
387         }
388 
389         *(cp) = '\0';
390         break;
391       } else {
392         cp++;
393       }
394     }
395   }
396 
397   if (cfg->debug) { // TODO(adma): too verbose? Delete?
398     D(cfg->debug_file, "Credential is \"%s\"", buf);
399   }
400 
401   return 1;
402 }
403 
404 static int ssh_get(const unsigned char **buf, size_t *size, unsigned char *dst,
405                    size_t len) {
406   if (*size < len)
407     return 0;
408   if (dst != NULL)
409     memcpy(dst, *buf, len);
410   *buf += len;
411   *size -= len;
412   return 1;
413 }
414 
415 static int ssh_get_u8(const unsigned char **buf, size_t *size, uint8_t *val) {
416   return ssh_get(buf, size, val, sizeof(*val));
417 }
418 
419 static int ssh_get_u32(const unsigned char **buf, size_t *size, uint32_t *val) {
420   if (!ssh_get(buf, size, (unsigned char *) val, sizeof(*val)))
421     return 0;
422   if (val != NULL)
423     *val = ntohl(*val);
424   return 1;
425 }
426 
427 static int ssh_get_string_ref(const unsigned char **buf, size_t *size,
428                               const unsigned char **ref, size_t *lenp) {
429   uint32_t len;
430 
431   if (!ssh_get_u32(buf, size, &len))
432     return 0;
433   if (!ssh_get(buf, size, NULL, len))
434     return 0;
435   if (ref != NULL)
436     *ref = *buf - len;
437   if (lenp != NULL)
438     *lenp = len;
439   return 1;
440 }
441 
442 static int ssh_get_cstring(const unsigned char **buf, size_t *size, char **str,
443                            size_t *lenp) {
444   const unsigned char *ref;
445   size_t len;
446 
447   if (!ssh_get_string_ref(buf, size, &ref, &len))
448     return 0;
449   if (str != NULL) {
450     if (len > SIZE_MAX - 1 || (*str = calloc(1, len + 1)) == NULL)
451       return 0;
452     memcpy(*str, ref, len);
453   }
454   if (lenp != NULL)
455     *lenp = len;
456   return 1;
457 }
458 
459 static int ssh_log_cstring(const cfg_t *cfg, const unsigned char **buf,
460                            size_t *size, const char *name) {
461   char *str = NULL;
462   size_t len;
463 
464   (void) name; // silence compiler warnings if PAM_DEBUG disabled
465 
466   if (!ssh_get_cstring(buf, size, &str, &len)) {
467     if (cfg->debug)
468       D(cfg->debug_file, "Malformed SSH key (%s)", name);
469     return 0;
470   }
471   if (cfg->debug)
472     D(cfg->debug_file, "%s (%zu) \"%s\"", name, len, str);
473 
474   free(str);
475   return 1;
476 }
477 
478 static int ssh_get_attrs(const cfg_t *cfg, const unsigned char **buf,
479                          size_t *size, char **attrs) {
480   char tmp[32] = {0};
481   uint8_t flags;
482   int r;
483 
484   // flags
485   if (!ssh_get_u8(buf, size, &flags)) {
486     if (cfg->debug) {
487       D(cfg->debug_file, "Malformed SSH key (flags)");
488     }
489     return 0;
490   }
491   if (cfg->debug) {
492     D(cfg->debug_file, "flags: %02x", flags);
493   }
494 
495   r = snprintf(tmp, sizeof(tmp), "%s%s",
496                flags & SSH_SK_USER_PRESENCE_REQD ? "+presence" : "",
497                flags & SSH_SK_USER_VERIFICATION_REQD ? "+verification" : "");
498   if (r < 0 || (size_t) r >= sizeof(tmp)) {
499     if (cfg->debug)
500       D(cfg->debug_file, "Unable to prepare flags");
501     return 0;
502   }
503 
504   if ((*attrs = strdup(tmp)) == NULL) {
505     if (cfg->debug) {
506       D(cfg->debug_file, "Unable to allocate attributes");
507     }
508     return 0;
509   }
510 
511   return 1;
512 }
513 
514 static int ssh_get_pubkey(const cfg_t *cfg, const unsigned char **buf,
515                           size_t *size, char **type_p, char **pubkey_p) {
516   char *ssh_type = NULL;
517   char *ssh_curve = NULL;
518   const unsigned char *blob;
519   size_t len;
520   int type;
521   size_t point_len;
522   int ok = 0;
523 
524   *type_p = NULL;
525   *pubkey_p = NULL;
526 
527   // key type
528   if (!ssh_get_cstring(buf, size, &ssh_type, &len)) {
529     if (cfg->debug) {
530       D(cfg->debug_file, "Malformed SSH key (keytype)");
531     }
532     goto err;
533   }
534 
535   if (len == SSH_ES256_LEN && memcmp(ssh_type, SSH_ES256, SSH_ES256_LEN) == 0) {
536     type = COSE_ES256;
537     point_len = SSH_ES256_POINT_LEN;
538   } else if (len == SSH_EDDSA_LEN &&
539              memcmp(ssh_type, SSH_EDDSA, SSH_EDDSA_LEN) == 0) {
540     type = COSE_EDDSA;
541     point_len = SSH_EDDSA_POINT_LEN;
542   } else {
543     if (cfg->debug) {
544       D(cfg->debug_file, "Unknown key type %s", ssh_type);
545     }
546     goto err;
547   }
548 
549   if (cfg->debug) {
550     D(cfg->debug_file, "keytype (%zu) \"%s\"", len, ssh_type);
551   }
552 
553   if (type == COSE_ES256) {
554     // curve name
555     if (!ssh_get_cstring(buf, size, &ssh_curve, &len)) {
556       if (cfg->debug) {
557         D(cfg->debug_file, "Malformed SSH key (curvename)");
558       }
559       goto err;
560     }
561 
562     if (len == SSH_P256_NAME_LEN &&
563         memcmp(ssh_curve, SSH_P256_NAME, SSH_P256_NAME_LEN) == 0) {
564       if (cfg->debug) {
565         D(cfg->debug_file, "curvename (%zu) \"%s\"", len, ssh_curve);
566       }
567     } else {
568       if (cfg->debug) {
569         D(cfg->debug_file, "Unknown curve %s", ssh_curve);
570       }
571       goto err;
572     }
573   }
574 
575   // point
576   if (!ssh_get_string_ref(buf, size, &blob, &len)) {
577     if (cfg->debug) {
578       D(cfg->debug_file, "Malformed SSH key (point)");
579     }
580     goto err;
581   }
582 
583   if (len != point_len) {
584     if (cfg->debug) {
585       D(cfg->debug_file, "Invalid point length, should be %zu, found %zu",
586         point_len, len);
587     }
588     goto err;
589   }
590 
591   if (type == COSE_ES256) {
592     // Skip the initial '04'
593     if (len < 1) {
594       if (cfg->debug) {
595         D(cfg->debug_file, "Failed to skip initial '04'");
596       }
597       goto err;
598     }
599     blob++;
600     len--;
601   }
602 
603   if (!b64_encode(blob, len, pubkey_p)) {
604     if (cfg->debug) {
605       D(cfg->debug_file, "Unable to allocate public key");
606     }
607     goto err;
608   }
609 
610   if ((*type_p = strdup(cose_string(type))) == NULL) {
611     if (cfg->debug) {
612       D(cfg->debug_file, "Unable to allocate COSE type");
613     }
614     goto err;
615   }
616 
617   ok = 1;
618 err:
619   if (!ok) {
620     free(*type_p);
621     free(*pubkey_p);
622     *type_p = NULL;
623     *pubkey_p = NULL;
624   }
625   free(ssh_type);
626   free(ssh_curve);
627 
628   return ok;
629 }
630 
631 static int parse_ssh_format(const cfg_t *cfg, char *buf, size_t buf_size,
632                             FILE *opwfile, size_t opwfile_size,
633                             device_t *devices, unsigned *n_devs) {
634 
635   const unsigned char *decoded;
636   unsigned char *decoded_initial = NULL;
637   size_t decoded_len;
638   const unsigned char *blob;
639   uint32_t check1, check2, tmp;
640   size_t len;
641 
642   // The logic below is inspired by
643   // how ssh parses its own keys. See sshkey.c
644   reset_device(&devices[0]);
645 
646   if (!load_ssh_key(cfg, buf, buf_size, opwfile, opwfile_size) ||
647       !b64_decode(buf, (void **) &decoded_initial, &decoded_len)) {
648     if (cfg->debug) {
649       D(cfg->debug_file, "Unable to decode credential");
650     }
651     goto out;
652   }
653 
654   decoded = decoded_initial;
655 
656   // magic
657   if (decoded_len < SSH_AUTH_MAGIC_LEN ||
658       memcmp(decoded, SSH_AUTH_MAGIC, SSH_AUTH_MAGIC_LEN) != 0) {
659     if (cfg->debug) {
660       D(cfg->debug_file, "Malformed SSH key (magic)");
661     }
662     goto out;
663   }
664 
665   decoded += SSH_AUTH_MAGIC_LEN;
666   decoded_len -= SSH_AUTH_MAGIC_LEN;
667 
668   if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "ciphername") ||
669       !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfname") ||
670       !ssh_log_cstring(cfg, &decoded, &decoded_len, "kdfoptions"))
671     goto out;
672 
673   if (!ssh_get_u32(&decoded, &decoded_len, &tmp)) {
674     if (cfg->debug) {
675       D(cfg->debug_file, "Malformed SSH key (nkeys)");
676     }
677     goto out;
678   }
679   if (cfg->debug) {
680     D(cfg->debug_file, "nkeys: %" PRIu32, tmp);
681   }
682   if (tmp != 1) {
683     if (cfg->debug) {
684       D(cfg->debug_file, "Multiple keys not supported");
685     }
686     goto out;
687   }
688 
689   // public_key (skip)
690   if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
691     if (cfg->debug) {
692       D(cfg->debug_file, "Malformed SSH key (pubkey)");
693     }
694     goto out;
695   }
696 
697   // private key (consume length)
698   if (!ssh_get_u32(&decoded, &decoded_len, &tmp) || decoded_len < tmp) {
699     if (cfg->debug) {
700       D(cfg->debug_file, "Malformed SSH key (pvtkey length)");
701     }
702     goto out;
703   }
704 
705   // check1, check2
706   if (!ssh_get_u32(&decoded, &decoded_len, &check1) ||
707       !ssh_get_u32(&decoded, &decoded_len, &check2)) {
708     if (cfg->debug) {
709       D(cfg->debug_file, "Malformed SSH key (check1, check2)");
710     }
711     goto out;
712   }
713   if (cfg->debug) {
714     D(cfg->debug_file, "check1: %" PRIu32, check1);
715     D(cfg->debug_file, "check2: %" PRIu32, check2);
716   }
717   if (check1 != check2) {
718     if (cfg->debug) {
719       D(cfg->debug_file, "Mismatched check values");
720       goto out;
721     }
722   }
723 
724   if (!ssh_get_pubkey(cfg, &decoded, &decoded_len, &devices[0].coseType,
725                       &devices[0].publicKey) ||
726       !ssh_log_cstring(cfg, &decoded, &decoded_len, "application") ||
727       !ssh_get_attrs(cfg, &decoded, &decoded_len, &devices[0].attributes))
728     goto out;
729 
730   // keyhandle
731   if (!ssh_get_string_ref(&decoded, &decoded_len, &blob, &len) ||
732       !b64_encode(blob, len, &devices[0].keyHandle)) {
733     if (cfg->debug) {
734       D(cfg->debug_file, "Malformed SSH key (keyhandle)");
735     }
736     goto out;
737   }
738 
739   if (cfg->debug) {
740     D(cfg->debug_file, "KeyHandle for device number 1: %s",
741       devices[0].keyHandle);
742     D(cfg->debug_file, "publicKey for device number 1: %s",
743       devices[0].publicKey);
744     D(cfg->debug_file, "COSE type for device number 1: %s",
745       devices[0].coseType);
746     D(cfg->debug_file, "Attributes for device number 1: %s",
747       devices[0].attributes);
748   }
749 
750   // reserved (skip)
751   if (!ssh_get_string_ref(&decoded, &decoded_len, NULL, NULL)) {
752     if (cfg->debug) {
753       D(cfg->debug_file, "Malformed SSH key (reserved)");
754     }
755     goto out;
756   }
757 
758   // comment
759   if (!ssh_log_cstring(cfg, &decoded, &decoded_len, "comment"))
760     goto out;
761 
762   // padding
763   if (decoded_len >= 255) {
764     if (cfg->debug) {
765       D(cfg->debug_file, "Malformed SSH key (padding length)");
766     }
767     goto out;
768   }
769 
770   for (int i = 1; (unsigned) i <= decoded_len; i++) {
771     if (decoded[i - 1] != i) {
772       if (cfg->debug) {
773         D(cfg->debug_file, "Malformed SSH key (padding)");
774       }
775       goto out;
776     }
777   }
778 
779   free(decoded_initial);
780   decoded_initial = NULL;
781 
782   *n_devs = 1;
783 
784   return 1;
785 
786 out:
787   reset_device(&devices[0]);
788 
789   if (decoded_initial) {
790     free(decoded_initial);
791     decoded_initial = NULL;
792   }
793 
794   return -1;
795 }
796 
797 int get_devices_from_authfile(const cfg_t *cfg, const char *username,
798                               device_t *devices, unsigned *n_devs) {
799 
800   char *buf = NULL;
801   int retval = 0;
802   int fd = -1;
803   struct stat st;
804   struct passwd *pw = NULL, pw_s;
805   char buffer[BUFSIZE];
806   int gpu_ret;
807   FILE *opwfile = NULL;
808   size_t opwfile_size;
809   unsigned i;
810 
811   /* Ensure we never return uninitialized count. */
812   *n_devs = 0;
813 
814   fd = open(cfg->auth_file, O_RDONLY | O_CLOEXEC | O_NOCTTY);
815   if (fd < 0) {
816     if (cfg->debug)
817       D(cfg->debug_file, "Cannot open file: %s (%s)", cfg->auth_file,
818         strerror(errno));
819     goto err;
820   }
821 
822   if (fstat(fd, &st) < 0) {
823     if (cfg->debug)
824       D(cfg->debug_file, "Cannot stat file: %s (%s)", cfg->auth_file,
825         strerror(errno));
826     goto err;
827   }
828 
829   if (!S_ISREG(st.st_mode)) {
830     if (cfg->debug)
831       D(cfg->debug_file, "%s is not a regular file", cfg->auth_file);
832     goto err;
833   }
834 
835   if (st.st_size == 0) {
836     if (cfg->debug)
837       D(cfg->debug_file, "File %s is empty", cfg->auth_file);
838     goto err;
839   }
840   opwfile_size = (size_t)st.st_size;
841 
842   gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw);
843   if (gpu_ret != 0 || pw == NULL) {
844     D(cfg->debug_file, "Unable to retrieve credentials for uid %u, (%s)",
845       st.st_uid, strerror(errno));
846     goto err;
847   }
848 
849   if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) {
850     if (strcmp(username, "root") != 0) {
851       D(cfg->debug_file,
852         "The owner of the authentication file is neither %s nor root",
853         username);
854     } else {
855       D(cfg->debug_file, "The owner of the authentication file is not root");
856     }
857     goto err;
858   }
859 
860   opwfile = fdopen(fd, "r");
861   if (opwfile == NULL) {
862     if (cfg->debug)
863       D(cfg->debug_file, "fdopen: %s", strerror(errno));
864     goto err;
865   } else {
866     fd = -1; /* fd belongs to opwfile */
867   }
868 
869   buf = calloc(1, (DEVSIZE * cfg->max_devs));
870   if (!buf) {
871     if (cfg->debug)
872       D(cfg->debug_file, "Unable to allocate memory");
873     goto err;
874   }
875 
876   if (cfg->sshformat == 0) {
877     retval = parse_native_format(cfg, username, buf, opwfile, devices, n_devs);
878   } else {
879     retval = parse_ssh_format(cfg, buf, DEVSIZE * cfg->max_devs, opwfile,
880                               opwfile_size, devices, n_devs);
881   }
882 
883   if (retval != 1) {
884     // NOTE(adma): error message is logged by the previous function
885     goto err;
886   }
887 
888   if (cfg->debug)
889     D(cfg->debug_file, "Found %d device(s) for user %s", *n_devs, username);
890 
891   retval = 1;
892   goto out;
893 
894 err:
895   for (i = 0; i < *n_devs; i++) {
896     reset_device(&devices[i]);
897   }
898 
899   *n_devs = 0;
900 
901 out:
902   if (buf) {
903     free(buf);
904     buf = NULL;
905   }
906 
907   if (opwfile)
908     fclose(opwfile);
909 
910   if (fd != -1)
911     close(fd);
912 
913   return retval;
914 }
915 
916 void free_devices(device_t *devices, const unsigned n_devs) {
917   unsigned i;
918 
919   if (!devices)
920     return;
921 
922   for (i = 0; i < n_devs; i++) {
923     reset_device(&devices[i]);
924   }
925 
926   free(devices);
927   devices = NULL;
928 }
929 
930 static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist,
931                               size_t devlist_len, fido_assert_t *assert,
932                               const int rk, fido_dev_t **authlist) {
933   const fido_dev_info_t *di = NULL;
934   fido_dev_t *dev = NULL;
935   int r;
936   size_t i;
937   size_t j;
938 
939   if (cfg->debug)
940     D(cfg->debug_file, "Working with %zu authenticator(s)", devlist_len);
941 
942   for (i = 0, j = 0; i < devlist_len; i++) {
943     if (cfg->debug)
944       D(cfg->debug_file, "Checking whether key exists in authenticator %zu", i);
945 
946     di = fido_dev_info_ptr(devlist, i);
947     if (!di) {
948       if (cfg->debug)
949         D(cfg->debug_file, "Unable to get device pointer");
950       continue;
951     }
952 
953     if (cfg->debug)
954       D(cfg->debug_file, "Authenticator path: %s", fido_dev_info_path(di));
955 
956     dev = fido_dev_new();
957     if (!dev) {
958       if (cfg->debug)
959         D(cfg->debug_file, "Unable to allocate device type");
960       continue;
961     }
962 
963     r = fido_dev_open(dev, fido_dev_info_path(di));
964     if (r != FIDO_OK) {
965       if (cfg->debug)
966         D(cfg->debug_file, "Failed to open authenticator: %s (%d)",
967           fido_strerr(r), r);
968       fido_dev_free(&dev);
969       continue;
970     }
971 
972     if (rk || cfg->nodetect) {
973       /* resident credential or nodetect: try all authenticators */
974       authlist[j++] = dev;
975     } else {
976       r = fido_dev_get_assert(dev, assert, NULL);
977       if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) ||
978           (fido_dev_is_fido2(dev) && r == FIDO_OK)) {
979         authlist[j++] = dev;
980         if (cfg->debug)
981           D(cfg->debug_file, "Found key in authenticator %zu", i);
982         return (1);
983       }
984       if (cfg->debug)
985         D(cfg->debug_file, "Key not found in authenticator %zu", i);
986 
987       fido_dev_close(dev);
988       fido_dev_free(&dev);
989     }
990   }
991 
992   if (j != 0)
993     return (1);
994   else {
995     if (cfg->debug)
996       D(cfg->debug_file, "Key not found");
997     return (0);
998   }
999 }
1000 
1001 static void init_opts(struct opts *opts) {
1002   opts->up = FIDO_OPT_FALSE;
1003   opts->uv = FIDO_OPT_OMIT;
1004   opts->pin = FIDO_OPT_FALSE;
1005 }
1006 
1007 static void parse_opts(const cfg_t *cfg, const char *attr, struct opts *opts) {
1008   if (cfg->userpresence == 1 || strstr(attr, "+presence")) {
1009     opts->up = FIDO_OPT_TRUE;
1010   } else if (cfg->userpresence == 0) {
1011     opts->up = FIDO_OPT_FALSE;
1012   } else {
1013     opts->up = FIDO_OPT_OMIT;
1014   }
1015 
1016   if (cfg->userverification == 1 || strstr(attr, "+verification")) {
1017     opts->uv = FIDO_OPT_TRUE;
1018   } else if (cfg->userverification == 0)
1019     opts->uv = FIDO_OPT_FALSE;
1020   else {
1021     opts->uv = FIDO_OPT_OMIT;
1022   }
1023 
1024   if (cfg->pinverification == 1 || strstr(attr, "+pin")) {
1025     opts->pin = FIDO_OPT_TRUE;
1026   } else if (cfg->pinverification == 0) {
1027     opts->pin = FIDO_OPT_FALSE;
1028   } else {
1029     opts->pin = FIDO_OPT_OMIT;
1030   }
1031 }
1032 
1033 static int get_device_opts(fido_dev_t *dev, int *pin, int *uv) {
1034   fido_cbor_info_t *info = NULL;
1035   char *const *ptr;
1036   const bool *val;
1037   size_t len;
1038 
1039   *pin = *uv = -1; /* unsupported */
1040 
1041   if (fido_dev_is_fido2(dev)) {
1042     if ((info = fido_cbor_info_new()) == NULL ||
1043         fido_dev_get_cbor_info(dev, info) != FIDO_OK) {
1044       fido_cbor_info_free(&info);
1045       return 0;
1046     }
1047 
1048     ptr = fido_cbor_info_options_name_ptr(info);
1049     val = fido_cbor_info_options_value_ptr(info);
1050     len = fido_cbor_info_options_len(info);
1051     for (size_t i = 0; i < len; i++) {
1052       if (strcmp(ptr[i], "clientPin") == 0) {
1053         *pin = val[i];
1054       } else if (strcmp(ptr[i], "uv") == 0) {
1055         *uv = val[i];
1056       }
1057     }
1058   }
1059 
1060   fido_cbor_info_free(&info);
1061   return 1;
1062 }
1063 
1064 static int match_device_opts(fido_dev_t *dev, struct opts *opts) {
1065   int pin, uv;
1066 
1067   /* FIXME: fido_dev_{supports,has}_{pin,uv} (1.7.0) */
1068   if (!get_device_opts(dev, &pin, &uv)) {
1069     return -1;
1070   }
1071 
1072   if (opts->uv == FIDO_OPT_FALSE && uv < 0) {
1073     opts->uv = FIDO_OPT_OMIT;
1074   }
1075 
1076   if ((opts->pin == FIDO_OPT_TRUE && pin != 1) ||
1077       (opts->uv == FIDO_OPT_TRUE && uv != 1)) {
1078     return 0;
1079   }
1080 
1081   return 1;
1082 }
1083 
1084 static int set_opts(const cfg_t *cfg, const struct opts *opts,
1085                     fido_assert_t *assert) {
1086   if (fido_assert_set_up(assert, opts->up) != FIDO_OK) {
1087     if (cfg->debug)
1088       D(cfg->debug_file, "Failed to set UP");
1089     return 0;
1090   }
1091   if (fido_assert_set_uv(assert, opts->uv) != FIDO_OK) {
1092     if (cfg->debug)
1093       D(cfg->debug_file, "Failed to set UV");
1094     return 0;
1095   }
1096 
1097   return 1;
1098 }
1099 
1100 static int set_cdh(const cfg_t *cfg, fido_assert_t *assert) {
1101   unsigned char cdh[32];
1102   int r;
1103 
1104   if (!random_bytes(cdh, sizeof(cdh))) {
1105     if (cfg->debug)
1106       D(cfg->debug_file, "Failed to generate challenge");
1107     return 0;
1108   }
1109 
1110   r = fido_assert_set_clientdata_hash(assert, cdh, sizeof(cdh));
1111   if (r != FIDO_OK) {
1112     if (cfg->debug)
1113       D(cfg->debug_file, "Unable to set challenge: %s (%d)", fido_strerr(r), r);
1114     return 0;
1115   }
1116 
1117   return 1;
1118 }
1119 
1120 static fido_assert_t *prepare_assert(const cfg_t *cfg, const device_t *device,
1121                                      const struct opts *opts) {
1122   fido_assert_t *assert = NULL;
1123   unsigned char *buf = NULL;
1124   size_t buf_len;
1125   int ok = 0;
1126   int r;
1127 
1128   if ((assert = fido_assert_new()) == NULL) {
1129     if (cfg->debug)
1130       D(cfg->debug_file, "Unable to allocate assertion");
1131     goto err;
1132   }
1133 
1134   if (device->old_format && strcmp(cfg->origin, cfg->appid) != 0)
1135     r = fido_assert_set_rp(assert, cfg->appid);
1136   else
1137     r = fido_assert_set_rp(assert, cfg->origin);
1138 
1139   if (r != FIDO_OK) {
1140     if (cfg->debug)
1141       D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r);
1142     goto err;
1143   }
1144 
1145   if (is_resident(device->keyHandle)) {
1146     if (cfg->debug)
1147       D(cfg->debug_file, "Credential is resident");
1148   } else {
1149     if (cfg->debug)
1150       D(cfg->debug_file, "Key handle: %s", device->keyHandle);
1151     if (!b64_decode(device->keyHandle, (void **) &buf, &buf_len)) {
1152       if (cfg->debug)
1153         D(cfg->debug_file, "Failed to decode key handle");
1154       goto err;
1155     }
1156 
1157     r = fido_assert_allow_cred(assert, buf, buf_len);
1158     if (r != FIDO_OK) {
1159       if (cfg->debug)
1160         D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r),
1161           r);
1162       goto err;
1163     }
1164   }
1165 
1166   if (!set_opts(cfg, opts, assert)) {
1167     if (cfg->debug)
1168       D(cfg->debug_file, "Failed to set assert options");
1169     goto err;
1170   }
1171 
1172   if (!set_cdh(cfg, assert)) {
1173     if (cfg->debug)
1174       D(cfg->debug_file, "Failed to set client data hash");
1175     goto err;
1176   }
1177 
1178   ok = 1;
1179 
1180 err:
1181   if (!ok)
1182     fido_assert_free(&assert);
1183 
1184   free(buf);
1185 
1186   return assert;
1187 }
1188 
1189 static void reset_pk(struct pk *pk) {
1190   if (pk->type == COSE_ES256) {
1191     es256_pk_free((es256_pk_t **) &pk->ptr);
1192   } else if (pk->type == COSE_RS256) {
1193     rs256_pk_free((rs256_pk_t **) &pk->ptr);
1194   } else if (pk->type == COSE_EDDSA) {
1195     eddsa_pk_free((eddsa_pk_t **) &pk->ptr);
1196   }
1197   memset(pk, 0, sizeof(*pk));
1198 }
1199 
1200 int cose_type(const char *str, int *type) {
1201   if (strcasecmp(str, "es256") == 0) {
1202     *type = COSE_ES256;
1203   } else if (strcasecmp(str, "rs256") == 0) {
1204     *type = COSE_RS256;
1205   } else if (strcasecmp(str, "eddsa") == 0) {
1206     *type = COSE_EDDSA;
1207   } else {
1208     *type = 0;
1209     return 0;
1210   }
1211 
1212   return 1;
1213 }
1214 
1215 const char *cose_string(int type) {
1216   switch (type) {
1217     case COSE_ES256:
1218       return "es256";
1219     case COSE_RS256:
1220       return "rs256";
1221     case COSE_EDDSA:
1222       return "eddsa";
1223     default:
1224       return "unknown";
1225   }
1226 }
1227 
1228 static int parse_pk(const cfg_t *cfg, int old, const char *type, const char *pk,
1229                     struct pk *out) {
1230   unsigned char *buf = NULL;
1231   size_t buf_len;
1232   int ok = 0;
1233   int r;
1234 
1235   reset_pk(out);
1236 
1237   if (old) {
1238     if (!hex_decode(pk, &buf, &buf_len)) {
1239       if (cfg->debug)
1240         D(cfg->debug_file, "Failed to decode public key");
1241       goto err;
1242     }
1243   } else {
1244     if (!b64_decode(pk, (void **) &buf, &buf_len)) {
1245       if (cfg->debug)
1246         D(cfg->debug_file, "Failed to decode public key");
1247       goto err;
1248     }
1249   }
1250 
1251   if (!cose_type(type, &out->type)) {
1252     if (cfg->debug)
1253       D(cfg->debug_file, "Unknown COSE type '%s'", type);
1254     goto err;
1255   }
1256 
1257   // For backwards compatibility, failure to pack the public key is not
1258   // returned as an error.  Instead, it is handled by fido_verify_assert().
1259   if (out->type == COSE_ES256) {
1260     if ((out->ptr = es256_pk_new()) == NULL) {
1261       if (cfg->debug)
1262         D(cfg->debug_file, "Failed to allocate ES256 public key");
1263       goto err;
1264     }
1265     if (old) {
1266       r = translate_old_format_pubkey(out->ptr, buf, buf_len);
1267     } else {
1268       r = es256_pk_from_ptr(out->ptr, buf, buf_len);
1269     }
1270     if (r != FIDO_OK) {
1271       if (cfg->debug)
1272         D(cfg->debug_file, "Failed to convert ES256 public key");
1273     }
1274   } else if (out->type == COSE_RS256) {
1275     if ((out->ptr = rs256_pk_new()) == NULL) {
1276       if (cfg->debug)
1277         D(cfg->debug_file, "Failed to allocate RS256 public key");
1278       goto err;
1279     }
1280     r = rs256_pk_from_ptr(out->ptr, buf, buf_len);
1281     if (r != FIDO_OK) {
1282       if (cfg->debug)
1283         D(cfg->debug_file, "Failed to convert RS256 public key");
1284     }
1285   } else if (out->type == COSE_EDDSA) {
1286     if ((out->ptr = eddsa_pk_new()) == NULL) {
1287       if (cfg->debug)
1288         D(cfg->debug_file, "Failed to allocate EDDSA public key");
1289       goto err;
1290     }
1291     r = eddsa_pk_from_ptr(out->ptr, buf, buf_len);
1292     if (r != FIDO_OK) {
1293       if (cfg->debug)
1294         D(cfg->debug_file, "Failed to convert EDDSA public key");
1295     }
1296   } else {
1297     if (cfg->debug)
1298       D(cfg->debug_file, "COSE type '%s' not handled", type);
1299     goto err;
1300   }
1301 
1302   ok = 1;
1303 err:
1304   free(buf);
1305 
1306   return ok;
1307 }
1308 
1309 int do_authentication(const cfg_t *cfg, const device_t *devices,
1310                       const unsigned n_devs, pam_handle_t *pamh) {
1311   fido_assert_t *assert = NULL;
1312   fido_dev_info_t *devlist = NULL;
1313   fido_dev_t **authlist = NULL;
1314   int cued = 0;
1315   int r;
1316   int retval = -2;
1317   size_t ndevs = 0;
1318   size_t ndevs_prev = 0;
1319   unsigned i = 0;
1320   struct opts opts;
1321   struct pk pk;
1322   char *pin = NULL;
1323 
1324   init_opts(&opts);
1325 #ifndef WITH_FUZZING
1326   fido_init(cfg->debug ? FIDO_DEBUG : 0);
1327 #else
1328   fido_init(0);
1329 #endif
1330   memset(&pk, 0, sizeof(pk));
1331 
1332   devlist = fido_dev_info_new(64);
1333   if (!devlist) {
1334     if (cfg->debug)
1335       D(cfg->debug_file, "Unable to allocate devlist");
1336     goto out;
1337   }
1338 
1339   r = fido_dev_info_manifest(devlist, 64, &ndevs);
1340   if (r != FIDO_OK) {
1341     if (cfg->debug)
1342       D(cfg->debug_file, "Unable to discover device(s), %s (%d)",
1343         fido_strerr(r), r);
1344     goto out;
1345   }
1346 
1347   ndevs_prev = ndevs;
1348 
1349   if (cfg->debug)
1350     D(cfg->debug_file, "Device max index is %zu", ndevs);
1351 
1352   authlist = calloc(64 + 1, sizeof(fido_dev_t *));
1353   if (!authlist) {
1354     if (cfg->debug)
1355       D(cfg->debug_file, "Unable to allocate authenticator list");
1356     goto out;
1357   }
1358 
1359   if (cfg->nodetect && cfg->debug)
1360     D(cfg->debug_file,
1361       "nodetect option specified, suitable key detection will be skipped");
1362 
1363   i = 0;
1364   while (i < n_devs) {
1365     retval = -2;
1366 
1367     if (cfg->debug)
1368       D(cfg->debug_file, "Attempting authentication with device number %d",
1369         i + 1);
1370 
1371     init_opts(&opts); /* used during authenticator discovery */
1372     assert = prepare_assert(cfg, &devices[i], &opts);
1373     if (assert == NULL) {
1374       if (cfg->debug)
1375         D(cfg->debug_file, "Failed to prepare assert");
1376       goto out;
1377     }
1378 
1379     if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
1380                   devices[i].publicKey, &pk)) {
1381       if (cfg->debug)
1382         D(cfg->debug_file, "Failed to parse public key");
1383       goto out;
1384     }
1385 
1386     if (get_authenticators(cfg, devlist, ndevs, assert,
1387                            is_resident(devices[i].keyHandle), authlist)) {
1388       for (size_t j = 0; authlist[j] != NULL; j++) {
1389         /* options used during authentication */
1390         parse_opts(cfg, devices[i].attributes, &opts);
1391 
1392         r = match_device_opts(authlist[j], &opts);
1393         if (r != 1) {
1394           if (cfg->debug) {
1395             D(cfg->debug_file, "%s, skipping authenticator",
1396               r < 0 ? "Failed to query supported options"
1397                     : "Unsupported options");
1398           }
1399           continue;
1400         }
1401 
1402         if (!set_opts(cfg, &opts, assert)) {
1403           if (cfg->debug)
1404             D(cfg->debug_file, "Failed to set assert options");
1405           goto out;
1406         }
1407 
1408         if (!set_cdh(cfg, assert)) {
1409           if (cfg->debug)
1410             D(cfg->debug_file, "Failed to reset client data hash");
1411           goto out;
1412         }
1413 
1414         if (opts.pin == FIDO_OPT_TRUE) {
1415           pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: ");
1416           if (pin == NULL) {
1417             D(cfg->debug_file, "converse() returned NULL");
1418             goto out;
1419           }
1420         }
1421         if (opts.up == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) {
1422           if (cfg->manual == 0 && cfg->cue && !cued) {
1423             cued = 1;
1424             converse(pamh, PAM_TEXT_INFO,
1425                      cfg->cue_prompt != NULL ? cfg->cue_prompt : DEFAULT_CUE);
1426           }
1427         }
1428         r = fido_dev_get_assert(authlist[j], assert, pin);
1429         if (pin) {
1430           explicit_bzero(pin, strlen(pin));
1431           free(pin);
1432           pin = NULL;
1433         }
1434         if (r == FIDO_OK) {
1435           if (opts.pin == FIDO_OPT_TRUE || opts.uv == FIDO_OPT_TRUE) {
1436             r = fido_assert_set_uv(assert, FIDO_OPT_TRUE);
1437             if (r != FIDO_OK) {
1438               D(cfg->debug_file, "Failed to set UV");
1439               goto out;
1440             }
1441           }
1442           r = fido_assert_verify(assert, 0, pk.type, pk.ptr);
1443           if (r == FIDO_OK) {
1444             retval = 1;
1445             goto out;
1446           }
1447         }
1448       }
1449     } else {
1450       if (cfg->debug)
1451         D(cfg->debug_file, "Device for this keyhandle is not present");
1452     }
1453 
1454     i++;
1455 
1456     fido_dev_info_free(&devlist, ndevs);
1457 
1458     devlist = fido_dev_info_new(64);
1459     if (!devlist) {
1460       if (cfg->debug)
1461         D(cfg->debug_file, "Unable to allocate devlist");
1462       goto out;
1463     }
1464 
1465     r = fido_dev_info_manifest(devlist, 64, &ndevs);
1466     if (r != FIDO_OK) {
1467       if (cfg->debug)
1468         D(cfg->debug_file, "Unable to discover device(s), %s (%d)",
1469           fido_strerr(r), r);
1470       goto out;
1471     }
1472 
1473     if (ndevs > ndevs_prev) {
1474       if (cfg->debug)
1475         D(cfg->debug_file,
1476           "Devices max_index has changed: %zu (was %zu). Starting over", ndevs,
1477           ndevs_prev);
1478       ndevs_prev = ndevs;
1479       i = 0;
1480     }
1481 
1482     for (size_t j = 0; authlist[j] != NULL; j++) {
1483       fido_dev_close(authlist[j]);
1484       fido_dev_free(&authlist[j]);
1485     }
1486 
1487     fido_assert_free(&assert);
1488   }
1489 
1490 out:
1491   reset_pk(&pk);
1492   fido_assert_free(&assert);
1493   fido_dev_info_free(&devlist, ndevs);
1494 
1495   if (authlist) {
1496     for (size_t j = 0; authlist[j] != NULL; j++) {
1497       fido_dev_close(authlist[j]);
1498       fido_dev_free(&authlist[j]);
1499     }
1500     free(authlist);
1501   }
1502 
1503   return retval;
1504 }
1505 
1506 #define MAX_PROMPT_LEN (1024)
1507 
1508 static int manual_get_assert(const cfg_t *cfg, const char *prompt,
1509                              pam_handle_t *pamh, fido_assert_t *assert) {
1510   char *b64_cdh = NULL;
1511   char *b64_rpid = NULL;
1512   char *b64_authdata = NULL;
1513   char *b64_sig = NULL;
1514   unsigned char *authdata = NULL;
1515   unsigned char *sig = NULL;
1516   size_t authdata_len;
1517   size_t sig_len;
1518   int r;
1519   int ok = 0;
1520 
1521   b64_cdh = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1522   b64_rpid = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1523   b64_authdata = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1524   b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1525 
1526   if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) {
1527     if (cfg->debug)
1528       D(cfg->debug_file, "Failed to decode authenticator data");
1529     goto err;
1530   }
1531 
1532   if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) {
1533     if (cfg->debug)
1534       D(cfg->debug_file, "Failed to decode signature");
1535     goto err;
1536   }
1537 
1538   r = fido_assert_set_count(assert, 1);
1539   if (r != FIDO_OK) {
1540     if (cfg->debug)
1541       D(cfg->debug_file, "Failed to set signature count of assertion");
1542     goto err;
1543   }
1544 
1545   r = fido_assert_set_authdata(assert, 0, authdata, authdata_len);
1546   if (r != FIDO_OK) {
1547     if (cfg->debug)
1548       D(cfg->debug_file, "Failed to set authdata of assertion");
1549     goto err;
1550   }
1551 
1552   r = fido_assert_set_sig(assert, 0, sig, sig_len);
1553   if (r != FIDO_OK) {
1554     if (cfg->debug)
1555       D(cfg->debug_file, "Failed to set signature of assertion");
1556     goto err;
1557   }
1558 
1559   ok = 1;
1560 err:
1561   free(b64_cdh);
1562   free(b64_rpid);
1563   free(b64_authdata);
1564   free(b64_sig);
1565   free(authdata);
1566   free(sig);
1567 
1568   return ok;
1569 }
1570 
1571 int do_manual_authentication(const cfg_t *cfg, const device_t *devices,
1572                              const unsigned n_devs, pam_handle_t *pamh) {
1573   fido_assert_t **assert = NULL;
1574   struct pk *pk = NULL;
1575   char *b64_challenge = NULL;
1576   char prompt[MAX_PROMPT_LEN];
1577   char buf[MAX_PROMPT_LEN];
1578   int retval = -2;
1579   int n;
1580   int r;
1581   unsigned i = 0;
1582   struct opts opts;
1583 
1584   init_opts(&opts);
1585   assert = calloc(n_devs, sizeof(*assert));
1586   if (assert == NULL)
1587 	goto out;
1588   pk = calloc(n_devs, sizeof(*pk));
1589   if (pk == NULL)
1590 	goto out;
1591 
1592 #ifndef WITH_FUZZING
1593   fido_init(cfg->debug ? FIDO_DEBUG : 0);
1594 #else
1595   fido_init(0);
1596 #endif
1597 
1598   for (i = 0; i < n_devs; ++i) {
1599     /* options used during authentication */
1600     parse_opts(cfg, devices[i].attributes, &opts);
1601     assert[i] = prepare_assert(cfg, &devices[i], &opts);
1602     if (assert[i] == NULL) {
1603       if (cfg->debug)
1604         D(cfg->debug_file, "Failed to prepare assert");
1605       goto out;
1606     }
1607 
1608     if (cfg->debug)
1609       D(cfg->debug_file, "Attempting authentication with device number %d",
1610         i + 1);
1611 
1612     if (!parse_pk(cfg, devices[i].old_format, devices[i].coseType,
1613                   devices[i].publicKey, &pk[i])) {
1614       if (cfg->debug)
1615         D(cfg->debug_file, "Unable to parse public key %u", i);
1616       goto out;
1617     }
1618 
1619     if (!b64_encode(fido_assert_clientdata_hash_ptr(assert[i]),
1620                     fido_assert_clientdata_hash_len(assert[i]),
1621                     &b64_challenge)) {
1622       if (cfg->debug)
1623         D(cfg->debug_file, "Failed to encode challenge");
1624       goto out;
1625     }
1626 
1627     if (cfg->debug)
1628       D(cfg->debug_file, "Challenge: %s", b64_challenge);
1629 
1630     n = snprintf(prompt, sizeof(prompt), "Challenge #%d:", i + 1);
1631     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
1632       if (cfg->debug)
1633         D(cfg->debug_file, "Failed to print challenge prompt");
1634       goto out;
1635     }
1636 
1637     converse(pamh, PAM_TEXT_INFO, prompt);
1638 
1639     n = snprintf(buf, sizeof(buf), "%s\n%s\n%s", b64_challenge, cfg->origin,
1640                  devices[i].keyHandle);
1641     if (n <= 0 || (size_t) n >= sizeof(buf)) {
1642       if (cfg->debug)
1643         D(cfg->debug_file, "Failed to print fido2-assert input string");
1644       goto out;
1645     }
1646 
1647     converse(pamh, PAM_TEXT_INFO, buf);
1648 
1649     free(b64_challenge);
1650     b64_challenge = NULL;
1651   }
1652 
1653   converse(pamh, PAM_TEXT_INFO,
1654            "Please pass the challenge(s) above to fido2-assert, and "
1655            "paste the results in the prompt below.");
1656 
1657   retval = -1;
1658 
1659   for (i = 0; i < n_devs; ++i) {
1660     n = snprintf(prompt, sizeof(prompt), "Response #%d: ", i + 1);
1661     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
1662       if (cfg->debug)
1663         D(cfg->debug_file, "Failed to print response prompt");
1664       goto out;
1665     }
1666 
1667     if (!manual_get_assert(cfg, prompt, pamh, assert[i])) {
1668       if (cfg->debug)
1669         D(cfg->debug_file, "Failed to get assert %u", i);
1670       goto out;
1671     }
1672 
1673     r = fido_assert_verify(assert[i], 0, pk[i].type, pk[i].ptr);
1674     if (r == FIDO_OK) {
1675       retval = 1;
1676       break;
1677     }
1678   }
1679 
1680 out:
1681   for (i = 0; i < n_devs; i++) {
1682     fido_assert_free(&assert[i]);
1683     reset_pk(&pk[i]);
1684   }
1685   free(assert);
1686   free(pk);
1687   free(b64_challenge);
1688 
1689   return retval;
1690 }
1691 
1692 static int _converse(pam_handle_t *pamh, int nargs,
1693                      const struct pam_message **message,
1694                      struct pam_response **response) {
1695   struct pam_conv *conv;
1696   int retval;
1697 
1698   retval = pam_get_item(pamh, PAM_CONV, (void *) &conv);
1699 
1700   if (retval != PAM_SUCCESS) {
1701     return retval;
1702   }
1703 
1704   return conv->conv(nargs, message, response, conv->appdata_ptr);
1705 }
1706 
1707 char *converse(pam_handle_t *pamh, int echocode, const char *prompt) {
1708   const struct pam_message msg = {.msg_style = echocode,
1709                                   .msg = (char *) (uintptr_t) prompt};
1710   const struct pam_message *msgs = &msg;
1711   struct pam_response *resp = NULL;
1712   int retval = _converse(pamh, 1, &msgs, &resp);
1713   char *ret = NULL;
1714 
1715   if (retval != PAM_SUCCESS || resp == NULL || resp->resp == NULL ||
1716       *resp->resp == '\000') {
1717 
1718     if (retval == PAM_SUCCESS && resp && resp->resp) {
1719       ret = resp->resp;
1720     }
1721   } else {
1722     ret = resp->resp;
1723   }
1724 
1725   // Deallocate temporary storage.
1726   if (resp) {
1727     if (!ret) {
1728       free(resp->resp);
1729     }
1730     free(resp);
1731   }
1732 
1733   return ret;
1734 }
1735 
1736 #if defined(PAM_DEBUG)
1737 void _debug(FILE *debug_file, const char *file, int line, const char *func,
1738             const char *fmt, ...) {
1739   va_list ap;
1740   va_start(ap, fmt);
1741 
1742 #if defined(WITH_FUZZING)
1743   (void) debug_file;
1744   snprintf(NULL, 0, DEBUG_STR, file, line, func);
1745   vsnprintf(NULL, 0, fmt, ap);
1746 #elif defined(LOG_DEBUG)
1747   if (debug_file == (FILE *) -1) {
1748     syslog(LOG_AUTHPRIV | LOG_DEBUG, DEBUG_STR, file, line, func);
1749     vsyslog(LOG_AUTHPRIV | LOG_DEBUG, fmt, ap);
1750   } else {
1751     fprintf(debug_file, DEBUG_STR, file, line, func);
1752     vfprintf(debug_file, fmt, ap);
1753     fprintf(debug_file, "\n");
1754   }
1755 #else  /* Windows, MAC */
1756   fprintf(debug_file, DEBUG_STR, file, line, func);
1757   vfprintf(debug_file, fmt, ap);
1758   fprintf(debug_file, "\n");
1759 #endif /* __linux__ */
1760   va_end(ap);
1761 }
1762 #endif /* PAM_DEBUG */
1763 
1764 #ifndef RANDOM_DEV
1765 #define RANDOM_DEV "/dev/urandom"
1766 #endif
1767 
1768 int random_bytes(void *buf, size_t cnt) {
1769   int fd;
1770   ssize_t n;
1771 
1772   fd = open(RANDOM_DEV, O_RDONLY);
1773   if (fd < 0)
1774     return (0);
1775 
1776   n = read(fd, buf, cnt);
1777   close(fd);
1778   if (n < 0 || (size_t) n != cnt)
1779     return (0);
1780 
1781   return (1);
1782 }
1783