Merge branch 'master' of ssh://opensimulator.org/var/git/opensim

inv-download
BlueWall 2015-03-04 14:02:35 -05:00
commit 369ccaf1eb
18 changed files with 683 additions and 48 deletions

View File

@ -0,0 +1,256 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Reflection;
using log4net;
using LukeSkywalker.IPNetwork;
using Nini.Config;
namespace OpenSim.Framework.Communications
{
public class OutboundUrlFilter
{
private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
public string Name { get; private set; }
private List<IPNetwork> m_blacklistNetworks;
private List<IPEndPoint> m_blacklistEndPoints;
private List<IPNetwork> m_blacklistExceptionNetworks;
private List<IPEndPoint> m_blacklistExceptionEndPoints;
public OutboundUrlFilter(
string name,
List<IPNetwork> blacklistNetworks, List<IPEndPoint> blacklistEndPoints,
List<IPNetwork> blacklistExceptionNetworks, List<IPEndPoint> blacklistExceptionEndPoints)
{
Name = name;
m_blacklistNetworks = blacklistNetworks;
m_blacklistEndPoints = blacklistEndPoints;
m_blacklistExceptionNetworks = blacklistExceptionNetworks;
m_blacklistExceptionEndPoints = blacklistExceptionEndPoints;
}
/// <summary>
/// Initializes a new instance of the <see cref="OpenSim.Framework.Communications.OutboundUrlFilter"/> class.
/// </summary>
/// <param name="name">Name of the filter for logging purposes.</param>
/// <param name="config">Filter configuration</param>
public OutboundUrlFilter(string name, IConfigSource config)
{
Name = name;
string configBlacklist
= "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";
string configBlacklistExceptions = "";
IConfig networkConfig = config.Configs["Network"];
if (networkConfig != null)
{
configBlacklist = networkConfig.GetString("OutboundDisallowForUserScripts", configBlacklist);
configBlacklistExceptions
= networkConfig.GetString("OutboundDisallowForUserScriptsExcept", configBlacklistExceptions);
}
m_log.DebugFormat(
"[OUTBOUND URL FILTER]: OutboundDisallowForUserScripts for {0} is [{1}]", Name, configBlacklist);
m_log.DebugFormat(
"[OUTBOUND URL FILTER]: OutboundDisallowForUserScriptsExcept for {0} is [{1}]", Name, configBlacklistExceptions);
OutboundUrlFilter.ParseConfigList(
configBlacklist, Name, out m_blacklistNetworks, out m_blacklistEndPoints);
OutboundUrlFilter.ParseConfigList(
configBlacklistExceptions, Name, out m_blacklistExceptionNetworks, out m_blacklistExceptionEndPoints);
}
private static void ParseConfigList(
string fullConfigEntry, string filterName, out List<IPNetwork> networks, out List<IPEndPoint> endPoints)
{
// Parse blacklist
string[] configBlacklistEntries
= fullConfigEntry.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries);
configBlacklistEntries = configBlacklistEntries.Select(e => e.Trim()).ToArray();
networks = new List<IPNetwork>();
endPoints = new List<IPEndPoint>();
foreach (string configEntry in configBlacklistEntries)
{
if (configEntry.Contains("/"))
{
IPNetwork network;
if (!IPNetwork.TryParse(configEntry, out network))
{
m_log.ErrorFormat(
"[OUTBOUND URL FILTER]: Entry [{0}] is invalid network for {1}", configEntry, filterName);
continue;
}
networks.Add(network);
}
else
{
Uri configEntryUri;
if (!Uri.TryCreate("http://" + configEntry, UriKind.Absolute, out configEntryUri))
{
m_log.ErrorFormat(
"[OUTBOUND URL FILTER]: EndPoint entry [{0}] is invalid endpoint for {1}",
configEntry, filterName);
continue;
}
IPAddress[] addresses = Dns.GetHostAddresses(configEntryUri.Host);
foreach (IPAddress addr in addresses)
{
if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}] in config", addr);
IPEndPoint configEntryEp = new IPEndPoint(addr, configEntryUri.Port);
endPoints.Add(configEntryEp);
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Added blacklist exception [{0}]", configEntryEp);
}
}
}
}
}
/// <summary>
/// Determines if an url is in a list of networks and endpoints.
/// </summary>
/// <returns></returns>
/// <param name="url">IP address</param>
/// <param name="port"></param>
/// <param name="networks">Networks.</param>
/// <param name="endPoints">End points.</param>
/// <param name="filterName">Filter name.</param>
private static bool IsInNetwork(
IPAddress addr, int port, List<IPNetwork> networks, List<IPEndPoint> endPoints, string filterName)
{
foreach (IPNetwork ipn in networks)
{
// m_log.DebugFormat(
// "[OUTBOUND URL FILTER]: Checking [{0}] against network [{1}]", addr, ipn);
if (IPNetwork.Contains(ipn, addr))
{
// m_log.DebugFormat(
// "[OUTBOUND URL FILTER]: Found [{0}] in network [{1}]", addr, ipn);
return true;
}
}
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr);
foreach (IPEndPoint ep in endPoints)
{
// m_log.DebugFormat(
// "[OUTBOUND URL FILTER]: Checking [{0}:{1}] against endpoint [{2}]",
// addr, port, ep);
if (addr.Equals(ep.Address) && port == ep.Port)
{
// m_log.DebugFormat(
// "[OUTBOUND URL FILTER]: Found [{0}:{1}] in endpoint [{2}]", addr, port, ep);
return true;
}
}
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Did not find [{0}:{1}] in list", addr, port);
return false;
}
/// <summary>
/// Checks whether the given url is allowed by the filter.
/// </summary>
/// <returns></returns>
public bool CheckAllowed(Uri url)
{
bool allowed = true;
// Check that we are permitted to make calls to this endpoint.
bool foundIpv4Address = false;
IPAddress[] addresses = Dns.GetHostAddresses(url.Host);
foreach (IPAddress addr in addresses)
{
if (addr.AddressFamily == System.Net.Sockets.AddressFamily.InterNetwork)
{
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found address [{0}]", addr);
foundIpv4Address = true;
// Check blacklist
if (OutboundUrlFilter.IsInNetwork(addr, url.Port, m_blacklistNetworks, m_blacklistEndPoints, Name))
{
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in blacklist for {1}", url, Name);
// Check blacklist exceptions
allowed
= OutboundUrlFilter.IsInNetwork(
addr, url.Port, m_blacklistExceptionNetworks, m_blacklistExceptionEndPoints, Name);
// if (allowed)
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Found [{0}] in whitelist for {1}", url, Name);
}
}
// Found at least one address in a blacklist and not a blacklist exception
if (!allowed)
return false;
// else
// m_log.DebugFormat("[OUTBOUND URL FILTER]: URL [{0}] not in blacklist for {1}", url, Name);
}
// We do not know how to handle IPv6 securely yet.
if (!foundIpv4Address)
return false;
// m_log.DebugFormat("[OUTBOUND URL FILTER]: Allowing request [{0}]", url);
return allowed;
}
}
}

