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 "gstdtlscertificate.h"
33
34 #include "gstdtlsagent.h"
35
36 #ifdef __APPLE__
37 # define __AVAILABILITYMACROS__
38 # define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER
39 #endif
40
41 #ifdef G_OS_WIN32
42 #include <windows.h>
43 #ifdef X509_NAME
44 #undef X509_NAME
45 #endif
46 #endif
47
48 #include <openssl/bn.h>
49 #include <openssl/rsa.h>
50 #include <openssl/ssl.h>
51
52 #if OPENSSL_VERSION_NUMBER < 0x10100000L
53 #define X509_getm_notBefore X509_get_notBefore
54 #define X509_getm_notAfter X509_get_notAfter
55 #endif
56
57 GST_DEBUG_CATEGORY_STATIC (gst_dtls_certificate_debug);
58 #define GST_CAT_DEFAULT gst_dtls_certificate_debug
59
60 enum
61 {
62 PROP_0,
63 PROP_PEM,
64 NUM_PROPERTIES
65 };
66
67 static GParamSpec *properties[NUM_PROPERTIES];
68
69 #define DEFAULT_PEM NULL
70
71 struct _GstDtlsCertificatePrivate
72 {
73 X509 *x509;
74 EVP_PKEY *private_key;
75
76 gchar *pem;
77 };
78
79 G_DEFINE_TYPE_WITH_CODE (GstDtlsCertificate, gst_dtls_certificate,
80 G_TYPE_OBJECT, G_ADD_PRIVATE (GstDtlsCertificate)
81 GST_DEBUG_CATEGORY_INIT (gst_dtls_certificate_debug,
82 "dtlscertificate", 0, "DTLS Certificate"));
83
84 static void gst_dtls_certificate_finalize (GObject * gobject);
85 static void gst_dtls_certificate_set_property (GObject *, guint prop_id,
86 const GValue *, GParamSpec *);
87 static void gst_dtls_certificate_get_property (GObject *, guint prop_id,
88 GValue *, GParamSpec *);
89
90 static void init_generated (GstDtlsCertificate *);
91 static void init_from_pem_string (GstDtlsCertificate *, const gchar * pem);
92
93 static void
gst_dtls_certificate_class_init(GstDtlsCertificateClass * klass)94 gst_dtls_certificate_class_init (GstDtlsCertificateClass * klass)
95 {
96 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
97
98 gobject_class->set_property = gst_dtls_certificate_set_property;
99 gobject_class->get_property = gst_dtls_certificate_get_property;
100
101 properties[PROP_PEM] =
102 g_param_spec_string ("pem",
103 "Pem string",
104 "A string containing a X509 certificate and RSA private key in PEM format",
105 DEFAULT_PEM,
106 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
107
108 g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
109
110 _gst_dtls_init_openssl ();
111
112 gobject_class->finalize = gst_dtls_certificate_finalize;
113 }
114
115 static void
gst_dtls_certificate_init(GstDtlsCertificate * self)116 gst_dtls_certificate_init (GstDtlsCertificate * self)
117 {
118 GstDtlsCertificatePrivate *priv;
119
120 self->priv = priv = gst_dtls_certificate_get_instance_private (self);
121
122 priv->x509 = NULL;
123 priv->private_key = NULL;
124 priv->pem = NULL;
125 }
126
127 static void
gst_dtls_certificate_finalize(GObject * gobject)128 gst_dtls_certificate_finalize (GObject * gobject)
129 {
130 GstDtlsCertificatePrivate *priv = GST_DTLS_CERTIFICATE (gobject)->priv;
131
132 X509_free (priv->x509);
133 priv->x509 = NULL;
134
135 EVP_PKEY_free (priv->private_key);
136 priv->private_key = NULL;
137
138
139 g_free (priv->pem);
140 priv->pem = NULL;
141
142 G_OBJECT_CLASS (gst_dtls_certificate_parent_class)->finalize (gobject);
143 }
144
145 static void
gst_dtls_certificate_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)146 gst_dtls_certificate_set_property (GObject * object, guint prop_id,
147 const GValue * value, GParamSpec * pspec)
148 {
149 GstDtlsCertificate *self = GST_DTLS_CERTIFICATE (object);
150 const gchar *pem;
151
152 switch (prop_id) {
153 case PROP_PEM:
154 pem = g_value_get_string (value);
155 if (pem) {
156 init_from_pem_string (self, pem);
157 } else {
158 init_generated (self);
159 }
160 break;
161 default:
162 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
163 }
164 }
165
166 static void
gst_dtls_certificate_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)167 gst_dtls_certificate_get_property (GObject * object, guint prop_id,
168 GValue * value, GParamSpec * pspec)
169 {
170 GstDtlsCertificate *self = GST_DTLS_CERTIFICATE (object);
171
172 switch (prop_id) {
173 case PROP_PEM:
174 g_return_if_fail (self->priv->pem);
175 g_value_set_string (value, self->priv->pem);
176 break;
177 default:
178 G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
179 }
180 }
181
182 static const gchar base64_alphabet[64] = {
183 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', 'K', 'L', 'M',
184 'N', 'O', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z',
185 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm',
186 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z',
187 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '+', '/'
188 };
189
190 static void
init_generated(GstDtlsCertificate * self)191 init_generated (GstDtlsCertificate * self)
192 {
193 GstDtlsCertificatePrivate *priv = self->priv;
194 RSA *rsa;
195 BIGNUM *serial_number;
196 ASN1_INTEGER *asn1_serial_number;
197 X509_NAME *name = NULL;
198 gchar common_name[9] = { 0, };
199 gint i;
200
201 g_return_if_fail (!priv->x509);
202 g_return_if_fail (!priv->private_key);
203
204 priv->private_key = EVP_PKEY_new ();
205
206 if (!priv->private_key) {
207 GST_WARNING_OBJECT (self, "failed to create private key");
208 return;
209 }
210
211 priv->x509 = X509_new ();
212
213 if (!priv->x509) {
214 GST_WARNING_OBJECT (self, "failed to create certificate");
215 EVP_PKEY_free (priv->private_key);
216 priv->private_key = NULL;
217 return;
218 }
219
220 /* XXX: RSA_generate_key is actually deprecated in 0.9.8 */
221 #if OPENSSL_VERSION_NUMBER < 0x10100001L
222 rsa = RSA_generate_key (2048, RSA_F4, NULL, NULL);
223 #else
224 rsa = RSA_new ();
225 if (rsa != NULL) {
226 BIGNUM *e = BN_new ();
227 if (e == NULL || !BN_set_word (e, RSA_F4)
228 || !RSA_generate_key_ex (rsa, 2048, e, NULL)) {
229 RSA_free (rsa);
230 rsa = NULL;
231 }
232 if (e)
233 BN_free (e);
234 }
235 #endif
236
237 if (!rsa) {
238 GST_WARNING_OBJECT (self, "failed to generate RSA");
239 EVP_PKEY_free (priv->private_key);
240 priv->private_key = NULL;
241 X509_free (priv->x509);
242 priv->x509 = NULL;
243 return;
244 }
245
246 if (!EVP_PKEY_assign_RSA (priv->private_key, rsa)) {
247 GST_WARNING_OBJECT (self, "failed to assign RSA");
248 RSA_free (rsa);
249 rsa = NULL;
250 EVP_PKEY_free (priv->private_key);
251 priv->private_key = NULL;
252 X509_free (priv->x509);
253 priv->x509 = NULL;
254 return;
255 }
256 rsa = NULL;
257
258 X509_set_version (priv->x509, 2);
259
260 /* Set a random 64 bit integer as serial number */
261 serial_number = BN_new ();
262 BN_pseudo_rand (serial_number, 64, 0, 0);
263 asn1_serial_number = X509_get_serialNumber (priv->x509);
264 BN_to_ASN1_INTEGER (serial_number, asn1_serial_number);
265 BN_free (serial_number);
266
267 /* Set a random 8 byte base64 string as issuer/subject */
268 name = X509_NAME_new ();
269 for (i = 0; i < 8; i++)
270 common_name[i] =
271 base64_alphabet[g_random_int_range (0, G_N_ELEMENTS (base64_alphabet))];
272 X509_NAME_add_entry_by_NID (name, NID_commonName, MBSTRING_ASC,
273 (const guchar *) common_name, -1, -1, 0);
274 X509_set_subject_name (priv->x509, name);
275 X509_set_issuer_name (priv->x509, name);
276 X509_NAME_free (name);
277
278 /* Set expiry in a year */
279 X509_gmtime_adj (X509_getm_notBefore (priv->x509), 0);
280 X509_gmtime_adj (X509_getm_notAfter (priv->x509), 31536000L); /* A year */
281 X509_set_pubkey (priv->x509, priv->private_key);
282
283 if (!X509_sign (priv->x509, priv->private_key, EVP_sha256 ())) {
284 GST_WARNING_OBJECT (self, "failed to sign certificate");
285 EVP_PKEY_free (priv->private_key);
286 priv->private_key = NULL;
287 X509_free (priv->x509);
288 priv->x509 = NULL;
289 return;
290 }
291
292 self->priv->pem = _gst_dtls_x509_to_pem (priv->x509);
293 }
294
295 static void
init_from_pem_string(GstDtlsCertificate * self,const gchar * pem)296 init_from_pem_string (GstDtlsCertificate * self, const gchar * pem)
297 {
298 GstDtlsCertificatePrivate *priv = self->priv;
299 BIO *bio;
300
301 g_return_if_fail (pem);
302 g_return_if_fail (!priv->x509);
303 g_return_if_fail (!priv->private_key);
304
305 bio = BIO_new_mem_buf ((gpointer) pem, -1);
306 g_return_if_fail (bio);
307
308 priv->x509 = PEM_read_bio_X509 (bio, NULL, NULL, NULL);
309
310 if (!priv->x509) {
311 GST_WARNING_OBJECT (self, "failed to read certificate from pem string");
312 return;
313 }
314
315 (void) BIO_reset (bio);
316
317 priv->private_key = PEM_read_bio_PrivateKey (bio, NULL, NULL, NULL);
318
319 BIO_free (bio);
320 bio = NULL;
321
322 if (!priv->private_key) {
323 GST_WARNING_OBJECT (self, "failed to read private key from pem string");
324 X509_free (priv->x509);
325 priv->x509 = NULL;
326 return;
327 }
328
329 self->priv->pem = g_strdup (pem);
330 }
331
332 gchar *
_gst_dtls_x509_to_pem(gpointer x509)333 _gst_dtls_x509_to_pem (gpointer x509)
334 {
335 #define GST_DTLS_BIO_BUFFER_SIZE 4096
336 BIO *bio;
337 gchar buffer[GST_DTLS_BIO_BUFFER_SIZE] = { 0 };
338 gint len;
339 gchar *pem = NULL;
340
341 bio = BIO_new (BIO_s_mem ());
342 g_return_val_if_fail (bio, NULL);
343
344 if (!PEM_write_bio_X509 (bio, (X509 *) x509)) {
345 g_warn_if_reached ();
346 goto beach;
347 }
348
349 len = BIO_read (bio, buffer, GST_DTLS_BIO_BUFFER_SIZE);
350 if (!len) {
351 g_warn_if_reached ();
352 goto beach;
353 }
354
355 pem = g_strndup (buffer, len);
356
357 beach:
358 BIO_free (bio);
359
360 return pem;
361 }
362
363 GstDtlsCertificateInternalCertificate
_gst_dtls_certificate_get_internal_certificate(GstDtlsCertificate * self)364 _gst_dtls_certificate_get_internal_certificate (GstDtlsCertificate * self)
365 {
366 g_return_val_if_fail (GST_IS_DTLS_CERTIFICATE (self), NULL);
367 return self->priv->x509;
368 }
369
370 GstDtlsCertificateInternalKey
_gst_dtls_certificate_get_internal_key(GstDtlsCertificate * self)371 _gst_dtls_certificate_get_internal_key (GstDtlsCertificate * self)
372 {
373 g_return_val_if_fail (GST_IS_DTLS_CERTIFICATE (self), NULL);
374 return self->priv->private_key;
375 }
376