diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 89e6b71f8..c749168f3 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -21,7 +21,7 @@ CSOURCES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ socks_gssapi.c socks_sspi.c curl_sspi.c slist.c nonblock.c \ curl_memrchr.c imap.c pop3.c smtp.c pingpong.c rtsp.c curl_threads.c \ warnless.c hmac.c polarssl.c curl_rtmp.c openldap.c curl_gethostname.c\ - gopher.c axtls.c idn_win32.c + gopher.c axtls.c idn_win32.c http_negotiate_sspi.c HHEADERS = arpa_telnet.h netrc.h file.h timeval.h qssl.h hostip.h \ progress.h formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h \ diff --git a/lib/http.c b/lib/http.c index 2cf4dd2d3..b61426ec5 100644 --- a/lib/http.c +++ b/lib/http.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -520,7 +520,7 @@ output_auth_headers(struct connectdata *conn, struct SessionHandle *data = conn->data; const char *auth=NULL; CURLcode result = CURLE_OK; -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE struct negotiatedata *negdata = proxy? &data->state.proxyneg:&data->state.negotiate; #endif @@ -530,7 +530,7 @@ output_auth_headers(struct connectdata *conn, (void)path; #endif -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE if((authstatus->picked == CURLAUTH_GSSNEGOTIATE) && negdata->context && !GSS_ERROR(negdata->status)) { auth="GSS-Negotiate"; @@ -727,7 +727,7 @@ CURLcode Curl_http_input_auth(struct connectdata *conn, * */ -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE if(checkprefix("GSS-Negotiate", start) || checkprefix("Negotiate", start)) { int neg; diff --git a/lib/http_negotiate.h b/lib/http_negotiate.h index 35501f044..e584d76fc 100644 --- a/lib/http_negotiate.h +++ b/lib/http_negotiate.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -22,7 +22,7 @@ * ***************************************************************************/ -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE /* this is for Negotiate header input */ int Curl_input_negotiate(struct connectdata *conn, bool proxy, @@ -33,6 +33,10 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy); void Curl_cleanup_negotiate(struct SessionHandle *data); -#endif /* HAVE_GSSAPI */ +#ifdef USE_WINDOWS_SSPI +#define GSS_ERROR(status) (status & 0x80000000) +#endif + +#endif /* USE_HTTP_NEGOTIATE */ #endif /* HEADER_CURL_HTTP_NEGOTIATE_H */ diff --git a/lib/http_negotiate_sspi.c b/lib/http_negotiate_sspi.c new file mode 100644 index 000000000..39d6aefba --- /dev/null +++ b/lib/http_negotiate_sspi.c @@ -0,0 +1,292 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2011, Daniel Stenberg, , 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. + * + ***************************************************************************/ +#include "setup.h" + +#ifdef USE_WINDOWS_SSPI + +#ifndef CURL_DISABLE_HTTP +/* -- WIN32 approved -- */ +#include +#include +#include +#include +#include + +#include "urldata.h" +#include "sendf.h" +#include "rawstr.h" +#include "curl_base64.h" +#include "http_negotiate.h" +#include "curl_memory.h" + +#define _MPRINTF_REPLACE /* use our functions only */ +#include + +/* The last #include file should be: */ +#include "memdebug.h" + +static int +get_gss_name(struct connectdata *conn, bool proxy, char *server) +{ + struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: + &conn->data->state.negotiate; + const char* service; + size_t length; + + /* GSSAPI implementation by Globus (known as GSI) requires the name to be + of form "/" instead of @ (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 @ form but uses 'http' as the service name, + and SSPI then generates an NTLM token. When using / a + Kerberos token is generated. */ + + if(neg_ctx->gss) + service = "KHTTP"; + else + service = "HTTP"; + + length = strlen(service) + 1 + strlen(proxy ? conn->proxy.name : + conn->host.name) + 1; + if(length + 1 > sizeof(neg_ctx->server_name)) + return EMSGSIZE; + + snprintf(server, sizeof(neg_ctx->server_name), "%s/%s", + service, proxy ? conn->proxy.name : conn->host.name); + + return 0; +} + +/* returning zero (0) means success, everything else is treated as "failure" + with no care exactly what the failure was */ +int Curl_input_negotiate(struct connectdata *conn, bool proxy, + const char *header) +{ + struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: + &conn->data->state.negotiate; + BYTE *input_token = 0; + SecBufferDesc out_buff_desc; + SecBuffer out_sec_buff; + SecBufferDesc in_buff_desc; + SecBuffer in_sec_buff; + ULONG context_attributes; + TimeStamp lifetime; + + int ret; + size_t len = 0, input_token_len = 0; + bool gss = FALSE; + const char* protocol; + + while(*header && ISSPACE(*header)) + header++; + + if(checkprefix("GSS-Negotiate", header)) { + protocol = "GSS-Negotiate"; + gss = TRUE; + } + else if(checkprefix("Negotiate", header)) { + protocol = "Negotiate"; + gss = FALSE; + } + else + return -1; + + if(neg_ctx->context) { + if(neg_ctx->gss != gss) { + return -1; + } + } + else { + neg_ctx->protocol = protocol; + neg_ctx->gss = gss; + } + + if(neg_ctx->context && neg_ctx->status == SEC_E_OK) { + /* 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(strlen(neg_ctx->server_name) == 0 && + (ret = get_gss_name(conn, proxy, neg_ctx->server_name))) + return ret; + + if (!neg_ctx->max_token_length) { + PSecPkgInfo SecurityPackage; + ret = s_pSecFn->QuerySecurityPackageInfo("Negotiate", &SecurityPackage); + if (ret != SEC_E_OK) + return -1; + + /* Allocate input and output buffers according to the max token size + as indicated by the security package */ + neg_ctx->max_token_length = SecurityPackage->cbMaxToken; + neg_ctx->output_token = (BYTE *)malloc(neg_ctx->max_token_length); + s_pSecFn->FreeContextBuffer(SecurityPackage); + } + + /* Obtain the input token, if any */ + header += strlen(neg_ctx->protocol); + while(*header && ISSPACE(*header)) + header++; + + len = strlen(header); + if(len > 0) { + input_token = malloc(neg_ctx->max_token_length); + if(!input_token) + return -1; + + input_token_len = Curl_base64_decode(header, + (unsigned char **)&input_token); + if(input_token_len == 0) + return -1; + } + + if ( !input_token ) { + /* first call in a new negotation, we have to require credentials, + and allocate memory for the context */ + + neg_ctx->credentials = (CredHandle *)malloc(sizeof(CredHandle)); + neg_ctx->context = (CtxtHandle *)malloc(sizeof(CtxtHandle)); + + if ( !neg_ctx->credentials || !neg_ctx->context) + return -1; + + neg_ctx->status = + s_pSecFn->AcquireCredentialsHandle(NULL, "Negotiate", + SECPKG_CRED_OUTBOUND, NULL, NULL, + NULL, NULL, neg_ctx->credentials, + &lifetime); + if ( neg_ctx->status != SEC_E_OK ) + return -1; + } + + /* prepare the output buffers, and input buffers if present */ + out_buff_desc.ulVersion = 0; + out_buff_desc.cBuffers = 1; + out_buff_desc.pBuffers = &out_sec_buff; + + out_sec_buff.cbBuffer = neg_ctx->max_token_length; + out_sec_buff.BufferType = SECBUFFER_TOKEN; + out_sec_buff.pvBuffer = neg_ctx->output_token; + + + if (input_token) { + in_buff_desc.ulVersion = 0; + in_buff_desc.cBuffers = 1; + in_buff_desc.pBuffers = &out_sec_buff; + + in_sec_buff.cbBuffer = input_token_len; + in_sec_buff.BufferType = SECBUFFER_TOKEN; + in_sec_buff.pvBuffer = input_token; + } + + neg_ctx->status = s_pSecFn->InitializeSecurityContext( + neg_ctx->credentials, + input_token ? neg_ctx->context : 0, + neg_ctx->server_name, + ISC_REQ_CONFIDENTIALITY, + 0, + SECURITY_NATIVE_DREP, + input_token ? &in_buff_desc : 0, + 0, + neg_ctx->context, + &out_buff_desc, + &context_attributes, + &lifetime); + + if ( GSS_ERROR(neg_ctx->status) ) + return -1; + + if ( neg_ctx->status == SEC_I_COMPLETE_NEEDED || + neg_ctx->status == SEC_I_COMPLETE_AND_CONTINUE ) { + neg_ctx->status = s_pSecFn->CompleteAuthToken(neg_ctx->context, + &out_buff_desc); + if ( GSS_ERROR(neg_ctx->status) ) + return -1; + } + + neg_ctx->output_token_length = out_sec_buff.cbBuffer; + + return 0; +} + + +CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) +{ + struct negotiatedata *neg_ctx = proxy?&conn->data->state.proxyneg: + &conn->data->state.negotiate; + char *encoded = NULL; + size_t len; + char *userp; + + len = Curl_base64_encode(conn->data, + neg_ctx->output_token, + neg_ctx->output_token_length, + &encoded); + + if(len == 0) + return CURLE_OUT_OF_MEMORY; + + userp = aprintf("%sAuthorization: %s %s\r\n", proxy ? "Proxy-" : "", + neg_ctx->protocol, encoded); + + if(proxy) + conn->allocptr.proxyuserpwd = userp; + else + conn->allocptr.userpwd = userp; + free(encoded); + Curl_cleanup_negotiate (conn->data); + return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; +} + +static void cleanup(struct negotiatedata *neg_ctx) +{ + if(neg_ctx->context) { + s_pSecFn->DeleteSecurityContext(neg_ctx->context); + free(neg_ctx->context); + neg_ctx->context = 0; + } + + if(neg_ctx->credentials) { + s_pSecFn->FreeCredentialsHandle(neg_ctx->credentials); + free(neg_ctx->credentials); + neg_ctx->credentials = 0; + } + + if(neg_ctx->output_token) { + free(neg_ctx->output_token); + neg_ctx->output_token = 0; + } +} + +void Curl_cleanup_negotiate(struct SessionHandle *data) +{ + cleanup(&data->state.negotiate); + cleanup(&data->state.proxyneg); +} + + +#endif +#endif diff --git a/lib/setup.h b/lib/setup.h index 0902d2c6f..63f51e4fb 100644 --- a/lib/setup.h +++ b/lib/setup.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -541,6 +541,10 @@ int netware_init(void); #define USE_SSL /* SSL support has been enabled */ #endif +#if defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI) +#define USE_HTTP_NEGOTIATE +#endif + #if !defined(CURL_DISABLE_HTTP) && !defined(CURL_DISABLE_NTLM) #if defined(USE_SSLEAY) || defined(USE_WINDOWS_SSPI) || defined(USE_GNUTLS) || defined(USE_NSS) #define USE_NTLM diff --git a/lib/url.c b/lib/url.c index 6741f18bb..f8bd07aad 100644 --- a/lib/url.c +++ b/lib/url.c @@ -1454,8 +1454,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, #ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ #endif -#ifndef HAVE_GSSAPI - auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */ +#ifndef USE_HTTP_NEGOTIATE + auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or WINDOWS_SSPI */ #endif if(!auth) return CURLE_FAILED_INIT; /* no supported types left! */ @@ -1514,8 +1514,8 @@ CURLcode Curl_setopt(struct SessionHandle *data, CURLoption option, #ifndef USE_NTLM auth &= ~CURLAUTH_NTLM; /* no NTLM without SSL */ #endif -#ifndef HAVE_GSSAPI - auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI */ +#ifndef USE_HTTP_NEGOTIATE + auth &= ~CURLAUTH_GSSNEGOTIATE; /* no GSS-Negotiate without GSSAPI or WINDOWS_SSPI */ #endif if(!auth) return CURLE_FAILED_INIT; /* no supported types left! */ diff --git a/lib/urldata.h b/lib/urldata.h index 2765c3ebe..55167fbc3 100644 --- a/lib/urldata.h +++ b/lib/urldata.h @@ -7,7 +7,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -358,17 +358,29 @@ struct ntlmdata { #endif }; -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE struct negotiatedata { /* when doing Negotiate we first need to receive an auth token and then we need to send our header */ enum { GSS_AUTHNONE, GSS_AUTHRECV, GSS_AUTHSENT } state; bool gss; /* Whether we're processing GSS-Negotiate or Negotiate */ const char* protocol; /* "GSS-Negotiate" or "Negotiate" */ +#ifdef HAVE_GSSAPI OM_uint32 status; gss_ctx_id_t context; gss_name_t server_name; gss_buffer_desc output_token; +#else +#ifdef USE_WINDOWS_SSPI + DWORD status; + CtxtHandle *context; + CredHandle *credentials; + char server_name[1024]; + size_t max_token_length; + BYTE *output_token; + size_t output_token_length; +#endif +#endif }; #endif @@ -1127,7 +1139,7 @@ struct UrlState { struct digestdata digest; /* state data for host Digest auth */ struct digestdata proxydigest; /* state data for proxy Digest auth */ -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE struct negotiatedata negotiate; /* state data for host Negotiate auth */ struct negotiatedata proxyneg; /* state data for proxy Negotiate auth */ #endif diff --git a/lib/version.c b/lib/version.c index aab5517d0..5931e7180 100644 --- a/lib/version.c +++ b/lib/version.c @@ -5,7 +5,7 @@ * | (__| |_| | _ <| |___ * \___|\___/|_| \_\_____| * - * Copyright (C) 1998 - 2010, Daniel Stenberg, , et al. + * Copyright (C) 1998 - 2011, Daniel Stenberg, , et al. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms @@ -243,7 +243,7 @@ static curl_version_info_data version_info = { #ifdef HAVE_LIBZ | CURL_VERSION_LIBZ #endif -#ifdef HAVE_GSSAPI +#ifdef USE_HTTP_NEGOTIATE | CURL_VERSION_GSSNEGOTIATE #endif #ifdef DEBUGBUILD