]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/ServerOptions.cc
Docs: Copyright updates for 2018 (#114)
[thirdparty/squid.git] / src / security / ServerOptions.cc
CommitLineData
474f076e 1/*
5b74111a 2 * Copyright (C) 1996-2018 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"
474f076e 11#include "base/Packable.h"
cf487124
AJ
12#include "cache_cf.h"
13#include "fatal.h"
474f076e
AJ
14#include "globals.h"
15#include "security/ServerOptions.h"
cf487124
AJ
16#include "security/Session.h"
17#include "SquidConfig.h"
0a28c16a
AJ
18#if USE_OPENSSL
19#include "ssl/support.h"
20#endif
474f076e
AJ
21
22#if HAVE_OPENSSL_ERR_H
23#include <openssl/err.h>
24#endif
25#if HAVE_OPENSSL_X509_H
26#include <openssl/x509.h>
27#endif
28
621f4299
AJ
29Security::ServerOptions &
30Security::ServerOptions::operator =(const Security::ServerOptions &old) {
31 if (this != &old) {
32 Security::PeerOptions::operator =(old);
33 clientCaFile = old.clientCaFile;
34 dh = old.dh;
35 dhParamsFile = old.dhParamsFile;
36 eecdhCurve = old.eecdhCurve;
37 parsedDhParams = old.parsedDhParams;
38#if USE_OPENSSL
39 if (auto *stk = SSL_dup_CA_list(old.clientCaStack.get()))
40 clientCaStack = Security::ServerOptions::X509_NAME_STACK_Pointer(stk);
cf487124 41 else
621f4299 42#endif
cf487124
AJ
43 clientCaStack = nullptr;
44
45 staticContextSessionId = old.staticContextSessionId;
46 generateHostCertificates = old.generateHostCertificates;
47 signingCert = old.signingCert;
48 signPkey = old.signPkey;
49 certsToChain = old.certsToChain;
50 untrustedSigningCert = old.untrustedSigningCert;
51 untrustedSignPkey = old.untrustedSignPkey;
52 dynamicCertMemCacheSize = old.dynamicCertMemCacheSize;
621f4299
AJ
53 }
54 return *this;
55}
56
474f076e
AJ
57void
58Security::ServerOptions::parse(const char *token)
59{
60 if (!*token) {
61 // config says just "ssl" or "tls" (or "tls-")
62 encryptTransport = true;
63 return;
64 }
65
66 // parse the server-only options
67 if (strncmp(token, "dh=", 3) == 0) {
68 // clear any previous Diffi-Helman configuration
69 dh.clear();
70 dhParamsFile.clear();
71 eecdhCurve.clear();
72
73 dh.append(token + 3);
74
75 if (!dh.isEmpty()) {
76 auto pos = dh.find(':');
77 if (pos != SBuf::npos) { // tls-dh=eecdhCurve:dhParamsFile
78 eecdhCurve = dh.substr(0,pos);
79 dhParamsFile = dh.substr(pos+1);
80 } else { // tls-dh=dhParamsFile
81 dhParamsFile = dh;
82 // empty eecdhCurve means "do not use EECDH"
83 }
84 }
85
104deb98
AJ
86 loadDhParams();
87
474f076e
AJ
88 } else if (strncmp(token, "dhparams=", 9) == 0) {
89 if (!eecdhCurve.isEmpty()) {
90 debugs(83, DBG_PARSE_NOTE(1), "UPGRADE WARNING: EECDH settings in tls-dh= override dhparams=");
91 return;
92 }
93
94 // backward compatibility for dhparams= configuration
95 dh.clear();
96 dh.append(token + 9);
97 dhParamsFile = dh;
98
104deb98
AJ
99 loadDhParams();
100
cf487124
AJ
101 } else if (strncmp(token, "dynamic_cert_mem_cache_size=", 28) == 0) {
102 parseBytesOptionValue(&dynamicCertMemCacheSize, "bytes", token + 28);
103 // XXX: parseBytesOptionValue() self_destruct()s on invalid values,
104 // probably making this comparison and misleading ERROR unnecessary.
105 if (dynamicCertMemCacheSize == std::numeric_limits<size_t>::max()) {
106 debugs(3, DBG_CRITICAL, "ERROR: Cannot allocate memory for '" << token << "'. Using default of 4MB instead.");
107 dynamicCertMemCacheSize = 4*1024*1024; // 4 MB
108 }
109
110 } else if (strcmp(token, "generate-host-certificates") == 0) {
111 generateHostCertificates = true;
112 } else if (strcmp(token, "generate-host-certificates=on") == 0) {
113 generateHostCertificates = true;
114 } else if (strcmp(token, "generate-host-certificates=off") == 0) {
115 generateHostCertificates = false;
116
117 } else if (strncmp(token, "context=", 8) == 0) {
118#if USE_OPENSSL
119 staticContextSessionId = SBuf(token+8);
120 // to hide its arguably sensitive value, do not print token in these debugs
121 if (staticContextSessionId.length() > SSL_MAX_SSL_SESSION_ID_LENGTH) {
122 debugs(83, DBG_CRITICAL, "FATAL: Option 'context=' value is too long. Maximum " << SSL_MAX_SSL_SESSION_ID_LENGTH << " characters.");
123 self_destruct();
124 }
125#else
126 debugs(83, DBG_PARSE_NOTE(DBG_IMPORTANT), "WARNING: Option 'context=' requires --with-openssl. Ignoring.");
127#endif
128
474f076e
AJ
129 } else {
130 // parse generic TLS options
131 Security::PeerOptions::parse(token);
132 }
133}
134
135void
136Security::ServerOptions::dumpCfg(Packable *p, const char *pfx) const
137{
138 // dump out the generic TLS options
139 Security::PeerOptions::dumpCfg(p, pfx);
140
141 if (!encryptTransport)
142 return; // no other settings are relevant
143
144 // dump the server-only options
145 if (!dh.isEmpty())
146 p->appendf(" %sdh=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(dh));
cf487124
AJ
147
148 if (!generateHostCertificates)
149 p->appendf(" %sgenerate-host-certificates=off", pfx);
150
151 if (dynamicCertMemCacheSize != 4*1024*1024) // 4MB default, no 'tls-' prefix
152 p->appendf(" dynamic_cert_mem_cache_size=%" PRIuSIZE "bytes", dynamicCertMemCacheSize);
153
154 if (!staticContextSessionId.isEmpty())
155 p->appendf(" %scontext=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(staticContextSessionId));
474f076e
AJ
156}
157
64769c79 158Security::ContextPointer
885f0ecf
AJ
159Security::ServerOptions::createBlankContext() const
160{
64769c79 161 Security::ContextPointer ctx;
885f0ecf 162#if USE_OPENSSL
0a28c16a
AJ
163 Ssl::Initialize();
164
8d56fe55 165#if HAVE_OPENSSL_SERVER_METHOD
64769c79 166 SSL_CTX *t = SSL_CTX_new(TLS_server_method());
885f0ecf 167#else
64769c79 168 SSL_CTX *t = SSL_CTX_new(SSLv23_server_method());
885f0ecf
AJ
169#endif
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
AJ
175
176#elif USE_GNUTLS
177 // Initialize for X.509 certificate exchange
64769c79 178 gnutls_certificate_credentials_t t;
885f0ecf 179 if (const int 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
9ad528b8 192bool
c75aba02
AJ
193Security::ServerOptions::createStaticServerContext(AnyP::PortCfg &port)
194{
195 updateTlsVersionLimits();
196
9ad528b8 197 Security::ContextPointer t(createBlankContext());
c75aba02
AJ
198 if (t) {
199#if USE_OPENSSL
9ad528b8
AJ
200 if (!Ssl::InitServerContext(t, port))
201 return false;
c75aba02 202#endif
621f4299
AJ
203 if (!loadClientCaFile())
204 return false;
c75aba02
AJ
205 }
206
9ad528b8
AJ
207 staticContext = std::move(t);
208 return bool(staticContext);
c75aba02
AJ
209}
210
cf487124
AJ
211void
212Security::ServerOptions::createSigningContexts(AnyP::PortCfg &port)
213{
214 const char *portType = AnyP::ProtocolType_str[port.transport.protocol];
215 if (!certs.empty()) {
216#if USE_OPENSSL
217 Security::KeyData &keys = certs.front();
218 Ssl::readCertChainAndPrivateKeyFromFiles(signingCert, signPkey, certsToChain, keys.certFile.c_str(), keys.privateKeyFile.c_str());
219#else
220 char buf[128];
221 fatalf("Directive '%s_port %s' requires --with-openssl.", portType, port.s.toUrl(buf, sizeof(buf)));
222#endif
223 }
224
225 if (!signingCert) {
226 char buf[128];
227 fatalf("No valid signing SSL certificate configured for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
228 }
229
230 if (!signPkey)
231 debugs(3, DBG_IMPORTANT, "No SSL private key configured for " << portType << "_port " << port.s);
232
233#if USE_OPENSSL
234 Ssl::generateUntrustedCert(untrustedSigningCert, untrustedSignPkey, signingCert, signPkey);
235#endif
236
237 if (!untrustedSigningCert) {
238 char buf[128];
239 fatalf("Unable to generate signing SSL certificate for untrusted sites for %s_port %s", portType, port.s.toUrl(buf, sizeof(buf)));
240 }
241
242 if (!createStaticServerContext(port)) {
243 char buf[128];
244 fatalf("%s_port %s initialization error", portType, port.s.toUrl(buf, sizeof(buf)));
245 }
246}
247
621f4299
AJ
248void
249Security::ServerOptions::syncCaFiles()
250{
251 // if caFiles is set, just use that
252 if (caFiles.size())
253 return;
254
255 // otherwise fall back to clientca if it is defined
256 if (!clientCaFile.isEmpty())
257 caFiles.emplace_back(clientCaFile);
258}
259
260/// load clientca= file (if any) into memory.
261/// \retval true clientca is not set, or loaded successfully
262/// \retval false unable to load the file, or not using OpenSSL
263bool
264Security::ServerOptions::loadClientCaFile()
265{
266 if (clientCaFile.isEmpty())
267 return true;
268
269#if USE_OPENSSL
270 auto *stk = SSL_load_client_CA_file(clientCaFile.c_str());
271 clientCaStack = Security::ServerOptions::X509_NAME_STACK_Pointer(stk);
272#endif
273 if (!clientCaStack) {
274 debugs(83, DBG_CRITICAL, "FATAL: Unable to read client CAs from file: " << clientCaFile);
275 }
276
277 return bool(clientCaStack);
278}
279
474f076e 280void
104deb98 281Security::ServerOptions::loadDhParams()
474f076e 282{
104deb98 283 if (dhParamsFile.isEmpty())
474f076e
AJ
284 return;
285
104deb98
AJ
286#if USE_OPENSSL
287 DH *dhp = nullptr;
288 if (FILE *in = fopen(dhParamsFile.c_str(), "r")) {
289 dhp = PEM_read_DHparams(in, NULL, NULL, NULL);
290 fclose(in);
474f076e
AJ
291 }
292
104deb98
AJ
293 if (!dhp) {
294 debugs(83, DBG_IMPORTANT, "WARNING: Failed to read DH parameters '" << dhParamsFile << "'");
474f076e
AJ
295 return;
296 }
297
104deb98
AJ
298 int codes;
299 if (DH_check(dhp, &codes) == 0) {
300 if (codes) {
301 debugs(83, DBG_IMPORTANT, "WARNING: Failed to verify DH parameters '" << dhParamsFile << "' (" << std::hex << codes << ")");
302 DH_free(dhp);
303 dhp = nullptr;
304 }
474f076e 305 }
104deb98 306
35b3559c 307 parsedDhParams.resetWithoutLocking(dhp);
104deb98
AJ
308#endif
309}
310
cf487124
AJ
311bool
312Security::ServerOptions::updateContextConfig(Security::ContextPointer &ctx)
313{
314 updateContextOptions(ctx);
315 updateContextSessionId(ctx);
316
317#if USE_OPENSSL
318 if (parsedFlags & SSL_FLAG_NO_SESSION_REUSE) {
319 SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF);
320 }
321
322 if (Config.SSL.unclean_shutdown) {
323 debugs(83, 5, "Enabling quiet SSL shutdowns (RFC violation).");
324 SSL_CTX_set_quiet_shutdown(ctx.get(), 1);
325 }
326
327 if (!sslCipher.isEmpty()) {
328 debugs(83, 5, "Using cipher suite " << sslCipher << ".");
329 if (!SSL_CTX_set_cipher_list(ctx.get(), sslCipher.c_str())) {
330 auto ssl_error = ERR_get_error();
331 debugs(83, DBG_CRITICAL, "ERROR: Failed to set SSL cipher suite '" << sslCipher << "': " << Security::ErrorString(ssl_error));
332 return false;
333 }
334 }
335
336 Ssl::MaybeSetupRsaCallback(ctx);
337#endif
338
339 updateContextEecdh(ctx);
340 updateContextCa(ctx);
341 updateContextClientCa(ctx);
342
343#if USE_OPENSSL
344 if (parsedFlags & SSL_FLAG_DONT_VERIFY_DOMAIN)
345 SSL_CTX_set_ex_data(ctx.get(), ssl_ctx_ex_index_dont_verify_domain, (void *) -1);
346
347 Security::SetSessionCacheCallbacks(ctx);
348#endif
349 return true;
350}
351
621f4299
AJ
352void
353Security::ServerOptions::updateContextClientCa(Security::ContextPointer &ctx)
354{
355#if USE_OPENSSL
356 if (clientCaStack) {
357 ERR_clear_error();
358 if (STACK_OF(X509_NAME) *clientca = SSL_dup_CA_list(clientCaStack.get())) {
359 SSL_CTX_set_client_CA_list(ctx.get(), clientca);
360 } else {
361 auto ssl_error = ERR_get_error();
362 debugs(83, DBG_CRITICAL, "ERROR: Failed to dupe the client CA list: " << Security::ErrorString(ssl_error));
363 return;
364 }
365
366 if (parsedFlags & SSL_FLAG_DELAYED_AUTH) {
367 debugs(83, 9, "Not requesting client certificates until acl processing requires one");
368 SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, nullptr);
369 } else {
370 debugs(83, 9, "Requiring client certificates.");
371 Ssl::SetupVerifyCallback(ctx);
372 }
373
374 updateContextCrl(ctx);
375
376 } else {
377 debugs(83, 9, "Not requiring any client certificates");
378 SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, NULL);
379 }
380#endif
381}
382
104deb98 383void
b23f5f9c 384Security::ServerOptions::updateContextEecdh(Security::ContextPointer &ctx)
104deb98
AJ
385{
386 // set Elliptic Curve details into the server context
387 if (!eecdhCurve.isEmpty()) {
388 debugs(83, 9, "Setting Ephemeral ECDH curve to " << eecdhCurve << ".");
389
390#if USE_OPENSSL && OPENSSL_VERSION_NUMBER >= 0x0090800fL && !defined(OPENSSL_NO_ECDH)
391 int nid = OBJ_sn2nid(eecdhCurve.c_str());
392 if (!nid) {
393 debugs(83, DBG_CRITICAL, "ERROR: Unknown EECDH curve '" << eecdhCurve << "'");
394 return;
395 }
396
397 auto ecdh = EC_KEY_new_by_curve_name(nid);
398 if (!ecdh) {
ea574635
AJ
399 const auto x = ERR_get_error();
400 debugs(83, DBG_CRITICAL, "ERROR: Unable to configure Ephemeral ECDH: " << Security::ErrorString(x));
104deb98
AJ
401 return;
402 }
403
b23f5f9c 404 if (!SSL_CTX_set_tmp_ecdh(ctx.get(), ecdh)) {
ea574635
AJ
405 const auto x = ERR_get_error();
406 debugs(83, DBG_CRITICAL, "ERROR: Unable to set Ephemeral ECDH: " << Security::ErrorString(x));
104deb98
AJ
407 }
408 EC_KEY_free(ecdh);
409
474f076e 410#else
104deb98
AJ
411 debugs(83, DBG_CRITICAL, "ERROR: EECDH is not available in this build." <<
412 " Please link against OpenSSL>=0.9.8 and ensure OPENSSL_NO_ECDH is not set.");
413#endif
414 }
415
416 // set DH parameters into the server context
417#if USE_OPENSSL
b1a522a0 418 if (parsedDhParams) {
b23f5f9c 419 SSL_CTX_set_tmp_dh(ctx.get(), parsedDhParams.get());
104deb98 420 }
474f076e
AJ
421#endif
422}
423
cf487124
AJ
424void
425Security::ServerOptions::updateContextSessionId(Security::ContextPointer &ctx)
426{
427#if USE_OPENSSL
428 if (!staticContextSessionId.isEmpty())
429 SSL_CTX_set_session_id_context(ctx.get(), reinterpret_cast<const unsigned char*>(staticContextSessionId.rawContent()), staticContextSessionId.length());
430#endif
431}
432