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