On Wed, Jun 14, 2006 at 09:30:44AM +0200, Zas wrote:
> On Wed, Jun 14, 2006 at 01:49:40AM +0200, Karel Zak wrote:
> > 
> >  Hi guys,
> 
> Hi,
> 
> > 
> >  here is a patch with negotiate-auth support based on GSSAPI. It's
> >  very useful feature for intranet web sites where is possible
> >  authenticate users by Kerberos. (This feature is already implemented
> >  in firefox and libcurl.)
> > 
> 
> This patch looks great, apart from the ELinks style conformity POV ;)
> 
> Have a serious look at doc/hacking.txt, fix the patch accordingly, and
> resubmit it.

 Fixed.

    Karel


 Makefile.config.in                 |    1 
 configure.in                       |   24 +++
 src/protocol/http/Makefile         |    2 
 src/protocol/http/http.c           |   46 ++++--
 src/protocol/http/http_negotiate.c |  290 ++++++++++++++++++++++++++++++++++++
 src/protocol/http/http_negotiate.h |   21 +++
 src/util/base64.c                  |   36 ++++
 src/util/base64.h                  |    3 
 8 files changed, 406 insertions(+), 17 deletions(-)
 create mode 100644 src/protocol/http/http_negotiate.c
 create mode 100644 src/protocol/http/http_negotiate.h

diff --git a/Makefile.config.in b/Makefile.config.in
index 4ab2bf2..8854477 100644
--- a/Makefile.config.in
+++ b/Makefile.config.in
@@ -158,6 +158,7 @@ CONFIG_SYSMOUSE = @CONFIG_SYSMOUSE@
 CONFIG_URI_REWRITE = @CONFIG_URI_REWRITE@
 CONFIG_XBEL_BOOKMARKS = @CONFIG_XBEL_BOOKMARKS@
 CONFIG_XMLTO = @CONFIG_XMLTO@
+CONFIG_GSSAPI = @CONFIG_GSSAPI@
 
 DEFS = @DEFS@
 CFLAGS = @CFLAGS@
diff --git a/configure.in b/configure.in
index 024ebc3..ad2831d 100644
--- a/configure.in
+++ b/configure.in
@@ -453,6 +453,30 @@ fi
 EL_ARG_ENABLE(CONFIG_LZMA, lzma, [lzma],
              [  --enable-lzma           enable lzma encoding 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 ===================================================================
 
diff --git a/src/protocol/http/Makefile b/src/protocol/http/Makefile
index 10ffb86..fb4dfa0 100644
--- a/src/protocol/http/Makefile
+++ b/src/protocol/http/Makefile
@@ -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
diff --git a/src/protocol/http/http.c b/src/protocol/http/http.c
index d05a99d..75df08f 100644
--- a/src/protocol/http/http.c
+++ b/src/protocol/http/http.c
@@ -47,6 +47,9 @@ #include "util/conv.h"
 #include "util/memory.h"
 #include "util/string.h"
 
+#ifdef CONFIG_GSSAPI
+#include "http_negotiate.h"
+#endif
 
 struct http_version {
        int major;
@@ -549,7 +552,7 @@ http_send_header(struct socket *socket)
        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;
@@ -806,7 +809,11 @@ #endif
                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;
@@ -1325,12 +1332,13 @@ get_header(struct read_buffer *rb)
        return 0;
 }
 
-
-static void
-check_http_authentication(struct uri *uri, unsigned char *header,
-                         unsigned char *header_field)
+/* returns 1 if we need retry the connection (for negotiate-auth only) */
+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) {
@@ -1356,10 +1364,24 @@ check_http_authentication(struct uri *ur
                        mem_free(d);
                        break;
                }
-
+#ifdef CONFIG_GSSAPI
+               else if (!strncasecmp(d, HTTPNEG_GSS_STR, HTTPNEG_GSS_STRLEN)) {
+                       if (http_negotiate_input(conn, uri, HTTPNEG_GSS, 
str)==0)
+                               ret = 1;
+                       mem_free(d);
+                       break;
+               }
+               else if (!strncasecmp(d, HTTPNEG_NEG_STR, HTTPNEG_NEG_STRLEN)) {
+                       if (http_negotiate_input(conn, uri, HTTPNEG_NEG, 
str)==0)
+                               ret = 1;
+                       mem_free(d);
+                       break;
+               }
+#endif
                mem_free(d);
                d = parse_header(str, header_field, &str);
        }
+       return ret;
 }
 
 
@@ -1586,11 +1608,13 @@ #endif
        }
 
        if (h == 401) {
-               unsigned char *head = conn->cached->head;
-
-               check_http_authentication(uri, head, "WWW-Authenticate");
+               if (check_http_authentication(conn, uri, 
+                               conn->cached->head, "WWW-Authenticate")) {
+                       retry_connection(conn, S_RESTART);
+                       return;
+               }
+       
        }
