I am throwing together a quick C# win forms app to help resolve a repetitive clerical job.
I have performed a search in AD for all user accounts and am adding them to a list view with check boxes.
I would like to default the listviewitems' default check state to depend upon the enabled/disabled state of the account.
string path = "LDAP://dc=example,dc=local";
DirectoryEntry directoryRoot = new DirectoryEntry(path);
DirectorySearcher searcher = new DirectorySearcher(directoryRoot,
"(&(objectClass=User)(objectCategory=Person))");
SearchResultCollection results = searcher.FindAll();
foreach (SearchResult result in results)
{
DirectoryEntry de = result.GetDirectoryEntry();
ListViewItem lvi = new ListViewItem(
(string)de.Properties["SAMAccountName"][0]);
// lvi.Checked = (bool) de.Properties["AccountEnabled"]
lvwUsers.Items.Add(lvi);
}
I'm struggling to find the right attribute to parse to get the state of the account from the DirectoryEntry object. I've searched for AD User attributes, but not found anything useful.
Can anyone offer any pointers?
this code here should work...
private bool IsActive(DirectoryEntry de)
{
if (de.NativeGuid == null) return false;
int flags = (int)de.Properties["userAccountControl"].Value;
return !Convert.ToBoolean(flags & 0x0002);
}
Using System.DirectoryServices.AccountManagement:
domainName and username must be the string values of the domain and username.
using (var domainContext = new PrincipalContext(ContextType.Domain, domainName))
{
using (var foundUser = UserPrincipal.FindByIdentity(domainContext, IdentityType.SamAccountName, username))
{
if (foundUser.Enabled.HasValue)
{
return (bool)foundUser.Enabled;
}
else
{
return true; //or false depending what result you want in the case of Enabled being NULL
}
}
}
Not that anyone asked, but here's a java version (since I ended up here looking for one). Null checking is left as an exercise for the reader.
private Boolean isActive(SearchResult searchResult) {
Attribute userAccountControlAttr = searchResult.getAttributes().get("UserAccountControl");
Integer userAccountControlInt = new Integer((String) userAccoutControlAttr.get());
Boolean disabled = BooleanUtils.toBooleanObject(userAccountControlInt & 0x0002);
return !disabled;
}
You can use something like this:
ADUserAccountControl flags;
Enum.TryParse(de.Properties["userAccountControl"].Value.ToString(), out flags);
if(flags.HasFlag(ADUserAccountControl.ACCOUNTDISABLE)
{
// account is disabled
}
Here is a complete list of all possible flags:
/// <summary>
/// Source: https://learn.microsoft.com/en-us/troubleshoot/windows-server/identity/useraccountcontrol-manipulate-account-properties
/// </summary>
public enum ADUserAccountControl : long
{
SCRIPT = 0x0001,
ACCOUNTDISABLE = 0x0002,
HOMEDIR_REQUIRED = 0x0008,
LOCKOUT = 0x0010,
PASSWD_NOTREQD = 0x0020,
PASSWD_CANT_CHANGE = 0x0040,
ENCRYPTED_TEXT_PWD_ALLOWED = 0x0080,
TEMP_DUPLICATE_ACCOUNT = 0x0100,
NORMAL_ACCOUNT = 0x0200,
INTERDOMAIN_TRUST_ACCOUNT = 0x0800,
WORKSTATION_TRUST_ACCOUNT = 0x1000,
SERVER_TRUST_ACCOUNT = 0x2000,
DONT_EXPIRE_PASSWORD = 0x10000,
MNS_LOGON_ACCOUNT = 0x20000,
SMARTCARD_REQUIRED = 0x40000,
TRUSTED_FOR_DELEGATION = 0x80000,
NOT_DELEGATED = 0x100000,
USE_DES_KEY_ONLY = 0x200000,
DONT_REQ_PREAUTH = 0x400000,
PASSWORD_EXPIRED = 0x800000,
TRUSTED_TO_AUTH_FOR_DELEGATION = 0x1000000,
PARTIAL_SECRETS_ACCOUNT = 0x04000000,
}
I came here looking for an answer, but it was only for DirectoryEntry. So here is a code that works for SearchResult / SearchResultCollection, for people who had the same problem:
private bool checkIfActive(SearchResult sr)
{
var vaPropertiy = sr.Properties["userAccountControl"];
if (vaPropertiy.Count > 0)
{
if (vaPropertiy[0].ToString() == "512" || vaPropertiy[0].ToString() == "66048")
{
return true;
}
return false;
}
return false;
}
Related
I have a web application that uses Active Directory to authenticate. I want to add an option that will notify the users when their password is close to expiring.
I managed to do something, but the problem I have is that the expiration days is negative (daysLeft parameter), yet I can still log in.
string domainAndUsername = #"LDAP://ldapUrl";
DirectoryEntry root = new DirectoryEntry(ldapServer, userID, userPwd, AuthenticationTypes.Secure);
DirectorySearcher mySearcher = new DirectorySearcher(root);
SearchResultCollection results;
string filter = "maxPwdAge=*";
mySearcher.Filter = filter;
results = mySearcher.FindAll();
long maxDays = 0;
if (results.Count >= 1)
{
Int64 maxPwdAge = (Int64)results[0].Properties["maxPwdAge"][0];
maxDays = maxPwdAge / -864000000000;
}
mySearcher = new DirectorySearcher(root);
mySearcher.Filter = "(&(objectCategory=user)(samaccountname=" + userID + "))";
results = mySearcher.FindAll();
long daysLeft = 0;
if (results.Count >= 1)
{
var lastChanged = results[0].Properties["pwdLastSet"][0];
daysLeft = maxDays - DateTime.Today.Subtract(
DateTime.FromFileTime((long)lastChanged)).Days;
}
Since a user couldn't log in if it's account has expired, I am guessing my error is in calculating the days left until account expires...but I can't seem to find where it is.
This snippet works correctly, I have three days left to change my pw, including today:
public static void Main(string[] args)
{
const ulong dataFromAD = 0xFFFFE86D079B8000;
var ticks = -unchecked((long)dataFromAD);
var maxPwdAge = TimeSpan.FromTicks(ticks);
var pwdLastSet = new DateTime(2015,12,16,9,19,13);
var pwdDeadline = (pwdLastSet + maxPwdAge).Date;
Console.WriteLine(pwdDeadline);
Console.WriteLine(pwdDeadline - DateTime.Today);
Console.ReadKey(true);
}
I also verified that TimeSpan.FromTicks(-(long)results[0].Properties["maxPwdAge"][0]) and DateTime.FromFileTime((long)results[0].Properties["pwdLastSet"][0]) are expressions correctly extracting the values from our AD.
I've used the following code in the past, and i think it's accurate. You may try it and see if it works for you:
private static DateTime? getPwdExpiration(string usuario)
{
DirectoryEntry searchGroup = new DirectoryEntry("LDAP://ldapUrl");
DateTime? dt=null;
foreach (DirectoryEntry user in searchGroup.Children)
{
if (user.InvokeGet("userPrincipalName") != null)
{
string username = user.InvokeGet("userPrincipalName").ToString();
username = username.Substring(0, username.IndexOf('#'));
if (username == usuario)
{
if (user.InvokeGet("PasswordExpirationDate") != null)
{
dt = (DateTime)user.InvokeGet("PasswordExpirationDate");
if (dt.Value.CompareTo(new DateTime(1970,1,1))==0)
{
//Password never expires
dt = null;
}
}
break;
}
}
}
return dt;
}
Usage:
DateTime? expirationDate = getPwdExpiration(username);
With Node JS you can achieve the password expiry. Try Something like this
const modifyChange = [
new ldap.Change({
operation: 'replace',
modification: {
pwdMaxAge: 3600 //in seconds
}
})];
client.modify('cn=Manager,dc=mydomain,dc=local', modifyChange, (err) => {
if(!err) {
console.log('password age updated')// updated
}
});
I am trying to check if window account is disabled or not in active directory, for this reason I tried System.DirectoryServices.AccountManagement namespace but could not find any method to check if account is disable unlike IsAccountLockedOut method.
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal =UserPrincipal.FindByIdentity(oPrincipalContext, sUserName);
oUserPrincipal.IsAccountLockedOut();
We use this method:
var context = new DirectoryContext(DirectoryContextType.Domain, "domain");
using (var domainController = DomainController.FindOne(context))
{
using (var directorySearcher = domainController.GetDirectorySearcher())
{
directorySearcher.Filter = String.Format("(sAMAccountName={0})", "login");
directorySearcher.SizeLimit = 1;
var userDirectory = directorySearcher.FindOne();
using (var userDirectoryEntry = userDirectory.GetDirectoryEntry())
{
var active = userDirectoryEntry.IsActive();
}
}
}
IsActive - is an extension method:
public static bool IsActive(this DirectoryEntry directoryEntry)
{
if (directoryEntry.NativeGuid == null) return false;
var value = directoryEntry.Properties["userAccountControl"].Value;
if (value == null)
return true;
var flags = (int)value;
return !Convert.ToBoolean(flags & 0x0002);
}
So, get DirectoryEntry of your account and call this method.
PrincipalContext oPrincipalContext = GetPrincipalContext();
UserPrincipal oUserPrincipal =UserPrincipal.FindByIdentity(oPrincipalContext, sUserName);
bool? IsEnabled = oUserPrincipal.Enabled;
// if IsEnabled = true then User Account is Enabled
// if IsEnabled = false then User Account is Disabled
According to Microsoft documentation NetValidatePasswordPolicy has possibility to implement feature to check password history and avoid reuse of previous passwords. I have gone through all the examples of this API but non of them provide me with functional of how to check Password history. Can somebody guide me how to verify password history?
public static NET_API_STATUS ValidatePassword(string password)
{
var outputArgs = new NET_VALIDATE_OUTPUT_ARG();
var inputArgs = new NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG();
var passwordHistory = new NET_VALIDATE_PASSWORD_HASH();
IntPtr inputPointer = IntPtr.Zero;
IntPtr outputPointer = IntPtr.Zero;
try
{
inputArgs.PasswordMatched = true;
inputArgs.ClearPassword = Marshal.StringToBSTR(password);
//var serverName = System.Environment.MachineName;
string userNameToCheck = #"usr";
//inputArgs.ClearPassword = Marshal.StringToBSTR(password);
inputArgs.UserAccountName = userNameToCheck;
// If using a secure string
////inputArgs.ClearPassword = Marshal.SecureStringToBSTR(secureStringPassword);
inputPointer = Marshal.AllocHGlobal(Marshal.SizeOf(inputArgs));
Marshal.StructureToPtr(inputArgs, inputPointer, false);
NET_API_STATUS status = NetValidatePasswordPolicy("serverdc1", IntPtr.Zero, NET_VALIDATE_PASSWORD_TYPE.NetValidatePasswordChange, inputPointer, ref outputPointer);
if (status == NET_API_STATUS.NERR_Success)
{
outputArgs = (NET_VALIDATE_OUTPUT_ARG)Marshal.PtrToStructure(outputPointer, typeof(NET_VALIDATE_OUTPUT_ARG));
NET_VALIDATE_PASSWORD_HASH OutPasswordHistory = new NET_VALIDATE_PASSWORD_HASH();
OutPasswordHistory.Hash = outputArgs.ChangedPersistedFields.PasswordHistory;
OutPasswordHistory.Length = outputArgs.ChangedPersistedFields.PasswordHistoryLength;
// Copy the Structure to the IntPtr
if (outputArgs.ValidationStatus == NET_API_STATUS.NERR_Success)
{
// Ok
string emtp;
}
return outputArgs.ValidationStatus;
}
else
{
return status;
}
}
I don't believe you can ask for a specific component of the policy to be tested - just the sum of the policy against the provided password.
I need to connect to a clients AD server to display information for all users. They've given me the following:
fqdn, netbios name and a domain controller. Is this enough to connect?
using (var context = new PrincipalContext(ContextType.Domain, "",))
using (var searcher = new PrincipalSearcher(new UserPrincipal(context)))
{
foreach (var result in searcher.FindAll())
{
DirectoryEntry de = result.GetUnderlyingObject() as DirectoryEntry;
}
}
Thanks!
I think Ryan was showing you the old way to do it. From your code it looks like you are using the newer classes.
// create a principal searcher for running a search operation
using (PrincipalSearcher pS = new PrincipalSearcher(uParams))
{
// assign the query filter property for the principal object you created
// you can also pass the user principal in the PrincipalSearcher constructor
pS.QueryFilter = uParams;
// run the query
using (PrincipalSearchResult<Principal> results = pS.FindAll())
{
foreach (Principal item in results)
{
UserPrincipal u = item as UserPrincipal;
list.Add(new MyCustomClass(u.UserPrincipalName)
{
Cn = u.Name,
Email = u.EmailAddress,
EmployeeId = u.EmployeeId,
NameFirst = u.GivenName,
NameLast = u.Surname,
ObjectSid = u.Sid.ToString(),
DistinguishedName = u.DistinguishedName,
SamAccount = u.SamAccountName
});
}
}
}
Note that the AD still imposes sometihng like a 1500 item limit on your queries so you will likely need to send your DirectoryEntry top to something like this:
/// <summary>
/// group member enumeration, simple and fast for large AD groups
/// </summary>
/// <param name="deGroup"></param>
/// <returns>list if distinguished names</returns>
public static List<string> GetMemberList(DirectoryEntry deGroup)
{
List<string> list = new List<string>();
DirectoryEntry entry = deGroup;
uint rangeStep = 1000;
uint rangeLow = 0;
uint rangeHigh = rangeLow + (rangeStep - 1);
bool lastQuery = false;
bool quitLoop = false;
do
{
string attributeWithRange;
if (!lastQuery)
{
attributeWithRange = String.Format("member;range={0}-{1}", rangeLow, rangeHigh);
}
else
{
attributeWithRange = String.Format("member;range={0}-*", rangeLow);
}
using (DirectorySearcher searcher = new DirectorySearcher(entry))
{
searcher.Filter = "(objectClass=*)";
//searcher.Filter = LdapObjectMgr.filterDisabledUsers;
searcher.PropertiesToLoad.Clear();
searcher.PropertiesToLoad.Add(attributeWithRange);
SearchResult results = searcher.FindOne();
foreach (string res in results.Properties.PropertyNames)
{
//list the property names
System.Diagnostics.Debug.WriteLine(res.ToString());
}
if (results.Properties.Contains(attributeWithRange))
{
foreach (object obj in results.Properties[attributeWithRange])
{
//Console.WriteLine(obj.GetType());
if (obj.GetType().Equals(typeof(System.String)))
{
}
else if (obj.GetType().Equals(typeof(System.Int32)))
{
}
//Console.WriteLine(obj.ToString());
list.Add(obj.ToString());
}
if (lastQuery)
{
quitLoop = true;
}
}
else
{
if (lastQuery == false)
{ lastQuery = true; }
else
{ quitLoop = true; }
}
if (!lastQuery)
{
rangeLow = rangeHigh + 1;
rangeHigh = rangeLow + (rangeStep - 1);
}
}
}
while (!quitLoop);
return list;
}
To connect via C# you will need something like this:
DirectoryEntry child = new DirectoryEntry("LDAP://" + domainControllerName + "/" +
objectDn, userName, password);
If you have the domain controller name, the object domain, a user name and a password, you should be good to go.
Just a heads up, you got downvoted because you didn't mention anything that you tried previously.
private bool HasRights(FileSystemRights fileSystemRights_, string fileName_, bool isFile_)
{
bool hasRights = false;
WindowsIdentity WinIdentity = System.Security.Principal.WindowsIdentity.GetCurrent();
WindowsPrincipal WinPrincipal = new WindowsPrincipal(WinIdentity);
AuthorizationRuleCollection arc = null;
if (isFile_)
{
FileInfo fi = new FileInfo(#fileName_);
arc = fi.GetAccessControl().GetAccessRules(true, true, typeof(NTAccount));
}
else
{
DirectoryInfo di = new DirectoryInfo(#fileName_);
arc = di.GetAccessControl().GetAccessRules(true, true, typeof(NTAccount));
}
foreach (FileSystemAccessRule rule in arc)
{
if (WinPrincipal.IsInRole(rule.IdentityReference.Value))
{
if (((int)rule.FileSystemRights & (int)fileSystemRights_) > 0)
{
if (rule.AccessControlType == AccessControlType.Allow)
hasRights = true;
else if (rule.AccessControlType == AccessControlType.Deny)
{
hasRights = false;
break;
}
}
}
}
return hasRights;
}
The above code block is causing me problems. When the WinPrincipal.IsInRole(rule.IdentityReference.Value) is executed the following exception occurs:
"The trust relationship between the primary domain and the trusted domain failed.".
I'm very new to using identities, principles and such so I don't know what's the problem. I'm assuming it's with the use of NTAccount?
Thanks
I could try to address your question by suggesting you use a SecurityIdentifier, but there are many other issues here that would still be show-stoppers even if this were addressed. I'm not talking about inefficiencies such as using FileInfo instead of File, but the basic logic you're trying to use to interpret the DACL.
Take a look at: http://msdn.microsoft.com/en-us/library/cc230290(PROT.10).aspx