aboutsummaryrefslogtreecommitdiff
path: root/src/microhttpd/digestauth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/microhttpd/digestauth.c')
-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