How to check password complexity using NetValidatePasswordPolicy

The NetValidatePasswordPolicy function allows an application to verify that passwords meet the complexity requirement.
Here is an important remark from Microsoft :
“The NetValidatePasswordPolicy function does not validate passwords in Active Directory accounts and cannot be used for this purpose. The only policy that this function checks a password against in Active Directory accounts is the password complexity (the password strength).”

The following code checks if the password meets complexity requirement using NetValidatePasswordPolicy:

#include <Windows.h>
#include <Lm.h>
#include <Dsgetdc.h>
#include <stdio.h>

WCHAR *DisplayErrorText(DWORD dwLastError)
{
    HMODULE hModule = NULL;
    LPWSTR MessageBuffer = NULL;
    DWORD dwBufferLength;

    DWORD dwFormatFlags = FORMAT_MESSAGE_ALLOCATE_BUFFER |
        FORMAT_MESSAGE_IGNORE_INSERTS |
        FORMAT_MESSAGE_FROM_SYSTEM ;

    if(dwLastError >= NERR_BASE && dwLastError <= MAX_NERR) {
        hModule = LoadLibraryEx(
            TEXT("netmsg.dll"),
            NULL,
            LOAD_LIBRARY_AS_DATAFILE
            );

        if(hModule != NULL)
            dwFormatFlags |= FORMAT_MESSAGE_FROM_HMODULE;
    }

    FormatMessageW(
        dwFormatFlags,
        hModule,
        dwLastError,
        MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
        (LPWSTR) &MessageBuffer,
        0,
        NULL
        );

    if(hModule != NULL)
        FreeLibrary(hModule);

	return MessageBuffer;
}


int wmain(int argc, wchar_t * argv[])
{
      NET_API_STATUS status;
      NET_VALIDATE_PASSWORD_CHANGE_INPUT_ARG InputArg = {0};
	  DWORD res = 0;
	  wchar_t* wzServer = NULL;
      NET_VALIDATE_OUTPUT_ARG* pOutputArg = NULL;

	  if(argc < 2)
	  {
		  wprintf(L"Usage: MyNetValidatePasswordPolicy password\r\n");
		  return -1;
	  }

	  PDOMAIN_CONTROLLER_INFO  DcInfo;// Get address of nearest DC as cached by OS at boot time.
      if (!DsGetDcName(NULL, NULL, NULL, NULL,
                    DS_DIRECTORY_SERVICE_REQUIRED | DS_RETURN_FLAT_NAME,
                    &DcInfo))
      {
		  wzServer = DcInfo->DomainControllerName;
	  }
	  else
		  wzServer = _wgetenv(L"LOGONSERVER");

      InputArg.ClearPassword = argv[1];
      InputArg.PasswordMatch = TRUE;
      status = NetValidatePasswordPolicy(wzServer, NULL, NetValidatePasswordChange, &InputArg, (void**)&pOutputArg);
	  if(status != NERR_Success)
	  {
		  wprintf(L"Error NetValidatePasswordPolicy %s\r\n", DisplayErrorText(status));
	  }
	  else
	  {
		  if(pOutputArg->ValidationStatus == 0)
			  wprintf(L"Password validated for a password change operation\r\n");
		  else
			  wprintf(L"Password validation failed for a password change operation: %s\r\n", DisplayErrorText(pOutputArg->ValidationStatus));
	  }
      NetValidatePasswordPolicyFree((void**)&pOutputArg);
	  return 0;
} 

How to test if user logged in with cached credentials using LsaGetLogonSessionData function in C++

In some case it will be useful to check if users are logged in with cached credentials (for example to notify users when they have been using cached credentials to log into their laptop for too long).

The following code gets user’s last logon information with the use of LSA (Local Security Authority) API LsaGetLogonSessionData.
When cached credentials are used the LOGON_CACHED_ACCOUNT flag is set (pLogonSessionData->UserFlags & LOGON_CACHED_ACCOUNT)

#include "stdafx.h"
#include <Windows.h>
#include <Ntsecapi.h>
#include <ntstatus.h> 
#include <malloc.h>
#include <strsafe.h>

