/*
  Copyright (C) 2000. Norbert Klasen.  All rights reserved.
  
  This module illustrates how to implement password change
  notification for synchronization of LDAP servers.        				
*/

#define DEBUG
#define UNICODE

#include "messages.h"
#include <windows.h>
#include <ntsecapi.h> 
#include <stdio.h>
#include <winldap.h>

#ifndef STATUS_SUCCESS
#define STATUS_SUCCESS  ((NTSTATUS)0x00000000L)
#endif

WCHAR host[256];
WCHAR namingAttribute[256];
WCHAR baseDn[256];
WCHAR binddn[256];
WCHAR bindpw[32];
DWORD namingAttributeSize, baseDnSize;

HANDLE eventlog;
PLDAP ld = NULL;
LPWSTR dnFormatString;


BOOLEAN CertRoutine (
			PLDAP Connection,
			PSecPkgContext_IssuerListInfoEx trusted_CAs,
			HCERTSTORE hCertStore,
			DWORD* pcCreds)
{
	// do not use a client certificate
	return FALSE;
}



NTSTATUS NTAPI PasswordChangeNotify(
					 PUNICODE_STRING UserName,
					 ULONG RelativeId,
					 PUNICODE_STRING Password)
{
	ULONG result;
	LDAPMod userpasswordMod;
	LPWSTR userpassword_values[2];
	LDAPMod *mods[2];
	LPWSTR dn;
	LPWSTR lpMsgBuf;
	
#ifdef DEBUG
	WCHAR RelativeIdString[16]; // DWORD SubAuthority
	LPWSTR lpStrings[3];
	lpStrings[0] = UserName->Buffer;
	lpStrings[1] = Password->Buffer;
	swprintf( RelativeIdString, L"%lu", RelativeId );
	lpStrings[2] = RelativeIdString;
	ReportEvent(eventlog, EVENTLOG_INFORMATION_TYPE, 0, MSG_PROMISCUOUS_LOG, NULL, 3, 0, lpStrings, NULL);
#endif /* DEBUG */
	
	
	// setup LDAPMod structure
	userpassword_values[0] = Password->Buffer;
	userpassword_values[1] = NULL;
	userpasswordMod.mod_op = LDAP_MOD_REPLACE;
	userpasswordMod.mod_type = L"userPassword";
	userpasswordMod.mod_values = userpassword_values; 

	mods[0] = &userpasswordMod;
	mods[1] = NULL;
	
	// generate dn
	dn = HeapAlloc(GetProcessHeap(), 0, UserName->Length + (256*sizeof(WCHAR)));
	if ( !dn ) {
		return FALSE;
	}
	swprintf(dn, dnFormatString, UserName->Length / sizeof(WCHAR), UserName->Buffer);
	 
  // modify password via ldap
	result = ldap_modify_ext_s(ld, dn, mods, NULL, NULL);
	if ( result != ERROR_SUCCESS ) {
		lpMsgBuf = ldap_err2string(result);
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, NULL, 1, 0, &lpMsgBuf, NULL);
		HeapFree(GetProcessHeap(), 0, dn);
		return FALSE;
	}
	
	HeapFree(GetProcessHeap(), 0, dn);
	
	return STATUS_SUCCESS;
}


BOOL NTAPI PasswordFilter(
			   PUNICODE_STRING UserName,
			   PUNICODE_STRING FullName,
			   PUNICODE_STRING Password,
			   BOOL SetOperation)
{
	return TRUE;
}


