]>
Commit | Line | Data |
---|---|---|
9a2f63e7 | 1 | /* |
77b1029d | 2 | * Copyright (C) 1996-2020 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" |
7f2cd2e8 | 15 | #include "Parsing.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 | |
d7208dbc AJ |
24 | Security::PeerOptions::PeerOptions() |
25 | { | |
ed5f5120 SM |
26 | // init options consistent with an empty sslOptions |
27 | parseOptions(); | |
d7208dbc AJ |
28 | } |
29 | ||
0b0e0864 AJ |
30 | void |
31 | Security::PeerOptions::parse(const char *token) | |
32 | { | |
d4ab7b63 AJ |
33 | if (!*token) { |
34 | // config says just "ssl" or "tls" (or "tls-") | |
35 | encryptTransport = true; | |
36 | return; | |
37 | } | |
38 | ||
41ee8990 AJ |
39 | if (strncmp(token, "disable", 7) == 0) { |
40 | clear(); | |
d4ab7b63 AJ |
41 | return; |
42 | } | |
43 | ||
44 | if (strncmp(token, "cert=", 5) == 0) { | |
d1d72d43 AJ |
45 | KeyData t; |
46 | t.privateKeyFile = t.certFile = SBuf(token + 5); | |
47 | certs.emplace_back(t); | |
0b0e0864 | 48 | } else if (strncmp(token, "key=", 4) == 0) { |
d1d72d43 | 49 | if (certs.empty() || certs.back().certFile.isEmpty()) { |
28e1d824 | 50 | fatal("cert= option must be set before key= is used."); |
d1d72d43 | 51 | return; |
0b0e0864 | 52 | } |
d1d72d43 AJ |
53 | KeyData &t = certs.back(); |
54 | t.privateKeyFile = SBuf(token + 4); | |
0b0e0864 | 55 | } else if (strncmp(token, "version=", 8) == 0) { |
2aa5a7d3 | 56 | debugs(0, DBG_PARSE_NOTE(1), "UPGRADE WARNING: SSL version= is deprecated. Use options= and tls-min-version= to limit protocols instead."); |
0b0e0864 | 57 | sslVersion = xatoi(token + 8); |
1cc44095 AJ |
58 | } else if (strncmp(token, "min-version=", 12) == 0) { |
59 | tlsMinVersion = SBuf(token + 12); | |
5dc9fd64 | 60 | optsReparse = true; |
0b0e0864 AJ |
61 | } else if (strncmp(token, "options=", 8) == 0) { |
62 | sslOptions = SBuf(token + 8); | |
2aa5a7d3 | 63 | optsReparse = true; |
0b0e0864 AJ |
64 | } else if (strncmp(token, "cipher=", 7) == 0) { |
65 | sslCipher = SBuf(token + 7); | |
66 | } else if (strncmp(token, "cafile=", 7) == 0) { | |
86a84cc0 | 67 | caFiles.emplace_back(SBuf(token + 7)); |
0b0e0864 AJ |
68 | } else if (strncmp(token, "capath=", 7) == 0) { |
69 | caDir = SBuf(token + 7); | |
86a84cc0 AJ |
70 | #if !USE_OPENSSL |
71 | debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl."); | |
72 | #endif | |
0b0e0864 AJ |
73 | } else if (strncmp(token, "crlfile=", 8) == 0) { |
74 | crlFile = SBuf(token + 8); | |
6b19d1f9 | 75 | loadCrlFile(); |
0b0e0864 | 76 | } else if (strncmp(token, "flags=", 6) == 0) { |
b24e9ae7 | 77 | if (parsedFlags != 0) { |
9a622f3e | 78 | debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags << " with " << SBuf(token + 6)); |
b24e9ae7 | 79 | } |
0b0e0864 | 80 | sslFlags = SBuf(token + 6); |
ec4defdb | 81 | parsedFlags = parseFlags(); |
435c72b0 AJ |
82 | } else if (strncmp(token, "default-ca=off", 14) == 0 || strncmp(token, "no-default-ca", 13) == 0) { |
83 | if (flags.tlsDefaultCa.configured() && flags.tlsDefaultCa) | |
84 | fatalf("ERROR: previous default-ca settings conflict with %s", token); | |
85 | flags.tlsDefaultCa.configure(false); | |
86 | } else if (strncmp(token, "default-ca=on", 13) == 0 || strncmp(token, "default-ca", 10) == 0) { | |
87 | if (flags.tlsDefaultCa.configured() && !flags.tlsDefaultCa) | |
88 | fatalf("ERROR: previous default-ca settings conflict with %s", token); | |
89 | flags.tlsDefaultCa.configure(true); | |
0b0e0864 AJ |
90 | } else if (strncmp(token, "domain=", 7) == 0) { |
91 | sslDomain = SBuf(token + 7); | |
b05d749d AJ |
92 | } else if (strncmp(token, "no-npn", 6) == 0) { |
93 | flags.tlsNpn = false; | |
9a622f3e AJ |
94 | } else { |
95 | debugs(3, DBG_CRITICAL, "ERROR: Unknown TLS option '" << token << "'"); | |
d4ab7b63 | 96 | return; |
0b0e0864 | 97 | } |
d4ab7b63 AJ |
98 | |
99 | encryptTransport = true; | |
0b0e0864 AJ |
100 | } |
101 | ||
8250ca31 AJ |
102 | void |
103 | Security::PeerOptions::dumpCfg(Packable *p, const char *pfx) const | |
104 | { | |
105 | if (!encryptTransport) { | |
106 | p->appendf(" %sdisable", pfx); | |
107 | return; // no other settings are relevant | |
108 | } | |
109 | ||
d1d72d43 AJ |
110 | for (auto &i : certs) { |
111 | if (!i.certFile.isEmpty()) | |
112 | p->appendf(" %scert=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.certFile)); | |
8250ca31 | 113 | |
d1d72d43 AJ |
114 | if (!i.privateKeyFile.isEmpty() && i.privateKeyFile != i.certFile) |
115 | p->appendf(" %skey=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i.privateKeyFile)); | |
116 | } | |
8250ca31 AJ |
117 | |
118 | if (!sslOptions.isEmpty()) | |
119 | p->appendf(" %soptions=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslOptions)); | |
120 | ||
121 | if (!sslCipher.isEmpty()) | |
122 | p->appendf(" %scipher=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslCipher)); | |
123 | ||
86a84cc0 AJ |
124 | for (auto i : caFiles) { |
125 | p->appendf(" %scafile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(i)); | |
126 | } | |
8250ca31 AJ |
127 | |
128 | if (!caDir.isEmpty()) | |
129 | p->appendf(" %scapath=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(caDir)); | |
130 | ||
131 | if (!crlFile.isEmpty()) | |
132 | p->appendf(" %scrlfile=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(crlFile)); | |
133 | ||
134 | if (!sslFlags.isEmpty()) | |
135 | p->appendf(" %sflags=" SQUIDSBUFPH, pfx, SQUIDSBUFPRINT(sslFlags)); | |
0278bdcb | 136 | |
435c72b0 AJ |
137 | if (flags.tlsDefaultCa.configured()) { |
138 | // default ON for peers / upstream servers | |
139 | // default OFF for listening ports | |
140 | if (flags.tlsDefaultCa) | |
141 | p->appendf(" %sdefault-ca", pfx); | |
142 | else | |
143 | p->appendf(" %sdefault-ca=off", pfx); | |
144 | } | |
b05d749d AJ |
145 | |
146 | if (!flags.tlsNpn) | |
147 | p->appendf(" %sno-npn", pfx); | |
8250ca31 AJ |
148 | } |
149 | ||
585c27eb AJ |
150 | void |
151 | Security::PeerOptions::updateTlsVersionLimits() | |
9a2f63e7 | 152 | { |
1cc44095 AJ |
153 | if (!tlsMinVersion.isEmpty()) { |
154 | ::Parser::Tokenizer tok(tlsMinVersion); | |
155 | int64_t v = 0; | |
5dc9fd64 | 156 | tlsMinOptions.clear(); |
bd71ba99 | 157 | if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 3) { |
1cc44095 | 158 | // only account for TLS here - SSL versions are handled by options= parameter |
bd71ba99 | 159 | // avoid affecting options= parameter in cachemgr config report |
5dc9fd64 | 160 | SBuf add; |
cc488ec9 | 161 | #if USE_OPENSSL |
1cc44095 | 162 | if (v > 0) |
5dc9fd64 | 163 | add.append(":NO_TLSv1"); |
1cc44095 | 164 | if (v > 1) |
5dc9fd64 | 165 | add.append(":NO_TLSv1_1"); |
1cc44095 | 166 | if (v > 2) |
5dc9fd64 | 167 | add.append(":NO_TLSv1_2"); |
cc488ec9 | 168 | #elif USE_GNUTLS |
cc488ec9 AJ |
169 | if (v > 0) |
170 | add.append(":-VERS-TLS1.0"); | |
171 | if (v > 1) | |
172 | add.append(":-VERS-TLS1.1"); | |
173 | if (v > 2) | |
174 | add.append(":-VERS-TLS1.2"); | |
5dc9fd64 | 175 | #endif |
cc488ec9 | 176 | |
5dc9fd64 | 177 | if (!tlsMinOptions.isEmpty()) |
cc488ec9 | 178 | add.chop(1); // remove the initial ':' |
5dc9fd64 | 179 | tlsMinOptions.append(add); |
2aa5a7d3 | 180 | optsReparse = true; |
1cc44095 AJ |
181 | |
182 | } else { | |
183 | debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion); | |
184 | } | |
185 | ||
cc488ec9 AJ |
186 | return; |
187 | } | |
188 | ||
ed5f5120 | 189 | if (sslVersion > 2) { |
1cc44095 AJ |
190 | // backward compatibility hack for sslversion= configuration |
191 | // only use if tls-min-version=N.N is not present | |
8250ca31 AJ |
192 | // values 0-2 for auto and SSLv2 are not supported any longer. |
193 | // Do it this way so we DO cause changes to options= in cachemgr config report | |
5badbadf | 194 | const char *add = nullptr; |
1cc44095 AJ |
195 | switch (sslVersion) { |
196 | case 3: | |
cc488ec9 | 197 | #if USE_OPENSSL |
d39e23a0 | 198 | add = ":NO_TLSv1:NO_TLSv1_1:NO_TLSv1_2:NO_TLSv1_3"; |
cc488ec9 | 199 | #elif USE_GNUTLS |
d39e23a0 | 200 | add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3"; |
cc488ec9 | 201 | #endif |
1cc44095 AJ |
202 | break; |
203 | case 4: | |
cc488ec9 | 204 | #if USE_OPENSSL |
d39e23a0 | 205 | add = ":NO_SSLv3:NO_TLSv1_1:NO_TLSv1_2:NO_TLSv1_3"; |
cc488ec9 | 206 | #elif USE_GNUTLS |
d39e23a0 | 207 | add = ":+VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3"; |
cc488ec9 | 208 | #endif |
1cc44095 AJ |
209 | break; |
210 | case 5: | |
cc488ec9 | 211 | #if USE_OPENSSL |
d39e23a0 | 212 | add = ":NO_SSLv3:NO_TLSv1:NO_TLSv1_2:NO_TLSv1_3"; |
cc488ec9 | 213 | #elif USE_GNUTLS |
d39e23a0 | 214 | add = ":-VERS-TLS1.0:+VERS-TLS1.1:-VERS-TLS1.2:-VERS-TLS1.3"; |
cc488ec9 | 215 | #endif |
1cc44095 AJ |
216 | break; |
217 | case 6: | |
cc488ec9 | 218 | #if USE_OPENSSL |
d39e23a0 | 219 | add = ":NO_SSLv3:NO_TLSv1:NO_TLSv1_1:NO_TLSv1_3"; |
cc488ec9 | 220 | #elif USE_GNUTLS |
d39e23a0 | 221 | add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.3"; |
cc488ec9 | 222 | #endif |
1cc44095 AJ |
223 | break; |
224 | default: // nothing | |
225 | break; | |
226 | } | |
227 | if (add) { | |
cc488ec9 AJ |
228 | if (sslOptions.isEmpty()) |
229 | sslOptions.append(add+1, strlen(add+1)); | |
230 | else | |
231 | sslOptions.append(add, strlen(add)); | |
2aa5a7d3 | 232 | optsReparse = true; |
1cc44095 AJ |
233 | } |
234 | sslVersion = 0; // prevent sslOptions being repeatedly appended | |
235 | } | |
585c27eb | 236 | } |
1cc44095 | 237 | |
64769c79 | 238 | Security::ContextPointer |
885f0ecf AJ |
239 | Security::PeerOptions::createBlankContext() const |
240 | { | |
64769c79 | 241 | Security::ContextPointer ctx; |
885f0ecf | 242 | #if USE_OPENSSL |
0a28c16a AJ |
243 | Ssl::Initialize(); |
244 | ||
64769c79 | 245 | SSL_CTX *t = SSL_CTX_new(TLS_client_method()); |
885f0ecf | 246 | if (!t) { |
ea574635 AJ |
247 | const auto x = ERR_get_error(); |
248 | fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x)); | |
885f0ecf | 249 | } |
df473b36 | 250 | ctx = convertContextFromRawPtr(t); |
885f0ecf AJ |
251 | |
252 | #elif USE_GNUTLS | |
253 | // Initialize for X.509 certificate exchange | |
64769c79 | 254 | gnutls_certificate_credentials_t t; |
885f0ecf | 255 | if (const int x = gnutls_certificate_allocate_credentials(&t)) { |
ea574635 | 256 | fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x)); |
885f0ecf | 257 | } |
df473b36 | 258 | ctx = convertContextFromRawPtr(t); |
885f0ecf AJ |
259 | |
260 | #else | |
c75aba02 | 261 | debugs(83, 1, "WARNING: Failed to allocate TLS client context: No TLS library"); |
885f0ecf AJ |
262 | |
263 | #endif | |
264 | ||
64769c79 | 265 | return ctx; |
885f0ecf AJ |
266 | } |
267 | ||
900daee3 | 268 | Security::ContextPointer |
a465e144 | 269 | Security::PeerOptions::createClientContext(bool setOptions) |
9a2f63e7 | 270 | { |
585c27eb | 271 | updateTlsVersionLimits(); |
86a84cc0 | 272 | |
900daee3 | 273 | Security::ContextPointer t(createBlankContext()); |
c75aba02 | 274 | if (t) { |
cf487124 AJ |
275 | if (setOptions) |
276 | updateContextOptions(t); | |
9a2f63e7 | 277 | #if USE_OPENSSL |
c75aba02 | 278 | // XXX: temporary performance regression. c_str() data copies and prevents this being a const method |
cc488ec9 | 279 | Ssl::InitClientContext(t, *this, parsedFlags); |
9a2f63e7 | 280 | #endif |
b05d749d | 281 | updateContextNpn(t); |
b23f5f9c AJ |
282 | updateContextCa(t); |
283 | updateContextCrl(t); | |
98f951b7 | 284 | updateContextTrust(t); |
86a84cc0 | 285 | } |
6b19d1f9 | 286 | |
900daee3 | 287 | return t; |
9a2f63e7 | 288 | } |
1f1f29e8 | 289 | |
cc488ec9 | 290 | #if USE_OPENSSL |
6bd62757 AJ |
291 | /// set of options we can parse and what they map to |
292 | static struct ssl_option { | |
293 | const char *name; | |
294 | long value; | |
295 | ||
296 | } ssl_options[] = { | |
297 | ||
298 | #if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG | |
299 | { | |
300 | "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG | |
301 | }, | |
302 | #endif | |
303 | #if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG | |
304 | { | |
305 | "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG | |
306 | }, | |
307 | #endif | |
308 | #if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER | |
309 | { | |
310 | "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER | |
311 | }, | |
312 | #endif | |
313 | #if SSL_OP_SSLEAY_080_CLIENT_DH_BUG | |
314 | { | |
315 | "SSLEAY_080_CLIENT_DH_BUG", SSL_OP_SSLEAY_080_CLIENT_DH_BUG | |
316 | }, | |
317 | #endif | |
318 | #if SSL_OP_TLS_D5_BUG | |
319 | { | |
320 | "TLS_D5_BUG", SSL_OP_TLS_D5_BUG | |
321 | }, | |
322 | #endif | |
323 | #if SSL_OP_TLS_BLOCK_PADDING_BUG | |
324 | { | |
325 | "TLS_BLOCK_PADDING_BUG", SSL_OP_TLS_BLOCK_PADDING_BUG | |
326 | }, | |
327 | #endif | |
328 | #if SSL_OP_TLS_ROLLBACK_BUG | |
329 | { | |
330 | "TLS_ROLLBACK_BUG", SSL_OP_TLS_ROLLBACK_BUG | |
331 | }, | |
332 | #endif | |
333 | #if SSL_OP_ALL | |
334 | { | |
335 | "ALL", (long)SSL_OP_ALL | |
336 | }, | |
337 | #endif | |
338 | #if SSL_OP_SINGLE_DH_USE | |
339 | { | |
340 | "SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE | |
341 | }, | |
342 | #endif | |
343 | #if SSL_OP_EPHEMERAL_RSA | |
344 | { | |
345 | "EPHEMERAL_RSA", SSL_OP_EPHEMERAL_RSA | |
346 | }, | |
347 | #endif | |
348 | #if SSL_OP_PKCS1_CHECK_1 | |
349 | { | |
350 | "PKCS1_CHECK_1", SSL_OP_PKCS1_CHECK_1 | |
351 | }, | |
352 | #endif | |
353 | #if SSL_OP_PKCS1_CHECK_2 | |
354 | { | |
355 | "PKCS1_CHECK_2", SSL_OP_PKCS1_CHECK_2 | |
356 | }, | |
357 | #endif | |
358 | #if SSL_OP_NETSCAPE_CA_DN_BUG | |
359 | { | |
360 | "NETSCAPE_CA_DN_BUG", SSL_OP_NETSCAPE_CA_DN_BUG | |
361 | }, | |
362 | #endif | |
363 | #if SSL_OP_NON_EXPORT_FIRST | |
364 | { | |
365 | "NON_EXPORT_FIRST", SSL_OP_NON_EXPORT_FIRST | |
366 | }, | |
367 | #endif | |
368 | #if SSL_OP_CIPHER_SERVER_PREFERENCE | |
369 | { | |
370 | "CIPHER_SERVER_PREFERENCE", SSL_OP_CIPHER_SERVER_PREFERENCE | |
371 | }, | |
372 | #endif | |
373 | #if SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG | |
374 | { | |
375 | "NETSCAPE_DEMO_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG | |
376 | }, | |
377 | #endif | |
378 | #if SSL_OP_NO_SSLv3 | |
379 | { | |
380 | "NO_SSLv3", SSL_OP_NO_SSLv3 | |
381 | }, | |
382 | #endif | |
383 | #if SSL_OP_NO_TLSv1 | |
384 | { | |
385 | "NO_TLSv1", SSL_OP_NO_TLSv1 | |
386 | }, | |
5dc9fd64 AJ |
387 | #else |
388 | { "NO_TLSv1", 0 }, | |
6bd62757 AJ |
389 | #endif |
390 | #if SSL_OP_NO_TLSv1_1 | |
391 | { | |
392 | "NO_TLSv1_1", SSL_OP_NO_TLSv1_1 | |
393 | }, | |
5dc9fd64 AJ |
394 | #else |
395 | { "NO_TLSv1_1", 0 }, | |
6bd62757 AJ |
396 | #endif |
397 | #if SSL_OP_NO_TLSv1_2 | |
398 | { | |
399 | "NO_TLSv1_2", SSL_OP_NO_TLSv1_2 | |
400 | }, | |
5dc9fd64 AJ |
401 | #else |
402 | { "NO_TLSv1_2", 0 }, | |
6bd62757 | 403 | #endif |
d39e23a0 CT |
404 | #if SSL_OP_NO_TLSv1_3 |
405 | { | |
406 | "NO_TLSv1_3", SSL_OP_NO_TLSv1_3 | |
407 | }, | |
408 | #else | |
409 | { "NO_TLSv1_3", 0 }, | |
410 | #endif | |
6bd62757 AJ |
411 | #if SSL_OP_NO_COMPRESSION |
412 | { | |
413 | "No_Compression", SSL_OP_NO_COMPRESSION | |
414 | }, | |
415 | #endif | |
416 | #if SSL_OP_NO_TICKET | |
417 | { | |
418 | "NO_TICKET", SSL_OP_NO_TICKET | |
419 | }, | |
585c27eb AJ |
420 | #endif |
421 | #if SSL_OP_SINGLE_ECDH_USE | |
422 | { | |
423 | "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE | |
424 | }, | |
6bd62757 AJ |
425 | #endif |
426 | { | |
427 | "", 0 | |
428 | }, | |
429 | { | |
430 | NULL, 0 | |
431 | } | |
432 | }; | |
cc488ec9 | 433 | #endif /* USE_OPENSSL */ |
6bd62757 | 434 | |
c62717bd AJ |
435 | /** |
436 | * Pre-parse TLS options= parameter to be applied when the TLS objects created. | |
437 | * Options must not used in the case of peek or stare bump mode. | |
438 | */ | |
cc488ec9 | 439 | void |
5badbadf | 440 | Security::PeerOptions::parseOptions() |
6bd62757 | 441 | { |
2aa5a7d3 AJ |
442 | // do not allow repeated parsing when multiple contexts are created |
443 | // NP: we cannot use !parsedOptions because a nil value does have meaning there | |
444 | if (!optsReparse) | |
445 | return; | |
446 | optsReparse = false; | |
447 | ||
5dc9fd64 AJ |
448 | // combination of settings we have to set via parsedOptions. |
449 | // options= with override by tls-min-version= | |
450 | SBuf str; | |
451 | str.append(sslOptions); | |
452 | str.append(tlsMinOptions); | |
453 | ||
cc488ec9 | 454 | #if USE_OPENSSL |
5dc9fd64 | 455 | ::Parser::Tokenizer tok(str); |
7ae61630 | 456 | long op = 0; |
6bd62757 | 457 | |
d7208dbc | 458 | while (!tok.atEnd()) { |
6bd62757 AJ |
459 | enum { |
460 | MODE_ADD, MODE_REMOVE | |
461 | } mode; | |
462 | ||
c62717bd | 463 | if (tok.skip('-') || tok.skip('!')) |
6bd62757 | 464 | mode = MODE_REMOVE; |
c62717bd AJ |
465 | else { |
466 | (void)tok.skip('+'); // default action is add. ignore if missing operator | |
6bd62757 | 467 | mode = MODE_ADD; |
c62717bd | 468 | } |
6bd62757 | 469 | |
c62717bd AJ |
470 | static const CharacterSet optChars = CharacterSet("TLS-option", "_") + CharacterSet::ALPHA + CharacterSet::DIGIT; |
471 | int64_t hex = 0; | |
472 | SBuf option; | |
473 | long value = 0; | |
474 | ||
144acb47 AJ |
475 | // Bug 4429: identify the full option name before determining text or numeric |
476 | if (tok.prefix(option, optChars)) { | |
6bd62757 | 477 | |
c62717bd AJ |
478 | // find the named option in our supported set |
479 | for (struct ssl_option *opttmp = ssl_options; opttmp->name; ++opttmp) { | |
480 | if (option.cmp(opttmp->name) == 0) { | |
481 | value = opttmp->value; | |
482 | break; | |
483 | } | |
6bd62757 | 484 | } |
144acb47 AJ |
485 | |
486 | // Special case.. hex specification | |
487 | ::Parser::Tokenizer tmp(option); | |
488 | if (!value && tmp.int64(hex, 16, false) && tmp.atEnd()) { | |
489 | value = hex; | |
490 | } | |
6bd62757 AJ |
491 | } |
492 | ||
40bc593b AJ |
493 | if (value) { |
494 | switch (mode) { | |
495 | case MODE_ADD: | |
496 | op |= value; | |
497 | break; | |
daf640c8 | 498 | case MODE_REMOVE: |
40bc593b AJ |
499 | op &= ~value; |
500 | break; | |
501 | } | |
502 | } else { | |
503 | debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option); | |
6bd62757 AJ |
504 | } |
505 | ||
c62717bd AJ |
506 | static const CharacterSet delims("TLS-option-delim",":,"); |
507 | if (!tok.skipAll(delims) && !tok.atEnd()) { | |
508 | fatalf("Unknown TLS option '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining())); | |
509 | } | |
6bd62757 | 510 | |
d7208dbc | 511 | } |
6bd62757 AJ |
512 | |
513 | #if SSL_OP_NO_SSLv2 | |
514 | // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0 | |
515 | op = op | SSL_OP_NO_SSLv2; | |
516 | #endif | |
5badbadf | 517 | parsedOptions = op; |
cc488ec9 AJ |
518 | |
519 | #elif USE_GNUTLS | |
5dc9fd64 | 520 | if (str.isEmpty()) { |
5badbadf AJ |
521 | parsedOptions.reset(); |
522 | return; | |
523 | } | |
524 | ||
cc488ec9 | 525 | const char *err = nullptr; |
5dc9fd64 | 526 | const char *priorities = str.c_str(); |
cc488ec9 | 527 | gnutls_priority_t op; |
60fcfadf AJ |
528 | int x = gnutls_priority_init(&op, priorities, &err); |
529 | if (x != GNUTLS_E_SUCCESS) { | |
530 | fatalf("(%s) in TLS options '%s'", ErrorString(x), err); | |
cc488ec9 | 531 | } |
c17dcc9a | 532 | parsedOptions = Security::ParsedOptions(op, [](gnutls_priority_t p) { |
df473b36 | 533 | debugs(83, 5, "gnutls_priority_deinit p=" << (void*)p); |
ed5f5120 | 534 | gnutls_priority_deinit(p); |
c17dcc9a | 535 | }); |
cc488ec9 | 536 | #endif |
6bd62757 AJ |
537 | } |
538 | ||
ec4defdb AJ |
539 | /** |
540 | * Parses the TLS flags squid.conf parameter | |
541 | */ | |
b24e9ae7 | 542 | long |
ec4defdb | 543 | Security::PeerOptions::parseFlags() |
b24e9ae7 | 544 | { |
ec4defdb | 545 | if (sslFlags.isEmpty()) |
b24e9ae7 AJ |
546 | return 0; |
547 | ||
548 | static struct { | |
549 | SBuf label; | |
550 | long mask; | |
551 | } flagTokens[] = { | |
552 | { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA }, | |
553 | { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH }, | |
554 | { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER }, | |
555 | { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN }, | |
556 | { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE }, | |
557 | #if X509_V_FLAG_CRL_CHECK | |
558 | { SBuf("VERIFY_CRL"), SSL_FLAG_VERIFY_CRL }, | |
559 | { SBuf("VERIFY_CRL_ALL"), SSL_FLAG_VERIFY_CRL_ALL }, | |
560 | #endif | |
561 | { SBuf(), 0 } | |
562 | }; | |
563 | ||
ec4defdb | 564 | ::Parser::Tokenizer tok(sslFlags); |
b24e9ae7 AJ |
565 | static const CharacterSet delims("Flag-delimiter", ":,"); |
566 | ||
567 | long fl = 0; | |
568 | do { | |
569 | long found = 0; | |
570 | for (size_t i = 0; flagTokens[i].mask; ++i) { | |
3f6c1586 | 571 | if (tok.skip(flagTokens[i].label)) { |
b24e9ae7 AJ |
572 | found = flagTokens[i].mask; |
573 | break; | |
574 | } | |
575 | } | |
576 | if (!found) | |
8250ca31 | 577 | fatalf("Unknown TLS flag '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining())); |
8b253b83 | 578 | if (found == SSL_FLAG_NO_DEFAULT_CA) { |
435c72b0 AJ |
579 | if (flags.tlsDefaultCa.configured() && flags.tlsDefaultCa) |
580 | fatal("ERROR: previous default-ca settings conflict with sslflags=NO_DEFAULT_CA"); | |
581 | debugs(83, DBG_PARSE_NOTE(2), "WARNING: flags=NO_DEFAULT_CA is deprecated. Use tls-default-ca=off instead."); | |
582 | flags.tlsDefaultCa.configure(false); | |
8b253b83 AJ |
583 | } else |
584 | fl |= found; | |
b24e9ae7 AJ |
585 | } while (tok.skipOne(delims)); |
586 | ||
587 | return fl; | |
588 | } | |
589 | ||
6b19d1f9 AJ |
590 | /// Load a CRLs list stored in the file whose /path/name is in crlFile |
591 | /// replaces any CRL loaded previously | |
592 | void | |
593 | Security::PeerOptions::loadCrlFile() | |
594 | { | |
595 | parsedCrl.clear(); | |
596 | if (crlFile.isEmpty()) | |
597 | return; | |
598 | ||
599 | #if USE_OPENSSL | |
600 | BIO *in = BIO_new_file(crlFile.c_str(), "r"); | |
601 | if (!in) { | |
602 | debugs(83, 2, "WARNING: Failed to open CRL file " << crlFile); | |
603 | return; | |
604 | } | |
605 | ||
606 | while (X509_CRL *crl = PEM_read_bio_X509_CRL(in,NULL,NULL,NULL)) { | |
607 | parsedCrl.emplace_back(Security::CrlPointer(crl)); | |
608 | } | |
609 | BIO_free(in); | |
610 | #endif | |
611 | } | |
612 | ||
cf487124 | 613 | void |
2aa5a7d3 | 614 | Security::PeerOptions::updateContextOptions(Security::ContextPointer &ctx) |
cf487124 | 615 | { |
2aa5a7d3 | 616 | parseOptions(); |
cf487124 AJ |
617 | #if USE_OPENSSL |
618 | SSL_CTX_set_options(ctx.get(), parsedOptions); | |
619 | #elif USE_GNUTLS | |
b491f761 | 620 | // NP: GnuTLS uses 'priorities' which are set only per-session instead. |
cf487124 AJ |
621 | #endif |
622 | } | |
623 | ||
b05d749d AJ |
624 | #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg) |
625 | // Dummy next_proto_neg callback | |
626 | static int | |
627 | ssl_next_proto_cb(SSL *s, unsigned char **out, unsigned char *outlen, const unsigned char *in, unsigned int inlen, void *arg) | |
628 | { | |
629 | static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'}; | |
630 | (void)SSL_select_next_proto(out, outlen, in, inlen, supported_protos, sizeof(supported_protos)); | |
631 | return SSL_TLSEXT_ERR_OK; | |
632 | } | |
633 | #endif | |
634 | ||
635 | void | |
64769c79 | 636 | Security::PeerOptions::updateContextNpn(Security::ContextPointer &ctx) |
b05d749d AJ |
637 | { |
638 | if (!flags.tlsNpn) | |
639 | return; | |
640 | ||
641 | #if USE_OPENSSL && defined(TLSEXT_TYPE_next_proto_neg) | |
64769c79 | 642 | SSL_CTX_set_next_proto_select_cb(ctx.get(), &ssl_next_proto_cb, nullptr); |
b05d749d AJ |
643 | #endif |
644 | ||
645 | // NOTE: GnuTLS does not support the obsolete NPN extension. | |
646 | // it does support ALPN per-session, not per-context. | |
647 | } | |
648 | ||
4f4d3a57 | 649 | static const char * |
b23f5f9c | 650 | loadSystemTrustedCa(Security::ContextPointer &ctx) |
4f4d3a57 | 651 | { |
dfe70a1b | 652 | debugs(83, 8, "Setting default system Trusted CA. ctx=" << (void*)ctx.get()); |
4f4d3a57 | 653 | #if USE_OPENSSL |
b23f5f9c | 654 | if (SSL_CTX_set_default_verify_paths(ctx.get()) == 0) |
ea574635 | 655 | return Security::ErrorString(ERR_get_error()); |
4f4d3a57 AJ |
656 | |
657 | #elif USE_GNUTLS | |
b23f5f9c | 658 | auto x = gnutls_certificate_set_x509_system_trust(ctx.get()); |
4f4d3a57 | 659 | if (x < 0) |
ea574635 | 660 | return Security::ErrorString(x); |
4f4d3a57 AJ |
661 | |
662 | #endif | |
663 | return nullptr; | |
664 | } | |
665 | ||
86a84cc0 | 666 | void |
b23f5f9c | 667 | Security::PeerOptions::updateContextCa(Security::ContextPointer &ctx) |
86a84cc0 AJ |
668 | { |
669 | debugs(83, 8, "Setting CA certificate locations."); | |
123d5c2a | 670 | #if USE_OPENSSL |
c8736993 CT |
671 | if (const char *path = caDir.isEmpty() ? nullptr : caDir.c_str()) { |
672 | if (!SSL_CTX_load_verify_locations(ctx.get(), nullptr, path)) { | |
673 | const auto x = ERR_get_error(); | |
674 | debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " << path << ": " << Security::ErrorString(x)); | |
675 | } | |
676 | } | |
123d5c2a | 677 | #endif |
86a84cc0 AJ |
678 | for (auto i : caFiles) { |
679 | #if USE_OPENSSL | |
c8736993 | 680 | if (!SSL_CTX_load_verify_locations(ctx.get(), i.c_str(), nullptr)) { |
ea574635 AJ |
681 | const auto x = ERR_get_error(); |
682 | debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " << | |
683 | i << ": " << Security::ErrorString(x)); | |
86a84cc0 AJ |
684 | } |
685 | #elif USE_GNUTLS | |
ea574635 AJ |
686 | const auto x = gnutls_certificate_set_x509_trust_file(ctx.get(), i.c_str(), GNUTLS_X509_FMT_PEM); |
687 | if (x < 0) { | |
688 | debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting CA certificate location " << | |
689 | i << ": " << Security::ErrorString(x)); | |
86a84cc0 AJ |
690 | } |
691 | #endif | |
692 | } | |
693 | ||
b2cd014b | 694 | if (!flags.tlsDefaultCa) |
86a84cc0 AJ |
695 | return; |
696 | ||
4f4d3a57 AJ |
697 | if (const char *err = loadSystemTrustedCa(ctx)) { |
698 | debugs(83, DBG_IMPORTANT, "WARNING: Ignoring error setting default trusted CA : " << err); | |
86a84cc0 | 699 | } |
86a84cc0 AJ |
700 | } |
701 | ||
6b19d1f9 | 702 | void |
b23f5f9c | 703 | Security::PeerOptions::updateContextCrl(Security::ContextPointer &ctx) |
6b19d1f9 AJ |
704 | { |
705 | #if USE_OPENSSL | |
706 | bool verifyCrl = false; | |
b23f5f9c | 707 | X509_STORE *st = SSL_CTX_get_cert_store(ctx.get()); |
6b19d1f9 AJ |
708 | if (parsedCrl.size()) { |
709 | for (auto &i : parsedCrl) { | |
710 | if (!X509_STORE_add_crl(st, i.get())) | |
711 | debugs(83, 2, "WARNING: Failed to add CRL"); | |
712 | else | |
713 | verifyCrl = true; | |
714 | } | |
715 | } | |
716 | ||
717 | #if X509_V_FLAG_CRL_CHECK | |
718 | if ((parsedFlags & SSL_FLAG_VERIFY_CRL_ALL)) | |
719 | X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); | |
720 | else if (verifyCrl || (parsedFlags & SSL_FLAG_VERIFY_CRL)) | |
721 | X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK); | |
722 | #endif | |
723 | ||
724 | #endif /* USE_OPENSSL */ | |
725 | } | |
726 | ||
98f951b7 AR |
727 | void |
728 | Security::PeerOptions::updateContextTrust(Security::ContextPointer &ctx) | |
729 | { | |
730 | #if USE_OPENSSL | |
731 | #if defined(X509_V_FLAG_PARTIAL_CHAIN) | |
732 | const auto st = SSL_CTX_get_cert_store(ctx.get()); | |
733 | assert(st); | |
734 | if (X509_STORE_set_flags(st, X509_V_FLAG_PARTIAL_CHAIN) != 1) { | |
735 | debugs(83, DBG_IMPORTANT, "ERROR: Failed to enable trust in intermediate CA certificates: " << | |
736 | Security::ErrorString(ERR_get_error())); | |
737 | } | |
738 | #endif | |
739 | #elif USE_GNUTLS | |
740 | // Modern GnuTLS versions trust intermediate CA certificates by default. | |
741 | #endif /* TLS library */ | |
742 | } | |
743 | ||
cc488ec9 AJ |
744 | void |
745 | Security::PeerOptions::updateSessionOptions(Security::SessionPointer &s) | |
746 | { | |
2aa5a7d3 | 747 | parseOptions(); |
c96b5508 | 748 | #if USE_OPENSSL |
60fcfadf | 749 | debugs(83, 5, "set OpenSSL options for session=" << s << ", parsedOptions=" << parsedOptions); |
b491f761 AJ |
750 | // XXX: Options already set before (via the context) are not cleared! |
751 | SSL_set_options(s.get(), parsedOptions); | |
752 | ||
c96b5508 | 753 | #elif USE_GNUTLS |
cc488ec9 | 754 | int x; |
5badbadf | 755 | SBuf errMsg; |
cc488ec9 AJ |
756 | if (!parsedOptions) { |
757 | debugs(83, 5, "set GnuTLS default priority/options for session=" << s); | |
758 | x = gnutls_set_default_priority(s.get()); | |
5badbadf AJ |
759 | static const SBuf defaults("default"); |
760 | errMsg = defaults; | |
cc488ec9 | 761 | } else { |
60fcfadf | 762 | debugs(83, 5, "set GnuTLS session=" << s << ", options='" << sslOptions << ":" << tlsMinOptions << "'"); |
cc488ec9 | 763 | x = gnutls_priority_set(s.get(), parsedOptions.get()); |
5badbadf | 764 | errMsg = sslOptions; |
cc488ec9 AJ |
765 | } |
766 | ||
767 | if (x != GNUTLS_E_SUCCESS) { | |
60fcfadf | 768 | debugs(83, DBG_IMPORTANT, "ERROR: session=" << s << " Failed to set TLS options (" << errMsg << ":" << tlsMinVersion << "). error: " << Security::ErrorString(x)); |
cc488ec9 AJ |
769 | } |
770 | #endif | |
771 | } | |
772 | ||
1f1f29e8 AJ |
773 | void |
774 | parse_securePeerOptions(Security::PeerOptions *opt) | |
775 | { | |
776 | while(const char *token = ConfigParser::NextToken()) | |
777 | opt->parse(token); | |
151644b7 | 778 | opt->parseOptions(); |
1f1f29e8 AJ |
779 | } |
780 |