//
// $Id: FileEffectiveRights.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 "FileEffectiveRights.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Class FileEffectiveRights  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//

FileEffectiveRights::FileEffectiveRights(string osNameIn, stringVector fileVectorIn, DBInterface *dbIn)
{
	er_tablename = "";
	er_tablename.append(osNameIn);
	er_tablename.append("FileEffectiveRights");
	
	fileVector = fileVectorIn;

	db = dbIn;
}

FileEffectiveRights::~FileEffectiveRights()
{

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

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

	// Delete any old data from this table.

	db->ClearTable(er_tablename);

	// Loop through each file contained in the fileVector and gather file attribute
	// information.

	LPTSTR lpszFilePath = NULL;
	DWORD dwResult = 0;
	DWORD dwBufferSize = 0;

	for (fvIterator=fileVector.begin(); fvIterator!=fileVector.end(); fvIterator++)
    {
		ZeroMemory(lpszFilePath, dwBufferSize);

		// Use the WinAPI ExpandEnvironmentStrings() to expand any environment-variable
		// strings (like %systemroot%) found in the file paths.  If the given buffer is
		// too small, ExpandEnvironmentStrings() returns the required buffer size.
		// Realloc the buffer and try again.

		dwResult = ExpandEnvironmentStrings((*fvIterator).c_str(),
										    lpszFilePath,
											dwBufferSize);
		if (dwResult > dwBufferSize)
		{
			dwBufferSize = dwResult;
			lpszFilePath = (LPTSTR)realloc(lpszFilePath, dwBufferSize * sizeof(TCHAR));
			if (lpszFilePath == NULL) dwResult = 0;
			else dwResult = ExpandEnvironmentStrings((*fvIterator).c_str(),
													 lpszFilePath,
													 dwBufferSize);
		}

		if (dwResult == 0)
		{
			string errorMessage = "";
			errorMessage.append("\nERROR: (FileEffectiveRights) There was an error ");
			errorMessage.append("expanding the file '");
			errorMessage.append(*fvIterator);
			errorMessage.append("'\n\n");
			cerr << errorMessage;
			Log::WriteLog(errorMessage);

			continue;
		}

		// Get file attribute data and stuff it into the database.

		GetFileEffectiveRights(lpszFilePath);
	}
}

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

