a0df870788e7e25f5c78e6e54dab951f99484f83
[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, GstRTSPClient * client,
50     GstRTSPClientState * state);
51 static gboolean default_validate (GstRTSPAuth * auth,
52     GstRTSPClient * client, GstRTSPClientState * state);
53 static gboolean default_check (GstRTSPAuth * auth, GstRTSPClient * client,
54     GQuark hint, GstRTSPClientState * state);
55
56 G_DEFINE_TYPE (GstRTSPAuth, gst_rtsp_auth, G_TYPE_OBJECT);
57
58 static void
59 gst_rtsp_auth_class_init (GstRTSPAuthClass * klass)
60 {
61   GObjectClass *gobject_class;
62
63   g_type_class_add_private (klass, sizeof (GstRTSPAuthPrivate));
64
65   gobject_class = G_OBJECT_CLASS (klass);
66
67   gobject_class->get_property = gst_rtsp_auth_get_property;
68   gobject_class->set_property = gst_rtsp_auth_set_property;
69   gobject_class->finalize = gst_rtsp_auth_finalize;
70
71   klass->setup = default_setup;
72   klass->validate = default_validate;
73   klass->check = default_check;
74
75   GST_DEBUG_CATEGORY_INIT (rtsp_auth_debug, "rtspauth", 0, "GstRTSPAuth");
76 }
77
78 static void
79 gst_rtsp_auth_init (GstRTSPAuth * auth)
80 {
81   GstRTSPAuthPrivate *priv;
82
83   auth->priv = priv = GST_RTSP_AUTH_GET_PRIVATE (auth);
84
85   g_mutex_init (&priv->lock);
86
87   priv->basic = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
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  * @authgroup: authorisation group
153  *
154  * Add a basic token for the default authentication algorithm that
155  * enables the client qith privileges from @authgroup.
156  */
157 void
158 gst_rtsp_auth_add_basic (GstRTSPAuth * auth, const gchar * basic,
159     const gchar * authgroup)
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 (authgroup != NULL);
166
167   priv = auth->priv;
168
169   g_mutex_lock (&priv->lock);
170   g_hash_table_replace (priv->basic, g_strdup (basic), g_strdup (authgroup));
171   g_mutex_unlock (&priv->lock);
172 }
173
174 /**
175  * gst_rtsp_auth_remove_basic:
176  * @auth: a #GstRTSPAuth
177  * @basic: (transfer none): the basic token
178  *
179  * Add a basic token for the default authentication algorithm that
180  * enables the client qith privileges from @authgroup.
181  */
182 void
183 gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
184 {
185   GstRTSPAuthPrivate *priv;
186
187   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
188   g_return_if_fail (basic != NULL);
189
190   priv = auth->priv;
191
192   g_mutex_lock (&priv->lock);
193   g_hash_table_remove (priv->basic, basic);
194   g_mutex_unlock (&priv->lock);
195 }
196
197 static gboolean
198 default_setup (GstRTSPAuth * auth, GstRTSPClient * client,
199     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, GstRTSPClient * client,
223     GstRTSPClientState * state)
224 {
225   gboolean result = FALSE;
226   GstRTSPAuthClass *klass;
227
228   g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), FALSE);
229   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
230   g_return_val_if_fail (state != NULL, FALSE);
231
232   klass = GST_RTSP_AUTH_GET_CLASS (auth);
233
234   GST_DEBUG_OBJECT (auth, "setup auth");
235
236   if (klass->setup)
237     result = klass->setup (auth, client, state);
238
239   return result;
240 }
241
242 static gboolean
243 default_validate (GstRTSPAuth * auth, GstRTSPClient * client,
244     GstRTSPClientState * state)
245 {
246   GstRTSPAuthPrivate *priv = auth->priv;
247   GstRTSPResult res;
248   gchar *authorization;
249
250   GST_DEBUG_OBJECT (auth, "validate");
251
252   res =
253       gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_AUTHORIZATION,
254       &authorization, 0);
255   if (res < 0)
256     goto no_auth;
257
258   /* parse type */
259   if (g_ascii_strncasecmp (authorization, "basic ", 6) == 0) {
260     gchar *authgroup;
261
262     GST_DEBUG_OBJECT (auth, "check Basic auth");
263     g_mutex_lock (&priv->lock);
264     if ((authgroup = g_hash_table_lookup (priv->basic, &authorization[6]))) {
265       GST_DEBUG_OBJECT (auth, "setting authgroup %s", authgroup);
266       state->authgroup = authgroup;
267     }
268     g_mutex_unlock (&priv->lock);
269   } else if (g_ascii_strncasecmp (authorization, "digest ", 7) == 0) {
270     GST_DEBUG_OBJECT (auth, "check Digest auth");
271     /* not implemented yet */
272   }
273   return TRUE;
274
275 no_auth:
276   {
277     GST_DEBUG_OBJECT (auth, "no authorization header found");
278     return TRUE;
279   }
280 }
281
282 static gboolean
283 default_check (GstRTSPAuth * auth, GstRTSPClient * client,
284     GQuark hint, GstRTSPClientState * state)
285 {
286   GstRTSPAuthPrivate *priv = auth->priv;
287   GstRTSPAuthClass *klass;
288
289   klass = GST_RTSP_AUTH_GET_CLASS (auth);
290
291   if ((state->method & priv->methods) != 0) {
292     /* we need an authgroup to check */
293     if (state->authgroup == NULL) {
294       if (klass->validate) {
295         if (!klass->validate (auth, client, state))
296           goto validate_failed;
297       }
298     }
299
300     if (state->authgroup == NULL)
301       goto no_auth;
302   }
303   return TRUE;
304
305 validate_failed:
306   {
307     GST_DEBUG_OBJECT (auth, "validation failed");
308     return FALSE;
309   }
310 no_auth:
311   {
312     GST_DEBUG_OBJECT (auth, "no authorization group found");
313     return FALSE;
314   }
315 }
316
317 /**
318  * gst_rtsp_auth_check:
319  * @auth: a #GstRTSPAuth
320  * @client: the client
321  * @hint: a hint
322  * @state: client state
323  *
324  * Check if @client with state is authorized to perform @hint in the
325  * current @state.
326  *
327  * Returns: FALSE if check failed.
328  */
329 gboolean
330 gst_rtsp_auth_check (GstRTSPAuth * auth, GstRTSPClient * client,
331     GQuark hint, GstRTSPClientState * state)
332 {
333   gboolean result = FALSE;
334   GstRTSPAuthClass *klass;
335
336   g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), FALSE);
337   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
338   g_return_val_if_fail (state != NULL, FALSE);
339
340   klass = GST_RTSP_AUTH_GET_CLASS (auth);
341
342   GST_DEBUG_OBJECT (auth, "check auth");
343
344   if (klass->check)
345     result = klass->check (auth, client, hint, state);
346
347   return result;
348 }
349
350 /**
351  * gst_rtsp_auth_make_basic:
352  * @user: a userid
353  * @pass: a password
354  *
355  * Construct a Basic authorisation token from @user and @pass.
356  *
357  * Returns: the base64 encoding of the string @user:@pass. g_free()
358  *    after usage.
359  */
360 gchar *
361 gst_rtsp_auth_make_basic (const gchar * user, const gchar * pass)
362 {
363   gchar *user_pass;
364   gchar *result;
365
366   g_return_val_if_fail (user != NULL, NULL);
367   g_return_val_if_fail (pass != NULL, NULL);
368
369   user_pass = g_strjoin (":", user, pass, NULL);
370   result = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
371   g_free (user_pass);
372
373   return result;
374 }