void PrintLogonType (SECURITY_LOGON_TYPE type)
{
    if (type < Interactive || type > CachedUnlock)
        _tprintf (TEXT("LogonType: UndefinedLogonType\n"));
    else {
        static LPTSTR szTypes[] = {
            TEXT("Interactive"),
            TEXT("Network"), 
            TEXT("Batch"), 
            TEXT("Service"), 
            TEXT("Proxy"),
            TEXT("Unlock"), 
            TEXT("NetworkCleartext"),
            TEXT("NewCredentials"), 
            TEXT("RemoteInteractive"),
            TEXT("CachedInteractive"),
            TEXT("CachedRemoteInteractive"),
            TEXT("CachedUnlock")
        };
        _tprintf (TEXT("LogonType: %s\n"), szTypes[(int)type-Interactive]);
    }
}


void PrintUnicodeString (LPCTSTR pszPrefix, LSA_UNICODE_STRING lsaString)
{
    if (lsaString.MaximumLength >= lsaString.Length + sizeof(WCHAR) &&
        lsaString.Buffer[lsaString.Length/sizeof(WCHAR)] == L'\0')
        _tprintf (TEXT("%s: %ls\n"), pszPrefix, lsaString.Buffer);
    else if (lsaString.Length <= STRSAFE_MAX_CCH * sizeof(TCHAR)) {
        LPWSTR sz = (LPWSTR) _alloca (lsaString.Length + sizeof(WCHAR));
        StringCbCopyNW (sz, lsaString.Length + sizeof(WCHAR), lsaString.Buffer, lsaString.Length);
        _tprintf (TEXT("%s: %ls\n"), pszPrefix, sz);
    }
}

int _tmain(int argc, _TCHAR* argv[])
{
    HANDLE hThread = NULL;
    DWORD dwSize;
    TOKEN_STATISTICS ts;
	LUID luid;

    if(!OpenProcessToken (GetCurrentProcess(), TOKEN_QUERY, &hThread))
	{
		wprintf(L"Error OpenProcessToken rc=0x%.8X\r\n", GetLastError());
		return -1;
	}
	dwSize = sizeof(TOKEN_STATISTICS);
    if(!GetTokenInformation (hThread, TokenStatistics, &ts, sizeof(TOKEN_STATISTICS), &dwSize))
	{
		wprintf(L"Error GetTokenInformation rc=0x%.8X\r\n", GetLastError());
		if (hThread)
            CloseHandle (hThread);
		return -1;
	}
    luid = ts.AuthenticationId;
    if (hThread)
        CloseHandle (hThread);
 
    PSECURITY_LOGON_SESSION_DATA pLogonSessionData = NULL;
    NTSTATUS ntStatus = LsaGetLogonSessionData (&luid, &pLogonSessionData);
    if (ntStatus != STATUS_SUCCESS) {
		wprintf(L"Error LsaGetLogonSessionData rc=0x%.8X\r\n", ntStatus);
		return -1;
	}

    if (pLogonSessionData->UserName.Length)
        PrintUnicodeString (TEXT("UserName"), pLogonSessionData->UserName);
    if (pLogonSessionData->LogonDomain.Length)
        PrintUnicodeString (TEXT("LogonDomain"), pLogonSessionData->LogonDomain);
    if (pLogonSessionData->AuthenticationPackage.Length)
        PrintUnicodeString (TEXT("AuthenticationPackage"), pLogonSessionData->AuthenticationPackage);
    PrintLogonType ((SECURITY_LOGON_TYPE)pLogonSessionData->LogonType);
    _tprintf (TEXT("Session: %d\n"), pLogonSessionData->Session);

    if (pLogonSessionData->LogonServer.Length)
        PrintUnicodeString (TEXT("LogonServer"), pLogonSessionData->LogonServer);
    if (pLogonSessionData->DnsDomainName.Length)
        PrintUnicodeString (TEXT("DnsDomainName"), pLogonSessionData->DnsDomainName);
    if (pLogonSessionData->Upn.Length)
        PrintUnicodeString (TEXT("Upn"), pLogonSessionData->Upn);

	if(pLogonSessionData->UserFlags & LOGON_GUEST) wprintf(L"UserFlags=LOGON_GUEST\r\n");
	if(pLogonSessionData->UserFlags & LOGON_NOENCRYPTION) wprintf(L"UserFlags=LOGON_NOENCRYPTION\r\n");
	if(pLogonSessionData->UserFlags & LOGON_CACHED_ACCOUNT) wprintf(L"UserFlags=LOGON_CACHED_ACCOUNT\r\n");
	if(pLogonSessionData->UserFlags & LOGON_USED_LM_PASSWORD) wprintf(L"UserFlags=LOGON_USED_LM_PASSWORD\r\n");
	if(pLogonSessionData->UserFlags & LOGON_EXTRA_SIDS) wprintf(L"UserFlags=LOGON_EXTRA_SIDS\r\n");
	if(pLogonSessionData->UserFlags & LOGON_SUBAUTH_SESSION_KEY) wprintf(L"UserFlags=LOGON_SUBAUTH_SESSION_KEY\r\n");
	if(pLogonSessionData->UserFlags & LOGON_SERVER_TRUST_ACCOUNT) wprintf(L"UserFlags=LOGON_SERVER_TRUST_ACCOUNT\r\n");
	if(pLogonSessionData->UserFlags & LOGON_NTLMV2_ENABLED) wprintf(L"UserFlags=LOGON_NTLMV2_ENABLED\r\n");
	if(pLogonSessionData->UserFlags & LOGON_RESOURCE_GROUPS) wprintf(L"UserFlags=LOGON_RESOURCE_GROUPS\r\n");
	if(pLogonSessionData->UserFlags & LOGON_PROFILE_PATH_RETURNED) wprintf(L"UserFlags=LOGON_PROFILE_PATH_RETURNED\r\n");
	if(pLogonSessionData->UserFlags & LOGON_NT_V2) wprintf(L"UserFlags=LOGON_NT_V2\r\n");
	if(pLogonSessionData->UserFlags & LOGON_LM_V2) wprintf(L"UserFlags=LOGON_LM_V2\r\n");
	if(pLogonSessionData->UserFlags & LOGON_NTLM_V2) wprintf(L"UserFlags=LOGON_NTLM_V2\r\n");

	if(pLogonSessionData->UserFlags & LOGON_WINLOGON) wprintf(L"UserFlags=LOGON_WINLOGON\r\n");
	if(pLogonSessionData->UserFlags & LOGON_PKINIT) wprintf(L"UserFlags=LOGON_PKINIT\r\n");
	if(pLogonSessionData->UserFlags & LOGON_OPTIMIZED) wprintf(L"UserFlags=LOGON_OPTIMIZED\r\n");
	if(pLogonSessionData->UserFlags & LOGON_NO_OPTIMIZED) wprintf(L"UserFlags=LOGON_NO_OPTIMIZED\r\n");
 
    if (pLogonSessionData)
        LsaFreeReturnBuffer(pLogonSessionData);

	return 0;
}

