GNU libmicrohttpd 0.9.73
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
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#include "mhd_assert.h"
35
36#if defined(MHD_W32_MUTEX_)
37#ifndef WIN32_LEAN_AND_MEAN
38#define WIN32_LEAN_AND_MEAN 1
39#endif /* !WIN32_LEAN_AND_MEAN */
40#include <windows.h>
41#endif /* MHD_W32_MUTEX_ */
42
46#define TIMESTAMP_BIN_SIZE 4
47
53#define NONCE_STD_LEN(digest_size) \
54 ((digest_size) * 2 + TIMESTAMP_BIN_SIZE * 2)
55
56
61#define MAX_DIGEST SHA256_DIGEST_SIZE
62
66#ifndef HAVE_C_VARARRAYS
72#define VLA_ARRAY_LEN_DIGEST(n) (MAX_DIGEST)
73
74#else
80#define VLA_ARRAY_LEN_DIGEST(n) (n)
81#endif
82
86#define VLA_CHECK_LEN_DIGEST(n) do { if ((n) > MAX_DIGEST) mhd_panic ( \
87 mhd_panic_cls, __FILE__, __LINE__, \
88 "VLA too big.\n"); } while (0)
89
90
94#define _BASE "Digest "
95
99#define MAX_USERNAME_LENGTH 128
100
104#define MAX_REALM_LENGTH 256
105
109#define MAX_AUTH_RESPONSE_LENGTH 256
110
111
117struct DigestAlgorithm
118{
122 unsigned int digest_size;
123
128 void *ctx;
129
133 const char *alg;
134
138 char *sessionkey;
139
143 void
144 (*init)(void *ctx);
145
153 void
154 (*update)(void *ctx,
155 const uint8_t *data,
156 size_t length);
157
165 void
166 (*digest)(void *ctx,
167 uint8_t *digest);
168};
169
170
178static void
179cvthex (const unsigned char *bin,
180 size_t len,
181 char *hex)
182{
183 size_t i;
184 unsigned int j;
185
186 for (i = 0; i < len; ++i)
187 {
188 j = (bin[i] >> 4) & 0x0f;
189 hex[i * 2] = (char) ((j <= 9) ? (j + '0') : (j - 10 + 'a'));
190 j = bin[i] & 0x0f;
191 hex[i * 2 + 1] = (char) ((j <= 9) ? (j + '0') : (j - 10 + 'a'));
192 }
193 hex[len * 2] = '\0';
194}
195
196
212static void
214 struct DigestAlgorithm *da,
215 const uint8_t *digest,
216 const char *nonce,
217 const char *cnonce)
218{
219 const unsigned int digest_size = da->digest_size;
220 if ( (MHD_str_equal_caseless_ (alg,
221 "md5-sess")) ||
223 "sha-256-sess")) )
224 {
225 uint8_t dig[VLA_ARRAY_LEN_DIGEST (digest_size)];
226
227 VLA_CHECK_LEN_DIGEST (digest_size);
228 da->init (da->ctx);
229 da->update (da->ctx,
230 digest,
232 da->update (da->ctx,
233 (const unsigned char *) ":",
234 1);
235 da->update (da->ctx,
236 (const unsigned char *) nonce,
237 strlen (nonce));
238 da->update (da->ctx,
239 (const unsigned char *) ":",
240 1);
241 da->update (da->ctx,
242 (const unsigned char *) cnonce,
243 strlen (cnonce));
244 da->digest (da->ctx,
245 dig);
246 cvthex (dig,
247 digest_size,
248 da->sessionkey);
249 }
250 else
251 {
252 cvthex (digest,
253 digest_size,
254 da->sessionkey);
255 }
256}
257
258
273static void
275 const char *username,
276 const char *realm,
277 const char *password,
278 const char *nonce,
279 const char *cnonce,
280 struct DigestAlgorithm *da)
281{
282 unsigned char ha1[VLA_ARRAY_LEN_DIGEST (da->digest_size)];
283
284 VLA_CHECK_LEN_DIGEST (da->digest_size);
285 da->init (da->ctx);
286 da->update (da->ctx,
287 (const unsigned char *) username,
288 strlen (username));
289 da->update (da->ctx,
290 (const unsigned char *) ":",
291 1);
292 da->update (da->ctx,
293 (const unsigned char *) realm,
294 strlen (realm));
295 da->update (da->ctx,
296 (const unsigned char *) ":",
297 1);
298 da->update (da->ctx,
299 (const unsigned char *) password,
300 strlen (password));
301 da->digest (da->ctx,
302 ha1);
304 da,
305 ha1,
306 nonce,
307 cnonce);
308}
309
310
327static void
328digest_calc_response (const char *ha1,
329 const char *nonce,
330 const char *noncecount,
331 const char *cnonce,
332 const char *qop,
333 const char *method,
334 const char *uri,
335 const char *hentity,
336 struct DigestAlgorithm *da)
337{
338 const unsigned int digest_size = da->digest_size;
339 unsigned char ha2[VLA_ARRAY_LEN_DIGEST (digest_size)];
340 unsigned char resphash[VLA_ARRAY_LEN_DIGEST (digest_size)];
341 (void) hentity; /* Unused. Silence compiler warning. */
342
343 VLA_CHECK_LEN_DIGEST (digest_size);
344 da->init (da->ctx);
345 da->update (da->ctx,
346 (const unsigned char *) method,
347 strlen (method));
348 da->update (da->ctx,
349 (const unsigned char *) ":",
350 1);
351 da->update (da->ctx,
352 (const unsigned char *) uri,
353 strlen (uri));
354#if 0
355 if (0 == strcasecmp (qop,
356 "auth-int"))
357 {
358 /* This is dead code since the rest of this module does
359 not support auth-int. */
360 da->update (da->ctx,
361 ":",
362 1);
363 if (NULL != hentity)
364 da->update (da->ctx,
365 hentity,
366 strlen (hentity));
367 }
368#endif
369 da->digest (da->ctx,
370 ha2);
371 cvthex (ha2,
372 digest_size,
373 da->sessionkey);
374 da->init (da->ctx);
375 /* calculate response */
376 da->update (da->ctx,
377 (const unsigned char *) ha1,
378 digest_size * 2);
379 da->update (da->ctx,
380 (const unsigned char *) ":",
381 1);
382 da->update (da->ctx,
383 (const unsigned char *) nonce,
384 strlen (nonce));
385 da->update (da->ctx,
386 (const unsigned char*) ":",
387 1);
388 if ('\0' != *qop)
389 {
390 da->update (da->ctx,
391 (const unsigned char *) noncecount,
392 strlen (noncecount));
393 da->update (da->ctx,
394 (const unsigned char *) ":",
395 1);
396 da->update (da->ctx,
397 (const unsigned char *) cnonce,
398 strlen (cnonce));
399 da->update (da->ctx,
400 (const unsigned char *) ":",
401 1);
402 da->update (da->ctx,
403 (const unsigned char *) qop,
404 strlen (qop));
405 da->update (da->ctx,
406 (const unsigned char *) ":",
407 1);
408 }
409 da->update (da->ctx,
410 (const unsigned char *) da->sessionkey,
411 digest_size * 2);
412 da->digest (da->ctx,
413 resphash);
414 cvthex (resphash,
415 digest_size,
416 da->sessionkey);
417}
418
419
434static size_t
436 size_t size,
437 const char *data,
438 const char *key)
439{
440 size_t keylen;
441 size_t len;
442 const char *ptr;
443 const char *eq;
444 const char *q1;
445 const char *q2;
446 const char *qn;
447
448 if (0 == size)
449 return 0;
450 keylen = strlen (key);
451 ptr = data;
452 while ('\0' != *ptr)
453 {
454 if (NULL == (eq = strchr (ptr,
455 '=')))
456 return 0;
457 q1 = eq + 1;
458 while (' ' == *q1)
459 q1++;
460 if ('\"' != *q1)
461 {
462 q2 = strchr (q1,
463 ',');
464 qn = q2;
465 }
466 else
467 {
468 q1++;
469 q2 = strchr (q1,
470 '\"');
471 if (NULL == q2)
472 return 0; /* end quote not found */
473 qn = q2 + 1;
474 }
475 if ( (MHD_str_equal_caseless_n_ (ptr,
476 key,
477 keylen)) &&
478 (eq == &ptr[keylen]) )
479 {
480 if (NULL == q2)
481 {
482 len = strlen (q1) + 1;
483 if (size > len)
484 size = len;
485 size--;
486 memcpy (dest,
487 q1,
488 size);
489 dest[size] = '\0';
490 return size;
491 }
492 else
493 {
494 if (size > (size_t) ((q2 - q1) + 1))
495 size = (q2 - q1) + 1;
496 size--;
497 memcpy (dest,
498 q1,
499 size);
500 dest[size] = '\0';
501 return size;
502 }
503 }
504 if (NULL == qn)
505 return 0;
506 ptr = strchr (qn,
507 ',');
508 if (NULL == ptr)
509 return 0;
510 ptr++;
511 while (' ' == *ptr)
512 ptr++;
513 }
514 return 0;
515}
516
517
527static enum MHD_Result
528check_nonce_nc (struct MHD_Connection *connection,
529 const char *nonce,
530 uint64_t nc)
531{
532 struct MHD_Daemon *daemon = connection->daemon;
533 struct MHD_NonceNc *nn;
534 uint32_t off;
535 uint32_t mod;
536 const char *np;
537 size_t noncelen;
538
539 noncelen = strlen (nonce) + 1;
540 if (MAX_NONCE_LENGTH < noncelen)
541 return MHD_NO; /* This should be impossible, but static analysis
542 tools have a hard time with it *and* this also
543 protects against unsafe modifications that may
544 happen in the future... */
545 mod = daemon->nonce_nc_size;
546 if (0 == mod)
547 return MHD_NO; /* no array! */
548 /* super-fast xor-based "hash" function for HT lookup in nonce array */
549 off = 0;
550 np = nonce;
551 while ('\0' != *np)
552 {
553 off = (off << 8) | (*np ^ (off >> 24));
554 np++;
555 }
556 off = off % mod;
557 /*
558 * Look for the nonce, if it does exist and its corresponding
559 * nonce counter is less than the current nonce counter by 1,
560 * then only increase the nonce counter by one.
561 */
562 nn = &daemon->nnc[off];
563#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
564 MHD_mutex_lock_chk_ (&daemon->nnc_lock);
565#endif
566 if (0 == nc)
567 {
568 /* Fresh nonce, reinitialize array */
569 memcpy (nn->nonce,
570 nonce,
571 noncelen);
572 nn->nc = 0;
573 nn->nmask = 0;
574#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
575 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
576#endif
577 return MHD_YES;
578 }
579 /* Note that we use 64 here, as we do not store the
580 bit for 'nn->nc' itself in 'nn->nmask' */
581 if ( (nc < nn->nc) &&
582 (nc + 64 > nc /* checking for overflow */) &&
583 (nc + 64 >= nn->nc) &&
584 (0 == ((1LLU << (nn->nc - nc - 1)) & nn->nmask)) )
585 {
586 /* Out-of-order nonce, but within 64-bit bitmask, set bit */
587 nn->nmask |= (1LLU << (nn->nc - nc - 1));
588#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
589 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
590#endif
591 return MHD_YES;
592 }
593
594 if ( (nc <= nn->nc) ||
595 (0 != strcmp (nn->nonce,
596 nonce)) )
597 {
598 /* Nonce does not match, fail */
599#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
600 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
601#endif
602#ifdef HAVE_MESSAGES
603 MHD_DLOG (daemon,
604 _ (
605 "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n"));
606#endif
607 return MHD_NO;
608 }
609 /* Nonce is larger, shift bitmask and bump limit */
610 if (64 > nc - nn->nc)
611 nn->nmask <<= (nc - nn->nc); /* small jump, less than mask width */
612 else
613 nn->nmask = 0; /* big jump, unset all bits in the mask */
614 nn->nc = nc;
615#if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
616 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
617#endif
618 return MHD_YES;
619}
620
621
631char *
633{
634 char user[MAX_USERNAME_LENGTH];
635 const char *header;
636
637 if (MHD_NO == MHD_lookup_connection_value_n (connection,
642 &header,
643 NULL))
644 return NULL;
645 if (0 != strncmp (header,
646 _BASE,
648 return NULL;
649 header += MHD_STATICSTR_LEN_ (_BASE);
650 if (0 == lookup_sub_value (user,
651 sizeof (user),
652 header,
653 "username"))
654 return NULL;
655 return strdup (user);
656}
657
658
674static void
675calculate_nonce (uint32_t nonce_time,
676 const char *method,
677 const char *rnd,
678 size_t rnd_size,
679 const char *uri,
680 const char *realm,
681 struct DigestAlgorithm *da,
682 char *nonce)
683{
684 unsigned char timestamp[TIMESTAMP_BIN_SIZE];
685 const unsigned int digest_size = da->digest_size;
686 unsigned char tmpnonce[VLA_ARRAY_LEN_DIGEST (digest_size)];
687
688 VLA_CHECK_LEN_DIGEST (digest_size);
689 da->init (da->ctx);
690 timestamp[0] = (unsigned char) ((nonce_time & 0xff000000) >> 0x18);
691 timestamp[1] = (unsigned char) ((nonce_time & 0x00ff0000) >> 0x10);
692 timestamp[2] = (unsigned char) ((nonce_time & 0x0000ff00) >> 0x08);
693 timestamp[3] = (unsigned char) ((nonce_time & 0x000000ff));
694 da->update (da->ctx,
695 timestamp,
696 sizeof (timestamp));
697 da->update (da->ctx,
698 (const unsigned char *) ":",
699 1);
700 da->update (da->ctx,
701 (const unsigned char *) method,
702 strlen (method));
703 da->update (da->ctx,
704 (const unsigned char *) ":",
705 1);
706 if (rnd_size > 0)
707 da->update (da->ctx,
708 (const unsigned char *) rnd,
709 rnd_size);
710 da->update (da->ctx,
711 (const unsigned char *) ":",
712 1);
713 da->update (da->ctx,
714 (const unsigned char *) uri,
715 strlen (uri));
716 da->update (da->ctx,
717 (const unsigned char *) ":",
718 1);
719 da->update (da->ctx,
720 (const unsigned char *) realm,
721 strlen (realm));
722 da->digest (da->ctx,
723 tmpnonce);
724 cvthex (tmpnonce,
725 digest_size,
726 nonce);
727 cvthex (timestamp,
728 sizeof (timestamp),
729 nonce + digest_size * 2);
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_NO == 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 const unsigned int digest_size = da->digest_size;
871 char ha1[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1];
872 char qop[15]; /* auth,auth-int */
873 char nc[20];
874 char response[MAX_AUTH_RESPONSE_LENGTH];
875 const char *hentity = NULL; /* "auth-int" is not supported */
876 char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1];
877 uint32_t nonce_time;
878 uint32_t t;
879 size_t left; /* number of characters left in 'header' for 'uri' */
880 uint64_t nci;
881 char *qmark;
882
883 VLA_CHECK_LEN_DIGEST (digest_size);
884 if (MHD_NO == MHD_lookup_connection_value_n (connection,
889 &header,
890 NULL))
891 return MHD_NO;
892 if (0 != strncmp (header,
893 _BASE,
895 return MHD_NO;
896 header += MHD_STATICSTR_LEN_ (_BASE);
897 left = strlen (header);
898
899 {
900 char un[MAX_USERNAME_LENGTH];
901
902 len = lookup_sub_value (un,
903 sizeof (un),
904 header,
905 "username");
906 if ( (0 == len) ||
907 (0 != strcmp (username,
908 un)) )
909 return MHD_NO;
910 left -= strlen ("username") + len;
911 }
912
913 {
914 char r[MAX_REALM_LENGTH];
915
916 len = lookup_sub_value (r,
917 sizeof (r),
918 header,
919 "realm");
920 if ( (0 == len) ||
921 (0 != strcmp (realm,
922 r)) )
923 return MHD_NO;
924 left -= strlen ("realm") + len;
925 }
926
927 if (0 == (len = lookup_sub_value (nonce,
928 sizeof (nonce),
929 header,
930 "nonce")))
931 return MHD_NO;
932 left -= strlen ("nonce") + len;
933 if (left > 32 * 1024)
934 {
935 /* we do not permit URIs longer than 32k, as we want to
936 make sure to not blow our stack (or per-connection
937 heap memory limit). Besides, 32k is already insanely
938 large, but of course in theory the
939 #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
940 and would thus permit sending a >32k authorization
941 header value. */
942 return MHD_NO;
943 }
944 if (TIMESTAMP_BIN_SIZE * 2 !=
945 MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_BIN_SIZE * 2,
947 &nonce_time))
948 {
949#ifdef HAVE_MESSAGES
950 MHD_DLOG (daemon,
951 _ ("Authentication failed, invalid timestamp format.\n"));
952#endif
953 return MHD_NO;
954 }
955 t = (uint32_t) MHD_monotonic_sec_counter ();
956 /*
957 * First level vetting for the nonce validity: if the timestamp
958 * attached to the nonce exceeds `nonce_timeout', then the nonce is
959 * invalid.
960 */
961 if ( (t > nonce_time + nonce_timeout) ||
962 (nonce_time + nonce_timeout < nonce_time) )
963 {
964 /* too old */
965 return MHD_INVALID_NONCE;
966 }
967
968 calculate_nonce (nonce_time,
969 connection->method,
970 daemon->digest_auth_random,
971 daemon->digest_auth_rand_size,
972 connection->url,
973 realm,
974 da,
975 noncehashexp);
976 /*
977 * Second level vetting for the nonce validity
978 * if the timestamp attached to the nonce is valid
979 * and possibly fabricated (in case of an attack)
980 * the attacker must also know the random seed to be
981 * able to generate a "sane" nonce, which if he does
982 * not, the nonce fabrication process going to be
983 * very hard to achieve.
984 */
985 if (0 != strcmp (nonce,
986 noncehashexp))
987 {
988 return MHD_INVALID_NONCE;
989 }
990 if ( (0 == lookup_sub_value (cnonce,
991 sizeof (cnonce),
992 header,
993 "cnonce")) ||
994 (0 == lookup_sub_value (qop,
995 sizeof (qop),
996 header,
997 "qop")) ||
998 ( (0 != strcmp (qop,
999 "auth")) &&
1000 (0 != strcmp (qop,
1001 "")) ) ||
1002 (0 == (len = lookup_sub_value (nc,
1003 sizeof (nc),
1004 header,
1005 "nc")) ) ||
1006 (0 == lookup_sub_value (response,
1007 sizeof (response),
1008 header,
1009 "response")) )
1010 {
1011#ifdef HAVE_MESSAGES
1012 MHD_DLOG (daemon,
1013 _ ("Authentication failed, invalid format.\n"));
1014#endif
1015 return MHD_NO;
1016 }
1017 if (len != MHD_strx_to_uint64_n_ (nc,
1018 len,
1019 &nci))
1020 {
1021#ifdef HAVE_MESSAGES
1022 MHD_DLOG (daemon,
1023 _ ("Authentication failed, invalid nc format.\n"));
1024#endif
1025 return MHD_NO; /* invalid nonce format */
1026 }
1027
1028 /*
1029 * Checking if that combination of nonce and nc is sound
1030 * and not a replay attack attempt. Also adds the nonce
1031 * to the nonce-nc map if it does not exist there.
1032 */
1033 if (MHD_NO ==
1034 check_nonce_nc (connection,
1035 nonce,
1036 nci))
1037 {
1038 return MHD_NO;
1039 }
1040
1041 {
1042 char *uri;
1043
1044 uri = malloc (left + 1);
1045 if (NULL == uri)
1046 {
1047#ifdef HAVE_MESSAGES
1048 MHD_DLOG (daemon,
1049 _ ("Failed to allocate memory for auth header processing.\n"));
1050#endif /* HAVE_MESSAGES */
1051 return MHD_NO;
1052 }
1053 if (0 == lookup_sub_value (uri,
1054 left + 1,
1055 header,
1056 "uri"))
1057 {
1058 free (uri);
1059 return MHD_NO;
1060 }
1061 if (NULL != digest)
1062 {
1063 /* This will initialize da->sessionkey (ha1) */
1065 da,
1066 digest,
1067 nonce,
1068 cnonce);
1069 }
1070 else
1071 {
1072 /* This will initialize da->sessionkey (ha1) */
1073 mhd_assert (NULL != password); /* NULL == digest => password != NULL */
1075 username,
1076 realm,
1077 password,
1078 nonce,
1079 cnonce,
1080 da);
1081 }
1082 memcpy (ha1,
1083 da->sessionkey,
1084 digest_size * 2 + 1);
1085 /* This will initialize da->sessionkey (respexp) */
1087 nonce,
1088 nc,
1089 cnonce,
1090 qop,
1091 connection->method,
1092 uri,
1093 hentity,
1094 da);
1095 qmark = strchr (uri,
1096 '?');
1097 if (NULL != qmark)
1098 *qmark = '\0';
1099
1100 /* Need to unescape URI before comparing with connection->url */
1102 connection,
1103 uri);
1104 if (0 != strcmp (uri,
1105 connection->url))
1106 {
1107#ifdef HAVE_MESSAGES
1108 MHD_DLOG (daemon,
1109 _ ("Authentication failed, URI does not match.\n"));
1110#endif
1111 free (uri);
1112 return MHD_NO;
1113 }
1114
1115 {
1116 const char *args = qmark;
1117
1118 if (NULL == args)
1119 args = "";
1120 else
1121 args++;
1122 if (MHD_NO ==
1123 check_argument_match (connection,
1124 args) )
1125 {
1126#ifdef HAVE_MESSAGES
1127 MHD_DLOG (daemon,
1128 _ ("Authentication failed, arguments do not match.\n"));
1129#endif
1130 free (uri);
1131 return MHD_NO;
1132 }
1133 }
1134 free (uri);
1135 return (0 == strcmp (response,
1136 da->sessionkey))
1137 ? MHD_YES
1138 : MHD_NO;
1139 }
1140}
1141
1142
1160_MHD_EXTERN int
1162 const char *realm,
1163 const char *username,
1164 const char *password,
1165 unsigned int nonce_timeout)
1166{
1167 return MHD_digest_auth_check2 (connection,
1168 realm,
1169 username,
1170 password,
1171 nonce_timeout,
1173}
1174
1175
1184#define SETUP_DA(algo,da) \
1185 union { \
1186 struct MD5Context md5; \
1187 struct sha256_ctx sha256; \
1188 } ctx; \
1189 union { \
1190 char md5[MD5_DIGEST_SIZE * 2 + 1]; \
1191 char sha256[SHA256_DIGEST_SIZE * 2 + 1]; \
1192 } skey; \
1193 struct DigestAlgorithm da; \
1194 \
1195 do { \
1196 switch (algo) { \
1197 case MHD_DIGEST_ALG_MD5: \
1198 da.digest_size = MD5_DIGEST_SIZE; \
1199 da.ctx = &ctx.md5; \
1200 da.alg = "md5"; \
1201 da.sessionkey = skey.md5; \
1202 da.init = &MHD_MD5Init; \
1203 da.update = &MHD_MD5Update; \
1204 da.digest = &MHD_MD5Final; \
1205 break; \
1206 case MHD_DIGEST_ALG_AUTO: \
1207 /* auto == SHA256, fall-though thus intentional! */ \
1208 case MHD_DIGEST_ALG_SHA256: \
1209 da.digest_size = SHA256_DIGEST_SIZE; \
1210 da.ctx = &ctx.sha256; \
1211 da.alg = "sha-256"; \
1212 da.sessionkey = skey.sha256; \
1213 da.init = &MHD_SHA256_init; \
1214 da.update = &MHD_SHA256_update; \
1215 da.digest = &sha256_finish; \
1216 break; \
1217 default: \
1218 mhd_assert (false); \
1219 break; \
1220 } \
1221 } while (0)
1222
1223
1238_MHD_EXTERN int
1240 const char *realm,
1241 const char *username,
1242 const char *password,
1243 unsigned int nonce_timeout,
1244 enum MHD_DigestAuthAlgorithm algo)
1245{
1246 SETUP_DA (algo, da);
1247
1248 mhd_assert (NULL != password);
1249 return digest_auth_check_all (connection,
1250 &da,
1251 realm,
1252 username,
1253 password,
1254 NULL,
1255 nonce_timeout);
1256}
1257
1258
1276_MHD_EXTERN int
1278 const char *realm,
1279 const char *username,
1280 const uint8_t *digest,
1281 size_t digest_size,
1282 unsigned int nonce_timeout,
1283 enum MHD_DigestAuthAlgorithm algo)
1284{
1285 SETUP_DA (algo, da);
1286
1287 mhd_assert (NULL != digest);
1288 if (da.digest_size != digest_size)
1289 MHD_PANIC (_ ("Digest size mismatch.\n")); /* API violation! */
1290 return digest_auth_check_all (connection,
1291 &da,
1292 realm,
1293 username,
1294 NULL,
1295 digest,
1296 nonce_timeout);
1297}
1298
1299
1317_MHD_EXTERN int
1319 const char *realm,
1320 const char *username,
1321 const uint8_t digest[MHD_MD5_DIGEST_SIZE],
1322 unsigned int nonce_timeout)
1323{
1324 return MHD_digest_auth_check_digest2 (connection,
1325 realm,
1326 username,
1327 digest,
1329 nonce_timeout,
1331}
1332
1333
1349enum MHD_Result
1351 const char *realm,
1352 const char *opaque,
1353 struct MHD_Response *response,
1354 int signal_stale,
1355 enum MHD_DigestAuthAlgorithm algo)
1356{
1357 int ret;
1358 int hlen;
1359 SETUP_DA (algo, da);
1360
1361 {
1362 char nonce[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1];
1363
1364 VLA_CHECK_LEN_DIGEST (da.digest_size);
1365 /* Generating the server nonce */
1367 connection->method,
1368 connection->daemon->digest_auth_random,
1369 connection->daemon->digest_auth_rand_size,
1370 connection->url,
1371 realm,
1372 &da,
1373 nonce);
1374 if (MHD_NO ==
1375 check_nonce_nc (connection,
1376 nonce,
1377 0))
1378 {
1379#ifdef HAVE_MESSAGES
1380 MHD_DLOG (connection->daemon,
1381 _ (
1382 "Could not register nonce (is the nonce array size zero?).\n"));
1383#endif
1384 return MHD_NO;
1385 }
1386 /* Building the authentication header */
1387 hlen = MHD_snprintf_ (NULL,
1388 0,
1389 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1390 realm,
1391 nonce,
1392 opaque,
1393 da.alg,
1394 signal_stale
1395 ? ",stale=\"true\""
1396 : "");
1397 if (hlen > 0)
1398 {
1399 char *header;
1400
1401 header = MHD_calloc_ (1,
1402 hlen + 1);
1403 if (NULL == header)
1404 {
1405#ifdef HAVE_MESSAGES
1406 MHD_DLOG (connection->daemon,
1407 _ ("Failed to allocate memory for auth response header.\n"));
1408#endif /* HAVE_MESSAGES */
1409 return MHD_NO;
1410 }
1411
1412 if (MHD_snprintf_ (header,
1413 hlen + 1,
1414 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1415 realm,
1416 nonce,
1417 opaque,
1418 da.alg,
1419 signal_stale
1420 ? ",stale=\"true\""
1421 : "") == hlen)
1422 ret = MHD_add_response_header (response,
1424 header);
1425 else
1426 ret = MHD_NO;
1427#if 0
1428 if ( (MHD_NO != ret) && (AND in state : 100 continue aborting ...))
1429 ret = MHD_add_response_header (response,
1431 "close");
1432#endif
1433 free (header);
1434 }
1435 else
1436 ret = MHD_NO;
1437 }
1438
1439 if (MHD_NO != ret)
1440 {
1441 ret = MHD_queue_response (connection,
1443 response);
1444 }
1445 else
1446 {
1447#ifdef HAVE_MESSAGES
1448 MHD_DLOG (connection->daemon,
1449 _ ("Failed to add Digest auth header.\n"));
1450#endif /* HAVE_MESSAGES */
1451 }
1452 return ret;
1453}
1454
1455
1472enum MHD_Result
1474 const char *realm,
1475 const char *opaque,
1476 struct MHD_Response *response,
1477 int signal_stale)
1478{
1479 return MHD_queue_auth_fail_response2 (connection,
1480 realm,
1481 opaque,
1482 response,
1483 signal_stale,
1485}
1486
1487
1488/* end of digestauth.c */
#define VLA_CHECK_LEN_DIGEST(n)
Definition: digestauth.c:86
#define SETUP_DA(algo, da)
Definition: digestauth.c:1184
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:328
#define MAX_REALM_LENGTH
Definition: digestauth.c:104
#define NONCE_STD_LEN(digest_size)
Definition: digestauth.c:53
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:109
#define TIMESTAMP_BIN_SIZE
Definition: digestauth.c:46
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:274
static size_t lookup_sub_value(char *dest, size_t size, const char *data, const char *key)
Definition: digestauth.c:435
#define _BASE
Definition: digestauth.c:94
static enum MHD_Result check_nonce_nc(struct MHD_Connection *connection, const char *nonce, uint64_t nc)
Definition: digestauth.c:528
#define MAX_USERNAME_LENGTH
Definition: digestauth.c:99
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:675
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:72
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:213
static void cvthex(const unsigned char *bin, size_t len, char *hex)
Definition: digestauth.c:179
_MHD_EXTERN char * MHD_digest_auth_get_username(struct MHD_Connection *connection)
Definition: digestauth.c:632
_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:1239
_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:1350
_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:1277
_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:1473
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:1318
_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:1161
#define MHD_INVALID_NONCE
Definition: microhttpd.h:3634
#define MHD_HTTP_HEADER_CONNECTION
Definition: microhttpd.h:566
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition: microhttpd.h:560
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition: microhttpd.h:638
#define MHD_HTTP_UNAUTHORIZED
Definition: microhttpd.h:384
_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:4035
_MHD_EXTERN enum MHD_Result MHD_add_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition: response.c:134
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:268
macros for mhd_assert()
Header for platform missing functions.
limits values definitions
internal monotonic clock functions implementations
Header for string manipulating helpers.
MHD_Result
Definition: microhttpd.h:139
@ MHD_YES
Definition: microhttpd.h:148
@ MHD_NO
Definition: microhttpd.h:143
void * data
Definition: microhttpd.h:3125
MHD_ValueKind
Definition: microhttpd.h:1800
@ MHD_HEADER_KIND
Definition: microhttpd.h:1815
@ MHD_GET_ARGUMENT_KIND
Definition: microhttpd.h:1836
MHD_DigestAuthAlgorithm
Definition: microhttpd.h:3665
@ MHD_DIGEST_ALG_MD5
Definition: microhttpd.h:3675
#define MHD_MD5_DIGEST_SIZE
Definition: microhttpd.h:320
platform-specific includes for libmicrohttpd
Calculation of SHA-256 digest.
const char * url
Definition: internal.h:834
struct MHD_HTTP_Header * headers_received
Definition: internal.h:786
char * method
Definition: internal.h:828
struct MHD_Daemon * daemon
Definition: internal.h:675
void * unescape_callback_cls
Definition: internal.h:1603
UnescapeCallback unescape_callback
Definition: internal.h:1598
size_t value_size
Definition: internal.h:338
char * header
Definition: internal.h:347
enum MHD_ValueKind kind
Definition: internal.h:358
size_t header_size
Definition: internal.h:328
struct MHD_HTTP_Header * next
Definition: internal.h:342
char * value
Definition: internal.h:352
uint64_t nc
Definition: internal.h:282
uint64_t nmask
Definition: internal.h:288
char nonce[MAX_NONCE_LENGTH]
Definition: internal.h:293