| /* |
| * 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 "gstdtlsagent.h" |
| |
| #ifdef __APPLE__ |
| # define __AVAILABILITYMACROS__ |
| # define DEPRECATED_IN_MAC_OS_X_VERSION_10_7_AND_LATER |
| #endif |
| |
| #include <openssl/err.h> |
| #include <openssl/ssl.h> |
| |
| GST_DEBUG_CATEGORY_STATIC (gst_dtls_agent_debug); |
| #define GST_CAT_DEFAULT gst_dtls_agent_debug |
| |
| G_DEFINE_TYPE (GstDtlsAgent, gst_dtls_agent, G_TYPE_OBJECT); |
| |
| #define GST_DTLS_AGENT_GET_PRIVATE(obj) (G_TYPE_INSTANCE_GET_PRIVATE((obj), GST_TYPE_DTLS_AGENT, GstDtlsAgentPrivate)) |
| |
| enum |
| { |
| PROP_0, |
| PROP_CERTIFICATE, |
| NUM_PROPERTIES |
| }; |
| |
| static GParamSpec *properties[NUM_PROPERTIES]; |
| |
| struct _GstDtlsAgentPrivate |
| { |
| SSL_CTX *ssl_context; |
| |
| GstDtlsCertificate *certificate; |
| }; |
| |
| static void gst_dtls_agent_finalize (GObject * gobject); |
| static void gst_dtls_agent_set_property (GObject *, guint prop_id, |
| const GValue *, GParamSpec *); |
| const gchar *gst_dtls_agent_peek_id (GstDtlsAgent *); |
| |
| static GRWLock *ssl_locks; |
| |
| static void |
| ssl_locking_function (gint mode, gint lock_num, const gchar * file, gint line) |
| { |
| gboolean locking; |
| gboolean reading; |
| GRWLock *lock; |
| |
| locking = mode & CRYPTO_LOCK; |
| reading = mode & CRYPTO_READ; |
| lock = &ssl_locks[lock_num]; |
| |
| GST_LOG_OBJECT (NULL, "%s SSL lock for %s, thread=%p location=%s:%d", |
| locking ? "locking" : "unlocking", reading ? "reading" : "writing", |
| g_thread_self (), file, line); |
| |
| if (locking) { |
| if (reading) { |
| g_rw_lock_reader_lock (lock); |
| } else { |
| g_rw_lock_writer_lock (lock); |
| } |
| } else { |
| if (reading) { |
| g_rw_lock_reader_unlock (lock); |
| } else { |
| g_rw_lock_writer_unlock (lock); |
| } |
| } |
| } |
| |
| static gulong |
| ssl_thread_id_function (void) |
| { |
| return (gulong) g_thread_self (); |
| } |
| |
| void |
| _gst_dtls_init_openssl (void) |
| { |
| static gsize is_init = 0; |
| gint i; |
| gint num_locks; |
| |
| if (g_once_init_enter (&is_init)) { |
| GST_DEBUG_CATEGORY_INIT (gst_dtls_agent_debug, "dtlsagent", 0, |
| "DTLS Agent"); |
| |
| if (OPENSSL_VERSION_NUMBER < 0x1000100fL) { |
| GST_WARNING_OBJECT (NULL, |
| "Incorrect OpenSSL version, should be >= 1.0.1, is %s", |
| OPENSSL_VERSION_TEXT); |
| g_assert_not_reached (); |
| } |
| |
| GST_INFO_OBJECT (NULL, "initializing openssl %lx", OPENSSL_VERSION_NUMBER); |
| SSL_library_init (); |
| SSL_load_error_strings (); |
| ERR_load_BIO_strings (); |
| |
| num_locks = CRYPTO_num_locks (); |
| ssl_locks = g_new (GRWLock, num_locks); |
| for (i = 0; i < num_locks; ++i) { |
| g_rw_lock_init (&ssl_locks[i]); |
| } |
| CRYPTO_set_locking_callback (ssl_locking_function); |
| CRYPTO_set_id_callback (ssl_thread_id_function); |
| |
| g_once_init_leave (&is_init, 1); |
| } |
| } |
| |
| static void |
| gst_dtls_agent_class_init (GstDtlsAgentClass * klass) |
| { |
| GObjectClass *gobject_class = G_OBJECT_CLASS (klass); |
| |
| g_type_class_add_private (klass, sizeof (GstDtlsAgentPrivate)); |
| |
| gobject_class->set_property = gst_dtls_agent_set_property; |
| gobject_class->finalize = gst_dtls_agent_finalize; |
| |
| properties[PROP_CERTIFICATE] = |
| g_param_spec_object ("certificate", |
| "GstDtlsCertificate", |
| "Sets the certificate of the agent", |
| GST_TYPE_DTLS_CERTIFICATE, |
| G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS); |
| |
| g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties); |
| |
| _gst_dtls_init_openssl (); |
| } |
| |
| static void |
| gst_dtls_agent_init (GstDtlsAgent * self) |
| { |
| GstDtlsAgentPrivate *priv = GST_DTLS_AGENT_GET_PRIVATE (self); |
| self->priv = priv; |
| |
| ERR_clear_error (); |
| |
| priv->ssl_context = SSL_CTX_new (DTLSv1_method ()); |
| if (ERR_peek_error () || !priv->ssl_context) { |
| char buf[512]; |
| |
| priv->ssl_context = NULL; |
| |
| GST_WARNING_OBJECT (self, "Error creating SSL Context: %s", |
| ERR_error_string (ERR_get_error (), buf)); |
| |
| g_return_if_reached (); |
| } |
| |
| SSL_CTX_set_verify_depth (priv->ssl_context, 2); |
| SSL_CTX_set_tlsext_use_srtp (priv->ssl_context, "SRTP_AES128_CM_SHA1_80"); |
| SSL_CTX_set_cipher_list (priv->ssl_context, |
| "ALL:!ADH:!LOW:!EXP:!MD5:@STRENGTH"); |
| SSL_CTX_set_read_ahead (priv->ssl_context, 1); |
| #if OPENSSL_VERSION_NUMBER >= 0x1000200fL |
| SSL_CTX_set_ecdh_auto (priv->ssl_context, 1); |
| #endif |
| } |
| |
| static void |
| gst_dtls_agent_finalize (GObject * gobject) |
| { |
| GstDtlsAgentPrivate *priv = GST_DTLS_AGENT (gobject)->priv; |
| |
| SSL_CTX_free (priv->ssl_context); |
| priv->ssl_context = NULL; |
| |
| GST_DEBUG_OBJECT (gobject, "finalized"); |
| |
| G_OBJECT_CLASS (gst_dtls_agent_parent_class)->finalize (gobject); |
| } |
| |
| static void |
| gst_dtls_agent_set_property (GObject * object, guint prop_id, |
| const GValue * value, GParamSpec * pspec) |
| { |
| GstDtlsAgent *self = GST_DTLS_AGENT (object); |
| GstDtlsCertificate *certificate; |
| |
| switch (prop_id) { |
| case PROP_CERTIFICATE: |
| certificate = GST_DTLS_CERTIFICATE (g_value_get_object (value)); |
| g_return_if_fail (GST_IS_DTLS_CERTIFICATE (certificate)); |
| g_return_if_fail (self->priv->ssl_context); |
| |
| self->priv->certificate = certificate; |
| g_object_ref (certificate); |
| |
| if (!SSL_CTX_use_certificate (self->priv->ssl_context, |
| _gst_dtls_certificate_get_internal_certificate (certificate))) { |
| GST_WARNING_OBJECT (self, "could not use certificate"); |
| g_return_if_reached (); |
| } |
| |
| if (!SSL_CTX_use_PrivateKey (self->priv->ssl_context, |
| _gst_dtls_certificate_get_internal_key (certificate))) { |
| GST_WARNING_OBJECT (self, "could not use private key"); |
| g_return_if_reached (); |
| } |
| |
| if (!SSL_CTX_check_private_key (self->priv->ssl_context)) { |
| GST_WARNING_OBJECT (self, "invalid private key"); |
| g_return_if_reached (); |
| } |
| break; |
| default: |
| G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec); |
| } |
| } |
| |
| GstDtlsCertificate * |
| gst_dtls_agent_get_certificate (GstDtlsAgent * self) |
| { |
| g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL); |
| if (self->priv->certificate) { |
| g_object_ref (self->priv->certificate); |
| } |
| return self->priv->certificate; |
| } |
| |
| gchar * |
| gst_dtls_agent_get_certificate_pem (GstDtlsAgent * self) |
| { |
| gchar *pem; |
| g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL); |
| g_return_val_if_fail (GST_IS_DTLS_CERTIFICATE (self->priv->certificate), |
| NULL); |
| |
| g_object_get (self->priv->certificate, "pem", &pem, NULL); |
| |
| return pem; |
| } |
| |
| const GstDtlsAgentContext |
| _gst_dtls_agent_peek_context (GstDtlsAgent * self) |
| { |
| g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL); |
| return self->priv->ssl_context; |
| } |