aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-08-26 09:09:31 +0300
committerEvgeny Grin (Karlson2k) <k2k@narod.ru>2022-09-04 12:25:06 +0300
commit0ccef4c16cb66f4c0a4ca3570dc536b8015c5231 (patch)
treed2ce5c215c79de3aa0dd86e86eac4e838b1263a1
parent2a9071b862d8f4647fe22770a0dacdf595ea72fb (diff)
downloadlibmicrohttpd-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.h166
-rw-r--r--src/microhttpd/digestauth.c282
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
4686MHD_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
4731MHD_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
5244MHD_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
1538calc_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
1586MHD_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
1623calc_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
1675MHD_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
1734MHD_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
1519struct test_header_param 1755struct 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 (&params->username, tmp1, 2 * digest_size)) 2358 if (! is_param_equal_caseless (&params->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