// ----------------------------------------------------------
// File:	ni_pam.c
//
// Contents:	main module for ni_pam.dll.
//				ni_pam.dll is the DLL to provide pluggable 
//				authentication for Windows-NT.  
// 
// History:		6-30-1997	created	by Naomaru Itoi.
//				7-14-1997	modified to support logout and change password.

#include "ni_pam.h"
#include "ni_common2.h"

// global valuables
HINSTANCE	hDllInstance;	// my instance
HANDLE		hGlobalWlx;		// Handle to tell winlogon who's calling
PWLX_DISPATCH_VERSION_1_0 pWlxFuncs; // Ptr to table of functions
NISTRUCT glbNistruct;

// 1998/7/26
LONGLONG freq;
int ni_time[10];
LARGE_INTEGER ni_counter;

/*
PCTSTRUCT ctAuth = (PCTSTRUCT)NULL;
PCTSTRUCT ctChpass = (PCTSTRUCT)NULL;
PCTSTRUCT ctObtain = (PCTSTRUCT)NULL;
*/

// function prototype declarations
INT WINAPI call_dll(LPWSTR, LPWSTR);
INT trans(INT, INT, INT);
VOID WINAPI free_ct();
// functions
// parse_ct
// get string in the registry.
// parse it to Configuration Table
// 6-30-1997, Naomaru Itoi, created
// 7-14-1997, modified to select where to save the CT

BOOL WINAPI parse_ct(LPWSTR inStr, PCTSTRUCT *ctList)
{
	PCTSTRUCT pNewCt, pWalker;
	PWSTR lpCfStr, lpDllName;
	WCHAR wcBuf[1024];

	/*DS(L"parse_ct() received this %s\n", inStr);*/

	pWalker = *ctList;
	
	lpCfStr = wcstok(inStr, SEPARATORS);
	while (lpCfStr != NULL){

		lpDllName = wcstok(NULL, SEPARATORS);
	
		// assign stuff to new CT
		pNewCt = (PCTSTRUCT) GlobalAlloc(GPTR, sizeof(CTSTRUCT));
		if (pNewCt==NULL){
			fwprintf(stderr, L"error : cannot allocate memory\n");
			return (FALSE);
		}
		wcsncpy(pNewCt->DllName, lpDllName, DllNameLength);
		pNewCt->rv = NI_FAILURE;

		if (wcscmp(lpCfStr, CF_REQUIRED_STR)==0){
			pNewCt->cf = CF_REQUIRED;
		}		
		else if (wcscmp(lpCfStr, CF_REQUISITE_STR)==0){
			pNewCt->cf = CF_REQUISITE;
		}
		else if (wcscmp(lpCfStr, CF_OPTIONAL_STR)==0){
			pNewCt->cf = CF_OPTIONAL;
		}
		else if (wcscmp(lpCfStr, CF_SUFFICIENT_STR)==0){
			pNewCt->cf = CF_SUFFICIENT;
		}
		else {
			fwprintf(stderr, L"unrecognized control flag %s\n", lpCfStr);
			return (FALSE);
		}

		if (pWalker == NULL){
			*ctList = pNewCt;
		}
		else {
			pWalker->next = pNewCt;
		}
		pWalker = pNewCt;

		// let's go next
		lpCfStr = wcstok(NULL, SEPARATORS);
	}	
	pWalker = *ctList;
	wcscpy(wcBuf, L"I call :\n");
	while (pWalker != NULL){
		wsprintf(wcBuf, L"%s%s\n",wcBuf, pWalker->DllName);
		pWalker = pWalker->next;
	}
	//NIError(wcBuf, L"NI_PAM");
}

