blob: 7aee423b2e9a199358e11749da8b427160b619bd [file] [log] [blame]
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001/* $Id$ */
2/*
3 * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19#include "test.h"
20
21#define THIS_FILE "sess_auth.c"
22
23#define REALM "STUN session test"
24#define USERNAME "theusername"
25#define PASSWORD "thepassword"
26#define NONCE "thenonce"
27
28
29/* STUN config */
30static pj_stun_config stun_cfg;
31
32
33//////////////////////////////////////////////////////////////////////////////////////////
34//
35// SERVER PART
36//
37
38
39/* Server instance */
40static struct server
41{
42 pj_pool_t *pool;
43 pj_sockaddr addr;
44 pj_stun_session *sess;
45
46 pj_bool_t responding;
47 unsigned recv_count;
48 pj_stun_auth_type auth_type;
49
50 pj_sock_t sock;
51
52 pj_bool_t quit;
53 pj_thread_t *thread;
54} *server;
55
56
57static pj_status_t server_send_msg(pj_stun_session *sess,
Benny Prijono84fde9e2008-04-09 13:34:49 +000058 void *token,
Benny Prijonoa5d214f2008-03-19 23:00:30 +000059 const void *pkt,
60 pj_size_t pkt_size,
61 const pj_sockaddr_t *dst_addr,
62 unsigned addr_len)
63{
64 pj_ssize_t len = pkt_size;
65
66 PJ_UNUSED_ARG(sess);
Benny Prijono84fde9e2008-04-09 13:34:49 +000067 PJ_UNUSED_ARG(token);
Benny Prijonoa5d214f2008-03-19 23:00:30 +000068
69 return pj_sock_sendto(server->sock, pkt, &len, 0, dst_addr, addr_len);
70}
71
72static pj_status_t server_on_rx_request(pj_stun_session *sess,
73 const pj_uint8_t *pkt,
74 unsigned pkt_len,
75 const pj_stun_rx_data *rdata,
Benny Prijono84fde9e2008-04-09 13:34:49 +000076 void *token,
Benny Prijonoa5d214f2008-03-19 23:00:30 +000077 const pj_sockaddr_t *src_addr,
78 unsigned src_addr_len)
79{
80 PJ_UNUSED_ARG(pkt);
81 PJ_UNUSED_ARG(pkt_len);
Benny Prijono84fde9e2008-04-09 13:34:49 +000082 PJ_UNUSED_ARG(token);
Benny Prijonoa5d214f2008-03-19 23:00:30 +000083
Benny Prijono84fde9e2008-04-09 13:34:49 +000084 return pj_stun_session_respond(sess, rdata, 0, NULL, NULL, PJ_TRUE,
Benny Prijonoa5d214f2008-03-19 23:00:30 +000085 src_addr, src_addr_len);
86}
87
88
89static pj_status_t server_get_auth(void *user_data,
90 pj_pool_t *pool,
91 pj_str_t *realm,
92 pj_str_t *nonce)
93{
94 PJ_UNUSED_ARG(user_data);
95 PJ_UNUSED_ARG(pool);
96
97 if (server->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
98 realm->slen = nonce->slen = 0;
99 } else {
100 *realm = pj_str(REALM);
101 *nonce = pj_str(NONCE);
102 }
103
104 return PJ_SUCCESS;
105}
106
107
108static pj_status_t server_get_password( const pj_stun_msg *msg,
109 void *user_data,
110 const pj_str_t *realm,
111 const pj_str_t *username,
112 pj_pool_t *pool,
113 pj_stun_passwd_type *data_type,
114 pj_str_t *data)
115{
116 PJ_UNUSED_ARG(msg);
117 PJ_UNUSED_ARG(user_data);
118 PJ_UNUSED_ARG(pool);
119
120 if (server->auth_type == PJ_STUN_AUTH_SHORT_TERM) {
121 if (realm && realm->slen) {
122 PJ_LOG(4,(THIS_FILE, " server expecting short term"));
123 return -1;
124 }
125 } else {
126 if (realm==NULL || realm->slen==0) {
127 PJ_LOG(4,(THIS_FILE, " realm not present"));
128 return -1;
129 }
130 }
131
132 if (pj_strcmp2(username, USERNAME) != 0) {
133 PJ_LOG(4,(THIS_FILE, " wrong username"));
134 return -1;
135 }
136
137 *data_type = PJ_STUN_PASSWD_PLAIN;
138 *data = pj_str(PASSWORD);
139
140 return PJ_SUCCESS;
141}
142
143
144static pj_bool_t server_verify_nonce(const pj_stun_msg *msg,
145 void *user_data,
146 const pj_str_t *realm,
147 const pj_str_t *username,
148 const pj_str_t *nonce)
149{
150 PJ_UNUSED_ARG(msg);
151 PJ_UNUSED_ARG(user_data);
152 PJ_UNUSED_ARG(realm);
153 PJ_UNUSED_ARG(username);
154
155 if (pj_strcmp2(nonce, NONCE) != 0)
156 return PJ_FALSE;
157
158 return PJ_TRUE;
159}
160
161
162static int server_thread(void *unused)
163{
164 PJ_UNUSED_ARG(unused);
165
Benny Prijono24a21852008-04-14 04:04:30 +0000166 PJ_LOG(5,("", " server thread started"));
167
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000168 while (!server->quit) {
169 pj_fd_set_t readset;
170 pj_time_val delay = {0, 10};
171
172 PJ_FD_ZERO(&readset);
173 PJ_FD_SET(server->sock, &readset);
174
Benny Prijono24a21852008-04-14 04:04:30 +0000175 if (pj_sock_select(server->sock+1, &readset, NULL, NULL, &delay)==1 &&
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000176 PJ_FD_ISSET(server->sock, &readset))
177 {
178 char pkt[1000];
179 pj_ssize_t len;
180 pj_status_t status;
181 pj_sockaddr src_addr;
182 int src_addr_len;
183
184 len = sizeof(pkt);
185 src_addr_len = sizeof(src_addr);
186
187 status = pj_sock_recvfrom(server->sock, pkt, &len, 0, &src_addr, &src_addr_len);
188 if (status != PJ_SUCCESS)
189 continue;
190
191 /* Increment server's receive count */
192 server->recv_count++;
193
194 /* Only pass to server if we allow to respond */
195 if (!server->responding)
196 continue;
197
198 pj_stun_session_on_rx_pkt(server->sess, pkt, len,
199 PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM,
Benny Prijono84fde9e2008-04-09 13:34:49 +0000200 NULL, NULL, &src_addr, src_addr_len);
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000201 }
202 }
203
204 return 0;
205}
206
207
208/* Destroy server */
209static void destroy_server(void)
210{
211 if (server->thread) {
212 server->quit = PJ_TRUE;
213 pj_thread_join(server->thread);
214 pj_thread_destroy(server->thread);
215 }
216
217 if (server->sock) {
218 pj_sock_close(server->sock);
219 }
220
221 if (server->sess) {
222 pj_stun_session_destroy(server->sess);
223 }
224
225 pj_pool_release(server->pool);
226 server = NULL;
227}
228
229/* Instantiate standard server */
230static int create_std_server(pj_stun_auth_type auth_type,
231 pj_bool_t responding)
232{
233 pj_pool_t *pool;
234 pj_stun_session_cb sess_cb;
235 pj_stun_auth_cred cred;
236 pj_status_t status;
237
238 /* Create server */
239 pool = pj_pool_create(mem, "server", 1000, 1000, NULL);
240 server = PJ_POOL_ZALLOC_T(pool, struct server);
241 server->pool = pool;
242 server->auth_type = auth_type;
243 server->responding = responding;
244
245 /* Create STUN session */
246 pj_bzero(&sess_cb, sizeof(sess_cb));
247 sess_cb.on_rx_request = &server_on_rx_request;
248 sess_cb.on_send_msg = &server_send_msg;
249 status = pj_stun_session_create(&stun_cfg, "server", &sess_cb, PJ_FALSE, &server->sess);
250 if (status != PJ_SUCCESS) {
251 destroy_server();
252 return -10;
253 }
254
255 /* Configure credential */
256 pj_bzero(&cred, sizeof(cred));
257 cred.type = PJ_STUN_AUTH_CRED_DYNAMIC;
258 cred.data.dyn_cred.get_auth = &server_get_auth;
259 cred.data.dyn_cred.get_password = &server_get_password;
260 cred.data.dyn_cred.verify_nonce = &server_verify_nonce;
261 status = pj_stun_session_set_credential(server->sess, auth_type, &cred);
262 if (status != PJ_SUCCESS) {
263 destroy_server();
264 return -20;
265 }
266
267 /* Create socket */
268 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &server->sock);
269 if (status != PJ_SUCCESS) {
270 destroy_server();
271 return -30;
272 }
273
274 /* Bind */
275 pj_sockaddr_in_init(&server->addr.ipv4, NULL, 0);
276 status = pj_sock_bind(server->sock, &server->addr, pj_sockaddr_get_len(&server->addr));
277 if (status != PJ_SUCCESS) {
278 destroy_server();
279 return -40;
280 } else {
281 /* Get the bound IP address */
282 int namelen = sizeof(server->addr);
283 pj_sockaddr addr;
284
285 status = pj_sock_getsockname(server->sock, &server->addr, &namelen);
286 if (status != PJ_SUCCESS) {
287 destroy_server();
288 return -43;
289 }
290
291 status = pj_gethostip(pj_AF_INET(), &addr);
292 if (status != PJ_SUCCESS) {
293 destroy_server();
294 return -45;
295 }
296
297 pj_sockaddr_copy_addr(&server->addr, &addr);
298 }
299
300
301 /* Create worker thread */
302 status = pj_thread_create(pool, "server", &server_thread, 0, 0, 0, &server->thread);
303 if (status != PJ_SUCCESS) {
304 destroy_server();
305 return -30;
306 }
307
308 return 0;
309}
310
311
312//////////////////////////////////////////////////////////////////////////////////////////
313//
314// CLIENT PART
315//
316
317static struct client
318{
319 pj_pool_t *pool;
320 pj_stun_session *sess;
321 pj_sem_t *test_complete;
322 pj_sock_t sock;
323
324 pj_bool_t responding;
325 unsigned recv_count;
326
327 pj_status_t response_status;
328 pj_stun_msg *response;
329
330 pj_bool_t quit;
331 pj_thread_t *thread;
332} *client;
333
334
335static pj_status_t client_send_msg(pj_stun_session *sess,
Benny Prijono84fde9e2008-04-09 13:34:49 +0000336 void *token,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000337 const void *pkt,
338 pj_size_t pkt_size,
339 const pj_sockaddr_t *dst_addr,
340 unsigned addr_len)
341{
342 pj_ssize_t len = pkt_size;
343
344 PJ_UNUSED_ARG(sess);
Benny Prijono84fde9e2008-04-09 13:34:49 +0000345 PJ_UNUSED_ARG(token);
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000346
347 return pj_sock_sendto(client->sock, pkt, &len, 0, dst_addr, addr_len);
348}
349
350
351static void client_on_request_complete( pj_stun_session *sess,
352 pj_status_t status,
Benny Prijono84fde9e2008-04-09 13:34:49 +0000353 void *token,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000354 pj_stun_tx_data *tdata,
355 const pj_stun_msg *response,
356 const pj_sockaddr_t *src_addr,
357 unsigned src_addr_len)
358{
359 PJ_UNUSED_ARG(sess);
Benny Prijono84fde9e2008-04-09 13:34:49 +0000360 PJ_UNUSED_ARG(token);
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000361 PJ_UNUSED_ARG(tdata);
362 PJ_UNUSED_ARG(src_addr);
363 PJ_UNUSED_ARG(src_addr_len);
364
365 client->response_status = status;
366 if (response)
367 client->response = pj_stun_msg_clone(client->pool, response);
368
369 pj_sem_post(client->test_complete);
370}
371
372
373static int client_thread(void *unused)
374{
375 PJ_UNUSED_ARG(unused);
376
377 while (!client->quit) {
378 pj_fd_set_t readset;
379 pj_time_val delay = {0, 10};
380
381 /* Also poll the timer heap */
382 pj_timer_heap_poll(stun_cfg.timer_heap, NULL);
383
384 /* Poll client socket */
385 PJ_FD_ZERO(&readset);
386 PJ_FD_SET(client->sock, &readset);
387
Benny Prijono24a21852008-04-14 04:04:30 +0000388 if (pj_sock_select(client->sock+1, &readset, NULL, NULL, &delay)==1 &&
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000389 PJ_FD_ISSET(client->sock, &readset))
390 {
391 char pkt[1000];
392 pj_ssize_t len;
393 pj_status_t status;
394 pj_sockaddr src_addr;
395 int src_addr_len;
396
397 len = sizeof(pkt);
398 src_addr_len = sizeof(src_addr);
399
400 status = pj_sock_recvfrom(client->sock, pkt, &len, 0, &src_addr, &src_addr_len);
401 if (status != PJ_SUCCESS)
402 continue;
403
404 /* Increment client's receive count */
405 client->recv_count++;
406
407 /* Only pass to client if we allow to respond */
408 if (!client->responding)
409 continue;
410
411 pj_stun_session_on_rx_pkt(client->sess, pkt, len,
412 PJ_STUN_CHECK_PACKET | PJ_STUN_IS_DATAGRAM,
Benny Prijono84fde9e2008-04-09 13:34:49 +0000413 NULL, NULL, &src_addr, src_addr_len);
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000414 }
415
416 }
417
418 return 0;
419}
420
421
422static void destroy_client_server(void)
423{
424 if (client->thread) {
425 client->quit = 1;
426 pj_thread_join(client->thread);
427 pj_thread_destroy(client->thread);
428 }
429
430 if (client->sess)
431 pj_stun_session_destroy(client->sess);
432
433 if (client->sock)
434 pj_sock_close(client->sock);
435
436 if (client->test_complete)
437 pj_sem_destroy(client->test_complete);
438
439 if (server)
440 destroy_server();
441}
442
443static int run_client_test(const char *title,
444
445 pj_bool_t server_responding,
446 pj_stun_auth_type server_auth_type,
447
448 pj_stun_auth_type client_auth_type,
449 const char *realm,
450 const char *username,
451 const char *nonce,
452 const char *password,
453 pj_bool_t dummy_mi,
454
Benny Prijono24a21852008-04-14 04:04:30 +0000455 pj_bool_t expected_error,
456 pj_status_t expected_code,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000457 const char *expected_realm,
458 const char *expected_nonce,
459
460 int (*more_check)(void))
461{
462 pj_pool_t *pool;
463 pj_stun_session_cb sess_cb;
464 pj_stun_auth_cred cred;
465 pj_stun_tx_data *tdata;
466 pj_status_t status;
467 int rc = 0;
468
469 PJ_LOG(3,(THIS_FILE, " %s test", title));
470
471 /* Create client */
472 pool = pj_pool_create(mem, "client", 1000, 1000, NULL);
473 client = PJ_POOL_ZALLOC_T(pool, struct client);
474 client->pool = pool;
475 client->responding = PJ_TRUE;
476
477 /* Create STUN session */
478 pj_bzero(&sess_cb, sizeof(sess_cb));
479 sess_cb.on_request_complete = &client_on_request_complete;
480 sess_cb.on_send_msg = &client_send_msg;
481 status = pj_stun_session_create(&stun_cfg, "client", &sess_cb, PJ_FALSE, &client->sess);
482 if (status != PJ_SUCCESS) {
483 destroy_client_server();
484 return -200;
485 }
486
487 /* Create semaphore */
488 status = pj_sem_create(pool, "client", 0, 1, &client->test_complete);
489 if (status != PJ_SUCCESS) {
490 destroy_client_server();
491 return -205;
492 }
493
494 /* Create client socket */
495 status = pj_sock_socket(pj_AF_INET(), pj_SOCK_DGRAM(), 0, &client->sock);
496 if (status != PJ_SUCCESS) {
497 destroy_client_server();
498 return -210;
499 }
500
501 /* Bind client socket */
502 status = pj_sock_bind_in(client->sock, 0, 0);
503 if (status != PJ_SUCCESS) {
504 destroy_client_server();
505 return -220;
506 }
507
508 /* Create client thread */
509 status = pj_thread_create(pool, "client", &client_thread, NULL, 0, 0, &client->thread);
510 if (status != PJ_SUCCESS) {
511 destroy_client_server();
512 return -230;
513 }
514
515 /* Initialize credential */
516 pj_bzero(&cred, sizeof(cred));
517 cred.type = PJ_STUN_AUTH_CRED_STATIC;
518 if (realm) cred.data.static_cred.realm = pj_str((char*)realm);
519 if (username) cred.data.static_cred.username = pj_str((char*)username);
520 if (nonce) cred.data.static_cred.nonce = pj_str((char*)nonce);
521 if (password) cred.data.static_cred.data = pj_str((char*)password);
522 cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
523 status = pj_stun_session_set_credential(client->sess, client_auth_type, &cred);
524 if (status != PJ_SUCCESS) {
525 destroy_client_server();
526 return -240;
527 }
528
529 /* Create the server */
530 status = create_std_server(server_auth_type, server_responding);
531 if (status != 0) {
532 destroy_client_server();
533 return status;
534 }
535
536 /* Create request */
537 status = pj_stun_session_create_req(client->sess, PJ_STUN_BINDING_REQUEST,
538 PJ_STUN_MAGIC, NULL, &tdata);
539 if (status != PJ_SUCCESS) {
540 destroy_client_server();
541 return -250;
542 }
543
544 /* Add our own attributes if client authentication is set to none */
545 if (client_auth_type == PJ_STUN_AUTH_NONE) {
546 pj_str_t tmp;
547 if (realm)
548 pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_REALM, pj_cstr(&tmp, realm));
549 if (username)
550 pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_USERNAME, pj_cstr(&tmp, username));
551 if (nonce)
552 pj_stun_msg_add_string_attr(tdata->pool, tdata->msg, PJ_STUN_ATTR_NONCE, pj_cstr(&tmp, nonce));
553 if (password) {
554 // ignored
555 }
556 if (dummy_mi) {
557 pj_stun_msgint_attr *mi;
558
559 pj_stun_msgint_attr_create(tdata->pool, &mi);
560 pj_stun_msg_add_attr(tdata->msg, &mi->hdr);
561 }
562
563 }
564
565 /* Send the request */
Benny Prijono84fde9e2008-04-09 13:34:49 +0000566 status = pj_stun_session_send_msg(client->sess, NULL, PJ_FALSE, PJ_TRUE, &server->addr,
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000567 pj_sockaddr_get_len(&server->addr), tdata);
568 if (status != PJ_SUCCESS) {
569 destroy_client_server();
570 return -270;
571 }
572
573 /* Wait until test complete */
574 pj_sem_wait(client->test_complete);
575
576
577 /* Verify response */
578 if (expected_error) {
579 if (expected_code != client->response_status) {
580 char e1[PJ_ERR_MSG_SIZE], e2[PJ_ERR_MSG_SIZE];
581
582 pj_strerror(expected_code, e1, sizeof(e1));
583 pj_strerror(client->response_status, e2, sizeof(e2));
584
585 PJ_LOG(3,(THIS_FILE, " err: expecting %d (%s) but got %d (%s) response",
586 expected_code, e1, client->response_status, e2));
587 rc = -500;
588 }
589
590 } else {
591 int res_code = 0;
592 pj_stun_realm_attr *arealm;
593 pj_stun_nonce_attr *anonce;
594
595 if (client->response_status != 0) {
596 PJ_LOG(3,(THIS_FILE, " err: expecting successful operation but got error %d",
597 client->response_status));
598 rc = -600;
599 goto done;
600 }
601
602 if (PJ_STUN_IS_ERROR_RESPONSE(client->response->hdr.type)) {
603 pj_stun_errcode_attr *aerr = NULL;
604
605 aerr = (pj_stun_errcode_attr*)
606 pj_stun_msg_find_attr(client->response,
607 PJ_STUN_ATTR_ERROR_CODE, 0);
608 if (aerr == NULL) {
609 PJ_LOG(3,(THIS_FILE, " err: received error response without ERROR-CODE"));
610 rc = -610;
611 goto done;
612 }
613
614 res_code = aerr->err_code;
615 } else {
616 res_code = 0;
617 }
618
619 /* Check that code matches */
620 if (expected_code != res_code) {
621 PJ_LOG(3,(THIS_FILE, " err: expecting response code %d but got %d",
622 expected_code, res_code));
623 rc = -620;
624 goto done;
625 }
626
627 /* Find REALM and NONCE attributes */
628 arealm = (pj_stun_realm_attr*)
629 pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0);
630 anonce = (pj_stun_nonce_attr*)
631 pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0);
632
633 if (expected_realm) {
634 if (arealm == NULL) {
635 PJ_LOG(3,(THIS_FILE, " err: expecting REALM in esponse"));
636 rc = -630;
637 goto done;
638 }
639 if (pj_strcmp2(&arealm->value, expected_realm)!=0) {
640 PJ_LOG(3,(THIS_FILE, " err: REALM mismatch in response"));
641 rc = -640;
642 goto done;
643 }
644 } else {
645 if (arealm != NULL) {
646 PJ_LOG(3,(THIS_FILE, " err: non expecting REALM in response"));
647 rc = -650;
648 goto done;
649 }
650 }
651
652 if (expected_nonce) {
653 if (anonce == NULL) {
654 PJ_LOG(3,(THIS_FILE, " err: expecting NONCE in esponse"));
655 rc = -660;
656 goto done;
657 }
658 if (pj_strcmp2(&anonce->value, expected_nonce)!=0) {
659 PJ_LOG(3,(THIS_FILE, " err: NONCE mismatch in response"));
660 rc = -670;
661 goto done;
662 }
663 } else {
664 if (anonce != NULL) {
665 PJ_LOG(3,(THIS_FILE, " err: non expecting NONCE in response"));
666 rc = -680;
667 goto done;
668 }
669 }
670 }
671
672 /* Our tests are okay so far. Let caller do some more tests if
673 * it wants to.
674 */
675 if (rc==0 && more_check) {
676 rc = (*more_check)();
677 }
678
679
680done:
681 destroy_client_server();
682 return rc;
683}
684
685
686//////////////////////////////////////////////////////////////////////////////////////////
687//
688// More verification
689//
690
691/* Retransmission test */
692static int retransmit_check(void)
693{
Benny Prijono24a21852008-04-14 04:04:30 +0000694
695 if (server->recv_count != PJ_STUN_MAX_TRANSMIT_COUNT) {
696 PJ_LOG(3,("", " expecting %d retransmissions, got %d",
697 PJ_STUN_MAX_TRANSMIT_COUNT, server->recv_count));
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000698 return -700;
Benny Prijono24a21852008-04-14 04:04:30 +0000699 }
Benny Prijonoa5d214f2008-03-19 23:00:30 +0000700 if (client->recv_count != 0)
701 return -710;
702
703 return 0;
704}
705
706static int long_term_check1(void)
707{
708 /* SHOULD NOT contain USERNAME or MESSAGE-INTEGRITY */
709 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0))
710 return -800;
711 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0))
712 return -800;
713
714 return 0;
715}
716
717static int long_term_check2(void)
718{
719 /* response SHOULD NOT include a USERNAME, NONCE, REALM or
720 * MESSAGE-INTEGRITY attribute.
721 */
722 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0))
723 return -900;
724 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0))
725 return -910;
726 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0))
727 return -920;
728 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0))
729 return -930;
730
731 return 0;
732}
733
734static int long_term_check3(void)
735{
736 /* response SHOULD NOT include a USERNAME, NONCE, and REALM */
737 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_USERNAME, 0))
738 return -1000;
739 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_NONCE, 0))
740 return -1010;
741 if (pj_stun_msg_find_attr(client->response, PJ_STUN_ATTR_REALM, 0))
742 return -1020;
743
744 return 0;
745}
746
747//////////////////////////////////////////////////////////////////////////////////////////
748//
749// TEST MAIN
750//
751
752
753int sess_auth_test(void)
754{
755 pj_pool_t *pool;
756 int rc;
757
758 PJ_LOG(3,(THIS_FILE, " STUN session authentication test"));
759
760 /* Init STUN config */
761 pj_stun_config_init(&stun_cfg, mem, 0, NULL, NULL);
762
763 /* Create pool and timer heap */
764 pool = pj_pool_create(mem, "authtest", 200, 200, NULL);
765 if (pj_timer_heap_create(pool, 20, &stun_cfg.timer_heap)) {
766 pj_pool_release(pool);
767 return -5;
768 }
769
770 /* Basic retransmission test */
771 rc = run_client_test("Retransmission", // title
772 PJ_FALSE, // server responding
773 PJ_STUN_AUTH_NONE, // server auth
774 PJ_STUN_AUTH_NONE, // client auth
775 NULL, // realm
776 NULL, // username
777 NULL, // nonce
778 NULL, // password
779 PJ_FALSE, // dummy MI
780 PJ_TRUE, // expected error
781 PJNATH_ESTUNTIMEDOUT,// expected code
782 NULL, // expected realm
783 NULL, // expected nonce
784 &retransmit_check // more check
785 );
786 if (rc != 0) {
787 goto done;
788 }
789
790 /*
791 * Short term credential.
792 * draft-ietf-behave-rfc3489bis-15#section-10.1.2
793 */
794
795 /*
796 * If the message does not contain both a MESSAGE-INTEGRITY and a
797 * USERNAME attribute, If the message is a request, the server MUST
798 * reject the request with an error response. This response MUST
799 * use an error code of 400 (Bad Request).
800 */
801 rc = run_client_test("Missing MESSAGE-INTEGRITY (short term)", // title
802 PJ_TRUE, // server responding
803 PJ_STUN_AUTH_SHORT_TERM, // server auth
804 PJ_STUN_AUTH_NONE, // client auth
805 NULL, // realm
806 NULL, // username
807 NULL, // nonce
808 NULL, // password
809 PJ_FALSE, // dummy MI
810 PJ_TRUE, // expected error
811 PJ_STATUS_FROM_STUN_CODE(400),// expected code
812 NULL, // expected realm
813 NULL, // expected nonce
814 NULL // more check
815 );
816 if (rc != 0) {
817 goto done;
818 }
819
820 /* If the USERNAME does not contain a username value currently valid
821 * within the server: If the message is a request, the server MUST
822 * reject the request with an error response. This response MUST use
823 * an error code of 401 (Unauthorized).
824 */
825 rc = run_client_test("USERNAME mismatch (short term)", // title
826 PJ_TRUE, // server responding
827 PJ_STUN_AUTH_SHORT_TERM, // server auth
828 PJ_STUN_AUTH_SHORT_TERM, // client auth
829 NULL, // realm
830 "anotheruser", // username
831 NULL, // nonce
832 "anotherpass", // password
833 PJ_FALSE, // dummy MI
834 PJ_TRUE, // expected error
835 PJ_STATUS_FROM_STUN_CODE(401),// expected code
836 NULL, // expected realm
837 NULL, // expected nonce
838 NULL // more check
839 );
840 if (rc != 0) {
841 goto done;
842 }
843
844 /* Using the password associated with the username, compute the value
845 * for the message-integrity as described in Section 15.4. If the
846 * resulting value does not match the contents of the MESSAGE-
847 * INTEGRITY attribute:
848 *
849 * - If the message is a request, the server MUST reject the request
850 * with an error response. This response MUST use an error code
851 * of 401 (Unauthorized).
852 */
853 rc = run_client_test("MESSAGE-INTEGRITY mismatch (short term)", // title
854 PJ_TRUE, // server responding
855 PJ_STUN_AUTH_SHORT_TERM, // server auth
856 PJ_STUN_AUTH_SHORT_TERM, // client auth
857 NULL, // realm
858 USERNAME, // username
859 NULL, // nonce
860 "anotherpass", // password
861 PJ_FALSE, // dummy MI
862 PJ_TRUE, // expected error
863 PJ_STATUS_FROM_STUN_CODE(401),// expected code
864 NULL, // expected realm
865 NULL, // expected nonce
866 NULL // more check
867 );
868 if (rc != 0) {
869 goto done;
870 }
871
872 /* USERNAME is not present, server must respond with 400 (Bad
873 * Request).
874 */
875 rc = run_client_test("Missing USERNAME (short term)",// title
876 PJ_TRUE, // server responding
877 PJ_STUN_AUTH_SHORT_TERM, // server auth
878 PJ_STUN_AUTH_NONE, // client auth
879 NULL, // realm
880 NULL, // username
881 NULL, // nonce
882 NULL, // password
883 PJ_TRUE, // dummy MI
884 PJ_TRUE, // expected error
885 PJ_STATUS_FROM_STUN_CODE(400), // expected code
886 NULL, // expected realm
887 NULL, // expected nonce
888 NULL // more check
889 );
890 if (rc != 0) {
891 goto done;
892 }
893
894 /* Successful short term authentication */
895 rc = run_client_test("Successful scenario (short term)", // title
896 PJ_TRUE, // server responding
897 PJ_STUN_AUTH_SHORT_TERM, // server auth
898 PJ_STUN_AUTH_SHORT_TERM, // client auth
899 NULL, // realm
900 USERNAME, // username
901 NULL, // nonce
902 PASSWORD, // password
903 PJ_FALSE, // dummy MI
904 PJ_FALSE, // expected error
905 PJ_SUCCESS, // expected code
906 NULL, // expected realm
907 NULL, // expected nonce
908 NULL // more check
909 );
910 if (rc != 0) {
911 goto done;
912 }
913
914 /*
915 * (our own) Extended tests for long term credential
916 */
917
918 /* When server wants to use short term credential, but request has
919 * REALM, reject with .... 401 ???
920 */
921 rc = run_client_test("Unwanted REALM (short term)", // title
922 PJ_TRUE, // server responding
923 PJ_STUN_AUTH_SHORT_TERM, // server auth
924 PJ_STUN_AUTH_NONE, // client auth
925 REALM, // realm
926 USERNAME, // username
927 NULL, // nonce
928 PASSWORD, // password
929 PJ_TRUE, // dummy MI
930 PJ_TRUE, // expected error
931 PJ_STATUS_FROM_STUN_CODE(401), // expected code
932 NULL, // expected realm
933 NULL, // expected nonce
934 &long_term_check2 // more check
935 );
936 if (rc != 0) {
937 goto done;
938 }
939
940
941 /*
942 * Long term credential.
943 * draft-ietf-behave-rfc3489bis-15#section-10.2.2
944 */
945
946 /* If the message does not contain a MESSAGE-INTEGRITY attribute, the
947 * server MUST generate an error response with an error code of 401
948 * (Unauthorized). This response MUST include a REALM value. It is
949 * RECOMMENDED that the REALM value be the domain name of the
950 * provider of the STUN server. The response MUST include a NONCE,
951 * selected by the server. The response SHOULD NOT contain a
952 * USERNAME or MESSAGE-INTEGRITY attribute.
953 */
954 rc = run_client_test("Missing M-I (long term)", // title
955 PJ_TRUE, // server responding
956 PJ_STUN_AUTH_LONG_TERM, // server auth
957 PJ_STUN_AUTH_NONE, // client auth
958 NULL, // client realm
959 NULL, // client username
960 NULL, // client nonce
961 NULL, // client password
962 PJ_FALSE, // client dummy MI
963 PJ_TRUE, // expected error
964 PJ_STATUS_FROM_STUN_CODE(401), // expected code
965 REALM, // expected realm
966 NONCE, // expected nonce
967 &long_term_check1 // more check
968 );
969 if (rc != 0) {
970 goto done;
971 }
972
973 /* If the message contains a MESSAGE-INTEGRITY attribute, but is
974 * missing the USERNAME, REALM or NONCE attributes, the server MUST
975 * generate an error response with an error code of 400 (Bad
976 * Request). This response SHOULD NOT include a USERNAME, NONCE,
977 * REALM or MESSAGE-INTEGRITY attribute.
978 */
979 /* Missing USERNAME */
980 rc = run_client_test("Missing USERNAME (long term)", // title
981 PJ_TRUE, // server responding
982 PJ_STUN_AUTH_LONG_TERM, // server auth
983 PJ_STUN_AUTH_NONE, // client auth
984 REALM, // client realm
985 NULL, // client username
986 NONCE, // client nonce
987 PASSWORD, // client password
988 PJ_TRUE, // client dummy MI
989 PJ_TRUE, // expected error
990 PJ_STATUS_FROM_STUN_CODE(400), // expected code
991 NULL, // expected realm
992 NULL, // expected nonce
993 &long_term_check2 // more check
994 );
995 if (rc != 0) {
996 goto done;
997 }
998
999 /* Missing REALM */
1000 rc = run_client_test("Missing REALM (long term)", // title
1001 PJ_TRUE, // server responding
1002 PJ_STUN_AUTH_LONG_TERM, // server auth
1003 PJ_STUN_AUTH_NONE, // client auth
1004 NULL, // client realm
1005 USERNAME, // client username
1006 NONCE, // client nonce
1007 PASSWORD, // client password
1008 PJ_TRUE, // client dummy MI
1009 PJ_TRUE, // expected error
1010 PJ_STATUS_FROM_STUN_CODE(400), // expected code
1011 NULL, // expected realm
1012 NULL, // expected nonce
1013 &long_term_check2 // more check
1014 );
1015 if (rc != 0) {
1016 goto done;
1017 }
1018
1019 /* Missing NONCE */
1020 rc = run_client_test("Missing NONCE (long term)", // title
1021 PJ_TRUE, // server responding
1022 PJ_STUN_AUTH_LONG_TERM, // server auth
1023 PJ_STUN_AUTH_NONE, // client auth
1024 REALM, // client realm
1025 USERNAME, // client username
1026 NULL, // client nonce
1027 PASSWORD, // client password
1028 PJ_TRUE, // client dummy MI
1029 PJ_TRUE, // expected error
1030 PJ_STATUS_FROM_STUN_CODE(400), // expected code
1031 NULL, // expected realm
1032 NULL, // expected nonce
1033 &long_term_check2 // more check
1034 );
1035 if (rc != 0) {
1036 goto done;
1037 }
1038
1039 /* If the NONCE is no longer valid, the server MUST generate an error
1040 * response with an error code of 438 (Stale Nonce). This response
1041 * MUST include a NONCE and REALM attribute and SHOULD NOT incude the
1042 * USERNAME or MESSAGE-INTEGRITY attribute. Servers can invalidate
1043 * nonces in order to provide additional security. See Section 4.3
1044 * of [RFC2617] for guidelines.
1045 */
1046 // how??
1047
1048 /* If the username in the USERNAME attribute is not valid, the server
1049 * MUST generate an error response with an error code of 401
1050 * (Unauthorized). This response MUST include a REALM value. It is
1051 * RECOMMENDED that the REALM value be the domain name of the
1052 * provider of the STUN server. The response MUST include a NONCE,
1053 * selected by the server. The response SHOULD NOT contain a
1054 * USERNAME or MESSAGE-INTEGRITY attribute.
1055 */
1056 rc = run_client_test("Invalid username (long term)", // title
1057 PJ_TRUE, // server responding
1058 PJ_STUN_AUTH_LONG_TERM, // server auth
1059 PJ_STUN_AUTH_LONG_TERM, // client auth
1060 REALM, // client realm
1061 "anotheruser", // client username
1062 "a nonce", // client nonce
1063 "somepassword", // client password
1064 PJ_FALSE, // client dummy MI
1065 PJ_TRUE, // expected error
1066 PJ_STATUS_FROM_STUN_CODE(401), // expected code
1067 REALM, // expected realm
1068 NONCE, // expected nonce
1069 &long_term_check1 // more check
1070 );
1071 if (rc != 0) {
1072 goto done;
1073 }
1074
1075 /* Successful long term authentication */
1076 rc = run_client_test("Successful scenario (long term)", // title
1077 PJ_TRUE, // server responding
1078 PJ_STUN_AUTH_LONG_TERM, // server auth
1079 PJ_STUN_AUTH_LONG_TERM, // client auth
1080 REALM, // client realm
1081 USERNAME, // client username
1082 "anothernonce", // client nonce
1083 PASSWORD, // client password
1084 PJ_FALSE, // client dummy MI
1085 PJ_FALSE, // expected error
1086 0, // expected code
1087 NULL, // expected realm
1088 NULL, // expected nonce
1089 &long_term_check3 // more check
1090 );
1091 if (rc != 0) {
1092 goto done;
1093 }
1094
1095 /*
1096 * (our own) Extended tests for long term credential
1097 */
1098
1099 /* If REALM doesn't match, server must respond with 401
1100 */
Benny Prijonoff1df042008-06-06 14:47:10 +00001101#if 0
1102 // STUN session now will just use the realm sent in the
1103 // response, so this test will fail because it will
1104 // authenticate successfully.
1105
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001106 rc = run_client_test("Invalid REALM (long term)", // title
1107 PJ_TRUE, // server responding
1108 PJ_STUN_AUTH_LONG_TERM, // server auth
1109 PJ_STUN_AUTH_LONG_TERM, // client auth
1110 "anotherrealm", // client realm
1111 USERNAME, // client username
1112 NONCE, // client nonce
1113 PASSWORD, // client password
1114 PJ_FALSE, // client dummy MI
1115 PJ_TRUE, // expected error
1116 PJ_STATUS_FROM_STUN_CODE(401), // expected code
1117 REALM, // expected realm
1118 NONCE, // expected nonce
1119 &long_term_check1 // more check
1120 );
1121 if (rc != 0) {
1122 goto done;
1123 }
Benny Prijonoff1df042008-06-06 14:47:10 +00001124#endif
Benny Prijonoa5d214f2008-03-19 23:00:30 +00001125
1126 /* Invalid HMAC */
1127
1128 /* Valid static short term, without NONCE */
1129
1130 /* Valid static short term, WITH NONCE */
1131
1132 /* Valid static long term (with NONCE */
1133
1134 /* Valid dynamic short term (without NONCE) */
1135
1136 /* Valid dynamic short term (with NONCE) */
1137
1138 /* Valid dynamic long term (with NONCE) */
1139
1140
1141done:
1142 pj_timer_heap_destroy(stun_cfg.timer_heap);
1143 pj_pool_release(pool);
1144 return rc;
1145}