1 /*
2 * Copyright (c) 2014, Ericsson AB. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without modification,
5 * are permitted provided that the following conditions are met:
6 *
7 * 1. Redistributions of source code must retain the above copyright notice, this
8 * list of conditions and the following disclaimer.
9 *
10 * 2. Redistributions in binary form must reproduce the above copyright notice, this
11 * list of conditions and the following disclaimer in the documentation and/or other
12 * materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
15 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
16 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
17 * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
18 * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
19 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
21 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
23 * OF SUCH DAMAGE.
24 */
25
26 #ifdef HAVE_CONFIG_H
27 #include "config.h"
28 #endif
29
30 #include <gst/gst.h>
31
32 #include "gstdtlsagent.h"
33
34 #ifdef __APPLE__
35 # define __AVAILABILITYMACROS__
36 # define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
37 #endif
38
39 #include <openssl/err.h>
40 #include <openssl/ssl.h>
41
42 GST_DEBUG_CATEGORY_STATIC (gst_dtls_agent_debug);
43 #define GST_CAT_DEFAULT gst_dtls_agent_debug
44
45 enum
46 {
47 PROP_0,
48 PROP_CERTIFICATE,
49 NUM_PROPERTIES
50 };
51
52 static GParamSpec *properties[NUM_PROPERTIES];
53
54 struct _GstDtlsAgentPrivate
55 {
56 SSL_CTX *ssl_context;
57
58 GstDtlsCertificate *certificate;
59 };
60
61 G_DEFINE_TYPE_WITH_PRIVATE (GstDtlsAgent, gst_dtls_agent, G_TYPE_OBJECT);
62
63 static void gst_dtls_agent_finalize (GObject * gobject);
64 static void gst_dtls_agent_set_property (GObject *, guint prop_id,
65 const GValue *, GParamSpec *);
66 const gchar *gst_dtls_agent_peek_id (GstDtlsAgent *);
67
68 #if OPENSSL_VERSION_NUMBER < 0x10100000L
69 static GRWLock *ssl_locks;
70
71 static void
ssl_locking_function(gint mode,gint lock_num,const gchar * file,gint line)72 ssl_locking_function (gint mode, gint lock_num, const gchar * file, gint line)
73 {
74 gboolean locking;
75 gboolean reading;
76 GRWLock *lock;
77
78 locking = mode & CRYPTO_LOCK;
79 reading = mode & CRYPTO_READ;
80 lock = &ssl_locks[lock_num];
81
82 GST_TRACE_OBJECT (NULL, "%s SSL lock for %s, thread=%p location=%s:%d",
83 locking ? "locking" : "unlocking", reading ? "reading" : "writing",
84 g_thread_self (), file, line);
85
86 if (locking) {
87 if (reading) {
88 g_rw_lock_reader_lock (lock);
89 } else {
90 g_rw_lock_writer_lock (lock);
91 }
92 } else {
93 if (reading) {
94 g_rw_lock_reader_unlock (lock);
95 } else {
96 g_rw_lock_writer_unlock (lock);
97 }
98 }
99 }
100
101 static void
ssl_thread_id_function(CRYPTO_THREADID * id)102 ssl_thread_id_function (CRYPTO_THREADID * id)
103 {
104 CRYPTO_THREADID_set_pointer (id, g_thread_self ());
105 }
106 #endif
107
108 void
_gst_dtls_init_openssl(void)109 _gst_dtls_init_openssl (void)
110 {
111 static gsize is_init = 0;
112
113 if (g_once_init_enter (&is_init)) {
114 GST_DEBUG_CATEGORY_INIT (gst_dtls_agent_debug, "dtlsagent", 0,
115 "DTLS Agent");
116
117 if (OPENSSL_VERSION_NUMBER < 0x1000100fL) {
118 GST_WARNING_OBJECT (NULL,
119 "Incorrect OpenSSL version, should be >= 1.0.1, is %s",
120 OPENSSL_VERSION_TEXT);
121 g_assert_not_reached ();
122 }
123 #if OPENSSL_VERSION_NUMBER < 0x10100000L
124 GST_INFO_OBJECT (NULL, "initializing openssl %lx", OPENSSL_VERSION_NUMBER);
125 SSL_library_init ();
126 SSL_load_error_strings ();
127 ERR_load_BIO_strings ();
128
129 if (!CRYPTO_get_locking_callback ()) {
130 gint i;
131 gint num_locks;
132 num_locks = CRYPTO_num_locks ();
133 ssl_locks = g_new (GRWLock, num_locks);
134 for (i = 0; i < num_locks; ++i) {
135 g_rw_lock_init (&ssl_locks[i]);
136 }
137 CRYPTO_set_locking_callback (ssl_locking_function);
138 CRYPTO_THREADID_set_callback (ssl_thread_id_function);
139 }
140 #endif
141
142 g_once_init_leave (&is_init, 1);
143 }
144 }
145
146 static void
gst_dtls_agent_class_init(GstDtlsAgentClass * klass)147 gst_dtls_agent_class_init (GstDtlsAgentClass * klass)
148 {
149 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
150
151 gobject_class->set_property = gst_dtls_agent_set_property;
152 gobject_class->finalize = gst_dtls_agent_finalize;
153
154 properties[PROP_CERTIFICATE] =
155 g_param_spec_object ("certificate",
156 "GstDtlsCertificate",
157 "Sets the certificate of the agent",
158 GST_TYPE_DTLS_CERTIFICATE,
159 G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
160
161 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
162
163 _gst_dtls_init_openssl ();
164 }
165
166 static int
ssl_warn_cb(const char * str,size_t len,void * u)167 ssl_warn_cb (const char *str, size_t len, void *u)
168 {
169 GstDtlsAgent *self = u;
170 GST_WARNING_OBJECT (self, "ssl error: %s", str);
171 return 0;
172 }
173
174 static void
gst_dtls_agent_init(GstDtlsAgent * self)175 gst_dtls_agent_init (GstDtlsAgent * self)
176 {
177 GstDtlsAgentPrivate *priv = gst_dtls_agent_get_instance_private (self);
178 self->priv = priv;
179
180 ERR_clear_error ();
181
182 #if OPENSSL_VERSION_NUMBER >= 0x1000200fL
183 priv->ssl_context = SSL_CTX_new (DTLS_method ());
184 #else
185 priv->ssl_context = SSL_CTX_new (DTLSv1_method ());
186 #endif
187 if (!priv->ssl_context) {
188 GST_WARNING_OBJECT (self, "Error creating SSL Context");
189 ERR_print_errors_cb (ssl_warn_cb, self);
190
191 g_return_if_reached ();
192 }
193 /* If any non-fatal issues happened, print them out and carry on */
194 if (ERR_peek_error ()) {
195 ERR_print_errors_cb (ssl_warn_cb, self);
196 ERR_clear_error ();
197 }
198
199 SSL_CTX_set_verify_depth (priv->ssl_context, 2);
200 SSL_CTX_set_tlsext_use_srtp (priv->ssl_context, "SRTP_AES128_CM_SHA1_80");
201 SSL_CTX_set_cipher_list (priv->ssl_context,
202 "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH");
203 SSL_CTX_set_read_ahead (priv->ssl_context, 1);
204 #if (OPENSSL_VERSION_NUMBER >= 0x1000200fL) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
205 SSL_CTX_set_ecdh_auto (priv->ssl_context, 1);
206 #endif
207 }
208
209 static void
gst_dtls_agent_finalize(GObject * gobject)210 gst_dtls_agent_finalize (GObject * gobject)
211 {
212 GstDtlsAgentPrivate *priv = GST_DTLS_AGENT (gobject)->priv;
213
214 SSL_CTX_free (priv->ssl_context);
215 priv->ssl_context = NULL;
216
217 g_clear_object (&priv->certificate);
218
219 GST_DEBUG_OBJECT (gobject, "finalized");
220
221 G_OBJECT_CLASS (gst_dtls_agent_parent_class)->finalize (gobject);
222 }
223
224 static void
gst_dtls_agent_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)225 gst_dtls_agent_set_property (GObject * object, guint prop_id,
226 const GValue * value, GParamSpec * pspec)
227 {
228 GstDtlsAgent *self = GST_DTLS_AGENT (object);
229 GstDtlsCertificate *certificate;
230
231 switch (prop_id) {
232 case PROP_CERTIFICATE:
233 certificate = GST_DTLS_CERTIFICATE (g_value_get_object (value));
234 g_return_if_fail (GST_IS_DTLS_CERTIFICATE (certificate));
235 g_return_if_fail (self->priv->ssl_context);
236
237 self->priv->certificate = certificate;
238 g_object_ref (certificate);
239
240 if (!SSL_CTX_use_certificate (self->priv->ssl_context,
241 _gst_dtls_certificate_get_internal_certificate (certificate))) {
242 GST_WARNING_OBJECT (self, "could not use certificate");
243 g_return_if_reached ();
244 }
245
246 if (!SSL_CTX_use_PrivateKey (self->priv->ssl_context,
247 _gst_dtls_certificate_get_internal_key (certificate))) {
248 GST_WARNING_OBJECT (self, "could not use private key");
249 g_return_if_reached ();
250 }
251
252 if (!SSL_CTX_check_private_key (self->priv->ssl_context)) {
253 GST_WARNING_OBJECT (self, "invalid private key");
254 g_return_if_reached ();
255 }
256 break;
257 default:
258 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
259 }
260 }
261
262 GstDtlsCertificate *
gst_dtls_agent_get_certificate(GstDtlsAgent * self)263 gst_dtls_agent_get_certificate (GstDtlsAgent * self)
264 {
265 g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL);
266 if (self->priv->certificate) {
267 g_object_ref (self->priv->certificate);
268 }
269 return self->priv->certificate;
270 }
271
272 gchar *
gst_dtls_agent_get_certificate_pem(GstDtlsAgent * self)273 gst_dtls_agent_get_certificate_pem (GstDtlsAgent * self)
274 {
275 gchar *pem;
276 g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL);
277 g_return_val_if_fail (GST_IS_DTLS_CERTIFICATE (self->priv->certificate),
278 NULL);
279
280 g_object_get (self->priv->certificate, "pem", &pem, NULL);
281
282 return pem;
283 }
284
285 const GstDtlsAgentContext
_gst_dtls_agent_peek_context(GstDtlsAgent * self)286 _gst_dtls_agent_peek_context (GstDtlsAgent * self)
287 {
288 g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL);
289 return self->priv->ssl_context;
290 }
291