diff options
Diffstat (limited to 'src/microhttpd/digestauth.c')
-rw-r--r-- | src/microhttpd/digestauth.c | 816 |
1 files changed, 584 insertions, 232 deletions
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c index b6a22785..f8f06d6d 100644 --- a/src/microhttpd/digestauth.c +++ b/src/microhttpd/digestauth.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "platform.h" | 30 | #include "platform.h" |
31 | #include "mhd_limits.h" | 31 | #include "mhd_limits.h" |
32 | #include "internal.h" | 32 | #include "internal.h" |
33 | #include "response.h" | ||
33 | #include "md5.h" | 34 | #include "md5.h" |
34 | #include "sha256.h" | 35 | #include "sha256.h" |
35 | #include "mhd_mono_clock.h" | 36 | #include "mhd_mono_clock.h" |
@@ -158,6 +159,16 @@ | |||
158 | #define _MHD_SESS_TOKEN "-sess" | 159 | #define _MHD_SESS_TOKEN "-sess" |
159 | 160 | ||
160 | /** | 161 | /** |
162 | * The "auth" token for QOP | ||
163 | */ | ||
164 | #define MHD_TOKEN_AUTH_ "auth" | ||
165 | |||
166 | /** | ||
167 | * The "auth-int" token for QOP | ||
168 | */ | ||
169 | #define MHD_TOKEN_AUTH_INT_ "auth-int" | ||
170 | |||
171 | /** | ||
161 | * The required prefix of parameter with the extended notation | 172 | * The required prefix of parameter with the extended notation |
162 | */ | 173 | */ |
163 | #define MHD_DAUTH_EXT_PARAM_PREFIX "UTF-8'" | 174 | #define MHD_DAUTH_EXT_PARAM_PREFIX "UTF-8'" |
@@ -193,6 +204,73 @@ enum MHD_CheckNonceNC_ | |||
193 | 204 | ||
194 | 205 | ||
195 | /** | 206 | /** |
207 | * Get base hash calculation algorithm from #MHD_DigestAuthAlgo3 value. | ||
208 | * @param algo3 the MHD_DigestAuthAlgo3 value | ||
209 | * @return the base hash calculation algorithm | ||
210 | */ | ||
211 | _MHD_static_inline enum MHD_DigestBaseAlgo | ||
212 | get_base_digest_algo (enum MHD_DigestAuthAlgo3 algo3) | ||
213 | { | ||
214 | unsigned int base_algo; | ||
215 | |||
216 | base_algo = | ||
217 | ((unsigned int) algo3) | ||
218 | & ~((unsigned int) | ||
219 | (MHD_DIGEST_AUTH_ALGO3_NON_SESSION | ||
220 | | MHD_DIGEST_AUTH_ALGO3_NON_SESSION)); | ||
221 | return (enum MHD_DigestBaseAlgo) base_algo; | ||
222 | } | ||
223 | |||
224 | |||
225 | /** | ||
226 | * Get digest size for specified algorithm. | ||
227 | * | ||
228 | * Internal inline version. | ||
229 | * @param algo3 the algorithm to check | ||
230 | * @return the size of the digest or zero if the input value is not | ||
231 | * recognised/valid | ||
232 | */ | ||
233 | _MHD_static_inline size_t | ||
234 | digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) | ||
235 | { | ||
236 | mhd_assert (MHD_MD5_DIGEST_SIZE == MD5_DIGEST_SIZE); | ||
237 | mhd_assert (MHD_SHA256_DIGEST_SIZE == SHA256_DIGEST_SIZE); | ||
238 | /* Both MD5 and SHA-256 must not be specified at the same time */ | ||
239 | mhd_assert ( (0 == (((unsigned int) algo3) \ | ||
240 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5))) || \ | ||
241 | (0 == (((unsigned int) algo3) \ | ||
242 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256))) ); | ||
243 | if (0 != (((unsigned int) algo3) | ||
244 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5))) | ||
245 | return MHD_MD5_DIGEST_SIZE; | ||
246 | else if (0 != (((unsigned int) algo3) | ||
247 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256))) | ||
248 | return MHD_SHA256_DIGEST_SIZE; | ||
249 | |||
250 | return 0; /* Wrong input */ | ||
251 | } | ||
252 | |||
253 | |||
254 | /** | ||
255 | * Get digest size for specified algorithm. | ||
256 | * | ||
257 | * The size of the digest specifies the size of the userhash, userdigest | ||
258 | * and other parameters which size depends on used hash algorithm. | ||
259 | * @param algo3 the algorithm to check | ||
260 | * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or | ||
261 | * #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not | ||
262 | * recognised/valid | ||
263 | * @note Available since #MHD_VERSION 0x00097526 | ||
264 | * @ingroup authentication | ||
265 | */ | ||
266 | _MHD_EXTERN size_t | ||
267 | MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) | ||
268 | { | ||
269 | return digest_get_hash_size (algo3); | ||
270 | } | ||
271 | |||
272 | |||
273 | /** | ||
196 | * Digest context data | 274 | * Digest context data |
197 | */ | 275 | */ |
198 | union DigestCtx | 276 | union DigestCtx |
@@ -1904,18 +1982,22 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam *param, | |||
1904 | /** | 1982 | /** |
1905 | * Authenticates the authorization header sent by the client | 1983 | * Authenticates the authorization header sent by the client |
1906 | * | 1984 | * |
1907 | * @param connection The MHD connection structure | 1985 | * @param connection the MHD connection structure |
1908 | * @param[in,out] da digest algorithm to use for checking (written to as | 1986 | * @param realm the realm presented to the client |
1909 | * part of the calculations, but the values left in the struct | 1987 | * @param username the username needs to be authenticated |
1910 | * are not actually expected to be useful for the caller) | 1988 | * @param password the password used in the authentication |
1911 | * @param realm The realm presented to the client | 1989 | * @param userdigest the optional precalculated binary hash of the string |
1912 | * @param username The username needs to be authenticated | 1990 | * "username:realm:password" |
1913 | * @param password The password used in the authentication | 1991 | * @param nonce_timeout the period of seconds since nonce generation, when |
1914 | * @param digest An optional binary hash | 1992 | * the nonce is recognised as valid and not stale. |
1915 | * of the precalculated hash value "username:realm:password" | 1993 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
1916 | * (must contain "digest_get_size(da)" bytes or be NULL) | 1994 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is |
1917 | * @param nonce_timeout The amount of time for a nonce to be | 1995 | * returned; |
1918 | * invalid in seconds | 1996 | * zero for no limit |
1997 | * @param mqop the QOP to use, currently the only allowed value is | ||
1998 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
1999 | * @param malgo3 digest algorithms to use, if several algorithms are specified | ||
2000 | * then MD5 is used (if allowed) | ||
1919 | * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, | 2001 | * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, |
1920 | * to be free if not NULL upon return | 2002 | * to be free if not NULL upon return |
1921 | * @return #MHD_DAUTH_OK if authenticated, | 2003 | * @return #MHD_DAUTH_OK if authenticated, |
@@ -1924,16 +2006,20 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam *param, | |||
1924 | */ | 2006 | */ |
1925 | static enum MHD_DigestAuthResult | 2007 | static enum MHD_DigestAuthResult |
1926 | digest_auth_check_all_inner (struct MHD_Connection *connection, | 2008 | digest_auth_check_all_inner (struct MHD_Connection *connection, |
1927 | struct DigestAlgorithm *da, | ||
1928 | const char *realm, | 2009 | const char *realm, |
1929 | const char *username, | 2010 | const char *username, |
1930 | const char *password, | 2011 | const char *password, |
1931 | const uint8_t *digest, | 2012 | const uint8_t *userdigest, |
1932 | unsigned int nonce_timeout, | 2013 | unsigned int nonce_timeout, |
2014 | uint32_t max_nc, | ||
2015 | enum MHD_DigestAuthMultiQOP mqop, | ||
2016 | enum MHD_DigestAuthMultiAlgo3 malgo3, | ||
1933 | char **pbuf) | 2017 | char **pbuf) |
1934 | { | 2018 | { |
1935 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); | 2019 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); |
1936 | const unsigned int digest_size = digest_get_size (da); | 2020 | enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */ |
2021 | struct DigestAlgorithm da; | ||
2022 | unsigned int digest_size; | ||
1937 | uint8_t hash1_bin[MAX_DIGEST]; | 2023 | uint8_t hash1_bin[MAX_DIGEST]; |
1938 | uint8_t hash2_bin[MAX_DIGEST]; | 2024 | uint8_t hash2_bin[MAX_DIGEST]; |
1939 | #if 0 | 2025 | #if 0 |
@@ -1960,6 +2046,28 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
1960 | if (NULL == params) | 2046 | if (NULL == params) |
1961 | return MHD_DAUTH_WRONG_HEADER; | 2047 | return MHD_DAUTH_WRONG_HEADER; |
1962 | 2048 | ||
2049 | /* ** Initial parameters checks and setup ** */ | ||
2050 | if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop) | ||
2051 | MHD_PANIC (_ ("Wrong 'mqop' value, API violation")); | ||
2052 | |||
2053 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION)) | ||
2054 | { | ||
2055 | #ifdef HAVE_MESSAGES | ||
2056 | MHD_DLOG (connection->daemon, | ||
2057 | _ ("The 'session' algorithms are not supported.\n")); | ||
2058 | #endif /* HAVE_MESSAGES */ | ||
2059 | return MHD_DAUTH_WRONG_ALGO; | ||
2060 | } | ||
2061 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) | ||
2062 | s_algo = MHD_DIGEST_AUTH_ALGO3_MD5; | ||
2063 | else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2064 | s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256; | ||
2065 | else | ||
2066 | MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); | ||
2067 | if (! digest_setup (&da, get_base_digest_algo (s_algo))) | ||
2068 | MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); | ||
2069 | digest_size = digest_get_size (&da); | ||
2070 | |||
1963 | /* ** A quick check for presence of all required parameters ** */ | 2071 | /* ** A quick check for presence of all required parameters ** */ |
1964 | 2072 | ||
1965 | if ((NULL == params->username.value.str) && | 2073 | if ((NULL == params->username.value.str) && |
@@ -1980,7 +2088,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
1980 | 2088 | ||
1981 | if (NULL == params->realm.value.str) | 2089 | if (NULL == params->realm.value.str) |
1982 | return MHD_DAUTH_WRONG_HEADER; | 2090 | return MHD_DAUTH_WRONG_HEADER; |
1983 | else if (((NULL == digest) || params->userhash) && | 2091 | else if (((NULL == userdigest) || params->userhash) && |
1984 | (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len)) | 2092 | (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len)) |
1985 | return MHD_DAUTH_TOO_LARGE; /* Realm is too large and it will be used in hash calculations */ | 2093 | return MHD_DAUTH_TOO_LARGE; /* Realm is too large and it will be used in hash calculations */ |
1986 | 2094 | ||
@@ -2029,16 +2137,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2029 | /* ** Check simple parameters match ** */ | 2137 | /* ** Check simple parameters match ** */ |
2030 | 2138 | ||
2031 | /* Check 'algorithm' */ | 2139 | /* Check 'algorithm' */ |
2032 | if (1) | 2140 | /* The 'algorithm' was checked at the start of the function */ |
2033 | { | ||
2034 | const enum MHD_DigestAuthAlgo3 r_algo = get_rq_algo (params); | ||
2035 | const enum MHD_DigestBaseAlgo p_algo = da->algo; | ||
2036 | if ( (! ((MHD_DIGEST_AUTH_ALGO3_MD5 == r_algo) && | ||
2037 | (MHD_DIGEST_BASE_ALGO_MD5 == p_algo))) && | ||
2038 | (! ((MHD_DIGEST_AUTH_ALGO3_SHA256 == r_algo) && | ||
2039 | (MHD_DIGEST_BASE_ALGO_SHA256 == p_algo))) ) | ||
2040 | return MHD_DAUTH_WRONG_ALGO; | ||
2041 | } | ||
2042 | /* 'algorithm' valid */ | 2141 | /* 'algorithm' valid */ |
2043 | 2142 | ||
2044 | /* Check 'qop' */ | 2143 | /* Check 'qop' */ |
@@ -2089,11 +2188,11 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2089 | else | 2188 | else |
2090 | { /* Userhash */ | 2189 | { /* Userhash */ |
2091 | mhd_assert (NULL != params->username.value.str); | 2190 | mhd_assert (NULL != params->username.value.str); |
2092 | digest_init (da); | 2191 | digest_init (&da); |
2093 | digest_update (da, username, username_len); | 2192 | digest_update (&da, username, username_len); |
2094 | digest_update_with_colon (da); | 2193 | digest_update_with_colon (&da); |
2095 | digest_update (da, realm, realm_len); | 2194 | digest_update (&da, realm, realm_len); |
2096 | digest_calc_hash (da, hash1_bin); | 2195 | digest_calc_hash (&da, hash1_bin); |
2097 | mhd_assert (sizeof (tmp1) >= (2 * digest_size + 1)); | 2196 | mhd_assert (sizeof (tmp1) >= (2 * digest_size + 1)); |
2098 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); | 2197 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); |
2099 | if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) | 2198 | if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) |
@@ -2127,7 +2226,10 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2127 | #endif | 2226 | #endif |
2128 | return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ | 2227 | return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ |
2129 | } | 2228 | } |
2229 | if ((0 != max_nc) && (max_nc < nci)) | ||
2230 | return MHD_DAUTH_NONCE_STALE; /* Too large 'nc' value */ | ||
2130 | /* Got 'nc' digital value */ | 2231 | /* Got 'nc' digital value */ |
2232 | |||
2131 | /* Get 'nonce' with basic checks */ | 2233 | /* Get 'nonce' with basic checks */ |
2132 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, | 2234 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, |
2133 | &unquoted); | 2235 | &unquoted); |
@@ -2183,7 +2285,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2183 | { | 2285 | { |
2184 | #ifdef HAVE_MESSAGES | 2286 | #ifdef HAVE_MESSAGES |
2185 | MHD_DLOG (daemon, | 2287 | MHD_DLOG (daemon, |
2186 | _ ("Received nonce that technically valid, but was not " | 2288 | _ ("Received nonce that was not " |
2187 | "generated by MHD. This may indicate an attack attempt.\n")); | 2289 | "generated by MHD. This may indicate an attack attempt.\n")); |
2188 | #endif | 2290 | #endif |
2189 | return MHD_DAUTH_NONCE_WRONG; | 2291 | return MHD_DAUTH_NONCE_WRONG; |
@@ -2196,9 +2298,9 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2196 | /* ** Build H(A2) and check URI match in the header and in the request ** */ | 2298 | /* ** Build H(A2) and check URI match in the header and in the request ** */ |
2197 | 2299 | ||
2198 | /* Get 'uri' */ | 2300 | /* Get 'uri' */ |
2199 | digest_init (da); | 2301 | digest_init (&da); |
2200 | digest_update_str (da, connection->method); | 2302 | digest_update_str (&da, connection->method); |
2201 | digest_update_with_colon (da); | 2303 | digest_update_with_colon (&da); |
2202 | #if 0 | 2304 | #if 0 |
2203 | /* TODO: add support for "auth-int" */ | 2305 | /* TODO: add support for "auth-int" */ |
2204 | digest_update_str (da, hentity); | 2306 | digest_update_str (da, hentity); |
@@ -2209,37 +2311,37 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2209 | if (_MHD_UNQ_OK != unq_res) | 2311 | if (_MHD_UNQ_OK != unq_res) |
2210 | return MHD_DAUTH_ERROR; | 2312 | return MHD_DAUTH_ERROR; |
2211 | 2313 | ||
2212 | digest_update (da, unq_copy.str, unq_copy.len); | 2314 | digest_update (&da, unq_copy.str, unq_copy.len); |
2213 | /* The next check will modify copied URI string */ | 2315 | /* The next check will modify copied URI string */ |
2214 | if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) | 2316 | if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) |
2215 | return MHD_DAUTH_WRONG_URI; | 2317 | return MHD_DAUTH_WRONG_URI; |
2216 | digest_calc_hash (da, hash2_bin); | 2318 | digest_calc_hash (&da, hash2_bin); |
2217 | /* Got H(A2) */ | 2319 | /* Got H(A2) */ |
2218 | 2320 | ||
2219 | /* ** Build H(A1) ** */ | 2321 | /* ** Build H(A1) ** */ |
2220 | if (NULL == digest) | 2322 | if (NULL == userdigest) |
2221 | { | 2323 | { |
2222 | digest_init (da); | 2324 | digest_init (&da); |
2223 | digest_update (da, (const uint8_t *) username, username_len); | 2325 | digest_update (&da, (const uint8_t *) username, username_len); |
2224 | digest_update_with_colon (da); | 2326 | digest_update_with_colon (&da); |
2225 | digest_update (da, (const uint8_t *) realm, realm_len); | 2327 | digest_update (&da, (const uint8_t *) realm, realm_len); |
2226 | digest_update_with_colon (da); | 2328 | digest_update_with_colon (&da); |
2227 | digest_update_str (da, password); | 2329 | digest_update_str (&da, password); |
2228 | digest_calc_hash (da, hash1_bin); | 2330 | digest_calc_hash (&da, hash1_bin); |
2229 | } | 2331 | } |
2230 | /* TODO: support '-sess' versions */ | 2332 | /* TODO: support '-sess' versions */ |
2231 | /* Got H(A1) */ | 2333 | /* Got H(A1) */ |
2232 | 2334 | ||
2233 | /* ** Check 'response' ** */ | 2335 | /* ** Check 'response' ** */ |
2234 | 2336 | ||
2235 | digest_init (da); | 2337 | digest_init (&da); |
2236 | /* Update digest with H(A1) */ | 2338 | /* Update digest with H(A1) */ |
2237 | mhd_assert (sizeof (tmp1) >= (digest_size * 2 + 1)); | 2339 | mhd_assert (sizeof (tmp1) >= (digest_size * 2 + 1)); |
2238 | if (NULL == digest) | 2340 | if (NULL == userdigest) |
2239 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); | 2341 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); |
2240 | else | 2342 | else |
2241 | MHD_bin_to_hex (digest, digest_size, tmp1); | 2343 | MHD_bin_to_hex (userdigest, digest_size, tmp1); |
2242 | digest_update (da, (const uint8_t *) tmp1, digest_size * 2); | 2344 | digest_update (&da, (const uint8_t *) tmp1, digest_size * 2); |
2243 | 2345 | ||
2244 | /* H(A1) is not needed anymore, reuse the buffer. | 2346 | /* H(A1) is not needed anymore, reuse the buffer. |
2245 | * Use hash1_bin for the client's 'response' decoded to binary form. */ | 2347 | * Use hash1_bin for the client's 'response' decoded to binary form. */ |
@@ -2251,46 +2353,46 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2251 | return MHD_DAUTH_RESPONSE_WRONG; | 2353 | return MHD_DAUTH_RESPONSE_WRONG; |
2252 | 2354 | ||
2253 | /* Update digest with ':' */ | 2355 | /* Update digest with ':' */ |
2254 | digest_update_with_colon (da); | 2356 | digest_update_with_colon (&da); |
2255 | /* Update digest with 'nonce' text value */ | 2357 | /* Update digest with 'nonce' text value */ |
2256 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, | 2358 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, |
2257 | &unquoted); | 2359 | &unquoted); |
2258 | if (_MHD_UNQ_OK != unq_res) | 2360 | if (_MHD_UNQ_OK != unq_res) |
2259 | return MHD_DAUTH_ERROR; | 2361 | return MHD_DAUTH_ERROR; |
2260 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2362 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2261 | /* Update digest with ':' */ | 2363 | /* Update digest with ':' */ |
2262 | digest_update_with_colon (da); | 2364 | digest_update_with_colon (&da); |
2263 | /* Update digest with 'nc' text value */ | 2365 | /* Update digest with 'nc' text value */ |
2264 | unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, | 2366 | unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, |
2265 | &unquoted); | 2367 | &unquoted); |
2266 | if (_MHD_UNQ_OK != unq_res) | 2368 | if (_MHD_UNQ_OK != unq_res) |
2267 | return MHD_DAUTH_ERROR; | 2369 | return MHD_DAUTH_ERROR; |
2268 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2370 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2269 | /* Update digest with ':' */ | 2371 | /* Update digest with ':' */ |
2270 | digest_update_with_colon (da); | 2372 | digest_update_with_colon (&da); |
2271 | /* Update digest with 'cnonce' value */ | 2373 | /* Update digest with 'cnonce' value */ |
2272 | unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, | 2374 | unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, |
2273 | &unquoted); | 2375 | &unquoted); |
2274 | if (_MHD_UNQ_OK != unq_res) | 2376 | if (_MHD_UNQ_OK != unq_res) |
2275 | return MHD_DAUTH_ERROR; | 2377 | return MHD_DAUTH_ERROR; |
2276 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2378 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2277 | /* Update digest with ':' */ | 2379 | /* Update digest with ':' */ |
2278 | digest_update_with_colon (da); | 2380 | digest_update_with_colon (&da); |
2279 | /* Update digest with 'qop' value */ | 2381 | /* Update digest with 'qop' value */ |
2280 | unq_res = get_unquoted_param (¶ms->qop, tmp1, ptmp2, &tmp2_size, | 2382 | unq_res = get_unquoted_param (¶ms->qop, tmp1, ptmp2, &tmp2_size, |
2281 | &unquoted); | 2383 | &unquoted); |
2282 | if (_MHD_UNQ_OK != unq_res) | 2384 | if (_MHD_UNQ_OK != unq_res) |
2283 | return MHD_DAUTH_ERROR; | 2385 | return MHD_DAUTH_ERROR; |
2284 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2386 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2285 | /* Update digest with ':' */ | 2387 | /* Update digest with ':' */ |
2286 | digest_update_with_colon (da); | 2388 | digest_update_with_colon (&da); |
2287 | /* Update digest with H(A2) */ | 2389 | /* Update digest with H(A2) */ |
2288 | MHD_bin_to_hex (hash2_bin, digest_size, tmp1); | 2390 | MHD_bin_to_hex (hash2_bin, digest_size, tmp1); |
2289 | digest_update (da, (const uint8_t *) tmp1, digest_size * 2); | 2391 | digest_update (&da, (const uint8_t *) tmp1, digest_size * 2); |
2290 | 2392 | ||
2291 | /* H(A2) is not needed anymore, reuse the buffer. | 2393 | /* H(A2) is not needed anymore, reuse the buffer. |
2292 | * Use hash2_bin for the calculated response in binary form */ | 2394 | * Use hash2_bin for the calculated response in binary form */ |
2293 | digest_calc_hash (da, hash2_bin); | 2395 | digest_calc_hash (&da, hash2_bin); |
2294 | 2396 | ||
2295 | if (0 != memcmp (hash1_bin, hash2_bin, digest_size)) | 2397 | if (0 != memcmp (hash1_bin, hash2_bin, digest_size)) |
2296 | return MHD_DAUTH_RESPONSE_WRONG; | 2398 | return MHD_DAUTH_RESPONSE_WRONG; |
@@ -2307,7 +2409,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2307 | connection->headers_received, | 2409 | connection->headers_received, |
2308 | realm, | 2410 | realm, |
2309 | realm_len, | 2411 | realm_len, |
2310 | da, | 2412 | &da, |
2311 | tmp1); | 2413 | tmp1); |
2312 | 2414 | ||
2313 | if (! is_param_equal (¶ms->nonce, tmp1, | 2415 | if (! is_param_equal (¶ms->nonce, tmp1, |
@@ -2322,37 +2424,46 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2322 | /** | 2424 | /** |
2323 | * Authenticates the authorization header sent by the client | 2425 | * Authenticates the authorization header sent by the client |
2324 | * | 2426 | * |
2325 | * @param connection The MHD connection structure | 2427 | * @param connection the MHD connection structure |
2326 | * @param[in,out] da digest algorithm to use for checking (written to as | 2428 | * @param realm the realm presented to the client |
2327 | * part of the calculations, but the values left in the struct | 2429 | * @param username the username needs to be authenticated |
2328 | * are not actually expected to be useful for the caller) | 2430 | * @param password the password used in the authentication |
2329 | * @param realm The realm presented to the client | 2431 | * @param userdigest the optional precalculated binary hash of the string |
2330 | * @param username The username needs to be authenticated | 2432 | * "username:realm:password" |
2331 | * @param password The password used in the authentication | 2433 | * @param nonce_timeout the period of seconds since nonce generation, when |
2332 | * @param digest An optional binary hash | 2434 | * the nonce is recognised as valid and not stale. |
2333 | * of the precalculated hash value "username:realm:password" | 2435 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
2334 | * (must contain "digest_get_size(da)" bytes or be NULL) | 2436 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is |
2335 | * @param nonce_timeout The amount of time for a nonce to be | 2437 | * returned; |
2336 | * invalid in seconds | 2438 | * zero for no limit |
2439 | * @param mqop the QOP to use, currently the only allowed value is | ||
2440 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2441 | * @param malgo3 digest algorithms to use, if several algorithms are specified | ||
2442 | * then MD5 is used (if allowed) | ||
2337 | * @return #MHD_DAUTH_OK if authenticated, | 2443 | * @return #MHD_DAUTH_OK if authenticated, |
2338 | * error code otherwise. | 2444 | * error code otherwise. |
2339 | * @ingroup authentication | 2445 | * @ingroup authentication |
2340 | */ | 2446 | */ |
2341 | static enum MHD_DigestAuthResult | 2447 | static enum MHD_DigestAuthResult |
2342 | digest_auth_check_all (struct MHD_Connection *connection, | 2448 | digest_auth_check_all (struct MHD_Connection *connection, |
2343 | struct DigestAlgorithm *da, | ||
2344 | const char *realm, | 2449 | const char *realm, |
2345 | const char *username, | 2450 | const char *username, |
2346 | const char *password, | 2451 | const char *password, |
2347 | const uint8_t *digest, | 2452 | const uint8_t *userdigest, |
2348 | unsigned int nonce_timeout) | 2453 | unsigned int nonce_timeout, |
2454 | uint32_t max_nc, | ||
2455 | enum MHD_DigestAuthMultiQOP mqop, | ||
2456 | enum MHD_DigestAuthMultiAlgo3 malgo3) | ||
2349 | { | 2457 | { |
2350 | enum MHD_DigestAuthResult res; | 2458 | enum MHD_DigestAuthResult res; |
2351 | char *buf; | 2459 | char *buf; |
2352 | 2460 | ||
2353 | buf = NULL; | 2461 | buf = NULL; |
2354 | res = digest_auth_check_all_inner (connection, da, realm, username, password, | 2462 | res = digest_auth_check_all_inner (connection, realm, username, password, |
2355 | digest, nonce_timeout, &buf); | 2463 | userdigest, |
2464 | nonce_timeout, | ||
2465 | max_nc, mqop, malgo3, | ||
2466 | &buf); | ||
2356 | if (NULL != buf) | 2467 | if (NULL != buf) |
2357 | free (buf); | 2468 | free (buf); |
2358 | 2469 | ||
@@ -2402,10 +2513,17 @@ MHD_digest_auth_check (struct MHD_Connection *connection, | |||
2402 | * @param username the username needs to be authenticated | 2513 | * @param username the username needs to be authenticated |
2403 | * @param password the password used in the authentication | 2514 | * @param password the password used in the authentication |
2404 | * @param nonce_timeout the nonce validity duration in seconds | 2515 | * @param nonce_timeout the nonce validity duration in seconds |
2405 | * @param algo the digest algorithms allowed for verification | 2516 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
2517 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is | ||
2518 | * returned; | ||
2519 | * zero for no limit | ||
2520 | * @param mqop the QOP to use, currently the only allowed value is | ||
2521 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2522 | * @param malgo3 digest algorithm to use, if several algorithms are specified | ||
2523 | * then MD5 is used (if allowed) | ||
2406 | * @return #MHD_DAUTH_OK if authenticated, | 2524 | * @return #MHD_DAUTH_OK if authenticated, |
2407 | * the error code otherwise | 2525 | * the error code otherwise |
2408 | * @note Available since #MHD_VERSION 0x00097513 | 2526 | * @note Available since #MHD_VERSION 0x00097526 |
2409 | * @ingroup authentication | 2527 | * @ingroup authentication |
2410 | */ | 2528 | */ |
2411 | _MHD_EXTERN enum MHD_DigestAuthResult | 2529 | _MHD_EXTERN enum MHD_DigestAuthResult |
@@ -2414,87 +2532,84 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
2414 | const char *username, | 2532 | const char *username, |
2415 | const char *password, | 2533 | const char *password, |
2416 | unsigned int nonce_timeout, | 2534 | unsigned int nonce_timeout, |
2417 | enum MHD_DigestAuthAlgorithm algo) | 2535 | uint32_t max_nc, |
2536 | enum MHD_DigestAuthMultiQOP mqop, | ||
2537 | enum MHD_DigestAuthMultiAlgo3 malgo3) | ||
2418 | { | 2538 | { |
2419 | struct DigestAlgorithm da; | ||
2420 | |||
2421 | mhd_assert (NULL != password); | 2539 | mhd_assert (NULL != password); |
2422 | 2540 | ||
2423 | if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | ||
2424 | { | ||
2425 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5)) | ||
2426 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2427 | } | ||
2428 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
2429 | { | ||
2430 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2431 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2432 | } | ||
2433 | else | ||
2434 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | ||
2435 | |||
2436 | return digest_auth_check_all (connection, | 2541 | return digest_auth_check_all (connection, |
2437 | &da, | ||
2438 | realm, | 2542 | realm, |
2439 | username, | 2543 | username, |
2440 | password, | 2544 | password, |
2441 | NULL, | 2545 | NULL, |
2442 | nonce_timeout); | 2546 | nonce_timeout, |
2547 | max_nc, | ||
2548 | mqop, | ||
2549 | malgo3); | ||
2443 | } | 2550 | } |
2444 | 2551 | ||
2445 | 2552 | ||
2446 | /** | 2553 | /** |
2447 | * Authenticates the authorization header sent by the client. | 2554 | * Authenticates the authorization header sent by the client by using |
2555 | * hash of "username:realm:password". | ||
2448 | * | 2556 | * |
2449 | * @param connection the MHD connection structure | 2557 | * @param connection the MHD connection structure |
2450 | * @param realm the realm to be used for authorization of the client | 2558 | * @param realm the realm presented to the client |
2451 | * @param username the username needs to be authenticated | 2559 | * @param username the username needs to be authenticated |
2452 | * @param digest the pointer to the binary digest for the precalculated hash | 2560 | * @param userdigest the precalculated binary hash of the string |
2453 | * value "username:realm:password" with specified @a algo | 2561 | * "username:realm:password" |
2454 | * @param digest_size the number of bytes in @a digest (the size must match | 2562 | * @param userdigest_size the size of the @a userdigest in bytes, must match the |
2455 | * @a algo!) | 2563 | * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, |
2456 | * @param nonce_timeout the nonce validity duration in seconds | 2564 | * #MHD_SHA256_DIGEST_SIZE) |
2457 | * @param algo digest algorithms allowed for verification | 2565 | * @param nonce_timeout the period of seconds since nonce generation, when |
2566 | * the nonce is recognised as valid and not stale. | ||
2567 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc | ||
2568 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is | ||
2569 | * returned; | ||
2570 | * zero for no limit | ||
2571 | * @param mqop the QOP to use, currently the only allowed value is | ||
2572 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2573 | * @param malgo3 the digest algorithms to use; both MD5-based and SHA-256-based | ||
2574 | * algorithms cannot be used at the same time for this function | ||
2575 | * as @a userdigest_size must match specified algorithm | ||
2458 | * @return #MHD_DAUTH_OK if authenticated, | 2576 | * @return #MHD_DAUTH_OK if authenticated, |
2459 | * the error code otherwise | 2577 | * the error code otherwise |
2460 | * @note Available since #MHD_VERSION 0x00097513 | 2578 | * @note Available since #MHD_VERSION 0x00097526 |
2461 | * @ingroup authentication | 2579 | * @ingroup authentication |
2462 | */ | 2580 | */ |
2463 | _MHD_EXTERN enum MHD_DigestAuthResult | 2581 | _MHD_EXTERN enum MHD_DigestAuthResult |
2464 | MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, | 2582 | MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, |
2465 | const char *realm, | 2583 | const char *realm, |
2466 | const char *username, | 2584 | const char *username, |
2467 | const uint8_t *digest, | 2585 | const void *userdigest, |
2468 | size_t digest_size, | 2586 | size_t userdigest_size, |
2469 | unsigned int nonce_timeout, | 2587 | unsigned int nonce_timeout, |
2470 | enum MHD_DigestAuthAlgorithm algo) | 2588 | uint32_t max_nc, |
2589 | enum MHD_DigestAuthMultiQOP mqop, | ||
2590 | enum MHD_DigestAuthMultiAlgo3 malgo3) | ||
2471 | { | 2591 | { |
2472 | struct DigestAlgorithm da; | 2592 | if (((unsigned int) (MHD_DIGEST_BASE_ALGO_MD5 |
2473 | 2593 | | MHD_DIGEST_BASE_ALGO_SHA256)) == | |
2474 | mhd_assert (NULL != digest); | 2594 | (((unsigned int) malgo3) & (MHD_DIGEST_BASE_ALGO_MD5 |
2475 | if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | 2595 | | MHD_DIGEST_BASE_ALGO_SHA256))) |
2476 | { | 2596 | MHD_PANIC (_ ("Wrong 'malgo3' value, both MD5 and SHA-256 specified, " |
2477 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5)) | 2597 | "API violation")); |
2478 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | 2598 | |
2479 | } | 2599 | if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) != |
2480 | else if (MHD_DIGEST_ALG_SHA256 == algo) | 2600 | userdigest_size) |
2481 | { | 2601 | MHD_PANIC (_ ("Wrong 'userdigest_size' value, not matching 'malgo3, " |
2482 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256)) | 2602 | "API violation")); |
2483 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2484 | } | ||
2485 | else | ||
2486 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | ||
2487 | |||
2488 | if (digest_get_size (&da) != digest_size) | ||
2489 | MHD_PANIC (_ ("Digest size mismatch.\n")); /* API violation! */ | ||
2490 | 2603 | ||
2491 | return digest_auth_check_all (connection, | 2604 | return digest_auth_check_all (connection, |
2492 | &da, | ||
2493 | realm, | 2605 | realm, |
2494 | username, | 2606 | username, |
2495 | NULL, | 2607 | NULL, |
2496 | digest, | 2608 | (const uint8_t *) userdigest, |
2497 | nonce_timeout); | 2609 | nonce_timeout, |
2610 | max_nc, | ||
2611 | mqop, | ||
2612 | malgo3); | ||
2498 | } | 2613 | } |
2499 | 2614 | ||
2500 | 2615 | ||
@@ -2523,12 +2638,24 @@ MHD_digest_auth_check2 (struct MHD_Connection *connection, | |||
2523 | enum MHD_DigestAuthAlgorithm algo) | 2638 | enum MHD_DigestAuthAlgorithm algo) |
2524 | { | 2639 | { |
2525 | enum MHD_DigestAuthResult res; | 2640 | enum MHD_DigestAuthResult res; |
2641 | enum MHD_DigestAuthMultiAlgo3 malgo3; | ||
2642 | |||
2643 | if (MHD_DIGEST_ALG_AUTO == algo) | ||
2644 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; | ||
2645 | else if (MHD_DIGEST_ALG_MD5 == algo) | ||
2646 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; | ||
2647 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
2648 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; | ||
2649 | else | ||
2650 | MHD_PANIC (_ ("Wrong 'algo' value, API violation")); | ||
2651 | |||
2526 | res = MHD_digest_auth_check3 (connection, | 2652 | res = MHD_digest_auth_check3 (connection, |
2527 | realm, | 2653 | realm, |
2528 | username, | 2654 | username, |
2529 | password, | 2655 | password, |
2530 | nonce_timeout, | 2656 | nonce_timeout, |
2531 | algo); | 2657 | 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, |
2658 | malgo3); | ||
2532 | if (MHD_DAUTH_OK == res) | 2659 | if (MHD_DAUTH_OK == res) |
2533 | return MHD_YES; | 2660 | return MHD_YES; |
2534 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) | 2661 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) |
@@ -2567,6 +2694,16 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection *connection, | |||
2567 | enum MHD_DigestAuthAlgorithm algo) | 2694 | enum MHD_DigestAuthAlgorithm algo) |
2568 | { | 2695 | { |
2569 | enum MHD_DigestAuthResult res; | 2696 | enum MHD_DigestAuthResult res; |
2697 | enum MHD_DigestAuthMultiAlgo3 malgo3; | ||
2698 | |||
2699 | if (MHD_DIGEST_ALG_AUTO == algo) | ||
2700 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; | ||
2701 | else if (MHD_DIGEST_ALG_MD5 == algo) | ||
2702 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; | ||
2703 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
2704 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; | ||
2705 | else | ||
2706 | MHD_PANIC (_ ("Wrong 'algo' value, API violation")); | ||
2570 | 2707 | ||
2571 | res = MHD_digest_auth_check_digest3 (connection, | 2708 | res = MHD_digest_auth_check_digest3 (connection, |
2572 | realm, | 2709 | realm, |
@@ -2574,7 +2711,8 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection *connection, | |||
2574 | digest, | 2711 | digest, |
2575 | digest_size, | 2712 | digest_size, |
2576 | nonce_timeout, | 2713 | nonce_timeout, |
2577 | algo); | 2714 | 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, |
2715 | malgo3); | ||
2578 | if (MHD_DAUTH_OK == res) | 2716 | if (MHD_DAUTH_OK == res) |
2579 | return MHD_YES; | 2717 | return MHD_YES; |
2580 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) | 2718 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) |
@@ -2622,47 +2760,94 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection, | |||
2622 | /** | 2760 | /** |
2623 | * Queues a response to request authentication from the client | 2761 | * Queues a response to request authentication from the client |
2624 | * | 2762 | * |
2625 | * @param connection The MHD connection structure | 2763 | * This function modifies provided @a response. The @a response must not be |
2764 | * reused and should be destroyed (by #MHD_destroy_response()) after call of | ||
2765 | * this function. | ||
2766 | * | ||
2767 | * @param connection the MHD connection structure | ||
2626 | * @param realm the realm presented to the client | 2768 | * @param realm the realm presented to the client |
2627 | * @param opaque string to user for opaque value | 2769 | * @param opaque the string for opaque value, can be NULL, but NULL is |
2628 | * @param response reply to send; should contain the "access denied" | 2770 | * not recommended for better compatibility with clients |
2629 | * body; note that this function will set the "WWW Authenticate" | 2771 | * @param domain the optional space-separated list of URIs for which the |
2630 | * header and that the caller should not do this; the NULL is tolerated | 2772 | * same authorisation could be used, URIs can be in form |
2631 | * @param signal_stale #MHD_YES if the nonce is stale to add | 2773 | * "path-absolute" (the path for the same host with initial slash) |
2632 | * 'stale=true' to the authentication header | 2774 | * or in form "absolute-URI" (the full path with protocol), in |
2633 | * @param algo digest algorithm to use | 2775 | * any case client may assume that any URI which starts with |
2776 | * any of specified URI is in the same "protection space"; | ||
2777 | * could be NULL (clients typically assume that the same | ||
2778 | * credentials could be used for any URI on the same host) | ||
2779 | * @param response the reply to send; should contain the "access denied" | ||
2780 | * body; note that this function sets the "WWW Authenticate" | ||
2781 | * header and that the caller should not do this; | ||
2782 | * the NULL is tolerated | ||
2783 | * @param signal_stale set to #MHD_YES if the nonce is stale to add 'stale=true' | ||
2784 | * to the authentication header, this instructs the client | ||
2785 | * to retry immediately with the new nonce and the same | ||
2786 | * credentials, without asking user for the new password | ||
2787 | * @param mqop the QOP to use, currently the only allowed value is | ||
2788 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2789 | * @param malgo3 digest algorithm to use, if several algorithms are specified | ||
2790 | * then MD5 is used (if allowed) | ||
2791 | * @param userhash_support if set to non-zero value (#MHD_YES) then support of | ||
2792 | * userhash is indicated, the client may provide | ||
2793 | * hash("username:realm") instead of username in | ||
2794 | * clear text; note that client is allowed to provide | ||
2795 | * the username in cleartext even if this parameter set | ||
2796 | * to non-zero | ||
2797 | * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is | ||
2798 | * added, indicating for the client that UTF-8 encoding | ||
2799 | * is preferred | ||
2634 | * @return #MHD_YES on success, #MHD_NO otherwise | 2800 | * @return #MHD_YES on success, #MHD_NO otherwise |
2635 | * @note Available since #MHD_VERSION 0x00096200 | 2801 | * @note Available since #MHD_VERSION 0x00097526 |
2636 | * @ingroup authentication | 2802 | * @ingroup authentication |
2637 | */ | 2803 | */ |
2638 | _MHD_EXTERN enum MHD_Result | 2804 | _MHD_EXTERN enum MHD_Result |
2639 | MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | 2805 | MHD_queue_auth_required_response3 (struct MHD_Connection *connection, |
2640 | const char *realm, | 2806 | const char *realm, |
2641 | const char *opaque, | 2807 | const char *opaque, |
2642 | struct MHD_Response *response, | 2808 | const char *domain, |
2643 | int signal_stale, | 2809 | struct MHD_Response *response, |
2644 | enum MHD_DigestAuthAlgorithm algo) | 2810 | int signal_stale, |
2811 | enum MHD_DigestAuthMultiQOP mqop, | ||
2812 | enum MHD_DigestAuthMultiAlgo3 malgo3, | ||
2813 | int userhash_support, | ||
2814 | int prefer_utf8) | ||
2645 | { | 2815 | { |
2646 | enum MHD_Result ret; | 2816 | static const char prefix_realm[] = "realm=\""; |
2647 | int hlen; | 2817 | static const char prefix_qop[] = "qop=\""; |
2648 | 2818 | static const char prefix_algo[] = "algorithm="; | |
2819 | static const char prefix_nonce[] = "nonce=\""; | ||
2820 | static const char prefix_opaque[] = "opaque=\""; | ||
2821 | static const char prefix_domain[] = "domain=\""; | ||
2822 | static const char str_charset[] = "charset=UTF-8"; | ||
2823 | static const char str_userhash[] = "userhash=true"; | ||
2824 | static const char str_stale[] = "stale=true"; | ||
2825 | enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */ | ||
2826 | size_t realm_len; | ||
2827 | size_t opaque_len; | ||
2828 | size_t domain_len; | ||
2829 | size_t buf_size; | ||
2830 | char *buf; | ||
2831 | size_t p; /* The position in the buffer */ | ||
2649 | struct DigestAlgorithm da; | 2832 | struct DigestAlgorithm da; |
2650 | 2833 | ||
2651 | if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | 2834 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION)) |
2652 | { | 2835 | { |
2653 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5)) | 2836 | #ifdef HAVE_MESSAGES |
2654 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | 2837 | MHD_DLOG (connection->daemon, |
2655 | } | 2838 | _ ("The 'session' algorithms are not supported.\n")); |
2656 | else if (MHD_DIGEST_ALG_SHA256 == algo) | 2839 | #endif /* HAVE_MESSAGES */ |
2657 | { | 2840 | return MHD_NO; |
2658 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2659 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2660 | } | 2841 | } |
2842 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) | ||
2843 | s_algo = MHD_DIGEST_AUTH_ALGO3_MD5; | ||
2844 | else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2845 | s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256; | ||
2661 | else | 2846 | else |
2662 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | 2847 | MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); |
2663 | 2848 | ||
2664 | if (NULL == response) | 2849 | if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop) |
2665 | return MHD_NO; | 2850 | MHD_PANIC (_ ("Wrong 'mqop' value, API violation")); |
2666 | 2851 | ||
2667 | if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) | 2852 | if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) |
2668 | { | 2853 | { |
@@ -2673,89 +2858,256 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | |||
2673 | return MHD_NO; | 2858 | return MHD_NO; |
2674 | } | 2859 | } |
2675 | 2860 | ||
2676 | if (1) | 2861 | if (! digest_setup (&da, get_base_digest_algo (s_algo))) |
2677 | { | 2862 | MHD_PANIC (_ ("Wrong 'algo' value, API violation")); |
2678 | char nonce[NONCE_STD_LEN (MAX_DIGEST) + 1]; | ||
2679 | 2863 | ||
2680 | /* VLA_CHECK_LEN_DIGEST (digest_get_size (&da)); */ | 2864 | /* Calculate required size */ |
2681 | if (! calculate_add_nonce_with_retry (connection, realm, &da, nonce)) | 2865 | buf_size = 0; |
2682 | { | 2866 | /* 'Digest ' */ |
2683 | #ifdef HAVE_MESSAGES | 2867 | buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */ |
2684 | MHD_DLOG (connection->daemon, | 2868 | buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */ |
2685 | _ ("Could not register nonce. Client's requests with this " | 2869 | /* 'realm="xxxx", ' */ |
2686 | "nonce will be always 'stale'. Probably clients' requests " | 2870 | realm_len = strlen (realm); |
2687 | "are too intensive.\n")); | 2871 | if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len) |
2688 | #else /* ! HAVE_MESSAGES */ | 2872 | return MHD_NO; |
2689 | (void) 0; | 2873 | if ((NULL != memchr (realm, '\r', realm_len)) || |
2690 | #endif /* ! HAVE_MESSAGES */ | 2874 | (NULL != memchr (realm, '\n', realm_len))) |
2691 | } | 2875 | return MHD_NO; |
2692 | /* Building the authentication header */ | 2876 | buf_size += realm_len * 2; /* Quoting may double the size */ |
2693 | hlen = MHD_snprintf_ (NULL, | 2877 | /* 'qop="xxxx", ' */ |
2694 | 0, | 2878 | buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */ |
2695 | "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s", | 2879 | buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); |
2696 | realm, | 2880 | /* 'algorithm="xxxx", ' */ |
2697 | nonce, | 2881 | buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */ |
2698 | opaque, | 2882 | if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) |
2699 | digest_get_algo_name (&da), | 2883 | buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); |
2700 | signal_stale | 2884 | else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) |
2701 | ? ",stale=\"true\"" | 2885 | buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); |
2702 | : ""); | 2886 | else |
2703 | if (hlen > 0) | 2887 | mhd_assert (0); |
2704 | { | 2888 | /* 'nonce="xxxx", ' */ |
2705 | char *header; | 2889 | buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */ |
2890 | buf_size += NONCE_STD_LEN (digest_get_size (&da)); /* Escaping not needed */ | ||
2891 | /* 'opaque="xxxx", ' */ | ||
2892 | if (NULL != opaque) | ||
2893 | { | ||
2894 | buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */ | ||
2895 | opaque_len = strlen (opaque); | ||
2896 | if ((NULL != memchr (opaque, '\r', opaque_len)) || | ||
2897 | (NULL != memchr (opaque, '\n', opaque_len))) | ||
2898 | return MHD_NO; | ||
2899 | buf_size += opaque_len * 2; /* Quoting may double the size */ | ||
2900 | } | ||
2901 | else | ||
2902 | opaque_len = 0; | ||
2903 | /* 'domain="xxxx", ' */ | ||
2904 | if (NULL != domain) | ||
2905 | { | ||
2906 | buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */ | ||
2907 | domain_len = strlen (domain); | ||
2908 | if ((NULL != memchr (domain, '\r', domain_len)) || | ||
2909 | (NULL != memchr (domain, '\n', domain_len))) | ||
2910 | return MHD_NO; | ||
2911 | buf_size += domain_len * 2; /* Quoting may double the size */ | ||
2912 | } | ||
2913 | else | ||
2914 | domain_len = 0; | ||
2915 | /* 'charset=UTF-8' */ | ||
2916 | if (MHD_NO != prefer_utf8) | ||
2917 | buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */ | ||
2918 | /* 'userhash=true' */ | ||
2919 | if (MHD_NO != userhash_support) | ||
2920 | buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */ | ||
2921 | /* 'stale=true' */ | ||
2922 | if (MHD_NO != signal_stale) | ||
2923 | buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */ | ||
2924 | |||
2925 | /* The calculated length is for string ended with ", ". One character will | ||
2926 | * be used for zero-termination, the last one will not be used. */ | ||
2927 | |||
2928 | /* Allocate the buffer */ | ||
2929 | buf = malloc (buf_size); | ||
2930 | if (NULL == buf) | ||
2931 | return MHD_NO; | ||
2706 | 2932 | ||
2707 | header = MHD_calloc_ (1, | 2933 | /* Build the challenge string */ |
2708 | (size_t) hlen + 1); | 2934 | p = 0; |
2709 | if (NULL == header) | 2935 | /* 'Digest: ' */ |
2710 | { | 2936 | memcpy (buf + p, _MHD_AUTH_DIGEST_BASE, |
2937 | MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE)); | ||
2938 | p += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE); | ||
2939 | buf[p++] = ' '; | ||
2940 | /* 'realm="xxxx", ' */ | ||
2941 | memcpy (buf + p, prefix_realm, | ||
2942 | MHD_STATICSTR_LEN_ (prefix_realm)); | ||
2943 | p += MHD_STATICSTR_LEN_ (prefix_realm); | ||
2944 | mhd_assert ((buf_size - p) >= (realm_len * 2)); | ||
2945 | p += MHD_str_quote (realm, realm_len, buf + p, buf_size - p); | ||
2946 | buf[p++] = '\"'; | ||
2947 | buf[p++] = ','; | ||
2948 | buf[p++] = ' '; | ||
2949 | /* 'qop="xxxx", ' */ | ||
2950 | memcpy (buf + p, prefix_qop, | ||
2951 | MHD_STATICSTR_LEN_ (prefix_qop)); | ||
2952 | p += MHD_STATICSTR_LEN_ (prefix_qop); | ||
2953 | memcpy (buf + p, MHD_TOKEN_AUTH_, | ||
2954 | MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_)); | ||
2955 | p += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); | ||
2956 | buf[p++] = '\"'; | ||
2957 | buf[p++] = ','; | ||
2958 | buf[p++] = ' '; | ||
2959 | /* 'algorithm="xxxx", ' */ | ||
2960 | memcpy (buf + p, prefix_algo, | ||
2961 | MHD_STATICSTR_LEN_ (prefix_algo)); | ||
2962 | p += MHD_STATICSTR_LEN_ (prefix_algo); | ||
2963 | if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) | ||
2964 | { | ||
2965 | memcpy (buf + p, _MHD_MD5_TOKEN, | ||
2966 | MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN)); | ||
2967 | p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); | ||
2968 | } | ||
2969 | else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) | ||
2970 | { | ||
2971 | memcpy (buf + p, _MHD_SHA256_TOKEN, | ||
2972 | MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN)); | ||
2973 | p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); | ||
2974 | } | ||
2975 | buf[p++] = ','; | ||
2976 | buf[p++] = ' '; | ||
2977 | /* 'nonce="xxxx", ' */ | ||
2978 | memcpy (buf + p, prefix_nonce, | ||
2979 | MHD_STATICSTR_LEN_ (prefix_nonce)); | ||
2980 | p += MHD_STATICSTR_LEN_ (prefix_nonce); | ||
2981 | mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (&da)))); | ||
2982 | if (! calculate_add_nonce_with_retry (connection, realm, &da, buf + p)) | ||
2983 | { | ||
2711 | #ifdef HAVE_MESSAGES | 2984 | #ifdef HAVE_MESSAGES |
2712 | MHD_DLOG (connection->daemon, | 2985 | MHD_DLOG (connection->daemon, |
2713 | _ ("Failed to allocate memory for auth response header.\n")); | 2986 | _ ("Could not register nonce. Client's requests with this " |
2987 | "nonce will be always 'stale'. Probably clients' requests " | ||
2988 | "are too intensive.\n")); | ||
2714 | #endif /* HAVE_MESSAGES */ | 2989 | #endif /* HAVE_MESSAGES */ |
2715 | return MHD_NO; | 2990 | (void) 0; /* Mute compiler warning for builds without messages */ |
2716 | } | ||
2717 | |||
2718 | if (MHD_snprintf_ (header, | ||
2719 | (size_t) hlen + 1, | ||
2720 | "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s", | ||
2721 | realm, | ||
2722 | nonce, | ||
2723 | opaque, | ||
2724 | digest_get_algo_name (&da), | ||
2725 | signal_stale | ||
2726 | ? ",stale=\"true\"" | ||
2727 | : "") == hlen) | ||
2728 | ret = MHD_add_response_header (response, | ||
2729 | MHD_HTTP_HEADER_WWW_AUTHENTICATE, | ||
2730 | header); | ||
2731 | else | ||
2732 | ret = MHD_NO; | ||
2733 | #if 0 | ||
2734 | if ( (MHD_NO != ret) && (AND in state : 100 continue aborting ...)) | ||
2735 | ret = MHD_add_response_header (response, | ||
2736 | MHD_HTTP_HEADER_CONNECTION, | ||
2737 | "close"); | ||
2738 | #endif | ||
2739 | free (header); | ||
2740 | } | ||
2741 | else | ||
2742 | ret = MHD_NO; | ||
2743 | } | 2991 | } |
2744 | 2992 | p += NONCE_STD_LEN (digest_get_size (&da)); | |
2745 | if (MHD_NO != ret) | 2993 | buf[p++] = '\"'; |
2994 | buf[p++] = ','; | ||
2995 | buf[p++] = ' '; | ||
2996 | /* 'opaque="xxxx", ' */ | ||
2997 | if (NULL != opaque) | ||
2746 | { | 2998 | { |
2747 | ret = MHD_queue_response (connection, | 2999 | memcpy (buf + p, prefix_opaque, |
2748 | MHD_HTTP_UNAUTHORIZED, | 3000 | MHD_STATICSTR_LEN_ (prefix_opaque)); |
2749 | response); | 3001 | p += MHD_STATICSTR_LEN_ (prefix_opaque); |
3002 | mhd_assert ((buf_size - p) >= (opaque_len * 2)); | ||
3003 | p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p); | ||
3004 | buf[p++] = '\"'; | ||
3005 | buf[p++] = ','; | ||
3006 | buf[p++] = ' '; | ||
2750 | } | 3007 | } |
2751 | else | 3008 | /* 'domain="xxxx", ' */ |
3009 | if (NULL != domain) | ||
3010 | { | ||
3011 | memcpy (buf + p, prefix_domain, | ||
3012 | MHD_STATICSTR_LEN_ (prefix_domain)); | ||
3013 | p += MHD_STATICSTR_LEN_ (prefix_domain); | ||
3014 | mhd_assert ((buf_size - p) >= (domain_len * 2)); | ||
3015 | p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p); | ||
3016 | buf[p++] = '\"'; | ||
3017 | buf[p++] = ','; | ||
3018 | buf[p++] = ' '; | ||
3019 | } | ||
3020 | /* 'charset=UTF-8' */ | ||
3021 | if (MHD_NO != prefer_utf8) | ||
3022 | { | ||
3023 | memcpy (buf + p, str_charset, | ||
3024 | MHD_STATICSTR_LEN_ (str_charset)); | ||
3025 | p += MHD_STATICSTR_LEN_ (str_charset); | ||
3026 | buf[p++] = ','; | ||
3027 | buf[p++] = ' '; | ||
3028 | } | ||
3029 | /* 'userhash=true' */ | ||
3030 | if (MHD_NO != userhash_support) | ||
3031 | { | ||
3032 | memcpy (buf + p, str_userhash, | ||
3033 | MHD_STATICSTR_LEN_ (str_userhash)); | ||
3034 | p += MHD_STATICSTR_LEN_ (str_userhash); | ||
3035 | buf[p++] = ','; | ||
3036 | buf[p++] = ' '; | ||
3037 | } | ||
3038 | /* 'stale=true' */ | ||
3039 | if (MHD_NO != signal_stale) | ||
3040 | { | ||
3041 | memcpy (buf + p, str_stale, | ||
3042 | MHD_STATICSTR_LEN_ (str_stale)); | ||
3043 | p += MHD_STATICSTR_LEN_ (str_stale); | ||
3044 | buf[p++] = ','; | ||
3045 | buf[p++] = ' '; | ||
3046 | } | ||
3047 | mhd_assert (buf_size >= p); | ||
3048 | /* The build string ends with ", ". Replace comma with zero-termination. */ | ||
3049 | --p; | ||
3050 | buf[--p] = 0; | ||
3051 | |||
3052 | if (! MHD_add_response_entry_no_check_ (response, MHD_HEADER_KIND, | ||
3053 | MHD_HTTP_HEADER_WWW_AUTHENTICATE, | ||
3054 | MHD_STATICSTR_LEN_ ( \ | ||
3055 | MHD_HTTP_HEADER_WWW_AUTHENTICATE), | ||
3056 | buf, p)) | ||
2752 | { | 3057 | { |
2753 | #ifdef HAVE_MESSAGES | 3058 | #ifdef HAVE_MESSAGES |
2754 | MHD_DLOG (connection->daemon, | 3059 | MHD_DLOG (connection->daemon, |
2755 | _ ("Failed to add Digest auth header.\n")); | 3060 | _ ("Failed to add Digest auth header.\n")); |
2756 | #endif /* HAVE_MESSAGES */ | 3061 | #endif /* HAVE_MESSAGES */ |
3062 | free (buf); | ||
3063 | return MHD_NO; | ||
2757 | } | 3064 | } |
2758 | return ret; | 3065 | free (buf); |
3066 | |||
3067 | return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); | ||
3068 | } | ||
3069 | |||
3070 | |||
3071 | /** | ||
3072 | * Queues a response to request authentication from the client | ||
3073 | * | ||
3074 | * @param connection The MHD connection structure | ||
3075 | * @param realm the realm presented to the client | ||
3076 | * @param opaque string to user for opaque value | ||
3077 | * @param response reply to send; should contain the "access denied" | ||
3078 | * body; note that this function will set the "WWW Authenticate" | ||
3079 | * header and that the caller should not do this; the NULL is tolerated | ||
3080 | * @param signal_stale #MHD_YES if the nonce is stale to add | ||
3081 | * 'stale=true' to the authentication header | ||
3082 | * @param algo digest algorithm to use | ||
3083 | * @return #MHD_YES on success, #MHD_NO otherwise | ||
3084 | * @note Available since #MHD_VERSION 0x00096200 | ||
3085 | * @ingroup authentication | ||
3086 | */ | ||
3087 | _MHD_EXTERN enum MHD_Result | ||
3088 | MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | ||
3089 | const char *realm, | ||
3090 | const char *opaque, | ||
3091 | struct MHD_Response *response, | ||
3092 | int signal_stale, | ||
3093 | enum MHD_DigestAuthAlgorithm algo) | ||
3094 | { | ||
3095 | enum MHD_DigestAuthMultiAlgo3 algo3; | ||
3096 | |||
3097 | if (MHD_DIGEST_ALG_MD5 == algo) | ||
3098 | algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; | ||
3099 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
3100 | algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; | ||
3101 | else if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | ||
3102 | algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; | ||
3103 | else | ||
3104 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | ||
3105 | |||
3106 | return MHD_queue_auth_required_response3 (connection, realm, opaque, | ||
3107 | NULL, response, signal_stale, | ||
3108 | MHD_DIGEST_AUTH_MULT_QOP_AUTH, | ||
3109 | algo3, | ||
3110 | 0, 0); | ||
2759 | } | 3111 | } |
2760 | 3112 | ||
2761 | 3113 | ||