]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/cryptsetup/cryptsetup-tokens/cryptsetup-token-systemd-fido2.c
hexdecoct: make unbase64mem and unhexmem always use SIZE_MAX
[thirdparty/systemd.git] / src / cryptsetup / cryptsetup-tokens / cryptsetup-token-systemd-fido2.c
CommitLineData
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}