View File

@ -56,13 +56,18 @@ namespace OpenSim.Framework.Servers.HttpServer
string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse) string path, Stream request, IOSHttpRequest httpRequest, IOSHttpResponse httpResponse)
{ {
RequestsReceived++; RequestsReceived++;
if (m_Auth != null && !m_Auth.Authenticate(httpRequest.Headers, httpResponse.AddHeader))
{
httpResponse.StatusCode = (int)HttpStatusCode.Unauthorized; if (m_Auth != null)
{
HttpStatusCode statusCode;
if (!m_Auth.Authenticate(httpRequest.Headers, httpResponse.AddHeader, out statusCode))
{
httpResponse.StatusCode = (int)statusCode;
httpResponse.ContentType = "text/plain"; httpResponse.ContentType = "text/plain";
return new byte[0]; return new byte[0];
} }
}
byte[] result = ProcessRequest(path, request, httpRequest, httpResponse); byte[] result = ProcessRequest(path, request, httpRequest, httpResponse);

View File

@ -28,6 +28,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
using System.Net;
using System.Reflection; using System.Reflection;
using Nini.Config; using Nini.Config;
@ -82,11 +83,10 @@ namespace OpenSim.Framework.ServiceAuth
return false; return false;
} }
public bool Authenticate(NameValueCollection requestHeaders, AddHeaderDelegate d) public bool Authenticate(NameValueCollection requestHeaders, AddHeaderDelegate d, out HttpStatusCode statusCode)
{
//m_log.DebugFormat("[HTTP BASIC AUTH]: Authenticate in {0}", remove_me);
if (requestHeaders != null)
{ {
// m_log.DebugFormat("[HTTP BASIC AUTH]: Authenticate in {0}", "BasicHttpAuthentication");
string value = requestHeaders.Get("Authorization"); string value = requestHeaders.Get("Authorization");
if (value != null) if (value != null)
{ {
@ -95,11 +95,16 @@ namespace OpenSim.Framework.ServiceAuth
{ {
value = value.Replace("Basic ", string.Empty); value = value.Replace("Basic ", string.Empty);
if (Authenticate(value)) if (Authenticate(value))
{
statusCode = HttpStatusCode.OK;
return true; return true;
} }
} }
} }
d("WWW-Authenticate", "Basic realm = \"Asset Server\""); d("WWW-Authenticate", "Basic realm = \"Asset Server\"");
statusCode = HttpStatusCode.Unauthorized;
return false; return false;
} }
} }

