]>
Commit | Line | Data |
---|---|---|
1 | /* | |
2 | * Copyright (C) 1996-2025 The Squid Software Foundation and contributors | |
3 | * | |
4 | * Squid software is distributed under GPLv2+ license and includes | |
5 | * contributions from numerous individuals and organizations. | |
6 | * Please see the COPYING and CONTRIBUTORS files for details. | |
7 | */ | |
8 | ||
9 | /* DEBUG: section 83 SSL accelerator support */ | |
10 | ||
11 | #include "squid.h" | |
12 | #include "base/IoManip.h" | |
13 | #include "ssl/support.h" | |
14 | ||
15 | /* support.cc says this is needed */ | |
16 | #if USE_OPENSSL | |
17 | ||
18 | #include "base/Raw.h" | |
19 | #include "comm.h" | |
20 | #include "fd.h" | |
21 | #include "fde.h" | |
22 | #include "globals.h" | |
23 | #include "ip/Address.h" | |
24 | #include "parser/BinaryTokenizer.h" | |
25 | #include "ssl/bio.h" | |
26 | ||
27 | #if _SQUID_WINDOWS_ | |
28 | extern int socket_read_method(int, char *, int); | |
29 | extern int socket_write_method(int, const char *, int); | |
30 | #endif | |
31 | ||
32 | /* BIO callbacks */ | |
33 | static int squid_bio_write(BIO *h, const char *buf, int num); | |
34 | static int squid_bio_read(BIO *h, char *buf, int size); | |
35 | static int squid_bio_puts(BIO *h, const char *str); | |
36 | //static int squid_bio_gets(BIO *h, char *str, int size); | |
37 | static long squid_bio_ctrl(BIO *h, int cmd, long arg1, void *arg2); | |
38 | static int squid_bio_create(BIO *h); | |
39 | static int squid_bio_destroy(BIO *data); | |
40 | /* SSL callbacks */ | |
41 | static void squid_ssl_info(const SSL *ssl, int where, int ret); | |
42 | ||
43 | #if HAVE_LIBCRYPTO_BIO_METH_NEW | |
44 | static BIO_METHOD *SquidMethods = nullptr; | |
45 | #else | |
46 | /// Initialization structure for the BIO table with | |
47 | /// Squid-specific methods and BIO method wrappers. | |
48 | static BIO_METHOD SquidMethods = { | |
49 | BIO_TYPE_SOCKET, | |
50 | "squid", | |
51 | squid_bio_write, | |
52 | squid_bio_read, | |
53 | squid_bio_puts, | |
54 | nullptr, // squid_bio_gets not supported | |
55 | squid_bio_ctrl, | |
56 | squid_bio_create, | |
57 | squid_bio_destroy, | |
58 | NULL // squid_callback_ctrl not supported | |
59 | }; | |
60 | #endif | |
61 | ||
62 | BIO * | |
63 | Ssl::Bio::Create(const int fd, Security::Io::Type type) | |
64 | { | |
65 | #if HAVE_LIBCRYPTO_BIO_METH_NEW | |
66 | if (!SquidMethods) { | |
67 | SquidMethods = BIO_meth_new(BIO_TYPE_SOCKET, "squid"); | |
68 | BIO_meth_set_write(SquidMethods, squid_bio_write); | |
69 | BIO_meth_set_read(SquidMethods, squid_bio_read); | |
70 | BIO_meth_set_puts(SquidMethods, squid_bio_puts); | |
71 | BIO_meth_set_gets(SquidMethods, nullptr); | |
72 | BIO_meth_set_ctrl(SquidMethods, squid_bio_ctrl); | |
73 | BIO_meth_set_create(SquidMethods, squid_bio_create); | |
74 | BIO_meth_set_destroy(SquidMethods, squid_bio_destroy); | |
75 | } | |
76 | BIO_METHOD *useMethod = SquidMethods; | |
77 | #else | |
78 | BIO_METHOD *useMethod = &SquidMethods; | |
79 | #endif | |
80 | ||
81 | if (BIO *bio = BIO_new(useMethod)) { | |
82 | BIO_int_ctrl(bio, BIO_C_SET_FD, type, fd); | |
83 | return bio; | |
84 | } | |
85 | return nullptr; | |
86 | } | |
87 | ||
88 | void | |
89 | Ssl::Bio::Link(SSL *ssl, BIO *bio) | |
90 | { | |
91 | SSL_set_bio(ssl, bio, bio); // cannot fail | |
92 | SSL_set_info_callback(ssl, &squid_ssl_info); // does not provide diagnostic | |
93 | } | |
94 | ||
95 | Ssl::Bio::Bio(const int anFd): fd_(anFd) | |
96 | { | |
97 | debugs(83, 7, "Bio constructed, this=" << this << " FD " << fd_); | |
98 | } | |
99 | ||
100 | Ssl::Bio::~Bio() | |
101 | { | |
102 | debugs(83, 7, "Bio destructing, this=" << this << " FD " << fd_); | |
103 | } | |
104 | ||
105 | int Ssl::Bio::write(const char *buf, int size, BIO *table) | |
106 | { | |
107 | errno = 0; | |
108 | #if _SQUID_WINDOWS_ | |
109 | const int result = socket_write_method(fd_, buf, size); | |
110 | #else | |
111 | const int result = default_write_method(fd_, buf, size); | |
112 | #endif | |
113 | const int xerrno = errno; | |
114 | debugs(83, 5, "FD " << fd_ << " wrote " << result << " <= " << size); | |
115 | ||
116 | BIO_clear_retry_flags(table); | |
117 | if (result < 0) { | |
118 | const bool ignoreError = ignoreErrno(xerrno) != 0; | |
119 | debugs(83, 5, "error: " << xerrno << " ignored: " << ignoreError); | |
120 | if (ignoreError) | |
121 | BIO_set_retry_write(table); | |
122 | } | |
123 | ||
124 | return result; | |
125 | } | |
126 | ||
127 | int | |
128 | Ssl::Bio::read(char *buf, int size, BIO *table) | |
129 | { | |
130 | errno = 0; | |
131 | #if _SQUID_WINDOWS_ | |
132 | const int result = socket_read_method(fd_, buf, size); | |
133 | #else | |
134 | const int result = default_read_method(fd_, buf, size); | |
135 | #endif | |
136 | const int xerrno = errno; | |
137 | debugs(83, 5, "FD " << fd_ << " read " << result << " <= " << size); | |
138 | ||
139 | BIO_clear_retry_flags(table); | |
140 | if (result < 0) { | |
141 | const bool ignoreError = ignoreErrno(xerrno) != 0; | |
142 | debugs(83, 5, "error: " << xerrno << " ignored: " << ignoreError); | |
143 | if (ignoreError) | |
144 | BIO_set_retry_read(table); | |
145 | } | |
146 | ||
147 | return result; | |
148 | } | |
149 | ||
150 | /// Called whenever the SSL connection state changes, an alert appears, or an | |
151 | /// error occurs. See SSL_set_info_callback(). | |
152 | void | |
153 | Ssl::Bio::stateChanged(const SSL *ssl, int where, int) | |
154 | { | |
155 | // Here we can use (where & STATE) to check the current state. | |
156 | // Many STATE values are possible, including: SSL_CB_CONNECT_LOOP, | |
157 | // SSL_CB_ACCEPT_LOOP, SSL_CB_HANDSHAKE_START, and SSL_CB_HANDSHAKE_DONE. | |
158 | // For example: | |
159 | // if (where & SSL_CB_HANDSHAKE_START) | |
160 | // debugs(83, 9, "Trying to establish the SSL connection"); | |
161 | // else if (where & SSL_CB_HANDSHAKE_DONE) | |
162 | // debugs(83, 9, "SSL connection established"); | |
163 | ||
164 | debugs(83, 7, "FD " << fd_ << " now: 0x" << asHex(where) << ' ' << | |
165 | SSL_state_string(ssl) << " (" << SSL_state_string_long(ssl) << ")"); | |
166 | } | |
167 | ||
168 | Ssl::ClientBio::ClientBio(const int anFd): | |
169 | Bio(anFd), | |
170 | holdRead_(false), | |
171 | holdWrite_(false), | |
172 | abortReason(nullptr) | |
173 | { | |
174 | renegotiations.configure(10*1000); | |
175 | } | |
176 | ||
177 | void | |
178 | Ssl::ClientBio::stateChanged(const SSL *ssl, int where, int ret) | |
179 | { | |
180 | Ssl::Bio::stateChanged(ssl, where, ret); | |
181 | // detect client-initiated renegotiations DoS (CVE-2011-1473) | |
182 | if (where & SSL_CB_HANDSHAKE_START) { | |
183 | const int reneg = renegotiations.count(1); | |
184 | ||
185 | if (abortReason) | |
186 | return; // already decided and informed the admin | |
187 | ||
188 | if (reneg > RenegotiationsLimit) { | |
189 | abortReason = "renegotiate requests flood"; | |
190 | debugs(83, DBG_IMPORTANT, "Terminating TLS connection [from " << fd_table[fd_].ipaddr << "] due to " << abortReason << ". This connection received " << | |
191 | reneg << " renegotiate requests in the last " << | |
192 | RenegotiationsWindow << " seconds (and " << | |
193 | renegotiations.remembered() << " requests total)."); | |
194 | } | |
195 | } | |
196 | } | |
197 | ||
198 | int | |
199 | Ssl::ClientBio::write(const char *buf, int size, BIO *table) | |
200 | { | |
201 | if (abortReason) { | |
202 | debugs(83, 3, "BIO on FD " << fd_ << " is aborted"); | |
203 | BIO_clear_retry_flags(table); | |
204 | return -1; | |
205 | } | |
206 | ||
207 | if (holdWrite_) { | |
208 | BIO_set_retry_write(table); | |
209 | return 0; | |
210 | } | |
211 | ||
212 | return Ssl::Bio::write(buf, size, table); | |
213 | } | |
214 | ||
215 | int | |
216 | Ssl::ClientBio::read(char *buf, int size, BIO *table) | |
217 | { | |
218 | if (abortReason) { | |
219 | debugs(83, 3, "BIO on FD " << fd_ << " is aborted"); | |
220 | BIO_clear_retry_flags(table); | |
221 | return -1; | |
222 | } | |
223 | ||
224 | if (holdRead_) { | |
225 | debugs(83, 7, "Hold flag is set, retry latter. (Hold " << size << "bytes)"); | |
226 | BIO_set_retry_read(table); | |
227 | return -1; | |
228 | } | |
229 | ||
230 | if (!rbuf.isEmpty()) { | |
231 | int bytes = (size <= (int)rbuf.length() ? size : rbuf.length()); | |
232 | memcpy(buf, rbuf.rawContent(), bytes); | |
233 | rbuf.consume(bytes); | |
234 | return bytes; | |
235 | } else | |
236 | return Ssl::Bio::read(buf, size, table); | |
237 | ||
238 | return -1; | |
239 | } | |
240 | ||
241 | Ssl::ServerBio::ServerBio(const int anFd): | |
242 | Bio(anFd), | |
243 | helloMsgSize(0), | |
244 | helloBuild(false), | |
245 | allowSplice(false), | |
246 | allowBump(false), | |
247 | holdWrite_(false), | |
248 | record_(false), | |
249 | parsedHandshake(false), | |
250 | parseError(false), | |
251 | bumpMode_(bumpNone), | |
252 | rbufConsumePos(0), | |
253 | parser_(Security::HandshakeParser::fromServer) | |
254 | { | |
255 | } | |
256 | ||
257 | void | |
258 | Ssl::ServerBio::stateChanged(const SSL *ssl, int where, int ret) | |
259 | { | |
260 | Ssl::Bio::stateChanged(ssl, where, ret); | |
261 | } | |
262 | ||
263 | void | |
264 | Ssl::ServerBio::setClientFeatures(Security::TlsDetails::Pointer const &details, SBuf const &aHello) | |
265 | { | |
266 | clientTlsDetails = details; | |
267 | clientSentHello = aHello; | |
268 | }; | |
269 | ||
270 | int | |
271 | Ssl::ServerBio::read(char *buf, int size, BIO *table) | |
272 | { | |
273 | if (parsedHandshake) // done parsing TLS Hello | |
274 | return readAndGive(buf, size, table); | |
275 | else | |
276 | return readAndParse(buf, size, table); | |
277 | } | |
278 | ||
279 | /// Read and give everything to OpenSSL. | |
280 | int | |
281 | Ssl::ServerBio::readAndGive(char *buf, const int size, BIO *table) | |
282 | { | |
283 | // If we have unused buffered bytes, give those bytes to OpenSSL now, | |
284 | // before reading more. TODO: Read if we have buffered less than size? | |
285 | if (rbufConsumePos < rbuf.length()) | |
286 | return giveBuffered(buf, size); | |
287 | ||
288 | if (record_) { | |
289 | const int result = readAndBuffer(table); | |
290 | if (result <= 0) | |
291 | return result; | |
292 | return giveBuffered(buf, size); | |
293 | } | |
294 | ||
295 | return Ssl::Bio::read(buf, size, table); | |
296 | } | |
297 | ||
298 | /// Read and give everything to our parser. | |
299 | /// When/if parsing is finished (successfully or not), start giving to OpenSSL. | |
300 | int | |
301 | Ssl::ServerBio::readAndParse(char *buf, const int size, BIO *table) | |
302 | { | |
303 | const int result = readAndBuffer(table); | |
304 | if (result <= 0) | |
305 | return result; | |
306 | ||
307 | try { | |
308 | if (!parser_.parseHello(rbuf)) { | |
309 | // need more data to finish parsing | |
310 | BIO_set_retry_read(table); | |
311 | return -1; | |
312 | } | |
313 | parsedHandshake = true; // done parsing (successfully) | |
314 | } | |
315 | catch (const std::exception &ex) { | |
316 | debugs(83, 2, "parsing error on FD " << fd_ << ": " << ex.what()); | |
317 | parsedHandshake = true; // done parsing (due to an error) | |
318 | parseError = true; | |
319 | } | |
320 | ||
321 | return giveBuffered(buf, size); | |
322 | } | |
323 | ||
324 | /// Reads more data into the read buffer. Returns either the number of bytes | |
325 | /// read or, on errors (including "try again" errors), a negative number. | |
326 | int | |
327 | Ssl::ServerBio::readAndBuffer(BIO *table) | |
328 | { | |
329 | char *space = rbuf.rawAppendStart(SQUID_TCP_SO_RCVBUF); | |
330 | const int result = Ssl::Bio::read(space, SQUID_TCP_SO_RCVBUF, table); | |
331 | if (result <= 0) | |
332 | return result; | |
333 | ||
334 | rbuf.rawAppendFinish(space, result); | |
335 | return result; | |
336 | } | |
337 | ||
338 | /// give previously buffered bytes to OpenSSL | |
339 | /// returns the number of bytes given | |
340 | int | |
341 | Ssl::ServerBio::giveBuffered(char *buf, const int size) | |
342 | { | |
343 | if (rbuf.length() <= rbufConsumePos) | |
344 | return -1; // buffered nothing yet | |
345 | ||
346 | const int unsent = rbuf.length() - rbufConsumePos; | |
347 | const int bytes = (size <= unsent ? size : unsent); | |
348 | memcpy(buf, rbuf.rawContent() + rbufConsumePos, bytes); | |
349 | rbufConsumePos += bytes; | |
350 | debugs(83, 7, bytes << "<=" << size << " bytes to OpenSSL"); | |
351 | return bytes; | |
352 | } | |
353 | ||
354 | int | |
355 | Ssl::ServerBio::write(const char *buf, int size, BIO *table) | |
356 | { | |
357 | ||
358 | if (holdWrite_) { | |
359 | debugs(83, 7, "postpone writing " << size << " bytes to SSL FD " << fd_); | |
360 | BIO_set_retry_write(table); | |
361 | return -1; | |
362 | } | |
363 | ||
364 | if (!helloBuild && (bumpMode_ == Ssl::bumpPeek || bumpMode_ == Ssl::bumpStare)) { | |
365 | // We have not seen any bytes, so the buffer must start with an | |
366 | // OpenSSL-generated TLSPlaintext record containing, for example, a | |
367 | // ClientHello or an alert message. We check these assumptions before we | |
368 | // substitute that record/message with clientSentHello. | |
369 | // TODO: Move these checks to where we actually rely on them. | |
370 | debugs(83, 7, "to-server" << Raw("TLSPlaintext", buf, size).hex()); | |
371 | Must(size >= 2); // enough for version and content_type checks below | |
372 | Must(buf[1] >= 3); // record's version.major; determines buf[0] meaning | |
373 | Must(20 <= buf[0] && buf[0] <= 23); // valid TLSPlaintext.content_type | |
374 | ||
375 | //Hello message is the first message we write to server | |
376 | assert(helloMsg.isEmpty()); | |
377 | ||
378 | if (bumpMode_ == Ssl::bumpPeek) { | |
379 | // we should not be here if we failed to parse the client-sent ClientHello | |
380 | Must(!clientSentHello.isEmpty()); | |
381 | allowSplice = true; | |
382 | // Replace OpenSSL-generated ClientHello with client-sent one. | |
383 | helloMsg.append(clientSentHello); | |
384 | debugs(83, 7, "FD " << fd_ << ": Using client-sent ClientHello for peek mode"); | |
385 | } else { /*Ssl::bumpStare*/ | |
386 | allowBump = true; | |
387 | } | |
388 | ||
389 | // if we did not use the client-sent ClientHello, then use the OpenSSL-generated one | |
390 | if (helloMsg.isEmpty()) | |
391 | helloMsg.append(buf, size); | |
392 | ||
393 | helloBuild = true; | |
394 | helloMsgSize = helloMsg.length(); | |
395 | ||
396 | if (allowSplice) { | |
397 | // Do not write yet..... | |
398 | BIO_set_retry_write(table); | |
399 | return -1; | |
400 | } | |
401 | } | |
402 | ||
403 | if (!helloMsg.isEmpty()) { | |
404 | debugs(83, 7, "buffered write for FD " << fd_); | |
405 | int ret = Ssl::Bio::write(helloMsg.rawContent(), helloMsg.length(), table); | |
406 | helloMsg.consume(ret); | |
407 | if (!helloMsg.isEmpty()) { | |
408 | // We need to retry sendind data. | |
409 | // Say to openSSL to retry sending hello message | |
410 | BIO_set_retry_write(table); | |
411 | return -1; | |
412 | } | |
413 | ||
414 | // Sending hello message complete. Do not send more data for now... | |
415 | holdWrite_ = true; | |
416 | ||
417 | // spoof openSSL that we write what it ask us to write | |
418 | return size; | |
419 | } else | |
420 | return Ssl::Bio::write(buf, size, table); | |
421 | } | |
422 | ||
423 | void | |
424 | Ssl::ServerBio::flush(BIO *table) | |
425 | { | |
426 | if (!helloMsg.isEmpty()) { | |
427 | int ret = Ssl::Bio::write(helloMsg.rawContent(), helloMsg.length(), table); | |
428 | helloMsg.consume(ret); | |
429 | } | |
430 | } | |
431 | ||
432 | bool | |
433 | Ssl::ServerBio::resumingSession() | |
434 | { | |
435 | return parser_.resumingSession; | |
436 | } | |
437 | ||
438 | bool | |
439 | Ssl::ServerBio::encryptedCertificates() const | |
440 | { | |
441 | return parser_.details->tlsSupportedVersion && | |
442 | Security::Tls1p3orLater(parser_.details->tlsSupportedVersion); | |
443 | } | |
444 | ||
445 | /// initializes BIO table after allocation | |
446 | static int | |
447 | squid_bio_create(BIO *bi) | |
448 | { | |
449 | #if !HAVE_LIBCRYPTO_BIO_GET_INIT | |
450 | bi->init = 0; // set when we store Bio object and socket fd (BIO_C_SET_FD) | |
451 | bi->num = 0; | |
452 | bi->flags = 0; | |
453 | #else | |
454 | // No need to set more, openSSL initialize BIO memory to zero. | |
455 | #endif | |
456 | ||
457 | BIO_set_data(bi, nullptr); | |
458 | return 1; | |
459 | } | |
460 | ||
461 | /// cleans BIO table before deallocation | |
462 | static int | |
463 | squid_bio_destroy(BIO *table) | |
464 | { | |
465 | delete static_cast<Ssl::Bio*>(BIO_get_data(table)); | |
466 | BIO_set_data(table, nullptr); | |
467 | return 1; | |
468 | } | |
469 | ||
470 | /// wrapper for Bio::write() | |
471 | static int | |
472 | squid_bio_write(BIO *table, const char *buf, int size) | |
473 | { | |
474 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); | |
475 | assert(bio); | |
476 | return bio->write(buf, size, table); | |
477 | } | |
478 | ||
479 | /// wrapper for Bio::read() | |
480 | static int | |
481 | squid_bio_read(BIO *table, char *buf, int size) | |
482 | { | |
483 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); | |
484 | assert(bio); | |
485 | return bio->read(buf, size, table); | |
486 | } | |
487 | ||
488 | /// implements puts() via write() | |
489 | static int | |
490 | squid_bio_puts(BIO *table, const char *str) | |
491 | { | |
492 | assert(str); | |
493 | return squid_bio_write(table, str, strlen(str)); | |
494 | } | |
495 | ||
496 | /// other BIO manipulations (those without dedicated callbacks in BIO table) | |
497 | static long | |
498 | squid_bio_ctrl(BIO *table, int cmd, long arg1, void *arg2) | |
499 | { | |
500 | debugs(83, 5, table << ' ' << cmd << '(' << arg1 << ", " << arg2 << ')'); | |
501 | ||
502 | switch (cmd) { | |
503 | case BIO_C_SET_FD: { | |
504 | assert(arg2); | |
505 | const int fd = *static_cast<int*>(arg2); | |
506 | Ssl::Bio *bio; | |
507 | if (arg1 == Security::Io::BIO_TO_SERVER) | |
508 | bio = new Ssl::ServerBio(fd); | |
509 | else | |
510 | bio = new Ssl::ClientBio(fd); | |
511 | assert(!BIO_get_data(table)); | |
512 | BIO_set_data(table, bio); | |
513 | BIO_set_init(table, 1); | |
514 | return 0; | |
515 | } | |
516 | ||
517 | case BIO_C_GET_FD: | |
518 | if (BIO_get_init(table)) { | |
519 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); | |
520 | assert(bio); | |
521 | if (arg2) | |
522 | *static_cast<int*>(arg2) = bio->fd(); | |
523 | return bio->fd(); | |
524 | } | |
525 | return -1; | |
526 | ||
527 | case BIO_CTRL_DUP: | |
528 | // Should implemented if the SSL_dup openSSL API function | |
529 | // used anywhere in squid. | |
530 | return 0; | |
531 | ||
532 | case BIO_CTRL_FLUSH: | |
533 | if (BIO_get_init(table)) { | |
534 | Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table)); | |
535 | assert(bio); | |
536 | bio->flush(table); | |
537 | return 1; | |
538 | } | |
539 | return 0; | |
540 | ||
541 | /* we may also need to implement these: | |
542 | case BIO_CTRL_RESET: | |
543 | case BIO_C_FILE_SEEK: | |
544 | case BIO_C_FILE_TELL: | |
545 | case BIO_CTRL_INFO: | |
546 | case BIO_CTRL_GET_CLOSE: | |
547 | case BIO_CTRL_SET_CLOSE: | |
548 | case BIO_CTRL_PENDING: | |
549 | case BIO_CTRL_WPENDING: | |
550 | */ | |
551 | default: | |
552 | return 0; | |
553 | ||
554 | } | |
555 | ||
556 | return 0; /* NOTREACHED */ | |
557 | } | |
558 | ||
559 | /// wrapper for Bio::stateChanged() | |
560 | static void | |
561 | squid_ssl_info(const SSL *ssl, int where, int ret) | |
562 | { | |
563 | if (BIO *table = SSL_get_rbio(ssl)) { | |
564 | if (Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table))) | |
565 | bio->stateChanged(ssl, where, ret); | |
566 | } | |
567 | } | |
568 | ||
569 | void | |
570 | applyTlsDetailsToSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, Ssl::BumpMode bumpMode) | |
571 | { | |
572 | // To increase the possibility for bumping after peek mode selection or | |
573 | // splicing after stare mode selection it is good to set the | |
574 | // SSL protocol version. | |
575 | // The SSL_set_ssl_method is wrong here because it will restrict the | |
576 | // permitted transport version to be identical to the version used in the | |
577 | // ClientHello message. | |
578 | // For example will prevent comunnicating with a tls1.0 server if the | |
579 | // client sent and tlsv1.2 Hello message. | |
580 | #if defined(TLSEXT_NAMETYPE_host_name) | |
581 | if (!details->serverName.isEmpty()) { | |
582 | SSL_set_tlsext_host_name(ssl, details->serverName.c_str()); | |
583 | } | |
584 | #endif | |
585 | ||
586 | if (!details->ciphers.empty()) { | |
587 | SBuf strCiphers; | |
588 | for (auto cipherId: details->ciphers) { | |
589 | unsigned char cbytes[3]; | |
590 | cbytes[0] = (cipherId >> 8) & 0xFF; | |
591 | cbytes[1] = cipherId & 0xFF; | |
592 | cbytes[2] = 0; | |
593 | if (const auto c = SSL_CIPHER_find(ssl, cbytes)) { | |
594 | if (!strCiphers.isEmpty()) | |
595 | strCiphers.append(":"); | |
596 | strCiphers.append(SSL_CIPHER_get_name(c)); | |
597 | } | |
598 | } | |
599 | if (!strCiphers.isEmpty()) | |
600 | SSL_set_cipher_list(ssl, strCiphers.c_str()); | |
601 | } | |
602 | ||
603 | #if defined(SSL_OP_NO_COMPRESSION) /* XXX: OpenSSL 0.9.8k lacks SSL_OP_NO_COMPRESSION */ | |
604 | if (!details->compressionSupported) | |
605 | SSL_set_options(ssl, SSL_OP_NO_COMPRESSION); | |
606 | #endif | |
607 | ||
608 | #if defined(SSL_OP_NO_TLSv1_3) | |
609 | // avoid "inappropriate fallback" OpenSSL error messages | |
610 | if (details->tlsSupportedVersion && Security::Tls1p2orEarlier(details->tlsSupportedVersion)) | |
611 | SSL_set_options(ssl, SSL_OP_NO_TLSv1_3); | |
612 | #endif | |
613 | ||
614 | #if defined(TLSEXT_STATUSTYPE_ocsp) | |
615 | if (details->tlsStatusRequest) | |
616 | SSL_set_tlsext_status_type(ssl, TLSEXT_STATUSTYPE_ocsp); | |
617 | #endif | |
618 | ||
619 | #if defined(TLSEXT_TYPE_application_layer_protocol_negotiation) | |
620 | if (!details->tlsAppLayerProtoNeg.isEmpty()) { | |
621 | if (bumpMode == Ssl::bumpPeek) | |
622 | SSL_set_alpn_protos(ssl, (const unsigned char*)details->tlsAppLayerProtoNeg.rawContent(), details->tlsAppLayerProtoNeg.length()); | |
623 | else { | |
624 | static const unsigned char supported_protos[] = {8, 'h','t','t', 'p', '/', '1', '.', '1'}; | |
625 | SSL_set_alpn_protos(ssl, supported_protos, sizeof(supported_protos)); | |
626 | } | |
627 | } | |
628 | #endif | |
629 | } | |
630 | ||
631 | #endif // USE_OPENSSL | |
632 |