/src/sudo/logsrvd/tls_init.c
Line | Count | Source |
1 | | /* |
2 | | * SPDX-License-Identifier: ISC |
3 | | * |
4 | | * Copyright (c) 2019-2022 Todd C. Miller <Todd.Miller@sudo.ws> |
5 | | * |
6 | | * Permission to use, copy, modify, and distribute this software for any |
7 | | * purpose with or without fee is hereby granted, provided that the above |
8 | | * copyright notice and this permission notice appear in all copies. |
9 | | * |
10 | | * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES |
11 | | * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF |
12 | | * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR |
13 | | * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES |
14 | | * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN |
15 | | * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF |
16 | | * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. |
17 | | */ |
18 | | |
19 | | #include <config.h> |
20 | | |
21 | | #ifdef HAVE_STDBOOL_H |
22 | | # include <stdbool.h> |
23 | | #else |
24 | | # include <compat/stdbool.h> |
25 | | #endif /* HAVE_STDBOOL_H */ |
26 | | #if defined(HAVE_STDINT_H) |
27 | | # include <stdint.h> |
28 | | #elif defined(HAVE_INTTYPES_H) |
29 | | # include <inttypes.h> |
30 | | #endif |
31 | | #include <stdio.h> |
32 | | #include <stdlib.h> |
33 | | #include <string.h> |
34 | | #include <fcntl.h> |
35 | | #include <unistd.h> |
36 | | |
37 | | #include <sudo_compat.h> |
38 | | #include <sudo_debug.h> |
39 | | #include <sudo_event.h> |
40 | | #include <sudo_fatal.h> |
41 | | #include <sudo_gettext.h> |
42 | | |
43 | | #include <tls_common.h> |
44 | | #include <hostcheck.h> |
45 | | |
46 | 0 | #define DEFAULT_CIPHER_LST12 "HIGH:!aNULL" |
47 | 0 | #define DEFAULT_CIPHER_LST13 "TLS_AES_256_GCM_SHA384" |
48 | | |
49 | | #if defined(HAVE_OPENSSL) |
50 | | # include <openssl/bio.h> |
51 | | # include <openssl/dh.h> |
52 | | |
53 | | static bool |
54 | | verify_cert_chain(SSL_CTX *ctx, const char *cert_file) |
55 | 0 | { |
56 | 0 | #ifdef HAVE_SSL_CTX_GET0_CERTIFICATE |
57 | 0 | const char *errstr; |
58 | 0 | bool ret = false; |
59 | 0 | X509_STORE_CTX *store_ctx = NULL; |
60 | 0 | X509_STORE *ca_store; |
61 | 0 | STACK_OF(X509) *chain_certs; |
62 | 0 | X509 *x509; |
63 | 0 | debug_decl(verify_cert_chain, SUDO_DEBUG_UTIL); |
64 | |
|
65 | 0 | if ((x509 = SSL_CTX_get0_certificate(ctx)) == NULL) { |
66 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
67 | 0 | sudo_warnx("SSL_CTX_get0_certificate: %s", |
68 | 0 | errstr ? errstr : strerror(errno)); |
69 | 0 | goto done; |
70 | 0 | } |
71 | | |
72 | 0 | if ((store_ctx = X509_STORE_CTX_new()) == NULL) { |
73 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
74 | 0 | sudo_warnx("X509_STORE_CTX_new: %s", |
75 | 0 | errstr ? errstr : strerror(errno)); |
76 | 0 | goto done; |
77 | 0 | } |
78 | | |
79 | 0 | if (!SSL_CTX_get0_chain_certs(ctx, &chain_certs)) { |
80 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
81 | 0 | sudo_warnx("SSL_CTX_get0_chain_certs: %s: %s", cert_file, |
82 | 0 | errstr ? errstr : strerror(errno)); |
83 | 0 | goto done; |
84 | 0 | } |
85 | | |
86 | 0 | ca_store = SSL_CTX_get_cert_store(ctx); |
87 | 0 | #ifdef X509_V_FLAG_X509_STRICT |
88 | 0 | if (ca_store != NULL) |
89 | 0 | X509_STORE_set_flags(ca_store, X509_V_FLAG_X509_STRICT); |
90 | 0 | #endif |
91 | |
|
92 | 0 | if (!X509_STORE_CTX_init(store_ctx, ca_store, x509, chain_certs)) { |
93 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
94 | 0 | sudo_warnx("X509_STORE_CTX_init: %s", |
95 | 0 | errstr ? errstr : strerror(errno)); |
96 | 0 | goto done; |
97 | 0 | } |
98 | | |
99 | 0 | if (X509_verify_cert(store_ctx) <= 0) { |
100 | 0 | errstr = |
101 | 0 | X509_verify_cert_error_string(X509_STORE_CTX_get_error(store_ctx)); |
102 | 0 | sudo_warnx("X509_verify_cert: %s: %s", cert_file, errstr); |
103 | 0 | goto done; |
104 | 0 | } |
105 | | |
106 | 0 | ret = true; |
107 | 0 | done: |
108 | 0 | X509_STORE_CTX_free(store_ctx); |
109 | |
|
110 | 0 | debug_return_bool(ret); |
111 | | #else |
112 | | /* TODO: verify server cert with old OpenSSL */ |
113 | | return true; |
114 | | #endif /* HAVE_SSL_CTX_GET0_CERTIFICATE */ |
115 | 0 | } |
116 | | |
117 | | static bool |
118 | | init_tls_ciphersuites(SSL_CTX *ctx, const char *ciphers_v12, |
119 | | const char *ciphers_v13) |
120 | 0 | { |
121 | 0 | const char *errstr; |
122 | 0 | int success = 0; |
123 | 0 | debug_decl(init_tls_ciphersuites, SUDO_DEBUG_UTIL); |
124 | |
|
125 | 0 | if (ciphers_v12 != NULL) { |
126 | | /* try to set TLS v1.2 ciphersuite list from config if given */ |
127 | 0 | success = SSL_CTX_set_cipher_list(ctx, ciphers_v12); |
128 | 0 | if (success) { |
129 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
130 | 0 | "TLS 1.2 ciphersuite list set to %s", ciphers_v12); |
131 | 0 | } else { |
132 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
133 | 0 | sudo_warnx(U_("unable to set TLS 1.2 ciphersuite to %s: %s"), |
134 | 0 | ciphers_v12, errstr ? errstr : strerror(errno)); |
135 | 0 | } |
136 | 0 | } |
137 | 0 | if (!success) { |
138 | | /* fallback to default ciphersuites for TLS v1.2 */ |
139 | 0 | if (SSL_CTX_set_cipher_list(ctx, DEFAULT_CIPHER_LST12) <= 0) { |
140 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
141 | 0 | sudo_warnx(U_("unable to set TLS 1.2 ciphersuite to %s: %s"), |
142 | 0 | DEFAULT_CIPHER_LST12, errstr ? errstr : strerror(errno)); |
143 | 0 | debug_return_bool(false); |
144 | 0 | } else { |
145 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
146 | 0 | "TLS v1.2 ciphersuite list set to %s (default)", |
147 | 0 | DEFAULT_CIPHER_LST12); |
148 | 0 | } |
149 | 0 | } |
150 | | |
151 | 0 | # if defined(HAVE_SSL_CTX_SET_CIPHERSUITES) |
152 | 0 | success = 0; |
153 | 0 | if (ciphers_v13 != NULL) { |
154 | | /* try to set TLSv1.3 ciphersuite list from config */ |
155 | 0 | success = SSL_CTX_set_ciphersuites(ctx, ciphers_v13); |
156 | 0 | if (success) { |
157 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
158 | 0 | "TLS v1.3 ciphersuite list set to %s", ciphers_v13); |
159 | 0 | } else { |
160 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
161 | 0 | sudo_warnx(U_("unable to set TLS 1.3 ciphersuite to %s: %s"), |
162 | 0 | ciphers_v13, errstr ? errstr : strerror(errno)); |
163 | 0 | } |
164 | 0 | } |
165 | 0 | if (!success) { |
166 | | /* fallback to default ciphersuites for TLS v1.3 */ |
167 | 0 | if (SSL_CTX_set_ciphersuites(ctx, DEFAULT_CIPHER_LST13) <= 0) { |
168 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
169 | 0 | sudo_warnx(U_("unable to set TLS 1.3 ciphersuite to %s: %s"), |
170 | 0 | DEFAULT_CIPHER_LST13, errstr ? errstr : strerror(errno)); |
171 | 0 | debug_return_bool(false); |
172 | 0 | } else { |
173 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
174 | 0 | "TLS v1.3 ciphersuite list set to %s (default)", |
175 | 0 | DEFAULT_CIPHER_LST13); |
176 | 0 | } |
177 | 0 | } |
178 | 0 | # endif |
179 | | |
180 | 0 | debug_return_bool(true); |
181 | 0 | } |
182 | | |
183 | | /* |
184 | | * Load diffie-hellman parameters from bio and store in ctx. |
185 | | * Returns true on success, else false. |
186 | | */ |
187 | | #ifdef HAVE_SSL_CTX_SET0_TMP_DH_PKEY |
188 | | static bool |
189 | | set_dhparams_bio(SSL_CTX *ctx, BIO *bio) |
190 | | { |
191 | | EVP_PKEY *dhparams; |
192 | | bool ret = false; |
193 | | debug_decl(set_dhparams_bio, SUDO_DEBUG_UTIL); |
194 | | |
195 | | dhparams = PEM_read_bio_Parameters(bio, NULL); |
196 | | if (dhparams != NULL) { |
197 | | /* dhparams is owned by ctx on success. */ |
198 | | ret = SSL_CTX_set0_tmp_dh_pkey(ctx, dhparams); |
199 | | if (!ret) { |
200 | | const char *errstr = ERR_reason_error_string(ERR_get_error()); |
201 | | sudo_warnx(U_("unable to set diffie-hellman parameters: %s"), |
202 | | errstr ? errstr : strerror(errno)); |
203 | | EVP_PKEY_free(dhparams); |
204 | | } |
205 | | } |
206 | | debug_return_bool(ret); |
207 | | } |
208 | | #else |
209 | | static bool |
210 | | set_dhparams_bio(SSL_CTX *ctx, BIO *bio) |
211 | 0 | { |
212 | 0 | DH *dhparams; |
213 | 0 | bool ret = false; |
214 | 0 | debug_decl(set_dhparams_bio, SUDO_DEBUG_UTIL); |
215 | |
|
216 | 0 | dhparams = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); |
217 | 0 | if (dhparams != NULL) { |
218 | | /* LEAK: dhparams leaked on config reload */ |
219 | 0 | ret = SSL_CTX_set_tmp_dh(ctx, dhparams); |
220 | 0 | if (!ret) { |
221 | 0 | const char *errstr = ERR_reason_error_string(ERR_get_error()); |
222 | 0 | sudo_warnx(U_("unable to set diffie-hellman parameters: %s"), |
223 | 0 | errstr ? errstr : strerror(errno)); |
224 | 0 | DH_free(dhparams); |
225 | 0 | } |
226 | 0 | } |
227 | 0 | debug_return_bool(ret); |
228 | 0 | } |
229 | | #endif /* HAVE_SSL_CTX_SET0_TMP_DH_PKEY */ |
230 | | |
231 | | /* |
232 | | * Load diffie-hellman parameters from the specified file and store in ctx. |
233 | | * Returns true on success, else false. |
234 | | */ |
235 | | static bool |
236 | | set_dhparams(SSL_CTX *ctx, const char *dhparam_file) |
237 | 0 | { |
238 | 0 | BIO *bio; |
239 | 0 | bool ret = false; |
240 | 0 | debug_decl(set_dhparams, SUDO_DEBUG_UTIL); |
241 | |
|
242 | 0 | bio = BIO_new_file(dhparam_file, "r"); |
243 | 0 | if (bio != NULL) { |
244 | 0 | if (set_dhparams_bio(ctx, bio)) { |
245 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
246 | 0 | "loaded diffie-hellman parameters from %s", dhparam_file); |
247 | 0 | ret = true; |
248 | 0 | } |
249 | 0 | BIO_free(bio); |
250 | 0 | } else { |
251 | 0 | sudo_warn(U_("unable to open %s"), dhparam_file); |
252 | 0 | } |
253 | |
|
254 | 0 | debug_return_bool(ret); |
255 | 0 | } |
256 | | |
257 | | SSL_CTX * |
258 | | init_tls_context(const char *ca_bundle_file, const char *cert_file, |
259 | | const char *key_file, const char *dhparam_file, const char *ciphers_v12, |
260 | | const char *ciphers_v13, bool verify_cert) |
261 | 0 | { |
262 | 0 | SSL_CTX *ctx = NULL; |
263 | 0 | const char *errstr; |
264 | 0 | static bool initialized; |
265 | 0 | debug_decl(init_tls_context, SUDO_DEBUG_UTIL); |
266 | | |
267 | | /* Only initialize the SSL library once. */ |
268 | 0 | if (!initialized) { |
269 | 0 | SSL_library_init(); |
270 | 0 | OpenSSL_add_all_algorithms(); |
271 | 0 | SSL_load_error_strings(); |
272 | 0 | initialized = true; |
273 | 0 | } |
274 | | |
275 | | /* Create the ssl context and enforce TLS 1.2 or higher. */ |
276 | 0 | if ((ctx = SSL_CTX_new(TLS_method())) == NULL) { |
277 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
278 | 0 | sudo_warnx(U_("unable to create TLS context: %s"), |
279 | 0 | errstr ? errstr : strerror(errno)); |
280 | 0 | goto bad; |
281 | 0 | } |
282 | 0 | #ifdef HAVE_SSL_CTX_SET_MIN_PROTO_VERSION |
283 | 0 | if (!SSL_CTX_set_min_proto_version(ctx, TLS1_2_VERSION)) { |
284 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
285 | 0 | sudo_warnx(U_("unable to set minimum protocol version to TLS 1.2: %s"), |
286 | 0 | errstr ? errstr : strerror(errno)); |
287 | 0 | goto bad; |
288 | 0 | } |
289 | | #else |
290 | | SSL_CTX_set_options(ctx, |
291 | | SSL_OP_NO_SSLv2|SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1); |
292 | | #endif |
293 | | |
294 | 0 | if (ca_bundle_file != NULL) { |
295 | 0 | STACK_OF(X509_NAME) *cacerts = |
296 | 0 | SSL_load_client_CA_file(ca_bundle_file); |
297 | |
|
298 | 0 | if (cacerts == NULL) { |
299 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
300 | 0 | sudo_warnx(U_("%s: %s"), ca_bundle_file, |
301 | 0 | errstr ? errstr : strerror(errno)); |
302 | 0 | goto bad; |
303 | 0 | } |
304 | 0 | SSL_CTX_set_client_CA_list(ctx, cacerts); |
305 | |
|
306 | 0 | if (SSL_CTX_load_verify_locations(ctx, ca_bundle_file, NULL) <= 0) { |
307 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
308 | 0 | sudo_warnx("SSL_CTX_load_verify_locations: %s: %s", ca_bundle_file, |
309 | 0 | errstr ? errstr : strerror(errno)); |
310 | 0 | goto bad; |
311 | 0 | } |
312 | 0 | } else { |
313 | 0 | if (!SSL_CTX_set_default_verify_paths(ctx)) { |
314 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
315 | 0 | sudo_warnx("SSL_CTX_set_default_verify_paths: %s", |
316 | 0 | errstr ? errstr : strerror(errno)); |
317 | 0 | goto bad; |
318 | 0 | } |
319 | 0 | } |
320 | | |
321 | 0 | if (cert_file != NULL) { |
322 | 0 | if (!SSL_CTX_use_certificate_chain_file(ctx, cert_file)) { |
323 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
324 | 0 | sudo_warnx(U_("%s: %s"), cert_file, |
325 | 0 | errstr ? errstr : strerror(errno)); |
326 | 0 | goto bad; |
327 | 0 | } |
328 | 0 | if (key_file == NULL) { |
329 | | /* No explicit key file set, try to use the cert file. */ |
330 | 0 | key_file = cert_file; |
331 | 0 | } |
332 | 0 | if (!SSL_CTX_use_PrivateKey_file(ctx, key_file, SSL_FILETYPE_PEM) || |
333 | 0 | !SSL_CTX_check_private_key(ctx)) { |
334 | 0 | errstr = ERR_reason_error_string(ERR_get_error()); |
335 | 0 | sudo_warnx(U_("%s: %s"), key_file, |
336 | 0 | errstr ? errstr : strerror(errno)); |
337 | 0 | goto bad; |
338 | 0 | } |
339 | | |
340 | | /* Optionally verify the certificate we are using. */ |
341 | 0 | if (verify_cert) { |
342 | 0 | if (!verify_cert_chain(ctx, cert_file)) |
343 | 0 | goto bad; |
344 | 0 | } else { |
345 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
346 | 0 | "skipping local cert check"); |
347 | 0 | } |
348 | 0 | } |
349 | | |
350 | | /* Initialize TLS 1.2 1.3 ciphersuites. */ |
351 | 0 | if (!init_tls_ciphersuites(ctx, ciphers_v12, ciphers_v13)) { |
352 | 0 | goto bad; |
353 | 0 | } |
354 | | |
355 | | /* |
356 | | * Load diffie-hellman parameters from a file if specified. |
357 | | * Failure to open the file is not a fatal error. |
358 | | */ |
359 | 0 | if (dhparam_file != NULL) { |
360 | 0 | if (!set_dhparams(ctx, dhparam_file)) { |
361 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
362 | 0 | "unable to load dhparam file, using default parameters"); |
363 | 0 | } |
364 | 0 | } else { |
365 | 0 | sudo_debug_printf(SUDO_DEBUG_INFO|SUDO_DEBUG_LINENO, |
366 | 0 | "dhparam file not specified, using default parameters"); |
367 | 0 | } |
368 | |
|
369 | 0 | goto done; |
370 | | |
371 | 0 | bad: |
372 | 0 | SSL_CTX_free(ctx); |
373 | 0 | ctx = NULL; |
374 | |
|
375 | 0 | done: |
376 | 0 | debug_return_ptr(ctx); |
377 | 0 | } |
378 | | #endif /* HAVE_OPENSSL */ |