//+---------------------------------------------------------------------------
//
//  Microsoft Windows NT Security
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       initpki.cpp
//
//  Contents:   migrates Bob Store to SPC store and adds root certificates
//
//  History:    03-Jun-97    kirtd    Created
//
//----------------------------------------------------------------------------

#include "global.hxx"
#include "cryptreg.h"
#include "..\mscat32\mscatprv.h"

HMODULE hModule = NULL;


//////////////////////////////////////////////////////////////////

#define INITPKI_HRESULT_FROM_WIN32(a) ((a >= 0x80000000) ? a : HRESULT_FROM_WIN32(a))


#define SHA1_HASH_LENGTH     20


#define wsz_ROOT_STORE      L"Root"
#define wsz_TRUST_STORE     L"Trust"
#define wsz_CA_STORE        L"CA"
#define wsz_TRUST_PUB_STORE L"TrustedPublisher"
#define wsz_DISALLOWED_STORE L"Disallowed"
static LPCWSTR rgpwszPredefinedEnterpriseStore[] = {
    wsz_ROOT_STORE,
    wsz_TRUST_STORE,
    wsz_CA_STORE,
    wsz_TRUST_PUB_STORE,
    wsz_DISALLOWED_STORE
};
#define NUM_PREDEFINED_ENTERPRISE_STORE \
    (sizeof(rgpwszPredefinedEnterpriseStore) / \
        sizeof(rgpwszPredefinedEnterpriseStore[0]))

void RegisterEnterpriseStores()
{
    DWORD i;

    for (i = 0; i < NUM_PREDEFINED_ENTERPRISE_STORE; i++) {
        CertRegisterSystemStore(
            rgpwszPredefinedEnterpriseStore[i],
            CERT_SYSTEM_STORE_LOCAL_MACHINE_ENTERPRISE,
            NULL,           // pSystemStoreInfo
            NULL            // pvReserved
            );
    }
}

void RemoveCert(HCERTSTORE hStore, BYTE *pThumbPrint)
{
    PCERT_CONTEXT   pCertContext;
    CRYPT_HASH_BLOB CryptHashBlob;

    CryptHashBlob.cbData    = SHA1_HASH_LENGTH;
    CryptHashBlob.pbData    = pThumbPrint;

    pCertContext = (PCERT_CONTEXT)CertFindCertificateInStore(  hStore,
                                                X509_ASN_ENCODING,
                                                0,
                                                CERT_FIND_SHA1_HASH,
                                                &CryptHashBlob,
                                                NULL);
    if (pCertContext)
    {
        CertDeleteCertificateFromStore(pCertContext);
    }
}

//
// if byte 0 is null, make sure to change while loop below!
//
BYTE CertRemoveList[][SHA1_HASH_LENGTH] =
{
    { 0x4B, 0x33, 0x8D, 0xCD, 0x50, 0x18, 0x10, 0xB9, 0x36, 0xA0,
      0x63, 0x61, 0x4C, 0x3C, 0xDD, 0x3F, 0xC2, 0xC4, 0x88, 0x55 },     // GTE Glue - '96

    { 0x56, 0xB0, 0x65, 0xA7, 0x4B, 0xDC, 0xE3, 0x7C, 0x96, 0xD3,
      0xBA, 0x69, 0x81, 0x08, 0x02, 0xD5, 0x87, 0x03, 0xC0, 0xBD },     // Verisign Comm Glue - '96

    { 0x13, 0x39, 0x72, 0xAA, 0x97, 0xD3, 0x65, 0xFB, 0x6A, 0x1D,
      0x47, 0xA5, 0xC7, 0x7A, 0x5C, 0x03, 0x94, 0xBD, 0xB9, 0xED },     // Verisign Indv Glue - '96

    { 0x69, 0xD0, 0x4F, 0xFB, 0x62, 0xE1, 0xC9, 0xAE, 0x30, 0x76,
      0x99, 0x2A, 0xE7, 0x46, 0xFD, 0x69, 0x08, 0x3A, 0xBD, 0xE9 },     // MS Root cert - '96

    { 0xA7, 0xD7, 0xD5, 0xFD, 0xBB, 0x26, 0xB4, 0x10, 0xC1, 0xD6,
      0x7A, 0xFB, 0xF5, 0xC9, 0x05, 0x39, 0x42, 0xDE, 0xE0, 0xEF },     // MS SGC Root Authority - '99

//    { 0xCC, 0x7E, 0xD0, 0x77, 0xF0, 0xF2, 0x92, 0x59, 0x5A, 0x81,
//      0x66, 0xB0, 0x17, 0x09, 0xE2, 0x0C, 0x08, 0x84, 0xA5, 0xF8 },     // verisign "Class1" - '97
//
//    { 0xD4, 0x73, 0x5D, 0x8A, 0x9A, 0xE5, 0xBC, 0x4B, 0x0A, 0x0D,
//      0xC2, 0x70, 0xD6, 0xA6, 0x25, 0x38, 0xA5, 0x87, 0xD3, 0x2F },     // verisign "timestamp" - '97
//
//    { 0x68, 0x8B, 0x6E, 0xB8, 0x07, 0xE8, 0xED, 0xA5, 0xC7, 0xB1,
//      0x7C, 0x43, 0x93, 0xD0, 0x79, 0x5F, 0x0F, 0xAE, 0x15, 0x5F },     // verisign "commercial" - '97
//
//    { 0xB1, 0x9D, 0xD0, 0x96, 0xDC, 0xD4, 0xE3, 0xE0, 0xFD, 0x67, 
//      0x68, 0x85, 0x50, 0x5A, 0x67, 0x2C, 0x43, 0x8D, 0x4E, 0x9C },     // verisign "individual" - '97


    { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }      // term

};

