[cpp-sp] 02/02: CPPSP-18 Investigate tlsCipherList for WinHttp

Codeberg noreply at shibboleth.net
Thu Aug 20 18:43:38 UTC 2026


This is an automated email from the git hooks/post-receive script.

codeberg pushed a commit to branch main
in repository cpp-sp.

View the commit online:
https://codeberg.org/Shibboleth/cpp-sp/commit/7748bf71864ad7b695bbb02dfcd8df32154e7932

commit 7748bf71864ad7b695bbb02dfcd8df32154e7932
Author: Rod Widdowson <rdw at steadingsoftware.com>
AuthorDate: Thu Aug 20 14:43:47 2026 +0100

    CPPSP-18 Investigate tlsCipherList for WinHttp
    
    https://shibboleth.atlassian.net/browse/CPPSP-18
    
    1) Add support for tlsCipherList and tls13CipherList
    2) Add support for enableTls12 and enableTls13
---
 shibsp/remoting/impl/WinHTTPRemotingService.cpp | 120 ++++++++++++++++++++++--
 1 file changed, 110 insertions(+), 10 deletions(-)

diff --git a/shibsp/remoting/impl/WinHTTPRemotingService.cpp b/shibsp/remoting/impl/WinHTTPRemotingService.cpp
index 05ae2de4..f4eb2603 100644
--- a/shibsp/remoting/impl/WinHTTPRemotingService.cpp
+++ b/shibsp/remoting/impl/WinHTTPRemotingService.cpp
@@ -21,6 +21,7 @@
 #include "internal.h"
 #include "exceptions.h"
 #include <Windows.h>
+#include <schannel.h>
 #include <winhttp.h>
 
 #include "Agent.h"
@@ -29,6 +30,7 @@
 #include "remoting/SecretSource.h"
 #include "remoting/impl/AbstractHTTPRemotingService.h"
 #include "util/BoostPropertySet.h"
+#include "util/Misc.h"
 
 #include <stdexcept>
 #include <boost/algorithm/string.hpp>
