]>
Commit | Line | Data |
---|---|---|
1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ | |
2 | ||
3 | #include "sd-json.h" | |
4 | ||
5 | #include "errno-util.h" | |
6 | #include "hexdecoct.h" | |
7 | #include "homectl-pkcs11.h" | |
8 | #include "libcrypt-util.h" | |
9 | #include "log.h" | |
10 | #include "openssl-util.h" | |
11 | #include "pkcs11-util.h" | |
12 | #include "string-util.h" | |
13 | #include "strv.h" | |
14 | ||
15 | int identity_add_token_pin(sd_json_variant **v, const char *pin) { | |
16 | _cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL, *l = NULL; | |
17 | _cleanup_strv_free_erase_ char **pins = NULL; | |
18 | int r; | |
19 | ||
20 | assert(v); | |
21 | ||
22 | if (isempty(pin)) | |
23 | return 0; | |
24 | ||
25 | w = sd_json_variant_ref(sd_json_variant_by_key(*v, "secret")); | |
26 | l = sd_json_variant_ref(sd_json_variant_by_key(w, "tokenPin")); | |
27 | ||
28 | r = sd_json_variant_strv(l, &pins); | |
29 | if (r < 0) | |
30 | return log_error_errno(r, "Failed to convert PIN array: %m"); | |
31 | ||
32 | if (strv_contains(pins, pin)) | |
33 | return 0; | |
34 | ||
35 | r = strv_extend(&pins, pin); | |
36 | if (r < 0) | |
37 | return log_oom(); | |
38 | ||
39 | strv_uniq(pins); | |
40 | ||
41 | l = sd_json_variant_unref(l); | |
42 | ||
43 | r = sd_json_variant_new_array_strv(&l, pins); | |
44 | if (r < 0) | |
45 | return log_error_errno(r, "Failed to allocate new PIN array JSON: %m"); | |
46 | ||
47 | sd_json_variant_sensitive(l); | |
48 | ||
49 | r = sd_json_variant_set_field(&w, "tokenPin", l); | |
50 | if (r < 0) | |
51 | return log_error_errno(r, "Failed to update PIN field: %m"); | |
52 | ||
53 | sd_json_variant_sensitive(w); | |
54 | ||
55 | r = sd_json_variant_set_field(v, "secret", w); | |
56 | if (r < 0) | |
57 | return log_error_errno(r, "Failed to update secret object: %m"); | |
58 | ||
59 | return 1; | |
60 | } | |
61 | ||
62 | #if HAVE_P11KIT | |
63 | ||
64 | static int add_pkcs11_token_uri(sd_json_variant **v, const char *uri) { | |
65 | _cleanup_(sd_json_variant_unrefp) sd_json_variant *w = NULL; | |
66 | _cleanup_strv_free_ char **l = NULL; | |
67 | int r; | |
68 | ||
69 | assert(v); | |
70 | assert(uri); | |
71 | ||
72 | w = sd_json_variant_ref(sd_json_variant_by_key(*v, "pkcs11TokenUri")); | |
73 | if (w) { | |
74 | r = sd_json_variant_strv(w, &l); | |
75 | if (r < 0) | |
76 | return log_error_errno(r, "Failed to parse PKCS#11 token list: %m"); | |
77 | ||
78 | if (strv_contains(l, uri)) | |
79 | return 0; | |
80 | } | |
81 | ||
82 | r = strv_extend(&l, uri); | |
83 | if (r < 0) | |
84 | return log_oom(); | |
85 | ||
86 | w = sd_json_variant_unref(w); | |
87 | r = sd_json_variant_new_array_strv(&w, l); | |
88 | if (r < 0) | |
89 | return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m"); | |
90 | ||
91 | r = sd_json_variant_set_field(v, "pkcs11TokenUri", w); | |
92 | if (r < 0) | |
93 | return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m"); | |
94 | ||
95 | return 0; | |
96 | } | |
97 | ||
98 | static int add_pkcs11_encrypted_key( | |
99 | sd_json_variant **v, | |
100 | const char *uri, | |
101 | const void *encrypted_key, size_t encrypted_key_size, | |
102 | const void *decrypted_key, size_t decrypted_key_size) { | |
103 | ||
104 | _cleanup_(sd_json_variant_unrefp) sd_json_variant *l = NULL, *w = NULL, *e = NULL; | |
105 | _cleanup_(erase_and_freep) char *base64_encoded = NULL, *hashed = NULL; | |
106 | ssize_t base64_encoded_size; | |
107 | int r; | |
108 | ||
109 | assert(v); | |
110 | assert(uri); | |
111 | assert(encrypted_key); | |
112 | assert(encrypted_key_size > 0); | |
113 | assert(decrypted_key); | |
114 | assert(decrypted_key_size > 0); | |
115 | ||
116 | /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends | |
117 | * expect a NUL terminated string, and we use a binary key */ | |
118 | base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); | |
119 | if (base64_encoded_size < 0) | |
120 | return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m"); | |
121 | ||
122 | r = hash_password(base64_encoded, &hashed); | |
123 | if (r < 0) | |
124 | return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m"); | |
125 | ||
126 | r = sd_json_buildo(&e, | |
127 | SD_JSON_BUILD_PAIR("uri", SD_JSON_BUILD_STRING(uri)), | |
128 | SD_JSON_BUILD_PAIR("data", SD_JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)), | |
129 | SD_JSON_BUILD_PAIR("hashedPassword", SD_JSON_BUILD_STRING(hashed))); | |
130 | if (r < 0) | |
131 | return log_error_errno(r, "Failed to build encrypted JSON key object: %m"); | |
132 | ||
133 | w = sd_json_variant_ref(sd_json_variant_by_key(*v, "privileged")); | |
134 | l = sd_json_variant_ref(sd_json_variant_by_key(w, "pkcs11EncryptedKey")); | |
135 | ||
136 | r = sd_json_variant_append_array(&l, e); | |
137 | if (r < 0) | |
138 | return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m"); | |
139 | ||
140 | r = sd_json_variant_set_field(&w, "pkcs11EncryptedKey", l); | |
141 | if (r < 0) | |
142 | return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m"); | |
143 | ||
144 | r = sd_json_variant_set_field(v, "privileged", w); | |
145 | if (r < 0) | |
146 | return log_error_errno(r, "Failed to update privileged field: %m"); | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | #endif | |
152 | ||
153 | int identity_add_pkcs11_key_data(sd_json_variant **v, const char *uri) { | |
154 | #if HAVE_P11KIT | |
155 | _cleanup_(erase_and_freep) void *decrypted_key = NULL, *saved_key = NULL; | |
156 | _cleanup_(erase_and_freep) char *pin = NULL; | |
157 | size_t decrypted_key_size, saved_key_size; | |
158 | _cleanup_(EVP_PKEY_freep) EVP_PKEY *pkey = NULL; | |
159 | int r; | |
160 | ||
161 | assert(v); | |
162 | ||
163 | r = pkcs11_acquire_public_key( | |
164 | uri, | |
165 | "home directory operation", | |
166 | "user-home", | |
167 | "home.token-pin", | |
168 | /* askpw_flags= */ 0, | |
169 | &pkey, | |
170 | &pin); | |
171 | if (r < 0) | |
172 | return r; | |
173 | ||
174 | r = pkey_generate_volume_keys(pkey, &decrypted_key, &decrypted_key_size, &saved_key, &saved_key_size); | |
175 | if (r < 0) | |
176 | return log_error_errno(r, "Failed to generate volume keys: %m"); | |
177 | ||
178 | /* Add the token URI to the public part of the record. */ | |
179 | r = add_pkcs11_token_uri(v, uri); | |
180 | if (r < 0) | |
181 | return r; | |
182 | ||
183 | /* Include the encrypted version of the random key we just generated in the privileged part of the record */ | |
184 | r = add_pkcs11_encrypted_key( | |
185 | v, | |
186 | uri, | |
187 | saved_key, saved_key_size, | |
188 | decrypted_key, decrypted_key_size); | |
189 | if (r < 0) | |
190 | return r; | |
191 | ||
192 | /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed | |
193 | * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or | |
194 | * fscrypt. */ | |
195 | r = identity_add_token_pin(v, pin); | |
196 | if (r < 0) | |
197 | return r; | |
198 | ||
199 | return 0; | |
200 | #else | |
201 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), "PKCS#11 tokens not supported on this build."); | |
202 | #endif | |
203 | } |