From dce04df4f229cbf5636a096c834202dec7cd1765 Mon Sep 17 00:00:00 2001 From: Melanie Date: Sat, 29 Aug 2009 17:37:41 +0100 Subject: Redesign the IAuthenticationService interface to use PKI. Sessioning is now in the domain of the presence module where it belongs. --- .../AuthenticationService/AuthenticationService.cs | 312 --------------------- .../Services/Interfaces/IAuthenticationService.cs | 108 ++++--- 2 files changed, 70 insertions(+), 350 deletions(-) delete mode 100644 OpenSim/Services/AuthenticationService/AuthenticationService.cs (limited to 'OpenSim') diff --git a/OpenSim/Services/AuthenticationService/AuthenticationService.cs b/OpenSim/Services/AuthenticationService/AuthenticationService.cs deleted file mode 100644 index b81a004..0000000 --- a/OpenSim/Services/AuthenticationService/AuthenticationService.cs +++ /dev/null @@ -1,312 +0,0 @@ -/* - * 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.Reflection; -using Nini.Config; -using log4net; -using OpenSim.Framework; -using OpenSim.Data; -using OpenSim.Services.Base; -using OpenSim.Services.Interfaces; -using OpenMetaverse; - -namespace OpenSim.Services.AuthenticationService -{ - /// - /// Simple authentication service implementation dealing only with users. - /// It uses the user DB directly to access user information. - /// It takes two config vars: - /// - Authenticate = {true|false} : to do or not to do authentication - /// - Authority = string like "osgrid.org" : this identity authority - /// that will be called back for identity verification - /// - public class HGAuthenticationService : ServiceBase, IAuthenticationService - { - private static readonly ILog m_log - = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType); - - protected IUserDataPlugin m_Database; - protected string m_AuthorityURL; - protected bool m_PerformAuthentication; - protected Dictionary> m_UserKeys = new Dictionary>(); - - - public HGAuthenticationService(IConfigSource config) : base(config) - { - string dllName = String.Empty; - string connString = String.Empty; - - // - // Try reading the [DatabaseService] section first, if it exists - // - IConfig dbConfig = config.Configs["DatabaseService"]; - if (dbConfig != null) - { - dllName = dbConfig.GetString("StorageProvider", String.Empty); - connString = dbConfig.GetString("ConnectionString", String.Empty); - } - - // - // Try reading the more specific [InventoryService] section, if it exists - // - IConfig authConfig = config.Configs["AuthenticationService"]; - if (authConfig != null) - { - dllName = authConfig.GetString("StorageProvider", dllName); - connString = authConfig.GetString("ConnectionString", connString); - - m_PerformAuthentication = authConfig.GetBoolean("Authenticate", true); - m_AuthorityURL = "http://" + authConfig.GetString("Authority", "localhost"); - if (!m_AuthorityURL.EndsWith("/")) - m_AuthorityURL += "/"; - } - - // - // We tried, but this doesn't exist. We can't proceed. - // - if (dllName.Equals(String.Empty)) - throw new Exception("No InventoryService configuration"); - - m_Database = LoadPlugin(dllName); - if (m_Database == null) - throw new Exception("Could not find a storage interface in the given module"); - - m_Database.Initialise(connString); - } - - public UUID AuthenticateKey(UUID principalID, string key) - { - bool writeAgentData = false; - - UserAgentData agent = m_Database.GetAgentByUUID(principalID); - if (agent == null) - { - agent = new UserAgentData(); - agent.ProfileID = principalID; - agent.SessionID = UUID.Random(); - agent.SecureSessionID = UUID.Random(); - agent.AgentIP = "127.0.0.1"; - agent.AgentPort = 0; - agent.AgentOnline = false; - - writeAgentData = true; - } - - if (!m_PerformAuthentication) - { - if (writeAgentData) - m_Database.AddNewUserAgent(agent); - return agent.SessionID; - } - - if (!VerifyKey(principalID, key)) - return UUID.Zero; - - if (writeAgentData) - m_Database.AddNewUserAgent(agent); - - return agent.SessionID; - } - - /// - /// This implementation only authenticates users. - /// - /// - /// - /// - public UUID AuthenticatePassword(UUID principalID, string password) - { - bool writeAgentData = false; - - UserAgentData agent = m_Database.GetAgentByUUID(principalID); - if (agent == null) - { - agent = new UserAgentData(); - agent.ProfileID = principalID; - agent.SessionID = UUID.Random(); - agent.SecureSessionID = UUID.Random(); - agent.AgentIP = "127.0.0.1"; - agent.AgentPort = 0; - agent.AgentOnline = false; - - writeAgentData = true; - } - - if (!m_PerformAuthentication) - { - if (writeAgentData) - m_Database.AddNewUserAgent(agent); - return agent.SessionID; - } - - UserProfileData profile = m_Database.GetUserByUUID(principalID); - bool passwordSuccess = false; - m_log.InfoFormat("[AUTH]: Authenticating {0} {1} ({2})", profile.FirstName, profile.SurName, profile.ID); - - // we do this to get our hash in a form that the server password code can consume - // when the web-login-form submits the password in the clear (supposed to be over SSL!) - if (!password.StartsWith("$1$")) - password = "$1$" + Util.Md5Hash(password); - - password = password.Remove(0, 3); //remove $1$ - - string s = Util.Md5Hash(password + ":" + profile.PasswordSalt); - // Testing... - //m_log.Info("[LOGIN]: SubHash:" + s + " userprofile:" + profile.passwordHash); - //m_log.Info("[LOGIN]: userprofile:" + profile.passwordHash + " SubCT:" + password); - - passwordSuccess = (profile.PasswordHash.Equals(s.ToString(), StringComparison.InvariantCultureIgnoreCase) - || profile.PasswordHash.Equals(password, StringComparison.InvariantCulture)); - - if (!passwordSuccess) - return UUID.Zero; - - if (writeAgentData) - m_Database.AddNewUserAgent(agent); - - return agent.SessionID; - } - - /// - /// This generates authorization keys in the form - /// http://authority/uuid - /// after verifying that the caller is, indeed, authorized to request a key - /// - /// The principal ID requesting the new key - /// The original authorization token for that principal, obtained during login - /// - public string GetKey(UUID principalID, string authToken) - { - UserProfileData profile = m_Database.GetUserByUUID(principalID); - string newKey = string.Empty; - - if (profile != null) - { - m_log.DebugFormat("[AUTH]: stored auth token is {0}. Given token is {1}", profile.WebLoginKey.ToString(), authToken); - // I'm overloading webloginkey for this, so that no changes are needed in the DB - // The uses of webloginkey are fairly mutually exclusive - if (profile.WebLoginKey.ToString().Equals(authToken)) - { - newKey = UUID.Random().ToString(); - List keys; - lock (m_UserKeys) - { - if (m_UserKeys.ContainsKey(principalID)) - { - keys = m_UserKeys[principalID]; - } - else - { - keys = new List(); - m_UserKeys.Add(principalID, keys); - } - keys.Add(newKey); - } - m_log.InfoFormat("[AUTH]: Successfully generated new auth key for {0}", principalID); - } - else - m_log.Warn("[AUTH]: Unauthorized key generation request. Denying new key."); - } - else - m_log.Warn("[AUTH]: Principal not found."); - - return m_AuthorityURL + newKey; - } - - /// - /// This verifies the uuid portion of the key given out by GenerateKey - /// - /// - /// - /// - public bool VerifyKey(UUID userID, string key) - { - lock (m_UserKeys) - { - if (m_UserKeys.ContainsKey(userID)) - { - List keys = m_UserKeys[userID]; - if (keys.Contains(key)) - { - // Keys are one-time only, so remove it - keys.Remove(key); - return true; - } - return false; - } - else - return false; - } - } - - public UUID CreateUserSession(UUID userID, UUID oldSessionID) - { - UserAgentData agent = m_Database.GetAgentByUUID(userID); - - if (agent == null) - return UUID.Zero; - - agent.SessionID = UUID.Random(); - - m_Database.AddNewUserAgent(agent); - return agent.SessionID; - } - - public bool VerifyUserSession(UUID userID, UUID sessionID) - { - UserProfileData userProfile = m_Database.GetUserByUUID(userID); - - if (userProfile != null && userProfile.CurrentAgent != null) - { - m_log.DebugFormat("[AUTH]: Verifying session {0} for {1}; current session {2}", sessionID, userID, userProfile.CurrentAgent.SessionID); - if (userProfile.CurrentAgent.SessionID == sessionID) - { - return true; - } - } - - return false; - } - - public bool DestroyUserSession(UUID userID, UUID sessionID) - { - if (!VerifyUserSession(userID, sessionID)) - return false; - - UserAgentData agent = m_Database.GetAgentByUUID(userID); - if (agent == null) - return false; - - agent.SessionID = UUID.Zero; - m_Database.AddNewUserAgent(agent); - - return true; - } - } -} diff --git a/OpenSim/Services/Interfaces/IAuthenticationService.cs b/OpenSim/Services/Interfaces/IAuthenticationService.cs index 2402414..d473cf8 100644 --- a/OpenSim/Services/Interfaces/IAuthenticationService.cs +++ b/OpenSim/Services/Interfaces/IAuthenticationService.cs @@ -38,57 +38,89 @@ namespace OpenSim.Services.Interfaces // public interface IAuthenticationService { - ////////////////////////////////////////////////// - // Web login key portion + ////////////////////////////////////////////////////// + // PKI Zone! // - - // Get a service key given that principal's - // authentication token (master key). + // HG2 authentication works by using a cryptographic + // exchange. + // This method must provide a public key, the other + // crypto methods must understand hoow to deal with + // messages encrypted to it. // - string GetKey(UUID principalID, string authToken); - - // Verify that a principal key is valid + // If the public key is of zero length, you will + // get NO encryption and NO security. + // + // For non-HG installations, this is not relevant // - bool VerifyKey(UUID principalID, string key); + // Implementors who are not using PKI can treat the + // cyphertext as a string and provide a zero-length + // key. Encryptionless implementations will not + // interoperate with implementations using encryption. + // If one side uses encryption, both must do so. + // + byte[] GetPublicKey(); - ////////////////////////////////////////////////// - // Password auth portion + ////////////////////////////////////////////////////// + // Authentication + // + // These methods will return a token, which can be used to access + // various services. + // + // The encrypted versions take the received cyphertext and + // the public key of the peer, which the connector must have + // obtained using a remote GetPublicKey call. // + string AuthenticatePassword(UUID principalID, string password); + byte[] AuthenticatePasswordEncrypted(byte[] cyphertext, byte[] key); - // Here's how thos works, and why. - // - // The authentication methods will return the existing session, - // or UUID.Zero if authentication failed. If there is no session, - // they will create one. - // The CreateUserSession method will unconditionally create a session - // and invalidate the prior session. - // Grid login uses this method to make sure that the session is - // fresh and new. Other software, like management applications, - // can obtain this existing session if they have a key or password - // for that account, this allows external apps to obtain credentials - // and use authenticating interface methods. - // - - // Check the pricipal's password - // - UUID AuthenticatePassword(UUID principalID, string password); + string AuthenticateWebkey(UUID principalID, string webkey); + byte[] AuthenticateWebkeyEncrypted(byte[] cyphertext, byte[] key); - // Check the principal's key + ////////////////////////////////////////////////////// + // Verification // - UUID AuthenticateKey(UUID principalID, string password); + // Allows to verify the authenticity of a token + // + // Tokens expire after 30 minutes and can be refreshed by + // re-verifying. + // + // If encrypted authentication was used, encrypted verification + // must be used to refresh. Unencrypted verification is still + // performed, but doesn't refresh token lifetime. + // + bool Verify(UUID principalID, string token); + bool VerifyEncrypted(byte[] cyphertext, byte[] key); - // Create a new session, invalidating the old ones + ////////////////////////////////////////////////////// + // Teardown + // + // A token can be returned before the timeout. This + // invalidates it and it can not subsequently be used + // or refreshed. + // + // Tokens created by encrypted authentication must + // be returned by encrypted release calls; // - UUID CreateUserSession(UUID principalID, UUID oldSessionID); + bool Release(UUID principalID, string token); + bool ReleaseEncrypted(byte[] cyphertext, byte[] key); - // Verify that a user session ID is valid. A session ID is - // considered valid when a user has successfully authenticated - // at least one time inside that session. + ////////////////////////////////////////////////////// + // Grid // - bool VerifyUserSession(UUID principalID, UUID sessionID); + // We no longer need a shared secret between grid + // servers. Anything a server requests from another + // server is either done on behalf of a user, in which + // case there is a token, or on behalf of a region, + // which has a session. So, no more keys. + // If sniffing on the local lan is an issue, admins + // need to take approriate action (IPSec is recommended) + // to secure inter-server traffic. - // Deauthenticate user + ////////////////////////////////////////////////////// + // NOTE // - bool DestroyUserSession(UUID principalID, UUID sessionID); + // Session IDs are not handled here. After obtaining + // a token, the session ID regions use can be + // obtained from the presence service. } } -- cgit v1.1