summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--configure.ac15
-rw-r--r--examples/get.c10
-rw-r--r--libsoup/Makefile.am25
-rw-r--r--libsoup/libsoup-2.4.sym568
-rw-r--r--libsoup/libsoup-gssapi-2.4.sym3
-rw-r--r--libsoup/soup-auth-negotiate.c437
-rw-r--r--libsoup/soup-auth-negotiate.h27
-rw-r--r--libsoup/soup-auth.h2
-rw-r--r--libsoup/soup-gssapi.c169
-rw-r--r--libsoup/soup-gssapi.h41
-rw-r--r--libsoup/soup-status.h1
11 files changed, 1296 insertions, 2 deletions
diff --git a/configure.ac b/configure.ac
index 2a984181..3be3a8b1 100644
--- a/configure.ac
+++ b/configure.ac
@@ -321,6 +321,21 @@ AC_DEFINE_UNQUOTED(NTLM_AUTH, "$ntlm_auth", [Samba's 'winbind' daemon helper 'nt
AX_CODE_COVERAGE
+dnl **********************
+dnl *** GSSAPI support ***
+dnl **********************
+AC_PATH_PROG([KRB5_CONFIG], krb5-config, none, $PATH:/usr/kerberos/bin)
+if test "x$KRB5_CONFIG" != "xnone"; then
+ KRB5_LIBS="`${KRB5_CONFIG} --libs gssapi`"
+ KRB5_CFLAGS="`${KRB5_CONFIG} --cflags gssapi`"
+ AC_SUBST(KRB5_CFLAGS)
+ AC_SUBST(KRB5_LIBS)
+ if test "$KRB5_CONFIG" != none; then
+ AC_DEFINE(HAVE_GSSAPI, 1, [Whether or not gssapi libs are available])
+ fi
+fi
+AM_CONDITIONAL(BUILD_LIBSOUP_GSSAPI, test $KRB5_CONFIG != none)
+
dnl ****************************************************
dnl *** Warnings to show if using GCC ***
dnl *** (do this last so -Werror won't mess up tests ***
diff --git a/examples/get.c b/examples/get.c
index a8888d49..6f761af4 100644
--- a/examples/get.c
+++ b/examples/get.c
@@ -89,7 +89,7 @@ get_url (const char *url)
}
static const char *ca_file, *proxy;
-static gboolean synchronous, ntlm;
+static gboolean synchronous, ntlm, negotiate;
static GOptionEntry entries[] = {
{ "ca-file", 'c', 0,
@@ -104,6 +104,9 @@ static GOptionEntry entries[] = {
{ "ntlm", 'n', 0,
G_OPTION_ARG_NONE, &ntlm,
"Use NTLM authentication", NULL },
+ { "negotiate", 'N', 0,
+ G_OPTION_ARG_NONE, &negotiate,
+ "Use Negotiate authentication", NULL },
{ "output", 'o', 0,
G_OPTION_ARG_STRING, &output_file_path,
"Write the received data to FILE instead of stdout", "FILE" },
@@ -183,6 +186,11 @@ main (int argc, char **argv)
soup_uri_free (proxy_uri);
}
+ if (negotiate) {
+ soup_session_add_feature_by_type(session,
+ SOUP_TYPE_AUTH_NEGOTIATE);
+ }
+
if (!synchronous)
loop = g_main_loop_new (NULL, TRUE);
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 27f8b279..8e9fcb2d 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -20,7 +20,8 @@ AM_CPPFLAGS = \
$(GLIB_CFLAGS) \
$(XML_CFLAGS) \
$(SQLITE_CFLAGS) \
- $(CODE_COVERAGE_CFLAGS)
+ $(CODE_COVERAGE_CFLAGS) \
+ $(KRB5_CFLAGS)
libsoupincludedir = $(includedir)/libsoup-2.4/libsoup
@@ -110,6 +111,8 @@ libsoup_2_4_la_SOURCES = \
soup-auth-digest.c \
soup-auth-ntlm.h \
soup-auth-ntlm.c \
+ soup-auth-negotiate.h \
+ soup-auth-negotiate.c \
soup-auth-domain.c \
soup-auth-domain-basic.c \
soup-auth-domain-digest.c \
@@ -242,6 +245,26 @@ libsoup_gnome_2_4_la_SOURCES = \
endif
+if BUILD_LIBSOUP_GSSAPI
+
+lib_LTLIBRARIES += libsoup-gssapi-2.4.la
+
+libsoup_gssapi_2_4_la_LDFLAGS = \
+ -version-info $(SOUP_CURRENT):$(SOUP_REVISION):$(SOUP_AGE) \
+ -no-undefined \
+ -export-symbols $(srcdir)/libsoup-gssapi-2.4.sym
+
+EXTRA_DIST += libsoup-gssapi-2.4.sym
+
+libsoup_gssapi_2_4_la_LIBADD = \
+ $(KRB5_LIBS) \
+ $(GLIB_LIBS)
+
+libsoup_gssapi_2_4_la_SOURCES = \
+ soup-gssapi.c
+
+endif
+
GLIB_GENERATED = soup-enum-types.c soup-enum-types.h
BUILT_SOURCES = \
$(GLIB_GENERATED) \
diff --git a/libsoup/libsoup-2.4.sym b/libsoup/libsoup-2.4.sym
new file mode 100644
index 00000000..11833cb5
--- /dev/null
+++ b/libsoup/libsoup-2.4.sym
@@ -0,0 +1,568 @@
+_SOUP_METHOD_CONNECT
+_SOUP_METHOD_COPY
+_SOUP_METHOD_DELETE
+_SOUP_METHOD_GET
+_SOUP_METHOD_HEAD
+_SOUP_METHOD_LOCK
+_SOUP_METHOD_MKCOL
+_SOUP_METHOD_MOVE
+_SOUP_METHOD_OPTIONS
+_SOUP_METHOD_POST
+_SOUP_METHOD_PROPFIND
+_SOUP_METHOD_PROPPATCH
+_SOUP_METHOD_PUT
+_SOUP_METHOD_TRACE
+_SOUP_METHOD_UNLOCK
+_SOUP_URI_SCHEME_DATA
+_SOUP_URI_SCHEME_FILE
+_SOUP_URI_SCHEME_FTP
+_SOUP_URI_SCHEME_HTTP
+_SOUP_URI_SCHEME_HTTPS
+_SOUP_URI_SCHEME_RESOURCE
+_SOUP_URI_SCHEME_WS
+_SOUP_URI_SCHEME_WSS
+soup_add_completion
+soup_add_idle
+soup_add_io_watch
+soup_add_timeout
+soup_address_equal_by_ip
+soup_address_equal_by_name
+soup_address_family_get_type
+soup_address_get_gsockaddr
+soup_address_get_name
+soup_address_get_physical
+soup_address_get_port
+soup_address_get_sockaddr
+soup_address_get_type
+soup_address_hash_by_ip
+soup_address_hash_by_name
+soup_address_is_resolved
+soup_address_new
+soup_address_new_any
+soup_address_new_from_sockaddr
+soup_address_resolve_async
+soup_address_resolve_sync
+soup_auth_authenticate
+soup_auth_basic_get_type
+soup_auth_digest_get_type
+soup_auth_domain_accepts
+soup_auth_domain_add_path
+soup_auth_domain_basic_get_type
+soup_auth_domain_basic_new
+soup_auth_domain_basic_set_auth_callback
+soup_auth_domain_challenge
+soup_auth_domain_check_password
+soup_auth_domain_covers
+soup_auth_domain_digest_encode_password
+soup_auth_domain_digest_get_type
+soup_auth_domain_digest_new
+soup_auth_domain_digest_set_auth_callback
+soup_auth_domain_get_realm
+soup_auth_domain_get_type
+soup_auth_domain_remove_path
+soup_auth_domain_set_filter
+soup_auth_domain_set_generic_auth_callback
+soup_auth_domain_try_generic_auth_callback
+soup_auth_free_protection_space
+soup_auth_get_authorization
+soup_auth_get_host
+soup_auth_get_info
+soup_auth_get_protection_space
+soup_auth_get_realm
+soup_auth_get_saved_password
+soup_auth_get_saved_users
+soup_auth_get_scheme_name
+soup_auth_get_type
+soup_auth_has_saved_password
+soup_auth_is_authenticated
+soup_auth_is_for_proxy
+soup_auth_is_ready
+soup_auth_manager_get_type
+soup_auth_manager_use_auth
+soup_auth_negotiate_get_type
+soup_auth_new
+soup_auth_ntlm_get_type
+soup_auth_save_password
+soup_auth_update
+soup_buffer_copy
+soup_buffer_free
+soup_buffer_get_as_bytes
+soup_buffer_get_data
+soup_buffer_get_owner
+soup_buffer_get_type
+soup_buffer_new
+soup_buffer_new_subbuffer
+soup_buffer_new_take
+soup_buffer_new_with_owner
+soup_byte_array_get_type
+soup_cache_clear
+soup_cache_dump
+soup_cache_flush
+soup_cache_get_max_size
+soup_cache_get_type
+soup_cache_load
+soup_cache_new
+soup_cache_response_get_type
+soup_cache_set_max_size
+soup_cache_type_get_type
+soup_cacheability_get_type
+soup_char_attributes
+soup_check_version
+soup_client_context_get_address
+soup_client_context_get_auth_domain
+soup_client_context_get_auth_user
+soup_client_context_get_gsocket
+soup_client_context_get_host
+soup_client_context_get_local_address
+soup_client_context_get_remote_address
+soup_client_context_get_socket
+soup_client_context_get_type
+soup_client_context_steal_connection
+soup_connection_state_get_type
+soup_content_decoder_get_type
+soup_content_sniffer_get_buffer_size
+soup_content_sniffer_get_type
+soup_content_sniffer_new
+soup_content_sniffer_sniff
+soup_cookie_applies_to_uri
+soup_cookie_copy
+soup_cookie_domain_matches
+soup_cookie_equal
+soup_cookie_free
+soup_cookie_get_domain
+soup_cookie_get_expires
+soup_cookie_get_http_only
+soup_cookie_get_name
+soup_cookie_get_path
+soup_cookie_get_secure
+soup_cookie_get_type
+soup_cookie_get_value
+soup_cookie_jar_accept_policy_get_type
+soup_cookie_jar_add_cookie
+soup_cookie_jar_add_cookie_with_first_party
+soup_cookie_jar_all_cookies
+soup_cookie_jar_db_get_type
+soup_cookie_jar_db_new
+soup_cookie_jar_delete_cookie
+soup_cookie_jar_get_accept_policy
+soup_cookie_jar_get_cookie_list
+soup_cookie_jar_get_cookies
+soup_cookie_jar_get_type
+soup_cookie_jar_is_persistent
+soup_cookie_jar_new
+soup_cookie_jar_save
+soup_cookie_jar_set_accept_policy
+soup_cookie_jar_set_cookie
+soup_cookie_jar_set_cookie_with_first_party
+soup_cookie_jar_text_get_type
+soup_cookie_jar_text_new
+soup_cookie_new
+soup_cookie_parse
+soup_cookie_set_domain
+soup_cookie_set_expires
+soup_cookie_set_http_only
+soup_cookie_set_max_age
+soup_cookie_set_name
+soup_cookie_set_path
+soup_cookie_set_secure
+soup_cookie_set_value
+soup_cookie_to_cookie_header
+soup_cookie_to_set_cookie_header
+soup_cookies_free
+soup_cookies_from_request
+soup_cookies_from_response
+soup_cookies_to_cookie_header
+soup_cookies_to_request
+soup_cookies_to_response
+soup_date_copy
+soup_date_format_get_type
+soup_date_free
+soup_date_get_day
+soup_date_get_hour
+soup_date_get_minute
+soup_date_get_month
+soup_date_get_offset
+soup_date_get_second
+soup_date_get_type
+soup_date_get_utc
+soup_date_get_year
+soup_date_is_past
+soup_date_new
+soup_date_new_from_now
+soup_date_new_from_string
+soup_date_new_from_time_t
+soup_date_to_string
+soup_date_to_time_t
+soup_date_to_timeval
+soup_encoding_get_type
+soup_expectation_get_type
+soup_form_decode
+soup_form_decode_multipart
+soup_form_encode
+soup_form_encode_datalist
+soup_form_encode_hash
+soup_form_encode_valist
+soup_form_request_new
+soup_form_request_new_from_datalist
+soup_form_request_new_from_hash
+soup_form_request_new_from_multipart
+soup_get_major_version
+soup_get_micro_version
+soup_get_minor_version
+soup_header_contains
+soup_header_free_list
+soup_header_free_param_list
+soup_header_g_string_append_param
+soup_header_g_string_append_param_quoted
+soup_header_parse_list
+soup_header_parse_param_list
+soup_header_parse_quality_list
+soup_header_parse_semi_param_list
+soup_headers_parse
+soup_headers_parse_request
+soup_headers_parse_response
+soup_headers_parse_status_line
+soup_http_error_quark
+soup_http_version_get_type
+soup_known_status_code_get_type
+soup_logger_attach
+soup_logger_detach
+soup_logger_get_type
+soup_logger_log_level_get_type
+soup_logger_new
+soup_logger_set_printer
+soup_logger_set_request_filter
+soup_logger_set_response_filter
+soup_memory_use_get_type
+soup_message_add_header_handler
+soup_message_add_status_code_handler
+soup_message_body_append
+soup_message_body_append_buffer
+soup_message_body_append_take
+soup_message_body_complete
+soup_message_body_flatten
+soup_message_body_free
+soup_message_body_get_accumulate
+soup_message_body_get_chunk
+soup_message_body_get_type
+soup_message_body_got_chunk
+soup_message_body_new
+soup_message_body_set_accumulate
+soup_message_body_truncate
+soup_message_body_wrote_chunk
+soup_message_content_sniffed
+soup_message_disable_feature
+soup_message_finished
+soup_message_flags_get_type
+soup_message_get_address
+soup_message_get_first_party
+soup_message_get_flags
+soup_message_get_http_version
+soup_message_get_https_status
+soup_message_get_priority
+soup_message_get_soup_request
+soup_message_get_type
+soup_message_get_uri
+soup_message_got_body
+soup_message_got_chunk
+soup_message_got_headers
+soup_message_got_informational
+soup_message_headers_append
+soup_message_headers_clean_connection_headers
+soup_message_headers_clear
+soup_message_headers_foreach
+soup_message_headers_free
+soup_message_headers_free_ranges
+soup_message_headers_get
+soup_message_headers_get_content_disposition
+soup_message_headers_get_content_length
+soup_message_headers_get_content_range
+soup_message_headers_get_content_type
+soup_message_headers_get_encoding
+soup_message_headers_get_expectations
+soup_message_headers_get_headers_type
+soup_message_headers_get_list
+soup_message_headers_get_one
+soup_message_headers_get_ranges
+soup_message_headers_get_type
+soup_message_headers_header_contains
+soup_message_headers_header_equals
+soup_message_headers_iter_init
+soup_message_headers_iter_next
+soup_message_headers_new
+soup_message_headers_remove
+soup_message_headers_replace
+soup_message_headers_set_content_disposition
+soup_message_headers_set_content_length
+soup_message_headers_set_content_range
+soup_message_headers_set_content_type
+soup_message_headers_set_encoding
+soup_message_headers_set_expectations
+soup_message_headers_set_range
+soup_message_headers_set_ranges
+soup_message_headers_type_get_type
+soup_message_io_cleanup
+soup_message_is_keepalive
+soup_message_new
+soup_message_new_from_uri
+soup_message_priority_get_type
+soup_message_restarted
+soup_message_set_chunk_allocator
+soup_message_set_first_party
+soup_message_set_flags
+soup_message_set_http_version
+soup_message_set_priority
+soup_message_set_redirect
+soup_message_set_request
+soup_message_set_response
+soup_message_set_status
+soup_message_set_status_full
+soup_message_set_uri
+soup_message_starting
+soup_message_wrote_body
+soup_message_wrote_body_data
+soup_message_wrote_chunk
+soup_message_wrote_headers
+soup_message_wrote_informational
+soup_multipart_append_form_file
+soup_multipart_append_form_string
+soup_multipart_append_part
+soup_multipart_free
+soup_multipart_get_length
+soup_multipart_get_part
+soup_multipart_get_type
+soup_multipart_input_stream_get_headers
+soup_multipart_input_stream_get_type
+soup_multipart_input_stream_new
+soup_multipart_input_stream_next_part
+soup_multipart_input_stream_next_part_async
+soup_multipart_input_stream_next_part_finish
+soup_multipart_new
+soup_multipart_new_from_message
+soup_multipart_to_message
+soup_password_manager_get_passwords_async
+soup_password_manager_get_passwords_sync
+soup_password_manager_get_type
+soup_proxy_resolver_default_get_type
+soup_proxy_resolver_get_proxy_async
+soup_proxy_resolver_get_proxy_sync
+soup_proxy_resolver_get_type
+soup_proxy_uri_resolver_get_proxy_uri_async
+soup_proxy_uri_resolver_get_proxy_uri_sync
+soup_proxy_uri_resolver_get_type
+soup_request_data_get_type
+soup_request_error_get_type
+soup_request_error_quark
+soup_request_file_get_file
+soup_request_file_get_type
+soup_request_get_content_length
+soup_request_get_content_type
+soup_request_get_session
+soup_request_get_type
+soup_request_get_uri
+soup_request_http_get_message
+soup_request_http_get_type
+soup_request_send
+soup_request_send_async
+soup_request_send_finish
+soup_requester_error_get_type
+soup_requester_error_quark
+soup_requester_get_type
+soup_requester_new
+soup_requester_request
+soup_requester_request_uri
+soup_server_accept_iostream
+soup_server_add_auth_domain
+soup_server_add_early_handler
+soup_server_add_handler
+soup_server_add_websocket_handler
+soup_server_disconnect
+soup_server_get_async_context
+soup_server_get_listener
+soup_server_get_listeners
+soup_server_get_port
+soup_server_get_type
+soup_server_get_uris
+soup_server_is_https
+soup_server_listen
+soup_server_listen_all
+soup_server_listen_fd
+soup_server_listen_local
+soup_server_listen_options_get_type
+soup_server_listen_socket
+soup_server_new
+soup_server_pause_message
+soup_server_quit
+soup_server_remove_auth_domain
+soup_server_remove_handler
+soup_server_run
+soup_server_run_async
+soup_server_set_ssl_cert_file
+soup_server_unpause_message
+soup_session_abort
+soup_session_add_feature
+soup_session_add_feature_by_type
+soup_session_async_get_type
+soup_session_async_new
+soup_session_async_new_with_options
+soup_session_cancel_message
+soup_session_feature_add_feature
+soup_session_feature_attach
+soup_session_feature_detach
+soup_session_feature_get_type
+soup_session_feature_has_feature
+soup_session_feature_remove_feature
+soup_session_get_async_context
+soup_session_get_feature
+soup_session_get_feature_for_message
+soup_session_get_features
+soup_session_get_type
+soup_session_has_feature
+soup_session_new
+soup_session_new_with_options
+soup_session_pause_message
+soup_session_prefetch_dns
+soup_session_prepare_for_uri
+soup_session_queue_message
+soup_session_redirect_message
+soup_session_remove_feature
+soup_session_remove_feature_by_type
+soup_session_request
+soup_session_request_http
+soup_session_request_http_uri
+soup_session_request_uri
+soup_session_requeue_message
+soup_session_send
+soup_session_send_async
+soup_session_send_finish
+soup_session_send_message
+soup_session_steal_connection
+soup_session_sync_get_type
+soup_session_sync_new
+soup_session_sync_new_with_options
+soup_session_unpause_message
+soup_session_websocket_connect_async
+soup_session_websocket_connect_finish
+soup_session_would_redirect
+soup_socket_connect_async
+soup_socket_connect_sync
+soup_socket_disconnect
+soup_socket_get_fd
+soup_socket_get_local_address
+soup_socket_get_remote_address
+soup_socket_get_type
+soup_socket_io_status_get_type
+soup_socket_is_connected
+soup_socket_is_ssl
+soup_socket_listen
+soup_socket_new
+soup_socket_read
+soup_socket_read_until
+soup_socket_start_proxy_ssl
+soup_socket_start_ssl
+soup_socket_write
+soup_ssl_supported
+soup_status_get_phrase
+soup_status_get_type
+soup_status_proxify
+soup_str_case_equal
+soup_str_case_hash
+soup_tld_domain_is_public_suffix
+soup_tld_error_get_type
+soup_tld_error_quark
+soup_tld_get_base_domain
+soup_uri_copy
+soup_uri_copy_host
+soup_uri_decode
+soup_uri_encode
+soup_uri_equal
+soup_uri_free
+soup_uri_get_fragment
+soup_uri_get_host
+soup_uri_get_password
+soup_uri_get_path
+soup_uri_get_port
+soup_uri_get_query
+soup_uri_get_scheme
+soup_uri_get_type
+soup_uri_get_user
+soup_uri_host_equal
+soup_uri_host_hash
+soup_uri_new
+soup_uri_new_with_base
+soup_uri_normalize
+soup_uri_set_fragment
+soup_uri_set_host
+soup_uri_set_password
+soup_uri_set_path
+soup_uri_set_port
+soup_uri_set_query
+soup_uri_set_query_from_fields
+soup_uri_set_query_from_form
+soup_uri_set_scheme
+soup_uri_set_user
+soup_uri_to_string
+soup_uri_uses_default_port
+soup_value_array_append
+soup_value_array_append_vals
+soup_value_array_from_args
+soup_value_array_get_nth
+soup_value_array_insert
+soup_value_array_new
+soup_value_array_new_with_vals
+soup_value_array_to_args
+soup_value_hash_insert
+soup_value_hash_insert_vals
+soup_value_hash_insert_value
+soup_value_hash_lookup
+soup_value_hash_lookup_vals
+soup_value_hash_new
+soup_value_hash_new_with_vals
+soup_websocket_client_prepare_handshake
+soup_websocket_client_verify_handshake
+soup_websocket_close_code_get_type
+soup_websocket_connection_close
+soup_websocket_connection_get_close_code
+soup_websocket_connection_get_close_data
+soup_websocket_connection_get_connection_type
+soup_websocket_connection_get_io_stream
+soup_websocket_connection_get_origin
+soup_websocket_connection_get_protocol
+soup_websocket_connection_get_state
+soup_websocket_connection_get_type
+soup_websocket_connection_get_uri
+soup_websocket_connection_new
+soup_websocket_connection_send_binary
+soup_websocket_connection_send_text
+soup_websocket_connection_type_get_type
+soup_websocket_data_type_get_type
+soup_websocket_error_get_quark
+soup_websocket_error_get_type
+soup_websocket_server_check_handshake
+soup_websocket_server_process_handshake
+soup_websocket_state_get_type
+soup_xmlrpc_build_fault
+soup_xmlrpc_build_method_call
+soup_xmlrpc_build_method_response
+soup_xmlrpc_build_request
+soup_xmlrpc_build_response
+soup_xmlrpc_error_get_type
+soup_xmlrpc_error_quark
+soup_xmlrpc_extract_method_call
+soup_xmlrpc_extract_method_response
+soup_xmlrpc_fault_get_type
+soup_xmlrpc_fault_quark
+soup_xmlrpc_message_new
+soup_xmlrpc_message_set_fault
+soup_xmlrpc_message_set_response
+soup_xmlrpc_params_free
+soup_xmlrpc_params_parse
+soup_xmlrpc_parse_method_call
+soup_xmlrpc_parse_method_response
+soup_xmlrpc_parse_request
+soup_xmlrpc_parse_response
+soup_xmlrpc_request_new
+soup_xmlrpc_set_fault
+soup_xmlrpc_set_response
+soup_xmlrpc_variant_get_datetime
+soup_xmlrpc_variant_new_datetime
diff --git a/libsoup/libsoup-gssapi-2.4.sym b/libsoup/libsoup-gssapi-2.4.sym
new file mode 100644
index 00000000..2df49d62
--- /dev/null
+++ b/libsoup/libsoup-gssapi-2.4.sym
@@ -0,0 +1,3 @@
+soup_gss_client_init
+soup_gss_client_step
+soup_gss_client_cleanup
diff --git a/libsoup/soup-auth-negotiate.c b/libsoup/soup-auth-negotiate.c
new file mode 100644
index 00000000..79db1075
--- /dev/null
+++ b/libsoup/soup-auth-negotiate.c
@@ -0,0 +1,437 @@
+/* -*- Mode: C; tabstop: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-negotiate.c: HTTP Negotiate Authentication helper
+ *
+ * Copyright (C) 2009,2013 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#ifdef HAVE_GSSAPI
+# include <gssapi/gssapi.h>
+#endif
+
+#include <string.h>
+
+#include "soup-auth-negotiate.h"
+#include "soup-gssapi.h"
+#include "soup-headers.h"
+#include "soup-message.h"
+#include "soup-message-private.h"
+#include "soup-misc.h"
+#include "soup-uri.h"
+
+static gboolean soup_gss_build_response (SoupNegotiateConnectionState *conn,
+ SoupAuth *auth, GError **err);
+static gchar** parse_trusted_uris (void);
+static gboolean check_auth_trusted_uri (SoupAuthNegotiate *negotiate,
+ SoupMessage *msg);
+
+typedef struct {
+ char *username;
+} SoupAuthNegotiatePrivate;
+#define SOUP_AUTH_NEGOTIATE_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiatePrivate))
+
+G_DEFINE_TYPE (SoupAuthNegotiate, soup_auth_negotiate, SOUP_TYPE_CONNECTION_AUTH)
+
+/* Function pointers to dlopen'ed libsoup-gssapi */
+struct {
+ int (*client_init)(SoupNegotiateConnectionState *conn,
+ const char *host,
+ GError **err);
+ int (*client_step)(SoupNegotiateConnectionState *conn,
+ const char *challenge,
+ GError **err);
+ void (*client_cleanup)(SoupNegotiateConnectionState *conn);
+} soup_gssapi_syms;
+gboolean have_gssapi;
+
+static gchar **trusted_uris;
+
+static void
+soup_auth_negotiate_init (SoupAuthNegotiate *negotiate)
+{
+}
+
+static void
+soup_auth_negotiate_finalize (GObject *object)
+{
+ SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (object);
+
+ g_free (priv->username);
+
+ G_OBJECT_CLASS (soup_auth_negotiate_parent_class)->finalize (object);
+}
+
+static gpointer
+soup_auth_negotiate_create_connection_state (SoupConnectionAuth *auth)
+{
+ return g_slice_new0 (SoupNegotiateConnectionState);
+}
+
+static void
+soup_auth_negotiate_free_connection_state (SoupConnectionAuth *auth,
+ gpointer state)
+{
+ SoupNegotiateConnectionState *conn = state;
+
+ if (have_gssapi)
+ soup_gssapi_syms.client_cleanup (conn);
+ g_free (conn->response_header);
+}
+
+
+static gboolean
+soup_auth_negotiate_update_connection (SoupConnectionAuth *auth, SoupMessage *msg,
+ const char *header, gpointer state)
+{
+ SoupAuthNegotiatePrivate *priv =
+ SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+ SoupNegotiateConnectionState *conn = state;
+ GError *err = NULL;
+
+ if (conn->state > SOUP_NEGOTIATE_RECEIVED_CHALLENGE) {
+ /* We already authenticated, but then got another 401.
+ * That means "permission denied", so don't try to
+ * authenticate again.
+ */
+ conn->state = SOUP_NEGOTIATE_FAILED;
+
+ /* Make sure we don't claim to be authenticated */
+ g_free (priv->username);
+ priv->username = NULL;
+
+ return FALSE;
+ }
+
+ /* Found negotiate header, start negotiate */
+ if (strcmp (header, "Negotiate") == 0) {
+ conn->state = SOUP_NEGOTIATE_RECEIVED_CHALLENGE;
+ if (soup_gss_build_response (conn, SOUP_AUTH (auth), &err))
+ return TRUE;
+ else {
+ /* FIXME: report further upward via
+ * soup_message_get_error_message */
+ g_warning ("gssapi step failed: %s", err->message);
+ }
+ }
+ g_clear_error (&err);
+ return FALSE;
+}
+
+static GSList *
+soup_auth_negotiate_get_protection_space (SoupAuth *auth, SoupURI *source_uri)
+{
+ char *space, *p;
+
+ space = g_strdup (source_uri->path);
+
+ /* Strip filename component */
+ p = strrchr (space, '/');
+ if (p && p != space && p[1])
+ *p = '\0';
+
+ return g_slist_prepend (NULL, space);
+}
+
+static void
+soup_auth_negotiate_authenticate (SoupAuth *auth, const char *username,
+ const char *password)
+{
+ SoupAuthNegotiatePrivate *priv = SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth);
+
+ g_return_if_fail (username != NULL);
+ priv->username = g_strdup (username);
+}
+
+static gboolean
+soup_auth_negotiate_is_authenticated (SoupAuth *auth)
+{
+ return SOUP_AUTH_NEGOTIATE_GET_PRIVATE (auth)->username != NULL;
+}
+
+static gboolean
+soup_auth_negotiate_is_ready (SoupAuth *auth,
+ SoupMessage *msg)
+{
+ SoupAuthNegotiate* negotiate = SOUP_AUTH_NEGOTIATE (auth);
+ return check_auth_trusted_uri (negotiate, msg);
+}
+
+
+static void
+check_server_response(SoupMessage *msg, gpointer state)
+{
+ gchar **parts, *p;
+ gint ret;
+ const char *auth_headers;
+ SoupNegotiateConnectionState *conn = state;
+ GError *err = NULL;
+
+ if (msg->status_code == SOUP_STATUS_UNAUTHORIZED)
+ return;
+
+ /* FIXME: need to check for proxy-auth too */
+ auth_headers = soup_message_headers_get_one (msg->response_headers,
+ "WWW-Authenticate");
+ parts = g_strsplit (auth_headers, " ", 0);
+ if (g_strv_length (parts) != 2) {
+ g_warning ("Failed to parse auth header %s", auth_headers);
+ conn->state = SOUP_NEGOTIATE_FAILED;
+ goto out;
+ }
+ if (g_ascii_strcasecmp (parts[0], "Negotiate")) {
+ g_warning ("Failed to parse auth header %s", auth_headers);
+ conn->state = SOUP_NEGOTIATE_FAILED;
+ }
+
+ p = parts[1];
+ ret = soup_gssapi_syms.client_step (conn, p, &err);
+
+ if (ret != AUTH_GSS_COMPLETE) {
+ g_warning ("%s", err->message);
+ conn->state = SOUP_NEGOTIATE_FAILED;
+ }
+ out:
+ g_clear_error (&err);
+ g_strfreev (parts);
+}
+
+
+static void
+remove_server_response_handler(SoupMessage *msg, gpointer state)
+{
+ g_signal_handlers_disconnect_by_func (msg,
+ G_CALLBACK (check_server_response),
+ state);
+}
+
+
+static char *
+soup_auth_negotiate_get_connection_authorization (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer state)
+{
+ SoupNegotiateConnectionState *conn = state;
+ char *header = NULL;
+
+ if (conn->state == SOUP_NEGOTIATE_RECEIVED_CHALLENGE) {
+ header = conn->response_header;
+ conn->response_header = NULL;
+ conn->state = SOUP_NEGOTIATE_SENT_RESPONSE;
+ }
+
+
+ g_signal_connect (msg,
+ "finished",
+ G_CALLBACK (remove_server_response_handler),
+ conn);
+
+ /* Wait for the 2xx response to verify server response */
+ g_signal_connect (msg,
+ "got_headers",
+ G_CALLBACK (check_server_response),
+ conn);
+
+ return header;
+}
+
+static gboolean
+soup_auth_negotiate_is_connection_ready (SoupConnectionAuth *auth,
+ SoupMessage *msg,
+ gpointer state)
+{
+ SoupNegotiateConnectionState *conn = state;
+
+ return conn->state != SOUP_NEGOTIATE_FAILED;
+}
+
+static gboolean
+soup_gssapi_load (void)
+{
+ GModule *gssapi;
+ const char *modulename = PACKAGE "-gssapi-2.4." G_MODULE_SUFFIX;
+
+ if (!g_module_supported ())
+ return FALSE;
+
+ gssapi = g_module_open (modulename, G_MODULE_BIND_LOCAL);
+ if (!gssapi) {
+ g_warning ("Failed to load %s - negotiate support will "
+ "be disabled.", modulename);
+ return FALSE;
+ }
+
+#define GSSAPI_BIND_SYMBOL(name) \
+ g_return_val_if_fail (g_module_symbol (gssapi, "soup_gss_" #name, (gpointer)&soup_gssapi_syms.name), FALSE)
+
+ GSSAPI_BIND_SYMBOL(client_step);
+ GSSAPI_BIND_SYMBOL(client_init);
+ GSSAPI_BIND_SYMBOL(client_cleanup);
+#undef GSSPI_BIND_SYMBOL
+ return TRUE;
+}
+
+static void
+soup_auth_negotiate_class_init (SoupAuthNegotiateClass *auth_negotiate_class)
+{
+ SoupAuthClass *auth_class = SOUP_AUTH_CLASS (auth_negotiate_class);
+ SoupConnectionAuthClass *connauth_class =
+ SOUP_CONNECTION_AUTH_CLASS (auth_negotiate_class);
+ GObjectClass *object_class = G_OBJECT_CLASS (auth_negotiate_class);
+
+ g_type_class_add_private (auth_negotiate_class, sizeof (SoupAuthNegotiatePrivate));
+
+ auth_class->scheme_name = "Negotiate";
+ auth_class->strength = 7;
+
+ auth_class->get_protection_space = soup_auth_negotiate_get_protection_space;
+ auth_class->authenticate = soup_auth_negotiate_authenticate;
+ auth_class->is_authenticated = soup_auth_negotiate_is_authenticated;
+ auth_class->is_ready = soup_auth_negotiate_is_ready;
+
+ connauth_class->create_connection_state = soup_auth_negotiate_create_connection_state;
+ connauth_class->free_connection_state = soup_auth_negotiate_free_connection_state;
+ connauth_class->update_connection = soup_auth_negotiate_update_connection;
+ connauth_class->get_connection_authorization = soup_auth_negotiate_get_connection_authorization;
+ connauth_class->is_connection_ready = soup_auth_negotiate_is_connection_ready;
+
+ object_class->finalize = soup_auth_negotiate_finalize;
+
+ trusted_uris = parse_trusted_uris ();
+ have_gssapi = soup_gssapi_load();
+}
+
+static gboolean
+soup_gss_build_response (SoupNegotiateConnectionState *conn, SoupAuth *auth, GError **err)
+{
+ if (!have_gssapi) {
+ if (err && *err == NULL) {
+ g_set_error (err,
+ SOUP_HTTP_ERROR,
+ SOUP_STATUS_GSSAPI_FAILED,
+ "GSSAPI unavailable");
+ }
+ return FALSE;
+ }
+
+ if (!soup_gssapi_syms.client_init (conn, soup_auth_get_host (SOUP_AUTH (auth)), err))
+ return FALSE;
+
+ if (soup_gssapi_syms.client_step (conn, "", err) != AUTH_GSS_CONTINUE)
+ return FALSE;
+
+ return TRUE;
+}
+
+/* Parses a comma separated list of URIS from the environment. */
+static gchar**
+parse_trusted_uris(void)
+{
+ gchar **uris = NULL;
+ const gchar *env;
+
+ env = g_getenv ("SOUP_AUTH_TRUSTED_URIS");
+ if (env)
+ uris = g_strsplit (env, ",", -1);
+ return uris;
+}
+
+
+/* check if scheme://host:port from msg matches the trusted uri */
+static gboolean
+match_base_uri (SoupMessage *msg, const gchar *trusted)
+{
+ SoupURI *uri;
+ gboolean ret = FALSE;
+
+ /* params of the trusted uri */
+ gchar **trusted_parts = NULL;
+ gchar **trusted_host_port = NULL;
+ const gchar *trusted_host = NULL;
+ gint trusted_host_len;
+
+ /* params of the msg's uri */
+ const gchar *host = NULL;
+ gint port;
+ gint host_len;
+
+ uri = soup_message_get_uri (msg);
+ /* split trusted uri into scheme and host/port */
+ if (strstr (trusted, "://")) {
+ trusted_parts = g_strsplit (trusted, "://", -1);
+
+ /* The scheme has to match exactly */
+ if (g_ascii_strcasecmp (trusted_parts[0],
+ soup_uri_get_scheme (uri))) {
+ goto out;
+ }
+ if (strlen (trusted_parts[1]) == 0) {
+ /* scheme only, we're done */
+ ret = TRUE;
+ goto out;
+ } else
+ trusted_host = trusted_parts[1];
+ } else {
+ trusted_host = trusted;
+ }
+
+ trusted_host_port = g_strsplit (trusted_host, ":", 2);
+ /* If we got a port in the trusted uri it has to match exactly */
+ if (g_strv_length (trusted_host_port) > 1) {
+ port = g_ascii_strtoll (trusted_host_port[1], NULL, 10);
+ if (port != soup_uri_get_port (uri)) {
+ goto out;
+ }
+ }
+
+ trusted_host = trusted_host_port[0];
+ host = soup_uri_get_host (uri);
+ if (g_str_has_suffix (host, trusted_host)) {
+ /* if the msg host ends with host from the trusted uri, then make
+ * sure it is either an exact match, or prefixed with a dot. We
+ * don't want "foobar.com" to match "bar.com"
+ */
+ if (g_ascii_strcasecmp (host, trusted_host) == 0) {
+ ret = TRUE;
+ goto out;
+ } else {
+ /* we don't want example.com to match fooexample.com
+ */
+ trusted_host_len = strlen (trusted_host);
+ host_len = strlen (host);
+ if (host[host_len - trusted_host_len - 1] == '.') {
+ ret = TRUE;
+ }
+ }
+ }
+out:
+ g_strfreev (trusted_parts);
+ g_strfreev (trusted_host_port);
+ return ret;
+}
+
+
+static gboolean
+check_auth_trusted_uri (SoupAuthNegotiate *negotiate, SoupMessage *msg)
+{
+ SoupAuthNegotiatePrivate *priv =
+ SOUP_AUTH_NEGOTIATE_GET_PRIVATE (negotiate);
+ int i;
+
+ g_return_val_if_fail (negotiate != NULL, FALSE);
+ g_return_val_if_fail (priv != NULL, FALSE);
+ g_return_val_if_fail (msg != NULL, FALSE);
+
+ if (!trusted_uris) {
+ return FALSE;
+ }
+
+ for (i=0; i < g_strv_length (trusted_uris); i++) {
+ if (match_base_uri (msg, trusted_uris[i]))
+ return TRUE;
+ }
+ return FALSE;
+}
diff --git a/libsoup/soup-auth-negotiate.h b/libsoup/soup-auth-negotiate.h
new file mode 100644
index 00000000..dbdfc3cb
--- /dev/null
+++ b/libsoup/soup-auth-negotiate.h
@@ -0,0 +1,27 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#ifndef SOUP_AUTH_NEGOTIATE_H
+#define SOUP_AUTH_NEGOTIATE_H 1
+
+#include "soup-connection-auth.h"
+
+#define SOUP_AUTH_NEGOTIATE(object) (G_TYPE_CHECK_INSTANCE_CAST ((object), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiate))
+#define SOUP_AUTH_NEGOTIATE_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiateClass))
+#define SOUP_IS_AUTH_NEGOTIATE(object) (G_TYPE_CHECK_INSTANCE_TYPE ((object), SOUP_TYPE_AUTH_NEGOTIATE))
+#define SOUP_IS_AUTH_NEGOTIATE_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((klass), SOUP_TYPE_AUTH_NEGOTIATE))
+#define SOUP_AUTH_NEGOTIATE_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_AUTH_NEGOTIATE, SoupAuthNegotiateClass))
+
+typedef struct {
+ SoupConnectionAuth parent;
+
+} SoupAuthNegotiate;
+
+typedef struct {
+ SoupConnectionAuthClass parent_class;
+
+} SoupAuthNegotiateClass;
+
+#endif /* SOUP_AUTH_NEGOTIATE_H */
diff --git a/libsoup/soup-auth.h b/libsoup/soup-auth.h
index d2e6a100..75ca01c2 100644
--- a/libsoup/soup-auth.h
+++ b/libsoup/soup-auth.h
@@ -115,6 +115,8 @@ GType soup_auth_digest_get_type (void);
#define SOUP_TYPE_AUTH_NTLM (soup_auth_ntlm_get_type ())
SOUP_AVAILABLE_IN_2_4
GType soup_auth_ntlm_get_type (void);
+#define SOUP_TYPE_AUTH_NEGOTIATE (soup_auth_negotiate_get_type ())
+GType soup_auth_negotiate_get_type (void);
/* Deprecated SoupPasswordManager-related APIs: all are now no-ops */
SOUP_AVAILABLE_IN_2_28
diff --git a/libsoup/soup-gssapi.c b/libsoup/soup-gssapi.c
new file mode 100644
index 00000000..78aaa773
--- /dev/null
+++ b/libsoup/soup-gssapi.c
@@ -0,0 +1,169 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-gssapi.c: GSSAPI related functions
+ *
+ * Copyright (C) 2013 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <soup-status.h>
+#include <soup-gssapi.h>
+
+#include <gssapi/gssapi.h>
+#include <gssapi/gssapi_krb5.h>
+
+int
+soup_gss_client_init (SoupNegotiateConnectionState *conn, const char *host, GError **err);
+
+int
+soup_gss_client_step (SoupNegotiateConnectionState *conn, const char *host, GError **err);
+
+void
+soup_gss_client_cleanup (SoupNegotiateConnectionState *conn);
+
+static void
+soup_gss_error (OM_uint32 err_maj, OM_uint32 err_min, GError **err)
+{
+ OM_uint32 maj_stat, min_stat, msg_ctx = 0;
+ gss_buffer_desc status;
+ char *buf_maj = NULL, *buf_min = NULL;
+
+ do {
+ maj_stat = gss_display_status (&min_stat,
+ err_maj,
+ GSS_C_GSS_CODE,
+ GSS_C_NO_OID,
+ &msg_ctx,
+ &status);
+ if (GSS_ERROR (maj_stat))
+ break;
+
+ buf_maj = g_strdup ((char*) status.value);
+ gss_release_buffer (&min_stat, &status);
+
+ maj_stat = gss_display_status (&min_stat,
+ err_min,
+ GSS_C_MECH_CODE,
+ GSS_C_NULL_OID,
+ &msg_ctx,
+ &status);
+ if (!GSS_ERROR (maj_stat)) {
+ buf_min = g_strdup ((char*) status.value);
+ gss_release_buffer (&min_stat, &status);
+ }
+
+ if (err && *err == NULL) {
+ g_set_error (err,
+ SOUP_HTTP_ERROR,
+ SOUP_STATUS_GSSAPI_FAILED,
+ "%s %s",
+ buf_maj,
+ buf_min ? buf_min : "");
+ }
+ g_free (buf_maj);
+ g_free (buf_min);
+ buf_min = buf_maj = NULL;
+ } while (!GSS_ERROR (maj_stat) && msg_ctx != 0);
+}
+
+G_MODULE_EXPORT int
+soup_gss_client_init (SoupNegotiateConnectionState *conn, const char *host, GError **err)
+{
+ OM_uint32 maj_stat, min_stat;
+ char *service = NULL;
+ gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
+ gboolean ret = FALSE;
+ gchar *h;
+
+ conn->server_name = GSS_C_NO_NAME;
+ conn->context = GSS_C_NO_CONTEXT;
+
+ h = g_ascii_strdown (host, -1);
+ service = g_strconcat ("HTTP/", h, NULL);
+ token.length = strlen (service);
+ token.value = (char *)service;
+
+ maj_stat = gss_import_name (&min_stat,
+ &token,
+ (gss_OID) GSS_KRB5_NT_PRINCIPAL_NAME,
+ &conn->server_name);
+
+ if (GSS_ERROR (maj_stat)) {
+ soup_gss_error (maj_stat, min_stat, err);
+ ret = FALSE;
+ goto out;
+ }
+
+ ret = TRUE;
+out:
+ g_free (h);
+ g_free (service);
+ return ret;
+}
+
+G_MODULE_EXPORT int
+soup_gss_client_step (SoupNegotiateConnectionState *conn, const char *challenge, GError **err)
+{
+ OM_uint32 maj_stat, min_stat;
+ gss_buffer_desc in = GSS_C_EMPTY_BUFFER;
+ gss_buffer_desc out = GSS_C_EMPTY_BUFFER;
+ int ret = AUTH_GSS_CONTINUE;
+
+ g_clear_pointer (&conn->response_header, g_free);
+
+ if (challenge && *challenge) {
+ size_t len;
+ in.value = g_base64_decode (challenge, &len);
+ in.length = len;
+ }
+
+ maj_stat = gss_init_sec_context (&min_stat,
+ GSS_C_NO_CREDENTIAL,
+ &conn->context,
+ conn->server_name,
+ GSS_C_NO_OID,
+ GSS_C_MUTUAL_FLAG,
+ GSS_C_INDEFINITE,
+ GSS_C_NO_CHANNEL_BINDINGS,
+ &in,
+ NULL,
+ &out,
+ NULL,
+ NULL);
+
+ if ((maj_stat != GSS_S_COMPLETE) && (maj_stat != GSS_S_CONTINUE_NEEDED)) {
+ soup_gss_error (maj_stat, min_stat, err);
+ ret = AUTH_GSS_ERROR;
+ goto out;
+ }
+
+ ret = (maj_stat == GSS_S_COMPLETE) ? AUTH_GSS_COMPLETE : AUTH_GSS_CONTINUE;
+ if (out.length) {
+ char *response = g_base64_encode ((const unsigned char *)out.value, out.length);
+ conn->response_header = g_strconcat ("Negotiate ", response, NULL);
+ g_free (response);
+ maj_stat = gss_release_buffer (&min_stat, &out);
+ }
+
+out:
+ if (out.value)
+ gss_release_buffer (&min_stat, &out);
+ if (in.value)
+ g_free (in.value);
+ return ret;
+}
+
+
+G_MODULE_EXPORT void
+soup_gss_client_cleanup (SoupNegotiateConnectionState *conn)
+{
+ OM_uint32 min_stat;
+
+ gss_release_name (&min_stat, &conn->server_name);
+}
+
diff --git a/libsoup/soup-gssapi.h b/libsoup/soup-gssapi.h
new file mode 100644
index 00000000..05a2b48f
--- /dev/null
+++ b/libsoup/soup-gssapi.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-auth-negotiate.c: HTTP Negotiate Authentication helper
+ *
+ * Copyright (C) 2013 Guido Guenther <agx@sigxcpu.org>
+ */
+
+#ifndef SOUP_GSSAPI_H
+#define SOUP_GSSAPI_H
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#ifdef HAVE_GSSAPI
+# include <gssapi/gssapi.h>
+#endif
+
+#define AUTH_GSS_ERROR -1
+#define AUTH_GSS_COMPLETE 1
+#define AUTH_GSS_CONTINUE 0
+
+typedef enum {
+ SOUP_NEGOTIATE_NEW,
+ SOUP_NEGOTIATE_RECEIVED_CHALLENGE, /* received intial negotiate header */
+ SOUP_NEGOTIATE_SENT_RESPONSE, /* sent response to server */
+ SOUP_NEGOTIATE_FAILED
+} SoupNegotiateState;
+
+typedef struct {
+ SoupNegotiateState state;
+
+#ifdef HAVE_GSSAPI
+ gss_ctx_id_t context;
+ gss_name_t server_name;
+#endif
+
+ char *response_header;
+} SoupNegotiateConnectionState;
+
+#endif /* SOUP_GSSAPI_H */
diff --git a/libsoup/soup-status.h b/libsoup/soup-status.h
index 28d481db..0ff7405b 100644
--- a/libsoup/soup-status.h
+++ b/libsoup/soup-status.h
@@ -34,6 +34,7 @@ typedef enum {
SOUP_STATUS_TRY_AGAIN,
SOUP_STATUS_TOO_MANY_REDIRECTS,
SOUP_STATUS_TLS_FAILED,
+ SOUP_STATUS_GSSAPI_FAILED,
/* HTTP Status Codes */
SOUP_STATUS_CONTINUE = 100,