]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/libfido2-util.c
sd-boot+bootctl: invert order of entries w/o sort-key
[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 char **i;
317
318 /* OK, we need a pin, try with all pins in turn */
319 if (strv_isempty(pins))
320 r = FIDO_ERR_PIN_REQUIRED;
321 else
322 STRV_FOREACH(i, pins) {
323 r = sym_fido_dev_get_assert(d, a, *i);
324 if (r != FIDO_ERR_PIN_INVALID)
325 break;
326 }
327
328 } else
329 r = sym_fido_dev_get_assert(d, a, NULL);
330
331 /* In some conditions, where a PIN or UP is required we might accept that. Let's check the
332 * conditions and if so try immediately again. */
333
334 switch (r) {
335
336 case FIDO_ERR_UP_REQUIRED:
337 /* So the token asked for "up". Try to turn it on, for compat with systemd 248 and try again. */
338
339 if (!has_up)
340 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
341 "Token asks for user presence test but doesn't advertise 'up' feature.");
342
343 if (FLAGS_SET(required, FIDO2ENROLL_UP))
344 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
345 "Token asks for user presence test but was already enabled.");
346
347 if (FLAGS_SET(required, FIDO2ENROLL_UP_IF_NEEDED)) {
348 log_notice("%s%sPlease confirm presence on security to unlock.",
349 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
350 emoji_enabled() ? " " : "");
351 retry_with_up = true;
352 }
353
354 break;
355
356 case FIDO_ERR_UNSUPPORTED_OPTION:
357 /* AuthenTrend ATKey.Pro returns this instead of FIDO_ERR_UP_REQUIRED, let's handle
358 * it gracefully (also see below.) */
359
360 if (has_up && (required & (FIDO2ENROLL_UP|FIDO2ENROLL_UP_IF_NEEDED)) == FIDO2ENROLL_UP_IF_NEEDED) {
361 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
362 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
363 emoji_enabled() ? " " : "");
364 retry_with_up = true;
365 }
366
367 break;
368
369 case FIDO_ERR_PIN_REQUIRED:
370 /* A pin was requested. Maybe supply one, if we are configured to do so on request */
371
372 if (!has_client_pin)
373 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
374 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
375
376 if (FLAGS_SET(required, FIDO2ENROLL_PIN) && !strv_isempty(pins))
377 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
378 "Token asks for PIN but one was already supplied.");
379
380 if ((required & (FIDO2ENROLL_PIN|FIDO2ENROLL_PIN_IF_NEEDED)) == FIDO2ENROLL_PIN_IF_NEEDED) {
381 /* If a PIN so far wasn't specified but is requested by the device, and
382 * FIDO2ENROLL_PIN_IF_NEEDED is set, then provide it */
383 log_debug("Retrying to create credential with PIN.");
384 retry_with_pin = true;
385 }
386
387 break;
388
389 default:
390 break;
391 }
392
393 if (!retry_with_up && !retry_with_pin)
394 break;
395
396 if (retry_with_up) {
397 r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
398 if (r != FIDO_OK)
399 return log_error_errno(SYNTHETIC_ERRNO(EIO),
400 "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r));
401
402 required |= FIDO2ENROLL_UP;
403 }
404
405 if (retry_with_pin)
406 required |= FIDO2ENROLL_PIN;
407 }
408
409 switch (r) {
410 case FIDO_OK:
411 break;
412 case FIDO_ERR_NO_CREDENTIALS:
413 return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
414 "Wrong security token; needed credentials not present on token.");
415 case FIDO_ERR_PIN_REQUIRED:
416 return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
417 "Security token requires PIN.");
418 case FIDO_ERR_PIN_AUTH_BLOCKED:
419 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
420 "PIN of security token is blocked, please remove/reinsert token.");
421 #ifdef FIDO_ERR_UV_BLOCKED
422 case FIDO_ERR_UV_BLOCKED:
423 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
424 "Verification of security token is blocked, please remove/reinsert token.");
425 #endif
426 case FIDO_ERR_PIN_INVALID:
427 return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
428 "PIN of security token incorrect.");
429 case FIDO_ERR_UP_REQUIRED:
430 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
431 "User presence required.");
432 case FIDO_ERR_ACTION_TIMEOUT:
433 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
434 "Token action timeout. (User didn't interact with token quickly enough.)");
435 default:
436 return log_error_errno(SYNTHETIC_ERRNO(EIO),
437 "Failed to ask token for assertion: %s", sym_fido_strerr(r));
438 }
439
440 hmac = sym_fido_assert_hmac_secret_ptr(a, 0);
441 if (!hmac)
442 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
443
444 hmac_size = sym_fido_assert_hmac_secret_len(a, 0);
445
446 hmac_copy = memdup(hmac, hmac_size);
447 if (!hmac_copy)
448 return log_oom();
449
450 *ret_hmac = TAKE_PTR(hmac_copy);
451 *ret_hmac_size = hmac_size;
452 return 0;
453 }
454
455 int fido2_use_hmac_hash(
456 const char *device,
457 const char *rp_id,
458 const void *salt,
459 size_t salt_size,
460 const void *cid,
461 size_t cid_size,
462 char **pins,
463 Fido2EnrollFlags required, /* client pin/user presence required */
464 void **ret_hmac,
465 size_t *ret_hmac_size) {
466
467 size_t allocated = 64, found = 0;
468 fido_dev_info_t *di = NULL;
469 int r;
470
471 r = dlopen_libfido2();
472 if (r < 0)
473 return log_error_errno(r, "FIDO2 support is not installed.");
474
475 if (device)
476 return fido2_use_hmac_hash_specific_token(device, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
477
478 di = sym_fido_dev_info_new(allocated);
479 if (!di)
480 return log_oom();
481
482 r = sym_fido_dev_info_manifest(di, allocated, &found);
483 if (r == FIDO_ERR_INTERNAL) {
484 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
485 r = log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got FIDO_ERR_INTERNAL, assuming no devices.");
486 goto finish;
487 }
488 if (r != FIDO_OK) {
489 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
490 goto finish;
491 }
492
493 for (size_t i = 0; i < found; i++) {
494 const fido_dev_info_t *entry;
495 const char *path;
496
497 entry = sym_fido_dev_info_ptr(di, i);
498 if (!entry) {
499 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
500 "Failed to get device information for FIDO device %zu.", i);
501 goto finish;
502 }
503
504 path = sym_fido_dev_info_path(entry);
505 if (!path) {
506 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
507 "Failed to query FIDO device path.");
508 goto finish;
509 }
510
511 r = fido2_use_hmac_hash_specific_token(path, rp_id, salt, salt_size, cid, cid_size, pins, required, ret_hmac, ret_hmac_size);
512 if (!IN_SET(r,
513 -EBADSLT, /* device doesn't understand our credential hash */
514 -ENODEV /* device is not a FIDO2 device with HMAC-SECRET */))
515 goto finish;
516 }
517
518 r = -EAGAIN;
519
520 finish:
521 sym_fido_dev_info_free(&di, allocated);
522 return r;
523 }
524
525 #define FIDO2_SALT_SIZE 32
526
527 int fido2_generate_hmac_hash(
528 const char *device,
529 const char *rp_id,
530 const char *rp_name,
531 const void *user_id, size_t user_id_len,
532 const char *user_name,
533 const char *user_display_name,
534 const char *user_icon,
535 const char *askpw_icon_name,
536 Fido2EnrollFlags lock_with,
537 void **ret_cid, size_t *ret_cid_size,
538 void **ret_salt, size_t *ret_salt_size,
539 void **ret_secret, size_t *ret_secret_size,
540 char **ret_usedpin,
541 Fido2EnrollFlags *ret_locked_with) {
542
543 _cleanup_(erase_and_freep) void *salt = NULL, *secret_copy = NULL;
544 _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
545 _cleanup_(fido_cred_free_wrapper) fido_cred_t *c = NULL;
546 _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
547 _cleanup_(erase_and_freep) char *used_pin = NULL;
548 bool has_rk, has_client_pin, has_up, has_uv;
549 _cleanup_free_ char *cid_copy = NULL;
550 size_t cid_size, secret_size;
551 const void *cid, *secret;
552 int r;
553
554 assert(device);
555 assert(ret_cid);
556 assert(ret_cid_size);
557 assert(ret_salt);
558 assert(ret_salt_size);
559 assert(ret_secret);
560 assert(ret_secret_size);
561
562 /* Construction is like this: we generate a salt of 32 bytes. We then ask the FIDO2 device to
563 * HMAC-SHA256 it for us with its internal key. The result is the key used by LUKS and account
564 * authentication. LUKS and UNIX password auth all do their own salting before hashing, so that FIDO2
565 * device never sees the volume key.
566 *
567 * S = HMAC-SHA256(I, D)
568 *
569 * with: S → LUKS/account authentication key (never stored)
570 * I → internal key on FIDO2 device (stored in the FIDO2 device)
571 * D → salt we generate here (stored in the privileged part of the JSON record)
572 *
573 */
574
575 assert(device);
576 assert((lock_with & ~(FIDO2ENROLL_PIN|FIDO2ENROLL_UP|FIDO2ENROLL_UV)) == 0);
577
578 r = dlopen_libfido2();
579 if (r < 0)
580 return log_error_errno(r, "FIDO2 token support is not installed.");
581
582 salt = malloc(FIDO2_SALT_SIZE);
583 if (!salt)
584 return log_oom();
585
586 r = genuine_random_bytes(salt, FIDO2_SALT_SIZE, RANDOM_BLOCK);
587 if (r < 0)
588 return log_error_errno(r, "Failed to generate salt: %m");
589
590 d = sym_fido_dev_new();
591 if (!d)
592 return log_oom();
593
594 r = sym_fido_dev_open(d, device);
595 if (r != FIDO_OK)
596 return log_error_errno(SYNTHETIC_ERRNO(EIO),
597 "Failed to open FIDO2 device %s: %s", device, sym_fido_strerr(r));
598
599 r = verify_features(d, device, LOG_ERR, &has_rk, &has_client_pin, &has_up, &has_uv);
600 if (r < 0)
601 return r;
602
603 /* While enrolling degrade gracefully if the requested feature set isn't available, but let the user know */
604 if (!has_client_pin && FLAGS_SET(lock_with, FIDO2ENROLL_PIN)) {
605 log_notice("Requested to lock with PIN, but FIDO2 device %s does not support it, disabling.", device);
606 lock_with &= ~FIDO2ENROLL_PIN;
607 }
608
609 if (!has_up && FLAGS_SET(lock_with, FIDO2ENROLL_UP)) {
610 log_notice("Locking with user presence test requested, but FIDO2 device %s does not support it, disabling.", device);
611 lock_with &= ~FIDO2ENROLL_UP;
612 }
613
614 if (!has_uv && FLAGS_SET(lock_with, FIDO2ENROLL_UV)) {
615 log_notice("Locking with user verification test requested, but FIDO2 device %s does not support it, disabling.", device);
616 lock_with &= ~FIDO2ENROLL_UV;
617 }
618
619 c = sym_fido_cred_new();
620 if (!c)
621 return log_oom();
622
623 r = sym_fido_cred_set_extensions(c, FIDO_EXT_HMAC_SECRET);
624 if (r != FIDO_OK)
625 return log_error_errno(SYNTHETIC_ERRNO(EIO),
626 "Failed to enable HMAC-SECRET extension on FIDO2 credential: %s", sym_fido_strerr(r));
627
628 r = sym_fido_cred_set_rp(c, rp_id, rp_name);
629 if (r != FIDO_OK)
630 return log_error_errno(SYNTHETIC_ERRNO(EIO),
631 "Failed to set FIDO2 credential relying party ID/name: %s", sym_fido_strerr(r));
632
633 r = sym_fido_cred_set_type(c, COSE_ES256);
634 if (r != FIDO_OK)
635 return log_error_errno(SYNTHETIC_ERRNO(EIO),
636 "Failed to set FIDO2 credential type to ES256: %s", sym_fido_strerr(r));
637
638 r = sym_fido_cred_set_user(
639 c,
640 user_id, user_id_len,
641 user_name,
642 user_display_name,
643 user_icon);
644 if (r != FIDO_OK)
645 return log_error_errno(SYNTHETIC_ERRNO(EIO),
646 "Failed to set FIDO2 credential user data: %s", sym_fido_strerr(r));
647
648 r = sym_fido_cred_set_clientdata_hash(c, (const unsigned char[32]) {}, 32);
649 if (r != FIDO_OK)
650 return log_error_errno(SYNTHETIC_ERRNO(EIO),
651 "Failed to set FIDO2 client data hash: %s", sym_fido_strerr(r));
652
653 if (has_rk) {
654 r = sym_fido_cred_set_rk(c, FIDO_OPT_FALSE);
655 if (r != FIDO_OK)
656 return log_error_errno(SYNTHETIC_ERRNO(EIO),
657 "Failed to turn off FIDO2 resident key option of credential: %s", sym_fido_strerr(r));
658 }
659
660 if (has_uv) {
661 r = sym_fido_cred_set_uv(c, FIDO_OPT_FALSE);
662 if (r != FIDO_OK)
663 return log_error_errno(SYNTHETIC_ERRNO(EIO),
664 "Failed to turn off FIDO2 user verification option of credential: %s", sym_fido_strerr(r));
665 }
666
667 /* As per specification "up" is assumed to be implicit when making credentials, hence we don't
668 * explicitly enable/disable it here */
669
670 log_info("Initializing FIDO2 credential on security token.");
671
672 if (has_uv || has_up)
673 log_notice("%s%s(Hint: This might require confirmation of user presence on security token.)",
674 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
675 emoji_enabled() ? " " : "");
676
677 r = sym_fido_dev_make_cred(d, c, NULL);
678 if (r == FIDO_ERR_PIN_REQUIRED) {
679
680 if (!has_client_pin)
681 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
682 "Token asks for PIN but doesn't advertise 'clientPin' feature.");
683
684 for (;;) {
685 _cleanup_(strv_free_erasep) char **pin = NULL;
686 char **i;
687
688 r = ask_password_auto("Please enter security token PIN:", askpw_icon_name, NULL, "fido2-pin", "fido2-pin", USEC_INFINITY, 0, &pin);
689 if (r < 0)
690 return log_error_errno(r, "Failed to acquire user PIN: %m");
691
692 r = FIDO_ERR_PIN_INVALID;
693 STRV_FOREACH(i, pin) {
694 if (isempty(*i)) {
695 log_notice("PIN may not be empty.");
696 continue;
697 }
698
699 r = sym_fido_dev_make_cred(d, c, *i);
700 if (r == FIDO_OK) {
701 used_pin = strdup(*i);
702 if (!used_pin)
703 return log_oom();
704 break;
705 }
706 if (r != FIDO_ERR_PIN_INVALID)
707 break;
708 }
709
710 if (r != FIDO_ERR_PIN_INVALID)
711 break;
712
713 log_notice("PIN incorrect, please try again.");
714 }
715 }
716 if (r == FIDO_ERR_PIN_AUTH_BLOCKED)
717 return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
718 "Token PIN is currently blocked, please remove and reinsert token.");
719 #ifdef FIDO_ERR_UV_BLOCKED
720 if (r == FIDO_ERR_UV_BLOCKED)
721 return log_notice_errno(SYNTHETIC_ERRNO(EPERM),
722 "Token verification is currently blocked, please remove and reinsert token.");
723 #endif
724 if (r == FIDO_ERR_ACTION_TIMEOUT)
725 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
726 "Token action timeout. (User didn't interact with token quickly enough.)");
727 if (r != FIDO_OK)
728 return log_error_errno(SYNTHETIC_ERRNO(EIO),
729 "Failed to generate FIDO2 credential: %s", sym_fido_strerr(r));
730
731 cid = sym_fido_cred_id_ptr(c);
732 if (!cid)
733 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to get FIDO2 credential ID.");
734
735 cid_size = sym_fido_cred_id_len(c);
736
737 a = sym_fido_assert_new();
738 if (!a)
739 return log_oom();
740
741 r = sym_fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
742 if (r != FIDO_OK)
743 return log_error_errno(SYNTHETIC_ERRNO(EIO),
744 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r));
745
746 r = sym_fido_assert_set_hmac_salt(a, salt, FIDO2_SALT_SIZE);
747 if (r != FIDO_OK)
748 return log_error_errno(SYNTHETIC_ERRNO(EIO),
749 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
750
751 r = sym_fido_assert_set_rp(a, rp_id);
752 if (r != FIDO_OK)
753 return log_error_errno(SYNTHETIC_ERRNO(EIO),
754 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
755
756 r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
757 if (r != FIDO_OK)
758 return log_error_errno(SYNTHETIC_ERRNO(EIO),
759 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
760
761 r = sym_fido_assert_allow_cred(a, cid, cid_size);
762 if (r != FIDO_OK)
763 return log_error_errno(SYNTHETIC_ERRNO(EIO),
764 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
765
766 log_info("Generating secret key on FIDO2 security token.");
767
768 if (has_up) {
769 r = sym_fido_assert_set_up(a, FLAGS_SET(lock_with, FIDO2ENROLL_UP) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
770 if (r != FIDO_OK)
771 return log_error_errno(SYNTHETIC_ERRNO(EIO),
772 "Failed to %s FIDO2 user presence test: %s",
773 enable_disable(FLAGS_SET(lock_with, FIDO2ENROLL_UP)),
774 sym_fido_strerr(r));
775
776 if (FLAGS_SET(lock_with, FIDO2ENROLL_UP))
777 log_notice("%s%sIn order to allow secret key generation, please confirm presence on security token.",
778 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
779 emoji_enabled() ? " " : "");
780 }
781
782 if (has_uv) {
783 r = sym_fido_assert_set_uv(a, FLAGS_SET(lock_with, FIDO2ENROLL_UV) ? FIDO_OPT_TRUE : FIDO_OPT_FALSE);
784 if (r != FIDO_OK)
785 return log_error_errno(SYNTHETIC_ERRNO(EIO),
786 "Failed to %s FIDO user verification: %s",
787 enable_disable(FLAGS_SET(lock_with, FIDO2ENROLL_UV)),
788 sym_fido_strerr(r));
789
790 if (FLAGS_SET(lock_with, FIDO2ENROLL_UV))
791 log_notice("%s%sIn order to allow secret key generation, please verify user on security token.",
792 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
793 emoji_enabled() ? " " : "");
794 }
795
796 for (;;) {
797 bool retry_with_up = false, retry_with_pin = false;
798
799 r = sym_fido_dev_get_assert(d, a, FLAGS_SET(lock_with, FIDO2ENROLL_PIN) ? used_pin : NULL);
800
801 switch (r) {
802
803 case FIDO_ERR_UP_REQUIRED:
804 /* If the token asks for "up" when we turn off, then this might be a feature that
805 * isn't optional. Let's enable it */
806
807 if (!has_up)
808 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
809 "Token asks for user presence test but doesn't advertise 'up' feature.");
810
811 if (FLAGS_SET(lock_with, FIDO2ENROLL_UP))
812 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
813 "Token asks for user presence test but was already enabled.");
814
815 log_notice("%s%sLocking without user presence test requested, but FIDO2 device %s requires it, enabling.",
816 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
817 emoji_enabled() ? " " : "",
818 device);
819
820 retry_with_up = true;
821 break;
822
823 case FIDO_ERR_UNSUPPORTED_OPTION:
824 /* AuthenTrend ATKey.Pro says it supports "up", but if we disable it it will fail
825 * with FIDO_ERR_UNSUPPORTED_OPTION, probably because it isn't actually
826 * optional. Let's see if turning it on works. This is very similar to the
827 * FIDO_ERR_UP_REQUIRED case, but since the error is so vague we implement it
828 * slightly more defensively. */
829
830 if (has_up && !FLAGS_SET(lock_with, FIDO2ENROLL_UP)) {
831 log_notice("%s%sGot unsupported option error when when user presence test is turned off. Trying with user presence test turned on.",
832 emoji_enabled() ? special_glyph(SPECIAL_GLYPH_TOUCH) : "",
833 emoji_enabled() ? " " : "");
834 retry_with_up = true;
835 }
836
837 break;
838
839 case FIDO_ERR_PIN_REQUIRED:
840 if (!has_client_pin)
841 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
842 "Token asks for client PIN check but doesn't advertise 'clientPin' feature.");
843
844 if (FLAGS_SET(lock_with, FIDO2ENROLL_PIN))
845 return log_error_errno(SYNTHETIC_ERRNO(EINVAL),
846 "Token asks for user client PIN check but was already enabled.");
847
848 log_debug("Token requires PIN for assertion, enabling.");
849 retry_with_pin = true;
850 break;
851
852 default:
853 break;
854 }
855
856 if (!retry_with_up && !retry_with_pin)
857 break;
858
859 if (retry_with_up) {
860 r = sym_fido_assert_set_up(a, FIDO_OPT_TRUE);
861 if (r != FIDO_OK)
862 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enable FIDO2 user presence test: %s", sym_fido_strerr(r));
863
864 lock_with |= FIDO2ENROLL_UP;
865 }
866
867 if (retry_with_pin)
868 lock_with |= FIDO2ENROLL_PIN;
869 }
870
871 if (r == FIDO_ERR_ACTION_TIMEOUT)
872 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
873 "Token action timeout. (User didn't interact with token quickly enough.)");
874 if (r != FIDO_OK)
875 return log_error_errno(SYNTHETIC_ERRNO(EIO),
876 "Failed to ask token for assertion: %s", sym_fido_strerr(r));
877
878 secret = sym_fido_assert_hmac_secret_ptr(a, 0);
879 if (!secret)
880 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
881
882 secret_size = sym_fido_assert_hmac_secret_len(a, 0);
883
884 secret_copy = memdup(secret, secret_size);
885 if (!secret_copy)
886 return log_oom();
887
888 cid_copy = memdup(cid, cid_size);
889 if (!cid_copy)
890 return log_oom();
891
892 *ret_cid = TAKE_PTR(cid_copy);
893 *ret_cid_size = cid_size;
894 *ret_salt = TAKE_PTR(salt);
895 *ret_salt_size = FIDO2_SALT_SIZE;
896 *ret_secret = TAKE_PTR(secret_copy);
897 *ret_secret_size = secret_size;
898
899 if (ret_usedpin)
900 *ret_usedpin = TAKE_PTR(used_pin);
901
902 if (ret_locked_with)
903 *ret_locked_with = lock_with;
904
905 return 0;
906 }
907 #endif
908
909 #if HAVE_LIBFIDO2
910 static int check_device_is_fido2_with_hmac_secret(const char *path) {
911 _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
912 int r;
913
914 d = sym_fido_dev_new();
915 if (!d)
916 return log_oom();
917
918 r = sym_fido_dev_open(d, path);
919 if (r != FIDO_OK)
920 return log_error_errno(SYNTHETIC_ERRNO(EIO),
921 "Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
922
923 r = verify_features(d, path, LOG_DEBUG, NULL, NULL, NULL, NULL);
924 if (r == -ENODEV) /* Not a FIDO2 device, or not implementing 'hmac-secret' */
925 return false;
926 if (r < 0)
927 return r;
928
929 return true;
930 }
931 #endif
932
933 int fido2_list_devices(void) {
934 #if HAVE_LIBFIDO2
935 _cleanup_(table_unrefp) Table *t = NULL;
936 size_t allocated = 64, found = 0;
937 fido_dev_info_t *di = NULL;
938 int r;
939
940 r = dlopen_libfido2();
941 if (r < 0)
942 return log_error_errno(r, "FIDO2 token support is not installed.");
943
944 di = sym_fido_dev_info_new(allocated);
945 if (!di)
946 return log_oom();
947
948 r = sym_fido_dev_info_manifest(di, allocated, &found);
949 if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
950 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
951 log_info("No FIDO2 devices found.");
952 r = 0;
953 goto finish;
954 }
955 if (r != FIDO_OK) {
956 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
957 goto finish;
958 }
959
960 t = table_new("path", "manufacturer", "product");
961 if (!t) {
962 r = log_oom();
963 goto finish;
964 }
965
966 for (size_t i = 0; i < found; i++) {
967 const fido_dev_info_t *entry;
968
969 entry = sym_fido_dev_info_ptr(di, i);
970 if (!entry) {
971 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
972 "Failed to get device information for FIDO device %zu.", i);
973 goto finish;
974 }
975
976 r = check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry));
977 if (r < 0)
978 goto finish;
979 if (!r)
980 continue;
981
982 r = table_add_many(
983 t,
984 TABLE_PATH, sym_fido_dev_info_path(entry),
985 TABLE_STRING, sym_fido_dev_info_manufacturer_string(entry),
986 TABLE_STRING, sym_fido_dev_info_product_string(entry));
987 if (r < 0) {
988 table_log_add_error(r);
989 goto finish;
990 }
991 }
992
993 r = table_print(t, stdout);
994 if (r < 0) {
995 log_error_errno(r, "Failed to show device table: %m");
996 goto finish;
997 }
998
999 r = 0;
1000
1001 finish:
1002 sym_fido_dev_info_free(&di, allocated);
1003 return r;
1004 #else
1005 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1006 "FIDO2 tokens not supported on this build.");
1007 #endif
1008 }
1009
1010 int fido2_find_device_auto(char **ret) {
1011 #if HAVE_LIBFIDO2
1012 _cleanup_free_ char *copy = NULL;
1013 size_t di_size = 64, found = 0;
1014 const fido_dev_info_t *entry;
1015 fido_dev_info_t *di = NULL;
1016 const char *path;
1017 int r;
1018
1019 r = dlopen_libfido2();
1020 if (r < 0)
1021 return log_error_errno(r, "FIDO2 token support is not installed.");
1022
1023 di = sym_fido_dev_info_new(di_size);
1024 if (!di)
1025 return log_oom();
1026
1027 r = sym_fido_dev_info_manifest(di, di_size, &found);
1028 if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
1029 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1030 r = log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No FIDO devices found.");
1031 goto finish;
1032 }
1033 if (r != FIDO_OK) {
1034 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO devices: %s", sym_fido_strerr(r));
1035 goto finish;
1036 }
1037 if (found > 1) {
1038 r = log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "More than one FIDO device found.");
1039 goto finish;
1040 }
1041
1042 entry = sym_fido_dev_info_ptr(di, 0);
1043 if (!entry) {
1044 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
1045 "Failed to get device information for FIDO device 0.");
1046 goto finish;
1047 }
1048
1049 r = check_device_is_fido2_with_hmac_secret(sym_fido_dev_info_path(entry));
1050 if (r < 0)
1051 goto finish;
1052 if (!r) {
1053 r = log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "FIDO device discovered does not implement FIDO2 with 'hmac-secret' extension.");
1054 goto finish;
1055 }
1056
1057 path = sym_fido_dev_info_path(entry);
1058 if (!path) {
1059 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
1060 "Failed to query FIDO device path.");
1061 goto finish;
1062 }
1063
1064 copy = strdup(path);
1065 if (!copy) {
1066 r = log_oom();
1067 goto finish;
1068 }
1069
1070 *ret = TAKE_PTR(copy);
1071 r = 0;
1072
1073 finish:
1074 sym_fido_dev_info_free(&di, di_size);
1075 return r;
1076 #else
1077 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1078 "FIDO2 tokens not supported on this build.");
1079 #endif
1080 }
1081
1082 int fido2_have_device(const char *device) {
1083 #if HAVE_LIBFIDO2
1084 size_t allocated = 64, found = 0;
1085 fido_dev_info_t *di = NULL;
1086 int r;
1087
1088 /* Return == 0 if not devices are found, > 0 if at least one is found */
1089
1090 r = dlopen_libfido2();
1091 if (r < 0)
1092 return log_error_errno(r, "FIDO2 support is not installed.");
1093
1094 if (device) {
1095 if (access(device, F_OK) < 0) {
1096 if (errno == ENOENT)
1097 return 0;
1098
1099 return log_error_errno(errno, "Failed to determine whether device '%s' exists: %m", device);
1100 }
1101
1102 return 1;
1103 }
1104
1105 di = sym_fido_dev_info_new(allocated);
1106 if (!di)
1107 return log_oom();
1108
1109 r = sym_fido_dev_info_manifest(di, allocated, &found);
1110 if (r == FIDO_ERR_INTERNAL) {
1111 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
1112 r = 0;
1113 goto finish;
1114 }
1115 if (r != FIDO_OK) {
1116 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
1117 goto finish;
1118 }
1119
1120 r = found;
1121
1122 finish:
1123 sym_fido_dev_info_free(&di, allocated);
1124 return r;
1125 #else
1126 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
1127 "FIDO2 tokens not supported on this build.");
1128 #endif
1129 }