//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1997 - 1999
//
//  File:       mvcerts.cpp
//
//--------------------------------------------------------------------------


#include    "global.hxx"

static HRESULT HError ()
{
    DWORD   dw = GetLastError ();
    HRESULT hr;
    if ( dw <= (DWORD) 0xFFFF )
        hr = HRESULT_FROM_WIN32 ( dw );
    else
        hr = dw;

    if ( ! FAILED ( hr ) )
    {
        // somebody failed a call without properly setting an error condition

        hr = E_UNEXPECTED;
    }
    return hr;
}


//
// The root of the certificate store that we manage.
//
#define SZIE30CERTROOT       "Software\\Microsoft\\Cryptography\\CertificateStore"
#define SZIE30CERTPARENT     "Software\\Microsoft\\Cryptography"
#define SZIE30CERTSTORE      "CertificateStore"
#define SZIE30CERTBUCKET     "Certificates"
#define SZIE30INDEXISSUER    "IndexByIssuerName"
#define SZIE30INDEXISSUERSER "IndexByIssuerNameAndSerialNumber"
#define SZIE30INDEXSUBJECT   "IndexBySubjectName"
#define SZIE30INDEXKEY       "IndexBySubjectPublicKey"
#define SZIE30CERTCLIENTAUTH "Software\\Microsoft\\Cryptography\\PersonalCertificates\\ClientAuth"
#define SZIE30TAGS           "CertificateTags"
#define SZIE30AUXINFO        "CertificateAuxiliaryInfo"
#define IE30CONVERTEDSTORE   "My"


HRESULT PurgeDuplicateCertificate(HCERTSTORE hStore, PCCERT_CONTEXT pCert)
{
    HRESULT hr = S_OK;
    PCCERT_CONTEXT pExistingCert = NULL;
    BOOL fRes = FALSE;

    // Check for existing certificates.
    pExistingCert = CertGetSubjectCertificateFromStore(hStore,
                                                       X509_ASN_ENCODING,
                                                       pCert->pCertInfo);
    if (pExistingCert) 
    {
        if (CompareFileTime(&pExistingCert->pCertInfo->NotBefore,
                            &pCert->pCertInfo->NotBefore) <= 0) {

            fRes = CertDeleteCertificateFromStore(pExistingCert);  // Delete existing
            pExistingCert = NULL;

            if (!(fRes))
            {
                goto CertDupError;
            }
        }
        else 
        {
            hr = S_FALSE;
        }
    }

    CommonReturn:
        if (pExistingCert)
        {
            CertFreeCertificateContext(pExistingCert);
        }
        return hr;


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

    SET_HRESULT_EX(DBG_SS, CertDupError, GetLastError());
}

