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