diff --git a/Src/DSInternals.Common/Data/Principals/DSAccount.cs b/Src/DSInternals.Common/Data/Principals/DSAccount.cs index 242404d..7fd4116 100644 --- a/Src/DSInternals.Common/Data/Principals/DSAccount.cs +++ b/Src/DSInternals.Common/Data/Principals/DSAccount.cs @@ -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 @@ /// 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(); - - 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(); + + 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(); } /// @@ -498,28 +491,22 @@ /// 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(); + + if (keyCredentialBlobs != null) { - byte[][] keyCredentialBlobs; - dsObject.ReadLinkedValues(CommonDirectoryAttributes.KeyCredentialLink, out keyCredentialBlobs); - - // Parse the blobs and combine them into one array. - var credentials = new List(); - - 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(); } } } \ No newline at end of file diff --git a/Src/DSInternals.DataStore/DatastoreObject.cs b/Src/DSInternals.DataStore/DatastoreObject.cs index e8fbc24..0026ea7 100644 --- a/Src/DSInternals.DataStore/DatastoreObject.cs +++ b/Src/DSInternals.DataStore/DatastoreObject.cs @@ -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()); } } -} +} \ No newline at end of file diff --git a/Src/DSInternals.DataStore/DirectorySchema.cs b/Src/DSInternals.DataStore/DirectorySchema.cs index aeb0142..216501b 100644 --- a/Src/DSInternals.DataStore/DirectorySchema.cs +++ b/Src/DSInternals.DataStore/DirectorySchema.cs @@ -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; /// /// 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; diff --git a/Src/DSInternals.DataStore/LinkResolver.cs b/Src/DSInternals.DataStore/LinkResolver.cs index ad6f8dc..0c02b3d 100644 --- a/Src/DSInternals.DataStore/LinkResolver.cs +++ b/Src/DSInternals.DataStore/LinkResolver.cs @@ -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 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 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); } } -} +} \ No newline at end of file