CCF
Loading...
Searching...
No Matches
tls_session.h
Go to the documentation of this file.
1// Copyright (c) Microsoft Corporation. All rights reserved.
2// Licensed under the Apache 2.0 License.
3#pragma once
4
6#include "ds/messaging.h"
7#include "ds/ring_buffer.h"
8#include "tcp/msg_types.h"
9#include "tls/context.h"
10#include "tls/tls.h"
11
12#include <exception>
13
14namespace ccf
15{
25
26 class TLSSession : public std::enable_shared_from_this<TLSSession>
27 {
28 public:
29 using HandshakeErrorCB = std::function<void(std::string&&)>;
30
31 protected:
34
35 private:
36 std::vector<uint8_t> pending_write;
37 std::vector<uint8_t> pending_read;
38 // Decrypted data
39 std::vector<uint8_t> read_buffer;
40
41 std::unique_ptr<tls::Context> ctx;
42 SessionStatus status = handshake;
43
44 HandshakeErrorCB handshake_error_cb;
45
46 bool can_send()
47 {
48 // Closing endpoint should still be able to respond to clients (e.g. to
49 // report errors)
50 return status == ready || status == closing;
51 }
52
53 bool can_recv()
54 {
55 return status == ready || status == handshake;
56 }
57
58 public:
60 int64_t session_id_,
61 ringbuffer::AbstractWriterFactory& writer_factory_,
62 std::unique_ptr<tls::Context> ctx_) :
63 to_host(writer_factory_.create_writer_to_outside()),
64 session_id(session_id_),
65 ctx(std::move(ctx_))
66 {
67 ctx->set_bio(this, send_callback_openssl, recv_callback_openssl);
68 }
69
70 virtual ~TLSSession()
71 {
72 RINGBUFFER_WRITE_MESSAGE(::tcp::tcp_closed, to_host, session_id);
73 }
74
76 {
77 return status;
78 }
79
80 void on_handshake_error(std::string&& error_msg)
81 {
82 if (handshake_error_cb)
83 {
84 handshake_error_cb(std::move(error_msg));
85 }
86 else
87 {
88 LOG_TRACE_FMT("{}", error_msg);
89 }
90 }
91
93 {
94 handshake_error_cb = std::move(cb);
95 }
96
97 std::string hostname()
98 {
99 if (status != ready)
100 {
101 return {};
102 }
103
104 return ctx->host();
105 }
106
107 std::vector<uint8_t> peer_cert()
108 {
109 return ctx->peer_cert();
110 }
111
112 // Returns count N of bytes read, which will be the first N bytes of data,
113 // up to a maximum of size. If exact is true, will only return either size
114 // or 0 (when size bytes are not currently available). data may be accessed
115 // beyond N during operation, up to size, but only the first N should be
116 // used by caller.
117 size_t read(uint8_t* data, size_t size, bool exact = false)
118 {
119 // This will return empty if the connection isn't
120 // ready, but it will not block on the handshake.
121 do_handshake();
122
123 if (status != ready)
124 {
125 LOG_TRACE_FMT("Not ready to read {} bytes", size);
126 return 0;
127 }
128
129 LOG_TRACE_FMT("Requesting up to {} bytes", size);
130
131 // Send pending writes.
132 flush();
133
134 size_t offset = 0;
135
136 if (!read_buffer.empty())
137 {
139 "Have existing read_buffer of size: {}", read_buffer.size());
140 offset = std::min(size, read_buffer.size());
141 ::memcpy(data, read_buffer.data(), offset);
142
143 if (offset < read_buffer.size())
144 {
145 read_buffer.erase(read_buffer.begin(), read_buffer.begin() + offset);
146 }
147 else
148 {
149 read_buffer.clear();
150 }
151
152 if (offset == size)
153 {
154 return size;
155 }
156
157 // NB: If we continue past here, read_buffer is empty
158 }
159
160 auto r = ctx->read(data + offset, size - offset);
161 LOG_TRACE_FMT("ctx->read returned: {}", r);
162
163 switch (r)
164 {
165 case 0:
167 {
169 "TLS {} close on read: {}", session_id, ::tls::error_string(r));
170
171 stop(closed);
172
173 if (!exact)
174 {
175 // Hit an error, but may still have some useful data from the
176 // previous read_buffer
177 return offset;
178 }
179
180 return 0;
181 }
182
185 {
186 if (!exact)
187 {
188 return offset;
189 }
190
191 // May have read something but not enough - copy it into read_buffer
192 // for next call
193 read_buffer.insert(read_buffer.end(), data, data + offset);
194 return 0;
195 }
196
197 default:
198 {
199 }
200 }
201
202 if (r < 0)
203 {
205 "TLS {} error on read: {}", session_id, ::tls::error_string(r));
206 stop(error);
207 return 0;
208 }
209
210 auto total = r + offset;
211
212 // We read _some_ data but not enough, and didn't get
213 // TLS_ERR_WANT_READ. Probably hit an internal size limit - try
214 // again
215 if (exact && (total < size))
216 {
218 "Asked for exactly {}, received {}, retrying", size, total);
219 read_buffer.insert(read_buffer.end(), data, data + total);
220 return read(data, size, exact);
221 }
222
223 return total;
224 }
225
226 void recv_buffered(const uint8_t* data, size_t size)
227 {
228 if (can_recv())
229 {
230 pending_read.insert(pending_read.end(), data, data + size);
231 }
232
233 do_handshake();
234 }
235
236 void close()
237 {
238 status = closing;
239
240 switch (status)
241 {
242 case handshake:
243 {
244 LOG_TRACE_FMT("TLS {} closed during handshake", session_id);
245 stop(closed);
246 break;
247 }
248
249 case ready:
250 case closing:
251 {
252 int r = ctx->close();
253
254 switch (r)
255 {
258 {
259 LOG_TRACE_FMT("TLS {} has pending data ({})", session_id, r);
260 // FALLTHROUGH
261 }
262 case 0:
263 {
264 LOG_TRACE_FMT("TLS {} closed ({})", session_id, r);
265 stop(closed);
266 break;
267 }
268
269 default:
270 {
272 "TLS {} error on_close: {}",
275 stop(error);
276 break;
277 }
278 }
279 break;
280 }
281
282 case closed:
283 case authfail:
284 case error:
285 {
286 break;
287 }
288 }
289 }
290
291 void send_data(const uint8_t* data, size_t size)
292 {
293 // Writes as much of the data as possible. If the data cannot all
294 // be written now, we store the remainder. We
295 // will try to send pending writes again whenever write() is called.
296 do_handshake();
297
298 if (status == handshake)
299 {
300 pending_write.insert(pending_write.end(), data, data + size);
301 return;
302 }
303
304 if (!can_send())
305 {
306 return;
307 }
308
309 pending_write.insert(pending_write.end(), data, data + size);
310
311 flush();
312 }
313
314 private:
315 void send_buffered(const std::vector<uint8_t>& data)
316 {
317 pending_write.insert(pending_write.end(), data.begin(), data.end());
318 }
319
320 void flush()
321 {
322 do_handshake();
323
324 if (!can_send())
325 {
326 return;
327 }
328
329 while (!pending_write.empty())
330 {
331 auto r = write_some(pending_write);
332
333 if (r > 0)
334 {
335 pending_write.erase(pending_write.begin(), pending_write.begin() + r);
336 }
337 else if (r == 0)
338 {
339 break;
340 }
341 else
342 {
343 LOG_TRACE_FMT("TLS session {} error on flush: {}", session_id, -r);
344 stop(error);
345 break;
346 }
347 }
348 }
349
350 void do_handshake()
351 {
352 // This should be called when additional data is written to the
353 // input buffer, until the handshake is complete.
354 if (status != handshake)
355 {
356 return;
357 }
358
359 auto rc = ctx->handshake();
360
361 switch (rc)
362 {
363 case 0:
364 {
365 status = ready;
366 break;
367 }
368
371 break;
372
374 {
375 on_handshake_error(fmt::format(
376 "TLS {} verify error on handshake: {}",
379 stop(authfail);
380 break;
381 }
382
384 {
386 "TLS {} closed on handshake: {}",
389 stop(closed);
390 break;
391 }
392
394 {
395 auto err = ctx->get_verify_error();
396 on_handshake_error(fmt::format(
397 "TLS {} invalid cert on handshake: {} [{}]",
399 err,
401 stop(authfail);
402 return;
403 }
404
405 default:
406 {
407 on_handshake_error(fmt::format(
408 "TLS {} error on handshake: {}",
411 stop(error);
412 break;
413 }
414 }
415 }
416
417 int write_some(const std::vector<uint8_t>& data)
418 {
419 auto r = ctx->write(data.data(), data.size());
420
421 switch (r)
422 {
425 return 0;
426
427 default:
428 return r;
429 }
430 }
431
432 void stop(SessionStatus status_)
433 {
434 switch (status)
435 {
436 case closed:
437 case authfail:
438 case error:
439 return;
440
441 case handshake:
442 case ready:
443 case closing:
444 {
445 break;
446 }
447 }
448
449 status = status_;
450
451 switch (status)
452 {
453 case handshake:
454 case ready:
455 {
456 break;
457 }
458 case closing:
459 case closed:
460 {
462 ::tcp::tcp_stop,
463 to_host,
465 std::string("Session closed"));
466 break;
467 }
468
469 case authfail:
470 {
472 ::tcp::tcp_stop,
473 to_host,
475 std::string("Authentication failed"));
476 }
477 case error:
478 {
480 ::tcp::tcp_stop, to_host, session_id, std::string("Error"));
481 break;
482 }
483
484 default:
485 throw std::logic_error(
486 fmt::format("TLS {} unknown status: {}", session_id, status));
487 }
488 }
489
490 int handle_send(const uint8_t* buf, size_t len)
491 {
492 // Either write all of the data or none of it.
493 auto wrote = RINGBUFFER_TRY_WRITE_MESSAGE(
494 ::tcp::tcp_outbound,
495 to_host,
497 serializer::ByteRange{buf, len});
498
499 if (!wrote)
500 {
501 return TLS_WRITING;
502 }
503
504 return static_cast<int>(len);
505 }
506
507 int handle_recv(uint8_t* buf, size_t len)
508 {
509 if (!pending_read.empty())
510 {
511 // Use the pending data vector. This is populated when the host
512 // writes a chunk larger than the size requested by the enclave.
513 size_t rd = std::min(len, pending_read.size());
514 ::memcpy(buf, pending_read.data(), rd);
515
516 if (rd >= pending_read.size())
517 {
518 pending_read.clear();
519 }
520 else
521 {
522 pending_read.erase(pending_read.begin(), pending_read.begin() + rd);
523 }
524
525 return (int)rd;
526 }
527
528 return TLS_READING;
529 }
530
531 static int send_callback(void* ctx, const unsigned char* buf, size_t len)
532 {
533 return reinterpret_cast<TLSSession*>(ctx)->handle_send(buf, len);
534 }
535
536 static int recv_callback(void* ctx, unsigned char* buf, size_t len)
537 {
538 return reinterpret_cast<TLSSession*>(ctx)->handle_recv(buf, len);
539 }
540
541 // These callbacks below are complex, using the callbacks above and
542 // manipulating OpenSSL's BIO objects accordingly. This is just so we can
543 // emulate what MbedTLS used to do.
544 // Now that we have removed it from the code, we can move the callbacks
545 // above to handle BIOs directly and hopefully remove the complexity below.
546 // This work will be carried out in #3429.
547 static long send_callback_openssl(
548 BIO* b,
549 int oper,
550 const char* argp,
551 size_t len,
552 int argi,
553 long argl,
554 int ret,
555 size_t* processed)
556 {
557 // Unused arguments
558 (void)argi;
559 (void)argl;
560 (void)argp;
561
562 if (ret != 0 && len > 0 && oper == (BIO_CB_WRITE | BIO_CB_RETURN))
563 {
564 // Flush BIO so the "pipe doesn't clog", but we don't use the
565 // data here, because 'argp' already has it.
566 BIO_flush(b);
567 size_t pending = BIO_pending(b);
568 if (pending != 0)
569 {
570 BIO_reset(b);
571 }
572
573 // Pipe object
574 void* ctx = BIO_get_callback_arg(b);
575 int put =
576 send_callback(ctx, reinterpret_cast<const uint8_t*>(argp), len);
577
578 // WANTS_WRITE
579 if (put == TLS_WRITING)
580 {
581 BIO_set_retry_write(b);
582 LOG_TRACE_FMT("TLS Session::send_cb() : WANTS_WRITE");
583 *processed = 0;
584 return -1;
585 }
586
587 LOG_TRACE_FMT("TLS Session::send_cb() : Put {} bytes", put);
588
589 // Update the number of bytes to external users
590 *processed = put;
591 }
592
593 // Unless we detected an error, the return value is always the same as the
594 // original operation.
595 return ret;
596 }
597
598 static long recv_callback_openssl(
599 BIO* b,
600 int oper,
601 const char* argp,
602 size_t len,
603 int argi,
604 long argl,
605 int ret,
606 size_t* processed)
607 {
608 // Unused arguments
609 (void)argi;
610 (void)argl;
611
612 if (ret == 1 && oper == (BIO_CB_CTRL | BIO_CB_RETURN))
613 {
614 // This callback may be fired at the end of large batches of TLS frames
615 // on OpenSSL 3.x. Note that processed == nullptr in this case, hence
616 // the early exit.
617 return 0;
618 }
619
620 if (ret != 0 && (oper == (BIO_CB_READ | BIO_CB_RETURN)))
621 {
622 // Pipe object
623 void* ctx = BIO_get_callback_arg(b);
624 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
625 int got = recv_callback(
626 ctx, reinterpret_cast<uint8_t*>(const_cast<char*>(argp)), len);
627
628 // WANTS_READ
629 if (got == TLS_READING)
630 {
631 BIO_set_retry_read(b);
632 LOG_TRACE_FMT("TLS Session::recv_cb() : WANTS_READ");
633 *processed = 0;
634 return -1;
635 }
636
637 LOG_TRACE_FMT("TLS Session::recv_cb() : Got {} bytes of {}", got, len);
638
639 // If got less than requested, return WANT_READ
640 if ((size_t)got < len)
641 {
642 *processed = got;
643 return 1;
644 }
645
646 // Write to the actual BIO so SSL can use it
647 BIO_write_ex(b, argp, got, processed);
648
649 // The buffer should be enough, we can't return WANT_WRITE here
650 if ((size_t)got != *processed)
651 {
652 LOG_TRACE_FMT("TLS Session::recv_cb() : BIO error");
653 *processed = got;
654 return -1;
655 }
656
657 // If original return was -1 because it didn't find anything to read,
658 // return 1 to say we actually read something. This is common when the
659 // buffer is empty and needs an external read, so let's not log this.
660 if (got > 0 && ret < 0)
661 {
662 return 1;
663 }
664 }
665
666 // Unless we detected an error, the return value is always the same as the
667 // original operation.
668 return ret;
669 }
670 };
671}
Definition tls_session.h:27
SessionStatus get_status() const
Definition tls_session.h:75
std::string hostname()
Definition tls_session.h:97
void recv_buffered(const uint8_t *data, size_t size)
Definition tls_session.h:226
std::function< void(std::string &&)> HandshakeErrorCB
Definition tls_session.h:29
size_t read(uint8_t *data, size_t size, bool exact=false)
Definition tls_session.h:117
TLSSession(int64_t session_id_, ringbuffer::AbstractWriterFactory &writer_factory_, std::unique_ptr< tls::Context > ctx_)
Definition tls_session.h:59
virtual ~TLSSession()
Definition tls_session.h:70
void on_handshake_error(std::string &&error_msg)
Definition tls_session.h:80
void close()
Definition tls_session.h:236
std::vector< uint8_t > peer_cert()
Definition tls_session.h:107
ringbuffer::WriterPtr to_host
Definition tls_session.h:32
void set_handshake_error_cb(HandshakeErrorCB &&cb)
Definition tls_session.h:92
void send_data(const uint8_t *data, size_t size)
Definition tls_session.h:291
::tcp::ConnID session_id
Definition tls_session.h:33
Definition ring_buffer_types.h:157
#define LOG_TRACE_FMT
Definition internal_logger.h:13
Definition app_interface.h:13
SessionStatus
Definition tls_session.h:17
@ closed
Definition tls_session.h:21
@ authfail
Definition tls_session.h:22
@ error
Definition tls_session.h:23
@ ready
Definition tls_session.h:19
@ closing
Definition tls_session.h:20
@ handshake
Definition tls_session.h:18
std::shared_ptr< AbstractWriter > WriterPtr
Definition ring_buffer_types.h:154
STL namespace.
int64_t ConnID
Definition msg_types.h:9
std::string error_string(int ec)
Definition tls.h:32
#define RINGBUFFER_TRY_WRITE_MESSAGE(MSG,...)
Definition ring_buffer_types.h:262
#define RINGBUFFER_WRITE_MESSAGE(MSG,...)
Definition ring_buffer_types.h:259
Definition serializer.h:27
#define TLS_ERR_X509_VERIFY
Definition tls.h:24
#define TLS_READING
Definition tls.h:14
#define TLS_ERR_WANT_WRITE
Definition tls.h:17
#define TLS_ERR_WANT_READ
Definition tls.h:16
#define TLS_WRITING
Definition tls.h:15
#define TLS_ERR_CONN_CLOSE_NOTIFY
Definition tls.h:18
#define TLS_ERR_NEED_CERT
Definition tls.h:19