/* $Id: sess_auth.c 4360 2013-02-21 11:26:35Z bennylp $ */ /* * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com) * Copyright (C) 2003-2008 Benny Prijono * * 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, void *token, 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); PJ_UNUSED_ARG(token); 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, void *token, const pj_sockaddr_t *src_addr, unsigned src_addr_len) { PJ_UNUSED_ARG(pkt); PJ_UNUSED_ARG(pkt_len); PJ_UNUSED_ARG(token); return pj_stun_session_respond(sess, rdata, 0, NULL, 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); PJ_LOG(5,("", " server thread started")); 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+1, &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, 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, NULL, &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, void *token, 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); PJ_UNUSED_ARG(token); 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, void *token, 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(token); 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+1, &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, 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_bool_t expected_error, pj_status_t 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, NULL, &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, NULL, PJ_FALSE, PJ_TRUE, &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) { PJ_LOG(3,("", " expecting %d retransmissions, got %d", PJ_STUN_MAX_TRANSMIT_COUNT, server->recv_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 */ #if 0 // STUN session now will just use the realm sent in the // response, so this test will fail because it will // authenticate successfully. 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; } #endif /* 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; }