baresip/modules/gzrtp/stream.cpp
2023-03-27 16:50:06 +02:00

731 lines
14 KiB
C++

/**
* @file stream.cpp GNU ZRTP: Stream class implementation
*
* Copyright (C) 2010 - 2017 Alfred E. Heggestad
*/
#include <stdint.h>
#include <re.h>
#include <baresip.h>
#include <libzrtpcpp/ZRtp.h>
#include <libzrtpcpp/ZrtpStateClass.h>
#include "session.h"
#include "stream.h"
#include "srtp.h"
// A burst of SRTP/SRTCP errors enough to display a warning
// Set to 1 to display all warnings
#define SRTP_ERR_BURST_THRESHOLD 20
enum {
PRESZ = 36 /* Preamble size for TURN/STUN header */
};
enum pkt_type {
PKT_TYPE_UNKNOWN = 0,
PKT_TYPE_RTP = 1,
PKT_TYPE_RTCP = 2,
PKT_TYPE_ZRTP = 4
};
static enum pkt_type get_packet_type(const struct mbuf *mb)
{
uint8_t b, pt;
uint32_t magic;
if (mbuf_get_left(mb) < 8)
return PKT_TYPE_UNKNOWN;
b = mbuf_buf(mb)[0];
if (127 < b && b < 192) {
pt = mbuf_buf(mb)[1] & 0x7f;
if (72 <= pt && pt <= 76)
return PKT_TYPE_RTCP;
else
return PKT_TYPE_RTP;
}
else {
memcpy(&magic, &mbuf_buf(mb)[4], 4);
magic = ntohl(magic);
if (magic == ZRTP_MAGIC)
return PKT_TYPE_ZRTP;
}
return PKT_TYPE_UNKNOWN;
}
ZRTPConfig::ZRTPConfig(const struct conf *conf, const char *conf_dir)
{
#ifdef GZRTP_USE_RE_SRTP
// Standard ciphers only
zrtp.clear();
zrtp.addAlgo(HashAlgorithm, zrtpHashes.getByName(s256));
zrtp.addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes3));
zrtp.addAlgo(CipherAlgorithm, zrtpSymCiphers.getByName(aes1));
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec25));
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh3k));
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(ec38));
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(dh2k));
zrtp.addAlgo(PubKeyAlgorithm, zrtpPubKeys.getByName(mult));
zrtp.addAlgo(SasType, zrtpSasTypes.getByName(b32));
zrtp.addAlgo(AuthLength, zrtpAuthLengths.getByName(hs32));
zrtp.addAlgo(AuthLength, zrtpAuthLengths.getByName(hs80));
#else
zrtp.setStandardConfig();
#endif
str_ncpy(client_id, "baresip/gzrtp", sizeof(client_id));
re_snprintf(zid_filename, sizeof(zid_filename),
"%s/gzrtp.zid", conf_dir);
start_parallel = true;
(void)conf_get_bool(conf, "zrtp_parallel", &start_parallel);
}
SRTPStat::SRTPStat(const Stream *st, bool srtcp, uint64_t threshold)
: m_stream(st)
, m_control(srtcp)
, m_threshold(threshold)
{
reset();
}
void SRTPStat::update(int ret_code, bool quiet)
{
const char *err_msg;
uint64_t *burst;
// Srtp::unprotect/unprotect_ctrl return codes
switch (ret_code) {
case 0:
++m_ok;
m_decode_burst = 0;
m_auth_burst = 0;
m_replay_burst = 0;
return;
case EBADMSG:
++m_decode;
burst = &m_decode_burst;
err_msg = "packet decode error";
break;
case EAUTH:
++m_auth;
burst = &m_auth_burst;
err_msg = "authentication failed";
break;
case EALREADY:
++m_replay;
burst = &m_replay_burst;
err_msg = "replay check failed";
break;
default:
warning("zrtp: %s unprotect failed: %m\n",
(m_control)? "SRTCP" : "SRTP", ret_code);
return;
}
++(*burst);
if (*burst == m_threshold) {
*burst = 0;
if (!quiet)
warning("zrtp: Stream <%s>: %s %s, %d packets\n",
m_stream->media_name(),
(m_control)? "SRTCP" : "SRTP",
err_msg,
m_threshold);
}
}
void SRTPStat::reset()
{
m_ok = 0;
m_decode = 0; m_auth = 0; m_replay = 0;
m_decode_burst = 0; m_auth_burst = 0; m_replay_burst = 0;
}
Stream::Stream(int& err, const ZRTPConfig& config, Session *session,
udp_sock *rtpsock, udp_sock *rtcpsock,
uint32_t local_ssrc, StreamMediaType media_type)
: m_session(session)
, m_zrtp(NULL)
, m_started(false)
, m_local_ssrc(local_ssrc)
, m_peer_ssrc(0)
, m_rtpsock(NULL)
, m_rtcpsock(NULL)
, m_uh_rtp(NULL)
, m_uh_rtcp(NULL)
, m_media_type(media_type)
, m_send_srtp(NULL)
, m_recv_srtp(NULL)
, m_srtp_stat(this, false, SRTP_ERR_BURST_THRESHOLD)
, m_srtcp_stat(this, true, SRTP_ERR_BURST_THRESHOLD)
{
err = 0;
m_zrtp_seq = rand_u16() & 0x7fff;
sa_init(&m_raddr, AF_INET);
tmr_init(&m_zrtp_timer);
err |= mtx_init(&m_zrtp_mutex, mtx_plain) != thrd_success;
err |= mtx_init(&m_send_mutex, mtx_plain) != thrd_success;
if (err)
return;
int layer = 10; // above zero
if (rtpsock) {
m_rtpsock = (struct udp_sock *)mem_ref(rtpsock);
err |= udp_register_helper(&m_uh_rtp, rtpsock, layer,
Stream::udp_helper_send_cb,
Stream::udp_helper_recv_cb,
this);
}
if (rtcpsock && (rtcpsock != rtpsock)) {
m_rtcpsock = (struct udp_sock *)mem_ref(rtcpsock);
err |= udp_register_helper(&m_uh_rtcp, rtcpsock, layer,
Stream::udp_helper_send_cb,
Stream::udp_helper_recv_cb,
this);
}
if (err)
return;
ZIDCache* zf = getZidCacheInstance();
if (!zf->isOpen()) {
if (zf->open((char *)config.zid_filename) == -1) {
warning("zrtp: Couldn't open/create ZID file %s\n",
config.zid_filename);
err = ENOENT;
return;
}
}
m_zrtp = new ZRtp((uint8_t *)zf->getZid(), this, config.client_id,
(ZrtpConfigure *)&config.zrtp, false, false);
if (!m_zrtp) {
err = ENOMEM;
return;
}
return;
}
Stream::~Stream()
{
mem_deref(m_uh_rtp);
mem_deref(m_uh_rtcp);
stop();
delete m_zrtp;
mem_deref(m_rtpsock);
mem_deref(m_rtcpsock);
mtx_destroy(&m_zrtp_mutex);
mtx_destroy(&m_send_mutex);
tmr_cancel(&m_zrtp_timer);
}
int Stream::start(Stream *master)
{
if (started())
return EPERM;
if (master) {
ZRtp *zrtp_master;
std::string params =
master->m_zrtp->getMultiStrParams(&zrtp_master);
if (params.empty())
return EPROTO;
m_zrtp->setMultiStrParams(params, zrtp_master);
}
debug("zrtp: Starting <%s> stream%s\n", media_name(),
(m_zrtp->isMultiStream())? " (multistream)" : "");
m_srtp_stat.reset();
m_srtcp_stat.reset();
m_sas.clear();
m_ciphers.clear();
m_started = true;
m_zrtp->startZrtpEngine();
return 0;
}
void Stream::stop()
{
if (!started())
return;
m_started = false;
// If we got only a small amount of valid SRTP packets after ZRTP
// negotiation then assume that our peer couldn't store the RS data,
// thus make sure we have a second retained shared secret available.
// Refer to RFC 6189bis, chapter 4.6.1 50 packets are about 1 second
// of audio data
if (!m_zrtp->isMultiStream() && m_recv_srtp && m_srtp_stat.ok() < 20) {
debug("zrtp: Stream <%s>: received too few valid SRTP "
"packets (%u), storing RS2\n",
media_name(), m_srtp_stat.ok());
m_zrtp->setRs2Valid();
}
debug("zrtp: Stopping <%s> stream\n", media_name());
m_zrtp->stopZrtp();
mtx_lock(&m_send_mutex);
delete m_send_srtp;
m_send_srtp = NULL;
mtx_unlock(&m_send_mutex);
delete m_recv_srtp;
m_recv_srtp = NULL;
debug("zrtp: Stream <%s> stopped\n", media_name());
}
int Stream::sdp_encode(struct sdp_media *sdpm)
{
(void)sdpm;
// NOTE: signaling hash
return 0;
}
int Stream::sdp_decode(const struct sdp_media *sdpm)
{
if (sa_isset(sdp_media_raddr(sdpm), SA_ALL)) {
m_raddr = *sdp_media_raddr(sdpm);
}
// NOTE: signaling hash
return 0;
}
bool Stream::udp_helper_send_cb(int *err, struct sa *src, struct mbuf *mb,
void *arg)
{
Stream *st = (Stream *)arg;
if (st)
return st->udp_helper_send(err, src, mb);
return false;
}
bool Stream::udp_helper_send(int *err, struct sa *src, struct mbuf *mb)
{
bool ret = false;
enum pkt_type ptype = get_packet_type(mb);
size_t len = mbuf_get_left(mb);
int rerr = 0;
(void)src;
mtx_lock(&m_send_mutex);
if (ptype == PKT_TYPE_RTCP && m_send_srtp && len > 8) {
rerr = m_send_srtp->protect_ctrl(mb);
}
else if (ptype == PKT_TYPE_RTP && m_send_srtp &&
len > RTP_HEADER_SIZE) {
rerr = m_send_srtp->protect(mb);
}
else
goto out;
if (rerr) {
warning("zrtp: protect/protect_ctrl failed (len=%u): %m\n",
len, rerr);
if (rerr == ENOMEM)
*err = rerr;
// drop
ret = true;
}
out:
mtx_unlock(&m_send_mutex);
return ret;
}
bool Stream::udp_helper_recv_cb(struct sa *src, struct mbuf *mb, void *arg)
{
Stream *st = (Stream *)arg;
if (st)
return st->udp_helper_recv(src, mb);
return false;
}
bool Stream::udp_helper_recv(struct sa *src, struct mbuf *mb)
{
(void)src;
if (!started())
return false;
enum pkt_type ptype = get_packet_type(mb);
int err = 0;
if (ptype == PKT_TYPE_RTCP && m_recv_srtp) {
err = m_recv_srtp->unprotect_ctrl(mb);
m_srtcp_stat.update(err);
}
else if (ptype == PKT_TYPE_RTP && m_recv_srtp) {
err = m_recv_srtp->unprotect(mb);
m_srtp_stat.update(err);
if (!err) {
// Got a good SRTP, check state and if in WaitConfAck
// (an Initiator state) then simulate a conf2Ack,
// refer to RFC 6189, chapter 4.6, last paragraph
if (m_zrtp->inState(WaitConfAck))
m_zrtp->conf2AckSecure();
}
}
else if (ptype == PKT_TYPE_ZRTP) {
return recv_zrtp(mb);
}
else
return false;
if (err)
// drop
return true;
return false;
}
// <RTP> + <ext. header> + <ZRTP message type> + CRC32
#define ZRTP_MIN_PACKET_LENGTH (RTP_HEADER_SIZE + 4 + 8 + 4)
bool Stream::recv_zrtp(struct mbuf *mb)
{
uint32_t crc32;
uint8_t *buf = mbuf_buf(mb);
size_t size = mbuf_get_left(mb);
if (size < ZRTP_MIN_PACKET_LENGTH) {
warning("zrtp: incoming packet size (%d) is too small\n",
size);
return false;
}
// check CRC
memcpy(&crc32, buf + size - 4, 4);
crc32 = ntohl(crc32);
if (!zrtpCheckCksum(buf, size - 4, crc32)) {
sendInfo(GnuZrtpCodes::Warning,
GnuZrtpCodes::WarningCRCmismatch);
return false;
}
// store peer's SSRC for creating the CryptoContext
memcpy(&m_peer_ssrc, buf + 8, 4);
m_peer_ssrc = ntohl(m_peer_ssrc);
m_zrtp->processZrtpMessage(buf + RTP_HEADER_SIZE, m_peer_ssrc, size);
return true;
}
void Stream::verify_sas(bool verify)
{
if (verify)
m_zrtp->SASVerified();
else
m_zrtp->resetSASVerified();
}
bool Stream::sas_verified()
{
return m_zrtp->isSASVerified();
}
//
// callbacks
//
int32_t Stream::sendDataZRTP(const uint8_t* data, int32_t length)
{
struct mbuf *mb;
uint8_t *crc_buf;
uint32_t crc32;
size_t start_pos = PRESZ;
int err = 0;
if (!sa_isset(&m_raddr, SA_ALL))
return 0;
mb = mbuf_alloc(start_pos + RTP_HEADER_SIZE + length);
if (!mb)
return 0;
mbuf_set_end(mb, start_pos);
mbuf_set_pos(mb, start_pos);
crc_buf = mbuf_buf(mb);
// write RTP header
err = mbuf_write_u8(mb, 0x10);
err |= mbuf_write_u8(mb, 0x00);
err |= mbuf_write_u16(mb, htons(m_zrtp_seq++));
err |= mbuf_write_u32(mb, htonl(ZRTP_MAGIC));
err |= mbuf_write_u32(mb, htonl(m_local_ssrc));
// copy ZRTP message data
err |= mbuf_write_mem(mb, data, length - 4);
// compute CRC
crc32 = zrtpGenerateCksum(crc_buf, RTP_HEADER_SIZE + length - 4);
crc32 = zrtpEndCksum(crc32);
// store CRC
err |= mbuf_write_u32(mb, htonl(crc32));
if (err)
goto out;
// send ZRTP packet using RTP socket
mbuf_set_pos(mb, start_pos);
err = udp_send_helper(m_rtpsock, &m_raddr, mb, m_uh_rtp);
if (err)
warning("zrtp: udp_send_helper: %m\n", err);
out:
mem_deref(mb);
return (err == 0);
}
void Stream::zrtp_timer_cb(void *arg)
{
Stream *s = (Stream *)arg;
s->m_zrtp->processTimeout();
}
int32_t Stream::activateTimer(int32_t time)
{
tmr_start(&m_zrtp_timer, time, &Stream::zrtp_timer_cb, this);
return 1;
}
int32_t Stream::cancelTimer()
{
tmr_cancel(&m_zrtp_timer);
return 1;
}
void Stream::sendInfo(GnuZrtpCodes::MessageSeverity severity, int32_t subCode)
{
print_message(severity, subCode);
if (severity == GnuZrtpCodes::Info) {
if (subCode == GnuZrtpCodes::InfoSecureStateOn) {
m_session->on_secure(this);
}
else if (subCode == GnuZrtpCodes::InfoHelloReceived &&
!m_zrtp->isMultiStream()) {
m_session->request_master(this);
}
}
}
bool Stream::srtpSecretsReady(SrtpSecret_t* secrets, EnableSecurity part)
{
Srtp *s;
int err = 0;
debug("zrtp: Stream <%s>: secrets are ready for %s\n",
media_name(),
(part == ForSender)? "sender" : "receiver");
s = new Srtp(err, secrets, part);
if (!s || err) {
warning("zrtp: Stream <%s>: Srtp creation failed: %m\n",
media_name(), err);
delete s;
return false;
}
if (part == ForSender) {
mtx_lock(&m_send_mutex);
m_send_srtp = s;
mtx_unlock(&m_send_mutex);
}
else if (part == ForReceiver)
m_recv_srtp = s;
else
return false;
return true;
}
void Stream::srtpSecretsOff(EnableSecurity part)
{
debug("zrtp: Stream <%s>: secrets are off for %s\n",
media_name(),
(part == ForSender)? "sender" : "receiver");
if (part == ForSender) {
mtx_lock(&m_send_mutex);
delete m_send_srtp;
m_send_srtp = NULL;
mtx_unlock(&m_send_mutex);
}
if (part == ForReceiver) {
delete m_recv_srtp;
m_recv_srtp = NULL;
}
}
void Stream::srtpSecretsOn(std::string c, std::string s, bool verified)
{
m_sas = s;
m_ciphers = c;
char buf[128] = "";
if (s.empty()) {
info("zrtp: Stream <%s> is encrypted (%s)\n",
media_name(), c.c_str());
}
else {
info("zrtp: Stream <%s> is encrypted (%s), "
"SAS is [%s] (%s)\n",
media_name(), c.c_str(), s.c_str(),
(verified)? "verified" : "NOT VERIFIED");
if (!verified) {
warning("zrtp: SAS is not verified, type "
"'/zrtp_verify %d' to verify\n",
m_session->id());
if (m_session->eventh) {
if (re_snprintf(buf, sizeof(buf), "%s,%d",
s.c_str(),
m_session->id()))
(m_session->eventh)
(MENC_EVENT_VERIFY_REQUEST,
buf,
NULL,
m_session->arg);
else
warning("zrtp: failed to print verify"
" arguments\n");
}
}
}
}
void Stream::handleGoClear()
{
}
void Stream::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity,
int32_t subCode)
{
(void)severity;
(void)subCode;
}
void Stream::zrtpNotSuppOther()
{
}
void Stream::synchEnter()
{
mtx_lock(&m_zrtp_mutex);
}
void Stream::synchLeave()
{
mtx_unlock(&m_zrtp_mutex);
}
void Stream::zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info)
{
(void)info;
}
void Stream::zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info)
{
(void)info;
}
void Stream::signSAS(uint8_t* sasHash)
{
(void)sasHash;
}
bool Stream::checkSASSignature(uint8_t* sasHash)
{
(void)sasHash;
return true;
}