Thursday, March 17, 2005
As an example for someone on the newsgroups, I put together an example of how to use the NetUserChangePassword API. I thought I would share it here. It is not too terribly difficult, but still... someone might find it useful.
Download hereIt includes one unit test to show how it works.
Monday, January 3, 2005
Here is a quick and dirty way to figure out when your account (or anyone else's) will expire on the domain.
Prerequisites:
1. You must be running this from a computer joined to the domain.
2. You must be running this with valid domain credentials.
How does it work:
There is no attribute that directly holds when your password expires. It is a calculation done based on two factors - 1. When you last set your password (pwdLastSet), and 2. What your domain policy for the maximum password age (MaxPwdAge) is.
Here is a simple command line app to demonstrate how this is done:
using System;
using System.DirectoryServices;
using System.Reflection;
class Invoker
{
[STAThread]
static void Main(string[] args)
{
try
{
if (args.Length != 1)
{
Console.WriteLine("Usage: {0} username", Environment.GetCommandLineArgs()[0]);
return;
}
PasswordExpires pe = new PasswordExpires();
Console.WriteLine("Password Policy: {0} days", 0 - pe.PasswordAge.Days);
TimeSpan t = pe.WhenExpires(args[0]);
if(t == TimeSpan.MaxValue)
Console.WriteLine("{0}: Password Never Expires", args[0]);
else if(t == TimeSpan.MinValue)
Console.WriteLine("{0}: Password Expired", args[0]);
else
Console.WriteLine("Password for {0} expires in {1} days at {2}", args[0], t.Days, DateTime.Now.Add(t));
}
catch(Exception ex)
{
Console.WriteLine(ex.ToString()); //debugging info
}
}
}
class PasswordExpires
{
DirectoryEntry _domain;
TimeSpan _passwordAge = TimeSpan.MinValue;
const int UF_DONT_EXPIRE_PASSWD = 0x10000;
public PasswordExpires()
{
//bind with current credentials
using (DirectoryEntry root = new DirectoryEntry("LDAP://rootDSE", null, null, AuthenticationTypes.Secure))
{
string adsPath = String.Format("LDAP://{0}", root.Properties["defaultNamingContext"][0]);
_domain = new DirectoryEntry(adsPath, null, null, AuthenticationTypes.Secure);
}
}
public TimeSpan PasswordAge
{
get
{
if(_passwordAge == TimeSpan.MinValue)
{
long ldate = LongFromLargeInteger(_domain.Properties["maxPwdAge"][0]);
_passwordAge = TimeSpan.FromTicks(ldate);
}
return _passwordAge;
}
}
public TimeSpan WhenExpires(string username)
{
DirectorySearcher ds = new DirectorySearcher(_domain);
ds.Filter = String.Format("(&(objectClass=user)(objectCategory=person)(sAMAccountName={0}))", username);
SearchResult sr = FindOne(ds);
int flags = (int)sr.Properties["userAccountControl"][0];
if(Convert.ToBoolean(flags & UF_DONT_EXPIRE_PASSWD))
{
return TimeSpan.MaxValue; //password never expires
}
//get when they last set their password
DateTime pwdLastSet = DateTime.FromFileTime((long)sr.Properties["pwdLastSet"][0]);
if(pwdLastSet.Subtract(PasswordAge).CompareTo(DateTime.Now) > 0)
{
return pwdLastSet.Subtract(PasswordAge).Subtract(DateTime.Now);
}
else
return TimeSpan.MinValue; //already expired
}
private long LongFromLargeInteger(object largeInteger)
{
System.Type type = largeInteger.GetType();
int highPart = (int)type.InvokeMember("HighPart",BindingFlags.GetProperty, null, largeInteger, null);
int lowPart = (int)type.InvokeMember("LowPart",BindingFlags.GetProperty, null, largeInteger, null);
return (long)highPart << 32 | (uint)lowPart;
}
private SearchResult FindOne(DirectorySearcher searcher)
{
SearchResult sr = null;
using (SearchResultCollection src = searcher.FindAll())
{
if(src.Count>0)
{
sr = src[0];
}
}
return sr;
}
}