gstreamer/subprojects/gst-plugins-bad/gst/rtmp2/gstrtmp2locationhandler.c
Nirbheek Chauhan 5da9f62313 rtsp+rtmp: Forward warning added to tls-validation-flags to our users
With the 2.72 release, glib-networking developers have decided that
TLS certificate validation cannot be implemented correctly by them, so
they've deprecated it.

In a nutshell: a cert can have several validation errors, but there
are no guarantees that the TLS backend will return all those errors,
and things are made even more complicated by the fact that the list of
errors might refer to certs that are added for backwards-compat and
won't actually be used by the TLS library.

Our best option is to ignore the deprecation and pass the warning onto
users so they can make an appropriate security decision regarding
this.

We can't deprecate the tls-validation-flags property because it is
very useful when connecting to RTSP cameras that will never get
updates to fix certificate errors.

Relevant upstream merge requests / issues:

https://gitlab.gnome.org/GNOME/glib/-/merge_requests/2214

https://gitlab.gnome.org/GNOME/glib-networking/-/issues/179

https://gitlab.gnome.org/GNOME/glib-networking/-/merge_requests/193

Part-of: <https://gitlab.freedesktop.org/gstreamer/gstreamer/-/merge_requests/2494>
2022-07-30 11:27:12 +00:00

300 lines
10 KiB
C

