/*********************************************************************** * * Copyright (C) 2005-2006 Novell, Inc. 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; version 2.1 * of the License. * * 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 * Library 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, Novell, Inc. * * To contact Novell about this file by physical or electronic mail, * you may find current contact information at www.novell.com. * ***********************************************************************/ using System; using System.Net; using System.IO; using System.Diagnostics; using Novell.Security; using System.Reflection; namespace Novell.Security.ClientPasswordManager { /// /// Provides an implementation of ICredentials that leverages the /// services of the SecretStore Client Service Wallet. /// public class NetCredential: ICredentials { #region Class Members and Defines // This is temporary until we add code to interact with the SecretStore private class UserCache { public string m_userName; public CredentialCache m_credentialCache; public UserCache() { m_userName = null; m_credentialCache = null; } } // This is temporary until we add code to interact with the SecretStore private static UserCache[] m_userCaches = new UserCache[10]; private string m_svcName = null; private string m_svcGroupName = null; private string m_userName = null; private string m_password = null; private string m_credScope = null; private bool m_haveCredentials; #endregion private static bool bMiCasaIsAvailable = false; private static Assembly assembly = null; // private static string MI_CASA_CLASS_NAME = "Novell.CASA.miCASA"; static NetCredential() { try { assembly = Assembly.LoadWithPartialName("Novell.CASA.miCASAWrapper"); if (assembly != null) bMiCasaIsAvailable = true; } catch (Exception e) { Dbg.trcError(e.ToString()); } } /// /// Constructs a NetCredential object for Windows Applications. /// /// There is a high probability that services under the same group share the /// same user name and passwords. By specifying a service group name, the caller wants to /// take advantage of this to try to improve the user experience. If the service group /// name is not specified then no effort is made to share usernames and passwords with /// other services. /// /// If the svcGroupNameIsRealmName parameter is set to true then it is assumed that /// the services in the group validate username and passwords against the same /// database or against synchronized databases. /// /// If the svcGroupNameIsRealmName parameter is set to false then an attempt will be /// made to share passwords between the services in the group but services will be /// allowed to use different passwords. Specifying a service group does not mean /// that the system will not be able to obtain a username and password that is /// unique to a particular service. /// /// Name of service requesting credentials. /// Name of group to which the service belongs, can be null or zero length. /// The group name is the name of the backend authentication realm. /// Name of the user. /// User password if known, otherwise null or empty string. public NetCredential(string svcName, string svcGroupName, bool svcGroupNameIsRealmName, string userName, string password) { // Check input parameters if (svcName == null || svcName.Length == 0 || (svcGroupNameIsRealmName && (svcGroupName == null || svcGroupName.Length == 0)) || userName == null || userName.Length == 0) { Dbg.trcError("NetCredential constructor- Invalid input parameter"); throw new Exception("NetCredential constructor- Invalid input parameter"); } m_svcName = svcName; m_svcGroupName = svcGroupName; m_userName = userName; m_password = password; // Determine the name under which the credentials are scoped if (svcGroupNameIsRealmName) m_credScope = m_svcGroupName; else m_credScope = m_svcName; // Check if we need to update the credential cache if (password != null && password.Length != 0) { if (bMiCasaIsAvailable) { UpdateMiCasaCredential(userName, password, m_svcName, svcGroupName); } else { UpdateCredentialCache(userName, password, m_credScope); } // Remember that there object has credentials associated with it to avoid // looking them up in the cache at a later time. m_haveCredentials = true; } else { // Indicate that we need to look up the credentials in the cache m_haveCredentials = false; } } /// /// Constructs a NetCredential object. /// /// No effort is made to share usernames and passwords with other services. /// /// Name of service requesting credentials. /// Name of the user. /// User password if known, otherwise null or empty string. Not meaningfull if username is not specified. public NetCredential(string svcName, string userName, string password) { // Check input parameters if (svcName == null || svcName.Length == 0 || userName == null || userName.Length == 0) { Dbg.trcError("NetCredential constructor- Invalid input parameter"); throw new Exception("NetCredential constructor- Invalid input parameter"); } m_svcName = svcName; m_userName = userName; m_password = password; // The credentials are scoped to the service name m_credScope = m_svcName; // Check if we need to update the credential cache if (password != null && password.Length != 0) { if (bMiCasaIsAvailable) { UpdateMiCasaCredential(userName, password, m_svcName, null); } else { UpdateCredentialCache(userName, password, m_credScope); } // Remember that there object has credentials associated with it to avoid // looking them up in the cache at a later time. m_haveCredentials = true; } else { // Indicate that we need to look up the credentials in the cache m_haveCredentials = false; } } /// /// Removes a NetworkCredential instance from the cache. /// /// A Uri that specifies the URI prefix of the resources that the credential is used for. /// The authentication scheme used by the host named in uriPrefix. public void Remove(Uri uriPrefix, String authType) { // Only process requests for the "Basic" authentication scheme if (authType.ToUpper() == "BASIC") { if (bMiCasaIsAvailable) { RemoveMiCasaCredential(uriPrefix.ToString()); return; } // Check if we already have credentials if (m_haveCredentials) { // Clear the credentials. m_password = null; m_haveCredentials = false; } // // Lookup the credentials in the cache // // Synchronize access into the cache lock (m_userCaches) { // Look for the user's cache int i; for (i = 0; i < m_userCaches.Length; i++) { // Check if the user cache has been instantiated if (m_userCaches[i] != null) { if (m_userCaches[i].m_userName == m_userName) { // We found a cache for this user, remove the credentials. m_userCaches[i].m_credentialCache.Remove(new Uri("http://" + m_credScope, true), "Basic"); break; } } } } } else { Dbg.trcError("NetCredential.GetCredential- Unsupported authentication scheme"); } } /// /// Obtains the network credentials necessary to gain access to the resources associated /// with the specified uri prefix. /// /// Uri that specifies the URI prefix of resources that the credential grant access to /// The authentication scheme used by the resource /// NetworkCredential object or null if not successful public NetworkCredential GetCredential(Uri uriPrefix, String authType) { NetworkCredential cred = null; // Only process requests for the "Basic" authentication scheme if (authType.ToUpper() == "BASIC") { if (bMiCasaIsAvailable) { return GetMiCasaCredential(m_svcName, m_svcGroupName); } // Check if we already have credentials if (m_haveCredentials) { // Create NetworkCredential object with the credentials that we have. cred = new NetworkCredential(m_userName, m_password); } else { // // Lookup the credentials in the cache // // Synchronize access into the cache lock (m_userCaches) { // Look for the user's cache int i; for (i = 0; i < m_userCaches.Length; i++) { // Check if the user cache has been instantiated if (m_userCaches[i] != null) { if (m_userCaches[i].m_userName == m_userName) { // We found a cache for this user, check if we already have the // necessary credentials. cred = m_userCaches[i].m_credentialCache.GetCredential(new Uri("http://" + m_credScope, true), "Basic"); break; } } } } } } else { Dbg.trcError("NetCredential.GetCredential- Unsuported authentication scheme"); } return cred; } /// /// Updates the credential cache with the supplied credentials. /// /// Name of the user. /// User password. /// Credential scope. private void UpdateCredentialCache(string userName, string password, string credScope) { // Check if we already have a cache for the user lock (m_userCaches) { int i; for (i = 0; i < m_userCaches.Length; i++) { // Check if the user cache has been instantiated if (m_userCaches[i] != null) { if (m_userCaches[i].m_userName == userName) { // A user cache has already been instantiated, check if // we already have a cache entry. if (m_userCaches[i].m_credentialCache.GetCredential(new Uri("http://" + credScope, true), "Basic") != null) { // We have a cache entry, remove it. m_userCaches[i].m_credentialCache.Remove(new Uri("http://" + credScope, true), "Basic"); } break; } } else { // Instantiate user cache and use it m_userCaches[i] = new UserCache(); // Setup user cache m_userCaches[i].m_userName = userName; m_userCaches[i].m_credentialCache = new CredentialCache(); break; } } // Exit if we failed to obtain a user cache if (i == m_userCaches.Length) return; // Create NetworkCredential object and add it to the credential cache NetworkCredential cred = new NetworkCredential(userName, password); m_userCaches[i].m_credentialCache.Add(new Uri("http://" + credScope, true), "Basic", cred); } } // the following is micasa/secretstore code. private void UpdateMiCasaCredential(string sUsername, string sPassword, string sSecretID, string sSharedID) { // call SetCredential through reflection Type type = assembly.GetType(MI_CASA_CLASS_NAME); object Instance = Activator.CreateInstance (type); object[] arguments = new object[4]; arguments[0] = sSecretID; arguments[1] = sSharedID; arguments[2] = sUsername; arguments[3] = sPassword; object result; try { result = type.InvokeMember("SetBasicCredential", BindingFlags.InvokeMethod, null, Instance, arguments); } catch (Exception e) { Dbg.trcError(e.ToString()); } } private NetworkCredential GetMiCasaCredential(string sSecretID, string sSharedID) { object oUsername; object oPassword; Type type = assembly.GetType(MI_CASA_CLASS_NAME); object Instance = Activator.CreateInstance (type); try { object[] arguments = new object[2]; arguments[0] = sSecretID; arguments[1] = sSharedID; // reflection call oUsername = type.InvokeMember("GetCredentialUsername", BindingFlags.InvokeMethod, null, Instance, arguments); oPassword = type.InvokeMember("GetCredentialPassword", BindingFlags.InvokeMethod, null, Instance, arguments); if ((oUsername != null) & (oPassword != null)) return new NetworkCredential(oUsername.ToString().Trim(), oPassword.ToString().Trim()); // invoke the GetUsername and GetPassword methods //object username = basicCred.InvokeMember("GetUsername", BindingFlags.InvokeMethod, null, result, null); //object password = basicCred.InvokeMember("GetPassword", BindingFlags.InvokeMethod, null, result, null); } catch (Exception e) { Dbg.trcError(e.ToString()); } return null; } private void RemoveMiCasaCredential(string sSecretID) { Type type = assembly.GetType(MI_CASA_CLASS_NAME); object Instance = Activator.CreateInstance (type); try { object[] arguments = new object[2]; arguments[0] = sSecretID; arguments[1] = null; // reflection call type.InvokeMember("RemoveBasicCredential", BindingFlags.InvokeMethod, null, Instance, arguments); } catch (Exception e) { Dbg.trcError(e.ToString()); } } } }