Active Directory user password expiration date .NET/OU Group Policy - c#

I have searched the site for information and found this:
ASP.NET C# Active Directory - See how long before a user's password expires
which explains how to get the value of when the password expires as per Domain Policy.
My question is this: what if the user has an OU Group Policy that has a different MaxPasswordAge value, overriding the one specified in Domain Group Policy? How to programatically get the OU's Group Policy Object?
Edit: To make this question a little bit more clear, I am adding this edit. What I am after is to being able to tell when user's password expires. As far as I understand that date value can either be governed by domains local policy or by group object policy. I have a Linq2DirectoryService Provider that translates Linq to Ldap queries. So an LDAP query to get the date expiration value would be optimal for this subj. If you answer includes what objects wrappers supported by .net are included into this equation - it would be a dead on answer!

Let me start with http://support.microsoft.com/kb/323750 which contains Visual Basic and VBScript examples and http://www.anitkb.com/2010/03/how-to-implement-active-directory.html which outlines how the maxPwdAge OU setting impacts computers, not users. It also has a comment pointing to AloInfo.exe as a tool from MS that can be used to get password ages.
Here is the example:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.DirectoryServices;
namespace LDAP
{
class Program
{
static void Main(string[] args)
{
string domainAndUsername = string.Empty;
string domain = string.Empty;
string userName = string.Empty;
string passWord = string.Empty;
AuthenticationTypes at = AuthenticationTypes.Anonymous;
StringBuilder sb = new StringBuilder();
domain = #"LDAP://w.x.y.z";
domainAndUsername = #"LDAP://w.x.y.z/cn=Lawrence E."+
" Smithmier\, Jr.,cn=Users,dc=corp,"+
"dc=productiveedge,dc=com";
userName = "Administrator";
passWord = "xxxpasswordxxx";
at = AuthenticationTypes.Secure;
DirectoryEntry entry = new DirectoryEntry(
domain, userName, passWord, at);
DirectorySearcher mySearcher = new DirectorySearcher(entry);
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;
}
DirectoryEntry entryUser = new DirectoryEntry(
domainAndUsername, userName, passWord, at);
mySearcher = new DirectorySearcher(entryUser);
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;
}
Console.WriteLine(
String.Format("You must change your password within"+
" {0} days"
, daysLeft));
Console.ReadLine();
}
}
}

The following code worked for me to get the password expiration date on both domain and local user accounts:
public static DateTime GetPasswordExpirationDate(string userId, string domainOrMachineName)
{
using (var userEntry = new DirectoryEntry("WinNT://" + domainOrMachineName + '/' + userId + ",user"))
{
return (DateTime)userEntry.InvokeGet("PasswordExpirationDate");
}
}

Use following method to get expiration date of the account-
public static DateTime GetPasswordExpirationDate(string userId)
{
string forestGc = String.Format("GC://{0}", Forest.GetCurrentForest().Name);
var searcher = new DirectorySearcher();
searcher = new DirectorySearcher(new DirectoryEntry(forestGc));
searcher.Filter = "(sAMAccountName=" + userId + ")";
var results = searcher.FindOne().GetDirectoryEntry();
return (DateTime)results.InvokeGet("PasswordExpirationDate");
}

Some of the previous answers rely on the DirectoryEntry.InvokeGet method, which MS says should not be used. So here's another approach:
public static DateTime GetPasswordExpirationDate(UserPrincipal user)
{
DirectoryEntry deUser = (DirectoryEntry)user.GetUnderlyingObject();
ActiveDs.IADsUser nativeDeUser = (ActiveDs.IADsUser)deUser.NativeObject;
return nativeDeUser.PasswordExpirationDate;
}
You'll need to add a reference to the ActiveDS COM library typically found at C:\Windows\System32\activeds.tlb.

Related

How to find what user last logged onto a given computer through Active Directory in C#?

