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