1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * soup-auth.c: HTTP Authentication framework
5 * Copyright (C) 2001-2003, Ximian, Inc.
12 #define LIBSOUP_I_HAVE_READ_BUG_594377_AND_KNOW_SOUP_PASSWORD_MANAGER_MIGHT_GO_AWAY
16 #include "soup-auth.h"
17 #include "soup-auth-basic.h"
18 #include "soup-auth-digest.h"
19 #include "soup-headers.h"
20 #include "soup-marshal.h"
25 * @short_description: HTTP client-side authentication support
26 * @see_also: #SoupSession
28 * #SoupAuth objects store the authentication data associated with a
29 * given bit of web space. They are created automatically by
36 * The abstract base class for handling authentication. Specific HTTP
37 * Authentication mechanisms are implemented by its subclasses, but
38 * applications never need to be aware of the specific subclasses
46 GHashTable *saved_passwords;
48 #define SOUP_AUTH_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH, SoupAuthPrivate))
50 G_DEFINE_ABSTRACT_TYPE (SoupAuth, soup_auth, G_TYPE_OBJECT)
57 static guint signals[LAST_SIGNAL] = { 0 };
66 PROP_IS_AUTHENTICATED,
71 static void set_property (GObject *object, guint prop_id,
72 const GValue *value, GParamSpec *pspec);
73 static void get_property (GObject *object, guint prop_id,
74 GValue *value, GParamSpec *pspec);
77 finalize (GObject *object)
79 SoupAuth *auth = SOUP_AUTH (object);
80 SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (auth);
84 if (priv->saved_passwords)
85 g_hash_table_destroy (priv->saved_passwords);
87 G_OBJECT_CLASS (soup_auth_parent_class)->finalize (object);
91 soup_auth_class_init (SoupAuthClass *auth_class)
93 GObjectClass *object_class = G_OBJECT_CLASS (auth_class);
95 g_type_class_add_private (auth_class, sizeof (SoupAuthPrivate));
97 object_class->finalize = finalize;
98 object_class->set_property = set_property;
99 object_class->get_property = get_property;
102 * SoupAuth::save-password:
104 * @username: the username to save
105 * @password: the password to save
107 * Emitted to request that the @username/@password pair be
108 * saved. If the session supports password-saving, it will
109 * connect to this signal before emitting
110 * #SoupSession::authenticate, so that it record the password
111 * if requested by the caller.
115 signals[SAVE_PASSWORD] =
116 g_signal_new ("save-password",
117 G_OBJECT_CLASS_TYPE (object_class),
120 soup_marshal_NONE__STRING_STRING,
127 * SOUP_AUTH_SCHEME_NAME:
129 * An alias for the #SoupAuth:scheme property. (The
130 * authentication scheme name.)
132 g_object_class_install_property (
133 object_class, PROP_SCHEME_NAME,
134 g_param_spec_string (SOUP_AUTH_SCHEME_NAME,
136 "Authentication scheme name",
142 * An alias for the #SoupAuth:realm property. (The
143 * authentication realm.)
145 g_object_class_install_property (
146 object_class, PROP_REALM,
147 g_param_spec_string (SOUP_AUTH_REALM,
149 "Authentication realm",
151 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
155 * An alias for the #SoupAuth:host property. (The
156 * host being authenticated to.)
158 g_object_class_install_property (
159 object_class, PROP_HOST,
160 g_param_spec_string (SOUP_AUTH_HOST,
162 "Authentication host",
164 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
166 * SOUP_AUTH_IS_FOR_PROXY:
168 * An alias for the #SoupAuth:is-for-proxy property. (Whether
169 * or not the auth is for a proxy server.)
171 g_object_class_install_property (
172 object_class, PROP_IS_FOR_PROXY,
173 g_param_spec_boolean (SOUP_AUTH_IS_FOR_PROXY,
175 "Whether or not the auth is for a proxy server",
177 G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
179 * SOUP_AUTH_IS_AUTHENTICATED:
181 * An alias for the #SoupAuth:is-authenticated property.
182 * (Whether or not the auth has been authenticated.)
184 g_object_class_install_property (
185 object_class, PROP_IS_AUTHENTICATED,
186 g_param_spec_boolean (SOUP_AUTH_IS_AUTHENTICATED,
188 "Whether or not the auth is authenticated",
194 soup_auth_init (SoupAuth *auth)
199 set_property (GObject *object, guint prop_id,
200 const GValue *value, GParamSpec *pspec)
202 SoupAuth *auth = SOUP_AUTH (object);
203 SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
207 auth->realm = g_value_dup_string (value);
210 priv->host = g_value_dup_string (value);
212 case PROP_IS_FOR_PROXY:
213 priv->proxy = g_value_get_boolean (value);
216 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
222 get_property (GObject *object, guint prop_id,
223 GValue *value, GParamSpec *pspec)
225 SoupAuth *auth = SOUP_AUTH (object);
226 SoupAuthPrivate *priv = SOUP_AUTH_GET_PRIVATE (object);
229 case PROP_SCHEME_NAME:
230 g_value_set_string (value, soup_auth_get_scheme_name (auth));
233 g_value_set_string (value, soup_auth_get_realm (auth));
236 g_value_set_string (value, soup_auth_get_host (auth));
238 case PROP_IS_FOR_PROXY:
239 g_value_set_boolean (value, priv->proxy);
241 case PROP_IS_AUTHENTICATED:
242 g_value_set_boolean (value, soup_auth_is_authenticated (auth));
245 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252 * @type: the type of auth to create (a subtype of #SoupAuth)
253 * @msg: the #SoupMessage the auth is being created for
254 * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
256 * Creates a new #SoupAuth of type @type with the information from
257 * @msg and @auth_header.
259 * This is called by #SoupSession; you will normally not create auths
262 * Return value: the new #SoupAuth, or %NULL if it could not be
266 soup_auth_new (GType type, SoupMessage *msg, const char *auth_header)
270 const char *scheme, *realm;
272 g_return_val_if_fail (g_type_is_a (type, SOUP_TYPE_AUTH), NULL);
273 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
274 g_return_val_if_fail (auth_header != NULL, NULL);
276 auth = g_object_new (type,
277 SOUP_AUTH_IS_FOR_PROXY, (msg->status_code == SOUP_STATUS_PROXY_UNAUTHORIZED),
278 SOUP_AUTH_HOST, soup_message_get_uri (msg)->host,
281 scheme = soup_auth_get_scheme_name (auth);
282 if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0) {
283 g_object_unref (auth);
287 params = soup_header_parse_param_list (auth_header + strlen (scheme));
289 g_object_unref (auth);
293 realm = g_hash_table_lookup (params, "realm");
295 soup_header_free_param_list (params);
296 g_object_unref (auth);
300 auth->realm = g_strdup (realm);
302 if (!SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params)) {
303 g_object_unref (auth);
306 soup_header_free_param_list (params);
313 * @msg: the #SoupMessage @auth is being updated for
314 * @auth_header: the WWW-Authenticate/Proxy-Authenticate header
316 * Updates @auth with the information from @msg and @auth_header,
317 * possibly un-authenticating it. As with soup_auth_new(), this is
318 * normally only used by #SoupSession.
320 * Return value: %TRUE if @auth is still a valid (but potentially
321 * unauthenticated) #SoupAuth. %FALSE if something about @auth_params
322 * could not be parsed or incorporated into @auth at all.
325 soup_auth_update (SoupAuth *auth, SoupMessage *msg, const char *auth_header)
328 const char *scheme, *realm;
329 gboolean was_authenticated, success;
331 g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
332 g_return_val_if_fail (SOUP_IS_MESSAGE (msg), FALSE);
333 g_return_val_if_fail (auth_header != NULL, FALSE);
335 scheme = soup_auth_get_scheme_name (auth);
336 if (g_ascii_strncasecmp (auth_header, scheme, strlen (scheme)) != 0)
339 params = soup_header_parse_param_list (auth_header + strlen (scheme));
343 realm = g_hash_table_lookup (params, "realm");
344 if (!realm || strcmp (realm, auth->realm) != 0) {
345 soup_header_free_param_list (params);
349 was_authenticated = soup_auth_is_authenticated (auth);
350 success = SOUP_AUTH_GET_CLASS (auth)->update (auth, msg, params);
351 if (was_authenticated != soup_auth_is_authenticated (auth))
352 g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
353 soup_header_free_param_list (params);
358 * soup_auth_authenticate:
360 * @username: the username provided by the user or client
361 * @password: the password provided by the user or client
363 * Call this on an auth to authenticate it; normally this will cause
364 * the auth's message to be requeued with the new authentication info.
366 * This does not cause the password to be saved to persistent storage;
367 * see soup_auth_save_password() for that.
370 soup_auth_authenticate (SoupAuth *auth, const char *username, const char *password)
372 gboolean was_authenticated;
374 g_return_if_fail (SOUP_IS_AUTH (auth));
375 g_return_if_fail (username != NULL);
376 g_return_if_fail (password != NULL);
378 was_authenticated = soup_auth_is_authenticated (auth);
379 SOUP_AUTH_GET_CLASS (auth)->authenticate (auth, username, password);
380 if (was_authenticated != soup_auth_is_authenticated (auth))
381 g_object_notify (G_OBJECT (auth), SOUP_AUTH_IS_AUTHENTICATED);
385 * soup_auth_is_for_proxy:
388 * Tests whether or not @auth is associated with a proxy server rather
389 * than an "origin" server.
391 * Return value: %TRUE or %FALSE
394 soup_auth_is_for_proxy (SoupAuth *auth)
396 g_return_val_if_fail (SOUP_IS_AUTH (auth), FALSE);
398 return SOUP_AUTH_GET_PRIVATE (auth)->proxy;
402 * soup_auth_get_scheme_name:
405 * Returns @auth's scheme name. (Eg, "Basic", "Digest", or "NTLM")
407 * Return value: the scheme name
410 soup_auth_get_scheme_name (SoupAuth *auth)
412 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
414 return SOUP_AUTH_GET_CLASS (auth)->scheme_name;
418 * soup_auth_get_host:
421 * Returns the host that @auth is associated with.
423 * Return value: the hostname
426 soup_auth_get_host (SoupAuth *auth)
428 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
430 return SOUP_AUTH_GET_PRIVATE (auth)->host;
435 * soup_auth_get_realm:
438 * Returns @auth's realm. This is an identifier that distinguishes
439 * separate authentication spaces on a given server, and may be some
440 * string that is meaningful to the user. (Although it is probably not
443 * Return value: the realm name
446 soup_auth_get_realm (SoupAuth *auth)
448 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
454 * soup_auth_get_info:
457 * Gets an opaque identifier for @auth, for use as a hash key or the
458 * like. #SoupAuth objects from the same server with the same
459 * identifier refer to the same authentication domain (eg, the URLs
460 * associated with them take the same usernames and passwords).
462 * Return value: the identifier
465 soup_auth_get_info (SoupAuth *auth)
467 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
469 return g_strdup_printf ("%s:%s",
470 SOUP_AUTH_GET_CLASS (auth)->scheme_name,
475 * soup_auth_is_authenticated:
478 * Tests if @auth has been given a username and password
480 * Return value: %TRUE if @auth has been given a username and password
483 soup_auth_is_authenticated (SoupAuth *auth)
485 g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
487 return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
491 * soup_auth_get_authorization:
493 * @msg: the #SoupMessage to be authorized
495 * Generates an appropriate "Authorization" header for @msg. (The
496 * session will only call this if soup_auth_is_authenticated()
499 * Return value: the "Authorization" header, which must be freed.
502 soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
504 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
505 g_return_val_if_fail (msg != NULL, NULL);
507 return SOUP_AUTH_GET_CLASS (auth)->get_authorization (auth, msg);
511 * soup_auth_get_protection_space:
513 * @source_uri: the URI of the request that @auth was generated in
516 * Returns a list of paths on the server which @auth extends over.
517 * (All subdirectories of these paths are also assumed to be part
518 * of @auth's protection space, unless otherwise discovered not to
521 * Return value: (element-type utf8): the list of paths, which can be freed with
522 * soup_auth_free_protection_space().
525 soup_auth_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
527 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
528 g_return_val_if_fail (source_uri != NULL, NULL);
530 return SOUP_AUTH_GET_CLASS (auth)->get_protection_space (auth, source_uri);
534 * soup_auth_free_protection_space: (skip)
536 * @space: the return value from soup_auth_get_protection_space()
541 soup_auth_free_protection_space (SoupAuth *auth, GSList *space)
545 for (s = space; s; s = s->next)
547 g_slist_free (space);
551 * soup_auth_get_saved_users:
554 * Gets a list of usernames for which a saved password is available.
555 * (If the session is not configured to save passwords, this will
558 * Return value: the list of usernames. You must free the list with
559 * g_slist_free(), but do not free or modify the contents.
564 soup_auth_get_saved_users (SoupAuth *auth)
566 SoupAuthPrivate *priv;
569 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
571 priv = SOUP_AUTH_GET_PRIVATE (auth);
574 if (priv->saved_passwords) {
578 g_hash_table_iter_init (&iter, priv->saved_passwords);
579 while (g_hash_table_iter_next (&iter, &key, &value))
580 users = g_slist_prepend (users, key);
586 * soup_auth_get_saved_password:
588 * @user: a username from the list returned from
589 * soup_auth_get_saved_users().
591 * Given a username for which @auth has a saved password, this returns
592 * that password. If @auth doesn't have a passwords saved for @user, it
595 * Return value: the saved password, or %NULL.
600 soup_auth_get_saved_password (SoupAuth *auth, const char *user)
602 SoupAuthPrivate *priv;
604 g_return_val_if_fail (SOUP_IS_AUTH (auth), NULL);
605 g_return_val_if_fail (user != NULL, NULL);
607 priv = SOUP_AUTH_GET_PRIVATE (auth);
608 if (!priv->saved_passwords)
610 return g_hash_table_lookup (priv->saved_passwords, user);
614 free_password (gpointer password)
616 memset (password, 0, strlen (password));
621 init_saved_passwords (SoupAuthPrivate *priv)
623 priv->saved_passwords = g_hash_table_new_full (
624 g_str_hash, g_str_equal, g_free, free_password);
628 * soup_auth_has_saved_password:
630 * @username: a username
631 * @password: a password
633 * Updates @auth to be aware of an already-saved username/password
634 * combination. This method <emphasis>does not</emphasis> cause the
635 * given @username and @password to be saved; use
636 * soup_auth_save_password() for that. (soup_auth_has_saved_password()
637 * is an internal method, which is used by the code that actually
638 * saves and restores the passwords.)
643 soup_auth_has_saved_password (SoupAuth *auth, const char *username,
644 const char *password)
646 SoupAuthPrivate *priv;
648 g_return_if_fail (SOUP_IS_AUTH (auth));
649 g_return_if_fail (username != NULL);
650 g_return_if_fail (password != NULL);
652 priv = SOUP_AUTH_GET_PRIVATE (auth);
654 if (!priv->saved_passwords)
655 init_saved_passwords (priv);
656 g_hash_table_insert (priv->saved_passwords,
657 g_strdup (username), g_strdup (password));
661 * soup_auth_save_password:
663 * @username: the username provided by the user or client
664 * @password: the password provided by the user or client
666 * Requests that the username/password pair be saved to whatever form
667 * of persistent password storage the session supports.
672 soup_auth_save_password (SoupAuth *auth, const char *username,
673 const char *password)
675 g_return_if_fail (SOUP_IS_AUTH (auth));
676 g_return_if_fail (username != NULL);
677 g_return_if_fail (password != NULL);
679 g_signal_emit (auth, signals[SAVE_PASSWORD], 0,