void FileEffectiveRights::GetFileEffectiveRights(string fileIn)
{
	//------------------------------------------------------------------------------------//
	//
	//  ABSTRACT
	//
	//  
	//
	//------------------------------------------------------------------------------------//

	// Retrieve a copy of the security descriptor for the specified file.  If an error
	// occured, exit this app.

	DWORD res;
	PACL pdacl;
	PSECURITY_DESCRIPTOR pSD;

	res = GetNamedSecurityInfo(const_cast<char*>(fileIn.c_str()),	// object name
							   SE_FILE_OBJECT,						// object type
							   DACL_SECURITY_INFORMATION,			// information type
							   NULL,								// owner SID
							   NULL,								// primary group SID
							   &pdacl,								// DACL
							   NULL,								// SACL
							   &pSD);								// Security Descriptor

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

			if (Log::verboseMode == true)
			{
				string errorMessage = "";
				errorMessage.append("\nERROR: (FileEffectiveRights) The file '");
				errorMessage.append(fileIn);
				errorMessage.append("' does not exist.\n\n");
				cerr << errorMessage;
				Log::WriteLog(errorMessage);
			}

			return;
		}
		else
		{
			string errorMessage = "";
			errorMessage.append("\nERROR: (FileEffectiveRights) Unable to retrieve a copy ");
			errorMessage.append("of the security descriptor for '");
			errorMessage.append(fileIn);
			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 app.

    if ((IsValidSecurityDescriptor(pSD) == 0) || (IsValidAcl(pdacl) == 0))
	{
		LocalFree(pSD);
		string errorMessage = "";
		errorMessage.append("\nERROR: (FileEffectiveRights) Invalid DACL or security ");
		errorMessage.append("descriptor.  Cannot get effective rights for '");
		errorMessage.append(fileIn);
		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,						// access-control list
						  (LPVOID)&acl_size_info,		// ACL information
						  acl_size,						// size of ACL information
						  AclSizeInformation) != 0)		// info class
	{
		LPTSTR owner_name = NULL;
		LPTSTR domain_name = NULL;

		// Loop through each ACE found in the ACL.

		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: (FileEffectiveRights) Unable to get next ");
				errorMessage.append("ACE for '");
				errorMessage.append(fileIn);
				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: (FileEffectiveRights) Invalid SID for '");
				errorMessage.append(fileIn);
				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,					// 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);			// 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: (FileEffectiveRights) Could not allocate space.  ");
				errorMessage.append("Cannot get effective rights for this ACE for '");
				errorMessage.append(fileIn);
				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: (FileEffectiveRights) Could not allocate space.  ");
				errorMessage.append("Cannot get effective rights for this ACE for '");
				errorMessage.append(fileIn);
				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: (FileEffectiveRights) Unable to get the name ");
				errorMessage.append("of the account for this SID for '");
				errorMessage.append(fileIn);
				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[31 - j] = '1';
				else
					mask[31 - j] = '0';
			}
			mask[32] = 0;

			// Produce textual SID.

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

			// Make the SQL statement.

			string sqlStmt = "";
			sqlStmt.append("INSERT INTO '");
			sqlStmt.append(er_tablename);
			sqlStmt.append("' VALUES ('");
			sqlStmt.append(Common::FixQuotes(fileIn));
			sqlStmt.append("','");
			sqlStmt.append(Common::FixQuotes(owner_name));
			sqlStmt.append("','");
			sqlStmt.append(Common::FixQuotes(domain_name));
			sqlStmt.append("','");
			sqlStmt.append(Common::FixQuotes(sid_string));
			sqlStmt.append("','");
			sqlStmt.append(1, mask[16]);  // DELETE
			sqlStmt.append("','");
			sqlStmt.append(1, mask[17]);  // READ_CONTROL
			sqlStmt.append("','");
			sqlStmt.append(1, mask[18]);  // WRITE_DAC
			sqlStmt.append("','");
			sqlStmt.append(1, mask[19]);  // WRITE_OWNER
			sqlStmt.append("','");
			sqlStmt.append(1, mask[20]);  // SYNCHRONIZE
			sqlStmt.append("','");
			sqlStmt.append(1, mask[24]);  // ACCESS_SYSTEM_SECURITY
			sqlStmt.append("','");
			sqlStmt.append(1, mask[31]);  // GENERIC_READ
			sqlStmt.append("','");
			sqlStmt.append(1, mask[30]);  // GENERIC_WRITE
			sqlStmt.append("','");
			sqlStmt.append(1, mask[29]);  // GENERIC_EXECUTE
			sqlStmt.append("','");
			sqlStmt.append(1, mask[28]);  // GENERIC_ALL
			sqlStmt.append("','");
			sqlStmt.append(1, mask[0]);   // FILE_READ_DATA
			sqlStmt.append("','");
			sqlStmt.append(1, mask[1]);   // FILE_WRITE_DATA
			sqlStmt.append("','");
			sqlStmt.append(1, mask[2]);   // FILE_APPEND_DATA
			sqlStmt.append("','");
			sqlStmt.append(1, mask[3]);   // FILE_READ_EA
			sqlStmt.append("','");
			sqlStmt.append(1, mask[4]);   // FILE_WRITE_EA
			sqlStmt.append("','");
			sqlStmt.append(1, mask[5]);   // FILE_EXECUTE 
			sqlStmt.append("','");
			sqlStmt.append(1, mask[6]);   // FILE_DELETE_CHILD 
			sqlStmt.append("','");
			sqlStmt.append(1, mask[7]);   // FILE_READ_ATTRIBUTES
			sqlStmt.append("','");
			sqlStmt.append(1, mask[8]);   // FILE_WRITE_ATTRIBUTES
			sqlStmt.append("')");

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

			free(sid_string);
		}

		free(domain_name);
		free(owner_name);
		LocalFree(pSD);
	}
	else
	{
		LocalFree(pSD);
		string errorMessage = "";
		errorMessage.append("\nERROR: (FileEffectiveRights) Unable to retrieve access-control ");
		errorMessage.append("list.  Cannot get effective rights for '");
		errorMessage.append(fileIn);
		errorMessage.append("'.\n\n");
		cerr << errorMessage;
		Log::WriteLog(errorMessage);
		return;
	}
}