-
        if (h == 407) {
                unsigned char *str;
 
diff --git a/src/protocol/http/http_negotiate.c 
b/src/protocol/http/http_negotiate.c
new file mode 100644
index 0000000..08a8b5d
--- /dev/null
+++ b/src/protocol/http/http_negotiate.c
@@ -0,0 +1,290 @@
+/* 
+ * 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 <[EMAIL PROTECTED]> 
+ */
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include <gssapi/gssapi.h>
+
+#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;
+       gss_buffer_desc input_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;
+
+       neg = mem_calloc(1, sizeof(*neg));
+       if (!neg)
+               return NULL;
+               
+       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) {
+               if (neg->server_name)
+                       gss_release_name(&minor_status, &neg->server_name);
+
+               if (neg->input_token.length != 0) {
+                       /* allocated by mem_free().. so beter not use 
gss_release_buffer() */
+                       mem_free(neg->input_token.value);
+                       neg->input_token.length = 0;
+               }       
+       
+               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 "<service>/<fqdn>" instead of <service>@<fqdn> (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 <service>@<fqdn> form but uses 'http' as the service 
name 
+        */
+       if (neg->type == HTTPNEG_GSS)
+               service = "KHTTP";
+       else
+               service = "HTTP";
+
+       token.length = strlen(service) + 1 + uri->hostlen + 1;
+       if (token.length + 1 > sizeof(name))
+               return -1;
+
+       snprintf(name, token.length, "[EMAIL PROTECTED]", 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;
+}
+
+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 == HTTPNEG_GSS ? HTTPNEG_GSS_STRLEN : HTTPNEG_NEG_STRLEN;
+       
+       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;
+}
+
+static int
+http_negotiate_create_context(struct negotiate *neg)
+{
+       OM_uint32 major_status, minor_status;
+
+       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,
+                                           &neg->input_token,
+                                           NULL,
+                                           &neg->output_token,
+                                           NULL,
+                                           NULL);
+       neg->status = major_status;
+
+       if (GSS_ERROR(major_status)) 
+               return -1;
+       if (neg->output_token.length == 0) 
+               return -1;
+
+       return 0;
+}
+
+/*
+ * Register new negotiate-auth request
+ *
+ * It's possible that server sends to client input token (at least
+ * libcurl supports it) in WWW-Authenticate header, but ususaly 
+ * is this input token undefined.
+ */
+int 
+http_negotiate_input(struct connection *conn, struct uri *uri, 
+                                       int type, unsigned char *data)
+{
+       struct negotiate *neg;
+       int ret = 0, isnew = 0;
+
+       neg = http_negotiate_get(uri, &isnew, 1);
+       
+       if (neg->context) {
+               if (type != HTTPNEG_GSS) 
+                       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 && http_negotiate_get_name(conn, neg) < 0)
+               return -1;
+       if (data && http_negotiate_parse_data(data, type, &neg->input_token)) 
+               return -1;
+       ret = http_negotiate_create_context(neg);
+       if (ret == 0 && isnew)
+               http_negotiate_save(neg);
+
+       return ret;
+}
+
+/*
+ * Fill output token to "Authorization: Negotiate <token>".
+ */
+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)
+               return -1;
+       
+       if (neg->output_token.length == 0) {
+               if (http_negotiate_create_context(neg) < 0) {           
+                       /* full cleanup on error and ask for 
+                        * new WWW-Authenticate from server 
+                        */
+                       http_negotiate_cleanup(neg, 1);
+                       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 == HTTPNEG_GSS ? 
+                       HTTPNEG_GSS_STR : HTTPNEG_NEG_STR);
+       add_char_to_string(header, ' ');
+       add_to_string(header, encoded);
+       add_crlf_to_string(header);
+       
+       http_negotiate_cleanup(neg, 0);
+
+       mem_free(encoded);
+       
+       return 0;
+}
+
diff --git a/src/protocol/http/http_negotiate.h 
b/src/protocol/http/http_negotiate.h
new file mode 100644
index 0000000..8666fdd
--- /dev/null
+++ b/src/protocol/http/http_negotiate.h
@@ -0,0 +1,21 @@
+
+#ifndef EL__PROTOCOL_HTTP_HTTP_NEGOTIATE_H
+#define EL__PROTOCOL_HTTP_HTTP_NEGOTIATE_H
+
+#define HTTPNEG_GSS            1
+#define HTTPNEG_NEG            2
+
+#define HTTPNEG_GSS_STR                "GSS-Negotiate"
+#define HTTPNEG_NEG_STR                "Negotiate"
+
+#define HTTPNEG_GSS_STRLEN     sizeof(HTTPNEG_GSS_STR)
+#define HTTPNEG_NEG_STRLEN     sizeof(HTTPNEG_NEG_STR)
+
+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 */
+
diff --git a/src/util/base64.c b/src/util/base64.c
index 8a3918e..2c2e9ad 100644
--- a/src/util/base64.c
+++ b/src/util/base64.c
@@ -17,14 +17,21 @@ static unsigned char base64_chars[] = "A
 unsigned char *
 base64_encode(register unsigned char *in)
 {
+       assert(in && *in);
+       if_assert_failed return NULL;
+       
+       return base64_encode_bin(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 @@ base64_encode(register unsigned char *in
        }
        *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 @@ base64_decode(register unsigned char *in
        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 @@ base64_decode(register unsigned char *in
        }
 
        *out = 0;
+
+       if (outlen)
+               *outlen = out-outstr;
+
        return outstr;
 
 decode_error:
diff --git a/src/util/base64.h b/src/util/base64.h
index cb5cd73..2bdf0e4 100644
--- a/src/util/base64.h
+++ b/src/util/base64.h
@@ -4,4 +4,7 @@ #define EL__UTIL_BASE64_H
 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-dev mailing list
[email protected]
http://linuxfromscratch.org/mailman/listinfo/elinks-dev

Reply via email to