|
|
//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright(C) 2001 - 2002 Microsoft Corporation
//
// File: auditing.cxx
//
//----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#include "authzi.h"
#include "msaudite.h"
#include "security.hxx"
#include "auditing.hxx"
AUTHZ_RESOURCE_MANAGER_HANDLE ghRM = 0; AUTHZ_AUDIT_EVENT_TYPE_HANDLE ghAuditEventType = 0;
HRESULT AuditATJob(const AT_INFO &AtInfo, LPCWSTR pwszFileName) { HRESULT hr = S_OK;
//
// Impersonate the caller
//
DWORD RpcStatus = RpcImpersonateClient(NULL); if (RpcStatus != RPC_S_OK) { hr = _HRESULT_FROM_WIN32(RpcStatus); return hr; }
//
// Open the thread token
//
HANDLE hThreadToken = NULL; BOOL bTokenOpened = OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, // Desired access.
TRUE, // Open as self.
&hThreadToken); //
// End impersonation.
//
if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK) { ERR_OUT("RpcRevertToSelf", RpcStatus); schAssert(!"RpcRevertToSelf failed"); hr = _HRESULT_FROM_WIN32(RpcStatus); }
if (SUCCEEDED(hr)) { if (bTokenOpened) { DWORD cbAccountSid = MAX_SID_SIZE; BYTE pbTaskSid[MAX_SID_SIZE]; hr = GetNSAccountSid(pbTaskSid, cbAccountSid); if (SUCCEEDED(hr)) { hr = AuditJob(hThreadToken, pbTaskSid, pwszFileName); } CloseHandle(hThreadToken); } else { hr = _HRESULT_FROM_WIN32(GetLastError()); CHECK_HRESULT(hr); } }
return hr; }
HRESULT AuditJob( HANDLE hThreadToken, PSID pTaskSid, LPCWSTR pwszFileName ) { //
// Caller has already been impersonated and thread token opened prior to calling of this function
//
HRESULT hr = S_OK;
//
// Get the user SID
//
BYTE rgbTokenInformation[USER_TOKEN_STACK_BUFFER_SIZE]; TOKEN_USER* pTokenUser = (TOKEN_USER*) rgbTokenInformation; DWORD cbReturnLength; if (GetTokenInformation(hThreadToken, TokenUser, pTokenUser, USER_TOKEN_STACK_BUFFER_SIZE, &cbReturnLength)) { //
// Get the Authentication ID
//
BYTE rgbTokenStatistics[sizeof(TOKEN_STATISTICS)]; TOKEN_STATISTICS* pTokenStatistics = (TOKEN_STATISTICS*) rgbTokenStatistics; if (GetTokenInformation(hThreadToken, TokenStatistics, pTokenStatistics, sizeof(TOKEN_STATISTICS), &cbReturnLength)) { DWORD dwRet = GenerateJobCreatedAudit(pTokenUser->User.Sid, pTaskSid, &(pTokenStatistics->AuthenticationId), pwszFileName); hr = _HRESULT_FROM_WIN32(dwRet); } else { hr = _HRESULT_FROM_WIN32(GetLastError()); } } else { hr = _HRESULT_FROM_WIN32(GetLastError()); }
return hr;
}
HRESULT GetJobAuditInfo( LPCWSTR pwszFileName, DWORD* pdwFlags, LPWSTR* ppwszCommandLine, LPWSTR* ppwszTriggers, FILETIME* pftNextRun ) { if (!pwszFileName || pwszFileName[0] == L'\0' || !pftNextRun || !ppwszTriggers) { CHECK_HRESULT(E_INVALIDARG); return(E_INVALIDARG); }
//
// Init
//
*pdwFlags = 0; *ppwszCommandLine = NULL; *ppwszTriggers = NULL; //
// Instantiate the job to get its run properties.
//
CJob* pJob = CJob::Create(); if (!pJob) { return E_OUTOFMEMORY; }
HRESULT hr = pJob->LoadP(pwszFileName, 0, FALSE, TRUE); if (FAILED(hr)) { schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->LoadP failed with error 0x%x\n", hr)); } else { //
// Get the flags
//
pJob->GetAllFlags(pdwFlags); // this call returns void
//
// Get the application
//
WCHAR* pwszApplicationName = NULL;
hr = pJob->GetApplicationName(&pwszApplicationName); if (FAILED(hr)) { schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetApplicationName failed with error 0x%x\n", hr)); } else { //
// Get the parameters
//
WCHAR* pwszParameters = NULL; hr = pJob->GetParameters(&pwszParameters); if (FAILED(hr)) { schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetParameters failed with error 0x%x\n", hr)); } else { //
// Produce command line by concatenating application name and parameters
//
size_t cchBuff = lstrlenW(pwszApplicationName) + 1 + lstrlenW(pwszParameters) + 1; WCHAR* pwszCommandLine = new WCHAR[cchBuff]; if (!pwszCommandLine) { hr = E_OUTOFMEMORY; } else { StringCchCopy(pwszCommandLine, cchBuff, pwszApplicationName); StringCchCat(pwszCommandLine, cchBuff, L" "); StringCchCat(pwszCommandLine, cchBuff, pwszParameters);
//
// Return pointer to the command line string.
// This string will need to be deleted by the caller.
//
*ppwszCommandLine = pwszCommandLine; //
// Zero out next run time return value in case of any failures.
// Get the next run time, convert it to a local file time then to UTC.
//
//pftNextRun->dwLowDateTime = 0;
//pftNextRun->dwHighDateTime = 0;
//
// due to temporary issues, use a different value than 0
// 1/1/1700 00:00:00,000
pftNextRun->dwLowDateTime = 0xAEC64000; pftNextRun->dwHighDateTime = 0x006EFDDD;
SYSTEMTIME stNextRun; hr = pJob->GetNextRunTime(&stNextRun); if (S_OK == hr) { FILETIME ftNextRunLocal; if (SystemTimeToFileTime(&stNextRun, &ftNextRunLocal)) { LocalFileTimeToFileTime(&ftNextRunLocal, pftNextRun); } } //
// Now get the triggers.
//
// Build up a string consisting of formatted string
// representations of all the triggers for the task.
//
WORD cTriggers; hr = pJob->GetTriggerCount(&cTriggers); if (FAILED(hr)) { schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetTriggerCount failed with error 0x%x\n", hr)); } else { //
// Allocate initial buffer for concatenation of all trigger strings
//
const DWORD INITIAL_BUFFER_SIZE = 256; // start with enough space for 256 chars including null
const DWORD BUFFER_SIZE_INCREMENT = 256; // if necessary, grow buffer by this amount
DWORD dwSize = INITIAL_BUFFER_SIZE; WCHAR* pwszTriggers = new WCHAR[dwSize]; // this will need to be deleted by the caller
if (!pwszTriggers) { hr = E_OUTOFMEMORY; } else { pwszTriggers[0] = L'\0';
DWORD dwSizeRequired; WCHAR* pwszTemp; WCHAR* pwszTriggerString;
for (short nTrigger = 0; nTrigger < cTriggers && SUCCEEDED(hr); nTrigger++) { hr = pJob->GetTriggerString(nTrigger, &pwszTriggerString); if (FAILED(hr)) { schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetTriggerString failed with error 0x%x\n", hr)); } else { //
// If not big enough, create larger buffer, copy, and delete old
//
dwSizeRequired = lstrlenW(pwszTriggers) + 2 // two spaces
+ lstrlenW(pwszTriggerString) + 2; // period plus null terminator
if (dwSizeRequired > dwSize) { dwSize += BUFFER_SIZE_INCREMENT; if (dwSizeRequired > dwSize) // if still not big enough (very unlikely),
dwSize = dwSizeRequired; // make it big enough
pwszTemp = new WCHAR[dwSize]; if (!pwszTemp) { hr = E_OUTOFMEMORY; } else { StringCchCopy(pwszTemp, dwSize, pwszTriggers); delete [] pwszTriggers; pwszTriggers = pwszTemp; } }
if (SUCCEEDED(hr)) { if (lstrlenW(pwszTriggers) > 0) StringCchCat(pwszTriggers, dwSize, L" "); StringCchCat(pwszTriggers, dwSize, pwszTriggerString); StringCchCat(pwszTriggers, dwSize, L"."); }
CoTaskMemFree(pwszTriggerString); } }
if (SUCCEEDED(hr)) { //
// If all was a success, return the pointer to the string of triggers.
// This string will need to be deleted by the caller.
//
*ppwszTriggers = pwszTriggers; } else { //
// If something went wrong, let's go ahead and clean up now.
//
if (pwszTriggers) delete [] pwszTriggers; } } } }
CoTaskMemFree(pwszParameters); }
CoTaskMemFree(pwszApplicationName); } }
if (pJob) { pJob->Release(); }
return hr; }
/***************************************************************************\
* FUNCTION: StartupAuditing * * PURPOSE: Creates the resource manager and the audit event type handles. * * PARAMETERS: * * RETURNS: win32 error code * * History: * 12-05-2001 maxa Created * \***************************************************************************/
DWORD StartupAuditing( ) { DWORD dwError = ERROR_SUCCESS; BOOL fResult = FALSE; BOOL fWasEnabled = TRUE;
if (ghRM && ghAuditEventType) { goto Cleanup; }
dwError = EnableNamedPrivilege( L"SeAuditPrivilege", TRUE, &fWasEnabled);
if (dwError != ERROR_SUCCESS) { goto Cleanup; }
fResult = AuthzInitializeResourceManager( 0, // no special flags
NULL, // PFN_AUTHZ_DYNAMIC_ACCESS_CHECK
NULL, // PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS
NULL, // PFN_AUTHZ_FREE_DYNAMIC_GROUPS
L"Scheduler", // RM name
&ghRM );
if (!fResult) { dwError = GetLastError(); goto Cleanup; }
fResult = AuthziInitializeAuditEventType( 0, SE_CATEGID_DETAILED_TRACKING, SE_AUDITID_JOB_CREATED, 7, &ghAuditEventType );
if (!fResult) { dwError = GetLastError(); goto Cleanup; }
Cleanup:
if (!fWasEnabled) { EnableNamedPrivilege( L"SeAuditPrivilege", FALSE, &fWasEnabled); }
if (dwError != ERROR_SUCCESS) { ShutdownAuditing(); }
return dwError; }
/***************************************************************************\
* FUNCTION: ShutdownAuditing * * PURPOSE: Deletes the resource manager and the audit event type handles. * * PARAMETERS: * * RETURNS: * * History: * 12-05-2001 maxa Created * \***************************************************************************/
VOID ShutdownAuditing( ) { if (ghAuditEventType) { AuthziFreeAuditEventType(ghAuditEventType); ghAuditEventType = 0; }
if (ghRM) { AuthzFreeResourceManager(ghRM); ghRM = 0; } }
/***************************************************************************\
* FUNCTION: GenerateJobCreatedAudit * * PURPOSE: Generates an audit-event indicating that a job has * been created. * * PARAMETERS: pUserSid: * SID of the account that created the job. * pTaskSid: * SID of the account the job is to run as. * pLogonId: * LogonId of the account that created the job. * pszFileName: * File name of the newly created job in the Tasks folder. * * RETURNS: win32 error code * * History: * 10-01-2001 maxa Created * 11-07-2001 shbrown Updated for use with tasks rather than AT jobs * \***************************************************************************/
DWORD GenerateJobCreatedAudit( IN PSID pUserSid, IN PSID pTaskSid, IN PLUID pLogonId, IN PCWSTR pwszFileName ) { DWORD dwError = ERROR_SUCCESS; BOOL fResult = FALSE; AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent = NULL; AUDIT_PARAMS AuditParams = {0}; AUDIT_PARAM ParamArray[10] = {APT_None}; PSID pDummySid = NULL;
ASSERT(pUserSid); ASSERT(pTaskSid); ASSERT(pLogonId); ASSERT(pwszFileName && pwszFileName[0]); ASSERT(ghRM); ASSERT(ghAuditEventType);
//
// Get the job audit info
//
DWORD dwFlags; LPWSTR pwszCommandLine = NULL; // this will need to be deleted after use
LPWSTR pwszTriggers = NULL; // this will need to be deleted after use
FILETIME ftNextRun; HRESULT hr = GetJobAuditInfo(pwszFileName, &dwFlags, &pwszCommandLine, &pwszTriggers, &ftNextRun); if (FAILED(hr)) { goto Cleanup; }
AuditParams.Parameters = ParamArray;
fResult = AuthziInitializeAuditParams( APF_AuditSuccess, &AuditParams, &pDummySid, L"Security", 7, APT_String | AP_Filespec, pwszFileName, APT_String, pwszCommandLine, APT_String, pwszTriggers, APT_Time, ftNextRun, APT_Ulong | AP_FormatHex, dwFlags, APT_Sid, pTaskSid, APT_LogonId, *pLogonId );
if (!fResult) { goto Error; }
//
// this is ugly, but currently there is no other way
// do we still need this?
//
ParamArray[0].Data0 = (ULONG_PTR)pUserSid;
fResult = AuthziInitializeAuditEvent( 0, // flags
ghRM, // resource manager
ghAuditEventType, &AuditParams, NULL, // hAuditQueue
INFINITE, // time out
L"", L"", L"", L"", // obj access strings
&hAuditEvent );
if (!fResult) { goto Error; }
fResult = AuthziLogAuditEvent( 0, // flags
hAuditEvent, NULL // reserved
);
if (!fResult) { goto Error; }
Cleanup:
if (pwszCommandLine) { delete [] pwszCommandLine; }
if (pwszTriggers) { delete [] pwszTriggers; }
if (hAuditEvent) { AuthzFreeAuditEvent(hAuditEvent); }
if (pDummySid) { LocalFree(pDummySid); }
return dwError;
Error:
dwError = GetLastError(); goto Cleanup; }
/***************************************************************************\
* FUNCTION: EnableNamedPrivilege * * PURPOSE: Enable or disable a privilege by its name. * * PARAMETERS: pszPrivName: * Name of privilege to enable. * fEnable: * Enable/Disable flag. * pfWasEnabled: * Optional pointer to flag to receive the previous state. * * RETURNS: win32 error code * * History: * 12-05-2001 maxa Created * \***************************************************************************/
DWORD EnableNamedPrivilege( IN PCWSTR pszPrivName, IN BOOL fEnable, OUT PBOOL pfWasEnabled OPTIONAL ) { DWORD dwError = ERROR_SUCCESS; BOOL fResult; HANDLE hToken = 0; DWORD dwSize = 0; TOKEN_PRIVILEGES newPriv; TOKEN_PRIVILEGES oldPriv;
fResult = OpenProcessToken( GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &hToken);
if (!fResult) { dwError = GetLastError(); goto Cleanup; }
fResult = LookupPrivilegeValue( 0, pszPrivName, &newPriv.Privileges[0].Luid);
if (!fResult) { dwError = GetLastError(); goto Cleanup; }
newPriv.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0; newPriv.PrivilegeCount = 1;
fResult = AdjustTokenPrivileges( hToken, FALSE, &newPriv, sizeof oldPriv, &oldPriv, &dwSize);
if (!fResult) { dwError = GetLastError(); goto Cleanup; }
if (pfWasEnabled) { if (oldPriv.PrivilegeCount == 0) { *pfWasEnabled = fEnable; } else { *pfWasEnabled = (oldPriv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ? TRUE : FALSE; } }
Cleanup:
if (hToken) { CloseHandle(hToken); }
return dwError; }
|