Files
WPinternals/DiscUtils/Ntfs/SecurityDescriptors.cs
T
2018-10-25 22:35:49 +02:00

384 lines
14 KiB
C#

//
// Copyright (c) 2008-2011, Kenneth Bell
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.
//
namespace DiscUtils.Ntfs
{
using System;
using System.Globalization;
using System.IO;
using System.Security.AccessControl;
internal sealed class SecurityDescriptors : IDiagnosticTraceable
{
// File consists of pairs of duplicate blocks (one after the other), providing
// redundancy. When a pair is full, the next pair is used.
private const int BlockSize = 0x40000;
private File _file;
private IndexView<HashIndexKey, HashIndexData> _hashIndex;
private IndexView<IdIndexKey, IdIndexData> _idIndex;
private uint _nextId;
private long _nextSpace;
public SecurityDescriptors(File file)
{
_file = file;
_hashIndex = new IndexView<HashIndexKey, HashIndexData>(file.GetIndex("$SDH"));
_idIndex = new IndexView<IdIndexKey, IdIndexData>(file.GetIndex("$SII"));
foreach (var entry in _idIndex.Entries)
{
if (entry.Key.Id > _nextId)
{
_nextId = entry.Key.Id;
}
long end = entry.Value.SdsOffset + entry.Value.SdsLength;
if (end > _nextSpace)
{
_nextSpace = end;
}
}
if (_nextId == 0)
{
_nextId = 256;
}
else
{
_nextId++;
}
_nextSpace = Utilities.RoundUp(_nextSpace, 16);
}
public static SecurityDescriptors Initialize(File file)
{
file.CreateIndex("$SDH", (AttributeType)0, AttributeCollationRule.SecurityHash);
file.CreateIndex("$SII", (AttributeType)0, AttributeCollationRule.UnsignedLong);
file.CreateStream(AttributeType.Data, "$SDS");
return new SecurityDescriptors(file);
}
public RawSecurityDescriptor GetDescriptorById(uint id)
{
IdIndexData data;
if (_idIndex.TryGetValue(new IdIndexKey(id), out data))
{
return ReadDescriptor(data).Descriptor;
}
return null;
}
public uint AddDescriptor(RawSecurityDescriptor newDescriptor)
{
// Search to see if this is a known descriptor
SecurityDescriptor newDescObj = new SecurityDescriptor(newDescriptor);
uint newHash = newDescObj.CalcHash();
byte[] newByteForm = new byte[newDescObj.Size];
newDescObj.WriteTo(newByteForm, 0);
foreach (var entry in _hashIndex.FindAll(new HashFinder(newHash)))
{
SecurityDescriptor stored = ReadDescriptor(entry.Value);
byte[] storedByteForm = new byte[stored.Size];
stored.WriteTo(storedByteForm, 0);
if (Utilities.AreEqual(newByteForm, storedByteForm))
{
return entry.Value.Id;
}
}
long offset = _nextSpace;
// Write the new descriptor to the end of the existing descriptors
SecurityDescriptorRecord record = new SecurityDescriptorRecord();
record.SecurityDescriptor = newByteForm;
record.Hash = newHash;
record.Id = _nextId;
// If we'd overflow into our duplicate block, skip over it to the
// start of the next block
if (((offset + record.Size) / BlockSize) % 2 == 1)
{
_nextSpace = Utilities.RoundUp(offset, BlockSize * 2);
offset = _nextSpace;
}
record.OffsetInFile = offset;
byte[] buffer = new byte[record.Size];
record.WriteTo(buffer, 0);
using (Stream s = _file.OpenStream(AttributeType.Data, "$SDS", FileAccess.ReadWrite))
{
s.Position = _nextSpace;
s.Write(buffer, 0, buffer.Length);
s.Position = BlockSize + _nextSpace;
s.Write(buffer, 0, buffer.Length);
}
// Make the next descriptor land at the end of this one
_nextSpace = Utilities.RoundUp(_nextSpace + buffer.Length, 16);
_nextId++;
// Update the indexes
HashIndexData hashIndexData = new HashIndexData();
hashIndexData.Hash = record.Hash;
hashIndexData.Id = record.Id;
hashIndexData.SdsOffset = record.OffsetInFile;
hashIndexData.SdsLength = (int)record.EntrySize;
HashIndexKey hashIndexKey = new HashIndexKey();
hashIndexKey.Hash = record.Hash;
hashIndexKey.Id = record.Id;
_hashIndex[hashIndexKey] = hashIndexData;
IdIndexData idIndexData = new IdIndexData();
idIndexData.Hash = record.Hash;
idIndexData.Id = record.Id;
idIndexData.SdsOffset = record.OffsetInFile;
idIndexData.SdsLength = (int)record.EntrySize;
IdIndexKey idIndexKey = new IdIndexKey();
idIndexKey.Id = record.Id;
_idIndex[idIndexKey] = idIndexData;
_file.UpdateRecordInMft();
return record.Id;
}
public void Dump(TextWriter writer, string indent)
{
writer.WriteLine(indent + "SECURITY DESCRIPTORS");
using (Stream s = _file.OpenStream(AttributeType.Data, "$SDS", FileAccess.Read))
{
byte[] buffer = Utilities.ReadFully(s, (int)s.Length);
foreach (var entry in _idIndex.Entries)
{
int pos = (int)entry.Value.SdsOffset;
SecurityDescriptorRecord rec = new SecurityDescriptorRecord();
if (!rec.Read(buffer, pos))
{
break;
}
string secDescStr = "--unknown--";
if (rec.SecurityDescriptor[0] != 0)
{
RawSecurityDescriptor sd = new RawSecurityDescriptor(rec.SecurityDescriptor, 0);
secDescStr = sd.GetSddlForm(AccessControlSections.All);
}
writer.WriteLine(indent + " SECURITY DESCRIPTOR RECORD");
writer.WriteLine(indent + " Hash: " + rec.Hash);
writer.WriteLine(indent + " Id: " + rec.Id);
writer.WriteLine(indent + " File Offset: " + rec.OffsetInFile);
writer.WriteLine(indent + " Size: " + rec.EntrySize);
writer.WriteLine(indent + " Value: " + secDescStr);
}
}
}
private SecurityDescriptor ReadDescriptor(IndexData data)
{
using (Stream s = _file.OpenStream(AttributeType.Data, "$SDS", FileAccess.Read))
{
s.Position = data.SdsOffset;
byte[] buffer = Utilities.ReadFully(s, data.SdsLength);
SecurityDescriptorRecord record = new SecurityDescriptorRecord();
record.Read(buffer, 0);
return new SecurityDescriptor(new RawSecurityDescriptor(record.SecurityDescriptor, 0));
}
}
internal abstract class IndexData
{
public uint Hash;
public uint Id;
public long SdsOffset;
public int SdsLength;
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[Data-Hash:{0},Id:{1},SdsOffset:{2},SdsLength:{3}]", Hash, Id, SdsOffset, SdsLength);
}
}
internal sealed class HashIndexKey : IByteArraySerializable
{
public uint Hash;
public uint Id;
public int Size
{
get { return 8; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Hash = Utilities.ToUInt32LittleEndian(buffer, offset + 0);
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 4);
return 8;
}
public void WriteTo(byte[] buffer, int offset)
{
Utilities.WriteBytesLittleEndian(Hash, buffer, offset + 0);
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 4);
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[Key-Hash:{0},Id:{1}]", Hash, Id);
}
}
internal sealed class HashIndexData : IndexData, IByteArraySerializable
{
public int Size
{
get { return 0x14; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Hash = Utilities.ToUInt32LittleEndian(buffer, offset + 0x00);
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 0x04);
SdsOffset = Utilities.ToInt64LittleEndian(buffer, offset + 0x08);
SdsLength = Utilities.ToInt32LittleEndian(buffer, offset + 0x10);
return 0x14;
}
public void WriteTo(byte[] buffer, int offset)
{
Utilities.WriteBytesLittleEndian(Hash, buffer, offset + 0x00);
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 0x04);
Utilities.WriteBytesLittleEndian(SdsOffset, buffer, offset + 0x08);
Utilities.WriteBytesLittleEndian(SdsLength, buffer, offset + 0x10);
////Array.Copy(new byte[] { (byte)'I', 0, (byte)'I', 0 }, 0, buffer, offset + 0x14, 4);
}
}
internal sealed class IdIndexKey : IByteArraySerializable
{
public uint Id;
public IdIndexKey()
{
}
public IdIndexKey(uint id)
{
Id = id;
}
public int Size
{
get { return 4; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 0);
return 4;
}
public void WriteTo(byte[] buffer, int offset)
{
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 0);
}
public override string ToString()
{
return string.Format(CultureInfo.InvariantCulture, "[Key-Id:{0}]", Id);
}
}
internal sealed class IdIndexData : IndexData, IByteArraySerializable
{
public int Size
{
get { return 0x14; }
}
public int ReadFrom(byte[] buffer, int offset)
{
Hash = Utilities.ToUInt32LittleEndian(buffer, offset + 0x00);
Id = Utilities.ToUInt32LittleEndian(buffer, offset + 0x04);
SdsOffset = Utilities.ToInt64LittleEndian(buffer, offset + 0x08);
SdsLength = Utilities.ToInt32LittleEndian(buffer, offset + 0x10);
return 0x14;
}
public void WriteTo(byte[] buffer, int offset)
{
Utilities.WriteBytesLittleEndian(Hash, buffer, offset + 0x00);
Utilities.WriteBytesLittleEndian(Id, buffer, offset + 0x04);
Utilities.WriteBytesLittleEndian(SdsOffset, buffer, offset + 0x08);
Utilities.WriteBytesLittleEndian(SdsLength, buffer, offset + 0x10);
}
}
private class HashFinder : IComparable<HashIndexKey>
{
private uint _toMatch;
public HashFinder(uint toMatch)
{
_toMatch = toMatch;
}
public int CompareTo(uint otherHash)
{
if (_toMatch < otherHash)
{
return -1;
}
else if (_toMatch > otherHash)
{
return 1;
}
return 0;
}
public int CompareTo(HashIndexKey other)
{
return CompareTo(other.Hash);
}
}
}
}