summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-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);