diff options
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 | ||