// read_ct(PWSTR svcName, PCTSTRUCT *ctList) 
// Read configuration table specified by svcName,
// and store it to ctList.
// 7-14-1997, Naomaru Itoi, created
INT WINAPI read_ct(PWSTR svcName, PCTSTRUCT *ctList)
{
	DWORD dwRcode, dwTmp, dwSz;
	HKEY hKey;
	WCHAR buffer[RegValueLength*8];

	//WCHAR wcBuf[256];

	// open registry
	if ((dwRcode=RegOpenKeyEx(HKEY_LOCAL_MACHINE,WINLOGON_KEY,(DWORD)0,KEY_READ,
		&hKey)) != ERROR_SUCCESS) {
		D(L"NI_PAM : read_ct() could not open registry\n");	
		return (FALSE);
	}         
	else {
		/*D(L"NI_PAM : read_ct() succeeded to open registry\n");*/
	}

	// get registry value
	if ((dwRcode = RegQueryValueEx(hKey,svcName,(LPDWORD)0,&dwTmp,
                  (LPBYTE)buffer,&dwSz)) == ERROR_SUCCESS)
         {
		  /*DS(L"NI_PAM : read_ct() succeeded to read value %s\n", buffer);*/
		  return parse_ct((LPWSTR)buffer, ctList); // parse configuration table 
         }
      else
	  {
			/*wprintf(L"(%s) %d %d %d\n", buffer, dwRcode, ERROR_SUCCESS, GetLastError());*/
			/*FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
                           NULL,GetLastError(),MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
                           wcBuf,256,NULL);
			wprintf(L"message : %s\n", wcBuf);*/
			DS(L"read_ct() failed to read value %s\n", SERVICE_NAME_AUTH);
			NIError(L"read_ct() failed", L"NI_PAM");
			return (NI_FAILURE);
	  }
}

// ni_pam_call_dlls
INT WINAPI ni_pam_call_dlls(PCTSTRUCT ctList, PWSTR FuncName) 
{
	PCTSTRUCT pWalker, pTmp;
	INT state, dll_rv, i=2;

	pWalker = ctList;
	pTmp = pWalker;

	// for all CTs
	state = ST_SUC;
	while (pWalker != NULL) {
		QueryPerformanceCounter (&ni_counter);
		ni_time[i++] = ni_counter.QuadPart * 1000000 / freq;
		//DS(L"dll %s.\n", pWalker->DllName);
		dll_rv = call_dll(pWalker->DllName, FuncName);
		//DS(L"dll %s ", pWalker->DllName);
		//DS(L"returns %d\n", dll_rv);
		QueryPerformanceCounter (&ni_counter);
		ni_time[i++] = ni_counter.QuadPart * 1000000 / freq;
		//ni_time[i++] = GetTickCount();
		// translation NI_S/* -> NI_S/NI_F
		if (dll_rv != NI_SUCCESS) dll_rv = NI_FAILURE;
		pWalker->rv = dll_rv;

#ifdef _DEBUGXX
		printf("transition (%d,%d,%d)\n", state, pWalker->cf, dll_rv);
#endif /* _DEBUGXX */
		state = trans(state, pWalker->cf, dll_rv);
		if (state == ST_END_SUC) return NI_SUCCESS;
		else if (state == ST_END_FAIL) return NI_FAILURE;
	
		pWalker = pWalker->next;
	}

	if (state == ST_SUC) return NI_SUCCESS;
	else if (state == ST_FAIL) return NI_FAILURE;
	else return SOMETHING_IS_WRONG;
}

// DllMain
// Dll hook
// 6-30-1997, Naomaru Itoi, created
BOOL
WINAPI
DllMain(
		HINSTANCE hInstance,
		DWORD dwReason,
		LPVOID lpReserved)
{
    switch (dwReason)
    {
        case DLL_PROCESS_ATTACH:
            DisableThreadLibraryCalls ( hInstance );
            hDllInstance = hInstance;

        case DLL_PROCESS_DETACH:
        default:
            return(TRUE);
    }
}

// ni_start
// start ni_pam
// 6-30-1997, Naomaru Itoi, created

BOOL
WINAPI
ni_start(HANDLE hWlx, PVOID pWinlogonFunctions)
{
	// set globals
	pWlxFuncs = (PWLX_DISPATCH_VERSION_1_0) pWinlogonFunctions;
	hGlobalWlx = hWlx;
	
	return (TRUE);
}

// ni_obtainpass
// call password obtaining mechanism (e.g. smart card)
// 9-22-1997, Naomaru Itoi, created.

