]> git.ipfire.org Git - thirdparty/squid.git/blob - src/security/PeerOptions.cc
SourceFormat Enforcement
[thirdparty/squid.git] / src / security / PeerOptions.cc
1 /*
2 * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
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"
10 #include "base/Packable.h"
11 #include "Debug.h"
12 #include "fatal.h"
13 #include "globals.h"
14 #include "parser/Tokenizer.h"
15 #include "parser/Tokenizer.h"
16 #include "Parsing.h"
17 #include "security/PeerOptions.h"
18
19 #if USE_OPENSSL
20 #include "ssl/support.h"
21 #endif
22
23 Security::PeerOptions Security::ProxyOutgoingConfig;
24
25 Security::PeerOptions::PeerOptions(const Security::PeerOptions &p) :
26 sslOptions(p.sslOptions),
27 caDir(p.caDir),
28 crlFile(p.crlFile),
29 sslCipher(p.sslCipher),
30 sslFlags(p.sslFlags),
31 sslDomain(p.sslDomain),
32 parsedOptions(p.parsedOptions),
33 parsedFlags(p.parsedFlags),
34 certs(p.certs),
35 caFiles(p.caFiles),
36 parsedCrl(p.parsedCrl),
37 sslVersion(p.sslVersion),
38 encryptTransport(p.encryptTransport)
39 {
40 memcpy(&flags, &p.flags, sizeof(flags));
41 }
42
43 void
44 Security::PeerOptions::parse(const char *token)
45 {
46 if (!*token) {
47 // config says just "ssl" or "tls" (or "tls-")
48 encryptTransport = true;
49 return;
50 }
51
52 if (strncmp(token, "disable", 7) == 0) {
53 clear();
54 return;
55 }
56
57 if (strncmp(token, "cert=", 5) == 0) {
58 KeyData t;
59 t.privateKeyFile = t.certFile = SBuf(token + 5);
60 certs.emplace_back(t);
61 } else if (strncmp(token, "key=", 4) == 0) {
62 if (certs.empty() || certs.back().certFile.isEmpty()) {
63 debugs(3, DBG_PARSE_NOTE(1), "ERROR: cert= option must be set before key= is used.");
64 return;
65 }
66 KeyData &t = certs.back();
67 t.privateKeyFile = SBuf(token + 4);
68 } else if (strncmp(token, "version=", 8) == 0) {
69 debugs(0, DBG_PARSE_NOTE(1), "UPGRADE WARNING: SSL version= is deprecated. Use options= to limit protocols instead.");
70 sslVersion = xatoi(token + 8);
71 } else if (strncmp(token, "min-version=", 12) == 0) {
72 tlsMinVersion = SBuf(token + 12);
73 } else if (strncmp(token, "options=", 8) == 0) {
74 sslOptions = SBuf(token + 8);
75 parsedOptions = parseOptions();
76 } else if (strncmp(token, "cipher=", 7) == 0) {
77 sslCipher = SBuf(token + 7);
78 } else if (strncmp(token, "cafile=", 7) == 0) {
79 caFiles.emplace_back(SBuf(token + 7));
80 } else if (strncmp(token, "capath=", 7) == 0) {
81 caDir = SBuf(token + 7);
82 #if !USE_OPENSSL
83 debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl.");
84 #endif
85 } else if (strncmp(token, "crlfile=", 8) == 0) {
86 crlFile = SBuf(token + 8);
87 loadCrlFile();
88 } else if (strncmp(token, "flags=", 6) == 0) {
89 if (parsedFlags != 0) {
90 debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags << " with " << SBuf(token + 6));
91 }
92 sslFlags = SBuf(token + 6);
93 parsedFlags = parseFlags();
94 } else if (strncmp(token, "no-default-ca", 13) == 0) {
95 flags.tlsDefaultCa = false;
96 } else if (strncmp(token, "domain=", 7) == 0) {
97 sslDomain = SBuf(token + 7);
98 } else if (strncmp(token, "no-npn", 6) == 0) {
99 flags.tlsNpn = false;
100 } else {
101 debugs(3, DBG_CRITICAL, "ERROR: Unknown TLS option '" << token << "'");
102 return;
103 }
104
105 encryptTransport = true;
106 }
107
108 void
109 Security::PeerOptions::dumpCfg(Packable *p, const char *pfx) const
110 {
111 if (!encryptTransport) {
112 p->appendf(" %sdisable", pfx);
113 return; // no other settings are relevant
114 }
115
116 for (auto &i : certs) {
117 if (!i.certFile.isEmpty())
118 p->appendf(" %scert=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.certFile));
119
120 if (!i.privateKeyFile.isEmpty() && i.privateKeyFile != i.certFile)
121 p->appendf(" %skey=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.privateKeyFile));
122 }
123
124 if (!sslOptions.isEmpty())
125 p->appendf(" %soptions=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslOptions));
126
127 if (!sslCipher.isEmpty())
128 p->appendf(" %scipher=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslCipher));
129
130 for (auto i : caFiles) {
131 p->appendf(" %scafile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i));
132 }
133
134 if (!caDir.isEmpty())
135 p->appendf(" %scapath=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(caDir));
136
137 if (!crlFile.isEmpty())
138 p->appendf(" %scrlfile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(crlFile));
139
140 if (!sslFlags.isEmpty())
141 p->appendf(" %sflags=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslFlags));
142
143 if (!flags.tlsDefaultCa)
144 p->appendf(" %sno-default-ca", pfx);
145
146 if (!flags.tlsNpn)
147 p->appendf(" %sno-npn", pfx);
148 }
149
150 void
151 Security::PeerOptions::updateTlsVersionLimits()
152 {
153 if (!tlsMinVersion.isEmpty()) {
154 ::Parser::Tokenizer tok(tlsMinVersion);
155 int64_t v = 0;
156 if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 3) {
157 // only account for TLS here - SSL versions are handled by options= parameter
158 // avoid affecting options= parameter in cachemgr config report
159 #if SSL_OP_NO_TLSv1
160 if (v > 0)
161 parsedOptions |= SSL_OP_NO_TLSv1;
162 #endif
163 #if SSL_OP_NO_TLSv1_1
164 if (v > 1)
165 parsedOptions |= SSL_OP_NO_TLSv1_1;
166 #endif
167 #if SSL_OP_NO_TLSv1_2
168 if (v > 2)
169 parsedOptions |= SSL_OP_NO_TLSv1_2;
170 #endif
171
172 } else {
173 debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion);
174 }
175
176 } else if (sslVersion > 2) {
177 // backward compatibility hack for sslversion= configuration
178 // only use if tls-min-version=N.N is not present
179 // values 0-2 for auto and SSLv2 are not supported any longer.
180 // Do it this way so we DO cause changes to options= in cachemgr config report
181 const char *add = NULL;
182 switch (sslVersion) {
183 case 3:
184 add = "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
185 break;
186 case 4:
187 add = "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
188 break;
189 case 5:
190 add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
191 break;
192 case 6:
193 add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
194 break;
195 default: // nothing
196 break;
197 }
198 if (add) {
199 if (!sslOptions.isEmpty())
200 sslOptions.append(",",1);
201 sslOptions.append(add, strlen(add));
202 }
203 sslVersion = 0; // prevent sslOptions being repeatedly appended
204 }
205 }
206
207 Security::ContextPtr
208 Security::PeerOptions::createBlankContext() const
209 {
210 Security::ContextPtr t = nullptr;
211
212 #if USE_OPENSSL
213 Ssl::Initialize();
214
215 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
216 t = SSL_CTX_new(TLS_client_method());
217 #else
218 t = SSL_CTX_new(SSLv23_client_method());
219 #endif
220 if (!t) {
221 const auto x = ERR_error_string(ERR_get_error(), nullptr);
222 fatalf("Failed to allocate TLS client context: %s\n", x);
223 }
224
225 #elif USE_GNUTLS
226 // Initialize for X.509 certificate exchange
227 if (const int x = gnutls_certificate_allocate_credentials(&t)) {
228 fatalf("Failed to allocate TLS client context: error=%d\n", x);
229 }
230
231 #else
232 fatal("Failed to allocate TLS client context: No TLS library\n");
233
234 #endif
235
236 return t;
237 }
238
239 Security::ContextPtr
240 Security::PeerOptions::createClientContext(bool setOptions)
241 {
242 Security::ContextPtr t = nullptr;
243
244 updateTlsVersionLimits();
245
246 #if USE_OPENSSL
247 // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
248 t = sslCreateClientContext(*this, (setOptions ? parsedOptions : 0), parsedFlags);
249
250 #elif USE_GNUTLS && WHEN_READY_FOR_GNUTLS
251 t = createBlankContext();
252
253 #endif
254
255 if (t) {
256 updateContextNpn(t);
257 updateContextCa(t);
258 updateContextCrl(t);
259 }
260
261 return t;
262 }
263
264 /// set of options we can parse and what they map to
265 static struct ssl_option {
266 const char *name;
267 long value;
268
269 } ssl_options[] = {
270
271 #if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
272 {
273 "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
274 },
275 #endif
276 #if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
277 {
278 "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
279 },
280 #endif
281 #if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
282 {
283 "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
284 },
285 #endif
286 #if SSL_OP_SSLEAY_080_CLIENT_DH_BUG
287 {
288 "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
289 },
290 #endif
291 #if SSL_OP_TLS_D5_BUG
292 {
293 "TLS_D5_BUG", SSL_OP_TLS_D5_BUG
294 },
295 #endif
296 #if SSL_OP_TLS_BLOCK_PADDING_BUG
297 {
298 "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
299 },
300 #endif
301 #if SSL_OP_TLS_ROLLBACK_BUG
302 {
303 "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
304 },
305 #endif
306 #if SSL_OP_ALL
307 {
308 "ALL", (long)SSL_OP_ALL
309 },
310 #endif
311 #if SSL_OP_SINGLE_DH_USE
312 {
313 "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
314 },
315 #endif
316 #if SSL_OP_EPHEMERAL_RSA
317 {
318 "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
319 },
320 #endif
321 #if SSL_OP_PKCS1_CHECK_1
322 {
323 "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
324 },
325 #endif
326 #if SSL_OP_PKCS1_CHECK_2
327 {
328 "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
329 },
330 #endif
331 #if SSL_OP_NETSCAPE_CA_DN_BUG
332 {
333 "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
334 },
335 #endif
336 #if SSL_OP_NON_EXPORT_FIRST
337 {
338 "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
339 },
340 #endif
341 #if SSL_OP_CIPHER_SERVER_PREFERENCE
342 {
343 "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
344 },
345 #endif
346 #if SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
347 {
348 "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
349 },
350 #endif
351 #if SSL_OP_NO_SSLv3
352 {
353 "NO_SSLv3", SSL_OP_NO_SSLv3
354 },
355 #endif
356 #if SSL_OP_NO_TLSv1
357 {
358 "NO_TLSv1", SSL_OP_NO_TLSv1
359 },
360 #endif
361 #if SSL_OP_NO_TLSv1_1
362 {
363 "NO_TLSv1_1", SSL_OP_NO_TLSv1_1
364 },
365 #endif
366 #if SSL_OP_NO_TLSv1_2
367 {
368 "NO_TLSv1_2", SSL_OP_NO_TLSv1_2
369 },
370 #endif
371 #if SSL_OP_NO_COMPRESSION
372 {
373 "No_Compression", SSL_OP_NO_COMPRESSION
374 },
375 #endif
376 #if SSL_OP_NO_TICKET
377 {
378 "NO_TICKET", SSL_OP_NO_TICKET
379 },
380 #endif
381 #if SSL_OP_SINGLE_ECDH_USE
382 {
383 "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
384 },
385 #endif
386 {
387 "", 0
388 },
389 {
390 NULL, 0
391 }
392 };
393
394 /**
395 * Pre-parse TLS options= parameter to be applied when the TLS objects created.
396 * Options must not used in the case of peek or stare bump mode.
397 */
398 long
399 Security::PeerOptions::parseOptions()
400 {
401 long op = 0;
402 ::Parser::Tokenizer tok(sslOptions);
403
404 do {
405 enum {
406 MODE_ADD, MODE_REMOVE
407 } mode;
408
409 if (tok.skip('-') || tok.skip('!'))
410 mode = MODE_REMOVE;
411 else {
412 (void)tok.skip('+'); // default action is add. ignore if missing operator
413 mode = MODE_ADD;
414 }
415
416 static const CharacterSet optChars = CharacterSet("TLS-option", "_") + CharacterSet::ALPHA + CharacterSet::DIGIT;
417 int64_t hex = 0;
418 SBuf option;
419 long value = 0;
420
421 if (tok.int64(hex, 16, false)) {
422 /* Special case.. hex specification */
423 value = hex;
424 }
425
426 else if (tok.prefix(option, optChars)) {
427 // find the named option in our supported set
428 for (struct ssl_option *opttmp = ssl_options; opttmp->name; ++opttmp) {
429 if (option.cmp(opttmp->name) == 0) {
430 value = opttmp->value;
431 break;
432 }
433 }
434 }
435
436 if (value) {
437 switch (mode) {
438 case MODE_ADD:
439 op |= value;
440 break;
441 case MODE_REMOVE:
442 op &= ~value;
443 break;
444 }
445 } else {
446 debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option);
447 }
448
449 static const CharacterSet delims("TLS-option-delim",":,");
450 if (!tok.skipAll(delims) && !tok.atEnd()) {
451 fatalf("Unknown TLS option '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
452 }
453
454 } while (!tok.atEnd());
455
456 #if SSL_OP_NO_SSLv2
457 // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
458 op = op | SSL_OP_NO_SSLv2;
459 #endif
460 return op;
461 }
462
463 /**
464 * Parses the TLS flags squid.conf parameter
465 */
466 long
467 Security::PeerOptions::parseFlags()
468 {
469 if (sslFlags.isEmpty())
470 return 0;
471
472 static struct {
473 SBuf label;
474 long mask;
475 } flagTokens[] = {
476 { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA },
477 { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH },
478 { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER },
479 { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN },
480 { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE },
481 #if X509_V_FLAG_CRL_CHECK
482 { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL },
483 { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL },
484 #endif
485 { SBuf(), 0 }
486 };
487
488 ::Parser::Tokenizer tok(sslFlags);
489 static const CharacterSet delims("Flag-delimiter", ":,");
490
491 long fl = 0;
492 do {
493 long found = 0;
494 for (size_t i = 0; flagTokens[i].mask; ++i) {
495 if (tok.skip(flagTokens[i].label) == 0) {
496 found = flagTokens[i].mask;
497 break;
498 }
499 }
500 if (!found)
501 fatalf("Unknown TLS flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
502 if (found == SSL_FLAG_NO_DEFAULT_CA) {
503 debugs(83, DBG_PARSE_NOTE(2), "UPGRADE WARNING: flags=NO_DEFAULT_CA is deprecated. Use tls-no-default-ca instead.");
504 flags.tlsDefaultCa = false;
505 } else
506 fl |= found;
507 } while (tok.skipOne(delims));
508
509 return fl;
510 }
511
512 /// Load a CRLs list stored in the file whose /path/name is in crlFile
513 /// replaces any CRL loaded previously
514 void
515 Security::PeerOptions::loadCrlFile()
516 {
517 parsedCrl.clear();
518 if (crlFile.isEmpty())
519 return;
520
521 #if USE_OPENSSL
522 BIO *in = BIO_new_file(crlFile.c_str(), "r");
523 if (!in) {
524 debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile);
525 return;
526 }
527
528 while (X509_CRL *crl = PEM_read_bio_X509_CRL(in,NULL,NULL,NULL)) {
529 parsedCrl.emplace_back(Security::CrlPointer(crl));
530 }
531 BIO_free(in);
532 #endif
533 }
534
535 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
536 // Dummy next_proto_neg callback
537 static int
538 ssl_next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg)
539 {
540 static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'};
541 (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos));
542 return SSL_TLSEXT_ERR_OK;
543 }
544 #endif
545
546 void
547 Security::PeerOptions::updateContextNpn(Security::ContextPtr &ctx)
548 {
549 if (!flags.tlsNpn)
550 return;
551
552 #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg)
553 SSL_CTX_set_next_proto_select_cb(ctx, &ssl_next_proto_cb, nullptr);
554 #endif
555
556 // NOTE: GnuTLS does not support the obsolete NPN extension.
557 // it does support ALPN per-session, not per-context.
558 }
559
560 void
561 Security::PeerOptions::updateContextCa(Security::ContextPtr &ctx)
562 {
563 debugs(83, 8, "Setting CA certificate locations.");
564
565 for (auto i : caFiles) {
566 #if USE_OPENSSL
567 if (!SSL_CTX_load_verify_locations(ctx, i.c_str(), caDir.c_str())) {
568 const int ssl_error = ERR_get_error();
569 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate locations: " << ERR_error_string(ssl_error, NULL));
570 }
571 #elif USE_GNUTLS
572 if (gnutls_certificate_set_x509_trust_file(ctx, i.c_str(), GNUTLS_X509_FMT_PEM) < 0) {
573 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location: " << i);
574 }
575 #endif
576 }
577
578 if (!flags.tlsDefaultCa)
579 return;
580
581 #if USE_OPENSSL
582 if (!SSL_CTX_set_default_verify_paths(ctx)) {
583 const int ssl_error = ERR_get_error();
584 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default trusted CA : "
585 << ERR_error_string(ssl_error, NULL));
586 }
587 #elif USE_GNUTLS
588 if (gnutls_certificate_set_x509_system_trust(ctx) != GNUTLS_E_SUCCESS) {
589 debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default trusted CA.");
590 }
591 #endif
592 }
593
594 void
595 Security::PeerOptions::updateContextCrl(Security::ContextPtr &ctx)
596 {
597 #if USE_OPENSSL
598 bool verifyCrl = false;
599 X509_STORE *st = SSL_CTX_get_cert_store(ctx);
600 if (parsedCrl.size()) {
601 for (auto &i : parsedCrl) {
602 if (!X509_STORE_add_crl(st, i.get()))
603 debugs(83, 2, "WARNING: Failed to add CRL");
604 else
605 verifyCrl = true;
606 }
607 }
608
609 #if X509_V_FLAG_CRL_CHECK
610 if ((parsedFlags & SSL_FLAG_VERIFY_CRL_ALL))
611 X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
612 else if (verifyCrl || (parsedFlags & SSL_FLAG_VERIFY_CRL))
613 X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK);
614 #endif
615
616 #endif /* USE_OPENSSL */
617 }
618
619 void
620 parse_securePeerOptions(Security::PeerOptions *opt)
621 {
622 while(const char *token = ConfigParser::NextToken())
623 opt->parse(token);
624 }
625