singrdk/base/Kernel/Singularity.Security/SDS/SdsAclImpl.sg

306 lines
10 KiB
Plaintext

////////////////////////////////////////////////////////////////////////////////
//Microsoft Research Singularity// Copyright (c) Microsoft
//Corporation. All rights reserved. File: SdsAclPerms.sg Note:
namespace Microsoft.Singularity.Security.SDS
{
using System;
using Microsoft.Contracts;
using Microsoft.Singularity.Security;
// This module support path-based ACLs in the Singularity Directory Service.
// ACLs are kept in prefix-table adjoining the SDS data structures.
// This module contains a non-persistent implementation. It can be subclassed to
// produce a persistent table.
public class SdsAclImpl : ISdsAcl
{
//
//All "path" variables here must start with "/", end with other than "/" (except for the
//root), and contain no repeated "/" characters.
//
// We have two subtypes of IEffectiveAclRef: one for inherited ACLs, and other
// for those that represent an exact match with the target. This here is of the
// former variety.
public class AclRef : IEffectiveAclRef {
private bool valid = true;
private HEntry! entry;
public bool GetCached (out string aclPattern) {
aclPattern = (entry.acl.Descendant != null) ? entry.acl.Descendant : entry.acl.Node;
return valid;
}
internal AclRef(HEntry! _entry)
{
entry = _entry;
}
internal void Invalidate() {
valid = false;
}
}
//This class is returned to clients for exact path matches.
internal class HEntry : IEffectiveAclRef
{
internal bool inTable;
internal string! path;
internal int pathlen;
internal Acl acl;
internal AclRef iref;
internal HEntry chain;
public bool GetCached (out string aclPattern) {
aclPattern = acl.Node;
return inTable;
}
// this constructor for placeholders
internal HEntry(string! _path, int _pathlen)
{
inTable = true;
path = _path;
pathlen = _pathlen;
acl = Acl.nullAcl;
iref = null;
chain = null;
}
// this constructor for regular entries
internal HEntry(string! _path, int _pathlen, Acl _acl)
{
inTable = true;
path = _path;
pathlen = _pathlen;
acl = _acl;
iref = null;
chain = null;
}
}
private const int DefaultHTSize = 64;
private int htSize = DefaultHTSize;
private int htEntries = 0;
private HEntry[]! ht = new HEntry[DefaultHTSize];
[NotDelayed]
public SdsAclImpl()
{
// add null default rule
HtInsert(new HEntry("/", 1, Acl.nullAcl));
}
// This method gets a ref for fetching the effective access control pattern
public string GetEffective(string! path, out IEffectiveAclRef aclRef)
{
HEntry h = null;
aclRef = null;
if (path[0] != '/') {
DebugStub.Print("SdsAcl: invalid get acl\n");
return null;
}
lock (this) {
// first look for exact match
h = HtFind(path, path.Length);
if (h != null && h.acl.Node != null) {
aclRef = h;
return h.acl.Node;
}
// from here, we're looking for an inheritance
int pathlen = 1;
h = HtFind(path, 1);
// h should be non-null here because we guarantee a non-null root
if (h == null) {
DebugStub.Print("SdsAcl: missing root acl\n");
return null;
}
while (true) {
HEntry hh;
pathlen = path.IndexOf('/', pathlen+1);
if (pathlen < 0)
break;
hh = HtFind(path, pathlen);
if (hh != null) {
if (hh.acl.Node != null || hh.acl.Descendant != null) {
h = hh;
}
}
}
// return the Node acl pattern if the Descendant one is null or we have an exact match
if (h.iref == null) {
h.iref = new AclRef(h);
}
aclRef = h.iref;
// we assert that any AclRef currently in the table is valid
string res;
aclRef.GetCached(out res);
return res;
}
}
// This method gets the ACL stored for the designated path (e.g. not inherited)
public Acl Get(string! path)
{
if (path[0] != '/') {
DebugStub.Print("SdsAcl: invalid get acl\n");
return Acl.nullAcl;
}
lock (this) {
HEntry h = null;
h = HtFind(path, path.Length);
if (h == null)
return Acl.nullAcl;
return h.acl;
}
}
// This method sets an ACL for a path and the paths that inherit from it.
public void Set(string! path, Acl acl)
{
int pathlen = path.Length;
if (path[0] != '/') {
DebugStub.Print("SdsAcl: invalid set acl");
return;
}
lock (this) {
HEntry h = new HEntry(path, pathlen, acl);
h = HtInsert(h);
while (true) {
if (h != null) {
if (pathlen == 1 || h.acl.Node != null || h.acl.Descendant != null) {
// invalidate the first non-placeholder, or the root if none
Invalidate(h);
break;
}
}
if (pathlen <= 0) {
DebugStub.Print("SdsAcl: missing root acl\n");
break;
}
// there should always be some non-placeholder ancestor
pathlen = path.LastIndexOf('/', pathlen-1);
if (pathlen == 0)
pathlen++; // special case to find root
h = HtFind(path, pathlen);
if (h == null) {
// previous was null, add a placeholder
HtInsert(new HEntry(path, pathlen));
}
}
}
}
// This method clears an ACL associated with a path;
public void Clear(string! path)
{
if (path[0] != '/' || path.Length == 1) {
DebugStub.Print("SdsAcl: invalid clear acl\n");
return;
}
lock (this) {
HEntry h = HtDelete(path, path.Length);
if (h != null)
Invalidate(h);
}
}
internal void Invalidate(HEntry h)
{
if (h != null) {
if (h.iref != null) {
h.iref.Invalidate();
}
}
}
internal HEntry HtFind(string! path, int pathlen)
{
uint hash = Hash(path, pathlen);
HEntry h = ht[hash];
while (h != null && !Comp(h, path, pathlen))
h = h.chain;
return h;
}
internal HEntry HtInsert(HEntry! h)
{
uint hash = Hash(h.path, h.pathlen);
HEntry prev = null;
HEntry hh = ht[hash];
while (hh != null) {
if (Comp(hh, h.path, h.pathlen)) {
if (prev != null)
prev.chain = hh.chain;
else
ht[hash] = hh.chain;
hh.chain = null;
htEntries--;
break;
}
prev = hh;
hh = hh.chain;
}
h.chain = ht[hash];
ht[hash] = h;
htEntries++;
if (hh != null)
hh.inTable = false;
return hh;
}
internal HEntry HtDelete(string! path, int pathlen)
{
uint hash = Hash(path, pathlen);
HEntry prev = null;
HEntry h = ht[hash];
while (h != null) {
if (Comp(h, path, pathlen)) {
if (prev != null)
prev.chain = h.chain;
else
ht[hash] = h.chain;
h.chain = null;
htEntries--; // note that pathlen of "/" is zero
h.inTable = false;
return h;
}
prev = h;
h = h.chain;
}
return null;
}
internal uint Hash(string! path, int pathlen)
{
uint hash = 5381;
int i = 0;
while (i != pathlen) {
hash = ((hash << 5) + hash) ^ Char.ToLower(path[i++]);
}
return hash % (uint) htSize;
}
internal static bool Comp(HEntry! h, string! path, int pathlen)
{
if (pathlen != h.pathlen)
return false;
int i = 0;
while (i != pathlen) {
if (Char.ToLower(path[i]) != Char.ToLower(h.path[i]))
return false;
i++;
}
return true;
}
}
}