diff --git a/OpenSim/Data/IAuthenticationData.cs b/OpenSim/Data/IAuthenticationData.cs index f84871690c..7753e04a8c 100644 --- a/OpenSim/Data/IAuthenticationData.cs +++ b/OpenSim/Data/IAuthenticationData.cs @@ -48,5 +48,9 @@ namespace OpenSim.Data bool Store(AuthenticationData data); bool SetDataItem(UUID principalID, string item, string value); + + bool SetToken(UUID principalID, string token, int lifetime); + + bool CheckToken(UUID principalID, string token, int lifetime); } } diff --git a/OpenSim/Data/MySQL/MySQLAuthenticationData.cs b/OpenSim/Data/MySQL/MySQLAuthenticationData.cs index 19575ec8e0..afd59bd141 100644 --- a/OpenSim/Data/MySQL/MySQLAuthenticationData.cs +++ b/OpenSim/Data/MySQL/MySQLAuthenticationData.cs @@ -39,11 +39,15 @@ namespace OpenSim.Data.MySQL { private string m_Realm; private List m_ColumnNames = null; + private int m_LastExpire = 0; public MySqlAuthenticationData(string connectionString, string realm) : base(connectionString) { m_Realm = realm; + + Migration m = new Migration(m_Connection, GetType().Assembly, "AuthStore"); + m.Update(); } public AuthenticationData Get(UUID principalID) @@ -153,5 +157,56 @@ namespace OpenSim.Data.MySQL return false; } + + public bool SetToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + MySqlCommand cmd = new MySqlCommand("insert into tokens (UUID, token, validity) values (?principalID, ?token, date_add(now(), interval ?lifetime minute))"); + cmd.Parameters.AddWithValue("?principalID", principalID.ToString()); + cmd.Parameters.AddWithValue("?token", token); + cmd.Parameters.AddWithValue("?lifetime", lifetime.ToString()); + + if (ExecuteNonQuery(cmd) > 0) + { + cmd.Dispose(); + return true; + } + + cmd.Dispose(); + return false; + } + + public bool CheckToken(UUID principalID, string token, int lifetime) + { + if (System.Environment.TickCount - m_LastExpire > 30000) + DoExpire(); + + MySqlCommand cmd = new MySqlCommand("update tokens set validity = date_add(now(), interval ?lifetime minute) where UUID = ?principalID and token = ?token and validity > now()"); + cmd.Parameters.AddWithValue("?principalID", principalID.ToString()); + cmd.Parameters.AddWithValue("?token", token); + cmd.Parameters.AddWithValue("?lifetime", lifetime.ToString()); + + if (ExecuteNonQuery(cmd) > 0) + { + cmd.Dispose(); + return true; + } + + cmd.Dispose(); + + return false; + } + + private void DoExpire() + { + MySqlCommand cmd = new MySqlCommand("delete from tokens where validity < now()"); + ExecuteNonQuery(cmd); + + cmd.Dispose(); + + m_LastExpire = System.Environment.TickCount; + } } } diff --git a/OpenSim/Data/MySQL/Resources/001_AuthStore.sql b/OpenSim/Data/MySQL/Resources/001_AuthStore.sql new file mode 100644 index 0000000000..c7e16fbdfb --- /dev/null +++ b/OpenSim/Data/MySQL/Resources/001_AuthStore.sql @@ -0,0 +1,21 @@ +begin; + +CREATE TABLE `auth` ( + `UUID` char(36) NOT NULL, + `passwordHash` char(32) NOT NULL default '', + `passwordSalt` char(32) NOT NULL default '', + `webLoginKey` varchar(255) NOT NULL default '', + PRIMARY KEY (`UUID`) +) ENGINE=InnoDB; + +CREATE TABLE `tokens` ( + `UUID` char(36) NOT NULL, + `token` varchar(255) NOT NULL, + `validity` datetime NOT NULL, + UNIQUE KEY `uuid_token` (`UUID`,`token`), + KEY `UUID` (`UUID`), + KEY `token` (`token`), + KEY `validity` (`validity`) +) ENGINE=InnoDB; + +commit; diff --git a/OpenSim/Framework/LandData.cs b/OpenSim/Framework/LandData.cs index e639da0636..071a66758f 100644 --- a/OpenSim/Framework/LandData.cs +++ b/OpenSim/Framework/LandData.cs @@ -27,6 +27,9 @@ using System; using System.Collections.Generic; +using System.Xml; +using System.Xml.Serialization; + using OpenMetaverse; namespace OpenSim.Framework @@ -36,6 +39,11 @@ namespace OpenSim.Framework /// public class LandData { + // use only one serializer to give the runtime a chance to + // optimize it (it won't do that if you use a new instance + // every time) + private static XmlSerializer serializer = new XmlSerializer(typeof (LandData)); + private Vector3 _AABBMax = new Vector3(); private Vector3 _AABBMin = new Vector3(); private int _area = 0; @@ -86,6 +94,7 @@ namespace OpenSim.Framework /// /// Upper corner of the AABB for the parcel /// + [XmlIgnore] public Vector3 AABBMax { get { return _AABBMax; @@ -97,6 +106,7 @@ namespace OpenSim.Framework /// /// Lower corner of the AABB for the parcel /// + [XmlIgnore] public Vector3 AABBMin { get { return _AABBMin; @@ -205,6 +215,7 @@ namespace OpenSim.Framework /// /// Number of SceneObjectPart that are owned by a Group /// + [XmlIgnore] public int GroupPrims { get { return _groupPrims; @@ -363,6 +374,7 @@ namespace OpenSim.Framework /// Number of SceneObjectPart that are owned by users who do not own the parcel /// and don't have the 'group. These are elegable for AutoReturn collection /// + [XmlIgnore] public int OtherPrims { get { return _otherPrims; @@ -388,6 +400,7 @@ namespace OpenSim.Framework /// /// Number of SceneObjectPart that are owned by the owner of the parcel /// + [XmlIgnore] public int OwnerPrims { get { return _ownerPrims; @@ -448,6 +461,7 @@ namespace OpenSim.Framework /// /// Number of SceneObjectPart that are currently selected by avatar /// + [XmlIgnore] public int SelectedPrims { get { return _selectedPrims; @@ -460,6 +474,7 @@ namespace OpenSim.Framework /// /// Number of meters^2 in the Simulator /// + [XmlIgnore] public int SimwideArea { get { return _simwideArea; @@ -472,6 +487,7 @@ namespace OpenSim.Framework /// /// Number of SceneObjectPart in the Simulator /// + [XmlIgnore] public int SimwidePrims { get { return _simwidePrims; @@ -607,5 +623,22 @@ namespace OpenSim.Framework return landData; } + + public void ToXml(XmlWriter xmlWriter) + { + serializer.Serialize(xmlWriter, this); + } + + /// + /// Restore a LandData object from the serialized xml representation. + /// + /// + /// + public static LandData FromXml(XmlReader xmlReader) + { + LandData land = (LandData)serializer.Deserialize(xmlReader); + + return land; + } } } diff --git a/OpenSim/Server/Base/ServerUtils.cs b/OpenSim/Server/Base/ServerUtils.cs index 8d76ffe47f..0a36bbee14 100644 --- a/OpenSim/Server/Base/ServerUtils.cs +++ b/OpenSim/Server/Base/ServerUtils.cs @@ -31,6 +31,7 @@ using System.Reflection; using System.Xml; using System.Xml.Serialization; using System.Text; +using System.Collections.Generic; using log4net; using OpenSim.Framework; @@ -156,5 +157,31 @@ namespace OpenSim.Server.Base return null; } } + + public static Dictionary ParseQueryString(string query) + { + Dictionary result = new Dictionary(); + string[] terms = query.Split(new char[] {'&'}); + + if (terms.Length == 0) + return result; + + foreach (string t in terms) + { + string[] elems = t.Split(new char[] {'='}); + if (elems.Length == 0) + continue; + + string name = System.Web.HttpUtility.UrlDecode(elems[0]); + string value = String.Empty; + + if (elems.Length > 1) + value = System.Web.HttpUtility.UrlDecode(elems[1]); + + result[name] = value; + } + + return result; + } } -} \ No newline at end of file +} diff --git a/OpenSim/Server/Handlers/Authentication/AuthenticationServerConnector.cs b/OpenSim/Server/Handlers/Authentication/AuthenticationServerConnector.cs index 03a7980ccc..589dc3b424 100644 --- a/OpenSim/Server/Handlers/Authentication/AuthenticationServerConnector.cs +++ b/OpenSim/Server/Handlers/Authentication/AuthenticationServerConnector.cs @@ -54,7 +54,7 @@ namespace OpenSim.Server.Handlers.Authentication Object[] args = new Object[] { config }; m_AuthenticationService = ServerUtils.LoadPlugin(authenticationService, args); - //server.AddStreamHandler(new AuthenticationServerGetHandler(m_AuthenticationService)); + server.AddStreamHandler(new AuthenticationServerPostHandler(m_AuthenticationService)); } } } diff --git a/OpenSim/Server/Handlers/Authentication/AuthenticationServerPostHandler.cs b/OpenSim/Server/Handlers/Authentication/AuthenticationServerPostHandler.cs new file mode 100644 index 0000000000..6cf7d56ec2 --- /dev/null +++ b/OpenSim/Server/Handlers/Authentication/AuthenticationServerPostHandler.cs @@ -0,0 +1,233 @@ +/* + * 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 Nini.Config; +using log4net; +using System; +using System.Reflection; +using System.IO; +using System.Net; +using System.Text; +using System.Text.RegularExpressions; +using System.Xml; +using System.Xml.Serialization; +using System.Collections.Generic; +using OpenSim.Server.Base; +using OpenSim.Services.Interfaces; +using OpenSim.Framework; +using OpenSim.Framework.Servers.HttpServer; +using OpenMetaverse; + +namespace OpenSim.Server.Handlers.Authentication +{ + public class AuthenticationServerPostHandler : BaseStreamHandler + { + // private static readonly ILog m_log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); + + private IAuthenticationService m_AuthenticationService; + + public AuthenticationServerPostHandler(IAuthenticationService service) : + base("POST", "/auth") + { + m_AuthenticationService = service; + } + + public override byte[] Handle(string path, Stream request, + OSHttpRequest httpRequest, OSHttpResponse httpResponse) + { + string[] p = SplitParams(path); + + if (p.Length > 0) + { + switch (p[0]) + { + case "plain": + StreamReader sr = new StreamReader(request); + string body = sr.ReadToEnd(); + sr.Close(); + + return DoPlainMethods(body); + case "crypt": + byte[] buffer = new byte[request.Length]; + long length = request.Length; + if (length > 16384) + length = 16384; + request.Read(buffer, 0, (int)length); + + return DoEncryptedMethods(buffer); + } + } + return new byte[0]; + } + + private byte[] DoPlainMethods(string body) + { + Dictionary request = + ServerUtils.ParseQueryString(body); + + int lifetime = 30; + + if (request.ContainsKey("LIFETIME")) + { + lifetime = Convert.ToInt32(request["LIFETIME"]); + if (lifetime > 30) + lifetime = 30; + } + + if (!request.ContainsKey("METHOD")) + return FailureResult(); + if (!request.ContainsKey("PRINCIPAL")) + return FailureResult(); + + string method = request["METHOD"]; + + UUID principalID; + string token; + + if (!UUID.TryParse(request["PRINCIPAL"], out principalID)) + return FailureResult(); + + switch (method) + { + case "authenticate": + if (!request.ContainsKey("PASSWORD")) + return FailureResult(); + + token = m_AuthenticationService.Authenticate(principalID, request["PASSWORD"], lifetime); + + if (token != String.Empty) + return SuccessResult(token); + return FailureResult(); + case "verify": + if (!request.ContainsKey("TOKEN")) + return FailureResult(); + + if (m_AuthenticationService.Verify(principalID, request["TOKEN"], lifetime)) + return SuccessResult(); + + return FailureResult(); + case "release": + if (!request.ContainsKey("TOKEN")) + return FailureResult(); + + if (m_AuthenticationService.Release(principalID, request["TOKEN"])) + return SuccessResult(); + + return FailureResult(); + } + + return FailureResult(); + } + + private byte[] DoEncryptedMethods(byte[] ciphertext) + { + return new byte[0]; + } + + private byte[] SuccessResult() + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "Authentication", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "Result", ""); + result.AppendChild(doc.CreateTextNode("Success")); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + + private byte[] FailureResult() + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "Authentication", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "Result", ""); + result.AppendChild(doc.CreateTextNode("Failure")); + + rootElement.AppendChild(result); + + return DocToBytes(doc); + } + + private byte[] SuccessResult(string token) + { + XmlDocument doc = new XmlDocument(); + + XmlNode xmlnode = doc.CreateNode(XmlNodeType.XmlDeclaration, + "", ""); + + doc.AppendChild(xmlnode); + + XmlElement rootElement = doc.CreateElement("", "Authentication", + ""); + + doc.AppendChild(rootElement); + + XmlElement result = doc.CreateElement("", "Result", ""); + result.AppendChild(doc.CreateTextNode("Success")); + + rootElement.AppendChild(result); + + XmlElement t = doc.CreateElement("", "Token", ""); + t.AppendChild(doc.CreateTextNode(token)); + + rootElement.AppendChild(t); + + return DocToBytes(doc); + } + + private byte[] DocToBytes(XmlDocument doc) + { + MemoryStream ms = new MemoryStream(); + XmlTextWriter xw = new XmlTextWriter(ms, null); + xw.Formatting = Formatting.Indented; + doc.WriteTo(xw); + xw.Flush(); + + return ms.GetBuffer(); + } + } +} diff --git a/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs index 200268b1ec..2ed177ccb6 100644 --- a/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs +++ b/OpenSim/Services/AuthenticationService/AuthenticationServiceBase.cs @@ -53,7 +53,7 @@ namespace OpenSim.Services.AuthenticationService { string dllName = String.Empty; string connString = String.Empty; - string realm = String.Empty; + string realm = "auth"; // // Try reading the [AuthenticationService] section first, if it exists @@ -95,14 +95,34 @@ namespace OpenSim.Services.AuthenticationService return new byte[0]; } - public virtual bool Release(UUID principalID, string token) + public bool Verify(UUID principalID, string token, int lifetime) + { + return m_Database.CheckToken(principalID, token, lifetime); + } + + public bool VerifyEncrypted(byte[] cyphertext, byte[] key) { return false; } + public virtual bool Release(UUID principalID, string token) + { + return m_Database.CheckToken(principalID, token, 0); + } + public virtual bool ReleaseEncrypted(byte[] cyphertext, byte[] key) { return false; } + + protected string GetToken(UUID principalID, int lifetime) + { + UUID token = UUID.Random(); + + if (m_Database.SetToken(principalID, token.ToString(), lifetime)) + return token.ToString(); + + return String.Empty; + } } } diff --git a/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs b/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs index 83ce0d0795..7fdbbf6eea 100644 --- a/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs +++ b/OpenSim/Services/AuthenticationService/PasswordAuthenticationService.cs @@ -56,8 +56,24 @@ namespace OpenSim.Services.AuthenticationService { } - public string Authenticate(UUID principalID, string password) + public string Authenticate(UUID principalID, string password, int lifetime) { + AuthenticationData data = m_Database.Get(principalID); + + if (!data.Data.ContainsKey("passwordHash") || + !data.Data.ContainsKey("passwordSalt")) + { + return String.Empty; + } + + string hashed = Util.Md5Hash(Util.Md5Hash(password) + ":" + + data.Data["passwordSalt"].ToString()); + + if (data.Data["passwordHash"].ToString() == hashed) + { + return GetToken(principalID, lifetime); + } + return String.Empty; } @@ -65,15 +81,5 @@ namespace OpenSim.Services.AuthenticationService { return new byte[0]; } - - public bool Verify(UUID principalID, string token) - { - return false; - } - - public bool VerifyEncrypted(byte[] cyphertext, byte[] key) - { - return false; - } } } diff --git a/OpenSim/Services/AuthenticationService/WebkeyAuthenticationService.cs b/OpenSim/Services/AuthenticationService/WebkeyAuthenticationService.cs index af55df02ae..0118c91868 100644 --- a/OpenSim/Services/AuthenticationService/WebkeyAuthenticationService.cs +++ b/OpenSim/Services/AuthenticationService/WebkeyAuthenticationService.cs @@ -52,7 +52,7 @@ namespace OpenSim.Services.AuthenticationService { } - public string Authenticate(UUID principalID, string password) + public string Authenticate(UUID principalID, string password, int lifetime) { return String.Empty; } @@ -61,15 +61,5 @@ namespace OpenSim.Services.AuthenticationService { return new byte[0]; } - - public bool Verify(UUID principalID, string token) - { - return false; - } - - public bool VerifyEncrypted(byte[] cyphertext, byte[] key) - { - return false; - } } } diff --git a/OpenSim/Services/Interfaces/IAuthenticationService.cs b/OpenSim/Services/Interfaces/IAuthenticationService.cs index f042c93298..b448a1405c 100644 --- a/OpenSim/Services/Interfaces/IAuthenticationService.cs +++ b/OpenSim/Services/Interfaces/IAuthenticationService.cs @@ -70,7 +70,7 @@ namespace OpenSim.Services.Interfaces // the public key of the peer, which the connector must have // obtained using a remote GetPublicKey call. // - string Authenticate(UUID principalID, string password); + string Authenticate(UUID principalID, string password, int lifetime); byte[] AuthenticateEncrypted(byte[] cyphertext, byte[] key); ////////////////////////////////////////////////////// @@ -85,7 +85,7 @@ namespace OpenSim.Services.Interfaces // must be used to refresh. Unencrypted verification is still // performed, but doesn't refresh token lifetime. // - bool Verify(UUID principalID, string token); + bool Verify(UUID principalID, string token, int lifetime); bool VerifyEncrypted(byte[] cyphertext, byte[] key); ////////////////////////////////////////////////////// diff --git a/bin/OpenSim.Server.ini.example b/bin/OpenSim.Server.ini.example index aab056698a..545d6ce179 100644 --- a/bin/OpenSim.Server.ini.example +++ b/bin/OpenSim.Server.ini.example @@ -5,6 +5,9 @@ ; * These are the IN connectors the server uses, the in connectors ; * read this config file and load the needed OUT and database connectors ; * +; * Add "OpenSim.Server.Handlers.dll:AuthenticationServiceConnector" to +; * enable the experimental authentication service +; * [Startup] ServiceConnectors = "OpenSim.Server.Handlers.dll:AssetServiceConnector,OpenSim.Server.Handlers.dll:InventoryServiceInConnector,OpenSim.Server.Handlers.dll:FreeswitchServerConnector" @@ -45,3 +48,15 @@ ConnectionString = "Data Source=localhost;Database=grid;User ID=grid;Password=gr ; * This is the configuration for the freeswitch server in grid mode [FreeswitchService] LocalServiceModule = "OpenSim.Services.FreeswitchService.dll:FreeswitchService" + +; * This is the new style authentication service. Currently, only MySQL +; * is implemented. "Realm" is the table that is used for user lookup. +; * By setting it to "users", you can use the old style users table +; * as an authentication source. +; * +[AuthenticationService] +AuthenticationServiceModule = "OpenSim.Services.AuthenticationService.dll:PasswordAuthenticationService" +StorageProvider = "OpenSim.Data.MySQL.dll" +ConnectionString = "Data Source=localhost;Database=grid;User ID=grid;Password=grid;" +; Realm = "auth" + diff --git a/prebuild.xml b/prebuild.xml index cdffd8a099..b84fddd17b 100644 --- a/prebuild.xml +++ b/prebuild.xml @@ -1399,6 +1399,7 @@ ../../../bin/ + @@ -1427,6 +1428,7 @@ ../../../bin/ +