BOOL WINAPI
ni_obtainpass(NISTRUCT *nistruct)
{
	//PCTSTRUCT pWalker, pTmp;
	PCTSTRUCT ct = (PCTSTRUCT)NULL;
	INT rv;

	read_ct(SERVICE_NAME_OBTAIN, &ct);

	glbNistruct.value = nistruct->value;	// assign port number
	
	if (ni_pam_call_dlls(ct, NI_SM_OBTAINPASS) == NI_SUCCESS) {
		wcscpy(nistruct->username, glbNistruct.username);
		wcscpy(nistruct->domain, glbNistruct.domain);
		wcscpy(nistruct->password, glbNistruct.password);
		rv = NI_SUCCESS;
	} else {
		wcscpy(nistruct->username, L"");
		wcscpy(nistruct->domain, L"");
		wcscpy(nistruct->password, L"");
		rv = NI_FAILURE;
	}
	free_ct(ct);
	return rv;
}

// ni_authenticate
// authentication function
// 6-30-1997, Naomaru Itoi, created
BOOL 
WINAPI
ni_authenticate(NISTRUCT *nistruct)
{
	INT rv;
	PCTSTRUCT *ctList;
	LARGE_INTEGER ni_freq;
	WCHAR wcBuf[256];
	//double utime;
	//PCTSTRUCT pWalker, pTmp;

	// for timestamp 0
	
	//ni_time[1] = GetTickCount();
	QueryPerformanceCounter(&ni_counter);
	ni_time[0] = nistruct->value;
	QueryPerformanceFrequency(&ni_freq);
	/*	printf("counter %u (%u %u)\n", 
		ni_counter.QuadPart, ni_counter.HighPart, ni_counter.LowPart);
	printf("freq %u (%u %u)\n", 
		ni_freq.QuadPart, ni_freq.HighPart, ni_freq.LowPart);*/

	freq = ni_freq.QuadPart;
	//utime = ni_counter.QuadPart * 1000000 / freq;
	//printf("utime = %f\n", utime);
	ni_time[1] = ni_counter.QuadPart * 1000000 / freq;

	// read configuration table to ct.
	ctList = (PCTSTRUCT *)GlobalAlloc(GPTR, sizeof(PCTSTRUCT *));
	read_ct(SERVICE_NAME_AUTH, ctList);
	/*DS(L"ct=%d\n", ct);*/

	// copy username & password passed from caller to global.

	wcscpy(glbNistruct.username, nistruct->username);
	wcscpy(glbNistruct.domain, nistruct->domain);
	wcscpy(glbNistruct.password, nistruct->password);
#ifdef NIXX
	wsprintf(wcBuf, L"ni_authenticate () is called for user %s[%s]", 
		glbNistruct.username, glbNistruct.password);
	NIError(wcBuf, L"ni_pam");
#endif

	// for all CTs
	rv = ni_pam_call_dlls(*ctList, NI_SM_AUTHENTICATE);
	free_ct(*ctList);
	QueryPerformanceCounter(&ni_counter);
	ni_time[9] = ni_counter.QuadPart * 1000000 / freq;
	wsprintf(wcBuf, L"t[0]=%u, t[1]=%u, t[2]=%u, t[3]=%u\nt[4]=%u, t[5]=%u, t[6]=%u, t[7]=%u\nt[8]=%u, t[9]=%u\n", 
		ni_time[0], ni_time[1], ni_time[2], ni_time[3], 
		ni_time[4], ni_time[5], ni_time[6], ni_time[7], 
		ni_time[8], ni_time[9]);
	//wsprintf(wcBuf, L"t[0]=%d", (int)ni_time[0]);
	printf("%u %u\n", ni_time[1], ni_time[9]);
	printf("%s", wcBuf);
	//MessageBox(NULL, wcBuf, L"NI_PAM", (MB_OK | MB_SETFOREGROUND));
	return rv;
}

// ni_logout
// logout function
// 7-13-1997, Naomaru Itoi, created
INT 
WINAPI
ni_logout()
{
	PCTSTRUCT ct = (PCTSTRUCT)NULL;
	PCTSTRUCT pWalker;
	WCHAR wcBuf[1024];
	INT dll_rv;

	read_ct(SERVICE_NAME_AUTH, &ct);
	pWalker = ct;

	while (pWalker != NULL){
		//if (pWalker->rv == NI_SUCCESS) {
			dll_rv = call_dll(pWalker->DllName, NI_SM_LOGOUT);
			//pWalker->rv = NI_FAILURE;
			wsprintf(wcBuf, L"Logging out %s returns %d\n",
			pWalker->DllName, dll_rv);
			/*NIError(wcBuf, L"NI_PAM");*/
		//}
		pWalker = pWalker->next;
	}
	return NI_SUCCESS;
}

