blob: 85a218ec5a259c3032aa2195d4d54ef87f4c548a [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 */
Adrien Béraud612b55b2023-05-29 10:42:04 -040017#include "peer_connection.h"
18#include "tls_session.h"
19
20#include <opendht/thread_pool.h>
21#include <opendht/logger.h>
22
23#include <algorithm>
24#include <chrono>
25#include <future>
26#include <vector>
27#include <atomic>
28#include <stdexcept>
29#include <istream>
30#include <ostream>
31#include <unistd.h>
32#include <cstdio>
33
34#ifdef _WIN32
35#include <winsock2.h>
36#include <ws2tcpip.h>
37#else
38#include <sys/select.h>
39#endif
40
41#ifndef _MSC_VER
42#include <sys/time.h>
43#endif
44
Adrien Béraud1ae60aa2023-07-07 09:55:09 -040045namespace dhtnet {
Adrien Béraud612b55b2023-05-29 10:42:04 -040046
47int
48init_crt(gnutls_session_t session, dht::crypto::Certificate& crt)
49{
50 // Support only x509 format
51 if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
52 return GNUTLS_E_CERTIFICATE_ERROR;
53 }
54
55 // Store verification status
56 unsigned int status = 0;
57 auto ret = gnutls_certificate_verify_peers2(session, &status);
58 if (ret < 0 or (status & GNUTLS_CERT_SIGNATURE_FAILURE) != 0) {
59 return GNUTLS_E_CERTIFICATE_ERROR;
60 }
61
62 unsigned int cert_list_size = 0;
63 auto cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
64 if (cert_list == nullptr) {
65 return GNUTLS_E_CERTIFICATE_ERROR;
66 }
67
68 // Check if received peer certificate is awaited
69 std::vector<std::pair<uint8_t*, uint8_t*>> crt_data;
70 crt_data.reserve(cert_list_size);
71 for (unsigned i = 0; i < cert_list_size; i++)
72 crt_data.emplace_back(cert_list[i].data, cert_list[i].data + cert_list[i].size);
73 crt = dht::crypto::Certificate {crt_data};
74
75 return GNUTLS_E_SUCCESS;
76}
77
Adrien Béraud612b55b2023-05-29 10:42:04 -040078//==============================================================================
79
80IceSocketEndpoint::IceSocketEndpoint(std::shared_ptr<IceTransport> ice, bool isSender)
81 : ice_(std::move(ice))
82 , iceIsSender(isSender)
83{}
84
85IceSocketEndpoint::~IceSocketEndpoint()
86{
87 shutdown();
88 if (ice_)
89 dht::ThreadPool::io().run([ice = std::move(ice_)] {});
90}
91
92void
93IceSocketEndpoint::shutdown()
94{
95 // Sometimes the other peer never send any packet
96 // So, we cancel pending read to avoid to have
97 // any blocking operation.
98 if (ice_)
99 ice_->cancelOperations();
100}
101
102int
103IceSocketEndpoint::waitForData(std::chrono::milliseconds timeout, std::error_code& ec) const
104{
105 if (ice_) {
106 if (!ice_->isRunning())
107 return -1;
108 return ice_->waitForData(compId_, timeout, ec);
109 }
110 return -1;
111}
112
113std::size_t
114IceSocketEndpoint::read(ValueType* buf, std::size_t len, std::error_code& ec)
115{
116 if (ice_) {
117 if (!ice_->isRunning())
118 return 0;
119 try {
120 auto res = ice_->recvfrom(compId_, reinterpret_cast<char*>(buf), len, ec);
121 if (res < 0)
122 shutdown();
123 return res;
124 } catch (const std::exception& e) {
125 if (auto logger = ice_->logger())
126 logger->error("IceSocketEndpoint::read exception: %s", e.what());
127 }
128 return 0;
129 }
130 return -1;
131}
132
133std::size_t
134IceSocketEndpoint::write(const ValueType* buf, std::size_t len, std::error_code& ec)
135{
136 if (ice_) {
137 if (!ice_->isRunning())
138 return 0;
139 auto res = 0;
140 res = ice_->send(compId_, reinterpret_cast<const unsigned char*>(buf), len);
141 if (res < 0) {
142 ec.assign(errno, std::generic_category());
143 shutdown();
144 } else {
145 ec.clear();
146 }
147 return res;
148 }
149 return -1;
150}
151
152//==============================================================================
153
154class TlsSocketEndpoint::Impl
155{
156public:
157 static constexpr auto TLS_TIMEOUT = std::chrono::seconds(40);
158
159 Impl(std::unique_ptr<IceSocketEndpoint>&& ep,
160 tls::CertificateStore& certStore,
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400161 const std::shared_ptr<asio::io_context>& ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400162 const dht::crypto::Certificate& peer_cert,
163 const Identity& local_identity,
164 const std::shared_future<tls::DhParams>& dh_params)
165 : peerCertificate {peer_cert}
166 , ep_ {ep.get()}
167 {
168 tls::TlsSession::TlsSessionCallbacks tls_cbs
169 = {/*.onStateChange = */ [this](tls::TlsSessionState state) { onTlsStateChange(state); },
170 /*.onRxData = */ [this](std::vector<uint8_t>&& buf) { onTlsRxData(std::move(buf)); },
171 /*.onCertificatesUpdate = */
172 [this](const gnutls_datum_t* l, const gnutls_datum_t* r, unsigned int n) {
173 onTlsCertificatesUpdate(l, r, n);
174 },
175 /*.verifyCertificate = */
176 [this](gnutls_session_t session) {
177 return verifyCertificate(session);
178 }};
179 tls::TlsParams tls_param = {
180 /*.ca_list = */ "",
181 /*.peer_ca = */ nullptr,
182 /*.cert = */ local_identity.second,
183 /*.cert_key = */ local_identity.first,
184 /*.dh_params = */ dh_params,
185 /*.certStore = */ certStore,
186 /*.timeout = */ TLS_TIMEOUT,
187 /*.cert_check = */ nullptr,
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400188 /*.io_context = */ ioContext,
189 /* .logger = */ ep->underlyingICE()->logger()
Adrien Béraud612b55b2023-05-29 10:42:04 -0400190 };
191 tls = std::make_unique<tls::TlsSession>(std::move(ep), tls_param, tls_cbs);
192 }
193
194 Impl(std::unique_ptr<IceSocketEndpoint>&& ep,
195 tls::CertificateStore& certStore,
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400196 std::shared_ptr<asio::io_context> ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400197 std::function<bool(const dht::crypto::Certificate&)>&& cert_check,
198 const Identity& local_identity,
199 const std::shared_future<tls::DhParams>& dh_params)
200 : peerCertificateCheckFunc {std::move(cert_check)}
201 , peerCertificate {null_cert}
202 , ep_ {ep.get()}
203 {
204 tls::TlsSession::TlsSessionCallbacks tls_cbs
205 = {/*.onStateChange = */ [this](tls::TlsSessionState state) { onTlsStateChange(state); },
206 /*.onRxData = */ [this](std::vector<uint8_t>&& buf) { onTlsRxData(std::move(buf)); },
207 /*.onCertificatesUpdate = */
208 [this](const gnutls_datum_t* l, const gnutls_datum_t* r, unsigned int n) {
209 onTlsCertificatesUpdate(l, r, n);
210 },
211 /*.verifyCertificate = */
212 [this](gnutls_session_t session) {
213 return verifyCertificate(session);
214 }};
215 tls::TlsParams tls_param = {
216 /*.ca_list = */ "",
217 /*.peer_ca = */ nullptr,
218 /*.cert = */ local_identity.second,
219 /*.cert_key = */ local_identity.first,
220 /*.dh_params = */ dh_params,
221 /*.certStore = */ certStore,
222 /*.timeout = */ std::chrono::duration_cast<decltype(tls::TlsParams::timeout)>(TLS_TIMEOUT),
223 /*.cert_check = */ nullptr,
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400224 /*.io_context = */ ioContext,
225 /* .logger = */ ep->underlyingICE()->logger()
Adrien Béraud612b55b2023-05-29 10:42:04 -0400226 };
227 tls = std::make_unique<tls::TlsSession>(std::move(ep), tls_param, tls_cbs);
228 }
229
230 ~Impl()
231 {
232 {
Adrien Béraud024c46f2024-03-02 23:53:18 -0500233 std::lock_guard lk(cbMtx_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400234 onStateChangeCb_ = {};
235 onReadyCb_ = {};
236 }
237 tls.reset();
238 }
239
240 std::shared_ptr<IceTransport> underlyingICE() const
241 {
242 if (ep_)
243 if (const auto* iceSocket = reinterpret_cast<const IceSocketEndpoint*>(ep_))
244 return iceSocket->underlyingICE();
245 return {};
246 }
247
248 // TLS callbacks
249 int verifyCertificate(gnutls_session_t);
250 void onTlsStateChange(tls::TlsSessionState);
251 void onTlsRxData(std::vector<uint8_t>&&);
252 void onTlsCertificatesUpdate(const gnutls_datum_t*, const gnutls_datum_t*, unsigned int);
253
254 std::mutex cbMtx_ {};
255 OnStateChangeCb onStateChangeCb_;
256 dht::crypto::Certificate null_cert;
257 std::function<bool(const dht::crypto::Certificate&)> peerCertificateCheckFunc;
258 const dht::crypto::Certificate& peerCertificate;
259 std::atomic_bool isReady_ {false};
260 OnReadyCb onReadyCb_;
261 std::unique_ptr<tls::TlsSession> tls;
262 const IceSocketEndpoint* ep_;
263};
264
265int
266TlsSocketEndpoint::Impl::verifyCertificate(gnutls_session_t session)
267{
268 dht::crypto::Certificate crt;
269 auto verified = init_crt(session, crt);
270 if (verified != GNUTLS_E_SUCCESS)
271 return verified;
272 if (peerCertificateCheckFunc) {
273 if (!peerCertificateCheckFunc(crt)) {
274 if (const auto& logger = tls->logger())
275 logger->error("[TLS-SOCKET] Refusing peer certificate");
276 return GNUTLS_E_CERTIFICATE_ERROR;
277 }
278
279 null_cert = std::move(crt);
280 } else {
281 if (crt.getPacked() != peerCertificate.getPacked()) {
282 if (const auto& logger = tls->logger())
283 logger->error("[TLS-SOCKET] Unexpected peer certificate");
284 return GNUTLS_E_CERTIFICATE_ERROR;
285 }
286 }
287
288 return GNUTLS_E_SUCCESS;
289}
290
291void
292TlsSocketEndpoint::Impl::onTlsStateChange(tls::TlsSessionState state)
293{
Adrien Béraud024c46f2024-03-02 23:53:18 -0500294 std::lock_guard lk(cbMtx_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400295 if ((state == tls::TlsSessionState::SHUTDOWN || state == tls::TlsSessionState::ESTABLISHED)
296 && !isReady_) {
297 isReady_ = true;
298 if (onReadyCb_)
299 onReadyCb_(state == tls::TlsSessionState::ESTABLISHED);
300 }
301 if (onStateChangeCb_ && !onStateChangeCb_(state))
302 onStateChangeCb_ = {};
303}
304
305void
306TlsSocketEndpoint::Impl::onTlsRxData([[maybe_unused]] std::vector<uint8_t>&& buf)
307{}
308
309void
310TlsSocketEndpoint::Impl::onTlsCertificatesUpdate([[maybe_unused]] const gnutls_datum_t* local_raw,
311 [[maybe_unused]] const gnutls_datum_t* remote_raw,
312 [[maybe_unused]] unsigned int remote_count)
313{}
314
315TlsSocketEndpoint::TlsSocketEndpoint(std::unique_ptr<IceSocketEndpoint>&& tr,
316 tls::CertificateStore& certStore,
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400317 const std::shared_ptr<asio::io_context>& ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400318 const Identity& local_identity,
319 const std::shared_future<tls::DhParams>& dh_params,
320 const dht::crypto::Certificate& peer_cert)
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400321 : pimpl_ {std::make_unique<Impl>(std::move(tr), certStore, ioContext, peer_cert, local_identity, dh_params)}
Adrien Béraud612b55b2023-05-29 10:42:04 -0400322{}
323
324TlsSocketEndpoint::TlsSocketEndpoint(
325 std::unique_ptr<IceSocketEndpoint>&& tr,
326 tls::CertificateStore& certStore,
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400327 const std::shared_ptr<asio::io_context>& ioContext,
Adrien Béraud612b55b2023-05-29 10:42:04 -0400328 const Identity& local_identity,
329 const std::shared_future<tls::DhParams>& dh_params,
330 std::function<bool(const dht::crypto::Certificate&)>&& cert_check)
331 : pimpl_ {
Adrien Béraud3f93ddf2023-07-21 14:46:22 -0400332 std::make_unique<Impl>(std::move(tr), certStore, ioContext, std::move(cert_check), local_identity, dh_params)}
Adrien Béraud612b55b2023-05-29 10:42:04 -0400333{}
334
335TlsSocketEndpoint::~TlsSocketEndpoint() {}
336
337bool
338TlsSocketEndpoint::isInitiator() const
339{
340 if (!pimpl_->tls) {
341 return false;
342 }
343 return pimpl_->tls->isInitiator();
344}
345
346int
347TlsSocketEndpoint::maxPayload() const
348{
349 if (!pimpl_->tls) {
350 return -1;
351 }
352 return pimpl_->tls->maxPayload();
353}
354
355std::size_t
356TlsSocketEndpoint::read(ValueType* buf, std::size_t len, std::error_code& ec)
357{
358 if (!pimpl_->tls) {
359 ec = std::make_error_code(std::errc::broken_pipe);
360 return -1;
361 }
362 return pimpl_->tls->read(buf, len, ec);
363}
364
365std::size_t
366TlsSocketEndpoint::write(const ValueType* buf, std::size_t len, std::error_code& ec)
367{
368 if (!pimpl_->tls) {
369 ec = std::make_error_code(std::errc::broken_pipe);
370 return -1;
371 }
372 return pimpl_->tls->write(buf, len, ec);
373}
374
375std::shared_ptr<dht::crypto::Certificate>
376TlsSocketEndpoint::peerCertificate() const
377{
378 if (!pimpl_->tls)
379 return {};
380 return pimpl_->tls->peerCertificate();
381}
382
Adrien Béraud612b55b2023-05-29 10:42:04 -0400383int
384TlsSocketEndpoint::waitForData(std::chrono::milliseconds timeout, std::error_code& ec) const
385{
386 if (!pimpl_->tls) {
387 ec = std::make_error_code(std::errc::broken_pipe);
388 return -1;
389 }
390 return pimpl_->tls->waitForData(timeout, ec);
391}
392
393void
394TlsSocketEndpoint::setOnStateChange(std::function<bool(tls::TlsSessionState state)>&& cb)
395{
Adrien Béraud024c46f2024-03-02 23:53:18 -0500396 std::lock_guard lk(pimpl_->cbMtx_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400397 pimpl_->onStateChangeCb_ = std::move(cb);
398}
399
400void
401TlsSocketEndpoint::setOnReady(std::function<void(bool ok)>&& cb)
402{
Adrien Béraud024c46f2024-03-02 23:53:18 -0500403 std::lock_guard lk(pimpl_->cbMtx_);
Adrien Béraud612b55b2023-05-29 10:42:04 -0400404 pimpl_->onReadyCb_ = std::move(cb);
405}
406
407void
408TlsSocketEndpoint::shutdown()
409{
410 pimpl_->tls->shutdown();
411 if (pimpl_->ep_) {
412 const auto* iceSocket = reinterpret_cast<const IceSocketEndpoint*>(pimpl_->ep_);
413 if (iceSocket && iceSocket->underlyingICE())
414 iceSocket->underlyingICE()->cancelOperations();
415 }
416}
417
418void
419TlsSocketEndpoint::monitor() const
420{
421 if (auto ice = pimpl_->underlyingICE())
422 if (auto logger = ice->logger())
423 logger->debug("\t- Ice connection: {}", ice->link());
424}
425
426IpAddr
427TlsSocketEndpoint::getLocalAddress() const
428{
429 if (auto ice = pimpl_->underlyingICE())
430 return ice->getLocalAddress(ICE_COMP_ID_SIP_TRANSPORT);
431 return {};
432}
433
434IpAddr
435TlsSocketEndpoint::getRemoteAddress() const
436{
437 if (auto ice = pimpl_->underlyingICE())
438 return ice->getRemoteAddress(ICE_COMP_ID_SIP_TRANSPORT);
439 return {};
440}
441
Sébastien Blin464bdff2023-07-19 08:02:53 -0400442} // namespace dhtnet