--- elinks-0.11.1/Makefile.config.in.negotiate 2006-01-29 14:10:33.000000000 +0100 +++ elinks-0.11.1/Makefile.config.in 2006-06-09 13:09:41.000000000 +0200 @@ -148,6 +148,7 @@ CONFIG_WIN32 = @CONFIG_WIN32@ CONFIG_XBEL_BOOKMARKS = @CONFIG_XBEL_BOOKMARKS@ CONFIG_XMLTO = @CONFIG_XMLTO@ +CONFIG_GSSAPI = @CONFIG_GSSAPI@ DEFS = @DEFS@ CFLAGS = @CFLAGS@ --- elinks-0.11.1/config.h.in.negotiate 2006-01-29 14:10:46.000000000 +0100 +++ elinks-0.11.1/config.h.in 2006-06-09 13:09:41.000000000 +0200 @@ -84,6 +84,9 @@ /* Define if you want: gpm support */ #undef CONFIG_GPM +/* Define if you want: GssApi support */ +#undef CONFIG_GSSAPI + /* Define if you want: Guile support */ #undef CONFIG_GUILE --- elinks-0.11.1/src/protocol/http/Makefile.negotiate 2006-01-29 14:10:39.000000000 +0100 +++ elinks-0.11.1/src/protocol/http/Makefile 2006-06-09 13:09:41.000000000 +0200 @@ -1,6 +1,8 @@ top_builddir=../../.. include $(top_builddir)/Makefile.config +OBJS-$(CONFIG_GSSAPI) += http_negotiate.o + OBJS = blacklist.o codes.o http.o include $(top_srcdir)/Makefile.lib --- /dev/null 2006-05-28 11:31:41.625940250 +0200 +++ elinks-0.11.1/src/protocol/http/http_negotiate.h 2006-06-09 13:09:41.000000000 +0200 @@ -0,0 +1,16 @@ + +#ifndef EL__PROTOCOL_HTTP_HTTP_NEGOTIATE_H +#define EL__PROTOCOL_HTTP_HTTP_NEGOTIATE_H + +#define PROTOCOL_HTTP_GSSNEG 1 +#define PROTOCOL_HTTP_NEG 2 + + +int http_negotiate_input(struct connection *conn, struct uri *uri, + int type, unsigned char *data); + +int http_negotiate_output(struct uri *uri, struct string *header); + + +#endif /* EL_PROTOCOL_HTTP_HTTP_NEGOTIATE_H */ + --- /dev/null 2006-05-28 11:31:41.625940250 +0200 +++ elinks-0.11.1/src/protocol/http/http_negotiate.c 2006-06-09 13:14:25.000000000 +0200 @@ -0,0 +1,259 @@ +/* + * HTTP Negotiate authentication method -- based on GSSAPI + * + * The Microsoft version with SPNEGO is unsupported. If you look for way how + * extend this code with SPNEGO see libcurl or firefox source code where is + * supported GSSAPI+SPNEGO. + * + * Copyright (C) 2006 Red Hat, Inc. + * Karel Zak + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include +#include +#include +#include + +#include + +#include "elinks.h" +#include "network/connection.h" +#include "protocol/uri.h" +#include "protocol/http/http.h" +#include "protocol/http/http_negotiate.h" +#include "util/base64.h" +#include "main/object.h" +#include "util/lists.h" + +struct negotiate { + OBJECT_HEAD(struct negotiate); + + struct uri *uri; + + int type; /* GSS-Negotiate or Negotiate or zero */ + OM_uint32 status; + gss_ctx_id_t context; + gss_name_t server_name; + gss_buffer_desc output_token; +}; + +static INIT_LIST_HEAD(negotiate_list); + +static struct negotiate * +http_negotiate_get(struct uri *uri, int *isnew, int alloc) +{ + struct negotiate *neg; + + foreach (neg, negotiate_list) { + if (compare_uri(neg->uri, uri, URI_HTTP_REFERRER_HOST)) + return neg; + } + if (!alloc) + return NULL; + if (isnew) + *isnew = 1; + + if (!(neg = mem_calloc(1, sizeof(*neg)))) + return NULL; + + memset(neg, 0, sizeof(*neg)); + neg->uri = get_uri_reference(uri); + + return neg; +} + +static void +http_negotiate_save(struct negotiate *neg) +{ + add_to_list(negotiate_list, neg); +} + +static void +http_negotiate_cleanup(struct negotiate *neg, int full) +{ + OM_uint32 minor_status; + + if (neg->context != GSS_C_NO_CONTEXT) + gss_delete_sec_context(&minor_status, &neg->context, GSS_C_NO_BUFFER); + + if (neg->output_token.length != 0) + gss_release_buffer(&minor_status, &neg->output_token); + + if (full && neg->server_name) + gss_release_name(&minor_status, &neg->server_name); + + if (full) + memset(neg, 0, sizeof(*neg)); +} + +static int +http_negotiate_get_name(struct connection *conn, struct negotiate *neg) +{ + OM_uint32 major_status, minor_status; + gss_buffer_desc token = GSS_C_EMPTY_BUFFER; + char name[2048]; + const char* service; + struct uri *uri = conn->proxied_uri; + + /* GSSAPI implementation by Globus (known as GSI) requires the name to be + of form "/" instead of @ (ie. slash instead + of at-sign). Also GSI servers are often identified as 'host' not 'khttp'. + Change following lines if you want to use GSI */ + + /* IIS uses the @ form but uses 'http' as the service name */ + + if (neg->type == PROTOCOL_HTTP_GSSNEG) + service = "KHTTP"; + else + service = "HTTP"; + + token.length = strlen(service) + 1 + uri->hostlen + 1; + if (token.length + 1 > sizeof(name)) + return EMSGSIZE; + + snprintf(name, token.length, "%s@%*s", service, uri->hostlen, uri->host); + + token.value = (void *) name; + major_status = gss_import_name(&minor_status, + &token, + GSS_C_NT_HOSTBASED_SERVICE, + &neg->server_name); + + return GSS_ERROR(major_status) ? -1 : 0; +} + +#define GSSNEG_LEN sizeof("GSS-Negotiate") +#define NEG_LEN sizeof("Negotiate") + +static int +http_negotiate_parse_data(unsigned char *data, int type, + gss_buffer_desc *token) +{ + int len = 0; + unsigned char *end; + + if (data==NULL || *data=='\0') + return 0; + + data += type==PROTOCOL_HTTP_GSSNEG ? GSSNEG_LEN : NEG_LEN; + + while(*data && isspace((int)*data)) + data++; + + if (*data=='\0' || *data==ASCII_CR || *data==ASCII_LF) + return 0; /* no data */ + + end = data; + while (isalnum((int) *end) || *end=='=') + end++; + + /* Ignore line if we encountered an unexpected char. */ + if (*end != ASCII_CR && *end != ASCII_LF) + return 0; + + len = end - data; + + if (!len) + return 0; + + token->value = (void *) base64_decode_bin(data, len, &token->length); + + if (!token->value) + return -1; + + return 0; +} + +int +http_negotiate_input(struct connection *conn, struct uri *uri, + int type, unsigned char *data) +{ + OM_uint32 major_status, minor_status, minor_status2; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + struct negotiate *neg; + int ret, isnew = 0; + + neg = http_negotiate_get(uri, &isnew, 1); + + if (neg->context) { + if (type != PROTOCOL_HTTP_GSSNEG) + return -1; + } + neg->type = type; + + if (neg->context && neg->status == GSS_S_COMPLETE) { + /* We finished succesfully our part of authentication, but server + * rejected it (since we're again here). Exit with an error since we + * can't invent anything better */ + http_negotiate_cleanup(neg, 1); + return -1; + } + if (neg->server_name == NULL && (ret = http_negotiate_get_name(conn, neg))) + return ret; + if (data && http_negotiate_parse_data(data, type, &input_token)) + return -1; + + major_status = gss_init_sec_context(&minor_status, + GSS_C_NO_CREDENTIAL, + &neg->context, + neg->server_name, + GSS_C_NO_OID, + GSS_C_DELEG_FLAG, + 0, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + NULL, + &output_token, + NULL, + NULL); + if (input_token.length > 0) + gss_release_buffer(&minor_status2, &input_token); + neg->status = major_status; + + if (GSS_ERROR(major_status)) + return -1; + if (output_token.length == 0) + return -1; + + neg->output_token = output_token; + + if (isnew) + http_negotiate_save(neg); + + return 0; +} + +int +http_negotiate_output(struct uri *uri, struct string *header) +{ + struct negotiate *neg; + char *encoded = NULL; + int len = 0; + + neg = http_negotiate_get(uri, NULL, 0); + if (neg==NULL || neg->output_token.length==0) + return -1; + + encoded = base64_encode_bin((unsigned char *) neg->output_token.value, + neg->output_token.length, &len); + + if (encoded==NULL || len==0) + return -1; + + add_to_string(header, "Authorization: "); + add_to_string(header, neg->type==PROTOCOL_HTTP_GSSNEG ? + "GSS-Negotiate " : "Negotiate "); + add_to_string(header, encoded); + add_crlf_to_string(header); + + http_negotiate_cleanup(neg, 0); + return 0; +} + --- elinks-0.11.1/src/protocol/http/http.c.negotiate 2006-01-29 14:10:39.000000000 +0100 +++ elinks-0.11.1/src/protocol/http/http.c 2006-06-09 13:09:41.000000000 +0200 @@ -47,6 +47,9 @@ #include "util/memory.h" #include "util/string.h" +#ifdef CONFIG_GSSAPI +#include "http_negotiate.h" +#endif struct http_version { int major; @@ -82,7 +85,6 @@ int code; }; - static struct auth_entry proxy_auth; static unsigned char *accept_charset = NULL; @@ -551,7 +553,7 @@ int trace = get_opt_bool("protocol.http.trace"); struct string header; unsigned char *post_data = NULL; - struct auth_entry *entry; + struct auth_entry *entry = NULL; struct uri *uri = conn->proxied_uri; /* Set to the real uri */ unsigned char *optstr; int use_connect, talking_to_proxy; @@ -808,7 +810,11 @@ add_crlf_to_string(&header); } - entry = find_auth(uri); +#ifdef CONFIG_GSSAPI + if (http_negotiate_output(uri, &header) != 0) +#endif + entry = find_auth(uri); + if (entry) { if (entry->digest) { unsigned char *response; @@ -1327,12 +1333,12 @@ return 0; } - -static void -check_http_authentication(struct uri *uri, unsigned char *header, - unsigned char *header_field) +static int +check_http_authentication(struct connection *conn, struct uri *uri, + unsigned char *header, unsigned char *header_field) { unsigned char *str, *d; + int ret = 0; d = parse_header(header, header_field, &str); while (d) { @@ -1358,10 +1364,24 @@ mem_free(d); break; } - +#ifdef CONFIG_GSSAPI + else if (!strncasecmp(d, "GSS-Negotiate", 13)) { + if (http_negotiate_input(conn, uri, PROTOCOL_HTTP_GSSNEG, str)==0) + ret = 1; + mem_free(d); + break; + } + else if (!strncasecmp(d, "Negotiate", 9)) { + if (http_negotiate_input(conn, uri, PROTOCOL_HTTP_NEG, str)==0) + ret = 1; + mem_free(d); + break; + } +#endif mem_free(d); d = parse_header(str, header_field, &str); } + return ret; } @@ -1588,11 +1608,17 @@ } if (h == 401) { - unsigned char *head = conn->cached->head; - - check_http_authentication(uri, head, "WWW-Authenticate"); + int ret = check_http_authentication(conn, uri, + conn->cached->head, "WWW-Authenticate"); + + + if (ret) { + // XXX: mem_free(conn->cached->head); + retry_connection(conn, S_RESTART); + return; + } + } - if (h == 407) { unsigned char *str; --- elinks-0.11.1/src/util/base64.c.negotiate 2006-01-29 14:10:39.000000000 +0100 +++ elinks-0.11.1/src/util/base64.c 2006-06-09 13:09:41.000000000 +0200 @@ -17,14 +17,21 @@ unsigned char * base64_encode(register unsigned char *in) { + assert(in && *in); + if_assert_failed return NULL; + + return base64_encode_bin((char *) in, strlen(in), NULL); +} + +unsigned char * +base64_encode_bin(register unsigned char *in, int inlen, int *outlen) +{ unsigned char *out; unsigned char *outstr; - int inlen; assert(in && *in); if_assert_failed return NULL; - inlen = strlen(in); out = outstr = mem_alloc((inlen / 3) * 4 + 4 + 1); if (!out) return NULL; @@ -49,16 +56,29 @@ } *out = 0; + if (outlen) + *outlen = out-outstr; + return outstr; } -/* Base64 decoding is used only with the CONFIG_FORMHIST feature, so i'll #ifdef it */ -#ifdef CONFIG_FORMHIST +/* Base64 decoding is used only with the CONFIG_FORMHIST or CONFIG_GSSAPI + feature, so i'll #ifdef it */ +#if defined(CONFIG_FORMHIST) || defined(CONFIG_GSSAPI) + +unsigned char * +base64_decode(register unsigned char *in) +{ + assert(in && *in); + if_assert_failed return NULL; + + return base64_decode_bin(in, strlen(in), NULL); +} /* base64_decode: @in string to decode * returns the string decoded (must be freed by the caller) */ unsigned char * -base64_decode(register unsigned char *in) +base64_decode_bin(register unsigned char *in, int inlen, int *outlen) { static unsigned char is_base64_char[256]; /* static to force initialization at zero */ static unsigned char decode[256]; @@ -71,7 +91,7 @@ assert(in && *in); if_assert_failed return NULL; - outstr = out = mem_alloc(strlen(in) / 4 * 3 + 1); + outstr = out = mem_alloc(inlen / 4 * 3 + 1); if (!outstr) return NULL; if (!once) { @@ -123,6 +143,10 @@ } *out = 0; + + if (outlen) + *outlen = out-outstr; + return outstr; decode_error: --- elinks-0.11.1/src/util/base64.h.negotiate 2006-01-29 14:10:39.000000000 +0100 +++ elinks-0.11.1/src/util/base64.h 2006-06-09 13:09:41.000000000 +0200 @@ -4,4 +4,7 @@ unsigned char *base64_encode(unsigned char *); unsigned char *base64_decode(unsigned char *); +unsigned char *base64_encode_bin(unsigned char *, int, int *); +unsigned char *base64_decode_bin(unsigned char *, int, int *); + #endif --- elinks-0.11.1/configure.in.negotiate 2006-06-09 13:09:41.000000000 +0200 +++ elinks-0.11.1/configure.in 2006-06-09 13:09:41.000000000 +0200 @@ -447,6 +447,30 @@ [ --without-idn disable international domain names support]) dnl =================================================================== +dnl Check for GSSAPI, optional even if installed. +dnl =================================================================== + +enable_gssapi="no"; + +AC_ARG_WITH(gssapi, [ --with-gssapi enable GSSAPI support], + [ if test "x$withval" != xno; then enable_gssapi=yes; fi ]) + +AC_MSG_CHECKING([for GSSAPI]) + +if test "$enable_gssapi" = "yes"; then + AC_MSG_RESULT(yes) + GSSAPI_CFLAGS=`krb5-config --cflags gssapi` + GSSAPI_LIBS=`krb5-config --libs gssapi` + CFLAGS="$GSSAPI_CFLAGS $CFLAGS" + LIBS="$GSSAPI_LIBS $LIBS" + EL_CONFIG(CONFIG_GSSAPI, [GssApi]) +else + AC_MSG_RESULT(no) +fi + +AC_SUBST(CONFIG_GSSAPI) + +dnl =================================================================== dnl Bookmark and XBEL support dnl ===================================================================