blob: 94078a3e64475837af071c88d99a7f4f798c15ca [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
4 * Author: Adrien Béraud <adrien.beraud@savoirfairelinux.com>
5 * Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
6 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
7 * Author: Vsevolod Ivanov <vsevolod.ivanov@savoirfairelinux.com>
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 3 of the License, or
12 * (at your option) any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
22 */
23#include "tls_session.h"
24#include "threadloop.h"
25#include "certstore.h"
26
27#include <gnutls/gnutls.h>
28#include <gnutls/dtls.h>
29#include <gnutls/abstract.h>
30
31#include <gnutls/crypto.h>
32#include <gnutls/ocsp.h>
33#include <opendht/http.h>
34#include <opendht/logger.h>
35
36#include <list>
37#include <mutex>
38#include <condition_variable>
39#include <utility>
40#include <map>
41#include <atomic>
42#include <iterator>
43#include <stdexcept>
44#include <algorithm>
45#include <cstring> // std::memset
46
47#include <cstdlib>
48#include <unistd.h>
49
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040050namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040051namespace tls {
52
53static constexpr const char* DTLS_CERT_PRIORITY_STRING {
54 "SECURE192:-VERS-TLS-ALL:+VERS-DTLS-ALL:-RSA:%SERVER_PRECEDENCE:%SAFE_RENEGOTIATION"};
55static constexpr const char* DTLS_FULL_PRIORITY_STRING {
56 "SECURE192:-KX-ALL:+ANON-ECDH:+ANON-DH:+SECURE192:-VERS-TLS-ALL:+VERS-DTLS-ALL:-RSA:%SERVER_"
57 "PRECEDENCE:%SAFE_RENEGOTIATION"};
58// Note: -GROUP-FFDHE4096:-GROUP-FFDHE6144:-GROUP-FFDHE8192:+GROUP-X25519:
59// is added after gnutls 3.6.7, because some safety checks were introduced for FFDHE resulting in a
60// performance drop for our usage (2/3s of delay) This performance drop is visible on mobiles devices.
61
62// Benchmark result (on a computer)
63// $gnutls-cli --benchmark-tls-kx
64// (TLS1.3)-(DHE-FFDHE3072)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM) 20.48 transactions/sec
65// (avg. handshake time: 48.45 ms, sample variance: 0.68)
66// (TLS1.3)-(ECDHE-SECP256R1)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM) 208.14 transactions/sec
67// (avg. handshake time: 4.01 ms, sample variance: 0.01)
68// (TLS1.3)-(ECDHE-X25519)-(RSA-PSS-RSAE-SHA256)-(AES-128-GCM) 240.93 transactions/sec
69// (avg. handshake time: 4.00 ms, sample variance: 0.00)
70static constexpr const char* TLS_CERT_PRIORITY_STRING {
71 "SECURE192:-RSA:-GROUP-FFDHE4096:-GROUP-FFDHE6144:-GROUP-FFDHE8192:+GROUP-X25519:%SERVER_"
72 "PRECEDENCE:%SAFE_RENEGOTIATION"};
73static constexpr const char* TLS_FULL_PRIORITY_STRING {
74 "SECURE192:-KX-ALL:+ANON-ECDH:+ANON-DH:+SECURE192:-RSA:-GROUP-FFDHE4096:-GROUP-FFDHE6144:-"
75 "GROUP-FFDHE8192:+GROUP-X25519:%SERVER_PRECEDENCE:%SAFE_RENEGOTIATION"};
76static constexpr uint32_t RX_MAX_SIZE {64 * 1024}; // 64k = max size of a UDP packet
77static constexpr std::size_t INPUT_MAX_SIZE {
78 1000}; // Maximum number of packets to store before dropping (pkt size = DTLS_MTU)
79static constexpr ssize_t FLOOD_THRESHOLD {4 * 1024};
80static constexpr auto FLOOD_PAUSE = std::chrono::milliseconds(
81 100); // Time to wait after an invalid cookie packet (anti flood attack)
82static constexpr size_t HANDSHAKE_MAX_RETRY {64};
83static constexpr auto DTLS_RETRANSMIT_TIMEOUT = std::chrono::milliseconds(
84 1000); // Delay between two handshake request on DTLS
85static constexpr auto COOKIE_TIMEOUT = std::chrono::seconds(
86 10); // Time to wait for a cookie packet from client
87static constexpr int MIN_MTU {
88 512 - 20 - 8}; // minimal payload size of a DTLS packet carried by an IPv4 packet
89static constexpr uint8_t HEARTBEAT_TRIES = 1; // Number of tries at each heartbeat ping send
90static constexpr auto HEARTBEAT_RETRANS_TIMEOUT = std::chrono::milliseconds(
91 700); // gnutls heartbeat retransmission timeout for each ping (in milliseconds)
92static constexpr auto HEARTBEAT_TOTAL_TIMEOUT
93 = HEARTBEAT_RETRANS_TIMEOUT
94 * HEARTBEAT_TRIES; // gnutls heartbeat time limit for heartbeat procedure (in milliseconds)
95static constexpr int MISS_ORDERING_LIMIT
96 = 32; // maximal accepted distance of out-of-order packet (note: must be a signed type)
97static constexpr auto RX_OOO_TIMEOUT = std::chrono::milliseconds(1500);
98static constexpr int ASYMETRIC_TRANSPORT_MTU_OFFSET
99 = 20; // when client, if your local IP is IPV4 and server is IPV6; you must reduce your MTU to
100 // avoid packet too big error on server side. the offset is the difference in size of IP headers
101static constexpr auto OCSP_REQUEST_TIMEOUT = std::chrono::seconds(
102 2); // Time to wait for an ocsp-request
103
104// Helper to cast any duration into an integer number of milliseconds
105template<class Rep, class Period>
106static std::chrono::milliseconds::rep
107duration2ms(std::chrono::duration<Rep, Period> d)
108{
109 return std::chrono::duration_cast<std::chrono::milliseconds>(d).count();
110}
111
112static inline uint64_t
113array2uint(const std::array<uint8_t, 8>& a)
114{
115 uint64_t res = 0;
116 for (int i = 0; i < 8; ++i)
117 res = (res << 8) + a[i];
118 return res;
119}
120
121//==============================================================================
122
123namespace {
124
125class TlsCertificateCredendials
126{
127 using T = gnutls_certificate_credentials_t;
128
129public:
130 TlsCertificateCredendials()
131 {
132 int ret = gnutls_certificate_allocate_credentials(&creds_);
133 if (ret < 0) {
134 //if (params_.logger)
135 // params_.logger->e("gnutls_certificate_allocate_credentials() failed with ret=%d", ret);
136 throw std::bad_alloc();
137 }
138 }
139
140 ~TlsCertificateCredendials() { gnutls_certificate_free_credentials(creds_); }
141
142 operator T() { return creds_; }
143
144private:
145 TlsCertificateCredendials(const TlsCertificateCredendials&) = delete;
146 TlsCertificateCredendials& operator=(const TlsCertificateCredendials&) = delete;
147 T creds_;
148};
149
150class TlsAnonymousClientCredendials
151{
152 using T = gnutls_anon_client_credentials_t;
153
154public:
155 TlsAnonymousClientCredendials()
156 {
157 int ret = gnutls_anon_allocate_client_credentials(&creds_);
158 if (ret < 0) {
159 //if (params_.logger)
160 // params_.logger->e("gnutls_anon_allocate_client_credentials() failed with ret=%d", ret);
161 throw std::bad_alloc();
162 }
163 }
164
165 ~TlsAnonymousClientCredendials() { gnutls_anon_free_client_credentials(creds_); }
166
167 operator T() { return creds_; }
168
169private:
170 TlsAnonymousClientCredendials(const TlsAnonymousClientCredendials&) = delete;
171 TlsAnonymousClientCredendials& operator=(const TlsAnonymousClientCredendials&) = delete;
172 T creds_;
173};
174
175class TlsAnonymousServerCredendials
176{
177 using T = gnutls_anon_server_credentials_t;
178
179public:
180 TlsAnonymousServerCredendials()
181 {
182 int ret = gnutls_anon_allocate_server_credentials(&creds_);
183 if (ret < 0) {
184 //if (params_.logger)
185 // params_.logger->e("gnutls_anon_allocate_server_credentials() failed with ret=%d", ret);
186 throw std::bad_alloc();
187 }
188 }
189
190 ~TlsAnonymousServerCredendials() { gnutls_anon_free_server_credentials(creds_); }
191
192 operator T() { return creds_; }
193
194private:
195 TlsAnonymousServerCredendials(const TlsAnonymousServerCredendials&) = delete;
196 TlsAnonymousServerCredendials& operator=(const TlsAnonymousServerCredendials&) = delete;
197 T creds_;
198};
199
200} // namespace
201
202//==============================================================================
203
204class TlsSession::TlsSessionImpl
205{
206public:
207 using clock = std::chrono::steady_clock;
208 using StateHandler = std::function<TlsSessionState(TlsSessionState state)>;
209 using OcspVerification = std::function<void(const int status)>;
210 using HttpResponse = std::function<void(const dht::http::Response& response)>;
211
212 // Constants (ctor init.)
213 const bool isServer_;
214 const TlsParams params_;
215 const TlsSessionCallbacks callbacks_;
216 const bool anonymous_;
217
218 TlsSessionImpl(std::unique_ptr<SocketType>&& transport,
219 const TlsParams& params,
220 const TlsSessionCallbacks& cbs,
221 bool anonymous);
222
223 ~TlsSessionImpl();
224
225 const char* typeName() const;
226
227 std::unique_ptr<SocketType> transport_;
228
229 // State protectors
230 std::mutex stateMutex_;
231 std::condition_variable stateCondition_;
232
233 // State machine
234 TlsSessionState handleStateSetup(TlsSessionState state);
235 TlsSessionState handleStateCookie(TlsSessionState state);
236 TlsSessionState handleStateHandshake(TlsSessionState state);
237 TlsSessionState handleStateMtuDiscovery(TlsSessionState state);
238 TlsSessionState handleStateEstablished(TlsSessionState state);
239 TlsSessionState handleStateShutdown(TlsSessionState state);
240 std::map<TlsSessionState, StateHandler> fsmHandlers_ {};
241 std::atomic<TlsSessionState> state_ {TlsSessionState::SETUP};
242 std::atomic<TlsSessionState> newState_ {TlsSessionState::NONE};
243 std::atomic<int> maxPayload_ {-1};
244
245 // IO GnuTLS <-> ICE
246 std::mutex rxMutex_ {};
247 std::condition_variable rxCv_ {};
248 std::list<std::vector<ValueType>> rxQueue_ {};
249
250 bool flushProcessing_ {false}; ///< protect against recursive call to flushRxQueue
251 std::vector<ValueType> rawPktBuf_; ///< gnutls incoming packet buffer
252 uint64_t baseSeq_ {0}; ///< sequence number of first application data packet received
253 uint64_t lastRxSeq_ {0}; ///< last received and valid packet sequence number
254 uint64_t gapOffset_ {0}; ///< offset of first byte not received yet
255 clock::time_point lastReadTime_;
256 std::map<uint64_t, std::vector<ValueType>> reorderBuffer_ {};
257 std::list<clock::time_point> nextFlush_ {};
258
259 std::size_t send(const ValueType*, std::size_t, std::error_code&);
260 ssize_t sendRaw(const void*, size_t);
261 ssize_t sendRawVec(const giovec_t*, int);
262 ssize_t recvRaw(void*, size_t);
263 int waitForRawData(std::chrono::milliseconds);
264
265 bool initFromRecordState(int offset = 0);
266 void handleDataPacket(std::vector<ValueType>&&, uint64_t);
267 void flushRxQueue(std::unique_lock<std::mutex>&);
268
269 // Statistics
270 std::atomic<std::size_t> stRxRawPacketCnt_ {0};
271 std::atomic<std::size_t> stRxRawBytesCnt_ {0};
272 std::atomic<std::size_t> stRxRawPacketDropCnt_ {0};
273 std::atomic<std::size_t> stTxRawPacketCnt_ {0};
274 std::atomic<std::size_t> stTxRawBytesCnt_ {0};
275 void dump_io_stats() const;
276
277 std::unique_ptr<TlsAnonymousClientCredendials> cacred_; // ctor init.
278 std::unique_ptr<TlsAnonymousServerCredendials> sacred_; // ctor init.
279 std::unique_ptr<TlsCertificateCredendials> xcred_; // ctor init.
280 std::mutex sessionReadMutex_;
281 std::mutex sessionWriteMutex_;
282 gnutls_session_t session_ {nullptr};
283 gnutls_datum_t cookie_key_ {nullptr, 0};
284 gnutls_dtls_prestate_st prestate_ {};
285 ssize_t cookie_count_ {0};
286
287 TlsSessionState setupClient();
288 TlsSessionState setupServer();
289 void initAnonymous();
290 void initCredentials();
291 bool commonSessionInit();
292
293 std::shared_ptr<dht::crypto::Certificate> peerCertificate(gnutls_session_t session) const;
294
295 /*
296 * Implicit certificate validations.
297 */
298 int verifyCertificateWrapper(gnutls_session_t session);
299 /*
300 * Verify OCSP (Online Certificate Service Protocol):
301 */
302 void verifyOcsp(const std::string& url,
303 dht::crypto::Certificate& cert,
304 gnutls_x509_crt_t issuer,
305 OcspVerification cb);
306 /*
307 * Send OCSP Request to the specified URI.
308 */
309 void sendOcspRequest(const std::string& uri,
310 std::string body,
311 std::chrono::seconds timeout,
312 HttpResponse cb = {});
313
314 // FSM thread (TLS states)
315 ThreadLoop thread_; // ctor init.
316 bool setup();
317 void process();
318 void cleanup();
319
320 // Path mtu discovery
321 std::array<int, 3> MTUS_;
322 int mtuProbe_;
323 int hbPingRecved_ {0};
324 bool pmtudOver_ {false};
325 void pathMtuHeartbeat();
326
327 std::mutex requestsMtx_;
328 std::set<std::shared_ptr<dht::http::Request>> requests_;
329 std::shared_ptr<dht::crypto::Certificate> pCert_ {};
330};
331
332TlsSession::TlsSessionImpl::TlsSessionImpl(std::unique_ptr<SocketType>&& transport,
333 const TlsParams& params,
334 const TlsSessionCallbacks& cbs,
335 bool anonymous)
336 : isServer_(not transport->isInitiator())
337 , params_(params)
338 , callbacks_(cbs)
339 , anonymous_(anonymous)
340 , transport_ {std::move(transport)}
341 , cacred_(nullptr)
342 , sacred_(nullptr)
343 , xcred_(nullptr)
344 , thread_(params.logger, [this] { return setup(); }, [this] { process(); }, [this] { cleanup(); })
345{
346 if (not transport_->isReliable()) {
347 transport_->setOnRecv([this](const ValueType* buf, size_t len) {
348 std::lock_guard<std::mutex> lk {rxMutex_};
349 if (rxQueue_.size() == INPUT_MAX_SIZE) {
350 rxQueue_.pop_front(); // drop oldest packet if input buffer is full
351 ++stRxRawPacketDropCnt_;
352 }
353 rxQueue_.emplace_back(buf, buf + len);
354 ++stRxRawPacketCnt_;
355 stRxRawBytesCnt_ += len;
356 rxCv_.notify_one();
357 return len;
358 });
359 }
360
361 // Run FSM into dedicated thread
362 thread_.start();
363}
364
365TlsSession::TlsSessionImpl::~TlsSessionImpl()
366{
367 state_ = TlsSessionState::SHUTDOWN;
368 stateCondition_.notify_all();
369 rxCv_.notify_all();
370 {
371 std::lock_guard<std::mutex> lock(requestsMtx_);
372 // requests_ store a shared_ptr, so we need to cancel requests
373 // to not be stuck in verifyCertificateWrapper
374 for (auto& request : requests_)
375 request->cancel();
376 requests_.clear();
377 }
378 thread_.join();
379 if (not transport_->isReliable())
380 transport_->setOnRecv(nullptr);
381}
382
383const char*
384TlsSession::TlsSessionImpl::typeName() const
385{
386 return isServer_ ? "server" : "client";
387}
388
389void
390TlsSession::TlsSessionImpl::dump_io_stats() const
391{
392 if (params_.logger)
393 params_.logger->debug("[TLS] RxRawPkt={:d} ({:d} bytes) - TxRawPkt={:d} ({:d} bytes)",
394 stRxRawPacketCnt_.load(),
395 stRxRawBytesCnt_.load(),
396 stTxRawPacketCnt_.load(),
397 stTxRawBytesCnt_.load());
398}
399
400TlsSessionState
401TlsSession::TlsSessionImpl::setupClient()
402{
403 int ret;
404
405 if (not transport_->isReliable()) {
406 ret = gnutls_init(&session_, GNUTLS_CLIENT | GNUTLS_DATAGRAM);
407 // uncoment to reactivate PMTUD
408 // if (params_.logger)
409 params_.logger->d("[TLS] set heartbeat reception for retrocompatibility check on server");
410 // gnutls_heartbeat_enable(session_,GNUTLS_HB_PEER_ALLOWED_TO_SEND);
411 } else {
412 ret = gnutls_init(&session_, GNUTLS_CLIENT);
413 }
414
415 if (ret != GNUTLS_E_SUCCESS) {
416 if (params_.logger)
417 params_.logger->e("[TLS] session init failed: %s", gnutls_strerror(ret));
418 return TlsSessionState::SHUTDOWN;
419 }
420
421 if (not commonSessionInit()) {
422 return TlsSessionState::SHUTDOWN;
423 }
424
425 return TlsSessionState::HANDSHAKE;
426}
427
428TlsSessionState
429TlsSession::TlsSessionImpl::setupServer()
430{
431 int ret;
432
433 if (not transport_->isReliable()) {
434 ret = gnutls_init(&session_, GNUTLS_SERVER | GNUTLS_DATAGRAM);
435
436 // uncoment to reactivate PMTUD
437 // if (params_.logger)
438 params_.logger->d("[TLS] set heartbeat reception");
439 // gnutls_heartbeat_enable(session_, GNUTLS_HB_PEER_ALLOWED_TO_SEND);
440
441 gnutls_dtls_prestate_set(session_, &prestate_);
442 } else {
443 ret = gnutls_init(&session_, GNUTLS_SERVER);
444 }
445
446 if (ret != GNUTLS_E_SUCCESS) {
447 if (params_.logger)
448 params_.logger->e("[TLS] session init failed: %s", gnutls_strerror(ret));
449 return TlsSessionState::SHUTDOWN;
450 }
451
452 gnutls_certificate_server_set_request(session_, GNUTLS_CERT_REQUIRE);
453
454 if (not commonSessionInit())
455 return TlsSessionState::SHUTDOWN;
456
457 return TlsSessionState::HANDSHAKE;
458}
459
460void
461TlsSession::TlsSessionImpl::initAnonymous()
462{
463 // credentials for handshaking and transmission
464 if (isServer_)
465 sacred_.reset(new TlsAnonymousServerCredendials());
466 else
467 cacred_.reset(new TlsAnonymousClientCredendials());
468
469 // Setup DH-params for anonymous authentification
470 if (isServer_) {
471 if (const auto& dh_params = params_.dh_params.get().get())
472 gnutls_anon_set_server_dh_params(*sacred_, dh_params);
473 else
474 if (params_.logger)
475 params_.logger->w("[TLS] DH params unavailable");
476 }
477}
478
479void
480TlsSession::TlsSessionImpl::initCredentials()
481{
482 int ret;
483
484 // credentials for handshaking and transmission
485 xcred_.reset(new TlsCertificateCredendials());
486
487 gnutls_certificate_set_verify_function(*xcred_, [](gnutls_session_t session) -> int {
488 auto this_ = reinterpret_cast<TlsSessionImpl*>(gnutls_session_get_ptr(session));
489 return this_->verifyCertificateWrapper(session);
490 });
491
492 // Load user-given CA list
493 if (not params_.ca_list.empty()) {
494 // Try PEM format first
495 ret = gnutls_certificate_set_x509_trust_file(*xcred_,
496 params_.ca_list.c_str(),
497 GNUTLS_X509_FMT_PEM);
498
499 // Then DER format
500 if (ret < 0)
501 ret = gnutls_certificate_set_x509_trust_file(*xcred_,
502 params_.ca_list.c_str(),
503 GNUTLS_X509_FMT_DER);
504 if (ret < 0)
505 throw std::runtime_error("can't load CA " + params_.ca_list + ": "
506 + std::string(gnutls_strerror(ret)));
507
508 if (params_.logger)
509 params_.logger->d("[TLS] CA list %s loadev", params_.ca_list.c_str());
510 }
511 if (params_.peer_ca) {
512 auto chain = params_.peer_ca->getChainWithRevocations();
513 auto ret = gnutls_certificate_set_x509_trust(*xcred_,
514 chain.first.data(),
515 chain.first.size());
516 if (not chain.second.empty())
517 gnutls_certificate_set_x509_crl(*xcred_, chain.second.data(), chain.second.size());
518 if (params_.logger)
519 params_.logger->debug("[TLS] Peer CA list {:d} ({:d} CRLs): {:d}",
520 chain.first.size(),
521 chain.second.size(),
522 ret);
523 }
524
525 // Load user-given identity (key and passwd)
526 if (params_.cert) {
527 std::vector<gnutls_x509_crt_t> certs;
528 certs.reserve(3);
529 auto crt = params_.cert;
530 while (crt) {
531 certs.emplace_back(crt->cert);
532 crt = crt->issuer;
533 }
534
535 ret = gnutls_certificate_set_x509_key(*xcred_,
536 certs.data(),
537 certs.size(),
538 params_.cert_key->x509_key);
539 if (ret < 0)
540 throw std::runtime_error("can't load certificate: " + std::string(gnutls_strerror(ret)));
541
542 if (params_.logger)
543 params_.logger->d("[TLS] User identity loaded");
544 }
545
546 // Setup DH-params (server only, may block on dh_params.get())
547 if (isServer_) {
548 if (const auto& dh_params = params_.dh_params.get().get())
549 gnutls_certificate_set_dh_params(*xcred_, dh_params);
550 else
551 if (params_.logger)
552 params_.logger->w("[TLS] DH params unavailable"); // YOMGUI: need to stop?
553 }
554}
555
556bool
557TlsSession::TlsSessionImpl::commonSessionInit()
558{
559 int ret;
560
561 if (anonymous_) {
562 // Force anonymous connection, see handleStateHandshake how we handle failures
563 ret = gnutls_priority_set_direct(session_,
564 transport_->isReliable() ? TLS_FULL_PRIORITY_STRING
565 : DTLS_FULL_PRIORITY_STRING,
566 nullptr);
567 if (ret != GNUTLS_E_SUCCESS) {
568 if (params_.logger)
569 params_.logger->e("[TLS] TLS priority set failed: %s", gnutls_strerror(ret));
570 return false;
571 }
572
573 // Add anonymous credentials
574 if (isServer_)
575 ret = gnutls_credentials_set(session_, GNUTLS_CRD_ANON, *sacred_);
576 else
577 ret = gnutls_credentials_set(session_, GNUTLS_CRD_ANON, *cacred_);
578
579 if (ret != GNUTLS_E_SUCCESS) {
580 if (params_.logger)
581 params_.logger->e("[TLS] anonymous credential set failed: %s", gnutls_strerror(ret));
582 return false;
583 }
584 } else {
585 // Use a classic non-encrypted CERTIFICATE exchange method (less anonymous)
586 ret = gnutls_priority_set_direct(session_,
587 transport_->isReliable() ? TLS_CERT_PRIORITY_STRING
588 : DTLS_CERT_PRIORITY_STRING,
589 nullptr);
590 if (ret != GNUTLS_E_SUCCESS) {
591 if (params_.logger)
592 params_.logger->e("[TLS] TLS priority set failed: %s", gnutls_strerror(ret));
593 return false;
594 }
595 }
596
597 // Add certificate credentials
598 ret = gnutls_credentials_set(session_, GNUTLS_CRD_CERTIFICATE, *xcred_);
599 if (ret != GNUTLS_E_SUCCESS) {
600 if (params_.logger)
601 params_.logger->e("[TLS] certificate credential set failed: %s", gnutls_strerror(ret));
602 return false;
603 }
604 gnutls_certificate_send_x509_rdn_sequence(session_, 0);
605
606 if (not transport_->isReliable()) {
607 // DTLS hanshake timeouts
608 auto re_tx_timeout = duration2ms(DTLS_RETRANSMIT_TIMEOUT);
609 gnutls_dtls_set_timeouts(session_,
610 re_tx_timeout,
611 std::max(duration2ms(params_.timeout), re_tx_timeout));
612
613 // gnutls DTLS mtu = maximum payload size given by transport
614 gnutls_dtls_set_mtu(session_, transport_->maxPayload());
615 }
616
617 // Stuff for transport callbacks
618 gnutls_session_set_ptr(session_, this);
619 gnutls_transport_set_ptr(session_, this);
620 gnutls_transport_set_vec_push_function(session_,
621 [](gnutls_transport_ptr_t t,
622 const giovec_t* iov,
623 int iovcnt) -> ssize_t {
624 auto this_ = reinterpret_cast<TlsSessionImpl*>(t);
625 return this_->sendRawVec(iov, iovcnt);
626 });
627 gnutls_transport_set_pull_function(session_,
628 [](gnutls_transport_ptr_t t, void* d, size_t s) -> ssize_t {
629 auto this_ = reinterpret_cast<TlsSessionImpl*>(t);
630 return this_->recvRaw(d, s);
631 });
632 gnutls_transport_set_pull_timeout_function(session_,
633 [](gnutls_transport_ptr_t t, unsigned ms) -> int {
634 auto this_ = reinterpret_cast<TlsSessionImpl*>(t);
635 return this_->waitForRawData(
636 std::chrono::milliseconds(ms));
637 });
638 // TODO -1 = default else set value
639 if (transport_->isReliable())
640 gnutls_handshake_set_timeout(session_, duration2ms(params_.timeout));
641 return true;
642}
643
644std::string
645getOcspUrl(gnutls_x509_crt_t cert)
646{
647 int ret;
648 gnutls_datum_t aia;
649 unsigned int seq = 0;
650 do {
651 // Extracts the Authority Information Access (AIA) extension, see RFC 5280 section 4.2.2.1
652 ret = gnutls_x509_crt_get_authority_info_access(cert, seq++, GNUTLS_IA_OCSP_URI, &aia, NULL);
653 } while (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
654 // could also try the issuer if we include ocsp uri into there
655 if (ret < 0) {
656 return {};
657 }
658 std::string url((const char*) aia.data, (size_t) aia.size);
659 gnutls_free(aia.data);
660 return url;
661}
662
663int
664TlsSession::TlsSessionImpl::verifyCertificateWrapper(gnutls_session_t session)
665{
666 // Perform user-set verification first to avoid flooding with ocsp-requests if peer is denied
667 int verified;
668 if (callbacks_.verifyCertificate) {
669 auto this_ = reinterpret_cast<TlsSessionImpl*>(gnutls_session_get_ptr(session));
670 verified = this_->callbacks_.verifyCertificate(session);
671 if (verified != GNUTLS_E_SUCCESS)
672 return verified;
673 } else {
674 verified = GNUTLS_E_SUCCESS;
675 }
676 /*
677 * Support only x509 format
678 */
679 if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509)
680 return GNUTLS_E_CERTIFICATE_ERROR;
681
682 pCert_ = peerCertificate(session);
683 if (!pCert_)
684 return GNUTLS_E_CERTIFICATE_ERROR;
685
686 std::string ocspUrl = getOcspUrl(pCert_->cert);
687 if (ocspUrl.empty()) {
688 // Skipping OCSP verification: AIA not found
689 return verified;
690 }
691
692 // OCSP (Online Certificate Service Protocol) {
693 std::promise<int> v;
694 std::future<int> f = v.get_future();
695
696 gnutls_x509_crt_t issuer_crt = pCert_->issuer ? pCert_->issuer->cert : nullptr;
697 verifyOcsp(ocspUrl, *pCert_, issuer_crt, [&](const int status) {
698 if (status == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
699 // OCSP URI is absent, don't fail the verification by overwritting the user-set one.
700 if (params_.logger)
701 params_.logger->w("Skipping OCSP verification %s: request failed", pCert_->getUID().c_str());
702 v.set_value(verified);
703 } else {
704 if (status != GNUTLS_E_SUCCESS) {
705 if (params_.logger)
706 params_.logger->e("OCSP verification failed for %s: %s (%i)",
707 pCert_->getUID().c_str(),
708 gnutls_strerror(status),
709 status);
710 }
711 v.set_value(status);
712 }
713 });
714 f.wait();
715
716 return f.get();
717}
718
719void
720TlsSession::TlsSessionImpl::verifyOcsp(const std::string& aia_uri,
721 dht::crypto::Certificate& cert,
722 gnutls_x509_crt_t issuer,
723 OcspVerification cb)
724{
725 if (params_.logger)
726 params_.logger->d("Certificate's AIA URI: %s", aia_uri.c_str());
727
728 // Generate OCSP request
729 std::pair<std::string, dht::Blob> ocsp_req;
730 try {
731 ocsp_req = cert.generateOcspRequest(issuer);
732 } catch (dht::crypto::CryptoException& e) {
733 if (params_.logger)
734 params_.logger->e("Failed to generate OCSP request: %s", e.what());
735 if (cb)
736 cb(GNUTLS_E_INVALID_REQUEST);
737 return;
738 }
739
740 sendOcspRequest(aia_uri,
741 std::move(ocsp_req.first),
742 OCSP_REQUEST_TIMEOUT,
743 [cb = std::move(cb), &cert, nonce = std::move(ocsp_req.second), this](
744 const dht::http::Response& r) {
745 // Prepare response data
746 // Verify response validity
747 if (r.status_code != 200) {
748 if (params_.logger)
749 params_.logger->w("HTTP OCSP Request Failed with code %i", r.status_code);
750 if (cb)
751 cb(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
752 return;
753 }
754 if (params_.logger)
755 params_.logger->d("HTTP OCSP Request done!");
756 gnutls_ocsp_cert_status_t verify = GNUTLS_OCSP_CERT_UNKNOWN;
757 try {
758 cert.ocspResponse = std::make_shared<dht::crypto::OcspResponse>(
759 (const uint8_t*) r.body.data(), r.body.size());
760 if (params_.logger)
761 params_.logger->d("%s", cert.ocspResponse->toString().c_str());
762 verify = cert.ocspResponse->verifyDirect(cert, nonce);
763 } catch (dht::crypto::CryptoException& e) {
764 if (params_.logger)
765 params_.logger->e("Failed to verify OCSP response: %s", e.what());
766 }
767 if (verify == GNUTLS_OCSP_CERT_UNKNOWN) {
768 // Soft-fail
769 if (cb)
770 cb(GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE);
771 return;
772 }
773 int status = GNUTLS_E_SUCCESS;
774 if (verify == GNUTLS_OCSP_CERT_GOOD) {
775 if (params_.logger)
776 params_.logger->d("OCSP verification success!");
777 } else {
778 status = GNUTLS_E_CERTIFICATE_ERROR;
779 if (params_.logger)
780 params_.logger->e("OCSP verification: certificate is revoked!");
781 }
782 // Save response into the certificate store
783 try {
784 params_.certStore.pinOcspResponse(cert);
785 } catch (std::exception& e) {
786 if (params_.logger)
787 params_.logger->error("{}", e.what());
788 }
789 if (cb)
790 cb(status);
791 });
792}
793
794void
795TlsSession::TlsSessionImpl::sendOcspRequest(const std::string& uri,
796 std::string body,
797 std::chrono::seconds timeout,
798 HttpResponse cb)
799{
800 using namespace dht;
801 auto request = std::make_shared<http::Request>(*params_.io_context,
802 uri); //, logger);
803 request->set_method(restinio::http_method_post());
804 request->set_header_field(restinio::http_field_t::user_agent, "Jami");
805 request->set_header_field(restinio::http_field_t::accept, "*/*");
806 request->set_header_field(restinio::http_field_t::content_type, "application/ocsp-request");
807 request->set_body(std::move(body));
808 request->set_connection_type(restinio::http_connection_header_t::close);
809 request->timeout(timeout, [request,l=params_.logger](const asio::error_code& ec) {
810 if (ec and ec != asio::error::operation_aborted)
811 if (l) l->error("HTTP OCSP Request timeout with error: {:s}", ec.message());
812 request->cancel();
813 });
814 request->add_on_state_change_callback([this, cb = std::move(cb)](const http::Request::State state,
815 const http::Response response) {
816 if (params_.logger)
817 params_.logger->d("HTTP OCSP Request state=%i status_code=%i",
818 (unsigned int) state,
819 response.status_code);
820 if (state != http::Request::State::DONE)
821 return;
822 if (cb)
823 cb(response);
824 if (auto request = response.request.lock()) {
825 std::lock_guard<std::mutex> lock(requestsMtx_);
826 requests_.erase(request);
827 }
828 });
829 {
830 std::lock_guard<std::mutex> lock(requestsMtx_);
831 requests_.emplace(request);
832 }
833 request->send();
834}
835
836std::shared_ptr<dht::crypto::Certificate>
837TlsSession::TlsSessionImpl::peerCertificate(gnutls_session_t session) const
838{
839 if (!session)
840 return {};
841 /*
842 * Get the peer's raw certificate (chain) as sent by the peer.
843 * The first certificate in the list is the peer's certificate, following the issuer's cert. etc.
844 */
845 unsigned int cert_list_size = 0;
846 auto cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
847
848 if (cert_list == nullptr)
849 return {};
850 std::vector<std::pair<uint8_t*, uint8_t*>> crt_data;
851 crt_data.reserve(cert_list_size);
852 for (unsigned i = 0; i < cert_list_size; i++)
853 crt_data.emplace_back(cert_list[i].data, cert_list[i].data + cert_list[i].size);
854 return std::make_shared<dht::crypto::Certificate>(crt_data);
855}
856
857std::size_t
858TlsSession::TlsSessionImpl::send(const ValueType* tx_data, std::size_t tx_size, std::error_code& ec)
859{
860 std::lock_guard<std::mutex> lk(sessionWriteMutex_);
861 if (state_ != TlsSessionState::ESTABLISHED) {
862 ec = std::error_code(GNUTLS_E_INVALID_SESSION, std::system_category());
863 return 0;
864 }
865
866 std::size_t total_written = 0;
867 std::size_t max_tx_sz;
868
869 if (transport_->isReliable())
870 max_tx_sz = tx_size;
871 else
872 max_tx_sz = gnutls_dtls_get_data_mtu(session_);
873
874 // Split incoming data into chunck suitable for the underlying transport
875 while (total_written < tx_size) {
876 auto chunck_sz = std::min(max_tx_sz, tx_size - total_written);
877 auto data_seq = tx_data + total_written;
878 ssize_t nwritten;
879 do {
880 nwritten = gnutls_record_send(session_, data_seq, chunck_sz);
881 } while ((nwritten == GNUTLS_E_INTERRUPTED and state_ != TlsSessionState::SHUTDOWN)
882 or nwritten == GNUTLS_E_AGAIN);
883 if (nwritten < 0) {
884 /* Normally we would have to retry record_send but our internal
885 * state has not changed, so we have to ask for more data first.
886 * We will just try again later, although this should never happen.
887 */
888 if (params_.logger)
889 params_.logger->error("[TLS] send failed (only {} bytes sent): {}", total_written, gnutls_strerror(nwritten));
890 ec = std::error_code(nwritten, std::system_category());
891 return 0;
892 }
893
894 total_written += nwritten;
895 }
896
897 ec.clear();
898 return total_written;
899}
900
901// Called by GNUTLS to send encrypted packet to low-level transport.
902// Should return a positive number indicating the bytes sent, and -1 on error.
903ssize_t
904TlsSession::TlsSessionImpl::sendRaw(const void* buf, size_t size)
905{
906 std::error_code ec;
907 unsigned retry_count = 0;
908 do {
909 auto n = transport_->write(reinterpret_cast<const ValueType*>(buf), size, ec);
910 if (!ec) {
911 // log only on success
912 ++stTxRawPacketCnt_;
913 stTxRawBytesCnt_ += n;
914 return n;
915 }
916
917 if (ec.value() == EAGAIN) {
918 if (params_.logger)
919 params_.logger->w("[TLS] EAGAIN from transport, retry#", ++retry_count);
920 std::this_thread::sleep_for(std::chrono::milliseconds(10));
921 if (retry_count == 100) {
922 if (params_.logger)
923 params_.logger->e("[TLS] excessive retry detected, aborting");
924 ec.assign(EIO, std::system_category());
925 }
926 }
927 } while (ec.value() == EAGAIN);
928
929 // Must be called to pass errno value to GnuTLS on Windows (cf. GnuTLS doc)
930 gnutls_transport_set_errno(session_, ec.value());
931 if (params_.logger)
932 params_.logger->e("[TLS] transport failure on tx: errno = {}", ec.value());
933 return -1;
934}
935
936// Called by GNUTLS to send encrypted packet to low-level transport.
937// Should return a positive number indicating the bytes sent, and -1 on error.
938ssize_t
939TlsSession::TlsSessionImpl::sendRawVec(const giovec_t* iov, int iovcnt)
940{
941 ssize_t sent = 0;
942 for (int i = 0; i < iovcnt; ++i) {
943 const giovec_t& dat = iov[i];
944 ssize_t ret = sendRaw(dat.iov_base, dat.iov_len);
945 if (ret < 0)
946 return -1;
947 sent += ret;
948 }
949 return sent;
950}
951
952// Called by GNUTLS to receive encrypted packet from low-level transport.
953// Should return 0 on connection termination,
954// a positive number indicating the number of bytes received,
955// and -1 on error.
956ssize_t
957TlsSession::TlsSessionImpl::recvRaw(void* buf, size_t size)
958{
959 if (transport_->isReliable()) {
960 std::error_code ec;
961 auto count = transport_->read(reinterpret_cast<ValueType*>(buf), size, ec);
962 if (!ec)
963 return count;
964 gnutls_transport_set_errno(session_, ec.value());
965 return -1;
966 }
967
968 std::lock_guard<std::mutex> lk {rxMutex_};
969 if (rxQueue_.empty()) {
970 gnutls_transport_set_errno(session_, EAGAIN);
971 return -1;
972 }
973
974 const auto& pkt = rxQueue_.front();
975 const std::size_t count = std::min(pkt.size(), size);
976 std::copy_n(pkt.begin(), count, reinterpret_cast<ValueType*>(buf));
977 rxQueue_.pop_front();
978 return count;
979}
980
981// Called by GNUTLS to wait for encrypted packet from low-level transport.
982// 'timeout' is in milliseconds.
983// Should return 0 on timeout, a positive number if data are available for read, or -1 on error.
984int
985TlsSession::TlsSessionImpl::waitForRawData(std::chrono::milliseconds timeout)
986{
987 if (transport_->isReliable()) {
988 std::error_code ec;
989 auto err = transport_->waitForData(timeout, ec);
990 if (err <= 0) {
991 // shutdown?
992 if (state_ == TlsSessionState::SHUTDOWN) {
993 gnutls_transport_set_errno(session_, EINTR);
994 return -1;
995 }
996 if (ec) {
997 gnutls_transport_set_errno(session_, ec.value());
998 return -1;
999 }
1000 return 0;
1001 }
1002 return 1;
1003 }
1004
1005 // non-reliable uses callback installed with setOnRecv()
1006 std::unique_lock<std::mutex> lk {rxMutex_};
1007 rxCv_.wait_for(lk, timeout, [this] {
1008 return !rxQueue_.empty() or state_ == TlsSessionState::SHUTDOWN;
1009 });
1010 if (state_ == TlsSessionState::SHUTDOWN) {
1011 gnutls_transport_set_errno(session_, EINTR);
1012 return -1;
1013 }
1014 if (rxQueue_.empty()) {
1015 if (params_.logger)
1016 params_.logger->error("[TLS] waitForRawData: timeout after {}", timeout);
1017 return 0;
1018 }
1019 return 1;
1020}
1021
1022bool
1023TlsSession::TlsSessionImpl::initFromRecordState(int offset)
1024{
1025 std::array<uint8_t, 8> seq;
1026 if (gnutls_record_get_state(session_, 1, nullptr, nullptr, nullptr, &seq[0])
1027 != GNUTLS_E_SUCCESS) {
1028 if (params_.logger)
1029 params_.logger->e("[TLS] Fatal-error Unable to read initial state");
1030 return false;
1031 }
1032
1033 baseSeq_ = array2uint(seq) + offset;
1034 gapOffset_ = baseSeq_;
1035 lastRxSeq_ = baseSeq_ - 1;
1036 if (params_.logger)
1037 params_.logger->debug("[TLS] Initial sequence number: {:d}", baseSeq_);
1038 return true;
1039}
1040
1041bool
1042TlsSession::TlsSessionImpl::setup()
1043{
1044 // Setup FSM
1045 fsmHandlers_[TlsSessionState::SETUP] = [this](TlsSessionState s) {
1046 return handleStateSetup(s);
1047 };
1048 fsmHandlers_[TlsSessionState::COOKIE] = [this](TlsSessionState s) {
1049 return handleStateCookie(s);
1050 };
1051 fsmHandlers_[TlsSessionState::HANDSHAKE] = [this](TlsSessionState s) {
1052 return handleStateHandshake(s);
1053 };
1054 fsmHandlers_[TlsSessionState::MTU_DISCOVERY] = [this](TlsSessionState s) {
1055 return handleStateMtuDiscovery(s);
1056 };
1057 fsmHandlers_[TlsSessionState::ESTABLISHED] = [this](TlsSessionState s) {
1058 return handleStateEstablished(s);
1059 };
1060 fsmHandlers_[TlsSessionState::SHUTDOWN] = [this](TlsSessionState s) {
1061 return handleStateShutdown(s);
1062 };
1063
1064 return true;
1065}
1066
1067void
1068TlsSession::TlsSessionImpl::cleanup()
1069{
1070 state_ = TlsSessionState::SHUTDOWN; // be sure to block any user operations
1071 stateCondition_.notify_all();
1072
1073 {
1074 std::lock_guard<std::mutex> lk1(sessionReadMutex_);
1075 std::lock_guard<std::mutex> lk2(sessionWriteMutex_);
1076 if (session_) {
1077 if (transport_->isReliable())
1078 gnutls_bye(session_, GNUTLS_SHUT_RDWR);
1079 else
1080 gnutls_bye(session_, GNUTLS_SHUT_WR); // not wait for a peer answer
1081 gnutls_deinit(session_);
1082 session_ = nullptr;
1083 }
1084 }
1085
1086 if (cookie_key_.data)
1087 gnutls_free(cookie_key_.data);
1088
1089 transport_->shutdown();
1090}
1091
1092TlsSessionState
1093TlsSession::TlsSessionImpl::handleStateSetup([[maybe_unused]] TlsSessionState state)
1094{
1095 if (params_.logger)
1096 params_.logger->d("[TLS] Start %s session", typeName());
1097
1098 try {
1099 if (anonymous_)
1100 initAnonymous();
1101 initCredentials();
1102 } catch (const std::exception& e) {
1103 if (params_.logger)
1104 params_.logger->e("[TLS] authentifications init failed: %s", e.what());
1105 return TlsSessionState::SHUTDOWN;
1106 }
1107
1108 if (not isServer_)
1109 return setupClient();
1110
1111 // Extra step for DTLS-like transports
1112 if (transport_ and not transport_->isReliable()) {
1113 gnutls_key_generate(&cookie_key_, GNUTLS_COOKIE_KEY_SIZE);
1114 return TlsSessionState::COOKIE;
1115 }
1116 return setupServer();
1117}
1118
1119TlsSessionState
1120TlsSession::TlsSessionImpl::handleStateCookie(TlsSessionState state)
1121{
1122 if (params_.logger)
1123 params_.logger->d("[TLS] SYN cookie");
1124
1125 std::size_t count;
1126 {
1127 // block until rx packet or shutdown
1128 std::unique_lock<std::mutex> lk {rxMutex_};
1129 if (!rxCv_.wait_for(lk, COOKIE_TIMEOUT, [this] {
1130 return !rxQueue_.empty() or state_ == TlsSessionState::SHUTDOWN;
1131 })) {
1132 if (params_.logger)
1133 params_.logger->e("[TLS] SYN cookie failed: timeout");
1134 return TlsSessionState::SHUTDOWN;
1135 }
1136 // Shutdown state?
1137 if (rxQueue_.empty())
1138 return TlsSessionState::SHUTDOWN;
1139 count = rxQueue_.front().size();
1140 }
1141
1142 // Total bytes rx during cookie checking (see flood protection below)
1143 cookie_count_ += count;
1144
1145 int ret;
1146
1147 // Peek and verify front packet
1148 {
1149 std::lock_guard<std::mutex> lk {rxMutex_};
1150 auto& pkt = rxQueue_.front();
1151 std::memset(&prestate_, 0, sizeof(prestate_));
1152 ret = gnutls_dtls_cookie_verify(&cookie_key_, nullptr, 0, pkt.data(), pkt.size(), &prestate_);
1153 }
1154
1155 if (ret < 0) {
1156 gnutls_dtls_cookie_send(&cookie_key_,
1157 nullptr,
1158 0,
1159 &prestate_,
1160 this,
1161 [](gnutls_transport_ptr_t t, const void* d, size_t s) -> ssize_t {
1162 auto this_ = reinterpret_cast<TlsSessionImpl*>(t);
1163 return this_->sendRaw(d, s);
1164 });
1165
1166 // Drop front packet
1167 {
1168 std::lock_guard<std::mutex> lk {rxMutex_};
1169 rxQueue_.pop_front();
1170 }
1171
1172 // Cookie may be sent on multiple network packets
1173 // So we retry until we get a valid cookie.
1174 // To protect against a flood attack we delay each retry after FLOOD_THRESHOLD rx bytes.
1175 if (cookie_count_ >= FLOOD_THRESHOLD) {
1176 if (params_.logger)
1177 params_.logger->warn("[TLS] flood threshold reach (retry in {})", FLOOD_PAUSE);
1178 dump_io_stats();
1179 std::this_thread::sleep_for(FLOOD_PAUSE); // flood attack protection
1180 }
1181 return state;
1182 }
1183
1184 if (params_.logger)
1185 params_.logger->d("[TLS] cookie ok");
1186
1187 return setupServer();
1188}
1189
1190TlsSessionState
1191TlsSession::TlsSessionImpl::handleStateHandshake(TlsSessionState state)
1192{
1193 int ret;
1194 size_t retry_count = 0;
1195 if (params_.logger)
1196 params_.logger->debug("[TLS] handshake");
1197 do {
1198 ret = gnutls_handshake(session_);
1199 } while ((ret == GNUTLS_E_INTERRUPTED or ret == GNUTLS_E_AGAIN)
1200 and ++retry_count < HANDSHAKE_MAX_RETRY
1201 and state_.load() != TlsSessionState::SHUTDOWN);
1202 if (retry_count > 0) {
1203 if (params_.logger)
1204 params_.logger->error("[TLS] handshake retried count: {}", retry_count);
1205 }
1206
1207 // Stop on fatal error
1208 if (gnutls_error_is_fatal(ret) || state_.load() == TlsSessionState::SHUTDOWN) {
1209 if (params_.logger)
1210 params_.logger->error("[TLS] handshake failed: {:s}", gnutls_strerror(ret));
1211 return TlsSessionState::SHUTDOWN;
1212 }
1213
1214 // Continue handshaking on non-fatal error
1215 if (ret != GNUTLS_E_SUCCESS) {
1216 // TODO: handle GNUTLS_E_LARGE_PACKET (MTU must be lowered)
1217 if (ret != GNUTLS_E_AGAIN)
1218 if (params_.logger)
1219 params_.logger->debug("[TLS] non-fatal handshake error: {:s}", gnutls_strerror(ret));
1220 return state;
1221 }
1222
1223 // Safe-Renegotiation status shall always be true to prevent MiM attack
1224 // Following https://www.gnutls.org/manual/html_node/Safe-renegotiation.html
1225 // "Unlike TLS 1.2, the server is not allowed to change identities"
1226 // So, we don't have to check the status if we are the client
1227 bool isTLS1_3 = gnutls_protocol_get_version(session_) == GNUTLS_TLS1_3;
1228 if (!isTLS1_3 || (isTLS1_3 && isServer_)) {
1229 if (!gnutls_safe_renegotiation_status(session_)) {
1230 if (params_.logger)
1231 params_.logger->error("[TLS] server identity changed! MiM attack?");
1232 return TlsSessionState::SHUTDOWN;
1233 }
1234 }
1235
1236 auto desc = gnutls_session_get_desc(session_);
1237 if (params_.logger)
1238 params_.logger->debug("[TLS] session established: {:s}", desc);
1239 gnutls_free(desc);
1240
1241 // Anonymous connection? rehandshake immediately with certificate authentification forced
1242 auto cred = gnutls_auth_get_type(session_);
1243 if (cred == GNUTLS_CRD_ANON) {
1244 if (params_.logger)
1245 params_.logger->debug("[TLS] renogotiate with certificate authentification");
1246
1247 // Re-setup TLS algorithms priority list with only certificate based cipher suites
1248 ret = gnutls_priority_set_direct(session_,
1249 transport_ and transport_->isReliable()
1250 ? TLS_CERT_PRIORITY_STRING
1251 : DTLS_CERT_PRIORITY_STRING,
1252 nullptr);
1253 if (ret != GNUTLS_E_SUCCESS) {
1254 if (params_.logger)
1255 params_.logger->error("[TLS] session TLS cert-only priority set failed: {:s}", gnutls_strerror(ret));
1256 return TlsSessionState::SHUTDOWN;
1257 }
1258
1259 // remove anon credentials and re-enable certificate ones
1260 gnutls_credentials_clear(session_);
1261 ret = gnutls_credentials_set(session_, GNUTLS_CRD_CERTIFICATE, *xcred_);
1262 if (ret != GNUTLS_E_SUCCESS) {
1263 if (params_.logger)
1264 params_.logger->error("[TLS] session credential set failed: {:s}", gnutls_strerror(ret));
1265 return TlsSessionState::SHUTDOWN;
1266 }
1267
1268 return state; // handshake
1269
1270 } else if (cred != GNUTLS_CRD_CERTIFICATE) {
1271 if (params_.logger)
Adrien Béraud6de3f882023-07-06 12:56:29 -04001272 params_.logger->error("[TLS] spurious session credential ({})", (int)cred);
Adrien Béraud612b55b2023-05-29 10:42:04 -04001273 return TlsSessionState::SHUTDOWN;
1274 }
1275
1276 // Aware about certificates updates
1277 if (callbacks_.onCertificatesUpdate) {
1278 unsigned int remote_count;
1279 auto local = gnutls_certificate_get_ours(session_);
1280 auto remote = gnutls_certificate_get_peers(session_, &remote_count);
1281 callbacks_.onCertificatesUpdate(local, remote, remote_count);
1282 }
1283
1284 return transport_ and transport_->isReliable() ? TlsSessionState::ESTABLISHED
1285 : TlsSessionState::MTU_DISCOVERY;
1286}
1287
1288TlsSessionState
1289TlsSession::TlsSessionImpl::handleStateMtuDiscovery([[maybe_unused]] TlsSessionState state)
1290{
1291 if (!transport_) {
1292 if (params_.logger)
1293 params_.logger->w("No transport available when discovering the MTU");
1294 return TlsSessionState::SHUTDOWN;
1295 }
1296 mtuProbe_ = transport_->maxPayload();
1297 assert(mtuProbe_ >= MIN_MTU);
1298 MTUS_ = {MIN_MTU, std::max((mtuProbe_ + MIN_MTU) / 2, MIN_MTU), mtuProbe_};
1299
1300 // retrocompatibility check
1301 if (gnutls_heartbeat_allowed(session_, GNUTLS_HB_LOCAL_ALLOWED_TO_SEND) == 1) {
1302 if (!isServer_) {
1303 pathMtuHeartbeat();
1304 if (state_ == TlsSessionState::SHUTDOWN) {
1305 if (params_.logger)
1306 params_.logger->e("[TLS] session destroyed while performing PMTUD, shuting down");
1307 return TlsSessionState::SHUTDOWN;
1308 }
1309 pmtudOver_ = true;
1310 }
1311 } else {
1312 if (params_.logger)
1313 params_.logger->e("[TLS] PEER HEARTBEAT DISABLED: using transport MTU value ", mtuProbe_);
1314 pmtudOver_ = true;
1315 }
1316
1317 gnutls_dtls_set_mtu(session_, mtuProbe_);
1318 maxPayload_ = gnutls_dtls_get_data_mtu(session_);
1319
1320 if (pmtudOver_) {
1321 if (params_.logger)
1322 params_.logger->d("[TLS] maxPayload: ", maxPayload_.load());
1323 if (!initFromRecordState())
1324 return TlsSessionState::SHUTDOWN;
1325 }
1326
1327 return TlsSessionState::ESTABLISHED;
1328}
1329
1330/*
1331 * Path MTU discovery heuristic
1332 * heuristic description:
1333 * The two members of the current tls connection will exchange dtls heartbeat messages
1334 * of increasing size until the heartbeat times out which will be considered as a packet
1335 * drop from the network due to the size of the packet. (one retry to test for a buffer issue)
1336 * when timeout happens or all the values have been tested, the mtu will be returned.
1337 * In case of unexpected error the first (and minimal) value of the mtu array
1338 */
1339void
1340TlsSession::TlsSessionImpl::pathMtuHeartbeat()
1341{
1342 if (params_.logger)
1343 params_.logger->debug("[TLS] PMTUD: starting probing with {} of retransmission timeout", HEARTBEAT_RETRANS_TIMEOUT);
1344
1345 gnutls_heartbeat_set_timeouts(session_,
1346 HEARTBEAT_RETRANS_TIMEOUT.count(),
1347 HEARTBEAT_TOTAL_TIMEOUT.count());
1348
1349 int errno_send = GNUTLS_E_SUCCESS;
1350 int mtuOffset = 0;
1351
1352 // when the remote (server) has a IPV6 interface selected by ICE, and local (client) has a IPV4
1353 // selected, the path MTU discovery triggers errors for packets too big on server side because
1354 // of different IP headers overhead. Hence we have to signal to the TLS session to reduce the
1355 // MTU on client size accordingly.
1356 if (transport_ and transport_->localAddr().isIpv4() and transport_->remoteAddr().isIpv6()) {
1357 mtuOffset = ASYMETRIC_TRANSPORT_MTU_OFFSET;
1358 if (params_.logger)
1359 params_.logger->w("[TLS] local/remote IP protocol version not alike, use an MTU offset of {} bytes to compensate", ASYMETRIC_TRANSPORT_MTU_OFFSET);
1360 }
1361
1362 mtuProbe_ = MTUS_[0];
1363
1364 for (auto mtu : MTUS_) {
1365 gnutls_dtls_set_mtu(session_, mtu);
1366 auto data_mtu = gnutls_dtls_get_data_mtu(session_);
1367 if (params_.logger)
1368 params_.logger->debug("[TLS] PMTUD: mtu {}, payload {}", mtu, data_mtu);
1369 auto bytesToSend = data_mtu - mtuOffset - 3; // want to know why -3? ask gnutls!
1370
1371 do {
1372 errno_send = gnutls_heartbeat_ping(session_,
1373 bytesToSend,
1374 HEARTBEAT_TRIES,
1375 GNUTLS_HEARTBEAT_WAIT);
1376 } while (errno_send == GNUTLS_E_AGAIN
1377 || (errno_send == GNUTLS_E_INTERRUPTED && state_ != TlsSessionState::SHUTDOWN));
1378
1379 if (errno_send != GNUTLS_E_SUCCESS) {
1380 if (params_.logger)
1381 params_.logger->debug("[TLS] PMTUD: mtu {} [FAILED]", mtu);
1382 break;
1383 }
1384
1385 mtuProbe_ = mtu;
1386 if (params_.logger)
1387 params_.logger->debug("[TLS] PMTUD: mtu {} [OK]", mtu);
1388 }
1389
1390 if (errno_send == GNUTLS_E_TIMEDOUT) { // timeout is considered as a packet loss, then the good
1391 // mtu is the precedent
1392 if (mtuProbe_ == MTUS_[0]) {
1393 if (params_.logger)
1394 params_.logger->warn("[TLS] PMTUD: no response on first ping, using minimal MTU value {}", mtuProbe_);
1395 } else {
1396 if (params_.logger)
1397 params_.logger->warn("[TLS] PMTUD: timed out, using last working mtu {}", mtuProbe_);
1398 }
1399 } else if (errno_send != GNUTLS_E_SUCCESS) {
1400 if (params_.logger)
1401 params_.logger->error("[TLS] PMTUD: failed with gnutls error '{}'", gnutls_strerror(errno_send));
1402 } else {
1403 if (params_.logger)
1404 params_.logger->debug("[TLS] PMTUD: reached maximal value");
1405 }
1406}
1407
1408void
1409TlsSession::TlsSessionImpl::handleDataPacket(std::vector<ValueType>&& buf, uint64_t pkt_seq)
1410{
1411 // Check for a valid seq. num. delta
1412 int64_t seq_delta = pkt_seq - lastRxSeq_;
1413 if (seq_delta > 0) {
1414 lastRxSeq_ = pkt_seq;
1415 } else {
1416 // too old?
1417 if (seq_delta <= -MISS_ORDERING_LIMIT) {
1418 if (params_.logger)
1419 params_.logger->warn("[TLS] drop old pkt: 0x{:x}", pkt_seq);
1420 return;
1421 }
1422
1423 // No duplicate check as DTLS prevents that for us (replay protection)
1424
1425 // accept Out-Of-Order pkt - will be reordered by queue flush operation
1426 if (params_.logger)
1427 params_.logger->warn("[TLS] OOO pkt: 0x{:x}", pkt_seq);
1428 }
1429
1430 std::unique_lock<std::mutex> lk {rxMutex_};
1431 auto now = clock::now();
1432 if (reorderBuffer_.empty())
1433 lastReadTime_ = now;
1434 reorderBuffer_.emplace(pkt_seq, std::move(buf));
1435 nextFlush_.emplace_back(now + RX_OOO_TIMEOUT);
1436 rxCv_.notify_one();
1437 // Try to flush right now as a new packet is available
1438 flushRxQueue(lk);
1439}
1440
1441///
1442/// Reorder and push received packet to upper layer
1443///
1444/// \note This method must be called continuously, faster than RX_OOO_TIMEOUT
1445///
1446void
1447TlsSession::TlsSessionImpl::flushRxQueue(std::unique_lock<std::mutex>& lk)
1448{
1449 // RAII bool swap
1450 class GuardedBoolSwap
1451 {
1452 public:
1453 explicit GuardedBoolSwap(bool& var)
1454 : var_ {var}
1455 {
1456 var_ = !var_;
1457 }
1458 ~GuardedBoolSwap() { var_ = !var_; }
1459
1460 private:
1461 bool& var_;
1462 };
1463
1464 if (reorderBuffer_.empty())
1465 return;
1466
1467 // Prevent re-entrant access as the callbacks_.onRxData() is called in unprotected region
1468 if (flushProcessing_)
1469 return;
1470
1471 GuardedBoolSwap swap_flush_processing {flushProcessing_};
1472
1473 auto now = clock::now();
1474
1475 auto item = std::begin(reorderBuffer_);
1476 auto next_offset = item->first;
1477
1478 // Wait for next continuous packet until timeout
1479 if ((now - lastReadTime_) >= RX_OOO_TIMEOUT) {
1480 // OOO packet timeout - consider waited packets as lost
1481 if (auto lost = next_offset - gapOffset_) {
1482 if (params_.logger)
1483 params_.logger->warn("[TLS] {:d} lost since 0x{:x}", lost, gapOffset_);
1484 } else if (params_.logger)
1485 params_.logger->warn("[TLS] slow flush");
1486 } else if (next_offset != gapOffset_)
1487 return;
1488
1489 // Loop on offset-ordered received packet until a discontinuity in sequence number
1490 while (item != std::end(reorderBuffer_) and item->first <= next_offset) {
1491 auto pkt_offset = item->first;
1492 auto pkt = std::move(item->second);
1493
1494 // Remove item before unlocking to not trash the item' relationship
1495 next_offset = pkt_offset + 1;
1496 item = reorderBuffer_.erase(item);
1497
1498 if (callbacks_.onRxData) {
1499 lk.unlock();
1500 callbacks_.onRxData(std::move(pkt));
1501 lk.lock();
1502 }
1503 }
1504
1505 gapOffset_ = std::max(gapOffset_, next_offset);
1506 lastReadTime_ = now;
1507}
1508
1509TlsSessionState
1510TlsSession::TlsSessionImpl::handleStateEstablished(TlsSessionState state)
1511{
1512 // Nothing to do in reliable mode, so just wait for state change
1513 if (transport_ and transport_->isReliable()) {
1514 auto disconnected = [this]() -> bool {
1515 return state_.load() != TlsSessionState::ESTABLISHED
1516 or newState_.load() != TlsSessionState::NONE;
1517 };
1518 std::unique_lock<std::mutex> lk(stateMutex_);
1519 stateCondition_.wait(lk, disconnected);
1520 auto oldState = state_.load();
1521 if (oldState == TlsSessionState::ESTABLISHED) {
1522 auto newState = newState_.load();
1523 if (newState != TlsSessionState::NONE) {
1524 newState_ = TlsSessionState::NONE;
1525 return newState;
1526 }
1527 }
1528 return oldState;
1529 }
1530
1531 // block until rx packet or state change
1532 {
1533 std::unique_lock<std::mutex> lk {rxMutex_};
1534 if (nextFlush_.empty())
1535 rxCv_.wait(lk, [this] {
1536 return state_ != TlsSessionState::ESTABLISHED or not rxQueue_.empty()
1537 or not nextFlush_.empty();
1538 });
1539 else
1540 rxCv_.wait_until(lk, nextFlush_.front(), [this] {
1541 return state_ != TlsSessionState::ESTABLISHED or !rxQueue_.empty();
1542 });
1543 state = state_.load();
1544 if (state != TlsSessionState::ESTABLISHED)
1545 return state;
1546
1547 if (not nextFlush_.empty()) {
1548 auto now = clock::now();
1549 if (nextFlush_.front() <= now) {
1550 while (nextFlush_.front() <= now)
1551 nextFlush_.pop_front();
1552 flushRxQueue(lk);
1553 return state;
1554 }
1555 }
1556 }
1557
1558 std::array<uint8_t, 8> seq;
1559 rawPktBuf_.resize(RX_MAX_SIZE);
1560 auto ret = gnutls_record_recv_seq(session_, rawPktBuf_.data(), rawPktBuf_.size(), &seq[0]);
1561
1562 if (ret > 0) {
1563 // Are we in PMTUD phase?
1564 if (!pmtudOver_) {
1565 mtuProbe_ = MTUS_[std::max(0, hbPingRecved_ - 1)];
1566 gnutls_dtls_set_mtu(session_, mtuProbe_);
1567 maxPayload_ = gnutls_dtls_get_data_mtu(session_);
1568 pmtudOver_ = true;
1569 if (params_.logger)
1570 params_.logger->debug("[TLS] maxPayload: {}", maxPayload_.load());
1571
1572 if (!initFromRecordState(-1))
1573 return TlsSessionState::SHUTDOWN;
1574 }
1575
1576 rawPktBuf_.resize(ret);
1577 handleDataPacket(std::move(rawPktBuf_), array2uint(seq));
1578 // no state change
1579 } else if (ret == GNUTLS_E_HEARTBEAT_PING_RECEIVED) {
1580 if (params_.logger)
1581 params_.logger->d("[TLS] PMTUD: ping received sending pong");
1582 auto errno_send = gnutls_heartbeat_pong(session_, 0);
1583
1584 if (errno_send != GNUTLS_E_SUCCESS) {
1585 if (params_.logger)
1586 params_.logger->e("[TLS] PMTUD: failed on pong with error %d: %s",
1587 errno_send,
1588 gnutls_strerror(errno_send));
1589 } else {
1590 ++hbPingRecved_;
1591 }
1592 // no state change
1593 } else if (ret == 0) {
1594 if (params_.logger)
1595 params_.logger->d("[TLS] eof");
1596 state = TlsSessionState::SHUTDOWN;
1597 } else if (ret == GNUTLS_E_REHANDSHAKE) {
1598 if (params_.logger)
1599 params_.logger->d("[TLS] re-handshake");
1600 state = TlsSessionState::HANDSHAKE;
1601 } else if (gnutls_error_is_fatal(ret)) {
1602 if (params_.logger)
1603 params_.logger->e("[TLS] fatal error in recv: %s", gnutls_strerror(ret));
1604 state = TlsSessionState::SHUTDOWN;
1605 } // else non-fatal error... let's continue
1606
1607 return state;
1608}
1609
1610TlsSessionState
1611TlsSession::TlsSessionImpl::handleStateShutdown(TlsSessionState state)
1612{
1613 if (params_.logger)
1614 params_.logger->d("[TLS] shutdown");
1615
1616 // Stop ourself
1617 thread_.stop();
1618 return state;
1619}
1620
1621void
1622TlsSession::TlsSessionImpl::process()
1623{
1624 auto old_state = state_.load();
1625 auto new_state = fsmHandlers_[old_state](old_state);
1626
1627 // update state_ with taking care for external state change
1628 if (not std::atomic_compare_exchange_strong(&state_, &old_state, new_state))
1629 new_state = old_state;
1630
1631 if (old_state != new_state)
1632 stateCondition_.notify_all();
1633
1634 if (old_state != new_state and callbacks_.onStateChange)
1635 callbacks_.onStateChange(new_state);
1636}
1637
1638//==============================================================================
1639
1640TlsSession::TlsSession(std::unique_ptr<SocketType>&& transport,
1641 const TlsParams& params,
1642 const TlsSessionCallbacks& cbs,
1643 bool anonymous)
1644
1645 : pimpl_ {std::make_unique<TlsSessionImpl>(std::move(transport), params, cbs, anonymous)}
1646{}
1647
1648TlsSession::~TlsSession() {}
1649
1650bool
1651TlsSession::isInitiator() const
1652{
1653 return !pimpl_->isServer_;
1654}
1655
1656bool
1657TlsSession::isReliable() const
1658{
1659 if (!pimpl_->transport_)
1660 return false;
1661 return pimpl_->transport_->isReliable();
1662}
1663
1664int
1665TlsSession::maxPayload() const
1666{
1667 if (pimpl_->state_ == TlsSessionState::SHUTDOWN)
1668 throw std::runtime_error("Getting maxPayload from non-valid TLS session");
1669 if (!pimpl_->transport_)
1670 return 0;
1671 return pimpl_->transport_->maxPayload();
1672}
1673
1674// Called by anyone to stop the connection and the FSM thread
1675void
1676TlsSession::shutdown()
1677{
1678 pimpl_->newState_ = TlsSessionState::SHUTDOWN;
1679 pimpl_->stateCondition_.notify_all();
1680 pimpl_->rxCv_.notify_one(); // unblock waiting FSM
1681}
1682
1683std::size_t
1684TlsSession::write(const ValueType* data, std::size_t size, std::error_code& ec)
1685{
1686 return pimpl_->send(data, size, ec);
1687}
1688
1689std::size_t
1690TlsSession::read(ValueType* data, std::size_t size, std::error_code& ec)
1691{
1692 std::errc error;
1693
1694 if (pimpl_->state_ != TlsSessionState::ESTABLISHED) {
1695 ec = std::make_error_code(std::errc::broken_pipe);
1696 return 0;
1697 }
1698
1699 while (true) {
1700 ssize_t ret;
1701 {
1702 std::lock_guard<std::mutex> lk(pimpl_->sessionReadMutex_);
1703 if (!pimpl_->session_)
1704 return 0;
1705 ret = gnutls_record_recv(pimpl_->session_, data, size);
1706 }
1707 if (ret > 0) {
1708 ec.clear();
1709 return ret;
1710 }
1711
1712 std::lock_guard<std::mutex> lk(pimpl_->stateMutex_);
1713 if (ret == 0) {
1714 if (pimpl_) {
1715 if (pimpl_->params_.logger)
1716 pimpl_->params_.logger->d("[TLS] eof");
1717 pimpl_->newState_ = TlsSessionState::SHUTDOWN;
1718 pimpl_->stateCondition_.notify_all();
1719 pimpl_->rxCv_.notify_one(); // unblock waiting FSM
1720 }
1721 error = std::errc::broken_pipe;
1722 break;
1723 } else if (ret == GNUTLS_E_REHANDSHAKE) {
1724 if (pimpl_->params_.logger)
1725 pimpl_->params_.logger->d("[TLS] re-handshake");
1726 pimpl_->newState_ = TlsSessionState::HANDSHAKE;
1727 pimpl_->rxCv_.notify_one(); // unblock waiting FSM
1728 pimpl_->stateCondition_.notify_all();
1729 } else if (gnutls_error_is_fatal(ret)) {
1730 if (pimpl_ && pimpl_->state_ != TlsSessionState::SHUTDOWN) {
1731 if (pimpl_->params_.logger)
1732 pimpl_->params_.logger->e("[TLS] fatal error in recv: %s", gnutls_strerror(ret));
1733 pimpl_->newState_ = TlsSessionState::SHUTDOWN;
1734 pimpl_->stateCondition_.notify_all();
1735 pimpl_->rxCv_.notify_one(); // unblock waiting FSM
1736 }
1737 error = std::errc::io_error;
1738 break;
1739 }
1740 }
1741
1742 ec = std::make_error_code(error);
1743 return 0;
1744}
1745
1746void
1747TlsSession::waitForReady(const duration& timeout)
1748{
1749 auto ready = [this]() -> bool {
1750 auto state = pimpl_->state_.load();
1751 return state == TlsSessionState::ESTABLISHED or state == TlsSessionState::SHUTDOWN;
1752 };
1753 std::unique_lock<std::mutex> lk(pimpl_->stateMutex_);
1754 if (timeout == duration::zero())
1755 pimpl_->stateCondition_.wait(lk, ready);
1756 else
1757 pimpl_->stateCondition_.wait_for(lk, timeout, ready);
1758
1759 if (!ready())
1760 throw std::logic_error("Invalid state in TlsSession::waitForReady: "
1761 + std::to_string((int) pimpl_->state_.load()));
1762}
1763
1764int
1765TlsSession::waitForData(std::chrono::milliseconds timeout, std::error_code& ec) const
1766{
1767 if (!pimpl_->transport_) {
1768 ec = std::make_error_code(std::errc::broken_pipe);
1769 return -1;
1770 }
1771 if (!pimpl_->transport_->waitForData(timeout, ec))
1772 return 0;
1773 return 1;
1774}
1775
1776std::shared_ptr<dht::crypto::Certificate>
1777TlsSession::peerCertificate() const
1778{
1779 return pimpl_->pCert_;
1780}
1781
1782const std::shared_ptr<dht::log::Logger>&
1783TlsSession::logger() const
1784{
1785 return pimpl_->params_.logger;
1786}
1787
1788} // namespace tls
1789} // namespace jami