xref: /netbsd-src/external/bsd/pam-u2f/dist/util.c (revision cef8759bd76c1b621f8eab8faa6f208faabc2e15)
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 
9 #include <openssl/ec.h>
10 #include <openssl/obj_mac.h>
11 
12 #include <stdbool.h>
13 #include <stdlib.h>
14 #include <fcntl.h>
15 #include <sys/stat.h>
16 #include <stdarg.h>
17 #include <syslog.h>
18 #include <pwd.h>
19 #include <errno.h>
20 #include <unistd.h>
21 #include <string.h>
22 
23 #include "b64.h"
24 #include "util.h"
25 
26 static int hex_decode(const char *ascii_hex, unsigned char **blob,
27                       size_t *blob_len) {
28   *blob = NULL;
29   *blob_len = 0;
30 
31   if (ascii_hex == NULL || (strlen(ascii_hex) % 2) != 0)
32     return (0);
33 
34   *blob_len = strlen(ascii_hex) / 2;
35   *blob = calloc(1, *blob_len);
36   if (*blob == NULL)
37     return (0);
38 
39   for (size_t i = 0; i < *blob_len; i++) {
40     unsigned int c;
41     int n = -1;
42     int r = sscanf(ascii_hex, "%02x%n", &c, &n);
43     if (r != 1 || n != 2 || c > UCHAR_MAX) {
44       free(*blob);
45       *blob = NULL;
46       *blob_len = 0;
47       return (0);
48     }
49     (*blob)[i] = (unsigned char) c;
50     ascii_hex += n;
51   }
52 
53   return (1);
54 }
55 
56 static char *normal_b64(const char *websafe_b64) {
57   char *b64;
58   char *p;
59   size_t n;
60 
61   n = strlen(websafe_b64);
62   if (n > SIZE_MAX - 3)
63     return (NULL);
64 
65   b64 = calloc(1, n + 3);
66   if (b64 == NULL)
67     return (NULL);
68 
69   memcpy(b64, websafe_b64, n);
70   p = b64;
71 
72   while ((p = strpbrk(p, "-_")) != NULL) {
73     switch (*p) {
74       case '-':
75         *p++ = '+';
76         break;
77       case '_':
78         *p++ = '/';
79         break;
80     }
81   }
82 
83   switch (n % 4) {
84     case 1:
85       b64[n] = '=';
86       break;
87     case 2:
88     case 3:
89       b64[n] = '=';
90       b64[n + 1] = '=';
91       break;
92   }
93 
94   return (b64);
95 }
96 
97 static es256_pk_t *translate_old_format_pubkey(const unsigned char *pk,
98                                                size_t pk_len) {
99   es256_pk_t *es256_pk = NULL;
100   EC_KEY *ec = NULL;
101   EC_POINT *q = NULL;
102   const EC_GROUP *g = NULL;
103   int ok = 0;
104 
105   if ((ec = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1)) == NULL ||
106       (g = EC_KEY_get0_group(ec)) == NULL)
107     goto fail;
108 
109   if ((q = EC_POINT_new(g)) == NULL ||
110       !EC_POINT_oct2point(g, q, pk, pk_len, NULL) ||
111       !EC_KEY_set_public_key(ec, q))
112     goto fail;
113 
114   es256_pk = es256_pk_new();
115   if (es256_pk == NULL || es256_pk_from_EC_KEY(es256_pk, ec) < 0)
116     goto fail;
117 
118   ok = 1;
119 fail:
120   if (ec != NULL)
121     EC_KEY_free(ec);
122   if (q != NULL)
123     EC_POINT_free(q);
124   if (!ok)
125     es256_pk_free(&es256_pk);
126 
127   return (es256_pk);
128 }
129 
130 int get_devices_from_authfile(const char *authfile, const char *username,
131                               unsigned max_devs, int verbose, FILE *debug_file,
132                               device_t *devices, unsigned *n_devs) {
133 
134   char *buf = NULL;
135   char *s_user, *s_token;
136   int retval = 0;
137   int fd = -1;
138   struct stat st;
139   struct passwd *pw = NULL, pw_s;
140   char buffer[BUFSIZE];
141   int gpu_ret;
142   FILE *opwfile = NULL;
143   unsigned i;
144 
145   /* Ensure we never return uninitialized count. */
146   *n_devs = 0;
147 
148   fd = open(authfile, O_RDONLY | O_CLOEXEC | O_NOCTTY);
149   if (fd < 0) {
150     if (verbose)
151       D(debug_file, "Cannot open file: %s (%s)", authfile, strerror(errno));
152     goto err;
153   }
154 
155   if (fstat(fd, &st) < 0) {
156     if (verbose)
157       D(debug_file, "Cannot stat file: %s (%s)", authfile, strerror(errno));
158     goto err;
159   }
160 
161   if (!S_ISREG(st.st_mode)) {
162     if (verbose)
163       D(debug_file, "%s is not a regular file", authfile);
164     goto err;
165   }
166 
167   if (st.st_size == 0) {
168     if (verbose)
169       D(debug_file, "File %s is empty", authfile);
170     goto err;
171   }
172 
173   gpu_ret = getpwuid_r(st.st_uid, &pw_s, buffer, sizeof(buffer), &pw);
174   if (gpu_ret != 0 || pw == NULL) {
175     D(debug_file, "Unable to retrieve credentials for uid %u, (%s)", st.st_uid,
176       strerror(errno));
177     goto err;
178   }
179 
180   if (strcmp(pw->pw_name, username) != 0 && strcmp(pw->pw_name, "root") != 0) {
181     if (strcmp(username, "root") != 0) {
182       D(debug_file,
183         "The owner of the authentication file is neither %s nor root",
184         username);
185     } else {
186       D(debug_file, "The owner of the authentication file is not root");
187     }
188     goto err;
189   }
190 
191   opwfile = fdopen(fd, "r");
192   if (opwfile == NULL) {
193     if (verbose)
194       D(debug_file, "fdopen: %s", strerror(errno));
195     goto err;
196   } else {
197     fd = -1; /* fd belongs to opwfile */
198   }
199 
200   buf = malloc(sizeof(char) * (DEVSIZE * max_devs));
201   if (!buf) {
202     if (verbose)
203       D(debug_file, "Unable to allocate memory");
204     goto err;
205   }
206 
207   retval = -2;
208   while (fgets(buf, (int) (DEVSIZE * (max_devs - 1)), opwfile)) {
209     char *saveptr = NULL;
210     size_t len = strlen(buf);
211     if (len > 0 && buf[len - 1] == '\n')
212       buf[len - 1] = '\0';
213 
214     if (verbose)
215       D(debug_file, "Authorization line: %s", buf);
216 
217     s_user = strtok_r(buf, ":", &saveptr);
218     if (s_user && strcmp(username, s_user) == 0) {
219       if (verbose)
220         D(debug_file, "Matched user: %s", s_user);
221 
222       retval = -1; // We found at least one line for the user
223 
224       // only keep last line for this user
225       for (i = 0; i < *n_devs; i++) {
226         free(devices[i].keyHandle);
227         free(devices[i].publicKey);
228         free(devices[i].coseType);
229         free(devices[i].attributes);
230         devices[i].keyHandle = NULL;
231         devices[i].publicKey = NULL;
232         devices[i].coseType = NULL;
233         devices[i].attributes = NULL;
234         devices[i].old_format = 0;
235       }
236       *n_devs = 0;
237 
238       i = 0;
239       while ((s_token = strtok_r(NULL, ",", &saveptr)) != NULL) {
240         if ((*n_devs)++ > max_devs - 1) {
241           *n_devs = max_devs;
242           if (verbose)
243             D(debug_file,
244               "Found more than %d devices, ignoring the remaining ones",
245               max_devs);
246           break;
247         }
248 
249         devices[i].keyHandle = NULL;
250         devices[i].publicKey = NULL;
251         devices[i].coseType = NULL;
252         devices[i].attributes = NULL;
253         devices[i].old_format = 0;
254 
255         if (verbose)
256           D(debug_file, "KeyHandle for device number %d: %s", i + 1, s_token);
257 
258         devices[i].keyHandle = strdup(s_token);
259 
260         if (!devices[i].keyHandle) {
261           if (verbose)
262             D(debug_file, "Unable to allocate memory for keyHandle number %d",
263               i);
264           goto err;
265         }
266 
267         if (!strcmp(devices[i].keyHandle, "*") && verbose)
268           D(debug_file, "Credential is resident");
269 
270         s_token = strtok_r(NULL, ",", &saveptr);
271 
272         if (!s_token) {
273           if (verbose)
274             D(debug_file, "Unable to retrieve publicKey number %d", i + 1);
275           goto err;
276         }
277 
278         if (verbose)
279           D(debug_file, "publicKey for device number %d: %s", i + 1, s_token);
280 
281         devices[i].publicKey = strdup(s_token);
282 
283         if (!devices[i].publicKey) {
284           if (verbose)
285             D(debug_file, "Unable to allocate memory for publicKey number %d",
286               i);
287           goto err;
288         }
289 
290         s_token = strtok_r(NULL, ",", &saveptr);
291 
292         devices[i].old_format = 0;
293 
294         if (!s_token) {
295           if (verbose) {
296             D(debug_file, "Unable to retrieve COSE type %d", i + 1);
297             D(debug_file, "Assuming ES256 (backwards compatibility)");
298           }
299           devices[i].old_format = 1;
300           devices[i].coseType = strdup("es256");
301         } else {
302           if (verbose)
303             D(debug_file, "COSE type for device number %d: %s", i + 1, s_token);
304           devices[i].coseType = strdup(s_token);
305         }
306 
307         if (!devices[i].coseType) {
308           if (verbose)
309             D(debug_file, "Unable to allocate memory for COSE type number %d",
310               i);
311           goto err;
312         }
313 
314         s_token = strtok_r(NULL, ":", &saveptr);
315 
316         if (!s_token) {
317           if (verbose) {
318             D(debug_file, "Unable to retrieve attributes %d", i + 1);
319             D(debug_file, "Assuming 'p' (backwards compatibility)");
320           }
321           devices[i].attributes = strdup("p");
322         } else {
323           if (verbose)
324             D(debug_file, "Attributes for device number %d: %s", i + 1,
325               s_token);
326           devices[i].attributes = strdup(s_token);
327         }
328 
329         if (!devices[i].attributes) {
330           if (verbose)
331             D(debug_file, "Unable to allocate memory for attributes number %d",
332               i);
333           goto err;
334         }
335 
336         if (devices[i].old_format) {
337           char *websafe_b64 = devices[i].keyHandle;
338           devices[i].keyHandle = normal_b64(websafe_b64);
339           free(websafe_b64);
340           if (!devices[i].keyHandle) {
341             if (verbose)
342               D(debug_file, "Unable to allocate memory for keyHandle number %d",
343                 i);
344             goto err;
345           }
346         }
347 
348         i++;
349       }
350     }
351   }
352 
353   if (verbose)
354     D(debug_file, "Found %d device(s) for user %s", *n_devs, username);
355 
356   retval = 1;
357   goto out;
358 
359 err:
360   for (i = 0; i < *n_devs; i++) {
361     free(devices[i].keyHandle);
362     free(devices[i].publicKey);
363     free(devices[i].coseType);
364     free(devices[i].attributes);
365     devices[i].keyHandle = NULL;
366     devices[i].publicKey = NULL;
367     devices[i].coseType = NULL;
368     devices[i].attributes = NULL;
369   }
370 
371   *n_devs = 0;
372 
373 out:
374   if (buf) {
375     free(buf);
376     buf = NULL;
377   }
378 
379   if (opwfile)
380     fclose(opwfile);
381 
382   if (fd != -1)
383     close(fd);
384 
385   return retval;
386 }
387 
388 void free_devices(device_t *devices, const unsigned n_devs) {
389   unsigned i;
390 
391   if (!devices)
392     return;
393 
394   for (i = 0; i < n_devs; i++) {
395     free(devices[i].keyHandle);
396     devices[i].keyHandle = NULL;
397 
398     free(devices[i].publicKey);
399     devices[i].publicKey = NULL;
400 
401     free(devices[i].coseType);
402     devices[i].coseType = NULL;
403 
404     free(devices[i].attributes);
405     devices[i].attributes = NULL;
406   }
407 
408   free(devices);
409   devices = NULL;
410 }
411 
412 static int get_authenticators(const cfg_t *cfg, const fido_dev_info_t *devlist,
413                               size_t devlist_len, fido_assert_t *assert,
414                               const void *kh, fido_dev_t **authlist) {
415   const fido_dev_info_t *di = NULL;
416   fido_dev_t *dev = NULL;
417   int r;
418   size_t i;
419   size_t j;
420 
421   if (cfg->debug)
422     D(cfg->debug_file, "Working with %zu authenticator(s)", devlist_len);
423 
424   for (i = 0, j = 0; i < devlist_len; i++) {
425     if (cfg->debug)
426       D(cfg->debug_file, "Checking whether key exists in authenticator %zu", i);
427 
428     di = fido_dev_info_ptr(devlist, i);
429     if (!di) {
430       if (cfg->debug)
431         D(cfg->debug_file, "Unable to get device pointer");
432       continue;
433     }
434 
435     if (cfg->debug)
436       D(cfg->debug_file, "Authenticator path: %s", fido_dev_info_path(di));
437 
438     dev = fido_dev_new();
439     if (!dev) {
440       if (cfg->debug)
441         D(cfg->debug_file, "Unable to allocate device type");
442       continue;
443     }
444 
445     r = fido_dev_open(dev, fido_dev_info_path(di));
446     if (r != FIDO_OK) {
447       if (cfg->debug)
448         D(cfg->debug_file, "Failed to open authenticator: %s (%d)",
449           fido_strerr(r), r);
450       fido_dev_free(&dev);
451       continue;
452     }
453 
454     if (kh == NULL || cfg->nodetect) {
455       /* resident credential or nodetect: try all authenticators */
456       authlist[j++] = dev;
457     } else {
458       r = fido_dev_get_assert(dev, assert, NULL);
459       if ((!fido_dev_is_fido2(dev) && r == FIDO_ERR_USER_PRESENCE_REQUIRED) ||
460           (fido_dev_is_fido2(dev) && r == FIDO_OK)) {
461         authlist[j++] = dev;
462         if (cfg->debug)
463           D(cfg->debug_file, "Found key in authenticator %zu", i);
464         return (1);
465       }
466       if (cfg->debug)
467         D(cfg->debug_file, "Key not found in authenticator %zu", i);
468 
469       fido_dev_close(dev);
470       fido_dev_free(&dev);
471     }
472   }
473 
474   if (kh == NULL && j != 0)
475     return (1);
476   else {
477     if (cfg->debug)
478       D(cfg->debug_file, "Key not found");
479     return (0);
480   }
481 }
482 
483 int do_authentication(const cfg_t *cfg, const device_t *devices,
484                       const unsigned n_devs, pam_handle_t *pamh) {
485   es256_pk_t *es256_pk = NULL;
486   rs256_pk_t *rs256_pk = NULL;
487   fido_assert_t *assert = NULL;
488   fido_dev_info_t *devlist = NULL;
489   fido_dev_t **authlist = NULL;
490   int cued = 0;
491   int r;
492   int retval = -2;
493   int cose_type;
494   size_t kh_len;
495   size_t ndevs = 0;
496   size_t ndevs_prev = 0;
497   size_t pk_len;
498   unsigned char challenge[32];
499   unsigned char *kh = NULL;
500   unsigned char *pk = NULL;
501   unsigned i = 0;
502   fido_opt_t user_presence = FIDO_OPT_OMIT;
503   fido_opt_t user_verification = FIDO_OPT_OMIT;
504   fido_opt_t pin_verification = FIDO_OPT_OMIT;
505   char *pin = NULL;
506 
507   fido_init(cfg->debug ? FIDO_DEBUG : 0);
508 
509   devlist = fido_dev_info_new(64);
510   if (!devlist) {
511     if (cfg->debug)
512       D(cfg->debug_file, "Unable to allocate devlist");
513     goto out;
514   }
515 
516   r = fido_dev_info_manifest(devlist, 64, &ndevs);
517   if (r != FIDO_OK) {
518     if (cfg->debug)
519       D(cfg->debug_file, "Unable to discover device(s), %s (%d)",
520         fido_strerr(r), r);
521     goto out;
522   }
523 
524   ndevs_prev = ndevs;
525 
526   if (cfg->debug)
527     D(cfg->debug_file, "Device max index is %u", ndevs);
528 
529   es256_pk = es256_pk_new();
530   if (!es256_pk) {
531     if (cfg->debug)
532       D(cfg->debug_file, "Unable to allocate ES256 public key");
533     goto out;
534   }
535 
536   rs256_pk = rs256_pk_new();
537   if (!rs256_pk) {
538     if (cfg->debug)
539       D(cfg->debug_file, "Unable to allocate RS256 public key");
540     goto out;
541   }
542 
543   authlist = calloc(64 + 1, sizeof(fido_dev_t *));
544   if (!authlist) {
545     if (cfg->debug)
546       D(cfg->debug_file, "Unable to allocate authenticator list");
547     goto out;
548   }
549 
550   if (cfg->nodetect && cfg->debug)
551     D(cfg->debug_file,
552       "nodetect option specified, suitable key detection will be skipped");
553 
554   i = 0;
555   while (i < n_devs) {
556     retval = -2;
557 
558     if (cfg->debug)
559       D(cfg->debug_file, "Attempting authentication with device number %d",
560         i + 1);
561 
562     assert = fido_assert_new();
563     if (!assert) {
564       if (cfg->debug)
565         D(cfg->debug_file, "Unable to allocate assertion");
566       goto out;
567     }
568 
569     r = fido_assert_set_rp(assert, cfg->origin);
570     if (r != FIDO_OK) {
571       if (cfg->debug)
572         D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r);
573       goto out;
574     }
575 
576     if (!strcmp(devices[i].keyHandle, "*")) {
577       if (cfg->debug)
578         D(cfg->debug_file, "Credential is resident");
579     } else {
580       if (cfg->debug)
581         D(cfg->debug_file, "Key handle: %s", devices[i].keyHandle);
582       if (!b64_decode(devices[i].keyHandle, (void **) &kh, &kh_len)) {
583         if (cfg->debug)
584           D(cfg->debug_file, "Failed to decode key handle");
585         goto out;
586       }
587 
588       r = fido_assert_allow_cred(assert, kh, kh_len);
589       if (r != FIDO_OK) {
590         if (cfg->debug)
591           D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r),
592             r);
593         goto out;
594       }
595     }
596 
597     if (devices[i].old_format) {
598       if (!hex_decode(devices[i].publicKey, &pk, &pk_len)) {
599         if (cfg->debug)
600           D(cfg->debug_file, "Failed to decode public key");
601         goto out;
602       }
603     } else {
604       if (!b64_decode(devices[i].publicKey, (void **) &pk, &pk_len)) {
605         if (cfg->debug)
606           D(cfg->debug_file, "Failed to decode public key");
607         goto out;
608       }
609     }
610 
611     if (!strcmp(devices[i].coseType, "es256")) {
612       if (devices[i].old_format) {
613         es256_pk = translate_old_format_pubkey(pk, pk_len);
614         if (es256_pk == NULL) {
615           if (cfg->debug)
616             D(cfg->debug_file, "Failed to convert ES256 public key");
617         }
618       } else {
619         r = es256_pk_from_ptr(es256_pk, pk, pk_len);
620         if (r != FIDO_OK) {
621           if (cfg->debug)
622             D(cfg->debug_file, "Failed to convert ES256 public key");
623         }
624       }
625       cose_type = COSE_ES256;
626     } else if (!strcmp(devices[i].coseType, "rs256")) {
627       r = rs256_pk_from_ptr(rs256_pk, pk, pk_len);
628       if (r != FIDO_OK) {
629         if (cfg->debug)
630           D(cfg->debug_file, "Failed to convert RS256 public key");
631       }
632       cose_type = COSE_RS256;
633     } else {
634       if (cfg->debug)
635         D(cfg->debug_file, "Unknown COSE type '%s'", devices[i].coseType);
636       goto out;
637     }
638 
639     if (cfg->userpresence == 1 || strstr(devices[i].attributes, "presence"))
640       user_presence = FIDO_OPT_TRUE;
641     else if (cfg->userpresence == 0)
642       user_presence = FIDO_OPT_FALSE;
643     else
644       user_presence = FIDO_OPT_OMIT;
645 
646     if (cfg->userverification == 1 ||
647         strstr(devices[i].attributes, "verification"))
648       user_verification = FIDO_OPT_TRUE;
649     else if (cfg->userverification == 0)
650       user_verification = FIDO_OPT_FALSE;
651     else
652       user_verification = FIDO_OPT_OMIT;
653 
654     if (cfg->pinverification == 1 || strstr(devices[i].attributes, "pin")) {
655       pin_verification = FIDO_OPT_TRUE;
656       user_verification = FIDO_OPT_TRUE;
657     } else if (cfg->pinverification == 0)
658       pin_verification = FIDO_OPT_FALSE;
659     else
660       pin_verification = FIDO_OPT_OMIT;
661 
662     r = fido_assert_set_up(assert, FIDO_OPT_FALSE);
663     if (r != FIDO_OK) {
664       if (cfg->debug)
665         D(cfg->debug_file, "Failed to set UP");
666       goto out;
667     }
668 
669     r = fido_assert_set_uv(assert, FIDO_OPT_OMIT);
670     if (r != FIDO_OK) {
671       if (cfg->debug)
672         D(cfg->debug_file, "Failed to set UV");
673       goto out;
674     }
675 
676     if (!random_bytes(challenge, sizeof(challenge))) {
677       if (cfg->debug)
678         D(cfg->debug_file, "Failed to generate challenge");
679       goto out;
680     }
681 
682     if (cfg->debug) {
683       char *b64_challenge;
684       if (!b64_encode(challenge, sizeof(challenge), &b64_challenge)) {
685         D(cfg->debug_file, "Failed to encode challenge");
686       } else {
687         D(cfg->debug_file, "Challenge: %s", b64_challenge);
688         free(b64_challenge);
689       }
690     }
691 
692     r = fido_assert_set_clientdata_hash(assert, challenge, sizeof(challenge));
693     if (r != FIDO_OK) {
694       if (cfg->debug)
695         D(cfg->debug_file, "Unable to set challenge: %s( %d)", fido_strerr(r),
696           r);
697       goto out;
698     }
699 
700     if (get_authenticators(cfg, devlist, ndevs, assert, kh, authlist)) {
701       for (size_t j = 0; authlist[j] != NULL; j++) {
702         r = fido_assert_set_up(assert, user_presence);
703         if (r != FIDO_OK) {
704           if (cfg->debug)
705             D(cfg->debug_file, "Failed to reset UP");
706           goto out;
707         }
708 
709         r = fido_assert_set_uv(assert, user_verification);
710         if (r != FIDO_OK) {
711           if (cfg->debug)
712             D(cfg->debug_file, "Failed to reset UV");
713           goto out;
714         }
715 
716         if (!random_bytes(challenge, sizeof(challenge))) {
717           if (cfg->debug)
718             D(cfg->debug_file, "Failed to regenerate challenge");
719           goto out;
720         }
721 
722         r =
723           fido_assert_set_clientdata_hash(assert, challenge, sizeof(challenge));
724         if (r != FIDO_OK) {
725           if (cfg->debug)
726             D(cfg->debug_file, "Unable to reset challenge: %s( %d)",
727               fido_strerr(r), r);
728           goto out;
729         }
730 
731         if (pin_verification == FIDO_OPT_TRUE)
732           pin = converse(pamh, PAM_PROMPT_ECHO_OFF, "Please enter the PIN: ");
733         if (user_presence == FIDO_OPT_TRUE ||
734             user_verification == FIDO_OPT_TRUE) {
735           if (cfg->manual == 0 && cfg->cue && !cued) {
736             cued = 1;
737             converse(pamh, PAM_TEXT_INFO,
738                      cfg->cue_prompt != NULL ? cfg->cue_prompt : DEFAULT_CUE);
739           }
740         }
741         r = fido_dev_get_assert(authlist[j], assert, pin);
742         if (pin) {
743           explicit_bzero(pin, strlen(pin));
744           free(pin);
745           pin = NULL;
746         }
747         if (r == FIDO_OK) {
748           r = fido_assert_verify(assert, 0, cose_type,
749                                  cose_type == COSE_ES256
750                                    ? (const void *) es256_pk
751                                    : (const void *) rs256_pk);
752           if (r == FIDO_OK) {
753             retval = 1;
754             goto out;
755           }
756         }
757       }
758     } else {
759       if (cfg->debug)
760         D(cfg->debug_file, "Device for this keyhandle is not present.");
761     }
762 
763     i++;
764 
765     fido_dev_info_free(&devlist, ndevs);
766 
767     devlist = fido_dev_info_new(64);
768     if (!devlist) {
769       if (cfg->debug)
770         D(cfg->debug_file, "Unable to allocate devlist");
771       goto out;
772     }
773 
774     r = fido_dev_info_manifest(devlist, 64, &ndevs);
775     if (r != FIDO_OK) {
776       if (cfg->debug)
777         D(cfg->debug_file, "Unable to discover device(s), %s (%d)",
778           fido_strerr(r), r);
779       goto out;
780     }
781 
782     if (ndevs > ndevs_prev) {
783       if (cfg->debug)
784         D(cfg->debug_file,
785           "Devices max_index has changed: %zu (was %zu). Starting over", ndevs,
786           ndevs_prev);
787       ndevs_prev = ndevs;
788       i = 0;
789     }
790 
791     free(kh);
792     free(pk);
793 
794     kh = NULL;
795     pk = NULL;
796 
797     for (size_t j = 0; authlist[j] != NULL; j++) {
798       fido_dev_close(authlist[j]);
799       fido_dev_free(&authlist[j]);
800     }
801 
802     fido_assert_free(&assert);
803   }
804 
805 out:
806   es256_pk_free(&es256_pk);
807   rs256_pk_free(&rs256_pk);
808   fido_assert_free(&assert);
809   fido_dev_info_free(&devlist, ndevs);
810 
811   if (authlist) {
812     for (size_t j = 0; authlist[j] != NULL; j++) {
813       fido_dev_close(authlist[j]);
814       fido_dev_free(&authlist[j]);
815     }
816     free(authlist);
817   }
818 
819   free(kh);
820   free(pk);
821 
822   return retval;
823 }
824 
825 #define MAX_PROMPT_LEN (1024)
826 
827 int do_manual_authentication(const cfg_t *cfg, const device_t *devices,
828                              const unsigned n_devs, pam_handle_t *pamh) {
829   fido_assert_t *assert[n_devs];
830   es256_pk_t *es256_pk[n_devs];
831   rs256_pk_t *rs256_pk[n_devs];
832   unsigned char challenge[32];
833   unsigned char *kh = NULL;
834   unsigned char *pk = NULL;
835   unsigned char *authdata = NULL;
836   unsigned char *sig = NULL;
837   char *b64_challenge = NULL;
838   char *b64_cdh = NULL;
839   char *b64_rpid = NULL;
840   char *b64_authdata = NULL;
841   char *b64_sig = NULL;
842   char prompt[MAX_PROMPT_LEN];
843   char buf[MAX_PROMPT_LEN];
844   size_t kh_len;
845   size_t pk_len;
846   size_t authdata_len;
847   size_t sig_len;
848   int cose_type[n_devs];
849   int retval = -2;
850   int n;
851   int r;
852   unsigned i = 0;
853   bool user_presence = false;
854   bool user_verification = false;
855 
856   memset(assert, 0, sizeof(assert));
857   memset(es256_pk, 0, sizeof(es256_pk));
858   memset(rs256_pk, 0, sizeof(rs256_pk));
859 
860   fido_init(cfg->debug ? FIDO_DEBUG : 0);
861 
862   for (i = 0; i < n_devs; ++i) {
863 
864     assert[i] = fido_assert_new();
865     if (!assert[i]) {
866       if (cfg->debug)
867         D(cfg->debug_file, "Unable to allocate assertion %u", i);
868       goto out;
869     }
870 
871     r = fido_assert_set_rp(assert[i], cfg->origin);
872     if (r != FIDO_OK) {
873       if (cfg->debug)
874         D(cfg->debug_file, "Unable to set origin: %s (%d)", fido_strerr(r), r);
875       goto out;
876     }
877 
878     if (strstr(devices[i].attributes, "presence"))
879       user_presence = true;
880     if (strstr(devices[i].attributes, "verification"))
881       user_verification = true;
882 
883     r = fido_assert_set_up(assert[i], user_presence);
884     if (r != FIDO_OK) {
885       if (cfg->debug)
886         D(cfg->debug_file, "Unable to set UP: %s (%d)", fido_strerr(r), r);
887       goto out;
888     }
889 
890     r = fido_assert_set_uv(assert[i], user_verification);
891     if (r != FIDO_OK) {
892       if (cfg->debug)
893         D(cfg->debug_file, "Unable to set UV: %s (%d)", fido_strerr(r), r);
894       goto out;
895     }
896 
897     if (cfg->debug)
898       D(cfg->debug_file, "Attempting authentication with device number %d",
899         i + 1);
900 
901     if (!strcmp(devices[i].keyHandle, "*")) {
902       if (cfg->debug)
903         D(cfg->debug_file, "Credential is resident");
904     } else {
905       if (!b64_decode(devices[i].keyHandle, (void **) &kh, &kh_len)) {
906         if (cfg->debug)
907           D(cfg->debug_file, "Failed to decode key handle");
908         goto out;
909       }
910 
911       r = fido_assert_allow_cred(assert[i], kh, kh_len);
912       if (r != FIDO_OK) {
913         if (cfg->debug)
914           D(cfg->debug_file, "Unable to set keyHandle: %s (%d)", fido_strerr(r),
915             r);
916         goto out;
917       }
918 
919       free(kh);
920       kh = NULL;
921     }
922 
923     if (devices[i].old_format) {
924       if (!hex_decode(devices[i].publicKey, &pk, &pk_len)) {
925         if (cfg->debug)
926           D(cfg->debug_file, "Failed to decode public key");
927         goto out;
928       }
929     } else {
930       if (!b64_decode(devices[i].publicKey, (void **) &pk, &pk_len)) {
931         if (cfg->debug)
932           D(cfg->debug_file, "Failed to decode public key");
933         goto out;
934       }
935     }
936 
937     if (!strcmp(devices[i].coseType, "es256")) {
938       es256_pk[i] = es256_pk_new();
939       if (!es256_pk[i]) {
940         if (cfg->debug)
941           D(cfg->debug_file, "Unable to allocate key %u", i);
942         goto out;
943       }
944 
945       if (es256_pk_from_ptr(es256_pk[i], pk, pk_len) != FIDO_OK) {
946         if (cfg->debug)
947           D(cfg->debug_file, "Failed to convert public key");
948         goto out;
949       }
950 
951       cose_type[i] = COSE_ES256;
952     } else {
953       rs256_pk[i] = rs256_pk_new();
954       if (!rs256_pk[i]) {
955         if (cfg->debug)
956           D(cfg->debug_file, "Unable to allocate key %u", i);
957         goto out;
958       }
959 
960       if (rs256_pk_from_ptr(rs256_pk[i], pk, pk_len) != FIDO_OK) {
961         if (cfg->debug)
962           D(cfg->debug_file, "Failed to convert public key");
963         goto out;
964       }
965 
966       cose_type[i] = COSE_RS256;
967     }
968 
969     free(pk);
970     pk = NULL;
971 
972     if (!random_bytes(challenge, sizeof(challenge))) {
973       if (cfg->debug)
974         D(cfg->debug_file, "Failed to generate challenge");
975       goto out;
976     }
977 
978     r =
979       fido_assert_set_clientdata_hash(assert[i], challenge, sizeof(challenge));
980     if (r != FIDO_OK) {
981       if (cfg->debug)
982         D(cfg->debug_file, "Failed to set challenge");
983       goto out;
984     }
985 
986     if (!b64_encode(challenge, sizeof(challenge), &b64_challenge)) {
987       if (cfg->debug)
988         D(cfg->debug_file, "Failed to encode challenge");
989       goto out;
990     }
991 
992     if (cfg->debug)
993       D(cfg->debug_file, "Challenge: %s", b64_challenge);
994 
995     n = snprintf(prompt, sizeof(prompt), "Challenge #%d:", i + 1);
996     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
997       if (cfg->debug)
998         D(cfg->debug_file, "Failed to print challenge prompt");
999       goto out;
1000     }
1001 
1002     converse(pamh, PAM_TEXT_INFO, prompt);
1003 
1004     n = snprintf(buf, sizeof(buf), "%s\n%s\n%s", b64_challenge, cfg->origin,
1005                  devices[i].keyHandle);
1006     if (n <= 0 || (size_t) n >= sizeof(buf)) {
1007       if (cfg->debug)
1008         D(cfg->debug_file, "Failed to print fido2-assert input string");
1009       goto out;
1010     }
1011 
1012     converse(pamh, PAM_TEXT_INFO, buf);
1013 
1014     free(b64_challenge);
1015     b64_challenge = NULL;
1016   }
1017 
1018   converse(pamh, PAM_TEXT_INFO,
1019            "Please pass the challenge(s) above to fido2-assert, and "
1020            "paste the results in the prompt below.");
1021 
1022   retval = -1;
1023 
1024   for (i = 0; i < n_devs; ++i) {
1025     n = snprintf(prompt, sizeof(prompt), "Response #%d: ", i + 1);
1026     if (n <= 0 || (size_t) n >= sizeof(prompt)) {
1027       if (cfg->debug)
1028         D(cfg->debug_file, "Failed to print response prompt");
1029       goto out;
1030     }
1031 
1032     b64_cdh = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1033     b64_rpid = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1034     b64_authdata = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1035     b64_sig = converse(pamh, PAM_PROMPT_ECHO_ON, prompt);
1036 
1037     if (!b64_decode(b64_authdata, (void **) &authdata, &authdata_len)) {
1038       if (cfg->debug)
1039         D(cfg->debug_file, "Failed to decode authenticator data");
1040       goto out;
1041     }
1042 
1043     if (!b64_decode(b64_sig, (void **) &sig, &sig_len)) {
1044       if (cfg->debug)
1045         D(cfg->debug_file, "Failed to decode signature");
1046       goto out;
1047     }
1048 
1049     free(b64_cdh);
1050     free(b64_rpid);
1051     free(b64_authdata);
1052     free(b64_sig);
1053 
1054     b64_cdh = NULL;
1055     b64_rpid = NULL;
1056     b64_authdata = NULL;
1057     b64_sig = NULL;
1058 
1059     r = fido_assert_set_count(assert[i], 1);
1060     if (r != FIDO_OK) {
1061       if (cfg->debug)
1062         D(cfg->debug_file, "Failed to set signature count of assertion %u", i);
1063       goto out;
1064     }
1065 
1066     r = fido_assert_set_authdata(assert[i], 0, authdata, authdata_len);
1067     if (r != FIDO_OK) {
1068       if (cfg->debug)
1069         D(cfg->debug_file, "Failed to set authdata of assertion %u", i);
1070       goto out;
1071     }
1072 
1073     r = fido_assert_set_sig(assert[i], 0, sig, sig_len);
1074     if (r != FIDO_OK) {
1075       if (cfg->debug)
1076         D(cfg->debug_file, "Failed to set signature of assertion %u", i);
1077       goto out;
1078     }
1079 
1080     free(authdata);
1081     free(sig);
1082 
1083     authdata = NULL;
1084     sig = NULL;
1085 
1086     if (cose_type[i] == COSE_ES256)
1087       r = fido_assert_verify(assert[i], 0, COSE_ES256, es256_pk[i]);
1088     else
1089       r = fido_assert_verify(assert[i], 0, COSE_RS256, rs256_pk[i]);
1090 
1091     if (r == FIDO_OK) {
1092       retval = 1;
1093       break;
1094     }
1095   }
1096 
1097 out:
1098   for (i = 0; i < n_devs; i++) {
1099     fido_assert_free(&assert[i]);
1100     es256_pk_free(&es256_pk[i]);
1101     rs256_pk_free(&rs256_pk[i]);
1102   }
1103 
1104   free(kh);
1105   free(pk);
1106   free(b64_challenge);
1107   free(b64_cdh);
1108   free(b64_rpid);
1109   free(b64_authdata);
1110   free(b64_sig);
1111   free(authdata);
1112   free(sig);
1113 
1114   return retval;
1115 }
1116 
1117 static int _converse(pam_handle_t *pamh, int nargs,
1118                      const struct pam_message **message,
1119                      struct pam_response **response) {
1120   struct pam_conv *conv;
1121   int retval;
1122 
1123   retval = pam_get_item(pamh, PAM_CONV, (void *) &conv);
1124 
1125   if (retval != PAM_SUCCESS) {
1126     return retval;
1127   }
1128 
1129   return conv->conv(nargs, message, response, conv->appdata_ptr);
1130 }
1131 
1132 char *converse(pam_handle_t *pamh, int echocode, const char *prompt) {
1133   const struct pam_message msg = {.msg_style = echocode,
1134                                   .msg = (char *)(uintptr_t)prompt};
1135   const struct pam_message *msgs = &msg;
1136   struct pam_response *resp = NULL;
1137   int retval = _converse(pamh, 1, &msgs, &resp);
1138   char *ret = NULL;
1139 
1140   if (retval != PAM_SUCCESS || resp == NULL || resp->resp == NULL ||
1141       *resp->resp == '\000') {
1142 
1143     if (retval == PAM_SUCCESS && resp && resp->resp) {
1144       ret = resp->resp;
1145     }
1146   } else {
1147     ret = resp->resp;
1148   }
1149 
1150   // Deallocate temporary storage.
1151   if (resp) {
1152     if (!ret) {
1153       free(resp->resp);
1154     }
1155     free(resp);
1156   }
1157 
1158   return ret;
1159 }
1160 
1161 #if defined(PAM_DEBUG)
1162 void _debug(FILE *debug_file, const char *file, int line, const char *func,
1163             const char *fmt, ...) {
1164   va_list ap;
1165   va_start(ap, fmt);
1166 #ifdef LOG_DEBUG
1167   if (debug_file == (FILE *) -1) {
1168     syslog(LOG_AUTHPRIV | LOG_DEBUG, DEBUG_STR, file, line, func);
1169     vsyslog(LOG_AUTHPRIV | LOG_DEBUG, fmt, ap);
1170   } else {
1171     fprintf(debug_file, DEBUG_STR, file, line, func);
1172     vfprintf(debug_file, fmt, ap);
1173     fprintf(debug_file, "\n");
1174   }
1175 #else  /* Windows, MAC */
1176   fprintf(debug_file, DEBUG_STR, file, line, func);
1177   vfprintf(debug_file, fmt, ap);
1178   fprintf(debug_file, "\n");
1179 #endif /* __linux__ */
1180   va_end(ap);
1181 }
1182 #endif /* PAM_DEBUG */
1183 
1184 #ifndef RANDOM_DEV
1185 #define RANDOM_DEV "/dev/urandom"
1186 #endif
1187 
1188 int random_bytes(void *buf, size_t cnt) {
1189   int fd;
1190   ssize_t n;
1191 
1192   fd = open(RANDOM_DEV, O_RDONLY);
1193   if (fd < 0)
1194     return (0);
1195 
1196   n = read(fd, buf, cnt);
1197   close(fd);
1198   if (n < 0 || (size_t) n != cnt)
1199     return (0);
1200 
1201   return (1);
1202 }
1203