auth: use the token after authentication
[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_authenticate (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->authenticate = default_authenticate;
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,
88       (GDestroyNotify) gst_rtsp_token_unref);
89
90   /* bitwise or of all methods that need authentication */
91   priv->methods = GST_RTSP_DESCRIBE |
92       GST_RTSP_ANNOUNCE |
93       GST_RTSP_GET_PARAMETER |
94       GST_RTSP_SET_PARAMETER |
95       GST_RTSP_PAUSE |
96       GST_RTSP_PLAY | GST_RTSP_RECORD | GST_RTSP_SETUP | GST_RTSP_TEARDOWN;
97 }
98
99 static void
100 gst_rtsp_auth_finalize (GObject * obj)
101 {
102   GstRTSPAuth *auth = GST_RTSP_AUTH (obj);
103   GstRTSPAuthPrivate *priv = auth->priv;
104
105   GST_INFO ("finalize auth %p", auth);
106   g_hash_table_unref (priv->basic);
107   g_mutex_clear (&priv->lock);
108
109   G_OBJECT_CLASS (gst_rtsp_auth_parent_class)->finalize (obj);
110 }
111
112 static void
113 gst_rtsp_auth_get_property (GObject * object, guint propid,
114     GValue * value, GParamSpec * pspec)
115 {
116   switch (propid) {
117     default:
118       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
119   }
120 }
121
122 static void
123 gst_rtsp_auth_set_property (GObject * object, guint propid,
124     const GValue * value, GParamSpec * pspec)
125 {
126   switch (propid) {
127     default:
128       G_OBJECT_WARN_INVALID_PROPERTY_ID (object, propid, pspec);
129   }
130 }
131
132 /**
133  * gst_rtsp_auth_new:
134  *
135  * Create a new #GstRTSPAuth instance.
136  *
137  * Returns: a new #GstRTSPAuth
138  */
139 GstRTSPAuth *
140 gst_rtsp_auth_new (void)
141 {
142   GstRTSPAuth *result;
143
144   result = g_object_new (GST_TYPE_RTSP_AUTH, NULL);
145
146   return result;
147 }
148
149 /**
150  * gst_rtsp_auth_add_basic:
151  * @auth: a #GstRTSPAuth
152  * @basic: the basic token
153  * @authgroup: authorisation group
154  *
155  * Add a basic token for the default authentication algorithm that
156  * enables the client qith privileges from @authgroup.
157  */
158 void
159 gst_rtsp_auth_add_basic (GstRTSPAuth * auth, const gchar * basic,
160     GstRTSPToken * token)
161 {
162   GstRTSPAuthPrivate *priv;
163
164   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
165   g_return_if_fail (basic != NULL);
166   g_return_if_fail (GST_IS_RTSP_TOKEN (token));
167
168   priv = auth->priv;
169
170   g_mutex_lock (&priv->lock);
171   g_hash_table_replace (priv->basic, g_strdup (basic),
172       gst_rtsp_token_ref (token));
173   g_mutex_unlock (&priv->lock);
174 }
175
176 /**
177  * gst_rtsp_auth_remove_basic:
178  * @auth: a #GstRTSPAuth
179  * @basic: (transfer none): the basic token
180  *
181  * Add a basic token for the default authentication algorithm that
182  * enables the client qith privileges from @authgroup.
183  */
184 void
185 gst_rtsp_auth_remove_basic (GstRTSPAuth * auth, const gchar * basic)
186 {
187   GstRTSPAuthPrivate *priv;
188
189   g_return_if_fail (GST_IS_RTSP_AUTH (auth));
190   g_return_if_fail (basic != NULL);
191
192   priv = auth->priv;
193
194   g_mutex_lock (&priv->lock);
195   g_hash_table_remove (priv->basic, basic);
196   g_mutex_unlock (&priv->lock);
197 }
198
199 static gboolean
200 default_setup (GstRTSPAuth * auth, GstRTSPClient * client,
201     GstRTSPClientState * state)
202 {
203   if (state->response == NULL)
204     return FALSE;
205
206   /* we only have Basic for now */
207   gst_rtsp_message_add_header (state->response, GST_RTSP_HDR_WWW_AUTHENTICATE,
208       "Basic realm=\"GStreamer RTSP Server\"");
209
210   return TRUE;
211 }
212
213 /**
214  * gst_rtsp_auth_setup:
215  * @auth: a #GstRTSPAuth
216  * @client: the client
217  * @state: TODO
218  *
219  * Add authentication tokens to @response.
220  *
221  * Returns: FALSE if something is wrong.
222  */
223 gboolean
224 gst_rtsp_auth_setup (GstRTSPAuth * auth, GstRTSPClient * client,
225     GstRTSPClientState * state)
226 {
227   gboolean result = FALSE;
228   GstRTSPAuthClass *klass;
229
230   g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), FALSE);
231   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
232   g_return_val_if_fail (state != NULL, FALSE);
233
234   klass = GST_RTSP_AUTH_GET_CLASS (auth);
235
236   GST_DEBUG_OBJECT (auth, "setup auth");
237
238   if (klass->setup)
239     result = klass->setup (auth, client, state);
240
241   return result;
242 }
243
244 static gboolean
245 default_authenticate (GstRTSPAuth * auth, GstRTSPClient * client,
246     GstRTSPClientState * state)
247 {
248   GstRTSPAuthPrivate *priv = auth->priv;
249   GstRTSPResult res;
250   gchar *authorization;
251
252   GST_DEBUG_OBJECT (auth, "authenticate");
253
254   res =
255       gst_rtsp_message_get_header (state->request, GST_RTSP_HDR_AUTHORIZATION,
256       &authorization, 0);
257   if (res < 0)
258     goto no_auth;
259
260   /* parse type */
261   if (g_ascii_strncasecmp (authorization, "basic ", 6) == 0) {
262     GstRTSPToken *token;
263
264     GST_DEBUG_OBJECT (auth, "check Basic auth");
265     g_mutex_lock (&priv->lock);
266     if ((token = g_hash_table_lookup (priv->basic, &authorization[6]))) {
267       GST_DEBUG_OBJECT (auth, "setting token %p", token);
268       state->token = token;
269     }
270     g_mutex_unlock (&priv->lock);
271   } else if (g_ascii_strncasecmp (authorization, "digest ", 7) == 0) {
272     GST_DEBUG_OBJECT (auth, "check Digest auth");
273     /* not implemented yet */
274   }
275   return TRUE;
276
277 no_auth:
278   {
279     GST_DEBUG_OBJECT (auth, "no authorization header found");
280     return TRUE;
281   }
282 }
283
284 static gboolean
285 default_check (GstRTSPAuth * auth, GstRTSPClient * client,
286     GQuark hint, GstRTSPClientState * state)
287 {
288   GstRTSPAuthPrivate *priv = auth->priv;
289   GstRTSPAuthClass *klass;
290
291   klass = GST_RTSP_AUTH_GET_CLASS (auth);
292
293   if ((state->method & priv->methods) != 0) {
294     /* we need an authgroup to check */
295     if (state->token == NULL) {
296       if (klass->authenticate) {
297         if (!klass->authenticate (auth, client, state))
298           goto authenticate_failed;
299       }
300     }
301
302     if (state->token == NULL)
303       goto no_auth;
304   }
305   return TRUE;
306
307 authenticate_failed:
308   {
309     GST_DEBUG_OBJECT (auth, "check failed");
310     return FALSE;
311   }
312 no_auth:
313   {
314     GST_DEBUG_OBJECT (auth, "no authorization group found");
315     return FALSE;
316   }
317 }
318
319 /**
320  * gst_rtsp_auth_check:
321  * @auth: a #GstRTSPAuth
322  * @client: the client
323  * @hint: a hint
324  * @state: client state
325  *
326  * Check if @client with state is authorized to perform @hint in the
327  * current @state.
328  *
329  * Returns: FALSE if check failed.
330  */
331 gboolean
332 gst_rtsp_auth_check (GstRTSPAuth * auth, GstRTSPClient * client,
333     GQuark hint, GstRTSPClientState * state)
334 {
335   gboolean result = FALSE;
336   GstRTSPAuthClass *klass;
337
338   g_return_val_if_fail (GST_IS_RTSP_AUTH (auth), FALSE);
339   g_return_val_if_fail (GST_IS_RTSP_CLIENT (client), FALSE);
340   g_return_val_if_fail (state != NULL, FALSE);
341
342   klass = GST_RTSP_AUTH_GET_CLASS (auth);
343
344   GST_DEBUG_OBJECT (auth, "check auth");
345
346   if (klass->check)
347     result = klass->check (auth, client, hint, state);
348
349   return result;
350 }
351
352 /**
353  * gst_rtsp_auth_make_basic:
354  * @user: a userid
355  * @pass: a password
356  *
357  * Construct a Basic authorisation token from @user and @pass.
358  *
359  * Returns: the base64 encoding of the string @user:@pass. g_free()
360  *    after usage.
361  */
362 gchar *
363 gst_rtsp_auth_make_basic (const gchar * user, const gchar * pass)
364 {
365   gchar *user_pass;
366   gchar *result;
367
368   g_return_val_if_fail (user != NULL, NULL);
369   g_return_val_if_fail (pass != NULL, NULL);
370
371   user_pass = g_strjoin (":", user, pass, NULL);
372   result = g_base64_encode ((guchar *) user_pass, strlen (user_pass));
373   g_free (user_pass);
374
375   return result;
376 }