|
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 */
|