]>
Commit | Line | Data |
---|---|---|
b3a8ae1b | 1 | /* |
4ac4a490 | 2 | * Copyright (C) 1996-2017 The Squid Software Foundation and contributors |
b3a8ae1b | 3 | * |
bbc27441 AJ |
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. | |
b3a8ae1b AR |
7 | */ |
8 | ||
bbc27441 AJ |
9 | /* DEBUG: section 83 SSL accelerator support */ |
10 | ||
b3a8ae1b | 11 | #include "squid.h" |
d620ae0e | 12 | #include "ssl/support.h" |
b3a8ae1b AR |
13 | |
14 | /* support.cc says this is needed */ | |
31855516 | 15 | #if USE_OPENSSL |
b3a8ae1b AR |
16 | |
17 | #include "comm.h" | |
2e198b84 | 18 | #include "fd.h" |
d620ae0e CT |
19 | #include "fde.h" |
20 | #include "globals.h" | |
40f1e76d | 21 | #include "ip/Address.h" |
7c8ee688 | 22 | #include "parser/BinaryTokenizer.h" |
edb876ab | 23 | #include "SquidTime.h" |
b3a8ae1b | 24 | #include "ssl/bio.h" |
8693472e | 25 | |
b3a8ae1b AR |
26 | #if HAVE_OPENSSL_SSL_H |
27 | #include <openssl/ssl.h> | |
28 | #endif | |
29 | ||
b3a8ae1b AR |
30 | #if _SQUID_WINDOWS_ |
31 | extern int socket_read_method(int, char *, int); | |
32 | extern int socket_write_method(int, const char *, int); | |
33 | #endif | |
34 | ||
35 | /* BIO callbacks */ | |
36 | static int squid_bio_write(BIO *h, const char *buf, int num); | |
37 | static int squid_bio_read(BIO *h, char *buf, int size); | |
38 | static int squid_bio_puts(BIO *h, const char *str); | |
39 | //static int squid_bio_gets(BIO *h, char *str, int size); | |
40 | static long squid_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2); | |
41 | static int squid_bio_create(BIO *h); | |
42 | static int squid_bio_destroy(BIO *data); | |
43 | /* SSL callbacks */ | |
44 | static void squid_ssl_info(const SSL *ssl, int where, int ret); | |
45 | ||
17e98f24 AJ |
46 | #if HAVE_LIBCRYPTO_BIO_METH_NEW |
47 | static BIO_METHOD *SquidMethods = nullptr; | |
48 | #else | |
b3a8ae1b AR |
49 | /// Initialization structure for the BIO table with |
50 | /// Squid-specific methods and BIO method wrappers. | |
51 | static BIO_METHOD SquidMethods = { | |
52 | BIO_TYPE_SOCKET, | |
53 | "squid", | |
54 | squid_bio_write, | |
55 | squid_bio_read, | |
56 | squid_bio_puts, | |
57 | NULL, // squid_bio_gets not supported | |
58 | squid_bio_ctrl, | |
59 | squid_bio_create, | |
60 | squid_bio_destroy, | |
61 | NULL // squid_callback_ctrl not supported | |
62 | }; | |
093deea9 | 63 | #endif |
b3a8ae1b AR |
64 | |
65 | BIO * | |
86f77270 | 66 | Ssl::Bio::Create(const int fd, Security::Io::Type type) |
b3a8ae1b | 67 | { |
17e98f24 | 68 | #if HAVE_LIBCRYPTO_BIO_METH_NEW |
093deea9 CT |
69 | if (!SquidMethods) { |
70 | SquidMethods = BIO_meth_new(BIO_TYPE_SOCKET, "squid"); | |
71 | BIO_meth_set_write(SquidMethods, squid_bio_write); | |
72 | BIO_meth_set_read(SquidMethods, squid_bio_read); | |
73 | BIO_meth_set_puts(SquidMethods, squid_bio_puts); | |
74 | BIO_meth_set_gets(SquidMethods, NULL); | |
75 | BIO_meth_set_ctrl(SquidMethods, squid_bio_ctrl); | |
76 | BIO_meth_set_create(SquidMethods, squid_bio_create); | |
77 | BIO_meth_set_destroy(SquidMethods, squid_bio_destroy); | |
78 | } | |
2a268a06 | 79 | const BIO_METHOD *useMethod = SquidMethods; |
17e98f24 AJ |
80 | #else |
81 | BIO_METHOD *useMethod = &SquidMethods; | |
093deea9 | 82 | #endif |
ac756c8c | 83 | |
2a268a06 CT |
84 | if (BIO *bio = BIO_new(useMethod)) { |
85 | BIO_int_ctrl(bio, BIO_C_SET_FD, type, fd); | |
86 | return bio; | |
87 | } | |
b3a8ae1b AR |
88 | return NULL; |
89 | } | |
90 | ||
91 | void | |
92 | Ssl::Bio::Link(SSL *ssl, BIO *bio) | |
93 | { | |
94 | SSL_set_bio(ssl, bio, bio); // cannot fail | |
95 | SSL_set_info_callback(ssl, &squid_ssl_info); // does not provide diagnostic | |
96 | } | |
97 | ||
b3a8ae1b AR |
98 | Ssl::Bio::Bio(const int anFd): fd_(anFd) |
99 | { | |
100 | debugs(83, 7, "Bio constructed, this=" << this << " FD " << fd_); | |
101 | } | |
102 | ||
103 | Ssl::Bio::~Bio() | |
104 | { | |
105 | debugs(83, 7, "Bio destructing, this=" << this << " FD " << fd_); | |
b3a8ae1b AR |
106 | } |
107 | ||
108 | int Ssl::Bio::write(const char *buf, int size, BIO *table) | |
109 | { | |
110 | errno = 0; | |
111 | #if _SQUID_WINDOWS_ | |
112 | const int result = socket_write_method(fd_, buf, size); | |
113 | #else | |
114 | const int result = default_write_method(fd_, buf, size); | |
115 | #endif | |
116 | const int xerrno = errno; | |
117 | debugs(83, 5, "FD " << fd_ << " wrote " << result << " <= " << size); | |
118 | ||
119 | BIO_clear_retry_flags(table); | |
120 | if (result < 0) { | |
121 | const bool ignoreError = ignoreErrno(xerrno) != 0; | |
122 | debugs(83, 5, "error: " << xerrno << " ignored: " << ignoreError); | |
123 | if (ignoreError) | |
124 | BIO_set_retry_write(table); | |
125 | } | |
126 | ||
127 | return result; | |
128 | } | |
129 | ||
130 | int | |
131 | Ssl::Bio::read(char *buf, int size, BIO *table) | |
132 | { | |
133 | errno = 0; | |
134 | #if _SQUID_WINDOWS_ | |
135 | const int result = socket_read_method(fd_, buf, size); | |
136 | #else | |
137 | const int result = default_read_method(fd_, buf, size); | |
138 | #endif | |
139 | const int xerrno = errno; | |
140 | debugs(83, 5, "FD " << fd_ << " read " << result << " <= " << size); | |
141 | ||
142 | BIO_clear_retry_flags(table); | |
143 | if (result < 0) { | |
144 | const bool ignoreError = ignoreErrno(xerrno) != 0; | |
145 | debugs(83, 5, "error: " << xerrno << " ignored: " << ignoreError); | |
146 | if (ignoreError) | |
147 | BIO_set_retry_read(table); | |
148 | } | |
149 | ||
150 | return result; | |
151 | } | |
152 | ||
153 | /// Called whenever the SSL connection state changes, an alert appears, or an | |
154 | /// error occurs. See SSL_set_info_callback(). | |
155 | void | |
156 | Ssl::Bio::stateChanged(const SSL *ssl, int where, int ret) | |
157 | { | |
158 | // Here we can use (where & STATE) to check the current state. | |
159 | // Many STATE values are possible, including: SSL_CB_CONNECT_LOOP, | |
160 | // SSL_CB_ACCEPT_LOOP, SSL_CB_HANDSHAKE_START, and SSL_CB_HANDSHAKE_DONE. | |
161 | // For example: | |
162 | // if (where & SSL_CB_HANDSHAKE_START) | |
163 | // debugs(83, 9, "Trying to establish the SSL connection"); | |
164 | // else if (where & SSL_CB_HANDSHAKE_DONE) | |
165 | // debugs(83, 9, "SSL connection established"); | |
166 | ||
d620ae0e | 167 | debugs(83, 7, "FD " << fd_ << " now: 0x" << std::hex << where << std::dec << ' ' << |
b3a8ae1b AR |
168 | SSL_state_string(ssl) << " (" << SSL_state_string_long(ssl) << ")"); |
169 | } | |
170 | ||
edb876ab CT |
171 | Ssl::ClientBio::ClientBio(const int anFd): |
172 | Bio(anFd), | |
173 | holdRead_(false), | |
174 | holdWrite_(false), | |
175 | helloSize(0), | |
176 | abortReason(nullptr) | |
177 | { | |
178 | renegotiations.configure(10*1000); | |
179 | } | |
180 | ||
e1f72a8b | 181 | void |
d620ae0e CT |
182 | Ssl::ClientBio::stateChanged(const SSL *ssl, int where, int ret) |
183 | { | |
184 | Ssl::Bio::stateChanged(ssl, where, ret); | |
edb876ab CT |
185 | // detect client-initiated renegotiations DoS (CVE-2011-1473) |
186 | if (where & SSL_CB_HANDSHAKE_START) { | |
187 | const int reneg = renegotiations.count(1); | |
188 | ||
189 | if (abortReason) | |
190 | return; // already decided and informed the admin | |
191 | ||
192 | if (reneg > RenegotiationsLimit) { | |
193 | abortReason = "renegotiate requests flood"; | |
194 | debugs(83, DBG_IMPORTANT, "Terminating TLS connection [from " << fd_table[fd_].ipaddr << "] due to " << abortReason << ". This connection received " << | |
195 | reneg << " renegotiate requests in the last " << | |
196 | RenegotiationsWindow << " seconds (and " << | |
197 | renegotiations.remembered() << " requests total)."); | |
198 | } | |
199 | } | |
d620ae0e CT |
200 | } |
201 | ||
202 | int | |
203 | Ssl::ClientBio::write(const char *buf, int size, BIO *table) | |
204 | { | |
edb876ab CT |
205 | if (abortReason) { |
206 | debugs(83, 3, "BIO on FD " << fd_ << " is aborted"); | |
207 | BIO_clear_retry_flags(table); | |
208 | return -1; | |
209 | } | |
210 | ||
d620ae0e CT |
211 | if (holdWrite_) { |
212 | BIO_set_retry_write(table); | |
213 | return 0; | |
214 | } | |
215 | ||
216 | return Ssl::Bio::write(buf, size, table); | |
217 | } | |
218 | ||
d620ae0e CT |
219 | int |
220 | Ssl::ClientBio::read(char *buf, int size, BIO *table) | |
221 | { | |
edb876ab CT |
222 | if (abortReason) { |
223 | debugs(83, 3, "BIO on FD " << fd_ << " is aborted"); | |
224 | BIO_clear_retry_flags(table); | |
225 | return -1; | |
226 | } | |
227 | ||
d620ae0e CT |
228 | if (holdRead_) { |
229 | debugs(83, 7, "Hold flag is set, retry latter. (Hold " << size << "bytes)"); | |
230 | BIO_set_retry_read(table); | |
231 | return -1; | |
232 | } | |
233 | ||
3cae14a6 CT |
234 | if (!rbuf.isEmpty()) { |
235 | int bytes = (size <= (int)rbuf.length() ? size : rbuf.length()); | |
236 | memcpy(buf, rbuf.rawContent(), bytes); | |
237 | rbuf.consume(bytes); | |
238 | return bytes; | |
239 | } else | |
240 | return Ssl::Bio::read(buf, size, table); | |
d620ae0e CT |
241 | |
242 | return -1; | |
243 | } | |
244 | ||
d20cf186 AR |
245 | Ssl::ServerBio::ServerBio(const int anFd): |
246 | Bio(anFd), | |
247 | helloMsgSize(0), | |
248 | helloBuild(false), | |
249 | allowSplice(false), | |
250 | allowBump(false), | |
251 | holdWrite_(false), | |
0bffe3ce | 252 | holdRead_(true), |
d20cf186 AR |
253 | record_(false), |
254 | parsedHandshake(false), | |
0bffe3ce | 255 | parseError(false), |
d20cf186 AR |
256 | bumpMode_(bumpNone), |
257 | rbufConsumePos(0) | |
258 | { | |
259 | } | |
260 | ||
d620ae0e CT |
261 | void |
262 | Ssl::ServerBio::stateChanged(const SSL *ssl, int where, int ret) | |
263 | { | |
264 | Ssl::Bio::stateChanged(ssl, where, ret); | |
265 | } | |
266 | ||
267 | void | |
21530947 | 268 | Ssl::ServerBio::setClientFeatures(Security::TlsDetails::Pointer const &details, SBuf const &aHello) |
d620ae0e | 269 | { |
21530947 | 270 | clientTlsDetails = details; |
6744c1a8 | 271 | clientSentHello = aHello; |
d620ae0e CT |
272 | }; |
273 | ||
55369ae6 | 274 | int |
a465cd53 | 275 | Ssl::ServerBio::read(char *buf, int size, BIO *table) |
55369ae6 | 276 | { |
d20cf186 | 277 | if (parsedHandshake) // done parsing TLS Hello |
a465cd53 | 278 | return readAndGive(buf, size, table); |
d20cf186 AR |
279 | else |
280 | return readAndParse(buf, size, table); | |
a465cd53 | 281 | } |
55369ae6 | 282 | |
a465cd53 AR |
283 | /// Read and give everything to OpenSSL. |
284 | int | |
285 | Ssl::ServerBio::readAndGive(char *buf, const int size, BIO *table) | |
6821c276 | 286 | { |
a465cd53 AR |
287 | // If we have unused buffered bytes, give those bytes to OpenSSL now, |
288 | // before reading more. TODO: Read if we have buffered less than size? | |
289 | if (rbufConsumePos < rbuf.length()) | |
290 | return giveBuffered(buf, size); | |
55369ae6 | 291 | |
a465cd53 AR |
292 | if (record_) { |
293 | const int result = readAndBuffer(table); | |
294 | if (result <= 0) | |
295 | return result; | |
296 | return giveBuffered(buf, size); | |
55369ae6 AR |
297 | } |
298 | ||
a465cd53 | 299 | return Ssl::Bio::read(buf, size, table); |
55369ae6 AR |
300 | } |
301 | ||
a465cd53 | 302 | /// Read and give everything to our parser. |
d20cf186 | 303 | /// When/if parsing is finished (successfully or not), start giving to OpenSSL. |
d620ae0e | 304 | int |
a465cd53 | 305 | Ssl::ServerBio::readAndParse(char *buf, const int size, BIO *table) |
d620ae0e | 306 | { |
a465cd53 AR |
307 | const int result = readAndBuffer(table); |
308 | if (result <= 0) | |
309 | return result; | |
6821c276 | 310 | |
d20cf186 AR |
311 | try { |
312 | if (!parser_.parseHello(rbuf)) { | |
313 | // need more data to finish parsing | |
6821c276 | 314 | BIO_set_retry_read(table); |
a465cd53 AR |
315 | return -1; |
316 | } | |
d20cf186 AR |
317 | parsedHandshake = true; // done parsing (successfully) |
318 | } | |
319 | catch (const std::exception &ex) { | |
320 | debugs(83, 2, "parsing error on FD " << fd_ << ": " << ex.what()); | |
321 | parsedHandshake = true; // done parsing (due to an error) | |
0bffe3ce | 322 | parseError = true; |
55369ae6 AR |
323 | } |
324 | ||
325 | if (holdRead_) { | |
3945c91d SM |
326 | debugs(83, 7, "Hold flag is set, retry latter. (Hold " << size << "bytes)"); |
327 | BIO_set_retry_read(table); | |
328 | return -1; | |
55369ae6 AR |
329 | } |
330 | ||
a465cd53 | 331 | return giveBuffered(buf, size); |
6821c276 | 332 | } |
55369ae6 | 333 | |
a465cd53 AR |
334 | /// Reads more data into the read buffer. Returns either the number of bytes |
335 | /// read or, on errors (including "try again" errors), a negative number. | |
d620ae0e | 336 | int |
a465cd53 | 337 | Ssl::ServerBio::readAndBuffer(BIO *table) |
d620ae0e | 338 | { |
a465cd53 AR |
339 | char *space = rbuf.rawSpace(SQUID_TCP_SO_RCVBUF); |
340 | const int result = Ssl::Bio::read(space, rbuf.spaceSize(), table); | |
341 | if (result <= 0) | |
342 | return result; | |
6821c276 | 343 | |
a465cd53 AR |
344 | rbuf.forceSize(rbuf.length() + result); |
345 | return result; | |
346 | } | |
6821c276 | 347 | |
a465cd53 AR |
348 | /// give previously buffered bytes to OpenSSL |
349 | /// returns the number of bytes given | |
350 | int | |
351 | Ssl::ServerBio::giveBuffered(char *buf, const int size) | |
352 | { | |
353 | if (rbuf.length() <= rbufConsumePos) | |
354 | return -1; // buffered nothing yet | |
355 | ||
356 | const int unsent = rbuf.length() - rbufConsumePos; | |
357 | const int bytes = (size <= unsent ? size : unsent); | |
358 | memcpy(buf, rbuf.rawContent() + rbufConsumePos, bytes); | |
359 | rbufConsumePos += bytes; | |
360 | debugs(83, 7, bytes << "<=" << size << " bytes to OpenSSL"); | |
361 | return bytes; | |
d620ae0e CT |
362 | } |
363 | ||
1110989a CT |
364 | // This function makes the required checks to examine if the client hello |
365 | // message is compatible with the features provided by OpenSSL toolkit. | |
a95989ed | 366 | // If the features are compatible and can be supported it tries to rewrite SSL |
1110989a | 367 | // structure members, to replace the hello message created by openSSL, with the |
a95989ed | 368 | // web client SSL hello message. |
1110989a CT |
369 | // This is mostly possible in the cases where the web client uses openSSL |
370 | // library similar with this one used by squid. | |
a95989ed | 371 | static bool |
21530947 | 372 | adjustSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, SBuf &helloMessage) |
7f4e9b73 | 373 | { |
a95989ed | 374 | #if SQUID_USE_OPENSSL_HELLO_OVERWRITE_HACK |
6744c1a8 CT |
375 | if (!details) |
376 | return false; | |
377 | ||
a95989ed CT |
378 | if (!ssl->s3) { |
379 | debugs(83, 5, "No SSLv3 data found!"); | |
380 | return false; | |
381 | } | |
382 | ||
7f4e9b73 CT |
383 | // If the client supports compression but our context does not support |
384 | // we can not adjust. | |
b4fca8e9 | 385 | #if !defined(OPENSSL_NO_COMP) |
d9219c2b | 386 | const bool requireCompression = (details->compressionSupported && ssl->ctx->comp_methods == nullptr); |
a36e9cb2 | 387 | #else |
67c99fc6 | 388 | const bool requireCompression = details->compressionSupported; |
a36e9cb2 SH |
389 | #endif |
390 | if (requireCompression) { | |
7f4e9b73 | 391 | debugs(83, 5, "Client Hello Data supports compression, but we do not!"); |
a95989ed | 392 | return false; |
7f4e9b73 CT |
393 | } |
394 | ||
7f4e9b73 | 395 | #if !defined(SSL_TLSEXT_HB_ENABLED) |
21530947 | 396 | if (details->doHeartBeats) { |
7f4e9b73 | 397 | debugs(83, 5, "Client Hello Data supports HeartBeats but we do not support!"); |
a95989ed | 398 | return false; |
7f4e9b73 CT |
399 | } |
400 | #endif | |
401 | ||
c05c0c94 AR |
402 | if (details->unsupportedExtensions) { |
403 | debugs(83, 5, "Client Hello contains extensions that we do not support!"); | |
404 | return false; | |
405 | } | |
406 | ||
407 | SSL3_BUFFER *wb=&(ssl->s3->wbuf); | |
408 | if (wb->len < (size_t)helloMessage.length()) { | |
409 | debugs(83, 5, "Client Hello exceeds OpenSSL buffer: " << helloMessage.length() << " >= " << wb->len); | |
410 | return false; | |
411 | } | |
412 | ||
413 | /* Check whether all on-the-wire ciphers are supported by OpenSSL. */ | |
414 | ||
415 | const auto &wireCiphers = details->ciphers; | |
416 | Security::TlsDetails::Ciphers::size_type ciphersToFind = wireCiphers.size(); | |
417 | ||
418 | // RFC 5746: "TLS_EMPTY_RENEGOTIATION_INFO_SCSV is not a true cipher suite". | |
419 | // It is commonly seen on the wire, including in from-OpenSSL traffic, but | |
420 | // SSL_get_ciphers() does not return this _pseudo_ cipher suite in my tests. | |
421 | // If OpenSSL supports scsvCipher, we count it (at most once) further below. | |
8693472e | 422 | #if defined(TLSEXT_TYPE_renegotiate) |
c05c0c94 AR |
423 | // the 0x00FFFF mask converts 3-byte OpenSSL cipher to our 2-byte cipher |
424 | const uint16_t scsvCipher = SSL3_CK_SCSV & 0x00FFFF; | |
425 | #else | |
426 | const uint16_t scsvCipher = 0; | |
7f4e9b73 | 427 | #endif |
c05c0c94 AR |
428 | |
429 | STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(ssl); | |
430 | const int supportedCipherCount = sk_SSL_CIPHER_num(cipher_stack); | |
431 | for (int idx = 0; idx < supportedCipherCount && ciphersToFind > 0; ++idx) { | |
432 | const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(cipher_stack, idx); | |
433 | const auto id = SSL_CIPHER_get_id(cipher) & 0x00FFFF; | |
434 | if (wireCiphers.find(id) != wireCiphers.end() && (!scsvCipher || id != scsvCipher)) | |
435 | --ciphersToFind; | |
7f4e9b73 CT |
436 | } |
437 | ||
c05c0c94 AR |
438 | if (ciphersToFind > 0 && scsvCipher && wireCiphers.find(scsvCipher) != wireCiphers.end()) |
439 | --ciphersToFind; | |
440 | ||
441 | if (ciphersToFind > 0) { | |
442 | // TODO: Add slowlyReportUnsupportedCiphers() to slowly find and report each of them | |
443 | debugs(83, 5, "Client Hello Data has " << ciphersToFind << " ciphers that we do not support!"); | |
5d65362c | 444 | return false; |
c05c0c94 | 445 | } |
7f4e9b73 | 446 | |
a95989ed | 447 | debugs(83, 5, "OpenSSL SSL struct will be adjusted to mimic client hello data!"); |
7f4e9b73 CT |
448 | |
449 | //Adjust ssl structure data. | |
7f4e9b73 | 450 | // We need to fix the random in SSL struct: |
21530947 CT |
451 | if (details->clientRandom.length() == SSL3_RANDOM_SIZE) |
452 | memcpy(ssl->s3->client_random, details->clientRandom.c_str(), SSL3_RANDOM_SIZE); | |
453 | memcpy(wb->buf, helloMessage.rawContent(), helloMessage.length()); | |
454 | wb->left = helloMessage.length(); | |
7f4e9b73 | 455 | |
21530947 CT |
456 | size_t mainHelloSize = helloMessage.length() - 5; |
457 | const char *mainHello = helloMessage.rawContent() + 5; | |
4cabb5e5 | 458 | assert((size_t)ssl->init_buf->max > mainHelloSize); |
7f4e9b73 CT |
459 | memcpy(ssl->init_buf->data, mainHello, mainHelloSize); |
460 | debugs(83, 5, "Hello Data init and adjustd sizes :" << ssl->init_num << " = "<< mainHelloSize); | |
461 | ssl->init_num = mainHelloSize; | |
462 | ssl->s3->wpend_ret = mainHelloSize; | |
463 | ssl->s3->wpend_tot = mainHelloSize; | |
a95989ed CT |
464 | return true; |
465 | #else | |
466 | return false; | |
467 | #endif | |
7f4e9b73 CT |
468 | } |
469 | ||
d620ae0e CT |
470 | int |
471 | Ssl::ServerBio::write(const char *buf, int size, BIO *table) | |
472 | { | |
473 | ||
474 | if (holdWrite_) { | |
a465cd53 | 475 | debugs(83, 7, "postpone writing " << size << " bytes to SSL FD " << fd_); |
d620ae0e CT |
476 | BIO_set_retry_write(table); |
477 | return -1; | |
478 | } | |
479 | ||
5d65362c | 480 | if (!helloBuild && (bumpMode_ == Ssl::bumpPeek || bumpMode_ == Ssl::bumpStare)) { |
6744c1a8 CT |
481 | // buf contains OpenSSL-generated ClientHello. We assume it has a |
482 | // complete ClientHello and nothing else, but cannot fully verify | |
483 | // that quickly. We only verify that buf starts with a v3+ record | |
484 | // containing ClientHello. | |
485 | Must(size >= 2); // enough for version and content_type checks below | |
486 | Must(buf[1] >= 3); // record's version.major; determines buf[0] meaning | |
487 | Must(buf[0] == 22); // TLSPlaintext.content_type == handshake in v3+ | |
488 | ||
489 | //Hello message is the first message we write to server | |
490 | assert(helloMsg.isEmpty()); | |
491 | ||
492 | if (auto ssl = fd_table[fd_].ssl.get()) { | |
493 | if (bumpMode_ == Ssl::bumpPeek) { | |
494 | // we should not be here if we failed to parse the client-sent ClientHello | |
495 | Must(!clientSentHello.isEmpty()); | |
496 | if (adjustSSL(ssl, clientTlsDetails, clientSentHello)) | |
5d65362c | 497 | allowBump = true; |
6744c1a8 CT |
498 | allowSplice = true; |
499 | // Replace OpenSSL-generated ClientHello with client-sent one. | |
500 | helloMsg.append(clientSentHello); | |
501 | debugs(83, 7, "FD " << fd_ << ": Using client-sent ClientHello for peek mode"); | |
502 | } else { /*Ssl::bumpStare*/ | |
503 | allowBump = true; | |
504 | if (!clientSentHello.isEmpty() && adjustSSL(ssl, clientTlsDetails, clientSentHello)) { | |
505 | allowSplice = true; | |
506 | helloMsg.append(clientSentHello); | |
507 | debugs(83, 7, "FD " << fd_ << ": Using client-sent ClientHello for stare mode"); | |
7f4e9b73 | 508 | } |
d620ae0e CT |
509 | } |
510 | } | |
6744c1a8 | 511 | // if we did not use the client-sent ClientHello, then use the OpenSSL-generated one |
8693472e | 512 | if (helloMsg.isEmpty()) |
7f4e9b73 CT |
513 | helloMsg.append(buf, size); |
514 | ||
d620ae0e | 515 | helloBuild = true; |
8693472e | 516 | helloMsgSize = helloMsg.length(); |
5d65362c | 517 | //allowBump = true; |
7f4e9b73 CT |
518 | |
519 | if (allowSplice) { | |
e1f72a8b | 520 | // Do not write yet..... |
7f4e9b73 CT |
521 | BIO_set_retry_write(table); |
522 | return -1; | |
523 | } | |
d620ae0e CT |
524 | } |
525 | ||
8693472e | 526 | if (!helloMsg.isEmpty()) { |
d620ae0e | 527 | debugs(83, 7, "buffered write for FD " << fd_); |
8693472e | 528 | int ret = Ssl::Bio::write(helloMsg.rawContent(), helloMsg.length(), table); |
d620ae0e | 529 | helloMsg.consume(ret); |
8693472e | 530 | if (!helloMsg.isEmpty()) { |
d620ae0e CT |
531 | // We need to retry sendind data. |
532 | // Say to openSSL to retry sending hello message | |
533 | BIO_set_retry_write(table); | |
534 | return -1; | |
535 | } | |
536 | ||
537 | // Sending hello message complete. Do not send more data for now... | |
7f4e9b73 CT |
538 | holdWrite_ = true; |
539 | ||
a95989ed CT |
540 | // spoof openSSL that we write what it ask us to write |
541 | return size; | |
d620ae0e CT |
542 | } else |
543 | return Ssl::Bio::write(buf, size, table); | |
544 | } | |
545 | ||
546 | void | |
547 | Ssl::ServerBio::flush(BIO *table) | |
548 | { | |
8693472e CT |
549 | if (!helloMsg.isEmpty()) { |
550 | int ret = Ssl::Bio::write(helloMsg.rawContent(), helloMsg.length(), table); | |
d620ae0e CT |
551 | helloMsg.consume(ret); |
552 | } | |
553 | } | |
554 | ||
89c5ca0f CT |
555 | bool |
556 | Ssl::ServerBio::resumingSession() | |
557 | { | |
d9219c2b | 558 | return parser_.resumingSession; |
55369ae6 | 559 | } |
89c5ca0f | 560 | |
b3a8ae1b AR |
561 | /// initializes BIO table after allocation |
562 | static int | |
563 | squid_bio_create(BIO *bi) | |
564 | { | |
17e98f24 | 565 | #if !HAVE_LIBCRYPTO_BIO_GET_INIT |
b3a8ae1b AR |
566 | bi->init = 0; // set when we store Bio object and socket fd (BIO_C_SET_FD) |
567 | bi->num = 0; | |
b3a8ae1b | 568 | bi->flags = 0; |
093deea9 CT |
569 | #else |
570 | // No need to set more, openSSL initialize BIO memory to zero. | |
571 | #endif | |
572 | ||
573 | BIO_set_data(bi, NULL); | |
b3a8ae1b AR |
574 | return 1; |
575 | } | |
576 | ||
577 | /// cleans BIO table before deallocation | |
578 | static int | |
579 | squid_bio_destroy(BIO *table) | |
580 | { | |
093deea9 CT |
581 | delete static_cast<Ssl::Bio*>(BIO_get_data(table)); |
582 | BIO_set_data(table, NULL); | |
b3a8ae1b AR |
583 | return 1; |
584 | } | |
585 | ||
586 | /// wrapper for Bio::write() | |
587 | static int | |
588 | squid_bio_write(BIO *table, const char *buf, int size) | |
589 | { | |
093deea9 | 590 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); |
b3a8ae1b AR |
591 | assert(bio); |
592 | return bio->write(buf, size, table); | |
593 | } | |
594 | ||
595 | /// wrapper for Bio::read() | |
596 | static int | |
597 | squid_bio_read(BIO *table, char *buf, int size) | |
598 | { | |
093deea9 | 599 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); |
b3a8ae1b AR |
600 | assert(bio); |
601 | return bio->read(buf, size, table); | |
602 | } | |
603 | ||
604 | /// implements puts() via write() | |
605 | static int | |
606 | squid_bio_puts(BIO *table, const char *str) | |
607 | { | |
608 | assert(str); | |
609 | return squid_bio_write(table, str, strlen(str)); | |
610 | } | |
611 | ||
612 | /// other BIO manipulations (those without dedicated callbacks in BIO table) | |
613 | static long | |
614 | squid_bio_ctrl(BIO *table, int cmd, long arg1, void *arg2) | |
615 | { | |
616 | debugs(83, 5, table << ' ' << cmd << '(' << arg1 << ", " << arg2 << ')'); | |
617 | ||
618 | switch (cmd) { | |
619 | case BIO_C_SET_FD: { | |
620 | assert(arg2); | |
621 | const int fd = *static_cast<int*>(arg2); | |
d620ae0e | 622 | Ssl::Bio *bio; |
86f77270 | 623 | if (arg1 == Security::Io::BIO_TO_SERVER) |
d620ae0e CT |
624 | bio = new Ssl::ServerBio(fd); |
625 | else | |
626 | bio = new Ssl::ClientBio(fd); | |
093deea9 CT |
627 | assert(!BIO_get_data(table)); |
628 | BIO_set_data(table, bio); | |
629 | BIO_set_init(table, 1); | |
b3a8ae1b AR |
630 | return 0; |
631 | } | |
632 | ||
633 | case BIO_C_GET_FD: | |
093deea9 CT |
634 | if (BIO_get_init(table)) { |
635 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); | |
b3a8ae1b AR |
636 | assert(bio); |
637 | if (arg2) | |
638 | *static_cast<int*>(arg2) = bio->fd(); | |
639 | return bio->fd(); | |
640 | } | |
641 | return -1; | |
642 | ||
54f3b032 | 643 | case BIO_CTRL_DUP: |
e1f72a8b | 644 | // Should implemented if the SSL_dup openSSL API function |
54f3b032 CT |
645 | // used anywhere in squid. |
646 | return 0; | |
647 | ||
b3a8ae1b | 648 | case BIO_CTRL_FLUSH: |
093deea9 CT |
649 | if (BIO_get_init(table)) { |
650 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); | |
b3a8ae1b | 651 | assert(bio); |
d620ae0e | 652 | bio->flush(table); |
b3a8ae1b AR |
653 | return 1; |
654 | } | |
655 | return 0; | |
656 | ||
f53969cc SM |
657 | /* we may also need to implement these: |
658 | case BIO_CTRL_RESET: | |
659 | case BIO_C_FILE_SEEK: | |
660 | case BIO_C_FILE_TELL: | |
661 | case BIO_CTRL_INFO: | |
662 | case BIO_CTRL_GET_CLOSE: | |
663 | case BIO_CTRL_SET_CLOSE: | |
664 | case BIO_CTRL_PENDING: | |
665 | case BIO_CTRL_WPENDING: | |
666 | */ | |
b3a8ae1b AR |
667 | default: |
668 | return 0; | |
669 | ||
670 | } | |
671 | ||
672 | return 0; /* NOTREACHED */ | |
673 | } | |
674 | ||
675 | /// wrapper for Bio::stateChanged() | |
676 | static void | |
677 | squid_ssl_info(const SSL *ssl, int where, int ret) | |
678 | { | |
679 | if (BIO *table = SSL_get_rbio(ssl)) { | |
093deea9 | 680 | if (Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table))) |
b3a8ae1b AR |
681 | bio->stateChanged(ssl, where, ret); |
682 | } | |
683 | } | |
684 | ||
21530947 CT |
685 | void |
686 | applyTlsDetailsToSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, Ssl::BumpMode bumpMode) | |
d620ae0e | 687 | { |
21530947 CT |
688 | // To increase the possibility for bumping after peek mode selection or |
689 | // splicing after stare mode selection it is good to set the | |
690 | // SSL protocol version. | |
d9219c2b CT |
691 | // The SSL_set_ssl_method is wrong here because it will restrict the |
692 | // permitted transport version to be identical to the version used in the | |
693 | // ClientHello message. | |
21530947 CT |
694 | // For example will prevent comunnicating with a tls1.0 server if the |
695 | // client sent and tlsv1.2 Hello message. | |
e1f72a8b | 696 | #if defined(TLSEXT_NAMETYPE_host_name) |
21530947 CT |
697 | if (!details->serverName.isEmpty()) { |
698 | SSL_set_tlsext_host_name(ssl, details->serverName.c_str()); | |
458fd470 | 699 | } |
e68e8b9a | 700 | #endif |
2bcab852 | 701 | |
21530947 CT |
702 | if (!details->ciphers.empty()) { |
703 | SBuf strCiphers; | |
704 | for (auto cipherId: details->ciphers) { | |
705 | unsigned char cbytes[3]; | |
706 | cbytes[0] = (cipherId >> 8) & 0xFF; | |
707 | cbytes[1] = cipherId & 0xFF; | |
708 | cbytes[2] = 0; | |
17e98f24 AJ |
709 | #if HAVE_LIBSSL_SSL_CIPHER_FIND |
710 | const SSL_CIPHER *c = SSL_CIPHER_find(ssl, cbytes); | |
711 | #else | |
21530947 | 712 | const SSL_METHOD *method = SSLv23_method(); |
21530947 | 713 | const SSL_CIPHER *c = method->get_cipher_by_char(cbytes); |
093deea9 | 714 | #endif |
d620ae0e | 715 | if (c != NULL) { |
21530947 CT |
716 | if (!strCiphers.isEmpty()) |
717 | strCiphers.append(":"); | |
093deea9 | 718 | strCiphers.append(SSL_CIPHER_get_name(c)); |
d620ae0e CT |
719 | } |
720 | } | |
21530947 CT |
721 | if (!strCiphers.isEmpty()) |
722 | SSL_set_cipher_list(ssl, strCiphers.c_str()); | |
d620ae0e | 723 | } |
d620ae0e | 724 | |
8693472e | 725 | #if defined(SSL_OP_NO_COMPRESSION) /* XXX: OpenSSL 0.9.8k lacks SSL_OP_NO_COMPRESSION */ |
67c99fc6 | 726 | if (!details->compressionSupported) |
a95989ed CT |
727 | SSL_set_options(ssl, SSL_OP_NO_COMPRESSION); |
728 | #endif | |
729 | ||
89c5ca0f | 730 | #if defined(TLSEXT_STATUSTYPE_ocsp) |
21530947 | 731 | if (details->tlsStatusRequest) |
89c5ca0f CT |
732 | SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp); |
733 | #endif | |
734 | ||
735 | #if defined(TLSEXT_TYPE_application_layer_protocol_negotiation) | |
21530947 | 736 | if (!details->tlsAppLayerProtoNeg.isEmpty()) { |
89c5ca0f | 737 | if (bumpMode == Ssl::bumpPeek) |
3152460d | 738 | SSL_set_alpn_protos(ssl, (const unsigned char*)details->tlsAppLayerProtoNeg.rawContent(), details->tlsAppLayerProtoNeg.length()); |
89c5ca0f CT |
739 | else { |
740 | static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'}; | |
741 | SSL_set_alpn_protos(ssl, supported_protos, sizeof(supported_protos)); | |
742 | } | |
743 | } | |
744 | #endif | |
a95989ed CT |
745 | } |
746 | ||
21530947 | 747 | #endif // USE_OPENSSL |
f53969cc | 748 |