////////////////////////////////////////////////////////////////////////////////
//
// 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
{
/// Ignored.
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 clients = new ESet();
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
};
/**
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.
*/
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/**/! _authenticationProtocols = new Hashtable();
public void Dispose()
{
}
}
///
/// 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.
///
abstract class AuthenticationProtocol
{
public abstract bool CreateSupplicant([Claims]ServiceContract.Exp! exp, Credentials! credentials, out CredError error);
}
}