I have written this JScript to validate Local Windows User Accounts:
function ValidateCredentials(strUsername, strPassword) {
var ADS_SECURE_AUTHENTICATION = 1;
var objWMISvc = GetObject("winmgmts:\\\\.\\root\\cimv2");
var colItems = objWMISvc.ExecQuery( "Select * from Win32_ComputerSystem");
for (var it = new Enumerator(colItems); !it.atEnd(); it.moveNext()) {
var objItem = it.item();
if (objItem.PartOfDomain)
continue;
var strWorkgroup = objItem.Domain;
var strComputer = objItem.Name;
var strPath = "WinNT://" + strWorkgroup + "/" + strComputer + "/" +
strUsername + ",user";
try {
var objIADS = GetObject("WinNT:").OpenDSObject(strPath, strUsername,
strPassword, ADS_SECURE_AUTHENTICATION);
WScript.Echo("OK");
} catch(e) {
WScript.Echo("Invalid Username/Password");
}
}
}
ValidateCredentials(WScript.Arguments(0), WScript.Arguments(1));
It works fine when I run it from the command prompt, both as an administrator as well as an ordinary user. When the script is invoked by a service process running as the LocalSystem user, it doesn't work. Instead the call to OpenDSObject then raises an exception with an error-code of -2147023584 (A specified logon session does not exist. It may already have been terminated).
What's the problem here, I thought the LocalSystem account is actually more privileged or trusted than administrator accounts?
You can try out the script for yourself by saving it to some .js file and then run it from a command prompt like this:
cscript.exe validate.js myUsername myPassword
If you just want to validate a password, I'd avoid all of the above. There's a somewhat different method that works nicely from an unprivileged account. In fact, you want to ensure the account doesn't actually have the right to change the password.
With this method, you call NetUserChangePassword
, which requires you to specify the user's existing password in order to authenticate before changing it.
When you do this from an account that doesn't have the right to change that user's password, it will obviously fail (so their password doesn't change). What we care about is exactly how it fails. If it fails by returning ERROR_INVALID_PASSWORD
, then you know the password was wrong. If it fails by returning ERROR_ACCESS_DENIED
, the password was correct, and the function failed because the account you're calling it from doesn't have the right to change that user's password.
So, rather than requiring a highly privileged account like LocalSystem or an administrator, this allows you to do the job from an account with essentially no rights at all (by strong preference, not even the right to change its own password).