]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/shared/libfido2-util.c
homed: move fido2 device enumeration logic to shared code
[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 "dlfcn-util.h"
8 #include "format-table.h"
9 #include "locale-util.h"
10 #include "log.h"
11
12 static void *libfido2_dl = NULL;
13
14 int (*sym_fido_assert_allow_cred)(fido_assert_t *, const unsigned char *, size_t) = NULL;
15 void (*sym_fido_assert_free)(fido_assert_t **) = NULL;
16 size_t (*sym_fido_assert_hmac_secret_len)(const fido_assert_t *, size_t) = NULL;
17 const unsigned char* (*sym_fido_assert_hmac_secret_ptr)(const fido_assert_t *, size_t) = NULL;
18 fido_assert_t* (*sym_fido_assert_new)(void) = NULL;
19 int (*sym_fido_assert_set_clientdata_hash)(fido_assert_t *, const unsigned char *, size_t) = NULL;
20 int (*sym_fido_assert_set_extensions)(fido_assert_t *, int) = NULL;
21 int (*sym_fido_assert_set_hmac_salt)(fido_assert_t *, const unsigned char *, size_t) = NULL;
22 int (*sym_fido_assert_set_rp)(fido_assert_t *, const char *) = NULL;
23 int (*sym_fido_assert_set_up)(fido_assert_t *, fido_opt_t) = NULL;
24 size_t (*sym_fido_cbor_info_extensions_len)(const fido_cbor_info_t *) = NULL;
25 char **(*sym_fido_cbor_info_extensions_ptr)(const fido_cbor_info_t *) = NULL;
26 void (*sym_fido_cbor_info_free)(fido_cbor_info_t **) = NULL;
27 fido_cbor_info_t* (*sym_fido_cbor_info_new)(void) = NULL;
28 void (*sym_fido_cred_free)(fido_cred_t **) = NULL;
29 size_t (*sym_fido_cred_id_len)(const fido_cred_t *) = NULL;
30 const unsigned char* (*sym_fido_cred_id_ptr)(const fido_cred_t *) = NULL;
31 fido_cred_t* (*sym_fido_cred_new)(void) = NULL;
32 int (*sym_fido_cred_set_clientdata_hash)(fido_cred_t *, const unsigned char *, size_t) = NULL;
33 int (*sym_fido_cred_set_extensions)(fido_cred_t *, int) = NULL;
34 int (*sym_fido_cred_set_rk)(fido_cred_t *, fido_opt_t) = NULL;
35 int (*sym_fido_cred_set_rp)(fido_cred_t *, const char *, const char *) = NULL;
36 int (*sym_fido_cred_set_type)(fido_cred_t *, int) = NULL;
37 int (*sym_fido_cred_set_user)(fido_cred_t *, const unsigned char *, size_t, const char *, const char *, const char *) = NULL;
38 int (*sym_fido_cred_set_uv)(fido_cred_t *, fido_opt_t) = NULL;
39 void (*sym_fido_dev_free)(fido_dev_t **) = NULL;
40 int (*sym_fido_dev_get_assert)(fido_dev_t *, fido_assert_t *, const char *) = NULL;
41 int (*sym_fido_dev_get_cbor_info)(fido_dev_t *, fido_cbor_info_t *) = NULL;
42 void (*sym_fido_dev_info_free)(fido_dev_info_t **, size_t) = NULL;
43 int (*sym_fido_dev_info_manifest)(fido_dev_info_t *, size_t, size_t *) = NULL;
44 const char* (*sym_fido_dev_info_manufacturer_string)(const fido_dev_info_t *) = NULL;
45 const char* (*sym_fido_dev_info_product_string)(const fido_dev_info_t *) = NULL;
46 fido_dev_info_t* (*sym_fido_dev_info_new)(size_t) = NULL;
47 const char* (*sym_fido_dev_info_path)(const fido_dev_info_t *) = NULL;
48 const fido_dev_info_t* (*sym_fido_dev_info_ptr)(const fido_dev_info_t *, size_t) = NULL;
49 bool (*sym_fido_dev_is_fido2)(const fido_dev_t *) = NULL;
50 int (*sym_fido_dev_make_cred)(fido_dev_t *, fido_cred_t *, const char *) = NULL;
51 fido_dev_t* (*sym_fido_dev_new)(void) = NULL;
52 int (*sym_fido_dev_open)(fido_dev_t *, const char *) = NULL;
53 const char* (*sym_fido_strerr)(int) = NULL;
54
55 int dlopen_libfido2(void) {
56 _cleanup_(dlclosep) void *dl = NULL;
57 int r;
58
59 if (libfido2_dl)
60 return 0; /* Already loaded */
61
62 dl = dlopen("libfido2.so.1", RTLD_LAZY);
63 if (!dl)
64 return log_debug_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
65 "libfido2 support is not installed: %s", dlerror());
66
67 r = dlsym_many_and_warn(
68 dl,
69 LOG_DEBUG,
70 DLSYM_ARG(fido_assert_allow_cred),
71 DLSYM_ARG(fido_assert_free),
72 DLSYM_ARG(fido_assert_hmac_secret_len),
73 DLSYM_ARG(fido_assert_hmac_secret_ptr),
74 DLSYM_ARG(fido_assert_new),
75 DLSYM_ARG(fido_assert_set_clientdata_hash),
76 DLSYM_ARG(fido_assert_set_extensions),
77 DLSYM_ARG(fido_assert_set_hmac_salt),
78 DLSYM_ARG(fido_assert_set_rp),
79 DLSYM_ARG(fido_assert_set_up),
80 DLSYM_ARG(fido_cbor_info_extensions_len),
81 DLSYM_ARG(fido_cbor_info_extensions_ptr),
82 DLSYM_ARG(fido_cbor_info_free),
83 DLSYM_ARG(fido_cbor_info_new),
84 DLSYM_ARG(fido_cred_free),
85 DLSYM_ARG(fido_cred_id_len),
86 DLSYM_ARG(fido_cred_id_ptr),
87 DLSYM_ARG(fido_cred_new),
88 DLSYM_ARG(fido_cred_set_clientdata_hash),
89 DLSYM_ARG(fido_cred_set_extensions),
90 DLSYM_ARG(fido_cred_set_rk),
91 DLSYM_ARG(fido_cred_set_rp),
92 DLSYM_ARG(fido_cred_set_type),
93 DLSYM_ARG(fido_cred_set_user),
94 DLSYM_ARG(fido_cred_set_uv),
95 DLSYM_ARG(fido_dev_free),
96 DLSYM_ARG(fido_dev_get_assert),
97 DLSYM_ARG(fido_dev_get_cbor_info),
98 DLSYM_ARG(fido_dev_info_free),
99 DLSYM_ARG(fido_dev_info_manifest),
100 DLSYM_ARG(fido_dev_info_manufacturer_string),
101 DLSYM_ARG(fido_dev_info_new),
102 DLSYM_ARG(fido_dev_info_path),
103 DLSYM_ARG(fido_dev_info_product_string),
104 DLSYM_ARG(fido_dev_info_ptr),
105 DLSYM_ARG(fido_dev_is_fido2),
106 DLSYM_ARG(fido_dev_make_cred),
107 DLSYM_ARG(fido_dev_new),
108 DLSYM_ARG(fido_dev_open),
109 DLSYM_ARG(fido_strerr),
110 NULL);
111 if (r < 0)
112 return r;
113
114 /* Note that we never release the reference here, because there's no real reason to, after all this
115 * was traditionally a regular shared library dependency which lives forever too. */
116 libfido2_dl = TAKE_PTR(dl);
117 return 1;
118 }
119 #endif
120
121 int fido2_list_devices(void) {
122 #if HAVE_LIBFIDO2
123 _cleanup_(table_unrefp) Table *t = NULL;
124 size_t allocated = 64, found = 0;
125 fido_dev_info_t *di = NULL;
126 int r;
127
128 r = dlopen_libfido2();
129 if (r < 0)
130 return log_error_errno(r, "FIDO2 token support is not installed.");
131
132 di = sym_fido_dev_info_new(allocated);
133 if (!di)
134 return log_oom();
135
136 r = sym_fido_dev_info_manifest(di, allocated, &found);
137 if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
138 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
139 log_info("No FIDO2 devices found.");
140 r = 0;
141 goto finish;
142 }
143 if (r != FIDO_OK) {
144 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
145 goto finish;
146 }
147
148 t = table_new("path", "manufacturer", "product");
149 if (!t) {
150 r = log_oom();
151 goto finish;
152 }
153
154 for (size_t i = 0; i < found; i++) {
155 const fido_dev_info_t *entry;
156
157 entry = sym_fido_dev_info_ptr(di, i);
158 if (!entry) {
159 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
160 "Failed to get device information for FIDO device %zu.", i);
161 goto finish;
162 }
163
164 r = table_add_many(
165 t,
166 TABLE_PATH, sym_fido_dev_info_path(entry),
167 TABLE_STRING, sym_fido_dev_info_manufacturer_string(entry),
168 TABLE_STRING, sym_fido_dev_info_product_string(entry));
169 if (r < 0) {
170 table_log_add_error(r);
171 goto finish;
172 }
173 }
174
175 r = table_print(t, stdout);
176 if (r < 0) {
177 log_error_errno(r, "Failed to show device table: %m");
178 goto finish;
179 }
180
181 r = 0;
182
183 finish:
184 sym_fido_dev_info_free(&di, allocated);
185 return r;
186 #else
187 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
188 "FIDO2 tokens not supported on this build.");
189 #endif
190 }
191
192 int fido2_find_device_auto(char **ret) {
193 #if HAVE_LIBFIDO2
194 _cleanup_free_ char *copy = NULL;
195 size_t di_size = 64, found = 0;
196 const fido_dev_info_t *entry;
197 fido_dev_info_t *di = NULL;
198 const char *path;
199 int r;
200
201 r = dlopen_libfido2();
202 if (r < 0)
203 return log_error_errno(r, "FIDO2 token support is not installed.");
204
205 di = sym_fido_dev_info_new(di_size);
206 if (!di)
207 return log_oom();
208
209 r = sym_fido_dev_info_manifest(di, di_size, &found);
210 if (r == FIDO_ERR_INTERNAL || (r == FIDO_OK && found == 0)) {
211 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
212 r = log_error_errno(SYNTHETIC_ERRNO(ENODEV), "No FIDO devices found.");
213 goto finish;
214 }
215 if (r != FIDO_OK) {
216 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO devices: %s", sym_fido_strerr(r));
217 goto finish;
218 }
219 if (found > 1) {
220 r = log_error_errno(SYNTHETIC_ERRNO(ENOTUNIQ), "More than one FIDO device found.");
221 goto finish;
222 }
223
224 entry = sym_fido_dev_info_ptr(di, 0);
225 if (!entry) {
226 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
227 "Failed to get device information for FIDO device 0.");
228 goto finish;
229 }
230
231 path = sym_fido_dev_info_path(entry);
232 if (!path) {
233 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
234 "Failed to query FIDO device path.");
235 goto finish;
236 }
237
238 copy = strdup(path);
239 if (!copy) {
240 r = log_oom();
241 goto finish;
242 }
243
244 *ret = TAKE_PTR(copy);
245 r = 0;
246
247 finish:
248 sym_fido_dev_info_free(&di, di_size);
249 return r;
250 #else
251 return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP),
252 "FIDO2 tokens not supported on this build.");
253 #endif
254 }