GNU libmicrohttpd  0.9.63
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 (NULL == (header =
633  MHD_lookup_connection_value (connection,
636  return NULL;
637  if (0 != strncmp (header,
638  _BASE,
640  return NULL;
641  header += MHD_STATICSTR_LEN_ (_BASE);
642  if (0 == (len = lookup_sub_value (user,
643  sizeof (user),
644  header,
645  "username")))
646  return NULL;
647  return strdup (user);
648 }
649 
650 
666 static void
667 calculate_nonce (uint32_t nonce_time,
668  const char *method,
669  const char *rnd,
670  size_t rnd_size,
671  const char *uri,
672  const char *realm,
673  struct DigestAlgorithm *da,
674  char *nonce)
675 {
676  unsigned char timestamp[TIMESTAMP_BIN_SIZE];
677  unsigned char tmpnonce[VLA_ARRAY_LEN_DIGEST(da->digest_size)];
678  char timestamphex[TIMESTAMP_BIN_SIZE * 2 + 1];
679 
680  VLA_CHECK_LEN_DIGEST(da->digest_size);
681  da->init (da->ctx);
682  timestamp[0] = (unsigned char)((nonce_time & 0xff000000) >> 0x18);
683  timestamp[1] = (unsigned char)((nonce_time & 0x00ff0000) >> 0x10);
684  timestamp[2] = (unsigned char)((nonce_time & 0x0000ff00) >> 0x08);
685  timestamp[3] = (unsigned char)((nonce_time & 0x000000ff));
686  da->update (da->ctx,
687  timestamp,
688  sizeof (timestamp));
689  da->update (da->ctx,
690  (const unsigned char *) ":",
691  1);
692  da->update (da->ctx,
693  (const unsigned char *) method,
694  strlen (method));
695  da->update (da->ctx,
696  (const unsigned char *) ":",
697  1);
698  if (rnd_size > 0)
699  da->update (da->ctx,
700  (const unsigned char *) rnd,
701  rnd_size);
702  da->update (da->ctx,
703  (const unsigned char *) ":",
704  1);
705  da->update (da->ctx,
706  (const unsigned char *) uri,
707  strlen (uri));
708  da->update (da->ctx,
709  (const unsigned char *) ":",
710  1);
711  da->update (da->ctx,
712  (const unsigned char *) realm,
713  strlen (realm));
714  da->digest (da->ctx,
715  tmpnonce);
716  cvthex (tmpnonce,
717  sizeof (tmpnonce),
718  nonce);
719  cvthex (timestamp,
720  sizeof (timestamp),
721  timestamphex);
722  strncat (nonce,
723  timestamphex,
724  8);
725 }
726 
727 
739 static int
740 test_header (struct MHD_Connection *connection,
741  const char *key,
742  const char *value,
743  enum MHD_ValueKind kind)
744 {
745  struct MHD_HTTP_Header *pos;
746 
747  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
748  {
749  if (kind != pos->kind)
750  continue;
751  if (0 != strcmp (key,
752  pos->header))
753  continue;
754  if ( (NULL == value) &&
755  (NULL == pos->value) )
756  return MHD_YES;
757  if ( (NULL == value) ||
758  (NULL == pos->value) ||
759  (0 != strcmp (value,
760  pos->value)) )
761  continue;
762  return MHD_YES;
763  }
764  return MHD_NO;
765 }
766 
767 
778 static int
780  const char *args)
781 {
782  struct MHD_HTTP_Header *pos;
783  char *argb;
784  unsigned int num_headers;
785  int ret;
786 
787  argb = strdup (args);
788  if (NULL == argb)
789  {
790 #ifdef HAVE_MESSAGES
791  MHD_DLOG (connection->daemon,
792  _("Failed to allocate memory for copy of URI arguments\n"));
793 #endif /* HAVE_MESSAGES */
794  return MHD_NO;
795  }
796  ret = MHD_parse_arguments_ (connection,
798  argb,
799  &test_header,
800  &num_headers);
801  free (argb);
802  if (MHD_YES != ret)
803  {
804  return MHD_NO;
805  }
806  /* also check that the number of headers matches */
807  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
808  {
809  if (MHD_GET_ARGUMENT_KIND != pos->kind)
810  continue;
811  num_headers--;
812  }
813  if (0 != num_headers)
814  {
815  /* argument count missmatch */
816  return MHD_NO;
817  }
818  return MHD_YES;
819 }
820 
821 
841 static int
843  struct DigestAlgorithm *da,
844  const char *realm,
845  const char *username,
846  const char *password,
847  const uint8_t *digest,
848  unsigned int nonce_timeout)
849 {
850  struct MHD_Daemon *daemon = connection->daemon;
851  size_t len;
852  const char *header;
853  char nonce[MAX_NONCE_LENGTH];
854  char cnonce[MAX_NONCE_LENGTH];
855  char ha1[VLA_ARRAY_LEN_DIGEST(da->digest_size) * 2 + 1];
856  char qop[15]; /* auth,auth-int */
857  char nc[20];
858  char response[MAX_AUTH_RESPONSE_LENGTH];
859  const char *hentity = NULL; /* "auth-int" is not supported */
860  char noncehashexp[NONCE_STD_LEN(VLA_ARRAY_LEN_DIGEST(da->digest_size)) + 1];
861  uint32_t nonce_time;
862  uint32_t t;
863  size_t left; /* number of characters left in 'header' for 'uri' */
864  uint64_t nci;
865  char *qmark;
866 
867  VLA_CHECK_LEN_DIGEST(da->digest_size);
868  header = MHD_lookup_connection_value (connection,
871  if (NULL == header)
872  return MHD_NO;
873  if (0 != strncmp (header,
874  _BASE,
876  return MHD_NO;
877  header += MHD_STATICSTR_LEN_ (_BASE);
878  left = strlen (header);
879 
880  {
881  char un[MAX_USERNAME_LENGTH];
882 
883  len = lookup_sub_value (un,
884  sizeof (un),
885  header,
886  "username");
887  if ( (0 == len) ||
888  (0 != strcmp (username,
889  un)) )
890  return MHD_NO;
891  left -= strlen ("username") + len;
892  }
893 
894  {
895  char r[MAX_REALM_LENGTH];
896 
897  len = lookup_sub_value (r,
898  sizeof (r),
899  header,
900  "realm");
901  if ( (0 == len) ||
902  (0 != strcmp (realm,
903  r)) )
904  return MHD_NO;
905  left -= strlen ("realm") + len;
906  }
907 
908  if (0 == (len = lookup_sub_value (nonce,
909  sizeof (nonce),
910  header,
911  "nonce")))
912  return MHD_NO;
913  left -= strlen ("nonce") + len;
914  if (left > 32 * 1024)
915  {
916  /* we do not permit URIs longer than 32k, as we want to
917  make sure to not blow our stack (or per-connection
918  heap memory limit). Besides, 32k is already insanely
919  large, but of course in theory the
920  #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
921  and would thus permit sending a >32k authorization
922  header value. */
923  return MHD_NO;
924  }
925  if (TIMESTAMP_BIN_SIZE * 2 !=
926  MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_BIN_SIZE * 2,
927  TIMESTAMP_BIN_SIZE * 2,
928  &nonce_time))
929  {
930 #ifdef HAVE_MESSAGES
931  MHD_DLOG (daemon,
932  _("Authentication failed, invalid timestamp format.\n"));
933 #endif
934  return MHD_NO;
935  }
936  t = (uint32_t) MHD_monotonic_sec_counter();
937  /*
938  * First level vetting for the nonce validity: if the timestamp
939  * attached to the nonce exceeds `nonce_timeout', then the nonce is
940  * invalid.
941  */
942  if ( (t > nonce_time + nonce_timeout) ||
943  (nonce_time + nonce_timeout < nonce_time) )
944  {
945  /* too old */
946  return MHD_INVALID_NONCE;
947  }
948 
949  calculate_nonce (nonce_time,
950  connection->method,
951  daemon->digest_auth_random,
952  daemon->digest_auth_rand_size,
953  connection->url,
954  realm,
955  da,
956  noncehashexp);
957  /*
958  * Second level vetting for the nonce validity
959  * if the timestamp attached to the nonce is valid
960  * and possibly fabricated (in case of an attack)
961  * the attacker must also know the random seed to be
962  * able to generate a "sane" nonce, which if he does
963  * not, the nonce fabrication process going to be
964  * very hard to achieve.
965  */
966 
967  if (0 != strcmp (nonce,
968  noncehashexp))
969  {
970  return MHD_INVALID_NONCE;
971  }
972  if ( (0 == lookup_sub_value (cnonce,
973  sizeof (cnonce),
974  header,
975  "cnonce")) ||
976  (0 == lookup_sub_value (qop,
977  sizeof (qop),
978  header,
979  "qop")) ||
980  ( (0 != strcmp (qop,
981  "auth")) &&
982  (0 != strcmp (qop,
983  "")) ) ||
984  (0 == (len = lookup_sub_value (nc,
985  sizeof (nc),
986  header,
987  "nc")) ) ||
988  (0 == lookup_sub_value (response,
989  sizeof (response),
990  header,
991  "response")) )
992  {
993 #ifdef HAVE_MESSAGES
994  MHD_DLOG (daemon,
995  _("Authentication failed, invalid format.\n"));
996 #endif
997  return MHD_NO;
998  }
999  if (len != MHD_strx_to_uint64_n_ (nc,
1000  len,
1001  &nci))
1002  {
1003 #ifdef HAVE_MESSAGES
1004  MHD_DLOG (daemon,
1005  _("Authentication failed, invalid nc format.\n"));
1006 #endif
1007  return MHD_NO; /* invalid nonce format */
1008  }
1009 
1010  /*
1011  * Checking if that combination of nonce and nc is sound
1012  * and not a replay attack attempt. Also adds the nonce
1013  * to the nonce-nc map if it does not exist there.
1014  */
1015  if (MHD_YES !=
1016  check_nonce_nc (connection,
1017  nonce,
1018  nci))
1019  {
1020  return MHD_NO;
1021  }
1022 
1023  {
1024  char *uri;
1025 
1026  uri = malloc (left + 1);
1027  if (NULL == uri)
1028  {
1029 #ifdef HAVE_MESSAGES
1030  MHD_DLOG(daemon,
1031  _("Failed to allocate memory for auth header processing\n"));
1032 #endif /* HAVE_MESSAGES */
1033  return MHD_NO;
1034  }
1035  if (0 == lookup_sub_value (uri,
1036  left + 1,
1037  header,
1038  "uri"))
1039  {
1040  free (uri);
1041  return MHD_NO;
1042  }
1043  if (NULL != digest)
1044  {
1045  /* This will initialize da->sessionkey (ha1) */
1046  digest_calc_ha1_from_digest (da->alg,
1047  da,
1048  digest,
1049  nonce,
1050  cnonce);
1051  }
1052  else
1053  {
1054  /* This will initialize da->sessionkey (ha1) */
1055  digest_calc_ha1_from_user (da->alg,
1056  username,
1057  realm,
1058  password,
1059  nonce,
1060  cnonce,
1061  da);
1062  }
1063  memcpy (ha1,
1064  da->sessionkey,
1065  sizeof (ha1));
1066  /* This will initialize da->sessionkey (respexp) */
1067  digest_calc_response (ha1,
1068  nonce,
1069  nc,
1070  cnonce,
1071  qop,
1072  connection->method,
1073  uri,
1074  hentity,
1075  da);
1076  qmark = strchr (uri,
1077  '?');
1078  if (NULL != qmark)
1079  *qmark = '\0';
1080 
1081  /* Need to unescape URI before comparing with connection->url */
1082  daemon->unescape_callback (daemon->unescape_callback_cls,
1083  connection,
1084  uri);
1085  if (0 != strcmp (uri,
1086  connection->url))
1087  {
1088 #ifdef HAVE_MESSAGES
1089  MHD_DLOG (daemon,
1090  _("Authentication failed, URI does not match.\n"));
1091 #endif
1092  free (uri);
1093  return MHD_NO;
1094  }
1095 
1096  {
1097  const char *args = qmark;
1098 
1099  if (NULL == args)
1100  args = "";
1101  else
1102  args++;
1103  if (MHD_YES !=
1104  check_argument_match (connection,
1105  args) )
1106  {
1107 #ifdef HAVE_MESSAGES
1108  MHD_DLOG (daemon,
1109  _("Authentication failed, arguments do not match.\n"));
1110 #endif
1111  free (uri);
1112  return MHD_NO;
1113  }
1114  }
1115  free (uri);
1116  return (0 == strcmp (response,
1117  da->sessionkey))
1118  ? MHD_YES
1119  : MHD_NO;
1120  }
1121 }
1122 
1123 
1141 _MHD_EXTERN int
1143  const char *realm,
1144  const char *username,
1145  const char *password,
1146  unsigned int nonce_timeout)
1147 {
1148  return MHD_digest_auth_check2 (connection,
1149  realm,
1150  username,
1151  password,
1152  nonce_timeout,
1154 }
1155 
1156 
1165 #define SETUP_DA(algo,da) \
1166  union { \
1167  struct MD5Context md5; \
1168  struct sha256_ctx sha256; \
1169  } ctx; \
1170  union { \
1171  char md5[MD5_DIGEST_SIZE * 2 + 1]; \
1172  char sha256[SHA256_DIGEST_SIZE * 2 + 1]; \
1173  } skey; \
1174  struct DigestAlgorithm da; \
1175  \
1176  switch (algo) { \
1177  case MHD_DIGEST_ALG_MD5: \
1178  da.digest_size = MD5_DIGEST_SIZE; \
1179  da.ctx = &ctx.md5; \
1180  da.alg = "md5"; \
1181  da.sessionkey = skey.md5; \
1182  da.init = &MD5Init; \
1183  da.update = &MD5Update; \
1184  da.digest = &MD5Final; \
1185  break; \
1186  case MHD_DIGEST_ALG_AUTO: \
1187  /* auto == SHA256, fall-though thus intentional! */ \
1188  case MHD_DIGEST_ALG_SHA256: \
1189  da.digest_size = SHA256_DIGEST_SIZE; \
1190  da.ctx = &ctx.sha256; \
1191  da.alg = "sha-256"; \
1192  da.sessionkey = skey.sha256; \
1193  da.init = &sha256_init; \
1194  da.update = &sha256_update; \
1195  da.digest = &sha256_digest; \
1196  break; \
1197  }
1198 
1199 
1200 
1215 _MHD_EXTERN int
1217  const char *realm,
1218  const char *username,
1219  const char *password,
1220  unsigned int nonce_timeout,
1221  enum MHD_DigestAuthAlgorithm algo)
1222 {
1223  SETUP_DA (algo, da);
1224 
1225  return digest_auth_check_all (connection,
1226  &da,
1227  realm,
1228  username,
1229  password,
1230  NULL,
1231  nonce_timeout);
1232 }
1233 
1234 
1252 _MHD_EXTERN int
1254  const char *realm,
1255  const char *username,
1256  const uint8_t *digest,
1257  size_t digest_size,
1258  unsigned int nonce_timeout,
1259  enum MHD_DigestAuthAlgorithm algo)
1260 {
1261  SETUP_DA (algo, da);
1262 
1263  if (da.digest_size != digest_size)
1264  MHD_PANIC (_("digest size missmatch")); /* API violation! */
1265  return digest_auth_check_all (connection,
1266  &da,
1267  realm,
1268  username,
1269  NULL,
1270  digest,
1271  nonce_timeout);
1272 }
1273 
1274 
1292 _MHD_EXTERN int
1294  const char *realm,
1295  const char *username,
1296  const uint8_t digest[MHD_MD5_DIGEST_SIZE],
1297  unsigned int nonce_timeout)
1298 {
1299  return MHD_digest_auth_check_digest2 (connection,
1300  realm,
1301  username,
1302  digest,
1304  nonce_timeout,
1306 }
1307 
1308 
1324 int
1326  const char *realm,
1327  const char *opaque,
1328  struct MHD_Response *response,
1329  int signal_stale,
1330  enum MHD_DigestAuthAlgorithm algo)
1331 {
1332  int ret;
1333  int hlen;
1334  SETUP_DA (algo, da);
1335 
1336  {
1337  char nonce[NONCE_STD_LEN(VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1];
1338 
1339  VLA_CHECK_LEN_DIGEST(da.digest_size);
1340  /* Generating the server nonce */
1342  connection->method,
1343  connection->daemon->digest_auth_random,
1344  connection->daemon->digest_auth_rand_size,
1345  connection->url,
1346  realm,
1347  &da,
1348  nonce);
1349  if (MHD_YES !=
1350  check_nonce_nc (connection,
1351  nonce,
1352  0))
1353  {
1354 #ifdef HAVE_MESSAGES
1355  MHD_DLOG (connection->daemon,
1356  _("Could not register nonce (is the nonce array size zero?).\n"));
1357 #endif
1358  return MHD_NO;
1359  }
1360  /* Building the authentication header */
1361  hlen = MHD_snprintf_ (NULL,
1362  0,
1363  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1364  realm,
1365  nonce,
1366  opaque,
1367  da.alg,
1368  signal_stale
1369  ? ",stale=\"true\""
1370  : "");
1371  if (hlen > 0)
1372  {
1373  char *header;
1374 
1375  header = MHD_calloc_ (1,
1376  hlen + 1);
1377  if (NULL == header)
1378  {
1379 #ifdef HAVE_MESSAGES
1380  MHD_DLOG(connection->daemon,
1381  _("Failed to allocate memory for auth response header\n"));
1382 #endif /* HAVE_MESSAGES */
1383  return MHD_NO;
1384  }
1385 
1386  if (MHD_snprintf_ (header,
1387  hlen + 1,
1388  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1389  realm,
1390  nonce,
1391  opaque,
1392  da.alg,
1393  signal_stale
1394  ? ",stale=\"true\""
1395  : "") == hlen)
1396  ret = MHD_add_response_header(response,
1398  header);
1399  else
1400  ret = MHD_NO;
1401  free (header);
1402  }
1403  else
1404  ret = MHD_NO;
1405  }
1406 
1407  if (MHD_YES == ret)
1408  {
1409  ret = MHD_queue_response (connection,
1411  response);
1412  }
1413  else
1414  {
1415 #ifdef HAVE_MESSAGES
1416  MHD_DLOG (connection->daemon,
1417  _("Failed to add Digest auth header\n"));
1418 #endif /* HAVE_MESSAGES */
1419  }
1420  return ret;
1421 }
1422 
1423 
1440 int
1442  const char *realm,
1443  const char *opaque,
1444  struct MHD_Response *response,
1445  int signal_stale)
1446 {
1447  return MHD_queue_auth_fail_response2 (connection,
1448  realm,
1449  opaque,
1450  response,
1451  signal_stale,
1453 }
1454 
1455 
1456 /* end of digestauth.c */
void * unescape_callback_cls
Definition: internal.h:1408
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:842
#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
_MHD_EXTERN const char * MHD_lookup_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key)
Definition: connection.c:784
void * data
Definition: microhttpd.h:2709
_MHD_EXTERN int MHD_add_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition: response.c:118
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:667
#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:134
bool MHD_parse_arguments_(struct MHD_Request *request, enum MHD_ValueKind kind, char *args, MHD_ArgumentIterator_ cb, unsigned int *num_headers)
Definition: internal.c: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:3167
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:1142
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:1325
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:299
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:1293
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:440
const char * url
Definition: internal.h:703
_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:1441
#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:1216
internal shared structures
char * method
Definition: internal.h:697
static int test_header(struct MHD_Connection *connection, const char *key, const char *value, enum MHD_ValueKind kind)
Definition: digestauth.c:740
char nonce[MAX_NONCE_LENGTH]
Definition: internal.h:242
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition: microhttpd.h:518
MHD_DigestAuthAlgorithm
Definition: microhttpd.h:3197
_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:1253
#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:779
#define NULL
Definition: reason_phrase.c:30
MHD_ValueKind
Definition: microhttpd.h:1554
Header for string manipulating helpers.
#define MHD_STATICSTR_LEN_(macro)
Definition: mhd_str.h:45
UnescapeCallback unescape_callback
Definition: internal.h:1403
_MHD_EXTERN int MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
Definition: connection.c:4018
#define MHD_HTTP_UNAUTHORIZED
Definition: microhttpd.h:336
#define _BASE
Definition: digestauth.c:91
#define SETUP_DA(algo, da)
Definition: digestauth.c:1165
#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
#define MHD_NO
Definition: microhttpd.h:139
struct MHD_HTTP_Header * headers_received
Definition: internal.h:655
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