gstreamer/ext/dtls/gstdtlsagent.c

289 lines
8.1 KiB
C
Raw Normal View History

/*
* 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>
2015-03-16 16:48:43 +00:00
GST_DEBUG_CATEGORY_STATIC (gst_dtls_agent_debug);
#define GST_CAT_DEFAULT gst_dtls_agent_debug
2015-03-16 16:34:05 +00:00
enum
{
PROP_0,
PROP_CERTIFICATE,
NUM_PROPERTIES
};
static GParamSpec *properties[NUM_PROPERTIES];
2015-03-16 16:48:43 +00:00
struct _GstDtlsAgentPrivate
2015-03-16 16:34:05 +00:00
{
SSL_CTX *ssl_context;
2015-03-16 16:48:43 +00:00
GstDtlsCertificate *certificate;
};
G_DEFINE_TYPE_WITH_PRIVATE (GstDtlsAgent, gst_dtls_agent, G_TYPE_OBJECT);
2015-03-16 16:48:43 +00:00
static void gst_dtls_agent_finalize (GObject * gobject);
static void gst_dtls_agent_set_property (GObject *, guint prop_id,
2015-03-16 16:34:05 +00:00
const GValue *, GParamSpec *);
2015-03-16 16:48:43 +00:00
const gchar *gst_dtls_agent_peek_id (GstDtlsAgent *);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
static GRWLock *ssl_locks;
2015-03-16 16:34:05 +00:00
static void
ssl_locking_function (gint mode, gint lock_num, const gchar * file, gint line)
{
2015-03-16 16:34:05 +00:00
gboolean locking;
gboolean reading;
GRWLock *lock;
locking = mode & CRYPTO_LOCK;
reading = mode & CRYPTO_READ;
lock = &ssl_locks[lock_num];
2016-10-28 20:39:13 +00:00
GST_TRACE_OBJECT (NULL, "%s SSL lock for %s, thread=%p location=%s:%d",
2015-03-16 16:34:05 +00:00
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 {
2015-03-16 16:34:05 +00:00
g_rw_lock_writer_unlock (lock);
}
2015-03-16 16:34:05 +00:00
}
}
static void
ssl_thread_id_function (CRYPTO_THREADID * id)
{
CRYPTO_THREADID_set_pointer (id, g_thread_self ());
}
#endif
2015-03-16 16:34:05 +00:00
void
_gst_dtls_init_openssl (void)
{
2015-03-16 16:34:05 +00:00
static gsize is_init = 0;
if (g_once_init_enter (&is_init)) {
GST_DEBUG_CATEGORY_INIT (gst_dtls_agent_debug, "dtlsagent", 0,
"DTLS Agent");
2015-03-16 16:34:05 +00:00
if (OPENSSL_VERSION_NUMBER < 0x1000100fL) {
GST_WARNING_OBJECT (NULL,
"Incorrect OpenSSL version, should be >= 1.0.1, is %s",
2015-03-16 16:34:05 +00:00
OPENSSL_VERSION_TEXT);
g_assert_not_reached ();
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
GST_INFO_OBJECT (NULL, "initializing openssl %lx", OPENSSL_VERSION_NUMBER);
2015-03-16 16:34:05 +00:00
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
2015-03-16 16:34:05 +00:00
g_once_init_leave (&is_init, 1);
}
}
2015-03-16 16:34:05 +00:00
static void
2015-03-16 16:48:43 +00:00
gst_dtls_agent_class_init (GstDtlsAgentClass * klass)
{
2015-03-16 16:34:05 +00:00
GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
2015-03-16 16:48:43 +00:00
gobject_class->set_property = gst_dtls_agent_set_property;
gobject_class->finalize = gst_dtls_agent_finalize;
2015-03-16 16:34:05 +00:00
properties[PROP_CERTIFICATE] =
g_param_spec_object ("certificate",
2015-03-16 16:48:43 +00:00
"GstDtlsCertificate",
2015-03-16 16:34:05 +00:00
"Sets the certificate of the agent",
2015-03-16 16:48:43 +00:00
GST_TYPE_DTLS_CERTIFICATE,
2015-03-16 16:34:05 +00:00
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
2015-03-16 16:34:05 +00:00
g_object_class_install_properties (gobject_class, NUM_PROPERTIES, properties);
2015-03-16 16:48:43 +00:00
_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;
}
2015-03-16 16:34:05 +00:00
static void
2015-03-16 16:48:43 +00:00
gst_dtls_agent_init (GstDtlsAgent * self)
{
GstDtlsAgentPrivate *priv = gst_dtls_agent_get_instance_private (self);
2015-03-16 16:34:05 +00:00
self->priv = priv;
2015-03-16 16:34:05 +00:00
ERR_clear_error ();
#if OPENSSL_VERSION_NUMBER >= 0x1000200fL
priv->ssl_context = SSL_CTX_new (DTLS_method ());
#else
2015-03-16 16:34:05 +00:00
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);
2015-03-16 16:34:05 +00:00
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 ();
}
2015-03-16 16:34:05 +00:00
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)
2015-03-16 16:34:05 +00:00
SSL_CTX_set_ecdh_auto (priv->ssl_context, 1);
#endif
}
2015-03-16 16:34:05 +00:00
static void
2015-03-16 16:48:43 +00:00
gst_dtls_agent_finalize (GObject * gobject)
{
2015-03-16 16:48:43 +00:00
GstDtlsAgentPrivate *priv = GST_DTLS_AGENT (gobject)->priv;
2015-03-16 16:34:05 +00:00
SSL_CTX_free (priv->ssl_context);
priv->ssl_context = NULL;
GST_DEBUG_OBJECT (gobject, "finalized");
2015-03-16 16:48:43 +00:00
G_OBJECT_CLASS (gst_dtls_agent_parent_class)->finalize (gobject);
}
2015-03-16 16:34:05 +00:00
static void
2015-03-16 16:48:43 +00:00
gst_dtls_agent_set_property (GObject * object, guint prop_id,
2015-03-16 16:34:05 +00:00
const GValue * value, GParamSpec * pspec)
{
2015-03-16 16:48:43 +00:00
GstDtlsAgent *self = GST_DTLS_AGENT (object);
GstDtlsCertificate *certificate;
2015-03-16 16:34:05 +00:00
switch (prop_id) {
case PROP_CERTIFICATE:
2015-03-16 16:48:43 +00:00
certificate = GST_DTLS_CERTIFICATE (g_value_get_object (value));
g_return_if_fail (GST_IS_DTLS_CERTIFICATE (certificate));
2015-03-16 16:34:05 +00:00
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,
2015-03-16 16:48:43 +00:00
_gst_dtls_certificate_get_internal_certificate (certificate))) {
GST_WARNING_OBJECT (self, "could not use certificate");
2015-03-16 16:34:05 +00:00
g_return_if_reached ();
}
if (!SSL_CTX_use_PrivateKey (self->priv->ssl_context,
2015-03-16 16:48:43 +00:00
_gst_dtls_certificate_get_internal_key (certificate))) {
GST_WARNING_OBJECT (self, "could not use private key");
2015-03-16 16:34:05 +00:00
g_return_if_reached ();
}
if (!SSL_CTX_check_private_key (self->priv->ssl_context)) {
GST_WARNING_OBJECT (self, "invalid private key");
2015-03-16 16:34:05 +00:00
g_return_if_reached ();
}
break;
default:
2015-03-16 16:34:05 +00:00
G_OBJECT_WARN_INVALID_PROPERTY_ID (self, prop_id, pspec);
}
}
2015-03-16 16:48:43 +00:00
GstDtlsCertificate *
gst_dtls_agent_get_certificate (GstDtlsAgent * self)
{
2015-03-16 16:48:43 +00:00
g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL);
2015-03-16 16:34:05 +00:00
if (self->priv->certificate) {
g_object_ref (self->priv->certificate);
}
return self->priv->certificate;
}
2015-03-16 16:34:05 +00:00
gchar *
2015-03-16 16:48:43 +00:00
gst_dtls_agent_get_certificate_pem (GstDtlsAgent * self)
{
2015-03-16 16:34:05 +00:00
gchar *pem;
2015-03-16 16:48:43 +00:00
g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL);
g_return_val_if_fail (GST_IS_DTLS_CERTIFICATE (self->priv->certificate),
NULL);
2015-03-16 16:34:05 +00:00
g_object_get (self->priv->certificate, "pem", &pem, NULL);
2015-03-16 16:34:05 +00:00
return pem;
}
2015-03-16 16:48:43 +00:00
const GstDtlsAgentContext
_gst_dtls_agent_peek_context (GstDtlsAgent * self)
{
2015-03-16 16:48:43 +00:00
g_return_val_if_fail (GST_IS_DTLS_AGENT (self), NULL);
2015-03-16 16:34:05 +00:00
return self->priv->ssl_context;
}