The RegEx Password Filter Sample
Now that you're aware of all the possible pitfalls, it's high time for code action. This section will walk you through the sample provided with this article. I've created a VS7 solution with the
PasswordFilterRegEx VC project.
As the Password Filter definition requires, you export three functions. Here's the code for the DEF file included within the sample project:
LIBRARY PasswordFilterRegEx
EXPORTS
InitializeChangeNotify
PasswordChangeNotify
PasswordFilter
The
PasswordFilterRegEx.cpp contains source code for the exported functions. The implementations of
InitializeChangeNotify and
PasswordChangeNotify are quite simple:
// Initialization of Password filter.
// This implementation just returns TRUE
// to let LSA know everything is fine
BOOLEAN __stdcall InitializeChangeNotify(void)
{
WriteToLog("InitializeChangeNotify()");
return TRUE;
}
// This function is called by LSA when password
// was successfully changed.
//
// This implementation just returns 0 (Success)
NTSTATUS __stdcall PasswordChangeNotify(
PUNICODE_STRING UserName,
ULONG RelativeId,
PUNICODE_STRING NewPassword
)
{
WriteToLog("PasswordChangeNotify()");
return 0;
}
The bulk of the work is done in the
PasswordFilter function (shown in
Listing 1). First, create a zero-terminating copy of a password string and assign it to an STL wstring object (STL is used in conjunction with the boost regex library):
wszPassword = new wchar_t[Password->Length + 1];
if (NULL == wszPassword)
{
throw E_OUTOFMEMORY;
}
wcsncpy(wszPassword, Password->Buffer, Password->Length);
wszPassword[Password->Length] = 0;
WriteToLog("Going to check password");
// Initialize STL string
wstrPassword = wszPassword;
Next, the regular expression is instantiated. The sample Password Filter reads the regular expression from the
RegEx value of the following registry key:
HKEY_LOCAL_MACHINE\\Software\\DevX\\PasswordFilter
If the value is not found in registry, the dummy default regular expression ("^(A)$") is used.
Finally, validate the password against the regular expression and return the results to the caller (LSA):
WriteToLog("Going to run match");
// Prepare iterators
wstring::const_iterator start = wstrPassword.begin();
wstring::const_iterator end = wstrPassword.end();
match_results<wstring::const_iterator> what;
unsigned int flags = match_default;
bMatch = regex_match(start, end, what, wrePassword);
if (bMatch)
{
WriteToLog("Password matches specified RegEx");
}
else
{
WriteToLog("Password does NOT match specified RegEx");
}
. . .
return bMatch;
Just before you return the results to LSA, perform memory clean-up:
// Erase all temporary password data
// for security reasons
wstrPassword.replace(0, wstrPassword.length(), wstrPassword.length(),
(wchar_t)'?');
wstrPassword.erase();
if (NULL != wszPassword)
{
ZeroMemory(wszPassword, Password->Length);
// Assure that there is no compiler optimizations and read random byte
// from cleaned password string
srand(time(NULL));
wchar_t wch = wszPassword[rand() % Password->Length];
delete [] wszPassword;
wszPassword = NULL;
}
return bMatch;