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
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;
}
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;
}
Exemple #5
0
NTSTATUS
ReinjectPendedPacket(
	IN PENDED_PACKET *packet,
	IN FLOW_DATA *flowData)
{
	NTSTATUS status;
	UINT32 flags;
	NET_BUFFER_LIST* netBufferList = NULL;
	FLOW_DATA *flowCtx;
	ULONG dataLength;

	if(packet->dataLength == 0 || packet->data == NULL)
	{
		return STATUS_UNSUCCESSFUL;
	}

	packet->mdl = IoAllocateMdl(
		packet->data,
		packet->dataLength,
		FALSE,
		FALSE,
		NULL);
	
	if (packet->mdl == NULL)
	{
		status = STATUS_NO_MEMORY;
		goto Exit;
	}

	MmBuildMdlForNonPagedPool(packet->mdl);
	
	status = FwpsAllocateNetBufferAndNetBufferList(
						gNetBufferListPool,
						0,
						0,
						packet->mdl,
						0,
						packet->dataLength,
						&netBufferList);

	if(!NT_SUCCESS(status))
	{
		goto Exit;
	}

	flags = packet->flags;
	dataLength = packet->dataLength;
	flowCtx = packet->flowContext;

#ifdef DEBUG
	debugPacket(packet);
	DbgPrintEx(
		DPFLTR_IHVNETWORK_ID,
		DPFLTR_ERROR_LEVEL,
		"\n localCtr=%d, remoteCtr=%d\n", flowCtx->localCounter, flowCtx->remoteCounter);
#endif

	// Keep correct sequence numbers
	// (Assume every reinjection is successful, otherwise synchronous injection is
	// needed for consistent sequence numbers implementation)
	if(flags & FWPS_STREAM_FLAG_SEND)
	{
		flowCtx->localCounter += dataLength;
	}
	else if(flags & FWPS_STREAM_FLAG_RECEIVE)
	{
		flowCtx->remoteCounter += dataLength;
	}
	else
	{
#ifdef DEBUG
		DbgBreakPoint();
#endif
	}

	status = FwpsStreamInjectAsync(
		gInjectionHandle,
		NULL,
		0,
		flowData->flowHandle,
		gStreamCalloutIdV4,
		FWPS_LAYER_STREAM_V4,
		flags, 
		netBufferList,
		packet->dataLength,
		StreamInjectCompletionFn,
		packet);

	if (!NT_SUCCESS(status))
	{
		goto Exit;
	}

	// Ownership transferred
	netBufferList = NULL;
	packet = NULL;

	

Exit:

	if (netBufferList != NULL)
	{
		FwpsFreeNetBufferList(netBufferList);
	}

	if (packet != NULL)
	{
		FreePendedPacket(packet);
	}

	return status;
}
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;
}