- Timestamp:
- Feb 26, 2007 10:31:06 PM (18 years ago)
- Location:
- pjproject/trunk/pjlib-util
- Files:
-
- 1 added
- 10 edited
Legend:
- Unmodified
- Added
- Removed
-
pjproject/trunk/pjlib-util/build/pjlib_util_test.dsp
r1001 r1003 43 43 # PROP Target_Dir "" 44 44 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 45 # ADD CPP /nologo /MD /W 3/GX /O2 /I "../include" /I "../../pjlib/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /c45 # ADD CPP /nologo /MD /W4 /GX /O2 /I "../include" /I "../../pjlib/include" /D "NDEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /c 46 46 # ADD BASE RSC /l 0x409 /d "NDEBUG" 47 47 # ADD RSC /l 0x409 /d "NDEBUG" … … 67 67 # PROP Target_Dir "" 68 68 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c 69 # ADD CPP /nologo /MTd /W 3/Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /GZ /c69 # ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /D "_DEBUG" /D "WIN32" /D "_CONSOLE" /D "_MBCS" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /YX /FD /GZ /c 70 70 # ADD BASE RSC /l 0x409 /d "_DEBUG" 71 71 # ADD RSC /l 0x409 /d "_DEBUG" … … 96 96 # Begin Source File 97 97 98 SOURCE="..\src\pjlib-util-test\stun.c" 99 # End Source File 100 # Begin Source File 101 98 102 SOURCE="..\src\pjlib-util-test\test.c" 99 103 # End Source File -
pjproject/trunk/pjlib-util/build/pjlib_util_test.vcproj
r1001 r1003 234 234 </File> 235 235 <File 236 RelativePath="..\src\pjlib-util-test\stun.c" 237 > 238 </File> 239 <File 236 240 RelativePath="..\src\pjlib-util-test\test.c" 237 241 > -
pjproject/trunk/pjlib-util/include/pjlib-util/config.h
r992 r1003 212 212 #endif 213 213 214 215 /* ************************************************************************** 216 * ENCRYPTION 217 */ 218 219 /** 220 * Specifies whether CRC32 algorithm should use the table based lookup table 221 * for faster calculation, at the expense of about 1KB table size on the 222 * executable. If zero, the CRC32 will use non-table based which is more than 223 * an order of magnitude slower. 224 * 225 * Default: 1 226 */ 227 #ifndef PJ_CRC32_HAS_TABLES 228 # define PJ_CRC32_HAS_TABLES 1 229 #endif 230 231 214 232 /** 215 233 * @} -
pjproject/trunk/pjlib-util/include/pjlib-util/crc32.h
r1002 r1003 25 25 */ 26 26 27 #include <pj /types.h>27 #include <pjlib-util/types.h> 28 28 29 29 PJ_BEGIN_DECL -
pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h
r1002 r1003 1246 1246 pj_stun_msg **p_response); 1247 1247 1248 typedef enum pj_stun_auth_policy_type 1249 { 1250 PJ_STUN_POLICY_NONE, 1251 PJ_STUN_POLICY_STATIC_SHORT_TERM, 1252 PJ_STUN_POLICY_STATIC_LONG_TERM, 1253 PJ_STUN_POLICY_DYNAMIC 1254 } pj_stun_auth_policy_type; 1255 1256 typedef struct pj_stun_auth_policy 1257 { 1258 pj_stun_auth_policy_type type; 1259 void *user_data; 1260 1261 union 1262 { 1263 struct 1264 { 1265 pj_str_t username; 1266 pj_str_t password; 1267 pj_str_t nonce; 1268 } static_short_term; 1269 1270 struct 1271 { 1272 pj_str_t realm; 1273 pj_str_t username; 1274 pj_str_t password; 1275 pj_str_t nonce; 1276 } static_long_term; 1277 1278 struct 1279 { 1280 /** 1281 * This callback is called by pj_stun_verify_credential() when 1282 * server needs to challenge the request with 401 response. 1283 * 1284 * @param user_data The user data as specified in the policy. 1285 * @param pool Pool to allocate memory. 1286 * @param realm On return, the function should fill in with 1287 * realm if application wants to use long term 1288 * credential. Otherwise application should set 1289 * empty string for the realm. 1290 * @param nonce On return, if application wants to use long 1291 * term credential, it MUST fill in the nonce 1292 * with some value. Otherwise if short term 1293 * credential is wanted, it MAY set this value. 1294 * If short term credential is wanted and the 1295 * application doesn't want to include NONCE, 1296 * then it must set this to empty string. 1297 * 1298 * @return The callback should return PJ_SUCCESS, or 1299 * otherwise response message will not be 1300 * created. 1301 */ 1302 pj_status_t (*get_auth)(void *user_data, 1303 pj_pool_t *pool, 1304 pj_str_t *realm, 1305 pj_str_t *nonce); 1306 1307 /** 1308 * Get the password for the specified username. This function 1309 * is also used to check whether the username is valid. 1310 * 1311 * @param user_data The user data as specified in the policy. 1312 * @param realm The realm as specified in the message. 1313 * @param username The username as specified in the message. 1314 * @param pool Pool to allocate memory when necessary. 1315 * @param password On return, application should fill up this 1316 * argument with the password. 1317 * 1318 * @return The callback should return PJ_SUCCESS if 1319 * username has been successfully verified 1320 * and password was obtained. If non-PJ_SUCCESS 1321 * is returned, it is assumed that the 1322 * username is not valid. 1323 */ 1324 pj_status_t (*get_password)(void *user_data, 1325 const pj_str_t *realm, 1326 const pj_str_t *username, 1327 pj_pool_t *pool, 1328 pj_str_t *password); 1329 pj_bool_t (*require_nonce)(void *user_data, 1330 const pj_str_t *realm, 1331 const pj_str_t *username); 1332 pj_bool_t (*verify_nonce)(void *data, 1333 const pj_str_t *realm, 1334 const pj_str_t *username, 1335 const pj_str_t *nonce); 1336 pj_status_t (*make_nonce)(void *user_data, 1337 const pj_str_t *realm, 1338 const pj_str_t *username, 1339 pj_pool_t *pool, 1340 pj_str_t *nonce); 1341 } dynamic; 1342 1343 } data; 1344 1345 } pj_stun_auth_policy; 1346 1347 1248 1348 /** 1249 1349 * Verify credential in the STUN message. Note that before calling this … … 1253 1353 * if it doesn't contain PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute. 1254 1354 * 1255 * @param msg The message to be verified. 1256 * @param realm Realm, if long term credential is required. If 1257 * short term credential is required, this argument 1258 * must be set to NULL. 1259 * @param username If this attribute is specified, then the USERNAME 1260 * attribute in the message will be compared against 1261 * this value. If NULL is specified, then this function 1262 * will accept any usernames. 1263 * @param password The password. 1264 * @param options Options, must be zero for now. 1355 * @param pkt The original packet which has been parsed into 1356 * the message. This packet MUST NOT have been modified 1357 * after the parsing. 1358 * @param pkt_len The length of the packet. 1359 * @param msg The parsed message to be verified. 1360 * @param policy Pointer to authentication policy. 1265 1361 * @param pool If response is to be created, then memory will 1266 1362 * be allocated from this pool. … … 1274 1370 * \a p_response. 1275 1371 */ 1276 PJ_DECL(pj_status_t) pj_stun_verify_credential(const pj_stun_msg *msg, 1277 const pj_str_t *realm, 1278 const pj_str_t *username, 1279 const pj_str_t *password, 1280 unsigned options, 1372 PJ_DECL(pj_status_t) pj_stun_verify_credential(const pj_uint8_t *pkt, 1373 unsigned pkt_len, 1374 const pj_stun_msg *msg, 1375 pj_stun_auth_policy *policy, 1281 1376 pj_pool_t *pool, 1282 1377 pj_stun_msg **p_response); -
pjproject/trunk/pjlib-util/src/pjlib-util-test/encryption.c
r1002 r1003 423 423 PJ_LOG(3, (THIS_FILE, " crc32 test..")); 424 424 425 /* testing pj_crc32_calc */ 425 426 for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) { 426 427 pj_uint32_t crc; … … 433 434 } 434 435 } 436 437 /* testing incremental CRC32 calculation */ 438 for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) { 439 pj_crc32_context ctx; 440 pj_uint32_t crc0, crc1; 441 unsigned len; 442 443 len = pj_ansi_strlen(crc32_test_data[i].input); 444 crc0 = pj_crc32_calc((pj_uint8_t*)crc32_test_data[i].input, len); 445 446 pj_crc32_init(&ctx); 447 pj_crc32_update(&ctx, (pj_uint8_t*)crc32_test_data[i].input, 448 len / 2); 449 450 if (len/2 > 0) { 451 pj_crc32_update(&ctx, (pj_uint8_t*)crc32_test_data[i].input + len/2, 452 len - len/2); 453 } 454 455 crc1 = pj_crc32_final(&ctx); 456 457 if (crc0 != crc1) { 458 PJ_LOG(3,(THIS_FILE, 459 " error: crc algorithm error on test %d", i)); 460 return -85; 461 } 462 463 } 435 464 return 0; 436 465 } … … 460 489 } 461 490 491 static void crc32_update(pj_crc32_context *c, const pj_uint8_t *data, 492 pj_size_t nbytes) 493 { 494 pj_crc32_update(c, data, nbytes); 495 } 496 497 static void crc32_final(pj_crc32_context *ctx, pj_uint32_t *digest) 498 { 499 *digest = pj_crc32_final(ctx); 500 } 501 502 int encryption_benchmark() 503 { 504 pj_pool_t *pool; 505 pj_uint8_t *input; 506 union { 507 pj_md5_context md5_context; 508 pj_sha1_context sha1_context; 509 } context; 510 pj_uint8_t digest[32]; 511 pj_size_t input_len; 512 struct algorithm 513 { 514 const char *name; 515 void (*init_context)(void*); 516 void (*update)(void*, const pj_uint8_t*, unsigned); 517 void (*final)(void*, void*); 518 pj_uint32_t t; 519 } algorithms[] = 520 { 521 { 522 "MD5 ", 523 &pj_md5_init, 524 &pj_md5_update, 525 &pj_md5_final 526 }, 527 { 528 "SHA1 ", 529 &pj_sha1_init, 530 &pj_sha1_update, 531 &pj_sha1_final 532 }, 533 { 534 "CRC32", 535 &pj_crc32_init, 536 &crc32_update, 537 &crc32_final 538 } 539 }; 540 #if defined(PJ_DEBUG) && PJ_DEBUG!=0 541 enum { LOOP = 1000 }; 542 #else 543 enum { LOOP = 10000 }; 544 #endif 545 unsigned i; 546 double total_len; 547 548 input_len = 2048; 549 total_len = input_len * LOOP; 550 pool = pj_pool_create(mem, "enc", input_len+256, 0, NULL); 551 if (!pool) 552 return PJ_ENOMEM; 553 554 input = pj_pool_alloc(pool, input_len); 555 pj_memset(input, '\xaa', input_len); 556 557 PJ_LOG(3, (THIS_FILE, " feeding %d Mbytes of data", 558 (unsigned)(total_len/1024/1024))); 559 560 /* Dry run */ 561 for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) { 562 algorithms[i].init_context(&context); 563 algorithms[i].update(&context, input, input_len); 564 algorithms[i].final(&context, digest); 565 } 566 567 /* Run */ 568 for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) { 569 int j; 570 pj_timestamp t1, t2; 571 572 pj_get_timestamp(&t1); 573 algorithms[i].init_context(&context); 574 for (j=0; j<LOOP; ++j) { 575 algorithms[i].update(&context, input, input_len); 576 } 577 algorithms[i].final(&context, digest); 578 pj_get_timestamp(&t2); 579 580 algorithms[i].t = pj_elapsed_usec(&t1, &t2); 581 } 582 583 /* Results */ 584 for (i=0; i<PJ_ARRAY_SIZE(algorithms); ++i) { 585 double bytes; 586 587 bytes = (total_len * 1000000 / algorithms[i].t); 588 PJ_LOG(3, (THIS_FILE, " %s:%8d usec (%3d.%03d Mbytes/sec)", 589 algorithms[i].name, algorithms[i].t, 590 (unsigned)(bytes / 1024 / 1024), 591 ((unsigned)(bytes) % (1024 * 1024)) / 1024)); 592 } 593 594 return 0; 595 } 596 462 597 463 598 -
pjproject/trunk/pjlib-util/src/pjlib-util-test/test.c
r1001 r1003 69 69 #if INCLUDE_ENCRYPTION_TEST 70 70 DO_TEST(encryption_test()); 71 DO_TEST(encryption_benchmark()); 71 72 #endif 72 73 74 #if INCLUDE_STUN_TEST 75 DO_TEST(stun_test()); 76 #endif 73 77 74 78 on_return: -
pjproject/trunk/pjlib-util/src/pjlib-util-test/test.h
r1001 r1003 21 21 #define INCLUDE_XML_TEST 1 22 22 #define INCLUDE_ENCRYPTION_TEST 1 23 #define INCLUDE_STUN_TEST 1 23 24 24 25 extern int xml_test(void); 25 26 extern int encryption_test(); 27 extern int encryption_benchmark(); 28 extern int stun_test(); 26 29 extern int test_main(void); 27 30 -
pjproject/trunk/pjlib-util/src/pjlib-util/crc32.c
r1002 r1003 4 4 * for a formal specification 5 5 * 6 * This file is partly taken from Crypto++ library (http://www.cryptopp.com). 6 * This file is partly taken from Crypto++ library (http://www.cryptopp.com) 7 * and http://www.di-mgt.com.au/crypto.html#CRC. 7 8 * 8 9 * Since the original version of the code is put in public domain, … … 11 12 #include <pjlib-util/crc32.h> 12 13 14 15 #define CRC32_NEGL 0xffffffffL 16 17 #if defined(PJ_CRC32_HAS_TABLES) && PJ_CRC32_HAS_TABLES!=0 13 18 // crc.cpp - written and placed in the public domain by Wei Dai 14 19 … … 139 144 140 145 141 #define CRC32_NEGL 0xffffffffL142 143 144 146 PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) 145 147 { … … 181 183 } 182 184 185 186 #else 187 188 PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) 189 { 190 ctx->crc_state = CRC32_NEGL; 191 } 192 193 194 PJ_DEF(pj_uint32_t) pj_crc32_update(pj_crc32_context *ctx, 195 const pj_uint8_t *octets, 196 pj_size_t len) 197 198 { 199 pj_uint32_t crc = ctx->crc_state; 200 201 while (len--) { 202 pj_uint32_t temp; 203 int j; 204 205 temp = (pj_uint32_t)((crc & 0xFF) ^ *octets++); 206 for (j = 0; j < 8; j++) 207 { 208 if (temp & 0x1) 209 temp = (temp >> 1) ^ 0xEDB88320; 210 else 211 temp >>= 1; 212 } 213 crc = (crc >> 8) ^ temp; 214 } 215 ctx->crc_state = crc; 216 217 return crc ^ CRC32_NEGL; 218 } 219 220 PJ_DEF(pj_uint32_t) pj_crc32_final(pj_crc32_context *ctx) 221 { 222 ctx->crc_state ^= CRC32_NEGL; 223 return ctx->crc_state; 224 } 225 226 #endif 227 228 183 229 PJ_DEF(pj_uint32_t) pj_crc32_calc( const pj_uint8_t *data, 184 230 pj_size_t nbytes) -
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.