diff --git a/client/transports/Network/CMakeLists.txt b/client/transports/Network/CMakeLists.txt index 86b51c35..0ff60de4 100644 --- a/client/transports/Network/CMakeLists.txt +++ b/client/transports/Network/CMakeLists.txt @@ -37,6 +37,10 @@ endif() add_library(transport_Network STATIC ${TRANSPORT_NETWORK_SOURCES}) +target_compile_definitions(transport_Network PRIVATE + QUIC_API_ENABLE_INSECURE_FEATURES=1 +) + target_include_directories(transport_Network PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../../src ) diff --git a/client/transports/Network/connection.c b/client/transports/Network/connection.c index c7ca32ed..f6ddd336 100644 --- a/client/transports/Network/connection.c +++ b/client/transports/Network/connection.c @@ -213,6 +213,7 @@ struct NetworkConnection uint16_t port; char * password; char * endpoint; + bool allowUnencrypted; NetworkKnownHosts * knownHosts; NetworkTrustPromptFn trustPrompt; @@ -2922,8 +2923,28 @@ static QUIC_STATUS QUIC_API networkConnectionCallback(HQUIC handle, return QUIC_STATUS_SUCCESS; } - HQUIC stream = NULL; - QUIC_STATUS status = this->api->StreamOpen(handle, + BOOLEAN encryptionDisabled = FALSE; + uint32_t encryptionDisabledLength = sizeof(encryptionDisabled); + QUIC_STATUS status = this->api->GetParam(handle, + QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION, + &encryptionDisabledLength, &encryptionDisabled); + if (QUIC_FAILED(status)) + { + DEBUG_ERROR("Failed to query 1-RTT encryption for %s: 0x%08x", + this->endpoint, (unsigned)status); + networkConnectionFail(this, LG_TRANSPORT_ERROR); + this->api->ConnectionShutdown(handle, + QUIC_CONNECTION_SHUTDOWN_FLAG_SILENT, 0); + return QUIC_STATUS_SUCCESS; + } + + DEBUG_INFO("Network connection to %s established in %s mode " + "(%s 1-RTT payloads)", this->endpoint, + encryptionDisabled ? "insecure" : "secure", + encryptionDisabled ? "unencrypted" : "encrypted"); + + HQUIC stream = NULL; + status = this->api->StreamOpen(handle, QUIC_STREAM_OPEN_FLAG_NONE, networkStreamCallback, this, &stream); if (QUIC_FAILED(status)) { @@ -3585,15 +3606,16 @@ bool networkConnectionCreate(NetworkConnection ** result, goto fail; } - this->port = params->port; - this->knownHosts = params->knownHosts; - this->trustPrompt = params->trustPrompt; - this->trustCancel = params->trustCancel; - this->packet = params->packet; - this->state = params->state; - this->sendReady = params->sendReady; - this->datagramLost = params->datagramLost; - this->opaque = params->opaque; + this->port = params->port; + this->allowUnencrypted = params->allowUnencrypted; + this->knownHosts = params->knownHosts; + this->trustPrompt = params->trustPrompt; + this->trustCancel = params->trustCancel; + this->packet = params->packet; + this->state = params->state; + this->sendReady = params->sendReady; + this->datagramLost = params->datagramLost; + this->opaque = params->opaque; this->connectEvent = lgCreateEvent(false, 0); this->closeEvent = lgCreateEvent(false, 0); @@ -3863,6 +3885,26 @@ LG_TransportStatus networkConnectionConnect(NetworkConnection * this, return LG_TRANSPORT_ERROR; } + if (this->allowUnencrypted) + { + const BOOLEAN disableEncryption = TRUE; + status = this->api->SetParam(this->connection, + QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION, + sizeof(disableEncryption), &disableEncryption); + if (QUIC_FAILED(status)) + { + DEBUG_ERROR("Failed to allow unencrypted 1-RTT payloads for %s: " + "0x%08x", this->endpoint, (unsigned)status); + this->api->ConnectionClose(this->connection); + this->connection = NULL; + LG_LOCK(this->lock); + this->connecting = false; + this->connectStatus = LG_TRANSPORT_ERROR; + LG_UNLOCK(this->lock); + return LG_TRANSPORT_ERROR; + } + } + status = this->api->ConnectionStart(this->connection, this->configuration, QUIC_ADDRESS_FAMILY_UNSPEC, this->address, this->port); if (QUIC_FAILED(status)) diff --git a/client/transports/Network/connection.h b/client/transports/Network/connection.h index f42ac9ab..258ad914 100644 --- a/client/transports/Network/connection.h +++ b/client/transports/Network/connection.h @@ -59,6 +59,7 @@ typedef struct NetworkConnectionParams const char * address; uint16_t port; const char * password; + bool allowUnencrypted; NetworkKnownHosts * knownHosts; NetworkTrustPromptFn trustPrompt; NetworkTrustCancelFn trustCancel; diff --git a/client/transports/Network/network.c b/client/transports/Network/network.c index 51c71bea..9de64d51 100644 --- a/client/transports/Network/network.c +++ b/client/transports/Network/network.c @@ -246,6 +246,13 @@ static void networkSetup(void) .sensitive = true, .value.x_string = NULL, }, + { + .module = "network", + .name = "allowUnencrypted", + .description = "Allow unencrypted 1-RTT network payloads", + .type = OPTION_TYPE_BOOL, + .value.x_bool = false, + }, { .module = "network", .name = "forgetCertificate", @@ -555,20 +562,21 @@ static bool networkCreate(LG_Transport ** result) const NetworkConnectionParams params = { - .address = address, - .port = port, - .password = option_get_string("network", "password"), - .knownHosts = &this->knownHosts, - .trustPrompt = networkTrustPrompt, - .trustCancel = networkCancelTrustPrompt, - .packet = networkPacket, - .state = networkConnectionState, - .sendReady = networkSendReady, - .datagramLost = networkDatagramLost, - .capabilities = NETWORK_CAPABILITIES, - .capabilityCount = sizeof(NETWORK_CAPABILITIES) / + .address = address, + .port = port, + .password = option_get_string("network", "password"), + .allowUnencrypted = option_get_bool("network", "allowUnencrypted"), + .knownHosts = &this->knownHosts, + .trustPrompt = networkTrustPrompt, + .trustCancel = networkCancelTrustPrompt, + .packet = networkPacket, + .state = networkConnectionState, + .sendReady = networkSendReady, + .datagramLost = networkDatagramLost, + .capabilities = NETWORK_CAPABILITIES, + .capabilityCount = sizeof(NETWORK_CAPABILITIES) / sizeof(NETWORK_CAPABILITIES[0]), - .opaque = this, + .opaque = this, }; if (!networkConnectionCreate(&this->connection, ¶ms)) { diff --git a/cmake/MsQuic.Static.targets b/cmake/MsQuic.Static.targets index e17738c7..d214cb36 100644 --- a/cmake/MsQuic.Static.targets +++ b/cmake/MsQuic.Static.targets @@ -13,7 +13,7 @@ $(LGMsQuicSourceDir)src\inc;%(AdditionalIncludeDirectories) - QUIC_BUILD_STATIC;%(PreprocessorDefinitions) + QUIC_BUILD_STATIC;QUIC_API_ENABLE_INSECURE_FEATURES=1;%(PreprocessorDefinitions) $(LGMsQuicStaticLibrary);$(LGMsQuicCoreLibrary);$(LGMsQuicPlatformLibrary);ws2_32.lib;schannel.lib;secur32.lib;OneCore.lib;wbemuuid.lib;winmm.lib;ntdll.lib;bcrypt.lib;ncrypt.lib;crypt32.lib;iphlpapi.lib;advapi32.lib;%(AdditionalDependencies) @@ -34,7 +34,7 @@ + Command="cmake -S "$(LGMsQuicSourceDir)." -B "$(LGMsQuicBuildDir)." -G "Visual Studio 17 2022" -A "$(LGMsQuicArchitecture)" -T v143 -DCMAKE_SYSTEM_VERSION="$(WindowsTargetPlatformVersion)" -DQUIC_BUILD_SHARED=OFF -DQUIC_BUILD_TOOLS=OFF -DQUIC_BUILD_TEST=OFF -DQUIC_BUILD_PERF=OFF -DQUIC_TLS_LIB=schannel -DQUIC_ENABLE_LOGGING=OFF -DQUIC_STATIC_LINK_CRT=OFF -DQUIC_STATIC_LINK_PARTIAL_CRT=OFF -DQUIC_SOURCE_LINK=OFF -DQUIC_PDBALTPATH=OFF -DQUIC_EMBED_GIT_HASH=OFF -DCMAKE_PROJECT_INCLUDE="$(MSBuildThisFileDirectory)MsQuic.cmake" -DLG_MSQUIC_DISABLE_CODE_ANALYSIS=ON -DLG_MSQUIC_ENABLE_INSECURE_FEATURES=ON" /> SetCallbackHandler(event->NEW_CONNECTION.Connection, - reinterpret_cast(ConnectionCallback), connection); - const QUIC_STATUS status = owner.api->ConnectionSetConfiguration( - event->NEW_CONNECTION.Connection, owner.configuration); + QUIC_STATUS status = QUIC_STATUS_SUCCESS; + if (owner.settings.allowUnencrypted) + { + const BOOLEAN disableEncryption = TRUE; + status = owner.api->SetParam(event->NEW_CONNECTION.Connection, + QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION, + sizeof(disableEncryption), &disableEncryption); + if (QUIC_FAILED(status)) + DEBUG_ERROR("Failed to allow unencrypted 1-RTT payloads for " + "network client %u: 0x%x", connection->client, + static_cast(status)); + } + + if (QUIC_SUCCEEDED(status)) + { + owner.api->SetCallbackHandler(event->NEW_CONNECTION.Connection, + reinterpret_cast(ConnectionCallback), connection); + status = owner.api->ConnectionSetConfiguration( + event->NEW_CONNECTION.Connection, owner.configuration); + } if (QUIC_FAILED(status)) { { @@ -5723,8 +5739,28 @@ retry: switch (event->Type) { case QUIC_CONNECTION_EVENT_CONNECTED: - DEBUG_INFO("Network client %u connected", connection.client); + { + BOOLEAN encryptionDisabled = FALSE; + uint32_t encryptionDisabledLength = sizeof(encryptionDisabled); + const QUIC_STATUS status = owner.api->GetParam(handle, + QUIC_PARAM_CONN_DISABLE_1RTT_ENCRYPTION, + &encryptionDisabledLength, &encryptionDisabled); + if (QUIC_FAILED(status)) + { + DEBUG_ERROR("Failed to query 1-RTT encryption for network " + "client %u: 0x%x", connection.client, + static_cast(status)); + owner.Shutdown(connection, NETWORK_ERROR_PROTOCOL); + return QUIC_STATUS_SUCCESS; + } + + DEBUG_INFO("Network client %u connected in %s mode " + "(%s 1-RTT payloads)", + connection.client, + encryptionDisabled ? "insecure" : "secure", + encryptionDisabled ? "unencrypted" : "encrypted"); return QUIC_STATUS_SUCCESS; + } case QUIC_CONNECTION_EVENT_PEER_STREAM_STARTED: { diff --git a/idd/LGIdd/transport/network/NetworkSettings.cpp b/idd/LGIdd/transport/network/NetworkSettings.cpp index b3a4c751..4abb920e 100644 --- a/idd/LGIdd/transport/network/NetworkSettings.cpp +++ b/idd/LGIdd/transport/network/NetworkSettings.cpp @@ -29,13 +29,14 @@ namespace { enum Field : unsigned { - FIELD_BIND = 1U << 0, - FIELD_PORT = 1U << 1, - FIELD_MAX_CLIENTS = 1U << 2, - FIELD_AUTH = 1U << 3, - FIELD_PASSWORD = 1U << 4, - FIELD_VIDEO_CHROMA = 1U << 5, - FIELD_VIDEO_BITRATE = 1U << 6, + FIELD_BIND = 1U << 0, + FIELD_PORT = 1U << 1, + FIELD_MAX_CLIENTS = 1U << 2, + FIELD_AUTH = 1U << 3, + FIELD_PASSWORD = 1U << 4, + FIELD_VIDEO_CHROMA = 1U << 5, + FIELD_VIDEO_BITRATE = 1U << 6, + FIELD_ALLOW_UNENCRYPTED = 1U << 7, }; std::wstring Trim(const std::wstring& value) @@ -93,6 +94,23 @@ namespace return true; } + bool ParseBoolean(const std::wstring& value, bool& result) + { + if (Equal(value, L"true") || Equal(value, L"yes") || + Equal(value, L"1")) + { + result = true; + return true; + } + if (Equal(value, L"false") || Equal(value, L"no") || + Equal(value, L"0")) + { + result = false; + return true; + } + return false; + } + bool SetField(const std::wstring& key, const std::wstring& rawValue, NetworkSettings& settings, unsigned& fields, std::wstring * error) @@ -139,6 +157,12 @@ namespace return Fail(error, L"invalid password"); settings.password = value; } + else if (Equal(key, L"allowUnencrypted")) + { + field = FIELD_ALLOW_UNENCRYPTED; + if (!ParseBoolean(value, settings.allowUnencrypted)) + return Fail(error, L"invalid allowUnencrypted value"); + } else if (Equal(key, L"videoChroma")) { field = FIELD_VIDEO_CHROMA; diff --git a/idd/LGIdd/transport/network/NetworkSettings.h b/idd/LGIdd/transport/network/NetworkSettings.h index f8eb2f99..8921104c 100644 --- a/idd/LGIdd/transport/network/NetworkSettings.h +++ b/idd/LGIdd/transport/network/NetworkSettings.h @@ -40,6 +40,7 @@ struct NetworkSettings uint16_t maxClients = NETWORK_DEFAULT_MAX_CLIENTS; LGNetAuthMode auth = LG_NET_AUTH_MODE_DEFAULT; std::wstring password; + bool allowUnencrypted = false; uint32_t videoBitrateMbps = NETWORK_DEFAULT_VIDEO_BITRATE_MBPS; LGNetVideoChromaSubsampling videoChroma = @@ -47,10 +48,11 @@ struct NetworkSettings }; // Settings use a semicolon-separated key=value list. Supported keys are -// bind, port, maxClients, auth, password, videoBitrateMbps, and videoChroma. -// videoBitrateMbps uses decimal Mbit/s and defaults to 200. videoChroma accepts -// 420 or 444 and defaults to 444. Password authentication requires a non-empty -// password. The default address requests a dual-stack wildcard listener from -// the eventual QUIC provider. +// bind, port, maxClients, auth, password, allowUnencrypted, videoBitrateMbps, +// and videoChroma. allowUnencrypted accepts true, false, yes, no, 1, or 0 and +// defaults to false. videoBitrateMbps uses decimal Mbit/s and defaults to 200. +// videoChroma accepts 420 or 444 and defaults to 444. Password authentication +// requires a non-empty password. The default address requests a dual-stack +// wildcard listener from the eventual QUIC provider. bool ParseNetworkSettings(const std::wstring& source, NetworkSettings& settings, std::wstring * error = nullptr);