HRESULT MoveSpcCerts(BOOL fDelete, HCERTSTORE hStore)
// Check for and copy any existing certificates stored in Bob's
// certificate store. Also, delete Bob's certificate keys from the registry.
{
    HRESULT hr = S_OK;
    LONG Status;
    HKEY hKeyRoot = NULL;
    HKEY hKeyParent = NULL;
    HKEY hKeyBucket = NULL;
    LPSTR pszName = NULL;
    BYTE *pbData = NULL;

    PKITRY {
        if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                          SZIE30CERTROOT,
                                          0,                  // dwReserved
                                          KEY_READ,
                                          &hKeyRoot))
            PKITHROW(S_OK);

        // Copy any existing certificates
        if (ERROR_SUCCESS != RegOpenKeyEx(hKeyRoot,
                                          SZIE30CERTBUCKET,
                                          0,                  // dwReserved
                                          KEY_READ,
                                          &hKeyBucket))
            PKITHROW(HError());

        DWORD   cValues, cchMaxName, cbMaxData;
        // see how many and how big the registry is
        if (ERROR_SUCCESS != RegQueryInfoKey(hKeyBucket,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             &cValues,
                                             &cchMaxName,
                                             &cbMaxData,
                                             NULL,
                                             NULL
                                             ))
            PKITHROW(HError());

        // allocate the memory needed to read the reg
        pszName = (LPSTR) LocalAlloc(LMEM_ZEROINIT, cchMaxName + 1);
        pbData = (BYTE *) LocalAlloc(LMEM_ZEROINIT, cbMaxData);
        if (NULL == pszName  || NULL == pbData)
            PKITHROW(E_OUTOFMEMORY);

        // enum the registry getting certs
        for (DWORD i = 0; i < cValues; i++ ) {
            DWORD dwType;
            DWORD cchName = cchMaxName + 1;
            DWORD cbData = cbMaxData;
            PCCERT_CONTEXT pCert = NULL;

            if (ERROR_SUCCESS != RegEnumValueA(hKeyBucket,
                                               i,
                                               pszName,
                                               &cchName,
                                               NULL,
                                               &dwType,
                                               pbData,
                                               &cbData)) {
                if (SUCCEEDED(hr))
                    hr = HError();
            } else if (cchName) {
                if((pCert = CertCreateCertificateContext(X509_ASN_ENCODING,
                                                         pbData,
                                                         cbData)) == NULL) {
                    hr = HError();
                }
                else {
                    HRESULT hr2 = PurgeDuplicateCertificate(hStore, pCert);
                    if(hr2 == S_OK) {
                        if(!CertAddCertificateContextToStore(hStore,
                                                             pCert,
                                                             CERT_STORE_ADD_USE_EXISTING,
                                                             NULL       // ppStoreContext
                                                             )) {
                            /*MessageBox(NULL, "Copy Certificate Failed", NULL,
                                       MB_OK);*/
                            if(SUCCEEDED(hr))
                                hr = HError();
                        }
                    }
                }
                if(pCert)
                    CertFreeCertificateContext(pCert);
                /*
                  if (!CertAddEncodedCertificateToStore(hStore,
                                                      X509_ASN_ENCODING,
                                                      pbData,
                                                      cbData,
                                                      CERT_STORE_ADD_USE_EXISTING,
                                                      NULL)) {               // ppCertContext
                    MessageBox(NULL, "Copy Certificate Failed", NULL,
                               MB_OK);
                               if (SUCCEEDED(hr))
                        hr = HError();
                        */
            }
        }
    }
    PKICATCH(err) {
        hr = err.pkiError;
    } PKIEND;

    if (pszName)
        LocalFree(pszName);
    if (pbData)
        LocalFree(pbData);
    if (hKeyBucket)
        RegCloseKey(hKeyBucket);
    if (hKeyRoot)
        RegCloseKey(hKeyRoot);


    if(SUCCEEDED(hr) && fDelete) {

        Status = ERROR_SUCCESS;
        while (Status == ERROR_SUCCESS) {
            // Re-open registry with write/delete access
            if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                              SZIE30CERTROOT,
                                              0,          // dwReserved
                                              KEY_ALL_ACCESS,
                                              &hKeyRoot))
                return HError();

            // Delete all of the store's subkeys including the certificates
            CHAR szSubKey[MAX_PATH+1];
            if (ERROR_SUCCESS == (Status = RegEnumKey(hKeyRoot,
                                                      0,          // iSubKey
                                                      szSubKey,
                                                      MAX_PATH + 1
                                                      )))
                Status = RegDeleteKey(hKeyRoot, szSubKey);
            RegCloseKey(hKeyRoot);
        }

        // Open the store's parent registry so we can delete the store
        if (ERROR_SUCCESS != RegOpenKeyEx(HKEY_LOCAL_MACHINE,
                                          SZIE30CERTPARENT,
                                          0,          // dwReserved
                                          KEY_ALL_ACCESS,
                                          &hKeyParent))
            return HError();

        if (ERROR_SUCCESS != RegDeleteKey(hKeyParent, SZIE30CERTSTORE))
            hr = HError();

        RegCloseKey(hKeyParent);
    }

    return hr;
}

