GNU libmicrohttpd 0.9.77
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 *const daemon = MHD_get_master (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 = &MHD_SHA256_finish; \
1216 break; \
1217 default: \
1218 da.digest_size = 0; \
1219 mhd_assert (false); \
1220 break; \
1221 } \
1222 } while (0)
1223
1224
1239_MHD_EXTERN int
1241 const char *realm,
1242 const char *username,
1243 const char *password,
1244 unsigned int nonce_timeout,
1245 enum MHD_DigestAuthAlgorithm algo)
1246{
1247 SETUP_DA (algo, da);
1248
1249 mhd_assert (NULL != password);
1250 return digest_auth_check_all (connection,
1251 &da,
1252 realm,
1253 username,
1254 password,
1255 NULL,
1256 nonce_timeout);
1257}
1258
1259
1277_MHD_EXTERN int
1279 const char *realm,
1280 const char *username,
1281 const uint8_t *digest,
1282 size_t digest_size,
1283 unsigned int nonce_timeout,
1284 enum MHD_DigestAuthAlgorithm algo)
1285{
1286 SETUP_DA (algo, da);
1287
1288 mhd_assert (NULL != digest);
1289 if (da.digest_size != digest_size)
1290 MHD_PANIC (_ ("Digest size mismatch.\n")); /* API violation! */
1291 return digest_auth_check_all (connection,
1292 &da,
1293 realm,
1294 username,
1295 NULL,
1296 digest,
1297 nonce_timeout);
1298}
1299
1300
1318_MHD_EXTERN int
1320 const char *realm,
1321 const char *username,
1322 const uint8_t digest[MHD_MD5_DIGEST_SIZE],
1323 unsigned int nonce_timeout)
1324{
1325 return MHD_digest_auth_check_digest2 (connection,
1326 realm,
1327 username,
1328 digest,
1330 nonce_timeout,
1332}
1333
1334
1350enum MHD_Result
1352 const char *realm,
1353 const char *opaque,
1354 struct MHD_Response *response,
1355 int signal_stale,
1356 enum MHD_DigestAuthAlgorithm algo)
1357{
1358 enum MHD_Result ret;
1359 int hlen;
1360 SETUP_DA (algo, da);
1361
1362 {
1363 char nonce[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1];
1364
1365 VLA_CHECK_LEN_DIGEST (da.digest_size);
1366 /* Generating the server nonce */
1368 connection->method,
1369 connection->daemon->digest_auth_random,
1370 connection->daemon->digest_auth_rand_size,
1371 connection->url,
1372 realm,
1373 &da,
1374 nonce);
1375 if (MHD_NO ==
1376 check_nonce_nc (connection,
1377 nonce,
1378 0))
1379 {
1380#ifdef HAVE_MESSAGES
1381 MHD_DLOG (connection->daemon,
1382 _ (
1383 "Could not register nonce (is the nonce array size zero?).\n"));
1384#endif
1385 return MHD_NO;
1386 }
1387 /* Building the authentication header */
1388 hlen = MHD_snprintf_ (NULL,
1389 0,
1390 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1391 realm,
1392 nonce,
1393 opaque,
1394 da.alg,
1395 signal_stale
1396 ? ",stale=\"true\""
1397 : "");
1398 if (hlen > 0)
1399 {
1400 char *header;
1401
1402 header = MHD_calloc_ (1,
1403 hlen + 1);
1404 if (NULL == header)
1405 {
1406#ifdef HAVE_MESSAGES
1407 MHD_DLOG (connection->daemon,
1408 _ ("Failed to allocate memory for auth response header.\n"));
1409#endif /* HAVE_MESSAGES */
1410 return MHD_NO;
1411 }
1412
1413 if (MHD_snprintf_ (header,
1414 hlen + 1,
1415 "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1416 realm,
1417 nonce,
1418 opaque,
1419 da.alg,
1420 signal_stale
1421 ? ",stale=\"true\""
1422 : "") == hlen)
1423 ret = MHD_add_response_header (response,
1425 header);
1426 else
1427 ret = MHD_NO;
1428#if 0
1429 if ( (MHD_NO != ret) && (AND in state : 100 continue aborting ...))
1430 ret = MHD_add_response_header (response,
1432 "close");
1433#endif
1434 free (header);
1435 }
1436 else
1437 ret = MHD_NO;
1438 }
1439
1440 if (MHD_NO != ret)
1441 {
1442 ret = MHD_queue_response (connection,
1444 response);
1445 }
1446 else
1447 {
1448#ifdef HAVE_MESSAGES
1449 MHD_DLOG (connection->daemon,
1450 _ ("Failed to add Digest auth header.\n"));
1451#endif /* HAVE_MESSAGES */
1452 }
1453 return ret;
1454}
1455
1456
1473enum MHD_Result
1475 const char *realm,
1476 const char *opaque,
1477 struct MHD_Response *response,
1478 int signal_stale)
1479{
1480 return MHD_queue_auth_fail_response2 (connection,
1481 realm,
1482 opaque,
1483 response,
1484 signal_stale,
1486}
1487
1488
1489/* end of digestauth.c */
struct MHD_Daemon * MHD_get_master(struct MHD_Daemon *daemon)
Definition daemon.c:261
#define VLA_CHECK_LEN_DIGEST(n)
Definition digestauth.c:86
#define SETUP_DA(algo, da)
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)
_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 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_queue_auth_fail_response(struct MHD_Connection *connection, const char *realm, const char *opaque, struct MHD_Response *response, int signal_stale)
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)
_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
Definition microhttpd.h:175
#define MHD_HTTP_HEADER_CONNECTION
Definition microhttpd.h:606
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition microhttpd.h:598
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition microhttpd.h:686
#define MHD_HTTP_UNAUTHORIZED
Definition microhttpd.h:399
_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:649
_MHD_EXTERN enum MHD_Result MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
_MHD_EXTERN enum MHD_Result MHD_add_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition response.c:493
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
#define _(String)
Definition mhd_options.h:42
#define _MHD_EXTERN
Definition mhd_options.h:50
MHD internal shared structures.
#define MAX_NONCE_LENGTH
Definition internal.h:277
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:158
@ MHD_YES
Definition microhttpd.h:167
@ MHD_NO
Definition microhttpd.h:162
void * data
MHD_ValueKind
@ MHD_HEADER_KIND
@ MHD_GET_ARGUMENT_KIND
MHD_DigestAuthAlgorithm
@ MHD_DIGEST_ALG_MD5
#define MHD_MD5_DIGEST_SIZE
Definition microhttpd.h:335
platform-specific includes for libmicrohttpd
Calculation of SHA-256 digest.
const char * url
Definition internal.h:1011
struct MHD_HTTP_Header * headers_received
Definition internal.h:958
struct MHD_Daemon * daemon
Definition internal.h:675
void * unescape_callback_cls
Definition internal.h:1811
UnescapeCallback unescape_callback
Definition internal.h:1806
size_t value_size
Definition internal.h:352
enum MHD_ValueKind kind
Definition internal.h:358
size_t header_size
Definition internal.h:342
struct MHD_HTTP_Header * next
Definition internal.h:342
uint64_t nc
Definition internal.h:291
uint64_t nmask
Definition internal.h:297
char nonce[MAX_NONCE_LENGTH]
Definition internal.h:302