Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

727 lines
21 KiB

//+---------------------------------------------------------------------------
//
// 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;
}