BOOL TestIE30Store(HKEY hRegRoot, LPCSTR psLoc)
{
    HRESULT hr = S_FALSE;
    HKEY hKeyRoot   = NULL;
    HKEY hKeyBucket = NULL;
    char pbValueName[MAX_PATH];
    DWORD cbValueName = MAX_PATH;
    DWORD cSubKeys;
    DWORD dwType;

    // __asm int 3

    PKITRY {
        if (ERROR_SUCCESS != RegOpenKeyExA(hRegRoot,
                                           psLoc,
                                           0,                  // dwReserved
                                           KEY_READ,
                                           &hKeyRoot))
            PKITHROW(S_FALSE);

        if (ERROR_SUCCESS != RegOpenKeyExA(hKeyRoot,
                                           SZIE30CERTBUCKET,
                                           0,                  // dwReserved
                                           KEY_READ,
                                           &hKeyBucket))
            PKITHROW(S_FALSE);

        DWORD   cValues, cchMaxName, cbMaxData;
        // see how many and how big the registry is
        if (ERROR_SUCCESS != RegQueryInfoKey(hKeyBucket,
                                             NULL,  // lpszClasss
                                             NULL,  // lpcchClass
                                             NULL,  // lpdwReserved
                                             &cSubKeys,
                                             NULL,  // lpcchMaxSubkey
                                             NULL,  // lpcchMaxClass
                                             &cValues,
                                             &cchMaxName,
                                             &cbMaxData,
                                             NULL,
                                             NULL
                                             ))
            PKITHROW(HError());

        if(cchMaxName < 40 && cSubKeys == 0)
            hr = S_OK;
    }
    PKICATCH(err) {
        hr = err.pkiError;
    } PKIEND

    if(hKeyRoot != NULL)
        RegCloseKey(hKeyRoot);
    if(hKeyBucket != NULL)
        RegCloseKey(hKeyBucket);
    return hr == S_OK ? TRUE : FALSE;
}


