• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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