//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1998 - 1999
//
//  File:       pfximpt.cpp
//
//  Contents:   PFX import dialog
//
//  History:    06/98   xtan
//
//----------------------------------------------------------------------------

#include "pch.cpp"
#pragma hdrstop

#include "cscsp.h"
#include "certmsg.h"
#include "initcert.h"
#include "setuput.h"
#include "cspenum.h"
#include "wizpage.h"
#include "usecert.h"


#define __dwFILE__      __dwFILE_OCMSETUP_PFXIMPT_CPP__


typedef struct _certpfximportinfo
{
    HINSTANCE hInstance;
    BOOL      fUnattended;
    WCHAR   *pwszFileName;
    DWORD    dwFileNameSize;
    WCHAR   *pwszPassword;
    DWORD    dwPasswordSize;
} CERTPFXIMPORTINFO;

HRESULT
CertBrowsePFX(HINSTANCE hInstance, HWND hDlg)
{
    HRESULT   hr;
    WCHAR    *pwszFileNameIn = NULL;
    WCHAR    *pwszFileNameOut = NULL;
    HWND      hCtrl = GetDlgItem(hDlg, IDC_PFX_FILENAME);

    hr = myUIGetWindowText(hCtrl, &pwszFileNameIn);
    _JumpIfError(hr, error, "myUIGetWindowText");

    hr = myGetOpenFileName(
             hDlg,
             hInstance,
             IDS_IMPORT_PFX_TITLE,
             IDS_PFX_FILE_FILTER,
             0, // no def ext
             OFN_PATHMUSTEXIST | OFN_HIDEREADONLY,
             pwszFileNameIn,
             &pwszFileNameOut);
    _JumpIfError(hr, error, "myGetOpenFileName");

    if (NULL != pwszFileNameOut)
    {
        SetWindowText(hCtrl, pwszFileNameOut);
    }

    hr = S_OK;
error:
    if (NULL != pwszFileNameOut)
    {
        LocalFree(pwszFileNameOut);
    }
    if (NULL != pwszFileNameIn)
    {
        LocalFree(pwszFileNameIn);
    }
    return hr;
}

HRESULT
GetPFXInfo(
    HWND               hDlg,
    CERTPFXIMPORTINFO* pCertPfxImportInfo)
{
    HRESULT hr;
    GetWindowText(GetDlgItem(hDlg, IDC_PFX_FILENAME),
                  pCertPfxImportInfo->pwszFileName,
                  pCertPfxImportInfo->dwFileNameSize);
    if (0x0 == pCertPfxImportInfo->pwszFileName[0])
    {
        // file can't empty
        hr = E_INVALIDARG;
        CertWarningMessageBox(
            pCertPfxImportInfo->hInstance,
            pCertPfxImportInfo->fUnattended,
            hDlg,
            IDS_ERR_EMPTYPFXFILE,
            0,
            NULL);
        SetFocus(GetDlgItem(hDlg, IDC_PFX_FILENAME));
        goto error;
    }
    GetWindowText(GetDlgItem(hDlg, IDC_PFX_PASSWORD),
                  pCertPfxImportInfo->pwszPassword,
                  pCertPfxImportInfo->dwPasswordSize);
    hr = S_OK;
error:
    return hr;
}

INT_PTR CALLBACK
CertPFXFilePasswordProc(
    HWND hDlg, 
    UINT iMsg, 
    WPARAM wParam, 
    LPARAM lParam) 
{
    HRESULT hr;
    BOOL  ret = FALSE;
    int   id = IDCANCEL;
    static CERTPFXIMPORTINFO *pCertPfxImportInfo = NULL;

    switch (iMsg)
    {
        case WM_INITDIALOG:
            pCertPfxImportInfo = (CERTPFXIMPORTINFO*)lParam;
            ret = TRUE;
        break;
        case WM_COMMAND:
            switch (LOWORD(wParam))
            {
                case IDC_PFX_FILENAME:
                break;
                case IDC_PFX_PASSWORD:
                break;
                case IDC_PFX_BROWSE:
                    CertBrowsePFX(pCertPfxImportInfo->hInstance, hDlg);
                    ret = TRUE;
                break;
                case IDOK:
                    hr = GetPFXInfo(hDlg, pCertPfxImportInfo);
                    if (S_OK != hr)
                    {
                        break;
                    }
                    id = IDOK;
                case IDCANCEL:
                    ret = EndDialog(hDlg, id);
                break;
            }
        break;
        default:
        ret = FALSE;
    }
    return ret;
}
 
int
CertGetPFXFileAndPassword(
    IN HWND       hwnd,
    IN HINSTANCE  hInstance,
    IN BOOL       fUnattended,
    IN OUT WCHAR *pwszFileName,
    IN DWORD      dwFileNameSize,
    IN OUT WCHAR *pwszPassword,
    IN DWORD      dwPasswordSize)
{
    CERTPFXIMPORTINFO    CertPfxImportInfo =
        {hInstance, fUnattended,
         pwszFileName, dwFileNameSize,
         pwszPassword, dwPasswordSize};

    return (int) DialogBoxParam(hInstance,
              MAKEINTRESOURCE(IDD_PFXIMPORT),
              hwnd,
              CertPFXFilePasswordProc,
              (LPARAM)&CertPfxImportInfo);
}

