static void validate (const uint8_t *msg, unsigned len) { unsigned i = 1; do { size_t vlen = stun_message_validate_buffer_length (msg, i, TRUE); if ((vlen & 3) || (vlen != ((i >= len) * len))) fatal ("%u/%u short message test failed", i, len); } while (i++ < (len + 4)); }
StunValidationStatus stun_agent_validate (StunAgent *agent, StunMessage *msg, const uint8_t *buffer, size_t buffer_len, StunMessageIntegrityValidate validater, void * validater_data) { StunTransactionId msg_id; uint32_t fpr; uint32_t crc32; int len; uint8_t *username = NULL; uint16_t username_len; uint8_t *key = NULL; size_t key_len; uint8_t *hash; uint8_t sha[20]; uint16_t hlen; int sent_id_idx = -1; uint16_t unknown; int error_code; int ignore_credentials = 0; uint8_t long_term_key[16]; bool long_term_key_valid = FALSE; len = stun_message_validate_buffer_length (buffer, buffer_len); if (len == STUN_MESSAGE_BUFFER_INVALID) { return STUN_VALIDATION_NOT_STUN; } else if (len == STUN_MESSAGE_BUFFER_INCOMPLETE) { return STUN_VALIDATION_INCOMPLETE_STUN; } else if (len != (int) buffer_len) { return STUN_VALIDATION_NOT_STUN; } msg->buffer = (uint8_t *) buffer; msg->buffer_len = buffer_len; msg->agent = agent; msg->key = NULL; msg->key_len = 0; msg->long_term_valid = FALSE; /* TODO: reject it or not ? */ if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 || agent->compatibility == STUN_COMPATIBILITY_WLM2009) && !stun_message_has_cookie (msg)) { stun_debug ("STUN demux error: no cookie!\n"); return STUN_VALIDATION_BAD_REQUEST; } if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 || agent->compatibility == STUN_COMPATIBILITY_WLM2009) && agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) { /* Looks for FINGERPRINT */ if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("STUN demux error: no FINGERPRINT attribute!\n"); return STUN_VALIDATION_BAD_REQUEST; } /* Checks FINGERPRINT */ crc32 = stun_fingerprint (msg->buffer, stun_message_length (msg), agent->compatibility == STUN_COMPATIBILITY_WLM2009); fpr = ntohl (fpr); if (fpr != crc32) { stun_debug ("STUN demux error: bad fingerprint: 0x%08x," " expected: 0x%08x!\n", fpr, crc32); return STUN_VALIDATION_BAD_REQUEST; } stun_debug ("STUN demux: OK!\n"); } if (stun_message_get_class (msg) == STUN_RESPONSE || stun_message_get_class (msg) == STUN_ERROR) { stun_message_id (msg, msg_id); for (sent_id_idx = 0; sent_id_idx < STUN_AGENT_MAX_SAVED_IDS; sent_id_idx++) { if (agent->sent_ids[sent_id_idx].valid == TRUE && agent->sent_ids[sent_id_idx].method == stun_message_get_method (msg) && memcmp (msg_id, agent->sent_ids[sent_id_idx].id, sizeof(StunTransactionId)) == 0) { key = agent->sent_ids[sent_id_idx].key; key_len = agent->sent_ids[sent_id_idx].key_len; memcpy (long_term_key, agent->sent_ids[sent_id_idx].long_term_key, sizeof(long_term_key)); long_term_key_valid = agent->sent_ids[sent_id_idx].long_term_valid; break; } } if (sent_id_idx == STUN_AGENT_MAX_SAVED_IDS) { return STUN_VALIDATION_UNMATCHED_RESPONSE; } } ignore_credentials = (agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) || (stun_message_get_class (msg) == STUN_ERROR && stun_message_find_error (msg, &error_code) == STUN_MESSAGE_RETURN_SUCCESS && (error_code == 400 || error_code == 401)) || (stun_message_get_class (msg) == STUN_INDICATION && (agent->usage_flags & STUN_AGENT_USAGE_NO_INDICATION_AUTH)); if (key == NULL && ignore_credentials == 0 && (stun_message_get_class (msg) == STUN_REQUEST || stun_message_get_class (msg) == STUN_INDICATION) && (((agent->usage_flags & STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS) && (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY))) || ((agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) && stun_message_get_class (msg) == STUN_REQUEST && (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_NONCE) || !stun_message_has_attribute (msg, STUN_ATTRIBUTE_REALM))) || ((agent->usage_flags & STUN_AGENT_USAGE_IGNORE_CREDENTIALS) == 0 && stun_message_has_attribute (msg, STUN_ATTRIBUTE_USERNAME) && !stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY)))) { return STUN_VALIDATION_UNAUTHORIZED_BAD_REQUEST; } if (stun_message_has_attribute (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY) && ((key == NULL && ignore_credentials == 0) || (agent->usage_flags & STUN_AGENT_USAGE_FORCE_VALIDATER))) { username_len = 0; username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME, &username_len); if (validater == NULL || validater (agent, msg, username, username_len, &key, &key_len, validater_data) == FALSE) { return STUN_VALIDATION_UNAUTHORIZED; } } if (ignore_credentials == 0 && key != NULL && key_len > 0) { hash = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, &hlen); if (hash) { /* We must give the size from start to the end of the attribute because you might have a FINGERPRINT attribute after it... */ if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) { uint8_t *realm = NULL; uint8_t *username = NULL; uint16_t realm_len; uint16_t username_len; uint8_t md5[16]; if (long_term_key_valid) { memcpy (md5, long_term_key, sizeof (md5)); } else { realm = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_REALM, &realm_len); username = (uint8_t *) stun_message_find (msg, STUN_ATTRIBUTE_USERNAME, &username_len); if (username == NULL || realm == NULL) { return STUN_VALIDATION_UNAUTHORIZED; } stun_hash_creds (realm, realm_len, username, username_len, key, key_len, md5); } memcpy (msg->long_term_key, md5, sizeof(md5)); msg->long_term_valid = TRUE; if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, md5, sizeof(md5), TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, stun_message_length (msg) - 20, sha, md5, sizeof(md5), TRUE); } else { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, md5, sizeof(md5), FALSE); } } else { if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, key, key_len, TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, stun_message_length (msg) - 20, sha, key, key_len, TRUE); } else { stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer, sha, key, key_len, FALSE); } } stun_debug (" Message HMAC-SHA1 fingerprint:"); stun_debug ("\nkey : "); stun_debug_bytes (key, key_len); stun_debug ("\n expected: "); stun_debug_bytes (sha, sizeof (sha)); stun_debug ("\n received: "); stun_debug_bytes (hash, sizeof (sha)); stun_debug ("\n"); if (memcmp (sha, hash, sizeof (sha))) { stun_debug ("STUN auth error: SHA1 fingerprint mismatch!\n"); return STUN_VALIDATION_UNAUTHORIZED; } stun_debug ("STUN auth: OK!\n"); msg->key = key; msg->key_len = key_len; } else if (!(stun_message_get_class (msg) == STUN_ERROR && stun_message_find_error (msg, &error_code) == STUN_MESSAGE_RETURN_SUCCESS && (error_code == 400 || error_code == 401))) { stun_debug ("STUN auth error: No message integrity attribute!\n"); return STUN_VALIDATION_UNAUTHORIZED; } } if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) { agent->sent_ids[sent_id_idx].valid = FALSE; } if (stun_agent_find_unknowns (agent, msg, &unknown, 1) > 0) { if (stun_message_get_class (msg) == STUN_REQUEST) return STUN_VALIDATION_UNKNOWN_REQUEST_ATTRIBUTE; else return STUN_VALIDATION_UNKNOWN_ATTRIBUTE; } return STUN_VALIDATION_SUCCESS; }
/* Tests for generic message validation routines */ static void test_message (void) { static const uint8_t extra_garbage[] = {0x15, 0x55, 0x00, 0x00, 0x21, 0x12, 0xA4, 0x42, // cookie 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0xaa, 0xbb, 0xcc, 0xdd}; //extra garbage static const uint8_t simple_resp[] = {0x15, 0x55, 0x00, 0x00, 0x21, 0x12, 0xA4, 0x42, // cookie 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}; static const uint8_t old_ind[] = {0x14, 0x55, 0x00, 0x00, 0xfe, 0xdc, 0xba, 0x98, // NO cookie 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10}; static const uint8_t fpr_resp[] = {0x15, 0x55, 0x00, 0x10, 0x21, 0x12, 0xA4, 0x42, // cookie 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x00, 0x06, 0x00, 0x04, // dummy USERNAME header 0x41, 0x42, 0x43, 0x44, 0x80, 0x28, 0x00, 0x04, // FINGERPRINT header 0xdc, 0x8d, 0xa7, 0x74}; // CRC32; static const uint8_t bad1[32] = {0x15, 0x55, 0x00, 0x08, 0x21, 0x12, 0xA4, 0x42, // cookie 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x00, 0x06, 0x00, 0x05, // too big attribute for message 0x11, 0x22, 0x33, 0x44, 0x55, 0x66, 0x77, 0x88}; static const uint8_t bad2[24] = {0x15, 0x55, 0x00, 0x05, // invalid message length 0x21, 0x12, 0xA4, 0x42, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x00, 0x06, 0x00, 0x01}; static const uint8_t bad3[27] = {0x15, 0x55, 0x00, 0x08, 0x21, 0x12, 0xA4, 0x42, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x00, 0x06, 0x00, 0x03, 0x11, 0x22, 0x33}; // missing padding static const uint8_t bad_crc[] = {0x15, 0x55, 0x00, 0x08, 0x21, 0x12, 0xA4, 0x42, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x76, 0x54, 0x32, 0x10, 0x80, 0x28, 0x00, 0x04, // FINGERPRINT header 0x04, 0x91, 0xcd, 0x78}; // CRC32 static uint8_t bad_crc_offset[] = {0x15, 0x55, 0x00, 0x10, 0x21, 0x12, 0xA4, 0x42, 0x76, 0x54, 0x32, 0x10, 0xfe, 0xdc, 0xba, 0x98, 0x20, 0x67, 0xc4, 0x09, 0x80, 0x28, 0x00, 0x04, // FINGERPRINT header 0x00, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x04, 0x41, 0x42, 0x43, 0x44}; static unsigned char req[] = {0x00, 0x01, 0x00, 0x00, 0x8b, 0x45, 0x9b, 0xc3, 0xe7, 0x7a, 0x05, 0xb3, 0xe4, 0xfe, 0x01, 0xf0, 0xaf, 0x83, 0xe1, 0x9e}; static uint8_t binding_error_resp[] = {0x01, 0x11, 0x00, 0x84, 0x8b, 0x45, 0x9b, 0xc3, 0xe7, 0x7a, 0x05, 0xb3, 0xe4, 0xfe, 0x01, 0xf0, 0xaf, 0x83, 0xe1, 0x9e, 0x00, 0x06, 0x00, 0x48, // USERNAME 0x92, 0x6b, 0x2b, 0x3e, 0x6a, 0xa5, 0x43, 0x58, 0xa8, 0x51, 0x25, 0xa6, 0xf7, 0x9c, 0x0a, 0xe7, 0xd8, 0x86, 0xf7, 0x76, 0xf9, 0xcd, 0x8a, 0x2e, 0x45, 0xd7, 0xcb, 0xbb, 0xae, 0xe5, 0x03, 0xc3, 0x3a, 0x32, 0x3a, 0xa9, 0x9e, 0xb7, 0x7b, 0x32, 0xe3, 0xf3, 0xa6, 0xc0, 0xe8, 0x54, 0x4b, 0xef, 0x52, 0xd2, 0xe2, 0xc0, 0x43, 0xc2, 0x4c, 0xbc, 0xaf, 0xd9, 0xf2, 0xfa, 0x48, 0x8b, 0x8c, 0xe6, 0x62, 0x14, 0x64, 0x3a, 0x32, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x1c, // ERROR-CODE 0x00, 0x00, 0x04, 0x1f, 0x49, 0x6e, 0x74, 0x65, 0x67, 0x72, 0x69, 0x74, 0x79, 0x20, 0x43, 0x68, 0x65, 0x63, 0x6b, 0x20, 0x46, 0x61, 0x69, 0x6c, 0x75, 0x72, 0x65, 0x2e, 0x00, 0x08, 0x00, 0x14, // MESSAGE-INTEGRITY 0xf7, 0x46, 0x81, 0xc4, 0x6f, 0x4c, 0x21, 0x5c, 0xf6, 0x8e, 0xc0, 0x81, 0x0e, 0x20, 0x3f, 0xb1, 0xb1, 0xad, 0xa4, 0x8a}; StunAgent agent; StunAgent agent2; StunMessage msg; uint16_t known_attributes[] = {STUN_ATTRIBUTE_USERNAME, STUN_ATTRIBUTE_ERROR_CODE, STUN_ATTRIBUTE_MESSAGE_INTEGRITY}; uint8_t username_v[] = {0x92, 0x6b, 0x2b, 0x3e, 0x6a, 0xa5, 0x43, 0x58, 0xa8, 0x51, 0x25, 0xa6, 0xf7, 0x9c, 0x0a, 0xe7, 0xd8, 0x86, 0xf7, 0x76, 0xf9, 0xcd, 0x8a, 0x2e, 0x45, 0xd7, 0xcb, 0xbb, 0xae, 0xe5, 0x03, 0xc3, 0x3a, 0x32, 0x3a, 0xa9, 0x9e, 0xb7, 0x7b, 0x32, 0xe3, 0xf3, 0xa6, 0xc0, 0xe8, 0x54, 0x4b, 0xef, 0x52, 0xd2, 0xe2, 0xc0, 0x43, 0xc2, 0x4c, 0xbc, 0xaf, 0xd9, 0xf2, 0xfa, 0x48, 0x8b, 0x8c, 0xe6, 0x62, 0x14, 0x64, 0x3a, 0x32, 0x00, 0x00, 0x00}; uint8_t password_v[] = {0x77, 0xd9, 0x7a, 0xe9, 0xcf, 0xe0, 0x3e, 0xa2, 0x28, 0xa0, 0x5d, 0xec, 0xcf, 0x36, 0xe8, 0x49}; StunDefaultValidaterData v = {username_v, 72, password_v, 16}; stun_agent_init (&agent, known_attributes, STUN_COMPATIBILITY_RFC5389, STUN_AGENT_USAGE_USE_FINGERPRINT); stun_agent_init (&agent2, known_attributes, STUN_COMPATIBILITY_RFC3489, STUN_AGENT_USAGE_SHORT_TERM_CREDENTIALS); stun_agent_validate (&agent2, &msg, req, sizeof(req), NULL, NULL); stun_agent_finish_message (&agent2, &msg, NULL, 0); if (stun_agent_validate (&agent2, &msg, binding_error_resp, sizeof(binding_error_resp), stun_agent_default_validater, &v) != STUN_VALIDATION_SUCCESS) fatal ("Binding Error Response failed"); if (stun_message_validate_buffer_length (NULL, 0, TRUE) != STUN_MESSAGE_BUFFER_INVALID) fatal ("0 bytes test failed"); if (stun_message_validate_buffer_length ((uint8_t *)"\xf0", 1, TRUE) >= 0) fatal ("1 byte test failed"); if (stun_message_validate_buffer_length (bad1, sizeof (bad1), TRUE) >= 0) fatal ("Badness 1 test failed"); if (stun_message_validate_buffer_length (bad2, sizeof (bad2), TRUE) >= 0) fatal ("Badness 2 test failed"); if (stun_message_validate_buffer_length (bad3, sizeof (bad3), TRUE) != 0) fatal ("Badness 3 test failed"); validate (simple_resp, 20); validate (old_ind, 20); validate (fpr_resp, 36); if (stun_agent_validate (&agent, &msg, extra_garbage, sizeof(extra_garbage), NULL, NULL) != STUN_VALIDATION_NOT_STUN) fatal ("Extra garbage test failed"); if (stun_agent_validate (&agent, &msg, simple_resp, sizeof(simple_resp), NULL, NULL) != STUN_VALIDATION_BAD_REQUEST) fatal ("Missing CRC test failed"); if (stun_agent_validate (&agent, &msg, old_ind, sizeof(old_ind), NULL, NULL) != STUN_VALIDATION_BAD_REQUEST) fatal ("Missing cookie test failed"); if (stun_agent_validate (&agent, &msg, bad_crc, sizeof(bad_crc), NULL, NULL) != STUN_VALIDATION_BAD_REQUEST) fatal ("Bad CRC test failed"); if (stun_agent_validate (&agent, &msg, bad_crc_offset, sizeof(bad_crc_offset), NULL, NULL) != STUN_VALIDATION_BAD_REQUEST) fatal ("Bad CRC offset test failed"); if (stun_agent_validate (&agent, &msg, fpr_resp, sizeof(fpr_resp), NULL, NULL) != STUN_VALIDATION_UNMATCHED_RESPONSE) fatal ("Good CRC test failed"); if (stun_message_get_class (&msg) != 3) fatal ("Class test failed"); if (stun_message_get_method (&msg) != 0x525) fatal ("Method test failed"); }