335 lines
12 KiB
Plaintext
335 lines
12 KiB
Plaintext
|
///////////////////////////////////////////////////////////////////////////////
|
||
|
//
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
//
|
||
|
|
||
|
namespace Microsoft.Singularity.Security
|
||
|
{
|
||
|
using System;
|
||
|
using System.Threading;
|
||
|
using System.Collections;
|
||
|
using System.Text;
|
||
|
|
||
|
using Microsoft.Singularity.Loader;
|
||
|
using Microsoft.Singularity.Xml;
|
||
|
|
||
|
using Microsoft.Singularity.Io;
|
||
|
|
||
|
public class PrincipalImpl
|
||
|
{
|
||
|
private const string KernelName = "kernel.localhost";
|
||
|
private const string UnknownName = "unknown";
|
||
|
private const string UnknownPublisher = "unknown";
|
||
|
private const string DefaultNameContext = "localhost";
|
||
|
private const string TruncateHistoryPrivilege = "$truncate-history-privilege.localhost";
|
||
|
|
||
|
// security tags in config XML
|
||
|
const string AuthPolicyXmlTag = "authpolicy";
|
||
|
const string SubexprXmlTag = "subexpr";
|
||
|
const string SubexprNameXmlAttribute = "name";
|
||
|
const string SubexprExpansionXmlAttribute = "expansion";
|
||
|
const string AuthorityXmlTag = "authority";
|
||
|
const string AuthorityNameXmlAttribute = "name";
|
||
|
const string AuthorityAclXmlAttribute = "acl";
|
||
|
|
||
|
private static Hashtable! principalHT; // map from principal Val (ulong) to PrincipalT
|
||
|
private static Hashtable! exprHT; // map from subexpr name to description (both strings)
|
||
|
private static Hashtable! privHT; // map from privilege name to ArrayList of PrincipalT
|
||
|
private static Hashtable! authorityHT; // map from authority name to acl expansion (both strings)
|
||
|
private static Hashtable! publisherHT; // map from publisher name to PrincipalT
|
||
|
|
||
|
private const char CharInvocation = '+';
|
||
|
private const char CharRole = '@';
|
||
|
private const string NullName = "";
|
||
|
|
||
|
private const ulong KernelIdNum = 1;
|
||
|
private static ulong nextIdVal = KernelIdNum + (ulong)1;
|
||
|
private static SecurityDiagnostics! secDiag;
|
||
|
private static AclCore! aclCore;
|
||
|
|
||
|
|
||
|
internal class PrincipalT {
|
||
|
internal string! name;
|
||
|
internal string manifestName;
|
||
|
internal string[] privs;
|
||
|
internal ulong val;
|
||
|
internal PrincipalT publisherT;
|
||
|
internal ArrayList principalHashes;
|
||
|
|
||
|
internal PrincipalT(string! _name, string _maniName, string[] _privs, PrincipalT _publisherT)
|
||
|
{
|
||
|
name = _name;
|
||
|
manifestName = _maniName;
|
||
|
privs = _privs;
|
||
|
val = 0L;
|
||
|
publisherT = _publisherT;
|
||
|
principalHashes = null;
|
||
|
}
|
||
|
|
||
|
internal PrincipalT(string! _name, string _maniName, string[] _privs, PrincipalT _publisherT, ArrayList _principalHashes)
|
||
|
{
|
||
|
name = _name;
|
||
|
manifestName = _maniName;
|
||
|
privs = _privs;
|
||
|
val = 0L;
|
||
|
publisherT = _publisherT;
|
||
|
principalHashes = _principalHashes;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static void Initialize(XmlNode! config)
|
||
|
{
|
||
|
// kernel publisher T is currently "unknown". fix this (maybe)
|
||
|
// one way to fix it is to have kernel manifest here, but that
|
||
|
// isn't currently possible.
|
||
|
principalHT = new Hashtable();
|
||
|
exprHT = new Hashtable();
|
||
|
privHT = new Hashtable();
|
||
|
authorityHT = new Hashtable();
|
||
|
publisherHT = new Hashtable();
|
||
|
secDiag = new SecurityDiagnostics();
|
||
|
aclCore = new AclCore("PrincipalImpl", null);
|
||
|
InstallPolicy(config);
|
||
|
principalHT.Add(KernelIdNum,
|
||
|
new PrincipalT(KernelName, null, null, GetPublisherT(UnknownPublisher)));
|
||
|
}
|
||
|
|
||
|
|
||
|
public static Principal Self()
|
||
|
{
|
||
|
return new Principal(KernelIdNum);
|
||
|
}
|
||
|
|
||
|
public static Principal MakePrincipal(ulong id)
|
||
|
{
|
||
|
return new Principal(id);
|
||
|
}
|
||
|
|
||
|
public static string! GetPrincipalName(Principal pr)
|
||
|
{
|
||
|
PrincipalT pT = (PrincipalT) principalHT[pr.Val];
|
||
|
if (pT == null)
|
||
|
return NullName;
|
||
|
return pT.name;
|
||
|
}
|
||
|
|
||
|
public static ArrayList GetPrincipalHashes(Principal pr)
|
||
|
{
|
||
|
PrincipalT pT = (PrincipalT) principalHT[pr.Val];
|
||
|
if (pT == null)
|
||
|
return new ArrayList();
|
||
|
return pT.principalHashes;
|
||
|
}
|
||
|
|
||
|
public static Principal NewInvocation(Principal parent, Manifest! manifest, string role, IoMemory rawImage)
|
||
|
{
|
||
|
string manifestName = manifest.Name;
|
||
|
string publisher = manifest.Publisher;
|
||
|
string name, newname;
|
||
|
bool eraseHistory = false;
|
||
|
string[] appPrivs = manifest.Privileges;
|
||
|
ArrayList arrarr = null;
|
||
|
byte[] imageHash = null;
|
||
|
ArrayList newprincipalHashes = null;
|
||
|
|
||
|
if (manifestName == null)
|
||
|
manifestName = UnknownName;
|
||
|
|
||
|
if (publisher == null)
|
||
|
publisher = UnknownPublisher;
|
||
|
|
||
|
PrincipalT publisherT = GetPublisherT(publisher);
|
||
|
|
||
|
manifestName = String.Format("{0}.{1}", manifestName, publisher);
|
||
|
name = manifestName;
|
||
|
|
||
|
// Here we look for authority to grant privilege to the new app
|
||
|
// Currently, we don't revisit this decision, although in future we might
|
||
|
if (appPrivs != null) {
|
||
|
for (int i=0; i< appPrivs.Length; i++) {
|
||
|
string s = (!)appPrivs[i];
|
||
|
string acl = (string) authorityHT[s];
|
||
|
// Would call the normal CheckAccess entry point here, but it can't equate
|
||
|
// the Principal type in this context, and that in the library. Grumble!
|
||
|
if (acl != null && aclCore.CheckAccess(acl, null,
|
||
|
publisherT.val, publisherT.name)) {
|
||
|
ArrayList arr = (ArrayList) privHT[s];
|
||
|
if (arr == null) {
|
||
|
lock (privHT) {
|
||
|
arr = new ArrayList();
|
||
|
privHT[s] = arr;
|
||
|
}
|
||
|
}
|
||
|
// make an array of ArrayLists which we'll add a principal handle to below
|
||
|
if (arrarr == null)
|
||
|
arrarr = new ArrayList();
|
||
|
arrarr.Add(arr);
|
||
|
if (s == TruncateHistoryPrivilege) {
|
||
|
eraseHistory = true;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (eraseHistory || (parent.Val == KernelIdNum)) {
|
||
|
// ignore role if eraseHistory or kernel parent
|
||
|
newname = name;
|
||
|
if (imageHash != null) {
|
||
|
newprincipalHashes = new ArrayList();
|
||
|
newprincipalHashes.Add(imageHash);
|
||
|
}
|
||
|
} else {
|
||
|
string parentName = parent.GetName();
|
||
|
if (parentName == NullName)
|
||
|
throw new Exception("NewInvocation with null parent");
|
||
|
int newlength = parentName.Length + name.Length + 2;
|
||
|
if (role != null)
|
||
|
newlength += (role.Length + 1);
|
||
|
StringBuilder sb = new StringBuilder(parentName, newlength);
|
||
|
if (role != null) {
|
||
|
sb.Append(CharRole);
|
||
|
sb.Append(role);
|
||
|
}
|
||
|
sb.Append(CharInvocation);
|
||
|
sb.Append(name);
|
||
|
newname = sb.ToString();
|
||
|
|
||
|
if (imageHash != null) {
|
||
|
ArrayList parentHashes = parent.GetHashes();
|
||
|
newprincipalHashes = new ArrayList(parentHashes);
|
||
|
newprincipalHashes.Add(imageHash);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
PrincipalT pT = new PrincipalT(newname, manifestName, appPrivs, publisherT, newprincipalHashes);
|
||
|
if (arrarr != null) {
|
||
|
foreach (Object! obj in arrarr) {
|
||
|
ArrayList arr = (ArrayList) obj;
|
||
|
lock (arr) {
|
||
|
arr.Add(pT);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
lock (principalHT)
|
||
|
{
|
||
|
pT.val = nextIdVal++;
|
||
|
principalHT.Add(pT.val, pT);
|
||
|
}
|
||
|
return new Principal(pT.val);
|
||
|
}
|
||
|
|
||
|
public static void Dispose(Principal pr)
|
||
|
{
|
||
|
PrincipalT pT = null;
|
||
|
lock (principalHT)
|
||
|
{
|
||
|
pT = (PrincipalT) principalHT[pr.Val];
|
||
|
if (pT == null)
|
||
|
return;
|
||
|
principalHT.Remove(pr.Val);
|
||
|
}
|
||
|
if (pT.privs != null) {
|
||
|
for (int i=0; i<pT.privs.Length; i++) {
|
||
|
ArrayList arr = (ArrayList) privHT[(!)pT.privs[i]];
|
||
|
if (arr != null) {
|
||
|
lock (arr) {
|
||
|
arr.Remove(pT);
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
public static string ExpandAclIndirection(string! name)
|
||
|
{
|
||
|
if (name.Length == 0)
|
||
|
return null;
|
||
|
|
||
|
if (name.IndexOf('.') == -1)
|
||
|
name = String.Format("{0}.{1}", name, DefaultNameContext);
|
||
|
|
||
|
string res = (string) exprHT[name];
|
||
|
if (res != null)
|
||
|
return res;
|
||
|
|
||
|
ArrayList arr = (ArrayList) privHT[name];
|
||
|
if (arr == null)
|
||
|
return null;
|
||
|
|
||
|
StringBuilder sb = new StringBuilder();
|
||
|
lock (arr) {
|
||
|
sb.Append("(");
|
||
|
for (int i=0; i<arr.Count; i++) {
|
||
|
PrincipalT pT = (PrincipalT!) arr[i];
|
||
|
if (pT == null || pT.manifestName == null)
|
||
|
continue;
|
||
|
if (i != 0)
|
||
|
sb.Append('|');
|
||
|
sb.Append(pT.manifestName);
|
||
|
}
|
||
|
sb.Append(")");
|
||
|
}
|
||
|
return sb.ToString();
|
||
|
}
|
||
|
|
||
|
public static void RegisterAclCore(Object core)
|
||
|
{
|
||
|
secDiag.RegisterAclCore((AclCore!)core);
|
||
|
}
|
||
|
|
||
|
public static void Export()
|
||
|
{
|
||
|
secDiag.Start();
|
||
|
}
|
||
|
|
||
|
private static void InstallPolicy(XmlNode! config)
|
||
|
{
|
||
|
// get all group definitions to register from the xml
|
||
|
XmlNode policyConfig = config.GetChild(AuthPolicyXmlTag);
|
||
|
|
||
|
if (policyConfig == null) {
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
bool isAuthority = false;
|
||
|
foreach (XmlNode! elem in policyConfig.Children) {
|
||
|
string name;
|
||
|
string target;
|
||
|
if (elem.Name == AuthorityXmlTag) {
|
||
|
name = (!)elem.GetAttribute(AuthorityNameXmlAttribute, "");
|
||
|
target = (!)elem.GetAttribute(AuthorityAclXmlAttribute, "");
|
||
|
isAuthority = true;
|
||
|
} else if (elem.Name == SubexprXmlTag) {
|
||
|
name = (!)elem.GetAttribute(SubexprNameXmlAttribute, "");
|
||
|
target = (!)elem.GetAttribute(SubexprExpansionXmlAttribute, "");
|
||
|
} else
|
||
|
continue;
|
||
|
if (name.Length != 0 && target.Length != 0) {
|
||
|
if (name.IndexOf('.') == -1)
|
||
|
name = String.Format("{0}.{1}", name, DefaultNameContext);
|
||
|
if (isAuthority)
|
||
|
authorityHT[name] = target;
|
||
|
else
|
||
|
exprHT[name] = target;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
private static PrincipalT! GetPublisherT(string! publisher)
|
||
|
{
|
||
|
PrincipalT publisherT = (PrincipalT) publisherHT[publisher];
|
||
|
if (publisherT == null) {
|
||
|
publisherT = new PrincipalT(publisher, null, null, null);
|
||
|
lock (principalHT) {
|
||
|
publisherT.val = nextIdVal++;
|
||
|
principalHT.Add(publisherT.val, publisherT);
|
||
|
}
|
||
|
}
|
||
|
return publisherT;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
|