Ticket #374: Update STUN specification from rfc3489bis-06 to rfc3489bis-10

git-svn-id: https://svn.pjsip.org/repos/pjproject/trunk@1439 74dad513-b988-da41-8d7b-12977e46ad98
This commit is contained in:
Benny Prijono 2007-09-18 19:33:33 +00:00
parent d7ea605849
commit e2d0acbaaa
10 changed files with 918 additions and 156 deletions

View File

@ -49,7 +49,7 @@
/* **************************************************************************
* STUN CLIENT CONFIGURATION
* STUN CONFIGURATION
*/
/**
@ -80,10 +80,10 @@
* After the last retransmission is sent and if no response is received
* after this time, the STUN transaction will be considered to have failed.
*
* The default value is 1600 miliseconds (as per RFC 3489-bis).
* The default value is 16x RTO (as per RFC 3489-bis).
*/
#ifndef PJ_STUN_TIMEOUT_VALUE
# define PJ_STUN_TIMEOUT_VALUE 1600
# define PJ_STUN_TIMEOUT_VALUE (16 * PJ_STUN_RTO_VALUE)
#endif
@ -121,6 +121,15 @@
#define PJ_STUN_PORT 3478
/**
* Padding character for string attributes.
*
* Default: ASCII 0
*/
#ifndef PJ_STUN_STRING_ATTR_PAD_CHR
# define PJ_STUN_STRING_ATTR_PAD_CHR 0
#endif
/* **************************************************************************
* ICE CONFIGURATION

View File

@ -280,7 +280,7 @@ typedef enum pj_stun_attr_type
PJ_STUN_ATTR_SOURCE_ADDR = 0x0004,/**< SOURCE-ADDRESS (deprecated)*/
PJ_STUN_ATTR_CHANGED_ADDR = 0x0005,/**< CHANGED-ADDRESS (deprecatd)*/
PJ_STUN_ATTR_USERNAME = 0x0006,/**< USERNAME attribute. */
PJ_STUN_ATTR_PASSWORD = 0x0007,/**< PASSWORD attribute. */
PJ_STUN_ATTR_PASSWORD = 0x0007,/**< was PASSWORD attribute. */
PJ_STUN_ATTR_MESSAGE_INTEGRITY = 0x0008,/**< MESSAGE-INTEGRITY. */
PJ_STUN_ATTR_ERROR_CODE = 0x0009,/**< ERROR-CODE. */
PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES = 0x000A,/**< UNKNOWN-ATTRIBUTES. */
@ -329,14 +329,17 @@ typedef enum pj_stun_status
PJ_STUN_SC_BAD_REQUEST = 400, /**< Bad Request */
PJ_STUN_SC_UNAUTHORIZED = 401, /**< Unauthorized */
PJ_STUN_SC_UNKNOWN_ATTRIBUTE = 420, /**< Unknown Attribute */
PJ_STUN_SC_STALE_CREDENTIALS = 430, /**< Stale Credentials */
PJ_STUN_SC_INTEGRITY_CHECK_FAILURE = 431, /**< Integrity Chk Fail */
PJ_STUN_SC_MISSING_USERNAME = 432, /**< Missing Username */
PJ_STUN_SC_USE_TLS = 433, /**< Use TLS */
PJ_STUN_SC_MISSING_REALM = 434, /**< Missing Realm */
PJ_STUN_SC_MISSING_NONCE = 435, /**< Missing Nonce */
PJ_STUN_SC_UNKNOWN_USERNAME = 436, /**< Unknown Username */
PJ_STUN_SC_NO_BINDING = 437, /**< No Binding. */
#if 0
/* These were obsolete in recent rfc3489bis */
//PJ_STUN_SC_STALE_CREDENTIALS = 430, /**< Stale Credentials */
//PJ_STUN_SC_INTEGRITY_CHECK_FAILURE= 431, /**< Integrity Chk Fail */
//PJ_STUN_SC_MISSING_USERNAME = 432, /**< Missing Username */
//PJ_STUN_SC_USE_TLS = 433, /**< Use TLS */
//PJ_STUN_SC_MISSING_REALM = 434, /**< Missing Realm */
//PJ_STUN_SC_MISSING_NONCE = 435, /**< Missing Nonce */
//PJ_STUN_SC_UNKNOWN_USERNAME = 436, /**< Unknown Username */
//PJ_STUN_SC_NO_BINDING = 437, /**< No Binding. */
#endif
PJ_STUN_SC_STALE_NONCE = 438, /**< Stale Nonce */
PJ_STUN_SC_TRANSITIONING = 439, /**< Transitioning. */
PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO = 442, /**< Unsupported Transport or
@ -1090,6 +1093,15 @@ PJ_DECL(const char*) pj_stun_get_attr_name(unsigned attr_type);
PJ_DECL(pj_str_t) pj_stun_get_err_reason(int err_code);
/**
* Internal: set the padding character for string attribute.
* The default padding character is PJ_STUN_STRING_ATTR_PAD_CHR.
*
* @return The previous padding character.
*/
PJ_DECL(int) pj_stun_set_padding_char(int chr);
/**
* Create a generic STUN message.
*

View File

@ -142,8 +142,8 @@ PJ_END_DECL
*
* References for STUN:
*
* - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-06.txt">
* <B>draft-ietf-behave-rfc3489bis-06</b></A>: Session Traversal
* - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-rfc3489bis-10.txt">
* <B>draft-ietf-behave-rfc3489bis-10</b></A>: Session Traversal
* Utilities for (NAT) (STUN),
* - <A HREF="http://www.ietf.org/internet-drafts/draft-ietf-behave-turn-03.txt">
* <B>draft-ietf-behave-turn-03</B></A>: Obtaining Relay Addresses

View File

@ -16,42 +16,392 @@
* 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 "stun.c"
static pj_stun_msg* create1(pj_pool_t*);
static int verify1(pj_stun_msg*);
static int verify2(pj_stun_msg*);
static int verify5(pj_stun_msg*);
static struct test
{
const char *title;
char *pdu;
unsigned pdu_len;
pj_stun_msg* (*create)(pj_pool_t*);
pj_status_t expected_status;
int (*verify)(pj_stun_msg*);
} tests[] =
{
{
"Invalid message type",
"\x11\x01\x00\x00\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
20,
NULL,
PJNATH_EINSTUNMSGTYPE,
NULL
},
{
"Short message (1) (partial header)",
"\x00\x01",
2,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Short message (2) (partial header)",
"\x00\x01\x00\x00\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00",
16,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Short message (3), (missing attribute)",
"\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00",
20,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Short message (4), (partial attribute header)",
"\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28",
22,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Short message (5), (partial attribute header)",
"\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00",
23,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Short message (6), (partial attribute header)",
"\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00\x04",
24,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Short message (7), (partial attribute body)",
"\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00\x04\x00\x00\x00",
27,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Message length in header is too long",
"\x00\x01\xff\xff\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00\x04\x00\x00\x00",
27,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Message length in header is shorter",
"\x00\x01\x00\x04\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00\x04\x00\x00\x00\x00",
28,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Invalid magic",
"\x00\x01\x00\x08\x00\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00\x04\x00\x00\x00\x00",
28,
NULL,
PJ_SUCCESS,
NULL
},
{
"Character beyond message",
"\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00\x04\x00\x00\x00\x00\x0a",
29,
NULL,
PJNATH_EINSTUNMSGLEN,
NULL
},
{
"Respond unknown mandatory attribute with 420 and "
"UNKNOWN-ATTRIBUTES attribute",
NULL,
0,
&create1,
0,
&verify1
},
{
"Unknown but non-mandatory should be okay",
"\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\xff\x00\x04\x00\x00\x00\x00",
28,
NULL,
PJ_SUCCESS,
&verify2
},
{
"String attr length larger than message",
"\x00\x01\x00\x08\x00\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x06\x00\xff\x00\x00\x00\x00",
28,
NULL,
PJNATH_ESTUNINATTRLEN,
NULL
},
{
"Attribute other than FINGERPRINT after MESSAGE-INTEGRITY is allowed",
"\x00\x01\x00\x20\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x08\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // M-I
"\x80\x24\x00\x04\x00\x00\x00\x00", // REFRESH-INTERVAL
52,
NULL,
PJ_SUCCESS,
NULL
},
{
"Attribute between MESSAGE-INTEGRITY and FINGERPRINT is allowed",
"\x00\x01\x00\x28\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x08\x00\x14\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00" // M-I
"\x80\x24\x00\x04\x00\x00\x00\x00" // REFRESH-INTERVAL
"\x80\x28\x00\x04\xc7\xde\xdd\x65", // FINGERPRINT
60,
NULL,
PJ_SUCCESS,
&verify5
},
{
"Attribute past FINGERPRINT is not allowed",
"\x00\x01\x00\x10\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x80\x28\x00\x04\x00\x00\x00\x00"
"\x80\x24\x00\x04\x00\x00\x00\x00",
36,
NULL,
PJNATH_ESTUNFINGERPOS,
NULL
}
};
static const char *err(pj_status_t status)
{
static char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
return errmsg;
}
static const pj_str_t USERNAME = {"user", 4};
static const pj_str_t PASSWORD = {"password", 8};
static int decode_test(void)
{
/* Invalid message type */
unsigned i;
pj_pool_t *pool;
int rc = 0;
pool = pj_pool_create(mem, "decode_test", 1024, 1024, NULL);
/* Short message */
PJ_LOG(3,(THIS_FILE, " STUN decode test"));
/* Long, random message */
for (i=0; i<PJ_ARRAY_SIZE(tests); ++i) {
struct test *t = &tests[i];
pj_stun_msg *msg, *msg2;
pj_uint8_t buf[1500];
pj_str_t key;
unsigned len;
pj_status_t status;
/* Message length in header is shorter */
PJ_LOG(3,(THIS_FILE, " %s", t->title));
/* Message length in header is longer */
if (t->pdu) {
status = pj_stun_msg_decode(pool, (pj_uint8_t*)t->pdu, t->pdu_len,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
&msg, NULL, NULL);
/* Invalid magic */
/* Check expected decode result */
if (t->expected_status != status) {
PJ_LOG(1,(THIS_FILE, " expecting status %d, got %d",
t->expected_status, status));
rc = -10;
goto on_return;
}
/* Attribute length is not valid */
} else {
msg = t->create(pool);
status = PJ_SUCCESS;
}
/* Unknown mandatory attribute type should generate error */
if (status != PJ_SUCCESS)
continue;
/* Unknown but non-mandatory should be okay */
/* Try to encode message */
pj_stun_create_key(pool, &key, NULL, &USERNAME, &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)));
rc = -40;
goto on_return;
}
/* String/binary attribute length is larger than the message */
/* Try to decode it once more */
status = pj_stun_msg_decode(pool, buf, len,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
&msg2, NULL, NULL);
if (status != PJ_SUCCESS) {
PJ_LOG(1,(THIS_FILE, " subsequent decoding failed: %s", err(status)));
rc = -50;
goto on_return;
}
/* Valid message with MESSAGE-INTEGRITY */
/* Verify */
if (t->verify) {
rc = t->verify(msg);
if (rc != 0) {
goto on_return;
}
}
}
/* Valid message with FINGERPRINT */
on_return:
pj_pool_release(pool);
if (rc == 0)
PJ_LOG(3,(THIS_FILE, "...success!"));
return rc;
}
/* Valid message with MESSAGE-INTEGRITY and FINGERPRINT */
/* Create 420 response */
static pj_stun_msg* create1(pj_pool_t *pool)
{
char *pdu = "\x00\x01\x00\x08\x21\x12\xa4\x42"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\xff\x00\x04\x00\x00\x00\x00";
unsigned pdu_len = 28;
pj_stun_msg *msg, *res;
pj_status_t status;
/* Another attribute not FINGERPRINT exists after MESSAGE-INTEGRITY */
status = pj_stun_msg_decode(pool, (pj_uint8_t*)pdu, pdu_len,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
&msg, NULL, &res);
pj_assert(status != PJ_SUCCESS);
pj_assert(res != NULL);
/* Another attribute exists after FINGERPRINT */
return res;
}
/* Error response MUST have ERROR-CODE attribute */
/* 420 response MUST contain UNKNOWN-ATTRIBUTES */
static int verify1(pj_stun_msg *msg)
{
pj_stun_errcode_attr *aerr;
pj_stun_unknown_attr *aunk;
if (!PJ_STUN_IS_ERROR_RESPONSE(msg->hdr.type)) {
PJ_LOG(1,(THIS_FILE, " expecting error message"));
return -100;
}
aerr = (pj_stun_errcode_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_ERROR_CODE, 0);
if (aerr == NULL) {
PJ_LOG(1,(THIS_FILE, " missing ERROR-CODE attribute"));
return -110;
}
if (aerr->err_code != 420) {
PJ_LOG(1,(THIS_FILE, " expecting 420 error"));
return -120;
}
aunk = (pj_stun_unknown_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_UNKNOWN_ATTRIBUTES, 0);
if (aunk == NULL) {
PJ_LOG(1,(THIS_FILE, " missing UNKNOWN-ATTRIBUTE attribute"));
return -130;
}
if (aunk->attr_count != 1) {
PJ_LOG(1,(THIS_FILE, " expecting one unknown attribute"));
return -140;
}
if (aunk->attrs[0] != 0xff) {
PJ_LOG(1,(THIS_FILE, " expecting 0xff as unknown attribute"));
return -150;
}
return 0;
}
/* Attribute count should be zero since unknown attribute is not parsed */
static int verify2(pj_stun_msg *msg)
{
if (msg->attr_count != 0) {
PJ_LOG(1,(THIS_FILE, " expecting zero attribute count"));
return -200;
}
return 0;
}
/* Attribute between MESSAGE-INTEGRITY and FINGERPRINT is allowed */
static int verify5(pj_stun_msg *msg)
{
if (msg->attr_count != 3) {
PJ_LOG(1,(THIS_FILE, " expecting 3 attribute count"));
return -500;
}
if (msg->attr[0]->type != PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
PJ_LOG(1,(THIS_FILE, " expecting MESSAGE-INTEGRITY"));
return -510;
}
if (msg->attr[1]->type != PJ_STUN_ATTR_REFRESH_INTERVAL) {
PJ_LOG(1,(THIS_FILE, " expecting REFRESH-INTERVAL"));
return -520;
}
if (msg->attr[2]->type != PJ_STUN_ATTR_FINGERPRINT) {
PJ_LOG(1,(THIS_FILE, " expecting FINGERPRINT"));
return -530;
}
return 0;
}
static int decode_verify(void)
{
/* Decode all attribute types */
@ -107,12 +457,310 @@ static int auth_test(void)
return 0;
}
typedef struct test_vector test_vector;
static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v);
static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v);
enum
{
USE_MESSAGE_INTEGRITY = 1,
USE_FINGERPRINT = 2
};
struct test_vector
{
unsigned msg_type;
char *tsx_id;
char *pdu;
unsigned pdu_len;
unsigned options;
char *username;
char *password;
pj_stun_msg* (*create)(pj_pool_t*, test_vector*);
} test_vectors[] =
{
{
PJ_STUN_BINDING_REQUEST,
"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae",
"\x00\x01\x00\x44\x21\x12\xa4\x42\xb7\xe7"
"\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
"\x00\x24\x00\x04\x6e\x00\x01\xff\x80\x29"
"\x00\x08\x93\x2f\xf9\xb1\x51\x26\x3b\x36"
"\x00\x06\x00\x09\x65\x76\x74\x6a\x3a\x68"
"\x36\x76\x59\x20\x20\x20\x00\x08\x00\x14"
"\x62\x4e\xeb\xdc\x3c\xc9\x2d\xd8\x4b\x74"
"\xbf\x85\xd1\xc0\xf5\xde\x36\x87\xbd\x33"
"\x80\x28\x00\x04\xad\x8a\x85\xff",
88,
USE_MESSAGE_INTEGRITY | USE_FINGERPRINT,
"evtj:h6vY",
"VOkJxbRl1RmTxUk/WvJxBt",
&create_msgint1
},
{
PJ_STUN_BINDING_RESPONSE,
"\xb7\xe7\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae",
"\x01\x01\x00\x3c\x21\x12\xa4\x42\xb7\xe7"
"\xa7\x01\xbc\x34\xd6\x86\xfa\x87\xdf\xae"
"\x80\x22\x00\x0b\x74\x65\x73\x74\x20\x76"
"\x65\x63\x74\x6f\x72\x20\x00\x20\x00\x08"
"\x00\x01\xa1\x47\x5e\x12\xa4\x43\x00\x08"
"\x00\x14\xab\x4e\x53\x29\x61\x00\x08\x4c"
"\x89\xf2\x7c\x69\x30\x33\x5c\xa3\x58\x14"
"\xea\x90\x80\x28\x00\x04\xae\x25\x8d\xf2",
80,
USE_MESSAGE_INTEGRITY | USE_FINGERPRINT,
"evtj:h6vY",
"VOkJxbRl1RmTxUk/WvJxBt",
&create_msgint2
}
};
static char* print_binary(const pj_uint8_t *data, unsigned data_len)
{
static char buf[1500];
unsigned length = sizeof(buf);
char *p = buf;
unsigned i;
for (i=0; i<data_len;) {
unsigned j;
pj_ansi_snprintf(p, 1500-(p-buf),
"%04d-%04d ",
i, (i+20 < data_len) ? i+20 : data_len);
p += 12;
for (j=0; j<20 && i<data_len && p<(buf+length-10); ++j, ++i) {
pj_ansi_sprintf(p, "%02x ", (*data) & 0xFF);
p += 3;
data++;
}
pj_ansi_sprintf(p, "\n");
p++;
}
return buf;
}
static int cmp_buf(const pj_uint8_t *s1, const pj_uint8_t *s2, unsigned len)
{
unsigned i;
for (i=0; i<len; ++i) {
if (s1[i] != s2[i])
return i;
}
return -1;
}
static int fingerprint_test_vector()
{
pj_pool_t *pool;
pj_status_t status;
unsigned i;
int rc = 0;
PJ_LOG(3,(THIS_FILE, " STUN message test vectors"));
pool = pj_pool_create(mem, "fingerprint", 1024, 1024, NULL);
for (i=0; i<PJ_ARRAY_SIZE(test_vectors); ++i) {
struct test_vector *v;
pj_stun_msg *ref_msg, *msg;
unsigned parsed_len;
unsigned len, pos;
pj_uint8_t buf[1500];
char print[1500];
pj_str_t key;
PJ_LOG(3,(THIS_FILE, " Running test %d/%d", i,
PJ_ARRAY_SIZE(test_vectors)));
v = &test_vectors[i];
/* Print reference message */
PJ_LOG(4,(THIS_FILE, "Reference message PDU:\n%s",
print_binary((pj_uint8_t*)v->pdu, v->pdu_len)));
/* Try to parse the reference message first */
status = pj_stun_msg_decode(pool, (pj_uint8_t*)v->pdu, v->pdu_len,
PJ_STUN_IS_DATAGRAM | PJ_STUN_CHECK_PACKET,
&ref_msg, &parsed_len, NULL);
if (status != PJ_SUCCESS) {
PJ_LOG(1,(THIS_FILE, " Error decoding reference message"));
rc = -1010;
goto on_return;
}
if (parsed_len != v->pdu_len) {
PJ_LOG(1,(THIS_FILE, " Parsed len error"));
rc = -1020;
goto on_return;
}
/* Print the reference message */
pj_stun_msg_dump(ref_msg, print, sizeof(print), NULL);
PJ_LOG(4,(THIS_FILE, "Reference message:\n%s", print));
/* Create our message */
msg = v->create(pool, v);
/* Encode message */
if (v->options & USE_MESSAGE_INTEGRITY) {
pj_str_t s1, s2;
pj_stun_create_key(pool, &key, NULL, pj_cstr(&s1, v->username),
pj_cstr(&s2, v->password));
pj_stun_msg_encode(msg, buf, sizeof(buf), 0, &key, &len);
} else {
pj_stun_msg_encode(msg, buf, sizeof(buf), 0, NULL, &len);
}
/* Print our raw message */
PJ_LOG(4,(THIS_FILE, "Message PDU:\n%s",
print_binary((pj_uint8_t*)buf, len)));
/* Print our message */
pj_stun_msg_dump(msg, print, sizeof(print), NULL);
PJ_LOG(4,(THIS_FILE, "Message is:\n%s", print));
/* Compare message length */
if (len != v->pdu_len) {
PJ_LOG(1,(THIS_FILE, " Message length mismatch"));
rc = -1050;
goto on_return;
}
pos = cmp_buf(buf, (const pj_uint8_t*)v->pdu, len);
if (pos != -1) {
PJ_LOG(1,(THIS_FILE, " Message mismatch at byte %d", pos));
rc = -1060;
goto on_return;
}
/* Authenticate the request/response */
if (v->options & USE_MESSAGE_INTEGRITY) {
if (PJ_STUN_IS_REQUEST(msg->hdr.type)) {
pj_stun_auth_cred cred;
pj_status_t status;
pj_bzero(&cred, sizeof(cred));
cred.type = PJ_STUN_AUTH_CRED_STATIC;
cred.data.static_cred.username = pj_str(v->username);
cred.data.static_cred.data = pj_str(v->password);
status = pj_stun_authenticate_request(buf, len, msg,
&cred, pool, NULL);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,(THIS_FILE,
" Request authentication failed: %s",
errmsg));
rc = -1070;
goto on_return;
}
} else if (PJ_STUN_IS_RESPONSE(msg->hdr.type)) {
pj_status_t status;
status = pj_stun_authenticate_response(buf, len, msg, &key);
if (status != PJ_SUCCESS) {
char errmsg[PJ_ERR_MSG_SIZE];
pj_strerror(status, errmsg, sizeof(errmsg));
PJ_LOG(1,(THIS_FILE,
" Response authentication failed: %s",
errmsg));
rc = -1080;
goto on_return;
}
}
}
}
on_return:
pj_pool_release(pool);
return rc;
}
static pj_stun_msg* create_msgint1(pj_pool_t *pool, test_vector *v)
{
pj_stun_msg *msg;
pj_timestamp u64;
pj_str_t s1;
pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC,
(pj_uint8_t*)v->tsx_id, &msg);
pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_PRIORITY, 0x6e0001ff);
u64.u32.hi = 0x932ff9b1;
u64.u32.lo = 0x51263b36;
pj_stun_msg_add_uint64_attr(pool, msg, PJ_STUN_ATTR_ICE_CONTROLLED,
&u64);
pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_USERNAME,
pj_cstr(&s1, v->username));
pj_stun_msg_add_msgint_attr(pool, msg);
pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0);
return msg;
}
static pj_stun_msg* create_msgint2(pj_pool_t *pool, test_vector *v)
{
pj_stun_msg *msg;
pj_sockaddr_in mapped_addr;
pj_str_t s1;
pj_stun_msg_create(pool, v->msg_type, PJ_STUN_MAGIC,
(pj_uint8_t*)v->tsx_id, &msg);
pj_stun_msg_add_string_attr(pool, msg, PJ_STUN_ATTR_SERVER,
pj_cstr(&s1, "test vector"));
pj_sockaddr_in_init(&mapped_addr, pj_cstr(&s1, "127.0.0.1"), 32853);
pj_stun_msg_add_sockaddr_attr(pool, msg, PJ_STUN_ATTR_XOR_MAPPED_ADDR,
PJ_TRUE, &mapped_addr,
sizeof(pj_sockaddr_in));
pj_stun_msg_add_msgint_attr(pool, msg);
pj_stun_msg_add_uint_attr(pool, msg, PJ_STUN_ATTR_FINGERPRINT, 0);
return msg;
}
int stun_test(void)
{
decode_verify();
decode_test();
auth_test();
return 0;
int pad, rc;
pad = pj_stun_set_padding_char(32);
rc = decode_test();
if (rc != 0)
goto on_return;
rc = decode_verify();
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;
on_return:
pj_stun_set_padding_char(pad);
return rc;
}

