• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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