mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-03 16:09:39 +00:00
288 lines
8.1 KiB
C
288 lines
8.1 KiB
C
/*
|
|
* 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
|
|
|
|
enum
|
|
{
|
|
PROP_0,
|
|
PROP_CERTIFICATE,
|
|
NUM_PROPERTIES
|
|
};
|
|
|
|
static GParamSpec *properties[NUM_PROPERTIES];
|
|
|
|
struct _GstDtlsAgentPrivate
|
|
{
|
|
SSL_CTX *ssl_context;
|
|
|
|
GstDtlsCertificate *certificate;
|
|
};
|
|
|
|
G_DEFINE_TYPE_WITH_PRIVATE (GstDtlsAgent, gst_dtls_agent, G_TYPE_OBJECT);
|
|
|
|
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 *);
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
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_TRACE_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 void
|
|
ssl_thread_id_function (CRYPTO_THREADID * id)
|
|
{
|
|
CRYPTO_THREADID_set_pointer (id, g_thread_self ());
|
|
}
|
|
#endif
|
|
|
|
void
|
|
_gst_dtls_init_openssl (void)
|
|
{
|
|
static gsize is_init = 0;
|
|
|
|
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 ();
|
|
}
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
GST_INFO_OBJECT (NULL, "initializing openssl %lx", OPENSSL_VERSION_NUMBER);
|
|
SSL_library_init ();
|
|
SSL_load_error_strings ();
|
|
ERR_load_BIO_strings ();
|
|
|
|
if (!CRYPTO_get_locking_callback ()) {
|
|
gint i;
|
|
gint num_locks;
|
|
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_THREADID_set_callback (ssl_thread_id_function);
|
|
}
|
|
#endif
|
|
|
|
g_once_init_leave (&is_init, 1);
|
|
}
|
|
}
|
|
|
|
static void
|
|
gst_dtls_agent_class_init (GstDtlsAgentClass * klass)
|
|
{
|
|
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
|
|
|
|
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 int
|
|
ssl_warn_cb (const char *str, size_t len, void *u)
|
|
{
|
|
GstDtlsAgent *self = u;
|
|
GST_WARNING_OBJECT (self, "ssl error: %s", str);
|
|
return 0;
|
|
}
|
|
|
|
static void
|
|
gst_dtls_agent_init (GstDtlsAgent * self)
|
|
{
|
|
GstDtlsAgentPrivate *priv = gst_dtls_agent_get_instance_private (self);
|
|
self->priv = priv;
|
|
|
|
ERR_clear_error ();
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
|
|
priv->ssl_context = SSL_CTX_new (DTLS_method ());
|
|
#else
|
|
priv->ssl_context = SSL_CTX_new (DTLSv1_method ());
|
|
#endif
|
|
if (!priv->ssl_context) {
|
|
GST_WARNING_OBJECT (self, "Error creating SSL Context");
|
|
ERR_print_errors_cb (ssl_warn_cb, self);
|
|
|
|
g_return_if_reached ();
|
|
}
|
|
/* If any non-fatal issues happened, print them out and carry on */
|
|
if (ERR_peek_error ()) {
|
|
ERR_print_errors_cb (ssl_warn_cb, self);
|
|
ERR_clear_error ();
|
|
}
|
|
|
|
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) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
|
|
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;
|
|
}
|