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.
686 lines
18 KiB
686 lines
18 KiB
//+---------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1992 - 1996.
|
|
//
|
|
// File: secmisc.cxx
|
|
//
|
|
// Contents: Code to retrieve security-related information from the job
|
|
// object. Function names partially describe the intended
|
|
// function - we don't want to give too much away.
|
|
//
|
|
// Classes: None.
|
|
//
|
|
// Functions: CloseFile
|
|
// GetFileInformation
|
|
//
|
|
// History: 15-May-96 MarkBl Created
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
|
|
#include "..\pch\headers.hxx"
|
|
#pragma hdrstop
|
|
#include <ntsecapi.h>
|
|
#include <mstask.h>
|
|
#include <msterr.h>
|
|
#include "debug.hxx"
|
|
#include "lsa.hxx"
|
|
#include "globals.hxx"
|
|
#include "misc.hxx"
|
|
|
|
BOOL WaitForMUP (DWORD dwMaxWait);
|
|
BOOL WaitForServiceToStart (LPTSTR lpServiceName, DWORD dwMaxWait);
|
|
|
|
//
|
|
// Defined in globals.cxx
|
|
//
|
|
extern CStaticCritSec gcsSSCritSection;
|
|
|
|
//
|
|
// Defined in security.cxx.
|
|
//
|
|
extern DWORD gdwKeyElement;
|
|
extern POLICY_ACCOUNT_DOMAIN_INFO * gpDomainInfo;
|
|
extern WCHAR gwszComputerName[MAX_COMPUTERNAME_LENGTH + 2];
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: CloseFile
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [hFile] --
|
|
// [ccApplication] --
|
|
// [wszApplication] --
|
|
// [hrPrevious] --
|
|
//
|
|
// Returns: S_OK
|
|
// SCHED_E_INVALID_TASK
|
|
// E_UNEXPECTED
|
|
// HRESULT argument, if it is an error.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
CloseFile(
|
|
HANDLE hFile,
|
|
WORD ccApplication,
|
|
WCHAR wszApplication[],
|
|
HRESULT hrPrevious)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
DWORD dwBytesRead;
|
|
WCHAR * pwsz;
|
|
WORD wAppOffset;
|
|
WORD cch;
|
|
|
|
//
|
|
// If the previous operation failed, skip the application read.
|
|
//
|
|
|
|
if (FAILED(hrPrevious))
|
|
{
|
|
hr = hrPrevious;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Read the offset to the application name.
|
|
//
|
|
|
|
if (!ReadFile(hFile, &wAppOffset, sizeof(wAppOffset), &dwBytesRead, NULL))
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
hr = SCHED_E_INVALID_TASK;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Move to read the application name.
|
|
//
|
|
|
|
if (SetFilePointer(hFile, wAppOffset, NULL, FILE_BEGIN) != -1)
|
|
{
|
|
//
|
|
// Read the application size, allocate sufficient buffer space
|
|
// and read the application string.
|
|
//
|
|
|
|
if (!ReadFile(hFile, &cch, sizeof(cch), &dwBytesRead, NULL) ||
|
|
dwBytesRead != sizeof(cch))
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
hr = SCHED_E_INVALID_TASK;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (!cch)
|
|
{
|
|
wszApplication[0] = L'\0';
|
|
}
|
|
else if (cch > ccApplication)
|
|
{
|
|
hr = E_UNEXPECTED;
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
else
|
|
{
|
|
if (!ReadFile(hFile,
|
|
wszApplication,
|
|
cch * sizeof(WCHAR),
|
|
&dwBytesRead,
|
|
NULL))
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
hr = SCHED_E_INVALID_TASK;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (dwBytesRead != (cch * sizeof(WCHAR)))
|
|
{
|
|
hr = SCHED_E_INVALID_TASK;
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (wszApplication[cch - 1] != L'\0')
|
|
{
|
|
hr = SCHED_E_INVALID_TASK;
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
hr = SCHED_E_INVALID_TASK;
|
|
}
|
|
|
|
ErrorExit:
|
|
if (hFile != NULL) CloseHandle(hFile);
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: GetFileInformation
|
|
//
|
|
// Synopsis:
|
|
//
|
|
// Arguments: [pwszFileName] --
|
|
// [hFile] --
|
|
// [pcbOwnerSid] --
|
|
// [ppOwnerSid] --
|
|
// [ppOwnerSecDescr] --
|
|
// [ccOwnerName] --
|
|
// [ccOwnerDomain] --
|
|
// [ccApplication] --
|
|
// [wszOwnerName] --
|
|
// [wszOwnerDomain] --
|
|
// [wszApplication] --
|
|
// [pftCreationTime] --
|
|
// [pdwVolumeSerialNo] --
|
|
//
|
|
// Returns: HRESULT
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
HRESULT
|
|
GetFileInformation(
|
|
LPCWSTR pwszFileName,
|
|
DWORD * pcbOwnerSid,
|
|
PSID * ppOwnerSid,
|
|
PSECURITY_DESCRIPTOR * ppOwnerSecDescr,
|
|
UUID * pJobID,
|
|
DWORD ccOwnerName,
|
|
DWORD ccOwnerDomain,
|
|
DWORD ccApplication,
|
|
WCHAR wszOwnerName[],
|
|
WCHAR wszOwnerDomain[],
|
|
WCHAR wszApplication[],
|
|
FILETIME * pftCreationTime,
|
|
DWORD * pdwVolumeSerialNo)
|
|
{
|
|
BY_HANDLE_FILE_INFORMATION hinfo;
|
|
HANDLE hFile;
|
|
SECURITY_DESCRIPTOR * pOwnerSecDescr = NULL;
|
|
PSID pOwnerSid = NULL;
|
|
DWORD cbOwnerSid = 0;
|
|
DWORD cbSizeNeeded;
|
|
BOOL fRet, fOwnerDefaulted;
|
|
static s_bWaitForWorkStation = TRUE;
|
|
|
|
HRESULT hr = OpenFileWithRetry(pwszFileName, GENERIC_READ, FILE_SHARE_READ, &hFile);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
return hr;
|
|
}
|
|
else
|
|
{
|
|
//
|
|
// Read the UUID from the job indicated.
|
|
//
|
|
|
|
BYTE pbBuffer[sizeof(DWORD) + sizeof(UUID)];
|
|
DWORD dwBytesRead;
|
|
|
|
if (!ReadFile(hFile, pbBuffer, sizeof(pbBuffer), &dwBytesRead, NULL))
|
|
{
|
|
CHECK_HRESULT(HRESULT_FROM_WIN32(GetLastError()));
|
|
CloseHandle(hFile);
|
|
return SCHED_E_INVALID_TASK;
|
|
}
|
|
|
|
if (dwBytesRead != sizeof(pbBuffer))
|
|
{
|
|
CHECK_HRESULT(SCHED_E_INVALID_TASK);
|
|
CloseHandle(hFile);
|
|
return SCHED_E_INVALID_TASK;
|
|
}
|
|
|
|
CopyMemory(pJobID, pbBuffer + sizeof(DWORD), sizeof(*pJobID));
|
|
}
|
|
|
|
//
|
|
// Retrieve file creation time and the volume serial number.
|
|
//
|
|
|
|
if (!GetFileInformationByHandle(hFile, &hinfo))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Retrieve the file owner. Call GetFileSecurity twice - first to get
|
|
// the buffer size, then the actual information retrieval.
|
|
//
|
|
|
|
if (GetFileSecurity(pwszFileName,
|
|
OWNER_SECURITY_INFORMATION,
|
|
NULL,
|
|
0,
|
|
&cbSizeNeeded))
|
|
{
|
|
//
|
|
// Didn't expect this to succeed!
|
|
//
|
|
|
|
hr = E_UNEXPECTED;
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if ((GetLastError() == ERROR_INSUFFICIENT_BUFFER) && (cbSizeNeeded > 0))
|
|
{
|
|
//
|
|
// Allocate the buffer space necessary and retrieve the info.
|
|
//
|
|
|
|
pOwnerSecDescr = (SECURITY_DESCRIPTOR *)new BYTE[cbSizeNeeded];
|
|
|
|
if (pOwnerSecDescr == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
if (!GetFileSecurity(pwszFileName,
|
|
OWNER_SECURITY_INFORMATION,
|
|
pOwnerSecDescr,
|
|
cbSizeNeeded,
|
|
&cbSizeNeeded))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CHECK_HRESULT(hr);
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Retrieve & validate the owner sid.
|
|
//
|
|
// NB : After this, pOwnerSid will point into the security descriptor,
|
|
// pOwnerSecDescr; hence, the descriptor must exist for the
|
|
// lifetime of pOwnerSid.
|
|
//
|
|
|
|
fRet = GetSecurityDescriptorOwner(pOwnerSecDescr,
|
|
&pOwnerSid,
|
|
&fOwnerDefaulted);
|
|
|
|
if (fRet)
|
|
{
|
|
if (fRet = IsValidSid(pOwnerSid))
|
|
{
|
|
cbOwnerSid = GetLengthSid(pOwnerSid);
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CHECK_HRESULT(hr);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CHECK_HRESULT(hr);
|
|
}
|
|
|
|
if (!fRet)
|
|
{
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Retrieve the account name & domain name from the file owner sid.
|
|
//
|
|
|
|
SID_NAME_USE snu;
|
|
BOOL bDoLookupAgain;
|
|
|
|
//
|
|
//Startup jobs for domain users will fail if workstation is not initialized
|
|
//If LookupAccountSid fails and we are booting then force the service to
|
|
// wait until workstation is fully initialized and then try again
|
|
//
|
|
do
|
|
{
|
|
bDoLookupAgain = FALSE;
|
|
|
|
schDebugOut((DEB_TRACE, "GetFileInformation: Calling LookupAccountSid\n"));
|
|
|
|
if (!LookupAccountSid(NULL,
|
|
pOwnerSid,
|
|
wszOwnerName,
|
|
&ccOwnerName,
|
|
wszOwnerDomain,
|
|
&ccOwnerDomain,
|
|
&snu))
|
|
{
|
|
hr = HRESULT_FROM_WIN32(GetLastError());
|
|
CHECK_HRESULT(hr);
|
|
|
|
if( s_bWaitForWorkStation )
|
|
{
|
|
schDebugOut((DEB_TRACE, "GetFileInformation: Delaying LookupAccountSid for boot\n"));
|
|
|
|
WaitForMUP(120000);
|
|
WaitForServiceToStart(L"workstation",120000);
|
|
WaitForServiceToStart(L"netlogon",120000);
|
|
|
|
bDoLookupAgain = TRUE;
|
|
s_bWaitForWorkStation = FALSE;
|
|
|
|
//Reset since CloseFile returns this value if failure
|
|
hr = ERROR_SUCCESS;
|
|
|
|
} else {
|
|
goto ErrorExit;
|
|
}
|
|
}
|
|
|
|
} while(bDoLookupAgain);
|
|
|
|
|
|
ErrorExit:
|
|
//
|
|
// Being a little sneaky here and reading the job application whilst
|
|
// closing the file handle. That is, if all succeeded above.
|
|
//
|
|
|
|
hr = CloseFile(hFile, (WORD)ccApplication, wszApplication, hr);
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
*pftCreationTime = hinfo.ftCreationTime;
|
|
*pdwVolumeSerialNo = hinfo.dwVolumeSerialNumber;
|
|
*pcbOwnerSid = cbOwnerSid;
|
|
*ppOwnerSid = pOwnerSid;
|
|
*ppOwnerSecDescr = pOwnerSecDescr;
|
|
|
|
//
|
|
// If not already done so, set the 'mystery' global DWORD.
|
|
// This DWORD, in addition to other data, is used to generate
|
|
// the encryption key for the SAC/SAI database.
|
|
//
|
|
// The reason why this is done here is to spread the key generation
|
|
// code around a bit.
|
|
//
|
|
|
|
if (!gdwKeyElement)
|
|
{
|
|
SetMysteryDWORDValue();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
delete pOwnerSecDescr;
|
|
}
|
|
|
|
return(hr);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Function: SetMysteryDWORDValue
|
|
//
|
|
// Synopsis: Initialize a global DWORD to be used as a data element in
|
|
// generation of the SAC/SAI database encryption key.
|
|
//
|
|
// Arguments: None.
|
|
//
|
|
// Returns: None.
|
|
//
|
|
// Notes: None.
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
void
|
|
SetMysteryDWORDValue(void)
|
|
{
|
|
//
|
|
// Set the global mystery dword to the first dword of the job or queue
|
|
// class ids, depending on the value of the machine sid.
|
|
//
|
|
|
|
EnterCriticalSection(&gcsSSCritSection);
|
|
|
|
if (!gdwKeyElement)
|
|
{
|
|
DWORD dwTmp;
|
|
|
|
//
|
|
// The last (3) subauthorities of the machine SID are unique per
|
|
// machine. Test LSB of the 2nd from the last subauthority.
|
|
//
|
|
|
|
PUCHAR pSidSubAuthorityCount = GetSidSubAuthorityCount(
|
|
gpDomainInfo->DomainSid);
|
|
schAssert(pSidSubAuthorityCount != NULL);
|
|
DWORD nSubAuthority = (pSidSubAuthorityCount != NULL ?
|
|
max(*pSidSubAuthorityCount, 2) : 2);
|
|
DWORD * pSubAuthority = GetSidSubAuthority(
|
|
gpDomainInfo->DomainSid,
|
|
nSubAuthority - 2);
|
|
schAssert(pSubAuthority != NULL);
|
|
|
|
if (pSubAuthority != NULL && *pSubAuthority & 0x00000001)
|
|
{
|
|
dwTmp = 0x255b3f60; // CLSID_CQueue.Data1
|
|
}
|
|
else
|
|
{
|
|
dwTmp = CLSID_CTask.Data1;
|
|
}
|
|
|
|
//
|
|
// Apply a mask to the mystery value to further disguise it.
|
|
//
|
|
|
|
if (gwszComputerName[0] & 0x0100)
|
|
{
|
|
dwTmp &= 0xC03F71C3;
|
|
}
|
|
else
|
|
{
|
|
dwTmp &= 0xE3507233;
|
|
}
|
|
|
|
gdwKeyElement = dwTmp;
|
|
}
|
|
|
|
LeaveCriticalSection(&gcsSSCritSection);
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// WaitForServiceToStart()
|
|
//
|
|
// Purpose: Waits for the specified service to start
|
|
//
|
|
// Parameters: dwMaxWait - Max wait time
|
|
//
|
|
//
|
|
// Return: TRUE if the network is started
|
|
// FALSE if not
|
|
//
|
|
//*************************************************************
|
|
BOOL WaitForServiceToStart (LPTSTR lpServiceName, DWORD dwMaxWait)
|
|
{
|
|
BOOL bStarted = FALSE;
|
|
DWORD dwSize = 512;
|
|
SC_HANDLE hScManager = NULL;
|
|
SC_HANDLE hService = NULL;
|
|
LPQUERY_SERVICE_CONFIG lpServiceConfig = NULL;
|
|
DWORD dwPoleWait = 1000;
|
|
DWORD StartTickCount;
|
|
SERVICE_STATUS ServiceStatus;
|
|
|
|
//
|
|
// OpenSCManager and the rpcss service
|
|
//
|
|
hScManager = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
|
|
if (!hScManager) {
|
|
goto Exit;
|
|
}
|
|
|
|
hService = OpenService(hScManager, lpServiceName,
|
|
SERVICE_QUERY_CONFIG | SERVICE_QUERY_STATUS);
|
|
if (!hService) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Query if the service is going to start
|
|
//
|
|
lpServiceConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc (LPTR, dwSize);
|
|
if (!lpServiceConfig) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (!QueryServiceConfig (hService, lpServiceConfig, dwSize, &dwSize)) {
|
|
|
|
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) {
|
|
goto Exit;
|
|
}
|
|
|
|
LocalFree (lpServiceConfig);
|
|
|
|
lpServiceConfig = (LPQUERY_SERVICE_CONFIG)LocalAlloc (LPTR, dwSize);
|
|
|
|
if (!lpServiceConfig) {
|
|
goto Exit;
|
|
}
|
|
|
|
if (!QueryServiceConfig (hService, lpServiceConfig, dwSize, &dwSize)) {
|
|
goto Exit;
|
|
}
|
|
}
|
|
|
|
if (lpServiceConfig->dwStartType != SERVICE_AUTO_START) {
|
|
goto Exit;
|
|
}
|
|
|
|
//
|
|
// Loop until the service starts or we think it never will start
|
|
// or we've exceeded our maximum time delay.
|
|
//
|
|
|
|
StartTickCount = GetTickCount();
|
|
|
|
while (!bStarted) {
|
|
|
|
if ((GetTickCount() - StartTickCount) > dwMaxWait) {
|
|
break;
|
|
}
|
|
|
|
if (!QueryServiceStatus(hService, &ServiceStatus )) {
|
|
break;
|
|
}
|
|
|
|
if (ServiceStatus.dwCurrentState == SERVICE_STOPPED) {
|
|
if (ServiceStatus.dwWin32ExitCode == ERROR_SERVICE_NEVER_STARTED) {
|
|
Sleep(dwPoleWait);
|
|
} else {
|
|
break;
|
|
}
|
|
} else if ( (ServiceStatus.dwCurrentState == SERVICE_RUNNING) ||
|
|
(ServiceStatus.dwCurrentState == SERVICE_CONTINUE_PENDING) ||
|
|
(ServiceStatus.dwCurrentState == SERVICE_PAUSE_PENDING) ||
|
|
(ServiceStatus.dwCurrentState == SERVICE_PAUSED) ) {
|
|
|
|
bStarted = TRUE;
|
|
|
|
} else if (ServiceStatus.dwCurrentState == SERVICE_START_PENDING) {
|
|
Sleep(dwPoleWait);
|
|
} else {
|
|
Sleep(dwPoleWait);
|
|
}
|
|
}
|
|
|
|
|
|
Exit:
|
|
|
|
if (lpServiceConfig) {
|
|
LocalFree (lpServiceConfig);
|
|
}
|
|
|
|
if (hService) {
|
|
CloseServiceHandle(hService);
|
|
}
|
|
|
|
if (hScManager) {
|
|
CloseServiceHandle(hScManager);
|
|
}
|
|
|
|
return bStarted;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// WaitForMUP()
|
|
//
|
|
// Purpose: Waits for the MUP to finish initializing
|
|
//
|
|
// Parameters: dwMaxWait - Max wait time
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
//*************************************************************
|
|
BOOL WaitForMUP (DWORD dwMaxWait)
|
|
{
|
|
HANDLE hEvent = NULL;
|
|
BOOL bResult;
|
|
INT i = 0;
|
|
|
|
//
|
|
// Try to open the event
|
|
//
|
|
do {
|
|
hEvent = OpenEvent (SYNCHRONIZE, FALSE,
|
|
TEXT("wkssvc: MUP finished initializing event"));
|
|
if (hEvent) {
|
|
break;
|
|
}
|
|
|
|
if (GetLastError() != ERROR_FILE_NOT_FOUND) {
|
|
break;
|
|
}
|
|
|
|
Sleep(500);
|
|
i++;
|
|
} while (i < 20);
|
|
|
|
if (!hEvent) {
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Wait for the event to be signalled
|
|
//
|
|
bResult = (WaitForSingleObject (hEvent, dwMaxWait) == WAIT_OBJECT_0);
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
CloseHandle (hEvent);
|
|
|
|
return bResult;
|
|
}
|
|
|