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