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