From e1dca8a11706360ef0f9403492ea4c9b67692615 Mon Sep 17 00:00:00 2001 From: Steve Holme Date: Sat, 12 Sep 2015 14:15:09 +0100 Subject: vauth: Moved the Kerberos V5 authentication code to the new vauth directory --- lib/Makefile.inc | 6 +- lib/Makefile.vc6 | 3 +- lib/curl_sasl.h | 28 --- lib/curl_sasl_gssapi.c | 375 ------------------------------------- lib/curl_sasl_sspi.c | 445 -------------------------------------------- lib/vauth/krb5_gssapi.c | 375 +++++++++++++++++++++++++++++++++++++ lib/vauth/krb5_sspi.c | 485 ++++++++++++++++++++++++++++++++++++++++++++++++ lib/vauth/vauth.h | 28 +++ 8 files changed, 893 insertions(+), 852 deletions(-) delete mode 100644 lib/curl_sasl_gssapi.c create mode 100644 lib/vauth/krb5_gssapi.c create mode 100644 lib/vauth/krb5_sspi.c (limited to 'lib') diff --git a/lib/Makefile.inc b/lib/Makefile.inc index 365ec8a34..856d914fa 100644 --- a/lib/Makefile.inc +++ b/lib/Makefile.inc @@ -21,7 +21,8 @@ ########################################################################### LIB_VAUTH_CFILES = vauth/vauth.c vauth/cleartext.c vauth/cram.c \ - vauth/digest.c vauth/digest_sspi.c + vauth/digest.c vauth/digest_sspi.c vauth/krb5_gssapi.c \ + vauth/krb5_sspi.c LIB_VAUTH_HFILES = vauth/vauth.h vauth/digest.h @@ -52,8 +53,7 @@ LIB_CFILES = file.c timeval.c base64.c hostip.c progress.c formdata.c \ asyn-thread.c curl_gssapi.c curl_ntlm.c curl_ntlm_wb.c \ curl_ntlm_core.c curl_ntlm_msgs.c curl_sasl.c curl_multibyte.c \ hostcheck.c conncache.c pipeline.c dotdot.c x509asn1.c \ - http2.c curl_sasl_sspi.c smb.c curl_sasl_gssapi.c curl_endian.c \ - curl_des.c + http2.c curl_sasl_sspi.c smb.c curl_endian.c curl_des.c LIB_HFILES = arpa_telnet.h netrc.h file.h timeval.h hostip.h progress.h \ formdata.h cookie.h http.h sendf.h ftp.h url.h dict.h if2ip.h \ diff --git a/lib/Makefile.vc6 b/lib/Makefile.vc6 index 3e65386e8..75f108710 100644 --- a/lib/Makefile.vc6 +++ b/lib/Makefile.vc6 @@ -546,7 +546,6 @@ X_OBJS= \ $(DIROBJ)\curl_ntlm_wb.obj \ $(DIROBJ)\curl_rtmp.obj \ $(DIROBJ)\curl_sasl.obj \ - $(DIROBJ)\curl_sasl_gssapi.obj \ $(DIROBJ)\curl_sasl_sspi.obj \ $(DIROBJ)\curl_sspi.obj \ $(DIROBJ)\curl_threads.obj \ @@ -625,6 +624,8 @@ X_OBJS= \ $(DIROBJ)\cram.obj \ $(DIROBJ)\digest.obj \ $(DIROBJ)\digest_sspi.obj \ + $(DIROBJ)\krb5_gssapi.obj \ + $(DIROBJ)\krb5_sspi.obj \ $(DIROBJ)\vtls.obj \ $(DIROBJ)\openssl.obj \ $(DIROBJ)\strdup.obj \ diff --git a/lib/curl_sasl.h b/lib/curl_sasl.h index e176729f2..7fd658846 100644 --- a/lib/curl_sasl.h +++ b/lib/curl_sasl.h @@ -31,10 +31,6 @@ struct connectdata; struct ntlmdata; #endif -#if defined(USE_KERBEROS5) -struct kerberos5data; -#endif - /* Authentication mechanism flags */ #define SASL_MECH_LOGIN (1 << 0) #define SASL_MECH_PLAIN (1 << 1) @@ -147,30 +143,6 @@ void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm); #endif /* USE_NTLM */ -#if defined(USE_KERBEROS5) -/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token - message */ -CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - const char *service, - const bool mutual, - const char *chlg64, - struct kerberos5data *krb5, - char **outptr, size_t *outlen); - -/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security - token message */ -CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, - const char *input, - struct kerberos5data *krb5, - char **outptr, - size_t *outlen); - -/* This is used to clean up the gssapi specific data */ -void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); -#endif /* USE_KERBEROS5 */ - /* This is used to cleanup any libraries or curl modules used by the sasl functions */ void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused); diff --git a/lib/curl_sasl_gssapi.c b/lib/curl_sasl_gssapi.c deleted file mode 100644 index 62a95a6c9..000000000 --- a/lib/curl_sasl_gssapi.c +++ /dev/null @@ -1,375 +0,0 @@ -/*************************************************************************** - * _ _ ____ _ - * Project ___| | | | _ \| | - * / __| | | | |_) | | - * | (__| |_| | _ <| |___ - * \___|\___/|_| \_\_____| - * - * Copyright (C) 2014 - 2016, Steve Holme, . - * Copyright (C) 2015, 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 https://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. - * - * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism - * - ***************************************************************************/ - -#include "curl_setup.h" - -#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) - -#include - -#include "vauth/vauth.h" -#include "curl_sasl.h" -#include "urldata.h" -#include "curl_base64.h" -#include "curl_gssapi.h" -#include "sendf.h" -#include "curl_printf.h" - -/* The last #include files should be: */ -#include "curl_memory.h" -#include "memdebug.h" - -/* - * Curl_sasl_create_gssapi_user_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) user token - * message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * userp [in] - The user name. - * passdwp [in] - The user's password. - * service [in] - The service type such as www, smtp, pop or imap. - * mutual_auth [in] - Flag specifing whether or not mutual authentication - * is enabled. - * chlg64 [in] - Pointer to the optional base64 encoded challenge - * message. - * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_user_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - const char *service, - const bool mutual_auth, - const char *chlg64, - struct kerberos5data *krb5, - char **outptr, size_t *outlen) -{ - CURLcode result = CURLE_OK; - size_t chlglen = 0; - unsigned char *chlg = NULL; - OM_uint32 gss_status; - OM_uint32 gss_major_status; - OM_uint32 gss_minor_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) userp; - (void) passwdp; - - if(krb5->context == GSS_C_NO_CONTEXT) { - /* Generate our SPN */ - char *spn = Curl_sasl_build_gssapi_spn(service, - data->easy_conn->host.name); - if(!spn) - return CURLE_OUT_OF_MEMORY; - - /* Populate the SPN structure */ - spn_token.value = spn; - spn_token.length = strlen(spn); - - /* Import the SPN */ - gss_major_status = gss_import_name(&gss_minor_status, &spn_token, - GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn); - if(GSS_ERROR(gss_major_status)) { - Curl_gss_log_error(data, gss_minor_status, "gss_import_name() failed: "); - - free(spn); - - return CURLE_OUT_OF_MEMORY; - } - - free(spn); - } - else { - /* Decode the base-64 encoded challenge message */ - if(strlen(chlg64) && *chlg64 != '=') { - result = Curl_base64_decode(chlg64, &chlg, &chlglen); - if(result) - return result; - } - - /* Ensure we have a valid challenge message */ - if(!chlg) { - infof(data, "GSSAPI 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; - } - - gss_major_status = Curl_gss_init_sec_context(data, - &gss_minor_status, - &krb5->context, - krb5->spn, - &Curl_krb5_mech_oid, - GSS_C_NO_CHANNEL_BINDINGS, - &input_token, - &output_token, - mutual_auth, - NULL); - - free(input_token.value); - - if(GSS_ERROR(gss_major_status)) { - if(output_token.value) - gss_release_buffer(&gss_status, &output_token); - - Curl_gss_log_error(data, gss_minor_status, - "gss_init_sec_context() failed: "); - - return CURLE_RECV_ERROR; - } - - if(output_token.value && output_token.length) { - /* Base64 encode the response */ - result = Curl_base64_encode(data, (char *) output_token.value, - output_token.length, outptr, outlen); - - gss_release_buffer(&gss_status, &output_token); - } - - return result; -} - -/* - * Curl_sasl_create_gssapi_security_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) security - * token message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * chlg64 [in] - Pointer to the optional base64 encoded challenge message. - * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_security_message(struct SessionHandle *data, - const char *chlg64, - struct kerberos5data *krb5, - char **outptr, - size_t *outlen) -{ - CURLcode result = CURLE_OK; - size_t chlglen = 0; - size_t messagelen = 0; - unsigned char *chlg = NULL; - unsigned char *message = NULL; - OM_uint32 gss_status; - OM_uint32 gss_major_status; - OM_uint32 gss_minor_status; - gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; - gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; - unsigned int indata = 0; - unsigned int outdata = 0; - gss_qop_t qop = GSS_C_QOP_DEFAULT; - unsigned int sec_layer = 0; - unsigned int max_size = 0; - gss_name_t username = GSS_C_NO_NAME; - gss_buffer_desc username_token; - - /* Decode the base-64 encoded input message */ - if(strlen(chlg64) && *chlg64 != '=') { - result = Curl_base64_decode(chlg64, &chlg, &chlglen); - if(result) - return result; - } - - /* Ensure we have a valid challenge message */ - if(!chlg) { - infof(data, "GSSAPI handshake failure (empty security message)\n"); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Get the fully qualified username back from the context */ - gss_major_status = gss_inquire_context(&gss_minor_status, krb5->context, - &username, NULL, NULL, NULL, NULL, - NULL, NULL); - if(GSS_ERROR(gss_major_status)) { - Curl_gss_log_error(data, gss_minor_status, - "gss_inquire_context() failed: "); - - free(chlg); - - return CURLE_OUT_OF_MEMORY; - } - - /* Convert the username from internal format to a displayable token */ - gss_major_status = gss_display_name(&gss_minor_status, username, - &username_token, NULL); - if(GSS_ERROR(gss_major_status)) { - Curl_gss_log_error(data, gss_minor_status, "gss_display_name() failed: "); - - free(chlg); - - return CURLE_OUT_OF_MEMORY; - } - - /* Setup the challenge "input" security buffer */ - input_token.value = chlg; - input_token.length = chlglen; - - /* Decrypt the inbound challenge and obtain the qop */ - gss_major_status = gss_unwrap(&gss_minor_status, krb5->context, &input_token, - &output_token, NULL, &qop); - if(GSS_ERROR(gss_major_status)) { - Curl_gss_log_error(data, gss_minor_status, "gss_unwrap() failed: "); - - gss_release_buffer(&gss_status, &username_token); - free(chlg); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ - if(output_token.length != 4) { - infof(data, "GSSAPI handshake failure (invalid security data)\n"); - - gss_release_buffer(&gss_status, &username_token); - free(chlg); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Copy the data out and free the challenge as it is not required anymore */ - memcpy(&indata, output_token.value, 4); - gss_release_buffer(&gss_status, &output_token); - free(chlg); - - /* Extract the security layer */ - sec_layer = indata & 0x000000FF; - if(!(sec_layer & GSSAUTH_P_NONE)) { - infof(data, "GSSAPI handshake failure (invalid security layer)\n"); - - gss_release_buffer(&gss_status, &username_token); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Extract the maximum message size the server can receive */ - max_size = ntohl(indata & 0xFFFFFF00); - if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we don't require one unless we are encrypting data, we tell the server - our receive buffer is zero. */ - max_size = 0; - } - - /* Allocate our message */ - messagelen = sizeof(outdata) + username_token.length + 1; - message = malloc(messagelen); - if(!message) { - gss_release_buffer(&gss_status, &username_token); - - return CURLE_OUT_OF_MEMORY; - } - - /* Populate the message with the security layer, client supported receive - message size and authorization identity including the 0x00 based - terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization - identity is not terminated with the zero-valued (%x00) octet." it seems - necessary to include it. */ - outdata = htonl(max_size) | sec_layer; - memcpy(message, &outdata, sizeof(outdata)); - memcpy(message + sizeof(outdata), username_token.value, - username_token.length); - message[messagelen - 1] = '\0'; - - /* Free the username token as it is not required anymore */ - gss_release_buffer(&gss_status, &username_token); - - /* Setup the "authentication data" security buffer */ - input_token.value = message; - input_token.length = messagelen; - - /* Encrypt the data */ - gss_major_status = gss_wrap(&gss_minor_status, krb5->context, 0, - GSS_C_QOP_DEFAULT, &input_token, NULL, - &output_token); - if(GSS_ERROR(gss_major_status)) { - Curl_gss_log_error(data, gss_minor_status, "gss_wrap() failed: "); - - free(message); - - return CURLE_OUT_OF_MEMORY; - } - - /* Base64 encode the response */ - result = Curl_base64_encode(data, (char *) output_token.value, - output_token.length, outptr, outlen); - - /* Free the output buffer */ - gss_release_buffer(&gss_status, &output_token); - - /* Free the message buffer */ - free(message); - - return result; -} - -/* - * Curl_sasl_gssapi_cleanup() - * - * This is used to clean up the GSSAPI (Kerberos V5) specific data. - * - * Parameters: - * - * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. - * - */ -void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) -{ - OM_uint32 minor_status; - - /* Free our security context */ - if(krb5->context != GSS_C_NO_CONTEXT) { - gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER); - krb5->context = GSS_C_NO_CONTEXT; - } - - /* Free the SPN */ - if(krb5->spn != GSS_C_NO_NAME) { - gss_release_name(&minor_status, &krb5->spn); - krb5->spn = GSS_C_NO_NAME; - } -} - -#endif /* HAVE_GSSAPI && USE_KERBEROS5 */ diff --git a/lib/curl_sasl_sspi.c b/lib/curl_sasl_sspi.c index 5ecab65ec..d3dde2cfe 100644 --- a/lib/curl_sasl_sspi.c +++ b/lib/curl_sasl_sspi.c @@ -322,449 +322,4 @@ void Curl_sasl_ntlm_cleanup(struct ntlmdata *ntlm) } #endif /* USE_NTLM */ -#if defined(USE_KERBEROS5) -/* - * Curl_sasl_create_gssapi_user_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) user token - * message ready for sending to the recipient. - * - * 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. - * mutual_auth [in] - Flag specifing whether or not mutual authentication - * is enabled. - * chlg64 [in] - The optional base64 encoded challenge message. - * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_user_message(struct SessionHandle *data, - const char *userp, - const char *passwdp, - const char *service, - const bool mutual_auth, - const char *chlg64, - struct kerberos5data *krb5, - char **outptr, size_t *outlen) -{ - CURLcode result = CURLE_OK; - size_t chlglen = 0; - unsigned char *chlg = NULL; - CtxtHandle context; - PSecPkgInfo SecurityPackage; - SecBuffer chlg_buf; - SecBuffer resp_buf; - SecBufferDesc chlg_desc; - SecBufferDesc resp_desc; - SECURITY_STATUS status; - unsigned long attrs; - TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ - - if(!krb5->credentials) { - /* Query the security package for Kerberos */ - status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) - TEXT(SP_NAME_KERBEROS), - &SecurityPackage); - if(status != SEC_E_OK) { - return CURLE_NOT_BUILT_IN; - } - - krb5->token_max = SecurityPackage->cbMaxToken; - - /* Release the package buffer as it is not required anymore */ - s_pSecFn->FreeContextBuffer(SecurityPackage); - - /* Allocate our response buffer */ - krb5->output_token = malloc(krb5->token_max); - if(!krb5->output_token) - return CURLE_OUT_OF_MEMORY; - - /* Generate our SPN */ - krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name); - if(!krb5->spn) - return CURLE_OUT_OF_MEMORY; - - if(userp && *userp) { - /* Populate our identity structure */ - result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); - if(result) - return result; - - /* Allow proper cleanup of the identity structure */ - krb5->p_identity = &krb5->identity; - } - else - /* Use the current Windows user */ - krb5->p_identity = NULL; - - /* Allocate our credentials handle */ - krb5->credentials = malloc(sizeof(CredHandle)); - if(!krb5->credentials) - return CURLE_OUT_OF_MEMORY; - - memset(krb5->credentials, 0, sizeof(CredHandle)); - - /* Acquire our credentials handle */ - status = s_pSecFn->AcquireCredentialsHandle(NULL, - (TCHAR *) - TEXT(SP_NAME_KERBEROS), - SECPKG_CRED_OUTBOUND, NULL, - krb5->p_identity, NULL, NULL, - krb5->credentials, &expiry); - if(status != SEC_E_OK) - return CURLE_LOGIN_DENIED; - - /* Allocate our new context handle */ - krb5->context = malloc(sizeof(CtxtHandle)); - if(!krb5->context) - return CURLE_OUT_OF_MEMORY; - - memset(krb5->context, 0, sizeof(CtxtHandle)); - } - else { - /* Decode the base-64 encoded challenge message */ - if(strlen(chlg64) && *chlg64 != '=') { - result = Curl_base64_decode(chlg64, &chlg, &chlglen); - if(result) - return result; - } - - /* Ensure we have a valid challenge message */ - if(!chlg) { - infof(data, "GSSAPI handshake failure (empty challenge message)\n"); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Setup the challenge "input" security buffer */ - chlg_desc.ulVersion = SECBUFFER_VERSION; - chlg_desc.cBuffers = 1; - chlg_desc.pBuffers = &chlg_buf; - chlg_buf.BufferType = SECBUFFER_TOKEN; - chlg_buf.pvBuffer = chlg; - chlg_buf.cbBuffer = curlx_uztoul(chlglen); - } - - /* Setup the response "output" security buffer */ - resp_desc.ulVersion = SECBUFFER_VERSION; - resp_desc.cBuffers = 1; - resp_desc.pBuffers = &resp_buf; - resp_buf.BufferType = SECBUFFER_TOKEN; - resp_buf.pvBuffer = krb5->output_token; - resp_buf.cbBuffer = curlx_uztoul(krb5->token_max); - - /* Generate our challenge-response message */ - status = s_pSecFn->InitializeSecurityContext(krb5->credentials, - chlg ? krb5->context : NULL, - krb5->spn, - (mutual_auth ? - ISC_REQ_MUTUAL_AUTH : 0), - 0, SECURITY_NATIVE_DREP, - chlg ? &chlg_desc : NULL, 0, - &context, - &resp_desc, &attrs, - &expiry); - - if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { - free(chlg); - - return CURLE_RECV_ERROR; - } - - if(memcmp(&context, krb5->context, sizeof(context))) { - s_pSecFn->DeleteSecurityContext(krb5->context); - - memcpy(krb5->context, &context, sizeof(context)); - } - - if(resp_buf.cbBuffer) { - /* Base64 encode the response */ - result = Curl_base64_encode(data, (char *)resp_buf.pvBuffer, - resp_buf.cbBuffer, outptr, outlen); - } - - /* Free the decoded challenge */ - free(chlg); - - return result; -} - -/* - * Curl_sasl_create_gssapi_security_message() - * - * This is used to generate an already encoded GSSAPI (Kerberos V5) security - * token message ready for sending to the recipient. - * - * Parameters: - * - * data [in] - The session handle. - * chlg64 [in] - The optional base64 encoded challenge message. - * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_security_message(struct SessionHandle *data, - const char *chlg64, - struct kerberos5data *krb5, - char **outptr, - size_t *outlen) -{ - CURLcode result = CURLE_OK; - size_t offset = 0; - size_t chlglen = 0; - size_t messagelen = 0; - size_t appdatalen = 0; - unsigned char *chlg = NULL; - unsigned char *trailer = NULL; - unsigned char *message = NULL; - unsigned char *padding = NULL; - unsigned char *appdata = NULL; - SecBuffer input_buf[2]; - SecBuffer wrap_buf[3]; - SecBufferDesc input_desc; - SecBufferDesc wrap_desc; - unsigned long indata = 0; - unsigned long outdata = 0; - unsigned long qop = 0; - unsigned long sec_layer = 0; - unsigned long max_size = 0; - SecPkgContext_Sizes sizes; - SecPkgCredentials_Names names; - SECURITY_STATUS status; - char *user_name; - - /* Decode the base-64 encoded input message */ - if(strlen(chlg64) && *chlg64 != '=') { - result = Curl_base64_decode(chlg64, &chlg, &chlglen); - if(result) - return result; - } - - /* Ensure we have a valid challenge message */ - if(!chlg) { - infof(data, "GSSAPI handshake failure (empty security message)\n"); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Get our response size information */ - status = s_pSecFn->QueryContextAttributes(krb5->context, - SECPKG_ATTR_SIZES, - &sizes); - if(status != SEC_E_OK) { - free(chlg); - - return CURLE_OUT_OF_MEMORY; - } - - /* Get the fully qualified username back from the context */ - status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials, - SECPKG_CRED_ATTR_NAMES, - &names); - if(status != SEC_E_OK) { - free(chlg); - - return CURLE_RECV_ERROR; - } - - /* Setup the "input" security buffer */ - input_desc.ulVersion = SECBUFFER_VERSION; - input_desc.cBuffers = 2; - input_desc.pBuffers = input_buf; - input_buf[0].BufferType = SECBUFFER_STREAM; - input_buf[0].pvBuffer = chlg; - input_buf[0].cbBuffer = curlx_uztoul(chlglen); - input_buf[1].BufferType = SECBUFFER_DATA; - input_buf[1].pvBuffer = NULL; - input_buf[1].cbBuffer = 0; - - /* Decrypt the inbound challenge and obtain the qop */ - status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); - if(status != SEC_E_OK) { - infof(data, "GSSAPI handshake failure (empty security message)\n"); - - free(chlg); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ - if(input_buf[1].cbBuffer != 4) { - infof(data, "GSSAPI handshake failure (invalid security data)\n"); - - free(chlg); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Copy the data out and free the challenge as it is not required anymore */ - memcpy(&indata, input_buf[1].pvBuffer, 4); - s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); - free(chlg); - - /* Extract the security layer */ - sec_layer = indata & 0x000000FF; - if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { - infof(data, "GSSAPI handshake failure (invalid security layer)\n"); - - return CURLE_BAD_CONTENT_ENCODING; - } - - /* Extract the maximum message size the server can receive */ - max_size = ntohl(indata & 0xFFFFFF00); - if(max_size > 0) { - /* The server has told us it supports a maximum receive buffer, however, as - we don't require one unless we are encrypting data, we tell the server - our receive buffer is zero. */ - max_size = 0; - } - - /* Allocate the trailer */ - trailer = malloc(sizes.cbSecurityTrailer); - if(!trailer) - return CURLE_OUT_OF_MEMORY; - - /* Convert the user name to UTF8 when operating with Unicode */ - user_name = Curl_convert_tchar_to_UTF8(names.sUserName); - if(!user_name) { - free(trailer); - - return CURLE_OUT_OF_MEMORY; - } - - /* Allocate our message */ - messagelen = sizeof(outdata) + strlen(user_name) + 1; - message = malloc(messagelen); - if(!message) { - free(trailer); - Curl_unicodefree(user_name); - - return CURLE_OUT_OF_MEMORY; - } - - /* Populate the message with the security layer, client supported receive - message size and authorization identity including the 0x00 based - terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization - identity is not terminated with the zero-valued (%x00) octet." it seems - necessary to include it. */ - outdata = htonl(max_size) | sec_layer; - memcpy(message, &outdata, sizeof(outdata)); - strcpy((char *) message + sizeof(outdata), user_name); - Curl_unicodefree(user_name); - - /* Allocate the padding */ - padding = malloc(sizes.cbBlockSize); - if(!padding) { - free(message); - free(trailer); - - return CURLE_OUT_OF_MEMORY; - } - - /* Setup the "authentication data" security buffer */ - wrap_desc.ulVersion = SECBUFFER_VERSION; - wrap_desc.cBuffers = 3; - wrap_desc.pBuffers = wrap_buf; - wrap_buf[0].BufferType = SECBUFFER_TOKEN; - wrap_buf[0].pvBuffer = trailer; - wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer; - wrap_buf[1].BufferType = SECBUFFER_DATA; - wrap_buf[1].pvBuffer = message; - wrap_buf[1].cbBuffer = curlx_uztoul(messagelen); - wrap_buf[2].BufferType = SECBUFFER_PADDING; - wrap_buf[2].pvBuffer = padding; - wrap_buf[2].cbBuffer = sizes.cbBlockSize; - - /* Encrypt the data */ - status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, - &wrap_desc, 0); - if(status != SEC_E_OK) { - free(padding); - free(message); - free(trailer); - - return CURLE_OUT_OF_MEMORY; - } - - /* Allocate the encryption (wrap) buffer */ - appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + - wrap_buf[2].cbBuffer; - appdata = malloc(appdatalen); - if(!appdata) { - free(padding); - free(message); - free(trailer); - - return CURLE_OUT_OF_MEMORY; - } - - /* Populate the encryption buffer */ - memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer); - offset += wrap_buf[0].cbBuffer; - memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer); - offset += wrap_buf[1].cbBuffer; - memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); - - /* Base64 encode the response */ - result = Curl_base64_encode(data, (char *)appdata, appdatalen, outptr, - outlen); - - /* Free all of our local buffers */ - free(appdata); - free(padding); - free(message); - free(trailer); - - return result; -} - -/* - * Curl_sasl_gssapi_cleanup() - * - * This is used to clean up the GSSAPI (Kerberos V5) specific data. - * - * Parameters: - * - * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. - * - */ -void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) -{ - /* Free our security context */ - if(krb5->context) { - s_pSecFn->DeleteSecurityContext(krb5->context); - free(krb5->context); - krb5->context = NULL; - } - - /* Free our credentials handle */ - if(krb5->credentials) { - s_pSecFn->FreeCredentialsHandle(krb5->credentials); - free(krb5->credentials); - krb5->credentials = NULL; - } - - /* Free our identity */ - Curl_sspi_free_identity(krb5->p_identity); - krb5->p_identity = NULL; - - /* Free the SPN and output token */ - Curl_safefree(krb5->spn); - Curl_safefree(krb5->output_token); - - /* Reset any variables */ - krb5->token_max = 0; -} -#endif /* USE_KERBEROS5 */ - #endif /* USE_WINDOWS_SSPI */ diff --git a/lib/vauth/krb5_gssapi.c b/lib/vauth/krb5_gssapi.c new file mode 100644 index 000000000..62a95a6c9 --- /dev/null +++ b/lib/vauth/krb5_gssapi.c @@ -0,0 +1,375 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2014 - 2016, Steve Holme, . + * Copyright (C) 2015, 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 https://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. + * + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5) + +#include + +#include "vauth/vauth.h" +#include "curl_sasl.h" +#include "urldata.h" +#include "curl_base64.h" +#include "curl_gssapi.h" +#include "sendf.h" +#include "curl_printf.h" + +/* The last #include files should be: */ +#include "curl_memory.h" +#include "memdebug.h" + +/* + * Curl_sasl_create_gssapi_user_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) user token + * message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * userp [in] - The user name. + * passdwp [in] - The user's password. + * service [in] - The service type such as www, smtp, pop or imap. + * mutual_auth [in] - Flag specifing whether or not mutual authentication + * is enabled. + * chlg64 [in] - Pointer to the optional base64 encoded challenge + * message. + * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_user_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + const char *service, + const bool mutual_auth, + const char *chlg64, + struct kerberos5data *krb5, + char **outptr, size_t *outlen) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + unsigned char *chlg = NULL; + OM_uint32 gss_status; + OM_uint32 gss_major_status; + OM_uint32 gss_minor_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) userp; + (void) passwdp; + + if(krb5->context == GSS_C_NO_CONTEXT) { + /* Generate our SPN */ + char *spn = Curl_sasl_build_gssapi_spn(service, + data->easy_conn->host.name); + if(!spn) + return CURLE_OUT_OF_MEMORY; + + /* Populate the SPN structure */ + spn_token.value = spn; + spn_token.length = strlen(spn); + + /* Import the SPN */ + gss_major_status = gss_import_name(&gss_minor_status, &spn_token, + GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_import_name() failed: "); + + free(spn); + + return CURLE_OUT_OF_MEMORY; + } + + free(spn); + } + else { + /* Decode the base-64 encoded challenge message */ + if(strlen(chlg64) && *chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "GSSAPI 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; + } + + gss_major_status = Curl_gss_init_sec_context(data, + &gss_minor_status, + &krb5->context, + krb5->spn, + &Curl_krb5_mech_oid, + GSS_C_NO_CHANNEL_BINDINGS, + &input_token, + &output_token, + mutual_auth, + NULL); + + free(input_token.value); + + if(GSS_ERROR(gss_major_status)) { + if(output_token.value) + gss_release_buffer(&gss_status, &output_token); + + Curl_gss_log_error(data, gss_minor_status, + "gss_init_sec_context() failed: "); + + return CURLE_RECV_ERROR; + } + + if(output_token.value && output_token.length) { + /* Base64 encode the response */ + result = Curl_base64_encode(data, (char *) output_token.value, + output_token.length, outptr, outlen); + + gss_release_buffer(&gss_status, &output_token); + } + + return result; +} + +/* + * Curl_sasl_create_gssapi_security_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) security + * token message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * chlg64 [in] - Pointer to the optional base64 encoded challenge message. + * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_security_message(struct SessionHandle *data, + const char *chlg64, + struct kerberos5data *krb5, + char **outptr, + size_t *outlen) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + size_t messagelen = 0; + unsigned char *chlg = NULL; + unsigned char *message = NULL; + OM_uint32 gss_status; + OM_uint32 gss_major_status; + OM_uint32 gss_minor_status; + gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER; + gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER; + unsigned int indata = 0; + unsigned int outdata = 0; + gss_qop_t qop = GSS_C_QOP_DEFAULT; + unsigned int sec_layer = 0; + unsigned int max_size = 0; + gss_name_t username = GSS_C_NO_NAME; + gss_buffer_desc username_token; + + /* Decode the base-64 encoded input message */ + if(strlen(chlg64) && *chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "GSSAPI handshake failure (empty security message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Get the fully qualified username back from the context */ + gss_major_status = gss_inquire_context(&gss_minor_status, krb5->context, + &username, NULL, NULL, NULL, NULL, + NULL, NULL); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, + "gss_inquire_context() failed: "); + + free(chlg); + + return CURLE_OUT_OF_MEMORY; + } + + /* Convert the username from internal format to a displayable token */ + gss_major_status = gss_display_name(&gss_minor_status, username, + &username_token, NULL); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_display_name() failed: "); + + free(chlg); + + return CURLE_OUT_OF_MEMORY; + } + + /* Setup the challenge "input" security buffer */ + input_token.value = chlg; + input_token.length = chlglen; + + /* Decrypt the inbound challenge and obtain the qop */ + gss_major_status = gss_unwrap(&gss_minor_status, krb5->context, &input_token, + &output_token, NULL, &qop); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_unwrap() failed: "); + + gss_release_buffer(&gss_status, &username_token); + free(chlg); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ + if(output_token.length != 4) { + infof(data, "GSSAPI handshake failure (invalid security data)\n"); + + gss_release_buffer(&gss_status, &username_token); + free(chlg); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Copy the data out and free the challenge as it is not required anymore */ + memcpy(&indata, output_token.value, 4); + gss_release_buffer(&gss_status, &output_token); + free(chlg); + + /* Extract the security layer */ + sec_layer = indata & 0x000000FF; + if(!(sec_layer & GSSAUTH_P_NONE)) { + infof(data, "GSSAPI handshake failure (invalid security layer)\n"); + + gss_release_buffer(&gss_status, &username_token); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Extract the maximum message size the server can receive */ + max_size = ntohl(indata & 0xFFFFFF00); + if(max_size > 0) { + /* The server has told us it supports a maximum receive buffer, however, as + we don't require one unless we are encrypting data, we tell the server + our receive buffer is zero. */ + max_size = 0; + } + + /* Allocate our message */ + messagelen = sizeof(outdata) + username_token.length + 1; + message = malloc(messagelen); + if(!message) { + gss_release_buffer(&gss_status, &username_token); + + return CURLE_OUT_OF_MEMORY; + } + + /* Populate the message with the security layer, client supported receive + message size and authorization identity including the 0x00 based + terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization + identity is not terminated with the zero-valued (%x00) octet." it seems + necessary to include it. */ + outdata = htonl(max_size) | sec_layer; + memcpy(message, &outdata, sizeof(outdata)); + memcpy(message + sizeof(outdata), username_token.value, + username_token.length); + message[messagelen - 1] = '\0'; + + /* Free the username token as it is not required anymore */ + gss_release_buffer(&gss_status, &username_token); + + /* Setup the "authentication data" security buffer */ + input_token.value = message; + input_token.length = messagelen; + + /* Encrypt the data */ + gss_major_status = gss_wrap(&gss_minor_status, krb5->context, 0, + GSS_C_QOP_DEFAULT, &input_token, NULL, + &output_token); + if(GSS_ERROR(gss_major_status)) { + Curl_gss_log_error(data, gss_minor_status, "gss_wrap() failed: "); + + free(message); + + return CURLE_OUT_OF_MEMORY; + } + + /* Base64 encode the response */ + result = Curl_base64_encode(data, (char *) output_token.value, + output_token.length, outptr, outlen); + + /* Free the output buffer */ + gss_release_buffer(&gss_status, &output_token); + + /* Free the message buffer */ + free(message); + + return result; +} + +/* + * Curl_sasl_gssapi_cleanup() + * + * This is used to clean up the GSSAPI (Kerberos V5) specific data. + * + * Parameters: + * + * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. + * + */ +void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) +{ + OM_uint32 minor_status; + + /* Free our security context */ + if(krb5->context != GSS_C_NO_CONTEXT) { + gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER); + krb5->context = GSS_C_NO_CONTEXT; + } + + /* Free the SPN */ + if(krb5->spn != GSS_C_NO_NAME) { + gss_release_name(&minor_status, &krb5->spn); + krb5->spn = GSS_C_NO_NAME; + } +} + +#endif /* HAVE_GSSAPI && USE_KERBEROS5 */ diff --git a/lib/vauth/krb5_sspi.c b/lib/vauth/krb5_sspi.c new file mode 100644 index 000000000..284fd98dd --- /dev/null +++ b/lib/vauth/krb5_sspi.c @@ -0,0 +1,485 @@ +/*************************************************************************** + * _ _ ____ _ + * Project ___| | | | _ \| | + * / __| | | | |_) | | + * | (__| |_| | _ <| |___ + * \___|\___/|_| \_\_____| + * + * Copyright (C) 2014 - 2016, Steve Holme, . + * + * 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. + * + * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism + * + ***************************************************************************/ + +#include "curl_setup.h" + +#if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5) + +#include + +#include "vauth/vauth.h" +#include "urldata.h" +#include "curl_base64.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_sasl_create_gssapi_user_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) user token + * message ready for sending to the recipient. + * + * 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. + * mutual_auth [in] - Flag specifing whether or not mutual authentication + * is enabled. + * chlg64 [in] - The optional base64 encoded challenge message. + * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_user_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + const char *service, + const bool mutual_auth, + const char *chlg64, + struct kerberos5data *krb5, + char **outptr, size_t *outlen) +{ + CURLcode result = CURLE_OK; + size_t chlglen = 0; + unsigned char *chlg = NULL; + CtxtHandle context; + PSecPkgInfo SecurityPackage; + SecBuffer chlg_buf; + SecBuffer resp_buf; + SecBufferDesc chlg_desc; + SecBufferDesc resp_desc; + SECURITY_STATUS status; + unsigned long attrs; + TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ + + if(!krb5->credentials) { + /* Query the security package for Kerberos */ + status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) + TEXT(SP_NAME_KERBEROS), + &SecurityPackage); + if(status != SEC_E_OK) { + return CURLE_NOT_BUILT_IN; + } + + krb5->token_max = SecurityPackage->cbMaxToken; + + /* Release the package buffer as it is not required anymore */ + s_pSecFn->FreeContextBuffer(SecurityPackage); + + /* Allocate our response buffer */ + krb5->output_token = malloc(krb5->token_max); + if(!krb5->output_token) + return CURLE_OUT_OF_MEMORY; + + /* Generate our SPN */ + krb5->spn = Curl_sasl_build_spn(service, data->easy_conn->host.name); + if(!krb5->spn) + return CURLE_OUT_OF_MEMORY; + + if(userp && *userp) { + /* Populate our identity structure */ + result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); + if(result) + return result; + + /* Allow proper cleanup of the identity structure */ + krb5->p_identity = &krb5->identity; + } + else + /* Use the current Windows user */ + krb5->p_identity = NULL; + + /* Allocate our credentials handle */ + krb5->credentials = malloc(sizeof(CredHandle)); + if(!krb5->credentials) + return CURLE_OUT_OF_MEMORY; + + memset(krb5->credentials, 0, sizeof(CredHandle)); + + /* Acquire our credentials handle */ + status = s_pSecFn->AcquireCredentialsHandle(NULL, + (TCHAR *) + TEXT(SP_NAME_KERBEROS), + SECPKG_CRED_OUTBOUND, NULL, + krb5->p_identity, NULL, NULL, + krb5->credentials, &expiry); + if(status != SEC_E_OK) + return CURLE_LOGIN_DENIED; + + /* Allocate our new context handle */ + krb5->context = malloc(sizeof(CtxtHandle)); + if(!krb5->context) + return CURLE_OUT_OF_MEMORY; + + memset(krb5->context, 0, sizeof(CtxtHandle)); + } + else { + /* Decode the base-64 encoded challenge message */ + if(strlen(chlg64) && *chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "GSSAPI handshake failure (empty challenge message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Setup the challenge "input" security buffer */ + chlg_desc.ulVersion = SECBUFFER_VERSION; + chlg_desc.cBuffers = 1; + chlg_desc.pBuffers = &chlg_buf; + chlg_buf.BufferType = SECBUFFER_TOKEN; + chlg_buf.pvBuffer = chlg; + chlg_buf.cbBuffer = curlx_uztoul(chlglen); + } + + /* Setup the response "output" security buffer */ + resp_desc.ulVersion = SECBUFFER_VERSION; + resp_desc.cBuffers = 1; + resp_desc.pBuffers = &resp_buf; + resp_buf.BufferType = SECBUFFER_TOKEN; + resp_buf.pvBuffer = krb5->output_token; + resp_buf.cbBuffer = curlx_uztoul(krb5->token_max); + + /* Generate our challenge-response message */ + status = s_pSecFn->InitializeSecurityContext(krb5->credentials, + chlg ? krb5->context : NULL, + krb5->spn, + (mutual_auth ? + ISC_REQ_MUTUAL_AUTH : 0), + 0, SECURITY_NATIVE_DREP, + chlg ? &chlg_desc : NULL, 0, + &context, + &resp_desc, &attrs, + &expiry); + + if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { + free(chlg); + + return CURLE_RECV_ERROR; + } + + if(memcmp(&context, krb5->context, sizeof(context))) { + s_pSecFn->DeleteSecurityContext(krb5->context); + + memcpy(krb5->context, &context, sizeof(context)); + } + + if(resp_buf.cbBuffer) { + /* Base64 encode the response */ + result = Curl_base64_encode(data, (char *) resp_buf.pvBuffer, + resp_buf.cbBuffer, outptr, outlen); + } + + /* Free the decoded challenge */ + free(chlg); + + return result; +} + +/* + * Curl_sasl_create_gssapi_security_message() + * + * This is used to generate an already encoded GSSAPI (Kerberos V5) security + * token message ready for sending to the recipient. + * + * Parameters: + * + * data [in] - The session handle. + * chlg64 [in] - The optional base64 encoded challenge message. + * krb5 [in/out] - The Kerberos 5 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_sasl_create_gssapi_security_message(struct SessionHandle *data, + const char *chlg64, + struct kerberos5data *krb5, + char **outptr, + size_t *outlen) +{ + CURLcode result = CURLE_OK; + size_t offset = 0; + size_t chlglen = 0; + size_t messagelen = 0; + size_t appdatalen = 0; + unsigned char *chlg = NULL; + unsigned char *trailer = NULL; + unsigned char *message = NULL; + unsigned char *padding = NULL; + unsigned char *appdata = NULL; + SecBuffer input_buf[2]; + SecBuffer wrap_buf[3]; + SecBufferDesc input_desc; + SecBufferDesc wrap_desc; + unsigned long indata = 0; + unsigned long outdata = 0; + unsigned long qop = 0; + unsigned long sec_layer = 0; + unsigned long max_size = 0; + SecPkgContext_Sizes sizes; + SecPkgCredentials_Names names; + SECURITY_STATUS status; + char *user_name; + + /* Decode the base-64 encoded input message */ + if(strlen(chlg64) && *chlg64 != '=') { + result = Curl_base64_decode(chlg64, &chlg, &chlglen); + if(result) + return result; + } + + /* Ensure we have a valid challenge message */ + if(!chlg) { + infof(data, "GSSAPI handshake failure (empty security message)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Get our response size information */ + status = s_pSecFn->QueryContextAttributes(krb5->context, + SECPKG_ATTR_SIZES, + &sizes); + if(status != SEC_E_OK) { + free(chlg); + + return CURLE_OUT_OF_MEMORY; + } + + /* Get the fully qualified username back from the context */ + status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials, + SECPKG_CRED_ATTR_NAMES, + &names); + if(status != SEC_E_OK) { + free(chlg); + + return CURLE_RECV_ERROR; + } + + /* Setup the "input" security buffer */ + input_desc.ulVersion = SECBUFFER_VERSION; + input_desc.cBuffers = 2; + input_desc.pBuffers = input_buf; + input_buf[0].BufferType = SECBUFFER_STREAM; + input_buf[0].pvBuffer = chlg; + input_buf[0].cbBuffer = curlx_uztoul(chlglen); + input_buf[1].BufferType = SECBUFFER_DATA; + input_buf[1].pvBuffer = NULL; + input_buf[1].cbBuffer = 0; + + /* Decrypt the inbound challenge and obtain the qop */ + status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); + if(status != SEC_E_OK) { + infof(data, "GSSAPI handshake failure (empty security message)\n"); + + free(chlg); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ + if(input_buf[1].cbBuffer != 4) { + infof(data, "GSSAPI handshake failure (invalid security data)\n"); + + free(chlg); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Copy the data out and free the challenge as it is not required anymore */ + memcpy(&indata, input_buf[1].pvBuffer, 4); + s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); + free(chlg); + + /* Extract the security layer */ + sec_layer = indata & 0x000000FF; + if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { + infof(data, "GSSAPI handshake failure (invalid security layer)\n"); + + return CURLE_BAD_CONTENT_ENCODING; + } + + /* Extract the maximum message size the server can receive */ + max_size = ntohl(indata & 0xFFFFFF00); + if(max_size > 0) { + /* The server has told us it supports a maximum receive buffer, however, as + we don't require one unless we are encrypting data, we tell the server + our receive buffer is zero. */ + max_size = 0; + } + + /* Allocate the trailer */ + trailer = malloc(sizes.cbSecurityTrailer); + if(!trailer) + return CURLE_OUT_OF_MEMORY; + + /* Convert the user name to UTF8 when operating with Unicode */ + user_name = Curl_convert_tchar_to_UTF8(names.sUserName); + if(!user_name) { + free(trailer); + + return CURLE_OUT_OF_MEMORY; + } + + /* Allocate our message */ + messagelen = sizeof(outdata) + strlen(user_name) + 1; + message = malloc(messagelen); + if(!message) { + free(trailer); + Curl_unicodefree(user_name); + + return CURLE_OUT_OF_MEMORY; + } + + /* Populate the message with the security layer, client supported receive + message size and authorization identity including the 0x00 based + terminator. Note: Dispite RFC4752 Section 3.1 stating "The authorization + identity is not terminated with the zero-valued (%x00) octet." it seems + necessary to include it. */ + outdata = htonl(max_size) | sec_layer; + memcpy(message, &outdata, sizeof(outdata)); + strcpy((char *) message + sizeof(outdata), user_name); + Curl_unicodefree(user_name); + + /* Allocate the padding */ + padding = malloc(sizes.cbBlockSize); + if(!padding) { + free(message); + free(trailer); + + return CURLE_OUT_OF_MEMORY; + } + + /* Setup the "authentication data" security buffer */ + wrap_desc.ulVersion = SECBUFFER_VERSION; + wrap_desc.cBuffers = 3; + wrap_desc.pBuffers = wrap_buf; + wrap_buf[0].BufferType = SECBUFFER_TOKEN; + wrap_buf[0].pvBuffer = trailer; + wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer; + wrap_buf[1].BufferType = SECBUFFER_DATA; + wrap_buf[1].pvBuffer = message; + wrap_buf[1].cbBuffer = curlx_uztoul(messagelen); + wrap_buf[2].BufferType = SECBUFFER_PADDING; + wrap_buf[2].pvBuffer = padding; + wrap_buf[2].cbBuffer = sizes.cbBlockSize; + + /* Encrypt the data */ + status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, + &wrap_desc, 0); + if(status != SEC_E_OK) { + free(padding); + free(message); + free(trailer); + + return CURLE_OUT_OF_MEMORY; + } + + /* Allocate the encryption (wrap) buffer */ + appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + + wrap_buf[2].cbBuffer; + appdata = malloc(appdatalen); + if(!appdata) { + free(padding); + free(message); + free(trailer); + + return CURLE_OUT_OF_MEMORY; + } + + /* Populate the encryption buffer */ + memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer); + offset += wrap_buf[0].cbBuffer; + memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer); + offset += wrap_buf[1].cbBuffer; + memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); + + /* Base64 encode the response */ + result = Curl_base64_encode(data, (char *) appdata, appdatalen, outptr, + outlen); + + /* Free all of our local buffers */ + free(appdata); + free(padding); + free(message); + free(trailer); + + return result; +} + +/* + * Curl_sasl_gssapi_cleanup() + * + * This is used to clean up the GSSAPI (Kerberos V5) specific data. + * + * Parameters: + * + * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. + * + */ +void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5) +{ + /* Free our security context */ + if(krb5->context) { + s_pSecFn->DeleteSecurityContext(krb5->context); + free(krb5->context); + krb5->context = NULL; + } + + /* Free our credentials handle */ + if(krb5->credentials) { + s_pSecFn->FreeCredentialsHandle(krb5->credentials); + free(krb5->credentials); + krb5->credentials = NULL; + } + + /* Free our identity */ + Curl_sspi_free_identity(krb5->p_identity); + krb5->p_identity = NULL; + + /* Free the SPN and output token */ + Curl_safefree(krb5->spn); + Curl_safefree(krb5->output_token); + + /* Reset any variables */ + krb5->token_max = 0; +} + +#endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/ diff --git a/lib/vauth/vauth.h b/lib/vauth/vauth.h index 71afc40b9..f38583a17 100644 --- a/lib/vauth/vauth.h +++ b/lib/vauth/vauth.h @@ -30,6 +30,10 @@ struct SessionHandle; struct digestdata; #endif +#if defined(USE_KERBEROS5) +struct kerberos5data; +#endif + /* This is used to build a SPN string */ #if !defined(USE_WINDOWS_SSPI) char *Curl_sasl_build_spn(const char *service, const char *instance); @@ -94,4 +98,28 @@ CURLcode Curl_sasl_create_digest_http_message(struct SessionHandle *data, void Curl_sasl_digest_cleanup(struct digestdata *digest); #endif /* !CURL_DISABLE_CRYPTO_AUTH */ +#if defined(USE_KERBEROS5) +/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) user token + message */ +CURLcode Curl_sasl_create_gssapi_user_message(struct SessionHandle *data, + const char *userp, + const char *passwdp, + const char *service, + const bool mutual, + const char *chlg64, + struct kerberos5data *krb5, + char **outptr, size_t *outlen); + +/* This is used to generate a base64 encoded GSSAPI (Kerberos V5) security + token message */ +CURLcode Curl_sasl_create_gssapi_security_message(struct SessionHandle *data, + const char *input, + struct kerberos5data *krb5, + char **outptr, + size_t *outlen); + +/* This is used to clean up the GSSAPI specific data */ +void Curl_sasl_gssapi_cleanup(struct kerberos5data *krb5); +#endif /* USE_KERBEROS5 */ + #endif /* HEADER_CURL_VAUTH_H */ -- cgit v1.2.3