1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2010, 2011, 2012 Daniel Pittman and Christian Grothoff 4 5 This library is free software; you can redistribute it and/or 6 modify it under the terms of the GNU Lesser General Public 7 License as published by the Free Software Foundation; either 8 version 2.1 of the License, or (at your option) any later version. 9 10 This library is distributed in the hope that it will be useful, 11 but WITHOUT ANY WARRANTY; without even the implied warranty of 12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 13 Lesser General Public License for more details. 14 15 You should have received a copy of the GNU Lesser General Public 16 License along with this library; if not, write to the Free Software 17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 18 */ 19 /** 20 * @file digestauth.c 21 * @brief Implements HTTP digest authentication 22 * @author Amr Ali 23 * @author Matthieu Speder 24 */ 25 #include "platform.h" 26 #include <limits.h> 27 #include "internal.h" 28 #include "md5.h" 29 30 #if defined(_WIN32) && defined(MHD_W32_MUTEX_) 31 #ifndef WIN32_LEAN_AND_MEAN 32 #define WIN32_LEAN_AND_MEAN 1 33 #endif /* !WIN32_LEAN_AND_MEAN */ 34 #include <windows.h> 35 #endif /* _WIN32 && MHD_W32_MUTEX_ */ 36 37 #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE) 38 39 /** 40 * Beginning string for any valid Digest authentication header. 41 */ 42 #define _BASE "Digest " 43 44 /** 45 * Maximum length of a username for digest authentication. 46 */ 47 #define MAX_USERNAME_LENGTH 128 48 49 /** 50 * Maximum length of a realm for digest authentication. 51 */ 52 #define MAX_REALM_LENGTH 256 53 54 /** 55 * Maximum length of the response in digest authentication. 56 */ 57 #define MAX_AUTH_RESPONSE_LENGTH 128 58 59 60 /** 61 * convert bin to hex 62 * 63 * @param bin binary data 64 * @param len number of bytes in bin 65 * @param hex pointer to len*2+1 bytes 66 */ 67 static void 68 cvthex (const unsigned char *bin, 69 size_t len, 70 char *hex) 71 { 72 size_t i; 73 unsigned int j; 74 75 for (i = 0; i < len; ++i) 76 { 77 j = (bin[i] >> 4) & 0x0f; 78 hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10); 79 j = bin[i] & 0x0f; 80 hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10); 81 } 82 hex[len * 2] = '\0'; 83 } 84 85 86 /** 87 * calculate H(A1) as per RFC2617 spec and store the 88 * result in 'sessionkey'. 89 * 90 * @param alg The hash algorithm used, can be "md5" or "md5-sess" 91 * @param username A `char *' pointer to the username value 92 * @param realm A `char *' pointer to the realm value 93 * @param password A `char *' pointer to the password value 94 * @param nonce A `char *' pointer to the nonce value 95 * @param cnonce A `char *' pointer to the cnonce value 96 * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes 97 */ 98 static void 99 digest_calc_ha1 (const char *alg, 100 const char *username, 101 const char *realm, 102 const char *password, 103 const char *nonce, 104 const char *cnonce, 105 char *sessionkey) 106 { 107 struct MD5Context md5; 108 unsigned char ha1[MD5_DIGEST_SIZE]; 109 110 MD5Init (&md5); 111 MD5Update (&md5, username, strlen (username)); 112 MD5Update (&md5, ":", 1); 113 MD5Update (&md5, realm, strlen (realm)); 114 MD5Update (&md5, ":", 1); 115 MD5Update (&md5, password, strlen (password)); 116 MD5Final (ha1, &md5); 117 if (MHD_str_equal_caseless_(alg, "md5-sess")) 118 { 119 MD5Init (&md5); 120 MD5Update (&md5, ha1, sizeof (ha1)); 121 MD5Update (&md5, ":", 1); 122 MD5Update (&md5, nonce, strlen (nonce)); 123 MD5Update (&md5, ":", 1); 124 MD5Update (&md5, cnonce, strlen (cnonce)); 125 MD5Final (ha1, &md5); 126 } 127 cvthex (ha1, sizeof (ha1), sessionkey); 128 } 129 130 131 /** 132 * Calculate request-digest/response-digest as per RFC2617 spec 133 * 134 * @param ha1 H(A1) 135 * @param nonce nonce from server 136 * @param noncecount 8 hex digits 137 * @param cnonce client nonce 138 * @param qop qop-value: "", "auth" or "auth-int" 139 * @param method method from request 140 * @param uri requested URL 141 * @param hentity H(entity body) if qop="auth-int" 142 * @param response request-digest or response-digest 143 */ 144 static void 145 digest_calc_response (const char *ha1, 146 const char *nonce, 147 const char *noncecount, 148 const char *cnonce, 149 const char *qop, 150 const char *method, 151 const char *uri, 152 const char *hentity, 153 char *response) 154 { 155 struct MD5Context md5; 156 unsigned char ha2[MD5_DIGEST_SIZE]; 157 unsigned char resphash[MD5_DIGEST_SIZE]; 158 char ha2hex[HASH_MD5_HEX_LEN + 1]; 159 160 MD5Init (&md5); 161 MD5Update (&md5, method, strlen(method)); 162 MD5Update (&md5, ":", 1); 163 MD5Update (&md5, uri, strlen(uri)); 164 #if 0 165 if (0 == strcasecmp(qop, "auth-int")) 166 { 167 /* This is dead code since the rest of this module does 168 not support auth-int. */ 169 MD5Update (&md5, ":", 1); 170 if (NULL != hentity) 171 MD5Update (&md5, hentity, strlen(hentity)); 172 } 173 #endif 174 MD5Final (ha2, &md5); 175 cvthex (ha2, MD5_DIGEST_SIZE, ha2hex); 176 MD5Init (&md5); 177 /* calculate response */ 178 MD5Update (&md5, ha1, HASH_MD5_HEX_LEN); 179 MD5Update (&md5, ":", 1); 180 MD5Update (&md5, nonce, strlen(nonce)); 181 MD5Update (&md5, ":", 1); 182 if ('\0' != *qop) 183 { 184 MD5Update (&md5, noncecount, strlen(noncecount)); 185 MD5Update (&md5, ":", 1); 186 MD5Update (&md5, cnonce, strlen(cnonce)); 187 MD5Update (&md5, ":", 1); 188 MD5Update (&md5, qop, strlen(qop)); 189 MD5Update (&md5, ":", 1); 190 } 191 MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN); 192 MD5Final (resphash, &md5); 193 cvthex (resphash, sizeof (resphash), response); 194 } 195 196 197 /** 198 * Lookup subvalue off of the HTTP Authorization header. 199 * 200 * A description of the input format for 'data' is at 201 * http://en.wikipedia.org/wiki/Digest_access_authentication 202 * 203 * 204 * @param dest where to store the result (possibly truncated if 205 * the buffer is not big enough). 206 * @param size size of dest 207 * @param data pointer to the Authorization header 208 * @param key key to look up in data 209 * @return size of the located value, 0 if otherwise 210 */ 211 static size_t 212 lookup_sub_value (char *dest, 213 size_t size, 214 const char *data, 215 const char *key) 216 { 217 size_t keylen; 218 size_t len; 219 const char *ptr; 220 const char *eq; 221 const char *q1; 222 const char *q2; 223 const char *qn; 224 225 if (0 == size) 226 return 0; 227 keylen = strlen (key); 228 ptr = data; 229 while ('\0' != *ptr) 230 { 231 if (NULL == (eq = strchr (ptr, '='))) 232 return 0; 233 q1 = eq + 1; 234 while (' ' == *q1) 235 q1++; 236 if ('\"' != *q1) 237 { 238 q2 = strchr (q1, ','); 239 qn = q2; 240 } 241 else 242 { 243 q1++; 244 q2 = strchr (q1, '\"'); 245 if (NULL == q2) 246 return 0; /* end quote not found */ 247 qn = q2 + 1; 248 } 249 if ((MHD_str_equal_caseless_n_(ptr, 250 key, 251 keylen)) && 252 (eq == &ptr[keylen]) ) 253 { 254 if (NULL == q2) 255 { 256 len = strlen (q1) + 1; 257 if (size > len) 258 size = len; 259 size--; 260 strncpy (dest, 261 q1, 262 size); 263 dest[size] = '\0'; 264 return size; 265 } 266 else 267 { 268 if (size > (size_t) ((q2 - q1) + 1)) 269 size = (q2 - q1) + 1; 270 size--; 271 memcpy (dest, 272 q1, 273 size); 274 dest[size] = '\0'; 275 return size; 276 } 277 } 278 if (NULL == qn) 279 return 0; 280 ptr = strchr (qn, ','); 281 if (NULL == ptr) 282 return 0; 283 ptr++; 284 while (' ' == *ptr) 285 ptr++; 286 } 287 return 0; 288 } 289 290 291 /** 292 * Check nonce-nc map array with either new nonce counter 293 * or a whole new nonce. 294 * 295 * @param connection The MHD connection structure 296 * @param nonce A pointer that referenced a zero-terminated array of nonce 297 * @param nc The nonce counter, zero to add the nonce to the array 298 * @return MHD_YES if successful, MHD_NO if invalid (or we have no NC array) 299 */ 300 static int 301 check_nonce_nc (struct MHD_Connection *connection, 302 const char *nonce, 303 unsigned long int nc) 304 { 305 uint32_t off; 306 uint32_t mod; 307 const char *np; 308 309 mod = connection->daemon->nonce_nc_size; 310 if (0 == mod) 311 return MHD_NO; /* no array! */ 312 /* super-fast xor-based "hash" function for HT lookup in nonce array */ 313 off = 0; 314 np = nonce; 315 while ('\0' != *np) 316 { 317 off = (off << 8) | (*np ^ (off >> 24)); 318 np++; 319 } 320 off = off % mod; 321 /* 322 * Look for the nonce, if it does exist and its corresponding 323 * nonce counter is less than the current nonce counter by 1, 324 * then only increase the nonce counter by one. 325 */ 326 327 (void) MHD_mutex_lock_ (&connection->daemon->nnc_lock); 328 if (0 == nc) 329 { 330 strcpy(connection->daemon->nnc[off].nonce, 331 nonce); 332 connection->daemon->nnc[off].nc = 0; 333 (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock); 334 return MHD_YES; 335 } 336 if ( (nc <= connection->daemon->nnc[off].nc) || 337 (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) ) 338 { 339 (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock); 340 #if HAVE_MESSAGES 341 MHD_DLOG (connection->daemon, 342 "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n"); 343 #endif 344 return MHD_NO; 345 } 346 connection->daemon->nnc[off].nc = nc; 347 (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock); 348 return MHD_YES; 349 } 350 351 352 /** 353 * Get the username from the authorization header sent by the client 354 * 355 * @param connection The MHD connection structure 356 * @return NULL if no username could be found, a pointer 357 * to the username if found 358 * @ingroup authentication 359 */ 360 char * 361 MHD_digest_auth_get_username(struct MHD_Connection *connection) 362 { 363 size_t len; 364 char user[MAX_USERNAME_LENGTH]; 365 const char *header; 366 367 if (NULL == (header = MHD_lookup_connection_value (connection, 368 MHD_HEADER_KIND, 369 MHD_HTTP_HEADER_AUTHORIZATION))) 370 return NULL; 371 if (0 != strncmp (header, _BASE, strlen (_BASE))) 372 return NULL; 373 header += strlen (_BASE); 374 if (0 == (len = lookup_sub_value (user, 375 sizeof (user), 376 header, 377 "username"))) 378 return NULL; 379 return strdup (user); 380 } 381 382 383 /** 384 * Calculate the server nonce so that it mitigates replay attacks 385 * The current format of the nonce is ... 386 * H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp) 387 * 388 * @param nonce_time The amount of time in seconds for a nonce to be invalid 389 * @param method HTTP method 390 * @param rnd A pointer to a character array for the random seed 391 * @param rnd_size The size of the random seed array @a rnd 392 * @param uri HTTP URI (in MHD, without the arguments ("?k=v") 393 * @param realm A string of characters that describes the realm of auth. 394 * @param nonce A pointer to a character array for the nonce to put in 395 */ 396 static void 397 calculate_nonce (uint32_t nonce_time, 398 const char *method, 399 const char *rnd, 400 size_t rnd_size, 401 const char *uri, 402 const char *realm, 403 char *nonce) 404 { 405 struct MD5Context md5; 406 unsigned char timestamp[4]; 407 unsigned char tmpnonce[MD5_DIGEST_SIZE]; 408 char timestamphex[sizeof(timestamp) * 2 + 1]; 409 410 MD5Init (&md5); 411 timestamp[0] = (nonce_time & 0xff000000) >> 0x18; 412 timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10; 413 timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08; 414 timestamp[3] = (nonce_time & 0x000000ff); 415 MD5Update (&md5, timestamp, 4); 416 MD5Update (&md5, ":", 1); 417 MD5Update (&md5, method, strlen (method)); 418 MD5Update (&md5, ":", 1); 419 if (rnd_size > 0) 420 MD5Update (&md5, rnd, rnd_size); 421 MD5Update (&md5, ":", 1); 422 MD5Update (&md5, uri, strlen (uri)); 423 MD5Update (&md5, ":", 1); 424 MD5Update (&md5, realm, strlen (realm)); 425 MD5Final (tmpnonce, &md5); 426 cvthex (tmpnonce, sizeof (tmpnonce), nonce); 427 cvthex (timestamp, 4, timestamphex); 428 strncat (nonce, timestamphex, 8); 429 } 430 431 432 /** 433 * Test if the given key-value pair is in the headers for the 434 * given connection. 435 * 436 * @param connection the connection 437 * @param key the key 438 * @param value the value, can be NULL 439 * @return #MHD_YES if the key-value pair is in the headers, 440 * #MHD_NO if not 441 */ 442 static int 443 test_header (struct MHD_Connection *connection, 444 const char *key, 445 const char *value) 446 { 447 struct MHD_HTTP_Header *pos; 448 449 for (pos = connection->headers_received; NULL != pos; pos = pos->next) 450 { 451 if (MHD_GET_ARGUMENT_KIND != pos->kind) 452 continue; 453 if (0 != strcmp (key, pos->header)) 454 continue; 455 if ( (NULL == value) && 456 (NULL == pos->value) ) 457 return MHD_YES; 458 if ( (NULL == value) || 459 (NULL == pos->value) || 460 (0 != strcmp (value, pos->value)) ) 461 continue; 462 return MHD_YES; 463 } 464 return MHD_NO; 465 } 466 467 468 /** 469 * Check that the arguments given by the client as part 470 * of the authentication header match the arguments we 471 * got as part of the HTTP request URI. 472 * 473 * @param connection connections with headers to compare against 474 * @param args argument URI string (after "?" in URI) 475 * @return MHD_YES if the arguments match, 476 * MHD_NO if not 477 */ 478 static int 479 check_argument_match (struct MHD_Connection *connection, 480 const char *args) 481 { 482 struct MHD_HTTP_Header *pos; 483 char *argb; 484 char *argp; 485 char *equals; 486 char *amper; 487 unsigned int num_headers; 488 489 argb = strdup(args); 490 if (NULL == argb) 491 { 492 #if HAVE_MESSAGES 493 MHD_DLOG(connection->daemon, 494 "Failed to allocate memory for copy of URI arguments\n"); 495 #endif /* HAVE_MESSAGES */ 496 return MHD_NO; 497 } 498 num_headers = 0; 499 argp = argb; 500 while ( (NULL != argp) && 501 ('\0' != argp[0]) ) 502 { 503 equals = strchr (argp, '='); 504 if (NULL == equals) 505 { 506 /* add with 'value' NULL */ 507 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, 508 connection, 509 argp); 510 if (MHD_YES != test_header (connection, argp, NULL)) 511 return MHD_NO; 512 num_headers++; 513 break; 514 } 515 equals[0] = '\0'; 516 equals++; 517 amper = strchr (equals, '&'); 518 if (NULL != amper) 519 { 520 amper[0] = '\0'; 521 amper++; 522 } 523 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, 524 connection, 525 argp); 526 connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls, 527 connection, 528 equals); 529 if (! test_header (connection, argp, equals)) 530 return MHD_NO; 531 num_headers++; 532 argp = amper; 533 } 534 535 /* also check that the number of headers matches */ 536 for (pos = connection->headers_received; NULL != pos; pos = pos->next) 537 { 538 if (MHD_GET_ARGUMENT_KIND != pos->kind) 539 continue; 540 num_headers--; 541 } 542 if (0 != num_headers) 543 return MHD_NO; 544 return MHD_YES; 545 } 546 547 548 /** 549 * Authenticates the authorization header sent by the client 550 * 551 * @param connection The MHD connection structure 552 * @param realm The realm presented to the client 553 * @param username The username needs to be authenticated 554 * @param password The password used in the authentication 555 * @param nonce_timeout The amount of time for a nonce to be 556 * invalid in seconds 557 * @return #MHD_YES if authenticated, #MHD_NO if not, 558 * #MHD_INVALID_NONCE if nonce is invalid 559 * @ingroup authentication 560 */ 561 int 562 MHD_digest_auth_check (struct MHD_Connection *connection, 563 const char *realm, 564 const char *username, 565 const char *password, 566 unsigned int nonce_timeout) 567 { 568 size_t len; 569 const char *header; 570 char *end; 571 char nonce[MAX_NONCE_LENGTH]; 572 char cnonce[MAX_NONCE_LENGTH]; 573 char qop[15]; /* auth,auth-int */ 574 char nc[20]; 575 char response[MAX_AUTH_RESPONSE_LENGTH]; 576 const char *hentity = NULL; /* "auth-int" is not supported */ 577 char ha1[HASH_MD5_HEX_LEN + 1]; 578 char respexp[HASH_MD5_HEX_LEN + 1]; 579 char noncehashexp[HASH_MD5_HEX_LEN + 9]; 580 uint32_t nonce_time; 581 uint32_t t; 582 size_t left; /* number of characters left in 'header' for 'uri' */ 583 unsigned long int nci; 584 585 header = MHD_lookup_connection_value (connection, 586 MHD_HEADER_KIND, 587 MHD_HTTP_HEADER_AUTHORIZATION); 588 if (NULL == header) 589 return MHD_NO; 590 if (0 != strncmp(header, _BASE, strlen(_BASE))) 591 return MHD_NO; 592 header += strlen (_BASE); 593 left = strlen (header); 594 595 { 596 char un[MAX_USERNAME_LENGTH]; 597 598 len = lookup_sub_value (un, 599 sizeof (un), 600 header, "username"); 601 if ( (0 == len) || 602 (0 != strcmp(username, un)) ) 603 return MHD_NO; 604 left -= strlen ("username") + len; 605 } 606 607 { 608 char r[MAX_REALM_LENGTH]; 609 610 len = lookup_sub_value(r, 611 sizeof (r), 612 header, "realm"); 613 if ( (0 == len) || 614 (0 != strcmp(realm, r)) ) 615 return MHD_NO; 616 left -= strlen ("realm") + len; 617 } 618 619 if (0 == (len = lookup_sub_value (nonce, 620 sizeof (nonce), 621 header, "nonce"))) 622 return MHD_NO; 623 left -= strlen ("nonce") + len; 624 if (left > 32 * 1024) 625 { 626 /* we do not permit URIs longer than 32k, as we want to 627 make sure to not blow our stack (or per-connection 628 heap memory limit). Besides, 32k is already insanely 629 large, but of course in theory the 630 #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large 631 and would thus permit sending a >32k authorization 632 header value. */ 633 return MHD_NO; 634 } 635 { 636 char *uri; 637 638 uri = malloc(left + 1); 639 if (NULL == uri) 640 { 641 #if HAVE_MESSAGES 642 MHD_DLOG(connection->daemon, 643 "Failed to allocate memory for auth header processing\n"); 644 #endif /* HAVE_MESSAGES */ 645 return MHD_NO; 646 } 647 if (0 == lookup_sub_value (uri, 648 left + 1, 649 header, "uri")) 650 { 651 free(uri); 652 return MHD_NO; 653 } 654 655 /* 8 = 4 hexadecimal numbers for the timestamp */ 656 nonce_time = strtoul (nonce + len - 8, (char **)NULL, 16); 657 t = (uint32_t) MHD_monotonic_time(); 658 /* 659 * First level vetting for the nonce validity: if the timestamp 660 * attached to the nonce exceeds `nonce_timeout', then the nonce is 661 * invalid. 662 */ 663 if ( (t > nonce_time + nonce_timeout) || 664 (nonce_time + nonce_timeout < nonce_time) ) 665 { 666 free(uri); 667 return MHD_INVALID_NONCE; 668 } 669 if (0 != strncmp (uri, 670 connection->url, 671 strlen (connection->url))) 672 { 673 #if HAVE_MESSAGES 674 MHD_DLOG (connection->daemon, 675 "Authentication failed, URI does not match.\n"); 676 #endif 677 free(uri); 678 return MHD_NO; 679 } 680 { 681 const char *args = strchr (uri, '?'); 682 683 if (NULL == args) 684 args = ""; 685 else 686 args++; 687 if (MHD_YES != 688 check_argument_match (connection, 689 args) ) 690 { 691 #if HAVE_MESSAGES 692 MHD_DLOG (connection->daemon, 693 "Authentication failed, arguments do not match.\n"); 694 #endif 695 free(uri); 696 return MHD_NO; 697 } 698 } 699 calculate_nonce (nonce_time, 700 connection->method, 701 connection->daemon->digest_auth_random, 702 connection->daemon->digest_auth_rand_size, 703 connection->url, 704 realm, 705 noncehashexp); 706 /* 707 * Second level vetting for the nonce validity 708 * if the timestamp attached to the nonce is valid 709 * and possibly fabricated (in case of an attack) 710 * the attacker must also know the random seed to be 711 * able to generate a "sane" nonce, which if he does 712 * not, the nonce fabrication process going to be 713 * very hard to achieve. 714 */ 715 716 if (0 != strcmp (nonce, noncehashexp)) 717 { 718 free(uri); 719 return MHD_INVALID_NONCE; 720 } 721 if ( (0 == lookup_sub_value (cnonce, 722 sizeof (cnonce), 723 header, "cnonce")) || 724 (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) || 725 ( (0 != strcmp (qop, "auth")) && 726 (0 != strcmp (qop, "")) ) || 727 (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) || 728 (0 == lookup_sub_value (response, sizeof (response), header, "response")) ) 729 { 730 #if HAVE_MESSAGES 731 MHD_DLOG (connection->daemon, 732 "Authentication failed, invalid format.\n"); 733 #endif 734 free(uri); 735 return MHD_NO; 736 } 737 nci = strtoul (nc, &end, 16); 738 if ( ('\0' != *end) || 739 ( (LONG_MAX == nci) && 740 (ERANGE == errno) ) ) 741 { 742 #if HAVE_MESSAGES 743 MHD_DLOG (connection->daemon, 744 "Authentication failed, invalid format.\n"); 745 #endif 746 free(uri); 747 return MHD_NO; /* invalid nonce format */ 748 } 749 /* 750 * Checking if that combination of nonce and nc is sound 751 * and not a replay attack attempt. Also adds the nonce 752 * to the nonce-nc map if it does not exist there. 753 */ 754 755 if (MHD_YES != check_nonce_nc (connection, nonce, nci)) 756 { 757 free(uri); 758 return MHD_NO; 759 } 760 761 digest_calc_ha1("md5", 762 username, 763 realm, 764 password, 765 nonce, 766 cnonce, 767 ha1); 768 digest_calc_response (ha1, 769 nonce, 770 nc, 771 cnonce, 772 qop, 773 connection->method, 774 uri, 775 hentity, 776 respexp); 777 free(uri); 778 return (0 == strcmp(response, respexp)) 779 ? MHD_YES 780 : MHD_NO; 781 } 782 } 783 784 785 /** 786 * Queues a response to request authentication from the client 787 * 788 * @param connection The MHD connection structure 789 * @param realm the realm presented to the client 790 * @param opaque string to user for opaque value 791 * @param response reply to send; should contain the "access denied" 792 * body; note that this function will set the "WWW Authenticate" 793 * header and that the caller should not do this 794 * @param signal_stale #MHD_YES if the nonce is invalid to add 795 * 'stale=true' to the authentication header 796 * @return #MHD_YES on success, #MHD_NO otherwise 797 * @ingroup authentication 798 */ 799 int 800 MHD_queue_auth_fail_response (struct MHD_Connection *connection, 801 const char *realm, 802 const char *opaque, 803 struct MHD_Response *response, 804 int signal_stale) 805 { 806 int ret; 807 size_t hlen; 808 char nonce[HASH_MD5_HEX_LEN + 9]; 809 810 /* Generating the server nonce */ 811 calculate_nonce ((uint32_t) MHD_monotonic_time(), 812 connection->method, 813 connection->daemon->digest_auth_random, 814 connection->daemon->digest_auth_rand_size, 815 connection->url, 816 realm, 817 nonce); 818 if (MHD_YES != check_nonce_nc (connection, nonce, 0)) 819 { 820 #if HAVE_MESSAGES 821 MHD_DLOG (connection->daemon, 822 "Could not register nonce (is the nonce array size zero?).\n"); 823 #endif 824 return MHD_NO; 825 } 826 /* Building the authentication header */ 827 hlen = MHD_snprintf_(NULL, 828 0, 829 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s", 830 realm, 831 nonce, 832 opaque, 833 signal_stale 834 ? ",stale=\"true\"" 835 : ""); 836 { 837 char *header; 838 839 header = malloc(hlen + 1); 840 if (NULL == header) 841 { 842 #if HAVE_MESSAGES 843 MHD_DLOG(connection->daemon, 844 "Failed to allocate memory for auth response header\n"); 845 #endif /* HAVE_MESSAGES */ 846 return MHD_NO; 847 } 848 849 MHD_snprintf_(header, 850 hlen + 1, 851 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s", 852 realm, 853 nonce, 854 opaque, 855 signal_stale 856 ? ",stale=\"true\"" 857 : ""); 858 ret = MHD_add_response_header(response, 859 MHD_HTTP_HEADER_WWW_AUTHENTICATE, 860 header); 861 free(header); 862 } 863 if (MHD_YES == ret) 864 ret = MHD_queue_response(connection, 865 MHD_HTTP_UNAUTHORIZED, 866 response); 867 return ret; 868 } 869 870 871 /* end of digestauth.c */ 872