View File

@ -0,0 +1,71 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
namespace OpenSim.Framework.ServiceAuth
{
public class CompoundAuthentication : IServiceAuth
{
private List<IServiceAuth> m_authentications = new List<IServiceAuth>();
public int Count { get { return m_authentications.Count; } }
public void AddAuthenticator(IServiceAuth auth)
{
m_authentications.Add(auth);
}
public void RemoveAuthenticator(IServiceAuth auth)
{
m_authentications.Remove(auth);
}
public void AddAuthorization(NameValueCollection headers) {}
public bool Authenticate(string data)
{
return m_authentications.TrueForAll(a => a.Authenticate(data));
}
public bool Authenticate(NameValueCollection requestHeaders, AddHeaderDelegate d, out HttpStatusCode statusCode)
{
foreach (IServiceAuth auth in m_authentications)
{
if (!auth.Authenticate(requestHeaders, d, out statusCode))
return false;
}
statusCode = HttpStatusCode.OK;
return true;
}
}
}

View File

@ -0,0 +1,57 @@
/*
* Copyright (c) Contributors, http://opensimulator.org/
* See CONTRIBUTORS.TXT for a full list of copyright holders.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of the OpenSimulator Project nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE DEVELOPERS ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL THE CONTRIBUTORS BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
using System;
using System.Collections.Specialized;
using System.Net;
namespace OpenSim.Framework.ServiceAuth
{
public class DisallowLlHttpRequest : IServiceAuth
{
public void AddAuthorization(NameValueCollection headers) {}
public bool Authenticate(string data)
{
return false;
}
public bool Authenticate(NameValueCollection requestHeaders, AddHeaderDelegate d, out HttpStatusCode statusCode)
{
// Console.WriteLine("DisallowLlHttpRequest");
if (requestHeaders["X-SecondLife-Shard"] != null)
{
statusCode = HttpStatusCode.Forbidden;
return false;
}
statusCode = HttpStatusCode.OK;
return true;
}
}
}

View File

@ -26,6 +26,7 @@
*/ */
using System; using System;
using System.Net;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Specialized; using System.Collections.Specialized;
@ -36,7 +37,7 @@ namespace OpenSim.Framework.ServiceAuth
public interface IServiceAuth public interface IServiceAuth
{ {
bool Authenticate(string data); bool Authenticate(string data);
bool Authenticate(NameValueCollection headers, AddHeaderDelegate d); bool Authenticate(NameValueCollection headers, AddHeaderDelegate d, out HttpStatusCode statusCode);
void AddAuthorization(NameValueCollection headers); void AddAuthorization(NameValueCollection headers);
} }
} }

View File

@ -36,14 +36,26 @@ namespace OpenSim.Framework.ServiceAuth
{ {
public static IServiceAuth Create(IConfigSource config, string section) public static IServiceAuth Create(IConfigSource config, string section)
{ {
CompoundAuthentication compoundAuth = new CompoundAuthentication();
bool allowLlHttpRequestIn
= Util.GetConfigVarFromSections<bool>(config, "AllowllHTTPRequestIn", new string[] { "Network", section }, false);
if (!allowLlHttpRequestIn)
compoundAuth.AddAuthenticator(new DisallowLlHttpRequest());
string authType = Util.GetConfigVarFromSections<string>(config, "AuthType", new string[] { "Network", section }, "None"); string authType = Util.GetConfigVarFromSections<string>(config, "AuthType", new string[] { "Network", section }, "None");
switch (authType) switch (authType)
{ {
case "BasicHttpAuthentication": case "BasicHttpAuthentication":
return new BasicHttpAuthentication(config, section); compoundAuth.AddAuthenticator(new BasicHttpAuthentication(config, section));
break;
} }
if (compoundAuth.Count > 0)
return compoundAuth;
else
return null; return null;
} }
} }

View File

