package org.red5.server.plugin.security; /* * RED5 Open Source Flash Server - http://www.osflash.org/red5 * * Copyright (c) 2006-2009 by respective authors (see below). All rights reserved. * * This library is free software; you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free Software * Foundation; either version 2.1 of the License, or (at your option) any later * version. * * This library is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License along * with this library; if not, write to the Free Software Foundation, Inc., * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ import java.io.BufferedReader; import java.io.DataInputStream; import java.io.InputStreamReader; import java.util.Map; import org.apache.commons.lang3.ArrayUtils; import org.red5.logging.Red5LoggerFactory; import org.red5.server.api.IConnection; import org.red5.server.api.Red5; import org.red5.server.api.scope.IScope; import org.red5.server.api.stream.IStreamPlaybackSecurity; import org.slf4j.Logger; public class PlaybackSecurityHandler extends SecurityBase implements IStreamPlaybackSecurity { private Boolean HTMLDomainsAuth = true; private Boolean SWFDomainsAuth = true; private String[] allowedHTMLDomains; private String[] allowedSWFDomains; private String htmlDomains = "allowedHTMLdomains.txt"; private String swfDomains = "allowedSWFdomains.txt"; private static Logger log = Red5LoggerFactory.getLogger(PlaybackSecurityHandler.class, "securityTest"); @Override public void init() { if (properties.containsKey("htmlDomains")) { htmlDomains = properties.get("htmlDomains").toString(); } if (properties.containsKey("swfDomains")) { swfDomains = properties.get("swfDomains").toString(); } allowedHTMLDomains = readValidDomains(htmlDomains, "HTMLDomains"); // Populating the list of domains which are allowed to host a SWF file // which may connect to this application allowedSWFDomains = readValidDomains(swfDomains, "SWFDomains"); // Logging if (HTMLDomainsAuth) { log.debug("Authentication of HTML page URL domains is enabled"); } if (SWFDomainsAuth) { log.debug("Authentication of SWF URL domains is enabled"); } log.debug("...loading completed."); //now register with the application application.registerStreamPlaybackSecurity(this); } public boolean isPlaybackAllowed(IScope scope, String name, int start, int length, boolean flushPlaylist) { IConnection conn = Red5.getConnectionLocal(); try { Map<String, Object> connectParams = conn.getConnectParams(); String pageUrl = conn.getConnectParams().get("pageUrl").toString(); String swfUrl = conn.getConnectParams().get("swfUrl").toString(); String ip = conn.getRemoteAddress(); if ((ip != "127.0.0.1") && HTMLDomainsAuth && !this.validate(pageUrl, this.allowedHTMLDomains)) { log.debug("Authentication failed for pageurl: " + pageUrl + ", rejecting connection from " + ip); return false; } // Authenticating the SWF file's domain for the request : // Don't call validate() when the request is from localhost // or SWF Domains Authentication is off. if ((ip != "127.0.0.1") && SWFDomainsAuth && !this.validate(swfUrl, this.allowedSWFDomains)) { log.debug("Authentication failed for referrer: " + swfUrl + ", rejecting connection from " + ip); return false; } } catch (Exception e) { if (HTMLDomainsAuth || SWFDomainsAuth) return false; return true; } return true; } private Boolean validate(String url, String[] patterns) { // Convert to lower case url = url.toLowerCase(); int domainStartPos = 0; // domain start position in the URL int domainEndPos = 0; // domain end position in the URL switch (url.indexOf("://")) { case 4: if (url.indexOf("http://") == 0) domainStartPos = 7; break; case 5: if (url.indexOf("https://") == 0) domainStartPos = 8; break; } if (domainStartPos == 0) { // URL must be HTTP or HTTPS protocol based return false; } domainEndPos = url.indexOf("/", domainStartPos); if (domainEndPos > 0) { int colonPos = url.indexOf(":", domainStartPos); if ((colonPos > 0) && (domainEndPos > colonPos)) { // probably URL contains a port number domainEndPos = colonPos; // truncate the port number in the URL } } url = url.substring(domainStartPos, domainEndPos); int indexOf = ArrayUtils.indexOf(patterns, url); if (ArrayUtils.indexOf(patterns, url) != ArrayUtils.INDEX_NOT_FOUND) { return true; } return false; } private String[] readValidDomains(String fileName, String domainsType) { String[] domainsArray = new String[100]; try { DataInputStream in = new DataInputStream(application.getResource("WEB-INF/" + fileName).getInputStream()); BufferedReader br = new BufferedReader(new InputStreamReader(in)); int index = 0; String strLine = ""; while ((strLine = br.readLine()) != null) { if (strLine.equals("") || strLine.indexOf("#") == 0) { continue; } if (strLine.indexOf(" ") < 0) { index++; domainsArray[index] = strLine.toLowerCase(); log.debug(domainsArray[index]); if (strLine.trim().equals("*")) { if (domainsType.equals("HTMLDomains")) { log.debug("Found wildcard (*) entry: disabling authentication of HTML file domains "); HTMLDomainsAuth = false; } else if (domainsType.equals("SWFDomains")) { log.debug("Found wildcard (*) entry: disabling authentication of SWF file domains "); SWFDomainsAuth = false; } } } } in.close(); } catch (Exception e) { log.error("{}", e.getMessage()); e.printStackTrace(); if (domainsType.equals("HTMLDomains")) { HTMLDomainsAuth = false; } else if (domainsType.equals("HTMLDomains")) { SWFDomainsAuth = false; } } return domainsArray; } }