109 lines
3.9 KiB
C#
109 lines
3.9 KiB
C#
namespace DSInternals.DataStore
|
|
{
|
|
using DSInternals.Common;
|
|
using DSInternals.Common.Exceptions;
|
|
using Microsoft.Database.Isam;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Security.AccessControl;
|
|
using System.Security.Cryptography;
|
|
|
|
public class SecurityDescriptorRersolver : IDisposable
|
|
{
|
|
private const string SdIdCol = "sd_id";
|
|
private const string SdValueCol = "sd_value";
|
|
private const string SdHashCol = "sd_hash";
|
|
private const string SdRefCountCol = "sd_refcount";
|
|
private const string SdIndex = "sd_id_index";
|
|
private const string SdHashIndex = "sd_hash_index";
|
|
private const int RootSecurityDescriptorOffset = sizeof(int);
|
|
|
|
private Cursor cursor;
|
|
|
|
public SecurityDescriptorRersolver(IsamDatabase database)
|
|
{
|
|
this.cursor = database.OpenCursor(ADConstants.SecurityDescriptorTableName);
|
|
}
|
|
|
|
public RawSecurityDescriptor GetDescriptor(long id)
|
|
{
|
|
this.cursor.CurrentIndex = SdIndex;
|
|
bool found = this.cursor.GotoKey(Key.Compose(id));
|
|
if (!found)
|
|
{
|
|
throw new DirectoryObjectNotFoundException(id);
|
|
}
|
|
var binaryForm = this.cursor.RetrieveColumnAsByteArray(SdValueCol);
|
|
// Strip the root SD prefix, which is 0x0F000000
|
|
int sdOffset = (id == ADConstants.RootSecurityDescriptorId) ? RootSecurityDescriptorOffset : 0;
|
|
return new RawSecurityDescriptor(binaryForm, sdOffset);
|
|
}
|
|
|
|
public IEnumerable<long> FindDescriptor(GenericSecurityDescriptor securityDescriptor)
|
|
{
|
|
byte[] sdHash = ComputeHash(securityDescriptor);
|
|
return this.FindDescriptorHash(sdHash);
|
|
}
|
|
|
|
public IEnumerable<long> FindDescriptor(string securityDescriptor)
|
|
{
|
|
byte[] sdHash = ComputeHash(securityDescriptor);
|
|
return this.FindDescriptorHash(sdHash);
|
|
}
|
|
|
|
public IEnumerable<long> FindDescriptorHash(byte[] sdHash)
|
|
{
|
|
Validator.AssertNotNull(sdHash, "sdHash");
|
|
this.cursor.CurrentIndex = SdHashIndex;
|
|
this.cursor.FindRecords(MatchCriteria.EqualTo, Key.Compose(sdHash));
|
|
while(cursor.MoveNext())
|
|
{
|
|
long id = this.cursor.RetrieveColumnAsLong(SdIdCol).Value;
|
|
yield return id;
|
|
}
|
|
}
|
|
|
|
public void Dispose()
|
|
{
|
|
this.Dispose(true);
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
public static byte[] ComputeHash(GenericSecurityDescriptor securityDescriptor)
|
|
{
|
|
Validator.AssertNotNull(securityDescriptor, "securityDescriptor");
|
|
|
|
// Convert to binary form. We have to use double conversion, because .NET returns the SD in different order than Win32 API used by AD.
|
|
string stringSecurityDescriptor = securityDescriptor.GetSddlForm(AccessControlSections.All);
|
|
return ComputeHash(stringSecurityDescriptor);
|
|
}
|
|
|
|
public static byte[] ComputeHash(string securityDescriptor)
|
|
{
|
|
Validator.AssertNotNullOrWhiteSpace(securityDescriptor, "securityDescriptor");
|
|
|
|
return ComputeHash(securityDescriptor.SddlToBinary());
|
|
}
|
|
|
|
public static byte[] ComputeHash(byte[] securityDescriptor)
|
|
{
|
|
Validator.AssertNotNull(securityDescriptor, "securityDescriptor");
|
|
|
|
using (var hashFunction = MD5.Create())
|
|
{
|
|
// TODO: Cache the hash function for performance reasons
|
|
return hashFunction.ComputeHash(securityDescriptor);
|
|
}
|
|
}
|
|
|
|
protected virtual void Dispose(bool disposing)
|
|
{
|
|
if (disposing && this.cursor != null)
|
|
{
|
|
this.cursor.Dispose();
|
|
this.cursor = null;
|
|
}
|
|
}
|
|
}
|
|
}
|