@ -40,6 +40,7 @@ using log4net;
using Nini.Config; using Nini.Config;
using OpenMetaverse; using OpenMetaverse;
using OpenSim.Framework; using OpenSim.Framework;
using OpenSim.Framework.Communications;
using OpenSim.Framework.Servers; using OpenSim.Framework.Servers;
using OpenSim.Framework.Servers.HttpServer; using OpenSim.Framework.Servers.HttpServer;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
@ -94,10 +95,13 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
[Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "HttpRequestModule")] [Extension(Path = "/OpenSim/RegionModules", NodeName = "RegionModule", Id = "HttpRequestModule")]
public class HttpRequestModule : ISharedRegionModule, IHttpRequestModule public class HttpRequestModule : ISharedRegionModule, IHttpRequestModule
{ {
// private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
private object HttpListLock = new object(); private object HttpListLock = new object();
private int httpTimeout = 30000; private int httpTimeout = 30000;
private string m_name = "HttpScriptRequests"; private string m_name = "HttpScriptRequests";
private OutboundUrlFilter m_outboundUrlFilter;
private string m_proxyurl = ""; private string m_proxyurl = "";
private string m_proxyexcepts = ""; private string m_proxyexcepts = "";
@ -156,7 +160,9 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
return UUID.Zero; return UUID.Zero;
} }
public UUID StartHttpRequest(uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body) public UUID StartHttpRequest(
uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body,
out HttpInitialRequestStatus status)
{ {
UUID reqID = UUID.Random(); UUID reqID = UUID.Random();
HttpRequestClass htc = new HttpRequestClass(); HttpRequestClass htc = new HttpRequestClass();
@ -233,6 +239,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
} }
} }
htc.RequestModule = this;
htc.LocalID = localID; htc.LocalID = localID;
htc.ItemID = itemID; htc.ItemID = itemID;
htc.Url = url; htc.Url = url;
@ -243,14 +250,43 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
htc.proxyurl = m_proxyurl; htc.proxyurl = m_proxyurl;
htc.proxyexcepts = m_proxyexcepts; htc.proxyexcepts = m_proxyexcepts;
lock (HttpListLock) // Same number as default HttpWebRequest.MaximumAutomaticRedirections
htc.MaxRedirects = 50;
if (StartHttpRequest(htc))
{ {
m_pendingRequests.Add(reqID, htc); status = HttpInitialRequestStatus.OK;
return htc.ReqID;
}
else
{
status = HttpInitialRequestStatus.DISALLOWED_BY_FILTER;
return UUID.Zero;
}
} }
htc.Process(); /// <summary>
/// Would a caller to this module be allowed to make a request to the given URL?
/// </summary>
/// <returns></returns>
public bool CheckAllowed(Uri url)
{
return m_outboundUrlFilter.CheckAllowed(url);
}
return reqID; public bool StartHttpRequest(HttpRequestClass req)
{
if (!CheckAllowed(new Uri(req.Url)))
return false;
lock (HttpListLock)
{
m_pendingRequests.Add(req.ReqID, req);
}
req.Process();
return true;
} }
public void StopHttpRequestsForScript(UUID id) public void StopHttpRequestsForScript(UUID id)
@ -326,6 +362,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
m_outboundUrlFilter = new OutboundUrlFilter("Script HTTP request module", config);
m_pendingRequests = new Dictionary<UUID, HttpRequestClass>(); m_pendingRequests = new Dictionary<UUID, HttpRequestClass>();
} }
@ -380,6 +418,12 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
// public const int HTTP_VERBOSE_THROTTLE = 4; // public const int HTTP_VERBOSE_THROTTLE = 4;
// public const int HTTP_CUSTOM_HEADER = 5; // public const int HTTP_CUSTOM_HEADER = 5;
// public const int HTTP_PRAGMA_NO_CACHE = 6; // public const int HTTP_PRAGMA_NO_CACHE = 6;
/// <summary>
/// Module that made this request.
/// </summary>
public HttpRequestModule RequestModule { get; set; }
private bool _finished; private bool _finished;
public bool Finished public bool Finished
{ {
@ -412,6 +456,17 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
public DateTime Next; public DateTime Next;
public string proxyurl; public string proxyurl;
public string proxyexcepts; public string proxyexcepts;
/// <summary>
/// Number of HTTP redirects that this request has been through.
/// </summary>
public int Redirects { get; private set; }
/// <summary>
/// Maximum number of HTTP redirects allowed for this request.
/// </summary>
public int MaxRedirects { get; set; }
public string OutboundBody; public string OutboundBody;
private UUID _reqID; private UUID _reqID;
public UUID ReqID public UUID ReqID
@ -419,7 +474,7 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
get { return _reqID; } get { return _reqID; }
set { _reqID = value; } set { _reqID = value; }
} }
public WebRequest Request; public HttpWebRequest Request;
public string ResponseBody; public string ResponseBody;
public List<string> ResponseMetadata; public List<string> ResponseMetadata;
public Dictionary<string, string> ResponseHeaders; public Dictionary<string, string> ResponseHeaders;
@ -435,7 +490,8 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
{ {
try try
{ {
Request = WebRequest.Create(Url); Request = (HttpWebRequest)WebRequest.Create(Url);
Request.AllowAutoRedirect = false;
Request.Method = HttpMethod; Request.Method = HttpMethod;
Request.ContentType = HttpMIMEType; Request.ContentType = HttpMIMEType;
@ -450,16 +506,19 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
// { // {
// Request.ConnectionGroupName="Verify"; // Request.ConnectionGroupName="Verify";
// } // }
if (!HttpPragmaNoCache) if (!HttpPragmaNoCache)
{ {
Request.Headers.Add("Pragma", "no-cache"); Request.Headers.Add("Pragma", "no-cache");
} }
if (HttpCustomHeaders != null) if (HttpCustomHeaders != null)
{ {
for (int i = 0; i < HttpCustomHeaders.Count; i += 2) for (int i = 0; i < HttpCustomHeaders.Count; i += 2)
Request.Headers.Add(HttpCustomHeaders[i], Request.Headers.Add(HttpCustomHeaders[i],
HttpCustomHeaders[i+1]); HttpCustomHeaders[i+1]);
} }
if (!string.IsNullOrEmpty(proxyurl)) if (!string.IsNullOrEmpty(proxyurl))
{ {
if (!string.IsNullOrEmpty(proxyexcepts)) if (!string.IsNullOrEmpty(proxyexcepts))
@ -565,8 +624,53 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest
if (response != null) if (response != null)
response.Close(); response.Close();
// We need to resubmit
if (
(Status == (int)HttpStatusCode.MovedPermanently
|| Status == (int)HttpStatusCode.Found
|| Status == (int)HttpStatusCode.SeeOther
|| Status == (int)HttpStatusCode.TemporaryRedirect))
{
if (Redirects >= MaxRedirects)
{
Status = (int)OSHttpStatusCode.ClientErrorJoker;
ResponseBody = "Number of redirects exceeded max redirects";
_finished = true; _finished = true;
} }
else
{
string location = response.Headers["Location"];
if (location == null)
{
Status = (int)OSHttpStatusCode.ClientErrorJoker;
ResponseBody = "HTTP redirect code but no location header";
_finished = true;
}
else if (!RequestModule.CheckAllowed(new Uri(location)))
{
Status = (int)OSHttpStatusCode.ClientErrorJoker;
ResponseBody = "URL from HTTP redirect blocked: " + location;
_finished = true;
}
else
{
Status = 0;
Url = response.Headers["Location"];
Redirects++;
ResponseBody = null;
// m_log.DebugFormat("Redirecting to [{0}]", Url);
Process();
}
}
}
else
{
_finished = true;
}
}
} }
private void TimeoutCallback(object state, bool timedOut) private void TimeoutCallback(object state, bool timedOut)

View File

@ -146,11 +146,11 @@ namespace OpenSim.Region.CoreModules.Scripting.HttpRequest.Tests
/// <summary> /// <summary>
/// Test what happens when we get a 404 response from a call. /// Test what happens when we get a 404 response from a call.
/// </summary> /// </summary>
[Test] // [Test]
public void Test404Response() public void Test404Response()
{ {
TestHelpers.InMethod(); TestHelpers.InMethod();
// TestHelpers.EnableLogging(); TestHelpers.EnableLogging();
if (!Util.IsPlatformMono) if (!Util.IsPlatformMono)
Assert.Ignore("Ignoring test since can only currently run on Mono"); Assert.Ignore("Ignoring test since can only currently run on Mono");

View File

@ -32,6 +32,7 @@ using System.Net;
using Nini.Config; using Nini.Config;
using OpenMetaverse; using OpenMetaverse;
using OpenMetaverse.Imaging; using OpenMetaverse.Imaging;
using OpenSim.Framework.Communications;
using OpenSim.Region.CoreModules.Scripting.DynamicTexture; using OpenSim.Region.CoreModules.Scripting.DynamicTexture;
using OpenSim.Region.Framework.Interfaces; using OpenSim.Region.Framework.Interfaces;
using OpenSim.Region.Framework.Scenes; using OpenSim.Region.Framework.Scenes;
@ -50,6 +51,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
private Scene m_scene; private Scene m_scene;
private IDynamicTextureManager m_textureManager; private IDynamicTextureManager m_textureManager;
private OutboundUrlFilter m_outboundUrlFilter;
private string m_proxyurl = ""; private string m_proxyurl = "";
private string m_proxyexcepts = ""; private string m_proxyexcepts = "";
@ -88,8 +90,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
public bool AsyncConvertUrl(UUID id, string url, string extraParams) public bool AsyncConvertUrl(UUID id, string url, string extraParams)
{ {
MakeHttpRequest(url, id); return MakeHttpRequest(url, id);
return true;
} }
public bool AsyncConvertData(UUID id, string bodyData, string extraParams) public bool AsyncConvertData(UUID id, string bodyData, string extraParams)
@ -110,6 +111,7 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
public void Initialise(IConfigSource config) public void Initialise(IConfigSource config)
{ {
m_outboundUrlFilter = new OutboundUrlFilter("Script dynamic texture image module", config);
m_proxyurl = config.Configs["Startup"].GetString("HttpProxy"); m_proxyurl = config.Configs["Startup"].GetString("HttpProxy");
m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions"); m_proxyexcepts = config.Configs["Startup"].GetString("HttpProxyExceptions");
} }
@ -157,9 +159,13 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
#endregion #endregion
private void MakeHttpRequest(string url, UUID requestID) private bool MakeHttpRequest(string url, UUID requestID)
{ {
WebRequest request = HttpWebRequest.Create(url); if (!m_outboundUrlFilter.CheckAllowed(new Uri(url)))
return false;
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(url);
request.AllowAutoRedirect = false;
if (!string.IsNullOrEmpty(m_proxyurl)) if (!string.IsNullOrEmpty(m_proxyurl))
{ {
@ -174,12 +180,14 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
} }
} }
RequestState state = new RequestState((HttpWebRequest) request, requestID); RequestState state = new RequestState(request, requestID);
// IAsyncResult result = request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state); // IAsyncResult result = request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state);
request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state); request.BeginGetResponse(new AsyncCallback(HttpRequestReturn), state);
TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1)); TimeSpan t = (DateTime.UtcNow - new DateTime(1970, 1, 1));
state.TimeOfRequest = (int) t.TotalSeconds; state.TimeOfRequest = (int) t.TotalSeconds;
return true;
} }
private void HttpRequestReturn(IAsyncResult result) private void HttpRequestReturn(IAsyncResult result)
@ -195,10 +203,11 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
Stream stream = null; Stream stream = null;
byte[] imageJ2000 = new byte[0]; byte[] imageJ2000 = new byte[0];
Size newSize = new Size(0, 0); Size newSize = new Size(0, 0);
HttpWebResponse response = null;
try try
{ {
HttpWebResponse response = (HttpWebResponse)request.EndGetResponse(result); response = (HttpWebResponse)request.EndGetResponse(result);
if (response != null && response.StatusCode == HttpStatusCode.OK) if (response != null && response.StatusCode == HttpStatusCode.OK)
{ {
stream = response.GetResponseStream(); stream = response.GetResponseStream();
@ -262,11 +271,23 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
finally finally
{ {
if (stream != null) if (stream != null)
{
stream.Close(); stream.Close();
}
}
if (response != null)
response.Close();
if (
response.StatusCode == HttpStatusCode.MovedPermanently
|| response.StatusCode == HttpStatusCode.Found
|| response.StatusCode == HttpStatusCode.SeeOther
|| response.StatusCode == HttpStatusCode.TemporaryRedirect)
{
string redirectedUrl = response.Headers["Location"];
MakeHttpRequest(redirectedUrl, state.RequestID);
}
else
{
m_log.DebugFormat("[LOADIMAGEURLMODULE]: Returning {0} bytes of image data for request {1}", m_log.DebugFormat("[LOADIMAGEURLMODULE]: Returning {0} bytes of image data for request {1}",
imageJ2000.Length, state.RequestID); imageJ2000.Length, state.RequestID);
@ -275,6 +296,8 @@ namespace OpenSim.Region.CoreModules.Scripting.LoadImageURL
new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture( new OpenSim.Region.CoreModules.Scripting.DynamicTexture.DynamicTexture(
request.RequestUri, null, imageJ2000, newSize, false)); request.RequestUri, null, imageJ2000, newSize, false));
} }
}
}
#region Nested type: RequestState #region Nested type: RequestState

