Annotation of embedaddon/curl/lib/vauth/krb5_gssapi.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: * Copyright (C) 2015, Daniel Stenberg, <daniel@haxx.se>, et al.
! 10: *
! 11: * This software is licensed as described in the file COPYING, which
! 12: * you should have received as part of this distribution. The terms
! 13: * are also available at https://curl.haxx.se/docs/copyright.html.
! 14: *
! 15: * You may opt to use, copy, modify, merge, publish, distribute and/or sell
! 16: * copies of the Software, and permit persons to whom the Software is
! 17: * furnished to do so, under the terms of the COPYING file.
! 18: *
! 19: * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
! 20: * KIND, either express or implied.
! 21: *
! 22: * RFC4752 The Kerberos V5 ("GSSAPI") SASL Mechanism
! 23: *
! 24: ***************************************************************************/
! 25:
! 26: #include "curl_setup.h"
! 27:
! 28: #if defined(HAVE_GSSAPI) && defined(USE_KERBEROS5)
! 29:
! 30: #include <curl/curl.h>
! 31:
! 32: #include "vauth/vauth.h"
! 33: #include "curl_sasl.h"
! 34: #include "urldata.h"
! 35: #include "curl_base64.h"
! 36: #include "curl_gssapi.h"
! 37: #include "sendf.h"
! 38: #include "curl_printf.h"
! 39:
! 40: /* The last #include files should be: */
! 41: #include "curl_memory.h"
! 42: #include "memdebug.h"
! 43:
! 44: /*
! 45: * Curl_auth_is_gssapi_supported()
! 46: *
! 47: * This is used to evaluate if GSSAPI (Kerberos V5) is supported.
! 48: *
! 49: * Parameters: None
! 50: *
! 51: * Returns TRUE if Kerberos V5 is supported by the GSS-API library.
! 52: */
! 53: bool Curl_auth_is_gssapi_supported(void)
! 54: {
! 55: return TRUE;
! 56: }
! 57:
! 58: /*
! 59: * Curl_auth_create_gssapi_user_message()
! 60: *
! 61: * This is used to generate an already encoded GSSAPI (Kerberos V5) user token
! 62: * message ready for sending to the recipient.
! 63: *
! 64: * Parameters:
! 65: *
! 66: * data [in] - The session handle.
! 67: * userp [in] - The user name.
! 68: * passwdp [in] - The user's password.
! 69: * service [in] - The service type such as http, smtp, pop or imap.
! 70: * host [in[ - The host name.
! 71: * mutual_auth [in] - Flag specifying whether or not mutual authentication
! 72: * is enabled.
! 73: * chlg64 [in] - Pointer to the optional base64 encoded challenge
! 74: * message.
! 75: * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
! 76: * outptr [in/out] - The address where a pointer to newly allocated memory
! 77: * holding the result will be stored upon completion.
! 78: * outlen [out] - The length of the output message.
! 79: *
! 80: * Returns CURLE_OK on success.
! 81: */
! 82: CURLcode Curl_auth_create_gssapi_user_message(struct Curl_easy *data,
! 83: const char *userp,
! 84: const char *passwdp,
! 85: const char *service,
! 86: const char *host,
! 87: const bool mutual_auth,
! 88: const char *chlg64,
! 89: struct kerberos5data *krb5,
! 90: char **outptr, size_t *outlen)
! 91: {
! 92: CURLcode result = CURLE_OK;
! 93: size_t chlglen = 0;
! 94: unsigned char *chlg = NULL;
! 95: OM_uint32 major_status;
! 96: OM_uint32 minor_status;
! 97: OM_uint32 unused_status;
! 98: gss_buffer_desc spn_token = GSS_C_EMPTY_BUFFER;
! 99: gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
! 100: gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
! 101:
! 102: (void) userp;
! 103: (void) passwdp;
! 104:
! 105: if(!krb5->spn) {
! 106: /* Generate our SPN */
! 107: char *spn = Curl_auth_build_spn(service, NULL, host);
! 108: if(!spn)
! 109: return CURLE_OUT_OF_MEMORY;
! 110:
! 111: /* Populate the SPN structure */
! 112: spn_token.value = spn;
! 113: spn_token.length = strlen(spn);
! 114:
! 115: /* Import the SPN */
! 116: major_status = gss_import_name(&minor_status, &spn_token,
! 117: GSS_C_NT_HOSTBASED_SERVICE, &krb5->spn);
! 118: if(GSS_ERROR(major_status)) {
! 119: Curl_gss_log_error(data, "gss_import_name() failed: ",
! 120: major_status, minor_status);
! 121:
! 122: free(spn);
! 123:
! 124: return CURLE_AUTH_ERROR;
! 125: }
! 126:
! 127: free(spn);
! 128: }
! 129:
! 130: if(chlg64 && *chlg64) {
! 131: /* Decode the base-64 encoded challenge message */
! 132: if(*chlg64 != '=') {
! 133: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
! 134: if(result)
! 135: return result;
! 136: }
! 137:
! 138: /* Ensure we have a valid challenge message */
! 139: if(!chlg) {
! 140: infof(data, "GSSAPI handshake failure (empty challenge message)\n");
! 141:
! 142: return CURLE_BAD_CONTENT_ENCODING;
! 143: }
! 144:
! 145: /* Setup the challenge "input" security buffer */
! 146: input_token.value = chlg;
! 147: input_token.length = chlglen;
! 148: }
! 149:
! 150: major_status = Curl_gss_init_sec_context(data,
! 151: &minor_status,
! 152: &krb5->context,
! 153: krb5->spn,
! 154: &Curl_krb5_mech_oid,
! 155: GSS_C_NO_CHANNEL_BINDINGS,
! 156: &input_token,
! 157: &output_token,
! 158: mutual_auth,
! 159: NULL);
! 160:
! 161: /* Free the decoded challenge as it is not required anymore */
! 162: free(input_token.value);
! 163:
! 164: if(GSS_ERROR(major_status)) {
! 165: if(output_token.value)
! 166: gss_release_buffer(&unused_status, &output_token);
! 167:
! 168: Curl_gss_log_error(data, "gss_init_sec_context() failed: ",
! 169: major_status, minor_status);
! 170:
! 171: return CURLE_AUTH_ERROR;
! 172: }
! 173:
! 174: if(output_token.value && output_token.length) {
! 175: /* Base64 encode the response */
! 176: result = Curl_base64_encode(data, (char *) output_token.value,
! 177: output_token.length, outptr, outlen);
! 178:
! 179: gss_release_buffer(&unused_status, &output_token);
! 180: }
! 181: else if(mutual_auth) {
! 182: *outptr = strdup("");
! 183: if(!*outptr)
! 184: result = CURLE_OUT_OF_MEMORY;
! 185: }
! 186:
! 187: return result;
! 188: }
! 189:
! 190: /*
! 191: * Curl_auth_create_gssapi_security_message()
! 192: *
! 193: * This is used to generate an already encoded GSSAPI (Kerberos V5) security
! 194: * token message ready for sending to the recipient.
! 195: *
! 196: * Parameters:
! 197: *
! 198: * data [in] - The session handle.
! 199: * chlg64 [in] - Pointer to the optional base64 encoded challenge message.
! 200: * krb5 [in/out] - The Kerberos 5 data struct being used and modified.
! 201: * outptr [in/out] - The address where a pointer to newly allocated memory
! 202: * holding the result will be stored upon completion.
! 203: * outlen [out] - The length of the output message.
! 204: *
! 205: * Returns CURLE_OK on success.
! 206: */
! 207: CURLcode Curl_auth_create_gssapi_security_message(struct Curl_easy *data,
! 208: const char *chlg64,
! 209: struct kerberos5data *krb5,
! 210: char **outptr,
! 211: size_t *outlen)
! 212: {
! 213: CURLcode result = CURLE_OK;
! 214: size_t chlglen = 0;
! 215: size_t messagelen = 0;
! 216: unsigned char *chlg = NULL;
! 217: unsigned char *message = NULL;
! 218: OM_uint32 major_status;
! 219: OM_uint32 minor_status;
! 220: OM_uint32 unused_status;
! 221: gss_buffer_desc input_token = GSS_C_EMPTY_BUFFER;
! 222: gss_buffer_desc output_token = GSS_C_EMPTY_BUFFER;
! 223: unsigned int indata = 0;
! 224: unsigned int outdata = 0;
! 225: gss_qop_t qop = GSS_C_QOP_DEFAULT;
! 226: unsigned int sec_layer = 0;
! 227: unsigned int max_size = 0;
! 228: gss_name_t username = GSS_C_NO_NAME;
! 229: gss_buffer_desc username_token;
! 230:
! 231: /* Decode the base-64 encoded input message */
! 232: if(strlen(chlg64) && *chlg64 != '=') {
! 233: result = Curl_base64_decode(chlg64, &chlg, &chlglen);
! 234: if(result)
! 235: return result;
! 236: }
! 237:
! 238: /* Ensure we have a valid challenge message */
! 239: if(!chlg) {
! 240: infof(data, "GSSAPI handshake failure (empty security message)\n");
! 241:
! 242: return CURLE_BAD_CONTENT_ENCODING;
! 243: }
! 244:
! 245: /* Get the fully qualified username back from the context */
! 246: major_status = gss_inquire_context(&minor_status, krb5->context,
! 247: &username, NULL, NULL, NULL, NULL,
! 248: NULL, NULL);
! 249: if(GSS_ERROR(major_status)) {
! 250: Curl_gss_log_error(data, "gss_inquire_context() failed: ",
! 251: major_status, minor_status);
! 252:
! 253: free(chlg);
! 254:
! 255: return CURLE_AUTH_ERROR;
! 256: }
! 257:
! 258: /* Convert the username from internal format to a displayable token */
! 259: major_status = gss_display_name(&minor_status, username,
! 260: &username_token, NULL);
! 261: if(GSS_ERROR(major_status)) {
! 262: Curl_gss_log_error(data, "gss_display_name() failed: ",
! 263: major_status, minor_status);
! 264:
! 265: free(chlg);
! 266:
! 267: return CURLE_AUTH_ERROR;
! 268: }
! 269:
! 270: /* Setup the challenge "input" security buffer */
! 271: input_token.value = chlg;
! 272: input_token.length = chlglen;
! 273:
! 274: /* Decrypt the inbound challenge and obtain the qop */
! 275: major_status = gss_unwrap(&minor_status, krb5->context, &input_token,
! 276: &output_token, NULL, &qop);
! 277: if(GSS_ERROR(major_status)) {
! 278: Curl_gss_log_error(data, "gss_unwrap() failed: ",
! 279: major_status, minor_status);
! 280:
! 281: gss_release_buffer(&unused_status, &username_token);
! 282: free(chlg);
! 283:
! 284: return CURLE_BAD_CONTENT_ENCODING;
! 285: }
! 286:
! 287: /* Not 4 octets long so fail as per RFC4752 Section 3.1 */
! 288: if(output_token.length != 4) {
! 289: infof(data, "GSSAPI handshake failure (invalid security data)\n");
! 290:
! 291: gss_release_buffer(&unused_status, &username_token);
! 292: free(chlg);
! 293:
! 294: return CURLE_BAD_CONTENT_ENCODING;
! 295: }
! 296:
! 297: /* Copy the data out and free the challenge as it is not required anymore */
! 298: memcpy(&indata, output_token.value, 4);
! 299: gss_release_buffer(&unused_status, &output_token);
! 300: free(chlg);
! 301:
! 302: /* Extract the security layer */
! 303: sec_layer = indata & 0x000000FF;
! 304: if(!(sec_layer & GSSAUTH_P_NONE)) {
! 305: infof(data, "GSSAPI handshake failure (invalid security layer)\n");
! 306:
! 307: gss_release_buffer(&unused_status, &username_token);
! 308:
! 309: return CURLE_BAD_CONTENT_ENCODING;
! 310: }
! 311:
! 312: /* Extract the maximum message size the server can receive */
! 313: max_size = ntohl(indata & 0xFFFFFF00);
! 314: if(max_size > 0) {
! 315: /* The server has told us it supports a maximum receive buffer, however, as
! 316: we don't require one unless we are encrypting data, we tell the server
! 317: our receive buffer is zero. */
! 318: max_size = 0;
! 319: }
! 320:
! 321: /* Allocate our message */
! 322: messagelen = sizeof(outdata) + username_token.length + 1;
! 323: message = malloc(messagelen);
! 324: if(!message) {
! 325: gss_release_buffer(&unused_status, &username_token);
! 326:
! 327: return CURLE_OUT_OF_MEMORY;
! 328: }
! 329:
! 330: /* Populate the message with the security layer, client supported receive
! 331: message size and authorization identity including the 0x00 based
! 332: terminator. Note: Despite RFC4752 Section 3.1 stating "The authorization
! 333: identity is not terminated with the zero-valued (%x00) octet." it seems
! 334: necessary to include it. */
! 335: outdata = htonl(max_size) | sec_layer;
! 336: memcpy(message, &outdata, sizeof(outdata));
! 337: memcpy(message + sizeof(outdata), username_token.value,
! 338: username_token.length);
! 339: message[messagelen - 1] = '\0';
! 340:
! 341: /* Free the username token as it is not required anymore */
! 342: gss_release_buffer(&unused_status, &username_token);
! 343:
! 344: /* Setup the "authentication data" security buffer */
! 345: input_token.value = message;
! 346: input_token.length = messagelen;
! 347:
! 348: /* Encrypt the data */
! 349: major_status = gss_wrap(&minor_status, krb5->context, 0,
! 350: GSS_C_QOP_DEFAULT, &input_token, NULL,
! 351: &output_token);
! 352: if(GSS_ERROR(major_status)) {
! 353: Curl_gss_log_error(data, "gss_wrap() failed: ",
! 354: major_status, minor_status);
! 355:
! 356: free(message);
! 357:
! 358: return CURLE_AUTH_ERROR;
! 359: }
! 360:
! 361: /* Base64 encode the response */
! 362: result = Curl_base64_encode(data, (char *) output_token.value,
! 363: output_token.length, outptr, outlen);
! 364:
! 365: /* Free the output buffer */
! 366: gss_release_buffer(&unused_status, &output_token);
! 367:
! 368: /* Free the message buffer */
! 369: free(message);
! 370:
! 371: return result;
! 372: }
! 373:
! 374: /*
! 375: * Curl_auth_cleanup_gssapi()
! 376: *
! 377: * This is used to clean up the GSSAPI (Kerberos V5) specific data.
! 378: *
! 379: * Parameters:
! 380: *
! 381: * krb5 [in/out] - The Kerberos 5 data struct being cleaned up.
! 382: *
! 383: */
! 384: void Curl_auth_cleanup_gssapi(struct kerberos5data *krb5)
! 385: {
! 386: OM_uint32 minor_status;
! 387:
! 388: /* Free our security context */
! 389: if(krb5->context != GSS_C_NO_CONTEXT) {
! 390: gss_delete_sec_context(&minor_status, &krb5->context, GSS_C_NO_BUFFER);
! 391: krb5->context = GSS_C_NO_CONTEXT;
! 392: }
! 393:
! 394: /* Free the SPN */
! 395: if(krb5->spn != GSS_C_NO_NAME) {
! 396: gss_release_name(&minor_status, &krb5->spn);
! 397: krb5->spn = GSS_C_NO_NAME;
! 398: }
! 399: }
! 400:
! 401: #endif /* HAVE_GSSAPI && USE_KERBEROS5 */
FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>