//
// $Id: RegKeyEffectiveRights.cpp,v 1.1 2004/06/01 17:05:21 bakerj Exp $
//
//************************** Property of the MITRE Corporation ***************************//
//
// Copyright (c) 2003 - The MITRE Corporation
//
// This file is part of the OVAL Query Interpreter.
//
// The OVAL Query Interpreter 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.
//
// The OVAL Query Interpreter 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 the OVAL
// Query Interpreter; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA
//
//****************************************************************************************//

#include "RegKeyEffectiveRights.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Class RegKeyEffectiveRights  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

RegKeyEffectiveRights::RegKeyEffectiveRights(string osNameIn, stringVector keyVectorIn, DBInterface *dbIn)
{
	rp_tablename = "";
	rp_tablename.append(osNameIn);
	rp_tablename.append("RegKeyEffectiveRights");

	keyVector = keyVectorIn;

	db = dbIn;
}

RegKeyEffectiveRights::~RegKeyEffectiveRights()
{
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Public Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

void RegKeyEffectiveRights::Run()
{
	//------------------------------------------------------------------------------------//
	//
	//  ABSTRACT
	//
	//  
	//
	//------------------------------------------------------------------------------------//

	// Delete any old data from this table.

	db->ClearTable(rp_tablename);

	// Gather new data and put in table.

	for (kvIterator=keyVector.begin(); kvIterator!=keyVector.end(); kvIterator++)
    {
		GetRegKeyEffectiveRights((*kvIterator));
	}
}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Private Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

void RegKeyEffectiveRights::GetRegKeyEffectiveRights(string regkeyIn)
{
	//------------------------------------------------------------------------------------//
	//
	//  ABSTRACT
	//
	//  This fetches a regkey's acl.  It prints the actual bit mask as a decimal.  It also
	//  prints whether this is an "ALLOWED" ace or "DENIED" ace.
	//
	//------------------------------------------------------------------------------------//

	// Retrieve a copy of the security descriptor for the specified registry key.

	DWORD res;
	PACL pdacl;
	PSECURITY_DESCRIPTOR pSD;

	res = GetNamedSecurityInfo(const_cast<char*>(FixKey(regkeyIn).c_str()),
							   SE_REGISTRY_KEY,
							   DACL_SECURITY_INFORMATION,
							   NULL,
							   NULL,
							   &pdacl,
							   NULL,
							   &pSD);

	// Make sure that no error occured.  If one did, exit this function.

	if (res != ERROR_SUCCESS)
	{
		if ((res == ERROR_FILE_NOT_FOUND) || (res == ERROR_PATH_NOT_FOUND))
		{
			// The specified registry key does not exist on this system.

			if (Log::verboseMode == true)
			{
				string errorMessage = "";
				errorMessage.append("\nERROR: (RegKeyEffectiveRights) The registry key '");
				errorMessage.append(regkeyIn);
				errorMessage.append("' does not exist.\n\n");
				cerr << errorMessage;
				Log::WriteLog(errorMessage);
			}
		}
		else
		{
			char buffer[33];
			_itoa(res, buffer, 10);

			string errorMessage = "";
			errorMessage.append("\nERROR: (RegKeyEffectiveRights) Unable to retrieve the ");
			errorMessage.append("security descriptor for the registry key '");
			errorMessage.append(regkeyIn);
			errorMessage.append("' due to error ");
			errorMessage.append(buffer);
			errorMessage.append(".\n\n");
			cerr << errorMessage;
			Log::WriteLog(errorMessage);
		}

		return;
	}

	// Check to see if a valid DACL and security descriptor were returned.  If either is
	// invalid, then exit the function.

    if ((IsValidSecurityDescriptor(pSD) == 0) || (IsValidAcl(pdacl) == 0))
	{
		LocalFree(pSD);

		string errorMessage = "";
		errorMessage.append("\nERROR: (RegKeyEffectiveRights) Invalid DACL or security ");
		errorMessage.append("descriptor for the registry key '");
		errorMessage.append(regkeyIn);
		errorMessage.append("'.\n\n");
		cerr << errorMessage;
		Log::WriteLog(errorMessage);

		return;
	}

	// Get the access control list.

	ACL_SIZE_INFORMATION acl_size_info;
	DWORD acl_size = sizeof(ACL_SIZE_INFORMATION);

	if (GetAclInformation(pdacl, (LPVOID) & acl_size_info, acl_size, AclSizeInformation) == 0)
	{
		LocalFree(pSD);

		string errorMessage = "";
		errorMessage.append("\nERROR: (RegKeyEffectiveRights) Unable to get the access ");
		errorMessage.append("control list for the registry key '");
		errorMessage.append(regkeyIn);
		errorMessage.append("'.\n\n");
		cerr << errorMessage;
		Log::WriteLog(errorMessage);

		return;
	}

	// Loop through each ACE found in the ACL.

	LPTSTR owner_name = NULL;
	LPTSTR domain_name = NULL;
	DWORD ace_number;

	for (ace_number = 0; ace_number < acl_size_info.AceCount; ace_number++)
	{
		// Get the ACE.

		ACCESS_ALLOWED_ACE* ace;

        if (GetAce(pdacl, ace_number, (LPVOID *) & ace) == 0)
		{
			string errorMessage = "";
			errorMessage.append("\nERROR: (RegKeyEffectiveRights) Unable to get next ACE ");
			errorMessage.append("for the registry key '");
			errorMessage.append(regkeyIn);
			errorMessage.append("'.\n\n");
			cerr << errorMessage;
			Log::WriteLog(errorMessage);

			continue;
        }

		// Get the SID associated with this ACE.

		PSID psid = (PSID) & (ace->SidStart);

		// Check to make sure the SID is valid.

        if (!(IsValidSid(psid)))
		{
			string errorMessage = "";
			errorMessage.append("\nERROR: (RegKeyEffectiveRights) Invalid SID associated ");
			errorMessage.append("with the ACE for the registry key '");
			errorMessage.append(regkeyIn);
			errorMessage.append("'.\n\n");
			cerr << errorMessage;
			Log::WriteLog(errorMessage);

			continue;
        }

		// Call LookupAccountSid with owner_name_size = 0 and domain_name_size =0. This
		// will cause LookupAccountSid to fail and return with the required buffer sizes.

		SID_NAME_USE sid_type;

		DWORD owner_name_size = 0;
		DWORD domain_name_size = 0;

		LookupAccountSid(NULL,
						 psid,
						 owner_name,
						 &owner_name_size,
						 domain_name,
						 &domain_name_size,
						 &sid_type);

        owner_name_size++;
        owner_name = (LPTSTR) realloc(owner_name, owner_name_size * sizeof(TCHAR));
        if (owner_name == NULL)
		{
			string errorMessage = "";
			errorMessage.append("\nERROR: (RegKeyEffectiveRights) Unable to allocate ");
			errorMessage.append(" enough space for owner's name for the registy key '");
			errorMessage.append(regkeyIn);
			errorMessage.append("'.\n\n");
			cerr << errorMessage;
			Log::WriteLog(errorMessage);
			continue;
        }

        domain_name_size++;
        domain_name = (LPTSTR) realloc(domain_name, domain_name_size * sizeof(TCHAR));
        if (domain_name == NULL)
		{
			string errorMessage = "";
			errorMessage.append("\nERROR: (RegKeyEffectiveRights) Unable to allocate ");
			errorMessage.append(" enough space for domain name for the registry key '");
			errorMessage.append(regkeyIn);
			errorMessage.append("'.\n\n");
			cerr << errorMessage;
			Log::WriteLog(errorMessage);
			continue;
        }
		
		// Call LookupAccountSid again to retrieve the name of the account and the
		// name of the first domain on which this SID is found.

        if (LookupAccountSid(NULL,					// name of local or remote computer
							 psid,					// security identifier
							 owner_name,			// account name buffer
							 &owner_name_size,		// size of account name buffer
							 domain_name,			// domain name
							 &domain_name_size,		// size of domain name buffer
							 &sid_type) == 0)		// SID type
		{
			string errorMessage = "";
			errorMessage.append("\nERROR: (RegKeyEffectiveRights) Unable to get the ");
			errorMessage.append("name of the account for this SID for the registry key '");
			errorMessage.append(regkeyIn);
			errorMessage.append("'.\n\n");
			cerr << errorMessage;
			Log::WriteLog(errorMessage);
			continue;
        }

		// Convert access mask to binary.

		int j;
		char mask[33];

		for (j = 0; j < 32; j++)
		{
			if (ace->Mask & (1 << j))
				mask[j] = '1';
			else
				mask[j] = '0';
		}
		mask[32] = 0;

		// Produce textual SID.
		//
		// SMC-AUDIT: REVIEW: where are the restrictions on buffer size for writing to
		// sid_string?  Can an attacker control the SID in any fashion?
		//
		// ARB:  The buffer's size is controlled inside GetTextualSid().

		LPTSTR sid_string;
		Common::GetTextualSid(psid, &sid_string);

		UpdateDatabase(regkeyIn.c_str(), owner_name, domain_name, sid_string, mask);

		free(sid_string);
	}

	free(domain_name);
	free(owner_name);
	LocalFree(pSD);
}

string RegKeyEffectiveRights::FixKey(string regkeyIn)
{
	//------------------------------------------------------------------------------------//
	//
	//  ABSTRACT
	//
	//  Registy keys begin with one of the predefined keys:
	//
	//      HKEY_CLASSES_ROOT
	//      HKEY_CURRENT_USER
	//      HKEY_LOCAL_MACHINE
	//      HKEY_USERS
	//
	//  But in GetNamedSecurityInfo(), the names of registry keys must use the following
	//  literal strings to identify the predefined registry keys: "CLASSES_ROOT",
	//  "CURRENT_USER", "MACHINE", and "USERS". 
	//
	//------------------------------------------------------------------------------------//

	string returnKey = regkeyIn;

    if (strnicmp(regkeyIn.c_str(), "HKEY_LOCAL_MACHINE", 18) == 0)
	{
		returnKey.replace(0, 18, "MACHINE");
    }
	else if (strnicmp(regkeyIn.c_str(), "HKEY_USERS", 10) == 0)
	{
		returnKey.replace(0, 10, "USERS");
    }
	else if (strnicmp(regkeyIn.c_str(), "HKEY_CURRENT_USER", 17) == 0)
	{
		returnKey.replace(0, 17, "CURRENT_USER");
    }
	else if (strnicmp(regkeyIn.c_str(), "HKEY_CLASSES_ROOT", 17) == 0)
	{
		returnKey.replace(0, 17, "CLASSES_ROOT");
    }

	return returnKey;
}

void RegKeyEffectiveRights::UpdateDatabase(string regkeyIn, string ownerNameIn, string domainNameIn, string sidStringIn, char* maskIn)
{
	//------------------------------------------------------------------------------------//
	//
	//  ABSTRACT
	//
	//  Create and SQL statement with the provided data and then use this statement to
	//  update the database.
	//
	//------------------------------------------------------------------------------------//

	// Make the SQL statement.

	string sqlStmt = "";
	sqlStmt.append("INSERT INTO ");
	sqlStmt.append(rp_tablename);
	sqlStmt.append(" VALUES ('");
	sqlStmt.append(Common::FixQuotes(regkeyIn));
	sqlStmt.append("','");
	sqlStmt.append(Common::FixQuotes(ownerNameIn));
	sqlStmt.append("','");
	sqlStmt.append(Common::FixQuotes(domainNameIn));
	sqlStmt.append("','");
	sqlStmt.append(Common::FixQuotes(sidStringIn));
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[16]);  // STANDARD_DELETE
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[17]);  // STANDARD_READ_CONTROL
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[18]);  // STANDARD_WRITE_DAC
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[19]);  // STANDARD_WRITE_OWNER
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[20]);  // STANDARD_SYNCHRONIZE
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[24]);  // ACCESS_SYSTEM_SECURITY
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[31]);  // GENERIC_READ
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[30]);  // GENERIC_WRITE
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[29]);  // GENERIC_EXECUTE
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[28]);  // GENERIC_ALL
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[0]);   // KEY_QUERY_VALUE
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[1]);   // KEY_SET_VALUE
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[2]);   // KEY_CREATE_SUB_KEY
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[3]);   // KEY_ENUMERATE_SUB_KEYS
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[4]);   // KEY_NOTIFY
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[5]);   // KEY_CREATE_LINK
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[8]);   // KEY_WOW64_64KEY
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[9]);   // KEY_WOW64_32KEY
	sqlStmt.append("','");
	sqlStmt.append(1, maskIn[10]);  // KEY_WOW64_RES
	sqlStmt.append("')");

	// Update the database.
	db->ExecSQL(sqlStmt);
}