BOOL NTAPI InitializeChangeNotify(void)
{
	LONG result;
	HKEY hk; 
	DWORD type, size;
	LPTSTR lpMsgBuf;
	ULONG version = LDAP_VERSION3;
	LPWSTR hostptr;

	// register with eventlog
	eventlog = RegisterEventSource(NULL, L"ldapsync");
	if ( !eventlog ) {
		return FALSE;
	}

	// get parameters from registry
	result = RegOpenKeyEx(HKEY_LOCAL_MACHINE, L"System\\CurrentControlSet\\Control\\Lsa\\ldapsync",0,KEY_READ,&hk);
	if ( result != ERROR_SUCCESS ) {
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,result,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL );
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, NULL, 1, 0, &lpMsgBuf, NULL);
		LocalFree( lpMsgBuf );
		DeregisterEventSource(eventlog);
		return FALSE;
	}
	
	size = sizeof(host);
	result = RegQueryValueEx(hk, L"host", NULL, &type, host, &size);

	size = sizeof(binddn);
	result = RegQueryValueEx(hk, L"binddn", NULL, &type, binddn, &size);

	size = sizeof(bindpw);
	result = RegQueryValueEx(hk, L"bindpw", NULL, &type, bindpw, &size);

	namingAttributeSize = sizeof(namingAttribute);
	result = RegQueryValueEx(hk, L"namingAttribute", NULL, &type, namingAttribute, &namingAttributeSize);

	baseDnSize = sizeof(baseDn);
	result = RegQueryValueEx(hk, L"baseDn", NULL, &type, baseDn, &baseDnSize);

  // generate printf string for dn generation
	dnFormatString = HeapAlloc(GetProcessHeap(), 0, namingAttributeSize + baseDnSize + sizeof( L"=%.*ls," ) );
	if ( !dnFormatString ) {
		return FALSE;
	}
	swprintf( dnFormatString, L"%s=%%.*ls,%s", namingAttribute, baseDn );

	if ( result != ERROR_SUCCESS ) {
		FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
			NULL,result,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
			(LPTSTR) &lpMsgBuf,
			0,
			NULL );
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, NULL, 1, 0, &lpMsgBuf, NULL);
		LocalFree( lpMsgBuf );
		DeregisterEventSource(eventlog);
		return FALSE;
	} else if ( type != REG_SZ ) {
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_WRONG_HOST_KEY_TYPE, NULL, 0, 0, NULL, NULL);
		DeregisterEventSource(eventlog);
		return FALSE;
	} 
	
	// success
	hostptr = host;
	ReportEvent(eventlog, EVENTLOG_INFORMATION_TYPE, 0, MSG_HOST, NULL, 1, 0, &hostptr, NULL);
		
	result = RegCloseKey(hk);


	// initialize LDAP connection
	ld = ldap_sslinit(host, LDAP_SSL_PORT, 1);
	if ( ld == NULL ) {
		result = LdapGetLastError();
		lpMsgBuf = ldap_err2string(result);
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, NULL, 1, 0, &lpMsgBuf, NULL);
		DeregisterEventSource(eventlog);
		return FALSE;
	}

  // set protocol version to ldapv3
	result = ldap_set_option( ld, LDAP_OPT_PROTOCOL_VERSION, &version );
	if ( result != LDAP_SUCCESS ) {
		lpMsgBuf = ldap_err2string(result);
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, NULL, 1, 0, &lpMsgBuf, NULL);
		DeregisterEventSource(eventlog);
		return FALSE;
	}

	// register client certificate routine
	ldap_set_option( ld, LDAP_OPT_CLIENT_CERTIFICATE, &CertRoutine);
	if ( result != LDAP_SUCCESS ) {
		lpMsgBuf = ldap_err2string(result);
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, NULL, 1, 0, &lpMsgBuf, NULL);
		DeregisterEventSource(eventlog);
		return FALSE;
	}
	
  // bind to ldap server
	result = ldap_simple_bind_s(ld, binddn, bindpw);
	if ( result != LDAP_SUCCESS ) {
		lpMsgBuf = ldap_err2string(result);
		ReportEvent(eventlog, EVENTLOG_ERROR_TYPE, 0, MSG_ERROR, NULL, 1, 0, &lpMsgBuf, NULL);
		DeregisterEventSource(eventlog);
		return FALSE;
	}

	return TRUE;
}
