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