diff options
-rw-r--r-- | lib/Makefile.inc | 3 | ||||
-rw-r--r-- | lib/Makefile.vc6 | 1 | ||||
-rw-r--r-- | lib/http_negotiate.c | 157 | ||||
-rw-r--r-- | lib/vauth/spnego_gssapi.c | 257 | ||||
-rw-r--r-- | lib/vauth/spnego_sspi.c | 1 | ||||
-rw-r--r-- | lib/vauth/vauth.h | 6 | ||||
-rw-r--r-- | packages/Symbian/group/libcurl.mmp | 3 |
7 files changed, 292 insertions, 136 deletions
diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 67918f2d0..b9bb7149f 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -23,8 +23,7 @@ LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c \ vauth/digest.c vauth/digest_sspi.c vauth/krb5_gssapi.c \ vauth/krb5_sspi.c vauth/ntlm.c vauth/ntlm_sspi.c vauth/oauth2.c \ - vauth/spnego_sspi.c - + vauth/spnego_gssapi.c vauth/spnego_sspi.c LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h vauth/ntlm.h diff --git a/lib/Makefile.vc6 b/lib/Makefile.vc6 index b916a2176..b87350a7f 100644 --- a/lib/Makefile.vc6 +++ b/lib/Makefile.vc6 @@ -627,6 +627,7 @@ X_OBJS= \ $(DIROBJ)\ntlm.obj \
$(DIROBJ)\ntlm_sspi.obj \
$(DIROBJ)\oauth2.obj \
+ $(DIROBJ)\spnego_gssapi.obj \
$(DIROBJ)\spnego_sspi.obj \
$(DIROBJ)\vtls.obj \
$(DIROBJ)\openssl.obj \
diff --git a/lib/http_negotiate.c b/lib/http_negotiate.c index 3f52bbcf3..587ea2a1c 100644 --- a/lib/http_negotiate.c +++ b/lib/http_negotiate.c @@ -26,12 +26,9 @@ #include "urldata.h" #include "sendf.h" -#include "curl_gssapi.h" #include "rawstr.h" -#include "curl_base64.h" #include "http_negotiate.h" #include "vauth/vauth.h" -#include "url.h" #include "curl_printf.h" /* The last #include files should be: */ @@ -42,136 +39,51 @@ CURLcode Curl_input_negotiate(struct connectdata *conn, bool proxy, const char *header) { struct SessionHandle *data = conn->data; - struct negotiatedata *neg_ctx = proxy?&data->state.proxyneg: - &data->state.negotiate; - OM_uint32 major_status, minor_status, discard_st; - gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - size_t len; - size_t rawlen = 0; - CURLcode result; - - if(neg_ctx->context && neg_ctx->status == GSS_S_COMPLETE) { - /* We finished successfully 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(data); - return CURLE_LOGIN_DENIED; - } - - if(!neg_ctx->server_name) { - /* Generate our SPN */ - char *spn = Curl_auth_build_gssapi_spn( - proxy ? data->set.str[STRING_PROXY_SERVICE_NAME] : - data->set.str[STRING_SERVICE_NAME], - proxy ? conn->proxy.name : conn->host.name); - if(!spn) - return CURLE_OUT_OF_MEMORY; - /* Populate the SPN structure */ - spn_token.value = spn; - spn_token.length = strlen(spn); + /* Point to the service and host */ + const char *service; + const char *host; - /* Import the SPN */ - major_status = gss_import_name(&minor_status, &spn_token, - GSS_C_NT_HOSTBASED_SERVICE, - &neg_ctx->server_name); - if(GSS_ERROR(major_status)) { - Curl_gss_log_error(data, minor_status, "gss_import_name() failed: "); + /* Point to the correct struct with this */ + struct negotiatedata *neg_ctx; - free(spn); - - return CURLE_OUT_OF_MEMORY; - } - - free(spn); + if(proxy) { + service = data->set.str[STRING_PROXY_SERVICE_NAME]; + host = conn->host.name; + neg_ctx = &data->state.proxyneg; + } + else { + service = data->set.str[STRING_SERVICE_NAME]; + host = conn->proxy.name; + neg_ctx = &data->state.negotiate; } + /* Obtain the input token, if any */ header += strlen("Negotiate"); while(*header && ISSPACE(*header)) header++; - len = strlen(header); - if(len > 0) { - result = Curl_base64_decode(header, (unsigned char **)&input_token.value, - &rawlen); - if(result) - return result; - - if(!rawlen) { - infof(data, "Negotiate handshake failure (empty challenge message)\n"); - - return CURLE_BAD_CONTENT_ENCODING; - } - - input_token.length = rawlen; - - DEBUGASSERT(input_token.value != NULL); - } - - major_status = Curl_gss_init_sec_context(data, - &minor_status, - &neg_ctx->context, - neg_ctx->server_name, - &Curl_spnego_mech_oid, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, - &output_token, - TRUE, - NULL); - Curl_safefree(input_token.value); - - neg_ctx->status = major_status; - if(GSS_ERROR(major_status)) { - if(output_token.value) - gss_release_buffer(&discard_st, &output_token); - Curl_gss_log_error(conn->data, minor_status, - "gss_init_sec_context() failed: "); - return CURLE_OUT_OF_MEMORY; - } - - if(!output_token.value || !output_token.length) { - if(output_token.value) - gss_release_buffer(&discard_st, &output_token); - return CURLE_OUT_OF_MEMORY; - } - - neg_ctx->output_token = output_token; - - return CURLE_OK; + /* Initilise the security context and decode our challenge */ + return Curl_auth_decode_spnego_message(data, NULL, NULL, service, host, + header, neg_ctx); } 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; + char *base64 = NULL; size_t len = 0; char *userp; CURLcode result; - OM_uint32 discard_st; - - result = Curl_base64_encode(conn->data, - neg_ctx->output_token.value, - neg_ctx->output_token.length, - &encoded, &len); - if(result) { - gss_release_buffer(&discard_st, &neg_ctx->output_token); - neg_ctx->output_token.value = NULL; - neg_ctx->output_token.length = 0; - return result; - } - if(!encoded || !len) { - gss_release_buffer(&discard_st, &neg_ctx->output_token); - neg_ctx->output_token.value = NULL; - neg_ctx->output_token.length = 0; - return CURLE_REMOTE_ACCESS_DENIED; - } + result = Curl_auth_create_spnego_message(conn->data, neg_ctx, &base64, &len); + if(result) + return result; userp = aprintf("%sAuthorization: Negotiate %s\r\n", proxy ? "Proxy-" : "", - encoded); + base64); + if(proxy) { Curl_safefree(conn->allocptr.proxyuserpwd); conn->allocptr.proxyuserpwd = userp; @@ -181,30 +93,15 @@ CURLcode Curl_output_negotiate(struct connectdata *conn, bool proxy) conn->allocptr.userpwd = userp; } - free(encoded); + free(base64); return (userp == NULL) ? CURLE_OUT_OF_MEMORY : CURLE_OK; } -static void cleanup(struct negotiatedata *neg_ctx) -{ - OM_uint32 minor_status; - 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.value) - 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)); -} - void Curl_cleanup_negotiate(struct SessionHandle *data) { - cleanup(&data->state.negotiate); - cleanup(&data->state.proxyneg); + Curl_auth_spnego_cleanup(&data->state.negotiate); + Curl_auth_spnego_cleanup(&data->state.proxyneg); } #endif /* HAVE_GSSAPI && !CURL_DISABLE_HTTP && USE_SPNEGO */ diff --git a/lib/vauth/spnego_gssapi.c b/lib/vauth/spnego_gssapi.c new file mode 100644 index 000000000..9aa96dfdb --- /dev/null +++ b/lib/vauth/spnego_gssapi.c @@ -0,0 +1,257 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 1998 - 2016, 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. + * + * RFC4178 Simple and Protected GSS-API Negotiation Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(HAVE_GSSAPI) && defined(USE_SPNEGO) + +#include <curl/curl.h> + +#include "vauth/vauth.h" +#include "urldata.h" +#include "curl_base64.h" +#include "curl_gssapi.h" +#include "warnless.h" +#include "curl_multibyte.h" +#include "sendf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_auth_decode_spnego_message() + * + * This is used to decode an already encoded SPNEGO (Negotiate) challenge + * message. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The user name in the format User or Domain\User. + * passdwp [in] - The user's password. + * service [in] - The service type such as www, smtp, pop or imap. + * hostname [in] - The host name. + * chlg64 [in] - The optional base64 encoded challenge message. + * nego [in/out] - The Negotiate data struct being used and modified. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_decode_spnego_message(struct SessionHandle *data, + const char *user, + const char *password, + const char *service, + const char *host, + const char *chlg64, + struct negotiatedata *nego) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + unsigned char *chlg = NULL; + OM_uint32 major_status; + OM_uint32 minor_status; + OM_uint32 unused_status; + gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + + (void) user; + (void) password; + + if(nego->context && nego->status == GSS_S_COMPLETE) { + /* We finished successfully 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_auth_spnego_cleanup(nego); + return CURLE_LOGIN_DENIED; + } + + /* Generate our SPN */ + if(!nego->server_name) { + char *spn = Curl_auth_build_gssapi_spn(service, host); + if(!spn) + return CURLE_OUT_OF_MEMORY; + + /* Populate the SPN structure */ + spn_token.value = spn; + spn_token.length = strlen(spn); + + /* Import the SPN */ + major_status = gss_import_name(&minor_status, &spn_token, + GSS_C_NT_HOSTBASED_SERVICE, + &nego->server_name); + if(GSS_ERROR(major_status)) { + Curl_gss_log_error(data, minor_status, "gss_import_name() failed: "); + + free(spn); + + return CURLE_OUT_OF_MEMORY; + } + + free(spn); + } + + if(chlg64 && strlen(chlg64)) { + /* Decode the base-64 encoded challenge message */ + if(*chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "SPNEGO handshake failure (empty challenge message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Setup the challenge "input" security buffer */ + input_token.value = chlg; + input_token.length = chlglen; + } + + /* Generate our challenge-response message */ + major_status = Curl_gss_init_sec_context(data, + &minor_status, + &nego->context, + nego->server_name, + &Curl_spnego_mech_oid, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + &output_token, + TRUE, + NULL); + Curl_safefree(input_token.value); + + nego->status = major_status; + if(GSS_ERROR(major_status)) { + if(output_token.value) + gss_release_buffer(&unused_status, &output_token); + + Curl_gss_log_error(data, minor_status, + "gss_init_sec_context() failed: "); + + return CURLE_OUT_OF_MEMORY; + } + + if(!output_token.value || !output_token.length) { + if(output_token.value) + gss_release_buffer(&unused_status, &output_token); + + return CURLE_OUT_OF_MEMORY; + } + + nego->output_token = output_token; + + return CURLE_OK; +} + +/* + * Curl_auth_create_spnego_message() + * + * This is used to generate an already encoded SPNEGO (Negotiate) response + * message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * nego [in/out] - The Negotiate data struct being used and modified. + * outptr [in/out] - The address where a pointer to newly allocated memory + * holding the result will be stored upon completion. + * outlen [out] - The length of the output message. + * + * Returns CURLE_OK on success. + */ +CURLcode Curl_auth_create_spnego_message(struct SessionHandle *data, + struct negotiatedata *nego, + char **outptr, size_t *outlen) +{ + CURLcode result; + OM_uint32 minor_status; + + /* Base64 encode the already generated response */ + result = Curl_base64_encode(data, + nego->output_token.value, + nego->output_token.length, + outptr, outlen); + + if(result) { + gss_release_buffer(&minor_status, &nego->output_token); + nego->output_token.value = NULL; + nego->output_token.length = 0; + + return result; + } + + if(!*outptr || !*outlen) { + gss_release_buffer(&minor_status, &nego->output_token); + nego->output_token.value = NULL; + nego->output_token.length = 0; + + return CURLE_REMOTE_ACCESS_DENIED; + } + + return CURLE_OK; +} + +/* + * Curl_auth_spnego_cleanup() + * + * This is used to clean up the SPNEGO (Negotiate) specific data. + * + * Parameters: + * + * nego [in/out] - The Negotiate data struct being cleaned up. + * + */ +void Curl_auth_spnego_cleanup(struct negotiatedata* nego) +{ + OM_uint32 minor_status; + + /* Free our security context */ + if(nego->context != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor_status, &nego->context, GSS_C_NO_BUFFER); + nego->context = GSS_C_NO_CONTEXT; + } + + /* Free the output token */ + if(nego->output_token.value) { + gss_release_buffer(&minor_status, &nego->output_token); + nego->output_token.value = NULL; + nego->output_token.length = 0; + + } + + /* Free the SPN */ + if(nego->server_name != GSS_C_NO_NAME) { + gss_release_name(&minor_status, &nego->server_name); + nego->server_name = GSS_C_NO_NAME; + } + + /* Reset any variables */ + nego->status = 0; +} + +#endif /* HAVE_GSSAPI && USE_SPNEGO */ diff --git a/lib/vauth/spnego_sspi.c b/lib/vauth/spnego_sspi.c index 8bfc61522..b60994c2d 100644 --- a/lib/vauth/spnego_sspi.c +++ b/lib/vauth/spnego_sspi.c @@ -291,6 +291,7 @@ void Curl_auth_spnego_cleanup(struct negotiatedata* nego) Curl_safefree(nego->output_token); /* Reset any variables */ + nego->status = 0; nego->token_max = 0; } diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 8a0c67765..161cb14b7 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -38,7 +38,7 @@ struct ntlmdata; struct kerberos5data; #endif -#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO) +#if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO) struct negotiatedata; #endif @@ -165,7 +165,7 @@ CURLcode Curl_auth_create_gssapi_security_message(struct SessionHandle *data, void Curl_auth_gssapi_cleanup(struct kerberos5data *krb5); #endif /* USE_KERBEROS5 */ -#if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO) +#if (defined(HAVE_GSSAPI) || defined(USE_WINDOWS_SSPI)) && defined(USE_SPNEGO) /* This is used to decode a base64 encoded SPNEGO (Negotiate) challenge message */ CURLcode Curl_auth_decode_spnego_message(struct SessionHandle *data, @@ -185,6 +185,6 @@ CURLcode Curl_auth_create_spnego_message(struct SessionHandle *data, /* This is used to clean up the SPNEGO specifiec data */ void Curl_auth_spnego_cleanup(struct negotiatedata* nego); -#endif /* USE_WINDOWS_SSPI && USE_SPNEGO */ +#endif /* (HAVE_GSSAPI || USE_WINDOWS_SSPI) && USE_SPNEGO */ #endif /* HEADER_CURL_VAUTH_H */ diff --git a/packages/Symbian/group/libcurl.mmp b/packages/Symbian/group/libcurl.mmp index aae111161..ec8e8d355 100644 --- a/packages/Symbian/group/libcurl.mmp +++ b/packages/Symbian/group/libcurl.mmp @@ -42,7 +42,8 @@ SOURCE \ curl_sasl_sspi.c smb.c curl_endian.c curl_des.c \ vauth/vauth.c vauth/cleartext.c vauth/cram.c vauth/digest.c \ vauth/digest_sspi.c vauth/krb5_gssapi.c vauth/krb5_sspi.c \ - vauth/ntlm.c vauth/ntlm_sspi.c vauth/oauth2.c vauth/spnego_sspi.c + vauth/ntlm.c vauth/ntlm_sspi.c vauth/oauth2.c vauth/spnego_gssapi.c \ + vauth/spnego_sspi.c USERINCLUDE ../../../lib ../../../include/curl #ifdef ENABLE_SSL |