mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-12-25 17:50:36 +00:00
1a43d57359
For pre-1.1.x openssl, a callback to set the thread id needs to be provided to openssl. In 0.9.x the thread id was an unsigned long. In 1.0.x it was expanded to be able to hold a void*. Here we change to use the 1.0.x API so that the thread id can always hold a GThread*, even on platforms like msvc x64 where unsigned long is only 32 bits. All of this is still #ifdef'd out of existence when building with openssl 1.1.x or later which changed the thread API again, and does not need a thread id callback. https://bugzilla.gnome.org/show_bug.cgi?id=775292
284 lines
7.9 KiB
C
284 lines
7.9 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
|
|
|
|
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 *);
|
|
|
|
#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 ();
|
|
}
|
|
|
|
GST_INFO_OBJECT (NULL, "initializing openssl %lx", OPENSSL_VERSION_NUMBER);
|
|
SSL_library_init ();
|
|
SSL_load_error_strings ();
|
|
ERR_load_BIO_strings ();
|
|
|
|
#if OPENSSL_VERSION_NUMBER < 0x10100000L
|
|
{
|
|
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);
|
|
|
|
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 ();
|
|
|
|
#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
|
|
priv->ssl_context = SSL_CTX_new (DTLS_method ());
|
|
#else
|
|
priv->ssl_context = SSL_CTX_new (DTLSv1_method ());
|
|
#endif
|
|
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;
|
|
}
|