Changeset 1003 for pjproject/trunk


Ignore:
Timestamp:
Feb 26, 2007 10:31:06 PM (18 years ago)
Author:
bennylp
Message:

Finishing up STUN server side authentication

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  
    4343# PROP Target_Dir "" 
    4444# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c 
    45 # ADD CPP /nologo /MD /W3 /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 
     45# 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 
    4646# ADD BASE RSC /l 0x409 /d "NDEBUG" 
    4747# ADD RSC /l 0x409 /d "NDEBUG" 
     
    6767# PROP Target_Dir "" 
    6868# 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 /W3 /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 
     69# 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 
    7070# ADD BASE RSC /l 0x409 /d "_DEBUG" 
    7171# ADD RSC /l 0x409 /d "_DEBUG" 
     
    9696# Begin Source File 
    9797 
     98SOURCE="..\src\pjlib-util-test\stun.c" 
     99# End Source File 
     100# Begin Source File 
     101 
    98102SOURCE="..\src\pjlib-util-test\test.c" 
    99103# End Source File 
  • pjproject/trunk/pjlib-util/build/pjlib_util_test.vcproj

    r1001 r1003  
    234234                        </File> 
    235235                        <File 
     236                                RelativePath="..\src\pjlib-util-test\stun.c" 
     237                                > 
     238                        </File> 
     239                        <File 
    236240                                RelativePath="..\src\pjlib-util-test\test.c" 
    237241                                > 
  • pjproject/trunk/pjlib-util/include/pjlib-util/config.h

    r992 r1003  
    212212#endif 
    213213 
     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 
    214232/** 
    215233 * @} 
  • pjproject/trunk/pjlib-util/include/pjlib-util/crc32.h

    r1002 r1003  
    2525 */ 
    2626 
    27 #include <pj/types.h> 
     27#include <pjlib-util/types.h> 
    2828 
    2929PJ_BEGIN_DECL 
  • pjproject/trunk/pjlib-util/include/pjlib-util/stun_msg.h

    r1002 r1003  
    12461246                                        pj_stun_msg **p_response); 
    12471247 
     1248typedef 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 
     1256typedef 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 
    12481348/** 
    12491349 * Verify credential in the STUN message. Note that before calling this 
     
    12531353 * if it doesn't contain PJ_STUN_ATTR_MESSAGE_INTEGRITY attribute. 
    12541354 * 
    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. 
    12651361 * @param pool          If response is to be created, then memory will 
    12661362 *                      be allocated from this pool. 
     
    12741370 *                      \a p_response. 
    12751371 */ 
    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, 
     1372PJ_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, 
    12811376                                               pj_pool_t *pool, 
    12821377                                               pj_stun_msg **p_response); 
  • pjproject/trunk/pjlib-util/src/pjlib-util-test/encryption.c

    r1002 r1003  
    423423    PJ_LOG(3, (THIS_FILE, "  crc32 test..")); 
    424424 
     425    /* testing pj_crc32_calc */ 
    425426    for (i=0; i<PJ_ARRAY_SIZE(crc32_test_data); ++i) { 
    426427        pj_uint32_t crc; 
     
    433434        } 
    434435    } 
     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    } 
    435464    return 0; 
    436465} 
     
    460489} 
    461490 
     491static 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 
     497static void crc32_final(pj_crc32_context *ctx, pj_uint32_t *digest) 
     498{ 
     499    *digest = pj_crc32_final(ctx); 
     500} 
     501 
     502int 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 
    462597 
    463598 
  • pjproject/trunk/pjlib-util/src/pjlib-util-test/test.c

    r1001 r1003  
    6969#if INCLUDE_ENCRYPTION_TEST 
    7070    DO_TEST(encryption_test()); 
     71    DO_TEST(encryption_benchmark()); 
    7172#endif 
    7273 
     74#if INCLUDE_STUN_TEST 
     75    DO_TEST(stun_test()); 
     76#endif 
    7377 
    7478on_return: 
  • pjproject/trunk/pjlib-util/src/pjlib-util-test/test.h

    r1001 r1003  
    2121#define INCLUDE_XML_TEST            1 
    2222#define INCLUDE_ENCRYPTION_TEST     1 
     23#define INCLUDE_STUN_TEST           1 
    2324 
    2425extern int xml_test(void); 
    2526extern int encryption_test(); 
     27extern int encryption_benchmark(); 
     28extern int stun_test(); 
    2629extern int test_main(void); 
    2730 
  • pjproject/trunk/pjlib-util/src/pjlib-util/crc32.c

    r1002 r1003  
    44 * for a formal specification 
    55 * 
    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. 
    78 * 
    89 * Since the original version of the code is put in public domain, 
     
    1112#include <pjlib-util/crc32.h> 
    1213 
     14 
     15#define CRC32_NEGL  0xffffffffL 
     16 
     17#if defined(PJ_CRC32_HAS_TABLES) && PJ_CRC32_HAS_TABLES!=0 
    1318// crc.cpp - written and placed in the public domain by Wei Dai 
    1419 
     
    139144 
    140145 
    141 #define CRC32_NEGL  0xffffffffL 
    142  
    143  
    144146PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) 
    145147{ 
     
    181183} 
    182184 
     185 
     186#else 
     187 
     188PJ_DEF(void) pj_crc32_init(pj_crc32_context *ctx) 
     189{ 
     190    ctx->crc_state = CRC32_NEGL; 
     191} 
     192 
     193 
     194PJ_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 
     220PJ_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 
    183229PJ_DEF(pj_uint32_t) pj_crc32_calc( const pj_uint8_t *data, 
    184230                                   pj_size_t nbytes) 
  • pjproject/trunk/pjlib-util/src/pjlib-util/stun_msg.c

    r1002 r1003  
    2222#include <pjlib-util/hmac_sha1.h> 
    2323#include <pjlib-util/md5.h> 
     24#include <pjlib-util/sha1.h> 
    2425#include <pj/assert.h> 
    2526#include <pj/log.h> 
     
    15601561    unsigned uattr_cnt; 
    15611562    const pj_uint8_t *start_pdu = pdu; 
     1563    pj_bool_t has_msg_int = PJ_FALSE; 
     1564    pj_bool_t has_fingerprint = PJ_FALSE; 
    15621565    pj_status_t status; 
    15631566 
     
    15871590 
    15881591    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; 
    15901594 
    15911595    /* No need to create response if this is not a request */ 
     
    16881692                return status; 
    16891693            } 
    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 
    16911739            /* Make sure we have rooms for the new attribute */ 
    16921740            if (msg->attr_count >= PJ_STUN_MAX_ATTR) { 
     
    17161764 
    17171765    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 */ 
     1771static 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); 
    17181816} 
    17191817 
     
    17961894    } 
    17971895 
     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 
    17981914    /* Calculate message integrity, if present */ 
    17991915    if (amsg_integrity != NULL) { 
     
    18361952 
    18371953        } 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); 
    18781956            key.ptr = (char*) md5_key_buf; 
    18791957            key.slen = 16; 
     
    19101988    } 
    19111989 
    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  
    19211990    /* Done */ 
    19221991    if (p_msg_len) 
     
    19462015 
    19472016 
     2017/**************************************************************************/ 
     2018/* 
     2019 * Authentication 
     2020 */ 
     2021 
     2022 
     2023/* Send 401 response */ 
     2024static 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 
    19482062/* 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, 
     2063PJ_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, 
    19542067                                               pj_pool_t *pool, 
    19552068                                               pj_stun_msg **p_response) 
    19562069{ 
     2070    pj_str_t realm, nonce, password; 
    19572071    const pj_stun_msg_integrity_attr *amsgi; 
     2072    unsigned amsgi_pos; 
    19582073    const pj_stun_username_attr *auser; 
     2074    pj_bool_t username_ok; 
    19592075    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); 
    19642087 
    19652088    if (p_response) 
    19662089        *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    } 
    19672111 
    19682112    /* First check that MESSAGE-INTEGRITY is present */ 
     
    19702114            pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); 
    19712115    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); 
    19832119        } 
    19842120        return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_UNAUTHORIZED); 
     
    19892125            pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0); 
    19902126    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); 
    20022130        } 
    20032131        return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_USERNAME); 
    20042132    } 
    20052133 
    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 */ 
    20252135    arealm = (const pj_stun_realm_attr*) 
    20262136             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); 
    20402179        } 
    20412180        return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_STATUS_MISSING_REALM); 
    20422181 
    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) { 
    20472207        /* We want to use short term credential, but client uses long 
    20482208         * term credential. The draft doesn't mention anything about 
    20492209         * switching between long term and short term. 
    20502210         */ 
    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.