Thursday, 17 March 2005

NetUserChangePassword implementation in C#

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 here

It includes one unit test to show how it works.

Monday, 03 January 2005

When does my Password Expire?

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;
    }
}