GNU libmicrohttpd 1.0.9
Loading...
Searching...
No Matches
digestauth.c
Go to the documentation of this file.
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2010, 2011, 2012, 2015, 2018 Daniel Pittman and Christian Grothoff
4 Copyright (C) 2014-2024 Evgeny Grin (Karlson2k)
5
6 This library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Lesser General Public
8 License as published by the Free Software Foundation; either
9 version 2.1 of the License, or (at your option) any later version.
10
11 This library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public
17 License along with this library; if not, write to the Free Software
18 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19*/
30#include "digestauth.h"
31#include "gen_auth.h"
32#include "platform.h"
33#include "mhd_limits.h"
34#include "internal.h"
35#include "response.h"
36#ifdef MHD_MD5_SUPPORT
37# include "mhd_md5_wrap.h"
38#endif /* MHD_MD5_SUPPORT */
39#ifdef MHD_SHA256_SUPPORT
40# include "mhd_sha256_wrap.h"
41#endif /* MHD_SHA256_SUPPORT */
42#ifdef MHD_SHA512_256_SUPPORT
43# include "sha512_256.h"
44#endif /* MHD_SHA512_256_SUPPORT */
45#include "mhd_locks.h"
46#include "mhd_mono_clock.h"
47#include "mhd_str.h"
48#include "mhd_compat.h"
49#include "mhd_bithelpers.h"
50#include "mhd_assert.h"
51#include "mhd_check.h"
52
53
60#define REUSE_TIMEOUT 30
61
66#define DAUTH_JUMPBACK_MAX (0x7F)
67
68
72#define TIMESTAMP_BIN_SIZE (48 / 8)
73
74
78#define TRIM_TO_TIMESTAMP(value) \
79 ((value) & ((UINT64_C (1) << (TIMESTAMP_BIN_SIZE * 8)) - 1))
80
81
85#define TIMESTAMP_CHARS_LEN (TIMESTAMP_BIN_SIZE * 2)
86
87
93#define NONCE_STD_LEN(digest_size) \
94 ((digest_size) * 2 + TIMESTAMP_CHARS_LEN)
95
96
97#ifdef MHD_SHA512_256_SUPPORT
102#define MAX_DIGEST SHA512_256_DIGEST_SIZE
103
107#define SHA256_SHA512_256_DIGEST_SIZE SHA512_256_DIGEST_SIZE
108#elif defined(MHD_SHA256_SUPPORT)
113#define MAX_DIGEST SHA256_DIGEST_SIZE
114
118#define SHA256_SHA512_256_DIGEST_SIZE SHA256_DIGEST_SIZE
119#elif defined(MHD_MD5_SUPPORT)
123#define MAX_DIGEST MD5_DIGEST_SIZE
124#else /* ! MHD_MD5_SUPPORT */
125#error At least one hashing algorithm must be enabled
126#endif /* ! MHD_MD5_SUPPORT */
127
128
132#ifndef HAVE_C_VARARRAYS
138#define VLA_ARRAY_LEN_DIGEST(n) (MAX_DIGEST)
139
140#else
146#define VLA_ARRAY_LEN_DIGEST(n) (n)
147#endif
148
152#define VLA_CHECK_LEN_DIGEST(n) \
153 do { if ((n) > MAX_DIGEST) MHD_PANIC (_ ("VLA too big.\n")); } while (0)
154
158#define MAX_USERNAME_LENGTH 128
159
163#define MAX_REALM_LENGTH 256
164
168#define MAX_AUTH_RESPONSE_LENGTH (MAX_DIGEST * 2)
169
173#define MHD_DAUTH_EXT_PARAM_PREFIX "UTF-8'"
174
178#define MHD_DAUTH_EXT_PARAM_MIN_LEN \
179 MHD_STATICSTR_LEN_ (MHD_DAUTH_EXT_PARAM_PREFIX "'")
180
203
204
210_MHD_static_inline enum MHD_DigestBaseAlgo
212{
213 unsigned int base_algo;
214
215 base_algo =
216 ((unsigned int) algo3)
217 & ~((unsigned int)
220 return (enum MHD_DigestBaseAlgo) base_algo;
221}
222
223
232_MHD_static_inline size_t
234{
235#ifdef MHD_MD5_SUPPORT
237#endif /* MHD_MD5_SUPPORT */
238#ifdef MHD_SHA256_SUPPORT
240#endif /* MHD_SHA256_SUPPORT */
241#ifdef MHD_SHA512_256_SUPPORT
243#ifdef MHD_SHA256_SUPPORT
245#endif /* MHD_SHA256_SUPPORT */
246#endif /* MHD_SHA512_256_SUPPORT */
247 /* Only one algorithm must be specified */
248 mhd_assert (1 == \
249 (((0 != (algo3 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0) \
250 + ((0 != (algo3 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0) \
251 + ((0 != (algo3 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)));
252#ifdef MHD_MD5_SUPPORT
253 if (0 != (((unsigned int) algo3)
254 & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5)))
255 return MHD_MD5_DIGEST_SIZE;
256 else
257#endif /* MHD_MD5_SUPPORT */
258#if defined(MHD_SHA256_SUPPORT) && defined(MHD_SHA512_256_SUPPORT)
259 if (0 != (((unsigned int) algo3)
260 & ( ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)
261 | ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256))))
262 return MHD_SHA256_DIGEST_SIZE; /* The same as SHA512_256_DIGEST_SIZE */
263 else
264#elif defined(MHD_SHA256_SUPPORT)
265 if (0 != (((unsigned int) algo3)
266 & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256)))
268 else
269#elif defined(MHD_SHA512_256_SUPPORT)
270 if (0 != (((unsigned int) algo3)
271 & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA512_256)))
273 else
274#endif /* MHD_SHA512_256_SUPPORT */
275 (void) 0; /* Unsupported algorithm */
276
277 return 0; /* Wrong input or unsupported algorithm */
278}
279
280
295_MHD_EXTERN size_t
297{
298 return digest_get_hash_size (algo3);
299}
300
301
305union DigestCtx
306{
307#ifdef MHD_MD5_SUPPORT
308 struct Md5CtxWr md5_ctx;
309#endif /* MHD_MD5_SUPPORT */
310#ifdef MHD_SHA256_SUPPORT
311 struct Sha256CtxWr sha256_ctx;
312#endif /* MHD_SHA256_SUPPORT */
313#ifdef MHD_SHA512_256_SUPPORT
314 struct Sha512_256Ctx sha512_256_ctx;
315#endif /* MHD_SHA512_256_SUPPORT */
316};
317
321struct DigestAlgorithm
322{
327 union DigestCtx ctx;
328
332 enum MHD_DigestBaseAlgo algo;
333
337#ifdef _DEBUG
338 bool uninitialised;
339 bool algo_selected;
340 bool ready_for_hashing;
341 bool hashing;
342#endif /* _DEBUG */
343};
344
345
351_MHD_static_inline unsigned int
352digest_get_size (struct DigestAlgorithm *da)
353{
354 mhd_assert (! da->uninitialised);
355 mhd_assert (da->algo_selected);
356#ifdef MHD_MD5_SUPPORT
357 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
358 return MD5_DIGEST_SIZE;
359#endif /* MHD_MD5_SUPPORT */
360#ifdef MHD_SHA256_SUPPORT
361 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
362 return SHA256_DIGEST_SIZE;
363#endif /* MHD_SHA256_SUPPORT */
364#ifdef MHD_SHA512_256_SUPPORT
365 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
367#endif /* MHD_SHA512_256_SUPPORT */
368 mhd_assert (0); /* May not happen */
369 return 0;
370}
371
372
373#if defined(MHD_MD5_HAS_DEINIT) || defined(MHD_SHA256_HAS_DEINIT)
377#define MHD_DIGEST_HAS_DEINIT 1
378#endif /* MHD_MD5_HAS_DEINIT || MHD_SHA256_HAS_DEINIT */
379
380#ifdef MHD_DIGEST_HAS_DEINIT
388_MHD_static_inline void
389digest_setup_zero (struct DigestAlgorithm *da)
390{
391#ifdef _DEBUG
392 da->uninitialised = false;
393 da->algo_selected = false;
394 da->ready_for_hashing = false;
395 da->hashing = false;
396#endif /* _DEBUG */
398}
399
400
410_MHD_static_inline void
411digest_deinit (struct DigestAlgorithm *da)
412{
413 mhd_assert (! da->uninitialised);
414#ifdef MHD_MD5_HAS_DEINIT
415 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
416 MHD_MD5_deinit (&da->ctx.md5_ctx);
417 else
418#endif /* MHD_MD5_HAS_DEINIT */
419#ifdef MHD_SHA256_HAS_DEINIT
420 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
421 MHD_SHA256_deinit (&da->ctx.sha256_ctx);
422 else
423#endif /* MHD_SHA256_HAS_DEINIT */
424 (void) 0;
426}
427
428
429#else /* ! MHD_DIGEST_HAS_DEINIT */
430#define digest_setup_zero(da) (void) 0
431#define digest_deinit(da) (void) 0
432#endif /* ! MHD_DIGEST_HAS_DEINIT */
433
434
448_MHD_static_inline bool
449digest_init_one_time (struct DigestAlgorithm *da,
450 enum MHD_DigestBaseAlgo algo)
451{
452#ifdef _DEBUG
453 da->uninitialised = false;
454 da->algo_selected = false;
455 da->ready_for_hashing = false;
456 da->hashing = false;
457#endif /* _DEBUG */
458#ifdef MHD_MD5_SUPPORT
459 if (MHD_DIGEST_BASE_ALGO_MD5 == algo)
460 {
461 da->algo = MHD_DIGEST_BASE_ALGO_MD5;
462#ifdef _DEBUG
463 da->algo_selected = true;
464#endif
465 MHD_MD5_init_one_time (&da->ctx.md5_ctx);
466#ifdef _DEBUG
467 da->ready_for_hashing = true;
468#endif
469 return true;
470 }
471#endif /* MHD_MD5_SUPPORT */
472#ifdef MHD_SHA256_SUPPORT
473 if (MHD_DIGEST_BASE_ALGO_SHA256 == algo)
474 {
476#ifdef _DEBUG
477 da->algo_selected = true;
478#endif
479 MHD_SHA256_init_one_time (&da->ctx.sha256_ctx);
480#ifdef _DEBUG
481 da->ready_for_hashing = true;
482#endif
483 return true;
484 }
485#endif /* MHD_SHA256_SUPPORT */
486#ifdef MHD_SHA512_256_SUPPORT
488 {
490#ifdef _DEBUG
491 da->algo_selected = true;
492#endif
493 MHD_SHA512_256_init (&da->ctx.sha512_256_ctx);
494#ifdef _DEBUG
495 da->ready_for_hashing = true;
496#endif
497 return true;
498 }
499#endif /* MHD_SHA512_256_SUPPORT */
500
502 return false; /* Unsupported or bad algorithm */
503}
504
505
512_MHD_static_inline void
513digest_update (struct DigestAlgorithm *da,
514 const void *data,
515 size_t length)
516{
517 mhd_assert (! da->uninitialised);
518 mhd_assert (da->algo_selected);
519 mhd_assert (da->ready_for_hashing);
520#ifdef MHD_MD5_SUPPORT
521 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
522 MHD_MD5_update (&da->ctx.md5_ctx, (const uint8_t *) data, length);
523 else
524#endif /* MHD_MD5_SUPPORT */
525#ifdef MHD_SHA256_SUPPORT
526 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
527 MHD_SHA256_update (&da->ctx.sha256_ctx, (const uint8_t *) data, length);
528 else
529#endif /* MHD_SHA256_SUPPORT */
530#ifdef MHD_SHA512_256_SUPPORT
531 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
532 MHD_SHA512_256_update (&da->ctx.sha512_256_ctx,
533 (const uint8_t *) data, length);
534 else
535#endif /* MHD_SHA512_256_SUPPORT */
536 mhd_assert (0); /* May not happen */
537#ifdef _DEBUG
538 da->hashing = true;
539#endif
540}
541
542
548_MHD_static_inline void
549digest_update_str (struct DigestAlgorithm *da,
550 const char *str)
551{
552 const size_t str_len = strlen (str);
553 digest_update (da, (const uint8_t *) str, str_len);
554}
555
556
562_MHD_static_inline void
563digest_update_with_colon (struct DigestAlgorithm *da)
564{
565 static const uint8_t colon = (uint8_t) ':';
566 digest_update (da, &colon, 1);
567}
568
569
576_MHD_static_inline void
577digest_calc_hash (struct DigestAlgorithm *da, uint8_t *digest)
578{
579 mhd_assert (! da->uninitialised);
580 mhd_assert (da->algo_selected);
581 mhd_assert (da->ready_for_hashing);
582#ifdef MHD_MD5_SUPPORT
583 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
584 {
585#ifdef MHD_MD5_HAS_FINISH
586 MHD_MD5_finish (&da->ctx.md5_ctx, digest);
587#ifdef _DEBUG
588 da->ready_for_hashing = false;
589#endif /* _DEBUG */
590#else /* ! MHD_MD5_HAS_FINISH */
591 MHD_MD5_finish_reset (&da->ctx.md5_ctx, digest);
592#ifdef _DEBUG
593 da->ready_for_hashing = true;
594#endif /* _DEBUG */
595#endif /* ! MHD_MD5_HAS_FINISH */
596 }
597 else
598#endif /* MHD_MD5_SUPPORT */
599#ifdef MHD_SHA256_SUPPORT
600 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
601 {
602#ifdef MHD_SHA256_HAS_FINISH
603 MHD_SHA256_finish (&da->ctx.sha256_ctx, digest);
604#ifdef _DEBUG
605 da->ready_for_hashing = false;
606#endif /* _DEBUG */
607#else /* ! MHD_SHA256_HAS_FINISH */
608 MHD_SHA256_finish_reset (&da->ctx.sha256_ctx, digest);
609#ifdef _DEBUG
610 da->ready_for_hashing = true;
611#endif /* _DEBUG */
612#endif /* ! MHD_SHA256_HAS_FINISH */
613 }
614 else
615#endif /* MHD_SHA256_SUPPORT */
616#ifdef MHD_SHA512_256_SUPPORT
617 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
618 {
619 MHD_SHA512_256_finish (&da->ctx.sha512_256_ctx, digest);
620#ifdef _DEBUG
621 da->ready_for_hashing = false;
622#endif /* _DEBUG */
623 }
624 else
625#endif /* MHD_SHA512_256_SUPPORT */
626 mhd_assert (0); /* Should not happen */
627#ifdef _DEBUG
628 da->hashing = false;
629#endif /* _DEBUG */
630}
631
632
638_MHD_static_inline void
639digest_reset (struct DigestAlgorithm *da)
640{
641 mhd_assert (! da->uninitialised);
642 mhd_assert (da->algo_selected);
643 mhd_assert (! da->hashing);
644#ifdef MHD_MD5_SUPPORT
645 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
646 {
647#ifdef MHD_MD5_HAS_FINISH
648 mhd_assert (! da->ready_for_hashing);
649#else /* ! MHD_MD5_HAS_FINISH */
650 mhd_assert (da->ready_for_hashing);
651#endif /* ! MHD_MD5_HAS_FINISH */
652 MHD_MD5_reset (&da->ctx.md5_ctx);
653#ifdef _DEBUG
654 da->ready_for_hashing = true;
655#endif /* _DEBUG */
656 }
657 else
658#endif /* MHD_MD5_SUPPORT */
659#ifdef MHD_SHA256_SUPPORT
660 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
661 {
662#ifdef MHD_SHA256_HAS_FINISH
663 mhd_assert (! da->ready_for_hashing);
664#else /* ! MHD_SHA256_HAS_FINISH */
665 mhd_assert (da->ready_for_hashing);
666#endif /* ! MHD_SHA256_HAS_FINISH */
667 MHD_SHA256_reset (&da->ctx.sha256_ctx);
668#ifdef _DEBUG
669 da->ready_for_hashing = true;
670#endif /* _DEBUG */
671 }
672 else
673#endif /* MHD_SHA256_SUPPORT */
674#ifdef MHD_SHA512_256_SUPPORT
675 if (MHD_DIGEST_BASE_ALGO_SHA512_256 == da->algo)
676 {
677 mhd_assert (! da->ready_for_hashing);
678 MHD_SHA512_256_init (&da->ctx.sha512_256_ctx);
679#ifdef _DEBUG
680 da->ready_for_hashing = true;
681#endif
682 }
683 else
684#endif /* MHD_SHA512_256_SUPPORT */
685 {
686#ifdef _DEBUG
687 da->ready_for_hashing = false;
688#endif
689 mhd_assert (0); /* May not happen, bad algorithm */
690 }
691}
692
693
694#if defined(MHD_MD5_HAS_EXT_ERROR) || defined(MHD_SHA256_HAS_EXT_ERROR)
698#define MHD_DIGEST_HAS_EXT_ERROR 1
699#endif /* MHD_MD5_HAS_EXT_ERROR || MHD_SHA256_HAS_EXT_ERROR */
700
701#ifdef MHD_DIGEST_HAS_EXT_ERROR
711_MHD_static_inline bool
712digest_ext_error (struct DigestAlgorithm *da)
713{
714 mhd_assert (! da->uninitialised);
715 mhd_assert (da->algo_selected);
716#ifdef MHD_MD5_HAS_EXT_ERROR
717 if (MHD_DIGEST_BASE_ALGO_MD5 == da->algo)
718 return 0 != da->ctx.md5_ctx.ext_error;
719#endif /* MHD_MD5_HAS_EXT_ERROR */
720#ifdef MHD_SHA256_HAS_EXT_ERROR
721 if (MHD_DIGEST_BASE_ALGO_SHA256 == da->algo)
722 return 0 != da->ctx.sha256_ctx.ext_error;
723#endif /* MHD_MD5_HAS_EXT_ERROR */
724 return false;
725}
726
727
728#else /* ! MHD_DIGEST_HAS_EXT_ERROR */
729#define digest_ext_error(da) (false)
730#endif /* ! MHD_DIGEST_HAS_EXT_ERROR */
731
732
741static bool
742get_nonce_timestamp (const char *const nonce,
743 size_t noncelen,
744 uint64_t *const ptimestamp)
745{
746 if (0 == noncelen)
747 noncelen = strlen (nonce);
748
749 if (true
750#ifdef MHD_MD5_SUPPORT
751 && (NONCE_STD_LEN (MD5_DIGEST_SIZE) != noncelen)
752#endif /* MHD_MD5_SUPPORT */
753#if defined(MHD_SHA256_SUPPORT) || defined(MHD_SHA512_256_SUPPORT)
754 && (NONCE_STD_LEN (SHA256_SHA512_256_DIGEST_SIZE) != noncelen)
755#endif /* MHD_SHA256_SUPPORT */
756 )
757 return false;
758
760 MHD_strx_to_uint64_n_ (nonce + noncelen - TIMESTAMP_CHARS_LEN,
762 ptimestamp))
763 return false;
764 return true;
765}
766
767
769
777static uint32_t
778fast_simple_hash (const uint8_t *data,
779 size_t data_size)
780{
781 uint32_t hash;
782
783 if (0 != data_size)
784 {
785 size_t i;
786 hash = data[0];
787 for (i = 1; i < data_size; i++)
788 hash = _MHD_ROTL32 (hash, 7) ^ data[i];
789 }
790 else
791 hash = 0;
792
793 return hash;
794}
795
796
798
807static size_t
808get_nonce_nc_idx (size_t arr_size,
809 const char *nonce,
810 size_t noncelen)
811{
812 mhd_assert (0 != arr_size);
813 mhd_assert (0 != noncelen);
814 return fast_simple_hash ((const uint8_t *) nonce, noncelen) % arr_size;
815}
816
817
832static enum MHD_CheckNonceNC_
833check_nonce_nc (struct MHD_Connection *connection,
834 const char *nonce,
835 size_t noncelen,
836 uint64_t nonce_time,
837 uint64_t nc)
838{
839 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
840 struct MHD_NonceNc *nn;
841 uint32_t mod;
842 enum MHD_CheckNonceNC_ ret;
843
844 mhd_assert (0 != noncelen);
845 mhd_assert (0 != nc);
846 if (MAX_DIGEST_NONCE_LENGTH < noncelen)
847 return MHD_CHECK_NONCENC_WRONG; /* This should be impossible, but static analysis
848 tools have a hard time with it *and* this also
849 protects against unsafe modifications that may
850 happen in the future... */
851 mod = daemon->nonce_nc_size;
852 if (0 == mod)
853 return MHD_CHECK_NONCENC_STALE; /* no array! */
854 if (nc >= UINT32_MAX - 64)
855 return MHD_CHECK_NONCENC_STALE; /* Overflow, unrealistically high value */
856
857 nn = &daemon->nnc[get_nonce_nc_idx (mod, nonce, noncelen)];
858
859 MHD_mutex_lock_chk_ (&daemon->nnc_lock);
860
861 /* The nonces recorded in the array do not all have the same length: the
862 * daemon may serve several digest algorithms at the same time and the
863 * length of the nonce depends on the size of the digest (44 characters
864 * for MD5, 76 for SHA-256 and SHA-512/256). The slot selected by the
865 * hash of the client's nonce may therefore hold a nonce of a completely
866 * different length, which must not be inspected with the client's
867 * length. */
868 if (strlen (nn->nonce) != noncelen)
869 {
870 if (0 == nn->nonce[0])
871 { /* The slot was never used, while the client's nonce value should be
872 * recorded when it was generated by MHD */
874 }
875 else
876 { /* The slot holds a nonce generated for another digest algorithm.
877 * The client's nonce is not (or no longer) recorded. */
879 }
880 }
881 else if (0 != memcmp (nn->nonce, nonce, noncelen))
882 { /* The nonce in the slot does not match nonce from the client */
883 if (0 == nn->nonce[0])
884 { /* The slot was never used, while the client's nonce value should be
885 * recorded when it was generated by MHD */
887 }
888 else
889 {
890 uint64_t slot_ts;
891 if (! get_nonce_timestamp (nn->nonce, noncelen, &slot_ts))
892 {
893 mhd_assert (0); /* The value is the slot is wrong */
895 }
896 else
897 {
898 /* Unsigned value, will be large if nonce_time is less than slot_ts */
899 const uint64_t ts_diff = TRIM_TO_TIMESTAMP (nonce_time - slot_ts);
900 if ((REUSE_TIMEOUT * 1000) >= ts_diff)
901 {
902 /* The nonce from the client may not have been placed in the slot
903 * because another nonce in that slot has not yet expired. */
905 }
906 else if (TRIM_TO_TIMESTAMP (UINT64_MAX) / 2 >= ts_diff)
907 {
908 /* Too large value means that nonce_time is less than slot_ts.
909 * The nonce from the client may have been overwritten by the newer
910 * nonce. */
912 }
913 else
914 {
915 /* The nonce from the client should be generated after the nonce
916 * in the slot has been expired, the nonce must be recorded, but
917 * it's not. */
919 }
920 }
921 }
922 }
923 else if (nc > nn->nc)
924 {
925 /* 'nc' is larger, shift bitmask and bump limit */
926 const uint32_t jump_size = (uint32_t) nc - nn->nc;
927 if (64 > jump_size)
928 {
929 /* small jump, less than mask width */
930 nn->nmask <<= jump_size;
931 /* Set bit for the old 'nc' value */
932 nn->nmask |= (UINT64_C (1) << (jump_size - 1));
933 }
934 else if (64 == jump_size)
935 nn->nmask = (UINT64_C (1) << 63);
936 else
937 nn->nmask = 0; /* big jump, unset all bits in the mask */
938 nn->nc = (uint32_t) nc;
940 }
941 else if (nc < nn->nc)
942 {
943 /* Note that we use 64 here, as we do not store the
944 bit for 'nn->nc' itself in 'nn->nmask' */
945 if ( (nc + 64 >= nn->nc) &&
946 (0 == ((UINT64_C (1) << (nn->nc - nc - 1)) & nn->nmask)) )
947 {
948 /* Out-of-order nonce, but within 64-bit bitmask, set bit */
949 nn->nmask |= (UINT64_C (1) << (nn->nc - nc - 1));
951 }
952 else
953 /* 'nc' was already used or too old (more then 64 values ago) */
955 }
956 else /* if (nc == nn->nc) */
957 /* 'nc' was already used */
959
960 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
961
962 return ret;
963}
964
965
973_MHD_static_inline enum MHD_DigestAuthUsernameType
974get_rq_uname_type (const struct MHD_RqDAuth *params)
975{
976 if (NULL != params->username.value.str)
977 {
978 if (NULL == params->username_ext.value.str)
979 return params->userhash ?
982 else /* Both 'username' and 'username*' are used */
984 }
985 else if (NULL != params->username_ext.value.str)
986 {
987 if (! params->username_ext.quoted && ! params->userhash &&
988 (MHD_DAUTH_EXT_PARAM_MIN_LEN <= params->username_ext.value.len) )
990 else
992 }
993
995}
996
997
1005_MHD_static_inline size_t
1006get_rq_unames_size (const struct MHD_RqDAuth *params,
1007 enum MHD_DigestAuthUsernameType uname_type)
1008{
1009 size_t s;
1010
1011 mhd_assert (get_rq_uname_type (params) == uname_type);
1012 s = 0;
1013 if ((MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type) ||
1014 (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type) )
1015 {
1016 s += params->username.value.len + 1; /* Add one byte for zero-termination */
1017 if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
1018 s += (params->username.value.len + 1) / 2;
1019 }
1020 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
1021 s += params->username_ext.value.len
1022 - MHD_DAUTH_EXT_PARAM_MIN_LEN + 1; /* Add one byte for zero-termination */
1023 return s;
1024}
1025
1026
1035static size_t
1036get_rq_param_unquoted_copy_z (const struct MHD_RqDAuthParam *param, char *buf)
1037{
1038 size_t len;
1039 mhd_assert (NULL != param->value.str);
1040 if (! param->quoted)
1041 {
1042 memcpy (buf, param->value.str, param->value.len);
1043 buf [param->value.len] = 0;
1044 return param->value.len;
1045 }
1046
1047 len = MHD_str_unquote (param->value.str, param->value.len, buf);
1048 mhd_assert (0 != len);
1049 mhd_assert (len < param->value.len);
1050 buf[len] = 0;
1051 return len;
1052}
1053
1054
1065static ssize_t
1066get_rq_extended_uname_copy_z (const char *uname_ext, size_t uname_ext_len,
1067 char *buf, size_t buf_size)
1068{
1069 size_t r;
1070 size_t w;
1071 if ((size_t) SSIZE_MAX < uname_ext_len)
1072 return -1; /* Too long input string */
1073
1074 if (MHD_DAUTH_EXT_PARAM_MIN_LEN > uname_ext_len)
1075 return -1; /* Required prefix is missing */
1076
1080 return -1; /* Only UTF-8 is supported, as it is implied by RFC 7616 */
1081
1083 /* Skip language tag */
1084 while (r < uname_ext_len && '\'' != uname_ext[r])
1085 {
1086 const char chr = uname_ext[r];
1087 if ((' ' == chr) || ('\t' == chr) || ('\"' == chr) || (',' == chr) ||
1088 (';' == chr) )
1089 return -1; /* Wrong char in language tag */
1090 r++;
1091 }
1092 if (r >= uname_ext_len)
1093 return -1; /* The end of the language tag was not found */
1094 r++; /* Advance to the next char */
1095
1096 w = MHD_str_pct_decode_strict_n_ (uname_ext + r, uname_ext_len - r,
1097 buf, buf_size);
1098 if ((0 == w) && (0 != uname_ext_len - r))
1099 return -1; /* Broken percent encoding */
1100 buf[w] = 0; /* Zero terminate the result */
1101 mhd_assert (SSIZE_MAX > w);
1102 return (ssize_t) w;
1103}
1104
1105
1115static size_t
1116get_rq_uname (const struct MHD_RqDAuth *params,
1117 enum MHD_DigestAuthUsernameType uname_type,
1118 struct MHD_DigestAuthUsernameInfo *uname_info,
1119 uint8_t *buf,
1120 size_t buf_size)
1121{
1122 size_t buf_used;
1123
1124 buf_used = 0;
1125 mhd_assert (get_rq_uname_type (params) == uname_type);
1128
1129 uname_info->username = NULL;
1130 uname_info->username_len = 0;
1131 uname_info->userhash_hex = NULL;
1132 uname_info->userhash_hex_len = 0;
1133 uname_info->userhash_bin = NULL;
1134
1135 if (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD == uname_type)
1136 {
1137 uname_info->username = (char *) (buf + buf_used);
1138 uname_info->username_len =
1139 get_rq_param_unquoted_copy_z (&params->username,
1140 uname_info->username);
1141 buf_used += uname_info->username_len + 1;
1143 }
1144 else if (MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH == uname_type)
1145 {
1146 size_t res;
1147
1148 uname_info->userhash_hex = (char *) (buf + buf_used);
1149 uname_info->userhash_hex_len =
1150 get_rq_param_unquoted_copy_z (&params->username,
1151 uname_info->userhash_hex);
1152 buf_used += uname_info->userhash_hex_len + 1;
1153 uname_info->userhash_bin = (uint8_t *) (buf + buf_used);
1154 res = MHD_hex_to_bin (uname_info->userhash_hex,
1155 uname_info->userhash_hex_len,
1156 uname_info->userhash_bin);
1157 if (res != uname_info->userhash_hex_len / 2)
1158 {
1159 uname_info->userhash_bin = NULL;
1161 }
1162 else
1163 {
1164 /* Avoid pointers outside allocated region when the size is zero */
1165 if (0 == res)
1166 uname_info->userhash_bin = (uint8_t *) uname_info->username;
1168 buf_used += res;
1169 }
1170 }
1171 else if (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED == uname_type)
1172 {
1173 ssize_t res;
1174 res = get_rq_extended_uname_copy_z (params->username_ext.value.str,
1175 params->username_ext.value.len,
1176 (char *) (buf + buf_used),
1177 buf_size - buf_used);
1178 if (0 > res)
1180 else
1181 {
1182 uname_info->username = (char *) (buf + buf_used);
1183 uname_info->username_len = (size_t) res;
1185 buf_used += uname_info->username_len + 1;
1186 }
1187 }
1188 else
1189 {
1190 mhd_assert (0);
1192 }
1193 mhd_assert (buf_size >= buf_used);
1194 return buf_used;
1195}
1196
1197
1209
1210
1217static enum MHD_GetRqNCResult
1218get_rq_nc (const struct MHD_RqDAuth *params,
1219 uint32_t *nc)
1220{
1221 const struct MHD_RqDAuthParam *const nc_param =
1222 &params->nc;
1223 char unq[16];
1224 const char *val;
1225 size_t val_len;
1226 size_t res;
1227 uint64_t nc_val;
1228
1229 if (NULL == nc_param->value.str)
1230 return MHD_GET_RQ_NC_NONE;
1231
1232 if (0 == nc_param->value.len)
1233 return MHD_GET_RQ_NC_BROKEN;
1234
1235 if (! nc_param->quoted)
1236 {
1237 val = nc_param->value.str;
1238 val_len = nc_param->value.len;
1239 }
1240 else
1241 {
1242 /* Actually no backslashes must be used in 'nc' */
1243 if (sizeof(unq) < params->nc.value.len)
1245 val_len = MHD_str_unquote (nc_param->value.str, nc_param->value.len, unq);
1246 if (0 == val_len)
1247 return MHD_GET_RQ_NC_BROKEN;
1248 val = unq;
1249 }
1250
1251 res = MHD_strx_to_uint64_n_ (val, val_len, &nc_val);
1252 if (0 == res)
1253 {
1254 const char f = val[0];
1255 if ( (('9' >= f) && ('0' <= f)) ||
1256 (('F' >= f) && ('A' <= f)) ||
1257 (('a' <= f) && ('f' >= f)) )
1259 else
1260 return MHD_GET_RQ_NC_BROKEN;
1261 }
1262 if (val_len != res)
1263 return MHD_GET_RQ_NC_BROKEN;
1264 if (UINT32_MAX < nc_val)
1266 *nc = (uint32_t) nc_val;
1267 return MHD_GET_RQ_NC_VALID;
1268}
1269
1270
1283{
1284 const struct MHD_RqDAuth *params;
1285 struct MHD_DigestAuthInfo *info;
1287 size_t unif_buf_size;
1288 uint8_t *unif_buf_ptr;
1289 size_t unif_buf_used;
1290 enum MHD_GetRqNCResult nc_res;
1291
1292 params = MHD_get_rq_dauth_params_ (connection);
1293 if (NULL == params)
1294 return NULL;
1295
1296 unif_buf_size = 0;
1297
1298 uname_type = get_rq_uname_type (params);
1299
1300 unif_buf_size += get_rq_unames_size (params, uname_type);
1301
1302 if (NULL != params->opaque.value.str)
1303 unif_buf_size += params->opaque.value.len + 1; /* Add one for zero-termination */
1304 if (NULL != params->realm.value.str)
1305 unif_buf_size += params->realm.value.len + 1; /* Add one for zero-termination */
1306 info = (struct MHD_DigestAuthInfo *)
1307 MHD_calloc_ (1, (sizeof(struct MHD_DigestAuthInfo)) + unif_buf_size);
1308 if (NULL == info)
1309 return NULL;
1310 unif_buf_ptr = (uint8_t *) (info + 1);
1311 unif_buf_used = 0;
1312
1313 info->algo3 = params->algo3;
1314
1317 unif_buf_used +=
1318 get_rq_uname (params, uname_type,
1319 (struct MHD_DigestAuthUsernameInfo *) info,
1320 unif_buf_ptr + unif_buf_used,
1321 unif_buf_size - unif_buf_used);
1322 else
1323 info->uname_type = uname_type;
1324
1325 if (NULL != params->opaque.value.str)
1326 {
1327 info->opaque = (char *) (unif_buf_ptr + unif_buf_used);
1328 info->opaque_len = get_rq_param_unquoted_copy_z (&params->opaque,
1329 info->opaque);
1330 unif_buf_used += info->opaque_len + 1;
1331 }
1332 if (NULL != params->realm.value.str)
1333 {
1334 info->realm = (char *) (unif_buf_ptr + unif_buf_used);
1335 info->realm_len = get_rq_param_unquoted_copy_z (&params->realm,
1336 info->realm);
1337 unif_buf_used += info->realm_len + 1;
1338 }
1339
1340 mhd_assert (unif_buf_size >= unif_buf_used);
1341
1342 info->qop = params->qop;
1343
1344 if (NULL != params->cnonce.value.str)
1345 info->cnonce_len = params->cnonce.value.len;
1346 else
1347 info->cnonce_len = 0;
1348
1349 nc_res = get_rq_nc (params, &info->nc);
1350 if (MHD_GET_RQ_NC_VALID != nc_res)
1352
1353 return info;
1354}
1355
1356
1373{
1374 const struct MHD_RqDAuth *params;
1375 struct MHD_DigestAuthUsernameInfo *uname_info;
1377 size_t unif_buf_size;
1378 uint8_t *unif_buf_ptr;
1379 size_t unif_buf_used;
1380
1381 params = MHD_get_rq_dauth_params_ (connection);
1382 if (NULL == params)
1383 return NULL;
1384
1385 uname_type = get_rq_uname_type (params);
1388 return NULL;
1389
1390 unif_buf_size = get_rq_unames_size (params, uname_type);
1391
1392 uname_info = (struct MHD_DigestAuthUsernameInfo *)
1393 MHD_calloc_ (1, (sizeof(struct MHD_DigestAuthUsernameInfo))
1394 + unif_buf_size);
1395 if (NULL == uname_info)
1396 return NULL;
1397 unif_buf_ptr = (uint8_t *) (uname_info + 1);
1398 unif_buf_used = get_rq_uname (params, uname_type, uname_info, unif_buf_ptr,
1399 unif_buf_size);
1400 mhd_assert (unif_buf_size >= unif_buf_used);
1401 (void) unif_buf_used; /* Mute compiler warning on non-debug builds */
1403
1405 {
1406 free (uname_info);
1407 return NULL;
1408 }
1409 mhd_assert (uname_type == uname_info->uname_type);
1410 uname_info->algo3 = params->algo3;
1411
1412 return uname_info;
1413}
1414
1415
1431_MHD_EXTERN char *
1433{
1434 const struct MHD_RqDAuth *params;
1435 char *username;
1436 size_t buf_size;
1437 enum MHD_DigestAuthUsernameType uname_type;
1438
1439 params = MHD_get_rq_dauth_params_ (connection);
1440 if (NULL == params)
1441 return NULL;
1442
1443 uname_type = get_rq_uname_type (params);
1444
1445 if ( (MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD != uname_type) &&
1446 (MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED != uname_type) )
1447 return NULL;
1448
1449 buf_size = get_rq_unames_size (params, uname_type);
1450
1451 mhd_assert (0 != buf_size);
1452
1453 username = (char *) MHD_calloc_ (1, buf_size);
1454 if (NULL == username)
1455 return NULL;
1456
1457 if (1)
1458 {
1459 struct MHD_DigestAuthUsernameInfo uname_strct;
1460 size_t used;
1461
1462 memset (&uname_strct, 0, sizeof(uname_strct));
1463
1464 used = get_rq_uname (params, uname_type, &uname_strct,
1465 (uint8_t *) username, buf_size);
1466 if (uname_type != uname_strct.uname_type)
1467 { /* Broken encoding for extended notation */
1468 free (username);
1469 return NULL;
1470 }
1471 (void) used; /* Mute compiler warning for non-debug builds */
1472 mhd_assert (buf_size >= used);
1473 }
1474
1475 return username;
1476}
1477
1478
1502static void
1503calculate_nonce (uint64_t nonce_time,
1504 enum MHD_HTTP_Method mthd_e,
1505 const char *method,
1506 const char *rnd,
1507 size_t rnd_size,
1508 const struct sockaddr_storage *saddr,
1509 size_t saddr_size,
1510 const char *uri,
1511 size_t uri_len,
1512 const struct MHD_HTTP_Req_Header *first_header,
1513 const char *realm,
1514 size_t realm_len,
1515 unsigned int bind_options,
1516 struct DigestAlgorithm *da,
1517 char *nonce)
1518{
1519 mhd_assert (! da->hashing);
1520 if (1)
1521 {
1522 /* Add the timestamp to the hash calculation */
1523 uint8_t timestamp[TIMESTAMP_BIN_SIZE];
1524 /* If the nonce_time is milliseconds, then the same 48 bit value will repeat
1525 * every 8 919 years, which is more than enough to mitigate a replay attack */
1526#if TIMESTAMP_BIN_SIZE != 6
1527#error The code needs to be updated here
1528#endif
1529 timestamp[0] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 0)));
1530 timestamp[1] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 1)));
1531 timestamp[2] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 2)));
1532 timestamp[3] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 3)));
1533 timestamp[4] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 4)));
1534 timestamp[5] = (uint8_t) (nonce_time >> (8 * (TIMESTAMP_BIN_SIZE - 1 - 5)));
1535 MHD_bin_to_hex (timestamp,
1536 sizeof (timestamp),
1537 nonce + digest_get_size (da) * 2);
1538 digest_update (da,
1539 timestamp,
1540 sizeof (timestamp));
1541 }
1542 if (rnd_size > 0)
1543 {
1544 /* Add the unique random value to the hash calculation */
1546 digest_update (da,
1547 rnd,
1548 rnd_size);
1549 }
1550 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) &&
1551 (0 != saddr_size) )
1552 {
1553 /* Add full client address including source port to make unique nonces
1554 * for requests received exactly at the same time */
1556 digest_update (da,
1557 saddr,
1558 saddr_size);
1559 }
1560 if ( (0 != (bind_options & MHD_DAUTH_BIND_NONCE_CLIENT_IP)) &&
1561 (0 != saddr_size) )
1562 {
1563 /* Add the client's IP address to the hash calculation */
1565 if (AF_INET == saddr->ss_family)
1566 digest_update (da,
1567 &((const struct sockaddr_in *) saddr)->sin_addr,
1568 sizeof(((const struct sockaddr_in *) saddr)->sin_addr));
1569#ifdef HAVE_INET6
1570 else if (AF_INET6 == saddr->ss_family)
1571 digest_update (da,
1572 &((const struct sockaddr_in6 *) saddr)->sin6_addr,
1573 sizeof(((const struct sockaddr_in6 *) saddr)->sin6_addr));
1574#endif /* HAVE_INET6 */
1575 }
1576 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) ||
1577 (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI)))
1578 {
1579 /* Add the request method to the hash calculation */
1581 if (MHD_HTTP_MTHD_OTHER != mthd_e)
1582 {
1583 uint8_t mthd_for_hash;
1584 if (MHD_HTTP_MTHD_HEAD != mthd_e)
1585 mthd_for_hash = (uint8_t) mthd_e;
1586 else /* Treat HEAD method in the same way as GET method */
1587 mthd_for_hash = (uint8_t) MHD_HTTP_MTHD_GET;
1588 digest_update (da,
1589 &mthd_for_hash,
1590 sizeof(mthd_for_hash));
1591 }
1592 else
1593 digest_update_str (da, method);
1594 }
1595
1596 if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI))
1597 {
1598 /* Add the request URI to the hash calculation */
1600
1601 digest_update (da,
1602 uri,
1603 uri_len);
1604 }
1605 if (0 != (bind_options & MHD_DAUTH_BIND_NONCE_URI_PARAMS))
1606 {
1607 /* Add the request URI parameters to the hash calculation */
1608 const struct MHD_HTTP_Req_Header *h;
1609
1611 for (h = first_header; NULL != h; h = h->next)
1612 {
1613 if (MHD_GET_ARGUMENT_KIND != h->kind)
1614 continue;
1615 digest_update (da, "\0", 2);
1616 if (0 != h->header_size)
1617 digest_update (da, h->header, h->header_size);
1618 digest_update (da, "", 1);
1619 if (0 != h->value_size)
1620 digest_update (da, h->value, h->value_size);
1621 }
1622 }
1623 if ( (MHD_DAUTH_BIND_NONCE_NONE == bind_options) ||
1624 (0 != (bind_options & MHD_DAUTH_BIND_NONCE_REALM)))
1625 {
1626 /* Add the realm to the hash calculation */
1628 digest_update (da,
1629 realm,
1630 realm_len);
1631 }
1632 if (1)
1633 {
1634 uint8_t hash[MAX_DIGEST];
1635 digest_calc_hash (da, hash);
1636 MHD_bin_to_hex (hash,
1637 digest_get_size (da),
1638 nonce);
1639 }
1640}
1641
1642
1658static bool
1659is_slot_available (const struct MHD_NonceNc *const nn,
1660 const uint64_t now,
1661 const char *const new_nonce,
1662 size_t new_nonce_len)
1663{
1664 uint64_t timestamp;
1665 bool timestamp_valid;
1666 mhd_assert (new_nonce_len <= NONCE_STD_LEN (MAX_DIGEST));
1668 if (0 == nn->nonce[0])
1669 return true; /* The slot is empty */
1670
1671 if (0 == memcmp (nn->nonce, new_nonce, new_nonce_len))
1672 {
1673 /* The slot has the same nonce already. This nonce cannot be registered
1674 * again as it would just clear 'nc' usage history. */
1675 return false;
1676 }
1677
1678 if (0 != nn->nc)
1679 return true; /* Client already used the nonce in this slot at least
1680 one time, re-use the slot */
1681
1682 /* The nonce must be zero-terminated */
1683 mhd_assert (0 == nn->nonce[sizeof(nn->nonce) - 1]);
1684 if (0 != nn->nonce[sizeof(nn->nonce) - 1])
1685 return true; /* Wrong nonce format in the slot */
1686
1687 timestamp_valid = get_nonce_timestamp (nn->nonce, 0, &timestamp);
1688 mhd_assert (timestamp_valid);
1689 if (! timestamp_valid)
1690 return true; /* Invalid timestamp in nonce-nc, should not be possible */
1691
1692 if ((REUSE_TIMEOUT * 1000) < TRIM_TO_TIMESTAMP (now - timestamp))
1693 return true;
1694
1695 return false;
1696}
1697
1698
1714static bool
1715calculate_add_nonce (struct MHD_Connection *const connection,
1716 uint64_t timestamp,
1717 const char *realm,
1718 size_t realm_len,
1719 struct DigestAlgorithm *da,
1720 char *nonce)
1721{
1722 struct MHD_Daemon *const daemon = MHD_get_master (connection->daemon);
1723 struct MHD_NonceNc *nn;
1724 const size_t nonce_size = NONCE_STD_LEN (digest_get_size (da));
1725 bool ret;
1726
1727 mhd_assert (! da->hashing);
1728 mhd_assert (MAX_DIGEST_NONCE_LENGTH >= nonce_size);
1729 mhd_assert (0 != nonce_size);
1730
1731 calculate_nonce (timestamp,
1732 connection->rq.http_mthd,
1733 connection->rq.method,
1734 daemon->digest_auth_random,
1735 daemon->digest_auth_rand_size,
1736 connection->addr,
1737 (size_t) connection->addr_len,
1738 connection->rq.url,
1739 connection->rq.url_len,
1740 connection->rq.headers_received,
1741 realm,
1742 realm_len,
1743 daemon->dauth_bind_type,
1744 da,
1745 nonce);
1746
1747#ifdef MHD_DIGEST_HAS_EXT_ERROR
1748 if (digest_ext_error (da))
1749 return false;
1750#endif /* MHD_DIGEST_HAS_EXT_ERROR */
1751
1752 if (0 == daemon->nonce_nc_size)
1753 return false;
1754
1755 /* Sanity check for values */
1757
1758 nn = daemon->nnc + get_nonce_nc_idx (daemon->nonce_nc_size,
1759 nonce,
1760 nonce_size);
1761
1762 MHD_mutex_lock_chk_ (&daemon->nnc_lock);
1763 if (is_slot_available (nn, timestamp, nonce, nonce_size))
1764 {
1765 memcpy (nn->nonce,
1766 nonce,
1767 nonce_size);
1768 nn->nonce[nonce_size] = 0; /* With terminating zero */
1769 nn->nc = 0;
1770 nn->nmask = 0;
1771 ret = true;
1772 }
1773 else
1774 ret = false;
1775 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
1776
1777 return ret;
1778}
1779
1780
1782
1794static bool
1796 const char *realm,
1797 struct DigestAlgorithm *da,
1798 char *nonce)
1799{
1800 const uint64_t timestamp1 = MHD_monotonic_msec_counter ();
1801 const size_t realm_len = strlen (realm);
1802 mhd_assert (! da->hashing);
1803
1804#ifdef HAVE_MESSAGES
1805 if (0 == MHD_get_master (connection->daemon)->digest_auth_rand_size)
1806 MHD_DLOG (connection->daemon,
1807 _ ("Random value was not initialised by " \
1808 "MHD_OPTION_DIGEST_AUTH_RANDOM or " \
1809 "MHD_OPTION_DIGEST_AUTH_RANDOM_COPY, generated nonces " \
1810 "are predictable.\n"));
1811#endif
1812
1813 if (! calculate_add_nonce (connection, timestamp1, realm, realm_len, da,
1814 nonce))
1815 {
1816 /* Either:
1817 * 1. The same nonce was already generated. If it will be used then one
1818 * of the clients will fail (as no initial 'nc' value could be given to
1819 * the client, the second client which will use 'nc=00000001' will fail).
1820 * 2. Another nonce uses the same slot, and this nonce never has been
1821 * used by the client and this nonce is still fresh enough.
1822 */
1823 const size_t digest_size = digest_get_size (da);
1824 char nonce2[NONCE_STD_LEN (MAX_DIGEST) + 1];
1825 uint64_t timestamp2;
1826#ifdef MHD_DIGEST_HAS_EXT_ERROR
1827 if (digest_ext_error (da))
1828 return false; /* No need to re-try */
1829#endif /* MHD_DIGEST_HAS_EXT_ERROR */
1830 if (0 == MHD_get_master (connection->daemon)->nonce_nc_size)
1831 return false; /* No need to re-try */
1832
1833 timestamp2 = MHD_monotonic_msec_counter ();
1834 if (timestamp1 == timestamp2)
1835 {
1836 /* The timestamps are equal, need to generate some arbitrary
1837 * difference for nonce. */
1838 /* As the number is needed only to differentiate clients, weak
1839 * pseudo-random generators could be used. Seeding is not needed. */
1840 uint64_t base1;
1841 uint32_t base2;
1842 uint16_t base3;
1843 uint8_t base4;
1844#ifdef HAVE_RANDOM
1845 base1 = ((uint64_t) random ()) ^ UINT64_C (0x54a5acff5be47e63);
1846 base4 = 0xb8;
1847#elif defined(HAVE_RAND)
1848 base1 = ((uint64_t) rand ()) ^ UINT64_C (0xc4bcf553b12f3965);
1849 base4 = 0x92;
1850#else
1851 /* Monotonic msec counter alone does not really help here as it is already
1852 known that this value is not unique. */
1853 base1 = ((uint64_t) (uintptr_t) nonce2) ^ UINT64_C (0xf2e1b21bc6c92655);
1854 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
1855 base2 = _MHD_ROTR32 (base2, 4);
1856 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
1857 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
1858 base1 = ((uint64_t) MHD_monotonic_msec_counter ())
1859 ^ UINT64_C (0xccab93f72cf5b15);
1860#endif
1861 base2 = ((uint32_t) (base1 >> 32)) ^ ((uint32_t) base1);
1862 base2 = _MHD_ROTL32 (base2, (((base4 >> 4) ^ base4) % 32));
1863 base3 = ((uint16_t) (base2 >> 16)) ^ ((uint16_t) base2);
1864 base4 = ((uint8_t) (base3 >> 8)) ^ ((uint8_t) base3);
1865 /* Use up to 127 ms difference */
1866 timestamp2 -= (base4 & DAUTH_JUMPBACK_MAX);
1867 if (timestamp1 == timestamp2)
1868 timestamp2 -= 2; /* Fallback value */
1869 }
1870 digest_reset (da);
1871 if (! calculate_add_nonce (connection, timestamp2, realm, realm_len, da,
1872 nonce2))
1873 {
1874 /* No free slot has been found. Re-tries are expensive, just use
1875 * the generated nonce. As it is not stored in nonce-nc map array,
1876 * the next request of the client will be recognized as valid, but 'stale'
1877 * so client should re-try automatically. */
1878 return false;
1879 }
1880 memcpy (nonce, nonce2, NONCE_STD_LEN (digest_size));
1881 }
1882 return true;
1883}
1884
1885
1887
1904_MHD_static_inline void
1905calc_userdigest (struct DigestAlgorithm *da,
1906 const char *username, const size_t username_len,
1907 const char *realm, const size_t realm_len,
1908 const char *password,
1909 uint8_t *ha1_bin)
1910{
1911 mhd_assert (! da->hashing);
1912 digest_update (da, username, username_len);
1914 digest_update (da, realm, realm_len);
1916 digest_update_str (da, password);
1917 digest_calc_hash (da, ha1_bin);
1918}
1919
1920
1956 const char *username,
1957 const char *realm,
1958 const char *password,
1959 void *userdigest_bin,
1960 size_t bin_buf_size)
1961{
1962 struct DigestAlgorithm da;
1963 enum MHD_Result ret;
1964 if (! digest_init_one_time (&da, get_base_digest_algo (algo3)))
1965 return MHD_NO;
1966
1967 if (digest_get_size (&da) > bin_buf_size)
1968 ret = MHD_NO;
1969 else
1970 {
1971 calc_userdigest (&da,
1972 username,
1973 strlen (username),
1974 realm,
1975 strlen (realm),
1976 password,
1977 userdigest_bin);
1978 ret = MHD_YES;
1979
1980#ifdef MHD_DIGEST_HAS_EXT_ERROR
1981 if (digest_ext_error (&da))
1982 ret = MHD_NO;
1983#endif /* MHD_DIGEST_HAS_EXT_ERROR */
1984 }
1985 digest_deinit (&da);
1986
1987 return ret;
1988}
1989
1990
2004_MHD_static_inline void
2005calc_userhash (struct DigestAlgorithm *da,
2006 const char *username, const size_t username_len,
2007 const char *realm, const size_t realm_len,
2008 uint8_t *digest_bin)
2009{
2010 mhd_assert (NULL != username);
2011 mhd_assert (! da->hashing);
2012 digest_update (da, username, username_len);
2014 digest_update (da, realm, realm_len);
2015 digest_calc_hash (da, digest_bin);
2016}
2017
2018
2061 const char *username,
2062 const char *realm,
2063 void *userhash_bin,
2064 size_t bin_buf_size)
2065{
2066 struct DigestAlgorithm da;
2067 enum MHD_Result ret;
2068
2069 if (! digest_init_one_time (&da, get_base_digest_algo (algo3)))
2070 return MHD_NO;
2071 if (digest_get_size (&da) > bin_buf_size)
2072 ret = MHD_NO;
2073 else
2074 {
2075 calc_userhash (&da,
2076 username,
2077 strlen (username),
2078 realm,
2079 strlen (realm),
2080 userhash_bin);
2081 ret = MHD_YES;
2082
2083#ifdef MHD_DIGEST_HAS_EXT_ERROR
2084 if (digest_ext_error (&da))
2085 ret = MHD_NO;
2086#endif /* MHD_DIGEST_HAS_EXT_ERROR */
2087 }
2088 digest_deinit (&da);
2089
2090 return ret;
2091}
2092
2093
2136 const char *username,
2137 const char *realm,
2138 char *userhash_hex,
2139 size_t hex_buf_size)
2140{
2141 uint8_t userhash_bin[MAX_DIGEST];
2142 size_t digest_size;
2143
2144 digest_size = digest_get_hash_size (algo3);
2145 if (digest_size * 2 + 1 > hex_buf_size)
2146 return MHD_NO;
2147 if (MHD_NO == MHD_digest_auth_calc_userhash (algo3, username, realm,
2148 userhash_bin, MAX_DIGEST))
2149 return MHD_NO;
2150
2151 MHD_bin_to_hex_z (userhash_bin, digest_size, userhash_hex);
2152 return MHD_YES;
2153}
2154
2155
2156struct test_header_param
2157{
2158 struct MHD_Connection *connection;
2159 size_t num_headers;
2160};
2161
2175static enum MHD_Result
2176test_header (void *cls,
2177 const char *key,
2178 size_t key_size,
2179 const char *value,
2180 size_t value_size,
2181 enum MHD_ValueKind kind)
2182{
2183 struct test_header_param *const param = (struct test_header_param *) cls;
2184 struct MHD_Connection *connection = param->connection;
2185 struct MHD_HTTP_Req_Header *pos;
2186 size_t i;
2187
2188 param->num_headers++;
2189 i = 0;
2190 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
2191 {
2192 if (kind != pos->kind)
2193 continue;
2194 if (++i == param->num_headers)
2195 {
2196 if (key_size != pos->header_size)
2197 return MHD_NO;
2198 if (value_size != pos->value_size)
2199 return MHD_NO;
2200 if (0 != key_size)
2201 {
2202 mhd_assert (NULL != key);
2203 mhd_assert (NULL != pos->header);
2204 if (0 != memcmp (key,
2205 pos->header,
2206 key_size))
2207 return MHD_NO;
2208 }
2209 if (0 != value_size)
2210 {
2211 mhd_assert (NULL != value);
2212 mhd_assert (NULL != pos->value);
2213 if (0 != memcmp (value,
2214 pos->value,
2215 value_size))
2216 return MHD_NO;
2217 }
2218 return MHD_YES;
2219 }
2220 }
2221 return MHD_NO;
2222}
2223
2224
2236static bool
2238 char *args)
2239{
2240 struct MHD_HTTP_Req_Header *pos;
2241 enum MHD_Result ret;
2242 struct test_header_param param;
2243
2244 param.connection = connection;
2245 param.num_headers = 0;
2246 ret = MHD_parse_arguments_ (connection,
2248 args,
2249 &test_header,
2250 &param);
2251 if (MHD_NO == ret)
2252 {
2253 return false;
2254 }
2255 /* also check that the number of headers matches */
2256 for (pos = connection->rq.headers_received; NULL != pos; pos = pos->next)
2257 {
2258 if (MHD_GET_ARGUMENT_KIND != pos->kind)
2259 continue;
2260 param.num_headers--;
2261 }
2262 if (0 != param.num_headers)
2263 {
2264 /* argument count mismatch */
2265 return false;
2266 }
2267 return true;
2268}
2269
2270
2283static bool
2284check_uri_match (struct MHD_Connection *connection, char *uri, size_t uri_len)
2285{
2286 char *qmark;
2287 char *args;
2288 struct MHD_Daemon *const daemon = connection->daemon;
2289
2290 uri[uri_len] = 0;
2291 qmark = memchr (uri,
2292 '?',
2293 uri_len);
2294 if (NULL != qmark)
2295 *qmark = '\0';
2296
2297 /* Need to unescape URI before comparing with connection->url */
2298 uri_len = daemon->unescape_callback (daemon->unescape_callback_cls,
2299 connection,
2300 uri);
2301 if ((uri_len != connection->rq.url_len) ||
2302 (0 != memcmp (uri, connection->rq.url, uri_len)))
2303 {
2304#ifdef HAVE_MESSAGES
2305 MHD_DLOG (daemon,
2306 _ ("Authentication failed, URI does not match.\n"));
2307#endif
2308 return false;
2309 }
2310
2311 args = (NULL != qmark) ? (qmark + 1) : uri + uri_len;
2312
2313 if (! check_argument_match (connection,
2314 args) )
2315 {
2316#ifdef HAVE_MESSAGES
2317 MHD_DLOG (daemon,
2318 _ ("Authentication failed, arguments do not match.\n"));
2319#endif
2320 return false;
2321 }
2322 return true;
2323}
2324
2325
2329#define _MHD_STATIC_UNQ_BUFFER_SIZE 128
2330
2331
2341static char *
2343 char **ptmp2,
2344 size_t *ptmp2_size,
2345 size_t required_size)
2346{
2347 mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2));
2348 mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size));
2349 mhd_assert ((0 == *ptmp2_size) || \
2350 (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size));
2351
2352 if (required_size <= _MHD_STATIC_UNQ_BUFFER_SIZE)
2353 return tmp1;
2354
2355 if (required_size <= *ptmp2_size)
2356 return *ptmp2;
2357
2358 if (required_size > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE)
2359 return NULL;
2360 if (NULL != *ptmp2)
2361 free (*ptmp2);
2362 *ptmp2 = (char *) malloc (required_size);
2363 if (NULL == *ptmp2)
2364 *ptmp2_size = 0;
2365 else
2366 *ptmp2_size = required_size;
2367 return *ptmp2;
2368}
2369
2370
2380
2390static enum _MHD_GetUnqResult
2391get_unquoted_param (const struct MHD_RqDAuthParam *param,
2392 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE],
2393 char **ptmp2,
2394 size_t *ptmp2_size,
2395 struct _MHD_str_w_len *unquoted)
2396{
2397 char *str;
2398 size_t len;
2399 mhd_assert (NULL != param->value.str);
2400 mhd_assert (0 != param->value.len);
2401
2402 if (! param->quoted)
2403 {
2404 unquoted->str = param->value.str;
2405 unquoted->len = param->value.len;
2406 return _MHD_UNQ_OK;
2407 }
2408 /* The value is present and is quoted, needs to be copied and unquoted */
2409 str = get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len);
2410 if (NULL == str)
2411 return (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ?
2413
2414 len = MHD_str_unquote (param->value.str, param->value.len, str);
2415 unquoted->str = str;
2416 unquoted->len = len;
2417 mhd_assert (0 != unquoted->len);
2418 mhd_assert (unquoted->len < param->value.len);
2419 return _MHD_UNQ_OK;
2420}
2421
2422
2433static enum _MHD_GetUnqResult
2434get_unquoted_param_copy (const struct MHD_RqDAuthParam *param,
2435 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE],
2436 char **ptmp2,
2437 size_t *ptmp2_size,
2438 struct _MHD_mstr_w_len *unquoted)
2439{
2440 mhd_assert (NULL != param->value.str);
2441 mhd_assert (0 != param->value.len);
2442
2443 /* The value is present and is quoted, needs to be copied and unquoted */
2444 /* Allocate buffer with one more additional byte for zero-termination */
2445 unquoted->str =
2446 get_buffer_for_size (tmp1, ptmp2, ptmp2_size, param->value.len + 1);
2447
2448 if (NULL == unquoted->str)
2449 return (param->value.len + 1 > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) ?
2451
2452 if (! param->quoted)
2453 {
2454 memcpy (unquoted->str, param->value.str, param->value.len);
2455 unquoted->len = param->value.len;
2456 return _MHD_UNQ_OK;
2457 }
2458
2459 unquoted->len =
2460 MHD_str_unquote (param->value.str, param->value.len, unquoted->str);
2461 mhd_assert (0 != unquoted->len);
2462 mhd_assert (unquoted->len < param->value.len);
2463 return _MHD_UNQ_OK;
2464}
2465
2466
2475_MHD_static_inline bool
2476is_param_equal (const struct MHD_RqDAuthParam *param,
2477 const char *const str,
2478 const size_t str_len)
2479{
2480 mhd_assert (NULL != param->value.str);
2481 mhd_assert (0 != param->value.len);
2482 if (param->quoted)
2483 return MHD_str_equal_quoted_bin_n (param->value.str, param->value.len,
2484 str, str_len);
2485 return (str_len == param->value.len) &&
2486 (0 == memcmp (str, param->value.str, str_len));
2487
2488}
2489
2490
2499_MHD_static_inline bool
2500is_param_equal_caseless (const struct MHD_RqDAuthParam *param,
2501 const char *const str,
2502 const size_t str_len)
2503{
2504 mhd_assert (NULL != param->value.str);
2505 mhd_assert (0 != param->value.len);
2506 if (param->quoted)
2507 return MHD_str_equal_caseless_quoted_bin_n (param->value.str,
2508 param->value.len,
2509 str,
2510 str_len);
2511 return (str_len == param->value.len) &&
2513 param->value.str,
2514 str_len));
2515}
2516
2517
2554static enum MHD_DigestAuthResult
2556 const char *realm,
2557 const char *username,
2558 const char *password,
2559 const uint8_t *userdigest,
2560 unsigned int nonce_timeout,
2561 uint32_t max_nc,
2562 enum MHD_DigestAuthMultiQOP mqop,
2563 enum MHD_DigestAuthMultiAlgo3 malgo3,
2564 char **pbuf,
2565 struct DigestAlgorithm *da)
2566{
2567 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
2568 enum MHD_DigestAuthAlgo3 c_algo;
2569 enum MHD_DigestAuthQOP c_qop;
2570 unsigned int digest_size;
2571 uint8_t hash1_bin[MAX_DIGEST];
2572 uint8_t hash2_bin[MAX_DIGEST];
2573#if 0
2574 const char *hentity = NULL; /* "auth-int" is not supported */
2575#endif
2576 uint64_t nonce_time;
2577 uint64_t nci;
2578 const struct MHD_RqDAuth *params;
2582 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE];
2583 char **const ptmp2 = pbuf;
2584 size_t tmp2_size;
2585 struct _MHD_str_w_len unquoted;
2586 struct _MHD_mstr_w_len unq_copy;
2587 enum _MHD_GetUnqResult unq_res;
2588 size_t username_len;
2589 size_t realm_len;
2590
2591 mhd_assert ((NULL != password) || (NULL != userdigest));
2592 mhd_assert (! ((NULL != userdigest) && (NULL != password)));
2593
2594 tmp2_size = 0;
2595
2596 params = MHD_get_rq_dauth_params_ (connection);
2597 if (NULL == params)
2599
2600 /* ** Initial parameters checks and setup ** */
2601 /* Get client's algorithm */
2602 c_algo = params->algo3;
2603 /* Check whether client's algorithm is allowed by function parameter */
2604 if (((unsigned int) c_algo) !=
2605 (((unsigned int) c_algo) & ((unsigned int) malgo3)))
2606 return MHD_DAUTH_WRONG_ALGO;
2607 if (MHD_DIGEST_AUTH_ALGO3_INVALID == c_algo)
2608 return MHD_DAUTH_WRONG_ALGO;
2609 /* Check whether client's algorithm is supported */
2610 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_AUTH_ALGO3_SESSION))
2611 {
2612#ifdef HAVE_MESSAGES
2613 MHD_DLOG (connection->daemon,
2614 _ ("The 'session' algorithms are not supported.\n"));
2615#endif /* HAVE_MESSAGES */
2616 return MHD_DAUTH_WRONG_ALGO;
2617 }
2618#ifndef MHD_MD5_SUPPORT
2619 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5))
2620 {
2621#ifdef HAVE_MESSAGES
2622 MHD_DLOG (connection->daemon,
2623 _ ("The MD5 algorithm is not supported by this MHD build.\n"));
2624#endif /* HAVE_MESSAGES */
2625 return MHD_DAUTH_WRONG_ALGO;
2626 }
2627#endif /* ! MHD_MD5_SUPPORT */
2628#ifndef MHD_SHA256_SUPPORT
2629 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA256))
2630 {
2631#ifdef HAVE_MESSAGES
2632 MHD_DLOG (connection->daemon,
2633 _ ("The SHA-256 algorithm is not supported by "
2634 "this MHD build.\n"));
2635#endif /* HAVE_MESSAGES */
2636 return MHD_DAUTH_WRONG_ALGO;
2637 }
2638#endif /* ! MHD_SHA256_SUPPORT */
2639#ifndef MHD_SHA512_256_SUPPORT
2640 if (0 != (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_SHA512_256))
2641 {
2642#ifdef HAVE_MESSAGES
2643 MHD_DLOG (connection->daemon,
2644 _ ("The SHA-512/256 algorithm is not supported by "
2645 "this MHD build.\n"));
2646#endif /* HAVE_MESSAGES */
2647 return MHD_DAUTH_WRONG_ALGO;
2648 }
2649#endif /* ! MHD_SHA512_256_SUPPORT */
2650 if (! digest_init_one_time (da, get_base_digest_algo (c_algo)))
2651 MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
2652 /* Check 'mqop' value */
2653 c_qop = params->qop;
2654 /* Check whether client's QOP is allowed by function parameter */
2655 if (((unsigned int) c_qop) !=
2656 (((unsigned int) c_qop) & ((unsigned int) mqop)))
2657 return MHD_DAUTH_WRONG_QOP;
2658 /* The numeric value of #MHD_DIGEST_AUTH_QOP_INVALID is zero, therefore
2659 the bitmask check above passes it for any 'mqop' value. */
2660 if (MHD_DIGEST_AUTH_QOP_INVALID == c_qop)
2661 return MHD_DAUTH_WRONG_QOP;
2662 if (0 != (((unsigned int) c_qop) & MHD_DIGEST_AUTH_QOP_AUTH_INT))
2663 {
2664#ifdef HAVE_MESSAGES
2665 MHD_DLOG (connection->daemon,
2666 _ ("The 'auth-int' QOP is not supported.\n"));
2667#endif /* HAVE_MESSAGES */
2668 return MHD_DAUTH_WRONG_QOP;
2669 }
2670#ifdef HAVE_MESSAGES
2671 if ((MHD_DIGEST_AUTH_QOP_NONE == c_qop) &&
2672 (0 == (((unsigned int) c_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
2673 MHD_DLOG (connection->daemon,
2674 _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \
2675 "non-standard extension.\n"));
2676#endif /* HAVE_MESSAGES */
2677
2678 digest_size = digest_get_size (da);
2679
2680 /* ** A quick check for presence of all required parameters ** */
2681
2682 if ((NULL == params->username.value.str) &&
2683 (NULL == params->username_ext.value.str))
2685 else if ((NULL != params->username.value.str) &&
2686 (NULL != params->username_ext.value.str))
2687 return MHD_DAUTH_WRONG_USERNAME; /* Parameters cannot be used together */
2688 else if ((NULL != params->username.value.str) &&
2689 (0 == params->username.value.len))
2690 return MHD_DAUTH_WRONG_USERNAME; /* Empty username */
2691 else if ((NULL != params->username_ext.value.str) &&
2692 (MHD_DAUTH_EXT_PARAM_MIN_LEN > params->username_ext.value.len))
2693 return MHD_DAUTH_WRONG_USERNAME; /* Broken extended notation */
2694 else if (params->userhash && (NULL == params->username.value.str))
2695 return MHD_DAUTH_WRONG_USERNAME; /* Userhash cannot be used with extended notation */
2696 else if (params->userhash && (digest_size * 2 > params->username.value.len))
2697 return MHD_DAUTH_WRONG_USERNAME; /* Too few chars for correct userhash */
2698 else if (params->userhash && (digest_size * 4 < params->username.value.len))
2699 return MHD_DAUTH_WRONG_USERNAME; /* Too many chars for correct userhash */
2700
2701 if (NULL == params->realm.value.str)
2702 return MHD_DAUTH_WRONG_REALM;
2703 else if (0 == params->realm.value.len)
2704 return MHD_DAUTH_WRONG_REALM; /* Empty realm */
2705 else if (((NULL == userdigest) || params->userhash) &&
2707 return MHD_DAUTH_TOO_LARGE; /* Realm is too large and should be used in hash calculations */
2708
2709 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
2710 {
2711 if (NULL == params->nc.value.str)
2713 else if (0 == params->nc.value.len)
2715 else if (4 * 8 < params->nc.value.len) /* Four times more than needed */
2717
2718 if (NULL == params->cnonce.value.str)
2720 else if (0 == params->cnonce.value.len)
2722 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->cnonce.value.len)
2723 return MHD_DAUTH_TOO_LARGE;
2724 }
2725
2726 /* The QOP parameter was checked already */
2727
2728 if (NULL == params->uri.value.str)
2729 return MHD_DAUTH_WRONG_URI;
2730 else if (0 == params->uri.value.len)
2731 return MHD_DAUTH_WRONG_URI;
2732 else if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->uri.value.len)
2733 return MHD_DAUTH_TOO_LARGE;
2734
2735 if (NULL == params->nonce.value.str)
2736 return MHD_DAUTH_NONCE_WRONG;
2737 else if (0 == params->nonce.value.len)
2738 return MHD_DAUTH_NONCE_WRONG;
2739 else if (NONCE_STD_LEN (digest_size) * 2 < params->nonce.value.len)
2740 return MHD_DAUTH_NONCE_WRONG;
2741
2742 if (NULL == params->response.value.str)
2744 else if (0 == params->response.value.len)
2746 else if (digest_size * 4 < params->response.value.len)
2748
2749 /* ** Check simple parameters match ** */
2750
2751 /* Check 'algorithm' */
2752 /* The 'algorithm' was checked at the start of the function */
2753 /* 'algorithm' valid */
2754
2755 /* Check 'qop' */
2756 /* The 'qop' was checked at the start of the function */
2757 /* 'qop' valid */
2758
2759 /* Check 'realm' */
2760 realm_len = strlen (realm);
2761 if (! is_param_equal (&params->realm, realm, realm_len))
2762 return MHD_DAUTH_WRONG_REALM;
2763 /* 'realm' valid */
2764
2765 /* Check 'username' */
2766 username_len = strlen (username);
2767 if (! params->userhash)
2768 {
2769 if (NULL != params->username.value.str)
2770 { /* Username in standard notation */
2771 if (! is_param_equal (&params->username, username, username_len))
2773 }
2774 else
2775 { /* Username in extended notation */
2776 char *r_uname;
2777 size_t buf_size = params->username_ext.value.len;
2778 ssize_t res;
2779
2780 mhd_assert (NULL != params->username_ext.value.str);
2781 mhd_assert (MHD_DAUTH_EXT_PARAM_MIN_LEN <= buf_size); /* It was checked already */
2782 buf_size += 1; /* For zero-termination */
2783 buf_size -= MHD_DAUTH_EXT_PARAM_MIN_LEN;
2784 r_uname = get_buffer_for_size (tmp1, ptmp2, &tmp2_size, buf_size);
2785 if (NULL == r_uname)
2786 return (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < buf_size) ?
2788 res = get_rq_extended_uname_copy_z (params->username_ext.value.str,
2789 params->username_ext.value.len,
2790 r_uname, buf_size);
2791 if (0 > res)
2792 return MHD_DAUTH_WRONG_HEADER; /* Broken extended notation */
2793 if ((username_len != (size_t) res) ||
2794 (0 != memcmp (username, r_uname, username_len)))
2796 }
2797 }
2798 else
2799 { /* Userhash */
2800 mhd_assert (NULL != params->username.value.str);
2801 calc_userhash (da, username, username_len, realm, realm_len, hash1_bin);
2802#ifdef MHD_DIGEST_HAS_EXT_ERROR
2803 if (digest_ext_error (da))
2804 return MHD_DAUTH_ERROR;
2805#endif /* MHD_DIGEST_HAS_EXT_ERROR */
2806 /* MHD_bin_to_hex()
2807 takes no output size and writes exactly 2 * digest_size bytes into
2808 this fixed-size stack buffer */
2809 MHD_CHECK_ (connection->daemon,
2810 (2 * digest_size) <= sizeof (tmp1),
2811 return MHD_DAUTH_ERROR);
2812 MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
2813 if (! is_param_equal_caseless (&params->username, tmp1, 2 * digest_size))
2815 /* To simplify the logic, the digest is reset here instead of resetting
2816 before the next hash calculation. */
2817 digest_reset (da);
2818 }
2819 /* 'username' valid */
2820
2821 /* ** Do basic nonce and nonce-counter checks (size, timestamp) ** */
2822
2823 /* Get 'nc' digital value */
2824 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
2825 {
2826
2827 unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
2828 &unquoted);
2829 if (_MHD_UNQ_OK != unq_res)
2830 return MHD_DAUTH_ERROR;
2831
2832 if (unquoted.len != MHD_strx_to_uint64_n_ (unquoted.str,
2833 unquoted.len,
2834 &nci))
2835 {
2836#ifdef HAVE_MESSAGES
2837 MHD_DLOG (daemon,
2838 _ ("Authentication failed, invalid nc format.\n"));
2839#endif
2840 return MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */
2841 }
2842 if (0 == nci)
2843 {
2844#ifdef HAVE_MESSAGES
2845 MHD_DLOG (daemon,
2846 _ ("Authentication failed, invalid 'nc' value.\n"));
2847#endif
2848 return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */
2849 }
2850 if ((0 != max_nc) && (max_nc < nci))
2851 return MHD_DAUTH_NONCE_STALE; /* Too large 'nc' value */
2852 }
2853 else
2854 nci = 1; /* Force 'nc' value */
2855 /* Got 'nc' digital value */
2856
2857 /* Get 'nonce' with basic checks */
2858 unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
2859 &unquoted);
2860 if (_MHD_UNQ_OK != unq_res)
2861 return MHD_DAUTH_ERROR;
2862
2863 if ((NONCE_STD_LEN (digest_size) != unquoted.len) ||
2864 (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time)))
2865 {
2866#ifdef HAVE_MESSAGES
2867 MHD_DLOG (daemon,
2868 _ ("Authentication failed, invalid nonce format.\n"));
2869#endif
2870 return MHD_DAUTH_NONCE_WRONG;
2871 }
2872
2873 if (1)
2874 {
2875 uint64_t t;
2876
2878 /*
2879 * First level vetting for the nonce validity: if the timestamp
2880 * attached to the nonce exceeds `nonce_timeout', then the nonce is
2881 * stale.
2882 */
2883 if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
2884 return MHD_DAUTH_NONCE_STALE; /* too old */
2885 }
2886 if (1)
2887 {
2888 enum MHD_CheckNonceNC_ nonce_nc_check;
2889 /*
2890 * Checking if that combination of nonce and nc is sound
2891 * and not a replay attack attempt. Refuse if nonce was not
2892 * generated previously.
2893 */
2894 nonce_nc_check = check_nonce_nc (connection,
2895 unquoted.str,
2896 NONCE_STD_LEN (digest_size),
2897 nonce_time,
2898 nci);
2899 if (MHD_CHECK_NONCENC_STALE == nonce_nc_check)
2900 {
2901#ifdef HAVE_MESSAGES
2902 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
2903 MHD_DLOG (daemon,
2904 _ ("Stale nonce received. If this happens a lot, you should "
2905 "probably increase the size of the nonce array.\n"));
2906 else
2907 MHD_DLOG (daemon,
2908 _ ("Stale nonce received. This is expected when client " \
2909 "uses RFC2069-compatible mode and makes more than one " \
2910 "request.\n"));
2911#endif
2912 return MHD_DAUTH_NONCE_STALE;
2913 }
2914 else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check)
2915 {
2916#ifdef HAVE_MESSAGES
2917 MHD_DLOG (daemon,
2918 _ ("Received nonce that was not "
2919 "generated by MHD. This may indicate an attack attempt.\n"));
2920#endif
2921 return MHD_DAUTH_NONCE_WRONG;
2922 }
2923 mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check);
2924 }
2925 /* The nonce was generated by MHD, is not stale and nonce-nc combination was
2926 not used before */
2927
2928 /* ** Build H(A2) and check URI match in the header and in the request ** */
2929
2930 /* Get 'uri' */
2931 mhd_assert (! da->hashing);
2932 digest_update_str (da, connection->rq.method);
2934#if 0
2935 /* TODO: add support for "auth-int" */
2936 digest_update_str (da, hentity);
2938#endif
2939 unq_res = get_unquoted_param_copy (&params->uri, tmp1, ptmp2, &tmp2_size,
2940 &unq_copy);
2941 if (_MHD_UNQ_OK != unq_res)
2942 return MHD_DAUTH_ERROR;
2943
2944 digest_update (da, unq_copy.str, unq_copy.len);
2945 /* The next check will modify copied URI string */
2946 if (! check_uri_match (connection, unq_copy.str, unq_copy.len))
2947 return MHD_DAUTH_WRONG_URI;
2948 digest_calc_hash (da, hash2_bin);
2949#ifdef MHD_DIGEST_HAS_EXT_ERROR
2950 /* Skip digest calculation external error check, the next one checks both */
2951#endif /* MHD_DIGEST_HAS_EXT_ERROR */
2952 /* Got H(A2) */
2953
2954 /* ** Build H(A1) ** */
2955 if (NULL == userdigest)
2956 {
2957 mhd_assert (! da->hashing);
2958 digest_reset (da);
2959 calc_userdigest (da,
2960 username, username_len,
2961 realm, realm_len,
2962 password,
2963 hash1_bin);
2964 }
2965 /* TODO: support '-sess' versions */
2966#ifdef MHD_DIGEST_HAS_EXT_ERROR
2967 if (digest_ext_error (da))
2968 return MHD_DAUTH_ERROR;
2969#endif /* MHD_DIGEST_HAS_EXT_ERROR */
2970 /* Got H(A1) */
2971
2972 /* ** Check 'response' ** */
2973
2974 mhd_assert (! da->hashing);
2975 digest_reset (da);
2976 /* Update digest with H(A1) */
2977 MHD_CHECK_ (connection->daemon,
2978 (digest_size * 2) <= sizeof (tmp1),
2979 return MHD_DAUTH_ERROR);
2980 if (NULL == userdigest)
2981 MHD_bin_to_hex (hash1_bin, digest_size, tmp1);
2982 else
2983 MHD_bin_to_hex (userdigest, digest_size, tmp1);
2984 digest_update (da, (const uint8_t *) tmp1, digest_size * 2);
2985
2986 /* H(A1) is not needed anymore, reuse the buffer.
2987 * Use hash1_bin for the client's 'response' decoded to binary form. */
2988 unq_res = get_unquoted_param (&params->response, tmp1, ptmp2, &tmp2_size,
2989 &unquoted);
2990 if (_MHD_UNQ_OK != unq_res)
2991 return MHD_DAUTH_ERROR;
2992 if (unquoted.len > MAX_AUTH_RESPONSE_LENGTH)
2993 return MHD_DAUTH_ERROR;
2994 if (digest_size !=
2995 MHD_hex_to_bin (unquoted.str,
2996 unquoted.len,
2997 hash1_bin))
2999
3000 /* Update digest with ':' */
3002 /* Update digest with 'nonce' text value */
3003 unq_res = get_unquoted_param (&params->nonce, tmp1, ptmp2, &tmp2_size,
3004 &unquoted);
3005 if (_MHD_UNQ_OK != unq_res)
3006 return MHD_DAUTH_ERROR;
3007 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
3008 /* Update digest with ':' */
3010 if (MHD_DIGEST_AUTH_QOP_NONE != c_qop)
3011 {
3012 /* Update digest with 'nc' text value */
3013 unq_res = get_unquoted_param (&params->nc, tmp1, ptmp2, &tmp2_size,
3014 &unquoted);
3015 if (_MHD_UNQ_OK != unq_res)
3016 return MHD_DAUTH_ERROR;
3017 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
3018 /* Update digest with ':' */
3020 /* Update digest with 'cnonce' value */
3021 unq_res = get_unquoted_param (&params->cnonce, tmp1, ptmp2, &tmp2_size,
3022 &unquoted);
3023 if (_MHD_UNQ_OK != unq_res)
3024 return MHD_DAUTH_ERROR;
3025 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
3026 /* Update digest with ':' */
3028 /* Update digest with 'qop' value */
3029 unq_res = get_unquoted_param (&params->qop_raw, tmp1, ptmp2, &tmp2_size,
3030 &unquoted);
3031 if (_MHD_UNQ_OK != unq_res)
3032 return MHD_DAUTH_ERROR;
3033 digest_update (da, (const uint8_t *) unquoted.str, unquoted.len);
3034 /* Update digest with ':' */
3036 }
3037 /* Update digest with H(A2) */
3038 MHD_bin_to_hex (hash2_bin,
3039 digest_size,
3040 tmp1);
3041 digest_update (da,
3042 (const uint8_t *) tmp1,
3043 digest_size * 2);
3044
3045 /* H(A2) is not needed anymore, reuse the buffer.
3046 * Use hash2_bin for the calculated response in binary form */
3047 digest_calc_hash (da, hash2_bin);
3048#ifdef MHD_DIGEST_HAS_EXT_ERROR
3049 if (digest_ext_error (da))
3050 return MHD_DAUTH_ERROR;
3051#endif /* MHD_DIGEST_HAS_EXT_ERROR */
3052
3053 if (0 != memcmp (hash1_bin,
3054 hash2_bin,
3055 digest_size))
3057
3058 if (MHD_DAUTH_BIND_NONCE_NONE != daemon->dauth_bind_type)
3059 {
3060 mhd_assert (sizeof(tmp1) >= (NONCE_STD_LEN (digest_size) + 1));
3061 /* It was already checked that 'nonce' (including timestamp) was generated
3062 by MHD. */
3063 mhd_assert (! da->hashing);
3064 digest_reset (da);
3065 calculate_nonce (nonce_time,
3066 connection->rq.http_mthd,
3067 connection->rq.method,
3068 daemon->digest_auth_random,
3069 daemon->digest_auth_rand_size,
3070 connection->addr,
3071 (size_t) connection->addr_len,
3072 connection->rq.url,
3073 connection->rq.url_len,
3074 connection->rq.headers_received,
3075 realm,
3076 realm_len,
3077 daemon->dauth_bind_type,
3078 da,
3079 tmp1);
3080
3081#ifdef MHD_DIGEST_HAS_EXT_ERROR
3082 if (digest_ext_error (da))
3083 return MHD_DAUTH_ERROR;
3084#endif /* MHD_DIGEST_HAS_EXT_ERROR */
3085
3086 if (! is_param_equal (&params->nonce, tmp1,
3087 NONCE_STD_LEN (digest_size)))
3089 /* The 'nonce' was generated in the same conditions */
3090 }
3091
3092 return MHD_DAUTH_OK;
3093}
3094
3095
3130static enum MHD_DigestAuthResult
3132 const char *realm,
3133 const char *username,
3134 const char *password,
3135 const uint8_t *userdigest,
3136 unsigned int nonce_timeout,
3137 uint32_t max_nc,
3138 enum MHD_DigestAuthMultiQOP mqop,
3139 enum MHD_DigestAuthMultiAlgo3 malgo3)
3140{
3141 enum MHD_DigestAuthResult res;
3142 char *buf;
3143 struct DigestAlgorithm da;
3144
3145 buf = NULL;
3146 digest_setup_zero (&da);
3147 if (0 == nonce_timeout)
3148 nonce_timeout = connection->daemon->dauth_def_nonce_timeout;
3149 if (0 == max_nc)
3150 max_nc = connection->daemon->dauth_def_max_nc;
3151 res = digest_auth_check_all_inner (connection, realm, username, password,
3152 userdigest,
3153 nonce_timeout,
3154 max_nc, mqop, malgo3,
3155 &buf, &da);
3156 digest_deinit (&da);
3157 if (NULL != buf)
3158 free (buf);
3159
3160 return res;
3161}
3162
3163
3182_MHD_EXTERN int
3184 const char *realm,
3185 const char *username,
3186 const char *password,
3187 unsigned int nonce_timeout)
3188{
3189 return MHD_digest_auth_check2 (connection,
3190 realm,
3191 username,
3192 password,
3193 nonce_timeout,
3195}
3196
3197
3231 const char *realm,
3232 const char *username,
3233 const char *password,
3234 unsigned int nonce_timeout,
3235 uint32_t max_nc,
3236 enum MHD_DigestAuthMultiQOP mqop,
3237 enum MHD_DigestAuthMultiAlgo3 malgo3)
3238{
3239 mhd_assert (NULL != password);
3240
3241 return digest_auth_check_all (connection,
3242 realm,
3243 username,
3244 password,
3245 NULL,
3246 nonce_timeout,
3247 max_nc,
3248 mqop,
3249 malgo3);
3250}
3251
3252
3297 const char *realm,
3298 const char *username,
3299 const void *userdigest,
3300 size_t userdigest_size,
3301 unsigned int nonce_timeout,
3302 uint32_t max_nc,
3303 enum MHD_DigestAuthMultiQOP mqop,
3304 enum MHD_DigestAuthMultiAlgo3 malgo3)
3305{
3306 if (1 != (((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_MD5)) ? 1 : 0)
3307 + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA256)) ? 1 : 0)
3308 + ((0 != (malgo3 & MHD_DIGEST_BASE_ALGO_SHA512_256)) ? 1 : 0)))
3309 MHD_PANIC (_ ("Wrong 'malgo3' value, only one base hashing algorithm " \
3310 "(MD5, SHA-256 or SHA-512/256) must be specified, " \
3311 "API violation"));
3312
3313#ifndef MHD_MD5_SUPPORT
3314 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
3315 {
3316#ifdef HAVE_MESSAGES
3317 MHD_DLOG (connection->daemon,
3318 _ ("The MD5 algorithm is not supported by this MHD build.\n"));
3319#endif /* HAVE_MESSAGES */
3320 return MHD_DAUTH_WRONG_ALGO;
3321 }
3322#endif /* ! MHD_MD5_SUPPORT */
3323#ifndef MHD_SHA256_SUPPORT
3324 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
3325 {
3326#ifdef HAVE_MESSAGES
3327 MHD_DLOG (connection->daemon,
3328 _ ("The SHA-256 algorithm is not supported by "
3329 "this MHD build.\n"));
3330#endif /* HAVE_MESSAGES */
3331 return MHD_DAUTH_WRONG_ALGO;
3332 }
3333#endif /* ! MHD_SHA256_SUPPORT */
3334#ifndef MHD_SHA512_256_SUPPORT
3335 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256))
3336 {
3337#ifdef HAVE_MESSAGES
3338 MHD_DLOG (connection->daemon,
3339 _ ("The SHA-512/256 algorithm is not supported by "
3340 "this MHD build.\n"));
3341#endif /* HAVE_MESSAGES */
3342 return MHD_DAUTH_WRONG_ALGO;
3343 }
3344#endif /* ! MHD_SHA512_256_SUPPORT */
3345
3346 if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) !=
3347 userdigest_size)
3348 MHD_PANIC (_ ("Wrong 'userdigest_size' value, does not match 'malgo3', "
3349 "API violation"));
3350
3351 return digest_auth_check_all (connection,
3352 realm,
3353 username,
3354 NULL,
3355 (const uint8_t *) userdigest,
3356 nonce_timeout,
3357 max_nc,
3358 mqop,
3359 malgo3);
3360}
3361
3362
3379_MHD_EXTERN int
3381 const char *realm,
3382 const char *username,
3383 const char *password,
3384 unsigned int nonce_timeout,
3385 enum MHD_DigestAuthAlgorithm algo)
3386{
3387 enum MHD_DigestAuthResult res;
3388 enum MHD_DigestAuthMultiAlgo3 malgo3;
3389
3390 if (MHD_DIGEST_ALG_AUTO == algo)
3392 else if (MHD_DIGEST_ALG_MD5 == algo)
3394 else if (MHD_DIGEST_ALG_SHA256 == algo)
3396 else
3397 MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
3398
3399 res = MHD_digest_auth_check3 (connection,
3400 realm,
3401 username,
3402 password,
3403 nonce_timeout,
3405 malgo3);
3406 if (MHD_DAUTH_OK == res)
3407 return MHD_YES;
3408 else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) ||
3410 return MHD_INVALID_NONCE;
3411 return MHD_NO;
3412
3413}
3414
3415
3438_MHD_EXTERN int
3440 const char *realm,
3441 const char *username,
3442 const uint8_t *digest,
3443 size_t digest_size,
3444 unsigned int nonce_timeout,
3445 enum MHD_DigestAuthAlgorithm algo)
3446{
3447 enum MHD_DigestAuthResult res;
3448 enum MHD_DigestAuthMultiAlgo3 malgo3;
3449
3450 if (MHD_DIGEST_ALG_AUTO == algo)
3451 {
3452 /* MHD_DIGEST_ALG_AUTO maps to more than one base hashing algorithm,
3453 * but @a digest is a hash of one specific algorithm and its length
3454 * does not identify which one (SHA-256 and SHA-512/256 hashes are
3455 * both 32 bytes). Forwarding AUTO to
3456 * MHD_digest_auth_check_digest3() would therefore reach the
3457 * MHD_PANIC() in that function and kill the process. Report it as
3458 * a failed authentication instead; the caller cannot recover from
3459 * an abort, but it can handle MHD_NO. */
3460#ifdef HAVE_MESSAGES
3461 MHD_DLOG (connection->daemon,
3462 _ ("MHD_DIGEST_ALG_AUTO cannot be used with a " \
3463 "pre-calculated digest: exactly one algorithm must be " \
3464 "named, as the digest was produced by one.\n"));
3465#endif /* HAVE_MESSAGES */
3466 return MHD_NO;
3467 }
3468 else if (MHD_DIGEST_ALG_MD5 == algo)
3470 else if (MHD_DIGEST_ALG_SHA256 == algo)
3472 else
3473 MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
3474
3475 res = MHD_digest_auth_check_digest3 (connection,
3476 realm,
3477 username,
3478 digest,
3479 digest_size,
3480 nonce_timeout,
3482 malgo3);
3483 if (MHD_DAUTH_OK == res)
3484 return MHD_YES;
3485 else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res) ||
3487 return MHD_INVALID_NONCE;
3488 return MHD_NO;
3489}
3490
3491
3511_MHD_EXTERN int
3513 const char *realm,
3514 const char *username,
3515 const uint8_t digest[MHD_MD5_DIGEST_SIZE],
3516 unsigned int nonce_timeout)
3517{
3518 return MHD_digest_auth_check_digest2 (connection,
3519 realm,
3520 username,
3521 digest,
3523 nonce_timeout,
3525}
3526
3527
3577static enum MHD_Result
3579 const char *realm,
3580 const char *opaque,
3581 const char *domain,
3582 struct MHD_Response *response,
3583 int signal_stale,
3584 enum MHD_DigestAuthMultiQOP mqop,
3585 enum MHD_DigestAuthMultiAlgo3 malgo3,
3586 int userhash_support,
3587 int prefer_utf8,
3588 char **buf_ptr,
3589 struct DigestAlgorithm *da)
3590{
3591 static const char prefix_realm[] = "realm=\"";
3592 static const char prefix_qop[] = "qop=\"";
3593 static const char prefix_algo[] = "algorithm=";
3594 static const char prefix_nonce[] = "nonce=\"";
3595 static const char prefix_opaque[] = "opaque=\"";
3596 static const char prefix_domain[] = "domain=\"";
3597 static const char str_charset[] = "charset=UTF-8";
3598 static const char str_userhash[] = "userhash=true";
3599 static const char str_stale[] = "stale=true";
3600 enum MHD_DigestAuthAlgo3 s_algo;
3601 size_t realm_len;
3602 size_t opaque_len;
3603 size_t domain_len;
3604 size_t buf_size;
3605 char *buf;
3606 size_t p; /* The position in the buffer */
3607 char *hdr_name;
3608
3609 if ((0 == (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_NON_SESSION)) ||
3610 (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION)))
3611 {
3612#ifdef HAVE_MESSAGES
3613 MHD_DLOG (connection->daemon,
3614 _ ("Only non-'session' algorithms are supported.\n"));
3615#endif /* HAVE_MESSAGES */
3616 return MHD_NO;
3617 }
3618 malgo3 =
3620 (malgo3
3622#ifdef MHD_MD5_SUPPORT
3623 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5))
3625 else
3626#endif /* MHD_MD5_SUPPORT */
3627#ifdef MHD_SHA256_SUPPORT
3628 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256))
3630 else
3631#endif /* MHD_SHA256_SUPPORT */
3632#ifdef MHD_SHA512_256_SUPPORT
3633 if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA512_256))
3635 else
3636#endif /* MHD_SHA512_256_SUPPORT */
3637 {
3638 if (0 == (((unsigned int) malgo3)
3641 MHD_PANIC (_ ("Wrong 'malgo3' value, API violation"));
3642 else
3643 {
3644#ifdef HAVE_MESSAGES
3645 MHD_DLOG (connection->daemon,
3646 _ ("No requested algorithm is supported by this MHD build.\n"));
3647#endif /* HAVE_MESSAGES */
3648 }
3649 return MHD_NO;
3650 }
3651
3653 MHD_PANIC (_ ("Wrong 'mqop' value, API violation"));
3654
3655 mqop = (enum MHD_DigestAuthMultiQOP)
3656 (mqop
3658
3659 if (! digest_init_one_time (da, get_base_digest_algo (s_algo)))
3660 MHD_PANIC (_ ("Wrong 'algo' value, API violation"));
3661
3663 {
3664#ifdef HAVE_MESSAGES
3665 if ((0 != userhash_support) || (0 != prefer_utf8))
3666 MHD_DLOG (connection->daemon,
3667 _ ("The 'userhash' and 'charset' ('prefer_utf8') parameters " \
3668 "are not compatible with RFC2069 and ignored.\n"));
3669 if (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5))
3670 MHD_DLOG (connection->daemon,
3671 _ ("RFC2069 with SHA-256 or SHA-512/256 algorithm is " \
3672 "non-standard extension.\n"));
3673#endif
3674 userhash_support = 0;
3675 prefer_utf8 = 0;
3676 }
3677
3678 if (0 == MHD_get_master (connection->daemon)->nonce_nc_size)
3679 {
3680#ifdef HAVE_MESSAGES
3681 MHD_DLOG (connection->daemon,
3682 _ ("The nonce array size is zero.\n"));
3683#endif /* HAVE_MESSAGES */
3684 return MHD_NO;
3685 }
3686
3687 /* Calculate required size */
3688 buf_size = 0;
3689 /* 'Digest ' */
3690 buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */
3691 buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */
3692 /* 'realm="xxxx", ' */
3693 realm_len = strlen (realm);
3694 if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len)
3695 {
3696#ifdef HAVE_MESSAGES
3697 MHD_DLOG (connection->daemon,
3698 _ ("The 'realm' is too large.\n"));
3699#endif /* HAVE_MESSAGES */
3700 return MHD_NO;
3701 }
3702 if ((NULL != memchr (realm, '\r', realm_len)) ||
3703 (NULL != memchr (realm, '\n', realm_len)))
3704 return MHD_NO;
3705
3706 buf_size += realm_len * 2; /* Quoting may double the size */
3707 /* 'qop="xxxx", ' */
3709 {
3710 buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */
3711 buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_);
3712 }
3713 /* 'algorithm="xxxx", ' */
3714 if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) ||
3715 (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
3716 {
3717 buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */
3718#ifdef MHD_MD5_SUPPORT
3719 if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
3720 buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN);
3721 else
3722#endif /* MHD_MD5_SUPPORT */
3723#ifdef MHD_SHA256_SUPPORT
3724 if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
3726 else
3727#endif /* MHD_SHA256_SUPPORT */
3728#ifdef MHD_SHA512_256_SUPPORT
3731 else
3732#endif /* MHD_SHA512_256_SUPPORT */
3733 mhd_assert (0);
3734 }
3735 /* 'nonce="xxxx", ' */
3736 buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */
3737 buf_size += NONCE_STD_LEN (digest_get_size (da)); /* Escaping not needed */
3738 /* 'opaque="xxxx", ' */
3739 if (NULL != opaque)
3740 {
3741 buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */
3742 opaque_len = strlen (opaque);
3743 if ((NULL != memchr (opaque, '\r', opaque_len)) ||
3744 (NULL != memchr (opaque, '\n', opaque_len)))
3745 return MHD_NO;
3746
3747 buf_size += opaque_len * 2; /* Quoting may double the size */
3748 }
3749 else
3750 opaque_len = 0;
3751 /* 'domain="xxxx", ' */
3752 if (NULL != domain)
3753 {
3754 buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */
3755 domain_len = strlen (domain);
3756 if ((NULL != memchr (domain, '\r', domain_len)) ||
3757 (NULL != memchr (domain, '\n', domain_len)))
3758 return MHD_NO;
3759
3760 buf_size += domain_len * 2; /* Quoting may double the size */
3761 }
3762 else
3763 domain_len = 0;
3764 /* 'charset=UTF-8' */
3765 if (MHD_NO != prefer_utf8)
3766 buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */
3767 /* 'userhash=true' */
3768 if (MHD_NO != userhash_support)
3769 buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */
3770 /* 'stale=true' */
3771 if (MHD_NO != signal_stale)
3772 buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */
3773
3774 /* The calculated length is for string ended with ", ". One character will
3775 * be used for zero-termination, the last one will not be used. */
3776
3777 /* Allocate the buffer */
3778 buf = malloc (buf_size);
3779 if (NULL == buf)
3780 return MHD_NO;
3781 *buf_ptr = buf;
3782
3783 /* Build the challenge string */
3784 p = 0;
3785 /* 'Digest: ' */
3786 memcpy (buf + p, _MHD_AUTH_DIGEST_BASE,
3789 buf[p++] = ' ';
3790 /* 'realm="xxxx", ' */
3791 memcpy (buf + p, prefix_realm,
3792 MHD_STATICSTR_LEN_ (prefix_realm));
3793 p += MHD_STATICSTR_LEN_ (prefix_realm);
3794 mhd_assert ((buf_size - p) >= (realm_len * 2));
3795 if (1)
3796 {
3797 size_t quoted_size;
3798 quoted_size = MHD_str_quote (realm, realm_len, buf + p, buf_size - p);
3799 if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < quoted_size)
3800 {
3801#ifdef HAVE_MESSAGES
3802 MHD_DLOG (connection->daemon,
3803 _ ("The 'realm' is too large after 'quoting'.\n"));
3804#endif /* HAVE_MESSAGES */
3805 return MHD_NO;
3806 }
3807 p += quoted_size;
3808 }
3809 buf[p++] = '\"';
3810 buf[p++] = ',';
3811 buf[p++] = ' ';
3812 /* 'qop="xxxx", ' */
3814 {
3815 memcpy (buf + p, prefix_qop,
3816 MHD_STATICSTR_LEN_ (prefix_qop));
3817 p += MHD_STATICSTR_LEN_ (prefix_qop);
3818 memcpy (buf + p, MHD_TOKEN_AUTH_,
3821 buf[p++] = '\"';
3822 buf[p++] = ',';
3823 buf[p++] = ' ';
3824 }
3825 /* 'algorithm="xxxx", ' */
3826 if (((MHD_DIGEST_AUTH_MULT_QOP_NONE) != mqop) ||
3827 (0 == (((unsigned int) s_algo) & MHD_DIGEST_BASE_ALGO_MD5)))
3828 {
3829 memcpy (buf + p, prefix_algo,
3830 MHD_STATICSTR_LEN_ (prefix_algo));
3831 p += MHD_STATICSTR_LEN_ (prefix_algo);
3832#ifdef MHD_MD5_SUPPORT
3833 if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo)
3834 {
3835 memcpy (buf + p, _MHD_MD5_TOKEN,
3838 }
3839 else
3840#endif /* MHD_MD5_SUPPORT */
3841#ifdef MHD_SHA256_SUPPORT
3842 if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo)
3843 {
3844 memcpy (buf + p, _MHD_SHA256_TOKEN,
3847 }
3848 else
3849#endif /* MHD_SHA256_SUPPORT */
3850#ifdef MHD_SHA512_256_SUPPORT
3852 {
3853 memcpy (buf + p, _MHD_SHA512_256_TOKEN,
3856 }
3857 else
3858#endif /* MHD_SHA512_256_SUPPORT */
3859 mhd_assert (0);
3860 buf[p++] = ',';
3861 buf[p++] = ' ';
3862 }
3863 /* 'nonce="xxxx", ' */
3864 memcpy (buf + p, prefix_nonce,
3865 MHD_STATICSTR_LEN_ (prefix_nonce));
3866 p += MHD_STATICSTR_LEN_ (prefix_nonce);
3867 mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (da))));
3868 if (! calculate_add_nonce_with_retry (connection, realm, da, buf + p))
3869 {
3870#ifdef MHD_DIGEST_HAS_EXT_ERROR
3871 if (digest_ext_error (da))
3872 {
3873#ifdef HAVE_MESSAGES
3874 MHD_DLOG (connection->daemon,
3875 _ ("TLS library reported hash calculation error, nonce could "
3876 "not be generated.\n"));
3877#endif /* HAVE_MESSAGES */
3878 return MHD_NO;
3879 }
3880#endif /* MHD_DIGEST_HAS_EXT_ERROR */
3881#ifdef HAVE_MESSAGES
3882 MHD_DLOG (connection->daemon,
3883 _ ("Could not register nonce. Client's requests with this "
3884 "nonce will be always 'stale'. Probably clients' requests "
3885 "are too intensive.\n"));
3886#endif /* HAVE_MESSAGES */
3887 (void) 0; /* Mute compiler warning for builds without messages */
3888 }
3889 p += NONCE_STD_LEN (digest_get_size (da));
3890 buf[p++] = '\"';
3891 buf[p++] = ',';
3892 buf[p++] = ' ';
3893 /* 'opaque="xxxx", ' */
3894 if (NULL != opaque)
3895 {
3896 memcpy (buf + p, prefix_opaque,
3897 MHD_STATICSTR_LEN_ (prefix_opaque));
3898 p += MHD_STATICSTR_LEN_ (prefix_opaque);
3899 mhd_assert ((buf_size - p) >= (opaque_len * 2));
3900 p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p);
3901 buf[p++] = '\"';
3902 buf[p++] = ',';
3903 buf[p++] = ' ';
3904 }
3905 /* 'domain="xxxx", ' */
3906 if (NULL != domain)
3907 {
3908 memcpy (buf + p, prefix_domain,
3909 MHD_STATICSTR_LEN_ (prefix_domain));
3910 p += MHD_STATICSTR_LEN_ (prefix_domain);
3911 mhd_assert ((buf_size - p) >= (domain_len * 2));
3912 p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p);
3913 buf[p++] = '\"';
3914 buf[p++] = ',';
3915 buf[p++] = ' ';
3916 }
3917 /* 'charset=UTF-8' */
3918 if (MHD_NO != prefer_utf8)
3919 {
3920 memcpy (buf + p, str_charset,
3921 MHD_STATICSTR_LEN_ (str_charset));
3922 p += MHD_STATICSTR_LEN_ (str_charset);
3923 buf[p++] = ',';
3924 buf[p++] = ' ';
3925 }
3926 /* 'userhash=true' */
3927 if (MHD_NO != userhash_support)
3928 {
3929 memcpy (buf + p, str_userhash,
3930 MHD_STATICSTR_LEN_ (str_userhash));
3931 p += MHD_STATICSTR_LEN_ (str_userhash);
3932 buf[p++] = ',';
3933 buf[p++] = ' ';
3934 }
3935 /* 'stale=true' */
3936 if (MHD_NO != signal_stale)
3937 {
3938 memcpy (buf + p, str_stale,
3939 MHD_STATICSTR_LEN_ (str_stale));
3940 p += MHD_STATICSTR_LEN_ (str_stale);
3941 buf[p++] = ',';
3942 buf[p++] = ' ';
3943 }
3944 mhd_assert (buf_size >= p);
3945 /* The built string ends with ", ". Replace comma with zero-termination. */
3946 --p;
3947 buf[--p] = 0;
3948
3949 hdr_name = malloc (MHD_STATICSTR_LEN_ (MHD_HTTP_HEADER_WWW_AUTHENTICATE) + 1);
3950 if (NULL != hdr_name)
3951 {
3952 memcpy (hdr_name, MHD_HTTP_HEADER_WWW_AUTHENTICATE,
3955 hdr_name,
3958 buf, p))
3959 {
3960 *buf_ptr = NULL; /* The buffer will be free()ed when the response is destroyed */
3961 return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response);
3962 }
3963#ifdef HAVE_MESSAGES
3964 else
3965 {
3966 MHD_DLOG (connection->daemon,
3967 _ ("Failed to add Digest auth header.\n"));
3968 }
3969#endif /* HAVE_MESSAGES */
3970 free (hdr_name);
3971 }
3972 return MHD_NO;
3973}
3974
3975
4040 const char *realm,
4041 const char *opaque,
4042 const char *domain,
4043 struct MHD_Response *response,
4044 int signal_stale,
4045 enum MHD_DigestAuthMultiQOP mqop,
4046 enum MHD_DigestAuthMultiAlgo3 malgo3,
4047 int userhash_support,
4048 int prefer_utf8)
4049{
4050 struct DigestAlgorithm da;
4051 char *buf_ptr;
4052 enum MHD_Result ret;
4053
4054 buf_ptr = NULL;
4055 digest_setup_zero (&da);
4056 ret = queue_auth_required_response3_inner (connection,
4057 realm,
4058 opaque,
4059 domain,
4060 response,
4061 signal_stale,
4062 mqop,
4063 malgo3,
4064 userhash_support,
4065 prefer_utf8,
4066 &buf_ptr,
4067 &da);
4068 digest_deinit (&da);
4069 if (NULL != buf_ptr)
4070 free (buf_ptr);
4071 return ret;
4072}
4073
4074
4093 const char *realm,
4094 const char *opaque,
4095 struct MHD_Response *response,
4096 int signal_stale,
4097 enum MHD_DigestAuthAlgorithm algo)
4098{
4099 enum MHD_DigestAuthMultiAlgo3 algo3;
4100
4101 if (MHD_DIGEST_ALG_MD5 == algo)
4103 else if (MHD_DIGEST_ALG_SHA256 == algo)
4105 else if (MHD_DIGEST_ALG_AUTO == algo)
4107 else
4108 MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */
4109
4110 return MHD_queue_auth_required_response3 (connection, realm, opaque,
4111 NULL, response, signal_stale,
4113 algo3,
4114 0, 0);
4115}
4116
4117
4137 const char *realm,
4138 const char *opaque,
4139 struct MHD_Response *response,
4140 int signal_stale)
4141{
4142 return MHD_queue_auth_fail_response2 (connection,
4143 realm,
4144 opaque,
4145 response,
4146 signal_stale,
4148}
4149
4150
4151/* end of digestauth.c */
_MHD_static_inline enum MHD_DigestBaseAlgo get_base_digest_algo(enum MHD_DigestAuthAlgo3 algo3)
Definition digestauth.c:211
#define digest_deinit(da)
Definition digestauth.c:431
_MHD_static_inline void calc_userhash(struct DigestAlgorithm *da, const char *username, const size_t username_len, const char *realm, const size_t realm_len, uint8_t *digest_bin)
MHD_CheckNonceNC_
Definition digestauth.c:185
@ MHD_CHECK_NONCENC_OK
Definition digestauth.c:189
@ MHD_CHECK_NONCENC_STALE
Definition digestauth.c:196
@ MHD_CHECK_NONCENC_WRONG
Definition digestauth.c:201
#define TRIM_TO_TIMESTAMP(value)
Definition digestauth.c:78
static enum MHD_Result test_header(void *cls, const char *key, size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind)
static bool check_uri_match(struct MHD_Connection *connection, char *uri, size_t uri_len)
#define TIMESTAMP_CHARS_LEN
Definition digestauth.c:85
static enum MHD_GetRqNCResult get_rq_nc(const struct MHD_RqDAuth *params, uint32_t *nc)
static MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ bool calculate_add_nonce_with_retry(struct MHD_Connection *const connection, const char *realm, struct DigestAlgorithm *da, char *nonce)
static MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ size_t get_nonce_nc_idx(size_t arr_size, const char *nonce, size_t noncelen)
Definition digestauth.c:808
_MHD_static_inline void digest_update_with_colon(struct DigestAlgorithm *da)
Definition digestauth.c:563
#define NONCE_STD_LEN(digest_size)
Definition digestauth.c:93
#define MAX_AUTH_RESPONSE_LENGTH
Definition digestauth.c:168
_MHD_static_inline void digest_reset(struct DigestAlgorithm *da)
Definition digestauth.c:639
#define TIMESTAMP_BIN_SIZE
Definition digestauth.c:72
MHD_GetRqNCResult
@ MHD_GET_RQ_NC_NONE
@ MHD_GET_RQ_NC_BROKEN
@ MHD_GET_RQ_NC_VALID
@ MHD_GET_RQ_NC_TOO_LONG
@ MHD_GET_RQ_NC_TOO_LARGE
static size_t get_rq_param_unquoted_copy_z(const struct MHD_RqDAuthParam *param, char *buf)
static ssize_t get_rq_extended_uname_copy_z(const char *uname_ext, size_t uname_ext_len, char *buf, size_t buf_size)
_MHD_GetUnqResult
@ _MHD_UNQ_TOO_LARGE
@ _MHD_UNQ_OUT_OF_MEM
@ _MHD_UNQ_OK
#define digest_ext_error(da)
Definition digestauth.c:729
_MHD_static_inline bool is_param_equal(const struct MHD_RqDAuthParam *param, const char *const str, const size_t str_len)
static void calculate_nonce(uint64_t nonce_time, enum MHD_HTTP_Method mthd_e, const char *method, const char *rnd, size_t rnd_size, const struct sockaddr_storage *saddr, size_t saddr_size, const char *uri, size_t uri_len, const struct MHD_HTTP_Req_Header *first_header, const char *realm, size_t realm_len, unsigned int bind_options, struct DigestAlgorithm *da, char *nonce)
static size_t get_rq_uname(const struct MHD_RqDAuth *params, enum MHD_DigestAuthUsernameType uname_type, struct MHD_DigestAuthUsernameInfo *uname_info, uint8_t *buf, size_t buf_size)
static enum _MHD_GetUnqResult get_unquoted_param_copy(const struct MHD_RqDAuthParam *param, char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], char **ptmp2, size_t *ptmp2_size, struct _MHD_mstr_w_len *unquoted)
static bool calculate_add_nonce(struct MHD_Connection *const connection, uint64_t timestamp, const char *realm, size_t realm_len, struct DigestAlgorithm *da, char *nonce)
_MHD_static_inline size_t digest_get_hash_size(enum MHD_DigestAuthAlgo3 algo3)
Definition digestauth.c:233
#define MHD_DAUTH_EXT_PARAM_PREFIX
Definition digestauth.c:173
static enum _MHD_GetUnqResult get_unquoted_param(const struct MHD_RqDAuthParam *param, char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], char **ptmp2, size_t *ptmp2_size, struct _MHD_str_w_len *unquoted)
static enum MHD_CheckNonceNC_ check_nonce_nc(struct MHD_Connection *connection, const char *nonce, size_t noncelen, uint64_t nonce_time, uint64_t nc)
Definition digestauth.c:833
static char * get_buffer_for_size(char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], char **ptmp2, size_t *ptmp2_size, size_t required_size)
_MHD_static_inline size_t get_rq_unames_size(const struct MHD_RqDAuth *params, enum MHD_DigestAuthUsernameType uname_type)
#define REUSE_TIMEOUT
Definition digestauth.c:60
#define DAUTH_JUMPBACK_MAX
Definition digestauth.c:66
_MHD_static_inline enum MHD_DigestAuthUsernameType get_rq_uname_type(const struct MHD_RqDAuth *params)
Definition digestauth.c:974
#define _MHD_STATIC_UNQ_BUFFER_SIZE
MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_ _MHD_static_inline void calc_userdigest(struct DigestAlgorithm *da, const char *username, const size_t username_len, const char *realm, const size_t realm_len, const char *password, uint8_t *ha1_bin)
_MHD_static_inline void digest_update(struct DigestAlgorithm *da, const void *data, size_t length)
Definition digestauth.c:513
_MHD_static_inline bool is_param_equal_caseless(const struct MHD_RqDAuthParam *param, const char *const str, const size_t str_len)
static bool get_nonce_timestamp(const char *const nonce, size_t noncelen, uint64_t *const ptimestamp)
Definition digestauth.c:742
_MHD_static_inline void digest_calc_hash(struct DigestAlgorithm *da, uint8_t *digest)
Definition digestauth.c:577
_MHD_static_inline void digest_update_str(struct DigestAlgorithm *da, const char *str)
Definition digestauth.c:549
static MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_ uint32_t fast_simple_hash(const uint8_t *data, size_t data_size)
Definition digestauth.c:778
_MHD_static_inline bool digest_init_one_time(struct DigestAlgorithm *da, enum MHD_DigestBaseAlgo algo)
Definition digestauth.c:449
_MHD_static_inline unsigned int digest_get_size(struct DigestAlgorithm *da)
Definition digestauth.c:352
static bool check_argument_match(struct MHD_Connection *connection, char *args)
#define digest_setup_zero(da)
Definition digestauth.c:430
#define MHD_DAUTH_EXT_PARAM_MIN_LEN
Definition digestauth.c:178
static bool is_slot_available(const struct MHD_NonceNc *const nn, const uint64_t now, const char *const new_nonce, size_t new_nonce_len)
#define _MHD_SHA256_TOKEN
Definition digestauth.h:60
#define _MHD_MD5_TOKEN
Definition digestauth.h:55
#define _MHD_SHA512_256_TOKEN
Definition digestauth.h:65
#define MHD_TOKEN_AUTH_
Definition digestauth.h:75
#define _MHD_AUTH_DIGEST_BASE
Definition digestauth.h:50
#define _MHD_AUTH_DIGEST_MAX_PARAM_SIZE
Definition digestauth.h:45
Declarations for HTTP authorisation general functions.
_MHD_EXTERN enum MHD_Result MHD_digest_auth_calc_userdigest(enum MHD_DigestAuthAlgo3 algo3, const char *username, const char *realm, const char *password, void *userdigest_bin, size_t bin_buf_size)
static enum MHD_DigestAuthResult digest_auth_check_all_inner(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, const uint8_t *userdigest, unsigned int nonce_timeout, uint32_t max_nc, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3, char **pbuf, struct DigestAlgorithm *da)
#define MHD_SHA512_256_DIGEST_SIZE
static enum MHD_DigestAuthResult digest_auth_check_all(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, const uint8_t *userdigest, unsigned int nonce_timeout, uint32_t max_nc, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3)
_MHD_EXTERN enum MHD_Result MHD_queue_auth_fail_response(struct MHD_Connection *connection, const char *realm, const char *opaque, struct MHD_Response *response, int signal_stale)
_MHD_EXTERN int MHD_digest_auth_check2(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout, enum MHD_DigestAuthAlgorithm algo)
_MHD_EXTERN enum MHD_Result MHD_queue_auth_required_response3(struct MHD_Connection *connection, const char *realm, const char *opaque, const char *domain, struct MHD_Response *response, int signal_stale, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 algo, int userhash_support, int prefer_utf8)
_MHD_EXTERN int MHD_digest_auth_check_digest2(struct MHD_Connection *connection, const char *realm, const char *username, const uint8_t *digest, size_t digest_size, unsigned int nonce_timeout, enum MHD_DigestAuthAlgorithm algo)
_MHD_EXTERN enum MHD_Result MHD_digest_auth_calc_userhash_hex(enum MHD_DigestAuthAlgo3 algo3, const char *username, const char *realm, char *userhash_hex, size_t hex_buf_size)
_MHD_EXTERN enum MHD_DigestAuthResult MHD_digest_auth_check_digest3(struct MHD_Connection *connection, const char *realm, const char *username, const void *userdigest, size_t userdigest_size, unsigned int nonce_timeout, uint32_t max_nc, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3)
_MHD_EXTERN enum MHD_Result MHD_queue_auth_fail_response2(struct MHD_Connection *connection, const char *realm, const char *opaque, struct MHD_Response *response, int signal_stale, enum MHD_DigestAuthAlgorithm algo)
_MHD_EXTERN struct MHD_DigestAuthInfo * MHD_digest_auth_get_request_info3(struct MHD_Connection *connection)
_MHD_EXTERN size_t MHD_digest_get_hash_size(enum MHD_DigestAuthAlgo3 algo3)
Definition digestauth.c:296
_MHD_EXTERN int MHD_digest_auth_check_digest(struct MHD_Connection *connection, const char *realm, const char *username, const uint8_t digest[MHD_MD5_DIGEST_SIZE], unsigned int nonce_timeout)
_MHD_EXTERN char * MHD_digest_auth_get_username(struct MHD_Connection *connection)
_MHD_EXTERN int MHD_digest_auth_check(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout)
#define MHD_INVALID_NONCE
static enum MHD_Result queue_auth_required_response3_inner(struct MHD_Connection *connection, const char *realm, const char *opaque, const char *domain, struct MHD_Response *response, int signal_stale, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3, int userhash_support, int prefer_utf8, char **buf_ptr, struct DigestAlgorithm *da)
#define MHD_SHA256_DIGEST_SIZE
_MHD_EXTERN enum MHD_DigestAuthResult MHD_digest_auth_check3(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout, uint32_t max_nc, enum MHD_DigestAuthMultiQOP mqop, enum MHD_DigestAuthMultiAlgo3 malgo3)
_MHD_EXTERN enum MHD_Result MHD_digest_auth_calc_userhash(enum MHD_DigestAuthAlgo3 algo3, const char *username, const char *realm, void *userhash_bin, size_t bin_buf_size)
_MHD_EXTERN struct MHD_DigestAuthUsernameInfo * MHD_digest_auth_get_username3(struct MHD_Connection *connection)
#define MHD_MD5_DIGEST_SIZE
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition microhttpd.h:664
#define MHD_HTTP_UNAUTHORIZED
Definition microhttpd.h:393
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
enum MHD_Result MHD_parse_arguments_(struct MHD_Connection *connection, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, void *cls)
Definition internal.c:169
MHD internal shared structures.
#define MAX_DIGEST_NONCE_LENGTH
Definition internal.h:269
_MHD_static_inline struct MHD_Daemon * MHD_get_master(struct MHD_Daemon *const daemon)
Definition internal.h:2930
MHD_HTTP_Method
Definition internal.h:899
@ MHD_HTTP_MTHD_GET
Definition internal.h:907
@ MHD_HTTP_MTHD_HEAD
Definition internal.h:911
@ MHD_HTTP_MTHD_OTHER
Definition internal.h:939
void MHD_MD5_finish(struct Md5Ctx *ctx, uint8_t digest[MD5_DIGEST_SIZE])
Definition md5.c:461
void MHD_MD5_update(struct Md5Ctx *ctx, const uint8_t *data, size_t length)
Definition md5.c:393
#define MD5_DIGEST_SIZE
Definition md5.h:61
macros for mhd_assert()
#define mhd_assert(ignore)
Definition mhd_assert.h:45
macros for bits manipulations
_MHD_static_inline uint32_t _MHD_ROTR32(uint32_t value32, int bits)
_MHD_static_inline uint32_t _MHD_ROTL32(uint32_t value32, int bits)
macros for MHD_CHECK_(), always-compiled memory-safety invariants
#define MHD_CHECK_(daemon, expr, fail_stmt)
Definition mhd_check.h:118
void * MHD_calloc_(size_t nelem, size_t elsize)
Definition mhd_compat.c:98
Header for platform missing functions.
#define MHD_PANIC(msg)
Definition mhd_itc.h:45
limits values definitions
#define SSIZE_MAX
Definition mhd_limits.h:121
#define UINT64_MAX
Definition mhd_limits.h:93
#define UINT32_MAX
Definition mhd_limits.h:85
Header for platform-independent locks abstraction.
#define MHD_mutex_unlock_chk_(ignore)
Definition mhd_locks.h:198
#define MHD_mutex_lock_chk_(ignore)
Definition mhd_locks.h:196
Simple wrapper for selection of built-in/external MD5 implementation.
#define MHD_MD5_finish_reset(ctx, digest)
#define MHD_MD5_deinit(ignore)
#define Md5CtxWr
#define MHD_MD5_reset(ctx)
#define MHD_MD5_init_one_time(ctx)
#define NULL
uint64_t MHD_monotonic_msec_counter(void)
internal monotonic clock functions implementations
#define _(String)
Definition mhd_options.h:42
#define MHD_DATA_TRUNCATION_RUNTIME_CHECK_DISABLE_
#define _MHD_EXTERN
Definition mhd_options.h:53
#define MHD_DATA_TRUNCATION_RUNTIME_CHECK_RESTORE_
Simple wrapper for selection of built-in/external SHA-256 implementation.
#define Sha256CtxWr
#define MHD_SHA256_deinit(ignore)
#define MHD_SHA256_init_one_time(ctx)
#define MHD_SHA256_reset(ctx)
#define MHD_SHA256_finish_reset(ctx, digest)
size_t MHD_bin_to_hex(const void *bin, size_t size, char *hex)
Definition mhd_str.c:1677
size_t MHD_str_pct_decode_strict_n_(const char *pct_encoded, size_t pct_encoded_len, char *decoded, size_t buf_size)
Definition mhd_str.c:1750
size_t MHD_bin_to_hex_z(const void *bin, size_t size, char *hex)
Definition mhd_str.c:1697
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition mhd_str.c:1416
bool MHD_str_equal_caseless_bin_n_(const char *const str1, const char *const str2, size_t len)
Definition mhd_str.c:750
size_t MHD_hex_to_bin(const char *hex, size_t len, void *bin)
Definition mhd_str.c:1711
Header for string manipulating helpers.
#define MHD_STATICSTR_LEN_(macro)
MHD_DigestAuthResult
@ MHD_DAUTH_NONCE_WRONG
@ MHD_DAUTH_RESPONSE_WRONG
@ MHD_DAUTH_WRONG_HEADER
@ MHD_DAUTH_NONCE_STALE
@ MHD_DAUTH_ERROR
@ MHD_DAUTH_WRONG_QOP
@ MHD_DAUTH_OK
@ MHD_DAUTH_NONCE_OTHER_COND
@ MHD_DAUTH_WRONG_REALM
@ MHD_DAUTH_WRONG_URI
@ MHD_DAUTH_WRONG_ALGO
@ MHD_DAUTH_TOO_LARGE
@ MHD_DAUTH_WRONG_USERNAME
MHD_Result
Definition microhttpd.h:163
@ MHD_YES
Definition microhttpd.h:172
@ MHD_NO
Definition microhttpd.h:167
#define MHD_DIGEST_AUTH_ALGO3_SESSION
#define MHD_DIGEST_AUTH_ALGO3_NON_SESSION
MHD_DigestAuthMultiQOP
@ MHD_DIGEST_AUTH_MULT_QOP_AUTH_INT
@ MHD_DIGEST_AUTH_MULT_QOP_AUTH
@ MHD_DIGEST_AUTH_MULT_QOP_NONE
MHD_DigestBaseAlgo
@ MHD_DIGEST_BASE_ALGO_INVALID
@ MHD_DIGEST_BASE_ALGO_SHA256
@ MHD_DIGEST_BASE_ALGO_SHA512_256
@ MHD_DIGEST_BASE_ALGO_MD5
MHD_DigestAuthAlgo3
@ MHD_DIGEST_AUTH_ALGO3_MD5
@ MHD_DIGEST_AUTH_ALGO3_SHA256
@ MHD_DIGEST_AUTH_ALGO3_INVALID
@ MHD_DIGEST_AUTH_ALGO3_SHA512_256
MHD_DigestAuthQOP
@ MHD_DIGEST_AUTH_QOP_INVALID
@ MHD_DIGEST_AUTH_QOP_NONE
@ MHD_DIGEST_AUTH_QOP_AUTH_INT
void * data
MHD_DigestAuthUsernameType
@ MHD_DIGEST_AUTH_UNAME_TYPE_STANDARD
@ MHD_DIGEST_AUTH_UNAME_TYPE_MISSING
@ MHD_DIGEST_AUTH_UNAME_TYPE_INVALID
@ MHD_DIGEST_AUTH_UNAME_TYPE_EXTENDED
@ MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH
MHD_ValueKind
@ MHD_HEADER_KIND
@ MHD_GET_ARGUMENT_KIND
#define MHD_DIGEST_AUTH_INVALID_NC_VALUE
MHD_DigestAuthMultiAlgo3
@ MHD_DIGEST_AUTH_MULT_ALGO3_SHA256
@ MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION
@ MHD_DIGEST_AUTH_MULT_ALGO3_MD5
MHD_DigestAuthAlgorithm
@ MHD_DIGEST_ALG_AUTO
@ MHD_DIGEST_ALG_MD5
@ MHD_DIGEST_ALG_SHA256
@ MHD_DAUTH_BIND_NONCE_NONE
@ MHD_DAUTH_BIND_NONCE_URI
@ MHD_DAUTH_BIND_NONCE_URI_PARAMS
@ MHD_DAUTH_BIND_NONCE_REALM
@ MHD_DAUTH_BIND_NONCE_CLIENT_IP
platform-specific includes for libmicrohttpd
bool MHD_add_response_entry_no_alloc_(struct MHD_Response *response, enum MHD_ValueKind kind, char *header, size_t header_len, char *content, size_t content_len)
Definition response.c:167
Methods for managing response objects.
void MHD_SHA256_update(struct Sha256Ctx *ctx, const uint8_t *data, size_t length)
Definition sha256.c:410
void MHD_SHA256_finish(struct Sha256Ctx *ctx, uint8_t digest[SHA256_DIGEST_SIZE])
Definition sha256.c:473
#define SHA256_DIGEST_SIZE
Definition sha256.h:55
void MHD_SHA512_256_finish(struct Sha512_256Ctx *ctx, uint8_t digest[SHA512_256_DIGEST_SIZE])
Definition sha512_256.c:539
void MHD_SHA512_256_init(struct Sha512_256Ctx *ctx)
Definition sha512_256.c:41
void MHD_SHA512_256_update(struct Sha512_256Ctx *ctx, const uint8_t *data, size_t length)
Definition sha512_256.c:460
Calculation of SHA-512/256 digest.
#define SHA512_256_DIGEST_SIZE
Definition sha512_256.h:62
socklen_t addr_len
Definition internal.h:1469
struct MHD_Request rq
Definition internal.h:1371
struct MHD_Daemon * daemon
Definition internal.h:1366
struct sockaddr_storage * addr
Definition internal.h:1421
void * unescape_callback_cls
Definition internal.h:2078
UnescapeCallback unescape_callback
Definition internal.h:2073
enum MHD_DigestAuthQOP qop
enum MHD_DigestAuthUsernameType uname_type
enum MHD_DigestAuthAlgo3 algo3
enum MHD_DigestAuthAlgo3 algo3
enum MHD_DigestAuthUsernameType uname_type
enum MHD_ValueKind kind
Definition internal.h:396
const char * value
Definition internal.h:386
struct MHD_HTTP_Req_Header * next
Definition internal.h:366
const char * header
Definition internal.h:376
uint32_t nc
Definition internal.h:285
uint64_t nmask
Definition internal.h:293
char nonce[MAX_DIGEST_NONCE_LENGTH+1]
Definition internal.h:298
struct MHD_HTTP_Req_Header * headers_received
Definition internal.h:1117
const char * url
Definition internal.h:1096
size_t url_len
Definition internal.h:1101
const char * method
Definition internal.h:1085
enum MHD_HTTP_Method http_mthd
Definition internal.h:1090
const char * str