1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2014 - 2016, 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 * RFC2831 DIGEST-MD5 authentication 23 * 24 ***************************************************************************/ 25 26#include "curl_setup.h" 27 28#if defined(USE_WINDOWS_SSPI) && !defined(CURL_DISABLE_CRYPTO_AUTH) 29 30#include <curl/curl.h> 31 32#include "vauth/vauth.h" 33#include "vauth/digest.h" 34#include "urldata.h" 35#include "curl_base64.h" 36#include "warnless.h" 37#include "curl_multibyte.h" 38#include "sendf.h" 39#include "strdup.h" 40#include "rawstr.h" 41 42/* The last #include files should be: */ 43#include "curl_memory.h" 44#include "memdebug.h" 45 46/* 47 * Curl_auth_create_digest_md5_message() 48 * 49 * This is used to generate an already encoded DIGEST-MD5 response message 50 * ready for sending to the recipient. 51 * 52 * Parameters: 53 * 54 * data [in] - The session handle. 55 * chlg64 [in] - The base64 encoded challenge message. 56 * userp [in] - The user name in the format User or Domain\User. 57 * passdwp [in] - The user's password. 58 * service [in] - The service type such as http, smtp, pop or imap. 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_digest_md5_message(struct Curl_easy *data, 66 const char *chlg64, 67 const char *userp, 68 const char *passwdp, 69 const char *service, 70 char **outptr, size_t *outlen) 71{ 72 CURLcode result = CURLE_OK; 73 TCHAR *spn = NULL; 74 size_t chlglen = 0; 75 size_t token_max = 0; 76 unsigned char *input_token = NULL; 77 unsigned char *output_token = NULL; 78 CredHandle credentials; 79 CtxtHandle context; 80 PSecPkgInfo SecurityPackage; 81 SEC_WINNT_AUTH_IDENTITY identity; 82 SEC_WINNT_AUTH_IDENTITY *p_identity; 83 SecBuffer chlg_buf; 84 SecBuffer resp_buf; 85 SecBufferDesc chlg_desc; 86 SecBufferDesc resp_desc; 87 SECURITY_STATUS status; 88 unsigned long attrs; 89 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ 90 91 /* Decode the base-64 encoded challenge message */ 92 if(strlen(chlg64) && *chlg64 != '=') { 93 result = Curl_base64_decode(chlg64, &input_token, &chlglen); 94 if(result) 95 return result; 96 } 97 98 /* Ensure we have a valid challenge message */ 99 if(!input_token) { 100 infof(data, "DIGEST-MD5 handshake failure (empty challenge message)\n"); 101 102 return CURLE_BAD_CONTENT_ENCODING; 103 } 104 105 /* Query the security package for DigestSSP */ 106 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), 107 &SecurityPackage); 108 if(status != SEC_E_OK) { 109 free(input_token); 110 111 return CURLE_NOT_BUILT_IN; 112 } 113 114 token_max = SecurityPackage->cbMaxToken; 115 116 /* Release the package buffer as it is not required anymore */ 117 s_pSecFn->FreeContextBuffer(SecurityPackage); 118 119 /* Allocate our response buffer */ 120 output_token = malloc(token_max); 121 if(!output_token) { 122 free(input_token); 123 124 return CURLE_OUT_OF_MEMORY; 125 } 126 127 /* Generate our SPN */ 128 spn = Curl_auth_build_spn(service, data->easy_conn->host.name, NULL); 129 if(!spn) { 130 free(output_token); 131 free(input_token); 132 133 return CURLE_OUT_OF_MEMORY; 134 } 135 136 if(userp && *userp) { 137 /* Populate our identity structure */ 138 result = Curl_create_sspi_identity(userp, passwdp, &identity); 139 if(result) { 140 free(spn); 141 free(output_token); 142 free(input_token); 143 144 return result; 145 } 146 147 /* Allow proper cleanup of the identity structure */ 148 p_identity = &identity; 149 } 150 else 151 /* Use the current Windows user */ 152 p_identity = NULL; 153 154 /* Acquire our credentials handle */ 155 status = s_pSecFn->AcquireCredentialsHandle(NULL, 156 (TCHAR *) TEXT(SP_NAME_DIGEST), 157 SECPKG_CRED_OUTBOUND, NULL, 158 p_identity, NULL, NULL, 159 &credentials, &expiry); 160 161 if(status != SEC_E_OK) { 162 Curl_sspi_free_identity(p_identity); 163 free(spn); 164 free(output_token); 165 free(input_token); 166 167 return CURLE_LOGIN_DENIED; 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 = input_token; 176 chlg_buf.cbBuffer = curlx_uztoul(chlglen); 177 178 /* Setup the response "output" security buffer */ 179 resp_desc.ulVersion = SECBUFFER_VERSION; 180 resp_desc.cBuffers = 1; 181 resp_desc.pBuffers = &resp_buf; 182 resp_buf.BufferType = SECBUFFER_TOKEN; 183 resp_buf.pvBuffer = output_token; 184 resp_buf.cbBuffer = curlx_uztoul(token_max); 185 186 /* Generate our response message */ 187 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, spn, 188 0, 0, 0, &chlg_desc, 0, 189 &context, &resp_desc, &attrs, 190 &expiry); 191 192 if(status == SEC_I_COMPLETE_NEEDED || 193 status == SEC_I_COMPLETE_AND_CONTINUE) 194 s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); 195 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { 196 s_pSecFn->FreeCredentialsHandle(&credentials); 197 Curl_sspi_free_identity(p_identity); 198 free(spn); 199 free(output_token); 200 free(input_token); 201 202 return CURLE_RECV_ERROR; 203 } 204 205 /* Base64 encode the response */ 206 result = Curl_base64_encode(data, (char *) output_token, resp_buf.cbBuffer, 207 outptr, outlen); 208 209 /* Free our handles */ 210 s_pSecFn->DeleteSecurityContext(&context); 211 s_pSecFn->FreeCredentialsHandle(&credentials); 212 213 /* Free the identity structure */ 214 Curl_sspi_free_identity(p_identity); 215 216 /* Free the SPN */ 217 free(spn); 218 219 /* Free the response buffer */ 220 free(output_token); 221 222 /* Free the decoded challenge message */ 223 free(input_token); 224 225 return result; 226} 227 228/* 229 * Curl_override_sspi_http_realm() 230 * 231 * This is used to populate the domain in a SSPI identity structure 232 * The realm is extracted from the challenge message and used as the 233 * domain if it is not already explicitly set. 234 * 235 * Parameters: 236 * 237 * chlg [in] - The challenge message. 238 * identity [in/out] - The identity structure. 239 * 240 * Returns CURLE_OK on success. 241 */ 242CURLcode Curl_override_sspi_http_realm(const char *chlg, 243 SEC_WINNT_AUTH_IDENTITY *identity) 244{ 245 xcharp_u domain, dup_domain; 246 247 /* If domain is blank or unset, check challenge message for realm */ 248 if(!identity->Domain || !identity->DomainLength) { 249 for(;;) { 250 char value[DIGEST_MAX_VALUE_LENGTH]; 251 char content[DIGEST_MAX_CONTENT_LENGTH]; 252 253 /* Pass all additional spaces here */ 254 while(*chlg && ISSPACE(*chlg)) 255 chlg++; 256 257 /* Extract a value=content pair */ 258 if(Curl_auth_digest_get_pair(chlg, value, content, &chlg)) { 259 if(Curl_raw_equal(value, "realm")) { 260 261 /* Setup identity's domain and length */ 262 domain.tchar_ptr = Curl_convert_UTF8_to_tchar((char *) content); 263 if(!domain.tchar_ptr) 264 return CURLE_OUT_OF_MEMORY; 265 266 dup_domain.tchar_ptr = _tcsdup(domain.tchar_ptr); 267 if(!dup_domain.tchar_ptr) { 268 Curl_unicodefree(domain.tchar_ptr); 269 return CURLE_OUT_OF_MEMORY; 270 } 271 272 free(identity->Domain); 273 identity->Domain = dup_domain.tbyte_ptr; 274 identity->DomainLength = curlx_uztoul(_tcslen(dup_domain.tchar_ptr)); 275 dup_domain.tchar_ptr = NULL; 276 277 Curl_unicodefree(domain.tchar_ptr); 278 } 279 else { 280 /* Unknown specifier, ignore it! */ 281 } 282 } 283 else 284 break; /* We're done here */ 285 286 /* Pass all additional spaces here */ 287 while(*chlg && ISSPACE(*chlg)) 288 chlg++; 289 290 /* Allow the list to be comma-separated */ 291 if(',' == *chlg) 292 chlg++; 293 } 294 } 295 296 return CURLE_OK; 297} 298 299/* 300 * Curl_auth_decode_digest_http_message() 301 * 302 * This is used to decode a HTTP DIGEST challenge message into the seperate 303 * attributes. 304 * 305 * Parameters: 306 * 307 * chlg [in] - The challenge message. 308 * digest [in/out] - The digest data struct being used and modified. 309 * 310 * Returns CURLE_OK on success. 311 */ 312CURLcode Curl_auth_decode_digest_http_message(const char *chlg, 313 struct digestdata *digest) 314{ 315 size_t chlglen = strlen(chlg); 316 317 /* We had an input token before and we got another one now. This means we 318 provided bad credentials in the previous request. */ 319 if(digest->input_token) 320 return CURLE_BAD_CONTENT_ENCODING; 321 322 /* Simply store the challenge for use later */ 323 digest->input_token = (BYTE *) Curl_memdup(chlg, chlglen); 324 if(!digest->input_token) 325 return CURLE_OUT_OF_MEMORY; 326 327 digest->input_token_len = chlglen; 328 329 return CURLE_OK; 330} 331 332/* 333 * Curl_auth_create_digest_http_message() 334 * 335 * This is used to generate a HTTP DIGEST response message ready for sending 336 * to the recipient. 337 * 338 * Parameters: 339 * 340 * data [in] - The session handle. 341 * userp [in] - The user name in the format User or Domain\User. 342 * passdwp [in] - The user's password. 343 * request [in] - The HTTP request. 344 * uripath [in] - The path of the HTTP uri. 345 * digest [in/out] - The digest data struct being used and modified. 346 * outptr [in/out] - The address where a pointer to newly allocated memory 347 * holding the result will be stored upon completion. 348 * outlen [out] - The length of the output message. 349 * 350 * Returns CURLE_OK on success. 351 */ 352CURLcode Curl_auth_create_digest_http_message(struct Curl_easy *data, 353 const char *userp, 354 const char *passwdp, 355 const unsigned char *request, 356 const unsigned char *uripath, 357 struct digestdata *digest, 358 char **outptr, size_t *outlen) 359{ 360 size_t token_max; 361 CredHandle credentials; 362 CtxtHandle context; 363 char *resp; 364 BYTE *output_token; 365 PSecPkgInfo SecurityPackage; 366 SEC_WINNT_AUTH_IDENTITY identity; 367 SEC_WINNT_AUTH_IDENTITY *p_identity; 368 SecBuffer chlg_buf[3]; 369 SecBuffer resp_buf; 370 SecBufferDesc chlg_desc; 371 SecBufferDesc resp_desc; 372 SECURITY_STATUS status; 373 unsigned long attrs; 374 TimeStamp expiry; /* For Windows 9x compatibility of SSPI calls */ 375 TCHAR *spn; 376 377 (void) data; 378 379 /* Query the security package for DigestSSP */ 380 status = s_pSecFn->QuerySecurityPackageInfo((TCHAR *) TEXT(SP_NAME_DIGEST), 381 &SecurityPackage); 382 if(status != SEC_E_OK) 383 return CURLE_NOT_BUILT_IN; 384 385 token_max = SecurityPackage->cbMaxToken; 386 387 /* Release the package buffer as it is not required anymore */ 388 s_pSecFn->FreeContextBuffer(SecurityPackage); 389 390 if(userp && *userp) { 391 /* Populate our identity structure */ 392 if(Curl_create_sspi_identity(userp, passwdp, &identity)) 393 return CURLE_OUT_OF_MEMORY; 394 395 /* Populate our identity domain */ 396 if(Curl_override_sspi_http_realm((const char*) digest->input_token, 397 &identity)) 398 return CURLE_OUT_OF_MEMORY; 399 400 /* Allow proper cleanup of the identity structure */ 401 p_identity = &identity; 402 } 403 else 404 /* Use the current Windows user */ 405 p_identity = NULL; 406 407 /* Acquire our credentials handle */ 408 status = s_pSecFn->AcquireCredentialsHandle(NULL, 409 (TCHAR *) TEXT(SP_NAME_DIGEST), 410 SECPKG_CRED_OUTBOUND, NULL, 411 p_identity, NULL, NULL, 412 &credentials, &expiry); 413 if(status != SEC_E_OK) { 414 Curl_sspi_free_identity(p_identity); 415 416 return CURLE_LOGIN_DENIED; 417 } 418 419 /* Allocate the output buffer according to the max token size as indicated 420 by the security package */ 421 output_token = malloc(token_max); 422 if(!output_token) { 423 s_pSecFn->FreeCredentialsHandle(&credentials); 424 425 Curl_sspi_free_identity(p_identity); 426 427 return CURLE_OUT_OF_MEMORY; 428 } 429 430 /* Setup the challenge "input" security buffer if present */ 431 chlg_desc.ulVersion = SECBUFFER_VERSION; 432 chlg_desc.cBuffers = 3; 433 chlg_desc.pBuffers = chlg_buf; 434 chlg_buf[0].BufferType = SECBUFFER_TOKEN; 435 chlg_buf[0].pvBuffer = digest->input_token; 436 chlg_buf[0].cbBuffer = curlx_uztoul(digest->input_token_len); 437 chlg_buf[1].BufferType = SECBUFFER_PKG_PARAMS; 438 chlg_buf[1].pvBuffer = (void *) request; 439 chlg_buf[1].cbBuffer = curlx_uztoul(strlen((const char *) request)); 440 chlg_buf[2].BufferType = SECBUFFER_PKG_PARAMS; 441 chlg_buf[2].pvBuffer = NULL; 442 chlg_buf[2].cbBuffer = 0; 443 444 /* Setup the response "output" security buffer */ 445 resp_desc.ulVersion = SECBUFFER_VERSION; 446 resp_desc.cBuffers = 1; 447 resp_desc.pBuffers = &resp_buf; 448 resp_buf.BufferType = SECBUFFER_TOKEN; 449 resp_buf.pvBuffer = output_token; 450 resp_buf.cbBuffer = curlx_uztoul(token_max); 451 452 spn = Curl_convert_UTF8_to_tchar((char *) uripath); 453 if(!spn) { 454 s_pSecFn->FreeCredentialsHandle(&credentials); 455 456 Curl_sspi_free_identity(p_identity); 457 free(output_token); 458 459 return CURLE_OUT_OF_MEMORY; 460 } 461 462 /* Generate our reponse message */ 463 status = s_pSecFn->InitializeSecurityContext(&credentials, NULL, 464 spn, 465 ISC_REQ_USE_HTTP_STYLE, 0, 0, 466 &chlg_desc, 0, &context, 467 &resp_desc, &attrs, &expiry); 468 Curl_unicodefree(spn); 469 470 if(status == SEC_I_COMPLETE_NEEDED || 471 status == SEC_I_COMPLETE_AND_CONTINUE) 472 s_pSecFn->CompleteAuthToken(&credentials, &resp_desc); 473 else if(status != SEC_E_OK && status != SEC_I_CONTINUE_NEEDED) { 474 s_pSecFn->FreeCredentialsHandle(&credentials); 475 476 Curl_sspi_free_identity(p_identity); 477 free(output_token); 478 479 return CURLE_OUT_OF_MEMORY; 480 } 481 482 resp = malloc(resp_buf.cbBuffer + 1); 483 if(!resp) { 484 s_pSecFn->DeleteSecurityContext(&context); 485 s_pSecFn->FreeCredentialsHandle(&credentials); 486 487 Curl_sspi_free_identity(p_identity); 488 free(output_token); 489 490 return CURLE_OUT_OF_MEMORY; 491 } 492 493 /* Copy the generated reponse */ 494 memcpy(resp, resp_buf.pvBuffer, resp_buf.cbBuffer); 495 resp[resp_buf.cbBuffer] = 0x00; 496 497 /* Return the response */ 498 *outptr = resp; 499 *outlen = resp_buf.cbBuffer; 500 501 /* Free our handles */ 502 s_pSecFn->DeleteSecurityContext(&context); 503 s_pSecFn->FreeCredentialsHandle(&credentials); 504 505 /* Free the identity structure */ 506 Curl_sspi_free_identity(p_identity); 507 508 /* Free the response buffer */ 509 free(output_token); 510 511 return CURLE_OK; 512} 513 514/* 515 * Curl_auth_digest_cleanup() 516 * 517 * This is used to clean up the digest specific data. 518 * 519 * Parameters: 520 * 521 * digest [in/out] - The digest data struct being cleaned up. 522 * 523 */ 524void Curl_auth_digest_cleanup(struct digestdata *digest) 525{ 526 /* Free the input token */ 527 Curl_safefree(digest->input_token); 528 529 /* Reset any variables */ 530 digest->input_token_len = 0; 531} 532 533#endif /* USE_WINDOWS_SSPI && !CURL_DISABLE_CRYPTO_AUTH */ 534