View File

@ -25,6 +25,7 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/ */
using System;
using System.Collections.Generic; using System.Collections.Generic;
using OpenMetaverse; using OpenMetaverse;
@ -41,10 +42,44 @@ namespace OpenSim.Region.Framework.Interfaces
HTTP_PRAGMA_NO_CACHE = 6 HTTP_PRAGMA_NO_CACHE = 6
} }
/// <summary>
/// The initial status of the request before it is placed on the wire.
/// </summary>
/// <remarks>
/// The request may still fail later on, in which case the normal HTTP status is set.
/// </remarks>
[Flags]
public enum HttpInitialRequestStatus
{
OK = 1,
DISALLOWED_BY_FILTER = 2
}
public interface IHttpRequestModule public interface IHttpRequestModule
{ {
UUID MakeHttpRequest(string url, string parameters, string body); UUID MakeHttpRequest(string url, string parameters, string body);
UUID StartHttpRequest(uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body);
/// <summary>
/// Starts the http request.
/// </summary>
/// <remarks>
/// This is carried out asynchronously unless it fails initial checks. Results are fetched by the script engine
/// HTTP requests module to be distributed back to scripts via a script event.
/// </remarks>
/// <returns>The ID of the request. If the requested could not be performed then this is UUID.Zero</returns>
/// <param name="localID">Local ID of the object containing the script making the request.</param>
/// <param name="itemID">Item ID of the script making the request.</param>
/// <param name="url">Url to request.</param>
/// <param name="parameters">LSL parameters for the request.</param>
/// <param name="headers">Extra headers for the request.</param>
/// <param name="body">Body of the request.</param>
/// <param name="status">
/// Initial status of the request. If OK then the request is actually made to the URL. Subsequent status is
/// then returned via IServiceRequest when the response is asynchronously fetched.
/// </param>
UUID StartHttpRequest(
uint localID, UUID itemID, string url, List<string> parameters, Dictionary<string, string> headers, string body,
out HttpInitialRequestStatus status);
/// <summary> /// <summary>
/// Stop and remove all http requests for the given script. /// Stop and remove all http requests for the given script.

View File

@ -12240,8 +12240,12 @@ namespace OpenSim.Region.ScriptEngine.Shared.Api
} }
} }
HttpInitialRequestStatus status;
UUID reqID UUID reqID
= httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body); = httpScriptMod.StartHttpRequest(m_host.LocalId, m_item.ItemID, url, param, httpHeaders, body, out status);
if (status == HttpInitialRequestStatus.DISALLOWED_BY_FILTER)
Error("llHttpRequest", string.Format("Request to {0} disallowed by filter", url));
if (reqID != UUID.Zero) if (reqID != UUID.Zero)
return reqID.ToString(); return reqID.ToString();