/* GStreamer
* Copyright (C) 2017 Make.TV, Inc. <info@make.tv>
* Contact: Jan Alexander Steffens (heftig) <jsteffens@make.tv>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public
* License along with this library; if not, write to the
* Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "gstrtmp2locationhandler.h"
#include "rtmp/rtmputils.h"
#include "rtmp/rtmpclient.h"
#include <string.h>
#define DEFAULT_SCHEME GST_RTMP_SCHEME_RTMP
#define DEFAULT_HOST "localhost"
#define DEFAULT_APPLICATION "live"
#define DEFAULT_STREAM "myStream"
#define DEFAULT_LOCATION "rtmp://" DEFAULT_HOST "/" DEFAULT_APPLICATION "/" DEFAULT_STREAM
#define DEFAULT_SECURE_TOKEN NULL
#define DEFAULT_USERNAME NULL
#define DEFAULT_PASSWORD NULL
#define DEFAULT_AUTHMOD GST_RTMP_AUTHMOD_AUTO
#define DEFAULT_TIMEOUT 5
#define DEFAULT_FLASH_VERSION "LNX 10,0,32,18"
G_DEFINE_INTERFACE (GstRtmpLocationHandler, gst_rtmp_location_handler, 0);
#define GST_CAT_DEFAULT gst_rtmp_location_handler_debug_category
GST_DEBUG_CATEGORY_STATIC (GST_CAT_DEFAULT);
static void
gst_rtmp_location_handler_default_init (GstRtmpLocationHandlerInterface * iface)
{
GST_DEBUG_CATEGORY_INIT (GST_CAT_DEFAULT, "rtmp2locationhandler", 0,
"RTMP2 Location Handling");
g_object_interface_install_property (iface, g_param_spec_string ("location",
"Location", "Location of RTMP stream to access", DEFAULT_LOCATION,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_enum ("scheme",
"Scheme", "RTMP connection scheme",
GST_TYPE_RTMP_SCHEME, DEFAULT_SCHEME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("host",
"Host", "RTMP server host name", DEFAULT_HOST,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_int ("port", "Port",
"RTMP server port", 1, 65535,
gst_rtmp_scheme_get_default_port (DEFAULT_SCHEME),
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface,
g_param_spec_string ("application", "Application",
"RTMP application path", DEFAULT_APPLICATION,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("stream",
"Stream", "RTMP stream path", DEFAULT_STREAM,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("username",
"User name", "RTMP authorization user name", DEFAULT_USERNAME,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_string ("password",
"Password", "RTMP authorization password", DEFAULT_PASSWORD,
G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface,
g_param_spec_string ("secure-token", "Secure token",
"RTMP authorization token", DEFAULT_SECURE_TOKEN,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_enum ("authmod",
"Authorization mode", "RTMP authorization mode",
GST_TYPE_RTMP_AUTHMOD, DEFAULT_AUTHMOD,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface, g_param_spec_uint ("timeout",
"Timeout", "RTMP timeout in seconds", 0, G_MAXUINT, DEFAULT_TIMEOUT,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
/**
* GstRtmpLocationHandler::tls-validation-flags:
*
* TLS certificate validation flags used to validate server
* certificate.
*
* GLib guarantees that if certificate verification fails, at least one
* error will be set, but it does not guarantee that all possible errors
* will be set. Accordingly, you may not safely decide to ignore any
* particular type of error.
*
* For example, it would be incorrect to mask %G_TLS_CERTIFICATE_EXPIRED if
* you want to allow expired certificates, because this could potentially be
* the only error flag set even if other problems exist with the
* certificate.
*/
g_object_interface_install_property (iface,
g_param_spec_flags ("tls-validation-flags", "TLS validation flags",
"TLS validation flags to use", G_TYPE_TLS_CERTIFICATE_FLAGS,
G_TLS_CERTIFICATE_VALIDATE_ALL,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_interface_install_property (iface,
g_param_spec_string ("flash-version", "Flash version",
"Flash version reported to the server", DEFAULT_FLASH_VERSION,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
}
static GstURIType
uri_handler_get_type_sink (GType type)
{
return GST_URI_SINK;
}
static GstURIType
uri_handler_get_type_src (GType type)
{
return GST_URI_SRC;
}
static const gchar *const *
uri_handler_get_protocols (GType type)
{
return gst_rtmp_scheme_get_strings ();
}
static gchar *
uri_handler_get_uri (GstURIHandler * handler)
{
GstRtmpLocation location = { 0, };
gchar *string;
g_object_get (handler, "scheme", &location.scheme, "host", &location.host,
"port", &location.port, "application", &location.application,
"stream", &location.stream, NULL);
string = gst_rtmp_location_get_string (&location, TRUE);
gst_rtmp_location_clear (&location);
return string;
}
static gboolean
uri_handler_set_uri (GstURIHandler * handler, const gchar * string,
GError ** error)
{
GstRtmpLocationHandler *self = GST_RTMP_LOCATION_HANDLER (handler);
GstUri *uri;
const gchar *scheme_sep, *path_sep, *stream_sep, *host, *userinfo;
GstRtmpScheme scheme;
guint port;
gboolean ret = FALSE;
GST_DEBUG_OBJECT (self, "setting URI from %s", GST_STR_NULL (string));
g_return_val_if_fail (string, FALSE);
scheme_sep = strstr (string, "://");
if (!scheme_sep) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks scheme: %s", string);
return FALSE;
}
path_sep = strchr (scheme_sep + 3, '/');
if (!path_sep) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks path: %s", string);
return FALSE;
}
stream_sep = strrchr (path_sep + 1, '/');
if (!stream_sep) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks stream: %s", string);
return FALSE;
}
{
gchar *string_without_path = g_strndup (string, path_sep - string);
uri = gst_uri_from_string_escaped (string_without_path);
g_free (string_without_path);
}
if (!uri) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_URI,
"URI failed to parse: %s", string);
return FALSE;
}
gst_uri_normalize (uri);
scheme = gst_rtmp_scheme_from_uri (uri);
if (scheme < 0) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI has bad scheme: %s", string);
goto out;
}
host = gst_uri_get_host (uri);
if (!host) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"URI lacks hostname: %s", string);
goto out;
}
port = gst_uri_get_port (uri);
if (port == GST_URI_NO_PORT) {
port = gst_rtmp_scheme_get_default_port (scheme);
}
{
const gchar *path = path_sep + 1, *stream = stream_sep + 1;
gchar *application = g_strndup (path, stream_sep - path);
GST_DEBUG_OBJECT (self, "setting location to %s://%s:%u/%s stream %s",
gst_rtmp_scheme_to_string (scheme), host, port, application, stream);
g_object_set (self, "scheme", scheme, "host", host, "port", port,
"application", application, "stream", stream, "username", NULL,
"password", NULL, NULL);
g_free (application);
}
userinfo = gst_uri_get_userinfo (uri);
if (userinfo) {
gchar *user, *pass;
gchar **split = g_strsplit (userinfo, ":", 2);
if (!split || !split[0] || !split[1]) {
g_set_error (error, GST_URI_ERROR, GST_URI_ERROR_BAD_REFERENCE,
"Failed to parse username:password data");
g_strfreev (split);
goto out;
}
if (g_strrstr (split[1], ":") != NULL)
GST_WARNING_OBJECT (self, "userinfo %s contains more than one ':', will "
"assume that the first ':' delineates user:pass. You should escape "
"the user and pass before adding to the URI.", userinfo);
user = g_uri_unescape_string (split[0], NULL);
pass = g_uri_unescape_string (split[1], NULL);
g_strfreev (split);
g_object_set (self, "username", user, "password", pass, NULL);
g_free (user);
g_free (pass);
}
ret = TRUE;
out:
gst_uri_unref (uri);
return ret;
}
void
gst_rtmp_location_handler_implement_uri_handler (GstURIHandlerInterface * iface,
GstURIType type)
{
switch (type) {
case GST_URI_SINK:
iface->get_type = uri_handler_get_type_sink;
break;
case GST_URI_SRC:
iface->get_type = uri_handler_get_type_src;
break;
default:
g_return_if_reached ();
}
iface->get_protocols = uri_handler_get_protocols;
iface->get_uri = uri_handler_get_uri;
iface->set_uri = uri_handler_set_uri;
}
gboolean
gst_rtmp_location_handler_set_uri (GstRtmpLocationHandler * handler,
const gchar * uri)
{
GError *error = NULL;
gboolean ret;
g_return_val_if_fail (GST_IS_RTMP_LOCATION_HANDLER (handler), FALSE);
ret = gst_uri_handler_set_uri (GST_URI_HANDLER (handler), uri, &error);
if (!ret) {
GST_ERROR_OBJECT (handler, "Failed to set URI: %s", error->message);
g_object_set (handler, "scheme", DEFAULT_SCHEME, "host", NULL,
"port", gst_rtmp_scheme_get_default_port (DEFAULT_SCHEME),
"application", NULL, "stream", NULL, NULL);
g_error_free (error);
}
return ret;
}