1 /* 2 * Copyright (c) 2020-2022 Yubico AB. All rights reserved. 3 * Use of this source code is governed by a BSD-style 4 * license that can be found in the LICENSE file. 5 * SPDX-License-Identifier: BSD-2-Clause 6 */ 7 8 #include <errno.h> 9 #include <fido.h> 10 #include <stdio.h> 11 #include <stdlib.h> 12 #include <time.h> 13 14 #include "../openbsd-compat/openbsd-compat.h" 15 16 #define FIDO_POLL_MS 50 17 18 #if defined(_MSC_VER) 19 static int 20 nanosleep(const struct timespec *rqtp, struct timespec *rmtp) 21 { 22 if (rmtp != NULL) { 23 errno = EINVAL; 24 return (-1); 25 } 26 27 Sleep((DWORD)(rqtp->tv_sec * 1000) + (DWORD)(rqtp->tv_nsec / 1000000)); 28 29 return (0); 30 } 31 #endif 32 33 static fido_dev_t * 34 open_dev(const fido_dev_info_t *di) 35 { 36 fido_dev_t *dev; 37 int r; 38 39 if ((dev = fido_dev_new()) == NULL) { 40 warnx("%s: fido_dev_new", __func__); 41 return (NULL); 42 } 43 44 if ((r = fido_dev_open(dev, fido_dev_info_path(di))) != FIDO_OK) { 45 warnx("%s: fido_dev_open %s: %s", __func__, 46 fido_dev_info_path(di), fido_strerr(r)); 47 fido_dev_free(&dev); 48 return (NULL); 49 } 50 51 printf("%s (0x%04x:0x%04x) is %s\n", fido_dev_info_path(di), 52 fido_dev_info_vendor(di), fido_dev_info_product(di), 53 fido_dev_is_fido2(dev) ? "fido2" : "u2f"); 54 55 return (dev); 56 } 57 58 static int 59 select_dev(const fido_dev_info_t *devlist, size_t ndevs, fido_dev_t **dev, 60 size_t *idx, int secs) 61 { 62 const fido_dev_info_t *di; 63 fido_dev_t **devtab; 64 struct timespec ts_start; 65 struct timespec ts_now; 66 struct timespec ts_delta; 67 struct timespec ts_pause; 68 size_t nopen = 0; 69 int touched; 70 int r; 71 long ms_remain; 72 73 *dev = NULL; 74 *idx = 0; 75 76 printf("%u authenticator(s) detected\n", (unsigned)ndevs); 77 78 if (ndevs == 0) 79 return (0); /* nothing to do */ 80 81 if ((devtab = calloc(ndevs, sizeof(*devtab))) == NULL) { 82 warn("%s: calloc", __func__); 83 return (-1); 84 } 85 86 for (size_t i = 0; i < ndevs; i++) { 87 di = fido_dev_info_ptr(devlist, i); 88 if ((devtab[i] = open_dev(di)) != NULL) { 89 *idx = i; 90 nopen++; 91 } 92 } 93 94 printf("%u authenticator(s) opened\n", (unsigned)nopen); 95 96 if (nopen < 2) { 97 if (nopen == 1) 98 *dev = devtab[*idx]; /* single candidate */ 99 r = 0; 100 goto out; 101 } 102 103 for (size_t i = 0; i < ndevs; i++) { 104 di = fido_dev_info_ptr(devlist, i); 105 if (devtab[i] == NULL) 106 continue; /* failed to open */ 107 if ((r = fido_dev_get_touch_begin(devtab[i])) != FIDO_OK) { 108 warnx("%s: fido_dev_get_touch_begin %s: %s", __func__, 109 fido_dev_info_path(di), fido_strerr(r)); 110 r = -1; 111 goto out; 112 } 113 } 114 115 if (clock_gettime(CLOCK_MONOTONIC, &ts_start) != 0) { 116 warn("%s: clock_gettime", __func__); 117 r = -1; 118 goto out; 119 } 120 121 ts_pause.tv_sec = 0; 122 ts_pause.tv_nsec = 200000000; /* 200ms */ 123 124 do { 125 nanosleep(&ts_pause, NULL); 126 127 for (size_t i = 0; i < ndevs; i++) { 128 di = fido_dev_info_ptr(devlist, i); 129 if (devtab[i] == NULL) { 130 /* failed to open or discarded */ 131 continue; 132 } 133 if ((r = fido_dev_get_touch_status(devtab[i], &touched, 134 FIDO_POLL_MS)) != FIDO_OK) { 135 warnx("%s: fido_dev_get_touch_status %s: %s", 136 __func__, fido_dev_info_path(di), 137 fido_strerr(r)); 138 fido_dev_close(devtab[i]); 139 fido_dev_free(&devtab[i]); 140 continue; /* discard */ 141 } 142 if (touched) { 143 *dev = devtab[i]; 144 *idx = i; 145 r = 0; 146 goto out; 147 } 148 } 149 150 if (clock_gettime(CLOCK_MONOTONIC, &ts_now) != 0) { 151 warn("%s: clock_gettime", __func__); 152 r = -1; 153 goto out; 154 } 155 156 timespecsub(&ts_now, &ts_start, &ts_delta); 157 ms_remain = (secs * 1000) - ((long)ts_delta.tv_sec * 1000) + 158 ((long)ts_delta.tv_nsec / 1000000); 159 } while (ms_remain > FIDO_POLL_MS); 160 161 printf("timeout after %d seconds\n", secs); 162 r = -1; 163 out: 164 if (r != 0) { 165 *dev = NULL; 166 *idx = 0; 167 } 168 169 for (size_t i = 0; i < ndevs; i++) { 170 if (devtab[i] && devtab[i] != *dev) { 171 fido_dev_cancel(devtab[i]); 172 fido_dev_close(devtab[i]); 173 fido_dev_free(&devtab[i]); 174 } 175 } 176 177 free(devtab); 178 179 return (r); 180 } 181 182 int 183 main(void) 184 { 185 const fido_dev_info_t *di; 186 fido_dev_info_t *devlist; 187 fido_dev_t *dev; 188 size_t idx; 189 size_t ndevs; 190 int r; 191 192 fido_init(0); 193 194 if ((devlist = fido_dev_info_new(64)) == NULL) 195 errx(1, "fido_dev_info_new"); 196 197 if ((r = fido_dev_info_manifest(devlist, 64, &ndevs)) != FIDO_OK) 198 errx(1, "fido_dev_info_manifest: %s (0x%x)", fido_strerr(r), r); 199 if (select_dev(devlist, ndevs, &dev, &idx, 15) != 0) 200 errx(1, "select_dev"); 201 if (dev == NULL) 202 errx(1, "no authenticator found"); 203 204 di = fido_dev_info_ptr(devlist, idx); 205 printf("%s: %s by %s (PIN %sset)\n", fido_dev_info_path(di), 206 fido_dev_info_product_string(di), 207 fido_dev_info_manufacturer_string(di), 208 fido_dev_has_pin(dev) ? "" : "un"); 209 210 fido_dev_close(dev); 211 fido_dev_free(&dev); 212 fido_dev_info_free(&devlist, ndevs); 213 214 exit(0); 215 } 216