Using .NET v1.0 and 1.1 you have several options, note that whatever method
you use it is not a trivial task, simply because DACL programming is hard as
you need to know exactly how DACL's work.
Listed in order of simplicity/availability....
1- Use the System.Management classes and the WMI classes.
Here is a complete sample illustrating how to list NTFS security objects and
adding an ACE to a DACL:
using System;
using System.Management;
using System.Collections;
// Access mask (see AccessMask property)
[Flags]
enum Mask : uint
{
FileReadData = 0x00000001,
FileWriteData = 0x00000002,
FileAppendData = 0x00000004,
FileReadEA = 0x00000008,
FileWriteEA = 0x00000010,
FileExecute = 0x00000020,
FileDeleteChild = 0x00000040,
FileReadAttributes = 0x00000080,
FileWriteAttributes= 0x00000100,
Delete = 0x00010000,
ReadControl = 0x00020000,
WriteDac = 0x00040000,
WriteOwner = 0x00080000,
Synchronize = 0x00100000,
AccessSystemSecurity = 0x01000000,
MaximumAllowed = 0x02000000,
GenericAll = 0x10000000,
GenericExecute= 0x20000000,
GenericWrite = 0x40000000,
GenericRead = 0x80000000
}
[Flags]
enum AceFlags : uint
{
NonInheritAce = 0,
ObjectInheritAce = 1,
ContainerInheritAce = 2,
NoPropagateInheritAce = 4,
InheritOnlyAce = 8,
InheritedAce = 16
}
[Flags]
enum AceType : uint
{
AccessAllowed = 0,
AccessDenied = 1,
Audit = 2
}
public class FileObjectSecurity : IDisposable
{
ManagementPath path;
ManagementObject lfs;
ManagementBaseObject Descriptor; // Security descriptor for this object
ManagementBaseObject[] dacl;
bool disposed;
public FileObjectSecurity(string FileObject)
{
path = new ManagementPath();
path.Server = "."; // server name (. for local machine)
path.NamespacePath = @"root\cimv2";
path.RelativePath = @"Win32_LogicalFileSecuritySetting.Path=" + "'" +
FileObject + "'";
lfs = new ManagementObject(path);
}
// Get array of ACE's.
// Dump ACE's to console if DumpToConsole = true
public void GetACEs(bool DumpToConsole)
{
GetObjectDacl();
if(DumpToConsole)
DumpDaclToConsole();
}
private void DumpDaclToConsole()
{
foreach(ManagementBaseObject mbo in this.dacl){
Console.WriteLine("-------------------------------------------------");
Console.WriteLine("{0:X} - {1} - {2}", mbo["AccessMask"],
Enum.Format(typeof(AceFlags), mbo["AceFlags"], "g") , mbo["AceType"]);
// Access allowed/denied ACE
if(Convert.ToInt32(mbo["AceType"]) == (int)AceType.AccessDenied)
Console.WriteLine("DENIED ACE TYPE");
else
Console.WriteLine("ALLOWED ACE TYPE");
// Dump trustees
ManagementBaseObject Trustee = ((ManagementBaseObject)(mbo["Trustee"]));
Console.WriteLine("Name: {0} - Domain: {1} - SID {2}\n",
Trustee.Properties["Name"].Value,
Trustee.Properties["Domain"].Value,
Trustee.Properties["SIDString"].Value);
// Dump ACE mask in readable form
UInt32 mask = (UInt32)mbo["AccessMask"];
Console.WriteLine(System.Enum.Format(typeof(Mask), mask, "g"));
}
}
private bool WriteSecurityDescriptor(ManagementBaseObject[] Dacl)
{
ManagementBaseObject inParams =
lfs.GetMethodParameters("SetSecurityDescriptor");
Descriptor.Properties["Dacl"].Value = Dacl;
inParams["Descriptor"] = Descriptor;
ManagementBaseObject ret = lfs.InvokeMethod("SetSecurityDescriptor",
inParams, null);
if ((Convert.ToUInt32(ret.Properties["ReturnValue"].Value) == 0))
return true;
else
return false;
}
// Add an ACE to the DACL
public bool AddACEToDacl(string TrusteeName, Enum Mask)
{
Array newDACL;
// Copy the non-inherit aces
ArrayList aceList = new ArrayList();
foreach(ManagementBaseObject entry in this.dacl)
{
if ((uint)entry["AceFlags"] == (uint)AceFlags.NonInheritAce)
{
aceList.Add(entry);
}
}
// Creates and initializes a one-dimensional Array of type
ManagementBaseObject
// with space for one extra direct ACE.
newDACL=Array.CreateInstance( typeof(ManagementBaseObject), aceList.Count
+ 1);
// Copy AL to Array
aceList.CopyTo(newDACL);
ManagementBaseObject trustee = null;
ManagementBaseObject ace = null;
// Initialize new Trustee (here a local accoun as sample)
try {
trustee = new ManagementClass( @"Win32_Trustee" );
// trustee.Properties["Domain"].Value = ""; // if domain other then local
machine
trustee.Properties["Name"].Value = TrusteeName;
}
// catch if non existing trustee
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
}
try {
ace = new ManagementClass( @"Win32_ACE" );
ace.Properties["AccessMask"].Value = Mask;
ace.Properties["AceFlags"].Value = AceFlags.NoPropagateInheritAce;
ace.Properties["AceType"].Value = AceType.AccessAllowed;
ace.Properties["Trustee"].Value = trustee;
}
catch (Exception e)
{
Console.WriteLine(e.Message);
return false;
}
Console.WriteLine(newDACL.Length);
newDACL.SetValue(ace, newDACL.Length - 1);
return WriteSecurityDescriptor((ManagementBaseObject[])newDACL);
}
private void GetObjectDacl()
{
// Get the security descriptor for this object
bool EnablePrivileges = lfs.Scope.Options.EnablePrivileges;
lfs.Scope.Options.EnablePrivileges = true;
ManagementBaseObject outParams =
lfs.InvokeMethod("GetSecurityDescriptor", null, null);
if (((uint)(outParams.Properties["ReturnValue"].Value)) == 0) // if
success
{
Descriptor =
((ManagementBaseObject)(outParams.Properties["Descriptor"].Value));
//The DACL is an array of Win32_ACE objects.
dacl = ((ManagementBaseObject[])(Descriptor.Properties["Dacl"].Value));
}
return;
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if(!this.disposed)
{
// Release ManagementObject.
lfs.Dispose();
}
disposed = true;
}
~FileObjectSecurity()
{
Dispose(false);
// BUG BUG, finalizer should never be called. Object should be Disposed
by client.
// Failing to call dispose, will throw in debug build
#if DEBUG
throw new Exception("Finalizer called on disposable object");
#endif
}
}
class Tester {
public static void Main()
{
// Create FileObjectSecurity passing the file object (filepath) to the
ctor.
// Beware the double backslashes !!!!
using (FileObjectSecurity fos = new
FileObjectSecurity(@"c:\\somedir\\somefile"))
{
fos.GetACEs(false); // Get DACL no console dump
fos.AddACEToDacl("Users", Mask.FileWriteData);
fos.GetACEs(true); // Get DACL and dump to console
}
}
}
2- Use System.DirectoryServices and ADSI security client (included in
activeds.dll on XP and higher).
Using this method is similar to 1, but it requires a reference to
activeds.tld and COM interop.
3- Use MC++ and IJW interop with the NTFS security API's.
4- Use PInvoke interop from C# to call the NTFS security API's.
5- Wait for .NET v2.0.
Willy.