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