static unsigned stun_agent_find_unknowns (StunAgent *agent, const StunMessage * msg, uint16_t *list, unsigned max) { unsigned count = 0; uint16_t len = stun_message_length (msg); size_t offset = 0; offset = STUN_MESSAGE_ATTRIBUTES_POS; while ((offset < len) && (count < max)) { size_t alen = stun_getw (msg->buffer + offset + STUN_ATTRIBUTE_TYPE_LEN); uint16_t atype = stun_getw (msg->buffer + offset); offset += STUN_ATTRIBUTE_VALUE_POS + stun_align (alen); if (!stun_optional (atype) && stun_agent_is_unknown (agent, atype)) { stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)\n", (unsigned)atype, (unsigned)alen); list[count++] = htons (atype); } } stun_debug ("STUN unknown: %u mandatory attribute(s)!\n", count); return count; }
static void test_hash_creds (void) { uint8_t md5[16]; uint8_t real_md5[] = { 0x84, 0x93, 0xfb, 0xc5, 0x3b, 0xa5, 0x82, 0xfb, 0x4c, 0x04, 0x4c, 0x45, 0x6b, 0xdc, 0x40, 0xeb}; puts ("Testing long term credentials hash algorithm..."); stun_hash_creds ("realm", strlen ("realm"), "user", strlen ("user"), "pass", strlen ("pass"), md5); stun_debug ("key for user:realm:pass is : "); stun_debug_bytes (md5, 16); stun_debug ("\n\n"); stun_debug ("RFC key for user:realm:pass is : "); stun_debug_bytes (real_md5, 16); stun_debug ("\n\n"); if(memcmp (md5, real_md5, sizeof(md5)) != 0) fatal ("MD5 hashes are different!"); puts ("Done!"); }
bool stun_agent_default_validater (StunAgent *agent, StunMessage *message, uint8_t *username, uint16_t username_len, uint8_t **password, size_t *password_len, void *user_data) { StunDefaultValidaterData* val = (StunDefaultValidaterData *) user_data; int i; for (i = 0; val && val[i].username ; i++) { #if 0 stun_debug ("Comparing username of size %d and %" PRIuPTR ": %d", username_len, val[i].username_len, (memcmp (username, val[i].username, username_len) == 0)); #endif stun_debug_bytes (" First username: "******" Second username: "******"Found valid username, returning password : '******'", *password); return TRUE; } } return FALSE; }
bool stun_agent_default_validater (StunAgent *agent, StunMessage *message, uint8_t *username, uint16_t username_len, uint8_t **password, size_t *password_len, void *user_data) { StunDefaultValidaterData* val = (StunDefaultValidaterData *) user_data; int i; for (i = 0; val && val[i].username ; i++) { stun_debug ("Comparing username '"); stun_debug_bytes (username, username_len); stun_debug ("' (%d) with '", username_len); stun_debug_bytes (val[i].username, val[i].username_len); stun_debug ("' (%d) : %d\n", val[i].username_len, memcmp (username, val[i].username, username_len)); if (username_len == val[i].username_len && memcmp (username, val[i].username, username_len) == 0) { *password = (uint8_t *) val[i].password; *password_len = val[i].password_len; stun_debug ("Found valid username, returning password : '******'\n", *password); return TRUE; } } return FALSE; }
void stun_debug_bytes (const void *data, size_t len) { size_t i; stun_debug ("0x"); for (i = 0; i < len; i++) stun_debug ("%02x", ((const unsigned char *)data)[i]); }
/** * Print debug information for the ICE Media * * @param pf Print function for debug output * @param icem ICE Media object * * @return 0 if success, otherwise errorcode */ int icem_debug(struct re_printf *pf, const struct icem *icem) { struct le *le; int err = 0; if (!icem) return 0; err |= re_hprintf(pf, "----- ICE Media <%s> -----\n", icem->name); err |= re_hprintf(pf, " Local Candidates: %H", icem_cands_debug, &icem->lcandl); err |= re_hprintf(pf, " Remote Candidates: %H", icem_cands_debug, &icem->rcandl); err |= re_hprintf(pf, " Check list: [%s]%H", ice_checkl_state2name(icem->state), icem_candpairs_debug, &icem->checkl); err |= re_hprintf(pf, " Valid list: %H", icem_candpairs_debug, &icem->validl); for (le = icem->compl.head; le; le = le->next) { const struct icem_comp *comp = le->data; if (comp->cp_sel) { err |= re_hprintf(pf, " Selected: %H\n", icem_candpair_debug, comp->cp_sel); } } err |= stun_debug(pf, icem->stun); return err; }
static int stun_bind_error (StunAgent *agent, StunMessage *msg, uint8_t *buf, size_t *plen, const StunMessage *req, StunError code) { size_t len = *plen; int val; *plen = 0; stun_debug ("STUN Error Reply (buffer size: %u)...\n", (unsigned)len); val = stun_agent_init_error (agent, msg, buf, len, req, code); if (!val) return val; len = stun_agent_finish_message (agent, msg, NULL, 0); if (len == 0) return 0; *plen = len; stun_debug (" Error response (%u) of %u bytes\n", (unsigned)code, (unsigned)*plen); return 1; }
StunUsageTurnReturn stun_usage_turn_refresh_process (StunMessage *msg, uint32_t *lifetime, StunUsageTurnCompatibility compatibility) { int code = -1; StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS; if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9 || compatibility == STUN_USAGE_TURN_COMPATIBILITY_RFC5766) { if (stun_message_get_method (msg) != STUN_REFRESH) return STUN_USAGE_TURN_RETURN_INVALID; } else { if (stun_message_get_method (msg) != STUN_ALLOCATE) return STUN_USAGE_TURN_RETURN_INVALID; } switch (stun_message_get_class (msg)) { case STUN_REQUEST: case STUN_INDICATION: return STUN_USAGE_TURN_RETURN_INVALID; case STUN_RESPONSE: break; case STUN_ERROR: if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) { /* missing ERROR-CODE: ignore message */ return STUN_USAGE_TURN_RETURN_INVALID; } return STUN_USAGE_TURN_RETURN_ERROR; default: /* Fall through. */ break; } stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime); stun_debug ("TURN Refresh successful!\n"); return ret; }
/** * Print debug information for the ICE Session * * @param pf Print function for debug output * @param ice ICE Session * * @return 0 if success, otherwise errorcode */ int ice_debug(struct re_printf *pf, const struct ice *ice) { struct le *le; int err = 0; if (!ice) return 0; err |= re_hprintf(pf, " local_mode=%s, remote_mode=%s", ice_mode2name(ice->lmode), ice_mode2name(ice->rmode)); err |= re_hprintf(pf, ", local_role=%s\n", ice_role2name(ice->lrole)); err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n", ice->lufrag, ice->lpwd); for (le = ice->ml.head; le; le = le->next) err |= icem_debug(pf, le->data); err |= stun_debug(pf, ice->stun); return err; }
/** * Print debug information for the ICE Media * * @param pf Print function for debug output * @param icem ICE Media object * * @return 0 if success, otherwise errorcode */ int icem_debug(struct re_printf *pf, const struct icem *icem) { struct le *le; int err = 0; if (!icem) return 0; err |= re_hprintf(pf, "----- ICE Media <%s> -----\n", icem->name); err |= re_hprintf(pf, " local_mode=%s, remote_mode=%s", ice_mode2name(icem->lmode), ice_mode2name(icem->rmode)); err |= re_hprintf(pf, ", local_role=%s\n", ice_role2name(icem->lrole)); err |= re_hprintf(pf, " local_ufrag=\"%s\" local_pwd=\"%s\"\n", icem->lufrag, icem->lpwd); err |= re_hprintf(pf, " Components: (%u)\n", list_count(&icem->compl)); for (le = icem->compl.head; le; le = le->next) { struct icem_comp *comp = le->data; err |= re_hprintf(pf, " %H\n", icecomp_debug, comp); } err |= re_hprintf(pf, " Local Candidates: %H", icem_cands_debug, &icem->lcandl); err |= re_hprintf(pf, " Remote Candidates: %H", icem_cands_debug, &icem->rcandl); err |= re_hprintf(pf, " Check list: [state=%s]%H", ice_checkl_state2name(icem->state), icem_candpairs_debug, &icem->checkl); err |= re_hprintf(pf, " Valid list: %H", icem_candpairs_debug, &icem->validl); err |= stun_debug(pf, icem->stun); return err; }
size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg, const uint8_t *key, size_t key_len) { uint8_t *ptr; uint32_t fpr; int saved_id_idx = 0; uint8_t md5[16]; if (stun_message_get_class (msg) == STUN_REQUEST) { for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) { if (agent->sent_ids[saved_id_idx].valid == FALSE) { break; } } } if (saved_id_idx == STUN_AGENT_MAX_SAVED_IDS) { stun_debug ("Saved ids full"); return 0; } if (msg->key != NULL) { key = msg->key; key_len = msg->key_len; } if (key != NULL) { bool skip = FALSE; if (msg->long_term_valid) { memcpy (md5, msg->long_term_key, sizeof(msg->long_term_key)); } else 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; 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) { skip = TRUE; } else { stun_hash_creds (realm, realm_len, username, username_len, key, key_len, md5); } memcpy (msg->long_term_key, md5, sizeof(msg->long_term_key)); msg->long_term_valid = TRUE; } /* If no realm/username and long term credentials, then don't send the message integrity */ if (skip == FALSE) { ptr = stun_message_append (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, 20); if (ptr == NULL) { return 0; } if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) { if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, md5, sizeof(md5), TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { size_t minus = 20; if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) minus -= 8; stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - minus, ptr, md5, sizeof(md5), TRUE); } else { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, md5, sizeof(md5), FALSE); } } else { if (agent->compatibility == STUN_COMPATIBILITY_RFC3489) { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, key, key_len, TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { size_t minus = 20; if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) minus -= 8; stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - minus, ptr, key, key_len, TRUE); } else { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, key, key_len, FALSE); } } stun_debug (" Message HMAC-SHA1 message integrity:" "\n key : "); stun_debug_bytes (key, key_len); stun_debug ("\n sent : "); stun_debug_bytes (ptr, 20); stun_debug ("\n"); } } if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 || agent->compatibility == STUN_COMPATIBILITY_WLM2009) && agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) { ptr = stun_message_append (msg, STUN_ATTRIBUTE_FINGERPRINT, 4); if (ptr == NULL) { return 0; } fpr = stun_fingerprint (msg->buffer, stun_message_length (msg), agent->compatibility == STUN_COMPATIBILITY_WLM2009); memcpy (ptr, &fpr, sizeof (fpr)); stun_debug (" Message HMAC-SHA1 fingerprint: "); stun_debug_bytes (ptr, 4); stun_debug ("\n"); } if (stun_message_get_class (msg) == STUN_REQUEST) { stun_message_id (msg, agent->sent_ids[saved_id_idx].id); agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg); agent->sent_ids[saved_id_idx].key = (uint8_t *) key; agent->sent_ids[saved_id_idx].key_len = key_len; memcpy (agent->sent_ids[saved_id_idx].long_term_key, msg->long_term_key, sizeof(msg->long_term_key)); agent->sent_ids[saved_id_idx].long_term_valid = msg->long_term_valid; agent->sent_ids[saved_id_idx].valid = TRUE; } msg->key = (uint8_t *) key; msg->key_len = key_len; return stun_message_length (msg); }
StunUsageTurnReturn stun_usage_turn_process (StunMessage *msg, struct sockaddr *relay_addr, socklen_t *relay_addrlen, struct sockaddr *addr, socklen_t *addrlen, struct sockaddr *alternate_server, socklen_t *alternate_server_len, uint32_t *bandwidth, uint32_t *lifetime, StunUsageTurnCompatibility compatibility) { int val, code = -1; StunUsageTurnReturn ret = STUN_USAGE_TURN_RETURN_RELAY_SUCCESS; if (stun_message_get_method (msg) != STUN_ALLOCATE) return STUN_USAGE_TURN_RETURN_INVALID; switch (stun_message_get_class (msg)) { case STUN_REQUEST: case STUN_INDICATION: return STUN_USAGE_TURN_RETURN_INVALID; case STUN_RESPONSE: break; case STUN_ERROR: if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) { /* missing ERROR-CODE: ignore message */ return STUN_USAGE_TURN_RETURN_INVALID; } /* NOTE: currently we ignore unauthenticated messages if the context * is authenticated, for security reasons. */ stun_debug (" STUN error message received (code: %d)\n", code); /* ALTERNATE-SERVER mechanism */ if ((code / 100) == 3) { if (alternate_server && alternate_server_len) { if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, alternate_server, alternate_server_len) != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute\n"); return STUN_USAGE_TURN_RETURN_ERROR; } } else { if (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER)) { stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute\n"); return STUN_USAGE_TURN_RETURN_ERROR; } } stun_debug ("Found alternate server\n"); return STUN_USAGE_TURN_RETURN_ALTERNATE_SERVER; } return STUN_USAGE_TURN_RETURN_ERROR; } stun_debug ("Received %u-bytes STUN message\n", stun_message_length (msg)); if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_DRAFT9) { val = stun_message_find_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen); if (val == STUN_MESSAGE_RETURN_SUCCESS) ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS; val = stun_message_find_xor_addr (msg, STUN_ATTRIBUTE_RELAY_ADDRESS, relay_addr, relay_addrlen); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" No RELAYED-ADDRESS: %d\n", val); return STUN_USAGE_TURN_RETURN_ERROR; } } else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_GOOGLE) { val = stun_message_find_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" No MAPPED-ADDRESS: %d\n", val); return STUN_USAGE_TURN_RETURN_ERROR; } } else if (compatibility == STUN_USAGE_TURN_COMPATIBILITY_MSN) { val = stun_message_find_addr (msg, STUN_ATTRIBUTE_MSN_MAPPED_ADDRESS, addr, addrlen); if (val == STUN_MESSAGE_RETURN_SUCCESS) ret = STUN_USAGE_TURN_RETURN_MAPPED_SUCCESS; val = stun_message_find_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, relay_addr, relay_addrlen); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" No MAPPED-ADDRESS: %d\n", val); return STUN_USAGE_TURN_RETURN_ERROR; } } stun_message_find32 (msg, STUN_ATTRIBUTE_LIFETIME, lifetime); stun_message_find32 (msg, STUN_ATTRIBUTE_BANDWIDTH, bandwidth); stun_debug (" Mapped address found!\n"); return ret; }
size_t stun_agent_finish_message (StunAgent *agent, StunMessage *msg, const uint8_t *key, size_t key_len) { uint8_t *ptr; uint32_t fpr; int saved_id_idx = 0; uint8_t md5[16]; bool remember_transaction; remember_transaction = (stun_message_get_class (msg) == STUN_REQUEST); if (agent->compatibility == STUN_COMPATIBILITY_OC2007 && stun_message_get_method (msg) == STUN_SEND) { /* As per [MS-TURN] Section 2.2.1, the TURN server doesn't send responses to * STUN_SEND requests, so don't bother waiting for them. More details at * https://msdn.microsoft.com/en-us/library/dd946797%28v=office.12%29.aspx. */ remember_transaction = FALSE; } if (remember_transaction) { for (saved_id_idx = 0; saved_id_idx < STUN_AGENT_MAX_SAVED_IDS; saved_id_idx++) { if (agent->sent_ids[saved_id_idx].valid == FALSE) { break; } } } if (saved_id_idx == STUN_AGENT_MAX_SAVED_IDS) { stun_debug ("WARNING: Saved IDs full. STUN message dropped."); return 0; } if (msg->key != NULL) { key = msg->key; key_len = msg->key_len; } if (key != NULL) { bool skip = FALSE; if (msg->long_term_valid) { memcpy (md5, msg->long_term_key, sizeof(msg->long_term_key)); } else 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; 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) { skip = TRUE; } else { stun_hash_creds (realm, realm_len, username, username_len, key, key_len, md5); memcpy (msg->long_term_key, md5, sizeof(msg->long_term_key)); msg->long_term_valid = TRUE; } } /* If no realm/username and long term credentials, then don't send the message integrity */ if (skip == FALSE) { ptr = stun_message_append (msg, STUN_ATTRIBUTE_MESSAGE_INTEGRITY, 20); if (ptr == NULL) { return 0; } if (agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS) { if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 || agent->compatibility == STUN_COMPATIBILITY_OC2007) { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, md5, sizeof(md5), TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { size_t minus = 20; if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) minus -= 8; stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - minus, ptr, md5, sizeof(md5), TRUE); } else { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, md5, sizeof(md5), FALSE); } } else { if (agent->compatibility == STUN_COMPATIBILITY_RFC3489 || agent->compatibility == STUN_COMPATIBILITY_OC2007) { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, key, key_len, TRUE); } else if (agent->compatibility == STUN_COMPATIBILITY_WLM2009) { size_t minus = 20; if (agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) minus -= 8; stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - minus, ptr, key, key_len, TRUE); } else { stun_sha1 (msg->buffer, stun_message_length (msg), stun_message_length (msg) - 20, ptr, key, key_len, FALSE); } } stun_debug (" Message HMAC-SHA1 message integrity:"); stun_debug_bytes (" key : ", key, key_len); stun_debug_bytes (" sent : ", ptr, 20); } } if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 || agent->compatibility == STUN_COMPATIBILITY_WLM2009) && agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) { ptr = stun_message_append (msg, STUN_ATTRIBUTE_FINGERPRINT, 4); if (ptr == NULL) { return 0; } fpr = stun_fingerprint (msg->buffer, stun_message_length (msg), agent->compatibility == STUN_COMPATIBILITY_WLM2009); memcpy (ptr, &fpr, sizeof (fpr)); stun_debug_bytes (" Message HMAC-SHA1 fingerprint: ", ptr, 4); } if (remember_transaction) { stun_message_id (msg, agent->sent_ids[saved_id_idx].id); agent->sent_ids[saved_id_idx].method = stun_message_get_method (msg); agent->sent_ids[saved_id_idx].key = (uint8_t *) key; agent->sent_ids[saved_id_idx].key_len = key_len; memcpy (agent->sent_ids[saved_id_idx].long_term_key, msg->long_term_key, sizeof(msg->long_term_key)); agent->sent_ids[saved_id_idx].long_term_valid = msg->long_term_valid; agent->sent_ids[saved_id_idx].valid = TRUE; } msg->key = (uint8_t *) key; msg->key_len = key_len; return stun_message_length (msg); }
StunUsageIceReturn stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, StunMessage *msg, uint8_t *buf, size_t *plen, const struct sockaddr *src, socklen_t srclen, bool *control, uint64_t tie, StunUsageIceCompatibility compatibility) { const char *username = NULL; uint16_t username_len; size_t len = *plen; uint64_t q; StunMessageReturn val = STUN_MESSAGE_RETURN_SUCCESS; StunUsageIceReturn ret = STUN_USAGE_ICE_RETURN_SUCCESS; #define err( code ) \ stun_bind_error (agent, msg, buf, &len, req, code); \ *plen = len *plen = 0; stun_debug ("STUN Reply (buffer size = %u)...\n", (unsigned)len); if (stun_message_get_class (req) != STUN_REQUEST) { stun_debug (" Unhandled non-request (class %u) message.\n", stun_message_get_class (req)); return STUN_USAGE_ICE_RETURN_INVALID_REQUEST; } if (stun_message_get_method (req) != STUN_BINDING) { stun_debug (" Bad request (method %u) message.\n", stun_message_get_method (req)); err (STUN_ERROR_BAD_REQUEST); return STUN_USAGE_ICE_RETURN_INVALID_METHOD; } /* Role conflict handling */ assert (control != NULL); if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("STUN Role Conflict detected:\n"); if (tie < q) { stun_debug (" switching role from \"controll%s\" to \"controll%s\"\n", *control ? "ing" : "ed", *control ? "ed" : "ing"); *control = !*control; ret = STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; } else { stun_debug (" staying \"controll%s\" (sending error)\n", *control ? "ing" : "ed"); err (STUN_ERROR_ROLE_CONFLICT); return STUN_USAGE_ICE_RETURN_SUCCESS; } } else { stun_debug ("STUN Role not specified by peer!\n"); } if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) { stun_debug ("Unable to create response\n"); goto failure; } if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) { StunTransactionId transid; uint32_t magic_cookie; stun_message_id (msg, transid); magic_cookie = *((uint32_t *) transid); val = stun_message_append_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen, htonl (magic_cookie)); } else if (stun_message_has_cookie (msg)) { val = stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen); } else { val = stun_message_append_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, src, srclen); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" Mapped address problem: %d\n", val); goto failure; } username = (const char *)stun_message_find (req, STUN_ATTRIBUTE_USERNAME, &username_len); if (username) { val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, username, username_len); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("Error appending username: %d\n", val); goto failure; } /* the stun agent will automatically use the password of the request */ len = stun_agent_finish_message (agent, msg, NULL, 0); if (len == 0) goto failure; *plen = len; stun_debug (" All done (response size: %u)\n", (unsigned)len); return ret; failure: assert (*plen == 0); stun_debug (" Fatal error formatting Response: %d\n", val); switch (val) { case STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE: return STUN_USAGE_ICE_RETURN_MEMORY_ERROR; case STUN_MESSAGE_RETURN_INVALID: case STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS: return STUN_USAGE_ICE_RETURN_INVALID_ADDRESS; default: return STUN_USAGE_ICE_RETURN_ERROR; } }
StunUsageBindReturn stun_usage_bind_process (StunMessage *msg, struct sockaddr *addr, socklen_t *addrlen, struct sockaddr *alternate_server, socklen_t *alternate_server_len) { int code = -1; StunMessageReturn val; if (stun_message_get_method (msg) != STUN_BINDING) return STUN_USAGE_BIND_RETURN_INVALID; switch (stun_message_get_class (msg)) { case STUN_REQUEST: case STUN_INDICATION: return STUN_USAGE_BIND_RETURN_INVALID; case STUN_RESPONSE: break; case STUN_ERROR: if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) { /* missing ERROR-CODE: ignore message */ return STUN_USAGE_BIND_RETURN_INVALID; } /* NOTE: currently we ignore unauthenticated messages if the context * is authenticated, for security reasons. */ stun_debug (" STUN error message received (code: %d)", code); /* ALTERNATE-SERVER mechanism */ if ((code / 100) == 3) { if (alternate_server && alternate_server_len) { if (stun_message_find_addr (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER, (struct sockaddr_storage *) alternate_server, alternate_server_len) != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute"); return STUN_USAGE_BIND_RETURN_ERROR; } } else { if (!stun_message_has_attribute (msg, STUN_ATTRIBUTE_ALTERNATE_SERVER)) { stun_debug (" Unexpectedly missing ALTERNATE-SERVER attribute"); return STUN_USAGE_BIND_RETURN_ERROR; } } stun_debug ("Found alternate server"); return STUN_USAGE_BIND_RETURN_ALTERNATE_SERVER; } return STUN_USAGE_BIND_RETURN_ERROR; default: /* Fall through. */ break; } stun_debug ("Received %u-bytes STUN message", stun_message_length (msg)); val = stun_message_find_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, (struct sockaddr_storage *)addr, addrlen); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" No XOR-MAPPED-ADDRESS: %d", val); val = stun_message_find_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, (struct sockaddr_storage *)addr, addrlen); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" No MAPPED-ADDRESS: %d", val); return STUN_USAGE_BIND_RETURN_ERROR; } } stun_debug (" Mapped address found!"); return STUN_USAGE_BIND_RETURN_SUCCESS; }
StunUsageIceReturn stun_usage_ice_conncheck_create_reply (StunAgent *agent, StunMessage *req, StunMessage *msg, uint8_t *buf, size_t *plen, const struct sockaddr_storage *src, socklen_t srclen, bool *control, uint64_t tie, StunUsageIceCompatibility compatibility) { const char *username = NULL; uint16_t username_len; size_t len = *plen; uint64_t q; StunMessageReturn val = STUN_MESSAGE_RETURN_SUCCESS; StunUsageIceReturn ret = STUN_USAGE_ICE_RETURN_SUCCESS; #define err( code ) \ stun_bind_error (agent, msg, buf, &len, req, code); \ *plen = len *plen = 0; stun_debug ("STUN Reply (buffer size = %u)...", (unsigned)len); if (stun_message_get_class (req) != STUN_REQUEST) { stun_debug (" Unhandled non-request (class %u) message.", stun_message_get_class (req)); return STUN_USAGE_ICE_RETURN_INVALID_REQUEST; } if (stun_message_get_method (req) != STUN_BINDING) { stun_debug (" Bad request (method %u) message.", stun_message_get_method (req)); err (STUN_ERROR_BAD_REQUEST); return STUN_USAGE_ICE_RETURN_INVALID_METHOD; } /* Role conflict handling */ assert (control != NULL); if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLING : STUN_ATTRIBUTE_ICE_CONTROLLED, &q) == STUN_MESSAGE_RETURN_SUCCESS) { /* we have the ice-controlling/controlled attribute, * and there's a role conflict */ stun_debug ("STUN Role Conflict detected:"); /* According to ICE RFC 5245, section 7.2.1.1, we consider the four * possible cases when a role conflict is detected: two cases are * resolved by switching role locally, and the two other cases are * handled by responding with a STUN error. */ if ((tie < q && *control) || (tie >= q && !*control)) { stun_debug (" switching role from \"controll%s\" to \"controll%s\"", *control ? "ing" : "ed", *control ? "ed" : "ing"); *control = !*control; ret = STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; } else { stun_debug (" staying \"controll%s\" (sending error)", *control ? "ing" : "ed"); err (STUN_ERROR_ROLE_CONFLICT); return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; } } else { if (stun_message_find64 (req, *control ? STUN_ATTRIBUTE_ICE_CONTROLLED : STUN_ATTRIBUTE_ICE_CONTROLLING, &q) != STUN_MESSAGE_RETURN_SUCCESS) { /* we don't have the expected ice-controlling/controlled * attribute */ if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_RFC5245 || compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2) { stun_debug ("STUN Role not specified by peer!"); } } } if (stun_agent_init_response (agent, msg, buf, len, req) == FALSE) { stun_debug ("Unable to create response"); goto failure; } if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) { union { StunTransactionId transid; uint32_t magic_cookie; } conv; stun_message_id (msg, conv.transid); val = stun_message_append_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen, htonl (conv.magic_cookie)); } else if (stun_message_has_cookie (msg) && compatibility != STUN_USAGE_ICE_COMPATIBILITY_GOOGLE) { val = stun_message_append_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, src, srclen); } else { val = stun_message_append_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, (struct sockaddr *) src, srclen); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" Mapped address problem: %d", val); goto failure; } username = (const char *)stun_message_find (req, STUN_ATTRIBUTE_USERNAME, &username_len); if (username) { val = stun_message_append_bytes (msg, STUN_ATTRIBUTE_USERNAME, username, username_len); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("Error appending username: %d", val); goto failure; } if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSICE2) { val = stun_message_append32 (msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION, 2); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug ("Error appending implementation version: %d", val); goto failure; } } /* the stun agent will automatically use the password of the request */ len = stun_agent_finish_message (agent, msg, NULL, 0); if (len == 0) goto failure; *plen = len; stun_debug (" All done (response size: %u)", (unsigned)len); return ret; failure: assert (*plen == 0); stun_debug (" Fatal error formatting Response: %d", val); switch (val) { case STUN_MESSAGE_RETURN_NOT_ENOUGH_SPACE: return STUN_USAGE_ICE_RETURN_MEMORY_ERROR; case STUN_MESSAGE_RETURN_INVALID: case STUN_MESSAGE_RETURN_UNSUPPORTED_ADDRESS: return STUN_USAGE_ICE_RETURN_INVALID_ADDRESS; case STUN_MESSAGE_RETURN_SUCCESS: assert (0); /* shouldn’t be reached */ case STUN_MESSAGE_RETURN_NOT_FOUND: default: return STUN_USAGE_ICE_RETURN_ERROR; } }
/* * Timer callback that handles scheduling new candidate discovery * processes (paced by the Ta timer), and handles running of the * existing discovery processes. * * This function is designed for the g_timeout_add() interface. * * @return will return FALSE when no more pending timers. */ static gboolean priv_discovery_tick_unlocked (gpointer pointer) { CandidateDiscovery *cand; NiceAgent *agent = pointer; GSList *i; int not_done = 0; /* note: track whether to continue timer */ size_t buffer_len = 0; { static int tick_counter = 0; if (tick_counter++ % 50 == 0) nice_debug ("Agent %p : discovery tick #%d with list %p (1)", agent, tick_counter, agent->discovery_list); } for (i = agent->discovery_list; i ; i = i->next) { cand = i->data; if (cand->pending != TRUE) { cand->pending = TRUE; if (agent->discovery_unsched_items) --agent->discovery_unsched_items; if (nice_debug_is_enabled ()) { gchar tmpbuf[INET6_ADDRSTRLEN]; nice_address_to_string (&cand->server, tmpbuf); nice_debug ("Agent %p : discovery - scheduling cand type %u addr %s.", agent, cand->type, tmpbuf); } if (nice_address_is_valid (&cand->server) && (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE || cand->type == NICE_CANDIDATE_TYPE_RELAYED)) { if (cand->component->state == NICE_COMPONENT_STATE_DISCONNECTED || cand->component->state == NICE_COMPONENT_STATE_FAILED) agent_signal_component_state_change (agent, cand->stream->id, cand->component->id, NICE_COMPONENT_STATE_GATHERING); if (cand->type == NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE) { buffer_len = stun_usage_bind_create (&cand->stun_agent, &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer)); } else if (cand->type == NICE_CANDIDATE_TYPE_RELAYED) { uint8_t *username = (uint8_t *)cand->turn->username; gsize username_len = strlen (cand->turn->username); uint8_t *password = (uint8_t *)cand->turn->password; gsize password_len = strlen (cand->turn->password); StunUsageTurnCompatibility turn_compat = agent_to_turn_compatibility (agent); if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN || turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) { username = g_base64_decode ((gchar *)username, &username_len); password = g_base64_decode ((gchar *)password, &password_len); } buffer_len = stun_usage_turn_create (&cand->stun_agent, &cand->stun_message, cand->stun_buffer, sizeof(cand->stun_buffer), cand->stun_resp_msg.buffer == NULL ? NULL : &cand->stun_resp_msg, STUN_USAGE_TURN_REQUEST_PORT_NORMAL, -1, -1, username, username_len, password, password_len, turn_compat); if (turn_compat == STUN_USAGE_TURN_COMPATIBILITY_MSN || turn_compat == STUN_USAGE_TURN_COMPATIBILITY_OC2007) { g_free (username); g_free (password); } } if (buffer_len > 0) { if (nice_socket_is_reliable (cand->nicesock)) { stun_timer_start_reliable (&cand->timer, agent->stun_reliable_timeout); } else { stun_timer_start (&cand->timer, agent->stun_initial_timeout, agent->stun_max_retransmissions); } /* send the conncheck */ agent_socket_send (cand->nicesock, &cand->server, buffer_len, (gchar *)cand->stun_buffer); /* case: success, start waiting for the result */ g_get_current_time (&cand->next_tick); } else { /* case: error in starting discovery, start the next discovery */ cand->done = TRUE; cand->stun_message.buffer = NULL; cand->stun_message.buffer_len = 0; continue; } } else /* allocate relayed candidates */ g_assert_not_reached (); ++not_done; /* note: new discovery scheduled */ } if (cand->done != TRUE) { GTimeVal now; g_get_current_time (&now); if (cand->stun_message.buffer == NULL) { nice_debug ("Agent %p : STUN discovery was cancelled, marking discovery done.", agent); cand->done = TRUE; } else if (priv_timer_expired (&cand->next_tick, &now)) { switch (stun_timer_refresh (&cand->timer)) { case STUN_USAGE_TIMER_RETURN_TIMEOUT: { /* Time out */ /* case: error, abort processing */ StunTransactionId id; stun_message_id (&cand->stun_message, id); stun_agent_forget_transaction (&cand->stun_agent, id); cand->done = TRUE; cand->stun_message.buffer = NULL; cand->stun_message.buffer_len = 0; nice_debug ("Agent %p : bind discovery timed out, aborting discovery item.", agent); break; } case STUN_USAGE_TIMER_RETURN_RETRANSMIT: { /* case: not ready complete, so schedule next timeout */ unsigned int timeout = stun_timer_remainder (&cand->timer); stun_debug ("STUN transaction retransmitted (timeout %dms).", timeout); /* retransmit */ agent_socket_send (cand->nicesock, &cand->server, stun_message_length (&cand->stun_message), (gchar *)cand->stun_buffer); /* note: convert from milli to microseconds for g_time_val_add() */ cand->next_tick = now; g_time_val_add (&cand->next_tick, timeout * 1000); ++not_done; /* note: retry later */ break; } case STUN_USAGE_TIMER_RETURN_SUCCESS: { unsigned int timeout = stun_timer_remainder (&cand->timer); cand->next_tick = now; g_time_val_add (&cand->next_tick, timeout * 1000); ++not_done; /* note: retry later */ break; } default: /* Nothing to do. */ break; } } else { ++not_done; /* note: discovery not expired yet */ } } } if (not_done == 0) { nice_debug ("Agent %p : Candidate gathering FINISHED, stopping discovery timer.", agent); discovery_free (agent); agent_gathering_done (agent); /* note: no pending timers, return FALSE to stop timer */ return FALSE; } return TRUE; }
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; }
StunUsageIceReturn stun_usage_ice_conncheck_process (StunMessage *msg, struct sockaddr *addr, socklen_t *addrlen, StunUsageIceCompatibility compatibility) { int code = -1; StunMessageReturn val; if (stun_message_get_method (msg) != STUN_BINDING) return STUN_USAGE_ICE_RETURN_INVALID; switch (stun_message_get_class (msg)) { case STUN_REQUEST: case STUN_INDICATION: return STUN_USAGE_ICE_RETURN_INVALID; case STUN_RESPONSE: break; case STUN_ERROR: if (stun_message_find_error (msg, &code) != STUN_MESSAGE_RETURN_SUCCESS) { /* missing ERROR-CODE: ignore message */ return STUN_USAGE_ICE_RETURN_INVALID; } if (code == STUN_ERROR_ROLE_CONFLICT) return STUN_USAGE_ICE_RETURN_ROLE_CONFLICT; /* NOTE: currently we ignore unauthenticated messages if the context * is authenticated, for security reasons. */ stun_debug (" STUN error message received (code: %d)\n", code); return STUN_USAGE_ICE_RETURN_ERROR; } stun_debug ("Received %u-bytes STUN message\n", stun_message_length (msg)); if (compatibility == STUN_USAGE_ICE_COMPATIBILITY_MSN) { StunTransactionId transid; uint32_t magic_cookie; stun_message_id (msg, transid); magic_cookie = *((uint32_t *) transid); val = stun_message_find_xor_addr_full (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen, htonl (magic_cookie)); } else { val = stun_message_find_xor_addr (msg, STUN_ATTRIBUTE_XOR_MAPPED_ADDRESS, addr, addrlen); } if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" No XOR-MAPPED-ADDRESS: %d\n", val); val = stun_message_find_addr (msg, STUN_ATTRIBUTE_MAPPED_ADDRESS, addr, addrlen); if (val != STUN_MESSAGE_RETURN_SUCCESS) { stun_debug (" No MAPPED-ADDRESS: %d\n", val); return STUN_USAGE_ICE_RETURN_NO_MAPPED_ADDRESS; } } stun_debug ("Mapped address found!\n"); return STUN_USAGE_ICE_RETURN_SUCCESS; }