C# / .NET – setting or clearing the Password Never Expires flag for a user
Posted by jpluimers on 2009/10/11
Recently, I had to change the “Password Never Expires” flag for some users on Windows systems.
In the past, there used to be a netuser tool available from WindowsITPro where you could use the pwnexp flag to set or clear that flag.
That tool seems to be vanished, so I was searching for alternatives.
Most alternatives I found depend on some kind of scripting, or the use of the WMIC WMI command line interface: that was “out” because this particular setup is running on Windows XP Embedded, which is trimmed down very much.
The only C# example I found was on CodeProject, but it does
- not take into account the existing flags correctly,
- have hard coded literals without any references where they are from,
- use bit flag arithmetic without letting the C# compiler do its magic with enums,
- use a call to the deprecated InvokeGet method,
- use the Invoke(“Put”, … way of calling that so many people use (which actually should have been an – also deprecated – InvokeSet method),
- use COM Interop
Hence the solution below:
C#, with the proper ADS_USER_FLAG_ENUM enum from the MSDN documentation and no COM Interop, it also moves all literals to constants.
This example does not use any Interop, but it
- requires the System.DirectoryServices.dll assembly (which ships with .NET 2.0 and up),
- uses the System.DirectoryServices namespace,
- and from there the DirectoryEntry class with the Properties property
With those, it becomes more readable, easier to debug and therefore a lot more maintainable.
Note that this code needs full trust security.
In my case that is not a problem, as this code executes while building the Windows XP Embedded image.
// see http://msdn.microsoft.com/en-us/library/aa772300(VS.85).aspx
[Flags]
enum ADS_USER_FLAG_ENUM
{
ADS_UF_SCRIPT = 1, // 0x1
ADS_UF_ACCOUNTDISABLE = 2, // 0x2
ADS_UF_HOMEDIR_REQUIRED = 8, // 0x8
ADS_UF_LOCKOUT = 16, // 0x10
ADS_UF_PASSWD_NOTREQD = 32, // 0x20
ADS_UF_PASSWD_CANT_CHANGE = 64, // 0x40
ADS_UF_ENCRYPTED_TEXT_PASSWORD_ALLOWED = 128, // 0x80
ADS_UF_TEMP_DUPLICATE_ACCOUNT = 256, // 0x100
ADS_UF_NORMAL_ACCOUNT = 512, // 0x200
ADS_UF_INTERDOMAIN_TRUST_ACCOUNT = 2048, // 0x800
ADS_UF_WORKSTATION_TRUST_ACCOUNT = 4096, // 0x1000
ADS_UF_SERVER_TRUST_ACCOUNT = 8192, // 0x2000
ADS_UF_DONT_EXPIRE_PASSWD = 65536, // 0x10000
ADS_UF_MNS_LOGON_ACCOUNT = 131072, // 0x20000
ADS_UF_SMARTCARD_REQUIRED = 262144, // 0x40000
ADS_UF_TRUSTED_FOR_DELEGATION = 524288, // 0x80000
ADS_UF_NOT_DELEGATED = 1048576, // 0x100000
ADS_UF_USE_DES_KEY_ONLY = 2097152, // 0x200000
ADS_UF_DONT_REQUIRE_PREAUTH = 4194304, // 0x400000
ADS_UF_PASSWORD_EXPIRED = 8388608, // 0x800000
ADS_UF_TRUSTED_TO_AUTHENTICATE_FOR_DELEGATION = 16777216 // 0x1000000
} ;
/// <summary>
/// Sets or clears the "Password Never Expires" flag for a user on the current system.
/// This is based on ideas in http://www.codeproject.com/KB/system/OSUserMangement.aspx,
/// but not that the code there is wrong: it does not take into account the current userFlags value
/// The code below does take that into account and is much more maintainable.
///
/// the logic can be made a bit easier by applying sets
/// </summary>
/// <param name="userName">name of the user to set the Password Never Expires flag</param>
/// <param name="passwordNeverExpires">new flag value</param>
protected virtual void setPasswordNeverExpires(string userName, bool passwordNeverExpires)
{
const string userNameString = "userName";
const string userFlagsString = "userFlags";
string machineName = Environment.MachineName;
DirectoryEntry userInThisComputerDirectoryEntry = getUserInThisComputerDirectoryEntry(machineName, userName);
if (null == userInThisComputerDirectoryEntry)
throw new ArgumentException("not found in " + machineName, userNameString);
PropertyValueCollection userFlagsProperties = userInThisComputerDirectoryEntry.Properties[userFlagsString];
ADS_USER_FLAG_ENUM userFlags = (ADS_USER_FLAG_ENUM)(userFlagsProperties.Value);
ADS_USER_FLAG_ENUM newUserFlags = userFlags;
if (passwordNeverExpires)
newUserFlags = newUserFlags | ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD;
else
newUserFlags = newUserFlags & (~ADS_USER_FLAG_ENUM.ADS_UF_DONT_EXPIRE_PASSWD);
userFlagsProperties.Value = newUserFlags;
userInThisComputerDirectoryEntry.CommitChanges();
}
protected virtual DirectoryEntry getUserInThisComputerDirectoryEntry(string machineName, string userName)
{
DirectoryEntry computerDirectoryEntry = getComputerDirectoryEntry(machineName);
const string userSchemaClassName = "user";
return computerDirectoryEntry.Children.Find(userName, userSchemaClassName);
}
protected virtual DirectoryEntry getComputerDirectoryEntry(string machineName)
{
//Initiate DirectoryEntry Class To Connect Through WINNT Protocol
// see: http://msdn.microsoft.com/en-us/library/system.directoryservices.directoryentry.path.aspx
const string pathUsingWinNTComputerMask = "WinNT://{0},computer";
string path = string.Format(pathUsingWinNTComputerMask, machineName);
DirectoryEntry thisComputerDirectoryEntry = new DirectoryEntry(path);
return thisComputerDirectoryEntry;
}
This code is just an example on how you can do it in a maintainable way.
It can easily be extended to other properties as well.
–jeroen
Rate this:
Share this:
- Click to share on Mastodon (Opens in new window) Mastodon
- Click to share on Bluesky (Opens in new window) Bluesky
- Share on Tumblr
- Click to share on Reddit (Opens in new window) Reddit
- Click to share on Threads (Opens in new window) Threads
- Tweet
- Click to share on Telegram (Opens in new window) Telegram
- Click to share on Nextdoor (Opens in new window) Nextdoor
- Click to share on WhatsApp (Opens in new window) WhatsApp
- Click to print (Opens in new window) Print
- Click to email a link to a friend (Opens in new window) Email
Related
This entry was posted on 2009/10/11 at 00:37 and is filed under .NET, C#, C# 2.0, CommandLine, Development, Software Development, XP-embedded. You can follow any responses to this entry through the RSS 2.0 feed. You can leave a response, or trackback from your own site.
This site uses Akismet to reduce spam. Learn how your comment data is processed.






Leave a comment