da575eed2ed01e7910c56dcc5f7304459b3d61b1
[platform/upstream/gstreamer.git] / gst / rtsp-server / rtsp-auth.c
1 /* GStreamer
2  * Copyright (C) 2010 Wim Taymans <wim.taymans at gmail.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 /**
20  * SECTION:rtsp-auth
21  * @short_description: Authentication and authorization
22  * @see_also: #GstRTSPPermission, #GstRTSPtoken
23  *
24  * The #GstRTSPAuth object is responsible for checking if the current user is
25  * allowed to perform requested actions. The default implementation has some
26  * reasonable checks but subclasses can implement custom security policies.
27  *
28  * A new auth object is made with gst_rtsp_auth_new(). It is usually configured
29  * on the #GstRTSPServer object.
30  *
31  * The RTSP server will call gst_rtsp_auth_check() with a string describing the
32  * check to perform. The possible checks are prefixed with
33  * #GST_RTSP_AUTH_CHECK_*. Depending on the check, the default implementation
34  * will use the current #GstRTSPToken, #GstRTSPClientState and
35  * #GstRTSPPermissions on the object to check if an operation is allowed.
36  *
37  * The default #GstRTSPAuth object has support for basic authentication. With
38  * gst_rtsp_auth_add_basic() you can add a basic authentication string together
39  * with the #GstRTSPToken that will become active when successfully
40  * authenticated.
41  *
42  * When a TLS certificate has been set with gst_rtsp_auth_set_tls_certificate(),
43  * the default auth object will require the client to connect with a TLS
44  * connection.
45  *
46  * Last reviewed on 2013-07-16 (1.0.0)
47  */
48
49 #include <string.h>
50
51 #include "rtsp-auth.h"
52
53 #define GST_RTSP_AUTH_GET_PRIVATE(obj)  \
54    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_AUTH, GstRTSPAuthPrivate))
55
56 struct _GstRTSPAuthPrivate
57 {
58   GMutex lock;
59
60   /* the TLS certificate */
61   GTlsCertificate *certificate;
62   GHashTable *basic;            /* protected by lock */
63   GstRTSPMethod methods;
64 };
65
66 enum
67 {
68   PROP_0,
69   PROP_LAST
70 };
71
72 GST_DEBUG_CATEGORY_STATIC (rtsp_auth_debug);
73 #define GST_CAT_DEFAULT rtsp_auth_debug
74
75 static void gst_rtsp_auth_get_property (GObject * object, guint propid,
76     GValue * value, GParamSpec * pspec);
77 static void gst_rtsp_auth_set_property (GObject * object, guint propid,
78     const GValue * value, GParamSpec * pspec);
79 static void gst_rtsp_auth_finalize (GObject * obj);
80
81 static gboolean default_authenticate (GstRTSPAuth * auth,
82     GstRTSPClientState * state);
83 static gboolean default_check (GstRTSPAuth * auth, GstRTSPClientState * state,
84     const gchar * check);
85
86 G_DEFINE_TYPE (GstRTSPAuth, gst_rtsp_auth, G_TYPE_OBJECT);
87
88 static void
89 gst_rtsp_auth_class_init (GstRTSPAuthClass * klass)
90 {
91   GObjectClass *gobject_class;
92
93   g_type_class_add_private (klass, sizeof (GstRTSPAuthPrivate));
94
95   gobject_class = G_OBJECT_CLASS (klass);
96
97   gobject_class->get_property = gst_rtsp_auth_get_property;
98   gobject_class->set_property = gst_rtsp_auth_set_property;
99   gobject_class->finalize = gst_rtsp_auth_finalize;
100
101   klass->authenticate = default_authenticate;
102   klass->check = default_check;
103
104   GST_DEBUG_CATEGORY_INIT (rtsp_auth_debug, "rtspauth", 0, "GstRTSPAuth");
105 }
106
107 static void
108 gst_rtsp_auth_init (GstRTSPAuth * auth)
109 {
110   GstRTSPAuthPrivate *priv;
111
112   auth->priv = priv = GST_RTSP_AUTH_GET_PRIVATE (auth);
113
114   g_mutex_init (&priv->lock);
115
116   priv->basic = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
117       (GDestroyNotify) gst_rtsp_token_unref);
118
119   /* bitwise or of all methods that need authentication */
120   priv->methods = 0;
121 }
122
123 static void
124 gst_rtsp_auth_finalize (GObject * obj)
125 {
126   GstRTSPAuth *auth = GST_RTSP_AUTH (obj);
127   GstRTSPAuthPrivate *priv = auth->priv;
128
129   GST_INFO ("finalize auth %p", auth);
130
131   if (priv->certificate)
132     g_object_unref (priv->certificate);
133   g_hash_table_unref (priv->basic);
134   g_mutex_clear (&priv->lock);
135
136   G_OBJECT_CLASS (gst_rtsp_auth_parent_class)->finalize (obj);
137 }
138
139 static void
140 gst_rtsp_auth_get_property (GObject * object, guint propid,
141     GValue * value, GParamSpec * pspec)
142 {
143   switch (propid) {
144     default:
145       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
146   }
147 }
148
149 static void
150 gst_rtsp_auth_set_property (GObject * object, guint propid,
151     const GValue * value, GParamSpec * pspec)
152 {
153   switch (propid) {
154     default:
155       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
156   }
157 }
158
159 /**
160  * gst_rtsp_auth_new:
161  *
162  * Create a new #GstRTSPAuth instance.
163  *
164  * Returns: a new #GstRTSPAuth
165  */
166 GstRTSPAuth *
167 gst_rtsp_auth_new (void)
168 {
169   GstRTSPAuth *result;
170
171   result = g_object_new (GST_TYPE_RTSP_AUTH, NULL);
172
173   return result;
174 }
175
176 /**
177  * gst_rtsp_auth_set_tls_certificate:
178  * @auth: a #GstRTSPAuth
179  * @cert: (allow none): a #GTlsCertificate
180  *
181  * Set the TLS certificate for the auth. Client connections will only
182  * be accepted when TLS is negotiated.
183  */
184 void
185 gst_rtsp_auth_set_tls_certificate (GstRTSPAuth * auth, GTlsCertificate * cert)
186 {
187   GstRTSPAuthPrivate *priv;
188   GTlsCertificate *old;
189
190   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
191
192   priv = auth->priv;
193
194   if (cert)
195     g_object_ref (cert);
196
197   g_mutex_lock (&priv->lock);
198   old = priv->certificate;
199   priv->certificate = cert;
200   g_mutex_unlock (&priv->lock);
201
202   if (old)
203     g_object_unref (old);
204 }
205
206 /**
207  * gst_rtsp_auth_get_tls_certificate:
208  * @auth: a #GstRTSPAuth
209  *
210  * Get the #GTlsCertificate used for negotiating TLS @auth.
211  *
212  * Returns: (transfer full): the #GTlsCertificate of @auth. g_object_unref() after
213  * usage.
214  */
215 GTlsCertificate *
216 gst_rtsp_auth_get_tls_certificate (GstRTSPAuth * auth)
217 {
218   GstRTSPAuthPrivate *priv;
219   GTlsCertificate *result;
220
221   g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), NULL);
222
223   priv = auth->priv;
224
225   g_mutex_lock (&priv->lock);
226   if ((result = priv->certificate))
227     g_object_ref (result);
228   g_mutex_unlock (&priv->lock);
229
230   return result;
231 }
232
233
234 /**
235  * gst_rtsp_auth_add_basic:
236  * @auth: a #GstRTSPAuth
237  * @basic: the basic token
238  * @token: authorisation token
239  *
240  * Add a basic token for the default authentication algorithm that
241  * enables the client with privileges listed in @token.
242  */
243 void
244 gst_rtsp_auth_add_basic (GstRTSPAuth * auth, const gchar * basic,
245     GstRTSPToken * token)
246 {
247   GstRTSPAuthPrivate *priv;
248
249   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
250   g_return_if_fail (basic != NULL);
251   g_return_if_fail (GST_IS_RTSP_TOKEN (token));
252
253   priv = auth->priv;
254
255   g_mutex_lock (&priv->lock);
256   g_hash_table_replace (priv->basic, g_strdup (basic),
257       gst_rtsp_token_ref (token));
258   g_mutex_unlock (&priv->lock);
259 }
260
261 /**
262  * gst_rtsp_auth_remove_basic:
263  * @auth: a #GstRTSPAuth
264  * @basic: (transfer none): the basic token
265  *
266  * Add a basic token for the default authentication algorithm that
267  * enables the client qith privileges from @authgroup.
268  */
269 void
270 gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
271 {
272   GstRTSPAuthPrivate *priv;
273
274   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
275   g_return_if_fail (basic != NULL);
276
277   priv = auth->priv;
278
279   g_mutex_lock (&priv->lock);
280   g_hash_table_remove (priv->basic, basic);
281   g_mutex_unlock (&priv->lock);
282 }
283
284 static gboolean
285 default_authenticate (GstRTSPAuth * auth, GstRTSPClientState * state)
286 {
287   GstRTSPAuthPrivate *priv = auth->priv;
288   GstRTSPResult res;
289   gchar *authorization;
290
291   GST_DEBUG_OBJECT (auth, "authenticate");
292
293   res =
294       gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_AUTHORIZATION,
295       &authorization, 0);
296   if (res < 0)
297     goto no_auth;
298
299   /* parse type */
300   if (g_ascii_strncasecmp (authorization, "basic ", 6) == 0) {
301     GstRTSPToken *token;
302
303     GST_DEBUG_OBJECT (auth, "check Basic auth");
304     g_mutex_lock (&priv->lock);
305     if ((token = g_hash_table_lookup (priv->basic, &authorization[6]))) {
306       GST_DEBUG_OBJECT (auth, "setting token %p", token);
307       state->token = token;
308     }
309     g_mutex_unlock (&priv->lock);
310   } else if (g_ascii_strncasecmp (authorization, "digest ", 7) == 0) {
311     GST_DEBUG_OBJECT (auth, "check Digest auth");
312     /* not implemented yet */
313   }
314   return TRUE;
315
316 no_auth:
317   {
318     GST_DEBUG_OBJECT (auth, "no authorization header found");
319     return TRUE;
320   }
321 }
322
323 static void
324 send_response (GstRTSPAuth * auth, GstRTSPStatusCode code,
325     GstRTSPClientState * state)
326 {
327   gst_rtsp_message_init_response (state->response, code,
328       gst_rtsp_status_as_text (code), state->request);
329
330   if (code == GST_RTSP_STS_UNAUTHORIZED) {
331     /* we only have Basic for now */
332     gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
333         "Basic realm=\"GStreamer RTSP Server\"");
334   }
335   gst_rtsp_client_send_message (state->client, state->session, state->response);
336 }
337
338 static gboolean
339 ensure_authenticated (GstRTSPAuth * auth, GstRTSPClientState * state)
340 {
341   GstRTSPAuthClass *klass;
342
343   klass = GST_RTSP_AUTH_GET_CLASS (auth);
344
345   /* we need a token to check */
346   if (state->token == NULL) {
347     if (klass->authenticate) {
348       if (!klass->authenticate (auth, state))
349         goto authenticate_failed;
350     }
351   }
352   if (state->token == NULL)
353     goto no_auth;
354
355   return TRUE;
356
357 /* ERRORS */
358 authenticate_failed:
359   {
360     GST_DEBUG_OBJECT (auth, "authenticate failed");
361     send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
362     return FALSE;
363   }
364 no_auth:
365   {
366     GST_DEBUG_OBJECT (auth, "no authorization token found");
367     send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
368     return FALSE;
369   }
370 }
371
372 /* new connection */
373 static gboolean
374 check_connect (GstRTSPAuth * auth, GstRTSPClientState * state,
375     const gchar * check)
376 {
377   GstRTSPAuthPrivate *priv = auth->priv;
378
379   if (priv->certificate) {
380     GTlsConnection *tls;
381
382     /* configure the connection */
383     tls = gst_rtsp_connection_get_tls (state->conn, NULL);
384     g_tls_connection_set_certificate (tls, priv->certificate);
385   }
386   return TRUE;
387 }
388
389 /* check url and methods */
390 static gboolean
391 check_url (GstRTSPAuth * auth, GstRTSPClientState * state, const gchar * check)
392 {
393   GstRTSPAuthPrivate *priv = auth->priv;
394
395   if ((state->method & priv->methods) != 0)
396     if (!ensure_authenticated (auth, state))
397       goto not_authenticated;
398
399   return TRUE;
400
401   /* ERRORS */
402 not_authenticated:
403   {
404     return FALSE;
405   }
406 }
407
408 /* check access to media factory */
409 static gboolean
410 check_factory (GstRTSPAuth * auth, GstRTSPClientState * state,
411     const gchar * check)
412 {
413   const gchar *role;
414   GstRTSPPermissions *perms;
415
416   if (!ensure_authenticated (auth, state))
417     return FALSE;
418
419   if (!(role = gst_rtsp_token_get_string (state->token,
420               GST_RTSP_TOKEN_MEDIA_FACTORY_ROLE)))
421     goto no_media_role;
422   if (!(perms = gst_rtsp_media_factory_get_permissions (state->factory)))
423     goto no_permissions;
424
425   if (g_str_equal (check, "auth.check.media.factory.access")) {
426     if (!gst_rtsp_permissions_is_allowed (perms, role,
427             GST_RTSP_PERM_MEDIA_FACTORY_ACCESS))
428       goto no_access;
429   } else if (g_str_equal (check, "auth.check.media.factory.construct")) {
430     if (!gst_rtsp_permissions_is_allowed (perms, role,
431             GST_RTSP_PERM_MEDIA_FACTORY_CONSTRUCT))
432       goto no_construct;
433   }
434   return TRUE;
435
436   /* ERRORS */
437 no_media_role:
438   {
439     GST_DEBUG_OBJECT (auth, "no media factory role found");
440     send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
441     return FALSE;
442   }
443 no_permissions:
444   {
445     GST_DEBUG_OBJECT (auth, "no permissions on media factory found");
446     send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
447     return FALSE;
448   }
449 no_access:
450   {
451     GST_DEBUG_OBJECT (auth, "no permissions to access media factory");
452     send_response (auth, GST_RTSP_STS_NOT_FOUND, state);
453     return FALSE;
454   }
455 no_construct:
456   {
457     GST_DEBUG_OBJECT (auth, "no permissions to construct media factory");
458     send_response (auth, GST_RTSP_STS_UNAUTHORIZED, state);
459     return FALSE;
460   }
461 }
462
463 static gboolean
464 check_client_settings (GstRTSPAuth * auth, GstRTSPClientState * state,
465     const gchar * check)
466 {
467   if (!ensure_authenticated (auth, state))
468     return FALSE;
469
470   return gst_rtsp_token_is_allowed (state->token,
471       GST_RTSP_TOKEN_TRANSPORT_CLIENT_SETTINGS);
472 }
473
474 static gboolean
475 default_check (GstRTSPAuth * auth, GstRTSPClientState * state,
476     const gchar * check)
477 {
478   gboolean res = FALSE;
479
480   /* FIXME, use hastable or so */
481   if (g_str_equal (check, GST_RTSP_AUTH_CHECK_CONNECT)) {
482     res = check_connect (auth, state, check);
483   } else if (g_str_equal (check, GST_RTSP_AUTH_CHECK_URL)) {
484     res = check_url (auth, state, check);
485   } else if (g_str_has_prefix (check, "auth.check.media.factory.")) {
486     res = check_factory (auth, state, check);
487   } else if (g_str_equal (check, GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS)) {
488     res = check_client_settings (auth, state, check);
489   }
490   return res;
491 }
492
493 static gboolean
494 no_auth_check (const gchar * check)
495 {
496   gboolean res;
497
498   if (g_str_equal (check, GST_RTSP_AUTH_CHECK_TRANSPORT_CLIENT_SETTINGS))
499     res = FALSE;
500   else
501     res = TRUE;
502
503   return res;
504 }
505
506 /**
507  * gst_rtsp_auth_check:
508  * @check: the item to check
509  *
510  * Check if @check is allowed in the current context.
511  *
512  * Returns: FALSE if check failed.
513  */
514 gboolean
515 gst_rtsp_auth_check (const gchar * check)
516 {
517   gboolean result = FALSE;
518   GstRTSPAuthClass *klass;
519   GstRTSPClientState *state;
520   GstRTSPAuth *auth;
521
522   g_return_val_if_fail (check != NULL, FALSE);
523
524   if (!(state = gst_rtsp_client_state_get_current ()))
525     goto no_state;
526
527   /* no auth, we don't need to check */
528   if (!(auth = state->auth))
529     return no_auth_check (check);
530
531   klass = GST_RTSP_AUTH_GET_CLASS (auth);
532
533   GST_DEBUG_OBJECT (auth, "check authorization '%s'", check);
534
535   if (klass->check)
536     result = klass->check (auth, state, check);
537
538   return result;
539
540   /* ERRORS */
541 no_state:
542   {
543     GST_ERROR ("no clientstate found");
544     return FALSE;
545   }
546 }
547
548 /**
549  * gst_rtsp_auth_make_basic:
550  * @user: a userid
551  * @pass: a password
552  *
553  * Construct a Basic authorisation token from @user and @pass.
554  *
555  * Returns: the base64 encoding of the string @user:@pass. g_free()
556  *    after usage.
557  */
558 gchar *
559 gst_rtsp_auth_make_basic (const gchar * user, const gchar * pass)
560 {
561   gchar *user_pass;
562   gchar *result;
563
564   g_return_val_if_fail (user != NULL, NULL);
565   g_return_val_if_fail (pass != NULL, NULL);
566
567   user_pass = g_strjoin (":", user, pass, NULL);
568   result = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
569   g_free (user_pass);
570
571   return result;
572 }