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