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  */
67 static void
cvthex(const unsigned char * bin,size_t len,char * hex)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 
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  */
98 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)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 
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  */
144 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)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 
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  */
211 static size_t
lookup_sub_value(char * dest,size_t size,const char * data,const char * key)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 
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  */
300 static int
check_nonce_nc(struct MHD_Connection * connection,const char * nonce,unsigned long int nc)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 
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  */
360 char *
MHD_digest_auth_get_username(struct MHD_Connection * connection)361 MHD_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  */
396 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)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 
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  */
442 static int
test_header(struct MHD_Connection * connection,const char * key,const char * value)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 
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  */
478 static int
check_argument_match(struct MHD_Connection * connection,const char * args)479 check_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  */
561 int
MHD_digest_auth_check(struct MHD_Connection * connection,const char * realm,const char * username,const char * password,unsigned int nonce_timeout)562 MHD_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  */
799 int
MHD_queue_auth_fail_response(struct MHD_Connection * connection,const char * realm,const char * opaque,struct MHD_Response * response,int signal_stale)800 MHD_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