summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Winship <danw@gnome.org>2012-12-12 10:19:00 +0100
committerDan Winship <danw@gnome.org>2013-01-02 15:34:12 -0500
commit586aef885f64352bbd3c1b9e68ae2020f33d5833 (patch)
tree2bb0a0f5a0c6e021553f8b6d02cd01d40689ab11
parentb24b8324b15250865f7f44178cb5c5fba80074e8 (diff)
downloadlibsoup-586aef885f64352bbd3c1b9e68ae2020f33d5833.tar.gz
soup-auth-manager: split out connection handling
Add a new SoupConnectionAuth class to help with connection tracking, and make SoupAuthNTLM a subclass of it. Allow a single SoupAuthNTLM to carry state information about multiple connections. Make SoupSession store the SoupConnection a SoupMessage is associated with on the message, and use that from SoupConnectionAuth rather than tracking sockets by hand like SoupAuthManager had previously done. Remove the connection tracking in SoupAuthManager, since it is no longer needed.
-rw-r--r--docs/reference/Makefile.am2
-rw-r--r--docs/reference/libsoup-2.4-sections.txt1
-rw-r--r--libsoup/Makefile.am2
-rw-r--r--libsoup/libsoup-2.4.sym1
-rw-r--r--libsoup/soup-auth-manager.c217
-rw-r--r--libsoup/soup-auth-ntlm.c192
-rw-r--r--libsoup/soup-auth-ntlm.h6
-rw-r--r--libsoup/soup-auth.c27
-rw-r--r--libsoup/soup-auth.h8
-rw-r--r--libsoup/soup-connection-auth.c173
-rw-r--r--libsoup/soup-connection-auth.h52
-rw-r--r--libsoup/soup-message-private.h5
-rw-r--r--libsoup/soup-message.c13
-rw-r--r--libsoup/soup-session.c1
14 files changed, 483 insertions, 217 deletions
diff --git a/docs/reference/Makefile.am b/docs/reference/Makefile.am
index 74a6d925..17ae6caf 100644
--- a/docs/reference/Makefile.am
+++ b/docs/reference/Makefile.am
@@ -32,7 +32,7 @@ CFILE_GLOB=
IGNORE_HFILES= soup.h soup-marshal.h soup-enum-types.h \
soup-message-private.h soup-session-private.h \
soup-auth-basic.h soup-auth-digest.h soup-auth-ntlm.h \
- soup-connection.h soup-auth-manager.h soup-auth-manager-ntlm.h \
+ soup-connection.h soup-auth-manager.h soup-connection-auth.h \
soup-message-queue.h soup-path-map.h soup-gnome-features.h \
soup-proxy-resolver.h soup-proxy-resolver-gnome.h \
soup-proxy-resolver-static.h soup-directory-input-stream.h \
diff --git a/docs/reference/libsoup-2.4-sections.txt b/docs/reference/libsoup-2.4-sections.txt
index 963cbce1..e1bbbffb 100644
--- a/docs/reference/libsoup-2.4-sections.txt
+++ b/docs/reference/libsoup-2.4-sections.txt
@@ -536,6 +536,7 @@ soup_auth_get_info
<SUBSECTION>
soup_auth_authenticate
soup_auth_is_authenticated
+soup_auth_is_ready
<SUBSECTION>
soup_auth_get_authorization
soup_auth_get_protection_space
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 7a9d6fca..59747441 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -119,6 +119,8 @@ libsoup_2_4_la_SOURCES = \
soup-client-input-stream.c \
soup-connection.h \
soup-connection.c \
+ soup-connection-auth.h \
+ soup-connection-auth.c \
soup-content-decoder.c \
soup-content-processor.h \
soup-content-processor.c \
diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
index 8ec30b14..521a5f71 100644
--- a/libsoup/libsoup-2.4.sym
+++ b/libsoup/libsoup-2.4.sym
@@ -53,6 +53,7 @@ soup_auth_get_type
soup_auth_has_saved_password
soup_auth_is_authenticated
soup_auth_is_for_proxy
+soup_auth_is_ready
soup_auth_new
soup_auth_ntlm_get_type
soup_auth_save_password
diff --git a/libsoup/soup-auth-manager.c b/libsoup/soup-auth-manager.c
index d284826e..773291b6 100644
--- a/libsoup/soup-auth-manager.c
+++ b/libsoup/soup-auth-manager.c
@@ -13,6 +13,7 @@
#include "soup-auth-manager.h"
#include "soup.h"
+#include "soup-connection-auth.h"
#include "soup-marshal.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
@@ -36,13 +37,10 @@ G_DEFINE_TYPE_WITH_CODE (SoupAuthManager, soup_auth_manager, G_TYPE_OBJECT,
typedef struct {
SoupSession *session;
GPtrArray *auth_types;
- gboolean has_ntlm;
+ gboolean auto_ntlm;
SoupAuth *proxy_auth;
GHashTable *auth_hosts;
-
- GHashTable *connauths;
- GHashTable *sockets_by_msg;
} SoupAuthManagerPrivate;
#define SOUP_AUTH_MANAGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_MANAGER, SoupAuthManagerPrivate))
@@ -53,6 +51,8 @@ typedef struct {
} SoupAuthHost;
static void soup_auth_host_free (SoupAuthHost *host);
+static SoupAuth *record_auth_for_uri (SoupAuthManagerPrivate *priv,
+ SoupURI *uri, SoupAuth *auth);
static void
soup_auth_manager_init (SoupAuthManager *manager)
@@ -64,10 +64,6 @@ soup_auth_manager_init (SoupAuthManager *manager)
soup_uri_host_equal,
NULL,
(GDestroyNotify)soup_auth_host_free);
-
- priv->connauths = g_hash_table_new_full (NULL, NULL,
- NULL, g_object_unref);
- priv->sockets_by_msg = g_hash_table_new (NULL, NULL);
}
static void
@@ -81,9 +77,6 @@ soup_auth_manager_finalize (GObject *object)
g_clear_object (&priv->proxy_auth);
- g_hash_table_destroy (priv->connauths);
- g_hash_table_destroy (priv->sockets_by_msg);
-
G_OBJECT_CLASS (soup_auth_manager_parent_class)->finalize (object);
}
@@ -133,7 +126,7 @@ soup_auth_manager_add_feature (SoupSessionFeature *feature, GType type)
g_ptr_array_sort (priv->auth_types, auth_type_compare_func);
if (type == SOUP_TYPE_AUTH_NTLM)
- priv->has_ntlm = TRUE;
+ priv->auto_ntlm = TRUE;
return TRUE;
}
@@ -153,7 +146,7 @@ soup_auth_manager_remove_feature (SoupSessionFeature *feature, GType type)
for (i = 0; i < priv->auth_types->len; i++) {
if (priv->auth_types->pdata[i] == (gpointer)auth_class) {
if (type == SOUP_TYPE_AUTH_NTLM)
- priv->has_ntlm = FALSE;
+ priv->auto_ntlm = FALSE;
g_ptr_array_remove_index (priv->auth_types, i);
return TRUE;
@@ -349,10 +342,9 @@ check_auth (SoupMessage *msg, SoupAuth *auth)
}
static SoupAuthHost *
-get_auth_host_for_message (SoupAuthManagerPrivate *priv, SoupMessage *msg)
+get_auth_host_for_uri (SoupAuthManagerPrivate *priv, SoupURI *uri)
{
SoupAuthHost *host;
- SoupURI *uri = soup_message_get_uri (msg);
host = g_hash_table_lookup (priv->auth_hosts, uri);
if (host)
@@ -375,14 +367,30 @@ soup_auth_host_free (SoupAuthHost *host)
g_slice_free (SoupAuthHost, host);
}
+static gboolean
+make_auto_ntlm_auth (SoupAuthManagerPrivate *priv, SoupAuthHost *host)
+{
+ SoupAuth *auth;
+
+ if (!priv->auto_ntlm)
+ return FALSE;
+
+ auth = g_object_new (SOUP_TYPE_AUTH_NTLM,
+ SOUP_AUTH_HOST, host->uri->host,
+ NULL);
+ record_auth_for_uri (priv, host->uri, auth);
+ g_object_unref (auth);
+ return TRUE;
+}
+
static SoupAuth *
lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
{
SoupAuthHost *host;
const char *path, *realm;
- host = get_auth_host_for_message (priv, msg);
- if (!host->auth_realms)
+ host = get_auth_host_for_uri (priv, soup_message_get_uri (msg));
+ if (!host->auth_realms && !make_auto_ntlm_auth (priv, host))
return NULL;
path = soup_message_get_uri (msg)->path;
@@ -395,7 +403,7 @@ lookup_auth (SoupAuthManagerPrivate *priv, SoupMessage *msg)
return NULL;
}
-static gboolean
+static void
authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
SoupMessage *msg, gboolean prior_auth_failed,
gboolean proxy, gboolean can_interact)
@@ -416,7 +424,7 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
uri = NULL;
if (!uri)
- return FALSE;
+ return;
} else
uri = soup_message_get_uri (msg);
@@ -431,110 +439,19 @@ authenticate_auth (SoupAuthManager *manager, SoupAuth *auth,
g_signal_emit (manager, signals[AUTHENTICATE], 0,
msg, auth, prior_auth_failed);
}
-
- return soup_auth_is_authenticated (auth);
-}
-
-static void
-delete_connauth (SoupSocket *socket, gpointer user_data)
-{
- SoupAuthManagerPrivate *priv = user_data;
-
- g_hash_table_remove (priv->connauths, socket);
- g_signal_handlers_disconnect_by_func (socket, delete_connauth, priv);
-}
-
-static SoupAuth *
-get_connauth (SoupAuthManagerPrivate *priv, SoupSocket *socket)
-{
- SoupAuth *auth;
-
- if (!priv->has_ntlm)
- return NULL;
-
- auth = g_hash_table_lookup (priv->connauths, socket);
- if (!auth) {
- auth = g_object_new (SOUP_TYPE_AUTH_NTLM, NULL);
- g_hash_table_insert (priv->connauths, socket, auth);
- g_signal_connect (socket, "disconnected",
- G_CALLBACK (delete_connauth), priv);
- }
-
- return auth;
-}
-
-static void
-unset_socket (SoupMessage *msg, gpointer user_data)
-{
- SoupAuthManagerPrivate *priv = user_data;
-
- g_hash_table_remove (priv->sockets_by_msg, msg);
- g_signal_handlers_disconnect_by_func (msg, unset_socket, priv);
-}
-
-static void
-set_socket_for_msg (SoupAuthManagerPrivate *priv,
- SoupMessage *msg, SoupSocket *sock)
-{
- if (!g_hash_table_lookup (priv->sockets_by_msg, msg)) {
- g_signal_connect (msg, "finished",
- G_CALLBACK (unset_socket), priv);
- g_signal_connect (msg, "restarted",
- G_CALLBACK (unset_socket), priv);
- }
- g_hash_table_insert (priv->sockets_by_msg, msg, sock);
}
static SoupAuth *
-get_connauth_for_msg (SoupAuthManagerPrivate *priv, SoupMessage *msg)
-{
- SoupSocket *sock;
-
- sock = g_hash_table_lookup (priv->sockets_by_msg, msg);
- if (sock)
- return g_hash_table_lookup (priv->connauths, sock);
- else
- return NULL;
-}
-
-static void
-auth_got_headers (SoupMessage *msg, gpointer manager)
+record_auth_for_uri (SoupAuthManagerPrivate *priv, SoupURI *uri,
+ SoupAuth *auth)
{
- SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
SoupAuthHost *host;
- SoupAuth *auth, *prior_auth, *old_auth;
+ SoupAuth *old_auth;
const char *path;
char *auth_info, *old_auth_info;
GSList *pspace, *p;
- gboolean prior_auth_failed = FALSE;
-
- auth = get_connauth_for_msg (priv, msg);
- if (auth) {
- const char *header;
-
- header = auth_header_for_message (msg);
- if (header && soup_auth_update (auth, msg, header)) {
- if (!soup_auth_is_authenticated (auth)) {
- g_signal_emit (manager, signals[AUTHENTICATE], 0,
- msg, auth, FALSE);
- }
- return;
- }
- }
- host = get_auth_host_for_message (priv, msg);
-
- /* See if we used auth last time */
- prior_auth = soup_message_get_auth (msg);
- if (prior_auth && check_auth (msg, prior_auth)) {
- auth = prior_auth;
- if (!soup_auth_is_authenticated (auth))
- prior_auth_failed = TRUE;
- } else {
- auth = create_auth (priv, msg);
- if (!auth)
- return;
- }
+ host = get_auth_host_for_uri (priv, uri);
auth_info = soup_auth_get_info (auth);
if (!host->auth_realms) {
@@ -544,7 +461,7 @@ auth_got_headers (SoupMessage *msg, gpointer manager)
}
/* Record where this auth realm is used. */
- pspace = soup_auth_get_protection_space (auth, soup_message_get_uri (msg));
+ pspace = soup_auth_get_protection_space (auth, uri);
for (p = pspace; p; p = p->next) {
path = p->data;
old_auth_info = soup_path_map_lookup (host->auth_realms, path);
@@ -566,16 +483,39 @@ auth_got_headers (SoupMessage *msg, gpointer manager)
old_auth = g_hash_table_lookup (host->auths, auth_info);
if (old_auth) {
g_free (auth_info);
- if (auth != old_auth && auth != prior_auth) {
- g_object_unref (auth);
- auth = old_auth;
- }
+ return old_auth;
+ } else {
+ g_hash_table_insert (host->auths, auth_info,
+ g_object_ref (auth));
+ return auth;
+ }
+}
+
+static void
+auth_got_headers (SoupMessage *msg, gpointer manager)
+{
+ SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
+ SoupAuth *auth, *prior_auth, *new_auth;
+ gboolean prior_auth_failed = FALSE;
+
+ /* See if we used auth last time */
+ prior_auth = soup_message_get_auth (msg);
+ if (prior_auth && check_auth (msg, prior_auth)) {
+ auth = g_object_ref (prior_auth);
+ if (!soup_auth_is_ready (auth, msg))
+ prior_auth_failed = TRUE;
} else {
- g_hash_table_insert (host->auths, auth_info, auth);
+ auth = create_auth (priv, msg);
+ if (!auth)
+ return;
}
+ new_auth = record_auth_for_uri (priv, soup_message_get_uri (msg),
+ auth);
+ g_object_unref (auth);
+
/* If we need to authenticate, try to do it. */
- authenticate_auth (manager, auth, msg,
+ authenticate_auth (manager, new_auth, msg,
prior_auth_failed, FALSE, TRUE);
}
@@ -585,16 +525,17 @@ auth_got_body (SoupMessage *msg, gpointer manager)
SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
SoupAuth *auth;
- auth = get_connauth_for_msg (priv, msg);
- if (auth && soup_auth_is_authenticated (auth)) {
- SoupMessageFlags flags;
+ auth = lookup_auth (priv, msg);
+ if (auth && soup_auth_is_ready (auth, msg)) {
+ if (SOUP_IS_CONNECTION_AUTH (auth)) {
+ SoupMessageFlags flags;
+
+ flags = soup_message_get_flags (msg);
+ soup_message_set_flags (msg, flags & ~SOUP_MESSAGE_NEW_CONNECTION);
+ }
- flags = soup_message_get_flags (msg);
- soup_message_set_flags (msg, flags & ~SOUP_MESSAGE_NEW_CONNECTION);
- } else
- auth = lookup_auth (priv, msg);
- if (auth && soup_auth_is_authenticated (auth))
soup_session_requeue_message (priv->session, msg);
+ }
}
static void
@@ -607,7 +548,7 @@ proxy_auth_got_headers (SoupMessage *msg, gpointer manager)
/* See if we used auth last time */
prior_auth = soup_message_get_proxy_auth (msg);
if (prior_auth && check_auth (msg, prior_auth)) {
- if (!soup_auth_is_authenticated (prior_auth))
+ if (!soup_auth_is_ready (prior_auth, msg))
prior_auth_failed = TRUE;
}
@@ -628,7 +569,7 @@ proxy_auth_got_body (SoupMessage *msg, gpointer manager)
SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
SoupAuth *auth = priv->proxy_auth;
- if (auth && soup_auth_is_authenticated (auth))
+ if (auth && soup_auth_is_ready (auth, msg))
soup_session_requeue_message (priv->session, msg);
}
@@ -662,18 +603,20 @@ soup_auth_manager_request_started (SoupSessionFeature *feature,
SoupAuthManagerPrivate *priv = SOUP_AUTH_MANAGER_GET_PRIVATE (manager);
SoupAuth *auth;
- set_socket_for_msg (priv, msg, socket);
auth = lookup_auth (priv, msg);
if (auth) {
- if (!authenticate_auth (manager, auth, msg, FALSE, FALSE, FALSE))
+ authenticate_auth (manager, auth, msg, FALSE, FALSE, FALSE);
+ if (!soup_auth_is_ready (auth, msg))
auth = NULL;
- } else
- auth = get_connauth (priv, socket);
+ }
soup_message_set_auth (msg, auth);
auth = priv->proxy_auth;
- if (!auth || !authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE))
- auth = NULL;
+ if (auth) {
+ authenticate_auth (manager, auth, msg, FALSE, TRUE, FALSE);
+ if (!soup_auth_is_ready (auth, msg))
+ auth = NULL;
+ }
soup_message_set_proxy_auth (msg, auth);
}
diff --git a/libsoup/soup-auth-ntlm.c b/libsoup/soup-auth-ntlm.c
index 63f8c8b2..367a3cac 100644
--- a/libsoup/soup-auth-ntlm.c
+++ b/libsoup/soup-auth-ntlm.c
@@ -44,10 +44,14 @@ typedef enum {
typedef struct {
SoupNTLMState state;
- char *username;
- guchar nt_hash[21], lm_hash[21];
- char *nonce, *domain;
+ char *nonce;
char *response_header;
+} SoupNTLMConnectionState;
+
+typedef struct {
+ char *username, *domain;
+ guchar nt_hash[21], lm_hash[21];
+ gboolean authenticated;
#ifdef USE_NTLM_AUTH
/* Use Samba's 'winbind' daemon to support NTLM single-sign-on,
@@ -80,19 +84,15 @@ static void sso_ntlm_close (SoupAuthNTLMPrivate *priv);
* Since: 2.34
*/
-G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_AUTH)
+G_DEFINE_TYPE (SoupAuthNTLM, soup_auth_ntlm, SOUP_TYPE_CONNECTION_AUTH)
static void
soup_auth_ntlm_init (SoupAuthNTLM *ntlm)
{
- SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (ntlm);
#ifdef USE_NTLM_AUTH
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (ntlm);
const char *username = NULL, *slash;
-#endif
-
- priv->state = SOUP_NTLM_NEW;
-#ifdef USE_NTLM_AUTH
priv->sso_available = TRUE;
priv->fd_in = -1;
priv->fd_out = -1;
@@ -123,9 +123,6 @@ soup_auth_ntlm_finalize (GObject *object)
memset (priv->nt_hash, 0, sizeof (priv->nt_hash));
memset (priv->lm_hash, 0, sizeof (priv->lm_hash));
- g_free (priv->nonce);
- g_free (priv->response_header);
-
#ifdef USE_NTLM_AUTH
sso_ntlm_close (priv);
#endif
@@ -255,75 +252,89 @@ wrfinish:
}
#endif /* USE_NTLM_AUTH */
+static gpointer
+soup_auth_ntlm_create_connection_state (SoupConnectionAuth *auth)
+{
+ SoupNTLMConnectionState *conn;
+
+ conn = g_slice_new0 (SoupNTLMConnectionState);
+ conn->state = SOUP_NTLM_NEW;
+
+ return conn;
+}
+
+static void
+soup_auth_ntlm_free_connection_state (SoupConnectionAuth *auth,
+ gpointer state)
+{
+ SoupNTLMConnectionState *conn = state;
+
+ g_free (conn->nonce);
+ g_free (conn->response_header);
+ g_slice_free (SoupNTLMConnectionState, conn);
+}
+
static gboolean
-soup_auth_ntlm_update (SoupAuth *auth, SoupMessage *msg,
- GHashTable *auth_params)
+soup_auth_ntlm_update_connection (SoupConnectionAuth *auth, SoupMessage *msg,
+ const char *auth_header, gpointer state)
{
SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
- GHashTableIter iter;
- gpointer key, value;
- char *header;
+ SoupNTLMConnectionState *conn = state;
gboolean success = TRUE;
- if (priv->state > SOUP_NTLM_SENT_REQUEST) {
+ if (conn->state > SOUP_NTLM_SENT_REQUEST) {
/* We already authenticated, but then got another 401.
* That means "permission denied", so don't try to
* authenticate again.
*/
- priv->state = SOUP_NTLM_FAILED;
+ conn->state = SOUP_NTLM_FAILED;
+
+ /* FIXME: we should only do this if the password never worked */
+ priv->authenticated = FALSE;
+
return FALSE;
}
- /* The header will be something like "NTLM blahblahblah===",
- * which soup_auth_update() will parse as
- * "blahblahblah" = "==". Undo that.
- */
- g_hash_table_iter_init (&iter, auth_params);
- if (!g_hash_table_iter_next (&iter, &key, &value))
+ if (!g_str_has_prefix (auth_header, "NTLM "))
return FALSE;
- if (value)
- header = g_strdup_printf ("%s=%s", (char *)key, (char *)value);
- else
- header = g_strdup (key);
-
- if (!soup_ntlm_parse_challenge (header, &priv->nonce, &priv->domain)) {
- g_free (header);
- priv->state = SOUP_NTLM_FAILED;
+
+ if (!soup_ntlm_parse_challenge (auth_header + 5, &conn->nonce, &priv->domain)) {
+ conn->state = SOUP_NTLM_FAILED;
return FALSE;
}
#ifdef USE_NTLM_AUTH
- if (priv->sso_available && priv->state == SOUP_NTLM_SENT_REQUEST) {
+ if (priv->sso_available && conn->state == SOUP_NTLM_SENT_REQUEST) {
char *input, *response;
/* Re-Initiate ntlm_auth process in case it was closed/killed abnormally */
if (!sso_ntlm_initiate (priv)) {
- priv->state = SOUP_NTLM_SSO_FAILED;
+ conn->state = SOUP_NTLM_SSO_FAILED;
success = FALSE;
goto out;
}
- input = g_strdup_printf ("TT %s\n", header);
- response = sso_ntlm_response (priv, input, priv->state);
+ input = g_strdup_printf ("TT %s\n", auth_header + 5);
+ response = sso_ntlm_response (priv, input, conn->state);
sso_ntlm_close (priv);
g_free (input);
if (!response) {
- priv->state = SOUP_NTLM_SSO_FAILED;
+ conn->state = SOUP_NTLM_SSO_FAILED;
success = FALSE;
} else if (!g_ascii_strcasecmp (response, "PW")) {
priv->sso_available = FALSE;
g_free (response);
- } else
- priv->response_header = response;
+ } else {
+ conn->response_header = response;
+ priv->authenticated = TRUE;
+ }
}
out:
#endif
- g_free (header);
-
- if (priv->state == SOUP_NTLM_SENT_REQUEST)
- priv->state = SOUP_NTLM_RECEIVED_CHALLENGE;
+ if (conn->state == SOUP_NTLM_SENT_REQUEST)
+ conn->state = SOUP_NTLM_RECEIVED_CHALLENGE;
g_object_set (G_OBJECT (auth),
SOUP_AUTH_REALM, priv->domain,
@@ -335,7 +346,16 @@ soup_auth_ntlm_update (SoupAuth *auth, SoupMessage *msg,
static GSList *
soup_auth_ntlm_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
{
- g_return_val_if_reached (NULL);
+ char *space, *p;
+
+ space = g_strdup (source_uri->path);
+
+ /* Strip query and filename component */
+ p = strrchr (space, '/');
+ if (p && p != space && p[1])
+ *p = '\0';
+
+ return g_slist_prepend (NULL, space);
}
static void
@@ -343,59 +363,66 @@ soup_auth_ntlm_authenticate (SoupAuth *auth, const char *username,
const char *password)
{
SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
- const char *slash, *domain;
+ const char *slash;
g_return_if_fail (username != NULL);
g_return_if_fail (password != NULL);
- g_return_if_fail (priv->nonce != NULL);
+
+ if (priv->username)
+ g_free (priv->username);
+ if (priv->domain)
+ g_free (priv->domain);
slash = strpbrk (username, "\\/");
if (slash) {
- if (priv->domain)
- g_free (priv->domain);
- domain = priv->domain = g_strndup (username, slash - username);
+ priv->domain = g_strndup (username, slash - username);
priv->username = g_strdup (slash + 1);
} else {
- domain = "";
+ priv->domain = g_strdup ("");
priv->username = g_strdup (username);
}
soup_ntlm_nt_hash (password, priv->nt_hash);
soup_ntlm_lanmanager_hash (password, priv->lm_hash);
- priv->response_header = soup_ntlm_response (priv->nonce,
- priv->username,
- priv->nt_hash,
- priv->lm_hash,
- NULL,
- domain);
-
- g_free (priv->nonce);
- priv->nonce = NULL;
+ priv->authenticated = TRUE;
}
static gboolean
soup_auth_ntlm_is_authenticated (SoupAuth *auth)
{
- return SOUP_AUTH_NTLM_GET_PRIVATE (auth)->username != NULL &&
- SOUP_AUTH_NTLM_GET_PRIVATE (auth)->response_header != NULL;
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+
+ return priv->authenticated;
+}
+
+static gboolean
+soup_auth_ntlm_is_connection_ready (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer state)
+{
+ SoupNTLMConnectionState *conn = state;
+
+ return conn->state != SOUP_NTLM_FAILED && conn->state != SOUP_NTLM_SSO_FAILED;
}
static char *
-soup_auth_ntlm_get_authorization (SoupAuth *auth, SoupMessage *msg)
+soup_auth_ntlm_get_connection_authorization (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer state)
{
- SoupAuthNTLMPrivate *priv =
- SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ SoupAuthNTLMPrivate *priv = SOUP_AUTH_NTLM_GET_PRIVATE (auth);
+ SoupNTLMConnectionState *conn = state;
char *header = NULL;
- switch (priv->state) {
+ switch (conn->state) {
case SOUP_NTLM_NEW:
#ifdef USE_NTLM_AUTH
if (sso_ntlm_initiate (priv)) {
- header = sso_ntlm_response (priv, "YR\n", priv->state);
+ header = sso_ntlm_response (priv, "YR\n", conn->state);
if (header) {
if (g_ascii_strcasecmp (header, "PW") != 0) {
- priv->state = SOUP_NTLM_SENT_REQUEST;
+ conn->state = SOUP_NTLM_SENT_REQUEST;
break;
} else {
g_free (header);
@@ -411,12 +438,22 @@ soup_auth_ntlm_get_authorization (SoupAuth *auth, SoupMessage *msg)
*/
#endif
header = soup_ntlm_request ();
- priv->state = SOUP_NTLM_SENT_REQUEST;
+ conn->state = SOUP_NTLM_SENT_REQUEST;
break;
case SOUP_NTLM_RECEIVED_CHALLENGE:
- header = priv->response_header;
- priv->response_header = NULL;
- priv->state = SOUP_NTLM_SENT_RESPONSE;
+ if (conn->response_header) {
+ header = conn->response_header;
+ conn->response_header = NULL;
+ } else {
+ header = soup_ntlm_response (conn->nonce,
+ priv->username,
+ priv->nt_hash,
+ priv->lm_hash,
+ NULL,
+ priv->domain);
+ }
+ g_clear_pointer (&conn->nonce, g_free);
+ conn->state = SOUP_NTLM_SENT_RESPONSE;
break;
#ifdef USE_NTLM_AUTH
case SOUP_NTLM_SSO_FAILED:
@@ -424,7 +461,7 @@ soup_auth_ntlm_get_authorization (SoupAuth *auth, SoupMessage *msg)
g_warning ("NTLM single-sign-on by using %s failed", NTLM_AUTH);
priv->sso_available = FALSE;
header = soup_ntlm_request ();
- priv->state = SOUP_NTLM_SENT_REQUEST;
+ conn->state = SOUP_NTLM_SENT_REQUEST;
break;
#endif
default:
@@ -438,6 +475,7 @@ static void
soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
{
SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_ntlm_class);
+ SoupConnectionAuthClass *connauth_class = SOUP_CONNECTION_AUTH_CLASS (auth_ntlm_class);
GObjectClass *object_class = G_OBJECT_CLASS (auth_ntlm_class);
g_type_class_add_private (auth_ntlm_class, sizeof (SoupAuthNTLMPrivate));
@@ -445,11 +483,15 @@ soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class)
auth_class->scheme_name = "NTLM";
auth_class->strength = 3;
- auth_class->update = soup_auth_ntlm_update;
auth_class->get_protection_space = soup_auth_ntlm_get_protection_space;
auth_class->authenticate = soup_auth_ntlm_authenticate;
auth_class->is_authenticated = soup_auth_ntlm_is_authenticated;
- auth_class->get_authorization = soup_auth_ntlm_get_authorization;
+
+ connauth_class->create_connection_state = soup_auth_ntlm_create_connection_state;
+ connauth_class->free_connection_state = soup_auth_ntlm_free_connection_state;
+ connauth_class->update_connection = soup_auth_ntlm_update_connection;
+ connauth_class->get_connection_authorization = soup_auth_ntlm_get_connection_authorization;
+ connauth_class->is_connection_ready = soup_auth_ntlm_is_connection_ready;
object_class->finalize = soup_auth_ntlm_finalize;
diff --git a/libsoup/soup-auth-ntlm.h b/libsoup/soup-auth-ntlm.h
index 7ed9bd3e..43c40856 100644
--- a/libsoup/soup-auth-ntlm.h
+++ b/libsoup/soup-auth-ntlm.h
@@ -6,7 +6,7 @@
#ifndef SOUP_AUTH_NTLM_H
#define SOUP_AUTH_NTLM_H 1
-#include "soup-auth.h"
+#include "soup-connection-auth.h"
#define SOUP_AUTH_NTLM(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLM))
#define SOUP_AUTH_NTLM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
@@ -15,12 +15,12 @@
#define SOUP_AUTH_NTLM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NTLM, SoupAuthNTLMClass))
typedef struct {
- SoupAuth parent;
+ SoupConnectionAuth parent;
} SoupAuthNTLM;
typedef struct {
- SoupAuthClass parent_class;
+ SoupConnectionAuthClass parent_class;
} SoupAuthNTLMClass;
diff --git a/libsoup/soup-auth.c b/libsoup/soup-auth.c
index 77fb45e9..5ce92d55 100644
--- a/libsoup/soup-auth.c
+++ b/libsoup/soup-auth.c
@@ -459,6 +459,33 @@ soup_auth_get_authorization (SoupAuth *auth, SoupMessage *msg)
}
/**
+ * soup_auth_is_ready:
+ * @auth: a #SoupAuth
+ * @msg: a #SoupMessage
+ *
+ * Tests if @auth is ready to make a request for @msg with. For most
+ * auths, this is equivalent to soup_auth_is_authenticated(), but for
+ * some auth types (eg, NTLM), the auth may be sendable (eg, as an
+ * authentication request) even before it is authenticated.
+ *
+ * Return value: %TRUE if @auth is ready to make a request with.
+ *
+ * Since: 2.42
+ **/
+gboolean
+soup_auth_is_ready (SoupAuth *auth,
+ SoupMessage *msg)
+{
+ g_return_val_if_fail (SOUP_IS_AUTH (auth), TRUE);
+ g_return_val_if_fail (SOUP_IS_MESSAGE (msg), TRUE);
+
+ if (SOUP_AUTH_GET_CLASS (auth)->is_ready)
+ return SOUP_AUTH_GET_CLASS (auth)->is_ready (auth, msg);
+ else
+ return SOUP_AUTH_GET_CLASS (auth)->is_authenticated (auth);
+}
+
+/**
* soup_auth_get_protection_space:
* @auth: a #SoupAuth
* @source_uri: the URI of the request that @auth was generated in
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
index 8abda8e0..824857e2 100644
--- a/libsoup/soup-auth.h
+++ b/libsoup/soup-auth.h
@@ -44,8 +44,11 @@ typedef struct {
char * (*get_authorization) (SoupAuth *auth,
SoupMessage *msg);
+
+ gboolean (*is_ready) (SoupAuth *auth,
+ SoupMessage *msg);
+
/* Padding for future expansion */
- void (*_libsoup_reserved1) (void);
void (*_libsoup_reserved2) (void);
void (*_libsoup_reserved3) (void);
void (*_libsoup_reserved4) (void);
@@ -76,6 +79,9 @@ void soup_auth_authenticate (SoupAuth *auth,
const char *username,
const char *password);
gboolean soup_auth_is_authenticated (SoupAuth *auth);
+SOUP_AVAILABLE_IN_2_42
+gboolean soup_auth_is_ready (SoupAuth *auth,
+ SoupMessage *msg);
char *soup_auth_get_authorization (SoupAuth *auth,
SoupMessage *msg);
diff --git a/libsoup/soup-connection-auth.c b/libsoup/soup-connection-auth.c
new file mode 100644
index 00000000..fc327335
--- /dev/null
+++ b/libsoup/soup-connection-auth.c
@@ -0,0 +1,173 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-connection-auth.c: Abstract base class for hacky Microsoft
+ * connection-based auth mechanisms (NTLM and Negotiate)
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <ctype.h>
+#include <string.h>
+
+#include "soup-connection-auth.h"
+#include "soup.h"
+#include "soup-connection.h"
+#include "soup-message-private.h"
+
+G_DEFINE_ABSTRACT_TYPE (SoupConnectionAuth, soup_connection_auth, SOUP_TYPE_AUTH)
+
+struct SoupConnectionAuthPrivate {
+ GHashTable *conns;
+};
+
+static void
+soup_connection_auth_init (SoupConnectionAuth *auth)
+{
+ auth->priv = G_TYPE_INSTANCE_GET_PRIVATE (auth, SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuthPrivate);
+
+ auth->priv->conns = g_hash_table_new (NULL, NULL);
+}
+
+static void connection_disconnected (SoupConnection *conn, gpointer user_data);
+
+static void
+soup_connection_auth_free_connection_state (SoupConnectionAuth *auth,
+ SoupConnection *conn,
+ gpointer state)
+{
+ g_signal_handlers_disconnect_by_func (conn, G_CALLBACK (connection_disconnected), auth);
+ SOUP_CONNECTION_AUTH_GET_CLASS (auth)->free_connection_state (auth, state);
+}
+
+static void
+connection_disconnected (SoupConnection *conn, gpointer user_data)
+{
+ SoupConnectionAuth *auth = user_data;
+ gpointer state;
+
+ state = g_hash_table_lookup (auth->priv->conns, conn);
+ g_hash_table_remove (auth->priv->conns, conn);
+ soup_connection_auth_free_connection_state (auth, conn, state);
+}
+
+static void
+soup_connection_auth_finalize (GObject *object)
+{
+ SoupConnectionAuth *auth = SOUP_CONNECTION_AUTH (object);
+ GHashTableIter iter;
+ gpointer conn, state;
+
+ g_hash_table_iter_init (&iter, auth->priv->conns);
+ while (g_hash_table_iter_next (&iter, &conn, &state)) {
+ soup_connection_auth_free_connection_state (auth, conn, state);
+ g_hash_table_iter_remove (&iter);
+ }
+ g_hash_table_destroy (auth->priv->conns);
+
+ G_OBJECT_CLASS (soup_connection_auth_parent_class)->finalize (object);
+}
+
+static gpointer
+get_connection_state_for_message (SoupConnectionAuth *auth, SoupMessage *msg)
+{
+ SoupConnection *conn;
+ gpointer state;
+
+ conn = soup_message_get_connection (msg);
+ state = g_hash_table_lookup (auth->priv->conns, conn);
+ if (state)
+ return state;
+
+ state = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->create_connection_state (auth);
+ if (conn) {
+ g_signal_connect (conn, "disconnected",
+ G_CALLBACK (connection_disconnected), auth);
+ }
+
+ g_hash_table_insert (auth->priv->conns, conn, state);
+ return state;
+}
+
+static gboolean
+soup_connection_auth_update (SoupAuth *auth,
+ SoupMessage *msg,
+ GHashTable *auth_params)
+{
+ SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
+ gpointer conn = get_connection_state_for_message (cauth, msg);
+ GHashTableIter iter;
+ GString *auth_header;
+ gpointer key, value;
+ gboolean result;
+
+ /* Recreate @auth_header out of @auth_params. If the
+ * base64 data ended with 1 or more "="s, then it
+ * will have been parsed as key=value. Otherwise
+ * it will all have been parsed as key, and value
+ * will be %NULL.
+ */
+ auth_header = g_string_new (soup_auth_get_scheme_name (auth));
+ g_hash_table_iter_init (&iter, auth_params);
+ if (g_hash_table_iter_next (&iter, &key, &value)) {
+ if (value) {
+ g_string_append_printf (auth_header, " %s=%s",
+ (char *)key,
+ (char *)value);
+ } else {
+ g_string_append_printf (auth_header, " %s",
+ (char *)key);
+ }
+
+ if (g_hash_table_iter_next (&iter, &key, &value)) {
+ g_string_free (auth_header, TRUE);
+ return FALSE;
+ }
+ }
+
+ result = SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
+ update_connection (cauth, msg, auth_header->str, conn);
+
+ g_string_free (auth_header, TRUE);
+ return result;
+}
+
+static char *
+soup_connection_auth_get_authorization (SoupAuth *auth,
+ SoupMessage *msg)
+{
+ SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
+ gpointer conn = get_connection_state_for_message (cauth, msg);
+
+ return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
+ get_connection_authorization (cauth, msg, conn);
+}
+
+static gboolean
+soup_connection_auth_is_ready (SoupAuth *auth,
+ SoupMessage *msg)
+{
+ SoupConnectionAuth *cauth = SOUP_CONNECTION_AUTH (auth);
+ gpointer conn = get_connection_state_for_message (cauth, msg);
+
+ return SOUP_CONNECTION_AUTH_GET_CLASS (auth)->
+ is_connection_ready (SOUP_CONNECTION_AUTH (auth), msg, conn);
+}
+
+static void
+soup_connection_auth_class_init (SoupConnectionAuthClass *connauth_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (connauth_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (connauth_class);
+
+ g_type_class_add_private (connauth_class, sizeof (SoupConnectionAuthPrivate));
+
+ auth_class->update = soup_connection_auth_update;
+ auth_class->get_authorization = soup_connection_auth_get_authorization;
+ auth_class->is_ready = soup_connection_auth_is_ready;
+
+ object_class->finalize = soup_connection_auth_finalize;
+}
diff --git a/libsoup/soup-connection-auth.h b/libsoup/soup-connection-auth.h
new file mode 100644
index 00000000..251ca359
--- /dev/null
+++ b/libsoup/soup-connection-auth.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef SOUP_CONNECTION_AUTH_H
+#define SOUP_CONNECTION_AUTH_H 1
+
+#include <libsoup/soup-auth.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_CONNECTION_AUTH (soup_connection_auth_get_type ())
+#define SOUP_CONNECTION_AUTH(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuth))
+#define SOUP_CONNECTION_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuthClass))
+#define SOUP_IS_CONNECTION_AUTH(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_CONNECTION_AUTH))
+#define SOUP_IS_CONNECTION_AUTH_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_CONNECTION_AUTH))
+#define SOUP_CONNECTION_AUTH_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_CONNECTION_AUTH, SoupConnectionAuthClass))
+
+typedef struct SoupConnectionAuthPrivate SoupConnectionAuthPrivate;
+
+typedef struct {
+ SoupAuth parent;
+
+ SoupConnectionAuthPrivate *priv;
+} SoupConnectionAuth;
+
+typedef struct {
+ SoupAuthClass parent_class;
+
+ gpointer (*create_connection_state) (SoupConnectionAuth *auth);
+ void (*free_connection_state) (SoupConnectionAuth *auth,
+ gpointer conn);
+
+ gboolean (*update_connection) (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ const char *auth_header,
+ gpointer conn);
+ char *(*get_connection_authorization) (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer conn);
+ gboolean (*is_connection_ready) (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer conn);
+} SoupConnectionAuthClass;
+
+SOUP_AVAILABLE_IN_2_42
+GType soup_connection_auth_get_type (void);
+
+G_END_DECLS
+
+#endif /* SOUP_CONNECTION_AUTH_H */
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 74b6e0d7..de7cb7d0 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -31,6 +31,7 @@ typedef struct {
SoupAddress *addr;
SoupAuth *auth, *proxy_auth;
+ SoupConnection *connection;
GSList *disabled_features;
@@ -138,4 +139,8 @@ GInputStream *soup_message_setup_body_istream (GInputStream *body_stream,
void soup_message_set_soup_request (SoupMessage *msg,
SoupRequest *req);
+SoupConnection *soup_message_get_connection (SoupMessage *msg);
+void soup_message_set_connection (SoupMessage *msg,
+ SoupConnection *conn);
+
#endif /* SOUP_MESSAGE_PRIVATE_H */
diff --git a/libsoup/soup-message.c b/libsoup/soup-message.c
index 7fe6db84..67b6e6f5 100644
--- a/libsoup/soup-message.c
+++ b/libsoup/soup-message.c
@@ -1260,6 +1260,19 @@ soup_message_get_proxy_auth (SoupMessage *msg)
return SOUP_MESSAGE_GET_PRIVATE (msg)->proxy_auth;
}
+SoupConnection *
+soup_message_get_connection (SoupMessage *msg)
+{
+ return SOUP_MESSAGE_GET_PRIVATE (msg)->connection;
+}
+
+void
+soup_message_set_connection (SoupMessage *msg,
+ SoupConnection *conn)
+{
+ SOUP_MESSAGE_GET_PRIVATE (msg)->connection = conn;
+}
+
/**
* soup_message_cleanup_response:
* @msg: a #SoupMessage
diff --git a/libsoup/soup-session.c b/libsoup/soup-session.c
index 63e90b3d..20e92329 100644
--- a/libsoup/soup-session.c
+++ b/libsoup/soup-session.c
@@ -1162,6 +1162,7 @@ soup_session_set_item_connection (SoupSession *session,
}
item->conn = conn;
+ soup_message_set_connection (item->msg, conn);
if (item->conn) {
g_object_ref (item->conn);