623 lines
30 KiB
Plaintext
623 lines
30 KiB
Plaintext
////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Microsoft Research Singularity
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
//
|
|
// File: Services/CredentialsManager/CredentialsManagerService.sg
|
|
//
|
|
// Note: Contains the entry point and main I/O loop for the service.
|
|
//
|
|
// All clients that connect using the CredentialsManagerContract are
|
|
// serviced on the "main" thread of the CM. All of the requests of
|
|
// that contract can be satisfied synchronously (without waiting for
|
|
// any external I/O or blocking).
|
|
//
|
|
// Supplicant channels are serviced on separate threads. Although it
|
|
// would be possible to service some of them on the main thread,
|
|
// separate threads make the code simpler, and allow for later protocols
|
|
// which might require more serious work be done to service requests.
|
|
//
|
|
|
|
using System;
|
|
using System.Collections;
|
|
using System.Diagnostics;
|
|
using Microsoft.Contracts;
|
|
using Microsoft.Singularity;
|
|
using Microsoft.Singularity.Channels;
|
|
using Microsoft.Singularity.Directory;
|
|
using Microsoft.Singularity.Io;
|
|
using Microsoft.SingSharp;
|
|
|
|
using Ex = Microsoft.Singularity.Security;
|
|
|
|
[assembly: Microsoft.Singularity.Security.ApplicationPublisherAttribute("singularity.microsoft.com")]
|
|
[assembly: Microsoft.Singularity.Security.AssertPrivilegeAttribute("$register-privilege.localhost")]
|
|
|
|
namespace Microsoft.Singularity.Security.CredentialsManager
|
|
{
|
|
internal class CredentialsManagerService : ITracked
|
|
{
|
|
/// <param name="args">Ignored.</param>
|
|
public static int Main(string[] args)
|
|
{
|
|
CredentialsManagerService service = new CredentialsManagerService();
|
|
try {
|
|
service.Run();
|
|
return 0;
|
|
} finally {
|
|
service.Dispose();
|
|
}
|
|
}
|
|
|
|
readonly CredentialsCollection! _credentials = new CredentialsCollection();
|
|
|
|
readonly ProtocolMappingCollection! _protocolMappings = new ProtocolMappingCollection();
|
|
|
|
void Run()
|
|
{
|
|
DebugLine("Credentials Manager service is initializing...");
|
|
|
|
_authenticationProtocols[AuthenticationProtocolNames.Ntlm] = new NtlmAuthenticationProtocol();
|
|
|
|
ServiceProviderContract.Imp! manager_provider_imp;
|
|
ServiceProviderContract.Exp! manager_provider;
|
|
ServiceProviderContract.NewChannel(out manager_provider_imp, out manager_provider);
|
|
|
|
DirectoryServiceContract.Imp! rootdir = DirectoryService.NewClientEndpoint();
|
|
rootdir.SendRegister(Bitter.FromString2(CredentialsManagerContract.ChannelPath), manager_provider_imp);
|
|
|
|
switch receive {
|
|
case rootdir.AckRegister():
|
|
break;
|
|
|
|
case rootdir.NakRegister(imp, error):
|
|
DebugLine("Failed to register control endpoint. Error: " + SdsUtils.ErrorCodeToString(error));
|
|
delete imp;
|
|
delete rootdir;
|
|
delete manager_provider;
|
|
throw new Exception("Failed to register control endpoint: " + SdsUtils.ErrorCodeToString(error));
|
|
|
|
case rootdir.NakRegisterReparse(path, rest, linkFound, imp):
|
|
DebugLine("Failed to register control endpoint. Traversal of reparse points is not supported.");
|
|
delete path;
|
|
delete rest;
|
|
delete imp;
|
|
delete rootdir;
|
|
delete manager_provider;
|
|
throw new Exception("Failed to register control endpoint; reparse points are not supported.");
|
|
}
|
|
|
|
delete rootdir;
|
|
|
|
ESet<CredentialsManagerContract.Exp:Ready> clients = new ESet<CredentialsManagerContract.Exp:Ready>();
|
|
|
|
DebugLine("Credentials Manager service is ready.");
|
|
|
|
for (;;)
|
|
{
|
|
try {
|
|
switch receive
|
|
{
|
|
case manager_provider.Connect(exp):
|
|
{
|
|
CredentialsManagerContract.Exp:Start client = exp as CredentialsManagerContract.Exp:Start;
|
|
if (client != null) {
|
|
manager_provider.SendAckConnect();
|
|
client.SendSuccess();
|
|
clients.Add(client);
|
|
} else {
|
|
manager_provider.SendNackConnect(exp);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case manager_provider.ChannelClosed():
|
|
DebugLine("Provider channel has closed!");
|
|
goto quit;
|
|
|
|
case client.ChannelClosed() in clients:
|
|
// DebugLine("Client has disconnected.");
|
|
delete client;
|
|
break;
|
|
|
|
case client.AddCredentials(Ex.CredentialsId excredentials, char[]! in ExHeap expassword, bool replace) in clients:
|
|
{
|
|
CredentialsId id = new CredentialsId(
|
|
Bitter.ToString2(excredentials.CredentialsName),
|
|
Bitter.ToString2(excredentials.Tag));
|
|
string! password = Bitter.ToString2(expassword);
|
|
|
|
excredentials.Dispose();
|
|
delete expassword;
|
|
|
|
if (TraceSwitches.ShowManagerMessages) {
|
|
DebugLine("Received AddCredentials: " + id + " [evidence not shown]");
|
|
}
|
|
|
|
if (_credentials.ContainsKey(id) && !replace) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Already have credentials matching that key.");
|
|
client.SendRequestFailed(CredError.MatchingEntryExists);
|
|
} else {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Setting credentials.");
|
|
|
|
PasswordEvidence evidence = new PasswordEvidence(password);
|
|
Credentials credentials = new Credentials(id, evidence);
|
|
_credentials[id] = credentials;
|
|
client.SendOk();
|
|
}
|
|
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.DeleteCredentials(excredentials) in clients:
|
|
{
|
|
CredentialsId id = new CredentialsId(
|
|
Bitter.ToString2(excredentials.CredentialsName),
|
|
Bitter.ToString2(excredentials.Tag));
|
|
excredentials.Dispose();
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received DeleteCredentials: " + id.ToString());
|
|
|
|
if (_credentials.ContainsKey(id)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Removing credentials.");
|
|
|
|
_credentials.Remove(id);
|
|
client.SendOk();
|
|
} else {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" No such credentials registered.");
|
|
|
|
client.SendRequestFailed(CredError.NoMatchingCredentials);
|
|
}
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.DeleteAllCredentials() in clients:
|
|
{
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received DeleteAllCredentials");
|
|
|
|
_credentials.Clear();
|
|
client.SendOk();
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.EnumerateCredentials() in clients:
|
|
{
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received EnumerateCredentials");
|
|
|
|
ICollection! keys = (!)_credentials.Keys;
|
|
int count = keys.Count;
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Sending " + count + " entries");
|
|
|
|
Ex.CredentialsId[]! in ExHeap list = new[ExHeap] Ex.CredentialsId[count];
|
|
|
|
IEnumerator enumerator = keys.GetEnumerator();
|
|
for (int i = 0; i < count; i++) {
|
|
|
|
if (!enumerator.MoveNext()) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Enumerator lied to us!");
|
|
break;
|
|
}
|
|
|
|
CredentialsId! id = (!)(CredentialsId)enumerator.Current;
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Entry: " + id.ToString());
|
|
|
|
expose(list[i]) {
|
|
// these will be null, but we have to keep the type checker happy
|
|
delete list[i].CredentialsName;
|
|
delete list[i].Tag;
|
|
|
|
list[i].CredentialsName = Bitter.FromString2(id.CredentialsName);
|
|
list[i].Tag = Bitter.FromString2(id.Tag);
|
|
}
|
|
}
|
|
|
|
client.SendCredentialsList(list);
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.AddProtocolMapping(Ex.ProtocolTuple extuple, Ex.CredentialsId excredentials, bool replace) in clients:
|
|
{
|
|
ProtocolTuple tuple = new ProtocolTuple(extuple);
|
|
CredentialsId credentials = new CredentialsId(excredentials);
|
|
extuple.Dispose();
|
|
excredentials.Dispose();
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received AddProtocolMapping: {0} -> {1}", tuple.ToString(), credentials.ToString());
|
|
|
|
if (!replace && _protocolMappings.ContainsKey(tuple)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("A protocol mapping with the same key already exists.");
|
|
|
|
client.SendRequestFailed(CredError.MatchingEntryExists);
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
if (!_credentials.ContainsKey(credentials)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("The specified credentials do not exist.");
|
|
|
|
client.SendRequestFailed(CredError.NoMatchingCredentials);
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
_protocolMappings[tuple] = credentials;
|
|
client.SendOk();
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.DeleteProtocolMapping(Ex.ProtocolTuple extuple) in clients:
|
|
ProtocolTuple tuple = new ProtocolTuple(extuple);
|
|
extuple.Dispose();
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received DeleteProtocolMapping: tuple = " + tuple.ToString());
|
|
|
|
if (_protocolMappings.ContainsKey(tuple)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Protocol mapping removed.");
|
|
_protocolMappings.Remove(tuple);
|
|
client.SendOk();
|
|
} else {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" No matching entry");
|
|
client.SendRequestFailed(CredError.NoMatchingCredentials);
|
|
}
|
|
clients.Add(client);
|
|
break;
|
|
|
|
case client.DeleteAllProtocolMappings() in clients:
|
|
{
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received DeleteAllProtocolMappings");
|
|
_protocolMappings.Clear();
|
|
client.SendOk();
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.FindMatchingProtocolMapping(Ex.ProtocolTuple extuple, bool useWildcards) in clients:
|
|
{
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received FindMatchingProtocolMapping:");
|
|
|
|
ProtocolTuple tuple = new ProtocolTuple(extuple);
|
|
extuple.Dispose();
|
|
|
|
CredentialsId match;
|
|
|
|
if (FindMatchingProtocolMapping(tuple, out match, useWildcards)) {
|
|
assert match != null;
|
|
DebugLine(" Match found: " + match.ToString());
|
|
client.SendMatchingProtocolMapping(match.ToExchange());
|
|
} else {
|
|
DebugLine(" No matching entry found.");
|
|
client.SendRequestFailed(CredError.NoMatchingCredentials);
|
|
}
|
|
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.EnumerateProtocolMappings() in clients:
|
|
{
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received EnumerateProtocolMappings");
|
|
|
|
int count = _protocolMappings.Count;
|
|
Ex.ProtocolMapping[]! in ExHeap list = new[ExHeap] Ex.ProtocolMapping[count];
|
|
|
|
IEnumerator enumerator = _protocolMappings.GetEnumerator();
|
|
for (int i = 0; i < count; i++) {
|
|
if (!enumerator.MoveNext()) {
|
|
DebugLine("Enumerator lied!!");
|
|
break;
|
|
}
|
|
|
|
DictionaryEntry entry = (DictionaryEntry)(!)enumerator.Current;
|
|
ProtocolTuple! tuple = (ProtocolTuple)entry.Key;
|
|
CredentialsId! id = (!)(CredentialsId)entry.Value;
|
|
|
|
expose (list[i]) {
|
|
list[i].Dispose();
|
|
list[i].ProtocolTuple.ApplicationProtocol = Bitter.FromString2(tuple.ApplicationProtocol);
|
|
list[i].ProtocolTuple.AuthenticationProtocol = Bitter.FromString2(tuple.AuthenticationProtocol);
|
|
list[i].ProtocolTuple.ServiceAddress = Bitter.FromString2(tuple.ServiceAddress);
|
|
list[i].ProtocolTuple.Realm = Bitter.FromString2(tuple.Realm);
|
|
list[i].CredentialsId.CredentialsName = Bitter.FromString2(id.CredentialsName);
|
|
list[i].CredentialsId.Tag = Bitter.FromString2(id.Tag);
|
|
}
|
|
}
|
|
|
|
client.SendProtocolMappings(list);
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.CreateSupplicant(
|
|
char[]! in ExHeap exauthenticationProtocol,
|
|
Ex.CredentialsId excredentials,
|
|
ServiceContract.Exp:Start! exp) in clients:
|
|
{
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine("Received CreateSupplicant");
|
|
|
|
string! authenticationProtocol = Util.ToStringDelete(exauthenticationProtocol);
|
|
CredentialsId! id = new CredentialsId(excredentials);
|
|
excredentials.Dispose();
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Credentials ID: " + id);
|
|
|
|
// First, find credentials.
|
|
|
|
Credentials credentials;
|
|
if (!_credentials.TryGetValue(id, out credentials)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Credentials not found.");
|
|
delete exp;
|
|
client.SendRequestFailed(CredError.NoMatchingCredentials);
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
assert credentials != null;
|
|
|
|
// For now, we support only a hard-coded set of authentication protocols.
|
|
|
|
AuthenticationProtocol protocol = (AuthenticationProtocol)_authenticationProtocols[authenticationProtocol];
|
|
if (protocol == null) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Authentication protocol not recognized: " + authenticationProtocol);
|
|
client.SendRequestFailed(CredError.NoMatchingAuthenticationProtocol);
|
|
delete exp;
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
CredError error;
|
|
if (protocol.CreateSupplicant(exp, credentials, out error)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Authentication protocol accepted client");
|
|
client.SendOk();
|
|
} else {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Authentication protocol refused client: " + error);
|
|
client.SendRequestFailed(error);
|
|
}
|
|
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
case client.CreateSupplicantForProtocol(Ex.ProtocolTuple extuple, ServiceContract.Exp:Start! exp) in clients:
|
|
{
|
|
ProtocolTuple tuple = new ProtocolTuple(extuple);
|
|
extuple.Dispose();
|
|
|
|
if (TraceSwitches.ShowManagerMessages) {
|
|
DebugLine("Received CreateSupplicantForProtocol");
|
|
DebugLine(" Tuple = " + tuple);
|
|
}
|
|
|
|
// First, find credentials.
|
|
|
|
CredentialsId id;
|
|
if (!FindMatchingProtocolMapping(tuple, out id, true)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" No matching credentials found.");
|
|
delete exp;
|
|
client.SendRequestFailed(CredError.NoMatchingCredentials);
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
assert id != null;
|
|
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Selected credentials: " + id);
|
|
|
|
Credentials credentials;
|
|
if (!_credentials.TryGetValue(id, out credentials)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Credentials not found.");
|
|
|
|
delete exp;
|
|
client.SendRequestFailed(CredError.NoMatchingCredentials);
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
assert credentials != null;
|
|
|
|
AuthenticationProtocol protocol = (AuthenticationProtocol)_authenticationProtocols[tuple.AuthenticationProtocol];
|
|
if (protocol == null) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Authentication protocol not recognized: " + tuple.AuthenticationProtocol);
|
|
|
|
client.SendRequestFailed(CredError.NoMatchingAuthenticationProtocol);
|
|
delete exp;
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
CredError error;
|
|
if (protocol.CreateSupplicant(exp, credentials, out error)) {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Authentication protocol accepted client");
|
|
client.SendAckCreateSupplicantForProtocol(id.ToExchange());
|
|
} else {
|
|
if (TraceSwitches.ShowManagerMessages)
|
|
DebugLine(" Authentication protocol refused client: " + error);
|
|
client.SendRequestFailed(error);
|
|
}
|
|
|
|
clients.Add(client);
|
|
break;
|
|
}
|
|
|
|
|
|
|
|
} // switch receive
|
|
|
|
} catch (Exception ex) {
|
|
DebugLine("Uncaught exception in switch-receive loop!");
|
|
Util.DumpException(ex);
|
|
DebugLine("Will soldier on...");
|
|
}
|
|
}
|
|
|
|
quit:
|
|
delete manager_provider;
|
|
clients.Dispose();
|
|
|
|
}
|
|
|
|
// This is a permutation on 4 bits, sorted in ascending order
|
|
// of the number of bits that are set to 1. We use this to order
|
|
// our search of the protocol mappings table. Each bit in the mask
|
|
// that is set to 1 indicates a field in ProtocolTuple to replace
|
|
// with a wildcard.
|
|
static readonly int[]! _WildcardMasks = {
|
|
0, // 0000 // 0 bits set
|
|
1, // 0001 // 1 bit set
|
|
2, // 0010
|
|
4, // 0100
|
|
8, // 1000
|
|
3, // 0011 // groups with 2 bits set
|
|
5, // 0101
|
|
6, // 0110
|
|
9, // 1001
|
|
10, // 1010
|
|
12, // 1100
|
|
7, // 0111 // 3 bits set
|
|
11, // 1011
|
|
13, // 1101
|
|
14, // 1110
|
|
15, // 1111 // 4 bits set
|
|
};
|
|
|
|
|
|
|
|
/** <summary>
|
|
This class searches the protocol mappings table for credentials that match a given
|
|
protocol tuple. If the useWildcard argument is false, then a single, exact match
|
|
is performed. If the useWildcard argument is true, then the method will search for
|
|
the most specific protocol mapping that matches the tuple provided. If an exact
|
|
search does not find a tuple, then the method will replace some of the fields with
|
|
the wildcard marker ("*") and try again, until it has tried all combinations of
|
|
fields, including the "all fields wild" tuple of < * * * * >. The _WildcardMasks
|
|
array controls the search order.
|
|
</summary> */
|
|
bool FindMatchingProtocolMapping(ProtocolTuple! tuple, out CredentialsId match, bool useWildcards)
|
|
{
|
|
if (useWildcards) {
|
|
if (TraceSwitches.ShowWildcardMatching) {
|
|
DebugLine(" Query is a wildcard query");
|
|
DebugLine("Generating variations of this tuple: " + tuple);
|
|
}
|
|
|
|
match = null;
|
|
|
|
// bit 0:
|
|
for (int permutation_index = 0; permutation_index < _WildcardMasks.Length; permutation_index++)
|
|
{
|
|
int wildcard_mask = _WildcardMasks[permutation_index];
|
|
|
|
// If the tuple that we're using as our search key
|
|
// Each bit in 'wildcard_mask' corresponds to a field in ProtocolTuple.
|
|
// Generate a search tuple (variation) with some values replaced with the wildcard marker "*".
|
|
// Then query the table for the variation.
|
|
|
|
ProtocolTuple variation = new ProtocolTuple(
|
|
((wildcard_mask & (1 << ProtocolTuple.ApplicationProtocolMaskBit)) != 0) ? ProtocolTuple.WildcardValue : tuple.ApplicationProtocol,
|
|
((wildcard_mask & (1 << ProtocolTuple.ServiceAddressMaskBit)) != 0) ? ProtocolTuple.WildcardValue : tuple.ServiceAddress,
|
|
((wildcard_mask & (1 << ProtocolTuple.AuthenticationProtocolMaskBit)) != 0) ? ProtocolTuple.WildcardValue : tuple.AuthenticationProtocol,
|
|
((wildcard_mask & (1 << ProtocolTuple.RealmMaskBit)) != 0) ? ProtocolTuple.WildcardValue : tuple.Realm);
|
|
|
|
CredentialsId m;
|
|
if (_protocolMappings.TryGetValue(variation, out m)) {
|
|
assert m != null;
|
|
if (TraceSwitches.ShowWildcardMatching)
|
|
DebugLine(" Variation MATCHES: mask: {0:x1} {1} -> {2}", wildcard_mask, variation.ToString(), m);
|
|
if (match == null) {
|
|
if (TraceSwitches.ShowWildcardMatching)
|
|
DebugLine(" (using this match, showing future matches only for debugging)");
|
|
match = m;
|
|
}
|
|
} else {
|
|
if (TraceSwitches.ShowWildcardMatching)
|
|
DebugLine(" Variation: no match, mask: {0:x1} {1}", wildcard_mask, variation.ToString());
|
|
}
|
|
}
|
|
|
|
if (match != null) {
|
|
if (TraceSwitches.ShowWildcardMatching)
|
|
DebugLine(" Final match: " + match);
|
|
return true;
|
|
} else {
|
|
if (TraceSwitches.ShowWildcardMatching)
|
|
DebugLine(" No matches.");
|
|
return false;
|
|
}
|
|
} else {
|
|
// The client wants to do a simple, exact match (no wildcards).
|
|
// This is easy.
|
|
if (TraceSwitches.ShowWildcardMatching)
|
|
DebugLine(" Query is an exact-match query (no wildcards)");
|
|
return _protocolMappings.TryGetValue(tuple, out match);
|
|
}
|
|
}
|
|
|
|
void DebugLine(string! format, params object[]! args)
|
|
{
|
|
DebugLine(String.Format(format, args));
|
|
}
|
|
|
|
void DebugLine(string! line)
|
|
{
|
|
DebugStub.WriteLine("CREDMGR: " + line);
|
|
}
|
|
|
|
void ITracked.Acquire() {}
|
|
void ITracked.Release() {}
|
|
void ITracked.Expose() {}
|
|
void ITracked.UnExpose() {}
|
|
|
|
readonly Hashtable/*<string,AuthenticationProtocol>*/! _authenticationProtocols = new Hashtable();
|
|
|
|
public void Dispose()
|
|
{
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an authentication protocol, but not an instance of such a protocol.
|
|
/// Derived classes override the CreateSupplicant method, which is used to create
|
|
/// instances of authentication protocols.
|
|
/// </summary>
|
|
abstract class AuthenticationProtocol
|
|
{
|
|
public abstract bool CreateSupplicant([Claims]ServiceContract.Exp! exp, Credentials! credentials, out CredError error);
|
|
}
|
|
}
|
|
|