diff --git a/src/dtf/SfxCA/SfxCA.vcxproj b/src/dtf/SfxCA/SfxCA.vcxproj index 5c4f674f6..ccebc3860 100644 --- a/src/dtf/SfxCA/SfxCA.vcxproj +++ b/src/dtf/SfxCA/SfxCA.vcxproj @@ -67,7 +67,9 @@ - + + 0x0409 + diff --git a/src/ext/Http/ca/sslcert.cpp b/src/ext/Http/ca/sslcert.cpp index d4276b788..f7c226268 100644 --- a/src/ext/Http/ca/sslcert.cpp +++ b/src/ext/Http/ca/sslcert.cpp @@ -2,10 +2,10 @@ #include "precomp.h" -static UINT SchedHttpSslCerts( +static UINT SchedHttpSslBindings( __in WCA_TODO todoSched ); -static HRESULT WriteExistingSslCert( +static HRESULT WriteExistingSslBinding( __in WCA_TODO action, __in_z LPCWSTR wzId, __in_z LPCWSTR wzHost, @@ -14,14 +14,13 @@ static HRESULT WriteExistingSslCert( __in HTTP_SERVICE_CONFIG_SSL_SET* pSslSet, __inout_z LPWSTR* psczCustomActionData ); -static HRESULT WriteSslCert( +static HRESULT WriteSslBinding( __in WCA_TODO action, __in_z LPCWSTR wzId, __in_z LPCWSTR wzHost, __in int iPort, __in int iHandleExisting, __in_z LPCWSTR wzCertificateThumbprint, - __in_z LPCWSTR wzCertificateLookup, __in_z LPCWSTR wzAppId, __in_z_opt LPCWSTR wzCertificateStore, __inout_z LPWSTR* psczCustomActionData @@ -34,7 +33,7 @@ static HRESULT StringFromGuid( __in REFGUID rguid, __inout_z LPWSTR* psczGuid ); -static HRESULT AddSslCert( +static HRESULT AddSslBinding( __in_z LPCWSTR wzId, __in_z LPWSTR wzHost, __in int iPort, @@ -43,82 +42,75 @@ static HRESULT AddSslCert( __in GUID* pAppId, __in_z LPWSTR wzSslCertStore ); -static HRESULT GetSslCert( +static HRESULT GetSslBinding( __in_z LPWSTR wzHost, __in int nPort, __out HTTP_SERVICE_CONFIG_SSL_SET** ppSet ); -static HRESULT RemoveSslCert( +static HRESULT RemoveSslBinding( __in_z LPCWSTR wzId, __in_z LPWSTR wzHost, __in int iPort ); -static HRESULT SetSslCertSetKey( +static HRESULT SetSslBindingSetKey( __in HTTP_SERVICE_CONFIG_SSL_KEY* pKey, __in_z LPWSTR wzHost, __in int iPort ); -static HRESULT FindExistingCertificate( - __in LPCWSTR wzName, - __in DWORD dwStoreLocation, - __in LPCWSTR wzStore, - __out BYTE** prgbCertificate, - __out DWORD* pcbCertificate -); -LPCWSTR vcsWixHttpSslCertQuery = -L"SELECT `WixHttpSslCert`, `Host`, `Port`, `Thumbprint`, `Certificate_`, `AppId`, `Store`, `HandleExisting`, `Component_` " -L"FROM `Wix4HttpSslCert`"; -enum eWixHttpSslCertQuery { hurqId = 1, hurqHost, hurqPort, hurqCertificateThumbprint, hurqCertificateLookup, hurqAppId, hurqCertificateStore, hurqHandleExisting, hurqComponent }; +LPCWSTR vcsWixHttpSslBindingQuery = +L"SELECT `WixHttpSslBinding`, `Host`, `Port`, `Thumbprint`, `AppId`, `Store`, `HandleExisting`, `Component_` " +L"FROM `Wix4HttpSslBinding`"; +enum eWixHttpSslBindingQuery { hurqId = 1, hurqHost, hurqPort, hurqCertificateThumbprint, hurqAppId, hurqCertificateStore, hurqHandleExisting, hurqComponent }; #define msierrCERTFailedOpen 26351 /****************************************************************** - SchedWixHttpSslCertsInstall - immediate custom action entry + SchedWixHttpSslBindingsInstall - immediate custom action entry point to prepare adding URL reservations. ********************************************************************/ -extern "C" UINT __stdcall SchedHttpSslCertsInstall( +extern "C" UINT __stdcall SchedHttpSslBindingsInstall( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; - hr = WcaInitialize(hInstall, "SchedHttpSslCertsInstall"); + hr = WcaInitialize(hInstall, "SchedHttpSslBindingsInstall"); ExitOnFailure(hr, "Failed to initialize"); - hr = SchedHttpSslCerts(WCA_TODO_INSTALL); + hr = SchedHttpSslBindings(WCA_TODO_INSTALL); LExit: return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); } /****************************************************************** - SchedWixHttpSslCertsUninstall - immediate custom action entry + SchedWixHttpSslBindingsUninstall - immediate custom action entry point to prepare removing URL reservations. ********************************************************************/ -extern "C" UINT __stdcall SchedHttpSslCertsUninstall( +extern "C" UINT __stdcall SchedHttpSslBindingsUninstall( __in MSIHANDLE hInstall ) { HRESULT hr = S_OK; - hr = WcaInitialize(hInstall, "SchedHttpSslCertsUninstall"); + hr = WcaInitialize(hInstall, "SchedHttpSslBindingsUninstall"); ExitOnFailure(hr, "Failed to initialize"); - hr = SchedHttpSslCerts(WCA_TODO_UNINSTALL); + hr = SchedHttpSslBindings(WCA_TODO_UNINSTALL); LExit: return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); } /****************************************************************** - ExecHttpSslCerts - deferred custom action entry point to + ExecHttpSslBindings - deferred custom action entry point to register and remove URL reservations. ********************************************************************/ -extern "C" UINT __stdcall ExecHttpSslCerts( +extern "C" UINT __stdcall ExecHttpSslBindings( __in MSIHANDLE hInstall ) { @@ -132,7 +124,6 @@ extern "C" UINT __stdcall ExecHttpSslCerts( int iPort = 0; eHandleExisting handleExisting = heIgnore; LPWSTR sczCertificateThumbprint = NULL; - LPWSTR sczCertificateLookup = NULL; LPWSTR sczAppId = NULL; LPWSTR sczCertificateStore = NULL; @@ -146,7 +137,7 @@ extern "C" UINT __stdcall ExecHttpSslCerts( DWORD cbCertificateThumbprint = 0; // Initialize. - hr = WcaInitialize(hInstall, "ExecHttpSslCerts"); + hr = WcaInitialize(hInstall, "ExecHttpSslBindings"); ExitOnFailure(hr, "Failed to initialize"); hr = HRESULT_FROM_WIN32(::HttpInitialize(HTTPAPI_VERSION_1, HTTP_INITIALIZE_CONFIG, NULL)); @@ -180,9 +171,6 @@ extern "C" UINT __stdcall ExecHttpSslCerts( hr = WcaReadStringFromCaData(&wz, &sczCertificateThumbprint); ExitOnFailure(hr, "Failed to read CertificateThumbprint from custom action data"); - hr = WcaReadStringFromCaData(&wz, &sczCertificateLookup); - ExitOnFailure(hr, "Failed to read CertificateLookup from custom action data"); - hr = WcaReadStringFromCaData(&wz, &sczAppId); ExitOnFailure(hr, "Failed to read AppId from custom action data"); @@ -194,66 +182,48 @@ extern "C" UINT __stdcall ExecHttpSslCerts( case WCA_TODO_INSTALL: case WCA_TODO_REINSTALL: fRemove = heReplace == handleExisting || fRollback; - fAdd = !fRollback || (*sczCertificateThumbprint || *sczCertificateLookup); + fAdd = !fRollback || *sczCertificateThumbprint; fFailOnExisting = heFail == handleExisting && !fRollback; break; case WCA_TODO_UNINSTALL: fRemove = !fRollback; - fAdd = fRollback && (*sczCertificateThumbprint || *sczCertificateLookup); + fAdd = fRollback && *sczCertificateThumbprint; fFailOnExisting = FALSE; break; } if (fRemove) { - hr = RemoveSslCert(sczId, sczHost, iPort); + hr = RemoveSslBinding(sczId, sczHost, iPort); if (S_OK == hr) { - WcaLog(LOGMSG_STANDARD, "Removed SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); + WcaLog(LOGMSG_STANDARD, "Removed SSL certificate binding '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); } else if (FAILED(hr)) { if (fRollback) { - WcaLogError(hr, "Failed to remove SSL certificate to rollback '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); + WcaLogError(hr, "Failed to remove SSL certificate binding to rollback '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); } else { - ExitOnFailure(hr, "Failed to remove SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); + ExitOnFailure(hr, "Failed to remove SSL certificate binding '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); } } } if (fAdd) { - WcaLog(LOGMSG_STANDARD, "Adding SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); + WcaLog(LOGMSG_STANDARD, "Adding SSL certificate binding '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); - // if we have been provided a thumbprint, then use that - if (*sczCertificateThumbprint) - { - hr = StrAllocHexDecode(sczCertificateThumbprint, &pbCertificateThumbprint, &cbCertificateThumbprint); - ExitOnFailure(hr, "Failed to convert thumbprint to bytes for SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); - } - - // if we have been provided with a cerificate lookup, use that to find an existing certificate - if (*sczCertificateLookup) - { - hr = FindExistingCertificate(sczCertificateLookup, CERT_SYSTEM_STORE_LOCAL_MACHINE, sczCertificateStore, &pbCertificateThumbprint, &cbCertificateThumbprint); - ExitOnFailure(hr, "Failed to convert thumbprint to bytes for referenced SSL certificate '%ls'", sczCertificateLookup); - if (S_FALSE == hr) - { - ExitOnFailure(HRESULT_FROM_WIN32(ERROR_NOT_FOUND), "Failed to find referenced SSL certificate '%ls'", sczCertificateLookup); - } - - hr = StrAllocHexEncode(pbCertificateThumbprint, cbCertificateThumbprint, &sczCertificateThumbprint); - ExitOnFailure(hr, "Failed to convert thumbprint for referenced SSL certificate '%ls'", sczCertificateLookup); - } + hr = StrAllocHexDecode(sczCertificateThumbprint, &pbCertificateThumbprint, &cbCertificateThumbprint); + ExitOnFailure(hr, "Failed to convert thumbprint to bytes for SSL certificate binding '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); hr = ::IIDFromString(sczAppId, &guidAppId); - ExitOnFailure(hr, "Failed to convert AppId '%ls' back to GUID for SSL certificate '%ls' for hostname: %ls:%d", sczAppId, sczId, sczHost, iPort); + ExitOnFailure(hr, "Failed to convert AppId '%ls' back to GUID for SSL certificate binding '%ls' for hostname: %ls:%d", sczAppId, sczId, sczHost, iPort); - hr = AddSslCert(sczId, sczHost, iPort, pbCertificateThumbprint, cbCertificateThumbprint, &guidAppId, sczCertificateStore && *sczCertificateStore ? sczCertificateStore : L"MY"); + hr = AddSslBinding(sczId, sczHost, iPort, pbCertificateThumbprint, cbCertificateThumbprint, &guidAppId, sczCertificateStore && *sczCertificateStore ? sczCertificateStore : L"MY"); if (S_FALSE == hr && fFailOnExisting) { hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); @@ -261,17 +231,17 @@ extern "C" UINT __stdcall ExecHttpSslCerts( if (S_OK == hr) { - WcaLog(LOGMSG_STANDARD, "Added SSL certificate '%ls' for hostname: %ls:%d with thumbprint: %ls", sczId, sczHost, iPort, sczCertificateThumbprint); + WcaLog(LOGMSG_STANDARD, "Added SSL certificate binding '%ls' for hostname: %ls:%d with thumbprint: %ls", sczId, sczHost, iPort, sczCertificateThumbprint); } else if (FAILED(hr)) { if (fRollback) { - WcaLogError(hr, "Failed to add SSL certificate to rollback '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); + WcaLogError(hr, "Failed to add SSL certificate binding to rollback '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); } else { - ExitOnFailure(hr, "Failed to add SSL certificate '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); + ExitOnFailure(hr, "Failed to add SSL certificate binding '%ls' for hostname: %ls:%d", sczId, sczHost, iPort); } } @@ -296,7 +266,7 @@ extern "C" UINT __stdcall ExecHttpSslCerts( return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); } -static UINT SchedHttpSslCerts( +static UINT SchedHttpSslBindings( __in WCA_TODO todoSched ) { @@ -319,7 +289,6 @@ static UINT SchedHttpSslCerts( LPWSTR sczHost = NULL; int iPort = 0; LPWSTR sczCertificateThumbprint = NULL; - LPWSTR sczCertificateLookup = NULL; LPWSTR sczAppId = NULL; LPWSTR sczCertificateStore = NULL; int iHandleExisting = 0; @@ -327,17 +296,17 @@ static UINT SchedHttpSslCerts( HTTP_SERVICE_CONFIG_SSL_SET* pExistingSslSet = NULL; // Anything to do? - hr = WcaTableExists(L"Wix4HttpSslCert"); - ExitOnFailure(hr, "Failed to check if the Wix4HttpSslCert table exists"); + hr = WcaTableExists(L"Wix4HttpSslBinding"); + ExitOnFailure(hr, "Failed to check if the Wix4HttpSslBinding table exists"); if (S_FALSE == hr) { - WcaLog(LOGMSG_STANDARD, "Wix4HttpSslCert table doesn't exist, so there are no URL reservations to configure"); + WcaLog(LOGMSG_STANDARD, "Wix4HttpSslBinding table doesn't exist, so there are no URL reservations to configure"); ExitFunction(); } - // Query and loop through all the SSL certificates. - hr = WcaOpenExecuteView(vcsWixHttpSslCertQuery, &hView); - ExitOnFailure(hr, "Failed to open view on the Wix4HttpSslCert table"); + // Query and loop through all the SSL certificate bindings. + hr = WcaOpenExecuteView(vcsWixHttpSslBindingQuery, &hView); + ExitOnFailure(hr, "Failed to open view on the Wix4HttpSslBinding table"); hr = HRESULT_FROM_WIN32(::HttpInitialize(HTTPAPI_VERSION_1, HTTP_INITIALIZE_CONFIG, NULL)); ExitOnFailure(hr, "Failed to initialize HTTP Server configuration"); @@ -347,69 +316,67 @@ static UINT SchedHttpSslCerts( while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) { hr = WcaGetRecordString(hRec, hurqId, &sczId); - ExitOnFailure(hr, "Failed to get Wix4HttpSslCert.Wix4HttpSslCert"); + ExitOnFailure(hr, "Failed to get Wix4HttpSslBinding.WixHttpSslBinding"); hr = WcaGetRecordString(hRec, hurqComponent, &sczComponent); - ExitOnFailure(hr, "Failed to get Wix4HttpSslCert.Component_"); + ExitOnFailure(hr, "Failed to get Wix4HttpSslBinding.Component_"); // Figure out what we're doing for this reservation, treating reinstall the same as install. todoComponent = WcaGetComponentToDo(sczComponent); if ((WCA_TODO_REINSTALL == todoComponent ? WCA_TODO_INSTALL : todoComponent) != todoSched) { - WcaLog(LOGMSG_STANDARD, "Component '%ls' action state (%d) doesn't match request (%d) for Wix4HttpSslCert '%ls'", sczComponent, todoComponent, todoSched, sczId); + WcaLog(LOGMSG_STANDARD, "Component '%ls' action state (%d) doesn't match request (%d) for Wix4HttpSslBinding '%ls'", sczComponent, todoComponent, todoSched, sczId); continue; } hr = WcaGetRecordFormattedString(hRec, hurqHost, &sczHost); - ExitOnFailure(hr, "Failed to get Wix4HttpSslCert.Host"); + ExitOnFailure(hr, "Failed to get Wix4HttpSslBinding.Host"); hr = WcaGetRecordFormattedInteger(hRec, hurqPort, &iPort); - ExitOnFailure(hr, "Failed to get Wix4HttpSslCert.Port"); + ExitOnFailure(hr, "Failed to get Wix4HttpSslBinding.Port"); hr = WcaGetRecordFormattedString(hRec, hurqCertificateThumbprint, &sczCertificateThumbprint); - ExitOnFailure(hr, "Failed to get Wix4HttpSslCert.CertificateThumbprint"); - - hr = WcaGetRecordString(hRec, hurqCertificateLookup, &sczCertificateLookup); - ExitOnFailure(hr, "Failed to get Wix4HttpSslCert.CertificateLookup"); + ExitOnFailure(hr, "Failed to get Wix4HttpSslBinding.CertificateThumbprint"); if (!sczHost || !*sczHost) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Require a Host value for Wix4HttpSslCert '%ls'", sczId); + ExitOnFailure(hr, "Require a Host value for Wix4HttpSslBinding '%ls'", sczId); } if (!iPort) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Require a Port value for Wix4HttpSslCert '%ls'", sczId); + ExitOnFailure(hr, "Require a Port value for Wix4HttpSslBinding '%ls'", sczId); } - /*if (!sczCertificateThumbprint || !*sczCertificateThumbprint) + // XX TODO implement lookup + if (!sczCertificateThumbprint || !*sczCertificateThumbprint) { hr = E_INVALIDARG; - ExitOnFailure(hr, "Require a CertificateThumbprint value for Wix4HttpSslCert '%ls'", sczId); - }*/ + ExitOnFailure(hr, "Require a CertificateThumbprint value for Wix4HttpSslBinding '%ls'", sczId); + } hr = WcaGetRecordFormattedString(hRec, hurqAppId, &sczAppId); - ExitOnFailure(hr, "Failed to get AppId for Wix4HttpSslCert '%ls'", sczId); + ExitOnFailure(hr, "Failed to get AppId for Wix4HttpSslBinding '%ls'", sczId); hr = WcaGetRecordFormattedString(hRec, hurqCertificateStore, &sczCertificateStore); - ExitOnFailure(hr, "Failed to get CertificateStore for Wix4HttpSslCert '%ls'", sczId); + ExitOnFailure(hr, "Failed to get CertificateStore for Wix4HttpSslBinding '%ls'", sczId); hr = WcaGetRecordInteger(hRec, hurqHandleExisting, &iHandleExisting); - ExitOnFailure(hr, "Failed to get HandleExisting for Wix4HttpSslCert '%ls'", sczId); + ExitOnFailure(hr, "Failed to get HandleExisting for Wix4HttpSslBinding '%ls'", sczId); - hr = GetSslCert(sczHost, iPort, &pExistingSslSet); - ExitOnFailure(hr, "Failed to get the existing SSL certificate for Wix4HttpSslCert '%ls'", sczId); + hr = GetSslBinding(sczHost, iPort, &pExistingSslSet); + ExitOnFailure(hr, "Failed to get the existing SSL certificate for Wix4HttpSslBinding '%ls'", sczId); hr = EnsureAppId(&sczAppId, pExistingSslSet); - ExitOnFailure(hr, "Failed to ensure AppId for Wix4HttpSslCert '%ls'", sczId); + ExitOnFailure(hr, "Failed to ensure AppId for Wix4HttpSslBinding '%ls'", sczId); - hr = WriteExistingSslCert(todoComponent, sczId, sczHost, iPort, iHandleExisting, pExistingSslSet, &sczRollbackCustomActionData); - ExitOnFailure(hr, "Failed to write rollback custom action data for Wix4HttpSslCert '%ls'", sczId); + hr = WriteExistingSslBinding(todoComponent, sczId, sczHost, iPort, iHandleExisting, pExistingSslSet, &sczRollbackCustomActionData); + ExitOnFailure(hr, "Failed to write rollback custom action data for Wix4HttpSslBinding '%ls'", sczId); - hr = WriteSslCert(todoComponent, sczId, sczHost, iPort, iHandleExisting, sczCertificateThumbprint, sczCertificateLookup, sczAppId, sczCertificateStore, &sczCustomActionData); - ExitOnFailure(hr, "Failed to write custom action data for Wix4HttpSslCert '%ls'", sczId); + hr = WriteSslBinding(todoComponent, sczId, sczHost, iPort, iHandleExisting, sczCertificateThumbprint, sczAppId, sczCertificateStore, &sczCustomActionData); + ExitOnFailure(hr, "Failed to write custom action data for Wix4HttpSslBinding '%ls'", sczId); ++cCertificates; ReleaseNullMem(pExistingSslSet); @@ -420,32 +387,32 @@ static UINT SchedHttpSslCerts( { hr = S_OK; } - ExitOnFailure(hr, "Failure occurred while processing Wix4HttpSslCert table"); + ExitOnFailure(hr, "Failure occurred while processing Wix4HttpSslBinding table"); // Schedule ExecHttpSslCerts if there's anything to do. if (cCertificates) { - WcaLog(LOGMSG_STANDARD, "Scheduling SSL certificate (%ls)", sczCustomActionData); - WcaLog(LOGMSG_STANDARD, "Scheduling rollback SSL certificate (%ls)", sczRollbackCustomActionData); + WcaLog(LOGMSG_STANDARD, "Scheduling SSL certificate binding (%ls)", sczCustomActionData); + WcaLog(LOGMSG_STANDARD, "Scheduling rollback SSL certificate binding (%ls)", sczRollbackCustomActionData); if (WCA_TODO_INSTALL == todoSched) { - hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackHttpSslCertsInstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SSL); - ExitOnFailure(hr, "Failed to schedule install SSL certificate rollback"); - hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecHttpSslCertsInstall"), sczCustomActionData, cCertificates * COST_HTTP_SSL); - ExitOnFailure(hr, "Failed to schedule install SSL certificate execution"); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackHttpSslBindingsInstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SSL); + ExitOnFailure(hr, "Failed to schedule install SSL certificate binding rollback"); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecHttpSslBindingsInstall"), sczCustomActionData, cCertificates * COST_HTTP_SSL); + ExitOnFailure(hr, "Failed to schedule install SSL certificate binding execution"); } else { - hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackHttpSslCertsUninstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SSL); - ExitOnFailure(hr, "Failed to schedule uninstall SSL certificate rollback"); - hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecHttpSslCertsUninstall"), sczCustomActionData, cCertificates * COST_HTTP_SSL); - ExitOnFailure(hr, "Failed to schedule uninstall SSL certificate execution"); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"RollbackHttpSslBindingsUninstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SSL); + ExitOnFailure(hr, "Failed to schedule uninstall SSL certificate binding rollback"); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION(L"ExecHttpSslBindingsUninstall"), sczCustomActionData, cCertificates * COST_HTTP_SSL); + ExitOnFailure(hr, "Failed to schedule uninstall SSL certificate binding execution"); } } else { - WcaLog(LOGMSG_STANDARD, "No SSL certificates scheduled"); + WcaLog(LOGMSG_STANDARD, "No SSL certificate bindings scheduled"); } LExit: @@ -467,7 +434,7 @@ static UINT SchedHttpSslCerts( return hr; } -static HRESULT WriteExistingSslCert( +static HRESULT WriteExistingSslBinding( __in WCA_TODO action, __in_z LPCWSTR wzId, __in_z LPCWSTR wzHost, @@ -485,16 +452,16 @@ static HRESULT WriteExistingSslCert( if (pSslSet) { hr = StrAllocHexEncode(reinterpret_cast(pSslSet->ParamDesc.pSslHash), pSslSet->ParamDesc.SslHashLength, &sczCertificateThumbprint); - ExitOnFailure(hr, "Failed to convert existing certificate thumbprint to hex for Wix4HttpSslCert '%ls'", wzId); + ExitOnFailure(hr, "Failed to convert existing certificate thumbprint to hex for Wix4HttpSslBinding '%ls'", wzId); hr = StringFromGuid(pSslSet->ParamDesc.AppId, &sczAppId); - ExitOnFailure(hr, "Failed to copy existing AppId for Wix4HttpSslCert '%ls'", wzId); + ExitOnFailure(hr, "Failed to copy existing AppId for Wix4HttpSslBinding '%ls'", wzId); wzCertificateStore = pSslSet->ParamDesc.pSslCertStoreName; } - hr = WriteSslCert(action, wzId, wzHost, iPort, iHandleExisting, sczCertificateThumbprint ? sczCertificateThumbprint : L"", NULL, sczAppId ? sczAppId : L"", wzCertificateStore ? wzCertificateStore : L"", psczCustomActionData); - ExitOnFailure(hr, "Failed to write custom action data for Wix4HttpSslCert '%ls'", wzId); + hr = WriteSslBinding(action, wzId, wzHost, iPort, iHandleExisting, sczCertificateThumbprint ? sczCertificateThumbprint : L"", sczAppId ? sczAppId : L"", wzCertificateStore ? wzCertificateStore : L"", psczCustomActionData); + ExitOnFailure(hr, "Failed to write custom action data for Wix4HttpSslBinding '%ls'", wzId); LExit: ReleaseStr(sczAppId); @@ -503,14 +470,13 @@ static HRESULT WriteExistingSslCert( return hr; } -static HRESULT WriteSslCert( +static HRESULT WriteSslBinding( __in WCA_TODO action, __in_z LPCWSTR wzId, __in_z LPCWSTR wzHost, __in int iPort, __in int iHandleExisting, __in_z LPCWSTR wzCertificateThumbprint, - __in_z LPCWSTR wzCertificateLookup, __in_z LPCWSTR wzAppId, __in_z_opt LPCWSTR wzCertificateStore, __inout_z LPWSTR* psczCustomActionData @@ -536,9 +502,6 @@ static HRESULT WriteSslCert( hr = WcaWriteStringToCaData(wzCertificateThumbprint ? wzCertificateThumbprint : L"", psczCustomActionData); ExitOnFailure(hr, "Failed to write CertificateThumbprint to custom action data"); - hr = WcaWriteStringToCaData(wzCertificateLookup ? wzCertificateLookup : L"", psczCustomActionData); - ExitOnFailure(hr, "Failed to write CertificateLookup to custom action data"); - hr = WcaWriteStringToCaData(wzAppId, psczCustomActionData); ExitOnFailure(hr, "Failed to write AppId to custom action data"); @@ -601,7 +564,7 @@ static HRESULT StringFromGuid( return hr; } -static HRESULT AddSslCert( +static HRESULT AddSslBinding( __in_z LPCWSTR /*wzId*/, __in_z LPWSTR wzHost, __in int iPort, @@ -617,7 +580,7 @@ static HRESULT AddSslCert( SOCKADDR_STORAGE addr = { }; set.KeyDesc.pIpPort = reinterpret_cast(&addr); - SetSslCertSetKey(&set.KeyDesc, wzHost, iPort); + SetSslBindingSetKey(&set.KeyDesc, wzHost, iPort); set.ParamDesc.SslHashLength = cbCertificateThumbprint; set.ParamDesc.pSslHash = rgbCertificateThumbprint; set.ParamDesc.AppId = *pAppId; @@ -636,7 +599,7 @@ static HRESULT AddSslCert( return hr; } -static HRESULT GetSslCert( +static HRESULT GetSslBinding( __in_z LPWSTR wzHost, __in int nPort, __out HTTP_SERVICE_CONFIG_SSL_SET** ppSet @@ -653,7 +616,7 @@ static HRESULT GetSslCert( query.QueryDesc = HttpServiceConfigQueryExact; query.KeyDesc.pIpPort = reinterpret_cast(&addr); - SetSslCertSetKey(&query.KeyDesc, wzHost, nPort); + SetSslBindingSetKey(&query.KeyDesc, wzHost, nPort); er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigSSLCertInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL); if (ERROR_INSUFFICIENT_BUFFER == er) @@ -684,7 +647,7 @@ static HRESULT GetSslCert( return hr; } -static HRESULT RemoveSslCert( +static HRESULT RemoveSslBinding( __in_z LPCWSTR /*wzId*/, __in_z LPWSTR wzHost, __in int iPort @@ -696,7 +659,7 @@ static HRESULT RemoveSslCert( SOCKADDR_STORAGE addr = { }; set.KeyDesc.pIpPort = reinterpret_cast(&addr); - SetSslCertSetKey(&set.KeyDesc, wzHost, iPort); + SetSslBindingSetKey(&set.KeyDesc, wzHost, iPort); er = ::HttpDeleteServiceConfiguration(NULL, HttpServiceConfigSSLCertInfo, &set, sizeof(set), NULL); if (ERROR_FILE_NOT_FOUND == er) @@ -711,7 +674,7 @@ static HRESULT RemoveSslCert( return hr; } -static HRESULT SetSslCertSetKey( +static HRESULT SetSslBindingSetKey( __in HTTP_SERVICE_CONFIG_SSL_KEY* pKey, __in_z LPWSTR wzHost, __in int iPort @@ -730,79 +693,3 @@ static HRESULT SetSslCertSetKey( HRESULT hr = HRESULT_FROM_WIN32(er); return hr; } - -static HRESULT FindExistingCertificate( - __in LPCWSTR wzName, - __in DWORD dwStoreLocation, - __in LPCWSTR wzStore, - __out BYTE** ppbCertificateThumbprint, - __out DWORD* pcbCertificateThumbprint -) -{ - HRESULT hr = S_FALSE; - HCERTSTORE hCertStore = NULL; - PCCERT_CONTEXT pCertContext = NULL; - BYTE* pbCertificateThumbprint = NULL; - DWORD cbCertificateThumbprint = 0; - - hCertStore = ::CertOpenStore(CERT_STORE_PROV_SYSTEM, 0, NULL, dwStoreLocation | CERT_STORE_READONLY_FLAG, wzStore); - MessageExitOnNullWithLastError(hCertStore, hr, msierrCERTFailedOpen, "Failed to open certificate store."); - - // Loop through the certificate, looking for certificates that match our friendly name. - pCertContext = CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); - while (pCertContext) - { - WCHAR wzFriendlyName[256] = { 0 }; - DWORD cbFriendlyName = sizeof(wzFriendlyName); - - if (::CertGetCertificateContextProperty(pCertContext, CERT_FRIENDLY_NAME_PROP_ID, reinterpret_cast(wzFriendlyName), &cbFriendlyName)) - { - LPCWSTR wzFound = wcsistr(wzFriendlyName, wzName); - if (wzFound && wzFound == wzFriendlyName) - { - // If the certificate with matching friendly name is valid, let's use that. - long lVerify = ::CertVerifyTimeValidity(NULL, pCertContext->pCertInfo); - if (0 == lVerify) - { - byte thumb[64] = { 0 }; - cbCertificateThumbprint = sizeof(thumb); - if (!CertGetCertificateContextProperty(pCertContext, CERT_HASH_PROP_ID, thumb, &cbCertificateThumbprint)) - { - ExitFunctionWithLastError(hr); - } - - pbCertificateThumbprint = static_cast(MemAlloc(cbCertificateThumbprint, FALSE)); - ExitOnNull(pbCertificateThumbprint, hr, E_OUTOFMEMORY, "Failed to allocate memory to copy out exist certificate thumbprint."); - - CopyMemory(pbCertificateThumbprint, thumb, cbCertificateThumbprint); - hr = S_OK; - break; // found a matching certificate, no more searching necessary - } - } - } - - // Next certificate in the store. - PCCERT_CONTEXT pNext = ::CertFindCertificateInStore(hCertStore, PKCS_7_ASN_ENCODING | X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, pCertContext); - // old pCertContext is freed by CertFindCertificateInStore - pCertContext = pNext; - } - - *ppbCertificateThumbprint = pbCertificateThumbprint; - *pcbCertificateThumbprint = cbCertificateThumbprint; - pbCertificateThumbprint = NULL; - -LExit: - ReleaseMem(pbCertificateThumbprint); - - if (pCertContext) - { - ::CertFreeCertificateContext(pCertContext); - } - - if (hCertStore) - { - ::CertCloseStore(hCertStore, 0); - } - - return hr; -} diff --git a/src/ext/Http/ca/wixhttpca.def b/src/ext/Http/ca/wixhttpca.def index 2632a2220..87fb3b348 100644 --- a/src/ext/Http/ca/wixhttpca.def +++ b/src/ext/Http/ca/wixhttpca.def @@ -10,6 +10,6 @@ EXPORTS SchedHttpSniSslCertsInstall SchedHttpSniSslCertsUninstall ExecHttpSniSslCerts - SchedHttpSslCertsInstall - SchedHttpSslCertsUninstall - ExecHttpSslCerts + SchedHttpSslBindingsInstall + SchedHttpSslBindingsUninstall + ExecHttpSslBindings diff --git a/src/ext/Http/test/WixToolsetTest.Http/HttpExtensionFixture.cs b/src/ext/Http/test/WixToolsetTest.Http/HttpExtensionFixture.cs index c28053159..bc376a552 100644 --- a/src/ext/Http/test/WixToolsetTest.Http/HttpExtensionFixture.cs +++ b/src/ext/Http/test/WixToolsetTest.Http/HttpExtensionFixture.cs @@ -29,22 +29,25 @@ public void CanBuildUsingSniSssl() } [Fact] - public void CanBuildUsingSsl() + public void CanBuildUsingSslBinding() { var folder = TestData.Get("TestData", "Ssl"); var build = new Builder(folder, typeof(HttpExtensionFactory), new[] { folder }); - var results = build.BuildAndQuery(Build, "CustomAction", "Wix4HttpSslCert"); + var results = build.BuildAndQuery(Build, "CustomAction", "Wix4HttpSslCertificate", "Wix4HttpSslBinding", "Wix4HttpSslBindingCertificates"); WixAssert.CompareLineByLine(new[] { "CustomAction:Wix4ExecHttpSslCertsInstall_X86\t11265\tWix4HttpCA_X86\tExecHttpSslCerts\t", "CustomAction:Wix4ExecHttpSslCertsUninstall_X86\t11265\tWix4HttpCA_X86\tExecHttpSslCerts\t", + "CustomAction:Wix4InstallHttpCertificates_X86\t1\tWix4HttpCA_X86\tInstallCertificates\t", "CustomAction:Wix4RollbackHttpSslCertsInstall_X86\t11521\tWix4HttpCA_X86\tExecHttpSslCerts\t", "CustomAction:Wix4RollbackHttpSslCertsUninstall_X86\t11521\tWix4HttpCA_X86\tExecHttpSslCerts\t", "CustomAction:Wix4SchedHttpSslCertsInstall_X86\t8193\tWix4HttpCA_X86\tSchedHttpSslCertsInstall\t", "CustomAction:Wix4SchedHttpSslCertsUninstall_X86\t8193\tWix4HttpCA_X86\tSchedHttpSslCertsUninstall\t", - "Wix4HttpSslCert:ssle0Wgg93FXwXdLjwZWqv0HKBhhKE\t0.0.0.0\t8080\t[SOME_THUMBPRINT]\t\t\t\t2\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo", - "Wix4HttpSslCert:ssltjEpdUFkxO7rNF2TrXuGLJg5NwE\t0.0.0.0\t8081\t\t1234\t\t\t2\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo", + "CustomAction:Wix4UninstallHttpCertificates_X86\t1\tWix4HttpCA_X86\tUninstallCertificates\t", + "Wix4HttpSslBinding:ssltjEpdUFkxO7rNF2TrXuGLJg5NwE\t0.0.0.0\t8081\t\t\t\t2\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo", + "Wix4HttpSslBindingCertificates:ssltjEpdUFkxO7rNF2TrXuGLJg5NwE\tSomeCertificate", + "Wix4HttpSslCertificate:SomeCertificate\tfilF5_pLhBuF5b4N9XEo52g_hUM5Lo\tSome Certificate\t2\tMY\t8\t\t[SOME_PATH]\t[PFX_PASS]", }, results); } diff --git a/src/ext/Http/test/WixToolsetTest.Http/TestData/Ssl/PackageComponents.wxs b/src/ext/Http/test/WixToolsetTest.Http/TestData/Ssl/PackageComponents.wxs index d8fc2b318..5f5f1e274 100644 --- a/src/ext/Http/test/WixToolsetTest.Http/TestData/Ssl/PackageComponents.wxs +++ b/src/ext/Http/test/WixToolsetTest.Http/TestData/Ssl/PackageComponents.wxs @@ -5,8 +5,15 @@ - - + + + + diff --git a/src/ext/Http/wixext/HttpCompiler.cs b/src/ext/Http/wixext/HttpCompiler.cs index 7d669ce39..3e19be199 100644 --- a/src/ext/Http/wixext/HttpCompiler.cs +++ b/src/ext/Http/wixext/HttpCompiler.cs @@ -9,6 +9,7 @@ namespace WixToolset.Http using WixToolset.Extensibility; using WixToolset.Extensibility.Data; using WixToolset.Http.Symbols; + using static System.Net.Mime.MediaTypeNames; /// /// The compiler for the WiX Toolset Http Extension. @@ -52,8 +53,12 @@ public override void ParseElement(Intermediate intermediate, IntermediateSection this.ParseSniSslCertificateElement(intermediate, section, element, componentId); break; - case "SslCertificate": - this.ParseSslCertificateElement(intermediate, section, element, componentId); + case "SslBinding": + this.ParseSslBindingElement(intermediate, section, element, componentId); + break; + + case "Certificate": + this.ParseCertificateElement(intermediate, section, element, componentId); break; case "UrlReservation": @@ -186,8 +191,8 @@ private void ParseSniSslCertificateElement(Intermediate intermediate, Intermedia /// Parses a Ssl element. /// /// The element to parse. - /// Identifier of the component that owns this SNI SSL Certificate. - private void ParseSslCertificateElement(Intermediate intermediate, IntermediateSection section, XElement node, string componentId) + /// Identifier of the component that owns this SSL Certificate. + private void ParseSslBindingElement(Intermediate intermediate, IntermediateSection section, XElement node, string componentId) { var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(node); Identifier id = null; @@ -195,8 +200,8 @@ private void ParseSslCertificateElement(Intermediate intermediate, IntermediateS string port = null; string appId = null; string store = null; + string certificateRef = null; string thumbprint = null; - string certificateLookup = null; var handleExisting = HandleExisting.Replace; foreach (var attrib in node.Attributes()) @@ -235,14 +240,11 @@ private void ParseSslCertificateElement(Intermediate intermediate, IntermediateS case "Port": port = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; - case "Store": - store = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); - break; case "Thumbprint": thumbprint = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; - case "CertificateLookup": - certificateLookup = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + case "Store": + store = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); break; default: this.ParseHelper.UnexpectedAttribute(node, attrib); @@ -272,27 +274,54 @@ private void ParseSslCertificateElement(Intermediate intermediate, IntermediateS this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Port")); } - if (null != certificateLookup && null != thumbprint) + foreach (var child in node.Elements()) { - this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Thumbprint", "CertificateLookup")); + if (this.Namespace == child.Name.Namespace) + { + var childSourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(child); + switch (child.Name.LocalName) + { + case "CertificateRef": + if (null != thumbprint) + { + this.Messaging.Write(ErrorMessages.UnexpectedElementWithAttribute(childSourceLineNumbers, node.Name.LocalName, child.Name.LocalName, "Thumbprint")); + } + + if (null == componentId) + { + this.Messaging.Write(HttpErrors.IllegalElementWithoutComponent(childSourceLineNumbers, child.Name.LocalName)); + } + certificateRef = this.ParseCertificateRefElement(intermediate, section, child, id?.Id); + + if (null == certificateRef) + { + this.Messaging.Write(ErrorMessages.ExpectedElement(sourceLineNumbers, node.Name.LocalName, "CertificateRef")); + } + + break; + default: + this.ParseHelper.UnexpectedElement(node, child); + break; + } + } + else + { + this.ParseHelper.ParseExtensionElement(this.Context.Extensions, intermediate, section, node, child); + } } - if (null == thumbprint && null == certificateLookup) + if (null == thumbprint && certificateRef == null) { - this.Messaging.Write(ErrorMessages.ExpectedAttributeWithoutOtherAttribute(sourceLineNumbers, node.Name.LocalName, "Thumbprint", "CertificateLookup")); + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, node.Name.LocalName, "Thumbprint")); } - // Parse unknown children. - this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, node); - if (!this.Messaging.EncounteredError) { - section.AddSymbol(new WixHttpSslCertSymbol(sourceLineNumbers, id) + section.AddSymbol(new WixHttpSslBindingSymbol(sourceLineNumbers, id) { Host = host, Port = port, Thumbprint = thumbprint, - CertificateLookup = certificateLookup, AppId = appId, Store = store, HandleExisting = handleExisting, @@ -304,6 +333,192 @@ private void ParseSslCertificateElement(Intermediate intermediate, IntermediateS } } + /// + /// Parses a certificate element. + /// + /// Element to parse. + /// Identifier for parent component. + private void ParseCertificateElement(Intermediate intermediate, IntermediateSection section, XElement element, string componentId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + int attributes = 8; // SCA_CERT_ATTRIBUTE_VITAL + string binaryRef = null; + string certificatePath = null; + string name = null; + string pfxPassword = null; + int storeLocation = 0; + string storeName = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + break; + case "BinaryRef": + attributes |= 2; // SCA_CERT_ATTRIBUTE_BINARYDATA + binaryRef = this.ParseHelper.GetAttributeIdentifierValue(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, SymbolDefinitions.Binary, binaryRef); + break; + case "CertificatePath": + certificatePath = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Name": + name = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Overwrite": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 4; // SCA_CERT_ATTRIBUTE_OVERWRITE + } + else + { + attributes &= ~4; // SCA_CERT_ATTRIBUTE_OVERWRITE + } + break; + case "PFXPassword": + pfxPassword = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + break; + case "Request": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 1; // SCA_CERT_ATTRIBUTE_REQUEST + } + else + { + attributes &= ~1; // SCA_CERT_ATTRIBUTE_REQUEST + } + break; + case "StoreLocation": + var storeLocationValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < storeLocationValue.Length) + { + switch (storeLocationValue) + { + case "currentUser": + storeLocation = 1; // SCA_CERTSYSTEMSTORE_CURRENTUSER + break; + case "localMachine": + storeLocation = 2; // SCA_CERTSYSTEMSTORE_LOCALMACHINE + break; + default: + storeLocation = -1; + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "StoreLocation", storeLocationValue, "currentUser", "localMachine")); + break; + } + } + break; + case "StoreName": + var storeNameValue = this.ParseHelper.GetAttributeValue(sourceLineNumbers, attrib); + if (0 < storeNameValue.Length) + { + switch (storeNameValue) + { + case "ca": + storeName = "CA"; + break; + case "my": + case "personal": + storeName = "MY"; + break; + case "request": + storeName = "REQUEST"; + break; + case "root": + storeName = "Root"; + break; + case "otherPeople": + storeName = "AddressBook"; + break; + case "trustedPeople": + storeName = "TrustedPeople"; + break; + case "trustedPublisher": + storeName = "TrustedPublisher"; + break; + default: + this.Messaging.Write(ErrorMessages.IllegalAttributeValue(sourceLineNumbers, element.Name.LocalName, "StoreName", storeNameValue, "ca", "my", "request", "root", "otherPeople", "trustedPeople", "trustedPublisher")); + break; + } + } + break; + case "Vital": + if (YesNoType.Yes == this.ParseHelper.GetAttributeYesNoValue(sourceLineNumbers, attrib)) + { + attributes |= 8; // SCA_CERT_ATTRIBUTE_VITAL + } + else + { + attributes &= ~8; // SCA_CERT_ATTRIBUTE_VITAL + } + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + id = this.ParseHelper.CreateIdentifier("crt", componentId, binaryRef, certificatePath); + } + + if (null == name) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Name")); + } + + if (0 == storeLocation) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "StoreLocation")); + } + + if (null == storeName) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "StoreName")); + } + + if (null != binaryRef && null != certificatePath) + { + this.Messaging.Write(ErrorMessages.IllegalAttributeWithOtherAttribute(sourceLineNumbers, element.Name.LocalName, "BinaryRef", "CertificatePath", certificatePath)); + } + else if (null == binaryRef && null == certificatePath) + { + this.Messaging.Write(ErrorMessages.ExpectedAttributes(sourceLineNumbers, element.Name.LocalName, "BinaryRef", "CertificatePath")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + // Reference InstallCertificates and UninstallCertificates since nothing will happen without them + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4InstallHttpCertificates", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + this.ParseHelper.CreateCustomActionReference(sourceLineNumbers, section, "Wix4UninstallHttpCertificates", this.Context.Platform, CustomActionPlatforms.X86 | CustomActionPlatforms.X64 | CustomActionPlatforms.ARM64); + //XX this.ParseHelper.EnsureTable(section, sourceLineNumbers, "Wix4CertificateHash"); // Certificate CustomActions require the CertificateHash table + + if (!this.Messaging.EncounteredError) + { + section.AddSymbol(new WixHttpCertificateSymbol(sourceLineNumbers, id) + { + ComponentRef = componentId, + Name = name, + StoreLocation = storeLocation, + StoreName = storeName, + Attributes = attributes, + BinaryRef = binaryRef, + CertificatePath = certificatePath, + PfxPassword = pfxPassword, + }); + } + } + /// /// Parses a UrlReservation element. /// @@ -504,5 +719,57 @@ private void ParseUrlAceElement(Intermediate intermediate, IntermediateSection s }); } } + + /// + /// Parses a CertificateRef extension element. + /// + /// Element to parse. + /// Identifier for parent Ssl binding. + private string ParseCertificateRefElement(Intermediate intermediate, IntermediateSection section, XElement element, string bindingId) + { + var sourceLineNumbers = this.ParseHelper.GetSourceLineNumbers(element); + Identifier id = null; + + foreach (var attrib in element.Attributes()) + { + if (String.IsNullOrEmpty(attrib.Name.NamespaceName) || this.Namespace == attrib.Name.Namespace) + { + switch (attrib.Name.LocalName) + { + case "Id": + id = this.ParseHelper.GetAttributeIdentifier(sourceLineNumbers, attrib); + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, HttpSymbolDefinitions.WixHttpCertificate, id.Id); + break; + default: + this.ParseHelper.UnexpectedAttribute(element, attrib); + break; + } + } + else + { + this.ParseHelper.ParseExtensionAttribute(this.Context.Extensions, intermediate, section, element, attrib); + } + } + + if (null == id) + { + this.Messaging.Write(ErrorMessages.ExpectedAttribute(sourceLineNumbers, element.Name.LocalName, "Id")); + } + + this.ParseHelper.ParseForExtensionElements(this.Context.Extensions, intermediate, section, element); + + if (!this.Messaging.EncounteredError) + { + this.ParseHelper.CreateSimpleReference(section, sourceLineNumbers, HttpSymbolDefinitions.WixHttpCertificate, id.Id); + + section.AddSymbol(new WixHttpSslBindingCertificateSymbol(sourceLineNumbers) + { + BindingRef = bindingId, + CertificateRef = id.Id, + }); + } + + return id?.Id; + } } } diff --git a/src/ext/Http/wixext/HttpErrors.cs b/src/ext/Http/wixext/HttpErrors.cs index e87adf544..07967e79e 100644 --- a/src/ext/Http/wixext/HttpErrors.cs +++ b/src/ext/Http/wixext/HttpErrors.cs @@ -23,9 +23,15 @@ private static Message Message(SourceLineNumber sourceLineNumber, Ids id, Resour return new Message(sourceLineNumber, MessageLevel.Error, (int)id, resourceManager, resourceName, args); } + public static Message IllegalElementWithoutComponent(SourceLineNumber sourceLineNumbers, string elementName) + { + return Message(sourceLineNumbers, Ids.IllegalElementWithoutComponent, "The {0} element cannot be specified unless the element has a Component as an ancestor. A {0} that does not have a Component ancestor is not installed.", elementName); + } + public enum Ids { NoSecuritySpecified = 6701, + IllegalElementWithoutComponent = 6721, } } } diff --git a/src/ext/Http/wixext/HttpTableDefinitions.cs b/src/ext/Http/wixext/HttpTableDefinitions.cs index 892685f99..573bf832b 100644 --- a/src/ext/Http/wixext/HttpTableDefinitions.cs +++ b/src/ext/Http/wixext/HttpTableDefinitions.cs @@ -23,16 +23,15 @@ public static class HttpTableDefinitions symbolIdIsPrimaryKey: true ); - public static readonly TableDefinition WixHttpSslCert = new TableDefinition( - "Wix4HttpSslCert", - HttpSymbolDefinitions.WixHttpSslCert, + public static readonly TableDefinition WixHttpSslBinding = new TableDefinition( + "Wix4HttpSslBinding", + HttpSymbolDefinitions.WixHttpSslBinding, new[] { - new ColumnDefinition("WixHttpSslCert", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "The non-localized primary key for the table.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("WixHttpSslBinding", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, description: "The non-localized primary key for the table.", modularizeType: ColumnModularizeType.Column), new ColumnDefinition("Host", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Host for the SSL certificate.", modularizeType: ColumnModularizeType.Property), new ColumnDefinition("Port", ColumnType.String, 0, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Port for the SSL certificate.", modularizeType: ColumnModularizeType.Property), new ColumnDefinition("Thumbprint", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Thumbprint of the SSL certificate to find.", modularizeType: ColumnModularizeType.Property), - new ColumnDefinition("Certificate_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Unknown, description: "The index into the Certificate table."), new ColumnDefinition("AppId", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Optional application id for the SSL certificate.", modularizeType: ColumnModularizeType.Property), new ColumnDefinition("Store", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Certificate store containing the SSL certificate.", modularizeType: ColumnModularizeType.Property), new ColumnDefinition("HandleExisting", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 2, description: "The behavior when trying to install a SSL certificate and it already exists."), @@ -41,6 +40,35 @@ public static class HttpTableDefinitions symbolIdIsPrimaryKey: true ); + public static readonly TableDefinition WixHttpSslBindingCertificates = new TableDefinition( + "Wix4HttpSslBindingCertificates", + HttpSymbolDefinitions.WixHttpSslBindingCertificates, + new[] + { + new ColumnDefinition("Binding_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyTable: "Wix4HttpSslBinding", keyColumn: 1, description: "The index into the SslBinding table.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Certificate_", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Text, keyTable: "Wix4HttpSslBindingificate", keyColumn: 1, description: "The index into the Certificate table.", modularizeType: ColumnModularizeType.Column), + }, + symbolIdIsPrimaryKey: false + ); + + public static readonly TableDefinition WixHttpSslCertificate = new TableDefinition( + "Wix4HttpSslCertificate", + HttpSymbolDefinitions.WixHttpCertificate, + new[] + { + new ColumnDefinition("Certificate", ColumnType.String, 72, primaryKey: true, nullable: false, ColumnCategory.Identifier, keyColumn: 1, description: "Identifier for the certificate in the package.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Component_", ColumnType.String, 72, primaryKey: false, nullable: false, ColumnCategory.Identifier, description: "Foreign key into the Component table used to determine install state", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("Name", ColumnType.String, 255, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name to be used for the Certificate."), + new ColumnDefinition("StoreLocation", ColumnType.Number, 2, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 1, maxValue: 2, description: "Location of the target certificate store (CurrentUser == 1, LocalMachine == 2)"), + new ColumnDefinition("StoreName", ColumnType.String, 64, primaryKey: false, nullable: false, ColumnCategory.Formatted, description: "Name of the target certificate store"), + new ColumnDefinition("Attributes", ColumnType.Number, 4, primaryKey: false, nullable: false, ColumnCategory.Unknown, minValue: 0, maxValue: 2147483647, description: "Attributes of the certificate"), + new ColumnDefinition("Binary_", ColumnType.String, 72, primaryKey: false, nullable: true, ColumnCategory.Identifier, keyTable: "Binary", keyColumn: 1, description: "Identifier to Binary table containing certificate.", modularizeType: ColumnModularizeType.Column), + new ColumnDefinition("CertificatePath", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Property to path of certificate.", modularizeType: ColumnModularizeType.Property), + new ColumnDefinition("PFXPassword", ColumnType.String, 0, primaryKey: false, nullable: true, ColumnCategory.Formatted, description: "Hidden property to a pfx password", modularizeType: ColumnModularizeType.Property), + }, + symbolIdIsPrimaryKey: true + ); + public static readonly TableDefinition WixHttpUrlReservation = new TableDefinition( "Wix4HttpUrlReservation", HttpSymbolDefinitions.WixHttpUrlReservation, @@ -73,7 +101,9 @@ public static class HttpTableDefinitions WixHttpSniSslCert, WixHttpUrlReservation, WixHttpUrlAce, - WixHttpSslCert, + WixHttpSslCertificate, + WixHttpSslBinding, + WixHttpSslBindingCertificates, }; } } diff --git a/src/ext/Http/wixext/Symbols/HttpSymbolDefinitions.cs b/src/ext/Http/wixext/Symbols/HttpSymbolDefinitions.cs index db41c3dad..823f19837 100644 --- a/src/ext/Http/wixext/Symbols/HttpSymbolDefinitions.cs +++ b/src/ext/Http/wixext/Symbols/HttpSymbolDefinitions.cs @@ -10,7 +10,9 @@ public enum HttpSymbolDefinitionType WixHttpSniSslCert, WixHttpUrlAce, WixHttpUrlReservation, - WixHttpSslCert, + WixHttpSslBinding, + WixHttpCertificate, + WixHttpSslBindingCertificates, } public static partial class HttpSymbolDefinitions @@ -34,8 +36,8 @@ public static IntermediateSymbolDefinition ByType(HttpSymbolDefinitionType type) case HttpSymbolDefinitionType.WixHttpSniSslCert: return HttpSymbolDefinitions.WixHttpSniSslCert; - case HttpSymbolDefinitionType.WixHttpSslCert: - return HttpSymbolDefinitions.WixHttpSslCert; + case HttpSymbolDefinitionType.WixHttpSslBinding: + return HttpSymbolDefinitions.WixHttpSslBinding; case HttpSymbolDefinitionType.WixHttpUrlAce: return HttpSymbolDefinitions.WixHttpUrlAce; @@ -43,6 +45,12 @@ public static IntermediateSymbolDefinition ByType(HttpSymbolDefinitionType type) case HttpSymbolDefinitionType.WixHttpUrlReservation: return HttpSymbolDefinitions.WixHttpUrlReservation; + case HttpSymbolDefinitionType.WixHttpCertificate: + return HttpSymbolDefinitions.WixHttpCertificate; + + case HttpSymbolDefinitionType.WixHttpSslBindingCertificates: + return HttpSymbolDefinitions.WixHttpSslBindingCertificates; + default: throw new ArgumentOutOfRangeException(nameof(type)); } diff --git a/src/ext/Http/wixext/Symbols/WixHttpCertificateSymbol.cs b/src/ext/Http/wixext/Symbols/WixHttpCertificateSymbol.cs new file mode 100644 index 000000000..ecabdb0ef --- /dev/null +++ b/src/ext/Http/wixext/Symbols/WixHttpCertificateSymbol.cs @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Http +{ + using WixToolset.Data; + using WixToolset.Http.Symbols; + + public static partial class HttpSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition WixHttpCertificate = new IntermediateSymbolDefinition( + HttpSymbolDefinitionType.WixHttpCertificate.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.ComponentRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.Name), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.StoreLocation), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.StoreName), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.Attributes), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.BinaryRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.CertificatePath), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(HttpCertificateSymbolFields.PfxPassword), IntermediateFieldType.String), + }, + typeof(WixHttpCertificateSymbol)); + } +} + +namespace WixToolset.Http.Symbols +{ + using WixToolset.Data; + using WixToolset.Http; + + public enum HttpCertificateSymbolFields + { + ComponentRef, + Name, + StoreLocation, + StoreName, + Attributes, + BinaryRef, + CertificatePath, + PfxPassword, + } + + public class WixHttpCertificateSymbol : IntermediateSymbol + { + public WixHttpCertificateSymbol() : base(HttpSymbolDefinitions.WixHttpCertificate, null, null) + { + } + + public WixHttpCertificateSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(HttpSymbolDefinitions.WixHttpCertificate, sourceLineNumber, id) + { + } + + public IntermediateField this[HttpCertificateSymbolFields index] => this.Fields[(int)index]; + + public string ComponentRef + { + get => this.Fields[(int)HttpCertificateSymbolFields.ComponentRef].AsString(); + set => this.Set((int)HttpCertificateSymbolFields.ComponentRef, value); + } + + public string Name + { + get => this.Fields[(int)HttpCertificateSymbolFields.Name].AsString(); + set => this.Set((int)HttpCertificateSymbolFields.Name, value); + } + + public int StoreLocation + { + get => this.Fields[(int)HttpCertificateSymbolFields.StoreLocation].AsNumber(); + set => this.Set((int)HttpCertificateSymbolFields.StoreLocation, value); + } + + public string StoreName + { + get => this.Fields[(int)HttpCertificateSymbolFields.StoreName].AsString(); + set => this.Set((int)HttpCertificateSymbolFields.StoreName, value); + } + + public int Attributes + { + get => this.Fields[(int)HttpCertificateSymbolFields.Attributes].AsNumber(); + set => this.Set((int)HttpCertificateSymbolFields.Attributes, value); + } + + public string BinaryRef + { + get => this.Fields[(int)HttpCertificateSymbolFields.BinaryRef].AsString(); + set => this.Set((int)HttpCertificateSymbolFields.BinaryRef, value); + } + + public string CertificatePath + { + get => this.Fields[(int)HttpCertificateSymbolFields.CertificatePath].AsString(); + set => this.Set((int)HttpCertificateSymbolFields.CertificatePath, value); + } + + public string PfxPassword + { + get => this.Fields[(int)HttpCertificateSymbolFields.PfxPassword].AsString(); + set => this.Set((int)HttpCertificateSymbolFields.PfxPassword, value); + } + } +} diff --git a/src/ext/Http/wixext/Symbols/WixHttpSslBindingCertificateSymbol.cs b/src/ext/Http/wixext/Symbols/WixHttpSslBindingCertificateSymbol.cs new file mode 100644 index 000000000..6fab5a5b3 --- /dev/null +++ b/src/ext/Http/wixext/Symbols/WixHttpSslBindingCertificateSymbol.cs @@ -0,0 +1,55 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Http +{ + using WixToolset.Data; + using WixToolset.Http.Symbols; + + public static partial class HttpSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition WixHttpSslBindingCertificates = new IntermediateSymbolDefinition( + HttpSymbolDefinitionType.WixHttpSslBindingCertificates.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(HttpSslBindingCertificatesSymbolFields.BindingRef), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(HttpSslBindingCertificatesSymbolFields.CertificateRef), IntermediateFieldType.String), + }, + typeof(WixHttpSslBindingCertificateSymbol)); + } +} + +namespace WixToolset.Http.Symbols +{ + using WixToolset.Data; + + public enum HttpSslBindingCertificatesSymbolFields + { + BindingRef, + CertificateRef, + } + + public class WixHttpSslBindingCertificateSymbol : IntermediateSymbol + { + public WixHttpSslBindingCertificateSymbol() : base(HttpSymbolDefinitions.WixHttpSslBindingCertificates, null, null) + { + } + + public WixHttpSslBindingCertificateSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(HttpSymbolDefinitions.WixHttpSslBindingCertificates, sourceLineNumber, id) + { + } + + public IntermediateField this[HttpSslBindingCertificatesSymbolFields index] => this.Fields[(int)index]; + + public string BindingRef + { + get => this.Fields[(int)HttpSslBindingCertificatesSymbolFields.BindingRef].AsString(); + set => this.Set((int)HttpSslBindingCertificatesSymbolFields.BindingRef, value); + } + + public string CertificateRef + { + get => this.Fields[(int)HttpSslBindingCertificatesSymbolFields.CertificateRef].AsString(); + set => this.Set((int)HttpSslBindingCertificatesSymbolFields.CertificateRef, value); + } + } +} diff --git a/src/ext/Http/wixext/Symbols/WixHttpSslBindingSymbol.cs b/src/ext/Http/wixext/Symbols/WixHttpSslBindingSymbol.cs new file mode 100644 index 000000000..a0f7f0d58 --- /dev/null +++ b/src/ext/Http/wixext/Symbols/WixHttpSslBindingSymbol.cs @@ -0,0 +1,95 @@ +// Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information. + +namespace WixToolset.Http +{ + using WixToolset.Data; + using WixToolset.Http.Symbols; + + public static partial class HttpSymbolDefinitions + { + public static readonly IntermediateSymbolDefinition WixHttpSslBinding = new IntermediateSymbolDefinition( + HttpSymbolDefinitionType.WixHttpSslBinding.ToString(), + new[] + { + new IntermediateFieldDefinition(nameof(WixHttpSslBindingSymbolFields.Host), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixHttpSslBindingSymbolFields.Port), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixHttpSslBindingSymbolFields.Thumbprint), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixHttpSslBindingSymbolFields.AppId), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixHttpSslBindingSymbolFields.Store), IntermediateFieldType.String), + new IntermediateFieldDefinition(nameof(WixHttpSslBindingSymbolFields.HandleExisting), IntermediateFieldType.Number), + new IntermediateFieldDefinition(nameof(WixHttpSslBindingSymbolFields.ComponentRef), IntermediateFieldType.String), + }, + typeof(WixHttpSslBindingSymbol)); + } +} + +namespace WixToolset.Http.Symbols +{ + using WixToolset.Data; + + public enum WixHttpSslBindingSymbolFields + { + Host, + Port, + Thumbprint, + AppId, + Store, + HandleExisting, + ComponentRef, + } + + public class WixHttpSslBindingSymbol : IntermediateSymbol + { + public WixHttpSslBindingSymbol() : base(HttpSymbolDefinitions.WixHttpSslBinding, null, null) + { + } + + public WixHttpSslBindingSymbol(SourceLineNumber sourceLineNumber, Identifier id = null) : base(HttpSymbolDefinitions.WixHttpSslBinding, sourceLineNumber, id) + { + } + + public IntermediateField this[WixHttpSslBindingSymbolFields index] => this.Fields[(int)index]; + + public string Host + { + get => this.Fields[(int)WixHttpSslBindingSymbolFields.Host].AsString(); + set => this.Set((int)WixHttpSslBindingSymbolFields.Host, value); + } + + public string Port + { + get => this.Fields[(int)WixHttpSslBindingSymbolFields.Port].AsString(); + set => this.Set((int)WixHttpSslBindingSymbolFields.Port, value); + } + + public string Thumbprint + { + get => this.Fields[(int)WixHttpSslBindingSymbolFields.Thumbprint].AsString(); + set => this.Set((int)WixHttpSslBindingSymbolFields.Thumbprint, value); + } + + public string AppId + { + get => this.Fields[(int)WixHttpSslBindingSymbolFields.AppId].AsString(); + set => this.Set((int)WixHttpSslBindingSymbolFields.AppId, value); + } + + public string Store + { + get => this.Fields[(int)WixHttpSslBindingSymbolFields.Store].AsString(); + set => this.Set((int)WixHttpSslBindingSymbolFields.Store, value); + } + + public HandleExisting HandleExisting + { + get => (HandleExisting)this.Fields[(int)WixHttpSslBindingSymbolFields.HandleExisting].AsNumber(); + set => this.Set((int)WixHttpSslBindingSymbolFields.HandleExisting, (int)value); + } + + public string ComponentRef + { + get => this.Fields[(int)WixHttpSslBindingSymbolFields.ComponentRef].AsString(); + set => this.Set((int)WixHttpSslBindingSymbolFields.ComponentRef, value); + } + } +} diff --git a/src/ext/Http/wixlib/HttpExtension_Platform.wxi b/src/ext/Http/wixlib/HttpExtension_Platform.wxi index 76f25a2dc..7ed80b4da 100644 --- a/src/ext/Http/wixlib/HttpExtension_Platform.wxi +++ b/src/ext/Http/wixlib/HttpExtension_Platform.wxi @@ -61,27 +61,35 @@ - - - - - - + + + + + + + + - - - - - - + + + + + + + + + - - + + + + diff --git a/src/ext/Http/wixlib/en-us.wxl b/src/ext/Http/wixlib/en-us.wxl index 9b172daa3..b959ddbee 100644 --- a/src/ext/Http/wixlib/en-us.wxl +++ b/src/ext/Http/wixlib/en-us.wxl @@ -15,10 +15,10 @@ - - - - - - + + + + + +