Re: [patch]: negotiate-auth

Zas <[email protected]>
Newsgroups gmane.comp.web.links
Message-ID <[email protected]>
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.)
> 
>     Karel
> 
> 
> 

[snip]

> +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);	
> +	

this should be rewritten (see doc/hacking.txt)

neg = mem_calloc(1, sizeof(*neg));
if (!neg)
	return NULL;

neg->uri = get_uri_reference(uri); 

Since mem_calloc() is used, memset() is superfluous.

> +	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 && 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;
> +	}
> +	
> +	if (full)
> +		memset(neg, 0, sizeof(*neg));
> +}

What about:

	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));
	}


I perhaps missed smt though.

> +
> +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 == PROTOCOL_HTTP_GSSNEG)
> +		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, "%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++;

Please add spaces around == operator.

> +
> +	/* 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 != 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 && http_negotiate_get_name(conn, neg) < 0)
> +		return -1;
> +	if (data && http_negotiate_parse_data(data, type, &neg->input_token)) 
> +		return -1;
> +	if ((ret=http_negotiate_create_context(neg)) == 0 && isnew)
> +		http_negotiate_save(neg);


Please write:
	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;
> +
> +	if (!(neg = http_negotiate_get(uri, NULL, 0)))
> +		return -1;

Same as above.

> +	
> +	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==PROTOCOL_HTTP_GSSNEG ? 
> +				"GSS-Negotiate " : "Negotiate ");
> +	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..cfd240f
> --- /dev/null
> +++ b/src/protocol/http/http_negotiate.h
> @@ -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 */
> +
> diff --git a/src/util/base64.c b/src/util/base64.c
> index 8a3918e..7f4e74e 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((char *) in, strlen(in), NULL);


Why this type casting ?

> +}
> +
> +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
> 



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.

Regards,

-- 

Zas
_______________________________________________
elinks-dev mailing list
[email protected]
http://linuxfromscratch.org/mailman/listinfo/elinks-dev
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.