GNU libmicrohttpd  0.9.65
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__
66 
71 #define VLA_ARRAY_LEN_DIGEST(n) (MAX_DIGEST)
72 
73 #else
74 
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(mhd_panic_cls, __FILE__, __LINE__, "VLA too big"); } while (0)
86 
87 
91 #define _BASE "Digest "
92 
96 #define MAX_USERNAME_LENGTH 128
97 
101 #define MAX_REALM_LENGTH 256
102 
106 #define MAX_AUTH_RESPONSE_LENGTH 256
107 
108 
114 struct DigestAlgorithm
115 {
119  unsigned int digest_size;
120 
125  void *ctx;
126 
130  const char *alg;
131 
135  char *sessionkey;
136 
140  void
141  (*init)(void *ctx);
142 
150  void
151  (*update)(void *ctx,
152  const uint8_t *data,
153  size_t length);
154 
162  void
163  (*digest)(void *ctx,
164  uint8_t *digest);
165 };
166 
167 
175 static void
176 cvthex (const unsigned char *bin,
177  size_t len,
178  char *hex)
179 {
180  size_t i;
181  unsigned int j;
182 
183  for (i = 0; i < len; ++i)
184  {
185  j = (bin[i] >> 4) & 0x0f;
186  hex[i * 2] = (char)((j <= 9) ? (j + '0') : (j - 10 + 'a'));
187  j = bin[i] & 0x0f;
188  hex[i * 2 + 1] = (char)((j <= 9) ? (j + '0') : (j - 10 + 'a'));
189  }
190  hex[len * 2] = '\0';
191 }
192 
193 
209 static void
211  struct DigestAlgorithm *da,
212  const uint8_t *digest,
213  const char *nonce,
214  const char *cnonce)
215 {
216  if ( (MHD_str_equal_caseless_(alg,
217  "md5-sess")) ||
219  "sha-256-sess")) )
220  {
221  uint8_t dig[VLA_ARRAY_LEN_DIGEST(da->digest_size)];
222 
223  VLA_CHECK_LEN_DIGEST(da->digest_size);
224  da->init (da->ctx);
225  da->update (da->ctx,
226  digest,
228  da->update (da->ctx,
229  (const unsigned char *) ":",
230  1);
231  da->update (da->ctx,
232  (const unsigned char *) nonce,
233  strlen (nonce));
234  da->update (da->ctx,
235  (const unsigned char *) ":",
236  1);
237  da->update (da->ctx,
238  (const unsigned char *) cnonce,
239  strlen (cnonce));
240  da->digest (da->ctx,
241  dig);
242  cvthex (dig,
243  sizeof (dig),
244  da->sessionkey);
245  }
246  else
247  {
248  cvthex (digest,
249  da->digest_size,
250  da->sessionkey);
251  }
252 }
253 
254 
269 static void
270 digest_calc_ha1_from_user (const char *alg,
271  const char *username,
272  const char *realm,
273  const char *password,
274  const char *nonce,
275  const char *cnonce,
276  struct DigestAlgorithm *da)
277 {
278  unsigned char ha1[VLA_ARRAY_LEN_DIGEST(da->digest_size)];
279 
280  VLA_CHECK_LEN_DIGEST(da->digest_size);
281  da->init (da->ctx);
282  da->update (da->ctx,
283  (const unsigned char *) username,
284  strlen (username));
285  da->update (da->ctx,
286  (const unsigned char *) ":",
287  1);
288  da->update (da->ctx,
289  (const unsigned char *) realm,
290  strlen (realm));
291  da->update (da->ctx,
292  (const unsigned char *) ":",
293  1);
294  da->update (da->ctx,
295  (const unsigned char *) password,
296  strlen (password));
297  da->digest (da->ctx,
298  ha1);
300  da,
301  ha1,
302  nonce,
303  cnonce);
304 }
305 
306 
323 static void
324 digest_calc_response (const char *ha1,
325  const char *nonce,
326  const char *noncecount,
327  const char *cnonce,
328  const char *qop,
329  const char *method,
330  const char *uri,
331  const char *hentity,
332  struct DigestAlgorithm *da)
333 {
334  unsigned char ha2[VLA_ARRAY_LEN_DIGEST(da->digest_size)];
335  unsigned char resphash[VLA_ARRAY_LEN_DIGEST(da->digest_size)];
336  (void)hentity; /* Unused. Silence compiler warning. */
337 
338  VLA_CHECK_LEN_DIGEST(da->digest_size);
339  da->init (da->ctx);
340  da->update (da->ctx,
341  (const unsigned char *) method,
342  strlen (method));
343  da->update (da->ctx,
344  (const unsigned char *) ":",
345  1);
346  da->update (da->ctx,
347  (const unsigned char *) uri,
348  strlen (uri));
349 #if 0
350  if (0 == strcasecmp (qop,
351  "auth-int"))
352  {
353  /* This is dead code since the rest of this module does
354  not support auth-int. */
355  da->update (da->ctx,
356  ":",
357  1);
358  if (NULL != hentity)
359  da->update (da->ctx,
360  hentity,
361  strlen (hentity));
362  }
363 #endif
364  da->digest (da->ctx,
365  ha2);
366  cvthex (ha2,
367  da->digest_size,
368  da->sessionkey);
369  da->init (da->ctx);
370  /* calculate response */
371  da->update (da->ctx,
372  (const unsigned char *) ha1,
373  da->digest_size * 2);
374  da->update (da->ctx,
375  (const unsigned char *) ":",
376  1);
377  da->update (da->ctx,
378  (const unsigned char *) nonce,
379  strlen (nonce));
380  da->update (da->ctx,
381  (const unsigned char*) ":",
382  1);
383  if ('\0' != *qop)
384  {
385  da->update (da->ctx,
386  (const unsigned char *) noncecount,
387  strlen (noncecount));
388  da->update (da->ctx,
389  (const unsigned char *) ":",
390  1);
391  da->update (da->ctx,
392  (const unsigned char *) cnonce,
393  strlen (cnonce));
394  da->update (da->ctx,
395  (const unsigned char *) ":",
396  1);
397  da->update (da->ctx,
398  (const unsigned char *) qop,
399  strlen (qop));
400  da->update (da->ctx,
401  (const unsigned char *) ":",
402  1);
403  }
404  da->update (da->ctx,
405  (const unsigned char *) da->sessionkey,
406  da->digest_size * 2);
407  da->digest (da->ctx,
408  resphash);
409  cvthex (resphash,
410  sizeof(resphash),
411  da->sessionkey);
412 }
413 
414 
429 static size_t
430 lookup_sub_value (char *dest,
431  size_t size,
432  const char *data,
433  const char *key)
434 {
435  size_t keylen;
436  size_t len;
437  const char *ptr;
438  const char *eq;
439  const char *q1;
440  const char *q2;
441  const char *qn;
442 
443  if (0 == size)
444  return 0;
445  keylen = strlen (key);
446  ptr = data;
447  while ('\0' != *ptr)
448  {
449  if (NULL == (eq = strchr (ptr,
450  '=')))
451  return 0;
452  q1 = eq + 1;
453  while (' ' == *q1)
454  q1++;
455  if ('\"' != *q1)
456  {
457  q2 = strchr (q1,
458  ',');
459  qn = q2;
460  }
461  else
462  {
463  q1++;
464  q2 = strchr (q1,
465  '\"');
466  if (NULL == q2)
467  return 0; /* end quote not found */
468  qn = q2 + 1;
469  }
470  if ( (MHD_str_equal_caseless_n_(ptr,
471  key,
472  keylen)) &&
473  (eq == &ptr[keylen]) )
474  {
475  if (NULL == q2)
476  {
477  len = strlen (q1) + 1;
478  if (size > len)
479  size = len;
480  size--;
481  strncpy (dest,
482  q1,
483  size);
484  dest[size] = '\0';
485  return size;
486  }
487  else
488  {
489  if (size > (size_t) ((q2 - q1) + 1))
490  size = (q2 - q1) + 1;
491  size--;
492  memcpy (dest,
493  q1,
494  size);
495  dest[size] = '\0';
496  return size;
497  }
498  }
499  if (NULL == qn)
500  return 0;
501  ptr = strchr (qn,
502  ',');
503  if (NULL == ptr)
504  return 0;
505  ptr++;
506  while (' ' == *ptr)
507  ptr++;
508  }
509  return 0;
510 }
511 
512 
522 static int
523 check_nonce_nc (struct MHD_Connection *connection,
524  const char *nonce,
525  uint64_t nc)
526 {
527  struct MHD_Daemon *daemon = connection->daemon;
528  struct MHD_NonceNc *nn;
529  uint32_t off;
530  uint32_t mod;
531  const char *np;
532  size_t noncelen;
533 
534  noncelen = strlen (nonce) + 1;
535  if (MAX_NONCE_LENGTH < noncelen)
536  return MHD_NO; /* This should be impossible, but static analysis
537  tools have a hard time with it *and* this also
538  protects against unsafe modifications that may
539  happen in the future... */
540  mod = daemon->nonce_nc_size;
541  if (0 == mod)
542  return MHD_NO; /* no array! */
543  /* super-fast xor-based "hash" function for HT lookup in nonce array */
544  off = 0;
545  np = nonce;
546  while ('\0' != *np)
547  {
548  off = (off << 8) | (*np ^ (off >> 24));
549  np++;
550  }
551  off = off % mod;
552  /*
553  * Look for the nonce, if it does exist and its corresponding
554  * nonce counter is less than the current nonce counter by 1,
555  * then only increase the nonce counter by one.
556  */
557  nn = &daemon->nnc[off];
558 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
559  MHD_mutex_lock_chk_ (&daemon->nnc_lock);
560 #endif
561  if (0 == nc)
562  {
563  /* Fresh nonce, reinitialize array */
564  memcpy (nn->nonce,
565  nonce,
566  noncelen);
567  nn->nc = 0;
568  nn->nmask = 0;
569 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
570  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
571 #endif
572  return MHD_YES;
573  }
574  /* Note that we use 64 here, as we do not store the
575  bit for 'nn->nc' itself in 'nn->nmask' */
576  if ( (nc < nn->nc) &&
577  (nc + 64 > nc /* checking for overflow */) &&
578  (nc + 64 >= nn->nc) &&
579  (0 == ((1LLU << (nn->nc - nc - 1)) & nn->nmask)) )
580  {
581  /* Out-of-order nonce, but within 64-bit bitmask, set bit */
582  nn->nmask |= (1LLU << (nn->nc - nc - 1));
583 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
584  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
585 #endif
586  return MHD_YES;
587  }
588 
589  if ( (nc <= nn->nc) ||
590  (0 != strcmp (nn->nonce,
591  nonce)) )
592  {
593  /* Nonce does not match, fail */
594 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
595  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
596 #endif
597 #ifdef HAVE_MESSAGES
598  MHD_DLOG (daemon,
599  _("Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n"));
600 #endif
601  return MHD_NO;
602  }
603  /* Nonce is larger, shift bitmask and bump limit */
604  if (64 > nc - nn->nc)
605  nn->nmask <<= (nc - nn->nc); /* small jump, less than mask width */
606  else
607  nn->nmask = 0; /* big jump, unset all bits in the mask */
608  nn->nc = nc;
609 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
610  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
611 #endif
612  return MHD_YES;
613 }
614 
615 
625 char *
627 {
628  size_t len;
629  char user[MAX_USERNAME_LENGTH];
630  const char *header;
631 
632  if (MHD_NO == MHD_lookup_connection_value_n (connection,
636  &header,
637  NULL))
638  return NULL;
639  if (0 != strncmp (header,
640  _BASE,
642  return NULL;
643  header += MHD_STATICSTR_LEN_ (_BASE);
644  if (0 == (len = lookup_sub_value (user,
645  sizeof (user),
646  header,
647  "username")))
648  return NULL;
649  return strdup (user);
650 }
651 
652 
668 static void
669 calculate_nonce (uint32_t nonce_time,
670  const char *method,
671  const char *rnd,
672  size_t rnd_size,
673  const char *uri,
674  const char *realm,
675  struct DigestAlgorithm *da,
676  char *nonce)
677 {
678  unsigned char timestamp[TIMESTAMP_BIN_SIZE];
679  unsigned char tmpnonce[VLA_ARRAY_LEN_DIGEST(da->digest_size)];
680  char timestamphex[TIMESTAMP_BIN_SIZE * 2 + 1];
681 
682  VLA_CHECK_LEN_DIGEST(da->digest_size);
683  da->init (da->ctx);
684  timestamp[0] = (unsigned char)((nonce_time & 0xff000000) >> 0x18);
685  timestamp[1] = (unsigned char)((nonce_time & 0x00ff0000) >> 0x10);
686  timestamp[2] = (unsigned char)((nonce_time & 0x0000ff00) >> 0x08);
687  timestamp[3] = (unsigned char)((nonce_time & 0x000000ff));
688  da->update (da->ctx,
689  timestamp,
690  sizeof (timestamp));
691  da->update (da->ctx,
692  (const unsigned char *) ":",
693  1);
694  da->update (da->ctx,
695  (const unsigned char *) method,
696  strlen (method));
697  da->update (da->ctx,
698  (const unsigned char *) ":",
699  1);
700  if (rnd_size > 0)
701  da->update (da->ctx,
702  (const unsigned char *) rnd,
703  rnd_size);
704  da->update (da->ctx,
705  (const unsigned char *) ":",
706  1);
707  da->update (da->ctx,
708  (const unsigned char *) uri,
709  strlen (uri));
710  da->update (da->ctx,
711  (const unsigned char *) ":",
712  1);
713  da->update (da->ctx,
714  (const unsigned char *) realm,
715  strlen (realm));
716  da->digest (da->ctx,
717  tmpnonce);
718  cvthex (tmpnonce,
719  sizeof (tmpnonce),
720  nonce);
721  cvthex (timestamp,
722  sizeof (timestamp),
723  timestamphex);
724  strncat (nonce,
725  timestamphex,
726  8);
727 }
728 
729 
743 static int
744 test_header (struct MHD_Connection *connection,
745  const char *key,
746  size_t key_size,
747  const char *value,
748  size_t value_size,
749  enum MHD_ValueKind kind)
750 {
751  struct MHD_HTTP_Header *pos;
752 
753  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
754  {
755  if (kind != pos->kind)
756  continue;
757  if (key_size != pos->header_size)
758  continue;
759  if (value_size != pos->value_size)
760  continue;
761  if (0 != memcmp (key,
762  pos->header,
763  key_size))
764  continue;
765  if ( (NULL == value) &&
766  (NULL == pos->value) )
767  return MHD_YES;
768  if ( (NULL == value) ||
769  (NULL == pos->value) ||
770  (0 != memcmp (value,
771  pos->value,
772  value_size)) )
773  continue;
774  return MHD_YES;
775  }
776  return MHD_NO;
777 }
778 
779 
790 static int
792  const char *args)
793 {
794  struct MHD_HTTP_Header *pos;
795  char *argb;
796  unsigned int num_headers;
797  int ret;
798 
799  argb = strdup (args);
800  if (NULL == argb)
801  {
802 #ifdef HAVE_MESSAGES
803  MHD_DLOG (connection->daemon,
804  _("Failed to allocate memory for copy of URI arguments\n"));
805 #endif /* HAVE_MESSAGES */
806  return MHD_NO;
807  }
808  ret = MHD_parse_arguments_ (connection,
810  argb,
811  &test_header,
812  &num_headers);
813  free (argb);
814  if (MHD_YES != ret)
815  {
816  return MHD_NO;
817  }
818  /* also check that the number of headers matches */
819  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
820  {
821  if (MHD_GET_ARGUMENT_KIND != pos->kind)
822  continue;
823  num_headers--;
824  }
825  if (0 != num_headers)
826  {
827  /* argument count mismatch */
828  return MHD_NO;
829  }
830  return MHD_YES;
831 }
832 
833 
853 static int
855  struct DigestAlgorithm *da,
856  const char *realm,
857  const char *username,
858  const char *password,
859  const uint8_t *digest,
860  unsigned int nonce_timeout)
861 {
862  struct MHD_Daemon *daemon = connection->daemon;
863  size_t len;
864  const char *header;
865  char nonce[MAX_NONCE_LENGTH];
866  char cnonce[MAX_NONCE_LENGTH];
867  char ha1[VLA_ARRAY_LEN_DIGEST(da->digest_size) * 2 + 1];
868  char qop[15]; /* auth,auth-int */
869  char nc[20];
870  char response[MAX_AUTH_RESPONSE_LENGTH];
871  const char *hentity = NULL; /* "auth-int" is not supported */
872  char noncehashexp[NONCE_STD_LEN(VLA_ARRAY_LEN_DIGEST(da->digest_size)) + 1];
873  uint32_t nonce_time;
874  uint32_t t;
875  size_t left; /* number of characters left in 'header' for 'uri' */
876  uint64_t nci;
877  char *qmark;
878 
879  VLA_CHECK_LEN_DIGEST(da->digest_size);
880  if (MHD_NO == MHD_lookup_connection_value_n (connection,
884  &header,
885  NULL))
886  return MHD_NO;
887  if (0 != strncmp (header,
888  _BASE,
890  return MHD_NO;
891  header += MHD_STATICSTR_LEN_ (_BASE);
892  left = strlen (header);
893 
894  {
895  char un[MAX_USERNAME_LENGTH];
896 
897  len = lookup_sub_value (un,
898  sizeof (un),
899  header,
900  "username");
901  if ( (0 == len) ||
902  (0 != strcmp (username,
903  un)) )
904  return MHD_NO;
905  left -= strlen ("username") + len;
906  }
907 
908  {
909  char r[MAX_REALM_LENGTH];
910 
911  len = lookup_sub_value (r,
912  sizeof (r),
913  header,
914  "realm");
915  if ( (0 == len) ||
916  (0 != strcmp (realm,
917  r)) )
918  return MHD_NO;
919  left -= strlen ("realm") + len;
920  }
921 
922  if (0 == (len = lookup_sub_value (nonce,
923  sizeof (nonce),
924  header,
925  "nonce")))
926  return MHD_NO;
927  left -= strlen ("nonce") + len;
928  if (left > 32 * 1024)
929  {
930  /* we do not permit URIs longer than 32k, as we want to
931  make sure to not blow our stack (or per-connection
932  heap memory limit). Besides, 32k is already insanely
933  large, but of course in theory the
934  #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
935  and would thus permit sending a >32k authorization
936  header value. */
937  return MHD_NO;
938  }
939  if (TIMESTAMP_BIN_SIZE * 2 !=
940  MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_BIN_SIZE * 2,
941  TIMESTAMP_BIN_SIZE * 2,
942  &nonce_time))
943  {
944 #ifdef HAVE_MESSAGES
945  MHD_DLOG (daemon,
946  _("Authentication failed, invalid timestamp format.\n"));
947 #endif
948  return MHD_NO;
949  }
950  t = (uint32_t) MHD_monotonic_sec_counter();
951  /*
952  * First level vetting for the nonce validity: if the timestamp
953  * attached to the nonce exceeds `nonce_timeout', then the nonce is
954  * invalid.
955  */
956  if ( (t > nonce_time + nonce_timeout) ||
957  (nonce_time + nonce_timeout < nonce_time) )
958  {
959  /* too old */
960  return MHD_INVALID_NONCE;
961  }
962 
963  calculate_nonce (nonce_time,
964  connection->method,
965  daemon->digest_auth_random,
966  daemon->digest_auth_rand_size,
967  connection->url,
968  realm,
969  da,
970  noncehashexp);
971  /*
972  * Second level vetting for the nonce validity
973  * if the timestamp attached to the nonce is valid
974  * and possibly fabricated (in case of an attack)
975  * the attacker must also know the random seed to be
976  * able to generate a "sane" nonce, which if he does
977  * not, the nonce fabrication process going to be
978  * very hard to achieve.
979  */
980 
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  */
1029  if (MHD_YES !=
1030  check_nonce_nc (connection,
1031  nonce,
1032  nci))
1033  {
1034  return MHD_NO;
1035  }
1036 
1037  {
1038  char *uri;
1039 
1040  uri = malloc (left + 1);
1041  if (NULL == uri)
1042  {
1043 #ifdef HAVE_MESSAGES
1044  MHD_DLOG(daemon,
1045  _("Failed to allocate memory for auth header processing\n"));
1046 #endif /* HAVE_MESSAGES */
1047  return MHD_NO;
1048  }
1049  if (0 == lookup_sub_value (uri,
1050  left + 1,
1051  header,
1052  "uri"))
1053  {
1054  free (uri);
1055  return MHD_NO;
1056  }
1057  if (NULL != digest)
1058  {
1059  /* This will initialize da->sessionkey (ha1) */
1060  digest_calc_ha1_from_digest (da->alg,
1061  da,
1062  digest,
1063  nonce,
1064  cnonce);
1065  }
1066  else
1067  {
1068  /* This will initialize da->sessionkey (ha1) */
1069  mhd_assert (NULL != password); /* NULL == digest => password != NULL */
1070  digest_calc_ha1_from_user (da->alg,
1071  username,
1072  realm,
1073  password,
1074  nonce,
1075  cnonce,
1076  da);
1077  }
1078  memcpy (ha1,
1079  da->sessionkey,
1080  sizeof (ha1));
1081  /* This will initialize da->sessionkey (respexp) */
1082  digest_calc_response (ha1,
1083  nonce,
1084  nc,
1085  cnonce,
1086  qop,
1087  connection->method,
1088  uri,
1089  hentity,
1090  da);
1091  qmark = strchr (uri,
1092  '?');
1093  if (NULL != qmark)
1094  *qmark = '\0';
1095 
1096  /* Need to unescape URI before comparing with connection->url */
1097  daemon->unescape_callback (daemon->unescape_callback_cls,
1098  connection,
1099  uri);
1100  if (0 != strcmp (uri,
1101  connection->url))
1102  {
1103 #ifdef HAVE_MESSAGES
1104  MHD_DLOG (daemon,
1105  _("Authentication failed, URI does not match.\n"));
1106 #endif
1107  free (uri);
1108  return MHD_NO;
1109  }
1110 
1111  {
1112  const char *args = qmark;
1113 
1114  if (NULL == args)
1115  args = "";
1116  else
1117  args++;
1118  if (MHD_YES !=
1119  check_argument_match (connection,
1120  args) )
1121  {
1122 #ifdef HAVE_MESSAGES
1123  MHD_DLOG (daemon,
1124  _("Authentication failed, arguments do not match.\n"));
1125 #endif
1126  free (uri);
1127  return MHD_NO;
1128  }
1129  }
1130  free (uri);
1131  return (0 == strcmp (response,
1132  da->sessionkey))
1133  ? MHD_YES
1134  : MHD_NO;
1135  }
1136 }
1137 
1138 
1156 _MHD_EXTERN int
1158  const char *realm,
1159  const char *username,
1160  const char *password,
1161  unsigned int nonce_timeout)
1162 {
1163  return MHD_digest_auth_check2 (connection,
1164  realm,
1165  username,
1166  password,
1167  nonce_timeout,
1169 }
1170 
1171 
1180 #define SETUP_DA(algo,da) \
1181  union { \
1182  struct MD5Context md5; \
1183  struct sha256_ctx sha256; \
1184  } ctx; \
1185  union { \
1186  char md5[MD5_DIGEST_SIZE * 2 + 1]; \
1187  char sha256[SHA256_DIGEST_SIZE * 2 + 1]; \
1188  } skey; \
1189  struct DigestAlgorithm da; \
1190  \
1191  do { \
1192  switch (algo) { \
1193  case MHD_DIGEST_ALG_MD5: \
1194  da.digest_size = MD5_DIGEST_SIZE; \
1195  da.ctx = &ctx.md5; \
1196  da.alg = "md5"; \
1197  da.sessionkey = skey.md5; \
1198  da.init = &MHD_MD5Init; \
1199  da.update = &MHD_MD5Update; \
1200  da.digest = &MHD_MD5Final; \
1201  break; \
1202  case MHD_DIGEST_ALG_AUTO: \
1203  /* auto == SHA256, fall-though thus intentional! */ \
1204  case MHD_DIGEST_ALG_SHA256: \
1205  da.digest_size = SHA256_DIGEST_SIZE; \
1206  da.ctx = &ctx.sha256; \
1207  da.alg = "sha-256"; \
1208  da.sessionkey = skey.sha256; \
1209  da.init = &MHD_SHA256_init; \
1210  da.update = &MHD_SHA256_update; \
1211  da.digest = &sha256_finish; \
1212  break; \
1213  } \
1214  } while(0)
1215 
1216 
1217 
1232 _MHD_EXTERN int
1234  const char *realm,
1235  const char *username,
1236  const char *password,
1237  unsigned int nonce_timeout,
1238  enum MHD_DigestAuthAlgorithm algo)
1239 {
1240  SETUP_DA (algo, da);
1241 
1242  return digest_auth_check_all (connection,
1243  &da,
1244  realm,
1245  username,
1246  password,
1247  NULL,
1248  nonce_timeout);
1249 }
1250 
1251 
1269 _MHD_EXTERN int
1271  const char *realm,
1272  const char *username,
1273  const uint8_t *digest,
1274  size_t digest_size,
1275  unsigned int nonce_timeout,
1276  enum MHD_DigestAuthAlgorithm algo)
1277 {
1278  SETUP_DA (algo, da);
1279 
1280  if (da.digest_size != digest_size)
1281  MHD_PANIC (_("digest size missmatch")); /* 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 
1341 int
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  _("Could not register nonce (is the nonce array size zero?).\n"));
1374 #endif
1375  return MHD_NO;
1376  }
1377  /* Building the authentication header */
1378  hlen = MHD_snprintf_ (NULL,
1379  0,
1380  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1381  realm,
1382  nonce,
1383  opaque,
1384  da.alg,
1385  signal_stale
1386  ? ",stale=\"true\""
1387  : "");
1388  if (hlen > 0)
1389  {
1390  char *header;
1391 
1392  header = MHD_calloc_ (1,
1393  hlen + 1);
1394  if (NULL == header)
1395  {
1396 #ifdef HAVE_MESSAGES
1397  MHD_DLOG(connection->daemon,
1398  _("Failed to allocate memory for auth response header\n"));
1399 #endif /* HAVE_MESSAGES */
1400  return MHD_NO;
1401  }
1402 
1403  if (MHD_snprintf_ (header,
1404  hlen + 1,
1405  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1406  realm,
1407  nonce,
1408  opaque,
1409  da.alg,
1410  signal_stale
1411  ? ",stale=\"true\""
1412  : "") == hlen)
1413  ret = MHD_add_response_header(response,
1415  header);
1416  else
1417  ret = MHD_NO;
1418  free (header);
1419  }
1420  else
1421  ret = MHD_NO;
1422  }
1423 
1424  if (MHD_YES == ret)
1425  {
1426  ret = MHD_queue_response (connection,
1428  response);
1429  }
1430  else
1431  {
1432 #ifdef HAVE_MESSAGES
1433  MHD_DLOG (connection->daemon,
1434  _("Failed to add Digest auth header\n"));
1435 #endif /* HAVE_MESSAGES */
1436  }
1437  return ret;
1438 }
1439 
1440 
1457 int
1459  const char *realm,
1460  const char *opaque,
1461  struct MHD_Response *response,
1462  int signal_stale)
1463 {
1464  return MHD_queue_auth_fail_response2 (connection,
1465  realm,
1466  opaque,
1467  response,
1468  signal_stale,
1470 }
1471 
1472 
1473 /* end of digestauth.c */
void * unescape_callback_cls
Definition: internal.h:1418
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:854
#define MHD_PANIC(msg)
Definition: internal.h:68
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition: mhd_str.c:359
Header for platform missing functions.
#define MAX_AUTH_RESPONSE_LENGTH
Definition: digestauth.c:106
void * data
Definition: microhttpd.h:2948
_MHD_EXTERN int MHD_add_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition: response.c:133
static void cvthex(const unsigned char *bin, size_t len, char *hex)
Definition: digestauth.c:176
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:669
#define MHD_mutex_unlock_chk_(pmutex)
Definition: mhd_locks.h:177
static size_t lookup_sub_value(char *dest, size_t size, const char *data, const char *key)
Definition: digestauth.c:430
internal monotonic clock functions implementations
#define TIMESTAMP_BIN_SIZE
Definition: digestauth.c:45
void * MHD_calloc_(size_t nelem, size_t elsize)
Definition: mhd_compat.c:96
#define MHD_YES
Definition: microhttpd.h:140
bool MHD_parse_arguments_(struct MHD_Request *request, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, unsigned int *num_headers)
Definition: internal.c:186
int MHD_str_equal_caseless_(const char *str1, const char *str2)
Definition: mhd_str.c:329
time_t MHD_monotonic_sec_counter(void)
char * value
Definition: internal.h:349
#define MHD_INVALID_NONCE
Definition: microhttpd.h:3407
enum MHD_ValueKind kind
Definition: internal.h:355
_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:1157
platform-specific includes for libmicrohttpd
_MHD_EXTERN int 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
char * header
Definition: internal.h:344
struct MHD_Daemon * daemon
Definition: internal.h:672
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:210
#define MHD_MD5_DIGEST_SIZE
Definition: microhttpd.h:305
uint64_t nmask
Definition: internal.h:237
_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
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:270
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition: microhttpd.h:533
const char * url
Definition: internal.h:713
_MHD_EXTERN int 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:1458
#define VLA_CHECK_LEN_DIGEST(n)
Definition: digestauth.c:85
size_t MHD_strx_to_uint64_n_(const char *str, size_t maxlen, uint64_t *out_val)
Definition: mhd_str.c:666
_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:1233
internal shared structures
char * method
Definition: internal.h:707
char nonce[MAX_NONCE_LENGTH]
Definition: internal.h:242
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition: microhttpd.h:611
MHD_DigestAuthAlgorithm
Definition: microhttpd.h:3437
_MHD_EXTERN char * MHD_digest_auth_get_username(struct MHD_Connection *connection)
Definition: digestauth.c:626
_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:1270
#define _MHD_EXTERN
Definition: mhd_options.h:51
#define MAX_NONCE_LENGTH
Definition: internal.h:217
static int check_argument_match(struct MHD_Connection *connection, const char *args)
Definition: digestauth.c:791
#define NULL
Definition: reason_phrase.c:30
MHD_ValueKind
Definition: microhttpd.h:1684
Header for string manipulating helpers.
size_t value_size
Definition: internal.h:286
#define MHD_STATICSTR_LEN_(macro)
Definition: mhd_str.h:45
UnescapeCallback unescape_callback
Definition: internal.h:1413
_MHD_EXTERN int MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
Definition: connection.c:4291
size_t header_size
Definition: internal.h:276
#define MHD_HTTP_UNAUTHORIZED
Definition: microhttpd.h:369
#define _BASE
Definition: digestauth.c:91
#define SETUP_DA(algo, da)
Definition: digestauth.c:1180
static int 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:744
#define mhd_assert(CHK)
Definition: mhd_assert.h:39
#define VLA_ARRAY_LEN_DIGEST(n)
Definition: digestauth.c:79
uint64_t nc
Definition: internal.h:231
struct MHD_HTTP_Header * next
Definition: internal.h:339
#define MHD_mutex_lock_chk_(pmutex)
Definition: mhd_locks.h:151
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:324
static int check_nonce_nc(struct MHD_Connection *connection, const char *nonce, uint64_t nc)
Definition: digestauth.c:523
#define NONCE_STD_LEN(digest_size)
Definition: digestauth.c:52
#define MAX_USERNAME_LENGTH
Definition: digestauth.c:96
#define _(String)
Definition: mhd_options.h:42
#define MAX_REALM_LENGTH
Definition: digestauth.c:101
_MHD_EXTERN int 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:957
#define MHD_NO
Definition: microhttpd.h:145
Calculation of SHA-256 digest.
struct MHD_HTTP_Header * headers_received
Definition: internal.h:665
limits values definitions
size_t MHD_strx_to_uint32_n_(const char *str, size_t maxlen, uint32_t *out_val)
Definition: mhd_str.c:581