//+---------------------------------------------------------------------------
//
//  Function:   PurgeExpiringCertsFromStores
//
//  Synopsis:   blech!
//
//----------------------------------------------------------------------------
HRESULT PurgeExpiringCertsFromStores ()
{
    DWORD       cRemove;
    DWORD       cStores;
    HCERTSTORE  hStore = NULL;
    HKEY        hKey = NULL;
    char        *pszStores[] = { "SPC", "ROOT", "CU_ROOT", NULL };

    //
    //  HACKHACK!  no crypt32 UI about the root store.
    //
    if (RegCreateHKCUKeyExU(HKEY_CURRENT_USER, ROOT_STORE_REGPATH, 
                            0, NULL, 0, KEY_ALL_ACCESS, NULL, &hKey, NULL) != ERROR_SUCCESS)
    {
        hKey = NULL;
    }

    cStores = 0;

    while (pszStores[cStores])
    {
        if (strcmp(pszStores[cStores], "CU_ROOT") == 0)
        {
            if (hKey)
                hStore = CertOpenStore(CERT_STORE_PROV_REG, 0, NULL, 0, (LPVOID)hKey);
            else
                hStore = NULL;
        }
        else
        {
            hStore = CertOpenStore(CERT_STORE_PROV_SYSTEM_A, 0, NULL,
                                   CERT_SYSTEM_STORE_LOCAL_MACHINE |
                                   CERT_STORE_NO_CRYPT_RELEASE_FLAG,
                                   pszStores[cStores]);
        }

        if (hStore)
        {
            cRemove = 0;
            while (CertRemoveList[cRemove][0] != 0x00)
            {
                if (hStore)
                {
                    RemoveCert(hStore, &CertRemoveList[cRemove][0]);
                }

                cRemove++;
            }
            
            CertCloseStore(hStore, 0);
        }

        cStores++;
    }

    if (hKey)
        RegCloseKey(hKey);
    return( S_OK );
}

PCCERT_CONTEXT FindCertificateInOtherStore(
    IN HCERTSTORE hOtherStore,
    IN PCCERT_CONTEXT pCert
    )
{
    BYTE rgbHash[SHA1_HASH_LENGTH];
    CRYPT_DATA_BLOB HashBlob;

    HashBlob.pbData = rgbHash;
    HashBlob.cbData = SHA1_HASH_LENGTH;
    if (!CertGetCertificateContextProperty(
            pCert,
            CERT_SHA1_HASH_PROP_ID,
            rgbHash,
            &HashBlob.cbData
            ) || SHA1_HASH_LENGTH != HashBlob.cbData)
        return NULL;

    return CertFindCertificateInStore(
            hOtherStore,
            0,                  // dwCertEncodingType
            0,                  // dwFindFlags
            CERT_FIND_SHA1_HASH,
            (const void *) &HashBlob,
            NULL                //pPrevCertContext
            );
}

BOOL IsCertificateInOtherStore(
    IN HCERTSTORE hOtherStore,
    IN PCCERT_CONTEXT pCert
    )
{
    PCCERT_CONTEXT pOtherCert;

    if (pOtherCert = FindCertificateInOtherStore(hOtherStore, pCert)) {
        CertFreeCertificateContext(pOtherCert);
        return TRUE;
    } else
        return FALSE;
}

void DeleteCertificateFromOtherStore(
    IN HCERTSTORE hOtherStore,
    IN PCCERT_CONTEXT pCert
    )
{
    PCCERT_CONTEXT pOtherCert;

    if (pOtherCert = FindCertificateInOtherStore(hOtherStore, pCert))
        CertDeleteCertificateFromStore(pOtherCert);
}


