auth_digest.c (110620B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2014-2025 Evgeny Grin (Karlson2k) 5 Copyright (C) 2010, 2011, 2012, 2015, 2018 Christian Grothoff 6 7 GNU libmicrohttpd is free software; you can redistribute it and/or 8 modify it under the terms of the GNU Lesser General Public 9 License as published by the Free Software Foundation; either 10 version 2.1 of the License, or (at your option) any later version. 11 12 GNU libmicrohttpd is distributed in the hope that it will be useful, 13 but WITHOUT ANY WARRANTY; without even the implied warranty of 14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 15 Lesser General Public License for more details. 16 17 Alternatively, you can redistribute GNU libmicrohttpd and/or 18 modify it under the terms of the GNU General Public License as 19 published by the Free Software Foundation; either version 2 of 20 the License, or (at your option) any later version, together 21 with the eCos exception, as follows: 22 23 As a special exception, if other files instantiate templates or 24 use macros or inline functions from this file, or you compile this 25 file and link it with other works to produce a work based on this 26 file, this file does not by itself cause the resulting work to be 27 covered by the GNU General Public License. However the source code 28 for this file must still be made available in accordance with 29 section (3) of the GNU General Public License v2. 30 31 This exception does not invalidate any other reasons why a work 32 based on this file might be covered by the GNU General Public 33 License. 34 35 You should have received copies of the GNU Lesser General Public 36 License and the GNU General Public License along with this library; 37 if not, see <https://www.gnu.org/licenses/>. 38 */ 39 40 /** 41 * @file src/mhd2/auth_digest.c 42 * @brief The implementation of the Digest Authorization internal functions 43 * @author Karlson2k (Evgeny Grin) 44 * Based on the MHD v0.xx code by Amr Ali, Matthieu Speder, Christian Grothoff, 45 * Dirk Brinkmeier and Evgeny Grin. 46 */ 47 48 #include "mhd_sys_options.h" 49 50 #include "mhd_digest_auth_data.h" 51 52 #include "mhd_assert.h" 53 #include "mhd_unreachable.h" 54 #include "mhd_assume.h" 55 56 #include <string.h> 57 #include "sys_malloc.h" 58 59 #include "mhd_str_macros.h" 60 #include "mhd_bithelpers.h" 61 #include "mhd_arr_num_elems.h" 62 #include "mhd_cntnr_ptr.h" 63 #include "mhd_limits.h" 64 #include "mhd_rng.h" 65 66 #include "mhd_str_types.h" 67 #include "mhd_buffer.h" 68 #include "mhd_daemon.h" 69 #include "mhd_request.h" 70 #include "mhd_connection.h" 71 72 #ifdef MHD_SUPPORT_SHA512_256 73 # include "mhd_sha512_256.h" 74 #endif /* MHD_SUPPORT_SHA512_256 */ 75 #ifdef MHD_SUPPORT_SHA256 76 # include "mhd_sha256.h" 77 #endif 78 #ifdef MHD_SUPPORT_MD5 79 # include "mhd_md5.h" 80 #endif 81 82 #include "mhd_str.h" 83 #include "mhd_mono_clock.h" 84 #include "mhd_atomic_counter.h" 85 #include "mhd_locks.h" 86 87 #include "request_auth_get.h" 88 #include "daemon_funcs.h" 89 #include "stream_funcs.h" 90 #include "stream_process_request.h" 91 92 #include "auth_digest.h" 93 94 /* 95 * The maximum size of the hash digest, in bytes 96 */ 97 #if defined(MHD_SUPPORT_SHA512_256) 98 # define mhd_MAX_DIGEST mhd_SHA512_256_DIGEST_SIZE 99 #elif defined(MHD_SUPPORT_SHA256) 100 # define mhd_MAX_DIGEST mhd_SHA256_DIGEST_SIZE 101 #else 102 # define mhd_MAX_DIGEST mhd_MD5_DIGEST_SIZE 103 #endif 104 105 /** 106 * MD5 algorithm identifier for Digest Auth headers 107 */ 108 #define mhd_MD5_TOKEN "MD5" 109 110 /** 111 * SHA-256 algorithm identifier for Digest Auth headers 112 */ 113 #define mhd_SHA256_TOKEN "SHA-256" 114 115 /** 116 * SHA-512/256 algorithm for Digest Auth headers. 117 */ 118 #define mhd_SHA512_256_TOKEN "SHA-512-256" 119 120 /** 121 * The suffix token for "session" algorithms for Digest Auth headers. 122 */ 123 #define mhd_SESS_TOKEN "-sess" 124 125 /** 126 * The "auth" token for QOP for Digest Auth headers. 127 */ 128 #define mhd_TOKEN_AUTH "auth" 129 130 /** 131 * The "auth-int" token for QOP for Digest Auth headers. 132 */ 133 #define mhd_TOKEN_AUTH_INT "auth-int" 134 135 136 /** 137 * The required prefix of parameter with the extended notation 138 */ 139 #define mhd_DAUTH_EXT_PARAM_PREFIX "UTF-8'" 140 141 /** 142 * The minimal size of the prefix for parameter with the extended notation 143 */ 144 #define mhd_DAUTH_EXT_PARAM_MIN_LEN \ 145 mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX "'") 146 147 /** 148 * The maximum supported size for Digest Auth parameters, like "realm", 149 * "username" etc. 150 * This limitation is used only for quoted parameters. 151 * Parameters without quoted backslash character will be processed as long 152 * as they fit connection memory pool (buffer) size. 153 */ 154 #define mhd_AUTH_DIGEST_MAX_PARAM_SIZE (65535) 155 156 /** 157 * Parameter of request's Digest Authorization header 158 */ 159 struct mhd_RqDAuthParam 160 { 161 /** 162 * The string with length, NOT zero-terminated 163 */ 164 struct MHD_StringNullable value; 165 /** 166 * True if string must be "unquoted" before processing. 167 * This member is false if the string is used in DQUOTE marks, but no 168 * backslash-escape is used in the string. 169 */ 170 bool quoted; 171 }; 172 173 /** 174 * Client's Digest Authorization header parameters 175 */ 176 struct mhd_AuthDigesReqParams 177 { 178 struct mhd_RqDAuthParam nonce; 179 struct mhd_RqDAuthParam opaque; 180 struct mhd_RqDAuthParam response; 181 struct mhd_RqDAuthParam username; 182 struct mhd_RqDAuthParam username_ext; 183 struct mhd_RqDAuthParam realm; 184 struct mhd_RqDAuthParam uri; 185 /* The raw QOP value, used in the 'response' calculation */ 186 struct mhd_RqDAuthParam qop_raw; 187 struct mhd_RqDAuthParam cnonce; 188 struct mhd_RqDAuthParam nc; 189 190 /* Decoded values are below */ 191 bool userhash; /* True if 'userhash' parameter has value 'true'. */ 192 enum MHD_DigestAuthAlgo algo; 193 enum MHD_DigestAuthQOP qop; 194 }; 195 196 /** 197 * Digest context data 198 */ 199 union DigestCtx 200 { 201 #ifdef MHD_SUPPORT_SHA512_256 202 struct mhd_Sha512_256Ctx sha512_256_ctx; 203 #endif /* MHD_SUPPORT_SHA512_256 */ 204 #ifdef MHD_SUPPORT_SHA256 205 struct mhd_Sha256Ctx sha256_ctx; 206 #endif /* MHD_SUPPORT_SHA256 */ 207 #ifdef MHD_SUPPORT_MD5 208 struct mhd_Md5Ctx md5_ctx; 209 #endif /* MHD_SUPPORT_MD5 */ 210 }; 211 212 mhd_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE 213 214 /** 215 * Generate simple hash. 216 * Very limited avalanche effect. To be used mainly for the table slot choice. 217 * @param data_size the size of the data to hash 218 * @param data the data to hash 219 * @return the hash value 220 */ 221 static MHD_FN_PAR_NONNULL_ALL_ 222 MHD_FN_PAR_IN_SIZE_ (2, 1) uint_fast64_t 223 simple_hash (size_t data_size, 224 const uint8_t *restrict data) 225 { 226 static const uint_fast64_t c[] = { /* Some fractional parts of Euler's number */ 227 UINT64_C (0xCC64D3484C3475A1), 228 UINT64_C (0xCF4DEBCB9ED801F2), 229 UINT64_C (0x0C8737A803CF46AD), 230 UINT64_C (0x294C9E0E0F9F14AB), 231 UINT64_C (0xAD786D855D4EBB1A) 232 }; 233 uint_fast64_t res; 234 size_t i; 235 236 res = UINT64_C (0x8316A8FE31A2228E); /* Some fractional part of Pi */ 237 i = 0; 238 while (1) 239 { 240 uint_fast64_t a = 0; 241 242 if (8 <= data_size) 243 memcpy (&a, data, 8); 244 else 245 memcpy (&a, data, data_size); 246 a ^= c[(i++) % mhd_ARR_NUM_ELEMS (c)]; 247 a = (uint_fast64_t)mhd_ROTR64 ((uint64_t)a, \ 248 (unsigned int)(res >> 58u)); 249 res ^= a; 250 if (8 >= data_size) 251 break; 252 data_size -= 8; 253 data += 8; 254 } 255 return res; 256 } 257 258 259 /** 260 * Find index of the provided nonce in the nonces table 261 * @param nonce the nonce to use 262 * @param arr_size the size of the nonces table 263 * @return the index 264 */ 265 static MHD_FN_PAR_NONNULL_ALL_ size_t 266 nonce_to_index (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE], 267 size_t arr_size) 268 { 269 uint_fast64_t hash; 270 hash = simple_hash (mhd_AUTH_DIGEST_NONCE_BIN_SIZE, 271 nonce); 272 if (arr_size == (arr_size & UINT32_C (0xFFFFFFFF))) 273 { /* 'arr_size' <=32-bit */ 274 hash = (hash ^ (hash >> 32)) & UINT32_C (0xFFFFFFFF); /* Fold hash */ 275 if (arr_size == (arr_size & UINT16_C (0xFFFF))) 276 { /* 'arr_size' <=16-bit */ 277 hash = (hash ^ (hash >> 16)) & UINT16_C (0xFFFF); /* Fold hash */ 278 if (arr_size == (arr_size & 0xFFu)) 279 hash = (hash ^ (hash >> 8)) & 0xFFu; /* 'arr_size' <=8-bit, fold hash */ 280 } 281 } 282 return ((size_t)hash) % arr_size; 283 } 284 285 286 mhd_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE 287 288 289 /** 290 * Generate a new nonce 291 * @param d the daemon to use (must match @a c connection) 292 * @param c the connection to generate nonce for 293 * @param[out] out_buf the output buffer to pull full nonce, including 294 * "expiration" tail 295 * @param[out] expir the expiration mark, duplicated for convenience 296 * @return 'true' if succeed, 297 * 'false' if failed 298 */ 299 static MHD_FN_PAR_NONNULL_ALL_ 300 MHD_FN_PAR_OUT_ (3) 301 MHD_FN_PAR_OUT_ (4) bool 302 gen_new_nonce (struct MHD_Daemon *restrict d, 303 struct MHD_Connection *restrict c, 304 uint8_t out_buf[mhd_AUTH_DIGEST_NONCE_BIN_SIZE], 305 uint_fast32_t *restrict expir) 306 { 307 uint_fast64_t expiration; 308 309 mhd_assert (!mhd_D_HAS_MASTER (d)); /* only master daemon should be used */ 310 mhd_assert (d == c->daemon); 311 mhd_assert (0 != d->auth_dg.cfg.nonce_tmout); 312 313 expiration = mhd_monotonic_msec_counter () 314 + d->auth_dg.cfg.nonce_tmout * (uint_fast64_t)1000; 315 316 if (!mhd_rng (mhd_AUTH_DIGEST_NONCE_BIN_SIZE, 317 out_buf)) 318 { 319 /* Fallback to generating nonce from application-provided 320 entropy. Note: this should fail if we do not have 321 application-provided entropy. */ 322 size_t gen_num; 323 union DigestCtx d_ctx; 324 325 gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces)); 326 327 #if defined(MHD_SUPPORT_SHA512_256) 328 mhd_SHA512_256_init (&(d_ctx.sha512_256_ctx)); 329 mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx), 330 d->auth_dg.entropy.size, 331 (const uint8_t *)d->auth_dg.entropy.data); 332 mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx), 333 sizeof(gen_num), 334 (const uint8_t *)&gen_num); 335 if (0 != c->sk.addr.size) 336 mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx), 337 c->sk.addr.size, 338 (const uint8_t *)c->sk.addr.data); 339 mhd_SHA512_256_update (&(d_ctx.sha512_256_ctx), 340 sizeof(expiration), 341 (const uint8_t *)&expiration); 342 mhd_SHA512_256_finish_deinit (&(d_ctx.sha512_256_ctx), \ 343 out_buf); 344 if (mhd_SHA512_256_has_err (&(d_ctx.sha512_256_ctx))) 345 return false; 346 #elif defined(MHD_SUPPORT_SHA256) 347 mhd_SHA256_init (&(d_ctx.sha256_ctx)); 348 mhd_SHA256_update (&(d_ctx.sha256_ctx), 349 d->auth_dg.entropy.size, 350 (const void *)d->auth_dg.entropy.data); 351 mhd_SHA256_update (&(d_ctx.sha256_ctx), 352 sizeof(gen_num), 353 (const void *)&gen_num); 354 if (0 != c->sk.addr.size) 355 mhd_SHA256_update (&(d_ctx.sha256_ctx), 356 c->sk.addr.size, 357 (const void *)c->sk.addr.data); 358 mhd_SHA256_update (&(d_ctx.sha256_ctx), 359 sizeof(expiration), 360 (const void *)&expiration); 361 mhd_SHA256_finish_deinit (&(d_ctx.sha256_ctx), \ 362 out_buf); 363 if (mhd_SHA256_has_err (&(d_ctx.sha256_ctx))) 364 return false; 365 #else /* MHD_SUPPORT_MD5 */ 366 # ifndef MHD_SUPPORT_MD5 367 # error At least one hashing algorithm must be enabled 368 # endif 369 mhd_MD5_init (&(d_ctx.md5_ctx)); 370 mhd_MD5_update (&(d_ctx.md5_ctx), 371 d->auth_dg.entropy.size, 372 (const void *)d->auth_dg.entropy.data); 373 mhd_MD5_update (&(d_ctx.md5_ctx), 374 sizeof(gen_num), 375 (const void *)&gen_num); 376 if (0 != c->sk.addr.size) 377 mhd_MD5_update (&(d_ctx.md5_ctx), 378 c->sk.addr.size, 379 (const void *)c->sk.addr.data); 380 mhd_MD5_update (&(d_ctx.md5_ctx), 381 sizeof(expiration), 382 (const void *)&expiration); 383 mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \ 384 out_buf); 385 if (mhd_MD5_has_err (&(d_ctx.md5_ctx))) 386 return false; 387 388 /* One more hash, for the second part */ 389 gen_num = mhd_atomic_counter_get_inc_wrap (&(d->auth_dg.num_gen_nonces)); 390 391 mhd_MD5_init (&(d_ctx.md5_ctx)); 392 mhd_MD5_update (&(d_ctx.md5_ctx), 393 d->auth_dg.entropy.size, 394 (const void *)d->auth_dg.entropy.data); 395 mhd_MD5_update (&(d_ctx.md5_ctx), 396 sizeof(gen_num), 397 (const void *)&gen_num); 398 if (0 != c->sk.addr.size) 399 mhd_MD5_update (&(d_ctx.md5_ctx), 400 c->sk.addr.size, 401 (const void *)c->sk.addr.data); 402 mhd_MD5_update (&(d_ctx.md5_ctx), 403 sizeof(expiration), 404 (const void *)&expiration); 405 mhd_MD5_finish_deinit (&(d_ctx.md5_ctx), \ 406 out_buf + mhd_MD5_DIGEST_SIZE); 407 if (mhd_MD5_has_err (&(d_ctx.md5_ctx))) 408 return false; 409 #endif /* MHD_SUPPORT_MD5 */ 410 411 } 412 413 *expir = (uint_fast32_t)(expiration / 1000u); 414 mhd_PUT_32BIT_LE_UNALIGN (out_buf + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE, \ 415 (uint32_t)(*expir & UINT32_C (0xFFFFFFFF))); 416 417 return true; 418 } 419 420 421 MHD_INTERNAL MHD_FN_PAR_NONNULL_ALL_ 422 MHD_FN_PAR_OUT_ (2) bool 423 mhd_auth_digest_get_new_nonce (struct MHD_Connection *restrict c, 424 char out_buf[mhd_AUTH_DIGEST_NONCE_LEN]) 425 { 426 static const int max_retries = 3; 427 struct MHD_Daemon *restrict d = mhd_daemon_get_master_daemon (c->daemon); 428 uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE]; 429 uint_fast32_t expir; 430 bool nonce_generated; 431 int i; 432 433 mhd_assert (0 != d->auth_dg.cfg.nonces_num); 434 mhd_assert (NULL != d->auth_dg.nonces); 435 436 nonce_generated = false; 437 for (i = 0; i < max_retries; ++i) 438 { 439 bool good_nonce; 440 struct mhd_DaemonAuthDigestNonceData *nonce_slot; 441 if (!gen_new_nonce (d, 442 c, 443 nonce_bin, 444 &expir)) 445 continue; /* Failed, re-try */ 446 447 nonce_generated = true; 448 nonce_slot = d->auth_dg.nonces 449 + nonce_to_index (nonce_bin, 450 d->auth_dg.cfg.nonces_num); 451 if (!mhd_mutex_lock (&(d->auth_dg.nonces_lock))) 452 return false; /* Failure exit point */ 453 /* Check whether the same nonce has been used before */ 454 good_nonce = (0 != memcmp (nonce_slot->nonce, 455 nonce_bin, 456 sizeof(nonce_slot->nonce))); 457 if (good_nonce) 458 { 459 memcpy (nonce_slot->nonce, 460 nonce_bin, 461 sizeof(nonce_slot->nonce)); 462 nonce_slot->valid_time = expir; 463 nonce_slot->max_recvd_nc = 0; 464 nonce_slot->nmask = 0; 465 } 466 else 467 { 468 /* Check whether the same nonce has been used with different expiration 469 time. */ 470 nonce_generated = (nonce_slot->valid_time == expir); 471 } 472 mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock)); 473 if (good_nonce) 474 break; 475 } 476 if (!nonce_generated) 477 return false; /* Failure exit point */ 478 479 /* Use the generated nonce even if it is duplicated. 480 One of the clients will just get "nonce stale" response with 481 the new nonce. */ 482 (void)mhd_bin_to_hex (nonce_bin, 483 sizeof(nonce_bin), 484 out_buf); 485 return true; /* Success exit point */ 486 } 487 488 489 /** 490 * Get client's Digest Authorization algorithm type. 491 * If no algorithm is specified by client, MD5 is assumed. 492 * @param algo_param the Digest Authorization 'algorithm' parameter 493 * @return the algorithm type 494 */ 495 static enum MHD_DigestAuthAlgo 496 get_rq_dauth_algo (const struct mhd_RqDAuthParam *const algo_param) 497 { 498 if (NULL == algo_param->value.cstr) 499 return MHD_DIGEST_AUTH_ALGO_MD5; /* Assume MD5 by default */ 500 501 if (algo_param->quoted) 502 { 503 if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \ 504 algo_param->value.len, \ 505 mhd_MD5_TOKEN)) 506 return MHD_DIGEST_AUTH_ALGO_MD5; 507 if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \ 508 algo_param->value.len, \ 509 mhd_SHA256_TOKEN)) 510 return MHD_DIGEST_AUTH_ALGO_SHA256; 511 if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \ 512 algo_param->value.len, \ 513 mhd_SHA512_256_TOKEN)) 514 return MHD_DIGEST_AUTH_ALGO_SHA512_256; 515 516 /* Algorithms below are not supported by MHD for authentication */ 517 518 if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \ 519 algo_param->value.len, \ 520 mhd_MD5_TOKEN mhd_SESS_TOKEN)) 521 return MHD_DIGEST_AUTH_ALGO_MD5_SESSION; 522 if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \ 523 algo_param->value.len, \ 524 mhd_SHA256_TOKEN \ 525 mhd_SESS_TOKEN)) 526 return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION; 527 if (mhd_str_equal_caseless_quoted_s_bin_n (algo_param->value.cstr, \ 528 algo_param->value.len, \ 529 mhd_SHA512_256_TOKEN \ 530 mhd_SESS_TOKEN)) 531 return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION; 532 533 /* No known algorithm has been detected */ 534 return MHD_DIGEST_AUTH_ALGO_INVALID; 535 } 536 /* The algorithm value is not quoted */ 537 if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN, \ 538 algo_param->value.cstr, \ 539 algo_param->value.len)) 540 return MHD_DIGEST_AUTH_ALGO_MD5; 541 if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN, \ 542 algo_param->value.cstr, \ 543 algo_param->value.len)) 544 return MHD_DIGEST_AUTH_ALGO_SHA256; 545 if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN, \ 546 algo_param->value.cstr, \ 547 algo_param->value.len)) 548 return MHD_DIGEST_AUTH_ALGO_SHA512_256; 549 550 /* Algorithms below are not supported by MHD for authentication */ 551 552 if (mhd_str_equal_caseless_n_st (mhd_MD5_TOKEN mhd_SESS_TOKEN, \ 553 algo_param->value.cstr, \ 554 algo_param->value.len)) 555 return MHD_DIGEST_AUTH_ALGO_MD5_SESSION; 556 if (mhd_str_equal_caseless_n_st (mhd_SHA256_TOKEN mhd_SESS_TOKEN, \ 557 algo_param->value.cstr, \ 558 algo_param->value.len)) 559 return MHD_DIGEST_AUTH_ALGO_SHA256_SESSION; 560 if (mhd_str_equal_caseless_n_st (mhd_SHA512_256_TOKEN mhd_SESS_TOKEN, \ 561 algo_param->value.cstr, \ 562 algo_param->value.len)) 563 return MHD_DIGEST_AUTH_ALGO_SHA512_256_SESSION; 564 565 /* No known algorithm has been detected */ 566 return MHD_DIGEST_AUTH_ALGO_INVALID; 567 } 568 569 570 /** 571 * Get QOP ('quality of protection') type. 572 * @param qop_param the Digest Authorization 'QOP' parameter 573 * @return detected QOP ('quality of protection') type. 574 */ 575 static enum MHD_DigestAuthQOP 576 get_rq_dauth_qop (const struct mhd_RqDAuthParam *const qop_param) 577 { 578 if (NULL == qop_param->value.cstr) 579 return MHD_DIGEST_AUTH_QOP_NONE; 580 if (qop_param->quoted) 581 { 582 if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \ 583 qop_param->value.len, \ 584 mhd_TOKEN_AUTH)) 585 return MHD_DIGEST_AUTH_QOP_AUTH; 586 if (mhd_str_equal_caseless_quoted_s_bin_n (qop_param->value.cstr, \ 587 qop_param->value.len, \ 588 mhd_TOKEN_AUTH_INT)) 589 return MHD_DIGEST_AUTH_QOP_AUTH_INT; 590 } 591 else 592 { 593 if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH, \ 594 qop_param->value.cstr, \ 595 qop_param->value.len)) 596 return MHD_DIGEST_AUTH_QOP_AUTH; 597 if (mhd_str_equal_caseless_n_st (mhd_TOKEN_AUTH_INT, \ 598 qop_param->value.cstr, \ 599 qop_param->value.len)) 600 return MHD_DIGEST_AUTH_QOP_AUTH_INT; 601 } 602 /* No know QOP has been detected */ 603 return MHD_DIGEST_AUTH_QOP_INVALID; 604 } 605 606 607 /** 608 * Parse request Authorization header parameters for Digest Authentication 609 * @param val the header string, everything after "Digest " substring 610 * @param[out] pdauth the pointer to the structure with Digest Authentication 611 * parameters 612 * @return true if parameters has been successfully parsed, 613 * false if format of the @a str is invalid 614 */ 615 static MHD_FN_PAR_NONNULL_ALL_ 616 MHD_FN_PAR_OUT_ (2) bool 617 parse_dauth_params (const struct MHD_String *restrict val, 618 struct mhd_AuthDigesReqParams *restrict pdauth) 619 { 620 /* The tokens */ 621 static const struct MHD_String nonce_tk = mhd_MSTR_INIT ("nonce"); 622 static const struct MHD_String opaque_tk = mhd_MSTR_INIT ("opaque"); 623 static const struct MHD_String algorithm_tk = mhd_MSTR_INIT ("algorithm"); 624 static const struct MHD_String response_tk = mhd_MSTR_INIT ("response"); 625 static const struct MHD_String username_tk = mhd_MSTR_INIT ("username"); 626 static const struct MHD_String username_ext_tk = mhd_MSTR_INIT ("username*"); 627 static const struct MHD_String realm_tk = mhd_MSTR_INIT ("realm"); 628 static const struct MHD_String uri_tk = mhd_MSTR_INIT ("uri"); 629 static const struct MHD_String qop_tk = mhd_MSTR_INIT ("qop"); 630 static const struct MHD_String cnonce_tk = mhd_MSTR_INIT ("cnonce"); 631 static const struct MHD_String nc_tk = mhd_MSTR_INIT ("nc"); 632 static const struct MHD_String userhash_tk = mhd_MSTR_INIT ("userhash"); 633 /* The locally processed parameters */ 634 struct mhd_RqDAuthParam userhash = { {0, NULL}, false}; 635 struct mhd_RqDAuthParam algorithm = { {0, NULL}, false}; 636 /* Indexes */ 637 size_t i; 638 size_t p; 639 /* The list of the tokens. 640 The order of the elements matches the next array. */ 641 static const struct MHD_String *const tk_names[] = { 642 &nonce_tk, /* 0 */ 643 &opaque_tk, /* 1 */ 644 &algorithm_tk, /* 2 */ 645 &response_tk, /* 3 */ 646 &username_tk, /* 4 */ 647 &username_ext_tk, /* 5 */ 648 &realm_tk, /* 6 */ 649 &uri_tk, /* 7 */ 650 &qop_tk, /* 8 */ 651 &cnonce_tk, /* 9 */ 652 &nc_tk, /* 10 */ 653 &userhash_tk /* 11 */ 654 }; 655 /* The list of the parameters. 656 The order of the elements matches the previous array. */ 657 struct mhd_RqDAuthParam *params[sizeof(tk_names) / sizeof(tk_names[0])]; 658 659 params[0] = &(pdauth->nonce); /* 0 */ 660 params[1] = &(pdauth->opaque); /* 1 */ 661 params[2] = &algorithm; /* 2 */ 662 params[3] = &(pdauth->response); /* 3 */ 663 params[4] = &(pdauth->username); /* 4 */ 664 params[5] = &(pdauth->username_ext); /* 5 */ 665 params[6] = &(pdauth->realm); /* 6 */ 666 params[7] = &(pdauth->uri); /* 7 */ 667 params[8] = &(pdauth->qop_raw); /* 8 */ 668 params[9] = &(pdauth->cnonce); /* 9 */ 669 params[10] = &(pdauth->nc); /* 10 */ 670 params[11] = &userhash; /* 11 */ 671 672 mhd_assert (mhd_ARR_NUM_ELEMS (tk_names) == \ 673 mhd_ARR_NUM_ELEMS (params)); 674 i = 0; 675 676 mhd_assert (' ' != val->cstr[0]); 677 mhd_assert ('\t' != val->cstr[0]); 678 679 while (val->len > i) 680 { 681 size_t left; 682 mhd_assert (' ' != val->cstr[i]); 683 mhd_assert ('\t' != val->cstr[i]); 684 685 left = val->len - i; 686 if ('=' == val->cstr[i]) 687 return false; /* The equal sign is not allowed as the first character */ 688 for (p = 0; p < mhd_ARR_NUM_ELEMS (tk_names); ++p) 689 { 690 const struct MHD_String *const tk_name = tk_names[p]; 691 struct mhd_RqDAuthParam *const param = params[p]; 692 if ((tk_name->len <= left) 693 && mhd_str_equal_caseless_bin_n (val->cstr + i, tk_name->cstr, 694 tk_name->len) 695 && ((tk_name->len == left) 696 || ('=' == val->cstr[i + tk_name->len]) 697 || (' ' == val->cstr[i + tk_name->len]) 698 || ('\t' == val->cstr[i + tk_name->len]) 699 || (',' == val->cstr[i + tk_name->len]) 700 || (';' == val->cstr[i + tk_name->len]))) 701 { 702 size_t value_start; 703 size_t value_len; 704 bool quoted; /* Only mark as "quoted" if backslash-escape used */ 705 706 if (tk_name->len == left) 707 return false; /* No equal sign after parameter name, broken data */ 708 709 quoted = false; 710 i += tk_name->len; 711 /* Skip all whitespaces before '=' */ 712 while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i])) 713 i++; 714 if ((i == val->len) || ('=' != val->cstr[i])) 715 return false; /* No equal sign, broken data */ 716 i++; 717 /* Skip all whitespaces after '=' */ 718 while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i])) 719 i++; 720 if ((val->len > i) && ('"' == val->cstr[i])) 721 { /* Value is in quotation marks */ 722 i++; /* Advance after the opening quote */ 723 value_start = i; 724 while (val->len > i && '"' != val->cstr[i]) 725 { 726 if ('\\' == val->cstr[i]) 727 { 728 i++; 729 quoted = true; /* Have escaped chars */ 730 } 731 if (0 == val->cstr[i]) 732 return false; /* Binary zero in parameter value */ 733 i++; 734 } 735 if (val->len <= i) 736 return false; /* No closing quote */ 737 mhd_assert ('"' == val->cstr[i]); 738 value_len = i - value_start; 739 i++; /* Advance after the closing quote */ 740 } 741 else 742 { 743 value_start = i; 744 while ((val->len > i) && (',' != val->cstr[i]) 745 && (' ' != val->cstr[i]) && ('\t' != val->cstr[i]) 746 && (';' != val->cstr[i])) 747 { 748 if (0 == val->cstr[i]) 749 return false; /* Binary zero in parameter value */ 750 i++; 751 } 752 if (';' == val->cstr[i]) 753 return false; /* Semicolon in parameter value */ 754 value_len = i - value_start; 755 } 756 /* Skip all whitespaces after parameter value */ 757 while (val->len > i && (' ' == val->cstr[i] || '\t' == val->cstr[i])) 758 i++; 759 if ((val->len > i) && (',' != val->cstr[i])) 760 return false; /* Garbage after parameter value */ 761 762 /* Have valid parameter name and value */ 763 mhd_assert (!quoted || 0 != value_len); 764 param->value.cstr = val->cstr + value_start; 765 param->value.len = value_len; 766 param->quoted = quoted; 767 768 break; /* Found matching parameter name */ 769 } 770 } 771 if (p == mhd_ARR_NUM_ELEMS (tk_names)) 772 { 773 /* No matching parameter name */ 774 while (val->len > i && ',' != val->cstr[i]) 775 { 776 if ((0 == val->cstr[i]) || (';' == val->cstr[i])) 777 return false; /* Not allowed characters */ 778 if ('"' == val->cstr[i]) 779 { /* Skip quoted part */ 780 i++; /* Advance after the opening quote */ 781 while (val->len > i && '"' != val->cstr[i]) 782 { 783 if (0 == val->cstr[i]) 784 return false; /* Binary zero is not allowed */ 785 if ('\\' == val->cstr[i]) 786 i++; /* Skip escaped char */ 787 i++; 788 } 789 if (val->len <= i) 790 return false; /* No closing quote */ 791 mhd_assert ('"' == val->cstr[i]); 792 } 793 i++; 794 } 795 } 796 mhd_assert (val->len == i || ',' == val->cstr[i]); 797 if (val->len > i) 798 i++; /* Advance after ',' */ 799 /* Skip all whitespaces before next parameter name */ 800 while (i < val->len && (' ' == val->cstr[i] || '\t' == val->cstr[i])) 801 i++; 802 } 803 804 /* Postprocess values */ 805 806 if (NULL != userhash.value.cstr) 807 { 808 if (userhash.quoted) 809 pdauth->userhash = 810 mhd_str_equal_caseless_quoted_s_bin_n (userhash.value.cstr, \ 811 userhash.value.len, \ 812 "true"); 813 else 814 pdauth->userhash = 815 mhd_str_equal_caseless_n_st ("true", userhash.value.cstr, \ 816 userhash.value.len); 817 818 } 819 else 820 pdauth->userhash = false; 821 822 pdauth->algo = get_rq_dauth_algo (&algorithm); 823 pdauth->qop = get_rq_dauth_qop (&pdauth->qop_raw); 824 825 return true; 826 } 827 828 829 /** 830 * Find and pre-parse request's Digest Authorisation parameters. 831 * 832 * Function returns result of pre-parsing of the request's "Authorization" 833 * header or returns cached result if the header has been already pre-parsed for 834 * the current request. 835 * @param req the request to process 836 * @return #MHD_SC_OK if succeed, 837 * #MHD_SC_AUTH_ABSENT if request has no Digest Authorisation, 838 * #MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA if not enough memory, 839 * #MHD_SC_REQ_AUTH_DATA_BROKEN if the header is broken. 840 */ 841 static MHD_FN_PAR_NONNULL_ALL_ enum MHD_StatusCode 842 get_rq_auth_digest_params (struct MHD_Request *restrict req) 843 { 844 struct MHD_String h_auth_value; 845 struct mhd_AuthDigesReqParams *dauth; 846 847 mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \ 848 mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage); 849 mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \ 850 mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage); 851 852 if (NULL != req->auth.digest.rqp) 853 return MHD_SC_OK; 854 855 if (!mhd_request_get_auth_header_value (req, 856 mhd_AUTH_HDR_DIGEST, 857 &h_auth_value)) 858 return MHD_SC_AUTH_ABSENT; 859 860 dauth = 861 (struct mhd_AuthDigesReqParams *) 862 mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, \ 863 struct MHD_Connection, \ 864 rq), 865 sizeof (struct mhd_AuthDigesReqParams)); 866 867 if (NULL == dauth) 868 return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA; 869 870 memset (dauth, 0, sizeof(struct mhd_AuthDigesReqParams)); 871 if (!parse_dauth_params (&h_auth_value, 872 dauth)) 873 return MHD_SC_REQ_AUTH_DATA_BROKEN; 874 875 req->auth.digest.rqp = dauth; 876 877 return MHD_SC_OK; 878 } 879 880 881 /** 882 * Get username type used by the client. 883 * This function does not check whether userhash can be decoded or 884 * extended notation (if used) is valid. 885 * @param params the Digest Authorization parameters 886 * @return the type of username 887 */ 888 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ enum MHD_DigestAuthUsernameType 889 get_rq_uname_type (const struct mhd_AuthDigesReqParams *params) 890 { 891 if (NULL != params->username.value.cstr) 892 { 893 if (NULL == params->username_ext.value.cstr) 894 return params->userhash ? 895 MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH : 896 MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD; 897 else /* Both 'username' and 'username*' are used */ 898 return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 899 } 900 else if (NULL != params->username_ext.value.cstr) 901 { 902 if (!params->username_ext.quoted && !params->userhash 903 && (mhd_DAUTH_EXT_PARAM_MIN_LEN <= params->username_ext.value.len)) 904 return MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED; 905 else 906 return MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 907 } 908 909 return MHD_DIGEST_AUTH_UNAME_TYPE_MISSING; 910 } 911 912 913 /** 914 * Get total size required for 'username' and 'userhash_bin' 915 * @param params the Digest Authorization parameters 916 * @param uname_type the type of username 917 * @return the total size required for 'username' and 918 * 'userhash_bin' is userhash is used 919 */ 920 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ size_t 921 get_rq_unames_size (const struct mhd_AuthDigesReqParams *params, 922 enum MHD_DigestAuthUsernameType uname_type) 923 { 924 size_t s; 925 926 mhd_assert (get_rq_uname_type (params) == uname_type); 927 s = 0; 928 if ((MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) 929 || (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)) 930 { 931 s += params->username.value.len + 1; /* Add one byte for zero-termination */ 932 if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) 933 s += (params->username.value.len + 1) / 2; 934 } 935 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type) 936 s += params->username_ext.value.len 937 - mhd_DAUTH_EXT_PARAM_MIN_LEN + 1; /* Add one byte for zero-termination */ 938 return s; 939 } 940 941 942 /** 943 * Get unquoted version of Digest Authorization parameter. 944 * This function automatically zero-teminate the result. 945 * @param param the parameter to extract 946 * @param[out] buf the output buffer, must have enough size to hold the result, 947 * the recommended size is 'param->value.len + 1' 948 * @return the size of the result, not including the terminating zero 949 */ 950 static MHD_FN_PAR_NONNULL_ALL_ 951 MHD_FN_PAR_OUT_ (2) size_t 952 get_rq_param_unquoted_copy_z (const struct mhd_RqDAuthParam *restrict param, 953 char *restrict buf) 954 { 955 size_t len; 956 mhd_assert (NULL != param->value.cstr); 957 if (!param->quoted) 958 { 959 memcpy (buf, param->value.cstr, param->value.len); 960 buf[param->value.len] = 0; 961 return param->value.len; 962 } 963 964 len = mhd_str_unquote (param->value.cstr, param->value.len, buf); 965 mhd_assert (0 != len); 966 mhd_assert (len < param->value.len); 967 buf[len] = 0; 968 return len; 969 } 970 971 972 /** 973 * Get decoded version of username from extended notation. 974 * This function automatically zero-teminate the result. 975 * @param uname_ext the string of client's 'username*' parameter value 976 * @param uname_ext_len the length of @a uname_ext in chars 977 * @param[out] buf the output buffer to put decoded username value 978 * @param buf_size the size of @a buf 979 * @return the number of characters copied to the output buffer or 980 * -1 if wrong extended notation is used. 981 */ 982 static MHD_FN_PAR_NONNULL_ALL_ 983 MHD_FN_PAR_IN_SIZE_ (1, 2) 984 MHD_FN_PAR_OUT_SIZE_ (3, 4) ssize_t 985 get_rq_extended_uname_copy_z (const char *restrict uname_ext, 986 size_t uname_ext_len, 987 char *restrict buf, 988 size_t buf_size) 989 { 990 size_t r; 991 size_t w; 992 if ((size_t)SSIZE_MAX < uname_ext_len) 993 return -1; /* Too long input string */ 994 995 if (mhd_DAUTH_EXT_PARAM_MIN_LEN > uname_ext_len) 996 return -1; /* Required prefix is missing */ 997 998 if (!mhd_str_equal_caseless_bin_n ( 999 uname_ext, 1000 mhd_DAUTH_EXT_PARAM_PREFIX, 1001 mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX))) 1002 return -1; /* Only UTF-8 is supported, as it is implied by RFC 7616 */ 1003 1004 r = mhd_SSTR_LEN (mhd_DAUTH_EXT_PARAM_PREFIX); 1005 /* Skip language tag */ 1006 while (r < uname_ext_len && '\'' != uname_ext[r]) 1007 { 1008 const char chr = uname_ext[r]; 1009 if ((' ' == chr) || ('\t' == chr) || ('\"' == chr) || (',' == chr) 1010 || (';' == chr)) 1011 return -1; /* Wrong char in language tag */ 1012 r++; 1013 } 1014 if (r >= uname_ext_len) 1015 return -1; /* The end of the language tag was not found */ 1016 r++; /* Advance to the next char */ 1017 1018 w = mhd_str_pct_decode_strict_n (uname_ext + r, uname_ext_len - r, 1019 buf, buf_size); 1020 if ((0 == w) && (0 != uname_ext_len - r)) 1021 return -1; /* Broken percent encoding */ 1022 buf[w] = 0; /* Zero terminate the result */ 1023 mhd_assert (SSIZE_MAX > w); 1024 return (ssize_t)w; 1025 } 1026 1027 1028 /** 1029 * Get copy of username used by the client. 1030 * @param params the Digest Authorization parameters 1031 * @param uname_type the type of username 1032 * @param[out] uname_info the pointer to the structure to be filled 1033 * @param buf the buffer to be used for usernames 1034 * @param buf_size the size of the @a buf 1035 * @return the size of the @a buf used by pointers in @a unames structure 1036 */ 1037 static size_t 1038 get_rq_uname (const struct mhd_AuthDigesReqParams *restrict params, 1039 enum MHD_DigestAuthUsernameType uname_type, 1040 struct MHD_AuthDigestInfo *restrict uname_info, 1041 uint8_t *restrict buf, 1042 size_t buf_size) 1043 { 1044 size_t buf_used; 1045 1046 buf_used = 0; 1047 mhd_assert (get_rq_uname_type (params) == uname_type); 1048 mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type); 1049 mhd_assert (MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type); 1050 1051 uname_info->username.cstr = NULL; 1052 uname_info->username.len = 0; 1053 uname_info->userhash_hex.cstr = NULL; 1054 uname_info->userhash_hex.len = 0; 1055 uname_info->userhash_bin = NULL; 1056 1057 if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) 1058 { 1059 // TODO: Avoid copying string if not quoted 1060 uname_info->username.cstr = (char *)(buf + buf_used); 1061 uname_info->username.len = 1062 get_rq_param_unquoted_copy_z (¶ms->username, 1063 (char *)(buf + buf_used)); 1064 buf_used += uname_info->username.len + 1; 1065 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD; 1066 } 1067 else if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) 1068 { 1069 size_t res; 1070 1071 uname_info->userhash_hex.cstr = (char *)(buf + buf_used); 1072 uname_info->userhash_hex.len = 1073 get_rq_param_unquoted_copy_z (¶ms->username, 1074 (char *)(buf + buf_used)); 1075 buf_used += uname_info->userhash_hex.len + 1; 1076 uname_info->userhash_bin = (uint8_t *)(buf + buf_used); 1077 res = mhd_hex_to_bin (uname_info->userhash_hex.cstr, 1078 uname_info->userhash_hex.len, 1079 (uint8_t *)(buf + buf_used)); 1080 if (res != uname_info->userhash_hex.len / 2) 1081 { 1082 uname_info->userhash_bin = NULL; 1083 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 1084 } 1085 else 1086 { 1087 /* Avoid pointers outside allocated region when the size is zero */ 1088 if (0 == res) 1089 uname_info->userhash_bin = (const uint8_t *)uname_info->username.cstr; 1090 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH; 1091 buf_used += res; 1092 } 1093 } 1094 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type) 1095 { 1096 ssize_t res; 1097 res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr, 1098 params->username_ext.value.len, 1099 (char *)(buf + buf_used), 1100 buf_size - buf_used); 1101 if (0 > res) 1102 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 1103 else 1104 { 1105 uname_info->username.cstr = (char *)(buf + buf_used); 1106 uname_info->username.len = (size_t)res; 1107 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED; 1108 buf_used += uname_info->username.len + 1; 1109 } 1110 } 1111 else 1112 { 1113 mhd_assert (0); 1114 uname_info->uname_type = MHD_DIGEST_AUTH_UNAME_TYPE_INVALID; 1115 } 1116 mhd_assert (buf_size >= buf_used); 1117 return buf_used; 1118 } 1119 1120 1121 /** 1122 * Result of request's Digest Authorization 'nc' value extraction 1123 */ 1124 enum MHD_FIXED_ENUM_ mhd_GetRqNCResult 1125 { 1126 mhd_GET_RQ_NC_NONE = MHD_DIGEST_AUTH_NC_NONE, /**< No 'nc' value */ 1127 mhd_GET_RQ_NC_VALID = MHD_DIGEST_AUTH_NC_NUMBER, /**< Readable 'nc' value */ 1128 mhd_GET_RQ_NC_TOO_LONG = MHD_DIGEST_AUTH_NC_TOO_LONG, /**< The 'nc' value is too long */ 1129 mhd_GET_RQ_NC_TOO_LARGE = MHD_DIGEST_AUTH_NC_TOO_LARGE, /**< The 'nc' value is too big to fit uint32_t */ 1130 mhd_GET_RQ_NC_BROKEN = 0 /**< The 'nc' value is not a number */ 1131 }; 1132 1133 1134 /** 1135 * Get 'nc' value from request's Authorization header 1136 * @param params the request digest authentication 1137 * @param[out] nc the pointer to put nc value to 1138 * @return enum value indicating the result 1139 */ 1140 static enum mhd_GetRqNCResult 1141 get_rq_nc (const struct mhd_AuthDigesReqParams *params, 1142 uint_fast32_t *nc) 1143 { 1144 const struct mhd_RqDAuthParam *const nc_param = 1145 ¶ms->nc; 1146 char unq[16]; 1147 const char *val; 1148 size_t val_len; 1149 size_t res; 1150 uint64_t nc_val; 1151 1152 if (NULL == nc_param->value.cstr) 1153 return mhd_GET_RQ_NC_NONE; 1154 1155 if (0 == nc_param->value.len) 1156 return mhd_GET_RQ_NC_BROKEN; 1157 1158 if (!nc_param->quoted) 1159 { 1160 val = nc_param->value.cstr; 1161 val_len = nc_param->value.len; 1162 } 1163 else 1164 { 1165 /* Actually no backslashes must be used in 'nc' */ 1166 if (sizeof(unq) < params->nc.value.len) 1167 return mhd_GET_RQ_NC_TOO_LONG; 1168 val_len = mhd_str_unquote (nc_param->value.cstr, nc_param->value.len, unq); 1169 if (0 == val_len) 1170 return mhd_GET_RQ_NC_BROKEN; 1171 val = unq; 1172 } 1173 1174 res = mhd_strx_to_uint64_n (val, 1175 val_len, 1176 &nc_val); 1177 if (0 == res) 1178 { 1179 const char f = val[0]; 1180 if ((('9' >= f) && ('0' <= f)) 1181 || (('F' >= f) && ('A' <= f)) 1182 || (('a' <= f) && ('f' >= f))) 1183 return mhd_GET_RQ_NC_TOO_LARGE; 1184 else 1185 return mhd_GET_RQ_NC_BROKEN; 1186 } 1187 if (val_len != res) 1188 return mhd_GET_RQ_NC_BROKEN; 1189 if (nc_val != (nc_val & UINT64_C (0xFFFFFFFF))) 1190 return mhd_GET_RQ_NC_TOO_LARGE; 1191 *nc = (uint_fast32_t)nc_val; 1192 return mhd_GET_RQ_NC_VALID; 1193 } 1194 1195 1196 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 1197 enum MHD_StatusCode 1198 find_and_parse_auth_digest_info (struct MHD_Request *restrict req) 1199 { 1200 enum MHD_StatusCode res; 1201 struct MHD_AuthDigestInfo *info; 1202 enum MHD_DigestAuthUsernameType uname_type; 1203 size_t unif_buf_size; 1204 uint8_t *unif_buf_ptr; 1205 size_t unif_buf_used; 1206 enum mhd_GetRqNCResult nc_res; 1207 1208 mhd_assert (NULL == req->auth.digest.info); 1209 1210 res = get_rq_auth_digest_params (req); 1211 if (MHD_SC_OK != res) 1212 return res; 1213 1214 unif_buf_size = 0; 1215 1216 uname_type = get_rq_uname_type (req->auth.digest.rqp); 1217 1218 unif_buf_size += get_rq_unames_size (req->auth.digest.rqp, 1219 uname_type); 1220 1221 if (NULL != req->auth.digest.rqp->opaque.value.cstr) 1222 unif_buf_size += req->auth.digest.rqp->opaque.value.len + 1; /* Add one for zero-termination */ 1223 if (NULL != req->auth.digest.rqp->realm.value.cstr) 1224 unif_buf_size += req->auth.digest.rqp->realm.value.len + 1; /* Add one for zero-termination */ 1225 info = 1226 (struct MHD_AuthDigestInfo *) 1227 mhd_stream_alloc_memory (mhd_CNTNR_PTR (req, struct MHD_Connection, rq), 1228 (sizeof(struct MHD_AuthDigestInfo)) 1229 + unif_buf_size); 1230 if (NULL == info) 1231 return MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA; 1232 1233 memset (info, 1234 0, 1235 (sizeof(struct MHD_AuthDigestInfo)) + unif_buf_size); 1236 #ifndef HAVE_NULL_PTR_ALL_ZEROS 1237 info->username.cstr = NULL; 1238 info->userhash_hex.cstr = NULL; 1239 info->userhash_bin = NULL; 1240 info->opaque.cstr = NULL; 1241 info->realm.cstr = NULL; 1242 #endif 1243 1244 unif_buf_ptr = (uint8_t *)(info + 1); 1245 unif_buf_used = 0; 1246 1247 info->algo = req->auth.digest.rqp->algo; 1248 1249 if ((MHD_DIGEST_AUTH_UNAME_TYPE_MISSING != uname_type) 1250 && (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID != uname_type)) 1251 unif_buf_used += 1252 get_rq_uname (req->auth.digest.rqp, uname_type, info, 1253 unif_buf_ptr + unif_buf_used, 1254 unif_buf_size - unif_buf_used); 1255 else 1256 info->uname_type = uname_type; 1257 1258 if (NULL != req->auth.digest.rqp->opaque.value.cstr) 1259 { 1260 info->opaque.cstr = (char *)(unif_buf_ptr + unif_buf_used); 1261 info->opaque.len = 1262 get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->opaque), 1263 (char *)(unif_buf_ptr + unif_buf_used)); 1264 unif_buf_used += info->opaque.len + 1; 1265 } 1266 if (NULL != req->auth.digest.rqp->realm.value.cstr) 1267 { 1268 info->realm.cstr = (char *)(unif_buf_ptr + unif_buf_used); 1269 info->realm.len = 1270 get_rq_param_unquoted_copy_z (&(req->auth.digest.rqp->realm), 1271 (char *)(unif_buf_ptr + unif_buf_used)); 1272 unif_buf_used += info->realm.len + 1; 1273 } 1274 1275 mhd_assert (unif_buf_size >= unif_buf_used); 1276 1277 info->qop = req->auth.digest.rqp->qop; 1278 1279 if (NULL != req->auth.digest.rqp->cnonce.value.cstr) 1280 info->cnonce_len = req->auth.digest.rqp->cnonce.value.len; 1281 else 1282 info->cnonce_len = 0; 1283 1284 nc_res = get_rq_nc (req->auth.digest.rqp, &info->nc); 1285 if (mhd_GET_RQ_NC_VALID == nc_res) 1286 { 1287 if (0 == info->nc) 1288 info->nc_type = MHD_DIGEST_AUTH_NC_ZERO; 1289 else 1290 info->nc_type = MHD_DIGEST_AUTH_NC_NUMBER; 1291 } 1292 else 1293 { 1294 info->nc = 0; 1295 if (mhd_GET_RQ_NC_BROKEN == nc_res) 1296 info->nc_type = MHD_DIGEST_AUTH_NC_NONE; 1297 else 1298 info->nc_type = (enum MHD_DigestAuthNC)nc_res; 1299 } 1300 1301 req->auth.digest.info = info; 1302 1303 mhd_assert (uname_type == info->uname_type); 1304 1305 if ((MHD_DIGEST_AUTH_UNAME_TYPE_MISSING == uname_type) 1306 || (MHD_DIGEST_AUTH_UNAME_TYPE_INVALID == uname_type) 1307 || ((mhd_GET_RQ_NC_BROKEN == nc_res))) 1308 return MHD_SC_REQ_AUTH_DATA_BROKEN; 1309 1310 return MHD_SC_OK; 1311 } 1312 1313 1314 MHD_INTERNAL MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 1315 MHD_FN_PAR_OUT_ (2) enum MHD_StatusCode 1316 mhd_request_get_auth_digest_info ( 1317 struct MHD_Request *restrict req, 1318 const struct MHD_AuthDigestInfo **restrict v_auth_digest_info) 1319 { 1320 mhd_assert (mhd_HTTP_STAGE_HEADERS_PROCESSED <= \ 1321 mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage); 1322 mhd_assert (mhd_HTTP_STAGE_REQ_RECV_FINISHED >= \ 1323 mhd_CNTNR_CPTR (req, struct MHD_Connection, rq)->stage); 1324 1325 if (MHD_SC_OK != req->auth.digest.parse_result) 1326 return req->auth.digest.parse_result; 1327 1328 if (NULL == req->auth.digest.info) 1329 req->auth.digest.parse_result = find_and_parse_auth_digest_info (req); 1330 1331 if (MHD_SC_OK != req->auth.digest.parse_result) 1332 return req->auth.digest.parse_result; /* Failure exit point */ 1333 1334 mhd_assert (NULL != req->auth.digest.info); 1335 *v_auth_digest_info = req->auth.digest.info; 1336 1337 return MHD_SC_OK; /* Success exit point */ 1338 } 1339 1340 1341 /** 1342 * Get base hash calculation algorithm from #MHD_DigestAuthAlgo value. 1343 * @param algo the MHD_DigestAuthAlgo value 1344 * @return the base hash calculation algorithm 1345 */ 1346 mhd_static_inline enum MHD_DigestBaseAlgo 1347 get_base_digest_algo (enum MHD_DigestAuthAlgo algo) 1348 { 1349 unsigned int base_algo; 1350 1351 base_algo = 1352 ((unsigned int)algo) 1353 & ~((unsigned int) 1354 (MHD_DIGEST_AUTH_ALGO_NON_SESSION 1355 | MHD_DIGEST_AUTH_ALGO_SESSION)); 1356 return (enum MHD_DigestBaseAlgo)base_algo; 1357 } 1358 1359 1360 /** 1361 * Get digest size in bytes for specified algorithm. 1362 * 1363 * Internal inline version. 1364 * @param algo the algorithm to check 1365 * @return the size of the digest (in bytes) or zero if the input value is not 1366 * supported/valid 1367 */ 1368 mhd_static_inline size_t 1369 digest_get_hash_size (enum MHD_DigestAuthAlgo algo) 1370 { 1371 #ifdef MHD_SUPPORT_MD5 1372 mhd_assert (MHD_MD5_DIGEST_SIZE == mhd_MD5_DIGEST_SIZE); 1373 #endif /* MHD_SUPPORT_MD5 */ 1374 #ifdef MHD_SUPPORT_SHA256 1375 mhd_assert (MHD_SHA256_DIGEST_SIZE == mhd_SHA256_DIGEST_SIZE); 1376 #endif /* MHD_SUPPORT_SHA256 */ 1377 #ifdef MHD_SUPPORT_SHA512_256 1378 mhd_assert (MHD_SHA512_256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE); 1379 # ifdef MHD_SUPPORT_SHA256 1380 mhd_assert (mhd_SHA256_DIGEST_SIZE == mhd_SHA512_256_DIGEST_SIZE); 1381 # endif /* MHD_SUPPORT_SHA256 */ 1382 #endif /* MHD_SUPPORT_SHA512_256 */ 1383 /* Only one algorithm must be specified */ 1384 mhd_assert (1 == \ 1385 (((0 != (((unsigned int)algo) \ 1386 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0) \ 1387 + ((0 != (((unsigned int)algo) \ 1388 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0) \ 1389 + ((0 != (((unsigned int)algo) \ 1390 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0))); 1391 #ifdef MHD_SUPPORT_MD5 1392 if (0 != (((unsigned int)algo) 1393 & ((unsigned int)MHD_DIGEST_BASE_ALGO_MD5))) 1394 return MHD_MD5_DIGEST_SIZE; 1395 else 1396 #endif /* MHD_SUPPORT_MD5 */ 1397 #if defined(MHD_SUPPORT_SHA256) && defined(MHD_SUPPORT_SHA512_256) 1398 if (0 != (((unsigned int)algo) 1399 & (((unsigned int)MHD_DIGEST_BASE_ALGO_SHA256) 1400 | ((unsigned int)MHD_DIGEST_BASE_ALGO_SHA512_256)))) 1401 return MHD_SHA256_DIGEST_SIZE; /* The same as mhd_SHA512_256_DIGEST_SIZE */ 1402 else 1403 #elif defined(MHD_SUPPORT_SHA256) 1404 if (0 != (((unsigned int)algo) 1405 & ((unsigned int)MHD_DIGEST_BASE_ALGO_SHA256))) 1406 return MHD_SHA256_DIGEST_SIZE; 1407 else 1408 #elif defined(MHD_SUPPORT_SHA512_256) 1409 if (0 != (((unsigned int)algo) 1410 & ((unsigned int)MHD_DIGEST_BASE_ALGO_SHA512_256))) 1411 return MHD_SHA512_256_DIGEST_SIZE; 1412 else 1413 #endif /* MHD_SUPPORT_SHA512_256 */ 1414 (void)0; /* Unsupported algorithm */ 1415 1416 return 0; /* Wrong input or unsupported algorithm */ 1417 } 1418 1419 1420 /** 1421 * Get digest size for specified algorithm. 1422 * 1423 * The size of the digest specifies the size of the userhash, userdigest 1424 * and other parameters which size depends on used hash algorithm. 1425 * @param algo the algorithm to check 1426 * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or 1427 * #MHD_SHA256_DIGEST_SIZE/MHD_SHA512_256_DIGEST_SIZE) 1428 * or zero if the input value is not supported or not valid 1429 * @sa #MHD_digest_auth_calc_userdigest() 1430 * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex() 1431 * @note Available since #MHD_VERSION 0x00097701 1432 * @ingroup authentication 1433 */ 1434 MHD_EXTERN_ MHD_FN_CONST_ size_t 1435 MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo algo) 1436 { 1437 return digest_get_hash_size (algo); 1438 } 1439 1440 1441 /** 1442 * The digest calculation structure. 1443 */ 1444 struct DigestAlgorithm 1445 { 1446 /** 1447 * A context for the digest algorithm, already initialized to be 1448 * useful for @e init, @e update and @e digest. 1449 */ 1450 union DigestCtx ctx; 1451 1452 /** 1453 * The hash calculation algorithm. 1454 */ 1455 enum MHD_DigestBaseAlgo algo; 1456 1457 /** 1458 * Buffer for hex-print of the final digest. 1459 */ 1460 #ifndef NDEBUG 1461 bool uninitialised; /**< The structure has been not set-up */ 1462 bool algo_selected; /**< The algorithm has been selected */ 1463 bool ready_for_hashing; /**< The structure is ready to hash data */ 1464 bool hashing; /**< Some data has been hashed, but the digest has not finalised yet */ 1465 #endif /* NDEBUG */ 1466 }; 1467 1468 1469 /** 1470 * Return the size of the digest. 1471 * @param da the digest calculation structure to identify 1472 * @return the size of the digest. 1473 */ 1474 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ unsigned int 1475 digest_get_size (struct DigestAlgorithm *da) 1476 { 1477 mhd_assert (!da->uninitialised); 1478 mhd_assert (da->algo_selected); 1479 #ifdef MHD_SUPPORT_MD5 1480 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 1481 return mhd_MD5_DIGEST_SIZE; 1482 #endif /* MHD_SUPPORT_MD5 */ 1483 #ifdef MHD_SUPPORT_SHA256 1484 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 1485 return mhd_SHA256_DIGEST_SIZE; 1486 #endif /* MHD_SUPPORT_SHA256 */ 1487 #ifdef MHD_SUPPORT_SHA512_256 1488 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) 1489 return mhd_SHA512_256_DIGEST_SIZE; 1490 #endif /* MHD_SUPPORT_SHA512_256 */ 1491 mhd_UNREACHABLE (); 1492 return 0; 1493 } 1494 1495 1496 #if defined(mhd_MD5_HAS_DEINIT) \ 1497 || defined(mhd_SHA256_HAS_DEINIT) \ 1498 || defined(mhd_SHA512_256_HAS_DEINIT) 1499 /** 1500 * Indicates presence of digest_deinit() function 1501 */ 1502 # define mhd_DIGEST_HAS_DEINIT 1 1503 #endif /* mhd_MD5_HAS_DEINIT || mhd_SHA256_HAS_DEINIT */ 1504 1505 #ifdef mhd_DIGEST_HAS_DEINIT 1506 /** 1507 * Zero-initialise digest calculation structure. 1508 * 1509 * This initialisation is enough to safely call #digest_deinit() only. 1510 * To make any real digest calculation, #digest_setup_and_init() must be called. 1511 * @param da the digest calculation 1512 */ 1513 mhd_static_inline void 1514 digest_setup_zero (struct DigestAlgorithm *da) 1515 { 1516 # ifndef NDEBUG 1517 da->uninitialised = false; 1518 da->algo_selected = false; 1519 da->ready_for_hashing = false; 1520 da->hashing = false; 1521 # endif /* _DEBUG */ 1522 da->algo = MHD_DIGEST_BASE_ALGO_INVALID; 1523 } 1524 1525 1526 /** 1527 * De-initialise digest calculation structure. 1528 * 1529 * This function must be called if #digest_setup_and_init() was called for 1530 * @a da. 1531 * This function must not be called if @a da was not initialised by 1532 * #digest_setup_and_init() or by #digest_setup_zero(). 1533 * @param da the digest calculation 1534 */ 1535 mhd_static_inline void 1536 digest_deinit (struct DigestAlgorithm *da) 1537 { 1538 mhd_assert (!da->uninitialised); 1539 # ifdef mhd_MD5_HAS_DEINIT 1540 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo) 1541 mhd_MD5_deinit (&(da->ctx.md5_ctx)); 1542 else 1543 # endif /* mhd_MD5_HAS_DEINIT */ 1544 # ifdef mhd_SHA256_HAS_DEINIT 1545 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo) 1546 mhd_SHA256_deinit (&(da->ctx.sha256_ctx)); 1547 else 1548 # endif /* mhd_SHA256_HAS_DEINIT */ 1549 # ifdef mhd_SHA512_256_HAS_DEINIT 1550 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo) 1551 mhd_SHA512_256_deinit (&(da->ctx.sha512_256_ctx)); 1552 else 1553 # endif /* mhd_SHA512_256_HAS_DEINIT */ 1554 (void)0; 1555 digest_setup_zero (da); 1556 } 1557 1558 1559 #else /* ! mhd_DIGEST_HAS_DEINIT */ 1560 # define digest_setup_zero(da) ((void) 0) 1561 # define digest_deinit(da) ((void) 0) 1562 #endif /* ! mhd_DIGEST_HAS_DEINIT */ 1563 1564 1565 /** 1566 * Set-up the digest calculation structure and initialise with initial values. 1567 * 1568 * If @a da was successfully initialised, #digest_deinit() must be called 1569 * after finishing using of the @a da. 1570 * 1571 * This function must not be called more than once for any @a da. 1572 * 1573 * @param da the structure to set-up 1574 * @param algo the algorithm to use for digest calculation 1575 * @return boolean 'true' if successfully set-up, 1576 * false otherwise. 1577 */ 1578 mhd_static_inline MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ bool 1579 digest_init_one_time (struct DigestAlgorithm *da, 1580 enum MHD_DigestBaseAlgo algo) 1581 { 1582 #ifndef NDEBUG 1583 da->uninitialised = false; 1584 da->algo_selected = false; 1585 da->ready_for_hashing = false; 1586 da->hashing = false; 1587 #endif /* _DEBUG */ 1588 switch (algo) 1589 { 1590 case MHD_DIGEST_BASE_ALGO_MD5: 1591 #ifdef MHD_SUPPORT_MD5 1592 da->algo = MHD_DIGEST_BASE_ALGO_MD5; 1593 # ifndef NDEBUG 1594 da->algo_selected = true; 1595 # endif 1596 mhd_MD5_init (&(da->ctx.md5_ctx)); 1597 # ifndef NDEBUG 1598 da->ready_for_hashing = true; 1599 # endif 1600 return true; 1601 #endif /* MHD_SUPPORT_MD5 */ 1602 break; 1603 1604 case MHD_DIGEST_BASE_ALGO_SHA256: 1605 #ifdef MHD_SUPPORT_SHA256 1606 da->algo = MHD_DIGEST_BASE_ALGO_SHA256; 1607 # ifndef NDEBUG 1608 da->algo_selected = true; 1609 # endif 1610 mhd_SHA256_init (&(da->ctx.sha256_ctx)); 1611 # ifndef NDEBUG 1612 da->ready_for_hashing = true; 1613 # endif 1614 return true; 1615 #endif /* MHD_SUPPORT_SHA256 */ 1616 break; 1617 1618 case MHD_DIGEST_BASE_ALGO_SHA512_256: 1619 #ifdef MHD_SUPPORT_SHA512_256 1620 da->algo = MHD_DIGEST_BASE_ALGO_SHA512_256; 1621 # ifndef NDEBUG 1622 da->algo_selected = true; 1623 # endif 1624 mhd_SHA512_256_init (&(da->ctx.sha512_256_ctx)); 1625 # ifndef NDEBUG 1626 da->ready_for_hashing = true; 1627 # endif 1628 return true; 1629 #endif /* MHD_SUPPORT_SHA512_256 */ 1630 break; 1631 1632 case MHD_DIGEST_BASE_ALGO_INVALID: 1633 default: 1634 mhd_UNREACHABLE (); 1635 break; 1636 } 1637 da->algo = MHD_DIGEST_BASE_ALGO_INVALID; 1638 return false; /* Unsupported or bad algorithm */ 1639 } 1640 1641 1642 /** 1643 * Hash more data for digest calculation. 1644 * @param da the digest calculation 1645 * @param size the size of the @a data in bytes 1646 * @param data the data to process 1647 */ 1648 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ 1649 MHD_FN_PAR_IN_SIZE_ (3, 2) void 1650 digest_update (struct DigestAlgorithm *restrict da, 1651 size_t size, 1652 const void *restrict data) 1653 { 1654 mhd_assert (!da->uninitialised); 1655 mhd_assert (da->algo_selected); 1656 mhd_assert (da->ready_for_hashing); 1657 switch (da->algo) 1658 { 1659 case MHD_DIGEST_BASE_ALGO_MD5: 1660 #ifdef MHD_SUPPORT_MD5 1661 mhd_MD5_update (&da->ctx.md5_ctx, 1662 size, 1663 (const uint8_t *)data); 1664 #else 1665 mhd_UNREACHABLE (); 1666 #endif 1667 break; 1668 case MHD_DIGEST_BASE_ALGO_SHA256: 1669 #ifdef MHD_SUPPORT_SHA256 1670 mhd_SHA256_update (&da->ctx.sha256_ctx, 1671 size, 1672 (const uint8_t *)data); 1673 #else 1674 mhd_UNREACHABLE (); 1675 #endif 1676 break; 1677 case MHD_DIGEST_BASE_ALGO_SHA512_256: 1678 #ifdef MHD_SUPPORT_SHA512_256 1679 mhd_SHA512_256_update (&da->ctx.sha512_256_ctx, 1680 size, 1681 (const uint8_t *)data); 1682 #else 1683 mhd_UNREACHABLE (); 1684 #endif 1685 break; 1686 case MHD_DIGEST_BASE_ALGO_INVALID: 1687 default: 1688 mhd_UNREACHABLE (); 1689 break; 1690 } 1691 #ifndef NDEBUG 1692 da->hashing = true; 1693 #endif 1694 } 1695 1696 1697 /** 1698 * Feed digest calculation with more data from string. 1699 * @param da the digest calculation 1700 * @param str the zero-terminated string to process 1701 */ 1702 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ 1703 MHD_FN_PAR_CSTR_ (2) void 1704 digest_update_str (struct DigestAlgorithm *restrict da, 1705 const char *restrict str) 1706 { 1707 digest_update (da, 1708 strlen (str), 1709 (const uint8_t *)str); 1710 } 1711 1712 1713 /** 1714 * Feed digest calculation with more data from string. 1715 * @param da the digest calculation 1716 * @param buf the sized buffer with the data 1717 */ 1718 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ 1719 MHD_FN_PAR_CSTR_ (2) void 1720 digest_update_cbuf (struct DigestAlgorithm *restrict da, 1721 const struct mhd_BufferConst *restrict buf) 1722 { 1723 digest_update (da, 1724 buf->size, 1725 (const uint8_t *)buf->data); 1726 } 1727 1728 1729 /** 1730 * Feed digest calculation with more data from string. 1731 * @param da the digest calculation 1732 * @param buf the sized buffer with the data 1733 */ 1734 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ 1735 MHD_FN_PAR_CSTR_ (2) void 1736 digest_update_buf (struct DigestAlgorithm *restrict da, 1737 const struct mhd_Buffer *restrict buf) 1738 { 1739 digest_update (da, 1740 buf->size, 1741 (const uint8_t *)buf->data); 1742 } 1743 1744 1745 /** 1746 * Feed digest calculation with single colon ':' character. 1747 * @param da the digest calculation 1748 */ 1749 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ void 1750 digest_update_with_colon (struct DigestAlgorithm *da) 1751 { 1752 static const uint8_t colon = (uint8_t)':'; 1753 digest_update (da, 1754 1, 1755 &colon); 1756 } 1757 1758 1759 /** 1760 * Finally calculate hash (the digest). 1761 * @param da the digest calculation 1762 * @param[out] digest the pointer to the buffer to put calculated digest, 1763 * must be at least digest_get_size(da) bytes large 1764 */ 1765 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ 1766 MHD_FN_PAR_OUT_ (2) void 1767 digest_calc_hash (struct DigestAlgorithm *da, 1768 uint8_t *digest) 1769 { 1770 mhd_assert (!da->uninitialised); 1771 mhd_assert (da->algo_selected); 1772 mhd_assert (da->ready_for_hashing); 1773 switch (da->algo) 1774 { 1775 case MHD_DIGEST_BASE_ALGO_MD5: 1776 #ifdef MHD_SUPPORT_MD5 1777 mhd_MD5_finish (&da->ctx.md5_ctx, digest); 1778 # ifndef NDEBUG 1779 da->ready_for_hashing = false; 1780 # endif /* _DEBUG */ 1781 #else /* ! MHD_SUPPORT_MD5 */ 1782 mhd_UNREACHABLE (); 1783 #endif /* ! MHD_SUPPORT_MD5 */ 1784 break; 1785 1786 case MHD_DIGEST_BASE_ALGO_SHA256: 1787 #ifdef MHD_SUPPORT_SHA256 1788 mhd_SHA256_finish (&da->ctx.sha256_ctx, digest); 1789 # ifndef NDEBUG 1790 da->ready_for_hashing = false; 1791 # endif /* _DEBUG */ 1792 #else /* ! MHD_SUPPORT_SHA256 */ 1793 mhd_UNREACHABLE (); 1794 #endif /* ! MHD_SUPPORT_SHA256 */ 1795 break; 1796 1797 case MHD_DIGEST_BASE_ALGO_SHA512_256: 1798 #ifdef MHD_SUPPORT_SHA512_256 1799 mhd_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest); 1800 # ifndef NDEBUG 1801 da->ready_for_hashing = false; 1802 # endif /* _DEBUG */ 1803 #else /* ! MHD_SUPPORT_SHA512_256 */ 1804 mhd_UNREACHABLE (); 1805 #endif /* ! MHD_SUPPORT_SHA512_256 */ 1806 break; 1807 1808 case MHD_DIGEST_BASE_ALGO_INVALID: 1809 default: 1810 mhd_UNREACHABLE (); 1811 break; 1812 } 1813 #ifndef NDEBUG 1814 da->hashing = false; 1815 #endif /* _DEBUG */ 1816 } 1817 1818 1819 /** 1820 * Reset the digest calculation structure and prepare for new calculation. 1821 * 1822 * @param da the structure to reset 1823 */ 1824 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ void 1825 digest_reset (struct DigestAlgorithm *da) 1826 { 1827 mhd_assert (!da->uninitialised); 1828 mhd_assert (da->algo_selected); 1829 mhd_assert (!da->hashing); 1830 switch (da->algo) 1831 { 1832 case MHD_DIGEST_BASE_ALGO_MD5: 1833 #ifdef MHD_SUPPORT_MD5 1834 mhd_assert (!da->ready_for_hashing); 1835 mhd_MD5_reset (&(da->ctx.md5_ctx)); 1836 # ifndef NDEBUG 1837 da->ready_for_hashing = true; 1838 # endif /* _DEBUG */ 1839 #else /* ! MHD_SUPPORT_MD5 */ 1840 mhd_UNREACHABLE (); 1841 #endif /* ! MHD_SUPPORT_MD5 */ 1842 break; 1843 1844 case MHD_DIGEST_BASE_ALGO_SHA256: 1845 #ifdef MHD_SUPPORT_SHA256 1846 mhd_assert (!da->ready_for_hashing); 1847 mhd_SHA256_reset (&(da->ctx.sha256_ctx)); 1848 # ifndef NDEBUG 1849 da->ready_for_hashing = true; 1850 # endif /* _DEBUG */ 1851 #else /* ! MHD_SUPPORT_SHA256 */ 1852 mhd_UNREACHABLE (); 1853 #endif /* ! MHD_SUPPORT_SHA256 */ 1854 break; 1855 1856 case MHD_DIGEST_BASE_ALGO_SHA512_256: 1857 #ifdef MHD_SUPPORT_SHA512_256 1858 mhd_assert (!da->ready_for_hashing); 1859 mhd_SHA512_256_reset (&(da->ctx.sha512_256_ctx)); 1860 # ifndef NDEBUG 1861 da->ready_for_hashing = true; 1862 # endif /* _DEBUG */ 1863 #else /* ! MHD_SUPPORT_SHA512_256 */ 1864 mhd_UNREACHABLE (); 1865 #endif /* ! MHD_SUPPORT_SHA512_256 */ 1866 break; 1867 1868 case MHD_DIGEST_BASE_ALGO_INVALID: 1869 default: 1870 #ifndef NDEBUG 1871 da->ready_for_hashing = false; 1872 #endif 1873 mhd_UNREACHABLE (); 1874 break; 1875 } 1876 } 1877 1878 1879 #if defined(mhd_MD5_HAS_EXT_ERROR) \ 1880 || defined(mhd_SHA256_HAS_EXT_ERROR) \ 1881 || defined(mhd_SHA512_256_HAS_EXT_ERROR) 1882 /** 1883 * Indicates that digest algorithm has external error status 1884 */ 1885 # define mhd_DIGEST_HAS_EXT_ERROR 1 1886 #endif /* mhd_MD5_HAS_EXT_ERROR || mhd_SHA256_HAS_EXT_ERROR 1887 || mhd_SHA512_256_HAS_EXT_ERROR*/ 1888 1889 #ifdef mhd_DIGEST_HAS_EXT_ERROR 1890 /** 1891 * Get external error state. 1892 * 1893 * When external digest calculation used, an error may occur during 1894 * initialisation or hashing data. This function checks whether external 1895 * error has been reported for digest calculation. 1896 * @param da the digest calculation 1897 * @return 'true' if external error occurs, 1898 * 'false' otherwise 1899 */ 1900 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ bool 1901 digest_has_error (struct DigestAlgorithm *da) 1902 { 1903 mhd_assert (!da->uninitialised); 1904 mhd_assert (da->algo_selected); 1905 switch (da->algo) 1906 { 1907 case MHD_DIGEST_BASE_ALGO_MD5: 1908 # ifdef MHD_SUPPORT_MD5 1909 return mhd_MD5_has_err (&(da->ctx.md5_ctx)); 1910 # else /* ! MHD_SUPPORT_MD5 */ 1911 mhd_UNREACHABLE (); 1912 # endif /* ! MHD_SUPPORT_MD5 */ 1913 break; 1914 1915 case MHD_DIGEST_BASE_ALGO_SHA256: 1916 # ifdef MHD_SUPPORT_SHA256 1917 return mhd_SHA256_has_err (&(da->ctx.sha256_ctx)); 1918 # else /* ! MHD_SUPPORT_SHA256 */ 1919 mhd_UNREACHABLE (); 1920 # endif /* ! MHD_SUPPORT_SHA256 */ 1921 break; 1922 1923 case MHD_DIGEST_BASE_ALGO_SHA512_256: 1924 # ifdef MHD_SUPPORT_SHA512_256 1925 return mhd_SHA512_256_has_err (&(da->ctx.sha512_256_ctx)); 1926 # else /* ! MHD_SUPPORT_SHA512_256 */ 1927 mhd_UNREACHABLE (); 1928 # endif /* ! MHD_SUPPORT_SHA512_256 */ 1929 break; 1930 1931 case MHD_DIGEST_BASE_ALGO_INVALID: 1932 default: 1933 break; 1934 } 1935 mhd_UNREACHABLE (); 1936 return true; 1937 } 1938 1939 1940 #else /* ! mhd_DIGEST_HAS_EXT_ERROR */ 1941 # define digest_has_error(da) (((void) (da)), ! ! 0) 1942 #endif /* ! mhd_DIGEST_HAS_EXT_ERROR */ 1943 1944 1945 /** 1946 * Calculate userdigest, return it as binary data. 1947 * 1948 * It is equal to H(A1) for non-session algorithms. 1949 * 1950 * MHD internal version. 1951 * 1952 * @param da the digest algorithm 1953 * @param username the username to use 1954 * @param username_len the length of the @a username 1955 * @param realm the realm to use 1956 * @param realm_len the length of the @a realm 1957 * @param password the password, must be zero-terminated 1958 * @param[out] ha1_bin the output buffer, must have at least 1959 * #digest_get_size(da) bytes available 1960 */ 1961 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ 1962 MHD_FN_PAR_IN_SIZE_ (2, 3) MHD_FN_PAR_IN_SIZE_ (4, 5) 1963 MHD_FN_PAR_CSTR_ (6) MHD_FN_PAR_OUT_ (7) void 1964 calc_userdigest (struct DigestAlgorithm *restrict da, 1965 const char *restrict username, const size_t username_len, 1966 const char *restrict realm, const size_t realm_len, 1967 const char *restrict password, 1968 uint8_t *ha1_bin) 1969 { 1970 mhd_assert (!da->uninitialised); 1971 mhd_assert (da->algo_selected); 1972 mhd_assert (!da->hashing); 1973 digest_update (da, username_len, username); 1974 digest_update_with_colon (da); 1975 digest_update (da, realm_len, realm); 1976 digest_update_with_colon (da); 1977 digest_update_str (da, password); 1978 digest_calc_hash (da, ha1_bin); 1979 } 1980 1981 1982 MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ 1983 MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) 1984 MHD_FN_PAR_OUT_SIZE_ (6, 5) enum MHD_StatusCode 1985 MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo algo, 1986 const char *MHD_RESTRICT username, 1987 const char *MHD_RESTRICT realm, 1988 const char *MHD_RESTRICT password, 1989 size_t bin_buf_size, 1990 void *MHD_RESTRICT userdigest_bin) 1991 { 1992 struct DigestAlgorithm da; 1993 enum MHD_StatusCode ret; 1994 if (!digest_init_one_time (&da, get_base_digest_algo (algo))) 1995 return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED; 1996 1997 if (digest_get_size (&da) > bin_buf_size) 1998 ret = MHD_SC_OUT_BUFF_TOO_SMALL; 1999 else 2000 { 2001 calc_userdigest (&da, 2002 username, 2003 strlen (username), 2004 realm, 2005 strlen (realm), 2006 password, 2007 (uint8_t *)userdigest_bin); 2008 ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK; 2009 } 2010 digest_deinit (&da); 2011 2012 return ret; 2013 } 2014 2015 2016 /** 2017 * Calculate userhash, return it as binary data. 2018 * 2019 * MHD internal version. 2020 * 2021 * @param da the digest algorithm 2022 * @param username_len the length of the @a username 2023 * @param username the username to use 2024 * @param realm_len the length of the @a realm 2025 * @param realm the realm to use 2026 * @param[out] digest_bin the output buffer, must have at least 2027 * #MHD_digest_get_hash_size(algo) bytes available 2028 */ 2029 mhd_static_inline MHD_FN_PAR_NONNULL_ALL_ 2030 MHD_FN_PAR_IN_SIZE_ (3, 2) MHD_FN_PAR_IN_SIZE_ (5, 4) MHD_FN_PAR_OUT_ (6) void 2031 calc_userhash (struct DigestAlgorithm *da, 2032 const size_t username_len, 2033 const char *username, 2034 const size_t realm_len, 2035 const char *realm, 2036 uint8_t *digest_bin) 2037 { 2038 mhd_assert (!da->uninitialised); 2039 mhd_assert (da->algo_selected); 2040 mhd_assert (!da->hashing); 2041 digest_update (da, username_len, username); 2042 digest_update_with_colon (da); 2043 digest_update (da, realm_len, realm); 2044 digest_calc_hash (da, digest_bin); 2045 } 2046 2047 2048 MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ 2049 MHD_FN_PAR_CSTR_ (2) 2050 MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5, 4) enum MHD_StatusCode 2051 MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo algo, 2052 const char *MHD_RESTRICT username, 2053 const char *MHD_RESTRICT realm, 2054 size_t bin_buf_size, 2055 void *MHD_RESTRICT userhash_bin) 2056 { 2057 struct DigestAlgorithm da; 2058 enum MHD_StatusCode ret; 2059 2060 if (!digest_init_one_time (&da, get_base_digest_algo (algo))) 2061 return MHD_SC_AUTH_DIGEST_ALGO_NOT_SUPPORTED; 2062 if (digest_get_size (&da) > bin_buf_size) 2063 ret = MHD_SC_OUT_BUFF_TOO_SMALL; 2064 else 2065 { 2066 calc_userhash (&da, 2067 strlen (username), 2068 username, 2069 strlen (realm), 2070 realm, 2071 (uint8_t *)userhash_bin); 2072 2073 ret = digest_has_error (&da) ? MHD_SC_HASH_FAILED : MHD_SC_OK; 2074 } 2075 digest_deinit (&da); 2076 2077 return ret; 2078 } 2079 2080 2081 MHD_EXTERN_ MHD_FN_PURE_ MHD_FN_PAR_NONNULL_ALL_ 2082 MHD_FN_PAR_CSTR_ (2) 2083 MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_OUT_SIZE_ (5, 4) enum MHD_StatusCode 2084 MHD_digest_auth_calc_userhash_hex ( 2085 enum MHD_DigestAuthAlgo algo, 2086 const char *MHD_RESTRICT username, 2087 const char *MHD_RESTRICT realm, 2088 size_t hex_buf_size, 2089 char userhash_hex[MHD_FN_PAR_DYN_ARR_SIZE_ (hex_buf_size)]) 2090 { 2091 uint8_t userhash_bin[mhd_MAX_DIGEST] = { 0u /* mute compiler warning */ }; 2092 size_t digest_size; 2093 enum MHD_StatusCode res; 2094 2095 digest_size = digest_get_hash_size (algo); 2096 if (digest_size * 2 + 1 > hex_buf_size) 2097 return MHD_SC_OUT_BUFF_TOO_SMALL; 2098 res = MHD_digest_auth_calc_userhash (algo, 2099 username, 2100 realm, 2101 sizeof(userhash_bin), 2102 userhash_bin); 2103 if (MHD_SC_OK != res) 2104 return res; 2105 2106 (void)mhd_bin_to_hex_z (userhash_bin, 2107 digest_size, 2108 userhash_hex); 2109 return MHD_SC_OK; 2110 } 2111 2112 2113 /** 2114 * Extract timestamp from the given nonce. 2115 * @param nonce the nonce to check in binary form 2116 * @return 'true' if timestamp was extracted, 2117 * 'false' if nonce does not have valid timestamp. 2118 */ 2119 mhd_static_inline uint_fast32_t 2120 get_nonce_timestamp (const uint8_t nonce[mhd_AUTH_DIGEST_NONCE_BIN_SIZE]) 2121 { 2122 return (uint_fast32_t) 2123 mhd_GET_32BIT_LE_UNALIGN (nonce + mhd_AUTH_DIGEST_NONCE_RAND_BIN_SIZE); 2124 } 2125 2126 2127 /** 2128 * The result of nonce-nc map array check. 2129 */ 2130 enum mhd_CheckNonceNC 2131 { 2132 /** 2133 * The nonce and NC are OK (valid and NC was not used before). 2134 */ 2135 mhd_CHECK_NONCENC_OK = MHD_DAUTH_OK, 2136 2137 /** 2138 * The 'nonce' is too old, has been overwritten with newer 'nonce' in 2139 * the same slot or 'nc' value has been used already. 2140 * The validity of the 'nonce' was not be checked. 2141 */ 2142 mhd_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE, 2143 2144 /** 2145 * The 'nonce' is wrong, it was not generated before. 2146 */ 2147 mhd_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG 2148 }; 2149 2150 2151 /** 2152 * Check nonce-nc map array with the new nonce counter. 2153 * 2154 * @param d the master daemon object 2155 * @param noncelen the length of @a nonce, in characters 2156 * @param nonce the pointer that referenced hex nonce, does not need to be 2157 * zero-terminated 2158 * @param nc the nonce counter 2159 * @param time_now the current timestamp 2160 * @return #MHD_DAUTH_NONCENC_OK if successful, 2161 * #MHD_DAUTH_NONCENC_STALE if nonce is stale (or no nonce-nc array 2162 * is available), 2163 * #MHD_DAUTH_NONCENC_WRONG if nonce was not recodered in nonce-nc map 2164 * array, while it should. 2165 */ 2166 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 2167 MHD_FN_PAR_IN_SIZE_ (3, 2) enum mhd_CheckNonceNC 2168 check_nonce_nc (struct MHD_Daemon *restrict d, 2169 size_t noncelen, 2170 const char *restrict nonce, 2171 uint_fast32_t nc, 2172 uint_fast32_t time_now) 2173 { 2174 uint8_t nonce_bin[mhd_AUTH_DIGEST_NONCE_BIN_SIZE]; 2175 struct mhd_DaemonAuthDigestNonceData *nonce_slot; 2176 uint_fast32_t valid_time; 2177 uint_fast32_t slot_valid_time; 2178 enum mhd_CheckNonceNC ret; 2179 2180 mhd_assert (!mhd_D_HAS_MASTER (d)); /* only master daemon should be used */ 2181 mhd_assert (0 != noncelen); 2182 mhd_assert (0 != nc); 2183 if (mhd_AUTH_DIGEST_NONCE_LEN != noncelen) 2184 return mhd_CHECK_NONCENC_WRONG; 2185 2186 if (mhd_AUTH_DIGEST_NONCE_BIN_SIZE != 2187 mhd_hex_to_bin (nonce, 2188 mhd_AUTH_DIGEST_NONCE_LEN, 2189 nonce_bin)) 2190 return mhd_CHECK_NONCENC_WRONG; 2191 2192 if ((NULL != memchr (nonce, 'A', mhd_AUTH_DIGEST_NONCE_LEN)) 2193 || (NULL != memchr (nonce, 'B', mhd_AUTH_DIGEST_NONCE_LEN)) 2194 || (NULL != memchr (nonce, 'C', mhd_AUTH_DIGEST_NONCE_LEN)) 2195 || (NULL != memchr (nonce, 'D', mhd_AUTH_DIGEST_NONCE_LEN)) 2196 || (NULL != memchr (nonce, 'E', mhd_AUTH_DIGEST_NONCE_LEN)) 2197 || (NULL != memchr (nonce, 'F', mhd_AUTH_DIGEST_NONCE_LEN))) 2198 return mhd_CHECK_NONCENC_WRONG; /* Upper case chars are not produced by MHD */ 2199 2200 valid_time = get_nonce_timestamp (nonce_bin); 2201 2202 nonce_slot = d->auth_dg.nonces 2203 + nonce_to_index (nonce_bin, 2204 d->auth_dg.cfg.nonces_num); 2205 2206 mhd_mutex_lock_chk (&(d->auth_dg.nonces_lock)); 2207 2208 slot_valid_time = nonce_slot->valid_time; 2209 if ((0 == memcmp (nonce_slot->nonce, 2210 nonce_bin, 2211 sizeof(nonce_slot->nonce))) 2212 && (slot_valid_time == valid_time)) 2213 { 2214 /* The nonce matches the stored nonce */ 2215 if (nonce_slot->max_recvd_nc < nc) 2216 { 2217 /* 'nc' is larger, shift bitmask and bump limit */ 2218 const uint_fast32_t jump_size = 2219 (uint_fast32_t)nc - nonce_slot->max_recvd_nc; 2220 if (64 > jump_size) 2221 { 2222 /* small jump, less than mask width */ 2223 nonce_slot->nmask <<= jump_size; 2224 /* Set bit for the old 'nc' value */ 2225 nonce_slot->nmask |= (UINT64_C (1) << (jump_size - 1)); 2226 } 2227 else if (64 == jump_size) 2228 nonce_slot->nmask = (UINT64_C (1) << 63); 2229 else 2230 nonce_slot->nmask = 0; /* big jump, unset all bits in the mask */ 2231 nonce_slot->max_recvd_nc = nc; 2232 ret = mhd_CHECK_NONCENC_OK; 2233 } 2234 else if (nonce_slot->max_recvd_nc == nc) 2235 /* 'nc' was already used */ 2236 ret = mhd_CHECK_NONCENC_STALE; 2237 else /* (nonce_slot->max_recvd_nc > nc) */ 2238 { 2239 /* Out-of-order 'nc' value. Check whether it was used before */ 2240 const uint_fast32_t nc_delta = nonce_slot->max_recvd_nc - nc; 2241 2242 mhd_ASSUME (0u != nc_delta); /* Guaranteed by if() branches */ 2243 2244 if (64u >= nc_delta) 2245 { 2246 const uint_fast64_t nc_bit = (UINT64_C (1) << (nc_delta - 1)); 2247 2248 mhd_ASSUME (0u != nc_bit); 2249 mhd_ASSUME (0u != (nc_bit & UINT64_C (0xFFFFFFFFFFFFFFFF))); 2250 2251 if (0u == (nc_bit & nonce_slot->nmask)) 2252 { 2253 /* 'nc' has not been used before. Set the bit. */ 2254 nonce_slot->nmask |= nc_bit; 2255 ret = mhd_CHECK_NONCENC_OK; 2256 } 2257 else 2258 ret = mhd_CHECK_NONCENC_STALE; /* 'nc' has been used before */ 2259 } 2260 else 2261 ret = mhd_CHECK_NONCENC_STALE; /* 'nc' is too old (more than 64 value before) */ 2262 } 2263 } 2264 else 2265 { 2266 /* The nonce does not match the stored nonce */ 2267 if (((valid_time - slot_valid_time) & UINT32_C (0xFFFFFFFF)) <= 2268 ((slot_valid_time - valid_time) & UINT32_C (0xFFFFFFFF))) 2269 { 2270 /* The stored nonce was generated before the checked nonce */ 2271 ret = mhd_CHECK_NONCENC_WRONG; 2272 } 2273 else 2274 { 2275 /* The stored nonce was generated after the checked nonce */ 2276 const uint_fast32_t nonce_gen_time = 2277 ((valid_time - d->auth_dg.cfg.nonce_tmout) & UINT32_C (0xFFFFFFFF)); 2278 if (((time_now - nonce_gen_time) & UINT32_C (0xFFFFFFFF)) < 2279 ((nonce_gen_time - time_now) & UINT32_C (0xFFFFFFFF))) 2280 ret = mhd_CHECK_NONCENC_WRONG; /* The nonce is generated in "future" */ 2281 else 2282 /* Probably the nonce has been overwritten with a newer nonce */ 2283 ret = mhd_CHECK_NONCENC_STALE; 2284 } 2285 } 2286 2287 mhd_mutex_unlock_chk (&(d->auth_dg.nonces_lock)); 2288 2289 return ret; 2290 } 2291 2292 2293 struct test_header_param 2294 { 2295 struct MHD_Request *request; 2296 size_t num_get_params; 2297 }; 2298 2299 /** 2300 * Test if the given key-value pair is in the headers for the 2301 * given request. 2302 * 2303 * @param cls the test context 2304 * @param name the name of the key 2305 * @param value the value of the key 2306 * @return 'true' if the key-value pair is in the headers, 2307 * 'false' if not 2308 */ 2309 static MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_NONNULL_ (3) bool 2310 test_header (void *restrict cls, 2311 const struct MHD_String *restrict name, 2312 const struct MHD_StringNullable *restrict value) 2313 { 2314 struct test_header_param *const param = (struct test_header_param *)cls; 2315 struct MHD_Request *req = param->request; 2316 struct mhd_RequestField *pos; 2317 size_t i; 2318 2319 param->num_get_params++; 2320 i = 0; 2321 for (pos = mhd_DLINKEDL_GET_FIRST (req, fields); 2322 NULL != pos; 2323 pos = mhd_DLINKEDL_GET_NEXT (pos, fields)) 2324 { 2325 if (MHD_VK_URI_QUERY_PARAM != pos->field.kind) 2326 continue; 2327 if (++i == param->num_get_params) 2328 { 2329 if (name->len != pos->field.nv.name.len) 2330 return false; 2331 if (value->len != pos->field.nv.value.len) 2332 return false; 2333 if (0 != name->len) 2334 { 2335 mhd_assert (NULL != name->cstr); 2336 mhd_assert (NULL != pos->field.nv.name.cstr); 2337 if (0 != memcmp (name->cstr, 2338 pos->field.nv.name.cstr, 2339 name->len)) 2340 return false; 2341 } 2342 if (0 != value->len) 2343 { 2344 mhd_assert (NULL != value->cstr); 2345 mhd_assert (NULL != pos->field.nv.value.cstr); 2346 if (0 != memcmp (value->cstr, 2347 pos->field.nv.value.cstr, 2348 value->len)) 2349 return false; 2350 } 2351 return true; 2352 } 2353 } 2354 return false; 2355 } 2356 2357 2358 /** 2359 * Check that the arguments given by the client as part 2360 * of the authentication header match the arguments we 2361 * got as part of the HTTP request URI. 2362 * 2363 * @param req the request with get arguments to compare against 2364 * @param args the copy of argument URI string (after "?" in URI), will be 2365 * modified by this function 2366 * @return 'true' if the arguments match, 2367 * 'false' if not 2368 */ 2369 static MHD_FN_PAR_NONNULL_ALL_ 2370 MHD_FN_PAR_CSTR_ (3) 2371 MHD_FN_PAR_INOUT_SIZE_ (3, 2) bool 2372 check_argument_match (struct MHD_Request *restrict req, 2373 size_t args_len, 2374 char *restrict args) 2375 { 2376 struct mhd_RequestField *pos; 2377 struct test_header_param param; 2378 2379 param.request = req; 2380 param.num_get_params = 0; 2381 if (!mhd_parse_uri_args (args_len, 2382 args, 2383 &test_header, 2384 ¶m)) 2385 return false; 2386 2387 /* Check that the number of arguments matches */ 2388 for (pos = mhd_DLINKEDL_GET_FIRST (req, fields); 2389 NULL != pos; 2390 pos = mhd_DLINKEDL_GET_NEXT (pos, fields)) 2391 { 2392 if (MHD_VK_URI_QUERY_PARAM != pos->field.kind) 2393 continue; 2394 param.num_get_params--; 2395 } 2396 2397 if (0 != param.num_get_params) 2398 return false; /* argument count mismatch */ 2399 2400 return true; 2401 } 2402 2403 2404 /** 2405 * Check that the URI provided by the client as part 2406 * of the authentication header match the real HTTP request URI. 2407 * 2408 * @param req the request to compare URI 2409 * @param uri the copy of URI in the authentication header, should point to 2410 * modifiable buffer at least @a uri_len + 1 characters long, 2411 * will be modified by this function, not valid upon return 2412 * @param uri_len the length of the @a uri string in characters 2413 * @return boolean true if the URIs match, 2414 * boolean false if not 2415 */ 2416 static MHD_FN_PAR_NONNULL_ALL_ 2417 MHD_FN_PAR_INOUT_ (3) bool 2418 check_uri_match (struct MHD_Request *restrict req, 2419 const size_t uri_len, 2420 char *restrict uri) 2421 { 2422 char *qmark; 2423 char *args; 2424 size_t url_len; /* The part before '?' char */ 2425 size_t args_len; 2426 2427 if (uri_len != req->req_target_len) 2428 return false; 2429 2430 uri[uri_len] = 0; 2431 qmark = (char *)memchr (uri, 2432 '?', 2433 uri_len); 2434 if (NULL != qmark) 2435 { 2436 *qmark = 0; 2437 url_len = (size_t)(qmark - uri); 2438 } 2439 else 2440 url_len = uri_len; 2441 2442 /* Need to unescape URI before comparing with req->url */ 2443 url_len = mhd_str_pct_decode_lenient_n (uri, 2444 url_len, 2445 uri, 2446 url_len, 2447 NULL); 2448 if ((url_len != req->url_len) 2449 || (0 != memcmp (uri, 2450 req->url, 2451 url_len))) 2452 return false; 2453 2454 args = (NULL != qmark) ? (qmark + 1) : uri + uri_len; 2455 args_len = (size_t)(uri + uri_len - args); 2456 2457 if (!check_argument_match (req, 2458 args_len, 2459 args)) 2460 return false; 2461 2462 return true; 2463 } 2464 2465 2466 /** 2467 * The size of the unquoting buffer in stack 2468 */ 2469 #define mhd_STATIC_UNQ_BUFFER_SIZE 128 2470 2471 2472 /** 2473 * Get the pointer to buffer with required size 2474 * @param tmp1 the first buffer with fixed size 2475 * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer 2476 * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2477 * @param required_size the required size in buffer 2478 * @return the pointer to the buffer or NULL if failed to allocate buffer with 2479 * requested size 2480 */ 2481 static MHD_FN_PAR_NONNULL_ALL_ 2482 MHD_FN_PAR_INOUT_ (2) MHD_FN_PAR_INOUT_ (3) char * 2483 get_buffer_for_size (char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE], 2484 char **restrict ptmp2, 2485 size_t *restrict ptmp2_size, 2486 size_t required_size) 2487 { 2488 mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); 2489 mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); 2490 mhd_assert ((0 == *ptmp2_size) \ 2491 || (mhd_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); 2492 2493 if (required_size <= mhd_STATIC_UNQ_BUFFER_SIZE) 2494 return tmp1; 2495 2496 if (required_size <= *ptmp2_size) 2497 return *ptmp2; 2498 2499 if (required_size > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) 2500 return NULL; 2501 if (NULL != *ptmp2) 2502 free (*ptmp2); 2503 *ptmp2 = (char *)malloc (required_size); 2504 if (NULL == *ptmp2) 2505 *ptmp2_size = 0; 2506 else 2507 *ptmp2_size = required_size; 2508 return *ptmp2; 2509 } 2510 2511 2512 /** 2513 * The result of parameter unquoting 2514 */ 2515 enum mhd_GetUnqResult 2516 { 2517 mhd_UNQ_OK = MHD_DAUTH_OK, /**< Got unquoted string */ 2518 mhd_UNQ_TOO_LARGE = MHD_DAUTH_TOO_LARGE, /**< The string is too large to unquote */ 2519 mhd_UNQ_OUT_OF_MEM = MHD_DAUTH_ERROR /**< Out of memory error */ 2520 }; 2521 2522 /** 2523 * Get Digest authorisation parameter as unquoted string. 2524 * @param param the parameter to process 2525 * @param[in,out] tmp1 the small buffer in stack 2526 * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer 2527 * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2528 * @param[out] unquoted the pointer to store the result, NOT zero terminated 2529 * @return enum code indicating result of the process 2530 */ 2531 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 2532 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4) 2533 MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult 2534 get_unquoted_param (const struct mhd_RqDAuthParam *param, 2535 char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE], 2536 char **restrict ptmp2, 2537 size_t *restrict ptmp2_size, 2538 struct mhd_BufferConst *restrict unquoted) 2539 { 2540 char *str; 2541 size_t len; 2542 mhd_assert (NULL != param->value.cstr); 2543 mhd_assert (0 != param->value.len); 2544 2545 if (!param->quoted) 2546 { 2547 unquoted->data = param->value.cstr; 2548 unquoted->size = param->value.len; 2549 return mhd_UNQ_OK; 2550 } 2551 /* The value is present and is quoted, needs to be copied and unquoted */ 2552 str = get_buffer_for_size (tmp1, 2553 ptmp2, 2554 ptmp2_size, 2555 param->value.len); 2556 if (NULL == str) 2557 return (param->value.len > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ? 2558 mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM; 2559 2560 len = mhd_str_unquote (param->value.cstr, 2561 param->value.len, 2562 str); 2563 unquoted->data = str; 2564 unquoted->size = len; 2565 mhd_assert (0 != unquoted->size); 2566 mhd_assert (unquoted->size < param->value.len); 2567 return mhd_UNQ_OK; 2568 } 2569 2570 2571 /** 2572 * Get copy of Digest authorisation parameter as unquoted string. 2573 * @param param the parameter to process 2574 * @param[in,out] tmp1 the small buffer in stack 2575 * @param[in,out] ptmp2 the pointer to pointer to malloc'ed buffer 2576 * @param[in,out] ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 2577 * @param[out] unquoted the pointer to store the result, NOT zero terminated, 2578 * but with enough space to zero-terminate 2579 * @return enum code indicating result of the process 2580 */ 2581 static MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 2582 MHD_FN_PAR_OUT_ (2) MHD_FN_PAR_INOUT_ (3) MHD_FN_PAR_INOUT_ (4) 2583 MHD_FN_PAR_OUT_ (5) enum mhd_GetUnqResult 2584 get_unquoted_param_copy (const struct mhd_RqDAuthParam *param, 2585 char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE], 2586 char **restrict ptmp2, 2587 size_t *restrict ptmp2_size, 2588 struct mhd_Buffer *restrict unquoted) 2589 { 2590 mhd_assert (NULL != param->value.cstr); 2591 mhd_assert (0 != param->value.len); 2592 2593 /* The value is present and is quoted, needs to be copied and unquoted */ 2594 /* Allocate buffer with one more additional byte for zero-termination */ 2595 unquoted->data = 2596 get_buffer_for_size (tmp1, 2597 ptmp2, 2598 ptmp2_size, 2599 param->value.len + 1); 2600 2601 if (NULL == unquoted->data) 2602 return (param->value.len + 1 > mhd_AUTH_DIGEST_MAX_PARAM_SIZE) ? 2603 mhd_UNQ_TOO_LARGE : mhd_UNQ_OUT_OF_MEM; 2604 2605 if (!param->quoted) 2606 { 2607 memcpy (unquoted->data, 2608 param->value.cstr, 2609 param->value.len); 2610 unquoted->size = param->value.len; 2611 return mhd_UNQ_OK; 2612 } 2613 2614 unquoted->size = 2615 mhd_str_unquote (param->value.cstr, 2616 param->value.len, 2617 unquoted->data); 2618 mhd_assert (0 != unquoted->size); 2619 mhd_assert (unquoted->size < param->value.len); 2620 return mhd_UNQ_OK; 2621 } 2622 2623 2624 /** 2625 * Check whether Digest Auth request parameter is equal to given string 2626 * @param param the parameter to check 2627 * @param str_len the length of the @a str 2628 * @param str the string to compare with, does not need to be zero-terminated 2629 * @return true is parameter is equal to the given string, 2630 * false otherwise 2631 */ 2632 mhd_static_inline MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 2633 MHD_FN_PAR_IN_SIZE_ (3, 2) bool 2634 is_param_equal (const struct mhd_RqDAuthParam *restrict param, 2635 const size_t str_len, 2636 const char *restrict str) 2637 { 2638 mhd_assert (NULL != param->value.cstr); 2639 mhd_assert (0 != param->value.len); 2640 if (param->quoted) 2641 return mhd_str_equal_quoted_bin_n (param->value.cstr, 2642 param->value.len, 2643 str, 2644 str_len); 2645 return (str_len == param->value.len) 2646 && (0 == memcmp (str, param->value.cstr, str_len)); 2647 } 2648 2649 2650 /** 2651 * Check whether Digest Auth request parameter is caseless equal to given string 2652 * @param param the parameter to check 2653 * @param str_len the length of the @a str 2654 * @param str the string to compare with, does not need to be zero-terminated 2655 * @return true is parameter is caseless equal to the given string, 2656 * false otherwise 2657 */ 2658 mhd_static_inline MHD_FN_MUST_CHECK_RESULT_ MHD_FN_PAR_NONNULL_ALL_ 2659 MHD_FN_PAR_IN_SIZE_ (3, 2) bool 2660 is_param_equal_caseless (const struct mhd_RqDAuthParam *restrict param, 2661 const size_t str_len, 2662 const char *restrict str) 2663 { 2664 mhd_assert (NULL != param->value.cstr); 2665 mhd_assert (0 != param->value.len); 2666 if (param->quoted) 2667 return mhd_str_equal_caseless_quoted_bin_n (param->value.cstr, 2668 param->value.len, 2669 str, 2670 str_len); 2671 return (str_len == param->value.len) 2672 && (mhd_str_equal_caseless_bin_n (str, param->value.cstr, str_len)); 2673 } 2674 2675 2676 /** 2677 * Authenticates the authorization header sent by the client 2678 * 2679 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 2680 * @a mqop and the client uses this mode, then server generated nonces are 2681 * used as one-time nonces because nonce-count is not supported in this old RFC. 2682 * Communication in this mode is very inefficient, especially if the client 2683 * requests several resources one-by-one as for every request new nonce must be 2684 * generated and client repeat all requests twice (the first time to get a new 2685 * nonce and the second time to perform an authorised request). 2686 * 2687 * @param req the request handle 2688 * @param realm the realm for authorization of the client 2689 * @param username the username to be authenticated, must be in clear text 2690 * even if userhash is used by the client 2691 * @param password the password used in the authentication, 2692 * must be NULL if @a userdigest is not NULL 2693 * @param userdigest the precalculated binary hash of the string 2694 * "username:realm:password", 2695 * must be NULL if @a password is not NULL 2696 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 2697 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 2698 * returned; 2699 * unlike #digest_auth_check_all() zero is treated as "no limit" 2700 * @param mqop the QOP to use 2701 * @param malgo digest algorithms allowed to use, fail if algorithm specified 2702 * by the client is not allowed by this parameter 2703 * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, 2704 * to be freed if not NULL upon return 2705 * @return #MHD_DAUTH_OK if authenticated, 2706 * error code otherwise. 2707 * @ingroup authentication 2708 */ 2709 static MHD_FN_MUST_CHECK_RESULT_ 2710 MHD_FN_PAR_NONNULL_ (1) 2711 MHD_FN_PAR_NONNULL_ (2) MHD_FN_PAR_CSTR_ (2) 2712 MHD_FN_PAR_NONNULL_ (3) MHD_FN_PAR_CSTR_ (3) 2713 MHD_FN_PAR_CSTR_ (4) 2714 enum MHD_DigestAuthResult 2715 digest_auth_check_all_inner (struct MHD_Request *restrict req, 2716 const char *restrict realm, 2717 const char *restrict username, 2718 const char *restrict password, 2719 const uint8_t *restrict userdigest, 2720 uint_fast32_t max_nc, 2721 enum MHD_DigestAuthMultiQOP mqop, 2722 enum MHD_DigestAuthMultiAlgo malgo, 2723 char **pbuf, 2724 struct DigestAlgorithm *da) 2725 { 2726 struct MHD_Daemon *const daemon = 2727 mhd_daemon_get_master_daemon ( 2728 mhd_CNTNR_PTR (req, struct MHD_Connection, rq)->daemon); 2729 enum MHD_DigestAuthAlgo c_algo; /**< Client's algorithm */ 2730 enum MHD_DigestAuthQOP c_qop; /**< Client's QOP */ 2731 unsigned int digest_size; 2732 uint8_t hash1_bin[mhd_MAX_DIGEST]; 2733 uint8_t hash2_bin[mhd_MAX_DIGEST]; 2734 uint_fast32_t nc; 2735 const struct mhd_AuthDigesReqParams *restrict params; 2736 /** 2737 * Temporal buffer in stack for unquoting and other needs 2738 */ 2739 char tmp1[mhd_STATIC_UNQ_BUFFER_SIZE]; 2740 char **const ptmp2 = pbuf; /**< Temporal malloc'ed buffer for unquoting */ 2741 size_t tmp2_size; /**< The size of @a tmp2 buffer */ 2742 struct mhd_BufferConst unquoted; 2743 struct mhd_Buffer unq_copy; 2744 enum mhd_GetUnqResult unq_res; 2745 size_t username_len; 2746 size_t realm_len; 2747 2748 mhd_assert ((NULL == password) != (NULL == userdigest)); 2749 2750 tmp2_size = 0; 2751 2752 if (1) 2753 { 2754 enum MHD_StatusCode res; 2755 2756 res = get_rq_auth_digest_params (req); 2757 if (MHD_SC_OK != res) 2758 { 2759 if (MHD_SC_AUTH_ABSENT == res) 2760 return MHD_DAUTH_HEADER_MISSING; 2761 else if (MHD_SC_CONNECTION_POOL_NO_MEM_AUTH_DATA == res) 2762 return MHD_DAUTH_ERROR; 2763 else if (MHD_SC_REQ_AUTH_DATA_BROKEN == res) 2764 return MHD_DAUTH_HEADER_BROKEN; 2765 else 2766 mhd_UNREACHABLE (); 2767 } 2768 params = req->auth.digest.rqp; 2769 } 2770 mhd_assert (NULL != params); 2771 2772 /* ** Initial parameters checks and setup ** */ 2773 /* Get client's algorithm */ 2774 c_algo = params->algo; 2775 /* Check whether client's algorithm is allowed by function parameter */ 2776 if (((unsigned int)c_algo) != 2777 (((unsigned int)c_algo) & ((unsigned int)malgo))) 2778 return MHD_DAUTH_WRONG_ALGO; 2779 /* Check whether client's algorithm is supported */ 2780 if (0 != (((unsigned int)c_algo) & MHD_DIGEST_AUTH_ALGO_SESSION)) 2781 return MHD_DAUTH_UNSUPPORTED_ALGO; 2782 #ifndef MHD_SUPPORT_MD5 2783 if (0 != (((unsigned int)c_algo) & MHD_DIGEST_BASE_ALGO_MD5)) 2784 return MHD_DAUTH_UNSUPPORTED_ALGO; 2785 #endif /* ! MHD_SUPPORT_MD5 */ 2786 #ifndef MHD_SUPPORT_SHA256 2787 if (0 != (((unsigned int)c_algo) & MHD_DIGEST_BASE_ALGO_SHA256)) 2788 return MHD_DAUTH_UNSUPPORTED_ALGO; 2789 #endif /* ! MHD_SUPPORT_SHA256 */ 2790 #ifndef MHD_SUPPORT_SHA512_256 2791 if (0 != (((unsigned int)c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 2792 return MHD_DAUTH_UNSUPPORTED_ALGO; 2793 #endif /* ! MHD_SUPPORT_SHA512_256 */ 2794 if (!digest_init_one_time (da, get_base_digest_algo (c_algo))) 2795 mhd_UNREACHABLE (); 2796 /* Check 'mqop' value */ 2797 c_qop = params->qop; 2798 /* Check whether client's QOP is allowed by function parameter */ 2799 if (((unsigned int)c_qop) != 2800 (((unsigned int)c_qop) & ((unsigned int)mqop))) 2801 return MHD_DAUTH_WRONG_QOP; 2802 if (0 != (((unsigned int)c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT)) 2803 return MHD_DAUTH_UNSUPPORTED_QOP; 2804 2805 digest_size = digest_get_size (da); 2806 2807 /* ** A quick check for presence of all required parameters ** */ 2808 2809 if ((NULL == params->username.value.cstr) 2810 && (NULL == params->username_ext.value.cstr)) 2811 return MHD_DAUTH_HEADER_BROKEN; 2812 else if ((NULL != params->username.value.cstr) 2813 && (NULL != params->username_ext.value.cstr)) 2814 return MHD_DAUTH_HEADER_BROKEN; /* Parameters cannot be used together */ 2815 else if ((NULL != params->username_ext.value.cstr) 2816 && (mhd_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len)) 2817 return MHD_DAUTH_HEADER_BROKEN; /* Broken extended notation */ 2818 else if (params->userhash && (NULL == params->username.value.cstr)) 2819 return MHD_DAUTH_HEADER_BROKEN; /* Userhash cannot be used with extended notation */ 2820 else if (params->userhash && (digest_size * 2 > params->username.value.len)) 2821 return MHD_DAUTH_WRONG_USERNAME; /* Too few chars for correct userhash */ 2822 else if (params->userhash && (digest_size * 4 < params->username.value.len)) 2823 return MHD_DAUTH_WRONG_USERNAME; /* Too many chars for correct userhash */ 2824 2825 if (NULL == params->realm.value.cstr) 2826 return MHD_DAUTH_HEADER_BROKEN; 2827 else if (((NULL == userdigest) || params->userhash) 2828 && (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len)) 2829 return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */ 2830 2831 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2832 { 2833 if (NULL == params->nc.value.cstr) 2834 return MHD_DAUTH_HEADER_BROKEN; 2835 else if (0 == params->nc.value.len) 2836 return MHD_DAUTH_HEADER_BROKEN; 2837 else if (4 * 8 < params->nc.value.len) /* Four times more than needed */ 2838 return MHD_DAUTH_HEADER_BROKEN; 2839 2840 if (NULL == params->cnonce.value.cstr) 2841 return MHD_DAUTH_HEADER_BROKEN; 2842 else if (0 == params->cnonce.value.len) 2843 return MHD_DAUTH_HEADER_BROKEN; 2844 else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len) 2845 return MHD_DAUTH_TOO_LARGE; 2846 } 2847 2848 /* The QOP parameter was checked already */ 2849 2850 if (NULL == params->uri.value.cstr) 2851 return MHD_DAUTH_HEADER_BROKEN; 2852 else if (0 == params->uri.value.len) 2853 return MHD_DAUTH_HEADER_BROKEN; 2854 else if (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len) 2855 return MHD_DAUTH_TOO_LARGE; 2856 2857 if (NULL == params->nonce.value.cstr) 2858 return MHD_DAUTH_HEADER_BROKEN; 2859 else if (0 == params->nonce.value.len) 2860 return MHD_DAUTH_HEADER_BROKEN; 2861 else if (mhd_AUTH_DIGEST_NONCE_LEN * 2 < params->nonce.value.len) 2862 return MHD_DAUTH_NONCE_WRONG; 2863 2864 if (NULL == params->response.value.cstr) 2865 return MHD_DAUTH_HEADER_BROKEN; 2866 else if (0 == params->response.value.len) 2867 return MHD_DAUTH_HEADER_BROKEN; 2868 else if (digest_size * 4 < params->response.value.len) 2869 return MHD_DAUTH_RESPONSE_WRONG; 2870 2871 /* ** Check simple parameters match ** */ 2872 2873 /* Check 'algorithm' */ 2874 /* The 'algorithm' was checked at the start of the function */ 2875 /* 'algorithm' valid */ 2876 2877 /* Check 'qop' */ 2878 /* The 'qop' was checked at the start of the function */ 2879 /* 'qop' valid */ 2880 2881 /* Check 'realm' */ 2882 realm_len = strlen (realm); 2883 if (!is_param_equal (¶ms->realm, 2884 realm_len, 2885 realm)) 2886 return MHD_DAUTH_WRONG_REALM; 2887 /* 'realm' valid */ 2888 2889 /* Check 'username' */ 2890 username_len = strlen (username); 2891 if (!params->userhash) 2892 { 2893 if (NULL != params->username.value.cstr) 2894 { /* Username in standard notation */ 2895 if (!is_param_equal (¶ms->username, username_len, username)) 2896 return MHD_DAUTH_WRONG_USERNAME; 2897 } 2898 else 2899 { /* Username in extended notation */ 2900 char *r_uname; 2901 size_t buf_size = params->username_ext.value.len; 2902 ssize_t res; 2903 2904 mhd_assert (NULL != params->username_ext.value.cstr); 2905 mhd_assert (mhd_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */ 2906 buf_size += 1; /* For zero-termination */ 2907 buf_size -= mhd_DAUTH_EXT_PARAM_MIN_LEN; 2908 r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size); 2909 if (NULL == r_uname) 2910 return (mhd_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ? 2911 MHD_DAUTH_TOO_LARGE : MHD_DAUTH_ERROR; 2912 res = get_rq_extended_uname_copy_z (params->username_ext.value.cstr, 2913 params->username_ext.value.len, 2914 r_uname, buf_size); 2915 if (0 > res) 2916 return MHD_DAUTH_HEADER_BROKEN; /* Broken extended notation */ 2917 if ((username_len != (size_t)res) 2918 || (0 != memcmp (username, r_uname, username_len))) 2919 return MHD_DAUTH_WRONG_USERNAME; 2920 } 2921 } 2922 else 2923 { /* Userhash */ 2924 mhd_assert (NULL != params->username.value.cstr); 2925 calc_userhash (da, 2926 username_len, 2927 username, 2928 realm_len, 2929 realm, 2930 hash1_bin); 2931 if (digest_has_error (da)) 2932 return MHD_DAUTH_ERROR; 2933 mhd_assert (sizeof (tmp1) >= (2 * digest_size)); 2934 mhd_bin_to_hex (hash1_bin, digest_size, tmp1); 2935 if (!is_param_equal_caseless (¶ms->username, 2 * digest_size, tmp1)) 2936 return MHD_DAUTH_WRONG_USERNAME; 2937 /* To simplify the logic, the digest is reset here instead of resetting 2938 before the next hash calculation. */ 2939 digest_reset (da); 2940 } 2941 /* 'username' valid */ 2942 2943 /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */ 2944 2945 /* Get 'nc' digital value */ 2946 nc = 0; 2947 switch (get_rq_nc (params, 2948 &nc)) 2949 { 2950 case mhd_GET_RQ_NC_NONE: 2951 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 2952 return MHD_DAUTH_HEADER_BROKEN; 2953 nc = 1; /* Force 'nc' value */ 2954 break; 2955 case mhd_GET_RQ_NC_VALID: 2956 if (MHD_DIGEST_AUTH_QOP_NONE == c_qop) 2957 return MHD_DAUTH_HEADER_BROKEN; 2958 break; 2959 case mhd_GET_RQ_NC_TOO_LONG: 2960 case mhd_GET_RQ_NC_TOO_LARGE: 2961 return MHD_DAUTH_NONCE_STALE; 2962 break; 2963 case mhd_GET_RQ_NC_BROKEN: 2964 return MHD_DAUTH_HEADER_BROKEN; 2965 break; 2966 default: 2967 mhd_UNREACHABLE (); 2968 break; 2969 } 2970 if (0 == nc) 2971 return MHD_DAUTH_HEADER_BROKEN; 2972 if (0 == max_nc) 2973 max_nc = daemon->auth_dg.cfg.def_max_nc; 2974 if (max_nc < nc) 2975 return MHD_DAUTH_NONCE_STALE; /* Too large 'nc' value */ 2976 /* Got 'nc' digital value */ 2977 2978 /* Get 'nonce' with basic checks */ 2979 unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, 2980 &unquoted); 2981 if (mhd_UNQ_TOO_LARGE == unq_res) 2982 return MHD_DAUTH_TOO_LARGE; 2983 if (mhd_UNQ_OUT_OF_MEM == unq_res) 2984 return MHD_DAUTH_ERROR; 2985 2986 2987 switch (check_nonce_nc (daemon, 2988 unquoted.size, 2989 unquoted.data, 2990 nc, 2991 (uint_fast32_t) 2992 ((mhd_monotonic_msec_counter () / 1000) 2993 & UINT32_C (0xFFFFFFFF)))) 2994 { 2995 case mhd_CHECK_NONCENC_OK: 2996 break; 2997 case mhd_CHECK_NONCENC_STALE: 2998 return MHD_DAUTH_NONCE_STALE; 2999 case mhd_CHECK_NONCENC_WRONG: 3000 return MHD_DAUTH_NONCE_WRONG; 3001 default: 3002 mhd_UNREACHABLE (); 3003 break; 3004 } 3005 /* The nonce was generated by MHD, is not stale and nonce-nc combination has 3006 not been used before */ 3007 3008 /* ** Build H(A2) and check URI match in the header and in the request ** */ 3009 3010 /* Get 'uri' */ 3011 mhd_assert (!da->hashing); 3012 digest_update (da, req->method.len, req->method.cstr); 3013 digest_update_with_colon (da); 3014 #if 0 3015 /* TODO: add support for "auth-int" */ 3016 digest_update_str (da, hentity); 3017 digest_update_with_colon (da); 3018 #endif 3019 unq_res = get_unquoted_param_copy (¶ms->uri, tmp1, ptmp2, &tmp2_size, 3020 &unq_copy); 3021 if (mhd_UNQ_TOO_LARGE == unq_res) 3022 return MHD_DAUTH_TOO_LARGE; 3023 if (mhd_UNQ_OUT_OF_MEM == unq_res) 3024 return MHD_DAUTH_ERROR; 3025 3026 digest_update_buf (da, &unq_copy); 3027 /* The next check will modify copied URI string */ 3028 if (!check_uri_match (req, unq_copy.size, unq_copy.data)) 3029 return MHD_DAUTH_WRONG_URI; 3030 digest_calc_hash (da, hash2_bin); 3031 #ifdef mhd_DIGEST_HAS_EXT_ERROR 3032 /* Skip digest calculation external error check, the next one checks both */ 3033 #endif /* mhd_DIGEST_HAS_EXT_ERROR */ 3034 /* Got H(A2) */ 3035 3036 /* ** Build H(A1) ** */ 3037 if (NULL == userdigest) 3038 { 3039 mhd_assert (!da->hashing); 3040 digest_reset (da); 3041 calc_userdigest (da, 3042 username, username_len, 3043 realm, realm_len, 3044 password, 3045 hash1_bin); 3046 } 3047 /* TODO: support '-sess' versions */ 3048 #ifdef mhd_DIGEST_HAS_EXT_ERROR 3049 if (digest_has_error (da)) 3050 return MHD_DAUTH_ERROR; 3051 #endif /* mhd_DIGEST_HAS_EXT_ERROR */ 3052 /* Got H(A1) */ 3053 3054 /* ** Check 'response' ** */ 3055 3056 mhd_assert (!da->hashing); 3057 digest_reset (da); 3058 /* Update digest with H(A1) */ 3059 mhd_assert (sizeof (tmp1) >= (digest_size * 2)); 3060 if (NULL == userdigest) 3061 mhd_bin_to_hex (hash1_bin, digest_size, tmp1); 3062 else 3063 mhd_bin_to_hex (userdigest, digest_size, tmp1); 3064 digest_update (da, digest_size * 2, (const uint8_t *)tmp1); 3065 3066 /* H(A1) is not needed anymore, reuse the buffer. 3067 * Use hash1_bin for the client's 'response' decoded to binary form. */ 3068 unq_res = get_unquoted_param (¶ms->response, tmp1, ptmp2, &tmp2_size, 3069 &unquoted); 3070 if (mhd_UNQ_TOO_LARGE == unq_res) 3071 return MHD_DAUTH_TOO_LARGE; 3072 if (mhd_UNQ_OUT_OF_MEM == unq_res) 3073 return MHD_DAUTH_ERROR; 3074 if (digest_size != mhd_hex_to_bin (unquoted.data, unquoted.size, hash1_bin)) 3075 return MHD_DAUTH_RESPONSE_WRONG; 3076 3077 /* Update digest with ':' */ 3078 digest_update_with_colon (da); 3079 /* Update digest with 'nonce' text value */ 3080 unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, 3081 &unquoted); 3082 if (mhd_UNQ_TOO_LARGE == unq_res) 3083 return MHD_DAUTH_TOO_LARGE; 3084 if (mhd_UNQ_OUT_OF_MEM == unq_res) 3085 return MHD_DAUTH_ERROR; 3086 digest_update_cbuf (da, &unquoted); 3087 /* Update digest with ':' */ 3088 digest_update_with_colon (da); 3089 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop) 3090 { 3091 /* Update digest with 'nc' text value */ 3092 unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, 3093 &unquoted); 3094 if (mhd_UNQ_TOO_LARGE == unq_res) 3095 return MHD_DAUTH_TOO_LARGE; 3096 if (mhd_UNQ_OUT_OF_MEM == unq_res) 3097 return MHD_DAUTH_ERROR; 3098 digest_update_cbuf (da, &unquoted); 3099 /* Update digest with ':' */ 3100 digest_update_with_colon (da); 3101 /* Update digest with 'cnonce' value */ 3102 unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, 3103 &unquoted); 3104 if (mhd_UNQ_TOO_LARGE == unq_res) 3105 return MHD_DAUTH_TOO_LARGE; 3106 if (mhd_UNQ_OUT_OF_MEM == unq_res) 3107 return MHD_DAUTH_ERROR; 3108 digest_update_cbuf (da, &unquoted); 3109 /* Update digest with ':' */ 3110 digest_update_with_colon (da); 3111 /* Update digest with 'qop' value */ 3112 unq_res = get_unquoted_param (¶ms->qop_raw, tmp1, ptmp2, &tmp2_size, 3113 &unquoted); 3114 if (mhd_UNQ_TOO_LARGE == unq_res) 3115 return MHD_DAUTH_TOO_LARGE; 3116 if (mhd_UNQ_OUT_OF_MEM == unq_res) 3117 return MHD_DAUTH_ERROR; 3118 digest_update_cbuf (da, &unquoted); 3119 /* Update digest with ':' */ 3120 digest_update_with_colon (da); 3121 } 3122 /* Update digest with H(A2) */ 3123 mhd_bin_to_hex (hash2_bin, digest_size, tmp1); 3124 digest_update (da, digest_size * 2, (const uint8_t *)tmp1); 3125 3126 /* H(A2) is not needed anymore, reuse the buffer. 3127 * Use hash2_bin for the calculated response in binary form */ 3128 digest_calc_hash (da, hash2_bin); 3129 #ifdef mhd_DIGEST_HAS_EXT_ERROR 3130 if (digest_has_error (da)) 3131 return MHD_DAUTH_ERROR; 3132 #endif /* mhd_DIGEST_HAS_EXT_ERROR */ 3133 3134 if (0 != memcmp (hash1_bin, hash2_bin, digest_size)) 3135 return MHD_DAUTH_RESPONSE_WRONG; 3136 3137 return MHD_DAUTH_OK; 3138 } 3139 3140 3141 /** 3142 * Authenticates the authorization header sent by the client 3143 * 3144 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3145 * @a mqop and the client uses this mode, then server generated nonces are 3146 * used as one-time nonces because nonce-count is not supported in this old RFC. 3147 * Communication in this mode is very inefficient, especially if the client 3148 * requests several resources one-by-one as for every request new nonce must be 3149 * generated and client repeat all requests twice (the first time to get a new 3150 * nonce and the second time to perform an authorised request). 3151 * 3152 * @param req the request handle 3153 * @param realm the realm for authorization of the client 3154 * @param username the username to be authenticated, must be in clear text 3155 * even if userhash is used by the client 3156 * @param password the password used in the authentication, 3157 * must be NULL if @a userdigest is not NULL 3158 * @param userdigest the precalculated binary hash of the string 3159 * "username:realm:password", 3160 * must be NULL if @a password is not NULL 3161 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3162 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3163 * returned; 3164 * if set to zero then daemon's default value is used 3165 * @param mqop the QOP to use 3166 * @param malgo digest algorithms allowed to use, fail if algorithm specified 3167 * by the client is not allowed by this parameter 3168 * @return #MHD_DAUTH_OK if authenticated, 3169 * error code otherwise. 3170 * @ingroup authentication 3171 */ 3172 static enum MHD_DigestAuthResult 3173 digest_auth_check_all (struct MHD_Request *restrict req, 3174 const char *restrict realm, 3175 const char *restrict username, 3176 const char *restrict password, 3177 const uint8_t *restrict userdigest, 3178 uint_fast32_t max_nc, 3179 enum MHD_DigestAuthMultiQOP mqop, 3180 enum MHD_DigestAuthMultiAlgo malgo) 3181 { 3182 enum MHD_DigestAuthResult res; 3183 char *buf; 3184 struct DigestAlgorithm da; 3185 3186 buf = NULL; 3187 digest_setup_zero (&da); 3188 res = digest_auth_check_all_inner (req, 3189 realm, 3190 username, 3191 password, 3192 userdigest, 3193 max_nc, 3194 mqop, 3195 malgo, 3196 &buf, 3197 &da); 3198 digest_deinit (&da); 3199 if (NULL != buf) 3200 free (buf); 3201 3202 return res; 3203 } 3204 3205 3206 /** 3207 * Authenticates the authorization header sent by the client. 3208 * 3209 * If RFC2069 mode is allowed by setting bit #MHD_DIGEST_AUTH_QOP_NONE in 3210 * @a mqop and the client uses this mode, then server generated nonces are 3211 * used as one-time nonces because nonce-count is not supported in this old RFC. 3212 * Communication in this mode is very inefficient, especially if the client 3213 * requests several resources one-by-one as for every request a new nonce must 3214 * be generated and client repeats all requests twice (first time to get a new 3215 * nonce and second time to perform an authorised request). 3216 * 3217 * @param request the request 3218 * @param realm the realm for authorization of the client 3219 * @param username the username to be authenticated, must be in clear text 3220 * even if userhash is used by the client 3221 * @param password the password matching the @a username (and the @a realm) 3222 * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc 3223 * exceeds the specified value then MHD_DAUTH_NONCE_STALE is 3224 * returned; 3225 * if zero is specified then daemon default value is used. 3226 * @param mqop the QOP to use 3227 * @param malgo digest algorithms allowed to use, fail if algorithm used 3228 * by the client is not allowed by this parameter 3229 * @return #MHD_DAUTH_OK if authenticated, 3230 * the error code otherwise 3231 * @ingroup authentication 3232 */ 3233 MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_ 3234 MHD_FN_PAR_CSTR_ (2) MHD_FN_PAR_CSTR_ (3) MHD_FN_PAR_CSTR_ (4) 3235 enum MHD_DigestAuthResult 3236 MHD_digest_auth_check (struct MHD_Request *MHD_RESTRICT request, 3237 const char *MHD_RESTRICT realm, 3238 const char *MHD_RESTRICT username, 3239 const char *MHD_RESTRICT password, 3240 uint_fast32_t max_nc, 3241 enum MHD_DigestAuthMultiQOP mqop, 3242 enum MHD_DigestAuthMultiAlgo malgo) 3243 { 3244 return digest_auth_check_all (request, 3245 realm, 3246 username, 3247 password, 3248 NULL, 3249 max_nc, 3250 mqop, 3251 malgo); 3252 } 3253 3254 3255 MHD_EXTERN_ MHD_FN_PAR_NONNULL_ALL_ 3256 MHD_FN_PAR_CSTR_ (2) 3257 MHD_FN_PAR_CSTR_ (3) 3258 MHD_FN_PAR_IN_SIZE_ (5, 4) enum MHD_DigestAuthResult 3259 MHD_digest_auth_check_digest (struct MHD_Request *MHD_RESTRICT request, 3260 const char *MHD_RESTRICT realm, 3261 const char *MHD_RESTRICT username, 3262 size_t userdigest_size, 3263 const void *MHD_RESTRICT userdigest, 3264 uint_fast32_t max_nc, 3265 enum MHD_DigestAuthMultiQOP mqop, 3266 enum MHD_DigestAuthMultiAlgo malgo) 3267 { 3268 if (1 != (((0 != (((unsigned int)malgo) \ 3269 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0) 3270 + ((0 != (((unsigned int)malgo) \ 3271 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0) 3272 + ((0 != (((unsigned int)malgo) \ 3273 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0))) 3274 return MHD_DAUTH_UNSUPPORTED_ALGO; 3275 3276 #ifndef MHD_SUPPORT_MD5 3277 if (0 != (((unsigned int)malgo) & MHD_DIGEST_BASE_ALGO_MD5)) 3278 return MHD_DAUTH_UNSUPPORTED_ALGO; 3279 #endif /* ! MHD_SUPPORT_MD5 */ 3280 #ifndef MHD_SUPPORT_SHA256 3281 if (0 != (((unsigned int)malgo) & MHD_DIGEST_BASE_ALGO_SHA256)) 3282 return MHD_DAUTH_UNSUPPORTED_ALGO; 3283 #endif /* ! MHD_SUPPORT_SHA256 */ 3284 #ifndef MHD_SUPPORT_SHA512_256 3285 if (0 != (((unsigned int)malgo) & MHD_DIGEST_BASE_ALGO_SHA512_256)) 3286 return MHD_DAUTH_UNSUPPORTED_ALGO; 3287 #endif /* ! MHD_SUPPORT_SHA512_256 */ 3288 3289 if (digest_get_hash_size ((enum MHD_DigestAuthAlgo)malgo) != 3290 userdigest_size) 3291 return MHD_DAUTH_INVALID_USERDIGEST_SIZE; 3292 3293 return digest_auth_check_all (request, 3294 realm, 3295 username, 3296 NULL, 3297 (const uint8_t *)userdigest, 3298 max_nc, 3299 mqop, 3300 malgo); 3301 }