//
// $Id: AuditEventPolicyProbe.cpp,v 1.2 2006/09/14 13:02:59 bakerj Exp $
//
//****************************************************************************************//
// Copyright (c) 2006, The MITRE Corporation
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without modification, are
// permitted provided that the following conditions are met:
//
//     * Redistributions of source code must retain the above copyright notice, this list
//       of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above copyright notice, this 
//       list of conditions and the following disclaimer in the documentation and/or other
//       materials provided with the distribution.
//     * Neither the name of The MITRE Corporation nor the names of its contributors may be
//       used to endorse or promote products derived from this software without specific 
//       prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY 
// EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
// OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT 
// SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
// OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
// HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
// TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
//****************************************************************************************//
#include "AuditEventPolicyProbe.h"

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  AuditEventPolicyProbe Class  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
AuditEventPolicyProbe* AuditEventPolicyProbe::instance = NULL;

AuditEventPolicyProbe::AuditEventPolicyProbe() {
	//------------------------------------------------------------------------------------//
	//  ABSTRACT
	//
	//
	//------------------------------------------------------------------------------------//
}

AuditEventPolicyProbe::~AuditEventPolicyProbe() {
	//------------------------------------------------------------------------------------//
	//  ABSTRACT
	//
	//  Do nothing for now
	//
	//------------------------------------------------------------------------------------//

}

//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Public Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
AbsProbe* AuditEventPolicyProbe::Instance() {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//	Ensure that the AuditEventPolicyProbe is a singleton.
	// -----------------------------------------------------------------------

	// Use lazy initialization
	if(instance == NULL) 
		instance = new AuditEventPolicyProbe();

	return instance;	
}

