Annotation of embedaddon/curl/lib/vauth/krb5_sspi.c, revision 1.1
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*/
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>