HRESULT TransferIE30Certificates(HKEY hRegRoot, LPCSTR psLoc, HCERTSTORE hStore, BOOL fDelete)
// Check for and copy any existing certificates stored in Bob's
// certificate store.
{
    HRESULT hr = S_OK;
    LONG Status;
    HKEY hKeyRoot   = NULL;
    HKEY hKeyBucket = NULL;
    HKEY hKeyTags   = NULL;
    HKEY hKeyAux    = NULL;

    if (ERROR_SUCCESS != RegOpenKeyExA(hRegRoot,
                                       psLoc,
                                       0,                  // dwReserved
                                       KEY_READ,
                                       &hKeyRoot
                                       ))
        return S_OK;

        // Copy any existing certificates
    if (ERROR_SUCCESS == RegOpenKeyExA(hKeyRoot,
                                       SZIE30CERTBUCKET,
                                       0,                  // dwReserved
                                       KEY_READ,
                                       &hKeyBucket
                                       )               &&

        ERROR_SUCCESS == RegOpenKeyExA(hKeyRoot,
                                       SZIE30AUXINFO,
                                       0,                  // dwReserved
                                       KEY_READ,
                                       &hKeyAux
                                       )               &&

        ERROR_SUCCESS == RegOpenKeyExA(hKeyRoot,
                                       SZIE30TAGS,
                                       0,                  // dwReserved
                                       KEY_READ,
                                       &hKeyTags
                                       )) {

        DWORD   cValuesCert, cchMaxNameCert, cbMaxDataCert;
        DWORD   cValuesTag, cchMaxNameTag, cbMaxDataTag;
        DWORD   cValuesAux, cchMaxNameAux, cbMaxDataAux;
        LPSTR   szName = NULL;
        BYTE *pbLoadCert = NULL;
        BYTE *pbFixedCert = NULL;
        BYTE *pbDataCert = NULL;
        BYTE *pbDataAux = NULL;
        BYTE *pbDataTag = NULL;

        // see how many and how big the registry is
        if (ERROR_SUCCESS != RegQueryInfoKey(hKeyBucket,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             &cValuesCert,
                                             &cchMaxNameCert,
                                             &cbMaxDataCert,
                                             NULL,
                                             NULL
                                             )           ||
            ERROR_SUCCESS != RegQueryInfoKey(hKeyTags,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             &cValuesTag,
                                             &cchMaxNameTag,
                                             &cbMaxDataTag,
                                             NULL,
                                             NULL
                                             )           ||
            ERROR_SUCCESS != RegQueryInfoKey(hKeyAux,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             NULL,
                                             &cValuesAux,
                                             &cchMaxNameAux,
                                             &cbMaxDataAux,
                                             NULL,
                                             NULL
                                             ))
            hr = HError();
        else {
            // allocate the memory needed to read the reg
            szName = (LPSTR) LocalAlloc(LMEM_ZEROINIT, cchMaxNameCert + 1);
            pbDataCert = (BYTE *) LocalAlloc(LMEM_ZEROINIT, cbMaxDataCert);
            pbDataTag = (BYTE *) LocalAlloc(LMEM_ZEROINIT, cbMaxDataTag);
            pbDataAux = (BYTE *) LocalAlloc(LMEM_ZEROINIT, cbMaxDataAux);

            if (NULL == szName      ||
                NULL == pbDataCert  ||
                NULL == pbDataAux   ||
                NULL == pbDataTag   )
                hr = E_OUTOFMEMORY;
        }

        // enum the registry getting certs
        for (DWORD i = 0; SUCCEEDED(hr) && i < cValuesCert; i++ ) {

            DWORD dwType;
            BYTE *  pb;
            CRYPT_KEY_PROV_INFO   keyInfo;
            DWORD cchName = cchMaxNameCert + 1;
            DWORD cbDataCert = cbMaxDataCert;
            DWORD cbLoadCert = 0;
            DWORD cbFixedCert = 0;
            DWORD cbDataTag = cbMaxDataTag;
            DWORD cbDataAux = cbMaxDataAux;
            PCCERT_CONTEXT pCertContxt = NULL;
            HRESULT status = S_OK;

            // don't have to worry about errors, just skip
            // sliently just be cause there is an internal
            // error in the registry doesn't mean we should
            // get all upset about it.

            // get the cert
            if (RegEnumValueA(hKeyBucket,
                              i,
                              szName,
                              &cchName,
                              NULL,
                              &dwType,
                              pbDataCert,
                              &cbDataCert
                              ) == ERROR_SUCCESS      &&

                dwType == REG_BINARY) {


                if(Fix7FCert(cbDataCert,
                             pbDataCert,
                             &cbFixedCert,
                             &pbFixedCert) &&
                   cbFixedCert != 0) {
                    pbLoadCert = pbFixedCert;
                    cbLoadCert = cbFixedCert;
                }
                else {
                    pbLoadCert = pbDataCert;
                    cbLoadCert = cbDataCert;
                }

                // get the cert context
                if((pCertContxt = CertCreateCertificateContext(X509_ASN_ENCODING,
                                                               pbLoadCert,
                                                               cbLoadCert)) != NULL) {

                    // See if it has a tag and aux info.
                    // get the tag
                    if(RegQueryValueExA(hKeyTags,
                                        szName,
                                        NULL,
                                        &dwType,
                                        pbDataTag,
                                        &cbDataTag) == ERROR_SUCCESS    &&

                       // get the aux info
                       RegQueryValueExA(hKeyAux,
                                        (LPTSTR) pbDataTag,
                                        NULL,
                                        &dwType,
                                        pbDataAux,
                                        &cbDataAux) == ERROR_SUCCESS ) {

                        // aux info is
                        // wszPurpose
                        // wszProvider
                        // wszKeySet
                        // wszFilename
                        // wszCredentials
                        // dwProviderType
                        // dwKeySpec

                        pb = pbDataAux;
                        memset(&keyInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));

                        // skip purpose, should be client auth
                        pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);

                        // get the provider
                        keyInfo.pwszProvName = (LPWSTR) pb;
                        pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);

                        // get the container name
                        keyInfo.pwszContainerName = (LPWSTR) pb;
                        pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);

                        // skip filename, should be '\0'
                        pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);

                        // skip credential, don't really know what it is?
                        pb += (lstrlenW((LPWSTR) pb) + 1) * sizeof(WCHAR);

                        // get the provider type
                        keyInfo.dwProvType = *((DWORD *) pb);
                        pb += sizeof(DWORD);

                        // get the key spec
                        keyInfo.dwKeySpec  = *((DWORD *) pb);


                        // add the property to the certificate
                        if( !CertSetCertificateContextProperty(pCertContxt,
                                                               CERT_KEY_PROV_INFO_PROP_ID,
                                                               0,
                                                               &keyInfo))
                            status = S_FALSE;
                    }

                    if(status == S_OK) {
                        HRESULT hr2 = PurgeDuplicateCertificate(hStore, pCertContxt);
                        if(hr2 == S_OK) {
                            if(!CertAddCertificateContextToStore(hStore,
                                                                 pCertContxt,
                                                                 CERT_STORE_ADD_USE_EXISTING,
                                                                 NULL       // ppStoreContext
                                                                 )) {
                                /*MessageBox(NULL,
                                  "Copy Certificate Failed",
                                  NULL,
                                  MB_OK);*/
                                hr = HError();
                            }
                        }
                    }
                }

            }
            if(pCertContxt != NULL)
                CertFreeCertificateContext(pCertContxt);
            if(pbFixedCert) {
                LocalFree(pbFixedCert);
                pbFixedCert = NULL;
            }
        }

        if (szName)
            LocalFree(szName);
        if (pbDataCert)
            LocalFree(pbDataCert);
        if(pbDataAux)
            LocalFree(pbDataAux);
        if(pbDataTag)
            LocalFree(pbDataTag);
    }



    if(hKeyRoot != NULL)
        RegCloseKey(hKeyRoot);
    if(hKeyBucket != NULL)
        RegCloseKey(hKeyBucket);
    if(hKeyTags != NULL)
        RegCloseKey(hKeyTags);
    if(hKeyAux != NULL)
        RegCloseKey(hKeyAux);
    if (FAILED(hr))
        return hr;

    if(SUCCEEDED(hr) && fDelete) {

        // Re-open registry with write/delete access
        if (ERROR_SUCCESS != RegOpenKeyEx(hRegRoot,
                                          psLoc,
                                          0,          // dwRes erved
                                          KEY_ALL_ACCESS,
                                          &hKeyRoot))
            return HError();

        Status = RegDeleteKey(hKeyRoot, SZIE30CERTBUCKET);
        Status = RegDeleteKey(hKeyRoot, SZIE30INDEXISSUER);
        Status = RegDeleteKey(hKeyRoot, SZIE30INDEXISSUERSER);
        Status = RegDeleteKey(hKeyRoot, SZIE30INDEXSUBJECT);
        Status = RegDeleteKey(hKeyRoot, SZIE30INDEXKEY);
        Status = RegDeleteKey(hKeyRoot, SZIE30TAGS);
        RegCloseKey(hKeyRoot);
    }

    return hr;
}