View File

@ -66,6 +66,10 @@ static int test_inner(void)
pjnath_init();
#if INCLUDE_STUN_TEST
DO_TEST(stun_test());
#endif
#if INCLUDE_ICE_TEST
DO_TEST(ice_test());
#endif

View File

@ -20,8 +20,10 @@
#include <pjlib-util.h>
#include <pjnath.h>
#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);

View File

@ -489,7 +489,7 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
/* Incoming response is authenticated with TX credential */
/* Verify username */
if (pj_strcmp(username, &ice->tx_uname) != 0)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
*data_type = 0;
*data = ice->tx_pass;
@ -507,13 +507,13 @@ static pj_status_t stun_auth_get_password(const pj_stun_msg *msg,
pos = (const char*)pj_memchr(username->ptr, ':', username->slen);
if (pos == NULL)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
ufrag.ptr = (char*)username->ptr;
ufrag.slen = (pos - username->ptr);
if (pj_strcmp(&ufrag, &ice->rx_ufrag) != 0)
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
*data_type = 0;
*data = ice->rx_pass;

View File

@ -59,21 +59,30 @@ PJ_INLINE(pj_uint16_t) GET_VAL16(const pj_uint8_t *pdu, unsigned pos)
}
PJ_INLINE(void) PUT_VAL16(pj_uint8_t *buf, unsigned pos, pj_uint16_t hval)
{
buf[pos+0] = (pj_uint8_t) ((hval & 0xFF00) >> 8);
buf[pos+1] = (pj_uint8_t) ((hval & 0x00FF) >> 0);
}
/* Send 401 response */
static pj_status_t create_challenge(pj_pool_t *pool,
const pj_stun_msg *msg,
int err_code,
const pj_str_t *err_msg,
const char *errstr,
const pj_str_t *realm,
const pj_str_t *nonce,
pj_stun_msg **p_response)
{
pj_stun_msg *response;
pj_str_t tmp_nonce;
pj_str_t err_msg;
pj_status_t rc;
rc = pj_stun_msg_create_response(pool, msg,
err_code, err_msg, &response);
rc = pj_stun_msg_create_response(pool, msg, err_code,
(errstr?pj_cstr(&err_msg, errstr):NULL),
&response);
if (rc != PJ_SUCCESS)
return rc;
@ -116,7 +125,8 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
{
pj_str_t realm, nonce, password;
const pj_stun_msgint_attr *amsgi;
unsigned amsgi_pos;
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;
@ -138,7 +148,7 @@ 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;
/* Get realm and nonce */
/* Get realm and nonce from credential */
realm.slen = nonce.slen = 0;
if (cred->type == PJ_STUN_AUTH_CRED_STATIC) {
realm = cred->data.static_cred.realm;
@ -153,26 +163,50 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
return PJ_EBUG;
}
/* First check that MESSAGE-INTEGRITY is present */
amsgi = (const pj_stun_msgint_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0);
/* Look for MESSAGE-INTEGRITY while counting the position */
amsgi_pos = 0;
has_attr_beyond_mi = PJ_FALSE;
amsgi = NULL;
for (i=0; i<msg->attr_count; ++i) {
if (msg->attr[i]->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
amsgi = (const pj_stun_msgint_attr*) msg->attr[i];
} else if (amsgi) {
has_attr_beyond_mi = PJ_TRUE;
break;
} else {
amsgi_pos += ((msg->attr[i]->length+3) & ~0x03) + 4;
}
}
if (amsgi == NULL) {
/* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should return 400
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, PJ_STUN_SC_UNAUTHORIZED, NULL,
create_challenge(pool, msg, code, NULL,
&realm, &nonce, p_response);
}
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
return PJ_STATUS_FROM_STUN_CODE(code);
}
/* Next check that USERNAME is present */
auser = (const pj_stun_username_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_USERNAME, 0);
if (auser == NULL) {
/* According to rfc3489bis-10 Sec 10.1.2/10.2.2, we should return 400
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, PJ_STUN_SC_MISSING_USERNAME, NULL,
create_challenge(pool, msg, code, "Missing USERNAME",
&realm, &nonce, p_response);
}
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_USERNAME);
return PJ_STATUS_FROM_STUN_CODE(code);
}
/* Get REALM, if any */
@ -200,11 +234,14 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
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_UNKNOWN_USERNAME, NULL,
create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED, NULL,
&realm, &nonce, p_response);
}
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNKNOWN_USERNAME);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
}
@ -216,10 +253,11 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
if (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_MISSING_REALM, NULL,
create_challenge(pool, msg, PJ_STUN_SC_BAD_REQUEST,
"Missing REALM",
&realm, &nonce, p_response);
}
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_REALM);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
} else if (realm.slen != 0 && arealm != NULL) {
/* We want long term, and REALM is present */
@ -227,20 +265,20 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
/* NONCE must be present. */
if (anonce == NULL && nonce.slen) {
if (p_response) {
create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE,
NULL, &realm, &nonce, 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_MISSING_NONCE);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
}
/* Verify REALM matches */
if (pj_stricmp(&arealm->value, &realm)) {
/* REALM doesn't match */
if (p_response) {
create_challenge(pool, msg, PJ_STUN_SC_MISSING_REALM,
NULL, &realm, &nonce, 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_MISSING_REALM);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
}
/* Valid case, will validate the message integrity later */
@ -260,10 +298,10 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
/* Application MAY request NONCE to be supplied */
if (nonce.slen != 0) {
if (p_response) {
create_challenge(pool, msg, PJ_STUN_SC_MISSING_NONCE,
NULL, &realm, &nonce, 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_MISSING_NONCE);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
}
}
@ -294,55 +332,49 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_request(const pj_uint8_t *pkt,
create_challenge(pool, msg, PJ_STUN_SC_STALE_NONCE,
NULL, &realm, &nonce, p_response);
}
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_MISSING_NONCE);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_STALE_NONCE);
}
}
/* Get the position of MESSAGE-INTEGRITY in the packet */
amsgi_pos = 20+msg->hdr.length-24;
if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
/* Found MESSAGE-INTEGRITY as the last attribute */
} else {
amsgi_pos = 0;
}
if (amsgi_pos==0) {
amsgi_pos = 20+msg->hdr.length-8-24;
if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
/* Found MESSAGE-INTEGRITY before FINGERPRINT */
} else {
amsgi_pos = 0;
}
}
if (amsgi_pos==0) {
pj_assert(!"Unable to find MESSAGE-INTEGRITY in the message!");
return PJ_EBUG;
}
/* Calculate key */
pj_stun_create_key(pool, &key, &realm, &auser->value, &password);
/* Now calculate HMAC of the message, adding zero padding if necessary
* to make the input 64 bytes aligned.
*/
/* Now calculate HMAC of the message. */
pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key.ptr, key.slen);
pj_hmac_sha1_update(&ctx, pkt, amsgi_pos);
if (amsgi_pos & 63) {
pj_uint8_t zeroes[64];
pj_bzero(zeroes, sizeof(zeroes));
pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 63));
/* First calculate HMAC for the header.
* The calculation is different depending on whether FINGERPRINT attribute
* is present in the message.
*/
if (has_attr_beyond_mi) {
pj_uint8_t hdr_copy[20];
pj_memcpy(hdr_copy, pkt, 20);
PUT_VAL16(hdr_copy, 2, (pj_uint16_t)(amsgi_pos + 24));
pj_hmac_sha1_update(&ctx, hdr_copy, 20);
} else {
pj_hmac_sha1_update(&ctx, pkt, 20);
}
/* Now update with the message body */
pj_hmac_sha1_update(&ctx, pkt+20, amsgi_pos);
// This is no longer necessary as per rfc3489bis-08
//if (amsgi_pos & 0x3F) {
// pj_uint8_t zeroes[64];
// pj_bzero(zeroes, sizeof(zeroes));
// pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 0x3F));
//}
pj_hmac_sha1_final(&ctx, digest);
/* Compare HMACs */
if (pj_memcmp(amsgi->hmac, digest, 20)) {
/* HMAC value mismatch */
/* According to rfc3489bis-10 Sec 10.1.2 we should return 401 */
if (p_response) {
create_challenge(pool, msg, PJ_STUN_SC_INTEGRITY_CHECK_FAILURE,
create_challenge(pool, msg, PJ_STUN_SC_UNAUTHORIZED,
NULL, &realm, &nonce, p_response);
}
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
}
/* Everything looks okay! */
@ -381,11 +413,11 @@ 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) */
//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) */
return PJ_FALSE;
default:
return PJ_TRUE;
@ -400,7 +432,8 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt,
const pj_str_t *key)
{
const pj_stun_msgint_attr *amsgi;
unsigned amsgi_pos;
unsigned i, amsgi_pos;
pj_bool_t has_attr_beyond_mi;
pj_hmac_sha1_context ctx;
pj_uint8_t digest[PJ_SHA1_DIGEST_SIZE];
@ -410,7 +443,7 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt,
amsgi = (const pj_stun_msgint_attr*)
pj_stun_msg_find_attr(msg, PJ_STUN_ATTR_MESSAGE_INTEGRITY, 0);
if (amsgi == NULL) {
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
}
@ -419,48 +452,55 @@ PJ_DEF(pj_status_t) pj_stun_authenticate_response(const pj_uint8_t *pkt,
return PJNATH_EINSTUNMSGLEN;
}
/* Get the position of MESSAGE-INTEGRITY in the packet */
amsgi_pos = 20+msg->hdr.length-24;
if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
/* Found MESSAGE-INTEGRITY as the last attribute */
} else {
amsgi_pos = 0;
}
if (amsgi_pos==0) {
/* Check that message length is valid */
if (msg->hdr.length < 32) {
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
}
amsgi_pos = 20+msg->hdr.length-8-24;
if (GET_VAL16(pkt, amsgi_pos) == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
/* Found MESSAGE-INTEGRITY before FINGERPRINT */
/* Look for MESSAGE-INTEGRITY while counting the position */
amsgi_pos = 0;
has_attr_beyond_mi = PJ_FALSE;
amsgi = NULL;
for (i=0; i<msg->attr_count; ++i) {
if (msg->attr[i]->type == PJ_STUN_ATTR_MESSAGE_INTEGRITY) {
amsgi = (const pj_stun_msgint_attr*) msg->attr[i];
} else if (amsgi) {
has_attr_beyond_mi = PJ_TRUE;
break;
} else {
amsgi_pos = 0;
amsgi_pos += ((msg->attr[i]->length+3) & ~0x03) + 4;
}
}
if (amsgi_pos==0) {
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
if (amsgi == NULL) {
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_BAD_REQUEST);
}
/* Now calculate HMAC of the message, adding zero padding if necessary
* to make the input 64 bytes aligned.
*/
/* Now calculate HMAC of the message. */
pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, key->slen);
pj_hmac_sha1_update(&ctx, pkt, amsgi_pos);
if (amsgi_pos & 0x3F) {
pj_uint8_t zeroes[64];
pj_bzero(zeroes, sizeof(zeroes));
pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 0x3F));
/* First calculate HMAC for the header.
* The calculation is different depending on whether FINGERPRINT attribute
* is present in the message.
*/
if (has_attr_beyond_mi) {
pj_uint8_t hdr_copy[20];
pj_memcpy(hdr_copy, pkt, 20);
PUT_VAL16(hdr_copy, 2, (pj_uint16_t)(amsgi_pos+24));
pj_hmac_sha1_update(&ctx, hdr_copy, 20);
} else {
pj_hmac_sha1_update(&ctx, pkt, 20);
}
/* Now update with the message body */
pj_hmac_sha1_update(&ctx, pkt+20, amsgi_pos);
// This is no longer necessary as per rfc3489bis-08
//if (amsgi_pos & 0x3F) {
// pj_uint8_t zeroes[64];
// pj_bzero(zeroes, sizeof(zeroes));
// pj_hmac_sha1_update(&ctx, zeroes, 64-(amsgi_pos & 0x3F));
//}
pj_hmac_sha1_final(&ctx, digest);
/* Compare HMACs */
if (pj_memcmp(amsgi->hmac, digest, 20)) {
/* HMAC value mismatch */
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_INTEGRITY_CHECK_FAILURE);
return PJ_STATUS_FROM_STUN_CODE(PJ_STUN_SC_UNAUTHORIZED);
}
/* Everything looks okay! */

