reSIProcate
SIP stack, Dialog Usage Manager and related libraries
Loading...
Searching...
No Matches
FlowManager.cxx
Go to the documentation of this file.
1#ifdef HAVE_CONFIG_H
2#include "config.h"
3#endif
4
5// !slg! At least for builds in Visual Studio on windows this include needs to be above ASIO includes since inlined shared_from_this has
6// a different linkage signature if included after - haven't investigated the full details as to exactly why this happens
7#include <memory>
8
9#include <asio.hpp>
10#ifdef USE_SSL
11#include <asio/ssl.hpp>
12#endif
13#include <map>
14
15#include <rutil/Log.hxx>
16#include <rutil/Logger.hxx>
17#include <rutil/ThreadIf.hxx>
18#include <rutil/Random.hxx>
19#include <rutil/Timer.hxx>
20
21#include "Srtp2Helper.hxx"
22
23#ifdef USE_SSL
24#include <openssl/evp.h>
25#include <openssl/rsa.h>
26#include <openssl/x509.h>
27#include <openssl/x509v3.h>
29#endif //USE_SSL
30
32#include "FlowManager.hxx"
33
34using namespace flowmanager;
35using namespace resip;
36#ifdef USE_SSL
37using namespace dtls;
38#endif
39using namespace std;
40
41#define RESIPROCATE_SUBSYSTEM FlowManagerSubsystem::FLOWMANAGER
42
43#define DTLS_CERT_KEY_LENGTH 4096
44
45namespace flowmanager
46{
48{
49public:
50 IOServiceThread(asio::io_context& ioService) : mIOService(ioService) {}
51
52 virtual ~IOServiceThread() {}
53
54 virtual void thread()
55 {
56 mIOService.run();
57 }
58private:
59 asio::io_context& mIOService;
60};
61}
62
64#ifdef USE_SSL
65 :
66 mSslContext(asio::ssl::context::sslv23), // Generic SSL/TLS - despite the sslv23 name, this uses dynamic negotiation to select the best protocol version
67 mClientCert(0),
68 mClientKey(0),
69 mDtlsFactory(0),
70 mDtlsTimerContext(0)
71#endif
72{
73 mIOServiceWork = new asio::executor_work_guard<asio::io_context::executor_type>(mIOService.get_executor());
74 mIOServiceThread = new IOServiceThread(mIOService);
75 mIOServiceThread->run();
76
77#ifdef USE_SSL
78 // Setup SSL context
79 asio::error_code ec;
80 mSslContext.set_verify_mode(asio::ssl::context::verify_peer |
81 asio::ssl::context::verify_fail_if_no_peer_cert);
82
83 // More can be added with addTlsRootCertificates()
84#define VERIFY_FILE "ca.pem"
85 mSslContext.load_verify_file(VERIFY_FILE, ec);
86 if(ec)
87 {
88 // App may not use TURN over TLS - log as Info only
89 InfoLog(<< "Unable to load verify file: " << VERIFY_FILE << ", error=" << ec.value() << "(" << ec.message() << ")");
90 }
91#endif
92
93 // Initialize SRTP
94 srtp_err_status_t status = srtp_init();
95 if(status && status != srtp_err_status_bad_param) // Note: err_status_bad_param happens if srtp_init is called twice - we allow this for test programs
96 {
97 ErrLog(<< "Unable to initialize SRTP engine, error code=" << status);
98 throw FlowManagerException("Unable to initialize SRTP engine", __FILE__, __LINE__);
99 }
100 status = srtp_install_event_handler(FlowManager::srtpEventHandler);
101}
102
103
105{
106#ifdef USE_SSL
107 // The io thread runs until it has nothing left to do: don't leave it waiting for DTLS
108 // retransmission timers (up to a minute away) of sockets that are gone
109 if(mDtlsTimerContext) mDtlsTimerContext->stop();
110#endif
111 delete mIOServiceWork;
112 mIOServiceThread->join();
113 delete mIOServiceThread;
114
115 #ifdef USE_SSL
116 if(mDtlsFactory) delete mDtlsFactory;
117 if(mClientCert) X509_free(mClientCert);
118 if(mClientKey) EVP_PKEY_free(mClientKey);
119 #endif
120}
121
122#ifdef USE_SSL
123void
125{
126 if(mDtlsFactory)
127 {
128 ErrLog(<< "initializeDtlsFactory called when DtlsFactory is already initialized.");
129 return;
130 }
131
132 Data aor(certAor);
133 if(createCert(aor, 365 /* expireDays */, DTLS_CERT_KEY_LENGTH /* keyLen */, mClientCert, mClientKey))
134 {
135 mDtlsTimerContext = new FlowDtlsTimerContext(mIOService);
136 mDtlsFactory = new DtlsFactory(std::unique_ptr<DtlsTimerContext>(mDtlsTimerContext), mClientCert, mClientKey);
137 resip_assert(mDtlsFactory);
138 }
139 else
140 {
141 ErrLog(<< "Unable to create a client cert, cannot use Dtls-Srtp.");
142 }
143}
144#endif
145
146void
147FlowManager::srtpEventHandler(srtp_event_data_t *data)
148{
149 switch(data->event) {
150 case event_ssrc_collision:
151 WarningLog(<< "SRTP SSRC collision");
152 break;
153 case event_key_soft_limit:
154 WarningLog(<< "SRTP key usage soft limit reached");
155 break;
156 case event_key_hard_limit:
157 WarningLog(<< "SRTP key usage hard limit reached");
158 break;
159 case event_packet_index_limit:
160 WarningLog(<< "SRTP packet index limit reached");
161 break;
162 default:
163 WarningLog(<< "SRTP unknown event reported to handler");
164 }
165 }
166
169 const StunTuple& localBinding,
170 bool rtcpEnabled,
171 MediaStream::NatTraversalMode natTraversalMode,
172 const char* natTraversalServerHostname,
173 unsigned short natTraversalServerPort,
174 const char* stunUsername,
175 const char* stunPassword,
176 bool forceCOMedia,
177 std::shared_ptr<FlowContext> context)
178{
179 MediaStream* newMediaStream = 0;
180 if(rtcpEnabled)
181 {
182 StunTuple localRtcpBinding(localBinding.getTransportType(), localBinding.getAddress(), localBinding.getPort() + 1);
183 newMediaStream = new MediaStream(mIOService,
184#ifdef USE_SSL
185 mSslContext,
186#endif
187 mediaStreamHandler,
188 localBinding,
189 localRtcpBinding,
190#ifdef USE_SSL
191 mDtlsFactory,
192#endif
193 natTraversalMode,
194 natTraversalServerHostname,
195 natTraversalServerPort,
196 stunUsername,
197 stunPassword,
198 forceCOMedia,
199 mRtcpEventLoggingHandler,
200 context,
201 mValidateTlsServerHostname);
202 }
203 else
204 {
205 StunTuple rtcpDisabled; // Default constructor sets transport type to None - this signals Rtcp is disabled
206 newMediaStream = new MediaStream(mIOService,
207#ifdef USE_SSL
208 mSslContext,
209#endif
210 mediaStreamHandler,
211 localBinding,
212 rtcpDisabled,
213#ifdef USE_SSL
214 mDtlsFactory,
215#endif
216 natTraversalMode,
217 natTraversalServerHostname,
218 natTraversalServerPort,
219 stunUsername,
220 stunPassword,
221 forceCOMedia,
222 nullptr,
223 context,
224 mValidateTlsServerHostname);
225 }
226 return newMediaStream;
227}
228
229#ifdef USE_SSL
230bool
232{
233 asio::error_code ec;
234 mSslContext.load_verify_file(rootCertificatesFile.c_str(), ec);
235 if(ec)
236 {
237 ErrLog(<< "Unable to load TLS root certificates from " << rootCertificatesFile << ", error=" << ec.value() << "(" << ec.message() << ")");
238 return false;
239 }
240 return true;
241}
242
243int
244FlowManager::createCert(const resip::Data& pAor, int expireDays, int keyLen, X509*& outCert, EVP_PKEY*& outKey )
245{
246 int ret;
247
248 Data aor = "sip:" + pAor;
249
250 // Make sure that necessary algorithms exist:
251 resip_assert(EVP_sha256());
252
253 // An RSA key (the default public exponent is 65537): ECDSA would be quicker to generate,
254 // but older endpoints may only support RSA
255 EVP_PKEY* privkey = NULL;
256 EVP_PKEY_CTX* keyCtx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL);
257 ret = keyCtx &&
258 EVP_PKEY_keygen_init(keyCtx) == 1 &&
259 EVP_PKEY_CTX_set_rsa_keygen_bits(keyCtx, keyLen) == 1 &&
260 EVP_PKEY_keygen(keyCtx, &privkey) == 1;
261 EVP_PKEY_CTX_free(keyCtx);
262 if(!ret)
263 {
264 return 0; // couldn't make key pair
265 }
266
267 X509* cert = X509_new();
268 resip_assert(cert);
269
270 X509_NAME* subject = X509_NAME_new();
271 X509_EXTENSION* ext;
272
273 // set version to X509v3 (starts from 0)
274 X509_set_version(cert, 2L);
275
276 int serial = Random::getRandom(); // get an int worth of randomness
277 resip_assert(sizeof(int)==4);
278 ASN1_INTEGER_set(X509_get_serialNumber(cert),serial);
279
280// ret = X509_NAME_add_entry_by_txt( subject, "O", MBSTRING_ASC,
281// (unsigned char *) domain.data(), domain.size(),
282// -1, 0);
283 resip_assert(ret);
284 ret = X509_NAME_add_entry_by_txt( subject, "CN", MBSTRING_ASC,
285 (unsigned char *) aor.data(), (int)aor.size(),
286 -1, 0);
287 resip_assert(ret);
288
289 ret = X509_set_issuer_name(cert, subject);
290 resip_assert(ret);
291 ret = X509_set_subject_name(cert, subject);
292 resip_assert(ret);
293 X509_NAME_free(subject); // the certificate holds copies
294
295 const long duration = 60*60*24*expireDays;
296 X509_gmtime_adj(X509_getm_notBefore(cert),0);
297 X509_gmtime_adj(X509_getm_notAfter(cert), duration);
298
299 ret = X509_set_pubkey(cert, privkey);
300 resip_assert(ret);
301
302 Data subjectAltNameStr = Data("URI:sip:") + aor
303 + Data(",URI:im:")+aor
304 + Data(",URI:pres:")+aor;
305 ext = X509V3_EXT_conf_nid( NULL , NULL , NID_subject_alt_name,
306 (char*) subjectAltNameStr.c_str() );
307 X509_add_ext( cert, ext, -1);
308 X509_EXTENSION_free(ext);
309
310 static char CA_FALSE[] = "CA:FALSE";
311 ext = X509V3_EXT_conf_nid(NULL, NULL, NID_basic_constraints, CA_FALSE);
312 ret = X509_add_ext( cert, ext, -1);
313 resip_assert(ret);
314 X509_EXTENSION_free(ext);
315
316 // TODO add extensions NID_subject_key_identifier and NID_authority_key_identifier
317
318 // SHA-256, the hash function of the fingerprint we advertise (RFC 4572 required them to match)
319 ret = X509_sign(cert, privkey, EVP_sha256());
320 resip_assert(ret);
321
322 outCert = cert;
323 outKey = privkey;
324 return ret;
325}
326#endif
327
328/* ====================================================================
329
330 Copyright (c) 2008-2026, SIP Spectrum, Inc. http://sipspectrum.com
331 Copyright (c) 2007-2008, Plantronics, Inc.
332 All rights reserved.
333
334 Redistribution and use in source and binary forms, with or without
335 modification, are permitted provided that the following conditions are
336 met:
337
338 1. Redistributions of source code must retain the above copyright
339 notice, this list of conditions and the following disclaimer.
340
341 2. Redistributions in binary form must reproduce the above copyright
342 notice, this list of conditions and the following disclaimer in the
343 documentation and/or other materials provided with the distribution.
344
345 3. Neither the name of Plantronics nor the names of its contributors
346 may be used to endorse or promote products derived from this
347 software without specific prior written permission.
348
349 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
350 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
351 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
352 A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
353 OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
354 SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
355 LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
356 DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
357 THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
358 (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
359 OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
360
361 ==================================================================== */
struct evp_pkey_st EVP_PKEY
struct x509_st X509
#define DTLS_CERT_KEY_LENGTH
#define VERIFY_FILE
#define InfoLog(args_)
Definition Logger.hxx:131
#define WarningLog(args_)
Definition Logger.hxx:134
#define ErrLog(args_)
Definition Logger.hxx:137
#define resip_assert(x)
This file needs to work for both C and C++ code.
This class is used to raise exceptions in the flow manager.
bool addTlsRootCertificates(const resip::Data &rootCertificatesFile)
Adds the root (CA) certificates in a PEM file to those TURN servers' TLS certificates are verified wi...
static int createCert(const resip::Data &pAor, int expireDays, int keyLen, X509 *&outCert, EVP_PKEY *&outKey)
void initializeDtlsFactory(const char *certAor)
MediaStream * createMediaStream(MediaStreamHandler &mediaStreamHandler, const StunTuple &localBinding, bool rtcpEnabled=true, MediaStream::NatTraversalMode natTraversalMode=MediaStream::NoNatTraversal, const char *natTraversalServerHostname=0, unsigned short natTraversalServerPort=0, const char *stunUsername=0, const char *stunPassword=0, bool forceCOMedia=false, std::shared_ptr< FlowContext > context=nullptr)
IOServiceThread(asio::io_context &ioService)
This class represents a media stream, that consists of a series of componenets or flows.
TransportType getTransportType() const
Definition StunTuple.hxx:41
const asio::ip::address & getAddress() const
Definition StunTuple.hxx:44
unsigned int getPort() const
Definition StunTuple.hxx:47
An alternative to std::string, encapsulates an arbitrary buffer of bytes.
Definition Data.hxx:90
const char * c_str() const
Returns a null-terminated string representing.
Definition Data.cxx:1097
size_type size() const noexcept
Returns the number of bytes in this Data.
Definition Data.hxx:599
const char * data() const noexcept
Returns a pointer to the contents of this Data.
Definition Data.hxx:608
static int getRandom()
Returns a postive integer (31 bits) of randomness.
Definition Random.cxx:261
A wrapper class to create and spawn a thread.
Definition ThreadIf.hxx:54
This class is used during media sessions that use Dtls-Srtp for a media transport.
Definition ErrorCode.hxx:4
dcm! – add UnusedChecking(_enum) below;