Enhanced support of downlevel DB schemas

This commit is contained in:
Michael Grafnetter 2018-07-14 11:13:31 +02:00
parent 7453ac8457
commit 1e5f7eef28
4 changed files with 160 additions and 97 deletions

View File

@ -1,7 +1,6 @@
namespace DSInternals.Common.Data
{
using DSInternals.Common.Cryptography;
using DSInternals.Common.Exceptions;
using System;
using System.Collections.Generic;
using System.Security.AccessControl;
@ -442,55 +441,49 @@
/// </summary>
protected void LoadRoamedCredentials(DirectoryObject dsObject)
{
try
// These attributes have been added in Windows Server 2008, so they might not be present on older DCs.
byte[] roamingTimeStamp;
dsObject.ReadAttribute(CommonDirectoryAttributes.PKIRoamingTimeStamp, out roamingTimeStamp);
if (roamingTimeStamp == null)
{
byte[] roamingTimeStamp;
dsObject.ReadAttribute(CommonDirectoryAttributes.PKIRoamingTimeStamp, out roamingTimeStamp);
if (roamingTimeStamp == null)
{
// This account does not have roamed credentials, so we skip their processing
return;
}
// The 16B of the value consist of two 8B actual time stamps.
long createdTimeStamp = BitConverter.ToInt64(roamingTimeStamp, 0);
long modifiedTimeStamp = BitConverter.ToInt64(roamingTimeStamp, sizeof(long));
this.RoamedCredentialsCreated = DateTime.FromFileTime(createdTimeStamp);
this.RoamedCredentialsModified = DateTime.FromFileTime(modifiedTimeStamp);
byte[][] masterKeyBlobs;
dsObject.ReadLinkedValues(CommonDirectoryAttributes.PKIDPAPIMasterKeys, out masterKeyBlobs);
byte[][] credentialBlobs;
dsObject.ReadLinkedValues(CommonDirectoryAttributes.PKIAccountCredentials, out credentialBlobs);
// Parse the blobs and combine them into one array.
var credentials = new List<RoamedCredential>();
if (masterKeyBlobs != null)
{
foreach (var blob in masterKeyBlobs)
{
credentials.Add(new RoamedCredential(blob, this.SamAccountName, this.Sid));
}
}
if(credentialBlobs != null)
{
foreach (var blob in credentialBlobs)
{
credentials.Add(new RoamedCredential(blob, this.SamAccountName, this.Sid));
}
}
this.RoamedCredentials = credentials.ToArray();
// This account does not have roamed credentials, so we skip their processing
return;
}
catch (SchemaAttributeNotFoundException)
// The 16B of the value consist of two 8B actual time stamps.
long createdTimeStamp = BitConverter.ToInt64(roamingTimeStamp, 0);
long modifiedTimeStamp = BitConverter.ToInt64(roamingTimeStamp, sizeof(long));
this.RoamedCredentialsCreated = DateTime.FromFileTime(createdTimeStamp);
this.RoamedCredentialsModified = DateTime.FromFileTime(modifiedTimeStamp);
byte[][] masterKeyBlobs;
dsObject.ReadLinkedValues(CommonDirectoryAttributes.PKIDPAPIMasterKeys, out masterKeyBlobs);
byte[][] credentialBlobs;
dsObject.ReadLinkedValues(CommonDirectoryAttributes.PKIAccountCredentials, out credentialBlobs);
// Parse the blobs and combine them into one array.
var credentials = new List<RoamedCredential>();
if (masterKeyBlobs != null)
{
// These attributes have been added in Windows Server 2008, so they might not be present on older DCs.
foreach (var blob in masterKeyBlobs)
{
credentials.Add(new RoamedCredential(blob, this.SamAccountName, this.Sid));
}
}
if(credentialBlobs != null)
{
foreach (var blob in credentialBlobs)
{
credentials.Add(new RoamedCredential(blob, this.SamAccountName, this.Sid));
}
}
this.RoamedCredentials = credentials.ToArray();
}
/// <summary>
@ -498,28 +491,22 @@
/// </summary>
protected void LoadKeyCredentials(DirectoryObject dsObject)
{
try
// This attribute has been added in Windows Server 2016, so it might not be present on older DCs.
byte[][] keyCredentialBlobs;
dsObject.ReadLinkedValues(CommonDirectoryAttributes.KeyCredentialLink, out keyCredentialBlobs);
// Parse the blobs and combine them into one array.
var credentials = new List<KeyCredential>();
if (keyCredentialBlobs != null)
{
byte[][] keyCredentialBlobs;
dsObject.ReadLinkedValues(CommonDirectoryAttributes.KeyCredentialLink, out keyCredentialBlobs);
// Parse the blobs and combine them into one array.
var credentials = new List<KeyCredential>();
if (keyCredentialBlobs != null)
foreach (var blob in keyCredentialBlobs)
{
foreach (var blob in keyCredentialBlobs)
{
credentials.Add(new KeyCredential(blob));
}
credentials.Add(new KeyCredential(blob));
}
}
this.KeyCredentials = credentials.ToArray();
}
catch (SchemaAttributeNotFoundException)
{
// This attribute has been added in Windows Server 2016, so it might not be present on older DCs.
}
this.KeyCredentials = credentials.ToArray();
}
}
}

View File

