diff options
Diffstat (limited to 'pjnath/src')
-rw-r--r-- | pjnath/src/pjnath-test/sess_auth.c | 1125 | ||||
-rw-r--r-- | pjnath/src/pjnath-test/stun.c | 57 | ||||
-rw-r--r-- | pjnath/src/pjnath-test/test.c | 1 | ||||
-rw-r--r-- | pjnath/src/pjnath-test/test.h | 7 | ||||
-rw-r--r-- | pjnath/src/pjnath/ice_session.c | 32 | ||||
-rw-r--r-- | pjnath/src/pjnath/stun_auth.c | 291 | ||||
-rw-r--r-- | pjnath/src/pjnath/stun_msg.c | 344 | ||||
-rw-r--r-- | pjnath/src/pjnath/stun_session.c | 453 | ||||
-rw-r--r-- | pjnath/src/pjnath/turn_session.c | 2 | ||||
-rw-r--r-- | pjnath/src/pjturn-srv/allocation.c | 65 | ||||
-rw-r--r-- | pjnath/src/pjturn-srv/auth.c | 23 | ||||
-rw-r--r-- | pjnath/src/pjturn-srv/auth.h | 12 | ||||
-rw-r--r-- | pjnath/src/pjturn-srv/server.c | 117 | ||||
-rw-r--r-- | pjnath/src/pjturn-srv/turn.h | 23 |
14 files changed, 1910 insertions, 642 deletions
diff --git a/pjnath/src/pjnath-test/sess_auth.c b/pjnath/src/pjnath-test/sess_auth.c new file mode 100644 index 00000000..86ac5d8a --- /dev/null +++ b/pjnath/src/pjnath-test/sess_auth.c @@ -0,0 +1,1125 @@ +/* $Id$ */ +/* + * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ +#include "test.h" + +#define THIS_FILE "sess_auth.c" + +#define REALM "STUN session test" +#define USERNAME "theusername" +#define PASSWORD "thepassword" +#define NONCE "thenonce" + + +/* STUN config */ +static pj_stun_config stun_cfg; + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// SERVER PART +// + + +/* Server instance */ +static struct server +{ + pj_pool_t *pool; + pj_sockaddr addr; + pj_stun_session *sess; + + pj_bool_t responding; + unsigned recv_count; + pj_stun_auth_type auth_type; + + pj_sock_t sock; + + pj_bool_t quit; + pj_thread_t *thread; +} *server; + + +static pj_status_t server_send_msg(pj_stun_session *sess, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len) +{ + pj_ssize_t len = pkt_size; + + PJ_UNUSED_ARG(sess); + + return pj_sock_sendto(server->sock, pkt, &len, 0, dst_addr, addr_len); +} + +static pj_status_t server_on_rx_request(pj_stun_session *sess, + const pj_uint8_t *pkt, + unsigned pkt_len, + const pj_stun_rx_data *rdata, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + PJ_UNUSED_ARG(pkt); + PJ_UNUSED_ARG(pkt_len); + + return pj_stun_session_respond(sess, rdata, 0, NULL, PJ_TRUE, + src_addr, src_addr_len); +} + + +static pj_status_t server_get_auth(void *user_data, + pj_pool_t *pool, + pj_str_t *realm, + pj_str_t *nonce) +{ + PJ_UNUSED_ARG(user_data); + PJ_UNUSED_ARG(pool); + + if (server->auth_type == PJ_STUN_AUTH_SHORT_TERM) { + realm->slen = nonce->slen = 0; + } else { + *realm = pj_str(REALM); + *nonce = pj_str(NONCE); + } + + return PJ_SUCCESS; +} + + +static pj_status_t server_get_password( const pj_stun_msg *msg, + void *user_data, + const pj_str_t *realm, + const pj_str_t *username, + pj_pool_t *pool, + pj_stun_passwd_type *data_type, + pj_str_t *data) +{ + PJ_UNUSED_ARG(msg); + PJ_UNUSED_ARG(user_data); + PJ_UNUSED_ARG(pool); + + if (server->auth_type == PJ_STUN_AUTH_SHORT_TERM) { + if (realm && realm->slen) { + PJ_LOG(4,(THIS_FILE, " server expecting short term")); + return -1; + } + } else { + if (realm==NULL || realm->slen==0) { + PJ_LOG(4,(THIS_FILE, " realm not present")); + return -1; + } + } + + if (pj_strcmp2(username, USERNAME) != 0) { + PJ_LOG(4,(THIS_FILE, " wrong username")); + return -1; + } + + *data_type = PJ_STUN_PASSWD_PLAIN; + *data = pj_str(PASSWORD); + + return PJ_SUCCESS; +} + + +static pj_bool_t server_verify_nonce(const pj_stun_msg *msg, + void *user_data, + const pj_str_t *realm, + const pj_str_t *username, + const pj_str_t *nonce) +{ + PJ_UNUSED_ARG(msg); + PJ_UNUSED_ARG(user_data); + PJ_UNUSED_ARG(realm); + PJ_UNUSED_ARG(username); + + if (pj_strcmp2(nonce, NONCE) != 0) + return PJ_FALSE; + + return PJ_TRUE; +} + + +static int server_thread(void *unused) +{ + PJ_UNUSED_ARG(unused); + + while (!server->quit) { + pj_fd_set_t readset; + pj_time_val delay = {0, 10}; + + PJ_FD_ZERO(&readset); + PJ_FD_SET(server->sock, &readset); + + if (pj_sock_select(server->sock, &readset, NULL, NULL, &delay)==1 && + PJ_FD_ISSET(server->sock, &readset)) + { + char pkt[1000]; + pj_ssize_t len; + pj_status_t status; + pj_sockaddr src_addr; + int src_addr_len; + + len = sizeof(pkt); + src_addr_len = sizeof(src_addr); + + status = pj_sock_recvfrom(server->sock, pkt, &len, 0, &src_addr, &src_addr_len); + if (status != PJ_SUCCESS) + continue; + + /* Increment server's receive count */ + server->recv_count++; + + /* Only pass to server if we allow to respond */ + if (!server->responding) + continue; + + pj_stun_session_on_rx_pkt(server->sess, pkt, len, + PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM, + NULL, &src_addr, src_addr_len); + } + } + + return 0; +} + + +/* Destroy server */ +static void destroy_server(void) +{ + if (server->thread) { + server->quit = PJ_TRUE; + pj_thread_join(server->thread); + pj_thread_destroy(server->thread); + } + + if (server->sock) { + pj_sock_close(server->sock); + } + + if (server->sess) { + pj_stun_session_destroy(server->sess); + } + + pj_pool_release(server->pool); + server = NULL; +} + +/* Instantiate standard server */ +static int create_std_server(pj_stun_auth_type auth_type, + pj_bool_t responding) +{ + pj_pool_t *pool; + pj_stun_session_cb sess_cb; + pj_stun_auth_cred cred; + pj_status_t status; + + /* Create server */ + pool = pj_pool_create(mem, "server", 1000, 1000, NULL); + server = PJ_POOL_ZALLOC_T(pool, struct server); + server->pool = pool; + server->auth_type = auth_type; + server->responding = responding; + + /* Create STUN session */ + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_rx_request = &server_on_rx_request; + sess_cb.on_send_msg = &server_send_msg; + status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, &server->sess); + if (status != PJ_SUCCESS) { + destroy_server(); + return -10; + } + + /* Configure credential */ + pj_bzero(&cred, sizeof(cred)); + cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; + cred.data.dyn_cred.get_auth = &server_get_auth; + cred.data.dyn_cred.get_password = &server_get_password; + cred.data.dyn_cred.verify_nonce = &server_verify_nonce; + status = pj_stun_session_set_credential(server->sess, auth_type, &cred); + if (status != PJ_SUCCESS) { + destroy_server(); + return -20; + } + + /* Create socket */ + status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &server->sock); + if (status != PJ_SUCCESS) { + destroy_server(); + return -30; + } + + /* Bind */ + pj_sockaddr_in_init(&server->addr.ipv4, NULL, 0); + status = pj_sock_bind(server->sock, &server->addr, pj_sockaddr_get_len(&server->addr)); + if (status != PJ_SUCCESS) { + destroy_server(); + return -40; + } else { + /* Get the bound IP address */ + int namelen = sizeof(server->addr); + pj_sockaddr addr; + + status = pj_sock_getsockname(server->sock, &server->addr, &namelen); + if (status != PJ_SUCCESS) { + destroy_server(); + return -43; + } + + status = pj_gethostip(pj_AF_INET(), &addr); + if (status != PJ_SUCCESS) { + destroy_server(); + return -45; + } + + pj_sockaddr_copy_addr(&server->addr, &addr); + } + + + /* Create worker thread */ + status = pj_thread_create(pool, "server", &server_thread, 0, 0, 0, &server->thread); + if (status != PJ_SUCCESS) { + destroy_server(); + return -30; + } + + return 0; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// CLIENT PART +// + +static struct client +{ + pj_pool_t *pool; + pj_stun_session *sess; + pj_sem_t *test_complete; + pj_sock_t sock; + + pj_bool_t responding; + unsigned recv_count; + + pj_status_t response_status; + pj_stun_msg *response; + + pj_bool_t quit; + pj_thread_t *thread; +} *client; + + +static pj_status_t client_send_msg(pj_stun_session *sess, + const void *pkt, + pj_size_t pkt_size, + const pj_sockaddr_t *dst_addr, + unsigned addr_len) +{ + pj_ssize_t len = pkt_size; + + PJ_UNUSED_ARG(sess); + + return pj_sock_sendto(client->sock, pkt, &len, 0, dst_addr, addr_len); +} + + +static void client_on_request_complete( pj_stun_session *sess, + pj_status_t status, + pj_stun_tx_data *tdata, + const pj_stun_msg *response, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len) +{ + PJ_UNUSED_ARG(sess); + PJ_UNUSED_ARG(tdata); + PJ_UNUSED_ARG(src_addr); + PJ_UNUSED_ARG(src_addr_len); + + client->response_status = status; + if (response) + client->response = pj_stun_msg_clone(client->pool, response); + + pj_sem_post(client->test_complete); +} + + +static int client_thread(void *unused) +{ + PJ_UNUSED_ARG(unused); + + while (!client->quit) { + pj_fd_set_t readset; + pj_time_val delay = {0, 10}; + + /* Also poll the timer heap */ + pj_timer_heap_poll(stun_cfg.timer_heap, NULL); + + /* Poll client socket */ + PJ_FD_ZERO(&readset); + PJ_FD_SET(client->sock, &readset); + + if (pj_sock_select(client->sock, &readset, NULL, NULL, &delay)==1 && + PJ_FD_ISSET(client->sock, &readset)) + { + char pkt[1000]; + pj_ssize_t len; + pj_status_t status; + pj_sockaddr src_addr; + int src_addr_len; + + len = sizeof(pkt); + src_addr_len = sizeof(src_addr); + + status = pj_sock_recvfrom(client->sock, pkt, &len, 0, &src_addr, &src_addr_len); + if (status != PJ_SUCCESS) + continue; + + /* Increment client's receive count */ + client->recv_count++; + + /* Only pass to client if we allow to respond */ + if (!client->responding) + continue; + + pj_stun_session_on_rx_pkt(client->sess, pkt, len, + PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM, + NULL, &src_addr, src_addr_len); + } + + } + + return 0; +} + + +static void destroy_client_server(void) +{ + if (client->thread) { + client->quit = 1; + pj_thread_join(client->thread); + pj_thread_destroy(client->thread); + } + + if (client->sess) + pj_stun_session_destroy(client->sess); + + if (client->sock) + pj_sock_close(client->sock); + + if (client->test_complete) + pj_sem_destroy(client->test_complete); + + if (server) + destroy_server(); +} + +static int run_client_test(const char *title, + + pj_bool_t server_responding, + pj_stun_auth_type server_auth_type, + + pj_stun_auth_type client_auth_type, + const char *realm, + const char *username, + const char *nonce, + const char *password, + pj_bool_t dummy_mi, + + pj_stun_status expected_error, + pj_stun_status expected_code, + const char *expected_realm, + const char *expected_nonce, + + int (*more_check)(void)) +{ + pj_pool_t *pool; + pj_stun_session_cb sess_cb; + pj_stun_auth_cred cred; + pj_stun_tx_data *tdata; + pj_status_t status; + int rc = 0; + + PJ_LOG(3,(THIS_FILE, " %s test", title)); + + /* Create client */ + pool = pj_pool_create(mem, "client", 1000, 1000, NULL); + client = PJ_POOL_ZALLOC_T(pool, struct client); + client->pool = pool; + client->responding = PJ_TRUE; + + /* Create STUN session */ + pj_bzero(&sess_cb, sizeof(sess_cb)); + sess_cb.on_request_complete = &client_on_request_complete; + sess_cb.on_send_msg = &client_send_msg; + status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, &client->sess); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -200; + } + + /* Create semaphore */ + status = pj_sem_create(pool, "client", 0, 1, &client->test_complete); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -205; + } + + /* Create client socket */ + status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &client->sock); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -210; + } + + /* Bind client socket */ + status = pj_sock_bind_in(client->sock, 0, 0); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -220; + } + + /* Create client thread */ + status = pj_thread_create(pool, "client", &client_thread, NULL, 0, 0, &client->thread); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -230; + } + + /* Initialize credential */ + pj_bzero(&cred, sizeof(cred)); + cred.type = PJ_STUN_AUTH_CRED_STATIC; + if (realm) cred.data.static_cred.realm = pj_str((char*)realm); + if (username) cred.data.static_cred.username = pj_str((char*)username); + if (nonce) cred.data.static_cred.nonce = pj_str((char*)nonce); + if (password) cred.data.static_cred.data = pj_str((char*)password); + cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN; + status = pj_stun_session_set_credential(client->sess, client_auth_type, &cred); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -240; + } + + /* Create the server */ + status = create_std_server(server_auth_type, server_responding); + if (status != 0) { + destroy_client_server(); + return status; + } + + /* Create request */ + status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST, + PJ_STUN_MAGIC, NULL, &tdata); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -250; + } + + /* Add our own attributes if client authentication is set to none */ + if (client_auth_type == PJ_STUN_AUTH_NONE) { + pj_str_t tmp; + if (realm) + pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_REALM, pj_cstr(&tmp, realm)); + if (username) + pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&tmp, username)); + if (nonce) + pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, nonce)); + if (password) { + // ignored + } + if (dummy_mi) { + pj_stun_msgint_attr *mi; + + pj_stun_msgint_attr_create(tdata->pool, &mi); + pj_stun_msg_add_attr(tdata->msg, &mi->hdr); + } + + } + + /* Send the request */ + status = pj_stun_session_send_msg(client->sess, PJ_FALSE, &server->addr, + pj_sockaddr_get_len(&server->addr), tdata); + if (status != PJ_SUCCESS) { + destroy_client_server(); + return -270; + } + + /* Wait until test complete */ + pj_sem_wait(client->test_complete); + + + /* Verify response */ + if (expected_error) { + if (expected_code != client->response_status) { + char e1[PJ_ERR_MSG_SIZE], e2[PJ_ERR_MSG_SIZE]; + + pj_strerror(expected_code, e1, sizeof(e1)); + pj_strerror(client->response_status, e2, sizeof(e2)); + + PJ_LOG(3,(THIS_FILE, " err: expecting %d (%s) but got %d (%s) response", + expected_code, e1, client->response_status, e2)); + rc = -500; + } + + } else { + int res_code = 0; + pj_stun_realm_attr *arealm; + pj_stun_nonce_attr *anonce; + + if (client->response_status != 0) { + PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d", + client->response_status)); + rc = -600; + goto done; + } + + if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) { + pj_stun_errcode_attr *aerr = NULL; + + aerr = (pj_stun_errcode_attr*) + pj_stun_msg_find_attr(client->response, + PJ_STUN_ATTR_ERROR_CODE, 0); + if (aerr == NULL) { + PJ_LOG(3,(THIS_FILE, " err: received error response without ERROR-CODE")); + rc = -610; + goto done; + } + + res_code = aerr->err_code; + } else { + res_code = 0; + } + + /* Check that code matches */ + if (expected_code != res_code) { + PJ_LOG(3,(THIS_FILE, " err: expecting response code %d but got %d", + expected_code, res_code)); + rc = -620; + goto done; + } + + /* Find REALM and NONCE attributes */ + arealm = (pj_stun_realm_attr*) + pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0); + anonce = (pj_stun_nonce_attr*) + pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0); + + if (expected_realm) { + if (arealm == NULL) { + PJ_LOG(3,(THIS_FILE, " err: expecting REALM in esponse")); + rc = -630; + goto done; + } + if (pj_strcmp2(&arealm->value, expected_realm)!=0) { + PJ_LOG(3,(THIS_FILE, " err: REALM mismatch in response")); + rc = -640; + goto done; + } + } else { + if (arealm != NULL) { + PJ_LOG(3,(THIS_FILE, " err: non expecting REALM in response")); + rc = -650; + goto done; + } + } + + if (expected_nonce) { + if (anonce == NULL) { + PJ_LOG(3,(THIS_FILE, " err: expecting NONCE in esponse")); + rc = -660; + goto done; + } + if (pj_strcmp2(&anonce->value, expected_nonce)!=0) { + PJ_LOG(3,(THIS_FILE, " err: NONCE mismatch in response")); + rc = -670; + goto done; + } + } else { + if (anonce != NULL) { + PJ_LOG(3,(THIS_FILE, " err: non expecting NONCE in response")); + rc = -680; + goto done; + } + } + } + + /* Our tests are okay so far. Let caller do some more tests if + * it wants to. + */ + if (rc==0 && more_check) { + rc = (*more_check)(); + } + + +done: + destroy_client_server(); + return rc; +} + + +////////////////////////////////////////////////////////////////////////////////////////// +// +// More verification +// + +/* Retransmission test */ +static int retransmit_check(void) +{ + if (server->recv_count != PJ_STUN_MAX_TRANSMIT_COUNT) + return -700; + if (client->recv_count != 0) + return -710; + + return 0; +} + +static int long_term_check1(void) +{ + /* SHOULD NOT contain USERNAME or MESSAGE-INTEGRITY */ + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0)) + return -800; + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + return -800; + + return 0; +} + +static int long_term_check2(void) +{ + /* response SHOULD NOT include a USERNAME, NONCE, REALM or + * MESSAGE-INTEGRITY attribute. + */ + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0)) + return -900; + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0)) + return -910; + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0)) + return -920; + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0)) + return -930; + + return 0; +} + +static int long_term_check3(void) +{ + /* response SHOULD NOT include a USERNAME, NONCE, and REALM */ + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0)) + return -1000; + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0)) + return -1010; + if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0)) + return -1020; + + return 0; +} + +////////////////////////////////////////////////////////////////////////////////////////// +// +// TEST MAIN +// + + +int sess_auth_test(void) +{ + pj_pool_t *pool; + int rc; + + PJ_LOG(3,(THIS_FILE, " STUN session authentication test")); + + /* Init STUN config */ + pj_stun_config_init(&stun_cfg, mem, 0, NULL, NULL); + + /* Create pool and timer heap */ + pool = pj_pool_create(mem, "authtest", 200, 200, NULL); + if (pj_timer_heap_create(pool, 20, &stun_cfg.timer_heap)) { + pj_pool_release(pool); + return -5; + } + + /* Basic retransmission test */ + rc = run_client_test("Retransmission", // title + PJ_FALSE, // server responding + PJ_STUN_AUTH_NONE, // server auth + PJ_STUN_AUTH_NONE, // client auth + NULL, // realm + NULL, // username + NULL, // nonce + NULL, // password + PJ_FALSE, // dummy MI + PJ_TRUE, // expected error + PJNATH_ESTUNTIMEDOUT,// expected code + NULL, // expected realm + NULL, // expected nonce + &retransmit_check // more check + ); + if (rc != 0) { + goto done; + } + + /* + * Short term credential. + * draft-ietf-behave-rfc3489bis-15#section-10.1.2 + */ + + /* + * If the message does not contain both a MESSAGE-INTEGRITY and a + * USERNAME attribute, If the message is a request, the server MUST + * reject the request with an error response. This response MUST + * use an error code of 400 (Bad Request). + */ + rc = run_client_test("Missing MESSAGE-INTEGRITY (short term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_SHORT_TERM, // server auth + PJ_STUN_AUTH_NONE, // client auth + NULL, // realm + NULL, // username + NULL, // nonce + NULL, // password + PJ_FALSE, // dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(400),// expected code + NULL, // expected realm + NULL, // expected nonce + NULL // more check + ); + if (rc != 0) { + goto done; + } + + /* If the USERNAME does not contain a username value currently valid + * within the server: If the message is a request, the server MUST + * reject the request with an error response. This response MUST use + * an error code of 401 (Unauthorized). + */ + rc = run_client_test("USERNAME mismatch (short term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_SHORT_TERM, // server auth + PJ_STUN_AUTH_SHORT_TERM, // client auth + NULL, // realm + "anotheruser", // username + NULL, // nonce + "anotherpass", // password + PJ_FALSE, // dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(401),// expected code + NULL, // expected realm + NULL, // expected nonce + NULL // more check + ); + if (rc != 0) { + goto done; + } + + /* Using the password associated with the username, compute the value + * for the message-integrity as described in Section 15.4. If the + * resulting value does not match the contents of the MESSAGE- + * INTEGRITY attribute: + * + * - If the message is a request, the server MUST reject the request + * with an error response. This response MUST use an error code + * of 401 (Unauthorized). + */ + rc = run_client_test("MESSAGE-INTEGRITY mismatch (short term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_SHORT_TERM, // server auth + PJ_STUN_AUTH_SHORT_TERM, // client auth + NULL, // realm + USERNAME, // username + NULL, // nonce + "anotherpass", // password + PJ_FALSE, // dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(401),// expected code + NULL, // expected realm + NULL, // expected nonce + NULL // more check + ); + if (rc != 0) { + goto done; + } + + /* USERNAME is not present, server must respond with 400 (Bad + * Request). + */ + rc = run_client_test("Missing USERNAME (short term)",// title + PJ_TRUE, // server responding + PJ_STUN_AUTH_SHORT_TERM, // server auth + PJ_STUN_AUTH_NONE, // client auth + NULL, // realm + NULL, // username + NULL, // nonce + NULL, // password + PJ_TRUE, // dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(400), // expected code + NULL, // expected realm + NULL, // expected nonce + NULL // more check + ); + if (rc != 0) { + goto done; + } + + /* Successful short term authentication */ + rc = run_client_test("Successful scenario (short term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_SHORT_TERM, // server auth + PJ_STUN_AUTH_SHORT_TERM, // client auth + NULL, // realm + USERNAME, // username + NULL, // nonce + PASSWORD, // password + PJ_FALSE, // dummy MI + PJ_FALSE, // expected error + PJ_SUCCESS, // expected code + NULL, // expected realm + NULL, // expected nonce + NULL // more check + ); + if (rc != 0) { + goto done; + } + + /* + * (our own) Extended tests for long term credential + */ + + /* When server wants to use short term credential, but request has + * REALM, reject with .... 401 ??? + */ + rc = run_client_test("Unwanted REALM (short term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_SHORT_TERM, // server auth + PJ_STUN_AUTH_NONE, // client auth + REALM, // realm + USERNAME, // username + NULL, // nonce + PASSWORD, // password + PJ_TRUE, // dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(401), // expected code + NULL, // expected realm + NULL, // expected nonce + &long_term_check2 // more check + ); + if (rc != 0) { + goto done; + } + + + /* + * Long term credential. + * draft-ietf-behave-rfc3489bis-15#section-10.2.2 + */ + + /* If the message does not contain a MESSAGE-INTEGRITY attribute, the + * server MUST generate an error response with an error code of 401 + * (Unauthorized). This response MUST include a REALM value. It is + * RECOMMENDED that the REALM value be the domain name of the + * provider of the STUN server. The response MUST include a NONCE, + * selected by the server. The response SHOULD NOT contain a + * USERNAME or MESSAGE-INTEGRITY attribute. + */ + rc = run_client_test("Missing M-I (long term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_LONG_TERM, // server auth + PJ_STUN_AUTH_NONE, // client auth + NULL, // client realm + NULL, // client username + NULL, // client nonce + NULL, // client password + PJ_FALSE, // client dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(401), // expected code + REALM, // expected realm + NONCE, // expected nonce + &long_term_check1 // more check + ); + if (rc != 0) { + goto done; + } + + /* If the message contains a MESSAGE-INTEGRITY attribute, but is + * missing the USERNAME, REALM or NONCE attributes, the server MUST + * generate an error response with an error code of 400 (Bad + * Request). This response SHOULD NOT include a USERNAME, NONCE, + * REALM or MESSAGE-INTEGRITY attribute. + */ + /* Missing USERNAME */ + rc = run_client_test("Missing USERNAME (long term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_LONG_TERM, // server auth + PJ_STUN_AUTH_NONE, // client auth + REALM, // client realm + NULL, // client username + NONCE, // client nonce + PASSWORD, // client password + PJ_TRUE, // client dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(400), // expected code + NULL, // expected realm + NULL, // expected nonce + &long_term_check2 // more check + ); + if (rc != 0) { + goto done; + } + + /* Missing REALM */ + rc = run_client_test("Missing REALM (long term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_LONG_TERM, // server auth + PJ_STUN_AUTH_NONE, // client auth + NULL, // client realm + USERNAME, // client username + NONCE, // client nonce + PASSWORD, // client password + PJ_TRUE, // client dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(400), // expected code + NULL, // expected realm + NULL, // expected nonce + &long_term_check2 // more check + ); + if (rc != 0) { + goto done; + } + + /* Missing NONCE */ + rc = run_client_test("Missing NONCE (long term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_LONG_TERM, // server auth + PJ_STUN_AUTH_NONE, // client auth + REALM, // client realm + USERNAME, // client username + NULL, // client nonce + PASSWORD, // client password + PJ_TRUE, // client dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(400), // expected code + NULL, // expected realm + NULL, // expected nonce + &long_term_check2 // more check + ); + if (rc != 0) { + goto done; + } + + /* If the NONCE is no longer valid, the server MUST generate an error + * response with an error code of 438 (Stale Nonce). This response + * MUST include a NONCE and REALM attribute and SHOULD NOT incude the + * USERNAME or MESSAGE-INTEGRITY attribute. Servers can invalidate + * nonces in order to provide additional security. See Section 4.3 + * of [RFC2617] for guidelines. + */ + // how?? + + /* If the username in the USERNAME attribute is not valid, the server + * MUST generate an error response with an error code of 401 + * (Unauthorized). This response MUST include a REALM value. It is + * RECOMMENDED that the REALM value be the domain name of the + * provider of the STUN server. The response MUST include a NONCE, + * selected by the server. The response SHOULD NOT contain a + * USERNAME or MESSAGE-INTEGRITY attribute. + */ + rc = run_client_test("Invalid username (long term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_LONG_TERM, // server auth + PJ_STUN_AUTH_LONG_TERM, // client auth + REALM, // client realm + "anotheruser", // client username + "a nonce", // client nonce + "somepassword", // client password + PJ_FALSE, // client dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(401), // expected code + REALM, // expected realm + NONCE, // expected nonce + &long_term_check1 // more check + ); + if (rc != 0) { + goto done; + } + + /* Successful long term authentication */ + rc = run_client_test("Successful scenario (long term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_LONG_TERM, // server auth + PJ_STUN_AUTH_LONG_TERM, // client auth + REALM, // client realm + USERNAME, // client username + "anothernonce", // client nonce + PASSWORD, // client password + PJ_FALSE, // client dummy MI + PJ_FALSE, // expected error + 0, // expected code + NULL, // expected realm + NULL, // expected nonce + &long_term_check3 // more check + ); + if (rc != 0) { + goto done; + } + + /* + * (our own) Extended tests for long term credential + */ + + /* If REALM doesn't match, server must respond with 401 + */ + rc = run_client_test("Invalid REALM (long term)", // title + PJ_TRUE, // server responding + PJ_STUN_AUTH_LONG_TERM, // server auth + PJ_STUN_AUTH_LONG_TERM, // client auth + "anotherrealm", // client realm + USERNAME, // client username + NONCE, // client nonce + PASSWORD, // client password + PJ_FALSE, // client dummy MI + PJ_TRUE, // expected error + PJ_STATUS_FROM_STUN_CODE(401), // expected code + REALM, // expected realm + NONCE, // expected nonce + &long_term_check1 // more check + ); + if (rc != 0) { + goto done; + } + + /* Invalid HMAC */ + + /* Valid static short term, without NONCE */ + + /* Valid static short term, WITH NONCE */ + + /* Valid static long term (with NONCE */ + + /* Valid dynamic short term (without NONCE) */ + + /* Valid dynamic short term (with NONCE) */ + + /* Valid dynamic long term (with NONCE) */ + + +done: + pj_timer_heap_destroy(stun_cfg.timer_heap); + pj_pool_release(pool); + return rc; +} diff --git a/pjnath/src/pjnath-test/stun.c b/pjnath/src/pjnath-test/stun.c index c0d74380..1b11f89b 100644 --- a/pjnath/src/pjnath-test/stun.c +++ b/pjnath/src/pjnath-test/stun.c @@ -269,7 +269,7 @@ static int decode_test(void) continue; /* Try to encode message */ - pj_stun_create_key(pool, &key, NULL, &USERNAME, &PASSWORD); + pj_stun_create_key(pool, &key, NULL, &USERNAME, PJ_STUN_PASSWD_PLAIN, &PASSWORD); status = pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len); if (status != PJ_SUCCESS) { PJ_LOG(1,(THIS_FILE, " encode error: %s", err(status))); @@ -408,55 +408,6 @@ static int decode_verify(void) return 0; } -static int auth_test(void) -{ - /* REALM and USERNAME is present, but MESSAGE-INTEGRITY is not present. - * For short term, must with reply 401 without REALM. - * For long term, must reply with 401 with REALM. - */ - - /* USERNAME is not present, server must respond with 432 (Missing - * Username). - */ - - /* If long term credential is wanted and REALM is not present, server - * must respond with 434 (Missing Realm) - */ - - /* If REALM doesn't match, server must respond with 434 (Missing Realm) - * too, containing REALM and NONCE attribute. - */ - - /* When long term authentication is wanted and NONCE is NOT present, - * server must respond with 435 (Missing Nonce), containing REALM and - * NONCE attribute. - */ - - /* Simulate 438 (Stale Nonce) */ - - /* Simulate 436 (Unknown Username) */ - - /* When server wants to use short term credential, but request has - * REALM, reject with .... ??? - */ - - /* Invalid HMAC */ - - /* Valid static short term, without NONCE */ - - /* Valid static short term, WITH NONCE */ - - /* Valid static long term (with NONCE */ - - /* Valid dynamic short term (without NONCE) */ - - /* Valid dynamic short term (with NONCE) */ - - /* Valid dynamic long term (with NONCE) */ - - return 0; -} - typedef struct test_vector test_vector; static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v); @@ -614,7 +565,7 @@ static int fingerprint_test_vector() pj_str_t s1, s2; pj_stun_create_key(pool, &key, NULL, pj_cstr(&s1, v->username), - pj_cstr(&s2, v->password)); + PJ_STUN_PASSWD_PLAIN, pj_cstr(&s2, v->password)); pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len); } else { @@ -751,10 +702,6 @@ int stun_test(void) if (rc != 0) goto on_return; - rc = auth_test(); - if (rc != 0) - goto on_return; - rc = fingerprint_test_vector(); if (rc != 0) goto on_return; diff --git a/pjnath/src/pjnath-test/test.c b/pjnath/src/pjnath-test/test.c index fc1c124c..312c1ea2 100644 --- a/pjnath/src/pjnath-test/test.c +++ b/pjnath/src/pjnath-test/test.c @@ -68,6 +68,7 @@ static int test_inner(void) #if INCLUDE_STUN_TEST DO_TEST(stun_test()); + DO_TEST(sess_auth_test()); #endif #if INCLUDE_ICE_TEST diff --git a/pjnath/src/pjnath-test/test.h b/pjnath/src/pjnath-test/test.h index 39ad4112..713f3da8 100644 --- a/pjnath/src/pjnath-test/test.h +++ b/pjnath/src/pjnath-test/test.h @@ -23,9 +23,10 @@ #define INCLUDE_STUN_TEST 1 #define INCLUDE_ICE_TEST 1 -extern int stun_test(void); -extern int ice_test(void); -extern int test_main(void); +int stun_test(void); +int sess_auth_test(void); +int ice_test(void); +int test_main(void); extern void app_perror(const char *title, pj_status_t rc); extern pj_pool_factory *mem; diff --git a/pjnath/src/pjnath/ice_session.c b/pjnath/src/pjnath/ice_session.c index 71ff41c4..5ecac387 100644 --- a/pjnath/src/pjnath/ice_session.c +++ b/pjnath/src/pjnath/ice_session.c @@ -120,7 +120,7 @@ static pj_status_t on_stun_send_msg(pj_stun_session *sess, static pj_status_t on_stun_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static void on_stun_request_complete(pj_stun_session *stun_sess, @@ -147,14 +147,14 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, pj_str_t *realm, pj_str_t *username, pj_str_t *nonce, - int *data_type, + pj_stun_passwd_type *data_type, pj_str_t *data); static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, void *user_data, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, - int *data_type, + pj_stun_passwd_type *data_type, pj_str_t *data); @@ -232,7 +232,8 @@ static pj_status_t init_comp(pj_ice_sess *ice, auth_cred.data.dyn_cred.get_cred = &stun_auth_get_cred; auth_cred.data.dyn_cred.get_password = &stun_auth_get_password; auth_cred.data.dyn_cred.user_data = comp->stun_sess; - pj_stun_session_set_credential(comp->stun_sess, &auth_cred); + pj_stun_session_set_credential(comp->stun_sess, PJ_STUN_AUTH_SHORT_TERM, + &auth_cred); return PJ_SUCCESS; } @@ -446,7 +447,7 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, pj_str_t *realm, pj_str_t *username, pj_str_t *nonce, - int *data_type, + pj_stun_passwd_type *data_type, pj_str_t *data) { pj_stun_session *sess = (pj_stun_session *)user_data; @@ -461,12 +462,12 @@ static pj_status_t stun_auth_get_cred(const pj_stun_msg *msg, * incoming requests. */ *username = ice->rx_uname; - *data_type = 0; + *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->rx_pass; } else { *username = ice->tx_uname; - *data_type = 0; + *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->tx_pass; } @@ -479,7 +480,7 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, const pj_str_t *realm, const pj_str_t *username, pj_pool_t *pool, - int *data_type, + pj_stun_passwd_type *data_type, pj_str_t *data) { pj_stun_session *sess = (pj_stun_session *)user_data; @@ -496,7 +497,7 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, /* Verify username */ if (pj_strcmp(username, &ice->tx_uname) != 0) return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); - *data_type = 0; + *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->tx_pass; } else { @@ -521,7 +522,7 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg, if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0) return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); - *data_type = 0; + *data_type = PJ_STUN_PASSWD_PLAIN; *data = ice->rx_pass; } @@ -1903,11 +1904,12 @@ static void on_stun_request_complete(pj_stun_session *stun_sess, static pj_status_t on_stun_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { stun_data *sd; + const pj_stun_msg *msg = rdata->msg; pj_ice_sess *ice; pj_stun_priority_attr *prio_attr; pj_stun_use_candidate_attr *uc_attr; @@ -1921,7 +1923,7 @@ 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_res(sess, msg, + status = pj_stun_session_create_res(sess, rdata, PJ_STUN_SC_BAD_REQUEST, NULL, &tdata); if (status != PJ_SUCCESS) @@ -1992,7 +1994,7 @@ 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_res(sess, msg, + status = pj_stun_session_create_res(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, NULL, &tdata); if (status == PJ_SUCCESS) { @@ -2008,7 +2010,7 @@ 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_res(sess, msg, + status = pj_stun_session_create_res(sess, rdata, PJ_STUN_SC_ROLE_CONFLICT, NULL, &tdata); if (status == PJ_SUCCESS) { @@ -2028,7 +2030,7 @@ static pj_status_t on_stun_rx_request(pj_stun_session *sess, /* * First send response to this request */ - status = pj_stun_session_create_res(sess, msg, 0, NULL, &tdata); + status = pj_stun_session_create_res(sess, rdata, 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 4a6024f0..4ef29599 100644 --- a/pjnath/src/pjnath/stun_auth.c +++ b/pjnath/src/pjnath/stun_auth.c @@ -19,9 +19,11 @@ #include <pjnath/stun_auth.h> #include <pjnath/errno.h> #include <pjlib-util/hmac_sha1.h> +#include <pjlib-util/md5.h> #include <pjlib-util/sha1.h> #include <pj/assert.h> #include <pj/log.h> +#include <pj/pool.h> #include <pj/string.h> #define THIS_FILE "stun_auth.c" @@ -53,6 +55,99 @@ PJ_DEF(void) pj_stun_auth_cred_dup( pj_pool_t *pool, } +/* + * Duplicate request credential. + */ +PJ_DEF(void) pj_stun_req_cred_info_dup( pj_pool_t *pool, + pj_stun_req_cred_info *dst, + const pj_stun_req_cred_info *src) +{ + pj_strdup(pool, &dst->realm, &src->realm); + pj_strdup(pool, &dst->username, &src->username); + pj_strdup(pool, &dst->nonce, &src->nonce); + pj_strdup(pool, &dst->auth_key, &src->auth_key); +} + + +/* Calculate HMAC-SHA1 key for long term credential, by getting + * MD5 digest of username, realm, and password. + */ +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 + * fields: (1) The username, with any quotes and trailing nulls + * removed, (2) A single colon, (3) The realm, with any quotes and + * trailing nulls removed, (4) A single colon, and (5) The + * password, with any trailing nulls removed. + */ + pj_md5_context ctx; + pj_str_t s; + + 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--; + + /* Add username */ + s = *username; + REMOVE_QUOTE(s); + pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); + + /* Add single colon */ + pj_md5_update(&ctx, (pj_uint8_t*)":", 1); + + /* Add realm */ + s = *realm; + REMOVE_QUOTE(s); + pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); + +#undef REMOVE_QUOTE + + /* Another colon */ + pj_md5_update(&ctx, (pj_uint8_t*)":", 1); + + /* Add password */ + pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, passwd->slen); + + /* Done */ + pj_md5_final(&ctx, digest); +} + + +/* + * 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, + pj_stun_passwd_type data_type, + const pj_str_t *data) +{ + PJ_ASSERT_ON_FAIL(pool && key && username && data, return); + + if (realm && realm->slen) { + if (data_type == PJ_STUN_PASSWD_PLAIN) { + key->ptr = (char*) pj_pool_alloc(pool, 16); + calc_md5_key((pj_uint8_t*)key->ptr, realm, username, data); + key->slen = 16; + } else { + pj_strdup(pool, key, data); + } + } else { + pj_assert(data_type == PJ_STUN_PASSWD_PLAIN); + pj_strdup(pool, key, data); + } +} + + PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos) { return (pj_uint16_t) ((pdu[pos] << 8) + pdu[pos+1]); @@ -86,8 +181,8 @@ static pj_status_t create_challenge(pj_pool_t *pool, if (rc != PJ_SUCCESS) return rc; - - if (realm && realm->slen) { + /* SHOULD NOT add REALM, NONCE, USERNAME, and M-I on 400 response */ + if (err_code!=400 && realm && realm->slen) { rc = pj_stun_msg_add_string_attr(pool, response, PJ_STUN_ATTR_REALM, realm); @@ -101,7 +196,7 @@ static pj_status_t create_challenge(pj_pool_t *pool, } } - if (nonce && nonce->slen) { + if (err_code!=400 && nonce && nonce->slen) { rc = pj_stun_msg_add_string_attr(pool, response, PJ_STUN_ATTR_NONCE, nonce); @@ -121,20 +216,20 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, const pj_stun_msg *msg, pj_stun_auth_cred *cred, pj_pool_t *pool, - pj_str_t *auth_key, + pj_stun_req_cred_info *p_info, pj_stun_msg **p_response) { - pj_str_t realm, nonce, password; + pj_stun_req_cred_info tmp_info; const pj_stun_msgint_attr *amsgi; unsigned i, amsgi_pos; pj_bool_t has_attr_beyond_mi; const pj_stun_username_attr *auser; - pj_bool_t username_ok; const pj_stun_realm_attr *arealm; const pj_stun_realm_attr *anonce; pj_hmac_sha1_context ctx; pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE]; - pj_str_t key; + pj_stun_status err_code; + const char *err_text = NULL; pj_status_t status; /* msg and credential MUST be specified */ @@ -149,18 +244,24 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, if (!PJ_STUN_IS_REQUEST(msg->hdr.type)) p_response = NULL; + if (p_info == NULL) + p_info = &tmp_info; + + pj_bzero(p_info, sizeof(pj_stun_req_cred_info)); + /* Get realm and nonce from credential */ - realm.slen = nonce.slen = 0; + p_info->realm.slen = p_info->nonce.slen = 0; if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { - realm = cred->data.static_cred.realm; - nonce = cred->data.static_cred.nonce; + p_info->realm = cred->data.static_cred.realm; + p_info->nonce = cred->data.static_cred.nonce; } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { status = cred->data.dyn_cred.get_auth(cred->data.dyn_cred.user_data, - pool, &realm, &nonce); + pool, &p_info->realm, + &p_info->nonce); if (status != PJ_SUCCESS) return status; } else { - pj_assert(!"Unexpected"); + pj_assert(!"Invalid credential type"); return PJ_EBUG; } @@ -184,14 +285,9 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, for short term, and 401 for long term. The rule has been changed from rfc3489bis-06 */ - int code; - - code = realm.slen ? PJ_STUN_SC_UNAUTHORIZED : PJ_STUN_SC_BAD_REQUEST; - if (p_response) { - create_challenge(pool, msg, code, NULL, - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(code); + err_code = p_info->realm.slen ? PJ_STUN_SC_UNAUTHORIZED : + PJ_STUN_SC_BAD_REQUEST; + goto on_auth_failed; } /* Next check that USERNAME is present */ @@ -202,48 +298,67 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, for both short and long term, since M-I is present. The rule has been changed from rfc3489bis-06 */ - int code = PJ_STUN_SC_BAD_REQUEST; - if (p_response) { - create_challenge(pool, msg, code, "Missing USERNAME", - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(code); + err_code = PJ_STUN_SC_BAD_REQUEST; + err_text = "Missing USERNAME"; + goto on_auth_failed; } /* Get REALM, if any */ arealm = (const pj_stun_realm_attr*) pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_REALM, 0); + /* Reject with 400 if we have long term credential and the request + * is missing REALM attribute. + */ + if (p_info->realm.slen && arealm==NULL) { + err_code = PJ_STUN_SC_BAD_REQUEST; + err_text = "Missing REALM"; + goto on_auth_failed; + } + /* Check if username match */ if (cred->type == PJ_STUN_AUTH_CRED_STATIC) { + pj_bool_t username_ok; username_ok = !pj_strcmp(&auser->value, &cred->data.static_cred.username); - password = cred->data.static_cred.data; + if (username_ok) { + pj_strdup(pool, &p_info->username, + &cred->data.static_cred.username); + pj_stun_create_key(pool, &p_info->auth_key, &p_info->realm, + &auser->value, cred->data.static_cred.data_type, + &cred->data.static_cred.data); + } else { + /* Username mismatch */ + /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should + * return 401 + */ + err_code = PJ_STUN_SC_UNAUTHORIZED; + goto on_auth_failed; + } } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { - int data_type = 0; + pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN; + pj_str_t password; pj_status_t rc; + rc = cred->data.dyn_cred.get_password(msg, cred->data.dyn_cred.user_data, (arealm?&arealm->value:NULL), &auser->value, pool, &data_type, &password); - username_ok = (rc == PJ_SUCCESS); + if (rc == PJ_SUCCESS) { + pj_strdup(pool, &p_info->username, &auser->value); + pj_stun_create_key(pool, &p_info->auth_key, + (arealm?&arealm->value:NULL), &auser->value, + data_type, &password); + } else { + err_code = PJ_STUN_SC_UNAUTHORIZED; + goto on_auth_failed; + } } else { - username_ok = PJ_TRUE; - password.slen = 0; + pj_assert(!"Invalid credential type"); + return PJ_EBUG; } - if (!username_ok) { - /* Username mismatch */ - /* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should - * return 401 - */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, NULL, - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); - } /* Get NONCE attribute */ @@ -251,40 +366,33 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_NONCE, 0); /* Check for long term/short term requirements. */ - if (realm.slen != 0 && arealm == NULL) { + if (p_info->realm.slen != 0 && arealm == NULL) { /* Long term credential is required and REALM is not present */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST, - "Missing REALM", - &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); + err_code = PJ_STUN_SC_BAD_REQUEST; + err_text = "Missing REALM"; + goto on_auth_failed; - } else if (realm.slen != 0 && arealm != NULL) { + } else if (p_info->realm.slen != 0 && arealm != NULL) { /* We want long term, and REALM is present */ /* NONCE must be present. */ - if (anonce == NULL && nonce.slen) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST, - "Missing NONCE", &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); + if (anonce == NULL && p_info->nonce.slen) { + err_code = PJ_STUN_SC_BAD_REQUEST; + err_text = "Missing NONCE"; + goto on_auth_failed; } /* Verify REALM matches */ - if (pj_stricmp(&arealm->value, &realm)) { + if (pj_stricmp(&arealm->value, &p_info->realm)) { /* REALM doesn't match */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, - "Invalid REALM", &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); + err_code = PJ_STUN_SC_UNAUTHORIZED; + err_text = "Invalid REALM"; + goto on_auth_failed; } /* Valid case, will validate the message integrity later */ - } else if (realm.slen == 0 && arealm != NULL) { + } else if (p_info->realm.slen == 0 && arealm != NULL) { /* We want to use short term credential, but client uses long * term credential. The draft doesn't mention anything about * switching between long term and short term. @@ -293,16 +401,14 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, /* For now just accept the credential, anyway it will probably * cause wrong message integrity value later. */ - } else if (realm.slen==0 && arealm == NULL) { + } else if (p_info->realm.slen==0 && arealm == NULL) { /* Short term authentication is wanted, and one is supplied */ /* Application MAY request NONCE to be supplied */ - if (nonce.slen != 0) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, - "NONCE required", &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); + if (p_info->nonce.slen != 0) { + err_code = PJ_STUN_SC_UNAUTHORIZED; + err_text = "NONCE required"; + goto on_auth_failed; } } @@ -321,31 +427,22 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, } else if (cred->type == PJ_STUN_AUTH_CRED_DYNAMIC) { ok = PJ_TRUE; } else { - if (nonce.slen) { - ok = !pj_strcmp(&anonce->value, &nonce); + if (p_info->nonce.slen) { + ok = !pj_strcmp(&anonce->value, &p_info->nonce); } else { ok = PJ_TRUE; } } if (!ok) { - if (p_response) { - create_challenge(pool, msg, PJ_STUN_SC_STALE_NONCE, - NULL, &realm, &nonce, p_response); - } - if (auth_key) { - pj_stun_create_key(pool, auth_key, &realm, - &auser->value, &password); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_STALE_NONCE); + err_code = PJ_STUN_SC_STALE_NONCE; + goto on_auth_failed; } } - /* Calculate key */ - pj_stun_create_key(pool, &key, &realm, &auser->value, &password); - /* Now calculate HMAC of the message. */ - pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen); + pj_hmac_sha1_init(&ctx, (pj_uint8_t*)p_info->auth_key.ptr, + p_info->auth_key.slen); #if PJ_STUN_OLD_STYLE_MI_FINGERPRINT /* Pre rfc3489bis-06 style of calculation */ @@ -382,15 +479,20 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt, if (pj_memcmp(amsgi->hmac, digest, 20)) { /* HMAC value mismatch */ /* According to rfc3489bis-10 Sec 10.1.2 we should return 401 */ - if (p_response) { - create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, - NULL, &realm, &nonce, p_response); - } - return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED); + err_code = PJ_STUN_SC_UNAUTHORIZED; + err_text = "MESSAGE-INTEGRITY mismatch"; + goto on_auth_failed; } /* Everything looks okay! */ return PJ_SUCCESS; + +on_auth_failed: + if (p_response) { + create_challenge(pool, msg, err_code, err_text, + &p_info->realm, &p_info->nonce, p_response); + } + return PJ_STATUS_FROM_STUN_CODE(err_code); } @@ -425,11 +527,10 @@ PJ_DEF(pj_bool_t) pj_stun_auth_valid_for_msg(const pj_stun_msg *msg) switch (err_attr->err_code) { case PJ_STUN_SC_BAD_REQUEST: /* 400 (Bad Request) */ case PJ_STUN_SC_UNAUTHORIZED: /* 401 (Unauthorized) */ - //case PJ_STUN_SC_STALE_CREDENTIALS: /* 430 (Stale Credential) */ - //case PJ_STUN_SC_MISSING_USERNAME: /* 432 (Missing Username) */ - //case PJ_STUN_SC_MISSING_REALM: /* 434 (Missing Realm) */ - //case PJ_STUN_SC_UNKNOWN_USERNAME: /* 436 (Unknown Username) */ - //case PJ_STUN_SC_INTEGRITY_CHECK_FAILURE:/* 431 (Integrity Check Fail) */ + + /* Due to the way this response is generated here, we can't really + * authenticate 420 (Unknown Attribute) response */ + case PJ_STUN_SC_UNKNOWN_ATTRIBUTE: return PJ_FALSE; default: return PJ_TRUE; diff --git a/pjnath/src/pjnath/stun_msg.c b/pjnath/src/pjnath/stun_msg.c index 3e01d4aa..716683fb 100644 --- a/pjnath/src/pjnath/stun_msg.c +++ b/pjnath/src/pjnath/stun_msg.c @@ -20,7 +20,6 @@ #include <pjnath/errno.h> #include <pjlib-util/crc32.h> #include <pjlib-util/hmac_sha1.h> -#include <pjlib-util/md5.h> #include <pj/assert.h> #include <pj/log.h> #include <pj/os.h> @@ -90,6 +89,7 @@ struct attr_desc void **p_attr); pj_status_t (*encode_attr)(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); + void* (*clone_attr)(pj_pool_t *pool, const void *src); }; static pj_status_t decode_sockaddr_attr(pj_pool_t *pool, @@ -101,47 +101,55 @@ static pj_status_t decode_xored_sockaddr_attr(pj_pool_t *pool, static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_sockaddr_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_string_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_string_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_msgint_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_msgint_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_errcode_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_errcode_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_unknown_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_unknown_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_uint_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_uint_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_uint64_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_uint64_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_binary_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); +static void* clone_binary_attr(pj_pool_t *pool, const void *src); static pj_status_t decode_empty_attr(pj_pool_t *pool, const pj_uint8_t *buf, void **p_attr); static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, unsigned len, unsigned *printed); - +static void* clone_empty_attr(pj_pool_t *pool, const void *src); static struct attr_desc mandatory_attr_desc[] = { @@ -149,235 +157,274 @@ static struct attr_desc mandatory_attr_desc[] = /* type zero */ NULL, NULL, + NULL, NULL }, { /* PJ_STUN_ATTR_MAPPED_ADDR, */ "MAPPED-ADDRESS", &decode_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_RESPONSE_ADDR, */ "RESPONSE-ADDRESS", &decode_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_CHANGE_REQUEST, */ "CHANGE-REQUEST", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_SOURCE_ADDR, */ "SOURCE-ADDRESS", &decode_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_CHANGED_ADDR, */ "CHANGED-ADDRESS", &decode_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_USERNAME, */ "USERNAME", &decode_string_attr, - &encode_string_attr + &encode_string_attr, + &clone_string_attr }, { /* PJ_STUN_ATTR_PASSWORD, */ "PASSWORD", &decode_string_attr, - &encode_string_attr + &encode_string_attr, + &clone_string_attr }, { /* PJ_STUN_ATTR_MESSAGE_INTEGRITY, */ "MESSAGE-INTEGRITY", &decode_msgint_attr, - &encode_msgint_attr + &encode_msgint_attr, + &clone_msgint_attr }, { /* PJ_STUN_ATTR_ERROR_CODE, */ "ERROR-CODE", &decode_errcode_attr, - &encode_errcode_attr + &encode_errcode_attr, + &clone_errcode_attr }, { /* PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, */ "UNKNOWN-ATTRIBUTES", &decode_unknown_attr, - &encode_unknown_attr + &encode_unknown_attr, + &clone_unknown_attr }, { /* PJ_STUN_ATTR_REFLECTED_FROM, */ "REFLECTED-FROM", &decode_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_CHANNEL_NUMBER (0x000C) */ "CHANNEL-NUMBER", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_LIFETIME, */ "LIFETIME", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* ID 0x000E is not assigned */ NULL, NULL, + NULL, NULL }, { /* PJ_STUN_ATTR_MAGIC_COOKIE */ "MAGIC-COOKIE", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_BANDWIDTH, */ "BANDWIDTH", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* ID 0x0011 is not assigned */ NULL, NULL, + NULL, NULL }, { /* PJ_STUN_ATTR_PEER_ADDRESS, */ "PEER-ADDRESS", &decode_xored_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_DATA, */ "DATA", &decode_binary_attr, - &encode_binary_attr + &encode_binary_attr, + &clone_binary_attr }, { /* PJ_STUN_ATTR_REALM, */ "REALM", &decode_string_attr, - &encode_string_attr + &encode_string_attr, + &clone_string_attr }, { /* PJ_STUN_ATTR_NONCE, */ "NONCE", &decode_string_attr, - &encode_string_attr + &encode_string_attr, + &clone_string_attr }, { /* PJ_STUN_ATTR_RELAY_ADDRESS, */ "RELAY-ADDRESS", &decode_xored_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_REQUESTED_ADDR_TYPE, */ "REQUESTED-ADDRESS-TYPE", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_REQUESTED_PROPS, */ "REQUESTED-PROPS", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_REQUESTED_TRANSPORT, */ "REQUESTED-TRANSPORT", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* ID 0x001A is not assigned */ NULL, NULL, + NULL, NULL }, { /* ID 0x001B is not assigned */ NULL, NULL, + NULL, NULL }, { /* ID 0x001C is not assigned */ NULL, NULL, + NULL, NULL }, { /* ID 0x001D is not assigned */ NULL, NULL, + NULL, NULL }, { /* ID 0x001E is not assigned */ NULL, NULL, + NULL, NULL }, { /* ID 0x001F is not assigned */ NULL, NULL, + NULL, NULL }, { /* PJ_STUN_ATTR_XOR_MAPPED_ADDRESS, */ "XOR-MAPPED-ADDRESS", &decode_xored_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_TIMER_VAL, */ "TIMER-VAL", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_RESERVATION_TOKEN, */ "RESERVATION-TOKEN", &decode_uint64_attr, - &encode_uint64_attr + &encode_uint64_attr, + &clone_uint64_attr }, { /* PJ_STUN_ATTR_XOR_REFLECTED_FROM, */ "XOR-REFLECTED-FROM", &decode_xored_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_PRIORITY, */ "PRIORITY", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_USE_CANDIDATE, */ "USE-CANDIDATE", &decode_empty_attr, - &encode_empty_attr + &encode_empty_attr, + &clone_empty_attr }, { /* PJ_STUN_ATTR_XOR_INTERNAL_ADDR, */ "XOR-INTERNAL-ADDRESS", &decode_xored_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, /* Sentinel */ @@ -385,6 +432,7 @@ static struct attr_desc mandatory_attr_desc[] = /* PJ_STUN_ATTR_END_MANDATORY_ATTR */ NULL, NULL, + NULL, NULL } }; @@ -395,61 +443,71 @@ static struct attr_desc extended_attr_desc[] = /* ID 0x8021 is not assigned */ NULL, NULL, + NULL, NULL }, { /* PJ_STUN_ATTR_SERVER, */ "SERVER", &decode_string_attr, - &encode_string_attr + &encode_string_attr, + &clone_string_attr }, { /* PJ_STUN_ATTR_ALTERNATE_SERVER, */ "ALTERNATE-SERVER", &decode_sockaddr_attr, - &encode_sockaddr_attr + &encode_sockaddr_attr, + &clone_sockaddr_attr }, { /* PJ_STUN_ATTR_REFRESH_INTERVAL, */ "REFRESH-INTERVAL", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* ID 0x8025 is not assigned*/ NULL, NULL, + NULL, NULL }, { /* PADDING, 0x8026 */ NULL, NULL, + NULL, NULL }, { /* CACHE-TIMEOUT, 0x8027 */ NULL, NULL, + NULL, NULL }, { /* PJ_STUN_ATTR_FINGERPRINT, */ "FINGERPRINT", &decode_uint_attr, - &encode_uint_attr + &encode_uint_attr, + &clone_uint_attr }, { /* PJ_STUN_ATTR_ICE_CONTROLLED, */ "ICE-CONTROLLED", &decode_uint64_attr, - &encode_uint64_attr + &encode_uint64_attr, + &clone_uint64_attr }, { /* PJ_STUN_ATTR_ICE_CONTROLLING, */ "ICE-CONTROLLING", &decode_uint64_attr, - &encode_uint64_attr + &encode_uint64_attr, + &clone_uint64_attr } }; @@ -826,6 +884,13 @@ static pj_status_t encode_sockaddr_attr(const void *a, pj_uint8_t *buf, } +static void* clone_sockaddr_attr(pj_pool_t *pool, const void *src) +{ + pj_stun_sockaddr_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_sockaddr_attr); + pj_memcpy(dst, src, sizeof(pj_stun_sockaddr_attr)); + return (void*)dst; +} + ////////////////////////////////////////////////////////////////////////////// /* * STUN generic string attribute @@ -934,6 +999,17 @@ static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf, } +static void* clone_string_attr(pj_pool_t *pool, const void *src) +{ + const pj_stun_string_attr *asrc = (const pj_stun_string_attr*)src; + pj_stun_string_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_string_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_attr_hdr)); + pj_strdup(pool, &dst->value, &asrc->value); + + return (void*)dst; +} + ////////////////////////////////////////////////////////////////////////////// /* * STUN empty attribute (used by USE-CANDIDATE). @@ -1018,6 +1094,15 @@ static pj_status_t encode_empty_attr(const void *a, pj_uint8_t *buf, } +static void* clone_empty_attr(pj_pool_t *pool, const void *src) +{ + pj_stun_empty_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_empty_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_empty_attr)); + + return (void*) dst; +} + ////////////////////////////////////////////////////////////////////////////// /* * STUN generic 32bit integer attribute. @@ -1103,6 +1188,16 @@ static pj_status_t encode_uint_attr(const void *a, pj_uint8_t *buf, return PJ_SUCCESS; } + +static void* clone_uint_attr(pj_pool_t *pool, const void *src) +{ + pj_stun_uint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_uint_attr)); + + return (void*)dst; +} + ////////////////////////////////////////////////////////////////////////////// /* @@ -1189,6 +1284,16 @@ static pj_status_t encode_uint64_attr(const void *a, pj_uint8_t *buf, } +static void* clone_uint64_attr(pj_pool_t *pool, const void *src) +{ + pj_stun_uint64_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_uint64_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_uint64_attr)); + + return (void*)dst; +} + + ////////////////////////////////////////////////////////////////////////////// /* * STUN MESSAGE-INTEGRITY attribute. @@ -1271,6 +1376,16 @@ static pj_status_t encode_msgint_attr(const void *a, pj_uint8_t *buf, return PJ_SUCCESS; } + +static void* clone_msgint_attr(pj_pool_t *pool, const void *src) +{ + pj_stun_msgint_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_msgint_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_msgint_attr)); + + return (void*) dst; +} + ////////////////////////////////////////////////////////////////////////////// /* * STUN ERROR-CODE @@ -1381,6 +1496,18 @@ static pj_status_t encode_errcode_attr(const void *a, pj_uint8_t *buf, return PJ_SUCCESS; } + +static void* clone_errcode_attr(pj_pool_t *pool, const void *src) +{ + const pj_stun_errcode_attr *asrc = (const pj_stun_errcode_attr*)src; + pj_stun_errcode_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_errcode_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_errcode_attr)); + pj_strdup(pool, &dst->reason, &asrc->reason); + + return (void*)dst; +} + ////////////////////////////////////////////////////////////////////////////// /* * STUN UNKNOWN-ATTRIBUTES attribute @@ -1499,6 +1626,15 @@ static pj_status_t encode_unknown_attr(const void *a, pj_uint8_t *buf, } +static void* clone_unknown_attr(pj_pool_t *pool, const void *src) +{ + pj_stun_unknown_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_unknown_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_unknown_attr)); + + return (void*)dst; +} + ////////////////////////////////////////////////////////////////////////////// /* * STUN generic binary attribute @@ -1597,6 +1733,21 @@ static pj_status_t encode_binary_attr(const void *a, pj_uint8_t *buf, } +static void* clone_binary_attr(pj_pool_t *pool, const void *src) +{ + const pj_stun_binary_attr *asrc = (const pj_stun_binary_attr*)src; + pj_stun_binary_attr *dst = PJ_POOL_ALLOC_T(pool, pj_stun_binary_attr); + + pj_memcpy(dst, src, sizeof(pj_stun_binary_attr)); + + if (asrc->length) { + dst->data = (pj_uint8_t*) pj_pool_alloc(pool, asrc->length); + pj_memcpy(dst->data, asrc->data, asrc->length); + } + + return (void*)dst; +} + ////////////////////////////////////////////////////////////////////////////// /* @@ -1641,6 +1792,31 @@ PJ_DEF(pj_status_t) pj_stun_msg_create( pj_pool_t *pool, /* + * Clone a STUN message with all of its attributes. + */ +PJ_DEF(pj_stun_msg*) pj_stun_msg_clone( pj_pool_t *pool, + const pj_stun_msg *src) +{ + pj_stun_msg *dst; + unsigned i; + + PJ_ASSERT_RETURN(pool && src, NULL); + + dst = PJ_POOL_ZALLOC_T(pool, pj_stun_msg); + pj_memcpy(dst, src, sizeof(pj_stun_msg)); + + /* Duplicate the attributes */ + for (i=0, dst->attr_count=0; i<src->attr_count; ++i) { + dst->attr[dst->attr_count] = pj_stun_attr_clone(pool, src->attr[i]); + if (dst->attr[dst->attr_count]) + ++dst->attr_count; + } + + return dst; +} + + +/* * Add STUN attribute to STUN message. */ PJ_DEF(pj_status_t) pj_stun_msg_add_attr(pj_stun_msg *msg, @@ -1981,79 +2157,6 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool, return PJ_SUCCESS; } -/* Calculate HMAC-SHA1 key for long term credential, by getting - * MD5 digest of username, realm, and password. - */ -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 - * fields: (1) The username, with any quotes and trailing nulls - * removed, (2) A single colon, (3) The realm, with any quotes and - * trailing nulls removed, (4) A single colon, and (5) The - * password, with any trailing nulls removed. - */ - pj_md5_context ctx; - pj_str_t s; - - 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--; - - /* Add username */ - s = *username; - REMOVE_QUOTE(s); - pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); - - /* Add single colon */ - pj_md5_update(&ctx, (pj_uint8_t*)":", 1); - - /* Add realm */ - s = *realm; - REMOVE_QUOTE(s); - pj_md5_update(&ctx, (pj_uint8_t*)s.ptr, s.slen); - -#undef REMOVE_QUOTE - - /* Another colon */ - pj_md5_update(&ctx, (pj_uint8_t*)":", 1); - - /* Add password */ - pj_md5_update(&ctx, (pj_uint8_t*)passwd->ptr, passwd->slen); - - /* Done */ - pj_md5_final(&ctx, digest); -} - - -/* - * 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) { @@ -2207,7 +2310,7 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg, /* MESSAGE-INTEGRITY must be the last attribute in the message, or * the last attribute before FINGERPRINT. */ - if (i < msg->attr_count-2) { + if (msg->attr_count>1 && i < msg->attr_count-2) { /* Should not happen for message generated by us */ pj_assert(PJ_FALSE); return PJNATH_ESTUNMSGINTPOS; @@ -2298,3 +2401,20 @@ PJ_DEF(pj_stun_attr_hdr*) pj_stun_msg_find_attr( const pj_stun_msg *msg, return NULL; } + +/* + * Clone a STUN attribute. + */ +PJ_DEF(pj_stun_attr_hdr*) pj_stun_attr_clone( pj_pool_t *pool, + const pj_stun_attr_hdr *attr) +{ + const struct attr_desc *adesc; + + /* Get the attribute descriptor */ + adesc = find_attr_desc(attr->type); + PJ_ASSERT_RETURN(adesc != NULL, NULL); + + return (pj_stun_attr_hdr*) (*adesc->clone_attr)(pool, attr); +} + + diff --git a/pjnath/src/pjnath/stun_session.c b/pjnath/src/pjnath/stun_session.c index 29dc3e57..02b017cf 100644 --- a/pjnath/src/pjnath/stun_session.c +++ b/pjnath/src/pjnath/stun_session.c @@ -17,6 +17,7 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include <pjnath/stun_session.h> +#include <pjnath/errno.h> #include <pjlib.h> struct pj_stun_session @@ -29,7 +30,14 @@ struct pj_stun_session void *user_data; pj_bool_t use_fingerprint; - pj_stun_auth_cred *cred; + + char dump_buf[1000]; + + pj_stun_auth_type auth_type; + pj_stun_auth_cred cred; + int auth_retry; + pj_str_t next_nonce; + pj_str_t srv_name; pj_stun_tx_data pending_request_list; @@ -125,39 +133,6 @@ static pj_status_t create_tdata(pj_stun_session *sess, return PJ_SUCCESS; } -static pj_status_t create_request_tdata(pj_stun_session *sess, - unsigned msg_type, - pj_uint32_t magic, - const pj_uint8_t tsx_id[12], - pj_stun_tx_data **p_tdata) -{ - pj_status_t status; - pj_stun_tx_data *tdata; - - status = create_tdata(sess, &tdata); - if (status != PJ_SUCCESS) - return status; - - /* Create STUN message */ - status = pj_stun_msg_create(tdata->pool, msg_type, magic, - tsx_id, &tdata->msg); - if (status != PJ_SUCCESS) { - pj_pool_release(tdata->pool); - return status; - } - - /* copy the request's transaction ID as the transaction key. */ - pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id)); - tdata->msg_magic = tdata->msg->hdr.magic; - pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id, - sizeof(tdata->msg->hdr.tsx_id)); - - *p_tdata = tdata; - - return PJ_SUCCESS; -} - - static void stun_tsx_on_destroy(pj_stun_client_tsx *tsx) { pj_stun_tx_data *tdata; @@ -215,55 +190,13 @@ static void on_cache_timeout(pj_timer_heap_t *timer_heap, pj_stun_msg_destroy_tdata(tdata->sess, tdata); } -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) { - auth_key->slen = 0; - return PJ_SUCCESS; - } else if (sess->cred->type == PJ_STUN_AUTH_CRED_STATIC) { - 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; - void *user_data = sess->cred->data.dyn_cred.user_data; - int data_type = 0; - pj_status_t status; - - realm.slen = username.slen = nonce.slen = 0; - password = PJ_POOL_ZALLOC_T(pool, pj_str_t); - status = (*sess->cred->data.dyn_cred.get_cred)(msg, user_data, pool, - &realm, &username, - &nonce, &data_type, - password); - if (status != PJ_SUCCESS) - return status; - - pj_stun_create_key(pool, auth_key, - &realm, &username, password); - - return PJ_SUCCESS; - - } else { - pj_assert(!"Unknown credential type"); - return PJ_EBUG; - } -} - static pj_status_t apply_msg_options(pj_stun_session *sess, pj_pool_t *pool, + const pj_stun_req_cred_info *auth_info, 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; + pj_str_t realm, username, nonce, auth_key; /* The server SHOULD include a SERVER attribute in all responses */ if (sess->srv_name.slen && PJ_STUN_IS_RESPONSE(msg->hdr.type)) { @@ -271,32 +204,16 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, &sess->srv_name); } - need_auth = pj_stun_auth_valid_for_msg(msg); - - if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_STATIC && - need_auth) - { - realm = sess->cred->data.static_cred.realm; - username = sess->cred->data.static_cred.username; - data_type = sess->cred->data.static_cred.data_type; - password = sess->cred->data.static_cred.data; - nonce = sess->cred->data.static_cred.nonce; - - } else if (sess->cred && sess->cred->type == PJ_STUN_AUTH_CRED_DYNAMIC && - need_auth) - { - void *user_data = sess->cred->data.dyn_cred.user_data; - - status = (*sess->cred->data.dyn_cred.get_cred)(msg, user_data, pool, - &realm, &username, - &nonce, &data_type, - &password); - if (status != PJ_SUCCESS) - return status; + if (pj_stun_auth_valid_for_msg(msg) && auth_info) { + realm = auth_info->realm; + username = auth_info->username; + nonce = auth_info->nonce; + auth_key = auth_info->auth_key; + } else { + realm.slen = username.slen = nonce.slen = auth_key.slen = 0; } - - /* Create and add USERNAME attribute for */ + /* Create and add USERNAME attribute if needed */ if (username.slen && PJ_STUN_IS_REQUEST(msg->hdr.type)) { status = pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME, @@ -323,7 +240,7 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, } /* Add MESSAGE-INTEGRITY attribute */ - if (username.slen && need_auth) { + if (username.slen && auth_key.slen) { status = pj_stun_msg_add_msgint_attr(pool, msg); PJ_ASSERT_RETURN(status==PJ_SUCCESS, status); } @@ -339,6 +256,105 @@ static pj_status_t apply_msg_options(pj_stun_session *sess, return PJ_SUCCESS; } +static pj_status_t handle_auth_challenge(pj_stun_session *sess, + const pj_stun_tx_data *request, + const pj_stun_msg *response, + const pj_sockaddr_t *src_addr, + unsigned src_addr_len, + pj_bool_t *notify_user) +{ + const pj_stun_errcode_attr *ea; + + *notify_user = PJ_TRUE; + + if (sess->auth_type != PJ_STUN_AUTH_LONG_TERM) + return PJ_SUCCESS; + + if (!PJ_STUN_IS_ERROR_RESPONSE(response->hdr.type)) + return PJ_SUCCESS; + + ea = (const pj_stun_errcode_attr*) + pj_stun_msg_find_attr(response, PJ_STUN_ATTR_ERROR_CODE, 0); + if (!ea) { + PJ_LOG(4,(SNAME(sess), "Invalid error response: no ERROR-CODE" + " attribute")); + *notify_user = PJ_FALSE; + return PJNATH_EINSTUNMSG; + } + + if (ea->err_code == PJ_STUN_SC_UNAUTHORIZED || + ea->err_code == PJ_STUN_SC_STALE_NONCE) + { + const pj_stun_nonce_attr *anonce; + pj_stun_tx_data *tdata; + unsigned i; + pj_status_t status; + + anonce = (const pj_stun_nonce_attr*) + pj_stun_msg_find_attr(response, PJ_STUN_ATTR_NONCE, 0); + if (!anonce) { + PJ_LOG(4,(SNAME(sess), "Invalid response: missing NONCE")); + *notify_user = PJ_FALSE; + return PJNATH_EINSTUNMSG; + } + + /* Bail out if we've supplied the correct nonce */ + if (pj_strcmp(&anonce->value, &sess->next_nonce)==0) { + return PJ_SUCCESS; + } + + /* Bail out if we've tried too many */ + if (++sess->auth_retry > 3) { + PJ_LOG(4,(SNAME(sess), "Error: authentication failed (too " + "many retries)")); + return PJ_STATUS_FROM_STUN_CODE(401); + } + + /* Save next_nonce */ + pj_strdup(sess->pool, &sess->next_nonce, &anonce->value); + + /* Create new request */ + status = pj_stun_session_create_req(sess, request->msg->hdr.type, + request->msg->hdr.magic, + NULL, &tdata); + if (status != PJ_SUCCESS) + return status; + + /* Duplicate all the attributes in the old request, except + * USERNAME, REALM, M-I, and NONCE + */ + for (i=0; i<request->msg->attr_count; ++i) { + const pj_stun_attr_hdr *asrc = request->msg->attr[i]; + + if (asrc->type == PJ_STUN_ATTR_USERNAME || + asrc->type == PJ_STUN_ATTR_REALM || + asrc->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY || + asrc->type == PJ_STUN_ATTR_NONCE) + { + continue; + } + + tdata->msg->attr[tdata->msg->attr_count++] = + pj_stun_attr_clone(tdata->pool, asrc); + } + + /* Will retry the request with authentication, no need to + * notify user. + */ + *notify_user = PJ_FALSE; + + PJ_LOG(4,(SNAME(sess), "Retrying request with new authentication")); + + /* Retry the request */ + status = pj_stun_session_send_msg(sess, PJ_TRUE, src_addr, + src_addr_len, tdata); + + } else { + sess->auth_retry = 0; + } + + return PJ_SUCCESS; +} static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, pj_status_t status, @@ -346,14 +362,21 @@ static void stun_tsx_on_complete(pj_stun_client_tsx *tsx, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { + pj_stun_session *sess; + pj_bool_t notify_user = PJ_TRUE; pj_stun_tx_data *tdata; tdata = (pj_stun_tx_data*) pj_stun_client_tsx_get_data(tsx); + sess = tdata->sess; + + /* Handle authentication challenge */ + handle_auth_challenge(sess, tdata, response, src_addr, + src_addr_len, ¬ify_user); - if (tdata->sess->cb.on_request_complete) { - (*tdata->sess->cb.on_request_complete)(tdata->sess, status, tdata, - response, - src_addr, src_addr_len); + if (notify_user && sess->cb.on_request_complete) { + (*sess->cb.on_request_complete)(sess, status, tdata, + response, + src_addr, src_addr_len); } } @@ -488,20 +511,65 @@ PJ_DEF(pj_status_t) pj_stun_session_set_server_name(pj_stun_session *sess, return PJ_SUCCESS; } -PJ_DEF(void) pj_stun_session_set_credential(pj_stun_session *sess, - const pj_stun_auth_cred *cred) +PJ_DEF(pj_status_t) pj_stun_session_set_credential(pj_stun_session *sess, + pj_stun_auth_type auth_type, + const pj_stun_auth_cred *cred) { - PJ_ASSERT_ON_FAIL(sess, return); + PJ_ASSERT_RETURN(sess, PJ_EINVAL); + + sess->auth_type = auth_type; if (cred) { - if (!sess->cred) - sess->cred = PJ_POOL_ALLOC_T(sess->pool, pj_stun_auth_cred); - pj_stun_auth_cred_dup(sess->pool, sess->cred, cred); + pj_stun_auth_cred_dup(sess->pool, &sess->cred, cred); } else { - sess->cred = NULL; + sess->auth_type = PJ_STUN_AUTH_NONE; + pj_bzero(&sess->cred, sizeof(sess->cred)); } + + return PJ_SUCCESS; } +static pj_status_t get_auth(pj_stun_session *sess, + pj_stun_tx_data *tdata) +{ + if (sess->cred.type == PJ_STUN_AUTH_CRED_STATIC) { + tdata->auth_info.realm = sess->cred.data.static_cred.realm; + tdata->auth_info.username = sess->cred.data.static_cred.username; + tdata->auth_info.nonce = sess->cred.data.static_cred.nonce; + + pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, + &tdata->auth_info.realm, + &tdata->auth_info.username, + sess->cred.data.static_cred.data_type, + &sess->cred.data.static_cred.data); + + } else if (sess->cred.type == PJ_STUN_AUTH_CRED_DYNAMIC) { + pj_str_t password; + void *user_data = sess->cred.data.dyn_cred.user_data; + pj_stun_passwd_type data_type = PJ_STUN_PASSWD_PLAIN; + pj_status_t rc; + + rc = (*sess->cred.data.dyn_cred.get_cred)(tdata->msg, user_data, + tdata->pool, + &tdata->auth_info.realm, + &tdata->auth_info.username, + &tdata->auth_info.nonce, + &data_type, &password); + if (rc != PJ_SUCCESS) + return rc; + + pj_stun_create_key(tdata->pool, &tdata->auth_info.auth_key, + &tdata->auth_info.realm, &tdata->auth_info.username, + data_type, &password); + + } else { + pj_assert(!"Unknown credential type"); + return PJ_EBUG; + } + + return PJ_SUCCESS; +} + PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, int method, pj_uint32_t magic, @@ -513,10 +581,56 @@ PJ_DEF(pj_status_t) pj_stun_session_create_req(pj_stun_session *sess, PJ_ASSERT_RETURN(sess && p_tdata, PJ_EINVAL); - status = create_request_tdata(sess, method, magic, tsx_id, &tdata); + status = create_tdata(sess, &tdata); if (status != PJ_SUCCESS) return status; + /* Create STUN message */ + status = pj_stun_msg_create(tdata->pool, method, magic, + tsx_id, &tdata->msg); + if (status != PJ_SUCCESS) { + pj_pool_release(tdata->pool); + return status; + } + + /* copy the request's transaction ID as the transaction key. */ + pj_assert(sizeof(tdata->msg_key)==sizeof(tdata->msg->hdr.tsx_id)); + tdata->msg_magic = tdata->msg->hdr.magic; + pj_memcpy(tdata->msg_key, tdata->msg->hdr.tsx_id, + sizeof(tdata->msg->hdr.tsx_id)); + + + /* Get authentication information for the request */ + if (sess->auth_type == PJ_STUN_AUTH_NONE) { + /* No authentication */ + + } else if (sess->auth_type == PJ_STUN_AUTH_SHORT_TERM) { + /* MUST put authentication in request */ + status = get_auth(sess, tdata); + if (status != PJ_SUCCESS) { + pj_pool_release(tdata->pool); + return status; + } + + } else if (sess->auth_type == PJ_STUN_AUTH_LONG_TERM) { + /* Only put authentication information if we've received + * response from server. + */ + if (sess->next_nonce.slen != 0) { + status = get_auth(sess, tdata); + if (status != PJ_SUCCESS) { + pj_pool_release(tdata->pool); + return status; + } + tdata->auth_info.nonce = sess->next_nonce; + } + + } else { + pj_assert(!"Invalid authentication type"); + pj_pool_release(tdata->pool); + return PJ_EBUG; + } + *p_tdata = tdata; return PJ_SUCCESS; } @@ -551,7 +665,7 @@ 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_res( pj_stun_session *sess, - const pj_stun_msg *req, + const pj_stun_rx_data *rdata, unsigned err_code, const pj_str_t *err_msg, pj_stun_tx_data **p_tdata) @@ -564,17 +678,21 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, return status; /* Create STUN response message */ - status = pj_stun_msg_create_response(tdata->pool, req, err_code, err_msg, - &tdata->msg); + status = pj_stun_msg_create_response(tdata->pool, rdata->msg, + err_code, err_msg, &tdata->msg); if (status != PJ_SUCCESS) { pj_pool_release(tdata->pool); return status; } /* copy the request's transaction ID as the transaction key. */ - pj_assert(sizeof(tdata->msg_key)==sizeof(req->hdr.tsx_id)); - tdata->msg_magic = req->hdr.magic; - pj_memcpy(tdata->msg_key, req->hdr.tsx_id, sizeof(req->hdr.tsx_id)); + pj_assert(sizeof(tdata->msg_key)==sizeof(rdata->msg->hdr.tsx_id)); + tdata->msg_magic = rdata->msg->hdr.magic; + pj_memcpy(tdata->msg_key, rdata->msg->hdr.tsx_id, + sizeof(rdata->msg->hdr.tsx_id)); + + /* copy the credential found in the request */ + pj_stun_req_cred_info_dup(tdata->pool, &tdata->auth_info, &rdata->info); *p_tdata = tdata; @@ -586,29 +704,18 @@ PJ_DEF(pj_status_t) pj_stun_session_create_res( pj_stun_session *sess, static void dump_tx_msg(pj_stun_session *sess, const pj_stun_msg *msg, unsigned pkt_size, const pj_sockaddr_t *addr) { - const char *dst_name; - int dst_port; - const pj_sockaddr *dst = (const pj_sockaddr*)addr; - char buf[800]; + char dst_name[80]; - if (dst->addr.sa_family == pj_AF_INET()) { - dst_name = pj_inet_ntoa(dst->ipv4.sin_addr); - dst_port = pj_ntohs(dst->ipv4.sin_port); - } else if (dst->addr.sa_family == pj_AF_INET6()) { - dst_name = "IPv6"; - dst_port = pj_ntohs(dst->ipv6.sin6_port); - } else { - LOG_ERR_(sess, "Invalid address family", PJ_EINVAL); - return; - } + pj_sockaddr_print(addr, dst_name, sizeof(dst_name), 3); PJ_LOG(5,(SNAME(sess), - "TX %d bytes STUN message to %s:%d:\n" + "TX %d bytes STUN message to %s:\n" "--- begin STUN message ---\n" "%s" "--- end of STUN message ---\n", - pkt_size, dst_name, dst_port, - pj_stun_msg_dump(msg, buf, sizeof(buf), NULL))); + pkt_size, dst_name, + pj_stun_msg_dump(msg, sess->dump_buf, sizeof(sess->dump_buf), + NULL))); } @@ -631,7 +738,8 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, pj_lock_acquire(sess->lock); /* Apply options */ - status = apply_msg_options(sess, tdata->pool, tdata->msg); + status = apply_msg_options(sess, tdata->pool, &tdata->auth_info, + tdata->msg); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); pj_lock_release(sess->lock); @@ -639,18 +747,10 @@ 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_lock_release(sess->lock); - 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, - &tdata->auth_key, + &tdata->auth_info.auth_key, &tdata->pkt_size); if (status != PJ_SUCCESS) { pj_stun_msg_destroy_tdata(sess, tdata); @@ -742,7 +842,7 @@ PJ_DEF(pj_status_t) pj_stun_session_send_msg( pj_stun_session *sess, * Create and send STUN response message. */ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, - const pj_stun_msg *req, + const pj_stun_rx_data *rdata, unsigned code, const char *errmsg, pj_bool_t cache, @@ -753,7 +853,7 @@ PJ_DEF(pj_status_t) pj_stun_session_respond( pj_stun_session *sess, pj_str_t reason; pj_stun_tx_data *tdata; - status = pj_stun_session_create_res(sess, req, code, + status = pj_stun_session_create_res(sess, rdata, code, (errmsg?pj_cstr(&reason,errmsg):NULL), &tdata); if (status != PJ_SUCCESS) @@ -813,7 +913,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, + const pj_stun_req_cred_info *auth_info, pj_bool_t retransmission, const pj_sockaddr_t *addr, unsigned addr_len) { @@ -823,7 +923,7 @@ static pj_status_t send_response(pj_stun_session *sess, /* Apply options */ if (!retransmission) { - status = apply_msg_options(sess, pool, response); + status = apply_msg_options(sess, pool, auth_info, response); if (status != PJ_SUCCESS) return status; } @@ -834,7 +934,7 @@ static pj_status_t send_response(pj_stun_session *sess, /* Encode */ status = pj_stun_msg_encode(response, out_pkt, out_max_len, 0, - auth_key, &out_len); + &auth_info->auth_key, &out_len); if (status != PJ_SUCCESS) { LOG_ERR_(sess, "Error encoding message", status); return status; @@ -853,23 +953,26 @@ static pj_status_t send_response(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, + pj_stun_rx_data *rdata, pj_pool_t *tmp_pool, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_stun_msg *response; - pj_str_t auth_key; pj_status_t status; - if (PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type) || sess->cred == NULL) + if (PJ_STUN_IS_ERROR_RESPONSE(rdata->msg->hdr.type) || + sess->auth_type == PJ_STUN_AUTH_NONE) + { return PJ_SUCCESS; + } - status = pj_stun_authenticate_request(pkt, pkt_len, msg, sess->cred, - tmp_pool, &auth_key, &response); + status = pj_stun_authenticate_request(pkt, pkt_len, rdata->msg, + &sess->cred, tmp_pool, &rdata->info, + &response); if (status != PJ_SUCCESS && response != NULL) { PJ_LOG(5,(SNAME(sess), "Message authentication failed")); - send_response(sess, tmp_pool, response, &auth_key, PJ_FALSE, + send_response(sess, tmp_pool, response, &rdata->info, PJ_FALSE, src_addr, src_addr_len); } @@ -897,14 +1000,18 @@ static pj_status_t on_incoming_response(pj_stun_session *sess, return PJ_SUCCESS; } + if (sess->auth_type == PJ_STUN_AUTH_NONE) + options |= PJ_STUN_NO_AUTHENTICATE; + /* Authenticate the message, unless PJ_STUN_NO_AUTHENTICATE * is specified in the option. */ - if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && tdata->auth_key.slen != 0 - && pj_stun_auth_valid_for_msg(msg)) + if ((options & PJ_STUN_NO_AUTHENTICATE) == 0 && + tdata->auth_info.auth_key.slen != 0 && + pj_stun_auth_valid_for_msg(msg)) { status = pj_stun_authenticate_response(pkt, pkt_len, msg, - &tdata->auth_key); + &tdata->auth_info.auth_key); if (status != PJ_SUCCESS) { PJ_LOG(5,(SNAME(sess), "Response authentication failed")); @@ -962,7 +1069,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, &t->auth_key, PJ_TRUE, + send_response(sess, tmp_pool, t->msg, &t->auth_info, PJ_TRUE, src_addr, src_addr_len); return PJ_SUCCESS; } @@ -976,18 +1083,26 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, pj_pool_t *tmp_pool, const pj_uint8_t *in_pkt, unsigned in_pkt_len, - const pj_stun_msg *msg, + pj_stun_msg *msg, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { + pj_stun_rx_data rdata; pj_status_t status; + /* Init rdata */ + rdata.msg = msg; + pj_bzero(&rdata.info, sizeof(rdata.info)); + + if (sess->auth_type == PJ_STUN_AUTH_NONE) + options |= PJ_STUN_NO_AUTHENTICATE; + /* 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); + &rdata, tmp_pool, src_addr, src_addr_len); if (status != PJ_SUCCESS) { return status; } @@ -995,14 +1110,16 @@ static pj_status_t on_incoming_request(pj_stun_session *sess, /* 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, + status = (*sess->cb.on_rx_request)(sess, in_pkt, in_pkt_len, &rdata, src_addr, src_addr_len); } else { + pj_str_t err_text; pj_stun_msg *response; + err_text = pj_str("Callback is not set to handle request"); status = pj_stun_msg_create_response(tmp_pool, msg, - PJ_STUN_SC_BAD_REQUEST, NULL, - &response); + PJ_STUN_SC_BAD_REQUEST, + &err_text, &response); if (status == PJ_SUCCESS && response) { status = send_response(sess, tmp_pool, response, NULL, PJ_FALSE, src_addr, src_addr_len); diff --git a/pjnath/src/pjnath/turn_session.c b/pjnath/src/pjnath/turn_session.c index 7b772dcf..e5540b00 100644 --- a/pjnath/src/pjnath/turn_session.c +++ b/pjnath/src/pjnath/turn_session.c @@ -521,7 +521,7 @@ PJ_DEF(pj_status_t) pj_turn_session_set_cred(pj_turn_session *sess, pj_lock_acquire(sess->lock); - pj_stun_session_set_credential(sess->stun, cred); + pj_stun_session_set_credential(sess->stun, PJ_STUN_AUTH_LONG_TERM, cred); pj_lock_release(sess->lock); diff --git a/pjnath/src/pjturn-srv/allocation.c b/pjnath/src/pjturn-srv/allocation.c index da35266c..14ed228b 100644 --- a/pjnath/src/pjturn-srv/allocation.c +++ b/pjnath/src/pjturn-srv/allocation.c @@ -72,7 +72,7 @@ static pj_status_t stun_on_send_msg(pj_stun_session *sess, static pj_status_t stun_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len); static pj_status_t stun_on_rx_indication(pj_stun_session *sess, @@ -97,10 +97,11 @@ static void alloc_err(pj_turn_allocation *alloc, const char *title, /* Parse ALLOCATE request */ static pj_status_t parse_allocate_req(alloc_request *cfg, pj_stun_session *sess, - const pj_stun_msg *req, + const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { + const pj_stun_msg *req = rdata->msg; pj_stun_bandwidth_attr *attr_bw; pj_stun_req_transport_attr *attr_req_tp; pj_stun_res_token_attr *attr_res_token; @@ -120,7 +121,8 @@ static pj_status_t parse_allocate_req(alloc_request *cfg, /* Check if we can satisfy the bandwidth */ if (cfg->bandwidth > MAX_CLIENT_BANDWIDTH) { - pj_stun_session_respond(sess, req, PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, + pj_stun_session_respond(sess, rdata, + PJ_STUN_SC_ALLOCATION_QUOTA_REACHED, "Invalid bandwidth", PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_ALLOCATION_QUOTA_REACHED); @@ -130,7 +132,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg, attr_req_tp = (pj_stun_uint_attr*) pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_TRANSPORT, 0); if (attr_req_tp == NULL) { - pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "Missing REQUESTED-TRANSPORT attribute", PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); @@ -140,7 +142,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg, /* Can only support UDP for now */ if (cfg->tp_type != PJ_TURN_TP_UDP) { - pj_stun_session_respond(sess, req, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, NULL, PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO); } @@ -151,7 +153,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg, 0); if (attr_res_token) { /* We don't support RESERVATION-TOKEN for now */ - pj_stun_session_respond(sess, req, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "RESERVATION-TOKEN is not supported", PJ_TRUE, src_addr, src_addr_len); @@ -163,7 +165,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg, pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REQ_PROPS, 0); if (attr_rpp) { /* We don't support REQUESTED-PROPS for now */ - pj_stun_session_respond(sess, req, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "REQUESTED-PROPS is not supported", PJ_TRUE, src_addr, src_addr_len); @@ -176,7 +178,7 @@ static pj_status_t parse_allocate_req(alloc_request *cfg, if (attr_lifetime) { cfg->lifetime = attr_lifetime->value; if (cfg->lifetime < MIN_LIFETIME) { - pj_stun_session_respond(sess, req, PJ_STUN_SC_BAD_REQUEST, + pj_stun_session_respond(sess, rdata, PJ_STUN_SC_BAD_REQUEST, "LIFETIME too short", PJ_TRUE, src_addr, src_addr_len); return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST); @@ -194,13 +196,13 @@ static pj_status_t parse_allocate_req(alloc_request *cfg, /* Respond to ALLOCATE request */ static pj_status_t send_allocate_response(pj_turn_allocation *alloc, pj_stun_session *srv_sess, - const pj_stun_msg *msg) + const pj_stun_rx_data *rdata) { pj_stun_tx_data *tdata; pj_status_t status; /* Respond the original ALLOCATE request */ - status = pj_stun_session_create_res(srv_sess, msg, 0, NULL, &tdata); + status = pj_stun_session_create_res(srv_sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) return status; @@ -284,11 +286,12 @@ static pj_status_t init_cred(pj_turn_allocation *alloc, const pj_stun_msg *req) PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener, const pj_sockaddr_t *src_addr, unsigned src_addr_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc) { pj_turn_srv *srv = listener->server; + const pj_stun_msg *msg = rdata->msg; pj_pool_t *pool; alloc_request req; pj_turn_allocation *alloc; @@ -297,7 +300,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener, pj_status_t status; /* Parse ALLOCATE request */ - status = parse_allocate_req(&req, srv_sess, msg, src_addr, src_addr_len); + status = parse_allocate_req(&req, srv_sess, rdata, src_addr, src_addr_len); if (status != PJ_SUCCESS) return status; @@ -354,7 +357,8 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener, } /* Attach authentication credential to STUN session */ - pj_stun_session_set_credential(alloc->sess, &alloc->cred); + pj_stun_session_set_credential(alloc->sess, PJ_STUN_AUTH_LONG_TERM, + &alloc->cred); /* Create the relay resource */ status = create_relay(srv, alloc, msg, &req, &alloc->relay); @@ -366,7 +370,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener, pj_turn_srv_register_allocation(srv, alloc); /* Respond to ALLOCATE request */ - status = send_allocate_response(alloc, srv_sess, msg); + status = send_allocate_response(alloc, srv_sess, rdata); if (status != PJ_SUCCESS) goto on_error; @@ -383,7 +387,7 @@ PJ_DEF(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener, on_error: /* Send reply to the ALLOCATE request */ pj_strerror(status, str_tmp, sizeof(str_tmp)); - pj_stun_session_respond(srv_sess, msg, PJ_STUN_SC_BAD_REQUEST, str_tmp, + pj_stun_session_respond(srv_sess, rdata, PJ_STUN_SC_BAD_REQUEST, str_tmp, PJ_TRUE, src_addr, src_addr_len); /* Cleanup */ @@ -709,13 +713,13 @@ static pj_status_t create_relay(pj_turn_srv *srv, /* Create and send error response */ static void send_reply_err(pj_turn_allocation *alloc, - const pj_stun_msg *req, + const pj_stun_rx_data *rdata, pj_bool_t cache, int code, const char *errmsg) { pj_status_t status; - status = pj_stun_session_respond(alloc->sess, req, code, errmsg, cache, + status = pj_stun_session_respond(alloc->sess, rdata, code, errmsg, cache, &alloc->hkey.clt_addr, pj_sockaddr_get_len(&alloc->hkey.clt_addr.addr)); if (status != PJ_SUCCESS) { @@ -726,13 +730,13 @@ static void send_reply_err(pj_turn_allocation *alloc, /* Create and send successful response */ static void send_reply_ok(pj_turn_allocation *alloc, - const pj_stun_msg *req) + const pj_stun_rx_data *rdata) { pj_status_t status; unsigned interval; pj_stun_tx_data *tdata; - status = pj_stun_session_create_res(alloc->sess, req, 0, NULL, &tdata); + status = pj_stun_session_create_res(alloc->sess, rdata, 0, NULL, &tdata); if (status != PJ_SUCCESS) { alloc_err(alloc, "Error creating STUN success response", status); return; @@ -1072,10 +1076,11 @@ static pj_status_t stun_on_send_msg(pj_stun_session *sess, static pj_status_t stun_on_rx_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { + const pj_stun_msg *msg = rdata->msg; pj_turn_allocation *alloc; PJ_UNUSED_ARG(pkt); @@ -1088,7 +1093,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess, /* Refuse to serve any request if we've been shutdown */ if (alloc->relay.lifetime == 0) { /* Reject with 437 if we're shutting down */ - send_reply_err(alloc, msg, PJ_TRUE, + send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); return PJ_SUCCESS; } @@ -1115,7 +1120,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess, alloc->relay.lifetime = 0; /* Respond first */ - send_reply_ok(alloc, msg); + send_reply_ok(alloc, rdata); /* Shutdown allocation */ PJ_LOG(4,(alloc->obj_name, @@ -1141,7 +1146,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess, resched_timeout(alloc); /* Send reply */ - send_reply_ok(alloc, msg); + send_reply_ok(alloc, rdata); } } else if (msg->hdr.type == PJ_STUN_CHANNEL_BIND_REQUEST) { @@ -1158,7 +1163,8 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess, pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_PEER_ADDR, 0); if (!ch_attr || !peer_attr) { - send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); + send_reply_err(alloc, rdata, PJ_TRUE, + PJ_STUN_SC_BAD_REQUEST, NULL); return PJ_SUCCESS; } @@ -1171,7 +1177,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess, if (p1) { if (pj_sockaddr_cmp(&p1->hkey.peer_addr, &peer_attr->sockaddr)) { /* Address mismatch. Send 400 */ - send_reply_err(alloc, msg, PJ_TRUE, + send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address mismatch"); return PJ_SUCCESS; @@ -1190,7 +1196,7 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess, p2 = lookup_permission_by_addr(alloc, &peer_attr->sockaddr, pj_sockaddr_get_len(&peer_attr->sockaddr)); if (p2 && p2->channel != PJ_TURN_INVALID_CHANNEL) { - send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, + send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, "Peer address already assigned a channel number"); return PJ_SUCCESS; } @@ -1210,19 +1216,20 @@ static pj_status_t stun_on_rx_request(pj_stun_session *sess, refresh_permission(p2); /* Reply */ - send_reply_ok(alloc, msg); + send_reply_ok(alloc, rdata); return PJ_SUCCESS; } else if (msg->hdr.type == PJ_STUN_ALLOCATE_REQUEST) { /* Respond with 437 (section 6.3 turn-07) */ - send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL); + send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_ALLOCATION_MISMATCH, + NULL); } else { /* Respond with Bad Request? */ - send_reply_err(alloc, msg, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); + send_reply_err(alloc, rdata, PJ_TRUE, PJ_STUN_SC_BAD_REQUEST, NULL); } diff --git a/pjnath/src/pjturn-srv/auth.c b/pjnath/src/pjturn-srv/auth.c index ad930251..3071221c 100644 --- a/pjnath/src/pjturn-srv/auth.c +++ b/pjnath/src/pjturn-srv/auth.c @@ -58,29 +58,6 @@ PJ_DEF(void) pj_turn_auth_dinit(void) } -PJ_DEF(pj_status_t) pj_turn_get_cred( const pj_stun_msg *msg, - void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *username, - pj_str_t *nonce, - int *data_type, - pj_str_t *data) -{ - PJ_UNUSED_ARG(msg); - PJ_UNUSED_ARG(pool); - PJ_UNUSED_ARG(user_data); - - *realm = pj_str(g_realm); - *username = pj_str(g_cred[0].username); - *nonce = pj_str(THE_NONCE); - *data_type = 0; - *data = pj_str(g_cred[0].passwd); - - return PJ_SUCCESS; -} - - /* * This function is called by pj_stun_verify_credential() when * server needs to challenge the request with 401 response. diff --git a/pjnath/src/pjturn-srv/auth.h b/pjnath/src/pjturn-srv/auth.h index d0b5a0ec..db928051 100644 --- a/pjnath/src/pjturn-srv/auth.h +++ b/pjnath/src/pjturn-srv/auth.h @@ -61,18 +61,6 @@ PJ_DECL(pj_status_t) pj_turn_get_auth(void *user_data, pj_str_t *nonce); /** - * Get credential. - */ -PJ_DECL(pj_status_t) pj_turn_get_cred(const pj_stun_msg *msg, - void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *username, - pj_str_t *nonce, - int *data_type, - pj_str_t *data); - -/** * This function is called to get the password for the specified username. * This function is also used to check whether the username is valid. * diff --git a/pjnath/src/pjturn-srv/server.c b/pjnath/src/pjturn-srv/server.c index 11180dbf..b22cc53b 100644 --- a/pjnath/src/pjturn-srv/server.c +++ b/pjnath/src/pjturn-srv/server.c @@ -40,7 +40,7 @@ static pj_status_t on_tx_stun_msg( pj_stun_session *sess, static pj_status_t on_rx_stun_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len); @@ -147,7 +147,6 @@ PJ_DEF(pj_status_t) pj_turn_srv_create(pj_pool_factory *pf, srv->core.cred.type = PJ_STUN_AUTH_CRED_DYNAMIC; srv->core.cred.data.dyn_cred.user_data = srv; srv->core.cred.data.dyn_cred.get_auth = &pj_turn_get_auth; - srv->core.cred.data.dyn_cred.get_cred = &pj_turn_get_cred; srv->core.cred.data.dyn_cred.get_password = &pj_turn_get_password; srv->core.cred.data.dyn_cred.verify_nonce = &pj_turn_verify_nonce; @@ -368,7 +367,8 @@ PJ_DEF(pj_status_t) pj_turn_srv_add_listener(pj_turn_srv *srv, } pj_stun_session_set_user_data(sess, lis); - pj_stun_session_set_credential(sess, &srv->core.cred); + pj_stun_session_set_credential(sess, PJ_STUN_AUTH_LONG_TERM, + &srv->core.cred); srv->core.stun_sess[index] = sess; lis->id = index; @@ -483,9 +483,8 @@ static pj_status_t on_tx_stun_msg( pj_stun_session *sess, /* Respond to STUN request */ -static pj_status_t stun_respond(pj_turn_srv *srv, - pj_stun_session *sess, - const pj_stun_msg *req, +static pj_status_t stun_respond(pj_stun_session *sess, + const pj_stun_rx_data *rdata, unsigned code, const char *errmsg, pj_bool_t cache, @@ -497,114 +496,17 @@ static pj_status_t stun_respond(pj_turn_srv *srv, pj_stun_tx_data *tdata; /* Create response */ - status = pj_stun_session_create_res(sess, req, code, + status = pj_stun_session_create_res(sess, rdata, code, (errmsg?pj_cstr(&reason,errmsg):NULL), &tdata); if (status != PJ_SUCCESS) return status; - /* Store the credential for future lookup. */ - if (pj_stun_auth_valid_for_msg(tdata->msg)) { - pj_turn_srv_put_cred(srv, req, tdata); - } - /* Send the response */ return pj_stun_session_send_msg(sess, cache, dst_addr, addr_len, tdata); } -/* - * Store the credential to put placed for the specified message for - * future retrieval. - */ -PJ_DEF(pj_status_t) pj_turn_srv_put_cred(pj_turn_srv *srv, - const pj_stun_msg *req, - pj_stun_tx_data *response) -{ - pj_stun_username_attr *user; - pj_stun_realm_attr *realm; - pj_stun_nonce_attr *nonce; - struct saved_cred *saved_cred; - pj_status_t status; - - realm = (pj_stun_realm_attr*) - pj_stun_msg_find_attr(req, PJ_STUN_ATTR_REALM, 0); - PJ_ASSERT_RETURN(realm != NULL, PJ_EBUG); - - user = (pj_stun_username_attr*) - pj_stun_msg_find_attr(req, PJ_STUN_ATTR_USERNAME, 0); - PJ_ASSERT_RETURN(user != NULL, PJ_EBUG); - - nonce = (pj_stun_nonce_attr*) - pj_stun_msg_find_attr(req, PJ_STUN_ATTR_NONCE, 0); - PJ_ASSERT_RETURN(nonce != NULL, PJ_EBUG); - - saved_cred = PJ_POOL_ALLOC_T(response->pool, struct saved_cred); - - /* Lookup the password */ - status = pj_turn_get_password(response->msg, NULL, &realm->value, - &user->value, response->pool, - &saved_cred->data_type, - &saved_cred->data); - if (status != PJ_SUCCESS) - return status; - - /* Store credential */ - pj_strdup(response->pool, &saved_cred->username, &user->value); - pj_strdup(response->pool, &saved_cred->realm, &realm->value); - pj_strdup(response->pool, &saved_cred->nonce, &nonce->value); - - pj_thread_local_set(srv->core.tls_key, response->msg); - pj_thread_local_set(srv->core.tls_data, saved_cred); - - return PJ_SUCCESS; -} - - -/** - * Retrieve previously stored credential for the specified message. - */ -PJ_DEF(pj_status_t) pj_turn_srv_get_cred(const pj_stun_msg *msg, - void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *username, - pj_str_t *nonce, - int *data_type, - pj_str_t *data) -{ - pj_turn_srv *srv; - const pj_stun_msg *saved_msg; - struct saved_cred *saved_cred; - - PJ_UNUSED_ARG(pool); - - srv = (pj_turn_srv*)user_data; - - /* Lookup stored message and make sure it's for the same message */ - saved_msg = (const pj_stun_msg*) - pj_thread_local_get(srv->core.tls_key); - PJ_ASSERT_RETURN(saved_msg==msg, PJ_ENOTFOUND); - - /* Lookup saved credential */ - saved_cred = (struct saved_cred*) - pj_thread_local_get(srv->core.tls_data); - PJ_ASSERT_RETURN(saved_cred != NULL, PJ_ENOTFOUND); - - - *realm = saved_cred->realm; - *username = saved_cred->username; - *nonce = saved_cred->nonce; - *data_type = saved_cred->data_type; - *data = saved_cred->data; - - - /* Don't clear saved_cred as this may be called more than once */ - - return PJ_SUCCESS; -} - - /* Callback from our own STUN session when incoming request arrives. * This function is triggered by pj_stun_session_on_rx_pkt() call in * pj_turn_srv_on_rx_pkt() function below. @@ -612,11 +514,12 @@ PJ_DEF(pj_status_t) pj_turn_srv_get_cred(const pj_stun_msg *msg, static pj_status_t on_rx_stun_request(pj_stun_session *sess, const pj_uint8_t *pkt, unsigned pkt_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { pj_turn_listener *listener; + const pj_stun_msg *msg = rdata->msg; pj_turn_srv *srv; pj_turn_allocation *alloc; pj_status_t status; @@ -629,7 +532,7 @@ static pj_status_t on_rx_stun_request(pj_stun_session *sess, /* Respond any requests other than ALLOCATE with 437 response */ if (msg->hdr.type != PJ_STUN_ALLOCATE_REQUEST) { - stun_respond(srv, sess, msg, PJ_STUN_SC_ALLOCATION_MISMATCH, + stun_respond(sess, rdata, PJ_STUN_SC_ALLOCATION_MISMATCH, NULL, PJ_FALSE, src_addr, src_addr_len); return PJ_SUCCESS; } @@ -638,7 +541,7 @@ static pj_status_t on_rx_stun_request(pj_stun_session *sess, * in this function. */ status = pj_turn_allocation_create(listener, src_addr, src_addr_len, - msg, sess, &alloc); + rdata, sess, &alloc); if (status != PJ_SUCCESS) { /* STUN response has been sent, no need to reply here */ return PJ_SUCCESS; diff --git a/pjnath/src/pjturn-srv/turn.h b/pjnath/src/pjturn-srv/turn.h index 871db425..2eb99254 100644 --- a/pjnath/src/pjturn-srv/turn.h +++ b/pjnath/src/pjturn-srv/turn.h @@ -204,7 +204,7 @@ struct pj_turn_permission PJ_DECL(pj_status_t) pj_turn_allocation_create(pj_turn_listener *listener, const pj_sockaddr_t *src_addr, unsigned src_addr_len, - const pj_stun_msg *msg, + const pj_stun_rx_data *rdata, pj_stun_session *srv_sess, pj_turn_allocation **p_alloc); /** @@ -459,26 +459,5 @@ PJ_DECL(void) pj_turn_srv_on_rx_pkt(pj_turn_srv *srv, pj_turn_pkt *pkt); -/** - * Store the credential to put placed for the specified message for - * future retrieval. - */ -PJ_DECL(pj_status_t) pj_turn_srv_put_cred(pj_turn_srv *srv, - const pj_stun_msg *request, - pj_stun_tx_data *response); - -/** - * Retrieve previously stored credential for the specified message. - */ -PJ_DECL(pj_status_t) pj_turn_srv_get_cred(const pj_stun_msg *msg, - void *user_data, - pj_pool_t *pool, - pj_str_t *realm, - pj_str_t *username, - pj_str_t *nonce, - int *data_type, - pj_str_t *data); - - #endif /* __PJ_TURN_SRV_TURN_H__ */ |