digestauth.c (142330B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2010, 2011, 2012, 2015, 2018 Daniel Pittman and Christian Grothoff 4 Copyright (C) 2014-2024 Evgeny Grin (Karlson2k) 5 6 This library is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 This library is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 You should have received a copy of the GNU Lesser General Public 17 License along with this library; if not, write to the Free Software 18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 19 */ 20 /** 21 * @file digestauth.c 22 * @brief Implements HTTP digest authentication 23 * @author Amr Ali 24 * @author Matthieu Speder 25 * @author Christian Grothoff (RFC 7616 support) 26 * @author Karlson2k (Evgeny Grin) (fixes, new API, improvements, large rewrite, 27 * many RFC 7616 features implementation, 28 * old RFC 2069 support) 29 */ 30 #include "digestauth.h" 31 #include "gen_auth.h" 32 #include "platform.h" 33 #include "mhd_limits.h" 34 #include "internal.h" 35 #include "response.h" 36 #ifdef MHD_MD5_SUPPORT 37 # include "mhd_md5_wrap.h" 38 #endif /* MHD_MD5_SUPPORT */ 39 #ifdef MHD_SHA256_SUPPORT 40 # include "mhd_sha256_wrap.h" 41 #endif /* MHD_SHA256_SUPPORT */ 42 #ifdef MHD_SHA512_256_SUPPORT 43 # include "sha512_256.h" 44 #endif /* MHD_SHA512_256_SUPPORT */ 45 #include "mhd_locks.h" 46 #include "mhd_mono_clock.h" 47 #include "mhd_str.h" 48 #include "mhd_compat.h" 49 #include "mhd_bithelpers.h" 50 #include "mhd_assert.h" 51 52 53 /** 54 * Allow re-use of the nonce-nc map array slot after #REUSE_TIMEOUT seconds, 55 * if this slot is needed for the new nonce, while the old nonce was not used 56 * even one time by the client. 57 * Typically clients immediately use generated nonce for new request. 58 */ 59 #define REUSE_TIMEOUT 30 60 61 /** 62 * The maximum value of artificial timestamp difference to avoid clashes. 63 * The value must be suitable for bitwise AND operation. 64 */ 65 #define DAUTH_JUMPBACK_MAX (0x7F) 66 67 68 /** 69 * 48 bit value in bytes 70 */ 71 #define TIMESTAMP_BIN_SIZE (48 / 8) 72 73 74 /** 75 * Trim value to the TIMESTAMP_BIN_SIZE size 76 */ 77 #define TRIM_TO_TIMESTAMP(value) \ 78 ((value) & ((UINT64_C (1) << (TIMESTAMP_BIN_SIZE * 8)) - 1)) 79 80 81 /** 82 * The printed timestamp size in chars 83 */ 84 #define TIMESTAMP_CHARS_LEN (TIMESTAMP_BIN_SIZE * 2) 85 86 87 /** 88 * Standard server nonce length, not including terminating null, 89 * 90 * @param digest_size digest size 91 */ 92 #define NONCE_STD_LEN(digest_size) \ 93 ((digest_size) * 2 + TIMESTAMP_CHARS_LEN) 94 95 96 #ifdef MHD_SHA512_256_SUPPORT 97 /** 98 * Maximum size of any digest hash supported by MHD. 99 * (SHA-512/256 > MD5). 100 */ 101 #define MAX_DIGEST SHA512_256_DIGEST_SIZE 102 103 /** 104 * The common size of SHA-256 digest and SHA-512/256 digest 105 */ 106 #define SHA256_SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_SIZE 107 #elif defined(MHD_SHA256_SUPPORT) 108 /** 109 * Maximum size of any digest hash supported by MHD. 110 * (SHA-256 > MD5). 111 */ 112 #define MAX_DIGEST SHA256_DIGEST_SIZE 113 114 /** 115 * The common size of SHA-256 digest and SHA-512/256 digest 116 */ 117 #define SHA256_SHA512_256_DIGEST_SIZE SHA256_DIGEST_SIZE 118 #elif defined(MHD_MD5_SUPPORT) 119 /** 120 * Maximum size of any digest hash supported by MHD. 121 */ 122 #define MAX_DIGEST MD5_DIGEST_SIZE 123 #else /* ! MHD_MD5_SUPPORT */ 124 #error At least one hashing algorithm must be enabled 125 #endif /* ! MHD_MD5_SUPPORT */ 126 127 128 /** 129 * Macro to avoid using VLAs if the compiler does not support them. 130 */ 131 #ifndef HAVE_C_VARARRAYS 132 /** 133 * Return #MAX_DIGEST. 134 * 135 * @param n length of the digest to be used for a VLA 136 */ 137 #define VLA_ARRAY_LEN_DIGEST(n) (MAX_DIGEST) 138 139 #else 140 /** 141 * Return @a n. 142 * 143 * @param n length of the digest to be used for a VLA 144 */ 145 #define VLA_ARRAY_LEN_DIGEST(n) (n) 146 #endif 147 148 /** 149 * Check that @a n is below #MAX_DIGEST 150 */ 151 #define VLA_CHECK_LEN_DIGEST(n) \ 152 do { if ((n) > MAX_DIGEST) MHD_PANIC (_ ("VLA too big.\n")); } while (0) 153 154 /** 155 * Maximum length of a username for digest authentication. 156 */ 157 #define MAX_USERNAME_LENGTH 128 158 159 /** 160 * Maximum length of a realm for digest authentication. 161 */ 162 #define MAX_REALM_LENGTH 256 163 164 /** 165 * Maximum length of the response in digest authentication. 166 */ 167 #define MAX_AUTH_RESPONSE_LENGTH (MAX_DIGEST * 2) 168 169 /** 170 * The required prefix of parameter with the extended notation 171 */ 172 #define MHD_DAUTH_EXT_PARAM_PREFIX "UTF-8'" 173 174 /** 175 * The minimal size of the prefix for parameter with the extended notation 176 */ 177 #define MHD_DAUTH_EXT_PARAM_MIN_LEN \ 178 MHD_STATICSTR_LEN_ (MHD_DAUTH_EXT_PARAM_PREFIX "'") 179 180 /** 181 * The result of nonce-nc map array check. 182 */ 183 enum MHD_CheckNonceNC_ 184 { 185 /** 186 * The nonce and NC are OK (valid and NC was not used before). 187 */ 188 MHD_CHECK_NONCENC_OK = MHD_DAUTH_OK, 189 190 /** 191 * The 'nonce' was overwritten with newer 'nonce' in the same slot or 192 * NC was already used. 193 * The validity of the 'nonce' was not be checked. 194 */ 195 MHD_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE, 196 197 /** 198 * The 'nonce' is wrong, it was not generated before. 199 */ 200 MHD_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG 201 }; 202 203 204 /** 205 * Get base hash calculation algorithm from #MHD_DigestAuthAlgo3 value. 206 * @param algo3 the MHD_DigestAuthAlgo3 value 207 * @return the base hash calculation algorithm 208 */ 209 _MHD_static_inline enum MHD_DigestBaseAlgo 210 get_base_digest_algo (enum MHD_DigestAuthAlgo3 algo3) 211 { 212 unsigned int base_algo; 213 214 base_algo = 215 ((unsigned int) algo3) 216 & ~((unsigned int) 217 (MHD_DIGEST_AUTH_ALGO3_NON_SESSION 218 | MHD_DIGEST_AUTH_ALGO3_SESSION)); 219 return (enum MHD_DigestBaseAlgo) base_algo; 220 } 221 222 223 /** 224 * Get digest size for specified algorithm. 225 * 226 * Internal inline version. 227 * @param algo3 the algorithm to check 228 * @return the size of the digest or zero if the input value is not 229 * supported/valid 230 */ 231 _MHD_static_inline size_t 232 digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) 233 { 234 #ifdef MHD_MD5_SUPPORT 235 mhd_assert (MHD_MD5_DIGEST_SIZE == MD5_DIGEST_SIZE); 236 #endif /* MHD_MD5_SUPPORT */ 237 #ifdef MHD_SHA256_SUPPORT 238 mhd_assert (MHD_SHA256_DIGEST_SIZE == SHA256_DIGEST_SIZE); 239 #endif /* MHD_SHA256_SUPPORT */ 240 #ifdef MHD_SHA512_256_SUPPORT 241 mhd_assert (MHD_SHA512_256_DIGEST_SIZE == SHA512_256_DIGEST_SIZE); 242 #ifdef MHD_SHA256_SUPPORT 243 mhd_assert (SHA256_DIGEST_SIZE == SHA512_256_DIGEST_SIZE); 244 #endif /* MHD_SHA256_SUPPORT */ 245 #endif /* MHD_SHA512_256_SUPPORT */ 246 /* Only one algorithm must be specified */ 247 mhd_assert (1 == \ 248 (((0 != (algo3 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0) \ 249 + ((0 != (algo3 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0) \ 250 + ((0 != (algo3 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0))); 251 #ifdef MHD_MD5_SUPPORT 252 if (0 != (((unsigned int) algo3) 253 & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5))) 254 return MHD_MD5_DIGEST_SIZE; 255 else 256 #endif /* MHD_MD5_SUPPORT */ 257 #if defined(MHD_SHA256_SUPPORT) && defined(MHD_SHA512_256_SUPPORT) 258 if (0 != (((unsigned int) algo3) 259 & ( ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256) 260 | ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256)))) 261 return MHD_SHA256_DIGEST_SIZE; /* The same as SHA512_256_DIGEST_SIZE */ 262 else 263 #elif defined(MHD_SHA256_SUPPORT) 264 if (0 != (((unsigned int) algo3) 265 & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256))) 266 return MHD_SHA256_DIGEST_SIZE; 267 else 268 #elif defined(MHD_SHA512_256_SUPPORT) 269 if (0 != (((unsigned int) algo3) 270 & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256))) 271 return MHD_SHA512_256_DIGEST_SIZE; 272 else 273 #endif /* MHD_SHA512_256_SUPPORT */ 274 (void) 0; /* Unsupported algorithm */ 275 276 return 0; /* Wrong input or unsupported algorithm */ 277 } 278 279 280 /** 281 * Get digest size for specified algorithm. 282 * 283 * The size of the digest specifies the size of the userhash, userdigest 284 * and other parameters which size depends on used hash algorithm. 285 * @param algo3 the algorithm to check 286 * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or 287 * #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE) 288 * or zero if the input value is not supported or not valid 289 * @sa #MHD_digest_auth_calc_userdigest() 290 * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex() 291 * @note Available since #MHD_VERSION 0x00097701 292 * @ingroup authentication 293 */ 294 _MHD_EXTERN size_t 295 MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) 296 { 297 return digest_get_hash_size (algo3); 298 } 299 300 301 /** 302 * Digest context data 303 */ 304 union DigestCtx 305 { 306 #ifdef MHD_MD5_SUPPORT 307 struct Md5CtxWr md5_ctx; 308 #endif /* MHD_MD5_SUPPORT */ 309 #ifdef MHD_SHA256_SUPPORT 310 struct Sha256CtxWr sha256_ctx; 311 #endif /* MHD_SHA256_SUPPORT */ 312 #ifdef MHD_SHA512_256_SUPPORT 313 struct Sha512_256Ctx sha512_256_ctx; 314 #endif /* MHD_SHA512_256_SUPPORT */ 315 }; 316 317 /** 318 * The digest calculation structure. 319 */ 320 struct DigestAlgorithm 321 { 322 /** 323 * A context for the digest algorithm, already initialized to be 324 * useful for @e init, @e update and @e digest. 325 */ 326 union DigestCtx ctx; 327 328 /** 329 * The hash calculation algorithm. 330 */ 331 enum MHD_DigestBaseAlgo algo; 332 333 /** 334 * Buffer for hex-print of the final digest. 335 */ 336 #ifdef _DEBUG 337 bool uninitialised; /**< The structure has been not set-up */ 338 bool algo_selected; /**< The algorithm has been selected */ 339 bool ready_for_hashing; /**< The structure is ready to hash data */ 340 bool hashing; /**< Some data has been hashed, but the digest has not finalised yet */ 341 #endif /* _DEBUG */ 342 }; 343 344 345 /** 346 * Return the size of the digest. 347 * @param da the digest calculation structure to identify 348 * @return the size of the digest. 349 */ 350 _MHD_static_inline unsigned int 351 digest_get_size (struct DigestAlgorithm *da) 352 { 353 mhd_assert (! da->uninitialised); 354 mhd_assert (da->algo_selected); 355 #ifdef MHD_MD5_SUPPORT 356 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 357 return MD5_DIGEST_SIZE; 358 #endif /* MHD_MD5_SUPPORT */ 359 #ifdef MHD_SHA256_SUPPORT 360 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 361 return SHA256_DIGEST_SIZE; 362 #endif /* MHD_SHA256_SUPPORT */ 363 #ifdef MHD_SHA512_256_SUPPORT 364 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) 365 return SHA512_256_DIGEST_SIZE; 366 #endif /* MHD_SHA512_256_SUPPORT */ 367 mhd_assert (0); /* May not happen */ 368 return 0; 369 } 370 371 372 #if defined(MHD_MD5_HAS_DEINIT) || defined(MHD_SHA256_HAS_DEINIT) 373 /** 374 * Indicates presence of digest_deinit() function 375 */ 376 #define MHD_DIGEST_HAS_DEINIT 1 377 #endif /* MHD_MD5_HAS_DEINIT || MHD_SHA256_HAS_DEINIT */ 378 379 #ifdef MHD_DIGEST_HAS_DEINIT 380 /** 381 * Zero-initialise digest calculation structure. 382 * 383 * This initialisation is enough to safely call #digest_deinit() only. 384 * To make any real digest calculation, #digest_setup_and_init() must be called. 385 * @param da the digest calculation 386 */ 387 _MHD_static_inline void 388 digest_setup_zero (struct DigestAlgorithm *da) 389 { 390 #ifdef _DEBUG 391 da->uninitialised = false; 392 da->algo_selected = false; 393 da->ready_for_hashing = false; 394 da->hashing = false; 395 #endif /* _DEBUG */ 396 da->algo = MHD_DIGEST_BASE_ALGO_INVALID; 397 } 398 399 400 /** 401 * De-initialise digest calculation structure. 402 * 403 * This function must be called if #digest_setup_and_init() was called for 404 * @a da. 405 * This function must not be called if @a da was not initialised by 406 * #digest_setup_and_init() or by #digest_setup_zero(). 407 * @param da the digest calculation 408 */ 409 _MHD_static_inline void 410 digest_deinit (struct DigestAlgorithm *da) 411 { 412 mhd_assert (! da->uninitialised); 413 #ifdef MHD_MD5_HAS_DEINIT 414 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 415 MHD_MD5_deinit (&da->ctx.md5_ctx); 416 else 417 #endif /* MHD_MD5_HAS_DEINIT */ 418 #ifdef MHD_SHA256_HAS_DEINIT 419 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 420 MHD_SHA256_deinit (&da->ctx.sha256_ctx); 421 else 422 #endif /* MHD_SHA256_HAS_DEINIT */ 423 (void) 0; 424 digest_setup_zero (da); 425 } 426 427 428 #else /* ! MHD_DIGEST_HAS_DEINIT */ 429 #define digest_setup_zero(da) (void)0 430 #define digest_deinit(da) (void)0 431 #endif /* ! MHD_DIGEST_HAS_DEINIT */ 432 433 434 /** 435 * Set-up the digest calculation structure and initialise with initial values. 436 * 437 * If @a da was successfully initialised, #digest_deinit() must be called 438 * after finishing using of the @a da. 439 * 440 * This function must not be called more than once for any @a da. 441 * 442 * @param da the structure to set-up 443 * @param algo the algorithm to use for digest calculation 444 * @return boolean 'true' if successfully set-up, 445 * false otherwise. 446 */ 447 _MHD_static_inline bool 448 digest_init_one_time (struct DigestAlgorithm *da, 449 enum MHD_DigestBaseAlgo algo) 450 { 451 #ifdef _DEBUG 452 da->uninitialised = false; 453 da->algo_selected = false; 454 da->ready_for_hashing = false; 455 da->hashing = false; 456 #endif /* _DEBUG */ 457 #ifdef MHD_MD5_SUPPORT 458 if (MHD_DIGEST_BASE_ALGO_MD5 == algo) 459 { 460 da->algo = MHD_DIGEST_BASE_ALGO_MD5; 461 #ifdef _DEBUG 462 da->algo_selected = true; 463 #endif 464 MHD_MD5_init_one_time (&da->ctx.md5_ctx); 465 #ifdef _DEBUG 466 da->ready_for_hashing = true; 467 #endif 468 return true; 469 } 470 #endif /* MHD_MD5_SUPPORT */ 471 #ifdef MHD_SHA256_SUPPORT 472 if (MHD_DIGEST_BASE_ALGO_SHA256 == algo) 473 { 474 da->algo = MHD_DIGEST_BASE_ALGO_SHA256; 475 #ifdef _DEBUG 476 da->algo_selected = true; 477 #endif 478 MHD_SHA256_init_one_time (&da->ctx.sha256_ctx); 479 #ifdef _DEBUG 480 da->ready_for_hashing = true; 481 #endif 482 return true; 483 } 484 #endif /* MHD_SHA256_SUPPORT */ 485 #ifdef MHD_SHA512_256_SUPPORT 486 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == algo) 487 { 488 da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256; 489 #ifdef _DEBUG 490 da->algo_selected = true; 491 #endif 492 MHD_SHA512_256_init (&da->ctx.sha512_256_ctx); 493 #ifdef _DEBUG 494 da->ready_for_hashing = true; 495 #endif 496 return true; 497 } 498 #endif /* MHD_SHA512_256_SUPPORT */ 499 500 da->algo = MHD_DIGEST_BASE_ALGO_INVALID; 501 return false; /* Unsupported or bad algorithm */ 502 } 503 504 505 /** 506 * Feed digest calculation with more data. 507 * @param da the digest calculation 508 * @param data the data to process 509 * @param length the size of the @a data in bytes 510 */ 511 _MHD_static_inline void 512 digest_update (struct DigestAlgorithm *da, 513 const void *data, 514 size_t length) 515 { 516 mhd_assert (! da->uninitialised); 517 mhd_assert (da->algo_selected); 518 mhd_assert (da->ready_for_hashing); 519 #ifdef MHD_MD5_SUPPORT 520 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 521 MHD_MD5_update (&da->ctx.md5_ctx, (const uint8_t *) data, length); 522 else 523 #endif /* MHD_MD5_SUPPORT */ 524 #ifdef MHD_SHA256_SUPPORT 525 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 526 MHD_SHA256_update (&da->ctx.sha256_ctx, (const uint8_t *) data, length); 527 else 528 #endif /* MHD_SHA256_SUPPORT */ 529 #ifdef MHD_SHA512_256_SUPPORT 530 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) 531 MHD_SHA512_256_update (&da->ctx.sha512_256_ctx, 532 (const uint8_t *) data, length); 533 else 534 #endif /* MHD_SHA512_256_SUPPORT */ 535 mhd_assert (0); /* May not happen */ 536 #ifdef _DEBUG 537 da->hashing = true; 538 #endif 539 } 540 541 542 /** 543 * Feed digest calculation with more data from string. 544 * @param da the digest calculation 545 * @param str the zero-terminated string to process 546 */ 547 _MHD_static_inline void 548 digest_update_str (struct DigestAlgorithm *da, 549 const char *str) 550 { 551 const size_t str_len = strlen (str); 552 digest_update (da, (const uint8_t *) str, str_len); 553 } 554 555 556 /** 557 * Feed digest calculation with single colon ':' character. 558 * @param da the digest calculation 559 * @param str the zero-terminated string to process 560 */ 561 _MHD_static_inline void 562 digest_update_with_colon (struct DigestAlgorithm *da) 563 { 564 static const uint8_t colon = (uint8_t) ':'; 565 digest_update (da, &colon, 1); 566 } 567 568 569 /** 570 * Finally calculate hash (the digest). 571 * @param da the digest calculation 572 * @param[out] digest the pointer to the buffer to put calculated digest, 573 * must be at least digest_get_size(da) bytes large 574 */ 575 _MHD_static_inline void 576 digest_calc_hash (struct DigestAlgorithm *da, uint8_t *digest) 577 { 578 mhd_assert (! da->uninitialised); 579 mhd_assert (da->algo_selected); 580 mhd_assert (da->ready_for_hashing); 581 #ifdef MHD_MD5_SUPPORT 582 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 583 { 584 #ifdef MHD_MD5_HAS_FINISH 585 MHD_MD5_finish (&da->ctx.md5_ctx, digest); 586 #ifdef _DEBUG 587 da->ready_for_hashing = false; 588 #endif /* _DEBUG */ 589 #else /* ! MHD_MD5_HAS_FINISH */ 590 MHD_MD5_finish_reset (&da->ctx.md5_ctx, digest); 591 #ifdef _DEBUG 592 da->ready_for_hashing = true; 593 #endif /* _DEBUG */ 594 #endif /* ! MHD_MD5_HAS_FINISH */ 595 } 596 else 597 #endif /* MHD_MD5_SUPPORT */ 598 #ifdef MHD_SHA256_SUPPORT 599 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 600 { 601 #ifdef MHD_SHA256_HAS_FINISH 602 MHD_SHA256_finish (&da->ctx.sha256_ctx, digest); 603 #ifdef _DEBUG 604 da->ready_for_hashing = false; 605 #endif /* _DEBUG */ 606 #else /* ! MHD_SHA256_HAS_FINISH */ 607 MHD_SHA256_finish_reset (&da->ctx.sha256_ctx, digest); 608 #ifdef _DEBUG 609 da->ready_for_hashing = true; 610 #endif /* _DEBUG */ 611 #endif /* ! MHD_SHA256_HAS_FINISH */ 612 } 613 else 614 #endif /* MHD_SHA256_SUPPORT */ 615 #ifdef MHD_SHA512_256_SUPPORT 616 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) 617 { 618 MHD_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest); 619 #ifdef _DEBUG 620 da->ready_for_hashing = false; 621 #endif /* _DEBUG */ 622 } 623 else 624 #endif /* MHD_SHA512_256_SUPPORT */ 625 mhd_assert (0); /* Should not happen */ 626 #ifdef _DEBUG 627 da->hashing = false; 628 #endif /* _DEBUG */ 629 } 630 631 632 /** 633 * Reset the digest calculation structure. 634 * 635 * @param da the structure to reset 636 */ 637 _MHD_static_inline void 638 digest_reset (struct DigestAlgorithm *da) 639 { 640 mhd_assert (! da->uninitialised); 641 mhd_assert (da->algo_selected); 642 mhd_assert (! da->hashing); 643 #ifdef MHD_MD5_SUPPORT 644 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 645 { 646 #ifdef MHD_MD5_HAS_FINISH 647 mhd_assert (! da->ready_for_hashing); 648 #else /* ! MHD_MD5_HAS_FINISH */ 649 mhd_assert (da->ready_for_hashing); 650 #endif /* ! MHD_MD5_HAS_FINISH */ 651 MHD_MD5_reset (&da->ctx.md5_ctx); 652 #ifdef _DEBUG 653 da->ready_for_hashing = true; 654 #endif /* _DEBUG */ 655 } 656 else 657 #endif /* MHD_MD5_SUPPORT */ 658 #ifdef MHD_SHA256_SUPPORT 659 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 660 { 661 #ifdef MHD_SHA256_HAS_FINISH 662 mhd_assert (! da->ready_for_hashing); 663 #else /* ! MHD_SHA256_HAS_FINISH */ 664 mhd_assert (da->ready_for_hashing); 665 #endif /* ! MHD_SHA256_HAS_FINISH */ 666 MHD_SHA256_reset (&da->ctx.sha256_ctx); 667 #ifdef _DEBUG 668 da->ready_for_hashing = true; 669 #endif /* _DEBUG */ 670 } 671 else 672 #endif /* MHD_SHA256_SUPPORT */ 673 #ifdef MHD_SHA512_256_SUPPORT 674 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) 675 { 676 mhd_assert (! da->ready_for_hashing); 677 MHD_SHA512_256_init (&da->ctx.sha512_256_ctx); 678 #ifdef _DEBUG 679 da->ready_for_hashing = true; 680 #endif 681 } 682 else 683 #endif /* MHD_SHA512_256_SUPPORT */ 684 { 685 #ifdef _DEBUG 686 da->ready_for_hashing = false; 687 #endif 688 mhd_assert (0); /* May not happen, bad algorithm */ 689 } 690 } 691 692 693 #if defined(MHD_MD5_HAS_EXT_ERROR) || defined(MHD_SHA256_HAS_EXT_ERROR) 694 /** 695 * Indicates that digest algorithm has external error status 696 */ 697 #define MHD_DIGEST_HAS_EXT_ERROR 1 698 #endif /* MHD_MD5_HAS_EXT_ERROR || MHD_SHA256_HAS_EXT_ERROR */ 699 700 #ifdef MHD_DIGEST_HAS_EXT_ERROR 701 /** 702 * Get external error code. 703 * 704 * When external digest calculation used, an error may occur during 705 * initialisation or hashing data. This function checks whether external 706 * error has been reported for digest calculation. 707 * @param da the digest calculation 708 * @return true if external error occurs 709 */ 710 _MHD_static_inline bool 711 digest_ext_error (struct DigestAlgorithm *da) 712 { 713 mhd_assert (! da->uninitialised); 714 mhd_assert (da->algo_selected); 715 #ifdef MHD_MD5_HAS_EXT_ERROR 716 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 717 return 0 != da->ctx.md5_ctx.ext_error; 718 #endif /* MHD_MD5_HAS_EXT_ERROR */ 719 #ifdef MHD_SHA256_HAS_EXT_ERROR 720 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 721 return 0 != da->ctx.sha256_ctx.ext_error; 722 #endif /* MHD_MD5_HAS_EXT_ERROR */ 723 return false; 724 } 725 726 727 #else /* ! MHD_DIGEST_HAS_EXT_ERROR */ 728 #define digest_ext_error(da) (false) 729 #endif /* ! MHD_DIGEST_HAS_EXT_ERROR */ 730 731 732 /** 733 * Extract timestamp from the given nonce. 734 * @param nonce the nonce to check 735 * @param noncelen the length of the nonce, zero for autodetect 736 * @param[out] ptimestamp the pointer to store extracted timestamp 737 * @return true if timestamp was extracted, 738 * false if nonce does not have valid timestamp. 739 */ 740 static bool 741 get_nonce_timestamp (const char *const nonce, 742 size_t noncelen, 743 uint64_t *const ptimestamp) 744 { 745 if (0 == noncelen) 746 noncelen = strlen (nonce); 747 748 if (true 749 #ifdef MHD_MD5_SUPPORT 750 && (NONCE_STD_LEN (MD5_DIGEST_SIZE) != noncelen) 751 #endif /* MHD_MD5_SUPPORT */ 752 #if defined(MHD_SHA256_SUPPORT) || defined(MHD_SHA512_256_SUPPORT) 753 && (NONCE_STD_LEN (SHA256_SHA512_256_DIGEST_SIZE) != noncelen) 754 #endif /* MHD_SHA256_SUPPORT */ 755 ) 756 return false; 757 758 if (TIMESTAMP_CHARS_LEN != 759 MHD_strx_to_uint64_n_ (nonce + noncelen - TIMESTAMP_CHARS_LEN, 760 TIMESTAMP_CHARS_LEN, 761 ptimestamp)) 762 return false; 763 return true; 764 } 765 766 767 MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ 768 769 /** 770 * Super-fast xor-based "hash" function 771 * 772 * @param data the data to calculate hash for 773 * @param data_size the size of the data in bytes 774 * @return the "hash" 775 */ 776 static uint32_t 777 fast_simple_hash (const uint8_t *data, 778 size_t data_size) 779 { 780 uint32_t hash; 781 782 if (0 != data_size) 783 { 784 size_t i; 785 hash = data[0]; 786 for (i = 1; i < data_size; i++) 787 hash = _MHD_ROTL32 (hash, 7) ^ data[i]; 788 } 789 else 790 hash = 0; 791 792 return hash; 793 } 794 795 796 MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ 797 798 /** 799 * Get index of the nonce in the nonce-nc map array. 800 * 801 * @param arr_size the size of nonce_nc array 802 * @param nonce the pointer that referenced a zero-terminated array of nonce 803 * @param noncelen the length of @a nonce, in characters 804 * @return #MHD_YES if successful, #MHD_NO if invalid (or we have no NC array) 805 */ 806 static size_t 807 get_nonce_nc_idx (size_t arr_size, 808 const char *nonce, 809 size_t noncelen) 810 { 811 mhd_assert (0 != arr_size); 812 mhd_assert (0 != noncelen); 813 return fast_simple_hash ((const uint8_t *) nonce, noncelen) % arr_size; 814 } 815 816 817 /** 818 * Check nonce-nc map array with the new nonce counter. 819 * 820 * @param connection The MHD connection structure 821 * @param nonce the pointer that referenced hex nonce, does not need to be 822 * zero-terminated 823 * @param noncelen the length of @a nonce, in characters 824 * @param nc The nonce counter 825 * @return #MHD_DAUTH_NONCENC_OK if successful, 826 * #MHD_DAUTH_NONCENC_STALE if nonce is stale (or no nonce-nc array 827 * is available), 828 * #MHD_DAUTH_NONCENC_WRONG if nonce was not recodered in nonce-nc map 829 * array, while it should. 830 */ 831 static enum MHD_CheckNonceNC_ 832 check_nonce_nc (struct MHD_Connection *connection, 833 const char *nonce, 834 size_t noncelen, 835 uint64_t nonce_time, 836 uint64_t nc) 837 { 838 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); 839 struct MHD_NonceNc *nn; 840 uint32_t mod; 841 enum MHD_CheckNonceNC_ ret; 842 843 mhd_assert (0 != noncelen); 844 mhd_assert (0 != nc); 845 if (MAX_DIGEST_NONCE_LENGTH < noncelen) 846 return MHD_CHECK_NONCENC_WRONG; /* This should be impossible, but static analysis 847 tools have a hard time with it *and* this also 848 protects against unsafe modifications that may 849 happen in the future... */ 850 mod = daemon->nonce_nc_size; 851 if (0 == mod) 852 return MHD_CHECK_NONCENC_STALE; /* no array! */ 853 if (nc >= UINT32_MAX - 64) 854 return MHD_CHECK_NONCENC_STALE; /* Overflow, unrealistically high value */ 855 856 nn = &daemon->nnc[get_nonce_nc_idx (mod, nonce, noncelen)]; 857 858 MHD_mutex_lock_chk_ (&daemon->nnc_lock); 859 860 mhd_assert (0 == nn->nonce[noncelen]); /* The old value must be valid */ 861 862 if ( (0 != memcmp (nn->nonce, nonce, noncelen)) || 863 (0 != nn->nonce[noncelen]) ) 864 { /* The nonce in the slot does not match nonce from the client */ 865 if (0 == nn->nonce[0]) 866 { /* The slot was never used, while the client's nonce value should be 867 * recorded when it was generated by MHD */ 868 ret = MHD_CHECK_NONCENC_WRONG; 869 } 870 else if (0 != nn->nonce[noncelen]) 871 { /* The value is the slot is wrong */ 872 ret = MHD_CHECK_NONCENC_STALE; 873 } 874 else 875 { 876 uint64_t slot_ts; /**< The timestamp in the slot */ 877 if (! get_nonce_timestamp (nn->nonce, noncelen, &slot_ts)) 878 { 879 mhd_assert (0); /* The value is the slot is wrong */ 880 ret = MHD_CHECK_NONCENC_STALE; 881 } 882 else 883 { 884 /* Unsigned value, will be large if nonce_time is less than slot_ts */ 885 const uint64_t ts_diff = TRIM_TO_TIMESTAMP (nonce_time - slot_ts); 886 if ((REUSE_TIMEOUT * 1000) >= ts_diff) 887 { 888 /* The nonce from the client may not have been placed in the slot 889 * because another nonce in that slot has not yet expired. */ 890 ret = MHD_CHECK_NONCENC_STALE; 891 } 892 else if (TRIM_TO_TIMESTAMP (UINT64_MAX) / 2 >= ts_diff) 893 { 894 /* Too large value means that nonce_time is less than slot_ts. 895 * The nonce from the client may have been overwritten by the newer 896 * nonce. */ 897 ret = MHD_CHECK_NONCENC_STALE; 898 } 899 else 900 { 901 /* The nonce from the client should be generated after the nonce 902 * in the slot has been expired, the nonce must be recorded, but 903 * it's not. */ 904 ret = MHD_CHECK_NONCENC_WRONG; 905 } 906 } 907 } 908 } 909 else if (nc > nn->nc) 910 { 911 /* 'nc' is larger, shift bitmask and bump limit */ 912 const uint32_t jump_size = (uint32_t) nc - nn->nc; 913 if (64 > jump_size) 914 { 915 /* small jump, less than mask width */ 916 nn->nmask <<= jump_size; 917 /* Set bit for the old 'nc' value */ 918 nn->nmask |= (UINT64_C (1) << (jump_size - 1)); 919 } 920 else if (64 == jump_size) 921 nn->nmask = (UINT64_C (1) << 63); 922 else 923 nn->nmask = 0; /* big jump, unset all bits in the mask */ 924 nn->nc = (uint32_t) nc; 925 ret = MHD_CHECK_NONCENC_OK; 926 } 927 else if (nc < nn->nc) 928 { 929 /* Note that we use 64 here, as we do not store the 930 bit for 'nn->nc' itself in 'nn->nmask' */ 931 if ( (nc + 64 >= nn->nc) && 932 (0 == ((UINT64_C (1) << (nn->nc - nc - 1)) & nn->nmask)) ) 933 { 934 /* Out-of-order nonce, but within 64-bit bitmask, set bit */ 935 nn->nmask |= (UINT64_C (1) << (nn->nc - nc - 1)); 936 ret = MHD_CHECK_NONCENC_OK; 937 } 938 else 939 /* 'nc' was already used or too old (more then 64 values ago) */ 940 ret = MHD_CHECK_NONCENC_STALE; 941 } 942 else /* if (nc == nn->nc) */ 943 /* 'nc' was already used */ 944 ret = MHD_CHECK_NONCENC_STALE; 945 946 MHD_mutex_unlock_chk_ (&daemon->nnc_lock); 947 948 return ret; 949 } 950 951 952 /** 953 * Get username type used by the client. 954 * This function does not check whether userhash can be decoded or 955 * extended notation (if used) is valid. 956 * @param params the Digest Authorization parameters 957 * @return the type of username 958 */ 959 _MHD_static_inline enum MHD_DigestAuthUsernameType 960 get_rq_uname_type (const struct MHD_RqDAuth *params) 961 { 962 if (NULL != params->username.value.str) 963 { 964 if (NULL == params->username_ext.value.str) 965 return params->userhash ? 966 MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH : 967 MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD; 968 else /* Both 'username' and 'username*' are used */ 969 return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 970 } 971 else if (NULL != params->username_ext.value.str) 972 { 973 if (! params->username_ext.quoted && ! params->userhash && 974 (MHD_DAUTH_EXT_PARAM_MIN_LEN <= params->username_ext.value.len) ) 975 return MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED; 976 else 977 return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 978 } 979 980 return MHD_DIGEST_AUTH_UNAME_TYPE_MISSING; 981 } 982 983 984 /** 985 * Get total size required for 'username' and 'userhash_bin' 986 * @param params the Digest Authorization parameters 987 * @param uname_type the type of username 988 * @return the total size required for 'username' and 989 * 'userhash_bin' is userhash is used 990 */ 991 _MHD_static_inline size_t 992 get_rq_unames_size (const struct MHD_RqDAuth *params, 993 enum MHD_DigestAuthUsernameType uname_type) 994 { 995 size_t s; 996 997 mhd_assert (get_rq_uname_type (params) == uname_type); 998 s = 0; 999 if ((MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) || 1000 (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) ) 1001 { 1002 s += params->username.value.len + 1; /* Add one byte for zero-termination */ 1003 if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) 1004 s += (params->username.value.len + 1) / 2; 1005 } 1006 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type) 1007 s += params->username_ext.value.len 1008 - MHD_DAUTH_EXT_PARAM_MIN_LEN + 1; /* Add one byte for zero-termination */ 1009 return s; 1010 } 1011 1012 1013 /** 1014 * Get unquoted version of Digest Authorization parameter. 1015 * This function automatically zero-teminate the result. 1016 * @param param the parameter to extract 1017 * @param[out] buf the output buffer, must be enough size to hold the result, 1018 * the recommended size is 'param->value.len + 1' 1019 * @return the size of the result, not including the terminating zero 1020 */ 1021 static size_t 1022 get_rq_param_unquoted_copy_z (const struct MHD_RqDAuthParam *param, char *buf) 1023 { 1024 size_t len; 1025 mhd_assert (NULL != param->value.str); 1026 if (! param->quoted) 1027 { 1028 memcpy (buf, param->value.str, param->value.len); 1029 buf [param->value.len] = 0; 1030 return param->value.len; 1031 } 1032 1033 len = MHD_str_unquote (param->value.str, param->value.len, buf); 1034 mhd_assert (0 != len); 1035 mhd_assert (len < param->value.len); 1036 buf[len] = 0; 1037 return len; 1038 } 1039 1040 1041 /** 1042 * Get decoded version of username from extended notation. 1043 * This function automatically zero-teminate the result. 1044 * @param uname_ext the string of client's 'username*' parameter value 1045 * @param uname_ext_len the length of @a uname_ext in chars 1046 * @param[out] buf the output buffer to put decoded username value 1047 * @param buf_size the size of @a buf 1048 * @return the number of characters copied to the output buffer or 1049 * -1 if wrong extended notation is used. 1050 */ 1051 static ssize_t 1052 get_rq_extended_uname_copy_z (const char *uname_ext, size_t uname_ext_len, 1053 char *buf, size_t buf_size) 1054 { 1055 size_t r; 1056 size_t w; 1057 if ((size_t) SSIZE_MAX < uname_ext_len) 1058 return -1; /* Too long input string */ 1059 1060 if (MHD_DAUTH_EXT_PARAM_MIN_LEN > uname_ext_len) 1061 return -1; /* Required prefix is missing */ 1062 1063 if (! MHD_str_equal_caseless_bin_n_ (uname_ext, MHD_DAUTH_EXT_PARAM_PREFIX, 1064 MHD_STATICSTR_LEN_ ( \ 1065 MHD_DAUTH_EXT_PARAM_PREFIX))) 1066 return -1; /* Only UTF-8 is supported, as it is implied by RFC 7616 */ 1067 1068 r = MHD_STATICSTR_LEN_ (MHD_DAUTH_EXT_PARAM_PREFIX); 1069 /* Skip language tag */ 1070 while (r < uname_ext_len && '\'' != uname_ext[r]) 1071 { 1072 const char chr = uname_ext[r]; 1073 if ((' ' == chr) || ('\t' == chr) || ('\"' == chr) || (',' == chr) || 1074 (';' == chr) ) 1075 return -1; /* Wrong char in language tag */ 1076 r++; 1077 } 1078 if (r >= uname_ext_len) 1079 return -1; /* The end of the language tag was not found */ 1080 r++; /* Advance to the next char */ 1081 1082 w = MHD_str_pct_decode_strict_n_ (uname_ext + r, uname_ext_len - r, 1083 buf, buf_size); 1084 if ((0 == w) && (0 != uname_ext_len - r)) 1085 return -1; /* Broken percent encoding */ 1086 buf[w] = 0; /* Zero terminate the result */ 1087 mhd_assert (SSIZE_MAX > w); 1088 return (ssize_t) w; 1089 } 1090 1091 1092 /** 1093 * Get copy of username used by the client. 1094 * @param params the Digest Authorization parameters 1095 * @param uname_type the type of username 1096 * @param[out] uname_info the pointer to the structure to be filled 1097 * @param buf the buffer to be used for usernames 1098 * @param buf_size the size of the @a buf 1099 * @return the size of the @a buf used by pointers in @a unames structure 1100 */ 1101 static size_t 1102 get_rq_uname (const struct MHD_RqDAuth *params, 1103 enum MHD_DigestAuthUsernameType uname_type, 1104 struct MHD_DigestAuthUsernameInfo *uname_info, 1105 uint8_t *buf, 1106 size_t buf_size) 1107 { 1108 size_t buf_used; 1109 1110 buf_used = 0; 1111 mhd_assert (get_rq_uname_type (params) == uname_type); 1112 mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type); 1113 mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type); 1114 1115 uname_info->username = NULL; 1116 uname_info->username_len = 0; 1117 uname_info->userhash_hex = NULL; 1118 uname_info->userhash_hex_len = 0; 1119 uname_info->userhash_bin = NULL; 1120 1121 if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) 1122 { 1123 uname_info->username = (char *) (buf + buf_used); 1124 uname_info->username_len = 1125 get_rq_param_unquoted_copy_z (¶ms->username, 1126 uname_info->username); 1127 buf_used += uname_info->username_len + 1; 1128 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD; 1129 } 1130 else if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) 1131 { 1132 size_t res; 1133 1134 uname_info->userhash_hex = (char *) (buf + buf_used); 1135 uname_info->userhash_hex_len = 1136 get_rq_param_unquoted_copy_z (¶ms->username, 1137 uname_info->userhash_hex); 1138 buf_used += uname_info->userhash_hex_len + 1; 1139 uname_info->userhash_bin = (uint8_t *) (buf + buf_used); 1140 res = MHD_hex_to_bin (uname_info->userhash_hex, 1141 uname_info->userhash_hex_len, 1142 uname_info->userhash_bin); 1143 if (res != uname_info->userhash_hex_len / 2) 1144 { 1145 uname_info->userhash_bin = NULL; 1146 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 1147 } 1148 else 1149 { 1150 /* Avoid pointers outside allocated region when the size is zero */ 1151 if (0 == res) 1152 uname_info->userhash_bin = (uint8_t *) uname_info->username; 1153 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH; 1154 buf_used += res; 1155 } 1156 } 1157 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type) 1158 { 1159 ssize_t res; 1160 res = get_rq_extended_uname_copy_z (params->username_ext.value.str, 1161 params->username_ext.value.len, 1162 (char *) (buf + buf_used), 1163 buf_size - buf_used); 1164 if (0 > res) 1165 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 1166 else 1167 { 1168 uname_info->username = (char *) (buf + buf_used); 1169 uname_info->username_len = (size_t) res; 1170 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED; 1171 buf_used += uname_info->username_len + 1; 1172 } 1173 } 1174 else 1175 { 1176 mhd_assert (0); 1177 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 1178 } 1179 mhd_assert (buf_size >= buf_used); 1180 return buf_used; 1181 } 1182 1183 1184 /** 1185 * Result of request's Digest Authorization 'nc' value extraction 1186 */ 1187 enum MHD_GetRqNCResult 1188 { 1189 MHD_GET_RQ_NC_NONE = -1, /**< No 'nc' value */ 1190 MHD_GET_RQ_NC_VALID = 0, /**< Readable 'nc' value */ 1191 MHD_GET_RQ_NC_TOO_LONG = 1, /**< The 'nc' value is too long */ 1192 MHD_GET_RQ_NC_TOO_LARGE = 2,/**< The 'nc' value is too big to fit uint32_t */ 1193 MHD_GET_RQ_NC_BROKEN = 3 /**< The 'nc' value is not a number */ 1194 }; 1195 1196 1197 /** 1198 * Get 'nc' value from request's Authorization header 1199 * @param params the request digest authentication 1200 * @param[out] nc the pointer to put nc value to 1201 * @return enum value indicating the result 1202 */ 1203 static enum MHD_GetRqNCResult 1204 get_rq_nc (const struct MHD_RqDAuth *params, 1205 uint32_t *nc) 1206 { 1207 const struct MHD_RqDAuthParam *const nc_param = 1208 ¶ms->nc; 1209 char unq[16]; 1210 const char *val; 1211 size_t val_len; 1212 size_t res; 1213 uint64_t nc_val; 1214 1215 if (NULL == nc_param->value.str) 1216 return MHD_GET_RQ_NC_NONE; 1217 1218 if (0 == nc_param->value.len) 1219 return MHD_GET_RQ_NC_BROKEN; 1220 1221 if (! nc_param->quoted) 1222 { 1223 val = nc_param->value.str; 1224 val_len = nc_param->value.len; 1225 } 1226 else 1227 { 1228 /* Actually no backslashes must be used in 'nc' */ 1229 if (sizeof(unq) < params->nc.value.len) 1230 return MHD_GET_RQ_NC_TOO_LONG; 1231 val_len = MHD_str_unquote (nc_param->value.str, nc_param->value.len, unq); 1232 if (0 == val_len) 1233 return MHD_GET_RQ_NC_BROKEN; 1234 val = unq; 1235 } 1236 1237 res = MHD_strx_to_uint64_n_ (val, val_len, &nc_val); 1238 if (0 == res) 1239 { 1240 const char f = val[0]; 1241 if ( (('9' >= f) && ('0' <= f)) || 1242 (('F' >= f) && ('A' <= f)) || 1243 (('a' <= f) && ('f' >= f)) ) 1244 return MHD_GET_RQ_NC_TOO_LARGE; 1245 else 1246 return MHD_GET_RQ_NC_BROKEN; 1247 } 1248 if (val_len != res) 1249 return MHD_GET_RQ_NC_BROKEN; 1250 if (UINT32_MAX < nc_val) 1251 return MHD_GET_RQ_NC_TOO_LARGE; 1252 *nc = (uint32_t) nc_val; 1253 return MHD_GET_RQ_NC_VALID; 1254 } 1255 1256 1257 /** 1258 * Get information about Digest Authorization client's header. 1259 * 1260 * @param connection The MHD connection structure 1261 * @return NULL no valid Digest Authorization header is used in the request; 1262 * a pointer structure with information if the valid request header 1263 * found, free using #MHD_free(). 1264 * @note Available since #MHD_VERSION 0x00097701 1265 * @ingroup authentication 1266 */ 1267 _MHD_EXTERN struct MHD_DigestAuthInfo * 1268 MHD_digest_auth_get_request_info3 (struct MHD_Connection *connection) 1269 { 1270 const struct MHD_RqDAuth *params; 1271 struct MHD_DigestAuthInfo *info; 1272 enum MHD_DigestAuthUsernameType uname_type; 1273 size_t unif_buf_size; 1274 uint8_t *unif_buf_ptr; 1275 size_t unif_buf_used; 1276 enum MHD_GetRqNCResult nc_res; 1277 1278 params = MHD_get_rq_dauth_params_ (connection); 1279 if (NULL == params) 1280 return NULL; 1281 1282 unif_buf_size = 0; 1283 1284 uname_type = get_rq_uname_type (params); 1285 1286 unif_buf_size += get_rq_unames_size (params, uname_type); 1287 1288 if (NULL != params->opaque.value.str) 1289 unif_buf_size += params->opaque.value.len + 1; /* Add one for zero-termination */ 1290 if (NULL != params->realm.value.str) 1291 unif_buf_size += params->realm.value.len + 1; /* Add one for zero-termination */ 1292 info = (struct MHD_DigestAuthInfo *) 1293 MHD_calloc_ (1, (sizeof(struct MHD_DigestAuthInfo)) + unif_buf_size); 1294 unif_buf_ptr = (uint8_t *) (info + 1); 1295 unif_buf_used = 0; 1296 1297 info->algo3 = params->algo3; 1298 1299 if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type) && 1300 (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type) ) 1301 unif_buf_used += 1302 get_rq_uname (params, uname_type, 1303 (struct MHD_DigestAuthUsernameInfo *) info, 1304 unif_buf_ptr + unif_buf_used, 1305 unif_buf_size - unif_buf_used); 1306 else 1307 info->uname_type = uname_type; 1308 1309 if (NULL != params->opaque.value.str) 1310 { 1311 info->opaque = (char *) (unif_buf_ptr + unif_buf_used); 1312 info->opaque_len = get_rq_param_unquoted_copy_z (¶ms->opaque, 1313 info->opaque); 1314 unif_buf_used += info->opaque_len + 1; 1315 } 1316 if (NULL != params->realm.value.str) 1317 { 1318 info->realm = (char *) (unif_buf_ptr + unif_buf_used); 1319 info->realm_len = get_rq_param_unquoted_copy_z (¶ms->realm, 1320 info->realm); 1321 unif_buf_used += info->realm_len + 1; 1322 } 1323 1324 mhd_assert (unif_buf_size >= unif_buf_used); 1325 1326 info->qop = params->qop; 1327 1328 if (NULL != params->cnonce.value.str) 1329 info->cnonce_len = params->cnonce.value.len; 1330 else 1331 info->cnonce_len = 0; 1332 1333 nc_res = get_rq_nc (params, &info->nc); 1334 if (MHD_GET_RQ_NC_VALID != nc_res) 1335 info->nc = MHD_DIGEST_AUTH_INVALID_NC_VALUE; 1336 1337 return info; 1338 } 1339 1340 1341 /** 1342 * Get the username from Digest Authorization client's header. 1343 * 1344 * @param connection The MHD connection structure 1345 * @return NULL if no valid Digest Authorization header is used in the request, 1346 * or no username parameter is present in the header, or username is 1347 * provided incorrectly by client (see description for 1348 * #MHD_DIGEST_AUTH_UNAME_TYPE_INVALID); 1349 * a pointer structure with information if the valid request header 1350 * found, free using #MHD_free(). 1351 * @sa MHD_digest_auth_get_request_info3() provides more complete information 1352 * @note Available since #MHD_VERSION 0x00097701 1353 * @ingroup authentication 1354 */ 1355 _MHD_EXTERN struct MHD_DigestAuthUsernameInfo * 1356 MHD_digest_auth_get_username3 (struct MHD_Connection *connection) 1357 { 1358 const struct MHD_RqDAuth *params; 1359 struct MHD_DigestAuthUsernameInfo *uname_info; 1360 enum MHD_DigestAuthUsernameType uname_type; 1361 size_t unif_buf_size; 1362 uint8_t *unif_buf_ptr; 1363 size_t unif_buf_used; 1364 1365 params = MHD_get_rq_dauth_params_ (connection); 1366 if (NULL == params) 1367 return NULL; 1368 1369 uname_type = get_rq_uname_type (params); 1370 if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) || 1371 (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) ) 1372 return NULL; 1373 1374 unif_buf_size = get_rq_unames_size (params, uname_type); 1375 1376 uname_info = (struct MHD_DigestAuthUsernameInfo *) 1377 MHD_calloc_ (1, (sizeof(struct MHD_DigestAuthUsernameInfo)) 1378 + unif_buf_size); 1379 unif_buf_ptr = (uint8_t *) (uname_info + 1); 1380 unif_buf_used = get_rq_uname (params, uname_type, uname_info, unif_buf_ptr, 1381 unif_buf_size); 1382 mhd_assert (unif_buf_size >= unif_buf_used); 1383 (void) unif_buf_used; /* Mute compiler warning on non-debug builds */ 1384 mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_info->uname_type); 1385 1386 if (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_info->uname_type) 1387 { 1388 free (uname_info); 1389 return NULL; 1390 } 1391 mhd_assert (uname_type == uname_info->uname_type); 1392 uname_info->algo3 = params->algo3; 1393 1394 return uname_info; 1395 } 1396 1397 1398 /** 1399 * Get the username from the authorization header sent by the client 1400 * 1401 * This function supports username in standard and extended notations. 1402 * "userhash" is not supported by this function. 1403 * 1404 * @param connection The MHD connection structure 1405 * @return NULL if no username could be found, username provided as 1406 * "userhash", extended notation broken or memory allocation error 1407 * occurs; 1408 * a pointer to the username if found, free using #MHD_free(). 1409 * @warning Returned value must be freed by #MHD_free(). 1410 * @sa #MHD_digest_auth_get_username3() 1411 * @ingroup authentication 1412 */ 1413 _MHD_EXTERN char * 1414 MHD_digest_auth_get_username (struct MHD_Connection *connection) 1415 { 1416 const struct MHD_RqDAuth *params; 1417 char *username; 1418 size_t buf_size; 1419 enum MHD_DigestAuthUsernameType uname_type; 1420 1421 params = MHD_get_rq_dauth_params_ (connection); 1422 if (NULL == params) 1423 return NULL; 1424 1425 uname_type = get_rq_uname_type (params); 1426 1427 if ( (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != uname_type) && 1428 (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != uname_type) ) 1429 return NULL; 1430 1431 buf_size = get_rq_unames_size (params, uname_type); 1432 1433 mhd_assert (0 != buf_size); 1434 1435 username = (char *) MHD_calloc_ (1, buf_size); 1436 if (NULL == username) 1437 return NULL; 1438 1439 if (1) 1440 { 1441 struct MHD_DigestAuthUsernameInfo uname_strct; 1442 size_t used; 1443 1444 memset (&uname_strct, 0, sizeof(uname_strct)); 1445 1446 used = get_rq_uname (params, uname_type, &uname_strct, 1447 (uint8_t *) username, buf_size); 1448 if (uname_type != uname_strct.uname_type) 1449 { /* Broken encoding for extended notation */ 1450 free (username); 1451 return NULL; 1452 } 1453 (void) used; /* Mute compiler warning for non-debug builds */ 1454 mhd_assert (buf_size >= used); 1455 } 1456 1457 return username; 1458 } 1459 1460 1461 /** 1462 * Calculate the server nonce so that it mitigates replay attacks 1463 * The current format of the nonce is ... 1464 * H(timestamp:random data:various parameters) + Hex(timestamp) 1465 * 1466 * @param nonce_time The amount of time in seconds for a nonce to be invalid 1467 * @param mthd_e HTTP method as enum value 1468 * @param method HTTP method as a string 1469 * @param rnd the pointer to a character array for the random seed 1470 * @param rnd_size The size of the random seed array @a rnd 1471 * @param saddr the pointer to the socket address structure 1472 * @param saddr_size the size of the socket address structure @a saddr 1473 * @param uri the HTTP URI (in MHD, without the arguments ("?k=v") 1474 * @param uri_len the length of the @a uri 1475 * @param first_header the pointer to the first request's header 1476 * @param realm A string of characters that describes the realm of auth. 1477 * @param realm_len the length of the @a realm. 1478 * @param bind_options the nonce bind options (#MHD_DAuthBindNonce values). 1479 * @param da digest algorithm to use 1480 * @param[out] nonce the pointer to a character array for the nonce to put in, 1481 * must provide NONCE_STD_LEN(digest_get_size(da)) bytes, 1482 * result is NOT zero-terminated 1483 */ 1484 static void 1485 calculate_nonce (uint64_t nonce_time, 1486 enum MHD_HTTP_Method mthd_e, 1487 const char *method, 1488 const char *rnd, 1489 size_t rnd_size, 1490 const struct sockaddr_storage *saddr, 1491 size_t saddr_size, 1492 const char *uri, 1493 size_t uri_len, 1494 const struct MHD_HTTP_Req_Header *first_header, 1495 const char *realm, 1496 size_t realm_len, 1497 unsigned int bind_options, 1498 struct DigestAlgorithm *da, 1499 char *nonce) 1500 { 1501 mhd_assert (! da->hashing); 1502 if (1) 1503 { 1504 /* Add the timestamp to the hash calculation */ 1505 uint8_t timestamp[TIMESTAMP_BIN_SIZE]; 1506 /* If the nonce_time is milliseconds, then the same 48 bit value will repeat 1507 * every 8 919 years, which is more than enough to mitigate a replay attack */ 1508 #if TIMESTAMP_BIN_SIZE != 6 1509 #error The code needs to be updated here 1510 #endif 1511 timestamp[0] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 0))); 1512 timestamp[1] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 1))); 1513 timestamp[2] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 2))); 1514 timestamp[3] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 3))); 1515 timestamp[4] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 4))); 1516 timestamp[5] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 5))); 1517 MHD_bin_to_hex (timestamp, 1518 sizeof (timestamp), 1519 nonce + digest_get_size (da) * 2); 1520 digest_update (da, 1521 timestamp, 1522 sizeof (timestamp)); 1523 } 1524 if (rnd_size > 0) 1525 { 1526 /* Add the unique random value to the hash calculation */ 1527 digest_update_with_colon (da); 1528 digest_update (da, 1529 rnd, 1530 rnd_size); 1531 } 1532 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) && 1533 (0 != saddr_size) ) 1534 { 1535 /* Add full client address including source port to make unique nonces 1536 * for requests received exactly at the same time */ 1537 digest_update_with_colon (da); 1538 digest_update (da, 1539 saddr, 1540 saddr_size); 1541 } 1542 if ( (0 != (bind_options & MHD_DAUTH_BIND_NONCE_CLIENT_IP)) && 1543 (0 != saddr_size) ) 1544 { 1545 /* Add the client's IP address to the hash calculation */ 1546 digest_update_with_colon (da); 1547 if (AF_INET == saddr->ss_family) 1548 digest_update (da, 1549 &((const struct sockaddr_in *) saddr)->sin_addr, 1550 sizeof(((const struct sockaddr_in *) saddr)->sin_addr)); 1551 #ifdef HAVE_INET6 1552 else if (AF_INET6 == saddr->ss_family) 1553 digest_update (da, 1554 &((const struct sockaddr_in6 *) saddr)->sin6_addr, 1555 sizeof(((const struct sockaddr_in6 *) saddr)->sin6_addr)); 1556 #endif /* HAVE_INET6 */ 1557 } 1558 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) || 1559 (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI))) 1560 { 1561 /* Add the request method to the hash calculation */ 1562 digest_update_with_colon (da); 1563 if (MHD_HTTP_MTHD_OTHER != mthd_e) 1564 { 1565 uint8_t mthd_for_hash; 1566 if (MHD_HTTP_MTHD_HEAD != mthd_e) 1567 mthd_for_hash = (uint8_t) mthd_e; 1568 else /* Treat HEAD method in the same way as GET method */ 1569 mthd_for_hash = (uint8_t) MHD_HTTP_MTHD_GET; 1570 digest_update (da, 1571 &mthd_for_hash, 1572 sizeof(mthd_for_hash)); 1573 } 1574 else 1575 digest_update_str (da, method); 1576 } 1577 1578 if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI)) 1579 { 1580 /* Add the request URI to the hash calculation */ 1581 digest_update_with_colon (da); 1582 1583 digest_update (da, 1584 uri, 1585 uri_len); 1586 } 1587 if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI_PARAMS)) 1588 { 1589 /* Add the request URI parameters to the hash calculation */ 1590 const struct MHD_HTTP_Req_Header *h; 1591 1592 digest_update_with_colon (da); 1593 for (h = first_header; NULL != h; h = h->next) 1594 { 1595 if (MHD_GET_ARGUMENT_KIND != h->kind) 1596 continue; 1597 digest_update (da, "\0", 2); 1598 if (0 != h->header_size) 1599 digest_update (da, h->header, h->header_size); 1600 digest_update (da, "", 1); 1601 if (0 != h->value_size) 1602 digest_update (da, h->value, h->value_size); 1603 } 1604 } 1605 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) || 1606 (0 != (bind_options & MHD_DAUTH_BIND_NONCE_REALM))) 1607 { 1608 /* Add the realm to the hash calculation */ 1609 digest_update_with_colon (da); 1610 digest_update (da, 1611 realm, 1612 realm_len); 1613 } 1614 if (1) 1615 { 1616 uint8_t hash[MAX_DIGEST]; 1617 digest_calc_hash (da, hash); 1618 MHD_bin_to_hex (hash, 1619 digest_get_size (da), 1620 nonce); 1621 } 1622 } 1623 1624 1625 /** 1626 * Check whether it is possible to use slot in nonce-nc map array. 1627 * 1628 * Should be called with mutex held to avoid external modification of 1629 * the slot data. 1630 * 1631 * @param nn the pointer to the nonce-nc slot 1632 * @param now the current time 1633 * @param new_nonce the new nonce supposed to be stored in this slot, 1634 * zero-terminated 1635 * @param new_nonce_len the length of the @a new_nonce in chars, not including 1636 * the terminating zero. 1637 * @return true if the slot can be used to store the new nonce, 1638 * false otherwise. 1639 */ 1640 static bool 1641 is_slot_available (const struct MHD_NonceNc *const nn, 1642 const uint64_t now, 1643 const char *const new_nonce, 1644 size_t new_nonce_len) 1645 { 1646 uint64_t timestamp; 1647 bool timestamp_valid; 1648 mhd_assert (new_nonce_len <= NONCE_STD_LEN (MAX_DIGEST)); 1649 mhd_assert (NONCE_STD_LEN (MAX_DIGEST) <= MAX_DIGEST_NONCE_LENGTH); 1650 if (0 == nn->nonce[0]) 1651 return true; /* The slot is empty */ 1652 1653 if (0 == memcmp (nn->nonce, new_nonce, new_nonce_len)) 1654 { 1655 /* The slot has the same nonce already. This nonce cannot be registered 1656 * again as it would just clear 'nc' usage history. */ 1657 return false; 1658 } 1659 1660 if (0 != nn->nc) 1661 return true; /* Client already used the nonce in this slot at least 1662 one time, re-use the slot */ 1663 1664 /* The nonce must be zero-terminated */ 1665 mhd_assert (0 == nn->nonce[sizeof(nn->nonce) - 1]); 1666 if (0 != nn->nonce[sizeof(nn->nonce) - 1]) 1667 return true; /* Wrong nonce format in the slot */ 1668 1669 timestamp_valid = get_nonce_timestamp (nn->nonce, 0, ×tamp); 1670 mhd_assert (timestamp_valid); 1671 if (! timestamp_valid) 1672 return true; /* Invalid timestamp in nonce-nc, should not be possible */ 1673 1674 if ((REUSE_TIMEOUT * 1000) < TRIM_TO_TIMESTAMP (now - timestamp)) 1675 return true; 1676 1677 return false; 1678 } 1679 1680 1681 /** 1682 * Calculate the server nonce so that it mitigates replay attacks and add 1683 * the new nonce to the nonce-nc map array. 1684 * 1685 * @param connection the MHD connection structure 1686 * @param timestamp the current timestamp 1687 * @param realm the string of characters that describes the realm of auth 1688 * @param realm_len the length of the @a realm 1689 * @param da the digest algorithm to use 1690 * @param[out] nonce the pointer to a character array for the nonce to put in, 1691 * must provide NONCE_STD_LEN(digest_get_size(da)) bytes, 1692 * result is NOT zero-terminated 1693 * @return true if the new nonce has been added to the nonce-nc map array, 1694 * false otherwise. 1695 */ 1696 static bool 1697 calculate_add_nonce (struct MHD_Connection *const connection, 1698 uint64_t timestamp, 1699 const char *realm, 1700 size_t realm_len, 1701 struct DigestAlgorithm *da, 1702 char *nonce) 1703 { 1704 struct MHD_Daemon *const daemon = MHD_get_master (connection->daemon); 1705 struct MHD_NonceNc *nn; 1706 const size_t nonce_size = NONCE_STD_LEN (digest_get_size (da)); 1707 bool ret; 1708 1709 mhd_assert (! da->hashing); 1710 mhd_assert (MAX_DIGEST_NONCE_LENGTH >= nonce_size); 1711 mhd_assert (0 != nonce_size); 1712 1713 calculate_nonce (timestamp, 1714 connection->rq.http_mthd, 1715 connection->rq.method, 1716 daemon->digest_auth_random, 1717 daemon->digest_auth_rand_size, 1718 connection->addr, 1719 (size_t) connection->addr_len, 1720 connection->rq.url, 1721 connection->rq.url_len, 1722 connection->rq.headers_received, 1723 realm, 1724 realm_len, 1725 daemon->dauth_bind_type, 1726 da, 1727 nonce); 1728 1729 #ifdef MHD_DIGEST_HAS_EXT_ERROR 1730 if (digest_ext_error (da)) 1731 return false; 1732 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 1733 1734 if (0 == daemon->nonce_nc_size) 1735 return false; 1736 1737 /* Sanity check for values */ 1738 mhd_assert (MAX_DIGEST_NONCE_LENGTH == NONCE_STD_LEN (MAX_DIGEST)); 1739 1740 nn = daemon->nnc + get_nonce_nc_idx (daemon->nonce_nc_size, 1741 nonce, 1742 nonce_size); 1743 1744 MHD_mutex_lock_chk_ (&daemon->nnc_lock); 1745 if (is_slot_available (nn, timestamp, nonce, nonce_size)) 1746 { 1747 memcpy (nn->nonce, 1748 nonce, 1749 nonce_size); 1750 nn->nonce[nonce_size] = 0; /* With terminating zero */ 1751 nn->nc = 0; 1752 nn->nmask = 0; 1753 ret = true; 1754 } 1755 else 1756 ret = false; 1757 MHD_mutex_unlock_chk_ (&daemon->nnc_lock); 1758 1759 return ret; 1760 } 1761 1762 1763 MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ 1764 1765 /** 1766 * Calculate the server nonce so that it mitigates replay attacks and add 1767 * the new nonce to the nonce-nc map array. 1768 * 1769 * @param connection the MHD connection structure 1770 * @param realm A string of characters that describes the realm of auth. 1771 * @param da digest algorithm to use 1772 * @param[out] nonce the pointer to a character array for the nonce to put in, 1773 * must provide NONCE_STD_LEN(digest_get_size(da)) bytes, 1774 * result is NOT zero-terminated 1775 */ 1776 static bool 1777 calculate_add_nonce_with_retry (struct MHD_Connection *const connection, 1778 const char *realm, 1779 struct DigestAlgorithm *da, 1780 char *nonce) 1781 { 1782 const uint64_t timestamp1 = MHD_monotonic_msec_counter (); 1783 const size_t realm_len = strlen (realm); 1784 mhd_assert (! da->hashing); 1785 1786 #ifdef HAVE_MESSAGES 1787 if (0 == MHD_get_master (connection->daemon)->digest_auth_rand_size) 1788 MHD_DLOG (connection->daemon, 1789 _ ("Random value was not initialised by " \ 1790 "MHD_OPTION_DIGEST_AUTH_RANDOM or " \ 1791 "MHD_OPTION_DIGEST_AUTH_RANDOM_COPY, generated nonces " \ 1792 "are predictable.\n")); 1793 #endif 1794 1795 if (! calculate_add_nonce (connection, timestamp1, realm, realm_len, da, 1796 nonce)) 1797 { 1798 /* Either: 1799 * 1. The same nonce was already generated. If it will be used then one 1800 * of the clients will fail (as no initial 'nc' value could be given to 1801 * the client, the second client which will use 'nc=00000001' will fail). 1802 * 2. Another nonce uses the same slot, and this nonce never has been 1803 * used by the client and this nonce is still fresh enough. 1804 */ 1805 const size_t digest_size = digest_get_size (da); 1806 char nonce2[NONCE_STD_LEN (MAX_DIGEST) + 1]; 1807 uint64_t timestamp2; 1808 #ifdef MHD_DIGEST_HAS_EXT_ERROR 1809 if (digest_ext_error (da)) 1810 return false; /* No need to re-try */ 1811 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 1812 if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) 1813 return false; /* No need to re-try */ 1814 1815 timestamp2 = MHD_monotonic_msec_counter (); 1816 if (timestamp1 == timestamp2) 1817 { 1818 /* The timestamps are equal, need to generate some arbitrary 1819 * difference for nonce. */ 1820 /* As the number is needed only to differentiate clients, weak 1821 * pseudo-random generators could be used. Seeding is not needed. */ 1822 uint64_t base1; 1823 uint32_t base2; 1824 uint16_t base3; 1825 uint8_t base4; 1826 #ifdef HAVE_RANDOM 1827 base1 = ((uint64_t) random ()) ^ UINT64_C (0x54a5acff5be47e63); 1828 base4 = 0xb8; 1829 #elif defined(HAVE_RAND) 1830 base1 = ((uint64_t) rand ()) ^ UINT64_C (0xc4bcf553b12f3965); 1831 base4 = 0x92; 1832 #else 1833 /* Monotonic msec counter alone does not really help here as it is already 1834 known that this value is not unique. */ 1835 base1 = ((uint64_t) (uintptr_t) nonce2) ^ UINT64_C (0xf2e1b21bc6c92655); 1836 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1); 1837 base2 = _MHD_ROTR32 (base2, 4); 1838 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2); 1839 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3); 1840 base1 = ((uint64_t) MHD_monotonic_msec_counter ()) 1841 ^ UINT64_C (0xccab93f72cf5b15); 1842 #endif 1843 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1); 1844 base2 = _MHD_ROTL32 (base2, (((base4 >> 4) ^ base4) % 32)); 1845 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2); 1846 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3); 1847 /* Use up to 127 ms difference */ 1848 timestamp2 -= (base4 & DAUTH_JUMPBACK_MAX); 1849 if (timestamp1 == timestamp2) 1850 timestamp2 -= 2; /* Fallback value */ 1851 } 1852 digest_reset (da); 1853 if (! calculate_add_nonce (connection, timestamp2, realm, realm_len, da, 1854 nonce2)) 1855 { 1856 /* No free slot has been found. Re-tries are expensive, just use 1857 * the generated nonce. As it is not stored in nonce-nc map array, 1858 * the next request of the client will be recognized as valid, but 'stale' 1859 * so client should re-try automatically. */ 1860 return false; 1861 } 1862 memcpy (nonce, nonce2, NONCE_STD_LEN (digest_size)); 1863 } 1864 return true; 1865 } 1866 1867 1868 MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ 1869 1870 /** 1871 * Calculate userdigest, return it as binary data. 1872 * 1873 * It is equal to H(A1) for non-session algorithms. 1874 * 1875 * MHD internal version. 1876 * 1877 * @param da the digest algorithm 1878 * @param username the username to use 1879 * @param username_len the length of the @a username 1880 * @param realm the realm to use 1881 * @param realm_len the length of the @a realm 1882 * @param password the password, must be zero-terminated 1883 * @param[out] ha1_bin the output buffer, must have at least 1884 * #digest_get_size(da) bytes available 1885 */ 1886 _MHD_static_inline void 1887 calc_userdigest (struct DigestAlgorithm *da, 1888 const char *username, const size_t username_len, 1889 const char *realm, const size_t realm_len, 1890 const char *password, 1891 uint8_t *ha1_bin) 1892 { 1893 mhd_assert (! da->hashing); 1894 digest_update (da, username, username_len); 1895 digest_update_with_colon (da); 1896 digest_update (da, realm, realm_len); 1897 digest_update_with_colon (da); 1898 digest_update_str (da, password); 1899 digest_calc_hash (da, ha1_bin); 1900 } 1901 1902 1903 /** 1904 * Calculate userdigest, return it as a binary data. 1905 * 1906 * The "userdigest" is the hash of the "username:realm:password" string. 1907 * 1908 * The "userdigest" can be used to avoid storing the password in clear text 1909 * in database/files 1910 * 1911 * This function is designed to improve security of stored credentials, 1912 * the "userdigest" does not improve security of the authentication process. 1913 * 1914 * The results can be used to store username & userdigest pairs instead of 1915 * username & password pairs. To further improve security, application may 1916 * store username & userhash & userdigest triplets. 1917 * 1918 * @param algo3 the digest algorithm 1919 * @param username the username 1920 * @param realm the realm 1921 * @param password the password 1922 * @param[out] userdigest_bin the output buffer for userdigest; 1923 * if this function succeeds, then this buffer has 1924 * #MHD_digest_get_hash_size(algo3) bytes of 1925 * userdigest upon return 1926 * @param bin_buf_size the size of the @a userdigest_bin buffer, must be 1927 * at least #MHD_digest_get_hash_size(algo3) bytes long 1928 * @return MHD_YES on success, 1929 * MHD_NO if @a userdigest_bin is too small or if @a algo3 algorithm is 1930 * not supported (or external error has occurred, 1931 * see #MHD_FEATURE_EXTERN_HASH). 1932 * @sa #MHD_digest_auth_check_digest3() 1933 * @note Available since #MHD_VERSION 0x00097701 1934 * @ingroup authentication 1935 */ 1936 _MHD_EXTERN enum MHD_Result 1937 MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo3 algo3, 1938 const char *username, 1939 const char *realm, 1940 const char *password, 1941 void *userdigest_bin, 1942 size_t bin_buf_size) 1943 { 1944 struct DigestAlgorithm da; 1945 enum MHD_Result ret; 1946 if (! digest_init_one_time (&da, get_base_digest_algo (algo3))) 1947 return MHD_NO; 1948 1949 if (digest_get_size (&da) > bin_buf_size) 1950 ret = MHD_NO; 1951 else 1952 { 1953 calc_userdigest (&da, 1954 username, 1955 strlen (username), 1956 realm, 1957 strlen (realm), 1958 password, 1959 userdigest_bin); 1960 ret = MHD_YES; 1961 1962 #ifdef MHD_DIGEST_HAS_EXT_ERROR 1963 if (digest_ext_error (&da)) 1964 ret = MHD_NO; 1965 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 1966 } 1967 digest_deinit (&da); 1968 1969 return ret; 1970 } 1971 1972 1973 /** 1974 * Calculate userhash, return it as binary data. 1975 * 1976 * MHD internal version. 1977 * 1978 * @param da the digest algorithm 1979 * @param username the username to use 1980 * @param username_len the length of the @a username 1981 * @param realm the realm to use 1982 * @param realm_len the length of the @a realm 1983 * @param[out] digest_bin the output buffer, must have at least 1984 * #MHD_digest_get_hash_size(algo3) bytes available 1985 */ 1986 _MHD_static_inline void 1987 calc_userhash (struct DigestAlgorithm *da, 1988 const char *username, const size_t username_len, 1989 const char *realm, const size_t realm_len, 1990 uint8_t *digest_bin) 1991 { 1992 mhd_assert (NULL != username); 1993 mhd_assert (! da->hashing); 1994 digest_update (da, username, username_len); 1995 digest_update_with_colon (da); 1996 digest_update (da, realm, realm_len); 1997 digest_calc_hash (da, digest_bin); 1998 } 1999 2000 2001 /** 2002 * Calculate "userhash", return it as binary data. 2003 * 2004 * The "userhash" is the hash of the string "username:realm". 2005 * 2006 * The "userhash" could be used to avoid sending username in cleartext in Digest 2007 * Authorization client's header. 2008 * 2009 * Userhash is not designed to hide the username in local database or files, 2010 * as username in cleartext is required for #MHD_digest_auth_check3() function 2011 * to check the response, but it can be used to hide username in HTTP headers. 2012 * 2013 * This function could be used when the new username is added to the username 2014 * database to save the "userhash" alongside with the username (preferably) or 2015 * when loading list of the usernames to generate the userhash for every loaded 2016 * username (this will cause delays at the start with the long lists). 2017 * 2018 * Once "userhash" is generated it could be used to identify users by clients 2019 * with "userhash" support. 2020 * Avoid repetitive usage of this function for the same username/realm 2021 * combination as it will cause excessive CPU load; save and re-use the result 2022 * instead. 2023 * 2024 * @param algo3 the algorithm for userhash calculations 2025 * @param username the username 2026 * @param realm the realm 2027 * @param[out] userhash_bin the output buffer for userhash as binary data; 2028 * if this function succeeds, then this buffer has 2029 * #MHD_digest_get_hash_size(algo3) bytes of userhash 2030 * upon return 2031 * @param bin_buf_size the size of the @a userhash_bin buffer, must be 2032 * at least #MHD_digest_get_hash_size(algo3) bytes long 2033 * @return MHD_YES on success, 2034 * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is 2035 * not supported (or external error has occurred, 2036 * see #MHD_FEATURE_EXTERN_HASH) 2037 * @sa #MHD_digest_auth_calc_userhash_hex() 2038 * @note Available since #MHD_VERSION 0x00097701 2039 * @ingroup authentication 2040 */ 2041 _MHD_EXTERN enum MHD_Result 2042 MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3, 2043 const char *username, 2044 const char *realm, 2045 void *userhash_bin, 2046 size_t bin_buf_size) 2047 { 2048 struct DigestAlgorithm da; 2049 enum MHD_Result ret; 2050 2051 if (! digest_init_one_time (&da, get_base_digest_algo (algo3))) 2052 return MHD_NO; 2053 if (digest_get_size (&da) > bin_buf_size) 2054 ret = MHD_NO; 2055 else 2056 { 2057 calc_userhash (&da, 2058 username, 2059 strlen (username), 2060 realm, 2061 strlen (realm), 2062 userhash_bin); 2063 ret = MHD_YES; 2064 2065 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2066 if (digest_ext_error (&da)) 2067 ret = MHD_NO; 2068 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2069 } 2070 digest_deinit (&da); 2071 2072 return ret; 2073 } 2074 2075 2076 /** 2077 * Calculate "userhash", return it as hexadecimal string. 2078 * 2079 * The "userhash" is the hash of the string "username:realm". 2080 * 2081 * The "userhash" could be used to avoid sending username in cleartext in Digest 2082 * Authorization client's header. 2083 * 2084 * Userhash is not designed to hide the username in local database or files, 2085 * as username in cleartext is required for #MHD_digest_auth_check3() function 2086 * to check the response, but it can be used to hide username in HTTP headers. 2087 * 2088 * This function could be used when the new username is added to the username 2089 * database to save the "userhash" alongside with the username (preferably) or 2090 * when loading list of the usernames to generate the userhash for every loaded 2091 * username (this will cause delays at the start with the long lists). 2092 * 2093 * Once "userhash" is generated it could be used to identify users by clients 2094 * with "userhash" support. 2095 * Avoid repetitive usage of this function for the same username/realm 2096 * combination as it will cause excessive CPU load; save and re-use the result 2097 * instead. 2098 * 2099 * @param algo3 the algorithm for userhash calculations 2100 * @param username the username 2101 * @param realm the realm 2102 * @param[out] userhash_hex the output buffer for userhash as hex string; 2103 * if this function succeeds, then this buffer has 2104 * #MHD_digest_get_hash_size(algo3)*2 chars long 2105 * userhash zero-terminated string 2106 * @param bin_buf_size the size of the @a userhash_bin buffer, must be 2107 * at least #MHD_digest_get_hash_size(algo3)*2+1 chars long 2108 * @return MHD_YES on success, 2109 * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is 2110 * not supported (or external error has occurred, 2111 * see #MHD_FEATURE_EXTERN_HASH). 2112 * @sa #MHD_digest_auth_calc_userhash() 2113 * @note Available since #MHD_VERSION 0x00097701 2114 * @ingroup authentication 2115 */ 2116 _MHD_EXTERN enum MHD_Result 2117 MHD_digest_auth_calc_userhash_hex (enum MHD_DigestAuthAlgo3 algo3, 2118 const char *username, 2119 const char *realm, 2120 char *userhash_hex, 2121 size_t hex_buf_size) 2122 { 2123 uint8_t userhash_bin[MAX_DIGEST]; 2124 size_t digest_size; 2125 2126 digest_size = digest_get_hash_size (algo3); 2127 if (digest_size * 2 + 1 > hex_buf_size) 2128 return MHD_NO; 2129 if (MHD_NO == MHD_digest_auth_calc_userhash (algo3, username, realm, 2130 userhash_bin, MAX_DIGEST)) 2131 return MHD_NO; 2132 2133 MHD_bin_to_hex_z (userhash_bin, digest_size, userhash_hex); 2134 return MHD_YES; 2135 } 2136 2137 2138 struct test_header_param 2139 { 2140 struct MHD_Connection *connection; 2141 size_t num_headers; 2142 }; 2143 2144 /** 2145 * Test if the given key-value pair is in the headers for the 2146 * given connection. 2147 * 2148 * @param cls the test context 2149 * @param key the key 2150 * @param key_size number of bytes in @a key 2151 * @param value the value, can be NULL 2152 * @param value_size number of bytes in @a value 2153 * @param kind type of the header 2154 * @return #MHD_YES if the key-value pair is in the headers, 2155 * #MHD_NO if not 2156 */ 2157 static enum MHD_Result 2158 test_header (void *cls, 2159 const char *key, 2160 size_t key_size, 2161 const char *value, 2162 size_t value_size, 2163 enum MHD_ValueKind kind) 2164 { 2165 struct test_header_param *const param = (struct test_header_param *) cls; 2166 struct MHD_Connection *connection = param->connection; 2167 struct MHD_HTTP_Req_Header *pos; 2168 size_t i; 2169 2170 param->num_headers++; 2171 i = 0; 2172 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next) 2173 { 2174 if (kind != pos->kind) 2175 continue; 2176 if (++i == param->num_headers) 2177 { 2178 if (key_size != pos->header_size) 2179 return MHD_NO; 2180 if (value_size != pos->value_size) 2181 return MHD_NO; 2182 if (0 != key_size) 2183 { 2184 mhd_assert (NULL != key); 2185 mhd_assert (NULL != pos->header); 2186 if (0 != memcmp (key, 2187 pos->header, 2188 key_size)) 2189 return MHD_NO; 2190 } 2191 if (0 != value_size) 2192 { 2193 mhd_assert (NULL != value); 2194 mhd_assert (NULL != pos->value); 2195 if (0 != memcmp (value, 2196 pos->value, 2197 value_size)) 2198 return MHD_NO; 2199 } 2200 return MHD_YES; 2201 } 2202 } 2203 return MHD_NO; 2204 } 2205 2206 2207 /** 2208 * Check that the arguments given by the client as part 2209 * of the authentication header match the arguments we 2210 * got as part of the HTTP request URI. 2211 * 2212 * @param connection connections with headers to compare against 2213 * @param args the copy of argument URI string (after "?" in URI), will be 2214 * modified by this function 2215 * @return boolean true if the arguments match, 2216 * boolean false if not 2217 */ 2218 static bool 2219 check_argument_match (struct MHD_Connection *connection, 2220 char *args) 2221 { 2222 struct MHD_HTTP_Req_Header *pos; 2223 enum MHD_Result ret; 2224 struct test_header_param param; 2225 2226 param.connection = connection; 2227 param.num_headers = 0; 2228 ret = MHD_parse_arguments_ (connection, 2229 MHD_GET_ARGUMENT_KIND, 2230 args, 2231 &test_header, 2232 ¶m); 2233 if (MHD_NO == ret) 2234 { 2235 return false; 2236 } 2237 /* also check that the number of headers matches */ 2238 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next) 2239 { 2240 if (MHD_GET_ARGUMENT_KIND != pos->kind) 2241 continue; 2242 param.num_headers--; 2243 } 2244 if (0 != param.num_headers) 2245 { 2246 /* argument count mismatch */ 2247 return false; 2248 } 2249 return true; 2250 } 2251 2252 2253 /** 2254 * Check that the URI provided by the client as part 2255 * of the authentication header match the real HTTP request URI. 2256 * 2257 * @param connection connections with headers to compare against 2258 * @param uri the copy of URI in the authentication header, should point to 2259 * modifiable buffer at least @a uri_len + 1 characters long, 2260 * will be modified by this function, not valid upon return 2261 * @param uri_len the length of the @a uri string in characters 2262 * @return boolean true if the URIs match, 2263 * boolean false if not 2264 */ 2265 static bool 2266 check_uri_match (struct MHD_Connection *connection, char *uri, size_t uri_len) 2267 { 2268 char *qmark; 2269 char *args; 2270 struct MHD_Daemon *const daemon = connection->daemon; 2271 2272 uri[uri_len] = 0; 2273 qmark = memchr (uri, 2274 '?', 2275 uri_len); 2276 if (NULL != qmark) 2277 *qmark = '\0'; 2278 2279 /* Need to unescape URI before comparing with connection->url */ 2280 uri_len = daemon->unescape_callback (daemon->unescape_callback_cls, 2281 connection, 2282 uri); 2283 if ((uri_len != connection->rq.url_len) || 2284 (0 != memcmp (uri, connection->rq.url, uri_len))) 2285 { 2286 #ifdef HAVE_MESSAGES 2287 MHD_DLOG (daemon, 2288 _ ("Authentication failed, URI does not match.\n")); 2289 #endif 2290 return false; 2291 } 2292 2293 args = (NULL != qmark) ? (qmark + 1) : uri + uri_len; 2294 2295 if (! check_argument_match (connection, 2296 args) ) 2297 { 2298 #ifdef HAVE_MESSAGES 2299 MHD_DLOG (daemon, 2300 _ ("Authentication failed, arguments do not match.\n")); 2301 #endif 2302 return false; 2303 } 2304 return true; 2305 } 2306 2307 2308 /** 2309 * The size of the unquoting buffer in stack 2310 */ 2311 #define _MHD_STATIC_UNQ_BUFFER_SIZE 128 2312 2313 2314 /** 2315 * Get the pointer to buffer with required size 2316 * @param tmp1 the first buffer with fixed size 2317 * @param ptmp2 the pointer to pointer to malloc'ed buffer 2318 * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2319 * @param required_size the required size in buffer 2320 * @return the pointer to the buffer or NULL if failed to allocate buffer with 2321 * requested size 2322 */ 2323 static char * 2324 get_buffer_for_size (char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], 2325 char **ptmp2, 2326 size_t *ptmp2_size, 2327 size_t required_size) 2328 { 2329 mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); 2330 mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); 2331 mhd_assert ((0 == *ptmp2_size) || \ 2332 (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); 2333 2334 if (required_size <= _MHD_STATIC_UNQ_BUFFER_SIZE) 2335 return tmp1; 2336 2337 if (required_size <= *ptmp2_size) 2338 return *ptmp2; 2339 2340 if (required_size > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) 2341 return NULL; 2342 if (NULL != *ptmp2) 2343 free (*ptmp2); 2344 *ptmp2 = (char *) malloc (required_size); 2345 if (NULL == *ptmp2) 2346 *ptmp2_size = 0; 2347 else 2348 *ptmp2_size = required_size; 2349 return *ptmp2; 2350 } 2351 2352 2353 /** 2354 * The result of parameter unquoting 2355 */ 2356 enum _MHD_GetUnqResult 2357 { 2358 _MHD_UNQ_OK = 0, /**< Got unquoted string */ 2359 _MHD_UNQ_TOO_LARGE = -7, /**< The string is too large to unquote */ 2360 _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */ 2361 }; 2362 2363 /** 2364 * Get Digest authorisation parameter as unquoted string. 2365 * @param param the parameter to process 2366 * @param tmp1 the small buffer in stack 2367 * @param ptmp2 the pointer to pointer to malloc'ed buffer 2368 * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2369 * @param[out] unquoted the pointer to store the result, NOT zero terminated 2370 * @return enum code indicating result of the process 2371 */ 2372 static enum _MHD_GetUnqResult 2373 get_unquoted_param (const struct MHD_RqDAuthParam *param, 2374 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], 2375 char **ptmp2, 2376 size_t *ptmp2_size, 2377 struct _MHD_str_w_len *unquoted) 2378 { 2379 char *str; 2380 size_t len; 2381 mhd_assert (NULL != param->value.str); 2382 mhd_assert (0 != param->value.len); 2383 2384 if (! param->quoted) 2385 { 2386 unquoted->str = param->value.str; 2387 unquoted->len = param->value.len; 2388 return _MHD_UNQ_OK; 2389 } 2390 /* The value is present and is quoted, needs to be copied and unquoted */ 2391 str = get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len); 2392 if (NULL == str) 2393 return (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? 2394 _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; 2395 2396 len = MHD_str_unquote (param->value.str, param->value.len, str); 2397 unquoted->str = str; 2398 unquoted->len = len; 2399 mhd_assert (0 != unquoted->len); 2400 mhd_assert (unquoted->len < param->value.len); 2401 return _MHD_UNQ_OK; 2402 } 2403 2404 2405 /** 2406 * Get copy of Digest authorisation parameter as unquoted string. 2407 * @param param the parameter to process 2408 * @param tmp1 the small buffer in stack 2409 * @param ptmp2 the pointer to pointer to malloc'ed buffer 2410 * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2411 * @param[out] unquoted the pointer to store the result, NOT zero terminated, 2412 * but with enough space to zero-terminate 2413 * @return enum code indicating result of the process 2414 */ 2415 static enum _MHD_GetUnqResult 2416 get_unquoted_param_copy (const struct MHD_RqDAuthParam *param, 2417 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], 2418 char **ptmp2, 2419 size_t *ptmp2_size, 2420 struct _MHD_mstr_w_len *unquoted) 2421 { 2422 mhd_assert (NULL != param->value.str); 2423 mhd_assert (0 != param->value.len); 2424 2425 /* The value is present and is quoted, needs to be copied and unquoted */ 2426 /* Allocate buffer with one more additional byte for zero-termination */ 2427 unquoted->str = 2428 get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len + 1); 2429 2430 if (NULL == unquoted->str) 2431 return (param->value.len + 1 > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? 2432 _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; 2433 2434 if (! param->quoted) 2435 { 2436 memcpy (unquoted->str, param->value.str, param->value.len); 2437 unquoted->len = param->value.len; 2438 return _MHD_UNQ_OK; 2439 } 2440 2441 unquoted->len = 2442 MHD_str_unquote (param->value.str, param->value.len, unquoted->str); 2443 mhd_assert (0 != unquoted->len); 2444 mhd_assert (unquoted->len < param->value.len); 2445 return _MHD_UNQ_OK; 2446 } 2447 2448 2449 /** 2450 * Check whether Digest Auth request parameter is equal to given string 2451 * @param param the parameter to check 2452 * @param str the string to compare with, does not need to be zero-terminated 2453 * @param str_len the length of the @a str 2454 * @return true is parameter is equal to the given string, 2455 * false otherwise 2456 */ 2457 _MHD_static_inline bool 2458 is_param_equal (const struct MHD_RqDAuthParam *param, 2459 const char *const str, 2460 const size_t str_len) 2461 { 2462 mhd_assert (NULL != param->value.str); 2463 mhd_assert (0 != param->value.len); 2464 if (param->quoted) 2465 return MHD_str_equal_quoted_bin_n (param->value.str, param->value.len, 2466 str, str_len); 2467 return (str_len == param->value.len) && 2468 (0 == memcmp (str, param->value.str, str_len)); 2469 2470 } 2471 2472 2473 /** 2474 * Check whether Digest Auth request parameter is caseless equal to given string 2475 * @param param the parameter to check 2476 * @param str the string to compare with, does not need to be zero-terminated 2477 * @param str_len the length of the @a str 2478 * @return true is parameter is caseless equal to the given string, 2479 * false otherwise 2480 */ 2481 _MHD_static_inline bool 2482 is_param_equal_caseless (const struct MHD_RqDAuthParam *param, 2483 const char *const str, 2484 const size_t str_len) 2485 { 2486 mhd_assert (NULL != param->value.str); 2487 mhd_assert (0 != param->value.len); 2488 if (param->quoted) 2489 return MHD_str_equal_quoted_bin_n (param->value.str, param->value.len, 2490 str, str_len); 2491 return (str_len == param->value.len) && 2492 (0 == memcmp (str, param->value.str, str_len)); 2493 2494 } 2495 2496 2497 /** 2498 * Authenticates the authorization header sent by the client 2499 * 2500 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 2501 * @a mqop and the client uses this mode, then server generated nonces are 2502 * used as one-time nonces because nonce-count is not supported in this old RFC. 2503 * Communication in this mode is very inefficient, especially if the client 2504 * requests several resources one-by-one as for every request new nonce must be 2505 * generated and client repeat all requests twice (the first time to get a new 2506 * nonce and the second time to perform an authorised request). 2507 * 2508 * @param connection the MHD connection structure 2509 * @param realm the realm for authorization of the client 2510 * @param username the username to be authenticated, must be in clear text 2511 * even if userhash is used by the client 2512 * @param password the password used in the authentication, 2513 * must be NULL if @a userdigest is not NULL 2514 * @param userdigest the precalculated binary hash of the string 2515 * "username:realm:password", 2516 * must be NULL if @a password is not NULL 2517 * @param nonce_timeout the period of seconds since nonce generation, when 2518 * the nonce is recognised as valid and not stale; 2519 * unlike #digest_auth_check_all() zero is used literally 2520 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 2521 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 2522 * returned; 2523 * unlike #digest_auth_check_all() zero is treated as "no limit" 2524 * @param mqop the QOP to use 2525 * @param malgo3 digest algorithms allowed to use, fail if algorithm specified 2526 * by the client is not allowed by this parameter 2527 * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, 2528 * to be freed if not NULL upon return 2529 * @return #MHD_DAUTH_OK if authenticated, 2530 * error code otherwise. 2531 * @ingroup authentication 2532 */ 2533 static enum MHD_DigestAuthResult 2534 digest_auth_check_all_inner (struct MHD_Connection *connection, 2535 const char *realm, 2536 const char *username, 2537 const char *password, 2538 const uint8_t *userdigest, 2539 unsigned int nonce_timeout, 2540 uint32_t max_nc, 2541 enum MHD_DigestAuthMultiQOP mqop, 2542 enum MHD_DigestAuthMultiAlgo3 malgo3, 2543 char **pbuf, 2544 struct DigestAlgorithm *da) 2545 { 2546 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); 2547 enum MHD_DigestAuthAlgo3 c_algo; /**< Client's algorithm */ 2548 enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */ 2549 unsigned int digest_size; 2550 uint8_t hash1_bin[MAX_DIGEST]; 2551 uint8_t hash2_bin[MAX_DIGEST]; 2552 #if 0 2553 const char *hentity = NULL; /* "auth-int" is not supported */ 2554 #endif 2555 uint64_t nonce_time; 2556 uint64_t nci; 2557 const struct MHD_RqDAuth *params; 2558 /** 2559 * Temporal buffer in stack for unquoting and other needs 2560 */ 2561 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE]; 2562 char **const ptmp2 = pbuf; /**< Temporal malloc'ed buffer for unquoting */ 2563 size_t tmp2_size; /**< The size of @a tmp2 buffer */ 2564 struct _MHD_str_w_len unquoted; 2565 struct _MHD_mstr_w_len unq_copy; 2566 enum _MHD_GetUnqResult unq_res; 2567 size_t username_len; 2568 size_t realm_len; 2569 2570 mhd_assert ((NULL != password) || (NULL != userdigest)); 2571 mhd_assert (! ((NULL != userdigest) && (NULL != password))); 2572 2573 tmp2_size = 0; 2574 2575 params = MHD_get_rq_dauth_params_ (connection); 2576 if (NULL == params) 2577 return MHD_DAUTH_WRONG_HEADER; 2578 2579 /* ** Initial parameters checks and setup ** */ 2580 /* Get client's algorithm */ 2581 c_algo = params->algo3; 2582 /* Check whether client's algorithm is allowed by function parameter */ 2583 if (((unsigned int) c_algo) != 2584 (((unsigned int) c_algo) & ((unsigned int) malgo3))) 2585 return MHD_DAUTH_WRONG_ALGO; 2586 /* Check whether client's algorithm is supported */ 2587 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_AUTH_ALGO3_SESSION)) 2588 { 2589 #ifdef HAVE_MESSAGES 2590 MHD_DLOG (connection->daemon, 2591 _ ("The 'session' algorithms are not supported.\n")); 2592 #endif /* HAVE_MESSAGES */ 2593 return MHD_DAUTH_WRONG_ALGO; 2594 } 2595 #ifndef MHD_MD5_SUPPORT 2596 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5)) 2597 { 2598 #ifdef HAVE_MESSAGES 2599 MHD_DLOG (connection->daemon, 2600 _ ("The MD5 algorithm is not supported by this MHD build.\n")); 2601 #endif /* HAVE_MESSAGES */ 2602 return MHD_DAUTH_WRONG_ALGO; 2603 } 2604 #endif /* ! MHD_MD5_SUPPORT */ 2605 #ifndef MHD_SHA256_SUPPORT 2606 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA256)) 2607 { 2608 #ifdef HAVE_MESSAGES 2609 MHD_DLOG (connection->daemon, 2610 _ ("The SHA-256 algorithm is not supported by " 2611 "this MHD build.\n")); 2612 #endif /* HAVE_MESSAGES */ 2613 return MHD_DAUTH_WRONG_ALGO; 2614 } 2615 #endif /* ! MHD_SHA256_SUPPORT */ 2616 #ifndef MHD_SHA512_256_SUPPORT 2617 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 2618 { 2619 #ifdef HAVE_MESSAGES 2620 MHD_DLOG (connection->daemon, 2621 _ ("The SHA-512/256 algorithm is not supported by " 2622 "this MHD build.\n")); 2623 #endif /* HAVE_MESSAGES */ 2624 return MHD_DAUTH_WRONG_ALGO; 2625 } 2626 #endif /* ! MHD_SHA512_256_SUPPORT */ 2627 if (! digest_init_one_time (da, get_base_digest_algo (c_algo))) 2628 MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); 2629 /* Check 'mqop' value */ 2630 c_qop = params->qop; 2631 /* Check whether client's QOP is allowed by function parameter */ 2632 if (((unsigned int) c_qop) != 2633 (((unsigned int) c_qop) & ((unsigned int) mqop))) 2634 return MHD_DAUTH_WRONG_QOP; 2635 if (0 != (((unsigned int) c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT)) 2636 { 2637 #ifdef HAVE_MESSAGES 2638 MHD_DLOG (connection->daemon, 2639 _ ("The 'auth-int' QOP is not supported.\n")); 2640 #endif /* HAVE_MESSAGES */ 2641 return MHD_DAUTH_WRONG_QOP; 2642 } 2643 #ifdef HAVE_MESSAGES 2644 if ((MHD_DIGEST_AUTH_QOP_NONE == c_qop) && 2645 (0 == (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5))) 2646 MHD_DLOG (connection->daemon, 2647 _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \ 2648 "non-standard extension.\n")); 2649 #endif /* HAVE_MESSAGES */ 2650 2651 digest_size = digest_get_size (da); 2652 2653 /* ** A quick check for presence of all required parameters ** */ 2654 2655 if ((NULL == params->username.value.str) && 2656 (NULL == params->username_ext.value.str)) 2657 return MHD_DAUTH_WRONG_USERNAME; 2658 else if ((NULL != params->username.value.str) && 2659 (NULL != params->username_ext.value.str)) 2660 return MHD_DAUTH_WRONG_USERNAME; /* Parameters cannot be used together */ 2661 else if ((NULL != params->username_ext.value.str) && 2662 (MHD_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len)) 2663 return MHD_DAUTH_WRONG_USERNAME; /* Broken extended notation */ 2664 else if (params->userhash && (NULL == params->username.value.str)) 2665 return MHD_DAUTH_WRONG_USERNAME; /* Userhash cannot be used with extended notation */ 2666 else if (params->userhash && (digest_size * 2 > params->username.value.len)) 2667 return MHD_DAUTH_WRONG_USERNAME; /* Too few chars for correct userhash */ 2668 else if (params->userhash && (digest_size * 4 < params->username.value.len)) 2669 return MHD_DAUTH_WRONG_USERNAME; /* Too many chars for correct userhash */ 2670 2671 if (NULL == params->realm.value.str) 2672 return MHD_DAUTH_WRONG_REALM; 2673 else if (((NULL == userdigest) || params->userhash) && 2674 (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len)) 2675 return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */ 2676 2677 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2678 { 2679 if (NULL == params->nc.value.str) 2680 return MHD_DAUTH_WRONG_HEADER; 2681 else if (0 == params->nc.value.len) 2682 return MHD_DAUTH_WRONG_HEADER; 2683 else if (4 * 8 < params->nc.value.len) /* Four times more than needed */ 2684 return MHD_DAUTH_WRONG_HEADER; 2685 2686 if (NULL == params->cnonce.value.str) 2687 return MHD_DAUTH_WRONG_HEADER; 2688 else if (0 == params->cnonce.value.len) 2689 return MHD_DAUTH_WRONG_HEADER; 2690 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len) 2691 return MHD_DAUTH_TOO_LARGE; 2692 } 2693 2694 /* The QOP parameter was checked already */ 2695 2696 if (NULL == params->uri.value.str) 2697 return MHD_DAUTH_WRONG_URI; 2698 else if (0 == params->uri.value.len) 2699 return MHD_DAUTH_WRONG_URI; 2700 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len) 2701 return MHD_DAUTH_TOO_LARGE; 2702 2703 if (NULL == params->nonce.value.str) 2704 return MHD_DAUTH_NONCE_WRONG; 2705 else if (0 == params->nonce.value.len) 2706 return MHD_DAUTH_NONCE_WRONG; 2707 else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len) 2708 return MHD_DAUTH_NONCE_WRONG; 2709 2710 if (NULL == params->response.value.str) 2711 return MHD_DAUTH_RESPONSE_WRONG; 2712 else if (0 == params->response.value.len) 2713 return MHD_DAUTH_RESPONSE_WRONG; 2714 else if (digest_size * 4 < params->response.value.len) 2715 return MHD_DAUTH_RESPONSE_WRONG; 2716 2717 /* ** Check simple parameters match ** */ 2718 2719 /* Check 'algorithm' */ 2720 /* The 'algorithm' was checked at the start of the function */ 2721 /* 'algorithm' valid */ 2722 2723 /* Check 'qop' */ 2724 /* The 'qop' was checked at the start of the function */ 2725 /* 'qop' valid */ 2726 2727 /* Check 'realm' */ 2728 realm_len = strlen (realm); 2729 if (! is_param_equal (¶ms->realm, realm, realm_len)) 2730 return MHD_DAUTH_WRONG_REALM; 2731 /* 'realm' valid */ 2732 2733 /* Check 'username' */ 2734 username_len = strlen (username); 2735 if (! params->userhash) 2736 { 2737 if (NULL != params->username.value.str) 2738 { /* Username in standard notation */ 2739 if (! is_param_equal (¶ms->username, username, username_len)) 2740 return MHD_DAUTH_WRONG_USERNAME; 2741 } 2742 else 2743 { /* Username in extended notation */ 2744 char *r_uname; 2745 size_t buf_size = params->username_ext.value.len; 2746 ssize_t res; 2747 2748 mhd_assert (NULL != params->username_ext.value.str); 2749 mhd_assert (MHD_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */ 2750 buf_size += 1; /* For zero-termination */ 2751 buf_size -= MHD_DAUTH_EXT_PARAM_MIN_LEN; 2752 r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size); 2753 if (NULL == r_uname) 2754 return (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ? 2755 MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR; 2756 res = get_rq_extended_uname_copy_z (params->username_ext.value.str, 2757 params->username_ext.value.len, 2758 r_uname, buf_size); 2759 if (0 > res) 2760 return MHD_DAUTH_WRONG_HEADER; /* Broken extended notation */ 2761 if ((username_len != (size_t) res) || 2762 (0 != memcmp (username, r_uname, username_len))) 2763 return MHD_DAUTH_WRONG_USERNAME; 2764 } 2765 } 2766 else 2767 { /* Userhash */ 2768 mhd_assert (NULL != params->username.value.str); 2769 calc_userhash (da, username, username_len, realm, realm_len, hash1_bin); 2770 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2771 if (digest_ext_error (da)) 2772 return MHD_DAUTH_ERROR; 2773 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2774 mhd_assert (sizeof (tmp1) >= (2 * digest_size)); 2775 MHD_bin_to_hex (hash1_bin, digest_size, tmp1); 2776 if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) 2777 return MHD_DAUTH_WRONG_USERNAME; 2778 /* To simplify the logic, the digest is reset here instead of resetting 2779 before the next hash calculation. */ 2780 digest_reset (da); 2781 } 2782 /* 'username' valid */ 2783 2784 /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */ 2785 2786 /* Get 'nc' digital value */ 2787 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2788 { 2789 2790 unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, 2791 &unquoted); 2792 if (_MHD_UNQ_OK != unq_res) 2793 return MHD_DAUTH_ERROR; 2794 2795 if (unquoted.len != MHD_strx_to_uint64_n_ (unquoted.str, 2796 unquoted.len, 2797 &nci)) 2798 { 2799 #ifdef HAVE_MESSAGES 2800 MHD_DLOG (daemon, 2801 _ ("Authentication failed, invalid nc format.\n")); 2802 #endif 2803 return MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ 2804 } 2805 if (0 == nci) 2806 { 2807 #ifdef HAVE_MESSAGES 2808 MHD_DLOG (daemon, 2809 _ ("Authentication failed, invalid 'nc' value.\n")); 2810 #endif 2811 return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ 2812 } 2813 if ((0 != max_nc) && (max_nc < nci)) 2814 return MHD_DAUTH_NONCE_STALE; /* Too large 'nc' value */ 2815 } 2816 else 2817 nci = 1; /* Force 'nc' value */ 2818 /* Got 'nc' digital value */ 2819 2820 /* Get 'nonce' with basic checks */ 2821 unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, 2822 &unquoted); 2823 if (_MHD_UNQ_OK != unq_res) 2824 return MHD_DAUTH_ERROR; 2825 2826 if ((NONCE_STD_LEN (digest_size) != unquoted.len) || 2827 (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time))) 2828 { 2829 #ifdef HAVE_MESSAGES 2830 MHD_DLOG (daemon, 2831 _ ("Authentication failed, invalid nonce format.\n")); 2832 #endif 2833 return MHD_DAUTH_NONCE_WRONG; 2834 } 2835 2836 if (1) 2837 { 2838 uint64_t t; 2839 2840 t = MHD_monotonic_msec_counter (); 2841 /* 2842 * First level vetting for the nonce validity: if the timestamp 2843 * attached to the nonce exceeds `nonce_timeout', then the nonce is 2844 * stale. 2845 */ 2846 if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000)) 2847 return MHD_DAUTH_NONCE_STALE; /* too old */ 2848 } 2849 if (1) 2850 { 2851 enum MHD_CheckNonceNC_ nonce_nc_check; 2852 /* 2853 * Checking if that combination of nonce and nc is sound 2854 * and not a replay attack attempt. Refuse if nonce was not 2855 * generated previously. 2856 */ 2857 nonce_nc_check = check_nonce_nc (connection, 2858 unquoted.str, 2859 NONCE_STD_LEN (digest_size), 2860 nonce_time, 2861 nci); 2862 if (MHD_CHECK_NONCENC_STALE == nonce_nc_check) 2863 { 2864 #ifdef HAVE_MESSAGES 2865 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2866 MHD_DLOG (daemon, 2867 _ ("Stale nonce received. If this happens a lot, you should " 2868 "probably increase the size of the nonce array.\n")); 2869 else 2870 MHD_DLOG (daemon, 2871 _ ("Stale nonce received. This is expected when client " \ 2872 "uses RFC2069-compatible mode and makes more than one " \ 2873 "request.\n")); 2874 #endif 2875 return MHD_DAUTH_NONCE_STALE; 2876 } 2877 else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check) 2878 { 2879 #ifdef HAVE_MESSAGES 2880 MHD_DLOG (daemon, 2881 _ ("Received nonce that was not " 2882 "generated by MHD. This may indicate an attack attempt.\n")); 2883 #endif 2884 return MHD_DAUTH_NONCE_WRONG; 2885 } 2886 mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check); 2887 } 2888 /* The nonce was generated by MHD, is not stale and nonce-nc combination was 2889 not used before */ 2890 2891 /* ** Build H(A2) and check URI match in the header and in the request ** */ 2892 2893 /* Get 'uri' */ 2894 mhd_assert (! da->hashing); 2895 digest_update_str (da, connection->rq.method); 2896 digest_update_with_colon (da); 2897 #if 0 2898 /* TODO: add support for "auth-int" */ 2899 digest_update_str (da, hentity); 2900 digest_update_with_colon (da); 2901 #endif 2902 unq_res = get_unquoted_param_copy (¶ms->uri, tmp1, ptmp2, &tmp2_size, 2903 &unq_copy); 2904 if (_MHD_UNQ_OK != unq_res) 2905 return MHD_DAUTH_ERROR; 2906 2907 digest_update (da, unq_copy.str, unq_copy.len); 2908 /* The next check will modify copied URI string */ 2909 if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) 2910 return MHD_DAUTH_WRONG_URI; 2911 digest_calc_hash (da, hash2_bin); 2912 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2913 /* Skip digest calculation external error check, the next one checks both */ 2914 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2915 /* Got H(A2) */ 2916 2917 /* ** Build H(A1) ** */ 2918 if (NULL == userdigest) 2919 { 2920 mhd_assert (! da->hashing); 2921 digest_reset (da); 2922 calc_userdigest (da, 2923 username, username_len, 2924 realm, realm_len, 2925 password, 2926 hash1_bin); 2927 } 2928 /* TODO: support '-sess' versions */ 2929 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2930 if (digest_ext_error (da)) 2931 return MHD_DAUTH_ERROR; 2932 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2933 /* Got H(A1) */ 2934 2935 /* ** Check 'response' ** */ 2936 2937 mhd_assert (! da->hashing); 2938 digest_reset (da); 2939 /* Update digest with H(A1) */ 2940 mhd_assert (sizeof (tmp1) >= (digest_size * 2)); 2941 if (NULL == userdigest) 2942 MHD_bin_to_hex (hash1_bin, digest_size, tmp1); 2943 else 2944 MHD_bin_to_hex (userdigest, digest_size, tmp1); 2945 digest_update (da, (const uint8_t *) tmp1, digest_size * 2); 2946 2947 /* H(A1) is not needed anymore, reuse the buffer. 2948 * Use hash1_bin for the client's 'response' decoded to binary form. */ 2949 unq_res = get_unquoted_param (¶ms->response, tmp1, ptmp2, &tmp2_size, 2950 &unquoted); 2951 if (_MHD_UNQ_OK != unq_res) 2952 return MHD_DAUTH_ERROR; 2953 if (digest_size != MHD_hex_to_bin (unquoted.str, unquoted.len, hash1_bin)) 2954 return MHD_DAUTH_RESPONSE_WRONG; 2955 2956 /* Update digest with ':' */ 2957 digest_update_with_colon (da); 2958 /* Update digest with 'nonce' text value */ 2959 unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, 2960 &unquoted); 2961 if (_MHD_UNQ_OK != unq_res) 2962 return MHD_DAUTH_ERROR; 2963 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2964 /* Update digest with ':' */ 2965 digest_update_with_colon (da); 2966 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2967 { 2968 /* Update digest with 'nc' text value */ 2969 unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, 2970 &unquoted); 2971 if (_MHD_UNQ_OK != unq_res) 2972 return MHD_DAUTH_ERROR; 2973 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2974 /* Update digest with ':' */ 2975 digest_update_with_colon (da); 2976 /* Update digest with 'cnonce' value */ 2977 unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, 2978 &unquoted); 2979 if (_MHD_UNQ_OK != unq_res) 2980 return MHD_DAUTH_ERROR; 2981 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2982 /* Update digest with ':' */ 2983 digest_update_with_colon (da); 2984 /* Update digest with 'qop' value */ 2985 unq_res = get_unquoted_param (¶ms->qop_raw, tmp1, ptmp2, &tmp2_size, 2986 &unquoted); 2987 if (_MHD_UNQ_OK != unq_res) 2988 return MHD_DAUTH_ERROR; 2989 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2990 /* Update digest with ':' */ 2991 digest_update_with_colon (da); 2992 } 2993 /* Update digest with H(A2) */ 2994 MHD_bin_to_hex (hash2_bin, digest_size, tmp1); 2995 digest_update (da, (const uint8_t *) tmp1, digest_size * 2); 2996 2997 /* H(A2) is not needed anymore, reuse the buffer. 2998 * Use hash2_bin for the calculated response in binary form */ 2999 digest_calc_hash (da, hash2_bin); 3000 #ifdef MHD_DIGEST_HAS_EXT_ERROR 3001 if (digest_ext_error (da)) 3002 return MHD_DAUTH_ERROR; 3003 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 3004 3005 if (0 != memcmp (hash1_bin, hash2_bin, digest_size)) 3006 return MHD_DAUTH_RESPONSE_WRONG; 3007 3008 if (MHD_DAUTH_BIND_NONCE_NONE != daemon->dauth_bind_type) 3009 { 3010 mhd_assert (sizeof(tmp1) >= (NONCE_STD_LEN (digest_size) + 1)); 3011 /* It was already checked that 'nonce' (including timestamp) was generated 3012 by MHD. */ 3013 mhd_assert (! da->hashing); 3014 digest_reset (da); 3015 calculate_nonce (nonce_time, 3016 connection->rq.http_mthd, 3017 connection->rq.method, 3018 daemon->digest_auth_random, 3019 daemon->digest_auth_rand_size, 3020 connection->addr, 3021 (size_t) connection->addr_len, 3022 connection->rq.url, 3023 connection->rq.url_len, 3024 connection->rq.headers_received, 3025 realm, 3026 realm_len, 3027 daemon->dauth_bind_type, 3028 da, 3029 tmp1); 3030 3031 #ifdef MHD_DIGEST_HAS_EXT_ERROR 3032 if (digest_ext_error (da)) 3033 return MHD_DAUTH_ERROR; 3034 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 3035 3036 if (! is_param_equal (¶ms->nonce, tmp1, 3037 NONCE_STD_LEN (digest_size))) 3038 return MHD_DAUTH_NONCE_OTHER_COND; 3039 /* The 'nonce' was generated in the same conditions */ 3040 } 3041 3042 return MHD_DAUTH_OK; 3043 } 3044 3045 3046 /** 3047 * Authenticates the authorization header sent by the client 3048 * 3049 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3050 * @a mqop and the client uses this mode, then server generated nonces are 3051 * used as one-time nonces because nonce-count is not supported in this old RFC. 3052 * Communication in this mode is very inefficient, especially if the client 3053 * requests several resources one-by-one as for every request new nonce must be 3054 * generated and client repeat all requests twice (the first time to get a new 3055 * nonce and the second time to perform an authorised request). 3056 * 3057 * @param connection the MHD connection structure 3058 * @param realm the realm for authorization of the client 3059 * @param username the username to be authenticated, must be in clear text 3060 * even if userhash is used by the client 3061 * @param password the password used in the authentication, 3062 * must be NULL if @a userdigest is not NULL 3063 * @param userdigest the precalculated binary hash of the string 3064 * "username:realm:password", 3065 * must be NULL if @a password is not NULL 3066 * @param nonce_timeout the period of seconds since nonce generation, when 3067 * the nonce is recognised as valid and not stale; 3068 * if set to zero then daemon's default value is used 3069 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3070 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3071 * returned; 3072 * if set to zero then daemon's default value is used 3073 * @param mqop the QOP to use 3074 * @param malgo3 digest algorithms allowed to use, fail if algorithm specified 3075 * by the client is not allowed by this parameter 3076 * @return #MHD_DAUTH_OK if authenticated, 3077 * error code otherwise. 3078 * @ingroup authentication 3079 */ 3080 static enum MHD_DigestAuthResult 3081 digest_auth_check_all (struct MHD_Connection *connection, 3082 const char *realm, 3083 const char *username, 3084 const char *password, 3085 const uint8_t *userdigest, 3086 unsigned int nonce_timeout, 3087 uint32_t max_nc, 3088 enum MHD_DigestAuthMultiQOP mqop, 3089 enum MHD_DigestAuthMultiAlgo3 malgo3) 3090 { 3091 enum MHD_DigestAuthResult res; 3092 char *buf; 3093 struct DigestAlgorithm da; 3094 3095 buf = NULL; 3096 digest_setup_zero (&da); 3097 if (0 == nonce_timeout) 3098 nonce_timeout = connection->daemon->dauth_def_nonce_timeout; 3099 if (0 == max_nc) 3100 max_nc = connection->daemon->dauth_def_max_nc; 3101 res = digest_auth_check_all_inner (connection, realm, username, password, 3102 userdigest, 3103 nonce_timeout, 3104 max_nc, mqop, malgo3, 3105 &buf, &da); 3106 digest_deinit (&da); 3107 if (NULL != buf) 3108 free (buf); 3109 3110 return res; 3111 } 3112 3113 3114 /** 3115 * Authenticates the authorization header sent by the client. 3116 * Uses #MHD_DIGEST_ALG_MD5 (for now, for backwards-compatibility). 3117 * Note that this MAY change to #MHD_DIGEST_ALG_AUTO in the future. 3118 * If you want to be sure you get MD5, use #MHD_digest_auth_check2() 3119 * and specify MD5 explicitly. 3120 * 3121 * @param connection The MHD connection structure 3122 * @param realm The realm presented to the client 3123 * @param username The username needs to be authenticated 3124 * @param password The password used in the authentication 3125 * @param nonce_timeout The amount of time for a nonce to be 3126 * invalid in seconds 3127 * @return #MHD_YES if authenticated, #MHD_NO if not, 3128 * #MHD_INVALID_NONCE if nonce is invalid or stale 3129 * @deprecated use MHD_digest_auth_check3() 3130 * @ingroup authentication 3131 */ 3132 _MHD_EXTERN int 3133 MHD_digest_auth_check (struct MHD_Connection *connection, 3134 const char *realm, 3135 const char *username, 3136 const char *password, 3137 unsigned int nonce_timeout) 3138 { 3139 return MHD_digest_auth_check2 (connection, 3140 realm, 3141 username, 3142 password, 3143 nonce_timeout, 3144 MHD_DIGEST_ALG_MD5); 3145 } 3146 3147 3148 /** 3149 * Authenticates the authorization header sent by the client. 3150 * 3151 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3152 * @a mqop and the client uses this mode, then server generated nonces are 3153 * used as one-time nonces because nonce-count is not supported in this old RFC. 3154 * Communication in this mode is very inefficient, especially if the client 3155 * requests several resources one-by-one as for every request a new nonce must 3156 * be generated and client repeats all requests twice (first time to get a new 3157 * nonce and second time to perform an authorised request). 3158 * 3159 * @param connection the MHD connection structure 3160 * @param realm the realm for authorization of the client 3161 * @param username the username to be authenticated, must be in clear text 3162 * even if userhash is used by the client 3163 * @param password the password matching the @a username (and the @a realm) 3164 * @param nonce_timeout the period of seconds since nonce generation, when 3165 * the nonce is recognised as valid and not stale; 3166 * if zero is specified then daemon default value is used. 3167 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3168 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3169 * returned; 3170 * if zero is specified then daemon default value is used. 3171 * @param mqop the QOP to use 3172 * @param malgo3 digest algorithms allowed to use, fail if algorithm used 3173 * by the client is not allowed by this parameter 3174 * @return #MHD_DAUTH_OK if authenticated, 3175 * the error code otherwise 3176 * @note Available since #MHD_VERSION 0x00097708 3177 * @ingroup authentication 3178 */ 3179 _MHD_EXTERN enum MHD_DigestAuthResult 3180 MHD_digest_auth_check3 (struct MHD_Connection *connection, 3181 const char *realm, 3182 const char *username, 3183 const char *password, 3184 unsigned int nonce_timeout, 3185 uint32_t max_nc, 3186 enum MHD_DigestAuthMultiQOP mqop, 3187 enum MHD_DigestAuthMultiAlgo3 malgo3) 3188 { 3189 mhd_assert (NULL != password); 3190 3191 return digest_auth_check_all (connection, 3192 realm, 3193 username, 3194 password, 3195 NULL, 3196 nonce_timeout, 3197 max_nc, 3198 mqop, 3199 malgo3); 3200 } 3201 3202 3203 /** 3204 * Authenticates the authorization header sent by the client by using 3205 * hash of "username:realm:password". 3206 * 3207 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3208 * @a mqop and the client uses this mode, then server generated nonces are 3209 * used as one-time nonces because nonce-count is not supported in this old RFC. 3210 * Communication in this mode is very inefficient, especially if the client 3211 * requests several resources one-by-one as for every request a new nonce must 3212 * be generated and client repeats all requests twice (first time to get a new 3213 * nonce and second time to perform an authorised request). 3214 * 3215 * @param connection the MHD connection structure 3216 * @param realm the realm for authorization of the client 3217 * @param username the username to be authenticated, must be in clear text 3218 * even if userhash is used by the client 3219 * @param userdigest the precalculated binary hash of the string 3220 * "username:realm:password", 3221 * see #MHD_digest_auth_calc_userdigest() 3222 * @param userdigest_size the size of the @a userdigest in bytes, must match the 3223 * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, 3224 * #MHD_SHA256_DIGEST_SIZE, #MHD_SHA512_256_DIGEST_SIZE, 3225 * #MHD_digest_get_hash_size()) 3226 * @param nonce_timeout the period of seconds since nonce generation, when 3227 * the nonce is recognised as valid and not stale; 3228 * if zero is specified then daemon default value is used. 3229 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3230 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3231 * returned; 3232 * if zero is specified then daemon default value is used. 3233 * @param mqop the QOP to use 3234 * @param malgo3 digest algorithms allowed to use, fail if algorithm used 3235 * by the client is not allowed by this parameter; 3236 * more than one base algorithms (MD5, SHA-256, SHA-512/256) 3237 * cannot be used at the same time for this function 3238 * as @a userdigest must match specified algorithm 3239 * @return #MHD_DAUTH_OK if authenticated, 3240 * the error code otherwise 3241 * @sa #MHD_digest_auth_calc_userdigest() 3242 * @note Available since #MHD_VERSION 0x00097708 3243 * @ingroup authentication 3244 */ 3245 _MHD_EXTERN enum MHD_DigestAuthResult 3246 MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, 3247 const char *realm, 3248 const char *username, 3249 const void *userdigest, 3250 size_t userdigest_size, 3251 unsigned int nonce_timeout, 3252 uint32_t max_nc, 3253 enum MHD_DigestAuthMultiQOP mqop, 3254 enum MHD_DigestAuthMultiAlgo3 malgo3) 3255 { 3256 if (1 != (((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0) 3257 + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0) 3258 + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0))) 3259 MHD_PANIC (_ ("Wrong 'malgo3' value, only one base hashing algorithm " \ 3260 "(MD5, SHA-256 or SHA-512/256) must be specified, " \ 3261 "API violation")); 3262 3263 #ifndef MHD_MD5_SUPPORT 3264 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) 3265 { 3266 #ifdef HAVE_MESSAGES 3267 MHD_DLOG (connection->daemon, 3268 _ ("The MD5 algorithm is not supported by this MHD build.\n")); 3269 #endif /* HAVE_MESSAGES */ 3270 return MHD_DAUTH_WRONG_ALGO; 3271 } 3272 #endif /* ! MHD_MD5_SUPPORT */ 3273 #ifndef MHD_SHA256_SUPPORT 3274 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) 3275 { 3276 #ifdef HAVE_MESSAGES 3277 MHD_DLOG (connection->daemon, 3278 _ ("The SHA-256 algorithm is not supported by " 3279 "this MHD build.\n")); 3280 #endif /* HAVE_MESSAGES */ 3281 return MHD_DAUTH_WRONG_ALGO; 3282 } 3283 #endif /* ! MHD_SHA256_SUPPORT */ 3284 #ifndef MHD_SHA512_256_SUPPORT 3285 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 3286 { 3287 #ifdef HAVE_MESSAGES 3288 MHD_DLOG (connection->daemon, 3289 _ ("The SHA-512/256 algorithm is not supported by " 3290 "this MHD build.\n")); 3291 #endif /* HAVE_MESSAGES */ 3292 return MHD_DAUTH_WRONG_ALGO; 3293 } 3294 #endif /* ! MHD_SHA512_256_SUPPORT */ 3295 3296 if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) != 3297 userdigest_size) 3298 MHD_PANIC (_ ("Wrong 'userdigest_size' value, does not match 'malgo3', " 3299 "API violation")); 3300 3301 return digest_auth_check_all (connection, 3302 realm, 3303 username, 3304 NULL, 3305 (const uint8_t *) userdigest, 3306 nonce_timeout, 3307 max_nc, 3308 mqop, 3309 malgo3); 3310 } 3311 3312 3313 /** 3314 * Authenticates the authorization header sent by the client. 3315 * 3316 * @param connection The MHD connection structure 3317 * @param realm The realm presented to the client 3318 * @param username The username needs to be authenticated 3319 * @param password The password used in the authentication 3320 * @param nonce_timeout The amount of time for a nonce to be 3321 * invalid in seconds 3322 * @param algo digest algorithms allowed for verification 3323 * @return #MHD_YES if authenticated, #MHD_NO if not, 3324 * #MHD_INVALID_NONCE if nonce is invalid or stale 3325 * @note Available since #MHD_VERSION 0x00096200 3326 * @deprecated use MHD_digest_auth_check3() 3327 * @ingroup authentication 3328 */ 3329 _MHD_EXTERN int 3330 MHD_digest_auth_check2 (struct MHD_Connection *connection, 3331 const char *realm, 3332 const char *username, 3333 const char *password, 3334 unsigned int nonce_timeout, 3335 enum MHD_DigestAuthAlgorithm algo) 3336 { 3337 enum MHD_DigestAuthResult res; 3338 enum MHD_DigestAuthMultiAlgo3 malgo3; 3339 3340 if (MHD_DIGEST_ALG_AUTO == algo) 3341 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; 3342 else if (MHD_DIGEST_ALG_MD5 == algo) 3343 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; 3344 else if (MHD_DIGEST_ALG_SHA256 == algo) 3345 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; 3346 else 3347 MHD_PANIC (_ ("Wrong 'algo' value, API violation")); 3348 3349 res = MHD_digest_auth_check3 (connection, 3350 realm, 3351 username, 3352 password, 3353 nonce_timeout, 3354 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, 3355 malgo3); 3356 if (MHD_DAUTH_OK == res) 3357 return MHD_YES; 3358 else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) || 3359 (MHD_DAUTH_NONCE_OTHER_COND == res) ) 3360 return MHD_INVALID_NONCE; 3361 return MHD_NO; 3362 3363 } 3364 3365 3366 /** 3367 * Authenticates the authorization header sent by the client. 3368 * 3369 * @param connection The MHD connection structure 3370 * @param realm The realm presented to the client 3371 * @param username The username needs to be authenticated 3372 * @param digest An `unsigned char *' pointer to the binary MD5 sum 3373 * for the precalculated hash value "username:realm:password" 3374 * of @a digest_size bytes 3375 * @param digest_size number of bytes in @a digest (size must match @a algo!) 3376 * @param nonce_timeout The amount of time for a nonce to be 3377 * invalid in seconds 3378 * @param algo digest algorithms allowed for verification 3379 * @return #MHD_YES if authenticated, #MHD_NO if not, 3380 * #MHD_INVALID_NONCE if nonce is invalid or stale 3381 * @note Available since #MHD_VERSION 0x00096200 3382 * @deprecated use MHD_digest_auth_check_digest3() 3383 * @ingroup authentication 3384 */ 3385 _MHD_EXTERN int 3386 MHD_digest_auth_check_digest2 (struct MHD_Connection *connection, 3387 const char *realm, 3388 const char *username, 3389 const uint8_t *digest, 3390 size_t digest_size, 3391 unsigned int nonce_timeout, 3392 enum MHD_DigestAuthAlgorithm algo) 3393 { 3394 enum MHD_DigestAuthResult res; 3395 enum MHD_DigestAuthMultiAlgo3 malgo3; 3396 3397 if (MHD_DIGEST_ALG_AUTO == algo) 3398 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; 3399 else if (MHD_DIGEST_ALG_MD5 == algo) 3400 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; 3401 else if (MHD_DIGEST_ALG_SHA256 == algo) 3402 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; 3403 else 3404 MHD_PANIC (_ ("Wrong 'algo' value, API violation")); 3405 3406 res = MHD_digest_auth_check_digest3 (connection, 3407 realm, 3408 username, 3409 digest, 3410 digest_size, 3411 nonce_timeout, 3412 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, 3413 malgo3); 3414 if (MHD_DAUTH_OK == res) 3415 return MHD_YES; 3416 else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) || 3417 (MHD_DAUTH_NONCE_OTHER_COND == res) ) 3418 return MHD_INVALID_NONCE; 3419 return MHD_NO; 3420 } 3421 3422 3423 /** 3424 * Authenticates the authorization header sent by the client 3425 * Uses #MHD_DIGEST_ALG_MD5 (required, as @a digest is of fixed 3426 * size). 3427 * 3428 * @param connection The MHD connection structure 3429 * @param realm The realm presented to the client 3430 * @param username The username needs to be authenticated 3431 * @param digest An `unsigned char *' pointer to the binary hash 3432 * for the precalculated hash value "username:realm:password"; 3433 * length must be #MHD_MD5_DIGEST_SIZE bytes 3434 * @param nonce_timeout The amount of time for a nonce to be 3435 * invalid in seconds 3436 * @return #MHD_YES if authenticated, #MHD_NO if not, 3437 * #MHD_INVALID_NONCE if nonce is invalid or stale 3438 * @note Available since #MHD_VERSION 0x00096000 3439 * @deprecated use #MHD_digest_auth_check_digest3() 3440 * @ingroup authentication 3441 */ 3442 _MHD_EXTERN int 3443 MHD_digest_auth_check_digest (struct MHD_Connection *connection, 3444 const char *realm, 3445 const char *username, 3446 const uint8_t digest[MHD_MD5_DIGEST_SIZE], 3447 unsigned int nonce_timeout) 3448 { 3449 return MHD_digest_auth_check_digest2 (connection, 3450 realm, 3451 username, 3452 digest, 3453 MHD_MD5_DIGEST_SIZE, 3454 nonce_timeout, 3455 MHD_DIGEST_ALG_MD5); 3456 } 3457 3458 3459 /** 3460 * Internal version of #MHD_queue_auth_required_response3() to simplify 3461 * cleanups. 3462 * 3463 * @param connection the MHD connection structure 3464 * @param realm the realm presented to the client 3465 * @param opaque the string for opaque value, can be NULL, but NULL is 3466 * not recommended for better compatibility with clients; 3467 * the recommended format is hex or Base64 encoded string 3468 * @param domain the optional space-separated list of URIs for which the 3469 * same authorisation could be used, URIs can be in form 3470 * "path-absolute" (the path for the same host with initial slash) 3471 * or in form "absolute-URI" (the full path with protocol), in 3472 * any case client may assume that URI is in the same "protection 3473 * space" if it starts with any of values specified here; 3474 * could be NULL (clients typically assume that the same 3475 * credentials could be used for any URI on the same host) 3476 * @param response the reply to send; should contain the "access denied" 3477 * body; note that this function sets the "WWW Authenticate" 3478 * header and that the caller should not do this; 3479 * the NULL is tolerated 3480 * @param signal_stale set to #MHD_YES if the nonce is stale to add 'stale=true' 3481 * to the authentication header, this instructs the client 3482 * to retry immediately with the new nonce and the same 3483 * credentials, without asking user for the new password 3484 * @param mqop the QOP to use 3485 * @param malgo3 digest algorithm to use, MHD selects; if several algorithms 3486 * are allowed then MD5 is preferred (currently, may be changed 3487 * in next versions) 3488 * @param userhash_support if set to non-zero value (#MHD_YES) then support of 3489 * userhash is indicated, the client may provide 3490 * hash("username:realm") instead of username in 3491 * clear text; 3492 * note that clients are allowed to provide the username 3493 * in cleartext even if this parameter set to non-zero; 3494 * when userhash is used, application must be ready to 3495 * identify users by provided userhash value instead of 3496 * username; see #MHD_digest_auth_calc_userhash() and 3497 * #MHD_digest_auth_calc_userhash_hex() 3498 * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is 3499 * added, indicating for the client that UTF-8 encoding 3500 * is preferred 3501 * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is 3502 * added, indicating for the client that UTF-8 encoding 3503 * is preferred 3504 * @return #MHD_YES on success, #MHD_NO otherwise 3505 * @note Available since #MHD_VERSION 0x00097701 3506 * @ingroup authentication 3507 */ 3508 static enum MHD_Result 3509 queue_auth_required_response3_inner (struct MHD_Connection *connection, 3510 const char *realm, 3511 const char *opaque, 3512 const char *domain, 3513 struct MHD_Response *response, 3514 int signal_stale, 3515 enum MHD_DigestAuthMultiQOP mqop, 3516 enum MHD_DigestAuthMultiAlgo3 malgo3, 3517 int userhash_support, 3518 int prefer_utf8, 3519 char **buf_ptr, 3520 struct DigestAlgorithm *da) 3521 { 3522 static const char prefix_realm[] = "realm=\""; 3523 static const char prefix_qop[] = "qop=\""; 3524 static const char prefix_algo[] = "algorithm="; 3525 static const char prefix_nonce[] = "nonce=\""; 3526 static const char prefix_opaque[] = "opaque=\""; 3527 static const char prefix_domain[] = "domain=\""; 3528 static const char str_charset[] = "charset=UTF-8"; 3529 static const char str_userhash[] = "userhash=true"; 3530 static const char str_stale[] = "stale=true"; 3531 enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */ 3532 size_t realm_len; 3533 size_t opaque_len; 3534 size_t domain_len; 3535 size_t buf_size; 3536 char *buf; 3537 size_t p; /* The position in the buffer */ 3538 char *hdr_name; 3539 3540 if ((0 == (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_NON_SESSION)) || 3541 (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))) 3542 { 3543 #ifdef HAVE_MESSAGES 3544 MHD_DLOG (connection->daemon, 3545 _ ("Only non-'session' algorithms are supported.\n")); 3546 #endif /* HAVE_MESSAGES */ 3547 return MHD_NO; 3548 } 3549 malgo3 = 3550 (enum MHD_DigestAuthMultiAlgo3) 3551 (malgo3 3552 & (~((enum MHD_DigestAuthMultiAlgo3) MHD_DIGEST_AUTH_ALGO3_NON_SESSION))); 3553 #ifdef MHD_MD5_SUPPORT 3554 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) 3555 s_algo = MHD_DIGEST_AUTH_ALGO3_MD5; 3556 else 3557 #endif /* MHD_MD5_SUPPORT */ 3558 #ifdef MHD_SHA256_SUPPORT 3559 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) 3560 s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256; 3561 else 3562 #endif /* MHD_SHA256_SUPPORT */ 3563 #ifdef MHD_SHA512_256_SUPPORT 3564 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 3565 s_algo = MHD_DIGEST_AUTH_ALGO3_SHA512_256; 3566 else 3567 #endif /* MHD_SHA512_256_SUPPORT */ 3568 { 3569 if (0 == (((unsigned int) malgo3) 3570 & (MHD_DIGEST_BASE_ALGO_MD5 | MHD_DIGEST_BASE_ALGO_SHA256 3571 | MHD_DIGEST_BASE_ALGO_SHA512_256))) 3572 MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); 3573 else 3574 { 3575 #ifdef HAVE_MESSAGES 3576 MHD_DLOG (connection->daemon, 3577 _ ("No requested algorithm is supported by this MHD build.\n")); 3578 #endif /* HAVE_MESSAGES */ 3579 } 3580 return MHD_NO; 3581 } 3582 3583 if (MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT == mqop) 3584 MHD_PANIC (_ ("Wrong 'mqop' value, API violation")); 3585 3586 mqop = (enum MHD_DigestAuthMultiQOP) 3587 (mqop 3588 & (~((enum MHD_DigestAuthMultiQOP) MHD_DIGEST_AUTH_QOP_AUTH_INT))); 3589 3590 if (! digest_init_one_time (da, get_base_digest_algo (s_algo))) 3591 MHD_PANIC (_ ("Wrong 'algo' value, API violation")); 3592 3593 if (MHD_DIGEST_AUTH_MULT_QOP_NONE == mqop) 3594 { 3595 #ifdef HAVE_MESSAGES 3596 if ((0 != userhash_support) || (0 != prefer_utf8)) 3597 MHD_DLOG (connection->daemon, 3598 _ ("The 'userhash' and 'charset' ('prefer_utf8') parameters " \ 3599 "are not compatible with RFC2069 and ignored.\n")); 3600 if (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)) 3601 MHD_DLOG (connection->daemon, 3602 _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \ 3603 "non-standard extension.\n")); 3604 #endif 3605 userhash_support = 0; 3606 prefer_utf8 = 0; 3607 } 3608 3609 if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) 3610 { 3611 #ifdef HAVE_MESSAGES 3612 MHD_DLOG (connection->daemon, 3613 _ ("The nonce array size is zero.\n")); 3614 #endif /* HAVE_MESSAGES */ 3615 return MHD_NO; 3616 } 3617 3618 /* Calculate required size */ 3619 buf_size = 0; 3620 /* 'Digest ' */ 3621 buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */ 3622 buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */ 3623 /* 'realm="xxxx", ' */ 3624 realm_len = strlen (realm); 3625 if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len) 3626 { 3627 #ifdef HAVE_MESSAGES 3628 MHD_DLOG (connection->daemon, 3629 _ ("The 'realm' is too large.\n")); 3630 #endif /* HAVE_MESSAGES */ 3631 return MHD_NO; 3632 } 3633 if ((NULL != memchr (realm, '\r', realm_len)) || 3634 (NULL != memchr (realm, '\n', realm_len))) 3635 return MHD_NO; 3636 3637 buf_size += realm_len * 2; /* Quoting may double the size */ 3638 /* 'qop="xxxx", ' */ 3639 if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop) 3640 { 3641 buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */ 3642 buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); 3643 } 3644 /* 'algorithm="xxxx", ' */ 3645 if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) || 3646 (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5))) 3647 { 3648 buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */ 3649 #ifdef MHD_MD5_SUPPORT 3650 if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) 3651 buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); 3652 else 3653 #endif /* MHD_MD5_SUPPORT */ 3654 #ifdef MHD_SHA256_SUPPORT 3655 if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) 3656 buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); 3657 else 3658 #endif /* MHD_SHA256_SUPPORT */ 3659 #ifdef MHD_SHA512_256_SUPPORT 3660 if (MHD_DIGEST_AUTH_ALGO3_SHA512_256 == s_algo) 3661 buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN); 3662 else 3663 #endif /* MHD_SHA512_256_SUPPORT */ 3664 mhd_assert (0); 3665 } 3666 /* 'nonce="xxxx", ' */ 3667 buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */ 3668 buf_size += NONCE_STD_LEN (digest_get_size (da)); /* Escaping not needed */ 3669 /* 'opaque="xxxx", ' */ 3670 if (NULL != opaque) 3671 { 3672 buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */ 3673 opaque_len = strlen (opaque); 3674 if ((NULL != memchr (opaque, '\r', opaque_len)) || 3675 (NULL != memchr (opaque, '\n', opaque_len))) 3676 return MHD_NO; 3677 3678 buf_size += opaque_len * 2; /* Quoting may double the size */ 3679 } 3680 else 3681 opaque_len = 0; 3682 /* 'domain="xxxx", ' */ 3683 if (NULL != domain) 3684 { 3685 buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */ 3686 domain_len = strlen (domain); 3687 if ((NULL != memchr (domain, '\r', domain_len)) || 3688 (NULL != memchr (domain, '\n', domain_len))) 3689 return MHD_NO; 3690 3691 buf_size += domain_len * 2; /* Quoting may double the size */ 3692 } 3693 else 3694 domain_len = 0; 3695 /* 'charset=UTF-8' */ 3696 if (MHD_NO != prefer_utf8) 3697 buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */ 3698 /* 'userhash=true' */ 3699 if (MHD_NO != userhash_support) 3700 buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */ 3701 /* 'stale=true' */ 3702 if (MHD_NO != signal_stale) 3703 buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */ 3704 3705 /* The calculated length is for string ended with ", ". One character will 3706 * be used for zero-termination, the last one will not be used. */ 3707 3708 /* Allocate the buffer */ 3709 buf = malloc (buf_size); 3710 if (NULL == buf) 3711 return MHD_NO; 3712 *buf_ptr = buf; 3713 3714 /* Build the challenge string */ 3715 p = 0; 3716 /* 'Digest: ' */ 3717 memcpy (buf + p, _MHD_AUTH_DIGEST_BASE, 3718 MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE)); 3719 p += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE); 3720 buf[p++] = ' '; 3721 /* 'realm="xxxx", ' */ 3722 memcpy (buf + p, prefix_realm, 3723 MHD_STATICSTR_LEN_ (prefix_realm)); 3724 p += MHD_STATICSTR_LEN_ (prefix_realm); 3725 mhd_assert ((buf_size - p) >= (realm_len * 2)); 3726 if (1) 3727 { 3728 size_t quoted_size; 3729 quoted_size = MHD_str_quote (realm, realm_len, buf + p, buf_size - p); 3730 if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < quoted_size) 3731 { 3732 #ifdef HAVE_MESSAGES 3733 MHD_DLOG (connection->daemon, 3734 _ ("The 'realm' is too large after 'quoting'.\n")); 3735 #endif /* HAVE_MESSAGES */ 3736 return MHD_NO; 3737 } 3738 p += quoted_size; 3739 } 3740 buf[p++] = '\"'; 3741 buf[p++] = ','; 3742 buf[p++] = ' '; 3743 /* 'qop="xxxx", ' */ 3744 if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop) 3745 { 3746 memcpy (buf + p, prefix_qop, 3747 MHD_STATICSTR_LEN_ (prefix_qop)); 3748 p += MHD_STATICSTR_LEN_ (prefix_qop); 3749 memcpy (buf + p, MHD_TOKEN_AUTH_, 3750 MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_)); 3751 p += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); 3752 buf[p++] = '\"'; 3753 buf[p++] = ','; 3754 buf[p++] = ' '; 3755 } 3756 /* 'algorithm="xxxx", ' */ 3757 if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) || 3758 (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5))) 3759 { 3760 memcpy (buf + p, prefix_algo, 3761 MHD_STATICSTR_LEN_ (prefix_algo)); 3762 p += MHD_STATICSTR_LEN_ (prefix_algo); 3763 #ifdef MHD_MD5_SUPPORT 3764 if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) 3765 { 3766 memcpy (buf + p, _MHD_MD5_TOKEN, 3767 MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN)); 3768 p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); 3769 } 3770 else 3771 #endif /* MHD_MD5_SUPPORT */ 3772 #ifdef MHD_SHA256_SUPPORT 3773 if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) 3774 { 3775 memcpy (buf + p, _MHD_SHA256_TOKEN, 3776 MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN)); 3777 p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); 3778 } 3779 else 3780 #endif /* MHD_SHA256_SUPPORT */ 3781 #ifdef MHD_SHA512_256_SUPPORT 3782 if (MHD_DIGEST_AUTH_ALGO3_SHA512_256 == s_algo) 3783 { 3784 memcpy (buf + p, _MHD_SHA512_256_TOKEN, 3785 MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN)); 3786 p += MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN); 3787 } 3788 else 3789 #endif /* MHD_SHA512_256_SUPPORT */ 3790 mhd_assert (0); 3791 buf[p++] = ','; 3792 buf[p++] = ' '; 3793 } 3794 /* 'nonce="xxxx", ' */ 3795 memcpy (buf + p, prefix_nonce, 3796 MHD_STATICSTR_LEN_ (prefix_nonce)); 3797 p += MHD_STATICSTR_LEN_ (prefix_nonce); 3798 mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (da)))); 3799 if (! calculate_add_nonce_with_retry (connection, realm, da, buf + p)) 3800 { 3801 #ifdef MHD_DIGEST_HAS_EXT_ERROR 3802 if (digest_ext_error (da)) 3803 { 3804 #ifdef HAVE_MESSAGES 3805 MHD_DLOG (connection->daemon, 3806 _ ("TLS library reported hash calculation error, nonce could " 3807 "not be generated.\n")); 3808 #endif /* HAVE_MESSAGES */ 3809 return MHD_NO; 3810 } 3811 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 3812 #ifdef HAVE_MESSAGES 3813 MHD_DLOG (connection->daemon, 3814 _ ("Could not register nonce. Client's requests with this " 3815 "nonce will be always 'stale'. Probably clients' requests " 3816 "are too intensive.\n")); 3817 #endif /* HAVE_MESSAGES */ 3818 (void) 0; /* Mute compiler warning for builds without messages */ 3819 } 3820 p += NONCE_STD_LEN (digest_get_size (da)); 3821 buf[p++] = '\"'; 3822 buf[p++] = ','; 3823 buf[p++] = ' '; 3824 /* 'opaque="xxxx", ' */ 3825 if (NULL != opaque) 3826 { 3827 memcpy (buf + p, prefix_opaque, 3828 MHD_STATICSTR_LEN_ (prefix_opaque)); 3829 p += MHD_STATICSTR_LEN_ (prefix_opaque); 3830 mhd_assert ((buf_size - p) >= (opaque_len * 2)); 3831 p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p); 3832 buf[p++] = '\"'; 3833 buf[p++] = ','; 3834 buf[p++] = ' '; 3835 } 3836 /* 'domain="xxxx", ' */ 3837 if (NULL != domain) 3838 { 3839 memcpy (buf + p, prefix_domain, 3840 MHD_STATICSTR_LEN_ (prefix_domain)); 3841 p += MHD_STATICSTR_LEN_ (prefix_domain); 3842 mhd_assert ((buf_size - p) >= (domain_len * 2)); 3843 p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p); 3844 buf[p++] = '\"'; 3845 buf[p++] = ','; 3846 buf[p++] = ' '; 3847 } 3848 /* 'charset=UTF-8' */ 3849 if (MHD_NO != prefer_utf8) 3850 { 3851 memcpy (buf + p, str_charset, 3852 MHD_STATICSTR_LEN_ (str_charset)); 3853 p += MHD_STATICSTR_LEN_ (str_charset); 3854 buf[p++] = ','; 3855 buf[p++] = ' '; 3856 } 3857 /* 'userhash=true' */ 3858 if (MHD_NO != userhash_support) 3859 { 3860 memcpy (buf + p, str_userhash, 3861 MHD_STATICSTR_LEN_ (str_userhash)); 3862 p += MHD_STATICSTR_LEN_ (str_userhash); 3863 buf[p++] = ','; 3864 buf[p++] = ' '; 3865 } 3866 /* 'stale=true' */ 3867 if (MHD_NO != signal_stale) 3868 { 3869 memcpy (buf + p, str_stale, 3870 MHD_STATICSTR_LEN_ (str_stale)); 3871 p += MHD_STATICSTR_LEN_ (str_stale); 3872 buf[p++] = ','; 3873 buf[p++] = ' '; 3874 } 3875 mhd_assert (buf_size >= p); 3876 /* The built string ends with ", ". Replace comma with zero-termination. */ 3877 --p; 3878 buf[--p] = 0; 3879 3880 hdr_name = malloc (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1); 3881 if (NULL != hdr_name) 3882 { 3883 memcpy (hdr_name, MHD_HTTP_HEADER_WWW_AUTHENTICATE, 3884 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1); 3885 if (MHD_add_response_entry_no_alloc_ (response, MHD_HEADER_KIND, 3886 hdr_name, 3887 MHD_STATICSTR_LEN_ ( \ 3888 MHD_HTTP_HEADER_WWW_AUTHENTICATE), 3889 buf, p)) 3890 { 3891 *buf_ptr = NULL; /* The buffer will be free()ed when the response is destroyed */ 3892 return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); 3893 } 3894 #ifdef HAVE_MESSAGES 3895 else 3896 { 3897 MHD_DLOG (connection->daemon, 3898 _ ("Failed to add Digest auth header.\n")); 3899 } 3900 #endif /* HAVE_MESSAGES */ 3901 free (hdr_name); 3902 } 3903 return MHD_NO; 3904 } 3905 3906 3907 /** 3908 * Queues a response to request authentication from the client 3909 * 3910 * This function modifies provided @a response. The @a response must not be 3911 * reused and should be destroyed (by #MHD_destroy_response()) after call of 3912 * this function. 3913 * 3914 * If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with 3915 * value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was 3916 * not set, because such response should be backward-compatible with RFC 2069. 3917 * 3918 * If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is 3919 * formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no 3920 * 'charset'). For better compatibility with clients, it is recommended (but 3921 * not required) to set @a domain to NULL in this mode. 3922 * 3923 * @param connection the MHD connection structure 3924 * @param realm the realm presented to the client 3925 * @param opaque the string for opaque value, can be NULL, but NULL is 3926 * not recommended for better compatibility with clients; 3927 * the recommended format is hex or Base64 encoded string 3928 * @param domain the optional space-separated list of URIs for which the 3929 * same authorisation could be used, URIs can be in form 3930 * "path-absolute" (the path for the same host with initial slash) 3931 * or in form "absolute-URI" (the full path with protocol), in 3932 * any case client may assume that URI is in the same "protection 3933 * space" if it starts with any of values specified here; 3934 * could be NULL (clients typically assume that the same 3935 * credentials could be used for any URI on the same host); 3936 * this list provides information for the client only and does 3937 * not actually restrict anything on the server side 3938 * @param response the reply to send; should contain the "access denied" 3939 * body; 3940 * note: this function sets the "WWW Authenticate" header and 3941 * the caller should not set this header; 3942 * the NULL is tolerated 3943 * @param signal_stale if set to #MHD_YES then indication of stale nonce used in 3944 * the client's request is signalled by adding 'stale=true' 3945 * to the authentication header, this instructs the client 3946 * to retry immediately with the new nonce and the same 3947 * credentials, without asking user for the new password 3948 * @param mqop the QOP to use 3949 * @param malgo3 digest algorithm to use; if several algorithms are allowed 3950 * then MD5 is preferred (currently, may be changed in next 3951 * versions) 3952 * @param userhash_support if set to non-zero value (#MHD_YES) then support of 3953 * userhash is indicated, allowing client to provide 3954 * hash("username:realm") instead of the username in 3955 * clear text; 3956 * note that clients are allowed to provide the username 3957 * in cleartext even if this parameter set to non-zero; 3958 * when userhash is used, application must be ready to 3959 * identify users by provided userhash value instead of 3960 * username; see #MHD_digest_auth_calc_userhash() and 3961 * #MHD_digest_auth_calc_userhash_hex() 3962 * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is 3963 * added, indicating for the client that UTF-8 encoding for 3964 * the username is preferred 3965 * @return #MHD_YES on success, #MHD_NO otherwise 3966 * @note Available since #MHD_VERSION 0x00097701 3967 * @ingroup authentication 3968 */ 3969 _MHD_EXTERN enum MHD_Result 3970 MHD_queue_auth_required_response3 (struct MHD_Connection *connection, 3971 const char *realm, 3972 const char *opaque, 3973 const char *domain, 3974 struct MHD_Response *response, 3975 int signal_stale, 3976 enum MHD_DigestAuthMultiQOP mqop, 3977 enum MHD_DigestAuthMultiAlgo3 malgo3, 3978 int userhash_support, 3979 int prefer_utf8) 3980 { 3981 struct DigestAlgorithm da; 3982 char *buf_ptr; 3983 enum MHD_Result ret; 3984 3985 buf_ptr = NULL; 3986 digest_setup_zero (&da); 3987 ret = queue_auth_required_response3_inner (connection, 3988 realm, 3989 opaque, 3990 domain, 3991 response, 3992 signal_stale, 3993 mqop, 3994 malgo3, 3995 userhash_support, 3996 prefer_utf8, 3997 &buf_ptr, 3998 &da); 3999 digest_deinit (&da); 4000 if (NULL != buf_ptr) 4001 free (buf_ptr); 4002 return ret; 4003 } 4004 4005 4006 /** 4007 * Queues a response to request authentication from the client 4008 * 4009 * @param connection The MHD connection structure 4010 * @param realm the realm presented to the client 4011 * @param opaque string to user for opaque value 4012 * @param response reply to send; should contain the "access denied" 4013 * body; note that this function will set the "WWW Authenticate" 4014 * header and that the caller should not do this; the NULL is tolerated 4015 * @param signal_stale #MHD_YES if the nonce is stale to add 4016 * 'stale=true' to the authentication header 4017 * @param algo digest algorithm to use 4018 * @return #MHD_YES on success, #MHD_NO otherwise 4019 * @note Available since #MHD_VERSION 0x00096200 4020 * @ingroup authentication 4021 */ 4022 _MHD_EXTERN enum MHD_Result 4023 MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, 4024 const char *realm, 4025 const char *opaque, 4026 struct MHD_Response *response, 4027 int signal_stale, 4028 enum MHD_DigestAuthAlgorithm algo) 4029 { 4030 enum MHD_DigestAuthMultiAlgo3 algo3; 4031 4032 if (MHD_DIGEST_ALG_MD5 == algo) 4033 algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; 4034 else if (MHD_DIGEST_ALG_SHA256 == algo) 4035 algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; 4036 else if (MHD_DIGEST_ALG_AUTO == algo) 4037 algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; 4038 else 4039 MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ 4040 4041 return MHD_queue_auth_required_response3 (connection, realm, opaque, 4042 NULL, response, signal_stale, 4043 MHD_DIGEST_AUTH_MULT_QOP_AUTH, 4044 algo3, 4045 0, 0); 4046 } 4047 4048 4049 /** 4050 * Queues a response to request authentication from the client. 4051 * For now uses MD5 (for backwards-compatibility). Still, if you 4052 * need to be sure, use #MHD_queue_auth_fail_response2(). 4053 * 4054 * @param connection The MHD connection structure 4055 * @param realm the realm presented to the client 4056 * @param opaque string to user for opaque value 4057 * @param response reply to send; should contain the "access denied" 4058 * body; note that this function will set the "WWW Authenticate" 4059 * header and that the caller should not do this; the NULL is tolerated 4060 * @param signal_stale #MHD_YES if the nonce is stale to add 4061 * 'stale=true' to the authentication header 4062 * @return #MHD_YES on success, #MHD_NO otherwise 4063 * @ingroup authentication 4064 * @deprecated use MHD_queue_auth_fail_response2() 4065 */ 4066 _MHD_EXTERN enum MHD_Result 4067 MHD_queue_auth_fail_response (struct MHD_Connection *connection, 4068 const char *realm, 4069 const char *opaque, 4070 struct MHD_Response *response, 4071 int signal_stale) 4072 { 4073 return MHD_queue_auth_fail_response2 (connection, 4074 realm, 4075 opaque, 4076 response, 4077 signal_stale, 4078 MHD_DIGEST_ALG_MD5); 4079 } 4080 4081 4082 /* end of digestauth.c */