@ -69,7 +69,6 @@
Columnid columnId = this.context.Schema.FindColumnId(CommonDirectoryAttributes.SIDHistory);
bool hasChanged = this.cursor.AddMultiValue(columnId, valuesToAdd);
return hasChanged;
}
public void Delete()
@ -85,52 +84,110 @@
public override bool HasAttribute(string name)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
long columnSize = cursor.Record.SizeOf(columnId);
return columnSize > 0;
if(this.context.Schema.ContainsAttribute(name))
{
// Read the appropriate column and check if it has a value
Columnid columnId = this.context.Schema.FindColumnId(name);
long columnSize = cursor.Record.SizeOf(columnId);
return columnSize > 0;
}
else
{
// The schema does not even contain this attribute, so the object cannot have it.
return false;
}
}
public override void ReadAttribute(string name, out byte[] value)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsByteArray(columnId);
if(this.context.Schema.ContainsAttribute(name))
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsByteArray(columnId);
}
else
{
value = null;
}
}
public override void ReadAttribute(string name, out byte[][] value)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsMultiByteArray(columnId);
if (this.context.Schema.ContainsAttribute(name))
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsMultiByteArray(columnId);
}
else
{
value = null;
}
}
public override void ReadAttribute(string name, out int? value)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsInt(columnId);
if (this.context.Schema.ContainsAttribute(name))
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsInt(columnId);
}
else
{
value = null;
}
}
public override void ReadAttribute(string name, out string value)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsString(columnId);
if (this.context.Schema.ContainsAttribute(name))
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsString(columnId);
}
else
{
value = null;
}
}
public override void ReadAttribute(string name, out string[] values)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
values = this.cursor.RetrieveColumnAsStringArray(columnId);
if (this.context.Schema.ContainsAttribute(name))
{
Columnid columnId = this.context.Schema.FindColumnId(name);
values = this.cursor.RetrieveColumnAsStringArray(columnId);
}
else
{
values = null;
}
}
public override void ReadAttribute(string name, out long? value)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsLong(columnId);
if (this.context.Schema.ContainsAttribute(name))
{
Columnid columnId = this.context.Schema.FindColumnId(name);
value = this.cursor.RetrieveColumnAsLong(columnId);
}
else
{
value = null;
}
}
public override void ReadAttribute(string name, out DistinguishedName value)
{
Columnid columnId = this.context.Schema.FindColumnId(name);
var dnt = this.cursor.RetrieveColumnAsDNTag(columnId);
value = this.context.DistinguishedNameResolver.Resolve(dnt.Value);
if (this.context.Schema.ContainsAttribute(name))
{
Columnid columnId = this.context.Schema.FindColumnId(name);
var dnt = this.cursor.RetrieveColumnAsDNTag(columnId);
value = this.context.DistinguishedNameResolver.Resolve(dnt.Value);
}
else
{
value = null;
}
}
public override void ReadAttribute(string name, out RawSecurityDescriptor value)
@ -150,6 +207,7 @@
public void ReadAttribute(string name, out AttributeMetadataCollection value)
{
byte[] binaryValue;
this.ReadAttribute(name, out binaryValue);
value = new AttributeMetadataCollection(binaryValue);
@ -209,4 +267,4 @@
this.SetAttribute(CommonDirectoryAttributes.PropertyMetaData, meta.ToByteArray());
}
}
}
}

View File

@ -1,14 +1,13 @@
namespace DSInternals.DataStore
{
using DSInternals.Common;
using DSInternals.Common.Data;
using DSInternals.Common.Exceptions;
using Microsoft.Database.Isam;
using Microsoft.Isam.Esent.Interop;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using DSInternals.Common.Data;
using Microsoft.Database.Isam;
using DSInternals.Common;
using DSInternals.Common.Exceptions;
using Microsoft.Isam.Esent.Interop;
/// <summary>
/// The ActiveDirectorySchema class represents the schema partition for a particular domain.
@ -82,6 +81,12 @@
}
}
public bool ContainsAttribute(string attributeName)
{
Validator.AssertNotNullOrWhiteSpace(attributeName, "attributeName");
return this.attributesByName.ContainsKey(attributeName.ToLower());
}
public SchemaAttribute FindAttribute(int internalId)
{
SchemaAttribute attribute;

View File

@ -1,10 +1,10 @@
using Microsoft.Database.Isam;
using System;
using System.Collections.Generic;
using System.Linq;
namespace DSInternals.DataStore
namespace DSInternals.DataStore
{
using Microsoft.Database.Isam;
using System;
using System.Collections.Generic;
using System.Linq;
public class LinkResolver : IDisposable
{
// Column names:
@ -56,6 +56,12 @@ namespace DSInternals.DataStore
public IEnumerable<int> GetLinkedDNTags(int dnTag, string attributeName)
{
if (!this.schema.ContainsAttribute(attributeName))
{
// If the schema does not contain this attribute at all, we pretend it to have an empty value.
yield break;
}
// TODO: Check that the attribute type is DN
this.FindLinkedRecords(dnTag, attributeName);
@ -70,6 +76,12 @@ namespace DSInternals.DataStore
public IEnumerable<byte[]> GetLinkedValues(int dnTag, string attributeName)
{
if(!this.schema.ContainsAttribute(attributeName))
{
// If the schema does not contain this attribute at all, we pretend it to have an empty value.
yield break;
}
// TODO: Check that the attribute is DN-Binary.
this.FindLinkedRecords(dnTag, attributeName);
@ -87,6 +99,7 @@ namespace DSInternals.DataStore
this.Dispose(true);
GC.SuppressFinalize(this);
}
protected virtual void Dispose(bool disposing)
{
if (disposing && cursor != null)
@ -112,4 +125,4 @@ namespace DSInternals.DataStore
this.cursor.FindRecords(MatchCriteria.EqualTo, key);
}
}
}
}