Daniel Kouril's patch that adds HTTP negotiation support to libcurl was

added.
This commit is contained in:
Daniel Stenberg 2003-06-10 12:22:19 +00:00
parent 696843c020
commit e56ae1426c
14 changed files with 425 additions and 6 deletions

View File

@ -7,6 +7,11 @@
Changelog
Daniel (10 June)
- Daniel Kouril added HTTP Negotiate authentication support, as defined in the
IETF draft draft-brezak-spnego-http-04.txt. In use already by various
Microsoft web applications. CURLOPT_HTTPNEGOTIATE and --negotiate are the
new family members.
- A missing ending bracket (']') while doing URL globbing could lead to a
segfault. While fixing this, I also introduced better error reporting in the
globbing code. (All this is application code outside libcurl.)

View File

@ -454,6 +454,63 @@ else
AC_MSG_RESULT(no)
fi
dnl **********************************************************************
dnl Check for GSS-API libraries
dnl **********************************************************************
AC_ARG_WITH(gssapi-includes,
AC_HELP_STRING([--with-gssapi-includes=DIR],
[Specify location of GSSAPI header]),
[ GSSAPI_INCS="-I$withval"
want_gss="yes" ]
)
AC_ARG_WITH(gssapi-libs,
AC_HELP_STRING([--with-gssapi-libs=DIR],
[Specify location of GSSAPI libs]),
[ GSSAPI_LIBS="-L$withval -lgssapi"
want_gss="yes" ]
)
AC_ARG_WITH(gssapi,
AC_HELP_STRING([--with-gssapi=DIR],
[Where to look for GSSAPI]),
[ GSSAPI_ROOT="$withval"
want_gss="yes" ]
)
AC_MSG_CHECKING([if GSSAPI support is requested])
if test x"$want_gss" = xyes; then
if test -z "$GSSAPI_INCS"; then
if test -f "$GSSAPI_ROOT/bin/krb5-config"; then
gss_cppflags=`$GSSAPI_ROOT/bin/krb5-config --cflags gssapi`
CPPFLAGS="$CPPFLAGS $gss_cppflags"
else
CPPFLAGS="$GSSAPI_ROOT/include"
fi
else
CPPFLAGS="$CPPFLAGS $GSSAPI_INCS"
fi
if test -z "$GSSAPI_LIB_DIR"; then
if test -f "$GSSAPI_ROOT/bin/krb5-config"; then
gss_ldflags=`$GSSAPI_ROOT/bin/krb5-config --libs gssapi`
LDFLAGS="$LDFLAGS $gss_ldflags"
else
LDFLAGS="$LDFLAGS $GSSAPI_ROOT/lib -lgssapi"
fi
else
LDFLAGS="$LDFLAGS $GSSAPI_LIB_DIR"
fi
AC_MSG_RESULT(yes)
AC_DEFINE(GSSAPI, 1, [if you have the gssapi libraries])
else
AC_MSG_RESULT(no)
fi
dnl Detect the pkg-config tool, as it may have extra info about the
dnl openssl installation we can use. I *believe* this is what we are
dnl expected to do on really recent Redhat Linux hosts.

View File

@ -184,6 +184,14 @@ method than the default Basic method, and prevents the password from being
sent over the wire in clear text. Use this in combination with the normal
-u/--user option to set user name and password. (Option added in curl 7.10.6)
If this option is used several times, each occurrence will toggle this on/off.
.IP "--negotiate"
(HTTP) Enables Negotiate authentication. The Negotiate method was designed by
Microsoft and is used in their web aplications. It is primarily meant as a
support for Kerberos5 authentication but may be also used along with another
authentication methods. For more information see IETF draft
draft-brezak-spnego-http-04.txt.
If this option is used several times, each occurrence will toggle this on/off.
.IP "--disable-epsv"
(FTP) Tell curl to disable the use of the EPSV command when doing passive FTP

View File