//+-------------------------------------------------------------------------
//  Read a SignedData message consisting of certificates and
//  CRLs from memory and copy to the specified cert store.
//
//  Except for the SPC being loaded from memory, identical to SpcReadSpcFile.
//
//  For hLMStore != NULL: if the certificate or CRL already exists in the
//  LocalMachine store don't add it. Also if it exists in hCertstore,
//  delete it.
//--------------------------------------------------------------------------
HRESULT
SpcReadSpcFromMemory(
    IN BYTE *pbData,
    IN DWORD cbData,
    IN HCERTSTORE hCertStore,
    IN DWORD dwMsgAndCertEncodingType,
    IN DWORD dwFlags,
    IN OPTIONAL HCERTSTORE hLMStore
    )
{
    HRESULT hr = S_OK;
    HCERTSTORE hSpcStore = NULL;
    CRYPT_DATA_BLOB sSpcBlob;
    HCRYPTPROV hCryptProv = NULL;
    PCCERT_CONTEXT pCert = NULL;
    PCCRL_CONTEXT pCrl = NULL;

    if (!(hCertStore))
    {
        goto InvalidArg;
    }

    // Set the blob data.
    sSpcBlob.pbData = pbData;
    sSpcBlob.cbData = cbData;


    // Open up the spc store
    hSpcStore = CertOpenStore(CERT_STORE_PROV_SERIALIZED, //CERT_STORE_PROV_PKCS7,
                              X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                              hCryptProv,
                              CERT_STORE_NO_CRYPT_RELEASE_FLAG,
                              &sSpcBlob);
    if (!hSpcStore)
    {
        goto CertStoreError;
    }

    // Copy in the certificates from the caller.
    while (pCert = CertEnumCertificatesInStore(hSpcStore, pCert))
    {
        if (hLMStore && IsCertificateInOtherStore(hLMStore, pCert))
            // Certificate exists in LocalMachine. Delete it from
            // CurrentUser if it already exists there.
            DeleteCertificateFromOtherStore(hCertStore, pCert);
        else
            CertAddCertificateContextToStore(hCertStore,
                                             pCert,
                                             CERT_STORE_ADD_REPLACE_EXISTING,
                                             NULL);
    }

    while (pCrl = CertEnumCRLsInStore(hSpcStore, pCrl))
    {
        CertAddCRLContextToStore(hCertStore,
                                 pCrl,
                                 CERT_STORE_ADD_NEWER,
                                 NULL);

        if (hLMStore) {
            // Check if newer or same CRL exists in the hLMStore
            PCCRL_CONTEXT pLMCrl;

            pLMCrl = CertFindCRLInStore(
                hLMStore,
                pCrl->dwCertEncodingType,
                0,                          // dwFindFlags
                CRL_FIND_EXISTING,
                (const void *) pCrl,
                NULL                        // pPrevCrlContext
                );

            if (NULL != pLMCrl) {
                PCCRL_CONTEXT pCUCrl;

                pCUCrl = CertFindCRLInStore(
                    hCertStore,
                    pCrl->dwCertEncodingType,
                    0,                          // dwFindFlags
                    CRL_FIND_EXISTING,
                    (const void *) pCrl,
                    NULL                        // pPrevCrlContext
                    );

                if (NULL != pCUCrl) {
                    if (0 <= CompareFileTime(
                            &pLMCrl->pCrlInfo->ThisUpdate,
                            &pCUCrl->pCrlInfo->ThisUpdate
                            ))
                        CertDeleteCRLFromStore(pCUCrl);
                    else
                        CertFreeCRLContext(pCUCrl);
                }

                CertFreeCRLContext(pLMCrl);
            }
        }
    }
    

    CommonReturn:
        if (hSpcStore)
        {
            CertCloseStore(hSpcStore, 0);
        }
        return(hr);

    ErrorReturn:
        SetLastError((DWORD)hr);
        goto CommonReturn;

    SET_HRESULT_EX(DBG_SS, InvalidArg, E_INVALIDARG);
    SET_HRESULT_EX(DBG_SS, CertStoreError, GetLastError());
}

