public static bool GotoParentObject(this Cursor dataTableCursor, DirectorySchema schema) { // TODO: Check if we are really dealing with the datatable. // Read parent DN Tag of the current record int parentDNTag = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.ParentDNTag)).Value; // Set the index to PDNT column dataTableCursor.CurrentIndex = schema.FindIndexName(CommonDirectoryAttributes.ParentDNTag); // Position the cursor to the only matching record return(dataTableCursor.GotoKey(Key.Compose(parentDNTag))); }
public static void FindChildren(this Cursor dataTableCursor, DirectorySchema schema) { // TODO: Check if we are really dealing with the datatable. // Read DN Tag of the current record int dnTag = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.DNTag)).Value; // Set the index to PDNT column to get all children pointing to the current record dataTableCursor.CurrentIndex = schema.FindIndexName(CommonDirectoryAttributes.ParentDNTag); // Position the cursor before the first child (Indexed columns: PDNT_col, name) dataTableCursor.FindRecords(MatchCriteria.EqualTo, Key.ComposeWildcard(dnTag)); }
/// <summary> /// /// </summary> /// <param name="dnTag"></param> /// <returns></returns> /// <exception cref="DirectoryObjectNotFoundException"></exception> public DistinguishedName Resolve(int dnTag) { if (dnTag < ADConstants.RootDNTag) { throw new ArgumentOutOfRangeException("dnTag"); } if (dnTag == ADConstants.RootDNTag) { // TODO: or null? return(new DistinguishedName()); } // TODO: Move to constructor? var dntColId = schema.FindColumnId(CommonDirectoryAttributes.DNTag); var pdntColId = schema.FindColumnId(CommonDirectoryAttributes.ParentDNTag); var rdnColId = schema.FindColumnId(CommonDirectoryAttributes.RDN); var rdnTypeColId = schema.FindColumnId(CommonDirectoryAttributes.RDNType); DistinguishedName dn = new DistinguishedName(); cursor.CurrentIndex = schema.FindIndexName(CommonDirectoryAttributes.DNTag); int currentDNTag = dnTag; do { bool found = cursor.GotoKey(Key.Compose(currentDNTag)); if (!found) { throw new DirectoryObjectNotFoundException(dnTag); } string name = cursor.RetrieveColumnAsString(rdnColId); int rdnType = cursor.RetrieveColumnAsInt(rdnTypeColId).Value; string rdnAtt = schema.FindAttribute(rdnType).Name.ToUpper(); dn.AddParent(rdnAtt, name); currentDNTag = cursor.RetrieveColumnAsDNTag(pdntColId).Value; } while (currentDNTag != ADConstants.RootDNTag); // TODO: Parent DN Caching return(dn); }
/// <summary> /// Recursively resolves a DN Tag to a full distinguished name. /// </summary> /// <param name="dnTag">Distinguished name tag</param> /// <returns>Resolved DN</returns> /// <exception cref="DirectoryObjectNotFoundException" /> public DistinguishedName Resolve(int dnTag) { // Check the DN cache first DistinguishedName dnFromCache = this.TryResolveFromCache(dnTag); if (dnFromCache != null) { // We just return the DN from cache. return(dnFromCache); } if (dnTag < ADConstants.RootDNTag) { // Minimum DN Tag is 2. throw new ArgumentOutOfRangeException("dnTag"); } // Cache column IDs var dntColId = schema.FindColumnId(CommonDirectoryAttributes.DNTag); var pdntColId = schema.FindColumnId(CommonDirectoryAttributes.ParentDNTag); var rdnColId = schema.FindColumnId(CommonDirectoryAttributes.RDN); var rdnTypeColId = schema.FindColumnId(CommonDirectoryAttributes.RDNType); // Set index to the Distinguished Name Tag (~primary key) cursor.CurrentIndex = schema.FindIndexName(CommonDirectoryAttributes.DNTag); // We will build the DN from leaf to root DistinguishedName result = new DistinguishedName(); int currentDNTag = dnTag; while (currentDNTag != ADConstants.RootDNTag) { // Move cursor to the current object bool found = cursor.GotoKey(Key.Compose(currentDNTag)); if (!found) { throw new DirectoryObjectNotFoundException(dnTag); } // Retrieve the current object's RDN, e.g. CN=Administrator string name = cursor.RetrieveColumnAsString(rdnColId); int rdnType = cursor.RetrieveColumnAsInt(rdnTypeColId).Value; string rdnAtt = schema.FindAttribute(rdnType).Name.ToUpper(); var currentRDN = new DistinguishedNameComponent(rdnAtt, name); // Concat the current RDN with the child RDN in result result.AddParent(currentRDN); // Identify the current object's parent int parentDNTag = cursor.RetrieveColumnAsDNTag(pdntColId).Value; // Check the DN cache DistinguishedName parentDN = this.TryResolveFromCache(parentDNTag); if (parentDN != null) { // We have found the parent object in DN cache. result.AddParent(parentDN); // Add the current object to cache if the parent is DC or root. bool shoulCache = parentDN.Components.Count == 0 || String.Equals(parentDN.Components[0].Name, CommonDirectoryAttributes.DomainComponent, StringComparison.OrdinalIgnoreCase); if (shoulCache) { var currentDN = new DistinguishedName(currentRDN); currentDN.AddParent(parentDN); this.dnCache.Add(currentDNTag, currentDN); } // We can stop the recursion as we have resolved the entire DN using cache. break; } // Move upwards to the object's parent as we have not found it in the DN cache. currentDNTag = parentDNTag; } return(result); }
public DomainController(DirectoryContext context) { // TODO: Split to different methods. // Open the hiddentable this.systemTableCursor = context.OpenSystemTable(); // Go to the first and only record in the hiddentable: this.systemTableCursor.MoveToFirst(); // Load attributes from the hiddentable: this.NTDSSettingsDNT = this.systemTableCursor.RetrieveColumnAsInt(ntdsSettingsCol).Value; if (this.systemTableCursor.TableDefinition.Columns.Contains(osVersionMajorCol)) { // Some databases like the initial adamntds.dit or ntds.dit on Windows Server 2003 do not contain the OS Version this.OSVersionMinor = this.systemTableCursor.RetrieveColumnAsUInt(osVersionMinorCol); this.OSVersionMajor = this.systemTableCursor.RetrieveColumnAsUInt(osVersionMajorCol); } if (this.systemTableCursor.TableDefinition.Columns.Contains(epochCol)) { // This is a new feature since Windows Server 2008 this.epochCache = this.systemTableCursor.RetrieveColumnAsInt(epochCol); } if (this.systemTableCursor.TableDefinition.Columns.Contains(usnAtIfmCol)) { this.UsnAtIfm = this.systemTableCursor.RetrieveColumnAsLong(usnAtIfmCol); } this.BackupExpiration = this.systemTableCursor.RetrieveColumnAsGeneralizedTime(backupExpirationCol); this.BackupUsn = this.systemTableCursor.RetrieveColumnAsLong(backupUsnCol); this.State = (DatabaseState)this.systemTableCursor.RetrieveColumnAsInt(stateCol).Value; byte[] binaryFlags = this.systemTableCursor.RetrieveColumnAsByteArray(flagsCol); var databaseFlags = new DatabaseFlags(binaryFlags); this.IsADAM = databaseFlags.ADAMDatabase; // TODO: Export other database flags, not just IsADAM. // TODO: Load database health this.highestUSNCache = this.systemTableCursor.RetrieveColumnAsLong(highestCommitedUsnCol).Value; // Now we can load the Invocation ID and other information from the datatable: using (var dataTableCursor = context.OpenDataTable()) { // Goto NTDS Settings object: DirectorySchema schema = context.Schema; dataTableCursor.CurrentIndex = schema.FindIndexName(CommonDirectoryAttributes.DNTag); bool ntdsFound = dataTableCursor.GotoKey(Key.Compose(this.NTDSSettingsDNT)); // Load data from the NTDS Settings object this.InvocationId = dataTableCursor.RetrieveColumnAsGuid(schema.FindColumnId(CommonDirectoryAttributes.InvocationId)).Value; this.DsaGuid = dataTableCursor.RetrieveColumnAsGuid(schema.FindColumnId(CommonDirectoryAttributes.ObjectGUID)).Value; this.Options = dataTableCursor.RetrieveColumnAsDomainControllerOptions(schema.FindColumnId(CommonDirectoryAttributes.Options)); string ntdsName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.CommonName)); // Retrieve Configuration Naming Context this.ConfigurationNamingContextDNT = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.NamingContextDNTag)).Value; this.ConfigurationNamingContext = context.DistinguishedNameResolver.Resolve(this.ConfigurationNamingContextDNT); // Retrieve Schema Naming Context this.SchemaNamingContextDNT = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.SchemaLocation)).Value; this.SchemaNamingContext = context.DistinguishedNameResolver.Resolve(this.SchemaNamingContextDNT); // Goto DC object (parent of NTDS): bool dcFound = dataTableCursor.GotoToParentObject(schema); // Load data from the DC object // Load DC name: string dcName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.CommonName)); // DC name is null in the initial database, so use NTDS Settings object's CN instead this.Name = dcName ?? ntdsName; // Load DNS Host Name this.DNSHostName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.DNSHostName)); // Load server reference to domain partition: int dcDNTag = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.DNTag)).Value; this.ServerReferenceDNT = context.LinkResolver.GetLinkedDNTag(dcDNTag, CommonDirectoryAttributes.ServerReference); // Goto Servers object (parent of DC): bool serversFound = dataTableCursor.GotoToParentObject(schema); // Goto Site object (parent of servers): bool siteFound = dataTableCursor.GotoToParentObject(schema); // Load data from the Site object if (siteFound) { this.SiteName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.CommonName)); } // Load partitions (linked multivalue attribute) // TODO: Does not return PAS partitions on RODCs IEnumerable <int> partitionDNTags = context.LinkResolver.GetLinkedDNTags(this.NTDSSettingsDNT, CommonDirectoryAttributes.MasterNamingContexts); this.WritablePartitions = context.DistinguishedNameResolver.Resolve(partitionDNTags).Select(dn => dn.ToString()).ToArray(); // Load domain (linked multivalue attribute) // TODO: Test this against a GC and RODC: this.DomainNamingContextDNT = context.LinkResolver.GetLinkedDNTag(this.NTDSSettingsDNT, CommonDirectoryAttributes.DomainNamingContexts); if (this.DomainNamingContextDNT.HasValue) { // Move cursor to domain: bool domainObjectFound = dataTableCursor.GotoKey(Key.Compose(this.DomainNamingContextDNT.Value)); // Load domain SID this.DomainSid = dataTableCursor.RetrieveColumnAsSid(schema.FindColumnId(CommonDirectoryAttributes.ObjectSid)); // Load domain naming context: this.DomainNamingContext = context.DistinguishedNameResolver.Resolve(this.DomainNamingContextDNT.Value); } // Goto server object in domain partition if (this.ServerReferenceDNT.HasValue) { bool serverFound = dataTableCursor.GotoKey(Key.Compose(this.ServerReferenceDNT.Value)); this.OSName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.OperatingSystemName)); this.ServerReference = context.DistinguishedNameResolver.Resolve(this.ServerReferenceDNT.Value); } } }
public DomainController(DirectoryContext context) { // TODO: Split to different methods. // Open the hiddentable this.systemTableCursor = context.OpenSystemTable(); // Go to the first and only record in the hiddentable: this.systemTableCursor.MoveToFirst(); // Load attributes from the hiddentable: this.NTDSSettingsDNT = this.systemTableCursor.RetrieveColumnAsInt(ntdsSettingsCol).Value; if (this.systemTableCursor.TableDefinition.Columns.Contains(osVersionMajorCol)) { // Some databases like the initial adamntds.dit or ntds.dit on Windows Server 2003 do not contain the OS Version this.OSVersionMinor = this.systemTableCursor.RetrieveColumnAsUInt(osVersionMinorCol); this.OSVersionMajor = this.systemTableCursor.RetrieveColumnAsUInt(osVersionMajorCol); } if (this.systemTableCursor.TableDefinition.Columns.Contains(epochCol)) { // This is a new feature since Windows Server 2008 this.epochCache = this.systemTableCursor.RetrieveColumnAsInt(epochCol); } if (this.systemTableCursor.TableDefinition.Columns.Contains(usnAtIfmCol)) { this.UsnAtIfm = this.systemTableCursor.RetrieveColumnAsLong(usnAtIfmCol); } // Load and cache the backup expiration time this.backupExpirationCache = this.systemTableCursor.RetrieveColumnAsGeneralizedTime(backupExpirationCol); this.BackupUsn = this.systemTableCursor.RetrieveColumnAsLong(backupUsnCol); this.State = (DatabaseState)this.systemTableCursor.RetrieveColumnAsInt(stateCol).Value; byte[] binaryFlags = this.systemTableCursor.RetrieveColumnAsByteArray(flagsCol); var databaseFlags = new DatabaseFlags(binaryFlags); this.IsADAM = databaseFlags.ADAMDatabase; // TODO: Export other database flags, not just IsADAM. // TODO: Load database health this.highestUSNCache = this.systemTableCursor.RetrieveColumnAsLong(highestCommitedUsnCol).Value; // Now we can load the Invocation ID and other information from the datatable: using (var dataTableCursor = context.OpenDataTable()) { // Goto NTDS Settings object: DirectorySchema schema = context.Schema; dataTableCursor.CurrentIndex = schema.FindIndexName(CommonDirectoryAttributes.DNTag); bool ntdsFound = dataTableCursor.GotoKey(Key.Compose(this.NTDSSettingsDNT)); // Load data from the NTDS Settings object this.InvocationId = dataTableCursor.RetrieveColumnAsGuid(schema.FindColumnId(CommonDirectoryAttributes.InvocationId)).Value; this.DsaGuid = dataTableCursor.RetrieveColumnAsGuid(schema.FindColumnId(CommonDirectoryAttributes.ObjectGUID)).Value; this.Options = dataTableCursor.RetrieveColumnAsDomainControllerOptions(schema.FindColumnId(CommonDirectoryAttributes.Options)); string ntdsName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.CommonName)); // Retrieve Configuration Naming Context this.ConfigurationNamingContextDNT = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.NamingContextDNTag)).Value; this.ConfigurationNamingContext = context.DistinguishedNameResolver.Resolve(this.ConfigurationNamingContextDNT); // Retrieve Schema Naming Context this.SchemaNamingContextDNT = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.SchemaLocation)).Value; this.SchemaNamingContext = context.DistinguishedNameResolver.Resolve(this.SchemaNamingContextDNT); // Goto DC object (parent of NTDS): bool dcFound = dataTableCursor.GotoParentObject(schema); // Load data from the DC object // Load DC name: string dcName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.CommonName)); // DC name is null in the initial database, so use NTDS Settings object's CN instead this.Name = dcName ?? ntdsName; // Load DNS Host Name this.DNSHostName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.DNSHostName)); // Load server reference to domain partition: int dcDNTag = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.DNTag)).Value; this.ServerReferenceDNT = context.LinkResolver.GetLinkedDNTag(dcDNTag, CommonDirectoryAttributes.ServerReference); // Goto Servers object (parent of DC): bool serversFound = dataTableCursor.GotoParentObject(schema); // Goto Site object (parent of servers): bool siteFound = dataTableCursor.GotoParentObject(schema); // Load data from the Site object if (siteFound) { this.SiteName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.CommonName)); } // Load partitions (linked multivalue attribute) // TODO: Does not return PAS partitions on RODCs IEnumerable <int> partitionDNTags = context.LinkResolver.GetLinkedDNTags(this.NTDSSettingsDNT, CommonDirectoryAttributes.MasterNamingContexts); this.WritablePartitions = context.DistinguishedNameResolver.Resolve(partitionDNTags).Select(dn => dn.ToString()).ToArray(); // Load domain (linked multivalue attribute) // TODO: Test this against a GC and RODC: this.DomainNamingContextDNT = context.LinkResolver.GetLinkedDNTag(this.NTDSSettingsDNT, CommonDirectoryAttributes.DomainNamingContexts); if (this.DomainNamingContextDNT.HasValue) { // Move cursor to domain: bool domainObjectFound = dataTableCursor.GotoKey(Key.Compose(this.DomainNamingContextDNT.Value)); // Load domain SID this.DomainSid = dataTableCursor.RetrieveColumnAsSid(schema.FindColumnId(CommonDirectoryAttributes.ObjectSid)); // Load domain GUID this.DomainGuid = dataTableCursor.RetrieveColumnAsGuid(schema.FindColumnId(CommonDirectoryAttributes.ObjectGUID)); // Load domain naming context: this.DomainNamingContext = context.DistinguishedNameResolver.Resolve(this.DomainNamingContextDNT.Value); // Load the domain functional level this.DomainMode = dataTableCursor.RetrieveColumnAsFunctionalLevel(schema.FindColumnId(CommonDirectoryAttributes.FunctionalLevel)); } // Goto server object in domain partition if (this.ServerReferenceDNT.HasValue) { bool serverFound = dataTableCursor.GotoKey(Key.Compose(this.ServerReferenceDNT.Value)); // Load DC OS this.OSName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.OperatingSystemName)); // Load DC GUID this.Guid = dataTableCursor.RetrieveColumnAsGuid(schema.FindColumnId(CommonDirectoryAttributes.ObjectGUID)); // Load DC SID this.Sid = dataTableCursor.RetrieveColumnAsSid(schema.FindColumnId(CommonDirectoryAttributes.ObjectSid)); // Load DC DN this.ServerReference = context.DistinguishedNameResolver.Resolve(this.ServerReferenceDNT.Value); } // The crossRefContainer does not exist in initial DBs, as it is created during dcpromo. if (this.State != DatabaseState.Boot) { // Construct crossRefContainer DN (CN=Partitions,CN=Configuration,...) var crossRefContainer = new DistinguishedName(CrossRefContainerRDN); crossRefContainer.AddParent(this.ConfigurationNamingContext); // Goto crossRefContainer var crossRefContainerDNT = context.DistinguishedNameResolver.Resolve(crossRefContainer); bool crossRefContainerFound = dataTableCursor.GotoKey(Key.Compose(crossRefContainerDNT)); // Read the forest functional level from the crossRefContainer object we just located this.ForestMode = dataTableCursor.RetrieveColumnAsFunctionalLevel(schema.FindColumnId(CommonDirectoryAttributes.FunctionalLevel)); // Go through all crossRef objects (children of the crossRefContainer) dataTableCursor.FindChildren(schema); while (dataTableCursor.MoveNext()) { // Find the directory partition that is associated with the current crossRef object var partitionNCNameDNT = dataTableCursor.RetrieveColumnAsDNTag(schema.FindColumnId(CommonDirectoryAttributes.NamingContextName)); // Note that foreign crossRef objects do not have nCName set if (partitionNCNameDNT == this.DomainNamingContextDNT) { // This must be the DC's domain crossRef object, so we can retrieve its NetBIOS name. this.NetBIOSDomainName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.NetBIOSName)); } if (partitionNCNameDNT == this.ConfigurationNamingContextDNT) { // This must be the configuration partition's crossRef object, so we can retrieve the forest DNS name. this.ForestName = dataTableCursor.RetrieveColumnAsString(schema.FindColumnId(CommonDirectoryAttributes.DNSRoot)); } } } } }