RASHOST.CPP

/////////////////////////////////////////////////////////////////////////////// 
//
// File Name
// RASHOST.CPP
//
// Description
// This file contains the initialization functions for the DLL library
// and for the Security Host protocol. The NT RAS manager will call into
// our DLL when a dialing user needs to be validated.
//
// Author
// Irving De la Cruz
//
// Revision: 1.1
//
// Written for Microsoft Developer Support
// Copyright 1995 - 1998 Microsoft Corporation. All rights reserved.
//
#include "RASHOST.H"

HINSTANCE ghLibHandle = NULL;
RASSECURITYDIALOGSENDPROC gpfnSendProc = NULL;
RASSECURITYDIALOGRECEIVEPROC gpfnReceiveProc = NULL;

///////////////////////////////////////////////////////////////////////////////
// LibMain()
//
// Parameters
// { Refer to the Win32 SDK documentation for DLL entry points }
//
// Purpose
// Entry point of this DLL
//
// Return Value
// TRUE if the DLL should proceed with the dwReason case, FALSE otherwise.
//
BOOL APIENTRY LibMain (HINSTANCE hDll, DWORD dwReason, LPVOID lpReserved)
{
if (DLL_PROCESS_DETACH == dwReason && ghLibHandle)
{
FreeLibrary (ghLibHandle);
}
return TRUE;
}

///////////////////////////////////////////////////////////////////////////////
// RasSecurityDialogBegin()
//
// Parameters
// hPort Handle to the COM port where the user is logging in
// (A RAS handle, not a standard handle)
// pSendBuf Pointer to a buffer where we can copy stuff to be sent
// over the wire
// SendBufSize Size of the outgoing buffer
// pRecvBuf Pointer a buffer where stuff is received from the
// remote client machine
// RecvBufSize Size of the incoming buffer
// RasSecurityDialogComplete Pointer to a callback function we must
// call after we have completed our validation, indicating
// the RAS manager weather or not we granted access to the
// caller.
//
// Purpose
// This is the entry point for our security DLL. When the RAS manager
// receives a call from a user, after it does its user validation, it
// will call into us at this entry point to allow us to do extra
// validation.
// NOTE: It is very important that this function does not BLOCK. If
// it does, the RAS manager will be block and no other calls
// will be accepted. That is why we copy the incoming
// parameters into a private structure and then spawn a thread
// to do the actual validation work.
// The passed parameters will be valid until we call the
// ending function.
//
// Return Value
// ERROR_SUCCESS is we were able to start the validation. Otherwise
// return and error from WINERROR.H or RASERROR.H
//
DWORD WINAPI RasSecurityDialogBegin (HPORT hPort,
PBYTE pSendBuf,
DWORD SendBufSize,
PBYTE pRecvBuf,
DWORD RecvBufSize,
VOID (WINAPI *RasSecurityDialogComplete)(SECURITY_MESSAGE*))
{
// Initialize the global pointer to the entry points in the RAS manager
// DLL. I use these entry points to send and receive data to the remote
// client machine. These functions will send and receive RAW buffers of
// data to the other machine just before PPP-framing starts.
// These functions have not been exported in any publish import library
// so we must do a dynamic binding to them.
if (NULL == ghLibHandle)
{
ghLibHandle = LoadLibrary ("RASMAN.DLL");
if (NULL == ghLibHandle)
{
return ERROR_DLL_NOT_FOUND;
}
}
// Obtain the send and received entry points from the RAS manager DLL.
if (NULL == gpfnSendProc)
{
gpfnSendProc = (RASSECURITYDIALOGSENDPROC)GetProcAddress (ghLibHandle, TEXT("RasSecurityDialogSend"));
if (NULL == gpfnSendProc)
{
return ERROR_PROC_NOT_FOUND;
}
}
if (NULL == gpfnReceiveProc)
{
gpfnReceiveProc = (RASSECURITYDIALOGRECEIVEPROC)GetProcAddress (ghLibHandle, TEXT("RasSecurityDialogReceive"));
if (NULL == gpfnReceiveProc)
{
return ERROR_PROC_NOT_FOUND;
}
}

// Save the parameters passed and give them to the working thread
// that will handle the authentication of the caller. The thread
// should release this memory.
// The parameters passed to use should be valid until we call the
// dialog end function or until we get call in the termination
// dialog entry point by the RAS system supervisor (the RAS manager)
PWORK_THREAD_DATA pCallData = (PWORK_THREAD_DATA)HeapAlloc (GetProcessHeap(),
HEAP_ZERO_MEMORY,
sizeof(WORK_THREAD_DATA));
if (NULL == pCallData)
{
return ERROR_OUTOFMEMORY;
}
// Initialize the members.
pCallData->hPort = hPort;
pCallData->pSendBuf = pSendBuf;
pCallData->SendBufSize = SendBufSize;
pCallData->pRecvBuf = pRecvBuf;
pCallData->RecvBufSize = RecvBufSize;
pCallData->pfnSecurityDialogEnd = RasSecurityDialogComplete;

// For each call, spawn a working thread that will make the neccesary
// authentication of the dial-up caller. We should never block the call
// to RasSecurityDialogBegin(). If we do so, RAS will also block and no
// more call would be accepted.
DWORD dwThreadID;
HANDLE hThread = CreateThread (NULL,
0,
(LPTHREAD_START_ROUTINE)CallWorkerThread,
(LPVOID)pCallData,
0,
&dwThreadID);
if (NULL == hThread)
{
// Call GetLastError() before calling HeapFree().
DWORD dwError = GetLastError();
HeapFree (GetProcessHeap(), 0, pCallData);
return dwError;
}
return ERROR_SUCCESS;
}

