711 lines
18 KiB
C++
711 lines
18 KiB
C++
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996.
|
|
//
|
|
// File: sadat.cxx
|
|
//
|
|
// Contents: Routines which manipulate the SA.DAT file in the Tasks
|
|
// folder. This file is used by both the service and the UI
|
|
// to determine service state, OS info, etc.
|
|
//
|
|
// Classes: None.
|
|
//
|
|
// Functions: SADatGetData
|
|
// SADatPath
|
|
// SADatCreate
|
|
// SADatSetData
|
|
// SADatSetSecurity
|
|
//
|
|
// History: 08-Jul-96 MarkBl Created
|
|
// 22-May-01 drbeck, jbenton Added SADatSetSecurity
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "..\pch\headers.hxx"
|
|
#pragma hdrstop
|
|
#include "..\inc\debug.hxx"
|
|
#include "..\inc\sadat.hxx"
|
|
|
|
//Required for adding ACE to sa.dat file
|
|
#include <Accctrl.h>
|
|
#include <Aclapi.h>
|
|
|
|
DWORD
|
|
SADatSetSecurity(
|
|
HANDLE hFile); // handle to file to add ACE
|
|
|
|
HRESULT
|
|
SADatGetData(
|
|
HANDLE hFile,
|
|
DWORD cbData,
|
|
BYTE rgbData[]);
|
|
|
|
void
|
|
SADatPath(
|
|
LPCTSTR ptszFolderPath,
|
|
LPTSTR ptszSADatPath);
|
|
|
|
#ifdef _CHICAGO_
|
|
//
|
|
// These routines exist on Win98, NT4 and NT5 but not on Win95
|
|
//
|
|
typedef VOID (APIENTRY *PTIMERAPCROUTINE)(
|
|
LPVOID lpArgToCompletionRoutine,
|
|
DWORD dwTimerLowValue,
|
|
DWORD dwTimerHighValue
|
|
);
|
|
|
|
typedef HANDLE (WINAPI *PFNCreateWaitableTimerA)(
|
|
LPSECURITY_ATTRIBUTES lpTimerAttributes,
|
|
BOOL bManualReset,
|
|
LPCSTR lpTimerName
|
|
);
|
|
|
|
typedef BOOL (WINAPI *PFNSetWaitableTimer)(
|
|
HANDLE hTimer,
|
|
const LARGE_INTEGER *lpDueTime,
|
|
LONG lPeriod,
|
|
PTIMERAPCROUTINE pfnCompletionRoutine,
|
|
LPVOID lpArgToCompletionRoutine,
|
|
BOOL fResume
|
|
);
|
|
#endif // _CHICAGO_
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SADatGetData
|
|
//
|
|
// Synopsis: Retrieve and validate data from the file, SA.DAT, located
|
|
// in the folder path specified.
|
|
//
|
|
// Arguments: [ptszFolderPath] -- SA.DAT path location.
|
|
// [cbData] -- Data buffer size.
|
|
// [rgbData] -- Data buffer.
|
|
// [phFile] -- Optional return handle.
|
|
//
|
|
// Returns: S_OK
|
|
// E_UNEXPECTED if the amount read isn't what we expected or the
|
|
// data is invalid.
|
|
// Create/ReadFile HRESULT status code on failure.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
SADatGetData(
|
|
LPCTSTR ptszFolderPath,
|
|
DWORD cbData,
|
|
BYTE rgbData[],
|
|
HANDLE * phFile)
|
|
{
|
|
schAssert(cbData >= SA_DAT_VERSION_ONE_SIZE);
|
|
|
|
//
|
|
// Open SA.DAT in the folder path indicated. Fail if it doesn't exist.
|
|
//
|
|
|
|
TCHAR tszSADatPath[MAX_PATH + 1];
|
|
SADatPath(ptszFolderPath, tszSADatPath);
|
|
|
|
HANDLE hFile = CreateFile(tszSADatPath,
|
|
GENERIC_READ |
|
|
(phFile != NULL ? GENERIC_WRITE : 0),
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
OPEN_EXISTING,
|
|
FILE_ATTRIBUTE_HIDDEN,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
#if DBG == 1
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
}
|
|
#endif // DBG == 1
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
//
|
|
// Read & validate file content.
|
|
//
|
|
|
|
HRESULT hr = SADatGetData(hFile, cbData, rgbData);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
//
|
|
// No need to verify the size or the service flags. We've read at
|
|
// least the amount expected, and for this version, only the LSB
|
|
// of the service flags is used. Future versions may wish to do
|
|
// further flag checks.
|
|
//
|
|
|
|
BYTE bPlatform;
|
|
CopyMemory(&bPlatform, rgbData + SA_DAT_PLATFORM_OFFSET,
|
|
sizeof(bPlatform));
|
|
|
|
if (bPlatform != VER_PLATFORM_WIN32_NT &&
|
|
bPlatform != VER_PLATFORM_WIN32_WINDOWS)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
CHECK_HRESULT(hr);
|
|
}
|
|
else
|
|
{
|
|
if (phFile != NULL) // Optional return handle.
|
|
{
|
|
//
|
|
// Reset the file pointer to the beginning for the returned
|
|
// handle.
|
|
//
|
|
|
|
if (SetFilePointer(hFile,
|
|
0,
|
|
NULL,
|
|
FILE_BEGIN) != -1)
|
|
{
|
|
*phFile = hFile;
|
|
hFile = NULL;
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CHECK_HRESULT(hr);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (hFile != NULL) CloseHandle(hFile);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SADatGetData
|
|
//
|
|
// Synopsis: A more refined version of the overloaded function above.
|
|
// Return individual fields instead of raw data.
|
|
//
|
|
// Arguments: [ptszFolderPath] -- SA.DAT path location.
|
|
// [pdwVersion] -- Returned version.
|
|
// [pbPlatform] -- Returned platform id.
|
|
// [prgSvcFlags] -- Returned service flags.
|
|
//
|
|
// Returns: SADatGetData return code.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
SADatGetData(
|
|
LPCTSTR ptszFolderPath,
|
|
DWORD * pdwVersion,
|
|
BYTE * pbPlatform,
|
|
BYTE * prgSvcFlags)
|
|
{
|
|
BYTE rgbData[SA_DAT_VERSION_ONE_SIZE];
|
|
HRESULT hr;
|
|
|
|
hr = SADatGetData(ptszFolderPath, SA_DAT_VERSION_ONE_SIZE, rgbData);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pdwVersion = (DWORD)*rgbData;
|
|
CopyMemory(pbPlatform, rgbData + SA_DAT_PLATFORM_OFFSET,
|
|
sizeof(*pbPlatform));
|
|
CopyMemory(prgSvcFlags, rgbData + SA_DAT_SVCFLAGS_OFFSET,
|
|
sizeof(*prgSvcFlags));
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SADatCreate
|
|
//
|
|
// Synopsis: Create & initialize the binary file, SA.DAT, in the Tasks
|
|
// folder. The platform id is set to indicate which OS we're
|
|
// currently running under; the service flag is set to 1
|
|
// to indicate the service is running.
|
|
//
|
|
// Arguments: [ptszFolderPath] -- Path to the Tasks folder.
|
|
// [fServiceRunning] -- Flag indicating running service.
|
|
//
|
|
// Returns: S_OK
|
|
// E_UNEXPECTED if the amount written isn't what we expected.
|
|
// Create/WriteFile HRESULT status code on failure.
|
|
//
|
|
// Notes: This is to be called by the service only with service start.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
SADatCreate(
|
|
LPCTSTR ptszFolderPath,
|
|
BOOL fServiceRunning)
|
|
{
|
|
BYTE rgbData[SA_DAT_VERSION_ONE_SIZE];
|
|
DWORD dwResult;
|
|
|
|
//
|
|
// Set size.
|
|
//
|
|
|
|
DWORD dwSize = SA_DAT_VERSION_ONE_SIZE;
|
|
CopyMemory(rgbData, &dwSize, sizeof(dwSize));
|
|
|
|
|
|
//
|
|
// Set the platform id.
|
|
//
|
|
|
|
OSVERSIONINFO osverinfo;
|
|
osverinfo.dwOSVersionInfoSize = sizeof(osverinfo);
|
|
|
|
if (!GetVersionEx(&osverinfo))
|
|
{
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
BYTE bPlatform;
|
|
if (osverinfo.dwPlatformId == VER_PLATFORM_WIN32_NT) // NT
|
|
{
|
|
bPlatform = VER_PLATFORM_WIN32_NT;
|
|
}
|
|
else // Assume windows
|
|
{
|
|
bPlatform = VER_PLATFORM_WIN32_WINDOWS;
|
|
}
|
|
|
|
CopyMemory(rgbData + SA_DAT_PLATFORM_OFFSET, &bPlatform,
|
|
sizeof(bPlatform));
|
|
|
|
//
|
|
// Set the service flags to indicate the service is running.
|
|
//
|
|
|
|
BYTE rgfServiceFlags = (fServiceRunning ? SA_DAT_SVCFLAG_SVC_RUNNING : 0);
|
|
|
|
//
|
|
// Determine whether the machine supports wakeup timers.
|
|
//
|
|
if (ResumeTimersSupported())
|
|
{
|
|
rgfServiceFlags |= SA_DAT_SVCFLAG_RESUME_TIMERS;
|
|
}
|
|
|
|
rgbData[SA_DAT_SVCFLAGS_OFFSET] = rgfServiceFlags;
|
|
|
|
//
|
|
// Create the file. Overwrite, if it exists.
|
|
//
|
|
|
|
TCHAR tszSADatPath[MAX_PATH + 1];
|
|
SADatPath(ptszFolderPath, tszSADatPath);
|
|
|
|
//
|
|
// First clear any extraneous attribute bits that were added by
|
|
// somebody else that would cause the CreateFile to fail
|
|
//
|
|
|
|
if (!SetFileAttributes(tszSADatPath, FILE_ATTRIBUTE_HIDDEN))
|
|
{
|
|
|
|
#if DBG == 1
|
|
|
|
//
|
|
// Not a problem if the file doesn't exist
|
|
//
|
|
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND)
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
}
|
|
|
|
#endif // DBG == 1
|
|
|
|
}
|
|
|
|
HANDLE hFile = CreateFile(tszSADatPath,
|
|
GENERIC_READ | GENERIC_WRITE | WRITE_DAC,
|
|
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_HIDDEN,
|
|
NULL);
|
|
|
|
if (hFile == INVALID_HANDLE_VALUE)
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
// Add read ACE for authenticated users
|
|
dwResult = SADatSetSecurity(hFile);
|
|
|
|
if( ERROR_SUCCESS != dwResult )
|
|
{
|
|
CloseHandle(hFile);
|
|
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(dwResult));
|
|
return HRESULT_FROM_WIN32(dwResult);
|
|
}
|
|
|
|
//
|
|
// Write out the contents.
|
|
//
|
|
|
|
HRESULT hr = SADatSetData(hFile, sizeof(rgbData), rgbData);
|
|
|
|
CloseHandle(hFile);
|
|
|
|
return hr;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SADatGetData
|
|
//
|
|
// Synopsis: Nothing SA.DAT-specific here. Just a helper to read a blob
|
|
// of bytes from the file indicated, and ensure we read the
|
|
// amount expected.
|
|
//
|
|
// Arguments: [hFile] -- Destination file.
|
|
// [cbData] -- Amount of data to read.
|
|
// [rgbData] -- Read data.
|
|
//
|
|
// Returns: S_OK
|
|
// E_UNEXPECTED if the amount read isn't what we expected.
|
|
// ReadFile HRESULT status code on failure.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
SADatGetData(
|
|
HANDLE hFile,
|
|
DWORD cbData,
|
|
BYTE rgbData[])
|
|
{
|
|
DWORD cbRead;
|
|
if (!ReadFile(hFile,
|
|
rgbData,
|
|
cbData,
|
|
&cbRead,
|
|
NULL))
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (cbRead != cbData)
|
|
{
|
|
CHECK_HRESULT(E_UNEXPECTED);
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SADatSetData
|
|
//
|
|
// Synopsis: Nothing SA.DAT-specific here. Just a helper to write a blob
|
|
// of bytes to the file indicated, and ensure we wrote the
|
|
// amount expected.
|
|
//
|
|
// Arguments: [hFile] -- Destination file.
|
|
// [cbData] -- Amount of data to write.
|
|
// [rgbData] -- Actual data.
|
|
//
|
|
// Returns: S_OK
|
|
// E_UNEXPECTED if the amount written isn't what we expected.
|
|
// WriteFile HRESULT status code on failure.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
SADatSetData(
|
|
HANDLE hFile,
|
|
DWORD cbData,
|
|
const BYTE rgbData[])
|
|
{
|
|
DWORD cbWritten;
|
|
if (!WriteFile(hFile,
|
|
rgbData,
|
|
cbData,
|
|
&cbWritten,
|
|
NULL))
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
return HRESULT_FROM_WIN32(GetLastError());
|
|
}
|
|
|
|
if (cbWritten != cbData)
|
|
{
|
|
CHECK_HRESULT(E_UNEXPECTED);
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SADatPath
|
|
//
|
|
// Synopsis: Return a concatenation the folder path and "\SA.DAT".
|
|
//
|
|
// Arguments: [ptszFolderPath] -- Folder path.
|
|
// [ptszSADatPath] -- New path.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
SADatPath(
|
|
LPCTSTR ptszFolderPath,
|
|
LPTSTR ptszSADatPath)
|
|
{
|
|
TCHAR tszSADat[] = TEXT("\\SA.DAT");
|
|
|
|
#if (DBG == 1)
|
|
|
|
//
|
|
// Assert that the folder path:
|
|
// is not NULL
|
|
// is not an empty string
|
|
// does not end in a backslash
|
|
//
|
|
|
|
schAssert(ptszFolderPath != NULL);
|
|
schAssert(*ptszFolderPath);
|
|
LPCTSTR ptszLastSlash = _tcsrchr(ptszFolderPath, TEXT('\\'));
|
|
schAssert(!ptszLastSlash || ptszLastSlash[1]);
|
|
|
|
#endif // (DBG == 1)
|
|
|
|
lstrcpy(ptszSADatPath, ptszFolderPath);
|
|
lstrcat(ptszSADatPath, tszSADat);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: ResumeTimersSupported
|
|
//
|
|
// Synopsis: Jumps through hoops to determine whether the machine supports
|
|
// resume timers (aka wakeup timers)
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: TRUE - Resume timers are supported
|
|
// FALSE - Resume timers are not supported
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
BOOL
|
|
ResumeTimersSupported()
|
|
{
|
|
HANDLE hTimer;
|
|
|
|
#ifdef _CHICAGO_
|
|
|
|
PFNCreateWaitableTimerA pfnCreateWaitableTimerA;
|
|
PFNSetWaitableTimer pfnSetWaitableTimer;
|
|
|
|
HMODULE hKernel32Dll = GetModuleHandle("KERNEL32.DLL");
|
|
if (hKernel32Dll == NULL)
|
|
{
|
|
ERR_OUT("Load of kernel32.dll", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
pfnCreateWaitableTimerA = (PFNCreateWaitableTimerA)
|
|
GetProcAddress(hKernel32Dll, "CreateWaitableTimerA");
|
|
pfnSetWaitableTimer = (PFNSetWaitableTimer)
|
|
GetProcAddress(hKernel32Dll, "SetWaitableTimer");
|
|
if (pfnCreateWaitableTimerA == NULL ||
|
|
pfnSetWaitableTimer == NULL)
|
|
{
|
|
ERR_OUT("GetProcAddress in kernel32.dll", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
hTimer = pfnCreateWaitableTimerA(NULL, TRUE, NULL);
|
|
|
|
#else // !_CHICAGO_
|
|
|
|
hTimer = CreateWaitableTimer(NULL, TRUE, NULL);
|
|
|
|
#endif // _CHICAGO_
|
|
|
|
if (hTimer == NULL)
|
|
{
|
|
ERR_OUT("CreateWaitableTimer", GetLastError());
|
|
return FALSE;
|
|
}
|
|
|
|
LARGE_INTEGER li = { 0xFFFFFFFF, 0xFFFFFFFF };
|
|
BOOL fResult = FALSE;
|
|
|
|
#ifdef _CHICAGO_
|
|
if (pfnSetWaitableTimer(hTimer, &li, 0, NULL, 0, TRUE))
|
|
#else // !_CHICAGO_
|
|
if (SetWaitableTimer(hTimer, &li, 0, NULL, 0, TRUE))
|
|
#endif // _CHICAGO_
|
|
{
|
|
//
|
|
// By design, this call to SetWaitableTimer will succeed even on
|
|
// machines that do NOT support resume timers. GetLastError must
|
|
// be used to determine if, indeed, the machine supports resume
|
|
// timers.
|
|
//
|
|
if (GetLastError() == ERROR_NOT_SUPPORTED)
|
|
{
|
|
// This machine does not support resume timers
|
|
DBG_OUT("Machine does not support resume timers");
|
|
}
|
|
else
|
|
{
|
|
DBG_OUT("Machine supports resume timers");
|
|
fResult = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ERR_OUT("SetWaitableTimer", GetLastError());
|
|
}
|
|
|
|
CloseHandle(hTimer);
|
|
|
|
return fResult;
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SADatSetSecurity
|
|
//
|
|
// Synopsis: Add an ACE to the file that allows authenticated users to
|
|
// read the file. We cannot rely on inheriting the necessary
|
|
// permissions of the containing folder.
|
|
//
|
|
// Arguments: [hFile] -- Destination file.
|
|
//
|
|
// Returns: ERROR_SUCCESS upon success
|
|
// Non zero value upon failure
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
DWORD SADatSetSecurity (
|
|
HANDLE hFile // handle to file
|
|
)
|
|
{
|
|
DWORD dwRes = ERROR_SUCCESS;
|
|
PSID pSid = NULL;
|
|
PACL pOldDACL = NULL;
|
|
PACL pNewDACL = NULL;
|
|
PSECURITY_DESCRIPTOR pSD = NULL;
|
|
EXPLICIT_ACCESS ExplicAcc;
|
|
|
|
schAssert(hFile != NULL);
|
|
|
|
// Create the SID for "NTAUTH\Athenticated Users"
|
|
SID_IDENTIFIER_AUTHORITY NtAuth = SECURITY_NT_AUTHORITY;
|
|
|
|
if ( !AllocateAndInitializeSid(
|
|
&NtAuth,
|
|
1,
|
|
SECURITY_AUTHENTICATED_USER_RID,
|
|
0, 0, 0, 0, 0, 0, 0,
|
|
&pSid
|
|
)
|
|
)
|
|
{
|
|
dwRes = GetLastError();
|
|
goto Cleanup;
|
|
}
|
|
|
|
|
|
// Get a pointer to the existing DACL.
|
|
dwRes = GetSecurityInfo(
|
|
hFile,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
NULL,
|
|
&pOldDACL,
|
|
NULL,
|
|
&pSD
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwRes)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Initialize an EXPLICIT_ACCESS structure for the new ACE.
|
|
|
|
ZeroMemory(&ExplicAcc, sizeof(EXPLICIT_ACCESS));
|
|
|
|
ExplicAcc.grfAccessPermissions = GENERIC_READ;
|
|
ExplicAcc.grfAccessMode = GRANT_ACCESS;
|
|
ExplicAcc.grfInheritance = NO_INHERITANCE;
|
|
ExplicAcc.Trustee.TrusteeForm = TRUSTEE_IS_SID;
|
|
ExplicAcc.Trustee.ptstrName = (LPTSTR)pSid;
|
|
|
|
// Create a new ACL that merges the new ACE
|
|
// into the existing DACL.
|
|
|
|
dwRes = SetEntriesInAcl(1, &ExplicAcc, pOldDACL, &pNewDACL);
|
|
if (ERROR_SUCCESS != dwRes)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
// Attach the new ACL as the file's DACL.
|
|
|
|
dwRes = SetSecurityInfo(
|
|
hFile,
|
|
SE_FILE_OBJECT,
|
|
DACL_SECURITY_INFORMATION,
|
|
NULL,
|
|
NULL,
|
|
pNewDACL,
|
|
NULL
|
|
);
|
|
|
|
if (ERROR_SUCCESS != dwRes)
|
|
{
|
|
goto Cleanup;
|
|
}
|
|
|
|
Cleanup:
|
|
|
|
if(pSD != NULL)
|
|
{
|
|
LocalFree((HLOCAL) pSD);
|
|
}
|
|
|
|
if(pNewDACL != NULL)
|
|
{
|
|
LocalFree((HLOCAL) pNewDACL);
|
|
}
|
|
|
|
if( pSid != NULL )
|
|
{
|
|
FreeSid(pSid);
|
|
}
|
|
|
|
return dwRes;
|
|
}
|
|
|