From ba7fd5837ea149b2e319cc577fad27ce1162a064 Mon Sep 17 00:00:00 2001 From: Bob Arnson Date: Mon, 17 Feb 2025 22:30:31 -0500 Subject: Support non-SNI SSL certificates in Http extension Implements https://github.com/wixtoolset/issues/issues/7622 --- src/ext/Http/ca/httpcerts.cpp | 938 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 938 insertions(+) create mode 100644 src/ext/Http/ca/httpcerts.cpp (limited to 'src/ext/Http/ca/httpcerts.cpp') diff --git a/src/ext/Http/ca/httpcerts.cpp b/src/ext/Http/ca/httpcerts.cpp new file mode 100644 index 00000000..c91dbbe1 --- /dev/null +++ b/src/ext/Http/ca/httpcerts.cpp @@ -0,0 +1,938 @@ +// 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. + +#include "precomp.h" + +#if _WIN32_WINNT < 0x0602 + +typedef struct _HTTP_SERVICE_CONFIG_SSL_SNI_KEY +{ + SOCKADDR_STORAGE IpPort; + PWSTR Host; +} HTTP_SERVICE_CONFIG_SSL_SNI_KEY, * PHTTP_SERVICE_CONFIG_SSL_SNI_KEY; + +typedef struct _HTTP_SERVICE_CONFIG_SSL_SNI_SET +{ + HTTP_SERVICE_CONFIG_SSL_SNI_KEY KeyDesc; + HTTP_SERVICE_CONFIG_SSL_PARAM ParamDesc; +} HTTP_SERVICE_CONFIG_SSL_SNI_SET, * PHTTP_SERVICE_CONFIG_SSL_SNI_SET; + +typedef struct _HTTP_SERVICE_CONFIG_SSL_SNI_QUERY +{ + HTTP_SERVICE_CONFIG_QUERY_TYPE QueryDesc; + HTTP_SERVICE_CONFIG_SSL_SNI_KEY KeyDesc; + DWORD dwToken; +} HTTP_SERVICE_CONFIG_SSL_SNI_QUERY, * PHTTP_SERVICE_CONFIG_SSL_SNI_QUERY; + +#define HttpServiceConfigSslSniCertInfo static_cast(HttpServiceConfigCache + 1) + +#endif + +static UINT SchedHttpCertificates( + __in WCA_TODO todoSched +); +static HRESULT FindExistingSniSslCertificate( + __in_z LPWSTR wzHost, + __in int nPort, + __out HTTP_SERVICE_CONFIG_SSL_SNI_SET** ppSet +); +static HRESULT FindExistingIpSslCertificate( + __in int nPort, + __out HTTP_SERVICE_CONFIG_SSL_SET** ppSet +); +static HRESULT WriteSniSslCertCustomActionData( + __in WCA_TODO action, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzHost, + __in int iPort, + __in int iHandleExisting, + __in HTTP_SERVICE_CONFIG_SSL_SNI_SET* pSniSslSet, + __inout_z LPWSTR* psczCustomActionData +); +static HRESULT WriteIpSslCertCustomActionData( + __in WCA_TODO action, + __in_z LPCWSTR wzId, + __in int iPort, + __in int iHandleExisting, + __in HTTP_SERVICE_CONFIG_SSL_SET* pSniSslSet, + __inout_z LPWSTR* psczCustomActionData +); +static HRESULT AddSniSslCert( + __in_z LPCWSTR wzId, + __in_z LPWSTR wzHost, + __in int iPort, + __in BYTE rgbCertificateThumbprint[], + __in DWORD cbCertificateThumbprint, + __in GUID* pAppId, + __in_z LPWSTR wzSslCertStore +); +static HRESULT AddIpSslCert( + __in_z LPCWSTR wzId, + __in int iPort, + __in BYTE rgbCertificateThumbprint[], + __in DWORD cbCertificateThumbprint, + __in GUID* pAppId, + __in_z LPWSTR wzSslCertStore +); +static HRESULT RemoveSniSslCert( + __in_z_opt LPCWSTR wzId, + __in_z LPWSTR wzHost, + __in int iPort +); +static HRESULT RemoveIpSslCert( + __in_z_opt LPCWSTR wzId, + __in int iPort +); +static void SetSniSslCertificateKeyPort( + __in HTTP_SERVICE_CONFIG_SSL_SNI_KEY* pKey, + __in_z LPWSTR wzHost, + __in int iPort +); +static void SetIpSslCertificateKeyPort( + __in HTTP_SERVICE_CONFIG_SSL_KEY* pKey, + __in SOCKADDR_IN* pSin, + __in int iPort +); +static HRESULT EnsureAppId( + __inout_z LPWSTR* psczAppId, + __in_opt GUID* pGuid +); +static HRESULT StringFromGuid( + __in REFGUID rguid, + __inout_z LPWSTR* psczGuid +); +static HRESULT WriteCertificateCaData( + __in eCertificateType certType, + __in WCA_TODO action, + __in_z LPCWSTR wzId, + __in_z_opt LPCWSTR wzHost, + __in int iPort, + __in int iHandleExisting, + __in_z LPCWSTR wzCertificateThumbprint, + __in_z_opt LPCWSTR wzAppId, + __in_z_opt LPCWSTR wzCertificateStore, + __inout_z LPWSTR* psczCustomActionData +); + + +LPCWSTR vcsHttpCertificatesQuery = +L"SELECT `HttpCertificate`, `Host`, `Port`, `Thumbprint`, `AppId`, `Store`, `HandleExisting`, `Type`, `Component_` " +L"FROM `Wix6HttpCertificate`"; +enum eHttpCertificatesQuery { hcqId = 1, hcqHost, hcqPort, hcqCertificateThumbprint, hcqAppId, hcqCertificateStore, hcqHandleExisting, hcqType, hcqComponent }; + +/****************************************************************** + SchedHttpCertificatesInstall - immediate custom action entry + point to prepare adding certificates. + +********************************************************************/ +extern "C" UINT __stdcall SchedHttpCertificatesInstall( + __in MSIHANDLE hInstall +) +{ + HRESULT hr = S_OK; + + hr = WcaInitialize(hInstall, "SchedHttpCertificatesInstall"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = SchedHttpCertificates(WCA_TODO_INSTALL); + +LExit: + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); +} + +/****************************************************************** + SchedWixHttpSniSslCertsUninstall - immediate custom action entry + point to prepare removing certificates. + +********************************************************************/ +extern "C" UINT __stdcall SchedHttpCertificatesUninstall( + __in MSIHANDLE hInstall +) +{ + HRESULT hr = S_OK; + + hr = WcaInitialize(hInstall, "SchedHttpCertificatesUninstall"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = SchedHttpCertificates(WCA_TODO_UNINSTALL); + +LExit: + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); +} + +/****************************************************************** + ExecHttpCertificates - deferred custom action entry point to + bind/unbind certificates. + +********************************************************************/ +extern "C" UINT __stdcall ExecHttpCertificates( + __in MSIHANDLE hInstall +) +{ + HRESULT hr = S_OK; + BOOL fHttpInitialized = FALSE; + LPWSTR sczCustomActionData = NULL; + LPWSTR wz = NULL; + int iTodo = WCA_TODO_UNKNOWN; + LPWSTR sczId = NULL; + LPWSTR sczHost = NULL; + int iPort = 0; + eHandleExisting handleExisting = heIgnore; + eCertificateType certificateType = ctSniSsl; + LPWSTR sczCertificateThumbprint = NULL; + LPWSTR sczAppId = NULL; + LPWSTR sczCertificateStore = NULL; + + BOOL fRollback = ::MsiGetMode(hInstall, MSIRUNMODE_ROLLBACK); + BOOL fRemove = FALSE; + BOOL fAdd = FALSE; + BOOL fFailOnExisting = FALSE; + + GUID guidAppId = { }; + BYTE* pbCertificateThumbprint = NULL; + DWORD cbCertificateThumbprint = 0; + + // Initialize. + hr = WcaInitialize(hInstall, "ExecHttpCertificates"); + ExitOnFailure(hr, "Failed to initialize"); + + hr = HRESULT_FROM_WIN32(::HttpInitialize(HTTPAPI_VERSION_1, HTTP_INITIALIZE_CONFIG, NULL)); + ExitOnFailure(hr, "Failed to initialize HTTP Server configuration"); + + fHttpInitialized = TRUE; + + hr = WcaGetProperty(L"CustomActionData", &sczCustomActionData); + ExitOnFailure(hr, "Failed to get CustomActionData"); + WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", sczCustomActionData); + + wz = sczCustomActionData; + while (wz && *wz) + { + // Extract the custom action data and if rolling back, swap INSTALL and UNINSTALL. + hr = WcaReadIntegerFromCaData(&wz, reinterpret_cast(&certificateType)); + ExitOnFailure(hr, "Failed to read Type from custom action data"); + + hr = WcaReadIntegerFromCaData(&wz, &iTodo); + ExitOnFailure(hr, "Failed to read todo from custom action data"); + + hr = WcaReadStringFromCaData(&wz, &sczId); + ExitOnFailure(hr, "Failed to read Id from custom action data"); + + hr = WcaReadStringFromCaData(&wz, &sczHost); + ExitOnFailure(hr, "Failed to read Host from custom action data"); + + hr = WcaReadIntegerFromCaData(&wz, &iPort); + ExitOnFailure(hr, "Failed to read Port from custom action data"); + + hr = WcaReadIntegerFromCaData(&wz, reinterpret_cast(&handleExisting)); + ExitOnFailure(hr, "Failed to read HandleExisting from custom action data"); + + hr = WcaReadStringFromCaData(&wz, &sczCertificateThumbprint); + ExitOnFailure(hr, "Failed to read CertificateThumbprint from custom action data"); + + hr = WcaReadStringFromCaData(&wz, &sczAppId); + ExitOnFailure(hr, "Failed to read AppId from custom action data"); + + hr = WcaReadStringFromCaData(&wz, &sczCertificateStore); + ExitOnFailure(hr, "Failed to read CertificateStore from custom action data"); + + switch (iTodo) + { + case WCA_TODO_INSTALL: + case WCA_TODO_REINSTALL: + fRemove = heReplace == handleExisting || fRollback; + fAdd = !fRollback || *sczCertificateThumbprint; + fFailOnExisting = heFail == handleExisting && !fRollback; + break; + + case WCA_TODO_UNINSTALL: + fRemove = !fRollback; + fAdd = fRollback && *sczCertificateThumbprint; + fFailOnExisting = FALSE; + break; + } + + if (fRemove) + { + if (ctSniSsl == certificateType) + { + hr = RemoveSniSslCert(sczId, sczHost, iPort); + } + else + { + hr = RemoveIpSslCert(sczId, iPort); + } + + if (S_OK == hr) + { + WcaLog(LOGMSG_STANDARD, "Removed SSL certificate '%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); + } + else + { + ExitOnFailure(hr, "Failed to remove SSL certificate '%ls' for hostname: %ls:%d.", sczId, sczHost, iPort); + } + } + } + + if (fAdd) + { + WcaLog(LOGMSG_STANDARD, "Adding SSL certificate '%ls' for hostname: %ls:%d.", sczId, sczHost, iPort); + + hr = StrAllocHexDecode(sczCertificateThumbprint, &pbCertificateThumbprint, &cbCertificateThumbprint); + ExitOnFailure(hr, "Failed to convert thumbprint to bytes for SSL certificate '%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); + if (ctSniSsl == certificateType) + { + hr = AddSniSslCert(sczId, sczHost, iPort, pbCertificateThumbprint, cbCertificateThumbprint, &guidAppId, sczCertificateStore && *sczCertificateStore ? sczCertificateStore : L"MY"); + } + else + { + hr = AddIpSslCert(sczId, iPort, pbCertificateThumbprint, cbCertificateThumbprint, &guidAppId, sczCertificateStore && *sczCertificateStore ? sczCertificateStore : L"MY"); + } + + if (S_FALSE == hr && fFailOnExisting) + { + hr = HRESULT_FROM_WIN32(ERROR_ALREADY_EXISTS); + } + + if (S_OK == hr) + { + WcaLog(LOGMSG_STANDARD, "Added SSL certificate '%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); + } + else + { + ExitOnFailure(hr, "Failed to add SSL certificate '%ls' for hostname: %ls:%d.", sczId, sczHost, iPort); + } + } + + ReleaseNullMem(pbCertificateThumbprint); + } + } + +LExit: + ReleaseMem(pbCertificateThumbprint); + ReleaseStr(sczCertificateStore); + ReleaseStr(sczAppId); + ReleaseStr(sczCertificateThumbprint); + ReleaseStr(sczHost); + ReleaseStr(sczId); + ReleaseStr(sczCustomActionData); + + if (fHttpInitialized) + { + ::HttpTerminate(HTTP_INITIALIZE_CONFIG, NULL); + } + + return WcaFinalize(FAILED(hr) ? ERROR_INSTALL_FAILURE : ERROR_SUCCESS); +} + +static UINT SchedHttpCertificates( + __in WCA_TODO todoSched +) +{ + HRESULT hr = S_OK; + UINT er = ERROR_SUCCESS; + BOOL fHttpInitialized = FALSE; + DWORD cCertificates = 0; + + PMSIHANDLE hView = NULL; + PMSIHANDLE hRec = NULL; + PMSIHANDLE hQueryReq = NULL; + PMSIHANDLE hAceView = NULL; + + LPWSTR sczCustomActionData = NULL; + LPWSTR sczRollbackCustomActionData = NULL; + + LPWSTR sczId = NULL; + LPWSTR sczComponent = NULL; + eCertificateType certificateType = ctSniSsl; + WCA_TODO todoComponent = WCA_TODO_UNKNOWN; + LPWSTR sczHost = NULL; + int iPort = 0; + LPWSTR sczCertificateThumbprint = NULL; + LPWSTR sczAppId = NULL; + LPWSTR sczCertificateStore = NULL; + int iHandleExisting = 0; + + HTTP_SERVICE_CONFIG_SSL_SNI_SET* pExistingSniSslSet = NULL; + HTTP_SERVICE_CONFIG_SSL_SET* pExistingIpSslSet = NULL; + + // Anything to do? + hr = WcaTableExists(L"Wix6HttpCertificate"); + ExitOnFailure(hr, "Failed to check if the Wix6HttpCertificate table exists"); + if (S_FALSE == hr) + { + WcaLog(LOGMSG_STANDARD, "Wix6HttpCertificate table doesn't exist, so there are no certificates to configure."); + ExitFunction(); + } + + // Query and loop through all the SNI SSL certificates. + hr = WcaOpenExecuteView(vcsHttpCertificatesQuery, &hView); + ExitOnFailure(hr, "Failed to open view on the Wix6HttpCertificate table"); + + hr = HRESULT_FROM_WIN32(::HttpInitialize(HTTPAPI_VERSION_1, HTTP_INITIALIZE_CONFIG, NULL)); + ExitOnFailure(hr, "Failed to initialize HTTP Server configuration"); + + fHttpInitialized = TRUE; + + while (S_OK == (hr = WcaFetchRecord(hView, &hRec))) + { + hr = WcaGetRecordString(hRec, hcqId, &sczId); + ExitOnFailure(hr, "Failed to get Wix6HttpCertificate.Wix6HttpCertificate"); + + hr = WcaGetRecordString(hRec, hcqComponent, &sczComponent); + ExitOnFailure(hr, "Failed to get Wix6HttpCertificate.Component_"); + + // Figure out what we're doing for this certificate, treating reinstall the same as install. + todoComponent = WcaGetComponentToDo(sczComponent); + if ((WCA_TODO_REINSTALL == todoComponent ? WCA_TODO_INSTALL : todoComponent) != todoSched) + { + WcaLog(LOGMSG_VERBOSE, "Component '%ls' action state (%d) doesn't match request (%d) for Wix6HttpCertificate '%ls'.", sczComponent, todoComponent, todoSched, sczId); + continue; + } + + hr = WcaGetRecordInteger(hRec, hcqType, reinterpret_cast(&certificateType)); + ExitOnFailure(hr, "Failed to get Type for Wix6HttpCertificate '%ls'", sczId); + + hr = WcaGetRecordFormattedString(hRec, hcqHost, &sczHost); + ExitOnFailure(hr, "Failed to get Wix6HttpCertificate.Host"); + + hr = WcaGetRecordFormattedInteger(hRec, hcqPort, &iPort); + ExitOnFailure(hr, "Failed to get Wix6HttpCertificate.Port"); + + hr = WcaGetRecordFormattedString(hRec, hcqCertificateThumbprint, &sczCertificateThumbprint); + ExitOnFailure(hr, "Failed to get Wix6HttpCertificate.CertificateThumbprint"); + + if (!iPort) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Missing Port value for Wix6HttpCertificate '%ls'", sczId); + } + + if (!sczCertificateThumbprint || !*sczCertificateThumbprint) + { + hr = E_INVALIDARG; + ExitOnFailure(hr, "Missing CertificateThumbprint value for Wix6HttpCertificate '%ls'", sczId); + } + + hr = WcaGetRecordFormattedString(hRec, hcqAppId, &sczAppId); + ExitOnFailure(hr, "Failed to get AppId for Wix6HttpCertificate '%ls'", sczId); + + hr = WcaGetRecordFormattedString(hRec, hcqCertificateStore, &sczCertificateStore); + ExitOnFailure(hr, "Failed to get CertificateStore for Wix6HttpCertificate '%ls'", sczId); + + hr = WcaGetRecordInteger(hRec, hcqHandleExisting, &iHandleExisting); + ExitOnFailure(hr, "Failed to get HandleExisting for Wix6HttpCertificate '%ls'", sczId); + + if (ctIpSsl == certificateType) + { + WcaLog(LOGMSG_STANDARD, "Processing IP SSL certificate: %ls on port %d.", sczId, iPort); + + hr = FindExistingIpSslCertificate(iPort, &pExistingIpSslSet); + ExitOnFailure(hr, "Failed to search for an existing IP SSL certificate for '%ls' on port %d", sczId, iPort); + + if (S_FALSE != hr) + { + hr = WriteIpSslCertCustomActionData(todoComponent, sczId, iPort, iHandleExisting, pExistingIpSslSet, &sczRollbackCustomActionData); + ExitOnFailure(hr, "Failed to write rollback custom action data for IP SSL '%ls' on port %d", sczId, iPort); + } + + hr = EnsureAppId(&sczAppId, pExistingIpSslSet ? &(pExistingIpSslSet->ParamDesc.AppId) : NULL); + ExitOnFailure(hr, "Failed to ensure AppId for IP SSL '%ls'", sczId); + } + else if (ctSniSsl == certificateType) + { + WcaLog(LOGMSG_STANDARD, "Processing SNI SSL certificate: %ls on host %ls:%d.", sczId, sczHost, iPort); + + hr = FindExistingSniSslCertificate(sczHost, iPort, &pExistingSniSslSet); + ExitOnFailure(hr, "Failed to search for an existing SNI SSL certificate for '%ls' on host '%ls', port %d", sczId, sczHost, iPort); + + if (S_FALSE != hr) + { + hr = WriteSniSslCertCustomActionData(todoComponent, sczId, sczHost, iPort, iHandleExisting, pExistingSniSslSet, &sczRollbackCustomActionData); + ExitOnFailure(hr, "Failed to write rollback custom action data for SNI SSL Wix6HttpCertificate '%ls' on host '%ls', port %d", sczId, sczHost, iPort); + } + + hr = EnsureAppId(&sczAppId, pExistingSniSslSet ? &(pExistingSniSslSet->ParamDesc.AppId) : NULL); + ExitOnFailure(hr, "Failed to ensure AppId for SNI SSL '%ls'", sczId); + } + + hr = WriteCertificateCaData(certificateType, todoComponent, sczId, sczHost, iPort, iHandleExisting, sczCertificateThumbprint, sczAppId, sczCertificateStore, &sczCustomActionData); + ExitOnFailure(hr, "Failed to write custom action data for SSL '%ls'", sczId); + ++cCertificates; + + ReleaseNullMem(pExistingSniSslSet); + ReleaseNullMem(pExistingIpSslSet); + } + + // Reaching the end of the list is not an error. + if (E_NOMOREITEMS == hr) + { + hr = S_OK; + } + ExitOnFailure(hr, "Failure occurred while processing Wix6HttpCertificate table"); + + WcaLog(LOGMSG_VERBOSE, "Scheduling %d certificates", cCertificates); + + // Schedule ExecHttpSniSslCerts if there's anything to do. + if (cCertificates) + { + WcaLog(LOGMSG_TRACEONLY, "Scheduling SSL certificate: `%ls`", sczCustomActionData); + WcaLog(LOGMSG_TRACEONLY, "Scheduling rollback SSL certificate: `%ls`", sczRollbackCustomActionData); + + if (WCA_TODO_INSTALL == todoSched) + { + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RollbackHttpCertificatesInstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SNI_SSL); + ExitOnFailure(hr, "Failed to schedule install SSL certificate rollback"); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"ExecHttpCertificatesInstall"), sczCustomActionData, cCertificates * COST_HTTP_SNI_SSL); + ExitOnFailure(hr, "Failed to schedule install SSL certificate execution"); + } + else + { + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"RollbackHttpCertificatesUninstall"), sczRollbackCustomActionData, cCertificates * COST_HTTP_SNI_SSL); + ExitOnFailure(hr, "Failed to schedule uninstall SSL certificate rollback"); + hr = WcaDoDeferredAction(CUSTOM_ACTION_DECORATION6(L"ExecHttpCertificatesUninstall"), sczCustomActionData, cCertificates * COST_HTTP_SNI_SSL); + ExitOnFailure(hr, "Failed to schedule uninstall SSL certificate execution"); + } + } + else + { + WcaLog(LOGMSG_STANDARD, "No SNI SSL certificates scheduled."); + } + +LExit: + ReleaseMem(pExistingSniSslSet); + ReleaseMem(pExistingIpSslSet); + ReleaseStr(sczCertificateStore); + ReleaseStr(sczAppId); + ReleaseStr(sczCertificateThumbprint); + ReleaseStr(sczHost); + ReleaseStr(sczComponent); + ReleaseStr(sczId); + ReleaseStr(sczRollbackCustomActionData); + ReleaseStr(sczCustomActionData); + + if (fHttpInitialized) + { + ::HttpTerminate(HTTP_INITIALIZE_CONFIG, NULL); + } + + return WcaFinalize(er = FAILED(hr) ? ERROR_INSTALL_FAILURE : er); +} + +static HRESULT FindExistingSniSslCertificate( + __in_z LPWSTR wzHost, + __in int nPort, + __out HTTP_SERVICE_CONFIG_SSL_SNI_SET** ppSet +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HTTP_SERVICE_CONFIG_SSL_SNI_QUERY query = { }; + HTTP_SERVICE_CONFIG_SSL_SNI_SET* pSet = NULL; + ULONG cbSet = 0; + + *ppSet = NULL; + + query.QueryDesc = HttpServiceConfigQueryExact; + SetSniSslCertificateKeyPort(&query.KeyDesc, wzHost, nPort); + + WcaLog(LOGMSG_TRACEONLY, "Querying for SNI SSL certificate on port %d...", nPort); + + er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + pSet = reinterpret_cast(MemAlloc(cbSet, TRUE)); + ExitOnNull(pSet, hr, E_OUTOFMEMORY, "Failed to allocate query SN SSL certificate buffer"); + + er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL); + } + + if (ERROR_SUCCESS == er) + { + *ppSet = pSet; + pSet = NULL; + } + else if (ERROR_FILE_NOT_FOUND == er || ERROR_NO_MORE_ITEMS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + ReleaseMem(pSet); + + return hr; +} + +static HRESULT FindExistingIpSslCertificate( + __in int nPort, + __out HTTP_SERVICE_CONFIG_SSL_SET** ppSet +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HTTP_SERVICE_CONFIG_SSL_QUERY query = { }; + SOCKADDR_IN sin = { }; + HTTP_SERVICE_CONFIG_SSL_SET* pSet = NULL; + ULONG cbSet = 0; + + *ppSet = NULL; + + query.QueryDesc = HttpServiceConfigQueryNext; + + SetIpSslCertificateKeyPort(&query.KeyDesc, &sin, nPort); + + WcaLog(LOGMSG_TRACEONLY, "Querying for IP SSL certificate on port %d...", nPort); + + er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigSSLCertInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL); + if (ERROR_INSUFFICIENT_BUFFER == er) + { + pSet = reinterpret_cast(MemAlloc(cbSet, TRUE)); + ExitOnNull(pSet, hr, E_OUTOFMEMORY, "Failed to allocate query IP SSL certificate buffer"); + + er = ::HttpQueryServiceConfiguration(NULL, HttpServiceConfigSSLCertInfo, &query, sizeof(query), pSet, cbSet, &cbSet, NULL); + } + + if (ERROR_SUCCESS == er) + { + *ppSet = pSet; + pSet = NULL; + } + else if (ERROR_FILE_NOT_FOUND == er || ERROR_NO_MORE_ITEMS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + +LExit: + ReleaseMem(pSet); + + return hr; +} + +static HRESULT WriteSniSslCertCustomActionData( + __in WCA_TODO action, + __in_z LPCWSTR wzId, + __in_z LPCWSTR wzHost, + __in int iPort, + __in int iHandleExisting, + __in HTTP_SERVICE_CONFIG_SSL_SNI_SET* pSniSslSet, + __inout_z LPWSTR* psczCustomActionData +) +{ + HRESULT hr = S_OK; + LPWSTR sczCertificateThumbprint = NULL; + LPWSTR sczAppId = NULL; + LPCWSTR wzCertificateStore = NULL; + + if (pSniSslSet) + { + hr = StrAllocHexEncode(reinterpret_cast(pSniSslSet->ParamDesc.pSslHash), pSniSslSet->ParamDesc.SslHashLength, &sczCertificateThumbprint); + ExitOnFailure(hr, "Failed to convert existing certificate thumbprint to hex for Wix6HttpCertificate '%ls'", wzId); + + hr = StringFromGuid(pSniSslSet->ParamDesc.AppId, &sczAppId); + ExitOnFailure(hr, "Failed to copy existing AppId for Wix6HttpCertificate '%ls'", wzId); + + wzCertificateStore = pSniSslSet->ParamDesc.pSslCertStoreName; + } + + hr = WriteCertificateCaData(ctSniSsl, action, wzId, wzHost, iPort, iHandleExisting, sczCertificateThumbprint ? sczCertificateThumbprint : L"", sczAppId ? sczAppId : L"", wzCertificateStore ? wzCertificateStore : L"", psczCustomActionData); + ExitOnFailure(hr, "Failed to write custom action data for Wix6HttpCertificate '%ls'", wzId); + +LExit: + ReleaseStr(sczAppId); + ReleaseStr(sczCertificateThumbprint); + + return hr; +} + +static HRESULT WriteIpSslCertCustomActionData( + __in WCA_TODO action, + __in_z LPCWSTR wzId, + __in int iPort, + __in int iHandleExisting, + __in HTTP_SERVICE_CONFIG_SSL_SET* pSslSet, + __inout_z LPWSTR* psczCustomActionData +) +{ + HRESULT hr = S_OK; + LPWSTR sczCertificateThumbprint = NULL; + LPWSTR sczAppId = NULL; + LPCWSTR wzCertificateStore = NULL; + + if (pSslSet) + { + hr = StrAllocHexEncode(reinterpret_cast(pSslSet->ParamDesc.pSslHash), pSslSet->ParamDesc.SslHashLength, &sczCertificateThumbprint); + ExitOnFailure(hr, "Failed to convert existing IP SSL certificate thumbprint to hex for Wix6HttpCertificate '%ls'", wzId); + + hr = StringFromGuid(pSslSet->ParamDesc.AppId, &sczAppId); + ExitOnFailure(hr, "Failed to copy existing IP SSL AppId for Wix6HttpCertificate '%ls'", wzId); + + wzCertificateStore = pSslSet->ParamDesc.pSslCertStoreName; + } + + hr = WriteCertificateCaData(ctIpSsl, action, wzId, /*wzHost*/NULL, iPort, iHandleExisting, sczCertificateThumbprint ? sczCertificateThumbprint : L"", sczAppId ? sczAppId : L"", wzCertificateStore ? wzCertificateStore : L"", psczCustomActionData); + ExitOnFailure(hr, "Failed to write custom action data for IP SSL Wix6HttpCertificate '%ls'", wzId); + +LExit: + ReleaseStr(sczAppId); + ReleaseStr(sczCertificateThumbprint); + + return hr; +} + +static HRESULT AddSniSslCert( + __in_z LPCWSTR /*wzId*/, + __in_z LPWSTR wzHost, + __in int iPort, + __in BYTE rgbCertificateThumbprint[], + __in DWORD cbCertificateThumbprint, + __in GUID* pAppId, + __in_z LPWSTR wzSslCertStore +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HTTP_SERVICE_CONFIG_SSL_SNI_SET set = { }; + + SetSniSslCertificateKeyPort(&set.KeyDesc, wzHost, iPort); + set.ParamDesc.SslHashLength = cbCertificateThumbprint; + set.ParamDesc.pSslHash = rgbCertificateThumbprint; + set.ParamDesc.AppId = *pAppId; + set.ParamDesc.pSslCertStoreName = wzSslCertStore; + + er = ::HttpSetServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &set, sizeof(set), NULL); + if (ERROR_ALREADY_EXISTS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + + return hr; +} + +static HRESULT AddIpSslCert( + __in_z LPCWSTR /*wzId*/, + __in int iPort, + __in BYTE rgbCertificateThumbprint[], + __in DWORD cbCertificateThumbprint, + __in GUID* pAppId, + __in_z LPWSTR wzSslCertStore +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HTTP_SERVICE_CONFIG_SSL_SET set = { }; + SOCKADDR_IN sin = { }; + + SetIpSslCertificateKeyPort(&set.KeyDesc, &sin, iPort); + set.ParamDesc.SslHashLength = cbCertificateThumbprint; + set.ParamDesc.pSslHash = rgbCertificateThumbprint; + set.ParamDesc.AppId = *pAppId; + set.ParamDesc.pSslCertStoreName = wzSslCertStore; + + er = ::HttpSetServiceConfiguration(NULL, HttpServiceConfigSSLCertInfo, &set, sizeof(set), NULL); + if (ERROR_ALREADY_EXISTS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + + return hr; +} + +static HRESULT RemoveSniSslCert( + __in_z_opt LPCWSTR /*wzId*/, + __in_z LPWSTR wzHost, + __in int iPort +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HTTP_SERVICE_CONFIG_SSL_SNI_SET set = { }; + + SetSniSslCertificateKeyPort(&set.KeyDesc, wzHost, iPort); + + er = ::HttpDeleteServiceConfiguration(NULL, HttpServiceConfigSslSniCertInfo, &set, sizeof(set), NULL); + if (ERROR_FILE_NOT_FOUND == er || ERROR_NO_MORE_ITEMS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + + return hr; +} + +static HRESULT RemoveIpSslCert( + __in_z_opt LPCWSTR /*wzId*/, + __in int iPort +) +{ + HRESULT hr = S_OK; + DWORD er = ERROR_SUCCESS; + HTTP_SERVICE_CONFIG_SSL_SET set = { }; + SOCKADDR_IN sin = { }; + + SetIpSslCertificateKeyPort(&set.KeyDesc, &sin, iPort); + + er = ::HttpDeleteServiceConfiguration(NULL, HttpServiceConfigSSLCertInfo, &set, sizeof(set), NULL); + if (ERROR_FILE_NOT_FOUND == er || ERROR_NO_MORE_ITEMS == er) + { + hr = S_FALSE; + } + else + { + hr = HRESULT_FROM_WIN32(er); + } + + return hr; +} + +static void SetSniSslCertificateKeyPort( + __in HTTP_SERVICE_CONFIG_SSL_SNI_KEY* pKey, + __in_z LPWSTR wzHost, + __in int iPort +) +{ + pKey->Host = wzHost; + SOCKADDR_IN* pss = reinterpret_cast(&pKey->IpPort); + pss->sin_family = AF_INET; + pss->sin_port = htons(static_cast(iPort)); +} + +static void SetIpSslCertificateKeyPort( + __in HTTP_SERVICE_CONFIG_SSL_KEY* pKey, + __in SOCKADDR_IN* pSin, + __in int iPort +) +{ + pSin->sin_family = AF_INET; + pSin->sin_port = htons(static_cast(iPort)); + pKey->pIpPort = reinterpret_cast(pSin); +} + +static HRESULT EnsureAppId( + __inout_z LPWSTR* psczAppId, + __in_opt GUID* pGuid +) +{ + HRESULT hr = S_OK; + GUID guid = { }; + + if (!psczAppId || !*psczAppId || !**psczAppId) + { + if (pGuid) + { + hr = StringFromGuid(*pGuid, psczAppId); + ExitOnFailure(hr, "Failed to ensure AppId guid"); + } + else + { + hr = HRESULT_FROM_RPC(::UuidCreate(&guid)); + ExitOnRootFailure(hr, "Failed to create guid for AppId"); + + hr = StringFromGuid(guid, psczAppId); + ExitOnFailure(hr, "Failed to ensure AppId guid"); + } + } + +LExit: + return hr; +} + +static HRESULT StringFromGuid( + __in REFGUID rguid, + __inout_z LPWSTR* psczGuid +) +{ + HRESULT hr = S_OK; + WCHAR wzGuid[39]; + + if (!::StringFromGUID2(rguid, wzGuid, countof(wzGuid))) + { + hr = E_OUTOFMEMORY; + ExitOnRootFailure(hr, "Failed to convert guid into string"); + } + + hr = StrAllocString(psczGuid, wzGuid, 0); + ExitOnFailure(hr, "Failed to copy guid"); + +LExit: + return hr; +} + +static HRESULT WriteCertificateCaData( + __in eCertificateType certType, + __in WCA_TODO action, + __in_z LPCWSTR wzId, + __in_z_opt LPCWSTR wzHost, + __in int iPort, + __in int iHandleExisting, + __in_z LPCWSTR wzCertificateThumbprint, + __in_z_opt LPCWSTR wzAppId, + __in_z_opt LPCWSTR wzCertificateStore, + __inout_z LPWSTR* psczCustomActionData +) +{ + HRESULT hr = S_OK; + + hr = WcaWriteIntegerToCaData(certType, psczCustomActionData); + ExitOnFailure(hr, "Failed to write IP SSL certificate type to custom action data"); + + hr = WcaWriteIntegerToCaData(action, psczCustomActionData); + ExitOnFailure(hr, "Failed to write action to custom action data"); + + hr = WcaWriteStringToCaData(wzId, psczCustomActionData); + ExitOnFailure(hr, "Failed to write id to custom action data"); + + hr = WcaWriteStringToCaData(wzHost ? wzHost : L"", psczCustomActionData); + ExitOnFailure(hr, "Failed to write Host to custom action data"); + + hr = WcaWriteIntegerToCaData(iPort, psczCustomActionData); + ExitOnFailure(hr, "Failed to write Port to custom action data"); + + hr = WcaWriteIntegerToCaData(iHandleExisting, psczCustomActionData); + ExitOnFailure(hr, "Failed to write HandleExisting to custom action data"); + + hr = WcaWriteStringToCaData(wzCertificateThumbprint, psczCustomActionData); + ExitOnFailure(hr, "Failed to write CertificateThumbprint to custom action data"); + + hr = WcaWriteStringToCaData(wzAppId ? wzAppId : L"", psczCustomActionData); + ExitOnFailure(hr, "Failed to write AppId to custom action data"); + + hr = WcaWriteStringToCaData(wzCertificateStore ? wzCertificateStore : L"", psczCustomActionData); + ExitOnFailure(hr, "Failed to write CertificateStore to custom action data"); + +LExit: + return hr; +} + -- cgit v1.2.3-55-g6feb