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