gstreamer/ext/dtls/gstdtlsagent.c
Andreas Frisch 51f0307900 dtls: Properly display all errors/warnings from ERR queue
Print out all errors from the OpenSSL error queue instead of just
looking at the topmost error. Using the callback interface also removes
the need for formatting using a buffer on the stack.
2018-11-06 16:23:50 +00:00

286 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
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 ();
}
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);
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 (ERR_peek_error () || !priv->ssl_context) {
priv->ssl_context = NULL;
GST_WARNING_OBJECT (self, "Error creating SSL Context");
ERR_print_errors_cb (ssl_warn_cb, self);
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;
}