|
|
/*++
Copyright (c) 1997-2001 Microsoft Corporation
Module Name:
ah.c
Abstract:
This module contains the code to create/verify Authentication Headers.
Author:
Sanjay Anand (SanjayAn) 2-January-1997 ChunYe
Environment:
Kernel mode
Revision History:
--*/
#include "precomp.h"
#ifdef RUN_WPP
#include "ah.tmh"
#endif
//
// This array assumes one-to-one correspondence with the algoIds and
// their order in ipsec.h.
//
#ifndef _TEST_PERF
AUTH_ALGO auth_algorithms[] = { { ah_nullinit, ah_nullupdate, ah_nullfinish, MD5DIGESTLEN}, { ah_hmacmd5init, ah_hmacmd5update, ah_hmacmd5finish, MD5DIGESTLEN}, { ah_hmacshainit, ah_hmacshaupdate, ah_hmacshafinish, A_SHA_DIGEST_LEN}, }; #else
AUTH_ALGO auth_algorithms[] = { { ah_nullinit, ah_nullupdate, ah_nullfinish, MD5DIGESTLEN}, { ah_nullinit, ah_nullupdate, ah_nullfinish, MD5DIGESTLEN}, { ah_nullinit, ah_nullupdate, ah_nullfinish, A_SHA_DIGEST_LEN}, }; #endif
NTSTATUS IPSecCreateAH( IN PUCHAR pIPHeader, IN PVOID pData, IN PVOID IPContext, IN PSA_TABLE_ENTRY pSA, IN ULONG Index, OUT PVOID *ppNewData, OUT PVOID *ppSCContext, OUT PULONG pExtraBytes, IN ULONG HdrSpace, IN BOOLEAN fSrcRoute, IN BOOLEAN fCryptoOnly ) /*++
Routine Description:
Create the AH, given the packet. On the send side.
Arguments:
pIPHeader - points to start of IP header.
pData - points to the data after the IP header. PNDIS_BUFFER
pSA - Sec. Assoc. entry
ppNewData - the new MDL chain to be used by TCPIP
ppSCContext - send complete context used to clean up IPSEC headers
pExtraBytes - the header expansion caused by this IPSEC header
Return Value:
STATUS_SUCCESS Others: STATUS_INSUFFICIENT_RESOURCES STATUS_UNSUCCESSFUL (error in algo.)
--*/ { NTSTATUS status = STATUS_SUCCESS; PNDIS_BUFFER pAHBuffer; PNDIS_BUFFER pHdrBuf = NULL; PNDIS_BUFFER pOptBuf = NULL; AH UNALIGNED *pAH; IPHeader UNALIGNED * pIPH; ULONG hdrLen; PIPSEC_SEND_COMPLETE_CONTEXT pContext; PAUTH_ALGO pAlgo; ULONG ahLen; ULONG ipNext; IPHeader UNALIGNED * pIPH2; UCHAR pAHData[MAX_AH_OUTPUT_LEN]; ULONG totalBytes = 0; ULONG saveFlags = 0; ULONG Seq; USHORT IPLength; PNDIS_BUFFER pSaveDataLinkage = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); PNDIS_BUFFER pSaveOptLinkage = NULL; BOOLEAN fOuterAH = ((pSA->sa_Flags & FLAGS_SA_TUNNEL) && (((Index == 1) && !pSA->COMP_ALGO(0)) || (Index == 2))); BOOLEAN fTunnel = ((pSA->sa_Flags & FLAGS_SA_TUNNEL) && ((Index == 0) || ((Index == 1) && pSA->COMP_ALGO(0)))); BOOLEAN fMuteDest = fSrcRoute && !fTunnel; Interface * DestIF = (Interface *) IPContext;
IPSEC_DEBUG(LL_A, DBF_AH, ("Entering IPSecCreateAH"));
#if DBG
IPSEC_DEBUG(LL_A,DBF_MDL, ("Entering IPSecCreateAH")); IPSEC_PRINT_CONTEXT(*ppSCContext); IPSEC_PRINT_MDL(pData); #endif
ASSERT(pSA->sa_Operation[Index] == Auth);
if (pSA->INT_ALGO(Index) > NUM_AUTH_ALGOS) { return STATUS_INVALID_PARAMETER; } pAlgo = &(auth_algorithms[pSA->INT_ALGO(Index)]);
ahLen = sizeof(AH) + pSA->sa_TruncatedLen * sizeof(UCHAR);
//
// If ESP was done previously, then dont alloc the context since we
// can use the one alloced in ESP processing
//
if (*ppSCContext == NULL) { pContext = IPSecAllocateSendCompleteCtx(IPSEC_TAG_AH);
if (!pContext) { IPSEC_DEBUG(LL_A,DBF_AH, ("Failed to alloc. SendCtx")); return STATUS_INSUFFICIENT_RESOURCES; }
IPSEC_INCREMENT(g_ipsec.NumSends);
IPSecZeroMemory(pContext, sizeof(IPSEC_SEND_COMPLETE_CONTEXT));
#if DBG
RtlCopyMemory(pContext->Signature, "ISC1", 4); #endif
*ppSCContext = pContext; } else { //
// Piggybacking on ESP Context
//
pContext = *ppSCContext; saveFlags = pContext->Flags; }
//
// Get buffer for AH since no space reserved in the stack. Allocate enough for
// the full hash, but hack the len to only truncated length.
//
IPSecAllocateBuffer(&status, &pAHBuffer, (PUCHAR *)&pAH, ahLen+(pAlgo->OutputLen - pSA->sa_TruncatedLen), IPSEC_TAG_AH);
if (!NT_SUCCESS(status)) { IPSEC_DEBUG(LL_A,DBF_AH, ("Failed to alloc. AH MDL")); pContext->Flags = saveFlags; return status; }
NdisAdjustBufferLength(pAHBuffer, ahLen);
pIPH = (IPHeader UNALIGNED *)pIPHeader; hdrLen = (pIPH->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2;
if (fTunnel) { PNDIS_BUFFER pSrcOptBuf; PUCHAR pOpt; PUCHAR pSrcOpt; ULONG optLen = 0;
IPSEC_DEBUG(LL_A,DBF_AH, ("AH Tunnel mode..."));
//
// Allocate an MDL for the new cleartext IP header
//
IPSecAllocateBuffer(&status, &pHdrBuf, (PUCHAR *)&pIPH2, sizeof(IPHeader), IPSEC_TAG_AH);
if (!NT_SUCCESS(status)) { NTSTATUS ntstatus; IPSEC_DEBUG(LL_A,DBF_AH, ("Failed to alloc. PAD MDL")); IPSecFreeBuffer(&ntstatus, pAHBuffer); pContext->Flags = saveFlags; return status; }
*pExtraBytes += ahLen + sizeof(IPHeader);
//
// if we are going to fragment, and were tunneling, then, copy over the options, if present.
// Also, use the original IP header on the outside and the new fabricated on the inside.
// This is to make sure we free headers appropriately on the send completes.
//
//
//
// Now hookup the MDLs
//
pContext->Flags |= SCF_AH_TU; pContext->AHTuMdl = pAHBuffer; pContext->PrevTuMdl = (PNDIS_BUFFER)pData; pContext->OriTuMdl = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData);
NDIS_BUFFER_LINKAGE(pAHBuffer) = pHdrBuf;
if (hdrLen > sizeof(IPHeader)) { if (HdrSpace < *pExtraBytes) {
IPSEC_DEBUG(LL_A,DBF_AH, ("Going to frag."));
pSrcOptBuf = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); pSaveOptLinkage = NDIS_BUFFER_LINKAGE(pSrcOptBuf); IPSecQueryNdisBuf(pSrcOptBuf, &pSrcOpt, &optLen); IPSecAllocateBuffer(&status, &pOptBuf, (PUCHAR *)&pOpt, hdrLen - sizeof(IPHeader), IPSEC_TAG_AH);
if (!NT_SUCCESS(status)) { NTSTATUS ntstatus; IPSEC_DEBUG(LL_A,DBF_AH, ("Failed to alloc. PAD MDL")); NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData) = pSaveDataLinkage; IPSecFreeBuffer(&ntstatus, pAHBuffer); IPSecFreeBuffer(&ntstatus, pHdrBuf); pContext->Flags = saveFlags; return status; }
RtlCopyMemory(pOpt, pSrcOpt, hdrLen-sizeof(IPHeader)); pContext->OptMdl = pOptBuf;
IPSEC_DEBUG(LL_A,DBF_AH, ("Copying options. S: %p, D: %p",pSrcOptBuf, pOptBuf));
//
// replace the original Opt Mdl with ours.
//
NDIS_BUFFER_LINKAGE(pOptBuf) = NDIS_BUFFER_LINKAGE(pSrcOptBuf); NDIS_BUFFER_LINKAGE(pHdrBuf) = pOptBuf;
IPSEC_DEBUG(LL_A,DBF_AH, ("Options; pointed Hdrbuf: %p to pOptBuf: %p", pHdrBuf, pOptBuf)); *pExtraBytes += hdrLen-sizeof(IPHeader);
} else { IPSEC_DEBUG(LL_A,DBF_AH, ("Options; pointed Hdrbuf: %p to link(pData): %p", pHdrBuf, NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData)));
NDIS_BUFFER_LINKAGE(pHdrBuf) = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); } } else { IPSEC_DEBUG(LL_A,DBF_AH, ("No options; pointed Hdrbuf: %p to link(pData): %p", pHdrBuf, NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData)));
NDIS_BUFFER_LINKAGE(pHdrBuf) = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); }
NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData) = pAHBuffer;
//
// xsum the new IP header since we expect that to be the case
// at this stage in tpt mode.
//
RtlCopyMemory(pIPH2, pIPH, sizeof(IPHeader));
//
// no options in the outer header; reset the len.
//
pIPH->iph_verlen = IP_VERSION + (sizeof(IPHeader) >> 2);
//
// also reset the frag. params.
//
pIPH->iph_offset &= ~(IP_MF_FLAG | IP_OFFSET_MASK);
if (DestIF->if_dfencap == ClearDfEncap) { pIPH->iph_offset &= ~(IP_DF_FLAG | IP_OFFSET_MASK); }
ASSERT(pSA->sa_TunnelAddr);
//
// Tunnel starts here; replace dest addr to point to Tunnel end if specified
// else tunnel ends at final dest
//
pIPH->iph_dest = pSA->sa_TunnelAddr;
//
// The first pended packet on a gateway (proxy negotiating for two subnets)
// would come via the transmit path. Hence the source address would not be
// kosher. We need to replace the src address in that case also.
// We get this from the corresponding inbound SA's tunnel addr.
//
pIPH->iph_src = pSA->sa_SrcTunnelAddr;
pIPH->iph_id = (ushort) TCPIP_GEN_IPID(); pIPH->iph_xsum = 0; pIPH->iph_xsum = ~xsum(pIPH, sizeof(IPHeader));
//
// Set up headers so CreateHash works as in Tpt mode.
//
pIPHeader = (PUCHAR)pIPH; *ppNewData = (PVOID)pData; ipNext = ((UNALIGNED IPHeader *)pIPHeader)->iph_protocol; pAH->ah_next = (UCHAR)IP_IN_IP; } else { *pExtraBytes += ahLen;
if (hdrLen > sizeof(IPHeader)) { //
// Options present - chain AH after options
//
if (fOuterAH) { pContext->Flags |= SCF_AH_2; pContext->OriAHMdl2 = NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData)); pContext->PrevAHMdl2 = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); pAHBuffer->Next = pContext->OriAHMdl2; } else { pContext->Flags |= SCF_AH; pContext->OriAHMdl = NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData)); pContext->PrevMdl = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); pAHBuffer->Next = pContext->OriAHMdl; } NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData)) = pAHBuffer; } else { //
// Chain the AH buffer after IP header
//
if (fOuterAH) { pContext->Flags |= SCF_AH_2; pContext->OriAHMdl2 = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); pContext->PrevAHMdl2 = (PNDIS_BUFFER)pData; pAHBuffer->Next = pContext->OriAHMdl2; } else { pContext->Flags |= SCF_AH; pContext->OriAHMdl = NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData); pContext->PrevMdl = (PNDIS_BUFFER)pData; pAHBuffer->Next = pContext->OriAHMdl; } NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData) = pAHBuffer; } if (fOuterAH) { pContext->AHMdl2 = pAHBuffer; } else { pContext->AHMdl = pAHBuffer; }
pAH->ah_next = ((UNALIGNED IPHeader *)pIPHeader)->iph_protocol; }
//
// Initialize the other fields of the AH header
//
pAH->ah_len = (UCHAR)((pSA->sa_TruncatedLen + pSA->sa_ReplayLen) >> 2); pAH->ah_reserved = 0; pAH->ah_spi = HOST_TO_NET_LONG(pSA->sa_OtherSPIs[Index]); Seq = IPSEC_INCREMENT(pSA->sa_ReplaySendSeq[Index]); pAH->ah_replay = HOST_TO_NET_LONG(Seq);
//
// Update the IP total length to reflect the AH header
//
IPLength = NET_SHORT(pIPH->iph_length) + (USHORT)ahLen; if (fTunnel) { IPLength += sizeof(IPHeader); }
UpdateIPLength(pIPH, NET_SHORT(IPLength)); UpdateIPProtocol(pIPH, PROTOCOL_AH);
ADD_TO_LARGE_INTEGER( &pSA->sa_Stats.AuthenticatedBytesSent, NET_SHORT(pIPH->iph_length));
ADD_TO_LARGE_INTEGER( &g_ipsec.Statistics.uAuthenticatedBytesSent, NET_SHORT(pIPH->iph_length));
//
// Generate the Hash.
//
if (!fCryptoOnly) { status = IPSecGenerateHash( pIPHeader, (PVOID)pData, pSA, (PUCHAR)(pAH + 1), fMuteDest, FALSE, // not on recv path
pAlgo, Index); if (!NT_SUCCESS(status)) { NTSTATUS ntstatus; IPSEC_DEBUG(LL_A,DBF_AH, ("Failed to hash, pAH: %p", pAH)); NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData) = pSaveDataLinkage; if (pSaveOptLinkage) { NDIS_BUFFER_LINKAGE(NDIS_BUFFER_LINKAGE((PNDIS_BUFFER)pData)) = pSaveOptLinkage; } IPSecFreeBuffer(&ntstatus, pAHBuffer); if (pHdrBuf) { IPSecFreeBuffer(&ntstatus, pHdrBuf); } if (pOptBuf) { IPSecFreeBuffer(&ntstatus, pOptBuf); } pContext->Flags = saveFlags; *ppNewData = NULL; return status; } } else { //
// Zero out the hash.
//
IPSecZeroMemory((PUCHAR)(pAH + 1), pSA->sa_TruncatedLen); }
//
// Bump up the bytes transformed count.
//
ADD_TO_LARGE_INTEGER( &pSA->sa_TotalBytesTransformed, NET_SHORT(pIPH->iph_length));
//
// Return modified packet.
//
IPSEC_DEBUG(LL_A,DBF_AH, ("Exiting IPSecCreateAH, ahLen: %lx, status: %lx", ahLen, status));
#if DBG
IPSEC_DEBUG(LL_A,DBF_MDL, ("Exiting IPSecCreateAH")); IPSEC_PRINT_CONTEXT(*ppSCContext); if (*ppNewData) { IPSEC_PRINT_MDL(*ppNewData); } else { IPSEC_PRINT_MDL(pData); } #endif
return STATUS_SUCCESS; }
NTSTATUS IPSecVerifyAH( IN PUCHAR *pIPHeader, IN PVOID pData, IN PSA_TABLE_ENTRY pSA, IN ULONG Index, OUT PULONG pExtraBytes, IN BOOLEAN fSrcRoute, IN BOOLEAN fCryptoDone, IN BOOLEAN fFastRcv ) /*++
Routine Description:
Verify the AH, given the packet. If AH kosher, strips off the AH from pData.
Arguments:
pIPHeader - points to start of IP header.
pData - points to the data after the IP header.
pSA - Sec. Assoc. entry
pExtraBytes - out param to inform IP on recv path how many bytes IPSEC took off.
Return Value:
STATUS_SUCCESS Others: STATUS_UNSUCCESSFUL (packet not kosher - bad AH) STATUS_INSUFFICIENT_RESOURCES
--*/ { NTSTATUS status = STATUS_SUCCESS; PUCHAR pPyld; ULONG Len; LONG ahLen; LONG totalLen; UCHAR Buf[MAX_AH_OUTPUT_LEN]; PUCHAR pAHData = Buf; IPHeader UNALIGNED *pIPH = (IPHeader UNALIGNED *)*pIPHeader; ULONG extraBytes = 0; ULONG hdrLen; PAUTH_ALGO pAlgo; USHORT FilterFlags; BOOLEAN fTunnel = ((pSA->sa_Flags & FLAGS_SA_TUNNEL) && ((Index == 0) || ((Index == 1) && (pSA->sa_Operation[0] == Compress))));
IPSEC_DEBUG(LL_A,DBF_AH, ("Entering IPSecVerifyAH"));
ASSERT(pSA->sa_Operation[Index] == Auth);
if (pSA->INT_ALGO(Index) > NUM_AUTH_ALGOS) { return STATUS_INVALID_PARAMETER; }
hdrLen = (pIPH->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2;
pAlgo = &(auth_algorithms[pSA->INT_ALGO(Index)]);
ahLen = sizeof(AH) + pSA->sa_TruncatedLen * sizeof(UCHAR);
IPSEC_GET_TOTAL_LEN_RCV_BUF(pData, &totalLen);
//
// Do we have enough in the buffer?
//
if (totalLen < ahLen) { return STATUS_INVALID_PARAMETER; }
//
// Compare the hash with the AH from packet
// First buffer has the AH
//
IPSecQueryRcvBuf(pData, &pPyld, &Len);
//
// Size OK?
//
if (((UNALIGNED AH *)pPyld)->ah_len != (UCHAR)((pSA->sa_TruncatedLen + pSA->sa_ReplayLen) >> 2)) { IPSEC_DEBUG(LL_A,DBF_AH, ("Failed size check: in: %x, need: %x", ((UNALIGNED AH *)pPyld)->ah_len, (UCHAR)((pSA->sa_TruncatedLen + pSA->sa_ReplayLen) >> 2))); return STATUS_INVALID_PARAMETER; }
//
// Generate the Hash
//
if (!fCryptoDone) { status = IPSecGenerateHash( *pIPHeader, pData, pSA, pAHData, fSrcRoute, TRUE, pAlgo, Index); // on recv path
if (!NT_SUCCESS(status)) { IPSEC_DEBUG(LL_A,DBF_AH, ("Failed to hash, pData: %p", pData)); return status; }
if (!IPSecEqualMemory( pAHData, pPyld + sizeof(AH), pSA->sa_TruncatedLen)) {
IPSecBufferEvent( pIPH->iph_src, EVENT_IPSEC_AUTH_FAILURE, 1, TRUE);
IPSEC_DEBUG(LL_A,DBF_AH, ("Failed to compare, pPyld: %p, pAHData: %p", pPyld, pAHData)); IPSEC_DEBUG(LL_A,DBF_GENHASH, ("AHData: %lx-%lx-%lx", *(ULONG *)&(pAHData)[0], *(ULONG *)&(pAHData)[4], *(ULONG *)&(pAHData)[8])); IPSEC_DEBUG(LL_A,DBF_GENHASH, ("PyldHash: %lx-%lx-%lx", *(ULONG *)&((UCHAR *)(pPyld + sizeof(AH)))[0], *(ULONG *)&((UCHAR *)(pPyld + sizeof(AH)))[4], *(ULONG *)&((UCHAR *)(pPyld + sizeof(AH)))[8])); IPSEC_INC_STATISTIC(dwNumPacketsNotAuthenticated);
return IPSEC_INVALID_AH; } }
ADD_TO_LARGE_INTEGER( &pSA->sa_Stats.AuthenticatedBytesReceived, NET_SHORT(pIPH->iph_length));
ADD_TO_LARGE_INTEGER( &g_ipsec.Statistics.uAuthenticatedBytesReceived, NET_SHORT(pIPH->iph_length));
//
// Check the replay window
//
status=IPSecChkReplayWindow( NET_TO_HOST_LONG(((UNALIGNED AH *)pPyld)->ah_replay), pSA, Index); if (!NT_SUCCESS(status)) { IPSEC_DEBUG(LL_A,DBF_AH, ("Replay check failed, pPyld: %p, pAHData: %p", pPyld, pAHData)); IPSEC_INC_STATISTIC(dwNumPacketsWithReplayDetection); return status; }
IPSEC_DEBUG(LL_A,DBF_AH, ("IP Len: %lx", pIPH->iph_length));
pIPH->iph_length = NET_SHORT(NET_SHORT(pIPH->iph_length) - (USHORT)ahLen);
IPSEC_DEBUG(LL_A,DBF_AH, ("IP Len: %lx", pIPH->iph_length));
//
// Restore the protocol from AH header
//
pIPH->iph_protocol = ((UNALIGNED AH *)pPyld)->ah_next;
IPSEC_DEBUG(LL_A,DBF_AH, ("Matched!! Restored protocol %x", pIPH->iph_protocol));
//
// Remove the AH from the packet
//
IPSEC_SET_OFFSET_IN_BUFFER(pData, ahLen);
//
// Move the IP header forward for filter/firewall hook, fast path only.
//
if (fFastRcv) { IPSecMoveMemory(((PUCHAR)pIPH) + ahLen, (PUCHAR)pIPH, hdrLen); *pIPHeader=(PUCHAR)pIPH+ahLen; pIPH = (IPHeader UNALIGNED *)*pIPHeader; }
extraBytes += ahLen;
//
// Bump up the bytes transformed count.
//
ADD_TO_LARGE_INTEGER( &pSA->sa_TotalBytesTransformed, NET_SHORT(pIPH->iph_length));
if (fTunnel) { if (pIPH->iph_protocol != IP_IN_IP) { IPSEC_DEBUG(LL_A,DBF_AH, ("BAD protocol in IP: %x", pIPH->iph_protocol)); return STATUS_INVALID_PARAMETER; } }
*pExtraBytes += extraBytes;
IPSEC_DEBUG(LL_A,DBF_AH, ("Exiting IPSecVerifyAH"));
return status; }
NTSTATUS IPSecGenerateHash( IN PUCHAR pIPHeader, IN PVOID pData, IN PSA_TABLE_ENTRY pSA, IN PUCHAR pAHData, IN BOOLEAN fMuteDest, IN BOOLEAN fIncoming, IN PAUTH_ALGO pAlgo, IN ULONG Index ) /*++
Routine Description:
Arguments:
pIPHeader - points to start of IP header.
pData - points to the entire IP datagram, starting at the IP Header
pSA - Sec. Assoc. entry
pAHData - buffer to contain the generated hash
fIncoming - TRUE if on recv path.
pAlgo - the auth_algo being used
Return Value:
STATUS_SUCCESS Others: STATUS_UNSUCCESSFUL (packet not kosher - bad AH) STATUS_INSUFFICIENT_RESOURCES
--*/ { ULONG numBytesPayload; ULONG i; PUCHAR pPayload; IPHeader UNALIGNED *pIPH = (UNALIGNED IPHeader *)pIPHeader; PUCHAR pOptions; PNDIS_BUFFER pBuf = (PNDIS_BUFFER)pData; ULONG hdrLen; ULONG ahLen; NTSTATUS status; ALGO_STATE State = {0}; BOOLEAN fTunnel = ( (pSA->sa_Flags & FLAGS_SA_TUNNEL) && ((Index == 0) || ((Index == 1) && (pSA->sa_Operation[0] == Compress))));
//
// These are saved since they can change enroute
//
//
// Scratch array used for AH calculation
//
UCHAR zero[MAX_IP_OPTION_SIZE]; UCHAR savetos; // Type of service.
USHORT saveoffset; // Flags and fragment offset.
UCHAR savettl; // Time to live.
USHORT savexsum; // Header checksum.
IPAddr savedest; // Dest address.
IPSEC_DEBUG(LL_A,DBF_AH, ("Entering IPSecGenerateHash"));
ahLen = sizeof(AH) + pSA->sa_TruncatedLen * sizeof(UCHAR);
State.as_sa = pSA; IPSecZeroMemory(zero, sizeof(zero));
status = pAlgo->init(&State, Index);
if (!NT_SUCCESS(status)) { IPSEC_DEBUG(LL_A,DBF_AH, ("init failed: %lx", status)); }
//
// Save, then zero out fields that can change enroute
//
savetos = pIPH->iph_tos; saveoffset = pIPH->iph_offset; savettl = pIPH->iph_ttl; savexsum = pIPH->iph_xsum;
pIPH->iph_tos = 0; pIPH->iph_offset = 0; pIPH->iph_ttl = 0; pIPH->iph_xsum = 0;
//
// Mute dest address as well if source routing
//
if (fMuteDest) { savedest = pIPH->iph_dest; pIPH->iph_dest = 0; }
//
// Call MD5 to create the header hash
//
pAlgo->update(&State, pIPHeader, sizeof(IPHeader));
#if DBG
if (fIncoming) { IPSEC_DEBUG(LL_A,DBF_GENHASH, ("IPHeader to Hash: %lx-%lx-%lx-%lx-%lx", *(ULONG *)&(pIPHeader)[0], *(ULONG *)&(pIPHeader)[4], *(ULONG *)&(pIPHeader)[8], *(ULONG *)&(pIPHeader)[12], *(ULONG *)&(pIPHeader)[16])); } #endif
//
// Restore the zeroed fields
//
pIPH->iph_tos = savetos; pIPH->iph_offset = saveoffset; pIPH->iph_ttl = savettl; pIPH->iph_xsum = savexsum;
//
// Restore dest address as well for source routing
//
if (fMuteDest) { pIPH->iph_dest = savedest; }
//
// Now, do the options if they exist
//
hdrLen = (pIPH->iph_verlen & (UCHAR)~IP_VER_FLAG) << 2;
if (hdrLen > sizeof(IPHeader)) { UCHAR cLength; ULONG uIndex = 0; ULONG uOptLen = hdrLen - sizeof(IPHeader);
ASSERT(!fTunnel);
if (fIncoming) { pOptions = (PUCHAR)(pIPH + 1); } else { //
// Options are in second MDL... on send side
//
pBuf = NDIS_BUFFER_LINKAGE(pBuf); IPSecQueryNdisBuf(pBuf, &pOptions, &uOptLen); }
IPSEC_DEBUG(LL_A,DBF_AH, ("Got options: %p", pOptions));
//
// Some options may need to be zeroed out...
//
while (uIndex < uOptLen) { switch (*pOptions) { case IP_OPT_EOL: pAlgo->update(&State, zero, 1); uIndex = uOptLen; break;
//
// Zeroed for AH calculation
//
case IP_OPT_NOP: pAlgo->update(&State, zero, 1); uIndex++; pOptions++; break;
case IP_OPT_LSRR: case IP_OPT_SSRR: case IP_OPT_RR: case IP_OPT_TS: cLength = pOptions[IP_OPT_LENGTH]; pAlgo->update(&State, zero, cLength); uIndex += cLength; pOptions += cLength; break;
//
// Assumed invariant; used for AH calc
//
case IP_OPT_ROUTER_ALERT: case IP_OPT_SECURITY: default: cLength = pOptions[IP_OPT_LENGTH]; pAlgo->update(&State, pOptions, cLength); uIndex += cLength; pOptions += cLength; break; } } }
//
// Go over the remaining payload, creating the hash
//
// NOTE: We differentiate between the send and recv since the
// buffer formats are different
//
if (fIncoming) { IPRcvBuf *pRcvBuf = (IPRcvBuf *)pData; ULONG Len; LONG remainLen;
UCHAR UNALIGNED *pPyld;
//
// First buffer shd be the AH itself
//
IPSecQueryRcvBuf(pRcvBuf, &pPyld, &Len);
//
// Do the first portion of the header.
//
pAlgo->update(&State, pPyld, sizeof(AH));
#if DBG
if (fIncoming) { IPSEC_DEBUG(LL_A,DBF_GENHASH, ("AHHeader to Hash: %lx-%lx-%lx", *(ULONG *)&(pPyld)[0], *(ULONG *)&(pPyld)[4], *(ULONG *)&(pPyld)[8])); } #endif
//
// The authentication data should be considered as 0.
// In our case, the data length is fixed at pSA->sa_TruncatedLen bytes
//
pAlgo->update(&State, zero, pSA->sa_TruncatedLen);
//
// Jump over the remaining AH: need to take care of situations
// where ICV is chained (Raid 146275).
//
if (((LONG)Len - (LONG)ahLen) >= 0) { pPyld += ahLen; IPSEC_DEBUG(LL_A,DBF_AH, ("Jumped over IPSEC res: %p, len: %lx", pPyld, Len));
//
// Tpt header is right after AH
//
pAlgo->update(&State, pPyld, Len - ahLen); } else { //
// Need to jump over ICV if it expands over multiple buffers
//
remainLen = pSA->sa_TruncatedLen - (Len - sizeof(AH)); IPSEC_DEBUG(LL_A,DBF_AH, ("Jumped over IPSEC res: %p, remainlen: %lx", pPyld, remainLen)); while (remainLen > 0 && (pRcvBuf = IPSEC_BUFFER_LINKAGE(pRcvBuf))) { IPSecQueryRcvBuf(pRcvBuf, &pPyld, &Len); remainLen -= Len; }
//
// Do the possible partial data after AH
//
if (remainLen < 0 && pRcvBuf) { pPyld += Len + remainLen; pAlgo->update(&State, pPyld, -remainLen); } }
//
// Now do the remaining chain
//
while (pRcvBuf = IPSEC_BUFFER_LINKAGE(pRcvBuf)) { IPSecQueryRcvBuf(pRcvBuf, &pPyld, &Len); pAlgo->update(&State, pPyld, Len); } } else { UCHAR UNALIGNED *pPyld; ULONG Len;
//
// Second (or third if options present) buffer shd be the AH itself
//
pBuf = NDIS_BUFFER_LINKAGE(pBuf); IPSecQueryNdisBuf(pBuf, &pPyld, &Len);
//
// Do the first portion of the header.
//
pAlgo->update(&State, pPyld, sizeof(AH));
//
// The authentication data should be considered as 0.
// In our case, the data length is fixed at pSA->sa_TruncatedLen bytes
//
pAlgo->update(&State, zero, pSA->sa_TruncatedLen);
//
// Skip over the remaining AH section
//
pPyld += ahLen;
IPSEC_DEBUG(LL_A,DBF_AH, ("Jumped over IPSEC Len: %lx, hdrlen: %lx", Len, hdrLen));
pAlgo->update(&State, pPyld, Len - ahLen);
//
// Now do the remaining chain
//
while (pBuf = NDIS_BUFFER_LINKAGE(pBuf)) { IPSecQueryNdisBuf(pBuf, &pPyld, &Len); pAlgo->update(&State, pPyld, Len); } }
pAlgo->finish(&State, pAHData, Index);
//
// Copy out the hash - get the truncated hash out, then zero out the rest
//
TRUNCATE(pAHData, pAHData, pSA->sa_TruncatedLen, MD5DIGESTLEN);
IPSEC_DEBUG(LL_A,DBF_AH, ("Exiting IPSecGenerateMD5"));
return STATUS_SUCCESS; }
|