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