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