/** * @file stream.cpp GNU ZRTP: Stream class implementation * * Copyright (C) 2010 - 2017 Creytiv.com */ #include #include #include #include #include #include #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 = 1; // TODO: randomize sa_init(&m_raddr, AF_INET); tmr_init(&m_zrtp_timer); pthread_mutexattr_t attr; err = pthread_mutexattr_init(&attr); err |= pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK); err |= pthread_mutex_init(&m_zrtp_mutex, &attr); err |= pthread_mutex_init(&m_send_mutex, &attr); 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() { stop(); delete m_zrtp; mem_deref(m_uh_rtp); mem_deref(m_uh_rtcp); mem_deref(m_rtpsock); mem_deref(m_rtcpsock); pthread_mutex_destroy(&m_zrtp_mutex); pthread_mutex_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(); pthread_mutex_lock(&m_send_mutex); delete m_send_srtp; m_send_srtp = NULL; pthread_mutex_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) { // TODO: 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); } // TODO: 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; pthread_mutex_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: pthread_mutex_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) { 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; } // + + + 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) { pthread_mutex_lock(&m_send_mutex); m_send_srtp = s; pthread_mutex_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) { pthread_mutex_lock(&m_send_mutex); delete m_send_srtp; m_send_srtp = NULL; pthread_mutex_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; 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()); } } void Stream::handleGoClear() { } void Stream::zrtpNegotiationFailed(GnuZrtpCodes::MessageSeverity severity, int32_t subCode) { } void Stream::zrtpNotSuppOther() { } void Stream::synchEnter() { pthread_mutex_lock(&m_zrtp_mutex); } void Stream::synchLeave() { pthread_mutex_unlock(&m_zrtp_mutex); } void Stream::zrtpAskEnrollment(GnuZrtpCodes::InfoEnrollment info) { } void Stream::zrtpInformEnrollment(GnuZrtpCodes::InfoEnrollment info) { } void Stream::signSAS(uint8_t* sasHash) { } bool Stream::checkSASSignature(uint8_t* sasHash) { return true; }