digestauth.c (142731B)
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 if (NULL == info) 1295 return NULL; 1296 unif_buf_ptr = (uint8_t *) (info + 1); 1297 unif_buf_used = 0; 1298 1299 info->algo3 = params->algo3; 1300 1301 if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type) && 1302 (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type) ) 1303 unif_buf_used += 1304 get_rq_uname (params, uname_type, 1305 (struct MHD_DigestAuthUsernameInfo *) info, 1306 unif_buf_ptr + unif_buf_used, 1307 unif_buf_size - unif_buf_used); 1308 else 1309 info->uname_type = uname_type; 1310 1311 if (NULL != params->opaque.value.str) 1312 { 1313 info->opaque = (char *) (unif_buf_ptr + unif_buf_used); 1314 info->opaque_len = get_rq_param_unquoted_copy_z (¶ms->opaque, 1315 info->opaque); 1316 unif_buf_used += info->opaque_len + 1; 1317 } 1318 if (NULL != params->realm.value.str) 1319 { 1320 info->realm = (char *) (unif_buf_ptr + unif_buf_used); 1321 info->realm_len = get_rq_param_unquoted_copy_z (¶ms->realm, 1322 info->realm); 1323 unif_buf_used += info->realm_len + 1; 1324 } 1325 1326 mhd_assert (unif_buf_size >= unif_buf_used); 1327 1328 info->qop = params->qop; 1329 1330 if (NULL != params->cnonce.value.str) 1331 info->cnonce_len = params->cnonce.value.len; 1332 else 1333 info->cnonce_len = 0; 1334 1335 nc_res = get_rq_nc (params, &info->nc); 1336 if (MHD_GET_RQ_NC_VALID != nc_res) 1337 info->nc = MHD_DIGEST_AUTH_INVALID_NC_VALUE; 1338 1339 return info; 1340 } 1341 1342 1343 /** 1344 * Get the username from Digest Authorization client's header. 1345 * 1346 * @param connection The MHD connection structure 1347 * @return NULL if no valid Digest Authorization header is used in the request, 1348 * or no username parameter is present in the header, or username is 1349 * provided incorrectly by client (see description for 1350 * #MHD_DIGEST_AUTH_UNAME_TYPE_INVALID); 1351 * a pointer structure with information if the valid request header 1352 * found, free using #MHD_free(). 1353 * @sa MHD_digest_auth_get_request_info3() provides more complete information 1354 * @note Available since #MHD_VERSION 0x00097701 1355 * @ingroup authentication 1356 */ 1357 _MHD_EXTERN struct MHD_DigestAuthUsernameInfo * 1358 MHD_digest_auth_get_username3 (struct MHD_Connection *connection) 1359 { 1360 const struct MHD_RqDAuth *params; 1361 struct MHD_DigestAuthUsernameInfo *uname_info; 1362 enum MHD_DigestAuthUsernameType uname_type; 1363 size_t unif_buf_size; 1364 uint8_t *unif_buf_ptr; 1365 size_t unif_buf_used; 1366 1367 params = MHD_get_rq_dauth_params_ (connection); 1368 if (NULL == params) 1369 return NULL; 1370 1371 uname_type = get_rq_uname_type (params); 1372 if ( (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) || 1373 (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) ) 1374 return NULL; 1375 1376 unif_buf_size = get_rq_unames_size (params, uname_type); 1377 1378 uname_info = (struct MHD_DigestAuthUsernameInfo *) 1379 MHD_calloc_ (1, (sizeof(struct MHD_DigestAuthUsernameInfo)) 1380 + unif_buf_size); 1381 if (NULL == uname_info) 1382 return NULL; 1383 unif_buf_ptr = (uint8_t *) (uname_info + 1); 1384 unif_buf_used = get_rq_uname (params, uname_type, uname_info, unif_buf_ptr, 1385 unif_buf_size); 1386 mhd_assert (unif_buf_size >= unif_buf_used); 1387 (void) unif_buf_used; /* Mute compiler warning on non-debug builds */ 1388 mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_info->uname_type); 1389 1390 if (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_info->uname_type) 1391 { 1392 free (uname_info); 1393 return NULL; 1394 } 1395 mhd_assert (uname_type == uname_info->uname_type); 1396 uname_info->algo3 = params->algo3; 1397 1398 return uname_info; 1399 } 1400 1401 1402 /** 1403 * Get the username from the authorization header sent by the client 1404 * 1405 * This function supports username in standard and extended notations. 1406 * "userhash" is not supported by this function. 1407 * 1408 * @param connection The MHD connection structure 1409 * @return NULL if no username could be found, username provided as 1410 * "userhash", extended notation broken or memory allocation error 1411 * occurs; 1412 * a pointer to the username if found, free using #MHD_free(). 1413 * @warning Returned value must be freed by #MHD_free(). 1414 * @sa #MHD_digest_auth_get_username3() 1415 * @ingroup authentication 1416 */ 1417 _MHD_EXTERN char * 1418 MHD_digest_auth_get_username (struct MHD_Connection *connection) 1419 { 1420 const struct MHD_RqDAuth *params; 1421 char *username; 1422 size_t buf_size; 1423 enum MHD_DigestAuthUsernameType uname_type; 1424 1425 params = MHD_get_rq_dauth_params_ (connection); 1426 if (NULL == params) 1427 return NULL; 1428 1429 uname_type = get_rq_uname_type (params); 1430 1431 if ( (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != uname_type) && 1432 (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != uname_type) ) 1433 return NULL; 1434 1435 buf_size = get_rq_unames_size (params, uname_type); 1436 1437 mhd_assert (0 != buf_size); 1438 1439 username = (char *) MHD_calloc_ (1, buf_size); 1440 if (NULL == username) 1441 return NULL; 1442 1443 if (1) 1444 { 1445 struct MHD_DigestAuthUsernameInfo uname_strct; 1446 size_t used; 1447 1448 memset (&uname_strct, 0, sizeof(uname_strct)); 1449 1450 used = get_rq_uname (params, uname_type, &uname_strct, 1451 (uint8_t *) username, buf_size); 1452 if (uname_type != uname_strct.uname_type) 1453 { /* Broken encoding for extended notation */ 1454 free (username); 1455 return NULL; 1456 } 1457 (void) used; /* Mute compiler warning for non-debug builds */ 1458 mhd_assert (buf_size >= used); 1459 } 1460 1461 return username; 1462 } 1463 1464 1465 /** 1466 * Calculate the server nonce so that it mitigates replay attacks 1467 * The current format of the nonce is ... 1468 * H(timestamp:random data:various parameters) + Hex(timestamp) 1469 * 1470 * @param nonce_time The amount of time in seconds for a nonce to be invalid 1471 * @param mthd_e HTTP method as enum value 1472 * @param method HTTP method as a string 1473 * @param rnd the pointer to a character array for the random seed 1474 * @param rnd_size The size of the random seed array @a rnd 1475 * @param saddr the pointer to the socket address structure 1476 * @param saddr_size the size of the socket address structure @a saddr 1477 * @param uri the HTTP URI (in MHD, without the arguments ("?k=v") 1478 * @param uri_len the length of the @a uri 1479 * @param first_header the pointer to the first request's header 1480 * @param realm A string of characters that describes the realm of auth. 1481 * @param realm_len the length of the @a realm. 1482 * @param bind_options the nonce bind options (#MHD_DAuthBindNonce values). 1483 * @param da digest algorithm to use 1484 * @param[out] nonce the pointer to a character array for the nonce to put in, 1485 * must provide NONCE_STD_LEN(digest_get_size(da)) bytes, 1486 * result is NOT zero-terminated 1487 */ 1488 static void 1489 calculate_nonce (uint64_t nonce_time, 1490 enum MHD_HTTP_Method mthd_e, 1491 const char *method, 1492 const char *rnd, 1493 size_t rnd_size, 1494 const struct sockaddr_storage *saddr, 1495 size_t saddr_size, 1496 const char *uri, 1497 size_t uri_len, 1498 const struct MHD_HTTP_Req_Header *first_header, 1499 const char *realm, 1500 size_t realm_len, 1501 unsigned int bind_options, 1502 struct DigestAlgorithm *da, 1503 char *nonce) 1504 { 1505 mhd_assert (! da->hashing); 1506 if (1) 1507 { 1508 /* Add the timestamp to the hash calculation */ 1509 uint8_t timestamp[TIMESTAMP_BIN_SIZE]; 1510 /* If the nonce_time is milliseconds, then the same 48 bit value will repeat 1511 * every 8 919 years, which is more than enough to mitigate a replay attack */ 1512 #if TIMESTAMP_BIN_SIZE != 6 1513 #error The code needs to be updated here 1514 #endif 1515 timestamp[0] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 0))); 1516 timestamp[1] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 1))); 1517 timestamp[2] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 2))); 1518 timestamp[3] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 3))); 1519 timestamp[4] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 4))); 1520 timestamp[5] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 5))); 1521 MHD_bin_to_hex (timestamp, 1522 sizeof (timestamp), 1523 nonce + digest_get_size (da) * 2); 1524 digest_update (da, 1525 timestamp, 1526 sizeof (timestamp)); 1527 } 1528 if (rnd_size > 0) 1529 { 1530 /* Add the unique random value to the hash calculation */ 1531 digest_update_with_colon (da); 1532 digest_update (da, 1533 rnd, 1534 rnd_size); 1535 } 1536 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) && 1537 (0 != saddr_size) ) 1538 { 1539 /* Add full client address including source port to make unique nonces 1540 * for requests received exactly at the same time */ 1541 digest_update_with_colon (da); 1542 digest_update (da, 1543 saddr, 1544 saddr_size); 1545 } 1546 if ( (0 != (bind_options & MHD_DAUTH_BIND_NONCE_CLIENT_IP)) && 1547 (0 != saddr_size) ) 1548 { 1549 /* Add the client's IP address to the hash calculation */ 1550 digest_update_with_colon (da); 1551 if (AF_INET == saddr->ss_family) 1552 digest_update (da, 1553 &((const struct sockaddr_in *) saddr)->sin_addr, 1554 sizeof(((const struct sockaddr_in *) saddr)->sin_addr)); 1555 #ifdef HAVE_INET6 1556 else if (AF_INET6 == saddr->ss_family) 1557 digest_update (da, 1558 &((const struct sockaddr_in6 *) saddr)->sin6_addr, 1559 sizeof(((const struct sockaddr_in6 *) saddr)->sin6_addr)); 1560 #endif /* HAVE_INET6 */ 1561 } 1562 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) || 1563 (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI))) 1564 { 1565 /* Add the request method to the hash calculation */ 1566 digest_update_with_colon (da); 1567 if (MHD_HTTP_MTHD_OTHER != mthd_e) 1568 { 1569 uint8_t mthd_for_hash; 1570 if (MHD_HTTP_MTHD_HEAD != mthd_e) 1571 mthd_for_hash = (uint8_t) mthd_e; 1572 else /* Treat HEAD method in the same way as GET method */ 1573 mthd_for_hash = (uint8_t) MHD_HTTP_MTHD_GET; 1574 digest_update (da, 1575 &mthd_for_hash, 1576 sizeof(mthd_for_hash)); 1577 } 1578 else 1579 digest_update_str (da, method); 1580 } 1581 1582 if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI)) 1583 { 1584 /* Add the request URI to the hash calculation */ 1585 digest_update_with_colon (da); 1586 1587 digest_update (da, 1588 uri, 1589 uri_len); 1590 } 1591 if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI_PARAMS)) 1592 { 1593 /* Add the request URI parameters to the hash calculation */ 1594 const struct MHD_HTTP_Req_Header *h; 1595 1596 digest_update_with_colon (da); 1597 for (h = first_header; NULL != h; h = h->next) 1598 { 1599 if (MHD_GET_ARGUMENT_KIND != h->kind) 1600 continue; 1601 digest_update (da, "\0", 2); 1602 if (0 != h->header_size) 1603 digest_update (da, h->header, h->header_size); 1604 digest_update (da, "", 1); 1605 if (0 != h->value_size) 1606 digest_update (da, h->value, h->value_size); 1607 } 1608 } 1609 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) || 1610 (0 != (bind_options & MHD_DAUTH_BIND_NONCE_REALM))) 1611 { 1612 /* Add the realm to the hash calculation */ 1613 digest_update_with_colon (da); 1614 digest_update (da, 1615 realm, 1616 realm_len); 1617 } 1618 if (1) 1619 { 1620 uint8_t hash[MAX_DIGEST]; 1621 digest_calc_hash (da, hash); 1622 MHD_bin_to_hex (hash, 1623 digest_get_size (da), 1624 nonce); 1625 } 1626 } 1627 1628 1629 /** 1630 * Check whether it is possible to use slot in nonce-nc map array. 1631 * 1632 * Should be called with mutex held to avoid external modification of 1633 * the slot data. 1634 * 1635 * @param nn the pointer to the nonce-nc slot 1636 * @param now the current time 1637 * @param new_nonce the new nonce supposed to be stored in this slot, 1638 * zero-terminated 1639 * @param new_nonce_len the length of the @a new_nonce in chars, not including 1640 * the terminating zero. 1641 * @return true if the slot can be used to store the new nonce, 1642 * false otherwise. 1643 */ 1644 static bool 1645 is_slot_available (const struct MHD_NonceNc *const nn, 1646 const uint64_t now, 1647 const char *const new_nonce, 1648 size_t new_nonce_len) 1649 { 1650 uint64_t timestamp; 1651 bool timestamp_valid; 1652 mhd_assert (new_nonce_len <= NONCE_STD_LEN (MAX_DIGEST)); 1653 mhd_assert (NONCE_STD_LEN (MAX_DIGEST) <= MAX_DIGEST_NONCE_LENGTH); 1654 if (0 == nn->nonce[0]) 1655 return true; /* The slot is empty */ 1656 1657 if (0 == memcmp (nn->nonce, new_nonce, new_nonce_len)) 1658 { 1659 /* The slot has the same nonce already. This nonce cannot be registered 1660 * again as it would just clear 'nc' usage history. */ 1661 return false; 1662 } 1663 1664 if (0 != nn->nc) 1665 return true; /* Client already used the nonce in this slot at least 1666 one time, re-use the slot */ 1667 1668 /* The nonce must be zero-terminated */ 1669 mhd_assert (0 == nn->nonce[sizeof(nn->nonce) - 1]); 1670 if (0 != nn->nonce[sizeof(nn->nonce) - 1]) 1671 return true; /* Wrong nonce format in the slot */ 1672 1673 timestamp_valid = get_nonce_timestamp (nn->nonce, 0, ×tamp); 1674 mhd_assert (timestamp_valid); 1675 if (! timestamp_valid) 1676 return true; /* Invalid timestamp in nonce-nc, should not be possible */ 1677 1678 if ((REUSE_TIMEOUT * 1000) < TRIM_TO_TIMESTAMP (now - timestamp)) 1679 return true; 1680 1681 return false; 1682 } 1683 1684 1685 /** 1686 * Calculate the server nonce so that it mitigates replay attacks and add 1687 * the new nonce to the nonce-nc map array. 1688 * 1689 * @param connection the MHD connection structure 1690 * @param timestamp the current timestamp 1691 * @param realm the string of characters that describes the realm of auth 1692 * @param realm_len the length of the @a realm 1693 * @param da the digest algorithm to use 1694 * @param[out] nonce the pointer to a character array for the nonce to put in, 1695 * must provide NONCE_STD_LEN(digest_get_size(da)) bytes, 1696 * result is NOT zero-terminated 1697 * @return true if the new nonce has been added to the nonce-nc map array, 1698 * false otherwise. 1699 */ 1700 static bool 1701 calculate_add_nonce (struct MHD_Connection *const connection, 1702 uint64_t timestamp, 1703 const char *realm, 1704 size_t realm_len, 1705 struct DigestAlgorithm *da, 1706 char *nonce) 1707 { 1708 struct MHD_Daemon *const daemon = MHD_get_master (connection->daemon); 1709 struct MHD_NonceNc *nn; 1710 const size_t nonce_size = NONCE_STD_LEN (digest_get_size (da)); 1711 bool ret; 1712 1713 mhd_assert (! da->hashing); 1714 mhd_assert (MAX_DIGEST_NONCE_LENGTH >= nonce_size); 1715 mhd_assert (0 != nonce_size); 1716 1717 calculate_nonce (timestamp, 1718 connection->rq.http_mthd, 1719 connection->rq.method, 1720 daemon->digest_auth_random, 1721 daemon->digest_auth_rand_size, 1722 connection->addr, 1723 (size_t) connection->addr_len, 1724 connection->rq.url, 1725 connection->rq.url_len, 1726 connection->rq.headers_received, 1727 realm, 1728 realm_len, 1729 daemon->dauth_bind_type, 1730 da, 1731 nonce); 1732 1733 #ifdef MHD_DIGEST_HAS_EXT_ERROR 1734 if (digest_ext_error (da)) 1735 return false; 1736 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 1737 1738 if (0 == daemon->nonce_nc_size) 1739 return false; 1740 1741 /* Sanity check for values */ 1742 mhd_assert (MAX_DIGEST_NONCE_LENGTH == NONCE_STD_LEN (MAX_DIGEST)); 1743 1744 nn = daemon->nnc + get_nonce_nc_idx (daemon->nonce_nc_size, 1745 nonce, 1746 nonce_size); 1747 1748 MHD_mutex_lock_chk_ (&daemon->nnc_lock); 1749 if (is_slot_available (nn, timestamp, nonce, nonce_size)) 1750 { 1751 memcpy (nn->nonce, 1752 nonce, 1753 nonce_size); 1754 nn->nonce[nonce_size] = 0; /* With terminating zero */ 1755 nn->nc = 0; 1756 nn->nmask = 0; 1757 ret = true; 1758 } 1759 else 1760 ret = false; 1761 MHD_mutex_unlock_chk_ (&daemon->nnc_lock); 1762 1763 return ret; 1764 } 1765 1766 1767 MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ 1768 1769 /** 1770 * Calculate the server nonce so that it mitigates replay attacks and add 1771 * the new nonce to the nonce-nc map array. 1772 * 1773 * @param connection the MHD connection structure 1774 * @param realm A string of characters that describes the realm of auth. 1775 * @param da digest algorithm to use 1776 * @param[out] nonce the pointer to a character array for the nonce to put in, 1777 * must provide NONCE_STD_LEN(digest_get_size(da)) bytes, 1778 * result is NOT zero-terminated 1779 */ 1780 static bool 1781 calculate_add_nonce_with_retry (struct MHD_Connection *const connection, 1782 const char *realm, 1783 struct DigestAlgorithm *da, 1784 char *nonce) 1785 { 1786 const uint64_t timestamp1 = MHD_monotonic_msec_counter (); 1787 const size_t realm_len = strlen (realm); 1788 mhd_assert (! da->hashing); 1789 1790 #ifdef HAVE_MESSAGES 1791 if (0 == MHD_get_master (connection->daemon)->digest_auth_rand_size) 1792 MHD_DLOG (connection->daemon, 1793 _ ("Random value was not initialised by " \ 1794 "MHD_OPTION_DIGEST_AUTH_RANDOM or " \ 1795 "MHD_OPTION_DIGEST_AUTH_RANDOM_COPY, generated nonces " \ 1796 "are predictable.\n")); 1797 #endif 1798 1799 if (! calculate_add_nonce (connection, timestamp1, realm, realm_len, da, 1800 nonce)) 1801 { 1802 /* Either: 1803 * 1. The same nonce was already generated. If it will be used then one 1804 * of the clients will fail (as no initial 'nc' value could be given to 1805 * the client, the second client which will use 'nc=00000001' will fail). 1806 * 2. Another nonce uses the same slot, and this nonce never has been 1807 * used by the client and this nonce is still fresh enough. 1808 */ 1809 const size_t digest_size = digest_get_size (da); 1810 char nonce2[NONCE_STD_LEN (MAX_DIGEST) + 1]; 1811 uint64_t timestamp2; 1812 #ifdef MHD_DIGEST_HAS_EXT_ERROR 1813 if (digest_ext_error (da)) 1814 return false; /* No need to re-try */ 1815 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 1816 if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) 1817 return false; /* No need to re-try */ 1818 1819 timestamp2 = MHD_monotonic_msec_counter (); 1820 if (timestamp1 == timestamp2) 1821 { 1822 /* The timestamps are equal, need to generate some arbitrary 1823 * difference for nonce. */ 1824 /* As the number is needed only to differentiate clients, weak 1825 * pseudo-random generators could be used. Seeding is not needed. */ 1826 uint64_t base1; 1827 uint32_t base2; 1828 uint16_t base3; 1829 uint8_t base4; 1830 #ifdef HAVE_RANDOM 1831 base1 = ((uint64_t) random ()) ^ UINT64_C (0x54a5acff5be47e63); 1832 base4 = 0xb8; 1833 #elif defined(HAVE_RAND) 1834 base1 = ((uint64_t) rand ()) ^ UINT64_C (0xc4bcf553b12f3965); 1835 base4 = 0x92; 1836 #else 1837 /* Monotonic msec counter alone does not really help here as it is already 1838 known that this value is not unique. */ 1839 base1 = ((uint64_t) (uintptr_t) nonce2) ^ UINT64_C (0xf2e1b21bc6c92655); 1840 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1); 1841 base2 = _MHD_ROTR32 (base2, 4); 1842 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2); 1843 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3); 1844 base1 = ((uint64_t) MHD_monotonic_msec_counter ()) 1845 ^ UINT64_C (0xccab93f72cf5b15); 1846 #endif 1847 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1); 1848 base2 = _MHD_ROTL32 (base2, (((base4 >> 4) ^ base4) % 32)); 1849 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2); 1850 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3); 1851 /* Use up to 127 ms difference */ 1852 timestamp2 -= (base4 & DAUTH_JUMPBACK_MAX); 1853 if (timestamp1 == timestamp2) 1854 timestamp2 -= 2; /* Fallback value */ 1855 } 1856 digest_reset (da); 1857 if (! calculate_add_nonce (connection, timestamp2, realm, realm_len, da, 1858 nonce2)) 1859 { 1860 /* No free slot has been found. Re-tries are expensive, just use 1861 * the generated nonce. As it is not stored in nonce-nc map array, 1862 * the next request of the client will be recognized as valid, but 'stale' 1863 * so client should re-try automatically. */ 1864 return false; 1865 } 1866 memcpy (nonce, nonce2, NONCE_STD_LEN (digest_size)); 1867 } 1868 return true; 1869 } 1870 1871 1872 MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ 1873 1874 /** 1875 * Calculate userdigest, return it as binary data. 1876 * 1877 * It is equal to H(A1) for non-session algorithms. 1878 * 1879 * MHD internal version. 1880 * 1881 * @param da the digest algorithm 1882 * @param username the username to use 1883 * @param username_len the length of the @a username 1884 * @param realm the realm to use 1885 * @param realm_len the length of the @a realm 1886 * @param password the password, must be zero-terminated 1887 * @param[out] ha1_bin the output buffer, must have at least 1888 * #digest_get_size(da) bytes available 1889 */ 1890 _MHD_static_inline void 1891 calc_userdigest (struct DigestAlgorithm *da, 1892 const char *username, const size_t username_len, 1893 const char *realm, const size_t realm_len, 1894 const char *password, 1895 uint8_t *ha1_bin) 1896 { 1897 mhd_assert (! da->hashing); 1898 digest_update (da, username, username_len); 1899 digest_update_with_colon (da); 1900 digest_update (da, realm, realm_len); 1901 digest_update_with_colon (da); 1902 digest_update_str (da, password); 1903 digest_calc_hash (da, ha1_bin); 1904 } 1905 1906 1907 /** 1908 * Calculate userdigest, return it as a binary data. 1909 * 1910 * The "userdigest" is the hash of the "username:realm:password" string. 1911 * 1912 * The "userdigest" can be used to avoid storing the password in clear text 1913 * in database/files 1914 * 1915 * This function is designed to improve security of stored credentials, 1916 * the "userdigest" does not improve security of the authentication process. 1917 * 1918 * The results can be used to store username & userdigest pairs instead of 1919 * username & password pairs. To further improve security, application may 1920 * store username & userhash & userdigest triplets. 1921 * 1922 * @param algo3 the digest algorithm 1923 * @param username the username 1924 * @param realm the realm 1925 * @param password the password 1926 * @param[out] userdigest_bin the output buffer for userdigest; 1927 * if this function succeeds, then this buffer has 1928 * #MHD_digest_get_hash_size(algo3) bytes of 1929 * userdigest upon return 1930 * @param bin_buf_size the size of the @a userdigest_bin buffer, must be 1931 * at least #MHD_digest_get_hash_size(algo3) bytes long 1932 * @return MHD_YES on success, 1933 * MHD_NO if @a userdigest_bin is too small or if @a algo3 algorithm is 1934 * not supported (or external error has occurred, 1935 * see #MHD_FEATURE_EXTERN_HASH). 1936 * @sa #MHD_digest_auth_check_digest3() 1937 * @note Available since #MHD_VERSION 0x00097701 1938 * @ingroup authentication 1939 */ 1940 _MHD_EXTERN enum MHD_Result 1941 MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo3 algo3, 1942 const char *username, 1943 const char *realm, 1944 const char *password, 1945 void *userdigest_bin, 1946 size_t bin_buf_size) 1947 { 1948 struct DigestAlgorithm da; 1949 enum MHD_Result ret; 1950 if (! digest_init_one_time (&da, get_base_digest_algo (algo3))) 1951 return MHD_NO; 1952 1953 if (digest_get_size (&da) > bin_buf_size) 1954 ret = MHD_NO; 1955 else 1956 { 1957 calc_userdigest (&da, 1958 username, 1959 strlen (username), 1960 realm, 1961 strlen (realm), 1962 password, 1963 userdigest_bin); 1964 ret = MHD_YES; 1965 1966 #ifdef MHD_DIGEST_HAS_EXT_ERROR 1967 if (digest_ext_error (&da)) 1968 ret = MHD_NO; 1969 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 1970 } 1971 digest_deinit (&da); 1972 1973 return ret; 1974 } 1975 1976 1977 /** 1978 * Calculate userhash, return it as binary data. 1979 * 1980 * MHD internal version. 1981 * 1982 * @param da the digest algorithm 1983 * @param username the username to use 1984 * @param username_len the length of the @a username 1985 * @param realm the realm to use 1986 * @param realm_len the length of the @a realm 1987 * @param[out] digest_bin the output buffer, must have at least 1988 * #MHD_digest_get_hash_size(algo3) bytes available 1989 */ 1990 _MHD_static_inline void 1991 calc_userhash (struct DigestAlgorithm *da, 1992 const char *username, const size_t username_len, 1993 const char *realm, const size_t realm_len, 1994 uint8_t *digest_bin) 1995 { 1996 mhd_assert (NULL != username); 1997 mhd_assert (! da->hashing); 1998 digest_update (da, username, username_len); 1999 digest_update_with_colon (da); 2000 digest_update (da, realm, realm_len); 2001 digest_calc_hash (da, digest_bin); 2002 } 2003 2004 2005 /** 2006 * Calculate "userhash", return it as binary data. 2007 * 2008 * The "userhash" is the hash of the string "username:realm". 2009 * 2010 * The "userhash" could be used to avoid sending username in cleartext in Digest 2011 * Authorization client's header. 2012 * 2013 * Userhash is not designed to hide the username in local database or files, 2014 * as username in cleartext is required for #MHD_digest_auth_check3() function 2015 * to check the response, but it can be used to hide username in HTTP headers. 2016 * 2017 * This function could be used when the new username is added to the username 2018 * database to save the "userhash" alongside with the username (preferably) or 2019 * when loading list of the usernames to generate the userhash for every loaded 2020 * username (this will cause delays at the start with the long lists). 2021 * 2022 * Once "userhash" is generated it could be used to identify users by clients 2023 * with "userhash" support. 2024 * Avoid repetitive usage of this function for the same username/realm 2025 * combination as it will cause excessive CPU load; save and re-use the result 2026 * instead. 2027 * 2028 * @param algo3 the algorithm for userhash calculations 2029 * @param username the username 2030 * @param realm the realm 2031 * @param[out] userhash_bin the output buffer for userhash as binary data; 2032 * if this function succeeds, then this buffer has 2033 * #MHD_digest_get_hash_size(algo3) bytes of userhash 2034 * upon return 2035 * @param bin_buf_size the size of the @a userhash_bin buffer, must be 2036 * at least #MHD_digest_get_hash_size(algo3) bytes long 2037 * @return MHD_YES on success, 2038 * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is 2039 * not supported (or external error has occurred, 2040 * see #MHD_FEATURE_EXTERN_HASH) 2041 * @sa #MHD_digest_auth_calc_userhash_hex() 2042 * @note Available since #MHD_VERSION 0x00097701 2043 * @ingroup authentication 2044 */ 2045 _MHD_EXTERN enum MHD_Result 2046 MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3, 2047 const char *username, 2048 const char *realm, 2049 void *userhash_bin, 2050 size_t bin_buf_size) 2051 { 2052 struct DigestAlgorithm da; 2053 enum MHD_Result ret; 2054 2055 if (! digest_init_one_time (&da, get_base_digest_algo (algo3))) 2056 return MHD_NO; 2057 if (digest_get_size (&da) > bin_buf_size) 2058 ret = MHD_NO; 2059 else 2060 { 2061 calc_userhash (&da, 2062 username, 2063 strlen (username), 2064 realm, 2065 strlen (realm), 2066 userhash_bin); 2067 ret = MHD_YES; 2068 2069 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2070 if (digest_ext_error (&da)) 2071 ret = MHD_NO; 2072 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2073 } 2074 digest_deinit (&da); 2075 2076 return ret; 2077 } 2078 2079 2080 /** 2081 * Calculate "userhash", return it as hexadecimal string. 2082 * 2083 * The "userhash" is the hash of the string "username:realm". 2084 * 2085 * The "userhash" could be used to avoid sending username in cleartext in Digest 2086 * Authorization client's header. 2087 * 2088 * Userhash is not designed to hide the username in local database or files, 2089 * as username in cleartext is required for #MHD_digest_auth_check3() function 2090 * to check the response, but it can be used to hide username in HTTP headers. 2091 * 2092 * This function could be used when the new username is added to the username 2093 * database to save the "userhash" alongside with the username (preferably) or 2094 * when loading list of the usernames to generate the userhash for every loaded 2095 * username (this will cause delays at the start with the long lists). 2096 * 2097 * Once "userhash" is generated it could be used to identify users by clients 2098 * with "userhash" support. 2099 * Avoid repetitive usage of this function for the same username/realm 2100 * combination as it will cause excessive CPU load; save and re-use the result 2101 * instead. 2102 * 2103 * @param algo3 the algorithm for userhash calculations 2104 * @param username the username 2105 * @param realm the realm 2106 * @param[out] userhash_hex the output buffer for userhash as hex string; 2107 * if this function succeeds, then this buffer has 2108 * #MHD_digest_get_hash_size(algo3)*2 chars long 2109 * userhash zero-terminated string 2110 * @param bin_buf_size the size of the @a userhash_bin buffer, must be 2111 * at least #MHD_digest_get_hash_size(algo3)*2+1 chars long 2112 * @return MHD_YES on success, 2113 * MHD_NO if @a bin_buf_size is too small or if @a algo3 algorithm is 2114 * not supported (or external error has occurred, 2115 * see #MHD_FEATURE_EXTERN_HASH). 2116 * @sa #MHD_digest_auth_calc_userhash() 2117 * @note Available since #MHD_VERSION 0x00097701 2118 * @ingroup authentication 2119 */ 2120 _MHD_EXTERN enum MHD_Result 2121 MHD_digest_auth_calc_userhash_hex (enum MHD_DigestAuthAlgo3 algo3, 2122 const char *username, 2123 const char *realm, 2124 char *userhash_hex, 2125 size_t hex_buf_size) 2126 { 2127 uint8_t userhash_bin[MAX_DIGEST]; 2128 size_t digest_size; 2129 2130 digest_size = digest_get_hash_size (algo3); 2131 if (digest_size * 2 + 1 > hex_buf_size) 2132 return MHD_NO; 2133 if (MHD_NO == MHD_digest_auth_calc_userhash (algo3, username, realm, 2134 userhash_bin, MAX_DIGEST)) 2135 return MHD_NO; 2136 2137 MHD_bin_to_hex_z (userhash_bin, digest_size, userhash_hex); 2138 return MHD_YES; 2139 } 2140 2141 2142 struct test_header_param 2143 { 2144 struct MHD_Connection *connection; 2145 size_t num_headers; 2146 }; 2147 2148 /** 2149 * Test if the given key-value pair is in the headers for the 2150 * given connection. 2151 * 2152 * @param cls the test context 2153 * @param key the key 2154 * @param key_size number of bytes in @a key 2155 * @param value the value, can be NULL 2156 * @param value_size number of bytes in @a value 2157 * @param kind type of the header 2158 * @return #MHD_YES if the key-value pair is in the headers, 2159 * #MHD_NO if not 2160 */ 2161 static enum MHD_Result 2162 test_header (void *cls, 2163 const char *key, 2164 size_t key_size, 2165 const char *value, 2166 size_t value_size, 2167 enum MHD_ValueKind kind) 2168 { 2169 struct test_header_param *const param = (struct test_header_param *) cls; 2170 struct MHD_Connection *connection = param->connection; 2171 struct MHD_HTTP_Req_Header *pos; 2172 size_t i; 2173 2174 param->num_headers++; 2175 i = 0; 2176 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next) 2177 { 2178 if (kind != pos->kind) 2179 continue; 2180 if (++i == param->num_headers) 2181 { 2182 if (key_size != pos->header_size) 2183 return MHD_NO; 2184 if (value_size != pos->value_size) 2185 return MHD_NO; 2186 if (0 != key_size) 2187 { 2188 mhd_assert (NULL != key); 2189 mhd_assert (NULL != pos->header); 2190 if (0 != memcmp (key, 2191 pos->header, 2192 key_size)) 2193 return MHD_NO; 2194 } 2195 if (0 != value_size) 2196 { 2197 mhd_assert (NULL != value); 2198 mhd_assert (NULL != pos->value); 2199 if (0 != memcmp (value, 2200 pos->value, 2201 value_size)) 2202 return MHD_NO; 2203 } 2204 return MHD_YES; 2205 } 2206 } 2207 return MHD_NO; 2208 } 2209 2210 2211 /** 2212 * Check that the arguments given by the client as part 2213 * of the authentication header match the arguments we 2214 * got as part of the HTTP request URI. 2215 * 2216 * @param connection connections with headers to compare against 2217 * @param args the copy of argument URI string (after "?" in URI), will be 2218 * modified by this function 2219 * @return boolean true if the arguments match, 2220 * boolean false if not 2221 */ 2222 static bool 2223 check_argument_match (struct MHD_Connection *connection, 2224 char *args) 2225 { 2226 struct MHD_HTTP_Req_Header *pos; 2227 enum MHD_Result ret; 2228 struct test_header_param param; 2229 2230 param.connection = connection; 2231 param.num_headers = 0; 2232 ret = MHD_parse_arguments_ (connection, 2233 MHD_GET_ARGUMENT_KIND, 2234 args, 2235 &test_header, 2236 ¶m); 2237 if (MHD_NO == ret) 2238 { 2239 return false; 2240 } 2241 /* also check that the number of headers matches */ 2242 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next) 2243 { 2244 if (MHD_GET_ARGUMENT_KIND != pos->kind) 2245 continue; 2246 param.num_headers--; 2247 } 2248 if (0 != param.num_headers) 2249 { 2250 /* argument count mismatch */ 2251 return false; 2252 } 2253 return true; 2254 } 2255 2256 2257 /** 2258 * Check that the URI provided by the client as part 2259 * of the authentication header match the real HTTP request URI. 2260 * 2261 * @param connection connections with headers to compare against 2262 * @param uri the copy of URI in the authentication header, should point to 2263 * modifiable buffer at least @a uri_len + 1 characters long, 2264 * will be modified by this function, not valid upon return 2265 * @param uri_len the length of the @a uri string in characters 2266 * @return boolean true if the URIs match, 2267 * boolean false if not 2268 */ 2269 static bool 2270 check_uri_match (struct MHD_Connection *connection, char *uri, size_t uri_len) 2271 { 2272 char *qmark; 2273 char *args; 2274 struct MHD_Daemon *const daemon = connection->daemon; 2275 2276 uri[uri_len] = 0; 2277 qmark = memchr (uri, 2278 '?', 2279 uri_len); 2280 if (NULL != qmark) 2281 *qmark = '\0'; 2282 2283 /* Need to unescape URI before comparing with connection->url */ 2284 uri_len = daemon->unescape_callback (daemon->unescape_callback_cls, 2285 connection, 2286 uri); 2287 if ((uri_len != connection->rq.url_len) || 2288 (0 != memcmp (uri, connection->rq.url, uri_len))) 2289 { 2290 #ifdef HAVE_MESSAGES 2291 MHD_DLOG (daemon, 2292 _ ("Authentication failed, URI does not match.\n")); 2293 #endif 2294 return false; 2295 } 2296 2297 args = (NULL != qmark) ? (qmark + 1) : uri + uri_len; 2298 2299 if (! check_argument_match (connection, 2300 args) ) 2301 { 2302 #ifdef HAVE_MESSAGES 2303 MHD_DLOG (daemon, 2304 _ ("Authentication failed, arguments do not match.\n")); 2305 #endif 2306 return false; 2307 } 2308 return true; 2309 } 2310 2311 2312 /** 2313 * The size of the unquoting buffer in stack 2314 */ 2315 #define _MHD_STATIC_UNQ_BUFFER_SIZE 128 2316 2317 2318 /** 2319 * Get the pointer to buffer with required size 2320 * @param tmp1 the first buffer with fixed size 2321 * @param ptmp2 the pointer to pointer to malloc'ed buffer 2322 * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2323 * @param required_size the required size in buffer 2324 * @return the pointer to the buffer or NULL if failed to allocate buffer with 2325 * requested size 2326 */ 2327 static char * 2328 get_buffer_for_size (char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], 2329 char **ptmp2, 2330 size_t *ptmp2_size, 2331 size_t required_size) 2332 { 2333 mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); 2334 mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); 2335 mhd_assert ((0 == *ptmp2_size) || \ 2336 (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); 2337 2338 if (required_size <= _MHD_STATIC_UNQ_BUFFER_SIZE) 2339 return tmp1; 2340 2341 if (required_size <= *ptmp2_size) 2342 return *ptmp2; 2343 2344 if (required_size > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) 2345 return NULL; 2346 if (NULL != *ptmp2) 2347 free (*ptmp2); 2348 *ptmp2 = (char *) malloc (required_size); 2349 if (NULL == *ptmp2) 2350 *ptmp2_size = 0; 2351 else 2352 *ptmp2_size = required_size; 2353 return *ptmp2; 2354 } 2355 2356 2357 /** 2358 * The result of parameter unquoting 2359 */ 2360 enum _MHD_GetUnqResult 2361 { 2362 _MHD_UNQ_OK = 0, /**< Got unquoted string */ 2363 _MHD_UNQ_TOO_LARGE = -7, /**< The string is too large to unquote */ 2364 _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */ 2365 }; 2366 2367 /** 2368 * Get Digest authorisation parameter as unquoted string. 2369 * @param param the parameter to process 2370 * @param tmp1 the small buffer in stack 2371 * @param ptmp2 the pointer to pointer to malloc'ed buffer 2372 * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2373 * @param[out] unquoted the pointer to store the result, NOT zero terminated 2374 * @return enum code indicating result of the process 2375 */ 2376 static enum _MHD_GetUnqResult 2377 get_unquoted_param (const struct MHD_RqDAuthParam *param, 2378 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], 2379 char **ptmp2, 2380 size_t *ptmp2_size, 2381 struct _MHD_str_w_len *unquoted) 2382 { 2383 char *str; 2384 size_t len; 2385 mhd_assert (NULL != param->value.str); 2386 mhd_assert (0 != param->value.len); 2387 2388 if (! param->quoted) 2389 { 2390 unquoted->str = param->value.str; 2391 unquoted->len = param->value.len; 2392 return _MHD_UNQ_OK; 2393 } 2394 /* The value is present and is quoted, needs to be copied and unquoted */ 2395 str = get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len); 2396 if (NULL == str) 2397 return (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? 2398 _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; 2399 2400 len = MHD_str_unquote (param->value.str, param->value.len, str); 2401 unquoted->str = str; 2402 unquoted->len = len; 2403 mhd_assert (0 != unquoted->len); 2404 mhd_assert (unquoted->len < param->value.len); 2405 return _MHD_UNQ_OK; 2406 } 2407 2408 2409 /** 2410 * Get copy of Digest authorisation parameter as unquoted string. 2411 * @param param the parameter to process 2412 * @param tmp1 the small buffer in stack 2413 * @param ptmp2 the pointer to pointer to malloc'ed buffer 2414 * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2415 * @param[out] unquoted the pointer to store the result, NOT zero terminated, 2416 * but with enough space to zero-terminate 2417 * @return enum code indicating result of the process 2418 */ 2419 static enum _MHD_GetUnqResult 2420 get_unquoted_param_copy (const struct MHD_RqDAuthParam *param, 2421 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], 2422 char **ptmp2, 2423 size_t *ptmp2_size, 2424 struct _MHD_mstr_w_len *unquoted) 2425 { 2426 mhd_assert (NULL != param->value.str); 2427 mhd_assert (0 != param->value.len); 2428 2429 /* The value is present and is quoted, needs to be copied and unquoted */ 2430 /* Allocate buffer with one more additional byte for zero-termination */ 2431 unquoted->str = 2432 get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len + 1); 2433 2434 if (NULL == unquoted->str) 2435 return (param->value.len + 1 > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ? 2436 _MHD_UNQ_TOO_LARGE : _MHD_UNQ_OUT_OF_MEM; 2437 2438 if (! param->quoted) 2439 { 2440 memcpy (unquoted->str, param->value.str, param->value.len); 2441 unquoted->len = param->value.len; 2442 return _MHD_UNQ_OK; 2443 } 2444 2445 unquoted->len = 2446 MHD_str_unquote (param->value.str, param->value.len, unquoted->str); 2447 mhd_assert (0 != unquoted->len); 2448 mhd_assert (unquoted->len < param->value.len); 2449 return _MHD_UNQ_OK; 2450 } 2451 2452 2453 /** 2454 * Check whether Digest Auth request parameter is equal to given string 2455 * @param param the parameter to check 2456 * @param str the string to compare with, does not need to be zero-terminated 2457 * @param str_len the length of the @a str 2458 * @return true is parameter is equal to the given string, 2459 * false otherwise 2460 */ 2461 _MHD_static_inline bool 2462 is_param_equal (const struct MHD_RqDAuthParam *param, 2463 const char *const str, 2464 const size_t str_len) 2465 { 2466 mhd_assert (NULL != param->value.str); 2467 mhd_assert (0 != param->value.len); 2468 if (param->quoted) 2469 return MHD_str_equal_quoted_bin_n (param->value.str, param->value.len, 2470 str, str_len); 2471 return (str_len == param->value.len) && 2472 (0 == memcmp (str, param->value.str, str_len)); 2473 2474 } 2475 2476 2477 /** 2478 * Check whether Digest Auth request parameter is caseless equal to given string 2479 * @param param the parameter to check 2480 * @param str the string to compare with, does not need to be zero-terminated 2481 * @param str_len the length of the @a str 2482 * @return true is parameter is caseless equal to the given string, 2483 * false otherwise 2484 */ 2485 _MHD_static_inline bool 2486 is_param_equal_caseless (const struct MHD_RqDAuthParam *param, 2487 const char *const str, 2488 const size_t str_len) 2489 { 2490 mhd_assert (NULL != param->value.str); 2491 mhd_assert (0 != param->value.len); 2492 if (param->quoted) 2493 return MHD_str_equal_caseless_quoted_bin_n (param->value.str, 2494 param->value.len, 2495 str, 2496 str_len); 2497 return (str_len == param->value.len) && 2498 (MHD_str_equal_caseless_bin_n_ (str, 2499 param->value.str, 2500 str_len)); 2501 } 2502 2503 2504 /** 2505 * Authenticates the authorization header sent by the client 2506 * 2507 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 2508 * @a mqop and the client uses this mode, then server generated nonces are 2509 * used as one-time nonces because nonce-count is not supported in this old RFC. 2510 * Communication in this mode is very inefficient, especially if the client 2511 * requests several resources one-by-one as for every request new nonce must be 2512 * generated and client repeat all requests twice (the first time to get a new 2513 * nonce and the second time to perform an authorised request). 2514 * 2515 * @param connection the MHD connection structure 2516 * @param realm the realm for authorization of the client 2517 * @param username the username to be authenticated, must be in clear text 2518 * even if userhash is used by the client 2519 * @param password the password used in the authentication, 2520 * must be NULL if @a userdigest is not NULL 2521 * @param userdigest the precalculated binary hash of the string 2522 * "username:realm:password", 2523 * must be NULL if @a password is not NULL 2524 * @param nonce_timeout the period of seconds since nonce generation, when 2525 * the nonce is recognised as valid and not stale; 2526 * unlike #digest_auth_check_all() zero is used literally 2527 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 2528 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 2529 * returned; 2530 * unlike #digest_auth_check_all() zero is treated as "no limit" 2531 * @param mqop the QOP to use 2532 * @param malgo3 digest algorithms allowed to use, fail if algorithm specified 2533 * by the client is not allowed by this parameter 2534 * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, 2535 * to be freed if not NULL upon return 2536 * @return #MHD_DAUTH_OK if authenticated, 2537 * error code otherwise. 2538 * @ingroup authentication 2539 */ 2540 static enum MHD_DigestAuthResult 2541 digest_auth_check_all_inner (struct MHD_Connection *connection, 2542 const char *realm, 2543 const char *username, 2544 const char *password, 2545 const uint8_t *userdigest, 2546 unsigned int nonce_timeout, 2547 uint32_t max_nc, 2548 enum MHD_DigestAuthMultiQOP mqop, 2549 enum MHD_DigestAuthMultiAlgo3 malgo3, 2550 char **pbuf, 2551 struct DigestAlgorithm *da) 2552 { 2553 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); 2554 enum MHD_DigestAuthAlgo3 c_algo; /**< Client's algorithm */ 2555 enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */ 2556 unsigned int digest_size; 2557 uint8_t hash1_bin[MAX_DIGEST]; 2558 uint8_t hash2_bin[MAX_DIGEST]; 2559 #if 0 2560 const char *hentity = NULL; /* "auth-int" is not supported */ 2561 #endif 2562 uint64_t nonce_time; 2563 uint64_t nci; 2564 const struct MHD_RqDAuth *params; 2565 /** 2566 * Temporal buffer in stack for unquoting and other needs 2567 */ 2568 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE]; 2569 char **const ptmp2 = pbuf; /**< Temporal malloc'ed buffer for unquoting */ 2570 size_t tmp2_size; /**< The size of @a tmp2 buffer */ 2571 struct _MHD_str_w_len unquoted; 2572 struct _MHD_mstr_w_len unq_copy; 2573 enum _MHD_GetUnqResult unq_res; 2574 size_t username_len; 2575 size_t realm_len; 2576 2577 mhd_assert ((NULL != password) || (NULL != userdigest)); 2578 mhd_assert (! ((NULL != userdigest) && (NULL != password))); 2579 2580 tmp2_size = 0; 2581 2582 params = MHD_get_rq_dauth_params_ (connection); 2583 if (NULL == params) 2584 return MHD_DAUTH_WRONG_HEADER; 2585 2586 /* ** Initial parameters checks and setup ** */ 2587 /* Get client's algorithm */ 2588 c_algo = params->algo3; 2589 /* Check whether client's algorithm is allowed by function parameter */ 2590 if (((unsigned int) c_algo) != 2591 (((unsigned int) c_algo) & ((unsigned int) malgo3))) 2592 return MHD_DAUTH_WRONG_ALGO; 2593 /* Check whether client's algorithm is supported */ 2594 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_AUTH_ALGO3_SESSION)) 2595 { 2596 #ifdef HAVE_MESSAGES 2597 MHD_DLOG (connection->daemon, 2598 _ ("The 'session' algorithms are not supported.\n")); 2599 #endif /* HAVE_MESSAGES */ 2600 return MHD_DAUTH_WRONG_ALGO; 2601 } 2602 #ifndef MHD_MD5_SUPPORT 2603 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5)) 2604 { 2605 #ifdef HAVE_MESSAGES 2606 MHD_DLOG (connection->daemon, 2607 _ ("The MD5 algorithm is not supported by this MHD build.\n")); 2608 #endif /* HAVE_MESSAGES */ 2609 return MHD_DAUTH_WRONG_ALGO; 2610 } 2611 #endif /* ! MHD_MD5_SUPPORT */ 2612 #ifndef MHD_SHA256_SUPPORT 2613 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA256)) 2614 { 2615 #ifdef HAVE_MESSAGES 2616 MHD_DLOG (connection->daemon, 2617 _ ("The SHA-256 algorithm is not supported by " 2618 "this MHD build.\n")); 2619 #endif /* HAVE_MESSAGES */ 2620 return MHD_DAUTH_WRONG_ALGO; 2621 } 2622 #endif /* ! MHD_SHA256_SUPPORT */ 2623 #ifndef MHD_SHA512_256_SUPPORT 2624 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 2625 { 2626 #ifdef HAVE_MESSAGES 2627 MHD_DLOG (connection->daemon, 2628 _ ("The SHA-512/256 algorithm is not supported by " 2629 "this MHD build.\n")); 2630 #endif /* HAVE_MESSAGES */ 2631 return MHD_DAUTH_WRONG_ALGO; 2632 } 2633 #endif /* ! MHD_SHA512_256_SUPPORT */ 2634 if (! digest_init_one_time (da, get_base_digest_algo (c_algo))) 2635 MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); 2636 /* Check 'mqop' value */ 2637 c_qop = params->qop; 2638 /* Check whether client's QOP is allowed by function parameter */ 2639 if (((unsigned int) c_qop) != 2640 (((unsigned int) c_qop) & ((unsigned int) mqop))) 2641 return MHD_DAUTH_WRONG_QOP; 2642 if (0 != (((unsigned int) c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT)) 2643 { 2644 #ifdef HAVE_MESSAGES 2645 MHD_DLOG (connection->daemon, 2646 _ ("The 'auth-int' QOP is not supported.\n")); 2647 #endif /* HAVE_MESSAGES */ 2648 return MHD_DAUTH_WRONG_QOP; 2649 } 2650 #ifdef HAVE_MESSAGES 2651 if ((MHD_DIGEST_AUTH_QOP_NONE == c_qop) && 2652 (0 == (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5))) 2653 MHD_DLOG (connection->daemon, 2654 _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \ 2655 "non-standard extension.\n")); 2656 #endif /* HAVE_MESSAGES */ 2657 2658 digest_size = digest_get_size (da); 2659 2660 /* ** A quick check for presence of all required parameters ** */ 2661 2662 if ((NULL == params->username.value.str) && 2663 (NULL == params->username_ext.value.str)) 2664 return MHD_DAUTH_WRONG_USERNAME; 2665 else if ((NULL != params->username.value.str) && 2666 (NULL != params->username_ext.value.str)) 2667 return MHD_DAUTH_WRONG_USERNAME; /* Parameters cannot be used together */ 2668 else if ((NULL != params->username_ext.value.str) && 2669 (MHD_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len)) 2670 return MHD_DAUTH_WRONG_USERNAME; /* Broken extended notation */ 2671 else if (params->userhash && (NULL == params->username.value.str)) 2672 return MHD_DAUTH_WRONG_USERNAME; /* Userhash cannot be used with extended notation */ 2673 else if (params->userhash && (digest_size * 2 > params->username.value.len)) 2674 return MHD_DAUTH_WRONG_USERNAME; /* Too few chars for correct userhash */ 2675 else if (params->userhash && (digest_size * 4 < params->username.value.len)) 2676 return MHD_DAUTH_WRONG_USERNAME; /* Too many chars for correct userhash */ 2677 2678 if (NULL == params->realm.value.str) 2679 return MHD_DAUTH_WRONG_REALM; 2680 else if (((NULL == userdigest) || params->userhash) && 2681 (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len)) 2682 return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */ 2683 2684 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2685 { 2686 if (NULL == params->nc.value.str) 2687 return MHD_DAUTH_WRONG_HEADER; 2688 else if (0 == params->nc.value.len) 2689 return MHD_DAUTH_WRONG_HEADER; 2690 else if (4 * 8 < params->nc.value.len) /* Four times more than needed */ 2691 return MHD_DAUTH_WRONG_HEADER; 2692 2693 if (NULL == params->cnonce.value.str) 2694 return MHD_DAUTH_WRONG_HEADER; 2695 else if (0 == params->cnonce.value.len) 2696 return MHD_DAUTH_WRONG_HEADER; 2697 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len) 2698 return MHD_DAUTH_TOO_LARGE; 2699 } 2700 2701 /* The QOP parameter was checked already */ 2702 2703 if (NULL == params->uri.value.str) 2704 return MHD_DAUTH_WRONG_URI; 2705 else if (0 == params->uri.value.len) 2706 return MHD_DAUTH_WRONG_URI; 2707 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len) 2708 return MHD_DAUTH_TOO_LARGE; 2709 2710 if (NULL == params->nonce.value.str) 2711 return MHD_DAUTH_NONCE_WRONG; 2712 else if (0 == params->nonce.value.len) 2713 return MHD_DAUTH_NONCE_WRONG; 2714 else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len) 2715 return MHD_DAUTH_NONCE_WRONG; 2716 2717 if (NULL == params->response.value.str) 2718 return MHD_DAUTH_RESPONSE_WRONG; 2719 else if (0 == params->response.value.len) 2720 return MHD_DAUTH_RESPONSE_WRONG; 2721 else if (digest_size * 4 < params->response.value.len) 2722 return MHD_DAUTH_RESPONSE_WRONG; 2723 2724 /* ** Check simple parameters match ** */ 2725 2726 /* Check 'algorithm' */ 2727 /* The 'algorithm' was checked at the start of the function */ 2728 /* 'algorithm' valid */ 2729 2730 /* Check 'qop' */ 2731 /* The 'qop' was checked at the start of the function */ 2732 /* 'qop' valid */ 2733 2734 /* Check 'realm' */ 2735 realm_len = strlen (realm); 2736 if (! is_param_equal (¶ms->realm, realm, realm_len)) 2737 return MHD_DAUTH_WRONG_REALM; 2738 /* 'realm' valid */ 2739 2740 /* Check 'username' */ 2741 username_len = strlen (username); 2742 if (! params->userhash) 2743 { 2744 if (NULL != params->username.value.str) 2745 { /* Username in standard notation */ 2746 if (! is_param_equal (¶ms->username, username, username_len)) 2747 return MHD_DAUTH_WRONG_USERNAME; 2748 } 2749 else 2750 { /* Username in extended notation */ 2751 char *r_uname; 2752 size_t buf_size = params->username_ext.value.len; 2753 ssize_t res; 2754 2755 mhd_assert (NULL != params->username_ext.value.str); 2756 mhd_assert (MHD_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */ 2757 buf_size += 1; /* For zero-termination */ 2758 buf_size -= MHD_DAUTH_EXT_PARAM_MIN_LEN; 2759 r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size); 2760 if (NULL == r_uname) 2761 return (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ? 2762 MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR; 2763 res = get_rq_extended_uname_copy_z (params->username_ext.value.str, 2764 params->username_ext.value.len, 2765 r_uname, buf_size); 2766 if (0 > res) 2767 return MHD_DAUTH_WRONG_HEADER; /* Broken extended notation */ 2768 if ((username_len != (size_t) res) || 2769 (0 != memcmp (username, r_uname, username_len))) 2770 return MHD_DAUTH_WRONG_USERNAME; 2771 } 2772 } 2773 else 2774 { /* Userhash */ 2775 mhd_assert (NULL != params->username.value.str); 2776 calc_userhash (da, username, username_len, realm, realm_len, hash1_bin); 2777 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2778 if (digest_ext_error (da)) 2779 return MHD_DAUTH_ERROR; 2780 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2781 mhd_assert (sizeof (tmp1) >= (2 * digest_size)); 2782 MHD_bin_to_hex (hash1_bin, digest_size, tmp1); 2783 if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) 2784 return MHD_DAUTH_WRONG_USERNAME; 2785 /* To simplify the logic, the digest is reset here instead of resetting 2786 before the next hash calculation. */ 2787 digest_reset (da); 2788 } 2789 /* 'username' valid */ 2790 2791 /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */ 2792 2793 /* Get 'nc' digital value */ 2794 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2795 { 2796 2797 unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, 2798 &unquoted); 2799 if (_MHD_UNQ_OK != unq_res) 2800 return MHD_DAUTH_ERROR; 2801 2802 if (unquoted.len != MHD_strx_to_uint64_n_ (unquoted.str, 2803 unquoted.len, 2804 &nci)) 2805 { 2806 #ifdef HAVE_MESSAGES 2807 MHD_DLOG (daemon, 2808 _ ("Authentication failed, invalid nc format.\n")); 2809 #endif 2810 return MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ 2811 } 2812 if (0 == nci) 2813 { 2814 #ifdef HAVE_MESSAGES 2815 MHD_DLOG (daemon, 2816 _ ("Authentication failed, invalid 'nc' value.\n")); 2817 #endif 2818 return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ 2819 } 2820 if ((0 != max_nc) && (max_nc < nci)) 2821 return MHD_DAUTH_NONCE_STALE; /* Too large 'nc' value */ 2822 } 2823 else 2824 nci = 1; /* Force 'nc' value */ 2825 /* Got 'nc' digital value */ 2826 2827 /* Get 'nonce' with basic checks */ 2828 unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, 2829 &unquoted); 2830 if (_MHD_UNQ_OK != unq_res) 2831 return MHD_DAUTH_ERROR; 2832 2833 if ((NONCE_STD_LEN (digest_size) != unquoted.len) || 2834 (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time))) 2835 { 2836 #ifdef HAVE_MESSAGES 2837 MHD_DLOG (daemon, 2838 _ ("Authentication failed, invalid nonce format.\n")); 2839 #endif 2840 return MHD_DAUTH_NONCE_WRONG; 2841 } 2842 2843 if (1) 2844 { 2845 uint64_t t; 2846 2847 t = MHD_monotonic_msec_counter (); 2848 /* 2849 * First level vetting for the nonce validity: if the timestamp 2850 * attached to the nonce exceeds `nonce_timeout', then the nonce is 2851 * stale. 2852 */ 2853 if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000)) 2854 return MHD_DAUTH_NONCE_STALE; /* too old */ 2855 } 2856 if (1) 2857 { 2858 enum MHD_CheckNonceNC_ nonce_nc_check; 2859 /* 2860 * Checking if that combination of nonce and nc is sound 2861 * and not a replay attack attempt. Refuse if nonce was not 2862 * generated previously. 2863 */ 2864 nonce_nc_check = check_nonce_nc (connection, 2865 unquoted.str, 2866 NONCE_STD_LEN (digest_size), 2867 nonce_time, 2868 nci); 2869 if (MHD_CHECK_NONCENC_STALE == nonce_nc_check) 2870 { 2871 #ifdef HAVE_MESSAGES 2872 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2873 MHD_DLOG (daemon, 2874 _ ("Stale nonce received. If this happens a lot, you should " 2875 "probably increase the size of the nonce array.\n")); 2876 else 2877 MHD_DLOG (daemon, 2878 _ ("Stale nonce received. This is expected when client " \ 2879 "uses RFC2069-compatible mode and makes more than one " \ 2880 "request.\n")); 2881 #endif 2882 return MHD_DAUTH_NONCE_STALE; 2883 } 2884 else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check) 2885 { 2886 #ifdef HAVE_MESSAGES 2887 MHD_DLOG (daemon, 2888 _ ("Received nonce that was not " 2889 "generated by MHD. This may indicate an attack attempt.\n")); 2890 #endif 2891 return MHD_DAUTH_NONCE_WRONG; 2892 } 2893 mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check); 2894 } 2895 /* The nonce was generated by MHD, is not stale and nonce-nc combination was 2896 not used before */ 2897 2898 /* ** Build H(A2) and check URI match in the header and in the request ** */ 2899 2900 /* Get 'uri' */ 2901 mhd_assert (! da->hashing); 2902 digest_update_str (da, connection->rq.method); 2903 digest_update_with_colon (da); 2904 #if 0 2905 /* TODO: add support for "auth-int" */ 2906 digest_update_str (da, hentity); 2907 digest_update_with_colon (da); 2908 #endif 2909 unq_res = get_unquoted_param_copy (¶ms->uri, tmp1, ptmp2, &tmp2_size, 2910 &unq_copy); 2911 if (_MHD_UNQ_OK != unq_res) 2912 return MHD_DAUTH_ERROR; 2913 2914 digest_update (da, unq_copy.str, unq_copy.len); 2915 /* The next check will modify copied URI string */ 2916 if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) 2917 return MHD_DAUTH_WRONG_URI; 2918 digest_calc_hash (da, hash2_bin); 2919 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2920 /* Skip digest calculation external error check, the next one checks both */ 2921 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2922 /* Got H(A2) */ 2923 2924 /* ** Build H(A1) ** */ 2925 if (NULL == userdigest) 2926 { 2927 mhd_assert (! da->hashing); 2928 digest_reset (da); 2929 calc_userdigest (da, 2930 username, username_len, 2931 realm, realm_len, 2932 password, 2933 hash1_bin); 2934 } 2935 /* TODO: support '-sess' versions */ 2936 #ifdef MHD_DIGEST_HAS_EXT_ERROR 2937 if (digest_ext_error (da)) 2938 return MHD_DAUTH_ERROR; 2939 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 2940 /* Got H(A1) */ 2941 2942 /* ** Check 'response' ** */ 2943 2944 mhd_assert (! da->hashing); 2945 digest_reset (da); 2946 /* Update digest with H(A1) */ 2947 mhd_assert (sizeof (tmp1) >= (digest_size * 2)); 2948 if (NULL == userdigest) 2949 MHD_bin_to_hex (hash1_bin, digest_size, tmp1); 2950 else 2951 MHD_bin_to_hex (userdigest, digest_size, tmp1); 2952 digest_update (da, (const uint8_t *) tmp1, digest_size * 2); 2953 2954 /* H(A1) is not needed anymore, reuse the buffer. 2955 * Use hash1_bin for the client's 'response' decoded to binary form. */ 2956 unq_res = get_unquoted_param (¶ms->response, tmp1, ptmp2, &tmp2_size, 2957 &unquoted); 2958 if (_MHD_UNQ_OK != unq_res) 2959 return MHD_DAUTH_ERROR; 2960 if (digest_size != MHD_hex_to_bin (unquoted.str, unquoted.len, hash1_bin)) 2961 return MHD_DAUTH_RESPONSE_WRONG; 2962 2963 /* Update digest with ':' */ 2964 digest_update_with_colon (da); 2965 /* Update digest with 'nonce' text value */ 2966 unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, 2967 &unquoted); 2968 if (_MHD_UNQ_OK != unq_res) 2969 return MHD_DAUTH_ERROR; 2970 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2971 /* Update digest with ':' */ 2972 digest_update_with_colon (da); 2973 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2974 { 2975 /* Update digest with 'nc' text value */ 2976 unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, 2977 &unquoted); 2978 if (_MHD_UNQ_OK != unq_res) 2979 return MHD_DAUTH_ERROR; 2980 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2981 /* Update digest with ':' */ 2982 digest_update_with_colon (da); 2983 /* Update digest with 'cnonce' value */ 2984 unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, 2985 &unquoted); 2986 if (_MHD_UNQ_OK != unq_res) 2987 return MHD_DAUTH_ERROR; 2988 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2989 /* Update digest with ':' */ 2990 digest_update_with_colon (da); 2991 /* Update digest with 'qop' value */ 2992 unq_res = get_unquoted_param (¶ms->qop_raw, tmp1, ptmp2, &tmp2_size, 2993 &unquoted); 2994 if (_MHD_UNQ_OK != unq_res) 2995 return MHD_DAUTH_ERROR; 2996 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); 2997 /* Update digest with ':' */ 2998 digest_update_with_colon (da); 2999 } 3000 /* Update digest with H(A2) */ 3001 MHD_bin_to_hex (hash2_bin, 3002 digest_size, 3003 tmp1); 3004 digest_update (da, 3005 (const uint8_t *) tmp1, 3006 digest_size * 2); 3007 3008 /* H(A2) is not needed anymore, reuse the buffer. 3009 * Use hash2_bin for the calculated response in binary form */ 3010 digest_calc_hash (da, hash2_bin); 3011 #ifdef MHD_DIGEST_HAS_EXT_ERROR 3012 if (digest_ext_error (da)) 3013 return MHD_DAUTH_ERROR; 3014 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 3015 3016 if (0 != memcmp (hash1_bin, 3017 hash2_bin, 3018 digest_size)) 3019 return MHD_DAUTH_RESPONSE_WRONG; 3020 3021 if (MHD_DAUTH_BIND_NONCE_NONE != daemon->dauth_bind_type) 3022 { 3023 mhd_assert (sizeof(tmp1) >= (NONCE_STD_LEN (digest_size) + 1)); 3024 /* It was already checked that 'nonce' (including timestamp) was generated 3025 by MHD. */ 3026 mhd_assert (! da->hashing); 3027 digest_reset (da); 3028 calculate_nonce (nonce_time, 3029 connection->rq.http_mthd, 3030 connection->rq.method, 3031 daemon->digest_auth_random, 3032 daemon->digest_auth_rand_size, 3033 connection->addr, 3034 (size_t) connection->addr_len, 3035 connection->rq.url, 3036 connection->rq.url_len, 3037 connection->rq.headers_received, 3038 realm, 3039 realm_len, 3040 daemon->dauth_bind_type, 3041 da, 3042 tmp1); 3043 3044 #ifdef MHD_DIGEST_HAS_EXT_ERROR 3045 if (digest_ext_error (da)) 3046 return MHD_DAUTH_ERROR; 3047 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 3048 3049 if (! is_param_equal (¶ms->nonce, tmp1, 3050 NONCE_STD_LEN (digest_size))) 3051 return MHD_DAUTH_NONCE_OTHER_COND; 3052 /* The 'nonce' was generated in the same conditions */ 3053 } 3054 3055 return MHD_DAUTH_OK; 3056 } 3057 3058 3059 /** 3060 * Authenticates the authorization header sent by the client 3061 * 3062 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3063 * @a mqop and the client uses this mode, then server generated nonces are 3064 * used as one-time nonces because nonce-count is not supported in this old RFC. 3065 * Communication in this mode is very inefficient, especially if the client 3066 * requests several resources one-by-one as for every request new nonce must be 3067 * generated and client repeat all requests twice (the first time to get a new 3068 * nonce and the second time to perform an authorised request). 3069 * 3070 * @param connection the MHD connection structure 3071 * @param realm the realm for authorization of the client 3072 * @param username the username to be authenticated, must be in clear text 3073 * even if userhash is used by the client 3074 * @param password the password used in the authentication, 3075 * must be NULL if @a userdigest is not NULL 3076 * @param userdigest the precalculated binary hash of the string 3077 * "username:realm:password", 3078 * must be NULL if @a password is not NULL 3079 * @param nonce_timeout the period of seconds since nonce generation, when 3080 * the nonce is recognised as valid and not stale; 3081 * if set to zero then daemon's default value is used 3082 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3083 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3084 * returned; 3085 * if set to zero then daemon's default value is used 3086 * @param mqop the QOP to use 3087 * @param malgo3 digest algorithms allowed to use, fail if algorithm specified 3088 * by the client is not allowed by this parameter 3089 * @return #MHD_DAUTH_OK if authenticated, 3090 * error code otherwise. 3091 * @ingroup authentication 3092 */ 3093 static enum MHD_DigestAuthResult 3094 digest_auth_check_all (struct MHD_Connection *connection, 3095 const char *realm, 3096 const char *username, 3097 const char *password, 3098 const uint8_t *userdigest, 3099 unsigned int nonce_timeout, 3100 uint32_t max_nc, 3101 enum MHD_DigestAuthMultiQOP mqop, 3102 enum MHD_DigestAuthMultiAlgo3 malgo3) 3103 { 3104 enum MHD_DigestAuthResult res; 3105 char *buf; 3106 struct DigestAlgorithm da; 3107 3108 buf = NULL; 3109 digest_setup_zero (&da); 3110 if (0 == nonce_timeout) 3111 nonce_timeout = connection->daemon->dauth_def_nonce_timeout; 3112 if (0 == max_nc) 3113 max_nc = connection->daemon->dauth_def_max_nc; 3114 res = digest_auth_check_all_inner (connection, realm, username, password, 3115 userdigest, 3116 nonce_timeout, 3117 max_nc, mqop, malgo3, 3118 &buf, &da); 3119 digest_deinit (&da); 3120 if (NULL != buf) 3121 free (buf); 3122 3123 return res; 3124 } 3125 3126 3127 /** 3128 * Authenticates the authorization header sent by the client. 3129 * Uses #MHD_DIGEST_ALG_MD5 (for now, for backwards-compatibility). 3130 * Note that this MAY change to #MHD_DIGEST_ALG_AUTO in the future. 3131 * If you want to be sure you get MD5, use #MHD_digest_auth_check2() 3132 * and specify MD5 explicitly. 3133 * 3134 * @param connection The MHD connection structure 3135 * @param realm The realm presented to the client 3136 * @param username The username needs to be authenticated 3137 * @param password The password used in the authentication 3138 * @param nonce_timeout The amount of time for a nonce to be 3139 * invalid in seconds 3140 * @return #MHD_YES if authenticated, #MHD_NO if not, 3141 * #MHD_INVALID_NONCE if nonce is invalid or stale 3142 * @deprecated use MHD_digest_auth_check3() 3143 * @ingroup authentication 3144 */ 3145 _MHD_EXTERN int 3146 MHD_digest_auth_check (struct MHD_Connection *connection, 3147 const char *realm, 3148 const char *username, 3149 const char *password, 3150 unsigned int nonce_timeout) 3151 { 3152 return MHD_digest_auth_check2 (connection, 3153 realm, 3154 username, 3155 password, 3156 nonce_timeout, 3157 MHD_DIGEST_ALG_MD5); 3158 } 3159 3160 3161 /** 3162 * Authenticates the authorization header sent by the client. 3163 * 3164 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3165 * @a mqop and the client uses this mode, then server generated nonces are 3166 * used as one-time nonces because nonce-count is not supported in this old RFC. 3167 * Communication in this mode is very inefficient, especially if the client 3168 * requests several resources one-by-one as for every request a new nonce must 3169 * be generated and client repeats all requests twice (first time to get a new 3170 * nonce and second time to perform an authorised request). 3171 * 3172 * @param connection the MHD connection structure 3173 * @param realm the realm for authorization of the client 3174 * @param username the username to be authenticated, must be in clear text 3175 * even if userhash is used by the client 3176 * @param password the password matching the @a username (and the @a realm) 3177 * @param nonce_timeout the period of seconds since nonce generation, when 3178 * the nonce is recognised as valid and not stale; 3179 * if zero is specified then daemon default value is used. 3180 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3181 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3182 * returned; 3183 * if zero is specified then daemon default value is used. 3184 * @param mqop the QOP to use 3185 * @param malgo3 digest algorithms allowed to use, fail if algorithm used 3186 * by the client is not allowed by this parameter 3187 * @return #MHD_DAUTH_OK if authenticated, 3188 * the error code otherwise 3189 * @note Available since #MHD_VERSION 0x00097708 3190 * @ingroup authentication 3191 */ 3192 _MHD_EXTERN enum MHD_DigestAuthResult 3193 MHD_digest_auth_check3 (struct MHD_Connection *connection, 3194 const char *realm, 3195 const char *username, 3196 const char *password, 3197 unsigned int nonce_timeout, 3198 uint32_t max_nc, 3199 enum MHD_DigestAuthMultiQOP mqop, 3200 enum MHD_DigestAuthMultiAlgo3 malgo3) 3201 { 3202 mhd_assert (NULL != password); 3203 3204 return digest_auth_check_all (connection, 3205 realm, 3206 username, 3207 password, 3208 NULL, 3209 nonce_timeout, 3210 max_nc, 3211 mqop, 3212 malgo3); 3213 } 3214 3215 3216 /** 3217 * Authenticates the authorization header sent by the client by using 3218 * hash of "username:realm:password". 3219 * 3220 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3221 * @a mqop and the client uses this mode, then server generated nonces are 3222 * used as one-time nonces because nonce-count is not supported in this old RFC. 3223 * Communication in this mode is very inefficient, especially if the client 3224 * requests several resources one-by-one as for every request a new nonce must 3225 * be generated and client repeats all requests twice (first time to get a new 3226 * nonce and second time to perform an authorised request). 3227 * 3228 * @param connection the MHD connection structure 3229 * @param realm the realm for authorization of the client 3230 * @param username the username to be authenticated, must be in clear text 3231 * even if userhash is used by the client 3232 * @param userdigest the precalculated binary hash of the string 3233 * "username:realm:password", 3234 * see #MHD_digest_auth_calc_userdigest() 3235 * @param userdigest_size the size of the @a userdigest in bytes, must match the 3236 * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, 3237 * #MHD_SHA256_DIGEST_SIZE, #MHD_SHA512_256_DIGEST_SIZE, 3238 * #MHD_digest_get_hash_size()) 3239 * @param nonce_timeout the period of seconds since nonce generation, when 3240 * the nonce is recognised as valid and not stale; 3241 * if zero is specified then daemon default value is used. 3242 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3243 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3244 * returned; 3245 * if zero is specified then daemon default value is used. 3246 * @param mqop the QOP to use 3247 * @param malgo3 digest algorithms allowed to use, fail if algorithm used 3248 * by the client is not allowed by this parameter; 3249 * more than one base algorithms (MD5, SHA-256, SHA-512/256) 3250 * cannot be used at the same time for this function 3251 * as @a userdigest must match specified algorithm 3252 * @return #MHD_DAUTH_OK if authenticated, 3253 * the error code otherwise 3254 * @sa #MHD_digest_auth_calc_userdigest() 3255 * @note Available since #MHD_VERSION 0x00097708 3256 * @ingroup authentication 3257 */ 3258 _MHD_EXTERN enum MHD_DigestAuthResult 3259 MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, 3260 const char *realm, 3261 const char *username, 3262 const void *userdigest, 3263 size_t userdigest_size, 3264 unsigned int nonce_timeout, 3265 uint32_t max_nc, 3266 enum MHD_DigestAuthMultiQOP mqop, 3267 enum MHD_DigestAuthMultiAlgo3 malgo3) 3268 { 3269 if (1 != (((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0) 3270 + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0) 3271 + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0))) 3272 MHD_PANIC (_ ("Wrong 'malgo3' value, only one base hashing algorithm " \ 3273 "(MD5, SHA-256 or SHA-512/256) must be specified, " \ 3274 "API violation")); 3275 3276 #ifndef MHD_MD5_SUPPORT 3277 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) 3278 { 3279 #ifdef HAVE_MESSAGES 3280 MHD_DLOG (connection->daemon, 3281 _ ("The MD5 algorithm is not supported by this MHD build.\n")); 3282 #endif /* HAVE_MESSAGES */ 3283 return MHD_DAUTH_WRONG_ALGO; 3284 } 3285 #endif /* ! MHD_MD5_SUPPORT */ 3286 #ifndef MHD_SHA256_SUPPORT 3287 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) 3288 { 3289 #ifdef HAVE_MESSAGES 3290 MHD_DLOG (connection->daemon, 3291 _ ("The SHA-256 algorithm is not supported by " 3292 "this MHD build.\n")); 3293 #endif /* HAVE_MESSAGES */ 3294 return MHD_DAUTH_WRONG_ALGO; 3295 } 3296 #endif /* ! MHD_SHA256_SUPPORT */ 3297 #ifndef MHD_SHA512_256_SUPPORT 3298 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 3299 { 3300 #ifdef HAVE_MESSAGES 3301 MHD_DLOG (connection->daemon, 3302 _ ("The SHA-512/256 algorithm is not supported by " 3303 "this MHD build.\n")); 3304 #endif /* HAVE_MESSAGES */ 3305 return MHD_DAUTH_WRONG_ALGO; 3306 } 3307 #endif /* ! MHD_SHA512_256_SUPPORT */ 3308 3309 if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) != 3310 userdigest_size) 3311 MHD_PANIC (_ ("Wrong 'userdigest_size' value, does not match 'malgo3', " 3312 "API violation")); 3313 3314 return digest_auth_check_all (connection, 3315 realm, 3316 username, 3317 NULL, 3318 (const uint8_t *) userdigest, 3319 nonce_timeout, 3320 max_nc, 3321 mqop, 3322 malgo3); 3323 } 3324 3325 3326 /** 3327 * Authenticates the authorization header sent by the client. 3328 * 3329 * @param connection The MHD connection structure 3330 * @param realm The realm presented to the client 3331 * @param username The username needs to be authenticated 3332 * @param password The password used in the authentication 3333 * @param nonce_timeout The amount of time for a nonce to be 3334 * invalid in seconds 3335 * @param algo digest algorithms allowed for verification 3336 * @return #MHD_YES if authenticated, #MHD_NO if not, 3337 * #MHD_INVALID_NONCE if nonce is invalid or stale 3338 * @note Available since #MHD_VERSION 0x00096200 3339 * @deprecated use MHD_digest_auth_check3() 3340 * @ingroup authentication 3341 */ 3342 _MHD_EXTERN int 3343 MHD_digest_auth_check2 (struct MHD_Connection *connection, 3344 const char *realm, 3345 const char *username, 3346 const char *password, 3347 unsigned int nonce_timeout, 3348 enum MHD_DigestAuthAlgorithm algo) 3349 { 3350 enum MHD_DigestAuthResult res; 3351 enum MHD_DigestAuthMultiAlgo3 malgo3; 3352 3353 if (MHD_DIGEST_ALG_AUTO == algo) 3354 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; 3355 else if (MHD_DIGEST_ALG_MD5 == algo) 3356 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; 3357 else if (MHD_DIGEST_ALG_SHA256 == algo) 3358 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; 3359 else 3360 MHD_PANIC (_ ("Wrong 'algo' value, API violation")); 3361 3362 res = MHD_digest_auth_check3 (connection, 3363 realm, 3364 username, 3365 password, 3366 nonce_timeout, 3367 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, 3368 malgo3); 3369 if (MHD_DAUTH_OK == res) 3370 return MHD_YES; 3371 else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) || 3372 (MHD_DAUTH_NONCE_OTHER_COND == res) ) 3373 return MHD_INVALID_NONCE; 3374 return MHD_NO; 3375 3376 } 3377 3378 3379 /** 3380 * Authenticates the authorization header sent by the client. 3381 * 3382 * @param connection The MHD connection structure 3383 * @param realm The realm presented to the client 3384 * @param username The username needs to be authenticated 3385 * @param digest An `unsigned char *' pointer to the binary MD5 sum 3386 * for the precalculated hash value "username:realm:password" 3387 * of @a digest_size bytes 3388 * @param digest_size number of bytes in @a digest (size must match @a algo!) 3389 * @param nonce_timeout The amount of time for a nonce to be 3390 * invalid in seconds 3391 * @param algo digest algorithms allowed for verification 3392 * @return #MHD_YES if authenticated, #MHD_NO if not, 3393 * #MHD_INVALID_NONCE if nonce is invalid or stale 3394 * @note Available since #MHD_VERSION 0x00096200 3395 * @deprecated use MHD_digest_auth_check_digest3() 3396 * @ingroup authentication 3397 */ 3398 _MHD_EXTERN int 3399 MHD_digest_auth_check_digest2 (struct MHD_Connection *connection, 3400 const char *realm, 3401 const char *username, 3402 const uint8_t *digest, 3403 size_t digest_size, 3404 unsigned int nonce_timeout, 3405 enum MHD_DigestAuthAlgorithm algo) 3406 { 3407 enum MHD_DigestAuthResult res; 3408 enum MHD_DigestAuthMultiAlgo3 malgo3; 3409 3410 if (MHD_DIGEST_ALG_AUTO == algo) 3411 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; 3412 else if (MHD_DIGEST_ALG_MD5 == algo) 3413 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; 3414 else if (MHD_DIGEST_ALG_SHA256 == algo) 3415 malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; 3416 else 3417 MHD_PANIC (_ ("Wrong 'algo' value, API violation")); 3418 3419 res = MHD_digest_auth_check_digest3 (connection, 3420 realm, 3421 username, 3422 digest, 3423 digest_size, 3424 nonce_timeout, 3425 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, 3426 malgo3); 3427 if (MHD_DAUTH_OK == res) 3428 return MHD_YES; 3429 else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) || 3430 (MHD_DAUTH_NONCE_OTHER_COND == res) ) 3431 return MHD_INVALID_NONCE; 3432 return MHD_NO; 3433 } 3434 3435 3436 /** 3437 * Authenticates the authorization header sent by the client 3438 * Uses #MHD_DIGEST_ALG_MD5 (required, as @a digest is of fixed 3439 * size). 3440 * 3441 * @param connection The MHD connection structure 3442 * @param realm The realm presented to the client 3443 * @param username The username needs to be authenticated 3444 * @param digest An `unsigned char *' pointer to the binary hash 3445 * for the precalculated hash value "username:realm:password"; 3446 * length must be #MHD_MD5_DIGEST_SIZE bytes 3447 * @param nonce_timeout The amount of time for a nonce to be 3448 * invalid in seconds 3449 * @return #MHD_YES if authenticated, #MHD_NO if not, 3450 * #MHD_INVALID_NONCE if nonce is invalid or stale 3451 * @note Available since #MHD_VERSION 0x00096000 3452 * @deprecated use #MHD_digest_auth_check_digest3() 3453 * @ingroup authentication 3454 */ 3455 _MHD_EXTERN int 3456 MHD_digest_auth_check_digest (struct MHD_Connection *connection, 3457 const char *realm, 3458 const char *username, 3459 const uint8_t digest[MHD_MD5_DIGEST_SIZE], 3460 unsigned int nonce_timeout) 3461 { 3462 return MHD_digest_auth_check_digest2 (connection, 3463 realm, 3464 username, 3465 digest, 3466 MHD_MD5_DIGEST_SIZE, 3467 nonce_timeout, 3468 MHD_DIGEST_ALG_MD5); 3469 } 3470 3471 3472 /** 3473 * Internal version of #MHD_queue_auth_required_response3() to simplify 3474 * cleanups. 3475 * 3476 * @param connection the MHD connection structure 3477 * @param realm the realm presented to the client 3478 * @param opaque the string for opaque value, can be NULL, but NULL is 3479 * not recommended for better compatibility with clients; 3480 * the recommended format is hex or Base64 encoded string 3481 * @param domain the optional space-separated list of URIs for which the 3482 * same authorisation could be used, URIs can be in form 3483 * "path-absolute" (the path for the same host with initial slash) 3484 * or in form "absolute-URI" (the full path with protocol), in 3485 * any case client may assume that URI is in the same "protection 3486 * space" if it starts with any of values specified here; 3487 * could be NULL (clients typically assume that the same 3488 * credentials could be used for any URI on the same host) 3489 * @param response the reply to send; should contain the "access denied" 3490 * body; note that this function sets the "WWW Authenticate" 3491 * header and that the caller should not do this; 3492 * the NULL is tolerated 3493 * @param signal_stale set to #MHD_YES if the nonce is stale to add 'stale=true' 3494 * to the authentication header, this instructs the client 3495 * to retry immediately with the new nonce and the same 3496 * credentials, without asking user for the new password 3497 * @param mqop the QOP to use 3498 * @param malgo3 digest algorithm to use, MHD selects; if several algorithms 3499 * are allowed then MD5 is preferred (currently, may be changed 3500 * in next versions) 3501 * @param userhash_support if set to non-zero value (#MHD_YES) then support of 3502 * userhash is indicated, the client may provide 3503 * hash("username:realm") instead of username in 3504 * clear text; 3505 * note that clients are allowed to provide the username 3506 * in cleartext even if this parameter set to non-zero; 3507 * when userhash is used, application must be ready to 3508 * identify users by provided userhash value instead of 3509 * username; see #MHD_digest_auth_calc_userhash() and 3510 * #MHD_digest_auth_calc_userhash_hex() 3511 * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is 3512 * added, indicating for the client that UTF-8 encoding 3513 * is preferred 3514 * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is 3515 * added, indicating for the client that UTF-8 encoding 3516 * is preferred 3517 * @return #MHD_YES on success, #MHD_NO otherwise 3518 * @note Available since #MHD_VERSION 0x00097701 3519 * @ingroup authentication 3520 */ 3521 static enum MHD_Result 3522 queue_auth_required_response3_inner (struct MHD_Connection *connection, 3523 const char *realm, 3524 const char *opaque, 3525 const char *domain, 3526 struct MHD_Response *response, 3527 int signal_stale, 3528 enum MHD_DigestAuthMultiQOP mqop, 3529 enum MHD_DigestAuthMultiAlgo3 malgo3, 3530 int userhash_support, 3531 int prefer_utf8, 3532 char **buf_ptr, 3533 struct DigestAlgorithm *da) 3534 { 3535 static const char prefix_realm[] = "realm=\""; 3536 static const char prefix_qop[] = "qop=\""; 3537 static const char prefix_algo[] = "algorithm="; 3538 static const char prefix_nonce[] = "nonce=\""; 3539 static const char prefix_opaque[] = "opaque=\""; 3540 static const char prefix_domain[] = "domain=\""; 3541 static const char str_charset[] = "charset=UTF-8"; 3542 static const char str_userhash[] = "userhash=true"; 3543 static const char str_stale[] = "stale=true"; 3544 enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */ 3545 size_t realm_len; 3546 size_t opaque_len; 3547 size_t domain_len; 3548 size_t buf_size; 3549 char *buf; 3550 size_t p; /* The position in the buffer */ 3551 char *hdr_name; 3552 3553 if ((0 == (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_NON_SESSION)) || 3554 (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION))) 3555 { 3556 #ifdef HAVE_MESSAGES 3557 MHD_DLOG (connection->daemon, 3558 _ ("Only non-'session' algorithms are supported.\n")); 3559 #endif /* HAVE_MESSAGES */ 3560 return MHD_NO; 3561 } 3562 malgo3 = 3563 (enum MHD_DigestAuthMultiAlgo3) 3564 (malgo3 3565 & (~((enum MHD_DigestAuthMultiAlgo3) MHD_DIGEST_AUTH_ALGO3_NON_SESSION))); 3566 #ifdef MHD_MD5_SUPPORT 3567 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) 3568 s_algo = MHD_DIGEST_AUTH_ALGO3_MD5; 3569 else 3570 #endif /* MHD_MD5_SUPPORT */ 3571 #ifdef MHD_SHA256_SUPPORT 3572 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) 3573 s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256; 3574 else 3575 #endif /* MHD_SHA256_SUPPORT */ 3576 #ifdef MHD_SHA512_256_SUPPORT 3577 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 3578 s_algo = MHD_DIGEST_AUTH_ALGO3_SHA512_256; 3579 else 3580 #endif /* MHD_SHA512_256_SUPPORT */ 3581 { 3582 if (0 == (((unsigned int) malgo3) 3583 & (MHD_DIGEST_BASE_ALGO_MD5 | MHD_DIGEST_BASE_ALGO_SHA256 3584 | MHD_DIGEST_BASE_ALGO_SHA512_256))) 3585 MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); 3586 else 3587 { 3588 #ifdef HAVE_MESSAGES 3589 MHD_DLOG (connection->daemon, 3590 _ ("No requested algorithm is supported by this MHD build.\n")); 3591 #endif /* HAVE_MESSAGES */ 3592 } 3593 return MHD_NO; 3594 } 3595 3596 if (MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT == mqop) 3597 MHD_PANIC (_ ("Wrong 'mqop' value, API violation")); 3598 3599 mqop = (enum MHD_DigestAuthMultiQOP) 3600 (mqop 3601 & (~((enum MHD_DigestAuthMultiQOP) MHD_DIGEST_AUTH_QOP_AUTH_INT))); 3602 3603 if (! digest_init_one_time (da, get_base_digest_algo (s_algo))) 3604 MHD_PANIC (_ ("Wrong 'algo' value, API violation")); 3605 3606 if (MHD_DIGEST_AUTH_MULT_QOP_NONE == mqop) 3607 { 3608 #ifdef HAVE_MESSAGES 3609 if ((0 != userhash_support) || (0 != prefer_utf8)) 3610 MHD_DLOG (connection->daemon, 3611 _ ("The 'userhash' and 'charset' ('prefer_utf8') parameters " \ 3612 "are not compatible with RFC2069 and ignored.\n")); 3613 if (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)) 3614 MHD_DLOG (connection->daemon, 3615 _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \ 3616 "non-standard extension.\n")); 3617 #endif 3618 userhash_support = 0; 3619 prefer_utf8 = 0; 3620 } 3621 3622 if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) 3623 { 3624 #ifdef HAVE_MESSAGES 3625 MHD_DLOG (connection->daemon, 3626 _ ("The nonce array size is zero.\n")); 3627 #endif /* HAVE_MESSAGES */ 3628 return MHD_NO; 3629 } 3630 3631 /* Calculate required size */ 3632 buf_size = 0; 3633 /* 'Digest ' */ 3634 buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */ 3635 buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */ 3636 /* 'realm="xxxx", ' */ 3637 realm_len = strlen (realm); 3638 if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len) 3639 { 3640 #ifdef HAVE_MESSAGES 3641 MHD_DLOG (connection->daemon, 3642 _ ("The 'realm' is too large.\n")); 3643 #endif /* HAVE_MESSAGES */ 3644 return MHD_NO; 3645 } 3646 if ((NULL != memchr (realm, '\r', realm_len)) || 3647 (NULL != memchr (realm, '\n', realm_len))) 3648 return MHD_NO; 3649 3650 buf_size += realm_len * 2; /* Quoting may double the size */ 3651 /* 'qop="xxxx", ' */ 3652 if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop) 3653 { 3654 buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */ 3655 buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); 3656 } 3657 /* 'algorithm="xxxx", ' */ 3658 if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) || 3659 (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5))) 3660 { 3661 buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */ 3662 #ifdef MHD_MD5_SUPPORT 3663 if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) 3664 buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); 3665 else 3666 #endif /* MHD_MD5_SUPPORT */ 3667 #ifdef MHD_SHA256_SUPPORT 3668 if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) 3669 buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); 3670 else 3671 #endif /* MHD_SHA256_SUPPORT */ 3672 #ifdef MHD_SHA512_256_SUPPORT 3673 if (MHD_DIGEST_AUTH_ALGO3_SHA512_256 == s_algo) 3674 buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN); 3675 else 3676 #endif /* MHD_SHA512_256_SUPPORT */ 3677 mhd_assert (0); 3678 } 3679 /* 'nonce="xxxx", ' */ 3680 buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */ 3681 buf_size += NONCE_STD_LEN (digest_get_size (da)); /* Escaping not needed */ 3682 /* 'opaque="xxxx", ' */ 3683 if (NULL != opaque) 3684 { 3685 buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */ 3686 opaque_len = strlen (opaque); 3687 if ((NULL != memchr (opaque, '\r', opaque_len)) || 3688 (NULL != memchr (opaque, '\n', opaque_len))) 3689 return MHD_NO; 3690 3691 buf_size += opaque_len * 2; /* Quoting may double the size */ 3692 } 3693 else 3694 opaque_len = 0; 3695 /* 'domain="xxxx", ' */ 3696 if (NULL != domain) 3697 { 3698 buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */ 3699 domain_len = strlen (domain); 3700 if ((NULL != memchr (domain, '\r', domain_len)) || 3701 (NULL != memchr (domain, '\n', domain_len))) 3702 return MHD_NO; 3703 3704 buf_size += domain_len * 2; /* Quoting may double the size */ 3705 } 3706 else 3707 domain_len = 0; 3708 /* 'charset=UTF-8' */ 3709 if (MHD_NO != prefer_utf8) 3710 buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */ 3711 /* 'userhash=true' */ 3712 if (MHD_NO != userhash_support) 3713 buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */ 3714 /* 'stale=true' */ 3715 if (MHD_NO != signal_stale) 3716 buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */ 3717 3718 /* The calculated length is for string ended with ", ". One character will 3719 * be used for zero-termination, the last one will not be used. */ 3720 3721 /* Allocate the buffer */ 3722 buf = malloc (buf_size); 3723 if (NULL == buf) 3724 return MHD_NO; 3725 *buf_ptr = buf; 3726 3727 /* Build the challenge string */ 3728 p = 0; 3729 /* 'Digest: ' */ 3730 memcpy (buf + p, _MHD_AUTH_DIGEST_BASE, 3731 MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE)); 3732 p += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE); 3733 buf[p++] = ' '; 3734 /* 'realm="xxxx", ' */ 3735 memcpy (buf + p, prefix_realm, 3736 MHD_STATICSTR_LEN_ (prefix_realm)); 3737 p += MHD_STATICSTR_LEN_ (prefix_realm); 3738 mhd_assert ((buf_size - p) >= (realm_len * 2)); 3739 if (1) 3740 { 3741 size_t quoted_size; 3742 quoted_size = MHD_str_quote (realm, realm_len, buf + p, buf_size - p); 3743 if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < quoted_size) 3744 { 3745 #ifdef HAVE_MESSAGES 3746 MHD_DLOG (connection->daemon, 3747 _ ("The 'realm' is too large after 'quoting'.\n")); 3748 #endif /* HAVE_MESSAGES */ 3749 return MHD_NO; 3750 } 3751 p += quoted_size; 3752 } 3753 buf[p++] = '\"'; 3754 buf[p++] = ','; 3755 buf[p++] = ' '; 3756 /* 'qop="xxxx", ' */ 3757 if (MHD_DIGEST_AUTH_MULT_QOP_NONE != mqop) 3758 { 3759 memcpy (buf + p, prefix_qop, 3760 MHD_STATICSTR_LEN_ (prefix_qop)); 3761 p += MHD_STATICSTR_LEN_ (prefix_qop); 3762 memcpy (buf + p, MHD_TOKEN_AUTH_, 3763 MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_)); 3764 p += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); 3765 buf[p++] = '\"'; 3766 buf[p++] = ','; 3767 buf[p++] = ' '; 3768 } 3769 /* 'algorithm="xxxx", ' */ 3770 if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) || 3771 (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5))) 3772 { 3773 memcpy (buf + p, prefix_algo, 3774 MHD_STATICSTR_LEN_ (prefix_algo)); 3775 p += MHD_STATICSTR_LEN_ (prefix_algo); 3776 #ifdef MHD_MD5_SUPPORT 3777 if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) 3778 { 3779 memcpy (buf + p, _MHD_MD5_TOKEN, 3780 MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN)); 3781 p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); 3782 } 3783 else 3784 #endif /* MHD_MD5_SUPPORT */ 3785 #ifdef MHD_SHA256_SUPPORT 3786 if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) 3787 { 3788 memcpy (buf + p, _MHD_SHA256_TOKEN, 3789 MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN)); 3790 p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); 3791 } 3792 else 3793 #endif /* MHD_SHA256_SUPPORT */ 3794 #ifdef MHD_SHA512_256_SUPPORT 3795 if (MHD_DIGEST_AUTH_ALGO3_SHA512_256 == s_algo) 3796 { 3797 memcpy (buf + p, _MHD_SHA512_256_TOKEN, 3798 MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN)); 3799 p += MHD_STATICSTR_LEN_ (_MHD_SHA512_256_TOKEN); 3800 } 3801 else 3802 #endif /* MHD_SHA512_256_SUPPORT */ 3803 mhd_assert (0); 3804 buf[p++] = ','; 3805 buf[p++] = ' '; 3806 } 3807 /* 'nonce="xxxx", ' */ 3808 memcpy (buf + p, prefix_nonce, 3809 MHD_STATICSTR_LEN_ (prefix_nonce)); 3810 p += MHD_STATICSTR_LEN_ (prefix_nonce); 3811 mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (da)))); 3812 if (! calculate_add_nonce_with_retry (connection, realm, da, buf + p)) 3813 { 3814 #ifdef MHD_DIGEST_HAS_EXT_ERROR 3815 if (digest_ext_error (da)) 3816 { 3817 #ifdef HAVE_MESSAGES 3818 MHD_DLOG (connection->daemon, 3819 _ ("TLS library reported hash calculation error, nonce could " 3820 "not be generated.\n")); 3821 #endif /* HAVE_MESSAGES */ 3822 return MHD_NO; 3823 } 3824 #endif /* MHD_DIGEST_HAS_EXT_ERROR */ 3825 #ifdef HAVE_MESSAGES 3826 MHD_DLOG (connection->daemon, 3827 _ ("Could not register nonce. Client's requests with this " 3828 "nonce will be always 'stale'. Probably clients' requests " 3829 "are too intensive.\n")); 3830 #endif /* HAVE_MESSAGES */ 3831 (void) 0; /* Mute compiler warning for builds without messages */ 3832 } 3833 p += NONCE_STD_LEN (digest_get_size (da)); 3834 buf[p++] = '\"'; 3835 buf[p++] = ','; 3836 buf[p++] = ' '; 3837 /* 'opaque="xxxx", ' */ 3838 if (NULL != opaque) 3839 { 3840 memcpy (buf + p, prefix_opaque, 3841 MHD_STATICSTR_LEN_ (prefix_opaque)); 3842 p += MHD_STATICSTR_LEN_ (prefix_opaque); 3843 mhd_assert ((buf_size - p) >= (opaque_len * 2)); 3844 p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p); 3845 buf[p++] = '\"'; 3846 buf[p++] = ','; 3847 buf[p++] = ' '; 3848 } 3849 /* 'domain="xxxx", ' */ 3850 if (NULL != domain) 3851 { 3852 memcpy (buf + p, prefix_domain, 3853 MHD_STATICSTR_LEN_ (prefix_domain)); 3854 p += MHD_STATICSTR_LEN_ (prefix_domain); 3855 mhd_assert ((buf_size - p) >= (domain_len * 2)); 3856 p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p); 3857 buf[p++] = '\"'; 3858 buf[p++] = ','; 3859 buf[p++] = ' '; 3860 } 3861 /* 'charset=UTF-8' */ 3862 if (MHD_NO != prefer_utf8) 3863 { 3864 memcpy (buf + p, str_charset, 3865 MHD_STATICSTR_LEN_ (str_charset)); 3866 p += MHD_STATICSTR_LEN_ (str_charset); 3867 buf[p++] = ','; 3868 buf[p++] = ' '; 3869 } 3870 /* 'userhash=true' */ 3871 if (MHD_NO != userhash_support) 3872 { 3873 memcpy (buf + p, str_userhash, 3874 MHD_STATICSTR_LEN_ (str_userhash)); 3875 p += MHD_STATICSTR_LEN_ (str_userhash); 3876 buf[p++] = ','; 3877 buf[p++] = ' '; 3878 } 3879 /* 'stale=true' */ 3880 if (MHD_NO != signal_stale) 3881 { 3882 memcpy (buf + p, str_stale, 3883 MHD_STATICSTR_LEN_ (str_stale)); 3884 p += MHD_STATICSTR_LEN_ (str_stale); 3885 buf[p++] = ','; 3886 buf[p++] = ' '; 3887 } 3888 mhd_assert (buf_size >= p); 3889 /* The built string ends with ", ". Replace comma with zero-termination. */ 3890 --p; 3891 buf[--p] = 0; 3892 3893 hdr_name = malloc (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1); 3894 if (NULL != hdr_name) 3895 { 3896 memcpy (hdr_name, MHD_HTTP_HEADER_WWW_AUTHENTICATE, 3897 MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1); 3898 if (MHD_add_response_entry_no_alloc_ (response, MHD_HEADER_KIND, 3899 hdr_name, 3900 MHD_STATICSTR_LEN_ ( \ 3901 MHD_HTTP_HEADER_WWW_AUTHENTICATE), 3902 buf, p)) 3903 { 3904 *buf_ptr = NULL; /* The buffer will be free()ed when the response is destroyed */ 3905 return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); 3906 } 3907 #ifdef HAVE_MESSAGES 3908 else 3909 { 3910 MHD_DLOG (connection->daemon, 3911 _ ("Failed to add Digest auth header.\n")); 3912 } 3913 #endif /* HAVE_MESSAGES */ 3914 free (hdr_name); 3915 } 3916 return MHD_NO; 3917 } 3918 3919 3920 /** 3921 * Queues a response to request authentication from the client 3922 * 3923 * This function modifies provided @a response. The @a response must not be 3924 * reused and should be destroyed (by #MHD_destroy_response()) after call of 3925 * this function. 3926 * 3927 * If @a mqop allows both RFC 2069 (MHD_DIGEST_AUTH_QOP_NONE) and QOP with 3928 * value, then response is formed like if MHD_DIGEST_AUTH_QOP_NONE bit was 3929 * not set, because such response should be backward-compatible with RFC 2069. 3930 * 3931 * If @a mqop allows only MHD_DIGEST_AUTH_MULT_QOP_NONE, then the response is 3932 * formed in strict accordance with RFC 2069 (no 'qop', no 'userhash', no 3933 * 'charset'). For better compatibility with clients, it is recommended (but 3934 * not required) to set @a domain to NULL in this mode. 3935 * 3936 * @param connection the MHD connection structure 3937 * @param realm the realm presented to the client 3938 * @param opaque the string for opaque value, can be NULL, but NULL is 3939 * not recommended for better compatibility with clients; 3940 * the recommended format is hex or Base64 encoded string 3941 * @param domain the optional space-separated list of URIs for which the 3942 * same authorisation could be used, URIs can be in form 3943 * "path-absolute" (the path for the same host with initial slash) 3944 * or in form "absolute-URI" (the full path with protocol), in 3945 * any case client may assume that URI is in the same "protection 3946 * space" if it starts with any of values specified here; 3947 * could be NULL (clients typically assume that the same 3948 * credentials could be used for any URI on the same host); 3949 * this list provides information for the client only and does 3950 * not actually restrict anything on the server side 3951 * @param response the reply to send; should contain the "access denied" 3952 * body; 3953 * note: this function sets the "WWW Authenticate" header and 3954 * the caller should not set this header; 3955 * the NULL is tolerated 3956 * @param signal_stale if set to #MHD_YES then indication of stale nonce used in 3957 * the client's request is signalled by adding 'stale=true' 3958 * to the authentication header, this instructs the client 3959 * to retry immediately with the new nonce and the same 3960 * credentials, without asking user for the new password 3961 * @param mqop the QOP to use 3962 * @param malgo3 digest algorithm to use; if several algorithms are allowed 3963 * then MD5 is preferred (currently, may be changed in next 3964 * versions) 3965 * @param userhash_support if set to non-zero value (#MHD_YES) then support of 3966 * userhash is indicated, allowing client to provide 3967 * hash("username:realm") instead of the username in 3968 * clear text; 3969 * note that clients are allowed to provide the username 3970 * in cleartext even if this parameter set to non-zero; 3971 * when userhash is used, application must be ready to 3972 * identify users by provided userhash value instead of 3973 * username; see #MHD_digest_auth_calc_userhash() and 3974 * #MHD_digest_auth_calc_userhash_hex() 3975 * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is 3976 * added, indicating for the client that UTF-8 encoding for 3977 * the username is preferred 3978 * @return #MHD_YES on success, #MHD_NO otherwise 3979 * @note Available since #MHD_VERSION 0x00097701 3980 * @ingroup authentication 3981 */ 3982 _MHD_EXTERN enum MHD_Result 3983 MHD_queue_auth_required_response3 (struct MHD_Connection *connection, 3984 const char *realm, 3985 const char *opaque, 3986 const char *domain, 3987 struct MHD_Response *response, 3988 int signal_stale, 3989 enum MHD_DigestAuthMultiQOP mqop, 3990 enum MHD_DigestAuthMultiAlgo3 malgo3, 3991 int userhash_support, 3992 int prefer_utf8) 3993 { 3994 struct DigestAlgorithm da; 3995 char *buf_ptr; 3996 enum MHD_Result ret; 3997 3998 buf_ptr = NULL; 3999 digest_setup_zero (&da); 4000 ret = queue_auth_required_response3_inner (connection, 4001 realm, 4002 opaque, 4003 domain, 4004 response, 4005 signal_stale, 4006 mqop, 4007 malgo3, 4008 userhash_support, 4009 prefer_utf8, 4010 &buf_ptr, 4011 &da); 4012 digest_deinit (&da); 4013 if (NULL != buf_ptr) 4014 free (buf_ptr); 4015 return ret; 4016 } 4017 4018 4019 /** 4020 * Queues a response to request authentication from the client 4021 * 4022 * @param connection The MHD connection structure 4023 * @param realm the realm presented to the client 4024 * @param opaque string to user for opaque value 4025 * @param response reply to send; should contain the "access denied" 4026 * body; note that this function will set the "WWW Authenticate" 4027 * header and that the caller should not do this; the NULL is tolerated 4028 * @param signal_stale #MHD_YES if the nonce is stale to add 4029 * 'stale=true' to the authentication header 4030 * @param algo digest algorithm to use 4031 * @return #MHD_YES on success, #MHD_NO otherwise 4032 * @note Available since #MHD_VERSION 0x00096200 4033 * @ingroup authentication 4034 */ 4035 _MHD_EXTERN enum MHD_Result 4036 MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, 4037 const char *realm, 4038 const char *opaque, 4039 struct MHD_Response *response, 4040 int signal_stale, 4041 enum MHD_DigestAuthAlgorithm algo) 4042 { 4043 enum MHD_DigestAuthMultiAlgo3 algo3; 4044 4045 if (MHD_DIGEST_ALG_MD5 == algo) 4046 algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; 4047 else if (MHD_DIGEST_ALG_SHA256 == algo) 4048 algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; 4049 else if (MHD_DIGEST_ALG_AUTO == algo) 4050 algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; 4051 else 4052 MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ 4053 4054 return MHD_queue_auth_required_response3 (connection, realm, opaque, 4055 NULL, response, signal_stale, 4056 MHD_DIGEST_AUTH_MULT_QOP_AUTH, 4057 algo3, 4058 0, 0); 4059 } 4060 4061 4062 /** 4063 * Queues a response to request authentication from the client. 4064 * For now uses MD5 (for backwards-compatibility). Still, if you 4065 * need to be sure, use #MHD_queue_auth_fail_response2(). 4066 * 4067 * @param connection The MHD connection structure 4068 * @param realm the realm presented to the client 4069 * @param opaque string to user for opaque value 4070 * @param response reply to send; should contain the "access denied" 4071 * body; note that this function will set the "WWW Authenticate" 4072 * header and that the caller should not do this; the NULL is tolerated 4073 * @param signal_stale #MHD_YES if the nonce is stale to add 4074 * 'stale=true' to the authentication header 4075 * @return #MHD_YES on success, #MHD_NO otherwise 4076 * @ingroup authentication 4077 * @deprecated use MHD_queue_auth_fail_response2() 4078 */ 4079 _MHD_EXTERN enum MHD_Result 4080 MHD_queue_auth_fail_response (struct MHD_Connection *connection, 4081 const char *realm, 4082 const char *opaque, 4083 struct MHD_Response *response, 4084 int signal_stale) 4085 { 4086 return MHD_queue_auth_fail_response2 (connection, 4087 realm, 4088 opaque, 4089 response, 4090 signal_stale, 4091 MHD_DIGEST_ALG_MD5); 4092 } 4093 4094 4095 /* end of digestauth.c */