#include "..\pch\headers.hxx"
#include "security.hxx"
#include "sysprep.hxx"

static WCHAR          gwszSysprepKey[]      = L"TSSK"; 
static WCHAR          gwszSysprepIdentity[] = L"TSSI"; 

extern  SERVICE_STATUS  g_SvcStatus;
#define SERVICE_RUNNING 0x00000004

HRESULT GetUniqueSPSName(ITaskScheduler* pITaskScheduler, ITask** ppITask, WCHAR* pwszTaskName)
{
    HRESULT hr = E_FAIL; 

    for (int i = 0; (i < 16) && FAILED(hr); i++)
    {
        if (FAILED(StringCchPrintf(pwszTaskName, 20, L"$~$Sys%X$", i)))
            break;

        hr = pITaskScheduler->NewWorkItem(pwszTaskName,           
             CLSID_CTask,            
             IID_ITask,              
             (IUnknown**)ppITask); 
    }

    return hr;
}

HRESULT PrepSysPrepTask(ITask** ppITaskToRun, WCHAR* pwszTaskName)
{
    HRESULT hr = E_FAIL;
    WCHAR applicationName[MAX_PATH +1];
    WCHAR argument[MAX_PATH +20];
    DWORD expandedSize;

    expandedSize = ExpandEnvironmentStrings(L"\"%SystemRoot%\\System32\\rundll32.exe\"", applicationName, MAX_PATH +1);
    if ((0 == expandedSize) || expandedSize > (MAX_PATH +1))
        return HRESULT_FROM_WIN32(GetLastError());

    expandedSize = ExpandEnvironmentStrings(L"\"%SystemRoot%\\System32\\SchedSvc.dll\",SysPrepCallback", argument, MAX_PATH +20);
    if ((0 == expandedSize) || expandedSize > (MAX_PATH +1))
        return HRESULT_FROM_WIN32(GetLastError());

    TASK_TRIGGER tigger;
    tigger.wStartHour = 10;            
    tigger.wStartMinute = 20;          
    tigger.wBeginYear = 1957;            
    tigger.wBeginMonth = 6;           
    tigger.wBeginDay = 14;             

    tigger.wEndYear = 2001;              
    tigger.wEndMonth = 10;              
    tigger.wEndDay = 8 ;               
    tigger.MinutesDuration = 0;      
    tigger.MinutesInterval = 0;      
    tigger.rgFlags = 0;              
    tigger.TriggerType = TASK_TIME_TRIGGER_ONCE;
    tigger.Reserved2 = 0;
    tigger.wRandomMinutesInterval = 0;
    tigger.cbTriggerSize = sizeof(TASK_TRIGGER);
    tigger.Reserved1 = 0;

    ITaskScheduler *pITaskScheduler = NULL;
    ITask *pITask = NULL;
    IPersistFile *pIPersistFile = NULL;
    ITaskTrigger* pTrigger = NULL;    
    WORD idontcare;

    hr = CoCreateInstance( CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void**)&pITaskScheduler);
    if (SUCCEEDED(hr) && 
        SUCCEEDED(hr = GetUniqueSPSName(pITaskScheduler, &pITask, pwszTaskName)) &&
        SUCCEEDED(hr = pITask->SetApplicationName(applicationName)) &&
        SUCCEEDED(hr = pITask->SetParameters(argument)) &&
        SUCCEEDED(hr = pITask->SetFlags(TASK_FLAG_DELETE_WHEN_DONE | TASK_FLAG_DISABLED))     &&
        SUCCEEDED(hr = pITask->SetAccountInformation(L"", NULL))     &&
        SUCCEEDED(hr = pITask->CreateTrigger(&idontcare, &pTrigger))  &&
        SUCCEEDED(hr = pTrigger->SetTrigger(&tigger))                  &&
        SUCCEEDED(hr = pITask->QueryInterface(IID_IPersistFile, (void **)&pIPersistFile)) &&
        SUCCEEDED(hr = pIPersistFile->Save(NULL, TRUE)))
    {

        *ppITaskToRun = pITask;
    }
    else
        if (pITask)
            pITask->Release();

    if (pITaskScheduler)
        pITaskScheduler->Release();

    if (pIPersistFile)
        pIPersistFile->Release();

    if (pTrigger)
        pTrigger->Release();

    return hr;   
}

HRESULT SaveSysprepInfo(void)
{

    InitSS();
    SetMysteryDWORDValue();

    HRESULT hr = PreProcessNetScheduleJobs();
    if (FAILED(hr))
    {

    }

    HCRYPTPROV hCSP = NULL;
    hr = GetCSPHandle(&hCSP);
    if (SUCCEEDED(hr))
    {
        if (hCSP)
        {

            hr = SaveSysprepKeyInfo(hCSP);
            if (SUCCEEDED(hr))
            {
                hr = SaveSysprepIdentityInfo(hCSP);
            }

            CloseCSPHandle(hCSP);
        }
        else
        {
            hr = E_FAIL;
        }
    }

    UninitSS();

    if (FAILED(hr))
    {
        CHECK_HRESULT(hr);
    }

    return hr;
}

HRESULT SaveSysprepKeyInfo(HCRYPTPROV hCSP)
{

    RC2_KEY_INFO RC2KeyInfo;
    HRESULT hr = ComputeCredentialKey(hCSP, &RC2KeyInfo);
    if (SUCCEEDED(hr))
    {

        hr = WriteLsaData(sizeof(gwszSysprepKey), gwszSysprepKey, sizeof(RC2KeyInfo), (BYTE*)&RC2KeyInfo);
    }
    return hr;
}

