From 6d6f9ca1d978817af2f2780dafcceba281256e11 Mon Sep 17 00:00:00 2001 From: Steve Holme Date: Sun, 13 Mar 2016 18:51:46 +0000 Subject: vauth: Moved the Negotiate authentication code to the new vauth directory Part 2 of 2 - Moved the GSS-API based Negotiate authentication code. --- lib/vauth/spnego_gssapi.c | 257 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 257 insertions(+) create mode 100644 lib/vauth/spnego_gssapi.c (limited to 'lib/vauth/spnego_gssapi.c') 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, , 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 + +#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 */ -- cgit v1.2.3