diff options
Diffstat (limited to 'src/microhttpd/digestauth.c')
-rw-r--r-- | src/microhttpd/digestauth.c | 282 |
1 files changed, 259 insertions, 23 deletions
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 |