How to export AD user attributes to a CSV file with powershell Get-ADUser?

The following command exports user attributes to a CSV file:

Exported user attributes are:

  • sAMAccountName
  • displayName
  • distinguishedName
  • employeeID
  • accountExpires
  • whenCreated
  • whenChanged
  • pwdLastSet
  • Password Never Expire
  • Account disabled
  • lastLogonTimestamp
  • lastLogon
Get-ADUser -Filter * -SearchBase "DC=fr,DC=contoso,DC=com" -Properties 
displayName,distinguishedName,sAMAccountName,employeeID,accountExpires,
whenCreated,whenChanged,pwdLastSet,userAccountControl,lastLogonTimestamp,lastLogon 
-Server FR-DC1| 
Select-Object -Property sAMAccountName,displayName,
distinguishedName,employeeID,
@{N='accountExpires';E={[DATETIME]::fromFileTime($_.accountExpires)}},
whenCreated,whenChanged,
@{n='pwdLastSet';e={[DateTime]::FromFileTime($_.pwdLastSet)}},
@{n="Password Never Expire";e={if(($_.userAccountControl[0] -band 65536) -ne 0) {"True"} else {"False"}}},
@{n="Account disabled";e={if(($_.userAccountControl[0] -band 2) -ne 0) {"True"} else {"False"}}},
@{n='lastLogonTimestamp';e={[DateTime]::FromFileTime($_.lastLogonTimestamp)}},
@{n='lastLogon';e={[DateTime]::FromFileTime($_.lastLogon)}} | 
export-csv c:\temp\user_fr.csv -encoding "unicode"