#define SWITCH_XML_WS "\t\r\n " /* whitespace */
#define SWITCH_XML_ERRL 128 /* maximum error string length */
+/* Limits for entity expansion to prevent excessive resource consumption */
+#define SWITCH_XML_MAX_ENTITY_EXPANSION_DEPTH 20 /* Maximum recursion depth for entity expansion */
+#define SWITCH_XML_MAX_ENTITY_EXPANSION_COUNT 10000 /* Maximum number of entity expansions */
+
static void preprocess_exec_set(char *keyval)
{
char *key = keyval;
return NULL;
}
-/* checks for circular entity references, returns non-zero if no circular
- references are found, zero otherwise */
-static int switch_xml_ent_ok(char *name, char *s, char **ent)
+/* Depth-limited version with resource limits for entity validation */
+static int switch_xml_ent_ok_with_depth(char *name, char *s, char **ent, int depth, unsigned long *check_count)
{
int i;
+ /* Prevent excessive recursion during entity validation */
+ if (depth > SWITCH_XML_MAX_ENTITY_EXPANSION_DEPTH) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+ "Entity validation depth limit exceeded (%d > %d) for entity: %s\n",
+ depth, SWITCH_XML_MAX_ENTITY_EXPANSION_DEPTH, name);
+
+ return 0; /* Treat as invalid - too deep */
+ }
+
for (;; s++) {
- while (*s && *s != '&')
+ while (*s && *s != '&') {
s++; /* find next entity reference */
+ }
+
if (!*s)
return 1;
+
+ /* Increment check counter for each entity reference found */
+ if (++(*check_count) > SWITCH_XML_MAX_ENTITY_EXPANSION_COUNT) {
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_WARNING,
+ "Entity validation count limit exceeded (%lu > %d) for entity: %s\n",
+ *check_count, SWITCH_XML_MAX_ENTITY_EXPANSION_COUNT, name);
+
+ return 0; /* Treat as invalid - too many entity references */
+ }
+
if (!strncmp(s + 1, name, strlen(name)))
return 0; /* circular ref. */
+
for (i = 0; ent[i] && strncmp(ent[i], s + 1, strlen(ent[i])); i += 2);
- if (ent[i] && !switch_xml_ent_ok(name, ent[i + 1], ent))
+
+ if (ent[i] && !switch_xml_ent_ok_with_depth(name, ent[i + 1], ent, depth + 1, check_count))
return 0;
}
}
+/* checks for circular entity references, returns non-zero if no circular
+ references are found, zero otherwise */
+static int switch_xml_ent_ok(char *name, char *s, char **ent)
+{
+ unsigned long check_count = 0;
+
+ return switch_xml_ent_ok_with_depth(name, s, ent, 0, &check_count);
+}
+
/* called when the parser finds a processing instruction */
static void switch_xml_proc_inst(switch_xml_root_t root, char *s, switch_size_t len)
{
}
FST_TEST_END()
+ FST_TEST_BEGIN(test_exponential_entity_expansion)
+ {
+ /* Test handling of exponentially nested entity definitions
+ * Each entity references the previous one 10 times, creating
+ * 10^10 total references which would consume excessive memory
+ * if fully expanded. Parser should enforce expansion limits.
+ */
+ const char *nested_entities =
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE lolz [\n"
+ "<!ENTITY lol \"lol\">\n"
+ "<!ELEMENT lolz (#PCDATA)>\n"
+ "<!ENTITY lol1 \"&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;&lol;\">\n"
+ "<!ENTITY lol2 \"&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;&lol1;\">\n"
+ "<!ENTITY lol3 \"&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;&lol2;\">\n"
+ "<!ENTITY lol4 \"&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;&lol3;\">\n"
+ "<!ENTITY lol5 \"&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;&lol4;\">\n"
+ "<!ENTITY lol6 \"&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;&lol5;\">\n"
+ "<!ENTITY lol7 \"&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;&lol6;\">\n"
+ "<!ENTITY lol8 \"&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;&lol7;\">\n"
+ "<!ENTITY lol9 \"&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;&lol8;\">\n"
+ "<!ENTITY lol10 \"&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;&lol9;\">\n"
+ "]>\n"
+ "<lolz>&lol10;</lolz>";
+
+ switch_xml_t xml = switch_xml_parse_str_dynamic((char *)nested_entities, SWITCH_TRUE);
+
+ if (xml) {
+ const char *error = switch_xml_error(xml);
+ if (error && *error) {
+ /* Parser enforced expansion limits */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
+ "Parser correctly enforced entity expansion limits: %s\n", error);
+ switch_xml_free(xml);
+ } else {
+ /* Parser did not enforce limits */
+ switch_xml_free(xml);
+ fst_fail("Parser did not enforce entity expansion limits");
+ }
+ } else {
+ /* Parser returned NULL */
+ switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_INFO,
+ "Parser rejected excessive entity expansion\n");
+ }
+ }
+ FST_TEST_END()
+
+ FST_TEST_BEGIN(test_entity_expansion_limit)
+ {
+ /* Test that reasonable entity usage still works */
+ const char *safe_entities =
+ "<?xml version=\"1.0\"?>\n"
+ "<!DOCTYPE test [\n"
+ "<!ENTITY company \"FreeSWITCH\">\n"
+ "<!ENTITY product \"&company; Media Server\">\n"
+ "]>\n"
+ "<test>&product;</test>";
+
+ switch_xml_t xml = switch_xml_parse_str_dynamic((char *)safe_entities, SWITCH_TRUE);
+
+ fst_requires(xml);
+ fst_check_string_equals(xml->txt, "FreeSWITCH Media Server");
+ switch_xml_free(xml);
+ }
+ FST_TEST_END()
}
FST_SUITE_END()
}
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>\r
+<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">\r
+ <ItemGroup Label="ProjectConfigurations">\r
+ <ProjectConfiguration Include="Debug|Win32">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Debug|x64">\r
+ <Configuration>Debug</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|Win32">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>Win32</Platform>\r
+ </ProjectConfiguration>\r
+ <ProjectConfiguration Include="Release|x64">\r
+ <Configuration>Release</Configuration>\r
+ <Platform>x64</Platform>\r
+ </ProjectConfiguration>\r
+ </ItemGroup>\r
+ <PropertyGroup Label="Globals">\r
+ <ProjectName>test_switch_xml</ProjectName>\r
+ <RootNamespace>test_switch_xml</RootNamespace>\r
+ <Keyword>Win32Proj</Keyword>\r
+ <WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>\r
+ <ProjectGuid>{FED39CFF-E6CD-1C09-844D-F1174B8A5C18}</ProjectGuid>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <CharacterSet>MultiByte</CharacterSet>\r
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <CharacterSet>MultiByte</CharacterSet>\r
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <CharacterSet>MultiByte</CharacterSet>\r
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>\r
+ </PropertyGroup>\r
+ <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">\r
+ <ConfigurationType>Application</ConfigurationType>\r
+ <CharacterSet>MultiByte</CharacterSet>\r
+ <PlatformToolset>$(DefaultPlatformToolset)</PlatformToolset>\r
+ </PropertyGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />\r
+ <Import Project="$(SolutionDir)w32\openssl.props" />\r
+ <ImportGroup Label="ExtensionSettings">\r
+ </ImportGroup>\r
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ <Import Project="$(SolutionDir)w32\winlibs.props" />\r
+ </ImportGroup>\r
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ <Import Project="$(SolutionDir)w32\winlibs.props" />\r
+ </ImportGroup>\r
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ <Import Project="$(SolutionDir)w32\winlibs.props" />\r
+ </ImportGroup>\r
+ <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">\r
+ <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />\r
+ <Import Project="$(SolutionDir)w32\winlibs.props" />\r
+ </ImportGroup>\r
+ <PropertyGroup Label="UserMacros" />\r
+ <PropertyGroup>\r
+ <_ProjectFileVersion>10.0.30319.1</_ProjectFileVersion>\r
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(SolutionDir)$(PlatformName)\$(Configuration)\</OutDir>\r
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">$(PlatformName)\$(Configuration)\$(ProjectName)\</IntDir>\r
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">false</LinkIncremental>\r
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(SolutionDir)$(PlatformName)\$(Configuration)\</OutDir>\r
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>\r
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">false</LinkIncremental>\r
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(SolutionDir)$(PlatformName)\$(Configuration)\</OutDir>\r
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">$(PlatformName)\$(Configuration)\$(ProjectName)\</IntDir>\r
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">false</LinkIncremental>\r
+ <OutDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(SolutionDir)$(Platform)\$(Configuration)\</OutDir>\r
+ <IntDir Condition="'$(Configuration)|$(Platform)'=='Release|x64'">$(Platform)\$(Configuration)\$(ProjectName)\</IntDir>\r
+ <LinkIncremental Condition="'$(Configuration)|$(Platform)'=='Release|x64'">false</LinkIncremental>\r
+ </PropertyGroup>\r
+ <ItemDefinitionGroup>\r
+ <ClCompile>\r
+ <AdditionalIncludeDirectories>$(SolutionDir)src\include;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>\r
+ <PreprocessorDefinitions>SWITCH_TEST_BASE_DIR_FOR_CONF="..\\..\\tests\\unit\\";%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ </ClCompile>\r
+ <Link>\r
+ <AdditionalDependencies>Bcrypt.lib;%(AdditionalDependencies)</AdditionalDependencies>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">\r
+ <BuildLog />\r
+ <ClCompile>\r
+ <Optimization>Disabled</Optimization>\r
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <MinimalRebuild>true</MinimalRebuild>\r
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r
+ <PrecompiledHeader>\r
+ </PrecompiledHeader>\r
+ <WarningLevel>Level4</WarningLevel>\r
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
+ <EnablePREfast>true</EnablePREfast>\r
+ <DisableSpecificWarnings>6031;6340;6246;6011;6387;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
+ </ClCompile>\r
+ <Link>\r
+ <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ <SubSystem>Console</SubSystem>\r
+ <RandomizedBaseAddress>true</RandomizedBaseAddress>\r
+ <DataExecutionPrevention>\r
+ </DataExecutionPrevention>\r
+ <TargetMachine>MachineX86</TargetMachine>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">\r
+ <BuildLog />\r
+ <Midl>\r
+ <TargetEnvironment>X64</TargetEnvironment>\r
+ </Midl>\r
+ <ClCompile>\r
+ <Optimization>Disabled</Optimization>\r
+ <PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <MinimalRebuild>true</MinimalRebuild>\r
+ <BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>\r
+ <RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>\r
+ <PrecompiledHeader>\r
+ </PrecompiledHeader>\r
+ <WarningLevel>Level4</WarningLevel>\r
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
+ <EnablePREfast>true</EnablePREfast>\r
+ <DisableSpecificWarnings>6031;6340;6246;6011;6387;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
+ </ClCompile>\r
+ <Link>\r
+ <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+ <GenerateDebugInformation>true</GenerateDebugInformation>\r
+ <SubSystem>Console</SubSystem>\r
+ <RandomizedBaseAddress>true</RandomizedBaseAddress>\r
+ <DataExecutionPrevention>\r
+ </DataExecutionPrevention>\r
+ <TargetMachine>MachineX64</TargetMachine>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">\r
+ <BuildLog />\r
+ <ClCompile>\r
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r
+ <PrecompiledHeader>\r
+ </PrecompiledHeader>\r
+ <WarningLevel>Level4</WarningLevel>\r
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
+ <DisableSpecificWarnings>6031;6340;6246;6011;6387;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
+ </ClCompile>\r
+ <Link>\r
+ <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+ <GenerateDebugInformation>false</GenerateDebugInformation>\r
+ <SubSystem>Console</SubSystem>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <RandomizedBaseAddress>true</RandomizedBaseAddress>\r
+ <DataExecutionPrevention>\r
+ </DataExecutionPrevention>\r
+ <TargetMachine>MachineX86</TargetMachine>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">\r
+ <BuildLog />\r
+ <Midl>\r
+ <TargetEnvironment>X64</TargetEnvironment>\r
+ </Midl>\r
+ <ClCompile>\r
+ <PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>\r
+ <RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>\r
+ <PrecompiledHeader>\r
+ </PrecompiledHeader>\r
+ <WarningLevel>Level4</WarningLevel>\r
+ <DebugInformationFormat>ProgramDatabase</DebugInformationFormat>\r
+ <DisableSpecificWarnings>6031;6340;6246;6011;6387;%(DisableSpecificWarnings)</DisableSpecificWarnings>\r
+ </ClCompile>\r
+ <Link>\r
+ <AdditionalLibraryDirectories>$(OutDir);%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>\r
+ <GenerateDebugInformation>false</GenerateDebugInformation>\r
+ <SubSystem>Console</SubSystem>\r
+ <OptimizeReferences>true</OptimizeReferences>\r
+ <EnableCOMDATFolding>true</EnableCOMDATFolding>\r
+ <RandomizedBaseAddress>true</RandomizedBaseAddress>\r
+ <DataExecutionPrevention>\r
+ </DataExecutionPrevention>\r
+ <TargetMachine>MachineX64</TargetMachine>\r
+ </Link>\r
+ </ItemDefinitionGroup>\r
+ <ItemGroup>\r
+ <ClCompile Include="switch_xml.c" />\r
+ </ItemGroup>\r
+ <ItemGroup>\r
+ <ProjectReference Include="$(SolutionDir)w32\Library\FreeSwitchCore.2017.vcxproj">\r
+ <Project>{202d7a4e-760d-4d0e-afa1-d7459ced30ff}</Project>\r
+ <ReferenceOutputAssembly>false</ReferenceOutputAssembly>\r
+ </ProjectReference>\r
+ </ItemGroup>\r
+ <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />\r
+ <ImportGroup Label="ExtensionTargets">\r
+ </ImportGroup>\r
+</Project>
\ No newline at end of file