]> git.ipfire.org Git - thirdparty/squid.git/blame - src/security/PeerOptions.cc
Fix cachemgr 'config' report output for TLS options
[thirdparty/squid.git] / src / security / PeerOptions.cc
CommitLineData
9a2f63e7 1/*
be75380c 2 * Copyright (C) 1996-2015 The Squid Software Foundation and contributors
9a2f63e7
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"
8250ca31 10#include "base/Packable.h"
0b0e0864 11#include "Debug.h"
6bd62757 12#include "fatal.h"
0b0e0864 13#include "globals.h"
1cc44095 14#include "parser/Tokenizer.h"
0b0e0864 15#include "Parsing.h"
b24e9ae7 16#include "parser/Tokenizer.h"
9a2f63e7
AJ
17#include "security/PeerOptions.h"
18
19#if USE_OPENSSL
20#include "ssl/support.h"
21#endif
22
7e62a74f 23Security::PeerOptions Security::ProxyOutgoingConfig;
195f8adb 24
9a622f3e
AJ
25Security::PeerOptions::PeerOptions(const Security::PeerOptions &p) :
26 certFile(p.certFile),
27 privateKeyFile(p.privateKeyFile),
28 sslOptions(p.sslOptions),
29 caFile(p.caFile),
30 caDir(p.caDir),
31 crlFile(p.crlFile),
32 sslCipher(p.sslCipher),
33 sslFlags(p.sslFlags),
34 sslDomain(p.sslDomain),
35 parsedOptions(p.parsedOptions),
36 parsedFlags(p.parsedFlags),
37 sslVersion(p.sslVersion),
38 encryptTransport(p.encryptTransport)
39{
40}
41
0b0e0864
AJ
42void
43Security::PeerOptions::parse(const char *token)
44{
41ee8990
AJ
45 if (strncmp(token, "disable", 7) == 0) {
46 clear();
47 } else if (strncmp(token, "cert=", 5) == 0) {
0b0e0864 48 certFile = SBuf(token + 5);
1f1f29e8
AJ
49 if (privateKeyFile.isEmpty())
50 privateKeyFile = certFile;
0b0e0864
AJ
51 } else if (strncmp(token, "key=", 4) == 0) {
52 privateKeyFile = SBuf(token + 4);
53 if (certFile.isEmpty()) {
9a622f3e 54 debugs(3, DBG_PARSE_NOTE(1), "WARNING: cert= option needs to be set before key= is used.");
0b0e0864
AJ
55 certFile = privateKeyFile;
56 }
57 } else if (strncmp(token, "version=", 8) == 0) {
1cc44095 58 debugs(0, DBG_PARSE_NOTE(1), "UPGRADE WARNING: SSL version= is deprecated. Use options= to limit protocols instead.");
0b0e0864 59 sslVersion = xatoi(token + 8);
1cc44095
AJ
60 } else if (strncmp(token, "min-version=", 12) == 0) {
61 tlsMinVersion = SBuf(token + 12);
0b0e0864
AJ
62 } else if (strncmp(token, "options=", 8) == 0) {
63 sslOptions = SBuf(token + 8);
36092741
AJ
64#if USE_OPENSSL
65 // Pre-parse SSL client options to be applied when the client SSL objects created.
66 // Options must not used in the case of peek or stare bump mode.
67 // XXX: performance regression. c_str() can reallocate
6bd62757 68 parsedOptions = Security::ParseOptions(sslOptions.c_str());
36092741 69#endif
0b0e0864
AJ
70 } else if (strncmp(token, "cipher=", 7) == 0) {
71 sslCipher = SBuf(token + 7);
72 } else if (strncmp(token, "cafile=", 7) == 0) {
73 caFile = SBuf(token + 7);
74 } else if (strncmp(token, "capath=", 7) == 0) {
75 caDir = SBuf(token + 7);
76 } else if (strncmp(token, "crlfile=", 8) == 0) {
77 crlFile = SBuf(token + 8);
78 } else if (strncmp(token, "flags=", 6) == 0) {
b24e9ae7 79 if (parsedFlags != 0) {
9a622f3e 80 debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags << " with " << SBuf(token + 6));
b24e9ae7 81 }
0b0e0864 82 sslFlags = SBuf(token + 6);
b24e9ae7 83 parsedFlags = Security::ParseFlags(sslFlags);
0b0e0864
AJ
84 } else if (strncmp(token, "domain=", 7) == 0) {
85 sslDomain = SBuf(token + 7);
9a622f3e
AJ
86 } else {
87 debugs(3, DBG_CRITICAL, "ERROR: Unknown TLS option '" << token << "'");
0b0e0864
AJ
88 }
89}
90
8250ca31
AJ
91void
92Security::PeerOptions::dumpCfg(Packable *p, const char *pfx) const
93{
94 if (!encryptTransport) {
95 p->appendf(" %sdisable", pfx);
96 return; // no other settings are relevant
97 }
98
99 if (!certFile.isEmpty())
100 p->appendf(" %scert=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(certFile));
101
102 if (!privateKeyFile.isEmpty() && privateKeyFile != certFile)
103 p->appendf(" %skey=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(privateKeyFile));
104
105 if (!sslOptions.isEmpty())
106 p->appendf(" %soptions=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslOptions));
107
108 if (!sslCipher.isEmpty())
109 p->appendf(" %scipher=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslCipher));
110
111 if (!caFile.isEmpty())
112 p->appendf(" %scafile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(caFile));
113
114 if (!caDir.isEmpty())
115 p->appendf(" %scapath=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(caDir));
116
117 if (!crlFile.isEmpty())
118 p->appendf(" %scrlfile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(crlFile));
119
120 if (!sslFlags.isEmpty())
121 p->appendf(" %sflags=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslFlags));
122}
123
585c27eb
AJ
124void
125Security::PeerOptions::updateTlsVersionLimits()
9a2f63e7 126{
1cc44095
AJ
127 if (!tlsMinVersion.isEmpty()) {
128 ::Parser::Tokenizer tok(tlsMinVersion);
129 int64_t v = 0;
130 if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 2) {
131 // only account for TLS here - SSL versions are handled by options= parameter
8250ca31
AJ
132 // avoid affectign options= parameter in cachemgr config report
133#if SSL_OP_NO_TLSv1
1cc44095 134 if (v > 0)
8250ca31
AJ
135 parsedOptions |= SSL_OP_NO_TLSv1;
136#endif
137#if SSL_OP_NO_TLSv1_1
1cc44095 138 if (v > 1)
8250ca31
AJ
139 parsedOptions |= SSL_OP_NO_TLSv1_1;
140#endif
141#if SSL_OP_NO_TLSv1_2
1cc44095 142 if (v > 2)
8250ca31
AJ
143 parsedOptions |= SSL_OP_NO_TLSv1_2;
144#endif
1cc44095
AJ
145
146 } else {
147 debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion);
148 }
149
150 } else if (sslVersion > 2) {
151 // backward compatibility hack for sslversion= configuration
152 // only use if tls-min-version=N.N is not present
8250ca31
AJ
153 // values 0-2 for auto and SSLv2 are not supported any longer.
154 // Do it this way so we DO cause changes to options= in cachemgr config report
1cc44095
AJ
155 const char *add = NULL;
156 switch (sslVersion) {
157 case 3:
158 add = "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
159 break;
160 case 4:
161 add = "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
162 break;
163 case 5:
164 add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
165 break;
166 case 6:
167 add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
168 break;
169 default: // nothing
170 break;
171 }
172 if (add) {
173 if (!sslOptions.isEmpty())
174 sslOptions.append(",",1);
175 sslOptions.append(add, strlen(add));
176 }
177 sslVersion = 0; // prevent sslOptions being repeatedly appended
178 }
585c27eb 179}
1cc44095 180
9a2f63e7
AJ
181// XXX: make a GnuTLS variant
182Security::ContextPointer
a465e144 183Security::PeerOptions::createClientContext(bool setOptions)
9a2f63e7
AJ
184{
185 Security::ContextPointer t = NULL;
186
585c27eb 187 updateTlsVersionLimits();
9a2f63e7 188#if USE_OPENSSL
1f1f29e8 189 // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
1cc44095 190 t = sslCreateClientContext(certFile.c_str(), privateKeyFile.c_str(), sslCipher.c_str(),
585c27eb 191 (setOptions ? parsedOptions : 0), parsedFlags,
1cc44095 192 caFile.c_str(), caDir.c_str(), crlFile.c_str());
9a2f63e7 193#endif
36092741 194
9a2f63e7
AJ
195 return t;
196}
1f1f29e8 197
6bd62757
AJ
198/// set of options we can parse and what they map to
199static struct ssl_option {
200 const char *name;
201 long value;
202
203} ssl_options[] = {
204
205#if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
206 {
207 "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
208 },
209#endif
210#if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
211 {
212 "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
213 },
214#endif
215#if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
216 {
217 "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
218 },
219#endif
220#if SSL_OP_SSLEAY_080_CLIENT_DH_BUG
221 {
222 "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG
223 },
224#endif
225#if SSL_OP_TLS_D5_BUG
226 {
227 "TLS_D5_BUG", SSL_OP_TLS_D5_BUG
228 },
229#endif
230#if SSL_OP_TLS_BLOCK_PADDING_BUG
231 {
232 "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG
233 },
234#endif
235#if SSL_OP_TLS_ROLLBACK_BUG
236 {
237 "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG
238 },
239#endif
240#if SSL_OP_ALL
241 {
242 "ALL", (long)SSL_OP_ALL
243 },
244#endif
245#if SSL_OP_SINGLE_DH_USE
246 {
247 "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE
248 },
249#endif
250#if SSL_OP_EPHEMERAL_RSA
251 {
252 "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA
253 },
254#endif
255#if SSL_OP_PKCS1_CHECK_1
256 {
257 "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1
258 },
259#endif
260#if SSL_OP_PKCS1_CHECK_2
261 {
262 "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2
263 },
264#endif
265#if SSL_OP_NETSCAPE_CA_DN_BUG
266 {
267 "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG
268 },
269#endif
270#if SSL_OP_NON_EXPORT_FIRST
271 {
272 "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST
273 },
274#endif
275#if SSL_OP_CIPHER_SERVER_PREFERENCE
276 {
277 "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE
278 },
279#endif
280#if SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
281 {
282 "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
283 },
284#endif
285#if SSL_OP_NO_SSLv3
286 {
287 "NO_SSLv3", SSL_OP_NO_SSLv3
288 },
289#endif
290#if SSL_OP_NO_TLSv1
291 {
292 "NO_TLSv1", SSL_OP_NO_TLSv1
293 },
294#endif
295#if SSL_OP_NO_TLSv1_1
296 {
297 "NO_TLSv1_1", SSL_OP_NO_TLSv1_1
298 },
299#endif
300#if SSL_OP_NO_TLSv1_2
301 {
302 "NO_TLSv1_2", SSL_OP_NO_TLSv1_2
303 },
304#endif
305#if SSL_OP_NO_COMPRESSION
306 {
307 "No_Compression", SSL_OP_NO_COMPRESSION
308 },
309#endif
310#if SSL_OP_NO_TICKET
311 {
312 "NO_TICKET", SSL_OP_NO_TICKET
313 },
585c27eb
AJ
314#endif
315#if SSL_OP_SINGLE_ECDH_USE
316 {
317 "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
318 },
6bd62757
AJ
319#endif
320 {
321 "", 0
322 },
323 {
324 NULL, 0
325 }
326};
327
328long
329Security::ParseOptions(const char *options)
330{
331 long op = 0;
332 char *tmp;
333 char *option;
334
335 if (options) {
336
337 tmp = xstrdup(options);
338 option = strtok(tmp, ":,");
339
340 while (option) {
341
342 enum {
343 MODE_ADD, MODE_REMOVE
344 } mode;
345
346 switch (*option) {
347
348 case '!':
349
350 case '-':
351 mode = MODE_REMOVE;
352 ++option;
353 break;
354
355 case '+':
356 mode = MODE_ADD;
357 ++option;
358 break;
359
360 default:
361 mode = MODE_ADD;
362 break;
363 }
364
365 struct ssl_option *opt = NULL;
366 for (struct ssl_option *opttmp = ssl_options; opttmp->name; ++opttmp) {
367 if (strcmp(opttmp->name, option) == 0) {
368 opt = opttmp;
369 break;
370 }
371 }
372
373 long value = 0;
374 if (opt)
375 value = opt->value;
376 else if (strncmp(option, "0x", 2) == 0) {
377 /* Special case.. hex specification */
378 value = strtol(option + 2, NULL, 16);
379 } else {
8250ca31 380 fatalf("Unknown TLS option '%s'", option);
6bd62757
AJ
381 value = 0; /* Keep GCC happy */
382 }
383
384 switch (mode) {
385
386 case MODE_ADD:
387 op |= value;
388 break;
389
390 case MODE_REMOVE:
391 op &= ~value;
392 break;
393 }
394
395 option = strtok(NULL, ":,");
396 }
397
398 safe_free(tmp);
399 }
400
401#if SSL_OP_NO_SSLv2
402 // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
403 op = op | SSL_OP_NO_SSLv2;
404#endif
405 return op;
406}
407
b24e9ae7
AJ
408long
409Security::ParseFlags(const SBuf &flags)
410{
411 if (flags.isEmpty())
412 return 0;
413
414 static struct {
415 SBuf label;
416 long mask;
417 } flagTokens[] = {
418 { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA },
419 { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH },
420 { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER },
421 { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN },
422 { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE },
423#if X509_V_FLAG_CRL_CHECK
424 { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL },
425 { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL },
426#endif
427 { SBuf(), 0 }
428 };
429
430 ::Parser::Tokenizer tok(flags);
431 static const CharacterSet delims("Flag-delimiter", ":,");
432
433 long fl = 0;
434 do {
435 long found = 0;
436 for (size_t i = 0; flagTokens[i].mask; ++i) {
437 if (tok.skip(flagTokens[i].label) == 0) {
438 found = flagTokens[i].mask;
439 break;
440 }
441 }
442 if (!found)
8250ca31 443 fatalf("Unknown TLS flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
b24e9ae7
AJ
444 fl |= found;
445 } while (tok.skipOne(delims));
446
447 return fl;
448}
449
1f1f29e8
AJ
450void
451parse_securePeerOptions(Security::PeerOptions *opt)
452{
453 while(const char *token = ConfigParser::NextToken())
454 opt->parse(token);
455}
456