1/*
2     This file is part of libmicrohttpd
3     Copyright (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*/
19/**
20 * @file digestauth.c
21 * @brief Implements HTTP digest authentication
22 * @author Amr Ali
23 * @author Matthieu Speder
24 */
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
39/**
40 * Beginning string for any valid Digest authentication header.
41 */
42#define _BASE		"Digest "
43
44/**
45 * Maximum length of a username for digest authentication.
46 */
47#define MAX_USERNAME_LENGTH 128
48
49/**
50 * Maximum length of a realm for digest authentication.
51 */
52#define MAX_REALM_LENGTH 256
53
54/**
55 * Maximum length of the response in digest authentication.
56 */
57#define MAX_AUTH_RESPONSE_LENGTH 128
58
59
60/**
61 * convert bin to hex
62 *
63 * @param bin binary data
64 * @param len number of bytes in bin
65 * @param hex pointer to len*2+1 bytes
66 */
67static void
68cvthex (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
86/**
87 * calculate H(A1) as per RFC2617 spec and store the
88 * result in 'sessionkey'.
89 *
90 * @param alg The hash algorithm used, can be "md5" or "md5-sess"
91 * @param username A `char *' pointer to the username value
92 * @param realm A `char *' pointer to the realm value
93 * @param password A `char *' pointer to the password value
94 * @param nonce A `char *' pointer to the nonce value
95 * @param cnonce A `char *' pointer to the cnonce value
96 * @param sessionkey pointer to buffer of HASH_MD5_HEX_LEN+1 bytes
97 */
98static void
99digest_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
131/**
132 * Calculate request-digest/response-digest as per RFC2617 spec
133 *
134 * @param ha1 H(A1)
135 * @param nonce nonce from server
136 * @param noncecount 8 hex digits
137 * @param cnonce client nonce
138 * @param qop qop-value: "", "auth" or "auth-int"
139 * @param method method from request
140 * @param uri requested URL
141 * @param hentity H(entity body) if qop="auth-int"
142 * @param response request-digest or response-digest
143 */
144static void
145digest_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
197/**
198 * Lookup subvalue off of the HTTP Authorization header.
199 *
200 * A description of the input format for 'data' is at
201 * http://en.wikipedia.org/wiki/Digest_access_authentication
202 *
203 *
204 * @param dest where to store the result (possibly truncated if
205 *             the buffer is not big enough).
206 * @param size size of dest
207 * @param data pointer to the Authorization header
208 * @param key key to look up in data
209 * @return size of the located value, 0 if otherwise
210 */
211static size_t
212lookup_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
291/**
292 * Check nonce-nc map array with either new nonce counter
293 * or a whole new nonce.
294 *
295 * @param connection The MHD connection structure
296 * @param nonce A pointer that referenced a zero-terminated array of nonce
297 * @param nc The nonce counter, zero to add the nonce to the array
298 * @return MHD_YES if successful, MHD_NO if invalid (or we have no NC array)
299 */
300static int
301check_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
352/**
353 * Get the username from the authorization header sent by the client
354 *
355 * @param connection The MHD connection structure
356 * @return NULL if no username could be found, a pointer
357 * 			to the username if found
358 * @ingroup authentication
359 */
360char *
361MHD_digest_auth_get_username(struct MHD_Connection *connection)
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,
368						     MHD_HEADER_KIND,
369						     MHD_HTTP_HEADER_AUTHORIZATION)))
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
383/**
384 * Calculate the server nonce so that it mitigates replay attacks
385 * The current format of the nonce is ...
386 * H(timestamp ":" method ":" random ":" uri ":" realm) + Hex(timestamp)
387 *
388 * @param nonce_time The amount of time in seconds for a nonce to be invalid
389 * @param method HTTP method
390 * @param rnd A pointer to a character array for the random seed
391 * @param rnd_size The size of the random seed array @a rnd
392 * @param uri HTTP URI (in MHD, without the arguments ("?k=v")
393 * @param realm A string of characters that describes the realm of auth.
394 * @param nonce A pointer to a character array for the nonce to put in
395 */
396static void
397calculate_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
432/**
433 * Test if the given key-value pair is in the headers for the
434 * given connection.
435 *
436 * @param connection the connection
437 * @param key the key
438 * @param value the value, can be NULL
439 * @return #MHD_YES if the key-value pair is in the headers,
440 *         #MHD_NO if not
441 */
442static int
443test_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
468/**
469 * Check that the arguments given by the client as part
470 * of the authentication header match the arguments we
471 * got as part of the HTTP request URI.
472 *
473 * @param connection connections with headers to compare against
474 * @param args argument URI string (after "?" in URI)
475 * @return MHD_YES if the arguments match,
476 *         MHD_NO if not
477 */
478static int
479check_argument_match (struct MHD_Connection *connection,
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
548/**
549 * Authenticates the authorization header sent by the client
550 *
551 * @param connection The MHD connection structure
552 * @param realm The realm presented to the client
553 * @param username The username needs to be authenticated
554 * @param password The password used in the authentication
555 * @param nonce_timeout The amount of time for a nonce to be
556 * 			invalid in seconds
557 * @return #MHD_YES if authenticated, #MHD_NO if not,
558 * 			#MHD_INVALID_NONCE if nonce is invalid
559 * @ingroup authentication
560 */
561int
562MHD_digest_auth_check (struct MHD_Connection *connection,
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,
586					MHD_HEADER_KIND,
587					MHD_HTTP_HEADER_AUTHORIZATION);
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);
768    digest_calc_response (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
785/**
786 * Queues a response to request authentication from the client
787 *
788 * @param connection The MHD connection structure
789 * @param realm the realm presented to the client
790 * @param opaque string to user for opaque value
791 * @param response reply to send; should contain the "access denied"
792 *        body; note that this function will set the "WWW Authenticate"
793 *        header and that the caller should not do this
794 * @param signal_stale #MHD_YES if the nonce is invalid to add
795 * 			'stale=true' to the authentication header
796 * @return #MHD_YES on success, #MHD_NO otherwise
797 * @ingroup authentication
798 */
799int
800MHD_queue_auth_fail_response (struct MHD_Connection *connection,
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,
859				  MHD_HTTP_HEADER_WWW_AUTHENTICATE,
860				  header);
861    free(header);
862  }
863  if (MHD_YES == ret)
864    ret = MHD_queue_response(connection,
865			     MHD_HTTP_UNAUTHORIZED,
866			     response);
867  return ret;
868}
869
870
871/* end of digestauth.c */
872