Commit 5c9ad0e1 authored by Ghislain MARY's avatar Ghislain MARY

Handle TURN request authentication.

parent 78c20768
......@@ -21,6 +21,7 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define ice_h
#include <mediastreamer2/mscommon.h>
#include <mediastreamer2/stun.h>
#include <ortp/ortp.h>
......@@ -103,6 +104,8 @@ struct _IceCheckList;
*/
typedef struct _IceSession {
struct _IceCheckList * streams[ICE_SESSION_MAX_CHECK_LISTS]; /**< Table of IceChecklist structure pointers. Each element represents a media stream */
MSStunAuthRequestedCb stun_auth_requested_cb; /**< Callback called when authentication is requested */
void *stun_auth_requested_userdata; /**< Userdata to pass to the STUN authentication requested callback */
char *local_ufrag; /**< Local username fragment for the session (assigned during the session creation) */
char *local_pwd; /**< Local password for the session (assigned during the session creation) */
char *remote_ufrag; /**< Remote username fragment for the session (provided via SDP by the peer) */
......@@ -136,6 +139,11 @@ typedef struct _IceStunServerCheck {
RtpTransport *rtptp;
int srcport;
MSList *transactions; /**< List of IceStunServerCheckTransaction structures. */
char *realm;
char *nonce;
char *username;
char *password;
char *ha1;
MSTimeSpec next_transmission_time;
bool_t responded;
} IceStunServerCheck;
......@@ -508,6 +516,8 @@ MS2_PUBLIC void ice_session_enable_forced_relay(IceSession *session, bool_t enab
*/
MS2_PUBLIC void ice_session_enable_turn(IceSession *session, bool_t enable);
MS2_PUBLIC void ice_session_set_stun_auth_requested_cb(IceSession *session, MSStunAuthRequestedCb cb, void *userdata);
/**
* Tell the average round trip time during the gathering process for an ICE session in ms.
*
......
......@@ -122,9 +122,11 @@ typedef struct {
UInt96 tr_id;
char *username;
char *password;
char *ha1;
char *realm;
char *message_integrity;
char *software;
char *nonce;
MSStunErrorCode error_code;
MSStunAddress mapped_address;
MSStunAddress xor_mapped_address;
......@@ -158,9 +160,13 @@ extern "C"
{
#endif
typedef void (*MSStunAuthRequestedCb)(void *userdata, const char *realm, const char *nonce, const char **username, const char **password, const char **ha1);
MS2_PUBLIC MSStunAddress4 ms_stun_hostname_to_stun_addr(const char *hostname, uint16_t default_port);
MS2_PUBLIC char * ms_stun_calculate_integrity_short_term(const char *buf, size_t bufsize, const char *key);
MS2_PUBLIC char * ms_stun_calculate_integrity_long_term(const char *buf, size_t bufsize, const char *realm, const char *username, const char *password);
MS2_PUBLIC char * ms_stun_calculate_integrity_long_term_from_ha1(const char *buf, size_t bufsize, const char *ha1_text);
MS2_PUBLIC uint32_t ms_stun_calculate_fingerprint(const char *buf, size_t bufsize);
MS2_PUBLIC MSStunMessage * ms_stun_message_create(uint16_t type, uint16_t method);
......@@ -184,10 +190,13 @@ MS2_PUBLIC void ms_stun_message_set_username(MSStunMessage *msg, const char *use
MS2_PUBLIC void ms_stun_message_include_username_attribute(MSStunMessage *msg, bool_t include);
MS2_PUBLIC const char * ms_stun_message_get_password(const MSStunMessage *msg);
MS2_PUBLIC void ms_stun_message_set_password(MSStunMessage *msg, const char *password);
MS2_PUBLIC void ms_stun_message_set_ha1(MSStunMessage *msg, const char *ha1_text);
MS2_PUBLIC const char * ms_stun_message_get_realm(const MSStunMessage *msg);
MS2_PUBLIC void ms_stun_message_set_realm(MSStunMessage *msg, const char *realm);
MS2_PUBLIC const char * ms_stun_message_get_software(const MSStunMessage *msg);
MS2_PUBLIC void ms_stun_message_set_software(MSStunMessage *msg, const char *software);
MS2_PUBLIC const char * ms_stun_message_get_nonce(const MSStunMessage *msg);
MS2_PUBLIC void ms_stun_message_set_nonce(MSStunMessage *msg, const char *nonce);
MS2_PUBLIC bool_t ms_stun_message_has_error_code(const MSStunMessage *msg);
MS2_PUBLIC uint16_t ms_stun_message_get_error_code(const MSStunMessage *msg, char **reason);
MS2_PUBLIC void ms_stun_message_set_error_code(MSStunMessage *msg, uint16_t number, const char *reason);
......
......@@ -340,6 +340,10 @@ static void ice_free_stun_server_check_transaction(IceStunServerCheckTransaction
static void ice_free_stun_server_check(IceStunServerCheck *check)
{
ms_list_for_each(check->transactions, (void (*)(void*))ice_free_stun_server_check_transaction);
if (check->realm != NULL) ms_free(check->realm);
if (check->nonce != NULL) ms_free(check->nonce);
if (check->username != NULL) ms_free(check->username);
if (check->password != NULL) ms_free(check->password);
ms_free(check);
}
......@@ -404,6 +408,52 @@ void ice_check_list_destroy(IceCheckList *cl)
}
static void ice_stun_server_check_set_realm(IceStunServerCheck *check, const char *realm)
{
if (check->realm != NULL) {
ms_free(check->realm);
check->realm = NULL;
}
if (realm != NULL) check->realm = ms_strdup(realm);
}
static void ice_stun_server_check_set_nonce(IceStunServerCheck *check, const char *nonce)
{
if (check->nonce != NULL) {
ms_free(check->nonce);
check->nonce = NULL;
}
if (nonce != NULL) check->nonce = ms_strdup(nonce);
}
static void ice_stun_server_check_set_username(IceStunServerCheck *check, const char *username)
{
if (check->username != NULL) {
ms_free(check->username);
check->username = NULL;
}
if (username != NULL) check->username = ms_strdup(username);
}
static void ice_stun_server_check_set_password(IceStunServerCheck *check, const char *password)
{
if (check->password != NULL) {
ms_free(check->password);
check->password = NULL;
}
if (password != NULL) check->password = ms_strdup(password);
}
static void ice_stun_server_check_set_ha1(IceStunServerCheck *check, const char *ha1)
{
if (check->ha1 != NULL) {
ms_free(check->ha1);
check->ha1 = NULL;
}
if (ha1 != NULL) check->ha1 = ms_strdup(ha1);
}
/******************************************************************************
* CANDIDATE ACCESSORS *
*****************************************************************************/
......@@ -1030,6 +1080,12 @@ void ice_session_enable_turn(IceSession *session, bool_t enable)
session->turn_enabled = enable;
}
void ice_session_set_stun_auth_requested_cb(IceSession *session, MSStunAuthRequestedCb cb, void *userdata)
{
session->stun_auth_requested_cb = cb;
session->stun_auth_requested_userdata = userdata;
}
static void ice_transaction_sum_gathering_round_trip_time(const IceStunServerCheckTransaction *transaction, StunRequestRoundTripTime *rtt)
{
if ((transaction->response_time.tv_sec != 0) && (transaction->response_time.tv_nsec != 0)) {
......@@ -1203,6 +1259,13 @@ static void stun_address_to_str(const MSStunAddress *stun_address, char *addr, i
static void ice_send_turn_server_allocate_request(RtpTransport *rtptp, const struct sockaddr *server, socklen_t addrlen, IceStunServerCheck *check)
{
MSStunMessage *msg = ms_turn_allocate_request_create();
if (check->realm != NULL) ms_stun_message_set_realm(msg, check->realm);
if (check->nonce != NULL) ms_stun_message_set_nonce(msg, check->nonce);
if (check->username != NULL) ms_stun_message_set_username(msg, check->username);
if (check->password != NULL) ms_stun_message_set_password(msg, check->password);
if (check->ha1 != NULL) ms_stun_message_set_ha1(msg, check->ha1);
if ((check->realm != NULL) || (check->nonce != NULL) || (check->username != NULL) || (check->password != NULL) || (check->ha1 != NULL))
ms_stun_message_enable_message_integrity(msg, TRUE);
ice_send_stun_request(rtptp, server, addrlen, check, msg, "TURN allocate request");
ms_stun_message_destroy(msg);
}
......@@ -2082,50 +2145,86 @@ static void ice_handle_received_binding_response(IceCheckList *cl, RtpSession *r
ice_conclude_processing(cl, rtp_session);
}
static void ice_handle_received_error_response(IceCheckList *cl, RtpSession *rtp_session, const MSStunMessage *msg)
static void ice_handle_stun_server_error_response(IceCheckList *cl, RtpSession *rtp_session, const OrtpEventData *evt_data, const MSStunMessage *msg)
{
IceCandidatePair *pair;
UInt96 tr_id = ms_stun_message_get_tr_id(msg);
MSList *elem = ms_list_find_custom(cl->transaction_list, (MSCompareFunc)ice_find_pair_from_transactionID, &tr_id);
if (elem == NULL) {
/* We received an error response concerning an unknown binding request, ignore it... */
return;
MSList *elem;
RtpTransport *rtptp = NULL;
char *reason = NULL;
uint16_t number = ms_stun_message_get_error_code(msg, &reason);
ice_get_transport_from_rtp_session(rtp_session, evt_data, &rtptp);
elem = ms_list_find_custom(cl->stun_server_checks, (MSCompareFunc)ice_find_stun_server_check, rtptp);
if (elem != NULL) {
IceStunServerCheck *check = (IceStunServerCheck *)elem->data;
if ((check != NULL) && (number == 401) && (cl->session->stun_auth_requested_cb != NULL)) {
const char *username = NULL;
const char *password = NULL;
const char *ha1 = NULL;
const char *realm = ms_stun_message_get_realm(msg);
const char *nonce = ms_stun_message_get_nonce(msg);
cl->session->stun_auth_requested_cb(cl->session->stun_auth_requested_userdata, realm, nonce, &username, &password, &ha1);
if ((username != NULL) && (cl->session->turn_enabled)) {
ice_stun_server_check_set_realm(check, realm);
ice_stun_server_check_set_nonce(check, nonce);
ice_stun_server_check_set_username(check, username);
ice_stun_server_check_set_password(check, password);
ice_stun_server_check_set_ha1(check, ha1);
check->next_transmission_time = ice_add_ms(ice_current_time(), ICE_DEFAULT_RTO_DURATION);
ice_send_turn_server_allocate_request(rtptp, (struct sockaddr *)&cl->session->ss, cl->session->ss_len, check);
}
}
}
}
pair = (IceCandidatePair *)((IceTransaction *)elem->data)->pair;
if (ms_stun_message_has_error_code(msg)
&& (ms_stun_message_get_error_code(msg, NULL) == MS_STUN_ERROR_CODE_UNAUTHORIZED)
&& pair->retry_with_dummy_message_integrity) {
ms_warning("ice pair [%p], retry skipping message integrity for compatibility with older version",pair);
pair->retry_with_dummy_message_integrity=FALSE;
pair->use_dummy_hmac=TRUE;
return;
static void ice_handle_received_error_response(IceCheckList *cl, RtpSession *rtp_session, const OrtpEventData *evt_data, const MSStunMessage *msg)
{
IceCandidatePair *pair;
if (cl->gathering_candidates == TRUE) {
ice_handle_stun_server_error_response(cl, rtp_session, evt_data, msg);
} else {
ice_pair_set_state(pair, ICP_Failed);
ms_message("ice: Error response, set state to Failed for pair %p: %s:%u:%s --> %s:%u:%s", pair,
pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type]);
}
if (ms_stun_message_has_error_code(msg) && (ms_stun_message_get_error_code(msg, NULL) == MS_ICE_ERROR_CODE_ROLE_CONFLICT)) {
/* Handle error 487 (Role Conflict) according to 7.1.3.1. */
switch (pair->role) {
case IR_Controlling:
ms_message("ice: Switch to the CONTROLLED role");
ice_session_set_role(cl->session, IR_Controlled);
break;
case IR_Controlled:
ms_message("ice: Switch to the CONTROLLING role");
ice_session_set_role(cl->session, IR_Controlling);
break;
UInt96 tr_id = ms_stun_message_get_tr_id(msg);
MSList *elem = ms_list_find_custom(cl->transaction_list, (MSCompareFunc)ice_find_pair_from_transactionID, &tr_id);
if (elem == NULL) {
/* We received an error response concerning an unknown binding request, ignore it... */
return;
}
/* Set the state of the pair to Waiting and trigger a check. */
ice_pair_set_state(pair, ICP_Waiting);
ice_check_list_queue_triggered_check(cl, pair);
}
pair = (IceCandidatePair *)((IceTransaction *)elem->data)->pair;
if (ms_stun_message_has_error_code(msg)
&& (ms_stun_message_get_error_code(msg, NULL) == MS_STUN_ERROR_CODE_UNAUTHORIZED)
&& pair->retry_with_dummy_message_integrity) {
ms_warning("ice pair [%p], retry skipping message integrity for compatibility with older version",pair);
pair->retry_with_dummy_message_integrity=FALSE;
pair->use_dummy_hmac=TRUE;
return;
ice_conclude_processing(cl, rtp_session);
} else {
ice_pair_set_state(pair, ICP_Failed);
ms_message("ice: Error response, set state to Failed for pair %p: %s:%u:%s --> %s:%u:%s", pair,
pair->local->taddr.ip, pair->local->taddr.port, candidate_type_values[pair->local->type],
pair->remote->taddr.ip, pair->remote->taddr.port, candidate_type_values[pair->remote->type]);
}
if (ms_stun_message_has_error_code(msg) && (ms_stun_message_get_error_code(msg, NULL) == MS_ICE_ERROR_CODE_ROLE_CONFLICT)) {
/* Handle error 487 (Role Conflict) according to 7.1.3.1. */
switch (pair->role) {
case IR_Controlling:
ms_message("ice: Switch to the CONTROLLED role");
ice_session_set_role(cl->session, IR_Controlled);
break;
case IR_Controlled:
ms_message("ice: Switch to the CONTROLLING role");
ice_session_set_role(cl->session, IR_Controlling);
break;
}
/* Set the state of the pair to Waiting and trigger a check. */
ice_pair_set_state(pair, ICP_Waiting);
ice_check_list_queue_triggered_check(cl, pair);
}
ice_conclude_processing(cl, rtp_session);
}
}
void ice_handle_stun_packet(IceCheckList *cl, RtpSession *rtp_session, const OrtpEventData *evt_data)
......@@ -2187,7 +2286,7 @@ void ice_handle_stun_packet(IceCheckList *cl, RtpSession *rtp_session, const Ort
ice_handle_received_binding_response(cl, rtp_session, evt_data, msg, &remote_addr);
} else if (ms_stun_message_is_error_response(msg)) {
ms_message("ice: Recv error response: %s:%u <-- %s:%u [%s]", source_addr_str, recvport, src6host, remote_port, tr_id_str);
ice_handle_received_error_response(cl, rtp_session, msg);
ice_handle_received_error_response(cl, rtp_session, evt_data, msg);
} else if (ms_stun_message_is_indication(msg)) {
ms_message("ice: Recv indication: %s:%u <-- %s:%u [%s]", source_addr_str, recvport, src6host, remote_port, tr_id_str);
} else {
......
......@@ -31,6 +31,8 @@ Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#define STUN_MAX_USERNAME_LENGTH 513
#define STUN_MAX_REASON_LENGTH 127
#define STUN_MAX_SOFTWARE_LENGTH 763 /* Length in bytes, it is supposed to be less than 128 UTF-8 characters (TODO) */
#define STUN_MAX_REALM_LENGTH 127
#define STUN_MAX_NONCE_LENGTH 127
#define STUN_STR_SETTER(field, value) \
......@@ -204,6 +206,15 @@ static void encode_integrity(StunMessageEncoder *encoder, const char *hmac) {
encode(encoder, hmac, 20);
}
static void encode_long_term_integrity_from_ha1(StunMessageEncoder *encoder, const char *ha1_text) {
char *hmac;
size_t message_length = stun_message_encoder_get_message_length(encoder);
encode_message_length(encoder, message_length - STUN_MESSAGE_HEADER_LENGTH + 24);
hmac = ms_stun_calculate_integrity_long_term_from_ha1(encoder->buffer, message_length, ha1_text);
encode_integrity(encoder, hmac);
ms_free(hmac);
}
static void encode_long_term_integrity(StunMessageEncoder *encoder, const char *realm, const char *username, const char *password) {
char *hmac;
size_t message_length = stun_message_encoder_get_message_length(encoder);
......@@ -506,17 +517,30 @@ char * ms_stun_calculate_integrity_short_term(const char *buf, size_t bufsize, c
return hmac;
}
char * ms_stun_calculate_integrity_long_term(const char *buf, size_t bufsize, const char *realm, const char *username, const char *password) {
unsigned char HA1[16];
char HA1_text[1024];
char * ms_stun_calculate_integrity_long_term_from_ha1(const char *buf, size_t bufsize, const char *ha1_text) {
unsigned char ha1[16];
unsigned int i, j;
char *hmac = ms_malloc(21);
memset(hmac, 0, 21);
snprintf(HA1_text, sizeof(HA1_text), "%s:%s:%s", username, realm, password);
bctbx_md5((unsigned char *)HA1_text, strlen(HA1_text), HA1);
memset(ha1, 0, sizeof(ha1));
for (i = 0, j = 0; (i < strlen(ha1_text)) && (j < sizeof(ha1)); i += 2, j++) {
char buf[5] = { '0', 'x', ha1_text[i], ha1_text[i + 1], '\0' };
ha1[j] = strtol(buf, NULL, 0);
}
/* SHA1 output length is 20 bytes, get them all */
bctbx_hmacSha1(ha1, sizeof(ha1), (const unsigned char *)buf, bufsize, 20, (unsigned char *)hmac);
return hmac;
}
char * ms_stun_calculate_integrity_long_term(const char *buf, size_t bufsize, const char *realm, const char *username, const char *password) {
unsigned char ha1[16];
char ha1_text[1024];
char *hmac = ms_malloc(21);
memset(hmac, 0, 21);
snprintf(ha1_text, sizeof(ha1_text), "%s:%s:%s", username, realm, password);
bctbx_md5((unsigned char *)ha1_text, strlen(ha1_text), ha1);
/* SHA1 output length is 20 bytes, get them all */
bctbx_hmacSha1(HA1, sizeof(HA1), (const unsigned char *)buf, bufsize, 20, (unsigned char *)hmac);
bctbx_hmacSha1(ha1, sizeof(ha1), (const unsigned char *)buf, bufsize, 20, (unsigned char *)hmac);
return hmac;
}
......@@ -700,6 +724,20 @@ MSStunMessage * ms_stun_message_create_from_buffer_parsing(const char *buf, size
case MS_TURN_ATTR_LIFETIME:
ms_stun_message_set_lifetime(msg, decode_lifetime(&decoder, length));
break;
case MS_STUN_ATTR_REALM:
{
char *realm = decode_string(&decoder, length, STUN_MAX_REALM_LENGTH);
ms_stun_message_set_realm(msg, realm);
if (realm != NULL) ms_free(realm);
}
break;
case MS_STUN_ATTR_NONCE:
{
char *nonce = decode_string(&decoder, length, STUN_MAX_NONCE_LENGTH);
ms_stun_message_set_nonce(msg, nonce);
if (nonce != NULL) ms_free(nonce);
}
break;
default:
if (type <= 0x7FFF) {
ms_error("STUN unknown Comprehension-Required attribute: 0x%04x", type);
......@@ -781,6 +819,8 @@ size_t ms_stun_message_encode(const MSStunMessage *msg, char **buf) {
if (stun_addr != NULL) encode_addr(&encoder, MS_STUN_ATTR_MAPPED_ADDRESS, stun_addr);
if (msg->change_request != 0) encode_change_request(&encoder, msg->change_request);
if (msg->username != NULL) encode_string(&encoder, MS_STUN_ATTR_USERNAME, msg->username, STUN_MAX_USERNAME_LENGTH);
if (msg->realm != NULL) encode_string(&encoder, MS_STUN_ATTR_REALM, msg->realm, STUN_MAX_REALM_LENGTH);
if (msg->nonce != NULL) encode_string(&encoder, MS_STUN_ATTR_NONCE, msg->nonce, STUN_MAX_NONCE_LENGTH);
if (ms_stun_message_has_error_code(msg)) {
char *reason = NULL;
uint16_t number = ms_stun_message_get_error_code(msg, &reason);
......@@ -802,7 +842,9 @@ size_t ms_stun_message_encode(const MSStunMessage *msg, char **buf) {
if (ms_stun_message_message_integrity_enabled(msg)) {
const char *username = ms_stun_message_get_username(msg);
const char *password = ms_stun_message_get_password(msg);
if ((username != NULL) && (password != NULL) && (strlen(username) > 0) && (strlen(password) > 0)) {
if (msg->ha1 != NULL) {
encode_long_term_integrity_from_ha1(&encoder, msg->ha1);
} else if ((username != NULL) && (password != NULL) && (strlen(username) > 0) && (strlen(password) > 0)) {
const char *realm = ms_stun_message_get_realm(msg);
if ((realm != NULL) && (strlen(realm) > 0)) {
encode_long_term_integrity(&encoder, realm, username, password);
......@@ -865,6 +907,10 @@ void ms_stun_message_set_password(MSStunMessage *msg, const char *password) {
STUN_STR_SETTER(msg->password, password);
}
void ms_stun_message_set_ha1(MSStunMessage *msg, const char *ha1) {
STUN_STR_SETTER(msg->ha1, ha1);
}
const char * ms_stun_message_get_realm(const MSStunMessage *msg) {
return msg->realm;
}
......@@ -881,6 +927,14 @@ void ms_stun_message_set_software(MSStunMessage *msg, const char *software) {
STUN_STR_SETTER(msg->software, software);
}
const char * ms_stun_message_get_nonce(const MSStunMessage *msg) {
return msg->nonce;
}
void ms_stun_message_set_nonce(MSStunMessage *msg, const char *nonce) {
STUN_STR_SETTER(msg->nonce, nonce);
}
bool_t ms_stun_message_has_error_code(const MSStunMessage *msg) {
return msg->has_error_code;
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment