docs: update docs
[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 #include <string.h>
21
22 #include "rtsp-auth.h"
23
24 #define GST_RTSP_AUTH_GET_PRIVATE(obj)  \
25    (G_TYPE_INSTANCE_GET_PRIVATE ((obj), GST_TYPE_RTSP_AUTH, GstRTSPAuthPrivate))
26
27 struct _GstRTSPAuthPrivate
28 {
29   GMutex lock;
30   GHashTable *basic;            /* protected by lock */
31   GstRTSPMethod methods;
32 };
33
34 enum
35 {
36   PROP_0,
37   PROP_LAST
38 };
39
40 GST_DEBUG_CATEGORY_STATIC (rtsp_auth_debug);
41 #define GST_CAT_DEFAULT rtsp_auth_debug
42
43 static void gst_rtsp_auth_get_property (GObject * object, guint propid,
44     GValue * value, GParamSpec * pspec);
45 static void gst_rtsp_auth_set_property (GObject * object, guint propid,
46     const GValue * value, GParamSpec * pspec);
47 static void gst_rtsp_auth_finalize (GObject * obj);
48
49 static gboolean default_setup (GstRTSPAuth * auth, GstRTSPClientState * state);
50 static gboolean default_authenticate (GstRTSPAuth * auth,
51     GstRTSPClientState * state);
52 static gboolean default_check (GstRTSPAuth * auth, GstRTSPClientState * state,
53     const gchar * check);
54
55 G_DEFINE_TYPE (GstRTSPAuth, gst_rtsp_auth, G_TYPE_OBJECT);
56
57 static void
58 gst_rtsp_auth_class_init (GstRTSPAuthClass * klass)
59 {
60   GObjectClass *gobject_class;
61
62   g_type_class_add_private (klass, sizeof (GstRTSPAuthPrivate));
63
64   gobject_class = G_OBJECT_CLASS (klass);
65
66   gobject_class->get_property = gst_rtsp_auth_get_property;
67   gobject_class->set_property = gst_rtsp_auth_set_property;
68   gobject_class->finalize = gst_rtsp_auth_finalize;
69
70   klass->setup = default_setup;
71   klass->authenticate = default_authenticate;
72   klass->check = default_check;
73
74   GST_DEBUG_CATEGORY_INIT (rtsp_auth_debug, "rtspauth", 0, "GstRTSPAuth");
75 }
76
77 static void
78 gst_rtsp_auth_init (GstRTSPAuth * auth)
79 {
80   GstRTSPAuthPrivate *priv;
81
82   auth->priv = priv = GST_RTSP_AUTH_GET_PRIVATE (auth);
83
84   g_mutex_init (&priv->lock);
85
86   priv->basic = g_hash_table_new_full (g_str_hash, g_str_equal, g_free,
87       (GDestroyNotify) gst_rtsp_token_unref);
88
89   /* bitwise or of all methods that need authentication */
90   priv->methods = GST_RTSP_DESCRIBE |
91       GST_RTSP_ANNOUNCE |
92       GST_RTSP_GET_PARAMETER |
93       GST_RTSP_SET_PARAMETER |
94       GST_RTSP_PAUSE |
95       GST_RTSP_PLAY | GST_RTSP_RECORD | GST_RTSP_SETUP | GST_RTSP_TEARDOWN;
96 }
97
98 static void
99 gst_rtsp_auth_finalize (GObject * obj)
100 {
101   GstRTSPAuth *auth = GST_RTSP_AUTH (obj);
102   GstRTSPAuthPrivate *priv = auth->priv;
103
104   GST_INFO ("finalize auth %p", auth);
105   g_hash_table_unref (priv->basic);
106   g_mutex_clear (&priv->lock);
107
108   G_OBJECT_CLASS (gst_rtsp_auth_parent_class)->finalize (obj);
109 }
110
111 static void
112 gst_rtsp_auth_get_property (GObject * object, guint propid,
113     GValue * value, GParamSpec * pspec)
114 {
115   switch (propid) {
116     default:
117       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
118   }
119 }
120
121 static void
122 gst_rtsp_auth_set_property (GObject * object, guint propid,
123     const GValue * value, GParamSpec * pspec)
124 {
125   switch (propid) {
126     default:
127       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
128   }
129 }
130
131 /**
132  * gst_rtsp_auth_new:
133  *
134  * Create a new #GstRTSPAuth instance.
135  *
136  * Returns: a new #GstRTSPAuth
137  */
138 GstRTSPAuth *
139 gst_rtsp_auth_new (void)
140 {
141   GstRTSPAuth *result;
142
143   result = g_object_new (GST_TYPE_RTSP_AUTH, NULL);
144
145   return result;
146 }
147
148 /**
149  * gst_rtsp_auth_add_basic:
150  * @auth: a #GstRTSPAuth
151  * @basic: the basic token
152  * @token: authorisation token
153  *
154  * Add a basic token for the default authentication algorithm that
155  * enables the client with privileges listed in @token.
156  */
157 void
158 gst_rtsp_auth_add_basic (GstRTSPAuth * auth, const gchar * basic,
159     GstRTSPToken * token)
160 {
161   GstRTSPAuthPrivate *priv;
162
163   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
164   g_return_if_fail (basic != NULL);
165   g_return_if_fail (GST_IS_RTSP_TOKEN (token));
166
167   priv = auth->priv;
168
169   g_mutex_lock (&priv->lock);
170   g_hash_table_replace (priv->basic, g_strdup (basic),
171       gst_rtsp_token_ref (token));
172   g_mutex_unlock (&priv->lock);
173 }
174
175 /**
176  * gst_rtsp_auth_remove_basic:
177  * @auth: a #GstRTSPAuth
178  * @basic: (transfer none): the basic token
179  *
180  * Add a basic token for the default authentication algorithm that
181  * enables the client qith privileges from @authgroup.
182  */
183 void
184 gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
185 {
186   GstRTSPAuthPrivate *priv;
187
188   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
189   g_return_if_fail (basic != NULL);
190
191   priv = auth->priv;
192
193   g_mutex_lock (&priv->lock);
194   g_hash_table_remove (priv->basic, basic);
195   g_mutex_unlock (&priv->lock);
196 }
197
198 static gboolean
199 default_setup (GstRTSPAuth * auth, GstRTSPClientState * state)
200 {
201   if (state->response == NULL)
202     return FALSE;
203
204   /* we only have Basic for now */
205   gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
206       "Basic realm=\"GStreamer RTSP Server\"");
207
208   return TRUE;
209 }
210
211 /**
212  * gst_rtsp_auth_setup:
213  * @auth: a #GstRTSPAuth
214  * @client: the client
215  * @state: TODO
216  *
217  * Add authentication tokens to @response.
218  *
219  * Returns: FALSE if something is wrong.
220  */
221 gboolean
222 gst_rtsp_auth_setup (GstRTSPAuth * auth, GstRTSPClientState * state)
223 {
224   gboolean result = FALSE;
225   GstRTSPAuthClass *klass;
226
227   g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), FALSE);
228   g_return_val_if_fail (state != NULL, FALSE);
229
230   klass = GST_RTSP_AUTH_GET_CLASS (auth);
231
232   GST_DEBUG_OBJECT (auth, "setup auth");
233
234   if (klass->setup)
235     result = klass->setup (auth, state);
236
237   return result;
238 }
239
240 static gboolean
241 default_authenticate (GstRTSPAuth * auth, GstRTSPClientState * state)
242 {
243   GstRTSPAuthPrivate *priv = auth->priv;
244   GstRTSPResult res;
245   gchar *authorization;
246
247   GST_DEBUG_OBJECT (auth, "authenticate");
248
249   res =
250       gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_AUTHORIZATION,
251       &authorization, 0);
252   if (res < 0)
253     goto no_auth;
254
255   /* parse type */
256   if (g_ascii_strncasecmp (authorization, "basic ", 6) == 0) {
257     GstRTSPToken *token;
258
259     GST_DEBUG_OBJECT (auth, "check Basic auth");
260     g_mutex_lock (&priv->lock);
261     if ((token = g_hash_table_lookup (priv->basic, &authorization[6]))) {
262       GST_DEBUG_OBJECT (auth, "setting token %p", token);
263       state->token = token;
264     }
265     g_mutex_unlock (&priv->lock);
266   } else if (g_ascii_strncasecmp (authorization, "digest ", 7) == 0) {
267     GST_DEBUG_OBJECT (auth, "check Digest auth");
268     /* not implemented yet */
269   }
270   return TRUE;
271
272 no_auth:
273   {
274     GST_DEBUG_OBJECT (auth, "no authorization header found");
275     return TRUE;
276   }
277 }
278
279 static gboolean
280 ensure_authenticated (GstRTSPAuth * auth, GstRTSPClientState * state)
281 {
282   GstRTSPAuthClass *klass;
283
284   klass = GST_RTSP_AUTH_GET_CLASS (auth);
285
286   /* we need a token to check */
287   if (state->token == NULL) {
288     if (klass->authenticate) {
289       if (!klass->authenticate (auth, state))
290         goto authenticate_failed;
291     }
292   }
293   if (state->token == NULL)
294     goto no_auth;
295
296   return TRUE;
297
298 /* ERRORS */
299 authenticate_failed:
300   {
301     GST_DEBUG_OBJECT (auth, "authenticate failed");
302     return FALSE;
303   }
304 no_auth:
305   {
306     GST_DEBUG_OBJECT (auth, "no authorization token found");
307     return FALSE;
308   }
309 }
310
311 static gboolean
312 default_check (GstRTSPAuth * auth, GstRTSPClientState * state,
313     const gchar * check)
314 {
315   GstRTSPAuthPrivate *priv = auth->priv;
316   gboolean res = FALSE;
317
318   if (g_str_equal (check, GST_RTSP_AUTH_CHECK_URL)) {
319     if ((state->method & priv->methods) != 0)
320       res = ensure_authenticated (auth, state);
321     else
322       res = TRUE;
323   } else if (g_str_has_prefix (check, "auth.check.media.factory.")) {
324     const gchar *role;
325     GstRTSPPermissions *perms;
326
327     if (!(role =
328             gst_rtsp_token_get_string (state->token,
329                 GST_RTSP_MEDIA_FACTORY_ROLE)))
330       goto done;
331     if (!(perms = gst_rtsp_media_factory_get_permissions (state->factory)))
332       goto done;
333
334     if (g_str_equal (check, "auth.check.media.factory.access"))
335       res =
336           gst_rtsp_permissions_is_allowed (perms, role,
337           GST_RTSP_MEDIA_FACTORY_PERM_ACCESS);
338     else if (g_str_equal (check, "auth.check.media.factory.construct"))
339       res =
340           gst_rtsp_permissions_is_allowed (perms, role,
341           GST_RTSP_MEDIA_FACTORY_PERM_CONSTRUCT);
342   }
343 done:
344   return res;
345 }
346
347 /**
348  * gst_rtsp_auth_check:
349  * @check: the item to check
350  *
351  * Check if @check is allowed in the current context.
352  *
353  * Returns: FALSE if check failed.
354  */
355 gboolean
356 gst_rtsp_auth_check (const gchar * check)
357 {
358   gboolean result = FALSE;
359   GstRTSPAuthClass *klass;
360   GstRTSPClientState *state;
361   GstRTSPAuth *auth;
362
363   g_return_val_if_fail (check != NULL, FALSE);
364
365   if (!(state = gst_rtsp_client_state_get_current ()))
366     goto no_state;
367
368   /* no auth, we don't need to check */
369   if (!(auth = state->auth))
370     return TRUE;
371
372   klass = GST_RTSP_AUTH_GET_CLASS (auth);
373
374   GST_DEBUG_OBJECT (auth, "check authorization '%s'", check);
375
376   if (klass->check)
377     result = klass->check (auth, state, check);
378
379   return result;
380
381   /* ERRORS */
382 no_state:
383   {
384     GST_ERROR ("no clientstate found");
385     return FALSE;
386   }
387 }
388
389 /**
390  * gst_rtsp_auth_make_basic:
391  * @user: a userid
392  * @pass: a password
393  *
394  * Construct a Basic authorisation token from @user and @pass.
395  *
396  * Returns: the base64 encoding of the string @user:@pass. g_free()
397  *    after usage.
398  */
399 gchar *
400 gst_rtsp_auth_make_basic (const gchar * user, const gchar * pass)
401 {
402   gchar *user_pass;
403   gchar *result;
404
405   g_return_val_if_fail (user != NULL, NULL);
406   g_return_val_if_fail (pass != NULL, NULL);
407
408   user_pass = g_strjoin (":", user, pass, NULL);
409   result = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
410   g_free (user_pass);
411
412   return result;
413 }