// For nonNULL pszLMStoreName, doesn't add certificates if already in
// pszLMStoreName store.
HRESULT AddCertificates2(
    IN LPCSTR pszStoreName,
    IN OPTIONAL LPCSTR pszLMStoreName,
    IN DWORD dwOpenStoreFlags,
    IN LPCSTR pszResourceName,
    IN LPCSTR pszResourceType
    )
{
    HRESULT    hr = S_OK;
    HCERTSTORE hCertStore = NULL;
    HCERTSTORE hLMStore = NULL;
    LPBYTE     pb = NULL;
    DWORD      cb;
    HRSRC      hrsrc;

    hCertStore = CertOpenStore(
        CERT_STORE_PROV_SYSTEM_REGISTRY_A,
        0,                                  // dwEncodingType
        NULL,                               // hCryptProv
        dwOpenStoreFlags |
            CERT_SYSTEM_STORE_UNPROTECTED_FLAG,
        (const void *) pszStoreName
        );

    if (!(hCertStore))
    {
        return(GetLastError());
    }

    if (NULL != pszLMStoreName)
    {
        hLMStore = CertOpenStore(
            CERT_STORE_PROV_SYSTEM_REGISTRY_A,
            0,                                  // dwEncodingType
            NULL,                               // hCryptProv
            CERT_SYSTEM_STORE_LOCAL_MACHINE |
                CERT_STORE_READONLY_FLAG |
                CERT_SYSTEM_STORE_UNPROTECTED_FLAG,
            (const void *) pszLMStoreName
            );
    }


    hrsrc = FindResourceA(hModule, pszResourceName, pszResourceType);
    if ( hrsrc != NULL )
    {
        HGLOBAL hglobRes;

        hglobRes = LoadResource(hModule, hrsrc);
        if ( hglobRes != NULL )
        {
            ULONG cbRes;
            BYTE* pbRes;

            cbRes = SizeofResource(hModule, hrsrc);
            pbRes = (BYTE *)LockResource(hglobRes);

            hr = SpcReadSpcFromMemory(  pbRes,
                                        cbRes,
                                        hCertStore,
                                        PKCS_7_ASN_ENCODING | X509_ASN_ENCODING,
                                        0,
                                        hLMStore);

            UnlockResource(hglobRes);
            FreeResource(hglobRes);

        }
        else
        {
            hr = HRESULT_FROM_WIN32(GetLastError());
        }
    }
    else
    {
        hr = HRESULT_FROM_WIN32(GetLastError());
    }

    if ( hCertStore != NULL )
    {
        CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
    }

    if ( hLMStore != NULL )
    {
        CertCloseStore(hLMStore, CERT_CLOSE_STORE_FORCE_FLAG);
    }

    return( hr );
}


// For non-LocalMachine store, doesn't add certificates if already in
// corresponding LocalMachine store.
HRESULT AddCertificates(
    IN LPCSTR pszStoreName,
    IN DWORD dwOpenStoreFlags,
    IN LPCSTR pszResourceName,
    IN LPCSTR pszResourceType
    )
{
    LPCSTR pszLMStoreName;

    if (CERT_SYSTEM_STORE_LOCAL_MACHINE !=
            (dwOpenStoreFlags & CERT_SYSTEM_STORE_LOCATION_MASK))
        pszLMStoreName = pszStoreName;
    else
        pszLMStoreName = NULL;

    return AddCertificates2(
        pszStoreName,
        pszLMStoreName,
        dwOpenStoreFlags,
        pszResourceName,
        pszResourceType
        );
}

HRESULT AddCurrentUserCACertificates()
{
    return AddCertificates(
        "CA",
        CERT_SYSTEM_STORE_CURRENT_USER,
        MAKEINTRESOURCE(IDR_CAS),
        "CAS"
        );
}
HRESULT AddLocalMachineCACertificates()
{
    return AddCertificates(
        "CA",
        CERT_SYSTEM_STORE_LOCAL_MACHINE,
        MAKEINTRESOURCE(IDR_CAS),
        "CAS"
        );
}

HRESULT AddCurrentUserDisallowedCertificates()
{
    return AddCertificates(
        "Disallowed",
        CERT_SYSTEM_STORE_CURRENT_USER,
        MAKEINTRESOURCE(IDR_DISALLOW),
        "DISALLOW"
        );
}
HRESULT AddLocalMachineDisallowedCertificates()
{
    return AddCertificates(
        "Disallowed",
        CERT_SYSTEM_STORE_LOCAL_MACHINE,
        MAKEINTRESOURCE(IDR_DISALLOW),
        "DISALLOW"
        );
}

HRESULT AddCurrentUserRootCertificates()
{
    HRESULT hr;
    HRESULT hr2;

    hr = AddCertificates(
        "Root",
        CERT_SYSTEM_STORE_CURRENT_USER,
        MAKEINTRESOURCE(IDR_ROOTS),
        "ROOTS"
        );

    hr2 = AddCertificates2(
        "Root",
        "AuthRoot",                     // check if already in LM AuthRoot store
        CERT_SYSTEM_STORE_CURRENT_USER,
        MAKEINTRESOURCE(IDR_AUTHROOTS),
        "AUTHROOTS"
        );

    if (hr == ERROR_SUCCESS)
        hr = hr2;

    return hr;
}

