Return to krb5_gssapi.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: * 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 */