Return to krb5_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) 2014 - 2019, Steve Holme, <steve_holme@hotmail.com>. ! 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: * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism ! 22: * ! 23: ***************************************************************************/ ! 24: ! 25: #include "curl_setup.h" ! 26: ! 27: #if defined(USE_WINDOWS_SSPI) && defined(USE_KERBEROS5) ! 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: ! 38: /* The last #include files should be: */ ! 39: #include "curl_memory.h" ! 40: #include "memdebug.h" ! 41: ! 42: /* ! 43: * Curl_auth_is_gssapi_supported() ! 44: * ! 45: * This is used to evaluate if GSSAPI (Kerberos V5) is supported. ! 46: * ! 47: * Parameters: None ! 48: * ! 49: * Returns TRUE if Kerberos V5 is supported by Windows SSPI. ! 50: */ ! 51: bool Curl_auth_is_gssapi_supported(void) ! 52: { ! 53: PSecPkgInfo SecurityPackage; ! 54: SECURITY_STATUS status; ! 55: ! 56: /* Query the security package for Kerberos */ ! 57: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) ! 58: TEXT(SP_NAME_KERBEROS), ! 59: &SecurityPackage); ! 60: ! 61: /* Release the package buffer as it is not required anymore */ ! 62: if(status == SEC_E_OK) { ! 63: s_pSecFn->FreeContextBuffer(SecurityPackage); ! 64: } ! 65: ! 66: return (status == SEC_E_OK ? TRUE : FALSE); ! 67: } ! 68: ! 69: /* ! 70: * Curl_auth_create_gssapi_user_message() ! 71: * ! 72: * This is used to generate an already encoded GSSAPI (Kerberos V5) user token ! 73: * message ready for sending to the recipient. ! 74: * ! 75: * Parameters: ! 76: * ! 77: * data [in] - The session handle. ! 78: * userp [in] - The user name in the format User or Domain\User. ! 79: * passwdp [in] - The user's password. ! 80: * service [in] - The service type such as http, smtp, pop or imap. ! 81: * host [in] - The host name. ! 82: * mutual_auth [in] - Flag specifying whether or not mutual authentication ! 83: * is enabled. ! 84: * chlg64 [in] - The optional base64 encoded challenge message. ! 85: * krb5 [in/out] - The Kerberos 5 data struct being used and modified. ! 86: * outptr [in/out] - The address where a pointer to newly allocated memory ! 87: * holding the result will be stored upon completion. ! 88: * outlen [out] - The length of the output message. ! 89: * ! 90: * Returns CURLE_OK on success. ! 91: */ ! 92: CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data, ! 93: const char *userp, ! 94: const char *passwdp, ! 95: const char *service, ! 96: const char *host, ! 97: const bool mutual_auth, ! 98: const char *chlg64, ! 99: struct kerberos5data *krb5, ! 100: char **outptr, size_t *outlen) ! 101: { ! 102: CURLcode result = CURLE_OK; ! 103: size_t chlglen = 0; ! 104: unsigned char *chlg = NULL; ! 105: CtxtHandle context; ! 106: PSecPkgInfo SecurityPackage; ! 107: SecBuffer chlg_buf; ! 108: SecBuffer resp_buf; ! 109: SecBufferDesc chlg_desc; ! 110: SecBufferDesc resp_desc; ! 111: SECURITY_STATUS status; ! 112: unsigned long attrs; ! 113: TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ ! 114: ! 115: if(!krb5->spn) { ! 116: /* Generate our SPN */ ! 117: krb5->spn = Curl_auth_build_spn(service, host, NULL); ! 118: if(!krb5->spn) ! 119: return CURLE_OUT_OF_MEMORY; ! 120: } ! 121: ! 122: if(!krb5->output_token) { ! 123: /* Query the security package for Kerberos */ ! 124: status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) ! 125: TEXT(SP_NAME_KERBEROS), ! 126: &SecurityPackage); ! 127: if(status != SEC_E_OK) { ! 128: return CURLE_NOT_BUILT_IN; ! 129: } ! 130: ! 131: krb5->token_max = SecurityPackage->cbMaxToken; ! 132: ! 133: /* Release the package buffer as it is not required anymore */ ! 134: s_pSecFn->FreeContextBuffer(SecurityPackage); ! 135: ! 136: /* Allocate our response buffer */ ! 137: krb5->output_token = malloc(krb5->token_max); ! 138: if(!krb5->output_token) ! 139: return CURLE_OUT_OF_MEMORY; ! 140: } ! 141: ! 142: if(!krb5->credentials) { ! 143: /* Do we have credentials to use or are we using single sign-on? */ ! 144: if(userp && *userp) { ! 145: /* Populate our identity structure */ ! 146: result = Curl_create_sspi_identity(userp, passwdp, &krb5->identity); ! 147: if(result) ! 148: return result; ! 149: ! 150: /* Allow proper cleanup of the identity structure */ ! 151: krb5->p_identity = &krb5->identity; ! 152: } ! 153: else ! 154: /* Use the current Windows user */ ! 155: krb5->p_identity = NULL; ! 156: ! 157: /* Allocate our credentials handle */ ! 158: krb5->credentials = calloc(1, sizeof(CredHandle)); ! 159: if(!krb5->credentials) ! 160: return CURLE_OUT_OF_MEMORY; ! 161: ! 162: /* Acquire our credentials handle */ ! 163: status = s_pSecFn->AcquireCredentialsHandle(NULL, ! 164: (TCHAR *) ! 165: TEXT(SP_NAME_KERBEROS), ! 166: SECPKG_CRED_OUTBOUND, NULL, ! 167: krb5->p_identity, NULL, NULL, ! 168: krb5->credentials, &expiry); ! 169: if(status != SEC_E_OK) ! 170: return CURLE_LOGIN_DENIED; ! 171: ! 172: /* Allocate our new context handle */ ! 173: krb5->context = calloc(1, sizeof(CtxtHandle)); ! 174: if(!krb5->context) ! 175: return CURLE_OUT_OF_MEMORY; ! 176: } ! 177: ! 178: if(chlg64 && *chlg64) { ! 179: /* Decode the base-64 encoded challenge message */ ! 180: if(*chlg64 != '=') { ! 181: result = Curl_base64_decode(chlg64, &chlg, &chlglen); ! 182: if(result) ! 183: return result; ! 184: } ! 185: ! 186: /* Ensure we have a valid challenge message */ ! 187: if(!chlg) { ! 188: infof(data, "GSSAPI handshake failure (empty challenge message)\n"); ! 189: ! 190: return CURLE_BAD_CONTENT_ENCODING; ! 191: } ! 192: ! 193: /* Setup the challenge "input" security buffer */ ! 194: chlg_desc.ulVersion = SECBUFFER_VERSION; ! 195: chlg_desc.cBuffers = 1; ! 196: chlg_desc.pBuffers = &chlg_buf; ! 197: chlg_buf.BufferType = SECBUFFER_TOKEN; ! 198: chlg_buf.pvBuffer = chlg; ! 199: chlg_buf.cbBuffer = curlx_uztoul(chlglen); ! 200: } ! 201: ! 202: /* Setup the response "output" security buffer */ ! 203: resp_desc.ulVersion = SECBUFFER_VERSION; ! 204: resp_desc.cBuffers = 1; ! 205: resp_desc.pBuffers = &resp_buf; ! 206: resp_buf.BufferType = SECBUFFER_TOKEN; ! 207: resp_buf.pvBuffer = krb5->output_token; ! 208: resp_buf.cbBuffer = curlx_uztoul(krb5->token_max); ! 209: ! 210: /* Generate our challenge-response message */ ! 211: status = s_pSecFn->InitializeSecurityContext(krb5->credentials, ! 212: chlg ? krb5->context : NULL, ! 213: krb5->spn, ! 214: (mutual_auth ? ! 215: ISC_REQ_MUTUAL_AUTH : 0), ! 216: 0, SECURITY_NATIVE_DREP, ! 217: chlg ? &chlg_desc : NULL, 0, ! 218: &context, ! 219: &resp_desc, &attrs, ! 220: &expiry); ! 221: ! 222: /* Free the decoded challenge as it is not required anymore */ ! 223: free(chlg); ! 224: ! 225: if(status == SEC_E_INSUFFICIENT_MEMORY) { ! 226: return CURLE_OUT_OF_MEMORY; ! 227: } ! 228: ! 229: if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { ! 230: return CURLE_AUTH_ERROR; ! 231: } ! 232: ! 233: if(memcmp(&context, krb5->context, sizeof(context))) { ! 234: s_pSecFn->DeleteSecurityContext(krb5->context); ! 235: ! 236: memcpy(krb5->context, &context, sizeof(context)); ! 237: } ! 238: ! 239: if(resp_buf.cbBuffer) { ! 240: /* Base64 encode the response */ ! 241: result = Curl_base64_encode(data, (char *) resp_buf.pvBuffer, ! 242: resp_buf.cbBuffer, outptr, outlen); ! 243: } ! 244: else if(mutual_auth) { ! 245: *outptr = strdup(""); ! 246: if(!*outptr) ! 247: result = CURLE_OUT_OF_MEMORY; ! 248: } ! 249: ! 250: return result; ! 251: } ! 252: ! 253: /* ! 254: * Curl_auth_create_gssapi_security_message() ! 255: * ! 256: * This is used to generate an already encoded GSSAPI (Kerberos V5) security ! 257: * token message ready for sending to the recipient. ! 258: * ! 259: * Parameters: ! 260: * ! 261: * data [in] - The session handle. ! 262: * chlg64 [in] - The optional base64 encoded challenge message. ! 263: * krb5 [in/out] - The Kerberos 5 data struct being used and modified. ! 264: * outptr [in/out] - The address where a pointer to newly allocated memory ! 265: * holding the result will be stored upon completion. ! 266: * outlen [out] - The length of the output message. ! 267: * ! 268: * Returns CURLE_OK on success. ! 269: */ ! 270: CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data, ! 271: const char *chlg64, ! 272: struct kerberos5data *krb5, ! 273: char **outptr, ! 274: size_t *outlen) ! 275: { ! 276: CURLcode result = CURLE_OK; ! 277: size_t offset = 0; ! 278: size_t chlglen = 0; ! 279: size_t messagelen = 0; ! 280: size_t appdatalen = 0; ! 281: unsigned char *chlg = NULL; ! 282: unsigned char *trailer = NULL; ! 283: unsigned char *message = NULL; ! 284: unsigned char *padding = NULL; ! 285: unsigned char *appdata = NULL; ! 286: SecBuffer input_buf[2]; ! 287: SecBuffer wrap_buf[3]; ! 288: SecBufferDesc input_desc; ! 289: SecBufferDesc wrap_desc; ! 290: unsigned long indata = 0; ! 291: unsigned long outdata = 0; ! 292: unsigned long qop = 0; ! 293: unsigned long sec_layer = 0; ! 294: unsigned long max_size = 0; ! 295: SecPkgContext_Sizes sizes; ! 296: SecPkgCredentials_Names names; ! 297: SECURITY_STATUS status; ! 298: char *user_name; ! 299: ! 300: /* Decode the base-64 encoded input message */ ! 301: if(strlen(chlg64) && *chlg64 != '=') { ! 302: result = Curl_base64_decode(chlg64, &chlg, &chlglen); ! 303: if(result) ! 304: return result; ! 305: } ! 306: ! 307: /* Ensure we have a valid challenge message */ ! 308: if(!chlg) { ! 309: infof(data, "GSSAPI handshake failure (empty security message)\n"); ! 310: ! 311: return CURLE_BAD_CONTENT_ENCODING; ! 312: } ! 313: ! 314: /* Get our response size information */ ! 315: status = s_pSecFn->QueryContextAttributes(krb5->context, ! 316: SECPKG_ATTR_SIZES, ! 317: &sizes); ! 318: if(status != SEC_E_OK) { ! 319: free(chlg); ! 320: ! 321: if(status == SEC_E_INSUFFICIENT_MEMORY) ! 322: return CURLE_OUT_OF_MEMORY; ! 323: ! 324: return CURLE_AUTH_ERROR; ! 325: } ! 326: ! 327: /* Get the fully qualified username back from the context */ ! 328: status = s_pSecFn->QueryCredentialsAttributes(krb5->credentials, ! 329: SECPKG_CRED_ATTR_NAMES, ! 330: &names); ! 331: if(status != SEC_E_OK) { ! 332: free(chlg); ! 333: ! 334: if(status == SEC_E_INSUFFICIENT_MEMORY) ! 335: return CURLE_OUT_OF_MEMORY; ! 336: ! 337: return CURLE_AUTH_ERROR; ! 338: } ! 339: ! 340: /* Setup the "input" security buffer */ ! 341: input_desc.ulVersion = SECBUFFER_VERSION; ! 342: input_desc.cBuffers = 2; ! 343: input_desc.pBuffers = input_buf; ! 344: input_buf[0].BufferType = SECBUFFER_STREAM; ! 345: input_buf[0].pvBuffer = chlg; ! 346: input_buf[0].cbBuffer = curlx_uztoul(chlglen); ! 347: input_buf[1].BufferType = SECBUFFER_DATA; ! 348: input_buf[1].pvBuffer = NULL; ! 349: input_buf[1].cbBuffer = 0; ! 350: ! 351: /* Decrypt the inbound challenge and obtain the qop */ ! 352: status = s_pSecFn->DecryptMessage(krb5->context, &input_desc, 0, &qop); ! 353: if(status != SEC_E_OK) { ! 354: infof(data, "GSSAPI handshake failure (empty security message)\n"); ! 355: ! 356: free(chlg); ! 357: ! 358: return CURLE_BAD_CONTENT_ENCODING; ! 359: } ! 360: ! 361: /* Not 4 octets long so fail as per RFC4752 Section 3.1 */ ! 362: if(input_buf[1].cbBuffer != 4) { ! 363: infof(data, "GSSAPI handshake failure (invalid security data)\n"); ! 364: ! 365: free(chlg); ! 366: ! 367: return CURLE_BAD_CONTENT_ENCODING; ! 368: } ! 369: ! 370: /* Copy the data out and free the challenge as it is not required anymore */ ! 371: memcpy(&indata, input_buf[1].pvBuffer, 4); ! 372: s_pSecFn->FreeContextBuffer(input_buf[1].pvBuffer); ! 373: free(chlg); ! 374: ! 375: /* Extract the security layer */ ! 376: sec_layer = indata & 0x000000FF; ! 377: if(!(sec_layer & KERB_WRAP_NO_ENCRYPT)) { ! 378: infof(data, "GSSAPI handshake failure (invalid security layer)\n"); ! 379: ! 380: return CURLE_BAD_CONTENT_ENCODING; ! 381: } ! 382: ! 383: /* Extract the maximum message size the server can receive */ ! 384: max_size = ntohl(indata & 0xFFFFFF00); ! 385: if(max_size > 0) { ! 386: /* The server has told us it supports a maximum receive buffer, however, as ! 387: we don't require one unless we are encrypting data, we tell the server ! 388: our receive buffer is zero. */ ! 389: max_size = 0; ! 390: } ! 391: ! 392: /* Allocate the trailer */ ! 393: trailer = malloc(sizes.cbSecurityTrailer); ! 394: if(!trailer) ! 395: return CURLE_OUT_OF_MEMORY; ! 396: ! 397: /* Convert the user name to UTF8 when operating with Unicode */ ! 398: user_name = Curl_convert_tchar_to_UTF8(names.sUserName); ! 399: if(!user_name) { ! 400: free(trailer); ! 401: ! 402: return CURLE_OUT_OF_MEMORY; ! 403: } ! 404: ! 405: /* Allocate our message */ ! 406: messagelen = sizeof(outdata) + strlen(user_name) + 1; ! 407: message = malloc(messagelen); ! 408: if(!message) { ! 409: free(trailer); ! 410: Curl_unicodefree(user_name); ! 411: ! 412: return CURLE_OUT_OF_MEMORY; ! 413: } ! 414: ! 415: /* Populate the message with the security layer, client supported receive ! 416: message size and authorization identity including the 0x00 based ! 417: terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization ! 418: identity is not terminated with the zero-valued (%x00) octet." it seems ! 419: necessary to include it. */ ! 420: outdata = htonl(max_size) | sec_layer; ! 421: memcpy(message, &outdata, sizeof(outdata)); ! 422: strcpy((char *) message + sizeof(outdata), user_name); ! 423: Curl_unicodefree(user_name); ! 424: ! 425: /* Allocate the padding */ ! 426: padding = malloc(sizes.cbBlockSize); ! 427: if(!padding) { ! 428: free(message); ! 429: free(trailer); ! 430: ! 431: return CURLE_OUT_OF_MEMORY; ! 432: } ! 433: ! 434: /* Setup the "authentication data" security buffer */ ! 435: wrap_desc.ulVersion = SECBUFFER_VERSION; ! 436: wrap_desc.cBuffers = 3; ! 437: wrap_desc.pBuffers = wrap_buf; ! 438: wrap_buf[0].BufferType = SECBUFFER_TOKEN; ! 439: wrap_buf[0].pvBuffer = trailer; ! 440: wrap_buf[0].cbBuffer = sizes.cbSecurityTrailer; ! 441: wrap_buf[1].BufferType = SECBUFFER_DATA; ! 442: wrap_buf[1].pvBuffer = message; ! 443: wrap_buf[1].cbBuffer = curlx_uztoul(messagelen); ! 444: wrap_buf[2].BufferType = SECBUFFER_PADDING; ! 445: wrap_buf[2].pvBuffer = padding; ! 446: wrap_buf[2].cbBuffer = sizes.cbBlockSize; ! 447: ! 448: /* Encrypt the data */ ! 449: status = s_pSecFn->EncryptMessage(krb5->context, KERB_WRAP_NO_ENCRYPT, ! 450: &wrap_desc, 0); ! 451: if(status != SEC_E_OK) { ! 452: free(padding); ! 453: free(message); ! 454: free(trailer); ! 455: ! 456: if(status == SEC_E_INSUFFICIENT_MEMORY) ! 457: return CURLE_OUT_OF_MEMORY; ! 458: ! 459: return CURLE_AUTH_ERROR; ! 460: } ! 461: ! 462: /* Allocate the encryption (wrap) buffer */ ! 463: appdatalen = wrap_buf[0].cbBuffer + wrap_buf[1].cbBuffer + ! 464: wrap_buf[2].cbBuffer; ! 465: appdata = malloc(appdatalen); ! 466: if(!appdata) { ! 467: free(padding); ! 468: free(message); ! 469: free(trailer); ! 470: ! 471: return CURLE_OUT_OF_MEMORY; ! 472: } ! 473: ! 474: /* Populate the encryption buffer */ ! 475: memcpy(appdata, wrap_buf[0].pvBuffer, wrap_buf[0].cbBuffer); ! 476: offset += wrap_buf[0].cbBuffer; ! 477: memcpy(appdata + offset, wrap_buf[1].pvBuffer, wrap_buf[1].cbBuffer); ! 478: offset += wrap_buf[1].cbBuffer; ! 479: memcpy(appdata + offset, wrap_buf[2].pvBuffer, wrap_buf[2].cbBuffer); ! 480: ! 481: /* Base64 encode the response */ ! 482: result = Curl_base64_encode(data, (char *) appdata, appdatalen, outptr, ! 483: outlen); ! 484: ! 485: /* Free all of our local buffers */ ! 486: free(appdata); ! 487: free(padding); ! 488: free(message); ! 489: free(trailer); ! 490: ! 491: return result; ! 492: } ! 493: ! 494: /* ! 495: * Curl_auth_cleanup_gssapi() ! 496: * ! 497: * This is used to clean up the GSSAPI (Kerberos V5) specific data. ! 498: * ! 499: * Parameters: ! 500: * ! 501: * krb5 [in/out] - The Kerberos 5 data struct being cleaned up. ! 502: * ! 503: */ ! 504: void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5) ! 505: { ! 506: /* Free our security context */ ! 507: if(krb5->context) { ! 508: s_pSecFn->DeleteSecurityContext(krb5->context); ! 509: free(krb5->context); ! 510: krb5->context = NULL; ! 511: } ! 512: ! 513: /* Free our credentials handle */ ! 514: if(krb5->credentials) { ! 515: s_pSecFn->FreeCredentialsHandle(krb5->credentials); ! 516: free(krb5->credentials); ! 517: krb5->credentials = NULL; ! 518: } ! 519: ! 520: /* Free our identity */ ! 521: Curl_sspi_free_identity(krb5->p_identity); ! 522: krb5->p_identity = NULL; ! 523: ! 524: /* Free the SPN and output token */ ! 525: Curl_safefree(krb5->spn); ! 526: Curl_safefree(krb5->output_token); ! 527: ! 528: /* Reset any variables */ ! 529: krb5->token_max = 0; ! 530: } ! 531: ! 532: #endif /* USE_WINDOWS_SSPI && USE_KERBEROS5*/