]>
Commit | Line | Data |
---|---|---|
351716e1 OK |
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
2 | ||
3 | #include <errno.h> | |
4 | #include <libcryptsetup.h> | |
5 | #include <string.h> | |
6 | ||
7 | #include "cryptsetup-token.h" | |
8 | #include "cryptsetup-token-util.h" | |
9 | #include "hexdecoct.h" | |
10 | #include "json.h" | |
11 | #include "luks2-fido2.h" | |
12 | #include "memory-util.h" | |
13 | #include "version.h" | |
14 | ||
15 | #define TOKEN_NAME "systemd-fido2" | |
16 | #define TOKEN_VERSION_MAJOR "1" | |
17 | #define TOKEN_VERSION_MINOR "0" | |
18 | ||
19 | /* for libcryptsetup debug purpose */ | |
20 | _public_ const char *cryptsetup_token_version(void) { | |
21 | return TOKEN_VERSION_MAJOR "." TOKEN_VERSION_MINOR " systemd-v" STRINGIFY(PROJECT_VERSION) " (" GIT_VERSION ")"; | |
22 | } | |
23 | ||
24 | _public_ int cryptsetup_token_open_pin( | |
25 | struct crypt_device *cd, /* is always LUKS2 context */ | |
26 | int token /* is always >= 0 */, | |
27 | const char *pin, | |
28 | size_t pin_size, | |
29 | char **password, /* freed by cryptsetup_token_buffer_free */ | |
30 | size_t *password_len, | |
31 | void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) { | |
32 | ||
33 | int r; | |
34 | const char *json; | |
35 | _cleanup_(erase_and_freep) char *pin_string = NULL; | |
36 | ||
37 | assert(!pin || pin_size); | |
38 | assert(token >= 0); | |
39 | ||
40 | /* This must not fail at this moment (internal error) */ | |
41 | r = crypt_token_json_get(cd, token, &json); | |
dfe7cfe4 FS |
42 | /* Use assert_se() here to avoid emitting warning with -DNDEBUG */ |
43 | assert_se(token == r); | |
351716e1 OK |
44 | assert(json); |
45 | ||
89db4755 JW |
46 | r = crypt_normalize_pin(pin, pin_size, &pin_string); |
47 | if (r < 0) | |
cee60fc3 | 48 | return crypt_log_debug_errno(cd, r, "Cannot normalize PIN: %m"); |
351716e1 | 49 | |
89db4755 | 50 | return acquire_luks2_key(cd, json, (const char *)usrptr, pin_string, password, password_len); |
351716e1 OK |
51 | } |
52 | ||
53 | /* | |
54 | * This function is called from within following libcryptsetup calls | |
55 | * provided conditions further below are met: | |
56 | * | |
57 | * crypt_activate_by_token(), crypt_activate_by_token_type(type == 'systemd-fido2'): | |
58 | * | |
59 | * - token is assigned to at least one luks2 keyslot eligible to activate LUKS2 device | |
60 | * (alternatively: name is set to null, flags contains CRYPT_ACTIVATE_ALLOW_UNBOUND_KEY | |
61 | * and token is assigned to at least single keyslot). | |
62 | * | |
d4e30ad1 | 63 | * - if plugin defines validate function (see cryptsetup_token_validate below) it must have |
351716e1 OK |
64 | * passed the check (aka return 0) |
65 | */ | |
66 | _public_ int cryptsetup_token_open( | |
67 | struct crypt_device *cd, /* is always LUKS2 context */ | |
68 | int token /* is always >= 0 */, | |
69 | char **password, /* freed by cryptsetup_token_buffer_free */ | |
70 | size_t *password_len, | |
71 | void *usrptr /* plugin defined parameter passed to crypt_activate_by_token*() API */) { | |
72 | ||
73 | return cryptsetup_token_open_pin(cd, token, NULL, 0, password, password_len, usrptr); | |
74 | } | |
75 | ||
76 | /* | |
77 | * libcryptsetup callback for memory deallocation of 'password' parameter passed in | |
78 | * any crypt_token_open_* plugin function | |
79 | */ | |
80 | _public_ void cryptsetup_token_buffer_free(void *buffer, size_t buffer_len) { | |
81 | erase_and_free(buffer); | |
82 | } | |
83 | ||
84 | /* | |
85 | * prints systemd-fido2 token content in crypt_dump(). | |
86 | * 'type' and 'keyslots' fields are printed by libcryptsetup | |
87 | */ | |
88 | _public_ void cryptsetup_token_dump( | |
89 | struct crypt_device *cd /* is always LUKS2 context */, | |
90 | const char *json /* validated 'systemd-tpm2' token if cryptsetup_token_validate is defined */) { | |
91 | ||
92 | int r; | |
93 | Fido2EnrollFlags required; | |
94 | size_t cid_size, salt_size; | |
95 | const char *client_pin_req_str, *up_req_str, *uv_req_str; | |
96 | _cleanup_free_ void *cid = NULL, *salt = NULL; | |
97 | _cleanup_free_ char *rp_id = NULL, *cid_str = NULL, *salt_str = NULL; | |
98 | ||
99 | assert(json); | |
100 | ||
101 | r = parse_luks2_fido2_data(cd, json, &rp_id, &salt, &salt_size, &cid, &cid_size, &required); | |
102 | if (r < 0) | |
103 | return (void) crypt_log_debug_errno(cd, r, "Failed to parse " TOKEN_NAME " metadata: %m."); | |
104 | ||
105 | r = crypt_dump_buffer_to_hex_string(cid, cid_size, &cid_str); | |
106 | if (r < 0) | |
cee60fc3 | 107 | return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m"); |
351716e1 OK |
108 | |
109 | r = crypt_dump_buffer_to_hex_string(salt, salt_size, &salt_str); | |
110 | if (r < 0) | |
cee60fc3 | 111 | return (void) crypt_log_debug_errno(cd, r, "Cannot dump " TOKEN_NAME " content: %m"); |
351716e1 OK |
112 | |
113 | if (required & FIDO2ENROLL_PIN) | |
114 | client_pin_req_str = "true"; | |
115 | else if (required & FIDO2ENROLL_PIN_IF_NEEDED) | |
116 | client_pin_req_str = NULL; | |
117 | else | |
118 | client_pin_req_str = "false"; | |
119 | ||
120 | if (required & FIDO2ENROLL_UP) | |
121 | up_req_str = "true"; | |
122 | else if (required & FIDO2ENROLL_UP_IF_NEEDED) | |
123 | up_req_str = NULL; | |
124 | else | |
125 | up_req_str = "false"; | |
126 | ||
127 | if (required & FIDO2ENROLL_UV) | |
128 | uv_req_str = "true"; | |
129 | else if (required & FIDO2ENROLL_UV_OMIT) | |
130 | uv_req_str = NULL; | |
131 | else | |
132 | uv_req_str = "false"; | |
133 | ||
134 | crypt_log(cd, "\tfido2-credential:" CRYPT_DUMP_LINE_SEP "%s\n", cid_str); | |
135 | crypt_log(cd, "\tfido2-salt: %s\n", salt_str); | |
136 | ||
137 | /* optional fields */ | |
138 | if (rp_id) | |
139 | crypt_log(cd, "\tfido2-rp: %s\n", rp_id); | |
140 | if (client_pin_req_str) | |
141 | crypt_log(cd, "\tfido2-clientPin-required:" CRYPT_DUMP_LINE_SEP "%s\n", | |
142 | client_pin_req_str); | |
143 | if (up_req_str) | |
144 | crypt_log(cd, "\tfido2-up-required:" CRYPT_DUMP_LINE_SEP "%s\n", up_req_str); | |
145 | if (uv_req_str) | |
146 | crypt_log(cd, "\tfido2-uv-required:" CRYPT_DUMP_LINE_SEP "%s\n", uv_req_str); | |
147 | } | |
148 | ||
149 | /* | |
150 | * Note: | |
151 | * If plugin is available in library path, it's called in before following libcryptsetup calls: | |
152 | * | |
153 | * crypt_token_json_set, crypt_dump, any crypt_activate_by_token_* flavour | |
154 | */ | |
155 | _public_ int cryptsetup_token_validate( | |
156 | struct crypt_device *cd, /* is always LUKS2 context */ | |
157 | const char *json /* contains valid 'type' and 'keyslots' fields. 'type' is 'systemd-tpm2' */) { | |
158 | ||
159 | int r; | |
160 | JsonVariant *w; | |
161 | _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; | |
162 | ||
163 | assert(json); | |
164 | ||
165 | r = json_parse(json, 0, &v, NULL, NULL); | |
166 | if (r < 0) | |
167 | return crypt_log_debug_errno(cd, r, "Could not parse " TOKEN_NAME " json object: %m."); | |
168 | ||
169 | w = json_variant_by_key(v, "fido2-credential"); | |
170 | if (!w || !json_variant_is_string(w)) { | |
171 | crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-credential' field."); | |
172 | return 1; | |
173 | } | |
174 | ||
bdd2036e | 175 | r = unbase64mem(json_variant_string(w), NULL, NULL); |
351716e1 OK |
176 | if (r < 0) |
177 | return crypt_log_debug_errno(cd, r, "Invalid base64 data in 'fido2-credential' field: %m"); | |
178 | ||
179 | w = json_variant_by_key(v, "fido2-salt"); | |
180 | if (!w || !json_variant_is_string(w)) { | |
181 | crypt_log_debug(cd, "FIDO2 token data lacks 'fido2-salt' field."); | |
182 | return 1; | |
183 | } | |
184 | ||
bdd2036e | 185 | r = unbase64mem(json_variant_string(w), NULL, NULL); |
351716e1 OK |
186 | if (r < 0) |
187 | return crypt_log_debug_errno(cd, r, "Failed to decode base64 encoded salt: %m."); | |
188 | ||
189 | /* The "rp" field is optional. */ | |
190 | w = json_variant_by_key(v, "fido2-rp"); | |
191 | if (w && !json_variant_is_string(w)) { | |
192 | crypt_log_debug(cd, "FIDO2 token data's 'fido2-rp' field is not a string."); | |
193 | return 1; | |
194 | } | |
195 | ||
196 | /* The "fido2-clientPin-required" field is optional. */ | |
197 | w = json_variant_by_key(v, "fido2-clientPin-required"); | |
198 | if (w && !json_variant_is_boolean(w)) { | |
199 | crypt_log_debug(cd, "FIDO2 token data's 'fido2-clientPin-required' field is not a boolean."); | |
200 | return 1; | |
201 | } | |
202 | ||
203 | /* The "fido2-up-required" field is optional. */ | |
204 | w = json_variant_by_key(v, "fido2-up-required"); | |
205 | if (w && !json_variant_is_boolean(w)) { | |
206 | crypt_log_debug(cd, "FIDO2 token data's 'fido2-up-required' field is not a boolean."); | |
207 | return 1; | |
208 | } | |
209 | ||
210 | /* The "fido2-uv-required" field is optional. */ | |
211 | w = json_variant_by_key(v, "fido2-uv-required"); | |
212 | if (w && !json_variant_is_boolean(w)) { | |
213 | crypt_log_debug(cd, "FIDO2 token data's 'fido2-uv-required' field is not a boolean."); | |
214 | return 1; | |
215 | } | |
216 | ||
217 | return 0; | |
218 | } |