//--------------------------------------------------------------------
HRESULT
ImportPFXAndUpdateCSPInfo(
    IN const HWND    hDlg,
    IN OUT PER_COMPONENT_DATA *pComp)
{
    HRESULT hr;
    int nDlgRet;
    BOOL bRetVal;
    DWORD dwVerificationFlags;
    BOOL bSelfSigned;
    CSP_HASH * pHash;
    WCHAR wszName[MAX_PATH];
    WCHAR wszPassword[MAX_PATH];
    CSP_INFO * pCSPInfo;
    DWORD dwCSPInfoSize;
    CASERVERSETUPINFO * pServer=pComp->CA.pServer;

    // variables that must be cleaned up
    CRYPT_KEY_PROV_INFO *pCertKeyProvInfo = NULL;
    CERT_CONTEXT const *pSavedLeafCert = NULL;

    wszName[0] = L'\0';

    // get file name & password
    if(pComp->fUnattended)
    {
        CSASSERT(NULL!=pServer->pwszPFXFile);

        if(MAX_PATH<=wcslen(pServer->pwszPFXFile)||
           NULL!=pServer->pwszPFXPassword && 
           MAX_PATH<=wcslen(pServer->pwszPFXPassword))
        {
            hr = ERROR_BAD_PATHNAME;
            CertWarningMessageBox(
                    pComp->hInstance,
                    pComp->fUnattended,
                    hDlg,
                    IDS_PFX_FILE_OR_PASSWORD_TOO_LONG,
                    0,
                    NULL);
            _JumpError(hr, error, "PFX file name or password is too long");
        }

        wcscpy(wszName, pServer->pwszPFXFile);
        wcscpy(wszPassword, 
            pServer->pwszPFXPassword?pServer->pwszPFXPassword:L"");

        if (NULL == pServer->pCSPInfoList)
        {
            hr = GetCSPInfoList(&pServer->pCSPInfoList);
            _JumpIfError(hr, error, "GetCSPInfoList");
        }
    }
    else{
        nDlgRet = CertGetPFXFileAndPassword(
                                    hDlg,
                                    pComp->hInstance,
                                    pComp->fUnattended,
                                    wszName,
                                    sizeof(wszName)/sizeof(WCHAR),
                                    wszPassword,
                                    sizeof(wszPassword)/sizeof(WCHAR));
        if (IDOK != nDlgRet)
        {
            // cancel
            hr=HRESULT_FROM_WIN32(ERROR_CANCELLED);
            _JumpError(hr, error, "CertGetPFXFileAndPassword canceled");
        }
    }

    // import pkcs12
    hr=myCertServerImportPFX(
               wszName,
               wszPassword,
               FALSE,
               NULL,
               NULL,
               &pSavedLeafCert);
    if (S_OK != hr)
    {
        if (HRESULT_FROM_WIN32(ERROR_INVALID_PASSWORD)==hr)
        {

            // tell the user that their password was invalid
            CertWarningMessageBox(
                    pComp->hInstance,
                    pComp->fUnattended,
                    hDlg,
                    IDS_PFX_INVALID_PASSWORD,
                    0,
                    NULL);
            _JumpError(hr, error, "myCertServerImportPFX");

        }
        else if (HRESULT_FROM_WIN32(ERROR_FILE_EXISTS) == hr)
        {

            if(pComp->fUnattended)
            {
                nDlgRet=IDYES;
            }
            else
            {
                // confirm from user that they want to overwrite
                // the existing key and cert
                nDlgRet=CertMessageBox(
                            pComp->hInstance,
                            pComp->fUnattended,
                            hDlg,
                            IDS_PFX_KEYANDCERTEXIST,
                            0,
                            MB_YESNO | MB_ICONWARNING | CMB_NOERRFROMSYS,
                            NULL);
            }
            if (IDYES==nDlgRet)
            {
                hr=myCertServerImportPFX(
                           wszName,
                           wszPassword,
                           TRUE,
                           NULL,
                           NULL, 
                           &pSavedLeafCert);
                _JumpIfError(hr, errorMsg, "myCertServerImportPFX");
            }
            else
            {
                // cancel
                hr=HRESULT_FROM_WIN32(ERROR_CANCELLED);
                _JumpError(hr, error, "myCertServerImportPFX canceled");
            }
        }
        else if (HRESULT_FROM_WIN32(ERROR_PATH_NOT_FOUND) == hr)
        {
            CertWarningMessageBox(
                    pComp->hInstance,
                    pComp->fUnattended,
                    hDlg,
                    IDS_PFX_PATH_INVALID,
                    0,
                    wszName);
            _JumpError(hr, error, "myCertServerImportPFX");
        }
        else if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr)
        {
            CertWarningMessageBox(
                    pComp->hInstance,
                    pComp->fUnattended,
                    hDlg,
                    IDS_PFX_FILE_NOT_FOUND,
                    0,
                    wszName);
            _JumpError(hr, error, "myCertServerImportPFX");
        }
        else if (HRESULT_FROM_WIN32(CRYPT_E_SELF_SIGNED) == hr)
        {
            // this cert is not appropriate for this CA type (no CA certs found at all)
            CertWarningMessageBox(
                pComp->hInstance,
                pComp->fUnattended,
                hDlg,
                IDS_PFX_WRONG_SELFSIGN_TYPE,
                S_OK, // don't show an error number
                NULL);
            _JumpError(hr, error, "This cert is not appropriate for this CA type");
        }
        else
        {
            // import failed for some other reason
            _JumpError(hr, errorMsg, "myCertServerImportPFX");
        }
    }

    // PFX import was successful. The cert is in the machine's MY store.
    CSASSERT(NULL!=pSavedLeafCert);

    // The following things have been verified by myCertServerImportPFX
    //  * The cert has an AT_SIGNATURE key
    //  * The key in the store matches the one on the cer
    //  * The cert is not expired
    //
    // We still need to check:
    //  * self-signed or not
    //  * verify chain

    // Note: IT IS VERY IMPORTANT that pfx import maintains all the
    //   invariants about CSP, key container, hash, cert validity, etc.
    //   that the rest of the UI maintains.

    // get key prov info from cert
    bRetVal=myCertGetCertificateContextProperty(
        pSavedLeafCert,
        CERT_KEY_PROV_INFO_PROP_ID,
        CERTLIB_USE_LOCALALLOC,
        (void **)&pCertKeyProvInfo,
        &dwCSPInfoSize);
    if (FALSE==bRetVal) {
        hr=myHLastError();
        _JumpError(hr, errorMsg, "myCertGetCertificateContextProperty");
    }

    // find our description of the CSP
    pCSPInfo=findCSPInfoFromList(pServer->pCSPInfoList,
        pCertKeyProvInfo->pwszProvName,
        pCertKeyProvInfo->dwProvType);
    CSASSERT(NULL!=pCSPInfo);
    if (pCSPInfo == NULL) // we don't have this CSP enumerated in our UI
    {
        hr = CRYPT_E_NOT_FOUND;
        _JumpError(hr, errorMsg, "pCSPInfo NULL");
    }

    //
    // Looks like this key is good. Use it.
    //

    // Stop using the previous cert and key
    // delete previously created key container, if necessary.
    ClearKeyContainerName(pServer);

    // update the CSP
    //   note: CSP, key container, and hash must all be consistent!
    pServer->pCSPInfo=pCSPInfo;

    hr = DetermineDefaultHash(pServer);
    _JumpIfError(hr, error, "DetermineDefaultHash");
    
    // save the name of the key container
    hr=SetKeyContainerName(pServer, pCertKeyProvInfo->pwszContainerName);
    _JumpIfError(hr, error, "SetKeyContainerName");

    //  See if we can use the cert

    // verify to make sure no cert in chain is revoked, but don't kill yourself if offline
    hr=myVerifyCertContext(
        pSavedLeafCert,
        CA_VERIFY_FLAGS_IGNORE_OFFLINE,
        0,
        NULL,
        HCCE_LOCAL_MACHINE,
        NULL,
        NULL);
    _JumpIfError(hr, errorMsg, "myVerifyCertContext");

    // See if this cert appropriately is self-signed or not.
    // A root CA cert must be self-signed, while
    // a subordinate CA cert must not be self-signed.
    hr=IsCertSelfSignedForCAType(pServer, pSavedLeafCert, &bRetVal);
    _JumpIfError(hr, errorMsg, "IsCertSelfSignedForCAType");
    if (FALSE==bRetVal) {

        // this cert is not appropriate for this CA type
        CertWarningMessageBox(
            pComp->hInstance,
            pComp->fUnattended,
            hDlg,
            IDS_PFX_WRONG_SELFSIGN_TYPE,
            S_OK, // don't show an error number
            NULL);

        hr=CRYPT_E_SELF_SIGNED;
        _JumpError(hr, error, "This cert is not appropriate for this CA type");
    }

    //
    // Looks like this cert is good. Use it.
    //

    // save the cert and update the hash algorithm
    hr=SetExistingCertToUse(pServer, pSavedLeafCert);
    _JumpIfError(hr, error, "SetExistingCertToUse");
    pSavedLeafCert=NULL;

    hr=S_OK;

errorMsg:
    if (FAILED(hr)) {
        // an error occurred while trying to import the PFX file
        CertWarningMessageBox(
            pComp->hInstance,
            pComp->fUnattended,
            hDlg,
            IDS_ERR_IMPORTPFX,
            hr,
            NULL);
    }

error:
    CSILOG(
        hr,
        IDS_LOG_IMPORTPFX,
        L'\0' == wszName[0]? NULL : wszName,
        NULL,
        NULL);
    if (NULL != pSavedLeafCert)
    {
        CertFreeCertificateContext(pSavedLeafCert);
    }
    if (NULL != pCertKeyProvInfo)
    {
        LocalFree(pCertKeyProvInfo);
    }
    return hr;
}