GNU libmicrohttpd 0.9.71
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
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
18*/
26#include "platform.h"
27#include "mhd_limits.h"
28#include "internal.h"
29#include "md5.h"
30#include "sha256.h"
31#include "mhd_mono_clock.h"
32#include "mhd_str.h"
33#include "mhd_compat.h"
34
35#if defined(MHD_W32_MUTEX_)
36#ifndef WIN32_LEAN_AND_MEAN
37#define WIN32_LEAN_AND_MEAN 1
38#endif /* !WIN32_LEAN_AND_MEAN */
39#include <windows.h>
40#endif /* MHD_W32_MUTEX_ */
41
45#define TIMESTAMP_BIN_SIZE 4
46
52#define NONCE_STD_LEN(digest_size) \
53 ((digest_size) * 2 + TIMESTAMP_BIN_SIZE * 2)
54
55
60#define MAX_DIGEST SHA256_DIGEST_SIZE
61
65#if __STDC_NO_VLA__
71#define VLA_ARRAY_LEN_DIGEST(n) (MAX_DIGEST)
72
73#else
79#define VLA_ARRAY_LEN_DIGEST(n) (n)
80#endif
81
85#define VLA_CHECK_LEN_DIGEST(n) do { if ((n) > MAX_DIGEST) mhd_panic ( \
86 mhd_panic_cls, __FILE__, __LINE__, \
87 "VLA too big.\n"); } while (0)
88
89
93#define _BASE "Digest "
94
98#define MAX_USERNAME_LENGTH 128
99
103#define MAX_REALM_LENGTH 256
104
108#define MAX_AUTH_RESPONSE_LENGTH 256
109
110
116struct DigestAlgorithm
117{
121 unsigned int digest_size;
122
127 void *ctx;
128
132 const char *alg;
133
137 char *sessionkey;
138
142 void
143 (*init)(void *ctx);
144
152 void
153 (*update)(void *ctx,
154 const uint8_t *data,
155 size_t length);
156
164 void
165 (*digest)(void *ctx,
166 uint8_t *digest);
167};
168
169
177static void
178cvthex (const unsigned char *bin,
179 size_t len,
180 char *hex)
181{
182 size_t i;
183 unsigned int j;
184
185 for (i = 0; i < len; ++i)
186 {
187 j = (bin[i] >> 4) & 0x0f;
188 hex[i * 2] = (char) ((j <= 9) ? (j + '0') : (j - 10 + 'a'));
189 j = bin[i] & 0x0f;
190 hex[i * 2 + 1] = (char) ((j <= 9) ? (j + '0') : (j - 10 + 'a'));
191 }
192 hex[len * 2] = '\0';
193}
194
195
211static void
213 struct DigestAlgorithm *da,
214 const uint8_t *digest,
215 const char *nonce,
216 const char *cnonce)
217{
218 if ( (MHD_str_equal_caseless_ (alg,
219 "md5-sess")) ||
221 "sha-256-sess")) )
222 {
223 uint8_t dig[VLA_ARRAY_LEN_DIGEST (da->digest_size)];
224
225 VLA_CHECK_LEN_DIGEST (da->digest_size);
226 da->init (da->ctx);
227 da->update (da->ctx,
228 digest,
230 da->update (da->ctx,
231 (const unsigned char *) ":",
232 1);
233 da->update (da->ctx,
234 (const unsigned char *) nonce,
235 strlen (nonce));
236 da->update (da->ctx,
237 (const unsigned char *) ":",
238 1);
239 da->update (da->ctx,
240 (const unsigned char *) cnonce,
241 strlen (cnonce));
242 da->digest (da->ctx,
243 dig);
244 cvthex (dig,
245 sizeof (dig),
246 da->sessionkey);
247 }
248 else
249 {
250 cvthex (digest,
251 da->digest_size,
252 da->sessionkey);
253 }
254}
255
256
271static void
273 const char *username,
274 const char *realm,
275 const char *password,
276 const char *nonce,
277 const char *cnonce,
278 struct DigestAlgorithm *da)
279{
280 unsigned char ha1[VLA_ARRAY_LEN_DIGEST (da->digest_size)];
281
282 VLA_CHECK_LEN_DIGEST (da->digest_size);
283 da->init (da->ctx);
284 da->update (da->ctx,
285 (const unsigned char *) username,
286 strlen (username));
287 da->update (da->ctx,
288 (const unsigned char *) ":",
289 1);
290 da->update (da->ctx,
291 (const unsigned char *) realm,
292 strlen (realm));
293 da->update (da->ctx,
294 (const unsigned char *) ":",
295 1);
296 da->update (da->ctx,
297 (const unsigned char *) password,
298 strlen (password));
299 da->digest (da->ctx,
300 ha1);
302 da,
303 ha1,
304 nonce,
305 cnonce);
306}
307
308
325static void
326digest_calc_response (const char *ha1,
327 const char *nonce,
328 const char *noncecount,
329 const char *cnonce,
330 const char *qop,
331 const char *method,
332 const char *uri,
333 const char *hentity,
334 struct DigestAlgorithm *da)
335{
336 unsigned char ha2[VLA_ARRAY_LEN_DIGEST (da->digest_size)];
337 unsigned char resphash[VLA_ARRAY_LEN_DIGEST (da->digest_size)];
338 (void) hentity; /* Unused. Silence compiler warning. */
339
340 VLA_CHECK_LEN_DIGEST (da->digest_size);
341 da->init (da->ctx);
342 da->update (da->ctx,
343 (const unsigned char *) method,
344 strlen (method));
345 da->update (da->ctx,
346 (const unsigned char *) ":",
347 1);
348 da->update (da->ctx,
349 (const unsigned char *) uri,
350 strlen (uri));
351#if 0
352 if (0 == strcasecmp (qop,
353 "auth-int"))
354 {
355 /* This is dead code since the rest of this module does
356 not support auth-int. */
357 da->update (da->ctx,
358 ":",
359 1);
360 if (NULL != hentity)
361 da->update (da->ctx,
362 hentity,
363 strlen (hentity));
364 }
365#endif
366 da->digest (da->ctx,
367 ha2);
368 cvthex (ha2,
369 da->digest_size,
370 da->sessionkey);
371 da->init (da->ctx);
372 /* calculate response */
373 da->update (da->ctx,
374 (const unsigned char *) ha1,
375 da->digest_size * 2);
376 da->update (da->ctx,
377 (const unsigned char *) ":",
378 1);
379 da->update (da->ctx,
380 (const unsigned char *) nonce,
381 strlen (nonce));
382 da->update (da->ctx,
383 (const unsigned char*) ":",
384 1);
385 if ('\0' != *qop)
386 {
387 da->update (da->ctx,
388 (const unsigned char *) noncecount,
389 strlen (noncecount));
390 da->update (da->ctx,
391 (const unsigned char *) ":",
392 1);
393 da->update (da->ctx,
394 (const unsigned char *) cnonce,
395 strlen (cnonce));
396 da->update (da->ctx,
397 (const unsigned char *) ":",
398 1);
399 da->update (da->ctx,
400 (const unsigned char *) qop,
401 strlen (qop));
402 da->update (da->ctx,
403 (const unsigned char *) ":",
404 1);
405 }
406 da->update (da->ctx,
407 (const unsigned char *) da->sessionkey,
408 da->digest_size * 2);
409 da->digest (da->ctx,
410 resphash);
411 cvthex (resphash,
412 sizeof(resphash),
413 da->sessionkey);
414}
415
416
431static size_t
433 size_t size,
434 const char *data,
435 const char *key)
436{
437 size_t keylen;
438 size_t len;
439 const char *ptr;
440 const char *eq;
441 const char *q1;
442 const char *q2;
443 const char *qn;
444
445 if (0 == size)
446 return 0;
447 keylen = strlen (key);
448 ptr = data;
449 while ('\0' != *ptr)
450 {
451 if (NULL == (eq = strchr (ptr,
452 '=')))
453 return 0;
454 q1 = eq + 1;
455 while (' ' == *q1)
456 q1++;
457 if ('\"' != *q1)
458 {
459 q2 = strchr (q1,
460 ',');
461 qn = q2;
462 }
463 else
464 {
465 q1++;
466 q2 = strchr (q1,
467 '\"');
468 if (NULL == q2)
469 return 0; /* end quote not found */
470 qn = q2 + 1;
471 }
472 if ( (MHD_str_equal_caseless_n_ (ptr,
473 key,
474 keylen)) &&
475 (eq == &ptr[keylen]) )
476 {
477 if (NULL == q2)
478 {
479 len = strlen (q1) + 1;
480 if (size > len)
481 size = len;
482 size--;
483 memcpy (dest,
484 q1,
485 size);
486 dest[size] = '\0';
487 return size;
488 }
489 else
490 {
491 if (size > (size_t) ((q2 - q1) + 1))
492 size = (q2 - q1) + 1;
493 size--;
494 memcpy (dest,
495 q1,
496 size);
497 dest[size] = '\0';
498 return size;
499 }
500 }
501 if (NULL == qn)
502 return 0;
503 ptr = strchr (qn,
504 ',');
505 if (NULL == ptr)
506 return 0;
507 ptr++;
508 while (' ' == *ptr)
509 ptr++;
510 }
511 return 0;
512}
513
514
524static enum MHD_Result
525check_nonce_nc (struct MHD_Connection *connection,
526 const char *nonce,
527 uint64_t nc)
528{
529 struct MHD_Daemon *daemon = connection->daemon;
530 struct MHD_NonceNc *nn;
531 uint32_t off;
532 uint32_t mod;
533 const char *np;
534 size_t noncelen;
535
536 noncelen = strlen (nonce) + 1;
537 if (MAX_NONCE_LENGTH < noncelen)
538 return MHD_NO; /* This should be impossible, but static analysis
539 tools have a hard time with it *and* this also
540 protects against unsafe modifications that may
541 happen in the future... */
542 mod = daemon->nonce_nc_size;
543 if (0 == mod)
544 return MHD_NO; /* no array! */
545 /* super-fast xor-based "hash" function for HT lookup in nonce array */
546 off = 0;
547 np = nonce;
548 while ('\0' != *np)
549 {
550 off = (off << 8) | (*np ^ (off >> 24));
551 np++;
552 }
553 off = off % mod;
554 /*
555 * Look for the nonce, if it does exist and its corresponding
556 * nonce counter is less than the current nonce counter by 1,
557 * then only increase the nonce counter by one.
558 */nn = &daemon->nnc[off];
559#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
560 MHD_mutex_lock_chk_ (&daemon->nnc_lock);
561#endif
562 if (0 == nc)
563 {
564 /* Fresh nonce, reinitialize array */
565 memcpy (nn->nonce,
566 nonce,
567 noncelen);
568 nn->nc = 0;
569 nn->nmask = 0;
570#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
571 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
572#endif
573 return MHD_YES;
574 }
575 /* Note that we use 64 here, as we do not store the
576 bit for 'nn->nc' itself in 'nn->nmask' */
577 if ( (nc < nn->nc) &&
578 (nc + 64 > nc /* checking for overflow */) &&
579 (nc + 64 >= nn->nc) &&
580 (0 == ((1LLU << (nn->nc - nc - 1)) & nn->nmask)) )
581 {
582 /* Out-of-order nonce, but within 64-bit bitmask, set bit */
583 nn->nmask |= (1LLU << (nn->nc - nc - 1));
584#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
585 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
586#endif
587 return MHD_YES;
588 }
589
590 if ( (nc <= nn->nc) ||
591 (0 != strcmp (nn->nonce,
592 nonce)) )
593 {
594 /* Nonce does not match, fail */
595#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
596 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
597#endif
598#ifdef HAVE_MESSAGES
599 MHD_DLOG (daemon,
600 _ (
601 "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n"));
602#endif
603 return MHD_NO;
604 }
605 /* Nonce is larger, shift bitmask and bump limit */
606 if (64 > nc - nn->nc)
607 nn->nmask <<= (nc - nn->nc); /* small jump, less than mask width */
608 else
609 nn->nmask = 0; /* big jump, unset all bits in the mask */
610 nn->nc = nc;
611#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
612 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
613#endif
614 return MHD_YES;
615}
616
617
627char *
629{
630 size_t len;
631 char user[MAX_USERNAME_LENGTH];
632 const char *header;
633
634 if (MHD_NO == MHD_lookup_connection_value_n (connection,
639 &header,
640 NULL))
641 return NULL;
642 if (0 != strncmp (header,
643 _BASE,
645 return NULL;
646 header += MHD_STATICSTR_LEN_ (_BASE);
647 if (0 == (len = lookup_sub_value (user,
648 sizeof (user),
649 header,
650 "username")))
651 return NULL;
652 return strdup (user);
653}
654
655
671static void
672calculate_nonce (uint32_t nonce_time,
673 const char *method,
674 const char *rnd,
675 size_t rnd_size,
676 const char *uri,
677 const char *realm,
678 struct DigestAlgorithm *da,
679 char *nonce)
680{
681 unsigned char timestamp[TIMESTAMP_BIN_SIZE];
682 unsigned char tmpnonce[VLA_ARRAY_LEN_DIGEST (da->digest_size)];
683 char timestamphex[TIMESTAMP_BIN_SIZE * 2 + 1];
684
685 VLA_CHECK_LEN_DIGEST (da->digest_size);
686 da->init (da->ctx);
687 timestamp[0] = (unsigned char) ((nonce_time & 0xff000000) >> 0x18);
688 timestamp[1] = (unsigned char) ((nonce_time & 0x00ff0000) >> 0x10);
689 timestamp[2] = (unsigned char) ((nonce_time & 0x0000ff00) >> 0x08);
690 timestamp[3] = (unsigned char) ((nonce_time & 0x000000ff));
691 da->update (da->ctx,
692 timestamp,
693 sizeof (timestamp));
694 da->update (da->ctx,
695 (const unsigned char *) ":",
696 1);
697 da->update (da->ctx,
698 (const unsigned char *) method,
699 strlen (method));
700 da->update (da->ctx,
701 (const unsigned char *) ":",
702 1);
703 if (rnd_size > 0)
704 da->update (da->ctx,
705 (const unsigned char *) rnd,
706 rnd_size);
707 da->update (da->ctx,
708 (const unsigned char *) ":",
709 1);
710 da->update (da->ctx,
711 (const unsigned char *) uri,
712 strlen (uri));
713 da->update (da->ctx,
714 (const unsigned char *) ":",
715 1);
716 da->update (da->ctx,
717 (const unsigned char *) realm,
718 strlen (realm));
719 da->digest (da->ctx,
720 tmpnonce);
721 cvthex (tmpnonce,
722 sizeof (tmpnonce),
723 nonce);
724 cvthex (timestamp,
725 sizeof (timestamp),
726 timestamphex);
727 strncat (nonce,
728 timestamphex,
729 8);
730}
731
732
746static enum MHD_Result
747test_header (struct MHD_Connection *connection,
748 const char *key,
749 size_t key_size,
750 const char *value,
751 size_t value_size,
752 enum MHD_ValueKind kind)
753{
754 struct MHD_HTTP_Header *pos;
755
756 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
757 {
758 if (kind != pos->kind)
759 continue;
760 if (key_size != pos->header_size)
761 continue;
762 if (value_size != pos->value_size)
763 continue;
764 if (0 != memcmp (key,
765 pos->header,
766 key_size))
767 continue;
768 if ( (NULL == value) &&
769 (NULL == pos->value) )
770 return MHD_YES;
771 if ( (NULL == value) ||
772 (NULL == pos->value) ||
773 (0 != memcmp (value,
774 pos->value,
775 value_size)) )
776 continue;
777 return MHD_YES;
778 }
779 return MHD_NO;
780}
781
782
793static enum MHD_Result
795 const char *args)
796{
797 struct MHD_HTTP_Header *pos;
798 char *argb;
799 unsigned int num_headers;
800 enum MHD_Result ret;
801
802 argb = strdup (args);
803 if (NULL == argb)
804 {
805#ifdef HAVE_MESSAGES
806 MHD_DLOG (connection->daemon,
807 _ ("Failed to allocate memory for copy of URI arguments.\n"));
808#endif /* HAVE_MESSAGES */
809 return MHD_NO;
810 }
811 ret = MHD_parse_arguments_ (connection,
813 argb,
815 &num_headers);
816 free (argb);
817 if (MHD_YES != ret)
818 {
819 return MHD_NO;
820 }
821 /* also check that the number of headers matches */
822 for (pos = connection->headers_received; NULL != pos; pos = pos->next)
823 {
824 if (MHD_GET_ARGUMENT_KIND != pos->kind)
825 continue;
826 num_headers--;
827 }
828 if (0 != num_headers)
829 {
830 /* argument count mismatch */
831 return MHD_NO;
832 }
833 return MHD_YES;
834}
835
836
856static int
858 struct DigestAlgorithm *da,
859 const char *realm,
860 const char *username,
861 const char *password,
862 const uint8_t *digest,
863 unsigned int nonce_timeout)
864{
865 struct MHD_Daemon *daemon = connection->daemon;
866 size_t len;
867 const char *header;
868 char nonce[MAX_NONCE_LENGTH];
869 char cnonce[MAX_NONCE_LENGTH];
870 char ha1[VLA_ARRAY_LEN_DIGEST (da->digest_size) * 2 + 1];
871 char qop[15]; /* auth,auth-int */
872 char nc[20];
873 char response[MAX_AUTH_RESPONSE_LENGTH];
874 const char *hentity = NULL; /* "auth-int" is not supported */
875 char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da->digest_size)) + 1];
876 uint32_t nonce_time;
877 uint32_t t;
878 size_t left; /* number of characters left in 'header' for 'uri' */
879 uint64_t nci;
880 char *qmark;
881
882 VLA_CHECK_LEN_DIGEST (da->digest_size);
883 if (MHD_NO == MHD_lookup_connection_value_n (connection,
888 &header,
889 NULL))
890 return MHD_NO;
891 if (0 != strncmp (header,
892 _BASE,
894 return MHD_NO;
895 header += MHD_STATICSTR_LEN_ (_BASE);
896 left = strlen (header);
897
898 {
899 char un[MAX_USERNAME_LENGTH];
900
901 len = lookup_sub_value (un,
902 sizeof (un),
903 header,
904 "username");
905 if ( (0 == len) ||
906 (0 != strcmp (username,
907 un)) )
908 return MHD_NO;
909 left -= strlen ("username") + len;
910 }
911
912 {
913 char r[MAX_REALM_LENGTH];
914
915 len = lookup_sub_value (r,
916 sizeof (r),
917 header,
918 "realm");
919 if ( (0 == len) ||
920 (0 != strcmp (realm,
921 r)) )
922 return MHD_NO;
923 left -= strlen ("realm") + len;
924 }
925
926 if (0 == (len = lookup_sub_value (nonce,
927 sizeof (nonce),
928 header,
929 "nonce")))
930 return MHD_NO;
931 left -= strlen ("nonce") + len;
932 if (left > 32 * 1024)
933 {
934 /* we do not permit URIs longer than 32k, as we want to
935 make sure to not blow our stack (or per-connection
936 heap memory limit). Besides, 32k is already insanely
937 large, but of course in theory the
938 #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
939 and would thus permit sending a >32k authorization
940 header value. */return MHD_NO;
941 }
942 if (TIMESTAMP_BIN_SIZE * 2 !=
943 MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_BIN_SIZE * 2,
945 &nonce_time))
946 {
947#ifdef HAVE_MESSAGES
948 MHD_DLOG (daemon,
949 _ ("Authentication failed, invalid timestamp format.\n"));
950#endif
951 return MHD_NO;
952 }
953 t = (uint32_t) MHD_monotonic_sec_counter ();
954 /*
955 * First level vetting for the nonce validity: if the timestamp
956 * attached to the nonce exceeds `nonce_timeout', then the nonce is
957 * invalid.
958 */if ( (t > nonce_time + nonce_timeout) ||
959 (nonce_time + nonce_timeout < nonce_time) )
960 {
961 /* too old */
962 return MHD_INVALID_NONCE;
963 }
964
965 calculate_nonce (nonce_time,
966 connection->method,
967 daemon->digest_auth_random,
968 daemon->digest_auth_rand_size,
969 connection->url,
970 realm,
971 da,
972 noncehashexp);
973 /*
974 * Second level vetting for the nonce validity
975 * if the timestamp attached to the nonce is valid
976 * and possibly fabricated (in case of an attack)
977 * the attacker must also know the random seed to be
978 * able to generate a "sane" nonce, which if he does
979 * not, the nonce fabrication process going to be
980 * very hard to achieve.
981 */if (0 != strcmp (nonce,
982 noncehashexp))
983 {
984 return MHD_INVALID_NONCE;
985 }
986 if ( (0 == lookup_sub_value (cnonce,
987 sizeof (cnonce),
988 header,
989 "cnonce")) ||
990 (0 == lookup_sub_value (qop,
991 sizeof (qop),
992 header,
993 "qop")) ||
994 ( (0 != strcmp (qop,
995 "auth")) &&
996 (0 != strcmp (qop,
997 "")) ) ||
998 (0 == (len = lookup_sub_value (nc,
999 sizeof (nc),
1000 header,
1001 "nc")) ) ||
1002 (0 == lookup_sub_value (response,
1003 sizeof (response),
1004 header,
1005 "response")) )
1006 {
1007#ifdef HAVE_MESSAGES
1008 MHD_DLOG (daemon,
1009 _ ("Authentication failed, invalid format.\n"));
1010#endif
1011 return MHD_NO;
1012 }
1013 if (len != MHD_strx_to_uint64_n_ (nc,
1014 len,
1015 &nci))
1016 {
1017#ifdef HAVE_MESSAGES
1018 MHD_DLOG (daemon,
1019 _ ("Authentication failed, invalid nc format.\n"));
1020#endif
1021 return MHD_NO; /* invalid nonce format */
1022 }
1023
1024 /*
1025 * Checking if that combination of nonce and nc is sound
1026 * and not a replay attack attempt. Also adds the nonce
1027 * to the nonce-nc map if it does not exist there.
1028 */if (MHD_YES !=
1029 check_nonce_nc (connection,
1030 nonce,
1031 nci))
1032 {
1033 return MHD_NO;
1034 }
1035
1036 {
1037 char *uri;
1038
1039 uri = malloc (left + 1);
1040 if (NULL == uri)
1041 {
1042#ifdef HAVE_MESSAGES
1043 MHD_DLOG (daemon,
1044 _ ("Failed to allocate memory for auth header processing.\n"));
1045#endif /* HAVE_MESSAGES */
1046 return MHD_NO;
1047 }
1048 if (0 == lookup_sub_value (uri,
1049 left + 1,
1050 header,
1051 "uri"))
1052 {
1053 free (uri);
1054 return MHD_NO;
1055 }
1056 if (NULL != digest)
1057 {
1058 /* This will initialize da->sessionkey (ha1) */
1060 da,
1061 digest,
1062 nonce,
1063 cnonce);
1064 }
1065 else
1066 {
1067 /* This will initialize da->sessionkey (ha1) */
1068 mhd_assert (NULL != password); /* NULL == digest => password != NULL */
1070 username,
1071 realm,
1072 password,
1073 nonce,
1074 cnonce,
1075 da);
1076 }
1077 memcpy (ha1,
1078 da->sessionkey,
1079 sizeof (ha1));
1080 /* This will initialize da->sessionkey (respexp) */
1082 nonce,
1083 nc,
1084 cnonce,
1085 qop,
1086 connection->method,
1087 uri,
1088 hentity,
1089 da);
1090 qmark = strchr (uri,
1091 '?');
1092 if (NULL != qmark)
1093 *qmark = '\0';
1094
1095 /* Need to unescape URI before comparing with connection->url */
1097 connection,
1098 uri);
1099 if (0 != strcmp (uri,
1100 connection->url))
1101 {
1102#ifdef HAVE_MESSAGES
1103 MHD_DLOG (daemon,
1104 _ ("Authentication failed, URI does not match.\n"));
1105#endif
1106 free (uri);
1107 return MHD_NO;
1108 }
1109
1110 {
1111 const char *args = qmark;
1112
1113 if (NULL == args)
1114 args = "";
1115 else
1116 args++;
1117 if (MHD_YES !=
1118 check_argument_match (connection,
1119 args) )
1120 {
1121#ifdef HAVE_MESSAGES
1122 MHD_DLOG (daemon,
1123 _ ("Authentication failed, arguments do not match.\n"));
1124#endif
1125 free (uri);
1126 return MHD_NO;
1127 }
1128 }
1129 free (uri);
1130 return (0 == strcmp (response,
1131 da->sessionkey))
1132 ? MHD_YES
1133 : MHD_NO;
1134 }
1135}
1136
1137
1155_MHD_EXTERN int
1157 const char *realm,
1158 const char *username,
1159 const char *password,
1160 unsigned int nonce_timeout)
1161{
1162 return MHD_digest_auth_check2 (connection,
1163 realm,
1164 username,
1165 password,
1166 nonce_timeout,
1168}
1169
1170
1179#define SETUP_DA(algo,da) \
1180 union { \
1181 struct MD5Context md5; \
1182 struct sha256_ctx sha256; \
1183 } ctx; \
1184 union { \
1185 char md5[MD5_DIGEST_SIZE * 2 + 1]; \
1186 char sha256[SHA256_DIGEST_SIZE * 2 + 1]; \
1187 } skey; \
1188 struct DigestAlgorithm da; \
1189 \
1190 do { \
1191 switch (algo) { \
1192 case MHD_DIGEST_ALG_MD5: \
1193 da.digest_size = MD5_DIGEST_SIZE; \
1194 da.ctx = &ctx.md5; \
1195 da.alg = "md5"; \
1196 da.sessionkey = skey.md5; \
1197 da.init = &MHD_MD5Init; \
1198 da.update = &MHD_MD5Update; \
1199 da.digest = &MHD_MD5Final; \
1200 break; \
1201 case MHD_DIGEST_ALG_AUTO: \
1202 /* auto == SHA256, fall-though thus intentional! */ \
1203 case MHD_DIGEST_ALG_SHA256: \
1204 da.digest_size = SHA256_DIGEST_SIZE; \
1205 da.ctx = &ctx.sha256; \
1206 da.alg = "sha-256"; \
1207 da.sessionkey = skey.sha256; \
1208 da.init = &MHD_SHA256_init; \
1209 da.update = &MHD_SHA256_update; \
1210 da.digest = &sha256_finish; \
1211 break; \
1212 } \
1213 } while (0)
1214
1215
1230_MHD_EXTERN int
1232 const char *realm,
1233 const char *username,
1234 const char *password,
1235 unsigned int nonce_timeout,
1236 enum MHD_DigestAuthAlgorithm algo)
1237{
1238 SETUP_DA (algo, da);
1239
1240 mhd_assert (NULL != password);
1241 return digest_auth_check_all (connection,
1242 &da,
1243 realm,
1244 username,
1245 password,
1246 NULL,
1247 nonce_timeout);
1248}
1249
1250
1268_MHD_EXTERN int
1270 const char *realm,
1271 const char *username,
1272 const uint8_t *digest,
1273 size_t digest_size,
1274 unsigned int nonce_timeout,
1275 enum MHD_DigestAuthAlgorithm algo)
1276{
1277 SETUP_DA (algo, da);
1278
1279 mhd_assert (NULL != digest);
1280 if (da.digest_size != digest_size)
1281 MHD_PANIC (_ ("Digest size mismatch.\n")); /* API violation! */
1282 return digest_auth_check_all (connection,
1283 &da,
1284 realm,
1285 username,
1286 NULL,
1287 digest,
1288 nonce_timeout);
1289}
1290
1291
1309_MHD_EXTERN int
1311 const char *realm,
1312 const char *username,
1313 const uint8_t digest[MHD_MD5_DIGEST_SIZE],
1314 unsigned int nonce_timeout)
1315{
1316 return MHD_digest_auth_check_digest2 (connection,
1317 realm,
1318 username,
1319 digest,
1321 nonce_timeout,
1323}
1324
1325
1341enum MHD_Result
1343 const char *realm,
1344 const char *opaque,
1345 struct MHD_Response *response,
1346 int signal_stale,
1347 enum MHD_DigestAuthAlgorithm algo)
1348{
1349 int ret;
1350 int hlen;
1351 SETUP_DA (algo, da);
1352
1353 {
1354 char nonce[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1];
1355
1356 VLA_CHECK_LEN_DIGEST (da.digest_size);
1357 /* Generating the server nonce */
1359 connection->method,
1360 connection->daemon->digest_auth_random,
1361 connection->daemon->digest_auth_rand_size,
1362 connection->url,
1363 realm,
1364 &da,
1365 nonce);
1366 if (MHD_YES !=
1367 check_nonce_nc (connection,
1368 nonce,
1369 0))
1370 {
1371#ifdef HAVE_MESSAGES
1372 MHD_DLOG (connection->daemon,
1373 _ (
1374 "Could not register nonce (is the nonce array size zero?).\n"));
1375#endif
1376 return MHD_NO;
1377 }
1378 /* Building the authentication header */
1379 hlen = MHD_snprintf_ (NULL,
1380 0,
1381 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1382 realm,
1383 nonce,
1384 opaque,
1385 da.alg,
1386 signal_stale
1387 ? ",stale=\"true\""
1388 : "");
1389 if (hlen > 0)
1390 {
1391 char *header;
1392
1393 header = MHD_calloc_ (1,
1394 hlen + 1);
1395 if (NULL == header)
1396 {
1397#ifdef HAVE_MESSAGES
1398 MHD_DLOG (connection->daemon,
1399 _ ("Failed to allocate memory for auth response header.\n"));
1400#endif /* HAVE_MESSAGES */
1401 return MHD_NO;
1402 }
1403
1404 if (MHD_snprintf_ (header,
1405 hlen + 1,
1406 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1407 realm,
1408 nonce,
1409 opaque,
1410 da.alg,
1411 signal_stale
1412 ? ",stale=\"true\""
1413 : "") == hlen)
1414 ret = MHD_add_response_header (response,
1416 header);
1417 else
1418 ret = MHD_NO;
1419#if 0
1420 if ( (MHD_YES == ret) && (AND in state : 100 continue aborting ...))
1421 ret = MHD_add_response_header (response,
1423 "close");
1424#endif
1425 free (header);
1426 }
1427 else
1428 ret = MHD_NO;
1429 }
1430
1431 if (MHD_YES == ret)
1432 {
1433 ret = MHD_queue_response (connection,
1435 response);
1436 }
1437 else
1438 {
1439#ifdef HAVE_MESSAGES
1440 MHD_DLOG (connection->daemon,
1441 _ ("Failed to add Digest auth header.\n"));
1442#endif /* HAVE_MESSAGES */
1443 }
1444 return ret;
1445}
1446
1447
1464enum MHD_Result
1466 const char *realm,
1467 const char *opaque,
1468 struct MHD_Response *response,
1469 int signal_stale)
1470{
1471 return MHD_queue_auth_fail_response2 (connection,
1472 realm,
1473 opaque,
1474 response,
1475 signal_stale,
1477}
1478
1479
1480/* end of digestauth.c */
#define VLA_CHECK_LEN_DIGEST(n)
Definition: digestauth.c:85
#define SETUP_DA(algo, da)
Definition: digestauth.c:1179
static void digest_calc_response(const char *ha1, const char *nonce, const char *noncecount, const char *cnonce, const char *qop, const char *method, const char *uri, const char *hentity, struct DigestAlgorithm *da)
Definition: digestauth.c:326
#define MAX_REALM_LENGTH
Definition: digestauth.c:103
#define NONCE_STD_LEN(digest_size)
Definition: digestauth.c:52
static enum MHD_Result check_argument_match(struct MHD_Connection *connection, const char *args)
Definition: digestauth.c:794
#define MAX_AUTH_RESPONSE_LENGTH
Definition: digestauth.c:108
#define TIMESTAMP_BIN_SIZE
Definition: digestauth.c:45
static void digest_calc_ha1_from_user(const char *alg, const char *username, const char *realm, const char *password, const char *nonce, const char *cnonce, struct DigestAlgorithm *da)
Definition: digestauth.c:272
static size_t lookup_sub_value(char *dest, size_t size, const char *data, const char *key)
Definition: digestauth.c:432
#define _BASE
Definition: digestauth.c:93
static enum MHD_Result check_nonce_nc(struct MHD_Connection *connection, const char *nonce, uint64_t nc)
Definition: digestauth.c:525
#define MAX_USERNAME_LENGTH
Definition: digestauth.c:98
static void calculate_nonce(uint32_t nonce_time, const char *method, const char *rnd, size_t rnd_size, const char *uri, const char *realm, struct DigestAlgorithm *da, char *nonce)
Definition: digestauth.c:672
static enum MHD_Result test_header(struct MHD_Connection *connection, const char *key, size_t key_size, const char *value, size_t value_size, enum MHD_ValueKind kind)
Definition: digestauth.c:747
#define VLA_ARRAY_LEN_DIGEST(n)
Definition: digestauth.c:79
static void digest_calc_ha1_from_digest(const char *alg, struct DigestAlgorithm *da, const uint8_t *digest, const char *nonce, const char *cnonce)
Definition: digestauth.c:212
static void cvthex(const unsigned char *bin, size_t len, char *hex)
Definition: digestauth.c:178
_MHD_EXTERN char * MHD_digest_auth_get_username(struct MHD_Connection *connection)
Definition: digestauth.c:628
_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)
Definition: digestauth.c:1231
_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)
Definition: digestauth.c:1342
_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)
Definition: digestauth.c:1269
_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)
Definition: digestauth.c:1465
static int digest_auth_check_all(struct MHD_Connection *connection, struct DigestAlgorithm *da, const char *realm, const char *username, const char *password, const uint8_t *digest, unsigned int nonce_timeout)
Definition: digestauth.c:857
_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)
Definition: digestauth.c:1310
_MHD_EXTERN int MHD_digest_auth_check(struct MHD_Connection *connection, const char *realm, const char *username, const char *password, unsigned int nonce_timeout)
Definition: digestauth.c:1156
#define MHD_INVALID_NONCE
Definition: microhttpd.h:3508
#define MHD_HTTP_HEADER_CONNECTION
Definition: microhttpd.h:568
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition: microhttpd.h:562
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition: microhttpd.h:640
#define MHD_HTTP_UNAUTHORIZED
Definition: microhttpd.h:386
_MHD_EXTERN enum MHD_Result MHD_lookup_connection_value_n(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key, size_t key_size, const char **value_ptr, size_t *value_size_ptr)
Definition: connection.c:512
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
Definition: connection.c:3903
_MHD_EXTERN enum MHD_Result MHD_add_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition: response.c:133
bool MHD_parse_arguments_(struct MHD_Request *request, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, unsigned int *num_headers)
Definition: internal.c:190
#define MHD_PANIC(msg)
Definition: internal.h:69
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
void * MHD_calloc_(size_t nelem, size_t elsize)
Definition: mhd_compat.c:98
#define MHD_mutex_unlock_chk_(pmutex)
Definition: mhd_locks.h:180
#define MHD_mutex_lock_chk_(pmutex)
Definition: mhd_locks.h:154
time_t MHD_monotonic_sec_counter(void)
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition: mhd_str.c:346
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition: mhd_str.c:692
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition: mhd_str.c:378
size_t MHD_strx_to_uint32_n_(const char *str, size_t maxlen, uint32_t *out_val)
Definition: mhd_str.c:605
#define MHD_STATICSTR_LEN_(macro)
Definition: mhd_str.h:45
#define NULL
Definition: reason_phrase.c:30
#define _(String)
Definition: mhd_options.h:42
#define _MHD_EXTERN
Definition: mhd_options.h:50
internal shared structures
#define MAX_NONCE_LENGTH
Definition: internal.h:220
Header for platform missing functions.
limits values definitions
internal monotonic clock functions implementations
Header for string manipulating helpers.
MHD_Result
Definition: microhttpd.h:141
@ MHD_YES
Definition: microhttpd.h:150
@ MHD_NO
Definition: microhttpd.h:145
void * data
Definition: microhttpd.h:3038
MHD_ValueKind
Definition: microhttpd.h:1766
@ MHD_HEADER_KIND
Definition: microhttpd.h:1781
@ MHD_GET_ARGUMENT_KIND
Definition: microhttpd.h:1802
MHD_DigestAuthAlgorithm
Definition: microhttpd.h:3539
@ MHD_DIGEST_ALG_MD5
Definition: microhttpd.h:3549
#define MHD_MD5_DIGEST_SIZE
Definition: microhttpd.h:322
platform-specific includes for libmicrohttpd
Calculation of SHA-256 digest.
const char * url
Definition: internal.h:718
struct MHD_HTTP_Header * headers_received
Definition: internal.h:670
char * method
Definition: internal.h:712
struct MHD_Daemon * daemon
Definition: internal.h:675
void * unescape_callback_cls
Definition: internal.h:1412
UnescapeCallback unescape_callback
Definition: internal.h:1407
size_t value_size
Definition: internal.h:290
char * header
Definition: internal.h:347
enum MHD_ValueKind kind
Definition: internal.h:358
size_t header_size
Definition: internal.h:280
struct MHD_HTTP_Header * next
Definition: internal.h:342
char * value
Definition: internal.h:352
uint64_t nc
Definition: internal.h:234
uint64_t nmask
Definition: internal.h:240
char nonce[MAX_NONCE_LENGTH]
Definition: internal.h:245