/*--------------------------------------------------------------------------- File: PwdSvc.cpp Comments: entry point functions and other exported functions for ADMT's password migration Lsa notification package. REVISION LOG ENTRY Revision By: Paul Thompson Revised on 09/06/00 --------------------------------------------------------------------------- */ #include "Pwd.h" #include "PwdSvc.h" #include "PwdSvc_s.c" // These global variables can be changed if required #define gsPwdProtoSeq TEXT("ncacn_np") #define gsPwdEndPoint TEXT("\\pipe\\PwdMigRpc") DWORD gPwdRpcMinThreads = 1; DWORD gPwdRpcMaxThreads = RPC_C_LISTEN_MAX_CALLS_DEFAULT; #ifndef STATUS_SUCCESS #define STATUS_SUCCESS ((NTSTATUS) 0x00000000L) #endif extern "C" { BOOL WINAPI _CRT_INIT( HANDLE hInstance, DWORD nReason, LPVOID pReserved ); } RPC_STATUS RPC_ENTRY SecurityCallback(RPC_IF_HANDLE hInterface, void* pContext); namespace { // // Timer Class // class CTimer { public: CTimer() : m_hTimer(NULL) { } ~CTimer() { if (m_hTimer) { Close(); } } DWORD Create() { ASSERT(m_hTimer == NULL); // // Create timer. Close timer first if already created. // if (m_hTimer) { Close(); } m_hTimer = CreateWaitableTimer(NULL, TRUE, NULL); return m_hTimer ? ERROR_SUCCESS : GetLastError(); } DWORD Wait(int nTime) { ASSERT(m_hTimer != NULL); DWORD dwError = ERROR_SUCCESS; if (m_hTimer) { // // Convert elapsed time parameter from milliseconds // to relative due time in 100s of nanoseconds. // LARGE_INTEGER liDueTime; liDueTime.QuadPart = nTime * -10000i64; // // Set timer and wait for timer to be signaled. // if (SetWaitableTimer(m_hTimer, &liDueTime, 0, NULL, NULL, FALSE)) { if (WaitForSingleObject(m_hTimer, INFINITE) == WAIT_FAILED) { dwError = GetLastError(); } } else { dwError = GetLastError(); } } else { dwError = ERROR_INVALID_HANDLE; } return dwError; } void Close() { ASSERT(m_hTimer != NULL); if (m_hTimer) { if (CloseHandle(m_hTimer) == FALSE) { DWORD dwError = GetLastError(); ASSERT(dwError == ERROR_SUCCESS); } m_hTimer = NULL; } } protected: HANDLE m_hTimer; }; } /****************************** * RPC Registration Functions * ******************************/ /********************************************************************* * * * Written by: Paul Thompson * * Date: 11 JUNE 2001 * * * * This function is called by a thread spawned from our * * "InitializeChangeNotify" password filter function to wait until * * SAM, and therefore RPC, is up and running. * * * * 04/17/02 MPO - rewritten to wait for SAM_SERVICE_STARTED event to * * be created first before waiting for this event to * * be signaled * *********************************************************************/ //BEGIN PwdMigWaitForSamService DWORD __stdcall PwdMigWaitForSamService() { DWORD dwError = ERROR_SUCCESS; // // Attempt to open the SAM service started event object. // // Note that we must use the Nt APIs to open the event object // as the name begins with a \ character which is not valid // in the object name space used by the OpenEvent API. // HANDLE hEvent = NULL; UNICODE_STRING usEventName; RtlInitUnicodeString(&usEventName, L"\\SAM_SERVICE_STARTED"); OBJECT_ATTRIBUTES oaEventAttributes; InitializeObjectAttributes(&oaEventAttributes, &usEventName, 0, 0, NULL); NTSTATUS ntStatus = NtOpenEvent(&hEvent, SYNCHRONIZE, &oaEventAttributes); if (NT_ERROR(ntStatus)) { // // If the SAM service started event object has not been // created yet then wait until it has been created. // if (ntStatus == STATUS_OBJECT_NAME_NOT_FOUND) { // // Enter a loop which waits until the open event API returns // an error other than the event object not found. The loop // waits 15 sec between attempts to open object. // CTimer timer; dwError = timer.Create(); if (dwError == ERROR_SUCCESS) { for (;;) { dwError = timer.Wait(15000); if (dwError != ERROR_SUCCESS) { break; } ntStatus = NtOpenEvent(&hEvent, SYNCHRONIZE, &oaEventAttributes); if (NT_SUCCESS(ntStatus)) { break; } if (ntStatus != STATUS_OBJECT_NAME_NOT_FOUND) { dwError = LsaNtStatusToWinError(ntStatus); break; } } } } else { dwError = LsaNtStatusToWinError(ntStatus); } } // // If event has been opened then wait for it to be signalled. // if (hEvent != NULL) { NTSTATUS ntWaitStatus = NtWaitForSingleObject(hEvent, FALSE, NULL); NTSTATUS ntCloseStatus = NtClose(hEvent); ASSERT(NT_SUCCESS(ntCloseStatus)); dwError = (ntWaitStatus == STATUS_WAIT_0) ? ERROR_SUCCESS : LsaNtStatusToWinError(ntWaitStatus); } return dwError; } //END PwdMigWaitForSamService /********************************************************************* * * * Written by: Paul Thompson * * Date: 11 JUNE 2001 * * * * This function is a spawned thread created by the * * "InitializeChangeNotify" password filter function to wait until * * SAM, and therefore RPC, is up and running and then register the * * ADMT Password Migration RPC interface. * * * * 04/17/02 MPO - rewritten to wait until critical section is * * initialized and to use a stronger authentication * * service when built for Windows 2000 or later * *********************************************************************/ //BEGIN PwdMigRPCRegProc DWORD WINAPI PwdMigRPCRegProc(LPVOID lpParameter) { RPC_STATUS rc = RPC_S_OK; // // Wait for the SAM service to start before registering RPC interface. // if (PwdMigWaitForSamService() == ERROR_SUCCESS) { // // Initialize critical section used by PwdRpc interface // implementation. // Note that the critical section must be initialized before // registering RPC interface to prevent a race condition. // bool bCriticalSection = false; try { InitializeCriticalSection(&csADMTCriticalSection); bCriticalSection = true; } catch (...) { ; } if (bCriticalSection == false) { // // The initialize critical section API must // have thrown a STATUS_NO_MEMORY exception. // // Enter a loop which waits until the critical // section is initialized. The loop waits 15 sec // between attempts to initialize critical section. // CTimer timer; DWORD dwError = timer.Create(); if (dwError == ERROR_SUCCESS) { while (bCriticalSection == false) { dwError = timer.Wait(15000); if (dwError != ERROR_SUCCESS) { break; } try { InitializeCriticalSection(&csADMTCriticalSection); bCriticalSection = true; } catch (...) { ; } } } if (dwError != ERROR_SUCCESS) { return dwError; } } // specify protocol sequence and endpoint // for receiving remote procedure calls rc = RpcServerUseProtseqEp(gsPwdProtoSeq, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, gsPwdEndPoint, NULL); if (rc == RPC_S_OK) { // // Register PwdMigRpc interface with the RPC run-time library. // Only allow connections with an authorization level higher than // RPC_C_AUTHN_LEVEL_NONE. Also specifying security callback to // validate client before allowing access to interface. // rc = RpcServerRegisterIfEx( PwdMigRpc_ServerIfHandle, NULL, NULL, RPC_IF_ALLOW_SECURE_ONLY, RPC_C_LISTEN_MAX_CALLS_DEFAULT, SecurityCallback ); if (rc == RPC_S_OK) { #ifdef PWD_W2KORLATER // // Register authentication information with RPC specifying // default principal name and specifying GSS negotiate. // PWCHAR pszPrincipalName = NULL; rc = RpcServerInqDefaultPrincName(RPC_C_AUTHN_GSS_NEGOTIATE, &pszPrincipalName); if (rc == RPC_S_OK) { ASSERT(pszPrincipalName && (wcslen(pszPrincipalName) != 0)); //set the authenification for this RPC interface rc = RpcServerRegisterAuthInfo(pszPrincipalName, RPC_C_AUTHN_GSS_NEGOTIATE, NULL, NULL); RpcStringFree(&pszPrincipalName); } #else //set the authenification for this RPC interface rc = RpcServerRegisterAuthInfo(NULL, RPC_C_AUTHN_WINNT, NULL, NULL); #endif } }//end if set protocal sequence and end point set }//end if RPC service is ready return 0; } //END PwdMigRPCRegProc /********************************************************************* * * * Written by: Paul Thompson * * Date: 7 SEPT 2000 * * * * This function is called by Lsa when trying to load all * * registered Lsa password filter notification dlls. Here we will * * initialize the RPC run-time library to handle our ADMT password * * migration RPC interface and to begin looking for RPC calls. If we* * fail to successfully setup our RPC, we will return FALSE from this* * function which will cause Lsa not to load this password filter * * Dll. * * Note that the other two password filter dll functions: * * PasswordChangeNotify and PasswordFilter do nothing at this point * * in time. * * * *********************************************************************/ //BEGIN InitializeChangeNotify BOOLEAN __stdcall InitializeChangeNotify() { /* local variables */ BOOLEAN bSuccess = FALSE; /* function body */ //spawn a seperate thread to register our RPC interface once RPC is up and running HANDLE h = CreateThread(NULL, 0, PwdMigRPCRegProc, NULL, 0, NULL); if (h != NULL) bSuccess = TRUE;; CloseHandle(h); return bSuccess; } //END InitializeChangeNotify /********************************************************************* * * * Written by: Paul Thompson * * Date: 7 SEPT 2000 * * * * This function is called by Lsa for all registered Lsa password* * filter notification dlls when a password in the domain has been * * modified. We will simply return STATUS_SUCCESS and do nothing. * * * *********************************************************************/ //BEGIN PasswordChangeNotify NTSTATUS __stdcall PasswordChangeNotify(PUNICODE_STRING UserName, ULONG RelativeId, PUNICODE_STRING NewPassword) { return STATUS_SUCCESS; } /********************************************************************* * * * Written by: Paul Thompson * * Date: 7 SEPT 2000 * * * * This function is called by Lsa for all registered Lsa password* * filter notification dlls when a password in the domain is being * * modified. This function is designed to indicate to Lsa if the new* * password is acceptable. We will simply return TRUE (indicating it* * is acceptable) and do nothing. * * * *********************************************************************/ //BEGIN PasswordFilter BOOLEAN __stdcall PasswordFilter(PUNICODE_STRING AccountName, PUNICODE_STRING FullName, PUNICODE_STRING Password, BOOLEAN SetOperation) { return TRUE; } //END PasswordFilter /***************************/ /* Internal DLL functions. */ /***************************/ static BOOL Initialize(void) { return TRUE; } static BOOL Terminate(BOOL procterm) { if (!procterm) return TRUE; /* XXX Do stuff here */ return TRUE; } BOOL WINAPI DllMain(HINSTANCE hinst, DWORD reason, VOID *rsvd) /*++ Routine description: Dynamic link library entry point. Does nothing meaningful. Arguments: hinst = handle for the DLL reason = code indicating reason for call rsvd = for process attach: non-NULL => process startup for process detach: non-NULL => process termination Return value: status = success/failure Side effects: None --*/ { switch (reason) { case DLL_PROCESS_ATTACH: { _CRT_INIT(hinst, reason, rsvd); DisableThreadLibraryCalls(hinst); return Initialize(); break; } case DLL_PROCESS_DETACH: { BOOL bStat = Terminate(rsvd != NULL); _CRT_INIT(hinst, reason, rsvd); return bStat; break; } case DLL_THREAD_ATTACH: case DLL_THREAD_DETACH: return TRUE; default: return FALSE; } } /////////////////////////////////////////////////////////////////////////////// // Midl allocate memory /////////////////////////////////////////////////////////////////////////////// void __RPC_FAR * __RPC_USER midl_user_allocate( size_t len ) { return new char[len]; } /////////////////////////////////////////////////////////////////////////////// // Midl free memory /////////////////////////////////////////////////////////////////////////////// void __RPC_USER midl_user_free( void __RPC_FAR * ptr ) { delete [] ptr; }