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