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