@ -272,7 +272,7 @@ The main point of this would be that the write callback gets called more often
and with smaller chunks. This is just treated as a request, not an order. You
cannot be guaranteed to actually get the given size. (Added in 7.10)
.PP
.SH NAMES and PASSWORDS OPTIONS
.SH NAMES and PASSWORDS OPTIONS (Authentication)
.TP 0.4i
.B CURLOPT_NETRC
This parameter controls the preference of libcurl between using user names and
@ -322,15 +322,31 @@ prompt function.
When using HTTP and CURLOPT_FOLLOWLOCATION, libcurl might perform several
requests to possibly different hosts. libcurl will only send this user and
password information to hosts using the initial host name, so if libcurl
follows locations to other hosts it will not send the user and password to
those. This is enforced to prevent accidental information leakage.
password information to hosts using the initial host name (unless
CURLOPT_UNRESTRICTED_AUTH is set), so if libcurl follows locations to other
hosts it will not send the user and password to those. This is enforced to
prevent accidental information leakage.
.TP
.B CURLOPT_PROXYUSERPWD
Pass a char * as parameter, which should be [user name]:[password] to use for
the connection to the HTTP proxy. If the password is left out, you will be
prompted for it. \fICURLOPT_PASSWDFUNCTION\fP can be used to set your own
prompt function.
.TP
.B CURLOPT_HTTPDIGEST
Pass a long set to a non-zero value to enable HTTP Digest authentication.
Digest authentication is defined in RFC2617 and is a somewhat more secure way
to do user+password checking over public networks than the regular
old-fashioned Basic authentication. By default, libcurl uses Basic. Set name
and password with the CURLOPT_USERPWD option. (Added in 7.10.6)
.TP
.B CURLOPT_HTTPNEGOTIATE
Pass a long set to a non-zero value to enable HTTP Negotiate authentication.
The Negotiate method was designed by Microsoft and is used in their web
aplications. It is primarily meant as a support for Kerberos5 authentication
but may be also used along with another authentication methods. For more
information see IETF draft draft-brezak-spnego-http-04.txt. Set name and
password with the CURLOPT_USERPWD option. (Added in 7.10.6)
.PP
.SH HTTP OPTIONS
.TP 0.4i

View File

@ -629,6 +629,10 @@ typedef enum {
You should use this in combination with CURLOPT_USERPWD. */
CINIT(HTTPDIGEST, LONG, 107),
/* Set this to a non-zero value to enable HTTP Negotiate Authentication.
You should use this in combination with CURLOPT_USERPWD. */
CINIT(HTTPNEGOTIATE, LONG, 108),
CURLOPT_LASTENTRY /* the last unused */
} CURLoption;

View File

@ -68,7 +68,7 @@ strequal.h easy.c security.h security.c krb4.c krb4.h memdebug.c \
memdebug.h inet_ntoa_r.h http_chunks.c http_chunks.h strtok.c strtok.h \
connect.c connect.h llist.c llist.h hash.c hash.h multi.c \
content_encoding.c content_encoding.h share.c share.h http_digest.c \
md5.c md5.h http_digest.h
md5.c md5.h http_digest.h http_negotiate.c http_negotiate.h
noinst_HEADERS = setup.h transfer.h

View File

