Annotation of embedaddon/curl/lib/vauth/spnego_sspi.c, revision 1.1
1.1 ! misho 1: /***************************************************************************
! 2: * _ _ ____ _
! 3: * Project ___| | | | _ \| |
! 4: * / __| | | | |_) | |
! 5: * | (__| |_| | _ <| |___
! 6: * \___|\___/|_| \_\_____|
! 7: *
! 8: * Copyright (C) 1998 - 2019, Daniel Stenberg, <daniel@haxx.se>, et al.
! 9: *
! 10: * This software is licensed as described in the file COPYING, which
! 11: * you should have received as part of this distribution. The terms
! 12: * are also available at https://curl.haxx.se/docs/copyright.html.
! 13: *
! 14: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
! 15: * copies of the Software, and permit persons to whom the Software is
! 16: * furnished to do so, under the terms of the COPYING file.
! 17: *
! 18: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
! 19: * KIND, either express or implied.
! 20: *
! 21: * RFC4178 Simple and Protected GSS-API Negotiation Mechanism
! 22: *
! 23: ***************************************************************************/
! 24:
! 25: #include "curl_setup.h"
! 26:
! 27: #if defined(USE_WINDOWS_SSPI) && defined(USE_SPNEGO)
! 28:
! 29: #include <curl/curl.h>
! 30:
! 31: #include "vauth/vauth.h"
! 32: #include "urldata.h"
! 33: #include "curl_base64.h"
! 34: #include "warnless.h"
! 35: #include "curl_multibyte.h"
! 36: #include "sendf.h"
! 37: #include "strerror.h"
! 38:
! 39: /* The last #include files should be: */
! 40: #include "curl_memory.h"
! 41: #include "memdebug.h"
! 42:
! 43: /*
! 44: * Curl_auth_is_spnego_supported()
! 45: *
! 46: * This is used to evaluate if SPNEGO (Negotiate) is supported.
! 47: *
! 48: * Parameters: None
! 49: *
! 50: * Returns TRUE if Negotiate is supported by Windows SSPI.
! 51: */
! 52: bool Curl_auth_is_spnego_supported(void)
! 53: {
! 54: PSecPkgInfo SecurityPackage;
! 55: SECURITY_STATUS status;
! 56:
! 57: /* Query the security package for Negotiate */
! 58: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
! 59: TEXT(SP_NAME_NEGOTIATE),
! 60: &SecurityPackage);
! 61:
! 62: /* Release the package buffer as it is not required anymore */
! 63: if(status == SEC_E_OK) {
! 64: s_pSecFn->FreeContextBuffer(SecurityPackage);
! 65: }
! 66:
! 67:
! 68: return (status == SEC_E_OK ? TRUE : FALSE);
! 69: }
! 70:
! 71: /*
! 72: * Curl_auth_decode_spnego_message()
! 73: *
! 74: * This is used to decode an already encoded SPNEGO (Negotiate) challenge
! 75: * message.
! 76: *
! 77: * Parameters:
! 78: *
! 79: * data [in] - The session handle.
! 80: * user [in] - The user name in the format User or Domain\User.
! 81: * password [in] - The user's password.
! 82: * service [in] - The service type such as http, smtp, pop or imap.
! 83: * host [in] - The host name.
! 84: * chlg64 [in] - The optional base64 encoded challenge message.
! 85: * nego [in/out] - The Negotiate data struct being used and modified.
! 86: *
! 87: * Returns CURLE_OK on success.
! 88: */
! 89: CURLcode Curl_auth_decode_spnego_message(struct Curl_easy *data,
! 90: const char *user,
! 91: const char *password,
! 92: const char *service,
! 93: const char *host,
! 94: const char *chlg64,
! 95: struct negotiatedata *nego)
! 96: {
! 97: CURLcode result = CURLE_OK;
! 98: size_t chlglen = 0;
! 99: unsigned char *chlg = NULL;
! 100: PSecPkgInfo SecurityPackage;
! 101: SecBuffer chlg_buf[2];
! 102: SecBuffer resp_buf;
! 103: SecBufferDesc chlg_desc;
! 104: SecBufferDesc resp_desc;
! 105: unsigned long attrs;
! 106: TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */
! 107:
! 108: #if defined(CURL_DISABLE_VERBOSE_STRINGS)
! 109: (void) data;
! 110: #endif
! 111:
! 112: if(nego->context && nego->status == SEC_E_OK) {
! 113: /* We finished successfully our part of authentication, but server
! 114: * rejected it (since we're again here). Exit with an error since we
! 115: * can't invent anything better */
! 116: Curl_auth_cleanup_spnego(nego);
! 117: return CURLE_LOGIN_DENIED;
! 118: }
! 119:
! 120: if(!nego->spn) {
! 121: /* Generate our SPN */
! 122: nego->spn = Curl_auth_build_spn(service, host, NULL);
! 123: if(!nego->spn)
! 124: return CURLE_OUT_OF_MEMORY;
! 125: }
! 126:
! 127: if(!nego->output_token) {
! 128: /* Query the security package for Negotiate */
! 129: nego->status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *)
! 130: TEXT(SP_NAME_NEGOTIATE),
! 131: &SecurityPackage);
! 132: if(nego->status != SEC_E_OK)
! 133: return CURLE_NOT_BUILT_IN;
! 134:
! 135: nego->token_max = SecurityPackage->cbMaxToken;
! 136:
! 137: /* Release the package buffer as it is not required anymore */
! 138: s_pSecFn->FreeContextBuffer(SecurityPackage);
! 139:
! 140: /* Allocate our output buffer */
! 141: nego->output_token = malloc(nego->token_max);
! 142: if(!nego->output_token)
! 143: return CURLE_OUT_OF_MEMORY;
! 144: }
! 145:
! 146: if(!nego->credentials) {
! 147: /* Do we have credentials to use or are we using single sign-on? */
! 148: if(user && *user) {
! 149: /* Populate our identity structure */
! 150: result = Curl_create_sspi_identity(user, password, &nego->identity);
! 151: if(result)
! 152: return result;
! 153:
! 154: /* Allow proper cleanup of the identity structure */
! 155: nego->p_identity = &nego->identity;
! 156: }
! 157: else
! 158: /* Use the current Windows user */
! 159: nego->p_identity = NULL;
! 160:
! 161: /* Allocate our credentials handle */
! 162: nego->credentials = calloc(1, sizeof(CredHandle));
! 163: if(!nego->credentials)
! 164: return CURLE_OUT_OF_MEMORY;
! 165:
! 166: /* Acquire our credentials handle */
! 167: nego->status =
! 168: s_pSecFn->AcquireCredentialsHandle(NULL,
! 169: (TCHAR *)TEXT(SP_NAME_NEGOTIATE),
! 170: SECPKG_CRED_OUTBOUND, NULL,
! 171: nego->p_identity, NULL, NULL,
! 172: nego->credentials, &expiry);
! 173: if(nego->status != SEC_E_OK)
! 174: return CURLE_AUTH_ERROR;
! 175:
! 176: /* Allocate our new context handle */
! 177: nego->context = calloc(1, sizeof(CtxtHandle));
! 178: if(!nego->context)
! 179: return CURLE_OUT_OF_MEMORY;
! 180: }
! 181:
! 182: if(chlg64 && *chlg64) {
! 183: /* Decode the base-64 encoded challenge message */
! 184: if(*chlg64 != '=') {
! 185: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
! 186: if(result)
! 187: return result;
! 188: }
! 189:
! 190: /* Ensure we have a valid challenge message */
! 191: if(!chlg) {
! 192: infof(data, "SPNEGO handshake failure (empty challenge message)\n");
! 193:
! 194: return CURLE_BAD_CONTENT_ENCODING;
! 195: }
! 196:
! 197: /* Setup the challenge "input" security buffer */
! 198: chlg_desc.ulVersion = SECBUFFER_VERSION;
! 199: chlg_desc.cBuffers = 1;
! 200: chlg_desc.pBuffers = &chlg_buf[0];
! 201: chlg_buf[0].BufferType = SECBUFFER_TOKEN;
! 202: chlg_buf[0].pvBuffer = chlg;
! 203: chlg_buf[0].cbBuffer = curlx_uztoul(chlglen);
! 204:
! 205: #ifdef SECPKG_ATTR_ENDPOINT_BINDINGS
! 206: /* ssl context comes from Schannel.
! 207: * When extended protection is used in IIS server,
! 208: * we have to pass a second SecBuffer to the SecBufferDesc
! 209: * otherwise IIS will not pass the authentication (401 response).
! 210: * Minimum supported version is Windows 7.
! 211: * https://docs.microsoft.com/en-us/security-updates
! 212: * /SecurityAdvisories/2009/973811
! 213: */
! 214: if(nego->sslContext) {
! 215: SEC_CHANNEL_BINDINGS channelBindings;
! 216: SecPkgContext_Bindings pkgBindings;
! 217: pkgBindings.Bindings = &channelBindings;
! 218: nego->status = s_pSecFn->QueryContextAttributes(
! 219: nego->sslContext,
! 220: SECPKG_ATTR_ENDPOINT_BINDINGS,
! 221: &pkgBindings
! 222: );
! 223: if(nego->status == SEC_E_OK) {
! 224: chlg_desc.cBuffers++;
! 225: chlg_buf[1].BufferType = SECBUFFER_CHANNEL_BINDINGS;
! 226: chlg_buf[1].cbBuffer = pkgBindings.BindingsLength;
! 227: chlg_buf[1].pvBuffer = pkgBindings.Bindings;
! 228: }
! 229: }
! 230: #endif
! 231: }
! 232:
! 233: /* Setup the response "output" security buffer */
! 234: resp_desc.ulVersion = SECBUFFER_VERSION;
! 235: resp_desc.cBuffers = 1;
! 236: resp_desc.pBuffers = &resp_buf;
! 237: resp_buf.BufferType = SECBUFFER_TOKEN;
! 238: resp_buf.pvBuffer = nego->output_token;
! 239: resp_buf.cbBuffer = curlx_uztoul(nego->token_max);
! 240:
! 241: /* Generate our challenge-response message */
! 242: nego->status = s_pSecFn->InitializeSecurityContext(nego->credentials,
! 243: chlg ? nego->context :
! 244: NULL,
! 245: nego->spn,
! 246: ISC_REQ_CONFIDENTIALITY,
! 247: 0, SECURITY_NATIVE_DREP,
! 248: chlg ? &chlg_desc : NULL,
! 249: 0, nego->context,
! 250: &resp_desc, &attrs,
! 251: &expiry);
! 252:
! 253: /* Free the decoded challenge as it is not required anymore */
! 254: free(chlg);
! 255:
! 256: if(GSS_ERROR(nego->status)) {
! 257: char buffer[STRERROR_LEN];
! 258: failf(data, "InitializeSecurityContext failed: %s",
! 259: Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
! 260:
! 261: if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
! 262: return CURLE_OUT_OF_MEMORY;
! 263:
! 264: return CURLE_AUTH_ERROR;
! 265: }
! 266:
! 267: if(nego->status == SEC_I_COMPLETE_NEEDED ||
! 268: nego->status == SEC_I_COMPLETE_AND_CONTINUE) {
! 269: nego->status = s_pSecFn->CompleteAuthToken(nego->context, &resp_desc);
! 270: if(GSS_ERROR(nego->status)) {
! 271: char buffer[STRERROR_LEN];
! 272: failf(data, "CompleteAuthToken failed: %s",
! 273: Curl_sspi_strerror(nego->status, buffer, sizeof(buffer)));
! 274:
! 275: if(nego->status == (DWORD)SEC_E_INSUFFICIENT_MEMORY)
! 276: return CURLE_OUT_OF_MEMORY;
! 277:
! 278: return CURLE_AUTH_ERROR;
! 279: }
! 280: }
! 281:
! 282: nego->output_token_length = resp_buf.cbBuffer;
! 283:
! 284: return result;
! 285: }
! 286:
! 287: /*
! 288: * Curl_auth_create_spnego_message()
! 289: *
! 290: * This is used to generate an already encoded SPNEGO (Negotiate) response
! 291: * message ready for sending to the recipient.
! 292: *
! 293: * Parameters:
! 294: *
! 295: * data [in] - The session handle.
! 296: * nego [in/out] - The Negotiate data struct being used and modified.
! 297: * outptr [in/out] - The address where a pointer to newly allocated memory
! 298: * holding the result will be stored upon completion.
! 299: * outlen [out] - The length of the output message.
! 300: *
! 301: * Returns CURLE_OK on success.
! 302: */
! 303: CURLcode Curl_auth_create_spnego_message(struct Curl_easy *data,
! 304: struct negotiatedata *nego,
! 305: char **outptr, size_t *outlen)
! 306: {
! 307: CURLcode result;
! 308:
! 309: /* Base64 encode the already generated response */
! 310: result = Curl_base64_encode(data,
! 311: (const char *) nego->output_token,
! 312: nego->output_token_length,
! 313: outptr, outlen);
! 314:
! 315: if(result)
! 316: return result;
! 317:
! 318: if(!*outptr || !*outlen) {
! 319: free(*outptr);
! 320: return CURLE_REMOTE_ACCESS_DENIED;
! 321: }
! 322:
! 323: return CURLE_OK;
! 324: }
! 325:
! 326: /*
! 327: * Curl_auth_cleanup_spnego()
! 328: *
! 329: * This is used to clean up the SPNEGO (Negotiate) specific data.
! 330: *
! 331: * Parameters:
! 332: *
! 333: * nego [in/out] - The Negotiate data struct being cleaned up.
! 334: *
! 335: */
! 336: void Curl_auth_cleanup_spnego(struct negotiatedata *nego)
! 337: {
! 338: /* Free our security context */
! 339: if(nego->context) {
! 340: s_pSecFn->DeleteSecurityContext(nego->context);
! 341: free(nego->context);
! 342: nego->context = NULL;
! 343: }
! 344:
! 345: /* Free our credentials handle */
! 346: if(nego->credentials) {
! 347: s_pSecFn->FreeCredentialsHandle(nego->credentials);
! 348: free(nego->credentials);
! 349: nego->credentials = NULL;
! 350: }
! 351:
! 352: /* Free our identity */
! 353: Curl_sspi_free_identity(nego->p_identity);
! 354: nego->p_identity = NULL;
! 355:
! 356: /* Free the SPN and output token */
! 357: Curl_safefree(nego->spn);
! 358: Curl_safefree(nego->output_token);
! 359:
! 360: /* Reset any variables */
! 361: nego->status = 0;
! 362: nego->token_max = 0;
! 363: nego->noauthpersist = FALSE;
! 364: nego->havenoauthpersist = FALSE;
! 365: nego->havenegdata = FALSE;
! 366: nego->havemultiplerequests = FALSE;
! 367: }
! 368:
! 369: #endif /* USE_WINDOWS_SSPI && USE_SPNEGO */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>