ItemVector* AuditEventPolicyProbe::CollectItems(Object *object) {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Run the AuditEventPolicyProbe
	// -----------------------------------------------------------------------
	ItemVector *collectedItems = NULL;

	Item* item = NULL;	

	//
	// First open a handle to a policy object.
	// msdn link on Opening a Policy Object Handle
	// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmgmt/security/opening_a_policy_object_handle.asp
	//

	LSA_OBJECT_ATTRIBUTES ObjectAttributes;
	NTSTATUS ntsResult;
	LSA_HANDLE lsahPolicyHandle;

	// Object attributes are reserved, so initialize to zeroes.
	ZeroMemory(&ObjectAttributes, sizeof(ObjectAttributes));

	// Get a handle to the Policy object.
	ntsResult = LsaOpenPolicy(
				NULL,    //Name of the target system. NULL opens localhost
				&ObjectAttributes, //Object attributes.
				POLICY_VIEW_AUDIT_INFORMATION , //Desired access permissions. POLICY_ALL_ACCESS
				&lsahPolicyHandle  //Receives the policy handle.
				);

	if (ntsResult != ERROR_SUCCESS) {
		// An error occurred. Display it as a win32 error code.
		wprintf(L"OpenPolicy returned %lu\n", LsaNtStatusToWinError(ntsResult));
	} 

	//
	// Second use the policy handle to get the audit event data.
	// msdn link on getting audit event data:
	// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secauthn/security/lsaqueryinformationpolicy.asp
	// The call below will retrieve a POLICY_AUDIT_EVENTS_INFO structure. 
	// msdn link on the above strucutre:
	// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmgmt/security/policy_audit_events_info.asp
	// if auditing is turned on loop through the array of POLICY_AUDIT_EVENT_OPTIONS
	// msdn link on the above structure
	// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmgmt/security/policy_audit_event_type.asp
	// 

	ntsResult = ERROR_SUCCESS;
	PPOLICY_AUDIT_EVENTS_INFO pPAEInfo = NULL;
	PWCHAR name = NULL;

	ntsResult = LsaQueryInformationPolicy(
				lsahPolicyHandle,                // Open handle to a Policy object.
				PolicyAuditEventsInformation,	 // The information to get.
				(PVOID *)&pPAEInfo               // Storage for the information.
				);

	if (ntsResult == ERROR_SUCCESS) {  

		item = this->CreateItem();
		item->SetStatus(OvalEnum::EXISTS_STATUS);
		collectedItems = new ItemVector();
		collectedItems->push_back(item);

		// if auditing is turned on loop through the auditing options
		if(pPAEInfo->AuditingMode) {

			// initialize the item with all of its fields
			ItemEntity *accountLogonItem = new ItemEntity("account_logon",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(accountLogonItem);
			ItemEntity *accountManagementItem = new ItemEntity("account_management",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(accountManagementItem);
			ItemEntity *detailedTrackingItem = new ItemEntity("detailed_tracking",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(detailedTrackingItem);
			ItemEntity *directoryServiceAccessItem = new ItemEntity("directory_service_access",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(directoryServiceAccessItem);
			ItemEntity *logonItem = new ItemEntity("logon",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(logonItem);
			ItemEntity *objectAccessItem = new ItemEntity("object_access",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(objectAccessItem);
			ItemEntity *policyChangeItem = new ItemEntity("policy_change",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(policyChangeItem);
			ItemEntity *privilegeUseItem = new ItemEntity("privilege_use",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(privilegeUseItem);
			ItemEntity *systemItem = new ItemEntity("system",  "", OvalEnum::STRING_TYPE, false, OvalEnum::ERROR_STATUS);
			item->AppendElement(systemItem);

			
			ULONG i = 0;
			ULONG current = 0;
			while(i < pPAEInfo->MaximumAuditEventCount) {
				current = pPAEInfo->EventAuditingOptions[i];

				switch (i) {
					case AuditCategorySystem:
						this->ReadAuditOptions(systemItem, current); 
						break;
					case AuditCategoryLogon:
						this->ReadAuditOptions(logonItem, current); 
						break;
					case AuditCategoryObjectAccess:
						this->ReadAuditOptions(objectAccessItem, current); 
						break;
					case AuditCategoryPrivilegeUse:
                        this->ReadAuditOptions(privilegeUseItem, current); 
						break;
					case AuditCategoryDetailedTracking:
						this->ReadAuditOptions(detailedTrackingItem, current); 
						break;
					case AuditCategoryPolicyChange:
						this->ReadAuditOptions(policyChangeItem, current); 
						break;
					case AuditCategoryAccountManagement:
						this->ReadAuditOptions(accountManagementItem, current); 
						break;
					case AuditCategoryDirectoryServiceAccess:
						this->ReadAuditOptions(directoryServiceAccessItem, current); 
						break;
					case AuditCategoryAccountLogon:
						this->ReadAuditOptions(accountLogonItem, current); 
						break;
					default:
						Log::Info("Unknown POLICY_AUDIT_EVENT_TYPE. ");
						break;
				}
				i++;
			}

		} else {
			// auditing is off so set all items to no auditing
			item->AppendElement(new ItemEntity("account_logon",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("account_management",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("detailed_tracking",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("directory_service_access",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("logon",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("object_access",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("policy_change",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("privilege_use",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
			item->AppendElement(new ItemEntity("system",  "AUDIT_NONE", OvalEnum::STRING_TYPE, true, OvalEnum::EXISTS_STATUS));
		}
	} else {
		// Show the corresponding win32 error code.
		throw ProbeException("Error obtaining audit event policy information - (win32) " + LsaNtStatusToWinError(ntsResult));
	}

	LsaFreeMemory(pPAEInfo);
	
	
	// close policy handle
	ntsResult = LsaClose(lsahPolicyHandle);

	return collectedItems;
}


//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~  Private Members  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~//
Item* AuditEventPolicyProbe::CreateItem() {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Return a new Item created for storing password policy information
	//
	// -----------------------------------------------------------------------

	Item* item = new Item(0, 
						"http://oval.mitre.org/XMLSchema/oval-system-characteristics-5#windows", 
						"win-sc", 
						"http://oval.mitre.org/XMLSchema/oval-system-characteristics-5#windows windows-system-characteristics-schema.xsd", 
						OvalEnum::ERROR_STATUS, 
						"auditeventpolicy_item");

	return item;
}

void AuditEventPolicyProbe::ReadAuditOptions(ItemEntity* itemEntity, ULONG auditPolicy) {
	// -----------------------------------------------------------------------
	//
	//  ABSTRACT
	//
	//  Read the Audit options and set the value of the ItemEntity.
	//	LSA Policy defines a mask for the valid event auditing options. 
	//	The POLICY_AUDIT_EVENT_MASK mask evaluates to TRUE if it is set 
	//	equal to any of the preceding event auditing options.
	//
	// -----------------------------------------------------------------------

	// AUDIT_NONE AUDIT_FAILURE AUDIT_SUCCESS AUDIT_SUCCESS_FAILURE


	if(auditPolicy & POLICY_AUDIT_EVENT_NONE) {
        itemEntity->SetValue("AUDIT_NONE");
		itemEntity->SetStatus(OvalEnum::EXISTS_STATUS);
	} else if(auditPolicy & POLICY_AUDIT_EVENT_FAILURE && auditPolicy & POLICY_AUDIT_EVENT_SUCCESS) {
		itemEntity->SetValue("AUDIT_SUCCESS_FAILURE");
		itemEntity->SetStatus(OvalEnum::EXISTS_STATUS);
	} else if(auditPolicy & POLICY_AUDIT_EVENT_FAILURE) {
		itemEntity->SetValue("AUDIT_FAILURE");
		itemEntity->SetStatus(OvalEnum::EXISTS_STATUS);
	} else if(auditPolicy & POLICY_AUDIT_EVENT_SUCCESS) {
		itemEntity->SetValue("AUDIT_SUCCESS");
		itemEntity->SetStatus(OvalEnum::EXISTS_STATUS);
	} else if (auditPolicy == POLICY_AUDIT_EVENT_UNCHANGED){
		// should not get here. For some reason we seem to hit this condition 
		// when looking at the permissions for AuditCategoryDetailedTracking
		// As documented at the link below i would not expect to get this value 
		// after doing a query. The value should only be used when doing a set.
		// Reference url:
		// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/secmgmt/security/policy_audit_events_info.asp
		itemEntity->SetStatus(OvalEnum::NOT_COLLECTED_STATUS);
	} else {
		// should never get here
		itemEntity->SetStatus(OvalEnum::ERROR_STATUS);
	} 
}
