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