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