]>
Commit | Line | Data |
---|---|---|
b3a8ae1b | 1 | /* |
bde978a6 | 2 | * Copyright (C) 1996-2015 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" | |
d620ae0e CT |
18 | #include "fde.h" |
19 | #include "globals.h" | |
40f1e76d | 20 | #include "ip/Address.h" |
b3a8ae1b | 21 | #include "ssl/bio.h" |
8693472e | 22 | |
b3a8ae1b AR |
23 | #if HAVE_OPENSSL_SSL_H |
24 | #include <openssl/ssl.h> | |
25 | #endif | |
26 | ||
d620ae0e CT |
27 | #undef DO_SSLV23 |
28 | ||
b3a8ae1b AR |
29 | #if _SQUID_WINDOWS_ |
30 | extern int socket_read_method(int, char *, int); | |
31 | extern int socket_write_method(int, const char *, int); | |
32 | #endif | |
33 | ||
34 | /* BIO callbacks */ | |
35 | static int squid_bio_write(BIO *h, const char *buf, int num); | |
36 | static int squid_bio_read(BIO *h, char *buf, int size); | |
37 | static int squid_bio_puts(BIO *h, const char *str); | |
38 | //static int squid_bio_gets(BIO *h, char *str, int size); | |
39 | static long squid_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2); | |
40 | static int squid_bio_create(BIO *h); | |
41 | static int squid_bio_destroy(BIO *data); | |
42 | /* SSL callbacks */ | |
43 | static void squid_ssl_info(const SSL *ssl, int where, int ret); | |
44 | ||
45 | /// Initialization structure for the BIO table with | |
46 | /// Squid-specific methods and BIO method wrappers. | |
47 | static BIO_METHOD SquidMethods = { | |
48 | BIO_TYPE_SOCKET, | |
49 | "squid", | |
50 | squid_bio_write, | |
51 | squid_bio_read, | |
52 | squid_bio_puts, | |
53 | NULL, // squid_bio_gets not supported | |
54 | squid_bio_ctrl, | |
55 | squid_bio_create, | |
56 | squid_bio_destroy, | |
57 | NULL // squid_callback_ctrl not supported | |
58 | }; | |
59 | ||
60 | BIO * | |
d620ae0e | 61 | Ssl::Bio::Create(const int fd, Ssl::Bio::Type type) |
b3a8ae1b AR |
62 | { |
63 | if (BIO *bio = BIO_new(&SquidMethods)) { | |
d620ae0e | 64 | BIO_int_ctrl(bio, BIO_C_SET_FD, type, fd); |
b3a8ae1b AR |
65 | return bio; |
66 | } | |
67 | return NULL; | |
68 | } | |
69 | ||
70 | void | |
71 | Ssl::Bio::Link(SSL *ssl, BIO *bio) | |
72 | { | |
73 | SSL_set_bio(ssl, bio, bio); // cannot fail | |
74 | SSL_set_info_callback(ssl, &squid_ssl_info); // does not provide diagnostic | |
75 | } | |
76 | ||
b3a8ae1b AR |
77 | Ssl::Bio::Bio(const int anFd): fd_(anFd) |
78 | { | |
79 | debugs(83, 7, "Bio constructed, this=" << this << " FD " << fd_); | |
80 | } | |
81 | ||
82 | Ssl::Bio::~Bio() | |
83 | { | |
84 | debugs(83, 7, "Bio destructing, this=" << this << " FD " << fd_); | |
b3a8ae1b AR |
85 | } |
86 | ||
87 | int Ssl::Bio::write(const char *buf, int size, BIO *table) | |
88 | { | |
89 | errno = 0; | |
90 | #if _SQUID_WINDOWS_ | |
91 | const int result = socket_write_method(fd_, buf, size); | |
92 | #else | |
93 | const int result = default_write_method(fd_, buf, size); | |
94 | #endif | |
95 | const int xerrno = errno; | |
96 | debugs(83, 5, "FD " << fd_ << " wrote " << result << " <= " << size); | |
97 | ||
98 | BIO_clear_retry_flags(table); | |
99 | if (result < 0) { | |
100 | const bool ignoreError = ignoreErrno(xerrno) != 0; | |
101 | debugs(83, 5, "error: " << xerrno << " ignored: " << ignoreError); | |
102 | if (ignoreError) | |
103 | BIO_set_retry_write(table); | |
104 | } | |
105 | ||
106 | return result; | |
107 | } | |
108 | ||
109 | int | |
110 | Ssl::Bio::read(char *buf, int size, BIO *table) | |
111 | { | |
112 | errno = 0; | |
113 | #if _SQUID_WINDOWS_ | |
114 | const int result = socket_read_method(fd_, buf, size); | |
115 | #else | |
116 | const int result = default_read_method(fd_, buf, size); | |
117 | #endif | |
118 | const int xerrno = errno; | |
119 | debugs(83, 5, "FD " << fd_ << " read " << result << " <= " << size); | |
120 | ||
121 | BIO_clear_retry_flags(table); | |
122 | if (result < 0) { | |
123 | const bool ignoreError = ignoreErrno(xerrno) != 0; | |
124 | debugs(83, 5, "error: " << xerrno << " ignored: " << ignoreError); | |
125 | if (ignoreError) | |
126 | BIO_set_retry_read(table); | |
127 | } | |
128 | ||
129 | return result; | |
130 | } | |
131 | ||
132 | /// Called whenever the SSL connection state changes, an alert appears, or an | |
133 | /// error occurs. See SSL_set_info_callback(). | |
134 | void | |
135 | Ssl::Bio::stateChanged(const SSL *ssl, int where, int ret) | |
136 | { | |
137 | // Here we can use (where & STATE) to check the current state. | |
138 | // Many STATE values are possible, including: SSL_CB_CONNECT_LOOP, | |
139 | // SSL_CB_ACCEPT_LOOP, SSL_CB_HANDSHAKE_START, and SSL_CB_HANDSHAKE_DONE. | |
140 | // For example: | |
141 | // if (where & SSL_CB_HANDSHAKE_START) | |
142 | // debugs(83, 9, "Trying to establish the SSL connection"); | |
143 | // else if (where & SSL_CB_HANDSHAKE_DONE) | |
144 | // debugs(83, 9, "SSL connection established"); | |
145 | ||
d620ae0e | 146 | debugs(83, 7, "FD " << fd_ << " now: 0x" << std::hex << where << std::dec << ' ' << |
b3a8ae1b AR |
147 | SSL_state_string(ssl) << " (" << SSL_state_string_long(ssl) << ")"); |
148 | } | |
149 | ||
d620ae0e CT |
150 | bool |
151 | Ssl::ClientBio::isClientHello(int state) | |
152 | { | |
d85aad1b | 153 | return ( |
c05a5e2d SM |
154 | state == SSL3_ST_SR_CLNT_HELLO_A || |
155 | state == SSL23_ST_SR_CLNT_HELLO_A || | |
156 | state == SSL23_ST_SR_CLNT_HELLO_B || | |
157 | state == SSL3_ST_SR_CLNT_HELLO_B || | |
158 | state == SSL3_ST_SR_CLNT_HELLO_C | |
e1f72a8b | 159 | ); |
d620ae0e CT |
160 | } |
161 | ||
e1f72a8b | 162 | void |
d620ae0e CT |
163 | Ssl::ClientBio::stateChanged(const SSL *ssl, int where, int ret) |
164 | { | |
165 | Ssl::Bio::stateChanged(ssl, where, ret); | |
166 | } | |
167 | ||
168 | int | |
169 | Ssl::ClientBio::write(const char *buf, int size, BIO *table) | |
170 | { | |
171 | if (holdWrite_) { | |
172 | BIO_set_retry_write(table); | |
173 | return 0; | |
174 | } | |
175 | ||
176 | return Ssl::Bio::write(buf, size, table); | |
177 | } | |
178 | ||
179 | const char *objToString(unsigned char const *bytes, int len) | |
180 | { | |
181 | static std::string buf; | |
182 | buf.clear(); | |
e1f72a8b | 183 | for (int i = 0; i < len; i++ ) { |
d620ae0e CT |
184 | char tmp[3]; |
185 | snprintf(tmp, sizeof(tmp), "%.2x", bytes[i]); | |
186 | buf.append(tmp); | |
187 | } | |
188 | return buf.c_str(); | |
189 | } | |
190 | ||
191 | int | |
192 | Ssl::ClientBio::read(char *buf, int size, BIO *table) | |
193 | { | |
a95989ed | 194 | if (helloState < atHelloReceived) { |
d620ae0e CT |
195 | |
196 | if (rbuf.isNull()) | |
7f4e9b73 | 197 | rbuf.init(1024, 16384); |
d620ae0e CT |
198 | |
199 | size = rbuf.spaceSize() > size ? size : rbuf.spaceSize(); | |
200 | ||
201 | if (!size) | |
202 | return 0; | |
203 | ||
204 | int bytes = Ssl::Bio::read(buf, size, table); | |
869b4328 CT |
205 | if (bytes <= 0) |
206 | return bytes; | |
d620ae0e CT |
207 | rbuf.append(buf, bytes); |
208 | debugs(83, 7, "rbuf size: " << rbuf.contentSize()); | |
209 | } | |
210 | ||
a95989ed | 211 | if (helloState == atHelloNone) { |
d620ae0e CT |
212 | |
213 | const unsigned char *head = (const unsigned char *)rbuf.content(); | |
214 | const char *s = objToString(head, rbuf.contentSize()); | |
215 | debugs(83, 7, "SSL Header: " << s); | |
216 | if (rbuf.contentSize() < 5) { | |
217 | BIO_set_retry_read(table); | |
218 | return 0; | |
219 | } | |
220 | ||
221 | if (head[0] == 0x16) { | |
222 | debugs(83, 7, "SSL version 3 handshake message"); | |
a95989ed CT |
223 | helloSize = (head[3] << 8) + head[4]; |
224 | debugs(83, 7, "SSL Header Size: " << helloSize); | |
225 | helloSize +=5; | |
8693472e | 226 | #if defined(DO_SSLV23) |
e1f72a8b | 227 | } else if ((head[0] & 0x80) && head[2] == 0x01 && head[3] == 0x03) { |
d620ae0e | 228 | debugs(83, 7, "SSL version 2 handshake message with v3 support"); |
a95989ed CT |
229 | helloSize = head[1]; |
230 | helloSize +=5; | |
d620ae0e | 231 | #endif |
e1f72a8b | 232 | } else { |
d620ae0e | 233 | debugs(83, 7, "Not an SSL acceptable handshake message (SSLv2 message?)"); |
3248e962 | 234 | wrongProtocol = true; |
d620ae0e CT |
235 | return -1; |
236 | } | |
237 | ||
a95989ed | 238 | helloState = atHelloStarted; //Next state |
d620ae0e CT |
239 | } |
240 | ||
a95989ed | 241 | if (helloState == atHelloStarted) { |
d620ae0e CT |
242 | const unsigned char *head = (const unsigned char *)rbuf.content(); |
243 | const char *s = objToString(head, rbuf.contentSize()); | |
244 | debugs(83, 7, "SSL Header: " << s); | |
245 | ||
a95989ed | 246 | if (helloSize > rbuf.contentSize()) { |
d620ae0e CT |
247 | BIO_set_retry_read(table); |
248 | return -1; | |
249 | } | |
250 | features.get((const unsigned char *)rbuf.content()); | |
a95989ed | 251 | helloState = atHelloReceived; |
d620ae0e CT |
252 | } |
253 | ||
254 | if (holdRead_) { | |
255 | debugs(83, 7, "Hold flag is set, retry latter. (Hold " << size << "bytes)"); | |
256 | BIO_set_retry_read(table); | |
257 | return -1; | |
258 | } | |
259 | ||
a95989ed | 260 | if (helloState == atHelloReceived) { |
d620ae0e CT |
261 | if (rbuf.hasContent()) { |
262 | int bytes = (size <= rbuf.contentSize() ? size : rbuf.contentSize()); | |
263 | memcpy(buf, rbuf.content(), bytes); | |
264 | rbuf.consume(bytes); | |
265 | return bytes; | |
266 | } else | |
267 | return Ssl::Bio::read(buf, size, table); | |
268 | } | |
269 | ||
270 | return -1; | |
271 | } | |
272 | ||
273 | void | |
274 | Ssl::ServerBio::stateChanged(const SSL *ssl, int where, int ret) | |
275 | { | |
276 | Ssl::Bio::stateChanged(ssl, where, ret); | |
277 | } | |
278 | ||
279 | void | |
7f4e9b73 | 280 | Ssl::ServerBio::setClientFeatures(const Ssl::Bio::sslFeatures &features) |
d620ae0e | 281 | { |
7f4e9b73 CT |
282 | clientFeatures.sslVersion = features.sslVersion; |
283 | clientFeatures.compressMethod = features.compressMethod; | |
284 | clientFeatures.serverName = features.serverName; | |
285 | clientFeatures.clientRequestedCiphers = features.clientRequestedCiphers; | |
286 | clientFeatures.unknownCiphers = features.unknownCiphers; | |
287 | memcpy(clientFeatures.client_random, features.client_random, SSL3_RANDOM_SIZE); | |
8693472e CT |
288 | clientFeatures.helloMessage.clear(); |
289 | clientFeatures.helloMessage.append(features.helloMessage.rawContent(), features.helloMessage.length()); | |
7f4e9b73 CT |
290 | clientFeatures.doHeartBeats = features.doHeartBeats; |
291 | clientFeatures.extensions = features.extensions; | |
292 | featuresSet = true; | |
d620ae0e CT |
293 | }; |
294 | ||
295 | int | |
296 | Ssl::ServerBio::read(char *buf, int size, BIO *table) | |
297 | { | |
298 | int bytes = Ssl::Bio::read(buf, size, table); | |
299 | ||
300 | if (bytes > 0 && record_) { | |
301 | if (rbuf.isNull()) | |
7f4e9b73 | 302 | rbuf.init(1024, 16384); |
d620ae0e | 303 | rbuf.append(buf, bytes); |
7f4e9b73 | 304 | debugs(83, 5, "Record is enabled store " << bytes << " bytes"); |
d620ae0e | 305 | } |
7f4e9b73 | 306 | debugs(83, 5, "Read " << bytes << " from " << size << " bytes"); |
d620ae0e CT |
307 | return bytes; |
308 | } | |
309 | ||
1110989a CT |
310 | // This function makes the required checks to examine if the client hello |
311 | // message is compatible with the features provided by OpenSSL toolkit. | |
a95989ed | 312 | // If the features are compatible and can be supported it tries to rewrite SSL |
1110989a | 313 | // structure members, to replace the hello message created by openSSL, with the |
a95989ed | 314 | // web client SSL hello message. |
1110989a CT |
315 | // This is mostly possible in the cases where the web client uses openSSL |
316 | // library similar with this one used by squid. | |
a95989ed CT |
317 | static bool |
318 | adjustSSL(SSL *ssl, Ssl::Bio::sslFeatures &features) | |
7f4e9b73 | 319 | { |
a95989ed CT |
320 | #if SQUID_USE_OPENSSL_HELLO_OVERWRITE_HACK |
321 | if (!ssl->s3) { | |
322 | debugs(83, 5, "No SSLv3 data found!"); | |
323 | return false; | |
324 | } | |
325 | ||
7f4e9b73 CT |
326 | // If the client supports compression but our context does not support |
327 | // we can not adjust. | |
a36e9cb2 SH |
328 | #if defined(OPENSSL_NO_COMP) |
329 | const bool requireCompression = (features.compressMethod && ssl->ctx->comp_methods == NULL); | |
330 | #else | |
331 | const bool requireCompression = features.compressMethod; | |
332 | #endif | |
333 | if (requireCompression) { | |
7f4e9b73 | 334 | debugs(83, 5, "Client Hello Data supports compression, but we do not!"); |
a95989ed | 335 | return false; |
7f4e9b73 CT |
336 | } |
337 | ||
a95989ed | 338 | // Check ciphers list |
7f4e9b73 CT |
339 | size_t token = 0; |
340 | size_t end = 0; | |
341 | while (token != std::string::npos) { | |
e1f72a8b CT |
342 | end = features.clientRequestedCiphers.find(':',token); |
343 | std::string cipher; | |
344 | cipher.assign(features.clientRequestedCiphers, token, end - token); | |
345 | token = (end != std::string::npos ? end + 1 : std::string::npos); | |
346 | bool found = false; | |
347 | STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(ssl); | |
348 | for (int i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) { | |
349 | SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i); | |
350 | const char *cname = SSL_CIPHER_get_name(c); | |
351 | if (cipher.compare(cname)) { | |
352 | found = true; | |
353 | break; | |
354 | } | |
355 | } | |
356 | if (!found) { | |
357 | debugs(83, 5, "Client Hello Data supports cipher '"<< cipher <<"' but we do not support it!"); | |
358 | return false; | |
359 | } | |
7f4e9b73 CT |
360 | } |
361 | ||
362 | #if !defined(SSL_TLSEXT_HB_ENABLED) | |
363 | if (features.doHeartBeats) { | |
364 | debugs(83, 5, "Client Hello Data supports HeartBeats but we do not support!"); | |
a95989ed | 365 | return false; |
7f4e9b73 CT |
366 | } |
367 | #endif | |
368 | ||
369 | for (std::list<int>::iterator it = features.extensions.begin(); it != features.extensions.end(); ++it) { | |
370 | static int supportedExtensions[] = { | |
8693472e | 371 | #if defined(TLSEXT_TYPE_server_name) |
7f4e9b73 | 372 | TLSEXT_TYPE_server_name, |
4cabb5e5 | 373 | #endif |
8693472e | 374 | #if defined(TLSEXT_TYPE_opaque_prf_input) |
7f4e9b73 CT |
375 | TLSEXT_TYPE_opaque_prf_input, |
376 | #endif | |
8693472e | 377 | #if defined(TLSEXT_TYPE_heartbeat) |
7f4e9b73 CT |
378 | TLSEXT_TYPE_heartbeat, |
379 | #endif | |
8693472e | 380 | #if defined(TLSEXT_TYPE_renegotiate) |
7f4e9b73 CT |
381 | TLSEXT_TYPE_renegotiate, |
382 | #endif | |
8693472e | 383 | #if defined(TLSEXT_TYPE_ec_point_formats) |
7f4e9b73 CT |
384 | TLSEXT_TYPE_ec_point_formats, |
385 | #endif | |
8693472e | 386 | #if defined(TLSEXT_TYPE_elliptic_curves) |
7f4e9b73 CT |
387 | TLSEXT_TYPE_elliptic_curves, |
388 | #endif | |
8693472e | 389 | #if defined(TLSEXT_TYPE_session_ticket) |
7f4e9b73 CT |
390 | TLSEXT_TYPE_session_ticket, |
391 | #endif | |
8693472e | 392 | #if defined(TLSEXT_TYPE_status_request) |
7f4e9b73 CT |
393 | TLSEXT_TYPE_status_request, |
394 | #endif | |
8693472e | 395 | #if defined(TLSEXT_TYPE_use_srtp) |
7f4e9b73 CT |
396 | TLSEXT_TYPE_use_srtp, |
397 | #endif | |
a95989ed | 398 | #if 0 //Allow 13172 Firefox supported extension for testing purposes |
7f4e9b73 CT |
399 | 13172, |
400 | #endif | |
401 | -1 | |
402 | }; | |
403 | bool found = false; | |
404 | for (int i = 0; supportedExtensions[i] != -1; i++) { | |
405 | if (*it == supportedExtensions[i]) { | |
406 | found = true; | |
407 | break; | |
408 | } | |
409 | } | |
410 | if (!found) { | |
411 | debugs(83, 5, "Extension " << *it << " does not supported!"); | |
a95989ed | 412 | return false; |
7f4e9b73 CT |
413 | } |
414 | } | |
415 | ||
a95989ed | 416 | SSL3_BUFFER *wb=&(ssl->s3->wbuf); |
8693472e | 417 | if (wb->len < (size_t)features.helloMessage.length()) |
5d65362c | 418 | return false; |
7f4e9b73 | 419 | |
a95989ed | 420 | debugs(83, 5, "OpenSSL SSL struct will be adjusted to mimic client hello data!"); |
7f4e9b73 CT |
421 | |
422 | //Adjust ssl structure data. | |
7f4e9b73 CT |
423 | // We need to fix the random in SSL struct: |
424 | memcpy(ssl->s3->client_random, features.client_random, SSL3_RANDOM_SIZE); | |
8693472e CT |
425 | memcpy(wb->buf, features.helloMessage.rawContent(), features.helloMessage.length()); |
426 | wb->left = features.helloMessage.length(); | |
7f4e9b73 | 427 | |
8693472e CT |
428 | size_t mainHelloSize = features.helloMessage.length() - 5; |
429 | const char *mainHello = features.helloMessage.rawContent() + 5; | |
4cabb5e5 | 430 | assert((size_t)ssl->init_buf->max > mainHelloSize); |
7f4e9b73 CT |
431 | memcpy(ssl->init_buf->data, mainHello, mainHelloSize); |
432 | debugs(83, 5, "Hello Data init and adjustd sizes :" << ssl->init_num << " = "<< mainHelloSize); | |
433 | ssl->init_num = mainHelloSize; | |
434 | ssl->s3->wpend_ret = mainHelloSize; | |
435 | ssl->s3->wpend_tot = mainHelloSize; | |
a95989ed CT |
436 | return true; |
437 | #else | |
438 | return false; | |
439 | #endif | |
7f4e9b73 CT |
440 | } |
441 | ||
d620ae0e CT |
442 | int |
443 | Ssl::ServerBio::write(const char *buf, int size, BIO *table) | |
444 | { | |
445 | ||
446 | if (holdWrite_) { | |
7f4e9b73 | 447 | debugs(83, 7, "Hold write, for SSL connection on " << fd_ << "will not write bytes of size " << size); |
d620ae0e CT |
448 | BIO_set_retry_write(table); |
449 | return -1; | |
450 | } | |
451 | ||
5d65362c | 452 | if (!helloBuild && (bumpMode_ == Ssl::bumpPeek || bumpMode_ == Ssl::bumpStare)) { |
d620ae0e CT |
453 | if ( |
454 | buf[1] >= 3 //it is an SSL Version3 message | |
455 | && buf[0] == 0x16 // and it is a Handshake/Hello message | |
e1f72a8b | 456 | ) { |
d620ae0e CT |
457 | |
458 | //Hello message is the first message we write to server | |
8693472e | 459 | assert(helloMsg.isEmpty()); |
d620ae0e CT |
460 | |
461 | SSL *ssl = fd_table[fd_].ssl; | |
a95989ed | 462 | if (featuresSet && ssl) { |
5d65362c | 463 | if (bumpMode_ == Ssl::bumpPeek) { |
a95989ed | 464 | if (adjustSSL(ssl, clientFeatures)) |
5d65362c | 465 | allowBump = true; |
7f4e9b73 | 466 | allowSplice = true; |
8693472e | 467 | helloMsg.append(clientFeatures.helloMessage); |
35178e02 | 468 | debugs(83, 7, "SSL HELLO message for FD " << fd_ << ": Random number is adjusted for peek mode"); |
5d65362c CT |
469 | } else { /*Ssl::bumpStare*/ |
470 | allowBump = true; | |
a95989ed | 471 | if (adjustSSL(ssl, clientFeatures)) { |
5d65362c | 472 | allowSplice = true; |
8693472e | 473 | helloMsg.append(clientFeatures.helloMessage); |
35178e02 CT |
474 | debugs(83, 7, "SSL HELLO message for FD " << fd_ << ": Random number is adjusted for stare mode"); |
475 | } | |
7f4e9b73 | 476 | } |
d620ae0e CT |
477 | } |
478 | } | |
7f4e9b73 | 479 | // If we do not build any hello message, copy the current |
8693472e | 480 | if (helloMsg.isEmpty()) |
7f4e9b73 CT |
481 | helloMsg.append(buf, size); |
482 | ||
d620ae0e | 483 | helloBuild = true; |
8693472e | 484 | helloMsgSize = helloMsg.length(); |
5d65362c | 485 | //allowBump = true; |
7f4e9b73 CT |
486 | |
487 | if (allowSplice) { | |
e1f72a8b | 488 | // Do not write yet..... |
7f4e9b73 CT |
489 | BIO_set_retry_write(table); |
490 | return -1; | |
491 | } | |
d620ae0e CT |
492 | } |
493 | ||
8693472e | 494 | if (!helloMsg.isEmpty()) { |
d620ae0e | 495 | debugs(83, 7, "buffered write for FD " << fd_); |
8693472e | 496 | int ret = Ssl::Bio::write(helloMsg.rawContent(), helloMsg.length(), table); |
d620ae0e | 497 | helloMsg.consume(ret); |
8693472e | 498 | if (!helloMsg.isEmpty()) { |
d620ae0e CT |
499 | // We need to retry sendind data. |
500 | // Say to openSSL to retry sending hello message | |
501 | BIO_set_retry_write(table); | |
502 | return -1; | |
503 | } | |
504 | ||
505 | // Sending hello message complete. Do not send more data for now... | |
7f4e9b73 CT |
506 | holdWrite_ = true; |
507 | ||
a95989ed CT |
508 | // spoof openSSL that we write what it ask us to write |
509 | return size; | |
d620ae0e CT |
510 | } else |
511 | return Ssl::Bio::write(buf, size, table); | |
512 | } | |
513 | ||
514 | void | |
515 | Ssl::ServerBio::flush(BIO *table) | |
516 | { | |
8693472e CT |
517 | if (!helloMsg.isEmpty()) { |
518 | int ret = Ssl::Bio::write(helloMsg.rawContent(), helloMsg.length(), table); | |
d620ae0e CT |
519 | helloMsg.consume(ret); |
520 | } | |
521 | } | |
522 | ||
b3a8ae1b AR |
523 | /// initializes BIO table after allocation |
524 | static int | |
525 | squid_bio_create(BIO *bi) | |
526 | { | |
527 | bi->init = 0; // set when we store Bio object and socket fd (BIO_C_SET_FD) | |
528 | bi->num = 0; | |
529 | bi->ptr = NULL; | |
530 | bi->flags = 0; | |
531 | return 1; | |
532 | } | |
533 | ||
534 | /// cleans BIO table before deallocation | |
535 | static int | |
536 | squid_bio_destroy(BIO *table) | |
537 | { | |
538 | delete static_cast<Ssl::Bio*>(table->ptr); | |
539 | table->ptr = NULL; | |
540 | return 1; | |
541 | } | |
542 | ||
543 | /// wrapper for Bio::write() | |
544 | static int | |
545 | squid_bio_write(BIO *table, const char *buf, int size) | |
546 | { | |
547 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr); | |
548 | assert(bio); | |
549 | return bio->write(buf, size, table); | |
550 | } | |
551 | ||
552 | /// wrapper for Bio::read() | |
553 | static int | |
554 | squid_bio_read(BIO *table, char *buf, int size) | |
555 | { | |
556 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr); | |
557 | assert(bio); | |
558 | return bio->read(buf, size, table); | |
559 | } | |
560 | ||
561 | /// implements puts() via write() | |
562 | static int | |
563 | squid_bio_puts(BIO *table, const char *str) | |
564 | { | |
565 | assert(str); | |
566 | return squid_bio_write(table, str, strlen(str)); | |
567 | } | |
568 | ||
569 | /// other BIO manipulations (those without dedicated callbacks in BIO table) | |
570 | static long | |
571 | squid_bio_ctrl(BIO *table, int cmd, long arg1, void *arg2) | |
572 | { | |
573 | debugs(83, 5, table << ' ' << cmd << '(' << arg1 << ", " << arg2 << ')'); | |
574 | ||
575 | switch (cmd) { | |
576 | case BIO_C_SET_FD: { | |
577 | assert(arg2); | |
578 | const int fd = *static_cast<int*>(arg2); | |
d620ae0e CT |
579 | Ssl::Bio *bio; |
580 | if (arg1 == Ssl::Bio::BIO_TO_SERVER) | |
581 | bio = new Ssl::ServerBio(fd); | |
582 | else | |
583 | bio = new Ssl::ClientBio(fd); | |
b3a8ae1b AR |
584 | assert(!table->ptr); |
585 | table->ptr = bio; | |
586 | table->init = 1; | |
587 | return 0; | |
588 | } | |
589 | ||
590 | case BIO_C_GET_FD: | |
591 | if (table->init) { | |
592 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr); | |
593 | assert(bio); | |
594 | if (arg2) | |
595 | *static_cast<int*>(arg2) = bio->fd(); | |
596 | return bio->fd(); | |
597 | } | |
598 | return -1; | |
599 | ||
54f3b032 | 600 | case BIO_CTRL_DUP: |
e1f72a8b | 601 | // Should implemented if the SSL_dup openSSL API function |
54f3b032 CT |
602 | // used anywhere in squid. |
603 | return 0; | |
604 | ||
b3a8ae1b AR |
605 | case BIO_CTRL_FLUSH: |
606 | if (table->init) { | |
607 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr); | |
608 | assert(bio); | |
d620ae0e | 609 | bio->flush(table); |
b3a8ae1b AR |
610 | return 1; |
611 | } | |
612 | return 0; | |
613 | ||
f53969cc SM |
614 | /* we may also need to implement these: |
615 | case BIO_CTRL_RESET: | |
616 | case BIO_C_FILE_SEEK: | |
617 | case BIO_C_FILE_TELL: | |
618 | case BIO_CTRL_INFO: | |
619 | case BIO_CTRL_GET_CLOSE: | |
620 | case BIO_CTRL_SET_CLOSE: | |
621 | case BIO_CTRL_PENDING: | |
622 | case BIO_CTRL_WPENDING: | |
623 | */ | |
b3a8ae1b AR |
624 | default: |
625 | return 0; | |
626 | ||
627 | } | |
628 | ||
629 | return 0; /* NOTREACHED */ | |
630 | } | |
631 | ||
632 | /// wrapper for Bio::stateChanged() | |
633 | static void | |
634 | squid_ssl_info(const SSL *ssl, int where, int ret) | |
635 | { | |
636 | if (BIO *table = SSL_get_rbio(ssl)) { | |
637 | if (Ssl::Bio *bio = static_cast<Ssl::Bio*>(table->ptr)) | |
638 | bio->stateChanged(ssl, where, ret); | |
639 | } | |
640 | } | |
641 | ||
7f4e9b73 | 642 | Ssl::Bio::sslFeatures::sslFeatures(): sslVersion(-1), compressMethod(-1), unknownCiphers(false), doHeartBeats(true) |
d620ae0e CT |
643 | { |
644 | memset(client_random, 0, SSL3_RANDOM_SIZE); | |
645 | } | |
646 | ||
647 | int Ssl::Bio::sslFeatures::toSquidSSLVersion() const | |
648 | { | |
e1f72a8b | 649 | if (sslVersion == SSL2_VERSION) |
d620ae0e | 650 | return 2; |
e1f72a8b | 651 | else if (sslVersion == SSL3_VERSION) |
d620ae0e | 652 | return 3; |
e1f72a8b | 653 | else if (sslVersion == TLS1_VERSION) |
d620ae0e CT |
654 | return 4; |
655 | #if OPENSSL_VERSION_NUMBER >= 0x10001000L | |
e1f72a8b | 656 | else if (sslVersion == TLS1_1_VERSION) |
d620ae0e | 657 | return 5; |
e1f72a8b | 658 | else if (sslVersion == TLS1_2_VERSION) |
d620ae0e CT |
659 | return 6; |
660 | #endif | |
661 | else | |
662 | return 1; | |
663 | } | |
664 | ||
665 | bool | |
666 | Ssl::Bio::sslFeatures::get(const SSL *ssl) | |
667 | { | |
668 | sslVersion = SSL_version(ssl); | |
669 | debugs(83, 7, "SSL version: " << SSL_get_version(ssl) << " (" << sslVersion << ")"); | |
670 | ||
e1f72a8b CT |
671 | #if defined(TLSEXT_NAMETYPE_host_name) |
672 | if (const char *server = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name)) | |
d620ae0e CT |
673 | serverName = server; |
674 | debugs(83, 7, "SNI server name: " << serverName); | |
4cabb5e5 | 675 | #endif |
d620ae0e | 676 | |
a36e9cb2 | 677 | #if defined(OPENSSL_NO_COMP) |
d620ae0e | 678 | if (ssl->session->compress_meth) |
e1f72a8b CT |
679 | compressMethod = ssl->session->compress_meth; |
680 | else if (sslVersion >= 3) //if it is 3 or newer version then compression is disabled | |
a36e9cb2 | 681 | #endif |
d620ae0e CT |
682 | compressMethod = 0; |
683 | debugs(83, 7, "SSL compression: " << compressMethod); | |
684 | ||
685 | STACK_OF(SSL_CIPHER) * ciphers = NULL; | |
686 | if (ssl->server) | |
687 | ciphers = ssl->session->ciphers; | |
688 | else | |
689 | ciphers = ssl->cipher_list; | |
690 | if (ciphers) { | |
691 | for (int i = 0; i < sk_SSL_CIPHER_num(ciphers); ++i) { | |
692 | SSL_CIPHER *c = sk_SSL_CIPHER_value(ciphers, i); | |
693 | if (c != NULL) { | |
e1f72a8b | 694 | if (!clientRequestedCiphers.empty()) |
d620ae0e CT |
695 | clientRequestedCiphers.append(":"); |
696 | clientRequestedCiphers.append(c->name); | |
697 | } | |
698 | } | |
699 | } | |
700 | debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers); | |
701 | ||
702 | if (sslVersion >=3 && ssl->s3 && ssl->s3->client_random[0]) { | |
703 | memcpy(client_random, ssl->s3->client_random, SSL3_RANDOM_SIZE); | |
704 | } | |
705 | ||
706 | #if 0 /* XXX: OpenSSL 0.9.8k lacks at least some of these tlsext_* fields */ | |
707 | //The following extracted for logging purpuses: | |
708 | // TLSEXT_TYPE_ec_point_formats | |
709 | unsigned char *p; | |
710 | int len; | |
711 | if (ssl->server) { | |
712 | p = ssl->session->tlsext_ecpointformatlist; | |
713 | len = ssl->session->tlsext_ecpointformatlist_length; | |
714 | } else { | |
715 | p = ssl->tlsext_ecpointformatlist; | |
716 | len = ssl->tlsext_ecpointformatlist_length; | |
717 | } | |
718 | if (p) { | |
719 | ecPointFormatList = objToString(p, len); | |
720 | debugs(83, 7, "tlsExtension ecPointFormatList of length " << len << " :" << ecPointFormatList); | |
721 | } | |
722 | ||
723 | // TLSEXT_TYPE_elliptic_curves | |
724 | if (ssl->server) { | |
725 | p = ssl->session->tlsext_ellipticcurvelist; | |
726 | len = ssl->session->tlsext_ellipticcurvelist_length; | |
727 | } else { | |
728 | p = ssl->tlsext_ellipticcurvelist; | |
729 | len = ssl->tlsext_ellipticcurvelist_length; | |
730 | } | |
731 | if (p) { | |
732 | ellipticCurves = objToString(p, len); | |
733 | debugs(83, 7, "tlsExtension ellipticCurveList of length " << len <<" :" << ellipticCurves); | |
734 | } | |
735 | // TLSEXT_TYPE_opaque_prf_input | |
736 | p = NULL; | |
737 | if (ssl->server) { | |
738 | if (ssl->s3 && ssl->s3->client_opaque_prf_input) { | |
739 | p = (unsigned char *)ssl->s3->client_opaque_prf_input; | |
740 | len = ssl->s3->client_opaque_prf_input_len; | |
741 | } | |
742 | } else { | |
743 | p = (unsigned char *)ssl->tlsext_opaque_prf_input; | |
744 | len = ssl->tlsext_opaque_prf_input_len; | |
745 | } | |
746 | if (p) { | |
747 | debugs(83, 7, "tlsExtension client-opaque-prf-input of length " << len); | |
748 | opaquePrf = objToString(p, len); | |
749 | } | |
750 | #endif | |
751 | return true; | |
752 | } | |
753 | ||
754 | bool | |
755 | Ssl::Bio::sslFeatures::get(const unsigned char *hello) | |
756 | { | |
757 | // The SSL handshake message should starts with a 0x16 byte | |
758 | if (hello[0] == 0x16) { | |
759 | return parseV3Hello(hello); | |
8693472e | 760 | #if defined(DO_SSLV23) |
d620ae0e CT |
761 | } else if ((hello[0] & 0x80) && hello[2] == 0x01 && hello[3] == 0x03) { |
762 | return parseV23Hello(hello); | |
763 | #endif | |
764 | } | |
e1f72a8b | 765 | |
d620ae0e CT |
766 | debugs(83, 7, "Not a known SSL handshake message"); |
767 | return false; | |
768 | } | |
769 | ||
770 | bool | |
771 | Ssl::Bio::sslFeatures::parseV3Hello(const unsigned char *hello) | |
772 | { | |
773 | debugs(83, 7, "Get fake features from v3 hello message."); | |
774 | // The SSL version exist in the 2nd and 3rd bytes | |
775 | sslVersion = (hello[1] << 8) | hello[2]; | |
776 | debugs(83, 7, "Get fake features. Version :" << std::hex << std::setw(8) << std::setfill('0')<< sslVersion); | |
777 | ||
778 | // The following hello message size exist in 4th and 5th bytes | |
779 | int helloSize = (hello[3] << 8) | hello[4]; | |
780 | helloSize += 5; //Include the 5 header bytes. | |
8693472e | 781 | helloMessage.clear(); |
7f4e9b73 | 782 | helloMessage.append((const char *)hello, helloSize); |
d620ae0e CT |
783 | |
784 | //For SSLv3 or TLSv1.* protocols we can get some more informations | |
785 | if (hello[1] == 0x3 && hello[5] == 0x1 /*HELLO A message*/) { | |
786 | // Get the correct version of the sub-hello message | |
787 | sslVersion = (hello[9] << 8) | hello[10]; | |
788 | //Get Client Random number. It starts on the position 11 of hello message | |
789 | memcpy(client_random, hello + 11, SSL3_RANDOM_SIZE); | |
790 | debugs(83, 7, "Client random: " << objToString(client_random, SSL3_RANDOM_SIZE)); | |
791 | ||
792 | // At the position 43 (11+SSL3_RANDOM_SIZE) | |
793 | int sessIDLen = (int)hello[43]; | |
794 | debugs(83, 7, "Session ID Length: " << sessIDLen); | |
795 | ||
796 | //Ciphers list. It is stored after the Session ID. | |
797 | const unsigned char *ciphers = hello + 44 + sessIDLen; | |
798 | int ciphersLen = (ciphers[0] << 8) | ciphers[1]; | |
799 | ciphers += 2; | |
800 | if (ciphersLen) { | |
801 | const SSL_METHOD *method = SSLv3_method(); | |
802 | int cs = method->put_cipher_by_char(NULL, NULL); | |
803 | assert(cs > 0); | |
804 | for (int i = 0; i < ciphersLen; i += cs) { | |
805 | const SSL_CIPHER *c = method->get_cipher_by_char((ciphers + i)); | |
806 | if (c != NULL) { | |
e1f72a8b | 807 | if (!clientRequestedCiphers.empty()) |
d620ae0e CT |
808 | clientRequestedCiphers.append(":"); |
809 | clientRequestedCiphers.append(c->name); | |
7f4e9b73 CT |
810 | } else |
811 | unknownCiphers = true; | |
d620ae0e CT |
812 | } |
813 | } | |
814 | debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers); | |
815 | ||
816 | // Compression field: 1 bytes the number of compression methods and | |
817 | // 1 byte for each compression method | |
818 | const unsigned char *compression = ciphers + ciphersLen; | |
819 | if (compression[0] > 1) | |
820 | compressMethod = 1; | |
821 | else | |
822 | compressMethod = 0; | |
823 | debugs(83, 7, "SSL compression methods number: " << (int)compression[0]); | |
824 | ||
7f4e9b73 | 825 | const unsigned char *pToExtensions = compression + 1 + (int)compression[0]; |
e1f72a8b | 826 | if (pToExtensions < hello + helloSize) { |
7f4e9b73 CT |
827 | int extensionsLen = (pToExtensions[0] << 8) | pToExtensions[1]; |
828 | const unsigned char *ext = pToExtensions + 2; | |
e1f72a8b | 829 | while (ext < pToExtensions + extensionsLen) { |
d620ae0e CT |
830 | short extType = (ext[0] << 8) | ext[1]; |
831 | ext += 2; | |
832 | short extLen = (ext[0] << 8) | ext[1]; | |
833 | ext += 2; | |
834 | debugs(83, 7, "SSL Exntension: " << std::hex << extType << " of size:" << extLen); | |
835 | //The SNI extension has the type 0 (extType == 0) | |
836 | // The two first bytes indicates the length of the SNI data (should be extLen-2) | |
837 | // The next byte is the hostname type, it should be '0' for normal hostname (ext[2] == 0) | |
838 | // The 3rd and 4th bytes are the length of the hostname | |
839 | if (extType == 0 && ext[2] == 0) { | |
840 | int hostLen = (ext[3] << 8) | ext[4]; | |
841 | serverName.assign((const char *)(ext+5), hostLen); | |
842 | debugs(83, 7, "Found server name: " << serverName); | |
7f4e9b73 CT |
843 | } else if (extType == 15 && ext[0] != 0) { |
844 | // The heartBeats are the type 15 | |
845 | doHeartBeats = true; | |
846 | } else | |
847 | extensions.push_back(extType); | |
e1f72a8b | 848 | |
d620ae0e CT |
849 | ext += extLen; |
850 | } | |
851 | } | |
852 | } | |
853 | return true; | |
854 | } | |
855 | ||
856 | bool | |
857 | Ssl::Bio::sslFeatures::parseV23Hello(const unsigned char *hello) | |
858 | { | |
8693472e | 859 | #if defined(DO_SSLV23) |
d620ae0e CT |
860 | debugs(83, 7, "Get fake features from v23 hello message."); |
861 | sslVersion = (hello[3] << 8) | hello[4]; | |
862 | debugs(83, 7, "Get fake features. Version :" << std::hex << std::setw(8) << std::setfill('0')<< sslVersion); | |
863 | ||
864 | // The following hello message size exist in 2nd byte | |
865 | int helloSize = hello[1]; | |
866 | helloSize += 2; //Include the 2 header bytes. | |
8693472e | 867 | helloMessage.clear(); |
7f4e9b73 | 868 | helloMessage.append((char *)hello, helloSize); |
d620ae0e CT |
869 | |
870 | //Ciphers list. It is stored after the Session ID. | |
871 | ||
872 | int ciphersLen = (hello[5] << 8) | hello[6]; | |
873 | const unsigned char *ciphers = hello + 11; | |
874 | if (ciphersLen) { | |
875 | const SSL_METHOD *method = SSLv23_method(); | |
876 | int cs = method->put_cipher_by_char(NULL, NULL); | |
877 | assert(cs > 0); | |
878 | for (int i = 0; i < ciphersLen; i += cs) { | |
879 | // The v2 hello messages cipher has 3 bytes. | |
880 | // The v2 cipher has the first byte not null | |
e1f72a8b | 881 | // Because we are going to sent only v3 message we |
d620ae0e CT |
882 | // are ignoring these ciphers |
883 | if (ciphers[i] != 0) | |
884 | continue; | |
885 | const SSL_CIPHER *c = method->get_cipher_by_char((ciphers + i + 1)); | |
886 | if (c != NULL) { | |
e1f72a8b | 887 | if (!clientRequestedCiphers.empty()) |
d620ae0e CT |
888 | clientRequestedCiphers.append(":"); |
889 | clientRequestedCiphers.append(c->name); | |
890 | } | |
891 | } | |
892 | } | |
e1f72a8b | 893 | debugs(83, 7, "Ciphers requested by client: " << clientRequestedCiphers); |
d620ae0e CT |
894 | |
895 | //Get Client Random number. It starts on the position 11 of hello message | |
896 | memcpy(client_random, ciphers + ciphersLen, SSL3_RANDOM_SIZE); | |
897 | debugs(83, 7, "Client random: " << objToString(client_random, SSL3_RANDOM_SIZE)); | |
898 | ||
899 | compressMethod = 0; | |
900 | return true; | |
901 | #else | |
902 | return false; | |
903 | #endif | |
904 | } | |
905 | ||
a95989ed CT |
906 | void |
907 | Ssl::Bio::sslFeatures::applyToSSL(SSL *ssl) const | |
908 | { | |
909 | // To increase the possibility for bumping after peek mode selection or | |
e1f72a8b | 910 | // splicing after stare mode selection it is good to set the |
a95989ed CT |
911 | // SSL protocol version. |
912 | // The SSL_set_ssl_method is not the correct method because it will strict | |
913 | // SSL version which can be used to the SSL version used for client hello message. | |
e1f72a8b | 914 | // For example will prevent comunnicating with a tls1.0 server if the |
a95989ed CT |
915 | // client sent and tlsv1.2 Hello message. |
916 | //SSL_set_ssl_method(ssl, Ssl::method(features.toSquidSSLVersion())); | |
8693472e CT |
917 | #if defined(TLSEXT_NAMETYPE_host_name) |
918 | if (!serverName.isEmpty()) { | |
a95989ed | 919 | SSL_set_tlsext_host_name(ssl, serverName.c_str()); |
8693472e | 920 | } |
a95989ed CT |
921 | #endif |
922 | if (!clientRequestedCiphers.empty()) | |
923 | SSL_set_cipher_list(ssl, clientRequestedCiphers.c_str()); | |
8693472e | 924 | #if defined(SSL_OP_NO_COMPRESSION) /* XXX: OpenSSL 0.9.8k lacks SSL_OP_NO_COMPRESSION */ |
a95989ed CT |
925 | if (compressMethod == 0) |
926 | SSL_set_options(ssl, SSL_OP_NO_COMPRESSION); | |
927 | #endif | |
928 | ||
929 | } | |
930 | ||
d620ae0e CT |
931 | std::ostream & |
932 | Ssl::Bio::sslFeatures::print(std::ostream &os) const | |
933 | { | |
934 | static std::string buf; | |
e1f72a8b | 935 | return os << "v" << sslVersion << |
40f1e76d | 936 | " SNI:" << (serverName.isEmpty() ? SBuf("-") : serverName) << |
e1f72a8b CT |
937 | " comp:" << compressMethod << |
938 | " Ciphers:" << clientRequestedCiphers << | |
939 | " Random:" << objToString(client_random, SSL3_RANDOM_SIZE) << | |
940 | " ecPointFormats:" << ecPointFormatList << | |
941 | " ec:" << ellipticCurves << | |
942 | " opaquePrf:" << opaquePrf; | |
d620ae0e CT |
943 | } |
944 | ||
b3a8ae1b | 945 | #endif /* USE_SSL */ |
f53969cc | 946 |