diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-07-22 10:31:04 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-07-28 07:21:35 +0300 |
commit | c03c57c9d2d95bd739ab8a149597658d1ec95478 (patch) | |
tree | d6c6c03b6bcbc1c8981db963612b8ef8914b5bc2 | |
parent | 4baec145d4c68fbd8efd4052abca24d244bf3188 (diff) | |
download | libmicrohttpd-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.
-rw-r--r-- | src/examples/digest_auth_example.c | 4 | ||||
-rw-r--r-- | src/include/microhttpd.h | 171 | ||||
-rw-r--r-- | src/microhttpd/digestauth.c | 816 | ||||
-rw-r--r-- | src/testcurl/test_digestauth_concurrent.c | 5 | ||||
-rw-r--r-- | src/testcurl/test_digestauth_emu_ext.c | 4 |
5 files changed, 734 insertions, 266 deletions
diff --git a/src/examples/digest_auth_example.c b/src/examples/digest_auth_example.c index 4f576bf0..db8a1f28 100644 --- a/src/examples/digest_auth_example.c +++ b/src/examples/digest_auth_example.c | |||
@@ -84,8 +84,8 @@ ahc_echo (void *cls, | |||
84 | res_e = MHD_digest_auth_check3 (connection, realm, | 84 | res_e = MHD_digest_auth_check3 (connection, realm, |
85 | username, | 85 | username, |
86 | password, | 86 | password, |
87 | 300, | 87 | 300, 60, MHD_DIGEST_AUTH_MULT_QOP_AUTH, |
88 | MHD_DIGEST_ALG_MD5); | 88 | MHD_DIGEST_AUTH_MULT_ALGO3_MD5); |
89 | MHD_free (username); | 89 | MHD_free (username); |
90 | if (res_e != MHD_DAUTH_OK) | 90 | if (res_e != MHD_DAUTH_OK) |
91 | { | 91 | { |
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index 9cf44ccb..aceed304 100644 --- a/src/include/microhttpd.h +++ b/src/include/microhttpd.h | |||
@@ -96,7 +96,7 @@ extern "C" | |||
96 | * they are parsed as decimal numbers. | 96 | * they are parsed as decimal numbers. |
97 | * Example: 0x01093001 = 1.9.30-1. | 97 | * Example: 0x01093001 = 1.9.30-1. |
98 | */ | 98 | */ |
99 | #define MHD_VERSION 0x00097525 | 99 | #define MHD_VERSION 0x00097526 |
100 | 100 | ||
101 | /* If generic headers don't work on your platform, include headers | 101 | /* If generic headers don't work on your platform, include headers |
102 | which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t', | 102 | which define 'va_list', 'size_t', 'ssize_t', 'intptr_t', 'off_t', |
@@ -330,12 +330,6 @@ _MHD_DEPR_MACRO ( \ | |||
330 | 330 | ||
331 | 331 | ||
332 | /** | 332 | /** |
333 | * Length of the binary output of the MD5 hash function. | ||
334 | */ | ||
335 | #define MHD_MD5_DIGEST_SIZE 16 | ||
336 | |||
337 | |||
338 | /** | ||
339 | * @defgroup httpcode HTTP response codes. | 333 | * @defgroup httpcode HTTP response codes. |
340 | * These are the status codes defined for HTTP responses. | 334 | * These are the status codes defined for HTTP responses. |
341 | * See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml | 335 | * See: https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml |
@@ -4328,6 +4322,22 @@ MHD_destroy_post_processor (struct MHD_PostProcessor *pp); | |||
4328 | 4322 | ||
4329 | 4323 | ||
4330 | /** | 4324 | /** |
4325 | * Length of the binary output of the MD5 hash function. | ||
4326 | * @sa #MHD_digest_get_hash_size() | ||
4327 | * @ingroup authentication | ||
4328 | */ | ||
4329 | #define MHD_MD5_DIGEST_SIZE 16 | ||
4330 | |||
4331 | |||
4332 | /** | ||
4333 | * Length of the binary output of the SHA-256 hash function. | ||
4334 | * @sa #MHD_digest_get_hash_size() | ||
4335 | * @ingroup authentication | ||
4336 | */ | ||
4337 | #define MHD_SHA256_DIGEST_SIZE 32 | ||
4338 | |||
4339 | |||
4340 | /** | ||
4331 | * Constant to indicate that the nonce of the provided | 4341 | * Constant to indicate that the nonce of the provided |
4332 | * authentication code was wrong. | 4342 | * authentication code was wrong. |
4333 | * @ingroup authentication | 4343 | * @ingroup authentication |
@@ -4707,7 +4717,7 @@ struct MHD_DigestAuthInfo | |||
4707 | * long. | 4717 | * long. |
4708 | * @warning This is binary data, no zero termination. | 4718 | * @warning This is binary data, no zero termination. |
4709 | * @warning To avoid buffer overruns, always check the size of the data before | 4719 | * @warning To avoid buffer overruns, always check the size of the data before |
4710 | * use, because @ userhash_bin can point even to zero-sized | 4720 | * use, because @a userhash_bin can point even to zero-sized |
4711 | * data. | 4721 | * data. |
4712 | */ | 4722 | */ |
4713 | uint8_t *userhash_bin; | 4723 | uint8_t *userhash_bin; |
@@ -4753,9 +4763,9 @@ struct MHD_DigestAuthInfo | |||
4753 | 4763 | ||
4754 | /** | 4764 | /** |
4755 | * The nc parameter value. | 4765 | * The nc parameter value. |
4756 | * Can be used by application to limit the number of nonce re-uses. If @ nc | 4766 | * Can be used by application to limit the number of nonce re-uses. If @a nc |
4757 | * is higher than application wants to allow, then fail response with | 4767 | * is higher than application wants to allow, then auth required response with |
4758 | * 'stale=true' could be used to ask force client to get the fresh 'nonce'. | 4768 | * 'stale=true' could be used to force client to get the fresh 'nonce'. |
4759 | * If not specified by client or does not have hexadecimal digits only, the | 4769 | * If not specified by client or does not have hexadecimal digits only, the |
4760 | * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE. | 4770 | * value is #MHD_DIGEST_AUTH_INVALID_NC_VALUE. |
4761 | */ | 4771 | */ |
@@ -4819,12 +4829,29 @@ struct MHD_DigestAuthUsernameInfo | |||
4819 | * long. | 4829 | * long. |
4820 | * @warning This is binary data, no zero termination. | 4830 | * @warning This is binary data, no zero termination. |
4821 | * @warning To avoid buffer overruns, always check the size of the data before | 4831 | * @warning To avoid buffer overruns, always check the size of the data before |
4822 | * use, because @ userhash_bin can point even to zero-sized | 4832 | * use, because @a userhash_bin can point even to zero-sized |
4823 | * data. | 4833 | * data. |
4824 | */ | 4834 | */ |
4825 | uint8_t *userhash_bin; | 4835 | uint8_t *userhash_bin; |
4826 | }; | 4836 | }; |
4827 | 4837 | ||
4838 | |||
4839 | /** | ||
4840 | * Get digest size for specified algorithm. | ||
4841 | * | ||
4842 | * The size of the digest specifies the size of the userhash, userdigest | ||
4843 | * and other parameters which size depends on used hash algorithm. | ||
4844 | * @param algo3 the algorithm to check | ||
4845 | * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or | ||
4846 | * #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not | ||
4847 | * recognised/valid | ||
4848 | * @note Available since #MHD_VERSION 0x00097526 | ||
4849 | * @ingroup authentication | ||
4850 | */ | ||
4851 | _MHD_EXTERN size_t | ||
4852 | MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3); | ||
4853 | |||
4854 | |||
4828 | /** | 4855 | /** |
4829 | * Get the username from Digest Authorization client's header. | 4856 | * Get the username from Digest Authorization client's header. |
4830 | * | 4857 | * |
@@ -4868,7 +4895,7 @@ enum MHD_DigestAuthAlgorithm | |||
4868 | { | 4895 | { |
4869 | 4896 | ||
4870 | /** | 4897 | /** |
4871 | * MHD should pick (currently defaults to SHA-256). | 4898 | * MHD should pick (currently defaults to MD5). |
4872 | */ | 4899 | */ |
4873 | MHD_DIGEST_ALG_AUTO = 0, | 4900 | MHD_DIGEST_ALG_AUTO = 0, |
4874 | 4901 | ||
@@ -4969,10 +4996,17 @@ enum MHD_DigestAuthResult | |||
4969 | * @param username the username needs to be authenticated | 4996 | * @param username the username needs to be authenticated |
4970 | * @param password the password used in the authentication | 4997 | * @param password the password used in the authentication |
4971 | * @param nonce_timeout the nonce validity duration in seconds | 4998 | * @param nonce_timeout the nonce validity duration in seconds |
4972 | * @param algo the digest algorithms allowed for verification | 4999 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
5000 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is | ||
5001 | * returned; | ||
5002 | * zero for no limit | ||
5003 | * @param mqop the QOP to use, currently the only allowed value is | ||
5004 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
5005 | * @param malgo3 digest algorithm to use, if several algorithms are specified | ||
5006 | * then MD5 is used (if allowed) | ||
4973 | * @return #MHD_DAUTH_OK if authenticated, | 5007 | * @return #MHD_DAUTH_OK if authenticated, |
4974 | * the error code otherwise | 5008 | * the error code otherwise |
4975 | * @note Available since #MHD_VERSION 0x00097521 | 5009 | * @note Available since #MHD_VERSION 0x00097526 |
4976 | * @ingroup authentication | 5010 | * @ingroup authentication |
4977 | */ | 5011 | */ |
4978 | _MHD_EXTERN enum MHD_DigestAuthResult | 5012 | _MHD_EXTERN enum MHD_DigestAuthResult |
@@ -4981,34 +5015,49 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
4981 | const char *username, | 5015 | const char *username, |
4982 | const char *password, | 5016 | const char *password, |
4983 | unsigned int nonce_timeout, | 5017 | unsigned int nonce_timeout, |
4984 | enum MHD_DigestAuthAlgorithm algo); | 5018 | uint32_t max_nc, |
5019 | enum MHD_DigestAuthMultiQOP mqop, | ||
5020 | enum MHD_DigestAuthMultiAlgo3 malgo3); | ||
4985 | 5021 | ||
4986 | 5022 | ||
4987 | /** | 5023 | /** |
4988 | * Authenticates the authorization header sent by the client. | 5024 | * Authenticates the authorization header sent by the client by using |
5025 | * hash of "username:realm:password". | ||
4989 | * | 5026 | * |
4990 | * @param connection the MHD connection structure | 5027 | * @param connection the MHD connection structure |
4991 | * @param realm the realm to be used for authorization of the client | 5028 | * @param realm the realm presented to the client |
4992 | * @param username the username needs to be authenticated | 5029 | * @param username the username needs to be authenticated |
4993 | * @param digest the pointer to the binary digest for the precalculated hash | 5030 | * @param userdigest the precalculated binary hash of the string |
4994 | * value "username:realm:password" with specified @a algo | 5031 | * "username:realm:password" |
4995 | * @param digest_size the number of bytes in @a digest (the size must match | 5032 | * @param userdigest_size the size of the @a userdigest in bytes, must match the |
4996 | * @a algo!) | 5033 | * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, |
4997 | * @param nonce_timeout the nonce validity duration in seconds | 5034 | * #MHD_SHA256_DIGEST_SIZE) |
4998 | * @param algo digest algorithms allowed for verification | 5035 | * @param nonce_timeout the period of seconds since nonce generation, when |
5036 | * the nonce is recognised as valid and not stale. | ||
5037 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc | ||
5038 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is | ||
5039 | * returned; | ||
5040 | * zero for no limit | ||
5041 | * @param mqop the QOP to use, currently the only allowed value is | ||
5042 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
5043 | * @param malgo3 the digest algorithms to use; both MD5-based and SHA-256-based | ||
5044 | * algorithms cannot be used at the same time for this function | ||
5045 | * as @a userdigest_size must match specified algorithm | ||
4999 | * @return #MHD_DAUTH_OK if authenticated, | 5046 | * @return #MHD_DAUTH_OK if authenticated, |
5000 | * the error code otherwise | 5047 | * the error code otherwise |
5001 | * @note Available since #MHD_VERSION 0x00097521 | 5048 | * @note Available since #MHD_VERSION 0x00097526 |
5002 | * @ingroup authentication | 5049 | * @ingroup authentication |
5003 | */ | 5050 | */ |
5004 | _MHD_EXTERN enum MHD_DigestAuthResult | 5051 | _MHD_EXTERN enum MHD_DigestAuthResult |
5005 | MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, | 5052 | MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, |
5006 | const char *realm, | 5053 | const char *realm, |
5007 | const char *username, | 5054 | const char *username, |
5008 | const uint8_t *digest, | 5055 | const void *userdigest, |
5009 | size_t digest_size, | 5056 | size_t userdigest_size, |
5010 | unsigned int nonce_timeout, | 5057 | unsigned int nonce_timeout, |
5011 | enum MHD_DigestAuthAlgorithm algo); | 5058 | uint32_t max_nc, |
5059 | enum MHD_DigestAuthMultiQOP mqop, | ||
5060 | enum MHD_DigestAuthMultiAlgo3 malgo3); | ||
5012 | 5061 | ||
5013 | 5062 | ||
5014 | /** | 5063 | /** |
@@ -5121,6 +5170,66 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection, | |||
5121 | /** | 5170 | /** |
5122 | * Queues a response to request authentication from the client | 5171 | * Queues a response to request authentication from the client |
5123 | * | 5172 | * |
5173 | * This function modifies provided @a response. The @a response must not be | ||
5174 | * reused and should be destroyed (by #MHD_destroy_response()) after call of | ||
5175 | * this function. | ||
5176 | * | ||
5177 | * @param connection the MHD connection structure | ||
5178 | * @param realm the realm presented to the client | ||
5179 | * @param opaque the string for opaque value, can be NULL, but NULL is | ||
5180 | * not recommended for better compatibility with clients | ||
5181 | * @param domain the optional space-separated list of URIs for which the | ||
5182 | * same authorisation could be used, URIs can be in form | ||
5183 | * "path-absolute" (the path for the same host with initial slash) | ||
5184 | * or in form "absolute-URI" (the full path with protocol), in | ||
5185 | * any case client may assume that any URI which starts with | ||
5186 | * any of specified URI is in the same "protection space"; | ||
5187 | * could be NULL (clients typically assume that the same | ||
5188 | * credentials could be used for any URI on the same host) | ||
5189 | * @param response the reply to send; should contain the "access denied" | ||
5190 | * body; note that this function sets the "WWW Authenticate" | ||
5191 | * header and that the caller should not do this; | ||
5192 | * the NULL is tolerated | ||
5193 | * @param signal_stale set to #MHD_YES if the nonce is stale to add 'stale=true' | ||
5194 | * to the authentication header, this instructs the client | ||
5195 | * to retry immediately with the new nonce and the same | ||
5196 | * credentials, without asking user for the new password | ||
5197 | * @param mqop the QOP to use, currently the only allowed value is | ||
5198 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
5199 | * @param malgo3 digest algorithm to use, if several algorithms are specified | ||
5200 | * then MD5 is used (if allowed) | ||
5201 | * @param userhash_support if set to non-zero value (#MHD_YES) then support of | ||
5202 | * userhash is indicated, the client may provide | ||
5203 | * hash("username:realm") instead of username in | ||
5204 | * clear text; note that client is allowed to provide | ||
5205 | * the username in cleartext even if this parameter set | ||
5206 | * to non-zero | ||
5207 | * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is | ||
5208 | * added, indicating for the client that UTF-8 encoding | ||
5209 | * is preferred | ||
5210 | * @return #MHD_YES on success, #MHD_NO otherwise | ||
5211 | * @note Available since #MHD_VERSION 0x00097526 | ||
5212 | * @ingroup authentication | ||
5213 | */ | ||
5214 | _MHD_EXTERN enum MHD_Result | ||
5215 | MHD_queue_auth_required_response3 (struct MHD_Connection *connection, | ||
5216 | const char *realm, | ||
5217 | const char *opaque, | ||
5218 | const char *domain, | ||
5219 | struct MHD_Response *response, | ||
5220 | int signal_stale, | ||
5221 | enum MHD_DigestAuthMultiQOP qop, | ||
5222 | enum MHD_DigestAuthMultiAlgo3 algo, | ||
5223 | int userhash_support, | ||
5224 | int prefer_utf8); | ||
5225 | |||
5226 | |||
5227 | /** | ||
5228 | * Queues a response to request authentication from the client | ||
5229 | * | ||
5230 | * This function modifies provided @a response. The @a response must not be | ||
5231 | * reused and should be destroyed after call of this function. | ||
5232 | * | ||
5124 | * @param connection The MHD connection structure | 5233 | * @param connection The MHD connection structure |
5125 | * @param realm the realm presented to the client | 5234 | * @param realm the realm presented to the client |
5126 | * @param opaque string to user for opaque value | 5235 | * @param opaque string to user for opaque value |
@@ -5132,6 +5241,7 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection, | |||
5132 | * @param algo digest algorithm to use | 5241 | * @param algo digest algorithm to use |
5133 | * @return #MHD_YES on success, #MHD_NO otherwise | 5242 | * @return #MHD_YES on success, #MHD_NO otherwise |
5134 | * @note Available since #MHD_VERSION 0x00096200 | 5243 | * @note Available since #MHD_VERSION 0x00096200 |
5244 | * @deprecated use MHD_queue_auth_required_response3() | ||
5135 | * @ingroup authentication | 5245 | * @ingroup authentication |
5136 | */ | 5246 | */ |
5137 | _MHD_EXTERN enum MHD_Result | 5247 | _MHD_EXTERN enum MHD_Result |
@@ -5148,6 +5258,9 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | |||
5148 | * For now uses MD5 (for backwards-compatibility). Still, if you | 5258 | * For now uses MD5 (for backwards-compatibility). Still, if you |
5149 | * need to be sure, use #MHD_queue_auth_fail_response2(). | 5259 | * need to be sure, use #MHD_queue_auth_fail_response2(). |
5150 | * | 5260 | * |
5261 | * This function modifies provided @a response. The @a response must not be | ||
5262 | * reused and should be destroyed after call of this function. | ||
5263 | * | ||
5151 | * @param connection The MHD connection structure | 5264 | * @param connection The MHD connection structure |
5152 | * @param realm the realm presented to the client | 5265 | * @param realm the realm presented to the client |
5153 | * @param opaque string to user for opaque value | 5266 | * @param opaque string to user for opaque value |
@@ -5157,8 +5270,8 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | |||
5157 | * @param signal_stale #MHD_YES if the nonce is stale to add | 5270 | * @param signal_stale #MHD_YES if the nonce is stale to add |
5158 | * 'stale=true' to the authentication header | 5271 | * 'stale=true' to the authentication header |
5159 | * @return #MHD_YES on success, #MHD_NO otherwise | 5272 | * @return #MHD_YES on success, #MHD_NO otherwise |
5273 | * @deprecated use MHD_queue_auth_required_response3() | ||
5160 | * @ingroup authentication | 5274 | * @ingroup authentication |
5161 | * @deprecated use MHD_queue_auth_fail_response2() | ||
5162 | */ | 5275 | */ |
5163 | _MHD_EXTERN enum MHD_Result | 5276 | _MHD_EXTERN enum MHD_Result |
5164 | MHD_queue_auth_fail_response (struct MHD_Connection *connection, | 5277 | MHD_queue_auth_fail_response (struct MHD_Connection *connection, |
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c index b6a22785..f8f06d6d 100644 --- a/src/microhttpd/digestauth.c +++ b/src/microhttpd/digestauth.c | |||
@@ -30,6 +30,7 @@ | |||
30 | #include "platform.h" | 30 | #include "platform.h" |
31 | #include "mhd_limits.h" | 31 | #include "mhd_limits.h" |
32 | #include "internal.h" | 32 | #include "internal.h" |
33 | #include "response.h" | ||
33 | #include "md5.h" | 34 | #include "md5.h" |
34 | #include "sha256.h" | 35 | #include "sha256.h" |
35 | #include "mhd_mono_clock.h" | 36 | #include "mhd_mono_clock.h" |
@@ -158,6 +159,16 @@ | |||
158 | #define _MHD_SESS_TOKEN "-sess" | 159 | #define _MHD_SESS_TOKEN "-sess" |
159 | 160 | ||
160 | /** | 161 | /** |
162 | * The "auth" token for QOP | ||
163 | */ | ||
164 | #define MHD_TOKEN_AUTH_ "auth" | ||
165 | |||
166 | /** | ||
167 | * The "auth-int" token for QOP | ||
168 | */ | ||
169 | #define MHD_TOKEN_AUTH_INT_ "auth-int" | ||
170 | |||
171 | /** | ||
161 | * The required prefix of parameter with the extended notation | 172 | * The required prefix of parameter with the extended notation |
162 | */ | 173 | */ |
163 | #define MHD_DAUTH_EXT_PARAM_PREFIX "UTF-8'" | 174 | #define MHD_DAUTH_EXT_PARAM_PREFIX "UTF-8'" |
@@ -193,6 +204,73 @@ enum MHD_CheckNonceNC_ | |||
193 | 204 | ||
194 | 205 | ||
195 | /** | 206 | /** |
207 | * Get base hash calculation algorithm from #MHD_DigestAuthAlgo3 value. | ||
208 | * @param algo3 the MHD_DigestAuthAlgo3 value | ||
209 | * @return the base hash calculation algorithm | ||
210 | */ | ||
211 | _MHD_static_inline enum MHD_DigestBaseAlgo | ||
212 | get_base_digest_algo (enum MHD_DigestAuthAlgo3 algo3) | ||
213 | { | ||
214 | unsigned int base_algo; | ||
215 | |||
216 | base_algo = | ||
217 | ((unsigned int) algo3) | ||
218 | & ~((unsigned int) | ||
219 | (MHD_DIGEST_AUTH_ALGO3_NON_SESSION | ||
220 | | MHD_DIGEST_AUTH_ALGO3_NON_SESSION)); | ||
221 | return (enum MHD_DigestBaseAlgo) base_algo; | ||
222 | } | ||
223 | |||
224 | |||
225 | /** | ||
226 | * Get digest size for specified algorithm. | ||
227 | * | ||
228 | * Internal inline version. | ||
229 | * @param algo3 the algorithm to check | ||
230 | * @return the size of the digest or zero if the input value is not | ||
231 | * recognised/valid | ||
232 | */ | ||
233 | _MHD_static_inline size_t | ||
234 | digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) | ||
235 | { | ||
236 | mhd_assert (MHD_MD5_DIGEST_SIZE == MD5_DIGEST_SIZE); | ||
237 | mhd_assert (MHD_SHA256_DIGEST_SIZE == SHA256_DIGEST_SIZE); | ||
238 | /* Both MD5 and SHA-256 must not be specified at the same time */ | ||
239 | mhd_assert ( (0 == (((unsigned int) algo3) \ | ||
240 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5))) || \ | ||
241 | (0 == (((unsigned int) algo3) \ | ||
242 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256))) ); | ||
243 | if (0 != (((unsigned int) algo3) | ||
244 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_MD5))) | ||
245 | return MHD_MD5_DIGEST_SIZE; | ||
246 | else if (0 != (((unsigned int) algo3) | ||
247 | & ((unsigned int) MHD_DIGEST_BASE_ALGO_SHA256))) | ||
248 | return MHD_SHA256_DIGEST_SIZE; | ||
249 | |||
250 | return 0; /* Wrong input */ | ||
251 | } | ||
252 | |||
253 | |||
254 | /** | ||
255 | * Get digest size for specified algorithm. | ||
256 | * | ||
257 | * The size of the digest specifies the size of the userhash, userdigest | ||
258 | * and other parameters which size depends on used hash algorithm. | ||
259 | * @param algo3 the algorithm to check | ||
260 | * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or | ||
261 | * #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not | ||
262 | * recognised/valid | ||
263 | * @note Available since #MHD_VERSION 0x00097526 | ||
264 | * @ingroup authentication | ||
265 | */ | ||
266 | _MHD_EXTERN size_t | ||
267 | MHD_digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) | ||
268 | { | ||
269 | return digest_get_hash_size (algo3); | ||
270 | } | ||
271 | |||
272 | |||
273 | /** | ||
196 | * Digest context data | 274 | * Digest context data |
197 | */ | 275 | */ |
198 | union DigestCtx | 276 | union DigestCtx |
@@ -1904,18 +1982,22 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam *param, | |||
1904 | /** | 1982 | /** |
1905 | * Authenticates the authorization header sent by the client | 1983 | * Authenticates the authorization header sent by the client |
1906 | * | 1984 | * |
1907 | * @param connection The MHD connection structure | 1985 | * @param connection the MHD connection structure |
1908 | * @param[in,out] da digest algorithm to use for checking (written to as | 1986 | * @param realm the realm presented to the client |
1909 | * part of the calculations, but the values left in the struct | 1987 | * @param username the username needs to be authenticated |
1910 | * are not actually expected to be useful for the caller) | 1988 | * @param password the password used in the authentication |
1911 | * @param realm The realm presented to the client | 1989 | * @param userdigest the optional precalculated binary hash of the string |
1912 | * @param username The username needs to be authenticated | 1990 | * "username:realm:password" |
1913 | * @param password The password used in the authentication | 1991 | * @param nonce_timeout the period of seconds since nonce generation, when |
1914 | * @param digest An optional binary hash | 1992 | * the nonce is recognised as valid and not stale. |
1915 | * of the precalculated hash value "username:realm:password" | 1993 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
1916 | * (must contain "digest_get_size(da)" bytes or be NULL) | 1994 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is |
1917 | * @param nonce_timeout The amount of time for a nonce to be | 1995 | * returned; |
1918 | * invalid in seconds | 1996 | * zero for no limit |
1997 | * @param mqop the QOP to use, currently the only allowed value is | ||
1998 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
1999 | * @param malgo3 digest algorithms to use, if several algorithms are specified | ||
2000 | * then MD5 is used (if allowed) | ||
1919 | * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, | 2001 | * @param[out] pbuf the pointer to pointer to internally malloc'ed buffer, |
1920 | * to be free if not NULL upon return | 2002 | * to be free if not NULL upon return |
1921 | * @return #MHD_DAUTH_OK if authenticated, | 2003 | * @return #MHD_DAUTH_OK if authenticated, |
@@ -1924,16 +2006,20 @@ is_param_equal_caseless (const struct MHD_RqDAuthParam *param, | |||
1924 | */ | 2006 | */ |
1925 | static enum MHD_DigestAuthResult | 2007 | static enum MHD_DigestAuthResult |
1926 | digest_auth_check_all_inner (struct MHD_Connection *connection, | 2008 | digest_auth_check_all_inner (struct MHD_Connection *connection, |
1927 | struct DigestAlgorithm *da, | ||
1928 | const char *realm, | 2009 | const char *realm, |
1929 | const char *username, | 2010 | const char *username, |
1930 | const char *password, | 2011 | const char *password, |
1931 | const uint8_t *digest, | 2012 | const uint8_t *userdigest, |
1932 | unsigned int nonce_timeout, | 2013 | unsigned int nonce_timeout, |
2014 | uint32_t max_nc, | ||
2015 | enum MHD_DigestAuthMultiQOP mqop, | ||
2016 | enum MHD_DigestAuthMultiAlgo3 malgo3, | ||
1933 | char **pbuf) | 2017 | char **pbuf) |
1934 | { | 2018 | { |
1935 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); | 2019 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); |
1936 | const unsigned int digest_size = digest_get_size (da); | 2020 | enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */ |
2021 | struct DigestAlgorithm da; | ||
2022 | unsigned int digest_size; | ||
1937 | uint8_t hash1_bin[MAX_DIGEST]; | 2023 | uint8_t hash1_bin[MAX_DIGEST]; |
1938 | uint8_t hash2_bin[MAX_DIGEST]; | 2024 | uint8_t hash2_bin[MAX_DIGEST]; |
1939 | #if 0 | 2025 | #if 0 |
@@ -1960,6 +2046,28 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
1960 | if (NULL == params) | 2046 | if (NULL == params) |
1961 | return MHD_DAUTH_WRONG_HEADER; | 2047 | return MHD_DAUTH_WRONG_HEADER; |
1962 | 2048 | ||
2049 | /* ** Initial parameters checks and setup ** */ | ||
2050 | if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop) | ||
2051 | MHD_PANIC (_ ("Wrong 'mqop' value, API violation")); | ||
2052 | |||
2053 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION)) | ||
2054 | { | ||
2055 | #ifdef HAVE_MESSAGES | ||
2056 | MHD_DLOG (connection->daemon, | ||
2057 | _ ("The 'session' algorithms are not supported.\n")); | ||
2058 | #endif /* HAVE_MESSAGES */ | ||
2059 | return MHD_DAUTH_WRONG_ALGO; | ||
2060 | } | ||
2061 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) | ||
2062 | s_algo = MHD_DIGEST_AUTH_ALGO3_MD5; | ||
2063 | else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2064 | s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256; | ||
2065 | else | ||
2066 | MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); | ||
2067 | if (! digest_setup (&da, get_base_digest_algo (s_algo))) | ||
2068 | MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); | ||
2069 | digest_size = digest_get_size (&da); | ||
2070 | |||
1963 | /* ** A quick check for presence of all required parameters ** */ | 2071 | /* ** A quick check for presence of all required parameters ** */ |
1964 | 2072 | ||
1965 | if ((NULL == params->username.value.str) && | 2073 | if ((NULL == params->username.value.str) && |
@@ -1980,7 +2088,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
1980 | 2088 | ||
1981 | if (NULL == params->realm.value.str) | 2089 | if (NULL == params->realm.value.str) |
1982 | return MHD_DAUTH_WRONG_HEADER; | 2090 | return MHD_DAUTH_WRONG_HEADER; |
1983 | else if (((NULL == digest) || params->userhash) && | 2091 | else if (((NULL == userdigest) || params->userhash) && |
1984 | (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len)) | 2092 | (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < params->realm.value.len)) |
1985 | return MHD_DAUTH_TOO_LARGE; /* Realm is too large and it will be used in hash calculations */ | 2093 | return MHD_DAUTH_TOO_LARGE; /* Realm is too large and it will be used in hash calculations */ |
1986 | 2094 | ||
@@ -2029,16 +2137,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2029 | /* ** Check simple parameters match ** */ | 2137 | /* ** Check simple parameters match ** */ |
2030 | 2138 | ||
2031 | /* Check 'algorithm' */ | 2139 | /* Check 'algorithm' */ |
2032 | if (1) | 2140 | /* The 'algorithm' was checked at the start of the function */ |
2033 | { | ||
2034 | const enum MHD_DigestAuthAlgo3 r_algo = get_rq_algo (params); | ||
2035 | const enum MHD_DigestBaseAlgo p_algo = da->algo; | ||
2036 | if ( (! ((MHD_DIGEST_AUTH_ALGO3_MD5 == r_algo) && | ||
2037 | (MHD_DIGEST_BASE_ALGO_MD5 == p_algo))) && | ||
2038 | (! ((MHD_DIGEST_AUTH_ALGO3_SHA256 == r_algo) && | ||
2039 | (MHD_DIGEST_BASE_ALGO_SHA256 == p_algo))) ) | ||
2040 | return MHD_DAUTH_WRONG_ALGO; | ||
2041 | } | ||
2042 | /* 'algorithm' valid */ | 2141 | /* 'algorithm' valid */ |
2043 | 2142 | ||
2044 | /* Check 'qop' */ | 2143 | /* Check 'qop' */ |
@@ -2089,11 +2188,11 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2089 | else | 2188 | else |
2090 | { /* Userhash */ | 2189 | { /* Userhash */ |
2091 | mhd_assert (NULL != params->username.value.str); | 2190 | mhd_assert (NULL != params->username.value.str); |
2092 | digest_init (da); | 2191 | digest_init (&da); |
2093 | digest_update (da, username, username_len); | 2192 | digest_update (&da, username, username_len); |
2094 | digest_update_with_colon (da); | 2193 | digest_update_with_colon (&da); |
2095 | digest_update (da, realm, realm_len); | 2194 | digest_update (&da, realm, realm_len); |
2096 | digest_calc_hash (da, hash1_bin); | 2195 | digest_calc_hash (&da, hash1_bin); |
2097 | mhd_assert (sizeof (tmp1) >= (2 * digest_size + 1)); | 2196 | mhd_assert (sizeof (tmp1) >= (2 * digest_size + 1)); |
2098 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); | 2197 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); |
2099 | if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) | 2198 | if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) |
@@ -2127,7 +2226,10 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2127 | #endif | 2226 | #endif |
2128 | return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ | 2227 | return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ |
2129 | } | 2228 | } |
2229 | if ((0 != max_nc) && (max_nc < nci)) | ||
2230 | return MHD_DAUTH_NONCE_STALE; /* Too large 'nc' value */ | ||
2130 | /* Got 'nc' digital value */ | 2231 | /* Got 'nc' digital value */ |
2232 | |||
2131 | /* Get 'nonce' with basic checks */ | 2233 | /* Get 'nonce' with basic checks */ |
2132 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, | 2234 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, |
2133 | &unquoted); | 2235 | &unquoted); |
@@ -2183,7 +2285,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2183 | { | 2285 | { |
2184 | #ifdef HAVE_MESSAGES | 2286 | #ifdef HAVE_MESSAGES |
2185 | MHD_DLOG (daemon, | 2287 | MHD_DLOG (daemon, |
2186 | _ ("Received nonce that technically valid, but was not " | 2288 | _ ("Received nonce that was not " |
2187 | "generated by MHD. This may indicate an attack attempt.\n")); | 2289 | "generated by MHD. This may indicate an attack attempt.\n")); |
2188 | #endif | 2290 | #endif |
2189 | return MHD_DAUTH_NONCE_WRONG; | 2291 | return MHD_DAUTH_NONCE_WRONG; |
@@ -2196,9 +2298,9 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2196 | /* ** Build H(A2) and check URI match in the header and in the request ** */ | 2298 | /* ** Build H(A2) and check URI match in the header and in the request ** */ |
2197 | 2299 | ||
2198 | /* Get 'uri' */ | 2300 | /* Get 'uri' */ |
2199 | digest_init (da); | 2301 | digest_init (&da); |
2200 | digest_update_str (da, connection->method); | 2302 | digest_update_str (&da, connection->method); |
2201 | digest_update_with_colon (da); | 2303 | digest_update_with_colon (&da); |
2202 | #if 0 | 2304 | #if 0 |
2203 | /* TODO: add support for "auth-int" */ | 2305 | /* TODO: add support for "auth-int" */ |
2204 | digest_update_str (da, hentity); | 2306 | digest_update_str (da, hentity); |
@@ -2209,37 +2311,37 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2209 | if (_MHD_UNQ_OK != unq_res) | 2311 | if (_MHD_UNQ_OK != unq_res) |
2210 | return MHD_DAUTH_ERROR; | 2312 | return MHD_DAUTH_ERROR; |
2211 | 2313 | ||
2212 | digest_update (da, unq_copy.str, unq_copy.len); | 2314 | digest_update (&da, unq_copy.str, unq_copy.len); |
2213 | /* The next check will modify copied URI string */ | 2315 | /* The next check will modify copied URI string */ |
2214 | if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) | 2316 | if (! check_uri_match (connection, unq_copy.str, unq_copy.len)) |
2215 | return MHD_DAUTH_WRONG_URI; | 2317 | return MHD_DAUTH_WRONG_URI; |
2216 | digest_calc_hash (da, hash2_bin); | 2318 | digest_calc_hash (&da, hash2_bin); |
2217 | /* Got H(A2) */ | 2319 | /* Got H(A2) */ |
2218 | 2320 | ||
2219 | /* ** Build H(A1) ** */ | 2321 | /* ** Build H(A1) ** */ |
2220 | if (NULL == digest) | 2322 | if (NULL == userdigest) |
2221 | { | 2323 | { |
2222 | digest_init (da); | 2324 | digest_init (&da); |
2223 | digest_update (da, (const uint8_t *) username, username_len); | 2325 | digest_update (&da, (const uint8_t *) username, username_len); |
2224 | digest_update_with_colon (da); | 2326 | digest_update_with_colon (&da); |
2225 | digest_update (da, (const uint8_t *) realm, realm_len); | 2327 | digest_update (&da, (const uint8_t *) realm, realm_len); |
2226 | digest_update_with_colon (da); | 2328 | digest_update_with_colon (&da); |
2227 | digest_update_str (da, password); | 2329 | digest_update_str (&da, password); |
2228 | digest_calc_hash (da, hash1_bin); | 2330 | digest_calc_hash (&da, hash1_bin); |
2229 | } | 2331 | } |
2230 | /* TODO: support '-sess' versions */ | 2332 | /* TODO: support '-sess' versions */ |
2231 | /* Got H(A1) */ | 2333 | /* Got H(A1) */ |
2232 | 2334 | ||
2233 | /* ** Check 'response' ** */ | 2335 | /* ** Check 'response' ** */ |
2234 | 2336 | ||
2235 | digest_init (da); | 2337 | digest_init (&da); |
2236 | /* Update digest with H(A1) */ | 2338 | /* Update digest with H(A1) */ |
2237 | mhd_assert (sizeof (tmp1) >= (digest_size * 2 + 1)); | 2339 | mhd_assert (sizeof (tmp1) >= (digest_size * 2 + 1)); |
2238 | if (NULL == digest) | 2340 | if (NULL == userdigest) |
2239 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); | 2341 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); |
2240 | else | 2342 | else |
2241 | MHD_bin_to_hex (digest, digest_size, tmp1); | 2343 | MHD_bin_to_hex (userdigest, digest_size, tmp1); |
2242 | digest_update (da, (const uint8_t *) tmp1, digest_size * 2); | 2344 | digest_update (&da, (const uint8_t *) tmp1, digest_size * 2); |
2243 | 2345 | ||
2244 | /* H(A1) is not needed anymore, reuse the buffer. | 2346 | /* H(A1) is not needed anymore, reuse the buffer. |
2245 | * Use hash1_bin for the client's 'response' decoded to binary form. */ | 2347 | * Use hash1_bin for the client's 'response' decoded to binary form. */ |
@@ -2251,46 +2353,46 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2251 | return MHD_DAUTH_RESPONSE_WRONG; | 2353 | return MHD_DAUTH_RESPONSE_WRONG; |
2252 | 2354 | ||
2253 | /* Update digest with ':' */ | 2355 | /* Update digest with ':' */ |
2254 | digest_update_with_colon (da); | 2356 | digest_update_with_colon (&da); |
2255 | /* Update digest with 'nonce' text value */ | 2357 | /* Update digest with 'nonce' text value */ |
2256 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, | 2358 | unq_res = get_unquoted_param (¶ms->nonce, tmp1, ptmp2, &tmp2_size, |
2257 | &unquoted); | 2359 | &unquoted); |
2258 | if (_MHD_UNQ_OK != unq_res) | 2360 | if (_MHD_UNQ_OK != unq_res) |
2259 | return MHD_DAUTH_ERROR; | 2361 | return MHD_DAUTH_ERROR; |
2260 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2362 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2261 | /* Update digest with ':' */ | 2363 | /* Update digest with ':' */ |
2262 | digest_update_with_colon (da); | 2364 | digest_update_with_colon (&da); |
2263 | /* Update digest with 'nc' text value */ | 2365 | /* Update digest with 'nc' text value */ |
2264 | unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, | 2366 | unq_res = get_unquoted_param (¶ms->nc, tmp1, ptmp2, &tmp2_size, |
2265 | &unquoted); | 2367 | &unquoted); |
2266 | if (_MHD_UNQ_OK != unq_res) | 2368 | if (_MHD_UNQ_OK != unq_res) |
2267 | return MHD_DAUTH_ERROR; | 2369 | return MHD_DAUTH_ERROR; |
2268 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2370 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2269 | /* Update digest with ':' */ | 2371 | /* Update digest with ':' */ |
2270 | digest_update_with_colon (da); | 2372 | digest_update_with_colon (&da); |
2271 | /* Update digest with 'cnonce' value */ | 2373 | /* Update digest with 'cnonce' value */ |
2272 | unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, | 2374 | unq_res = get_unquoted_param (¶ms->cnonce, tmp1, ptmp2, &tmp2_size, |
2273 | &unquoted); | 2375 | &unquoted); |
2274 | if (_MHD_UNQ_OK != unq_res) | 2376 | if (_MHD_UNQ_OK != unq_res) |
2275 | return MHD_DAUTH_ERROR; | 2377 | return MHD_DAUTH_ERROR; |
2276 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2378 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2277 | /* Update digest with ':' */ | 2379 | /* Update digest with ':' */ |
2278 | digest_update_with_colon (da); | 2380 | digest_update_with_colon (&da); |
2279 | /* Update digest with 'qop' value */ | 2381 | /* Update digest with 'qop' value */ |
2280 | unq_res = get_unquoted_param (¶ms->qop, tmp1, ptmp2, &tmp2_size, | 2382 | unq_res = get_unquoted_param (¶ms->qop, tmp1, ptmp2, &tmp2_size, |
2281 | &unquoted); | 2383 | &unquoted); |
2282 | if (_MHD_UNQ_OK != unq_res) | 2384 | if (_MHD_UNQ_OK != unq_res) |
2283 | return MHD_DAUTH_ERROR; | 2385 | return MHD_DAUTH_ERROR; |
2284 | digest_update (da, (const uint8_t *) unquoted.str, unquoted.len); | 2386 | digest_update (&da, (const uint8_t *) unquoted.str, unquoted.len); |
2285 | /* Update digest with ':' */ | 2387 | /* Update digest with ':' */ |
2286 | digest_update_with_colon (da); | 2388 | digest_update_with_colon (&da); |
2287 | /* Update digest with H(A2) */ | 2389 | /* Update digest with H(A2) */ |
2288 | MHD_bin_to_hex (hash2_bin, digest_size, tmp1); | 2390 | MHD_bin_to_hex (hash2_bin, digest_size, tmp1); |
2289 | digest_update (da, (const uint8_t *) tmp1, digest_size * 2); | 2391 | digest_update (&da, (const uint8_t *) tmp1, digest_size * 2); |
2290 | 2392 | ||
2291 | /* H(A2) is not needed anymore, reuse the buffer. | 2393 | /* H(A2) is not needed anymore, reuse the buffer. |
2292 | * Use hash2_bin for the calculated response in binary form */ | 2394 | * Use hash2_bin for the calculated response in binary form */ |
2293 | digest_calc_hash (da, hash2_bin); | 2395 | digest_calc_hash (&da, hash2_bin); |
2294 | 2396 | ||
2295 | if (0 != memcmp (hash1_bin, hash2_bin, digest_size)) | 2397 | if (0 != memcmp (hash1_bin, hash2_bin, digest_size)) |
2296 | return MHD_DAUTH_RESPONSE_WRONG; | 2398 | return MHD_DAUTH_RESPONSE_WRONG; |
@@ -2307,7 +2409,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2307 | connection->headers_received, | 2409 | connection->headers_received, |
2308 | realm, | 2410 | realm, |
2309 | realm_len, | 2411 | realm_len, |
2310 | da, | 2412 | &da, |
2311 | tmp1); | 2413 | tmp1); |
2312 | 2414 | ||
2313 | if (! is_param_equal (¶ms->nonce, tmp1, | 2415 | if (! is_param_equal (¶ms->nonce, tmp1, |
@@ -2322,37 +2424,46 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2322 | /** | 2424 | /** |
2323 | * Authenticates the authorization header sent by the client | 2425 | * Authenticates the authorization header sent by the client |
2324 | * | 2426 | * |
2325 | * @param connection The MHD connection structure | 2427 | * @param connection the MHD connection structure |
2326 | * @param[in,out] da digest algorithm to use for checking (written to as | 2428 | * @param realm the realm presented to the client |
2327 | * part of the calculations, but the values left in the struct | 2429 | * @param username the username needs to be authenticated |
2328 | * are not actually expected to be useful for the caller) | 2430 | * @param password the password used in the authentication |
2329 | * @param realm The realm presented to the client | 2431 | * @param userdigest the optional precalculated binary hash of the string |
2330 | * @param username The username needs to be authenticated | 2432 | * "username:realm:password" |
2331 | * @param password The password used in the authentication | 2433 | * @param nonce_timeout the period of seconds since nonce generation, when |
2332 | * @param digest An optional binary hash | 2434 | * the nonce is recognised as valid and not stale. |
2333 | * of the precalculated hash value "username:realm:password" | 2435 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
2334 | * (must contain "digest_get_size(da)" bytes or be NULL) | 2436 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is |
2335 | * @param nonce_timeout The amount of time for a nonce to be | 2437 | * returned; |
2336 | * invalid in seconds | 2438 | * zero for no limit |
2439 | * @param mqop the QOP to use, currently the only allowed value is | ||
2440 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2441 | * @param malgo3 digest algorithms to use, if several algorithms are specified | ||
2442 | * then MD5 is used (if allowed) | ||
2337 | * @return #MHD_DAUTH_OK if authenticated, | 2443 | * @return #MHD_DAUTH_OK if authenticated, |
2338 | * error code otherwise. | 2444 | * error code otherwise. |
2339 | * @ingroup authentication | 2445 | * @ingroup authentication |
2340 | */ | 2446 | */ |
2341 | static enum MHD_DigestAuthResult | 2447 | static enum MHD_DigestAuthResult |
2342 | digest_auth_check_all (struct MHD_Connection *connection, | 2448 | digest_auth_check_all (struct MHD_Connection *connection, |
2343 | struct DigestAlgorithm *da, | ||
2344 | const char *realm, | 2449 | const char *realm, |
2345 | const char *username, | 2450 | const char *username, |
2346 | const char *password, | 2451 | const char *password, |
2347 | const uint8_t *digest, | 2452 | const uint8_t *userdigest, |
2348 | unsigned int nonce_timeout) | 2453 | unsigned int nonce_timeout, |
2454 | uint32_t max_nc, | ||
2455 | enum MHD_DigestAuthMultiQOP mqop, | ||
2456 | enum MHD_DigestAuthMultiAlgo3 malgo3) | ||
2349 | { | 2457 | { |
2350 | enum MHD_DigestAuthResult res; | 2458 | enum MHD_DigestAuthResult res; |
2351 | char *buf; | 2459 | char *buf; |
2352 | 2460 | ||
2353 | buf = NULL; | 2461 | buf = NULL; |
2354 | res = digest_auth_check_all_inner (connection, da, realm, username, password, | 2462 | res = digest_auth_check_all_inner (connection, realm, username, password, |
2355 | digest, nonce_timeout, &buf); | 2463 | userdigest, |
2464 | nonce_timeout, | ||
2465 | max_nc, mqop, malgo3, | ||
2466 | &buf); | ||
2356 | if (NULL != buf) | 2467 | if (NULL != buf) |
2357 | free (buf); | 2468 | free (buf); |
2358 | 2469 | ||
@@ -2402,10 +2513,17 @@ MHD_digest_auth_check (struct MHD_Connection *connection, | |||
2402 | * @param username the username needs to be authenticated | 2513 | * @param username the username needs to be authenticated |
2403 | * @param password the password used in the authentication | 2514 | * @param password the password used in the authentication |
2404 | * @param nonce_timeout the nonce validity duration in seconds | 2515 | * @param nonce_timeout the nonce validity duration in seconds |
2405 | * @param algo the digest algorithms allowed for verification | 2516 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
2517 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is | ||
2518 | * returned; | ||
2519 | * zero for no limit | ||
2520 | * @param mqop the QOP to use, currently the only allowed value is | ||
2521 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2522 | * @param malgo3 digest algorithm to use, if several algorithms are specified | ||
2523 | * then MD5 is used (if allowed) | ||
2406 | * @return #MHD_DAUTH_OK if authenticated, | 2524 | * @return #MHD_DAUTH_OK if authenticated, |
2407 | * the error code otherwise | 2525 | * the error code otherwise |
2408 | * @note Available since #MHD_VERSION 0x00097513 | 2526 | * @note Available since #MHD_VERSION 0x00097526 |
2409 | * @ingroup authentication | 2527 | * @ingroup authentication |
2410 | */ | 2528 | */ |
2411 | _MHD_EXTERN enum MHD_DigestAuthResult | 2529 | _MHD_EXTERN enum MHD_DigestAuthResult |
@@ -2414,87 +2532,84 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
2414 | const char *username, | 2532 | const char *username, |
2415 | const char *password, | 2533 | const char *password, |
2416 | unsigned int nonce_timeout, | 2534 | unsigned int nonce_timeout, |
2417 | enum MHD_DigestAuthAlgorithm algo) | 2535 | uint32_t max_nc, |
2536 | enum MHD_DigestAuthMultiQOP mqop, | ||
2537 | enum MHD_DigestAuthMultiAlgo3 malgo3) | ||
2418 | { | 2538 | { |
2419 | struct DigestAlgorithm da; | ||
2420 | |||
2421 | mhd_assert (NULL != password); | 2539 | mhd_assert (NULL != password); |
2422 | 2540 | ||
2423 | if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | ||
2424 | { | ||
2425 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5)) | ||
2426 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2427 | } | ||
2428 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
2429 | { | ||
2430 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2431 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2432 | } | ||
2433 | else | ||
2434 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | ||
2435 | |||
2436 | return digest_auth_check_all (connection, | 2541 | return digest_auth_check_all (connection, |
2437 | &da, | ||
2438 | realm, | 2542 | realm, |
2439 | username, | 2543 | username, |
2440 | password, | 2544 | password, |
2441 | NULL, | 2545 | NULL, |
2442 | nonce_timeout); | 2546 | nonce_timeout, |
2547 | max_nc, | ||
2548 | mqop, | ||
2549 | malgo3); | ||
2443 | } | 2550 | } |
2444 | 2551 | ||
2445 | 2552 | ||
2446 | /** | 2553 | /** |
2447 | * Authenticates the authorization header sent by the client. | 2554 | * Authenticates the authorization header sent by the client by using |
2555 | * hash of "username:realm:password". | ||
2448 | * | 2556 | * |
2449 | * @param connection the MHD connection structure | 2557 | * @param connection the MHD connection structure |
2450 | * @param realm the realm to be used for authorization of the client | 2558 | * @param realm the realm presented to the client |
2451 | * @param username the username needs to be authenticated | 2559 | * @param username the username needs to be authenticated |
2452 | * @param digest the pointer to the binary digest for the precalculated hash | 2560 | * @param userdigest the precalculated binary hash of the string |
2453 | * value "username:realm:password" with specified @a algo | 2561 | * "username:realm:password" |
2454 | * @param digest_size the number of bytes in @a digest (the size must match | 2562 | * @param userdigest_size the size of the @a userdigest in bytes, must match the |
2455 | * @a algo!) | 2563 | * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, |
2456 | * @param nonce_timeout the nonce validity duration in seconds | 2564 | * #MHD_SHA256_DIGEST_SIZE) |
2457 | * @param algo digest algorithms allowed for verification | 2565 | * @param nonce_timeout the period of seconds since nonce generation, when |
2566 | * the nonce is recognised as valid and not stale. | ||
2567 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc | ||
2568 | * exceeds the specified value then MHD_DAUTH_NONCE_STALE is | ||
2569 | * returned; | ||
2570 | * zero for no limit | ||
2571 | * @param mqop the QOP to use, currently the only allowed value is | ||
2572 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2573 | * @param malgo3 the digest algorithms to use; both MD5-based and SHA-256-based | ||
2574 | * algorithms cannot be used at the same time for this function | ||
2575 | * as @a userdigest_size must match specified algorithm | ||
2458 | * @return #MHD_DAUTH_OK if authenticated, | 2576 | * @return #MHD_DAUTH_OK if authenticated, |
2459 | * the error code otherwise | 2577 | * the error code otherwise |
2460 | * @note Available since #MHD_VERSION 0x00097513 | 2578 | * @note Available since #MHD_VERSION 0x00097526 |
2461 | * @ingroup authentication | 2579 | * @ingroup authentication |
2462 | */ | 2580 | */ |
2463 | _MHD_EXTERN enum MHD_DigestAuthResult | 2581 | _MHD_EXTERN enum MHD_DigestAuthResult |
2464 | MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, | 2582 | MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, |
2465 | const char *realm, | 2583 | const char *realm, |
2466 | const char *username, | 2584 | const char *username, |
2467 | const uint8_t *digest, | 2585 | const void *userdigest, |
2468 | size_t digest_size, | 2586 | size_t userdigest_size, |
2469 | unsigned int nonce_timeout, | 2587 | unsigned int nonce_timeout, |
2470 | enum MHD_DigestAuthAlgorithm algo) | 2588 | uint32_t max_nc, |
2589 | enum MHD_DigestAuthMultiQOP mqop, | ||
2590 | enum MHD_DigestAuthMultiAlgo3 malgo3) | ||
2471 | { | 2591 | { |
2472 | struct DigestAlgorithm da; | 2592 | if (((unsigned int) (MHD_DIGEST_BASE_ALGO_MD5 |
2473 | 2593 | | MHD_DIGEST_BASE_ALGO_SHA256)) == | |
2474 | mhd_assert (NULL != digest); | 2594 | (((unsigned int) malgo3) & (MHD_DIGEST_BASE_ALGO_MD5 |
2475 | if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | 2595 | | MHD_DIGEST_BASE_ALGO_SHA256))) |
2476 | { | 2596 | MHD_PANIC (_ ("Wrong 'malgo3' value, both MD5 and SHA-256 specified, " |
2477 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5)) | 2597 | "API violation")); |
2478 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | 2598 | |
2479 | } | 2599 | if (digest_get_hash_size ((enum MHD_DigestAuthAlgo3) malgo3) != |
2480 | else if (MHD_DIGEST_ALG_SHA256 == algo) | 2600 | userdigest_size) |
2481 | { | 2601 | MHD_PANIC (_ ("Wrong 'userdigest_size' value, not matching 'malgo3, " |
2482 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256)) | 2602 | "API violation")); |
2483 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2484 | } | ||
2485 | else | ||
2486 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | ||
2487 | |||
2488 | if (digest_get_size (&da) != digest_size) | ||
2489 | MHD_PANIC (_ ("Digest size mismatch.\n")); /* API violation! */ | ||
2490 | 2603 | ||
2491 | return digest_auth_check_all (connection, | 2604 | return digest_auth_check_all (connection, |
2492 | &da, | ||
2493 | realm, | 2605 | realm, |
2494 | username, | 2606 | username, |
2495 | NULL, | 2607 | NULL, |
2496 | digest, | 2608 | (const uint8_t *) userdigest, |
2497 | nonce_timeout); | 2609 | nonce_timeout, |
2610 | max_nc, | ||
2611 | mqop, | ||
2612 | malgo3); | ||
2498 | } | 2613 | } |
2499 | 2614 | ||
2500 | 2615 | ||
@@ -2523,12 +2638,24 @@ MHD_digest_auth_check2 (struct MHD_Connection *connection, | |||
2523 | enum MHD_DigestAuthAlgorithm algo) | 2638 | enum MHD_DigestAuthAlgorithm algo) |
2524 | { | 2639 | { |
2525 | enum MHD_DigestAuthResult res; | 2640 | enum MHD_DigestAuthResult res; |
2641 | enum MHD_DigestAuthMultiAlgo3 malgo3; | ||
2642 | |||
2643 | if (MHD_DIGEST_ALG_AUTO == algo) | ||
2644 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; | ||
2645 | else if (MHD_DIGEST_ALG_MD5 == algo) | ||
2646 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; | ||
2647 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
2648 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; | ||
2649 | else | ||
2650 | MHD_PANIC (_ ("Wrong 'algo' value, API violation")); | ||
2651 | |||
2526 | res = MHD_digest_auth_check3 (connection, | 2652 | res = MHD_digest_auth_check3 (connection, |
2527 | realm, | 2653 | realm, |
2528 | username, | 2654 | username, |
2529 | password, | 2655 | password, |
2530 | nonce_timeout, | 2656 | nonce_timeout, |
2531 | algo); | 2657 | 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, |
2658 | malgo3); | ||
2532 | if (MHD_DAUTH_OK == res) | 2659 | if (MHD_DAUTH_OK == res) |
2533 | return MHD_YES; | 2660 | return MHD_YES; |
2534 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) | 2661 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) |
@@ -2567,6 +2694,16 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection *connection, | |||
2567 | enum MHD_DigestAuthAlgorithm algo) | 2694 | enum MHD_DigestAuthAlgorithm algo) |
2568 | { | 2695 | { |
2569 | enum MHD_DigestAuthResult res; | 2696 | enum MHD_DigestAuthResult res; |
2697 | enum MHD_DigestAuthMultiAlgo3 malgo3; | ||
2698 | |||
2699 | if (MHD_DIGEST_ALG_AUTO == algo) | ||
2700 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; | ||
2701 | else if (MHD_DIGEST_ALG_MD5 == algo) | ||
2702 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; | ||
2703 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
2704 | malgo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; | ||
2705 | else | ||
2706 | MHD_PANIC (_ ("Wrong 'algo' value, API violation")); | ||
2570 | 2707 | ||
2571 | res = MHD_digest_auth_check_digest3 (connection, | 2708 | res = MHD_digest_auth_check_digest3 (connection, |
2572 | realm, | 2709 | realm, |
@@ -2574,7 +2711,8 @@ MHD_digest_auth_check_digest2 (struct MHD_Connection *connection, | |||
2574 | digest, | 2711 | digest, |
2575 | digest_size, | 2712 | digest_size, |
2576 | nonce_timeout, | 2713 | nonce_timeout, |
2577 | algo); | 2714 | 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, |
2715 | malgo3); | ||
2578 | if (MHD_DAUTH_OK == res) | 2716 | if (MHD_DAUTH_OK == res) |
2579 | return MHD_YES; | 2717 | return MHD_YES; |
2580 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) | 2718 | else if ((MHD_DAUTH_NONCE_STALE == res) || (MHD_DAUTH_NONCE_WRONG == res)) |
@@ -2622,47 +2760,94 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection, | |||
2622 | /** | 2760 | /** |
2623 | * Queues a response to request authentication from the client | 2761 | * Queues a response to request authentication from the client |
2624 | * | 2762 | * |
2625 | * @param connection The MHD connection structure | 2763 | * This function modifies provided @a response. The @a response must not be |
2764 | * reused and should be destroyed (by #MHD_destroy_response()) after call of | ||
2765 | * this function. | ||
2766 | * | ||
2767 | * @param connection the MHD connection structure | ||
2626 | * @param realm the realm presented to the client | 2768 | * @param realm the realm presented to the client |
2627 | * @param opaque string to user for opaque value | 2769 | * @param opaque the string for opaque value, can be NULL, but NULL is |
2628 | * @param response reply to send; should contain the "access denied" | 2770 | * not recommended for better compatibility with clients |
2629 | * body; note that this function will set the "WWW Authenticate" | 2771 | * @param domain the optional space-separated list of URIs for which the |
2630 | * header and that the caller should not do this; the NULL is tolerated | 2772 | * same authorisation could be used, URIs can be in form |
2631 | * @param signal_stale #MHD_YES if the nonce is stale to add | 2773 | * "path-absolute" (the path for the same host with initial slash) |
2632 | * 'stale=true' to the authentication header | 2774 | * or in form "absolute-URI" (the full path with protocol), in |
2633 | * @param algo digest algorithm to use | 2775 | * any case client may assume that any URI which starts with |
2776 | * any of specified URI is in the same "protection space"; | ||
2777 | * could be NULL (clients typically assume that the same | ||
2778 | * credentials could be used for any URI on the same host) | ||
2779 | * @param response the reply to send; should contain the "access denied" | ||
2780 | * body; note that this function sets the "WWW Authenticate" | ||
2781 | * header and that the caller should not do this; | ||
2782 | * the NULL is tolerated | ||
2783 | * @param signal_stale set to #MHD_YES if the nonce is stale to add 'stale=true' | ||
2784 | * to the authentication header, this instructs the client | ||
2785 | * to retry immediately with the new nonce and the same | ||
2786 | * credentials, without asking user for the new password | ||
2787 | * @param mqop the QOP to use, currently the only allowed value is | ||
2788 | * #MHD_DIGEST_AUTH_MULT_QOP_AUTH | ||
2789 | * @param malgo3 digest algorithm to use, if several algorithms are specified | ||
2790 | * then MD5 is used (if allowed) | ||
2791 | * @param userhash_support if set to non-zero value (#MHD_YES) then support of | ||
2792 | * userhash is indicated, the client may provide | ||
2793 | * hash("username:realm") instead of username in | ||
2794 | * clear text; note that client is allowed to provide | ||
2795 | * the username in cleartext even if this parameter set | ||
2796 | * to non-zero | ||
2797 | * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is | ||
2798 | * added, indicating for the client that UTF-8 encoding | ||
2799 | * is preferred | ||
2634 | * @return #MHD_YES on success, #MHD_NO otherwise | 2800 | * @return #MHD_YES on success, #MHD_NO otherwise |
2635 | * @note Available since #MHD_VERSION 0x00096200 | 2801 | * @note Available since #MHD_VERSION 0x00097526 |
2636 | * @ingroup authentication | 2802 | * @ingroup authentication |
2637 | */ | 2803 | */ |
2638 | _MHD_EXTERN enum MHD_Result | 2804 | _MHD_EXTERN enum MHD_Result |
2639 | MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | 2805 | MHD_queue_auth_required_response3 (struct MHD_Connection *connection, |
2640 | const char *realm, | 2806 | const char *realm, |
2641 | const char *opaque, | 2807 | const char *opaque, |
2642 | struct MHD_Response *response, | 2808 | const char *domain, |
2643 | int signal_stale, | 2809 | struct MHD_Response *response, |
2644 | enum MHD_DigestAuthAlgorithm algo) | 2810 | int signal_stale, |
2811 | enum MHD_DigestAuthMultiQOP mqop, | ||
2812 | enum MHD_DigestAuthMultiAlgo3 malgo3, | ||
2813 | int userhash_support, | ||
2814 | int prefer_utf8) | ||
2645 | { | 2815 | { |
2646 | enum MHD_Result ret; | 2816 | static const char prefix_realm[] = "realm=\""; |
2647 | int hlen; | 2817 | static const char prefix_qop[] = "qop=\""; |
2648 | 2818 | static const char prefix_algo[] = "algorithm="; | |
2819 | static const char prefix_nonce[] = "nonce=\""; | ||
2820 | static const char prefix_opaque[] = "opaque=\""; | ||
2821 | static const char prefix_domain[] = "domain=\""; | ||
2822 | static const char str_charset[] = "charset=UTF-8"; | ||
2823 | static const char str_userhash[] = "userhash=true"; | ||
2824 | static const char str_stale[] = "stale=true"; | ||
2825 | enum MHD_DigestAuthAlgo3 s_algo; /**< Selected algorithm */ | ||
2826 | size_t realm_len; | ||
2827 | size_t opaque_len; | ||
2828 | size_t domain_len; | ||
2829 | size_t buf_size; | ||
2830 | char *buf; | ||
2831 | size_t p; /* The position in the buffer */ | ||
2649 | struct DigestAlgorithm da; | 2832 | struct DigestAlgorithm da; |
2650 | 2833 | ||
2651 | if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | 2834 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_AUTH_ALGO3_SESSION)) |
2652 | { | 2835 | { |
2653 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_MD5)) | 2836 | #ifdef HAVE_MESSAGES |
2654 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | 2837 | MHD_DLOG (connection->daemon, |
2655 | } | 2838 | _ ("The 'session' algorithms are not supported.\n")); |
2656 | else if (MHD_DIGEST_ALG_SHA256 == algo) | 2839 | #endif /* HAVE_MESSAGES */ |
2657 | { | 2840 | return MHD_NO; |
2658 | if (! digest_setup (&da, MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2659 | MHD_PANIC (_ ("Error initialising hash algorithm.\n")); | ||
2660 | } | 2841 | } |
2842 | if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_MD5)) | ||
2843 | s_algo = MHD_DIGEST_AUTH_ALGO3_MD5; | ||
2844 | else if (0 != (((unsigned int) malgo3) & MHD_DIGEST_BASE_ALGO_SHA256)) | ||
2845 | s_algo = MHD_DIGEST_AUTH_ALGO3_SHA256; | ||
2661 | else | 2846 | else |
2662 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | 2847 | MHD_PANIC (_ ("Wrong 'malgo3' value, API violation")); |
2663 | 2848 | ||
2664 | if (NULL == response) | 2849 | if (MHD_DIGEST_AUTH_MULT_QOP_AUTH != mqop) |
2665 | return MHD_NO; | 2850 | MHD_PANIC (_ ("Wrong 'mqop' value, API violation")); |
2666 | 2851 | ||
2667 | if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) | 2852 | if (0 == MHD_get_master (connection->daemon)->nonce_nc_size) |
2668 | { | 2853 | { |
@@ -2673,89 +2858,256 @@ MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | |||
2673 | return MHD_NO; | 2858 | return MHD_NO; |
2674 | } | 2859 | } |
2675 | 2860 | ||
2676 | if (1) | 2861 | if (! digest_setup (&da, get_base_digest_algo (s_algo))) |
2677 | { | 2862 | MHD_PANIC (_ ("Wrong 'algo' value, API violation")); |
2678 | char nonce[NONCE_STD_LEN (MAX_DIGEST) + 1]; | ||
2679 | 2863 | ||
2680 | /* VLA_CHECK_LEN_DIGEST (digest_get_size (&da)); */ | 2864 | /* Calculate required size */ |
2681 | if (! calculate_add_nonce_with_retry (connection, realm, &da, nonce)) | 2865 | buf_size = 0; |
2682 | { | 2866 | /* 'Digest ' */ |
2683 | #ifdef HAVE_MESSAGES | 2867 | buf_size += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE) + 1; /* 1 for ' ' */ |
2684 | MHD_DLOG (connection->daemon, | 2868 | buf_size += MHD_STATICSTR_LEN_ (prefix_realm) + 3; /* 3 for '", ' */ |
2685 | _ ("Could not register nonce. Client's requests with this " | 2869 | /* 'realm="xxxx", ' */ |
2686 | "nonce will be always 'stale'. Probably clients' requests " | 2870 | realm_len = strlen (realm); |
2687 | "are too intensive.\n")); | 2871 | if (_MHD_AUTH_DIGEST_MAX_PARAM_SIZE < realm_len) |
2688 | #else /* ! HAVE_MESSAGES */ | 2872 | return MHD_NO; |
2689 | (void) 0; | 2873 | if ((NULL != memchr (realm, '\r', realm_len)) || |
2690 | #endif /* ! HAVE_MESSAGES */ | 2874 | (NULL != memchr (realm, '\n', realm_len))) |
2691 | } | 2875 | return MHD_NO; |
2692 | /* Building the authentication header */ | 2876 | buf_size += realm_len * 2; /* Quoting may double the size */ |
2693 | hlen = MHD_snprintf_ (NULL, | 2877 | /* 'qop="xxxx", ' */ |
2694 | 0, | 2878 | buf_size += MHD_STATICSTR_LEN_ (prefix_qop) + 3; /* 3 for '", ' */ |
2695 | "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s", | 2879 | buf_size += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); |
2696 | realm, | 2880 | /* 'algorithm="xxxx", ' */ |
2697 | nonce, | 2881 | buf_size += MHD_STATICSTR_LEN_ (prefix_algo) + 2; /* 2 for ', ' */ |
2698 | opaque, | 2882 | if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) |
2699 | digest_get_algo_name (&da), | 2883 | buf_size += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); |
2700 | signal_stale | 2884 | else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) |
2701 | ? ",stale=\"true\"" | 2885 | buf_size += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); |
2702 | : ""); | 2886 | else |
2703 | if (hlen > 0) | 2887 | mhd_assert (0); |
2704 | { | 2888 | /* 'nonce="xxxx", ' */ |
2705 | char *header; | 2889 | buf_size += MHD_STATICSTR_LEN_ (prefix_nonce) + 3; /* 3 for '", ' */ |
2890 | buf_size += NONCE_STD_LEN (digest_get_size (&da)); /* Escaping not needed */ | ||
2891 | /* 'opaque="xxxx", ' */ | ||
2892 | if (NULL != opaque) | ||
2893 | { | ||
2894 | buf_size += MHD_STATICSTR_LEN_ (prefix_opaque) + 3; /* 3 for '", ' */ | ||
2895 | opaque_len = strlen (opaque); | ||
2896 | if ((NULL != memchr (opaque, '\r', opaque_len)) || | ||
2897 | (NULL != memchr (opaque, '\n', opaque_len))) | ||
2898 | return MHD_NO; | ||
2899 | buf_size += opaque_len * 2; /* Quoting may double the size */ | ||
2900 | } | ||
2901 | else | ||
2902 | opaque_len = 0; | ||
2903 | /* 'domain="xxxx", ' */ | ||
2904 | if (NULL != domain) | ||
2905 | { | ||
2906 | buf_size += MHD_STATICSTR_LEN_ (prefix_domain) + 3; /* 3 for '", ' */ | ||
2907 | domain_len = strlen (domain); | ||
2908 | if ((NULL != memchr (domain, '\r', domain_len)) || | ||
2909 | (NULL != memchr (domain, '\n', domain_len))) | ||
2910 | return MHD_NO; | ||
2911 | buf_size += domain_len * 2; /* Quoting may double the size */ | ||
2912 | } | ||
2913 | else | ||
2914 | domain_len = 0; | ||
2915 | /* 'charset=UTF-8' */ | ||
2916 | if (MHD_NO != prefer_utf8) | ||
2917 | buf_size += MHD_STATICSTR_LEN_ (str_charset) + 2; /* 2 for ', ' */ | ||
2918 | /* 'userhash=true' */ | ||
2919 | if (MHD_NO != userhash_support) | ||
2920 | buf_size += MHD_STATICSTR_LEN_ (str_userhash) + 2; /* 2 for ', ' */ | ||
2921 | /* 'stale=true' */ | ||
2922 | if (MHD_NO != signal_stale) | ||
2923 | buf_size += MHD_STATICSTR_LEN_ (str_stale) + 2; /* 2 for ', ' */ | ||
2924 | |||
2925 | /* The calculated length is for string ended with ", ". One character will | ||
2926 | * be used for zero-termination, the last one will not be used. */ | ||
2927 | |||
2928 | /* Allocate the buffer */ | ||
2929 | buf = malloc (buf_size); | ||
2930 | if (NULL == buf) | ||
2931 | return MHD_NO; | ||
2706 | 2932 | ||
2707 | header = MHD_calloc_ (1, | 2933 | /* Build the challenge string */ |
2708 | (size_t) hlen + 1); | 2934 | p = 0; |
2709 | if (NULL == header) | 2935 | /* 'Digest: ' */ |
2710 | { | 2936 | memcpy (buf + p, _MHD_AUTH_DIGEST_BASE, |
2937 | MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE)); | ||
2938 | p += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE); | ||
2939 | buf[p++] = ' '; | ||
2940 | /* 'realm="xxxx", ' */ | ||
2941 | memcpy (buf + p, prefix_realm, | ||
2942 | MHD_STATICSTR_LEN_ (prefix_realm)); | ||
2943 | p += MHD_STATICSTR_LEN_ (prefix_realm); | ||
2944 | mhd_assert ((buf_size - p) >= (realm_len * 2)); | ||
2945 | p += MHD_str_quote (realm, realm_len, buf + p, buf_size - p); | ||
2946 | buf[p++] = '\"'; | ||
2947 | buf[p++] = ','; | ||
2948 | buf[p++] = ' '; | ||
2949 | /* 'qop="xxxx", ' */ | ||
2950 | memcpy (buf + p, prefix_qop, | ||
2951 | MHD_STATICSTR_LEN_ (prefix_qop)); | ||
2952 | p += MHD_STATICSTR_LEN_ (prefix_qop); | ||
2953 | memcpy (buf + p, MHD_TOKEN_AUTH_, | ||
2954 | MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_)); | ||
2955 | p += MHD_STATICSTR_LEN_ (MHD_TOKEN_AUTH_); | ||
2956 | buf[p++] = '\"'; | ||
2957 | buf[p++] = ','; | ||
2958 | buf[p++] = ' '; | ||
2959 | /* 'algorithm="xxxx", ' */ | ||
2960 | memcpy (buf + p, prefix_algo, | ||
2961 | MHD_STATICSTR_LEN_ (prefix_algo)); | ||
2962 | p += MHD_STATICSTR_LEN_ (prefix_algo); | ||
2963 | if (MHD_DIGEST_AUTH_ALGO3_MD5 == s_algo) | ||
2964 | { | ||
2965 | memcpy (buf + p, _MHD_MD5_TOKEN, | ||
2966 | MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN)); | ||
2967 | p += MHD_STATICSTR_LEN_ (_MHD_MD5_TOKEN); | ||
2968 | } | ||
2969 | else if (MHD_DIGEST_AUTH_ALGO3_SHA256 == s_algo) | ||
2970 | { | ||
2971 | memcpy (buf + p, _MHD_SHA256_TOKEN, | ||
2972 | MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN)); | ||
2973 | p += MHD_STATICSTR_LEN_ (_MHD_SHA256_TOKEN); | ||
2974 | } | ||
2975 | buf[p++] = ','; | ||
2976 | buf[p++] = ' '; | ||
2977 | /* 'nonce="xxxx", ' */ | ||
2978 | memcpy (buf + p, prefix_nonce, | ||
2979 | MHD_STATICSTR_LEN_ (prefix_nonce)); | ||
2980 | p += MHD_STATICSTR_LEN_ (prefix_nonce); | ||
2981 | mhd_assert ((buf_size - p) >= (NONCE_STD_LEN (digest_get_size (&da)))); | ||
2982 | if (! calculate_add_nonce_with_retry (connection, realm, &da, buf + p)) | ||
2983 | { | ||
2711 | #ifdef HAVE_MESSAGES | 2984 | #ifdef HAVE_MESSAGES |
2712 | MHD_DLOG (connection->daemon, | 2985 | MHD_DLOG (connection->daemon, |
2713 | _ ("Failed to allocate memory for auth response header.\n")); | 2986 | _ ("Could not register nonce. Client's requests with this " |
2987 | "nonce will be always 'stale'. Probably clients' requests " | ||
2988 | "are too intensive.\n")); | ||
2714 | #endif /* HAVE_MESSAGES */ | 2989 | #endif /* HAVE_MESSAGES */ |
2715 | return MHD_NO; | 2990 | (void) 0; /* Mute compiler warning for builds without messages */ |
2716 | } | ||
2717 | |||
2718 | if (MHD_snprintf_ (header, | ||
2719 | (size_t) hlen + 1, | ||
2720 | "Digest realm=\"%s\",qop=\"auth\",nonce=\"%s\",opaque=\"%s\",algorithm=%s%s", | ||
2721 | realm, | ||
2722 | nonce, | ||
2723 | opaque, | ||
2724 | digest_get_algo_name (&da), | ||
2725 | signal_stale | ||
2726 | ? ",stale=\"true\"" | ||
2727 | : "") == hlen) | ||
2728 | ret = MHD_add_response_header (response, | ||
2729 | MHD_HTTP_HEADER_WWW_AUTHENTICATE, | ||
2730 | header); | ||
2731 | else | ||
2732 | ret = MHD_NO; | ||
2733 | #if 0 | ||
2734 | if ( (MHD_NO != ret) && (AND in state : 100 continue aborting ...)) | ||
2735 | ret = MHD_add_response_header (response, | ||
2736 | MHD_HTTP_HEADER_CONNECTION, | ||
2737 | "close"); | ||
2738 | #endif | ||
2739 | free (header); | ||
2740 | } | ||
2741 | else | ||
2742 | ret = MHD_NO; | ||
2743 | } | 2991 | } |
2744 | 2992 | p += NONCE_STD_LEN (digest_get_size (&da)); | |
2745 | if (MHD_NO != ret) | 2993 | buf[p++] = '\"'; |
2994 | buf[p++] = ','; | ||
2995 | buf[p++] = ' '; | ||
2996 | /* 'opaque="xxxx", ' */ | ||
2997 | if (NULL != opaque) | ||
2746 | { | 2998 | { |
2747 | ret = MHD_queue_response (connection, | 2999 | memcpy (buf + p, prefix_opaque, |
2748 | MHD_HTTP_UNAUTHORIZED, | 3000 | MHD_STATICSTR_LEN_ (prefix_opaque)); |
2749 | response); | 3001 | p += MHD_STATICSTR_LEN_ (prefix_opaque); |
3002 | mhd_assert ((buf_size - p) >= (opaque_len * 2)); | ||
3003 | p += MHD_str_quote (opaque, opaque_len, buf + p, buf_size - p); | ||
3004 | buf[p++] = '\"'; | ||
3005 | buf[p++] = ','; | ||
3006 | buf[p++] = ' '; | ||
2750 | } | 3007 | } |
2751 | else | 3008 | /* 'domain="xxxx", ' */ |
3009 | if (NULL != domain) | ||
3010 | { | ||
3011 | memcpy (buf + p, prefix_domain, | ||
3012 | MHD_STATICSTR_LEN_ (prefix_domain)); | ||
3013 | p += MHD_STATICSTR_LEN_ (prefix_domain); | ||
3014 | mhd_assert ((buf_size - p) >= (domain_len * 2)); | ||
3015 | p += MHD_str_quote (domain, domain_len, buf + p, buf_size - p); | ||
3016 | buf[p++] = '\"'; | ||
3017 | buf[p++] = ','; | ||
3018 | buf[p++] = ' '; | ||
3019 | } | ||
3020 | /* 'charset=UTF-8' */ | ||
3021 | if (MHD_NO != prefer_utf8) | ||
3022 | { | ||
3023 | memcpy (buf + p, str_charset, | ||
3024 | MHD_STATICSTR_LEN_ (str_charset)); | ||
3025 | p += MHD_STATICSTR_LEN_ (str_charset); | ||
3026 | buf[p++] = ','; | ||
3027 | buf[p++] = ' '; | ||
3028 | } | ||
3029 | /* 'userhash=true' */ | ||
3030 | if (MHD_NO != userhash_support) | ||
3031 | { | ||
3032 | memcpy (buf + p, str_userhash, | ||
3033 | MHD_STATICSTR_LEN_ (str_userhash)); | ||
3034 | p += MHD_STATICSTR_LEN_ (str_userhash); | ||
3035 | buf[p++] = ','; | ||
3036 | buf[p++] = ' '; | ||
3037 | } | ||
3038 | /* 'stale=true' */ | ||
3039 | if (MHD_NO != signal_stale) | ||
3040 | { | ||
3041 | memcpy (buf + p, str_stale, | ||
3042 | MHD_STATICSTR_LEN_ (str_stale)); | ||
3043 | p += MHD_STATICSTR_LEN_ (str_stale); | ||
3044 | buf[p++] = ','; | ||
3045 | buf[p++] = ' '; | ||
3046 | } | ||
3047 | mhd_assert (buf_size >= p); | ||
3048 | /* The build string ends with ", ". Replace comma with zero-termination. */ | ||
3049 | --p; | ||
3050 | buf[--p] = 0; | ||
3051 | |||
3052 | if (! MHD_add_response_entry_no_check_ (response, MHD_HEADER_KIND, | ||
3053 | MHD_HTTP_HEADER_WWW_AUTHENTICATE, | ||
3054 | MHD_STATICSTR_LEN_ ( \ | ||
3055 | MHD_HTTP_HEADER_WWW_AUTHENTICATE), | ||
3056 | buf, p)) | ||
2752 | { | 3057 | { |
2753 | #ifdef HAVE_MESSAGES | 3058 | #ifdef HAVE_MESSAGES |
2754 | MHD_DLOG (connection->daemon, | 3059 | MHD_DLOG (connection->daemon, |
2755 | _ ("Failed to add Digest auth header.\n")); | 3060 | _ ("Failed to add Digest auth header.\n")); |
2756 | #endif /* HAVE_MESSAGES */ | 3061 | #endif /* HAVE_MESSAGES */ |
3062 | free (buf); | ||
3063 | return MHD_NO; | ||
2757 | } | 3064 | } |
2758 | return ret; | 3065 | free (buf); |
3066 | |||
3067 | return MHD_queue_response (connection, MHD_HTTP_UNAUTHORIZED, response); | ||
3068 | } | ||
3069 | |||
3070 | |||
3071 | /** | ||
3072 | * Queues a response to request authentication from the client | ||
3073 | * | ||
3074 | * @param connection The MHD connection structure | ||
3075 | * @param realm the realm presented to the client | ||
3076 | * @param opaque string to user for opaque value | ||
3077 | * @param response reply to send; should contain the "access denied" | ||
3078 | * body; note that this function will set the "WWW Authenticate" | ||
3079 | * header and that the caller should not do this; the NULL is tolerated | ||
3080 | * @param signal_stale #MHD_YES if the nonce is stale to add | ||
3081 | * 'stale=true' to the authentication header | ||
3082 | * @param algo digest algorithm to use | ||
3083 | * @return #MHD_YES on success, #MHD_NO otherwise | ||
3084 | * @note Available since #MHD_VERSION 0x00096200 | ||
3085 | * @ingroup authentication | ||
3086 | */ | ||
3087 | _MHD_EXTERN enum MHD_Result | ||
3088 | MHD_queue_auth_fail_response2 (struct MHD_Connection *connection, | ||
3089 | const char *realm, | ||
3090 | const char *opaque, | ||
3091 | struct MHD_Response *response, | ||
3092 | int signal_stale, | ||
3093 | enum MHD_DigestAuthAlgorithm algo) | ||
3094 | { | ||
3095 | enum MHD_DigestAuthMultiAlgo3 algo3; | ||
3096 | |||
3097 | if (MHD_DIGEST_ALG_MD5 == algo) | ||
3098 | algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_MD5; | ||
3099 | else if (MHD_DIGEST_ALG_SHA256 == algo) | ||
3100 | algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_SHA256; | ||
3101 | else if ((MHD_DIGEST_ALG_MD5 == algo) || (MHD_DIGEST_ALG_AUTO == algo)) | ||
3102 | algo3 = MHD_DIGEST_AUTH_MULT_ALGO3_ANY_NON_SESSION; | ||
3103 | else | ||
3104 | MHD_PANIC (_ ("Wrong algo value.\n")); /* API violation! */ | ||
3105 | |||
3106 | return MHD_queue_auth_required_response3 (connection, realm, opaque, | ||
3107 | NULL, response, signal_stale, | ||
3108 | MHD_DIGEST_AUTH_MULT_QOP_AUTH, | ||
3109 | algo3, | ||
3110 | 0, 0); | ||
2759 | } | 3111 | } |
2760 | 3112 | ||
2761 | 3113 | ||
diff --git a/src/testcurl/test_digestauth_concurrent.c b/src/testcurl/test_digestauth_concurrent.c index 9a2f6227..c80bf382 100644 --- a/src/testcurl/test_digestauth_concurrent.c +++ b/src/testcurl/test_digestauth_concurrent.c | |||
@@ -312,8 +312,9 @@ ahc_echo (void *cls, | |||
312 | realm, | 312 | realm, |
313 | username, | 313 | username, |
314 | password, | 314 | password, |
315 | 300, | 315 | 50 * TIMEOUTS_VAL, |
316 | MHD_DIGEST_ALG_MD5); | 316 | 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, |
317 | MHD_DIGEST_AUTH_MULT_ALGO3_MD5); | ||
317 | MHD_free (username); | 318 | MHD_free (username); |
318 | if (ret_e != MHD_DAUTH_OK) | 319 | if (ret_e != MHD_DAUTH_OK) |
319 | { | 320 | { |
diff --git a/src/testcurl/test_digestauth_emu_ext.c b/src/testcurl/test_digestauth_emu_ext.c index f4a0b9e8..468279ca 100644 --- a/src/testcurl/test_digestauth_emu_ext.c +++ b/src/testcurl/test_digestauth_emu_ext.c | |||
@@ -483,7 +483,9 @@ ahc_echo (void *cls, | |||
483 | 483 | ||
484 | check_res = MHD_digest_auth_check3 (connection, REALM, USERNAME, | 484 | check_res = MHD_digest_auth_check3 (connection, REALM, USERNAME, |
485 | PASSWORD_VALUE, | 485 | PASSWORD_VALUE, |
486 | 300, MHD_DIGEST_ALG_MD5); | 486 | 50 * TIMEOUTS_VAL, |
487 | 0, MHD_DIGEST_AUTH_MULT_QOP_AUTH, | ||
488 | MHD_DIGEST_AUTH_MULT_ALGO3_MD5); | ||
487 | 489 | ||
488 | switch (check_res) | 490 | switch (check_res) |
489 | { | 491 | { |