1/*************************************************************************** 2 * _ _ ____ _ 3 * Project ___| | | | _ \| | 4 * / __| | | | |_) | | 5 * | (__| |_| | _ <| |___ 6 * \___|\___/|_| \_\_____| 7 * 8 * Copyright (C) 2012 - 2013, Daniel Stenberg, <daniel@haxx.se>, et al. 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 http://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 * RFC2195 CRAM-MD5 authentication 22 * RFC2831 DIGEST-MD5 authentication 23 * RFC4422 Simple Authentication and Security Layer (SASL) 24 * RFC4616 PLAIN authentication 25 * 26 ***************************************************************************/ 27 28#include "curl_setup.h" 29 30#include <curl/curl.h> 31#include "urldata.h" 32 33#include "curl_base64.h" 34#include "curl_md5.h" 35#include "curl_rand.h" 36#include "curl_hmac.h" 37#include "curl_ntlm_msgs.h" 38#include "curl_sasl.h" 39#include "warnless.h" 40#include "curl_memory.h" 41 42#define _MPRINTF_REPLACE /* use our functions only */ 43#include <curl/mprintf.h> 44 45/* The last #include file should be: */ 46#include "memdebug.h" 47 48#ifndef CURL_DISABLE_CRYPTO_AUTH 49/* Retrieves the value for a corresponding key from the challenge string 50 * returns TRUE if the key could be found, FALSE if it does not exists 51 */ 52static bool sasl_digest_get_key_value(const unsigned char *chlg, 53 const char *key, 54 char *value, 55 size_t max_val_len, 56 char end_char) 57{ 58 char *find_pos; 59 size_t i; 60 61 find_pos = strstr((const char *) chlg, key); 62 if(!find_pos) 63 return FALSE; 64 65 find_pos += strlen(key); 66 67 for(i = 0; *find_pos && *find_pos != end_char && i < max_val_len - 1; ++i) 68 value[i] = *find_pos++; 69 value[i] = '\0'; 70 71 return TRUE; 72} 73#endif 74 75/* 76 * Curl_sasl_create_plain_message() 77 * 78 * This is used to generate an already encoded PLAIN message ready 79 * for sending to the recipient. 80 * 81 * Parameters: 82 * 83 * data [in] - The session handle. 84 * userp [in] - The user name. 85 * passdwp [in] - The user's password. 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 */ 92CURLcode Curl_sasl_create_plain_message(struct SessionHandle *data, 93 const char *userp, 94 const char *passwdp, 95 char **outptr, size_t *outlen) 96{ 97 char plainauth[2 * MAX_CURL_USER_LENGTH + MAX_CURL_PASSWORD_LENGTH]; 98 size_t ulen; 99 size_t plen; 100 101 ulen = strlen(userp); 102 plen = strlen(passwdp); 103 104 if(2 * ulen + plen + 2 > sizeof(plainauth)) { 105 *outlen = 0; 106 *outptr = NULL; 107 108 /* Plainauth too small */ 109 return CURLE_OUT_OF_MEMORY; 110 } 111 112 /* Calculate the reply */ 113 memcpy(plainauth, userp, ulen); 114 plainauth[ulen] = '\0'; 115 memcpy(plainauth + ulen + 1, userp, ulen); 116 plainauth[2 * ulen + 1] = '\0'; 117 memcpy(plainauth + 2 * ulen + 2, passwdp, plen); 118 119 /* Base64 encode the reply */ 120 return Curl_base64_encode(data, plainauth, 2 * ulen + plen + 2, outptr, 121 outlen); 122} 123 124/* 125 * Curl_sasl_create_login_message() 126 * 127 * This is used to generate an already encoded LOGIN message containing the 128 * user name or password ready for sending to the recipient. 129 * 130 * Parameters: 131 * 132 * data [in] - The session handle. 133 * valuep [in] - The user name or user's password. 134 * outptr [in/out] - The address where a pointer to newly allocated memory 135 * holding the result will be stored upon completion. 136 * outlen [out] - The length of the output message. 137 * 138 * Returns CURLE_OK on success. 139 */ 140CURLcode Curl_sasl_create_login_message(struct SessionHandle *data, 141 const char *valuep, char **outptr, 142 size_t *outlen) 143{ 144 size_t vlen = strlen(valuep); 145 146 if(!vlen) { 147 /* Calculate an empty reply */ 148 *outptr = strdup("="); 149 if(*outptr) { 150 *outlen = (size_t) 1; 151 return CURLE_OK; 152 } 153 154 *outlen = 0; 155 return CURLE_OUT_OF_MEMORY; 156 } 157 158 /* Base64 encode the value */ 159 return Curl_base64_encode(data, valuep, vlen, outptr, outlen); 160} 161 162#ifndef CURL_DISABLE_CRYPTO_AUTH 163/* 164 * Curl_sasl_create_cram_md5_message() 165 * 166 * This is used to generate an already encoded CRAM-MD5 response message ready 167 * for sending to the recipient. 168 * 169 * Parameters: 170 * 171 * data [in] - The session handle. 172 * chlg64 [in] - Pointer to the base64 encoded challenge buffer. 173 * userp [in] - The user name. 174 * passdwp [in] - The user's password. 175 * outptr [in/out] - The address where a pointer to newly allocated memory 176 * holding the result will be stored upon completion. 177 * outlen [out] - The length of the output message. 178 * 179 * Returns CURLE_OK on success. 180 */ 181CURLcode Curl_sasl_create_cram_md5_message(struct SessionHandle *data, 182 const char *chlg64, 183 const char *userp, 184 const char *passwdp, 185 char **outptr, size_t *outlen) 186{ 187 CURLcode result = CURLE_OK; 188 size_t chlg64len = strlen(chlg64); 189 unsigned char *chlg = (unsigned char *) NULL; 190 size_t chlglen = 0; 191 HMAC_context *ctxt; 192 unsigned char digest[MD5_DIGEST_LEN]; 193 char response[MAX_CURL_USER_LENGTH + 2 * MD5_DIGEST_LEN + 1]; 194 195 /* Decode the challenge if necessary */ 196 if(chlg64len && *chlg64 != '=') { 197 result = Curl_base64_decode(chlg64, &chlg, &chlglen); 198 199 if(result) 200 return result; 201 } 202 203 /* Compute the digest using the password as the key */ 204 ctxt = Curl_HMAC_init(Curl_HMAC_MD5, 205 (const unsigned char *) passwdp, 206 curlx_uztoui(strlen(passwdp))); 207 208 if(!ctxt) { 209 Curl_safefree(chlg); 210 return CURLE_OUT_OF_MEMORY; 211 } 212 213 /* Update the digest with the given challenge */ 214 if(chlglen > 0) 215 Curl_HMAC_update(ctxt, chlg, curlx_uztoui(chlglen)); 216 217 Curl_safefree(chlg); 218 219 /* Finalise the digest */ 220 Curl_HMAC_final(ctxt, digest); 221 222 /* Prepare the response */ 223 snprintf(response, sizeof(response), 224 "%s %02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", 225 userp, digest[0], digest[1], digest[2], digest[3], digest[4], 226 digest[5], digest[6], digest[7], digest[8], digest[9], digest[10], 227 digest[11], digest[12], digest[13], digest[14], digest[15]); 228 229 /* Base64 encode the reply */ 230 return Curl_base64_encode(data, response, 0, outptr, outlen); 231} 232 233/* 234 * Curl_sasl_create_digest_md5_message() 235 * 236 * This is used to generate an already encoded DIGEST-MD5 response message 237 * ready for sending to the recipient. 238 * 239 * Parameters: 240 * 241 * data [in] - The session handle. 242 * chlg64 [in] - Pointer to the base64 encoded challenge buffer. 243 * userp [in] - The user name. 244 * passdwp [in] - The user's password. 245 * service [in] - The service type such as www, smtp or pop 246 * outptr [in/out] - The address where a pointer to newly allocated memory 247 * holding the result will be stored upon completion. 248 * outlen [out] - The length of the output message. 249 * 250 * Returns CURLE_OK on success. 251 */ 252CURLcode Curl_sasl_create_digest_md5_message(struct SessionHandle *data, 253 const char *chlg64, 254 const char *userp, 255 const char *passwdp, 256 const char *service, 257 char **outptr, size_t *outlen) 258{ 259 static const char table16[] = "0123456789abcdef"; 260 261 CURLcode result = CURLE_OK; 262 unsigned char *chlg = (unsigned char *) NULL; 263 size_t chlglen = 0; 264 size_t i; 265 MD5_context *ctxt; 266 unsigned char digest[MD5_DIGEST_LEN]; 267 char HA1_hex[2 * MD5_DIGEST_LEN + 1]; 268 char HA2_hex[2 * MD5_DIGEST_LEN + 1]; 269 char resp_hash_hex[2 * MD5_DIGEST_LEN + 1]; 270 271 char nonce[64]; 272 char realm[128]; 273 char alg[64]; 274 char nonceCount[] = "00000001"; 275 char cnonce[] = "12345678"; /* will be changed */ 276 char method[] = "AUTHENTICATE"; 277 char qop[] = "auth"; 278 char uri[128]; 279 char response[512]; 280 281 result = Curl_base64_decode(chlg64, &chlg, &chlglen); 282 283 if(result) 284 return result; 285 286 if(!chlg) 287 return CURLE_LOGIN_DENIED; 288 289 /* Retrieve nonce string from the challenge */ 290 if(!sasl_digest_get_key_value(chlg, "nonce=\"", nonce, 291 sizeof(nonce), '\"')) { 292 Curl_safefree(chlg); 293 return CURLE_LOGIN_DENIED; 294 } 295 296 /* Retrieve realm string from the challenge */ 297 if(!sasl_digest_get_key_value(chlg, "realm=\"", realm, 298 sizeof(realm), '\"')) { 299 /* Challenge does not have a realm, set empty string [RFC2831] page 6 */ 300 strcpy(realm, ""); 301 } 302 303 /* Retrieve algorithm string from the challenge */ 304 if(!sasl_digest_get_key_value(chlg, "algorithm=", alg, sizeof(alg), ',')) { 305 Curl_safefree(chlg); 306 return CURLE_LOGIN_DENIED; 307 } 308 309 Curl_safefree(chlg); 310 311 /* We do not support other algorithms */ 312 if(strcmp(alg, "md5-sess") != 0) 313 return CURLE_LOGIN_DENIED; 314 315 /* Generate 64 bits of random data */ 316 for(i = 0; i < 8; i++) 317 cnonce[i] = table16[Curl_rand()%16]; 318 319 /* So far so good, now calculate A1 and H(A1) according to RFC 2831 */ 320 ctxt = Curl_MD5_init(Curl_DIGEST_MD5); 321 if(!ctxt) 322 return CURLE_OUT_OF_MEMORY; 323 324 Curl_MD5_update(ctxt, (const unsigned char *) userp, 325 curlx_uztoui(strlen(userp))); 326 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 327 Curl_MD5_update(ctxt, (const unsigned char *) realm, 328 curlx_uztoui(strlen(realm))); 329 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 330 Curl_MD5_update(ctxt, (const unsigned char *) passwdp, 331 curlx_uztoui(strlen(passwdp))); 332 Curl_MD5_final(ctxt, digest); 333 334 ctxt = Curl_MD5_init(Curl_DIGEST_MD5); 335 if(!ctxt) 336 return CURLE_OUT_OF_MEMORY; 337 338 Curl_MD5_update(ctxt, (const unsigned char *) digest, MD5_DIGEST_LEN); 339 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 340 Curl_MD5_update(ctxt, (const unsigned char *) nonce, 341 curlx_uztoui(strlen(nonce))); 342 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 343 Curl_MD5_update(ctxt, (const unsigned char *) cnonce, 344 curlx_uztoui(strlen(cnonce))); 345 Curl_MD5_final(ctxt, digest); 346 347 /* Convert calculated 16 octet hex into 32 bytes string */ 348 for(i = 0; i < MD5_DIGEST_LEN; i++) 349 snprintf(&HA1_hex[2 * i], 3, "%02x", digest[i]); 350 351 /* Prepare the URL string */ 352 snprintf(uri, sizeof(uri), "%s/%s", service, realm); 353 354 /* Calculate H(A2) */ 355 ctxt = Curl_MD5_init(Curl_DIGEST_MD5); 356 if(!ctxt) 357 return CURLE_OUT_OF_MEMORY; 358 359 Curl_MD5_update(ctxt, (const unsigned char *) method, 360 curlx_uztoui(strlen(method))); 361 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 362 Curl_MD5_update(ctxt, (const unsigned char *) uri, 363 curlx_uztoui(strlen(uri))); 364 Curl_MD5_final(ctxt, digest); 365 366 for(i = 0; i < MD5_DIGEST_LEN; i++) 367 snprintf(&HA2_hex[2 * i], 3, "%02x", digest[i]); 368 369 /* Now calculate the response hash */ 370 ctxt = Curl_MD5_init(Curl_DIGEST_MD5); 371 if(!ctxt) 372 return CURLE_OUT_OF_MEMORY; 373 374 Curl_MD5_update(ctxt, (const unsigned char *) HA1_hex, 2 * MD5_DIGEST_LEN); 375 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 376 Curl_MD5_update(ctxt, (const unsigned char *) nonce, 377 curlx_uztoui(strlen(nonce))); 378 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 379 380 Curl_MD5_update(ctxt, (const unsigned char *) nonceCount, 381 curlx_uztoui(strlen(nonceCount))); 382 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 383 Curl_MD5_update(ctxt, (const unsigned char *) cnonce, 384 curlx_uztoui(strlen(cnonce))); 385 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 386 Curl_MD5_update(ctxt, (const unsigned char *) qop, 387 curlx_uztoui(strlen(qop))); 388 Curl_MD5_update(ctxt, (const unsigned char *) ":", 1); 389 390 Curl_MD5_update(ctxt, (const unsigned char *) HA2_hex, 2 * MD5_DIGEST_LEN); 391 Curl_MD5_final(ctxt, digest); 392 393 for(i = 0; i < MD5_DIGEST_LEN; i++) 394 snprintf(&resp_hash_hex[2 * i], 3, "%02x", digest[i]); 395 396 snprintf(response, sizeof(response), 397 "username=\"%s\",realm=\"%s\",nonce=\"%s\"," 398 "cnonce=\"%s\",nc=\"%s\",digest-uri=\"%s\",response=%s", 399 userp, realm, nonce, 400 cnonce, nonceCount, uri, resp_hash_hex); 401 402 /* Base64 encode the reply */ 403 return Curl_base64_encode(data, response, 0, outptr, outlen); 404} 405#endif 406 407#ifdef USE_NTLM 408/* 409 * Curl_sasl_create_ntlm_type1_message() 410 * 411 * This is used to generate an already encoded NTLM type-1 message ready for 412 * sending to the recipient. 413 * 414 * Note: This is a simple wrapper of the NTLM function which means that any 415 * SASL based protocols don't have to include the NTLM functions directly. 416 * 417 * Parameters: 418 * 419 * userp [in] - The user name in the format User or Domain\User. 420 * passdwp [in] - The user's password. 421 * ntlm [in/out] - The ntlm data struct being used and modified. 422 * outptr [in/out] - The address where a pointer to newly allocated memory 423 * holding the result will be stored upon completion. 424 * outlen [out] - The length of the output message. 425 * 426 * Returns CURLE_OK on success. 427 */ 428CURLcode Curl_sasl_create_ntlm_type1_message(const char *userp, 429 const char *passwdp, 430 struct ntlmdata *ntlm, 431 char **outptr, size_t *outlen) 432{ 433 return Curl_ntlm_create_type1_message(userp, passwdp, ntlm, outptr, 434 outlen); 435} 436 437/* 438 * Curl_sasl_create_ntlm_type3_message() 439 * 440 * This is used to generate an already encoded NTLM type-3 message ready for 441 * sending to the recipient. 442 * 443 * Parameters: 444 * 445 * data [in] - Pointer to session handle. 446 * header [in] - Pointer to the base64 encoded type-2 message buffer. 447 * userp [in] - The user name in the format User or Domain\User. 448 * passdwp [in] - The user's password. 449 * ntlm [in/out] - The ntlm data struct being used and modified. 450 * outptr [in/out] - The address where a pointer to newly allocated memory 451 * holding the result will be stored upon completion. 452 * outlen [out] - The length of the output message. 453 * 454 * Returns CURLE_OK on success. 455 */ 456CURLcode Curl_sasl_create_ntlm_type3_message(struct SessionHandle *data, 457 const char *header, 458 const char *userp, 459 const char *passwdp, 460 struct ntlmdata *ntlm, 461 char **outptr, size_t *outlen) 462{ 463 CURLcode result = Curl_ntlm_decode_type2_message(data, header, ntlm); 464 465 if(!result) 466 result = Curl_ntlm_create_type3_message(data, userp, passwdp, ntlm, 467 outptr, outlen); 468 469 return result; 470} 471#endif /* USE_NTLM */ 472 473/* 474 * Curl_sasl_cleanup() 475 * 476 * This is used to cleanup any libraries or curl modules used by the sasl 477 * functions. 478 * 479 * Parameters: 480 * 481 * conn [in] - Pointer to the connection data. 482 * authused [in] - The authentication mechanism used. 483 */ 484void Curl_sasl_cleanup(struct connectdata *conn, unsigned int authused) 485{ 486#ifdef USE_NTLM 487 /* Cleanup the ntlm structure */ 488 if(authused == SASL_MECH_NTLM) { 489 Curl_ntlm_sspi_cleanup(&conn->ntlm); 490 } 491 (void)conn; 492#else 493 /* Reserved for future use */ 494 (void)conn; 495 (void)authused; 496#endif 497} 498