472 lines
16 KiB
C#
472 lines
16 KiB
C#
|
/***********************************************************************
|
||
|
* 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
|
||
|
{
|
||
|
/// <summary>
|
||
|
/// Provides an implementation of ICredentials that leverages the
|
||
|
/// services of the SecretStore Client Service Wallet.
|
||
|
/// </summary>
|
||
|
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());
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/// <summary>
|
||
|
/// 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.
|
||
|
/// </summary>
|
||
|
/// <param name="svcName">Name of service requesting credentials.</param>
|
||
|
/// <param name="svcGroupName">Name of group to which the service belongs, can be null or zero length.</param>
|
||
|
/// <param name="svcGroupNameIsRealmName">The group name is the name of the backend authentication realm.</param>
|
||
|
/// <param name="userName">Name of the user.</param>
|
||
|
/// <param name="password">User password if known, otherwise null or empty string.</param>
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Constructs a NetCredential object.
|
||
|
///
|
||
|
/// No effort is made to share usernames and passwords with other services.
|
||
|
/// </summary>
|
||
|
/// <param name="svcName">Name of service requesting credentials.</param>
|
||
|
/// <param name="userName">Name of the user.</param>
|
||
|
/// <param name="password">User password if known, otherwise null or empty string. Not meaningfull if username is not specified.</param>
|
||
|
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;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Removes a NetworkCredential instance from the cache.
|
||
|
/// </summary>
|
||
|
/// <param name="uriPrefix">A Uri that specifies the URI prefix of the resources that the credential is used for.</param>
|
||
|
/// <param name="authType">The authentication scheme used by the host named in <i>uriPrefix</i>. </param>
|
||
|
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");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Obtains the network credentials necessary to gain access to the resources associated
|
||
|
/// with the specified uri prefix.
|
||
|
/// </summary>
|
||
|
/// <param name="uriPrefix">Uri that specifies the URI prefix of resources that the credential grant access to</param>
|
||
|
/// <param name="authType">The authentication scheme used by the resource</param>
|
||
|
/// <returns>NetworkCredential object or null if not successful</returns>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Updates the credential cache with the supplied credentials.
|
||
|
/// </summary>
|
||
|
/// <param name="userName">Name of the user.</param>
|
||
|
/// <param name="password">User password.</param>
|
||
|
/// <param name="credScope">Credential scope.</param>
|
||
|
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());
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|