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