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 default:
283 {
284 }
285 }
286 }
287
288 void send_data(const uint8_t* data, size_t size)
289 {
290 // Writes as much of the data as possible. If the data cannot all
291 // be written now, we store the remainder. We
292 // will try to send pending writes again whenever write() is called.
293 do_handshake();
294
295 if (status == handshake)
296 {
297 pending_write.insert(pending_write.end(), data, data + size);
298 return;
299 }
300
301 if (!can_send())
302 {
303 return;
304 }
305
306 pending_write.insert(pending_write.end(), data, data + size);
307
308 flush();
309 }
310
311 private:
312 void send_buffered(const std::vector<uint8_t>& data)
313 {
314 pending_write.insert(pending_write.end(), data.begin(), data.end());
315 }
316
317 void flush()
318 {
319 do_handshake();
320
321 if (!can_send())
322 {
323 return;
324 }
325
326 while (!pending_write.empty())
327 {
328 auto r = write_some(pending_write);
329
330 if (r > 0)
331 {
332 pending_write.erase(pending_write.begin(), pending_write.begin() + r);
333 }
334 else if (r == 0)
335 {
336 break;
337 }
338 else
339 {
340 LOG_TRACE_FMT("TLS session {} error on flush: {}", session_id, -r);
341 stop(error);
342 break;
343 }
344 }
345 }
346
347 void do_handshake()
348 {
349 // This should be called when additional data is written to the
350 // input buffer, until the handshake is complete.
351 if (status != handshake)
352 {
353 return;
354 }
355
356 auto rc = ctx->handshake();
357
358 switch (rc)
359 {
360 case 0:
361 {
362 status = ready;
363 break;
364 }
365
368 break;
369
371 {
372 on_handshake_error(fmt::format(
373 "TLS {} verify error on handshake: {}",
376 stop(authfail);
377 break;
378 }
379
381 {
383 "TLS {} closed on handshake: {}",
386 stop(closed);
387 break;
388 }
389
391 {
392 auto err = ctx->get_verify_error();
393 on_handshake_error(fmt::format(
394 "TLS {} invalid cert on handshake: {} [{}]",
396 err,
398 stop(authfail);
399 return;
400 }
401
402 default:
403 {
404 on_handshake_error(fmt::format(
405 "TLS {} error on handshake: {}",
408 stop(error);
409 break;
410 }
411 }
412 }
413
414 int write_some(const std::vector<uint8_t>& data)
415 {
416 auto r = ctx->write(data.data(), data.size());
417
418 switch (r)
419 {
422 return 0;
423
424 default:
425 return r;
426 }
427 }
428
429 void stop(SessionStatus status_)
430 {
431 switch (status)
432 {
433 case closed:
434 case authfail:
435 case error:
436 return;
437
438 default:
439 {
440 }
441 }
442
443 status = status_;
444
445 switch (status)
446 {
447 case closing:
448 case closed:
449 {
451 ::tcp::tcp_stop,
452 to_host,
454 std::string("Session closed"));
455 break;
456 }
457
458 case authfail:
459 {
461 ::tcp::tcp_stop,
462 to_host,
464 std::string("Authentication failed"));
465 }
466 case error:
467 {
469 ::tcp::tcp_stop, to_host, session_id, std::string("Error"));
470 break;
471 }
472
473 default:
474 {
475 }
476 }
477 }
478
479 int handle_send(const uint8_t* buf, size_t len)
480 {
481 // Either write all of the data or none of it.
482 auto wrote = RINGBUFFER_TRY_WRITE_MESSAGE(
483 ::tcp::tcp_outbound,
484 to_host,
486 serializer::ByteRange{buf, len});
487
488 if (!wrote)
489 {
490 return TLS_WRITING;
491 }
492
493 return static_cast<int>(len);
494 }
495
496 int handle_recv(uint8_t* buf, size_t len)
497 {
498 if (!pending_read.empty())
499 {
500 // Use the pending data vector. This is populated when the host
501 // writes a chunk larger than the size requested by the enclave.
502 size_t rd = std::min(len, pending_read.size());
503 ::memcpy(buf, pending_read.data(), rd);
504
505 if (rd >= pending_read.size())
506 {
507 pending_read.clear();
508 }
509 else
510 {
511 pending_read.erase(pending_read.begin(), pending_read.begin() + rd);
512 }
513
514 return (int)rd;
515 }
516
517 return TLS_READING;
518 }
519
520 static int send_callback(void* ctx, const unsigned char* buf, size_t len)
521 {
522 return reinterpret_cast<TLSSession*>(ctx)->handle_send(buf, len);
523 }
524
525 static int recv_callback(void* ctx, unsigned char* buf, size_t len)
526 {
527 return reinterpret_cast<TLSSession*>(ctx)->handle_recv(buf, len);
528 }
529
530 // These callbacks below are complex, using the callbacks above and
531 // manipulating OpenSSL's BIO objects accordingly. This is just so we can
532 // emulate what MbedTLS used to do.
533 // Now that we have removed it from the code, we can move the callbacks
534 // above to handle BIOs directly and hopefully remove the complexity below.
535 // This work will be carried out in #3429.
536 static long send_callback_openssl(
537 BIO* b,
538 int oper,
539 const char* argp,
540 size_t len,
541 int argi,
542 long argl,
543 int ret,
544 size_t* processed)
545 {
546 // Unused arguments
547 (void)argi;
548 (void)argl;
549 (void)argp;
550
551 if (ret != 0 && len > 0 && oper == (BIO_CB_WRITE | BIO_CB_RETURN))
552 {
553 // Flush BIO so the "pipe doesn't clog", but we don't use the
554 // data here, because 'argp' already has it.
555 BIO_flush(b);
556 size_t pending = BIO_pending(b);
557 if (pending != 0)
558 {
559 BIO_reset(b);
560 }
561
562 // Pipe object
563 void* ctx = BIO_get_callback_arg(b);
564 int put =
565 send_callback(ctx, reinterpret_cast<const uint8_t*>(argp), len);
566
567 // WANTS_WRITE
568 if (put == TLS_WRITING)
569 {
570 BIO_set_retry_write(b);
571 LOG_TRACE_FMT("TLS Session::send_cb() : WANTS_WRITE");
572 *processed = 0;
573 return -1;
574 }
575
576 LOG_TRACE_FMT("TLS Session::send_cb() : Put {} bytes", put);
577
578 // Update the number of bytes to external users
579 *processed = put;
580 }
581
582 // Unless we detected an error, the return value is always the same as the
583 // original operation.
584 return ret;
585 }
586
587 static long recv_callback_openssl(
588 BIO* b,
589 int oper,
590 const char* argp,
591 size_t len,
592 int argi,
593 long argl,
594 int ret,
595 size_t* processed)
596 {
597 // Unused arguments
598 (void)argi;
599 (void)argl;
600
601 if (ret == 1 && oper == (BIO_CB_CTRL | BIO_CB_RETURN))
602 {
603 // This callback may be fired at the end of large batches of TLS frames
604 // on OpenSSL 3.x. Note that processed == nullptr in this case, hence
605 // the early exit.
606 return 0;
607 }
608
609 if (ret != 0 && (oper == (BIO_CB_READ | BIO_CB_RETURN)))
610 {
611 // Pipe object
612 void* ctx = BIO_get_callback_arg(b);
613 // NOLINTNEXTLINE(cppcoreguidelines-pro-type-const-cast)
614 int got = recv_callback(
615 ctx, reinterpret_cast<uint8_t*>(const_cast<char*>(argp)), len);
616
617 // WANTS_READ
618 if (got == TLS_READING)
619 {
620 BIO_set_retry_read(b);
621 LOG_TRACE_FMT("TLS Session::recv_cb() : WANTS_READ");
622 *processed = 0;
623 return -1;
624 }
625
626 LOG_TRACE_FMT("TLS Session::recv_cb() : Got {} bytes of {}", got, len);
627
628 // If got less than requested, return WANT_READ
629 if ((size_t)got < len)
630 {
631 *processed = got;
632 return 1;
633 }
634
635 // Write to the actual BIO so SSL can use it
636 BIO_write_ex(b, argp, got, processed);
637
638 // The buffer should be enough, we can't return WANT_WRITE here
639 if ((size_t)got != *processed)
640 {
641 LOG_TRACE_FMT("TLS Session::recv_cb() : BIO error");
642 *processed = got;
643 return -1;
644 }
645
646 // If original return was -1 because it didn't find anything to read,
647 // return 1 to say we actually read something. This is common when the
648 // buffer is empty and needs an external read, so let's not log this.
649 if (got > 0 && ret < 0)
650 {
651 return 1;
652 }
653 }
654
655 // Unless we detected an error, the return value is always the same as the
656 // original operation.
657 return ret;
658 }
659 };
660}
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:288
::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:14
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