diff options
-rw-r--r-- | CHANGES | 5 | ||||
-rw-r--r-- | configure.ac | 57 | ||||
-rw-r--r-- | docs/curl.1 | 8 | ||||
-rw-r--r-- | docs/libcurl/curl_easy_setopt.3 | 24 | ||||
-rw-r--r-- | include/curl/curl.h | 4 | ||||
-rw-r--r-- | lib/Makefile.am | 2 | ||||
-rw-r--r-- | lib/http.c | 8 | ||||
-rw-r--r-- | lib/http_negotiate.c | 217 | ||||
-rw-r--r-- | lib/http_negotiate.h | 39 | ||||
-rw-r--r-- | lib/transfer.c | 17 | ||||
-rw-r--r-- | lib/url.c | 9 | ||||
-rw-r--r-- | lib/urldata.h | 20 | ||||
-rw-r--r-- | src/main.c | 19 | ||||
-rw-r--r-- | src/version.h | 2 |
14 files changed, 425 insertions, 6 deletions
@@ -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.) diff --git a/configure.ac b/configure.ac index c2245a243..2707aa2e4 100644 --- a/configure.ac +++ b/configure.ac @@ -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. diff --git a/docs/curl.1 b/docs/curl.1 index b4ec7f6d9..5e687528b 100644 --- a/docs/curl.1 +++ b/docs/curl.1 @@ -185,6 +185,14 @@ 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 downloads. Curl will normally always first attempt to use EPSV before PASV, diff --git a/docs/libcurl/curl_easy_setopt.3 b/docs/libcurl/curl_easy_setopt.3 index 4eeaac79d..ea66ace5b 100644 --- a/docs/libcurl/curl_easy_setopt.3 +++ b/docs/libcurl/curl_easy_setopt.3 @@ -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 diff --git a/include/curl/curl.h b/include/curl/curl.h index a81afba2c..46e09f429 100644 --- a/include/curl/curl.h +++ b/include/curl/curl.h @@ -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; diff --git a/lib/Makefile.am b/lib/Makefile.am index b17bac198..64cfaa74f 100644 --- a/lib/Makefile.am +++ b/lib/Makefile.am @@ -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 diff --git a/lib/http.c b/lib/http.c index 16a87d937..e5d2b49d3 100644 --- a/lib/http.c +++ b/lib/http.c @@ -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, diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c new file mode 100644 index 000000000..9a2d46a1f --- /dev/null +++ b/lib/http_negotiate.c @@ -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 diff --git a/lib/http_negotiate.h b/lib/http_negotiate.h new file mode 100644 index 000000000..09141bda7 --- /dev/null +++ b/lib/http_negotiate.h @@ -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 diff --git a/lib/transfer.c b/lib/transfer.c index 6571e1636..2eda08889 100644 --- a/lib/transfer.c +++ b/lib/transfer.c @@ -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 @@ -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 diff --git a/lib/urldata.h b/lib/urldata.h index 58f700677..4dbd4092a 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -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: */ diff --git a/src/main.c b/src/main.c index 3a52401e0..0e740f3ed 100644 --- a/src/main.c +++ b/src/main.c @@ -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; @@ -2976,6 +2991,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) { diff --git a/src/version.h b/src/version.h index 897334a59..be9b11869 100644 --- a/src/version.h +++ b/src/version.h @@ -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 ") " |