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