mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-08-10 16:32:33 +00:00
Thanks to Phil Zimmermann for the code and for the license exception we needed to include it. There remains some build system integration work to be done before this code will build properly in the FreeSWITCH tree.
673 lines
21 KiB
C
673 lines
21 KiB
C
/*
|
|
* libZRTP SDK library, implements the ZRTP secure VoIP protocol.
|
|
* Copyright (c) 2006-2009 Philip R. Zimmermann. All rights reserved.
|
|
* Contact: http://philzimmermann.com
|
|
* For licensing and other legal details, see the file zrtp_legal.c.
|
|
*
|
|
* Viktor Krykun <v.krikun at zfoneproject.com>
|
|
*/
|
|
|
|
#include "zrtp.h"
|
|
|
|
#define _ZTU_ "zrtp mitm"
|
|
|
|
extern zrtp_status_t _zrtp_machine_process_goclear(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* State-Machine related functions */
|
|
/*===========================================================================*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_sasrelay(zrtp_stream_t *stream, zrtp_retry_task_t* task)
|
|
{
|
|
if (task->_retrys >= ZRTP_T2_MAX_COUNT) {
|
|
ZRTP_LOG(1,(_ZTU_,"WARNING! SASRELAY Max retransmissions count reached. ID=%u\n", stream->id));
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_timeout, 0);
|
|
} else if (task->_is_enabled) {
|
|
zrtp_status_t s = _zrtp_packet_send_message(stream, ZRTP_SASRELAY, &stream->messages.sasrelay);
|
|
task->timeout = _zrtp_get_timeout((uint32_t)task->timeout, ZRTP_SASRELAY);
|
|
if (zrtp_status_ok == s) {
|
|
task->_retrys++;
|
|
}
|
|
if (stream->zrtp->cb.sched_cb.on_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_call_later(stream, task);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
static zrtp_status_t _create_sasrelay( zrtp_stream_t *stream,
|
|
zrtp_sas_id_t transf_sas_scheme,
|
|
zrtp_string32_t* transf_sas_value,
|
|
uint8_t transf_ac_flag,
|
|
uint8_t transf_d_flag,
|
|
zrtp_packet_SASRelay_t* sasrelay )
|
|
{
|
|
zrtp_session_t *session = stream->session;
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
void* cipher_ctx = NULL;
|
|
|
|
/* (padding + sig_len + flags) + SAS scheme and SASHash */
|
|
const uint8_t encrypted_body_size = (2 + 1 + 1) + 4 + 32;
|
|
|
|
zrtp_memset(sasrelay, 0, sizeof(zrtp_packet_SASRelay_t));
|
|
|
|
/* generate a random initialization vector for CFB cipher */
|
|
if (ZRTP_CFBIV_SIZE != zrtp_randstr(session->zrtp, sasrelay->iv, ZRTP_CFBIV_SIZE)) {
|
|
return zrtp_status_rp_fail;
|
|
}
|
|
|
|
sasrelay->flags |= (session->profile.disclose_bit || transf_d_flag) ? 0x01 : 0x00;
|
|
sasrelay->flags |= (session->profile.allowclear && transf_ac_flag) ? 0x02 : 0x00;
|
|
sasrelay->flags |= 0x04;
|
|
|
|
zrtp_memcpy( sasrelay->sas_scheme,
|
|
zrtp_comp_id2type(ZRTP_CC_SAS, transf_sas_scheme),
|
|
ZRTP_COMP_TYPE_SIZE );
|
|
if (transf_sas_value)
|
|
zrtp_memcpy(sasrelay->sashash, transf_sas_value->buffer, transf_sas_value->length);
|
|
|
|
/* Then we need to encrypt Confirm before computing Hmac. Use AES CFB */
|
|
do {
|
|
cipher_ctx = session->blockcipher->start( session->blockcipher,
|
|
(uint8_t*)stream->cc.zrtp_key.buffer,
|
|
NULL,
|
|
ZRTP_CIPHER_MODE_CFB );
|
|
if (!cipher_ctx) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->set_iv( session->blockcipher,
|
|
cipher_ctx,
|
|
(zrtp_v128_t*)sasrelay->iv);
|
|
if (zrtp_status_ok != s) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->encrypt( session->blockcipher,
|
|
cipher_ctx,
|
|
(uint8_t*)&sasrelay->pad,
|
|
encrypted_body_size );
|
|
} while(0);
|
|
if (cipher_ctx) {
|
|
session->blockcipher->stop(session->blockcipher, cipher_ctx);
|
|
}
|
|
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! Failed to encrypt SASRELAY Message status=%d. ID=%u\n", s, stream->id));
|
|
return s;
|
|
}
|
|
|
|
/* Compute Hmac over encrypted part of Confirm */
|
|
{
|
|
zrtp_string128_t hmac = ZSTR_INIT_EMPTY(hmac);
|
|
s = session->hash->hmac_c( session->hash,
|
|
stream->cc.hmackey.buffer,
|
|
stream->cc.hmackey.length,
|
|
(const char*)&sasrelay->pad,
|
|
encrypted_body_size,
|
|
ZSTR_GV(hmac) );
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! Failed to compute CONFIRM hmac status=%d. ID=%u\n", s, stream->id));
|
|
return s;
|
|
}
|
|
zrtp_memcpy(sasrelay->hmac, hmac.buffer, ZRTP_HMAC_SIZE);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_sasrelay(zrtp_stream_t *stream, zrtp_rtp_info_t *packet)
|
|
{
|
|
zrtp_session_t *session = stream->session;
|
|
zrtp_packet_SASRelay_t *sasrelay = (zrtp_packet_SASRelay_t*) packet->message;
|
|
void* cipher_ctx = NULL;
|
|
zrtp_sas_id_t rendering_id = ZRTP_COMP_UNKN;
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
zrtp_string128_t hmac = ZSTR_INIT_EMPTY(hmac);
|
|
char zerosashash[32];
|
|
|
|
/* (padding + sig_len + flags) + SAS scheme and SAS hash */
|
|
const uint8_t encrypted_body_size = (2 + 1 + 1) + 4 + 32;
|
|
|
|
zrtp_memset(zerosashash, 0, sizeof(zerosashash));
|
|
|
|
/* Check if the remote endpoint is assiggneed to relay the SAS values */
|
|
if (!stream->peer_mitm_flag) {
|
|
ZRTP_LOG(2,(_ZTU_, ZRTP_RELAYED_SAS_FROM_NONMITM_STR));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Check the HMAC */
|
|
s = session->hash->hmac_c( session->hash,
|
|
stream->cc.peer_hmackey.buffer,
|
|
stream->cc.peer_hmackey.length,
|
|
(const char*)&sasrelay->pad,
|
|
encrypted_body_size,
|
|
ZSTR_GV(hmac) );
|
|
if (zrtp_status_ok != s ) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! Failed to compute CONFIRM hmac. status=%d ID=%u\n", s, stream->id));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
if (0 != zrtp_memcmp(sasrelay->hmac, hmac.buffer, ZRTP_HMAC_SIZE)) {
|
|
ZRTP_LOG(2,(_ZTU_, ZRTP_VERIFIED_RESP_WARNING_STR));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_, "\tHMAC value for the SASRELAY is correct - decryptiong...\n"));
|
|
|
|
/* Then we need to decrypt Confirm body */
|
|
do
|
|
{
|
|
cipher_ctx = session->blockcipher->start( session->blockcipher,
|
|
(uint8_t*)stream->cc.peer_zrtp_key.buffer,
|
|
NULL,
|
|
ZRTP_CIPHER_MODE_CFB );
|
|
if (!cipher_ctx) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->set_iv(session->blockcipher, cipher_ctx, (zrtp_v128_t*)sasrelay->iv);
|
|
if (zrtp_status_ok != s) {
|
|
break;
|
|
}
|
|
|
|
s = session->blockcipher->encrypt( session->blockcipher,
|
|
cipher_ctx,
|
|
(uint8_t*)&sasrelay->pad,
|
|
encrypted_body_size);
|
|
} while(0);
|
|
if (cipher_ctx) {
|
|
session->blockcipher->stop(session->blockcipher, cipher_ctx);
|
|
}
|
|
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! Failed to decrypt Confirm. status=%d ID=%u\n", s, stream->id));
|
|
return s;
|
|
}
|
|
|
|
ZRTP_LOG(2,(_ZTU_,"\tSasRelay FLAGS old/new A=%d/%d, D=%d/%d.\n",
|
|
stream->allowclear, (uint8_t)(sasrelay->flags & 0x02),
|
|
stream->peer_disclose_bit, (uint8_t)(sasrelay->flags & 0x01)));
|
|
|
|
/* Set evil bit if other-side disclosed session key */
|
|
stream->peer_disclose_bit = (sasrelay->flags & 0x01);
|
|
|
|
/* Enable ALLOWCLEAR option only if both sides support it */
|
|
stream->allowclear = (sasrelay->flags & 0x02) && session->profile.allowclear;
|
|
|
|
/*
|
|
* We don't handle verified flag in SASRelaying because it makes no
|
|
* sense in implementation of the ZRTP Internet Draft.
|
|
*/
|
|
|
|
/*
|
|
* Only enrolled users can do SAS transferring. (Non-enrolled users can
|
|
* only change the SAS rendering scheme).
|
|
*/
|
|
|
|
rendering_id = zrtp_comp_type2id(ZRTP_CC_SAS, (char*)sasrelay->sas_scheme);
|
|
if (-1 == zrtp_profile_find(&session->profile, ZRTP_CC_SAS, rendering_id)) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! PBX Confirm packet with transferred SAS have unknown or"
|
|
" unsupported rendering scheme %.4s.ID=%u\n", sasrelay->sas_scheme, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
session->sasscheme = zrtp_comp_find(ZRTP_CC_SAS, rendering_id, session->zrtp );
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tSasrelay: New Rendering scheme %.4s.\n", session->sasscheme->base.type));
|
|
|
|
if (session->secrets.matches & ZRTP_BIT_PBX) {
|
|
if ( ( ((uint32_t) *sasrelay->sas_scheme) != (uint32_t)0x0L ) &&
|
|
(0 != zrtp_memcmp(sasrelay->sashash, zerosashash, sizeof(sasrelay->sashash))) )
|
|
{
|
|
char buff[256];
|
|
session->sasbin.length = ZRTP_MITM_SAS_SIZE;
|
|
/* First 32 bits if sashash includes sasvalue */
|
|
zrtp_memcpy(session->sasbin.buffer, sasrelay->sashash, session->sasbin.length);
|
|
stream->mitm_mode = ZRTP_MITM_MODE_RECONFIRM_CLIENT;
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tSasRelay: SAS value was updated bin=%s.\n",
|
|
hex2str(buff, sizeof(buff), session->sasbin.buffer, session->sasbin.length)));
|
|
}
|
|
} else if (0 != zrtp_memcmp(sasrelay->sashash, zerosashash, sizeof(sasrelay->sashash))) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tWARNING! SAS Value was received from NOT Trusted MiTM. ID=%u\n", stream->id));
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_possible_mitm3, 1);
|
|
return zrtp_status_fail;
|
|
} else {
|
|
ZRTP_LOG(1,(_ZTU_, "\rERROR! For SasRelay Other secret doesn't match. ID=%u\n", stream->id));
|
|
}
|
|
|
|
s = session->sasscheme->compute(session->sasscheme, stream, session->hash, 1);
|
|
if (zrtp_status_ok != s) {
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_software, 1);
|
|
return s;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tSasRelay: Updated SAS is <%s> <%s>.\n", session->sas1.buffer, session->sas2.buffer));
|
|
|
|
if (session->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
session->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_LOCAL_SAS_UPDATED);
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_sasrelaying( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_RELAYACK:
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_SASRELAY);
|
|
_zrtp_change_state(stream, ZRTP_STATE_SECURE);
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_REMOTE_SAS_UPDATED);
|
|
}
|
|
break;
|
|
|
|
case ZRTP_GOCLEAR:
|
|
s = _zrtp_machine_process_goclear(stream, packet);
|
|
if (zrtp_status_ok == s) {
|
|
s = _zrtp_machine_enter_pendingclear(stream);
|
|
}
|
|
break;
|
|
|
|
case ZRTP_NONE:
|
|
s = _zrtp_protocol_decrypt(stream->protocol, packet, 1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* ZRTP API for PBX */
|
|
/*===========================================================================*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_stream_registration_start(zrtp_stream_t* stream, uint32_t ssrc)
|
|
{
|
|
if (!stream) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"START REGISTRATION STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
if (NULL == stream->zrtp->cb.cache_cb.on_get_mitm) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING: Can't use MiTM Functions with no ZRTP Cache.\n"));
|
|
return zrtp_status_notavailable;
|
|
}
|
|
|
|
stream->mitm_mode = ZRTP_MITM_MODE_REG_SERVER;
|
|
return zrtp_stream_start(stream, ssrc);
|
|
}
|
|
|
|
zrtp_status_t zrtp_stream_registration_secure(zrtp_stream_t* stream)
|
|
{
|
|
if (!stream) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"SECURE REGISTRATION STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
if (NULL == stream->zrtp->cb.cache_cb.on_get_mitm) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING: Can't use MiTM Functions with no ZRTP Cache.\n"));
|
|
return zrtp_status_notavailable;
|
|
}
|
|
|
|
stream->mitm_mode = ZRTP_MITM_MODE_REG_SERVER;
|
|
return zrtp_stream_secure(stream);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_register_with_trusted_mitm(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_session_t *session = stream->session;
|
|
zrtp_status_t s = zrtp_status_bad_param;
|
|
|
|
if (!stream) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"MARKING this call as REGISTRATION ID=%u\n", stream->id));
|
|
|
|
if (NULL == stream->zrtp->cb.cache_cb.on_get_mitm) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING: Can't use MiTM Functions with no ZRTP Cache.\n"));
|
|
return zrtp_status_notavailable;
|
|
}
|
|
|
|
if (!stream->protocol) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Passive Client endpoint should NOT generate PBX Secret. */
|
|
if ((stream->mitm_mode == ZRTP_MITM_MODE_REG_CLIENT) &&
|
|
(ZRTP_LICENSE_MODE_PASSIVE != stream->zrtp->lic_mode)) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING: Passive Client endpoint should NOT generate PBX Secert.\n"));
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/*
|
|
* Generate new MitM cache:
|
|
* pbxsecret = KDF(ZRTPSess, "Trusted MiTM key", (ZIDi | ZIDr), negotiated hash length)
|
|
*/
|
|
if ( (stream->state == ZRTP_STATE_SECURE) &&
|
|
((stream->mitm_mode == ZRTP_MITM_MODE_REG_CLIENT) || (stream->mitm_mode == ZRTP_MITM_MODE_REG_SERVER)) )
|
|
{
|
|
zrtp_string32_t kdf_context = ZSTR_INIT_EMPTY(kdf_context);
|
|
static const zrtp_string32_t trusted_mitm_key_label = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_TRUSTMITMKEY_STR);
|
|
zrtp_string16_t *zidi, *zidr;
|
|
|
|
if (stream->protocol->type == ZRTP_STATEMACHINE_INITIATOR) {
|
|
zidi = &session->zid;
|
|
zidr = &session->peer_zid;
|
|
} else {
|
|
zidi = &session->peer_zid;
|
|
zidr = &session->zid;
|
|
}
|
|
|
|
zrtp_zstrcat(ZSTR_GV(kdf_context), ZSTR_GVP(zidi));
|
|
zrtp_zstrcat(ZSTR_GV(kdf_context), ZSTR_GVP(zidr));
|
|
|
|
_zrtp_kdf( stream,
|
|
ZSTR_GV(session->zrtpsess),
|
|
ZSTR_GV(trusted_mitm_key_label),
|
|
ZSTR_GV(kdf_context),
|
|
ZRTP_HASH_SIZE,
|
|
ZSTR_GV(session->secrets.pbxs->value));
|
|
|
|
session->secrets.pbxs->_cachedflag = 1;
|
|
session->secrets.pbxs->lastused_at = (uint32_t)(zrtp_time_now()/1000);
|
|
session->secrets.cached |= ZRTP_BIT_PBX;
|
|
session->secrets.matches |= ZRTP_BIT_PBX;
|
|
|
|
s = zrtp_status_ok;
|
|
if (session->zrtp->cb.cache_cb.on_put_mitm) {
|
|
s = session->zrtp->cb.cache_cb.on_put_mitm( ZSTR_GV(session->zid),
|
|
ZSTR_GV(session->peer_zid),
|
|
session->secrets.pbxs);
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"Makring this call as REGISTRATION - DONE\n"));
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_link_mitm_calls(zrtp_stream_t *stream1, zrtp_stream_t *stream2)
|
|
{
|
|
if (!stream1 || !stream2) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"Link to MiTM call together stream1=%u stream2=%u.\n", stream1->id, stream2->id));
|
|
|
|
/* This APi is for MiTM endpoints only. */
|
|
if (stream1->zrtp->is_mitm) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
stream1->linked_mitm = stream2;
|
|
stream2->linked_mitm = stream1;
|
|
|
|
{
|
|
zrtp_stream_t *passive = NULL;
|
|
zrtp_stream_t *unlimited = NULL;
|
|
|
|
/* Check if we have at least one Unlimited endpoint. */
|
|
if (stream1->peer_super_flag)
|
|
unlimited = stream1;
|
|
else if (stream2->peer_super_flag)
|
|
unlimited = stream2;
|
|
|
|
/* Check if the peer stream is Passive */
|
|
if (unlimited) {
|
|
passive = (stream1 == unlimited) ? stream2 : stream1;
|
|
if (!passive->peer_passive)
|
|
passive = NULL;
|
|
}
|
|
|
|
/* Ok, we haver Unlimited and Passive at two ends, let's make an exception and switch Passive to Secure. */
|
|
if (unlimited && passive) {
|
|
if (passive->state == ZRTP_STATE_CLEAR) {
|
|
ZRTP_LOG(2,(_ZTU_,"INFO: zrtp_link_mitm_calls() stream with id=%u is Unlimited and"
|
|
" Peer stream with id=%u is Passive in CLEAR state, switch the passive one to SECURE.\n"));
|
|
|
|
/* @note: don't use zrtp_secure_stream() wrapper as it checks for Active/Passive stuff. */
|
|
_zrtp_machine_start_initiating_secure(passive);
|
|
}
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_update_remote_options( zrtp_stream_t* stream,
|
|
zrtp_sas_id_t transf_sas_scheme,
|
|
zrtp_string32_t* transf_sas_value,
|
|
uint8_t transf_ac_flag,
|
|
uint8_t transf_d_flag )
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.sasrelay_task;
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
char buff[256];
|
|
|
|
if (!stream) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"UPDATE REMOTE SAS OPTIONS mode. ID=%u\n", stream->id));
|
|
ZRTP_LOG(3,(_ZTU_,"transf_sas=%s scheme=%d.\n", transf_sas_value ?
|
|
hex2str((const char*)transf_sas_value->buffer, transf_sas_value->length, (char*)buff, sizeof(buff)) : "NULL",
|
|
transf_sas_scheme));
|
|
|
|
if (NULL == stream->zrtp->cb.cache_cb.on_get_mitm) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING: Can't use MiTM Functions with no ZRTP Cache.\n"));
|
|
return zrtp_status_notavailable;
|
|
}
|
|
|
|
/* The TRANSFERRING option is only available from the SECURE state. */
|
|
if (stream->state != ZRTP_STATE_SECURE) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Don't transfer an SAS to a non-enrolled user */
|
|
if (transf_sas_value && !(stream->session->secrets.matches & ZRTP_BIT_PBX)) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Don't allow to transfer the SAS if the library wasn't initalized as MiTM endpoint */
|
|
if (!stream->zrtp->is_mitm) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tERROR! The endpoint can't transfer SAS values to other endpoints"
|
|
" without introducing itself by M-flag in Hello. see zrtp_init().\n"));
|
|
return zrtp_status_wrong_state;
|
|
}
|
|
|
|
s = _create_sasrelay( stream,
|
|
transf_sas_scheme,
|
|
transf_sas_value,
|
|
transf_ac_flag,
|
|
transf_d_flag,
|
|
&stream->messages.sasrelay);
|
|
if(zrtp_status_ok != s) {
|
|
return s;
|
|
}
|
|
|
|
s = _zrtp_packet_fill_msg_hdr( stream,
|
|
ZRTP_SASRELAY,
|
|
sizeof(zrtp_packet_SASRelay_t) - sizeof(zrtp_msg_hdr_t),
|
|
&stream->messages.sasrelay.hdr);
|
|
if(zrtp_status_ok != s) {
|
|
return s;
|
|
}
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_SASRELAYING);
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_sasrelay;
|
|
task->_retrys = 0;
|
|
_send_and_resend_sasrelay(stream, task);
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_resolve_mitm_call( zrtp_stream_t* stream1,
|
|
zrtp_stream_t* stream2)
|
|
{
|
|
zrtp_stream_t* enrolled = NULL;
|
|
zrtp_stream_t* non_enrolled = NULL;
|
|
zrtp_sas_id_t mitm_sas_scheme = ZRTP_COMP_UNKN;
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
if (!stream1 || !stream2) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"RESOLVE MITM CALL s1=%u, s2=%u...\n", stream1->id, stream2->id));
|
|
|
|
if (NULL == stream1->zrtp->cb.cache_cb.on_get_mitm) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING: Can't use MiTM Functions with no ZRTP Cache.\n"));
|
|
return zrtp_status_notavailable;
|
|
}
|
|
|
|
/*
|
|
* Both sides must be in the Secure state and at least one should be
|
|
* enrolled.
|
|
*/
|
|
if ((stream1->state != ZRTP_STATE_SECURE) || (stream2->state != ZRTP_STATE_SECURE)) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Check the stream enrollment options and choose one for transferring the call. */
|
|
if (zrtp_is_user_enrolled(stream1)) {
|
|
if (zrtp_is_user_enrolled(stream2)) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tBoth streams are enrolled - choose one with bigger ZID.\n"));
|
|
enrolled = zrtp_choose_one_enrolled(stream1, stream2);
|
|
} else {
|
|
enrolled = stream1;
|
|
}
|
|
} else if (zrtp_is_user_enrolled(stream2)) {
|
|
enrolled = stream2;
|
|
}
|
|
|
|
if (!enrolled) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
else {
|
|
non_enrolled = (stream1 == enrolled) ? stream2 : stream1;
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tAfter Resolving: S1 is %s and S2 is %s.\n",
|
|
(stream1 == enrolled) ? "ENROLLED" : "NON-ENROLLED",
|
|
(stream2 == enrolled) ? "ENROLLED" : "NON-ENROLLED"));
|
|
|
|
/*
|
|
* Choose the best SAS rendering scheme supported by both peers. Find the
|
|
* stream that can change it.
|
|
*/
|
|
{
|
|
uint8_t i=0;
|
|
|
|
zrtp_packet_Hello_t *enhello = &enrolled->messages.peer_hello;
|
|
char *encp = (char*)enhello->comp + (enhello->hc +
|
|
enhello->cc +
|
|
enhello->ac +
|
|
enhello->kc)* ZRTP_COMP_TYPE_SIZE;
|
|
|
|
|
|
for (i=0; i<enhello->sc; i++, encp+=ZRTP_COMP_TYPE_SIZE)
|
|
{
|
|
uint8_t j=0;
|
|
zrtp_packet_Hello_t *nonenhello = &non_enrolled->messages.peer_hello;
|
|
char *nonencp = (char*)nonenhello->comp + (nonenhello->hc +
|
|
nonenhello->cc +
|
|
nonenhello->ac +
|
|
nonenhello->kc)* ZRTP_COMP_TYPE_SIZE;
|
|
|
|
for (j=0; j<nonenhello->sc; j++, nonencp+=ZRTP_COMP_TYPE_SIZE)
|
|
{
|
|
if (0 == zrtp_memcmp(encp, nonencp, ZRTP_COMP_TYPE_SIZE)) {
|
|
mitm_sas_scheme = zrtp_comp_type2id(ZRTP_CC_SAS, encp);
|
|
ZRTP_LOG(3,(_ZTU_,"\tMITM SAS scheme=%.4s was choosen.\n", encp));
|
|
break;
|
|
}
|
|
}
|
|
if (j != nonenhello->sc) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if (ZRTP_COMP_UNKN == mitm_sas_scheme) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! Can't find matched SAS schemes on MiTM Resolving.\n"
|
|
" s1=%u s2=$u", stream1->id, stream2->id));
|
|
return zrtp_status_algo_fail;
|
|
}
|
|
|
|
s = zrtp_update_remote_options( enrolled,
|
|
mitm_sas_scheme,
|
|
&non_enrolled->session->sasbin,
|
|
non_enrolled->allowclear,
|
|
non_enrolled->peer_disclose_bit );
|
|
if (zrtp_status_ok != s) {
|
|
return s;
|
|
}
|
|
|
|
/* NOTE: new request from Philip Zimmermann - always send SASRelay to BOTH parties. */
|
|
/* If non-enrolled party has SAS scheme different from chosen one - update */
|
|
/*if (non_enrolled->session->sasscheme->base.id != mitm_sas_scheme) { */
|
|
s = zrtp_update_remote_options( non_enrolled,
|
|
mitm_sas_scheme,
|
|
NULL,
|
|
enrolled->allowclear,
|
|
enrolled->peer_disclose_bit );
|
|
if (zrtp_status_ok != s) {
|
|
return s;
|
|
}
|
|
/*}*/
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
uint8_t zrtp_is_user_enrolled(zrtp_stream_t* stream)
|
|
{
|
|
if (!stream) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
return ( (stream->session->secrets.cached & ZRTP_BIT_PBX) &&
|
|
(stream->session->secrets.matches & ZRTP_BIT_PBX) );
|
|
}
|
|
|
|
zrtp_stream_t* zrtp_choose_one_enrolled(zrtp_stream_t* stream1, zrtp_stream_t* stream2)
|
|
{
|
|
if (!stream1 || !stream2) {
|
|
return NULL;
|
|
}
|
|
|
|
if (zrtp_memcmp( stream1->session->zid.buffer,
|
|
stream2->session->zid.buffer,
|
|
stream1->session->zid.length) > 0) {
|
|
return stream1;
|
|
} else {
|
|
return stream2;
|
|
}
|
|
}
|