mirror of
https://github.com/signalwire/freeswitch.git
synced 2025-02-09 09:17:34 +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.
1481 lines
47 KiB
C
1481 lines
47 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 engine"
|
|
|
|
/*!
|
|
* Data type for state-handlers: every state has a state handler
|
|
* function which is called by zrtp_process_srtp().
|
|
*/
|
|
typedef zrtp_status_t state_handler_t( zrtp_stream_t* stream, zrtp_rtp_info_t* packet );
|
|
extern state_handler_t* state_handler[ZRTP_STATE_COUNT];
|
|
|
|
extern zrtp_status_t _zrtp_machine_process_sasrelay(zrtp_stream_t *stream, zrtp_rtp_info_t *packet);
|
|
|
|
static void _zrtp_machine_switch_to_error(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_enter_initiatingclear(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_enter_clear(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_enter_pendingerror(zrtp_stream_t *stream, zrtp_protocol_error_t code);
|
|
|
|
zrtp_status_t _zrtp_machine_process_hello(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
zrtp_status_t _zrtp_machine_process_goclear(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
static void _send_helloack(zrtp_stream_t* stream);
|
|
static void _send_goclearack(zrtp_stream_t* stream);
|
|
|
|
zrtp_status_t _zrtp_machine_start_send_and_resend_hello(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_goclear(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_errorack(zrtp_stream_t* stream);
|
|
static zrtp_status_t _zrtp_machine_start_send_and_resend_error(zrtp_stream_t* stream);
|
|
|
|
void _clear_stream_crypto(zrtp_stream_t* stream);
|
|
|
|
|
|
/*===========================================================================*/
|
|
// MARK: ===> Main ZRTP interfaces
|
|
/*===========================================================================*/
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_process_rtcp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
|
|
/*
|
|
* In transition states, drop outgoing packets. In SECURE state, encrypt
|
|
outgoing packets. In all other states leave them unchanged.
|
|
*/
|
|
|
|
if (stream) {
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_START_INITIATINGSECURE:
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM1:
|
|
case ZRTP_STATE_WAIT_CONFIRMACK:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM2:
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
return zrtp_status_drop;
|
|
|
|
case ZRTP_STATE_SASRELAYING:
|
|
case ZRTP_STATE_SECURE:
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
|
|
if (*length < RTCP_HDR_SIZE) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
zrtp_memset(&info, 0, sizeof(info));
|
|
info.packet = packet;
|
|
info.length = length;
|
|
info.seq = 0; /*sequence number will be generated in zrtp_srtp_protect_rtcp()*/
|
|
info.ssrc = (uint32_t) *(packet+sizeof(uint32_t));
|
|
|
|
return _zrtp_protocol_encrypt(stream->protocol, &info, 0);
|
|
}
|
|
|
|
default:
|
|
return zrtp_status_ok;
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_process_srtcp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
|
|
/*
|
|
* In transition states, drop incoming packets. In SECURE state, decrypt
|
|
* incoming packets. In all other states leave them unchanged.
|
|
*/
|
|
|
|
if (stream) {
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_INITIATINGCLEAR:
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
return zrtp_status_drop;
|
|
|
|
case ZRTP_STATE_SECURE:
|
|
case ZRTP_STATE_SASRELAYING:
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
|
|
if (*length < RTCP_HDR_SIZE) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
zrtp_memset(&info, 0, sizeof(info));
|
|
info.packet = packet;
|
|
info.length = length;
|
|
info.seq = 0; /*sequence number will be determined from packet in zrtp_srtp_unprotect_rtcp()*/
|
|
info.ssrc = (uint32_t) *(packet+sizeof(uint32_t));
|
|
|
|
return _zrtp_protocol_decrypt(stream->protocol, &info, 0);
|
|
}
|
|
|
|
default:
|
|
return zrtp_status_ok;
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_process_rtp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
|
|
if (!stream || !packet || !length) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Skip packet processing within uninitiated stream */
|
|
if ((stream->state < ZRTP_STATE_START) || (stream->state > ZRTP_STATE_NO_ZRTP)) {
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/* Prepare RTP packet: detect type and other options */
|
|
if (zrtp_status_ok != _zrtp_packet_preparse(stream, packet, length, &info, 0)) {
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Drop packets in transition states and encrypt in SECURE state */
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_START_INITIATINGSECURE:
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM1:
|
|
case ZRTP_STATE_WAIT_CONFIRMACK:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM2:
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
if (ZRTP_NONE == info.type) {
|
|
/* Add dropped media to the entropy hash */
|
|
ZRTP_LOG(1,(_ZTU_,"Add %d bytes of entropy to the RNG pool.\n", *length));
|
|
zrtp_entropy_add(stream->zrtp, (unsigned char*)packet, *length);
|
|
|
|
return zrtp_status_drop;
|
|
}
|
|
break;
|
|
|
|
case ZRTP_STATE_SASRELAYING:
|
|
case ZRTP_STATE_SECURE:
|
|
if (ZRTP_NONE == info.type) {
|
|
return _zrtp_protocol_encrypt(stream->protocol, &info, 1);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
extern int _send_message(zrtp_stream_t* stream, zrtp_msg_type_t type, const void* message, uint32_t ssrc);
|
|
zrtp_status_t zrtp_process_srtp(zrtp_stream_t *stream, char* packet, unsigned int* length)
|
|
{
|
|
zrtp_rtp_info_t info;
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
if (!stream || !packet || !length) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
if (*length <= RTP_HDR_SIZE) {
|
|
return zrtp_status_bad_param;
|
|
}
|
|
|
|
/* Preparse RTP packet: detect type and other options */
|
|
s = _zrtp_packet_preparse(stream, packet, length, &info, 1);
|
|
if (zrtp_status_ok != s) {
|
|
return s;
|
|
}
|
|
|
|
/*************************************************************************/
|
|
/* For Zfone3 Compatibility */
|
|
if (ZRTP_ZFONEPING == info.type) {
|
|
zrtp_packet_zfoneping_t* ping = (zrtp_packet_zfoneping_t*) info.message;
|
|
zrtp_packet_zfonepingack_t pingack;
|
|
|
|
zrtp_memcpy(pingack.version, ZRTP_ZFONE_PROTOCOL_VERSION, 4);
|
|
zrtp_memcpy(pingack.endpointhash, stream->session->zid.buffer, sizeof(pingack.endpointhash));
|
|
zrtp_memcpy(pingack.peerendpointhash, ping->endpointhash, sizeof(pingack.endpointhash));
|
|
pingack.peerssrc = info.ssrc;
|
|
|
|
_zrtp_packet_fill_msg_hdr( stream,
|
|
ZRTP_ZFONEPINGACK,
|
|
sizeof(zrtp_packet_zfonepingack_t) - sizeof(zrtp_msg_hdr_t),
|
|
&pingack.hdr);
|
|
|
|
_zrtp_packet_send_message(stream, ZRTP_ZFONEPINGACK, &pingack);
|
|
return zrtp_status_drop;
|
|
}
|
|
/*************************************************************************/
|
|
|
|
/* Skip packet processing within non-started stream */
|
|
if ((stream->state < ZRTP_STATE_START) || (stream->state > ZRTP_STATE_NO_ZRTP)) {
|
|
return (ZRTP_NONE == info.type) ? zrtp_status_ok : zrtp_status_drop;
|
|
}
|
|
|
|
/*
|
|
* This mutex should protect stream data against asynchr. calls e.g.:
|
|
* zrtp_stream_secure(), zrtp_stream_clear() etc. Media packet handlers
|
|
* don't change any internal data, so this applies only to ZRTP messages.
|
|
*/
|
|
if (info.type != ZRTP_NONE) {
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
}
|
|
|
|
/* Extra protection. We need protocol to handle ZRTP messages in following states. */
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_INITIATINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM1:
|
|
case ZRTP_STATE_WAIT_CONFIRMACK:
|
|
case ZRTP_STATE_PENDINGSECURE:
|
|
case ZRTP_STATE_WAIT_CONFIRM2:
|
|
case ZRTP_STATE_SECURE:
|
|
case ZRTP_STATE_SASRELAYING:
|
|
if (!stream->protocol) {
|
|
if (info.type != ZRTP_NONE) {
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
}
|
|
return zrtp_status_fail;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
/* Handle Error packet from any state */
|
|
if (ZRTP_ERROR == info.type && stream->state > ZRTP_STATE_START)
|
|
{
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_NONE:
|
|
case ZRTP_STATE_ACTIVE:
|
|
case ZRTP_STATE_SECURE:
|
|
case ZRTP_STATE_PENDINGERROR:
|
|
case ZRTP_STATE_INITIATINGERROR:
|
|
case ZRTP_STATE_NO_ZRTP:
|
|
break;
|
|
default:
|
|
{
|
|
zrtp_packet_Error_t* error = (zrtp_packet_Error_t*) info.message;
|
|
_zrtp_machine_enter_pendingerror(stream, zrtp_ntoh32(error->code));
|
|
} break;
|
|
}
|
|
}
|
|
|
|
/* Process packet by state-machine according to packet type and current protocol state */
|
|
if (state_handler[stream->state]) {
|
|
s = state_handler[stream->state](stream, &info);
|
|
}
|
|
|
|
/* Unlock stream mutex for a ZRTP message packet. See comments above. */
|
|
if (info.type != ZRTP_NONE) {
|
|
s = zrtp_status_drop;
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_stream_start(zrtp_stream_t* stream, uint32_t ssrc)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
/*
|
|
* (ZRTP stream starts from START state and HELLO packets resending.
|
|
* Stream can be started from START, ERROR or NOZRTP states only.)
|
|
*/
|
|
ZRTP_LOG(3,(_ZTU_,"START STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
if ( (ZRTP_STATE_ACTIVE != stream->state) &&
|
|
(ZRTP_STATE_ERROR != stream->state) &&
|
|
(ZRTP_STATE_NO_ZRTP != stream->state)) {
|
|
ZRTP_LOG(1,(_ZTU_,"ERROR! Can't start Stream ID=%u from %s state.\n",
|
|
stream->id, zrtp_log_state2str(stream->state)));
|
|
s = zrtp_status_wrong_state;
|
|
} else {
|
|
stream->media_ctx.ssrc = zrtp_hton32(ssrc);
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_START);
|
|
_zrtp_machine_start_send_and_resend_hello(stream);
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_stream_stop(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
/*
|
|
* Stop all packet replays, deinitialize crypto data and prepare the stream
|
|
* for the next use. The stream can be terminated from any protocol state.
|
|
*/
|
|
ZRTP_LOG(3,(_ZTU_,"STOP STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
/*
|
|
* Unlink deleted stream for the peer MiTM stream if necessary. It may
|
|
* prevent some recae-conditions as we always test for NULL before
|
|
* accessing linked_mitm.
|
|
*/
|
|
if (stream->linked_mitm) {
|
|
stream->linked_mitm->linked_mitm = NULL;
|
|
}
|
|
|
|
if (stream->state != ZRTP_STATE_NONE) {
|
|
/*
|
|
* This function can be called in parallel to the main processing loop
|
|
* - protect internal stream data.
|
|
*/
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
if (stream->zrtp->cb.sched_cb.on_wait_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_wait_call_later(stream);
|
|
}
|
|
|
|
_clear_stream_crypto(stream);
|
|
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
zrtp_mutex_destroy(stream->stream_protector);
|
|
|
|
zrtp_memset(stream, 0, sizeof(zrtp_stream_t));
|
|
|
|
stream->mode = ZRTP_STREAM_MODE_UNKN;
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_NONE);
|
|
} else {
|
|
s = zrtp_status_wrong_state;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
zrtp_status_t zrtp_stream_clear(zrtp_stream_t *stream)
|
|
{
|
|
/*
|
|
* This function can be called for two reasons: either our user is
|
|
* initiating the go-clear ritual or we accepting that ritual as
|
|
* initiated by the other end of the line. If our user initiates the
|
|
* go-clear process libzrtp switches to INITIATING_CLEAR and runs
|
|
* GoClear replays. The go-clear ritual can be started from SECURE state
|
|
* only. If the other end of the line is initiating and this function is
|
|
* being called to accept the go-clear procedure - protocol transites to
|
|
* CLEAR state imediately. One can accept go-clear from PENDING CLEAR
|
|
* state only. See state-macine diagram for more information.
|
|
*/
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
|
|
/* This function can be called in parallel to the main processing loop - protect stream data. */
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"CLEAR STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
switch (stream->state)
|
|
{
|
|
case ZRTP_STATE_SECURE:
|
|
/* Clearing ritual can't be started if "allow clear" is disabled */
|
|
if (stream->session->profile.allowclear) {
|
|
s = _zrtp_machine_enter_initiatingclear(stream);
|
|
}
|
|
break;
|
|
case ZRTP_STATE_PENDINGCLEAR:
|
|
s = _zrtp_machine_enter_clear(stream);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
|
|
return s;
|
|
}
|
|
|
|
/*----------------------------------------------------------------------------*/
|
|
void _initiating_secure(zrtp_stream_t *stream, zrtp_retry_task_t* task)
|
|
{
|
|
/*
|
|
* In accordance with the ZRTP standard, there can be multiple simultaneous
|
|
* DH streams, as well as preshared streams.
|
|
*
|
|
* Before entering the INITIATING_SECURE state, we check several conditions.
|
|
* For details see \doc\img\odg\zrtp_streams.odg and zrtp_statemach.odg)
|
|
*/
|
|
|
|
/* The first call to this function is already protected by a mutex in zrtp_process_srtp() */
|
|
uint8_t use_mutex = (task->_retrys > 0);
|
|
|
|
if (!task->_is_enabled) {
|
|
return;
|
|
}
|
|
|
|
if (use_mutex) {
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure iteration... ID=%u.\n", stream->id));
|
|
|
|
/* Skip the last replay after switching to another state to avoid unwanted replays */
|
|
if (stream->state <= ZRTP_STATE_START_INITIATINGSECURE)
|
|
{
|
|
stream->mode = _zrtp_define_stream_mode(stream);
|
|
ZRTP_LOG(3,(_ZTU_,"\tGot mode=%s. Check approval of starting.\n", zrtp_log_mode2str(stream->mode)));
|
|
if (!_zrtp_can_start_stream(stream, &stream->concurrent, stream->mode))
|
|
{
|
|
if (task->_retrys > ZRTP_PROCESS_T1_MAX_COUNT) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure. Max retransmissions count reached"
|
|
"for stream ID=%u.\n", stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_timeout, 0);
|
|
} else {
|
|
ZRTP_LOG(3,(_ZTU_,"\tInitiating Secure. stream ID=%u is DH but one more DH"
|
|
" stream is in progress - waiting...\n", stream->id));
|
|
|
|
task->_retrys++;
|
|
if (stream->zrtp->cb.sched_cb.on_call_later) {
|
|
stream->zrtp->cb.sched_cb.on_call_later(stream, task);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ZRTP_LOG(3,(_ZTU_,"\tMode=%s Cccepted. Starting ZRTP Initiator Protocol.\n", zrtp_log_mode2str(stream->mode)));
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_PROCESS);
|
|
_zrtp_machine_enter_initiatingsecure(stream);
|
|
}
|
|
}
|
|
|
|
if (use_mutex) {
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
}
|
|
}
|
|
|
|
zrtp_status_t _zrtp_machine_start_initiating_secure(zrtp_stream_t *stream)
|
|
{
|
|
/*
|
|
* This function creates a task to do retries of the first packet in the
|
|
* "Going secure" procedure, and then _initiating_secure() will start
|
|
* protocol.
|
|
*/
|
|
zrtp_retry_task_t* task = &stream->messages.dh_task;
|
|
task->_is_enabled = 1;
|
|
task->_retrys = 0;
|
|
task->callback = _initiating_secure;
|
|
task->timeout = ZRTP_PROCESS_T1;
|
|
|
|
/*
|
|
* Prevent race conditions on starting multiple streams.
|
|
*/
|
|
zrtp_mutex_lock(stream->session->init_protector);
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_START_INITIATINGSECURE);
|
|
_initiating_secure(stream, task);
|
|
|
|
zrtp_mutex_unlock(stream->session->init_protector);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
zrtp_status_t zrtp_stream_secure(zrtp_stream_t *stream)
|
|
{
|
|
/*
|
|
* Wrapper function for going into secure mode. It can be initiated in
|
|
* parallel to the main processing loop. The internal stream data has to
|
|
* be protected by mutex.
|
|
*/
|
|
|
|
zrtp_status_t s = zrtp_status_fail;
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"SECURE STREAM ID=%u mode=%s state=%s.\n",
|
|
stream->id, zrtp_log_mode2str(stream->mode), zrtp_log_state2str(stream->state)));
|
|
|
|
zrtp_mutex_lock(stream->stream_protector);
|
|
|
|
/* Limit ZRTP Session initiation procedure according to the license */
|
|
if ( (stream->state == ZRTP_STATE_CLEAR) && ZRTP_PASSIVE1_TEST(stream)) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
ZRTP_LOG(1,(_ZTU_,"\tWARNING! Can't Start Stream from %s state and with %d license mode. ID=%u\n",
|
|
zrtp_log_state2str(stream->state), stream->zrtp->lic_mode, stream->id));
|
|
|
|
if (!ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event ) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
}
|
|
}
|
|
|
|
zrtp_mutex_unlock(stream->stream_protector);
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* State handlers */
|
|
/*===========================================================================*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_start( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
s = _zrtp_machine_process_hello(stream, packet);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello() failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in START state. */
|
|
}
|
|
|
|
/* Now we have ZIDs for both sides and can upload secrets from the cache */
|
|
s = _zrtp_prepare_secrets(stream->session);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_prepare_secrets() failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in START state. */
|
|
}
|
|
|
|
_send_helloack(stream);
|
|
_zrtp_change_state(stream, ZRTP_STATE_WAIT_HELLOACK);
|
|
break;
|
|
|
|
case ZRTP_HELLOACK:
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
_zrtp_change_state(stream, ZRTP_STATE_WAIT_HELLO);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_wait4hello( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
s = _zrtp_machine_process_hello(stream, packet);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello()2 failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in the current state. */
|
|
}
|
|
|
|
/* Now we have ZIDs for both sides and can upload secrets from the cache */
|
|
s = _zrtp_prepare_secrets(stream->session);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_prepare_secrets()2 failed with status=%d. ID=%u\n", s, stream->id));
|
|
break; /* Just stay in the current state. */
|
|
}
|
|
|
|
/* Start initiating the secure state if "autosecure" is enabled */
|
|
if ((stream->session->profile.autosecure) && ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (!stream->session->profile.discovery_optimization) {
|
|
_send_helloack(stream); /* Response with HelloAck before start computing DH value */
|
|
}
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
_send_helloack(stream);
|
|
|
|
if (!ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO: Switching to Clear due to Active/Passive restrictions.\n"));
|
|
}
|
|
|
|
s = _zrtp_machine_enter_clear(stream);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_wait4helloack( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t status = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
_send_helloack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
/* Passive Initiator can't talk to anyone */
|
|
if (ZRTP_PASSIVE2_TEST(stream))
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
status = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
status = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
status = zrtp_status_fail;
|
|
}
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tERROR: The endpoint is in passive mode and Signaling Initiator -"
|
|
" can't handle connections from anyone. ID=%u\n", stream->id));
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_service_unavail, 1);
|
|
}
|
|
} break;
|
|
|
|
case ZRTP_HELLOACK:
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_HELLO);
|
|
|
|
/* Start initiating the secure state if "autosecure" is enabled */
|
|
if ((stream->session->profile.autosecure) && ZRTP_PASSIVE1_TEST(stream)) {
|
|
status = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
if (!ZRTP_PASSIVE1_TEST(stream)) {
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
ZRTP_LOG(2,(_ZTU_,"\tINFO: Switching to Clear due to Active/Passive restrictions.\n"));
|
|
}
|
|
status = _zrtp_machine_enter_clear(stream);
|
|
}
|
|
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_clear( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_GOCLEAR:
|
|
_send_goclearack(stream);
|
|
break;
|
|
|
|
case ZRTP_HELLO:
|
|
_send_helloack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_initiatingclear( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_GOCLEARACK:
|
|
case ZRTP_COMMIT:
|
|
s = _zrtp_machine_enter_clear(stream);
|
|
break;
|
|
|
|
case ZRTP_NONE:
|
|
s = zrtp_status_drop;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_pendingclear( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_GOCLEAR:
|
|
_send_goclearack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_start_initiatingsecure( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
_send_helloack(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT:
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_PROCESS);
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_secure( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_CONFIRM2:
|
|
_zrtp_packet_send_message(stream, ZRTP_CONFIRM2ACK, NULL);
|
|
break;
|
|
|
|
case ZRTP_SASRELAY:
|
|
/*
|
|
* _zrtp_machine_process_sasrelay() updates SAS, sends events and does
|
|
* other things if SAS transferring is allowed
|
|
*/
|
|
s = _zrtp_machine_process_sasrelay(stream, packet);
|
|
if (zrtp_status_ok == s) {
|
|
_zrtp_packet_send_message(stream, ZRTP_RELAYACK, NULL);
|
|
}
|
|
break;
|
|
|
|
case ZRTP_GOCLEAR:
|
|
s = _zrtp_machine_process_goclear(stream, packet);
|
|
if (zrtp_status_ok == s) {
|
|
s = _zrtp_machine_enter_pendingclear(stream);
|
|
_send_goclearack(stream);
|
|
}
|
|
break;
|
|
|
|
case ZRTP_NONE:
|
|
s = _zrtp_protocol_decrypt(stream->protocol, packet, 1);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_initiatingerror( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_ERROR:
|
|
_zrtp_machine_enter_pendingerror(stream, ((zrtp_packet_Error_t*) packet->message)->code );
|
|
break;
|
|
|
|
case ZRTP_ERRORACK:
|
|
_zrtp_machine_switch_to_error(stream);
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_while_in_nozrtp( zrtp_stream_t* stream,
|
|
zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_status_t s = zrtp_status_ok;
|
|
|
|
switch (packet->type)
|
|
{
|
|
case ZRTP_HELLO:
|
|
s = _zrtp_machine_process_hello(stream, packet);
|
|
if (zrtp_status_ok != s) {
|
|
ZRTP_LOG(1,(_ZTU_,"\tERROR! _zrtp_machine_process_hello()3 failed with status=%d ID=%u.\n", s, stream->id));
|
|
break;
|
|
}
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_START);
|
|
_zrtp_machine_start_send_and_resend_hello(stream);
|
|
break;
|
|
|
|
case ZRTP_COMMIT: /* this logic should be similar to Commit handler in ZRTP_STATE_WAIT_HELLOACK state */
|
|
{
|
|
/* Passive Initiator can't talk to anyone */
|
|
if (ZRTP_PASSIVE2_TEST(stream))
|
|
{
|
|
zrtp_statemachine_type_t role = _zrtp_machine_preparse_commit(stream, packet);
|
|
if (ZRTP_STATEMACHINE_RESPONDER == role) {
|
|
s = _zrtp_machine_enter_pendingsecure(stream, packet);
|
|
} else if (ZRTP_STATEMACHINE_INITIATOR == role) {
|
|
s = _zrtp_machine_start_initiating_secure(stream);
|
|
} else {
|
|
s = zrtp_status_fail;
|
|
}
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tERROR: The endpoint is in passive mode and Signaling Initiator -"
|
|
" can't handle connections from anyone. ID=%u\n", stream->id));
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event ) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PASSIVE_RESTRICTION);
|
|
}
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_service_unavail, 1);
|
|
}
|
|
} break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return s;
|
|
}
|
|
|
|
|
|
/* Initiator logic */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_initiatingsecure(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirmack(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirm1(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
/* Responder logic */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_pendingsecure(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_waitconfirm2(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
/* PBX transferring logic */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_sasrelaying(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
|
|
#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
|
|
/* Driven Discovery state-machine */
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_driven_initiator(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_driven_responder(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
extern zrtp_status_t _zrtp_machine_process_while_in_driven_pending(zrtp_stream_t* stream, zrtp_rtp_info_t* packet);
|
|
#endif
|
|
|
|
state_handler_t* state_handler[ZRTP_STATE_COUNT] =
|
|
{
|
|
NULL,
|
|
NULL,
|
|
_zrtp_machine_process_while_in_start,
|
|
_zrtp_machine_process_while_in_wait4helloack,
|
|
_zrtp_machine_process_while_in_wait4hello,
|
|
_zrtp_machine_process_while_in_clear,
|
|
_zrtp_machine_process_while_in_start_initiatingsecure,
|
|
_zrtp_machine_process_while_in_initiatingsecure,
|
|
_zrtp_machine_process_while_in_waitconfirm1,
|
|
_zrtp_machine_process_while_in_waitconfirmack,
|
|
_zrtp_machine_process_while_in_pendingsecure,
|
|
_zrtp_machine_process_while_in_waitconfirm2,
|
|
_zrtp_machine_process_while_in_secure,
|
|
_zrtp_machine_process_while_in_sasrelaying,
|
|
_zrtp_machine_process_while_in_initiatingclear,
|
|
_zrtp_machine_process_while_in_pendingclear,
|
|
_zrtp_machine_process_while_in_initiatingerror,
|
|
NULL,
|
|
NULL,
|
|
#if (defined(ZRTP_BUILD_FOR_CSD) && (ZRTP_BUILD_FOR_CSD == 1))
|
|
_zrtp_machine_process_while_in_driven_initiator,
|
|
_zrtp_machine_process_while_in_driven_responder,
|
|
_zrtp_machine_process_while_in_driven_pending,
|
|
#endif
|
|
_zrtp_machine_process_while_in_nozrtp
|
|
};
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* State switchers */
|
|
/*===========================================================================*/
|
|
|
|
static void _zrtp_machine_switch_to_error(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_clear_stream_crypto(stream);
|
|
|
|
_zrtp_change_state(stream, ZRTP_STATE_ERROR);
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_security_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_security_event(stream, ZRTP_EVENT_PROTOCOL_ERROR);
|
|
}
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_not_secure) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_not_secure(stream);
|
|
}
|
|
stream->last_error = 0;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_enter_pendingclear(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_PENDINGCLEAR);
|
|
|
|
/*
|
|
* We have to destroy the ZRTP Session Key because user may not press "clear
|
|
* button", and the remote endpoint may subsequently initiate a new secure
|
|
* session. Other secret values will be destroyed in Clear state or
|
|
* rewritten with new.
|
|
*/
|
|
{
|
|
zrtp_string64_t new_zrtpsess = ZSTR_INIT_EMPTY(new_zrtpsess);
|
|
// TODO: hash
|
|
stream->session->hash->hash( stream->session->hash,
|
|
ZSTR_GV(stream->session->zrtpsess),
|
|
ZSTR_GV(new_zrtpsess));
|
|
zrtp_zstrcpy(ZSTR_GV(stream->session->zrtpsess), ZSTR_GV(new_zrtpsess));
|
|
}
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_PENDINGCLEAR);
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static zrtp_status_t _zrtp_machine_enter_initiatingclear(zrtp_stream_t* stream)
|
|
{
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_INITIATINGCLEAR);
|
|
|
|
{
|
|
zrtp_string64_t new_zrtpsess = ZSTR_INIT_EMPTY(new_zrtpsess);
|
|
// TODO: hash
|
|
stream->session->hash->hash( stream->session->hash,
|
|
ZSTR_GV(stream->session->zrtpsess),
|
|
ZSTR_GV(new_zrtpsess));
|
|
zrtp_zstrcpy(ZSTR_GV(stream->session->zrtpsess), ZSTR_GV(new_zrtpsess));
|
|
}
|
|
|
|
return _zrtp_machine_start_send_and_resend_goclear(stream);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static zrtp_status_t _zrtp_machine_enter_clear(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_clear_stream_crypto(stream);
|
|
_zrtp_change_state(stream, ZRTP_STATE_CLEAR);
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_IS_CLEAR);
|
|
}
|
|
|
|
/*
|
|
* Now, let's check if the transition to CLEAR was caused by Active/Passive rules.
|
|
* If local endpoint is a MitM and peer MiTM linked stream is Unlimited, we
|
|
* could break the rules and send commit to Passive endpoint.
|
|
*/
|
|
if (stream->zrtp->is_mitm && stream->peer_passive) {
|
|
if (stream->linked_mitm && stream->linked_mitm->peer_super_flag) {
|
|
ZRTP_LOG(2,(_ZTU_,"INFO: Current stream ID=%u was switched to CLEAR-mode due to Active/Passive"
|
|
" restrictions, but we are running in MiTM mode and peer linked stream is"
|
|
" Super-active. Go Secure!\n", stream->id));
|
|
|
|
/* @note: don't use zrtp_secure_stream() wrapper as it checks for Active/Passive stuff. */
|
|
_zrtp_machine_start_initiating_secure(stream);
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_enter_initiatingerror( zrtp_stream_t *stream,
|
|
zrtp_protocol_error_t code,
|
|
uint8_t notif)
|
|
{
|
|
if ( (ZRTP_STATE_ERROR != stream->state) &&
|
|
(ZRTP_STATE_INITIATINGERROR != stream->state) &&
|
|
(ZRTP_STATE_PENDINGERROR != stream->state) )
|
|
{
|
|
stream->last_error = code;
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tEnter InitiatingError State with ERROR:<%s>, notification %s. ID=%u\n",
|
|
zrtp_log_error2str(stream->last_error), (notif?"Enabled":"Disabled"), stream->id));
|
|
|
|
/* If we can't deliver a ZRTP message, just switch to the ERROR state. */
|
|
if (notif) {
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_INITIATINGERROR);
|
|
_zrtp_machine_start_send_and_resend_error(stream);
|
|
} else {
|
|
_zrtp_machine_switch_to_error(stream);
|
|
}
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
zrtp_status_t _zrtp_machine_enter_pendingerror(zrtp_stream_t *stream, zrtp_protocol_error_t code)
|
|
{
|
|
ZRTP_LOG(3,(_ZTU_,"\tEnter PendingError State with ERROR:<%s>. ID=%u\n",
|
|
zrtp_log_error2str(stream->last_error), stream->id));
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_zrtp_change_state(stream, ZRTP_STATE_PENDINGERROR);
|
|
|
|
stream->last_error = code;
|
|
_zrtp_machine_start_send_and_resend_errorack(stream);
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* Packet handlers */
|
|
/*===========================================================================*/
|
|
|
|
zrtp_status_t _zrtp_machine_process_goclear(zrtp_stream_t* stream, zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_packet_GoClear_t *goclear = (zrtp_packet_GoClear_t*) packet->message;
|
|
zrtp_string128_t clear_hmac = ZSTR_INIT_EMPTY(clear_hmac);
|
|
static const zrtp_string16_t clear_hmac_str = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_CLEAR_HMAC_STR);
|
|
|
|
if (!stream->allowclear) {
|
|
ZRTP_LOG(2, (_ZTU_,"\tWARNING! Allowclear is disabled but GoClear was received. ID=%u.\n", stream->id));
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_goclear_unsp, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
stream->session->hash->hmac( stream->session->hash,
|
|
ZSTR_GV(stream->cc.peer_hmackey),
|
|
ZSTR_GV(clear_hmac_str),
|
|
ZSTR_GV(clear_hmac));
|
|
clear_hmac.length = ZRTP_HMAC_SIZE;
|
|
|
|
if (0 != zrtp_memcmp(clear_hmac.buffer, goclear->clear_hmac, ZRTP_HMAC_SIZE)) {
|
|
ZRTP_LOG(2, (_ZTU_,"\tWARNING! Wrong GoClear hmac. ID=%u.\n", stream->id));
|
|
return zrtp_status_fail; /* EH: Just ignore malformed packets */
|
|
}
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
zrtp_status_t _zrtp_machine_process_hello(zrtp_stream_t* stream, zrtp_rtp_info_t* packet)
|
|
{
|
|
zrtp_session_t* session = stream->session;
|
|
zrtp_packet_Hello_t* peer_hello = NULL;
|
|
uint32_t comp_block_len = 0;
|
|
uint8_t id = 0;
|
|
|
|
/* Size of HELLO packet must be bigger then <RTP+static HELLO part>. */
|
|
if (*(packet->length) < (ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE)) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO static size=%d must be=%d. ID=%u\n", *packet->length,
|
|
ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
peer_hello = (zrtp_packet_Hello_t*) packet->message;
|
|
|
|
/* Now we can verify packet size according to size of its parts */
|
|
comp_block_len = ( peer_hello->hc + peer_hello->cc +
|
|
peer_hello->ac + peer_hello->kc +
|
|
peer_hello->sc) * ZRTP_COMP_TYPE_SIZE;
|
|
|
|
if (*packet->length < (ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + comp_block_len + ZRTP_HMAC_SIZE))
|
|
{
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO dynamic size=%d must be=%d. ID=%u\n", *packet->length,
|
|
comp_block_len+ ZRTP_MIN_PACKET_LENGTH + ZRTP_HELLO_STATIC_SIZE + ZRTP_HMAC_SIZE, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Every component quantity must be less than or equal to 7 */
|
|
if ( (peer_hello->hc > ZRTP_MAX_COMP_COUNT) || (peer_hello->cc > ZRTP_MAX_COMP_COUNT) ||
|
|
(peer_hello->ac > ZRTP_MAX_COMP_COUNT) || (peer_hello->kc > ZRTP_MAX_COMP_COUNT) ||
|
|
(peer_hello->sc > ZRTP_MAX_COMP_COUNT) )
|
|
{
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Wrong HELLO packet data. Components count can't be greater"
|
|
" then 7. ID=%u\n", stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_invalid_packet, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Print out ZRTP Hello message for debug purposes */
|
|
{
|
|
char print_buffer[ZRTP_MAX_COMP_COUNT*20];
|
|
zrtp_memcpy(print_buffer, peer_hello->comp, comp_block_len);
|
|
print_buffer[comp_block_len] = 0;
|
|
ZRTP_LOG(3,(_ZTU_,"\tProcessing HELLO from %.16s V=%.4s, P=%d, M=%d.\n",
|
|
peer_hello->cliend_id, peer_hello->version, peer_hello->pasive, peer_hello->mitmflag));
|
|
ZRTP_LOG(3,(_ZTU_,"\t\tac=%d cc=%d sc=%d kc=%d\n",
|
|
peer_hello->ac, peer_hello->cc, peer_hello->sc, peer_hello->kc));
|
|
ZRTP_LOG(3,(_ZTU_,"\t\t%s\n", print_buffer));
|
|
}
|
|
|
|
/*
|
|
* Check protocol version. Try to resolve versions missmatch according to ZRTP Draft sec. 5.1
|
|
*/
|
|
{
|
|
uint32_t peer_version = 0;
|
|
peer_version = (char)((*peer_hello->version) - '0') *10; /* only 3 first octets are significant */
|
|
peer_version += (char)(*(peer_hello->version+2) - '0');
|
|
|
|
if ((ZRTP_PROTOCOL_VERSION_VALUE/10) == peer_version) {
|
|
ZRTP_LOG(3,(_ZTU_,"\tReceived HELLO had the same protocol V.\n"));
|
|
}
|
|
else if ((ZRTP_PROTOCOL_VERSION_VALUE/10) < peer_version) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO greater ZRTP V=%d - wait for other party"
|
|
" to resolve this issue. ID=%u.\n", peer_version, stream->id));
|
|
} else {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received a ZRTP_HELLO smaller ZRTP V=%d and we don't"
|
|
" support it - terminate session. ID=%u\n", peer_version, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_version, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
}
|
|
|
|
/* Close session if ZID duplication */
|
|
if (!zrtp_memcmp(stream->messages.hello.zid, peer_hello->zid, sizeof(zrtp_zid_t))) {
|
|
ZRTP_LOG(2,(_ZTU_,ZRTP_EQUAL_ZID_WARNING_STR));
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_equal_zid, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* All streams within a single session MUST have the same ZID */
|
|
if (session->peer_zid.length > 0) {
|
|
if (0 != zrtp_memcmp(session->peer_zid.buffer, peer_hello->zid, sizeof(zrtp_zid_t))) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO which had a different ZID from that of the"
|
|
" previous stream within the same session. sID=%u ID=%u\n", session->id, stream->id));
|
|
|
|
_zrtp_machine_enter_initiatingerror(stream, zrtp_error_wrong_zid, 1);
|
|
return zrtp_status_fail;
|
|
}
|
|
} else {
|
|
zrtp_zstrncpyc(ZSTR_GV(session->peer_zid), (const char*) peer_hello->zid, sizeof(zrtp_zid_t));
|
|
}
|
|
|
|
/*
|
|
* Process Remote flags.
|
|
*/
|
|
if (peer_hello->pasive && peer_hello->uflag) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received HELLO which both P and U flags set.\n"));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
stream->peer_passive = peer_hello->pasive;
|
|
stream->peer_super_flag = peer_hello->uflag;
|
|
|
|
stream->peer_mitm_flag = peer_hello->mitmflag;
|
|
if (stream->peer_mitm_flag) {
|
|
stream->mitm_mode = ZRTP_MITM_MODE_CLIENT;
|
|
}
|
|
|
|
/* Current version doesn't support Digital Signatures. Ignore peer Hello with S flag enabled. */
|
|
if (peer_hello->sigflag) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! Received a ZRTP_HELLO with S flag enabled. We don't support Digital Signatures - ignore message.\n"));
|
|
return zrtp_status_fail;
|
|
}
|
|
|
|
/* Copy packet for future hashing */
|
|
zrtp_memcpy(&stream->messages.peer_hello, peer_hello, zrtp_ntoh16(peer_hello->hdr.length)*4);
|
|
stream->is_hello_received = 1;
|
|
|
|
/*
|
|
* Choose PK exchange scheme and PK mode.
|
|
* We do this right after receiving Hello to speedup DH calculations.
|
|
*/
|
|
stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, ZRTP_PKTYPE_DH3072, session->zrtp);
|
|
id = _zrtp_choose_best_comp(&session->profile, peer_hello, ZRTP_CC_PKT);
|
|
if (id != ZRTP_COMP_UNKN) {
|
|
stream->pubkeyscheme = zrtp_comp_find(ZRTP_CC_PKT, id, session->zrtp);
|
|
}
|
|
|
|
ZRTP_LOG(3,(_ZTU_,"\tReceived HELLO Accepted\n"));
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
/*===========================================================================*/
|
|
/* Packet senders */
|
|
/*===========================================================================*/
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_hello(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if ((task->_retrys == ZRTP_NO_ZRTP_FAST_COUNT) && !stream->is_hello_received) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING! HELLO have been resent %d times without a response."
|
|
" Raising ZRTP_EVENT_NO_ZRTP_QUICK event. ID=%u\n", task->_retrys, stream->id));
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_NO_ZRTP_QUICK);
|
|
}
|
|
}
|
|
|
|
if (task->_retrys >= (uint32_t)((ZRTP_STATE_WAIT_HELLOACK==stream->state)?ZRTP_T1_MAX_COUNT_EXT:ZRTP_T1_MAX_COUNT)) {
|
|
ZRTP_LOG(2,(_ZTU_,"WARNING! HELLO Max retransmissions count reached (%d retries). ID=%u\n", task->_retrys, stream->id));
|
|
|
|
_zrtp_cancel_send_packet_later(stream, ZRTP_NONE);
|
|
_clear_stream_crypto(stream);
|
|
_zrtp_change_state(stream, ZRTP_STATE_NO_ZRTP);
|
|
|
|
if (stream->zrtp->cb.event_cb.on_zrtp_protocol_event) {
|
|
stream->zrtp->cb.event_cb.on_zrtp_protocol_event(stream, ZRTP_EVENT_NO_ZRTP);
|
|
}
|
|
} else if (task->_is_enabled) {
|
|
zrtp_status_t s = _zrtp_packet_send_message(stream, ZRTP_HELLO, &stream->messages.hello);
|
|
task->timeout = _zrtp_get_timeout((uint32_t)task->timeout, ZRTP_HELLO);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
|
|
zrtp_status_t _zrtp_machine_start_send_and_resend_hello(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.hello_task;
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_hello;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_hello(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
static void _send_helloack(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_packet_send_message(stream, ZRTP_HELLOACK, NULL);
|
|
}
|
|
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_goclear(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if (task->_is_enabled) {
|
|
if (task->_retrys > ZRTP_T2_MAX_COUNT) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING!: GOCLEAR Nax retransmissions count reached. ID=%u\n", stream->id));
|
|
_zrtp_machine_enter_clear(stream);
|
|
} else {
|
|
zrtp_packet_GoClear_t* goclear = (zrtp_packet_GoClear_t*) &stream->messages.goclear;
|
|
|
|
_zrtp_packet_send_message(stream, ZRTP_GOCLEAR, goclear);
|
|
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 _zrtp_machine_start_send_and_resend_goclear(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.goclear_task;
|
|
zrtp_string128_t clear_hmac = ZSTR_INIT_EMPTY(clear_hmac);
|
|
static const zrtp_string16_t clear_hmac_str = ZSTR_INIT_WITH_CONST_CSTRING(ZRTP_CLEAR_HMAC_STR);
|
|
|
|
zrtp_memset(&stream->messages.goclear, 0, sizeof(zrtp_packet_GoClear_t));
|
|
|
|
/* Compute Clear HMAC as: HMAC(hmackey, "Clear hmac") */
|
|
stream->session->hash->hmac( stream->session->hash,
|
|
ZSTR_GV(stream->cc.hmackey),
|
|
ZSTR_GV(clear_hmac_str),
|
|
ZSTR_GV(clear_hmac));
|
|
clear_hmac.length = ZRTP_HMAC_SIZE;
|
|
|
|
zrtp_memcpy(stream->messages.goclear.clear_hmac, clear_hmac.buffer, clear_hmac.length);
|
|
_zrtp_packet_fill_msg_hdr( stream,
|
|
ZRTP_GOCLEAR,
|
|
sizeof(zrtp_packet_GoClear_t) - sizeof(zrtp_msg_hdr_t),
|
|
&stream->messages.goclear.hdr);
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_goclear;
|
|
task->timeout = ZRTP_T2;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_goclear(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
static void _send_goclearack(zrtp_stream_t* stream)
|
|
{
|
|
_zrtp_packet_send_message(stream, ZRTP_GOCLEARACK, NULL);
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_error(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if (task->_retrys >= ZRTP_ETI_MAX_COUNT) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! ERROR Max retransmissions count reached. ID=%u\n", stream->id));
|
|
_zrtp_machine_switch_to_error(stream);
|
|
} else if (task->_is_enabled) {
|
|
if (zrtp_status_ok == _zrtp_packet_send_message(stream, ZRTP_ERROR, &stream->messages.error)) {
|
|
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 _zrtp_machine_start_send_and_resend_error(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.error_task;
|
|
|
|
zrtp_memset(&stream->messages.error, 0, sizeof(zrtp_packet_Error_t));
|
|
stream->messages.error.code = zrtp_hton32(stream->last_error);
|
|
|
|
_zrtp_packet_fill_msg_hdr( stream,
|
|
ZRTP_ERROR,
|
|
sizeof(zrtp_packet_Error_t) - sizeof(zrtp_msg_hdr_t),
|
|
&stream->messages.error.hdr);
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_error;
|
|
task->timeout = ZRTP_ET;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_error(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
static void _send_and_resend_errorack(zrtp_stream_t* stream, zrtp_retry_task_t* task)
|
|
{
|
|
if (task->_retrys >= ZRTP_ETR_MAX_COUNT) {
|
|
ZRTP_LOG(2,(_ZTU_,"\tWARNING! ERRORACK Max retransmissions count reached. ID=%u\n", stream->id));
|
|
_zrtp_machine_switch_to_error(stream);
|
|
} else if (task->_is_enabled) {
|
|
if (zrtp_status_ok == _zrtp_packet_send_message(stream, ZRTP_ERRORACK, NULL)) {
|
|
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 _zrtp_machine_start_send_and_resend_errorack(zrtp_stream_t* stream)
|
|
{
|
|
zrtp_retry_task_t* task = &stream->messages.errorack_task;
|
|
|
|
task->_is_enabled = 1;
|
|
task->callback = _send_and_resend_errorack;
|
|
task->timeout = ZRTP_ET;
|
|
task->_retrys = 0;
|
|
|
|
_send_and_resend_errorack(stream, task);
|
|
|
|
return zrtp_status_ok;
|
|
}
|
|
|
|
|
|
void _clear_stream_crypto(zrtp_stream_t* stream)
|
|
{
|
|
if (stream->protocol) {
|
|
_zrtp_protocol_destroy(stream->protocol);
|
|
stream->protocol = 0;
|
|
}
|
|
|
|
zrtp_wipe_zstring(ZSTR_GV(stream->cc.hmackey));
|
|
zrtp_wipe_zstring(ZSTR_GV(stream->cc.peer_hmackey));
|
|
zrtp_wipe_zstring(ZSTR_GV(&stream->cc.zrtp_key));
|
|
zrtp_wipe_zstring(ZSTR_GV(stream->cc.peer_zrtp_key));
|
|
}
|