]>
Commit | Line | Data |
---|---|---|
db9ecf05 | 1 | /* SPDX-License-Identifier: LGPL-2.1-or-later */ |
93295a25 LP |
2 | |
3 | #include "errno-util.h" | |
0eb3be46 | 4 | #include "format-table.h" |
93295a25 LP |
5 | #include "hexdecoct.h" |
6 | #include "homectl-pkcs11.h" | |
7 | #include "libcrypt-util.h" | |
8 | #include "memory-util.h" | |
9 | #include "openssl-util.h" | |
10 | #include "pkcs11-util.h" | |
11 | #include "random-util.h" | |
12 | #include "strv.h" | |
13 | ||
93295a25 LP |
14 | static int add_pkcs11_encrypted_key( |
15 | JsonVariant **v, | |
16 | const char *uri, | |
17 | const void *encrypted_key, size_t encrypted_key_size, | |
18 | const void *decrypted_key, size_t decrypted_key_size) { | |
19 | ||
20 | _cleanup_(json_variant_unrefp) JsonVariant *l = NULL, *w = NULL, *e = NULL; | |
0e98d17e | 21 | _cleanup_(erase_and_freep) char *base64_encoded = NULL, *hashed = NULL; |
5e476b85 | 22 | ssize_t base64_encoded_size; |
93295a25 LP |
23 | int r; |
24 | ||
25 | assert(v); | |
26 | assert(uri); | |
27 | assert(encrypted_key); | |
28 | assert(encrypted_key_size > 0); | |
29 | assert(decrypted_key); | |
30 | assert(decrypted_key_size > 0); | |
31 | ||
93295a25 LP |
32 | /* Before using UNIX hashing on the supplied key we base64 encode it, since crypt_r() and friends |
33 | * expect a NUL terminated string, and we use a binary key */ | |
5e476b85 LP |
34 | base64_encoded_size = base64mem(decrypted_key, decrypted_key_size, &base64_encoded); |
35 | if (base64_encoded_size < 0) | |
36 | return log_error_errno(base64_encoded_size, "Failed to base64 encode secret key: %m"); | |
93295a25 | 37 | |
0e98d17e ZJS |
38 | r = hash_password(base64_encoded, &hashed); |
39 | if (r < 0) | |
93295a25 LP |
40 | return log_error_errno(errno_or_else(EINVAL), "Failed to UNIX hash secret key: %m"); |
41 | ||
42 | r = json_build(&e, JSON_BUILD_OBJECT( | |
43 | JSON_BUILD_PAIR("uri", JSON_BUILD_STRING(uri)), | |
44 | JSON_BUILD_PAIR("data", JSON_BUILD_BASE64(encrypted_key, encrypted_key_size)), | |
0e98d17e | 45 | JSON_BUILD_PAIR("hashedPassword", JSON_BUILD_STRING(hashed)))); |
93295a25 LP |
46 | if (r < 0) |
47 | return log_error_errno(r, "Failed to build encrypted JSON key object: %m"); | |
48 | ||
49 | w = json_variant_ref(json_variant_by_key(*v, "privileged")); | |
50 | l = json_variant_ref(json_variant_by_key(w, "pkcs11EncryptedKey")); | |
51 | ||
52 | r = json_variant_append_array(&l, e); | |
53 | if (r < 0) | |
54 | return log_error_errno(r, "Failed append PKCS#11 encrypted key: %m"); | |
55 | ||
56 | r = json_variant_set_field(&w, "pkcs11EncryptedKey", l); | |
57 | if (r < 0) | |
58 | return log_error_errno(r, "Failed to set PKCS#11 encrypted key: %m"); | |
59 | ||
60 | r = json_variant_set_field(v, "privileged", w); | |
61 | if (r < 0) | |
62 | return log_error_errno(r, "Failed to update privileged field: %m"); | |
63 | ||
64 | return 0; | |
65 | } | |
66 | ||
67 | static int add_pkcs11_token_uri(JsonVariant **v, const char *uri) { | |
68 | _cleanup_(json_variant_unrefp) JsonVariant *w = NULL; | |
69 | _cleanup_strv_free_ char **l = NULL; | |
70 | int r; | |
71 | ||
72 | assert(v); | |
73 | assert(uri); | |
74 | ||
75 | w = json_variant_ref(json_variant_by_key(*v, "pkcs11TokenUri")); | |
76 | if (w) { | |
77 | r = json_variant_strv(w, &l); | |
78 | if (r < 0) | |
79 | return log_error_errno(r, "Failed to parse PKCS#11 token list: %m"); | |
80 | ||
81 | if (strv_contains(l, uri)) | |
82 | return 0; | |
83 | } | |
84 | ||
85 | r = strv_extend(&l, uri); | |
86 | if (r < 0) | |
87 | return log_oom(); | |
88 | ||
89 | w = json_variant_unref(w); | |
90 | r = json_variant_new_array_strv(&w, l); | |
91 | if (r < 0) | |
92 | return log_error_errno(r, "Failed to create PKCS#11 token URI JSON: %m"); | |
93 | ||
94 | r = json_variant_set_field(v, "pkcs11TokenUri", w); | |
95 | if (r < 0) | |
96 | return log_error_errno(r, "Failed to update PKCS#11 token URI list: %m"); | |
97 | ||
98 | return 0; | |
99 | } | |
100 | ||
101 | int identity_add_token_pin(JsonVariant **v, const char *pin) { | |
102 | _cleanup_(json_variant_unrefp) JsonVariant *w = NULL, *l = NULL; | |
103 | _cleanup_(strv_free_erasep) char **pins = NULL; | |
104 | int r; | |
105 | ||
106 | assert(v); | |
107 | ||
108 | if (isempty(pin)) | |
109 | return 0; | |
110 | ||
111 | w = json_variant_ref(json_variant_by_key(*v, "secret")); | |
112 | l = json_variant_ref(json_variant_by_key(w, "tokenPin")); | |
113 | ||
114 | r = json_variant_strv(l, &pins); | |
115 | if (r < 0) | |
116 | return log_error_errno(r, "Failed to convert PIN array: %m"); | |
117 | ||
d29cc4d6 | 118 | if (strv_contains(pins, pin)) |
93295a25 LP |
119 | return 0; |
120 | ||
121 | r = strv_extend(&pins, pin); | |
122 | if (r < 0) | |
123 | return log_oom(); | |
124 | ||
125 | strv_uniq(pins); | |
126 | ||
127 | l = json_variant_unref(l); | |
128 | ||
129 | r = json_variant_new_array_strv(&l, pins); | |
130 | if (r < 0) | |
131 | return log_error_errno(r, "Failed to allocate new PIN array JSON: %m"); | |
132 | ||
133 | json_variant_sensitive(l); | |
134 | ||
135 | r = json_variant_set_field(&w, "tokenPin", l); | |
136 | if (r < 0) | |
137 | return log_error_errno(r, "Failed to update PIN field: %m"); | |
138 | ||
139 | r = json_variant_set_field(v, "secret", w); | |
140 | if (r < 0) | |
141 | return log_error_errno(r, "Failed to update secret object: %m"); | |
142 | ||
143 | return 1; | |
144 | } | |
145 | ||
bf108eb9 FW |
146 | static int acquire_pkcs11_certificate( |
147 | const char *uri, | |
148 | const char *askpw_friendly_name, | |
149 | const char *askpw_icon_name, | |
150 | X509 **ret_cert, | |
151 | char **ret_pin_used) { | |
152 | #if HAVE_P11KIT | |
153 | return pkcs11_acquire_certificate(uri, askpw_friendly_name, askpw_icon_name, ret_cert, ret_pin_used); | |
154 | #else | |
155 | return log_error_errno(SYNTHETIC_ERRNO(EOPNOTSUPP), | |
156 | "PKCS#11 tokens not supported on this build."); | |
157 | #endif | |
158 | } | |
159 | ||
93295a25 LP |
160 | int identity_add_pkcs11_key_data(JsonVariant **v, const char *uri) { |
161 | _cleanup_(erase_and_freep) void *decrypted_key = NULL, *encrypted_key = NULL; | |
162 | _cleanup_(erase_and_freep) char *pin = NULL; | |
163 | size_t decrypted_key_size, encrypted_key_size; | |
164 | _cleanup_(X509_freep) X509 *cert = NULL; | |
165 | EVP_PKEY *pkey; | |
d041e4fc | 166 | int r; |
93295a25 LP |
167 | |
168 | assert(v); | |
169 | ||
bf108eb9 | 170 | r = acquire_pkcs11_certificate(uri, "home directory operation", "user-home", &cert, &pin); |
93295a25 LP |
171 | if (r < 0) |
172 | return r; | |
173 | ||
174 | pkey = X509_get0_pubkey(cert); | |
175 | if (!pkey) | |
176 | return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to extract public key from X.509 certificate."); | |
177 | ||
d041e4fc LP |
178 | r = rsa_pkey_to_suitable_key_size(pkey, &decrypted_key_size); |
179 | if (r < 0) | |
180 | return log_error_errno(r, "Failed to extract RSA key size from X509 certificate."); | |
93295a25 LP |
181 | |
182 | log_debug("Generating %zu bytes random key.", decrypted_key_size); | |
183 | ||
184 | decrypted_key = malloc(decrypted_key_size); | |
185 | if (!decrypted_key) | |
186 | return log_oom(); | |
187 | ||
87cb1ab6 | 188 | r = crypto_random_bytes(decrypted_key, decrypted_key_size); |
93295a25 LP |
189 | if (r < 0) |
190 | return log_error_errno(r, "Failed to generate random key: %m"); | |
191 | ||
f2d5df8a | 192 | r = rsa_encrypt_bytes(pkey, decrypted_key, decrypted_key_size, &encrypted_key, &encrypted_key_size); |
93295a25 LP |
193 | if (r < 0) |
194 | return log_error_errno(r, "Failed to encrypt key: %m"); | |
195 | ||
196 | /* Add the token URI to the public part of the record. */ | |
197 | r = add_pkcs11_token_uri(v, uri); | |
198 | if (r < 0) | |
199 | return r; | |
200 | ||
201 | /* Include the encrypted version of the random key we just generated in the privileged part of the record */ | |
202 | r = add_pkcs11_encrypted_key( | |
203 | v, | |
204 | uri, | |
205 | encrypted_key, encrypted_key_size, | |
206 | decrypted_key, decrypted_key_size); | |
207 | if (r < 0) | |
208 | return r; | |
209 | ||
210 | /* If we acquired the PIN also include it in the secret section of the record, so that systemd-homed | |
211 | * can use it if it needs to, given that it likely needs to decrypt the key again to pass to LUKS or | |
212 | * fscrypt. */ | |
213 | r = identity_add_token_pin(v, pin); | |
214 | if (r < 0) | |
215 | return r; | |
216 | ||
217 | return 0; | |
218 | } |