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