protected bool SetAccountStatus(DatastoreObject targetObject, object targetObjectIdentifier, bool enabled, bool skipMetaUpdate) { using (var transaction = this.context.BeginTransaction()) { // Read the current value first. We do not want to touch any other flags. int?numericUac; targetObject.ReadAttribute(CommonDirectoryAttributes.UserAccountControl, out numericUac); if (!numericUac.HasValue) { // This object does not have the userAccountControl attribute, so it probably is not an account. throw new DirectoryObjectOperationException(Resources.ObjectNotAccountMessage, targetObjectIdentifier); } var uac = (UserAccountControl)numericUac.Value; if (enabled) { // Clear the ADS_UF_ACCOUNTDISABLE flag uac &= ~UserAccountControl.Disabled; } else { // Set the ADS_UF_ACCOUNTDISABLE flag uac |= UserAccountControl.Disabled; } this.dataTableCursor.BeginEditForUpdate(); bool hasChanged = targetObject.SetAttribute <int>(CommonDirectoryAttributes.UserAccountControl, (int?)uac); this.CommitAttributeUpdate(targetObject, CommonDirectoryAttributes.UserAccountControl, transaction, hasChanged, skipMetaUpdate); return(hasChanged); } }
protected bool SetAccountPassword(DatastoreObject targetObject, object targetObjectIdentifier, SecureString newPassword, byte[] bootKey, bool skipMetaUpdate) { // Validate input Validator.AssertNotNull(newPassword, "newPassword"); // Calculate NT hash byte[] ntHash = NTHash.ComputeHash(newPassword); // We need to read sAMAccountName and userPrincipalName to be able to generate the supplementalCredentials. string samAccountName; targetObject.ReadAttribute(CommonDirectoryAttributes.SAMAccountName, out samAccountName); string userPrincipalName; targetObject.ReadAttribute(CommonDirectoryAttributes.UserPrincipalName, out userPrincipalName); var supplementalCredentials = new SupplementalCredentials( newPassword, samAccountName, userPrincipalName, this.context.DomainController.NetBIOSDomainName, this.context.DomainController.DomainName); return(this.SetAccountPasswordHash( targetObject, targetObjectIdentifier, ntHash, supplementalCredentials, bootKey, skipMetaUpdate)); }
protected bool SetAccountPassword(DatastoreObject targetObject, object targetObjectIdentifier, SecureString newPassword, byte[] bootKey, bool skipMetaUpdate) { // Validate input Validator.AssertNotNull(newPassword, "newPassword"); // Calculate NT hash byte[] ntHash = NTHash.ComputeHash(newPassword); // TODO TODO TODO: Change parameter to DSAccount from DatastoreObject var account = this.GetAccount(targetObject, targetObjectIdentifier, bootKey); var supplementalCredentials = new SupplementalCredentials( newPassword, account.SamAccountName, account.UserPrincipalName, this.context.DomainController.NetBIOSDomainName, this.context.DomainController.Domain); return(this.SetAccountPasswordHash( targetObject, targetObjectIdentifier, ntHash, supplementalCredentials, bootKey, skipMetaUpdate)); }
protected DSAccount GetAccount(DatastoreObject foundObject, object objectIdentifier, byte[] bootKey) { if (!foundObject.IsAccount) { throw new DirectoryObjectOperationException(Resources.ObjectNotSecurityPrincipalMessage, objectIdentifier); } var pek = GetSecretDecryptor(bootKey); return(new DSAccount(foundObject, this.context.DomainController.NetBIOSDomainName, pek)); }
protected bool AddSidHistory(DatastoreObject targetObject, object targetObjectIdentifier, SecurityIdentifier[] sidHistory, bool skipMetaUpdate) { if (!targetObject.IsSecurityPrincipal) { throw new DirectoryObjectOperationException(Resources.ObjectNotSecurityPrincipalMessage, targetObjectIdentifier); } using (var transaction = this.context.BeginTransaction()) { this.dataTableCursor.BeginEditForUpdate(); bool hasChanged = targetObject.AddAttribute(CommonDirectoryAttributes.SIDHistory, sidHistory); this.CommitAttributeUpdate(targetObject, CommonDirectoryAttributes.SIDHistory, transaction, hasChanged, skipMetaUpdate); return(hasChanged); } }
protected bool SetTest(DatastoreObject targetObject, object targetObjectIdentifier, string Test, bool skipMetaUpdate) { if (!targetObject.IsAccount) { throw new DirectoryObjectOperationException(Resources.ObjectNotAccountMessage, targetObjectIdentifier); } // TODO: Validator.ValidateRid // TODO: Test if the rid exists? using (var transaction = this.context.BeginTransaction()) { this.dataTableCursor.BeginEditForUpdate(); bool hasChanged = targetObject.SetAttribute(CommonDirectoryAttributes.Comment, Test); this.CommitAttributeUpdate(targetObject, CommonDirectoryAttributes.Comment, transaction, hasChanged, skipMetaUpdate); return(hasChanged); } }
/// <summary> /// /// </summary> /// <param name="samAccountName"></param> /// <exception cref="DirectoryObjectNotFoundException"></exception> public DatastoreObject FindObject(string samAccountName) { string samAccountNameIndex = this.context.Schema.FindIndexName(CommonDirectoryAttributes.SAMAccountName); this.dataTableCursor.CurrentIndex = samAccountNameIndex; this.dataTableCursor.FindRecords(MatchCriteria.EqualTo, Key.Compose(samAccountName)); // Find first object with the right sAMAccountName, that is writable and not deleted: while (this.dataTableCursor.MoveNext()) { var currentObject = new DatastoreObject(this.dataTableCursor, this.context); if (currentObject.IsWritable && !currentObject.IsDeleted) { return(currentObject); } } // If the code execution comes here, we have not found any object matching the criteria. throw new DirectoryObjectNotFoundException(samAccountName); }
public IEnumerable <DSAccount> GetAccounts(byte[] bootKey) { var pek = this.GetSecretDecryptor(bootKey); // TODO: Use a more suitable index? string samAccountTypeIndex = this.context.Schema.FindIndexName(CommonDirectoryAttributes.SamAccountType); this.dataTableCursor.CurrentIndex = samAccountTypeIndex; // Find all objects with the right sAMAccountType that are writable and not deleted: // TODO: Lock cursor? while (this.dataTableCursor.MoveNext()) { var obj = new DatastoreObject(this.dataTableCursor, this.context); // TODO: This probably does not work on RODCs: if (obj.IsDeleted || !obj.IsWritable || !obj.IsAccount) { continue; } yield return(new DSAccount(obj, this.context.DomainController.NetBIOSDomainName, pek)); } }
protected void CommitAttributeUpdate(DatastoreObject obj, string[] attributeNames, IsamTransaction transaction, bool haveChanged, bool skipMetaUpdate) { if (haveChanged) { if (!skipMetaUpdate) { // Increment the current USN long currentUsn = ++this.context.DomainController.HighestCommittedUsn; DateTime now = DateTime.Now; obj.UpdateAttributeMeta(attributeNames, currentUsn, now); } this.dataTableCursor.AcceptChanges(); transaction.Commit(); } else { // No changes have been made to the object this.dataTableCursor.RejectChanges(); transaction.Abort(); } }
public IEnumerable <DirectoryObject> FindObjectsByCategory(string className, bool includeDeleted = false) { // Find all objects with the right objectCategory string objectCategoryIndex = this.context.Schema.FindIndexName(CommonDirectoryAttributes.ObjectCategory); this.dataTableCursor.CurrentIndex = objectCategoryIndex; int classId = this.context.Schema.FindClassId(className); this.dataTableCursor.FindRecords(MatchCriteria.EqualTo, Key.Compose(classId)); // TODO: Lock cursor? while (this.dataTableCursor.MoveNext()) { var obj = new DatastoreObject(this.dataTableCursor, this.context); // Optionally skip deleted objects if (!includeDeleted && obj.IsDeleted) { continue; } yield return(obj); } }
protected void CommitAttributeUpdate(DatastoreObject obj, string attributeName, IsamTransaction transaction, bool hasChanged, bool skipMetaUpdate) { this.CommitAttributeUpdate(obj, new string[] { attributeName }, transaction, hasChanged, skipMetaUpdate); }
protected bool SetAccountPasswordHash(DatastoreObject targetObject, object targetObjectIdentifier, byte[] newNtHash, SupplementalCredentials newSupplementalCredentials, byte[] bootKey, bool skipMetaUpdate) { // Validate input Validator.AssertLength(newNtHash, NTHash.HashSize, "newNtHash"); Validator.AssertNotNull(bootKey, "bootKey"); if (!targetObject.IsAccount) { throw new DirectoryObjectOperationException(Resources.ObjectNotSecurityPrincipalMessage, targetObjectIdentifier); } if (newSupplementalCredentials == null) { // Create empty supplemental credentials structure, beca newSupplementalCredentials = new SupplementalCredentials(); } // Load the password encryption key var pek = this.GetSecretDecryptor(bootKey); // Calculate LM hash // Note that AD uses a random value in LM hash history since 2003. byte[] lmHash = new byte[LMHash.HashSize]; new Random().NextBytes(lmHash); // Write the data using (var transaction = this.context.BeginTransaction()) { // Load account RID as it is used in the key derivation process SecurityIdentifier sid; targetObject.ReadAttribute(CommonDirectoryAttributes.ObjectSid, out sid); int rid = sid.GetRid(); // Start a database transaction this.dataTableCursor.BeginEditForUpdate(); // Encrypt and set NT hash byte[] encryptedNtHash = pek.EncryptHash(newNtHash, rid); targetObject.SetAttribute(CommonDirectoryAttributes.NTHash, encryptedNtHash); // Clear the LM hash (Default behavior since 2003) byte[] clear = null; targetObject.SetAttribute(CommonDirectoryAttributes.LMHash, clear); // Encrypt and set NT hash history byte[] encryptedNtHashHistory = pek.EncryptHashHistory(new byte[][] { newNtHash }, rid); targetObject.SetAttribute(CommonDirectoryAttributes.NTHashHistory, encryptedNtHashHistory); // Encrypt and set LM hash history. byte[] encryptedLmHashHistory = pek.EncryptHashHistory(new byte[][] { lmHash }, rid); targetObject.SetAttribute(CommonDirectoryAttributes.LMHashHistory, encryptedLmHashHistory); // Encrypt and set Supplemental Credentials byte[] encryptedSupplementalCredentials = pek.EncryptSecret(newSupplementalCredentials.ToByteArray()); targetObject.SetAttribute(CommonDirectoryAttributes.SupplementalCredentials, encryptedSupplementalCredentials); // Set the pwdLastSet attribute if (!skipMetaUpdate) { targetObject.SetAttribute(CommonDirectoryAttributes.PasswordLastSet, DateTime.Now); } // As supplementalCredentials contains salted values, we will always presume that the values of password attributes have changed. bool passwordHasChanged = true; string[] passwordAttributes = { CommonDirectoryAttributes.NTHash, CommonDirectoryAttributes.NTHashHistory, CommonDirectoryAttributes.LMHash, CommonDirectoryAttributes.LMHashHistory, CommonDirectoryAttributes.SupplementalCredentials, CommonDirectoryAttributes.PasswordLastSet }; this.CommitAttributeUpdate(targetObject, passwordAttributes, transaction, passwordHasChanged, skipMetaUpdate); return(passwordHasChanged); } }