|
|
// -*- mode: C++; tab-width: 4; indent-tabs-mode: nil -*- (for GNU Emacs)
//
// Copyright (c) 1985-2000 Microsoft Corporation
//
// This file is part of the Microsoft Research IPv6 Network Protocol Stack.
// You should have received a copy of the Microsoft End-User License Agreement
// for this software along with this release; see the file "license.txt".
// If not, please see http://www.research.microsoft.com/msripv6/license.htm,
// or write to Microsoft Research, One Microsoft Way, Redmond, WA 98052-6399.
//
// Abstract:
//
// IP security routines for Internet Protocol Version 6.
//
#include "oscfg.h"
#include "ndis.h"
#include "ip6imp.h"
#include "ip6def.h"
#include "ipsec.h"
#include "security.h"
#include "alloca.h"
//
// Global Variables.
//
KSPIN_LOCK IPSecLock; SecurityPolicy *SecurityPolicyList; SecurityAssociation *SecurityAssociationList = NULL; ulong SecurityStateValidationCounter; uchar Zero[max(MAXUCHAR, MAX_RESULT_SIZE)] = {0};
SecurityAlgorithm AlgorithmTable[NUM_ALGORITHMS];
#ifdef IPSEC_DEBUG
void dump_encoded_mesg(uchar *buff, uint len) { uint i, cnt = 0; uint bytes = 0; uint wrds = 0; uchar *buf = buff;
for (i = 0; i < len; i++) { if (wrds == 0) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "&%02x: ", cnt)); }
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "%02x", *buf)); buf++; bytes++; if (!(bytes % 4)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, " ")); bytes = 0; } wrds++; if (!(wrds % 16)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "\n")); wrds = 0; cnt += 16; } }
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "\n")); }
void DumpKey(uchar *buff, uint len) { uint i;
for (i = 0; i < len; i++) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "|%c", buff[i])); } KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "|\n")); } #endif
//* SPCheckAddr - Compare IP address in packet to IP address in policy.
//
// SPAddrField specifies the type of comparison:
// WILDCARD_VALUE, SINGLE_VALUE, or RANGE_VALUE.
//
int SPCheckAddr( IPv6Addr *PacketAddr, uint SPAddrField, IPv6Addr *SPAddr, IPv6Addr *SPAddrData) { int Result;
switch (SPAddrField) {
case WILDCARD_VALUE: //
// Always a match since the address is don't care.
//
Result = TRUE; break;
case SINGLE_VALUE: //
// Check if the address of the packet matches the SP selector.
//
Result = IP6_ADDR_EQUAL(PacketAddr, SPAddr); break;
case RANGE_VALUE: //
// Check if the address is in the specified selector range.
//
Result = (IP6_ADDR_LTEQ(SPAddr, PacketAddr) && IP6_ADDR_LTEQ(PacketAddr, SPAddrData)); break;
default: //
// Should never happen.
//
ABORTMSG("SPCheckAddr: invalid SPAddrField value"); Result = FALSE; }
return Result; }
//* SPCheckPort - Compare port in packet to port in policy.
//
uint SPCheckPort( ushort PacketPort, uint SPPortField, ushort SPPort, ushort SPPortData) { uint Result = FALSE;
switch (SPPortField) {
case WILDCARD_VALUE: // Always a match since the port is don't care.
Result = TRUE; break;
case SINGLE_VALUE: // Check if the port of the packet matches the SP selector.
if (PacketPort == SPPort) { Result = TRUE; } break;
case RANGE_VALUE: // Check if port is between range.
if (PacketPort >= SPPort && PacketPort <= SPPortData) { Result = TRUE; } break;
default: //
// Should never happen.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "SPCheckPort: invalid value for SPPortField (%u)\n", SPPortField)); }
return Result; }
//* ReleaseSA
//
// Releases a reference to an SA.
//
void ReleaseSA(SecurityAssociation *SA) { if (InterlockedDecrement((PLONG)&SA->RefCnt) == 0) { //
// No more references, so deallocate it.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_STATE, "Freeing SA: %p\n", SA)); RemoveSecurityAssociation(SA); ExFreePool(SA); } }
//* DeleteSA - Invalidate a security association.
//
// The SA is removed from the SA chain. All pointers from the SA entry are
// removed and the related reference counts decremented. The SP pointers to
// the SA can be removed; however, there could be references from the temp SA
// holders used during IPSec traffic processing.
//
// The temp SA references (IPSecProc and SALinkage) remove the references
// when traffic processing is done. The case can occur where the SA is
// deleted but the temp SA holder still has a reference. In that case,
// the SA is not removed from the global list.
//
int DeleteSA( SecurityAssociation *SA) { SecurityAssociation *FirstSA, *PrevSA = NULL; uint Direction;
//
// Get the start of the SA Chain.
//
Direction = SA->DirectionFlag;
if (Direction == INBOUND) { FirstSA = SA->SecPolicy->InboundSA; } else { FirstSA = SA->SecPolicy->OutboundSA; }
//
// Find the invalid SA and keep track of the SA before it.
//
while (FirstSA != SA) { PrevSA = FirstSA; if (PrevSA == NULL) { // This is a problem it should never happen.
// REVIEW: Can we change this to an ASSERT?
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE, "DeleteSA: SA was not found\n")); return FALSE; } FirstSA = FirstSA->ChainedSecAssoc; }
//
// Remove the SA from the SA Chain.
//
// Check if the invalid SA is the First SA of the chain.
if (PrevSA == NULL) { // The invalid SA is the first SA so the SP needs to be adjusted.
if (Direction == INBOUND) { SA->SecPolicy->InboundSA = FirstSA->ChainedSecAssoc; } else { SA->SecPolicy->OutboundSA = FirstSA->ChainedSecAssoc; } } else { // Just a entry in the Chain.
PrevSA->ChainedSecAssoc = FirstSA->ChainedSecAssoc; }
// Decrement the reference count of the SP.
SA->SecPolicy->RefCnt--;
// Remove the reference to the SP.
SA->SecPolicy = NULL;
SA->Valid = SA_REMOVED;
// Decrement the reference count of the SA.
ReleaseSA(SA);
InvalidateSecurityState();
return TRUE; }
//* RemoveSecurityPolicy
//
// Remove a policy from the global list.
// References are not held for the global list links.
//
void RemoveSecurityPolicy(SecurityPolicy *SP) { if (SP->Prev == NULL) { SecurityPolicyList = SP->Next; } else { SP->Prev->Next = SP->Next; } if (SP->Next != NULL) { SP->Next->Prev = SP->Prev; } }
//* DeleteSP - Removes an SP entry from the kernel.
//
// The removal of an SP makes all the SAs belonging to the SP invalid.
// Unlike the SA removal, this removes every reference to the invalid SP.
// Therefore, a check does not need to be made to ensure the SP is valid.
//
// Called with the security lock held.
//
int DeleteSP( SecurityPolicy *SP) { //
// Remove the SP's SAs.
//
while (SP->InboundSA != NULL) { if (!(DeleteSA(SP->InboundSA))) { return FALSE; } } while (SP->OutboundSA != NULL) { if (!(DeleteSA(SP->OutboundSA))) { return FALSE; } }
//
// Take it off the global list.
//
RemoveSecurityPolicy(SP);
// Check if this is part of an SA bundle.
if (SP->SABundle != NULL) { SecurityPolicy *PrevSABundle, *NextSABundle;
//
// The SP pointer being removed is a middle or first SABundle pointer.
//
PrevSABundle = SP->PrevSABundle; NextSABundle = SP->SABundle; NextSABundle->PrevSABundle = PrevSABundle;
if (PrevSABundle == NULL) { // First SABundle pointer.
NextSABundle->RefCnt--; } else { //
// Clean up the SABundle deletion affects on other SP pointers.
//
while (PrevSABundle != NULL) { PrevSABundle->NestCount--; PrevSABundle->SABundle = NextSABundle; NextSABundle = PrevSABundle; PrevSABundle = PrevSABundle->PrevSABundle; }
SP->RefCnt--; }
SP->RefCnt--; }
//
// Check if anything else is referencing the invalid SP.
// All the interfaces and SA references have been removed.
// The only thing left are SABundle pointers.
//
if (SP->RefCnt != 0) { SecurityPolicy *PrevSABundle, *NextSABundle;
//
// The SP pointer being removed is the last of the bundle pointers.
//
PrevSABundle = SP->PrevSABundle; NextSABundle = SP->SABundle;
ASSERT(PrevSABundle != NULL); ASSERT(NextSABundle == NULL);
PrevSABundle->RefCnt--;
//
// Cleanup the SABundle deletion affects on other SP pointers.
//
while (PrevSABundle != NULL) { PrevSABundle->NestCount--; PrevSABundle->SABundle = NextSABundle; NextSABundle = PrevSABundle; PrevSABundle = PrevSABundle->PrevSABundle; }
SP->RefCnt--;
// Now the reference count better be zero.
if (SP->RefCnt != 0) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE, "DeleteSP: The SP list is corrupt!\n")); return FALSE; } }
// Free the memory.
ExFreePool(SP);
InvalidateSecurityState();
return TRUE; }
//* RemoveSecurityAssociation
//
// Remove an association from the global list.
// References are not held for the global list links.
//
void RemoveSecurityAssociation(SecurityAssociation *SA) { if (SA->Prev == NULL) { SecurityAssociationList = SA->Next; } else { SA->Prev->Next = SA->Next; } if (SA->Next != NULL) { SA->Next->Prev = SA->Prev; } }
//* FreeIPSecToDo
//
void FreeIPSecToDo( IPSecProc *IPSecToDo, uint Number) { uint i;
for (i = 0; i < Number; i++) { ReleaseSA(IPSecToDo[i].SA); }
ExFreePool(IPSecToDo); }
//* InboundSAFind - find a SA in the Security Association Database.
//
// A Security Association on a receiving machine is uniquely identified
// by the tuple of SPI, IP Destination, and security protocol.
//
// REVIEW: Since we can choose our SPI's to be system-wide unique, we
// REVIEW: could do the lookup solely via SPI and just verify the others.
//
// REVIEW: Should we do our IP Destination lookup via ADE? Faster.
//
SecurityAssociation * InboundSAFind( ulong SPI, // Security Parameter Index.
IPv6Addr *Dest, // Destination address.
uint Protocol) // Security protocol in use (e.g. AH or ESP).
{ SecurityAssociation *SA; KIRQL OldIrql;
// Get Security Lock.
KeAcquireSpinLock(&IPSecLock, &OldIrql);
// Start at the first SA entry.
for (SA = SecurityAssociationList; SA != NULL; SA = SA->Next) { // Check SPI.
if (SPI == SA->SPI) { // Check destination IP address and IPSec protocol.
if (IP6_ADDR_EQUAL(Dest, &SA->SADestAddr) && (Protocol == SA->IPSecProto)) {
// Check direction.
if (SA->DirectionFlag == INBOUND) { // Check if the SA entry is valid.
if (SA->Valid == SA_VALID) { AddRefSA(SA); break; } // Not valid so continue checking.
continue; } } } }
// Release lock.
KeReleaseSpinLock(&IPSecLock, OldIrql);
return SA; }
//* InboundSALookup - Check the matched SP for a matching SA.
//
// In the SABundle case, this function is called recursively to compare all
// the SA entries. Note, the selectors are not compared for SABundles.
//
uint InboundSALookup( SecurityPolicy *SP, SALinkage *SAPerformed) { SecurityAssociation *SA; uint Result = FALSE;
for (SA = SP->InboundSA; SA != NULL; SA = SA->ChainedSecAssoc) { if (SA == SAPerformed->This && SA->DirectionFlag == INBOUND) { //
// Check if the SP entry is a bundle.
//
if (SP->SABundle != NULL && SAPerformed->Next != NULL) { // Recursive call.
if (InboundSALookup(SP->SABundle, SAPerformed->Next)) { Result = TRUE; break; }
} else if (SP->SABundle == NULL && SAPerformed->Next == NULL) { // Found a match and no bundle to check.
if (SA->Valid == SA_VALID) { Result = TRUE; } else { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE, "InboundSALookup: Invalid SA\n")); } break;
} else { // SAs in packet disagree with SABundle so no match.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_RARE, "InboundSALookup: SA seen disagrees with SA " "in SABundle\n")); break; } } }
return Result; }
//* InboundSecurityCheck - IPSec processing verification.
//
// This function is called from the transport layer. The policy selectors
// are compared with the packet to find a match. The search continues
// until there is a match.
//
// The RFC says that the inbound SPD does not need to be ordered.
// However if that is the case, then Bypass and discard mode couldn't
// be used to quickly handle a packet. Also since most of the SPs are
// bidirectional, the SP entries are ordered. We require the administrator
// to order the policies.
//
int InboundSecurityCheck( IPv6Packet *Packet, ushort TransportProtocol, ushort SourcePort, ushort DestPort, Interface *IF) { SecurityPolicy *SP; int Result = FALSE; KIRQL OldIrql;
//
// Get IPSec lock then get interface lock.
// REVIEW: Do we still need to grab the IF lock here?
//
KeAcquireSpinLock(&IPSecLock, &OldIrql); KeAcquireSpinLockAtDpcLevel(&IF->Lock);
for (SP = SecurityPolicyList; SP != NULL; SP = SP->Next) { // Check Interface.
if ((SP->IFIndex != 0) && (SP->IFIndex != IF->Index)) continue;
// Check Direction of SP.
if (!(SP->DirectionFlag == INBOUND || SP->DirectionFlag == BIDIRECTIONAL)) { continue; }
// Check Remote Address.
if (!SPCheckAddr(AlignAddr(&Packet->IP->Source), SP->RemoteAddrField, &SP->RemoteAddr, &SP->RemoteAddrData)) { continue; }
// Check Local Address.
if (!SPCheckAddr(AlignAddr(&Packet->IP->Dest), SP->LocalAddrField, &SP->LocalAddr, &SP->LocalAddrData)) { continue; }
// Check Transport Protocol.
if (SP->TransportProto == NONE) { // None so protocol passed.
} else { if (SP->TransportProto != TransportProtocol) { continue; } else { // Check Remote Port.
if (!SPCheckPort(SourcePort, SP->RemotePortField, SP->RemotePort, SP->RemotePortData)) { continue; }
// Check Local Port.
if (!SPCheckPort(DestPort, SP->LocalPortField, SP->LocalPort, SP->LocalPortData)) { continue; } } }
// Check if the packet should be dropped.
if (SP->SecPolicyFlag == IPSEC_DISCARD) { //
// Packet is dropped by transport layer.
// This essentially denies traffic.
//
break; }
// Check if packet bypassed IPSec processing.
if (Packet->SAPerformed == NULL) { //
// Check if this is bypass mode.
//
if (SP->SecPolicyFlag == IPSEC_BYPASS) { // Packet is okay to be processed by transport layer.
Result = TRUE; break; } // Check other policies, may change this to dropping later.
continue; }
//
// Getting here means the packet saw an SA.
//
// Check IPSec mode.
if (SP->IPSecSpec.Mode != Packet->SAPerformed->Mode) { //
// Wrong mode for this traffic drop packet.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "InboundSecurityCheck: Wrong IPSec mode for traffic " "Policy #%d\n", SP->Index)); break; }
// Check SA pointer.
if (!InboundSALookup(SP, Packet->SAPerformed)) { //
// SA lookup failed.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "InboundSecurityCheck: SA lookup failed Policy #%d\n", SP->Index)); break; }
// Successful verification of IPSec.
Result = TRUE; break; }
// Release locks.
KeReleaseSpinLockFromDpcLevel(&IF->Lock); KeReleaseSpinLock(&IPSecLock, OldIrql);
return Result; }
//* OutboundSALookup - Find a SA for the matching SP.
//
// This function is called after an SP match is found. The SAs associated
// with the SP are searched for a match. If match is found, a check to see
// if the SP contains a bundle is done. A bundle causes a similar lookup.
// If any of the bundle SAs are not found, the lookup is a failure.
//
IPSecProc * OutboundSALookup( IPv6Addr *SourceAddr, IPv6Addr *DestAddr, ushort TransportProtocol, ushort DestPort, ushort SourcePort, SecurityPolicy *SP, uint *Action) { SecurityAssociation *SA; uint i; uint BundleCount = 0; IPSecProc *IPSecToDo = NULL;
*Action = LOOKUP_DROP;
//
// Find the SA entry associated with the found SP entry.
// If there is a bundle, a subsequent search finds the bundled SAs.
//
for (SA = SP->OutboundSA; SA != NULL; SA = SA->ChainedSecAssoc) { if (SP->RemoteAddrSelector == PACKET_SELECTOR) { // Check Remote Address.
if (!IP6_ADDR_EQUAL(DestAddr, &SA->DestAddr)) { continue; } }
if (SP->LocalAddrSelector == PACKET_SELECTOR) { // Check Remote Address.
if (!IP6_ADDR_EQUAL(SourceAddr, &SA->SrcAddr)) { continue; } }
if (SP->RemotePortSelector == PACKET_SELECTOR) { if (DestPort != SA->DestPort) { continue; } }
if (SP->LocalPortSelector == PACKET_SELECTOR) { if (SourcePort != SA->SrcPort) { continue; } }
if (SP->TransportProtoSelector == PACKET_SELECTOR) { if (TransportProtocol != SA->TransportProto) { continue; } }
// Check if the SA is valid.
if (SA->Valid != SA_VALID) { // SA is invalid continue checking.
continue; }
//
// Match found.
//
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Send using SP->Index=%d, SA->Index=%d\n", SP->Index, SA->Index)); #endif
BundleCount = SP->NestCount;
// Allocate the IPSecToDo array.
IPSecToDo = ExAllocatePool(NonPagedPool, (sizeof *IPSecToDo) * BundleCount); if (IPSecToDo == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "OutboundSALookup: " "Couldn't allocate memory for IPSecToDo!?!\n")); return NULL; }
//
// Fill in IPSecToDo first entry.
//
IPSecToDo[0].SA = SA; AddRefSA(SA); IPSecToDo[0].Mode = SP->IPSecSpec.Mode; IPSecToDo[0].BundleSize = SP->NestCount; *Action = LOOKUP_CONT; break; } // end of for (SA = SP->OutboundSA; ...)
// Check if there is a bundled SA.
for (i = 1; i < BundleCount; i++) { *Action = LOOKUP_DROP;
// Check to make sure the bundle pointer is not null (safety check).
if (SP->SABundle == NULL) { // This is bad so exit loop.
// Free IPSecToDo memory.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "OutboundSALookup: SP entry %d SABundle pointer is " "NULL\n", SP->Index)); FreeIPSecToDo(IPSecToDo, i); break; }
SP = SP->SABundle;
// Search through the SAs for this SP.
for (SA = SP->OutboundSA; SA != NULL; SA = SA->ChainedSecAssoc) { if (SP->RemoteAddrSelector == PACKET_SELECTOR) { // Check Remote Address.
if (!IP6_ADDR_EQUAL(DestAddr, &SA->DestAddr)) { continue; } }
if (SP->LocalAddrSelector == PACKET_SELECTOR) { // Check Remote Address.
if (!IP6_ADDR_EQUAL(SourceAddr, &SA->SrcAddr)) { continue; } }
if (SP->RemotePortSelector == PACKET_SELECTOR) { if (DestPort != SA->DestPort) { continue; } }
if (SP->LocalPortSelector == PACKET_SELECTOR) { if (SourcePort != SA->SrcPort) { continue; } }
if (SP->TransportProtoSelector == PACKET_SELECTOR) { if (TransportProtocol != SA->TransportProto) { continue; } }
// Check if the SA is valid.
if (SA->Valid != SA_VALID) { // SA is invalid continue checking.
continue; }
//
// Match found.
//
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Send using SP->Index=%d, SA->Index=%d\n", SP->Index, SA->Index)); #endif
//
// Fill in IPSecToDo entry.
//
IPSecToDo[i].SA = SA; AddRefSA(SA); IPSecToDo[i].Mode = SP->IPSecSpec.Mode; IPSecToDo[i].BundleSize = SP->NestCount; *Action = LOOKUP_CONT; break; } // end of for (SA = SP->OutboundSA; ...)
// Check if the match was found.
if (*Action == LOOKUP_DROP) { // No match so free IPSecToDo memory.
FreeIPSecToDo(IPSecToDo, i); break; } } // end of for (i = 1; ...)
return IPSecToDo; }
//* OutboundSPLookup - Do the IPSec processing associated with an outbound SP.
//
// This function is called from the transport layer to find an appropriate
// SA or SABundle associated with the traffic. The Outbound SPD is sorted
// so the first SP found is for this traffic.
//
IPSecProc * OutboundSPLookup( IPv6Addr *SourceAddr, // Source Address.
IPv6Addr *DestAddr, // Destination Address.
ushort TransportProtocol, // Transport Protocol.
ushort SourcePort, // Source Port.
ushort DestPort, // Destination Port.
Interface *IF, // Interface Pointer.
uint *Action) // Action to do.
{ SecurityPolicy *SP; KIRQL OldIrql; IPSecProc *IPSecToDo;
IPSecToDo = NULL; *Action = LOOKUP_DROP;
//
// Get IPSec lock then get interface lock.
// REVIEW: Do we still need to grab the IF lock here?
//
KeAcquireSpinLock(&IPSecLock, &OldIrql); KeAcquireSpinLockAtDpcLevel(&IF->Lock);
for (SP = SecurityPolicyList; SP != NULL; SP = SP->Next) { // Check Interface.
if ((SP->IFIndex != 0) && (SP->IFIndex != IF->Index)) continue;
// Check Direction of SP.
if (!(SP->DirectionFlag == OUTBOUND || SP->DirectionFlag == BIDIRECTIONAL)) { continue; }
// Check Remote Address.
if (!SPCheckAddr(DestAddr, SP->RemoteAddrField, &SP->RemoteAddr, &SP->RemoteAddrData)) { continue; }
// Check Local Address.
if (!SPCheckAddr(SourceAddr, SP->LocalAddrField, &SP->LocalAddr, &SP->LocalAddrData)) { continue; }
// Check Transport Protocol.
if (SP->TransportProto == NONE) { // None so protocol passed.
} else { if (SP->TransportProto != TransportProtocol) { continue; } else { // Check Remote Port.
if (!SPCheckPort(DestPort, SP->RemotePortField, SP->RemotePort, SP->RemotePortData)) { continue; }
// Check Local Port.
if (!SPCheckPort(SourcePort, SP->LocalPortField, SP->LocalPort, SP->LocalPortData)) { continue; } } }
//
// Check IPSec Action.
//
if (SP->SecPolicyFlag == IPSEC_APPLY) { // Search for an SA entry that matches.
IPSecToDo = OutboundSALookup(SourceAddr, DestAddr, TransportProtocol, DestPort, SourcePort, SP, Action); if (IPSecToDo == NULL) { // No SA was found for the outgoing traffic.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "OutboundSPLookup: No SA found for SP entry %d\n", SP->Index)); *Action = LOOKUP_DROP; } } else { if (SP->SecPolicyFlag == IPSEC_DISCARD) { // Packet is dropped.
IPSecToDo = NULL; *Action = LOOKUP_DROP; } else { //
// This is Bypass or "App determines" mode.
// REVIEW: What is the app determine mode?
//
IPSecToDo = NULL; *Action = LOOKUP_BYPASS; } } break; }
// Release locks.
KeReleaseSpinLockFromDpcLevel(&IF->Lock); KeReleaseSpinLock(&IPSecLock, OldIrql);
return IPSecToDo; }
//* PerformDeferredAHProcessing - Helper for AuthenticationHeaderReceive
//
// This routine handles processing the AH authentication algorithm over
// a given extension header once we know which header logically follows it.
//
void PerformDeferredAHProcessing( SecurityAlgorithm *Alg, // Authentication algorithm to use.
void *Context, // Context to use with algorithm.
uchar *Key, // Key to use with algorithm.
uint AmountSkipped, // Size of headers not included in AH validation.
void *Data, // Start of header we're currently processing.
uchar ThisHeader, // Which header we're currently processing.
uchar NextHeader) // Header logically following this one.
{ uint Dummy; ushort PayloadLength;
switch(ThisHeader) {
case IP_PROTOCOL_V6: { IPv6Header UNALIGNED *IP = (IPv6Header UNALIGNED *)Data;
//
// REVIEW: Cache IPv6 header so we can give it to Operate as a single
// REVIEW: chunk and avoid all these individual calls? More efficient?
//
// In VersClassFlow, only the IP version is immutable.
Dummy = IP_VERSION;
//
// For non-jumbograms, the payload length needs to be altered to
// reflect the lack of those headers which aren't included in the
// authentication check.
//
PayloadLength = net_short(IP->PayloadLength); if (PayloadLength != 0) { PayloadLength = PayloadLength - AmountSkipped; } PayloadLength = net_short(PayloadLength);
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "\nAH Receive Data:\n")); dump_encoded_mesg((uchar *)&Dummy, 4); dump_encoded_mesg((uchar *)&PayloadLength, 2); dump_encoded_mesg((uchar *)&NextHeader, 1); #endif
(*Alg->Operate)(Context, Key, (uchar *)&Dummy, 4); (*Alg->Operate)(Context, Key, (uchar *)&PayloadLength, 2); (*Alg->Operate)(Context, Key, (uchar *)&NextHeader, 1); Dummy = 0; // Hop Limit is mutable.
#ifdef IPSEC_DEBUG
dump_encoded_mesg((uchar *)&Dummy, 1); dump_encoded_mesg((uchar *)&IP->Source, 2 * sizeof(IPv6Addr)); #endif
(*Alg->Operate)(Context, Key, (uchar *)&Dummy, 1); (*Alg->Operate)(Context, Key, (uchar *)&IP->Source, 2 * sizeof(IPv6Addr)); break; }
case IP_PROTOCOL_HOP_BY_HOP: case IP_PROTOCOL_DEST_OPTS: { IPv6OptionsHeader UNALIGNED *Ext; uint HdrLen, Amount; uchar *Start, *Current;
//
// The options headers have the NextHeader field as the first byte.
//
C_ASSERT(FIELD_OFFSET(IPv6OptionsHeader, NextHeader) == 0);
//
// First feed the NextHeader field into the algorithm.
// We use the one that logically follows, not the one in the header.
//
#ifdef IPSEC_DEBUG
dump_encoded_mesg(&NextHeader, 1); #endif
(*Alg->Operate)(Context, Key, &NextHeader, 1);
//
// Now feed the rest of this header into the algorithm.
// This includes the remainder of the base header and any
// non-mutable options. For mutable options, we feed the
// algorithm with the equivalent number of zeroes.
//
Ext = (IPv6OptionsHeader UNALIGNED *)Data; HdrLen = (Ext->HeaderExtLength + 1) * EXT_LEN_UNIT; Start = (uchar *)Data + 1; Current = (uchar *)Data + sizeof(IPv6OptionsHeader); HdrLen -= sizeof(IPv6OptionsHeader); while (HdrLen) {
if (*Current == OPT6_PAD_1) { //
// This is the special one byte pad option. Immutable.
//
Current++; HdrLen--; continue; }
if ((*Current == OPT6_JUMBO_PAYLOAD) && (AmountSkipped != 0 )) { //
// Special case for jumbo payload option where we have to
// update the payload length to reflect skipped headers.
//
//
// First feed in everything up to the option data.
//
Amount = (uint)(Current - Start) + sizeof(OptionHeader); #ifdef IPSEC_DEBUG
dump_encoded_mesg(Start, Amount); #endif
(*Alg->Operate)(Context, Key, Start, Amount);
//
// Adjust the payload length before feeding it in.
//
Current += sizeof(OptionHeader); Dummy = net_long(net_long(*(ulong *)Current) - AmountSkipped); #ifdef IPSEC_DEBUG
dump_encoded_mesg((uchar *)&Dummy, 4); #endif
(*Alg->Operate)(Context, Key, (uchar *)&Dummy, 4);
HdrLen -= sizeof(OptionHeader) + sizeof(ulong); Current += sizeof(ulong); Start = Current; continue; }
if (OPT6_ISMUTABLE(*Current)) { //
// This option's data is mutable. Everything preceeding
// the option data is not.
//
Amount = (uint)(Current - Start) + 2; // Immutable amount.
#ifdef IPSEC_DEBUG
dump_encoded_mesg(Start, Amount); #endif
(*Alg->Operate)(Context, Key, Start, Amount);
Current++; // Now on option data length byte.
Amount = *Current; // Mutable amount.
#ifdef IPSEC_DEBUG
dump_encoded_mesg(Zero, Amount); #endif
(*Alg->Operate)(Context, Key, Zero, Amount);
HdrLen -= Amount + 2; Current += Amount + 1; Start = Current;
} else {
//
// This option's data is not mutable.
// Just skip over it.
//
Current++; // Now on option data length byte.
Amount = *Current; HdrLen -= Amount + 2; Current += Amount + 1; } } if (Start != Current) { //
// Option block ends with an immutable region.
//
Amount = (uint)(Current - Start); #ifdef IPSEC_DEBUG
dump_encoded_mesg(Start, Amount); #endif
(*Alg->Operate)(Context, Key, Start, Amount); } break; }
case IP_PROTOCOL_ROUTING: { IPv6RoutingHeader UNALIGNED *Route; uint HdrLen;
//
// The routing header has the NextHeader field as the first byte.
//
C_ASSERT(FIELD_OFFSET(IPv6RoutingHeader, NextHeader) == 0);
//
// First feed the NextHeader field into the algorithm.
// We use the one that logically follows, not the one in the header.
//
#ifdef IPSEC_DEBUG
dump_encoded_mesg(&NextHeader, 1); #endif
(*Alg->Operate)(Context, Key, &NextHeader, 1);
//
// Now feed the rest of this header into the algorithm.
// It's all immutable.
//
Route = (IPv6RoutingHeader UNALIGNED *)Data; HdrLen = ((Route->HeaderExtLength + 1) * EXT_LEN_UNIT) - 1; ((uchar *)Data)++; #ifdef IPSEC_DEBUG
dump_encoded_mesg(Data, HdrLen); #endif
(*Alg->Operate)(Context, Key, Data, HdrLen); break; }
default: //
// Unrecognized header.
// The only way this can happen is if somebody later adds code
// to AuthenticationHeaderReceive to call this function for a
// new header and neglects to add corresponding support here.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "PerformDeferredAHProcessing: " "Unsupported header = %d\n", ThisHeader)); ABORT(); } }
//* AuthenticationHeaderReceive - Handle an IPv6 AH header.
//
// This is the routine called to process an Authentication Header,
// next header value of 51.
//
uchar AuthenticationHeaderReceive( IPv6Packet *Packet) // Packet handed to us by IPv6Receive.
{ AHHeader UNALIGNED *AH; SecurityAssociation *SA; SecurityAlgorithm *Alg; uint ResultSize, AHHeaderLen; void *Context; uchar *Result, *AuthData; SALinkage *SAPerformed; uint SavePosition; void *SaveData; uint SaveContigSize; uint SaveTotalSize; void *SaveAuxList = NULL; uchar NextHeader, DeferredHeader; void *DeferredData; uint Done;
//
// Verify that we have enough contiguous data to overlay an Authentication
// Header structure on the incoming packet. Then do so and skip over it.
//
if (! PacketPullup(Packet, sizeof(AHHeader), 1, 0)) { // Pullup failed.
if (Packet->TotalSize < sizeof(AHHeader)) KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "AuthenticationHeaderReceive: Incoming packet too small" " to contain authentication header\n")); return IP_PROTOCOL_NONE; // Drop packet.
} AH = (AHHeader UNALIGNED *)Packet->Data; // Remember offset to this header's NextHeader field.
Packet->NextHeaderPosition = Packet->Position + FIELD_OFFSET(AHHeader, NextHeader); AdjustPacketParams(Packet, sizeof(AHHeader));
//
// Lookup Security Association in the Security Association Database.
//
SA = InboundSAFind(net_long(AH->SPI), AlignAddr(&Packet->IP->Dest), IP_PROTOCOL_AH); if (SA == NULL) { // No SA exists for this packet.
// Drop packet. NOTE: This is an auditable event.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "AuthenticationHeaderReceive: " "No matching SA in database\n")); return IP_PROTOCOL_NONE; }
//
// Verify the Sequence Number if required to do so by the SA.
// Since we only support manual keying currently, we treat all SAs
// as not requiring this check.
// TBD: Will need to change this when we add support for dynamic
// TBD: keying (IKE).
//
//
// Perform Integrity check.
//
// First ensure that the amount of Authentication Data claimed to exist
// in this packet by the AH header's PayloadLen field is large enough to
// contain the amount that is required by the algorithm specified in the
// SA. Note that the former may contain padding to make it a multiple
// of 32 bits. Then check the packet size to ensure that it is big
// enough to hold what the header claims is present.
//
AHHeaderLen = (AH->PayloadLen + 2) * 4; if (AHHeaderLen < sizeof (AHHeader)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "AuthenticationHeaderReceive: Bogus AH header length\n")); goto ErrorReturn; } AHHeaderLen -= sizeof(AHHeader); // May include padding.
Alg = &AlgorithmTable[SA->AlgorithmId]; ResultSize = Alg->ResultSize; // Does not include padding.
if (ResultSize > AHHeaderLen) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "AuthenticationHeaderReceive: Incoming packet's AHHeader" " length inconsistent with algorithm's AuthData size\n")); goto ErrorReturn; } if (! PacketPullup(Packet, AHHeaderLen, 1, 0)) { // Pullup failed.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "AuthenticationHeaderReceive: Incoming packet too small" " to contain authentication data\n")); goto ErrorReturn; } AuthData = (uchar *)Packet->Data; AdjustPacketParams(Packet, AHHeaderLen);
//
// AH authenticates everything (expect mutable fields) starting from
// the previous IPv6 header. Stash away our current position (so we can
// restore it later) and backup to the previous IPv6 header.
//
SavePosition = Packet->Position; SaveData = Packet->Data; SaveContigSize = Packet->ContigSize; SaveTotalSize = Packet->TotalSize; SaveAuxList = Packet->AuxList; PositionPacketAt(Packet, Packet->IPPosition); Packet->AuxList = NULL;
//
// Initialize this particular algorithm.
//
Context = alloca(Alg->ContextSize); (*Alg->Initialize)(Context, SA->Key);
//
// Run algorithm over packet data. We start with the IP header that
// encapsulates this AH header. We proceed through the end of the
// packet, skipping over certain headers which are not part of the
// logical packet being secured. We also treat any mutable fields
// as zero for the purpose of the algorithm calculation.
//
// Note: We only search for mutable fields in Destination Options
// headers that appear before this AH header. While the spec doesn't
// explicitly spell this out anywhere, this is the behavior that makes
// the most sense and we've verified this interpretation in the working
// group. However, because of this, our interpretation fails a TAHI test.
// TAHI will hopefully fix their test, if they haven't already.
//
//
// Start by pulling up the IP header and seeing which header physically
// follows it.
//
if (! PacketPullup(Packet, sizeof(IPv6Header), __builtin_alignof(IPv6Addr), 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: Out of memory!?!\n")); goto ErrorReturn; } NextHeader = Packet->IP->NextHeader;
//
// Defer processing of this header until after we've determined
// whether or not we'll be skipping the following header. This allows us
// to use the correct NextHeader field value when running the algorithm.
//
DeferredHeader = IP_PROTOCOL_V6; DeferredData = Packet->Data; AdjustPacketParams(Packet, sizeof(IPv6Header));
//
// Continue over the various extension headers until we reach the
// AH header for which we're running this authentication algoritm.
// We've already parsed this far, so we know these headers are legit.
//
for (Done = FALSE; !Done;) { switch (NextHeader) {
case IP_PROTOCOL_HOP_BY_HOP: case IP_PROTOCOL_DEST_OPTS: { IPv6OptionsHeader *Ext; uint HdrLen;
//
// These headers are not skipped, so process the header
// logically preceeding this one. Its NextHeader field
// will contain the Protocol value for this header.
//
PerformDeferredAHProcessing(Alg, Context, SA->Key, Packet->SkippedHeaderLength, DeferredData, DeferredHeader, NextHeader);
//
// Remember this header for deferred processing.
//
DeferredHeader = NextHeader;
//
// Get the extension header and all the options pulled up
// into one nice contiguous chunk.
//
if (! PacketPullup(Packet, sizeof(ExtensionHeader), __builtin_alignof(ExtensionHeader), 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: " "Out of mem!?!\n")); goto ErrorReturn; } Ext = (IPv6OptionsHeader *)Packet->Data; NextHeader = Ext->NextHeader; HdrLen = (Ext->HeaderExtLength + 1) * EXT_LEN_UNIT; if (! PacketPullup(Packet, HdrLen, 1, 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: " "Out of mem!?!\n")); goto ErrorReturn; }
//
// Remember where this header starts for deferred processing.
//
DeferredData = Packet->Data;
AdjustPacketParams(Packet, HdrLen); break; }
case IP_PROTOCOL_ROUTING: { IPv6RoutingHeader *Route; uint HdrLen;
//
// This header is not skipped, so process the header
// logically preceeding this one. Its NextHeader field
// will contain the Protocol value for this header.
//
PerformDeferredAHProcessing(Alg, Context, SA->Key, Packet->SkippedHeaderLength, DeferredData, DeferredHeader, IP_PROTOCOL_ROUTING);
//
// Remember this header for deferred processing.
//
DeferredHeader = IP_PROTOCOL_ROUTING;
//
// Get the extension header and all the options pulled up
// into one nice contiguous chunk.
//
if (! PacketPullup(Packet, sizeof(IPv6RoutingHeader), __builtin_alignof(IPv6RoutingHeader), 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: " "Out of mem!?!\n")); goto ErrorReturn; } Route = (IPv6RoutingHeader *)Packet->Data; NextHeader = Route->NextHeader; HdrLen = (Route->HeaderExtLength + 1) * EXT_LEN_UNIT; if (! PacketPullup(Packet, HdrLen, 1, 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: " "Out of mem!?!\n")); goto ErrorReturn; }
//
// Remember where this header starts for deferred processing.
//
DeferredData = Packet->Data;
AdjustPacketParams(Packet, HdrLen); break; }
case IP_PROTOCOL_AH: { //
// We don't know yet whether we'll be including this AH header
// in the algorithm calculation we're currently running.
// See below.
//
AHHeader UNALIGNED *ThisAH; uint ThisHdrLen; uint Padding;
//
// Pullup the AH header.
//
if (! PacketPullup(Packet, sizeof(AHHeader), 1, 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: " "Out of mem!?!\n")); goto ErrorReturn; } ThisAH = (AHHeader UNALIGNED *)Packet->Data; AdjustPacketParams(Packet, sizeof(AHHeader)); ThisHdrLen = ((ThisAH->PayloadLen + 2) * 4) - sizeof(AHHeader); if (! PacketPullup(Packet, ThisHdrLen, 1, 0)) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: " "Out of mem!?!\n")); goto ErrorReturn; } AdjustPacketParams(Packet, ThisHdrLen);
//
// If this is another AH header encapsulating the one we are
// currently processing, then don't include it in the integrity
// check as per AH spec section 3.3.
//
if (Packet->Position != SavePosition) { NextHeader = ThisAH->NextHeader; break; }
//
// Otherwise this is the AH header that we're currently processing,
// and we include it in its own integrity check. But first we
// need to process the header logically preceeding this one (which
// we previously defered). Its NextHeader field will contain the
// Protocol value for this header.
//
PerformDeferredAHProcessing(Alg, Context, SA->Key, Packet->SkippedHeaderLength, DeferredData, DeferredHeader, IP_PROTOCOL_AH);
//
// Now process this AH header. We do not need to defer processing
// of this header, since everything following it is included in
// the check. The Authentication Data is mutable, the rest of the
// AH header is not.
//
ASSERT(Packet->TotalSize == SaveTotalSize); #ifdef IPSEC_DEBUG
dump_encoded_mesg((uchar *)AH, sizeof(AHHeader)); #endif
(*Alg->Operate)(Context, SA->Key, (uchar *)AH, sizeof(AHHeader));
#ifdef IPSEC_DEBUG
dump_encoded_mesg(Zero, ResultSize); #endif
(*Alg->Operate)(Context, SA->Key, Zero, ResultSize);
//
// The Authentication Data may be padded. This padding is
// included as non-mutable in the integrity calculation.
// REVIEW: We should double-check our interpretation of the RFC
// about this with the IPSec working group.
//
Padding = AHHeaderLen - ResultSize; if (Padding != 0) { #ifdef IPSEC_DEBUG
dump_encoded_mesg((uchar *)(Packet->Data) - Padding, Padding); #endif
(*Alg->Operate)(Context, SA->Key, (uchar *)(Packet->Data) - Padding, Padding); }
Done = TRUE; break; }
case IP_PROTOCOL_ESP: { //
// We don't include other IPSec headers in the integrity check
// as per AH spec section 3.3. So just skip over this. Tricky
// part is that the NextHeader was in the ESP trailer which we've
// already thrown away at this point.
//
ESPHeader UNALIGNED *ThisESP; ulong ThisSPI; SALinkage *ThisSAL;
//
// Get the SPI out of the ESP header so we can identify its
// SALinkage entry on the SAPerformed chain. Skip over the
// ESP header while we're at it.
//
if (! PacketPullup(Packet, sizeof(ESPHeader), 1, 0)) { // Pullup failed.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "AuthenticationHeaderReceive: Out of mem!?!\n")); goto ErrorReturn; } ThisESP = (ESPHeader UNALIGNED *)Packet->Data; AdjustPacketParams(Packet, sizeof(ESPHeader)); ThisSPI = net_long(ThisESP->SPI);
//
// Find the SALinkage entry on the SAPerformed chain with the
// matching SPI. It must be present.
// REVIEW: This code assumes we made SPIs system-wide unique.
//
for (ThisSAL = Packet->SAPerformed; ThisSAL->This->SPI != ThisSPI; ThisSAL = ThisSAL->Next) ASSERT(ThisSAL->Next != NULL);
//
// Pull NextHeader value out of the SALinkage (where we stashed
// it back in EncapsulatingSecurityPayloadReceive).
//
NextHeader = (uchar)ThisSAL->NextHeader;
break; }
case IP_PROTOCOL_FRAGMENT: { //
// We normally won't encounter a fragment header here,
// since reassembly will occur before authentication.
// However, our implementation optimizes the reassembly of
// single-fragment packets by leaving the fragment header in
// place. When performing the authentication calculation,
// we treat such fragment headers as if they didn't exist.
//
FragmentHeader UNALIGNED *Frag;
if (! PacketPullup(Packet, sizeof(FragmentHeader), 1, 0)) { KdPrintEx((DPFLTR_TCPIP_ID, DPFLTR_NTOS_ERROR, "AuthenticationHeaderReceive: Out of mem!?\n")); goto ErrorReturn; } Frag = (FragmentHeader UNALIGNED *)Packet->Data; NextHeader = Frag->NextHeader;
AdjustPacketParams(Packet, sizeof(FragmentHeader));
break; }
default: // Unrecognized header.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "AuthenticationHeaderReceive: " "Unsupported header = %d\n", NextHeader)); goto ErrorReturn; } }
//
// Everything inside this AH header is treated as immutable.
//
// REVIEW: For performance reasons, the ContigSize check could be moved
// REVIEW: before the loop for the additional code space cost of
// REVIEW: duplicating the PacketPullup call.
//
while (Packet->TotalSize != 0) {
if (Packet->ContigSize == 0) { //
// Ran out of contiguous data.
// Get next buffer in packet.
//
PacketPullupSubr(Packet, 0, 1, 0); // Moves to next buffer.
} #ifdef IPSEC_DEBUG
dump_encoded_mesg(Packet->Data, Packet->ContigSize); #endif
(*Alg->Operate)(Context, SA->Key, Packet->Data, Packet->ContigSize); AdjustPacketParams(Packet, Packet->ContigSize); }
//
// Get final result from the algorithm.
//
Result = alloca(ResultSize); (*Alg->Finalize)(Context, SA->Key, Result); #ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Recv Key (%d bytes)): ", SA->RawKeyLength)); DumpKey(SA->RawKey, SA->RawKeyLength); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Recv AuthData:\n")); dump_encoded_mesg(Result, ResultSize);
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Sent AuthData:\n")); dump_encoded_mesg(AuthData, ResultSize); #endif
//
// Compare result to authentication data in packet. They should match.
//
if (RtlCompareMemory(Result, AuthData, ResultSize) != ResultSize) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "AuthenticationHeaderReceive: Failed integrity check\n")); goto ErrorReturn; }
//
// Restore our packet position (to just after AH Header).
//
PacketPullupCleanup(Packet); Packet->Position = SavePosition; Packet->Data = SaveData; Packet->ContigSize = SaveContigSize; Packet->TotalSize = SaveTotalSize; Packet->AuxList = SaveAuxList; SaveAuxList = NULL;
//
// Nested AH headers don't include this one in their calculations.
//
Packet->SkippedHeaderLength += sizeof(AHHeader) + AHHeaderLen;
//
// Add this SA to the list of those that this packet has passed.
//
SAPerformed = ExAllocatePool(NonPagedPool, sizeof *SAPerformed); if (SAPerformed == NULL) { ErrorReturn: ReleaseSA(SA); if (SaveAuxList != NULL) { PacketPullupCleanup(Packet); Packet->AuxList = SaveAuxList; } return IP_PROTOCOL_NONE; // Drop packet.
} SAPerformed->This = SA; SAPerformed->Next = Packet->SAPerformed; // This SA is now first on list.
SAPerformed->Mode = TRANSPORT; // Assume trans until we see an IPv6Header.
Packet->SAPerformed = SAPerformed;
return AH->NextHeader; }
//* EncapsulatingSecurityPayloadReceive - Handle an IPv6 ESP header.
//
// This is the routine called to process an Encapsulating Security Payload,
// next header value of 50.
//
uchar EncapsulatingSecurityPayloadReceive( IPv6Packet *Packet) // Packet handed to us by IPv6Receive.
{ ESPHeader UNALIGNED *ESP; ESPTrailer TrailerBuffer; ESPTrailer UNALIGNED *ESPT; SecurityAssociation *SA; PNDIS_BUFFER NdisBuffer; SALinkage *SAPerformed;
//
// Verify that we have enough contiguous data to overlay an Encapsulating
// Security Payload Header structure on the incoming packet. Since the
// authentication check covers the ESP header, we don't skip over it yet.
//
if (! PacketPullup(Packet, sizeof(ESPHeader), 1, 0)) { // Pullup failed.
if (Packet->TotalSize < sizeof(ESPHeader)) KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "EncapsulatingSecurityPayloadReceive: " "Incoming packet too small to contain ESP header\n")); return IP_PROTOCOL_NONE; // Drop packet.
} ESP = (ESPHeader UNALIGNED *)Packet->Data;
//
// Lookup Security Association in the Security Association Database.
//
SA = InboundSAFind(net_long(ESP->SPI), AlignAddr(&Packet->IP->Dest), IP_PROTOCOL_ESP); if (SA == NULL){ // No SA exists for this packet.
// Drop packet. NOTE: This is an auditable event.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "EncapsulatingSecurityPayloadReceive: " "No SA found for the packet\n")); return IP_PROTOCOL_NONE; }
//
// Verify the Sequence Number if required to do so by the SA.
// Since we only support manual keying currently, we treat all SAs
// as not requiring this check.
// TBD: Will need to change this when we add support for dynamic
// TBD: keying (IKE).
//
//
// Perform integrity check if authentication has been selected.
// TBD: When (if?) we add encryption support, we'll want to check the
// TBD: SA to see if authentication is desired. Hardwired for now.
//
{ SecurityAlgorithm *Alg; uint AuthDataSize; uint PayloadLength; void *Context; IPv6Packet Clone; uint DoNow = 0; uchar *AuthData; uchar *Result;
//
// First ensure that the incoming packet is large enough to hold the
// Authentication Data required by the algorithm specified in the SA.
// Then calculate the amount of data covered by authentication.
//
Alg = &AlgorithmTable[SA->AlgorithmId]; AuthDataSize = Alg->ResultSize; if (Packet->TotalSize < sizeof(ESPHeader) + sizeof(ESPTrailer) + AuthDataSize) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "EncapsulatingSecurityPaylofadReceive: " "Packet too short to hold Authentication Data\n")); goto ErrorReturn; } PayloadLength = Packet->TotalSize - AuthDataSize;
//
// Clone the packet positioning information so we can step through
// the packet without losing our current place. Start clone with
// a fresh pullup history, however.
//
Clone = *Packet; Clone.AuxList = NULL;
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "\nESP Receive Data:\n")); #endif
//
// Initialize this particular algorithm.
//
Context = alloca(Alg->ContextSize); (*Alg->Initialize)(Context, SA->Key);
//
// Run algorithm over packet data.
// ESP authenticates everything beginning with the ESP Header and
// ending just prior to the Authentication Data.
//
while (PayloadLength != 0) { DoNow = MIN(PayloadLength, Clone.ContigSize);
#ifdef IPSEC_DEBUG
dump_encoded_mesg(Clone.Data, DoNow); #endif
(*Alg->Operate)(Context, SA->Key, Clone.Data, DoNow); if (DoNow < PayloadLength) { //
// Not done yet, must have run out of contiguous data.
// Get next buffer in packet.
//
AdjustPacketParams(&Clone, DoNow); PacketPullupSubr(&Clone, 0, 1, 0); // Moves to next buffer.
} PayloadLength -= DoNow; }
AdjustPacketParams(&Clone, DoNow);
//
// Get final result from the algorithm.
//
Result = alloca(AuthDataSize); (*Alg->Finalize)(Context, SA->Key, Result);
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Calculated AuthData:\n")); dump_encoded_mesg(Result, AuthDataSize); #endif
//
// The Authentication Data immediately follows the region of
// authentication coverage. So our clone should be positioned
// at the beginning of it. Ensure that it's contiguous.
//
if (! PacketPullup(&Clone, AuthDataSize, 1, 0)) { // Pullup failed. Should never happen due to earlier check.
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "EncapsulatingSecurityPayloadReceive: " "Incoming packet too small for Auth Data\n")); goto ErrorReturn; }
// Point to Authentication data.
AuthData = Clone.Data;
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Received AuthData:\n")); dump_encoded_mesg(AuthData, AuthDataSize); #endif
//
// Compare our result to the Authentication Data. They should match.
//
if (RtlCompareMemory(Result, AuthData, AuthDataSize) != AuthDataSize) { //
// Integrity check failed. NOTE: This is an auditable event.
//
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "EncapsulatingSecurityPayloadReceive: " "Failed integrity check\n")); PacketPullupCleanup(&Clone); goto ErrorReturn; }
//
// Done with the clone, clean up after it.
//
PacketPullupCleanup(&Clone);
//
// Truncate our packet to no longer include the Authentication Data.
//
Packet->TotalSize -= AuthDataSize; if (Packet->ContigSize > Packet->TotalSize) Packet->ContigSize = Packet->TotalSize; }
//
// We can consume the ESP Header now since it isn't
// covered by confidentiality.
//
AdjustPacketParams(Packet, sizeof(ESPHeader));
//
// Decrypt Packet if confidentiality has been selected.
// TBD: When (if?) we add encryption support, we'll want to check the
// TBD: SA to see if encryption is desired. Hardwired for now.
//
#if 0
if (0) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "EncapsulatingSecurityPayloadReceive: " "SA requested confidentiality -- unsupported feature\n")); goto ErrorReturn; } #endif
//
// Remove trailer and padding (if any). Note that padding may appear
// even in the no-encryption case in order to align the Authentication
// Data on a four byte boundary.
//
if (Packet->NdisPacket == NULL) { //
// This packet must be just a single contiguous region.
// Finding the trailer is a simple matter of arithmetic.
//
ESPT = (ESPTrailer UNALIGNED *) ((uchar *)Packet->Data + Packet->TotalSize - sizeof(ESPTrailer)); } else { //
// Need to find the trailer in the Ndis buffer chain.
//
NdisQueryPacket(Packet->NdisPacket, NULL, NULL, &NdisBuffer, NULL); ESPT = (ESPTrailer UNALIGNED *) GetDataFromNdis(NdisBuffer, Packet->Position + Packet->TotalSize - sizeof(ESPTrailer), sizeof(ESPTrailer), (uchar *)&TrailerBuffer); } Packet->TotalSize -= sizeof(ESPTrailer); if (ESPT->PadLength > Packet->TotalSize) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_BAD_PACKET, "EncapsulatingSecurityPayloadReceive: " "PadLength impossibly large (%u of %u bytes)\n", ESPT->PadLength, Packet->TotalSize)); goto ErrorReturn; } // Remember offset to this header's NextHeader field.
Packet->NextHeaderPosition = Packet->Position + Packet->TotalSize + FIELD_OFFSET(ESPTrailer, NextHeader); // Remove padding.
Packet->TotalSize -= ESPT->PadLength; if (Packet->ContigSize > Packet->TotalSize) Packet->ContigSize = Packet->TotalSize;
//
// Encapsulated AH headers don't include this ESP header when
// authenticating the packet.
//
Packet->SkippedHeaderLength += sizeof(ESPHeader) + sizeof(ESPTrailer) + ESPT->PadLength;
//
// Add this SA to the list of those that this packet has passed.
//
SAPerformed = ExAllocatePool(NonPagedPool, sizeof *SAPerformed); if (SAPerformed == NULL) { ErrorReturn: ReleaseSA(SA); return IP_PROTOCOL_NONE; // Drop packet.
} SAPerformed->This = SA; SAPerformed->Next = Packet->SAPerformed; // This SA is now first on list.
SAPerformed->Mode = TRANSPORT; // Assume trans until we see an IPv6Header.
SAPerformed->NextHeader = ESPT->NextHeader; Packet->SAPerformed = SAPerformed;
return ESPT->NextHeader; }
//* InsertSecurityPolicy
//
// Add a security policy to the global list (a.k.a. "SecurityPolicyList").
// The global list is doubly-linked, ordered by the index value with the
// higher numbers (more specific policies) first.
//
// Called with security lock held.
//
int InsertSecurityPolicy( SecurityPolicy *NewSP) // Policy to insert.
{ SecurityPolicy *CurrentSP, *PrevSP;
//
// Run through the SP list looking for place to insert.
//
CurrentSP = PrevSP = SecurityPolicyList; while (CurrentSP != NULL) { if (CurrentSP->Index <= NewSP->Index) { break; }
// Move down the list.
PrevSP = CurrentSP; CurrentSP = CurrentSP->Next; }
//
// See where we ended up.
//
if (CurrentSP == NULL) { //
// Ran off the end of the list.
// New entry will become the last element.
//
NewSP->Next = NULL; } else { //
// Check for duplicate entries.
//
if (CurrentSP->Index == NewSP->Index) { // A policy with this index value already exists.
return FALSE; } //
// Put new one before 'current'.
//
NewSP->Next = CurrentSP; NewSP->Prev = CurrentSP->Prev; CurrentSP->Prev = NewSP; }
if (CurrentSP == SecurityPolicyList) { //
// Still at the front of the list.
// New entry becomes new list head.
//
NewSP->Prev = NULL; SecurityPolicyList = NewSP; } else { //
// Add new entry after 'previous'.
//
NewSP->Prev = PrevSP; PrevSP->Next = NewSP; }
InvalidateSecurityState();
return TRUE; }
//* InsertSecurityAssociation - Insert SA entry on SecurityAssociationList.
//
// Add a security association to the global list.
// The global list is doubly-linked, ordered by the index value with the
// higher numbers first.
// REVIEW: the order is arbitrary - just to look nicer when print out.
//
int InsertSecurityAssociation( SecurityAssociation *NewSA) // Association to insert.
{ SecurityAssociation *CurrentSA, *PrevSA;
//
// Run through the SA list looking for place to insert.
//
CurrentSA = PrevSA = SecurityAssociationList; while (CurrentSA != NULL) { if (CurrentSA->Index <= NewSA->Index) { break; }
// Move down the list.
PrevSA = CurrentSA; CurrentSA = CurrentSA->Next; }
//
// See where we ended up.
//
if (CurrentSA == NULL) { //
// Ran off the end of the list.
// New entry will become the last element.
//
NewSA->Next = NULL; } else { //
// Check for duplicate entries.
//
if (CurrentSA->Index == NewSA->Index) { // An association with this index value already exists.
return FALSE; } //
// Put new one before 'current'.
//
NewSA->Next = CurrentSA; NewSA->Prev = CurrentSA->Prev; CurrentSA->Prev = NewSA; }
if (CurrentSA == SecurityAssociationList) { //
// Still at the front of the list.
// New entry becomes new list head.
//
NewSA->Prev = NULL; SecurityAssociationList = NewSA; } else { //
// Add new entry after 'previous'.
//
NewSA->Prev = PrevSA; PrevSA->Next = NewSA; }
InvalidateSecurityState();
return TRUE; }
//* FindSecurityPolicyMatch - Find matching SP entry.
//
// Called with security lock held.
//
SecurityPolicy * FindSecurityPolicyMatch( SecurityPolicy *Start, // Head of list to search.
uint InterfaceIndex, // Interface number to match, 0 to wildcard.
uint PolicyIndex) // Policy number to match, 0 to wildcard.
{ SecurityPolicy *ThisSP;
//
// Search the Security Policy List for a match.
//
for (ThisSP = Start; ThisSP != NULL; ThisSP = ThisSP->Next) { //
// Desired policy must be wildcarded or match.
//
if ((PolicyIndex != 0) && (PolicyIndex != ThisSP->Index)) continue; //
// Interface must be wildcarded or match. Note that the policy,
// as well as the query, may have a wildcarded interface index.
//
if ((InterfaceIndex != 0) && (ThisSP->IFIndex != 0) && (InterfaceIndex != ThisSP->IFIndex)) continue;
break; // Match.
}
return ThisSP; }
//* FindSecurityAssociationMatch - Find SA Entry corresponding to index value.
//
// Called with security lock held.
//
SecurityAssociation * FindSecurityAssociationMatch( ulong Index) // Association number to match, 0 to wildcard.
{ SecurityAssociation *ThisSA;
//
// Search the Security Association List starting with the first SA.
//
for (ThisSA = SecurityAssociationList; ThisSA != NULL; ThisSA = ThisSA->Next) { //
// Desired association must be wildcarded or match.
//
if ((Index == 0) || (Index == ThisSA->Index)) break; }
return ThisSA; }
//* GetSecurityPolicyIndex - Return SP Index or NONE.
//
ulong GetSecurityPolicyIndex( SecurityPolicy *SP) { ulong Index = NONE;
// Get Index from SP.
if (SP != NULL) { Index = SP->Index; }
return Index; }
//* IPSecInit - Initialize the Common SPD.
//
int IPSecInit(void) { SecurityPolicy *SP;
// Allocate memory for Security Policy.
SP = ExAllocatePool(NonPagedPool, sizeof *SP); if (SP == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "IPSecInit - couldn't allocate pool for SP!?!\n")); return FALSE; }
//
// Initialize a default common policy that allows everything.
//
SP->Next = NULL; SP->Prev = NULL;
SP->RemoteAddrField = WILDCARD_VALUE; SP->RemoteAddr = UnspecifiedAddr; SP->RemoteAddrData = UnspecifiedAddr; SP->RemoteAddrSelector = POLICY_SELECTOR;
SP->LocalAddrField = WILDCARD_VALUE; SP->LocalAddr = UnspecifiedAddr; SP->LocalAddrData = UnspecifiedAddr; SP->LocalAddrSelector = POLICY_SELECTOR;
SP->TransportProto = NONE; SP->TransportProtoSelector = POLICY_SELECTOR;
SP->RemotePortField = WILDCARD_VALUE; SP->RemotePort = NONE; SP->RemotePortData = NONE; SP->RemotePortSelector = POLICY_SELECTOR;
SP->LocalPortField = WILDCARD_VALUE; SP->LocalPort = NONE; SP->LocalPortData = NONE; SP->LocalPortSelector = POLICY_SELECTOR;
SP->SecPolicyFlag = IPSEC_BYPASS;
SP->IPSecSpec.Protocol = NONE; SP->IPSecSpec.Mode = NONE; SP->IPSecSpec.RemoteSecGWIPAddr = UnspecifiedAddr;
SP->DirectionFlag = BIDIRECTIONAL; SP->OutboundSA = NULL; SP->InboundSA = NULL; SP->SABundle = NULL; SP->Index = 1; SP->RefCnt = 0; SP->IFIndex = 0;
//
// Initialize the global Security Policy list with the default policy.
//
SecurityPolicyList = SP;
KeInitializeSpinLock(&IPSecLock);
//
// Initialize the security algorithms table.
//
AlgorithmsInit();
return(TRUE); }
//* IPSecUnload
//
// Cleanup and prepare for stack unload.
//
void IPSecUnload(void) { KIRQL OldIrql;
// Get Security Lock.
KeAcquireSpinLock(&IPSecLock, &OldIrql);
//
// Delete all the policies on the global Security Policy list.
// This will take out any associations hanging off them as well.
//
while (SecurityPolicyList != NULL) { DeleteSP(SecurityPolicyList); }
// Release lock.
KeReleaseSpinLock(&IPSecLock, OldIrql); }
//* IPSecBytesToInsert
//
uint IPSecBytesToInsert( IPSecProc *IPSecToDo, uint *TunnelStart, uint *TrailerLength) { uint i, Padding; uint BytesInHeader, BytesToInsert = 0, BytesForTrailer = 0; SecurityAlgorithm *Alg; SecurityAssociation *SA; uint IPSEC_TUNNEL = FALSE;
for (i = 0; i < IPSecToDo->BundleSize; i++) { SA = IPSecToDo[i].SA; Alg = &AlgorithmTable[SA->AlgorithmId];
//
// Calculate bytes to insert for each IPSec header..
//
// Check if this is tunnel or transport mode.
if (IPSecToDo[i].Mode == TUNNEL) { // Outer IPv6 header.
BytesToInsert += sizeof(IPv6Header);
if (!IPSEC_TUNNEL) { // Set the tunnel start location.
*TunnelStart = i; IPSEC_TUNNEL = TRUE; } }
// Check which IPSec protocol.
if (SA->IPSecProto == IP_PROTOCOL_AH) { BytesInHeader = (sizeof(AHHeader) + Alg->ResultSize);
//
// The AH header must be a integral multiple of 64 bits in length.
// Check if padding needs to be added to the ICV result to make
// the Auth Data field a legitimate length.
//
Padding = BytesInHeader % 8; if (Padding != 0) { BytesInHeader += (8 - Padding); } ASSERT(BytesInHeader % 8 == 0);
} else { BytesInHeader = sizeof(ESPHeader); BytesForTrailer += (sizeof(ESPTrailer) + Alg->ResultSize); }
// Store the byte size of the IPSec header.
IPSecToDo[i].ByteSize = BytesInHeader;
// Add the IPSec header size to the total bytes to insert.
BytesToInsert += BytesInHeader; }
// See if our caller wants the trailer length too.
if (TrailerLength != NULL) *TrailerLength = BytesForTrailer;
return BytesToInsert; }
//* IPSecInsertHeaders
//
uint IPSecInsertHeaders( uint Mode, // Transport or Tunnel.
IPSecProc *IPSecToDo, uchar **InsertPoint, uchar *NewMemory, PNDIS_PACKET Packet, uint *TotalPacketSize, uchar *PrevNextHdr, uint TunnelStart, uint *BytesInserted, uint *NumESPTrailers, uint *JUST_ESP) { uint i, NumHeaders = 0; AHHeader *AH; ESPHeader *ESP; uchar NextHeader; uint Padding, j; ESPTrailer *ESPTlr; PNDIS_BUFFER ESPTlrBuffer; uchar *ESPTlrMemory; uint ESPTlrBufSize; NDIS_STATUS Status; SecurityAlgorithm *Alg; SecurityAssociation *SA; uint Action = LOOKUP_CONT;
NextHeader = *PrevNextHdr;
if (Mode == TRANSPORT) { i = 0; if (TunnelStart != NO_TUNNEL) { NumHeaders = TunnelStart; } else { NumHeaders = IPSecToDo->BundleSize; } } else { // Tunnel.
i = TunnelStart; // Get the end of the tunnels.
for (j = TunnelStart + 1; j < IPSecToDo->BundleSize; j++) { if (IPSecToDo[j].Mode == TUNNEL) { // Another tunnel.
NumHeaders = j; break; } } if (NumHeaders == 0) { // No other tunnels.
NumHeaders = IPSecToDo->BundleSize; } }
*JUST_ESP = TRUE;
for (i; i < NumHeaders; i++) { SA = IPSecToDo[i].SA;
if (SA->IPSecProto == IP_PROTOCOL_AH) { *JUST_ESP = FALSE;
// Move insert point up to start of AH Header.
*InsertPoint -= IPSecToDo[i].ByteSize;
*BytesInserted += IPSecToDo[i].ByteSize;
AH = (AHHeader *)(*InsertPoint);
//
// Insert AH Header.
//
AH->NextHeader = NextHeader; // Set previous header's next header field to AH.
NextHeader = IP_PROTOCOL_AH; // Payload length is in 32 bit counts.
AH->PayloadLen = (IPSecToDo[i].ByteSize / 4) - 2; AH->Reserved = 0; AH->SPI = net_long(SA->SPI); // TBD: Note that when we add support for dynamic keying,
// TBD: we'll also need to check for sequence number wrap here.
AH->Seq = net_long(InterlockedIncrement((PLONG)&SA->SequenceNum));
//
// Store point where to put AH Auth Data after authentication.
//
IPSecToDo[i].AuthData = (*InsertPoint) + sizeof(AHHeader);
//
// Zero out Auth Data and padding.
// The Auth Data value will be filled in later.
//
RtlZeroMemory(IPSecToDo[i].AuthData, IPSecToDo[i].ByteSize - sizeof(AHHeader));
} else { // ESP.
Alg = &AlgorithmTable[SA->AlgorithmId];
// Move insert point up to start of ESP Header.
*InsertPoint -= IPSecToDo[i].ByteSize;
*BytesInserted += IPSecToDo[i].ByteSize;
ESP = (ESPHeader *)(*InsertPoint);
//
// Insert ESP Header.
//
ESP->SPI = net_long(SA->SPI); // TBD: Note that when we add support for dynamic keying,
// TBD: we'll also need to check for sequence number wrap here.
ESP->Seq = net_long(InterlockedIncrement((PLONG)&SA->SequenceNum));
//
// Compute padding that needs to be added in ESP Trailer.
// The PadLength and Next header must end of a 4-byte boundary
// with respect to the start of the IPv6 header.
// TotalPacketSize is the length of everything in the original
// packet from the start of the IPv6 header.
//
Padding = *TotalPacketSize % 4;
if (Padding == 0) { Padding = 2; } else if (Padding == 2) { Padding = 0; }
// Adjust total packet size to account for padding.
*TotalPacketSize += Padding;
// Where to start the Authentication for this ESP header.
IPSecToDo[i].Offset = (uint)((*InsertPoint) - NewMemory);
ESPTlrBufSize = Padding + sizeof(ESPTrailer) + Alg->ResultSize;
*BytesInserted += ESPTlrBufSize;
//
// Allocate ESP Trailer.
//
ESPTlrMemory = ExAllocatePool(NonPagedPool, ESPTlrBufSize); if (ESPTlrMemory == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "InsertTransportIPSec: " "Couldn't allocate ESPTlrMemory!?!\n")); Action = LOOKUP_DROP; break; }
NdisAllocateBuffer(&Status, &ESPTlrBuffer, IPv6BufferPool, ESPTlrMemory, ESPTlrBufSize); if (Status != NDIS_STATUS_SUCCESS) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NTOS_ERROR, "InsertTransportIPSec: " "Couldn't allocate ESP Trailer buffer!?!\n")); ExFreePool(ESPTlrMemory); Action = LOOKUP_DROP; break; }
// Format Padding.
for (j = 0; j < Padding; j++) { // Add padding.
*(ESPTlrMemory + j) = j + 1; }
ESPTlr = (ESPTrailer *)(ESPTlrMemory + Padding);
//
// Format ESP Trailer.
//
ESPTlr->PadLength = (uchar)j; ESPTlr->NextHeader = NextHeader; // Set previous header's next header field to ESP.
NextHeader = IP_PROTOCOL_ESP;
//
// Store pointer of where to put ESP Authentication Data.
//
IPSecToDo[i].AuthData = ESPTlrMemory + Padding + sizeof(ESPTrailer);
// Set Authentication Data to 0s (MUTABLE).
RtlZeroMemory(IPSecToDo[i].AuthData, Alg->ResultSize);
// Link the ESP trailer to the back of the buffer chain.
NdisChainBufferAtBack(Packet, ESPTlrBuffer);
// Increment the number of ESP trailers present.
*NumESPTrailers += 1;
} // end of else
} // end of for (i; i < NumHeaders; i++)
*PrevNextHdr = NextHeader;
return Action; }
//* IPSecAdjustMutableFields
//
uint IPSecAdjustMutableFields( uchar *Data, IPv6RoutingHeader *SavedRtHdr) { uint i; uchar NextHeader; IPv6Header UNALIGNED *IP;
//
// Walk original buffer starting at IP header and continuing to the end
// of the mutable headers, zeroing the mutable fields.
//
IP = (IPv6Header UNALIGNED *)Data;
// In VersClassFlow, only the IP version is immutable, so zero the rest.
IP->VersClassFlow = IP_VERSION;
// Hop limit is mutable.
IP->HopLimit = 0;
NextHeader = IP->NextHeader;
Data = (uchar *)(IP + 1);
//
// Loop through the original headers. Zero out the mutable fields.
//
for (;;) { switch (NextHeader) {
case IP_PROTOCOL_AH: case IP_PROTOCOL_ESP: // done.
return LOOKUP_CONT;
case IP_PROTOCOL_HOP_BY_HOP: case IP_PROTOCOL_DEST_OPTS: { IPv6OptionsHeader *NewOptHdr; uint HdrLen, Amount; uchar *Current;
NewOptHdr = (IPv6OptionsHeader *)Data; HdrLen = (NewOptHdr->HeaderExtLength + 1) * EXT_LEN_UNIT; Data += HdrLen;
//
// Run through options to see if any are mutable.
//
Current = (uchar *)NewOptHdr + sizeof(IPv6OptionsHeader); HdrLen -= sizeof(IPv6OptionsHeader); while (HdrLen) { if (*Current == OPT6_PAD_1) { //
// This is the special one byte pad option. Immutable.
//
Current++; HdrLen--; continue; }
if (OPT6_ISMUTABLE(*Current)) { //
// This option's data is mutable. Everything preceeding
// the option data is not.
//
Current++; // Now on option data length byte.
Amount = *Current; // Mutable amount.
Current++; // Now on first data byte.
RtlZeroMemory(Current, Amount);
HdrLen -= Amount + 2; Current += Amount;
} else { //
// This option's data is not mutable.
// Just skip over it.
//
Current++; // Now on option data length byte.
Amount = *Current; HdrLen -= Amount + 2; Current += Amount + 1; } }
NextHeader = NewOptHdr->NextHeader;
break; } case IP_PROTOCOL_ROUTING: { IPv6RoutingHeader *NewRtHdr; IPv6Addr *RecvRtAddr, *SendRtAddr;
// Verify there is a SavedRtHdr.
if (SavedRtHdr == NULL) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "IPSecAdjustMutableFields: " "No Saved routing header")); return LOOKUP_DROP; }
// Point to the saved first routing address.
SendRtAddr = (IPv6Addr *)(SavedRtHdr + 1);
// New buffer routing header.
NewRtHdr = (IPv6RoutingHeader *)Data; // Point to the first routing address.
RecvRtAddr = (IPv6Addr *)(NewRtHdr + 1);
NewRtHdr->SegmentsLeft = 0;
// Copy the IP dest addr to first routing address.
RtlCopyMemory(RecvRtAddr, &IP->Dest, sizeof(IPv6Addr));
for (i = 0; i < (uint)(SavedRtHdr->SegmentsLeft - 1); i++) { //
// Copy the routing addresses as they will look
// at receive.
//
RtlCopyMemory(&RecvRtAddr[i+1], &SendRtAddr[i], sizeof(IPv6Addr)); }
// Copy the last routing address to the IP dest address.
RtlCopyMemory(&IP->Dest, &SendRtAddr[i], sizeof(IPv6Addr));
Data += (NewRtHdr->HeaderExtLength + 1) * 8; NextHeader = NewRtHdr->NextHeader; break; } default: KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INTERNAL_ERROR, "IPSecAdjustMutableFields: Don't support header %d\n", NextHeader)); return LOOKUP_DROP; }// end of switch(NextHeader);
} // end of for (;;)
}
//* IPSecAuthenticatePacket
//
void IPSecAuthenticatePacket( uint Mode, IPSecProc *IPSecToDo, uchar *InsertPoint, uint *TunnelStart, uchar *NewMemory, uchar *EndNewMemory, PNDIS_BUFFER NewBuffer1) { uchar *Data; uint i, NumHeaders = 0, DataLen, j; void *Context; void *VirtualAddr; PNDIS_BUFFER NextBuffer; SecurityAlgorithm *Alg; SecurityAssociation *SA;
if (Mode == TRANSPORT) { i = 0; if (*TunnelStart != NO_TUNNEL) { NumHeaders = *TunnelStart; } else { NumHeaders = IPSecToDo->BundleSize; } } else { // Tunnel.
i = *TunnelStart; // Get the end of the tunnels.
for (j = *TunnelStart + 1; j < IPSecToDo->BundleSize; j++) { if (IPSecToDo[j].Mode == TUNNEL) { // Another tunnel.
NumHeaders = j; break; } } if (NumHeaders == 0) { // No other tunnels.
NumHeaders = IPSecToDo->BundleSize; }
// Set TunnelStart for loop in IPv6Send2().
*TunnelStart = NumHeaders; }
for (i; i < NumHeaders; i++) { SA = IPSecToDo[i].SA; Alg = &AlgorithmTable[SA->AlgorithmId];
if (SA->IPSecProto == IP_PROTOCOL_AH) { uint FirstIPSecHeader = NumHeaders - 1;
// AH starts at the IP header.
Data = InsertPoint;
//
// Check if there are other IPSec headers after this in the
// same IP area (IP_"after"<IP Area>_Data). Other IPSec headers
// need to be ignored in the authentication calculation.
// NOTE: This is not a required IPSec header combination.
//
if (i < FirstIPSecHeader) { uchar *StopPoint; uint n;
n = i + 1;
//
// There are some other IPSec headers.
// Need to authenticate from the IP header to
// the last header before the first IPSec header hit.
// Then if there is no ESP, just authenticate from the
// current IPSec header to the end of the packet.
// If there is ESP, need to ignore the ESP trailers.
//
//
// Calculate start of first IPSec header.
//
if (IPSecToDo[FirstIPSecHeader].SA->IPSecProto == IP_PROTOCOL_AH) { StopPoint = (IPSecToDo[FirstIPSecHeader].AuthData - sizeof(AHHeader)); } else { StopPoint = NewMemory + IPSecToDo[FirstIPSecHeader].Offset; }
// Length from IP to first IPSec header.
DataLen = (uint)(StopPoint - Data);
// Initialize Context.
Context = alloca(Alg->ContextSize); (*Alg->Initialize)(Context, SA->Key);
// Authentication to the first IPSec header.
(*Alg->Operate)(Context, SA->Key, Data, DataLen);
// Set the data start to the current IPSec header.
Data = IPSecToDo[i].AuthData - sizeof(AHHeader); DataLen = (uint)(EndNewMemory - Data);
//
// Authenticate from current IPSec header to the
// end of the new allocated buffer.
//
(*Alg->Operate)(Context, SA->Key, Data, DataLen);
//
// Need to authenticate other buffers if there are any.
// Ignore the ESP trailers.
//
// Check for an ESP header closest to the current IPSec header.
while (n < NumHeaders) { if (IPSecToDo[n].SA->IPSecProto == IP_PROTOCOL_ESP) { break; } n++; }
// Get the next buffer in the packet.
NdisGetNextBuffer(NewBuffer1, &NextBuffer);
while (NextBuffer != NULL) { // Get the start of the data and the data length.
NdisQueryBuffer(NextBuffer, &VirtualAddr, &DataLen); Data = (uchar *)VirtualAddr;
//
// Check if this is the ESP Trailer by seeing if the
// AuthData storage is in the buffer.
//
if (n < NumHeaders) if (IPSecToDo[n].AuthData > Data && IPSecToDo[n].AuthData < (Data + DataLen)) {
// Stop here this is the ESP trailer.
break; }
// Feed the buffer to the Authentication function.
(*Alg->Operate)(Context, SA->Key, Data, DataLen);
// Get the next buffer in the packet.
NdisGetNextBuffer(NextBuffer, &NextBuffer) } // end of while (NextBuffer != NULL)
// Get the Authentication Data.
(*Alg->Finalize)(Context, SA->Key, IPSecToDo[i].AuthData);
// Resume loop for other IPSec headers.
continue; } } else { // ESP.
// ESP starts the authentication at the ESP header.
Data = NewMemory + IPSecToDo[i].Offset; }
DataLen = (uint)(EndNewMemory - Data);
// Initialize Context.
Context = alloca(Alg->ContextSize); (*Alg->Initialize)(Context, SA->Key);
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "\nSend Data[%d]:\n", i)); dump_encoded_mesg(Data, DataLen); #endif
// Feed the new buffer to the Authentication function.
(*Alg->Operate)(Context, SA->Key, Data, DataLen);
// Get the next buffer in the packet.
NdisGetNextBuffer(NewBuffer1, &NextBuffer);
while (NextBuffer != NULL) { // Get the start of the data and the data length.
NdisQueryBuffer(NextBuffer, &VirtualAddr, &DataLen); Data = (uchar *)VirtualAddr;
//
// Check if this is the ESP Trailer by seeing if the
// AuthData storage is in the buffer.
//
if (SA->IPSecProto == IP_PROTOCOL_ESP && IPSecToDo[i].AuthData > Data && IPSecToDo[i].AuthData < (Data + DataLen)) { // Don't include the Authentication Data
// in the ICV calculation.
DataLen = (uint)(IPSecToDo[i].AuthData - Data); #ifdef IPSEC_DEBUG
dump_encoded_mesg(Data, DataLen); #endif
// Feed the buffer to the Authentication function.
(*Alg->Operate)(Context, SA->Key, Data, DataLen); break; } #ifdef IPSEC_DEBUG
dump_encoded_mesg(Data, DataLen); #endif
// Feed the buffer to the Authentication function.
(*Alg->Operate)(Context, SA->Key, Data, DataLen);
// Get the next buffer in the packet.
NdisGetNextBuffer(NextBuffer, &NextBuffer) } // end of while (NextBuffer != NULL)
// Get the Authentication Data.
(*Alg->Finalize)(Context, SA->Key, IPSecToDo[i].AuthData);
#ifdef IPSEC_DEBUG
KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Send Key (%d bytes): ", SA->RawKeyLength)); DumpKey(SA->RawKey, SA->RawKeyLength); KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_INFO_IPSEC, "Send AuthData:\n")); dump_encoded_mesg(IPSecToDo[i].AuthData, Alg->ResultSize); #endif
} // end of for (i = 0; ...)
}
|