@ -688,6 +688,14 @@ CURLcode Curl_http(struct connectdata *conn)
conn->allocptr.uagent=NULL;
}
#ifdef GSSAPI
if (data->state.negotiate.context &&
!GSS_ERROR(data->state.negotiate.status)) {
result = Curl_output_negotiate(conn);
if (result)
return result;
} else
#endif
if(data->state.digest.nonce) {
result = Curl_output_digest(conn,
(unsigned char *)request,

217
lib/http_negotiate.c Normal file
View File

@ -0,0 +1,217 @@
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2003, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#include "setup.h"
#ifdef GSSAPI
#ifndef CURL_DISABLE_HTTP
/* -- WIN32 approved -- */
#include <stdio.h>
#include <string.h>
#include <stdarg.h>
#include <stdlib.h>
#include <ctype.h>
#include "urldata.h"
#include "sendf.h"
#include "strequal.h"
#include "http_negotiate.h"
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
/* The last #include file should be: */
#ifdef MALLOCDEBUG
#include "memdebug.h"
#endif
static int
get_gss_name(struct connectdata *conn, gss_name_t *server)
{
OM_uint32 major_status, minor_status;
gss_buffer_desc token = GSS_C_EMPTY_BUFFER;
char name[2048];
#ifdef KRB5
token.length = strlen("khttp@") + strlen(conn->hostname) + 1;
#els
token.length = strlen("host/") + strlen(conn->hostname) + 1;
#endif
if (token.length + 1 > sizeof(name))
return EMSGSIZE;
#ifdef KRB5
sprintf(name, "khttp@%s", conn->hostname);
#else
sprintf(name, "host/%s", conn->hostname);
#endif
token.value = (void *) name;
major_status = gss_import_name(&minor_status,
&token,
GSS_C_NT_HOSTBASED_SERVICE,
server);
return GSS_ERROR(major_status) ? -1 : 0;
}
static void
log_gss_error(struct connectdata *conn, OM_uint32 error_status, char *prefix)
{
OM_uint32 maj_stat, min_stat;
OM_uint32 msg_ctx = 0;
gss_buffer_desc status_string;
char buf[1024];
size_t len;
snprintf(buf, sizeof(buf), "%s", prefix);
len = strlen(buf);
do {
maj_stat = gss_display_status (&min_stat,
error_status,
GSS_C_MECH_CODE,
GSS_C_NO_OID,
&msg_ctx,
&status_string);
if (sizeof(buf) > len + status_string.length + 1) {
sprintf(buf + len, ": %s", (char*) status_string.value);
len += status_string.length;
}
gss_release_buffer(&min_stat, &status_string);
} while (!GSS_ERROR(maj_stat) && msg_ctx != 0);
infof(conn->data, buf);
}
CURLcode Curl_input_negotiate(struct connectdata *conn, char *header)
{
struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
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;
int ret;
size_t len;
while(*header && isspace((int)*header))
header++;
if(!checkprefix("GSS-Negotiate", header))
return -1;
if (neg_ctx->context && neg_ctx->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 */
Curl_cleanup_negotiate(conn->data);
return -1;
}
if (neg_ctx->server_name == NULL &&
(ret = get_gss_name(conn, &neg_ctx->server_name)))
return ret;
header += strlen("GSS-Negotiate");
while(*header && isspace((int)*header))
header++;
len = strlen(header);
if (len > 0) {
input_token.length = (len+3)/4 * 3;
input_token.value = malloc(input_token.length);
if (input_token.value == NULL)
return ENOMEM;
input_token.length = Curl_base64_decode(header, input_token.value);
if (input_token.length < 0)
return -1;
}
major_status = gss_init_sec_context(&minor_status,
GSS_C_NO_CREDENTIAL,
&neg_ctx->context,
neg_ctx->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_ctx->status = major_status;
if (GSS_ERROR(major_status)) {
/* Curl_cleanup_negotiate(conn->data) ??? */
log_gss_error(conn, minor_status, "gss_init_sec_context() failed: ");
return -1;
}
if (output_token.length == 0) {
return -1;
}
neg_ctx->output_token = output_token;
/* conn->bits.close = FALSE; */
return 0;
}
CURLcode Curl_output_negotiate(struct connectdata *conn)
{
struct negotiatedata *neg_ctx = &conn->data->state.negotiate;
OM_uint32 minor_status;
char *encoded = NULL;
size_t len;
len = Curl_base64_encode(neg_ctx->output_token.value,
neg_ctx->output_token.length,
&encoded);
if (len < 0)
return -1;
conn->allocptr.userpwd =
aprintf("Authorization: GSS-Negotiate %s\r\n", encoded);
free(encoded);
gss_release_buffer(&minor_status, &neg_ctx->output_token);
return (conn->allocptr.userpwd == NULL) ? ENOMEM : 0;
}
void Curl_cleanup_negotiate(struct SessionHandle *data)
{
OM_uint32 minor_status;
struct negotiatedata *neg_ctx = &data->state.negotiate;
if (neg_ctx->context != GSS_C_NO_CONTEXT)
gss_delete_sec_context(&minor_status, &neg_ctx->context, GSS_C_NO_BUFFER);
if (neg_ctx->output_token.length != 0)
gss_release_buffer(&minor_status, &neg_ctx->output_token);
if (neg_ctx->server_name != GSS_C_NO_NAME)
gss_release_name(&minor_status, &neg_ctx->server_name);
memset(neg_ctx, 0, sizeof(*neg_ctx));
}
#endif
#endif

39
lib/http_negotiate.h Normal file
View File

@ -0,0 +1,39 @@
#ifndef __HTTP_NEGOTIATE_H
#define __HTTP_NEGOTIATE_H
/***************************************************************************
* _ _ ____ _
* Project ___| | | | _ \| |
* / __| | | | |_) | |
* | (__| |_| | _ <| |___
* \___|\___/|_| \_\_____|
*
* Copyright (C) 1998 - 2003, Daniel Stenberg, <daniel@haxx.se>, et al.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://curl.haxx.se/docs/copyright.html.
*
* You may opt to use, copy, modify, merge, publish, distribute and/or sell
* copies of the Software, and permit persons to whom the Software is
* furnished to do so, under the terms of the COPYING file.
*
* This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
* KIND, either express or implied.
*
* $Id$
***************************************************************************/
#ifdef GSSAPI
/* this is for Negotiate header input */
CURLcode Curl_input_negotiate(struct connectdata *conn, char *header);
/* this is for creating Negotiate header output */
CURLcode Curl_output_negotiate(struct connectdata *conn);
void Curl_cleanup_negotiate(struct SessionHandle *data);
#endif
#endif

View File

@ -96,6 +96,9 @@
#include "getinfo.h"
#include "ssluse.h"
#include "http_digest.h"
#ifdef GSSAPI
#include "http_negotiate.h"
#endif
#define _MPRINTF_REPLACE /* use our functions only */
#include <curl/mprintf.h>
@ -719,6 +722,20 @@ CURLcode Curl_readwrite(struct connectdata *conn,
if(data->set.get_filetime)
data->info.filetime = k->timeofdoc;
}
#ifdef GSSAPI
else if (Curl_compareheader(k->p, "WWW-Authenticate:",
"GSS-Negotiate") &&
(401 == k->httpcode) &&
data->set.httpnegotiate) {
int neg;
neg = Curl_input_negotiate(conn,
k->p+strlen("WWW-Authenticate:"));
if (neg == 0)
/* simulate redirection to make curl send the request again */
conn->newurl = strdup(data->change.url);
}
#endif
else if(checkprefix("WWW-Authenticate:", k->p) &&
(401 == k->httpcode) &&
data->set.httpdigest /* Digest authentication is

View File

@ -108,6 +108,9 @@
#include "share.h"
#include "content_encoding.h"
#include "http_digest.h"
#ifdef GSSAPI
#include "http_negotiate.h"
#endif
/* And now for the protocols */
#include "ftp.h"
@ -847,6 +850,12 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, ...)
*/
data->set.httpdigest = va_arg(param, long);
break;
#ifdef GSSAPI
case CURLOPT_HTTPNEGOTIATE:
/* Enable HTTP Negotaiate authentication */
data->set.httpnegotiate = va_arg(param, long);
break;
#endif
case CURLOPT_USERPWD:
/*
* user:password to use in the operation

View File

@ -86,6 +86,10 @@
#include <zlib.h> /* for content-encoding 08/28/02 jhrg */
#endif
#ifdef GSSAPI
#include <gssapi.h>
#endif
/* Download buffer size, keep it fairly big for speed reasons */
#define BUFSIZE CURL_MAX_WRITE_SIZE
@ -160,6 +164,15 @@ struct digestdata {
int algo;
};
#ifdef GSSAPI
struct negotiatedata {
OM_uint32 status;
gss_ctx_id_t context;
gss_name_t server_name;
gss_buffer_desc output_token;
};
#endif
/****************************************************************************
* HTTP unique setup
***************************************************************************/
@ -627,6 +640,10 @@ struct UrlState {
is always set TRUE when curl_easy_perform() is called. */
struct digestdata digest;
#ifdef GSSAPI
struct negotiatedata negotiate;
#endif
};
@ -672,6 +689,9 @@ struct UserDefined {
long use_port; /* which port to use (when not using default) */
char *userpwd; /* <user:password>, if used */
bool httpdigest; /* if HTTP Digest is enabled */
#ifdef GSSAPI
bool httpnegotiate; /* if HTTP Negotiate authentication is enabled */
#endif
char *set_range; /* range, if used. See README for detailed specification
on this syntax. */
long followlocation; /* as in HTTP Location: */

View File

@ -359,6 +359,9 @@ static void help(void)
" -d/--data <data> HTTP POST data (H)\n"
" --data-ascii <data> HTTP POST ASCII data (H)\n"
" --data-binary <data> HTTP POST binary data (H)\n"
#ifdef GSSAPI
" --negotiate Enable HTTP Negotiate Authentication\n"
#endif
" --digest Enable HTTP Digest Authentication");
puts(" --disable-eprt Prevents curl from using EPRT or LPRT (F)\n"
" --disable-epsv Prevents curl from using EPSV (F)\n"
@ -461,6 +464,9 @@ struct Configurable {
bool cookiesession; /* new session? */
bool encoding; /* Accept-Encoding please */
bool digest; /* Digest Authentication */
#ifdef GSSAPI
bool negotiate; /* Negotiate Authentication */
#endif
bool use_resume;
bool resume_from_current;
bool disable_epsv;
@ -1053,6 +1059,9 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
{"5i", "limit-rate", TRUE},
{"5j", "compressed", FALSE}, /* might take an arg someday */
{"5k", "digest", FALSE},
#ifdef GSSAPI
{"5l", "negotiate", FALSE},
#endif
{"0", "http1.0", FALSE},
{"1", "tlsv1", FALSE},
{"2", "sslv2", FALSE},
@ -1281,6 +1290,12 @@ static ParameterError getparameter(char *flag, /* f or -long-flag */
config->digest ^= TRUE;
break;
#ifdef GSSAPI
case 'l': /* --negotiate */
config->negotiate ^= TRUE;
break;
#endif
default: /* the URL! */
{
struct getout *url;
@ -2977,6 +2992,10 @@ operate(struct Configurable *config, int argc, char *argv[])
/* new in libcurl 7.10.6 */
curl_easy_setopt(curl, CURLOPT_HTTPDIGEST, config->digest);
#ifdef GSSAPI
curl_easy_setopt(curl, CURLOPT_HTTPNEGOTIATE, config->negotiate);
#endif
/* new in curl 7.9.7 */
if(config->trace_dump) {
curl_easy_setopt(curl, CURLOPT_DEBUGFUNCTION, my_trace);

View File

@ -1,3 +1,3 @@
#define CURL_NAME "curl"
#define CURL_VERSION "7.10.5"
#define CURL_VERSION "7.10.6-pre1"
#define CURL_ID CURL_NAME " " CURL_VERSION " (" OS ") "