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
nanosleep(const struct timespec * rqtp,struct timespec * rmtp)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 *
open_dev(const fido_dev_info_t * di)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
select_dev(const fido_dev_info_t * devlist,size_t ndevs,fido_dev_t ** dev,size_t * idx,int secs)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
main(void)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