From 6884e427ef4ced8ded52e3e210d5fc465e9cf1ba Mon Sep 17 00:00:00 2001 From: Daniel Gultsch Date: Fri, 10 Apr 2020 07:45:23 +0200 Subject: [PATCH] require dtls and ensure procceds get tracked --- .../generator/MessageGenerator.java | 2 +- .../conversations/parser/MessageParser.java | 13 +++++++---- .../xmpp/jingle/AbstractJingleConnection.java | 3 ++- .../xmpp/jingle/JingleConnectionManager.java | 8 +++++++ .../xmpp/jingle/JingleRtpConnection.java | 22 ++++++++++++++----- .../xmpp/jingle/RtpContentMap.java | 22 +++++++++++++++---- 6 files changed, 55 insertions(+), 15 deletions(-) diff --git a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java index 7e93303cd..d61e42b22 100644 --- a/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java +++ b/src/main/java/eu/siacs/conversations/generator/MessageGenerator.java @@ -238,7 +238,7 @@ public class MessageGenerator extends AbstractGenerator { final MessagePacket packet = new MessagePacket(); packet.setType(MessagePacket.TYPE_CHAT); //we want to carbon copy those packet.setTo(proposal.with); - packet.setId(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX+proposal.sessionId); + packet.setId(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX +proposal.sessionId); final Element propose = packet.addChild("propose", Namespace.JINGLE_MESSAGE); propose.setAttribute("id", proposal.sessionId); propose.addChild("description", Namespace.JINGLE_APPS_RTP); diff --git a/src/main/java/eu/siacs/conversations/parser/MessageParser.java b/src/main/java/eu/siacs/conversations/parser/MessageParser.java index 4b15b8b72..f7c40269e 100644 --- a/src/main/java/eu/siacs/conversations/parser/MessageParser.java +++ b/src/main/java/eu/siacs/conversations/parser/MessageParser.java @@ -306,12 +306,17 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece final Jid from = packet.getFrom(); final String id = packet.getId(); if (from != null && id != null) { - if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX)) { - final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX.length()); + if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX)) { + final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX.length()); mXmppConnectionService.getJingleConnectionManager() .updateProposedSessionDiscovered(account, from, sessionId, JingleConnectionManager.DeviceDiscoveryState.FAILED); return true; } + if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROCEED_ID_PREFIX)) { + final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_PROCEED_ID_PREFIX.length()); + mXmppConnectionService.getJingleConnectionManager().failProceed(account, from, sessionId); + return true; + } mXmppConnectionService.markMessage(account, from.asBareJid(), id, @@ -845,8 +850,8 @@ public class MessageParser extends AbstractParser implements OnMessagePacketRece query.removePendingReceiptRequest(new ReceiptRequest(packet.getTo(), id)); } } else if (id != null) { - if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX)) { - final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_ID_PREFIX.length()); + if (id.startsWith(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX)) { + final String sessionId = id.substring(JingleRtpConnection.JINGLE_MESSAGE_PROPOSE_ID_PREFIX.length()); mXmppConnectionService.getJingleConnectionManager() .updateProposedSessionDiscovered(account, from, sessionId, JingleConnectionManager.DeviceDiscoveryState.DISCOVERED); } else { diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java index 6cf7d2a91..bea185902 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/AbstractJingleConnection.java @@ -11,7 +11,8 @@ import rocks.xmpp.addr.Jid; public abstract class AbstractJingleConnection { - public static final String JINGLE_MESSAGE_ID_PREFIX = "jm-propose-"; + public static final String JINGLE_MESSAGE_PROPOSE_ID_PREFIX = "jm-propose-"; + public static final String JINGLE_MESSAGE_PROCEED_ID_PREFIX = "jm-proceed-"; protected final JingleConnectionManager jingleConnectionManager; protected final XmppConnectionService xmppConnectionService; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java index 88ce13d6e..f5fb9c1dc 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleConnectionManager.java @@ -354,6 +354,14 @@ public class JingleConnectionManager extends AbstractConnectionManager { } } + public void failProceed(Account account, final Jid with, String sessionId) { + final AbstractJingleConnection.Id id = AbstractJingleConnection.Id.of(account, with, sessionId); + final AbstractJingleConnection existingJingleConnection = connections.get(id); + if (existingJingleConnection instanceof JingleRtpConnection) { + ((JingleRtpConnection) existingJingleConnection).deliverFailedProceed(); + } + } + public static class RtpSessionProposal { private final Account account; public final Jid with; diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java index 5a8359de7..946d021ab 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/JingleRtpConnection.java @@ -81,7 +81,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web private RtpContentMap responderRtpContentMap; - public JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) { + JingleRtpConnection(JingleConnectionManager jingleConnectionManager, Id id, Jid initiator) { super(jingleConnectionManager, id, initiator); } @@ -186,8 +186,8 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web try { contentMap = RtpContentMap.of(jinglePacket); contentMap.requireContentDescriptions(); - //TODO requireTransportWithDtls(); - } catch (IllegalArgumentException | IllegalStateException | NullPointerException e) { + contentMap.requireDTLSFingerprint(); + } catch (final IllegalArgumentException | IllegalStateException | NullPointerException e) { respondOk(jinglePacket); sendSessionTerminate(Reason.FAILED_APPLICATION); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents", e); @@ -226,7 +226,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web try { contentMap = RtpContentMap.of(jinglePacket); contentMap.requireContentDescriptions(); - //TODO requireTransportWithDtls(); + contentMap.requireDTLSFingerprint(); } catch (IllegalArgumentException | IllegalStateException | NullPointerException e) { respondOk(jinglePacket); Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": improperly formatted contents in session-accept", e); @@ -351,6 +351,15 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web } } + void deliverFailedProceed() { + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": receive message error for proceed message"); + if (transition(State.TERMINATED_CONNECTIVITY_ERROR)) { + webRTCWrapper.close(); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": transitioned into connectivity error"); + this.jingleConnectionManager.finishConnection(this); + } + } + private void receiveAccept(Jid from, Element message) { final boolean originatedFromMyself = from.asBareJid().equals(id.account.getJid().asBareJid()); if (originatedFromMyself) { @@ -533,7 +542,7 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web } private void terminateWithOutOfOrder(final JinglePacket jinglePacket) { - Log.d(Config.LOGTAG,id.account.getJid().asBareJid()+": terminating session with out-of-order"); + Log.d(Config.LOGTAG, id.account.getJid().asBareJid() + ": terminating session with out-of-order"); webRTCWrapper.close(); transitionOrThrow(State.TERMINATED_APPLICATION_FAILURE); respondWithOutOfOrder(jinglePacket); @@ -681,6 +690,9 @@ public class JingleRtpConnection extends AbstractJingleConnection implements Web private void sendJingleMessage(final String action, final Jid to) { final MessagePacket messagePacket = new MessagePacket(); + if ("proceed".equals(action)) { + messagePacket.setId(JINGLE_MESSAGE_PROCEED_ID_PREFIX + id.sessionId); + } messagePacket.setType(MessagePacket.TYPE_CHAT); //we want to carbon copy those messagePacket.setTo(to); messagePacket.addChild(action, Namespace.JINGLE_MESSAGE).setAttribute("id", id.sessionId); diff --git a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java index 1ebd810b7..fcac3d729 100644 --- a/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java +++ b/src/main/java/eu/siacs/conversations/xmpp/jingle/RtpContentMap.java @@ -4,6 +4,7 @@ import android.util.Log; import com.google.common.base.Function; import com.google.common.base.Preconditions; +import com.google.common.base.Strings; import com.google.common.collect.ImmutableMap; import com.google.common.collect.Iterables; import com.google.common.collect.Maps; @@ -51,13 +52,26 @@ public class RtpContentMap { if (this.contents.size() == 0) { throw new IllegalStateException("No contents available"); } - for(Map.Entry entry : this.contents.entrySet()) { + for (Map.Entry entry : this.contents.entrySet()) { if (entry.getValue().description == null) { throw new IllegalStateException(String.format("%s is lacking content description", entry.getKey())); } } } + public void requireDTLSFingerprint() { + if (this.contents.size() == 0) { + throw new IllegalStateException("No contents available"); + } + for (Map.Entry entry : this.contents.entrySet()) { + final IceUdpTransportInfo transport = entry.getValue().transport; + final IceUdpTransportInfo.Fingerprint fingerprint = transport.getFingerprint(); + if (fingerprint == null || Strings.isNullOrEmpty(fingerprint.getContent()) || Strings.isNullOrEmpty(fingerprint.getHash())) { + throw new SecurityException(String.format("Use of DTLS-SRTP (XEP-0320) is required for content %s", entry.getKey())); + } + } + } + public JinglePacket toJinglePacket(final JinglePacket.Action action, final String sessionId) { final JinglePacket jinglePacket = new JinglePacket(action, sessionId); if (this.group != null) { @@ -75,14 +89,14 @@ public class RtpContentMap { } public RtpContentMap transportInfo(final String contentName, final IceUdpTransportInfo.Candidate candidate) { - final RtpContentMap.DescriptionTransport descriptionTransport = contents.get(contentName); + final RtpContentMap.DescriptionTransport descriptionTransport = contents.get(contentName); final IceUdpTransportInfo transportInfo = descriptionTransport == null ? null : descriptionTransport.transport; if (transportInfo == null) { - throw new IllegalArgumentException("Unable to find transport info for content name "+contentName); + throw new IllegalArgumentException("Unable to find transport info for content name " + contentName); } final IceUdpTransportInfo newTransportInfo = transportInfo.cloneWrapper(); newTransportInfo.addChild(candidate); - return new RtpContentMap(null, ImmutableMap.of(contentName, new DescriptionTransport(null,newTransportInfo))); + return new RtpContentMap(null, ImmutableMap.of(contentName, new DescriptionTransport(null, newTransportInfo))); }