]>
Commit | Line | Data |
---|---|---|
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 | 23 | Security::PeerOptions Security::ProxyOutgoingConfig; |
195f8adb | 24 | |
9a622f3e AJ |
25 | Security::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 |
42 | void |
43 | Security::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 |
91 | void |
92 | Security::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 |
124 | void |
125 | Security::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 |
182 | Security::ContextPointer | |
a465e144 | 183 | Security::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 |
199 | static 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 | ||
328 | long | |
329 | Security::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 |
408 | long |
409 | Security::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 |
450 | void |
451 | parse_securePeerOptions(Security::PeerOptions *opt) | |
452 | { | |
453 | while(const char *token = ConfigParser::NextToken()) | |
454 | opt->parse(token); | |
455 | } | |
456 |