Blame libsoup/soup-auth-negotiate.c

Packit Service ca3877
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Packit Service ca3877
/*
Packit Service ca3877
 * soup-auth-negotiate.c: HTTP Negotiate Authentication helper
Packit Service ca3877
 *
Packit Service ca3877
 * Copyright (C) 2009,2013 Guido Guenther <agx@sigxcpu.org>
Packit Service ca3877
 * Copyright (C) 2016 Red Hat, Inc.
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
#ifdef HAVE_CONFIG_H
Packit Service ca3877
#include <config.h>
Packit Service ca3877
#endif
Packit Service ca3877
Packit Service ca3877
#include <string.h>
Packit Service ca3877
Packit Service ca3877
#ifdef LIBSOUP_HAVE_GSSAPI
Packit Service ca3877
#include <gssapi/gssapi.h>
Packit Service ca3877
#endif /* LIBSOUP_HAVE_GSSAPI */
Packit Service ca3877
Packit Service ca3877
#include "soup-auth-negotiate.h"
Packit Service ca3877
#include "soup-headers.h"
Packit Service ca3877
#include "soup-message.h"
Packit Service ca3877
#include "soup-message-private.h"
Packit Service ca3877
#include "soup-misc-private.h"
Packit Service ca3877
#include "soup-uri.h"
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_auth_negotiate_supported:
Packit Service ca3877
 *
Packit Service ca3877
 * Indicates whether libsoup was built with GSSAPI support. If this is
Packit Service ca3877
 * %FALSE, %SOUP_TYPE_AUTH_NEGOTIATE will still be defined and can
Packit Service ca3877
 * still be added to a #SoupSession, but libsoup will never attempt to
Packit Service ca3877
 * actually use this auth type.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.54
Packit Service ca3877
 */
