singrdk/base/Kernel/Singularity.Security/AccessControl/AclConverter.sg

360 lines
13 KiB
Plaintext

// ----------------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
// ----------------------------------------------------------------------------
// #define VERBOSE
// #define REALLY_VERBOSE
namespace Microsoft.Singularity.Security.AccessControl
{
using System;
using System.Text;
using System.Text.RegularExpressions;
using System.Collections;
using Microsoft.SingSharp;
using Microsoft.Singularity.Security;
using Microsoft.Singularity.Channels;
/// <summary>
/// Converts an Access Control List description to a regular expression.
/// Caches group expansions for improved performance.
/// </summary>
public class AclConverter
{
/// <summary>
/// Regular expression matching an path fragment
/// </summary>
protected const string pathfrag = @"[a-zA-Z1-90_]+(\.[a-zA-Z1-90_]+)*";
/// <summary>
/// Regular expression matching the beginning of a string
/// </summary>
protected const string start = "^(";
/// <summary>
/// Regular expression matching the end of a string
/// </summary>
protected const string end = ")$";
/// <summary>expression
/// The reader of group definitions
/// </summary>
protected Cache! cache;
private IAclCoreSupport support;
/// <summary>
/// Create new converter,
/// </summary>
private const int ExpnCachePrunePercent = 20;
public AclConverter(IAclCoreSupport _support, int expnCacheMaxEntries, int expnCacheExpirySeconds)
{
this.support = _support;
this.cache = new Cache(expnCacheMaxEntries, expnCacheExpirySeconds,
ExpnCachePrunePercent, "Expansion Cache: ");
}
/// <summary>
/// Convert the specified ACL to a regular expression
/// </summary>
/// <param name="acl">The access control list</param>
/// <returns>An equivalent text regular expression</returns>
public void DumpStats(StringBuilder! sb)
{
cache.DumpStats(sb);
}
public void ClearStats()
{
cache.ResetStats();
}
public void FlushCache()
{
cache.Prune(true);
}
public string! Convert(string! aclstr, bool bypassCache, out DateTime minExpiry)
{
StringBuilder! sb = new StringBuilder();
Stack! stack = new Stack();
int nSubExprs = 0;
minExpiry = DateTime.MaxValue;
sb.Append(start);
DoExpand(aclstr, sb, "", stack, bypassCache, ref nSubExprs, ref minExpiry);
sb.Append(end);
return sb.ToString();
}
public string! ConvertTest(string! aclstr, out int nSubExprs)
{
DateTime minExpiry = DateTime.MaxValue;
StringBuilder! sb = new StringBuilder();
Stack! stack = new Stack();
nSubExprs = 0;
sb.Append(start);
DoExpand(aclstr, sb, "", stack, false, ref nSubExprs, ref minExpiry);
sb.Append(end);
return sb.ToString();
}
internal class CacheEntry : ICacheValue
{
public string! expansion;
public bool constant;
public int nSubExprs;
public CacheEntry(Cache! cache, string! expansion, bool constant,
int nSubExprs, DateTime minExpiry)
{
base(cache, minExpiry);
this.expansion = expansion;
this.constant = constant;
this.nSubExprs = nSubExprs;
}
}
private void DoExpand(string! pattern, StringBuilder! sb, string! ns,
Stack! stack, bool bypassCache, ref int nSubExprs, ref DateTime minExpiry)
{
AclToken t;
AclParser parser = new AclParser(pattern);
CacheEntry ce = null;
int eStart = sb.Length;
int eSubExprs = nSubExprs;
ce = (CacheEntry) cache.GetEntry(pattern);
if (ce != null) {
if (ce.Expired)
ce = null;
else if (!bypassCache || ce.constant) {
#if VERBOSE
DebugStub.Print("DoExpand: Cache hit {0}\n", __arglist(path));
#endif
if (ce.expiry < minExpiry)
minExpiry = ce.expiry;
sb.Append(ce.expansion);
nSubExprs += ce.nSubExprs;
return;
}
}
while ((t = parser.NextToken()) != null) {
switch (t.Type) {
case AclTokenType.Literal:
#if REALLY_VERBOSE
DebugStub.Print("Literal: {0}\n", __arglist(t.Text));
#endif
sb.Append(t.Text);
break;
case AclTokenType.Arc:
#if REALLY_VERBOSE
DebugStub.Print("Arc: {0}\n", __arglist(t.Text));
#endif
// Arcs need no transformation
sb.Append(t.Text);
break;
case AclTokenType.Any:
// Any matches any path fragment
#if REALLY_VERBOSE
DebugStub.Print("Any: {0}\n", __arglist(t.Text));
#endif
sb.Append(pathfrag);
break;
case AclTokenType.Escape:
// Reserved regular expression symbol: escape it
#if REALLY_VERBOSE
DebugStub.Print("Escape: {0}\n", __arglist(t.Text));
#endif
sb.AppendFormat("\\{0}", t.Text);
break;
case AclTokenType.GroupName:
//
//A sub-expression can have relative or absolute name.
//We resolve relative names by keeping track of the namespace
//of the last resolution.
//
bool isPath = true;
string path = t.Text;
nSubExprs++;
if (path.Length != 0) {
if (path[0] == '$') {
isPath = false;
}
else {
if (path[0] != '/') {
// this is a relative path. append the namespace
path = ns + "/" + path;
}
// update the namespace name:
int index = path.LastIndexOf('/');
if (index == -1) {
ns = String.Empty;
}
else {
ns = (!)path.Substring(0, index);
}
}
string subexpr = ReadSubexpression(path, isPath);
if (subexpr == null) {
#if VERBOSE
DebugStub.Print("Undefined subexpression: {0}\n", __arglist(t.Text));
#endif
}
if (stack.Contains(path)) {
throw new Exception("Infinite recursion detected while expanding acl.");
}
sb.Append('(');
stack.Push(path);
if (subexpr != null && subexpr.Length != 0)
DoExpand(subexpr, sb, ns, stack, bypassCache, ref nSubExprs, ref minExpiry);
stack.Pop();
sb.Append(')');
}
break;
case AclTokenType.Miscellaneous:
DebugStub.Print("Invalid token: {0} Skipping it\n", __arglist(t.Text));
break;
}
}
// cache the results of the expansion at this level
string expansion = sb.ToString(eStart, sb.Length-eStart);
if (ce == null || expansion != ce.expansion) {
ce = new CacheEntry(cache, expansion, false, nSubExprs-eSubExprs, minExpiry);
cache.AddEntry(pattern, ce);
}
else if (ce != null) {
// Old expansion was the same, update expiry.
ce.ResetExpiry(cache, minExpiry);
}
if (ce.expiry < minExpiry)
minExpiry = ce.expiry;
}
private string ReadSubexpression(string! path, bool isPath)
{
string result = null;
if (isPath) {
if (support == null)
DebugStub.Print("ReadSubexpression: AclCoreSupport object missing\n");
else
result = support.Expand(path);
}
else
result = Principal.ExpandAclIndirection(path);
return result;
}
//
//// this code has been moved to the client impl (e.g. DirAclCoreSupport.sg)
//
//private string ReadGroup(string! path)
//{
// return support.GetGroupContents(path);
//}
//
//// read the file size
// DirectoryServiceContract.Imp:Ready! ns = AcquireNSRoot();
// byte[] result = ReadFromFile(path, ns);
// ReleaseNSRoot(ns);
//
// if (result == null)
// return null;
//
// string! subexpression = (!)encoder.GetString(result);
// return subexpression;
//}
//
//private DirectoryServiceContract.Imp:Ready! AcquireNSRoot()
//{
//
// if (cachedRootNS == null)
// cachedRootNS =
// new TRef<DirectoryServiceContract.Imp:Ready>(support.GroupDirectoryRoot());
// return cachedRootNS.Acquire();
//}
//
//private void ReleaseNSRoot([Claims] DirectoryServiceContract.Imp:Ready! ns)
//{
// cachedRootNS.Release(ns);
//}
//
//private byte[] ReadFromFile(string!path, DirectoryServiceContract.Imp:Ready! ns)
//{
// ErrorCode errCode;
// byte[] result;
// long size = 0;
// int readSize = 4096;
// NodeType nodeType;
//
//
// try {
// bool temp = SdsUtils.GetAttributes(path, ns, out size, out nodeType, out errCode);
// if (!temp)
// // element does not exist or an error has occurred
// throw new Exception("Cannot get attributes - " + SdsUtils.ErrorCodeToString(errCode));
//
// result = new byte[size];
// byte[] in ExHeap buf = new[ExHeap] byte[readSize];
// if (result == null || buf == null)
// throw new Exception("Cannot allocate memory");
//
// FileContract.Imp! fileImp;
// FileContract.Exp! fileExp;
// FileContract.NewChannel(out fileImp, out fileExp);
// bool bindOk = SdsUtils.Bind(path, ns, fileExp, out errCode);
// if (!bindOk) {
// delete fileImp;
// throw new Exception("Can't read group - " + SdsUtils.ErrorCodeToString(errCode));
// }
// fileImp.RecvSuccess();
//
// long readOffset = 0;
// while (true) {
// fileImp.SendRead(buf, 0, readOffset, readSize);
// switch receive {
// case fileImp.AckRead(localbuf, bytesRead, error) :
// // move the memory
// Bitter.ToByteArray(localbuf, 0, (int)bytesRead, result, (int)readOffset);
// if (bytesRead == readSize) {
// // see if there is more
// readOffset += bytesRead;
// buf = localbuf;
// continue;
// }
// delete localbuf;
// break;
// case fileImp.ChannelClosed() :
// break;
// case unsatisfiable :
// break;
// }
// break;
// }
// delete fileImp;
// }
// catch (Exception e) {
// DebugStub.Print("An exception occurred while reading group definition: {0}\n",
// __arglist(path));
// DebugStub.Print("Message: {0}\n", __arglist(e.Message));
// return null;
// }
// return result;
//}
//
}
}