567 lines
17 KiB
C++
567 lines
17 KiB
C++
/*---------------------------------------------------------------------------
|
||
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;
|
||
}
|
||
|