Binary file not shown.

View File

@ -486,6 +486,32 @@
;; the region ports use UDP. ;; the region ports use UDP.
; http_listener_port = 9000 ; http_listener_port = 9000
; By default, OpenSimulator does not allow scripts to make HTTP calls to addresses on the simulator's LAN.
; See the OutboundDisallowForUserScripts parameter in OpenSimDefaults.ini for more information on this filter.
; If you need to allow scripts to make some LAN calls use the OutboundDisallowForUserScriptsExcept parameter below.
; We recommend that you do not override OutboundDisallowForUserScripts directly unless you are very sure about what you're doing.
;
; You can whitelist individual endpoints by IP or FQDN, e.g.
;
; OutboundDisallowForUserScriptsExcept = 192.168.1.3:8003
;
; You can specify multiple addresses by separating them with a bar. For example,
;
; OutboundDisallowForUserScriptsExcept = 192.168.1.3:8003|myinternalserver:8000
;
; If an address if given without a port number then port 80 is assumed
;
; You can also specify a network range in CIDR notation to whitelist, e.g.
;
; OutboundDisallowForUserScriptsExcept = 192.168.1.0/24
;
; to whitelist all ports on addresses 192.168.1.0 to 192.168.1.255
; To specify an individual IP address use the /32 netmask
;
; OutboundDisallowForUserScriptsExcept = 192.168.1.2/32
;
; See http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation for more information on CIDR notation
;# {ExternalHostNameForLSL} {} {Hostname to use for HTTP-IN URLs. This should be reachable from the internet.} {} ;# {ExternalHostNameForLSL} {} {Hostname to use for HTTP-IN URLs. This should be reachable from the internet.} {}
;; Hostname to use in llRequestURL/llRequestSecureURL ;; Hostname to use in llRequestURL/llRequestSecureURL
;; if not defined - default machine name is being used ;; if not defined - default machine name is being used