@@ -55,10 +57,14 @@ namespace {
 
         wstring utf8ToUtf16(const char* input) const;
 
+        string utf16ToUtf8(const wchar_t* input) const;
+
         void send(const char* path, istream& input, ostream& output) const;
 
         void handleCert(HINTERNET handle) const;
 
+        void handleCipher(HINTERNET handle) const;
+
         void logSecureFailure(DWORD status) const;
 
     private:
@@ -66,7 +72,6 @@ namespace {
         bool m_init;
         HINTERNET m_session;
         HINTERNET m_connection;
-        string m_ciphers;
         bool m_secure;
         wstring m_baseURLPath;
         wstring m_username;
@@ -74,6 +79,8 @@ namespace {
         HCERTCHAINENGINE m_caChainEngine;
         HCERTSTORE m_caStore;
         void setupCaChecking();
+        set<string> m_cipherSet;
+        set<string> m_tls13CipherSet;
         string getCertName(PCCERT_CONTEXT certContext) const;
     };
 
@@ -116,6 +123,26 @@ wstring WinHTTPRemotingService::utf8ToUtf16(const char* input) const {
     return result;
 }
 
+string WinHTTPRemotingService::utf16ToUtf8(const wchar_t* input) const {
+    DWORD sizeNeeded = WideCharToMultiByte(CP_UTF8, 0, input, -1, nullptr, 0, nullptr, nullptr);
+
+    LPSTR output = new CHAR[sizeNeeded + 1];
+    if (output == nullptr) {
+        m_log.crit("Out of memory allocating %d bytes for conversion buffer", sizeNeeded + 1);
+        throw runtime_error("Utf16toUtf8 conversion failed");
+    }
+    ZeroMemory(output, sizeNeeded + 1);
+
+    if (WideCharToMultiByte(CP_UTF8, 0, input, -1, output, sizeNeeded, nullptr, nullptr) == 0) {
+        m_log.crit("MultiByteToWideChar failure: %d", GetLastError());
+        throw runtime_error("Utf16toUtf8 conversion failed");
+    }
+
+    string result(output);
+    delete[] output;
+    return result;
+}
+
 void WinHTTPRemotingService::setupCaChecking() {
 
     if (!getCAFile())
@@ -206,9 +233,12 @@ StatusCallback(
 
     if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SENDING_REQUEST) {
         service->handleCert(hInternet);
+        service->handleCipher(hInternet);
     } else if (dwInternetStatus == WINHTTP_CALLBACK_STATUS_SECURE_FAILURE) {
         service->logSecureFailure(*((DWORD*)lpvStatusInformation));
     }
+
+
 };
 
 WinHTTPRemotingService::WinHTTPRemotingService(ptree& pt)
@@ -216,19 +246,22 @@ WinHTTPRemotingService::WinHTTPRemotingService(ptree& pt)
     AbstractHTTPRemotingService(pt),
     m_log(Category::getInstance(SHIBSP_LOGCAT ".RemotingService")),
     m_secure(false), m_caChainEngine(nullptr), m_caStore(nullptr),
-    m_init(false)
+    m_init(false), m_cipherSet(), m_tls13CipherSet()
 {
     if (getUserAgent() == nullptr) {
         string useragent = string(PACKAGE_NAME) + '/' + PACKAGE_VERSION + '/' + "WINHTTP";
         setUserAgent(useragent.c_str());
     }
 
-    //static const char CIPHER_LIST_PROP_NAME[] = "tlsCipherList";
-    static const char CHUNKED_PROP_NAME[] = "chunkedEncoding";
-    
+    static const char TLS_12_PROP_NAME[] = "enableTls12";
+    static const char TLS_13_PROP_NAME[] = "enableTls13";
+
     BoostPropertySet props;
     props.load(pt);
 
+    bool enableTls12 = props.getBool(TLS_12_PROP_NAME, true);
+    bool enableTls13 = props.getBool(TLS_13_PROP_NAME, true);
+
     m_username = utf8ToUtf16(getAgentID());
     switch (getAuthMethod()) {
         case agent_auth_basic:  m_authScheme = WINHTTP_AUTH_SCHEME_BASIC; break;
@@ -247,12 +280,13 @@ WinHTTPRemotingService::WinHTTPRemotingService(ptree& pt)
         throw runtime_error("WinHHHTP failed to initialize service (WinHttpOpen)");
     }
 
+    split_to_container(m_cipherSet, getCiphers().c_str());
+    split_to_container(m_tls13CipherSet, getTls13Ciphers().c_str());
     //
     // Set up our CaPath environment
     //
     setupCaChecking();
 
-
     //
     // Pop the URL into a wstring which we can parse and then point
     // other wstring constructors at
@@ -288,7 +322,7 @@ WinHTTPRemotingService::WinHTTPRemotingService(ptree& pt)
     }
     m_secure = (components.nScheme == INTERNET_SCHEME_HTTPS);
 
-    if (m_secure && (m_caChainEngine != NULL)) {
+    if (m_secure) {
         //
         // Arrange to be called back so we can check certificates
         //
@@ -299,12 +333,30 @@ WinHTTPRemotingService::WinHTTPRemotingService(ptree& pt)
             m_log.crit("WinHttpSetOption failure: %d", GetLastError());
             throw runtime_error("WinHHHTP failed to initialize: Could not set callback option");
         }
+        DWORD flags = WINHTTP_CALLBACK_STATUS_SENDING_REQUEST |
+                        WINHTTP_CALLBACK_STATUS_SECURE_FAILURE;
 
         // And register the callback
-        if (WinHttpSetStatusCallback(m_session, StatusCallback, WINHTTP_CALLBACK_STATUS_SENDING_REQUEST | WINHTTP_CALLBACK_STATUS_SECURE_FAILURE, NULL) == WINHTTP_INVALID_STATUS_CALLBACK) {
+        if (WinHttpSetStatusCallback(m_session, StatusCallback, flags, NULL) == WINHTTP_INVALID_STATUS_CALLBACK) {
             m_log.crit("WinHttpSetStatusCallback failure: %d", GetLastError());
             throw runtime_error("WinHHHTP failed to initialize: Could not register callback");
         }
+
+        flags = 0;
+        if (enableTls12)
+            flags |= WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_2;
+        if (enableTls13)
+            flags |= WINHTTP_FLAG_SECURE_PROTOCOL_TLS1_3;
+
+        if (flags) {
+            if (!WinHttpSetOption(m_session, WINHTTP_OPTION_SECURE_PROTOCOLS, &flags, sizeof(flags))) {
+                m_log.crit("WinHttpSetOption failure: %d", GetLastError());
+                throw runtime_error("WinHHHTP failed to initialize: Could not set tls options");
+            }
+        } else {
+            // both set to false == use machine defaults
+            // https://learn.microsoft.com/en-us/windows-server/networking/configure-secure-protocol-options-winhttp
+        }
     }
 
     wstring host(components.lpszHostName, components.dwHostNameLength);
@@ -568,6 +620,52 @@ void WinHTTPRemotingService::logSecureFailure(DWORD Status) const
     m_log.crit("WinHttp Security Error 0x%x (%s)", Status, details.c_str());
 }
 
+
+
+//
+// Called during WinHttpSendRequest to allow us to police the cipher.
+//
+void WinHTTPRemotingService::handleCipher(HINTERNET handle) const
+{
+    if (m_tls13CipherSet.empty() && m_cipherSet.empty())
+        return;
+
+    WINHTTP_SECURITY_INFO secinfo;
+    DWORD len = sizeof(secinfo);
+    if (!WinHttpQueryOption(handle, WINHTTP_OPTION_SECURITY_INFO, &secinfo, &len)) {
+        m_log.crit("Could not get the securty info on connect %d", GetLastError());
+        throw RemotingException("Could not get securty info");
+    }
+    string cipher(utf16ToUtf8(secinfo.CipherInfo.szCipherSuite));
+    if (cipher.empty())
+        return;
+
+    m_log.debug("cipher presented : {} ", cipher);
+
+    if (secinfo.ConnectionInfo.dwProtocol == SP_PROT_TLS1_2_CLIENT) {
+        if (m_cipherSet.count(cipher)) {
+            // match, nothing to do
+            return;
+        }
+        m_log.crit("Cipher {} not enabled for TLS1.2", cipher);
+        throw RemotingException("Cipher not enabled");
+    }
+
+    if (secinfo.ConnectionInfo.dwProtocol == SP_PROT_TLS1_3_CLIENT) {
+        if (m_tls13CipherSet.count(cipher)) {
+            // match, nothing to do
+            return;
+        }
+        m_log.crit("Cipher {} not enabled for TLS1.3", cipher);
+        throw RemotingException("Cipher not enabled");
+
+    }
+    //
+    // Nothing else to do
+    //
+}
+
+
 //
 // Called during WinHttpSendRequest to allow us to police the certificate.
 //
@@ -576,10 +674,12 @@ void WinHTTPRemotingService::handleCert(HINTERNET handle) const
     PCCERT_CONTEXT certCtx = NULL;
     DWORD size = sizeof(certCtx);
 
+    if (m_caChainEngine == NULL)
+        return;
+
     if (!WinHttpQueryOption(handle, WINHTTP_OPTION_SERVER_CERT_CONTEXT, &certCtx, &size)) {
-        m_log.crit("Could not get the certificate on connect");
+        m_log.crit("Could not get the certificate on connect %d", GetLastError());
         throw RemotingException("Could not get certificate");
-
     }
 
     if (m_log.isDebugEnabled()) {

-- 
To stop receiving notification emails like this one, please contact
the administrator of this repository.


More information about the commits mailing list