| /* |
| * Copyright (c) 2014, Ericsson AB. All rights reserved. |
| * |
| * Redistribution and use in source and binary forms, with or without modification, |
| * are permitted provided that the following conditions are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright notice, this |
| * list of conditions and the following disclaimer. |
| * |
| * 2. Redistributions in binary form must reproduce the above copyright notice, this |
| * list of conditions and the following disclaimer in the documentation and/or other |
| * materials provided with the distribution. |
| * |
| * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND |
| * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. |
| * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, |
| * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT |
| * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR |
| * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, |
| * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) |
| * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY |
| * OF SUCH DAMAGE. |
| */ |
| |
| #ifdef HAVE_CONFIG_H |
| #include "config.h" |
| #endif |
| |
| #include <gst/gst.h> |
| |
| #include "gstdtlscertificate.h" |
| |
| #include "gstdtlsagent.h" |
| |
| #ifdef __APPLE__ |
| # define __AVAILABILITYMACROS__ |
| # define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER |
| #endif |
| |
| #ifdef G_OS_WIN32 |
| #include <windows.h> |
| #ifdef X509_NAME |
| #undef X509_NAME |
| #endif |
| #endif |
| |
| #include <openssl/ssl.h> |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_dtls_certificate_debug); |
| #define GST_CAT_DEFAULT gst_dtls_certificate_debug |
| |
| G_DEFINE_TYPE_WITH_CODE (GstDtlsCertificate, gst_dtls_certificate, |
| G_TYPE_OBJECT, GST_DEBUG_CATEGORY_INIT (gst_dtls_certificate_debug, |
| "dtlscertificate", 0, "DTLS Certificate")); |
| |
| #define GST_DTLS_CERTIFICATE_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GST_TYPE_DTLS_CERTIFICATE, GstDtlsCertificatePrivate)) |
| |
| enum |
| { |
| PROP_0, |
| PROP_PEM, |
| NUM_PROPERTIES |
| }; |
| |
| static GParamSpec *properties[NUM_PROPERTIES]; |
| |
| #define DEFAULT_PEM NULL |
| |
| struct _GstDtlsCertificatePrivate |
| { |
| X509 *x509; |
| EVP_PKEY *private_key; |
| |
| gchar *pem; |
| }; |
| |
| static void gst_dtls_certificate_finalize (GObject * gobject); |
| static void gst_dtls_certificate_set_property (GObject *, guint prop_id, |
| const GValue *, GParamSpec *); |
| static void gst_dtls_certificate_get_property (GObject *, guint prop_id, |
| GValue *, GParamSpec *); |
| |
| static void init_generated (GstDtlsCertificate *); |
| static void init_from_pem_string (GstDtlsCertificate *, const gchar * pem); |
| |
| static void |
| gst_dtls_certificate_class_init (GstDtlsCertificateClass * klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| g_type_class_add_private (klass, sizeof (GstDtlsCertificatePrivate)); |
| |
| gobject_class->set_property = gst_dtls_certificate_set_property; |
| gobject_class->get_property = gst_dtls_certificate_get_property; |
| |
| properties[PROP_PEM] = |
| g_param_spec_string ("pem", |
| "Pem string", |
| "A string containing a X509 certificate and RSA private key in PEM format", |
| DEFAULT_PEM, |
| G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
| |
| g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties); |
| |
| _gst_dtls_init_openssl (); |
| |
| gobject_class->finalize = gst_dtls_certificate_finalize; |
| } |
| |
| static void |
| gst_dtls_certificate_init (GstDtlsCertificate * self) |
| { |
| GstDtlsCertificatePrivate *priv = GST_DTLS_CERTIFICATE_GET_PRIVATE (self); |
| self->priv = priv; |
| |
| priv->x509 = NULL; |
| priv->private_key = NULL; |
| priv->pem = NULL; |
| } |
| |
| static void |
| gst_dtls_certificate_finalize (GObject * gobject) |
| { |
| GstDtlsCertificatePrivate *priv = GST_DTLS_CERTIFICATE (gobject)->priv; |
| |
| X509_free (priv->x509); |
| priv->x509 = NULL; |
| |
| EVP_PKEY_free (priv->private_key); |
| priv->private_key = NULL; |
| |
| |
| g_free (priv->pem); |
| priv->pem = NULL; |
| |
| G_OBJECT_CLASS (gst_dtls_certificate_parent_class)->finalize (gobject); |
| } |
| |
| static void |
| gst_dtls_certificate_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstDtlsCertificate *self = GST_DTLS_CERTIFICATE (object); |
| const gchar *pem; |
| |
| switch (prop_id) { |
| case PROP_PEM: |
| pem = g_value_get_string (value); |
| if (pem) { |
| init_from_pem_string (self, pem); |
| } else { |
| init_generated (self); |
| } |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); |
| } |
| } |
| |
| static void |
| gst_dtls_certificate_get_property (GObject * object, guint prop_id, |
| GValue * value, GParamSpec * pspec) |
| { |
| GstDtlsCertificate *self = GST_DTLS_CERTIFICATE (object); |
| |
| switch (prop_id) { |
| case PROP_PEM: |
| g_return_if_fail (self->priv->pem); |
| g_value_set_string (value, self->priv->pem); |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); |
| } |
| } |
| |
| static void |
| init_generated (GstDtlsCertificate * self) |
| { |
| GstDtlsCertificatePrivate *priv = self->priv; |
| RSA *rsa; |
| X509_NAME *name = NULL; |
| |
| g_return_if_fail (!priv->x509); |
| g_return_if_fail (!priv->private_key); |
| |
| priv->private_key = EVP_PKEY_new (); |
| |
| if (!priv->private_key) { |
| GST_WARNING_OBJECT (self, "failed to create private key"); |
| return; |
| } |
| |
| priv->x509 = X509_new (); |
| |
| if (!priv->x509) { |
| GST_WARNING_OBJECT (self, "failed to create certificate"); |
| EVP_PKEY_free (priv->private_key); |
| priv->private_key = NULL; |
| return; |
| } |
| |
| /* XXX: RSA_generate_key is actually deprecated in 0.9.8 */ |
| #if OPENSSL_VERSION_NUMBER < 0x10100001L |
| rsa = RSA_generate_key (2048, RSA_F4, NULL, NULL); |
| #else |
| rsa = RSA_new (); |
| if (rsa != NULL) { |
| BIGNUM *e = BN_new (); |
| if (e == NULL || !BN_set_word (e, RSA_F4) |
| || !RSA_generate_key_ex (rsa, 2048, e, NULL)) { |
| RSA_free (rsa); |
| rsa = NULL; |
| } |
| if (e) |
| BN_free (e); |
| } |
| #endif |
| |
| if (!rsa) { |
| GST_WARNING_OBJECT (self, "failed to generate RSA"); |
| EVP_PKEY_free (priv->private_key); |
| priv->private_key = NULL; |
| X509_free (priv->x509); |
| priv->x509 = NULL; |
| return; |
| } |
| |
| if (!EVP_PKEY_assign_RSA (priv->private_key, rsa)) { |
| GST_WARNING_OBJECT (self, "failed to assign RSA"); |
| RSA_free (rsa); |
| rsa = NULL; |
| EVP_PKEY_free (priv->private_key); |
| priv->private_key = NULL; |
| X509_free (priv->x509); |
| priv->x509 = NULL; |
| return; |
| } |
| rsa = NULL; |
| |
| X509_set_version (priv->x509, 2); |
| ASN1_INTEGER_set (X509_get_serialNumber (priv->x509), 0); |
| X509_gmtime_adj (X509_get_notBefore (priv->x509), 0); |
| X509_gmtime_adj (X509_get_notAfter (priv->x509), 31536000L); /* A year */ |
| X509_set_pubkey (priv->x509, priv->private_key); |
| |
| name = X509_get_subject_name (priv->x509); |
| X509_NAME_add_entry_by_txt (name, "C", MBSTRING_ASC, (unsigned char *) "SE", |
| -1, -1, 0); |
| X509_NAME_add_entry_by_txt (name, "CN", MBSTRING_ASC, |
| (unsigned char *) "OpenWebRTC", -1, -1, 0); |
| X509_set_issuer_name (priv->x509, name); |
| name = NULL; |
| |
| if (!X509_sign (priv->x509, priv->private_key, EVP_sha256 ())) { |
| GST_WARNING_OBJECT (self, "failed to sign certificate"); |
| EVP_PKEY_free (priv->private_key); |
| priv->private_key = NULL; |
| X509_free (priv->x509); |
| priv->x509 = NULL; |
| return; |
| } |
| |
| self->priv->pem = _gst_dtls_x509_to_pem (priv->x509); |
| } |
| |
| static void |
| init_from_pem_string (GstDtlsCertificate * self, const gchar * pem) |
| { |
| GstDtlsCertificatePrivate *priv = self->priv; |
| BIO *bio; |
| |
| g_return_if_fail (pem); |
| g_return_if_fail (!priv->x509); |
| g_return_if_fail (!priv->private_key); |
| |
| bio = BIO_new_mem_buf ((gpointer) pem, -1); |
| g_return_if_fail (bio); |
| |
| priv->x509 = PEM_read_bio_X509 (bio, NULL, NULL, NULL); |
| |
| if (!priv->x509) { |
| GST_WARNING_OBJECT (self, "failed to read certificate from pem string"); |
| return; |
| } |
| |
| (void) BIO_reset (bio); |
| |
| priv->private_key = PEM_read_bio_PrivateKey (bio, NULL, NULL, NULL); |
| |
| BIO_free (bio); |
| bio = NULL; |
| |
| if (!priv->private_key) { |
| GST_WARNING_OBJECT (self, "failed to read private key from pem string"); |
| X509_free (priv->x509); |
| priv->x509 = NULL; |
| return; |
| } |
| |
| self->priv->pem = g_strdup (pem); |
| } |
| |
| gchar * |
| _gst_dtls_x509_to_pem (gpointer x509) |
| { |
| #define GST_DTLS_BIO_BUFFER_SIZE 4096 |
| BIO *bio; |
| gchar buffer[GST_DTLS_BIO_BUFFER_SIZE] = { 0 }; |
| gint len; |
| gchar *pem = NULL; |
| |
| bio = BIO_new (BIO_s_mem ()); |
| g_return_val_if_fail (bio, NULL); |
| |
| if (!PEM_write_bio_X509 (bio, (X509 *) x509)) { |
| g_warn_if_reached (); |
| goto beach; |
| } |
| |
| len = BIO_read (bio, buffer, GST_DTLS_BIO_BUFFER_SIZE); |
| if (!len) { |
| g_warn_if_reached (); |
| goto beach; |
| } |
| |
| pem = g_strndup (buffer, len); |
| |
| beach: |
| BIO_free (bio); |
| |
| return pem; |
| } |
| |
| GstDtlsCertificateInternalCertificate |
| _gst_dtls_certificate_get_internal_certificate (GstDtlsCertificate * self) |
| { |
| g_return_val_if_fail (GST_IS_DTLS_CERTIFICATE (self), NULL); |
| return self->priv->x509; |
| } |
| |
| GstDtlsCertificateInternalKey |
| _gst_dtls_certificate_get_internal_key (GstDtlsCertificate * self) |
| { |
| g_return_val_if_fail (GST_IS_DTLS_CERTIFICATE (self), NULL); |
| return self->priv->private_key; |
| } |