blob: 0b4ede53c06b0a329677556cb98d1df3f332c90b [file] [log] [blame]
Adrien Béraud612b55b2023-05-29 10:42:04 -04001/*
2 * Copyright (C) 2004-2023 Savoir-faire Linux Inc.
3 *
4 * Author: Guillaume Roguez <guillaume.roguez@savoirfairelinux.com>
5 * Author: Sébastien Blin <sebastien.blin@savoirfairelinux.com>
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
20 */
21
22#include "peer_connection.h"
23#include "tls_session.h"
24
25#include <opendht/thread_pool.h>
26#include <opendht/logger.h>
27
28#include <algorithm>
29#include <chrono>
30#include <future>
31#include <vector>
32#include <atomic>
33#include <stdexcept>
34#include <istream>
35#include <ostream>
36#include <unistd.h>
37#include <cstdio>
38
39#ifdef _WIN32
40#include <winsock2.h>
41#include <ws2tcpip.h>
42#else
43#include <sys/select.h>
44#endif
45
46#ifndef _MSC_VER
47#include <sys/time.h>
48#endif
49
50static constexpr int ICE_COMP_ID_SIP_TRANSPORT {1};
51
52namespace jami {
53
54int
55init_crt(gnutls_session_t session, dht::crypto::Certificate& crt)
56{
57 // Support only x509 format
58 if (gnutls_certificate_type_get(session) != GNUTLS_CRT_X509) {
59 return GNUTLS_E_CERTIFICATE_ERROR;
60 }
61
62 // Store verification status
63 unsigned int status = 0;
64 auto ret = gnutls_certificate_verify_peers2(session, &status);
65 if (ret < 0 or (status & GNUTLS_CERT_SIGNATURE_FAILURE) != 0) {
66 return GNUTLS_E_CERTIFICATE_ERROR;
67 }
68
69 unsigned int cert_list_size = 0;
70 auto cert_list = gnutls_certificate_get_peers(session, &cert_list_size);
71 if (cert_list == nullptr) {
72 return GNUTLS_E_CERTIFICATE_ERROR;
73 }
74
75 // Check if received peer certificate is awaited
76 std::vector<std::pair<uint8_t*, uint8_t*>> crt_data;
77 crt_data.reserve(cert_list_size);
78 for (unsigned i = 0; i < cert_list_size; i++)
79 crt_data.emplace_back(cert_list[i].data, cert_list[i].data + cert_list[i].size);
80 crt = dht::crypto::Certificate {crt_data};
81
82 return GNUTLS_E_SUCCESS;
83}
84
85using lock = std::lock_guard<std::mutex>;
86
87//==============================================================================
88
89IceSocketEndpoint::IceSocketEndpoint(std::shared_ptr<IceTransport> ice, bool isSender)
90 : ice_(std::move(ice))
91 , iceIsSender(isSender)
92{}
93
94IceSocketEndpoint::~IceSocketEndpoint()
95{
96 shutdown();
97 if (ice_)
98 dht::ThreadPool::io().run([ice = std::move(ice_)] {});
99}
100
101void
102IceSocketEndpoint::shutdown()
103{
104 // Sometimes the other peer never send any packet
105 // So, we cancel pending read to avoid to have
106 // any blocking operation.
107 if (ice_)
108 ice_->cancelOperations();
109}
110
111int
112IceSocketEndpoint::waitForData(std::chrono::milliseconds timeout, std::error_code& ec) const
113{
114 if (ice_) {
115 if (!ice_->isRunning())
116 return -1;
117 return ice_->waitForData(compId_, timeout, ec);
118 }
119 return -1;
120}
121
122std::size_t
123IceSocketEndpoint::read(ValueType* buf, std::size_t len, std::error_code& ec)
124{
125 if (ice_) {
126 if (!ice_->isRunning())
127 return 0;
128 try {
129 auto res = ice_->recvfrom(compId_, reinterpret_cast<char*>(buf), len, ec);
130 if (res < 0)
131 shutdown();
132 return res;
133 } catch (const std::exception& e) {
134 if (auto logger = ice_->logger())
135 logger->error("IceSocketEndpoint::read exception: %s", e.what());
136 }
137 return 0;
138 }
139 return -1;
140}
141
142std::size_t
143IceSocketEndpoint::write(const ValueType* buf, std::size_t len, std::error_code& ec)
144{
145 if (ice_) {
146 if (!ice_->isRunning())
147 return 0;
148 auto res = 0;
149 res = ice_->send(compId_, reinterpret_cast<const unsigned char*>(buf), len);
150 if (res < 0) {
151 ec.assign(errno, std::generic_category());
152 shutdown();
153 } else {
154 ec.clear();
155 }
156 return res;
157 }
158 return -1;
159}
160
161//==============================================================================
162
163class TlsSocketEndpoint::Impl
164{
165public:
166 static constexpr auto TLS_TIMEOUT = std::chrono::seconds(40);
167
168 Impl(std::unique_ptr<IceSocketEndpoint>&& ep,
169 tls::CertificateStore& certStore,
170 const dht::crypto::Certificate& peer_cert,
171 const Identity& local_identity,
172 const std::shared_future<tls::DhParams>& dh_params)
173 : peerCertificate {peer_cert}
174 , ep_ {ep.get()}
175 {
176 tls::TlsSession::TlsSessionCallbacks tls_cbs
177 = {/*.onStateChange = */ [this](tls::TlsSessionState state) { onTlsStateChange(state); },
178 /*.onRxData = */ [this](std::vector<uint8_t>&& buf) { onTlsRxData(std::move(buf)); },
179 /*.onCertificatesUpdate = */
180 [this](const gnutls_datum_t* l, const gnutls_datum_t* r, unsigned int n) {
181 onTlsCertificatesUpdate(l, r, n);
182 },
183 /*.verifyCertificate = */
184 [this](gnutls_session_t session) {
185 return verifyCertificate(session);
186 }};
187 tls::TlsParams tls_param = {
188 /*.ca_list = */ "",
189 /*.peer_ca = */ nullptr,
190 /*.cert = */ local_identity.second,
191 /*.cert_key = */ local_identity.first,
192 /*.dh_params = */ dh_params,
193 /*.certStore = */ certStore,
194 /*.timeout = */ TLS_TIMEOUT,
195 /*.cert_check = */ nullptr,
196 };
197 tls = std::make_unique<tls::TlsSession>(std::move(ep), tls_param, tls_cbs);
198 }
199
200 Impl(std::unique_ptr<IceSocketEndpoint>&& ep,
201 tls::CertificateStore& certStore,
202 std::function<bool(const dht::crypto::Certificate&)>&& cert_check,
203 const Identity& local_identity,
204 const std::shared_future<tls::DhParams>& dh_params)
205 : peerCertificateCheckFunc {std::move(cert_check)}
206 , peerCertificate {null_cert}
207 , ep_ {ep.get()}
208 {
209 tls::TlsSession::TlsSessionCallbacks tls_cbs
210 = {/*.onStateChange = */ [this](tls::TlsSessionState state) { onTlsStateChange(state); },
211 /*.onRxData = */ [this](std::vector<uint8_t>&& buf) { onTlsRxData(std::move(buf)); },
212 /*.onCertificatesUpdate = */
213 [this](const gnutls_datum_t* l, const gnutls_datum_t* r, unsigned int n) {
214 onTlsCertificatesUpdate(l, r, n);
215 },
216 /*.verifyCertificate = */
217 [this](gnutls_session_t session) {
218 return verifyCertificate(session);
219 }};
220 tls::TlsParams tls_param = {
221 /*.ca_list = */ "",
222 /*.peer_ca = */ nullptr,
223 /*.cert = */ local_identity.second,
224 /*.cert_key = */ local_identity.first,
225 /*.dh_params = */ dh_params,
226 /*.certStore = */ certStore,
227 /*.timeout = */ std::chrono::duration_cast<decltype(tls::TlsParams::timeout)>(TLS_TIMEOUT),
228 /*.cert_check = */ nullptr,
229 };
230 tls = std::make_unique<tls::TlsSession>(std::move(ep), tls_param, tls_cbs);
231 }
232
233 ~Impl()
234 {
235 {
236 std::lock_guard<std::mutex> lk(cbMtx_);
237 onStateChangeCb_ = {};
238 onReadyCb_ = {};
239 }
240 tls.reset();
241 }
242
243 std::shared_ptr<IceTransport> underlyingICE() const
244 {
245 if (ep_)
246 if (const auto* iceSocket = reinterpret_cast<const IceSocketEndpoint*>(ep_))
247 return iceSocket->underlyingICE();
248 return {};
249 }
250
251 // TLS callbacks
252 int verifyCertificate(gnutls_session_t);
253 void onTlsStateChange(tls::TlsSessionState);
254 void onTlsRxData(std::vector<uint8_t>&&);
255 void onTlsCertificatesUpdate(const gnutls_datum_t*, const gnutls_datum_t*, unsigned int);
256
257 std::mutex cbMtx_ {};
258 OnStateChangeCb onStateChangeCb_;
259 dht::crypto::Certificate null_cert;
260 std::function<bool(const dht::crypto::Certificate&)> peerCertificateCheckFunc;
261 const dht::crypto::Certificate& peerCertificate;
262 std::atomic_bool isReady_ {false};
263 OnReadyCb onReadyCb_;
264 std::unique_ptr<tls::TlsSession> tls;
265 const IceSocketEndpoint* ep_;
266};
267
268int
269TlsSocketEndpoint::Impl::verifyCertificate(gnutls_session_t session)
270{
271 dht::crypto::Certificate crt;
272 auto verified = init_crt(session, crt);
273 if (verified != GNUTLS_E_SUCCESS)
274 return verified;
275 if (peerCertificateCheckFunc) {
276 if (!peerCertificateCheckFunc(crt)) {
277 if (const auto& logger = tls->logger())
278 logger->error("[TLS-SOCKET] Refusing peer certificate");
279 return GNUTLS_E_CERTIFICATE_ERROR;
280 }
281
282 null_cert = std::move(crt);
283 } else {
284 if (crt.getPacked() != peerCertificate.getPacked()) {
285 if (const auto& logger = tls->logger())
286 logger->error("[TLS-SOCKET] Unexpected peer certificate");
287 return GNUTLS_E_CERTIFICATE_ERROR;
288 }
289 }
290
291 return GNUTLS_E_SUCCESS;
292}
293
294void
295TlsSocketEndpoint::Impl::onTlsStateChange(tls::TlsSessionState state)
296{
297 std::lock_guard<std::mutex> lk(cbMtx_);
298 if ((state == tls::TlsSessionState::SHUTDOWN || state == tls::TlsSessionState::ESTABLISHED)
299 && !isReady_) {
300 isReady_ = true;
301 if (onReadyCb_)
302 onReadyCb_(state == tls::TlsSessionState::ESTABLISHED);
303 }
304 if (onStateChangeCb_ && !onStateChangeCb_(state))
305 onStateChangeCb_ = {};
306}
307
308void
309TlsSocketEndpoint::Impl::onTlsRxData([[maybe_unused]] std::vector<uint8_t>&& buf)
310{}
311
312void
313TlsSocketEndpoint::Impl::onTlsCertificatesUpdate([[maybe_unused]] const gnutls_datum_t* local_raw,
314 [[maybe_unused]] const gnutls_datum_t* remote_raw,
315 [[maybe_unused]] unsigned int remote_count)
316{}
317
318TlsSocketEndpoint::TlsSocketEndpoint(std::unique_ptr<IceSocketEndpoint>&& tr,
319 tls::CertificateStore& certStore,
320 const Identity& local_identity,
321 const std::shared_future<tls::DhParams>& dh_params,
322 const dht::crypto::Certificate& peer_cert)
323 : pimpl_ {std::make_unique<Impl>(std::move(tr), certStore, peer_cert, local_identity, dh_params)}
324{}
325
326TlsSocketEndpoint::TlsSocketEndpoint(
327 std::unique_ptr<IceSocketEndpoint>&& tr,
328 tls::CertificateStore& certStore,
329 const Identity& local_identity,
330 const std::shared_future<tls::DhParams>& dh_params,
331 std::function<bool(const dht::crypto::Certificate&)>&& cert_check)
332 : pimpl_ {
333 std::make_unique<Impl>(std::move(tr), certStore, std::move(cert_check), local_identity, dh_params)}
334{}
335
336TlsSocketEndpoint::~TlsSocketEndpoint() {}
337
338bool
339TlsSocketEndpoint::isInitiator() const
340{
341 if (!pimpl_->tls) {
342 return false;
343 }
344 return pimpl_->tls->isInitiator();
345}
346
347int
348TlsSocketEndpoint::maxPayload() const
349{
350 if (!pimpl_->tls) {
351 return -1;
352 }
353 return pimpl_->tls->maxPayload();
354}
355
356std::size_t
357TlsSocketEndpoint::read(ValueType* buf, std::size_t len, std::error_code& ec)
358{
359 if (!pimpl_->tls) {
360 ec = std::make_error_code(std::errc::broken_pipe);
361 return -1;
362 }
363 return pimpl_->tls->read(buf, len, ec);
364}
365
366std::size_t
367TlsSocketEndpoint::write(const ValueType* buf, std::size_t len, std::error_code& ec)
368{
369 if (!pimpl_->tls) {
370 ec = std::make_error_code(std::errc::broken_pipe);
371 return -1;
372 }
373 return pimpl_->tls->write(buf, len, ec);
374}
375
376std::shared_ptr<dht::crypto::Certificate>
377TlsSocketEndpoint::peerCertificate() const
378{
379 if (!pimpl_->tls)
380 return {};
381 return pimpl_->tls->peerCertificate();
382}
383
384void
385TlsSocketEndpoint::waitForReady(const std::chrono::milliseconds& timeout)
386{
387 if (!pimpl_->tls) {
388 return;
389 }
390 pimpl_->tls->waitForReady(timeout);
391}
392
393int
394TlsSocketEndpoint::waitForData(std::chrono::milliseconds timeout, std::error_code& ec) const
395{
396 if (!pimpl_->tls) {
397 ec = std::make_error_code(std::errc::broken_pipe);
398 return -1;
399 }
400 return pimpl_->tls->waitForData(timeout, ec);
401}
402
403void
404TlsSocketEndpoint::setOnStateChange(std::function<bool(tls::TlsSessionState state)>&& cb)
405{
406 std::lock_guard<std::mutex> lk(pimpl_->cbMtx_);
407 pimpl_->onStateChangeCb_ = std::move(cb);
408}
409
410void
411TlsSocketEndpoint::setOnReady(std::function<void(bool ok)>&& cb)
412{
413 std::lock_guard<std::mutex> lk(pimpl_->cbMtx_);
414 pimpl_->onReadyCb_ = std::move(cb);
415}
416
417void
418TlsSocketEndpoint::shutdown()
419{
420 pimpl_->tls->shutdown();
421 if (pimpl_->ep_) {
422 const auto* iceSocket = reinterpret_cast<const IceSocketEndpoint*>(pimpl_->ep_);
423 if (iceSocket && iceSocket->underlyingICE())
424 iceSocket->underlyingICE()->cancelOperations();
425 }
426}
427
428void
429TlsSocketEndpoint::monitor() const
430{
431 if (auto ice = pimpl_->underlyingICE())
432 if (auto logger = ice->logger())
433 logger->debug("\t- Ice connection: {}", ice->link());
434}
435
436IpAddr
437TlsSocketEndpoint::getLocalAddress() const
438{
439 if (auto ice = pimpl_->underlyingICE())
440 return ice->getLocalAddress(ICE_COMP_ID_SIP_TRANSPORT);
441 return {};
442}
443
444IpAddr
445TlsSocketEndpoint::getRemoteAddress() const
446{
447 if (auto ice = pimpl_->underlyingICE())
448 return ice->getRemoteAddress(ICE_COMP_ID_SIP_TRANSPORT);
449 return {};
450}
451
452} // namespace jami