diff options
author | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-05-30 21:54:09 +0300 |
---|---|---|
committer | Evgeny Grin (Karlson2k) <k2k@narod.ru> | 2022-05-31 11:45:36 +0300 |
commit | 9039d65241daf512e7756319cd64d3d54750cb22 (patch) | |
tree | 9c109e217ad1014a89de5495fcebdc1723672e31 /src/microhttpd/digestauth.c | |
parent | c15077e312ffe934b242c7ecc6559c6eb4eb62cb (diff) | |
download | libmicrohttpd-9039d65241daf512e7756319cd64d3d54750cb22.tar.gz libmicrohttpd-9039d65241daf512e7756319cd64d3d54750cb22.zip |
authentication: reworked header parsing
Added single function to parse all enabled authentication schemes header
strings.
The parsing result is cached and reused thus avoiding repetitive header
parsing.
The new function correctly "unquotes" values (backslashes are removed)
as required by RFC.
Diffstat (limited to 'src/microhttpd/digestauth.c')
-rw-r--r-- | src/microhttpd/digestauth.c | 932 |
1 files changed, 572 insertions, 360 deletions
diff --git a/src/microhttpd/digestauth.c b/src/microhttpd/digestauth.c index 7ac5ad8b..23b41193 100644 --- a/src/microhttpd/digestauth.c +++ b/src/microhttpd/digestauth.c | |||
@@ -26,6 +26,7 @@ | |||
26 | * @author Karlson2k (Evgeny Grin) | 26 | * @author Karlson2k (Evgeny Grin) |
27 | */ | 27 | */ |
28 | #include "digestauth.h" | 28 | #include "digestauth.h" |
29 | #include "gen_auth.h" | ||
29 | #include "platform.h" | 30 | #include "platform.h" |
30 | #include "mhd_limits.h" | 31 | #include "mhd_limits.h" |
31 | #include "internal.h" | 32 | #include "internal.h" |
@@ -158,19 +159,19 @@ enum MHD_CheckNonceNC_ | |||
158 | /** | 159 | /** |
159 | * The nonce and NC are OK (valid and NC was not used before). | 160 | * The nonce and NC are OK (valid and NC was not used before). |
160 | */ | 161 | */ |
161 | MHD_DAUTH_NONCENC_OK = MHD_DAUTH_OK, | 162 | MHD_CHECK_NONCENC_OK = MHD_DAUTH_OK, |
162 | 163 | ||
163 | /** | 164 | /** |
164 | * The 'nonce' was overwritten with newer 'nonce' in the same slot or | 165 | * The 'nonce' was overwritten with newer 'nonce' in the same slot or |
165 | * NC was already used. | 166 | * NC was already used. |
166 | * The validity of the 'nonce' was not be checked. | 167 | * The validity of the 'nonce' was not be checked. |
167 | */ | 168 | */ |
168 | MHD_DAUTH_NONCENC_STALE = MHD_DAUTH_NONCE_STALE, | 169 | MHD_CHECK_NONCENC_STALE = MHD_DAUTH_NONCE_STALE, |
169 | 170 | ||
170 | /** | 171 | /** |
171 | * The 'nonce' is wrong, it was not generated before. | 172 | * The 'nonce' is wrong, it was not generated before. |
172 | */ | 173 | */ |
173 | MHD_DAUTH_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG, | 174 | MHD_CHECK_NONCENC_WRONG = MHD_DAUTH_NONCE_WRONG, |
174 | }; | 175 | }; |
175 | 176 | ||
176 | 177 | ||
@@ -536,7 +537,8 @@ digest_calc_ha1_from_user (const char *alg, | |||
536 | * @param cnonce client nonce | 537 | * @param cnonce client nonce |
537 | * @param qop qop-value: "", "auth" or "auth-int" (NOTE: only 'auth' is supported today.) | 538 | * @param qop qop-value: "", "auth" or "auth-int" (NOTE: only 'auth' is supported today.) |
538 | * @param method method from request | 539 | * @param method method from request |
539 | * @param uri requested URL | 540 | * @param uri requested URL, could be not zero-terminated |
541 | * @param uri_len the length of @a uri, in characters | ||
540 | * @param hentity H(entity body) if qop="auth-int" | 542 | * @param hentity H(entity body) if qop="auth-int" |
541 | * @param[in,out] da digest algorithm to use, also | 543 | * @param[in,out] da digest algorithm to use, also |
542 | * we write da->sessionkey (set to response request-digest or response-digest) | 544 | * we write da->sessionkey (set to response request-digest or response-digest) |
@@ -549,6 +551,7 @@ digest_calc_response (const char *ha1, | |||
549 | const char *qop, | 551 | const char *qop, |
550 | const char *method, | 552 | const char *method, |
551 | const char *uri, | 553 | const char *uri, |
554 | size_t uri_len, | ||
552 | const char *hentity, | 555 | const char *hentity, |
553 | struct DigestAlgorithm *da) | 556 | struct DigestAlgorithm *da) |
554 | { | 557 | { |
@@ -564,7 +567,7 @@ digest_calc_response (const char *ha1, | |||
564 | 1); | 567 | 1); |
565 | digest_update (da, | 568 | digest_update (da, |
566 | (const unsigned char *) uri, | 569 | (const unsigned char *) uri, |
567 | strlen (uri)); | 570 | uri_len); |
568 | #if 0 | 571 | #if 0 |
569 | if (0 == strcasecmp (qop, | 572 | if (0 == strcasecmp (qop, |
570 | "auth-int")) | 573 | "auth-int")) |
@@ -627,101 +630,17 @@ digest_calc_response (const char *ha1, | |||
627 | } | 630 | } |
628 | 631 | ||
629 | 632 | ||
630 | /** | 633 | static const struct MHD_RqDAuth * |
631 | * Lookup subvalue off of the HTTP Authorization header. | 634 | get_rq_dauth_params (struct MHD_Connection *connection) |
632 | * | ||
633 | * A description of the input format for 'data' is at | ||
634 | * http://en.wikipedia.org/wiki/Digest_access_authentication | ||
635 | * | ||
636 | * | ||
637 | * @param dest where to store the result (possibly truncated if | ||
638 | * the buffer is not big enough). | ||
639 | * @param size size of dest | ||
640 | * @param data pointer to the Authorization header | ||
641 | * @param key key to look up in data | ||
642 | * @return size of the located value, 0 if otherwise | ||
643 | */ | ||
644 | static size_t | ||
645 | lookup_sub_value (char *dest, | ||
646 | size_t size, | ||
647 | const char *data, | ||
648 | const char *key) | ||
649 | { | 635 | { |
650 | size_t keylen; | 636 | const struct MHD_AuthRqHeader *rq_params; |
651 | size_t len; | 637 | |
652 | const char *ptr; | 638 | rq_params = MHD_get_auth_rq_params_ (connection); |
653 | const char *eq; | 639 | if ( (NULL == rq_params) || |
654 | const char *q1; | 640 | (MHD_AUTHTYPE_DIGEST != rq_params->auth_type) ) |
655 | const char *q2; | 641 | return NULL; |
656 | const char *qn; | 642 | |
657 | 643 | return rq_params->params.dauth; | |
658 | if (0 == size) | ||
659 | return 0; | ||
660 | keylen = strlen (key); | ||
661 | ptr = data; | ||
662 | while ('\0' != *ptr) | ||
663 | { | ||
664 | if (NULL == (eq = strchr (ptr, | ||
665 | '='))) | ||
666 | return 0; | ||
667 | q1 = eq + 1; | ||
668 | while (' ' == *q1) | ||
669 | q1++; | ||
670 | if ('\"' != *q1) | ||
671 | { | ||
672 | q2 = strchr (q1, | ||
673 | ','); | ||
674 | qn = q2; | ||
675 | } | ||
676 | else | ||
677 | { | ||
678 | q1++; | ||
679 | q2 = strchr (q1, | ||
680 | '\"'); | ||
681 | if (NULL == q2) | ||
682 | return 0; /* end quote not found */ | ||
683 | qn = q2 + 1; | ||
684 | } | ||
685 | if ( (MHD_str_equal_caseless_n_ (ptr, | ||
686 | key, | ||
687 | keylen)) && | ||
688 | (eq == &ptr[keylen]) ) | ||
689 | { | ||
690 | if (NULL == q2) | ||
691 | { | ||
692 | len = strlen (q1) + 1; | ||
693 | if (size > len) | ||
694 | size = len; | ||
695 | size--; | ||
696 | memcpy (dest, | ||
697 | q1, | ||
698 | size); | ||
699 | dest[size] = '\0'; | ||
700 | return size; | ||
701 | } | ||
702 | else | ||
703 | { | ||
704 | if (size > (size_t) ((q2 - q1) + 1)) | ||
705 | size = (size_t) (q2 - q1) + 1; | ||
706 | size--; | ||
707 | memcpy (dest, | ||
708 | q1, | ||
709 | size); | ||
710 | dest[size] = '\0'; | ||
711 | return size; | ||
712 | } | ||
713 | } | ||
714 | if (NULL == qn) | ||
715 | return 0; | ||
716 | ptr = strchr (qn, | ||
717 | ','); | ||
718 | if (NULL == ptr) | ||
719 | return 0; | ||
720 | ptr++; | ||
721 | while (' ' == *ptr) | ||
722 | ptr++; | ||
723 | } | ||
724 | return 0; | ||
725 | } | 644 | } |
726 | 645 | ||
727 | 646 | ||
@@ -738,7 +657,6 @@ get_nonce_timestamp (const char *const nonce, | |||
738 | size_t noncelen, | 657 | size_t noncelen, |
739 | uint64_t *const ptimestamp) | 658 | uint64_t *const ptimestamp) |
740 | { | 659 | { |
741 | mhd_assert ((0 == noncelen) || (strlen (nonce) == noncelen)); | ||
742 | if (0 == noncelen) | 660 | if (0 == noncelen) |
743 | noncelen = strlen (nonce); | 661 | noncelen = strlen (nonce); |
744 | 662 | ||
@@ -826,18 +744,19 @@ check_nonce_nc (struct MHD_Connection *connection, | |||
826 | uint32_t mod; | 744 | uint32_t mod; |
827 | enum MHD_CheckNonceNC_ ret; | 745 | enum MHD_CheckNonceNC_ ret; |
828 | 746 | ||
747 | mhd_assert (0 != noncelen); | ||
829 | mhd_assert (strlen (nonce) == noncelen); | 748 | mhd_assert (strlen (nonce) == noncelen); |
830 | mhd_assert (0 != nc); | 749 | mhd_assert (0 != nc); |
831 | if (MAX_NONCE_LENGTH < noncelen) | 750 | if (MAX_NONCE_LENGTH < noncelen) |
832 | return MHD_DAUTH_NONCENC_WRONG; /* This should be impossible, but static analysis | 751 | return MHD_CHECK_NONCENC_WRONG; /* This should be impossible, but static analysis |
833 | tools have a hard time with it *and* this also | 752 | tools have a hard time with it *and* this also |
834 | protects against unsafe modifications that may | 753 | protects against unsafe modifications that may |
835 | happen in the future... */ | 754 | happen in the future... */ |
836 | mod = daemon->nonce_nc_size; | 755 | mod = daemon->nonce_nc_size; |
837 | if (0 == mod) | 756 | if (0 == mod) |
838 | return MHD_DAUTH_NONCENC_STALE; /* no array! */ | 757 | return MHD_CHECK_NONCENC_STALE; /* no array! */ |
839 | if (nc >= UINT64_MAX - 64) | 758 | if (nc >= UINT64_MAX - 64) |
840 | return MHD_DAUTH_NONCENC_STALE; /* Overflow, unrealistically high value */ | 759 | return MHD_CHECK_NONCENC_STALE; /* Overflow, unrealistically high value */ |
841 | 760 | ||
842 | nn = &daemon->nnc[get_nonce_nc_idx (mod, nonce, noncelen)]; | 761 | nn = &daemon->nnc[get_nonce_nc_idx (mod, nonce, noncelen)]; |
843 | 762 | ||
@@ -851,11 +770,11 @@ check_nonce_nc (struct MHD_Connection *connection, | |||
851 | if (0 == nn->nonce[0]) | 770 | if (0 == nn->nonce[0]) |
852 | { /* The slot was never used, while the client's nonce value should be | 771 | { /* The slot was never used, while the client's nonce value should be |
853 | * recorded when it was generated by MHD */ | 772 | * recorded when it was generated by MHD */ |
854 | ret = MHD_DAUTH_NONCENC_WRONG; | 773 | ret = MHD_CHECK_NONCENC_WRONG; |
855 | } | 774 | } |
856 | else if (0 != nn->nonce[noncelen]) | 775 | else if (0 != nn->nonce[noncelen]) |
857 | { /* The value is the slot is wrong */ | 776 | { /* The value is the slot is wrong */ |
858 | ret = MHD_DAUTH_NONCENC_STALE; | 777 | ret = MHD_CHECK_NONCENC_STALE; |
859 | } | 778 | } |
860 | else | 779 | else |
861 | { | 780 | { |
@@ -863,7 +782,7 @@ check_nonce_nc (struct MHD_Connection *connection, | |||
863 | if (! get_nonce_timestamp (nn->nonce, 0, &slot_ts)) | 782 | if (! get_nonce_timestamp (nn->nonce, 0, &slot_ts)) |
864 | { | 783 | { |
865 | mhd_assert (0); /* The value is the slot is wrong */ | 784 | mhd_assert (0); /* The value is the slot is wrong */ |
866 | ret = MHD_DAUTH_NONCENC_STALE; | 785 | ret = MHD_CHECK_NONCENC_STALE; |
867 | } | 786 | } |
868 | else | 787 | else |
869 | { | 788 | { |
@@ -873,21 +792,21 @@ check_nonce_nc (struct MHD_Connection *connection, | |||
873 | { | 792 | { |
874 | /* The nonce from the client may not have been placed in the slot | 793 | /* The nonce from the client may not have been placed in the slot |
875 | * because another nonce in that slot has not yet expired. */ | 794 | * because another nonce in that slot has not yet expired. */ |
876 | ret = MHD_DAUTH_NONCENC_STALE; | 795 | ret = MHD_CHECK_NONCENC_STALE; |
877 | } | 796 | } |
878 | else if (TRIM_TO_TIMESTAMP (UINT64_MAX) / 2 >= ts_diff) | 797 | else if (TRIM_TO_TIMESTAMP (UINT64_MAX) / 2 >= ts_diff) |
879 | { | 798 | { |
880 | /* Too large value means that nonce_time is less than slot_ts. | 799 | /* Too large value means that nonce_time is less than slot_ts. |
881 | * The nonce from the client may have been overwritten by the newer | 800 | * The nonce from the client may have been overwritten by the newer |
882 | * nonce. */ | 801 | * nonce. */ |
883 | ret = MHD_DAUTH_NONCENC_STALE; | 802 | ret = MHD_CHECK_NONCENC_STALE; |
884 | } | 803 | } |
885 | else | 804 | else |
886 | { | 805 | { |
887 | /* The nonce from the client should be generated after the nonce | 806 | /* The nonce from the client should be generated after the nonce |
888 | * in the slot has been expired, the nonce must be recorded, but | 807 | * in the slot has been expired, the nonce must be recorded, but |
889 | * it's not. */ | 808 | * it's not. */ |
890 | ret = MHD_DAUTH_NONCENC_WRONG; | 809 | ret = MHD_CHECK_NONCENC_WRONG; |
891 | } | 810 | } |
892 | } | 811 | } |
893 | } | 812 | } |
@@ -908,7 +827,7 @@ check_nonce_nc (struct MHD_Connection *connection, | |||
908 | else | 827 | else |
909 | nn->nmask = 0; /* big jump, unset all bits in the mask */ | 828 | nn->nmask = 0; /* big jump, unset all bits in the mask */ |
910 | nn->nc = nc; | 829 | nn->nc = nc; |
911 | ret = MHD_DAUTH_NONCENC_OK; | 830 | ret = MHD_CHECK_NONCENC_OK; |
912 | } | 831 | } |
913 | else if (nc < nn->nc) | 832 | else if (nc < nn->nc) |
914 | { | 833 | { |
@@ -919,15 +838,15 @@ check_nonce_nc (struct MHD_Connection *connection, | |||
919 | { | 838 | { |
920 | /* Out-of-order nonce, but within 64-bit bitmask, set bit */ | 839 | /* Out-of-order nonce, but within 64-bit bitmask, set bit */ |
921 | nn->nmask |= (UINT64_C (1) << (nn->nc - nc - 1)); | 840 | nn->nmask |= (UINT64_C (1) << (nn->nc - nc - 1)); |
922 | ret = MHD_DAUTH_NONCENC_OK; | 841 | ret = MHD_CHECK_NONCENC_OK; |
923 | } | 842 | } |
924 | else | 843 | else |
925 | /* 'nc' was already used or too old (more then 64 values ago) */ | 844 | /* 'nc' was already used or too old (more then 64 values ago) */ |
926 | ret = MHD_DAUTH_NONCENC_STALE; | 845 | ret = MHD_CHECK_NONCENC_STALE; |
927 | } | 846 | } |
928 | else /* if (nc == nn->nc) */ | 847 | else /* if (nc == nn->nc) */ |
929 | /* 'nc' was already used */ | 848 | /* 'nc' was already used */ |
930 | ret = MHD_DAUTH_NONCENC_STALE; | 849 | ret = MHD_CHECK_NONCENC_STALE; |
931 | 850 | ||
932 | MHD_mutex_unlock_chk_ (&daemon->nnc_lock); | 851 | MHD_mutex_unlock_chk_ (&daemon->nnc_lock); |
933 | 852 | ||
@@ -947,28 +866,40 @@ check_nonce_nc (struct MHD_Connection *connection, | |||
947 | _MHD_EXTERN char * | 866 | _MHD_EXTERN char * |
948 | MHD_digest_auth_get_username (struct MHD_Connection *connection) | 867 | MHD_digest_auth_get_username (struct MHD_Connection *connection) |
949 | { | 868 | { |
950 | char user[MAX_USERNAME_LENGTH]; | 869 | const struct MHD_RqDAuth *params; |
951 | const char *header; | 870 | char *username; |
952 | 871 | size_t username_len; | |
953 | if (MHD_NO == MHD_lookup_connection_value_n (connection, | 872 | |
954 | MHD_HEADER_KIND, | 873 | params = get_rq_dauth_params (connection); |
955 | MHD_HTTP_HEADER_AUTHORIZATION, | 874 | if (NULL == params) |
956 | MHD_STATICSTR_LEN_ ( | ||
957 | MHD_HTTP_HEADER_AUTHORIZATION), | ||
958 | &header, | ||
959 | NULL)) | ||
960 | return NULL; | 875 | return NULL; |
961 | if (0 != strncmp (header, | 876 | |
962 | _MHD_AUTH_DIGEST_BASE, | 877 | if (NULL == params->username.value.str) |
963 | MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE))) | ||
964 | return NULL; | 878 | return NULL; |
965 | header += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE); | 879 | |
966 | if (0 == lookup_sub_value (user, | 880 | username_len = params->username.value.len; |
967 | sizeof (user), | 881 | username = malloc (username_len + 1); |
968 | header, | 882 | if (NULL == username) |
969 | "username")) | ||
970 | return NULL; | 883 | return NULL; |
971 | return strdup (user); | 884 | |
885 | if (! params->username.quoted) | ||
886 | { | ||
887 | /* The username is not quoted, no need to unquote */ | ||
888 | if (0 != username_len) | ||
889 | memcpy (username, params->username.value.str, username_len); | ||
890 | username[username_len] = 0; /* Zero-terminate */ | ||
891 | } | ||
892 | else | ||
893 | { | ||
894 | /* Need to properly unquote the username */ | ||
895 | mhd_assert (0 != username_len); /* Quoted string may not be zero-legth */ | ||
896 | username_len = MHD_str_unquote (params->username.value.str, username_len, | ||
897 | username); | ||
898 | mhd_assert (0 != username_len); /* The unquoted string cannot be empty */ | ||
899 | username[username_len] = 0; /* Zero-terminate */ | ||
900 | } | ||
901 | |||
902 | return username; | ||
972 | } | 903 | } |
973 | 904 | ||
974 | 905 | ||
@@ -1347,6 +1278,91 @@ check_argument_match (struct MHD_Connection *connection, | |||
1347 | 1278 | ||
1348 | 1279 | ||
1349 | /** | 1280 | /** |
1281 | * The size of the unquoting buffer in stack | ||
1282 | */ | ||
1283 | #define _MHD_STATIC_UNQ_BUFFER_SIZE 128 | ||
1284 | |||
1285 | /** | ||
1286 | * The result of parameter unquoting | ||
1287 | */ | ||
1288 | enum _MHD_GetUnqResult | ||
1289 | { | ||
1290 | _MHD_UNQ_NON_EMPTY = 0, /**< The sting is not empty */ | ||
1291 | _MHD_UNQ_NO_STRING = MHD_DAUTH_WRONG_HEADER, /**< No string (no such parameter) */ | ||
1292 | _MHD_UNQ_EMPTY = 1, /**< The string is empty */ | ||
1293 | _MHD_UNQ_TOO_LARGE = 2, /**< The string is too large to unqoute */ | ||
1294 | _MHD_UNQ_OUT_OF_MEM = 3 /**< Out of memory error */ | ||
1295 | }; | ||
1296 | |||
1297 | |||
1298 | /** | ||
1299 | * Get Digest authorisation parameter as unquoted string. | ||
1300 | * @param param the parameter to process | ||
1301 | * @param tmp1 the small buffer in stack | ||
1302 | * @param ptmp2 the pointer to pointer to malloc'ed buffer | ||
1303 | * @param ptmp2_size the pointer to the size of the buffer pointed by @a ptmp2 | ||
1304 | * @param[out] unquoted the pointer to store the result, NOT zero terminated | ||
1305 | * @return enum code indicating result of the process | ||
1306 | */ | ||
1307 | static enum _MHD_GetUnqResult | ||
1308 | get_unqouted_param (const struct MHD_RqDAuthParam *param, | ||
1309 | char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE], | ||
1310 | char **ptmp2, | ||
1311 | size_t *ptmp2_size, | ||
1312 | struct _MHD_cstr_w_len *unquoted) | ||
1313 | { | ||
1314 | char *str; | ||
1315 | size_t len; | ||
1316 | mhd_assert ((0 == *ptmp2_size) || (NULL != *ptmp2)); | ||
1317 | mhd_assert ((NULL != *ptmp2) || (0 == *ptmp2_size)); | ||
1318 | mhd_assert ((0 == *ptmp2_size) || \ | ||
1319 | (_MHD_STATIC_UNQ_BUFFER_SIZE < *ptmp2_size)); | ||
1320 | |||
1321 | if (NULL == param->value.str) | ||
1322 | { | ||
1323 | const struct _MHD_cstr_w_len res = {NULL, 0}; | ||
1324 | mhd_assert (! param->quoted); | ||
1325 | mhd_assert (0 == param->value.len); | ||
1326 | memcpy (unquoted, &res, sizeof(res)); | ||
1327 | return _MHD_UNQ_NO_STRING; | ||
1328 | } | ||
1329 | if (! param->quoted) | ||
1330 | { | ||
1331 | memcpy (unquoted, ¶m->value, sizeof(param->value)); | ||
1332 | return (0 == param->value.len) ? _MHD_UNQ_EMPTY : _MHD_UNQ_NON_EMPTY; | ||
1333 | } | ||
1334 | /* The value is present and is quoted, needs to be copied and unquoted */ | ||
1335 | mhd_assert (0 != param->value.len); | ||
1336 | if (param->value.len <= _MHD_STATIC_UNQ_BUFFER_SIZE) | ||
1337 | str = tmp1; | ||
1338 | else if (param->value.len <= *ptmp2_size) | ||
1339 | str = *ptmp2; | ||
1340 | else | ||
1341 | { | ||
1342 | if (param->value.len > _MHD_AUTH_DIGEST_MAX_PARAM_SIZE) | ||
1343 | return _MHD_UNQ_TOO_LARGE; | ||
1344 | if (NULL != *ptmp2) | ||
1345 | free (*ptmp2); | ||
1346 | *ptmp2 = (char *) malloc (param->value.len); | ||
1347 | if (NULL == *ptmp2) | ||
1348 | return _MHD_UNQ_OUT_OF_MEM; | ||
1349 | *ptmp2_size = param->value.len; | ||
1350 | str = *ptmp2; | ||
1351 | } | ||
1352 | |||
1353 | len = MHD_str_unquote (param->value.str, param->value.len, str); | ||
1354 | if (1) | ||
1355 | { | ||
1356 | const struct _MHD_cstr_w_len res = {str, len}; | ||
1357 | memcpy (unquoted, &res, sizeof(res)); | ||
1358 | } | ||
1359 | mhd_assert (0 != unquoted->len); | ||
1360 | mhd_assert (unquoted->len < param->value.len); | ||
1361 | return _MHD_UNQ_NON_EMPTY; | ||
1362 | } | ||
1363 | |||
1364 | |||
1365 | /** | ||
1350 | * Authenticates the authorization header sent by the client | 1366 | * Authenticates the authorization header sent by the client |
1351 | * | 1367 | * |
1352 | * @param connection The MHD connection structure | 1368 | * @param connection The MHD connection structure |
@@ -1375,10 +1391,6 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1375 | unsigned int nonce_timeout) | 1391 | unsigned int nonce_timeout) |
1376 | { | 1392 | { |
1377 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); | 1393 | struct MHD_Daemon *daemon = MHD_get_master (connection->daemon); |
1378 | size_t len; | ||
1379 | const char *header; | ||
1380 | char nonce[MAX_NONCE_LENGTH]; | ||
1381 | size_t nonce_len; | ||
1382 | char cnonce[MAX_NONCE_LENGTH]; | 1394 | char cnonce[MAX_NONCE_LENGTH]; |
1383 | const unsigned int digest_size = digest_get_size (da); | 1395 | const unsigned int digest_size = digest_get_size (da); |
1384 | char ha1[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1]; | 1396 | char ha1[VLA_ARRAY_LEN_DIGEST (digest_size) * 2 + 1]; |
@@ -1389,219 +1401,368 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1389 | char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1]; | 1401 | char noncehashexp[NONCE_STD_LEN (VLA_ARRAY_LEN_DIGEST (digest_size)) + 1]; |
1390 | uint64_t nonce_time; | 1402 | uint64_t nonce_time; |
1391 | uint64_t t; | 1403 | uint64_t t; |
1392 | size_t left; /* number of characters left in 'header' for 'uri' */ | ||
1393 | uint64_t nci; | 1404 | uint64_t nci; |
1394 | char *qmark; | 1405 | char *qmark; |
1406 | const struct MHD_RqDAuth *params; | ||
1407 | char tmp1[_MHD_STATIC_UNQ_BUFFER_SIZE]; /**< Temporal buffer in stack for unqouting */ | ||
1408 | char *tmp2; /**< Temporal malloc'ed buffer for unqouting */ | ||
1409 | size_t tmp2_size; /**< The size of @a tmp2 buffer */ | ||
1410 | struct _MHD_cstr_w_len unquoted; | ||
1411 | enum _MHD_GetUnqResult unq_res; | ||
1412 | enum MHD_DigestAuthResult ret; | ||
1413 | #ifdef HAVE_MESSAGES | ||
1414 | bool err_logged; | ||
1415 | #endif /* HAVE_MESSAGES */ | ||
1395 | 1416 | ||
1396 | VLA_CHECK_LEN_DIGEST (digest_size); | 1417 | tmp2 = NULL; |
1397 | if (MHD_NO == MHD_lookup_connection_value_n (connection, | 1418 | tmp2_size = 0; |
1398 | MHD_HEADER_KIND, | ||
1399 | MHD_HTTP_HEADER_AUTHORIZATION, | ||
1400 | MHD_STATICSTR_LEN_ ( | ||
1401 | MHD_HTTP_HEADER_AUTHORIZATION), | ||
1402 | &header, | ||
1403 | NULL)) | ||
1404 | return MHD_DAUTH_WRONG_HEADER; | ||
1405 | if (0 != strncmp (header, | ||
1406 | _MHD_AUTH_DIGEST_BASE, | ||
1407 | MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE))) | ||
1408 | return MHD_DAUTH_WRONG_HEADER; | ||
1409 | header += MHD_STATICSTR_LEN_ (_MHD_AUTH_DIGEST_BASE); | ||
1410 | left = strlen (header); | ||
1411 | |||
1412 | if (1) | ||
1413 | { | ||
1414 | char un[MAX_USERNAME_LENGTH]; | ||
1415 | |||
1416 | len = lookup_sub_value (un, | ||
1417 | sizeof (un), | ||
1418 | header, | ||
1419 | "username"); | ||
1420 | if (0 == len) | ||
1421 | return MHD_DAUTH_WRONG_HEADER; | ||
1422 | if (0 != strcmp (username, | ||
1423 | un)) | ||
1424 | return MHD_DAUTH_WRONG_USERNAME; | ||
1425 | left -= strlen ("username") + len; | ||
1426 | } | ||
1427 | |||
1428 | if (1) | ||
1429 | { | ||
1430 | char r[MAX_REALM_LENGTH]; | ||
1431 | |||
1432 | len = lookup_sub_value (r, | ||
1433 | sizeof (r), | ||
1434 | header, | ||
1435 | "realm"); | ||
1436 | if (0 == len) | ||
1437 | return MHD_DAUTH_WRONG_HEADER; | ||
1438 | if (0 != strcmp (realm, | ||
1439 | r)) | ||
1440 | return MHD_DAUTH_WRONG_REALM; | ||
1441 | left -= strlen ("realm") + len; | ||
1442 | } | ||
1443 | |||
1444 | if (0 == (len = lookup_sub_value (nonce, | ||
1445 | sizeof (nonce), | ||
1446 | header, | ||
1447 | "nonce"))) | ||
1448 | return MHD_DAUTH_WRONG_HEADER; | ||
1449 | nonce_len = len; | ||
1450 | left -= strlen ("nonce") + len; | ||
1451 | if (left > 32 * 1024) | ||
1452 | { | ||
1453 | /* we do not permit URIs longer than 32k, as we want to | ||
1454 | make sure to not blow our stack (or per-connection | ||
1455 | heap memory limit). Besides, 32k is already insanely | ||
1456 | large, but of course in theory the | ||
1457 | #MHD_OPTION_CONNECTION_MEMORY_LIMIT might be very large | ||
1458 | and would thus permit sending a >32k authorization | ||
1459 | header value. */ | ||
1460 | return MHD_DAUTH_WRONG_HEADER; | ||
1461 | } | ||
1462 | if (! get_nonce_timestamp (nonce, nonce_len, &nonce_time)) | ||
1463 | { | ||
1464 | #ifdef HAVE_MESSAGES | 1419 | #ifdef HAVE_MESSAGES |
1465 | MHD_DLOG (daemon, | 1420 | err_logged = false; |
1466 | _ ("Authentication failed, invalid timestamp format.\n")); | 1421 | #endif /* HAVE_MESSAGES */ |
1467 | #endif | 1422 | |
1423 | params = get_rq_dauth_params (connection); | ||
1424 | if (NULL == params) | ||
1468 | return MHD_DAUTH_WRONG_HEADER; | 1425 | return MHD_DAUTH_WRONG_HEADER; |
1469 | } | ||
1470 | 1426 | ||
1471 | t = MHD_monotonic_msec_counter (); | 1427 | do /* Only to avoid "goto" */ |
1472 | /* | ||
1473 | * First level vetting for the nonce validity: if the timestamp | ||
1474 | * attached to the nonce exceeds `nonce_timeout', then the nonce is | ||
1475 | * invalid. | ||
1476 | */ | ||
1477 | if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000)) | ||
1478 | { | 1428 | { |
1479 | /* too old */ | 1429 | /* Check 'username' */ |
1480 | return MHD_DAUTH_NONCE_STALE; | 1430 | unq_res = get_unqouted_param (¶ms->username, tmp1, &tmp2, &tmp2_size, |
1481 | } | 1431 | &unquoted); |
1432 | if (_MHD_UNQ_NON_EMPTY != unq_res) | ||
1433 | { | ||
1434 | if (_MHD_UNQ_NO_STRING == unq_res) | ||
1435 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1436 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1437 | ret = MHD_DAUTH_WRONG_USERNAME; | ||
1438 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1439 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1440 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1441 | ret = MHD_DAUTH_ERROR; | ||
1442 | else | ||
1443 | { | ||
1444 | mhd_assert (0); /* Must not happen */ | ||
1445 | ret = MHD_DAUTH_ERROR; | ||
1446 | } | ||
1447 | break; | ||
1448 | } | ||
1449 | /* 'unquoted" may not contain binary zero */ | ||
1450 | if ( (0 != strncmp (username, unquoted.str, unquoted.len)) || | ||
1451 | (0 != username[unquoted.len]) ) | ||
1452 | { | ||
1453 | ret = MHD_DAUTH_WRONG_USERNAME; | ||
1454 | break; | ||
1455 | } | ||
1456 | /* 'username' valid */ | ||
1482 | 1457 | ||
1483 | calculate_nonce (nonce_time, | 1458 | /* Check 'realm' */ |
1484 | connection->method, | 1459 | unq_res = get_unqouted_param (¶ms->realm, tmp1, &tmp2, &tmp2_size, |
1485 | daemon->digest_auth_random, | 1460 | &unquoted); |
1486 | daemon->digest_auth_rand_size, | 1461 | if (_MHD_UNQ_NON_EMPTY != unq_res) |
1487 | connection->url, | 1462 | { |
1488 | realm, | 1463 | if (_MHD_UNQ_NO_STRING == unq_res) |
1489 | da, | 1464 | ret = MHD_DAUTH_WRONG_HEADER; |
1490 | noncehashexp); | 1465 | else if (_MHD_UNQ_EMPTY == unq_res) |
1491 | /* | 1466 | ret = MHD_DAUTH_WRONG_REALM; |
1492 | * Second level vetting for the nonce validity | 1467 | else if (_MHD_UNQ_TOO_LARGE == unq_res) |
1493 | * if the timestamp attached to the nonce is valid | 1468 | ret = MHD_DAUTH_WRONG_HEADER; |
1494 | * and possibly fabricated (in case of an attack) | 1469 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) |
1495 | * the attacker must also know the random seed to be | 1470 | ret = MHD_DAUTH_ERROR; |
1496 | * able to generate a "sane" nonce, which if he does | 1471 | else |
1497 | * not, the nonce fabrication process going to be | 1472 | { |
1498 | * very hard to achieve. | 1473 | mhd_assert (0); /* Must not happen */ |
1499 | */ | 1474 | ret = MHD_DAUTH_ERROR; |
1500 | if (0 != strcmp (nonce, | 1475 | } |
1501 | noncehashexp)) | 1476 | break; |
1502 | { | 1477 | } |
1503 | return MHD_DAUTH_NONCE_WRONG; | 1478 | /* 'unquoted" may not contain binary zero */ |
1504 | } | 1479 | if ( (0 != strncmp (realm, unquoted.str, unquoted.len)) || |
1505 | if ( (0 == lookup_sub_value (cnonce, | 1480 | (0 != realm[unquoted.len]) ) |
1506 | sizeof (cnonce), | 1481 | { |
1507 | header, | 1482 | ret = MHD_DAUTH_WRONG_REALM; |
1508 | "cnonce")) || | 1483 | break; |
1509 | (0 == lookup_sub_value (qop, | 1484 | } |
1510 | sizeof (qop), | 1485 | /* 'realm' valid */ |
1511 | header, | 1486 | |
1512 | "qop")) || | 1487 | /* Check 'nonce' */ |
1513 | ( (0 != strcmp (qop, | 1488 | unq_res = get_unqouted_param (¶ms->nonce, tmp1, &tmp2, &tmp2_size, |
1514 | "auth")) && | 1489 | &unquoted); |
1515 | (0 != strcmp (qop, | 1490 | if (_MHD_UNQ_NON_EMPTY != unq_res) |
1516 | "")) ) || | 1491 | { |
1517 | (0 == (len = lookup_sub_value (nc, | 1492 | if (_MHD_UNQ_NO_STRING == unq_res) |
1518 | sizeof (nc), | 1493 | ret = MHD_DAUTH_WRONG_HEADER; |
1519 | header, | 1494 | else if (_MHD_UNQ_EMPTY == unq_res) |
1520 | "nc")) ) || | 1495 | ret = MHD_DAUTH_NONCE_WRONG; |
1521 | (0 == lookup_sub_value (response, | 1496 | else if (_MHD_UNQ_TOO_LARGE == unq_res) |
1522 | sizeof (response), | 1497 | ret = MHD_DAUTH_WRONG_HEADER; |
1523 | header, | 1498 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) |
1524 | "response")) ) | 1499 | ret = MHD_DAUTH_ERROR; |
1525 | { | 1500 | else |
1526 | #ifdef HAVE_MESSAGES | 1501 | { |
1527 | MHD_DLOG (daemon, | 1502 | mhd_assert (0); /* Must not happen */ |
1528 | _ ("Authentication failed, invalid format.\n")); | 1503 | ret = MHD_DAUTH_ERROR; |
1529 | #endif | 1504 | } |
1530 | return MHD_DAUTH_WRONG_HEADER; | 1505 | break; |
1531 | } | 1506 | } |
1532 | if (len != MHD_strx_to_uint64_n_ (nc, | 1507 | /* TODO: check correct 'nonce' length */ |
1533 | len, | 1508 | if (! get_nonce_timestamp (unquoted.str, unquoted.len, &nonce_time)) |
1534 | &nci)) | 1509 | { |
1535 | { | ||
1536 | #ifdef HAVE_MESSAGES | ||
1537 | MHD_DLOG (daemon, | ||
1538 | _ ("Authentication failed, invalid nc format.\n")); | ||
1539 | #endif | ||
1540 | return MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ | ||
1541 | } | ||
1542 | if (0 == nci) | ||
1543 | { | ||
1544 | #ifdef HAVE_MESSAGES | 1510 | #ifdef HAVE_MESSAGES |
1545 | MHD_DLOG (daemon, | 1511 | MHD_DLOG (daemon, |
1546 | _ ("Authentication failed, invalid 'nc' value.\n")); | 1512 | _ ("Authentication failed, invalid timestamp format.\n")); |
1513 | err_logged = true; | ||
1547 | #endif | 1514 | #endif |
1548 | return MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ | 1515 | ret = MHD_DAUTH_NONCE_WRONG; |
1549 | } | 1516 | break; |
1517 | } | ||
1518 | t = MHD_monotonic_msec_counter (); | ||
1519 | /* | ||
1520 | * First level vetting for the nonce validity: if the timestamp | ||
1521 | * attached to the nonce exceeds `nonce_timeout', then the nonce is | ||
1522 | * invalid. | ||
1523 | */ | ||
1524 | if (TRIM_TO_TIMESTAMP (t - nonce_time) > (nonce_timeout * 1000)) | ||
1525 | { | ||
1526 | /* too old */ | ||
1527 | ret = MHD_DAUTH_NONCE_STALE; | ||
1528 | break; | ||
1529 | } | ||
1550 | 1530 | ||
1551 | if (1) | 1531 | calculate_nonce (nonce_time, |
1552 | { | 1532 | connection->method, |
1553 | enum MHD_CheckNonceNC_ nonce_nc_check; | 1533 | daemon->digest_auth_random, |
1534 | daemon->digest_auth_rand_size, | ||
1535 | connection->url, | ||
1536 | realm, | ||
1537 | da, | ||
1538 | noncehashexp); | ||
1554 | /* | 1539 | /* |
1555 | * Checking if that combination of nonce and nc is sound | 1540 | * Second level vetting for the nonce validity |
1556 | * and not a replay attack attempt. Refuse if nonce was not | 1541 | * if the timestamp attached to the nonce is valid |
1557 | * generated previously. | 1542 | * and possibly fabricated (in case of an attack) |
1543 | * the attacker must also know the random seed to be | ||
1544 | * able to generate a "sane" nonce, which if he does | ||
1545 | * not, the nonce fabrication process going to be | ||
1546 | * very hard to achieve. | ||
1558 | */ | 1547 | */ |
1559 | nonce_nc_check = check_nonce_nc (connection, | 1548 | if ((0 != strncmp (noncehashexp, unquoted.str, unquoted.len)) || |
1560 | nonce, | 1549 | (0 != noncehashexp[unquoted.len])) |
1561 | nonce_len, | 1550 | { |
1562 | nonce_time, | 1551 | ret = MHD_DAUTH_NONCE_WRONG; |
1563 | nci); | 1552 | break; |
1564 | if (MHD_DAUTH_NONCENC_STALE == nonce_nc_check) | 1553 | } |
1554 | /* 'nonce' valid */ | ||
1555 | |||
1556 | /* Get 'cnonce' */ | ||
1557 | unq_res = get_unqouted_param (¶ms->cnonce, tmp1, &tmp2, &tmp2_size, | ||
1558 | &unquoted); | ||
1559 | if (_MHD_UNQ_NON_EMPTY != unq_res) | ||
1560 | { | ||
1561 | if (_MHD_UNQ_NO_STRING == unq_res) | ||
1562 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1563 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1564 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1565 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1566 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1567 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1568 | ret = MHD_DAUTH_ERROR; | ||
1569 | else | ||
1570 | { | ||
1571 | mhd_assert (0); /* Must not happen */ | ||
1572 | ret = MHD_DAUTH_ERROR; | ||
1573 | } | ||
1574 | break; | ||
1575 | } | ||
1576 | if (sizeof(cnonce) <= unquoted.len) | ||
1577 | { | ||
1578 | /* TODO: handle large client nonces */ | ||
1579 | ret = MHD_DAUTH_ERROR; | ||
1580 | break; | ||
1581 | } | ||
1582 | /* TODO: avoid memcpy() */ | ||
1583 | memcpy (cnonce, unquoted.str, unquoted.len); | ||
1584 | cnonce[unquoted.len] = 0; | ||
1585 | /* Got 'cnonce' */ | ||
1586 | |||
1587 | /* Get 'qop' */ | ||
1588 | unq_res = get_unqouted_param (¶ms->qop, tmp1, &tmp2, &tmp2_size, | ||
1589 | &unquoted); | ||
1590 | if (_MHD_UNQ_NON_EMPTY != unq_res) | ||
1591 | { | ||
1592 | if (_MHD_UNQ_NO_STRING == unq_res) | ||
1593 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1594 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1595 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1596 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1597 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1598 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1599 | ret = MHD_DAUTH_ERROR; | ||
1600 | else | ||
1601 | { | ||
1602 | mhd_assert (0); /* Must not happen */ | ||
1603 | ret = MHD_DAUTH_ERROR; | ||
1604 | } | ||
1605 | break; | ||
1606 | } | ||
1607 | if (sizeof(qop) <= unquoted.len) | ||
1608 | { | ||
1609 | /* TODO: handle large client qop */ | ||
1610 | ret = MHD_DAUTH_ERROR; | ||
1611 | break; | ||
1612 | } | ||
1613 | /* TODO: avoid memcpy() */ | ||
1614 | memcpy (qop, unquoted.str, unquoted.len); | ||
1615 | qop[unquoted.len] = 0; | ||
1616 | /* TODO: use caseless match, use dedicated return code */ | ||
1617 | if ( (0 != strcmp (qop, "auth")) && | ||
1618 | (0 != strcmp (qop,"")) ) | ||
1619 | { | ||
1620 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1621 | break; | ||
1622 | } | ||
1623 | /* Got 'qop' */ | ||
1624 | |||
1625 | /* Get 'nc' */ | ||
1626 | unq_res = get_unqouted_param (¶ms->nc, tmp1, &tmp2, &tmp2_size, | ||
1627 | &unquoted); | ||
1628 | if (_MHD_UNQ_NON_EMPTY != unq_res) | ||
1629 | { | ||
1630 | if (_MHD_UNQ_NO_STRING == unq_res) | ||
1631 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1632 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1633 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1634 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1635 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1636 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1637 | ret = MHD_DAUTH_ERROR; | ||
1638 | else | ||
1639 | { | ||
1640 | mhd_assert (0); /* Must not happen */ | ||
1641 | ret = MHD_DAUTH_ERROR; | ||
1642 | } | ||
1643 | break; | ||
1644 | } | ||
1645 | if (sizeof(nc) <= unquoted.len) | ||
1646 | { | ||
1647 | /* TODO: handle large client nc */ | ||
1648 | ret = MHD_DAUTH_ERROR; | ||
1649 | break; | ||
1650 | } | ||
1651 | /* TODO: avoid memcpy() */ | ||
1652 | memcpy (nc, unquoted.str, unquoted.len); | ||
1653 | nc[unquoted.len] = 0; | ||
1654 | if (unquoted.len != MHD_strx_to_uint64_n_ (nc, | ||
1655 | unquoted.len, | ||
1656 | &nci)) | ||
1565 | { | 1657 | { |
1566 | #ifdef HAVE_MESSAGES | 1658 | #ifdef HAVE_MESSAGES |
1567 | MHD_DLOG (daemon, | 1659 | MHD_DLOG (daemon, |
1568 | _ ("Stale nonce received. If this happens a lot, you should " | 1660 | _ ("Authentication failed, invalid nc format.\n")); |
1569 | "probably increase the size of the nonce array.\n")); | 1661 | err_logged = true; |
1570 | #endif | 1662 | #endif |
1571 | return MHD_DAUTH_NONCE_STALE; | 1663 | ret = MHD_DAUTH_WRONG_HEADER; /* invalid nonce format */ |
1664 | break; | ||
1572 | } | 1665 | } |
1573 | else if (MHD_DAUTH_NONCENC_WRONG == nonce_nc_check) | 1666 | if (0 == nci) |
1574 | { | 1667 | { |
1575 | #ifdef HAVE_MESSAGES | 1668 | #ifdef HAVE_MESSAGES |
1576 | MHD_DLOG (daemon, | 1669 | MHD_DLOG (daemon, |
1577 | _ ("Received nonce that technically valid, but was not " | 1670 | _ ("Authentication failed, invalid 'nc' value.\n")); |
1578 | "generated by MHD. This may indicate an attack attempt.\n")); | 1671 | err_logged = true; |
1579 | #endif | 1672 | #endif |
1580 | return MHD_DAUTH_NONCE_WRONG; | 1673 | ret = MHD_DAUTH_WRONG_HEADER; /* invalid nc value */ |
1674 | break; | ||
1581 | } | 1675 | } |
1582 | mhd_assert (MHD_DAUTH_NONCENC_OK == nonce_nc_check); | 1676 | /* Got 'nc' */ |
1583 | } | ||
1584 | 1677 | ||
1585 | if (1) | 1678 | /* Get 'response' */ |
1586 | { | 1679 | unq_res = get_unqouted_param (¶ms->response, tmp1, &tmp2, &tmp2_size, |
1587 | char *uri; | 1680 | &unquoted); |
1681 | if (_MHD_UNQ_NON_EMPTY != unq_res) | ||
1682 | { | ||
1683 | if (_MHD_UNQ_NO_STRING == unq_res) | ||
1684 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1685 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1686 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1687 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1688 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1689 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1690 | ret = MHD_DAUTH_ERROR; | ||
1691 | else | ||
1692 | { | ||
1693 | mhd_assert (0); /* Must not happen */ | ||
1694 | ret = MHD_DAUTH_ERROR; | ||
1695 | } | ||
1696 | break; | ||
1697 | } | ||
1698 | if (sizeof(response) <= unquoted.len) | ||
1699 | { | ||
1700 | /* TODO: handle large client response */ | ||
1701 | ret = MHD_DAUTH_ERROR; | ||
1702 | break; | ||
1703 | } | ||
1704 | /* TODO: avoid memcpy() */ | ||
1705 | memcpy (response, unquoted.str, unquoted.len); | ||
1706 | response[unquoted.len] = 0; | ||
1707 | /* Got 'response' */ | ||
1588 | 1708 | ||
1589 | uri = malloc (left + 1); | 1709 | if (1) |
1590 | if (NULL == uri) | ||
1591 | { | 1710 | { |
1711 | enum MHD_CheckNonceNC_ nonce_nc_check; | ||
1712 | /* | ||
1713 | * Checking if that combination of nonce and nc is sound | ||
1714 | * and not a replay attack attempt. Refuse if nonce was not | ||
1715 | * generated previously. | ||
1716 | */ | ||
1717 | nonce_nc_check = check_nonce_nc (connection, | ||
1718 | noncehashexp, | ||
1719 | NONCE_STD_LEN (digest_size), | ||
1720 | nonce_time, | ||
1721 | nci); | ||
1722 | if (MHD_CHECK_NONCENC_STALE == nonce_nc_check) | ||
1723 | { | ||
1592 | #ifdef HAVE_MESSAGES | 1724 | #ifdef HAVE_MESSAGES |
1593 | MHD_DLOG (daemon, | 1725 | MHD_DLOG (daemon, |
1594 | _ ("Failed to allocate memory for auth header processing.\n")); | 1726 | _ ("Stale nonce received. If this happens a lot, you should " |
1595 | #endif /* HAVE_MESSAGES */ | 1727 | "probably increase the size of the nonce array.\n")); |
1596 | return MHD_DAUTH_ERROR; | 1728 | err_logged = true; |
1729 | #endif | ||
1730 | ret = MHD_DAUTH_NONCE_STALE; | ||
1731 | break; | ||
1732 | } | ||
1733 | else if (MHD_CHECK_NONCENC_WRONG == nonce_nc_check) | ||
1734 | { | ||
1735 | #ifdef HAVE_MESSAGES | ||
1736 | MHD_DLOG (daemon, | ||
1737 | _ ("Received nonce that technically valid, but was not " | ||
1738 | "generated by MHD. This may indicate an attack attempt.\n")); | ||
1739 | err_logged = true; | ||
1740 | #endif | ||
1741 | ret = MHD_DAUTH_NONCE_WRONG; | ||
1742 | break; | ||
1743 | } | ||
1744 | mhd_assert (MHD_CHECK_NONCENC_OK == nonce_nc_check); | ||
1597 | } | 1745 | } |
1598 | if (0 == lookup_sub_value (uri, | 1746 | |
1599 | left + 1, | 1747 | /* Get 'uri' */ |
1600 | header, | 1748 | unq_res = get_unqouted_param (¶ms->uri, tmp1, &tmp2, &tmp2_size, |
1601 | "uri")) | 1749 | &unquoted); |
1750 | if (_MHD_UNQ_NON_EMPTY != unq_res) | ||
1602 | { | 1751 | { |
1603 | free (uri); | 1752 | if (_MHD_UNQ_NO_STRING == unq_res) |
1604 | return MHD_DAUTH_WRONG_HEADER; | 1753 | ret = MHD_DAUTH_WRONG_HEADER; |
1754 | else if (_MHD_UNQ_EMPTY == unq_res) | ||
1755 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1756 | else if (_MHD_UNQ_TOO_LARGE == unq_res) | ||
1757 | ret = MHD_DAUTH_WRONG_HEADER; | ||
1758 | else if (_MHD_UNQ_OUT_OF_MEM == unq_res) | ||
1759 | ret = MHD_DAUTH_ERROR; | ||
1760 | else | ||
1761 | { | ||
1762 | mhd_assert (0); /* Must not happen */ | ||
1763 | ret = MHD_DAUTH_ERROR; | ||
1764 | } | ||
1765 | break; | ||
1605 | } | 1766 | } |
1606 | if (NULL != digest) | 1767 | if (NULL != digest) |
1607 | { | 1768 | { |
@@ -1609,7 +1770,7 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1609 | digest_calc_ha1_from_digest (digest_get_algo_name (da), | 1770 | digest_calc_ha1_from_digest (digest_get_algo_name (da), |
1610 | da, | 1771 | da, |
1611 | digest, | 1772 | digest, |
1612 | nonce, | 1773 | noncehashexp, |
1613 | cnonce); | 1774 | cnonce); |
1614 | } | 1775 | } |
1615 | else | 1776 | else |
@@ -1620,7 +1781,7 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1620 | username, | 1781 | username, |
1621 | realm, | 1782 | realm, |
1622 | password, | 1783 | password, |
1623 | nonce, | 1784 | noncehashexp, |
1624 | cnonce, | 1785 | cnonce, |
1625 | da); | 1786 | da); |
1626 | } | 1787 | } |
@@ -1629,60 +1790,111 @@ digest_auth_check_all (struct MHD_Connection *connection, | |||
1629 | digest_size * 2 + 1); | 1790 | digest_size * 2 + 1); |
1630 | /* This will initialize da->sessionkey (respexp) */ | 1791 | /* This will initialize da->sessionkey (respexp) */ |
1631 | digest_calc_response (ha1, | 1792 | digest_calc_response (ha1, |
1632 | nonce, | 1793 | noncehashexp, |
1633 | nc, | 1794 | nc, |
1634 | cnonce, | 1795 | cnonce, |
1635 | qop, | 1796 | qop, |
1636 | connection->method, | 1797 | connection->method, |
1637 | uri, | 1798 | unquoted.str, |
1799 | unquoted.len, | ||
1638 | hentity, | 1800 | hentity, |
1639 | da); | 1801 | da); |
1640 | qmark = strchr (uri, | ||
1641 | '?'); | ||
1642 | if (NULL != qmark) | ||
1643 | *qmark = '\0'; | ||
1644 | |||
1645 | /* Need to unescape URI before comparing with connection->url */ | ||
1646 | daemon->unescape_callback (daemon->unescape_callback_cls, | ||
1647 | connection, | ||
1648 | uri); | ||
1649 | if (0 != strcmp (uri, | ||
1650 | connection->url)) | ||
1651 | { | ||
1652 | #ifdef HAVE_MESSAGES | ||
1653 | MHD_DLOG (daemon, | ||
1654 | _ ("Authentication failed, URI does not match.\n")); | ||
1655 | #endif | ||
1656 | free (uri); | ||
1657 | return MHD_DAUTH_WRONG_URI; | ||
1658 | } | ||
1659 | |||
1660 | if (1) | 1802 | if (1) |
1661 | { | 1803 | { |
1662 | const char *args = qmark; | 1804 | char *uri; |
1663 | 1805 | size_t uri_len; | |
1664 | if (NULL == args) | 1806 | uri_len = unquoted.len; |
1665 | args = ""; | 1807 | /* TODO: simplify string copy, avoid potential double copy */ |
1808 | if ( ((tmp1 != unquoted.str) && (tmp2 != unquoted.str)) || | ||
1809 | ((tmp1 == unquoted.str) && (sizeof(tmp1) >= unquoted.len)) || | ||
1810 | ((tmp2 == unquoted.str) && (tmp2_size >= unquoted.len))) | ||
1811 | { | ||
1812 | char *buf; | ||
1813 | mhd_assert ((tmp1 != unquoted.str) || \ | ||
1814 | (sizeof(tmp1) == unquoted.len)); | ||
1815 | mhd_assert ((tmp2 != unquoted.str) || \ | ||
1816 | (tmp2_size == unquoted.len)); | ||
1817 | buf = malloc (unquoted.len + 1); | ||
1818 | if (NULL == buf) | ||
1819 | { | ||
1820 | ret = MHD_DAUTH_ERROR; | ||
1821 | break; | ||
1822 | } | ||
1823 | memcpy (buf, unquoted.str, unquoted.len); | ||
1824 | if (NULL != tmp2) | ||
1825 | free (tmp2); | ||
1826 | tmp2 = buf; | ||
1827 | tmp2_size = unquoted.len + 1; | ||
1828 | uri = tmp2; | ||
1829 | } | ||
1830 | else if (tmp1 == unquoted.str) | ||
1831 | uri = tmp1; | ||
1666 | else | 1832 | else |
1667 | args++; | 1833 | { |
1668 | if (MHD_NO == | 1834 | mhd_assert (tmp2 == unquoted.str); |
1669 | check_argument_match (connection, | 1835 | uri = tmp2; |
1670 | args) ) | 1836 | } |
1837 | |||
1838 | uri[uri_len] = 0; | ||
1839 | qmark = memchr (uri, | ||
1840 | '?', | ||
1841 | uri_len); | ||
1842 | if (NULL != qmark) | ||
1843 | *qmark = '\0'; | ||
1844 | |||
1845 | /* Need to unescape URI before comparing with connection->url */ | ||
1846 | daemon->unescape_callback (daemon->unescape_callback_cls, | ||
1847 | connection, | ||
1848 | uri); | ||
1849 | if (0 != strcmp (uri, | ||
1850 | connection->url)) | ||
1671 | { | 1851 | { |
1672 | #ifdef HAVE_MESSAGES | 1852 | #ifdef HAVE_MESSAGES |
1673 | MHD_DLOG (daemon, | 1853 | MHD_DLOG (daemon, |
1674 | _ ("Authentication failed, arguments do not match.\n")); | 1854 | _ ("Authentication failed, URI does not match.\n")); |
1855 | err_logged = true; | ||
1675 | #endif | 1856 | #endif |
1676 | free (uri); | 1857 | ret = MHD_DAUTH_WRONG_URI; |
1677 | return MHD_DAUTH_WRONG_URI; | 1858 | break; |
1678 | } | 1859 | } |
1860 | |||
1861 | if (1) | ||
1862 | { | ||
1863 | const char *args = qmark; | ||
1864 | |||
1865 | if (NULL == args) | ||
1866 | args = ""; | ||
1867 | else | ||
1868 | args++; | ||
1869 | if (MHD_NO == | ||
1870 | check_argument_match (connection, | ||
1871 | args) ) | ||
1872 | { | ||
1873 | #ifdef HAVE_MESSAGES | ||
1874 | MHD_DLOG (daemon, | ||
1875 | _ ("Authentication failed, arguments do not match.\n")); | ||
1876 | err_logged = true; | ||
1877 | #endif | ||
1878 | ret = MHD_DAUTH_WRONG_URI; | ||
1879 | break; | ||
1880 | } | ||
1881 | } | ||
1882 | |||
1883 | ret = (0 == strcmp (response, | ||
1884 | digest_get_hex_buffer (da))) | ||
1885 | ? MHD_DAUTH_OK | ||
1886 | : MHD_DAUTH_RESPONSE_WRONG; | ||
1679 | } | 1887 | } |
1680 | free (uri); | 1888 | } while (0); |
1889 | if (NULL != tmp2) | ||
1890 | free (tmp2); | ||
1891 | |||
1892 | if ((MHD_DAUTH_OK != ret) && ! err_logged) | ||
1893 | { | ||
1894 | (void) 0; /* TODO: add logging */ | ||
1681 | } | 1895 | } |
1682 | return (0 == strcmp (response, | 1896 | |
1683 | digest_get_hex_buffer (da))) | 1897 | return ret; |
1684 | ? MHD_DAUTH_OK | ||
1685 | : MHD_DAUTH_RESPONSE_WRONG; | ||
1686 | } | 1898 | } |
1687 | 1899 | ||
1688 | 1900 | ||