]>
Commit | Line | Data |
---|---|---|
474f076e | 1 | /* |
b8ae064d | 2 | * Copyright (C) 1996-2023 The Squid Software Foundation and contributors |
474f076e AJ |
3 | * |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
9 | #include "squid.h" | |
cf487124 | 10 | #include "anyp/PortCfg.h" |
a4dd5bfa | 11 | #include "base/IoManip.h" |
474f076e | 12 | #include "base/Packable.h" |
cf487124 | 13 | #include "cache_cf.h" |
742236c7 | 14 | #include "error/SysErrorDetail.h" |
cf487124 | 15 | #include "fatal.h" |
474f076e AJ |
16 | #include "globals.h" |
17 | #include "security/ServerOptions.h" | |
cf487124 AJ |
18 | #include "security/Session.h" |
19 | #include "SquidConfig.h" | |
0a28c16a | 20 | #if USE_OPENSSL |
24b30fdc | 21 | #include "compat/openssl.h" |
0a28c16a | 22 | #include "ssl/support.h" |
474f076e | 23 | |
742236c7 AJ |
24 | #if HAVE_OPENSSL_DECODER_H |
25 | #include <openssl/decoder.h> | |
26 | #endif | |
474f076e AJ |
27 | #if HAVE_OPENSSL_ERR_H |
28 | #include <openssl/err.h> | |
29 | #endif | |
474f076e AJ |
30 | #endif |
31 | ||
1f8b5f0e | 32 | #include <limits> |
33 | ||
621f4299 AJ |
34 | Security::ServerOptions & |
35 | Security::ServerOptions::operator =(const Security::ServerOptions &old) { | |
36 | if (this != &old) { | |
37 | Security::PeerOptions::operator =(old); | |
38 | clientCaFile = old.clientCaFile; | |
39 | dh = old.dh; | |
40 | dhParamsFile = old.dhParamsFile; | |
41 | eecdhCurve = old.eecdhCurve; | |
42 | parsedDhParams = old.parsedDhParams; | |
43 | #if USE_OPENSSL | |
44 | if (auto *stk = SSL_dup_CA_list(old.clientCaStack.get())) | |
45 | clientCaStack = Security::ServerOptions::X509_NAME_STACK_Pointer(stk); | |
cf487124 | 46 | else |
621f4299 | 47 | #endif |
cf487124 AJ |
48 | clientCaStack = nullptr; |
49 | ||
50 | staticContextSessionId = old.staticContextSessionId; | |
51 | generateHostCertificates = old.generateHostCertificates; | |
51e09c08 AJ |
52 | signingCa = old.signingCa; |
53 | untrustedSigningCa = old.untrustedSigningCa; | |
cf487124 | 54 | dynamicCertMemCacheSize = old.dynamicCertMemCacheSize; |
621f4299 AJ |
55 | } |
56 | return *this; | |
57 | } | |
58 | ||
474f076e AJ |
59 | void |
60 | Security::ServerOptions::parse(const char *token) | |
61 | { | |
62 | if (!*token) { | |
63 | // config says just "ssl" or "tls" (or "tls-") | |
64 | encryptTransport = true; | |
65 | return; | |
66 | } | |
67 | ||
68 | // parse the server-only options | |
fbffd8a3 AR |
69 | if (strncmp(token, "clientca=", 9) == 0) { |
70 | clientCaFile = SBuf(token + 9); | |
71 | } else if (strncmp(token, "dh=", 3) == 0) { | |
474f076e AJ |
72 | // clear any previous Diffi-Helman configuration |
73 | dh.clear(); | |
74 | dhParamsFile.clear(); | |
75 | eecdhCurve.clear(); | |
76 | ||
77 | dh.append(token + 3); | |
78 | ||
79 | if (!dh.isEmpty()) { | |
80 | auto pos = dh.find(':'); | |
81 | if (pos != SBuf::npos) { // tls-dh=eecdhCurve:dhParamsFile | |
82 | eecdhCurve = dh.substr(0,pos); | |
83 | dhParamsFile = dh.substr(pos+1); | |
84 | } else { // tls-dh=dhParamsFile | |
85 | dhParamsFile = dh; | |
86 | // empty eecdhCurve means "do not use EECDH" | |
87 | } | |
88 | } | |
89 | ||
104deb98 AJ |
90 | loadDhParams(); |
91 | ||
474f076e AJ |
92 | } else if (strncmp(token, "dhparams=", 9) == 0) { |
93 | if (!eecdhCurve.isEmpty()) { | |
d816f28d | 94 | debugs(83, DBG_PARSE_NOTE(1), "WARNING: UPGRADE: EECDH settings in tls-dh= override dhparams="); |
474f076e AJ |
95 | return; |
96 | } | |
97 | ||
98 | // backward compatibility for dhparams= configuration | |
99 | dh.clear(); | |
100 | dh.append(token + 9); | |
101 | dhParamsFile = dh; | |
102 | ||
104deb98 AJ |
103 | loadDhParams(); |
104 | ||
cf487124 AJ |
105 | } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) { |
106 | parseBytesOptionValue(&dynamicCertMemCacheSize, "bytes", token + 28); | |
107 | // XXX: parseBytesOptionValue() self_destruct()s on invalid values, | |
108 | // probably making this comparison and misleading ERROR unnecessary. | |
109 | if (dynamicCertMemCacheSize == std::numeric_limits<size_t>::max()) { | |
110 | debugs(3, DBG_CRITICAL, "ERROR: Cannot allocate memory for '" << token << "'. Using default of 4MB instead."); | |
111 | dynamicCertMemCacheSize = 4*1024*1024; // 4 MB | |
112 | } | |
113 | ||
114 | } else if (strcmp(token, "generate-host-certificates") == 0) { | |
115 | generateHostCertificates = true; | |
116 | } else if (strcmp(token, "generate-host-certificates=on") == 0) { | |
117 | generateHostCertificates = true; | |
118 | } else if (strcmp(token, "generate-host-certificates=off") == 0) { | |
119 | generateHostCertificates = false; | |
120 | ||
121 | } else if (strncmp(token, "context=", 8) == 0) { | |
122 | #if USE_OPENSSL | |
123 | staticContextSessionId = SBuf(token+8); | |
124 | // to hide its arguably sensitive value, do not print token in these debugs | |
125 | if (staticContextSessionId.length() > SSL_MAX_SSL_SESSION_ID_LENGTH) { | |
126 | debugs(83, DBG_CRITICAL, "FATAL: Option 'context=' value is too long. Maximum " << SSL_MAX_SSL_SESSION_ID_LENGTH << " characters."); | |
127 | self_destruct(); | |
128 | } | |
129 | #else | |
130 | debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Option 'context=' requires --with-openssl. Ignoring."); | |
131 | #endif | |
132 | ||
474f076e AJ |
133 | } else { |
134 | // parse generic TLS options | |
135 | Security::PeerOptions::parse(token); | |
136 | } | |
137 | } | |
138 | ||
139 | void | |
90153ed6 | 140 | Security::ServerOptions::dumpCfg(std::ostream &os, const char *pfx) const |
474f076e AJ |
141 | { |
142 | // dump out the generic TLS options | |
90153ed6 | 143 | Security::PeerOptions::dumpCfg(os, pfx); |
474f076e AJ |
144 | |
145 | if (!encryptTransport) | |
146 | return; // no other settings are relevant | |
147 | ||
148 | // dump the server-only options | |
149 | if (!dh.isEmpty()) | |
90153ed6 | 150 | os << ' ' << pfx << "dh=" << dh; |
cf487124 AJ |
151 | |
152 | if (!generateHostCertificates) | |
90153ed6 | 153 | os << ' ' << pfx << "generate-host-certificates=off"; |
cf487124 AJ |
154 | |
155 | if (dynamicCertMemCacheSize != 4*1024*1024) // 4MB default, no 'tls-' prefix | |
90153ed6 | 156 | os << ' ' << "dynamic_cert_mem_cache_size=" << dynamicCertMemCacheSize << "bytes"; |
cf487124 AJ |
157 | |
158 | if (!staticContextSessionId.isEmpty()) | |
90153ed6 | 159 | os << ' ' << pfx << "context=" << staticContextSessionId; |
474f076e AJ |
160 | } |
161 | ||
64769c79 | 162 | Security::ContextPointer |
885f0ecf AJ |
163 | Security::ServerOptions::createBlankContext() const |
164 | { | |
64769c79 | 165 | Security::ContextPointer ctx; |
885f0ecf | 166 | #if USE_OPENSSL |
0a28c16a AJ |
167 | Ssl::Initialize(); |
168 | ||
64769c79 | 169 | SSL_CTX *t = SSL_CTX_new(TLS_server_method()); |
885f0ecf | 170 | if (!t) { |
ea574635 AJ |
171 | const auto x = ERR_get_error(); |
172 | debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x)); | |
885f0ecf | 173 | } |
df473b36 | 174 | ctx = convertContextFromRawPtr(t); |
885f0ecf | 175 | |
c813943d | 176 | #elif HAVE_LIBGNUTLS |
885f0ecf | 177 | // Initialize for X.509 certificate exchange |
64769c79 | 178 | gnutls_certificate_credentials_t t; |
83b053a0 | 179 | if (const auto x = gnutls_certificate_allocate_credentials(&t)) { |
ea574635 | 180 | debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x)); |
885f0ecf | 181 | } |
df473b36 | 182 | ctx = convertContextFromRawPtr(t); |
885f0ecf AJ |
183 | |
184 | #else | |
185 | debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: No TLS library"); | |
186 | ||
187 | #endif | |
188 | ||
64769c79 | 189 | return ctx; |
885f0ecf AJ |
190 | } |
191 | ||
51e09c08 AJ |
192 | void |
193 | Security::ServerOptions::initServerContexts(AnyP::PortCfg &port) | |
194 | { | |
195 | const char *portType = AnyP::ProtocolType_str[port.transport.protocol]; | |
196 | for (auto &keyData : certs) { | |
197 | keyData.loadFromFiles(port, portType); | |
198 | } | |
199 | ||
200 | if (generateHostCertificates) { | |
201 | createSigningContexts(port); | |
1700fab7 | 202 | } |
51e09c08 | 203 | |
1700fab7 | 204 | if (!certs.empty() && !createStaticServerContext(port)) { |
51e09c08 AJ |
205 | char buf[128]; |
206 | fatalf("%s_port %s initialization error", portType, port.s.toUrl(buf, sizeof(buf))); | |
207 | } | |
1700fab7 AJ |
208 | |
209 | // if generate-host-certificates=off and certs is empty, no contexts may be created. | |
210 | // features depending on contexts do their own checks and error messages later. | |
51e09c08 AJ |
211 | } |
212 | ||
9ad528b8 | 213 | bool |
8b082ed9 | 214 | Security::ServerOptions::createStaticServerContext(AnyP::PortCfg &) |
c75aba02 AJ |
215 | { |
216 | updateTlsVersionLimits(); | |
217 | ||
9ad528b8 | 218 | Security::ContextPointer t(createBlankContext()); |
c75aba02 | 219 | if (t) { |
51e09c08 | 220 | |
c75aba02 | 221 | #if USE_OPENSSL |
51e09c08 AJ |
222 | if (certs.size() > 1) { |
223 | // NOTE: calling SSL_CTX_use_certificate() repeatedly _replaces_ the previous cert details. | |
224 | // so we cannot use it and support multiple server certificates with OpenSSL. | |
2f8abb64 | 225 | debugs(83, DBG_CRITICAL, "ERROR: OpenSSL does not support multiple server certificates. Ignoring additional cert= parameters."); |
51e09c08 AJ |
226 | } |
227 | ||
228 | const auto &keys = certs.front(); | |
229 | ||
230 | if (!SSL_CTX_use_certificate(t.get(), keys.cert.get())) { | |
231 | const auto x = ERR_get_error(); | |
232 | debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS certificate '" << keys.certFile << "': " << Security::ErrorString(x)); | |
9ad528b8 | 233 | return false; |
51e09c08 AJ |
234 | } |
235 | ||
236 | if (!SSL_CTX_use_PrivateKey(t.get(), keys.pkey.get())) { | |
237 | const auto x = ERR_get_error(); | |
238 | debugs(83, DBG_CRITICAL, "ERROR: Failed to acquire TLS private key '" << keys.privateKeyFile << "': " << Security::ErrorString(x)); | |
239 | return false; | |
240 | } | |
241 | ||
242 | for (auto cert : keys.chain) { | |
243 | if (SSL_CTX_add_extra_chain_cert(t.get(), cert.get())) { | |
244 | // increase the certificate lock | |
245 | X509_up_ref(cert.get()); | |
246 | } else { | |
247 | const auto error = ERR_get_error(); | |
248 | debugs(83, DBG_IMPORTANT, "WARNING: can not add certificate to SSL context chain: " << Security::ErrorString(error)); | |
249 | } | |
250 | } | |
251 | ||
c813943d | 252 | #elif HAVE_LIBGNUTLS |
51e09c08 AJ |
253 | for (auto &keys : certs) { |
254 | gnutls_x509_crt_t crt = keys.cert.get(); | |
255 | gnutls_x509_privkey_t xkey = keys.pkey.get(); | |
256 | const auto x = gnutls_certificate_set_x509_key(t.get(), &crt, 1, xkey); | |
257 | if (x != GNUTLS_E_SUCCESS) { | |
258 | SBuf whichFile = keys.certFile; | |
259 | if (keys.certFile != keys.privateKeyFile) { | |
260 | whichFile.appendf(" and "); | |
261 | whichFile.append(keys.privateKeyFile); | |
262 | } | |
263 | debugs(83, DBG_CRITICAL, "ERROR: Failed to initialize server context with keys from " << whichFile << ": " << Security::ErrorString(x)); | |
264 | return false; | |
265 | } | |
f3a5731a | 266 | // XXX: add cert chain to the context |
51e09c08 | 267 | } |
c75aba02 | 268 | #endif |
51e09c08 | 269 | |
fbffd8a3 AR |
270 | if (!loadClientCaFile()) |
271 | return false; | |
272 | ||
273 | // by this point all config related files must be loaded | |
51e09c08 AJ |
274 | if (!updateContextConfig(t)) { |
275 | debugs(83, DBG_CRITICAL, "ERROR: Configuring static TLS context"); | |
276 | return false; | |
277 | } | |
c75aba02 AJ |
278 | } |
279 | ||
9ad528b8 AJ |
280 | staticContext = std::move(t); |
281 | return bool(staticContext); | |
c75aba02 AJ |
282 | } |
283 | ||
cf487124 | 284 | void |
51e09c08 | 285 | Security::ServerOptions::createSigningContexts(const AnyP::PortCfg &port) |
cf487124 | 286 | { |
51e09c08 AJ |
287 | // For signing we do not have a pre-initialized context object. Instead |
288 | // contexts are generated as needed. This method initializes the cert | |
289 | // and key pointers used to sign those contexts later. | |
cf487124 | 290 | |
51e09c08 AJ |
291 | signingCa = certs.front(); |
292 | ||
293 | const char *portType = AnyP::ProtocolType_str[port.transport.protocol]; | |
294 | if (!signingCa.cert) { | |
cf487124 | 295 | char buf[128]; |
51e09c08 AJ |
296 | // XXX: we never actually checked that the cert is capable of signing! |
297 | fatalf("No valid signing certificate configured for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf))); | |
cf487124 AJ |
298 | } |
299 | ||
51e09c08 AJ |
300 | if (!signingCa.pkey) |
301 | debugs(3, DBG_IMPORTANT, "No TLS private key configured for " << portType << "_port " << port.s); | |
cf487124 AJ |
302 | |
303 | #if USE_OPENSSL | |
51e09c08 | 304 | Ssl::generateUntrustedCert(untrustedSigningCa.cert, untrustedSigningCa.pkey, signingCa.cert, signingCa.pkey); |
c813943d | 305 | #elif HAVE_LIBGNUTLS |
51e09c08 AJ |
306 | // TODO: implement for GnuTLS. Just a warning for now since generate is implicitly on for all crypto builds. |
307 | signingCa.cert.reset(); | |
308 | signingCa.pkey.reset(); | |
309 | debugs(83, DBG_CRITICAL, "WARNING: Dynamic TLS certificate generation requires --with-openssl."); | |
310 | return; | |
311 | #else | |
312 | debugs(83, DBG_CRITICAL, "ERROR: Dynamic TLS certificate generation requires --with-openssl."); | |
313 | return; | |
cf487124 AJ |
314 | #endif |
315 | ||
51e09c08 | 316 | if (!untrustedSigningCa.cert) { |
cf487124 | 317 | char buf[128]; |
51e09c08 | 318 | fatalf("Unable to generate signing certificate for untrusted sites for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf))); |
cf487124 AJ |
319 | } |
320 | } | |
321 | ||
621f4299 AJ |
322 | void |
323 | Security::ServerOptions::syncCaFiles() | |
324 | { | |
325 | // if caFiles is set, just use that | |
326 | if (caFiles.size()) | |
327 | return; | |
328 | ||
329 | // otherwise fall back to clientca if it is defined | |
330 | if (!clientCaFile.isEmpty()) | |
331 | caFiles.emplace_back(clientCaFile); | |
332 | } | |
333 | ||
334 | /// load clientca= file (if any) into memory. | |
335 | /// \retval true clientca is not set, or loaded successfully | |
336 | /// \retval false unable to load the file, or not using OpenSSL | |
337 | bool | |
338 | Security::ServerOptions::loadClientCaFile() | |
339 | { | |
340 | if (clientCaFile.isEmpty()) | |
341 | return true; | |
342 | ||
343 | #if USE_OPENSSL | |
344 | auto *stk = SSL_load_client_CA_file(clientCaFile.c_str()); | |
345 | clientCaStack = Security::ServerOptions::X509_NAME_STACK_Pointer(stk); | |
346 | #endif | |
347 | if (!clientCaStack) { | |
348 | debugs(83, DBG_CRITICAL, "FATAL: Unable to read client CAs from file: " << clientCaFile); | |
349 | } | |
350 | ||
351 | return bool(clientCaStack); | |
352 | } | |
353 | ||
474f076e | 354 | void |
104deb98 | 355 | Security::ServerOptions::loadDhParams() |
474f076e | 356 | { |
104deb98 | 357 | if (dhParamsFile.isEmpty()) |
474f076e AJ |
358 | return; |
359 | ||
742236c7 AJ |
360 | // TODO: After loading and validating parameters, also validate that "the |
361 | // public and private components have the correct mathematical | |
362 | // relationship". See EVP_PKEY_check(). | |
363 | ||
104deb98 | 364 | #if USE_OPENSSL |
742236c7 | 365 | #if OPENSSL_VERSION_MAJOR < 3 |
104deb98 AJ |
366 | DH *dhp = nullptr; |
367 | if (FILE *in = fopen(dhParamsFile.c_str(), "r")) { | |
aee3523a | 368 | dhp = PEM_read_DHparams(in, nullptr, nullptr, nullptr); |
104deb98 | 369 | fclose(in); |
742236c7 AJ |
370 | } else { |
371 | const auto xerrno = errno; | |
372 | debugs(83, DBG_IMPORTANT, "WARNING: Failed to open '" << dhParamsFile << "'" << ReportSysError(xerrno)); | |
373 | return; | |
474f076e AJ |
374 | } |
375 | ||
104deb98 AJ |
376 | if (!dhp) { |
377 | debugs(83, DBG_IMPORTANT, "WARNING: Failed to read DH parameters '" << dhParamsFile << "'"); | |
474f076e AJ |
378 | return; |
379 | } | |
380 | ||
104deb98 AJ |
381 | int codes; |
382 | if (DH_check(dhp, &codes) == 0) { | |
383 | if (codes) { | |
a4dd5bfa | 384 | debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhParamsFile << "' (" << asHex(codes) << ")"); |
104deb98 AJ |
385 | DH_free(dhp); |
386 | dhp = nullptr; | |
387 | } | |
474f076e | 388 | } |
104deb98 | 389 | |
35b3559c | 390 | parsedDhParams.resetWithoutLocking(dhp); |
742236c7 AJ |
391 | |
392 | #else // OpenSSL 3.0+ | |
393 | const auto type = eecdhCurve.isEmpty() ? "DH" : "EC"; | |
394 | ||
395 | Ssl::ForgetErrors(); | |
396 | EVP_PKEY *rawPkey = nullptr; | |
397 | using DecoderContext = std::unique_ptr<OSSL_DECODER_CTX, HardFun<void, OSSL_DECODER_CTX*, &OSSL_DECODER_CTX_free> >; | |
398 | if (const DecoderContext dctx{OSSL_DECODER_CTX_new_for_pkey(&rawPkey, "PEM", nullptr, type, 0, nullptr, nullptr)}) { | |
399 | ||
400 | // OpenSSL documentation is vague on this, but OpenSSL code and our | |
401 | // tests suggest that rawPkey remains nil here while rawCtx keeps | |
402 | // rawPkey _address_ for use by the decoder (see OSSL_DECODER_from_fp() | |
403 | // below). Thus, we must not move *rawPkey into a smart pointer until | |
404 | // decoding is over. For cleanup code simplicity, we assert nil rawPkey. | |
405 | assert(!rawPkey); | |
406 | ||
407 | if (OSSL_DECODER_CTX_get_num_decoders(dctx.get()) == 0) { | |
408 | debugs(83, DBG_IMPORTANT, "WARNING: No suitable decoders found for " << type << " parameters" << Ssl::ReportAndForgetErrors); | |
409 | return; | |
410 | } | |
411 | ||
412 | if (const auto in = fopen(dhParamsFile.c_str(), "r")) { | |
413 | if (OSSL_DECODER_from_fp(dctx.get(), in)) { | |
414 | assert(rawPkey); | |
415 | const Security::DhePointer pkey(rawPkey); | |
416 | // TODO: verify that the loaded parameters match the curve named in eecdhCurve | |
417 | ||
418 | if (const Ssl::EVP_PKEY_CTX_Pointer pkeyCtx{EVP_PKEY_CTX_new_from_pkey(nullptr, pkey.get(), nullptr)}) { | |
419 | switch (EVP_PKEY_param_check(pkeyCtx.get())) { | |
420 | case 1: // success | |
421 | parsedDhParams = pkey; | |
422 | break; | |
423 | case -2: | |
424 | debugs(83, DBG_PARSE_NOTE(2), "WARNING: OpenSSL does not support " << type << " parameters check: " << dhParamsFile << Ssl::ReportAndForgetErrors); | |
425 | break; | |
426 | default: | |
427 | debugs(83, DBG_IMPORTANT, "ERROR: Failed to verify " << type << " parameters in " << dhParamsFile << Ssl::ReportAndForgetErrors); | |
428 | break; | |
429 | } | |
430 | } else { | |
431 | // TODO: Reduce error reporting code duplication. | |
432 | debugs(83, DBG_IMPORTANT, "ERROR: Cannot check " << type << " parameters in " << dhParamsFile << Ssl::ReportAndForgetErrors); | |
433 | } | |
434 | } else { | |
435 | debugs(83, DBG_IMPORTANT, "WARNING: Failed to decode " << type << " parameters '" << dhParamsFile << "'" << Ssl::ReportAndForgetErrors); | |
436 | EVP_PKEY_free(rawPkey); // probably still nil, but just in case | |
437 | } | |
438 | fclose(in); | |
439 | } else { | |
440 | const auto xerrno = errno; | |
441 | debugs(83, DBG_IMPORTANT, "WARNING: Failed to open '" << dhParamsFile << "'" << ReportSysError(xerrno)); | |
442 | } | |
443 | ||
444 | } else { | |
445 | debugs(83, DBG_IMPORTANT, "WARNING: Unable to create decode context for " << type << " parameters" << Ssl::ReportAndForgetErrors); | |
446 | return; | |
447 | } | |
104deb98 | 448 | #endif |
742236c7 | 449 | #endif // USE_OPENSSL |
104deb98 AJ |
450 | } |
451 | ||
cf487124 AJ |
452 | bool |
453 | Security::ServerOptions::updateContextConfig(Security::ContextPointer &ctx) | |
454 | { | |
455 | updateContextOptions(ctx); | |
456 | updateContextSessionId(ctx); | |
457 | ||
458 | #if USE_OPENSSL | |
459 | if (parsedFlags & SSL_FLAG_NO_SESSION_REUSE) { | |
460 | SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF); | |
461 | } | |
462 | ||
463 | if (Config.SSL.unclean_shutdown) { | |
464 | debugs(83, 5, "Enabling quiet SSL shutdowns (RFC violation)."); | |
465 | SSL_CTX_set_quiet_shutdown(ctx.get(), 1); | |
466 | } | |
467 | ||
468 | if (!sslCipher.isEmpty()) { | |
469 | debugs(83, 5, "Using cipher suite " << sslCipher << "."); | |
470 | if (!SSL_CTX_set_cipher_list(ctx.get(), sslCipher.c_str())) { | |
471 | auto ssl_error = ERR_get_error(); | |
472 | debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << sslCipher << "': " << Security::ErrorString(ssl_error)); | |
473 | return false; | |
474 | } | |
475 | } | |
476 | ||
477 | Ssl::MaybeSetupRsaCallback(ctx); | |
478 | #endif | |
479 | ||
480 | updateContextEecdh(ctx); | |
481 | updateContextCa(ctx); | |
482 | updateContextClientCa(ctx); | |
483 | ||
484 | #if USE_OPENSSL | |
c97d832b | 485 | SSL_CTX_set_mode(ctx.get(), SSL_MODE_NO_AUTO_CHAIN); |
cf487124 AJ |
486 | if (parsedFlags & SSL_FLAG_DONT_VERIFY_DOMAIN) |
487 | SSL_CTX_set_ex_data(ctx.get(), ssl_ctx_ex_index_dont_verify_domain, (void *) -1); | |
488 | ||
489 | Security::SetSessionCacheCallbacks(ctx); | |
490 | #endif | |
491 | return true; | |
492 | } | |
493 | ||
621f4299 AJ |
494 | void |
495 | Security::ServerOptions::updateContextClientCa(Security::ContextPointer &ctx) | |
496 | { | |
497 | #if USE_OPENSSL | |
498 | if (clientCaStack) { | |
499 | ERR_clear_error(); | |
500 | if (STACK_OF(X509_NAME) *clientca = SSL_dup_CA_list(clientCaStack.get())) { | |
501 | SSL_CTX_set_client_CA_list(ctx.get(), clientca); | |
502 | } else { | |
503 | auto ssl_error = ERR_get_error(); | |
504 | debugs(83, DBG_CRITICAL, "ERROR: Failed to dupe the client CA list: " << Security::ErrorString(ssl_error)); | |
505 | return; | |
506 | } | |
507 | ||
983fab6e | 508 | Ssl::ConfigurePeerVerification(ctx, parsedFlags); |
621f4299 AJ |
509 | |
510 | updateContextCrl(ctx); | |
98f951b7 | 511 | updateContextTrust(ctx); |
621f4299 AJ |
512 | |
513 | } else { | |
983fab6e | 514 | Ssl::DisablePeerVerification(ctx); |
621f4299 | 515 | } |
8b082ed9 FC |
516 | #else |
517 | (void)ctx; | |
621f4299 AJ |
518 | #endif |
519 | } | |
520 | ||
104deb98 | 521 | void |
b23f5f9c | 522 | Security::ServerOptions::updateContextEecdh(Security::ContextPointer &ctx) |
104deb98 AJ |
523 | { |
524 | // set Elliptic Curve details into the server context | |
525 | if (!eecdhCurve.isEmpty()) { | |
526 | debugs(83, 9, "Setting Ephemeral ECDH curve to " << eecdhCurve << "."); | |
527 | ||
528 | #if USE_OPENSSL && OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH) | |
742236c7 AJ |
529 | |
530 | Ssl::ForgetErrors(); | |
531 | ||
104deb98 AJ |
532 | int nid = OBJ_sn2nid(eecdhCurve.c_str()); |
533 | if (!nid) { | |
534 | debugs(83, DBG_CRITICAL, "ERROR: Unknown EECDH curve '" << eecdhCurve << "'"); | |
535 | return; | |
536 | } | |
537 | ||
742236c7 | 538 | #if OPENSSL_VERSION_MAJOR < 3 |
104deb98 AJ |
539 | auto ecdh = EC_KEY_new_by_curve_name(nid); |
540 | if (!ecdh) { | |
ea574635 AJ |
541 | const auto x = ERR_get_error(); |
542 | debugs(83, DBG_CRITICAL, "ERROR: Unable to configure Ephemeral ECDH: " << Security::ErrorString(x)); | |
104deb98 AJ |
543 | return; |
544 | } | |
545 | ||
b23f5f9c | 546 | if (!SSL_CTX_set_tmp_ecdh(ctx.get(), ecdh)) { |
ea574635 AJ |
547 | const auto x = ERR_get_error(); |
548 | debugs(83, DBG_CRITICAL, "ERROR: Unable to set Ephemeral ECDH: " << Security::ErrorString(x)); | |
104deb98 AJ |
549 | } |
550 | EC_KEY_free(ecdh); | |
551 | ||
742236c7 AJ |
552 | #else |
553 | // TODO: Support multiple group names via SSL_CTX_set1_groups_list(). | |
554 | if (!SSL_CTX_set1_groups(ctx.get(), &nid, 1)) { | |
555 | debugs(83, DBG_CRITICAL, "ERROR: Unable to set Ephemeral ECDH: " << Ssl::ReportAndForgetErrors); | |
556 | return; | |
557 | } | |
558 | #endif | |
474f076e | 559 | #else |
104deb98 AJ |
560 | debugs(83, DBG_CRITICAL, "ERROR: EECDH is not available in this build." << |
561 | " Please link against OpenSSL>=0.9.8 and ensure OPENSSL_NO_ECDH is not set."); | |
8b082ed9 | 562 | (void)ctx; |
104deb98 AJ |
563 | #endif |
564 | } | |
565 | ||
566 | // set DH parameters into the server context | |
567 | #if USE_OPENSSL | |
b1a522a0 | 568 | if (parsedDhParams) { |
b23f5f9c | 569 | SSL_CTX_set_tmp_dh(ctx.get(), parsedDhParams.get()); |
104deb98 | 570 | } |
474f076e AJ |
571 | #endif |
572 | } | |
573 | ||
cf487124 AJ |
574 | void |
575 | Security::ServerOptions::updateContextSessionId(Security::ContextPointer &ctx) | |
576 | { | |
577 | #if USE_OPENSSL | |
578 | if (!staticContextSessionId.isEmpty()) | |
579 | SSL_CTX_set_session_id_context(ctx.get(), reinterpret_cast<const unsigned char*>(staticContextSessionId.rawContent()), staticContextSessionId.length()); | |
8b082ed9 FC |
580 | #else |
581 | (void)ctx; | |
cf487124 AJ |
582 | #endif |
583 | } | |
584 |