/* * 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 #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 #ifdef X509_NAME #undef X509_NAME #endif #endif #include GST_DEBUG_CATEGORY_STATIC (gst_dtls_certificate_debug); #define GST_CAT_DEFAULT gst_dtls_certificate_debug 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; }; G_DEFINE_TYPE_WITH_CODE (GstDtlsCertificate, gst_dtls_certificate, G_TYPE_OBJECT, G_ADD_PRIVATE (GstDtlsCertificate) GST_DEBUG_CATEGORY_INIT (gst_dtls_certificate_debug, "dtlscertificate", 0, "DTLS Certificate")); 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); 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; self->priv = priv = gst_dtls_certificate_get_instance_private (self); 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; }