// -*- 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. // ASSERT(!"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(&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) { SecurityPolicy *IFSP, *PrevSP = NULL; // // 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. // 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. // 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)); ASSERT(FALSE); } } //* 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; 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; // // 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); 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. // if (1) { SecurityAlgorithm *Alg; uint AuthDataSize; uint PayloadLength; void *Context; IPv6Packet Clone; uint DoNow; 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) { KdPrintEx((DPFLTR_TCPIP6_ID, DPFLTR_NET_ERROR, "EncapsulatingSecurityPaylofadReceive: " "SA requested confidentiality -- unsupported feature\n")); goto ErrorReturn; } // // 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; Interface *IF; // 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, int *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, Buffer, NextBuffer; uchar *ESPTlrMemory; uint ESPTlrBufSize; NDIS_STATUS Status; SecurityAlgorithm *Alg; SecurityAssociation *SA; uint Action = LOOKUP_CONT; uint BufferCount; 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(&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(&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 (;;) return LOOKUP_CONT; } //* 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"_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; ...) }