I am trying to programmatically find who last logged onto a given computer and when with C#. Given the name of a computer as a string, I have learned about Getting last Logon Time on Computers in Active Directory. However, there doesn't seem to be a property for which user was the one that actually logged in. Do I have to take a different approach for this? Anything I found online that was remotely related to this was in VBScript, but this must be done in C#.
Simply query the necessary information from the System Registry. The following method will set the Registry View based on whether the machine is 64-bit or 32-bit - although if you're doing this remotely - then the approach to obtain this information may need to be altered, but the general approach should be the same.
The Base Key is selected using the name of the machine that you pass an argument along with the Registry View and of course the Registy Hive as Local Machine. Then you open up the Base Key and finally the necessary Sub Key where the information you desire resides.
The location where that information is contained is:
SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI
And from there grab the value from LastLoggedOnUser.
Here is the code in C#:
private static string GetLastUserLoggedOn(string machineName)
{
string location = #"SOFTWARE\Microsoft\Windows\CurrentVersion\Authentication\LogonUI";
var registryView = Environment.Is64BitOperatingSystem ? RegistryView.Registry64 : RegistryView.Registry32;
using (var hive = RegistryKey.OpenRemoteBaseKey(RegistryHive.LocalMachine, machineName, registryView))
{
using (var key = hive.OpenSubKey(location))
{
var item = key.GetValue("LastLoggedOnUser");
string itemValue = item == null ? "No Logon Found" : item.ToString();
return itemValue;
}
}
}
Here is some code I found:
using System;
// has DateTime class
using System.Collections.Generic;
// has the Dictionary class
using System.DirectoryServices;
// has all the LDAP classes such as DirectoryEntry
using ActiveDs;
// has the IADsLargeInteger class
// Get the root entry
DirectoryEntry rootDSE = new DirectoryEntry("LDAP://RootDSE");
string configurationNamingContext =
(string)rootDSE.Properties["configurationNamingContext"].Value;
string defaultNamingContext =
(string)rootDSE.Properties["defaultNamingContext"].Value;
// Get all the domain controllers
// Get all the domain controllers
DirectoryEntry deConfig =
new DirectoryEntry("LDAP://" + configurationNamingContext);
DirectorySearcher dsConfig = new DirectorySearcher(deConfig);
dsConfig.Filter = "(objectClass=nTDSDSA)";
foreach (SearchResult srDomains in dsConfig.FindAll())
{
DirectoryEntry deDomain = srDomains.GetDirectoryEntry();
if (deDomain != null)
{
string dnsHostName =
deDomain.Parent.Properties["DNSHostName"].Value.ToString();
// Get all the users for that domain
}
}
// Get all the users for that domain
DirectoryEntry deUsers =
new DirectoryEntry("LDAP://" + dnsHostName + "/" + defaultNamingContext);
DirectorySearcher dsUsers = new DirectorySearcher(deUsers);
dsUsers.Filter = "(&(objectCategory=person)(objectClass=user))";
foreach (SearchResult srUsers in dsUsers.FindAll())
{
DirectoryEntry deUser = srUsers.GetDirectoryEntry();
if (deUser != null)
{
// Get the distinguishedName and lastLogon for each user
// Save the most recent logon for each user in a Dictionary object
}
}
//Create Dictionary
Dictionary<string, Int64> lastLogons = new Dictionary<string, Int64>();
// Get the distinguishedName and lastLogon for each user
string distinguishedName =
deUser.Properties["distinguishedName"].Value.ToString();
Int64 lastLogonThisServer = new Int64();
if (deUser.Properties["lastLogon"].Value != null)
{
IADsLargeInteger lgInt =
(IADsLargeInteger)deUser.Properties["lastLogon"].Value;
lastLogonThisServer = ((long)lgInt.HighPart << 32) + lgInt.LowPart;
}
// Save the most recent logon for each user in a Dictionary object
if (lastLogons.ContainsKey(distinguishedName))
{
if (lastLogons[distinguishedName] < lastLogonThisServer)
{
lastLogons[distinguishedName] = lastLogonThisServer;
}
}
else
{
lastLogons.Add(distinguishedName, lastLogonThisServer);
}
//Get the time
// Convert the long integer to a DateTime value
string readableLastLogon =
DateTime.FromFileTime(lastLogonThisServer).ToString();
Here is the website where all of this code came from. The developer explained the code in detail.
http://www.codeproject.com/Articles/19181/Find-LastLogon-Across-All-Windows-Domain-Controlle

How to get all members of a local WinNT group?