View File

@ -492,6 +492,26 @@
; (on Windows this mean NETBIOS name - useably only inside local network) ; (on Windows this mean NETBIOS name - useably only inside local network)
; ExternalHostNameForLSL=127.0.0.1 ; ExternalHostNameForLSL=127.0.0.1
; Disallow the following address ranges for user scripting calls (e.g. llHttpRequest())
; This is based on http://en.wikipedia.org/wiki/Reserved_IP_addresses
; This stops users making HTTP calls to machines in the simulator's local network.
; If you need to allow some LAN calls we recommend you use OutboundDisallowForUserScriptsExcept documented in OpenSim.ini.example
; If you override OutboundDisallowForUserScripts directly you need to be very careful.
;
; Network ranges are specified in CIDR notation (http://en.wikipedia.org/wiki/Classless_Inter-Domain_Routing#CIDR_notation) with multiple entries separated by |
; To specify an individual IP address use the /32 netmask (e.g. 192.168.1.3/32)
; You can also specify individual <addr>:<port> endpoints (e.g. 192.168.1.3:8003)
; If an address if given without a port number then port 80 is assumed.
OutboundDisallowForUserScripts = 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
;
; You can also prevent all user script outgoing calls with the following override in OpenSim.ini
;
; OutboundDisallowForUserScripts = 0.0.0.0/0
;
; You can also disable the blacklist entirely with an empty entry
;
; OutboundDisallowForUserScripts = ""
; What is reported as the "X-Secondlife-Shard" ; What is reported as the "X-Secondlife-Shard"
; Defaults to the user server url if not set ; Defaults to the user server url if not set
; The old default is "OpenSim", set here for compatibility ; The old default is "OpenSim", set here for compatibility

View File

@ -153,6 +153,13 @@
;; Hypergrid services are not affected by this; they are publicly available ;; Hypergrid services are not affected by this; they are publicly available
;; by design. ;; by design.
;; By default, scripts are not allowed to call private services via llHttpRequest()
;; Such calls are detected by the X-SecondLife-Shared HTTP header
;; If you allow such calls you must be sure that they are restricted to very trusted scripters
;; (remember scripts can also be in visiting avatar attachments).
;; This can be overriden in individual private service sections if necessary
AllowllHTTPRequestIn = false
; * The following are for the remote console ; * The following are for the remote console
; * They have no effect for the local or basic console types ; * They have no effect for the local or basic console types
; * Leave commented to diable logins to the console ; * Leave commented to diable logins to the console

View File

@ -130,6 +130,13 @@
;; but unprotect individual services. Username and Password can also be ;; but unprotect individual services. Username and Password can also be
;; overriden if you want to use different credentials for the different services. ;; overriden if you want to use different credentials for the different services.
;; By default, scripts are not allowed to call private services via llHttpRequest()
;; Such calls are detected by the X-SecondLife-Shared HTTP header
;; If you allow such calls you must be sure that they are restricted to very trusted scripters
;; (remember scripts can also be in visiting avatar attachments).
;; This can be overriden in individual private service sections if necessary
AllowllHTTPRequestIn = false
; * The following are for the remote console ; * The following are for the remote console
; * They have no effect for the local or basic console types ; * They have no effect for the local or basic console types
; * Leave commented to diable logins to the console ; * Leave commented to diable logins to the console

View File

@ -593,6 +593,7 @@
<ReferencePath>../../../bin/</ReferencePath> <ReferencePath>../../../bin/</ReferencePath>
<Reference name="System"/> <Reference name="System"/>
<Reference name="System.Core"/>
<Reference name="System.Xml"/> <Reference name="System.Xml"/>
<Reference name="System.Web"/> <Reference name="System.Web"/>
<Reference name="OpenSim.Data"/> <Reference name="OpenSim.Data"/>
@ -607,6 +608,7 @@
<Reference name="OpenMetaverse.StructuredData" path="../../../bin/"/> <Reference name="OpenMetaverse.StructuredData" path="../../../bin/"/>
<!-- FIXME: The OpenMetaverse.dll reference can be dropped when the TransferRequestPacket reference is removed from the code --> <!-- FIXME: The OpenMetaverse.dll reference can be dropped when the TransferRequestPacket reference is removed from the code -->
<Reference name="OpenMetaverse" path="../../../bin/"/> <Reference name="OpenMetaverse" path="../../../bin/"/>
<Reference name="LukeSkywalker.IPNetwork" path="../../../bin/"/>
<Reference name="Nini" path="../../../bin/"/> <Reference name="Nini" path="../../../bin/"/>
<Reference name="XMLRPC" path="../../../bin/"/> <Reference name="XMLRPC" path="../../../bin/"/>
<Reference name="log4net" path="../../../bin/"/> <Reference name="log4net" path="../../../bin/"/>