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