aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-05-30 21:54:09 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-05-31 11:45:36 +0300
commit9039d65241daf512e7756319cd64d3d54750cb22 (patch)
tree9c109e217ad1014a89de5495fcebdc1723672e31
parentc15077e312ffe934b242c7ecc6559c6eb4eb62cb (diff)
downloadlibmicrohttpd-9039d65241daf512e7756319cd64d3d54750cb22.tar.gz
libmicrohttpd-9039d65241daf512e7756319cd64d3d54750cb22.zip
authentication: reworked header parsing
Added single function to parse all enabled authentication schemes header strings. The parsing result is cached and reused thus avoiding repetitive header parsing. The new function correctly "unquotes" values (backslashes are removed) as required by RFC.
-rw-r--r--configure.ac2
-rw-r--r--src/include/microhttpd.h4
-rw-r--r--src/microhttpd/Makefile.am4
-rw-r--r--src/microhttpd/base64.c9
-rw-r--r--src/microhttpd/base64.h13
-rw-r--r--src/microhttpd/basicauth.c113
-rw-r--r--src/microhttpd/basicauth.h9
-rw-r--r--src/microhttpd/connection.c3
-rw-r--r--src/microhttpd/digestauth.c932
-rw-r--r--src/microhttpd/digestauth.h42
-rw-r--r--src/microhttpd/gen_auth.c511
-rw-r--r--src/microhttpd/gen_auth.h93
-rw-r--r--src/microhttpd/internal.h11
-rw-r--r--src/testcurl/test_digestauth_concurrent.c2
-rw-r--r--w32/common/libmicrohttpd-files.vcxproj2
-rw-r--r--w32/common/libmicrohttpd-filters.vcxproj6
16 files changed, 1344 insertions, 412 deletions
diff --git a/configure.ac b/configure.ac
index 170c9aaa..7cba50af 100644
--- a/configure.ac
+++ b/configure.ac
@@ -2916,6 +2916,8 @@ AS_IF([[test "x$enable_dauth" != "xno"]],
2916AM_CONDITIONAL([ENABLE_DAUTH], [test "x$enable_dauth" != "xno"]) 2916AM_CONDITIONAL([ENABLE_DAUTH], [test "x$enable_dauth" != "xno"])
2917AC_MSG_RESULT([[$enable_dauth]]) 2917AC_MSG_RESULT([[$enable_dauth]])
2918 2918
2919AM_CONDITIONAL([HAVE_ANYAUTH],[test "x$enable_bauth" != "xno" || test "x$enable_dauth" != "xno"])
2920
2919# optional: HTTP "Upgrade" support. Enabled by default 2921# optional: HTTP "Upgrade" support. Enabled by default
2920AC_MSG_CHECKING([[whether to support HTTP "Upgrade"]]) 2922AC_MSG_CHECKING([[whether to support HTTP "Upgrade"]])
2921AC_ARG_ENABLE([[httpupgrade]], 2923AC_ARG_ENABLE([[httpupgrade]],
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h
index 84561dec..92a9278f 100644
--- a/src/include/microhttpd.h
+++ b/src/include/microhttpd.h
@@ -96,7 +96,7 @@ extern "C"
96 * they are parsed as decimal numbers. 96 * they are parsed as decimal numbers.
97 * Example: 0x01093001 = 1.9.30-1. 97 * Example: 0x01093001 = 1.9.30-1.
98 */ 98 */
99#define MHD_VERSION 0x00097514 99#define MHD_VERSION 0x00097515
100 100
101/* If generic headers don't work on your platform, include headers 101/* If generic headers don't work on your platform, include headers
102 which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t', 102 which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t',
@@ -4368,6 +4368,8 @@ enum MHD_DigestAuthAlgorithm
4368/** 4368/**
4369 * The result of digest authentication of the client. 4369 * The result of digest authentication of the client.
4370 * 4370 *
4371 * All error values are zero or negative.
4372 *
4371 * @note Available since #MHD_VERSION 0x00097513 4373 * @note Available since #MHD_VERSION 0x00097513
4372 */ 4374 */
4373enum MHD_DigestAuthResult 4375enum MHD_DigestAuthResult
diff --git a/src/microhttpd/Makefile.am b/src/microhttpd/Makefile.am
index 20e29c66..cf7e7f76 100644
--- a/src/microhttpd/Makefile.am
+++ b/src/microhttpd/Makefile.am
@@ -161,6 +161,10 @@ libmicrohttpd_la_SOURCES += \
161 postprocessor.c 161 postprocessor.c
162endif 162endif
163 163
164if HAVE_ANYAUTH
165libmicrohttpd_la_SOURCES += \
166 gen_auth.c gen_auth.h
167endif
164if ENABLE_DAUTH 168if ENABLE_DAUTH
165libmicrohttpd_la_SOURCES += \ 169libmicrohttpd_la_SOURCES += \
166 digestauth.c digestauth.h \ 170 digestauth.c digestauth.h \
diff --git a/src/microhttpd/base64.c b/src/microhttpd/base64.c
index 1f07cd7e..3d2dd3f1 100644
--- a/src/microhttpd/base64.c
+++ b/src/microhttpd/base64.c
@@ -37,9 +37,10 @@ static const char base64_digits[] =
37 37
38 38
39char * 39char *
40BASE64Decode (const char *src) 40BASE64Decode (const char *src,
41 size_t in_len,
42 size_t *out_len)
41{ 43{
42 size_t in_len = strlen (src);
43 unsigned char *dest; 44 unsigned char *dest;
44 char *result; 45 char *result;
45 46
@@ -52,7 +53,7 @@ BASE64Decode (const char *src)
52 result = (char *) dest; 53 result = (char *) dest;
53 if (NULL == result) 54 if (NULL == result)
54 return NULL; /* out of memory */ 55 return NULL; /* out of memory */
55 while (*src) 56 for (; 0 < in_len && 0 != *src; in_len -= 4)
56 { 57 {
57 char a = base64_digits[(unsigned char) *(src++)]; 58 char a = base64_digits[(unsigned char) *(src++)];
58 char b = base64_digits[(unsigned char) *(src++)]; 59 char b = base64_digits[(unsigned char) *(src++)];
@@ -75,6 +76,8 @@ BASE64Decode (const char *src)
75 | ((unsigned char) d); 76 | ((unsigned char) d);
76 } 77 }
77 *dest = 0; 78 *dest = 0;
79 if (NULL != out_len)
80 *out_len = (size_t) (dest - (unsigned char *) result);
78 return result; 81 return result;
79} 82}
80 83
diff --git a/src/microhttpd/base64.h b/src/microhttpd/base64.h
index a489dc69..e5f11319 100644
--- a/src/microhttpd/base64.h
+++ b/src/microhttpd/base64.h
@@ -9,7 +9,18 @@
9#ifndef BASE64_H 9#ifndef BASE64_H
10#define BASE64_H 10#define BASE64_H
11 11
12#include "mhd_options.h"
13#ifdef HAVE_STDDEF_H
14#include <stddef.h>
15#elif defined(HAVE_STDLIB_H)
16#include <stdlib.h>
17#else
18#include <stdio.h>
19#endif
20
12char * 21char *
13BASE64Decode (const char *src); 22BASE64Decode (const char *src,
23 size_t in_len,
24 size_t *out_len);
14 25
15#endif /* !BASE64_H */ 26#endif /* !BASE64_H */
diff --git a/src/microhttpd/basicauth.c b/src/microhttpd/basicauth.c
index 9ef26dca..19315d14 100644
--- a/src/microhttpd/basicauth.c
+++ b/src/microhttpd/basicauth.c
@@ -25,6 +25,7 @@
25 * @author Karlson2k (Evgeny Grin) 25 * @author Karlson2k (Evgeny Grin)
26 */ 26 */
27#include "basicauth.h" 27#include "basicauth.h"
28#include "gen_auth.h"
28#include "platform.h" 29#include "platform.h"
29#include "mhd_limits.h" 30#include "mhd_limits.h"
30#include "internal.h" 31#include "internal.h"
@@ -33,76 +34,102 @@
33 34
34 35
35/** 36/**
37 * Get request's Basic Authorisation parameters.
38 * @param connection the connection to process
39 * @return pointer to request Basic Authorisation parameters structure if
40 * request has such header (allocated in connection's pool),
41 * NULL otherwise.
42 */
43static const struct MHD_RqBAuth *
44get_rq_bauth_params (struct MHD_Connection *connection)
45{
46 const struct MHD_AuthRqHeader *rq_params;
47
48 rq_params = MHD_get_auth_rq_params_ (connection);
49 if ( (NULL == rq_params) ||
50 (MHD_AUTHTYPE_BASIC != rq_params->auth_type) )
51 return NULL;
52
53 return rq_params->params.bauth;
54}
55
56
57/**
36 * Get the username and password from the basic authorization header sent by the client 58 * Get the username and password from the basic authorization header sent by the client
37 * 59 *
38 * @param connection The MHD connection structure 60 * @param connection The MHD connection structure
39 * @param password a pointer for the password 61 * @param[out] password a pointer for the password, free using #MHD_free().
40 * @return NULL if no username could be found, a pointer 62 * @return NULL if no username could be found, a pointer
41 * to the username if found 63 * to the username if found, free using #MHD_free().
42 * @ingroup authentication 64 * @ingroup authentication
43 */ 65 */
44_MHD_EXTERN char * 66_MHD_EXTERN char *
45MHD_basic_auth_get_username_password (struct MHD_Connection *connection, 67MHD_basic_auth_get_username_password (struct MHD_Connection *connection,
46 char **password) 68 char **password)
47{ 69{
48 const char *header; 70 const struct MHD_RqBAuth *params;
49 char *decode; 71 char *decode;
72 size_t decode_len;
50 const char *separator; 73 const char *separator;
51 char *user; 74
52 75 params = get_rq_bauth_params (connection);
53 if ( (MHD_NO == MHD_lookup_connection_value_n (connection, 76
54 MHD_HEADER_KIND, 77 if (NULL == params)
55 MHD_HTTP_HEADER_AUTHORIZATION, 78 return NULL;
56 MHD_STATICSTR_LEN_ ( 79
57 MHD_HTTP_HEADER_AUTHORIZATION), 80 if ((NULL == params->token68.str) || (0 == params->token68.len))
58 &header,
59 NULL)) ||
60 (0 != strncmp (header,
61 _MHD_AUTH_BASIC_BASE,
62 MHD_STATICSTR_LEN_ (_MHD_AUTH_BASIC_BASE))) )
63 return NULL; 81 return NULL;
64 header += MHD_STATICSTR_LEN_ (_MHD_AUTH_BASIC_BASE); 82
65 if (NULL == (decode = BASE64Decode (header))) 83 decode = BASE64Decode (params->token68.str, params->token68.len, &decode_len);
84 if ((NULL == decode) || (0 == decode_len))
66 { 85 {
67#ifdef HAVE_MESSAGES 86#ifdef HAVE_MESSAGES
68 MHD_DLOG (connection->daemon, 87 MHD_DLOG (connection->daemon,
69 _ ("Error decoding basic authentication.\n")); 88 _ ("Error decoding basic authentication.\n"));
70#endif 89#endif
90 if (NULL != decode)
91 free (decode);
71 return NULL; 92 return NULL;
72 } 93 }
73 /* Find user:password pattern */ 94 /* Find user:password pattern */
74 if (NULL == (separator = strchr (decode, 95 if (NULL != (separator = memchr (decode,
75 ':'))) 96 ':',
97 decode_len)))
76 { 98 {
77#ifdef HAVE_MESSAGES 99 char *user;
78 MHD_DLOG (connection->daemon, 100 size_t user_len;
79 _ ("Basic authentication doesn't contain ':' separator.\n")); 101 size_t password_len;
80#endif 102
81 free (decode); 103 user = decode; /* Reuse already allocated buffer */
82 return NULL; 104 user_len = (size_t) (separator - decode);
83 } 105 user[user_len] = 0;
84 if (NULL == (user = strdup (decode))) 106
85 { 107 if (NULL == password)
86 free (decode); 108 return user;
87 return NULL; 109
88 } 110 password_len = decode_len - user_len - 1;
89 user[separator - decode] = '\0'; /* cut off at ':' */ 111 *password = (char *) malloc (password_len + 1);
90 if (NULL != password) 112 if (NULL != *password)
91 {
92 *password = strdup (separator + 1);
93 if (NULL == *password)
94 { 113 {
114 if (0 != password_len)
115 memcpy (*password, decode + user_len + 1, password_len);
116 (*password)[password_len] = 0;
117
118 return user;
119 }
95#ifdef HAVE_MESSAGES 120#ifdef HAVE_MESSAGES
121 else
96 MHD_DLOG (connection->daemon, 122 MHD_DLOG (connection->daemon,
97 _ ("Failed to allocate memory for password.\n")); 123 _ ("Failed to allocate memory.\n"));
98#endif 124#endif /* HAVE_MESSAGES */
99 free (decode);
100 free (user);
101 return NULL;
102 }
103 } 125 }
126#ifdef HAVE_MESSAGES
127 else
128 MHD_DLOG (connection->daemon,
129 _ ("Basic authentication doesn't contain ':' separator.\n"));
130#endif
104 free (decode); 131 free (decode);
105 return user; 132 return NULL;
106} 133}
107 134
108 135
diff --git a/src/microhttpd/basicauth.h b/src/microhttpd/basicauth.h
index c3ba4a4b..c46fe27a 100644
--- a/src/microhttpd/basicauth.h
+++ b/src/microhttpd/basicauth.h
@@ -28,10 +28,17 @@
28#ifndef MHD_BASICAUTH_H 28#ifndef MHD_BASICAUTH_H
29#define MHD_BASICAUTH_H 1 29#define MHD_BASICAUTH_H 1
30 30
31#include "mhd_str.h"
32
31/** 33/**
32 * Beginning string for any valid Basic authentication header. 34 * Beginning string for any valid Basic authentication header.
33 */ 35 */
34#define _MHD_AUTH_BASIC_BASE "Basic " 36#define _MHD_AUTH_BASIC_BASE "Basic"
37
38struct MHD_RqBAuth
39{
40 struct _MHD_cstr_w_len token68;
41};
35 42
36#endif /* ! MHD_BASICAUTH_H */ 43#endif /* ! MHD_BASICAUTH_H */
37 44
diff --git a/src/microhttpd/connection.c b/src/microhttpd/connection.c
index f178f44e..9977ea6b 100644
--- a/src/microhttpd/connection.c
+++ b/src/microhttpd/connection.c
@@ -4724,6 +4724,9 @@ connection_reset (struct MHD_Connection *connection,
4724 c->last = NULL; 4724 c->last = NULL;
4725 c->colon = NULL; 4725 c->colon = NULL;
4726 c->header_size = 0; 4726 c->header_size = 0;
4727#if defined(BAUTH_SUPPORT) || defined(DAUTH_SUPPORT)
4728 c->rq_auth = NULL;
4729#endif
4727 c->keepalive = MHD_CONN_KEEPALIVE_UNKOWN; 4730 c->keepalive = MHD_CONN_KEEPALIVE_UNKOWN;
4728 /* Reset the read buffer to the starting size, 4731 /* Reset the read buffer to the starting size,
4729 preserving the bytes we have already read. */ 4732 preserving the bytes we have already read. */
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c
index 7ac5ad8b..23b41193 100644
--- a/src/microhttpd/digestauth.c
+++ b/src/microhttpd/digestauth.c
@@ -26,6 +26,7 @@
26 * @author Karlson2k (Evgeny Grin) 26 * @author Karlson2k (Evgeny Grin)
27 */ 27 */
28#include "digestauth.h" 28#include "digestauth.h"
29#include "gen_auth.h"
29#include "platform.h" 30#include "platform.h"
30#include "mhd_limits.h" 31#include "mhd_limits.h"
31#include "internal.h" 32#include "internal.h"
@@ -158,19 +159,19 @@ enum MHD_CheckNonceNC_
158 /** 159 /**
159 * The nonce and NC are OK (valid and NC was not used before). 160 * The nonce and NC are OK (valid and NC was not used before).
160 */ 161 */
161 MHD_DAUTH_NONCENC_OK = MHD_DAUTH_OK, 162 MHD_CHECK_NONCENC_OK = MHD_DAUTH_OK,
162 163
163 /** 164 /**
164 * The 'nonce' was overwritten with newer 'nonce' in the same slot or 165 * The 'nonce' was overwritten with newer 'nonce' in the same slot or
165 * NC was already used. 166 * NC was already used.
166 * The validity of the 'nonce' was not be checked. 167 * The validity of the 'nonce' was not be checked.
167 */ 168 */
168 MHD_DAUTH_NONCENC_STALE = MHD_DAUTH_NONCE_STALE, 169 MHD_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE,
169 170
170 /** 171 /**
171 * The 'nonce' is wrong, it was not generated before. 172 * The 'nonce' is wrong, it was not generated before.
172 */ 173 */
173 MHD_DAUTH_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG, 174 MHD_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG,
174}; 175};
175 176
176 177
@@ -536,7 +537,8 @@ digest_calc_ha1_from_user (const char *alg,
536 * @param cnonce client nonce 537 * @param cnonce client nonce
537 * @param qop qop-value: "", "auth" or "auth-int" (NOTE: only 'auth' is supported today.) 538 * @param qop qop-value: "", "auth" or "auth-int" (NOTE: only 'auth' is supported today.)
538 * @param method method from request 539 * @param method method from request
539 * @param uri requested URL 540 * @param uri requested URL, could be not zero-terminated
541 * @param uri_len the length of @a uri, in characters
540 * @param hentity H(entity body) if qop="auth-int" 542 * @param hentity H(entity body) if qop="auth-int"
541 * @param[in,out] da digest algorithm to use, also 543 * @param[in,out] da digest algorithm to use, also
542 * we write da->sessionkey (set to response request-digest or response-digest) 544 * we write da->sessionkey (set to response request-digest or response-digest)
@@ -549,6 +551,7 @@ digest_calc_response (const char *ha1,
549 const char *qop, 551 const char *qop,
550 const char *method, 552 const char *method,
551 const char *uri, 553 const char *uri,
554 size_t uri_len,
552 const char *hentity, 555 const char *hentity,
553 struct DigestAlgorithm *da) 556 struct DigestAlgorithm *da)
554{ 557{
@@ -564,7 +567,7 @@ digest_calc_response (const char *ha1,
564 1); 567 1);
565 digest_update (da, 568 digest_update (da,
566 (const unsigned char *) uri, 569 (const unsigned char *) uri,
567 strlen (uri)); 570 uri_len);
568#if 0 571#if 0
569 if (0 == strcasecmp (qop, 572 if (0 == strcasecmp (qop,
570 "auth-int")) 573 "auth-int"))
@@ -627,101 +630,17 @@ digest_calc_response (const char *ha1,
627} 630}
628 631
629 632
630/** 633static const struct MHD_RqDAuth *
631 * Lookup subvalue off of the HTTP Authorization header. 634get_rq_dauth_params (struct MHD_Connection *connection)
632 *
633 * A description of the input format for 'data' is at
634 * http://en.wikipedia.org/wiki/Digest_access_authentication
635 *
636 *
637 * @param dest where to store the result (possibly truncated if
638 * the buffer is not big enough).
639 * @param size size of dest
640 * @param data pointer to the Authorization header
641 * @param key key to look up in data
642 * @return size of the located value, 0 if otherwise
643 */
644static size_t
645lookup_sub_value (char *dest,
646 size_t size,
647 const char *data,
648 const char *key)
649{ 635{
650 size_t keylen; 636 const struct MHD_AuthRqHeader *rq_params;
651 size_t len; 637
652 const char *ptr; 638 rq_params = MHD_get_auth_rq_params_ (connection);
653 const char *eq; 639 if ( (NULL == rq_params) ||
654 const char *q1; 640 (MHD_AUTHTYPE_DIGEST != rq_params->auth_type) )
655 const char *q2; 641 return NULL;
656 const char *qn; 642
657 643 return rq_params->params.dauth;
658 if (0 == size)
659 return 0;
660 keylen = strlen (key);
661 ptr = data;
662 while ('\0' != *ptr)
663 {
664 if (NULL == (eq = strchr (ptr,
665 '=')))
666 return 0;
667 q1 = eq + 1;
668 while (' ' == *q1)
669 q1++;
670 if ('\"' != *q1)
671 {
672 q2 = strchr (q1,
673 ',');
674 qn = q2;
675 }
676 else
677 {
678 q1++;
679 q2 = strchr (q1,
680 '\"');
681 if (NULL == q2)
682 return 0; /* end quote not found */
683 qn = q2 + 1;
684 }
685 if ( (MHD_str_equal_caseless_n_ (ptr,
686 key,
687 keylen)) &&
688 (eq == &ptr[keylen]) )
689 {
690 if (NULL == q2)
691 {
692 len = strlen (q1) + 1;
693 if (size > len)
694 size = len;
695 size--;
696 memcpy (dest,
697 q1,
698 size);
699 dest[size] = '\0';
700 return size;
701 }
702 else
703 {
704 if (size > (size_t) ((q2 - q1) + 1))
705 size = (size_t) (q2 - q1) + 1;
706 size--;
707 memcpy (dest,
708 q1,
709 size);
710 dest[size] = '\0';
711 return size;
712 }
713 }
714 if (NULL == qn)
715 return 0;
716 ptr = strchr (qn,
717 ',');
718 if (NULL == ptr)
719 return 0;
720 ptr++;
721 while (' ' == *ptr)
722 ptr++;
723 }
724 return 0;
725} 644}
726 645
727 646
@@ -738,7 +657,6 @@ get_nonce_timestamp (const char *const nonce,
738 size_t noncelen, 657 size_t noncelen,
739 uint64_t *const ptimestamp) 658 uint64_t *const ptimestamp)
740{ 659{
741 mhd_assert ((0 == noncelen) || (strlen (nonce) == noncelen));
742 if (0 == noncelen) 660 if (0 == noncelen)
743 noncelen = strlen (nonce); 661 noncelen = strlen (nonce);
744 662
@@ -826,18 +744,19 @@ check_nonce_nc (struct MHD_Connection *connection,
826 uint32_t mod; 744 uint32_t mod;
827 enum MHD_CheckNonceNC_ ret; 745 enum MHD_CheckNonceNC_ ret;
828 746
747 mhd_assert (0 != noncelen);
829 mhd_assert (strlen (nonce) == noncelen); 748 mhd_assert (strlen (nonce) == noncelen);
830 mhd_assert (0 != nc); 749 mhd_assert (0 != nc);
831 if (MAX_NONCE_LENGTH < noncelen) 750 if (MAX_NONCE_LENGTH < noncelen)
832 return MHD_DAUTH_NONCENC_WRONG; /* This should be impossible, but static analysis 751 return MHD_CHECK_NONCENC_WRONG; /* This should be impossible, but static analysis
833 tools have a hard time with it *and* this also 752 tools have a hard time with it *and* this also
834 protects against unsafe modifications that may 753 protects against unsafe modifications that may
835 happen in the future... */ 754 happen in the future... */
836 mod = daemon->nonce_nc_size; 755 mod = daemon->nonce_nc_size;
837 if (0 == mod) 756 if (0 == mod)
838 return MHD_DAUTH_NONCENC_STALE; /* no array! */ 757 return MHD_CHECK_NONCENC_STALE; /* no array! */
839 if (nc >= UINT64_MAX - 64) 758 if (nc >= UINT64_MAX - 64)
840 return MHD_DAUTH_NONCENC_STALE; /* Overflow, unrealistically high value */ 759 return MHD_CHECK_NONCENC_STALE; /* Overflow, unrealistically high value */
841 760
842 nn = &daemon->nnc[get_nonce_nc_idx (mod, nonce, noncelen)]; 761 nn = &daemon->nnc[get_nonce_nc_idx (mod, nonce, noncelen)];
843 762
@@ -851,11 +770,11 @@ check_nonce_nc (struct MHD_Connection *connection,
851 if (0 == nn->nonce[0]) 770 if (0 == nn->nonce[0])
852 { /* The slot was never used, while the client's nonce value should be 771 { /* The slot was never used, while the client's nonce value should be
853 * recorded when it was generated by MHD */ 772 * recorded when it was generated by MHD */
854 ret = MHD_DAUTH_NONCENC_WRONG; 773 ret = MHD_CHECK_NONCENC_WRONG;
855 } 774 }
856 else if (0 != nn->nonce[noncelen]) 775 else if (0 != nn->nonce[noncelen])
857 { /* The value is the slot is wrong */ 776 { /* The value is the slot is wrong */
858 ret = MHD_DAUTH_NONCENC_STALE; 777 ret = MHD_CHECK_NONCENC_STALE;
859 } 778 }
860 else 779 else
861 { 780 {
@@ -863,7 +782,7 @@ check_nonce_nc (struct MHD_Connection *connection,
863 if (! get_nonce_timestamp (nn->nonce, 0, &slot_ts)) 782 if (! get_nonce_timestamp (nn->nonce, 0, &slot_ts))
864 { 783 {
865 mhd_assert (0); /* The value is the slot is wrong */ 784 mhd_assert (0); /* The value is the slot is wrong */
866 ret = MHD_DAUTH_NONCENC_STALE; 785 ret = MHD_CHECK_NONCENC_STALE;
867 } 786 }
868 else 787 else
869 { 788 {
@@ -873,21 +792,21 @@ check_nonce_nc (struct MHD_Connection *connection,
873 { 792 {
874 /* The nonce from the client may not have been placed in the slot 793 /* The nonce from the client may not have been placed in the slot
875 * because another nonce in that slot has not yet expired. */ 794 * because another nonce in that slot has not yet expired. */
876 ret = MHD_DAUTH_NONCENC_STALE; 795 ret = MHD_CHECK_NONCENC_STALE;
877 } 796 }
878 else if (TRIM_TO_TIMESTAMP (UINT64_MAX) / 2 >= ts_diff) 797 else if (TRIM_TO_TIMESTAMP (UINT64_MAX) / 2 >= ts_diff)
879 { 798 {
880 /* Too large value means that nonce_time is less than slot_ts. 799 /* Too large value means that nonce_time is less than slot_ts.
881 * The nonce from the client may have been overwritten by the newer 800 * The nonce from the client may have been overwritten by the newer
882 * nonce. */ 801 * nonce. */
883 ret = MHD_DAUTH_NONCENC_STALE; 802 ret = MHD_CHECK_NONCENC_STALE;
884 } 803 }
885 else 804 else
886 { 805 {
887 /* The nonce from the client should be generated after the nonce 806 /* The nonce from the client should be generated after the nonce
888 * in the slot has been expired, the nonce must be recorded, but 807 * in the slot has been expired, the nonce must be recorded, but
889 * it's not. */ 808 * it's not. */
890 ret = MHD_DAUTH_NONCENC_WRONG; 809 ret = MHD_CHECK_NONCENC_WRONG;
891 } 810 }
892 } 811 }
893 } 812 }
@@ -908,7 +827,7 @@ check_nonce_nc (struct MHD_Connection *connection,
908 else 827 else
909 nn->nmask = 0; /* big jump, unset all bits in the mask */ 828 nn->nmask = 0; /* big jump, unset all bits in the mask */
910 nn->nc = nc; 829 nn->nc = nc;
911 ret = MHD_DAUTH_NONCENC_OK; 830 ret = MHD_CHECK_NONCENC_OK;
912 } 831 }
913 else if (nc < nn->nc) 832 else if (nc < nn->nc)
914 { 833 {
@@ -919,15 +838,15 @@ check_nonce_nc (struct MHD_Connection *connection,
919 { 838 {
920 /* Out-of-order nonce, but within 64-bit bitmask, set bit */ 839 /* Out-of-order nonce, but within 64-bit bitmask, set bit */
921 nn->nmask |= (UINT64_C (1) << (nn->nc - nc - 1)); 840 nn->nmask |= (UINT64_C (1) << (nn->nc - nc - 1));
922 ret = MHD_DAUTH_NONCENC_OK; 841 ret = MHD_CHECK_NONCENC_OK;
923 } 842 }
924 else 843 else
925 /* 'nc' was already used or too old (more then 64 values ago) */ 844 /* 'nc' was already used or too old (more then 64 values ago) */
926 ret = MHD_DAUTH_NONCENC_STALE; 845 ret = MHD_CHECK_NONCENC_STALE;
927 } 846 }
928 else /* if (nc == nn->nc) */ 847 else /* if (nc == nn->nc) */
929 /* 'nc' was already used */ 848 /* 'nc' was already used */
930 ret = MHD_DAUTH_NONCENC_STALE; 849 ret = MHD_CHECK_NONCENC_STALE;
931 850
932 MHD_mutex_unlock_chk_ (&daemon->nnc_lock); 851 MHD_mutex_unlock_chk_ (&daemon->nnc_lock);
933 852
@@ -947,28 +866,40 @@ check_nonce_nc (struct MHD_Connection *connection,
947_MHD_EXTERN char * 866_MHD_EXTERN char *
948MHD_digest_auth_get_username (struct MHD_Connection *connection) 867MHD_digest_auth_get_username (struct MHD_Connection *connection)
949{ 868{
950 char user[MAX_USERNAME_LENGTH]; 869 const struct MHD_RqDAuth *params;
951 const char *header; 870 char *username;
952 871 size_t username_len;
953 if (MHD_NO == MHD_lookup_connection_value_n (connection, 872
954 MHD_HEADER_KIND, 873 params = get_rq_dauth_params (connection);
955 MHD_HTTP_HEADER_AUTHORIZATION, 874 if (NULL == params)
956 MHD_STATICSTR_LEN_ (
957 MHD_HTTP_HEADER_AUTHORIZATION),
958 &header,
959 NULL))
960 return NULL; 875 return NULL;
961 if (0 != strncmp (header, 876
962 _MHD_AUTH_DIGEST_BASE, 877 if (NULL == params->username.value.str)
963 MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE)))
964 return NULL; 878 return NULL;
965 header += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE); 879
966 if (0 == lookup_sub_value (user, 880 username_len = params->username.value.len;
967 sizeof (user), 881 username = malloc (username_len + 1);
968 header, 882 if (NULL == username)
969 "username"))
970 return NULL; 883 return NULL;
971 return strdup (user); 884
885 if (! params->username.quoted)
886 {
887 /* The username is not quoted, no need to unquote */
888 if (0 != username_len)
889 memcpy (username, params->username.value.str, username_len);
890 username[username_len] = 0; /* Zero-terminate */
891 }
892 else
893 {
894 /* Need to properly unquote the username */
895 mhd_assert (0 != username_len); /* Quoted string may not be zero-legth */
896 username_len = MHD_str_unquote (params->username.value.str, username_len,
897 username);
898 mhd_assert (0 != username_len); /* The unquoted string cannot be empty */
899 username[username_len] = 0; /* Zero-terminate */
900 }
901
902 return username;
972} 903}
973 904
974 905
@@ -1347,6 +1278,91 @@ check_argument_match (struct MHD_Connection *connection,
1347 1278
1348 1279
1349/** 1280/**
1281 * The size of the unquoting buffer in stack
1282 */
1283#define _MHD_STATIC_UNQ_BUFFER_SIZE 128
1284
1285/**
1286 * The result of parameter unquoting
1287 */
1288enum _MHD_GetUnqResult
1289{
1290 _MHD_UNQ_NON_EMPTY = 0, /**< The sting is not empty */
1291 _MHD_UNQ_NO_STRING = MHD_DAUTH_WRONG_HEADER, /**< No string (no such parameter) */
1292 _MHD_UNQ_EMPTY = 1, /**< The string is empty */
1293 _MHD_UNQ_TOO_LARGE = 2, /**< The string is too large to unqoute */
1294 _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */
1295};
1296
1297
1298/**
1299 * Get Digest authorisation parameter as unquoted string.
1300 * @param param the parameter to process
1301 * @param tmp1 the small buffer in stack
1302 * @param ptmp2 the pointer to pointer to malloc'ed buffer
1303 * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2
1304 * @param[out] unquoted the pointer to store the result, NOT zero terminated
1305 * @return enum code indicating result of the process
1306 */
1307static enum _MHD_GetUnqResult
1308get_unqouted_param (const struct MHD_RqDAuthParam *param,
1309 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE],
1310 char **ptmp2,
1311 size_t *ptmp2_size,
1312 struct _MHD_cstr_w_len *unquoted)
1313{
1314 char *str;
1315 size_t len;
1316 mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2));
1317 mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size));
1318 mhd_assert ((0 == *ptmp2_size) || \
1319 (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size));
1320
1321 if (NULL == param->value.str)
1322 {
1323 const struct _MHD_cstr_w_len res = {NULL, 0};
1324 mhd_assert (! param->quoted);
1325 mhd_assert (0 == param->value.len);
1326 memcpy (unquoted, &res, sizeof(res));
1327 return _MHD_UNQ_NO_STRING;
1328 }
1329 if (! param->quoted)
1330 {
1331 memcpy (unquoted, &param->value, sizeof(param->value));
1332 return (0 == param->value.len) ? _MHD_UNQ_EMPTY : _MHD_UNQ_NON_EMPTY;
1333 }
1334 /* The value is present and is quoted, needs to be copied and unquoted */
1335 mhd_assert (0 != param->value.len);
1336 if (param->value.len <= _MHD_STATIC_UNQ_BUFFER_SIZE)
1337 str = tmp1;
1338 else if (param->value.len <= *ptmp2_size)
1339 str = *ptmp2;
1340 else
1341 {
1342 if (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE)
1343 return _MHD_UNQ_TOO_LARGE;
1344 if (NULL != *ptmp2)
1345 free (*ptmp2);
1346 *ptmp2 = (char *) malloc (param->value.len);
1347 if (NULL == *ptmp2)
1348 return _MHD_UNQ_OUT_OF_MEM;
1349 *ptmp2_size = param->value.len;
1350 str = *ptmp2;
1351 }
1352
1353 len = MHD_str_unquote (param->value.str, param->value.len, str);
1354 if (1)
1355 {
1356 const struct _MHD_cstr_w_len res = {str, len};
1357 memcpy (unquoted, &res, sizeof(res));
1358 }
1359 mhd_assert (0 != unquoted->len);
1360 mhd_assert (unquoted->len < param->value.len);
1361 return _MHD_UNQ_NON_EMPTY;
1362}
1363
1364
1365/**
1350 * Authenticates the authorization header sent by the client 1366 * Authenticates the authorization header sent by the client
1351 * 1367 *
1352 * @param connection The MHD connection structure 1368 * @param connection The MHD connection structure
@@ -1375,10 +1391,6 @@ digest_auth_check_all (struct MHD_Connection *connection,
1375 unsigned int nonce_timeout) 1391 unsigned int nonce_timeout)
1376{ 1392{
1377 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); 1393 struct MHD_Daemon *daemon = MHD_get_master (connection->daemon);
1378 size_t len;
1379 const char *header;
1380 char nonce[MAX_NONCE_LENGTH];
1381 size_t nonce_len;
1382 char cnonce[MAX_NONCE_LENGTH]; 1394 char cnonce[MAX_NONCE_LENGTH];
1383 const unsigned int digest_size = digest_get_size (da); 1395 const unsigned int digest_size = digest_get_size (da);
1384 char ha1[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1]; 1396 char ha1[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1];
@@ -1389,219 +1401,368 @@ digest_auth_check_all (struct MHD_Connection *connection,
1389 char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1]; 1401 char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1];
1390 uint64_t nonce_time; 1402 uint64_t nonce_time;
1391 uint64_t t; 1403 uint64_t t;
1392 size_t left; /* number of characters left in 'header' for 'uri' */
1393 uint64_t nci; 1404 uint64_t nci;
1394 char *qmark; 1405 char *qmark;
1406 const struct MHD_RqDAuth *params;
1407 char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE]; /**< Temporal buffer in stack for unqouting */
1408 char *tmp2; /**< Temporal malloc'ed buffer for unqouting */
1409 size_t tmp2_size; /**< The size of @a tmp2 buffer */
1410 struct _MHD_cstr_w_len unquoted;
1411 enum _MHD_GetUnqResult unq_res;
1412 enum MHD_DigestAuthResult ret;
1413#ifdef HAVE_MESSAGES
1414 bool err_logged;
1415#endif /* HAVE_MESSAGES */
1395 1416
1396 VLA_CHECK_LEN_DIGEST (digest_size); 1417 tmp2 = NULL;
1397 if (MHD_NO == MHD_lookup_connection_value_n (connection, 1418 tmp2_size = 0;
1398 MHD_HEADER_KIND,
1399 MHD_HTTP_HEADER_AUTHORIZATION,
1400 MHD_STATICSTR_LEN_ (
1401 MHD_HTTP_HEADER_AUTHORIZATION),
1402 &header,
1403 NULL))
1404 return MHD_DAUTH_WRONG_HEADER;
1405 if (0 != strncmp (header,
1406 _MHD_AUTH_DIGEST_BASE,
1407 MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE)))
1408 return MHD_DAUTH_WRONG_HEADER;
1409 header += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE);
1410 left = strlen (header);
1411
1412 if (1)
1413 {
1414 char un[MAX_USERNAME_LENGTH];
1415
1416 len = lookup_sub_value (un,
1417 sizeof (un),
1418 header,
1419 "username");
1420 if (0 == len)
1421 return MHD_DAUTH_WRONG_HEADER;
1422 if (0 != strcmp (username,
1423 un))
1424 return MHD_DAUTH_WRONG_USERNAME;
1425 left -= strlen ("username") + len;
1426 }
1427
1428 if (1)
1429 {
1430 char r[MAX_REALM_LENGTH];
1431
1432 len = lookup_sub_value (r,
1433 sizeof (r),
1434 header,
1435 "realm");
1436 if (0 == len)
1437 return MHD_DAUTH_WRONG_HEADER;
1438 if (0 != strcmp (realm,
1439 r))
1440 return MHD_DAUTH_WRONG_REALM;
1441 left -= strlen ("realm") + len;
1442 }
1443
1444 if (0 == (len = lookup_sub_value (nonce,
1445 sizeof (nonce),
1446 header,
1447 "nonce")))
1448 return MHD_DAUTH_WRONG_HEADER;
1449 nonce_len = len;
1450 left -= strlen ("nonce") + len;
1451 if (left > 32 * 1024)
1452 {
1453 /* we do not permit URIs longer than 32k, as we want to
1454 make sure to not blow our stack (or per-connection
1455 heap memory limit). Besides, 32k is already insanely
1456 large, but of course in theory the
1457 #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large
1458 and would thus permit sending a >32k authorization
1459 header value. */
1460 return MHD_DAUTH_WRONG_HEADER;
1461 }
1462 if (! get_nonce_timestamp (nonce, nonce_len, &nonce_time))
1463 {
1464#ifdef HAVE_MESSAGES 1419#ifdef HAVE_MESSAGES
1465 MHD_DLOG (daemon, 1420 err_logged = false;
1466 _ ("Authentication failed, invalid timestamp format.\n")); 1421#endif /* HAVE_MESSAGES */
1467#endif 1422
1423 params = get_rq_dauth_params (connection);
1424 if (NULL == params)
1468 return MHD_DAUTH_WRONG_HEADER; 1425 return MHD_DAUTH_WRONG_HEADER;
1469 }
1470 1426
1471 t = MHD_monotonic_msec_counter (); 1427 do /* Only to avoid "goto" */
1472 /*
1473 * First level vetting for the nonce validity: if the timestamp
1474 * attached to the nonce exceeds `nonce_timeout', then the nonce is
1475 * invalid.
1476 */
1477 if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
1478 { 1428 {
1479 /* too old */ 1429 /* Check 'username' */
1480 return MHD_DAUTH_NONCE_STALE; 1430 unq_res = get_unqouted_param (&params->username, tmp1, &tmp2, &tmp2_size,
1481 } 1431 &unquoted);
1432 if (_MHD_UNQ_NON_EMPTY != unq_res)
1433 {
1434 if (_MHD_UNQ_NO_STRING == unq_res)
1435 ret = MHD_DAUTH_WRONG_HEADER;
1436 else if (_MHD_UNQ_EMPTY == unq_res)
1437 ret = MHD_DAUTH_WRONG_USERNAME;
1438 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1439 ret = MHD_DAUTH_WRONG_HEADER;
1440 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1441 ret = MHD_DAUTH_ERROR;
1442 else
1443 {
1444 mhd_assert (0); /* Must not happen */
1445 ret = MHD_DAUTH_ERROR;
1446 }
1447 break;
1448 }
1449 /* 'unquoted" may not contain binary zero */
1450 if ( (0 != strncmp (username, unquoted.str, unquoted.len)) ||
1451 (0 != username[unquoted.len]) )
1452 {
1453 ret = MHD_DAUTH_WRONG_USERNAME;
1454 break;
1455 }
1456 /* 'username' valid */
1482 1457
1483 calculate_nonce (nonce_time, 1458 /* Check 'realm' */
1484 connection->method, 1459 unq_res = get_unqouted_param (&params->realm, tmp1, &tmp2, &tmp2_size,
1485 daemon->digest_auth_random, 1460 &unquoted);
1486 daemon->digest_auth_rand_size, 1461 if (_MHD_UNQ_NON_EMPTY != unq_res)
1487 connection->url, 1462 {
1488 realm, 1463 if (_MHD_UNQ_NO_STRING == unq_res)
1489 da, 1464 ret = MHD_DAUTH_WRONG_HEADER;
1490 noncehashexp); 1465 else if (_MHD_UNQ_EMPTY == unq_res)
1491 /* 1466 ret = MHD_DAUTH_WRONG_REALM;
1492 * Second level vetting for the nonce validity 1467 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1493 * if the timestamp attached to the nonce is valid 1468 ret = MHD_DAUTH_WRONG_HEADER;
1494 * and possibly fabricated (in case of an attack) 1469 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1495 * the attacker must also know the random seed to be 1470 ret = MHD_DAUTH_ERROR;
1496 * able to generate a "sane" nonce, which if he does 1471 else
1497 * not, the nonce fabrication process going to be 1472 {
1498 * very hard to achieve. 1473 mhd_assert (0); /* Must not happen */
1499 */ 1474 ret = MHD_DAUTH_ERROR;
1500 if (0 != strcmp (nonce, 1475 }
1501 noncehashexp)) 1476 break;
1502 { 1477 }
1503 return MHD_DAUTH_NONCE_WRONG; 1478 /* 'unquoted" may not contain binary zero */
1504 } 1479 if ( (0 != strncmp (realm, unquoted.str, unquoted.len)) ||
1505 if ( (0 == lookup_sub_value (cnonce, 1480 (0 != realm[unquoted.len]) )
1506 sizeof (cnonce), 1481 {
1507 header, 1482 ret = MHD_DAUTH_WRONG_REALM;
1508 "cnonce")) || 1483 break;
1509 (0 == lookup_sub_value (qop, 1484 }
1510 sizeof (qop), 1485 /* 'realm' valid */
1511 header, 1486
1512 "qop")) || 1487 /* Check 'nonce' */
1513 ( (0 != strcmp (qop, 1488 unq_res = get_unqouted_param (&params->nonce, tmp1, &tmp2, &tmp2_size,
1514 "auth")) && 1489 &unquoted);
1515 (0 != strcmp (qop, 1490 if (_MHD_UNQ_NON_EMPTY != unq_res)
1516 "")) ) || 1491 {
1517 (0 == (len = lookup_sub_value (nc, 1492 if (_MHD_UNQ_NO_STRING == unq_res)
1518 sizeof (nc), 1493 ret = MHD_DAUTH_WRONG_HEADER;
1519 header, 1494 else if (_MHD_UNQ_EMPTY == unq_res)
1520 "nc")) ) || 1495 ret = MHD_DAUTH_NONCE_WRONG;
1521 (0 == lookup_sub_value (response, 1496 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1522 sizeof (response), 1497 ret = MHD_DAUTH_WRONG_HEADER;
1523 header, 1498 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1524 "response")) ) 1499 ret = MHD_DAUTH_ERROR;
1525 { 1500 else
1526#ifdef HAVE_MESSAGES 1501 {
1527 MHD_DLOG (daemon, 1502 mhd_assert (0); /* Must not happen */
1528 _ ("Authentication failed, invalid format.\n")); 1503 ret = MHD_DAUTH_ERROR;
1529#endif 1504 }
1530 return MHD_DAUTH_WRONG_HEADER; 1505 break;
1531 } 1506 }
1532 if (len != MHD_strx_to_uint64_n_ (nc, 1507 /* TODO: check correct 'nonce' length */
1533 len, 1508 if (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time))
1534 &nci)) 1509 {
1535 {
1536#ifdef HAVE_MESSAGES
1537 MHD_DLOG (daemon,
1538 _ ("Authentication failed, invalid nc format.\n"));
1539#endif
1540 return MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */
1541 }
1542 if (0 == nci)
1543 {
1544#ifdef HAVE_MESSAGES 1510#ifdef HAVE_MESSAGES
1545 MHD_DLOG (daemon, 1511 MHD_DLOG (daemon,
1546 _ ("Authentication failed, invalid 'nc' value.\n")); 1512 _ ("Authentication failed, invalid timestamp format.\n"));
1513 err_logged = true;
1547#endif 1514#endif
1548 return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ 1515 ret = MHD_DAUTH_NONCE_WRONG;
1549 } 1516 break;
1517 }
1518 t = MHD_monotonic_msec_counter ();
1519 /*
1520 * First level vetting for the nonce validity: if the timestamp
1521 * attached to the nonce exceeds `nonce_timeout', then the nonce is
1522 * invalid.
1523 */
1524 if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000))
1525 {
1526 /* too old */
1527 ret = MHD_DAUTH_NONCE_STALE;
1528 break;
1529 }
1550 1530
1551 if (1) 1531 calculate_nonce (nonce_time,
1552 { 1532 connection->method,
1553 enum MHD_CheckNonceNC_ nonce_nc_check; 1533 daemon->digest_auth_random,
1534 daemon->digest_auth_rand_size,
1535 connection->url,
1536 realm,
1537 da,
1538 noncehashexp);
1554 /* 1539 /*
1555 * Checking if that combination of nonce and nc is sound 1540 * Second level vetting for the nonce validity
1556 * and not a replay attack attempt. Refuse if nonce was not 1541 * if the timestamp attached to the nonce is valid
1557 * generated previously. 1542 * and possibly fabricated (in case of an attack)
1543 * the attacker must also know the random seed to be
1544 * able to generate a "sane" nonce, which if he does
1545 * not, the nonce fabrication process going to be
1546 * very hard to achieve.
1558 */ 1547 */
1559 nonce_nc_check = check_nonce_nc (connection, 1548 if ((0 != strncmp (noncehashexp, unquoted.str, unquoted.len)) ||
1560 nonce, 1549 (0 != noncehashexp[unquoted.len]))
1561 nonce_len, 1550 {
1562 nonce_time, 1551 ret = MHD_DAUTH_NONCE_WRONG;
1563 nci); 1552 break;
1564 if (MHD_DAUTH_NONCENC_STALE == nonce_nc_check) 1553 }
1554 /* 'nonce' valid */
1555
1556 /* Get 'cnonce' */
1557 unq_res = get_unqouted_param (&params->cnonce, tmp1, &tmp2, &tmp2_size,
1558 &unquoted);
1559 if (_MHD_UNQ_NON_EMPTY != unq_res)
1560 {
1561 if (_MHD_UNQ_NO_STRING == unq_res)
1562 ret = MHD_DAUTH_WRONG_HEADER;
1563 else if (_MHD_UNQ_EMPTY == unq_res)
1564 ret = MHD_DAUTH_WRONG_HEADER;
1565 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1566 ret = MHD_DAUTH_WRONG_HEADER;
1567 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1568 ret = MHD_DAUTH_ERROR;
1569 else
1570 {
1571 mhd_assert (0); /* Must not happen */
1572 ret = MHD_DAUTH_ERROR;
1573 }
1574 break;
1575 }
1576 if (sizeof(cnonce) <= unquoted.len)
1577 {
1578 /* TODO: handle large client nonces */
1579 ret = MHD_DAUTH_ERROR;
1580 break;
1581 }
1582 /* TODO: avoid memcpy() */
1583 memcpy (cnonce, unquoted.str, unquoted.len);
1584 cnonce[unquoted.len] = 0;
1585 /* Got 'cnonce' */
1586
1587 /* Get 'qop' */
1588 unq_res = get_unqouted_param (&params->qop, tmp1, &tmp2, &tmp2_size,
1589 &unquoted);
1590 if (_MHD_UNQ_NON_EMPTY != unq_res)
1591 {
1592 if (_MHD_UNQ_NO_STRING == unq_res)
1593 ret = MHD_DAUTH_WRONG_HEADER;
1594 else if (_MHD_UNQ_EMPTY == unq_res)
1595 ret = MHD_DAUTH_WRONG_HEADER;
1596 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1597 ret = MHD_DAUTH_WRONG_HEADER;
1598 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1599 ret = MHD_DAUTH_ERROR;
1600 else
1601 {
1602 mhd_assert (0); /* Must not happen */
1603 ret = MHD_DAUTH_ERROR;
1604 }
1605 break;
1606 }
1607 if (sizeof(qop) <= unquoted.len)
1608 {
1609 /* TODO: handle large client qop */
1610 ret = MHD_DAUTH_ERROR;
1611 break;
1612 }
1613 /* TODO: avoid memcpy() */
1614 memcpy (qop, unquoted.str, unquoted.len);
1615 qop[unquoted.len] = 0;
1616 /* TODO: use caseless match, use dedicated return code */
1617 if ( (0 != strcmp (qop, "auth")) &&
1618 (0 != strcmp (qop,"")) )
1619 {
1620 ret = MHD_DAUTH_WRONG_HEADER;
1621 break;
1622 }
1623 /* Got 'qop' */
1624
1625 /* Get 'nc' */
1626 unq_res = get_unqouted_param (&params->nc, tmp1, &tmp2, &tmp2_size,
1627 &unquoted);
1628 if (_MHD_UNQ_NON_EMPTY != unq_res)
1629 {
1630 if (_MHD_UNQ_NO_STRING == unq_res)
1631 ret = MHD_DAUTH_WRONG_HEADER;
1632 else if (_MHD_UNQ_EMPTY == unq_res)
1633 ret = MHD_DAUTH_WRONG_HEADER;
1634 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1635 ret = MHD_DAUTH_WRONG_HEADER;
1636 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1637 ret = MHD_DAUTH_ERROR;
1638 else
1639 {
1640 mhd_assert (0); /* Must not happen */
1641 ret = MHD_DAUTH_ERROR;
1642 }
1643 break;
1644 }
1645 if (sizeof(nc) <= unquoted.len)
1646 {
1647 /* TODO: handle large client nc */
1648 ret = MHD_DAUTH_ERROR;
1649 break;
1650 }
1651 /* TODO: avoid memcpy() */
1652 memcpy (nc, unquoted.str, unquoted.len);
1653 nc[unquoted.len] = 0;
1654 if (unquoted.len != MHD_strx_to_uint64_n_ (nc,
1655 unquoted.len,
1656 &nci))
1565 { 1657 {
1566#ifdef HAVE_MESSAGES 1658#ifdef HAVE_MESSAGES
1567 MHD_DLOG (daemon, 1659 MHD_DLOG (daemon,
1568 _ ("Stale nonce received. If this happens a lot, you should " 1660 _ ("Authentication failed, invalid nc format.\n"));
1569 "probably increase the size of the nonce array.\n")); 1661 err_logged = true;
1570#endif 1662#endif
1571 return MHD_DAUTH_NONCE_STALE; 1663 ret = MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */
1664 break;
1572 } 1665 }
1573 else if (MHD_DAUTH_NONCENC_WRONG == nonce_nc_check) 1666 if (0 == nci)
1574 { 1667 {
1575#ifdef HAVE_MESSAGES 1668#ifdef HAVE_MESSAGES
1576 MHD_DLOG (daemon, 1669 MHD_DLOG (daemon,
1577 _ ("Received nonce that technically valid, but was not " 1670 _ ("Authentication failed, invalid 'nc' value.\n"));
1578 "generated by MHD. This may indicate an attack attempt.\n")); 1671 err_logged = true;
1579#endif 1672#endif
1580 return MHD_DAUTH_NONCE_WRONG; 1673 ret = MHD_DAUTH_WRONG_HEADER; /* invalid nc value */
1674 break;
1581 } 1675 }
1582 mhd_assert (MHD_DAUTH_NONCENC_OK == nonce_nc_check); 1676 /* Got 'nc' */
1583 }
1584 1677
1585 if (1) 1678 /* Get 'response' */
1586 { 1679 unq_res = get_unqouted_param (&params->response, tmp1, &tmp2, &tmp2_size,
1587 char *uri; 1680 &unquoted);
1681 if (_MHD_UNQ_NON_EMPTY != unq_res)
1682 {
1683 if (_MHD_UNQ_NO_STRING == unq_res)
1684 ret = MHD_DAUTH_WRONG_HEADER;
1685 else if (_MHD_UNQ_EMPTY == unq_res)
1686 ret = MHD_DAUTH_WRONG_HEADER;
1687 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1688 ret = MHD_DAUTH_WRONG_HEADER;
1689 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1690 ret = MHD_DAUTH_ERROR;
1691 else
1692 {
1693 mhd_assert (0); /* Must not happen */
1694 ret = MHD_DAUTH_ERROR;
1695 }
1696 break;
1697 }
1698 if (sizeof(response) <= unquoted.len)
1699 {
1700 /* TODO: handle large client response */
1701 ret = MHD_DAUTH_ERROR;
1702 break;
1703 }
1704 /* TODO: avoid memcpy() */
1705 memcpy (response, unquoted.str, unquoted.len);
1706 response[unquoted.len] = 0;
1707 /* Got 'response' */
1588 1708
1589 uri = malloc (left + 1); 1709 if (1)
1590 if (NULL == uri)
1591 { 1710 {
1711 enum MHD_CheckNonceNC_ nonce_nc_check;
1712 /*
1713 * Checking if that combination of nonce and nc is sound
1714 * and not a replay attack attempt. Refuse if nonce was not
1715 * generated previously.
1716 */
1717 nonce_nc_check = check_nonce_nc (connection,
1718 noncehashexp,
1719 NONCE_STD_LEN (digest_size),
1720 nonce_time,
1721 nci);
1722 if (MHD_CHECK_NONCENC_STALE == nonce_nc_check)
1723 {
1592#ifdef HAVE_MESSAGES 1724#ifdef HAVE_MESSAGES
1593 MHD_DLOG (daemon, 1725 MHD_DLOG (daemon,
1594 _ ("Failed to allocate memory for auth header processing.\n")); 1726 _ ("Stale nonce received. If this happens a lot, you should "
1595#endif /* HAVE_MESSAGES */ 1727 "probably increase the size of the nonce array.\n"));
1596 return MHD_DAUTH_ERROR; 1728 err_logged = true;
1729#endif
1730 ret = MHD_DAUTH_NONCE_STALE;
1731 break;
1732 }
1733 else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check)
1734 {
1735#ifdef HAVE_MESSAGES
1736 MHD_DLOG (daemon,
1737 _ ("Received nonce that technically valid, but was not "
1738 "generated by MHD. This may indicate an attack attempt.\n"));
1739 err_logged = true;
1740#endif
1741 ret = MHD_DAUTH_NONCE_WRONG;
1742 break;
1743 }
1744 mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check);
1597 } 1745 }
1598 if (0 == lookup_sub_value (uri, 1746
1599 left + 1, 1747 /* Get 'uri' */
1600 header, 1748 unq_res = get_unqouted_param (&params->uri, tmp1, &tmp2, &tmp2_size,
1601 "uri")) 1749 &unquoted);
1750 if (_MHD_UNQ_NON_EMPTY != unq_res)
1602 { 1751 {
1603 free (uri); 1752 if (_MHD_UNQ_NO_STRING == unq_res)
1604 return MHD_DAUTH_WRONG_HEADER; 1753 ret = MHD_DAUTH_WRONG_HEADER;
1754 else if (_MHD_UNQ_EMPTY == unq_res)
1755 ret = MHD_DAUTH_WRONG_HEADER;
1756 else if (_MHD_UNQ_TOO_LARGE == unq_res)
1757 ret = MHD_DAUTH_WRONG_HEADER;
1758 else if (_MHD_UNQ_OUT_OF_MEM == unq_res)
1759 ret = MHD_DAUTH_ERROR;
1760 else
1761 {
1762 mhd_assert (0); /* Must not happen */
1763 ret = MHD_DAUTH_ERROR;
1764 }
1765 break;
1605 } 1766 }
1606 if (NULL != digest) 1767 if (NULL != digest)
1607 { 1768 {
@@ -1609,7 +1770,7 @@ digest_auth_check_all (struct MHD_Connection *connection,
1609 digest_calc_ha1_from_digest (digest_get_algo_name (da), 1770 digest_calc_ha1_from_digest (digest_get_algo_name (da),
1610 da, 1771 da,
1611 digest, 1772 digest,
1612 nonce, 1773 noncehashexp,
1613 cnonce); 1774 cnonce);
1614 } 1775 }
1615 else 1776 else
@@ -1620,7 +1781,7 @@ digest_auth_check_all (struct MHD_Connection *connection,
1620 username, 1781 username,
1621 realm, 1782 realm,
1622 password, 1783 password,
1623 nonce, 1784 noncehashexp,
1624 cnonce, 1785 cnonce,
1625 da); 1786 da);
1626 } 1787 }
@@ -1629,60 +1790,111 @@ digest_auth_check_all (struct MHD_Connection *connection,
1629 digest_size * 2 + 1); 1790 digest_size * 2 + 1);
1630 /* This will initialize da->sessionkey (respexp) */ 1791 /* This will initialize da->sessionkey (respexp) */
1631 digest_calc_response (ha1, 1792 digest_calc_response (ha1,
1632 nonce, 1793 noncehashexp,
1633 nc, 1794 nc,
1634 cnonce, 1795 cnonce,
1635 qop, 1796 qop,
1636 connection->method, 1797 connection->method,
1637 uri, 1798 unquoted.str,
1799 unquoted.len,
1638 hentity, 1800 hentity,
1639 da); 1801 da);
1640 qmark = strchr (uri,
1641 '?');
1642 if (NULL != qmark)
1643 *qmark = '\0';
1644
1645 /* Need to unescape URI before comparing with connection->url */
1646 daemon->unescape_callback (daemon->unescape_callback_cls,
1647 connection,
1648 uri);
1649 if (0 != strcmp (uri,
1650 connection->url))
1651 {
1652#ifdef HAVE_MESSAGES
1653 MHD_DLOG (daemon,
1654 _ ("Authentication failed, URI does not match.\n"));
1655#endif
1656 free (uri);
1657 return MHD_DAUTH_WRONG_URI;
1658 }
1659
1660 if (1) 1802 if (1)
1661 { 1803 {
1662 const char *args = qmark; 1804 char *uri;
1663 1805 size_t uri_len;
1664 if (NULL == args) 1806 uri_len = unquoted.len;
1665 args = ""; 1807 /* TODO: simplify string copy, avoid potential double copy */
1808 if ( ((tmp1 != unquoted.str) && (tmp2 != unquoted.str)) ||
1809 ((tmp1 == unquoted.str) && (sizeof(tmp1) >= unquoted.len)) ||
1810 ((tmp2 == unquoted.str) && (tmp2_size >= unquoted.len)))
1811 {
1812 char *buf;
1813 mhd_assert ((tmp1 != unquoted.str) || \
1814 (sizeof(tmp1) == unquoted.len));
1815 mhd_assert ((tmp2 != unquoted.str) || \
1816 (tmp2_size == unquoted.len));
1817 buf = malloc (unquoted.len + 1);
1818 if (NULL == buf)
1819 {
1820 ret = MHD_DAUTH_ERROR;
1821 break;
1822 }
1823 memcpy (buf, unquoted.str, unquoted.len);
1824 if (NULL != tmp2)
1825 free (tmp2);
1826 tmp2 = buf;
1827 tmp2_size = unquoted.len + 1;
1828 uri = tmp2;
1829 }
1830 else if (tmp1 == unquoted.str)
1831 uri = tmp1;
1666 else 1832 else
1667 args++; 1833 {
1668 if (MHD_NO == 1834 mhd_assert (tmp2 == unquoted.str);
1669 check_argument_match (connection, 1835 uri = tmp2;
1670 args) ) 1836 }
1837
1838 uri[uri_len] = 0;
1839 qmark = memchr (uri,
1840 '?',
1841 uri_len);
1842 if (NULL != qmark)
1843 *qmark = '\0';
1844
1845 /* Need to unescape URI before comparing with connection->url */
1846 daemon->unescape_callback (daemon->unescape_callback_cls,
1847 connection,
1848 uri);
1849 if (0 != strcmp (uri,
1850 connection->url))
1671 { 1851 {
1672#ifdef HAVE_MESSAGES 1852#ifdef HAVE_MESSAGES
1673 MHD_DLOG (daemon, 1853 MHD_DLOG (daemon,
1674 _ ("Authentication failed, arguments do not match.\n")); 1854 _ ("Authentication failed, URI does not match.\n"));
1855 err_logged = true;
1675#endif 1856#endif
1676 free (uri); 1857 ret = MHD_DAUTH_WRONG_URI;
1677 return MHD_DAUTH_WRONG_URI; 1858 break;
1678 } 1859 }
1860
1861 if (1)
1862 {
1863 const char *args = qmark;
1864
1865 if (NULL == args)
1866 args = "";
1867 else
1868 args++;
1869 if (MHD_NO ==
1870 check_argument_match (connection,
1871 args) )
1872 {
1873#ifdef HAVE_MESSAGES
1874 MHD_DLOG (daemon,
1875 _ ("Authentication failed, arguments do not match.\n"));
1876 err_logged = true;
1877#endif
1878 ret = MHD_DAUTH_WRONG_URI;
1879 break;
1880 }
1881 }
1882
1883 ret = (0 == strcmp (response,
1884 digest_get_hex_buffer (da)))
1885 ? MHD_DAUTH_OK
1886 : MHD_DAUTH_RESPONSE_WRONG;
1679 } 1887 }
1680 free (uri); 1888 } while (0);
1889 if (NULL != tmp2)
1890 free (tmp2);
1891
1892 if ((MHD_DAUTH_OK != ret) && ! err_logged)
1893 {
1894 (void) 0; /* TODO: add logging */
1681 } 1895 }
1682 return (0 == strcmp (response, 1896
1683 digest_get_hex_buffer (da))) 1897 return ret;
1684 ? MHD_DAUTH_OK
1685 : MHD_DAUTH_RESPONSE_WRONG;
1686} 1898}
1687 1899
1688 1900
diff --git a/src/microhttpd/digestauth.h b/src/microhttpd/digestauth.h
index 689c0487..77c7c402 100644
--- a/src/microhttpd/digestauth.h
+++ b/src/microhttpd/digestauth.h
@@ -28,10 +28,48 @@
28 28
29#ifndef MHD_DIGESTAUTH_H 29#ifndef MHD_DIGESTAUTH_H
30#define MHD_DIGESTAUTH_H 1 30#define MHD_DIGESTAUTH_H 1
31
32#include "mhd_options.h"
33#include "mhd_str.h"
34#ifdef HAVE_STDBOOL_H
35#include <stdbool.h>
36#endif /* HAVE_STDBOOL_H */
37
38
39/**
40 * The maximum supported size for Digest Auth parameters, like "real",
41 * "username" etc. This limitation is used only for quoted parameters.
42 * Parameters without quoted backslash character will be processed as long
43 * as they fit connection pool (buffer) size.
44 */
45#define _MHD_AUTH_DIGEST_MAX_PARAM_SIZE (65535)
46
31/** 47/**
32 * Beginning string for any valid Digest authentication header. 48 * Beginning string for any valid Digest Authentication header.
33 */ 49 */
34#define _MHD_AUTH_DIGEST_BASE "Digest " 50#define _MHD_AUTH_DIGEST_BASE "Digest"
51
52struct MHD_RqDAuthParam
53{
54 struct _MHD_cstr_w_len value;
55 bool quoted;
56};
57
58struct MHD_RqDAuth
59{
60 struct MHD_RqDAuthParam nonce;
61 struct MHD_RqDAuthParam opaque;
62 struct MHD_RqDAuthParam algorithm;
63 struct MHD_RqDAuthParam response;
64 struct MHD_RqDAuthParam username;
65 struct MHD_RqDAuthParam username_ext;
66 struct MHD_RqDAuthParam realm;
67 struct MHD_RqDAuthParam uri;
68 struct MHD_RqDAuthParam qop;
69 struct MHD_RqDAuthParam cnonce;
70 struct MHD_RqDAuthParam nc;
71 bool userhash;
72};
35 73
36#endif /* ! MHD_DIGESTAUTH_H */ 74#endif /* ! MHD_DIGESTAUTH_H */
37 75
diff --git a/src/microhttpd/gen_auth.c b/src/microhttpd/gen_auth.c
new file mode 100644
index 00000000..e993fb20
--- /dev/null
+++ b/src/microhttpd/gen_auth.c
@@ -0,0 +1,511 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2022 Evgeny Grin (Karlson2k)
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.
17 If not, see <http://www.gnu.org/licenses/>.
18*/
19
20/**
21 * @file microhttpd/gen_auth.c
22 * @brief HTTP authorisation general functions
23 * @author Karlson2k (Evgeny Grin)
24 */
25
26#include "gen_auth.h"
27#include "internal.h"
28#include "connection.h"
29#include "mhd_str.h"
30#include "mhd_assert.h"
31
32#ifdef BAUTH_SUPPORT
33#include "basicauth.h"
34#endif /* BAUTH_SUPPORT */
35#ifdef DAUTH_SUPPORT
36#include "digestauth.h"
37#endif /* DAUTH_SUPPORT */
38
39#if ! defined(BAUTH_SUPPORT) && ! defined(DAUTH_SUPPORT)
40#error This file requires Basic or Digest authentication support
41#endif
42
43#ifdef BAUTH_SUPPORT
44/**
45 * Parse request Authorization header parameters for Basic Authentication
46 * @param str the header string, everything after "Basic " substring
47 * @param str_len the length of @a str in characters
48 * @param[out] pbauth the pointer to the structure with Basic Authentication
49 * parameters
50 * @return true if parameters has been successfully parsed,
51 * false if format of the @a str is invalid
52 */
53static bool
54parse_bauth_params (const char *str,
55 size_t str_len,
56 struct MHD_RqBAuth *pbauth)
57{
58 size_t i;
59
60 i = 0;
61
62 /* Skip all whitespaces at start */
63 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
64 i++;
65
66 if (str_len > i)
67 {
68 size_t token68_start;
69 size_t token68_len;
70
71 /* 'i' points to the first non-whitespace char after scheme token */
72 token68_start = i;
73 /* Find end of the token. Token cannot contain whitespace. */
74 while (i < str_len && ' ' != str[i] && '\t' != str[i])
75 {
76 if (0 == str[0])
77 return false; /* Binary zero is not allowed */
78 i++;
79 }
80 token68_len = i - token68_start;
81 mhd_assert (0 != token68_len);
82
83 /* Skip all whitespaces */
84 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
85 i++;
86 /* Check whether any garbage is present at the end of the string */
87 if (str_len != i)
88 return false;
89 else
90 {
91 /* No more data in the string, only single token68. */
92 const struct _MHD_cstr_w_len tkn = { str + token68_start, token68_len};
93 memcpy (&pbauth->token68, &tkn, sizeof(tkn));
94 }
95 }
96 return true;
97}
98
99
100#endif /* BAUTH_SUPPORT */
101
102#ifdef DAUTH_SUPPORT
103
104/**
105 * Helper structure to map token name to position where to put token's value
106 */
107struct dauth_token_param
108{
109 const struct _MHD_cstr_w_len *const tk_name;
110 struct MHD_RqDAuthParam *const param;
111};
112
113/**
114 * Parse request Authorization header parameters for Digest Authentication
115 * @param str the header string, everything after "Digest " substring
116 * @param str_len the length of @a str in characters
117 * @param[out] pdauth the pointer to the structure with Digest Authentication
118 * parameters
119 * @return true if parameters has been successfully parsed,
120 * false if format of the @a str is invalid
121 */
122static bool
123parse_dauth_params (const char *str,
124 const size_t str_len,
125 struct MHD_RqDAuth *pdauth)
126{
127 static const struct _MHD_cstr_w_len nonce_tk = _MHD_S_STR_W_LEN ("nonce");
128 static const struct _MHD_cstr_w_len opaque_tk = _MHD_S_STR_W_LEN ("opaque");
129 static const struct _MHD_cstr_w_len algorithm_tk =
130 _MHD_S_STR_W_LEN ("algorithm");
131 static const struct _MHD_cstr_w_len response_tk =
132 _MHD_S_STR_W_LEN ("response");
133 static const struct _MHD_cstr_w_len username_tk =
134 _MHD_S_STR_W_LEN ("username");
135 static const struct _MHD_cstr_w_len username_ext_tk =
136 _MHD_S_STR_W_LEN ("username*");
137 static const struct _MHD_cstr_w_len realm_tk = _MHD_S_STR_W_LEN ("realm");
138 static const struct _MHD_cstr_w_len uri_tk = _MHD_S_STR_W_LEN ("uri");
139 static const struct _MHD_cstr_w_len qop_tk = _MHD_S_STR_W_LEN ("qop");
140 static const struct _MHD_cstr_w_len cnonce_tk = _MHD_S_STR_W_LEN ("cnonce");
141 static const struct _MHD_cstr_w_len nc_tk = _MHD_S_STR_W_LEN ("nc");
142 static const struct _MHD_cstr_w_len userhash_tk =
143 _MHD_S_STR_W_LEN ("userhash");
144 struct MHD_RqDAuthParam userhash;
145 struct dauth_token_param map[] = {
146 {&nonce_tk, &(pdauth->nonce)},
147 {&opaque_tk, &(pdauth->opaque)},
148 {&algorithm_tk, &(pdauth->algorithm)},
149 {&response_tk, &(pdauth->response)},
150 {&username_tk, &(pdauth->username)},
151 {&username_ext_tk, &(pdauth->username_ext)},
152 {&realm_tk, &(pdauth->realm)},
153 {&uri_tk, &(pdauth->uri)},
154 {&qop_tk, &(pdauth->qop)},
155 {&cnonce_tk, &(pdauth->cnonce)},
156 {&nc_tk, &(pdauth->nc)},
157 {&userhash_tk, &userhash}
158 };
159 size_t i;
160 size_t p;
161
162 memset (&userhash, 0, sizeof(userhash));
163 i = 0;
164
165 /* Skip all whitespaces at start */
166 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
167 i++;
168
169 while (str_len > i)
170 {
171 size_t left;
172 mhd_assert (' ' != str[i]);
173 mhd_assert ('\t' != str[i]);
174
175 left = str_len - i;
176 for (p = 0; p < sizeof(map) / sizeof(map[0]); p++)
177 {
178 struct dauth_token_param *const aparam = map + p;
179 if ( (aparam->tk_name->len < left) &&
180 MHD_str_equal_caseless_bin_n_ (str + i, aparam->tk_name->str,
181 aparam->tk_name->len) &&
182 (('=' == str[i + aparam->tk_name->len]) ||
183 (' ' == str[i + aparam->tk_name->len]) ||
184 ('\t' == str[i + aparam->tk_name->len])) )
185 {
186 size_t value_start;
187 size_t value_len;
188 bool quoted; /* Only mark as "quoted" if backslash-escape used */
189
190 quoted = false;
191 i += aparam->tk_name->len;
192 /* Skip all whitespaces before '=' */
193 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
194 i++;
195 if ((i == str_len) || ('=' != str[i]))
196 return false; /* No equal sign, broken data */
197 i++;
198 /* Skip all whitespaces after '=' */
199 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
200 i++;
201 if ((str_len > i) && ('"' == str[i]))
202 { /* Value is in quotation marks */
203 i++; /* Advance after the opening quote */
204 value_start = i;
205 while (str_len > i && '"' != str[i])
206 {
207 if ('\\' == str[i])
208 {
209 i++;
210 quoted = true; /* Have escaped chars */
211 }
212 if (0 == str[i])
213 return false; /* Binary zero in parameter value */
214 i++;
215 }
216 if (str_len <= i)
217 return false; /* No closing quote */
218 mhd_assert ('"' == str[i]);
219 value_len = i - value_start;
220 i++; /* Advance after the closing quote */
221 }
222 else
223 {
224 value_start = i;
225 while (str_len > i && ',' != str[i] &&
226 ' ' != str[i] && '\t' != str[i] && ';' != str[i])
227 {
228 if (0 == str[i])
229 return false; /* Binary zero in parameter value */
230 i++;
231 }
232 value_len = i - value_start;
233 }
234 /* Skip all whitespaces after parameter value */
235 while (str_len > i && (' ' == str[i] || '\t' == str[i]))
236 i++;
237 if ((str_len > i) && (',' != str[i]))
238 return false; /* Garbage after parameter value */
239
240 /* Have valid parameter name and value */
241 mhd_assert (! quoted || 0 != value_len);
242 if (1)
243 {
244 const struct _MHD_cstr_w_len val = {str + value_start, value_len};
245 memcpy (&aparam->param->value, &val, sizeof(val));
246 }
247 aparam->param->quoted = quoted;
248
249 break; /* Found matching parameter name */
250 }
251 }
252 if (p == sizeof(map) / sizeof(map[0]))
253 {
254 /* No matching parameter name */
255 while (str_len > i && ',' != str[i])
256 {
257 if ('"' == str[i])
258 { /* Skip quoted part */
259 i++; /* Advance after the opening quote */
260 while (str_len > i && '"' != str[i])
261 {
262 if ('\\' == str[i])
263 i++; /* Skip escaped char */
264 i++;
265 }
266 if (str_len <= i)
267 return false; /* No closing quote */
268 mhd_assert ('"' == str[i]);
269 }
270 i++;
271 }
272 }
273 mhd_assert (str_len == i || ',' == str[i]);
274 if (str_len > i)
275 i++; /* Advance after ',' */
276 /* Skip all whitespaces before next parameter name */
277 while (i < str_len && (' ' == str[i] || '\t' == str[i]))
278 i++;
279 }
280
281 /* Postprocess values */
282 if ((NULL != userhash.value.str) && (0 != userhash.value.len))
283 {
284 const char *param_str;
285 size_t param_len;
286 char buf[5 * 2]; /* 5 is the length of "false" (longer then "true") */
287 if (! userhash.quoted)
288 {
289 param_str = userhash.value.str;
290 param_len = userhash.value.len;
291 }
292 else
293 {
294 if (sizeof(buf) / sizeof(buf[0]) >= userhash.value.len)
295 {
296 param_len = MHD_str_unquote (userhash.value.str, userhash.value.len,
297 buf);
298 param_str = buf;
299 }
300 else
301 param_len = 0;
302 }
303 if ((param_len == 4) && MHD_str_equal_caseless_bin_n_ (param_str, "true",
304 4))
305 pdauth->userhash = true;
306 else
307 pdauth->userhash = false;
308 }
309 else
310 pdauth->userhash = false;
311
312 return true;
313}
314
315
316#endif /* DAUTH_SUPPORT */
317
318
319/**
320 * Parse request "Authorization" header
321 * @param c the connection to process
322 * @return true if any supported Authorisation scheme were found,
323 * false if no "Authorization" header found, no supported scheme found,
324 * or an error occurred.
325 */
326_MHD_static_inline bool
327parse_auth_rq_header_ (struct MHD_Connection *c)
328{
329 const char *h; /**< The "Authorization" header */
330 size_t h_len;
331 struct MHD_AuthRqHeader *rq_auth;
332 size_t i;
333
334 mhd_assert (NULL == c->rq_auth);
335 mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= c->state);
336 if (MHD_CONNECTION_HEADERS_PROCESSED > c->state)
337 return false;
338
339 if (MHD_NO ==
340 MHD_lookup_connection_value_n (c, MHD_HEADER_KIND,
341 MHD_HTTP_HEADER_AUTHORIZATION,
342 MHD_STATICSTR_LEN_ ( \
343 MHD_HTTP_HEADER_AUTHORIZATION), &h,
344 &h_len))
345 {
346 rq_auth = (struct MHD_AuthRqHeader *)
347 MHD_connection_alloc_memory_ (c,
348 sizeof (struct MHD_AuthRqHeader));
349 c->rq_auth = rq_auth;
350 if (NULL != rq_auth)
351 {
352 memset (rq_auth, 0, sizeof(struct MHD_AuthRqHeader));
353 rq_auth->auth_type = MHD_AUTHTYPE_NONE;
354 }
355 return false;
356 }
357
358 rq_auth = NULL;
359 i = 0;
360 /* Skip the leading whitespace */
361 while (i < h_len)
362 {
363 const char ch = h[i];
364 if ((' ' != ch) && ('\t' != ch))
365 break;
366 i++;
367 }
368 h += i;
369 h_len -= i;
370
371#ifdef DAUTH_SUPPORT
372 if (1)
373 {
374 static const struct _MHD_cstr_w_len scheme_token =
375 _MHD_S_STR_W_LEN (_MHD_AUTH_DIGEST_BASE);
376
377 if ((scheme_token.len <= h_len) &&
378 MHD_str_equal_caseless_bin_n_ (h, scheme_token.str, scheme_token.len))
379 {
380 i = scheme_token.len;
381 /* RFC 7235 require only space after scheme token */
382 if ( (h_len <= i) ||
383 ((' ' == h[i]) || ('\t' == h[i])) ) /* Actually tab should NOT be allowed */
384 { /* Matched Digest authorisation scheme */
385 i++; /* Advance to the next char (even if it is beyond the end of the string) */
386
387 rq_auth = (struct MHD_AuthRqHeader *)
388 MHD_connection_alloc_memory_ (c,
389 sizeof (struct MHD_AuthRqHeader)
390 + sizeof (struct MHD_RqDAuth));
391 c->rq_auth = rq_auth;
392 if (NULL == rq_auth)
393 {
394#ifdef HAVE_MESSAGES
395 MHD_DLOG (c->daemon,
396 _ ("Failed to allocate memory in connection pool to " \
397 "process \"" MHD_HTTP_HEADER_AUTHORIZATION "\" " \
398 "header.\n"));
399#endif /* HAVE_MESSAGES */
400 return false;
401 }
402 memset (rq_auth, 0, sizeof (struct MHD_AuthRqHeader)
403 + sizeof (struct MHD_RqDAuth));
404 rq_auth->params.dauth = (struct MHD_RqDAuth *) (rq_auth + 1);
405
406 if (h_len > i)
407 {
408 if (! parse_dauth_params (h + i, h_len - i, rq_auth->params.dauth))
409 {
410 rq_auth->auth_type = MHD_AUTHTYPE_INVALID;
411 return false;
412 }
413 }
414
415 rq_auth->auth_type = MHD_AUTHTYPE_DIGEST;
416 return true;
417 }
418 }
419 }
420#endif /* DAUTH_SUPPORT */
421#ifdef BAUTH_SUPPORT
422 if (1)
423 {
424 static const struct _MHD_cstr_w_len scheme_token =
425 _MHD_S_STR_W_LEN (_MHD_AUTH_BASIC_BASE);
426
427 if ((scheme_token.len <= h_len) &&
428 MHD_str_equal_caseless_bin_n_ (h, scheme_token.str, scheme_token.len))
429 {
430 i = scheme_token.len;
431 /* RFC 7235 require only space after scheme token */
432 if ( (h_len <= i) ||
433 ((' ' == h[i]) || ('\t' == h[i])) ) /* Actually tab should NOT be allowed */
434 { /* Matched Basic authorisation scheme */
435 i++; /* Advance to the next char (even if it is beyond the end of the string) */
436
437 rq_auth = (struct MHD_AuthRqHeader *)
438 MHD_connection_alloc_memory_ (c,
439 sizeof (struct MHD_AuthRqHeader)
440 + sizeof (struct MHD_RqBAuth));
441 c->rq_auth = rq_auth;
442 if (NULL == rq_auth)
443 {
444#ifdef HAVE_MESSAGES
445 MHD_DLOG (c->daemon,
446 _ ("Failed to allocate memory in connection pool to " \
447 "process \"" MHD_HTTP_HEADER_AUTHORIZATION "\" " \
448 "header.\n"));
449#endif /* HAVE_MESSAGES */
450 return false;
451 }
452 memset (rq_auth, 0, sizeof (struct MHD_AuthRqHeader)
453 + sizeof (struct MHD_RqBAuth));
454 rq_auth->params.bauth = (struct MHD_RqBAuth *) (rq_auth + 1);
455
456 if (h_len > i)
457 {
458 if (! parse_bauth_params (h + i, h_len - i, rq_auth->params.bauth))
459 {
460 rq_auth->auth_type = MHD_AUTHTYPE_INVALID;
461 return false;
462 }
463 }
464
465 rq_auth->auth_type = MHD_AUTHTYPE_BASIC;
466 return true;
467 }
468 }
469 }
470#endif /* BAUTH_SUPPORT */
471
472 if (NULL == rq_auth)
473 rq_auth = (struct MHD_AuthRqHeader *)
474 MHD_connection_alloc_memory_ (c,
475 sizeof (struct MHD_AuthRqHeader));
476 c->rq_auth = rq_auth;
477 if (NULL != rq_auth)
478 {
479 memset (rq_auth, 0, sizeof(struct MHD_AuthRqHeader));
480 rq_auth->auth_type = MHD_AUTHTYPE_INVALID;
481 }
482 return false;
483}
484
485
486/**
487 * Return request's Authentication type and parameters.
488 *
489 * Function return result of parsing of the request's "Authorization" header or
490 * returns cached parsing result if the header was already parsed for
491 * the current request.
492 * @param connection the connection to process
493 * @return the pointer to structure with Authentication type and parameters,
494 * NULL if no memory in memory pool or if called too early (before
495 * header has been received).
496 */
497const struct MHD_AuthRqHeader *
498MHD_get_auth_rq_params_ (struct MHD_Connection *connection)
499{
500 mhd_assert (MHD_CONNECTION_HEADERS_PROCESSED <= connection->state);
501
502 if (NULL != connection->rq_auth)
503 return connection->rq_auth;
504
505 if (MHD_CONNECTION_HEADERS_PROCESSED > connection->state)
506 return NULL;
507
508 parse_auth_rq_header_ (connection);
509
510 return connection->rq_auth;
511}
diff --git a/src/microhttpd/gen_auth.h b/src/microhttpd/gen_auth.h
new file mode 100644
index 00000000..6be4eb4d
--- /dev/null
+++ b/src/microhttpd/gen_auth.h
@@ -0,0 +1,93 @@
1/*
2 This file is part of libmicrohttpd
3 Copyright (C) 2022 Evgeny Grin (Karlson2k)
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.
17 If not, see <http://www.gnu.org/licenses/>.
18*/
19
20/**
21 * @file microhttpd/gen_auth.h
22 * @brief Declarations for HTTP authorisation general functions
23 * @author Karlson2k (Evgeny Grin)
24 */
25
26#ifndef MHD_GET_AUTH_H
27#define MHD_GET_AUTH_H 1
28
29#include "mhd_options.h"
30#ifdef HAVE_STDBOOL_H
31#include <stdbool.h>
32#endif /* HAVE_STDBOOL_H */
33
34struct MHD_Connection; /* Forward declaration to avoid include of the large headers */
35
36/**
37 * Type of authorisation
38 */
39enum MHD_AuthType
40{
41 MHD_AUTHTYPE_NONE = 0,/**< No authorisation */
42 MHD_AUTHTYPE_BASIC, /**< Basic Authorisation, RFC 7617 */
43 MHD_AUTHTYPE_DIGEST, /**< Digest Authorisation, RFC 7616 */
44 MHD_AUTHTYPE_INVALID /**< Wrong/Unknown/Unsupported authorisation type */
45};
46
47#ifdef BAUTH_SUPPORT
48/* Forward declaration to avoid additional headers inclusion */
49struct MHD_RqBAuth;
50#endif /* BAUTH_SUPPORT */
51#ifdef DAUTH_SUPPORT
52/* Forward declaration to avoid additional headers inclusion */
53struct MHD_RqDAuth;
54#endif /* DAUTH_SUPPORT */
55
56/**
57 * Universal Authorisation Request parameters
58 */
59union MHD_AuthRqParams
60{
61#ifdef BAUTH_SUPPORT
62 struct MHD_RqBAuth *bauth;
63#endif /* BAUTH_SUPPORT */
64#ifdef DAUTH_SUPPORT
65 struct MHD_RqDAuth *dauth;
66#endif /* DAUTH_SUPPORT */
67};
68
69/**
70 * Request Authentication type and parameters
71 */
72struct MHD_AuthRqHeader
73{
74 enum MHD_AuthType auth_type;
75 union MHD_AuthRqParams params;
76};
77
78
79/**
80 * Return request's Authentication type and parameters.
81 *
82 * Function return result of parsing of the request's "Authorization" header or
83 * returns cached parsing result if the header was already parsed for
84 * the current request.
85 * @param connection the connection to process
86 * @return the pointer to structure with Authentication type and parameters,
87 * NULL if no memory in memory pool or if called too early (before
88 * header has been received).
89 */
90const struct MHD_AuthRqHeader *
91MHD_get_auth_rq_params_ (struct MHD_Connection *connection);
92
93#endif /* ! MHD_GET_AUTH_H */
diff --git a/src/microhttpd/internal.h b/src/microhttpd/internal.h
index 3dafe866..f6452b2c 100644
--- a/src/microhttpd/internal.h
+++ b/src/microhttpd/internal.h
@@ -778,6 +778,8 @@ typedef ssize_t
778 size_t max_bytes); 778 size_t max_bytes);
779 779
780 780
781struct MHD_AuthRqHeader; /* Forward declaration only */
782
781/** 783/**
782 * Ability to use same connection for next request 784 * Ability to use same connection for next request
783 */ 785 */
@@ -1034,6 +1036,15 @@ struct MHD_Connection
1034 */ 1036 */
1035 enum MHD_HTTP_Version http_ver; 1037 enum MHD_HTTP_Version http_ver;
1036 1038
1039#if defined(BAUTH_SUPPORT) || defined(DAUTH_SUPPORT)
1040 /**
1041 * Pointer to request authorization structure.
1042 * Allocated in pool.
1043 */
1044 const struct MHD_AuthRqHeader *rq_auth;
1045#endif
1046
1047
1037 /** 1048 /**
1038 * Close connection after sending response? 1049 * Close connection after sending response?
1039 * Functions may change value from "Unknown" or "KeepAlive" to "Must close", 1050 * Functions may change value from "Unknown" or "KeepAlive" to "Must close",
diff --git a/src/testcurl/test_digestauth_concurrent.c b/src/testcurl/test_digestauth_concurrent.c
index 045b673d..adfe172d 100644
--- a/src/testcurl/test_digestauth_concurrent.c
+++ b/src/testcurl/test_digestauth_concurrent.c
@@ -567,7 +567,7 @@ testDigestAuth (void)
567 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT)) 567 if (MHD_NO != MHD_is_feature_supported (MHD_FEATURE_AUTODETECT_BIND_PORT))
568 port = 0; 568 port = 0;
569 else 569 else
570 port = 1165; 570 port = 4200;
571 571
572 getRnd (rnd, sizeof(rnd)); 572 getRnd (rnd, sizeof(rnd));
573 573
diff --git a/w32/common/libmicrohttpd-files.vcxproj b/w32/common/libmicrohttpd-files.vcxproj
index 2fa2debc..391e1cec 100644
--- a/w32/common/libmicrohttpd-files.vcxproj
+++ b/w32/common/libmicrohttpd-files.vcxproj
@@ -6,6 +6,7 @@
6 <ClCompile Include="$(MhdSrc)microhttpd\connection.c" /> 6 <ClCompile Include="$(MhdSrc)microhttpd\connection.c" />
7 <ClCompile Include="$(MhdSrc)microhttpd\daemon.c" /> 7 <ClCompile Include="$(MhdSrc)microhttpd\daemon.c" />
8 <ClCompile Include="$(MhdSrc)microhttpd\digestauth.c" /> 8 <ClCompile Include="$(MhdSrc)microhttpd\digestauth.c" />
9 <ClCompile Include="$(MhdSrc)microhttpd\gen_auth.c" />
9 <ClCompile Include="$(MhdSrc)microhttpd\internal.c" /> 10 <ClCompile Include="$(MhdSrc)microhttpd\internal.c" />
10 <ClCompile Include="$(MhdSrc)microhttpd\md5.c" /> 11 <ClCompile Include="$(MhdSrc)microhttpd\md5.c" />
11 <ClCompile Include="$(MhdSrc)microhttpd\sha256.c" /> 12 <ClCompile Include="$(MhdSrc)microhttpd\sha256.c" />
@@ -35,6 +36,7 @@
35 <ClInclude Include="$(MhdSrc)microhttpd\basicauth.h" /> 36 <ClInclude Include="$(MhdSrc)microhttpd\basicauth.h" />
36 <ClInclude Include="$(MhdSrc)microhttpd\connection.h" /> 37 <ClInclude Include="$(MhdSrc)microhttpd\connection.h" />
37 <ClInclude Include="$(MhdSrc)microhttpd\digestauth.h" /> 38 <ClInclude Include="$(MhdSrc)microhttpd\digestauth.h" />
39 <ClInclude Include="$(MhdSrc)microhttpd\gen_auth.h" />
38 <ClInclude Include="$(MhdSrc)microhttpd\internal.h" /> 40 <ClInclude Include="$(MhdSrc)microhttpd\internal.h" />
39 <ClInclude Include="$(MhdSrc)microhttpd\md5.h" /> 41 <ClInclude Include="$(MhdSrc)microhttpd\md5.h" />
40 <ClInclude Include="$(MhdSrc)microhttpd\sha256.h" /> 42 <ClInclude Include="$(MhdSrc)microhttpd\sha256.h" />
diff --git a/w32/common/libmicrohttpd-filters.vcxproj b/w32/common/libmicrohttpd-filters.vcxproj
index 684f8beb..16c6b085 100644
--- a/w32/common/libmicrohttpd-filters.vcxproj
+++ b/w32/common/libmicrohttpd-filters.vcxproj
@@ -52,6 +52,9 @@
52 <ClInclude Include="$(MhdSrc)microhttpd\digestauth.h"> 52 <ClInclude Include="$(MhdSrc)microhttpd\digestauth.h">
53 <Filter>Internal Headers</Filter> 53 <Filter>Internal Headers</Filter>
54 </ClInclude> 54 </ClInclude>
55 <ClInclude Include="$(MhdSrc)microhttpd\gen_auth.h">
56 <Filter>Internal Headers</Filter>
57 </ClInclude>
55 <ClInclude Include="$(MhdSrc)microhttpd\internal.h"> 58 <ClInclude Include="$(MhdSrc)microhttpd\internal.h">
56 <Filter>Internal Headers</Filter> 59 <Filter>Internal Headers</Filter>
57 </ClInclude> 60 </ClInclude>
@@ -135,6 +138,9 @@
135 <ClCompile Include="$(MhdSrc)microhttpd\digestauth.c"> 138 <ClCompile Include="$(MhdSrc)microhttpd\digestauth.c">
136 <Filter>Source Files</Filter> 139 <Filter>Source Files</Filter>
137 </ClCompile> 140 </ClCompile>
141 <ClCompile Include="$(MhdSrc)microhttpd\gen_auth.c">
142 <Filter>Source Files</Filter>
143 </ClCompile>
138 <ClCompile Include="$(MhdSrc)microhttpd\internal.c"> 144 <ClCompile Include="$(MhdSrc)microhttpd\internal.c">
139 <Filter>Source Files</Filter> 145 <Filter>Source Files</Filter>
140 </ClCompile> 146 </ClCompile>