HRESULT SaveSysprepIdentityInfo(HCRYPTPROV hCSP)
{
    WCHAR* pwszTasksFolder = NULL;
    HANDLE hFileEnum = INVALID_HANDLE_VALUE;
    DWORD  cbSAI;
    DWORD  cbSAC;
    BYTE*  pbSAI = NULL;
    BYTE*  pbSAC = NULL;

    HRESULT hr = ReadSecurityDBase(&cbSAI, &pbSAI, &cbSAC, &pbSAC);
    if (SUCCEEDED(hr))
    {
        if (cbSAI <= SAI_HEADER_SIZE)
        {

        }
        else
        {

            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;
                        DWORD dwRet = 0;
                        if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
                        {

                            dwRet = GetLastError();
                            if (dwRet != ERROR_FILE_NOT_FOUND)
                                hr = _HRESULT_FROM_WIN32(dwRet);
                        }
                        else
                        {

                            hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
                            if (SUCCEEDED(hr))
                            {
                                DWORD iConcatenation = lstrlenW(pwszTasksFolder);
                                wszSearchPath[iConcatenation++] = L'\\';

                                BYTE  rgbIdentityData[MAX_SECRET_SIZE];
                                BYTE* pbCurrentData = (BYTE*)&rgbIdentityData;
                                DWORD cbIdentityData = 0;

                                BYTE  rgbIdentity[HASH_DATA_SIZE];
                                BOOL  bIsPasswordNull;
                                DWORD dwCredentialIndex;
                                DWORD cbFileName;

                                while (dwRet != ERROR_NO_MORE_FILES)
                                {

                                    wszSearchPath[iConcatenation] = L'\0';
                                    if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
                                    {

                                        if (SUCCEEDED(HashJobIdentity(hCSP, wszSearchPath, rgbIdentity)))
                                        {

                                            hr = SAIFindIdentity(rgbIdentity,
                                                                 cbSAI,
                                                                 pbSAI,
                                                                 &dwCredentialIndex,
                                                                 &bIsPasswordNull,
                                                                 NULL,
                                                                 NULL,
                                                                 NULL);

                                            if (S_OK == hr)
                                            {
                                                cbFileName = (lstrlenW(fd.cFileName) + 1) * sizeof(WCHAR);

                                                if ((cbIdentityData + cbFileName + sizeof(BOOL) + sizeof(DWORD)) > MAX_SECRET_SIZE)
                                                {

                                                    hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
                                                    break;
                                                }

                                                CopyMemory(pbCurrentData, fd.cFileName, cbFileName);
                                                pbCurrentData += cbFileName;
                                                CopyMemory(pbCurrentData, &bIsPasswordNull, sizeof(BOOL));
                                                pbCurrentData += sizeof(BOOL);
                                                CopyMemory(pbCurrentData, &dwCredentialIndex, sizeof(DWORD));
                                                pbCurrentData += sizeof(DWORD);
                                                cbIdentityData += (cbFileName + sizeof(BOOL) + sizeof(DWORD));                
                                            }
                                            else
                                            {
                                                hr = S_OK;  
                                            }
                                        }
                                    }

                                    if (!FindNextFile(hFileEnum, &fd))
                                    {
                                        dwRet = GetLastError();
                                        if (dwRet != ERROR_NO_MORE_FILES)
                                            hr = _HRESULT_FROM_WIN32(dwRet);

                                        break;
                                    }
                                }

                                if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr)
                                {
                                    hr = WriteLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity, cbIdentityData, (BYTE*)&rgbIdentityData);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    if (pbSAI) LocalFree(pbSAI);
    if (pbSAC) LocalFree(pbSAC);

    if (pwszTasksFolder)
        delete [] pwszTasksFolder;

    if (hFileEnum != INVALID_HANDLE_VALUE)
        FindClose(hFileEnum);

    return hr;
}

HRESULT PreProcessNetScheduleJobs(void)
{
    HANDLE hFileEnum = INVALID_HANDLE_VALUE;

    WCHAR* pwszTasksFolder = NULL;
    HRESULT 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, L"\\" TSZ_AT_JOB_PREFIX L"*" TSZ_DOTJOB);
            if (SUCCEEDED(hr))
            {
                WIN32_FIND_DATA fd;
                DWORD dwRet = 0;
                if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
                {

                    dwRet = GetLastError();
                    if (dwRet != ERROR_FILE_NOT_FOUND)
                        hr = _HRESULT_FROM_WIN32(dwRet);
                }
                else
                {

                    hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, pwszTasksFolder);
                    if (SUCCEEDED(hr))
                    {
                        DWORD iConcatenation = lstrlenW(pwszTasksFolder);
                        wszSearchPath[iConcatenation++] = L'\\';

                        while (dwRet != ERROR_NO_MORE_FILES)
                        {

                            wszSearchPath[iConcatenation] = L'\0';
                            if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
                            {

                                CJob* pJob = CJob::Create();
                                if (!pJob)
                                {
                                    hr = E_OUTOFMEMORY;
                                    break;
                                }

                                if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
                                {
                                    if (!pJob->VerifySignature())
                                    {
                                        if (!DeleteFile(wszSearchPath))
                                        {
                                            dwRet = GetLastError();

                                        }
                                    }
                                }

                                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);
            }
        }
    }

    if (pwszTasksFolder)
        delete [] pwszTasksFolder;

    return hr;
}

HRESULT GetSysprepIdentityInfo(DWORD* pcbIdentityData, BYTE** ppIdentityData)
{
    *ppIdentityData = NULL;

    HRESULT hr = ReadLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity, pcbIdentityData, ppIdentityData);
    if (FAILED(hr))
    {
        if (*ppIdentityData != NULL)
        {
            LocalFree(*ppIdentityData);
            *ppIdentityData = NULL;
        }
    }

    return hr;
}

HRESULT GetSysprepKeyInfo(DWORD* pcbRC2KeyInfo, RC2_KEY_INFO** ppRC2KeyInfo)
{
    *ppRC2KeyInfo = NULL;

    HRESULT hr = ReadLsaData(sizeof(gwszSysprepKey), gwszSysprepKey, pcbRC2KeyInfo, (BYTE**)ppRC2KeyInfo);
    if (SUCCEEDED(hr))
    {

        if (*pcbRC2KeyInfo != sizeof(RC2_KEY_INFO))
        {
            *pcbRC2KeyInfo = 0;
            LocalFree(*ppRC2KeyInfo);
            *ppRC2KeyInfo = NULL;
            hr = E_FAIL;
        }
    }
    else  
    {
        if (*ppRC2KeyInfo != NULL)
        {
            LocalFree(*ppRC2KeyInfo);
            *ppRC2KeyInfo = NULL;
        }
    }

    return hr;
}

HRESULT ConvertSysprepInfo(void)
{

    HCRYPTPROV hCSP = NULL;

    BYTE* pbSAI = NULL;
    DWORD cbSAI = 0;
    BYTE* pbSAC = NULL;
    DWORD cbSAC = 0;

    RC2_KEY_INFO* pRC2KeyPreSysprep  = NULL;
    DWORD         cbRC2KeyPreSysprep = 0;
    RC2_KEY_INFO  RC2KeyPostSysprep;

    InitSS();
    SetMysteryDWORDValue();

    g_SvcStatus.dwCurrentState = SERVICE_RUNNING;

    HRESULT hr = GetTasksFolder(&g_TasksFolderInfo.ptszPath);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    hr = GetCSPHandle(&hCSP);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    hr = GetSysprepKeyInfo(&cbRC2KeyPreSysprep, &pRC2KeyPreSysprep);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    hr = ComputeCredentialKey(hCSP, &RC2KeyPostSysprep);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    hr = ConvertNetScheduleJobs();
    if (FAILED(hr))
    {

        hr = S_OK;
    }

    hr = ConvertNetScheduleCredentialData(pRC2KeyPreSysprep, &RC2KeyPostSysprep);
    if (FAILED(hr))
    {

        hr = S_OK;
    }

    hr = ReadSecurityDBase(&cbSAI, &pbSAI, &cbSAC, &pbSAC);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    if (cbSAI <= SAI_HEADER_SIZE || pbSAI == NULL ||
        cbSAC <= SAC_HEADER_SIZE || pbSAC == NULL)
    {
        goto ErrorExit;
    }

    hr = ConvertIdentityData(hCSP, &cbSAI, &pbSAI, &cbSAC, &pbSAC);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    hr = ConvertCredentialData(pRC2KeyPreSysprep, &RC2KeyPostSysprep, &cbSAI, &pbSAI, &cbSAC, &pbSAC);

    SecureZeroMemory(&RC2KeyPostSysprep, sizeof(RC2KeyPostSysprep));

    if (SUCCEEDED(hr))
    {

        hr = WriteSecurityDBase(cbSAI, pbSAI, cbSAC, pbSAC);
        if (SUCCEEDED(hr))
        {

            ScavengeSASecurityDBase();
        }
    }

ErrorExit:

    DeleteLsaData(sizeof(gwszSysprepKey), gwszSysprepKey);
    DeleteLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity);

    if (g_TasksFolderInfo.ptszPath)
        delete [] g_TasksFolderInfo.ptszPath;

    if (hCSP) CloseCSPHandle(hCSP);

    if (pbSAI) LocalFree(pbSAI);
    if (pbSAC) LocalFree(pbSAC);

    if (pRC2KeyPreSysprep) LocalFree(pRC2KeyPreSysprep);

    if (hr == SCHED_E_ACCOUNT_DBASE_CORRUPT)
    {

        DWORD dwZero = 0;
        WriteSecurityDBase(sizeof(dwZero), (BYTE*)&dwZero, sizeof(dwZero), (BYTE*)&dwZero);
    }

    UninitSS();

    return(hr);
}