HRESULT AddLocalMachineRootCertificates()
{
    HRESULT hr;
    HRESULT hr2;
    HRESULT hr3;
    HRESULT hr4;

    hr = AddCertificates(
        "AuthRoot",
        CERT_SYSTEM_STORE_LOCAL_MACHINE,
        MAKEINTRESOURCE(IDR_AUTHROOTS),
        "AUTHROOTS"
        );

    // Remove all the AuthRoots from the "Root" store
    hr2 = AddCertificates2(
        "Root",
        "AuthRoot",                     // check if already in LM AuthRoot store
        CERT_SYSTEM_STORE_LOCAL_MACHINE,
        MAKEINTRESOURCE(IDR_AUTHROOTS),
        "AUTHROOTS"
        );

    hr3 = AddCertificates(
        "Root",
        CERT_SYSTEM_STORE_LOCAL_MACHINE,
        MAKEINTRESOURCE(IDR_ROOTS),
        "ROOTS"
        );

    // Remove all the Roots from the "AuthRoot" store
    hr4 = AddCertificates2(
        "AuthRoot",
        "Root",                     // check if already in LM Root store
        CERT_SYSTEM_STORE_LOCAL_MACHINE,
        MAKEINTRESOURCE(IDR_ROOTS),
        "ROOTS"
        );

    if (hr == ERROR_SUCCESS)
        hr = hr2;
    if (hr == ERROR_SUCCESS)
        hr = hr3;
    if (hr == ERROR_SUCCESS)
        hr = hr4;

    return hr;
}

void CreateKey(
    IN HKEY hKey,
    IN LPCWSTR pwszSubKey
    )
{
    LONG err;
    DWORD dwDisposition;
    HKEY hSubKey;

    if (ERROR_SUCCESS != (err = RegCreateKeyExU(
            hKey,
            pwszSubKey,
            0,                      // dwReserved
            NULL,                   // lpClass
            REG_OPTION_NON_VOLATILE,
            MAXIMUM_ALLOWED,
            NULL,                   // lpSecurityAttributes
            &hSubKey,
            &dwDisposition))) {
#if DBG
        DbgPrintf(DBG_SS_CRYPT32,
            "RegCreateKeyEx(%S) returned error: %d 0x%x\n",
            pwszSubKey, err, err);
#endif
    } else {
        RegCloseKey(hSubKey);
    }
}

// Loop through the certificates in the "My" store and get their
// KeyIdentifier property. If the certificate also has a KEY_PROV_INFO,
// then, this will cause its KeyIdentifier to be created.
void UpdateMyKeyIdentifiers(
    IN DWORD dwOpenStoreFlags
    )
{
    HCERTSTORE hStore;
    if (hStore = CertOpenStore(
            CERT_STORE_PROV_SYSTEM_A,
            0,                                  // dwEncodingType
            NULL,                               // hCryptProv
            dwOpenStoreFlags | CERT_STORE_ENUM_ARCHIVED_FLAG,
            (const void *) "My"
            )) {
        PCCERT_CONTEXT pCert = NULL;
        while (pCert = CertEnumCertificatesInStore(hStore, pCert)) {
            DWORD cbData = 0;

            // Dummy get to force the KeyIdentifer property to be created
            // if it doesn't already exist.
            CertGetCertificateContextProperty(
                pCert,
                CERT_KEY_IDENTIFIER_PROP_ID,
                NULL,                           // pvData
                &cbData
                );
        }

        CertCloseStore(hStore, 0);
    }
}

//---------------------------------------------------------------------------
//	 Set Software Publisher State Key Value
//	 
//---------------------------------------------------------------------------
BOOL SetSoftPubKey(DWORD dwMask, BOOL fOn)
{
    DWORD	dwState=0;
    DWORD	dwDisposition=0;
    DWORD	dwType=0;
    DWORD	cbData=0;
    LPWSTR  wszState=REGNAME_WINTRUST_POLICY_FLAGS;
    BOOL    fResult=FALSE;

    HKEY	hKey=NULL;


    // Set the State in the registry
    if (ERROR_SUCCESS != RegCreateKeyExU(
            HKEY_CURRENT_USER,
            REGPATH_WINTRUST_POLICY_FLAGS,
            0,          // dwReserved
            NULL,       // lpszClass
            REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS,
            NULL,       // lpSecurityAttributes
            &hKey,
            &dwDisposition))
        goto RegErr;


    dwState = 0;
    cbData = sizeof(dwState);

    if(ERROR_SUCCESS != RegQueryValueExU
	(   hKey,
        wszState,
        NULL,          // lpReserved
        &dwType,
        (BYTE *) &dwState,
        &cbData
        ))
        goto RegErr;

    if ((dwType != REG_DWORD) && (dwType != REG_BINARY))
        goto UnexpectedErr;

    switch(dwMask) 
    {
        case WTPF_IGNOREREVOCATIONONTS:
        case WTPF_IGNOREREVOKATION:
        case WTPF_IGNOREEXPIRATION:
            // Revocation and expiration are a double negative so the bit set
            // means revocation and expriation checking is off.
            fOn = !fOn;
            break;
        default:
            break;
    };

    if (fOn)
        dwState |= dwMask;
    else
        dwState &= ~dwMask;

    if(ERROR_SUCCESS != RegSetValueExU(
        hKey,
        wszState,
        0,          // dwReserved
        REG_DWORD,
        (BYTE *) &dwState,
        sizeof(dwState)
        ))
        goto SetValueErr;


    fResult=TRUE;

CommonReturn:
    if(hKey)
        RegCloseKey(hKey);

	return fResult;

ErrorReturn:

	goto CommonReturn;

TRACE_ERROR(RegErr);
SET_ERROR(UnexpectedErr, E_UNEXPECTED);
TRACE_ERROR(SetValueErr);

}


