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.
 
 
 
 
 
 

523 lines
20 KiB

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 2002.
//
// File: conversion.cxx
//
// Contents: data conversion functions
//
// History: 11-Nov-02 Created
//
//----------------------------------------------------------------------------
#include "..\pch\headers.hxx"
#include "common.hxx"
#include "security.hxx"
#include "statsync.hxx"
extern CStaticCritSec gcsSSCritSection;
#define SCH_DATA_VERSION L"DataVersion"
//+---------------------------------------------------------------------------
//
// Function: CheckDataVersion
//
// Synopsis: Check for registry key that will indicate
// the version of the scheduler data.
//
// Arguments: None
//
// Returns: DWORD - indicates data version
//
//----------------------------------------------------------------------------
DWORD CheckDataVersion(void)
{
DWORD dwDataVersion = 0;
HKEY hSchedKey = NULL;
long lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SCH_AGENT_KEY,
0,
KEY_QUERY_VALUE | KEY_SET_VALUE,
&hSchedKey);
if (lErr == ERROR_SUCCESS)
{
DWORD dwType;
DWORD cb = sizeof(dwDataVersion);
lErr = RegQueryValueEx(hSchedKey,
SCH_DATA_VERSION,
NULL,
&dwType,
(LPBYTE) &dwDataVersion,
&cb);
if (lErr != ERROR_SUCCESS || dwType != REG_DWORD)
{
dwDataVersion = 0;
}
}
if (hSchedKey != NULL)
{
RegCloseKey(hSchedKey);
}
return dwDataVersion;
}
//+---------------------------------------------------------------------------
//
// Function: RecordDataVersion
//
// Synopsis: Update registry key to record current data version
//
// Arguments: dwDataVersion -- version value to store
//
// Returns: HRESULT
//
//----------------------------------------------------------------------------
HRESULT RecordDataVersion(DWORD dwDataVersion)
{
HRESULT hr = S_OK;
HKEY hSchedKey = NULL;
long lErr = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
SCH_AGENT_KEY,
0,
KEY_QUERY_VALUE | KEY_SET_VALUE,
&hSchedKey);
if (lErr == ERROR_SUCCESS)
{
DWORD cb = sizeof(dwDataVersion);
RegSetValueEx(hSchedKey,
SCH_DATA_VERSION,
NULL,
REG_DWORD,
(CONST BYTE *)&dwDataVersion,
cb);
if (lErr != ERROR_SUCCESS)
{
schDebugOut((DEB_ERROR, "RegSetValueEx of RecordDataVersion value failed %ld\n", lErr));
hr = E_FAIL;
}
}
if (hSchedKey != NULL)
{
RegCloseKey(hSchedKey);
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: ConvertJobIdentityHashMethod
//
// Synopsis: Create new identity entries for each job file in the SAI
// utilizing the new hash method.
//
// Arguments: None
//
// Returns: HRESULT
//
//----------------------------------------------------------------------------
HRESULT ConvertJobIdentityHashMethod(void)
{
HCRYPTPROV hCSP = NULL;
WCHAR* pwszTasksFolder = NULL;
HANDLE hFileEnum = INVALID_HANDLE_VALUE;
DWORD cbSAI = 0;
DWORD cbSAC = 0;
BYTE* pbSAI = NULL;
BYTE* pbSAC = NULL;
BYTE* pbIdentitySet = NULL;
//
// Obtain a provider handle to the CSP (for use with Crypto API).
//
HRESULT hr = GetCSPHandle(&hCSP);
if (FAILED(hr))
{
return(hr);
}
//
// Guard SA security database access.
//
EnterCriticalSection(&gcsSSCritSection);
//
// Read the security database so we can do the lookups
//
hr = ReadSecurityDBase(&cbSAI, &pbSAI, &cbSAC, &pbSAC);
if (SUCCEEDED(hr))
{
if (cbSAI <= SAI_HEADER_SIZE)
{
//
// Database empty, nothing to do.
//
}
else
{
//
// Enumerate job objects in the task's folder directory.
//
hr = GetTasksFolder(&pwszTasksFolder);
if (SUCCEEDED(hr))
{
WCHAR wszSearchPath[MAX_PATH + 1];
hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
if (SUCCEEDED(hr))
{
hr = StringCchCat(wszSearchPath, MAX_PATH + 1, EXTENSION_WILDCARD TSZ_JOB);
if (SUCCEEDED(hr))
{
WIN32_FIND_DATA fd;
ZeroMemory(&fd, sizeof(fd));
DWORD dwRet = 0;
if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
{
//
// Either no jobs (this is OK), or an error occurred.
//
dwRet = GetLastError();
if (dwRet != ERROR_FILE_NOT_FOUND)
{
hr = _HRESULT_FROM_WIN32(dwRet);
}
}
else
{
//
// Must concatenate the filename returned from the enumeration onto the folder path
// before computing the hash. Prepare for doing that repeatedly by taking the path,
// adding a slash, and remembering the next character position.
//
hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
if (SUCCEEDED(hr))
{
DWORD iConcatenation = lstrlenW(pwszTasksFolder);
wszSearchPath[iConcatenation++] = L'\\';
//
// Process each found file
//
BYTE rgbIdentity[HASH_DATA_SIZE];
DWORD dwCredentialIndex = 0;
BOOL bIsPasswordNull = FALSE;
BYTE* pbFoundIdentity = NULL;
DWORD cbFileName = 0;
while (dwRet != ERROR_NO_MORE_FILES)
{
//
// Truncate the existing name after the folder path,
// then concatenate the new filename onto it.
//
wszSearchPath[iConcatenation] = L'\0';
if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
{
//
// Hash the job into a unique identity using the old method
//
if (SUCCEEDED(HashJobIdentity(hCSP, wszSearchPath, rgbIdentity, 0)))
{
//
// Find the identity in the SAI for this job
//
hr = SAIFindIdentity(rgbIdentity,
cbSAI,
pbSAI,
&dwCredentialIndex,
&bIsPasswordNull,
&pbFoundIdentity,
NULL,
NULL);
//
// S_OK means the identity was found; S_FALSE means it wasn't
// Other codes are errors. We process only the S_OKs, of course.
//
if (S_OK == hr)
{
//
// Hash the job into a unique identity using the new method
//
if (SUCCEEDED(HashJobIdentity(hCSP, wszSearchPath, rgbIdentity)))
{
//
// Store a NULL password by flipping the last bit of the hash data.
//
if (bIsPasswordNull)
{
LAST_HASH_BYTE(rgbIdentity) ^= 1;
}
//
// Update the old identity in place with the new one
//
hr = SAIUpdateIdentity(rgbIdentity,
pbFoundIdentity,
cbSAI,
pbSAI);
if (FAILED(hr))
{
break;
}
}
else
{
//
// we failed to produce a new hash for this file;
// while unexpected, it shouldn't be fatal to the conversion process;
// we just won't be able to convert this job; go on to the next job
//
}
}
else
{
//
// we failed to find the original job identity;
// perhaps its credentials have already been lost;
// there's nothing we can do; go on to the next job
//
hr = S_OK;
}
}
}
if (!FindNextFile(hFileEnum, &fd))
{
dwRet = GetLastError();
if (dwRet != ERROR_NO_MORE_FILES)
{
hr = _HRESULT_FROM_WIN32(dwRet);
}
break;
}
}
if (SUCCEEDED(hr))
{
//
// Update security database with the new identities
//
hr = WriteSecurityDBase(cbSAI, pbSAI, cbSAC, pbSAC);
}
}
}
}
}
}
}
}
if (hFileEnum != INVALID_HANDLE_VALUE)
{
FindClose(hFileEnum);
}
if (pwszTasksFolder)
{
delete [] pwszTasksFolder;
}
if (pbSAI)
{
LocalFree(pbSAI);
}
if (pbSAC)
{
LocalFree(pbSAC);
}
if (hCSP)
{
CloseCSPHandle(hCSP);
}
LeaveCriticalSection(&gcsSSCritSection);
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: ConvertNetScheduleJobSignatures
//
// Synopsis: Netschedule jobs (AT jobs) are signed internally with a hash so
// that the service can validate their authenticity at run time.
// Since we are changing how the hash is created, in order to be
// runnable again, the AT jobs must be signed again with the new hash.
// Prior to re-signing, we will verify the jobs using the old hash.
//
// Arguments: None
//
// Returns: HRESULT
//
//----------------------------------------------------------------------------
HRESULT ConvertNetScheduleJobSignatures(void)
{
HRESULT hr = S_OK;
HANDLE hFileEnum = INVALID_HANDLE_VALUE;
WCHAR wszSearchPath[MAX_PATH + 1];
hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
if (SUCCEEDED(hr))
{
hr = StringCchCat(wszSearchPath, MAX_PATH + 1, L"\\" TSZ_AT_JOB_PREFIX L"*" TSZ_DOTJOB);
if (SUCCEEDED(hr))
{
WIN32_FIND_DATA fd;
ZeroMemory(&fd, sizeof(fd));
DWORD dwRet = 0;
if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
{
//
// Either no jobs (this is OK), or an error occurred.
//
dwRet = GetLastError();
if (dwRet != ERROR_FILE_NOT_FOUND)
{
hr = _HRESULT_FROM_WIN32(dwRet);
}
}
else
{
//
// Must concatenate the filename returned from the enumeration onto the folder path
// before loading the job. Prepare for doing that repeatedly by taking the path,
// adding a slash, and remembering the next character position.
//
hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
if (SUCCEEDED(hr))
{
DWORD iConcatenation = lstrlenW(g_TasksFolderInfo.ptszPath);
wszSearchPath[iConcatenation++] = L'\\';
//
// Process each found file
//
while (dwRet != ERROR_NO_MORE_FILES)
{
//
// Truncate the existing name after the folder path,
// then concatenate the new filename onto it.
//
wszSearchPath[iConcatenation] = L'\0';
if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
{
//
// Load, verify, sign, save, and release the job
//
CJob* pJob = CJob::Create();
if (!pJob)
{
hr = E_OUTOFMEMORY;
break;
}
if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
{
//
// verify using the old hash
//
if (pJob->VerifySignature(0))
{
//
// sign using the new hash
//
if (SUCCEEDED(pJob->Sign()))
{
pJob->SaveWithRetry(pJob->GetFileName(),
FALSE,
SAVEP_VARIABLE_LENGTH_DATA |
SAVEP_PRESERVE_NET_SCHEDULE);
}
}
}
pJob->Release();
}
if (!FindNextFile(hFileEnum, &fd))
{
dwRet = GetLastError();
if (dwRet != ERROR_NO_MORE_FILES)
{
hr = _HRESULT_FROM_WIN32(dwRet);
}
break;
}
}
}
}
if (hFileEnum != INVALID_HANDLE_VALUE)
{
FindClose(hFileEnum);
}
}
}
return hr;
}
//+---------------------------------------------------------------------------
//
// Function: PerformDataConversions
//
// Synopsis: Perform any data conversions that may be required as a result
// of changes to task scheduler. This function is intended to
// allow potential future conversions to be added easily, and to
// allow conversions of data that may have already had earlier
// conversions applied.
//
// Arguments: None
//
// Returns: HRESULT
//
//----------------------------------------------------------------------------
HRESULT PerformDataConversions(void)
{
HRESULT hr = S_OK;
switch (CheckDataVersion())
{
case 0:
hr = ConvertJobIdentityHashMethod();
if (SUCCEEDED(hr))
{
hr = RecordDataVersion(1);
}
// fall through to handle next higher conversion
case 1:
if (SUCCEEDED(hr))
{
hr = ConvertNetScheduleJobSignatures();
if (SUCCEEDED(hr))
{
hr = RecordDataVersion(2);
}
}
// fall through to handle next higher conversion
case 2:
// nothing to do for now
// version 2 is currently the highest and already has all changes applied
break;
default:
// if the value is not listed above, then someone has altered the registry;
// the version value is meaningless to us, so it is safest to not do anything,
// but reflect the unusual situation in the error code in case we want to log it
hr = S_FALSE;
break;
}
return hr;
}