OutboundUrlFilter.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. /*
  2. * Copyright (c) Contributors, http://opensimulator.org/
  3. * See CONTRIBUTORS.TXT for a full list of copyright holders.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions are met:
  7. * * Redistributions of source code must retain the above copyright
  8. * notice, this list of conditions and the following disclaimer.
  9. * * Redistributions in binary form must reproduce the above copyright
  10. * notice, this list of conditions and the following disclaimer in the
  11. * documentation and/or other materials provided with the distribution.
  12. * * Neither the name of the OpenSimulator Project nor the
  13. * names of its contributors may be used to endorse or promote products
  14. * derived from this software without specific prior written permission.
  15. *
  16. * THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
  17. * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
  18. * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
  19. * DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
  20. * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
  21. * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
  22. * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
  23. * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  24. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
  25. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  26. */
  27. using System;
  28. using System.Collections.Generic;
  29. using System.Linq;
  30. using System.Net;
  31. using System.Reflection;
  32. using log4net;
  33. using LukeSkywalker.IPNetwork;
  34. using Nini.Config;
  35. namespace OpenSim.Framework
  36. {
  37. public class OutboundUrlFilter
  38. {
  39. private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
  40. public string Name { get; private set; }
  41. private List<IPNetwork> m_blacklistNetworks;
  42. private List<IPEndPoint> m_blacklistEndPoints;
  43. private List<IPNetwork> m_blacklistExceptionNetworks;
  44. private List<IPEndPoint> m_blacklistExceptionEndPoints;
  45. public OutboundUrlFilter(
  46. string name,
  47. List<IPNetwork> blacklistNetworks, List<IPEndPoint> blacklistEndPoints,
  48. List<IPNetwork> blacklistExceptionNetworks, List<IPEndPoint> blacklistExceptionEndPoints)
  49. {
  50. Name = name;
  51. m_blacklistNetworks = blacklistNetworks;
  52. m_blacklistEndPoints = blacklistEndPoints;
  53. m_blacklistExceptionNetworks = blacklistExceptionNetworks;
  54. m_blacklistExceptionEndPoints = blacklistExceptionEndPoints;
  55. }
  56. /// <summary>
  57. /// Initializes a new instance of the <see cref="OpenSim.Framework.OutboundUrlFilter"/> class.
  58. /// </summary>
  59. /// <param name="name">Name of the filter for logging purposes.</param>
  60. /// <param name="config">Filter configuration</param>
  61. public OutboundUrlFilter(string name, IConfigSource config)
  62. {
  63. Name = name;
  64. string configBlacklist
  65. = "0.0.0.0/8|10.0.0.0/8|100.64.0.0/10|127.0.0.0/8|169.254.0.0/16|172.16.0.0/12|192.0.0.0/24|192.0.2.0/24|192.88.99.0/24|192.168.0.0/16|198.18.0.0/15|198.51.100.0/24|203.0.113.0/24|224.0.0.0/4|240.0.0.0/4|255.255.255.255/32";
  66. string configBlacklistExceptions = "";
  67. IConfig networkConfig = config.Configs["Network"];
  68. if (networkConfig != null)
  69. {
  70. configBlacklist = networkConfig.GetString("OutboundDisallowForUserScripts", configBlacklist);
  71. configBlacklistExceptions
  72. = networkConfig.GetString("OutboundDisallowForUserScriptsExcept", configBlacklistExceptions);
  73. }
  74. m_log.DebugFormat(
  75. "[OUTBOUND URL FILTER]: OutboundDisallowForUserScripts for {0} is [{1}]", Name, configBlacklist);
  76. m_log.DebugFormat(
  77. "[OUTBOUND URL FILTER]: OutboundDisallowForUserScriptsExcept for {0} is [{1}]", Name, configBlacklistExceptions);
  78. OutboundUrlFilter.ParseConfigList(
  79. configBlacklist, Name, out m_blacklistNetworks, out m_blacklistEndPoints);
  80. OutboundUrlFilter.ParseConfigList(
  81. configBlacklistExceptions, Name, out m_blacklistExceptionNetworks, out m_blacklistExceptionEndPoints);
  82. }
  83. private static void ParseConfigList(
  84. string fullConfigEntry, string filterName, out List<IPNetwork> networks, out List<IPEndPoint> endPoints)
  85. {
  86. // Parse blacklist
  87. string[] configBlacklistEntries
  88. = fullConfigEntry.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
  89. configBlacklistEntries = configBlacklistEntries.Select(e => e.Trim()).ToArray();
  90. networks = new List<IPNetwork>();
  91. endPoints = new List<IPEndPoint>();
  92. foreach (string configEntry in configBlacklistEntries)
  93. {
  94. if (configEntry.Contains("/"))
  95. {
  96. IPNetwork network;
  97. if (!IPNetwork.TryParse(configEntry, out network))
  98. {
  99. m_log.ErrorFormat(
  100. "[OUTBOUND URL FILTER]: Entry [{0}] is invalid network for {1}", configEntry, filterName);
  101. continue;
  102. }
  103. networks.Add(network);
  104. }
  105. else
  106. {
  107. Uri configEntryUri;
  108. if (!Uri.TryCreate("http://" + configEntry, UriKind.Absolute, out configEntryUri))
  109. {
  110. m_log.ErrorFormat(
  111. "[OUTBOUND URL FILTER]: EndPoint entry [{0}] is invalid endpoint for {1}",
  112. configEntry, filterName);
  113. continue;
  114. }
  115. IPAddress[] addresses = Dns.GetHostAddresses(configEntryUri.Host);
  116. foreach (IPAddress addr in addresses)
  117. {
  118. if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
  119. {
  120. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}] in config", addr);
  121. IPEndPoint configEntryEp = new IPEndPoint(addr, configEntryUri.Port);
  122. endPoints.Add(configEntryEp);
  123. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Added blacklist exception [{0}]", configEntryEp);
  124. }
  125. }
  126. }
  127. }
  128. }
  129. /// <summary>
  130. /// Determines if an url is in a list of networks and endpoints.
  131. /// </summary>
  132. /// <returns></returns>
  133. /// <param name="url">IP address</param>
  134. /// <param name="port"></param>
  135. /// <param name="networks">Networks.</param>
  136. /// <param name="endPoints">End points.</param>
  137. /// <param name="filterName">Filter name.</param>
  138. private static bool IsInNetwork(
  139. IPAddress addr, int port, List<IPNetwork> networks, List<IPEndPoint> endPoints, string filterName)
  140. {
  141. foreach (IPNetwork ipn in networks)
  142. {
  143. // m_log.DebugFormat(
  144. // "[OUTBOUND URL FILTER]: Checking [{0}] against network [{1}]", addr, ipn);
  145. if (IPNetwork.Contains(ipn, addr))
  146. {
  147. // m_log.DebugFormat(
  148. // "[OUTBOUND URL FILTER]: Found [{0}] in network [{1}]", addr, ipn);
  149. return true;
  150. }
  151. }
  152. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr);
  153. foreach (IPEndPoint ep in endPoints)
  154. {
  155. // m_log.DebugFormat(
  156. // "[OUTBOUND URL FILTER]: Checking [{0}:{1}] against endpoint [{2}]",
  157. // addr, port, ep);
  158. if (addr.Equals(ep.Address) && port == ep.Port)
  159. {
  160. // m_log.DebugFormat(
  161. // "[OUTBOUND URL FILTER]: Found [{0}:{1}] in endpoint [{2}]", addr, port, ep);
  162. return true;
  163. }
  164. }
  165. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Did not find [{0}:{1}] in list", addr, port);
  166. return false;
  167. }
  168. /// <summary>
  169. /// Checks whether the given url is allowed by the filter.
  170. /// </summary>
  171. /// <returns></returns>
  172. public bool CheckAllowed(Uri url)
  173. {
  174. bool allowed = true;
  175. // Check that we are permitted to make calls to this endpoint.
  176. bool foundIpv4Address = false;
  177. IPAddress[] addresses = null;
  178. try
  179. {
  180. addresses = Dns.GetHostAddresses(url.Host);
  181. }
  182. catch
  183. {
  184. // If there is a DNS error, we can't stop the script!
  185. return true;
  186. }
  187. foreach (IPAddress addr in addresses)
  188. {
  189. if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
  190. {
  191. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr);
  192. foundIpv4Address = true;
  193. // Check blacklist
  194. if (OutboundUrlFilter.IsInNetwork(addr, url.Port, m_blacklistNetworks, m_blacklistEndPoints, Name))
  195. {
  196. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in blacklist for {1}", url, Name);
  197. // Check blacklist exceptions
  198. allowed
  199. = OutboundUrlFilter.IsInNetwork(
  200. addr, url.Port, m_blacklistExceptionNetworks, m_blacklistExceptionEndPoints, Name);
  201. // if (allowed)
  202. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in whitelist for {1}", url, Name);
  203. }
  204. }
  205. // Found at least one address in a blacklist and not a blacklist exception
  206. if (!allowed)
  207. return false;
  208. // else
  209. // m_log.DebugFormat("[OUTBOUND URL FILTER]: URL [{0}] not in blacklist for {1}", url, Name);
  210. }
  211. // We do not know how to handle IPv6 securely yet.
  212. if (!foundIpv4Address)
  213. return false;
  214. // m_log.DebugFormat("[OUTBOUND URL FILTER]: Allowing request [{0}]", url);
  215. return allowed;
  216. }
  217. }
  218. }