GNU libmicrohttpd  0.9.29
 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  (C) 2010, 2011, 2012 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 */
25 #include "platform.h"
26 #include <limits.h>
27 #include "internal.h"
28 #include "md5.h"
29 
30 #if defined(_WIN32) && defined(MHD_W32_MUTEX_)
31 #ifndef WIN32_LEAN_AND_MEAN
32 #define WIN32_LEAN_AND_MEAN 1
33 #endif /* !WIN32_LEAN_AND_MEAN */
34 #include <windows.h>
35 #endif /* _WIN32 && MHD_W32_MUTEX_ */
36 
37 #define HASH_MD5_HEX_LEN (2 * MD5_DIGEST_SIZE)
38 
42 #define _BASE "Digest "
43 
47 #define MAX_USERNAME_LENGTH 128
48 
52 #define MAX_REALM_LENGTH 256
53 
57 #define MAX_AUTH_RESPONSE_LENGTH 128
58 
59 
67 static void
68 cvthex (const unsigned char *bin,
69  size_t len,
70  char *hex)
71 {
72  size_t i;
73  unsigned int j;
74 
75  for (i = 0; i < len; ++i)
76  {
77  j = (bin[i] >> 4) & 0x0f;
78  hex[i * 2] = j <= 9 ? (j + '0') : (j + 'a' - 10);
79  j = bin[i] & 0x0f;
80  hex[i * 2 + 1] = j <= 9 ? (j + '0') : (j + 'a' - 10);
81  }
82  hex[len * 2] = '\0';
83 }
84 
85 
98 static void
99 digest_calc_ha1 (const char *alg,
100  const char *username,
101  const char *realm,
102  const char *password,
103  const char *nonce,
104  const char *cnonce,
105  char *sessionkey)
106 {
107  struct MD5Context md5;
108  unsigned char ha1[MD5_DIGEST_SIZE];
109 
110  MD5Init (&md5);
111  MD5Update (&md5, username, strlen (username));
112  MD5Update (&md5, ":", 1);
113  MD5Update (&md5, realm, strlen (realm));
114  MD5Update (&md5, ":", 1);
115  MD5Update (&md5, password, strlen (password));
116  MD5Final (ha1, &md5);
117  if (MHD_str_equal_caseless_(alg, "md5-sess"))
118  {
119  MD5Init (&md5);
120  MD5Update (&md5, ha1, sizeof (ha1));
121  MD5Update (&md5, ":", 1);
122  MD5Update (&md5, nonce, strlen (nonce));
123  MD5Update (&md5, ":", 1);
124  MD5Update (&md5, cnonce, strlen (cnonce));
125  MD5Final (ha1, &md5);
126  }
127  cvthex (ha1, sizeof (ha1), sessionkey);
128 }
129 
130 
144 static void
145 digest_calc_response (const char *ha1,
146  const char *nonce,
147  const char *noncecount,
148  const char *cnonce,
149  const char *qop,
150  const char *method,
151  const char *uri,
152  const char *hentity,
153  char *response)
154 {
155  struct MD5Context md5;
156  unsigned char ha2[MD5_DIGEST_SIZE];
157  unsigned char resphash[MD5_DIGEST_SIZE];
158  char ha2hex[HASH_MD5_HEX_LEN + 1];
159 
160  MD5Init (&md5);
161  MD5Update (&md5, method, strlen(method));
162  MD5Update (&md5, ":", 1);
163  MD5Update (&md5, uri, strlen(uri));
164 #if 0
165  if (0 == strcasecmp(qop, "auth-int"))
166  {
167  /* This is dead code since the rest of this module does
168  not support auth-int. */
169  MD5Update (&md5, ":", 1);
170  if (NULL != hentity)
171  MD5Update (&md5, hentity, strlen(hentity));
172  }
173 #endif
174  MD5Final (ha2, &md5);
175  cvthex (ha2, MD5_DIGEST_SIZE, ha2hex);
176  MD5Init (&md5);
177  /* calculate response */
178  MD5Update (&md5, ha1, HASH_MD5_HEX_LEN);
179  MD5Update (&md5, ":", 1);
180  MD5Update (&md5, nonce, strlen(nonce));
181  MD5Update (&md5, ":", 1);
182  if ('\0' != *qop)
183  {
184  MD5Update (&md5, noncecount, strlen(noncecount));
185  MD5Update (&md5, ":", 1);
186  MD5Update (&md5, cnonce, strlen(cnonce));
187  MD5Update (&md5, ":", 1);
188  MD5Update (&md5, qop, strlen(qop));
189  MD5Update (&md5, ":", 1);
190  }
191  MD5Update (&md5, ha2hex, HASH_MD5_HEX_LEN);
192  MD5Final (resphash, &md5);
193  cvthex (resphash, sizeof (resphash), response);
194 }
195 
196 
211 static size_t
212 lookup_sub_value (char *dest,
213  size_t size,
214  const char *data,
215  const char *key)
216 {
217  size_t keylen;
218  size_t len;
219  const char *ptr;
220  const char *eq;
221  const char *q1;
222  const char *q2;
223  const char *qn;
224 
225  if (0 == size)
226  return 0;
227  keylen = strlen (key);
228  ptr = data;
229  while ('\0' != *ptr)
230  {
231  if (NULL == (eq = strchr (ptr, '=')))
232  return 0;
233  q1 = eq + 1;
234  while (' ' == *q1)
235  q1++;
236  if ('\"' != *q1)
237  {
238  q2 = strchr (q1, ',');
239  qn = q2;
240  }
241  else
242  {
243  q1++;
244  q2 = strchr (q1, '\"');
245  if (NULL == q2)
246  return 0; /* end quote not found */
247  qn = q2 + 1;
248  }
249  if ((MHD_str_equal_caseless_n_(ptr,
250  key,
251  keylen)) &&
252  (eq == &ptr[keylen]) )
253  {
254  if (NULL == q2)
255  {
256  len = strlen (q1) + 1;
257  if (size > len)
258  size = len;
259  size--;
260  strncpy (dest,
261  q1,
262  size);
263  dest[size] = '\0';
264  return size;
265  }
266  else
267  {
268  if (size > (size_t) ((q2 - q1) + 1))
269  size = (q2 - q1) + 1;
270  size--;
271  memcpy (dest,
272  q1,
273  size);
274  dest[size] = '\0';
275  return size;
276  }
277  }
278  if (NULL == qn)
279  return 0;
280  ptr = strchr (qn, ',');
281  if (NULL == ptr)
282  return 0;
283  ptr++;
284  while (' ' == *ptr)
285  ptr++;
286  }
287  return 0;
288 }
289 
290 
300 static int
301 check_nonce_nc (struct MHD_Connection *connection,
302  const char *nonce,
303  unsigned long int nc)
304 {
305  uint32_t off;
306  uint32_t mod;
307  const char *np;
308 
309  mod = connection->daemon->nonce_nc_size;
310  if (0 == mod)
311  return MHD_NO; /* no array! */
312  /* super-fast xor-based "hash" function for HT lookup in nonce array */
313  off = 0;
314  np = nonce;
315  while ('\0' != *np)
316  {
317  off = (off << 8) | (*np ^ (off >> 24));
318  np++;
319  }
320  off = off % mod;
321  /*
322  * Look for the nonce, if it does exist and its corresponding
323  * nonce counter is less than the current nonce counter by 1,
324  * then only increase the nonce counter by one.
325  */
326 
327  (void) MHD_mutex_lock_ (&connection->daemon->nnc_lock);
328  if (0 == nc)
329  {
330  strcpy(connection->daemon->nnc[off].nonce,
331  nonce);
332  connection->daemon->nnc[off].nc = 0;
333  (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock);
334  return MHD_YES;
335  }
336  if ( (nc <= connection->daemon->nnc[off].nc) ||
337  (0 != strcmp(connection->daemon->nnc[off].nonce, nonce)) )
338  {
339  (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock);
340 #if HAVE_MESSAGES
341  MHD_DLOG (connection->daemon,
342  "Stale nonce received. If this happens a lot, you should probably increase the size of the nonce array.\n");
343 #endif
344  return MHD_NO;
345  }
346  connection->daemon->nnc[off].nc = nc;
347  (void) MHD_mutex_unlock_ (&connection->daemon->nnc_lock);
348  return MHD_YES;
349 }
350 
351 
360 char *
362 {
363  size_t len;
364  char user[MAX_USERNAME_LENGTH];
365  const char *header;
366 
367  if (NULL == (header = MHD_lookup_connection_value (connection,
370  return NULL;
371  if (0 != strncmp (header, _BASE, strlen (_BASE)))
372  return NULL;
373  header += strlen (_BASE);
374  if (0 == (len = lookup_sub_value (user,
375  sizeof (user),
376  header,
377  "username")))
378  return NULL;
379  return strdup (user);
380 }
381 
382 
396 static void
397 calculate_nonce (uint32_t nonce_time,
398  const char *method,
399  const char *rnd,
400  size_t rnd_size,
401  const char *uri,
402  const char *realm,
403  char *nonce)
404 {
405  struct MD5Context md5;
406  unsigned char timestamp[4];
407  unsigned char tmpnonce[MD5_DIGEST_SIZE];
408  char timestamphex[sizeof(timestamp) * 2 + 1];
409 
410  MD5Init (&md5);
411  timestamp[0] = (nonce_time & 0xff000000) >> 0x18;
412  timestamp[1] = (nonce_time & 0x00ff0000) >> 0x10;
413  timestamp[2] = (nonce_time & 0x0000ff00) >> 0x08;
414  timestamp[3] = (nonce_time & 0x000000ff);
415  MD5Update (&md5, timestamp, 4);
416  MD5Update (&md5, ":", 1);
417  MD5Update (&md5, method, strlen (method));
418  MD5Update (&md5, ":", 1);
419  if (rnd_size > 0)
420  MD5Update (&md5, rnd, rnd_size);
421  MD5Update (&md5, ":", 1);
422  MD5Update (&md5, uri, strlen (uri));
423  MD5Update (&md5, ":", 1);
424  MD5Update (&md5, realm, strlen (realm));
425  MD5Final (tmpnonce, &md5);
426  cvthex (tmpnonce, sizeof (tmpnonce), nonce);
427  cvthex (timestamp, 4, timestamphex);
428  strncat (nonce, timestamphex, 8);
429 }
430 
431 
442 static int
443 test_header (struct MHD_Connection *connection,
444  const char *key,
445  const char *value)
446 {
447  struct MHD_HTTP_Header *pos;
448 
449  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
450  {
451  if (MHD_GET_ARGUMENT_KIND != pos->kind)
452  continue;
453  if (0 != strcmp (key, pos->header))
454  continue;
455  if ( (NULL == value) &&
456  (NULL == pos->value) )
457  return MHD_YES;
458  if ( (NULL == value) ||
459  (NULL == pos->value) ||
460  (0 != strcmp (value, pos->value)) )
461  continue;
462  return MHD_YES;
463  }
464  return MHD_NO;
465 }
466 
467 
478 static int
480  const char *args)
481 {
482  struct MHD_HTTP_Header *pos;
483  char *argb;
484  char *argp;
485  char *equals;
486  char *amper;
487  unsigned int num_headers;
488 
489  argb = strdup(args);
490  if (NULL == argb)
491  {
492 #if HAVE_MESSAGES
493  MHD_DLOG(connection->daemon,
494  "Failed to allocate memory for copy of URI arguments\n");
495 #endif /* HAVE_MESSAGES */
496  return MHD_NO;
497  }
498  num_headers = 0;
499  argp = argb;
500  while ( (NULL != argp) &&
501  ('\0' != argp[0]) )
502  {
503  equals = strchr (argp, '=');
504  if (NULL == equals)
505  {
506  /* add with 'value' NULL */
507  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
508  connection,
509  argp);
510  if (MHD_YES != test_header (connection, argp, NULL))
511  return MHD_NO;
512  num_headers++;
513  break;
514  }
515  equals[0] = '\0';
516  equals++;
517  amper = strchr (equals, '&');
518  if (NULL != amper)
519  {
520  amper[0] = '\0';
521  amper++;
522  }
523  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
524  connection,
525  argp);
526  connection->daemon->unescape_callback (connection->daemon->unescape_callback_cls,
527  connection,
528  equals);
529  if (! test_header (connection, argp, equals))
530  return MHD_NO;
531  num_headers++;
532  argp = amper;
533  }
534 
535  /* also check that the number of headers matches */
536  for (pos = connection->headers_received; NULL != pos; pos = pos->next)
537  {
538  if (MHD_GET_ARGUMENT_KIND != pos->kind)
539  continue;
540  num_headers--;
541  }
542  if (0 != num_headers)
543  return MHD_NO;
544  return MHD_YES;
545 }
546 
547 
561 int
563  const char *realm,
564  const char *username,
565  const char *password,
566  unsigned int nonce_timeout)
567 {
568  size_t len;
569  const char *header;
570  char *end;
571  char nonce[MAX_NONCE_LENGTH];
572  char cnonce[MAX_NONCE_LENGTH];
573  char qop[15]; /* auth,auth-int */
574  char nc[20];
575  char response[MAX_AUTH_RESPONSE_LENGTH];
576  const char *hentity = NULL; /* "auth-int" is not supported */
577  char ha1[HASH_MD5_HEX_LEN + 1];
578  char respexp[HASH_MD5_HEX_LEN + 1];
579  char noncehashexp[HASH_MD5_HEX_LEN + 9];
580  uint32_t nonce_time;
581  uint32_t t;
582  size_t left; /* number of characters left in 'header' for 'uri' */
583  unsigned long int nci;
584 
585  header = MHD_lookup_connection_value (connection,
588  if (NULL == header)
589  return MHD_NO;
590  if (0 != strncmp(header, _BASE, strlen(_BASE)))
591  return MHD_NO;
592  header += strlen (_BASE);
593  left = strlen (header);
594 
595  {
596  char un[MAX_USERNAME_LENGTH];
597 
598  len = lookup_sub_value (un,
599  sizeof (un),
600  header, "username");
601  if ( (0 == len) ||
602  (0 != strcmp(username, un)) )
603  return MHD_NO;
604  left -= strlen ("username") + len;
605  }
606 
607  {
608  char r[MAX_REALM_LENGTH];
609 
610  len = lookup_sub_value(r,
611  sizeof (r),
612  header, "realm");
613  if ( (0 == len) ||
614  (0 != strcmp(realm, r)) )
615  return MHD_NO;
616  left -= strlen ("realm") + len;
617  }
618 
619  if (0 == (len = lookup_sub_value (nonce,
620  sizeof (nonce),
621  header, "nonce")))
622  return MHD_NO;
623  left -= strlen ("nonce") + len;
624  if (left > 32 * 1024)
625  {
626  /* we do not permit URIs longer than 32k, as we want to
627  make sure to not blow our stack (or per-connection
628  heap memory limit). Besides, 32k is already insanely
629  large, but of course in theory the
630  #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
631  and would thus permit sending a >32k authorization
632  header value. */
633  return MHD_NO;
634  }
635  {
636  char *uri;
637 
638  uri = malloc(left + 1);
639  if (NULL == uri)
640  {
641 #if HAVE_MESSAGES
642  MHD_DLOG(connection->daemon,
643  "Failed to allocate memory for auth header processing\n");
644 #endif /* HAVE_MESSAGES */
645  return MHD_NO;
646  }
647  if (0 == lookup_sub_value (uri,
648  left + 1,
649  header, "uri"))
650  {
651  free(uri);
652  return MHD_NO;
653  }
654 
655  /* 8 = 4 hexadecimal numbers for the timestamp */
656  nonce_time = strtoul (nonce + len - 8, (char **)NULL, 16);
657  t = (uint32_t) MHD_monotonic_time();
658  /*
659  * First level vetting for the nonce validity: if the timestamp
660  * attached to the nonce exceeds `nonce_timeout', then the nonce is
661  * invalid.
662  */
663  if ( (t > nonce_time + nonce_timeout) ||
664  (nonce_time + nonce_timeout < nonce_time) )
665  {
666  free(uri);
667  return MHD_INVALID_NONCE;
668  }
669  if (0 != strncmp (uri,
670  connection->url,
671  strlen (connection->url)))
672  {
673 #if HAVE_MESSAGES
674  MHD_DLOG (connection->daemon,
675  "Authentication failed, URI does not match.\n");
676 #endif
677  free(uri);
678  return MHD_NO;
679  }
680  {
681  const char *args = strchr (uri, '?');
682 
683  if (NULL == args)
684  args = "";
685  else
686  args++;
687  if (MHD_YES !=
688  check_argument_match (connection,
689  args) )
690  {
691 #if HAVE_MESSAGES
692  MHD_DLOG (connection->daemon,
693  "Authentication failed, arguments do not match.\n");
694 #endif
695  free(uri);
696  return MHD_NO;
697  }
698  }
699  calculate_nonce (nonce_time,
700  connection->method,
701  connection->daemon->digest_auth_random,
702  connection->daemon->digest_auth_rand_size,
703  connection->url,
704  realm,
705  noncehashexp);
706  /*
707  * Second level vetting for the nonce validity
708  * if the timestamp attached to the nonce is valid
709  * and possibly fabricated (in case of an attack)
710  * the attacker must also know the random seed to be
711  * able to generate a "sane" nonce, which if he does
712  * not, the nonce fabrication process going to be
713  * very hard to achieve.
714  */
715 
716  if (0 != strcmp (nonce, noncehashexp))
717  {
718  free(uri);
719  return MHD_INVALID_NONCE;
720  }
721  if ( (0 == lookup_sub_value (cnonce,
722  sizeof (cnonce),
723  header, "cnonce")) ||
724  (0 == lookup_sub_value (qop, sizeof (qop), header, "qop")) ||
725  ( (0 != strcmp (qop, "auth")) &&
726  (0 != strcmp (qop, "")) ) ||
727  (0 == lookup_sub_value (nc, sizeof (nc), header, "nc")) ||
728  (0 == lookup_sub_value (response, sizeof (response), header, "response")) )
729  {
730 #if HAVE_MESSAGES
731  MHD_DLOG (connection->daemon,
732  "Authentication failed, invalid format.\n");
733 #endif
734  free(uri);
735  return MHD_NO;
736  }
737  nci = strtoul (nc, &end, 16);
738  if ( ('\0' != *end) ||
739  ( (LONG_MAX == nci) &&
740  (ERANGE == errno) ) )
741  {
742 #if HAVE_MESSAGES
743  MHD_DLOG (connection->daemon,
744  "Authentication failed, invalid format.\n");
745 #endif
746  free(uri);
747  return MHD_NO; /* invalid nonce format */
748  }
749  /*
750  * Checking if that combination of nonce and nc is sound
751  * and not a replay attack attempt. Also adds the nonce
752  * to the nonce-nc map if it does not exist there.
753  */
754 
755  if (MHD_YES != check_nonce_nc (connection, nonce, nci))
756  {
757  free(uri);
758  return MHD_NO;
759  }
760 
761  digest_calc_ha1("md5",
762  username,
763  realm,
764  password,
765  nonce,
766  cnonce,
767  ha1);
769  nonce,
770  nc,
771  cnonce,
772  qop,
773  connection->method,
774  uri,
775  hentity,
776  respexp);
777  free(uri);
778  return (0 == strcmp(response, respexp))
779  ? MHD_YES
780  : MHD_NO;
781  }
782 }
783 
784 
799 int
801  const char *realm,
802  const char *opaque,
803  struct MHD_Response *response,
804  int signal_stale)
805 {
806  int ret;
807  size_t hlen;
808  char nonce[HASH_MD5_HEX_LEN + 9];
809 
810  /* Generating the server nonce */
811  calculate_nonce ((uint32_t) MHD_monotonic_time(),
812  connection->method,
813  connection->daemon->digest_auth_random,
814  connection->daemon->digest_auth_rand_size,
815  connection->url,
816  realm,
817  nonce);
818  if (MHD_YES != check_nonce_nc (connection, nonce, 0))
819  {
820 #if HAVE_MESSAGES
821  MHD_DLOG (connection->daemon,
822  "Could not register nonce (is the nonce array size zero?).\n");
823 #endif
824  return MHD_NO;
825  }
826  /* Building the authentication header */
827  hlen = MHD_snprintf_(NULL,
828  0,
829  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
830  realm,
831  nonce,
832  opaque,
833  signal_stale
834  ? ",stale=\"true\""
835  : "");
836  {
837  char *header;
838 
839  header = malloc(hlen + 1);
840  if (NULL == header)
841  {
842 #if HAVE_MESSAGES
843  MHD_DLOG(connection->daemon,
844  "Failed to allocate memory for auth response header\n");
845 #endif /* HAVE_MESSAGES */
846  return MHD_NO;
847  }
848 
849  MHD_snprintf_(header,
850  hlen + 1,
851  "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\"%s",
852  realm,
853  nonce,
854  opaque,
855  signal_stale
856  ? ",stale=\"true\""
857  : "");
858  ret = MHD_add_response_header(response,
860  header);
861  free(header);
862  }
863  if (MHD_YES == ret)
864  ret = MHD_queue_response(connection,
866  response);
867  return ret;
868 }
869 
870 
871 /* end of digestauth.c */
void * unescape_callback_cls
Definition: internal.h:1024
#define MAX_AUTH_RESPONSE_LENGTH
Definition: digestauth.c:57
_MHD_EXTERN const char * MHD_lookup_connection_value(struct MHD_Connection *connection, enum MHD_ValueKind kind, const char *key)
Definition: connection.c:217
_MHD_EXTERN int MHD_add_response_header(struct MHD_Response *response, const char *header, const char *content)
Definition: response.c:100
static void cvthex(const unsigned char *bin, size_t len, char *hex)
Definition: digestauth.c:68
static int check_nonce_nc(struct MHD_Connection *connection, const char *nonce, unsigned long int nc)
Definition: digestauth.c:301
static size_t lookup_sub_value(char *dest, size_t size, const char *data, const char *key)
Definition: digestauth.c:212
#define MHD_YES
Definition: microhttpd.h:138
#define MHD_INVALID_NONCE
Definition: microhttpd.h:2264
enum MHD_ValueKind kind
Definition: internal.h:236
_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:562
platform-specific includes for libmicrohttpd
static int test_header(struct MHD_Connection *connection, const char *key, const char *value)
Definition: digestauth.c:443
char * value
Definition: internal.h:230
struct MHD_Daemon * daemon
Definition: internal.h:551
#define MHD_str_equal_caseless_(a, b)
#define HASH_MD5_HEX_LEN
Definition: digestauth.c:37
static void calculate_nonce(uint32_t nonce_time, const char *method, const char *rnd, size_t rnd_size, const char *uri, const char *realm, char *nonce)
Definition: digestauth.c:397
return NULL
Definition: tsearch.c:32
static void digest_calc_ha1(const char *alg, const char *username, const char *realm, const char *password, const char *nonce, const char *cnonce, char *sessionkey)
Definition: digestauth.c:99
#define MHD_HTTP_HEADER_AUTHORIZATION
Definition: microhttpd.h:312
_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:800
internal shared structures
char * method
Definition: internal.h:592
time_t MHD_monotonic_time(void)
Definition: internal.c:182
#define MHD_HTTP_HEADER_WWW_AUTHENTICATE
Definition: microhttpd.h:355
_MHD_EXTERN char * MHD_digest_auth_get_username(struct MHD_Connection *connection)
Definition: digestauth.c:361
#define MAX_NONCE_LENGTH
Definition: internal.h:177
static int check_argument_match(struct MHD_Connection *connection, const char *args)
Definition: digestauth.c:479
#define MHD_snprintf_
char * url
Definition: internal.h:598
UnescapeCallback unescape_callback
Definition: internal.h:1019
_MHD_EXTERN int MHD_queue_response(struct MHD_Connection *connection, unsigned int status_code, struct MHD_Response *response)
Definition: connection.c:2907
#define MHD_HTTP_UNAUTHORIZED
Definition: microhttpd.h:249
#define _BASE
Definition: digestauth.c:42
char * header
Definition: internal.h:225
void MD5Update(struct MD5Context *ctx, const void *data, unsigned len)
Definition: md5.c:170
void MD5Final(unsigned char digest[16], struct MD5Context *ctx)
Definition: md5.c:222
void MD5Init(struct MD5Context *ctx)
Definition: md5.c: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, char *response)
Definition: digestauth.c:145
Definition: md5.h:32
struct MHD_HTTP_Header * next
Definition: internal.h:219
#define MD5_DIGEST_SIZE
Definition: md5.h:30
#define MAX_USERNAME_LENGTH
Definition: digestauth.c:47
#define MAX_REALM_LENGTH
Definition: digestauth.c:52
#define MHD_str_equal_caseless_n_(a, b, n)
#define MHD_NO
Definition: microhttpd.h:143
struct MHD_HTTP_Header * headers_received
Definition: internal.h:556