diff --git a/src/NativeUnitTests/NativeUnitTests.vcxproj b/src/NativeUnitTests/NativeUnitTests.vcxproj
index 55a3bc0..9a56858 100644
--- a/src/NativeUnitTests/NativeUnitTests.vcxproj
+++ b/src/NativeUnitTests/NativeUnitTests.vcxproj
@@ -164,9 +164,11 @@
+
+
Create
Create
diff --git a/src/NativeUnitTests/NativeUnitTests.vcxproj.filters b/src/NativeUnitTests/NativeUnitTests.vcxproj.filters
index e0e5c4a..21f52e9 100644
--- a/src/NativeUnitTests/NativeUnitTests.vcxproj.filters
+++ b/src/NativeUnitTests/NativeUnitTests.vcxproj.filters
@@ -62,5 +62,11 @@
Source Files
+
+ Source Files
+
+
+ Source Files
+
\ No newline at end of file
diff --git a/src/NativeUnitTests/PasswordEvaluatorInScopeTests.cpp b/src/NativeUnitTests/PasswordEvaluatorInScopeTests.cpp
new file mode 100644
index 0000000..498110b
--- /dev/null
+++ b/src/NativeUnitTests/PasswordEvaluatorInScopeTests.cpp
@@ -0,0 +1,191 @@
+#include "stdafx.h"
+#include "CppUnitTest.h"
+#include "passwordevaluator.h"
+#include
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+static void SetRegistryExcludedAccounts(const std::vector& accounts)
+{
+ HKEY hKey;
+ LONG result = RegCreateKeyEx(HKEY_LOCAL_MACHINE, REG_BASE_SETTINGS_KEY, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
+ if (result == ERROR_SUCCESS)
+ {
+ // Calculate the size of the MULTI_SZ string
+ size_t totalSize = 0;
+ for (const auto& account : accounts)
+ {
+ totalSize += (account.size() + 1) * sizeof(wchar_t);
+ }
+ totalSize += sizeof(wchar_t); // For the final null terminator
+
+ // Create the MULTI_SZ string
+ std::vector multiSz(totalSize / sizeof(wchar_t));
+ wchar_t* ptr = multiSz.data();
+ for (const auto& account : accounts)
+ {
+ wcscpy_s(ptr, account.size() + 1, account.c_str());
+ ptr += account.size() + 1;
+ }
+ *ptr = L'\0'; // Final null terminator
+
+ // Set the registry value
+ RegSetValueEx(hKey, REG_VALUE_EXCLUDEDACCOUNTS, 0, REG_MULTI_SZ, (const BYTE*)multiSz.data(), totalSize);
+ RegCloseKey(hKey);
+ }
+}
+
+static void SetRegistryIncludedAccounts(const std::vector& accounts)
+{
+ HKEY hKey;
+ LONG result = RegCreateKeyEx(HKEY_LOCAL_MACHINE, REG_BASE_SETTINGS_KEY, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
+ if (result == ERROR_SUCCESS)
+ {
+ // Calculate the size of the MULTI_SZ string
+ size_t totalSize = 0;
+ for (const auto& account : accounts)
+ {
+ totalSize += (account.size() + 1) * sizeof(wchar_t);
+ }
+ totalSize += sizeof(wchar_t); // For the final null terminator
+
+ // Create the MULTI_SZ string
+ std::vector multiSz(totalSize / sizeof(wchar_t));
+ wchar_t* ptr = multiSz.data();
+ for (const auto& account : accounts)
+ {
+ wcscpy_s(ptr, account.size() + 1, account.c_str());
+ ptr += account.size() + 1;
+ }
+ *ptr = L'\0'; // Final null terminator
+
+ // Set the registry value
+ RegSetValueEx(hKey, REG_VALUE_INCLUDEDACCOUNTS, 0, REG_MULTI_SZ, (const BYTE*)multiSz.data(), totalSize);
+ RegCloseKey(hKey);
+ }
+}
+
+static void DeleteRegistryValues()
+{
+ HKEY hKey;
+ LONG result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, REG_BASE_SETTINGS_KEY, 0, KEY_WRITE, &hKey);
+ if (result == ERROR_SUCCESS)
+ {
+ RegDeleteValue(hKey, REG_VALUE_EXCLUDEDACCOUNTS);
+ RegDeleteValue(hKey, REG_VALUE_INCLUDEDACCOUNTS);
+ RegCloseKey(hKey);
+ }
+}
+
+namespace NativeUnitTests
+{
+ TEST_CLASS(PasswordEvaluatorInScopeTests)
+ {
+ public:
+
+ TEST_METHOD_INITIALIZE(CleanupRegistry)
+ {
+ DeleteRegistryValues();
+ }
+
+ TEST_METHOD(TestIsUserInScope_ExcludedAccount)
+ {
+ std::wstring accountName = L"krbtgt";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsFalse(result);
+ }
+
+ TEST_METHOD(TestIsUserInScope_NotExcludedAccount)
+ {
+ std::wstring accountName = L"testuser";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsTrue(result);
+ }
+
+ TEST_METHOD(TestIsUserInScope_ExcludedAccountFromRegistry)
+ {
+ std::vector excludedAccounts = { L"excludeduser" };
+ SetRegistryExcludedAccounts(excludedAccounts);
+
+ std::wstring accountName = L"excludeduser";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsFalse(result);
+ }
+
+ TEST_METHOD(TestIsUserInScope_MultipleExcludedAccountsFromRegistry)
+ {
+ std::vector excludedAccounts = { L"excludeduser1", L"excludeduser2", L"excludeduser3" };
+ SetRegistryExcludedAccounts(excludedAccounts);
+
+ std::wstring accountName1 = L"excludeduser1";
+ BOOLEAN result1 = IsUserInScope(accountName1);
+ Assert::IsFalse(result1);
+
+ std::wstring accountName2 = L"excludeduser2";
+ BOOLEAN result2 = IsUserInScope(accountName2);
+ Assert::IsFalse(result2);
+
+ std::wstring accountName3 = L"excludeduser3";
+ BOOLEAN result3 = IsUserInScope(accountName3);
+ Assert::IsFalse(result3);
+
+ std::wstring accountName4 = L"includeduser";
+ BOOLEAN result4 = IsUserInScope(accountName4);
+ Assert::IsTrue(result4);
+ }
+
+ TEST_METHOD(TestIsUserInScope_NotExcludedAccountFromRegistry)
+ {
+ std::vector excludedAccounts = { L"excludeduser1", L"excludeduser2", L"excludeduser3" };
+ SetRegistryExcludedAccounts(excludedAccounts);
+
+ std::wstring accountName = L"includeduser";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsTrue(result);
+ }
+
+ // New test cases for 'included user' logic
+
+ TEST_METHOD(TestIsUserInScope_IncludedAccount)
+ {
+ std::vector includedAccounts = { L"includeduser" };
+ SetRegistryIncludedAccounts(includedAccounts);
+
+ std::wstring accountName = L"includeduser";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsTrue(result);
+ }
+
+ TEST_METHOD(TestIsUserInScope_IncludedAccountListEmpty)
+ {
+ std::vector includedAccounts = {};
+ SetRegistryIncludedAccounts(includedAccounts);
+
+ std::wstring accountName = L"anyuser";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsTrue(result);
+ }
+
+ TEST_METHOD(TestIsUserInScope_NotIncludedAccount)
+ {
+ std::vector includedAccounts = { L"includeduser1", L"includeduser2" };
+ SetRegistryIncludedAccounts(includedAccounts);
+
+ std::wstring accountName = L"notincludeduser";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsFalse(result);
+ }
+
+ TEST_METHOD(TestIsUserInScope_IncludedAndExcludedAccount)
+ {
+ std::vector includedAccounts = { L"includeduser" };
+ std::vector excludedAccounts = { L"includeduser" };
+ SetRegistryIncludedAccounts(includedAccounts);
+ SetRegistryExcludedAccounts(excludedAccounts);
+
+ std::wstring accountName = L"includeduser";
+ BOOLEAN result = IsUserInScope(accountName);
+ Assert::IsFalse(result);
+ }
+ };
+}
diff --git a/src/NativeUnitTests/RegistryTests.cpp b/src/NativeUnitTests/RegistryTests.cpp
new file mode 100644
index 0000000..d64ca17
--- /dev/null
+++ b/src/NativeUnitTests/RegistryTests.cpp
@@ -0,0 +1,98 @@
+#include "stdafx.h"
+#include "CppUnitTest.h"
+#include "registry.h"
+#include
+
+using namespace Microsoft::VisualStudio::CppUnitTestFramework;
+
+namespace NativeUnitTests
+{
+ TEST_CLASS(RegistryTests)
+ {
+ public:
+
+ TEST_METHOD(TestGetRegValueString)
+ {
+ registry reg;
+ std::wstring defaultValue = L"default";
+ std::wstring value = reg.GetRegValue(L"NonExistentValue", defaultValue);
+ Assert::AreEqual(defaultValue, value);
+ }
+
+ TEST_METHOD(TestGetRegValueDWORD)
+ {
+ registry reg;
+ DWORD defaultValue = 1234;
+ DWORD value = reg.GetRegValue(L"NonExistentValue", defaultValue);
+ Assert::AreEqual(defaultValue, value);
+ }
+
+ TEST_METHOD(TestGetRegValueMultiString)
+ {
+ registry reg;
+ std::vector defaultValue = { L"default1", L"default2" };
+ std::vector value = reg.GetRegValue(L"NonExistentValue", REG_DEFAULT_MAX_ITEMS, defaultValue);
+ Assert::IsTrue(defaultValue == value);
+ }
+
+ TEST_METHOD(TestGetRegistryForUser)
+ {
+ registry reg = registry::GetRegistryForUser(L"testuser");
+ Assert::IsNotNull(®);
+ }
+
+ TEST_METHOD(TestSetAndGetRegValueString)
+ {
+ HKEY hKey;
+ LPCWSTR subKey = REG_BASE_SETTINGS_KEY;
+ LPCWSTR valueName = L"TestString";
+ std::wstring setValue = L"TestValue";
+
+ RegCreateKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
+ RegSetValueEx(hKey, valueName, 0, REG_SZ, (const BYTE*)setValue.c_str(), (setValue.size() + 1) * sizeof(wchar_t));
+ RegCloseKey(hKey);
+
+ registry reg;
+ std::wstring getValue = reg.GetRegValue(valueName, L"");
+ Assert::AreEqual(setValue, getValue);
+ }
+
+ TEST_METHOD(TestSetAndGetRegValueDWORD)
+ {
+ HKEY hKey;
+ LPCWSTR subKey = REG_BASE_SETTINGS_KEY;
+ LPCWSTR valueName = L"TestDWORD";
+ DWORD setValue = 5678;
+
+ RegCreateKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
+ RegSetValueEx(hKey, valueName, 0, REG_DWORD, (const BYTE*)&setValue, sizeof(setValue));
+ RegCloseKey(hKey);
+
+ registry reg;
+ DWORD getValue = reg.GetRegValue(valueName, 0);
+ Assert::AreEqual(setValue, getValue);
+ }
+
+ TEST_METHOD(TestSetAndGetRegValueMultiString)
+ {
+ HKEY hKey;
+ LPCWSTR subKey = REG_BASE_SETTINGS_KEY;
+ LPCWSTR valueName = L"TestMultiString";
+ std::vector setValue = { L"Value1", L"Value2" };
+ std::wstring multiString;
+ for (const auto& str : setValue)
+ {
+ multiString.append(str).append(1, L'\0');
+ }
+ multiString.append(1, L'\0');
+
+ RegCreateKeyEx(HKEY_LOCAL_MACHINE, subKey, 0, NULL, 0, KEY_WRITE, NULL, &hKey, NULL);
+ RegSetValueEx(hKey, valueName, 0, REG_MULTI_SZ, (const BYTE*)multiString.c_str(), (multiString.size() + 1) * sizeof(wchar_t));
+ RegCloseKey(hKey);
+
+ registry reg;
+ std::vector getValue = reg.GetRegValue(valueName, REG_DEFAULT_MAX_ITEMS, std::vector());
+ Assert::IsTrue(std::equal(setValue.begin(), setValue.end(), getValue.begin(), getValue.end()));
+ }
+ };
+}
diff --git a/src/PasswordFilter/filter.cpp b/src/PasswordFilter/filter.cpp
index 4ead0bd..a600bd0 100644
--- a/src/PasswordFilter/filter.cpp
+++ b/src/PasswordFilter/filter.cpp
@@ -64,6 +64,12 @@ extern "C" __declspec(dllexport) BOOLEAN __stdcall PasswordFilter(
simulate = reg.GetRegValue(REG_VALUE_AUDITONLY, 0) != 0;
std::wstring accountName(AccountName->Buffer, AccountName->Length / sizeof(WCHAR));
+
+ if (!IsUserInScope(accountName))
+ {
+ return TRUE;
+ }
+
std::wstring fullName(FullName->Buffer, FullName->Length / sizeof(WCHAR));
SecureArrayT password = UnicodeStringToWcharArray(*Password);
@@ -146,6 +152,12 @@ extern "C" __declspec(dllexport) int __stdcall PasswordFilterEx(
}
std::wstring accountName = AccountName;
+
+ if (!IsUserInScope(accountName))
+ {
+ return PASSWORD_APPROVED;
+ }
+
std::wstring fullName = FullName;
SecureArrayT password = StringToWcharArray(Password);
diff --git a/src/PasswordFilter/messages.h b/src/PasswordFilter/messages.h
index dd18649..c7b913c 100644
--- a/src/PasswordFilter/messages.h
+++ b/src/PasswordFilter/messages.h
@@ -105,12 +105,43 @@
//
// MessageText:
//
-// There was a problem opening the store file. Check that the store folder exists and is accessible.\n
+// There was a problem opening the store file. Check that the store folder exists and is accessible.
// Error code: %1
// Path: %2
//
#define MSG_STOREERROR ((DWORD)0xC0020009L)
+//
+// MessageId: MSG_USEREXCLUDED
+//
+// MessageText:
+//
+// The user %1 was in the exclusion list and was not evaluated by the password filter.
+//
+#define MSG_USEREXCLUDED ((DWORD)0x4002000AL)
+
+//
+// MessageId: MSG_USERNOTINCLUDED
+//
+// MessageText:
+//
+// The user %1 was not in the inclusion list and was not evaluated by the password filter.
+//
+#define MSG_USERNOTINCLUDED ((DWORD)0x4002000BL)
+
+//
+// MessageId: MSG_REG_READ_ERROR
+//
+// MessageText:
+//
+// A registry read error occurred.
+// Error code: %1
+// Key: %2
+// Value: %3
+// Type: %4
+//
+#define MSG_REG_READ_ERROR ((DWORD)0xC002000CL)
+
//
// MessageId: MSG_PASSWORD_REJECTED_ON_ERROR
//
diff --git a/src/PasswordFilter/messages.mc b/src/PasswordFilter/messages.mc
index 96641fe..0dc582b 100644
--- a/src/PasswordFilter/messages.mc
+++ b/src/PasswordFilter/messages.mc
@@ -77,6 +77,34 @@ Error code: %1
Path: %2
.
+MessageId=0xA
+Severity=Informational
+Facility=Runtime
+SymbolicName=MSG_USEREXCLUDED
+Language=English
+The user %1 was in the exclusion list and was not evaluated by the password filter.
+.
+
+MessageId=0xB
+Severity=Informational
+Facility=Runtime
+SymbolicName=MSG_USERNOTINCLUDED
+Language=English
+The user %1 was not in the inclusion list and was not evaluated by the password filter.
+.
+
+MessageId=0xC
+Severity=Error
+Facility=Runtime
+SymbolicName=MSG_REG_READ_ERROR
+Language=English
+A registry read error occurred.
+Error code: %1
+Key: %2
+Value: %3
+Type: %4
+.
+
MessageId=0x2001
Severity=Warning
Facility=System
diff --git a/src/PasswordFilter/messages.res b/src/PasswordFilter/messages.res
index 482025c..3ab06d5 100644
Binary files a/src/PasswordFilter/messages.res and b/src/PasswordFilter/messages.res differ
diff --git a/src/PasswordFilter/passwordevaluator.cpp b/src/PasswordFilter/passwordevaluator.cpp
index 664ad15..eee54c8 100644
--- a/src/PasswordFilter/passwordevaluator.cpp
+++ b/src/PasswordFilter/passwordevaluator.cpp
@@ -9,8 +9,50 @@
#include "utils.h"
#include "complexityevaluator.h"
#include "v3store.h"
+#include
-int ProcessPassword(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation)
+BOOLEAN IsUserInScope(const std::wstring& accountName)
+{
+ if (_wcsicmp(accountName.c_str(), L"krbtgt") == 0)
+ {
+ eventlog::getInstance().logw(EVENTLOG_INFORMATION_TYPE, MSG_USEREXCLUDED, 1, accountName.c_str());
+ return FALSE;
+ }
+
+ registry reg;
+
+ for (const auto& excludedAccount : reg.GetRegValue(REG_VALUE_EXCLUDEDACCOUNTS, REG_DEFAULT_MAX_ITEMS, std::vector()))
+ {
+ if (_wcsicmp(accountName.c_str(), excludedAccount.c_str()) == 0)
+ {
+ eventlog::getInstance().logw(EVENTLOG_INFORMATION_TYPE, MSG_USEREXCLUDED, 1, accountName.c_str());
+ return FALSE;
+ }
+ }
+
+ // See if we have an INCLUDEDACCOUNTS value. If it is present, then only allow those accounts to be processed. If it is blank, allow all accounts.
+
+ std::vector includedAccounts = reg.GetRegValue(REG_VALUE_INCLUDEDACCOUNTS, REG_DEFAULT_MAX_ITEMS, std::vector());
+
+ if (!includedAccounts.empty())
+ {
+ for (const auto& includedAccount : includedAccounts)
+ {
+ if (_wcsicmp(accountName.c_str(), includedAccount.c_str()) == 0)
+ {
+ return TRUE;
+ }
+ }
+
+ eventlog::getInstance().logw(EVENTLOG_INFORMATION_TYPE, MSG_USERNOTINCLUDED, 1, accountName.c_str());
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+int ProcessPassword(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation)
{
eventlog::getInstance().logw(EVENTLOG_INFORMATION_TYPE, MSG_PROCESSING_REQUEST, 3, setOperation ? L"set" : L"change", accountName.c_str(), fullName.c_str());
@@ -73,9 +115,9 @@ int ProcessPassword(const SecureArrayT &password, const std::wstring &acc
return PASSWORD_APPROVED;
}
-BOOLEAN ProcessPasswordRaw(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordRaw(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
- if ((setOperation && reg.GetRegValue(REG_VALUE_CHECKBANNEDPASSWORDONSET, 0) != 0) ||
+ if ((setOperation && reg.GetRegValue(REG_VALUE_CHECKBANNEDPASSWORDONSET, 0) != 0) ||
(!setOperation && reg.GetRegValue(REG_VALUE_CHECKBANNEDPASSWORDONCHANGE, 0) != 0))
{
OutputDebugString(L"Checking raw password");
@@ -93,10 +135,10 @@ BOOLEAN ProcessPasswordRaw(const SecureArrayT &password, const std::wstri
return TRUE;
}
-BOOLEAN ProcessPasswordNormalizedPasswordStore(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordNormalizedPasswordStore(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
if (setOperation && reg.GetRegValue(REG_VALUE_CHECKNORMALIZEDBANNEDPASSWORDONSET, 0) != 0 ||
- !setOperation && reg.GetRegValue(REG_VALUE_CHECKNORMALIZEDBANNEDPASSWORDONCHANGE, 0) != 0 )
+ !setOperation && reg.GetRegValue(REG_VALUE_CHECKNORMALIZEDBANNEDPASSWORDONCHANGE, 0) != 0)
{
bool result = TRUE;
@@ -121,7 +163,7 @@ BOOLEAN ProcessPasswordNormalizedPasswordStore(const SecureArrayT &passwo
}
-BOOLEAN ProcessPasswordNormalizedWordStore(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordNormalizedWordStore(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
if (setOperation && reg.GetRegValue(REG_VALUE_CHECKNORMALIZEDBANNEDWORDONSET, 0) != 0 ||
!setOperation && reg.GetRegValue(REG_VALUE_CHECKNORMALIZEDBANNEDWORDONCHANGE, 0) != 0)
@@ -149,7 +191,7 @@ BOOLEAN ProcessPasswordNormalizedWordStore(const SecureArrayT &password,
}
-BOOLEAN ProcessPasswordLength(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordLength(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
const int minLength = reg.GetRegValue(REG_VALUE_MINIMUMLENGTH, 0);
@@ -170,7 +212,7 @@ BOOLEAN ProcessPasswordLength(const SecureArrayT &password, const std::ws
return TRUE;
}
-BOOLEAN ProcessPasswordDoesntContainAccountName(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordDoesntContainAccountName(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
int flag = reg.GetRegValue(REG_VALUE_PASSWORDDOESNTCONTAINACCOUNTNAME, 0);
@@ -178,7 +220,7 @@ BOOLEAN ProcessPasswordDoesntContainAccountName(const SecureArrayT &passw
{
OutputDebugString(L"Checking to see if password contains account name");
- for each (std::wstring substring in SplitString(accountName, L' '))
+ for each(std::wstring substring in SplitString(accountName, L' '))
{
if (substring.length() > 3)
{
@@ -197,7 +239,7 @@ BOOLEAN ProcessPasswordDoesntContainAccountName(const SecureArrayT &passw
return TRUE;
}
-BOOLEAN ProcessPasswordDoesntContainFullName(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordDoesntContainFullName(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
int flag = reg.GetRegValue(REG_VALUE_PASSWORDDOESNTCONTAINFULLNAME, 0);
@@ -205,7 +247,7 @@ BOOLEAN ProcessPasswordDoesntContainFullName(const SecureArrayT &password
{
OutputDebugString(L"Checking to see if password contains full name");
- for each (std::wstring substring in SplitString(fullName, L' '))
+ for each(std::wstring substring in SplitString(fullName, L' '))
{
if (substring.length() > 3)
{
@@ -224,7 +266,7 @@ BOOLEAN ProcessPasswordDoesntContainFullName(const SecureArrayT &password
return TRUE;
}
-BOOLEAN ProcessPasswordRegexApprove(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordRegexApprove(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
std::wstring regex = reg.GetRegValue(REG_VALUE_REGEXAPPROVE, L"");
@@ -247,7 +289,7 @@ BOOLEAN ProcessPasswordRegexApprove(const SecureArrayT &password, const s
return TRUE;
}
-BOOLEAN ProcessPasswordRegexReject(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®)
+BOOLEAN ProcessPasswordRegexReject(const SecureArrayT& password, const std::wstring& accountName, const std::wstring& fullName, const BOOLEAN& setOperation, const registry& reg)
{
std::wstring regex = reg.GetRegValue(REG_VALUE_REGEXREJECT, L"");
diff --git a/src/PasswordFilter/passwordevaluator.h b/src/PasswordFilter/passwordevaluator.h
index 54a896e..b4c4e82 100644
--- a/src/PasswordFilter/passwordevaluator.h
+++ b/src/PasswordFilter/passwordevaluator.h
@@ -16,6 +16,8 @@ static const int PASSWORD_REJECTED_BANNED_NORMALIZED_WORD = 10;
static const int PASSWORD_REJECTED_BLANK = 11;
static const int FILTER_ERROR = 100;
+BOOLEAN IsUserInScope(const std::wstring& accountName);
+
int ProcessPassword(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation);
BOOLEAN ProcessPasswordRaw(const SecureArrayT &password, const std::wstring &accountName, const std::wstring &fullName, const BOOLEAN &setOperation, const registry ®);
diff --git a/src/PasswordFilter/registry.cpp b/src/PasswordFilter/registry.cpp
index e039faa..b2e2fa9 100644
--- a/src/PasswordFilter/registry.cpp
+++ b/src/PasswordFilter/registry.cpp
@@ -1,6 +1,9 @@
#include "stdafx.h"
#include "registry.h"
#include
+#include
+#include "eventlog.h"
+#include "messages.h"
registry::registry()
{
@@ -18,22 +21,27 @@ registry::registry(std::wstring policyGroup)
registry::~registry()
= default;
-std::wstring registry::GetRegValue(const std::wstring & valueName, const std::wstring & defaultValue) const
+std::wstring registry::GetRegValue(const std::wstring& valueName, const std::wstring& defaultValue) const
{
return GetPolicyOrSettingsValue(valueName, defaultValue);
}
-DWORD registry::GetRegValue(const std::wstring & valueName, DWORD defaultValue) const
+std::vector registry::GetRegValue(const std::wstring& valueName, DWORD maxItems, const std::vector& defaultValue) const
+{
+ return GetPolicyOrSettingsValue(maxItems, valueName, defaultValue);
+}
+
+DWORD registry::GetRegValue(const std::wstring& valueName, DWORD defaultValue) const
{
return GetPolicyOrSettingsValue(valueName, defaultValue);
}
-registry registry::GetRegistryForUser(const std::wstring & user)
+registry registry::GetRegistryForUser(const std::wstring& user)
{
return registry(L"default");
}
-DWORD registry::GetPolicyOrSettingsValue(const std::wstring & valueName, DWORD defaultValue) const
+DWORD registry::GetPolicyOrSettingsValue(const std::wstring& valueName, DWORD defaultValue) const
{
DWORD dwBufferSize(sizeof(DWORD));
DWORD value(0);
@@ -67,7 +75,7 @@ DWORD registry::GetPolicyOrSettingsValue(const std::wstring & valueName, DWORD d
return defaultValue;
}
-const std::wstring registry::GetKeyName(LPCWSTR & key) const
+const std::wstring registry::GetKeyName(LPCWSTR& key) const
{
std::wstring name(key);
if (!this->policyGroup.empty())
@@ -79,7 +87,7 @@ const std::wstring registry::GetKeyName(LPCWSTR & key) const
return name;
}
-const std::wstring registry::GetPolicyOrSettingsValue(const std::wstring & valueName, const std::wstring & defaultValue) const
+const std::wstring registry::GetPolicyOrSettingsValue(const std::wstring& valueName, const std::wstring& defaultValue) const
{
DWORD dwBufferSize = 0;
@@ -112,7 +120,89 @@ const std::wstring registry::GetPolicyOrSettingsValue(const std::wstring & value
return defaultValue;
}
-const std::wstring registry::GetValueString(DWORD & dwBufferSize, const std::wstring & keyName, const std::wstring & valueName, const std::wstring & defaultValue) const
+const std::vector registry::GetPolicyOrSettingsValue(DWORD maxItems, const std::wstring& valueName, const std::vector& defaultValue) const
+{
+ DWORD dwBufferSize = 0;
+
+ long result = RegGetValue(HKEY_LOCAL_MACHINE,
+ this->policyKeyName.c_str(),
+ valueName.c_str(),
+ RRF_RT_REG_MULTI_SZ,
+ NULL,
+ NULL,
+ &dwBufferSize);
+
+ if (result == ERROR_SUCCESS)
+ {
+ return GetValueMultiString(dwBufferSize, maxItems, this->policyKeyName, valueName, defaultValue);
+ }
+
+ result = RegGetValue(HKEY_LOCAL_MACHINE,
+ this->settingsKeyName.c_str(),
+ valueName.c_str(),
+ RRF_RT_REG_MULTI_SZ,
+ NULL,
+ NULL,
+ &dwBufferSize);
+
+ if (result == ERROR_SUCCESS)
+ {
+ return GetValueMultiString(dwBufferSize, maxItems, this->settingsKeyName, valueName, defaultValue);
+ }
+
+ return defaultValue;
+}
+
+const std::vector registry::GetValueMultiString(DWORD& dwBufferSize, DWORD maxItems, const std::wstring& keyName, const std::wstring& valueName, const std::vector& defaultValue) const
+{
+ std::vector result;
+
+ std::vector data;
+ data.resize(dwBufferSize / sizeof(wchar_t));
+
+ DWORD lResult = RegGetValue(HKEY_LOCAL_MACHINE,
+ keyName.c_str(),
+ valueName.c_str(),
+ RRF_RT_REG_MULTI_SZ,
+ NULL,
+ &data[0],
+ &dwBufferSize
+ );
+
+ if (lResult != ERROR_SUCCESS)
+ {
+ eventlog::getInstance().logw(EVENTLOG_ERROR_TYPE, MSG_REG_READ_ERROR, 4, std::to_wstring(lResult).c_str(), keyName.c_str(), valueName.c_str(), L"REG_MULTI_SZ");
+ return defaultValue;
+ }
+
+ data.resize(dwBufferSize / sizeof(wchar_t));
+
+ std::wstring current;
+ for (DWORD i = 0; i < dwBufferSize / sizeof(WCHAR); i++)
+ {
+ if (data[i] == L'\0')
+ {
+ if (!current.empty())
+ {
+ if (result.size() >= maxItems)
+ {
+ break;
+ }
+
+ result.push_back(current);
+ current.clear();
+ }
+ }
+ else
+ {
+ current += data[i];
+ }
+ }
+
+ return result;
+}
+
+const std::wstring registry::GetValueString(DWORD& dwBufferSize, const std::wstring& keyName, const std::wstring& valueName, const std::wstring& defaultValue) const
{
std::unique_ptr szBuffer = std::make_unique(dwBufferSize / sizeof(WCHAR));
@@ -126,6 +216,7 @@ const std::wstring registry::GetValueString(DWORD & dwBufferSize, const std::wst
if (result != ERROR_SUCCESS)
{
+ eventlog::getInstance().logw(EVENTLOG_ERROR_TYPE, MSG_REG_READ_ERROR, 4, std::to_wstring(result).c_str(), keyName.c_str(), valueName.c_str(), L"REG_SZ");
return defaultValue;
}
diff --git a/src/PasswordFilter/registry.h b/src/PasswordFilter/registry.h
index 0cc6082..3aa7cfc 100644
--- a/src/PasswordFilter/registry.h
+++ b/src/PasswordFilter/registry.h
@@ -1,12 +1,16 @@
#pragma once
#include "stdafx.h"
+#include
+static DWORD REG_DEFAULT_MAX_ITEMS = 32;
static LPCWSTR REG_BASE_SETTINGS_KEY = L"SOFTWARE\\Lithnet\\PasswordFilter";
static LPCWSTR REG_BASE_POLICY_KEY = L"SOFTWARE\\Policies\\Lithnet\\PasswordFilter";
static LPCWSTR REG_VALUE_STOREPATH = L"Store";
static LPCWSTR REG_VALUE_DISABLED = L"Disabled";
static LPCWSTR REG_VALUE_AUDITONLY = L"AuditOnly";
+static LPCWSTR REG_VALUE_EXCLUDEDACCOUNTS = L"ExcludedAccounts";
+static LPCWSTR REG_VALUE_INCLUDEDACCOUNTS = L"IncludedAccounts";
static LPCWSTR REG_VALUE_MINIMUMLENGTH = L"MinimumLength";
@@ -76,15 +80,18 @@ class registry
registry();
registry(std::wstring policyGroup);
~registry();
- std::wstring GetRegValue(const std::wstring & valueName, const std::wstring & defaultValue) const;
- DWORD GetRegValue(const std::wstring & valueName, DWORD defaultValue) const;
- static registry GetRegistryForUser(const std::wstring & user);
+ std::wstring GetRegValue(const std::wstring& valueName, const std::wstring& defaultValue) const;
+ DWORD GetRegValue(const std::wstring& valueName, DWORD defaultValue) const;
+ std::vector registry::GetRegValue(const std::wstring& valueName, DWORD maxItems, const std::vector& defaultValue) const;
+ static registry GetRegistryForUser(const std::wstring& user);
private:
- DWORD GetPolicyOrSettingsValue(const std::wstring & valueName, DWORD defaultValue) const;
const std::wstring GetKeyName(LPCWSTR& key) const;
- const std::wstring GetPolicyOrSettingsValue(const std::wstring & valueName, const std::wstring & defaultValue) const;
- const std::wstring GetValueString(DWORD & dwBufferSize, const std::wstring & keyName, const std::wstring & valueName, const std::wstring & defaultValue) const;
+ DWORD GetPolicyOrSettingsValue(const std::wstring& valueName, DWORD defaultValue) const;
+ const std::vector GetPolicyOrSettingsValue(DWORD maxItems, const std::wstring& valueName, const std::vector< std::wstring>& defaultValue) const;
+ const std::wstring GetPolicyOrSettingsValue(const std::wstring& valueName, const std::wstring& defaultValue) const;
+ const std::wstring GetValueString(DWORD& dwBufferSize, const std::wstring& keyName, const std::wstring& valueName, const std::wstring& defaultValue) const;
+ const std::vector GetValueMultiString(DWORD& dwBufferSize, DWORD maxItems, const std::wstring& keyName, const std::wstring& valueName, const std::vector& defaultValue) const;
};
diff --git a/src/PasswordProtection/PolicyDefinitions/en-US/lithnet.activedirectory.passwordfilter.adml b/src/PasswordProtection/PolicyDefinitions/en-US/lithnet.activedirectory.passwordfilter.adml
index 121faf0..7162179 100644
--- a/src/PasswordProtection/PolicyDefinitions/en-US/lithnet.activedirectory.passwordfilter.adml
+++ b/src/PasswordProtection/PolicyDefinitions/en-US/lithnet.activedirectory.passwordfilter.adml
@@ -10,7 +10,13 @@
Disable password filter
When enabled, prevents the password filter from evaluating password change requests. If disabled, or set to not configured, the password filter will evaluate password change requests
- Minimum password length
+ Exclude user accounts from password filtering
+ When enabled, the password filter will ignore password changes for the listed accounts. A maximum of 32 accounts can be listed. If disabled, or set to not configured, the password filter will evaluate password change requests for all accounts
+
+ Enable password filtering only on specified user accounts
+ When enabled, the password filter will ONLY process password changes for the listed accounts. A maximum of 32 accounts can be listed. If disabled, or set to not configured, the password filter will evaluate password change requests for all accounts. Note if a user is added to the exclusion list, this takes priority over the inclusion list
+
+ Minimum password length
When enabled, specifies the minimum password length to enforce. If disabled or not configured, no minimum password length is enforced
Passwords must meet specified number of complexity points
@@ -85,7 +91,15 @@ If this policy disabled, or set to not configured, the password filter will not
-
+
+ List of user account names to exclude (one per line, samAccountName only)
+
+
+
+ List of user account names to include (one per line, samAccountName only)
+
+
+
Minimum password length
diff --git a/src/PasswordProtection/PolicyDefinitions/lithnet.activedirectory.passwordfilter.admx b/src/PasswordProtection/PolicyDefinitions/lithnet.activedirectory.passwordfilter.admx
index b9b4135..ae06a46 100644
--- a/src/PasswordProtection/PolicyDefinitions/lithnet.activedirectory.passwordfilter.admx
+++ b/src/PasswordProtection/PolicyDefinitions/lithnet.activedirectory.passwordfilter.admx
@@ -38,7 +38,36 @@
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+