When I retrieve members of a local WinNT group, someway somehow not all members are returned. I do add:
Active Directory users
Active Directory groups
Both successful (see picture), but only the users show up afterwards.
Question is:
What happens to added groups?
See last method in code sample 'GetMembers()'
Is this a known issue?
Any workaround available?
Many thanks!!
string _domainName = #"MYDOMAIN";
string _basePath = #"WinNT://MYDOMAIN/myserver";
string _userName = #"MYDOMAIN\SvcAccount";
string _password = #"********";
void Main()
{
CreateGroup("lg_TestGroup");
AddMember("lg_TestGroup", #"m.y.username");
AddMember("lg_TestGroup", #"Test_DomainGroup");
GetMembers("lg_TestGroup");
}
// Method added for reference.
void CreateGroup(string accountName)
{
using (DirectoryEntry rootEntry = new DirectoryEntry(_basePath, _userName, _password))
{
DirectoryEntry newEntry = rootEntry.Children.Add(accountName, "group");
newEntry.CommitChanges();
}
}
// Add Active Directory member to the local group.
void AddMember(string groupAccountName, string userName)
{
string path = string.Format(#"{0}/{1}", _basePath, groupAccountName);
using (DirectoryEntry entry = new DirectoryEntry(path, _userName, _password))
{
userName = string.Format("WinNT://{0}/{1}", _domainName, userName);
entry.Invoke("Add", new object[] { userName });
entry.CommitChanges();
}
}
// Get all members of the local group.
void GetMembers(string groupAccountName)
{
string path = string.Format(#"{0}/{1}", _basePath, groupAccountName);
using (DirectoryEntry entry = new DirectoryEntry(path, _userName, _password))
{
foreach (object member in (IEnumerable) entry.Invoke("Members"))
{
using (DirectoryEntry memberEntry = new DirectoryEntry(member))
{
string accountName = memberEntry.Path.Replace(string.Format("WinNT://{0}/", _domainName), string.Format(#"{0}\", _domainName));
Console.WriteLine("- " + accountName); // No groups displayed...
}
}
}
}
Update #1
The sequence of the group members seems to be essential. As soon as the enumerator in GetMembers() stumbles on an Active Directory group, the remaining items are not displayed either. So if 'Test_DomainGroup' is listed first in this example, GetMembers() does not display anything at all.
I know it's an old question and you've likely found the answers you need, but just in case someone else stumbles accross this...
The WinNT ADSI provider you're using in your DirectoryEntry [ie. WinNT://MYDOMAIN/myserver] has pretty limited capabilities for working with Windows Domains that are not stuck in the old Windows 2000/NT functional level (https://support.microsoft.com/en-us/kb/322692).
In this case the problem is that the WinNT provider doesn't know how to handle Global or Universal security groups (which didn't exist in Windows NT and are activated as soon as you raise your domain level above Windows 2000 mixed mode). So, if any groups of those types are nested under a local group you'll generally have problems like the one you described.
The only solution/workaround I've found is to determine if the group you're enumerating is from a domain and if so, then switch to the LDAP provider which will display all members properly when invoking "Members".
Unfortunately I don't know of an "easy" way to just switch from using the WinNT provider to using the LDAP provider using the DirectoryEntry you already have bound to the WinNT provider. So, in the projects I've worked on, I generally prefer to get the SID of the current WinNT object and then use LDAP to search for domain objects with that same SID.
For Windows 2003+ domains you can convert your SID byte array to the usual SDDL format (S-1-5-21...) and then bind to an object with a matching SID using something like:
Byte[] SIDBytes = (Byte[])memberEntry.Properties["objectSID"].Value;
System.Security.Principal.SecurityIdentifier SID = new System.Security.Principal.SecurityIdentifier(SIDBytes, 0);
memberEntry.Dispose();
memberEntry = new DirectoryEntry("LDAP://" + _domainName + "/<SID=" + SID.ToString() + ">");
For Windows 2000 domains you can't bind directly to an object by SID. So, you have to convert your SID byte array to an array of the hex values with a "\" prefix (\01\06\05\16\EF\A2..) and then use the DirectorySearcher to find an object with a matching SID. A method to do this would look something like:
public DirectoryEntry FindMatchingSID(Byte[] SIDBytes, String Win2KDNSDomainName)
{
using (DirectorySearcher Searcher = new DirectorySearcher("LDAP://" + Win2KDNSDomainName))
{
System.Text.StringBuilder SIDByteString = new System.Text.StringBuilder(SIDBytes.Length * 3);
for (Int32 sidByteIndex = 0; sidByteIndex < SIDBytes.Length; sidByteIndex++)
SIDByteString.AppendFormat("\\{0:x2}", SIDBytes[sidByteIndex]);
Searcher.Filter = "(objectSid=" + SIDByteString.ToString() + ")";
SearchResult result = Searcher.FindOne();
if (result == null)
throw new Exception("Unable to find an object using \"" + Searcher.Filter + "\".");
else
return result.GetDirectoryEntry();
}
}

problem to read UserLoginName of user from active directory on local network

Az you know every user that is defined in active directory has FirstName,LastName ,UserLoginName(sAMAccountName),Email,....
now i have FirstName+LastName of any users on my local network and i want to get UserLoginName of any users from server's active directory.what can i do??
and here my code::
DirectoryServices.SearchResult myResult;
string filterString = string.Empty;
string EntryString = "LDAP:// MyDomain";
DirectoryServices.DirectorySearcher myDirectorySearcher = new DirectoryServices.DirectorySearcher(new DirectoryServices.DirectoryEntry(EntryString));
string tempStr;
string[] splStr = new string[3];
filterString = "CN="+FisrtAndLastNameOfUser;
myDirectorySearcher.Filter = filterString;
myDirectorySearcher.PropertiesToLoad.Add("UserName");
myResult = myDirectorySearcher.FindOne();
splStr = Regex.Split(myResult.Properties("UserName").Item(0).ToString, " ");
tempStr = splStr(1).ToString + " " + splStr(0).ToString;
Label1.Text = "Hello " + tempStr;
the outPut of splStr=null;
where is my wrong??
thanks alot.
If you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
Basically, you can define a domain context and easily find users and/or groups in AD:
// set up domain context for default domain
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find user by name
UserPrincipal user = UserPrincipal.FindByIdentity(FirstAndLastNameOfUser);
// if found - access any of its properties
if(user != null)
{
string userLoginName = user.SamAccountName; // or whatever else you're looking for
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:

How to retrieve login name of a user from Active Directory?

I want to retrieve the login name of a user from Active Directory.
For example the name is 'Jan Van der Linden'
After giving this name as parameter I must get his login name in return for example jvdlinden
Since you're on .NET 3.5 and up, you should check out the System.DirectoryServices.AccountManagement (S.DS.AM) namespace. Read all about it here:
Managing Directory Security Principals in the .NET Framework 3.5
Basically, you can define a domain context and easily find users and/or groups in AD:
public string GetLoginName(string userName)
{
// set up domain context
PrincipalContext ctx = new PrincipalContext(ContextType.Domain);
// find user by name
UserPrincipal user = UserPrincipal.FindByIdentity(ctx, userName);
if(user != null)
return user.SamAccountName;
else
return string.Empty;
}
The new S.DS.AM makes it really easy to play around with users and groups in AD:
this actually does almost the opposite but can be a starting point to check and modify as needed:
Finding a User in Active Directory with the Login Name
using .net library you can use the following code to get username or any info from active directory
using System.Management;
using System.Management.Instrumentation;
using System.Runtime.InteropServices;
using System.DirectoryServices;
ManagementObjectSearcher Usersearcher = new ManagementObjectSearcher("Select * From Win32_ComputerSystem Where (Name LIKE 'ws%' or Name LIKE 'it%')");
ManagementObjectCollection Usercollection = Usersearcher.Get();
string[] sep = { "\\" };
string[] UserNameDomain = Usercollection.Cast<ManagementBaseObject>().First()["UserName"].ToString().Split(sep, StringSplitOptions.None);
i add "Select * From Win32_ComputerSystem Where (Name LIKE 'ws%' or Name LIKE 'it%')"
this will get the user name by the full name
hope this could help you
Check this link has needed code snipple
Validate AD-LDAP USer
using (DirectoryEntry entry = new DirectoryEntry())
{
entry.Username = "DOMAIN\\LOGINNAME";
entry.Password = "PASSWORD";
DirectorySearcher searcher = new DirectorySearcher(entry);
searcher.Filter = "(objectclass=user)";
try
{
searcher.FindOne();
{
//Add Your Code if user Found..
}
}
catch (COMException ex)
{
if (ex.ErrorCode == -2147023570)
{
ex.Message.ToString();
// Login or password is incorrect
}
}
}
Without Identity:
private string GetLogonFromDisplayName(string displayName)
{
var search = new DirectorySearcher(string.Format("(&(displayname={0})(objectCategory=user))", displayName));
search.PropertiesToLoad.Add("sAMAccountName");
SearchResult result = search.FindOne();
if (result != null)
{
var logonNameResults = result.Properties["sAMAccountName"];
if (logonNameResults == null || logonNameResults.Count == 0)
{
return null;
}
return logonNameResults[0].ToString();
}
return null;
}

Creating local user account c# and .NET 2.0

How can I create a local user account using .NET 2.0 and c# and also be able to set the "Password never expires" to never.
I have tried using "Net.exe" using Process.Start and passing its parameters but it seems that the "net user" is unable to set the "Password never expires" to never.
This code will create a local account with the password never expires option set:
using System.DirectoryServices;
DirectoryEntry hostMachineDirectory = new DirectoryEntry("WinNT://localhost");
DirectoryEntries entries = hostMachineDirectory.Children;
bool userExists = false;
foreach (DirectoryEntry each in entries)
{
userExists = each.Name.Equals("NewUser",
StringComparison.CurrentCultureIgnoreCase);
if (systemtestUserExists)
break;
}
if (false == userExists)
{
DirectoryEntry obUser = entries.Add("NewUser", "User");
obUser.Properties["FullName"].Add("Local user");
obUser.Invoke("SetPassword", "abcdefg12345#");
obUser.Invoke("Put", new object[] {"UserFlags", 0x10000});
obUser.CommitChanges();
}
The 0x10000 flag means PasswordNeverExpires.
I spent a long time figuring out how to create a local user account with the password set not to expire. It seems that when you try to use:
int val = (int)newUser.Properties["userAccountControl"].Value;
newUser.Properties["userAccountControl"].Value = val | 0x10000
permissions from active directory come into play. If you have active directory permissions everything works fine. If you don't then getting the userAccountControl property will always result in a null value. Trying to set userAccountControl will result in an exception "The directory property cannot be found in the cache".
However after much hunting around I found another property "UserFlags" that needs to be set using Invoke. You can use this to set the flag on a local account. I've tried this code and it worked on windows server 2008.
Hope this helps
Read this excellent CodeProject article
Howto: (Almost) Everything In Active Directory via C#
There is a section "Create User Account" and "Dealing with User Passwords".
UPDATE:
To adapt the code for local accounts replace the respective lines with these:
DirectoryEntry localMachine = new DirectoryEntry("WinNT://" +
Environment.MachineName);
DirectoryEntry newUser = localMachine.Children.Add("localuser", "user");
Here starts the original code snippet for domain accounts:
public string CreateUserAccount(string ldapPath, string userName,
string userPassword)
{
string oGUID = string.Empty;
try
{
string connectionPrefix = "LDAP://" + ldapPath;
DirectoryEntry dirEntry = new DirectoryEntry(connectionPrefix);
DirectoryEntry newUser = dirEntry.Children.Add
("CN=" + userName, "user");
newUser.Properties["samAccountName"].Value = userName;
int val = (int)newUser.Properties["userAccountControl"].Value;
newUser.Properties["userAccountControl"].Value = val | 0x10000;
newUser.CommitChanges();
oGUID = newUser.Guid.ToString();
newUser.Invoke("SetPassword", new object[] { userPassword });
newUser.CommitChanges();
dirEntry.Close();
newUser.Close();
}
catch (System.DirectoryServices.DirectoryServicesCOMException E)
{
//DoSomethingwith --> E.Message.ToString();
}
return oGUID;
}
There are some specifics to understand
when dealing with user passwords and
boundaries around passwords such as
forcing a user to change their
password on the next logon, denying
the user the right to change their own
passwords, setting passwords to never
expire, to when to expire, and these
tasks can be accomplished using
UserAccountControl flags that are
demonstrated in the proceeding
sections.
Please refer to this great
MSDN article: Managing User Passwords
for examples and documentation
regarding these features.
CONST HEX
------------------------------------------
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
using System.DirectoryServices;
DirectoryEntry hostMachineDirectory = new DirectoryEntry("WinNT://localhost");
DirectoryEntries entries = hostMachineDirectory.Children;
bool userExists = false;
foreach (DirectoryEntry each in entries)
{
userExists = each.Name.Equals("NewUser",
StringComparison.CurrentCultureIgnoreCase);
if (systemtestUserExists)
break;
}
if (false == userExists)
{
DirectoryEntry obUser = entries.Add("NewUser", "User");
obUser.Properties["FullName"].Add("Local user");
obUser.Invoke("SetPassword", "abcdefg12345#");
obUser.Invoke("Put", new object[] {"UserFlags", 0x10000});
obUser.CommitChanges();

Categories

Resources