HRESULT MoveCertificates(BOOL fDelete)
{
    HRESULT hr = S_OK;
    HRESULT hr2 = S_OK;

    HCERTSTORE hSpcStore = NULL;
    HCERTSTORE hStore = NULL;
    HCRYPTPROV hCrypt = NULL;
    //__asm int 3
    PKITRY {
        /*
        if (!CryptAcquireContext(&hCrypt, NULL, MS_DEF_PROV, PROV_RSA_FULL, 0))
            PKITHROW(HError());
            */
        hSpcStore = CertOpenSystemStore( NULL, TEXT("SPC") );

        if(!hSpcStore)
            PKITHROW(HError());
        hr = MoveSpcCerts(fDelete, hSpcStore);

        hStore = CertOpenSystemStore(NULL, TEXT(IE30CONVERTEDSTORE));
        if(!hStore)
            PKITHROW(HError());

        hr2 = TransferIE30Certificates(HKEY_CURRENT_USER, SZIE30CERTCLIENTAUTH, hStore, fDelete);

    }
    PKICATCH(err) {
        hr = err.pkiError;
    } PKIEND;

    if(SUCCEEDED(hr)) hr = hr2;
    if(hSpcStore)
        CertCloseStore( hSpcStore, 0 );
    if(hStore)
        CertCloseStore(hStore, 0);
    if(hCrypt)
        CryptReleaseContext(hCrypt, 0);
    return hr;
}