libnice/stun/stunagent.c
Matthew Waters 4a378b880e agent: implement support for RFC7675 - Consent Freshness
Specified in https://tools.ietf.org/html/rfc7675

RFC 7675 is a slight modification of the existing keepalive connection
checks that could be enabled manually or were used with the GOOGLE
compatibility mode.

Slight differences from the existing keepalive connection checks
include:
- an additional consent expiry timer instead of relying on all binding
  requests to succeed.
- 403: 'Forbidden' stun error-code which revokes consent with immediate
  effect.
2020-12-10 11:27:53 -05:00

746 lines
24 KiB
C

/*
* This file is part of the Nice GLib ICE library.
*
* (C) 2008-2009 Collabora Ltd.
* Contact: Youness Alaoui
* (C) 2008-2009 Nokia Corporation. All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is the Nice GLib ICE library.
*
* The Initial Developers of the Original Code are Collabora Ltd and Nokia
* Corporation. All Rights Reserved.
*
* Contributors:
* Youness Alaoui, Collabora Ltd.
*
* Alternatively, the contents of this file may be used under the terms of the
* the GNU Lesser General Public License Version 2.1 (the "LGPL"), in which
* case the provisions of LGPL are applicable instead of those above. If you
* wish to allow use of your version of this file only under the terms of the
* LGPL and not to allow others to use your version of this file under the
* MPL, indicate your decision by deleting the provisions above and replace
* them with the notice and other provisions required by the LGPL. If you do
* not delete the provisions above, a recipient may use your version of this
* file under either the MPL or the LGPL.
*/
#ifdef HAVE_CONFIG_H
# include <config.h>
#endif
#include "stunmessage.h"
#include "stunagent.h"
#include "stunhmac.h"
#include "stun5389.h"
#include "utils.h"
#include <string.h>
#include <stdlib.h>
#include <inttypes.h>
static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type);
static unsigned stun_agent_find_unknowns (StunAgent *agent,
const StunMessage * msg, uint16_t *list, unsigned max);
void stun_agent_init (StunAgent *agent, const uint16_t *known_attributes,
StunCompatibility compatibility, StunAgentUsageFlags usage_flags)
{
int i;
agent->known_attributes = (uint16_t *) known_attributes;
agent->compatibility = compatibility;
agent->usage_flags = usage_flags;
agent->software_attribute = NULL;
agent->ms_ice2_send_legacy_connchecks =
compatibility == STUN_COMPATIBILITY_MSICE2;
for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
agent->sent_ids[i].valid = 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++) {
#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: ", username, username_len);
stun_debug_bytes (" Second username: ", val[i].username,
val[i].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 : '%s'", *password);
return TRUE;
}
}
return FALSE;
}
static bool stun_agent_check_fingerprint(StunAgent *agent, StunMessage *msg)
{
uint32_t fpr;
uint32_t crc32;
uint16_t msg_len;
/* Looks for FINGERPRINT */
if (stun_message_find32 (msg, STUN_ATTRIBUTE_FINGERPRINT, &fpr) !=
STUN_MESSAGE_RETURN_SUCCESS) {
stun_debug ("STUN demux error: no FINGERPRINT attribute!");
return FALSE;
}
msg_len = stun_message_length (msg);
/* Checks FINGERPRINT */
crc32 = stun_fingerprint (msg->buffer, msg_len, FALSE);
fpr = ntohl (fpr);
if (fpr != crc32) {
uint16_t palen;
/* [MS-ICE2] 3.1.4.8.2 Connectivity Checks Phase - legacy compatibility */
if (agent->compatibility == STUN_COMPATIBILITY_MSICE2 &&
stun_message_find (msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION,
&palen) == NULL &&
fpr == stun_fingerprint (msg->buffer, msg_len, TRUE)) {
return TRUE;
}
stun_debug ("STUN demux error: bad fingerprint: 0x%08x, expected: 0x%08x!",
fpr, crc32);
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;
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;
uint32_t implementation_version;
int sent_id_idx = -1;
uint16_t unknown;
int error_code;
int ignore_credentials = 0;
uint8_t long_term_key[16] = { 0 };
bool long_term_key_valid = FALSE;
len = stun_message_validate_buffer_length (buffer, buffer_len,
!(agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES));
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_MSICE2) &&
!stun_message_has_cookie (msg)) {
stun_debug ("STUN demux error: no cookie!");
return STUN_VALIDATION_BAD_REQUEST;
}
if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
agent->usage_flags & STUN_AGENT_USAGE_USE_FINGERPRINT) {
if (stun_agent_check_fingerprint(agent, msg) == FALSE) {
return STUN_VALIDATION_BAD_REQUEST;
}
stun_debug ("STUN demux: OK!");
}
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 == STUN_ERROR_BAD_REQUEST ||
error_code == STUN_ERROR_UNAUTHORIZED ||
error_code == STUN_ERROR_STALE_NONCE ||
error_code == STUN_ERROR_TRY_ALTERNATE)) ||
(stun_message_get_class (msg) == STUN_INDICATION &&
(agent->usage_flags & STUN_AGENT_USAGE_LONG_TERM_CREDENTIALS ||
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;
uint16_t realm_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 ||
agent->compatibility == STUN_COMPATIBILITY_OC2007) {
stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
sha, md5, sizeof(md5), TRUE);
} else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
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 ||
agent->compatibility == STUN_COMPATIBILITY_OC2007) {
stun_sha1 (msg->buffer, hash + 20 - msg->buffer, hash - msg->buffer,
sha, key, key_len, TRUE);
} else if (agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
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_bytes (" key : ", key, key_len);
stun_debug_bytes (" expected: ", sha, sizeof (sha));
stun_debug_bytes (" received: ", hash, sizeof (sha));
if (memcmp (sha, hash, sizeof (sha))) {
stun_debug ("STUN auth error: SHA1 fingerprint mismatch!");
return STUN_VALIDATION_UNAUTHORIZED;
}
stun_debug ("STUN auth: OK!");
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 == STUN_ERROR_BAD_REQUEST ||
error_code == STUN_ERROR_UNAUTHORIZED))) {
stun_debug ("STUN auth error: No message integrity attribute!");
return STUN_VALIDATION_UNAUTHORIZED;
}
}
if (agent->usage_flags & STUN_AGENT_USAGE_CONSENT_FRESHNESS &&
stun_message_get_class (msg) == STUN_ERROR) {
stun_message_find_error (msg, &error_code);
if (error_code == STUN_ERROR_FORBIDDEN) {
return STUN_VALIDATION_FORBIDDEN;
}
}
if (sent_id_idx != -1 && sent_id_idx < STUN_AGENT_MAX_SAVED_IDS) {
agent->sent_ids[sent_id_idx].valid = FALSE;
}
/* [MS-ICE2] 3.1.4.8.2 stop sending additional connectivity checks */
if (stun_message_find32(msg, STUN_ATTRIBUTE_MS_IMPLEMENTATION_VERSION,
&implementation_version) == STUN_MESSAGE_RETURN_SUCCESS) {
msg->agent->ms_ice2_send_legacy_connchecks = 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;
}
bool stun_agent_forget_transaction (StunAgent *agent, StunTransactionId id)
{
int i;
for (i = 0; i < STUN_AGENT_MAX_SAVED_IDS; i++) {
if (agent->sent_ids[i].valid == TRUE &&
memcmp (id, agent->sent_ids[i].id,
sizeof(StunTransactionId)) == 0) {
agent->sent_ids[i].valid = FALSE;
return TRUE;
}
}
return FALSE;
}
bool stun_agent_init_request (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, StunMethod m)
{
bool ret;
StunTransactionId id;
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = NULL;
msg->key_len = 0;
msg->long_term_valid = FALSE;
stun_make_transid (id);
ret = stun_message_init (msg, STUN_REQUEST, m, id);
if (ret) {
if (agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
}
if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
(agent->software_attribute != NULL ||
agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
stun_message_append_software (msg, agent->software_attribute);
}
}
return ret;
}
bool stun_agent_init_indication (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, StunMethod m)
{
bool ret;
StunTransactionId id;
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = NULL;
msg->key_len = 0;
msg->long_term_valid = FALSE;
stun_make_transid (id);
ret = stun_message_init (msg, STUN_INDICATION, m, id);
if (ret) {
if (agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
agent->compatibility == STUN_COMPATIBILITY_MSICE2) {
uint32_t cookie = htonl (STUN_MAGIC_COOKIE);
memcpy (msg->buffer + STUN_MESSAGE_TRANS_ID_POS, &cookie, sizeof (cookie));
}
}
return ret;
}
bool stun_agent_init_response (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, const StunMessage *request)
{
StunTransactionId id;
if (stun_message_get_class (request) != STUN_REQUEST) {
return FALSE;
}
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = request->key;
msg->key_len = request->key_len;
memmove (msg->long_term_key, request->long_term_key,
sizeof(msg->long_term_key));
msg->long_term_valid = request->long_term_valid;
stun_message_id (request, id);
if (stun_message_init (msg, STUN_RESPONSE,
stun_message_get_method (request), id)) {
if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
(agent->software_attribute != NULL ||
agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
stun_message_append_software (msg, agent->software_attribute);
}
return TRUE;
}
return FALSE;
}
bool stun_agent_init_error (StunAgent *agent, StunMessage *msg,
uint8_t *buffer, size_t buffer_len, const StunMessage *request,
StunError err)
{
StunTransactionId id;
if (stun_message_get_class (request) != STUN_REQUEST) {
return FALSE;
}
msg->buffer = buffer;
msg->buffer_len = buffer_len;
msg->agent = agent;
msg->key = request->key;
msg->key_len = request->key_len;
memmove (msg->long_term_key, request->long_term_key,
sizeof(msg->long_term_key));
msg->long_term_valid = request->long_term_valid;
stun_message_id (request, id);
if (stun_message_init (msg, STUN_ERROR,
stun_message_get_method (request), id)) {
if ((agent->compatibility == STUN_COMPATIBILITY_RFC5389 ||
agent->compatibility == STUN_COMPATIBILITY_MSICE2) &&
(agent->software_attribute != NULL ||
agent->usage_flags & STUN_AGENT_USAGE_ADD_SOFTWARE)) {
stun_message_append_software (msg, agent->software_attribute);
}
if (stun_message_append_error (msg, err) == STUN_MESSAGE_RETURN_SUCCESS) {
return TRUE;
}
}
return FALSE;
}
size_t stun_agent_build_unknown_attributes_error (StunAgent *agent,
StunMessage *msg, uint8_t *buffer, size_t buffer_len,
const StunMessage *request)
{
unsigned counter;
uint16_t ids[STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES];
counter = stun_agent_find_unknowns (agent, request,
ids, STUN_AGENT_MAX_UNKNOWN_ATTRIBUTES);
if (stun_agent_init_error (agent, msg, buffer, buffer_len,
request, STUN_ERROR_UNKNOWN_ATTRIBUTE) == FALSE) {
return 0;
}
/* NOTE: Old RFC3489 compatibility:
* When counter is odd, duplicate one value for 32-bits padding. */
if (!stun_message_has_cookie (request) && (counter & 1))
ids[counter++] = ids[0];
if (stun_message_append_bytes (msg, STUN_ATTRIBUTE_UNKNOWN_ATTRIBUTES,
ids, counter * 2) == STUN_MESSAGE_RETURN_SUCCESS) {
return stun_agent_finish_message (agent, msg, request->key, request->key_len);
}
return 0;
}
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_MSICE2) {
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_MSICE2) {
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_MSICE2) &&
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), FALSE);
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);
}
static bool stun_agent_is_unknown (StunAgent *agent, uint16_t type)
{
uint16_t *known_attr = agent->known_attributes;
while(*known_attr != 0) {
if (*known_attr == type) {
return FALSE;
}
known_attr++;
}
return TRUE;
}
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);
if (!stun_optional (atype) && stun_agent_is_unknown (agent, atype))
{
stun_debug ("STUN unknown: attribute 0x%04x(%u bytes)",
(unsigned)atype, (unsigned)alen);
list[count++] = htons (atype);
}
if (!(agent->usage_flags & STUN_AGENT_USAGE_NO_ALIGNED_ATTRIBUTES))
alen = stun_align (alen);
offset += STUN_ATTRIBUTE_VALUE_POS + alen;
}
stun_debug ("STUN unknown: %u mandatory attribute(s)!", count);
return count;
}
void stun_agent_set_software (StunAgent *agent, const char *software)
{
agent->software_attribute = software;
}