From 83d9fdb16885f947b797aff3a47d9986b264c2cb Mon Sep 17 00:00:00 2001 From: Richard Alam <ritzalam@gmail.com> Date: Thu, 25 Jan 2018 12:00:07 -0800 Subject: [PATCH] - lock down screenshare to prevent unauthorized publishing of stream into the red5 app --- .../screenshare/IScreenShareApplication.java | 1 + .../UnauthorizedBroadcastStreamEvent.java | 15 +++++ .../red5/CloseConnectionMessage.java | 16 +++++ .../red5/ConnectionInvokerService.java | 67 ++++++++++++++++--- .../screenshare/red5/EventListenerImp.java | 11 +++ .../app/screenshare/red5/Red5AppAdapter.java | 11 ++- .../screenshare/ScreenShareApplication.scala | 5 ++ .../server/sessions/Screenshare.scala | 29 ++++++-- .../server/sessions/ScreenshareManager.scala | 19 +++++- .../server/sessions/messages/IMessage.scala | 2 + 10 files changed, 157 insertions(+), 19 deletions(-) create mode 100755 bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/UnauthorizedBroadcastStreamEvent.java create mode 100755 bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/CloseConnectionMessage.java diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/IScreenShareApplication.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/IScreenShareApplication.java index a99e0e9aaf..78728cd6fb 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/IScreenShareApplication.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/IScreenShareApplication.java @@ -26,4 +26,5 @@ public interface IScreenShareApplication { void meetingHasEnded(String meetingId); void meetingCreated(String meetingId, Boolean record); void screenShareClientPongMessage(String meetingId, String userId, String streamId, Long timestamp); + void authorizeBroadcastStream(String meetingId, String streamId, String connId, String scope); } diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/UnauthorizedBroadcastStreamEvent.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/UnauthorizedBroadcastStreamEvent.java new file mode 100755 index 0000000000..7de53f2967 --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/events/UnauthorizedBroadcastStreamEvent.java @@ -0,0 +1,15 @@ +package org.bigbluebutton.app.screenshare.events; + +public class UnauthorizedBroadcastStreamEvent implements IEvent { + public final String meetingId; + public final String streamId; + public final String connId; + public final String scope; + + public UnauthorizedBroadcastStreamEvent(String meetingId, String streamId, String connId, String scope) { + this.meetingId = meetingId; + this.streamId = streamId; + this.connId = connId; + this.scope = scope; + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/CloseConnectionMessage.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/CloseConnectionMessage.java new file mode 100755 index 0000000000..dbcbd49dfb --- /dev/null +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/CloseConnectionMessage.java @@ -0,0 +1,16 @@ +package org.bigbluebutton.app.screenshare.red5; + +public class CloseConnectionMessage implements ClientMessage { + + public final String meetingId; + public final String streamId; + public final String connId; + public final String scope; + + public CloseConnectionMessage(String meetingId, String streamId, String connId, String scope) { + this.meetingId = meetingId; + this.streamId = streamId; + this.connId = connId; + this.scope = scope; + } +} diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/ConnectionInvokerService.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/ConnectionInvokerService.java index 1b5db18896..12128c8a91 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/ConnectionInvokerService.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/ConnectionInvokerService.java @@ -18,11 +18,8 @@ */ package org.bigbluebutton.app.screenshare.red5; -import java.util.Set; -import java.util.ArrayList; -import java.util.List; +import java.util.*; import java.util.concurrent.BlockingQueue; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.Executor; import java.util.concurrent.Executors; import java.util.concurrent.LinkedBlockingQueue; @@ -36,6 +33,7 @@ import org.red5.server.api.so.ISharedObjectService; import org.red5.server.so.SharedObjectService; import org.red5.server.util.ScopeUtils; import org.slf4j.Logger; +import com.google.gson.Gson; public class ConnectionInvokerService { private static Logger log = Red5LoggerFactory.getLogger(ConnectionInvokerService.class, "screenshare"); @@ -74,7 +72,7 @@ public class ConnectionInvokerService { } } }; - exec.execute(sender); + exec.execute(sender); } public void stop() { @@ -96,8 +94,43 @@ public class ConnectionInvokerService { handlDisconnectClientMessage((DisconnectClientMessage) message); } else if (message instanceof DisconnectAllClientsMessage) { handleDisconnectAllClientsMessage((DisconnectAllClientsMessage) message); + } else if (message instanceof CloseConnectionMessage) { + handleCloseConnectionMessage((CloseConnectionMessage) message); } - } + } + + private void handleCloseConnectionMessage(CloseConnectionMessage msg) { + Map<String, Object> logData = new HashMap<String, Object>(); + logData.put("meetingId", msg.meetingId); + logData.put("connId", msg.connId); + logData.put("streamId", msg.streamId); + logData.put("scope", msg.scope); + logData.put("event", "unauth_publish_stream_bbb_screenshare"); + logData.put("description", "Unauthorized publish stream."); + + Gson gson = new Gson(); + String logStr = gson.toJson(logData); + log.info(logStr); + + IScope meetingScope = null; + + if (bbbAppScope.getName().equals(msg.scope)) { + meetingScope = bbbAppScope; + } else { + meetingScope = getScope(msg.scope); + } + + if (meetingScope != null) { + IConnection conn = getConnectionWithConnId(meetingScope, msg.connId); + if (conn != null) { + if (conn.isConnected()) { + log.info("Disconnecting connection. data={}", logStr); + conn.close(); + } + } + } + + } private void handleDisconnectAllClientsMessage(DisconnectAllClientsMessage msg) { IScope meetingScope = getScope(msg.getMeetingId()); @@ -123,9 +156,9 @@ public class ConnectionInvokerService { log.info("Disconnecting user=[{}] from meeting=[{}]", msg.getUserId(), msg.getMeetingId()); conn.close(); } - } - } - } + } + } + } private void sendSharedObjectMessage(SharedObjectClientMessage msg) { System.out.println("*********** Request to send [" + msg.getMessageName() + "] using shared object."); @@ -153,14 +186,14 @@ public class ConnectionInvokerService { public void run() { IScope meetingScope = getScope(msg.getMeetingID()); if (meetingScope != null) { - log.debug("Found scope =[{}] for meeting=[{}]", meetingScope.getName(), msg.getMeetingID()); + //log.debug("Found scope =[{}] for meeting=[{}]", meetingScope.getName(), msg.getMeetingID()); IConnection conn = getConnection(meetingScope, msg.getUserID()); if (conn != null) { if (conn.isConnected()) { List<Object> params = new ArrayList<Object>(); params.add(msg.getMessageName()); params.add(msg.getMessage()); - log.debug("Sending message=[{}] to meeting=[{}]", msg.getMessageName(), msg.getMeetingID()); + //log.debug("Sending message=[{}] to meeting=[{}]", msg.getMessageName(), msg.getMeetingID()); ServiceUtils.invokeOnConnection(conn, "onMessageFromServer", params.toArray()); } else { log.warn("Connection not connected for userid=[{}] in meeting=[{}]", msg.getUserID(), msg.getMeetingID()); @@ -191,6 +224,18 @@ public class ConnectionInvokerService { runExec.execute(sender); } + private IConnection getConnectionWithConnId(IScope scope, String connId) { + Set<IConnection> conns = scope.getClientConnections(); + for (IConnection conn : conns) { + String connID = (String) conn.getSessionId(); + if (connID != null && connID.equals(connId)) { + return conn; + } + } + + return null; + } + private IConnection getConnection(IScope scope, String userID) { Set<IConnection> conns = scope.getClientConnections(); for (IConnection conn : conns) { diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java index d862ee9d1e..eee8bc7272 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/EventListenerImp.java @@ -27,10 +27,21 @@ public class EventListenerImp implements IEventListener { sendIsScreenSharingResponse((IsScreenSharingResponse) event); } else if (event instanceof ScreenShareClientPing) { sendScreenShareClientPing((ScreenShareClientPing) event); + } else if (event instanceof UnauthorizedBroadcastStreamEvent) { + sendUnauthorizedBroadcastStreamEvent((UnauthorizedBroadcastStreamEvent) event); } } + private void sendUnauthorizedBroadcastStreamEvent(UnauthorizedBroadcastStreamEvent event) { + if (log.isDebugEnabled()) { + log.debug("Sending CloseConnectionMessage to client, meetingId=" + event.meetingId + " streamId=" + event.streamId); + } + + CloseConnectionMessage msg = new CloseConnectionMessage(event.meetingId, event.streamId, event.connId, event.scope); + sender.sendMessage(msg); + } + private void sendScreenShareClientPing(ScreenShareClientPing event) { Map<String, Object> data = new HashMap<String, Object>(); data.put("meetingId", event.meetingId); diff --git a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java index ac7e460228..47c47a5756 100755 --- a/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java +++ b/bbb-screenshare/app/src/main/java/org/bigbluebutton/app/screenshare/red5/Red5AppAdapter.java @@ -140,6 +140,10 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { super.streamBroadcastStart(stream); log.info("streamBroadcastStart " + stream.getPublishedName() + "]"); + + String connId = conn.getSessionId(); + String scopeName = stream.getScope().getName(); + String streamId = stream.getPublishedName(); Matcher matcher = STREAM_ID_PATTERN.matcher(stream.getPublishedName()); if (matcher.matches()) { @@ -147,7 +151,11 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { String url = streamBaseUrl + "/" + meetingId + "/" + streamId; app.streamStarted(meetingId, streamId, url); - boolean recordVideoStream = app.recordStream(meetingId, streamId); + + + app.authorizeBroadcastStream(meetingId, streamId, connId, scopeName); + + boolean recordVideoStream = app.recordStream(meetingId, streamId); if (recordVideoStream) { recordStream(stream); ScreenshareStreamListener listener = new ScreenshareStreamListener(recordingService, recordingDirectory); @@ -167,6 +175,7 @@ public class Red5AppAdapter extends MultiThreadedApplicationAdapter { log.info("ScreenShare broadcast started: data={}", logStr); } else { log.error("Invalid streamid format [{}]", streamId); + conn.close(); } } diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/ScreenShareApplication.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/ScreenShareApplication.scala index e28ad314e7..f1ae74490c 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/ScreenShareApplication.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/ScreenShareApplication.scala @@ -219,6 +219,11 @@ class ScreenShareApplication(val bus: IEventsMessageBus, val jnlpFile: String, screenShareManager ! new StreamStartedMessage(meetingId, streamId, url) } + def authorizeBroadcastStream(meetingId: String, streamId: String, connId: String, scope: String): Unit = { + + screenShareManager ! new AuthorizeBroadcastStreamMessage(meetingId, streamId, connId, scope) + } + def streamStopped(meetingId: String, streamId: String) { // if (logger.isDebugEnabled()) { // logger.debug("Received stream stopped on meeting=[" + meetingId diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/Screenshare.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/Screenshare.scala index 8f73aae384..6940a8b7c8 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/Screenshare.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/Screenshare.scala @@ -90,8 +90,6 @@ class Screenshare(val sessionManager: ScreenshareManager, //private val sessions = new HashMap[String, ActiveSession] - private var activeSession:Option[ActiveSession] = None - private val START = "START" private val PAUSE = "PAUSE" private val RUNNING = "RUNNING" @@ -105,8 +103,14 @@ class Screenshare(val sessionManager: ScreenshareManager, // start-pause-stop private var streamIdCount = 0 + // A screen sharing session that has lifecyle of start, pause, resume, and stop. private var screenShareSession: Option[String] = None - private var currentPresenterId: Option[String] = None + + // A broadcast stream session withing the screen share session. + private var activeSession:Option[ActiveSession] = None + + + private var currentPresenterId: Option[String] = None private var width: Option[Int] = None private var height: Option[Int] = None @@ -168,6 +172,8 @@ class Screenshare(val sessionManager: ScreenshareManager, case msg: MeetingEnded => handleMeetingHasEnded(msg) case msg: SessionAuditMessage => handleSessionAuditMessage(msg) case msg: ClientPongMessage => handleClientPongMessage(msg) + case msg: AuthorizeBroadcastStreamMessage => handleAuthorizeBroadcastStreamMessage(msg) + case m: Any => log.warning("Session: Unknown message [{}]", m) } @@ -284,9 +290,7 @@ class Screenshare(val sessionManager: ScreenshareManager, if (as.token == msg.token) { sender ! new ScreenShareInfoRequestReply(msg.meetingId, as.streamId, sss, as.tunnel) } - } - } private def handleIsStreamRecorded(msg: IsStreamRecorded) { @@ -385,6 +389,21 @@ class Screenshare(val sessionManager: ScreenshareManager, } } + private def handleAuthorizeBroadcastStreamMessage(msg: AuthorizeBroadcastStreamMessage): Unit = { + if (log.isDebugEnabled) { + log.debug("handleAuthorizeBroadcastStreamMessage meetingId=" + msg.meetingId + + " streamId=" + msg.streamId + " connId=" + msg.connId + " scope=" + msg.scope) + } + + activeSession match { + case Some(as) => + if (as.streamId != msg.streamId) { + bus.send(new UnauthorizedBroadcastStreamEvent(msg.meetingId, msg.streamId, msg.connId, msg.scope)) + } + case None => bus.send(new UnauthorizedBroadcastStreamEvent(msg.meetingId, msg.streamId, msg.connId, msg.scope)) + } + } + private def resetScreenShareSession() = { width = None height = None diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala index 5fbe4f31a5..e0c4229b69 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/ScreenshareManager.scala @@ -18,11 +18,11 @@ */ package org.bigbluebutton.app.screenshare.server.sessions -import akka.actor.{Actor, ActorLogging, ActorSystem, Props} import org.bigbluebutton.app.screenshare.StreamInfo import org.bigbluebutton.app.screenshare.server.sessions.Session.StopSession +import akka.actor.{Actor, ActorLogging, ActorSystem, Props} import scala.collection.mutable.HashMap -import org.bigbluebutton.app.screenshare.events.{IEventsMessageBus, IsScreenSharingResponse, ScreenShareRequestTokenFailedResponse} +import org.bigbluebutton.app.screenshare.events.{IEventsMessageBus, IsScreenSharingResponse, ScreenShareRequestTokenFailedResponse, UnauthorizedBroadcastStreamEvent} import org.bigbluebutton.app.screenshare.server.sessions.messages._ object ScreenshareManager { @@ -57,6 +57,7 @@ class ScreenshareManager(val aSystem: ActorSystem, val bus: IEventsMessageBus) case msg: MeetingEnded => handleMeetingHasEnded(msg) case msg: MeetingCreated => handleMeetingCreated(msg) case msg: ClientPongMessage => handleClientPongMessage(msg) + case msg: AuthorizeBroadcastStreamMessage => handleAuthorizeBroadcastStreamMessage(msg) case msg: Any => log.warning("Unknown message " + msg) } @@ -205,6 +206,20 @@ class ScreenshareManager(val aSystem: ActorSystem, val bus: IEventsMessageBus) } } + private def handleAuthorizeBroadcastStreamMessage(msg: AuthorizeBroadcastStreamMessage): Unit = { + if (log.isDebugEnabled) { + log.debug("handleAuthorizeBroadcastStreamMessage meetingId=" + msg.meetingId + + " streamId=" + msg.streamId + " connId=" + msg.connId + " scope=" + msg.scope) + } + + screenshares.get(msg.meetingId) match { + case Some(ss) => + ss.actorRef forward msg + case None => + bus.send(new UnauthorizedBroadcastStreamEvent(msg.meetingId, msg.streamId, msg.connId, msg.scope)) + } + } + private def handleStopShareRequestMessage(msg: StopShareRequestMessage) { if (log.isDebugEnabled) { log.debug("Received stop share request message for meeting=[" + msg.meetingId + "]") diff --git a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala index 9819d80723..35025736e9 100755 --- a/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala +++ b/bbb-screenshare/app/src/main/scala/org/bigbluebutton/app/screenshare/server/sessions/messages/IMessage.scala @@ -14,6 +14,8 @@ case class StopShareRequestMessage(meetingId: String, streamId: String) case class StreamStartedMessage(meetingId: String, streamId: String, url: String) +case class AuthorizeBroadcastStreamMessage(meetingId: String, streamId: String, connId: String, scope: String) + case class StreamStoppedMessage(meetingId: String, streamId: String) case class SharingStartedMessage(meetingId: String, streamId: String, width: Int, height: Int) -- GitLab