// ni_end
// called when ni_pam is finished 
// 7-13-1997, Naomaru Itoi, created
INT WINAPI ni_end()
{
	return NI_SUCCESS;
}

// ni_chpass
// change password
// 7-14-1997, Naomaru Itoi, created
INT
WINAPI
ni_chpass(LPWSTR oldPass, LPWSTR newPass)
{
	PCTSTRUCT ct = (PCTSTRUCT)NULL;
	INT rv;
	
	wcscpy(glbNistruct.oldPassword, oldPass);
	wcscpy(glbNistruct.newPassword, newPass);
	// read configuration table to ctAuth.
	read_ct(SERVICE_NAME_CHPASS, &ct);
	
	rv = ni_pam_call_dlls(ct, NI_SM_CHPASS_CHECK);
//	wsprintf(wcBuf, L"ni_sm_chpass_check()[] returns %d\n", rv);
//	NIError (wcBuf, L"NI_PAM");
	if (rv==NI_FAILURE) return NI_FAILURE;
	/*printf("ni_sm_chpass_check() returns %d\n", rv);*/
	rv = ni_pam_call_dlls(ct, NI_SM_CHPASS);

	//wsprintf(wcBuf, L"ni_sm_chpass()[] returns %d\n", rv);
	/*printf("ni_sm_chpass() returns %d\n", rv);*/
	//NIError (wcBuf, L"NI_PAM");
	return rv;
}

// call_dll
// gets dll name, and calls it
// 6-30-1997, Naomaru Itoi, created.

INT WINAPI call_dll(LPWSTR dllName, LPWSTR svcName)
{
	typedef INT (* lpFunc1)();
	lpFunc1	ni_sm_function;
	HMODULE hNi_dll;
	INT dll_rv;
	CHAR cBuf[256];
    
	hNi_dll = LoadLibrary(dllName);
    if (hNi_dll) {
		D("call_dll : LoadLibrary succeeded\n");
	}
	else {
		wprintf(L"error : call_dll : LoadLibrary(%s)\n", dllName);
		return NI_FAILURE;
	}

	WideCharToMultiByte(CP_ACP,0,svcName,-1,cBuf,
						256,(LPCSTR)0,(LPBOOL)0);
	ni_sm_function = (lpFunc1)GetProcAddress(hNi_dll, cBuf);
	if (ni_sm_function) {
		/*D("got ni_sm_function address\n");*/
	}
	else {
		DS(L"error : call_dll : getting %s() address\n", svcName);
		return NI_FAILURE;
	}
	dll_rv = ni_sm_function(&glbNistruct);
	return dll_rv;
}	

// trans
// do state transition
// 6-30-1997, Naomaru Itoi, created

INT trans(INT state, INT cf, INT dll_rv)
{
	if (state == ST_SUC) {
		if (dll_rv == NI_FAILURE && cf == CF_REQUISITE) {
			return ST_END_FAIL;
		}
		else if (dll_rv == NI_SUCCESS && cf == CF_SUFFICIENT) {
			return ST_END_SUC;
		}
		else if (dll_rv == NI_FAILURE && cf == CF_REQUIRED) {
			return ST_FAIL;
		}
		else return ST_SUC;
	} 
	else if (state == ST_FAIL) {
		if (dll_rv == NI_SUCCESS && cf == CF_SUFFICIENT) {
			return ST_END_SUC;
		}
		else return ST_FAIL;
	}
	return SOMETHING_IS_WRONG;
}

VOID WINAPI free_ct(PCTSTRUCT ct)
{
	PCTSTRUCT pWalker, pTmp;
	pTmp = pWalker = ct;

	while (pWalker != NULL){
		pTmp = pWalker;
		pWalker = pWalker->next;
		/*wprintf(L"free %s", pTmp->DllName);*/
		GlobalFree(pTmp);
		/*wprintf(L"freed\n");*/
	}
	//GlobalFree(ctList);
	ct = NULL;

	pWalker = ct;
	/*wprintf(L"%d %d\n", pWalker, ct);*/
}


