1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
3 #include "ask-password-api.h"
4 #include "cryptsetup-fido2.h"
9 #include "libfido2-util.h"
10 #include "parse-util.h"
11 #include "random-util.h"
14 int acquire_fido2_key(
15 const char *volume_name
,
16 const char *friendly_name
,
23 uint64_t key_file_offset
,
27 Fido2EnrollFlags required
,
28 const char *askpw_credential
,
29 AskPasswordFlags askpw_flags
,
30 void **ret_decrypted_key
,
31 size_t *ret_decrypted_key_size
) {
33 _cleanup_(erase_and_freep
) char *envpw
= NULL
;
34 _cleanup_strv_free_erase_
char **pins
= NULL
;
35 _cleanup_free_
void *loaded_salt
= NULL
;
36 bool device_exists
= false;
41 if ((required
& (FIDO2ENROLL_PIN
| FIDO2ENROLL_UP
| FIDO2ENROLL_UV
)) && FLAGS_SET(askpw_flags
, ASK_PASSWORD_HEADLESS
))
42 return log_error_errno(SYNTHETIC_ERRNO(ENOPKG
),
43 "Local verification is required to unlock this volume, but the 'headless' parameter was set.");
45 askpw_flags
|= ASK_PASSWORD_PUSH_CACHE
| ASK_PASSWORD_ACCEPT_CACHED
;
48 assert(key_file
|| key_data
);
52 salt_size
= key_data_size
;
54 _cleanup_free_
char *bindname
= NULL
;
56 /* If we read the salt via AF_UNIX, make this client recognizable */
57 if (asprintf(&bindname
, "@%" PRIx64
"/cryptsetup-fido2/%s", random_u64(), volume_name
) < 0)
60 r
= read_full_file_full(
62 key_file_offset
== 0 ? UINT64_MAX
: key_file_offset
,
63 key_file_size
== 0 ? SIZE_MAX
: key_file_size
,
64 READ_FULL_FILE_CONNECT_SOCKET
,
66 (char**) &loaded_salt
, &salt_size
);
73 r
= getenv_steal_erase("PIN", &envpw
);
75 return log_error_errno(r
, "Failed to acquire password from environment: %m");
77 pins
= strv_new(envpw
);
84 /* Before we inquire for the PIN we'll need, if we never talked to the device, check
85 * if the device actually is plugged in. Otherwise we'll ask for the PIN already when
86 * the device is not plugged in, which is confusing. */
88 r
= fido2_have_device(device
);
91 if (r
== 0) /* no device found, return EAGAIN so that caller will wait/watch udev */
94 device_exists
= true; /* now we know for sure, a device exists, no need to ask again */
97 /* Always make an attempt before asking for PIN.
98 * fido2_use_hmac_hash() will perform a pre-flight check for whether the credential for
99 * can be found on one of the connected devices. This way, we can avoid prompting the user
100 * for a PIN when we are sure that no device can be used. */
101 r
= fido2_use_hmac_hash(
103 rp_id
?: "io.systemd.cryptsetup",
109 ret_decrypted_key_size
);
111 -ENOANO
, /* needs pin */
112 -ENOLCK
)) /* pin incorrect */
115 device_exists
= true; /* that a PIN is needed/wasn't correct means that we managed to
116 * talk to a device */
118 if (FLAGS_SET(askpw_flags
, ASK_PASSWORD_HEADLESS
))
119 return log_error_errno(SYNTHETIC_ERRNO(ENOPKG
), "PIN querying disabled via 'headless' option. Use the '$PIN' environment variable.");
121 static const AskPasswordRequest req
= {
122 .message
= "Please enter security token PIN:",
123 .icon
= "drive-harddisk",
124 .keyring
= "fido2-pin",
125 .credential
= "cryptsetup.fido2-pin",
128 pins
= strv_free_erase(pins
);
129 r
= ask_password_auto(&req
, until
, askpw_flags
, &pins
);
131 return log_error_errno(r
, "Failed to ask for user password: %m");
133 askpw_flags
&= ~ASK_PASSWORD_ACCEPT_CACHED
;
137 int acquire_fido2_key_auto(
138 struct crypt_device
*cd
,
140 const char *friendly_name
,
141 const char *fido2_device
,
143 const char *askpw_credential
,
144 AskPasswordFlags askpw_flags
,
145 void **ret_decrypted_key
,
146 size_t *ret_decrypted_key_size
) {
148 _cleanup_free_
void *cid
= NULL
;
150 int r
, ret
= -ENOENT
;
151 Fido2EnrollFlags required
= 0;
155 assert(ret_decrypted_key
);
156 assert(ret_decrypted_key_size
);
158 /* Loads FIDO2 metadata from LUKS2 JSON token headers. */
160 for (int token
= 0; token
< sym_crypt_token_max(CRYPT_LUKS2
); token
++) {
161 _cleanup_(json_variant_unrefp
) JsonVariant
*v
= NULL
;
163 _cleanup_free_
void *salt
= NULL
;
164 _cleanup_free_
char *rp
= NULL
;
165 size_t salt_size
= 0;
168 r
= cryptsetup_get_token_as_json(cd
, token
, "systemd-fido2", &v
);
169 if (IN_SET(r
, -ENOENT
, -EINVAL
, -EMEDIUMTYPE
))
172 return log_error_errno(r
, "Failed to read JSON token data off disk: %m");
174 ks
= cryptsetup_get_keyslot_from_token(v
);
176 /* Handle parsing errors of the keyslots field gracefully, since it's not 'owned' by
177 * us, but by the LUKS2 spec */
178 log_warning_errno(ks
, "Failed to extract keyslot index from FIDO2 JSON data token %i, skipping: %m", token
);
182 w
= json_variant_by_key(v
, "fido2-credential");
183 if (!w
|| !json_variant_is_string(w
))
184 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
185 "FIDO2 token data lacks 'fido2-credential' field.");
187 r
= unbase64mem(json_variant_string(w
), &cid
, &cid_size
);
189 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
190 "Invalid base64 data in 'fido2-credential' field.");
192 w
= json_variant_by_key(v
, "fido2-salt");
193 if (!w
|| !json_variant_is_string(w
))
194 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
195 "FIDO2 token data lacks 'fido2-salt' field.");
198 assert(salt_size
== 0);
199 r
= unbase64mem(json_variant_string(w
), &salt
, &salt_size
);
201 return log_error_errno(r
, "Failed to decode base64 encoded salt.");
203 w
= json_variant_by_key(v
, "fido2-rp");
205 /* The "rp" field is optional. */
207 if (!json_variant_is_string(w
))
208 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
209 "FIDO2 token data's 'fido2-rp' field is not a string.");
212 rp
= strdup(json_variant_string(w
));
217 w
= json_variant_by_key(v
, "fido2-clientPin-required");
219 /* The "fido2-clientPin-required" field is optional. */
221 if (!json_variant_is_boolean(w
))
222 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
223 "FIDO2 token data's 'fido2-clientPin-required' field is not a boolean.");
225 SET_FLAG(required
, FIDO2ENROLL_PIN
, json_variant_boolean(w
));
227 required
|= FIDO2ENROLL_PIN_IF_NEEDED
; /* compat with 248, where the field was unset */
229 w
= json_variant_by_key(v
, "fido2-up-required");
231 /* The "fido2-up-required" field is optional. */
233 if (!json_variant_is_boolean(w
))
234 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
235 "FIDO2 token data's 'fido2-up-required' field is not a boolean.");
237 SET_FLAG(required
, FIDO2ENROLL_UP
, json_variant_boolean(w
));
239 required
|= FIDO2ENROLL_UP_IF_NEEDED
; /* compat with 248 */
241 w
= json_variant_by_key(v
, "fido2-uv-required");
243 /* The "fido2-uv-required" field is optional. */
245 if (!json_variant_is_boolean(w
))
246 return log_error_errno(SYNTHETIC_ERRNO(EINVAL
),
247 "FIDO2 token data's 'fido2-uv-required' field is not a boolean.");
249 SET_FLAG(required
, FIDO2ENROLL_UV
, json_variant_boolean(w
));
251 required
|= FIDO2ENROLL_UV_OMIT
; /* compat with 248 */
253 ret
= acquire_fido2_key(
259 /* key_file= */ NULL
, /* salt is read from LUKS header instead of key_file */
260 /* key_file_size= */ 0,
261 /* key_file_offset= */ 0,
265 "cryptsetup.fido2-pin",
268 ret_decrypted_key_size
);
274 return log_error_errno(SYNTHETIC_ERRNO(ENXIO
),
275 "No valid FIDO2 token data found.");
277 if (ret
== -EAGAIN
) /* fido2 device does not exist, or UV is blocked; caller will prompt for retry */
278 return log_debug_errno(ret
, "FIDO2 token does not exist, or UV is blocked.");
280 return log_error_errno(ret
, "Failed to unlock LUKS volume with FIDO2 token: %m");
282 log_info("Unlocked volume via automatically discovered security FIDO2 token.");