mirror of
https://github.com/rkd77/elinks.git
synced 2025-10-21 19:54:04 -04:00
Add forgotten files from negotiate-auth patch.
This commit is contained in:
committed by
Laurent MONIN
parent
b373ded19b
commit
2e0d5aa197
290
src/protocol/http/http_negotiate.c
Normal file
290
src/protocol/http/http_negotiate.c
Normal file
@@ -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 <kzak@redhat.com>
|
||||
*/
|
||||
|
||||
#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, "%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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
21
src/protocol/http/http_negotiate.h
Normal file
21
src/protocol/http/http_negotiate.h
Normal file
@@ -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 */
|
||||
|
||||
Reference in New Issue
Block a user