mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2024-11-26 19:51:11 +00:00
rtsp-auth: Add support for Digest authentication
https://bugzilla.gnome.org/show_bug.cgi?id=774416
This commit is contained in:
parent
d7676bfba3
commit
927a44c55b
5 changed files with 531 additions and 23 deletions
|
@ -1,5 +1,5 @@
|
||||||
noinst_PROGRAMS = test-video test-ogg test-mp4 test-readme \
|
noinst_PROGRAMS = test-video test-ogg test-mp4 test-readme \
|
||||||
test-launch test-sdp test-uri test-auth \
|
test-launch test-sdp test-uri test-auth test-auth-digest \
|
||||||
test-multicast test-multicast2 test-appsrc \
|
test-multicast test-multicast2 test-appsrc \
|
||||||
test-video-rtx test-record test-record-auth \
|
test-video-rtx test-record test-record-auth \
|
||||||
test-netclock test-netclock-client
|
test-netclock test-netclock-client
|
||||||
|
|
184
examples/test-auth-digest.c
Normal file
184
examples/test-auth-digest.c
Normal file
|
@ -0,0 +1,184 @@
|
||||||
|
/* GStreamer
|
||||||
|
* Copyright (C) 2008 Wim Taymans <wim.taymans at gmail.com>
|
||||||
|
*
|
||||||
|
* 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 <gst/gst.h>
|
||||||
|
|
||||||
|
#include <gst/rtsp-server/rtsp-server.h>
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
remove_func (GstRTSPSessionPool * pool, GstRTSPSession * session,
|
||||||
|
GstRTSPServer * server)
|
||||||
|
{
|
||||||
|
return GST_RTSP_FILTER_REMOVE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
remove_sessions (GstRTSPServer * server)
|
||||||
|
{
|
||||||
|
GstRTSPSessionPool *pool;
|
||||||
|
|
||||||
|
g_print ("removing all sessions\n");
|
||||||
|
pool = gst_rtsp_server_get_session_pool (server);
|
||||||
|
gst_rtsp_session_pool_filter (pool,
|
||||||
|
(GstRTSPSessionPoolFilterFunc) remove_func, server);
|
||||||
|
g_object_unref (pool);
|
||||||
|
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
timeout (GstRTSPServer * server)
|
||||||
|
{
|
||||||
|
GstRTSPSessionPool *pool;
|
||||||
|
|
||||||
|
pool = gst_rtsp_server_get_session_pool (server);
|
||||||
|
gst_rtsp_session_pool_cleanup (pool);
|
||||||
|
g_object_unref (pool);
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
main (int argc, char *argv[])
|
||||||
|
{
|
||||||
|
GMainLoop *loop;
|
||||||
|
GstRTSPServer *server;
|
||||||
|
GstRTSPMountPoints *mounts;
|
||||||
|
GstRTSPMediaFactory *factory;
|
||||||
|
GstRTSPAuth *auth;
|
||||||
|
GstRTSPToken *token;
|
||||||
|
|
||||||
|
gst_init (&argc, &argv);
|
||||||
|
|
||||||
|
loop = g_main_loop_new (NULL, FALSE);
|
||||||
|
|
||||||
|
/* create a server instance */
|
||||||
|
server = gst_rtsp_server_new ();
|
||||||
|
|
||||||
|
/* get the mounts for this server, every server has a default mapper object
|
||||||
|
* that be used to map uri mount points to media factories */
|
||||||
|
mounts = gst_rtsp_server_get_mount_points (server);
|
||||||
|
|
||||||
|
|
||||||
|
/* make a media factory for a test stream. The default media factory can use
|
||||||
|
* gst-launch syntax to create pipelines.
|
||||||
|
* any launch line works as long as it contains elements named pay%d. Each
|
||||||
|
* element with pay%d names will be a stream */
|
||||||
|
factory = gst_rtsp_media_factory_new ();
|
||||||
|
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||||
|
"videotestsrc ! video/x-raw,width=352,height=288,framerate=15/1 ! "
|
||||||
|
"x264enc ! rtph264pay name=pay0 pt=96 "
|
||||||
|
"audiotestsrc ! audio/x-raw,rate=8000 ! "
|
||||||
|
"alawenc ! rtppcmapay name=pay1 pt=97 " ")");
|
||||||
|
/* attach the test factory to the /test url */
|
||||||
|
gst_rtsp_mount_points_add_factory (mounts, "/test", factory);
|
||||||
|
|
||||||
|
/* allow user and admin to access this resource */
|
||||||
|
gst_rtsp_media_factory_add_role (factory, "user",
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||||
|
gst_rtsp_media_factory_add_role (factory, "admin",
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||||
|
/* admin2 can look at the media but not construct so he gets a
|
||||||
|
* 401 Unauthorized */
|
||||||
|
gst_rtsp_media_factory_add_role (factory, "admin2",
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, FALSE, NULL);
|
||||||
|
/* Anonymous user can do the same things as admin2 on this resource */
|
||||||
|
gst_rtsp_media_factory_add_role (factory, "anonymous",
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, FALSE, NULL);
|
||||||
|
|
||||||
|
/* make another factory */
|
||||||
|
factory = gst_rtsp_media_factory_new ();
|
||||||
|
gst_rtsp_media_factory_set_launch (factory, "( "
|
||||||
|
"videotestsrc ! video/x-raw,width=352,height=288,framerate=30/1 ! "
|
||||||
|
"x264enc ! rtph264pay name=pay0 pt=96 )");
|
||||||
|
/* attach the test factory to the /test url */
|
||||||
|
gst_rtsp_mount_points_add_factory (mounts, "/test2", factory);
|
||||||
|
|
||||||
|
/* allow admin2 to access this resource */
|
||||||
|
/* user and admin have no permissions so they can't even see the
|
||||||
|
* media and get a 404 Not Found */
|
||||||
|
gst_rtsp_media_factory_add_role (factory, "admin2",
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_ACCESS, G_TYPE_BOOLEAN, TRUE,
|
||||||
|
GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT, G_TYPE_BOOLEAN, TRUE, NULL);
|
||||||
|
|
||||||
|
/* don't need the ref to the mapper anymore */
|
||||||
|
g_object_unref (mounts);
|
||||||
|
|
||||||
|
/* make a new authentication manager */
|
||||||
|
auth = gst_rtsp_auth_new ();
|
||||||
|
gst_rtsp_auth_set_supported_methods (auth, GST_RTSP_AUTH_DIGEST);
|
||||||
|
|
||||||
|
/* make default token, it has no permissions */
|
||||||
|
token =
|
||||||
|
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||||
|
"anonymous", NULL);
|
||||||
|
gst_rtsp_auth_set_default_token (auth, token);
|
||||||
|
gst_rtsp_token_unref (token);
|
||||||
|
|
||||||
|
/* make user token */
|
||||||
|
token =
|
||||||
|
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||||
|
"user", NULL);
|
||||||
|
gst_rtsp_auth_add_digest (auth, "user", "password", token);
|
||||||
|
gst_rtsp_token_unref (token);
|
||||||
|
|
||||||
|
/* make admin token */
|
||||||
|
token =
|
||||||
|
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||||
|
"admin", NULL);
|
||||||
|
gst_rtsp_auth_add_digest (auth, "admin", "power", token);
|
||||||
|
gst_rtsp_token_unref (token);
|
||||||
|
|
||||||
|
/* make admin2 token */
|
||||||
|
token =
|
||||||
|
gst_rtsp_token_new (GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE, G_TYPE_STRING,
|
||||||
|
"admin2", NULL);
|
||||||
|
gst_rtsp_auth_add_digest (auth, "admin2", "power2", token);
|
||||||
|
gst_rtsp_token_unref (token);
|
||||||
|
|
||||||
|
/* set as the server authentication manager */
|
||||||
|
gst_rtsp_server_set_auth (server, auth);
|
||||||
|
g_object_unref (auth);
|
||||||
|
|
||||||
|
/* attach the server to the default maincontext */
|
||||||
|
if (gst_rtsp_server_attach (server, NULL) == 0)
|
||||||
|
goto failed;
|
||||||
|
|
||||||
|
g_timeout_add_seconds (2, (GSourceFunc) timeout, server);
|
||||||
|
g_timeout_add_seconds (10, (GSourceFunc) remove_sessions, server);
|
||||||
|
|
||||||
|
/* start serving */
|
||||||
|
g_print ("stream with user:password ready at rtsp://127.0.0.1:8554/test\n");
|
||||||
|
g_print ("stream with admin:power ready at rtsp://127.0.0.1:8554/test\n");
|
||||||
|
g_print ("stream with admin2:power2 ready at rtsp://127.0.0.1:8554/test2\n");
|
||||||
|
g_main_loop_run (loop);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* ERRORS */
|
||||||
|
failed:
|
||||||
|
{
|
||||||
|
g_print ("failed to attach the server\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
|
@ -62,10 +62,43 @@ struct _GstRTSPAuthPrivate
|
||||||
GTlsDatabase *database;
|
GTlsDatabase *database;
|
||||||
GTlsAuthenticationMode mode;
|
GTlsAuthenticationMode mode;
|
||||||
GHashTable *basic; /* protected by lock */
|
GHashTable *basic; /* protected by lock */
|
||||||
|
GHashTable *digest, *nonces; /* protected by lock */
|
||||||
|
guint64 last_nonce_check;
|
||||||
GstRTSPToken *default_token;
|
GstRTSPToken *default_token;
|
||||||
GstRTSPMethod methods;
|
GstRTSPMethod methods;
|
||||||
|
GstRTSPAuthMethod auth_methods;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GstRTSPToken *token;
|
||||||
|
gchar *pass;
|
||||||
|
} GstRTSPDigestEntry;
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
gchar *nonce;
|
||||||
|
gchar *ip;
|
||||||
|
guint64 timestamp;
|
||||||
|
gpointer client;
|
||||||
|
} GstRTSPDigestNonce;
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_rtsp_digest_entry_free (GstRTSPDigestEntry * entry)
|
||||||
|
{
|
||||||
|
gst_rtsp_token_unref (entry->token);
|
||||||
|
g_free (entry->pass);
|
||||||
|
g_free (entry);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
gst_rtsp_digest_nonce_free (GstRTSPDigestNonce * nonce)
|
||||||
|
{
|
||||||
|
g_free (nonce->nonce);
|
||||||
|
g_free (nonce->ip);
|
||||||
|
g_free (nonce);
|
||||||
|
}
|
||||||
|
|
||||||
enum
|
enum
|
||||||
{
|
{
|
||||||
PROP_0,
|
PROP_0,
|
||||||
|
@ -92,6 +125,9 @@ static void gst_rtsp_auth_finalize (GObject * obj);
|
||||||
static gboolean default_authenticate (GstRTSPAuth * auth, GstRTSPContext * ctx);
|
static gboolean default_authenticate (GstRTSPAuth * auth, GstRTSPContext * ctx);
|
||||||
static gboolean default_check (GstRTSPAuth * auth, GstRTSPContext * ctx,
|
static gboolean default_check (GstRTSPAuth * auth, GstRTSPContext * ctx,
|
||||||
const gchar * check);
|
const gchar * check);
|
||||||
|
static void default_generate_authenticate_header (GstRTSPAuth * auth,
|
||||||
|
GstRTSPContext * ctx);
|
||||||
|
|
||||||
|
|
||||||
G_DEFINE_TYPE (GstRTSPAuth, gst_rtsp_auth, G_TYPE_OBJECT);
|
G_DEFINE_TYPE (GstRTSPAuth, gst_rtsp_auth, G_TYPE_OBJECT);
|
||||||
|
|
||||||
|
@ -110,6 +146,7 @@ gst_rtsp_auth_class_init (GstRTSPAuthClass * klass)
|
||||||
|
|
||||||
klass->authenticate = default_authenticate;
|
klass->authenticate = default_authenticate;
|
||||||
klass->check = default_check;
|
klass->check = default_check;
|
||||||
|
klass->generate_authenticate_header = default_generate_authenticate_header;
|
||||||
|
|
||||||
GST_DEBUG_CATEGORY_INIT (rtsp_auth_debug, "rtspauth", 0, "GstRTSPAuth");
|
GST_DEBUG_CATEGORY_INIT (rtsp_auth_debug, "rtspauth", 0, "GstRTSPAuth");
|
||||||
|
|
||||||
|
@ -150,9 +187,14 @@ gst_rtsp_auth_init (GstRTSPAuth * auth)
|
||||||
|
|
||||||
priv->basic = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
priv->basic = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
||||||
(GDestroyNotify) gst_rtsp_token_unref);
|
(GDestroyNotify) gst_rtsp_token_unref);
|
||||||
|
priv->digest = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
||||||
|
(GDestroyNotify) gst_rtsp_digest_entry_free);
|
||||||
|
priv->nonces = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
|
||||||
|
(GDestroyNotify) gst_rtsp_digest_nonce_free);
|
||||||
|
|
||||||
/* bitwise or of all methods that need authentication */
|
/* bitwise or of all methods that need authentication */
|
||||||
priv->methods = 0;
|
priv->methods = 0;
|
||||||
|
priv->auth_methods = GST_RTSP_AUTH_BASIC;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
@ -168,6 +210,8 @@ gst_rtsp_auth_finalize (GObject * obj)
|
||||||
if (priv->database)
|
if (priv->database)
|
||||||
g_object_unref (priv->database);
|
g_object_unref (priv->database);
|
||||||
g_hash_table_unref (priv->basic);
|
g_hash_table_unref (priv->basic);
|
||||||
|
g_hash_table_unref (priv->digest);
|
||||||
|
g_hash_table_unref (priv->nonces);
|
||||||
g_mutex_clear (&priv->lock);
|
g_mutex_clear (&priv->lock);
|
||||||
|
|
||||||
G_OBJECT_CLASS (gst_rtsp_auth_parent_class)->finalize (obj);
|
G_OBJECT_CLASS (gst_rtsp_auth_parent_class)->finalize (obj);
|
||||||
|
@ -469,8 +513,7 @@ gst_rtsp_auth_add_basic (GstRTSPAuth * auth, const gchar * basic,
|
||||||
* @auth: a #GstRTSPAuth
|
* @auth: a #GstRTSPAuth
|
||||||
* @basic: (transfer none): the basic token
|
* @basic: (transfer none): the basic token
|
||||||
*
|
*
|
||||||
* Add a basic token for the default authentication algorithm that
|
* Removes @basic authentication token.
|
||||||
* enables the client with privileges from @authgroup.
|
|
||||||
*/
|
*/
|
||||||
void
|
void
|
||||||
gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
|
gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
|
||||||
|
@ -487,12 +530,216 @@ gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
|
||||||
g_mutex_unlock (&priv->lock);
|
g_mutex_unlock (&priv->lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_rtsp_auth_add_digest:
|
||||||
|
* @auth: a #GstRTSPAuth
|
||||||
|
* @user: the digest user name
|
||||||
|
* @pass: the digest password
|
||||||
|
* @token: (transfer none): authorisation token
|
||||||
|
*
|
||||||
|
* Add a digest @user and @pass for the default authentication algorithm that
|
||||||
|
* enables the client with privileges listed in @token.
|
||||||
|
*
|
||||||
|
* Since: 1.12
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_rtsp_auth_add_digest (GstRTSPAuth * auth, const gchar * user,
|
||||||
|
const gchar * pass, GstRTSPToken * token)
|
||||||
|
{
|
||||||
|
GstRTSPAuthPrivate *priv;
|
||||||
|
GstRTSPDigestEntry *entry;
|
||||||
|
|
||||||
|
g_return_if_fail (GST_IS_RTSP_AUTH (auth));
|
||||||
|
g_return_if_fail (user != NULL);
|
||||||
|
g_return_if_fail (pass != NULL);
|
||||||
|
g_return_if_fail (GST_IS_RTSP_TOKEN (token));
|
||||||
|
|
||||||
|
priv = auth->priv;
|
||||||
|
|
||||||
|
entry = g_new0 (GstRTSPDigestEntry, 1);
|
||||||
|
entry->token = gst_rtsp_token_ref (token);
|
||||||
|
entry->pass = g_strdup (pass);
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->lock);
|
||||||
|
g_hash_table_replace (priv->digest, g_strdup (user), entry);
|
||||||
|
g_mutex_unlock (&priv->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_rtsp_auth_remove_digest:
|
||||||
|
* @auth: a #GstRTSPAuth
|
||||||
|
* @user: (transfer none): the digest user name
|
||||||
|
*
|
||||||
|
* Removes a digest user.
|
||||||
|
*
|
||||||
|
* Since: 1.12
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_rtsp_auth_remove_digest (GstRTSPAuth * auth, const gchar * user)
|
||||||
|
{
|
||||||
|
GstRTSPAuthPrivate *priv;
|
||||||
|
|
||||||
|
g_return_if_fail (GST_IS_RTSP_AUTH (auth));
|
||||||
|
g_return_if_fail (user != NULL);
|
||||||
|
|
||||||
|
priv = auth->priv;
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->lock);
|
||||||
|
g_hash_table_remove (priv->digest, user);
|
||||||
|
g_mutex_unlock (&priv->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_rtsp_auth_set_supported_methods:
|
||||||
|
* @auth: a #GstRTSPAuth
|
||||||
|
* @methods: supported methods
|
||||||
|
*
|
||||||
|
* Sets the supported authentication @methods for @auth.
|
||||||
|
*
|
||||||
|
* Since: 1.12
|
||||||
|
*/
|
||||||
|
void
|
||||||
|
gst_rtsp_auth_set_supported_methods (GstRTSPAuth * auth,
|
||||||
|
GstRTSPAuthMethod methods)
|
||||||
|
{
|
||||||
|
GstRTSPAuthPrivate *priv;
|
||||||
|
|
||||||
|
g_return_if_fail (GST_IS_RTSP_AUTH (auth));
|
||||||
|
|
||||||
|
priv = auth->priv;
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->lock);
|
||||||
|
priv->auth_methods = methods;
|
||||||
|
g_mutex_unlock (&priv->lock);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* gst_rtsp_auth_get_supported_methods:
|
||||||
|
* @auth: a #GstRTSPAuth
|
||||||
|
*
|
||||||
|
* Gets the supported authentication methods of @auth.
|
||||||
|
*
|
||||||
|
* Returns: The supported authentication methods
|
||||||
|
*
|
||||||
|
* Since: 1.12
|
||||||
|
*/
|
||||||
|
GstRTSPAuthMethod
|
||||||
|
gst_rtsp_auth_get_supported_methods (GstRTSPAuth * auth)
|
||||||
|
{
|
||||||
|
GstRTSPAuthPrivate *priv;
|
||||||
|
GstRTSPAuthMethod methods;
|
||||||
|
|
||||||
|
g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), 0);
|
||||||
|
|
||||||
|
priv = auth->priv;
|
||||||
|
|
||||||
|
g_mutex_lock (&priv->lock);
|
||||||
|
methods = priv->auth_methods;
|
||||||
|
g_mutex_unlock (&priv->lock);
|
||||||
|
|
||||||
|
return methods;
|
||||||
|
}
|
||||||
|
|
||||||
|
typedef struct
|
||||||
|
{
|
||||||
|
GstRTSPAuth *auth;
|
||||||
|
GstRTSPDigestNonce *nonce;
|
||||||
|
} RemoveNonceData;
|
||||||
|
|
||||||
|
static void
|
||||||
|
remove_nonce (gpointer data, GObject * object)
|
||||||
|
{
|
||||||
|
RemoveNonceData *remove_nonce_data = data;
|
||||||
|
|
||||||
|
g_mutex_lock (&remove_nonce_data->auth->priv->lock);
|
||||||
|
g_hash_table_remove (remove_nonce_data->auth->priv->nonces,
|
||||||
|
remove_nonce_data->nonce->nonce);
|
||||||
|
g_mutex_unlock (&remove_nonce_data->auth->priv->lock);
|
||||||
|
|
||||||
|
g_object_unref (remove_nonce_data->auth);
|
||||||
|
g_free (remove_nonce_data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static gboolean
|
||||||
|
default_digest_auth (GstRTSPAuth * auth, GstRTSPContext * ctx,
|
||||||
|
GstRTSPAuthParam ** param)
|
||||||
|
{
|
||||||
|
const gchar *realm = NULL, *user = NULL, *nonce = NULL;
|
||||||
|
const gchar *response = NULL, *uri = NULL;
|
||||||
|
GstRTSPDigestNonce *nonce_entry = NULL;
|
||||||
|
GstRTSPDigestEntry *digest_entry;
|
||||||
|
gchar *expected_response = NULL;
|
||||||
|
gboolean ret = FALSE;
|
||||||
|
|
||||||
|
GST_DEBUG_OBJECT (auth, "check Digest auth");
|
||||||
|
|
||||||
|
if (!param)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
while (*param) {
|
||||||
|
if (!realm && strcmp ((*param)->name, "realm") == 0 && (*param)->value)
|
||||||
|
realm = (*param)->value;
|
||||||
|
else if (!user && strcmp ((*param)->name, "username") == 0
|
||||||
|
&& (*param)->value)
|
||||||
|
user = (*param)->value;
|
||||||
|
else if (!nonce && strcmp ((*param)->name, "nonce") == 0 && (*param)->value)
|
||||||
|
nonce = (*param)->value;
|
||||||
|
else if (!response && strcmp ((*param)->name, "response") == 0
|
||||||
|
&& (*param)->value)
|
||||||
|
response = (*param)->value;
|
||||||
|
else if (!uri && strcmp ((*param)->name, "uri") == 0 && (*param)->value)
|
||||||
|
uri = (*param)->value;
|
||||||
|
|
||||||
|
param++;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!realm || !user || !nonce || !response || !uri)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
g_mutex_lock (&auth->priv->lock);
|
||||||
|
digest_entry = g_hash_table_lookup (auth->priv->digest, user);
|
||||||
|
if (!digest_entry)
|
||||||
|
goto out;
|
||||||
|
nonce_entry = g_hash_table_lookup (auth->priv->nonces, nonce);
|
||||||
|
if (!nonce_entry)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
if (strcmp (nonce_entry->ip, gst_rtsp_connection_get_ip (ctx->conn)) != 0)
|
||||||
|
goto out;
|
||||||
|
if (nonce_entry->client && nonce_entry->client != ctx->client)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
expected_response =
|
||||||
|
gst_rtsp_generate_digest_auth_response (NULL,
|
||||||
|
gst_rtsp_method_as_text (ctx->method), "GStreamer RTSP Server", user,
|
||||||
|
digest_entry->pass, uri, nonce);
|
||||||
|
if (!expected_response || strcmp (response, expected_response) != 0)
|
||||||
|
goto out;
|
||||||
|
|
||||||
|
ctx->token = digest_entry->token;
|
||||||
|
ret = TRUE;
|
||||||
|
|
||||||
|
out:
|
||||||
|
if (nonce_entry && !nonce_entry->client) {
|
||||||
|
RemoveNonceData *remove_nonce_data = g_new (RemoveNonceData, 1);
|
||||||
|
|
||||||
|
nonce_entry->client = ctx->client;
|
||||||
|
remove_nonce_data->nonce = nonce_entry;
|
||||||
|
remove_nonce_data->auth = g_object_ref (auth);
|
||||||
|
g_object_weak_ref (G_OBJECT (ctx->client), remove_nonce, remove_nonce_data);
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&auth->priv->lock);
|
||||||
|
|
||||||
|
g_free (expected_response);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
default_authenticate (GstRTSPAuth * auth, GstRTSPContext * ctx)
|
default_authenticate (GstRTSPAuth * auth, GstRTSPContext * ctx)
|
||||||
{
|
{
|
||||||
GstRTSPAuthPrivate *priv = auth->priv;
|
GstRTSPAuthPrivate *priv = auth->priv;
|
||||||
GstRTSPResult res;
|
GstRTSPAuthCredential **credentials, **credential;
|
||||||
gchar *authorization;
|
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (auth, "authenticate");
|
GST_DEBUG_OBJECT (auth, "authenticate");
|
||||||
|
|
||||||
|
@ -502,27 +749,38 @@ default_authenticate (GstRTSPAuth * auth, GstRTSPContext * ctx)
|
||||||
ctx->token = priv->default_token;
|
ctx->token = priv->default_token;
|
||||||
g_mutex_unlock (&priv->lock);
|
g_mutex_unlock (&priv->lock);
|
||||||
|
|
||||||
res =
|
credentials =
|
||||||
gst_rtsp_message_get_header (ctx->request, GST_RTSP_HDR_AUTHORIZATION,
|
gst_rtsp_message_parse_auth_credentials (ctx->request,
|
||||||
&authorization, 0);
|
GST_RTSP_HDR_AUTHORIZATION);
|
||||||
if (res < 0)
|
if (!credentials)
|
||||||
goto no_auth;
|
goto no_auth;
|
||||||
|
|
||||||
/* parse type */
|
/* parse type */
|
||||||
if (g_ascii_strncasecmp (authorization, "basic ", 6) == 0) {
|
credential = credentials;
|
||||||
GstRTSPToken *token;
|
while (*credential) {
|
||||||
|
if ((*credential)->scheme == GST_RTSP_AUTH_BASIC) {
|
||||||
|
GstRTSPToken *token;
|
||||||
|
|
||||||
GST_DEBUG_OBJECT (auth, "check Basic auth");
|
GST_DEBUG_OBJECT (auth, "check Basic auth");
|
||||||
g_mutex_lock (&priv->lock);
|
g_mutex_lock (&priv->lock);
|
||||||
if ((token = g_hash_table_lookup (priv->basic, &authorization[6]))) {
|
if ((token =
|
||||||
GST_DEBUG_OBJECT (auth, "setting token %p", token);
|
g_hash_table_lookup (priv->basic,
|
||||||
ctx->token = token;
|
(*credential)->authorization))) {
|
||||||
|
GST_DEBUG_OBJECT (auth, "setting token %p", token);
|
||||||
|
ctx->token = token;
|
||||||
|
g_mutex_unlock (&priv->lock);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
g_mutex_unlock (&priv->lock);
|
||||||
|
} else if ((*credential)->scheme == GST_RTSP_AUTH_DIGEST) {
|
||||||
|
if (default_digest_auth (auth, ctx, (*credential)->params))
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
g_mutex_unlock (&priv->lock);
|
|
||||||
} else if (g_ascii_strncasecmp (authorization, "digest ", 7) == 0) {
|
credential++;
|
||||||
GST_DEBUG_OBJECT (auth, "check Digest auth");
|
|
||||||
/* not implemented yet */
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gst_rtsp_auth_credentials_free (credentials);
|
||||||
return TRUE;
|
return TRUE;
|
||||||
|
|
||||||
no_auth:
|
no_auth:
|
||||||
|
@ -532,6 +790,57 @@ no_auth:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
default_generate_authenticate_header (GstRTSPAuth * auth, GstRTSPContext * ctx)
|
||||||
|
{
|
||||||
|
if (auth->priv->auth_methods & GST_RTSP_AUTH_BASIC) {
|
||||||
|
gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
|
||||||
|
"Basic realm=\"GStreamer RTSP Server\"");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (auth->priv->auth_methods & GST_RTSP_AUTH_DIGEST) {
|
||||||
|
GstRTSPDigestNonce *nonce;
|
||||||
|
gchar *nonce_value, *auth_header;
|
||||||
|
|
||||||
|
nonce_value =
|
||||||
|
g_strdup_printf ("%08x%08x", g_random_int (), g_random_int ());
|
||||||
|
|
||||||
|
auth_header =
|
||||||
|
g_strdup_printf
|
||||||
|
("Digest realm=\"GStreamer RTSP Server\", nonce=\"%s\"", nonce_value);
|
||||||
|
gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
|
||||||
|
auth_header);
|
||||||
|
g_free (auth_header);
|
||||||
|
|
||||||
|
nonce = g_new0 (GstRTSPDigestNonce, 1);
|
||||||
|
nonce->nonce = g_strdup (nonce_value);
|
||||||
|
nonce->timestamp = g_get_monotonic_time ();
|
||||||
|
nonce->ip = g_strdup (gst_rtsp_connection_get_ip (ctx->conn));
|
||||||
|
g_mutex_lock (&auth->priv->lock);
|
||||||
|
g_hash_table_replace (auth->priv->nonces, nonce_value, nonce);
|
||||||
|
|
||||||
|
if (auth->priv->last_nonce_check == 0)
|
||||||
|
auth->priv->last_nonce_check = nonce->timestamp;
|
||||||
|
|
||||||
|
/* 30 second nonce timeout */
|
||||||
|
if (nonce->timestamp - auth->priv->last_nonce_check >= 30 * G_USEC_PER_SEC) {
|
||||||
|
GHashTableIter iter;
|
||||||
|
gpointer key, value;
|
||||||
|
|
||||||
|
g_hash_table_iter_init (&iter, auth->priv->nonces);
|
||||||
|
while (g_hash_table_iter_next (&iter, &key, &value)) {
|
||||||
|
GstRTSPDigestNonce *tmp = value;
|
||||||
|
|
||||||
|
if (nonce->timestamp - tmp->timestamp >= 30 * G_USEC_PER_SEC)
|
||||||
|
g_hash_table_iter_remove (&iter);
|
||||||
|
}
|
||||||
|
auth->priv->last_nonce_check = nonce->timestamp;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_mutex_unlock (&auth->priv->lock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
send_response (GstRTSPAuth * auth, GstRTSPStatusCode code, GstRTSPContext * ctx)
|
send_response (GstRTSPAuth * auth, GstRTSPStatusCode code, GstRTSPContext * ctx)
|
||||||
{
|
{
|
||||||
|
@ -539,9 +848,12 @@ send_response (GstRTSPAuth * auth, GstRTSPStatusCode code, GstRTSPContext * ctx)
|
||||||
gst_rtsp_status_as_text (code), ctx->request);
|
gst_rtsp_status_as_text (code), ctx->request);
|
||||||
|
|
||||||
if (code == GST_RTSP_STS_UNAUTHORIZED) {
|
if (code == GST_RTSP_STS_UNAUTHORIZED) {
|
||||||
/* we only have Basic for now */
|
GstRTSPAuthClass *klass;
|
||||||
gst_rtsp_message_add_header (ctx->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
|
|
||||||
"Basic realm=\"GStreamer RTSP Server\"");
|
klass = GST_RTSP_AUTH_GET_CLASS (auth);
|
||||||
|
|
||||||
|
if (klass->generate_authenticate_header)
|
||||||
|
klass->generate_authenticate_header (auth, ctx);
|
||||||
}
|
}
|
||||||
gst_rtsp_client_send_message (ctx->client, ctx->session, ctx->response);
|
gst_rtsp_client_send_message (ctx->client, ctx->session, ctx->response);
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,6 +72,7 @@ struct _GstRTSPAuthClass {
|
||||||
gboolean (*authenticate) (GstRTSPAuth *auth, GstRTSPContext *ctx);
|
gboolean (*authenticate) (GstRTSPAuth *auth, GstRTSPContext *ctx);
|
||||||
gboolean (*check) (GstRTSPAuth *auth, GstRTSPContext *ctx,
|
gboolean (*check) (GstRTSPAuth *auth, GstRTSPContext *ctx,
|
||||||
const gchar *check);
|
const gchar *check);
|
||||||
|
void (*generate_authenticate_header) (GstRTSPAuth *auth, GstRTSPContext *ctx);
|
||||||
gboolean (*accept_certificate) (GstRTSPAuth *auth,
|
gboolean (*accept_certificate) (GstRTSPAuth *auth,
|
||||||
GTlsConnection *connection,
|
GTlsConnection *connection,
|
||||||
GTlsCertificate *peer_cert,
|
GTlsCertificate *peer_cert,
|
||||||
|
@ -100,6 +101,13 @@ void gst_rtsp_auth_add_basic (GstRTSPAuth *auth, const gc
|
||||||
GstRTSPToken *token);
|
GstRTSPToken *token);
|
||||||
void gst_rtsp_auth_remove_basic (GstRTSPAuth *auth, const gchar * basic);
|
void gst_rtsp_auth_remove_basic (GstRTSPAuth *auth, const gchar * basic);
|
||||||
|
|
||||||
|
void gst_rtsp_auth_add_digest (GstRTSPAuth *auth, const gchar *user,
|
||||||
|
const gchar *pass, GstRTSPToken *token);
|
||||||
|
void gst_rtsp_auth_remove_digest (GstRTSPAuth *auth, const gchar *user);
|
||||||
|
|
||||||
|
void gst_rtsp_auth_set_supported_methods (GstRTSPAuth *auth, GstRTSPAuthMethod methods);
|
||||||
|
GstRTSPAuthMethod gst_rtsp_auth_get_supported_methods (GstRTSPAuth *auth);
|
||||||
|
|
||||||
gboolean gst_rtsp_auth_check (const gchar *check);
|
gboolean gst_rtsp_auth_check (const gchar *check);
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,8 +11,10 @@ EXPORTS
|
||||||
gst_rtsp_address_pool_new
|
gst_rtsp_address_pool_new
|
||||||
gst_rtsp_address_pool_reserve_address
|
gst_rtsp_address_pool_reserve_address
|
||||||
gst_rtsp_auth_add_basic
|
gst_rtsp_auth_add_basic
|
||||||
|
gst_rtsp_auth_add_digest
|
||||||
gst_rtsp_auth_check
|
gst_rtsp_auth_check
|
||||||
gst_rtsp_auth_get_default_token
|
gst_rtsp_auth_get_default_token
|
||||||
|
gst_rtsp_auth_get_supported_methods
|
||||||
gst_rtsp_auth_get_tls_authentication_mode
|
gst_rtsp_auth_get_tls_authentication_mode
|
||||||
gst_rtsp_auth_get_tls_certificate
|
gst_rtsp_auth_get_tls_certificate
|
||||||
gst_rtsp_auth_get_tls_database
|
gst_rtsp_auth_get_tls_database
|
||||||
|
@ -20,7 +22,9 @@ EXPORTS
|
||||||
gst_rtsp_auth_make_basic
|
gst_rtsp_auth_make_basic
|
||||||
gst_rtsp_auth_new
|
gst_rtsp_auth_new
|
||||||
gst_rtsp_auth_remove_basic
|
gst_rtsp_auth_remove_basic
|
||||||
|
gst_rtsp_auth_remove_digest
|
||||||
gst_rtsp_auth_set_default_token
|
gst_rtsp_auth_set_default_token
|
||||||
|
gst_rtsp_auth_set_supported_methods
|
||||||
gst_rtsp_auth_set_tls_authentication_mode
|
gst_rtsp_auth_set_tls_authentication_mode
|
||||||
gst_rtsp_auth_set_tls_certificate
|
gst_rtsp_auth_set_tls_certificate
|
||||||
gst_rtsp_auth_set_tls_database
|
gst_rtsp_auth_set_tls_database
|
||||||
|
|
Loading…
Reference in a new issue