/////////////////////////////////////////////////////////////////////////////
//  Copyright (C) 2004 Martin Scharpf. All Rights Reserved.
//
//  This program is free software; you can redistribute it and/or modify
//  it under the terms of the GNU General Public License as published by
//  the Free Software Foundation; either version 2 of the License, or
//  (at your option) any later version.
//
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU General Public License for more details.
//
//  You should have received a copy of the GNU General Public License
//  along with this program; if not, write to the Free Software
//  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,
//  USA.
//
// If the source code for the program is not available from the place from
// which you received this file, check 
// http://ultravnc.sourceforge.net/

#include "vncAccessControl.h"

#define __try
#define __leave goto cleanup
#define __finally cleanup:

/*
 * GetACL: Gets ACL from reg and puts it in class variable pACL.
 */
PACL
vncAccessControl::GetACL(void){
	HKEY hk = NULL; 
	PACL pInitACL = NULL;
	DWORD dwValueLength = 0;

	__try{
		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\iTALC Solutions\\iTALC\\Authentication"),
			0, KEY_QUERY_VALUE, &hk) != ERROR_SUCCESS){
			__leave;
		}

		// Read the ACL value from the VNC registry key
		// First call to RegQueryValueEx just gets the buffer length.
		if (RegQueryValueEx(hk, _T("LogonACL"), 0, 0, NULL, &dwValueLength) 
			!= ERROR_SUCCESS){
			__leave;
		}
		if (dwValueLength >= sizeof(ACL))
			pInitACL = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, dwValueLength);
		else {
			__leave;
		}
		if (RegQueryValueEx(hk, _T("LogonACL"), 0, 0, (LPBYTE) pInitACL, &dwValueLength)
			!= ERROR_SUCCESS){
			__leave;
		}

	} __finally {
		if (hk)
			RegCloseKey(hk); 
	}
	return pInitACL;
}



/*
 * GetSD: Accessor function for SecurityDescriptor
 *        Creates an (absolute) SD from the GetACL return value
 */
PSECURITY_DESCRIPTOR
vncAccessControl::GetSD(){
	PSECURITY_DESCRIPTOR pSD;
	PSECURITY_DESCRIPTOR pSelfRelativeSD;
	PACL pACL = NULL;
	DWORD dwBufferLength = 0;

	// If we can't retrieve a valid ACL we create an empty one (i.e. no access).
	if (!(pACL = GetACL()) || !IsValidAcl(pACL)) {
		pACL = (PACL) HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY,	sizeof(ACL));
		// Initialize the new ACL.
		if (!InitializeAcl(pACL, sizeof(ACL), ACL_REVISION)) {
			; // Todo: Report an error.
		}
	}

	// Construct SD
	pSD = HeapAlloc(GetProcessHeap(), 
		HEAP_ZERO_MEMORY, SECURITY_DESCRIPTOR_MIN_LENGTH);
	if(InitializeSecurityDescriptor(pSD, SECURITY_DESCRIPTOR_REVISION) &&
		// Set our ACL to the SD.
		SetSecurityDescriptorDacl(pSD, TRUE, pACL, FALSE) &&
		// AccessCheck() is picky about what is in the SD.
		SetSecurityDescriptorOwner(pSD, GetOwnerSID(), FALSE) &&
		SetSecurityDescriptorGroup(pSD, GetOwnerSID(), FALSE)) {
	} else {
		// Todo: Report an error.
	}
	// Make SD self-relative and use LocalAlloc
	MakeSelfRelativeSD(pSD, NULL, &dwBufferLength);
	pSelfRelativeSD = (PSECURITY_DESCRIPTOR) LocalAlloc(0, dwBufferLength);
	MakeSelfRelativeSD(pSD, pSelfRelativeSD, &dwBufferLength);
	FreeSD(pSD);
	return pSelfRelativeSD;
}

/*
 * SetSD: Changes the class variable pACL and the reg key.
 *        The ACL is the DACL of the provided SD.
 */
