aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-07-22 10:31:04 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-07-28 07:21:35 +0300
commitc03c57c9d2d95bd739ab8a149597658d1ec95478 (patch)
treed6c6c03b6bcbc1c8981db963612b8ef8914b5bc2 /src/microhttpd
parent4baec145d4c68fbd8efd4052abca24d244bf3188 (diff)
downloadlibmicrohttpd-c03c57c9d2d95bd739ab8a149597658d1ec95478.tar.gz
libmicrohttpd-c03c57c9d2d95bd739ab8a149597658d1ec95478.zip
Added MHD_queue_auth_required_response3(); Refactored public Digest Auth API v3
Added brand new function with more complete support for RFC 7616 features. New function implemented from scratch. Old functions became wrappers for the new function, so fixes are inherited. Fixes: * All user values are properly quoted Features: * Added support for optional 'domain' Digest auth parameter * Realm now optional * Added userhash support * Added charset support For other Digest Auth v3 functions: * Added more parameters for complete control of Auth process from application side.
Diffstat (limited to 'src/microhttpd')
-rw-r--r--src/microhttpd/digestauth.c816
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
212get_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
234digest_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
267MHD_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 */
198union DigestCtx 276union 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 */
1925static enum MHD_DigestAuthResult 2007static enum MHD_DigestAuthResult
1926digest_auth_check_all_inner (struct MHD_Connection *connection, 2008digest_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 (&params->username, tmp1, 2 * digest_size)) 2198 if (! is_param_equal_caseless (&params->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 (&params->nonce, tmp1, ptmp2, &tmp2_size, 2234 unq_res = get_unquoted_param (&params->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 (&params->nonce, tmp1, ptmp2, &tmp2_size, 2358 unq_res = get_unquoted_param (&params->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 (&params->nc, tmp1, ptmp2, &tmp2_size, 2366 unq_res = get_unquoted_param (&params->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 (&params->cnonce, tmp1, ptmp2, &tmp2_size, 2374 unq_res = get_unquoted_param (&params->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 (&params->qop, tmp1, ptmp2, &tmp2_size, 2382 unq_res = get_unquoted_param (&params->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 (&params->nonce, tmp1, 2415 if (! is_param_equal (&params->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 */
2341static enum MHD_DigestAuthResult 2447static enum MHD_DigestAuthResult
2342digest_auth_check_all (struct MHD_Connection *connection, 2448digest_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
2464MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, 2582MHD_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
2639MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, 2805MHD_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
3088MHD_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