]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/libfido2-util.c
strv: make iterator in STRV_FOREACH() declaread in the loop
[thirdparty/systemd.git] / src / shared / libfido2-util.c
1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
2
3 #include "libfido2-util.h"
4
5 #if HAVE_LIBFIDO2
6 #include "alloc-util.h"
7 #include "ask-password-api.h"
8 #include "dlfcn-util.h"
9 #include "format-table.h"
10 #include "glyph-util.h"
11 #include "log.h"
12 #include "memory-util.h"
13 #include "random-util.h"
14 #include "strv.h"
15 #include "unistd.h"
16
17 static void *libfido2_dl = NULL;
18
19 int (*sym_fido_assert_allow_cred)(fido_assert_t *, const unsigned char *, size_t) = NULL;
20 void (*sym_fido_assert_free)(fido_assert_t **) = NULL;
21 size_t (*sym_fido_assert_hmac_secret_len)(const fido_assert_t *, size_t) = NULL;
22 const unsigned char* (*sym_fido_assert_hmac_secret_ptr)(const fido_assert_t *, size_t) = NULL;
23 fido_assert_t* (*sym_fido_assert_new)(void) = NULL;
24 int (*sym_fido_assert_set_clientdata_hash)(fido_assert_t *, const unsigned char *, size_t) = NULL;
25 int (*sym_fido_assert_set_extensions)(fido_assert_t *, int) = NULL;
26 int (*sym_fido_assert_set_hmac_salt)(fido_assert_t *, const unsigned char *, size_t) = NULL;
27 int (*sym_fido_assert_set_rp)(fido_assert_t *, const char *) = NULL;
28 int (*sym_fido_assert_set_up)(fido_assert_t *, fido_opt_t) = NULL;
29 int (*sym_fido_assert_set_uv)(fido_assert_t *, fido_opt_t) = NULL;
30 size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *) = NULL;
31 char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *) = NULL;
32 void (*sym_fido_cbor_info_free)(fido_cbor_info_t **) = NULL;
33 fido_cbor_info_t* (*sym_fido_cbor_info_new)(void) = NULL;
34 size_t (*sym_fido_cbor_info_options_len)(const fido_cbor_info_t *) = NULL;
35 char** (*sym_fido_cbor_info_options_name_ptr)(const fido_cbor_info_t *) = NULL;
36 const bool* (*sym_fido_cbor_info_options_value_ptr)(const fido_cbor_info_t *) = NULL;
37 void (*sym_fido_cred_free)(fido_cred_t **) = NULL;
38 size_t (*sym_fido_cred_id_len)(const fido_cred_t *) = NULL;
39 const unsigned char* (*sym_fido_cred_id_ptr)(const fido_cred_t *) = NULL;
40 fido_cred_t* (*sym_fido_cred_new)(void) = NULL;
41 int (*sym_fido_cred_set_clientdata_hash)(fido_cred_t *, const unsigned char *, size_t) = NULL;
42 int (*sym_fido_cred_set_extensions)(fido_cred_t *, int) = NULL;
43 int (*sym_fido_cred_set_rk)(fido_cred_t *, fido_opt_t) = NULL;
44 int (*sym_fido_cred_set_rp)(fido_cred_t *, const char *, const char *) = NULL;
45 int (*sym_fido_cred_set_type)(fido_cred_t *, int) = NULL;
46 int (*sym_fido_cred_set_user)(fido_cred_t *, const unsigned char *, size_t, const char *, const char *, const char *) = NULL;
47 int (*sym_fido_cred_set_uv)(fido_cred_t *, fido_opt_t) = NULL;
48 void (*sym_fido_dev_free)(fido_dev_t **) = NULL;
49 int (*sym_fido_dev_get_assert)(fido_dev_t *, fido_assert_t *, const char *) = NULL;
50 int (*sym_fido_dev_get_cbor_info)(fido_dev_t *, fido_cbor_info_t *) = NULL;
51 void (*sym_fido_dev_info_free)(fido_dev_info_t **, size_t) = NULL;
52 int (*sym_fido_dev_info_manifest)(fido_dev_info_t *, size_t, size_t *) = NULL;
53 const char* (*sym_fido_dev_info_manufacturer_string)(const fido_dev_info_t *) = NULL;
54 const char* (*sym_fido_dev_info_product_string)(const fido_dev_info_t *) = NULL;
55 fido_dev_info_t* (*sym_fido_dev_info_new)(size_t) = NULL;
56 const char* (*sym_fido_dev_info_path)(const fido_dev_info_t *) = NULL;
57 const fido_dev_info_t* (*sym_fido_dev_info_ptr)(const fido_dev_info_t *, size_t) = NULL;
58 bool (*sym_fido_dev_is_fido2)(const fido_dev_t *) = NULL;
59 int (*sym_fido_dev_make_cred)(fido_dev_t *, fido_cred_t *, const char *) = NULL;
60 fido_dev_t* (*sym_fido_dev_new)(void) = NULL;
61 int (*sym_fido_dev_open)(fido_dev_t *, const char *) = NULL;
62 int (*sym_fido_dev_close)(fido_dev_t *) = NULL;
63 const char* (*sym_fido_strerr)(int) = NULL;
64
65 int dlopen_libfido2(void) {
66 return dlopen_many_sym_or_warn(
67 &libfido2_dl, "libfido2.so.1", LOG_DEBUG,
68 DLSYM_ARG(fido_assert_allow_cred),
69 DLSYM_ARG(fido_assert_free),
70 DLSYM_ARG(fido_assert_hmac_secret_len),
71 DLSYM_ARG(fido_assert_hmac_secret_ptr),
72 DLSYM_ARG(fido_assert_new),
73 DLSYM_ARG(fido_assert_set_clientdata_hash),
74 DLSYM_ARG(fido_assert_set_extensions),
75 DLSYM_ARG(fido_assert_set_hmac_salt),
76 DLSYM_ARG(fido_assert_set_rp),
77 DLSYM_ARG(fido_assert_set_up),
78 DLSYM_ARG(fido_assert_set_uv),
79 DLSYM_ARG(fido_cbor_info_extensions_len),
80 DLSYM_ARG(fido_cbor_info_extensions_ptr),
81 DLSYM_ARG(fido_cbor_info_free),
82 DLSYM_ARG(fido_cbor_info_new),
83 DLSYM_ARG(fido_cbor_info_options_len),
84 DLSYM_ARG(fido_cbor_info_options_name_ptr),
85 DLSYM_ARG(fido_cbor_info_options_value_ptr),
86 DLSYM_ARG(fido_cred_free),
87 DLSYM_ARG(fido_cred_id_len),
88 DLSYM_ARG(fido_cred_id_ptr),
89 DLSYM_ARG(fido_cred_new),
90 DLSYM_ARG(fido_cred_set_clientdata_hash),
91 DLSYM_ARG(fido_cred_set_extensions),
92 DLSYM_ARG(fido_cred_set_rk),
93 DLSYM_ARG(fido_cred_set_rp),
94 DLSYM_ARG(fido_cred_set_type),
95 DLSYM_ARG(fido_cred_set_user),
96 DLSYM_ARG(fido_cred_set_uv),
97 DLSYM_ARG(fido_dev_free),
98 DLSYM_ARG(fido_dev_get_assert),
99 DLSYM_ARG(fido_dev_get_cbor_info),
100 DLSYM_ARG(fido_dev_info_free),
101 DLSYM_ARG(fido_dev_info_manifest),
102 DLSYM_ARG(fido_dev_info_manufacturer_string),
103 DLSYM_ARG(fido_dev_info_new),
104 DLSYM_ARG(fido_dev_info_path),
105 DLSYM_ARG(fido_dev_info_product_string),
106 DLSYM_ARG(fido_dev_info_ptr),
107 DLSYM_ARG(fido_dev_is_fido2),
108 DLSYM_ARG(fido_dev_make_cred),
109 DLSYM_ARG(fido_dev_new),
110 DLSYM_ARG(fido_dev_open),
111 DLSYM_ARG(fido_dev_close),
112 DLSYM_ARG(fido_strerr));
113 }
114
115 static int verify_features(
116 fido_dev_t *d,
117 const char *path,
118 int log_level, /* the log level to use when device is not FIDO2 with hmac-secret */
119 bool *ret_has_rk,
120 bool *ret_has_client_pin,
121 bool *ret_has_up,
122 bool *ret_has_uv) {
123
124 _cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
125 bool found_extension = false;
126 char **e, **o;
127 const bool *b;
128 bool has_rk = false, has_client_pin = false, has_up = true, has_uv = false; /* Defaults are per table in 5.4 in FIDO2 spec */
129 size_t n;
130 int r;
131
132 assert(d);
133 assert(path);
134
135 if (!sym_fido_dev_is_fido2(d))
136 return log_full_errno(log_level,
137 SYNTHETIC_ERRNO(ENODEV),
138 "Specified device %s is not a FIDO2 device.", path);
139
140 di = sym_fido_cbor_info_new();
141 if (!di)
142 return log_oom();
143
144 r = sym_fido_dev_get_cbor_info(d, di);
145 if (r != FIDO_OK)
146 return log_error_errno(SYNTHETIC_ERRNO(EIO),
147 "Failed to get CBOR device info for %s: %s", path, sym_fido_strerr(r));
148
149 e = sym_fido_cbor_info_extensions_ptr(di);
150 n = sym_fido_cbor_info_extensions_len(di);
151 for (size_t i = 0; i < n; i++) {
152 log_debug("FIDO2 device implements extension: %s", e[i]);
153 if (streq(e[i], "hmac-secret"))
154 found_extension = true;
155 }
156
157 o = sym_fido_cbor_info_options_name_ptr(di);
158 b = sym_fido_cbor_info_options_value_ptr(di);
159 n = sym_fido_cbor_info_options_len(di);
160 for (size_t i = 0; i < n; i++) {
161 log_debug("FIDO2 device implements option %s: %s", o[i], yes_no(b[i]));
162 if (streq(o[i], "rk"))
163 has_rk = b[i];
164 if (streq(o[i], "clientPin"))
165 has_client_pin = b[i];
166 if (streq(o[i], "up"))
167 has_up = b[i];
168 if (streq(o[i], "uv"))
169 has_uv = b[i];
170 }
171
172 if (!found_extension)
173 return log_full_errno(log_level,
174 SYNTHETIC_ERRNO(ENODEV),
175 "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path);
176
177 log_debug("Has rk ('Resident Key') support: %s\n"
178 "Has clientPin support: %s\n"
179 "Has up ('User Presence') support: %s\n"
180 "Has uv ('User Verification') support: %s\n",
181 yes_no(has_rk),
182 yes_no(has_client_pin),
183 yes_no(has_up),
184 yes_no(has_uv));
185
186 if (ret_has_rk)
187 *ret_has_rk = has_rk;
188 if (ret_has_client_pin)
189 *ret_has_client_pin = has_client_pin;
190 if (ret_has_up)
191 *ret_has_up = has_up;
192 if (ret_has_uv)
193 *ret_has_uv = has_uv;
194
195 return 0;
196 }
197
198 static int fido2_use_hmac_hash_specific_token(
199 const char *path,
200 const char *rp_id,
201 const void *salt,
202 size_t salt_size,
203 const void *cid,
204 size_t cid_size,
205 char **pins,
206 Fido2EnrollFlags required, /* client pin/user presence required */
207 void **ret_hmac,
208 size_t *ret_hmac_size) {
209
210 _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
211 _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
212 _cleanup_(erase_and_freep) void *hmac_copy = NULL;
213 bool has_up, has_client_pin, has_uv;
214 size_t hmac_size;
215 const void *hmac;
216 int r;
217
218 assert(path);
219 assert(rp_id);
220 assert(salt);
221 assert(cid);
222 assert(ret_hmac);
223 assert(ret_hmac_size);
224
225 d = sym_fido_dev_new();
226 if (!d)
227 return log_oom();
228
229 r = sym_fido_dev_open(d, path);
230 if (r != FIDO_OK)
231 return log_error_errno(SYNTHETIC_ERRNO(EIO),
232 "Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
233
234 r = verify_features(d, path, LOG_ERR, NULL, &has_client_pin, &has_up, &has_uv);
235 if (r < 0)
236 return r;
237
238 if (!has_client_pin && FLAGS_SET(required, FIDO2ENROLL_PIN))
239 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
240 "PIN required to unlock, but FIDO2 device %s does not support it.",
241 path);
242
243 if (!has_up && FLAGS_SET(required, FIDO2ENROLL_UP))
244 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
245 "User presence test required to unlock, but FIDO2 device %s does not support it.",
246 path);
247
248 if (!has_uv && FLAGS_SET(required, FIDO2ENROLL_UV))
249 return log_error_errno(SYNTHETIC_ERRNO(EHWPOISON),
250 "User verification required to unlock, but FIDO2 device %s does not support it.",
251 path);
252
253 a = sym_fido_assert_new();
254 if (!a)
255 return log_oom();
256
257 r = sym_fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
258 if (r != FIDO_OK)
259 return log_error_errno(SYNTHETIC_ERRNO(EIO),
260 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r));
261
262 r = sym_fido_assert_set_hmac_salt(a, salt, salt_size);
263 if (r != FIDO_OK)
264 return log_error_errno(SYNTHETIC_ERRNO(EIO),
265 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
266
267 r = sym_fido_assert_set_rp(a, rp_id);
268 if (r != FIDO_OK)
269 return log_error_errno(SYNTHETIC_ERRNO(EIO),
270 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
271
272 r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
273 if (r != FIDO_OK)
274 return log_error_errno(SYNTHETIC_ERRNO(EIO),
275 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
276
277 r = sym_fido_assert_allow_cred(a, cid, cid_size);
278 if (r != FIDO_OK)
279 return log_error_errno(SYNTHETIC_ERRNO(EIO),
280 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
281
282 log_info("Asking FIDO2 token for authentication.");
283
284 if (has_up) {
285 r = sym_fido_assert_set_up(a, FLAGS_SET(required, FIDO2ENROLL_UP) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
286 if (r != FIDO_OK)
287 return log_error_errno(SYNTHETIC_ERRNO(EIO),
288 "Failed to %s FIDO2 user presence test: %s",
289 enable_disable(FLAGS_SET(required, FIDO2ENROLL_UP)),
290 sym_fido_strerr(r));
291
292 if (FLAGS_SET(required, FIDO2ENROLL_UP))
293 log_notice("%s%sPlease confirm presence on security token to unlock.",
294 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
295 emoji_enabled() ? " " : "");
296 }
297
298 if (has_uv && !FLAGS_SET(required, FIDO2ENROLL_UV_OMIT)) {
299 r = sym_fido_assert_set_uv(a, FLAGS_SET(required, FIDO2ENROLL_UV) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
300 if (r != FIDO_OK)
301 return log_error_errno(SYNTHETIC_ERRNO(EIO),
302 "Failed to %s FIDO2 user verification: %s",
303 enable_disable(FLAGS_SET(required, FIDO2ENROLL_UV)),
304 sym_fido_strerr(r));
305
306 if (FLAGS_SET(required, FIDO2ENROLL_UV))
307 log_notice("%s%sPlease verify user on security token to unlock.",
308 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
309 emoji_enabled() ? " " : "");
310 }
311
312 for (;;) {
313 bool retry_with_up = false, retry_with_pin = false;
314
315 if (FLAGS_SET(required, FIDO2ENROLL_PIN)) {
316 /* OK, we need a pin, try with all pins in turn */
317 if (strv_isempty(pins))
318 r = FIDO_ERR_PIN_REQUIRED;
319 else
320 STRV_FOREACH(i, pins) {
321 r = sym_fido_dev_get_assert(d, a, *i);
322 if (r != FIDO_ERR_PIN_INVALID)
323 break;
324 }
325
326 } else
327 r = sym_fido_dev_get_assert(d, a, NULL);
328
329 /* In some conditions, where a PIN or UP is required we might accept that. Let's check the
330 * conditions and if so try immediately again. */
331
332 switch (r) {
333
334 case FIDO_ERR_UP_REQUIRED:
335 /* So the token asked for "up". Try to turn it on, for compat with systemd 248 and try again. */
336
337 if (!has_up)
338 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
339 "Token asks for user presence test but doesn't advertise 'up' feature.");
340
341 if (FLAGS_SET(required, FIDO2ENROLL_UP))
342 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
343 "Token asks for user presence test but was already enabled.");
344
345 if (FLAGS_SET(required, FIDO2ENROLL_UP_IF_NEEDED)) {
346 log_notice("%s%sPlease confirm presence on security to unlock.",
347 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
348 emoji_enabled() ? " " : "");
349 retry_with_up = true;
350 }
351
352 break;
353
354 case FIDO_ERR_UNSUPPORTED_OPTION:
355 /* AuthenTrend ATKey.Pro returns this instead of FIDO_ERR_UP_REQUIRED, let's handle
356 * it gracefully (also see below.) */
357
358 if (has_up && (required & (FIDO2ENROLL_UP|FIDO2ENROLL_UP_IF_NEEDED)) == FIDO2ENROLL_UP_IF_NEEDED) {
359 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
360 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
361 emoji_enabled() ? " " : "");
362 retry_with_up = true;
363 }
364
365 break;
366
367 case FIDO_ERR_PIN_REQUIRED:
368 /* A pin was requested. Maybe supply one, if we are configured to do so on request */
369
370 if (!has_client_pin)
371 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
372 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
373
374 if (FLAGS_SET(required, FIDO2ENROLL_PIN) && !strv_isempty(pins))
375 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
376 "Token asks for PIN but one was already supplied.");
377
378 if ((required & (FIDO2ENROLL_PIN|FIDO2ENROLL_PIN_IF_NEEDED)) == FIDO2ENROLL_PIN_IF_NEEDED) {
379 /* If a PIN so far wasn't specified but is requested by the device, and
380 * FIDO2ENROLL_PIN_IF_NEEDED is set, then provide it */
381 log_debug("Retrying to create credential with PIN.");
382 retry_with_pin = true;
383 }
384
385 break;
386
387 default:
388 break;
389 }
390
391 if (!retry_with_up && !retry_with_pin)
392 break;
393
394 if (retry_with_up) {
395 r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
396 if (r != FIDO_OK)
397 return log_error_errno(SYNTHETIC_ERRNO(EIO),
398 "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r));
399
400 required |= FIDO2ENROLL_UP;
401 }
402
403 if (retry_with_pin)
404 required |= FIDO2ENROLL_PIN;
405 }
406
407 switch (r) {
408 case FIDO_OK:
409 break;
410 case FIDO_ERR_NO_CREDENTIALS:
411 return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
412 "Wrong security token; needed credentials not present on token.");
413 case FIDO_ERR_PIN_REQUIRED:
414 return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
415 "Security token requires PIN.");
416 case FIDO_ERR_PIN_AUTH_BLOCKED:
417 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
418 "PIN of security token is blocked, please remove/reinsert token.");
419 #ifdef FIDO_ERR_UV_BLOCKED
420 case FIDO_ERR_UV_BLOCKED:
421 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
422 "Verification of security token is blocked, please remove/reinsert token.");
423 #endif
424 case FIDO_ERR_PIN_INVALID:
425 return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
426 "PIN of security token incorrect.");
427 case FIDO_ERR_UP_REQUIRED:
428 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
429 "User presence required.");
430 case FIDO_ERR_ACTION_TIMEOUT:
431 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
432 "Token action timeout. (User didn't interact with token quickly enough.)");
433 default:
434 return log_error_errno(SYNTHETIC_ERRNO(EIO),
435 "Failed to ask token for assertion: %s", sym_fido_strerr(r));
436 }
437
438 hmac = sym_fido_assert_hmac_secret_ptr(a, 0);
439 if (!hmac)
440 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
441
442 hmac_size = sym_fido_assert_hmac_secret_len(a, 0);
443
444 hmac_copy = memdup(hmac, hmac_size);
445 if (!hmac_copy)
446 return log_oom();
447
448 *ret_hmac = TAKE_PTR(hmac_copy);
449 *ret_hmac_size = hmac_size;
450 return 0;
451 }
452
453 int fido2_use_hmac_hash(
454 const char *device,
455 const char *rp_id,
456 const void *salt,
457 size_t salt_size,
458 const void *cid,
459 size_t cid_size,
460 char **pins,
461 Fido2EnrollFlags required, /* client pin/user presence required */
462 void **ret_hmac,
463 size_t *ret_hmac_size) {
464
465 size_t allocated = 64, found = 0;
466 fido_dev_info_t *di = NULL;
467 int r;
468
469 r = dlopen_libfido2();
470 if (r < 0)
471 return log_error_errno(r, "FIDO2 support is not installed.");
472
473 if (device)
474 return fido2_use_hmac_hash_specific_token(device, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
475
476 di = sym_fido_dev_info_new(allocated);
477 if (!di)
478 return log_oom();
479
480 r = sym_fido_dev_info_manifest(di, allocated, &found);
481 if (r == FIDO_ERR_INTERNAL) {
482 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
483 r = log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got FIDO_ERR_INTERNAL, assuming no devices.");
484 goto finish;
485 }
486 if (r != FIDO_OK) {
487 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
488 goto finish;
489 }
490
491 for (size_t i = 0; i < found; i++) {
492 const fido_dev_info_t *entry;
493 const char *path;
494
495 entry = sym_fido_dev_info_ptr(di, i);
496 if (!entry) {
497 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
498 "Failed to get device information for FIDO device %zu.", i);
499 goto finish;
500 }
501
502 path = sym_fido_dev_info_path(entry);
503 if (!path) {
504 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
505 "Failed to query FIDO device path.");
506 goto finish;
507 }
508
509 r = fido2_use_hmac_hash_specific_token(path, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
510 if (!IN_SET(r,
511 -EBADSLT, /* device doesn't understand our credential hash */
512 -ENODEV /* device is not a FIDO2 device with HMAC-SECRET */))
513 goto finish;
514 }
515
516 r = -EAGAIN;
517
518 finish:
519 sym_fido_dev_info_free(&di, allocated);
520 return r;
521 }
522
523 #define FIDO2_SALT_SIZE 32
524
525 int fido2_generate_hmac_hash(
526 const char *device,
527 const char *rp_id,
528 const char *rp_name,
529 const void *user_id, size_t user_id_len,
530 const char *user_name,
531 const char *user_display_name,
532 const char *user_icon,
533 const char *askpw_icon_name,
534 Fido2EnrollFlags lock_with,
535 void **ret_cid, size_t *ret_cid_size,
536 void **ret_salt, size_t *ret_salt_size,
537 void **ret_secret, size_t *ret_secret_size,
538 char **ret_usedpin,
539 Fido2EnrollFlags *ret_locked_with) {
540
541 _cleanup_(erase_and_freep) void *salt = NULL, *secret_copy = NULL;
542 _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
543 _cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
544 _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
545 _cleanup_(erase_and_freep) char *used_pin = NULL;
546 bool has_rk, has_client_pin, has_up, has_uv;
547 _cleanup_free_ char *cid_copy = NULL;
548 size_t cid_size, secret_size;
549 const void *cid, *secret;
550 int r;
551
552 assert(device);
553 assert(ret_cid);
554 assert(ret_cid_size);
555 assert(ret_salt);
556 assert(ret_salt_size);
557 assert(ret_secret);
558 assert(ret_secret_size);
559
560 /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
561 * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
562 * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
563 * device never sees the volume key.
564 *
565 * S = HMAC-SHA256(I, D)
566 *
567 * with: S → LUKS/account authentication key (never stored)
568 * I → internal key on FIDO2 device (stored in the FIDO2 device)
569 * D → salt we generate here (stored in the privileged part of the JSON record)
570 *
571 */
572
573 assert(device);
574 assert((lock_with & ~(FIDO2ENROLL_PIN|FIDO2ENROLL_UP|FIDO2ENROLL_UV)) == 0);
575
576 r = dlopen_libfido2();
577 if (r < 0)
578 return log_error_errno(r, "FIDO2 token support is not installed.");
579
580 salt = malloc(FIDO2_SALT_SIZE);
581 if (!salt)
582 return log_oom();
583
584 r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK);
585 if (r < 0)
586 return log_error_errno(r, "Failed to generate salt: %m");
587
588 d = sym_fido_dev_new();
589 if (!d)
590 return log_oom();
591
592 r = sym_fido_dev_open(d, device);
593 if (r != FIDO_OK)
594 return log_error_errno(SYNTHETIC_ERRNO(EIO),
595 "Failed to open FIDO2 device %s: %s", device, sym_fido_strerr(r));
596
597 r = verify_features(d, device, LOG_ERR, &has_rk, &has_client_pin, &has_up, &has_uv);
598 if (r < 0)
599 return r;
600
601 /* While enrolling degrade gracefully if the requested feature set isn't available, but let the user know */
602 if (!has_client_pin && FLAGS_SET(lock_with, FIDO2ENROLL_PIN)) {
603 log_notice("Requested to lock with PIN, but FIDO2 device %s does not support it, disabling.", device);
604 lock_with &= ~FIDO2ENROLL_PIN;
605 }
606
607 if (!has_up && FLAGS_SET(lock_with, FIDO2ENROLL_UP)) {
608 log_notice("Locking with user presence test requested, but FIDO2 device %s does not support it, disabling.", device);
609 lock_with &= ~FIDO2ENROLL_UP;
610 }
611
612 if (!has_uv && FLAGS_SET(lock_with, FIDO2ENROLL_UV)) {
613 log_notice("Locking with user verification test requested, but FIDO2 device %s does not support it, disabling.", device);
614 lock_with &= ~FIDO2ENROLL_UV;
615 }
616
617 c = sym_fido_cred_new();
618 if (!c)
619 return log_oom();
620
621 r = sym_fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
622 if (r != FIDO_OK)
623 return log_error_errno(SYNTHETIC_ERRNO(EIO),
624 "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r));
625
626 r = sym_fido_cred_set_rp(c, rp_id, rp_name);
627 if (r != FIDO_OK)
628 return log_error_errno(SYNTHETIC_ERRNO(EIO),
629 "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r));
630
631 r = sym_fido_cred_set_type(c, COSE_ES256);
632 if (r != FIDO_OK)
633 return log_error_errno(SYNTHETIC_ERRNO(EIO),
634 "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r));
635
636 r = sym_fido_cred_set_user(
637 c,
638 user_id, user_id_len,
639 user_name,
640 user_display_name,
641 user_icon);
642 if (r != FIDO_OK)
643 return log_error_errno(SYNTHETIC_ERRNO(EIO),
644 "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r));
645
646 r = sym_fido_cred_set_clientdata_hash(c, (const unsigned char[32]) {}, 32);
647 if (r != FIDO_OK)
648 return log_error_errno(SYNTHETIC_ERRNO(EIO),
649 "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r));
650
651 if (has_rk) {
652 r = sym_fido_cred_set_rk(c, FIDO_OPT_FALSE);
653 if (r != FIDO_OK)
654 return log_error_errno(SYNTHETIC_ERRNO(EIO),
655 "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r));
656 }
657
658 if (has_uv) {
659 r = sym_fido_cred_set_uv(c, FIDO_OPT_FALSE);
660 if (r != FIDO_OK)
661 return log_error_errno(SYNTHETIC_ERRNO(EIO),
662 "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r));
663 }
664
665 /* As per specification "up" is assumed to be implicit when making credentials, hence we don't
666 * explicitly enable/disable it here */
667
668 log_info("Initializing FIDO2 credential on security token.");
669
670 if (has_uv || has_up)
671 log_notice("%s%s(Hint: This might require confirmation of user presence on security token.)",
672 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
673 emoji_enabled() ? " " : "");
674
675 r = sym_fido_dev_make_cred(d, c, NULL);
676 if (r == FIDO_ERR_PIN_REQUIRED) {
677
678 if (!has_client_pin)
679 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
680 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
681
682 for (;;) {
683 _cleanup_(strv_free_erasep) char **pin = NULL;
684
685 r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", "fido2-pin", USEC_INFINITY, 0, &pin);
686 if (r < 0)
687 return log_error_errno(r, "Failed to acquire user PIN: %m");
688
689 r = FIDO_ERR_PIN_INVALID;
690 STRV_FOREACH(i, pin) {
691 if (isempty(*i)) {
692 log_notice("PIN may not be empty.");
693 continue;
694 }
695
696 r = sym_fido_dev_make_cred(d, c, *i);
697 if (r == FIDO_OK) {
698 used_pin = strdup(*i);
699 if (!used_pin)
700 return log_oom();
701 break;
702 }
703 if (r != FIDO_ERR_PIN_INVALID)
704 break;
705 }
706
707 if (r != FIDO_ERR_PIN_INVALID)
708 break;
709
710 log_notice("PIN incorrect, please try again.");
711 }
712 }
713 if (r == FIDO_ERR_PIN_AUTH_BLOCKED)
714 return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
715 "Token PIN is currently blocked, please remove and reinsert token.");
716 #ifdef FIDO_ERR_UV_BLOCKED
717 if (r == FIDO_ERR_UV_BLOCKED)
718 return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
719 "Token verification is currently blocked, please remove and reinsert token.");
720 #endif
721 if (r == FIDO_ERR_ACTION_TIMEOUT)
722 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
723 "Token action timeout. (User didn't interact with token quickly enough.)");
724 if (r != FIDO_OK)
725 return log_error_errno(SYNTHETIC_ERRNO(EIO),
726 "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r));
727
728 cid = sym_fido_cred_id_ptr(c);
729 if (!cid)
730 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get FIDO2 credential ID.");
731
732 cid_size = sym_fido_cred_id_len(c);
733
734 a = sym_fido_assert_new();
735 if (!a)
736 return log_oom();
737
738 r = sym_fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
739 if (r != FIDO_OK)
740 return log_error_errno(SYNTHETIC_ERRNO(EIO),
741 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r));
742
743 r = sym_fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE);
744 if (r != FIDO_OK)
745 return log_error_errno(SYNTHETIC_ERRNO(EIO),
746 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
747
748 r = sym_fido_assert_set_rp(a, rp_id);
749 if (r != FIDO_OK)
750 return log_error_errno(SYNTHETIC_ERRNO(EIO),
751 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
752
753 r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
754 if (r != FIDO_OK)
755 return log_error_errno(SYNTHETIC_ERRNO(EIO),
756 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
757
758 r = sym_fido_assert_allow_cred(a, cid, cid_size);
759 if (r != FIDO_OK)
760 return log_error_errno(SYNTHETIC_ERRNO(EIO),
761 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
762
763 log_info("Generating secret key on FIDO2 security token.");
764
765 if (has_up) {
766 r = sym_fido_assert_set_up(a, FLAGS_SET(lock_with, FIDO2ENROLL_UP) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
767 if (r != FIDO_OK)
768 return log_error_errno(SYNTHETIC_ERRNO(EIO),
769 "Failed to %s FIDO2 user presence test: %s",
770 enable_disable(FLAGS_SET(lock_with, FIDO2ENROLL_UP)),
771 sym_fido_strerr(r));
772
773 if (FLAGS_SET(lock_with, FIDO2ENROLL_UP))
774 log_notice("%s%sIn order to allow secret key generation, please confirm presence on security token.",
775 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
776 emoji_enabled() ? " " : "");
777 }
778
779 if (has_uv) {
780 r = sym_fido_assert_set_uv(a, FLAGS_SET(lock_with, FIDO2ENROLL_UV) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
781 if (r != FIDO_OK)
782 return log_error_errno(SYNTHETIC_ERRNO(EIO),
783 "Failed to %s FIDO user verification: %s",
784 enable_disable(FLAGS_SET(lock_with, FIDO2ENROLL_UV)),
785 sym_fido_strerr(r));
786
787 if (FLAGS_SET(lock_with, FIDO2ENROLL_UV))
788 log_notice("%s%sIn order to allow secret key generation, please verify user on security token.",
789 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
790 emoji_enabled() ? " " : "");
791 }
792
793 for (;;) {
794 bool retry_with_up = false, retry_with_pin = false;
795
796 r = sym_fido_dev_get_assert(d, a, FLAGS_SET(lock_with, FIDO2ENROLL_PIN) ? used_pin : NULL);
797
798 switch (r) {
799
800 case FIDO_ERR_UP_REQUIRED:
801 /* If the token asks for "up" when we turn off, then this might be a feature that
802 * isn't optional. Let's enable it */
803
804 if (!has_up)
805 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
806 "Token asks for user presence test but doesn't advertise 'up' feature.");
807
808 if (FLAGS_SET(lock_with, FIDO2ENROLL_UP))
809 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
810 "Token asks for user presence test but was already enabled.");
811
812 log_notice("%s%sLocking without user presence test requested, but FIDO2 device %s requires it, enabling.",
813 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
814 emoji_enabled() ? " " : "",
815 device);
816
817 retry_with_up = true;
818 break;
819
820 case FIDO_ERR_UNSUPPORTED_OPTION:
821 /* AuthenTrend ATKey.Pro says it supports "up", but if we disable it it will fail
822 * with FIDO_ERR_UNSUPPORTED_OPTION, probably because it isn't actually
823 * optional. Let's see if turning it on works. This is very similar to the
824 * FIDO_ERR_UP_REQUIRED case, but since the error is so vague we implement it
825 * slightly more defensively. */
826
827 if (has_up && !FLAGS_SET(lock_with, FIDO2ENROLL_UP)) {
828 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
829 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
830 emoji_enabled() ? " " : "");
831 retry_with_up = true;
832 }
833
834 break;
835
836 case FIDO_ERR_PIN_REQUIRED:
837 if (!has_client_pin)
838 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
839 "Token asks for client PIN check but doesn't advertise 'clientPin' feature.");
840
841 if (FLAGS_SET(lock_with, FIDO2ENROLL_PIN))
842 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
843 "Token asks for user client PIN check but was already enabled.");
844
845 log_debug("Token requires PIN for assertion, enabling.");
846 retry_with_pin = true;
847 break;
848
849 default:
850 break;
851 }
852
853 if (!retry_with_up && !retry_with_pin)
854 break;
855
856 if (retry_with_up) {
857 r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
858 if (r != FIDO_OK)
859 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r));
860
861 lock_with |= FIDO2ENROLL_UP;
862 }
863
864 if (retry_with_pin)
865 lock_with |= FIDO2ENROLL_PIN;
866 }
867
868 if (r == FIDO_ERR_ACTION_TIMEOUT)
869 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
870 "Token action timeout. (User didn't interact with token quickly enough.)");
871 if (r != FIDO_OK)
872 return log_error_errno(SYNTHETIC_ERRNO(EIO),
873 "Failed to ask token for assertion: %s", sym_fido_strerr(r));
874
875 secret = sym_fido_assert_hmac_secret_ptr(a, 0);
876 if (!secret)
877 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
878
879 secret_size = sym_fido_assert_hmac_secret_len(a, 0);
880
881 secret_copy = memdup(secret, secret_size);
882 if (!secret_copy)
883 return log_oom();
884
885 cid_copy = memdup(cid, cid_size);
886 if (!cid_copy)
887 return log_oom();
888
889 *ret_cid = TAKE_PTR(cid_copy);
890 *ret_cid_size = cid_size;
891 *ret_salt = TAKE_PTR(salt);
892 *ret_salt_size = FIDO2_SALT_SIZE;
893 *ret_secret = TAKE_PTR(secret_copy);
894 *ret_secret_size = secret_size;
895
896 if (ret_usedpin)
897 *ret_usedpin = TAKE_PTR(used_pin);
898
899 if (ret_locked_with)
900 *ret_locked_with = lock_with;
901
902 return 0;
903 }
904 #endif
905
906 #if HAVE_LIBFIDO2
907 static int check_device_is_fido2_with_hmac_secret(const char *path) {
908 _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
909 int r;
910
911 d = sym_fido_dev_new();
912 if (!d)
913 return log_oom();
914
915 r = sym_fido_dev_open(d, path);
916 if (r != FIDO_OK)
917 return log_error_errno(SYNTHETIC_ERRNO(EIO),
918 "Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
919
920 r = verify_features(d, path, LOG_DEBUG, NULL, NULL, NULL, NULL);
921 if (r == -ENODEV) /* Not a FIDO2 device, or not implementing 'hmac-secret' */
922 return false;
923 if (r < 0)
924 return r;
925
926 return true;
927 }
928 #endif
929
930 int fido2_list_devices(void) {
931 #if HAVE_LIBFIDO2
932 _cleanup_(table_unrefp) Table *t = NULL;
933 size_t allocated = 64, found = 0;
934 fido_dev_info_t *di = NULL;
935 int r;
936
937 r = dlopen_libfido2();
938 if (r < 0)
939 return log_error_errno(r, "FIDO2 token support is not installed.");
940
941 di = sym_fido_dev_info_new(allocated);
942 if (!di)
943 return log_oom();
944
945 r = sym_fido_dev_info_manifest(di, allocated, &found);
946 if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
947 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
948 log_info("No FIDO2 devices found.");
949 r = 0;
950 goto finish;
951 }
952 if (r != FIDO_OK) {
953 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
954 goto finish;
955 }
956
957 t = table_new("path", "manufacturer", "product");
958 if (!t) {
959 r = log_oom();
960 goto finish;
961 }
962
963 for (size_t i = 0; i < found; i++) {
964 const fido_dev_info_t *entry;
965
966 entry = sym_fido_dev_info_ptr(di, i);
967 if (!entry) {
968 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
969 "Failed to get device information for FIDO device %zu.", i);
970 goto finish;
971 }
972
973 r = check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry));
974 if (r < 0)
975 goto finish;
976 if (!r)
977 continue;
978
979 r = table_add_many(
980 t,
981 TABLE_PATH, sym_fido_dev_info_path(entry),
982 TABLE_STRING, sym_fido_dev_info_manufacturer_string(entry),
983 TABLE_STRING, sym_fido_dev_info_product_string(entry));
984 if (r < 0) {
985 table_log_add_error(r);
986 goto finish;
987 }
988 }
989
990 r = table_print(t, stdout);
991 if (r < 0) {
992 log_error_errno(r, "Failed to show device table: %m");
993 goto finish;
994 }
995
996 r = 0;
997
998 finish:
999 sym_fido_dev_info_free(&di, allocated);
1000 return r;
1001 #else
1002 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1003 "FIDO2 tokens not supported on this build.");
1004 #endif
1005 }
1006
1007 int fido2_find_device_auto(char **ret) {
1008 #if HAVE_LIBFIDO2
1009 _cleanup_free_ char *copy = NULL;
1010 size_t di_size = 64, found = 0;
1011 const fido_dev_info_t *entry;
1012 fido_dev_info_t *di = NULL;
1013 const char *path;
1014 int r;
1015
1016 r = dlopen_libfido2();
1017 if (r < 0)
1018 return log_error_errno(r, "FIDO2 token support is not installed.");
1019
1020 di = sym_fido_dev_info_new(di_size);
1021 if (!di)
1022 return log_oom();
1023
1024 r = sym_fido_dev_info_manifest(di, di_size, &found);
1025 if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
1026 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1027 r = log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No FIDO devices found.");
1028 goto finish;
1029 }
1030 if (r != FIDO_OK) {
1031 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO devices: %s", sym_fido_strerr(r));
1032 goto finish;
1033 }
1034 if (found > 1) {
1035 r = log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "More than one FIDO device found.");
1036 goto finish;
1037 }
1038
1039 entry = sym_fido_dev_info_ptr(di, 0);
1040 if (!entry) {
1041 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
1042 "Failed to get device information for FIDO device 0.");
1043 goto finish;
1044 }
1045
1046 r = check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry));
1047 if (r < 0)
1048 goto finish;
1049 if (!r) {
1050 r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "FIDO device discovered does not implement FIDO2 with 'hmac-secret' extension.");
1051 goto finish;
1052 }
1053
1054 path = sym_fido_dev_info_path(entry);
1055 if (!path) {
1056 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
1057 "Failed to query FIDO device path.");
1058 goto finish;
1059 }
1060
1061 copy = strdup(path);
1062 if (!copy) {
1063 r = log_oom();
1064 goto finish;
1065 }
1066
1067 *ret = TAKE_PTR(copy);
1068 r = 0;
1069
1070 finish:
1071 sym_fido_dev_info_free(&di, di_size);
1072 return r;
1073 #else
1074 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1075 "FIDO2 tokens not supported on this build.");
1076 #endif
1077 }
1078
1079 int fido2_have_device(const char *device) {
1080 #if HAVE_LIBFIDO2
1081 size_t allocated = 64, found = 0;
1082 fido_dev_info_t *di = NULL;
1083 int r;
1084
1085 /* Return == 0 if not devices are found, > 0 if at least one is found */
1086
1087 r = dlopen_libfido2();
1088 if (r < 0)
1089 return log_error_errno(r, "FIDO2 support is not installed.");
1090
1091 if (device) {
1092 if (access(device, F_OK) < 0) {
1093 if (errno == ENOENT)
1094 return 0;
1095
1096 return log_error_errno(errno, "Failed to determine whether device '%s' exists: %m", device);
1097 }
1098
1099 return 1;
1100 }
1101
1102 di = sym_fido_dev_info_new(allocated);
1103 if (!di)
1104 return log_oom();
1105
1106 r = sym_fido_dev_info_manifest(di, allocated, &found);
1107 if (r == FIDO_ERR_INTERNAL) {
1108 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1109 r = 0;
1110 goto finish;
1111 }
1112 if (r != FIDO_OK) {
1113 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
1114 goto finish;
1115 }
1116
1117 r = found;
1118
1119 finish:
1120 sym_fido_dev_info_free(&di, allocated);
1121 return r;
1122 #else
1123 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1124 "FIDO2 tokens not supported on this build.");
1125 #endif
1126 }