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