/***********************************************************************
* File: NetCredential.cs
* Author: Juan Carlos Luciani (jluciani@novell.com)
*
* Namespace: Novell.Security.ClientPasswordManager
*
* Classes implemented: NetCredential.
*
* Copyright (C) 2004 Novell, Inc.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free
* Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
***********************************************************************/
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());
}
}
}
}