1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197
|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.ServiceModel.Security.Tokens
{
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IdentityModel.Policy;
using System.IdentityModel.Selectors;
using System.IdentityModel.Tokens;
using System.Security.Cryptography;
using System.Text;
public interface ILogonTokenCacheManager
{
bool RemoveCachedLogonToken(string username);
void FlushLogonTokenCache();
}
class LogonTokenCache : TimeBoundedCache
{
const int lowWaterMarkFactor = 75;
const int saltSize = 4;
TimeSpan cachedLogonTokenLifetime;
RNGCryptoServiceProvider random;
public LogonTokenCache(int maxCachedLogonTokens, TimeSpan cachedLogonTokenLifetime)
: base((maxCachedLogonTokens * lowWaterMarkFactor) / 100, maxCachedLogonTokens, StringComparer.OrdinalIgnoreCase, PurgingMode.TimerBasedPurge, TimeSpan.FromTicks(cachedLogonTokenLifetime.Ticks >> 2), true)
{
this.cachedLogonTokenLifetime = cachedLogonTokenLifetime;
this.random = new RNGCryptoServiceProvider();
}
public bool TryGetTokenCache(string userName, out LogonToken token)
{
token = (LogonToken)GetItem(userName);
return token != null;
}
public bool TryAddTokenCache(string userName, string password, ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
{
byte[] salt = new byte[saltSize];
this.random.GetBytes(salt);
LogonToken token = new LogonToken(userName, password, salt, authorizationPolicies);
DateTime expirationTime = DateTime.UtcNow.Add(this.cachedLogonTokenLifetime);
return TryAddItem(userName, token, expirationTime, true);
}
// Remove those about to expire
protected override ArrayList OnQuotaReached(Hashtable cacheTable)
{
List<IExpirableItem> items = new List<IExpirableItem>(cacheTable.Count);
foreach (IExpirableItem value in cacheTable.Values)
{
items.Add(value);
}
// Those expired soon in front
items.Sort(ExpirableItemComparer.Default);
int pruningAmount = (items.Count * (100 - lowWaterMarkFactor)) / 100;
// edge case
pruningAmount = pruningAmount <= 0 ? items.Count : pruningAmount;
ArrayList keys = new ArrayList(pruningAmount);
for (int i = 0; i < pruningAmount; ++i)
{
LogonToken token = (LogonToken)ExtractItem(items[i]);
keys.Add(token.UserName);
OnRemove(token);
}
return keys;
}
public bool TryRemoveTokenCache(string userName)
{
return this.TryRemoveItem(userName);
}
public void Flush()
{
this.ClearItems();
}
protected override void OnRemove(object item)
{
((LogonToken)item).Dispose();
base.OnRemove(item);
}
}
class LogonToken : IDisposable
{
string userName;
byte[] passwordHash;
byte[] salt;
ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies;
public LogonToken(string userName, string password, byte[] salt, ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies)
{
this.userName = userName;
this.passwordHash = ComputeHash(password, salt);
this.salt = salt;
this.authorizationPolicies = System.IdentityModel.SecurityUtils.CloneAuthorizationPoliciesIfNecessary(authorizationPolicies);
}
public bool PasswordEquals(string password)
{
byte[] passwordHash = ComputeHash(password, this.salt);
return CryptoHelper.IsEqual(this.passwordHash, passwordHash);
}
public string UserName
{
get { return this.userName; }
}
public ReadOnlyCollection<IAuthorizationPolicy> GetAuthorizationPolicies()
{
return System.IdentityModel.SecurityUtils.CloneAuthorizationPoliciesIfNecessary(this.authorizationPolicies);
}
public void Dispose()
{
System.IdentityModel.SecurityUtils.DisposeAuthorizationPoliciesIfNecessary(this.authorizationPolicies);
}
static byte[] ComputeHash(string password, byte[] salt)
{
if (String.IsNullOrEmpty(password))
{
return salt;
}
byte[] bytes = Encoding.Unicode.GetBytes(password);
int saltSize = salt.Length;
for (int i = 0; i < bytes.Length; ++i)
{
bytes[i] ^= salt[i % saltSize];
}
using (HashAlgorithm hashAlgorithm = CryptoHelper.NewSha1HashAlgorithm())
{
return hashAlgorithm.ComputeHash(bytes);
}
}
}
class WindowsUserNameCachingSecurityTokenAuthenticator : WindowsUserNameSecurityTokenAuthenticator, ILogonTokenCacheManager, IDisposable
{
LogonTokenCache logonTokenCache;
public WindowsUserNameCachingSecurityTokenAuthenticator(bool includeWindowsGroups, int maxCachedLogonTokens, TimeSpan cachedLogonTokenLifetime)
: base(includeWindowsGroups)
{
this.logonTokenCache = new LogonTokenCache(maxCachedLogonTokens, cachedLogonTokenLifetime);
}
public void Dispose()
{
FlushLogonTokenCache();
}
protected override ReadOnlyCollection<IAuthorizationPolicy> ValidateUserNamePasswordCore(string userName, string password)
{
LogonToken token;
if (this.logonTokenCache.TryGetTokenCache(userName, out token))
{
if (token.PasswordEquals(password))
{
return token.GetAuthorizationPolicies();
}
else
{
// this prevents logon with old password.
this.logonTokenCache.TryRemoveTokenCache(userName);
}
}
ReadOnlyCollection<IAuthorizationPolicy> authorizationPolicies = base.ValidateUserNamePasswordCore(userName, password);
this.logonTokenCache.TryAddTokenCache(userName, password, authorizationPolicies);
return authorizationPolicies;
}
public bool RemoveCachedLogonToken(string username)
{
if (this.logonTokenCache == null)
return false;
return this.logonTokenCache.TryRemoveTokenCache(username);
}
public void FlushLogonTokenCache()
{
if (this.logonTokenCache != null)
this.logonTokenCache.Flush();
}
}
}
|