View File

@ -31,6 +31,8 @@
#define THIS_FILE "stun_msg.c"
#define STUN_XOR_FINGERPRINT 0x5354554eL
static int padding_char;
static const char *stun_method_names[] =
{
"Unknown", /* 0 */
@ -54,14 +56,14 @@ static struct
{ PJ_STUN_SC_BAD_REQUEST, "Bad Request"},
{ PJ_STUN_SC_UNAUTHORIZED, "Unauthorized"},
{ PJ_STUN_SC_UNKNOWN_ATTRIBUTE, "Unknown Attribute"},
{ PJ_STUN_SC_STALE_CREDENTIALS, "Stale Credentials"},
{ PJ_STUN_SC_INTEGRITY_CHECK_FAILURE, "Integrity Check Failure"},
{ PJ_STUN_SC_MISSING_USERNAME, "Missing Username"},
{ PJ_STUN_SC_USE_TLS, "Use TLS"},
{ PJ_STUN_SC_MISSING_REALM, "Missing Realm"},
{ PJ_STUN_SC_MISSING_NONCE, "Missing Nonce"},
{ PJ_STUN_SC_UNKNOWN_USERNAME, "Unknown Username"},
{ PJ_STUN_SC_NO_BINDING, "No Binding"},
//{ PJ_STUN_SC_STALE_CREDENTIALS, "Stale Credentials"},
//{ PJ_STUN_SC_INTEGRITY_CHECK_FAILURE, "Integrity Check Failure"},
//{ PJ_STUN_SC_MISSING_USERNAME, "Missing Username"},
//{ PJ_STUN_SC_USE_TLS, "Use TLS"},
//{ PJ_STUN_SC_MISSING_REALM, "Missing Realm"},
//{ PJ_STUN_SC_MISSING_NONCE, "Missing Nonce"},
//{ PJ_STUN_SC_UNKNOWN_USERNAME, "Unknown Username"},
//{ PJ_STUN_SC_NO_BINDING, "No Binding"},
{ PJ_STUN_SC_STALE_NONCE, "Stale Nonce"},
{ PJ_STUN_SC_TRANSITIONING, "Active Destination Already Set"},
{ PJ_STUN_SC_UNSUPP_TRANSPORT_PROTO, "Unsupported Transport Protocol"},
@ -568,6 +570,17 @@ PJ_DEF(pj_str_t) pj_stun_get_err_reason(int err_code)
}
/*
* Set padding character.
*/
PJ_DEF(int) pj_stun_set_padding_char(int chr)
{
int old_pad = padding_char;
padding_char = chr;
return old_pad;
}
//////////////////////////////////////////////////////////////////////////////
@ -900,6 +913,14 @@ static pj_status_t encode_string_attr(const void *a, pj_uint8_t *buf,
/* Copy the string */
pj_memcpy(buf+ATTR_HDR_LEN, ca->value.ptr, ca->value.slen);
/* Add padding character, if string is not 4-bytes aligned. */
if (ca->value.slen & 0x03) {
pj_uint8_t pad[3];
pj_memset(pad, padding_char, sizeof(pad));
pj_memcpy(buf+ATTR_HDR_LEN+ca->value.slen, pad,
4-(ca->value.slen & 0x03));
}
/* Done */
return PJ_SUCCESS;
}
@ -1378,11 +1399,13 @@ PJ_DEF(pj_status_t) pj_stun_unknown_attr_create(pj_pool_t *pool,
/* If the number of unknown attributes is an odd number, one of the
* attributes MUST be repeated in the list.
*/
/* No longer necessary
if ((attr_cnt & 0x01)) {
attr->attrs[attr_cnt] = attr_array[attr_cnt-1];
}
*/
*p_attr = NULL;
*p_attr = attr;
return PJ_SUCCESS;
}
@ -1636,6 +1659,13 @@ PJ_DEF(pj_status_t) pj_stun_msg_check(const pj_uint8_t *pdu, unsigned pdu_len,
return PJNATH_EINSTUNMSGLEN;
}
/* STUN message is always padded to the nearest 4 bytes, thus
* the last two bits of the length field are always zero.
*/
if ((msg_len & 0x03) != 0) {
return PJNATH_EINSTUNMSGLEN;
}
/* If magic is set, then there is great possibility that this is
* a STUN message.
*/
@ -1878,16 +1908,17 @@ PJ_DEF(pj_status_t) pj_stun_msg_decode(pj_pool_t *pool,
}
has_fingerprint = PJ_TRUE;
} else {
if (has_msg_int || has_fingerprint) {
if (has_fingerprint) {
/* Another attribute is found which is not FINGERPRINT
* after FINGERPRINT or MESSAGE-INTEGRITY */
* after FINGERPRINT. Note that non-FINGERPRINT is
* allowed to appear after M-I
*/
if (p_response) {
pj_stun_msg_create_response(pool, msg,
PJ_STUN_SC_BAD_REQUEST,
NULL, p_response);
}
return has_fingerprint ? PJNATH_ESTUNFINGERPOS :
PJNATH_ESTUNMSGINTPOS;
return PJNATH_ESTUNFINGERPOS;
}
}
@ -2114,16 +2145,11 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
}
}
/* We MUST update the message length in the header NOW before
* calculating MESSAGE-INTEGRITY and FINGERPRINT.
* Note that length is not including the 20 bytes header.
/* If MESSAGE-INTEGRITY is present, include the M-I attribute
* in message length before calculating M-I
*/
if (amsgint && afingerprint) {
body_len = (pj_uint16_t)((buf - start) - 20 + 24 + 8);
} else if (amsgint) {
if (amsgint) {
body_len = (pj_uint16_t)((buf - start) - 20 + 24);
} else if (afingerprint) {
body_len = (pj_uint16_t)((buf - start) - 20 + 8);
} else {
body_len = (pj_uint16_t)((buf - start) - 20);
}
@ -2161,11 +2187,12 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
*/
pj_hmac_sha1_init(&ctx, (pj_uint8_t*)key->ptr, key->slen);
pj_hmac_sha1_update(&ctx, (pj_uint8_t*)start, buf-start);
if ((buf-start) & 0x3F) {
pj_uint8_t zeroes[64];
pj_bzero(zeroes, sizeof(zeroes));
pj_hmac_sha1_update(&ctx, zeroes, 64-((buf-start) & 0x3F));
}
// These are obsoleted in rfc3489bis-08
//if ((buf-start) & 0x3F) {
// pj_uint8_t zeroes[64];
// pj_bzero(zeroes, sizeof(zeroes));
// pj_hmac_sha1_update(&ctx, zeroes, 64-((buf-start) & 0x3F));
//}
pj_hmac_sha1_final(&ctx, amsgint->hmac);
/* Put this attribute in the message */
@ -2180,6 +2207,10 @@ PJ_DEF(pj_status_t) pj_stun_msg_encode(pj_stun_msg *msg,
/* Calculate FINGERPRINT if present */
if (afingerprint != NULL) {
/* Update message length */
PUTVAL16H(start, 2,
(pj_uint16_t)(GETVAL16H(start, 2)+8));
afingerprint->value = pj_crc32_calc(start, buf-start);
afingerprint->value ^= STUN_XOR_FINGERPRINT;

View File

@ -192,6 +192,22 @@ static int print_attr(char *buffer, unsigned length,
APPLY();
}
break;
case PJ_STUN_ATTR_ICE_CONTROLLED:
case PJ_STUN_ATTR_ICE_CONTROLLING:
{
const pj_stun_uint64_attr *attr;
pj_uint8_t data[8];
int i;
attr = (const pj_stun_uint64_attr*) ahdr;
for (i=0; i<8; ++i)
data[i] = ((const pj_uint8_t*)&attr->value)[7-i];
len = print_binary(p, end-p, data, 8);
APPLY();
}
break;
case PJ_STUN_ATTR_USE_CANDIDATE:
default:
len = pj_ansi_snprintf(p, end-p, "\n");