Packit Service ca3877
gboolean
Packit Service ca3877
soup_auth_negotiate_supported (void)
Packit Service ca3877
{
Packit Service ca3877
#ifdef LIBSOUP_HAVE_GSSAPI
Packit Service ca3877
	return TRUE;
Packit Service ca3877
#else
Packit Service ca3877
	return FALSE;
Packit Service ca3877
#endif
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
#define AUTH_GSS_ERROR      -1
Packit Service ca3877
#define AUTH_GSS_COMPLETE    1
Packit Service ca3877
#define AUTH_GSS_CONTINUE    0
Packit Service ca3877
Packit Service ca3877
typedef enum {
Packit Service ca3877
	SOUP_NEGOTIATE_NEW,
Packit Service ca3877
	SOUP_NEGOTIATE_RECEIVED_CHALLENGE, /* received initial negotiate header */
Packit Service ca3877
	SOUP_NEGOTIATE_SENT_RESPONSE,      /* sent response to server */
Packit Service ca3877
	SOUP_NEGOTIATE_FAILED
Packit Service ca3877
} SoupNegotiateState;
Packit Service ca3877
Packit Service ca3877
typedef struct {
Packit Service ca3877
	gboolean initialized;
Packit Service ca3877
	gchar *response_header;
Packit Service ca3877
Packit Service ca3877
#ifdef LIBSOUP_HAVE_GSSAPI
Packit Service ca3877
	gss_ctx_id_t context;
Packit Service ca3877
	gss_name_t   server_name;
Packit Service ca3877
#endif
Packit Service ca3877
Packit Service ca3877
	SoupNegotiateState state;
Packit Service ca3877
} SoupNegotiateConnectionState;
Packit Service ca3877
Packit Service ca3877
typedef struct {
Packit Service ca3877
	gboolean is_authenticated;
Packit Service ca3877
} SoupAuthNegotiatePrivate;
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SOUP_TYPE_AUTH_NEGOTIATE:
Packit Service ca3877
 *
Packit Service ca3877
 * A #GType corresponding to HTTP-based GSS-Negotiate authentication.
Packit Service ca3877
 * #SoupSessions do not support this type by default; if you want to
Packit Service ca3877
 * enable support for it, call soup_session_add_feature_by_type(),
Packit Service ca3877
 * passing %SOUP_TYPE_AUTH_NEGOTIATE.
Packit Service ca3877
 *
Packit Service ca3877
 * This auth type will only work if libsoup was compiled with GSSAPI
Packit Service ca3877
 * support; you can check soup_auth_negotiate_supported() to see if it
Packit Service ca3877
 * was.
Packit Service ca3877
 *
Packit Service ca3877
 * Since: 2.54
Packit Service ca3877
 */
Packit Service ca3877
G_DEFINE_TYPE_WITH_PRIVATE (SoupAuthNegotiate, soup_auth_negotiate, SOUP_TYPE_CONNECTION_AUTH)
Packit Service ca3877
Packit Service ca3877
#ifdef LIBSOUP_HAVE_GSSAPI
Packit Service ca3877
static gboolean check_auth_trusted_uri (SoupConnectionAuth *auth,
Packit Service ca3877
					SoupMessage *msg);
Packit Service ca3877
static gboolean soup_gss_build_response (SoupNegotiateConnectionState *conn,
Packit Service ca3877
					 SoupAuth *auth, GError **err);
Packit Service ca3877
static void soup_gss_client_cleanup (SoupNegotiateConnectionState *conn);
Packit Service ca3877
static gboolean soup_gss_client_init (SoupNegotiateConnectionState *conn,
Packit Service ca3877
				      const char *host, GError **err);
Packit Service ca3877
static int soup_gss_client_step (SoupNegotiateConnectionState *conn,
Packit Service ca3877
				 const char *host, GError **err);
Packit Service ca3877
Packit Service ca3877
static GSList *trusted_uris;
Packit Service ca3877
static GSList *blacklisted_uris;
Packit Service ca3877
Packit Service ca3877
static void parse_uris_from_env_variable (const gchar *env_variable, GSList **list);
Packit Service ca3877
Packit Service ca3877
static void check_server_response (SoupMessage *msg, gpointer auth);
Packit Service ca3877
Packit Service ca3877
static const char spnego_OID[] = "\x2b\x06\x01\x05\x05\x02";
Packit Service ca3877
static const gss_OID_desc gss_mech_spnego = { sizeof (spnego_OID) - 1, (void *) &spnego_OID };
Packit Service ca3877
Packit Service ca3877
static gpointer
Packit Service ca3877
soup_auth_negotiate_create_connection_state (SoupConnectionAuth *auth)
Packit Service ca3877
{
Packit Service ca3877
	SoupNegotiateConnectionState *conn;
Packit Service ca3877
Packit Service ca3877
	conn = g_slice_new0 (SoupNegotiateConnectionState);
Packit Service ca3877
	conn->state = SOUP_NEGOTIATE_NEW;
Packit Service ca3877
Packit Service ca3877
	return conn;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
free_connection_state_data (SoupNegotiateConnectionState *conn)
Packit Service ca3877
{
Packit Service ca3877
	soup_gss_client_cleanup (conn);
Packit Service ca3877
	g_free (conn->response_header);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_auth_negotiate_free_connection_state (SoupConnectionAuth *auth,
Packit Service ca3877
					   gpointer state)
Packit Service ca3877
{
Packit Service ca3877
	SoupNegotiateConnectionState *conn = state;
Packit Service ca3877
Packit Service ca3877
	free_connection_state_data (conn);
Packit Service ca3877
Packit Service ca3877
	g_slice_free (SoupNegotiateConnectionState, conn);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static GSList *
Packit Service ca3877
soup_auth_negotiate_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
Packit Service ca3877
{
Packit Service ca3877
	char *space, *p;
Packit Service ca3877
Packit Service ca3877
	space = g_strdup (source_uri->path);
Packit Service ca3877
Packit Service ca3877
	/* Strip filename component */
Packit Service ca3877
	p = strrchr (space, '/');
Packit Service ca3877
	if (p && p == space && p[1])
Packit Service ca3877
		p[1] = '\0';
Packit Service ca3877
	else if (p && p[1])
Packit Service ca3877
		*p = '\0';
Packit Service ca3877
Packit Service ca3877
	return g_slist_prepend (NULL, space);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_auth_negotiate_authenticate (SoupAuth *auth, const char *username,
Packit Service ca3877
				  const char *password)
Packit Service ca3877
{
Packit Service ca3877
	SoupAuthNegotiate *negotiate = SOUP_AUTH_NEGOTIATE (auth);
Packit Service ca3877
	SoupAuthNegotiatePrivate *priv = soup_auth_negotiate_get_instance_private (negotiate);
Packit Service ca3877
Packit Service ca3877
	/* It is not possible to authenticate with username and password. */
Packit Service ca3877
	priv->is_authenticated = FALSE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
soup_auth_negotiate_is_authenticated (SoupAuth *auth)
Packit Service ca3877
{
Packit Service ca3877
	SoupAuthNegotiate *negotiate = SOUP_AUTH_NEGOTIATE (auth);
Packit Service ca3877
	SoupAuthNegotiatePrivate *priv = soup_auth_negotiate_get_instance_private (negotiate);
Packit Service ca3877
Packit Service ca3877
	/* We are authenticated just in case we received the GSS_S_COMPLETE. */
Packit Service ca3877
	return priv->is_authenticated;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
soup_auth_negotiate_can_authenticate (SoupAuth *auth)
Packit Service ca3877
{
Packit Service ca3877
	return FALSE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static char *
Packit Service ca3877
soup_auth_negotiate_get_connection_authorization (SoupConnectionAuth *auth,
Packit Service ca3877
						  SoupMessage *msg,
Packit Service ca3877
						  gpointer state)
Packit Service ca3877
{
Packit Service ca3877
	SoupNegotiateConnectionState *conn = state;
Packit Service ca3877
	char *header = NULL;
Packit Service ca3877
Packit Service ca3877
	if (conn->state == SOUP_NEGOTIATE_NEW) {
Packit Service ca3877
		GError *err = NULL;
Packit Service ca3877
Packit Service ca3877
		if (!check_auth_trusted_uri (auth, msg)) {
Packit Service ca3877
			conn->state = SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
			return NULL;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		if (!soup_gss_build_response (conn, SOUP_AUTH (auth), &err)) {
Packit Service ca3877
			/* FIXME: report further upward via
Packit Service ca3877
			 * soup_message_get_error_message  */
Packit Service ca3877
			if (conn->initialized)
Packit Service ca3877
				g_warning ("gssapi step failed: %s", err->message);
Packit Service ca3877
			else
Packit Service ca3877
				g_warning ("gssapi init failed: %s", err->message);
Packit Service ca3877
			conn->state = SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
			g_clear_error (&err;;
Packit Service ca3877
Packit Service ca3877
			return NULL;
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (conn->response_header) {
Packit Service ca3877
		header = conn->response_header;
Packit Service ca3877
		conn->response_header = NULL;
Packit Service ca3877
		conn->state = SOUP_NEGOTIATE_SENT_RESPONSE;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	return header;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
soup_auth_negotiate_is_connection_ready (SoupConnectionAuth *auth,
Packit Service ca3877
					 SoupMessage        *msg,
Packit Service ca3877
					 gpointer            state)
Packit Service ca3877
{
Packit Service ca3877
	SoupNegotiateConnectionState *conn = state;
Packit Service ca3877
Packit Service ca3877
	return conn->state != SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
}
Packit Service ca3877
#endif /* LIBSOUP_HAVE_GSSAPI */
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
soup_auth_negotiate_update_connection (SoupConnectionAuth *auth, SoupMessage *msg,
Packit Service ca3877
				       const char *header, gpointer state)
Packit Service ca3877
{
Packit Service ca3877
#ifdef LIBSOUP_HAVE_GSSAPI
Packit Service ca3877
	gboolean success = TRUE;
Packit Service ca3877
	SoupNegotiateConnectionState *conn = state;
Packit Service ca3877
	GError *err = NULL;
Packit Service ca3877
Packit Service ca3877
	if (!check_auth_trusted_uri (auth, msg)) {
Packit Service ca3877
		conn->state = SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
		goto out;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	/* Found negotiate header with no token, start negotiate */
Packit Service ca3877
	if (strcmp (header, "Negotiate") == 0) {
Packit Service ca3877
		/* If we were already negotiating and we get a 401
Packit Service ca3877
		 * with no token, start again. */
Packit Service ca3877
		if (conn->state == SOUP_NEGOTIATE_SENT_RESPONSE) {
Packit Service ca3877
			free_connection_state_data (conn);
Packit Service ca3877
			conn->initialized = FALSE;
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		conn->state = SOUP_NEGOTIATE_RECEIVED_CHALLENGE;
Packit Service ca3877
		if (soup_gss_build_response (conn, SOUP_AUTH (auth), &err)) {
Packit Service ca3877
			/* Connect the signal only once per message */
Packit Service ca3877
			if (!g_object_get_data (G_OBJECT (msg), "negotiate-got-headers-connected")) {
Packit Service ca3877
				/* Wait for the 2xx response to verify server response */
Packit Service ca3877
				g_signal_connect_data (msg,
Packit Service ca3877
						       "got_headers",
Packit Service ca3877
						       G_CALLBACK (check_server_response),
Packit Service ca3877
						       g_object_ref (auth),
Packit Service ca3877
						       (GClosureNotify) g_object_unref,
Packit Service ca3877
						       0);
Packit Service ca3877
				/* Mark that the signal was connected */
Packit Service ca3877
				g_object_set_data (G_OBJECT (msg),
Packit Service ca3877
						   "negotiate-got-headers-connected",
Packit Service ca3877
						   GINT_TO_POINTER (1));
Packit Service ca3877
			}
Packit Service ca3877
			goto out;
Packit Service ca3877
		} else {
Packit Service ca3877
			/* FIXME: report further upward via
Packit Service ca3877
			 * soup_message_get_error_message  */
Packit Service ca3877
			if (conn->initialized)
Packit Service ca3877
				g_warning ("gssapi step failed: %s", err->message);
Packit Service ca3877
			else
Packit Service ca3877
				g_warning ("gssapi init failed: %s", err->message);
Packit Service ca3877
			success = FALSE;
Packit Service ca3877
		}
Packit Service ca3877
	} else if (!strncmp (header, "Negotiate ", 10)) {
Packit Service ca3877
		if (soup_gss_client_step (conn, header + 10, &err) == AUTH_GSS_CONTINUE) {
Packit Service ca3877
			conn->state = SOUP_NEGOTIATE_RECEIVED_CHALLENGE;
Packit Service ca3877
			goto out;
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	conn->state = SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
 out:
Packit Service ca3877
	g_clear_error (&err;;
Packit Service ca3877
	return success;
Packit Service ca3877
#else
Packit Service ca3877
	return FALSE;
Packit Service ca3877
#endif /* LIBSOUP_HAVE_GSSAPI */
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_auth_negotiate_init (SoupAuthNegotiate *negotiate)
Packit Service ca3877
{
Packit Service ca3877
	g_object_set (G_OBJECT (negotiate), SOUP_AUTH_REALM, "", NULL);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_auth_negotiate_class_init (SoupAuthNegotiateClass *auth_negotiate_class)
Packit Service ca3877
{
Packit Service ca3877
	SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_negotiate_class);
Packit Service ca3877
	SoupConnectionAuthClass *conn_auth_class =
Packit Service ca3877
			SOUP_CONNECTION_AUTH_CLASS (auth_negotiate_class);
Packit Service ca3877
Packit Service ca3877
	auth_class->scheme_name = "Negotiate";
Packit Service ca3877
	auth_class->strength = 0;
Packit Service ca3877
Packit Service ca3877
	conn_auth_class->update_connection = soup_auth_negotiate_update_connection;
Packit Service ca3877
#ifdef LIBSOUP_HAVE_GSSAPI
Packit Service ca3877
	auth_class->strength = 7;
Packit Service ca3877
Packit Service ca3877
	conn_auth_class->create_connection_state = soup_auth_negotiate_create_connection_state;
Packit Service ca3877
	conn_auth_class->free_connection_state = soup_auth_negotiate_free_connection_state;
Packit Service ca3877
	conn_auth_class->get_connection_authorization = soup_auth_negotiate_get_connection_authorization;
Packit Service ca3877
	conn_auth_class->is_connection_ready = soup_auth_negotiate_is_connection_ready;
Packit Service ca3877
Packit Service ca3877
	auth_class->get_protection_space = soup_auth_negotiate_get_protection_space;
Packit Service ca3877
	auth_class->authenticate = soup_auth_negotiate_authenticate;
Packit Service ca3877
	auth_class->is_authenticated = soup_auth_negotiate_is_authenticated;
Packit Service ca3877
	auth_class->can_authenticate = soup_auth_negotiate_can_authenticate;
Packit Service ca3877
Packit Service ca3877
	parse_uris_from_env_variable ("SOUP_GSSAPI_TRUSTED_URIS", &trusted_uris);
Packit Service ca3877
	parse_uris_from_env_variable ("SOUP_GSSAPI_BLACKLISTED_URIS", &blacklisted_uris);
Packit Service ca3877
#endif /* LIBSOUP_HAVE_GSSAPI */
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
#ifdef LIBSOUP_HAVE_GSSAPI
Packit Service ca3877
static void
Packit Service ca3877
check_server_response (SoupMessage *msg, gpointer auth)
Packit Service ca3877
{
Packit Service ca3877
	gint ret;
Packit Service ca3877
	const char *auth_headers;
Packit Service ca3877
	GError *err = NULL;
Packit Service ca3877
	SoupAuthNegotiate *negotiate = auth;
Packit Service ca3877
	SoupAuthNegotiatePrivate *priv = soup_auth_negotiate_get_instance_private (negotiate);
Packit Service ca3877
	SoupNegotiateConnectionState *conn;
Packit Service ca3877
Packit Service ca3877
	conn = soup_connection_auth_get_connection_state_for_message (SOUP_CONNECTION_AUTH (auth), msg);
Packit Service ca3877
	if (!conn)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	if (auth != soup_message_get_auth (msg))
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	if (msg->status_code == SOUP_STATUS_UNAUTHORIZED)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	/* FIXME: need to check for proxy-auth too */
Packit Service ca3877
	auth_headers = soup_message_headers_get_one (msg->response_headers,
Packit Service ca3877
						     "WWW-Authenticate");
Packit Service ca3877
	if (!auth_headers || g_ascii_strncasecmp (auth_headers, "Negotiate ", 10) != 0) {
Packit Service ca3877
		g_warning ("Failed to parse auth header");
Packit Service ca3877
		conn->state = SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
		goto out;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	ret = soup_gss_client_step (conn, auth_headers + 10, &err;;
Packit Service ca3877
Packit Service ca3877
	switch (ret) {
Packit Service ca3877
	case AUTH_GSS_COMPLETE:
Packit Service ca3877
		priv->is_authenticated = TRUE;
Packit Service ca3877
		break;
Packit Service ca3877
	case AUTH_GSS_CONTINUE:
Packit Service ca3877
		conn->state = SOUP_NEGOTIATE_RECEIVED_CHALLENGE;
Packit Service ca3877
		break;
Packit Service ca3877
	case AUTH_GSS_ERROR:
Packit Service ca3877
		if (err)
Packit Service ca3877
			g_warning ("%s", err->message);
Packit Service ca3877
		/* Unfortunately, so many programs (curl, Firefox, ..) ignore
Packit Service ca3877
		 * the return token that is included in the response, so it is
Packit Service ca3877
		 * possible that there are servers that send back broken stuff.
Packit Service ca3877
		 * Try to behave in the right way (pass the token to
Packit Service ca3877
		 * gss_init_sec_context()), show a warning, but don't fail
Packit Service ca3877
		 * if the server returned 200. */
Packit Service ca3877
		if (msg->status_code == SOUP_STATUS_OK)
Packit Service ca3877
			priv->is_authenticated = TRUE;
Packit Service ca3877
		else
Packit Service ca3877
			conn->state = SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
		break;
Packit Service ca3877
	default:
Packit Service ca3877
		conn->state = SOUP_NEGOTIATE_FAILED;
Packit Service ca3877
	}
Packit Service ca3877
 out:
Packit Service ca3877
	g_clear_error (&err;;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/* Check if scheme://host:port from message matches the given URI. */
Packit Service ca3877
static gint
Packit Service ca3877
match_base_uri (SoupURI *list_uri, SoupURI *msg_uri)
Packit Service ca3877
{
Packit Service ca3877
	if (msg_uri->scheme != list_uri->scheme)
Packit Service ca3877
		return 1;
Packit Service ca3877
Packit Service ca3877
	if (list_uri->port && (msg_uri->port != list_uri->port))
Packit Service ca3877
		return 1;
Packit Service ca3877
Packit Service ca3877
	if (list_uri->host)
Packit Service ca3877
		return !soup_host_matches_host (msg_uri->host, list_uri->host);
Packit Service ca3877
Packit Service ca3877
	return 0;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/* Parses a comma separated list of URIS from the environment. */
Packit Service ca3877
static void
Packit Service ca3877
parse_uris_from_env_variable (const gchar *env_variable, GSList **list)
Packit Service ca3877
{
Packit Service ca3877
	gchar **uris = NULL;
Packit Service ca3877
	const gchar *env;
Packit Service ca3877
	gint i;
Packit Service ca3877
	guint length;
Packit Service ca3877
Packit Service ca3877
	/* Initialize the list */
Packit Service ca3877
	*list = NULL;
Packit Service ca3877
Packit Service ca3877
	if (!(env = g_getenv (env_variable)))
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	if (!(uris = g_strsplit (env, ",", -1)))
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	length = g_strv_length (uris);
Packit Service ca3877
	for (i = 0; i < length; i++) {
Packit Service ca3877
		SoupURI *uri;
Packit Service ca3877
Packit Service ca3877
		/* If the supplied URI is valid, append it to the list */
Packit Service ca3877
		if ((uri = soup_uri_new (uris[i])))
Packit Service ca3877
			*list = g_slist_prepend (*list, uri);
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_strfreev (uris);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
check_auth_trusted_uri (SoupConnectionAuth *auth, SoupMessage *msg)
Packit Service ca3877
{
Packit Service ca3877
	SoupURI *msg_uri;
Packit Service ca3877
	GSList *matched = NULL;
Packit Service ca3877
Packit Service ca3877
	g_return_val_if_fail (auth != NULL, FALSE);
Packit Service ca3877
	g_return_val_if_fail (msg != NULL, FALSE);
Packit Service ca3877
Packit Service ca3877
	msg_uri = soup_message_get_uri (msg);
Packit Service ca3877
Packit Service ca3877
	/* First check if the URI is not on blacklist */
Packit Service ca3877
	if (blacklisted_uris &&
Packit Service ca3877
	    g_slist_find_custom (blacklisted_uris, msg_uri, (GCompareFunc) match_base_uri))
Packit Service ca3877
		return FALSE;
Packit Service ca3877
Packit Service ca3877
	/* If no trusted URIs are set, we allow all HTTPS URIs */
Packit Service ca3877
	if (!trusted_uris)
Packit Service ca3877
		return soup_uri_is_https (msg_uri, NULL);
Packit Service ca3877
Packit Service ca3877
	matched = g_slist_find_custom (trusted_uris,
Packit Service ca3877
				       msg_uri,
Packit Service ca3877
				       (GCompareFunc) match_base_uri);
Packit Service ca3877
Packit Service ca3877
	return matched ? TRUE : FALSE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
soup_gss_build_response (SoupNegotiateConnectionState *conn, SoupAuth *auth, GError **err)
Packit Service ca3877
{
Packit Service ca3877
	if (!conn->initialized)
Packit Service ca3877
		if (!soup_gss_client_init (conn, soup_auth_get_host (auth), err))
Packit Service ca3877
			return FALSE;
Packit Service ca3877
Packit Service ca3877
	if (soup_gss_client_step (conn, "", err) != AUTH_GSS_CONTINUE)
Packit Service ca3877
		return FALSE;
Packit Service ca3877
Packit Service ca3877
	return TRUE;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_gss_error (OM_uint32 err_maj, OM_uint32 err_min, GError **err)
Packit Service ca3877
{
Packit Service ca3877
	OM_uint32 maj_stat, min_stat, msg_ctx = 0;
Packit Service ca3877
	gss_buffer_desc status;
Packit Service ca3877
	gchar *buf_maj = NULL, *buf_min = NULL;
Packit Service ca3877
Packit Service ca3877
	do {
Packit Service ca3877
		maj_stat = gss_display_status (&min_stat,
Packit Service ca3877
					       err_maj,
Packit Service ca3877
					       GSS_C_GSS_CODE,
Packit Service ca3877
					       (gss_OID) &gss_mech_spnego,
Packit Service ca3877
					       &msg_ctx,
Packit Service ca3877
					       &status);
Packit Service ca3877
		if (GSS_ERROR (maj_stat))
Packit Service ca3877
			break;
Packit Service ca3877
Packit Service ca3877
		buf_maj = g_strdup ((gchar *) status.value);
Packit Service ca3877
		gss_release_buffer (&min_stat, &status);
Packit Service ca3877
Packit Service ca3877
		maj_stat = gss_display_status (&min_stat,
Packit Service ca3877
					       err_min,
Packit Service ca3877
					       GSS_C_MECH_CODE,
Packit Service ca3877
					       GSS_C_NULL_OID,
Packit Service ca3877
					       &msg_ctx,
Packit Service ca3877
					       &status);
Packit Service ca3877
		if (!GSS_ERROR (maj_stat)) {
Packit Service ca3877
			buf_min = g_strdup ((gchar *) status.value);
Packit Service ca3877
			gss_release_buffer (&min_stat, &status);
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		if (err && *err == NULL) {
Packit Service ca3877
			g_set_error (err,
Packit Service ca3877
				     SOUP_HTTP_ERROR,
Packit Service ca3877
				     SOUP_STATUS_UNAUTHORIZED,
Packit Service ca3877
				     "%s: %s",
Packit Service ca3877
				     buf_maj,
Packit Service ca3877
				     buf_min ? buf_min : "");
Packit Service ca3877
		}
Packit Service ca3877
		g_free (buf_maj);
Packit Service ca3877
		g_free (buf_min);
Packit Service ca3877
		buf_min = buf_maj = NULL;
Packit Service ca3877
	} while (!GSS_ERROR (maj_stat) && msg_ctx != 0);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gboolean
Packit Service ca3877
soup_gss_client_init (SoupNegotiateConnectionState *conn, const gchar *host, GError **err)
Packit Service ca3877
{
Packit Service ca3877
	OM_uint32 maj_stat, min_stat;
Packit Service ca3877
	gchar *service = NULL;
Packit Service ca3877
	gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
Packit Service ca3877
	gboolean ret = FALSE;
Packit Service ca3877
	gchar *h;
Packit Service ca3877
Packit Service ca3877
	conn->server_name = GSS_C_NO_NAME;
Packit Service ca3877
	conn->context = GSS_C_NO_CONTEXT;
Packit Service ca3877
Packit Service ca3877
	h = g_ascii_strdown (host, -1);
Packit Service ca3877
	service = g_strconcat ("HTTP@", h, NULL);
Packit Service ca3877
	token.length = strlen (service);
Packit Service ca3877
	token.value = (gchar *) service;
Packit Service ca3877
Packit Service ca3877
	maj_stat = gss_import_name (&min_stat,
Packit Service ca3877
				    &token,
Packit Service ca3877
				    (gss_OID) GSS_C_NT_HOSTBASED_SERVICE,
Packit Service ca3877
				    &conn->server_name);
Packit Service ca3877
Packit Service ca3877
	if (GSS_ERROR (maj_stat)) {
Packit Service ca3877
		soup_gss_error (maj_stat, min_stat, err);
Packit Service ca3877
		ret = FALSE;
Packit Service ca3877
		goto out;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	conn->initialized = TRUE;
Packit Service ca3877
	ret = TRUE;
Packit Service ca3877
out:
Packit Service ca3877
	g_free (h);
Packit Service ca3877
	g_free (service);
Packit Service ca3877
	return ret;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static gint
Packit Service ca3877
soup_gss_client_step (SoupNegotiateConnectionState *conn, const gchar *challenge, GError **err)
Packit Service ca3877
{
Packit Service ca3877
	OM_uint32 maj_stat, min_stat;
Packit Service ca3877
	gss_buffer_desc in = GSS_C_EMPTY_BUFFER;
Packit Service ca3877
	gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
Packit Service ca3877
	gint ret = AUTH_GSS_CONTINUE;
Packit Service ca3877
Packit Service ca3877
	g_clear_pointer (&conn->response_header, g_free);
Packit Service ca3877
Packit Service ca3877
	if (challenge && *challenge) {
Packit Service ca3877
		size_t len;
Packit Service ca3877
		in.value = g_base64_decode (challenge, &len;;
Packit Service ca3877
		in.length = len;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	maj_stat = gss_init_sec_context (&min_stat,
Packit Service ca3877
					 GSS_C_NO_CREDENTIAL,
Packit Service ca3877
					 &conn->context,
Packit Service ca3877
					 conn->server_name,
Packit Service ca3877
					 (gss_OID) &gss_mech_spnego,
Packit Service ca3877
					 GSS_C_MUTUAL_FLAG,
Packit Service ca3877
					 GSS_C_INDEFINITE,
Packit Service ca3877
					 GSS_C_NO_CHANNEL_BINDINGS,
Packit Service ca3877
					 &in,
Packit Service ca3877
					 NULL,
Packit Service ca3877
					 &out,
Packit Service ca3877
					 NULL,
Packit Service ca3877
					 NULL);
Packit Service ca3877
Packit Service ca3877
	if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) {
Packit Service ca3877
		soup_gss_error (maj_stat, min_stat, err);
Packit Service ca3877
		ret = AUTH_GSS_ERROR;
Packit Service ca3877
		goto out;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
Packit Service ca3877
	if (out.length) {
Packit Service ca3877
		gchar *response = g_base64_encode ((const guchar *) out.value, out.length);
Packit Service ca3877
		conn->response_header = g_strconcat ("Negotiate ", response, NULL);
Packit Service ca3877
		g_free (response);
Packit Service ca3877
		maj_stat = gss_release_buffer (&min_stat, &out;;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
out:
Packit Service ca3877
	if (out.value)
Packit Service ca3877
		gss_release_buffer (&min_stat, &out;;
Packit Service ca3877
	if (in.value)
Packit Service ca3877
		g_free (in.value);
Packit Service ca3877
	return ret;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_gss_client_cleanup (SoupNegotiateConnectionState *conn)
Packit Service ca3877
{
Packit Service ca3877
	OM_uint32 maj_stat, min_stat;
Packit Service ca3877
Packit Service ca3877
	gss_release_name (&min_stat, &conn->server_name);
Packit Service ca3877
	maj_stat = gss_delete_sec_context (&min_stat, &conn->context, GSS_C_NO_BUFFER);
Packit Service ca3877
	if (maj_stat != GSS_S_COMPLETE)
Packit Service ca3877
		maj_stat = gss_delete_sec_context (&min_stat, &conn->context, GSS_C_NO_BUFFER);
Packit Service ca3877
}
Packit Service ca3877
#endif /* LIBSOUP_HAVE_GSSAPI */