//+---------------------------------------------------------------------------
//
//  Function:   GetNextRegToken
//
//  Synopsis:   
//              Find the next token with space as the deliminator
//----------------------------------------------------------------------------
LPWSTR  GetNextRegToken(LPWSTR  pwsz, LPWSTR  pwszPreToken, BOOL  *pfEnd)
{
    LPWSTR  pwszStart=NULL;
    LPWSTR  pwszSearch=NULL;
    
    if(NULL == pwsz)
        return NULL;

    if(TRUE == (*pfEnd))
        return NULL;

    pwszStart=pwsz;

    if(pwszPreToken)
        pwszStart=pwszPreToken + wcslen(pwszPreToken) + 1;

    //skip the spaces
    while((*pwszStart)==L' ')
        pwszStart++;

    //check for NULL
    if(*pwszStart==L'\0')
        return NULL;

    pwszSearch=pwszStart;

    while(((*pwszSearch) != L'\0') && ((*pwszSearch) !=L' ') )
        pwszSearch++;

    if(*pwszSearch == L'\0')
    {
        *pfEnd=TRUE;
        return pwszStart;
    }

    *pwszSearch=L'\0';

    *pfEnd=FALSE;

    return pwszStart;
}

//+---------------------------------------------------------------------------
//
//  Function:   InitRegistryValue
//
//  Synopsis:   This function replace SetReg.exe.  The expected
//              command line would be: 1 TRUE 3 FALSE 9 TRUE 4 FALSE ....
//
//----------------------------------------------------------------------------
HRESULT InitRegistryValue(LPWSTR pwszCommand)
{
    HRESULT            hr= E_FAIL;
    DWORD              SoftPubFlags[] = 
                            {
                            WTPF_TRUSTTEST | WTPF_TESTCANBEVALID,
                            WTPF_IGNOREEXPIRATION,
                            WTPF_IGNOREREVOKATION,
                            WTPF_OFFLINEOK_IND,
                            WTPF_OFFLINEOK_COM,
                            WTPF_OFFLINEOKNBU_IND,
                            WTPF_OFFLINEOKNBU_COM,
                            WTPF_VERIFY_V1_OFF,
                            WTPF_IGNOREREVOCATIONONTS,
                            WTPF_ALLOWONLYPERTRUST
                            };


    LPWSTR              pwszNextToken=NULL;
    int                 iIndex=-1;
    BOOL                fOn=FALSE;
    int                 cFlags=sizeof(SoftPubFlags)/sizeof(SoftPubFlags[0]);
    DWORD               cParam=0;
    LPWSTR              pwszCopy=NULL;
    BOOL                fPassThrough=FALSE;

    //make a copy of the command line since we will change it
    pwszCopy=(LPWSTR)LocalAlloc(LPTR, (1+wcslen(pwszCommand)) * sizeof(WCHAR));

    if(NULL== pwszCopy)
        goto MemoryErr;

    wcscpy(pwszCopy, pwszCommand);

    while(pwszNextToken=GetNextRegToken(pwszCopy, pwszNextToken, &fPassThrough))
    {
        
        if(-1 == iIndex)
        {
            iIndex=_wtoi(pwszNextToken);

            if((iIndex <= 0) || (iIndex > cFlags))
                goto InvalidArgErr;

            cParam++;
        }
        else
        {
            
            if(0 == _wcsicmp(pwszNextToken, L"true"))
                fOn=TRUE;
            else
            {
                if(0 == _wcsicmp(pwszNextToken, L"false"))
                    fOn=FALSE;
                else
                    goto InvalidArgErr;
            } 

            cParam++;

            //set the registry value
            if(!SetSoftPubKey(SoftPubFlags[iIndex-1], 
                              fOn))
            {
                hr=INITPKI_HRESULT_FROM_WIN32(GetLastError());
                goto SetKeyErr;
            }

            //reset the value for dwIndex
            iIndex=-1;
        }
    }

    //we have to have even number of parameters
    if( (0 != (cParam %2)) || (0 == cParam))
        goto InvalidArgErr;

    hr=S_OK;

CommonReturn:

    if(pwszCopy)
        LocalFree((HLOCAL)pwszCopy);

	return hr;

ErrorReturn:

	goto CommonReturn;

SET_ERROR(InvalidArgErr, E_INVALIDARG);
SET_ERROR_VAR(SetKeyErr, hr);
SET_ERROR(MemoryErr, E_OUTOFMEMORY);
}


