From 240fa9cd6f0ede43211b799f4706df92c62a5be9 Mon Sep 17 00:00:00 2001 From: Benny Prijono Date: Tue, 15 May 2007 10:42:56 +0000 Subject: Fixed several STUN bugs: USERNAME, REALM etc are not allowed in the response, retransmission timer calculation bug, etc. git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@1275 74dad513-b988-da41-8d7b-12977e46ad98 --- pjnath/src/pjnath/ice_session.c | 20 ++--- pjnath/src/pjnath/stun_auth.c | 105 +++++++++++++++++++----- pjnath/src/pjnath/stun_msg.c | 110 ++++++++++++-------------- pjnath/src/pjnath/stun_session.c | 136 ++++++++++++++++++++------------ pjnath/src/pjnath/stun_transaction.c | 15 ++-- pjnath/src/pjstun-client/client_main.c | 15 +++- pjnath/src/pjstun-srv-test/bind_usage.c | 2 +- pjnath/src/pjstun-srv-test/main.c | 17 +++- pjnath/src/pjstun-srv-test/server.h | 2 + pjnath/src/pjstun-srv-test/turn_usage.c | 37 ++++++--- 10 files changed, 300 insertions(+), 159 deletions(-) (limited to 'pjnath/src') diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 085227dd..246e4564 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -1869,9 +1869,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, /* Reject any requests except Binding request */ if (msg->hdr.type != PJ_STUN_BINDING_REQUEST) { - status = pj_stun_session_create_response(sess, msg, - PJ_STUN_SC_BAD_REQUEST, - NULL, &tdata); + status = pj_stun_session_create_res(sess, msg, + PJ_STUN_SC_BAD_REQUEST, + NULL, &tdata); if (status != PJ_SUCCESS) return status; @@ -1927,9 +1927,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, pj_ice_sess_change_role(ice, PJ_ICE_SESS_ROLE_CONTROLLED); } else { /* Generate 487 response */ - status = pj_stun_session_create_response(sess, msg, - PJ_STUN_SC_ROLE_CONFLICT, - NULL, &tdata); + status = pj_stun_session_create_res(sess, msg, + PJ_STUN_SC_ROLE_CONFLICT, + NULL, &tdata); if (status == PJ_SUCCESS) { pj_stun_session_send_msg(sess, PJ_TRUE, src_addr, src_addr_len, tdata); @@ -1943,9 +1943,9 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, { if (pj_cmp_timestamp(&ice->tie_breaker, &role_attr->value) < 0) { /* Generate 487 response */ - status = pj_stun_session_create_response(sess, msg, - PJ_STUN_SC_ROLE_CONFLICT, - NULL, &tdata); + status = pj_stun_session_create_res(sess, msg, + PJ_STUN_SC_ROLE_CONFLICT, + NULL, &tdata); if (status == PJ_SUCCESS) { pj_stun_session_send_msg(sess, PJ_TRUE, src_addr, src_addr_len, tdata); @@ -1976,7 +1976,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, /* * First send response to this request */ - status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata); + status = pj_stun_session_create_res(sess, msg, 0, NULL, &tdata); if (status != PJ_SUCCESS) { pj_mutex_unlock(ice->mutex); return status; diff --git a/pjnath/src/pjnath/stun_auth.c b/pjnath/src/pjnath/stun_auth.c index d49b4fa2..c670bfcd 100644 --- a/pjnath/src/pjnath/stun_auth.c +++ b/pjnath/src/pjnath/stun_auth.c @@ -104,13 +104,13 @@ static pj_status_t create_challenge(pj_pool_t *pool, } -/* Verify credential */ -PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, - unsigned pkt_len, - const pj_stun_msg *msg, - pj_stun_auth_cred *cred, - pj_pool_t *pool, - pj_stun_msg **p_response) +/* Verify credential in the request */ +PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + pj_stun_auth_cred *cred, + pj_pool_t *pool, + pj_stun_msg **p_response) { pj_str_t realm, nonce, password; const pj_stun_msgint_attr *amsgi; @@ -121,7 +121,6 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, const pj_stun_realm_attr *anonce; pj_hmac_sha1_context ctx; pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; - pj_uint8_t md5_digest[16]; pj_str_t key; pj_status_t status; @@ -224,7 +223,7 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, /* We want long term, and REALM is present */ /* NONCE must be present. */ - if (anonce == NULL) { + if (anonce == NULL && nonce.slen) { if (p_response) { create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE, NULL, &realm, &nonce, p_response); @@ -319,24 +318,18 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, return PJ_EBUG; } - /* Determine which key to use */ - if (realm.slen) { - pj_stun_calc_md5_key(md5_digest, &realm, &auser->value, &password); - key.ptr = (char*)md5_digest; - key.slen = 16; - } else { - key = password; - } + /* Calculate key */ + pj_stun_create_key(pool, &key, &realm, &auser->value, &password); /* Now calculate HMAC of the message, adding zero padding if necessary * to make the input 64 bytes aligned. */ pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen); pj_hmac_sha1_update(&ctx, pkt, amsgi_pos); - if (amsgi_pos & 0x3F) { + if (amsgi_pos & 63) { pj_uint8_t zeroes[64]; pj_bzero(zeroes, sizeof(zeroes)); - pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 0x3F)); + pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 63)); } pj_hmac_sha1_final(&ctx, digest); @@ -355,3 +348,77 @@ PJ_DEF(pj_status_t) pj_stun_verify_credential( const pj_uint8_t *pkt, } +/* Authenticate MESSAGE-INTEGRITY in the response */ +PJ_DEF(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_msg *msg, + const pj_str_t *key) +{ + const pj_stun_msgint_attr *amsgi; + unsigned amsgi_pos; + pj_hmac_sha1_context ctx; + pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; + + PJ_ASSERT_RETURN(pkt && pkt_len && msg && key, PJ_EINVAL); + + /* First check that MESSAGE-INTEGRITY is present */ + amsgi = (const pj_stun_msgint_attr*) + pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0); + if (amsgi == NULL) { + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE); + } + + + /* Check that message length is valid */ + if (msg->hdr.length < 24) { + return PJNATH_EINSTUNMSGLEN; + } + + /* Get the position of MESSAGE-INTEGRITY in the packet */ + amsgi_pos = 20+msg->hdr.length-24; + if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { + /* Found MESSAGE-INTEGRITY as the last attribute */ + } else { + amsgi_pos = 0; + } + + if (amsgi_pos==0) { + /* Check that message length is valid */ + if (msg->hdr.length < 32) { + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE); + } + + amsgi_pos = 20+msg->hdr.length-8-24; + if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) { + /* Found MESSAGE-INTEGRITY before FINGERPRINT */ + } else { + amsgi_pos = 0; + } + } + + if (amsgi_pos==0) { + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE); + } + + /* Now calculate HMAC of the message, adding zero padding if necessary + * to make the input 64 bytes aligned. + */ + pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, key->slen); + pj_hmac_sha1_update(&ctx, pkt, amsgi_pos); + if (amsgi_pos & 0x3F) { + pj_uint8_t zeroes[64]; + pj_bzero(zeroes, sizeof(zeroes)); + pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 0x3F)); + } + pj_hmac_sha1_final(&ctx, digest); + + /* Compare HMACs */ + if (pj_memcmp(amsgi->hmac, digest, 20)) { + /* HMAC value mismatch */ + return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE); + } + + /* Everything looks okay! */ + return PJ_SUCCESS; +} + diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c index 0c17bc5b..74dbcfa9 100644 --- a/pjnath/src/pjnath/stun_msg.c +++ b/pjnath/src/pjnath/stun_msg.c @@ -17,7 +17,6 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include -#include #include #include #include @@ -1948,10 +1947,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, /* Calculate HMAC-SHA1 key for long term credential, by getting * MD5 digest of username, realm, and password. */ -void pj_stun_calc_md5_key(pj_uint8_t digest[16], - const pj_str_t *realm, - const pj_str_t *username, - const pj_str_t *passwd) +static void calc_md5_key(pj_uint8_t digest[16], + const pj_str_t *realm, + const pj_str_t *username, + const pj_str_t *passwd) { /* The 16-byte key for MESSAGE-INTEGRITY HMAC is formed by taking * the MD5 hash of the result of concatenating the following five @@ -1966,9 +1965,9 @@ void pj_stun_calc_md5_key(pj_uint8_t digest[16], pj_md5_init(&ctx); #define REMOVE_QUOTE(s) if (s.slen && *s.ptr=='"') \ - s.ptr++, s.slen--; \ - if (s.slen && s.ptr[s.slen-1]=='"') \ - s.slen--; + s.ptr++, s.slen--; \ + if (s.slen && s.ptr[s.slen-1]=='"') \ + s.slen--; /* Add username */ s = *username; @@ -1996,6 +1995,28 @@ void pj_stun_calc_md5_key(pj_uint8_t digest[16], } +/* + * Create authentication key to be used for encoding the message with + * MESSAGE-INTEGRITY. + */ +PJ_DEF(void) pj_stun_create_key(pj_pool_t *pool, + pj_str_t *key, + const pj_str_t *realm, + const pj_str_t *username, + const pj_str_t *passwd) +{ + PJ_ASSERT_ON_FAIL(pool && key && username && passwd, return); + + if (realm && realm->slen) { + key->ptr = (char*) pj_pool_alloc(pool, 16); + calc_md5_key((pj_uint8_t*)key->ptr, realm, username, passwd); + key->slen = 16; + } else { + pj_strdup(pool, key, passwd); + } +} + + /* static char *print_binary(const pj_uint8_t *data, unsigned data_len) { @@ -2028,16 +2049,13 @@ static char *print_binary(const pj_uint8_t *data, unsigned data_len) PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, pj_uint8_t *buf, unsigned buf_size, unsigned options, - const pj_str_t *password, + const pj_str_t *key, unsigned *p_msg_len) { - pj_stun_msg_hdr *hdr; pj_uint8_t *start = buf; - pj_stun_realm_attr *arealm = NULL; - pj_stun_username_attr *auname = NULL; pj_stun_msgint_attr *amsgint = NULL; pj_stun_fingerprint_attr *afingerprint = NULL; - unsigned printed = 0; + unsigned printed = 0, body_len; pj_status_t status; unsigned i; @@ -2052,16 +2070,16 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, */ if (buf_size < sizeof(pj_stun_msg_hdr)) return PJ_ETOOSMALL; - pj_memcpy(buf, &msg->hdr, sizeof(pj_stun_msg_hdr)); - hdr = (pj_stun_msg_hdr*) buf; - hdr->magic = pj_htonl(hdr->magic); - hdr->type = pj_htons(hdr->type); - /* We'll fill in the length later */ + + PUTVAL16H(buf, 0, msg->hdr.type); + PUTVAL16H(buf, 2, 0); /* length will be calculated later */ + PUTVAL32H(buf, 4, msg->hdr.magic); + pj_memcpy(buf+8, msg->hdr.tsx_id, sizeof(msg->hdr.tsx_id)); buf += sizeof(pj_stun_msg_hdr); buf_size -= sizeof(pj_stun_msg_hdr); - /* Print each attribute */ + /* Encode each attribute to the message */ for (i=0; iattr_count; ++i) { const struct attr_desc *adesc; const pj_stun_attr_hdr *attr_hdr = msg->attr[i]; @@ -2073,14 +2091,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, /* Stop when encountering MESSAGE-INTEGRITY */ break; - } else if (attr_hdr->type == PJ_STUN_ATTR_USERNAME) { - pj_assert(auname == NULL); - auname = (pj_stun_username_attr*) attr_hdr; - - } else if (attr_hdr->type == PJ_STUN_ATTR_REALM) { - pj_assert(arealm == NULL); - arealm = (pj_stun_realm_attr*) attr_hdr; - } else if (attr_hdr->type == PJ_STUN_ATTR_FINGERPRINT) { afingerprint = (pj_stun_fingerprint_attr*) attr_hdr; break; @@ -2123,25 +2133,24 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, * Note that length is not including the 20 bytes header. */ if (amsgint && afingerprint) { - msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24 + 8); + body_len = (pj_uint16_t)((buf - start) - 20 + 24 + 8); } else if (amsgint) { - msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 24); + body_len = (pj_uint16_t)((buf - start) - 20 + 24); } else if (afingerprint) { - msg->hdr.length = (pj_uint16_t)((buf - start) - 20 + 8); + body_len = (pj_uint16_t)((buf - start) - 20 + 8); } else { - msg->hdr.length = (pj_uint16_t)((buf - start) - 20); + body_len = (pj_uint16_t)((buf - start) - 20); } /* hdr->length = pj_htons(length); */ - start[2] = (pj_uint8_t)((msg->hdr.length >> 8) & 0x00FF); - start[3] = (pj_uint8_t)(msg->hdr.length & 0x00FF); + PUTVAL16H(start, 2, (pj_uint16_t)body_len); /* Calculate message integrity, if present */ if (amsgint != NULL) { - - pj_uint8_t md5_key_buf[16]; pj_hmac_sha1_context ctx; - pj_str_t key; + + /* Key MUST be specified */ + PJ_ASSERT_RETURN(key, PJ_EINVALIDOP); /* MESSAGE-INTEGRITY must be the last attribute in the message, or * the last attribute before FINGERPRINT. @@ -2161,32 +2170,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, } } - /* Must have USERNAME attribute */ - if (auname == NULL) { - /* Should not happen for message generated by us */ - pj_assert(PJ_FALSE); - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_USERNAME); - } - - /* Password must be specified */ - PJ_ASSERT_RETURN(password, PJ_EINVAL); - - /* Get the key to sign the message */ - if (arealm == NULL ) { - /* For short term credential, the key is the password */ - key = *password; - - } else { - pj_stun_calc_md5_key(md5_key_buf, &arealm->value, - &auname->value, password); - key.ptr = (char*) md5_key_buf; - key.slen = 16; - } - /* Calculate HMAC-SHA1 digest, add zero padding to input * if necessary to make the input 64 bytes aligned. */ - pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen); + pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, key->slen); pj_hmac_sha1_update(&ctx, (pj_uint8_t*)start, buf-start); if ((buf-start) & 0x3F) { pj_uint8_t zeroes[64]; @@ -2220,7 +2207,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, buf_size -= printed; } - /* Done */ + /* Update message length. */ + msg->hdr.length = (pj_uint16_t) ((buf - start) - 20); + + /* Return the length */ if (p_msg_len) *p_msg_len = (buf - start); diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c index 541288de..7586dfc2 100644 --- a/pjnath/src/pjnath/stun_session.c +++ b/pjnath/src/pjnath/stun_session.c @@ -211,13 +211,18 @@ static void on_cache_timeout(pj_timer_heap_t *timer_heap, pj_stun_msg_destroy_tdata(tdata->sess, tdata); } -static pj_str_t *get_passwd(pj_stun_session *sess, pj_pool_t *pool, - const pj_stun_msg *msg) +static pj_status_t get_key(pj_stun_session *sess, pj_pool_t *pool, + const pj_stun_msg *msg, pj_str_t *auth_key) { if (sess->cred == NULL) { - return NULL; + auth_key->slen = 0; + return PJ_SUCCESS; } else if (sess->cred->type == PJ_STUN_AUTH_CRED_STATIC) { - return &sess->cred->data.static_cred.data; + pj_stun_create_key(pool, auth_key, + &sess->cred->data.static_cred.realm, + &sess->cred->data.static_cred.username, + &sess->cred->data.static_cred.data); + return PJ_SUCCESS; } else if (sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { pj_str_t realm, username, nonce; pj_str_t *password; @@ -231,10 +236,17 @@ static pj_str_t *get_passwd(pj_stun_session *sess, pj_pool_t *pool, &realm, &username, &nonce, &data_type, password); - return password; + if (status != PJ_SUCCESS) + return status; + + pj_stun_create_key(pool, auth_key, + &realm, &username, password); + + return PJ_SUCCESS; } else { - return NULL; + pj_assert(!"Unknown credential type"); + return PJ_EBUG; } } @@ -243,26 +255,23 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, pj_stun_msg *msg) { pj_status_t status = 0; + pj_bool_t need_auth; pj_str_t realm, username, nonce, password; int data_type = 0; realm.slen = username.slen = nonce.slen = password.slen = 0; /* The server SHOULD include a SERVER attribute in all responses */ - if (sess->srv_name.slen && (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || - PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) - { + if (sess->srv_name.slen && PJ_STUN_IS_RESPONSE(msg->hdr.type)) { pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SERVER, &sess->srv_name); } - /* From draft-ietf-behave-rfc3489bis-05.txt - * Section 8.3.1. Formulating the Request Message - * - * Note: only put MESSAGE-INTEGRITY in non error response. - */ + need_auth = PJ_STUN_IS_REQUEST(msg->hdr.type) || + PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type); + if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_STATIC && - !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + need_auth) { realm = sess->cred->data.static_cred.realm; username = sess->cred->data.static_cred.username; @@ -271,7 +280,7 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, nonce = sess->cred->data.static_cred.nonce; } else if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC && - !PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) + need_auth) { void *user_data = sess->cred->data.dyn_cred.user_data; @@ -284,8 +293,8 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, } - /* Create and add USERNAME attribute */ - if (username.slen) { + /* Create and add USERNAME attribute for */ + if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, &username); @@ -293,7 +302,7 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, } /* Add REALM only when long term credential is used */ - if (realm.slen) { + if (realm.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_REALM, &realm); @@ -301,14 +310,17 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, } /* Add NONCE when desired */ - if (nonce.slen) { + if (nonce.slen && + (PJ_STUN_IS_REQUEST(msg->hdr.type) || + PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type))) + { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_NONCE, &nonce); } /* Add MESSAGE-INTEGRITY attribute */ - if (username.slen) { + if (username.slen && need_auth) { status = pj_stun_msg_add_msgint_attr(pool, msg); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } @@ -508,11 +520,11 @@ PJ_DEF(pj_status_t) pj_stun_session_create_ind(pj_stun_session *sess, /* * Create a STUN response message. */ -PJ_DEF(pj_status_t) pj_stun_session_create_response( pj_stun_session *sess, - const pj_stun_msg *req, - unsigned err_code, - const pj_str_t *err_msg, - pj_stun_tx_data **p_tdata) +PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, + const pj_stun_msg *req, + unsigned err_code, + const pj_str_t *err_msg, + pj_stun_tx_data **p_tdata) { pj_status_t status; pj_stun_tx_data *tdata = NULL; @@ -597,10 +609,18 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, return status; } + status = get_key(sess, tdata->pool, tdata->msg, &tdata->auth_key); + if (status != PJ_SUCCESS) { + pj_stun_msg_destroy_tdata(sess, tdata); + pj_mutex_unlock(sess->mutex); + LOG_ERR_(sess, "Error getting creadential's key", status); + return status; + } + /* Encode message */ status = pj_stun_msg_encode(tdata->msg, (pj_uint8_t*)tdata->pkt, tdata->max_len, 0, - get_passwd(sess, tdata->pool, tdata->msg), + &tdata->auth_key, &tdata->pkt_size); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); @@ -737,6 +757,7 @@ PJ_DEF(pj_status_t) pj_stun_session_retransmit_req(pj_stun_session *sess, /* Send response */ static pj_status_t send_response(pj_stun_session *sess, pj_pool_t *pool, pj_stun_msg *response, + const pj_str_t *auth_key, pj_bool_t retransmission, const pj_sockaddr_t *addr, unsigned addr_len) { @@ -757,8 +778,7 @@ static pj_status_t send_response(pj_stun_session *sess, /* Encode */ status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, - get_passwd(sess, pool, response), - &out_len); + auth_key, &out_len); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "Error encoding message", status); return status; @@ -774,7 +794,7 @@ static pj_status_t send_response(pj_stun_session *sess, } /* Authenticate incoming message */ -static pj_status_t authenticate_msg(pj_stun_session *sess, +static pj_status_t authenticate_req(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, const pj_stun_msg *msg, @@ -788,11 +808,11 @@ static pj_status_t authenticate_msg(pj_stun_session *sess, if (PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type) || sess->cred == NULL) return PJ_SUCCESS; - status = pj_stun_verify_credential(pkt, pkt_len, msg, sess->cred, - tmp_pool, &response); + status = pj_stun_authenticate_request(pkt, pkt_len, msg, sess->cred, + tmp_pool, &response); if (status != PJ_SUCCESS && response != NULL) { PJ_LOG(5,(SNAME(sess), "Message authentication failed")); - send_response(sess, tmp_pool, response, PJ_FALSE, + send_response(sess, tmp_pool, response, NULL, PJ_FALSE, src_addr, src_addr_len); } @@ -802,6 +822,9 @@ static pj_status_t authenticate_msg(pj_stun_session *sess, /* Handle incoming response */ static pj_status_t on_incoming_response(pj_stun_session *sess, + unsigned options, + const pj_uint8_t *pkt, + unsigned pkt_len, pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) @@ -817,6 +840,18 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, return PJ_SUCCESS; } + /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE + * is specified in the option. + */ + if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) { + status = pj_stun_authenticate_response(pkt, pkt_len, msg, &tdata->auth_key); + if (status != PJ_SUCCESS) { + PJ_LOG(5,(SNAME(sess), + "Response authentication failed")); + return status; + } + } + /* Pass the response to the transaction. * If the message is accepted, transaction callback will be called, * and this will call the session callback too. @@ -866,7 +901,7 @@ static pj_status_t check_cached_response(pj_stun_session *sess, PJ_LOG(5,(SNAME(sess), "Request retransmission, sending cached response")); - send_response(sess, tmp_pool, t->msg, PJ_TRUE, + send_response(sess, tmp_pool, t->msg, &t->auth_key, PJ_TRUE, src_addr, src_addr_len); return PJ_SUCCESS; } @@ -876,6 +911,7 @@ static pj_status_t check_cached_response(pj_stun_session *sess, /* Handle incoming request */ static pj_status_t on_incoming_request(pj_stun_session *sess, + unsigned options, pj_pool_t *tmp_pool, const pj_uint8_t *in_pkt, unsigned in_pkt_len, @@ -885,6 +921,17 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, { pj_status_t status; + /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE + * is specified in the option. + */ + if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) { + status = authenticate_req(sess, (const pj_uint8_t*) in_pkt, in_pkt_len, + msg, tmp_pool, src_addr, src_addr_len); + if (status != PJ_SUCCESS) { + return status; + } + } + /* Distribute to handler, or respond with Bad Request */ if (sess->cb.on_rx_request) { status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, msg, @@ -897,7 +944,7 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, &response); if (status == PJ_SUCCESS && response) { status = send_response(sess, tmp_pool, response, - PJ_FALSE, src_addr, src_addr_len); + NULL, PJ_FALSE, src_addr, src_addr_len); } } @@ -952,7 +999,7 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, if (status != PJ_SUCCESS) { LOG_ERR_(sess, "STUN msg_decode() error", status); if (response) { - send_response(sess, tmp_pool, response, + send_response(sess, tmp_pool, response, NULL, PJ_FALSE, src_addr, src_addr_len); } pj_pool_release(tmp_pool); @@ -977,26 +1024,17 @@ PJ_DEF(pj_status_t) pj_stun_session_on_rx_pkt(pj_stun_session *sess, goto on_return; } - /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE - * is specified in the option. - */ - if ((options & PJ_STUN_NO_AUTHENTICATE) == 0) { - status = authenticate_msg(sess, (const pj_uint8_t*) packet, pkt_size, - msg, tmp_pool, src_addr, src_addr_len); - if (status != PJ_SUCCESS) { - goto on_return; - } - } - /* Handle message */ if (PJ_STUN_IS_SUCCESS_RESPONSE(msg->hdr.type) || PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) { - status = on_incoming_response(sess, msg, src_addr, src_addr_len); + status = on_incoming_response(sess, options, + (const pj_uint8_t*) packet, pkt_size, + msg, src_addr, src_addr_len); } else if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { - status = on_incoming_request(sess, tmp_pool, + status = on_incoming_request(sess, options, tmp_pool, (const pj_uint8_t*) packet, pkt_size, msg, src_addr, src_addr_len); diff --git a/pjnath/src/pjnath/stun_transaction.c b/pjnath/src/pjnath/stun_transaction.c index 30330718..88b19c95 100644 --- a/pjnath/src/pjnath/stun_transaction.c +++ b/pjnath/src/pjnath/stun_transaction.c @@ -188,11 +188,11 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) tsx->retransmit_time.sec = 0; tsx->retransmit_time.msec = tsx->cfg->rto_msec; - } else if (tsx->transmit_count < PJ_STUN_MAX_RETRANSMIT_COUNT-1) { + } else if (tsx->transmit_count < PJ_STUN_MAX_TRANSMIT_COUNT-1) { unsigned msec; msec = PJ_TIME_VAL_MSEC(tsx->retransmit_time); - msec = (msec << 1) + 100; + msec <<= 1; tsx->retransmit_time.sec = msec / 1000; tsx->retransmit_time.msec = msec % 1000; @@ -216,6 +216,11 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) } + tsx->transmit_count++; + + PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)", + tsx->transmit_count)); + /* Send message */ status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size); if (status != PJ_SUCCESS) { @@ -228,10 +233,6 @@ static pj_status_t tsx_transmit_msg(pj_stun_client_tsx *tsx) return status; } - tsx->transmit_count++; - - PJ_LOG(5,(tsx->obj_name, "STUN sending message (transmit count=%d)", - tsx->transmit_count)); return status; } @@ -268,7 +269,7 @@ static void retransmit_timer_callback(pj_timer_heap_t *timer_heap, PJ_UNUSED_ARG(timer_heap); - if (tsx->transmit_count >= PJ_STUN_MAX_RETRANSMIT_COUNT) { + if (tsx->transmit_count >= PJ_STUN_MAX_TRANSMIT_COUNT) { /* Retransmission count exceeded. Transaction has failed */ tsx->retransmit_timer.id = 0; PJ_LOG(4,(tsx->obj_name, "STUN timeout waiting for response")); diff --git a/pjnath/src/pjstun-client/client_main.c b/pjnath/src/pjstun-client/client_main.c index 10834105..567a6b75 100644 --- a/pjnath/src/pjstun-client/client_main.c +++ b/pjnath/src/pjstun-client/client_main.c @@ -107,6 +107,15 @@ static void on_request_complete(pj_stun_session *sess, case PJ_STUN_ALLOCATE_RESPONSE: { pj_stun_relay_addr_attr *ar; + pj_stun_lifetime_attr *al; + + al = (pj_stun_lifetime_attr*) + pj_stun_msg_find_attr(response, + PJ_STUN_ATTR_LIFETIME, 0); + if (!al) { + PJ_LOG(1,(THIS_FILE, "Error: LIFETIME attribute not present")); + return; + } ar = (pj_stun_relay_addr_attr*) pj_stun_msg_find_attr(response, @@ -120,6 +129,10 @@ static void on_request_complete(pj_stun_session *sess, } else { pj_memset(&g.relay_addr, 0, sizeof(g.relay_addr)); } + + if (al->value == 0) { + PJ_LOG(3,(THIS_FILE, "Relay deallocated")); + } } break; } @@ -632,7 +645,7 @@ int main(int argc, char *argv[]) g.data = g.data_buf; - while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) { + while((c=pj_getopt_long(argc,argv, "r:u:p:N:hF", long_options, &opt_id))!=-1) { switch (c) { case 'r': o.realm = pj_optarg; diff --git a/pjnath/src/pjstun-srv-test/bind_usage.c b/pjnath/src/pjstun-srv-test/bind_usage.c index 680dd5c0..09112bdd 100644 --- a/pjnath/src/pjstun-srv-test/bind_usage.c +++ b/pjnath/src/pjstun-srv-test/bind_usage.c @@ -157,7 +157,7 @@ static pj_status_t sess_on_rx_request(pj_stun_session *sess, PJ_UNUSED_ARG(pkt_len); /* Create response */ - status = pj_stun_session_create_response(sess, msg, 0, NULL, &tdata); + status = pj_stun_session_create_res(sess, msg, 0, NULL, &tdata); if (status != PJ_SUCCESS) return status; diff --git a/pjnath/src/pjstun-srv-test/main.c b/pjnath/src/pjstun-srv-test/main.c index c462d47e..aad1c0f2 100644 --- a/pjnath/src/pjstun-srv-test/main.c +++ b/pjnath/src/pjstun-srv-test/main.c @@ -78,9 +78,10 @@ int main(int argc, char *argv[]) int c, opt_id; pj_caching_pool cp; pj_stun_server *srv; + pj_stun_usage *turn; pj_status_t status; - while((c=pj_getopt_long(argc,argv, "r:u:p:hF", long_options, &opt_id))!=-1) { + while((c=pj_getopt_long(argc,argv, "r:u:p:N:hF", long_options, &opt_id))!=-1) { switch (c) { case 'r': o.realm = pj_optarg; @@ -131,12 +132,24 @@ int main(int argc, char *argv[]) */ status = pj_stun_turn_usage_create(srv, PJ_SOCK_DGRAM, NULL, - 3478, NULL); + 3478, &turn); if (status != PJ_SUCCESS) { pj_stun_perror(THIS_FILE, "Unable to create bind usage", status); return 1; } + if (o.user_name && o.password) { + pj_stun_auth_cred cred; + pj_bzero(&cred, sizeof(cred)); + cred.type = PJ_STUN_AUTH_CRED_STATIC; + cred.data.static_cred.realm = pj_str(o.realm); + cred.data.static_cred.username = pj_str(o.user_name); + cred.data.static_cred.data_type = 0; + cred.data.static_cred.data = pj_str(o.password); + cred.data.static_cred.nonce = pj_str(o.nonce); + pj_stun_turn_usage_set_credential(turn, &cred); + } + server_main(srv); pj_stun_server_destroy(srv); diff --git a/pjnath/src/pjstun-srv-test/server.h b/pjnath/src/pjstun-srv-test/server.h index 633131c6..f06d7f85 100644 --- a/pjnath/src/pjstun-srv-test/server.h +++ b/pjnath/src/pjstun-srv-test/server.h @@ -124,6 +124,8 @@ PJ_DECL(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv, unsigned port, pj_stun_usage **p_bu); +PJ_DECL(pj_status_t) pj_stun_turn_usage_set_credential(pj_stun_usage *turn, + const pj_stun_auth_cred *cred); pj_status_t pj_stun_server_register_usage(pj_stun_server *srv, pj_stun_usage *usage); diff --git a/pjnath/src/pjstun-srv-test/turn_usage.c b/pjnath/src/pjstun-srv-test/turn_usage.c index 145d8020..c3e53577 100644 --- a/pjnath/src/pjstun-srv-test/turn_usage.c +++ b/pjnath/src/pjstun-srv-test/turn_usage.c @@ -78,6 +78,7 @@ struct turn_usage int type; pj_stun_session *default_session; pj_hash_table_t *client_htable; + pj_stun_auth_cred *cred; unsigned max_bw_kbps; unsigned max_lifetime; @@ -225,6 +226,19 @@ PJ_DEF(pj_status_t) pj_stun_turn_usage_create(pj_stun_server *srv, } +PJ_DEF(pj_status_t) pj_stun_turn_usage_set_credential(pj_stun_usage *turn, + const pj_stun_auth_cred *c) +{ + struct turn_usage *tu; + tu = (struct turn_usage*) pj_stun_usage_get_user_data(turn); + + tu->cred = PJ_POOL_ZALLOC_T(tu->pool, pj_stun_auth_cred); + pj_stun_auth_cred_dup(tu->pool, tu->cred, c); + pj_stun_session_set_credential(tu->default_session, tu->cred); + return PJ_SUCCESS; +} + + /* * This is a callback called by usage.c when the particular STUN usage * is to be destroyed. @@ -441,9 +455,9 @@ static pj_status_t tu_sess_on_rx_request(pj_stun_session *sess, } else if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { if (PJ_STUN_IS_REQUEST(msg->hdr.type)) { - status = pj_stun_session_create_response(sess, msg, - PJ_STUN_SC_NO_BINDING, - NULL, &tdata); + status = pj_stun_session_create_res(sess, msg, + PJ_STUN_SC_NO_BINDING, + NULL, &tdata); if (status==PJ_SUCCESS) { status = pj_stun_session_send_msg(sess, PJ_FALSE, src_addr, src_addr_len, @@ -630,6 +644,9 @@ static pj_status_t client_create(struct turn_usage *tu, return status; } + if (tu->cred) + pj_stun_session_set_credential(client->session, tu->cred); + sd = PJ_POOL_ZALLOC_T(pool, struct session_data); sd->tu = tu; sd->client = client; @@ -863,9 +880,9 @@ static pj_status_t client_respond(struct turn_client *client, if (custom_msg) pj_cstr(&err_msg, custom_msg), p_err_msg = &err_msg; - status = pj_stun_session_create_response(client->session, msg, - err_code, p_err_msg, - &response); + status = pj_stun_session_create_res(client->session, msg, + err_code, p_err_msg, + &response); if (status == PJ_SUCCESS) status = pj_stun_session_send_msg(client->session, PJ_TRUE, dst_addr, dst_addr_len, response); @@ -1014,8 +1031,8 @@ static pj_status_t client_handle_allocate_req(struct turn_client *client, client->expiry_timer.id = PJ_TRUE; /* Done successfully, create and send success response */ - status = pj_stun_session_create_response(client->session, msg, - 0, NULL, &response); + status = pj_stun_session_create_res(client->session, msg, + 0, NULL, &response); if (status != PJ_SUCCESS) { return status; } @@ -1062,8 +1079,8 @@ static pj_status_t handle_binding_req(pj_stun_session *session, pj_status_t status; /* Create response */ - status = pj_stun_session_create_response(session, msg, 0, NULL, - &tdata); + status = pj_stun_session_create_res(session, msg, 0, NULL, + &tdata); if (status != PJ_SUCCESS) return status; -- cgit v1.2.3