]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/home/homework-fido2.c
homed: turn libfido2 into a dlopen() type dependency
[thirdparty/systemd.git] / src / home / homework-fido2.c
CommitLineData
db9ecf05 1/* SPDX-License-Identifier: LGPL-2.1-or-later */
7b78db28
LP
2
3#include <fido.h>
4
5#include "hexdecoct.h"
6#include "homework-fido2.h"
69cb2896 7#include "libfido2-util.h"
7b78db28
LP
8#include "strv.h"
9
10static int fido2_use_specific_token(
11 const char *path,
12 UserRecord *h,
13 UserRecord *secret,
14 const Fido2HmacSalt *salt,
15 char **ret) {
16
69cb2896
LP
17 _cleanup_(fido_cbor_info_free_wrapper) fido_cbor_info_t *di = NULL;
18 _cleanup_(fido_assert_free_wrapper) fido_assert_t *a = NULL;
19 _cleanup_(fido_dev_free_wrapper) fido_dev_t *d = NULL;
7b78db28
LP
20 bool found_extension = false;
21 size_t n, hmac_size;
22 const void *hmac;
23 char **e;
24 int r;
25
69cb2896 26 d = sym_fido_dev_new();
7b78db28
LP
27 if (!d)
28 return log_oom();
29
69cb2896 30 r = sym_fido_dev_open(d, path);
7b78db28
LP
31 if (r != FIDO_OK)
32 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 33 "Failed to open FIDO2 device %s: %s", path, sym_fido_strerr(r));
7b78db28 34
69cb2896 35 if (!sym_fido_dev_is_fido2(d))
7b78db28
LP
36 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
37 "Specified device %s is not a FIDO2 device.", path);
38
69cb2896 39 di = sym_fido_cbor_info_new();
7b78db28
LP
40 if (!di)
41 return log_oom();
42
69cb2896 43 r = sym_fido_dev_get_cbor_info(d, di);
7b78db28
LP
44 if (r != FIDO_OK)
45 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 46 "Failed to get CBOR device info for %s: %s", path, sym_fido_strerr(r));
7b78db28 47
69cb2896
LP
48 e = sym_fido_cbor_info_extensions_ptr(di);
49 n = sym_fido_cbor_info_extensions_len(di);
7b78db28
LP
50
51 for (size_t i = 0; i < n; i++)
52 if (streq(e[i], "hmac-secret")) {
53 found_extension = true;
54 break;
55 }
56
57 if (!found_extension)
58 return log_error_errno(SYNTHETIC_ERRNO(ENODEV),
59 "Specified device %s is a FIDO2 device, but does not support the required HMAC-SECRET extension.", path);
60
69cb2896 61 a = sym_fido_assert_new();
7b78db28
LP
62 if (!a)
63 return log_oom();
64
69cb2896 65 r = sym_fido_assert_set_extensions(a, FIDO_EXT_HMAC_SECRET);
7b78db28
LP
66 if (r != FIDO_OK)
67 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 68 "Failed to enable HMAC-SECRET extension on FIDO2 assertion: %s", sym_fido_strerr(r));
7b78db28 69
69cb2896 70 r = sym_fido_assert_set_hmac_salt(a, salt->salt, salt->salt_size);
7b78db28
LP
71 if (r != FIDO_OK)
72 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 73 "Failed to set salt on FIDO2 assertion: %s", sym_fido_strerr(r));
7b78db28 74
69cb2896 75 r = sym_fido_assert_set_rp(a, "io.systemd.home");
7b78db28
LP
76 if (r != FIDO_OK)
77 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 78 "Failed to set FIDO2 assertion ID: %s", sym_fido_strerr(r));
7b78db28 79
69cb2896 80 r = sym_fido_assert_set_clientdata_hash(a, (const unsigned char[32]) {}, 32);
7b78db28
LP
81 if (r != FIDO_OK)
82 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 83 "Failed to set FIDO2 assertion client data hash: %s", sym_fido_strerr(r));
7b78db28 84
69cb2896 85 r = sym_fido_assert_allow_cred(a, salt->credential.id, salt->credential.size);
7b78db28
LP
86 if (r != FIDO_OK)
87 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 88 "Failed to add FIDO2 assertion credential ID: %s", sym_fido_strerr(r));
7b78db28 89
69cb2896 90 r = sym_fido_assert_set_up(a, h->fido2_user_presence_permitted <= 0 ? FIDO_OPT_FALSE : FIDO_OPT_TRUE);
7b78db28
LP
91 if (r != FIDO_OK)
92 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 93 "Failed to set FIDO2 assertion user presence: %s", sym_fido_strerr(r));
7b78db28
LP
94
95 log_info("Asking FIDO2 token for authentication.");
96
69cb2896 97 r = sym_fido_dev_get_assert(d, a, NULL); /* try without pin first */
7b78db28
LP
98 if (r == FIDO_ERR_PIN_REQUIRED) {
99 char **i;
100
101 /* OK, we needed a pin, try with all pins in turn */
102 STRV_FOREACH(i, secret->token_pin) {
69cb2896 103 r = sym_fido_dev_get_assert(d, a, *i);
7b78db28
LP
104 if (r != FIDO_ERR_PIN_INVALID)
105 break;
106 }
107 }
108
109 switch (r) {
110 case FIDO_OK:
111 break;
112 case FIDO_ERR_NO_CREDENTIALS:
113 return log_error_errno(SYNTHETIC_ERRNO(EBADSLT),
114 "Wrong security token; needed credentials not present on token.");
115 case FIDO_ERR_PIN_REQUIRED:
116 return log_error_errno(SYNTHETIC_ERRNO(ENOANO),
117 "Security token requires PIN.");
118 case FIDO_ERR_PIN_AUTH_BLOCKED:
119 return log_error_errno(SYNTHETIC_ERRNO(EOWNERDEAD),
120 "PIN of security token is blocked, please remove/reinsert token.");
121 case FIDO_ERR_PIN_INVALID:
122 return log_error_errno(SYNTHETIC_ERRNO(ENOLCK),
123 "PIN of security token incorrect.");
124 case FIDO_ERR_UP_REQUIRED:
125 return log_error_errno(SYNTHETIC_ERRNO(EMEDIUMTYPE),
126 "User presence required.");
127 case FIDO_ERR_ACTION_TIMEOUT:
128 return log_error_errno(SYNTHETIC_ERRNO(ENOSTR),
129 "Token action timeout. (User didn't interact with token quickly enough.)");
130 default:
131 return log_error_errno(SYNTHETIC_ERRNO(EIO),
69cb2896 132 "Failed to ask token for assertion: %s", sym_fido_strerr(r));
7b78db28
LP
133 }
134
69cb2896 135 hmac = sym_fido_assert_hmac_secret_ptr(a, 0);
7b78db28
LP
136 if (!hmac)
137 return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to retrieve HMAC secret.");
138
69cb2896 139 hmac_size = sym_fido_assert_hmac_secret_len(a, 0);
7b78db28
LP
140
141 r = base64mem(hmac, hmac_size, ret);
142 if (r < 0)
143 return log_error_errno(r, "Failed to base64 encode HMAC secret: %m");
144
145 return 0;
146}
147
148int fido2_use_token(UserRecord *h, UserRecord *secret, const Fido2HmacSalt *salt, char **ret) {
149 size_t allocated = 64, found = 0;
150 fido_dev_info_t *di = NULL;
151 int r;
152
69cb2896
LP
153 r = dlopen_libfido2();
154 if (r < 0)
155 return log_error_errno(r, "FIDO2 support is not installed.");
156
157 di = sym_fido_dev_info_new(allocated);
7b78db28
LP
158 if (!di)
159 return log_oom();
160
69cb2896 161 r = sym_fido_dev_info_manifest(di, allocated, &found);
7b78db28
LP
162 if (r == FIDO_ERR_INTERNAL) {
163 /* The library returns FIDO_ERR_INTERNAL when no devices are found. I wish it wouldn't. */
164 r = log_debug_errno(SYNTHETIC_ERRNO(EAGAIN), "Got FIDO_ERR_INTERNAL, assuming no devices.");
165 goto finish;
166 }
167 if (r != FIDO_OK) {
69cb2896 168 r = log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to enumerate FIDO2 devices: %s", sym_fido_strerr(r));
7b78db28
LP
169 goto finish;
170 }
171
172 for (size_t i = 0; i < found; i++) {
173 const fido_dev_info_t *entry;
174 const char *path;
175
69cb2896 176 entry = sym_fido_dev_info_ptr(di, i);
7b78db28
LP
177 if (!entry) {
178 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
179 "Failed to get device information for FIDO device %zu.", i);
180 goto finish;
181 }
182
69cb2896 183 path = sym_fido_dev_info_path(entry);
7b78db28
LP
184 if (!path) {
185 r = log_error_errno(SYNTHETIC_ERRNO(EIO),
186 "Failed to query FIDO device path.");
187 goto finish;
188 }
189
190 r = fido2_use_specific_token(path, h, secret, salt, ret);
191 if (!IN_SET(r,
192 -EBADSLT, /* device doesn't understand our credential hash */
193 -ENODEV /* device is not a FIDO2 device with HMAC-SECRET */))
194 goto finish;
195 }
196
197 r = -EAGAIN;
198
199finish:
69cb2896 200 sym_fido_dev_info_free(&di, allocated);
7b78db28
LP
201 return r;
202}