//+---------------------------------------------------------------------------
//
//  Function:   DllMain
//
//  Synopsis:
//
//----------------------------------------------------------------------------
BOOL WINAPI DllMain(HMODULE hInstDLL, DWORD fdwReason, LPVOID lpvReserved)
{
    switch ( fdwReason )
    {
    case DLL_PROCESS_ATTACH:
         hModule = hInstDLL;
         break;
    }

    return( TRUE );
}

//+---------------------------------------------------------------------------
//
//  Function:   DllInstall
//
//  Synopsis:   dll installation entry point
//
//----------------------------------------------------------------------------
STDAPI DllInstall (BOOL fRegister, LPCSTR pszCommand)
{
    HRESULT hr = S_OK;
    HRESULT hr2;
    LPWSTR  pwszCommand=NULL;

    if ( fRegister == FALSE )
    {
        return( E_NOTIMPL );
    }

    switch ( *pszCommand )
    {
        //letter S stands for setreg input parameters
        //the command line should look like following:
        //S 1 TRUE 2 FALSE 3 FALSE ...
        //pszCommand is ACTUALLY LPWSTR for BOTH
        //NT5, NT4 and Win95.
        case 'S':
        case 's':
                pwszCommand=(LPWSTR)pszCommand;

                if(wcslen(pwszCommand) <= 2)
                {
                    hr=E_INVALIDARG;
                }
                else
                {
                    hr=InitRegistryValue((LPWSTR)(&(pwszCommand[1])));
                }

            break;
        case 'M':
        case 'm':
            MoveCertificates( TRUE );
            PurgeExpiringCertsFromStores();
            break;
        
        case 'U':
        case 'u':
            _AdjustPolicyFlags(psPolicySettings);

            PurgeExpiringCertsFromStores();

            // Ensure we have a registry entry for the Group Policy
            // SystemCertificates. On NT 4.0 or Win98, we emulate NT 5.0 GPT
            // notification by doing a RegNotifyChangeKeyValue on this
            // registry key.
            CreateKey(HKEY_CURRENT_USER, GROUP_POLICY_STORE_REGPATH);

            // Before adding to CurrentUser, will check if the root or CA
            // already exists in LocalMachine. If it exists in
            // LocalMachine and also exists in CurrentUser, will delete it
            // from CurrentUser instead of adding.
            hr = AddCurrentUserRootCertificates();
            hr2 = AddCurrentUserCACertificates();
            if (hr == ERROR_SUCCESS)
                hr = hr2;
            hr2 = AddCurrentUserDisallowedCertificates();
            if (hr == ERROR_SUCCESS)
                hr = hr2;

            // Protect the CurrentUser roots and purge any existing
            // protected CurrentUser roots also in LocalMachine
            //
            // Note, once the roots are protected, all subsequent adds are
            // done by a special service executing with System privileges.
            // This special service does secure attention sequence (SAS) UI
            // before doing the add.
            //
            // Note, subsequent purges are exempt from UI. ie, this function
            // doesn't do any SAS UI.
            I_CertProtectFunction(
                CERT_PROT_PURGE_LM_ROOTS_FUNC_ID,
                0,                              // dwFlags
                NULL,                           // pwszIn
                NULL,                           // pbIn
                0,                              // cbIn
                NULL,                           // ppbOut
                NULL                            // pcbOut
                );

            UpdateMyKeyIdentifiers(CERT_SYSTEM_STORE_CURRENT_USER);
            break;
        
        case 'B':
        case 'b':
        case 'R':
        case 'r':
        case 'A':
        case 'a':
            // Initialize HKLM registry locations used by PKI to
            // only give Everyone KEY_READ access. Also gives the
            // IEDirtyFlags registry key KEY_SET_VALUE access for
            // Everyone.
            InitializeHKLMAcls();

            // Ensure we have a registry entry for the IEDirtyFlags
            // This key should have already been created
            // by InitializeHKLMAcls() for NT. Ensure its also there
            // for Win95 and Win98
            CreateKey(HKEY_LOCAL_MACHINE, CERT_IE_DIRTY_FLAGS_REGPATH);

            MoveCertificates(TRUE);
            PurgeExpiringCertsFromStores();

            // Ensure we have a registry entry for the Group Policy
            // SystemCertificates. On NT 4.0 or Win98, we emulate NT 5.0 GPT
            // notification by doing a RegNotifyChangeKeyValue on this
            // registry key.
            CreateKey(HKEY_LOCAL_MACHINE, GROUP_POLICY_STORE_REGPATH);
            CreateKey(HKEY_CURRENT_USER, GROUP_POLICY_STORE_REGPATH);

            // Ensure we have existing predefined stores for the LocalMachine
            // Enterprise system stores. These stores are periodically updated
            // from the DS by a system service. RegNotifyChangeKeyValue is
            // used to signal clients about Enterprise store changes.
            RegisterEnterpriseStores();

            // Our goal is to get the roots and CAs into LocalMachine.
            // Note previously, they were only copied to CurrentUser.
            AddLocalMachineRootCertificates();
            AddLocalMachineCACertificates();
            AddLocalMachineDisallowedCertificates();

            // If the above adds to LocalMachine failed, then, add
            // to CurrentUser.
            //
            // Before adding to CurrentUser, will check if the root or CA
            // already exists in LocalMachine. If it exists in
            // LocalMachine and also exists in CurrentUser, will delete it
            // from CurrentUser instead of adding.
            hr = AddCurrentUserRootCertificates();
            hr2 = AddCurrentUserCACertificates();
            if (hr == ERROR_SUCCESS)
                hr = hr2;
            hr2 = AddCurrentUserDisallowedCertificates();
            if (hr == ERROR_SUCCESS)
                hr = hr2;

            // Protect the CurrentUser roots and purge any existing
            // protected CurrentUser roots also in LocalMachine
            //
            // Note, once the roots are protected, all subsequent adds are
            // done by a special service executing with System privileges.
            // This special service does secure attention sequence (SAS) UI
            // before doing the add.
            //
            // Note, subsequent purges are exempt from UI. ie, this function
            // doesn't do any SAS UI.
            I_CertProtectFunction(
                CERT_PROT_PURGE_LM_ROOTS_FUNC_ID,
                0,                              // dwFlags
                NULL,                           // pwszIn
                NULL,                           // pbIn
                0,                              // cbIn
                NULL,                           // ppbOut
                NULL                            // pcbOut
                );

            UpdateMyKeyIdentifiers(CERT_SYSTEM_STORE_CURRENT_USER);
            UpdateMyKeyIdentifiers(CERT_SYSTEM_STORE_LOCAL_MACHINE);

            CleanupRegistry();
            hr2 = RegisterCryptoDlls(TRUE);
            if (hr == ERROR_SUCCESS)
                hr = hr2;

            if (!I_CryptCatAdminMigrateToNewCatDB())
            {
                hr2 = HRESULT_FROM_WIN32(GetLastError());
            }
            else
            {
                hr2 = ERROR_SUCCESS;
            }
            if (hr == ERROR_SUCCESS)
                hr = hr2;

            break;

        case 'C':
        case 'c':
            // Win9x migration post setup cleanup. Remove any migrated
            // roots that exist in the AuthRoot store.
            AddLocalMachineRootCertificates();
            AddCurrentUserRootCertificates();
            break;

        case 'Z':
        case 'z':

            //
            // This is for componentization install
            //

            pwszCommand=(LPWSTR)pszCommand;
            
            if (_wcsicmp(pwszCommand, L"z CoreCertificateServices") == 0)
            {
                InitializeHKLMAcls();   
                AddLocalMachineRootCertificates();
                AddLocalMachineCACertificates();
                AddLocalMachineDisallowedCertificates();

                if (!_LoadAndRegister("wintrust.dll", FALSE) ||
                    !_LoadAndRegister("mssign32.dll", FALSE) ||
                    !_LoadAndRegister("xenroll.dll", FALSE)  ||
                    !_AdjustPolicyFlags(psPolicySettings))
                {
                    hr = S_FALSE;                                           
                }

                RegisterWinlogonExtension("crypt32chain", "crypt32.dll",
                    "ChainWlxLogoffEvent");
                RegisterWinlogonExtension("cryptnet", "cryptnet.dll",
                    "CryptnetWlxLogoffEvent");
            
                RegisterCrypt32EventSource();
            }
            else if (_wcsicmp(pwszCommand, L"z CertificateUIServices") == 0)
            {
                if (!_LoadAndRegister("cryptui.dll", FALSE))
                {
                    hr = S_FALSE;
                }
            }
            else if (_wcsicmp(pwszCommand, L"z CryptographicNetworkServices") == 0)
            {
                if (!_LoadAndRegister("cryptnet.dll", FALSE))
                {
                    hr = S_FALSE;
                }
            }
            else if (_wcsicmp(pwszCommand, L"z CertificateUIExtensions") == 0)
            {
                if (!_LoadAndRegister("cryptext.dll", FALSE))
                {
                    hr = S_FALSE;
                }
            }
            else
            {
                hr = E_INVALIDARG;
            }

            break;

        default:
            hr = E_INVALIDARG;
    }

    return( hr );
}

STDAPI DllRegisterServer(void)
{
    return(DllInstall(TRUE, "A"));
}

STDAPI DllUnregisterServer(void)
{
    return(UnregisterCryptoDlls());
}

BOOL WINAPI InitializePKI(void)
{
    if (RegisterCryptoDlls(TRUE) != S_OK)
    {
        return(FALSE);
    }

    return(TRUE);
}