Return to spnego_sspi.c CVS log | Up to [ELWIX - Embedded LightWeight unIX -] / embedaddon / curl / lib / vauth |
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 */