Git init
[profile/ivi/libsoup2.4.git] / libsoup / soup-auth-domain-basic.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 /*
3  * soup-auth-domain-basic.c: HTTP Basic Authentication (server-side)
4  *
5  * Copyright (C) 2007 Novell, Inc.
6  */
7
8 #ifdef HAVE_CONFIG_H
9 #include <config.h>
10 #endif
11
12 #include <string.h>
13
14 #include "soup-auth-domain-basic.h"
15 #include "soup-headers.h"
16 #include "soup-marshal.h"
17 #include "soup-message.h"
18
19 /**
20  * SECTION:soup-auth-domain-basic
21  * @short_description: Server-side "Basic" authentication
22  *
23  * #SoupAuthDomainBasic handles the server side of HTTP "Basic" (ie,
24  * cleartext password) authentication.
25  **/
26
27 enum {
28         PROP_0,
29
30         PROP_AUTH_CALLBACK,
31         PROP_AUTH_DATA,
32
33         LAST_PROP
34 };
35
36 typedef struct {
37         SoupAuthDomainBasicAuthCallback auth_callback;
38         gpointer auth_data;
39         GDestroyNotify auth_dnotify;
40 } SoupAuthDomainBasicPrivate;
41
42 #define SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_DOMAIN_BASIC, SoupAuthDomainBasicPrivate))
43
44 G_DEFINE_TYPE (SoupAuthDomainBasic, soup_auth_domain_basic, SOUP_TYPE_AUTH_DOMAIN)
45
46 static char    *accepts        (SoupAuthDomain *domain,
47                                 SoupMessage    *msg,
48                                 const char     *header);
49 static char    *challenge      (SoupAuthDomain *domain,
50                                 SoupMessage    *msg);
51 static gboolean check_password (SoupAuthDomain *domain,
52                                 SoupMessage    *msg,
53                                 const char     *username,
54                                 const char     *password);
55
56 static void set_property (GObject *object, guint prop_id,
57                           const GValue *value, GParamSpec *pspec);
58 static void get_property (GObject *object, guint prop_id,
59                           GValue *value, GParamSpec *pspec);
60
61 static void
62 soup_auth_domain_basic_init (SoupAuthDomainBasic *basic)
63 {
64 }
65
66 static void
67 finalize (GObject *object)
68 {
69         SoupAuthDomainBasicPrivate *priv =
70                 SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
71
72         if (priv->auth_dnotify)
73                 priv->auth_dnotify (priv->auth_data);
74
75         G_OBJECT_CLASS (soup_auth_domain_basic_parent_class)->finalize (object);
76 }
77
78 static void
79 soup_auth_domain_basic_class_init (SoupAuthDomainBasicClass *basic_class)
80 {
81         SoupAuthDomainClass *auth_domain_class =
82                 SOUP_AUTH_DOMAIN_CLASS (basic_class);
83         GObjectClass *object_class = G_OBJECT_CLASS (basic_class);
84
85         g_type_class_add_private (basic_class, sizeof (SoupAuthDomainBasicPrivate));
86
87         auth_domain_class->accepts        = accepts;
88         auth_domain_class->challenge      = challenge;
89         auth_domain_class->check_password = check_password;
90
91         object_class->finalize     = finalize;
92         object_class->set_property = set_property;
93         object_class->get_property = get_property;
94
95         /**
96          * SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK:
97          *
98          * Alias for the #SoupAuthDomainBasic:auth-callback property.
99          * (The #SoupAuthDomainBasicAuthCallback.)
100          **/
101         g_object_class_install_property (
102                 object_class, PROP_AUTH_CALLBACK,
103                 g_param_spec_pointer (SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK,
104                                       "Authentication callback",
105                                       "Password-checking callback",
106                                       G_PARAM_READWRITE));
107         /**
108          * SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA:
109          *
110          * Alias for the #SoupAuthDomainBasic:auth-data property.
111          * (The data to pass to the #SoupAuthDomainBasicAuthCallback.)
112          **/
113         g_object_class_install_property (
114                 object_class, PROP_AUTH_DATA,
115                 g_param_spec_pointer (SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA,
116                                       "Authentication callback data",
117                                       "Data to pass to authentication callback",
118                                       G_PARAM_READWRITE));
119 }
120
121 static void
122 set_property (GObject *object, guint prop_id,
123               const GValue *value, GParamSpec *pspec)
124 {
125         SoupAuthDomainBasicPrivate *priv =
126                 SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
127
128         switch (prop_id) {
129         case PROP_AUTH_CALLBACK:
130                 priv->auth_callback = g_value_get_pointer (value);
131                 break;
132         case PROP_AUTH_DATA:
133                 if (priv->auth_dnotify) {
134                         priv->auth_dnotify (priv->auth_data);
135                         priv->auth_dnotify = NULL;
136                 }
137                 priv->auth_data = g_value_get_pointer (value);
138                 break;
139         default:
140                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
141                 break;
142         }
143 }
144
145 static void
146 get_property (GObject *object, guint prop_id,
147               GValue *value, GParamSpec *pspec)
148 {
149         SoupAuthDomainBasicPrivate *priv =
150                 SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (object);
151
152         switch (prop_id) {
153         case PROP_AUTH_CALLBACK:
154                 g_value_set_pointer (value, priv->auth_callback);
155                 break;
156         case PROP_AUTH_DATA:
157                 g_value_set_pointer (value, priv->auth_data);
158                 break;
159         default:
160                 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
161                 break;
162         }
163 }
164
165 /**
166  * soup_auth_domain_basic_new:
167  * @optname1: name of first option, or %NULL
168  * @...: option name/value pairs
169  *
170  * Creates a #SoupAuthDomainBasic. You must set the
171  * %SOUP_AUTH_DOMAIN_REALM parameter, to indicate the realm name to be
172  * returned with the authentication challenge to the client. Other
173  * parameters are optional.
174  *
175  * Return value: the new #SoupAuthDomain
176  **/
177 SoupAuthDomain *
178 soup_auth_domain_basic_new (const char *optname1, ...)
179 {
180         SoupAuthDomain *domain;
181         va_list ap;
182
183         va_start (ap, optname1);
184         domain = (SoupAuthDomain *)g_object_new_valist (SOUP_TYPE_AUTH_DOMAIN_BASIC,
185                                                         optname1, ap);
186         va_end (ap);
187
188         g_return_val_if_fail (soup_auth_domain_get_realm (domain) != NULL, NULL);
189
190         return domain;
191 }
192
193 /**
194  * SoupAuthDomainBasicAuthCallback:
195  * @domain: the domain
196  * @msg: the message being authenticated
197  * @username: the username provided by the client
198  * @password: the password provided by the client
199  * @user_data: the data passed to soup_auth_domain_basic_set_auth_callback()
200  *
201  * Callback used by #SoupAuthDomainBasic for authentication purposes.
202  * The application should verify that @username and @password and valid
203  * and return %TRUE or %FALSE.
204  *
205  * If you are maintaining your own password database (rather than
206  * using the password to authenticate against some other system like
207  * PAM or a remote server), you should make sure you know what you are
208  * doing. In particular, don't store cleartext passwords, or
209  * easily-computed hashes of cleartext passwords, even if you don't
210  * care that much about the security of your server, because users
211  * will frequently use the same password for multiple sites, and so
212  * compromising any site with a cleartext (or easily-cracked) password
213  * database may give attackers access to other more-interesting sites
214  * as well.
215  *
216  * Return value: %TRUE if @username and @password are valid
217  **/
218
219 /**
220  * soup_auth_domain_basic_set_auth_callback:
221  * @domain: the domain
222  * @callback: the callback
223  * @user_data: data to pass to @auth_callback
224  * @dnotify: destroy notifier to free @user_data when @domain
225  * is destroyed
226  *
227  * Sets the callback that @domain will use to authenticate incoming
228  * requests. For each request containing authorization, @domain will
229  * invoke the callback, and then either accept or reject the request
230  * based on @callback's return value.
231  *
232  * You can also set the auth callback by setting the
233  * %SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK and
234  * %SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA properties, which can also be
235  * used to set the callback at construct time.
236  **/
237 void
238 soup_auth_domain_basic_set_auth_callback (SoupAuthDomain *domain,
239                                           SoupAuthDomainBasicAuthCallback callback,
240                                           gpointer        user_data,
241                                           GDestroyNotify  dnotify)
242 {
243         SoupAuthDomainBasicPrivate *priv =
244                 SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
245
246         if (priv->auth_dnotify)
247                 priv->auth_dnotify (priv->auth_data);
248
249         priv->auth_callback = callback;
250         priv->auth_data = user_data;
251         priv->auth_dnotify = dnotify;
252
253         g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_BASIC_AUTH_CALLBACK);
254         g_object_notify (G_OBJECT (domain), SOUP_AUTH_DOMAIN_BASIC_AUTH_DATA);
255 }
256
257 static void
258 pw_free (char *pw)
259 {
260         memset (pw, 0, strlen (pw));
261         g_free (pw);
262 }
263
264 static gboolean
265 parse_basic (SoupMessage *msg, const char *header,
266              char **username, char **password)
267 {
268         char *decoded, *colon;
269         gsize len, plen;
270
271         if (strncmp (header, "Basic ", 6) != 0)
272                 return FALSE;
273
274         decoded = (char *)g_base64_decode (header + 6, &len);
275         if (!decoded)
276                 return FALSE;
277
278         colon = memchr (decoded, ':', len);
279         if (!colon) {
280                 pw_free (decoded);
281                 return FALSE;
282         }
283         *colon = '\0';
284         plen = len - (colon - decoded) - 1;
285
286         *password = g_strndup (colon + 1, plen);
287         memset (colon + 1, 0, plen);
288         *username = decoded;
289         return TRUE;
290 }
291
292 static char *
293 accepts (SoupAuthDomain *domain, SoupMessage *msg, const char *header)
294 {
295         SoupAuthDomainBasicPrivate *priv =
296                 SOUP_AUTH_DOMAIN_BASIC_GET_PRIVATE (domain);
297         char *username, *password;
298         gboolean ok = FALSE;
299
300         if (!parse_basic (msg, header, &username, &password))
301                 return NULL;
302
303         if (priv->auth_callback) {
304                 ok = priv->auth_callback (domain, msg, username, password,
305                                           priv->auth_data);
306         } else {
307                 ok = soup_auth_domain_try_generic_auth_callback (
308                         domain, msg, username);
309         }
310
311         pw_free (password);
312
313         if (ok)
314                 return username;
315         else {
316                 g_free (username);
317                 return NULL;
318         }
319 }
320
321 static char *
322 challenge (SoupAuthDomain *domain, SoupMessage *msg)
323 {
324         GString *challenge;
325
326         challenge = g_string_new ("Basic ");
327         soup_header_g_string_append_param (challenge, "realm", soup_auth_domain_get_realm (domain));
328         return g_string_free (challenge, FALSE);
329 }
330
331 static gboolean
332 check_password (SoupAuthDomain *domain,
333                 SoupMessage    *msg,
334                 const char     *username,
335                 const char     *password)
336 {
337         const char *header;
338         char *msg_username, *msg_password;
339         gboolean ok;
340
341         header = soup_message_headers_get_one (msg->request_headers,
342                                                "Authorization");
343         if (!parse_basic (msg, header, &msg_username, &msg_password))
344                 return FALSE;
345
346         ok = (!strcmp (username, msg_username) &&
347               !strcmp (password, msg_password));
348         g_free (msg_username);
349         pw_free (msg_password);
350
351         return ok;
352 }