///////////////////////////////////////////////////////////////////////////////
// RasSecurityDialogEnd()
//
// Parameters
// hPort Handle to the COM port (A RAS handle, not a standard handle)
//
// Purpose
// Stub function. Need to export this by the security DLL even if
// not needed. We need to return non-zero to indicate RAS we are no
// longer responsible for the port connection. It will take care
// of the rest.
//
// Return Value
// ERROR_PORT_DISCONNECTED always
//
DWORD WINAPI RasSecurityDialogEnd (HPORT hPort)
{
return ERROR_PORT_DISCONNECTED;
}

///////////////////////////////////////////////////////////////////////////////
// CallWorkerThread()
//
// Parameters
// pCallData Pointer to the data structure with the data for the
// instance we are handling.
//
// Purpose
// This is the function for the thread that interacts with a dialing RAS
// user. Here we query the user for its identity and based on it we
// compute a challenge to for it. Once the response from the client
// is received, we validate it and decide wether or not we allow access.
//
// Return Value
// None
//
void WINAPI CallWorkerThread (PWORK_THREAD_DATA pCallData)
{
// Initialize the appropiate structures
SECURITY_MESSAGE smResult = { 0 };
smResult.hPort = pCallData->hPort;
lstrcpy (smResult.UserName, TEXT("(RASHOST - Unknown)"));

DWORD dwBytes = pCallData->RecvBufSize;
ZeroMemory (pCallData->pRecvBuf, dwBytes);
dwBytes = sizeof(RASHOST_DATA);

// Initialize the structure that we'll send to the caller
RASHOST_DATA HostData = { 0 };
HostData.dwSize = sizeof(RASHOST_DATA);

// Create an event to wait for client responses
HANDLE hEvent = CreateEvent (NULL, FALSE, FALSE, NULL);

// We must copy the data from the structure to the buffer RAS
// manager uses to transmit the information. This buffer
// cannot be larger than 1500 bytes
CopyMemory (pCallData->pSendBuf, &HostData, sizeof(RASHOST_DATA));
DWORD dwError = 0;

gpfnSendProc (pCallData->hPort,
pCallData->pSendBuf,
(WORD)sizeof(RASHOST_DATA));

// Wait for an answer from the client responder
gpfnReceiveProc (pCallData->hPort,
pCallData->pRecvBuf,
(WORD *)&dwBytes,
0,
hEvent);
DWORD dwWait;
dwWait = WaitForSingleObject (hEvent, CLIENT_RESPONSE_TIMEOUT);
if (WAIT_OBJECT_0 != dwWait)
{
dwError = ERROR_SMM_TIMEOUT;
goto ErrorExit;
}

// Copy the client data from the incoming buffer to the data structure
CopyMemory (&HostData, pCallData->pRecvBuf, sizeof(RASHOST_DATA));

// Save this information for RAS authentication with the NT RAS manager.
// The user name must be the same as the one in the NT account database
// on the domain where the user is granted access.
lstrcpy (smResult.UserName, HostData.szUserName);
lstrcpy (smResult.Domain, HostData.szUserDomain);

//lstrcpy (smResult.UserName, "IrvingD");

BOOL fAccessGranted; // Weather or not access was granted
// With the received client response, validate the access for the client
dwError = ValidateCallingUser (&HostData, &fAccessGranted);
if (dwError)
{
goto ErrorExit;
}

if (fAccessGranted)
{
// Access has been granted
smResult.dwMsgId = SECURITYMSG_SUCCESS;
}
else
{
// The caller did not respond correctly
smResult.dwMsgId = SECURITYMSG_FAILURE;
}

ErrorExit:
if (dwError)
{
// There was an error, deny access.
if (ERROR_SMM_TIMEOUT == dwError)
{
smResult.dwMsgId = SECURITYMSG_FAILURE;
}
else
{
smResult.dwMsgId = SECURITYMSG_ERROR;
smResult.dwError = dwError;
}
}
// Release the synchronization objects
CloseHandle (hEvent);
// Tell the RAS manager we are finished by calling the END dialog funcition.
pCallData->pfnSecurityDialogEnd (&smResult);
// Free the memory of this call instance before exiting the thread
HeapFree (GetProcessHeap(), 0, pCallData);
}

///////////////////////////////////////////////////////////////////////////////
// ValidateCallingUser()
//
// Parameters
// pData Pointer to the host data structure
// pfAccessGranted We return TRUE here is access is granted.
// FALSE otherwise.
//
// Purpose
// Here we validate the answer to the challenge sent to the client.
//
// Return Value
// ERROR_SUCCESS if successful. Otherwiser an error code from WINERROR.H
// or RASERROR.H
//
DWORD WINAPI ValidateCallingUser (PRASHOST_DATA pData,
BOOL * pfAccessGranted)
{
DWORD dwResult = ERROR_SUCCESS;
*pfAccessGranted = FALSE;

// Do stuff here

return dwResult;
}

// End of file for RASHOST.CPP