diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-08-26 09:09:31 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-09-04 12:25:06 +0300 |
commit | 0ccef4c16cb66f4c0a4ca3570dc536b8015c5231 (patch) | |
tree | d2ce5c215c79de3aa0dd86e86eac4e838b1263a1 | |
parent | 2a9071b862d8f4647fe22770a0dacdf595ea72fb (diff) | |
download | libmicrohttpd-0ccef4c16cb66f4c0a4ca3570dc536b8015c5231.tar.gz libmicrohttpd-0ccef4c16cb66f4c0a4ca3570dc536b8015c5231.zip |
Added new public functions for userhash and userdigest calculations
These functions can be used when the new user is added to the users
database.
-rw-r--r-- | src/include/microhttpd.h | 166 | ||||
-rw-r--r-- | src/microhttpd/digestauth.c | 282 |
2 files changed, 414 insertions, 34 deletions
diff --git a/src/include/microhttpd.h b/src/include/microhttpd.h index 1a23bc8b..450b65c5 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 0x00097534 | 99 | #define MHD_VERSION 0x00097535 |
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', |
@@ -4543,6 +4543,8 @@ enum MHD_DigestAuthAlgo3 | |||
4543 | * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or | 4543 | * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or |
4544 | * #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not | 4544 | * #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not |
4545 | * recognised/valid | 4545 | * recognised/valid |
4546 | * @sa #MHD_digest_auth_calc_userdigest() | ||
4547 | * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex() | ||
4546 | * @note Available since #MHD_VERSION 0x00097526 | 4548 | * @note Available since #MHD_VERSION 0x00097526 |
4547 | * @ingroup authentication | 4549 | * @ingroup authentication |
4548 | */ | 4550 | */ |
@@ -4642,6 +4644,97 @@ enum MHD_DigestAuthMultiAlgo3 | |||
4642 | (0x3F) | MHD_DIGEST_AUTH_ALGO3_NON_SESSION | MHD_DIGEST_AUTH_ALGO3_SESSION | 4644 | (0x3F) | MHD_DIGEST_AUTH_ALGO3_NON_SESSION | MHD_DIGEST_AUTH_ALGO3_SESSION |
4643 | }; | 4645 | }; |
4644 | 4646 | ||
4647 | |||
4648 | /** | ||
4649 | * Calculate "userhash", return it as binary data. | ||
4650 | * | ||
4651 | * The "userhash" is the hash of the string "username:realm". | ||
4652 | * | ||
4653 | * The "Userhash" could be used to avoid sending username in cleartext in Digest | ||
4654 | * Authorization client's header. | ||
4655 | * | ||
4656 | * Userhash is not designed to hide the username in local database or files, | ||
4657 | * as username in cleartext is required for #MHD_digest_auth_check3() function | ||
4658 | * to check the response, but it can be used to hide username in HTTP headers. | ||
4659 | * | ||
4660 | * This function could be used when the new username is added to the username | ||
4661 | * database to save the "userhash" alongside with the username (preferably) or | ||
4662 | * when loading list of the usernames to generate the userhash for every loaded | ||
4663 | * username (this will cause delays at the start with the long lists). | ||
4664 | * | ||
4665 | * Once "userhash" is generated it could be used to identify users for clients | ||
4666 | * with "userhash" support. | ||
4667 | * Avoid repetitive usage of this function for the same username/realm | ||
4668 | * combination as it will cause excessive CPU load; save and re-use the result | ||
4669 | * instead. | ||
4670 | * | ||
4671 | * @param algo3 the algorithm for userhash calculations | ||
4672 | * @param username the username | ||
4673 | * @param realm the realm | ||
4674 | * @param[out] userhash_bin the output buffer for userhash as binary data; | ||
4675 | * if this function succeeds, then this buffer has | ||
4676 | * #MHD_digest_get_hash_size(algo3) bytes of userhash | ||
4677 | * upon return | ||
4678 | * @param bin_buf_size the size of the @a userhash_bin buffer, must be | ||
4679 | * at least #MHD_digest_get_hash_size(algo3) bytes long | ||
4680 | * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or | ||
4681 | * if @a algo3 algorithm is not supported. | ||
4682 | * @note Available since #MHD_VERSION 0x00097535 | ||
4683 | * @ingroup authentication | ||
4684 | */ | ||
4685 | _MHD_EXTERN enum MHD_Result | ||
4686 | MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3, | ||
4687 | const char *username, | ||
4688 | const char *realm, | ||
4689 | void *userhash_bin, | ||
4690 | size_t bin_buf_size); | ||
4691 | |||
4692 | |||
4693 | /** | ||
4694 | * Calculate "userhash", return it as hexadecimal data. | ||
4695 | * | ||
4696 | * The "userhash" is the hash of the string "username:realm". | ||
4697 | * | ||
4698 | * The "Userhash" could be used to avoid sending username in cleartext in Digest | ||
4699 | * Authorization client's header. | ||
4700 | * | ||
4701 | * Userhash is not designed to hide the username in local database or files, | ||
4702 | * as username in cleartext is required for #MHD_digest_auth_check3() function | ||
4703 | * to check the response, but it can be used to hide username in HTTP headers. | ||
4704 | * | ||
4705 | * This function could be used when the new username is added to the username | ||
4706 | * database to save the "userhash" alongside with the username (preferably) or | ||
4707 | * when loading list of the usernames to generate the userhash for every loaded | ||
4708 | * username (this will cause delays at the start with the long lists). | ||
4709 | * | ||
4710 | * Once "userhash" is generated it could be used to identify users for clients | ||
4711 | * with "userhash" support. | ||
4712 | * Avoid repetitive usage of this function for the same username/realm | ||
4713 | * combination as it will cause excessive CPU load; save and re-use the result | ||
4714 | * instead. | ||
4715 | * | ||
4716 | * @param algo3 the algorithm for userhash calculations | ||
4717 | * @param username the username | ||
4718 | * @param realm the realm | ||
4719 | * @param[out] userhash_hex the output buffer for userhash as hex data; | ||
4720 | * if this function succeeds, then this buffer has | ||
4721 | * #MHD_digest_get_hash_size(algo3)*2 chars long | ||
4722 | * userhash string | ||
4723 | * @param bin_buf_size the size of the @a userhash_bin buffer, must be | ||
4724 | * at least #MHD_digest_get_hash_size(algo3)*2+1 chars long | ||
4725 | * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or | ||
4726 | * if @a algo3 algorithm is not supported. | ||
4727 | * @note Available since #MHD_VERSION 0x00097535 | ||
4728 | * @ingroup authentication | ||
4729 | */ | ||
4730 | _MHD_EXTERN enum MHD_Result | ||
4731 | MHD_digest_auth_calc_userhash_hex (enum MHD_DigestAuthAlgo3 algo3, | ||
4732 | const char *username, | ||
4733 | const char *realm, | ||
4734 | char *userhash_hex, | ||
4735 | size_t hex_buf_size); | ||
4736 | |||
4737 | |||
4645 | /** | 4738 | /** |
4646 | * The type of username used by client in Digest Authorization header | 4739 | * The type of username used by client in Digest Authorization header |
4647 | * | 4740 | * |
@@ -4671,6 +4764,7 @@ enum MHD_DigestAuthUsernameType | |||
4671 | /** | 4764 | /** |
4672 | * The username provided in form of 'userhash' as | 4765 | * The username provided in form of 'userhash' as |
4673 | * specified by RFC 7616 #section-3.4.4. | 4766 | * specified by RFC 7616 #section-3.4.4. |
4767 | * @sa #MHD_digest_auth_calc_userhash_hex(), #MHD_digest_auth_calc_userhash() | ||
4674 | */ | 4768 | */ |
4675 | MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = 3, | 4769 | MHD_DIGEST_AUTH_UNAME_TYPE_USERHASH = 3, |
4676 | 4770 | ||
@@ -4815,6 +4909,7 @@ struct MHD_DigestAuthInfo | |||
4815 | * with charset and language tag removed (i.e. it is original username | 4909 | * with charset and language tag removed (i.e. it is original username |
4816 | * extracted from the extended notation). | 4910 | * extracted from the extended notation). |
4817 | * This can be NULL is username is missing or invalid. | 4911 | * This can be NULL is username is missing or invalid. |
4912 | * @sa #MHD_digest_auth_calc_userhash_hex() | ||
4818 | */ | 4913 | */ |
4819 | char *username; | 4914 | char *username; |
4820 | 4915 | ||
@@ -4834,6 +4929,7 @@ struct MHD_DigestAuthInfo | |||
4834 | * @warning To avoid buffer overruns, always check the size of the data before | 4929 | * @warning To avoid buffer overruns, always check the size of the data before |
4835 | * use, because @a userhash_bin can point even to zero-sized | 4930 | * use, because @a userhash_bin can point even to zero-sized |
4836 | * data. | 4931 | * data. |
4932 | * @sa #MHD_digest_auth_calc_userhash() | ||
4837 | */ | 4933 | */ |
4838 | uint8_t *userhash_bin; | 4934 | uint8_t *userhash_bin; |
4839 | 4935 | ||
@@ -4932,13 +5028,12 @@ struct MHD_DigestAuthUsernameInfo | |||
4932 | 5028 | ||
4933 | /** | 5029 | /** |
4934 | * The username string. | 5030 | * The username string. |
4935 | * Valid only if username is standard, extended, or userhash. | ||
4936 | * For userhash this is unqoted string without decoding of the | 5031 | * For userhash this is unqoted string without decoding of the |
4937 | * hexadecimal digits (as provided by client). | 5032 | * hexadecimal digits (as provided by client). |
4938 | * If extended notation is used, this string is pct-decoded string | 5033 | * If extended notation is used, this string is pct-decoded string |
4939 | * with charset and language tag removed (i.e. it is original username | 5034 | * with charset and language tag removed (i.e. it is original username |
4940 | * extracted from the extended notation). | 5035 | * extracted from the extended notation). |
4941 | * This can be NULL is username is missing or invalid. | 5036 | * @sa #MHD_digest_auth_calc_userhash_hex() |
4942 | */ | 5037 | */ |
4943 | char *username; | 5038 | char *username; |
4944 | 5039 | ||
@@ -4958,6 +5053,7 @@ struct MHD_DigestAuthUsernameInfo | |||
4958 | * @warning To avoid buffer overruns, always check the size of the data before | 5053 | * @warning To avoid buffer overruns, always check the size of the data before |
4959 | * use, because @a userhash_bin can point even to zero-sized | 5054 | * use, because @a userhash_bin can point even to zero-sized |
4960 | * data. | 5055 | * data. |
5056 | * @sa #MHD_digest_auth_calc_userhash() | ||
4961 | */ | 5057 | */ |
4962 | uint8_t *userhash_bin; | 5058 | uint8_t *userhash_bin; |
4963 | }; | 5059 | }; |
@@ -5086,7 +5182,8 @@ enum MHD_DigestAuthResult | |||
5086 | * | 5182 | * |
5087 | * @param connection the MHD connection structure | 5183 | * @param connection the MHD connection structure |
5088 | * @param realm the realm to be used for authorization of the client | 5184 | * @param realm the realm to be used for authorization of the client |
5089 | * @param username the username needs to be authenticated | 5185 | * @param username the username needs to be authenticated, must be in clear text |
5186 | * even if userhash is used by the client | ||
5090 | * @param password the password used in the authentication | 5187 | * @param password the password used in the authentication |
5091 | * @param nonce_timeout the nonce validity duration in seconds | 5188 | * @param nonce_timeout the nonce validity duration in seconds |
5092 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc | 5189 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
@@ -5113,6 +5210,46 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
5113 | 5210 | ||
5114 | 5211 | ||
5115 | /** | 5212 | /** |
5213 | * Calculate userdigest, return it as binary data. | ||
5214 | * | ||
5215 | * The "userdigest" is the hash of the "username:realm:password" string. | ||
5216 | * | ||
5217 | * The "userdigest" can be used to avoid storing the password in clear text | ||
5218 | * in database/files | ||
5219 | * | ||
5220 | * This function is designed to improve security of stored credentials, | ||
5221 | * the "userdigest" does not improve security of the authentication process. | ||
5222 | * | ||
5223 | * The results can be used to store username & userdigest pairs instead of | ||
5224 | * username & password pairs. To further improve security, application may | ||
5225 | * store username & userhash & userdigest triplets. | ||
5226 | * | ||
5227 | * @param algo3 the digest algorithm | ||
5228 | * @param username the username | ||
5229 | * @param realm the realm | ||
5230 | * @param password the password, must be zero-terminated | ||
5231 | * @param[out] userdigest_bin the output buffer for userdigest; | ||
5232 | * if this function succeeds, then this buffer has | ||
5233 | * #MHD_digest_get_hash_size(algo3) bytes of | ||
5234 | * userdigest upon return | ||
5235 | * @param userdigest_bin the size of the @a userdigest_bin buffer, must be | ||
5236 | * at least #MHD_digest_get_hash_size(algo3) bytes long | ||
5237 | * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or | ||
5238 | * if @a algo3 algorithm is not supported. | ||
5239 | * @sa #MHD_digest_auth_check_digest3() | ||
5240 | * @note Available since #MHD_VERSION 0x00097535 | ||
5241 | * @ingroup authentication | ||
5242 | */ | ||
5243 | _MHD_EXTERN enum MHD_Result | ||
5244 | MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo3 algo3, | ||
5245 | const char *username, | ||
5246 | const char *realm, | ||
5247 | const char *password, | ||
5248 | void *userdigest_bin, | ||
5249 | size_t bin_buf_size); | ||
5250 | |||
5251 | |||
5252 | /** | ||
5116 | * Authenticates the authorization header sent by the client by using | 5253 | * Authenticates the authorization header sent by the client by using |
5117 | * hash of "username:realm:password". | 5254 | * hash of "username:realm:password". |
5118 | * | 5255 | * |
@@ -5125,13 +5262,15 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
5125 | * nonce and second time to perform an authorised request). | 5262 | * nonce and second time to perform an authorised request). |
5126 | * | 5263 | * |
5127 | * @param connection the MHD connection structure | 5264 | * @param connection the MHD connection structure |
5128 | * @param realm the realm presented to the client | 5265 | * @param realm the realm to be used for authorization of the client |
5129 | * @param username the username needs to be authenticated | 5266 | * @param username the username needs to be authenticated, must be in clear text |
5267 | * even if userhash is used by the client | ||
5130 | * @param userdigest the precalculated binary hash of the string | 5268 | * @param userdigest the precalculated binary hash of the string |
5131 | * "username:realm:password" | 5269 | * "username:realm:password", |
5270 | * see #MHD_digest_auth_calc_userdigest() | ||
5132 | * @param userdigest_size the size of the @a userdigest in bytes, must match the | 5271 | * @param userdigest_size the size of the @a userdigest in bytes, must match the |
5133 | * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, | 5272 | * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, |
5134 | * #MHD_SHA256_DIGEST_SIZE) | 5273 | * #MHD_SHA256_DIGEST_SIZE, #MHD_digest_get_hash_size()) |
5135 | * @param nonce_timeout the period of seconds since nonce generation, when | 5274 | * @param nonce_timeout the period of seconds since nonce generation, when |
5136 | * the nonce is recognised as valid and not stale. | 5275 | * the nonce is recognised as valid and not stale. |
5137 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc | 5276 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
@@ -5146,6 +5285,7 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
5146 | * match specified algorithm | 5285 | * match specified algorithm |
5147 | * @return #MHD_DAUTH_OK if authenticated, | 5286 | * @return #MHD_DAUTH_OK if authenticated, |
5148 | * the error code otherwise | 5287 | * the error code otherwise |
5288 | * @sa #MHD_digest_auth_calc_userdigest() | ||
5149 | * @note Available since #MHD_VERSION 0x00097528 | 5289 | * @note Available since #MHD_VERSION 0x00097528 |
5150 | * @ingroup authentication | 5290 | * @ingroup authentication |
5151 | */ | 5291 | */ |
@@ -5205,9 +5345,13 @@ MHD_digest_auth_check_digest3 (struct MHD_Connection *connection, | |||
5205 | * @param userhash_support if set to non-zero value (#MHD_YES) then support of | 5345 | * @param userhash_support if set to non-zero value (#MHD_YES) then support of |
5206 | * userhash is indicated, the client may provide | 5346 | * userhash is indicated, the client may provide |
5207 | * hash("username:realm") instead of username in | 5347 | * hash("username:realm") instead of username in |
5208 | * clear text; note that client is allowed to provide | 5348 | * clear text; |
5209 | * the username in cleartext even if this parameter set | 5349 | * note that clients are allowed to provide the username |
5210 | * to non-zero | 5350 | * in cleartext even if this parameter set to non-zero; |
5351 | * when userhash is used, application must be ready to | ||
5352 | * identify users by provided userhash value instead of | ||
5353 | * username; see #MHD_digest_auth_calc_userhash() and | ||
5354 | * #MHD_digest_auth_calc_userhash_hex() | ||
5211 | * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is | 5355 | * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is |
5212 | * added, indicating for the client that UTF-8 encoding | 5356 | * added, indicating for the client that UTF-8 encoding |
5213 | * is preferred | 5357 | * is preferred |
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c index 0a892e22..fac3e280 100644 --- a/src/microhttpd/digestauth.c +++ b/src/microhttpd/digestauth.c | |||
@@ -225,6 +225,8 @@ digest_get_hash_size (enum MHD_DigestAuthAlgo3 algo3) | |||
225 | * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or | 225 | * @return the size of the digest (either #MHD_MD5_DIGEST_SIZE or |
226 | * #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not | 226 | * #MHD_SHA256_DIGEST_SIZE) or zero if the input value is not |
227 | * recognised/valid | 227 | * recognised/valid |
228 | * @sa #MHD_digest_auth_calc_userdigest() | ||
229 | * @sa #MHD_digest_auth_calc_userhash(), #MHD_digest_auth_calc_userhash_hex() | ||
228 | * @note Available since #MHD_VERSION 0x00097526 | 230 | * @note Available since #MHD_VERSION 0x00097526 |
229 | * @ingroup authentication | 231 | * @ingroup authentication |
230 | */ | 232 | */ |
@@ -1137,7 +1139,7 @@ MHD_digest_auth_get_username (struct MHD_Connection *connection) | |||
1137 | /** | 1139 | /** |
1138 | * Calculate the server nonce so that it mitigates replay attacks | 1140 | * Calculate the server nonce so that it mitigates replay attacks |
1139 | * The current format of the nonce is ... | 1141 | * The current format of the nonce is ... |
1140 | * H(various parameters) + Hex(timestamp) | 1142 | * H(timestamp:random data:various parameters) + Hex(timestamp) |
1141 | * | 1143 | * |
1142 | * @param nonce_time The amount of time in seconds for a nonce to be invalid | 1144 | * @param nonce_time The amount of time in seconds for a nonce to be invalid |
1143 | * @param mthd_e HTTP method as enum value | 1145 | * @param mthd_e HTTP method as enum value |
@@ -1516,6 +1518,240 @@ calculate_add_nonce_with_retry (struct MHD_Connection *const connection, | |||
1516 | } | 1518 | } |
1517 | 1519 | ||
1518 | 1520 | ||
1521 | /** | ||
1522 | * Calculate userdigest, return it as binary data. | ||
1523 | * | ||
1524 | * It is equal to H(A1) for non-session algorithms. | ||
1525 | * | ||
1526 | * MHD internal version. | ||
1527 | * | ||
1528 | * @param da the digest algorithm | ||
1529 | * @param username the username to use | ||
1530 | * @param username_len the length of the @a username | ||
1531 | * @param realm the realm to use | ||
1532 | * @param realm_len the length of the @a realm | ||
1533 | * @param password the password, must be zero-terminated | ||
1534 | * @param[out] ha1_bin the output buffer, must have at least | ||
1535 | * #digest_get_size(da) bytes available | ||
1536 | */ | ||
1537 | _MHD_static_inline void | ||
1538 | calc_userdigest (struct DigestAlgorithm *da, | ||
1539 | const char *username, const size_t username_len, | ||
1540 | const char *realm, const size_t realm_len, | ||
1541 | const char *password, | ||
1542 | uint8_t *ha1_bin) | ||
1543 | { | ||
1544 | digest_init (da); | ||
1545 | digest_update (da, username, username_len); | ||
1546 | digest_update_with_colon (da); | ||
1547 | digest_update (da, realm, realm_len); | ||
1548 | digest_update_with_colon (da); | ||
1549 | digest_update_str (da, password); | ||
1550 | digest_calc_hash (da, ha1_bin); | ||
1551 | } | ||
1552 | |||
1553 | |||
1554 | /** | ||
1555 | * Calculate userdigest, return it as binary data. | ||
1556 | * | ||
1557 | * The "userdigest" is the hash of the "username:realm:password" string. | ||
1558 | * | ||
1559 | * The "userdigest" can be used to avoid storing the password in clear text | ||
1560 | * in database/files | ||
1561 | * | ||
1562 | * This function is designed to improve security of stored credentials, | ||
1563 | * the "userdigest" does not improve security of the authentication process. | ||
1564 | * | ||
1565 | * The results can be used to store username & userdigest pairs instead of | ||
1566 | * username & password pairs. To further improve security, application may | ||
1567 | * store username & userhash & userdigest triplets. | ||
1568 | * | ||
1569 | * @param algo3 the digest algorithm | ||
1570 | * @param username the username | ||
1571 | * @param realm the realm | ||
1572 | * @param password the password, must be zero-terminated | ||
1573 | * @param[out] userdigest_bin the output buffer for userdigest; | ||
1574 | * if this function succeeds, then this buffer has | ||
1575 | * #MHD_digest_get_hash_size(algo3) bytes of | ||
1576 | * userdigest upon return | ||
1577 | * @param userdigest_bin the size of the @a userdigest_bin buffer, must be | ||
1578 | * at least #MHD_digest_get_hash_size(algo3) bytes long | ||
1579 | * @return MHD_YES on success, MHD_NO if @a userdigest_bin is too small or | ||
1580 | * if @a algo3 algorithm is not supported. | ||
1581 | * @sa #MHD_digest_auth_check_digest3() | ||
1582 | * @note Available since #MHD_VERSION 0x00097535 | ||
1583 | * @ingroup authentication | ||
1584 | */ | ||
1585 | _MHD_EXTERN enum MHD_Result | ||
1586 | MHD_digest_auth_calc_userdigest (enum MHD_DigestAuthAlgo3 algo3, | ||
1587 | const char *username, | ||
1588 | const char *realm, | ||
1589 | const char *password, | ||
1590 | void *userdigest_bin, | ||
1591 | size_t bin_buf_size) | ||
1592 | { | ||
1593 | struct DigestAlgorithm da; | ||
1594 | if (! digest_setup (&da, get_base_digest_algo (algo3))) | ||
1595 | return MHD_NO; | ||
1596 | if (digest_get_size (&da) > bin_buf_size) | ||
1597 | return MHD_NO; | ||
1598 | calc_userdigest (&da, | ||
1599 | username, | ||
1600 | strlen (username), | ||
1601 | realm, | ||
1602 | strlen (realm), | ||
1603 | password, | ||
1604 | userdigest_bin); | ||
1605 | return MHD_YES; | ||
1606 | } | ||
1607 | |||
1608 | |||
1609 | /** | ||
1610 | * Calculate userhash, return it as binary data. | ||
1611 | * | ||
1612 | * MHD internal version. | ||
1613 | * | ||
1614 | * @param da the digest algorithm | ||
1615 | * @param username the username to use | ||
1616 | * @param username_len the length of the @a username | ||
1617 | * @param realm the realm to use | ||
1618 | * @param realm_len the length of the @a realm | ||
1619 | * @param[out] digest_bin the output buffer, must have at least | ||
1620 | * #MHD_digest_get_hash_size(algo3) bytes available | ||
1621 | */ | ||
1622 | _MHD_static_inline void | ||
1623 | calc_userhash (struct DigestAlgorithm *da, | ||
1624 | const char *username, const size_t username_len, | ||
1625 | const char *realm, const size_t realm_len, | ||
1626 | uint8_t *digest_bin) | ||
1627 | { | ||
1628 | mhd_assert (NULL != username); | ||
1629 | digest_init (da); | ||
1630 | digest_update (da, username, username_len); | ||
1631 | digest_update_with_colon (da); | ||
1632 | digest_update (da, realm, realm_len); | ||
1633 | digest_calc_hash (da, digest_bin); | ||
1634 | } | ||
1635 | |||
1636 | |||
1637 | /** | ||
1638 | * Calculate "userhash", return it as binary data. | ||
1639 | * | ||
1640 | * The "userhash" is the hash of the string "username:realm". | ||
1641 | * | ||
1642 | * The "Userhash" could be used to avoid sending username in cleartext in Digest | ||
1643 | * Authorization client's header. | ||
1644 | * | ||
1645 | * Userhash is not designed to hide the username in local database or files, | ||
1646 | * as username in cleartext is required for #MHD_digest_auth_check3() function | ||
1647 | * to check the response, but it can be used to hide username in HTTP headers. | ||
1648 | * | ||
1649 | * This function could be used when the new username is added to the username | ||
1650 | * database to save the "userhash" alongside with the username (preferably) or | ||
1651 | * when loading list of the usernames to generate the userhash for every loaded | ||
1652 | * username (this will cause delays at the start with the long lists). | ||
1653 | * | ||
1654 | * Once "userhash" is generated it could be used to identify users for clients | ||
1655 | * with "userhash" support. | ||
1656 | * Avoid repetitive usage of this function for the same username/realm | ||
1657 | * combination as it will cause excessive CPU load; save and re-use the result | ||
1658 | * instead. | ||
1659 | * | ||
1660 | * @param algo3 the algorithm for userhash calculations | ||
1661 | * @param username the username | ||
1662 | * @param realm the realm | ||
1663 | * @param[out] userhash_bin the output buffer for userhash as binary data; | ||
1664 | * if this function succeeds, then this buffer has | ||
1665 | * #MHD_digest_get_hash_size(algo3) bytes of userhash | ||
1666 | * upon return | ||
1667 | * @param bin_buf_size the size of the @a userhash_bin buffer, must be | ||
1668 | * at least #MHD_digest_get_hash_size(algo3) bytes long | ||
1669 | * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or | ||
1670 | * if @a algo3 algorithm is not supported. | ||
1671 | * @note Available since #MHD_VERSION 0x00097535 | ||
1672 | * @ingroup authentication | ||
1673 | */ | ||
1674 | _MHD_EXTERN enum MHD_Result | ||
1675 | MHD_digest_auth_calc_userhash (enum MHD_DigestAuthAlgo3 algo3, | ||
1676 | const char *username, | ||
1677 | const char *realm, | ||
1678 | void *userhash_bin, | ||
1679 | size_t bin_buf_size) | ||
1680 | { | ||
1681 | struct DigestAlgorithm da; | ||
1682 | if (! digest_setup (&da, get_base_digest_algo (algo3))) | ||
1683 | return MHD_NO; | ||
1684 | if (digest_get_size (&da) > bin_buf_size) | ||
1685 | return MHD_NO; | ||
1686 | calc_userhash (&da, | ||
1687 | username, | ||
1688 | strlen (username), | ||
1689 | realm, | ||
1690 | strlen (realm), | ||
1691 | userhash_bin); | ||
1692 | return MHD_YES; | ||
1693 | } | ||
1694 | |||
1695 | |||
1696 | /** | ||
1697 | * Calculate "userhash", return it as hexadecimal data. | ||
1698 | * | ||
1699 | * The "userhash" is the hash of the string "username:realm". | ||
1700 | * | ||
1701 | * The "Userhash" could be used to avoid sending username in cleartext in Digest | ||
1702 | * Authorization client's header. | ||
1703 | * | ||
1704 | * Userhash is not designed to hide the username in local database or files, | ||
1705 | * as username in cleartext is required for #MHD_digest_auth_check3() function | ||
1706 | * to check the response, but it can be used to hide username in HTTP headers. | ||
1707 | * | ||
1708 | * This function could be used when the new username is added to the username | ||
1709 | * database to save the "userhash" alongside with the username (preferably) or | ||
1710 | * when loading list of the usernames to generate the userhash for every loaded | ||
1711 | * username (this will cause delays at the start with the long lists). | ||
1712 | * | ||
1713 | * Once "userhash" is generated it could be used to identify users for clients | ||
1714 | * with "userhash" support. | ||
1715 | * Avoid repetitive usage of this function for the same username/realm | ||
1716 | * combination as it will cause excessive CPU load; save and re-use the result | ||
1717 | * instead. | ||
1718 | * | ||
1719 | * @param algo3 the algorithm for userhash calculations | ||
1720 | * @param username the username | ||
1721 | * @param realm the realm | ||
1722 | * @param[out] userhash_hex the output buffer for userhash as hex data; | ||
1723 | * if this function succeeds, then this buffer has | ||
1724 | * #MHD_digest_get_hash_size(algo3)*2 chars long | ||
1725 | * userhash string | ||
1726 | * @param bin_buf_size the size of the @a userhash_bin buffer, must be | ||
1727 | * at least #MHD_digest_get_hash_size(algo3)*2+1 chars long | ||
1728 | * @return MHD_YES on success, MHD_NO if @a bin_buf_size is too small or | ||
1729 | * if @a algo3 algorithm is not supported. | ||
1730 | * @note Available since #MHD_VERSION 0x00097535 | ||
1731 | * @ingroup authentication | ||
1732 | */ | ||
1733 | _MHD_EXTERN enum MHD_Result | ||
1734 | MHD_digest_auth_calc_userhash_hex (enum MHD_DigestAuthAlgo3 algo3, | ||
1735 | const char *username, | ||
1736 | const char *realm, | ||
1737 | char *userhash_hex, | ||
1738 | size_t hex_buf_size) | ||
1739 | { | ||
1740 | uint8_t userhash_bin[MAX_DIGEST]; | ||
1741 | size_t digest_size; | ||
1742 | |||
1743 | digest_size = digest_get_hash_size (algo3); | ||
1744 | if (digest_size * 2 + 1 > hex_buf_size) | ||
1745 | return MHD_NO; | ||
1746 | if (MHD_NO == MHD_digest_auth_calc_userhash (algo3, username, realm, | ||
1747 | userhash_bin, MAX_DIGEST)) | ||
1748 | return MHD_NO; | ||
1749 | |||
1750 | MHD_bin_to_hex_z (userhash_bin, digest_size, userhash_hex); | ||
1751 | return MHD_YES; | ||
1752 | } | ||
1753 | |||
1754 | |||
1519 | struct test_header_param | 1755 | struct test_header_param |
1520 | { | 1756 | { |
1521 | struct MHD_Connection *connection; | 1757 | struct MHD_Connection *connection; |
@@ -2116,11 +2352,7 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2116 | else | 2352 | else |
2117 | { /* Userhash */ | 2353 | { /* Userhash */ |
2118 | mhd_assert (NULL != params->username.value.str); | 2354 | mhd_assert (NULL != params->username.value.str); |
2119 | digest_init (&da); | 2355 | calc_userhash (&da, username, username_len, realm, realm_len, hash1_bin); |
2120 | digest_update (&da, username, username_len); | ||
2121 | digest_update_with_colon (&da); | ||
2122 | digest_update (&da, realm, realm_len); | ||
2123 | digest_calc_hash (&da, hash1_bin); | ||
2124 | mhd_assert (sizeof (tmp1) >= (2 * digest_size)); | 2356 | mhd_assert (sizeof (tmp1) >= (2 * digest_size)); |
2125 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); | 2357 | MHD_bin_to_hex (hash1_bin, digest_size, tmp1); |
2126 | if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) | 2358 | if (! is_param_equal_caseless (¶ms->username, tmp1, 2 * digest_size)) |
@@ -2260,15 +2492,11 @@ digest_auth_check_all_inner (struct MHD_Connection *connection, | |||
2260 | 2492 | ||
2261 | /* ** Build H(A1) ** */ | 2493 | /* ** Build H(A1) ** */ |
2262 | if (NULL == userdigest) | 2494 | if (NULL == userdigest) |
2263 | { | 2495 | calc_userdigest (&da, |
2264 | digest_init (&da); | 2496 | username, username_len, |
2265 | digest_update (&da, (const uint8_t *) username, username_len); | 2497 | realm, realm_len, |
2266 | digest_update_with_colon (&da); | 2498 | password, |
2267 | digest_update (&da, (const uint8_t *) realm, realm_len); | 2499 | hash1_bin); |
2268 | digest_update_with_colon (&da); | ||
2269 | digest_update_str (&da, password); | ||
2270 | digest_calc_hash (&da, hash1_bin); | ||
2271 | } | ||
2272 | /* TODO: support '-sess' versions */ | 2500 | /* TODO: support '-sess' versions */ |
2273 | /* Got H(A1) */ | 2501 | /* Got H(A1) */ |
2274 | 2502 | ||
@@ -2476,7 +2704,8 @@ MHD_digest_auth_check (struct MHD_Connection *connection, | |||
2476 | * | 2704 | * |
2477 | * @param connection the MHD connection structure | 2705 | * @param connection the MHD connection structure |
2478 | * @param realm the realm to be used for authorization of the client | 2706 | * @param realm the realm to be used for authorization of the client |
2479 | * @param username the username needs to be authenticated | 2707 | * @param username the username needs to be authenticated, must be in clear text |
2708 | * even if userhash is used by the client | ||
2480 | * @param password the password used in the authentication | 2709 | * @param password the password used in the authentication |
2481 | * @param nonce_timeout the nonce validity duration in seconds | 2710 | * @param nonce_timeout the nonce validity duration in seconds |
2482 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc | 2711 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
@@ -2528,13 +2757,15 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
2528 | * nonce and second time to perform an authorised request). | 2757 | * nonce and second time to perform an authorised request). |
2529 | * | 2758 | * |
2530 | * @param connection the MHD connection structure | 2759 | * @param connection the MHD connection structure |
2531 | * @param realm the realm presented to the client | 2760 | * @param realm the realm to be used for authorization of the client |
2532 | * @param username the username needs to be authenticated | 2761 | * @param username the username needs to be authenticated, must be in clear text |
2762 | * even if userhash is used by the client | ||
2533 | * @param userdigest the precalculated binary hash of the string | 2763 | * @param userdigest the precalculated binary hash of the string |
2534 | * "username:realm:password" | 2764 | * "username:realm:password", |
2765 | * see #MHD_digest_auth_calc_userdigest() | ||
2535 | * @param userdigest_size the size of the @a userdigest in bytes, must match the | 2766 | * @param userdigest_size the size of the @a userdigest in bytes, must match the |
2536 | * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, | 2767 | * hashing algorithm (see #MHD_MD5_DIGEST_SIZE, |
2537 | * #MHD_SHA256_DIGEST_SIZE) | 2768 | * #MHD_SHA256_DIGEST_SIZE, #MHD_digest_get_hash_size()) |
2538 | * @param nonce_timeout the period of seconds since nonce generation, when | 2769 | * @param nonce_timeout the period of seconds since nonce generation, when |
2539 | * the nonce is recognised as valid and not stale. | 2770 | * the nonce is recognised as valid and not stale. |
2540 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc | 2771 | * @param max_nc the maximum allowed nc (Nonce Count) value, if client's nc |
@@ -2549,6 +2780,7 @@ MHD_digest_auth_check3 (struct MHD_Connection *connection, | |||
2549 | * match specified algorithm | 2780 | * match specified algorithm |
2550 | * @return #MHD_DAUTH_OK if authenticated, | 2781 | * @return #MHD_DAUTH_OK if authenticated, |
2551 | * the error code otherwise | 2782 | * the error code otherwise |
2783 | * @sa #MHD_digest_auth_calc_userdigest() | ||
2552 | * @note Available since #MHD_VERSION 0x00097528 | 2784 | * @note Available since #MHD_VERSION 0x00097528 |
2553 | * @ingroup authentication | 2785 | * @ingroup authentication |
2554 | */ | 2786 | */ |
@@ -2777,9 +3009,13 @@ MHD_digest_auth_check_digest (struct MHD_Connection *connection, | |||
2777 | * @param userhash_support if set to non-zero value (#MHD_YES) then support of | 3009 | * @param userhash_support if set to non-zero value (#MHD_YES) then support of |
2778 | * userhash is indicated, the client may provide | 3010 | * userhash is indicated, the client may provide |
2779 | * hash("username:realm") instead of username in | 3011 | * hash("username:realm") instead of username in |
2780 | * clear text; note that client is allowed to provide | 3012 | * clear text; |
2781 | * the username in cleartext even if this parameter set | 3013 | * note that clients are allowed to provide the username |
2782 | * to non-zero | 3014 | * in cleartext even if this parameter set to non-zero; |
3015 | * when userhash is used, application must be ready to | ||
3016 | * identify users by provided userhash value instead of | ||
3017 | * username; see #MHD_digest_auth_calc_userhash() and | ||
3018 | * #MHD_digest_auth_calc_userhash_hex() | ||
2783 | * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is | 3019 | * @param prefer_utf8 if not set to #MHD_NO, parameter 'charset=UTF-8' is |
2784 | * added, indicating for the client that UTF-8 encoding | 3020 | * added, indicating for the client that UTF-8 encoding |
2785 | * is preferred | 3021 | * is preferred |