HRESULT ConvertNetScheduleJobs(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;
            DWORD dwRet = 0;
            if ((hFileEnum = FindFirstFile(wszSearchPath, &fd)) == INVALID_HANDLE_VALUE)
            {

                dwRet = GetLastError();
                if (dwRet != ERROR_FILE_NOT_FOUND)
                    hr = _HRESULT_FROM_WIN32(dwRet);
            }
            else
            {

                hr = StringCchCopy(wszSearchPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
                if (SUCCEEDED(hr))
                {
                    DWORD iConcatenation = lstrlenW(g_TasksFolderInfo.ptszPath);
                    wszSearchPath[iConcatenation++] = L'\\';

                    while (dwRet != ERROR_NO_MORE_FILES)
                    {

                        wszSearchPath[iConcatenation] = L'\0';
                        if (SUCCEEDED(StringCchCat(wszSearchPath, MAX_PATH + 1, fd.cFileName)))
                        {

                            CJob* pJob = CJob::Create();
                            if (!pJob)
                            {
                                hr = E_OUTOFMEMORY;
                                break;
                            }

                            if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
                            {
                                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;
}

HRESULT ConvertIdentityData(HCRYPTPROV hCSP, DWORD* pcbSAI, BYTE** ppbSAI, DWORD* pcbSAC, BYTE** ppbSAC)
{

    BYTE* pbIdentityData = NULL;
    DWORD cbIdentityData = 0;

    HRESULT hr = GetSysprepIdentityInfo(&cbIdentityData, &pbIdentityData);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    if (pbIdentityData)
    {

        WCHAR  wszJobPath[MAX_PATH + 1];
        hr = StringCchCopy(wszJobPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
        if (SUCCEEDED(hr))
        {
            DWORD iConcatenation = lstrlenW(g_TasksFolderInfo.ptszPath);
            wszJobPath[iConcatenation++] = L'\\';

            BYTE*  pbCurrentData      = pbIdentityData;
            BYTE*  pbLastByte         = (BYTE*)(pbIdentityData + cbIdentityData - 1);
            WCHAR* pwszFileName       = NULL;
            BOOL   bIsPasswordNull;
            DWORD  dwCredentialIndex;
            BYTE   rgbIdentity[HASH_DATA_SIZE];
            BYTE*  pbIdentitySet      = NULL;

            while (pbCurrentData <= pbLastByte)
            {
                pwszFileName = (WCHAR*) pbCurrentData;
                pbCurrentData += ((lstrlenW(pwszFileName) + 1) * sizeof(WCHAR));
                CopyMemory(&bIsPasswordNull, pbCurrentData, sizeof(BOOL));
                pbCurrentData += sizeof(BOOL);
                CopyMemory(&dwCredentialIndex, pbCurrentData, sizeof(DWORD));

                if ((pbCurrentData + sizeof(DWORD)) > (pbLastByte + 1))
                {

                    hr = E_FAIL;
                    goto ErrorExit;
                }

                wszJobPath[iConcatenation] = L'\0';
                if (SUCCEEDED(StringCchCat(wszJobPath, MAX_PATH + 1, pwszFileName)))
                {

                    if (SUCCEEDED(HashJobIdentity(hCSP, (LPCWSTR)&wszJobPath, rgbIdentity)))
                    {

                        if (bIsPasswordNull)
                        {
                            LAST_HASH_BYTE(rgbIdentity) ^= 1;
                        }

                        hr = SAIIndexIdentity(*pcbSAI,
                                              *ppbSAI,
                                              dwCredentialIndex,
                                              0,
                                              NULL,
                                              NULL,
                                              &pbIdentitySet);
                        if (hr == S_FALSE)
                        {

                            ASSERT_SECURITY_DBASE_CORRUPT();
                            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
                            goto ErrorExit;
                        }
                        else if (SUCCEEDED(hr))
                        {
                            hr = SAIInsertIdentity(rgbIdentity,
                                                   pbIdentitySet,
                                                   pcbSAI,
                                                   ppbSAI);
                        }
                        else
                        {
                            hr = S_OK;  
                        }
                    }
                }

                pbCurrentData += sizeof(DWORD);
            }
        }
    }

ErrorExit:
    if (pbIdentityData)
        LocalFree(pbIdentityData);

    return(hr);
}

HRESULT ConvertCredentialData(RC2_KEY_INFO* pRC2KeyPreSysprep,
                              RC2_KEY_INFO* pRC2KeyPostSysprep,
                              DWORD* pcbSAI,
                              BYTE** ppbSAI,
                              DWORD* pcbSAC,
                              BYTE** ppbSAC)
{
    HRESULT hr    = S_OK;
    HRESULT hRes2 = S_OK; 

    BYTE* pbEncryptedData = NULL;
    DWORD cbEncryptedData = 0;

    BYTE* pbSACEnd = *ppbSAC + *pcbSAC;
    BYTE* pbCredential = *ppbSAC + USN_SIZE;     
    DWORD cbCredential = 0;

    DWORD dwCredentialCount = 0;
    CopyMemory(&dwCredentialCount, pbCredential, sizeof(dwCredentialCount));
    pbCredential += sizeof(dwCredentialCount);

    JOB_CREDENTIALS jc;
    RC2_KEY_INFO    RC2KeyPreSysprepCopy;
    RC2_KEY_INFO    RC2KeyPostSysprepCopy;

    for (DWORD dwCredentialIndex = 0;
         (dwCredentialIndex < dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) < *pcbSAC);
         dwCredentialIndex++
        )
    {

        if ((pbCredential + sizeof(cbCredential)) > pbSACEnd)
        {
            ASSERT_SECURITY_DBASE_CORRUPT();
            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
            goto ErrorExit;
        }

        CopyMemory(&cbCredential, pbCredential, sizeof(cbCredential));
        pbCredential += sizeof(cbCredential);

        if ((pbCredential + HASH_DATA_SIZE) > pbSACEnd)
        {
            ASSERT_SECURITY_DBASE_CORRUPT();
            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
            goto ErrorExit;
        }

        if ((pbCredential + cbCredential) > pbSACEnd)
        {
            ASSERT_SECURITY_DBASE_CORRUPT();
            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
            goto ErrorExit;
        }

        cbEncryptedData = cbCredential - HASH_DATA_SIZE;
        pbEncryptedData = new BYTE[cbEncryptedData];
        CopyMemory(pbEncryptedData, pbCredential + HASH_DATA_SIZE, cbEncryptedData);

        CopyMemory(&RC2KeyPreSysprepCopy, pRC2KeyPreSysprep, sizeof(RC2_KEY_INFO));

        hRes2 = DecryptCredentials(RC2KeyPreSysprepCopy, cbEncryptedData, pbEncryptedData, &jc);

        SecureZeroMemory(pbEncryptedData, cbEncryptedData);

        if (SUCCEEDED(hRes2))
        {

            CopyMemory(&RC2KeyPostSysprepCopy, pRC2KeyPostSysprep, sizeof(RC2_KEY_INFO));

            hRes2 = EncryptCredentials(RC2KeyPostSysprepCopy,
                                    jc.wszAccount,
                                    jc.wszDomain,
                                    jc.wszPassword,
                                    NULL,
                                    &cbEncryptedData,
                                    &pbEncryptedData);

            ZERO_PASSWORD(jc.wszPassword);
            jc.ccPassword = 0;

            if (SUCCEEDED(hRes2))
            {

                hr = SACUpdateCredential(cbEncryptedData,
                                         pbEncryptedData,
                                         cbCredential,
                                         pbCredential,
                                         pcbSAC,
                                         ppbSAC);
                if (FAILED(hr))
                {
                    goto ErrorExit;  

                }
            }
        }

        delete pbEncryptedData;
        pbEncryptedData = NULL;

        pbCredential += (HASH_DATA_SIZE + cbEncryptedData);
    }

    if ((dwCredentialIndex == dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) != *pcbSAC) ||
        (dwCredentialIndex != dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) > *pcbSAC))
    {

        ASSERT_SECURITY_DBASE_CORRUPT();
        hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
        goto ErrorExit;
    }

ErrorExit:
    if (pbEncryptedData) LocalFree(pbEncryptedData);

    return(hr);
}

HRESULT ConvertNetScheduleCredentialData(RC2_KEY_INFO* pRC2KeyPreSysprep, RC2_KEY_INFO* pRC2KeyPostSysprep)
{
    BYTE* pbEncryptedData = NULL;
    DWORD cbEncryptedData = 0;

    HRESULT hr = ReadLsaData(sizeof(WSZ_SANSC), WSZ_SANSC, &cbEncryptedData, &pbEncryptedData);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    if (hr == S_FALSE || cbEncryptedData <= sizeof(DWORD))
    {

    }
    else
    {

        RC2_KEY_INFO RC2KeyPreSysprepCopy;
        CopyMemory(&RC2KeyPreSysprepCopy, pRC2KeyPreSysprep, sizeof(RC2_KEY_INFO));

        JOB_CREDENTIALS jc;
        hr = DecryptCredentials(RC2KeyPreSysprepCopy,
                                cbEncryptedData,
                                pbEncryptedData,
                                &jc);

        SecureZeroMemory(&RC2KeyPreSysprepCopy, sizeof(RC2_KEY_INFO));

        if (FAILED(hr))
        {
            goto ErrorExit;
        }

        SecureZeroMemory(pbEncryptedData, cbEncryptedData);

        RC2_KEY_INFO RC2KeyPostSysprepCopy;
        CopyMemory(&RC2KeyPostSysprepCopy, pRC2KeyPostSysprep, sizeof(RC2_KEY_INFO));

        hr = EncryptCredentials(RC2KeyPostSysprepCopy,
                                jc.wszAccount,
                                jc.wszDomain,
                                jc.wszPassword,
                                NULL,
                                &cbEncryptedData,
                                &pbEncryptedData);

        SecureZeroMemory(&RC2KeyPostSysprepCopy, sizeof(RC2_KEY_INFO));

        if (FAILED(hr))
        {
            goto ErrorExit;
        }

        ZERO_PASSWORD(jc.wszPassword);

        hr = WriteLsaData(sizeof(WSZ_SANSC), WSZ_SANSC, cbEncryptedData, pbEncryptedData);
   }

ErrorExit:
    if (pbEncryptedData != NULL)
        LocalFree(pbEncryptedData);

    return(hr);
}
//+---------------------------------------------------------------------------
//
//  Microsoft Windows
//  Copyright(C) 2002 Microsoft Corporation
//
//  File: sysprep.cxx
//
//----------------------------------------------------------------------------

#include "..\pch\headers.hxx"
#include "security.hxx"
#include "sysprep.hxx"

static WCHAR          gwszSysprepKey[]      = L"TSSK"; // Task Scheduler Sysprep Key
static WCHAR          gwszSysprepIdentity[] = L"TSSI"; // Task Scheduler Sysprep Identity Data

// needed by ScavengeSASecurityDBase
extern  SERVICE_STATUS  g_SvcStatus;
#define SERVICE_RUNNING 0x00000004

//+---------------------------------------------------------------------------
//
//  Function:   GetUniqueSPSName
//
//  Synopsis:   calls NewWorkItem a few times trying to get a unique file name out of it
//
//  Arguments:  ITaskScheduler *pITaskScheduler, IUnknown** pITask
//              WCHAR* pwszTaskName, to receive the name that was actually
//              used in the creation of the task
//
//  Returns:    Various HRESULTs
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
HRESULT GetUniqueSPSName(ITaskScheduler* pITaskScheduler, ITask** ppITask, WCHAR* pwszTaskName)
{
    HRESULT hr = E_FAIL; 
    
    // okay, so we're not distinguishing between errors, code's simpler
    // and the only expectable error is "already exists"
    for (int i = 0; (i < 16) && FAILED(hr); i++)
    {
        if (FAILED(StringCchPrintf(pwszTaskName, 20, L"$~$Sys%X$", i)))
            break;
    
        hr = pITaskScheduler->NewWorkItem(pwszTaskName,           
             CLSID_CTask,            
             IID_ITask,              
             (IUnknown**)ppITask); 
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   PrepSysPrepTask
//
//  Synopsis:   Creates a task to be run which will call run the sysprep
//              code in the local system account
//
//  Arguments:  Task** ppITaskToRun, to receive pointer to ITask interface
//              task is to activated by calling the Run() method
//              WCHAR* pwszTaskName, to receive the name that was actually
//              used in the creation of the task
//
//  Returns:    Various HRESULTs
//
//  Notes:      None.
//
//----------------------------------------------------------------------------
HRESULT PrepSysPrepTask(ITask** ppITaskToRun, WCHAR* pwszTaskName)
{
    HRESULT hr = E_FAIL;
    WCHAR applicationName[MAX_PATH +1];
    WCHAR argument[MAX_PATH +20];
    DWORD expandedSize;

    expandedSize = ExpandEnvironmentStrings(L"\"%SystemRoot%\\System32\\rundll32.exe\"", applicationName, MAX_PATH +1);
    if ((0 == expandedSize) || expandedSize > (MAX_PATH +1))
        return HRESULT_FROM_WIN32(GetLastError());

    expandedSize = ExpandEnvironmentStrings(L"\"%SystemRoot%\\System32\\SchedSvc.dll\",SysPrepCallback", argument, MAX_PATH +20);
    if ((0 == expandedSize) || expandedSize > (MAX_PATH +1))
        return HRESULT_FROM_WIN32(GetLastError());

    TASK_TRIGGER tigger;
    tigger.wStartHour = 10;            
    tigger.wStartMinute = 20;          
    tigger.wBeginYear = 1957;            
    tigger.wBeginMonth = 6;           
    tigger.wBeginDay = 14;             

    tigger.wEndYear = 2001;              
    tigger.wEndMonth = 10;              
    tigger.wEndDay = 8 ;               
    tigger.MinutesDuration = 0;      
    tigger.MinutesInterval = 0;      
    tigger.rgFlags = 0;              
    tigger.TriggerType = TASK_TIME_TRIGGER_ONCE;
    tigger.Reserved2 = 0;
    tigger.wRandomMinutesInterval = 0;
    tigger.cbTriggerSize = sizeof(TASK_TRIGGER);
    tigger.Reserved1 = 0;

    ITaskScheduler *pITaskScheduler = NULL;
    ITask *pITask = NULL;
    IPersistFile *pIPersistFile = NULL;
    ITaskTrigger* pTrigger = NULL;    
    WORD idontcare;

    hr = CoCreateInstance( CLSID_CTaskScheduler, NULL, CLSCTX_INPROC_SERVER, IID_ITaskScheduler, (void**)&pITaskScheduler);
    if (SUCCEEDED(hr) && 
        SUCCEEDED(hr = GetUniqueSPSName(pITaskScheduler, &pITask, pwszTaskName)) &&
        SUCCEEDED(hr = pITask->SetApplicationName(applicationName)) &&
        SUCCEEDED(hr = pITask->SetParameters(argument)) &&
        SUCCEEDED(hr = pITask->SetFlags(TASK_FLAG_DELETE_WHEN_DONE | TASK_FLAG_DISABLED))     &&
        SUCCEEDED(hr = pITask->SetAccountInformation(L"", NULL))     &&
        SUCCEEDED(hr = pITask->CreateTrigger(&idontcare, &pTrigger))  &&
        SUCCEEDED(hr = pTrigger->SetTrigger(&tigger))                  &&
        SUCCEEDED(hr = pITask->QueryInterface(IID_IPersistFile, (void **)&pIPersistFile)) &&
        SUCCEEDED(hr = pIPersistFile->Save(NULL, TRUE)))
    {
        // return it to the caller
        *ppITaskToRun = pITask;
    }
    else
        if (pITask)
            pITask->Release();

    if (pITaskScheduler)
        pITaskScheduler->Release();

    if (pIPersistFile)
        pIPersistFile->Release();

    if (pTrigger)
        pTrigger->Release();

    return hr;   
}

//+---------------------------------------------------------------------------
//
//  Function:   SaveSysprepInfo
//
//  Synopsis:   Saves job identity and credential key information prior to Sysprep
//              so that it can be used to convert old data after Sysprep.
//
//  Arguments:  None.
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT SaveSysprepInfo(void)
{
    //
    //  Initialize security
    //
    InitSS();
    SetMysteryDWORDValue();

    HRESULT hr = PreProcessNetScheduleJobs();
    if (FAILED(hr))
    {
        // ignore, we still need to do the other processing
    }

    //
    // Obtain a provider handle to the CSP (for use with Crypto API).
    //
    HCRYPTPROV hCSP = NULL;
    hr = GetCSPHandle(&hCSP);
    if (SUCCEEDED(hr))
    {
        if (hCSP)
        {
            //
            // We've got the handle, now save the important stuff
            //
            hr = SaveSysprepKeyInfo(hCSP);
            if (SUCCEEDED(hr))
            {
                hr = SaveSysprepIdentityInfo(hCSP);
            }

            CloseCSPHandle(hCSP);
        }
        else
        {
            hr = E_FAIL;
        }
    }

    //
    // Done doing security stuff
    //
    UninitSS();

    if (FAILED(hr))
    {
        CHECK_HRESULT(hr);
    }
    
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   SaveSysprepKeyInfo
//
//  Synopsis:   Stores the computed CredentialKey as a secret prior to sysprep so that it can be
//              retrieved and used to decrypt credentials after sysprep, so those credentials may
//              be encrypted again using the post-sysprep key.
//
//  Arguments:  hCSP - handle to the crypto service provider
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT SaveSysprepKeyInfo(HCRYPTPROV hCSP)
{
    //
    // Generate the encryption key
    //
    RC2_KEY_INFO RC2KeyInfo;
    HRESULT hr = ComputeCredentialKey(hCSP, &RC2KeyInfo);
    if (SUCCEEDED(hr))
    {
        //
        // Write the key to LSA
        //
        hr = WriteLsaData(sizeof(gwszSysprepKey), gwszSysprepKey, sizeof(RC2KeyInfo), (BYTE*)&RC2KeyInfo);
    }
    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   SaveSysprepIdentityInfo
//
//  Synopsis:   Obtains the credential index and NULL password setting for each
//              existing job identity and stores that information along with the
//              associated filename in an LSA secret so that the identities can
//              be rehashed and stored in the identity database, postsysprep,
//              still associated with the correct credential.
//
//  Arguments:  hCSP - handle to the crypto service provider
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT SaveSysprepIdentityInfo(HCRYPTPROV hCSP)
{
    WCHAR* pwszTasksFolder = NULL;
    HANDLE hFileEnum = INVALID_HANDLE_VALUE;
    DWORD  cbSAI;
    DWORD  cbSAC;
    BYTE*  pbSAI = NULL;
    BYTE*  pbSAC = NULL;

    //
    // First, read the security database so we can do the lookups
    // 
    HRESULT 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;
                        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'\\';
            
                                //
                                // Allocate buffer used to collect identity data that will be written to the LSA secret
                                //
                                BYTE  rgbIdentityData[MAX_SECRET_SIZE];
                                BYTE* pbCurrentData = (BYTE*)&rgbIdentityData;
                                DWORD cbIdentityData = 0;
            
                                //
                                // Process each found file
                                //
                                BYTE  rgbIdentity[HASH_DATA_SIZE];
                                BOOL  bIsPasswordNull;
                                DWORD dwCredentialIndex;
                                DWORD cbFileName;
            
                                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.
                                        //
                                        if (SUCCEEDED(HashJobIdentity(hCSP, wszSearchPath, rgbIdentity)))
                                        {
                                            //
                                            // Find the identity in the SAI for this job
                                            //
                                            hr = SAIFindIdentity(rgbIdentity,
                                                                 cbSAI,
                                                                 pbSAI,
                                                                 &dwCredentialIndex,
                                                                 &bIsPasswordNull,
                                                                 NULL,
                                                                 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)
                                            {
                                                cbFileName = (lstrlenW(fd.cFileName) + 1) * sizeof(WCHAR);
                                                
                                                if ((cbIdentityData + cbFileName + sizeof(BOOL) + sizeof(DWORD)) > MAX_SECRET_SIZE)
                                                {
                                                    // 
                                                    // this should _never_ happen, as we shouldn't be able to exceed the size
                                                    // given that we only add data for jobs that have already been found in
                                                    // the SAI, and we are collecting less data than was stored there
                                                    //
                                                    hr = HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER);
                                                    break;
                                                }
                
                                                CopyMemory(pbCurrentData, fd.cFileName, cbFileName);
                                                pbCurrentData += cbFileName;
                                                CopyMemory(pbCurrentData, &bIsPasswordNull, sizeof(BOOL));
                                                pbCurrentData += sizeof(BOOL);
                                                CopyMemory(pbCurrentData, &dwCredentialIndex, sizeof(DWORD));
                                                pbCurrentData += sizeof(DWORD);
                                                cbIdentityData += (cbFileName + sizeof(BOOL) + sizeof(DWORD));                
                                            }
                                            else
                                            {
                                                hr = S_OK;  // OK, we failed this one, go on to the next
                                            }
                                        }
                                    }
                                
                                    if (!FindNextFile(hFileEnum, &fd))
                                    {
                                        dwRet = GetLastError();
                                        if (dwRet != ERROR_NO_MORE_FILES)
                                            hr = _HRESULT_FROM_WIN32(dwRet);
                
                                        break;
                                    }
                                }
                
                                if (HRESULT_FROM_WIN32(ERROR_INSUFFICIENT_BUFFER) != hr)
                                {
                                    hr = WriteLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity, cbIdentityData, (BYTE*)&rgbIdentityData);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    if (pbSAI) LocalFree(pbSAI);
    if (pbSAC) LocalFree(pbSAC);

    if (pwszTasksFolder)
        delete [] pwszTasksFolder;

    if (hFileEnum != INVALID_HANDLE_VALUE)
        FindClose(hFileEnum);

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   PreProcessNetScheduleJobs
//
//  Synopsis:   Netschedule jobs (AT jobs) are signed internally with a hash so
//              that the service can validate their authenticity at run time.
//              Sysprep changes important data used by the Crypto API such that
//              the generated hash will be different and can never match the hash
//              stored in the AT job.  In order to be runnable again, the AT jobs
//              must be signed again with the new hash.
//
//              *** WARNING ***
//              This means that if someone could drop a bogus AT job into the tasks
//              folder, it would automatically get signed as a result of running sysprep.
//              In order to prevent this, check all AT jobs prior to sysprep and
//              eliminate any that are not valid.
//
//  Arguments:  None
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT PreProcessNetScheduleJobs(void)
{
    HANDLE hFileEnum = INVALID_HANDLE_VALUE;

    WCHAR* pwszTasksFolder = NULL;
    HRESULT 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, L"\\" TSZ_AT_JOB_PREFIX L"*" TSZ_DOTJOB);
            if (SUCCEEDED(hr))
            {
                WIN32_FIND_DATA 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, pwszTasksFolder);
                    if (SUCCEEDED(hr))
                    {
                        DWORD iConcatenation = lstrlenW(pwszTasksFolder);
                        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 and check signature of each job
                                //
                                CJob* pJob = CJob::Create();
                                if (!pJob)
                                {
                                    hr = E_OUTOFMEMORY;
                                    break;
                                }
                                
                                if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
                                {
                                    if (!pJob->VerifySignature())
                                    {
                                        if (!DeleteFile(wszSearchPath))
                                        {
                                            dwRet = GetLastError();
                                            
                                            // go on anyway                                            
                                        }
                                    }
                                }
                    
                                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);
            }
        }
    }

    if (pwszTasksFolder)
        delete [] pwszTasksFolder;

    return hr;
}








//+---------------------------------------------------------------------------
//
//  Function:   GetSysprepIdentityInfo
//
//  Synopsis:   Retrieves the stored job identity data so new hashes can be calculated.
//
//  Arguments:  pcbIdentityData - pointer to count of bytes in the identity data
//              ppIdentityData  - pointer to pointer to retrieved identity data
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT GetSysprepIdentityInfo(DWORD* pcbIdentityData, BYTE** ppIdentityData)
{
    *ppIdentityData = NULL;

    //
    // Retrieve job identity data from LSA, if it exists
    //
    HRESULT hr = ReadLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity, pcbIdentityData, ppIdentityData);
    if (FAILED(hr))
    {
        if (*ppIdentityData != NULL)
        {
            LocalFree(*ppIdentityData);
            *ppIdentityData = NULL;
        }
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   GetSysprepKeyInfo
//
//  Synopsis:   Retrieves the stored pre-sysprep CredentialKey so that it can be used to decrypt
//              credentials after sysprep, so those credentials may be encrypted again using the
//              post-sysprep key.
//
//  Arguments:  pcbRC2KeyInfo   - pointer to count of bytes in key data
//              ppRC2KeyInfo    - pointer to pointer to retrieved key
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT GetSysprepKeyInfo(DWORD* pcbRC2KeyInfo, RC2_KEY_INFO** ppRC2KeyInfo)
{
    *ppRC2KeyInfo = NULL;

    //
    // Get the key from LSA
    //
    HRESULT hr = ReadLsaData(sizeof(gwszSysprepKey), gwszSysprepKey, pcbRC2KeyInfo, (BYTE**)ppRC2KeyInfo);
    if (SUCCEEDED(hr))
    {
        //
        // Check the size.  We know exactly how big this should be, so make sure it is.
        //
        if (*pcbRC2KeyInfo != sizeof(RC2_KEY_INFO))
        {
            *pcbRC2KeyInfo = 0;
            LocalFree(*ppRC2KeyInfo);
            *ppRC2KeyInfo = NULL;
            hr = E_FAIL;
        }
    }
    else  // failed!
    {
        if (*ppRC2KeyInfo != NULL)
        {
            LocalFree(*ppRC2KeyInfo);
            *ppRC2KeyInfo = NULL;
        }
    }

    return hr;
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertSysprepInfo
//
//  Synopsis:   Converts credentials stored prior to sysprep so they may be decrypted after sysprep.
//              It does this by retrieving the pre-sysprep key, decrypting the credentials with it, and then
//              encrypting them using the new post-sysprep key.
//
//  Arguments:  None
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT ConvertSysprepInfo(void)
{
    //
    // Initialize these up here so they will have values if we exit early
    // 
    HCRYPTPROV hCSP = NULL;
    
    BYTE* pbSAI = NULL;
    DWORD cbSAI = 0;
    BYTE* pbSAC = NULL;
    DWORD cbSAC = 0;

    RC2_KEY_INFO* pRC2KeyPreSysprep  = NULL;
    DWORD         cbRC2KeyPreSysprep = 0;
    RC2_KEY_INFO  RC2KeyPostSysprep;

    //
    //  Initialize security
    //
    InitSS();
    SetMysteryDWORDValue();

    //
    // Set this as it is relied on by ScavengeSASecurityDBase
    //
    g_SvcStatus.dwCurrentState = SERVICE_RUNNING;

    //
    // Get the tasks folder for use by ConvertNetScheduleJobs, ConvertIdentityData, and ScavengeSASecurityDBase
    //
    HRESULT hr = GetTasksFolder(&g_TasksFolderInfo.ptszPath);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    //
    // Obtain a provider handle to the CSP (for use with Crypto API)
    //
    hr = GetCSPHandle(&hCSP);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    //
    // Get the pre-sysprep key from LSA
    //
    hr = GetSysprepKeyInfo(&cbRC2KeyPreSysprep, &pRC2KeyPreSysprep);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    //
    // Generate the post-sysprep key
    //
    hr = ComputeCredentialKey(hCSP, &RC2KeyPostSysprep);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    //
    // The Net Schedule conversions are independent of the rest of the logic,
    // so do them first and get them out of the way.
    //
    hr = ConvertNetScheduleJobs();
    if (FAILED(hr))
    {
        // ignore, we still can do our other conversions
        hr = S_OK;
    }

    hr = ConvertNetScheduleCredentialData(pRC2KeyPreSysprep, &RC2KeyPostSysprep);
    if (FAILED(hr))
    {
        // ignore, we still can do our other conversions
        hr = S_OK;
    }

    //
    // Read SAI & SAC databases.
    // It is not necessary to guard security db access with a critsec as elsewhere in the code,
    // as the service will not be running during MiniSetup and there will be no other threads accessing this data.
    //
    hr = ReadSecurityDBase(&cbSAI, &pbSAI, &cbSAC, &pbSAC);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    //
    // Do some validations on the database
    //
    if (cbSAI <= SAI_HEADER_SIZE || pbSAI == NULL ||
        cbSAC <= SAC_HEADER_SIZE || pbSAC == NULL)
    {
        goto ErrorExit;
    }

    //
    // Place updated entries into SAI for all jobs
    //
    hr = ConvertIdentityData(hCSP, &cbSAI, &pbSAI, &cbSAC, &pbSAC);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    //
    // Place updated entries into SAC for all credentials
    //
    hr = ConvertCredentialData(pRC2KeyPreSysprep, &RC2KeyPostSysprep, &cbSAI, &pbSAI, &cbSAC, &pbSAC);

    //
    // Clearing key content at earliest opportunity
    //
    SecureZeroMemory(&RC2KeyPostSysprep, sizeof(RC2KeyPostSysprep));
 
    if (SUCCEEDED(hr))
    {
        //
        // Update security database with new identities and converted credentials
        //
        hr = WriteSecurityDBase(cbSAI, pbSAI, cbSAC, pbSAC);
        if (SUCCEEDED(hr))
        {
            //
            // Allow scavenger cleanup to delete old identities
            //
            ScavengeSASecurityDBase();
        }
    }

ErrorExit:
    //
    // Delete the secrets
    //
    DeleteLsaData(sizeof(gwszSysprepKey), gwszSysprepKey);
    DeleteLsaData(sizeof(gwszSysprepIdentity), gwszSysprepIdentity);

    //
    // Clean up
    //
    if (g_TasksFolderInfo.ptszPath)
        delete [] g_TasksFolderInfo.ptszPath;

    if (hCSP) CloseCSPHandle(hCSP);
    
    if (pbSAI) LocalFree(pbSAI);
    if (pbSAC) LocalFree(pbSAC);
    
    if (pRC2KeyPreSysprep) LocalFree(pRC2KeyPreSysprep);

    //
    // Log an error & reset the SA security dbases SAI & SAC if corruption is detected.
    //
    if (hr == SCHED_E_ACCOUNT_DBASE_CORRUPT)
    {
        //
        // Reset SAI & SAC by writing four bytes of zeros into each.
        // Ignore the return code. No recourse if this fails.
        //
        DWORD dwZero = 0;
        WriteSecurityDBase(sizeof(dwZero), (BYTE*)&dwZero, sizeof(dwZero), (BYTE*)&dwZero);
    }

    //
    // Done doing security stuff
    //
    UninitSS();
    
    return(hr);
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertNetScheduleJobs
//
//  Synopsis:   Netschedule jobs (AT jobs) are signed internally with a hash so
//              that the service can validate their authenticity at run time.
//              Sysprep changes important data used by the Crypto API such that
//              the generated hash will be different and can never match the hash
//              stored in the AT job.  In order to be runnable again, the AT jobs
//              must be signed again with the new hash.
//
//              *** WARNING ***
//              This means that if someone could drop a bogus AT job into the tasks
//              folder, it would automatically get signed as a result of running sysprep.
//              In order to prevent this, PreProcessNetScheduleJobs has already checked
//              all AT jobs prior to sysprep and eliminated any that were not valid.
//  Arguments:  None
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT ConvertNetScheduleJobs(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;
            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, sign, save, and release the job
                            //
                            CJob* pJob = CJob::Create();
                            if (!pJob)
                            {
                                hr = E_OUTOFMEMORY;
                                break;
                            }
                            
                            if (SUCCEEDED(pJob->Load(wszSearchPath, 0)))
                            {
                                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:   ConvertIdentityData
//
//  Synopsis:   Create new job identity entries in SAI representing the existing
//              jobs on the system.  It does this by retrieving information stored
//              pre-sysprep associating each existing job with its credential, then
//              creating and storing a new identity to reference that credential.
//
//  Arguments:  hCSP   - handle to crypto service provider
//              pcbSAI - pointer to dword containing count of bytes in SAI
//              ppbSAI - pointer to pointer to SAI data
//              pcbSAC - pointer to dword containing count of bytes in SAC
//              ppbSAC - pointer to pointer to SAC data
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT ConvertIdentityData(HCRYPTPROV hCSP, DWORD* pcbSAI, BYTE** ppbSAI, DWORD* pcbSAC, BYTE** ppbSAC)
{
    //
    // Get the job identity data from LSA
    //
    BYTE* pbIdentityData = NULL;
    DWORD cbIdentityData = 0;

    HRESULT hr = GetSysprepIdentityInfo(&cbIdentityData, &pbIdentityData);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    //
    // Insert updated hashes into SAI for pre-sysprep jobs
    //
    if (pbIdentityData)
    {
        //
        // Get the tasks folder and prepare buffer for use in producing job path
        //
        WCHAR  wszJobPath[MAX_PATH + 1];
        hr = StringCchCopy(wszJobPath, MAX_PATH + 1, g_TasksFolderInfo.ptszPath);
        if (SUCCEEDED(hr))
        {
            DWORD iConcatenation = lstrlenW(g_TasksFolderInfo.ptszPath);
            wszJobPath[iConcatenation++] = L'\\';
    
            //
            // Process each stored task
            //        
            BYTE*  pbCurrentData      = pbIdentityData;
            BYTE*  pbLastByte         = (BYTE*)(pbIdentityData + cbIdentityData - 1);
            WCHAR* pwszFileName       = NULL;
            BOOL   bIsPasswordNull;
            DWORD  dwCredentialIndex;
            BYTE   rgbIdentity[HASH_DATA_SIZE];
            BYTE*  pbIdentitySet      = NULL;
    
            while (pbCurrentData <= pbLastByte)
            {
                pwszFileName = (WCHAR*) pbCurrentData;
                pbCurrentData += ((lstrlenW(pwszFileName) + 1) * sizeof(WCHAR));
                CopyMemory(&bIsPasswordNull, pbCurrentData, sizeof(BOOL));
                pbCurrentData += sizeof(BOOL);
                CopyMemory(&dwCredentialIndex, pbCurrentData, sizeof(DWORD));
    
                if ((pbCurrentData + sizeof(DWORD)) > (pbLastByte + 1))
                {
                    // 
                    // the first DWORD after pbCurrentData is beyond the first BYTE after pbLastByte;
                    // this means that the DWORD pointed to by pbCurrentData is partially or wholly 
                    // beyond the end of the data -- something is corrupt
                    //
                    hr = E_FAIL;
                    goto ErrorExit;
                }
    
                //
                // Truncate the existing name after the folder path,
                // then concatenate the new filename onto it.
                //
                wszJobPath[iConcatenation] = L'\0';
                if (SUCCEEDED(StringCchCat(wszJobPath, MAX_PATH + 1, pwszFileName)))
                {
                    //
                    // Hash the job into a unique identity.
                    //
                    if (SUCCEEDED(HashJobIdentity(hCSP, (LPCWSTR)&wszJobPath, rgbIdentity)))
                    {
                        //
                        // Store a NULL password by flipping the last bit of the hash data.
                        //
                        if (bIsPasswordNull)
                        {
                            LAST_HASH_BYTE(rgbIdentity) ^= 1;
                        }
            
                        //
                        // Insert the job identity into the SAI identity set associated
                        // with this credential.
                        //
                        hr = SAIIndexIdentity(*pcbSAI,
                                              *ppbSAI,
                                              dwCredentialIndex,
                                              0,
                                              NULL,
                                              NULL,
                                              &pbIdentitySet);
                        if (hr == S_FALSE)
                        {
                            //
                            // The SAC & SAI databases are out of sync. Should *never* occur.
                            //
                            ASSERT_SECURITY_DBASE_CORRUPT();
                            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
                            goto ErrorExit;
                        }
                        else if (SUCCEEDED(hr))
                        {
                            hr = SAIInsertIdentity(rgbIdentity,
                                                   pbIdentitySet,
                                                   pcbSAI,
                                                   ppbSAI);
                        }
                        else
                        {
                            hr = S_OK;  // OK, we failed this one, go on to the next
                        }
                    }
                }
                
                pbCurrentData += sizeof(DWORD);
            }
        }
    }

ErrorExit:
    if (pbIdentityData)
        LocalFree(pbIdentityData);
    
    return(hr);
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertCredentialData
//
//  Synopsis:   Converts credentials stored prior to sysprep so they may be decrypted after sysprep.
//              It does this by retrieving the pre-sysprep key, decrypting the credentials with it, and then
//              encrypting them using the new post-sysprep key.
//
//  Arguments:  pRC2KeyPreSysprep - pointer to presysprep key
//              pRC2KeyPostSysprep - pointer to postsysprep key
//              pcbSAI - pointer to dword containing count of bytes in SAI
//              ppbSAI - pointer to pointer to SAI data
//              pcbSAC - pointer to dword containing count of bytes in SAC
//              ppbSAC - pointer to pointer to SAC data
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT ConvertCredentialData(RC2_KEY_INFO* pRC2KeyPreSysprep,
                              RC2_KEY_INFO* pRC2KeyPostSysprep,
                              DWORD* pcbSAI,
                              BYTE** ppbSAI,
                              DWORD* pcbSAC,
                              BYTE** ppbSAC)
{
    HRESULT hr    = S_OK;
    HRESULT hRes2 = S_OK; // for hresults that we don't want to affect our return code

    BYTE* pbEncryptedData = NULL;
    DWORD cbEncryptedData = 0;
    
    BYTE* pbSACEnd = *ppbSAC + *pcbSAC;
    BYTE* pbCredential = *ppbSAC + USN_SIZE;     // Advance past USN.
    DWORD cbCredential = 0;

    //
    // Read credential count
    //
    DWORD dwCredentialCount = 0;
    CopyMemory(&dwCredentialCount, pbCredential, sizeof(dwCredentialCount));
    pbCredential += sizeof(dwCredentialCount);

    //        
    // Loop through all credentials, decrypting, encrypting, and updating each one
    //
    JOB_CREDENTIALS jc;
    RC2_KEY_INFO    RC2KeyPreSysprepCopy;
    RC2_KEY_INFO    RC2KeyPostSysprepCopy;
    
    for (DWORD dwCredentialIndex = 0;
         (dwCredentialIndex < dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) < *pcbSAC);
         dwCredentialIndex++
        )
    {
        //
        // Ensure sufficient space remains in the buffer.
        //
        if ((pbCredential + sizeof(cbCredential)) > pbSACEnd)
        {
            ASSERT_SECURITY_DBASE_CORRUPT();
            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
            goto ErrorExit;
        }

        CopyMemory(&cbCredential, pbCredential, sizeof(cbCredential));
        pbCredential += sizeof(cbCredential);

        //
        // Check remaining buffer size again
        //
        if ((pbCredential + HASH_DATA_SIZE) > pbSACEnd)
        {
            ASSERT_SECURITY_DBASE_CORRUPT();
            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
            goto ErrorExit;
        }

        //
        // Check remaining buffer size yet again
        //
        if ((pbCredential + cbCredential) > pbSACEnd)
        {
            ASSERT_SECURITY_DBASE_CORRUPT();
            hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
            goto ErrorExit;
        }

        //
        // The start of the credential refers to the credential identity.
        // Skip over this to refer to the encrypted bits. Copy the pbCredential
        // and cbCredential for this dwCredentialIndex so that it can be
        // manipulated without altering the original buffer.
        //
        cbEncryptedData = cbCredential - HASH_DATA_SIZE;
        pbEncryptedData = new BYTE[cbEncryptedData];
        CopyMemory(pbEncryptedData, pbCredential + HASH_DATA_SIZE, cbEncryptedData);

        //
        // The decryption process (CBC call) clobbers the key, so make a fresh
        // copy from the source each time through the loop
        //
        CopyMemory(&RC2KeyPreSysprepCopy, pRC2KeyPreSysprep, sizeof(RC2_KEY_INFO));

        //
        // Decrypt credential using the pre-sysprep key
        //
        //           *** Important ***
        //
        // The encrypted credentials passed are decrypted *in-place*.
        // Therefore, buffer content has been compromised;
        // plus, the decrypted data must be zeroed immediately
        // following decryption (even in a failure case).
        //
        hRes2 = DecryptCredentials(RC2KeyPreSysprepCopy, cbEncryptedData, pbEncryptedData, &jc);

        //
        // Don't leave the plain-text password on the heap.
        //
        SecureZeroMemory(pbEncryptedData, cbEncryptedData);

        if (SUCCEEDED(hRes2))
        {
            //
            // The encryption process (CBC call) clobbers the key, so make a fresh
            // copy from the source each time through the loop
            //
            CopyMemory(&RC2KeyPostSysprepCopy, pRC2KeyPostSysprep, sizeof(RC2_KEY_INFO));
    
            //
            // Encrypt credential using the post-sysprep key
            //
            hRes2 = EncryptCredentials(RC2KeyPostSysprepCopy,
                                    jc.wszAccount,
                                    jc.wszDomain,
                                    jc.wszPassword,
                                    NULL,
                                    &cbEncryptedData,
                                    &pbEncryptedData);
            //
            // Don't leave the plain-text password on the stack
            //
            ZERO_PASSWORD(jc.wszPassword);
            jc.ccPassword = 0;
    
            if (SUCCEEDED(hRes2))
            {
                //
                // Update the old encrypted credential with the newly encrypted credential
                //
                hr = SACUpdateCredential(cbEncryptedData,
                                         pbEncryptedData,
                                         cbCredential,
                                         pbCredential,
                                         pcbSAC,
                                         ppbSAC);
                if (FAILED(hr))
                {
                    goto ErrorExit;  // a failure to update leaves the SAC data
                                      // in an unknown state, so we need to bail
                }
            }
        }
        
        //
        // Clean up
        //
        delete pbEncryptedData;
        pbEncryptedData = NULL;
        
        //
        // Advance to next credential.
        //
        pbCredential += (HASH_DATA_SIZE + cbEncryptedData);
    }

    //
    // Still more integrity checking.
    // Did we reach the end of the buffer exactly when we thought we should?
    // If not, something is wrong somewhere.
    //
    if ((dwCredentialIndex == dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) != *pcbSAC) ||
        (dwCredentialIndex != dwCredentialCount) && ((DWORD)(pbCredential - *ppbSAC) > *pcbSAC))
    {
        //
        // The database appears to be truncated.
        //
        ASSERT_SECURITY_DBASE_CORRUPT();
        hr = SCHED_E_ACCOUNT_DBASE_CORRUPT;
        goto ErrorExit;
    }

ErrorExit:
    if (pbEncryptedData) LocalFree(pbEncryptedData);
    
    return(hr);
}

//+---------------------------------------------------------------------------
//
//  Function:   ConvertNetScheduleCredentialData
//
//  Synopsis:   Converts Net Schedule credential stored prior to sysprep so it may be decrypted after sysprep.
//              It does this by using the pre-sysprep key to decrypt the credential with it, and then
//              encrypting it using the new post-sysprep key.
//
//  Arguments:  pRC2KeyPreSysprep - pointer to presysprep key
//              pRC2KeyPostSysprep - pointer to postsysprep key
//
//  Returns:    HRESULT
//
//----------------------------------------------------------------------------
HRESULT ConvertNetScheduleCredentialData(RC2_KEY_INFO* pRC2KeyPreSysprep, RC2_KEY_INFO* pRC2KeyPostSysprep)
{
    BYTE* pbEncryptedData = NULL;
    DWORD cbEncryptedData = 0;
    
    //
    // Read the Net Schedule account from LSA
    //
    HRESULT hr = ReadLsaData(sizeof(WSZ_SANSC), WSZ_SANSC, &cbEncryptedData, &pbEncryptedData);
    if (FAILED(hr))
    {
        goto ErrorExit;
    }

    if (hr == S_FALSE || cbEncryptedData <= sizeof(DWORD))
    {
        //
        // The information was specified previously but has been reset since.
        // NOTE:  This will be the case if the value has been reset back to LocalSystem,
        // as it merely stores a dword = 0x00000000 in that case
        //

        //
        // Do nothing -- there is no data to convert
        //
    }
    else
    {
        //
        // The decryption process (CBC call) clobbers the key, so make a copy for use
        //
        RC2_KEY_INFO RC2KeyPreSysprepCopy;
        CopyMemory(&RC2KeyPreSysprepCopy, pRC2KeyPreSysprep, sizeof(RC2_KEY_INFO));
    
        JOB_CREDENTIALS jc;
        hr = DecryptCredentials(RC2KeyPreSysprepCopy,
                                cbEncryptedData,
                                pbEncryptedData,
                                &jc);
        
        SecureZeroMemory(&RC2KeyPreSysprepCopy, sizeof(RC2_KEY_INFO));
        
        if (FAILED(hr))
        {
            goto ErrorExit;
        }
        
        //
        // Don't leave the plain-text password on the heap.
        //
        SecureZeroMemory(pbEncryptedData, cbEncryptedData);

        //
        // The encryption process (CBC call) clobbers the key, so make a copy for use
        //
        RC2_KEY_INFO RC2KeyPostSysprepCopy;
        CopyMemory(&RC2KeyPostSysprepCopy, pRC2KeyPostSysprep, sizeof(RC2_KEY_INFO));

        //
        // Encrypt credential using the post-sysprep key
        //
        hr = EncryptCredentials(RC2KeyPostSysprepCopy,
                                jc.wszAccount,
                                jc.wszDomain,
                                jc.wszPassword,
                                NULL,
                                &cbEncryptedData,
                                &pbEncryptedData);
        
        SecureZeroMemory(&RC2KeyPostSysprepCopy, sizeof(RC2_KEY_INFO));

        if (FAILED(hr))
        {
            goto ErrorExit;
        }

        //
        // Don't leave the plain-text password on the stack
        //
        ZERO_PASSWORD(jc.wszPassword);

        hr = WriteLsaData(sizeof(WSZ_SANSC), WSZ_SANSC, cbEncryptedData, pbEncryptedData);
   }

ErrorExit:
    if (pbEncryptedData != NULL)
        LocalFree(pbEncryptedData);

    return(hr);
}
