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