GNU libmicrohttpd  0.9.68
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  strncpy (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  */
559  nn = &daemon->nnc[off];
560 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
561  MHD_mutex_lock_chk_ (&daemon->nnc_lock);
562 #endif
563  if (0 == nc)
564  {
565  /* Fresh nonce, reinitialize array */
566  memcpy (nn->nonce,
567  nonce,
568  noncelen);
569  nn->nc = 0;
570  nn->nmask = 0;
571 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
572  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
573 #endif
574  return MHD_YES;
575  }
576  /* Note that we use 64 here, as we do not store the
577  bit for 'nn->nc' itself in 'nn->nmask' */
578  if ( (nc < nn->nc) &&
579  (nc + 64 > nc /* checking for overflow */) &&
580  (nc + 64 >= nn->nc) &&
581  (0 == ((1LLU << (nn->nc - nc - 1)) & nn->nmask)) )
582  {
583  /* Out-of-order nonce, but within 64-bit bitmask, set bit */
584  nn->nmask |= (1LLU << (nn->nc - nc - 1));
585 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
586  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
587 #endif
588  return MHD_YES;
589  }
590 
591  if ( (nc <= nn->nc) ||
592  (0 != strcmp (nn->nonce,
593  nonce)) )
594  {
595  /* Nonce does not match, fail */
596 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
597  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
598 #endif
599 #ifdef HAVE_MESSAGES
600  MHD_DLOG (daemon,
601  _ (
602  "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n"));
603 #endif
604  return MHD_NO;
605  }
606  /* Nonce is larger, shift bitmask and bump limit */
607  if (64 > nc - nn->nc)
608  nn->nmask <<= (nc - nn->nc); /* small jump, less than mask width */
609  else
610  nn->nmask = 0; /* big jump, unset all bits in the mask */
611  nn->nc = nc;
612 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
613  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
614 #endif
615  return MHD_YES;
616 }
617 
618 
628 char *
630 {
631  size_t len;
632  char user[MAX_USERNAME_LENGTH];
633  const char *header;
634 
635  if (MHD_NO == MHD_lookup_connection_value_n (connection,
640  &header,
641  NULL))
642  return NULL;
643  if (0 != strncmp (header,
644  _BASE,
646  return NULL;
647  header += MHD_STATICSTR_LEN_ (_BASE);
648  if (0 == (len = lookup_sub_value (user,
649  sizeof (user),
650  header,
651  "username")))
652  return NULL;
653  return strdup (user);
654 }
655 
656 
672 static void
673 calculate_nonce (uint32_t nonce_time,
674  const char *method,
675  const char *rnd,
676  size_t rnd_size,
677  const char *uri,
678  const char *realm,
679  struct DigestAlgorithm *da,
680  char *nonce)
681 {
682  unsigned char timestamp[TIMESTAMP_BIN_SIZE];
683  unsigned char tmpnonce[VLA_ARRAY_LEN_DIGEST (da->digest_size)];
684  char timestamphex[TIMESTAMP_BIN_SIZE * 2 + 1];
685 
686  VLA_CHECK_LEN_DIGEST (da->digest_size);
687  da->init (da->ctx);
688  timestamp[0] = (unsigned char) ((nonce_time & 0xff000000) >> 0x18);
689  timestamp[1] = (unsigned char) ((nonce_time & 0x00ff0000) >> 0x10);
690  timestamp[2] = (unsigned char) ((nonce_time & 0x0000ff00) >> 0x08);
691  timestamp[3] = (unsigned char) ((nonce_time & 0x000000ff));
692  da->update (da->ctx,
693  timestamp,
694  sizeof (timestamp));
695  da->update (da->ctx,
696  (const unsigned char *) ":",
697  1);
698  da->update (da->ctx,
699  (const unsigned char *) method,
700  strlen (method));
701  da->update (da->ctx,
702  (const unsigned char *) ":",
703  1);
704  if (rnd_size > 0)
705  da->update (da->ctx,
706  (const unsigned char *) rnd,
707  rnd_size);
708  da->update (da->ctx,
709  (const unsigned char *) ":",
710  1);
711  da->update (da->ctx,
712  (const unsigned char *) uri,
713  strlen (uri));
714  da->update (da->ctx,
715  (const unsigned char *) ":",
716  1);
717  da->update (da->ctx,
718  (const unsigned char *) realm,
719  strlen (realm));
720  da->digest (da->ctx,
721  tmpnonce);
722  cvthex (tmpnonce,
723  sizeof (tmpnonce),
724  nonce);
725  cvthex (timestamp,
726  sizeof (timestamp),
727  timestamphex);
728  strncat (nonce,
729  timestamphex,
730  8);
731 }
732 
733 
747 static int
748 test_header (struct MHD_Connection *connection,
749  const char *key,
750  size_t key_size,
751  const char *value,
752  size_t value_size,
753  enum MHD_ValueKind kind)
754 {
755  struct MHD_HTTP_Header *pos;
756 
757  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
758  {
759  if (kind != pos->kind)
760  continue;
761  if (key_size != pos->header_size)
762  continue;
763  if (value_size != pos->value_size)
764  continue;
765  if (0 != memcmp (key,
766  pos->header,
767  key_size))
768  continue;
769  if ( (NULL == value) &&
770  (NULL == pos->value) )
771  return MHD_YES;
772  if ( (NULL == value) ||
773  (NULL == pos->value) ||
774  (0 != memcmp (value,
775  pos->value,
776  value_size)) )
777  continue;
778  return MHD_YES;
779  }
780  return MHD_NO;
781 }
782 
783 
794 static int
796  const char *args)
797 {
798  struct MHD_HTTP_Header *pos;
799  char *argb;
800  unsigned int num_headers;
801  int ret;
802 
803  argb = strdup (args);
804  if (NULL == argb)
805  {
806 #ifdef HAVE_MESSAGES
807  MHD_DLOG (connection->daemon,
808  _ ("Failed to allocate memory for copy of URI arguments\n"));
809 #endif /* HAVE_MESSAGES */
810  return MHD_NO;
811  }
812  ret = MHD_parse_arguments_ (connection,
814  argb,
815  &test_header,
816  &num_headers);
817  free (argb);
818  if (MHD_YES != ret)
819  {
820  return MHD_NO;
821  }
822  /* also check that the number of headers matches */
823  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
824  {
825  if (MHD_GET_ARGUMENT_KIND != pos->kind)
826  continue;
827  num_headers--;
828  }
829  if (0 != num_headers)
830  {
831  /* argument count mismatch */
832  return MHD_NO;
833  }
834  return MHD_YES;
835 }
836 
837 
857 static int
859  struct DigestAlgorithm *da,
860  const char *realm,
861  const char *username,
862  const char *password,
863  const uint8_t *digest,
864  unsigned int nonce_timeout)
865 {
866  struct MHD_Daemon *daemon = connection->daemon;
867  size_t len;
868  const char *header;
869  char nonce[MAX_NONCE_LENGTH];
870  char cnonce[MAX_NONCE_LENGTH];
871  char ha1[VLA_ARRAY_LEN_DIGEST (da->digest_size) * 2 + 1];
872  char qop[15]; /* auth,auth-int */
873  char nc[20];
874  char response[MAX_AUTH_RESPONSE_LENGTH];
875  const char *hentity = NULL; /* "auth-int" is not supported */
876  char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da->digest_size)) + 1];
877  uint32_t nonce_time;
878  uint32_t t;
879  size_t left; /* number of characters left in 'header' for 'uri' */
880  uint64_t nci;
881  char *qmark;
882 
883  VLA_CHECK_LEN_DIGEST (da->digest_size);
884  if (MHD_NO == MHD_lookup_connection_value_n (connection,
889  &header,
890  NULL))
891  return MHD_NO;
892  if (0 != strncmp (header,
893  _BASE,
895  return MHD_NO;
896  header += MHD_STATICSTR_LEN_ (_BASE);
897  left = strlen (header);
898 
899  {
900  char un[MAX_USERNAME_LENGTH];
901 
902  len = lookup_sub_value (un,
903  sizeof (un),
904  header,
905  "username");
906  if ( (0 == len) ||
907  (0 != strcmp (username,
908  un)) )
909  return MHD_NO;
910  left -= strlen ("username") + len;
911  }
912 
913  {
914  char r[MAX_REALM_LENGTH];
915 
916  len = lookup_sub_value (r,
917  sizeof (r),
918  header,
919  "realm");
920  if ( (0 == len) ||
921  (0 != strcmp (realm,
922  r)) )
923  return MHD_NO;
924  left -= strlen ("realm") + len;
925  }
926 
927  if (0 == (len = lookup_sub_value (nonce,
928  sizeof (nonce),
929  header,
930  "nonce")))
931  return MHD_NO;
932  left -= strlen ("nonce") + len;
933  if (left > 32 * 1024)
934  {
935  /* we do not permit URIs longer than 32k, as we want to
936  make sure to not blow our stack (or per-connection
937  heap memory limit). Besides, 32k is already insanely
938  large, but of course in theory the
939  #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
940  and would thus permit sending a >32k authorization
941  header value. */
942  return MHD_NO;
943  }
944  if (TIMESTAMP_BIN_SIZE * 2 !=
945  MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_BIN_SIZE * 2,
946  TIMESTAMP_BIN_SIZE * 2,
947  &nonce_time))
948  {
949 #ifdef HAVE_MESSAGES
950  MHD_DLOG (daemon,
951  _ ("Authentication failed, invalid timestamp format.\n"));
952 #endif
953  return MHD_NO;
954  }
955  t = (uint32_t) MHD_monotonic_sec_counter ();
956  /*
957  * First level vetting for the nonce validity: if the timestamp
958  * attached to the nonce exceeds `nonce_timeout', then the nonce is
959  * invalid.
960  */
961  if ( (t > nonce_time + nonce_timeout) ||
962  (nonce_time + nonce_timeout < nonce_time) )
963  {
964  /* too old */
965  return MHD_INVALID_NONCE;
966  }
967 
968  calculate_nonce (nonce_time,
969  connection->method,
970  daemon->digest_auth_random,
971  daemon->digest_auth_rand_size,
972  connection->url,
973  realm,
974  da,
975  noncehashexp);
976  /*
977  * Second level vetting for the nonce validity
978  * if the timestamp attached to the nonce is valid
979  * and possibly fabricated (in case of an attack)
980  * the attacker must also know the random seed to be
981  * able to generate a "sane" nonce, which if he does
982  * not, the nonce fabrication process going to be
983  * very hard to achieve.
984  */
985 
986  if (0 != strcmp (nonce,
987  noncehashexp))
988  {
989  return MHD_INVALID_NONCE;
990  }
991  if ( (0 == lookup_sub_value (cnonce,
992  sizeof (cnonce),
993  header,
994  "cnonce")) ||
995  (0 == lookup_sub_value (qop,
996  sizeof (qop),
997  header,
998  "qop")) ||
999  ( (0 != strcmp (qop,
1000  "auth")) &&
1001  (0 != strcmp (qop,
1002  "")) ) ||
1003  (0 == (len = lookup_sub_value (nc,
1004  sizeof (nc),
1005  header,
1006  "nc")) ) ||
1007  (0 == lookup_sub_value (response,
1008  sizeof (response),
1009  header,
1010  "response")) )
1011  {
1012 #ifdef HAVE_MESSAGES
1013  MHD_DLOG (daemon,
1014  _ ("Authentication failed, invalid format.\n"));
1015 #endif
1016  return MHD_NO;
1017  }
1018  if (len != MHD_strx_to_uint64_n_ (nc,
1019  len,
1020  &nci))
1021  {
1022 #ifdef HAVE_MESSAGES
1023  MHD_DLOG (daemon,
1024  _ ("Authentication failed, invalid nc format.\n"));
1025 #endif
1026  return MHD_NO; /* invalid nonce format */
1027  }
1028 
1029  /*
1030  * Checking if that combination of nonce and nc is sound
1031  * and not a replay attack attempt. Also adds the nonce
1032  * to the nonce-nc map if it does not exist there.
1033  */
1034  if (MHD_YES !=
1035  check_nonce_nc (connection,
1036  nonce,
1037  nci))
1038  {
1039  return MHD_NO;
1040  }
1041 
1042  {
1043  char *uri;
1044 
1045  uri = malloc (left + 1);
1046  if (NULL == uri)
1047  {
1048 #ifdef HAVE_MESSAGES
1049  MHD_DLOG (daemon,
1050  _ ("Failed to allocate memory for auth header processing\n"));
1051 #endif /* HAVE_MESSAGES */
1052  return MHD_NO;
1053  }
1054  if (0 == lookup_sub_value (uri,
1055  left + 1,
1056  header,
1057  "uri"))
1058  {
1059  free (uri);
1060  return MHD_NO;
1061  }
1062  if (NULL != digest)
1063  {
1064  /* This will initialize da->sessionkey (ha1) */
1065  digest_calc_ha1_from_digest (da->alg,
1066  da,
1067  digest,
1068  nonce,
1069  cnonce);
1070  }
1071  else
1072  {
1073  /* This will initialize da->sessionkey (ha1) */
1074  mhd_assert (NULL != password); /* NULL == digest => password != NULL */
1075  digest_calc_ha1_from_user (da->alg,
1076  username,
1077  realm,
1078  password,
1079  nonce,
1080  cnonce,
1081  da);
1082  }
1083  memcpy (ha1,
1084  da->sessionkey,
1085  sizeof (ha1));
1086  /* This will initialize da->sessionkey (respexp) */
1087  digest_calc_response (ha1,
1088  nonce,
1089  nc,
1090  cnonce,
1091  qop,
1092  connection->method,
1093  uri,
1094  hentity,
1095  da);
1096  qmark = strchr (uri,
1097  '?');
1098  if (NULL != qmark)
1099  *qmark = '\0';
1100 
1101  /* Need to unescape URI before comparing with connection->url */
1102  daemon->unescape_callback (daemon->unescape_callback_cls,
1103  connection,
1104  uri);
1105  if (0 != strcmp (uri,
1106  connection->url))
1107  {
1108 #ifdef HAVE_MESSAGES
1109  MHD_DLOG (daemon,
1110  _ ("Authentication failed, URI does not match.\n"));
1111 #endif
1112  free (uri);
1113  return MHD_NO;
1114  }
1115 
1116  {
1117  const char *args = qmark;
1118 
1119  if (NULL == args)
1120  args = "";
1121  else
1122  args++;
1123  if (MHD_YES !=
1124  check_argument_match (connection,
1125  args) )
1126  {
1127 #ifdef HAVE_MESSAGES
1128  MHD_DLOG (daemon,
1129  _ ("Authentication failed, arguments do not match.\n"));
1130 #endif
1131  free (uri);
1132  return MHD_NO;
1133  }
1134  }
1135  free (uri);
1136  return (0 == strcmp (response,
1137  da->sessionkey))
1138  ? MHD_YES
1139  : MHD_NO;
1140  }
1141 }
1142 
1143 
1161 _MHD_EXTERN int
1163  const char *realm,
1164  const char *username,
1165  const char *password,
1166  unsigned int nonce_timeout)
1167 {
1168  return MHD_digest_auth_check2 (connection,
1169  realm,
1170  username,
1171  password,
1172  nonce_timeout,
1174 }
1175 
1176 
1185 #define SETUP_DA(algo,da) \
1186  union { \
1187  struct MD5Context md5; \
1188  struct sha256_ctx sha256; \
1189  } ctx; \
1190  union { \
1191  char md5[MD5_DIGEST_SIZE * 2 + 1]; \
1192  char sha256[SHA256_DIGEST_SIZE * 2 + 1]; \
1193  } skey; \
1194  struct DigestAlgorithm da; \
1195  \
1196  do { \
1197  switch (algo) { \
1198  case MHD_DIGEST_ALG_MD5: \
1199  da.digest_size = MD5_DIGEST_SIZE; \
1200  da.ctx = &ctx.md5; \
1201  da.alg = "md5"; \
1202  da.sessionkey = skey.md5; \
1203  da.init = &MHD_MD5Init; \
1204  da.update = &MHD_MD5Update; \
1205  da.digest = &MHD_MD5Final; \
1206  break; \
1207  case MHD_DIGEST_ALG_AUTO: \
1208  /* auto == SHA256, fall-though thus intentional! */ \
1209  case MHD_DIGEST_ALG_SHA256: \
1210  da.digest_size = SHA256_DIGEST_SIZE; \
1211  da.ctx = &ctx.sha256; \
1212  da.alg = "sha-256"; \
1213  da.sessionkey = skey.sha256; \
1214  da.init = &MHD_SHA256_init; \
1215  da.update = &MHD_SHA256_update; \
1216  da.digest = &sha256_finish; \
1217  break; \
1218  } \
1219  } while (0)
1220 
1221 
1222 
1237 _MHD_EXTERN int
1239  const char *realm,
1240  const char *username,
1241  const char *password,
1242  unsigned int nonce_timeout,
1243  enum MHD_DigestAuthAlgorithm algo)
1244 {
1245  SETUP_DA (algo, da);
1246 
1247  return digest_auth_check_all (connection,
1248  &da,
1249  realm,
1250  username,
1251  password,
1252  NULL,
1253  nonce_timeout);
1254 }
1255 
1256 
1274 _MHD_EXTERN int
1276  const char *realm,
1277  const char *username,
1278  const uint8_t *digest,
1279  size_t digest_size,
1280  unsigned int nonce_timeout,
1281  enum MHD_DigestAuthAlgorithm algo)
1282 {
1283  SETUP_DA (algo, da);
1284 
1285  if (da.digest_size != digest_size)
1286  MHD_PANIC (_ ("digest size missmatch")); /* API violation! */
1287  return digest_auth_check_all (connection,
1288  &da,
1289  realm,
1290  username,
1291  NULL,
1292  digest,
1293  nonce_timeout);
1294 }
1295 
1296 
1314 _MHD_EXTERN int
1316  const char *realm,
1317  const char *username,
1318  const uint8_t digest[MHD_MD5_DIGEST_SIZE],
1319  unsigned int nonce_timeout)
1320 {
1321  return MHD_digest_auth_check_digest2 (connection,
1322  realm,
1323  username,
1324  digest,
1326  nonce_timeout,
1328 }
1329 
1330 
1346 int
1348  const char *realm,
1349  const char *opaque,
1350  struct MHD_Response *response,
1351  int signal_stale,
1352  enum MHD_DigestAuthAlgorithm algo)
1353 {
1354  int ret;
1355  int hlen;
1356  SETUP_DA (algo, da);
1357 
1358  {
1359  char nonce[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (da.digest_size)) + 1];
1360 
1361  VLA_CHECK_LEN_DIGEST (da.digest_size);
1362  /* Generating the server nonce */
1364  connection->method,
1365  connection->daemon->digest_auth_random,
1366  connection->daemon->digest_auth_rand_size,
1367  connection->url,
1368  realm,
1369  &da,
1370  nonce);
1371  if (MHD_YES !=
1372  check_nonce_nc (connection,
1373  nonce,
1374  0))
1375  {
1376 #ifdef HAVE_MESSAGES
1377  MHD_DLOG (connection->daemon,
1378  _ (
1379  "Could not register nonce (is the nonce array size zero?).\n"));
1380 #endif
1381  return MHD_NO;
1382  }
1383  /* Building the authentication header */
1384  hlen = MHD_snprintf_ (NULL,
1385  0,
1386  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1387  realm,
1388  nonce,
1389  opaque,
1390  da.alg,
1391  signal_stale
1392  ? ",stale=\"true\""
1393  : "");
1394  if (hlen > 0)
1395  {
1396  char *header;
1397 
1398  header = MHD_calloc_ (1,
1399  hlen + 1);
1400  if (NULL == header)
1401  {
1402 #ifdef HAVE_MESSAGES
1403  MHD_DLOG (connection->daemon,
1404  _ ("Failed to allocate memory for auth response header\n"));
1405 #endif /* HAVE_MESSAGES */
1406  return MHD_NO;
1407  }
1408 
1409  if (MHD_snprintf_ (header,
1410  hlen + 1,
1411  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1412  realm,
1413  nonce,
1414  opaque,
1415  da.alg,
1416  signal_stale
1417  ? ",stale=\"true\""
1418  : "") == hlen)
1419  ret = MHD_add_response_header (response,
1421  header);
1422  else
1423  ret = MHD_NO;
1424  free (header);
1425  }
1426  else
1427  ret = MHD_NO;
1428  }
1429 
1430  if (MHD_YES == ret)
1431  {
1432  ret = MHD_queue_response (connection,
1434  response);
1435  }
1436  else
1437  {
1438 #ifdef HAVE_MESSAGES
1439  MHD_DLOG (connection->daemon,
1440  _ ("Failed to add Digest auth header\n"));
1441 #endif /* HAVE_MESSAGES */
1442  }
1443  return ret;
1444 }
1445 
1446 
1463 int
1465  const char *realm,
1466  const char *opaque,
1467  struct MHD_Response *response,
1468  int signal_stale)
1469 {
1470  return MHD_queue_auth_fail_response2 (connection,
1471  realm,
1472  opaque,
1473  response,
1474  signal_stale,
1476 }
1477 
1478 
1479 /* end of digestauth.c */
void * unescape_callback_cls
Definition: internal.h:1415
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:858
#define MHD_PANIC(msg)
Definition: internal.h:69
int MHD_str_equal_caseless_n_(const char *const str1, const char *const str2, size_t maxlen)
Definition: mhd_str.c:363
Header for platform missing functions.
#define MAX_AUTH_RESPONSE_LENGTH
Definition: digestauth.c:108
void * data
Definition: microhttpd.h:3031
_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:178
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:673
#define MHD_mutex_unlock_chk_(pmutex)
Definition: mhd_locks.h:180
static size_t lookup_sub_value(char *dest, size_t size, const char *data, const char *key)
Definition: digestauth.c:432
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:333
time_t MHD_monotonic_sec_counter(void)
char * value
Definition: internal.h:352
#define MHD_INVALID_NONCE
Definition: microhttpd.h:3503
enum MHD_ValueKind kind
Definition: internal.h:358
_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:1162
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:1347
char * header
Definition: internal.h:347
struct MHD_Daemon * daemon
Definition: internal.h:675
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
#define MHD_MD5_DIGEST_SIZE
Definition: microhttpd.h:314
uint64_t nmask
Definition: internal.h:240
_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:1315
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
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition: microhttpd.h:554
const char * url
Definition: internal.h:716
_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:1464
#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:676
_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:1238
internal shared structures
char * method
Definition: internal.h:710
char nonce[MAX_NONCE_LENGTH]
Definition: internal.h:245
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition: microhttpd.h:632
MHD_DigestAuthAlgorithm
Definition: microhttpd.h:3533
_MHD_EXTERN char * MHD_digest_auth_get_username(struct MHD_Connection *connection)
Definition: digestauth.c:629
_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:1275
#define _MHD_EXTERN
Definition: mhd_options.h:51
#define MAX_NONCE_LENGTH
Definition: internal.h:220
static int check_argument_match(struct MHD_Connection *connection, const char *args)
Definition: digestauth.c:795
#define NULL
Definition: reason_phrase.c:30
MHD_ValueKind
Definition: microhttpd.h:1757
Header for string manipulating helpers.
size_t value_size
Definition: internal.h:289
#define MHD_STATICSTR_LEN_(macro)
Definition: mhd_str.h:45
UnescapeCallback unescape_callback
Definition: internal.h:1410
_MHD_EXTERN int MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
Definition: connection.c:3960
size_t header_size
Definition: internal.h:279
#define MHD_HTTP_UNAUTHORIZED
Definition: microhttpd.h:378
#define _BASE
Definition: digestauth.c:93
#define SETUP_DA(algo, da)
Definition: digestauth.c:1185
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:748
#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:234
struct MHD_HTTP_Header * next
Definition: internal.h:342
#define MHD_mutex_lock_chk_(pmutex)
Definition: mhd_locks.h:154
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
static int check_nonce_nc(struct MHD_Connection *connection, const char *nonce, uint64_t nc)
Definition: digestauth.c:525
#define NONCE_STD_LEN(digest_size)
Definition: digestauth.c:52
#define MAX_USERNAME_LENGTH
Definition: digestauth.c:98
#define _(String)
Definition: mhd_options.h:42
#define MAX_REALM_LENGTH
Definition: digestauth.c:103
_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:565
#define MHD_NO
Definition: microhttpd.h:145
Calculation of SHA-256 digest.
struct MHD_HTTP_Header * headers_received
Definition: internal.h:668
limits values definitions
size_t MHD_strx_to_uint32_n_(const char *str, size_t maxlen, uint32_t *out_val)
Definition: mhd_str.c:589