Changeset 1003 for pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c
- Timestamp:
- Feb 26, 2007 10:31:06 PM (17 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c
r1002 r1003 22 22 #include <pjlib-util/hmac_sha1.h> 23 23 #include <pjlib-util/md5.h> 24 #include <pjlib-util/sha1.h> 24 25 #include <pj/assert.h> 25 26 #include <pj/log.h> … … 1560 1561 unsigned uattr_cnt; 1561 1562 const pj_uint8_t *start_pdu = pdu; 1563 pj_bool_t has_msg_int = PJ_FALSE; 1564 pj_bool_t has_fingerprint = PJ_FALSE; 1562 1565 pj_status_t status; 1563 1566 … … 1587 1590 1588 1591 pdu += sizeof(pj_stun_msg_hdr); 1589 pdu_len -= sizeof(pj_stun_msg_hdr); 1592 /* pdu_len -= sizeof(pj_stun_msg_hdr); */ 1593 pdu_len = msg->hdr.length; 1590 1594 1591 1595 /* No need to create response if this is not a request */ … … 1688 1692 return status; 1689 1693 } 1690 1694 1695 if (attr_type == PJ_STUN_ATTR_MESSAGE_INTEGRITY && 1696 !has_fingerprint) 1697 { 1698 if (has_msg_int) { 1699 /* Already has MESSAGE-INTEGRITY */ 1700 if (p_response) { 1701 pj_str_t e; 1702 e = pj_str("MESSAGE-INTEGRITY already present"); 1703 pj_stun_msg_create_response(pool, msg, 1704 PJ_STUN_STATUS_BAD_REQUEST, 1705 NULL, p_response); 1706 } 1707 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_BAD_REQUEST); 1708 } 1709 has_msg_int = PJ_TRUE; 1710 1711 } else if (attr_type == PJ_STUN_ATTR_FINGERPRINT) { 1712 if (has_fingerprint) { 1713 /* Already has FINGERPRINT */ 1714 if (p_response) { 1715 pj_str_t e; 1716 e = pj_str("FINGERPRINT already present"); 1717 pj_stun_msg_create_response(pool, msg, 1718 PJ_STUN_STATUS_BAD_REQUEST, 1719 NULL, p_response); 1720 } 1721 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_BAD_REQUEST); 1722 } 1723 has_fingerprint = PJ_TRUE; 1724 } else { 1725 if (has_msg_int || has_fingerprint) { 1726 /* Another attribute is found which is not FINGERPRINT 1727 * after FINGERPRINT or MESSAGE-INTEGRITY */ 1728 if (p_response) { 1729 pj_str_t e; 1730 e = pj_str("Invalid attribute order"); 1731 pj_stun_msg_create_response(pool, msg, 1732 PJ_STUN_STATUS_BAD_REQUEST, 1733 NULL, p_response); 1734 } 1735 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_BAD_REQUEST); 1736 } 1737 } 1738 1691 1739 /* Make sure we have rooms for the new attribute */ 1692 1740 if (msg->attr_count >= PJ_STUN_MAX_ATTR) { … … 1716 1764 1717 1765 return PJ_SUCCESS; 1766 } 1767 1768 /* Calculate HMAC-SHA1 key for long term credential, by getting 1769 * MD5 digest of username, realm, and password. 1770 */ 1771 static void calc_md5_key(pj_uint8_t digest[16], 1772 const pj_str_t *realm, 1773 const pj_str_t *username, 1774 const pj_str_t *passwd) 1775 { 1776 /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking 1777 * the MD5 hash of the result of concatenating the following five 1778 * fields: (1) The username, with any quotes and trailing nulls 1779 * removed, (2) A single colon, (3) The realm, with any quotes and 1780 * trailing nulls removed, (4) A single colon, and (5) The 1781 * password, with any trailing nulls removed. 1782 */ 1783 pj_md5_context ctx; 1784 pj_str_t s; 1785 1786 pj_md5_init(&ctx); 1787 1788 #define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ 1789 s.ptr++, s.slen--; \ 1790 if (s.slen && s.ptr[s.slen-1]=='"') \ 1791 s.slen--; 1792 1793 /* Add username */ 1794 s = *username; 1795 REMOVE_QUOTE(s); 1796 pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); 1797 1798 /* Add single colon */ 1799 pj_md5_update(&ctx, (pj_uint8_t*)":", 1); 1800 1801 /* Add realm */ 1802 s = *realm; 1803 REMOVE_QUOTE(s); 1804 pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); 1805 1806 #undef REMOVE_QUOTE 1807 1808 /* Another colon */ 1809 pj_md5_update(&ctx, (pj_uint8_t*)":", 1); 1810 1811 /* Add password */ 1812 pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, passwd->slen); 1813 1814 /* Done */ 1815 pj_md5_final(&ctx, digest); 1718 1816 } 1719 1817 … … 1796 1894 } 1797 1895 1896 /* We MUST update the message length in the header NOW before 1897 * calculating MESSAGE-INTEGRITY and FINGERPRINT. 1898 * Note that length is not including the 20 bytes header. 1899 */ 1900 if (amsg_integrity && afingerprint) { 1901 length = (pj_uint16_t)((buf - start) - 20 + 24 + 8); 1902 } else if (amsg_integrity) { 1903 length = (pj_uint16_t)((buf - start) - 20 + 24); 1904 } else if (afingerprint) { 1905 length = (pj_uint16_t)((buf - start) - 20 + 8); 1906 } else { 1907 length = (pj_uint16_t)((buf - start) - 20); 1908 } 1909 1910 /* hdr->length = pj_htons(length); */ 1911 *(buf+2) = (pj_uint8_t)((length >> 8) & 0x00FF); 1912 *(buf+3) = (pj_uint8_t)(length & 0x00FF); 1913 1798 1914 /* Calculate message integrity, if present */ 1799 1915 if (amsg_integrity != NULL) { … … 1836 1952 1837 1953 } else { 1838 /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking 1839 * the MD5 hash of the result of concatenating the following five 1840 * fields: (1) The username, with any quotes and trailing nulls 1841 * removed, (2) A single colon, (3) The realm, with any quotes and 1842 * trailing nulls removed, (4) A single colon, and (5) The 1843 * password, with any trailing nulls removed. 1844 */ 1845 pj_md5_context ctx; 1846 pj_str_t s; 1847 1848 pj_md5_init(&ctx); 1849 1850 #define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ 1851 s.ptr++, s.slen--; \ 1852 if (s.slen && s.ptr[s.slen-1]=='"') \ 1853 s.slen--; 1854 1855 /* Add username */ 1856 s = auname->value; 1857 REMOVE_QUOTE(s); 1858 pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); 1859 1860 /* Add single colon */ 1861 pj_md5_update(&ctx, (pj_uint8_t*)":", 1); 1862 1863 /* Add realm */ 1864 s = arealm->value; 1865 REMOVE_QUOTE(s); 1866 pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); 1867 1868 #undef REMOVE_QUOTE 1869 1870 /* Another colon */ 1871 pj_md5_update(&ctx, (pj_uint8_t*)":", 1); 1872 1873 /* Add password */ 1874 pj_md5_update(&ctx, (pj_uint8_t*)password->ptr, password->slen); 1875 1876 /* Done */ 1877 pj_md5_final(&ctx, md5_key_buf); 1954 calc_md5_key(md5_key_buf, &arealm->value, &auname->value, 1955 password); 1878 1956 key.ptr = (char*) md5_key_buf; 1879 1957 key.slen = 16; … … 1910 1988 } 1911 1989 1912 /* Update the message length in the header.1913 * Note that length is not including the 20 bytes header.1914 */1915 length = (pj_uint16_t)((buf - start) - 20);1916 /* hdr->length = pj_htons(length); */1917 *(buf+2) = (pj_uint8_t)((length >> 8) & 0x00FF);1918 *(buf+3) = (pj_uint8_t)(length & 0x00FF);1919 1920 1921 1990 /* Done */ 1922 1991 if (p_msg_len) … … 1946 2015 1947 2016 2017 /**************************************************************************/ 2018 /* 2019 * Authentication 2020 */ 2021 2022 2023 /* Send 401 response */ 2024 static pj_status_t create_challenge(pj_pool_t *pool, 2025 const pj_stun_msg *msg, 2026 int err_code, 2027 const pj_str_t *err_msg, 2028 const pj_str_t *realm, 2029 const pj_str_t *nonce, 2030 pj_stun_msg **p_response) 2031 { 2032 pj_stun_msg *response; 2033 pj_status_t rc; 2034 2035 rc = pj_stun_msg_create_response(pool, msg, 2036 err_code, err_msg, &response); 2037 if (rc != PJ_SUCCESS) 2038 return rc; 2039 2040 2041 if (realm && realm->slen) { 2042 rc = pj_stun_msg_add_generic_string_attr(pool, response, 2043 PJ_STUN_ATTR_REALM, 2044 realm); 2045 if (rc != PJ_SUCCESS) 2046 return rc; 2047 } 2048 2049 if (nonce && nonce->slen) { 2050 rc = pj_stun_msg_add_generic_string_attr(pool, response, 2051 PJ_STUN_ATTR_NONCE, 2052 nonce); 2053 if (rc != PJ_SUCCESS) 2054 return rc; 2055 } 2056 2057 *p_response = response; 2058 2059 return PJ_SUCCESS; 2060 } 2061 1948 2062 /* Verify credential */ 1949 PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_stun_msg *msg, 1950 const pj_str_t *realm, 1951 const pj_str_t *username, 1952 const pj_str_t *password, 1953 unsigned options, 2063 PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, 2064 unsigned pkt_len, 2065 const pj_stun_msg *msg, 2066 pj_stun_auth_policy *pol, 1954 2067 pj_pool_t *pool, 1955 2068 pj_stun_msg **p_response) 1956 2069 { 2070 pj_str_t realm, nonce, password; 1957 2071 const pj_stun_msg_integrity_attr *amsgi; 2072 unsigned amsgi_pos; 1958 2073 const pj_stun_username_attr *auser; 2074 pj_bool_t username_ok; 1959 2075 const pj_stun_realm_attr *arealm; 1960 1961 PJ_ASSERT_RETURN(msg && password, PJ_EINVAL); 1962 PJ_ASSERT_RETURN(options==0, PJ_EINVAL); 1963 PJ_UNUSED_ARG(options); 2076 const pj_stun_realm_attr *anonce; 2077 pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; 2078 pj_uint8_t md5_digest[16]; 2079 pj_str_t key; 2080 pj_status_t status; 2081 2082 /* msg and policy MUST be specified */ 2083 PJ_ASSERT_RETURN(pkt && pkt_len && msg && pol, PJ_EINVAL); 2084 2085 /* If p_response is specified, pool MUST be specified. */ 2086 PJ_ASSERT_RETURN(!p_response || pool, PJ_EINVAL); 1964 2087 1965 2088 if (p_response) 1966 2089 *p_response = NULL; 2090 2091 if (PJ_STUN_IS_REQUEST(msg->hdr.type)) 2092 p_response = NULL; 2093 2094 /* Get realm and nonce */ 2095 realm.slen = nonce.slen = 0; 2096 if (pol->type == PJ_STUN_POLICY_STATIC_SHORT_TERM) { 2097 realm.slen = 0; 2098 nonce = pol->data.static_short_term.nonce; 2099 } else if (pol->type == PJ_STUN_POLICY_STATIC_LONG_TERM) { 2100 realm = pol->data.static_long_term.realm; 2101 nonce = pol->data.static_long_term.nonce; 2102 } else if (pol->type == PJ_STUN_POLICY_DYNAMIC) { 2103 status = pol->data.dynamic.get_auth(pol->user_data, pool, 2104 &realm, &nonce); 2105 if (status != PJ_SUCCESS) 2106 return status; 2107 } else { 2108 pj_assert(!"Unexpected"); 2109 return PJ_EBUG; 2110 } 1967 2111 1968 2112 /* First check that MESSAGE-INTEGRITY is present */ … … 1970 2114 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); 1971 2115 if (amsgi == NULL) { 1972 if (pool && p_response) { 1973 pj_status_t rc; 1974 1975 rc = pj_stun_msg_create_response(pool, msg, 1976 PJ_STUN_STATUS_UNAUTHORIZED, 1977 NULL, p_response); 1978 if (rc==PJ_SUCCESS && realm) { 1979 pj_stun_msg_add_generic_string_attr(pool, *p_response, 1980 PJ_STUN_ATTR_REALM, 1981 realm); 1982 } 2116 if (p_response) { 2117 create_challenge(pool, msg, PJ_STUN_STATUS_UNAUTHORIZED, NULL, 2118 &realm, &nonce, p_response); 1983 2119 } 1984 2120 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_UNAUTHORIZED); … … 1989 2125 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); 1990 2126 if (auser == NULL) { 1991 if (pool && p_response) { 1992 pj_status_t rc; 1993 1994 rc = pj_stun_msg_create_response(pool, msg, 1995 PJ_STUN_STATUS_MISSING_USERNAME, 1996 NULL, p_response); 1997 if (rc==PJ_SUCCESS && realm) { 1998 pj_stun_msg_add_generic_string_attr(pool, *p_response, 1999 PJ_STUN_ATTR_REALM, 2000 realm); 2001 } 2127 if (p_response) { 2128 create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_USERNAME, NULL, 2129 &realm, &nonce, p_response); 2002 2130 } 2003 2131 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_USERNAME); 2004 2132 } 2005 2133 2006 /* Check if username match */ 2007 if (username && pj_stricmp(&auser->value, username) != 0) { 2008 /* Username mismatch */ 2009 if (pool && p_response) { 2010 pj_status_t rc; 2011 2012 rc = pj_stun_msg_create_response(pool, msg, 2013 PJ_STUN_STATUS_WRONG_USERNAME, 2014 NULL, p_response); 2015 if (rc==PJ_SUCCESS && realm) { 2016 pj_stun_msg_add_generic_string_attr(pool, *p_response, 2017 PJ_STUN_ATTR_REALM, 2018 realm); 2019 } 2020 } 2021 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_WRONG_USERNAME); 2022 } 2023 2024 /* Next check that REALM is present */ 2134 /* Get REALM, if any */ 2025 2135 arealm = (const pj_stun_realm_attr*) 2026 2136 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); 2027 if (realm != NULL && arealm == NULL) { 2028 /* Long term credential is required */ 2029 if (pool && p_response) { 2030 pj_status_t rc; 2031 2032 rc = pj_stun_msg_create_response(pool, msg, 2033 PJ_STUN_STATUS_MISSING_REALM, 2034 NULL, p_response); 2035 if (rc==PJ_SUCCESS) { 2036 pj_stun_msg_add_generic_string_attr(pool, *p_response, 2037 PJ_STUN_ATTR_REALM, 2038 realm); 2039 } 2137 2138 /* Check if username match */ 2139 if (pol->type == PJ_STUN_POLICY_STATIC_SHORT_TERM) { 2140 username_ok = !pj_strcmp(&auser->value, 2141 &pol->data.static_short_term.username); 2142 password = pol->data.static_short_term.password; 2143 } else if (pol->type == PJ_STUN_POLICY_STATIC_LONG_TERM) { 2144 username_ok = !pj_strcmp(&auser->value, 2145 &pol->data.static_long_term.username); 2146 password = pol->data.static_long_term.password; 2147 } else if (pol->type == PJ_STUN_POLICY_DYNAMIC) { 2148 pj_status_t rc; 2149 rc = pol->data.dynamic.get_password(pol->user_data, 2150 (arealm?&arealm->value:NULL), 2151 &auser->value, pool, 2152 &password); 2153 username_ok = (rc == PJ_SUCCESS); 2154 } else { 2155 username_ok = PJ_TRUE; 2156 password.slen = 0; 2157 } 2158 2159 if (!username_ok) { 2160 /* Username mismatch */ 2161 if (p_response) { 2162 create_challenge(pool, msg, PJ_STUN_STATUS_UNKNOWN_USERNAME, NULL, 2163 &realm, &nonce, p_response); 2164 } 2165 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_UNKNOWN_USERNAME); 2166 } 2167 2168 2169 /* Get NONCE attribute */ 2170 anonce = (pj_stun_nonce_attr*) 2171 pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_NONCE, 0); 2172 2173 /* Check for long term/short term requirements. */ 2174 if (realm.slen != 0 && arealm == NULL) { 2175 /* Long term credential is required and REALM is not present */ 2176 if (p_response) { 2177 create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, NULL, 2178 &realm, &nonce, p_response); 2040 2179 } 2041 2180 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_REALM); 2042 2181 2043 } else if (realm != NULL && arealm != NULL) { 2044 2045 2046 } else if (realm == NULL && arealm != NULL) { 2182 } else if (realm.slen != 0 && arealm != NULL) { 2183 /* We want long term, and REALM is present */ 2184 2185 /* NONCE must be present. */ 2186 if (anonce == NULL) { 2187 if (p_response) { 2188 create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, 2189 NULL, &realm, &nonce, p_response); 2190 } 2191 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_NONCE); 2192 } 2193 2194 /* Verify REALM matches */ 2195 if (pj_stricmp(&arealm->value, &realm)) { 2196 /* REALM doesn't match */ 2197 if (p_response) { 2198 create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_REALM, 2199 NULL, &realm, &nonce, p_response); 2200 } 2201 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_REALM); 2202 } 2203 2204 /* Valid case, will validate the message integrity later */ 2205 2206 } else if (realm.slen == 0 && arealm != NULL) { 2047 2207 /* We want to use short term credential, but client uses long 2048 2208 * term credential. The draft doesn't mention anything about 2049 2209 * switching between long term and short term. 2050 2210 */ 2051 PJ_TODO(SWITCHING_BETWEEN_SHORT_TERM_AND_LONG_TERM); 2052 } 2053 2054 PJ_TODO(CONTINUE_IMPLEMENTATION); 2055 2056 return PJ_SUCCESS; 2057 } 2058 2059 2211 2212 /* For now just accept the credential, anyway it will probably 2213 * cause wrong message integrity value later. 2214 */ 2215 } else if (realm.slen==0 && arealm == NULL) { 2216 /* Short term authentication is wanted, and one is supplied */ 2217 2218 /* Application MAY request NONCE to be supplied */ 2219 if (nonce.slen != 0) { 2220 if (p_response) { 2221 create_challenge(pool, msg, PJ_STUN_STATUS_MISSING_NONCE, 2222 NULL, &realm, &nonce, p_response); 2223 } 2224 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_NONCE); 2225 } 2226 } 2227 2228 /* If NONCE is present, validate it */ 2229 if (anonce) { 2230 pj_bool_t ok; 2231 2232 if (pol->type == PJ_STUN_POLICY_DYNAMIC) { 2233 ok = pol->data.dynamic.verify_nonce(pol->user_data, 2234 (arealm?&arealm->value:NULL), 2235 &auser->value, 2236 &anonce->value); 2237 } else { 2238 if (nonce.slen) { 2239 ok = !pj_strcmp(&anonce->value, &nonce); 2240 } else { 2241 ok = PJ_TRUE; 2242 } 2243 } 2244 2245 if (!ok) { 2246 if (p_response) { 2247 create_challenge(pool, msg, PJ_STUN_STATUS_STALE_NONCE, 2248 NULL, &realm, &nonce, p_response); 2249 } 2250 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_STALE_NONCE); 2251 } 2252 } 2253 2254 /* Get the position of MESSAGE-INTEGRITY in the packet */ 2255 amsgi_pos = 20+msg->hdr.length-22; 2256 if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { 2257 /* Found MESSAGE-INTEGRITY as the last attribute */ 2258 } else { 2259 amsgi_pos = 0; 2260 } 2261 2262 if (amsgi_pos==0) { 2263 amsgi_pos = 20+msg->hdr.length-8-22; 2264 if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { 2265 /* Found MESSAGE-INTEGRITY before FINGERPRINT */ 2266 } else { 2267 amsgi_pos = 0; 2268 } 2269 } 2270 2271 if (amsgi_pos==0) { 2272 pj_assert(!"Unable to find MESSAGE-INTEGRITY in the message!"); 2273 return PJ_EBUG; 2274 } 2275 2276 /* Determine which key to use */ 2277 if (realm.slen) { 2278 calc_md5_key(md5_digest, &realm, &auser->value, &password); 2279 key.ptr = (char*)md5_digest; 2280 key.slen = 16; 2281 } else { 2282 key = password; 2283 } 2284 2285 /* Now calculate HMAC of the message */ 2286 pj_hmac_sha1(pkt, amsgi_pos, (pj_uint8_t*)key.ptr, key.slen, digest); 2287 2288 /* Compare HMACs */ 2289 if (pj_memcmp(amsgi->hmac, digest, 20)) { 2290 /* HMAC value mismatch */ 2291 if (p_response) { 2292 create_challenge(pool, msg, PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE, 2293 NULL, &realm, &nonce, p_response); 2294 } 2295 return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_INTEGRITY_CHECK_FAILURE); 2296 } 2297 2298 /* Everything looks okay! */ 2299 return PJ_SUCCESS; 2300 } 2301 2302
Note: See TracChangeset
for help on using the changeset viewer.