diff options
| author | karlf <[email protected]> | 2016-08-11 13:28:13 -0700 |
|---|---|---|
| committer | karlf <[email protected]> | 2016-08-11 13:28:13 -0700 |
| commit | 96eb96dfb613e4c745db6bd1f53a92fe7e2290fc (patch) | |
| tree | ad5f3ede5cbcd6b598677ce41bcf8318471bdd92 /network/trans | |
| parent | 687b274aa38fd05c8c26e3068932121876d7f745 (diff) | |
Updated for "Windows 10 Anniversary Update" (Version 1607)
Diffstat (limited to 'network/trans')
30 files changed, 4659 insertions, 3020 deletions
diff --git a/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp index f2782ce7..e70d9999 100644 --- a/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp +++ b/network/trans/WFPSampler/syslib/HelperFunctions_Headers.cpp @@ -113,20 +113,20 @@ NTSTATUS PrvKrnlHlprCopyBufferToMDL(_In_reads_(bytesToCopy) const BYTE* pBuffer, SIZE_T mdlByteCount = 0; SIZE_T remainingBytesToCopy = bytesToCopy; SIZE_T copySize = 0; + UINT32 noExecute = 0; *pBytesCopied = 0; - if(MmGetMdlByteCount(pMDL) >= mdlOffset + bytesToCopy) - { - BYTE* pSystemAddress = 0; - UINT32 noExecute = 0; - #if(NTDDI_VERSION >= NTDDI_WIN8) - noExecute = MdlMappingNoExecute; + noExecute = MdlMappingNoExecute; #endif /// (NTDDI_VERSION >= NTDDI_WIN8) + if(MmGetMdlByteCount(pMDL) >= mdlOffset + bytesToCopy) + { + BYTE* pSystemAddress = 0; + pSystemAddress = (BYTE*)MmGetSystemAddressForMdlSafe(pMDL, LowPagePriority | noExecute); if(pSystemAddress) @@ -171,7 +171,7 @@ NTSTATUS PrvKrnlHlprCopyBufferToMDL(_In_reads_(bytesToCopy) const BYTE* pBuffer, mdlByteCount); pSystemAddress = (BYTE*)MmGetSystemAddressForMdlSafe(pMDL, - LowPagePriority); + LowPagePriority | noExecute); if(pSystemAddress) { RtlCopyMemory(pSystemAddress + mdlOffset, diff --git a/network/trans/ddproxy/ddproxy.sln b/network/trans/ddproxy/ddproxy.sln index 0b35cc64..f5f56da8 100644 --- a/network/trans/ddproxy/ddproxy.sln +++ b/network/trans/ddproxy/ddproxy.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddproxy", "sys\ddproxy.vcxproj", "{88969E59-3140-4A06-8E52-7E9483F6E42C}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ddproxy", "sys\ddproxy.vcxproj", "{FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|Win32.ActiveCfg = Debug|Win32 - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|Win32.Build.0 = Debug|Win32 - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|Win32.ActiveCfg = Release|Win32 - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|Win32.Build.0 = Release|Win32 - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|x64.ActiveCfg = Debug|x64 - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Debug|x64.Build.0 = Debug|x64 - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|x64.ActiveCfg = Release|x64 - {88969E59-3140-4A06-8E52-7E9483F6E42C}.Release|x64.Build.0 = Release|x64 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|Win32.ActiveCfg = Debug|Win32 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|Win32.Build.0 = Debug|Win32 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|Win32.ActiveCfg = Release|Win32 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|Win32.Build.0 = Release|Win32 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|x64.ActiveCfg = Debug|x64 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Debug|x64.Build.0 = Debug|x64 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|x64.ActiveCfg = Release|x64 + {FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj b/network/trans/ddproxy/sys/ddproxy.vcxproj index 09c0e9bc..4e2da962 100644 --- a/network/trans/ddproxy/sys/ddproxy.vcxproj +++ b/network/trans/ddproxy/sys/ddproxy.vcxproj @@ -19,12 +19,12 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{88969E59-3140-4A06-8E52-7E9483F6E42C}</ProjectGuid> + <ProjectGuid>{FEABD37B-18D6-4A7B-9AD2-F5A65A904A57}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{DA73B837-D1BB-4D0D-908B-717988311EE3}</SampleGuid> + <SampleGuid>{6462DC10-B4DC-441C-8A08-D8E436A4566C}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters index 031dba36..8e16cf3d 100644 --- a/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters +++ b/network/trans/ddproxy/sys/ddproxy.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{8D3EB44B-EF28-4AEB-BB5B-C48CB78F4C77}</UniqueIdentifier> + <UniqueIdentifier>{BFAC6914-B9A3-4F2B-BFE9-BFC27304CA74}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{E9F8ED7B-6F16-4D51-ADE0-E86D3E1BD5CE}</UniqueIdentifier> + <UniqueIdentifier>{8FB969E4-D27A-4B85-832B-EACA8E280317}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{4EEA7F36-617A-4A62-BE16-0D2EBF088BEF}</UniqueIdentifier> + <UniqueIdentifier>{293AD434-85E8-431D-9C4A-989DBCE5D623}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{B59BFA52-A599-4C9B-9360-ABB5C95276BC}</UniqueIdentifier> + <UniqueIdentifier>{9B57B05B-FB4B-4582-8F2B-E95E2F7CD3E3}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/network/trans/inspect/inspect.sln b/network/trans/inspect/inspect.sln index e5e2cc7e..40b0cead 100644 --- a/network/trans/inspect/inspect.sln +++ b/network/trans/inspect/inspect.sln @@ -3,7 +3,7 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inspect", "sys\inspect.vcxproj", "{72A988A3-0603-4315-A76D-AADCC057DB3F}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "inspect", "sys\inspect.vcxproj", "{5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -13,14 +13,14 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|Win32.ActiveCfg = Debug|Win32 - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|Win32.Build.0 = Debug|Win32 - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|Win32.ActiveCfg = Release|Win32 - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|Win32.Build.0 = Release|Win32 - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|x64.ActiveCfg = Debug|x64 - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Debug|x64.Build.0 = Debug|x64 - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|x64.ActiveCfg = Release|x64 - {72A988A3-0603-4315-A76D-AADCC057DB3F}.Release|x64.Build.0 = Release|x64 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.ActiveCfg = Debug|Win32 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|Win32.Build.0 = Debug|Win32 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.ActiveCfg = Release|Win32 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|Win32.Build.0 = Release|Win32 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|x64.ActiveCfg = Debug|x64 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Debug|x64.Build.0 = Debug|x64 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|x64.ActiveCfg = Release|x64 + {5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/trans/inspect/sys/inspect.c b/network/trans/inspect/sys/inspect.c index 26b01e9b..418daf9a 100644 --- a/network/trans/inspect/sys/inspect.c +++ b/network/trans/inspect/sys/inspect.c @@ -1233,6 +1233,8 @@ TLInspectWorker( KLOCK_QUEUE_HANDLE packetQueueLockHandle; KLOCK_QUEUE_HANDLE connListLockHandle; + BOOLEAN found = FALSE; + UNREFERENCED_PARAMETER(StartContext); for(;;) @@ -1261,15 +1263,45 @@ TLInspectWorker( if (!IsListEmpty(&gConnList)) { - _Analysis_assume_(gConnList.Flink != NULL); - listEntry = gConnList.Flink; + // + // Skip pended connections in the list, for which the auth decision is already taken. + // They should not be for inbound connections. + // + _Analysis_assume_(gConnList.Flink != NULL); + for (listEntry = gConnList.Flink; + listEntry != &gConnList; + listEntry = listEntry->Flink) + { + packet = CONTAINING_RECORD( + listEntry, + TL_INSPECT_PENDED_PACKET, + listEntry + ); - packet = CONTAINING_RECORD( - listEntry, - TL_INSPECT_PENDED_PACKET, - listEntry - ); - if (packet->direction == FWP_DIRECTION_INBOUND) + NT_ASSERT((packet->direction == FWP_DIRECTION_INBOUND) || + (packet->authConnectDecision == 0)); + + if (packet->authConnectDecision == 0) + { + found = TRUE; + break; + } + } + + // + // If not found, reset entry and packet + // + if (!found) + { + listEntry = NULL; + packet = NULL; + } + + // + // Completing a pended recv_accept auth does not trigger reauth. + // So the pended entries for AUTH_RECV_ACCEPT are removed here. + // + if (packet != NULL && packet->direction == FWP_DIRECTION_INBOUND) { RemoveEntryList(&packet->listEntry); } diff --git a/network/trans/inspect/sys/inspect.vcxproj b/network/trans/inspect/sys/inspect.vcxproj index 7c7ef16b..2115972e 100644 --- a/network/trans/inspect/sys/inspect.vcxproj +++ b/network/trans/inspect/sys/inspect.vcxproj @@ -19,12 +19,12 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{72A988A3-0603-4315-A76D-AADCC057DB3F}</ProjectGuid> + <ProjectGuid>{5CF7CFC1-02B4-4938-AC5F-47D743D82E8A}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{9F9DA1EB-580C-4DA1-A053-63D6E3EE2D2F}</SampleGuid> + <SampleGuid>{910EE323-2C33-42E0-B2DD-3C3BB1F4689E}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/network/trans/inspect/sys/inspect.vcxproj.Filters b/network/trans/inspect/sys/inspect.vcxproj.Filters index 602cb4af..96fc4202 100644 --- a/network/trans/inspect/sys/inspect.vcxproj.Filters +++ b/network/trans/inspect/sys/inspect.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{8A1B2EA9-BFC6-4FD8-B2F2-0216B285279F}</UniqueIdentifier> + <UniqueIdentifier>{A71F6518-899B-48DE-AA03-736717A44CB7}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{A6A4AE15-BDDC-4797-B4FA-CE4EB8CF8E04}</UniqueIdentifier> + <UniqueIdentifier>{DAB5B4B7-AD3F-47C2-9EAD-148B9BC6A298}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{BC4CCBE2-11CC-4557-8986-1D28735C2F00}</UniqueIdentifier> + <UniqueIdentifier>{1A63D560-FDA1-4F2F-8133-00B67CBBA270}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{B4358050-C623-4EE4-9397-7BA2FFC69B7E}</UniqueIdentifier> + <UniqueIdentifier>{B163A086-1586-4E13-AED0-DA6AEEE0D31F}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/network/trans/msnmntr/exe/monitor.vcxproj b/network/trans/msnmntr/exe/monitor.vcxproj index ced8f9d3..242045dd 100644 --- a/network/trans/msnmntr/exe/monitor.vcxproj +++ b/network/trans/msnmntr/exe/monitor.vcxproj @@ -19,11 +19,11 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{73B16E02-2FA9-45E9-BAC6-D2371C627EE8}</ProjectGuid> + <ProjectGuid>{A775BC8D-48D4-4332-B731-6135FDD0C94A}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{7817CD59-C9C2-4DE2-90D9-EF9E3F2489A8}</SampleGuid> + <SampleGuid>{2142A4F7-2EDA-43A2-AA9B-237435A8B04E}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/network/trans/msnmntr/exe/monitor.vcxproj.Filters b/network/trans/msnmntr/exe/monitor.vcxproj.Filters index 64c456be..c4048b2e 100644 --- a/network/trans/msnmntr/exe/monitor.vcxproj.Filters +++ b/network/trans/msnmntr/exe/monitor.vcxproj.Filters @@ -3,15 +3,15 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{C70F02F2-1801-4CE3-9B14-C3AF71C4A36B}</UniqueIdentifier> + <UniqueIdentifier>{23749ED7-3361-4C55-BC65-7FEB41F22410}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{3CE61C1C-1EDB-4A29-82C7-5BDEC0568397}</UniqueIdentifier> + <UniqueIdentifier>{514D9180-B3CA-48A3-A0A1-6E1693F852C4}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{0A993A34-46CF-4CCB-B3AE-1714A7538940}</UniqueIdentifier> + <UniqueIdentifier>{DB4C51F6-0F6E-404E-9F69-06769044B4B1}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/network/trans/msnmntr/msnmntr.sln b/network/trans/msnmntr/msnmntr.sln index 9c496dca..364b4268 100644 --- a/network/trans/msnmntr/msnmntr.sln +++ b/network/trans/msnmntr/msnmntr.sln @@ -3,13 +3,13 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2013 VisualStudioVersion = 12.0 MinimumVisualStudioVersion = 12.0 -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{1F0A7D31-AD24-4250-A45D-1C6ED72D3AA9}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Exe", "Exe", "{60E53BA1-BED0-47E5-9907-8CEA2995D3A4}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{E66178FA-3CEE-447F-ACEE-15052142FE60}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Sys", "Sys", "{AD377DD1-7334-4B0B-BDDA-3BF2E96C8247}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitor", "exe\monitor.vcxproj", "{73B16E02-2FA9-45E9-BAC6-D2371C627EE8}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "monitor", "exe\monitor.vcxproj", "{A775BC8D-48D4-4332-B731-6135FDD0C94A}" EndProject -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msnmntr", "sys\msnmntr.vcxproj", "{8D90BF13-8184-4C0E-A938-99B22509F363}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "msnmntr", "sys\msnmntr.vcxproj", "{0334D910-32A8-48D1-945A-A58B9F2FAD22}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -19,28 +19,28 @@ Global Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|Win32.ActiveCfg = Debug|Win32 - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|Win32.Build.0 = Debug|Win32 - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|Win32.ActiveCfg = Release|Win32 - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|Win32.Build.0 = Release|Win32 - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|x64.ActiveCfg = Debug|x64 - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Debug|x64.Build.0 = Debug|x64 - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|x64.ActiveCfg = Release|x64 - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8}.Release|x64.Build.0 = Release|x64 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|Win32.ActiveCfg = Debug|Win32 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|Win32.Build.0 = Debug|Win32 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|Win32.ActiveCfg = Release|Win32 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|Win32.Build.0 = Release|Win32 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|x64.ActiveCfg = Debug|x64 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Debug|x64.Build.0 = Debug|x64 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|x64.ActiveCfg = Release|x64 - {8D90BF13-8184-4C0E-A938-99B22509F363}.Release|x64.Build.0 = Release|x64 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|Win32.ActiveCfg = Debug|Win32 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|Win32.Build.0 = Debug|Win32 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|Win32.ActiveCfg = Release|Win32 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|Win32.Build.0 = Release|Win32 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|x64.ActiveCfg = Debug|x64 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Debug|x64.Build.0 = Debug|x64 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|x64.ActiveCfg = Release|x64 + {A775BC8D-48D4-4332-B731-6135FDD0C94A}.Release|x64.Build.0 = Release|x64 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|Win32.ActiveCfg = Debug|Win32 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|Win32.Build.0 = Debug|Win32 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|Win32.ActiveCfg = Release|Win32 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|Win32.Build.0 = Release|Win32 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|x64.ActiveCfg = Debug|x64 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Debug|x64.Build.0 = Debug|x64 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|x64.ActiveCfg = Release|x64 + {0334D910-32A8-48D1-945A-A58B9F2FAD22}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {73B16E02-2FA9-45E9-BAC6-D2371C627EE8} = {1F0A7D31-AD24-4250-A45D-1C6ED72D3AA9} - {8D90BF13-8184-4C0E-A938-99B22509F363} = {E66178FA-3CEE-447F-ACEE-15052142FE60} + {A775BC8D-48D4-4332-B731-6135FDD0C94A} = {60E53BA1-BED0-47E5-9907-8CEA2995D3A4} + {0334D910-32A8-48D1-945A-A58B9F2FAD22} = {AD377DD1-7334-4B0B-BDDA-3BF2E96C8247} EndGlobalSection EndGlobal diff --git a/network/trans/msnmntr/sys/msnmntr.vcxproj b/network/trans/msnmntr/sys/msnmntr.vcxproj index 3e5e6b63..07458206 100644 --- a/network/trans/msnmntr/sys/msnmntr.vcxproj +++ b/network/trans/msnmntr/sys/msnmntr.vcxproj @@ -19,12 +19,12 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{8D90BF13-8184-4C0E-A938-99B22509F363}</ProjectGuid> + <ProjectGuid>{0334D910-32A8-48D1-945A-A58B9F2FAD22}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{CBB4D2BA-9991-40A9-BDA7-A164E72B50A4}</SampleGuid> + <SampleGuid>{E5A95292-DFAE-41E4-A0B2-62D97EC6575F}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> diff --git a/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters index c367ced7..3ed91334 100644 --- a/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters +++ b/network/trans/msnmntr/sys/msnmntr.vcxproj.Filters @@ -3,19 +3,19 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{21B35188-8ABA-4678-8D98-453C58E58904}</UniqueIdentifier> + <UniqueIdentifier>{57260C1A-8BC8-4BFB-8443-187F6386E3CD}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{203B4798-2BF9-4998-8E54-C0474AD49CB7}</UniqueIdentifier> + <UniqueIdentifier>{E379C1F3-41C5-407E-A95E-087A8732A4CE}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{B3C8566B-2D21-4AA7-BC03-4EE7DED7DD43}</UniqueIdentifier> + <UniqueIdentifier>{552405F1-D8E0-402D-85DD-0B8E345F0634}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{6DB69CEC-0955-4458-8D01-665D15B180CA}</UniqueIdentifier> + <UniqueIdentifier>{F56EC5D2-6494-433C-9F61-D4F039E15BE3}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> diff --git a/network/trans/stmedit/stmedit.sln b/network/trans/stmedit/stmedit.sln index e99fea00..1dc7150f 100644 --- a/network/trans/stmedit/stmedit.sln +++ b/network/trans/stmedit/stmedit.sln @@ -1,26 +1,27 @@ Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 2013 -VisualStudioVersion = 12.0 +# Visual Studio 14 +VisualStudioVersion = 14.0.24720.0 MinimumVisualStudioVersion = 12.0 -Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stmedit", "sys\stmedit.vcxproj", "{00B26024-D8C5-40FD-A6C7-BA15FD324FD0}" +Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "stmedit", "stmedit.vcxproj", "{9CE912A5-6210-4EF8-B22D-611D13254D4C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 - Release|Win32 = Release|Win32 Debug|x64 = Debug|x64 + Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|Win32.ActiveCfg = Debug|Win32 - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|Win32.Build.0 = Debug|Win32 - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|Win32.ActiveCfg = Release|Win32 - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|Win32.Build.0 = Release|Win32 - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|x64.ActiveCfg = Debug|x64 - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Debug|x64.Build.0 = Debug|x64 - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|x64.ActiveCfg = Release|x64 - {00B26024-D8C5-40FD-A6C7-BA15FD324FD0}.Release|x64.Build.0 = Release|x64 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|Win32.ActiveCfg = Debug|Win32 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|Win32.Build.0 = Debug|Win32 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|x64.ActiveCfg = Release|Win32 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|x64.Build.0 = Release|Win32 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Debug|x64.Deploy.0 = Release|Win32 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|Win32.ActiveCfg = Release|Win32 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|Win32.Build.0 = Release|Win32 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|x64.ActiveCfg = Release|x64 + {9CE912A5-6210-4EF8-B22D-611D13254D4C}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/network/trans/stmedit/sys/InlineEdit.c b/network/trans/stmedit/sys/InlineEdit.c new file mode 100644 index 00000000..a251d722 --- /dev/null +++ b/network/trans/stmedit/sys/InlineEdit.c @@ -0,0 +1,566 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + Stream Edit Callout Driver Sample. + + This sample demonstrates inline stream inspection/editing + via the WFP stream API. + +Environment: + Kernel mode +--*/ + +#include "Trace.h" +#include "StreamEdit.h" +#include "InlineEdit.tmh" + +#if defined _MODULE_ID +#undef _MODULE_ID +#endif +#define _MODULE_ID 'I' + +NTSTATUS +InlineEditFlushData( + _In_ STREAM_FLOW_CONTEXT *pFlowContext, + _In_ ULONG DataLength, + _In_ UINT StreamFlags + ) +/* + This function re-injects buffered data back to the data stream. + The data was buffered because it was not big enough (size wise) + to make an editing decision. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + PVOID Buffer = NULL; + MDL* mdl = NULL; + NET_BUFFER_LIST* NetBufferList = NULL; + + NT_ASSERT(!(StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && + !(StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); + + if (DataLength == 0) + { + DataLength = (ULONG)pFlowContext->ScratchDataLength; + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, Flushing %u bytes", pFlowContext, DataLength); + do + { + if (DataLength == 0) + break; + + Buffer = ExAllocatePoolWithTag(NonPagedPool, DataLength, STMEDIT_TAG_MDL_DATA); + if (Buffer == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate Buffer to flush data!"); + break; + } + + // Copy the contents that need to be flushed. + RtlMoveMemory(Buffer, pFlowContext->ScratchBuffer, DataLength); + + mdl = IoAllocateMdl( + Buffer, + DataLength, + FALSE, + FALSE, + NULL); + + if (mdl == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate MDL"); + break; + } + + MmBuildMdlForNonPagedPool(mdl); + + Status = FwpsAllocateNetBufferAndNetBufferList( + Globals.NetBufferListPool, + 0, + 0, + mdl, + 0, + DataLength, + &NetBufferList); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "FwpsAllocateNetBufferAndNetBufferList Failed with %!STATUS!", Status); + break; + } + + Status = FwpsStreamInjectAsync( + Globals.InjectionHandle, + NULL, + 0, + pFlowContext->FlowHandle, + pFlowContext->CalloutId, + pFlowContext->LayerId, + StreamFlags, + NetBufferList, + DataLength, + StreamEditInjectCompletionFn, + mdl); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "FwpsStreamInjectAsync failed with %!STATUS!", Status); + break; + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Flushed %lu bytes via NBL %p, MDL %p", + pFlowContext, DataLength, NetBufferList, mdl); + + // Control transferred to WFP. + mdl = NULL; + NetBufferList = NULL; + Buffer = NULL; + + } while (FALSE); + + + if (!NT_SUCCESS(Status)) + { + if (Buffer != NULL) + { + ExFreePoolWithTag(Buffer, STMEDIT_TAG_MDL_DATA); + } + + if (mdl != NULL) + { + IoFreeMdl(mdl); + } + + if (NetBufferList != NULL) + { + FwpsFreeNetBufferList(NetBufferList); + } + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", pFlowContext, Status); + return Status; +} + +#define PermitBytes(_l)\ +{\ + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE;\ + ioPacket->countBytesEnforced = (_l);\ + ClassifyOut->actionType = FWP_ACTION_PERMIT;\ +} + +NTSTATUS +InlineInjectToken( + PSTREAM_FLOW_CONTEXT FlowContext, + UINT32 StreamFlags + ) +/* + Inject a replacement token into the data stream! +*/ +{ + NTSTATUS Status; + NET_BUFFER_LIST* NetBufferList; + + do + { + Status = FwpsAllocateNetBufferAndNetBufferList( + Globals.NetBufferListPool, + 0, + 0, + Globals.StringToReplaceMdl, + 0, + Globals.StringToReplaceLength, + &NetBufferList + ); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, + "FlowCtx %p, FwpsAllocateNetBufferAndNetBufferList failed with %#x, dropping connection", + FlowContext, Status); + break; + } + + Status = FwpsStreamInjectAsync( + Globals.InjectionHandle, + NULL, + 0, + FlowContext->FlowHandle, + FlowContext->CalloutId, + FlowContext->LayerId, + StreamFlags, + NetBufferList, + Globals.StringToReplaceLength, + StreamEditInjectCompletionFn, + NULL + ); + + if (!NT_SUCCESS(Status)) + { + FwpsFreeNetBufferList(NetBufferList); + + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, + "FlowCtx %p, FwpsStreamInjectAsync failed with %!STATUS!, dropping connection", + FlowContext, Status); + break; + } + + FlowContext->ScratchDataOffset += Globals.StringXLength; + FlowContext->ScratchDataLength -= Globals.StringXLength; + + if (FlowContext->ScratchDataLength > 0) + { + FlowContext->InlineEditState = INLINE_EDIT_SCANNING; + } + else + { + FlowContext->ScratchDataOffset = 0; + FlowContext->InlineEditState = INLINE_EDIT_IDLE; + } + } while (FALSE); + + return Status; +} + + +VOID +NTAPI +InlineEditClassify( + _In_ const FWPS_INCOMING_VALUES* InFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues, + _Inout_ VOID* LayerData, + _In_ const FWPS_FILTER* Filter, + _In_ UINT64 InFlowContext, + _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut + ) +/* + This is the ClassifyFn function registered by the inline stream edit callout. + + An inline stream modification callout performs editing from within the + classifyFn call by permitting sections of the content and replacing other + sections by removing them and injecting new content. + + Here we implement the state machine that scans the content and computes the + number of bytes to permit, bytes to block, and performs stream injection to + replace the blocked data. + + @ Inline Stream Inspection + @ https://msdn.microsoft.com/en-us/library/windows/hardware/ff570891.aspx + + To replace a pattern Found in the middle of an indicated segment (for example, + n bytes followed by a pattern of p bytes followed by m bytes), the callout + would follow these steps: + + 1. The callout's classifyFn function is invoked with 'n + p + m' bytes. + 2. The callout returns FWP_ACTION_PERMIT with countBytesEnforced set to n. + 3. The callout's classifyFn function is called again with p + m bytes. + (WFP will call classifyFn again if countBytesEnforced is less than the + indicated amount.) + 4. From the classifyFn function, the callout calls the FwpStreamInjectAsync0 + function to inject the replacement pattern for p. The callout then + returns FWP_ACTION_BLOCK with countBytesEnforced set to p. + 5. The callout's classifyFn function is called again with m bytes. + 6. The callout returns FWP_ACTION_PERMIT with countBytesEnforced set to m. + + If the indicated data is insufficient for the callout to make an inspection + decision, it can set FWPS_STREAM_CALLOUT_IO_PACKET->streamAction to + FWPS_STREAM_ACTION_NEED_MORE_DATA and set the countBytesRequired member to the + minimal amount WFP should accumulate before the data is indicated again. When + streamAction is set, the callout should return FWP_ACTION_NONE from the + classifyFn function. +*/ +{ + FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket; + FWPS_STREAM_DATA* streamData; + PSTREAM_FLOW_CONTEXT FlowContext = (PSTREAM_FLOW_CONTEXT)(ULONG_PTR)InFlowContext; + ULONG PartialLength = 0; + NTSTATUS Status = STATUS_SUCCESS; + + UNREFERENCED_PARAMETER(InFixedValues); + UNREFERENCED_PARAMETER(InMetaValues); + + ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)LayerData; + streamData = ioPacket->streamData; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, sFlags %#x, Length %Iu, LayerId %hu, CalloutId %u, " + "FlowId %I64u, cFlags %#x, Flow State %!InlineState!, Scratch:%Iu/%Iu", + FlowContext, + streamData->flags, + streamData->dataLength, + InFixedValues->layerId, + Filter->action.calloutId, + InMetaValues->flowHandle, + ClassifyOut->flags, + FlowContext->InlineEditState, + FlowContext->ScratchDataLength, + FlowContext->ScratchDataOffset); + + + // The following checks if the classifyFn is invoked + // simultaneously on multiple processors for the same flow. + // This should NOT happen! + { + ULONG Processor = KeGetCurrentProcessorIndex(); + Processor = InterlockedExchange(&FlowContext->CurrentProcessor, Processor); + NT_ASSERT(Processor == INVALID_PROC_NUMBER); + } + + // If a FIN/RST has been classified, flush any data and permit the FIN/RST. + // + if ((!FlowContext->bFlowActive) || + (streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || + (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, FIN/RST classified (Flow Active %d)!", FlowContext, FlowContext->bFlowActive); + + if (FlowContext->ScratchDataLength > 0) + { + InlineEditFlushData(FlowContext, 0, FlowContext->PartialSFlags); + FlowContext->ScratchDataLength = 0; + FlowContext->ScratchDataOffset = 0; + } + + NT_ASSERT(FlowContext->InlineEditState == INLINE_EDIT_IDLE); + + PermitBytes(0); + + goto Exit; + } + + if (streamData->dataLength == 0) + { + + PermitBytes(0); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p -> DataLength = 0, Permit!", FlowContext); + goto Exit; + } + + if (FlowContext->InlineEditState != INLINE_EDIT_SKIPPING) + { + if ((streamData->dataLength < Globals.StringXLength) && + !(ClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA; + ioPacket->countBytesRequired = (UINT32)Globals.StringXLength; + + ClassifyOut->actionType = FWP_ACTION_NONE; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p - need more data!", FlowContext); + goto Exit; + } + } + + switch (FlowContext->InlineEditState) + { + case INLINE_EDIT_IDLE: + { + NT_ASSERT(FlowContext->ScratchDataOffset == 0); + + PartialLength = (ULONG)FlowContext->ScratchDataLength; + if (PartialLength) + { + NT_ASSERT(FlowContext->PartialSFlags); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Partial Data Found. Length %u", FlowContext, PartialLength); + } + + if (FALSE == StreamEditCopyDataForInspection(FlowContext, streamData, streamData->dataLength)) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + ClassifyOut->actionType = FWP_ACTION_NONE; + + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, + "FlowCtx %p, failed to copy data, drop connection", FlowContext); + goto Exit; + } + + // + // Fall-thru to scanning + // + } + + case INLINE_EDIT_SCANNING: + { + UINT i; + BYTE* DataStart = (BYTE*)FlowContext->ScratchBuffer + FlowContext->ScratchDataOffset; + BOOLEAN Found = FALSE; + + for (i = 0; i < FlowContext->ScratchDataLength; ++i) + { + // Look for a Full match if we have enough data to scan + // + if (i + Globals.StringXLength <= FlowContext->ScratchDataLength) + { + if (RtlCompareMemory(DataStart + i, Globals.StringX, Globals.StringXLength ) == Globals.StringXLength) + { + // We Found a pattern match + Found = TRUE; + + FlowContext->InlineEditState = INLINE_EDIT_MODIFYING; + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Found match @ %lu", FlowContext, i-PartialLength); + + // If the match is in the middle of data packet, permit the data + // before match from (n + p + m), permit n. We'll be reclassified + // at beginning of the match (with p + m). + // + if (i != 0) + { + // Flush any left over partial match data on the scratch buffer. + if (PartialLength) + { + InlineEditFlushData(FlowContext, PartialLength, FlowContext->PartialSFlags); + } + + PermitBytes(i - PartialLength); + + FlowContext->ScratchDataOffset += i; + FlowContext->ScratchDataLength -= i; + + break; + } + else + { + Status = InlineInjectToken(FlowContext, streamData->flags); + if (NT_SUCCESS(Status)) + { + // Block the segment for which we injected a replacement + // + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = Globals.StringXLength - PartialLength; + ClassifyOut->actionType = FWP_ACTION_BLOCK; + } + break; // out of for loop. + } + } // if (RtlCompareMemory ...) + } + + // + // If we do not have enough data, try to look for a partial pattern match + // + else + { + // If we do not expect more data to come in, quit now! + if (ClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) + { + PermitBytes(0); + break; + } + + if (RtlCompareMemory(DataStart + i, Globals.StringX, FlowContext->ScratchDataLength - i ) + == FlowContext->ScratchDataLength - i) + { + Found = TRUE; // This is a partial match + FlowContext->InlineEditState = INLINE_EDIT_SKIPPING; + + // Permit data before partial match. When we get more data, we'll look for a complete match. + // + if (PartialLength) + { + InlineEditFlushData(FlowContext, PartialLength, FlowContext->PartialSFlags); + } + + PermitBytes(i - PartialLength); + + // Move partial matching data to Scratch Buffer. + // + RtlMoveMemory(FlowContext->ScratchBuffer, DataStart + i, FlowContext->ScratchDataLength - i ); + + FlowContext->PartialSFlags = streamData->flags; + + FlowContext->ScratchDataOffset = 0; + FlowContext->ScratchDataLength -= i; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p, Found partial match of %Iu byte(s) @ %lu", + FlowContext, FlowContext->ScratchDataLength, i - PartialLength); + break; + } + } + } // for ( i = 0; i < ScratchDataLength... + + // If no match is Found, inject the whole chunk back into the stream + // + if (!Found) + { + FlowContext->InlineEditState = INLINE_EDIT_IDLE; + + if (PartialLength) + { + InlineEditFlushData(FlowContext, PartialLength, FlowContext->PartialSFlags ); + } + + PermitBytes(0); + + FlowContext->ScratchDataOffset = 0; + FlowContext->ScratchDataLength = 0; + } + + break; + } + + // No new data will be classified until we consume all the data originally + // indicated. We block the partially matching data (left in scratch-buffer), + // and continue search for a full match when more data in indicated. + // + case INLINE_EDIT_SKIPPING: + { + NT_ASSERT(FWPS_STREAM_ACTION_NONE == ioPacket->streamAction); + ioPacket->countBytesEnforced = 0; + ClassifyOut->actionType = FWP_ACTION_BLOCK; + + FlowContext->InlineEditState = INLINE_EDIT_IDLE; + DoTraceLevelMessage(TRACE_LEVEL_VERBOSE, CO_GENERAL, "Contents of Blocked buffer : %!HEXDUMP!", + log_xstr(FlowContext->ScratchBuffer, (USHORT)streamData->dataLength)); + break; + } + + // Injecting a replacement pattern when a full match is + // Found at beginning of the scan buffer + case INLINE_EDIT_MODIFYING: + { + Status = InlineInjectToken(FlowContext, streamData->flags); + if (NT_SUCCESS(Status)) + { + // Block the segment for which we injected a replacement + // + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + ioPacket->countBytesEnforced = Globals.StringXLength - PartialLength; + ClassifyOut->actionType = FWP_ACTION_BLOCK; + } + break; + } + + default: + NT_ASSERT(FALSE); + break; + }; // switch + +Exit: + + if (!NT_SUCCESS (Status)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + ClassifyOut->actionType = FWP_ACTION_NONE; + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p, cOut->Action %#x, sAction %#x, #Enforced %Iu, Scratch: %Iu/%Iu, New State %!InlineState!", + FlowContext, + ClassifyOut->actionType, + ioPacket->streamAction, + ioPacket->countBytesEnforced, + FlowContext->ScratchDataLength, + FlowContext->ScratchDataOffset, + FlowContext->InlineEditState); + + InterlockedExchange(&FlowContext->CurrentProcessor, INVALID_PROC_NUMBER); + + return; +} diff --git a/network/trans/stmedit/sys/LwQueue.c b/network/trans/stmedit/sys/LwQueue.c new file mode 100644 index 00000000..a88186c7 --- /dev/null +++ b/network/trans/stmedit/sys/LwQueue.c @@ -0,0 +1,170 @@ +/*++ +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + Stream Edit Callout Driver Sample. + This file implements Light Weight queues using Worker Item routines. + +Environment: + Kernel mode + +--*/ + +#include "LwQueue.h" + +IO_WORKITEM_ROUTINE LwWorker; + +NTSTATUS +LwInitializeQueue( + _In_ PVOID IoObject, + _Out_ PLW_QUEUE Queue, + _In_ PIO_WORKITEM_ROUTINE WorkerRoutine + ) +/* + Initializes Light Weight Queue data structures! +*/ +{ + RtlZeroMemory(Queue, sizeof(Queue[0])); + + Queue->Head = &Queue->Dummy; + Queue->Tail = &Queue->Dummy; + Queue->WorkerScheduled = FALSE; + + Queue->WorkerRoutine = WorkerRoutine; + Queue->IoObject = IoObject; + + KeInitializeSpinLock(&Queue->Lock); + + Queue->WorkItem = + ExAllocatePoolWithTag( + NonPagedPool, IoSizeofWorkItem(), STMEDIT_TAG_LQWI); + + if (Queue->WorkItem == NULL) + { + return STATUS_INSUFFICIENT_RESOURCES; + } + + IoInitializeWorkItem(IoObject, Queue->WorkItem); + Queue->Initialized = TRUE; + return STATUS_SUCCESS; +} + +VOID +LwUninitializeQueue( + _Inout_ PLW_QUEUE Queue + ) +/* + De-Initializes the Light Weight Queue! +*/ +{ + if (Queue->Initialized == FALSE) + { + return; + } + + Queue->Initialized = FALSE; + + IoUninitializeWorkItem(Queue->WorkItem); + ExFreePoolWithTag(Queue->WorkItem, STMEDIT_TAG_LQWI); + Queue->WorkItem = NULL; +} + +__drv_functionClass(IO_WORKITEM_ROUTINE) +__drv_requiresIRQL(PASSIVE_LEVEL) +__drv_sameIRQL +VOID +LwWorker( + _In_ PDEVICE_OBJECT DeviceObject, + _In_opt_ PVOID Context + ) +/* + Queue processing workitem routine. + Drains the task queue and invokes OOB Workitem to process the tasks. +*/ +{ + PLW_ENTRY Entry; + PLW_QUEUE Queue = (PLW_QUEUE)Context; + + UNREFERENCED_PARAMETER(DeviceObject); + + NT_ASSERT(Queue != NULL); + Entry = LwDequeueAll(Queue); + + // + // Why were we scheduled if there are no entries? + // + NT_ASSERT(Entry != NULL); + + while (Entry != NULL) + { + // + // Invoke the caller's worker routine. + // + Queue->WorkerRoutine(DeviceObject, Entry); + + // + // Check if any other entries were added while we were + // working. + // + Entry = LwDequeueAll(Queue); + } +} + +VOID +LwEnqueue( + _In_ PLW_QUEUE Queue, + _In_ PLW_ENTRY Entry + ) +/* + Queue a task into the task queue. +*/ +{ + KLOCK_QUEUE_HANDLE LockHandle; + + NT_ASSERT(Entry != NULL); + NT_ASSERT(Queue != NULL); + + KeAcquireInStackQueuedSpinLock(&Queue->Lock, &LockHandle); + + Entry->Next = NULL; + Queue->Tail->Next = Entry; + Queue->Tail = Entry; + + if (!Queue->WorkerScheduled) + { + Queue->WorkerScheduled = TRUE; + + IoQueueWorkItem( + Queue->WorkItem, LwWorker, DelayedWorkQueue, Queue); + } + + KeReleaseInStackQueuedSpinLock(&LockHandle); +} + +PLW_ENTRY +LwDequeueAll( + _In_ PLW_QUEUE Queue + ) +{ + KLOCK_QUEUE_HANDLE LockHandle; + PLW_ENTRY Entry = NULL; + + KeAcquireInStackQueuedSpinLock(&Queue->Lock, &LockHandle); + + NT_ASSERT(Queue->Head == &Queue->Dummy); + + // + // Snap and return the entire queue contents. + // + Entry = Queue->Head->Next; + Queue->Head->Next = NULL; + Queue->Tail = Queue->Head; + + if (Entry == NULL) + { + Queue->WorkerScheduled = FALSE; + } + + KeReleaseInStackQueuedSpinLock(&LockHandle); + return Entry; +} diff --git a/network/trans/stmedit/sys/LwQueue.h b/network/trans/stmedit/sys/LwQueue.h new file mode 100644 index 00000000..d62f41aa --- /dev/null +++ b/network/trans/stmedit/sys/LwQueue.h @@ -0,0 +1,69 @@ +#ifndef _LWQUEUE_H +#define _LWQUEUE_H + +#include <wdm.h> + +#define STMEDIT_TAG_LQWI 'wLeS' // Light Weight Queue Work Items. + +typedef struct _LW_ENTRY +{ + struct _LW_ENTRY *Next; +} LW_ENTRY, *PLW_ENTRY; + +typedef struct _LW_QUEUE +{ + // Queue Head + PLW_ENTRY Head; + + // Queue Tail + PLW_ENTRY Tail; + + // Dummy Queue head. + LW_ENTRY Dummy; + + // Lock for queue synchronization + KSPIN_LOCK Lock; + + // If a workitem is already scheduled + BOOLEAN WorkerScheduled; + + // Is the Queue initialized + BOOLEAN Initialized; + + // Workitem for IoXxxWorkItem + PIO_WORKITEM WorkItem; + + // Callback routine to be invoked + PIO_WORKITEM_ROUTINE WorkerRoutine; + + // One of the caller's device objects. + PVOID IoObject; + +} LW_QUEUE, *PLW_QUEUE; + + +NTSTATUS +LwInitializeQueue( + _In_ PVOID IoObject, + _Out_ PLW_QUEUE Queue, + _In_ PIO_WORKITEM_ROUTINE WorkerRoutine + ); + +VOID +LwUninitializeQueue( + _Inout_ PLW_QUEUE Queue + ); + +VOID +LwEnqueue( + _In_ PLW_QUEUE Queue, + _In_ PLW_ENTRY Entry + ); + +PLW_ENTRY +LwDequeueAll( + _In_ PLW_QUEUE Queue + ); + + +#endif // _LWQUEUE_H diff --git a/network/trans/stmedit/sys/OobEdit.c b/network/trans/stmedit/sys/OobEdit.c new file mode 100644 index 00000000..28883ab7 --- /dev/null +++ b/network/trans/stmedit/sys/OobEdit.c @@ -0,0 +1,1412 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + Stream Edit Callout Driver Sample. + + This sample demonstrates Out-of-band (OOB) stream inspection/editing + via the WFP stream API. + +Environment: + Kernel mode + +--*/ + +#include "Trace.h" +#include "StreamEdit.h" +#include "OobEdit.tmh" + +#if defined _MODULE_ID +#undef _MODULE_ID +#endif +#define _MODULE_ID 'O' + +NTSTATUS +StreamEditInitializeWorkitemPool( + ) +{ +/* + This functions initializes the Light Weight Queue (LW_QUEUE) pool. +*/ + NTSTATUS Status; + ULONG nCount; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!"); + + for (nCount = 0; nCount < NUM_WORKITEM_QUEUES; nCount++) + { + NT_ASSERT(Globals.WdmDevice); + Status = LwInitializeQueue(Globals.WdmDevice, &Globals.ProcessingQueues[nCount], StreamEditOobPoolWorker); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Worker Queue Initialization failed with %!STATUS!", Status); + break; + } + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: Status %!STATUS!", Status); + return Status; +} + +VOID +NTAPI +StreamOobInjectCompletionFn( + _In_ PVOID Context, + _Inout_ NET_BUFFER_LIST* NetBufferList, + _In_ BOOLEAN DispatchLevel + ) +/* + Injection completion function for injecting an NBL created using + FwpsAllocateNetBufferAndNetBufferList. This function frees up + resources allocated during StreamOobReinjectData(). +*/ +{ + MDL* mdl = (MDL *)Context; + + UNREFERENCED_PARAMETER(DispatchLevel); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "-><- %!FUNC!: NBL %p, Status=%!STATUS!, MDL %p", NetBufferList, NetBufferList->Status, mdl); + + + // Supress warning 28922: Redundant test against NULL. Pointer is already guaranteed to be non-NULL. + // Rationale : mdl is not guaranteed to be non-NULL here... + +#pragma prefast(push) +#pragma prefast(disable:28922) + + if (mdl != NULL) + { + // + // The MDL mapped over a pool alloc which we need to free here. + // + ExFreePoolWithTag(mdl->MappedSystemVa, STMEDIT_TAG_MDL_DATA); + + IoFreeMdl(mdl); + } +#pragma prefast(pop) + + FwpsFreeNetBufferList(NetBufferList); +} + +VOID +NTAPI +StreamOobCloneInjectCompletionFn( + _In_ VOID* Context, + _Inout_ NET_BUFFER_LIST* NetBufferList, + _In_ BOOLEAN DispatchLevel + ) +/* + Injection completion function for injecting one of the NBLs cloned + via FwpsCloneStreamData. + + FwpsCloneStreamData can return a chain of cloned NBLs; each NBL will + complete separately. +*/ +{ + UNREFERENCED_PARAMETER(Context); + UNREFERENCED_PARAMETER(DispatchLevel); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "-><- %!FUNC!: NBL %p, Status=%!STATUS!", NetBufferList, NetBufferList->Status); + FwpsFreeCloneNetBufferList(NetBufferList, 0); +} + +_Requires_lock_held_(TaskEntry->FlowCtx->OobInfo.EditLock) +NTSTATUS +StreamOobQueueUpIncomingData( + _Inout_ PTASK_ENTRY TaskEntry, + _Inout_ FWPS_STREAM_DATA* streamData, + _In_ UINT32 flags + ) +/* + This function clones the indicated stream data into a NBL chain and + saves it in the task entry, along with other information (Flags etc.) + + This function assumes that the OobInfo lock inside the flow's context + is being held. +*/ +{ + NTSTATUS Status; + NET_BUFFER_LIST* ClonedNbl; + PSTREAM_FLOW_CONTEXT FlowCtx = TaskEntry->FlowCtx; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p, Task %p, StreamData %p", + FlowCtx, + TaskEntry, + streamData); + + Status = FwpsCloneStreamData(streamData, NULL, NULL, 0, &ClonedNbl); + if (NT_SUCCESS(Status)) + { + PLW_QUEUE Queue; + ULONG Count; + TaskEntry->StreamFlags = streamData->flags; + TaskEntry->NetBufferList = ClonedNbl; + TaskEntry->DataLength = streamData->dataLength; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "NBL Chain @ %p was Cloned as %p and added to Task %p", + streamData->netBufferListChain, ClonedNbl, TaskEntry); + + Count = InterlockedIncrement(& (LONG)FlowCtx->OobInfo.PendingTasks); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT, + "FlowCtx %p, QueueIncoming : PendingTasks @++ = %lu", FlowCtx, Count); + + // Reference the flow for task + StmEditReferenceFlow(FlowCtx, _MODULE_ID, __LINE__); + + Queue = &(Globals.ProcessingQueues[FlowCtx->OobInfo.QueueNumber]); + LwEnqueue(Queue , &TaskEntry->LwQLink); + + if (flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) + { + FlowCtx->bNoMoreData = TRUE; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p, NO_MORE_DATA flag set, bNoMoreData = TRUE.", FlowCtx); + } + + // + // TCP FIN is indicated by an empty NBL with disconnect flag set and + // does not contain any data. + // + + if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || + (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Received FIN for FlowCtx %p in Task %p", FlowCtx, TaskEntry); + + NT_ASSERT(FlowCtx->bNoMoreData); + NT_ASSERT(streamData->dataLength == 0); + + FlowCtx->bFlowTerminating = TRUE; + } + + InterlockedAdd( (LONG *)&FlowCtx->OobInfo.PendedDataLength, (LONG)streamData->dataLength); + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p, Pending %Iu, %!STATUS!", FlowCtx, FlowCtx->OobInfo.PendedDataLength, Status); + return Status; +} + +_Requires_lock_not_held_(FlowContext->OobInfo.EditLock) +NTSTATUS +StreamOobQueueUpOutgoingData( + _Inout_ PSTREAM_FLOW_CONTEXT FlowContext, + _Inout_ PNET_BUFFER_LIST NetBufferList, + _In_ BOOLEAN IsClone, + _In_ size_t DataLength, + _In_ UINT32 StreamFlags, + _In_opt_ MDL* Mdl + ) +/* + This function queues up processed data (either sections of the indicated + data or newly created data) such that they can be (re-)injected back to + the data stream during the following context. + + 1. Before FWP_ACTION_BLOCK is returned from the ClassifyFn, or + 2. After EOF is indicated. + + Under the conditions above, the incoming data (which we pend) and the + outgoing data (which we (re-)inject) can be synchronized properly). +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + KLOCK_QUEUE_HANDLE LockHandle; + POUTGOING_STREAM_DATA OutgoingData; + + NT_ASSERT(0 != StreamFlags); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, DataLength %Iu, sFlags %#x", + FlowContext, DataLength, StreamFlags); + + OutgoingData = (POUTGOING_STREAM_DATA)ExAllocateFromLookasideListEx(&Globals.LookasideList); + + if (OutgoingData == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + return Status; + } + + OutgoingData->NetBufferList = NetBufferList; + OutgoingData->isClone = IsClone; + OutgoingData->DataLength = DataLength; + OutgoingData->StreamFlags = StreamFlags; + OutgoingData->Mdl = Mdl; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "OutgoingStreamData %p has NBL %p (clone %d) with Length of %Iu, sFlags %#x, mdl %p", + OutgoingData, NetBufferList, IsClone, DataLength, StreamFlags, Mdl); + + KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle); + InsertTailList(&FlowContext->OobInfo.OutgoingDataQueue, &OutgoingData->Link); + KeReleaseInStackQueuedSpinLock(&LockHandle); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"<-- %!FUNC!: %!STATUS!", Status); + return Status; +} + +NTSTATUS +StreamOobFlushOutgoingData( + _Inout_ PSTREAM_FLOW_CONTEXT FlowContext + ) +{ + NTSTATUS Status = STATUS_SUCCESS; + + KLOCK_QUEUE_HANDLE LockHandle; + OUTGOING_STREAM_DATA* OutgoingData = NULL; + BOOLEAN FlowInError = FALSE; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p", FlowContext); + + for (;;) + { + KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle); + + if (!IsListEmpty(&FlowContext->OobInfo.OutgoingDataQueue)) + { + LIST_ENTRY* Entry = RemoveHeadList(&FlowContext->OobInfo.OutgoingDataQueue); + OutgoingData = CONTAINING_RECORD(Entry, OUTGOING_STREAM_DATA, Link); + + FlowInError = (OOB_EDIT_ERROR == FlowContext->OobInfo.EditState); + } + KeReleaseInStackQueuedSpinLock(&LockHandle); + + if (OutgoingData == NULL || FlowInError) + { + break; + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "Oob Flush Outgoing: OutgoingStreamData %p, Injecting NBL %p, Data Length %Iu, Stream Flags %#x", + OutgoingData, + OutgoingData->NetBufferList, + OutgoingData->DataLength, + OutgoingData->StreamFlags); + + Status = FwpsStreamInjectAsync( + Globals.InjectionHandle, + NULL, + 0, + FlowContext->FlowHandle, + FlowContext->CalloutId, + FlowContext->LayerId, + OutgoingData->StreamFlags, + OutgoingData->NetBufferList, + OutgoingData->DataLength, + OutgoingData->isClone ? StreamOobCloneInjectCompletionFn : StreamOobInjectCompletionFn, + OutgoingData->Mdl + ); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, + "OobFlushOutgoingData: FwpsStreamInjectAsync() failed with Status %!STATUS!", Status); + break; + } + + ExFreeToLookasideListEx(&Globals.LookasideList, OutgoingData); + OutgoingData = NULL; + } + + if (!NT_SUCCESS(Status) || FlowInError) + { + while (OutgoingData != NULL) + { + if (OutgoingData->isClone) + { + FwpsDiscardClonedStreamData(OutgoingData->NetBufferList, 0, FALSE); + } + else + { + // Invoke the injection completion routine to free the resources + StreamOobInjectCompletionFn(OutgoingData->Mdl, OutgoingData->NetBufferList, FALSE); + } + + ExFreeToLookasideListEx(&Globals.LookasideList, OutgoingData); + OutgoingData = NULL; + + // Free up any queued data. + // + KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle); + + if (!IsListEmpty(&FlowContext->OobInfo.OutgoingDataQueue)) + { + LIST_ENTRY* Entry = RemoveHeadList(&FlowContext->OobInfo.OutgoingDataQueue); + OutgoingData = CONTAINING_RECORD(Entry, OUTGOING_STREAM_DATA, Link); + } + KeReleaseInStackQueuedSpinLock(&LockHandle); + } + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p, FlowInError %d, Status %!STATUS!", + FlowContext, FlowInError, Status); + return Status; +} + +_Requires_lock_not_held_(FlowContext->OobInfo.EditLock) +NTSTATUS +StreamOobReinjectData( + _In_ STREAM_FLOW_CONTEXT* FlowContext, + _In_ const PVOID Data, + _In_ size_t Length, + _In_ UINT32 StreamFlags + ) +/* + This function injects a section of the original indicated data back + to the data stream. + + An MDL is allocated to describe the data section. +*/ +{ + NTSTATUS Status; + + VOID* DataCopy = NULL; + MDL* mdl = NULL; + NET_BUFFER_LIST* NetBufferList = NULL; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, Length %Iu, sFlags 0x%x", FlowContext, Length, StreamFlags); + + NT_ASSERT(StreamFlags); + NT_ASSERT(!(StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && + !(StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); + NT_ASSERT(Length); + + do + { + DataCopy = ExAllocatePoolWithTag( + NonPagedPool, + Length, + STMEDIT_TAG_MDL_DATA + ); + + if (DataCopy == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate memory."); + break; + } + + RtlCopyMemory(DataCopy, Data, Length); + + mdl = IoAllocateMdl( + DataCopy, + (ULONG)Length, + FALSE, + FALSE, + NULL); + + if (mdl == NULL) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "Failed to allocate MDL for re-injection."); + break; + } + MmBuildMdlForNonPagedPool(mdl); + + Status = FwpsAllocateNetBufferAndNetBufferList( + Globals.NetBufferListPool, + 0, + 0, + mdl, + 0, + Length, + &NetBufferList + ); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "AllocateNetBufferAndNetBufferList failed with %!STATUS!\n", Status); + break; + } + + Status = StreamOobQueueUpOutgoingData( + FlowContext, + NetBufferList, + FALSE, + Length, + StreamFlags, + mdl + ); + + if (NT_SUCCESS(Status)) + { + DataCopy = NULL; + mdl = NULL; + NetBufferList = NULL; + } + } while (FALSE); + + if (!NT_SUCCESS(Status)) + { + if (NetBufferList != NULL) + { + FwpsFreeNetBufferList(NetBufferList); + } + + if (mdl != NULL) + { + IoFreeMdl(mdl); + } + + if (DataCopy != NULL) + { + ExFreePoolWithTag(DataCopy, STMEDIT_TAG_MDL_DATA ); + } + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p, %!STATUS!", FlowContext, Status); + + return Status; +} + +_Requires_lock_not_held_(FlowContext->OobInfo.EditLock) +NTSTATUS +StreamOobInjectReplacement( + _In_ STREAM_FLOW_CONTEXT* FlowContext, + _In_ UINT32 StreamFlags, + _In_ MDL* Mdl, // Optional + _In_ size_t Length + ) +/* + This function injects a section of replacement data (in place of data + removed from the stream) into the data stream. + + The MDL describing the replacement data is allocated during DriverEntry + and does not need to be freed during injection completion. +*/ +{ + NTSTATUS Status; + NET_BUFFER_LIST* NetBufferList = NULL; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, sFlags %#x, Length %Iu", FlowContext, StreamFlags, Length); + + Status = FwpsAllocateNetBufferAndNetBufferList( + Globals.NetBufferListPool, + 0, + 0, + Mdl, + 0, + Length, + &NetBufferList + ); + + if (NT_SUCCESS(Status)) + { + NT_ASSERT(!(StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && + !(StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); + + Status = StreamOobQueueUpOutgoingData( + FlowContext, + NetBufferList, + FALSE, + Length, + StreamFlags, + NULL + ); + + if (NT_SUCCESS(Status)) + { + NetBufferList = NULL; + } + } + + if (NetBufferList) + { + FwpsFreeNetBufferList(NetBufferList); + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", FlowContext, Status); + + return Status; +} + +FORCEINLINE +NTSTATUS +StreamOobCopyDataToFlatBuffer( + _Inout_ PTASK_ENTRY TaskEntry + ) +/* + This function copies the data described by NBL(s) into a flat buffer for easy searching. + + It reuses the FwpsCopyStreamDataToBuffer API (via StreamEditCopyDataForInspection) + by creating a FWPS_STREAM_DATA struct. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + FWPS_STREAM_DATA streamData; + + if (TaskEntry->DataLength > 0) + { + streamData.netBufferListChain = TaskEntry->NetBufferList; + streamData.dataLength = TaskEntry->DataLength; + streamData.flags = TaskEntry->StreamFlags; + + streamData.dataOffset.netBufferList = TaskEntry->NetBufferList; + streamData.dataOffset.netBuffer = NET_BUFFER_LIST_FIRST_NB(streamData.dataOffset.netBufferList); + streamData.dataOffset.mdl = NET_BUFFER_CURRENT_MDL(streamData.dataOffset.netBuffer); + streamData.dataOffset.mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(streamData.dataOffset.netBuffer); + + streamData.dataOffset.netBufferOffset = 0; + streamData.dataOffset.streamDataOffset = 0; + + if (!StreamEditCopyDataForInspection(TaskEntry->FlowCtx, &streamData, streamData.dataLength)) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + } + } + return Status; +} + +_Requires_lock_not_held_(TaskEntry->FlowCtx->OobInfo.EditLock) +NTSTATUS +StreamOobEditData( +_Inout_ PTASK_ENTRY TaskEntry +) +/* + This function processes Stream data in "Out of Band" processing, looking + for pattern matches, and replacing them. + + It first copies the stream data into a flat inspection buffer; + it then parses the buffer looking for the matching pattern. For + non-matching sections it re-injects the data back; for a match, it skips + over and injects a replacement pattern. + + If a match can not be determined due to lack of data, it injects the + non-matching section back and moves the potential (or partial) match + to the beginning of the (flattened) inspection buffer. + + If a FIN is presented by the NetBufferList, it flushes all processed stream + sections back and re-injects the FIN back at the end the stream. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + PSTREAM_FLOW_CONTEXT FlowContext = TaskEntry->FlowCtx; + UINT i; + BOOLEAN bStreamModified, bPartialMatch; + BYTE* dataStart; + + + size_t BytesToProcess = 0; // # of bytes to be processed in one loop! + size_t ProcessedBytes = 0; // # of bytes of original data actually processed/consumed + size_t BytesRemaining = TaskEntry->DataLength; // # of bytes of original data remaining to be processed. + + FWPS_STREAM_DATA StreamData; // Local StreamData struct for keeping track of stream data offset + PNET_BUFFER_LIST TheNbl; // The NBL to be processed next in the StreamData's netBufferListChain + + BOOLEAN bIsLastNbl; // True if last NBL of the chain is being processed + BOOLEAN bProcessingPartialTask = FALSE; // True if we are only processing a part of current task data + + NT_ASSERT(TaskEntry->NetBufferList != NULL); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, Task %p, NBL Chain @ %p, Length %Iu, sFlags %#x, Pended Data %Iu", + FlowContext, + TaskEntry, + TaskEntry->NetBufferList, + TaskEntry->DataLength, + TaskEntry->StreamFlags, + FlowContext->OobInfo.PendedDataLength); + + StreamData.dataLength = TaskEntry->DataLength; + StreamData.netBufferListChain = TheNbl = TaskEntry->NetBufferList; + StreamData.flags = TaskEntry->StreamFlags; + StreamData.dataOffset.streamDataOffset = 0; + + while (BytesRemaining) + { + bStreamModified = bPartialMatch = FALSE; + + StreamData.dataOffset.netBufferList = TheNbl; + + StreamData.dataOffset.netBuffer = NET_BUFFER_LIST_FIRST_NB(TheNbl); + StreamData.dataOffset.netBufferOffset = NET_BUFFER_CURRENT_MDL_OFFSET(StreamData.dataOffset.netBuffer); + + StreamData.dataOffset.mdl = NET_BUFFER_CURRENT_MDL(StreamData.dataOffset.netBuffer); + StreamData.dataOffset.mdlOffset = NET_BUFFER_CURRENT_MDL_OFFSET(StreamData.dataOffset.netBuffer); + + StreamData.dataOffset.streamDataOffset += BytesToProcess; + + // Presume we are going to process all data. + BytesToProcess = BytesRemaining; + bIsLastNbl = TRUE; + + // However, if we have more data than we can handle, lets try to consume it in chunks! + // + if (BytesRemaining + FlowContext->ScratchDataLength > Globals.BusyThreshold) + { + + // Process ONE NBL at a time... + // + // It is possible that the length of NBL being processed is more than the + // BusyThreshold limit (though, very unlikely for a realistic BusyThreshold). + // + // The code can be modified to handle such situations -- i.e. to process a + // partial NBL or to process multiple NBLs (or a combination thereof ) at a + // time by appropriately setting StreamData.dataOffset. + // + // This is left as an exercise for the user. + // + + BytesToProcess = NetBufferListLength(TheNbl); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "Partially processing task %p - Now Processing NBL %p with %Iu bytes", + TaskEntry, TheNbl, BytesToProcess); + + TheNbl = TheNbl->Next; + + if (TheNbl != NULL) + bIsLastNbl = FALSE; + + bProcessingPartialTask = TRUE; + + if (BytesToProcess == 0) + { + NT_ASSERT(TheNbl != NULL); + continue; + } + } + + + if (!StreamEditCopyDataForInspection(TaskEntry->FlowCtx, &StreamData, BytesToProcess)) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p, Task %p - CopyDataToFlatBuffer failed with status %#x", + FlowContext, TaskEntry, Status); + + goto Exit; + } + + + /* + Search for a pattern through the "flattened" buffer. + + If we find a full match, inject the (if any) data before the match into stream, + followed by the injection of replacement pattern. + + If we find a partial match (at the end of flat buffer), then inject the data + before the pattern, and move the partial pattern at the beginning of the ScratchBuffer. + Next time (if) we get more data, we can try for a full match. + */ + + NT_ASSERT(FlowContext->ScratchDataOffset == 0); + dataStart = (BYTE*)FlowContext->ScratchBuffer; + + ProcessedBytes = FlowContext->ScratchDataLength; + + for (i = 0; i < FlowContext->ScratchDataLength; ++i) + { + // If there is enough data on which to perform a search + // + if (i + Globals.StringToFindLength <= FlowContext->ScratchDataLength) + { + if (RtlCompareMemory( + dataStart + i, + Globals.StringToFind, + Globals.StringToFindLength + ) == Globals.StringToFindLength) + { + // If the match is not at the beginning of the data, + // inject back the data before the match + // + if (i != 0) + { + Status = StreamOobReinjectData(FlowContext, dataStart, i, TaskEntry->StreamFlags); + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + FlowContext->ScratchDataOffset += i; + FlowContext->ScratchDataLength -= i; + i = 0; + } + + // Now inject the replacement string in place of the match (Globals.StringToFind)! + // + Status = StreamOobInjectReplacement( + FlowContext, + TaskEntry->StreamFlags, + Globals.StringXMdl, + Globals.StringXLength + ); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + FlowContext->ScratchDataOffset += Globals.StringToFindLength; + FlowContext->ScratchDataLength -= Globals.StringToFindLength; + + bStreamModified = TRUE; + + // Still more data to be searched for the match + // + if (FlowContext->ScratchDataLength > 0) + { + dataStart = (BYTE*)FlowContext->ScratchBuffer + FlowContext->ScratchDataOffset; + --i; + + continue; + } + else { + FlowContext->ScratchDataOffset = 0; + } + } + } + else // If we do not have enough data to perform a search on + { + // If we do not expect more data to come in, get out... + // 1 == FlowContext->OobInfo.RefCount ==> This is the last + // (data-processing) Task being processed for the flow + // + if (bIsLastNbl && FlowContext->bNoMoreData && (0 == FlowContext->OobInfo.PendingTasks)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p -> giving up on partial match search - offset %lu, scratch length %Iu", + FlowContext, i, FlowContext->ScratchDataLength); + + break; + } + + // Look for a partial match! + // If we find a partial match, move the partially matching pattern + // to the beginning of ScratchBuffer... when more data comes in, + // we'll try a complete match again. + // + if (RtlCompareMemory(dataStart + i, Globals.StringToFind, FlowContext->ScratchDataLength - i) + == FlowContext->ScratchDataLength - i) + { + bPartialMatch = TRUE; // This is a partial find + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p -> partial match @ offset %lu, match length %Iu", + FlowContext, i, (FlowContext->ScratchDataLength - i)); + + if (i != 0) + { + // Inject any data before partial match back into the stream + // + Status = StreamOobReinjectData( + FlowContext, + dataStart, + i, + TaskEntry->StreamFlags + ); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + } + // Move the partially matching bytes to the beginning of scratch buffer + // + RtlMoveMemory((BYTE*)FlowContext->ScratchBuffer, dataStart + i, FlowContext->ScratchDataLength - i); + FlowContext->PartialSFlags = TaskEntry->StreamFlags; + + FlowContext->ScratchDataOffset = 0; + FlowContext->ScratchDataLength -= i; + + break; + } + } + } + + // + // At this point we should fall into one of the following : + // case 1: We found a partial match (irrespective of whether a full match was + // found before it or not). Data before the partial match has already + // been injected; we do not need to inject any more data. + // + // case 2: We found a full match (but no partial match). + // Data before the match has been taken care of; if there is any data + // left after the matching token, inject it. + // + // case 3: No full or partial match was found. + // subcase 1: There is data left in the ScratchBuffer from a previous + // partial match: + // inject "data left from previous match" + // . . . then the current NBL (subcase 2 below) + // + // subcase 2: There is no data left from previous partial match + // (i.e. all data is what was in the indicated NBL Chain) : + // inject the indicated NetBufferList Chain... + // + // Injecting (original/indicated) NBLs will save us from having to + // allocate memory and unnecessary processing to create new NBL. + // + + if (bPartialMatch) + { + // case 1. nothing to be done! + // + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p: Partial match, nothing to reinject, %Iu bytes left...", + FlowContext, FlowContext->ScratchDataLength); + } + else if (bStreamModified) + { + // case 2. We found a match. Reinject data after matching token + // + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p: Full match, reinjecting %Iu remaining bytes", + FlowContext, FlowContext->ScratchDataLength); + + if (FlowContext->ScratchDataLength > 0) + { + Status = StreamOobReinjectData( + FlowContext, + dataStart, + FlowContext->ScratchDataLength, + TaskEntry->StreamFlags + ); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + FlowContext->ScratchDataOffset = FlowContext->ScratchDataLength = 0; + } + } + else + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p: No match, reinjecting %Iu+%Iu bytes", + FlowContext, + (FlowContext->ScratchDataLength - BytesToProcess), + BytesToProcess); + + if (!bProcessingPartialTask) + { + NT_ASSERT(TaskEntry->DataLength == BytesToProcess); + + if (FlowContext->ScratchDataLength > BytesToProcess) + { + // Case 3/subcase 1: We have partial data left in the scratch buffer. + // + NT_ASSERT(FlowContext->ScratchBuffer == dataStart); + NT_ASSERT(FlowContext->ScratchDataOffset == 0); + + Status = StreamOobReinjectData( + FlowContext, + dataStart, + (FlowContext->ScratchDataLength - BytesToProcess), + FlowContext->PartialSFlags + ); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + FlowContext->ScratchDataLength = BytesToProcess; + } + + // case 3/subcase 2: Inject the rest of the (original) data. + // + if (TaskEntry->DataLength) + { + NT_ASSERT(TaskEntry->DataLength == FlowContext->ScratchDataLength); + + Status = StreamOobQueueUpOutgoingData( + FlowContext, + TaskEntry->NetBufferList, + TRUE, + TaskEntry->DataLength, + TaskEntry->StreamFlags, + NULL + ); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + TaskEntry->NetBufferList = NULL; + } + } + else + { + NT_ASSERT(FlowContext->ScratchBuffer == dataStart); + NT_ASSERT(FlowContext->ScratchDataOffset == 0); + + Status = StreamOobReinjectData( + FlowContext, + FlowContext->ScratchBuffer, + FlowContext->ScratchDataLength, + TaskEntry->StreamFlags + ); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + } + + FlowContext->ScratchDataOffset = FlowContext->ScratchDataLength = 0; + } + + ProcessedBytes -= FlowContext->ScratchDataLength; + + NT_ASSERT((signed)ProcessedBytes >= 0); + InterlockedAdd((LONG *)&FlowContext->OobInfo.PendedDataLength, -(signed)ProcessedBytes); + + + BytesRemaining -= BytesToProcess; + + NT_ASSERT(FlowContext->ScratchDataLength < Globals.StringToFindLength); + + } // while (BytesRemaining); + + // If we received a FIN and there are left overs in ScratchBuffer, + // let us inject it before injecting the FIN. + // + if ((TRUE == FlowContext->bFlowTerminating) && + (0 == TaskEntry->DataLength)) + { + NT_ASSERT(FlowContext->OobInfo.PendedDataLength == FlowContext->ScratchDataLength); + + if (FlowContext->OobInfo.PendedDataLength) + { + NT_ASSERT(FlowContext->ScratchDataOffset == 0); + + Status = StreamOobReinjectData(FlowContext, + FlowContext->ScratchBuffer, + FlowContext->ScratchDataLength, + FlowContext->PartialSFlags); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + // InterlockedExchangeSubtract can be used as well! + // + InterlockedAdd((LONG *)&FlowContext->OobInfo.PendedDataLength, -(signed)FlowContext->ScratchDataLength); + FlowContext->ScratchDataOffset = FlowContext->ScratchDataLength = 0; + } + } + + + // + // Its a good time to reinject the processed data back into the stream + // + Status = StreamOobFlushOutgoingData(FlowContext); + + if (!NT_SUCCESS(Status)) + { + goto Exit; + } + + // FIN/RST :: inject FIN after flushing the processed stream data + // + if ( (TRUE == FlowContext->bFlowTerminating) && + (0 == TaskEntry->DataLength) ) + { + NT_ASSERT(TRUE == FlowContext->bNoMoreData); + NT_ASSERT(FlowContext->OobInfo.PendedDataLength == 0); + + NT_ASSERT( TaskEntry->StreamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT || + TaskEntry->StreamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Now injecting FIN for FlowCtx %p", FlowContext); + Status = FwpsStreamInjectAsync( + Globals.InjectionHandle, + NULL, + 0, + FlowContext->FlowHandle, + FlowContext->CalloutId, + FlowContext->LayerId, + (DWORD)TaskEntry->StreamFlags, + TaskEntry->NetBufferList, + 0, + StreamOobCloneInjectCompletionFn, + NULL + ); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FIN injection for FlowCtx %p failed with %!STATUS!", FlowContext, Status); + goto Exit; + } + + TaskEntry->NetBufferList = NULL; + } + +Exit: + + if (TaskEntry->NetBufferList != NULL) + { + FwpsDiscardClonedStreamData(TaskEntry->NetBufferList, 0, FALSE); + TaskEntry->NetBufferList = NULL; + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", FlowContext, Status); + return Status; +} + +VOID +StreamEditOobProcessTask( + TASK_ENTRY *TaskEntry + ) +/* + This function processes a Task Entry data and resume + a paused stream if conditions are right. +*/ +{ + KLOCK_QUEUE_HANDLE LockHandle; + NTSTATUS Status = STATUS_SUCCESS; + STREAM_FLOW_CONTEXT* FlowCtx = TaskEntry->FlowCtx; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: Processing Task %p for FlowCtx %p", TaskEntry, FlowCtx); + + do + { + KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle); + + if (OOB_EDIT_ERROR == FlowCtx->OobInfo.EditState) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "TaskProc: Flow in Error/Shutdown (%d) state", FlowCtx->OobInfo.EditState); + KeReleaseInStackQueuedSpinLock(&LockHandle); + break; + } + + KeReleaseInStackQueuedSpinLock(&LockHandle); + + Status = StreamOobEditData(TaskEntry); + + if (!NT_SUCCESS(Status)) + { + KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle); + FlowCtx->OobInfo.EditState = OOB_EDIT_ERROR; + KeReleaseInStackQueuedSpinLock(&LockHandle); + break; + } + + // Wait till we have some room to process more data + // + if (FlowCtx->OobInfo.PendedDataLength <= (Globals.BusyThreshold >> 2)) + { + BOOLEAN bStreamPaused; + + KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle); + bStreamPaused = (OOB_EDIT_BUSY == FlowCtx->OobInfo.EditState); + + if (FlowCtx->OobInfo.PendedDataLength < Globals.StringToFindLength) + FlowCtx->OobInfo.EditState = OOB_EDIT_IDLE; + else + if (FlowCtx->OobInfo.EditState == OOB_EDIT_BUSY) + FlowCtx->OobInfo.EditState = OOB_EDIT_PROCESSING; + + KeReleaseInStackQueuedSpinLock(&LockHandle); + + if (bStreamPaused) + { + NTSTATUS ContinueStatus; + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "TaskProc: Calling FwpsStreamContinue. PendedLength %Iu, new State %!OobState!", + FlowCtx->OobInfo.PendedDataLength, + FlowCtx->OobInfo.EditState); + + ContinueStatus = FwpsStreamContinue( + FlowCtx->FlowHandle, + FlowCtx->CalloutId, + FlowCtx->LayerId, + FlowCtx->OobInfo.StreamFlags + ); + + if (!NT_SUCCESS(ContinueStatus)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "TaskProc: FwpsStreamContinue() returns with %!STATUS!", ContinueStatus); + + KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle); + FlowCtx->OobInfo.EditState = OOB_EDIT_ERROR; + KeReleaseInStackQueuedSpinLock(&LockHandle); + + break; + } + } + } + } while (FALSE); + + // DeRef the FlowContext for completed task + StmEditDeReferenceFlow(TaskEntry->FlowCtx, _MODULE_ID, __LINE__); + + KeAcquireInStackQueuedSpinLock(&FlowCtx->OobInfo.EditLock, &LockHandle); + if (OOB_EDIT_ERROR == FlowCtx->OobInfo.EditState) + { + KeReleaseInStackQueuedSpinLock(&LockHandle); + +#if (NTDDI_VERSION >= NTDDI_WIN8) + (VOID)StreamOobFlushOutgoingData(FlowCtx); + Status = FwpsFlowAbort(FlowCtx->FlowHandle); + + if (!NT_SUCCESS(Status)) + { + (VOID) FwpsFlowRemoveContext(FlowCtx->FlowHandle, FlowCtx->LayerId, FlowCtx->CalloutId); + } +#else + (VOID) StreamEditRemoveFlowCtx(FlowCtx); +#endif + + } + else + { + KeReleaseInStackQueuedSpinLock(&LockHandle); + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: Task %p for FlowCtx %p (Ref %lu, Pending %lu), State %!OobState!", + TaskEntry, FlowCtx, FlowCtx->RefCount, FlowCtx->OobInfo.PendingTasks, FlowCtx->OobInfo.EditState); +} + + +__drv_functionClass(IO_WORKITEM_ROUTINE) +__drv_requiresIRQL(PASSIVE_LEVEL) +__drv_sameIRQL +VOID +StreamEditOobPoolWorker( + _In_ PDEVICE_OBJECT DevObj, + _In_opt_ PVOID Context + ) +{ + PLW_ENTRY LinkEntry = (PLW_ENTRY)Context; + PTASK_ENTRY TaskEntry; + ULONG Count; + + UNREFERENCED_PARAMETER(DevObj); + + NT_ASSERT(LinkEntry != NULL); + + while (LinkEntry) + { + TaskEntry = CONTAINING_RECORD(LinkEntry, TASK_ENTRY, LwQLink); + NT_ASSERT(TaskEntry); + LinkEntry = LinkEntry->Next; + + // + // Process the task. + // + Count = InterlockedDecrement(& (LONG)TaskEntry->FlowCtx->OobInfo.PendingTasks); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT, + "FlowCtx %p, ProcessingTask %p: PendingTasks @-- = %lu", TaskEntry->FlowCtx, TaskEntry, Count); + + StreamEditOobProcessTask(TaskEntry); + + // + // Free the task Memory. + // + ExFreeToLookasideListEx(&Globals.LookasideList, TaskEntry); + } + + return; +} + +VOID +OobEditClassify( + _In_ const FWPS_INCOMING_VALUES* InFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues, + _Inout_ VOID* LayerData, + _In_ const FWPS_FILTER* Filter, + _In_ UINT64 InFlowContext, + _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut + ) +/* + This is the ClassifyFn function registered by the OOB stream edit callout. + + An OOB stream modification callout blocks all indicated data after cloning + them for processing by a kernel mode worker thread (or marshalling the data + to user mode for inspection); the resultant/edited data will then be put + back to the stream via the stream injection API. + +*/ +{ + FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket; + FWPS_STREAM_DATA* streamData; + + PTASK_ENTRY TaskEntry = NULL; + NTSTATUS Status = STATUS_SUCCESS; + KLOCK_QUEUE_HANDLE LockHandle; + STREAM_FLOW_CONTEXT* FlowContext = (STREAM_FLOW_CONTEXT *)(ULONG_PTR)InFlowContext; + + ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)LayerData; + NT_ASSERT(ioPacket != NULL); + + streamData = ioPacket->streamData; + NT_ASSERT(streamData != NULL); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, sFlags %#x, Length %Iu, LayerId %hu, CalloutId %u, FlowId %I64u, cFlags %#x", + FlowContext, + streamData->flags, + streamData->dataLength, + InFixedValues->layerId, + Filter->action.calloutId, + InMetaValues->flowHandle, + ClassifyOut->flags); + + // Set default classify action to Block, and stream action to None! + // + ClassifyOut->actionType = FWP_ACTION_BLOCK; + ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; + + // Setting countBytesEnforced to 0 bytes means that action applies + // to entire stream... We'll consume or reject the entire stream data segment + // + ioPacket->countBytesEnforced = 0; + + // + // If the indicated data is insufficient for the callout to make an inspection + // decision, it can request for more data. To do so : + // + // 1. set IoPacket->streamAction to FWPS_STREAM_ACTION_NEED_MORE_DATA + // 2. set the countBytesRequired member to the minimal amount WFP should + // accumulate before the data is indicated again. + // 3. return FWP_ACTION_NONE from the classifyFn function + // + + if ((streamData->dataLength < Globals.StringToFindLength) && + !(ClassifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA; + ioPacket->countBytesRequired = (UINT32)Globals.StringToFindLength; + + ClassifyOut->actionType = FWP_ACTION_NONE; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p - Need more data.", FlowContext); + return; + } + + if (FlowContext->bFlowTerminating) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "Received data classified after FIN for FlowCtx %p.", FlowContext); + return; + } + + KeAcquireInStackQueuedSpinLock(&FlowContext->OobInfo.EditLock, &LockHandle ); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p: PendedDataLength %Iu, StreamDataLength %Iu, State %!OobState!, sFlags %#x", + FlowContext, + FlowContext->OobInfo.PendedDataLength, + streamData->dataLength, + FlowContext->OobInfo.EditState, + streamData->flags); + + switch (FlowContext->OobInfo.EditState) + { + case OOB_EDIT_PROCESSING: + + // If we are to pause a flow, make sure it is an inbound flow. Currently, + // WFP Stream layer only supports pausing and resuming inbound flows. + // + // Let's not pause the stream if we receive a DISCONNECT (FIN) packet. + // For FIN packets, streamData->dataLength == 0, and streamData->flags has + // one of the following flags set: + // FWPS_STREAM_FLAG_SEND_DISCONNECT + // FWPS_STREAM_FLAG_RECEIVE_DISCONNECT + // + if (( streamData->flags & FWPS_STREAM_FLAG_RECEIVE) && + streamData->dataLength && + FALSE == FlowContext->bNoMoreData ) + { + // While the stream is paused/deferred, it keeps storing incoming data + // for the callouts. If resuming a stream causes to give us more data + // than we can handle, then pause the stream again. + // + if (((FlowContext->OobInfo.PendedDataLength + streamData->dataLength) > Globals.BusyThreshold)) + { + ioPacket->streamAction = FWPS_STREAM_ACTION_DEFER; + ClassifyOut->actionType = FWP_ACTION_NONE; + + FlowContext->OobInfo.StreamFlags = streamData->flags; + FlowContext->OobInfo.EditState = OOB_EDIT_BUSY; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, Flow is pausing", FlowContext); + break; + } + } + + // If this is an outbound flow OR inbound flow that has not yet hit + // threshold limit, keep queueing the data for processing. + // + // Fall through to IDLE State. + // + + case OOB_EDIT_IDLE: + +#pragma prefast(push) +#pragma prefast(disable:6014, "Warning Leaking memory 'TaskEntry' : TaskEntry is freed in StreamEditOobPoolWorker") + TaskEntry = (PTASK_ENTRY) ExAllocateFromLookasideListEx(&Globals.LookasideList); +#pragma prefast(pop) + + if (TaskEntry) + { + TaskEntry->FlowCtx = FlowContext; + +#pragma prefast(push) +#pragma prefast(disable: 26110, "The lock is being held via KeAcquireInStackQueuedSpinLock") + Status = StreamOobQueueUpIncomingData(TaskEntry, streamData, ClassifyOut->flags); +#pragma prefast(pop) + + if (NT_SUCCESS(Status)) + { + // If the previous state was _PROCESSING, it stays the same... + FlowContext->OobInfo.EditState = OOB_EDIT_PROCESSING; + + // Block original data + // + break; + } + else + { + ExFreeToLookasideListEx(&Globals.LookasideList, TaskEntry); + TaskEntry = NULL; + } + } + + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, + "OC: FlowCtx %p ERROR. Task %p, %!STATUS!", FlowContext, TaskEntry, Status); + + FlowContext->OobInfo.EditState = OOB_EDIT_ERROR; + + // + // Fall through for ERROR processing + // + + case OOB_EDIT_ERROR: + // + // An out-of-band inspection module must not arbitrarily inject a FIN + // (which indicates no more data from the sender) into an (outgoing) data + // stream. If the module must drop the connection, its classifyFn callout + // function must set the FWPS_STREAM_CALLOUT_IO_PACKET->streamAction to + // FWPS_STREAM_ACTION_DROP_CONNECTION. + // + + ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; + break; + + default: + NT_ASSERT(FALSE); + break; + }; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p, cAction %#x, sAction %#x, #Enforced %Iu, New State %!OobState!", + FlowContext, ClassifyOut->actionType, ioPacket->streamAction, ioPacket->countBytesEnforced, FlowContext->OobInfo.EditState); + + KeReleaseInStackQueuedSpinLock(&LockHandle); + return; +} diff --git a/network/trans/stmedit/sys/StreamEdit.c b/network/trans/stmedit/sys/StreamEdit.c new file mode 100644 index 00000000..9bad88d1 --- /dev/null +++ b/network/trans/stmedit/sys/StreamEdit.c @@ -0,0 +1,1633 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + Stream Edit Callout Driver Sample. + + This sample demonstrates finding and replacing a string pattern from a + live TCP stream via the WFP stream API. + + The driver demonstrates the two modes of stream editing/inspection -- + + o Inline Editing where all modification is carried out within the + WFP ClassifyFn callout function. + + o Out-of-band (OOB) Editing where all modification is done by a + worker thread. (this is the default) + + The mode setting, along with other inspection parameters are configurable + via the following registry values + + HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\StmEdit\Parameters + + o StringToFind (REG_SZ, default = "rainy") + o StringX (REG_SZ, default = "cloudy") + o StringToReplace (REG_SZ, default = "sunny") + + o InspectionLocalPort (REG_DWORD, default = 8888) + + o InspectionRemotePort (REG_DWORD, default = 0) + Note: for this sample, a local or remote port is mandatory. Both cannot be zero. + + o InspectioDirection (REG_DWORD, default = 2) + possible values : 2 (inbound + outbound), 0 (FWP_DIRECTION_OUTBOUND), 1 (FWP_DIRECTION_INBOUND) + + o MultipleCallouts (REG_DWORD, default = true/1) + controls registration of multiple callouts. Set 0 for false, other for TRUE + + o BusyThreshold (REG_DWORD, default = 16KB) + BusyThreshold value is in KBs (e.g. a value of 5 means 5KB) + + The sample is IP version agnostic. It is capable of performing inspections + on both IPv4 and IPv6 data streams + + Before experimenting with the sample, please be sure to add an exception for + the InspectionPort configured to the firewall. + +Environment: + Kernel mode + +--*/ + +#include "Trace.h" +#include "StreamEdit.h" +#include "StreamEdit.tmh" + +STMEDIT_GLOBALS Globals; + +DRIVER_INITIALIZE DriverEntry; +EVT_WDF_DRIVER_UNLOAD StreamEditEvtDriverUnload; + +#if defined _MODULE_ID +#undef _MODULE_ID +#endif +#define _MODULE_ID 'S' + +VOID +StmEditReferenceFlow( + _Inout_ PSTREAM_FLOW_CONTEXT FlowContext, + _In_ char Module, + _In_ UINT Line + ) +{ + LONG Count = InterlockedIncrement((LONG *)&FlowContext->RefCount); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT, "FlowCtx %p RefCount++ @ %c/%lu = %lu", FlowContext, Module, Line, Count); +} + +VOID +StmEditDeReferenceFlow( + _Inout_ PSTREAM_FLOW_CONTEXT FlowContext, + _In_ char Module, + _In_ UINT Line + ) +{ + LONG Count; + KLOCK_QUEUE_HANDLE LockHandle; + + NT_ASSERT(FlowContext->RefCount > 0); + + Count = InterlockedDecrement((LONG *)&FlowContext->RefCount); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_REFCOUNT, "FlowCtx %p RefCount-- @ %c/%lu = %lu", FlowContext, Module, Line, Count); + + if (Count == 0) + { + NT_ASSERT( ! FlowContext->bFlowActive); + + // Remove the context from global context list + // + KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &LockHandle); + + if (!FlowContext->bEntryRemoved) + { + RemoveEntryList(&FlowContext->Link); + FlowContext->bEntryRemoved = TRUE; + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, -- Link removed", FlowContext); + } + Count = --Globals.FlowContextCount; + + if (Globals.FlowContextCount == 0) + { + NT_ASSERT(IsListEmpty(&Globals.FlowContextList)); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Setting ZeroFlowCountEvent."); + KeSetEvent(&Globals.ZeroFlowCountEvent, IO_NO_INCREMENT, FALSE); + } + KeReleaseInStackQueuedSpinLock(&LockHandle); + + if (!FlowContext->bEditInline) + { + NT_ASSERT(IsListEmpty(&FlowContext->OobInfo.OutgoingDataQueue)); + } + + if (FlowContext->ScratchBuffer) + { + ExFreePoolWithTag(FlowContext->ScratchBuffer, STMEDIT_TAG_FLAT_BUFFER); + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p is being freed., %lu remain @--", FlowContext, Count); + ExFreePoolWithTag(FlowContext, STMEDIT_TAG_FLOWCTX); + } +} + + +NTSTATUS +StreamEditNotifyFunction( + _In_ FWPS_CALLOUT_NOTIFY_TYPE NotifyType, + _In_ const GUID* FilterKey, + _In_ const FWPS_FILTER* Filter + ) +{ +/* + Notify Function. +*/ + UNREFERENCED_PARAMETER(FilterKey); + +#if 0 + UNREFERENCED_PARAMETER(notifyType); + UNREFERENCED_PARAMETER(filter); +#else + NT_ASSERT(Filter != NULL); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "-><- %!FUNC! invoked with %I64u for Filter ID %I64u", + NotifyType, Filter->filterId); + +#endif + return STATUS_SUCCESS; +} + +void +NTAPI +StreamEditInjectCompletionFn( + _In_ VOID* Context, + _Inout_ NET_BUFFER_LIST* NetBufferList, + _In_ BOOLEAN DispatchLevel + ) +/* + Injection completion function for injecting an NBL created using + FwpsAllocateNetBufferAndNetBufferList. +*/ +{ + MDL* mdl = (MDL*)Context; + + UNREFERENCED_PARAMETER(DispatchLevel); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "-><- %!FUNC!: NBL %p (%!STATUS!), MDL %p", NetBufferList, NetBufferList->Status, mdl); + + // Supress warning 28922: Redundant test against NULL. Pointer is already guaranteed to be non-NULL. + // Rationale : mdl is not guaranteed to be non-NULL here. +#pragma prefast(push) +#pragma prefast(disable:28922) + + if (mdl != NULL) + { + // + // The MDL mapped over a pool alloc which we need to free here. + // + + ExFreePoolWithTag(mdl->MappedSystemVa, STMEDIT_TAG_MDL_DATA); + + IoFreeMdl(mdl); + } +#pragma prefast(pop) + + NT_ASSERT(NetBufferList != NULL); + FwpsFreeNetBufferList(NetBufferList); +} + +NTSTATUS +StreamEditRemoveFlowCtx( + _In_ PSTREAM_FLOW_CONTEXT Context + ) +/* + Function to disassociate a previously associated context from a data flow. + This will cause flowDelete function to be invoked (either synchronously or asynchronously). + + Remarks @ http://msdn.microsoft.com/en-us/library/windows/hardware/ff551169.aspx + + If the FwpsFlowRemoveContext0 function returns STATUS_SUCCESS, FwpsFlowRemoveContext0 + calls the flowDeleteFn callout function synchronously.If FwpsFlowRemoveContext0 returns + STATUS_PENDING, FwpsFlowRemoveContext0 calls flowDeleteFn asynchronously because an + active callout classification is in progress. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + // + // Possible synchronization problem for accessing bFlowActive... + // while we are flushing the data, FlowDeleteFn can get invoked. + // + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p", Context); + + NT_ASSERT(Context); + + if (Context->bEditInline) + { + (VOID) InlineEditFlushData(Context, 0, Context->PartialSFlags); + } + else { + (VOID) StreamOobFlushOutgoingData(Context); + } + + if (Context->bFlowActive) + { + + Status = FwpsFlowRemoveContext( + Context->FlowHandle, + Context->LayerId, + Context->CalloutId); + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p, %!STATUS!", Context, Status); + return Status; +} + +VOID +StreamEditSignalShutdown( +) +/* + This function attempts to Disassociate all active FlowContexts so that + a shutdown can be performed. +*/ +{ + KLOCK_QUEUE_HANDLE LockHandle; + PSTREAM_FLOW_CONTEXT FlowContext; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!"); + + KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &LockHandle); + while ( ! IsListEmpty(&Globals.FlowContextList) ) + { + PLIST_ENTRY Entry = RemoveHeadList(&Globals.FlowContextList); + + FlowContext = CONTAINING_RECORD(Entry, STREAM_FLOW_CONTEXT, Link); + FlowContext->bEntryRemoved = TRUE; + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, -- Link removed at shutdown", FlowContext); + + if (FlowContext->bFlowActive) + { + KeReleaseInStackQueuedSpinLock(&LockHandle); + (VOID)StreamEditRemoveFlowCtx(FlowContext); + KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &LockHandle); + } + } + KeReleaseInStackQueuedSpinLock(&LockHandle); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!"); +} + +VOID +StreamEditFlowDeleteFunction( + _In_ UINT16 LayerId, + _In_ UINT32 CalloutId, + _In_ UINT64 Context + ) +/* + This is the flowDeleteFn function. This callback is invoked when a flow is + terminated or due to call to FwpsFlowRemoveContext0. + + We removes the FlowContext from the global FlowcCntextList and releases resources. + + IRQL <= DISPATCH_LEVEL +*/ +{ + PSTREAM_FLOW_CONTEXT FlowCtx = (PSTREAM_FLOW_CONTEXT)(ULONG_PTR)Context; + + NT_ASSERT(NULL != FlowCtx); + NT_ASSERT(TRUE == FlowCtx->bFlowActive); + + InterlockedExchange8(&FlowCtx->bFlowActive, FALSE); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: FlowCtx %p, LayerId %hu, CalloutId %u, FlowId %I64u (RefCt = %lu)", + FlowCtx, LayerId, CalloutId, FlowCtx->FlowHandle, FlowCtx->RefCount ); + + // Deref the reference taken in Flow-established when the FlowCtx was allocated + // + StmEditDeReferenceFlow(FlowCtx, _MODULE_ID, __LINE__); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: FlowCtx %p", FlowCtx); +} + + +VOID +NTAPI +StreamEditCommonStreamClassify( + _In_ const FWPS_INCOMING_VALUES* InFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues, + _In_ PVOID LayerData, +#if(NTDDI_VERSION >= NTDDI_WIN7) + _In_ const VOID* ClassifyContext, +#endif + _In_ const FWPS_FILTER* Filter, + _In_ UINT64 InFlowContext, + _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut + ) +/* + Common classifyFn for both Inline and Out-of-band Stream layer callouts. + Invokes corresponding classify-function based on FlowContext->bEditInline flag. +*/ +{ + PSTREAM_FLOW_CONTEXT FlowContext = (PSTREAM_FLOW_CONTEXT)(ULONG_PTR)InFlowContext; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(ClassifyContext); +#endif + + NT_ASSERT(FlowContext); + + // Reference the flow to keep around while we are in classifyFn + // + StmEditReferenceFlow(FlowContext, _MODULE_ID, __LINE__); + + if (FlowContext->bEditInline) + { + InlineEditClassify( + InFixedValues, + InMetaValues, + LayerData, + Filter, + InFlowContext, + ClassifyOut + ); + } + else + { + OobEditClassify ( + InFixedValues, + InMetaValues, + LayerData, + Filter, + InFlowContext, + ClassifyOut + ); + } + + StmEditDeReferenceFlow(FlowContext, _MODULE_ID, __LINE__); +} + +VOID +StreamEditFlowEstablishedClassify( + _In_ const FWPS_INCOMING_VALUES* InFixedValues, + _In_ const FWPS_INCOMING_METADATA_VALUES* InMetaValues, + _In_ PVOID Packet, +#if(NTDDI_VERSION >= NTDDI_WIN7) + _In_ const void* ClassifyContext, +#endif + _In_ const FWPS_FILTER* Filter, + _In_ UINT64 InFlowContext, + _Inout_ FWPS_CLASSIFY_OUT* ClassifyOut +) +/* + Flow-established call out for IPV4 and IPV6 traffic. + Allocates and sets up a flow-context, and associate it with the flow. +*/ +{ + NTSTATUS Status; + PSTREAM_FLOW_CONTEXT StreamFlowContext; + UINT32 StreamCalloutId; + UINT16 StreamLayerId; + KLOCK_QUEUE_HANDLE lockHandle; + USHORT ipProtIndex; + + int CalloutSet = 0; + +#if(NTDDI_VERSION >= NTDDI_WIN7) + UNREFERENCED_PARAMETER(ClassifyContext); +#endif + UNREFERENCED_PARAMETER(InFlowContext); + UNREFERENCED_PARAMETER(Packet); + + + if ((Filter->action.calloutId == Globals.FlowEstablishedV4Callout1) || + (Filter->action.calloutId == Globals.FlowEstablishedV6Callout1)) + { + CalloutSet = 1; + } + else + if ((Filter->action.calloutId == Globals.FlowEstablishedV4Callout2) || + (Filter->action.calloutId == Globals.FlowEstablishedV6Callout2)) + { + CalloutSet = 2; + NT_ASSERT(TRUE == Globals.MultipleCallouts); + } + else + { + NT_ASSERT(FALSE); + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!%d: LayerId %hu, CalloutId %u, FlowId %I64u", + CalloutSet, InFixedValues->layerId, Filter->action.calloutId, InMetaValues->flowHandle); + + ClassifyOut->actionType = FWP_ACTION_CONTINUE; + + // Lets not entertain any new flows if the driver is unloading! + // + if (Globals.DriverUnloading) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"<-- %!FUNC!: -- Driver unloading, flow not being associated with"); + return; + } + + // + // Setup the flow context for IPV4 Flows + // + if (FWPS_LAYER_ALE_FLOW_ESTABLISHED_V4 == InFixedValues->layerId) + { + ipProtIndex = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V4_IP_PROTOCOL; + StreamLayerId = FWPS_LAYER_STREAM_V4; + + StreamCalloutId = CalloutSet == 1 ? Globals.StreamLayerV4Callout1 : Globals.StreamLayerV4Callout2; + } + // + // Setup the flow context for IPV6 Flows + // + else if (FWPS_LAYER_ALE_FLOW_ESTABLISHED_V6 == InFixedValues->layerId) + { + ipProtIndex = FWPS_FIELD_ALE_FLOW_ESTABLISHED_V6_IP_PROTOCOL; + StreamLayerId = FWPS_LAYER_STREAM_V6; + + StreamCalloutId = CalloutSet == 1 ? Globals.StreamLayerV6Callout1 : Globals.StreamLayerV6Callout2; + } + else + { + // We should not be here. + // + NT_ASSERT(FALSE); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!: -- Invalid layer."); + return; + } + + // + // Creates a flow context and associate it with the current flow + // FlowContext gets deleted via flowDeleteFn + // + + do + { + StreamFlowContext = ExAllocatePoolWithTag(NonPagedPoolNx, + sizeof(STREAM_FLOW_CONTEXT), + STMEDIT_TAG_FLOWCTX); + + if (StreamFlowContext == NULL) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Unable to allocate flow context"); + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // Initialize the flow-context + // + RtlZeroMemory(StreamFlowContext, sizeof(STREAM_FLOW_CONTEXT)); + + StreamFlowContext->IpProto = InFixedValues->incomingValue[ipProtIndex].value.uint16; + StreamFlowContext->bFlowActive = TRUE; + + StreamFlowContext->FlowHandle = InMetaValues->flowHandle; + StreamFlowContext->LayerId = StreamLayerId; + StreamFlowContext->CalloutId = StreamCalloutId; + + // Reference to take ownership! + StmEditReferenceFlow(StreamFlowContext, _MODULE_ID, __LINE__); + + // Callout Set #1 is for Out of Band editing + // + if (CalloutSet == 1) + { + // Initialize OOB editing specific flow context structure fields + // this includes, creating a worker thread to handle + + KeInitializeSpinLock(&StreamFlowContext->OobInfo.EditLock); + InitializeListHead(&StreamFlowContext->OobInfo.OutgoingDataQueue); + + StreamFlowContext->OobInfo.EditState = OOB_EDIT_IDLE; + StreamFlowContext->OobInfo.QueueNumber = InterlockedIncrement((LONG *)&Globals.QueueIndex) % NUM_WORKITEM_QUEUES; + + } + // Callout Set #2 is for InLine editing + // + else + { + // Initialize inline editing specific flow context structure areas + // + StreamFlowContext->InlineEditState = INLINE_EDIT_IDLE; + StreamFlowContext->bEditInline = TRUE; + + StreamFlowContext->CurrentProcessor = INVALID_PROC_NUMBER; + } + + // Add the newly created context on global context list + KeAcquireInStackQueuedSpinLock(&Globals.FlowContextListLock, &lockHandle); + InsertTailList(&Globals.FlowContextList, &StreamFlowContext->Link); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p, ++ Link inserted into global list", StreamFlowContext); + + ++Globals.FlowContextCount; + if (Globals.FlowContextCount == 1) + { + // Reset the shut-down event in case it was set due to no active flows + // + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Clearing ZeroFlowCountEvent."); + KeClearEvent(&Globals.ZeroFlowCountEvent); + } + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FlowCtx %p is allocated., Total %lu @++", StreamFlowContext, Globals.FlowContextCount); + + KeReleaseInStackQueuedSpinLock(&lockHandle); + + Status = FwpsFlowAssociateContext( + StreamFlowContext->FlowHandle, + StreamLayerId, + StreamCalloutId, + (UINT64)StreamFlowContext); + + // + // If not able to associate a flow context, free the memory and return. + // + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "FlowContext association to FlowId %I64u failed with %!STATUS!", + InMetaValues->flowHandle, Status); + break; + } + + } while (FALSE); + + if (!NT_SUCCESS(Status) && StreamFlowContext) + { + StmEditDeReferenceFlow(StreamFlowContext, _MODULE_ID, __LINE__); + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!%d: FlowCtx %p, cOut->Action %#x, %!STATUS!", + CalloutSet, StreamFlowContext, ClassifyOut->actionType, Status); + return; +} + +NTSTATUS +StreamEditRegisterFlowEstablishedCallouts( + _In_ PVOID DeviceObject, + _In_ const GUID* LayerKey, + _In_ const GUID* CalloutKey, + _In_ FWPM_DISPLAY_DATA* DisplayData, + _Out_ UINT32* CalloutId, + _In_ int CalloutNum + ) +/* + This function registers callouts and filters that intercept TCP + traffic at WFP FWPM_LAYER_STREAM_V4 or FWPM_LAYER_STREAM_V6 layer. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + USHORT condIndex = 0; + BOOLEAN calloutRegistered = FALSE; + + FWPS_CALLOUT sCallout = { 0 }; + FWPM_CALLOUT mCallout = { 0 }; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!%d", CalloutNum); + + sCallout.calloutKey = *CalloutKey; // STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4 / V6; + sCallout.notifyFn = StreamEditNotifyFunction; + + sCallout.classifyFn = StreamEditFlowEstablishedClassify; + + Status = FwpsCalloutRegister(DeviceObject, &sCallout, CalloutId); + + if (NT_SUCCESS(Status)) + { + calloutRegistered = TRUE; + + mCallout.calloutKey = *CalloutKey; + mCallout.displayData = *DisplayData; + mCallout.applicableLayer = *LayerKey; // FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 / V6 + + Status = FwpmCalloutAdd(Globals.EngineHandle, &mCallout, NULL, NULL); + + if (NT_SUCCESS(Status)) + { + FWPM_FILTER filter = { 0 }; + FWPM_FILTER_CONDITION filterConditions[4] = { 0 }; + + // Add Filters for StreamEditFlowEstablishedClassify + // + + filter.layerKey = *LayerKey; + filter.displayData.name = L"Stream Edit Sample Filter"; + filter.displayData.description = L"Filter that finds and replaces a token from a TCP stream (@ Flow Established)"; + + filter.action.type = FWP_ACTION_CALLOUT_INSPECTION; // FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = *CalloutKey; + filter.filterCondition = filterConditions; + + // In this sample, we edit TCP streams only + // + filterConditions[condIndex].fieldKey = FWPM_CONDITION_IP_PROTOCOL; + filterConditions[condIndex].matchType = FWP_MATCH_EQUAL; + filterConditions[condIndex].conditionValue.type = FWP_UINT8; + filterConditions[condIndex].conditionValue.uint8 = IPPROTO_TCP; + filter.numFilterConditions++; + condIndex++; + + // Filter according to the direction of the flow we are interested in + // + // @ http://msdn.microsoft.com/en-us/library/windows/desktop/aa364005.aspx + // + // For stream layers (FWPM_LAYER_STREAM_*) and flow established layers + // ( FWPM_LAYER_ALE_FLOW_ESTABLISHED_* ), the value will be the same as + // direction of the connection. + // + // For example, when a local application initiates the connection, an + // inbound packet has FWPM_CONDITION_DIRECTION set to FWP_DIRECTION_OUTBOUND. + // + + if (Globals.InspectionDirection != FWP_DIRECTION_MAX) + { + filterConditions[condIndex].fieldKey = FWPM_CONDITION_DIRECTION; + filterConditions[condIndex].matchType = FWP_MATCH_EQUAL; + filterConditions[condIndex].conditionValue.type = FWP_UINT32; + filterConditions[condIndex].conditionValue.uint32 = Globals.InspectionDirection; + filter.numFilterConditions++; + condIndex++; + } + + // Make sure that either the remote or the local port is specified... + // i.e. both ports are not zero + // + + if (Globals.InspectionLocalPort > 0) + { + filterConditions[condIndex].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT; + filterConditions[condIndex].matchType = FWP_MATCH_EQUAL; + filterConditions[condIndex].conditionValue.type = FWP_UINT16; + filterConditions[condIndex].conditionValue.uint16 = Globals.InspectionLocalPort; + filter.numFilterConditions++; + condIndex++; + } + + if (Globals.InspectionRemotePort > 0) + { + filterConditions[condIndex].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT; + filterConditions[condIndex].matchType = FWP_MATCH_EQUAL; + filterConditions[condIndex].conditionValue.type = FWP_UINT16; + filterConditions[condIndex].conditionValue.uint16 = Globals.InspectionRemotePort; + filter.numFilterConditions++; + } + + filter.subLayerKey = CalloutNum == 1 ? STREAM_EDITOR_SUBLAYER_1 : STREAM_EDITOR_SUBLAYER_2; + filter.weight.type = FWP_EMPTY; + + Status = FwpmFilterAdd(Globals.EngineHandle, &filter, NULL, NULL); + + } //FwpmCalloutAdd + } //FwpsCalloutRegister + + if (!NT_SUCCESS(Status)) + { + if (calloutRegistered) + { + FwpsCalloutUnregisterById(*CalloutId); + } + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status); + return Status; +} + +NTSTATUS +StreamEditRegisterStreamLayerCallouts( + _In_ PVOID DeviceObject, + _In_ const GUID* LayerKey, + _In_ const GUID* CalloutKey, + _In_ FWPM_DISPLAY_DATA* DisplayData, + _Out_ UINT32* CalloutId, + _In_ const int CalloutNum + ) +/* + This function registers callouts that intercept TCP traffic + at WFP FWPM_LAYER_STREAM_V4 or FWPM_LAYER_STREAM_V6 layer. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + FWPS_CALLOUT sCallout = {0}; + FWPM_CALLOUT mCallout = {0}; + BOOLEAN calloutRegistered = FALSE; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!%d", CalloutNum); + + sCallout.calloutKey = *CalloutKey; + sCallout.classifyFn = StreamEditCommonStreamClassify; + sCallout.notifyFn = StreamEditNotifyFunction; + sCallout.flowDeleteFn = StreamEditFlowDeleteFunction; + + // http://msdn.microsoft.com/en-us/library/windows/hardware/ff551224.aspx + // + // FWPS_CALLOUT0 structure + // + // FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW + // If this flag is specified, the filter engine calls the callout driver's + // classifyFn callout function only if there is a context associated with + // the data flow. + // + + sCallout.flags = FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW; + + Status = FwpsCalloutRegister( + DeviceObject, + &sCallout, + CalloutId ); + + if (NT_SUCCESS(Status)) + { + calloutRegistered = TRUE; + + mCallout.calloutKey = *CalloutKey; + mCallout.displayData = *DisplayData; + mCallout.applicableLayer = *LayerKey; // FWPM_LAYER_STREAM_V4 / V6 + + Status = FwpmCalloutAdd( + Globals.EngineHandle, + &mCallout, + NULL, + NULL ); + + if (NT_SUCCESS(Status)) + { + // + // Add Filters for Stream Classify + // + // Note : we are adding a filter with no filter conditions -- i.e. this classifyFn callout + // will be classified for ALL streams/flows. + // However, due to FWP_CALLOUT_FLAG_CONDITIONAL_ON_FLOW set above, + // the classifyFn will be classified only for flows that have a context associated. + // + + FWPM_FILTER filter = { 0 }; + + filter.layerKey = *LayerKey; + filter.displayData.name = L"Stream Edit Sample Filter"; + filter.displayData.description = L"Filter that finds and replaces a token from a TCP stream (@ Stream Layer)"; + + filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; + filter.action.calloutKey = *CalloutKey; + filter.numFilterConditions = 0; + filter.filterCondition = 0; + + filter.subLayerKey = CalloutNum == 1 ? STREAM_EDITOR_SUBLAYER_1 : STREAM_EDITOR_SUBLAYER_2; + filter.weight.type = FWP_EMPTY; // auto-weight + + Status = FwpmFilterAdd(Globals.EngineHandle, &filter, NULL, NULL); + } + } + + if (!NT_SUCCESS(Status)) + { + if (calloutRegistered) + { + FwpsCalloutUnregisterById(*CalloutId); + } + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status); + return Status; +} + + +NTSTATUS +StreamEditRegisterCallouts( +_In_ PVOID DeviceObject + ) +/* + This function registers dynamic callouts and filters that intercept + TCP traffic at WFP FWPM_LAYER_STREAM_V4 and FWPM_LAYER_STREAM_V6 + layer. + + Callouts and filters will be removed during DriverUnload. +*/ +{ + NTSTATUS Status = STATUS_SUCCESS; + + BOOLEAN EngineOpened = FALSE; + BOOLEAN InTransaction = FALSE; + + FWPM_SESSION session = {0}; + + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"--> %!FUNC!"); + + session.flags = FWPM_SESSION_FLAG_DYNAMIC; + + Status = FwpmEngineOpen( + NULL, + RPC_C_AUTHN_WINNT, + NULL, + &session, + &Globals.EngineHandle + ); + + if (NT_SUCCESS(Status)) + { + EngineOpened = TRUE; + + Status = FwpmTransactionBegin(Globals.EngineHandle, 0); + if (NT_SUCCESS(Status)) + { + FWPM_SUBLAYER0 StreamEditSubLayer = { 0 }; + FWPM_DISPLAY_DATA DisplayData; + NTSTATUS StatusV6; + + InTransaction = TRUE; + + // Add SubLayer for Callout Set 1 (OoB V4/V6 callouts) + // Register the first set of callouts at Flow-established V4 and V6 layers + // + StreamEditSubLayer.subLayerKey = STREAM_EDITOR_SUBLAYER_1; + StreamEditSubLayer.displayData.name = L"Stream Edit Sample Sub-Layer 1"; + StreamEditSubLayer.displayData.description = L"Sub-Layer for use by Stream Edit Sample callouts"; + StreamEditSubLayer.weight = 0x40; + + Status = FwpmSubLayerAdd(Globals.EngineHandle, &StreamEditSubLayer, NULL); + + if (NT_SUCCESS(Status)) + { + DisplayData.name = L"Stream Editor Sample ALE Flow Established V4 Callout #1"; + DisplayData.description = L"Flow Established V4 Callout to associate flow-contexts with flows"; + + Status = StreamEditRegisterFlowEstablishedCallouts( + DeviceObject, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, + &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4, + &DisplayData, + &Globals.FlowEstablishedV4Callout1, + 1); + + DisplayData.name = L"Stream Editor Sample ALE Flow Established V6 Callout #1"; + DisplayData.description = L"Flow Established V6 Callout to associate flow-contexts with flows"; + + StatusV6 = StreamEditRegisterFlowEstablishedCallouts( + DeviceObject, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, + &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6, + &DisplayData, + &Globals.FlowEstablishedV6Callout1, + 1); + + if (NT_SUCCESS(Status) || NT_SUCCESS(StatusV6)) + { + DisplayData.name = L"Stream Editor Sample Stream Layer V4 Callout #1"; + DisplayData.description = L"Stream-Layer V4 Callout finds and replaces token(s) from a TCP stream"; + + Status = StreamEditRegisterStreamLayerCallouts( + DeviceObject, + &FWPM_LAYER_STREAM_V4, + &STREAM_EDITOR_STREAM_CALLOUT_V4, + &DisplayData, + &Globals.StreamLayerV4Callout1, + 1); + + DisplayData.name = L"Stream Editor Sample Stream Layer V6 Callout #1"; + DisplayData.description = L"Stream-Layer V6 Callout finds and replaces token(s) from a TCP stream"; + + StatusV6 = StreamEditRegisterStreamLayerCallouts( + DeviceObject, + &FWPM_LAYER_STREAM_V6, + &STREAM_EDITOR_STREAM_CALLOUT_V6, + &DisplayData, + &Globals.StreamLayerV6Callout1, + 1); + + if (!(NT_SUCCESS(Status) || NT_SUCCESS(StatusV6))) + { + NT_ASSERT(FALSE); + } + } // RegisterStreamLayerCallouts + }//FwpmSubLayerAdd + + + + + if (Globals.MultipleCallouts) + { + // Add SubLayer for Callout Set 2 (OoB V4/V6 callouts) + // Register the second set of callouts at Flow-established V4 and V6 layers + // + + StreamEditSubLayer.subLayerKey = STREAM_EDITOR_SUBLAYER_2; + StreamEditSubLayer.displayData.name = L"Stream Edit Sample Sub-Layer 2"; + StreamEditSubLayer.displayData.description = L"Sub-Layer for use by Stream Edit Sample callouts"; + StreamEditSubLayer.flags = 0; + StreamEditSubLayer.weight = 0x20; + + Status = FwpmSubLayerAdd(Globals.EngineHandle, &StreamEditSubLayer, NULL); + + if (NT_SUCCESS(Status)) + { + // Register second set of callouts at Flow-established V4 and V6 layers + // + DisplayData.name = L"Stream Editor Sample ALE Flow Established V4 Callout #2"; + DisplayData.description = L"Flow Established V4 Callout to associate flow-contexts with flows"; + + Status = StreamEditRegisterFlowEstablishedCallouts( + DeviceObject, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4, + &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4_2, + &DisplayData, + &Globals.FlowEstablishedV4Callout2, + 2); + + DisplayData.name = L"Stream Editor Sample ALE Flow Established V6 Callout #2"; + DisplayData.description = L"Flow Established V6 Callout to associate flow-contexts with flows"; + + StatusV6 = StreamEditRegisterFlowEstablishedCallouts( + DeviceObject, + &FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6, + &STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6_2, + &DisplayData, + &Globals.FlowEstablishedV6Callout2, + 2); + + if (NT_SUCCESS(Status) || NT_SUCCESS(StatusV6)) + { + DisplayData.name = L"Stream Editor Sample Stream Layer V4 Callout #2"; + DisplayData.description = L"Stream-Layer V4 Callout finds and replaces token(s) from a TCP stream"; + + Status = StreamEditRegisterStreamLayerCallouts( + DeviceObject, + &FWPM_LAYER_STREAM_V4, + &STREAM_EDITOR_STREAM_CALLOUT_V4_2, + &DisplayData, + &Globals.StreamLayerV4Callout2, + 2); + + DisplayData.name = L"Stream Editor Sample Stream Layer V6 Callout #2"; + DisplayData.description = L"Stream-Layer V6 Callout finds and replaces token(s) from a TCP stream"; + + StatusV6 = StreamEditRegisterStreamLayerCallouts( + DeviceObject, + &FWPM_LAYER_STREAM_V6, + &STREAM_EDITOR_STREAM_CALLOUT_V6_2, + &DisplayData, + &Globals.StreamLayerV6Callout2, + 2); + + if (!(NT_SUCCESS(Status) || NT_SUCCESS(StatusV6))) + { + NT_ASSERT(FALSE); + } + } + } + } // MultiCallout. + + Status = FwpmTransactionCommit(Globals.EngineHandle); + + if (NT_SUCCESS(Status)) + { + InTransaction = FALSE; + } + } // FwpmTransactionBegin + } // FwpmEngineOpen + + if (!NT_SUCCESS(Status)) + { + if (InTransaction) + { + NTSTATUS AbortStatus; + AbortStatus = FwpmTransactionAbort(Globals.EngineHandle); + _Analysis_assume_(NT_SUCCESS(AbortStatus)); + } + + if (EngineOpened) + { + FwpmEngineClose(Globals.EngineHandle); + Globals.EngineHandle = NULL; + } + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status); + return Status; +} + +VOID +StreamEditUnregisterCallout(VOID) +{ + // Unregister the callouts for Callout #1 + // + FwpsCalloutUnregisterById(Globals.FlowEstablishedV4Callout1); + FwpsCalloutUnregisterById(Globals.StreamLayerV4Callout1); + + FwpsCalloutUnregisterById(Globals.FlowEstablishedV6Callout1); + FwpsCalloutUnregisterById(Globals.StreamLayerV6Callout1); + + FwpmSubLayerDeleteByKey(Globals.EngineHandle, &STREAM_EDITOR_SUBLAYER_1); + + // Unregister the callouts for Callout #2 + // + if (Globals.MultipleCallouts) + { + FwpsCalloutUnregisterById(Globals.FlowEstablishedV4Callout2); + FwpsCalloutUnregisterById(Globals.StreamLayerV4Callout2); + + FwpsCalloutUnregisterById(Globals.FlowEstablishedV6Callout2); + FwpsCalloutUnregisterById(Globals.StreamLayerV6Callout2); + + FwpmSubLayerDeleteByKey(Globals.EngineHandle, &STREAM_EDITOR_SUBLAYER_2); + } + + NT_ASSERT(Globals.EngineHandle != NULL); + FwpmEngineClose(Globals.EngineHandle); + Globals.EngineHandle = NULL; +} + +_Function_class_(EVT_WDF_DRIVER_UNLOAD) +_IRQL_requires_same_ +_IRQL_requires_max_(PASSIVE_LEVEL) +VOID +StreamEditEvtDriverUnload( + _In_ WDFDRIVER DriverObject + ) +{ + ULONG nCount; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "--> %!FUNC!: (DrvObj %p)", DriverObject); + InterlockedExchange8( &Globals.DriverUnloading, TRUE); + + StreamEditSignalShutdown(); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "DriverUnload -- Waiting for all the flows to terminate"); + KeWaitForSingleObject(&Globals.ZeroFlowCountEvent, Executive, KernelMode, FALSE, NULL); + + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "DriverUnload -- Calling FwpsInjectionHandleDestroy"); + // FwpsInjectionHandleDestroy will _not_ return to the + // caller until all pending injections are completed. + // + if (Globals.InjectionHandle != NULL) + FwpsInjectionHandleDestroy(Globals.InjectionHandle); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "DriverUnload -- Now, uninitializing LW Queues"); + for (nCount = 0; nCount < NUM_WORKITEM_QUEUES; ++nCount) + { + LwUninitializeQueue(&Globals.ProcessingQueues[nCount]); + } + + if (Globals.LookasideCreated) + ExDeleteLookasideListEx(&Globals.LookasideList); + + if (Globals.EngineHandle != NULL) + StreamEditUnregisterCallout(); + + if (Globals.NetBufferListPool != NULL) + NdisFreeNetBufferListPool(Globals.NetBufferListPool); + + if (Globals.NdisGenericObj != NULL) + NdisFreeGenericObject(Globals.NdisGenericObj); + + if (Globals.StringToReplaceMdl != NULL) + IoFreeMdl(Globals.StringToReplaceMdl); + + if (Globals.StringXMdl != NULL) + IoFreeMdl(Globals.StringXMdl); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!"); + WPP_CLEANUP(DriverObject); +} + +VOID +StreamEditInitConfig( + _In_ const WDFDRIVER driver + ) +/* + This function loads the default StreamEditor configuration values, + then overrides any values specified in the registry. +*/ +{ + + NTSTATUS Status = STATUS_SUCCESS; + DECLARE_CONST_UNICODE_STRING(stringToFindKey, L"StringToFind"); + DECLARE_CONST_UNICODE_STRING(stringInMiddleKey, L"StringX"); + DECLARE_CONST_UNICODE_STRING(stringToReplaceKey, L"StringToReplace"); + DECLARE_CONST_UNICODE_STRING(inspectionLocalPortKey, L"InspectionLocalPort"); + DECLARE_CONST_UNICODE_STRING(inspectionRemotePortKey, L"InspectionRemotePort"); + DECLARE_CONST_UNICODE_STRING(multiCalloutKey, L"MultipleCallouts"); + DECLARE_CONST_UNICODE_STRING(inspectionDirectionKey, L"InspectionDirection"); + DECLARE_CONST_UNICODE_STRING(thresholdKey, L"BusyThreshold"); + + UNICODE_STRING stringValue; + WCHAR buffer[STR_MAX_SIZE]; + USHORT requiredSize; + ULONG valueSize; + ULONG ulongValue; + WDFKEY hKey; + + // Initialize with default values. + // String lengths will be initialized later (below). + // + Globals.InspectionLocalPort = CFG_LOCAL_PORT; + Globals.InspectionRemotePort = 0; + Globals.InspectionDirection = FWP_DIRECTION_MAX; // Inbound + outbound + Globals.BusyThreshold = 0x4000; // == 16K; + Globals.MultipleCallouts = TRUE; + + Globals.StringToFind[0] = Globals.StringX[0] = Globals.StringToReplace[0] = '\0'; + + Status = WdfDriverOpenParametersRegistryKey(driver, KEY_READ, WDF_NO_OBJECT_ATTRIBUTES, &hKey); + if (NT_SUCCESS(Status)) + { + + if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &inspectionLocalPortKey, &ulongValue))) + { + Globals.InspectionLocalPort = (USHORT)ulongValue; + } + + if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &inspectionRemotePortKey, &ulongValue))) + { + Globals.InspectionRemotePort = (USHORT)ulongValue; + } + + if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &inspectionDirectionKey, &ulongValue))) + { + Globals.InspectionDirection = (UCHAR)ulongValue; + NT_ASSERT((Globals.InspectionDirection >= 0 && Globals.InspectionDirection <= FWP_DIRECTION_MAX)); + + if (Globals.InspectionDirection > FWP_DIRECTION_MAX) + Globals.InspectionDirection = FWP_DIRECTION_MAX; + } + + if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &multiCalloutKey, &ulongValue))) + { + Globals.MultipleCallouts = !(ulongValue == 0); + } + + + // Attempt to read StringToFind value from registry + // + stringValue.Buffer = buffer; + stringValue.Length = 0; + stringValue.MaximumLength = sizeof(buffer); + + Status = WdfRegistryQueryUnicodeString(hKey, &stringToFindKey, &requiredSize, &stringValue); + if (NT_SUCCESS(Status)) + { + // stringValue is NULL terminated. + + // Translate Unicode string + Status = RtlUnicodeToMultiByteN( + Globals.StringToFind, + sizeof(Globals.StringToFind), + &valueSize, + stringValue.Buffer, + (ULONG)requiredSize); + + if (NT_SUCCESS(Status)) + { + valueSize -= sizeof(char); + Globals.StringToFindLength = valueSize; + NT_ASSERT(Globals.StringToFind[valueSize] == '\0'); + } + } + + stringValue.MaximumLength = sizeof(buffer); + //Attempt to read StringX value from registry + // + Status = WdfRegistryQueryUnicodeString(hKey, &stringInMiddleKey, &requiredSize, &stringValue); + if (NT_SUCCESS(Status)) + { + // Translate Unicode string + Status = RtlUnicodeToMultiByteN( + Globals.StringX, + sizeof(Globals.StringX), + &valueSize, + stringValue.Buffer, + (ULONG)requiredSize); + + if (NT_SUCCESS(Status)) + { + valueSize -= sizeof(char); // NULL terminator. + Globals.StringXLength = valueSize; + NT_ASSERT(Globals.StringX[valueSize] == '\0'); + } + } + + stringValue.MaximumLength = sizeof(buffer); + // Attempt to read StringToReplace value from registry + // + Status = WdfRegistryQueryUnicodeString(hKey, &stringToReplaceKey, &requiredSize, &stringValue); + if (NT_SUCCESS(Status)) + { + // Translate Unicode string + Status = RtlUnicodeToMultiByteN( + Globals.StringToReplace, + sizeof(Globals.StringToReplace), + &valueSize, + stringValue.Buffer, + (ULONG)requiredSize); + + if (NT_SUCCESS(Status)) + { + valueSize -= sizeof(char); + Globals.StringToReplaceLength = valueSize; + NT_ASSERT(Globals.StringToReplace[valueSize] == '\0'); + } + } + + if (NT_SUCCESS(WdfRegistryQueryULong(hKey, &thresholdKey, &ulongValue))) + { + Globals.BusyThreshold = (size_t)ulongValue << 10; + NT_ASSERT(Globals.BusyThreshold != 0); + } + + WdfRegistryClose(hKey); + } + else + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "WdfDriverOpenParametersRegistryKey failed with %!STATUS!", Status); + } + + // Calculate the length of tokens to be found/replaced. + // + if (Globals.StringToFindLength == 0) + { + NT_ASSERT(Globals.StringToFind[0] == 0); + + RtlStringCchCopyA(Globals.StringToFind, STR_MAX_SIZE, "rainy"); + Status = RtlStringCchLengthA(Globals.StringToFind, STR_MAX_SIZE, &Globals.StringToFindLength); + + //Handle Error. + NT_ASSERT(NT_SUCCESS(Status)); + } + + if (Globals.StringXLength == 0) + { + NT_ASSERT(Globals.StringX[0] == 0); + + RtlStringCchCopyA(Globals.StringX, STR_MAX_SIZE, "cloudy"); + Status = RtlStringCchLengthA(Globals.StringX, STR_MAX_SIZE, &Globals.StringXLength); + + //Handle Error. + NT_ASSERT(NT_SUCCESS(Status)); + } + + if (Globals.StringToReplaceLength == 0) + { + NT_ASSERT(Globals.StringToReplace[0] == 0); + + RtlStringCchCopyA(Globals.StringToReplace, STR_MAX_SIZE, "sunny"); + Status = RtlStringCchLengthA(Globals.StringToReplace, STR_MAX_SIZE, &Globals.StringToReplaceLength); + + //Handle Error. + NT_ASSERT(NT_SUCCESS(Status)); + } + + NT_ASSERT(Globals.StringToFindLength != 0); + NT_ASSERT(Globals.StringXLength != 0); + NT_ASSERT(Globals.StringToReplaceLength != 0); + + // In this sample, we want to make sure that at least one port (either local or remote) is non-zero. + // + if ((Globals.InspectionLocalPort == 0) && (Globals.InspectionRemotePort == 0)) + { + Globals.InspectionLocalPort = CFG_LOCAL_PORT; + } + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL,"StreamEdit Configuration\r" + "\tStringToFind: %s\r\tStringToReplace: %s\r\tStringX: %s\r" + "\tInspectionLocalPort: %hu\r\tInspectionRemotePort: %hu\r" + "\tInspectionDirection: %!FWP_DIRECTION!\r\tBusyThreshold: 0x%IX\r\tMultiple Callouts: %!bool!", + Globals.StringToFind, + Globals.StringToReplace, + Globals.StringX, + Globals.InspectionLocalPort, + Globals.InspectionRemotePort, + Globals.InspectionDirection, + Globals.BusyThreshold, + Globals.MultipleCallouts ); + +} + +NTSTATUS +StreamEditInitDriverObjects( + _Inout_ DRIVER_OBJECT* driverObject, + _In_ const UNICODE_STRING* registryPath, + _Out_ WDFDRIVER* pDriver, + _Out_ WDFDEVICE* pDevice + ) +{ + NTSTATUS Status; + WDF_DRIVER_CONFIG config; + PWDFDEVICE_INIT pInit = NULL; + + WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); + + config.DriverInitFlags |= WdfDriverInitNonPnpDriver; + config.EvtDriverUnload = StreamEditEvtDriverUnload; + + Status = WdfDriverCreate( + driverObject, + registryPath, + WDF_NO_OBJECT_ATTRIBUTES, + &config, + pDriver + ); + + if (NT_SUCCESS(Status)) + { + Status = STATUS_INSUFFICIENT_RESOURCES; + pInit = WdfControlDeviceInitAllocate(*pDriver, &SDDL_DEVOBJ_KERNEL_ONLY); + + if (pInit) + { + WdfDeviceInitSetCharacteristics(pInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE); + //WdfDeviceInitSetDeviceType(pInit, FILE_DEVICE_NETWORK); + WdfDeviceInitSetDeviceClass(pInit, &WFP_DRIVER_CLASS_GUID); + WdfDeviceInitSetCharacteristics(pInit, FILE_DEVICE_SECURE_OPEN, TRUE); + + Status = WdfDeviceCreate(&pInit, WDF_NO_OBJECT_ATTRIBUTES, pDevice); + + if (NT_SUCCESS(Status)) + { + WdfControlFinishInitializing(*pDevice); + } + } + } + + if (!NT_SUCCESS(Status)) + { + if ( pInit) + WdfDeviceInitFree(pInit); + } + + return Status; +} + +NTSTATUS +DriverEntry( + _In_ DRIVER_OBJECT* DriverObject, + _In_ UNICODE_STRING* RegistryPath + ) +{ + NTSTATUS Status; + WDFDEVICE WdfDevice; + WDFDRIVER WdfDriver; + NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParams = {0}; + + WPP_INIT_TRACING(DriverObject, RegistryPath); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT,"--> %!FUNC!: DrvObj %p, Regpath %wZ", DriverObject, RegistryPath); + + do { + // + // Initialize globals and Configuration structures. + // + + RtlZeroMemory(&Globals, sizeof(Globals)); + Globals.QueueIndex = (ULONG)-1; + + InitializeListHead(&Globals.FlowContextList); + KeInitializeSpinLock(&Globals.FlowContextListLock); + + // Initialize DriverUnload/Shutdown Event (to a signalled state) + // + KeInitializeEvent(&Globals.ZeroFlowCountEvent, NotificationEvent, TRUE); + + Status = StreamEditInitDriverObjects( + DriverObject, + RegistryPath, + &WdfDriver, + &WdfDevice); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "StreamEditInitDriverObjects failed with 0x%X", Status); + break; + } + + Globals.WdmDevice = WdfDeviceWdmGetDeviceObject(WdfDevice); + + // Initialize and read driver configuration overrides. + // + StreamEditInitConfig(WdfDriver); + + Globals.StringToReplaceMdl = IoAllocateMdl( + Globals.StringToReplace, + (ULONG)Globals.StringToReplaceLength, + FALSE, + FALSE, + NULL); + + if (Globals.StringToReplaceMdl == NULL) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Unable to allocate StringToReplace Mdl"); + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + MmBuildMdlForNonPagedPool(Globals.StringToReplaceMdl); + + Globals.StringXMdl = IoAllocateMdl( + Globals.StringX, + (ULONG)Globals.StringXLength, + FALSE, + FALSE, + NULL); + + if (Globals.StringXMdl == NULL) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Unable to allocate Mdl#2"); + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + MmBuildMdlForNonPagedPool(Globals.StringXMdl); + + Globals.NdisGenericObj = NdisAllocateGenericObject(DriverObject, STMEDIT_TAG_NDIS_OBJ, 0); + if (Globals.NdisGenericObj == NULL) + { + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_GENERAL, "NdisAllocateGenericObject failed."); + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + // + // Allocate a NDIS/NBL Pool + + nblPoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; + nblPoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + nblPoolParams.Header.Size = NDIS_SIZEOF_NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; + nblPoolParams.fAllocateNetBuffer = TRUE; + nblPoolParams.DataSize = 0; + nblPoolParams.PoolTag = STMEDIT_TAG_NBL_POOL; + + Globals.NetBufferListPool = NdisAllocateNetBufferListPool( + Globals.NdisGenericObj, + &nblPoolParams); + + if (Globals.NetBufferListPool == NULL) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "NdisAllocateNetBufferListPool failed."); + Status = STATUS_INSUFFICIENT_RESOURCES; + break; + } + + Status = ExInitializeLookasideListEx( + &Globals.LookasideList, + NULL, + NULL, + NonPagedPool, // POOL_NX_OPTIN_AUTO ==> NonPagedPool := NonPagedPoolNx + 0, + max(sizeof(TASK_ENTRY), sizeof(OUTGOING_STREAM_DATA)), + STMEDIT_TAG_TASK_ENTRY, + 0); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "Task LookasideList Creation failed with %!STATUS!", Status); + break; + } + + Globals.LookasideCreated = TRUE; + + Status = StreamEditInitializeWorkitemPool(); + + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "InitializeWorkerPool failed with %!STATUS!", Status); + break; + } + + // Create WFP Injection handle + // + Status = FwpsInjectionHandleCreate(AF_UNSPEC, FWPS_INJECTION_TYPE_STREAM, &Globals.InjectionHandle); + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "FwpsInjectionHandleCreate failed with %!STATUS!", Status); + break; + } + + // + // Finally, register the sublayer(s) and callouts with WFP + // + Status = StreamEditRegisterCallouts(Globals.WdmDevice); + if (!NT_SUCCESS(Status)) + { + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, "StreamEditRegisterCallouts failed with %!STATUS!", Status); + break; + } + + } while (FALSE); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, "<-- %!FUNC!, %!STATUS!", Status); + + if (!NT_SUCCESS(Status)) + { + StreamEditEvtDriverUnload(WdfDriver); + } + + return Status; +} + +BOOLEAN +StreamEditCopyDataForInspection( +_In_ STREAM_FLOW_CONTEXT *FlowContext, +_In_ const FWPS_STREAM_DATA* StreamData, +_In_ SIZE_T BytesToCopy +) +/* + This function copies stream data described by the FWPS_STREAM_DATA + structure into a flat buffer. + + Return : TRUE if able to copy stream-data to a flat buffer successfully, FALSE otherwise. + +*/ +{ + SIZE_T BytesCopied; + size_t ExistingDataLength = FlowContext->ScratchDataLength; + + NT_ASSERT(BytesToCopy > 0); + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "--> %!FUNC!: FlowCtx %p, streamData %p, copy %Iu of %Iu, old ScratchLength %Iu", + FlowContext, + StreamData, + BytesToCopy, + StreamData->dataLength, + ExistingDataLength); + + NT_ASSERT(FlowContext->ScratchDataOffset == 0); + + // If the existing scratch buffer is not sufficient to accommodate the new data + // try to allocate a bigger buffer (so that we don't have to keep allocating + // these for some time. + // + if (FlowContext->ScratchBufferSize - ExistingDataLength < BytesToCopy) + { + size_t NewBufferSize = BytesToCopy + ExistingDataLength; + + PVOID NewBuffer = ExAllocatePoolWithTag( + NonPagedPool, + (NewBufferSize + (NewBufferSize >> 1) ), // 1.5 times the needed size. + STMEDIT_TAG_FLAT_BUFFER); + + if (NewBuffer == NULL) + { + + // We are not able to allocate a much bigger buffer ... lets try an exact fit. + // + NewBuffer = ExAllocatePoolWithTag(NonPagedPool, NewBufferSize, STMEDIT_TAG_FLAT_BUFFER); + } + + if (NewBuffer != NULL) + { + + // Move the existing contents of scratch buffer over to newly allocated buffer + // + if (ExistingDataLength > 0) + { + + NT_ASSERT(FlowContext->ScratchBuffer != NULL); + RtlCopyMemory(NewBuffer, FlowContext->ScratchBuffer, ExistingDataLength); + } + } + + // Free the old scratch buffer... + // + if (FlowContext->ScratchBuffer) + { + + ExFreePoolWithTag(FlowContext->ScratchBuffer, STMEDIT_TAG_FLAT_BUFFER); + + FlowContext->ScratchBuffer = NULL; + FlowContext->ScratchBufferSize = 0; + FlowContext->ScratchDataLength = 0; + } + + if (NewBuffer) + { + + FlowContext->ScratchBuffer = NewBuffer; + FlowContext->ScratchBufferSize = NewBufferSize; + FlowContext->ScratchDataLength = ExistingDataLength; + } + else + { + + DoTraceLevelMessage(TRACE_LEVEL_ERROR, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p, Failed to allocate flat buffer for NBL %p", + FlowContext, StreamData->netBufferListChain); + return FALSE; + } + } + + // Append the NBL chain data on to (any) existing data in scratch buffer + // + FwpsCopyStreamDataToBuffer( + StreamData, + (BYTE*)FlowContext->ScratchBuffer + FlowContext->ScratchDataLength, + BytesToCopy, + &BytesCopied + ); + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_GENERAL, + "FlowCtx %p, FwpsCopyStreamDataToBuffer flattened %Iu of %Iu bytes", + FlowContext, BytesCopied, StreamData->dataLength); + + NT_ASSERT(BytesCopied == BytesToCopy); + FlowContext->ScratchDataLength += BytesCopied; + + DoTraceLevelMessage(TRACE_LEVEL_INFORMATION, CO_ENTER_EXIT, + "<-- %!FUNC!: FlowCtx %p, new ScratchLength %Iu, return TRUE", FlowContext, FlowContext->ScratchDataLength); + return TRUE; +} diff --git a/network/trans/stmedit/sys/StreamEdit.h b/network/trans/stmedit/sys/StreamEdit.h new file mode 100644 index 00000000..8d203db7 --- /dev/null +++ b/network/trans/stmedit/sys/StreamEdit.h @@ -0,0 +1,558 @@ +/*++ + +Copyright (c) Microsoft Corporation. All rights reserved + +Abstract: + Stream Edit Callout Driver Sample. + + This sample demonstrates finding and replacing a string pattern from a + live TCP stream via the WFP stream API. + +Environment: + Kernel mode + +--*/ + +#ifndef _STREAM_EDIT_H +#define _STREAM_EDIT_H + +#include <ntifs.h> +#include <wdf.h> + +#include <ntddk.h> + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union +#include <fwpsk.h> +#pragma warning(pop) + +#pragma warning(push) +#pragma prefast(disable: 26061) // warning 26061: Range postcondition violation +#pragma prefast(disable: 28196) // RtlUnicodeStringInitWorker restriction +#include <ntstrsafe.h> +#pragma warning(pop) + +#include <fwpmk.h> +#include "LwQueue.h" + +// +// Pool Tags used for allocations +// +#define STMEDIT_TAG_NDIS_OBJ 'oNeS' +#define STMEDIT_TAG_NBL_POOL 'pNeS' +#define STMEDIT_TAG_FLAT_BUFFER 'bSeS' +#define STMEDIT_TAG_FLOWCTX 'cFeS' +#define STMEDIT_TAG_TASK_ENTRY 'eTeS' +#define STMEDIT_TAG_MDL_DATA 'dMeS' + +#define CFG_LOCAL_PORT 8888 +#define STR_MAX_SIZE 128 +#define NUM_WORKITEM_QUEUES 2 +#define INVALID_PROC_NUMBER -1 + +#pragma warning(disable: 4127) // conditional expression is constant -- for do-while(true/false) loops! + +// +// Inline editing states of a stream +// +typedef enum _INLINE_EDIT_STATE +{ + INLINE_EDIT_IDLE = 0, + INLINE_EDIT_SKIPPING, + INLINE_EDIT_MODIFYING, + INLINE_EDIT_SCANNING +} INLINE_EDIT_STATE; + +// +// Out-Of-Band editing states of a stream +// +typedef enum _OOB_EDIT_STATE +{ + OOB_EDIT_IDLE = 0, + OOB_EDIT_PROCESSING, + OOB_EDIT_BUSY, + OOB_EDIT_ERROR +} OOB_EDIT_STATE; + +typedef struct _OUTGOING_STREAM_DATA +{ + // Link for placement on Queue. + LIST_ENTRY Link; + + // NetBuffer List containing data + NET_BUFFER_LIST* NetBufferList; + + // If the NetBufferList is a clone or did we allocate it + BOOLEAN isClone; + + // Valid data length + size_t DataLength; + + // Stream Flags to be used when injecting this segment of data + DWORD StreamFlags; + + MDL* Mdl; +} OUTGOING_STREAM_DATA, *POUTGOING_STREAM_DATA; + +#pragma warning(push) +#pragma warning(disable:4201) // unnamed struct/union +#pragma warning(disable:4214) // nonstandard extension used : bit field types other than int + +// +// Flow Context associated with a flow +// +typedef struct _STREAM_FLOW_CONTEXT +{ + // for putting on the global FlowContest list + LIST_ENTRY Link; + + union + { + // Inline data editting state + struct + { + // State of inline stream edit processing + INLINE_EDIT_STATE InlineEditState; + + // Current Processor on which inline classify function is invoked! + volatile LONG CurrentProcessor; + }; + + struct + { + // Internal Stream State for OoB Processing + OOB_EDIT_STATE EditState; + + // Spin lock to sync editing + KSPIN_LOCK EditLock; + + // Processed data ready to be injected back into the stream. + LIST_ENTRY OutgoingDataQueue; + + // Index to LW Queue, assigned to the flow. + ULONG QueueNumber; + + // Length of data classified, but not yet processed. + volatile size_t PendedDataLength; + + // Number of data processing tasks still queued + volatile ULONG PendingTasks; + + // Flags that specify characteristics of the data stream. + // + // A callout driver should specify the same stream flags that + // were set in the 'flags' member of the _STREAM_DATA structure + // that the filter engine passed to the callout driver's + // classifyFn callout function when the callout deferred the + // data stream. + // + UINT32 StreamFlags; + + } OobInfo; + }; + + // Ref count - # of unprocessed task + volatile ULONG RefCount; + + // IVP4 or V6 ? For troubleshooting only + UINT16 IpProto; + + // Stream Flags for partially matching data. + UINT32 PartialSFlags; + + // + // For copying stream data to a (flat)buffer for inspection + // + PVOID ScratchBuffer; + size_t ScratchBufferSize; + size_t ScratchDataOffset; + size_t ScratchDataLength; + + // + // Flow information for data (re-)injection. + // + + UINT64 FlowHandle; + UINT16 LayerId; + UINT32 CalloutId; + + volatile char bFlowActive; // If the Flow is still active. true @FlowEstablished, false @FlowDelete + + BOOLEAN bFlowTerminating : 1; // FIN/RST has been received. + BOOLEAN bNoMoreData : 1; // classifyOut->flags has FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA bit set + BOOLEAN bEntryRemoved : 1; // True when the context is not on the 'global context list'. + BOOLEAN bEditInline : 1; + BOOLEAN bUnused : 4; + +} STREAM_FLOW_CONTEXT, *PSTREAM_FLOW_CONTEXT; + +#pragma warning(pop) + + +// Task Entry structure +// For dispatching tasks to threads! +// +typedef struct _TASK_ENTRY +{ + // Link for placement on Queues + LW_ENTRY LwQLink; + + // Target Flow Context + PSTREAM_FLOW_CONTEXT FlowCtx; + + // NetBufferList Chain to be processed + NET_BUFFER_LIST *NetBufferList; + + // Length of NBL Chain + size_t DataLength; + + // Flags.. + DWORD StreamFlags; + +} TASK_ENTRY, *PTASK_ENTRY; + +// +// Stream Editor Globals block +// +typedef struct _STMEDIT_GLOBALS +{ + // Lookaside list for Task entries and OutgoingData structs. + // On 64-bit platforms, this structure must be 16-byte aligned. + // + DECLSPEC_CACHEALIGN LOOKASIDE_LIST_EX LookasideList; + + + // ... For Callout #1 + // + // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 layer + UINT32 FlowEstablishedV4Callout1; + + // IPV4 callout for FWPM_LAYER_STREAM_V4 layer + UINT32 StreamLayerV4Callout1; + + // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 layer + UINT32 FlowEstablishedV6Callout1; + + // IPV4 callout for FWPM_LAYER_STREAM_V6 layer + UINT32 StreamLayerV6Callout1; + + // + // ...For Callout #2 + // + // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V4 layer + UINT32 FlowEstablishedV4Callout2; + + // IPV4 callout for FWPM_LAYER_STREAM_V4 layer + UINT32 StreamLayerV4Callout2; + + // IPV4 callout for FWPM_LAYER_ALE_FLOW_ESTABLISHED_V6 layer + UINT32 FlowEstablishedV6Callout2; + + // IPV4 callout for FWPM_LAYER_STREAM_V6 layer + UINT32 StreamLayerV6Callout2; + + // WFP injection handles + HANDLE InjectionHandle; + + // WFP Engine handle + HANDLE EngineHandle; + + // NDIS uses a generic object to manage resources that are allocated + // by a component that does not otherwise have an NDIS handle. + NDIS_GENERIC_OBJECT* NdisGenericObj; + + // To allocate NetBuffer and NetBufferLists for injection + NDIS_HANDLE NetBufferListPool; + + // MDL chain used to initialize the preallocated NET_BUFFER + // structure for injecting replacement string. + MDL* StringToReplaceMdl; + MDL* StringXMdl; + + // Number of flow-context structures allocated (mostly for troubleshooting) + ULONG FlowContextCount; + + // A linked list of flow-context structures + LIST_ENTRY FlowContextList; + + // Spin lock for synchronizing FlowContextList access + KSPIN_LOCK FlowContextListLock; + + // Event to signal if Flow Context count is zero! + KEVENT ZeroFlowCountEvent; + + DEVICE_OBJECT* WdmDevice; // Device object for StreamEdit + + // + // Configurable parameters block + // + + // Register two sets of callouts ? + BOOLEAN MultipleCallouts; + + // Local port for inspecting network traffic + USHORT InspectionLocalPort; + + // Remote port for inspecting network traffic + USHORT InspectionRemotePort; + + // What traffic direction to inspect + UCHAR InspectionDirection; + + // Busy Threshold (to Defer/Resume stream) + size_t BusyThreshold; + + // String to search for in the stream + CHAR StringToFind[STR_MAX_SIZE]; + + // Replacement string! + CHAR StringX[STR_MAX_SIZE]; + + // Replacement string! + CHAR StringToReplace[STR_MAX_SIZE]; + + // Length of StringToFind + size_t StringToFindLength; + + // Length of StringToReplace + size_t StringXLength; + + // Length of StringToReplace + size_t StringToReplaceLength; + + // True if the driver is unloading/shutting down + volatile char DriverUnloading; + + // To generate workitem queue array index to be associated to a flow. + volatile ULONG QueueIndex; + + // Queues for processing task workitems + LW_QUEUE ProcessingQueues[NUM_WORKITEM_QUEUES]; + + // True if TaskEntry look aside list is successfully initialized. + BOOLEAN LookasideCreated; + +} STMEDIT_GLOBALS; + + +extern STMEDIT_GLOBALS Globals; + +typedef +VOID +WFP_CLASSIFY_FUNCTION( + _In_ const FWPS_INCOMING_VALUES*, + _In_ const FWPS_INCOMING_METADATA_VALUES*, + _In_ PVOID, +#if(NTDDI_VERSION >= NTDDI_WIN7) + _In_ const void*, +#endif + _In_ const FWPS_FILTER*, + _In_ UINT64, + _Inout_ FWPS_CLASSIFY_OUT* +); + + +IO_WORKITEM_ROUTINE StreamEditOobPoolWorker; + +NTSTATUS +StreamEditInitializeWorkitemPool( + ); + +NTSTATUS +StreamOobFlushOutgoingData( + _Inout_ PSTREAM_FLOW_CONTEXT + ); + +NTSTATUS +StreamEditRemoveFlowCtx( + _In_ STREAM_FLOW_CONTEXT* + ); + +VOID +StreamEditSignalShutdown( + ); + +BOOLEAN +StreamEditCopyDataForInspection( + _In_ PSTREAM_FLOW_CONTEXT, + _In_ const FWPS_STREAM_DATA*, + _In_ SIZE_T BytesToCopy + ); + +VOID +NTAPI +StreamEditInjectCompletionFn( + _In_ VOID*, + _Inout_ NET_BUFFER_LIST*, + _In_ BOOLEAN + ); + +VOID +NTAPI +StreamOobCloneInjectCompletionFn( + _In_ VOID*, + _Inout_ NET_BUFFER_LIST*, + _In_ BOOLEAN + ); + +NTSTATUS +OobEditCreateThread( + _Inout_ PSTREAM_FLOW_CONTEXT + ); + +VOID +InlineEditClassify( + _In_ const FWPS_INCOMING_VALUES*, + _In_ const FWPS_INCOMING_METADATA_VALUES*, + _Inout_ PVOID, + _In_ const FWPS_FILTER*, + _In_ UINT64, + _Inout_ FWPS_CLASSIFY_OUT* + ); + +VOID +OobEditClassify( + _In_ const FWPS_INCOMING_VALUES*, + _In_ const FWPS_INCOMING_METADATA_VALUES*, + _Inout_ PVOID, + _In_ const FWPS_FILTER*, + _In_ UINT64, + _Inout_ FWPS_CLASSIFY_OUT* + ); + +WFP_CLASSIFY_FUNCTION StreamEditFlowEstablishedClassify; +WFP_CLASSIFY_FUNCTION StreamEditCommonStreamClassify; + +NTSTATUS +InlineEditFlushData( + _In_ PSTREAM_FLOW_CONTEXT, + _In_ ULONG, + _In_ UINT + ); + +FORCEINLINE +VOID +StreamEditFreeFlowCtxCommon( + _Inout_ PSTREAM_FLOW_CONTEXT + ); + +VOID +StmEditReferenceFlow( + _Inout_ PSTREAM_FLOW_CONTEXT, + _In_ char, + _In_ UINT + ); + +VOID +StmEditDeReferenceFlow( + _Inout_ PSTREAM_FLOW_CONTEXT, + _In_ char, + _In_ UINT + ); + +FORCEINLINE +ULONG NetBufferListLength(PNET_BUFFER_LIST Nbl) +{ + ULONG Length = 0; + PNET_BUFFER nb = NET_BUFFER_LIST_FIRST_NB(Nbl); + + while (nb) + { + Length += NET_BUFFER_DATA_LENGTH(nb); + nb = NET_BUFFER_NEXT_NB(nb); + } + + return Length; +} + +// +// Callout driver keys +// + +/* 524F4849-5420-5241-494E-41205A9EEF0B */ +__declspec (selectany) const GUID STREAM_EDITOR_SUBLAYER_1 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0A } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF0B */ +__declspec (selectany) const GUID STREAM_EDITOR_SUBLAYER_2 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0B } +}; + + +/* 524F4849-5420-5241-494E-41205A9EEF0C */ +__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V4 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0C } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF0D */ +__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V6 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0D } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF0E */ +__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0E } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF0F */ +__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x0F } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF01 */ +__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V4_2 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x01 } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF02 */ +__declspec (selectany) const GUID STREAM_EDITOR_STREAM_CALLOUT_V6_2 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x02 } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF03 */ +__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V4_2 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x03 } +}; + +/* 524F4849-5420-5241-494E-41205A9EEF04 */ +__declspec (selectany) const GUID STREAM_EDITOR_FLOW_ESTABLISHED_CALLOUT_V6_2 = { + 0x524F4849, + 0x5420, + 0x5241, + { 0x49, 0x4E, 0x41, 0x20, 0x5A, 0x9E, 0xEF, 0x04 } +}; + +// {C1EA91DC-A37F-453E-BFD5-EF68E36EEDAA} +__declspec (selectany) const GUID WFP_DRIVER_CLASS_GUID = +{ 0xc1ea91dc, 0xa37f, 0x453e,{ 0xbf, 0xd5, 0xef, 0x68, 0xe3, 0x6e, 0xed, 0xaa } }; + + +#endif // _STREAM_EDIT_H diff --git a/network/trans/stmedit/sys/Trace.h b/network/trans/stmedit/sys/Trace.h new file mode 100644 index 00000000..b52033cd --- /dev/null +++ b/network/trans/stmedit/sys/Trace.h @@ -0,0 +1,61 @@ + +#ifndef _STREAM_TRACE_H +#define _STREAM_TRACE_H + +// WPP Software Tracing Definitions + + +/* 524F4849-5420-5241-494E-41205A9EEF01 */ + +// You can have up to 32 defines. If you want more than that, +// you have to provide another trace control GUID + +// Don't use 0x suffix in hex numbers in the guid + +#define WPP_CONTROL_GUIDS \ + WPP_DEFINE_CONTROL_GUID(CtlGuid, \ + (524F4849, 5420, 5241, 494E, 41205A9EEF01),\ + WPP_DEFINE_BIT(CO_ENTER_EXIT) /* bit 0 = 0x00000001 */ \ + WPP_DEFINE_BIT(CO_GENERAL) /* bit 1 = 0x00000002 */ \ + WPP_DEFINE_BIT(CO_REFCOUNT) /* bit 2 = 0x00000004 */ \ + ) + +// For DoTraceLevelMessage +#define WPP_LEVEL_FLAGS_ENABLED(lvl, flags) (WPP_FLAG_ENABLED(flags) && WPP_CONTROL(WPP_BIT_ ## flags).Level >= lvl) +#define WPP_LEVEL_FLAGS_LOGGER(lvl, flags) WPP_LEVEL_LOGGER(flags) + + +#pragma warning(disable:4204) // C4204 nonstandard extension used : non-constant aggregate initializer + +// +// Define the 'xstr' structure for logging buffer and length pairs +// and the 'log_xstr' function which returns it to create one in-place. +// this enables logging of complex data types. +// +typedef struct xstr { char * _buf; short _len; } xstr_t; +__inline xstr_t log_xstr(void * p, short l) { xstr_t xs = { (char*)p, l }; return xs; } + +#pragma warning(default:4204) + +// +// Define the macro required for a hexdump use as: +// +// DebugTraceEx((LEVEL, FLAG,"%!HEXDUMP!\n", log_xstr(buffersize,(char *)buffer) )); +// +// +#define WPP_LOGHEXDUMP(x) WPP_LOGPAIR(2, &((x)._len)) WPP_LOGPAIR((x)._len, (x)._buf) + +// begin_wpp config +// CUSTOM_TYPE(InlineState, ItemListLong(Idle, Skipping, Modifying, Scanning)); +// CUSTOM_TYPE(OobState, ItemListLong(Idle, Processing, Busy, Error)); +// CUSTOM_TYPE(FWP_DIRECTION, ItemListLong(Outbound, Inbound, Outbound+Inbound)); +// +// CUSTOM_TYPE(FWPS_NOTIFY, ItemEnum(FWPS_CALLOUT_NOTIFY_TYPE)); +// CUSTOM_TYPE(SACTION, ItemEnum(FWPS_STREAM_ACTION_TYPE)); +// +// DEFINE_CPLX_TYPE(HEXDUMP, WPP_LOGHEXDUMP, xstr_t, ItemHEXDump, "s", _HEX_, 0, 2); +// +// end_wpp + + +#endif // _STREAM_TRACE_H diff --git a/network/trans/stmedit/sys/inline_edit.c b/network/trans/stmedit/sys/inline_edit.c deleted file mode 100644 index 16685a9e..00000000 --- a/network/trans/stmedit/sys/inline_edit.c +++ /dev/null @@ -1,546 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved - -Abstract: - - Stream Edit Callout Driver Sample. - - This sample demonstrates inline stream inspection/editing - via the WFP stream API. - -Environment: - - Kernel mode - ---*/ - -#include <ntddk.h> - -#pragma warning(push) -#pragma warning(disable:4201) // unnamed struct/union - -#include <fwpsk.h> - -#pragma warning(pop) - -#include <fwpmk.h> - -#include "inline_edit.h" -#include "oob_edit.h" -#include "stream_callout.h" - -void -InlineEditInit( - _Out_ STREAM_EDITOR* streamEditor - ) -{ - streamEditor->editInline = TRUE; - streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; -} - -void -NTAPI StreamInjectCompletionFn( - _Inout_ void* context, - _Inout_ NET_BUFFER_LIST* netBufferList, - _In_ BOOLEAN dispatchLevel - ) -{ - MDL* mdl = (MDL*)context; - - UNREFERENCED_PARAMETER(dispatchLevel); - - if (mdl != NULL) - { - IoFreeMdl(mdl); - } - - FwpsFreeNetBufferList(netBufferList); -} - -NTSTATUS -StreamEditFlushData( - _Inout_ STREAM_EDITOR* streamEditor, - UINT64 flowId, - UINT32 calloutId, - UINT16 layerId, - UINT32 streamFlags - ) -/* ++ - - This function re-injects buffered data back to the data stream upon - receiving a FIN. The data was buffered because it was not big enough - (size wise) to make an editing decision. - --- */ -{ - NTSTATUS status; - - MDL* mdl = NULL; - NET_BUFFER_LIST* netBufferList = NULL; - - NT_ASSERT(streamEditor->dataOffset == 0); - - mdl = IoAllocateMdl( - streamEditor->scratchBuffer, - (ULONG)(streamEditor->dataLength), - FALSE, - FALSE, - NULL - ); - - if (mdl == NULL) - { - status = STATUS_NO_MEMORY; - goto Exit; - } - - MmBuildMdlForNonPagedPool(mdl); - - status = FwpsAllocateNetBufferAndNetBufferList( - gNetBufferListPool, - 0, - 0, - mdl, - 0, - streamEditor->dataLength, - &netBufferList - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - streamFlags &= ~(FWPS_STREAM_FLAG_SEND_DISCONNECT | FWPS_STREAM_FLAG_RECEIVE_DISCONNECT); - - status = FwpsStreamInjectAsync( - gInjectionHandle, - NULL, - 0, - flowId, - calloutId, - layerId, - streamFlags, - netBufferList, - streamEditor->dataLength, - StreamInjectCompletionFn, - mdl - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - mdl = NULL; - netBufferList = NULL; - -Exit: - - if (mdl != NULL) - { - IoFreeMdl(mdl); - } - if (netBufferList != NULL) - { - FwpsFreeNetBufferList(netBufferList); - } - - return status; -} - -void -StreamInlineEdit( - _Inout_ STREAM_EDITOR* streamEditor, - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _In_ const FWPS_FILTER* filter, - _In_ const FWPS_STREAM_DATA* streamData, - _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ) -/* ++ - - This function implements the state machine that scans the content - and computes the number of bytes to permit, bytes to block, and - performs stream injection to replace the blocked data. - --- */ -{ - UINT findLength = (UINT) strlen(configStringToFind); - UINT replaceLength = (UINT) strlen(configStringToReplace); - - if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || - (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) - { - if (streamEditor->dataLength > 0) - { - StreamEditFlushData( - streamEditor, - inMetaValues->flowHandle, - filter->action.calloutId, - inFixedValues->layerId, - streamData->flags - ); - - streamEditor->dataLength = 0; - streamEditor->dataOffset = 0; - } - - NT_ASSERT(streamEditor->inlineEditState == INLINE_EDIT_WAITING_FOR_DATA); - - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - goto Exit; - } - - if (streamData->dataLength == 0) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - goto Exit; - } - - if (streamEditor->inlineEditState != INLINE_EDIT_SKIPPING) - { - if ((streamData->dataLength < findLength) && - !(classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA; - ioPacket->countBytesRequired = findLength; - - classifyOut->actionType = FWP_ACTION_NONE; - goto Exit; - } - } - - switch (streamEditor->inlineEditState) - { - case INLINE_EDIT_WAITING_FOR_DATA: - { - if (StreamCopyDataForInspection( - streamEditor, - streamData - ) == FALSE) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - goto Exit; - } - - // - // Pass-thru to scanning - // - } - case INLINE_EDIT_SCANNING: - { - UINT i; - BYTE* dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; - BOOLEAN found = FALSE; - - for (i = 0; i < streamEditor->dataLength; ++i) - { - if (i + findLength <= streamEditor->dataLength) - { - if (RtlCompareMemory( - dataStart + i, - configStringToFind, - findLength - ) == findLength) - { - found = TRUE; - - streamEditor->inlineEditState = INLINE_EDIT_MODIFYING; - - if (i != 0) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = i; - - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - streamEditor->dataOffset += i; - streamEditor->dataLength -= i; - - break; - } - else - { - goto modify_data; - } - } - } - else - { - if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) - { - break; - } - - if (RtlCompareMemory( - dataStart + i, - configStringToFind, - streamEditor->dataLength - i - ) == streamEditor->dataLength - i) - { - found = TRUE; // this is a partial find - - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = i; - - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - RtlMoveMemory( - streamEditor->scratchBuffer, - dataStart + i, - streamEditor->dataLength - i - ); - - streamEditor->dataOffset = 0; - streamEditor->dataLength = streamEditor->dataLength - i; - - streamEditor->inlineEditState = INLINE_EDIT_SKIPPING; - - break; - } - } - } - - if (!found) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = 0; - - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - streamEditor->dataOffset = 0; - streamEditor->dataLength = 0; - - streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; - } - - break; - } - case INLINE_EDIT_SKIPPING: - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = 0; - - classifyOut->actionType = FWP_ACTION_BLOCK; - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - - streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; - - break; - } - case INLINE_EDIT_MODIFYING: - -modify_data: - - { - NTSTATUS status; - NET_BUFFER_LIST* netBufferList; - - status = FwpsAllocateNetBufferAndNetBufferList( - gNetBufferListPool, - 0, - 0, - gStringToReplaceMdl, - 0, - replaceLength, - &netBufferList - ); - - if (!NT_SUCCESS(status)) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - goto Exit; - } - - status = FwpsStreamInjectAsync( - gInjectionHandle, - NULL, - 0, - inMetaValues->flowHandle, - filter->action.calloutId, - inFixedValues->layerId, - streamData->flags, - netBufferList, - replaceLength, - StreamInjectCompletionFn, - NULL - ); - - if (!NT_SUCCESS(status)) - { - FwpsFreeNetBufferList(netBufferList); - - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - goto Exit; - } - - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = findLength; - - classifyOut->actionType = FWP_ACTION_BLOCK; - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - - streamEditor->dataOffset += findLength; - streamEditor->dataLength -= findLength; - - if (streamEditor->dataLength > 0) - { - streamEditor->inlineEditState = INLINE_EDIT_SCANNING; - } - else - { - streamEditor->dataOffset = 0; - - streamEditor->inlineEditState = INLINE_EDIT_WAITING_FOR_DATA; - } - - break; - } - default: - NT_ASSERT(FALSE); - break; - }; - -Exit: - - return; -} - -#if(NTDDI_VERSION >= NTDDI_WIN7) - -void -NTAPI -StreamInlineEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const void* classifyContext, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ) - -#else - -void -NTAPI -StreamInlineEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Out_ FWPS_CLASSIFY_OUT* classifyOut - ) - -#endif /// (NTDDI_VERSION >= NTDDI_WIN7) - -/* ++ - - This is the ClassifyFn function registered by the inline stream edit callout. - - An inline stream modification callout performs editing from within the - ClassifyFn call by permitting sections of the content and replacing other - sections by removing them and injecting new content. - --- */ -{ - FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket; - FWPS_STREAM_DATA* streamData; - - ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData; - NT_ASSERT(ioPacket != NULL); - - streamData = ioPacket->streamData; - NT_ASSERT(streamData != NULL); - -#if(NTDDI_VERSION >= NTDDI_WIN7) - UNREFERENCED_PARAMETER(classifyContext); -#endif /// (NTDDI_VERSION >= NTDDI_WIN7) - UNREFERENCED_PARAMETER(flowContext); - - RtlZeroMemory(classifyOut, sizeof(FWPS_CLASSIFY_OUT)); - - // - // Let go the traffic that the editor does not care about. - // - - if ((configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_RECEIVE)) || - (!configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_SEND))) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - goto Exit; - } - - // - // In this sample we don't edit TCP urgent data - // - - if ((streamData->flags & FWPS_STREAM_FLAG_SEND_EXPEDITED) || - (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_EXPEDITED)) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - goto Exit; - } - - StreamInlineEdit( - &gStreamEditor, - inFixedValues, - inMetaValues, - filter, - streamData, - ioPacket, - classifyOut - ); - -Exit: - - return; -} diff --git a/network/trans/stmedit/sys/inline_edit.h b/network/trans/stmedit/sys/inline_edit.h deleted file mode 100644 index a9345324..00000000 --- a/network/trans/stmedit/sys/inline_edit.h +++ /dev/null @@ -1,65 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved - -Abstract: - - Stream Edit Callout Driver Sample. - - This sample demonstrates inline stream inspection/editing - via the WFP stream API. - -Environment: - - Kernel mode - ---*/ - -#ifndef _INLINE_EDIT_H -#define _INLINE_EDIT_H - -typedef enum INLINE_EDIT_STATE_ -{ - INLINE_EDIT_WAITING_FOR_DATA, - INLINE_EDIT_SKIPPING, - INLINE_EDIT_MODIFYING, - INLINE_EDIT_SCANNING -} INLINE_EDIT_STATE; - -typedef struct STREAM_EDITOR_ STREAM_EDITOR; - -void -InlineEditInit( - _Out_ STREAM_EDITOR* - ); - -#if(NTDDI_VERSION >= NTDDI_WIN7) - -void -NTAPI -StreamInlineEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const void* classifyContext, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ); - -#else - -void -NTAPI -StreamInlineEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ); - -#endif /// (NTDDI_VERSION >= NTDDI_WIN7) - -#endif // _INLINE_EDIT_H diff --git a/network/trans/stmedit/sys/oob_edit.c b/network/trans/stmedit/sys/oob_edit.c deleted file mode 100644 index ee53bc5e..00000000 --- a/network/trans/stmedit/sys/oob_edit.c +++ /dev/null @@ -1,1363 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved - -Abstract: - - Stream Edit Callout Driver Sample. - - This sample demonstrates Out-of-band (OOB) stream inspection/editing - via the WFP stream API. - -Environment: - - Kernel mode - ---*/ - -#include <ntddk.h> - -#pragma warning(push) -#pragma warning(disable:4201) // unnamed struct/union - -#include <fwpsk.h> - -#pragma warning(pop) - -#include <fwpmk.h> - -#include "inline_edit.h" -#include "oob_edit.h" -#include "stream_callout.h" - -#define STREAM_EDITOR_OUTGOING_DATA_TAG 'doeS' -#define STREAM_EDITOR_MDL_DATA_TAG 'dmeS' - -void* gThreadObj; - -KSTART_ROUTINE StreamOobEditWorker; - -NTSTATUS -OobEditInit( - _Out_ STREAM_EDITOR* streamEditor - ) -{ - NTSTATUS status = STATUS_SUCCESS; - - HANDLE threadHandle; - - streamEditor->editInline = FALSE; - - KeInitializeSpinLock(&streamEditor->oobEditInfo.editLock); - - KeInitializeEvent( - &streamEditor->oobEditInfo.editEvent, - NotificationEvent, - FALSE - ); - - streamEditor->oobEditInfo.busyThreshold = 32 * 1024; - streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE; - - InitializeListHead(&streamEditor->oobEditInfo.outgoingDataQueue); - - - status = PsCreateSystemThread( - &threadHandle, - THREAD_ALL_ACCESS, - NULL, - NULL, - NULL, - StreamOobEditWorker, - &gStreamEditor - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - status = ObReferenceObjectByHandle( - threadHandle, - 0, - NULL, - KernelMode, - &gThreadObj, - NULL - ); - NT_ASSERT(NT_SUCCESS(status)); - - ZwClose(threadHandle); - -Exit: - - return status; -} - -void -OobEditShutdown( - _Out_ STREAM_EDITOR* streamEditor - ) -{ - KLOCK_QUEUE_HANDLE editLockHandle; - - KeAcquireInStackQueuedSpinLock( - &streamEditor->oobEditInfo.editLock, - &editLockHandle - ); - - streamEditor->oobEditInfo.shuttingDown = TRUE; - - switch (streamEditor->oobEditInfo.editState) - { - case OOB_EDIT_IDLE: - { - streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; - - KeSetEvent( - &gStreamEditor.oobEditInfo.editEvent, - IO_NO_INCREMENT, - FALSE - ); - - break; - } - default: - break; - }; - - KeReleaseInStackQueuedSpinLock(&editLockHandle); - - NT_ASSERT(gThreadObj != NULL); - - KeWaitForSingleObject( - gThreadObj, - Executive, - KernelMode, - FALSE, - NULL - ); - - ObDereferenceObject(gThreadObj); -} - -__inline -NET_BUFFER_LIST* -TailOfNetBufferListChain( - _In_ NET_BUFFER_LIST* netBufferListChain - ) -{ - NT_ASSERT(netBufferListChain != NULL); - - while (netBufferListChain->Next != NULL) - { - netBufferListChain = netBufferListChain->Next; - } - - return netBufferListChain; -} - -void -NTAPI -StreamOobInjectCompletionFn( - _Inout_ void* context, - _Inout_ NET_BUFFER_LIST* netBufferList, - BOOLEAN dispatchLevel - ) -/* ++ - - Injection completion function for injecting an NBL created using - FwpsAllocateNetBufferAndNetBufferList. This function frees up - resources allocated during StreamOobReinjectData(). - --- */ -{ - MDL* mdl = (MDL*)context; - - UNREFERENCED_PARAMETER(dispatchLevel); - - FwpsFreeNetBufferList(netBufferList); - - if (mdl != NULL) - { - IoFreeMdl(mdl); - - // - // The MDL mapped over a pool alloc which we need to free here. - // - - ExFreePoolWithTag( - mdl->MappedSystemVa, - STREAM_EDITOR_MDL_DATA_TAG - ); - } -} - -void -NTAPI StreamOobInjectCloneCompletionFn( - _Inout_ void* context, - _Inout_ NET_BUFFER_LIST* netBufferList, - BOOLEAN dispatchLevel - ) -/* ++ - - Injection completion function for injecting one of the NBLs cloned - via FwpsCloneStreamData. - - FwpsCloneStreamData can return a chain of cloned NBLs; each NBL will - complete separately. - --- */ -{ - UNREFERENCED_PARAMETER(context); - UNREFERENCED_PARAMETER(dispatchLevel); - - FwpsFreeCloneNetBufferList(netBufferList, 0); -} - -NTSTATUS -StreamOobQueueUpIncomingData( - _Inout_ STREAM_EDITOR* streamEditor, - _Inout_ FWPS_STREAM_DATA* streamData - ) -/* ++ - - This function clones the indicated stream data into a NBL chain and - appends the chain at the end of the existing chain (if exists) inside - the streamEditor. - - This function assumes that the oobEditInfo lock inside streamEditor is - being held. - --- */ -{ - NTSTATUS status; - - NET_BUFFER_LIST* clonedNetBufferListChain; - - status = FwpsCloneStreamData( - streamData, - NULL, - NULL, - 0, - &clonedNetBufferListChain - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - // - // TCP Fin (EOF) is indicated by an empty NBL with disconnect flag - // set, since it does not contain any data we queue it up separately. - // - - if ((streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) || - (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) - { - NT_ASSERT(streamEditor->oobEditInfo.noMoreData); - NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL); - NT_ASSERT(streamData->dataLength == 0); - - streamEditor->oobEditInfo.nblEof = clonedNetBufferListChain; - - status = STATUS_SUCCESS; - goto Exit; - } - - if (streamEditor->oobEditInfo.nblTail != NULL) - { - NT_ASSERT(streamEditor->oobEditInfo.nblHead != NULL); - NT_ASSERT(streamEditor->oobEditInfo.nblTail->Next == NULL); - - streamEditor->oobEditInfo.nblTail->Next = clonedNetBufferListChain; - } - else - { - NT_ASSERT(streamEditor->oobEditInfo.nblHead == NULL); - - streamEditor->oobEditInfo.nblHead = clonedNetBufferListChain; - } - - streamEditor->oobEditInfo.nblTail = - TailOfNetBufferListChain(clonedNetBufferListChain); - - streamEditor->oobEditInfo.totalDataLength += streamData->dataLength; - - streamEditor->oobEditInfo.streamFlags = streamData->flags; - -Exit: - - return status; -} - -void -StreamOobEdit( - _Inout_ STREAM_EDITOR* streamEditor, - const FWPS_INCOMING_VALUES* inFixedValues, - const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - const FWPS_FILTER* filter, - _Inout_ FWPS_STREAM_DATA* streamData, - _Inout_ FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ) -/* ++ - - This function queues up incoming data and notifies the worker thread - to process them. The incoming data is blocked and removed from the - stream while data is pending. - - If the editor is shutdown (e.g. during driverUnload) as indicated - by OOB_EDIT_SHUT_DOWN state, it permits the indicated data inline after - flushing all pended data (to be carried out by the caller). - --- */ - -{ - NTSTATUS status; - - KLOCK_QUEUE_HANDLE editLockHandle; - - KeAcquireInStackQueuedSpinLock( - &streamEditor->oobEditInfo.editLock, - &editLockHandle - ); - - if (streamEditor->oobEditInfo.nblEof != NULL) - { - // - // A new flow arrives before we finish processing an earlier flow. Production - // code should create 1:1 between streamEditor and flow to handle this - // condition. See the "MSN Monitor sample" for how that can be implemented. - // - - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - - goto Exit; - } - - if (classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA) - { - NT_ASSERT(streamEditor->oobEditInfo.nblEof == NULL); - - streamEditor->oobEditInfo.noMoreData = TRUE; - } - - // - // Record needed flow information etc for data (re-)injection. - // - - streamEditor->oobEditInfo.calloutId = filter->action.calloutId; - streamEditor->oobEditInfo.flowId = inMetaValues->flowHandle; - streamEditor->oobEditInfo.layerId = inFixedValues->layerId; - - switch (streamEditor->oobEditInfo.editState) - { - case OOB_EDIT_PROCESSING: - { - if ((streamEditor->oobEditInfo.totalDataLength + streamData->dataLength) > - streamEditor->oobEditInfo.busyThreshold) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_DEFER; - classifyOut->actionType = FWP_ACTION_NONE; - - streamEditor->oobEditInfo.editState = OOB_EDIT_BUSY; - } - else - { - status = StreamOobQueueUpIncomingData( - streamEditor, - streamData - ); - - if (!NT_SUCCESS(status)) - { - streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; - - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - } - else - { - // - // State remains at OOB_EDIT_PROCESSING state. Since the worker thread - // is active there is no need to set the event (to wake it up) - // - - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = 0; - classifyOut->actionType = FWP_ACTION_BLOCK; - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - } - - break; - } - case OOB_EDIT_IDLE: - { - status = StreamOobQueueUpIncomingData( - streamEditor, - streamData - ); - - if (!NT_SUCCESS(status)) - { - streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; - - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - } - else - { - streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; - - // - // The worker thread is idle waiting for more work, now wake it up. - // - KeSetEvent( - &streamEditor->oobEditInfo.editEvent, - 0, - FALSE - ); - - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = 0; - classifyOut->actionType = FWP_ACTION_BLOCK; - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - break; - } - case OOB_EDIT_SHUT_DOWN: - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - ioPacket->countBytesEnforced = 0; - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - break; - } - case OOB_EDIT_ERROR: - { - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - break; - } - default: - NT_ASSERT(FALSE); - }; - -Exit: - - KeReleaseInStackQueuedSpinLock(&editLockHandle); -} - -NTSTATUS -StreamOobQueueUpOutgoingData( - _Inout_ STREAM_EDITOR* streamEditor, - _Inout_ NET_BUFFER_LIST* netBufferList, - BOOLEAN isClone, - size_t dataLength, - DWORD streamFlags, - _In_opt_ MDL* mdl - ) -/* ++ - - This function queues up processed data (either sections of the indicated - data or newly created data) such that they can be (re-)injected back to - the data stream during the following context. - - 1. Before FWP_ACTION_BLOCK is returned from the ClassifyFn, or - 2. After EOF is indicated. - - Under the conditions above, the incoming data (which we pend) and the - outgoing data (which we (re-)inject) can be synchronized properly). - --- */ -{ - NTSTATUS status = STATUS_SUCCESS; - KLOCK_QUEUE_HANDLE editLockHandle; - OUTGOING_STREAM_DATA* outgoingStreamData; - - outgoingStreamData = (OUTGOING_STREAM_DATA*) ExAllocatePoolWithTag( - NonPagedPool, - sizeof(OUTGOING_STREAM_DATA), - STREAM_EDITOR_OUTGOING_DATA_TAG - ); - - if (outgoingStreamData == NULL) - { - status = STATUS_NO_MEMORY; - return status; - } - - RtlZeroMemory(outgoingStreamData, sizeof(OUTGOING_STREAM_DATA)); - - outgoingStreamData->netBufferList = netBufferList; - outgoingStreamData->isClone = isClone; - outgoingStreamData->dataLength = dataLength; - outgoingStreamData->streamFlags = streamFlags; - outgoingStreamData->mdl = mdl; - - KeAcquireInStackQueuedSpinLock( - &streamEditor->oobEditInfo.editLock, - &editLockHandle - ); - - InsertTailList( - &streamEditor->oobEditInfo.outgoingDataQueue, - &outgoingStreamData->listEntry - ); - - KeReleaseInStackQueuedSpinLock(&editLockHandle); - - return status; -} - -NTSTATUS -StreamOobFlushOutgoingData( - _Inout_ STREAM_EDITOR* streamEditor - ) -{ - NTSTATUS status = STATUS_SUCCESS; - - KLOCK_QUEUE_HANDLE editLockHandle; - OUTGOING_STREAM_DATA* outgoingStreamData = NULL; - - for(;;) - { - KeAcquireInStackQueuedSpinLock( - &streamEditor->oobEditInfo.editLock, - &editLockHandle - ); - - if (!IsListEmpty(&streamEditor->oobEditInfo.outgoingDataQueue)) - { - LIST_ENTRY* listEntry = - RemoveHeadList(&streamEditor->oobEditInfo.outgoingDataQueue); - - outgoingStreamData = CONTAINING_RECORD( - listEntry, - OUTGOING_STREAM_DATA, - listEntry - ); - } - - KeReleaseInStackQueuedSpinLock(&editLockHandle); - - if (outgoingStreamData == NULL) - { - break; - } - - status = FwpsStreamInjectAsync( - gInjectionHandle, - NULL, - 0, - streamEditor->oobEditInfo.flowId, - streamEditor->oobEditInfo.calloutId, - streamEditor->oobEditInfo.layerId, - outgoingStreamData->streamFlags, - outgoingStreamData->netBufferList, - outgoingStreamData->dataLength, - outgoingStreamData->isClone ? StreamOobInjectCloneCompletionFn : - StreamOobInjectCompletionFn, - outgoingStreamData->mdl - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - ExFreePoolWithTag( - outgoingStreamData, - STREAM_EDITOR_OUTGOING_DATA_TAG - ); - - outgoingStreamData = NULL; - } - -Exit: - - if (outgoingStreamData != NULL) - { - NT_ASSERT(!NT_SUCCESS(status)); - - if (outgoingStreamData->isClone) - { - FwpsDiscardClonedStreamData( - outgoingStreamData->netBufferList, - 0, - FALSE - ); - } - else - { - FwpsFreeNetBufferList(outgoingStreamData->netBufferList); - - if (outgoingStreamData->mdl != NULL) - { - IoFreeMdl(outgoingStreamData->mdl); - - ExFreePoolWithTag( - outgoingStreamData->mdl->MappedSystemVa, - STREAM_EDITOR_MDL_DATA_TAG - ); - } - } - - ExFreePoolWithTag( - outgoingStreamData, - STREAM_EDITOR_OUTGOING_DATA_TAG - ); - } - - return status; -} - - -NTSTATUS -StreamOobReinjectData( - _Inout_ STREAM_EDITOR* streamEditor, - UINT32 streamFlags, - const void* data, - size_t length - ) -/* ++ - - This function injects a section of the original indicated data back - to the data stream. - - An MDL is allocated to describe the data section. - --- */ -{ - NTSTATUS status; - - void* dataCopy = NULL; - MDL* mdl = NULL; - NET_BUFFER_LIST* netBufferList = NULL; - - dataCopy = ExAllocatePoolWithTag( - NonPagedPool, - length, - STREAM_EDITOR_MDL_DATA_TAG - ); - - if (dataCopy == NULL) - { - status = STATUS_NO_MEMORY; - goto Exit; - } - - RtlCopyMemory(dataCopy, data, length); - - mdl = IoAllocateMdl( - dataCopy, - (ULONG)length, - FALSE, - FALSE, - NULL - ); - if (mdl == NULL) - { - status = STATUS_NO_MEMORY; - goto Exit; - } - - MmBuildMdlForNonPagedPool(mdl); - - status = FwpsAllocateNetBufferAndNetBufferList( - gNetBufferListPool, - 0, - 0, - mdl, - 0, - length, - &netBufferList - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && - !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); - - status = StreamOobQueueUpOutgoingData( - streamEditor, - netBufferList, - FALSE, - length, - streamFlags, - mdl - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - dataCopy = NULL; - mdl = NULL; - netBufferList = NULL; - -Exit: - - if (netBufferList != NULL) - { - FwpsFreeNetBufferList(netBufferList); - } - if (mdl != NULL) - { - IoFreeMdl(mdl); - } - if (dataCopy != NULL) - { - ExFreePoolWithTag( - dataCopy, - STREAM_EDITOR_MDL_DATA_TAG - ); - } - return status; -} - -NTSTATUS -StreamOobInjectReplacement( - _Inout_ STREAM_EDITOR* streamEditor, - UINT32 streamFlags, - _In_opt_ MDL* data, - size_t length - ) -/* ++ - - This function injects a section of replacement data (in place of data - removed from the stream) into the data stream. - - The MDL describes the replacement data is allocated during DriverEntry - and does not need to be freed during injection completion. - --- */ -{ - NTSTATUS status; - - NET_BUFFER_LIST* netBufferList = NULL; - - status = FwpsAllocateNetBufferAndNetBufferList( - gNetBufferListPool, - 0, - 0, - data, - 0, - length, - &netBufferList - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && - !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); - - status = StreamOobQueueUpOutgoingData( - streamEditor, - netBufferList, - FALSE, - length, - streamFlags, - NULL - ); - - if (!NT_SUCCESS(status)) - { - FwpsFreeNetBufferList(netBufferList); - goto Exit; - } - - netBufferList = NULL; - -Exit: - - if (netBufferList != NULL) - { - FwpsFreeNetBufferList(netBufferList); - } - - return status; -} - -NTSTATUS -StreamOobCopyDataToFlatBuffer( - _Inout_ STREAM_EDITOR* streamEditor, - _Inout_ NET_BUFFER_LIST* netBufferListChain, - size_t totalDataLength, - DWORD streamFlags - ) -/* ++ - - This function copies the data described by NBL(s) into a flat buffer. - - It reuses the FwpsCopyStreamDataToBuffer API (via StreamCopyDataForInspection) - by creating a FWPS_STREAM_DATA struct. - --- */ -{ - NTSTATUS status = STATUS_SUCCESS; - - FWPS_STREAM_DATA streamData = {0}; - - if (totalDataLength > 0) - { - streamData.netBufferListChain = netBufferListChain; - streamData.dataLength = totalDataLength; - streamData.flags = streamFlags; - - streamData.dataOffset.netBufferList = netBufferListChain; - streamData.dataOffset.netBuffer = - NET_BUFFER_LIST_FIRST_NB(streamData.dataOffset.netBufferList); - streamData.dataOffset.mdl = - NET_BUFFER_CURRENT_MDL(streamData.dataOffset.netBuffer); - streamData.dataOffset.mdlOffset = - NET_BUFFER_CURRENT_MDL_OFFSET(streamData.dataOffset.netBuffer); - - if (StreamCopyDataForInspection( - streamEditor, - &streamData - ) == FALSE) - { - status = STATUS_NO_MEMORY; - } - } - - return status; -} - -NTSTATUS -StreamOobEditData( - _Inout_ STREAM_EDITOR* streamEditor, - _Inout_ NET_BUFFER_LIST* netBufferListChain, - size_t totalDataLength, - DWORD streamFlags - ) -/* ++ - - This function first copies the stream data into a flat inspection buffer; - it then parses the buffer looking for the matching pattern. For - non-matching sections it re-injects the data back; for a match it skips - over and injects an replacement section. - - If a match can not be determined due to lack of data, it injects the - non-matching section back and moves the potential match to the beginning - of the inspection buffer. - - When an EOF is reached, it flushes all processed stream sections back - and re-injects the FIN back to end the stream. - --- */ -{ - NTSTATUS status = STATUS_SUCCESS; - - UINT i = 0; - BOOLEAN streamModified = FALSE; - BOOLEAN potentialMatch = FALSE; - - BYTE* dataStart; - - UINT findLength = (UINT) strlen(configStringToFind); - - status = StreamOobCopyDataToFlatBuffer( - streamEditor, - netBufferListChain, - totalDataLength, - streamFlags - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; - - for (; i < streamEditor->dataLength; ++i) - { - if (i + findLength <= streamEditor->dataLength) - { - if (RtlCompareMemory( - dataStart + i, - configStringToFind, - findLength - ) == findLength) - { - if (i != 0) - { - status = StreamOobReinjectData( - streamEditor, - streamFlags, - dataStart, - i - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - streamEditor->dataOffset += i; - streamEditor->dataLength -= i; - - i = 0; - } - - status = StreamOobInjectReplacement( - streamEditor, - streamFlags, - gStringToReplaceMdl, - strlen(configStringToReplace) - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - streamEditor->dataOffset += findLength; - streamEditor->dataLength -= findLength; - - streamModified = TRUE; - - if (streamEditor->dataLength > 0) - { - dataStart = (BYTE*)streamEditor->scratchBuffer + streamEditor->dataOffset; - - --i; - - continue; - } - else - { - streamEditor->dataOffset = 0; - } - } - } - else - { - if (streamEditor->oobEditInfo.noMoreData) - { - break; - } - - if (RtlCompareMemory( - dataStart + i, - configStringToFind, - streamEditor->dataLength - i - ) == streamEditor->dataLength - i) - { - potentialMatch = TRUE; // this is a partial find - - status = StreamOobReinjectData( - streamEditor, - streamFlags, - dataStart, - i - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - RtlMoveMemory( - (BYTE*)streamEditor->scratchBuffer, - dataStart + i, - streamEditor->dataLength - i - ); - - streamEditor->dataOffset = 0; - streamEditor->dataLength = streamEditor->dataLength - i; - - break; - } - } - } - - if (streamModified && streamEditor->dataLength > 0) - { - status = StreamOobReinjectData( - streamEditor, - streamFlags, - dataStart, - streamEditor->dataLength - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - streamEditor->dataOffset = 0; - streamEditor->dataLength = 0; - } - - if (!streamModified && !potentialMatch) - { - if (totalDataLength > 0) - { - NT_ASSERT(!(streamFlags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && - !(streamFlags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)); - - status = StreamOobQueueUpOutgoingData( - streamEditor, - netBufferListChain, - TRUE, - totalDataLength, - streamFlags, - NULL - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - netBufferListChain = NULL; - } - else if (streamEditor->dataLength > 0) - { - status = StreamOobReinjectData( - streamEditor, - streamFlags, - dataStart, - streamEditor->dataLength - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - } - - streamEditor->dataOffset = 0; - streamEditor->dataLength = 0; - } - - if (streamEditor->oobEditInfo.nblEof != NULL) - { - status = StreamOobFlushOutgoingData(streamEditor); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - status = FwpsStreamInjectAsync( - gInjectionHandle, - NULL, - 0, - streamEditor->oobEditInfo.flowId, - streamEditor->oobEditInfo.calloutId, - streamEditor->oobEditInfo.layerId, - streamFlags | (configInspectionOutbound ? FWPS_STREAM_FLAG_SEND_DISCONNECT : - FWPS_STREAM_FLAG_RECEIVE_DISCONNECT), - streamEditor->oobEditInfo.nblEof, - 0, - StreamOobInjectCompletionFn, - NULL - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - streamEditor->oobEditInfo.nblEof = NULL; - streamEditor->oobEditInfo.noMoreData = FALSE; - } - -Exit: - - if (netBufferListChain != NULL) - { - FwpsDiscardClonedStreamData( - netBufferListChain, - 0, - FALSE - ); - } - - if (streamEditor->oobEditInfo.nblEof != NULL) - { - FwpsDiscardClonedStreamData( - streamEditor->oobEditInfo.nblEof, - 0, - FALSE - ); - - streamEditor->oobEditInfo.nblEof = NULL; - } - - return status; -} - -_IRQL_requires_same_ -_Function_class_(KSTART_ROUTINE) -void -StreamOobEditWorker( - _In_ void* StartContext - ) -/* ++ - - This function waits for an event which gets signalled when there is data - waiting to be inspected. - - Once awaken, the worker thread edits the stream until all stream data is - processed (and then it waits for more work again). - - When requested to shutdown, it will finish the editing task and enters - "shutdown" state. - --- */ -{ - NTSTATUS status = STATUS_SUCCESS; - - NET_BUFFER_LIST* netBufferListChain = NULL; - size_t totalDataLength; - STREAM_EDITOR* streamEditor = (STREAM_EDITOR*)StartContext; - DWORD streamFlags; - - for(;;) - { - KLOCK_QUEUE_HANDLE editLockHandle; - - KeWaitForSingleObject( - &streamEditor->oobEditInfo.editEvent, - Executive, - KernelMode, - FALSE, - NULL - ); - - if (streamEditor->oobEditInfo.editState == OOB_EDIT_ERROR || - streamEditor->oobEditInfo.editState == OOB_EDIT_SHUT_DOWN) - { - break; - } - - KeAcquireInStackQueuedSpinLock( - &streamEditor->oobEditInfo.editLock, - &editLockHandle - ); - - NT_ASSERT(streamEditor->oobEditInfo.editState == OOB_EDIT_PROCESSING || - streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY); - - netBufferListChain = streamEditor->oobEditInfo.nblHead; - totalDataLength = streamEditor->oobEditInfo.totalDataLength; - streamFlags = streamEditor->oobEditInfo.streamFlags; - - streamEditor->oobEditInfo.nblHead = NULL; - streamEditor->oobEditInfo.nblTail = NULL; - streamEditor->oobEditInfo.totalDataLength = 0; - - KeReleaseInStackQueuedSpinLock(&editLockHandle); - - _Analysis_assume_(netBufferListChain != NULL); - status = StreamOobEditData( - streamEditor, - netBufferListChain, - totalDataLength, - streamFlags - ); - - if (!NT_SUCCESS(status)) - { - streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; - break; - } - - if (streamEditor->oobEditInfo.editState == OOB_EDIT_BUSY) - { - NTSTATUS streamContinueStatus; - streamEditor->oobEditInfo.editState = OOB_EDIT_PROCESSING; - - streamContinueStatus = FwpsStreamContinue( - streamEditor->oobEditInfo.flowId, - streamEditor->oobEditInfo.calloutId, - streamEditor->oobEditInfo.layerId, - streamEditor->oobEditInfo.streamFlags - ); - - if (!NT_SUCCESS(streamContinueStatus)) - { - streamEditor->oobEditInfo.editState = OOB_EDIT_ERROR; - break; - } - } - - KeAcquireInStackQueuedSpinLock( - &streamEditor->oobEditInfo.editLock, - &editLockHandle - ); - - if (streamEditor->oobEditInfo.nblHead == NULL) - { - if (!streamEditor->oobEditInfo.shuttingDown) - { - streamEditor->oobEditInfo.editState = OOB_EDIT_IDLE; - KeClearEvent(&streamEditor->oobEditInfo.editEvent); - } - else - { - streamEditor->oobEditInfo.editState = OOB_EDIT_SHUT_DOWN; - } - } - - KeReleaseInStackQueuedSpinLock(&editLockHandle); - } - - PsTerminateSystemThread(status); -} - -#if(NTDDI_VERSION >= NTDDI_WIN7) - -void -NTAPI -StreamOobEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const void* classifyContext, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ) - -#else - -void -NTAPI -StreamOobEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ) - -#endif /// (NTDDI_VERSION >= NTDDI_WIN7) -/* ++ - - This is the ClassifyFn function registered by the OOB stream edit callout. - - An OOB stream modification callout blocks all indicated data after cloning - them for processing by a kernel mode worker thread (or marshalling the data - to user mode for inspection); the resultant/edited data will then be put - back to the stream via the stream injection API. - - For such a callout, the processed data must be (re-)injected back to the - stream from within the ClassifyFn. - --- */ -{ - FWPS_STREAM_CALLOUT_IO_PACKET* ioPacket; - FWPS_STREAM_DATA* streamData; - - UINT findLength = (UINT) strlen(configStringToFind); - - ioPacket = (FWPS_STREAM_CALLOUT_IO_PACKET*)layerData; - NT_ASSERT(ioPacket != NULL); - - streamData = ioPacket->streamData; - NT_ASSERT(streamData != NULL); - -#if(NTDDI_VERSION >= NTDDI_WIN7) - UNREFERENCED_PARAMETER(classifyContext); -#endif /// (NTDDI_VERSION >= NTDDI_WIN7) - UNREFERENCED_PARAMETER(flowContext); - - RtlZeroMemory(classifyOut, sizeof(FWPS_CLASSIFY_OUT)); - - // - // Let go the traffic that the editor does not care about. - // - - if ((configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_RECEIVE)) || - (!configInspectionOutbound && (streamData->flags & FWPS_STREAM_FLAG_SEND))) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - goto Exit; - } - - // - // In this sample we don't edit TCP urgent data - // - - if ((streamData->flags & FWPS_STREAM_FLAG_SEND_EXPEDITED) || - (streamData->flags & FWPS_STREAM_FLAG_RECEIVE_EXPEDITED)) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NONE; - classifyOut->actionType = FWP_ACTION_PERMIT; - - if (filter->flags & FWPS_FILTER_FLAG_CLEAR_ACTION_RIGHT) - { - classifyOut->rights &= ~FWPS_RIGHT_ACTION_WRITE; - } - - goto Exit; - } - - if ((streamData->dataLength < findLength) && - !(classifyOut->flags & FWPS_CLASSIFY_OUT_FLAG_NO_MORE_DATA)) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_NEED_MORE_DATA; - ioPacket->countBytesRequired = findLength; - - classifyOut->actionType = FWP_ACTION_NONE; - goto Exit; - } - - StreamOobEdit( - &gStreamEditor, - inFixedValues, - inMetaValues, - filter, - streamData, - ioPacket, - classifyOut - ); - - if (classifyOut->actionType == FWP_ACTION_BLOCK || - classifyOut->actionType == FWP_ACTION_PERMIT) - { - if (!(streamData->flags & FWPS_STREAM_FLAG_SEND_DISCONNECT) && - !(streamData->flags & FWPS_STREAM_FLAG_RECEIVE_DISCONNECT)) - { - NTSTATUS status = StreamOobFlushOutgoingData( &gStreamEditor); - if (!NT_SUCCESS(status)) - { - ioPacket->streamAction = FWPS_STREAM_ACTION_DROP_CONNECTION; - classifyOut->actionType = FWP_ACTION_NONE; - } - } - } - -Exit: - - return; -} diff --git a/network/trans/stmedit/sys/oob_edit.h b/network/trans/stmedit/sys/oob_edit.h deleted file mode 100644 index e5a0e506..00000000 --- a/network/trans/stmedit/sys/oob_edit.h +++ /dev/null @@ -1,83 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved - -Abstract: - - Stream Edit Callout Driver Sample. - - This sample demonstrates Out-of-band (OOB) stream inspection/editing - via the WFP stream API. - -Environment: - - Kernel mode - ---*/ - -#ifndef _OOB_EDIT_H -#define _OOB_EDIT_H - -typedef enum OOB_EDIT_STATE_ -{ - OOB_EDIT_IDLE, - OOB_EDIT_PROCESSING, - OOB_EDIT_BUSY, - OOB_EDIT_SHUT_DOWN, - OOB_EDIT_ERROR -} OOB_EDIT_STATE; - -typedef struct OUTGOING_STREAM_DATA_ -{ - LIST_ENTRY listEntry; - - NET_BUFFER_LIST* netBufferList; - BOOLEAN isClone; - size_t dataLength; - DWORD streamFlags; - MDL* mdl; -} OUTGOING_STREAM_DATA; - -typedef struct STREAM_EDITOR_ STREAM_EDITOR; - -NTSTATUS -OobEditInit( - _Out_ STREAM_EDITOR* - ); - -void -OobEditShutdown( - _Out_ STREAM_EDITOR* streamEditor - ); - -#if(NTDDI_VERSION >= NTDDI_WIN7) - -void -NTAPI -StreamOobEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const void* classifyContext, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ); - -#else - -void -NTAPI -StreamOobEditClassify( - _In_ const FWPS_INCOMING_VALUES* inFixedValues, - _In_ const FWPS_INCOMING_METADATA_VALUES* inMetaValues, - _Inout_ void* layerData, - _In_ const FWPS_FILTER* filter, - _In_ UINT64 flowContext, - _Inout_ FWPS_CLASSIFY_OUT* classifyOut - ); - -#endif /// (NTDDI_VERSION >= NTDDI_WIN7) - - -#endif // _OOB_EDIT_H diff --git a/network/trans/stmedit/sys/stmedit.inf b/network/trans/stmedit/sys/stmedit.inf index b9ee15ea..fac93c26 100644 --- a/network/trans/stmedit/sys/stmedit.inf +++ b/network/trans/stmedit/sys/stmedit.inf @@ -9,9 +9,9 @@ Signature = "$Windows NT$" Class = WFPCALLOUTS ClassGuid = {57465043-616C-6C6F-7574-5F636C617373} - Provider = %ProviderString% + Provider = %Contoso% CatalogFile = StmEdit.cat - DriverVer = 11/24/2014,14.24.55.836 + DriverVer = 01/01/2015,1.0.09.15 [SourceDisksNames] 1 = %StmEditDisk%,,,"" @@ -50,17 +50,17 @@ AddReg = StmEdit.AddRegistry [StmEdit.AddRegistry] - HKR,"Parameters","EditInline",0x00010001,0 ; FLG_ADDREG_TYPE_DWORD - HKR,"Parameters","StringToFind",0x00000000,"rainy" ; FLG_ADDREG_TYPE_SZ - HKR,"Parameters","StringToReplace",0x00000000,"sunny" ; FLG_ADDREG_TYPE_SZ - HKR,"Parameters","InspectionPort",0x00010001,5001 ; FLG_ADDREG_TYPE_DWORD - HKR,"Parameters","InspectOutbound",0x00010001,0 ; FLG_ADDREG_TYPE_DWORD + HKR,"Parameters","MultipleCallouts", 0x00010001, 1 ; FLG_ADDREG_TYPE_DWORD + HKR,"Parameters","StringToFind", 0x00000000, "rainy" ; FLG_ADDREG_TYPE_SZ + HKR,"Parameters","InspectionRemotePort", 0x00010001, 0 ; FLG_ADDREG_TYPE_DWORD + HKR,"Parameters","InspectionLocalPort", 0x00010001, 8888 ; FLG_ADDREG_TYPE_DWORD + [StmEdit.DelRegistry] HKR,"Parameters",,, [Strings] - ProviderString = "TODO-Set-Provider" + Contoso = "Contoso Ltd." StmEditDisk = "Stream Edit Installation Disk" StmEditServiceDesc = "Stream Edit Callout Driver" StmEditServiceName = "StmEdit"
\ No newline at end of file diff --git a/network/trans/stmedit/sys/stmedit.vcxproj b/network/trans/stmedit/sys/stmedit.vcxproj index 6194a510..7617ad5d 100644 --- a/network/trans/stmedit/sys/stmedit.vcxproj +++ b/network/trans/stmedit/sys/stmedit.vcxproj @@ -19,12 +19,12 @@ </ProjectConfiguration> </ItemGroup> <PropertyGroup Label="Globals"> - <ProjectGuid>{00B26024-D8C5-40FD-A6C7-BA15FD324FD0}</ProjectGuid> + <ProjectGuid>{9CE912A5-6210-4EF8-B22D-611D13254D4C}</ProjectGuid> <RootNamespace>$(MSBuildProjectName)</RootNamespace> <KMDF_VERSION_MAJOR>1</KMDF_VERSION_MAJOR> <Configuration Condition="'$(Configuration)' == ''">Debug</Configuration> <Platform Condition="'$(Platform)' == ''">Win32</Platform> - <SampleGuid>{4DEE9F36-71EC-4057-8531-96C8B4624E91}</SampleGuid> + <SampleGuid>{8FEDC4BC-EFA4-4BF4-91B6-E33FA555EB15}</SampleGuid> </PropertyGroup> <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" /> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> @@ -36,12 +36,15 @@ <ConfigurationType>Driver</ConfigurationType> </PropertyGroup> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'"> - <TargetVersion>Windows10</TargetVersion> + <TargetVersion> + </TargetVersion> <UseDebugLibraries>True</UseDebugLibraries> - <DriverTargetPlatform>Universal</DriverTargetPlatform> + <DriverTargetPlatform> + </DriverTargetPlatform> <DriverType>KMDF</DriverType> <PlatformToolset>WindowsKernelModeDriver10.0</PlatformToolset> <ConfigurationType>Driver</ConfigurationType> + <SupportsPackaging>false</SupportsPackaging> </PropertyGroup> <PropertyGroup Label="Configuration" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'"> <TargetVersion>Windows10</TargetVersion> @@ -98,6 +101,10 @@ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> + <WppEnabled>true</WppEnabled> + <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction> + <WppModuleName>StmEdit</WppModuleName> + <WppScanConfigurationData>Trace.h</WppScanConfigurationData> </ClCompile> <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> @@ -117,6 +124,10 @@ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> + <WppEnabled>true</WppEnabled> + <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction> + <WppModuleName>StmEdit</WppModuleName> + <WppScanConfigurationData>Trace.h</WppScanConfigurationData> </ClCompile> <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> @@ -136,6 +147,10 @@ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> + <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction> + <WppEnabled>true</WppEnabled> + <WppModuleName>StmEdit</WppModuleName> + <WppScanConfigurationData>Trace.h</WppScanConfigurationData> </ClCompile> <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> @@ -155,6 +170,10 @@ <PreprocessorDefinitions>%(PreprocessorDefinitions);BINARY_COMPATIBLE=0;NT;UNICODE;_UNICODE;NDIS60;NDIS_SUPPORT_NDIS6;POOL_NX_OPTIN_AUTO</PreprocessorDefinitions> <ExceptionHandling> </ExceptionHandling> + <WppEnabled>true</WppEnabled> + <WppTraceFunction>DoTraceLevelMessage(LEVEL,FLAGS,MSG,...)</WppTraceFunction> + <WppModuleName>StmEdit</WppModuleName> + <WppScanConfigurationData>Trace.h</WppScanConfigurationData> </ClCompile> <Midl> <AdditionalIncludeDirectories>%(AdditionalIncludeDirectories);$(DDK_INC_PATH)</AdditionalIncludeDirectories> @@ -165,9 +184,10 @@ </Link> </ItemDefinitionGroup> <ItemGroup> - <ClCompile Include="inline_edit.c" /> - <ClCompile Include="oob_edit.c" /> - <ClCompile Include="stream_callout.c" /> + <ClCompile Include="InlineEdit.c" /> + <ClCompile Include="LwQueue.c" /> + <ClCompile Include="OobEdit.c" /> + <ClCompile Include="StreamEdit.c" /> </ItemGroup> <ItemGroup> <Inf Exclude="@(Inf)" Include="*.inf" /> diff --git a/network/trans/stmedit/sys/stmedit.vcxproj.Filters b/network/trans/stmedit/sys/stmedit.vcxproj.Filters index 671a946a..9a0c9d49 100644 --- a/network/trans/stmedit/sys/stmedit.vcxproj.Filters +++ b/network/trans/stmedit/sys/stmedit.vcxproj.Filters @@ -3,30 +3,55 @@ <ItemGroup> <Filter Include="Source Files"> <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx;*</Extensions> - <UniqueIdentifier>{4EB0EC22-2845-4C9E-B03A-227073F198E7}</UniqueIdentifier> + <UniqueIdentifier>{B982E3FA-FE5E-446B-BF39-65C533B117A9}</UniqueIdentifier> </Filter> <Filter Include="Header Files"> <Extensions>h;hpp;hxx;hm;inl;inc;xsd</Extensions> - <UniqueIdentifier>{3953E48D-12FA-4B3E-ABA3-BEE2AFF7C284}</UniqueIdentifier> + <UniqueIdentifier>{92648371-83D5-4CCE-AA54-9B689B706227}</UniqueIdentifier> </Filter> <Filter Include="Resource Files"> <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms;man;xml</Extensions> - <UniqueIdentifier>{D5A362D5-FA75-4E10-9D64-22B6CCB57052}</UniqueIdentifier> + <UniqueIdentifier>{163FDC8F-1EFF-4545-99A5-B189C0A6F18A}</UniqueIdentifier> </Filter> <Filter Include="Driver Files"> <Extensions>inf;inv;inx;mof;mc;</Extensions> - <UniqueIdentifier>{9B77DAC7-BF0A-4BAD-B6E0-C663693F98BB}</UniqueIdentifier> + <UniqueIdentifier>{E7E65CD5-C0F5-4962-AAB1-B9A9BA383DD9}</UniqueIdentifier> </Filter> </ItemGroup> <ItemGroup> - <ClCompile Include="inline_edit.c"> + <ClCompile Include="InlineEdit.c"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="oob_edit.c"> + <ClCompile Include="LwQueue.c"> <Filter>Source Files</Filter> </ClCompile> - <ClCompile Include="stream_callout.c"> + <ClCompile Include="OobEdit.c"> <Filter>Source Files</Filter> </ClCompile> + <ClCompile Include="StreamEdit.c"> + <Filter>Source Files</Filter> + </ClCompile> + </ItemGroup> + <ItemGroup> + <ClInclude Include="LwQueue.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="StreamEdit.h" /> + <ClInclude Include="Trace.h" /> + <ClInclude Include="LwQueue.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="StreamEdit.h" /> + <ClInclude Include="Trace.h" /> + <ClInclude Include="LwQueue.h"> + <Filter>Header Files</Filter> + </ClInclude> + <ClInclude Include="StreamEdit.h" /> + <ClInclude Include="Trace.h" /> + </ItemGroup> + <ItemGroup> + <Inf Include="*.inf"> + <Filter>Driver Files</Filter> + </Inf> </ItemGroup> </Project>
\ No newline at end of file diff --git a/network/trans/stmedit/sys/stream_callout.c b/network/trans/stmedit/sys/stream_callout.c deleted file mode 100644 index 7816c46d..00000000 --- a/network/trans/stmedit/sys/stream_callout.c +++ /dev/null @@ -1,772 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved - -Abstract: - - Stream Edit Callout Driver Sample. - - This sample demonstrates finding and replacing a string pattern from a - live TCP stream via the WFP stream API. - - The driver can function in one of the two modes -- - - o Inline Editing where all modification is carried out within the - WFP ClassifyFn callout function. - - o Out-of-band (OOB) Editing where all modification is done by a - worker thread. (this is the default) - - The mode setting, along with other inspection parameters are configurable - via the following registry values - - HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\stmedit\Parameters - - o StringToFind (REG_SZ, default = "rainy") - o StringToReplace (REG_SZ, default = "sunny") - o InspectionPort (REG_DWORD, default = 5001) - o InspectOutbound (REG_DWORD, default = 0) - o EditInline (REG_DWORD, default = 0) - - The sample is IP version agnostic. It performs inspection on both IPv4 and - IPv6 data streams. - - Before experimenting with the sample, please be sure to add an exception for - the InspectionPort configured to the firewall. - -Environment: - - Kernel mode - ---*/ - -#include <ntifs.h> -#include <wdf.h> - -#pragma warning(push) -#pragma warning(disable:4201) // unnamed struct/union - -#include <fwpsk.h> - -#pragma warning(pop) - -#include <fwpmk.h> - -#include "inline_edit.h" -#include "oob_edit.h" -#include "stream_callout.h" - -#define INITGUID -#include <guiddef.h> - -// -// Configurable parameters -// - -USHORT configInspectionPort = 5001; -BOOLEAN configInspectionOutbound = FALSE; -BOOLEAN configEditInline = FALSE; - -CHAR configStringToFind[128] = "rainy"; -CHAR configStringToReplace[128] = "sunny"; - -// -// Callout driver keys -// - -// e6011cdc-440b-4a6f-8499-6fdb55fb1f92 -DEFINE_GUID( - STREAM_EDITOR_STREAM_CALLOUT_V4, - 0xe6011cdc, - 0x440b, - 0x4a6f, - 0x84, 0x99, 0x6f, 0xdb, 0x55, 0xfb, 0x1f, 0x92 -); -// c0bc07b4-aaf6-4242-a3dc-3ef341ffde5d -DEFINE_GUID( - STREAM_EDITOR_STREAM_CALLOUT_V6, - 0xc0bc07b4, - 0xaaf6, - 0x4242, - 0xa3, 0xdc, 0x3e, 0xf3, 0x41, 0xff, 0xde, 0x5d -); - -// -// Callout driver global variables -// - -MDL* gStringToReplaceMdl; - -STREAM_EDITOR gStreamEditor; - -HANDLE gEngineHandle; -UINT32 gCalloutIdV4; -UINT32 gCalloutIdV6; - -DEVICE_OBJECT* gWdmDevice; - -HANDLE gInjectionHandle; - -NDIS_GENERIC_OBJECT* gNdisGenericObj; -NDIS_HANDLE gNetBufferListPool; - -#define STREAM_EDITOR_NDIS_OBJ_TAG 'oneS' -#define STREAM_EDITOR_NBL_POOL_TAG 'pneS' -#define STREAM_EDITOR_FLAT_BUFFER_TAG 'bfeS' - - -DRIVER_INITIALIZE DriverEntry; -EVT_WDF_DRIVER_UNLOAD StreamEditEvtDriverUnload; - -NTSTATUS -StreamEditNotify( - FWPS_CALLOUT_NOTIFY_TYPE notifyType, - const GUID* filterKey, - const FWPS_FILTER* filter - ) -{ - UNREFERENCED_PARAMETER(notifyType); - UNREFERENCED_PARAMETER(filterKey); - UNREFERENCED_PARAMETER(filter); - - return STATUS_SUCCESS; -} - -NTSTATUS -RegisterCalloutForLayer( - const GUID* layerKey, - const GUID* calloutKey, - _Inout_ void* deviceObject, - _Out_ UINT32* calloutId - ) -/* ++ - - This function registers callouts and filters that intercept TCP - traffic at WFP FWPM_LAYER_STREAM_V4 or FWPM_LAYER_STREAM_V6 layer. - --- */ -{ - NTSTATUS status = STATUS_SUCCESS; - - FWPS_CALLOUT sCallout = {0}; - - FWPM_FILTER filter = {0}; - FWPM_FILTER_CONDITION filterConditions[1] = {0}; - - FWPM_CALLOUT mCallout = {0}; - FWPM_DISPLAY_DATA displayData = {0}; - - BOOLEAN calloutRegistered = FALSE; - - sCallout.calloutKey = *calloutKey; - sCallout.classifyFn = (configEditInline ? StreamInlineEditClassify : - StreamOobEditClassify); - sCallout.notifyFn = StreamEditNotify; - - status = FwpsCalloutRegister( - deviceObject, - &sCallout, - calloutId - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - calloutRegistered = TRUE; - - displayData.name = L"Stream Edit Callout"; - displayData.description = L"Callout that finds and replaces a token from a TCP stream"; - - mCallout.calloutKey = *calloutKey; - mCallout.displayData = displayData; - mCallout.applicableLayer = *layerKey; - status = FwpmCalloutAdd( - gEngineHandle, - &mCallout, - NULL, - NULL - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - filter.layerKey = *layerKey; - filter.displayData.name = L"Stream Edit Filter"; - filter.displayData.description = L"Filter that finds and replaces a token from a TCP stream"; - - filter.action.type = FWP_ACTION_CALLOUT_TERMINATING; - filter.action.calloutKey = *calloutKey; - filter.filterCondition = filterConditions; - filter.numFilterConditions = 1; - filter.subLayerKey = FWPM_SUBLAYER_UNIVERSAL; - filter.weight.type = FWP_EMPTY; // auto-weight. - - filterConditions[0].fieldKey = (configInspectionOutbound ? FWPM_CONDITION_IP_REMOTE_PORT : - FWPM_CONDITION_IP_LOCAL_PORT); - filterConditions[0].matchType = FWP_MATCH_EQUAL; - filterConditions[0].conditionValue.type = FWP_UINT16; - filterConditions[0].conditionValue.uint16 = configInspectionPort; - - status = FwpmFilterAdd( - gEngineHandle, - &filter, - NULL, - NULL); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - -Exit: - - if (!NT_SUCCESS(status)) - { - if (calloutRegistered) - { - FwpsCalloutUnregisterById(*calloutId); - } - } - - return status; -} - -NTSTATUS -StreamEditRegisterCallout( - const STREAM_EDITOR* streamEditor, - _Inout_ void* deviceObject - ) -/* ++ - - This function registers dynamic callouts and filters that intercept - TCP traffic at WFP FWPM_LAYER_STREAM_V4 and FWPM_LAYER_STREAM_V6 - layer. - - Callouts and filters will be removed during DriverUnload. - --- */ -{ - NTSTATUS status = STATUS_SUCCESS; - - BOOLEAN engineOpened = FALSE; - BOOLEAN inTransaction = FALSE; - - FWPM_SESSION session = {0}; - - UNREFERENCED_PARAMETER(streamEditor); - - session.flags = FWPM_SESSION_FLAG_DYNAMIC; - - status = FwpmEngineOpen( - NULL, - RPC_C_AUTHN_WINNT, - NULL, - &session, - &gEngineHandle - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - engineOpened = TRUE; - - status = FwpmTransactionBegin(gEngineHandle, 0); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - inTransaction = TRUE; - - status = RegisterCalloutForLayer( - &FWPM_LAYER_STREAM_V4, - &STREAM_EDITOR_STREAM_CALLOUT_V4, - deviceObject, - &gCalloutIdV4 - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - status = RegisterCalloutForLayer( - &FWPM_LAYER_STREAM_V6, - &STREAM_EDITOR_STREAM_CALLOUT_V6, - deviceObject, - &gCalloutIdV6 - ); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - status = FwpmTransactionCommit(gEngineHandle); - if (!NT_SUCCESS(status)) - { - goto Exit; - } - inTransaction = FALSE; - -Exit: - - if (!NT_SUCCESS(status)) - { - if (inTransaction) - { - NTSTATUS abortStatus; - abortStatus = FwpmTransactionAbort(gEngineHandle); - _Analysis_assume_(NT_SUCCESS(abortStatus)); - } - if (engineOpened) - { - FwpmEngineClose(gEngineHandle); - gEngineHandle = NULL; - } - } - - return status; -} - -void -StreamEditUnregisterCallout(void) -{ - FwpmEngineClose(gEngineHandle); - gEngineHandle = NULL; - - FwpsCalloutUnregisterById(gCalloutIdV6); - FwpsCalloutUnregisterById(gCalloutIdV4); -} - -_Function_class_(EVT_WDF_DRIVER_UNLOAD) -_IRQL_requires_same_ -_IRQL_requires_max_(PASSIVE_LEVEL) -void -StreamEditEvtDriverUnload( - _In_ WDFDRIVER driverObject - ) -{ - - UNREFERENCED_PARAMETER(driverObject); - - if (!configEditInline) - { - OobEditShutdown(&gStreamEditor); - } - - if (gStreamEditor.scratchBuffer != NULL) - { - ExFreePoolWithTag( - gStreamEditor.scratchBuffer, - STREAM_EDITOR_FLAT_BUFFER_TAG - ); - - } - - StreamEditUnregisterCallout(); - - FwpsInjectionHandleDestroy(gInjectionHandle); - - NdisFreeNetBufferListPool(gNetBufferListPool); - NdisFreeGenericObject(gNdisGenericObj); - - IoFreeMdl(gStringToReplaceMdl); -} - -NTSTATUS -StreamEditLoadConfig( - const WDFKEY key - ) -{ - NTSTATUS status = STATUS_SUCCESS; - DECLARE_CONST_UNICODE_STRING(stringToFindKey, L"StringToFind"); - DECLARE_CONST_UNICODE_STRING(stringToReplaceKey, L"StringToReplace"); - DECLARE_CONST_UNICODE_STRING(inspectionPortKey, L"InspectionPort"); - DECLARE_CONST_UNICODE_STRING(editInlineKey, L"EditInline"); - DECLARE_CONST_UNICODE_STRING(inspectOutboundKey, L"InspectOutbound"); - - UNICODE_STRING stringValue; - WCHAR buffer[128]; - USHORT requiredSize; - ULONG valueSize; - ULONG ulongValue; - - stringValue.Buffer = buffer; - stringValue.Length = 0; - stringValue.MaximumLength = sizeof(buffer) - sizeof(buffer[0]); - - if (NT_SUCCESS( - WdfRegistryQueryUnicodeString( - key, - &stringToFindKey, - &requiredSize, - &stringValue - ))) - { - stringValue.Buffer[stringValue.Length/sizeof(stringValue.Buffer[0])] = - UNICODE_NULL; - - status = RtlUnicodeToMultiByteN( - configStringToFind, - sizeof(configStringToFind) - 1, - &valueSize, - stringValue.Buffer, - (ULONG)requiredSize - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - configStringToFind[valueSize] = '\0'; - } - - if (NT_SUCCESS( - WdfRegistryQueryUnicodeString( - key, - &stringToReplaceKey, - &requiredSize, - &stringValue - ))) - { - status = RtlUnicodeToMultiByteN( - configStringToReplace, - sizeof(configStringToReplace) - 1, - &valueSize, - stringValue.Buffer, - (ULONG)requiredSize - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - configStringToReplace[valueSize] = '\0'; - } - - if (NT_SUCCESS( - WdfRegistryQueryULong( - key, - &inspectionPortKey, - &ulongValue - ))) - { - configInspectionPort = (USHORT) ulongValue; - } - - if (NT_SUCCESS( - WdfRegistryQueryULong( - key, - &editInlineKey, - &ulongValue - ))) - { - configEditInline = (ulongValue != 0); - } - - if (NT_SUCCESS( - WdfRegistryQueryULong( - key, - &inspectOutboundKey, - &ulongValue - ))) - { - configInspectionOutbound = (ulongValue != 0); - } - -Exit: - return status; -} - -NTSTATUS -StreamEditInitDriverObjects( - _Inout_ DRIVER_OBJECT* driverObject, - const UNICODE_STRING* registryPath, - _Out_ WDFDRIVER* pDriver, - _Out_ WDFDEVICE* pDevice - ) -{ - NTSTATUS status; - WDF_DRIVER_CONFIG config; - PWDFDEVICE_INIT pInit = NULL; - - WDF_DRIVER_CONFIG_INIT(&config, WDF_NO_EVENT_CALLBACK); - - config.DriverInitFlags |= WdfDriverInitNonPnpDriver; - config.EvtDriverUnload = StreamEditEvtDriverUnload; - - status = WdfDriverCreate( - driverObject, - registryPath, - WDF_NO_OBJECT_ATTRIBUTES, - &config, - pDriver - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - pInit = WdfControlDeviceInitAllocate(*pDriver, &SDDL_DEVOBJ_KERNEL_ONLY); - - if (!pInit) - { - status = STATUS_INSUFFICIENT_RESOURCES; - goto Exit; - } - - WdfDeviceInitSetCharacteristics(pInit, FILE_AUTOGENERATED_DEVICE_NAME, TRUE); - WdfDeviceInitSetDeviceType(pInit, FILE_DEVICE_NETWORK); - WdfDeviceInitSetCharacteristics(pInit, FILE_DEVICE_SECURE_OPEN, TRUE); - status = WdfDeviceCreate(&pInit, WDF_NO_OBJECT_ATTRIBUTES, pDevice); - - if (!NT_SUCCESS(status)) - { - WdfDeviceInitFree(pInit); - goto Exit; - } - - WdfControlFinishInitializing(*pDevice); - -Exit: - return status; -} - -NTSTATUS -DriverEntry( - DRIVER_OBJECT* driverObject, - UNICODE_STRING* registryPath - ) -{ - NTSTATUS status; - WDFDEVICE device; - WDFDRIVER driver; - WDFKEY configKey; - NET_BUFFER_LIST_POOL_PARAMETERS nblPoolParams = {0}; - - // Request NX Non-Paged Pool when available - ExInitializeDriverRuntime(DrvRtPoolNxOptIn); - - status = StreamEditInitDriverObjects( - driverObject, - registryPath, - &driver, - &device - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - status = WdfDriverOpenParametersRegistryKey( - driver, - KEY_READ, - WDF_NO_OBJECT_ATTRIBUTES, - &configKey - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - status = StreamEditLoadConfig(configKey); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - gStringToReplaceMdl = IoAllocateMdl( - configStringToReplace, - (ULONG) strlen(configStringToReplace), - FALSE, - FALSE, - NULL - ); - if (gStringToReplaceMdl == NULL) - { - status = STATUS_NO_MEMORY; - goto Exit; - } - - MmBuildMdlForNonPagedPool(gStringToReplaceMdl); - - gNdisGenericObj = NdisAllocateGenericObject( - driverObject, - STREAM_EDITOR_NDIS_OBJ_TAG, - 0 - ); - - if (gNdisGenericObj == NULL) - { - status = STATUS_NO_MEMORY; - goto Exit; - } - - nblPoolParams.Header.Type = NDIS_OBJECT_TYPE_DEFAULT; - nblPoolParams.Header.Revision = NET_BUFFER_LIST_POOL_PARAMETERS_REVISION_1; - nblPoolParams.Header.Size = sizeof(nblPoolParams); - - nblPoolParams.fAllocateNetBuffer = TRUE; - nblPoolParams.DataSize = 0; - - nblPoolParams.PoolTag = STREAM_EDITOR_NBL_POOL_TAG; - - gNetBufferListPool = NdisAllocateNetBufferListPool( - gNdisGenericObj, - &nblPoolParams - ); - - if (gNetBufferListPool == NULL) - { - status = STATUS_NO_MEMORY; - goto Exit; - } - - status = FwpsInjectionHandleCreate( - AF_UNSPEC, - FWPS_INJECTION_TYPE_STREAM, - &gInjectionHandle - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - gWdmDevice = WdfDeviceWdmGetDeviceObject(device); - - status = StreamEditRegisterCallout( - &gStreamEditor, - gWdmDevice - ); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - - if (configEditInline) - { - InlineEditInit(&gStreamEditor); - } - else - { - - status = OobEditInit(&gStreamEditor); - - if (!NT_SUCCESS(status)) - { - goto Exit; - } - } - -Exit: - - if (!NT_SUCCESS(status)) - { - if (gEngineHandle != NULL) - { - StreamEditUnregisterCallout(); - } - if (gInjectionHandle != NULL) - { - FwpsInjectionHandleDestroy(gInjectionHandle); - } - if (gNetBufferListPool != NULL) - { - NdisFreeNetBufferListPool(gNetBufferListPool); - } - if (gNdisGenericObj != NULL) - { - NdisFreeGenericObject(gNdisGenericObj); - } - if (gStringToReplaceMdl != NULL) - { - IoFreeMdl(gStringToReplaceMdl); - } - } - - return status; -} - -BOOLEAN -StreamCopyDataForInspection( - _Inout_ STREAM_EDITOR* streamEditor, - const FWPS_STREAM_DATA* streamData - ) -/* ++ - - This function copies stream data described by the FWPS_STREAM_DATA - structure into a flat buffer. - --- */ - -{ - SIZE_T bytesCopied; - - size_t existingDataLength = streamEditor->dataLength; - NT_ASSERT(streamEditor->dataOffset == 0); - if (streamEditor->bufferSize - existingDataLength < streamData->dataLength) - { - size_t newBufferSize = (streamData->dataLength + existingDataLength) * 2; - void* newBuffer = ExAllocatePoolWithTag( - NonPagedPool, - newBufferSize, - STREAM_EDITOR_FLAT_BUFFER_TAG - ); - - if (newBuffer != NULL) - { - if (existingDataLength > 0) - { - NT_ASSERT(streamEditor->scratchBuffer != NULL); - RtlCopyMemory( - newBuffer, - streamEditor->scratchBuffer, - existingDataLength - ); - } - } - - if (streamEditor->scratchBuffer != NULL) - { - ExFreePoolWithTag( - streamEditor->scratchBuffer, - STREAM_EDITOR_FLAT_BUFFER_TAG - ); - - streamEditor->scratchBuffer = NULL; - streamEditor->bufferSize = 0; - streamEditor->dataLength = 0; - } - - if (newBuffer != NULL) - { - streamEditor->scratchBuffer = newBuffer; - streamEditor->bufferSize = newBufferSize; - streamEditor->dataLength = existingDataLength; - } - else - { - return FALSE; - } - } - - FwpsCopyStreamDataToBuffer( - streamData, - (BYTE*)streamEditor->scratchBuffer + streamEditor->dataLength, - streamData->dataLength, - &bytesCopied - ); - - NT_ASSERT(bytesCopied == streamData->dataLength); - - streamEditor->dataLength += bytesCopied; - - return TRUE; -} diff --git a/network/trans/stmedit/sys/stream_callout.h b/network/trans/stmedit/sys/stream_callout.h deleted file mode 100644 index 7fff047f..00000000 --- a/network/trans/stmedit/sys/stream_callout.h +++ /dev/null @@ -1,79 +0,0 @@ -/*++ - -Copyright (c) Microsoft Corporation. All rights reserved - -Abstract: - - Stream Edit Callout Driver Sample. - - This sample demonstrates finding and replacing a string pattern from a - live TCP stream via the WFP stream API. - ---*/ - -#ifndef _STREAM_CALLOUT_H -#define _STREAM_CALLOUT_H - -extern MDL* gStringToReplaceMdl; -extern HANDLE gInjectionHandle; -extern NDIS_HANDLE gNetBufferListPool; -extern STREAM_EDITOR gStreamEditor; - -// -// Configurable parameters -// - -extern USHORT configInspectionPort; -extern BOOLEAN configInspectionOutbound; -extern BOOLEAN configEditInline; - -extern CHAR configStringToFind[]; -extern CHAR configStringToReplace[]; - -#pragma warning(push) -#pragma warning(disable:4201) // unnamed struct/union - -typedef struct STREAM_EDITOR_ -{ - BOOLEAN editInline; - - union - { - INLINE_EDIT_STATE inlineEditState; - struct - { - OOB_EDIT_STATE editState; - BOOLEAN shuttingDown; - - KSPIN_LOCK editLock; - NET_BUFFER_LIST* nblHead; - NET_BUFFER_LIST* nblTail; - size_t totalDataLength; - BOOLEAN noMoreData; - NET_BUFFER_LIST* nblEof; - size_t busyThreshold; - UINT64 flowId; - UINT32 calloutId; - UINT16 layerId; - DWORD streamFlags; - KEVENT editEvent; - LIST_ENTRY outgoingDataQueue; - } oobEditInfo; - }; - - void* scratchBuffer; - size_t bufferSize; - size_t dataOffset; - size_t dataLength; - -}STREAM_EDITOR; - -#pragma warning(pop) - -BOOLEAN -StreamCopyDataForInspection( - _Inout_ STREAM_EDITOR* streamEditor, - const FWPS_STREAM_DATA* streamData - ); - -#endif // _STREAM_CALLOUT_H |