BOOL
vncAccessControl::SetSD(PSECURITY_DESCRIPTOR pSD){
	BOOL isOK = FALSE;
	BOOL bDaclPresent = FALSE;
	BOOL bDaclDefaulted = FALSE;
	PACL pDACL = NULL;

	GetSecurityDescriptorDacl(pSD, &bDaclPresent, &pDACL, &bDaclDefaulted);

	if (bDaclPresent && pDACL && IsValidAcl(pDACL) && StoreACL(pDACL))
		isOK = TRUE;
	return isOK;
}

/*
 * GetOwnerSID: Helperfunction for SD creation
 */
PSID
vncAccessControl::GetOwnerSID(void){
	PSID pAdminSid = NULL;
	SID_IDENTIFIER_AUTHORITY SIDAuth = { SECURITY_NT_AUTHORITY };
	// Create a SID for the BUILTIN\Administrators group.
	AllocateAndInitializeSid( &SIDAuth, 2,
		SECURITY_BUILTIN_DOMAIN_RID,
		DOMAIN_ALIAS_RID_ADMINS,
		0, 0, 0, 0, 0, 0, &pAdminSid);
	return pAdminSid;
}

/*
 * StoreACL: Stores the value of the class variable pACL in the registry.
 * Returns TRUE if successful.
 */
BOOL 
vncAccessControl::StoreACL(PACL pACL){
    HKEY hk = NULL; 
	BOOL isSaveOK = FALSE;

    ACL_SIZE_INFORMATION AclInfo = {0, 0, 0};
	DWORD nAclInformationLength = sizeof(AclInfo);

	// Todo: Better error handling
	if (pACL)
		GetAclInformation(pACL, &AclInfo, nAclInformationLength, AclSizeInformation);

	__try{ if (RegCreateKey(HKEY_LOCAL_MACHINE, _T("Software\\iTALC Solutions\\iTALC\\Authentication"), &hk)
			!= ERROR_SUCCESS){
			__leave;
		}
		  if (hk)
		  RegCloseKey(hk);

		if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("Software\\iTALC Solutions\\iTALC\\Authentication"), 0, KEY_SET_VALUE, &hk)
			!= ERROR_SUCCESS){
			__leave;
		}
		
		if (RegSetValueEx(hk, _T("LogonACL"), 0, REG_BINARY, (LPBYTE) pACL, AclInfo.AclBytesInUse)
			!= ERROR_SUCCESS){
			__leave;
		}
		isSaveOK = TRUE;
	} __finally {
		if (hk)
			RegCloseKey(hk); 
	}
	return isSaveOK;
} 

/*
 * FreeSD: Frees the memory of an absolute SD.
 */
void
vncAccessControl::FreeSD(PSECURITY_DESCRIPTOR pSD){
	PSID pOwnerSID = NULL;
	PSID pGroupSID = NULL;
	PACL pDACL = NULL;
	PACL pSACL = NULL;
	BOOL bOwnerDefaulted = FALSE;
	BOOL bGroupDefaulted = FALSE;
	BOOL bDaclPresent = FALSE;
	BOOL bDaclDefaulted = FALSE;
	BOOL bSaclPresent = FALSE;
	BOOL bSaclDefaulted = FALSE;
	
	if (pSD) {
		GetSecurityDescriptorOwner(pSD, &pOwnerSID, &bOwnerDefaulted);
		GetSecurityDescriptorGroup(pSD, &pGroupSID, &bGroupDefaulted);
		GetSecurityDescriptorDacl(pSD, &bDaclPresent, &pDACL, &bDaclDefaulted);
		GetSecurityDescriptorSacl(pSD, &bSaclPresent, &pSACL, &bSaclDefaulted);
	}
	// Clean up
	if (pSD)
		HeapFree(GetProcessHeap(), 0, pSD);
	if (bDaclPresent && pDACL)
		HeapFree(GetProcessHeap(), 0, pDACL);
	if (bSaclPresent && pSACL)
		HeapFree(GetProcessHeap(), 0, pSACL);
	if (pOwnerSID)
		HeapFree(GetProcessHeap(), 0, pOwnerSID);
	if (pGroupSID)
		HeapFree(GetProcessHeap(), 0, pGroupSID);

}
