GNU libmicrohttpd  0.9.62
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Macros Groups
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 
58 #define _BASE "Digest "
59 
63 #define MAX_USERNAME_LENGTH 128
64 
68 #define MAX_REALM_LENGTH 256
69 
73 #define MAX_AUTH_RESPONSE_LENGTH 256
74 
75 
81 struct DigestAlgorithm
82 {
86  unsigned int digest_size;
87 
92  void *ctx;
93 
97  const char *alg;
98 
102  char *sessionkey;
103 
107  void
108  (*init)(void *ctx);
109 
117  void
118  (*update)(void *ctx,
119  const uint8_t *data,
120  size_t length);
121 
129  void
130  (*digest)(void *ctx,
131  uint8_t *digest);
132 };
133 
134 
142 static void
143 cvthex (const unsigned char *bin,
144  size_t len,
145  char *hex)
146 {
147  size_t i;
148  unsigned int j;
149 
150  for (i = 0; i < len; ++i)
151  {
152  j = (bin[i] >> 4) & 0x0f;
153  hex[i * 2] = (char)((j <= 9) ? (j + '0') : (j - 10 + 'a'));
154  j = bin[i] & 0x0f;
155  hex[i * 2 + 1] = (char)((j <= 9) ? (j + '0') : (j - 10 + 'a'));
156  }
157  hex[len * 2] = '\0';
158 }
159 
160 
176 static void
178  struct DigestAlgorithm *da,
179  const uint8_t *digest,
180  const char *nonce,
181  const char *cnonce)
182 {
183  if ( (MHD_str_equal_caseless_(alg,
184  "md5-sess")) ||
186  "sha-256-sess")) )
187  {
188  uint8_t dig[da->digest_size];
189 
190  da->init (da->ctx);
191  da->update (da->ctx,
192  digest,
194  da->update (da->ctx,
195  (const unsigned char *) ":",
196  1);
197  da->update (da->ctx,
198  (const unsigned char *) nonce,
199  strlen (nonce));
200  da->update (da->ctx,
201  (const unsigned char *) ":",
202  1);
203  da->update (da->ctx,
204  (const unsigned char *) cnonce,
205  strlen (cnonce));
206  da->digest (da->ctx,
207  dig);
208  cvthex (dig,
209  sizeof (dig),
210  da->sessionkey);
211  }
212  else
213  {
214  cvthex (digest,
215  da->digest_size,
216  da->sessionkey);
217  }
218 }
219 
220 
235 static void
236 digest_calc_ha1_from_user (const char *alg,
237  const char *username,
238  const char *realm,
239  const char *password,
240  const char *nonce,
241  const char *cnonce,
242  struct DigestAlgorithm *da)
243 {
244  unsigned char ha1[da->digest_size];
245 
246  da->init (da->ctx);
247  da->update (da->ctx,
248  (const unsigned char *) username,
249  strlen (username));
250  da->update (da->ctx,
251  (const unsigned char *) ":",
252  1);
253  da->update (da->ctx,
254  (const unsigned char *) realm,
255  strlen (realm));
256  da->update (da->ctx,
257  (const unsigned char *) ":",
258  1);
259  da->update (da->ctx,
260  (const unsigned char *) password,
261  strlen (password));
262  da->digest (da->ctx,
263  ha1);
265  da,
266  ha1,
267  nonce,
268  cnonce);
269 }
270 
271 
288 static void
289 digest_calc_response (const char *ha1,
290  const char *nonce,
291  const char *noncecount,
292  const char *cnonce,
293  const char *qop,
294  const char *method,
295  const char *uri,
296  const char *hentity,
297  struct DigestAlgorithm *da)
298 {
299  unsigned char ha2[da->digest_size];
300  unsigned char resphash[da->digest_size];
301  (void)hentity; /* Unused. Silence compiler warning. */
302 
303  da->init (da->ctx);
304  da->update (da->ctx,
305  (const unsigned char *) method,
306  strlen (method));
307  da->update (da->ctx,
308  (const unsigned char *) ":",
309  1);
310  da->update (da->ctx,
311  (const unsigned char *) uri,
312  strlen (uri));
313 #if 0
314  if (0 == strcasecmp (qop,
315  "auth-int"))
316  {
317  /* This is dead code since the rest of this module does
318  not support auth-int. */
319  da->update (da->ctx,
320  ":",
321  1);
322  if (NULL != hentity)
323  da->update (da->ctx,
324  hentity,
325  strlen (hentity));
326  }
327 #endif
328  da->digest (da->ctx,
329  ha2);
330  cvthex (ha2,
331  da->digest_size,
332  da->sessionkey);
333  da->init (da->ctx);
334  /* calculate response */
335  da->update (da->ctx,
336  (const unsigned char *) ha1,
337  da->digest_size * 2);
338  da->update (da->ctx,
339  (const unsigned char *) ":",
340  1);
341  da->update (da->ctx,
342  (const unsigned char *) nonce,
343  strlen (nonce));
344  da->update (da->ctx,
345  (const unsigned char*) ":",
346  1);
347  if ('\0' != *qop)
348  {
349  da->update (da->ctx,
350  (const unsigned char *) noncecount,
351  strlen (noncecount));
352  da->update (da->ctx,
353  (const unsigned char *) ":",
354  1);
355  da->update (da->ctx,
356  (const unsigned char *) cnonce,
357  strlen (cnonce));
358  da->update (da->ctx,
359  (const unsigned char *) ":",
360  1);
361  da->update (da->ctx,
362  (const unsigned char *) qop,
363  strlen (qop));
364  da->update (da->ctx,
365  (const unsigned char *) ":",
366  1);
367  }
368  da->update (da->ctx,
369  (const unsigned char *) da->sessionkey,
370  da->digest_size * 2);
371  da->digest (da->ctx,
372  resphash);
373  cvthex (resphash,
374  sizeof(resphash),
375  da->sessionkey);
376 }
377 
378 
393 static size_t
394 lookup_sub_value (char *dest,
395  size_t size,
396  const char *data,
397  const char *key)
398 {
399  size_t keylen;
400  size_t len;
401  const char *ptr;
402  const char *eq;
403  const char *q1;
404  const char *q2;
405  const char *qn;
406 
407  if (0 == size)
408  return 0;
409  keylen = strlen (key);
410  ptr = data;
411  while ('\0' != *ptr)
412  {
413  if (NULL == (eq = strchr (ptr,
414  '=')))
415  return 0;
416  q1 = eq + 1;
417  while (' ' == *q1)
418  q1++;
419  if ('\"' != *q1)
420  {
421  q2 = strchr (q1,
422  ',');
423  qn = q2;
424  }
425  else
426  {
427  q1++;
428  q2 = strchr (q1,
429  '\"');
430  if (NULL == q2)
431  return 0; /* end quote not found */
432  qn = q2 + 1;
433  }
434  if ( (MHD_str_equal_caseless_n_(ptr,
435  key,
436  keylen)) &&
437  (eq == &ptr[keylen]) )
438  {
439  if (NULL == q2)
440  {
441  len = strlen (q1) + 1;
442  if (size > len)
443  size = len;
444  size--;
445  strncpy (dest,
446  q1,
447  size);
448  dest[size] = '\0';
449  return size;
450  }
451  else
452  {
453  if (size > (size_t) ((q2 - q1) + 1))
454  size = (q2 - q1) + 1;
455  size--;
456  memcpy (dest,
457  q1,
458  size);
459  dest[size] = '\0';
460  return size;
461  }
462  }
463  if (NULL == qn)
464  return 0;
465  ptr = strchr (qn,
466  ',');
467  if (NULL == ptr)
468  return 0;
469  ptr++;
470  while (' ' == *ptr)
471  ptr++;
472  }
473  return 0;
474 }
475 
476 
486 static int
487 check_nonce_nc (struct MHD_Connection *connection,
488  const char *nonce,
489  uint64_t nc)
490 {
491  struct MHD_Daemon *daemon = connection->daemon;
492  struct MHD_NonceNc *nn;
493  uint32_t off;
494  uint32_t mod;
495  const char *np;
496  size_t noncelen;
497 
498  noncelen = strlen (nonce) + 1;
499  if (MAX_NONCE_LENGTH < noncelen)
500  return MHD_NO; /* This should be impossible, but static analysis
501  tools have a hard time with it *and* this also
502  protects against unsafe modifications that may
503  happen in the future... */
504  mod = daemon->nonce_nc_size;
505  if (0 == mod)
506  return MHD_NO; /* no array! */
507  /* super-fast xor-based "hash" function for HT lookup in nonce array */
508  off = 0;
509  np = nonce;
510  while ('\0' != *np)
511  {
512  off = (off << 8) | (*np ^ (off >> 24));
513  np++;
514  }
515  off = off % mod;
516  /*
517  * Look for the nonce, if it does exist and its corresponding
518  * nonce counter is less than the current nonce counter by 1,
519  * then only increase the nonce counter by one.
520  */
521  nn = &daemon->nnc[off];
522 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
523  MHD_mutex_lock_chk_ (&daemon->nnc_lock);
524 #endif
525  if (0 == nc)
526  {
527  /* Fresh nonce, reinitialize array */
528  memcpy (nn->nonce,
529  nonce,
530  noncelen);
531  nn->nc = 0;
532  nn->nmask = 0;
533 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
534  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
535 #endif
536  return MHD_YES;
537  }
538  /* Note that we use 64 here, as we do not store the
539  bit for 'nn->nc' itself in 'nn->nmask' */
540  if ( (nc < nn->nc) &&
541  (nc + 64 > nc /* checking for overflow */) &&
542  (nc + 64 >= nn->nc) &&
543  (0 == ((1LLU << (nn->nc - nc - 1)) & nn->nmask)) )
544  {
545  /* Out-of-order nonce, but within 64-bit bitmask, set bit */
546  nn->nmask |= (1LLU << (nn->nc - nc - 1));
547 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
548  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
549 #endif
550  return MHD_YES;
551  }
552 
553  if ( (nc <= nn->nc) ||
554  (0 != strcmp (nn->nonce,
555  nonce)) )
556  {
557  /* Nonce does not match, fail */
558 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
559  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
560 #endif
561 #ifdef HAVE_MESSAGES
562  MHD_DLOG (daemon,
563  _("Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n"));
564 #endif
565  return MHD_NO;
566  }
567  /* Nonce is larger, shift bitmask and bump limit */
568  if (64 > nc - nn->nc)
569  nn->nmask <<= (nc - nn->nc); /* small jump, less than mask width */
570  else
571  nn->nmask = 0; /* big jump, unset all bits in the mask */
572  nn->nc = nc;
573 #if defined(MHD_USE_POSIX_THREADS) || defined(MHD_USE_W32_THREADS)
574  MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
575 #endif
576  return MHD_YES;
577 }
578 
579 
589 char *
591 {
592  size_t len;
593  char user[MAX_USERNAME_LENGTH];
594  const char *header;
595 
596  if (NULL == (header =
597  MHD_lookup_connection_value (connection,
600  return NULL;
601  if (0 != strncmp (header,
602  _BASE,
604  return NULL;
605  header += MHD_STATICSTR_LEN_ (_BASE);
606  if (0 == (len = lookup_sub_value (user,
607  sizeof (user),
608  header,
609  "username")))
610  return NULL;
611  return strdup (user);
612 }
613 
614 
630 static void
631 calculate_nonce (uint32_t nonce_time,
632  const char *method,
633  const char *rnd,
634  size_t rnd_size,
635  const char *uri,
636  const char *realm,
637  struct DigestAlgorithm *da,
638  char *nonce)
639 {
640  unsigned char timestamp[TIMESTAMP_BIN_SIZE];
641  unsigned char tmpnonce[da->digest_size];
642  char timestamphex[TIMESTAMP_BIN_SIZE * 2 + 1];
643 
644  da->init (da->ctx);
645  timestamp[0] = (unsigned char)((nonce_time & 0xff000000) >> 0x18);
646  timestamp[1] = (unsigned char)((nonce_time & 0x00ff0000) >> 0x10);
647  timestamp[2] = (unsigned char)((nonce_time & 0x0000ff00) >> 0x08);
648  timestamp[3] = (unsigned char)((nonce_time & 0x000000ff));
649  da->update (da->ctx,
650  timestamp,
651  sizeof (timestamp));
652  da->update (da->ctx,
653  (const unsigned char *) ":",
654  1);
655  da->update (da->ctx,
656  (const unsigned char *) method,
657  strlen (method));
658  da->update (da->ctx,
659  (const unsigned char *) ":",
660  1);
661  if (rnd_size > 0)
662  da->update (da->ctx,
663  (const unsigned char *) rnd,
664  rnd_size);
665  da->update (da->ctx,
666  (const unsigned char *) ":",
667  1);
668  da->update (da->ctx,
669  (const unsigned char *) uri,
670  strlen (uri));
671  da->update (da->ctx,
672  (const unsigned char *) ":",
673  1);
674  da->update (da->ctx,
675  (const unsigned char *) realm,
676  strlen (realm));
677  da->digest (da->ctx,
678  tmpnonce);
679  cvthex (tmpnonce,
680  sizeof (tmpnonce),
681  nonce);
682  cvthex (timestamp,
683  sizeof (timestamp),
684  timestamphex);
685  strncat (nonce,
686  timestamphex,
687  8);
688 }
689 
690 
702 static int
703 test_header (struct MHD_Connection *connection,
704  const char *key,
705  const char *value,
706  enum MHD_ValueKind kind)
707 {
708  struct MHD_HTTP_Header *pos;
709 
710  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
711  {
712  if (kind != pos->kind)
713  continue;
714  if (0 != strcmp (key,
715  pos->header))
716  continue;
717  if ( (NULL == value) &&
718  (NULL == pos->value) )
719  return MHD_YES;
720  if ( (NULL == value) ||
721  (NULL == pos->value) ||
722  (0 != strcmp (value,
723  pos->value)) )
724  continue;
725  return MHD_YES;
726  }
727  return MHD_NO;
728 }
729 
730 
741 static int
743  const char *args)
744 {
745  struct MHD_HTTP_Header *pos;
746  char *argb;
747  unsigned int num_headers;
748  int ret;
749 
750  argb = strdup (args);
751  if (NULL == argb)
752  {
753 #ifdef HAVE_MESSAGES
754  MHD_DLOG (connection->daemon,
755  _("Failed to allocate memory for copy of URI arguments\n"));
756 #endif /* HAVE_MESSAGES */
757  return MHD_NO;
758  }
759  ret = MHD_parse_arguments_ (connection,
761  argb,
762  &test_header,
763  &num_headers);
764  free (argb);
765  if (MHD_YES != ret)
766  {
767  return MHD_NO;
768  }
769  /* also check that the number of headers matches */
770  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
771  {
772  if (MHD_GET_ARGUMENT_KIND != pos->kind)
773  continue;
774  num_headers--;
775  }
776  if (0 != num_headers)
777  {
778  /* argument count missmatch */
779  return MHD_NO;
780  }
781  return MHD_YES;
782 }
783 
784 
804 static int
806  struct DigestAlgorithm *da,
807  const char *realm,
808  const char *username,
809  const char *password,
810  const uint8_t *digest,
811  unsigned int nonce_timeout)
812 {
813  struct MHD_Daemon *daemon = connection->daemon;
814  size_t len;
815  const char *header;
816  char nonce[MAX_NONCE_LENGTH];
817  char cnonce[MAX_NONCE_LENGTH];
818  char ha1[da->digest_size * 2 + 1];
819  char qop[15]; /* auth,auth-int */
820  char nc[20];
821  char response[MAX_AUTH_RESPONSE_LENGTH];
822  const char *hentity = NULL; /* "auth-int" is not supported */
823  char noncehashexp[NONCE_STD_LEN(da->digest_size) + 1];
824  uint32_t nonce_time;
825  uint32_t t;
826  size_t left; /* number of characters left in 'header' for 'uri' */
827  uint64_t nci;
828 
829  header = MHD_lookup_connection_value (connection,
832  if (NULL == header)
833  return MHD_NO;
834  if (0 != strncmp (header,
835  _BASE,
837  return MHD_NO;
838  header += MHD_STATICSTR_LEN_ (_BASE);
839  left = strlen (header);
840 
841  {
842  char un[MAX_USERNAME_LENGTH];
843 
844  len = lookup_sub_value (un,
845  sizeof (un),
846  header,
847  "username");
848  if ( (0 == len) ||
849  (0 != strcmp (username,
850  un)) )
851  return MHD_NO;
852  left -= strlen ("username") + len;
853  }
854 
855  {
856  char r[MAX_REALM_LENGTH];
857 
858  len = lookup_sub_value (r,
859  sizeof (r),
860  header,
861  "realm");
862  if ( (0 == len) ||
863  (0 != strcmp (realm,
864  r)) )
865  return MHD_NO;
866  left -= strlen ("realm") + len;
867  }
868 
869  if (0 == (len = lookup_sub_value (nonce,
870  sizeof (nonce),
871  header,
872  "nonce")))
873  return MHD_NO;
874  left -= strlen ("nonce") + len;
875  if (left > 32 * 1024)
876  {
877  /* we do not permit URIs longer than 32k, as we want to
878  make sure to not blow our stack (or per-connection
879  heap memory limit). Besides, 32k is already insanely
880  large, but of course in theory the
881  #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
882  and would thus permit sending a >32k authorization
883  header value. */
884  return MHD_NO;
885  }
886  if (TIMESTAMP_BIN_SIZE * 2 !=
887  MHD_strx_to_uint32_n_ (nonce + len - TIMESTAMP_BIN_SIZE * 2,
888  TIMESTAMP_BIN_SIZE * 2,
889  &nonce_time))
890  {
891 #ifdef HAVE_MESSAGES
892  MHD_DLOG (daemon,
893  _("Authentication failed, invalid timestamp format.\n"));
894 #endif
895  return MHD_NO;
896  }
897  t = (uint32_t) MHD_monotonic_sec_counter();
898  /*
899  * First level vetting for the nonce validity: if the timestamp
900  * attached to the nonce exceeds `nonce_timeout', then the nonce is
901  * invalid.
902  */
903  if ( (t > nonce_time + nonce_timeout) ||
904  (nonce_time + nonce_timeout < nonce_time) )
905  {
906  /* too old */
907  return MHD_INVALID_NONCE;
908  }
909 
910  calculate_nonce (nonce_time,
911  connection->method,
912  daemon->digest_auth_random,
913  daemon->digest_auth_rand_size,
914  connection->url,
915  realm,
916  da,
917  noncehashexp);
918  /*
919  * Second level vetting for the nonce validity
920  * if the timestamp attached to the nonce is valid
921  * and possibly fabricated (in case of an attack)
922  * the attacker must also know the random seed to be
923  * able to generate a "sane" nonce, which if he does
924  * not, the nonce fabrication process going to be
925  * very hard to achieve.
926  */
927 
928  if (0 != strcmp (nonce,
929  noncehashexp))
930  {
931  return MHD_INVALID_NONCE;
932  }
933  if ( (0 == lookup_sub_value (cnonce,
934  sizeof (cnonce),
935  header,
936  "cnonce")) ||
937  (0 == lookup_sub_value (qop,
938  sizeof (qop),
939  header,
940  "qop")) ||
941  ( (0 != strcmp (qop,
942  "auth")) &&
943  (0 != strcmp (qop,
944  "")) ) ||
945  (0 == (len = lookup_sub_value (nc,
946  sizeof (nc),
947  header,
948  "nc")) ) ||
949  (0 == lookup_sub_value (response,
950  sizeof (response),
951  header,
952  "response")) )
953  {
954 #ifdef HAVE_MESSAGES
955  MHD_DLOG (daemon,
956  _("Authentication failed, invalid format.\n"));
957 #endif
958  return MHD_NO;
959  }
960  if (len != MHD_strx_to_uint64_n_ (nc,
961  len,
962  &nci))
963  {
964 #ifdef HAVE_MESSAGES
965  MHD_DLOG (daemon,
966  _("Authentication failed, invalid nc format.\n"));
967 #endif
968  return MHD_NO; /* invalid nonce format */
969  }
970 
971  /*
972  * Checking if that combination of nonce and nc is sound
973  * and not a replay attack attempt. Also adds the nonce
974  * to the nonce-nc map if it does not exist there.
975  */
976  if (MHD_YES !=
977  check_nonce_nc (connection,
978  nonce,
979  nci))
980  {
981  return MHD_NO;
982  }
983 
984  {
985  char *uri;
986 
987  uri = malloc (left + 1);
988  if (NULL == uri)
989  {
990 #ifdef HAVE_MESSAGES
991  MHD_DLOG(daemon,
992  _("Failed to allocate memory for auth header processing\n"));
993 #endif /* HAVE_MESSAGES */
994  return MHD_NO;
995  }
996  if (0 == lookup_sub_value (uri,
997  left + 1,
998  header,
999  "uri"))
1000  {
1001  free (uri);
1002  return MHD_NO;
1003  }
1004  if (NULL != digest)
1005  {
1006  /* This will initialize da->sessionkey (ha1) */
1007  digest_calc_ha1_from_digest (da->alg,
1008  da,
1009  digest,
1010  nonce,
1011  cnonce);
1012  }
1013  else
1014  {
1015  /* This will initialize da->sessionkey (ha1) */
1016  digest_calc_ha1_from_user (da->alg,
1017  username,
1018  realm,
1019  password,
1020  nonce,
1021  cnonce,
1022  da);
1023  }
1024  memcpy (ha1,
1025  da->sessionkey,
1026  sizeof (ha1));
1027  /* This will initialize da->sessionkey (respexp) */
1028  digest_calc_response (ha1,
1029  nonce,
1030  nc,
1031  cnonce,
1032  qop,
1033  connection->method,
1034  uri,
1035  hentity,
1036  da);
1037 
1038 
1039  /* Need to unescape URI before comparing with connection->url */
1040  daemon->unescape_callback (daemon->unescape_callback_cls,
1041  connection,
1042  uri);
1043  if (0 != strncmp (uri,
1044  connection->url,
1045  strlen (connection->url)))
1046  {
1047 #ifdef HAVE_MESSAGES
1048  MHD_DLOG (daemon,
1049  _("Authentication failed, URI does not match.\n"));
1050 #endif
1051  free (uri);
1052  return MHD_NO;
1053  }
1054 
1055  {
1056  const char *args = strchr (uri,
1057  '?');
1058 
1059  if (NULL == args)
1060  args = "";
1061  else
1062  args++;
1063  if (MHD_YES !=
1064  check_argument_match (connection,
1065  args) )
1066  {
1067 #ifdef HAVE_MESSAGES
1068  MHD_DLOG (daemon,
1069  _("Authentication failed, arguments do not match.\n"));
1070 #endif
1071  free (uri);
1072  return MHD_NO;
1073  }
1074  }
1075  free (uri);
1076  return (0 == strcmp (response,
1077  da->sessionkey))
1078  ? MHD_YES
1079  : MHD_NO;
1080  }
1081 }
1082 
1083 
1101 _MHD_EXTERN int
1103  const char *realm,
1104  const char *username,
1105  const char *password,
1106  unsigned int nonce_timeout)
1107 {
1108  return MHD_digest_auth_check2 (connection,
1109  realm,
1110  username,
1111  password,
1112  nonce_timeout,
1114 }
1115 
1116 
1125 #define SETUP_DA(algo,da) \
1126  union { \
1127  struct MD5Context md5; \
1128  struct sha256_ctx sha256; \
1129  } ctx; \
1130  union { \
1131  char md5[MD5_DIGEST_SIZE * 2 + 1]; \
1132  char sha256[SHA256_DIGEST_SIZE * 2 + 1]; \
1133  } skey; \
1134  struct DigestAlgorithm da; \
1135  \
1136  switch (algo) { \
1137  case MHD_DIGEST_ALG_MD5: \
1138  da.digest_size = MD5_DIGEST_SIZE; \
1139  da.ctx = &ctx.md5; \
1140  da.alg = "md5"; \
1141  da.sessionkey = skey.md5; \
1142  da.init = &MD5Init; \
1143  da.update = &MD5Update; \
1144  da.digest = &MD5Final; \
1145  break; \
1146  case MHD_DIGEST_ALG_AUTO: \
1147  /* auto == SHA256, fall-though thus intentional! */ \
1148  case MHD_DIGEST_ALG_SHA256: \
1149  da.digest_size = SHA256_DIGEST_SIZE; \
1150  da.ctx = &ctx.sha256; \
1151  da.alg = "sha-256"; \
1152  da.sessionkey = skey.sha256; \
1153  da.init = &sha256_init; \
1154  da.update = &sha256_update; \
1155  da.digest = &sha256_digest; \
1156  break; \
1157  }
1158 
1159 
1160 
1175 _MHD_EXTERN int
1177  const char *realm,
1178  const char *username,
1179  const char *password,
1180  unsigned int nonce_timeout,
1181  enum MHD_DigestAuthAlgorithm algo)
1182 {
1183  SETUP_DA (algo, da);
1184 
1185  return digest_auth_check_all (connection,
1186  &da,
1187  realm,
1188  username,
1189  password,
1190  NULL,
1191  nonce_timeout);
1192 }
1193 
1194 
1212 _MHD_EXTERN int
1214  const char *realm,
1215  const char *username,
1216  const uint8_t *digest,
1217  size_t digest_size,
1218  unsigned int nonce_timeout,
1219  enum MHD_DigestAuthAlgorithm algo)
1220 {
1221  SETUP_DA (algo, da);
1222 
1223  if (da.digest_size != digest_size)
1224  MHD_PANIC (_("digest size missmatch")); /* API violation! */
1225  return digest_auth_check_all (connection,
1226  &da,
1227  realm,
1228  username,
1229  NULL,
1230  digest,
1231  nonce_timeout);
1232 }
1233 
1234 
1252 _MHD_EXTERN int
1254  const char *realm,
1255  const char *username,
1256  const uint8_t digest[MHD_MD5_DIGEST_SIZE],
1257  unsigned int nonce_timeout)
1258 {
1259  return MHD_digest_auth_check_digest2 (connection,
1260  realm,
1261  username,
1262  digest,
1263  MHD_MD5_DIGEST_SIZE,
1264  nonce_timeout,
1266 }
1267 
1268 
1284 int
1286  const char *realm,
1287  const char *opaque,
1288  struct MHD_Response *response,
1289  int signal_stale,
1290  enum MHD_DigestAuthAlgorithm algo)
1291 {
1292  int ret;
1293  int hlen;
1294  SETUP_DA (algo, da);
1295 
1296  {
1297  char nonce[NONCE_STD_LEN(da.digest_size) + 1];
1298  /* Generating the server nonce */
1300  connection->method,
1301  connection->daemon->digest_auth_random,
1302  connection->daemon->digest_auth_rand_size,
1303  connection->url,
1304  realm,
1305  &da,
1306  nonce);
1307  if (MHD_YES !=
1308  check_nonce_nc (connection,
1309  nonce,
1310  0))
1311  {
1312 #ifdef HAVE_MESSAGES
1313  MHD_DLOG (connection->daemon,
1314  _("Could not register nonce (is the nonce array size zero?).\n"));
1315 #endif
1316  return MHD_NO;
1317  }
1318  /* Building the authentication header */
1319  hlen = MHD_snprintf_ (NULL,
1320  0,
1321  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1322  realm,
1323  nonce,
1324  opaque,
1325  da.alg,
1326  signal_stale
1327  ? ",stale=\"true\""
1328  : "");
1329  if (hlen > 0)
1330  {
1331  char *header;
1332 
1333  header = MHD_calloc_ (1,
1334  hlen + 1);
1335  if (NULL == header)
1336  {
1337 #ifdef HAVE_MESSAGES
1338  MHD_DLOG(connection->daemon,
1339  _("Failed to allocate memory for auth response header\n"));
1340 #endif /* HAVE_MESSAGES */
1341  return MHD_NO;
1342  }
1343 
1344  if (MHD_snprintf_ (header,
1345  hlen + 1,
1346  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s",
1347  realm,
1348  nonce,
1349  opaque,
1350  da.alg,
1351  signal_stale
1352  ? ",stale=\"true\""
1353  : "") == hlen)
1354  ret = MHD_add_response_header(response,
1356  header);
1357  else
1358  ret = MHD_NO;
1359  free (header);
1360  }
1361  else
1362  ret = MHD_NO;
1363  }
1364 
1365  if (MHD_YES == ret)
1366  {
1367  ret = MHD_queue_response (connection,
1369  response);
1370  }
1371  else
1372  {
1373 #ifdef HAVE_MESSAGES
1374  MHD_DLOG (connection->daemon,
1375  _("Failed to add Digest auth header\n"));
1376 #endif /* HAVE_MESSAGES */
1377  }
1378  return ret;
1379 }
1380 
1381 
1398 int
1400  const char *realm,
1401  const char *opaque,
1402  struct MHD_Response *response,
1403  int signal_stale)
1404 {
1405  return MHD_queue_auth_fail_response2 (connection,
1406  realm,
1407  opaque,
1408  response,
1409  signal_stale,
1411 }
1412 
1413 
1414 /* end of digestauth.c */
void * unescape_callback_cls
Definition: internal.h:1395
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:805
#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:73
_MHD_EXTERN const char * MHD_lookup_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key)
Definition: connection.c:797
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:143
MHD_mutex_lock_chk_ & daemon
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:631
#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:394
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
bool ret
_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:1102
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:1285
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:177
#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:1253
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:236
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition: microhttpd.h:440
const char * url
Definition: internal.h:702
_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:1399
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:1176
internal shared structures
char * method
Definition: internal.h:696
static int test_header(struct MHD_Connection *connection, const char *key, const char *value, enum MHD_ValueKind kind)
Definition: digestauth.c:703
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:590
_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:1213
#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:742
#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:1390
_MHD_EXTERN int MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
Definition: connection.c:4028
#define MHD_HTTP_UNAUTHORIZED
Definition: microhttpd.h:336
#define _BASE
Definition: digestauth.c:58
#define SETUP_DA(algo, da)
Definition: digestauth.c:1125
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:289
static int check_nonce_nc(struct MHD_Connection *connection, const char *nonce, uint64_t nc)
Definition: digestauth.c:487
#define NONCE_STD_LEN(digest_size)
Definition: digestauth.c:52
#define MAX_USERNAME_LENGTH
Definition: digestauth.c:63
#define _(String)
Definition: mhd_options.h:42
#define MAX_REALM_LENGTH
Definition: digestauth.c:68
#define MHD_NO
Definition: microhttpd.h:139
struct MHD_HTTP_Header * headers_received
Definition: internal.h:654
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