mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4217 lines
110 KiB
4217 lines
110 KiB
/*++
|
|
|
|
Copyright (c) 1997-2001 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
saapi.c
|
|
|
|
Abstract:
|
|
|
|
This module contains the SAAPI implementation
|
|
|
|
Author:
|
|
|
|
Sanjay Anand (SanjayAn) 12-May-1997
|
|
ChunYe
|
|
|
|
Environment:
|
|
|
|
Kernel mode
|
|
|
|
Revision History:
|
|
|
|
--*/
|
|
|
|
|
|
#include "precomp.h"
|
|
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
#ifdef ALLOC_PRAGMA
|
|
#pragma alloc_text(PAGE, IPSecInitRandom)
|
|
#endif
|
|
|
|
|
|
BOOLEAN
|
|
IPSecInitRandom(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the IPSecRngKey by calling into ksecdd to get 2048 bits of random
|
|
and create the RC4 key.
|
|
|
|
Arguments:
|
|
|
|
Called at PASSIVE level.
|
|
|
|
Return Value:
|
|
|
|
TRUE/FALSE
|
|
|
|
--*/
|
|
{
|
|
UCHAR pBuf[RNG_KEY_SIZE];
|
|
|
|
PAGED_CODE();
|
|
|
|
if (IPSEC_GEN_RANDOM(pBuf, RNG_KEY_SIZE) == FALSE) {
|
|
IPSEC_DEBUG(LOAD, ("IPSEC_GEN_RANDOM failure.\n"));
|
|
return FALSE;
|
|
}
|
|
|
|
//
|
|
// Generate the key control structure.
|
|
//
|
|
IPSEC_RC4_KEY(&IPSecRngKey, RNG_KEY_SIZE, pBuf);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecRngRekey(
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize the IPSecRngKey by calling into ksecdd to get 2048 bits of random
|
|
and create the RC4 key.
|
|
|
|
Arguments:
|
|
|
|
Called at PASSIVE level.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
IPSecInitRandom();
|
|
|
|
IPSEC_DECREMENT(g_ipsec.NumWorkers);
|
|
|
|
#if DBG
|
|
IPSecRngInRekey = 0;
|
|
#endif
|
|
|
|
IPSEC_SET_VALUE(IPSecRngBytes, 0);
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IPSecGenerateRandom(
|
|
IN PUCHAR pBuf,
|
|
IN ULONG BytesNeeded
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Generate a positive pseudo-random number between Lower and Upper bounds;
|
|
simple linear congruential algorithm. ANSI C "rand()" function. Courtesy JameelH.
|
|
|
|
Arguments:
|
|
|
|
LowerBound, UpperBound - range of random number.
|
|
|
|
Return Value:
|
|
|
|
a random number.
|
|
|
|
--*/
|
|
{
|
|
ULONG RngBytes;
|
|
|
|
IPSEC_RC4(&IPSecRngKey, BytesNeeded, pBuf);
|
|
|
|
//
|
|
// Rekey if we have exceeded the threshold.
|
|
//
|
|
RngBytes = IPSEC_ADD_VALUE(IPSecRngBytes, BytesNeeded);
|
|
if (RngBytes <= RNG_REKEY_THRESHOLD &&
|
|
(RngBytes + BytesNeeded) > RNG_REKEY_THRESHOLD) {
|
|
//
|
|
// Create a worker thread to perform the rekey since it has to be done
|
|
// as paged code.
|
|
//
|
|
#if DBG
|
|
ASSERT(IPSecRngInRekey == 0);
|
|
IPSecRngInRekey = 1;
|
|
#endif
|
|
|
|
ExInitializeWorkItem( &IPSecRngQueueItem,
|
|
IPSecRngRekey,
|
|
NULL);
|
|
|
|
ExQueueWorkItem(&IPSecRngQueueItem, DelayedWorkQueue);
|
|
|
|
IPSEC_INCREMENT(g_ipsec.NumWorkers);
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecCleanupOutboundSA(
|
|
IN PSA_TABLE_ENTRY pInboundSA,
|
|
IN PSA_TABLE_ENTRY pOutboundSA,
|
|
IN BOOLEAN fNoDelete
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes an outbound SA.
|
|
|
|
Called with SADB lock held, returns with it.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
KIRQL kIrql;
|
|
|
|
IPSEC_DEBUG(ACQUIRE, ("Deleting assoc (outbound) SA: %lx\n", pOutboundSA));
|
|
|
|
pInboundSA->sa_AssociatedSA = NULL;
|
|
|
|
//
|
|
// de-link from the Filter lists
|
|
//
|
|
if (pOutboundSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
pOutboundSA->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&pOutboundSA->sa_FilterLinkage);
|
|
}
|
|
|
|
//
|
|
// So, we dont delete the Rekeyoriginal SA again.
|
|
//
|
|
if (pOutboundSA->sa_Flags & FLAGS_SA_REKEY_ORI) {
|
|
pOutboundSA->sa_Flags &= ~FLAGS_SA_REKEY_ORI;
|
|
if (pOutboundSA->sa_RekeyLarvalSA) {
|
|
ASSERT(pOutboundSA->sa_RekeyLarvalSA->sa_Flags & FLAGS_SA_REKEY);
|
|
pOutboundSA->sa_RekeyLarvalSA->sa_RekeyOriginalSA = NULL;
|
|
}
|
|
}
|
|
|
|
//
|
|
// invalidate the associated cache entry
|
|
//
|
|
IPSecInvalidateSACacheEntry(pOutboundSA);
|
|
|
|
pOutboundSA->sa_State = STATE_SA_ZOMBIE;
|
|
pOutboundSA->sa_AssociatedSA = NULL;
|
|
|
|
if (pOutboundSA->sa_Flags & FLAGS_SA_HW_PLUMBED) {
|
|
IPSecDelHWSAAtDpc(pOutboundSA);
|
|
}
|
|
|
|
IPSEC_DEBUG(REF, ("Out Deref IPSecCleanupOutboundSA\n"));
|
|
IPSecStopTimerDerefSA(pOutboundSA);
|
|
|
|
IPSEC_INC_STATISTIC(dwNumKeyDeletions);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecCleanupLarvalSA(
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the LarvalSA.
|
|
|
|
Called with Outbound Lock held, returns with it.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pOutboundSA;
|
|
KIRQL kIrql1;
|
|
KIRQL kIrql2;
|
|
|
|
//
|
|
// Also remove from Pending list if queued there.
|
|
//
|
|
ACQUIRE_LOCK(&g_ipsec.AcquireInfo.Lock, &kIrql1);
|
|
if (pSA->sa_Flags & FLAGS_SA_PENDING) {
|
|
ASSERT(pSA->sa_State == STATE_SA_LARVAL);
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecSAExpired: Removed from pending too: %lx\n", pSA));
|
|
IPSecRemoveEntryList(&pSA->sa_PendingLinkage);
|
|
pSA->sa_Flags &= ~FLAGS_SA_PENDING;
|
|
}
|
|
RELEASE_LOCK(&g_ipsec.AcquireInfo.Lock, kIrql1);
|
|
|
|
//
|
|
// Flush all the queued packets
|
|
//
|
|
IPSecFlushQueuedPackets(pSA, STATUS_TIMEOUT);
|
|
|
|
//
|
|
// remove from inbound sa list
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql1);
|
|
IPSecRemoveSPIEntry(pSA);
|
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql1);
|
|
|
|
//
|
|
// invalidate the associated cache entry
|
|
//
|
|
ACQUIRE_LOCK(&pSA->sa_Lock, &kIrql2);
|
|
if (pSA->sa_AcquireCtx) {
|
|
IPSecInvalidateHandle(pSA->sa_AcquireCtx);
|
|
pSA->sa_AcquireCtx = NULL;
|
|
}
|
|
RELEASE_LOCK(&pSA->sa_Lock, kIrql2);
|
|
|
|
IPSecInvalidateSACacheEntry(pSA);
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
pSA->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&pSA->sa_FilterLinkage);
|
|
}
|
|
|
|
if (pSA->sa_RekeyOriginalSA) {
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_REKEY);
|
|
ASSERT(pSA->sa_RekeyOriginalSA->sa_RekeyLarvalSA == pSA);
|
|
ASSERT(pSA->sa_RekeyOriginalSA->sa_Flags & FLAGS_SA_REKEY_ORI);
|
|
|
|
pSA->sa_RekeyOriginalSA->sa_Flags &= ~FLAGS_SA_REKEY_ORI;
|
|
pSA->sa_RekeyOriginalSA->sa_RekeyLarvalSA = NULL;
|
|
pSA->sa_RekeyOriginalSA = NULL;
|
|
}
|
|
|
|
if (pOutboundSA = pSA->sa_AssociatedSA) {
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumActiveAssociations);
|
|
IPSEC_DEC_TUNNELS(pOutboundSA);
|
|
IPSEC_DECREMENT(g_ipsec.NumOutboundSAs);
|
|
|
|
IPSecCleanupOutboundSA(pSA, pOutboundSA, FALSE);
|
|
}
|
|
|
|
IPSEC_DEBUG(REF, ("In Deref DeleteLarvalSA\n"));
|
|
IPSecStopTimerDerefSA(pSA);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecDeleteLarvalSA(
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the LarvalSA.
|
|
|
|
Called with Outbound Lock held, returns with it.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
KIRQL kIrql;
|
|
|
|
ASSERT((pSA->sa_Flags & FLAGS_SA_OUTBOUND) == 0);
|
|
|
|
//
|
|
// Remove from larval list
|
|
//
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
|
IPSecRemoveEntryList(&pSA->sa_LarvalLinkage);
|
|
IPSEC_DEC_STATISTIC(dwNumPendingKeyOps);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
|
|
//
|
|
// Cleanup the rest of larval SA
|
|
//
|
|
IPSecCleanupLarvalSA(pSA);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecDeleteInboundSA(
|
|
IN PSA_TABLE_ENTRY pInboundSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the corresponding outbound SA, and then deletes itself.
|
|
|
|
Called with Outbound Lock held, returns with it.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pOutboundSA;
|
|
PSA_TABLE_ENTRY pSA;
|
|
KIRQL kIrql;
|
|
PLIST_ENTRY pEntry;
|
|
PFILTER pFilter;
|
|
|
|
ACQUIRE_LOCK(&g_ipsec.AcquireInfo.Lock, &kIrql);
|
|
IPSecNotifySAExpiration(pInboundSA, NULL, kIrql, FALSE);
|
|
|
|
if (pOutboundSA = pInboundSA->sa_AssociatedSA) {
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumActiveAssociations);
|
|
IPSEC_DEC_TUNNELS(pOutboundSA);
|
|
IPSEC_DECREMENT(g_ipsec.NumOutboundSAs);
|
|
|
|
IPSecCleanupOutboundSA(pInboundSA, pOutboundSA, FALSE);
|
|
}
|
|
|
|
IPSEC_DEBUG(ACQUIRE, ("Deleting inbound SA: %lx\n", pInboundSA));
|
|
|
|
//
|
|
// remove from inbound sa list
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql);
|
|
IPSecRemoveSPIEntry(pInboundSA);
|
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql);
|
|
|
|
//
|
|
// invalidate the associated cache entry
|
|
//
|
|
IPSecInvalidateSACacheEntry(pInboundSA);
|
|
|
|
//
|
|
// also remove from the filter list
|
|
//
|
|
if (pInboundSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
pInboundSA->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&pInboundSA->sa_FilterLinkage);
|
|
}
|
|
|
|
if (pInboundSA->sa_Flags & FLAGS_SA_HW_PLUMBED) {
|
|
IPSecDelHWSAAtDpc(pInboundSA);
|
|
}
|
|
|
|
ASSERT(pInboundSA->sa_AssociatedSA == NULL);
|
|
|
|
IPSEC_DEBUG(REF, ("In Deref DeleteInboundSA\n"));
|
|
IPSecStopTimerDerefSA(pInboundSA);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecExpireInboundSA(
|
|
IN PSA_TABLE_ENTRY pInboundSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deletes the corresponding outbound SA, and places itself (inbound) on timer
|
|
Queue for later.
|
|
|
|
NOTE: Called with SADB lock held.
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pOutboundSA;
|
|
KIRQL OldIrq;
|
|
KIRQL kIrql;
|
|
|
|
if (pInboundSA->sa_Flags & FLAGS_SA_HW_PLUMBED) {
|
|
IPSecDelHWSAAtDpc(pInboundSA);
|
|
}
|
|
|
|
ACQUIRE_LOCK(&g_ipsec.AcquireInfo.Lock, &OldIrq);
|
|
IPSecNotifySAExpiration(pInboundSA, NULL, OldIrq, FALSE);
|
|
|
|
if (pOutboundSA = pInboundSA->sa_AssociatedSA) {
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumActiveAssociations);
|
|
IPSEC_DEC_TUNNELS(pOutboundSA);
|
|
IPSEC_DECREMENT(g_ipsec.NumOutboundSAs);
|
|
|
|
IPSecCleanupOutboundSA(pInboundSA, pOutboundSA, TRUE);
|
|
}
|
|
|
|
IPSEC_DEBUG(ACQUIRE, ("Queueing inbound SA: %lx\n", pInboundSA));
|
|
|
|
//
|
|
// Place this on the timer Q so it gets cleared when the next interval hits.
|
|
//
|
|
ACQUIRE_LOCK(&pInboundSA->sa_Lock, &kIrql);
|
|
|
|
if (pInboundSA->sa_AcquireCtx) {
|
|
IPSecInvalidateHandle(pInboundSA->sa_AcquireCtx);
|
|
pInboundSA->sa_AcquireCtx = NULL;
|
|
}
|
|
|
|
IPSecStartSATimer( pInboundSA,
|
|
IPSecSAExpired,
|
|
IPSEC_INBOUND_KEEPALIVE_TIME);
|
|
|
|
RELEASE_LOCK(&pInboundSA->sa_Lock, kIrql);
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecCheckInboundSA(
|
|
IN PSA_STRUCT pSAStruct,
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Ensures that the SA being updated is actually the SA we initially
|
|
kicked off negotiation for.
|
|
|
|
Arguments:
|
|
|
|
pSAInfo - information about the SA
|
|
|
|
pSA - SA to be populated.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the buffer is to be held on to, the normal case.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER uliSrcDstAddr;
|
|
LARGE_INTEGER uliSrcDstMask;
|
|
LARGE_INTEGER uliProtoSrcDstPort;
|
|
PSECURITY_ASSOCIATION pSAInfo = &pSAStruct->SecAssoc[pSAStruct->NumSAs - 1];
|
|
|
|
|
|
IPSEC_BUILD_SRC_DEST_ADDR( uliSrcDstAddr,
|
|
pSAStruct->InstantiatedFilter.SrcAddr,
|
|
pSAStruct->InstantiatedFilter.DestAddr);
|
|
|
|
IPSEC_BUILD_SRC_DEST_MASK( uliSrcDstMask,
|
|
pSAStruct->InstantiatedFilter.SrcMask,
|
|
pSAStruct->InstantiatedFilter.DestMask);
|
|
|
|
IPSEC_BUILD_PROTO_PORT_LI( uliProtoSrcDstPort,
|
|
pSAStruct->InstantiatedFilter.Protocol,
|
|
pSAStruct->InstantiatedFilter.SrcPort,
|
|
pSAStruct->InstantiatedFilter.DestPort);
|
|
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecCheckInboundSA: S: %lx-%lx, D: %lx-%lx\n",
|
|
SRC_ADDR, SRC_MASK, DEST_ADDR, DEST_MASK));
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecCheckInboundSA: SA->S: %lx-%lx, SA->D: %lx-%lx\n",
|
|
pSA->SA_SRC_ADDR, pSA->SA_SRC_MASK, pSA->SA_DEST_ADDR, pSA->SA_DEST_MASK));
|
|
|
|
if ((pSA->sa_TunnelAddr != 0) || (pSA->sa_Flags & FLAGS_SA_TUNNEL)) {
|
|
if (((SRC_ADDR & pSA->SA_SRC_MASK) ==
|
|
(pSA->SA_SRC_ADDR & pSA->SA_SRC_MASK)) &&
|
|
((DEST_ADDR & pSA->SA_DEST_MASK) ==
|
|
(pSA->SA_DEST_ADDR & pSA->SA_DEST_MASK)) &&
|
|
(pSA->sa_SPI == pSAInfo->SPI)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
if ((uliSrcDstAddr.QuadPart == pSA->sa_uliSrcDstAddr.QuadPart) &&
|
|
(pSA->sa_SPI == pSAInfo->SPI)) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
return STATUS_FAIL_CHECK;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IPSecIsWeakDESKey(
|
|
IN PUCHAR Key
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks for weak DES keys
|
|
|
|
Arguments:
|
|
|
|
Key - the key to be checked.
|
|
|
|
Return Value:
|
|
|
|
TRUE/FALSE
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
ULONG j;
|
|
|
|
for (j = 0; j < NUM_WEAK_KEYS; j++) {
|
|
if (IPSecEqualMemory(Key, weak_keys[j], DES_BLOCKLEN)) {
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
BOOLEAN
|
|
IPSecIsWeak3DESKey(
|
|
IN PUCHAR Key
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Checks for weak Triple DES keys
|
|
|
|
Arguments:
|
|
|
|
Key - the key to be checked.
|
|
|
|
Return Value:
|
|
|
|
TRUE/FALSE
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
if (IPSecEqualMemory(Key, Key + DES_BLOCKLEN, DES_BLOCKLEN) ||
|
|
IPSecEqualMemory(Key + DES_BLOCKLEN, Key + 2 * DES_BLOCKLEN, DES_BLOCKLEN)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecPopulateSA(
|
|
IN PSA_STRUCT pSAStruct,
|
|
IN ULONG KeyLen,
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Populates an SA with info passed in the SECURITY_ASSOCIATION block
|
|
|
|
Arguments:
|
|
|
|
pSAInfo - information about the SA
|
|
|
|
KeyLen - the length of the composite key (we do the slicing/dicing here)
|
|
|
|
pSA - SA to be populated.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the buffer is to be held on to, the normal case.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSECURITY_ASSOCIATION pSAInfo = &pSAStruct->SecAssoc[0];
|
|
ULONG Index;
|
|
ULONG len = 0;
|
|
|
|
IPSEC_BUILD_SRC_DEST_ADDR( pSA->sa_uliSrcDstAddr,
|
|
pSAStruct->InstantiatedFilter.SrcAddr,
|
|
pSAStruct->InstantiatedFilter.DestAddr);
|
|
|
|
IPSEC_BUILD_SRC_DEST_MASK( pSA->sa_uliSrcDstMask,
|
|
pSAStruct->InstantiatedFilter.SrcMask,
|
|
pSAStruct->InstantiatedFilter.DestMask);
|
|
|
|
IPSEC_BUILD_PROTO_PORT_LI( pSA->sa_uliProtoSrcDstPort,
|
|
pSAStruct->InstantiatedFilter.Protocol,
|
|
pSAStruct->InstantiatedFilter.SrcPort,
|
|
pSAStruct->InstantiatedFilter.DestPort);
|
|
|
|
if ((pSAStruct->NumSAs < 1) ||
|
|
(pSAStruct->NumSAs > MAX_SAS)) {
|
|
IPSEC_DEBUG(SAAPI, ("Invalid NumOps count: %d\n", pSAStruct->NumSAs));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// If inbound SA, ensure that the last SPI is the one we returned.
|
|
//
|
|
if (!(pSA->sa_Flags & FLAGS_SA_OUTBOUND)) {
|
|
if (pSA->sa_SPI != pSAStruct->SecAssoc[pSAStruct->NumSAs - 1].SPI) {
|
|
IPSEC_DEBUG(SAAPI, ("SPI in invalid location: SPI: %lx, in loc: %lx\n",
|
|
pSA->sa_SPI,
|
|
pSAStruct->SecAssoc[pSAStruct->NumSAs - 1].SPI));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
if (pSAStruct->Flags & IPSEC_SA_TUNNEL) {
|
|
IPSEC_DEBUG(TUNNEL, ("SA %lx tunneled to %lx\n", pSA, pSAStruct->TunnelAddr));
|
|
pSA->sa_TunnelAddr = pSAStruct->TunnelAddr;
|
|
pSA->sa_Flags |= FLAGS_SA_TUNNEL;
|
|
}
|
|
|
|
if (pSAStruct->Flags & IPSEC_SA_DISABLE_IDLE_OUT) {
|
|
pSA->sa_Flags |= FLAGS_SA_DISABLE_IDLE_OUT;
|
|
}
|
|
|
|
if (pSAStruct->Flags & IPSEC_SA_DISABLE_ANTI_REPLAY_CHECK) {
|
|
pSA->sa_Flags |= FLAGS_SA_DISABLE_ANTI_REPLAY_CHECK;
|
|
}
|
|
|
|
if (pSAStruct->Flags & IPSEC_SA_DISABLE_LIFETIME_CHECK) {
|
|
pSA->sa_Flags |= FLAGS_SA_DISABLE_LIFETIME_CHECK;
|
|
}
|
|
|
|
pSA->sa_NumOps = pSAStruct->NumSAs;
|
|
pSA->sa_Lifetime = pSAStruct->Lifetime;
|
|
pSA->sa_TruncatedLen = TRUNCATED_HASH_LEN;
|
|
pSA->sa_ReplayLen = sizeof(ULONG);
|
|
|
|
pSA->sa_QMPFSGroup = pSAStruct->dwQMPFSGroup;
|
|
RtlCopyMemory( &pSA->sa_CookiePair,
|
|
&pSAStruct->CookiePair,
|
|
sizeof(IKE_COOKIE_PAIR));
|
|
|
|
for (Index = 0; Index < pSAStruct->NumSAs; Index++) {
|
|
pSAInfo = &pSAStruct->SecAssoc[Index];
|
|
pSA->sa_OtherSPIs[Index] = pSAInfo->SPI;
|
|
pSA->sa_Operation[Index] = pSAInfo->Operation;
|
|
pSA->sa_ReplaySendSeq[Index] = pSA->sa_ReplayStartPoint;
|
|
pSA->sa_ReplayLastSeq[Index] = pSA->sa_ReplayStartPoint + 1;
|
|
|
|
//
|
|
// Now parse the Algorithm info..
|
|
//
|
|
switch (pSA->sa_Operation[Index]) {
|
|
case None:
|
|
IPSEC_DEBUG(ACQUIRE, ("NULL operation.\n"));
|
|
if (pSA->sa_NumOps > 1) {
|
|
IPSEC_DEBUG(SAAPI, ("Invalid NumOps count; none specified, but more ops than 1\n"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
break;
|
|
|
|
case Auth: {
|
|
pSA->INT_ALGO(Index) = pSAInfo->EXT_INT_ALGO;
|
|
|
|
if (pSA->INT_ALGO(Index) >= IPSEC_AH_MAX) {
|
|
IPSEC_DEBUG(SAAPI, ("Invalid int algo: %d %d\n", pSA->INT_ALGO(Index), IPSEC_AH_MAX));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pSA->INT_KEYLEN(Index) = pSAInfo->EXT_INT_KEYLEN;
|
|
pSA->INT_ROUNDS(Index) = pSAInfo->EXT_INT_ROUNDS;
|
|
|
|
//
|
|
// Make sure the right key len was passed in
|
|
//
|
|
if (KeyLen > 0 && pSAInfo->EXT_INT_KEYLEN == (KeyLen - len)) {
|
|
IPSEC_DEBUG(SAAPI, ("Key len more than reserved, allocing new keys\n"));
|
|
|
|
if (!(pSA->INT_KEY(Index) = IPSecAllocateKeyBuffer(KeyLen))) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
RtlCopyMemory( pSA->INT_KEY(Index),
|
|
(UCHAR UNALIGNED *)(pSAStruct->KeyMat + len),
|
|
pSAInfo->EXT_INT_KEYLEN);
|
|
} else {
|
|
//
|
|
// bogus - reject
|
|
//
|
|
IPSEC_DEBUG(SAAPI, ("AH: Key len is bogus - extra bytes: %d, keylen in struct: %d.\n",
|
|
KeyLen-len,
|
|
pSAInfo->EXT_INT_KEYLEN));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
len = pSAInfo->EXT_INT_KEYLEN;
|
|
|
|
break;
|
|
}
|
|
|
|
case Encrypt: {
|
|
pSA->INT_ALGO(Index) = pSAInfo->EXT_INT_ALGO;
|
|
|
|
if (pSA->INT_ALGO(Index) >= IPSEC_AH_MAX) {
|
|
IPSEC_DEBUG(SAAPI, ("Invalid int algo: %d %d\n", pSA->INT_ALGO(Index), IPSEC_AH_MAX));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if (pSA->INT_ALGO(Index) == IPSEC_AH_NONE) {
|
|
IPSEC_DEBUG(SAAPI, ("None Auth algo\n"));
|
|
//pSA->sa_TruncatedLen = 0;
|
|
}
|
|
|
|
pSA->INT_KEYLEN(Index) = pSAInfo->EXT_INT_KEYLEN;
|
|
pSA->INT_ROUNDS(Index) = pSAInfo->EXT_INT_ROUNDS;
|
|
|
|
pSA->CONF_ALGO(Index) = pSAInfo->EXT_CONF_ALGO;
|
|
|
|
if (pSA->CONF_ALGO(Index) >= IPSEC_ESP_MAX) {
|
|
IPSEC_DEBUG(SAAPI, ("Invalid conf algo: %d %d\n", pSA->CONF_ALGO(Index), IPSEC_ESP_MAX));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((pSA->CONF_ALGO(Index) == IPSEC_ESP_DES) ||
|
|
(pSA->CONF_ALGO(Index) == IPSEC_ESP_3_DES) ||
|
|
(pSA->CONF_ALGO(Index) == IPSEC_ESP_NONE)) {
|
|
LARGE_INTEGER Li;
|
|
|
|
NdisGetCurrentSystemTime(&Li);
|
|
pSA->sa_ivlen = DES_BLOCKLEN;
|
|
|
|
*(UNALIGNED ULONG *)&pSA->sa_iv[Index][0] = Li.LowPart;
|
|
*(UNALIGNED ULONG *)&pSA->sa_iv[Index][4] = Li.HighPart;
|
|
IPSecGenerateRandom((PUCHAR)&pSA->sa_iv[Index][0], DES_BLOCKLEN);
|
|
|
|
IPSEC_DEBUG(SAAPI, ("IV: %lx-%lx\n", *(PULONG)&pSA->sa_iv[Index][0], *(PULONG)&pSA->sa_iv[Index][4]));
|
|
|
|
pSA->CONF_KEYLEN(Index) = pSAInfo->EXT_CONF_KEYLEN;
|
|
pSA->CONF_ROUNDS(Index) = pSAInfo->EXT_CONF_ROUNDS;
|
|
|
|
//
|
|
// Make sure the right key len was passed in
|
|
//
|
|
if ((KeyLen-len == pSAStruct->KeyLen) &&
|
|
(pSAInfo->EXT_INT_KEYLEN + pSAInfo->EXT_CONF_KEYLEN <= KeyLen-len)) {
|
|
|
|
//
|
|
// confKeyMatLen is the amount of conf key material that came down.
|
|
// this is the reduced (weakened) length for export.
|
|
// it is expanded to the real length later.
|
|
//
|
|
ULONG confKeyMatLen = pSAInfo->EXT_CONF_KEYLEN;
|
|
ULONG realConfKeyLen = 0;
|
|
|
|
realConfKeyLen = confKeyMatLen;
|
|
|
|
if (pSA->CONF_ALGO(Index) == IPSEC_ESP_DES) {
|
|
if (pSAInfo->EXT_CONF_KEYLEN != DES_BLOCKLEN) {
|
|
ASSERT(FALSE);
|
|
IPSEC_DEBUG(SAAPI, ("Bad DES key length: pSAInfo->EXT_CONF_KEYLEN: %lx, conf: %lx, DES_BLOCKLEN: %lx\n",
|
|
pSAInfo->EXT_CONF_KEYLEN, confKeyMatLen, DES_BLOCKLEN));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
} else if (pSA->CONF_ALGO(Index) == IPSEC_ESP_3_DES) {
|
|
if (pSAInfo->EXT_CONF_KEYLEN != 3 * DES_BLOCKLEN) {
|
|
ASSERT(FALSE);
|
|
IPSEC_DEBUG(SAAPI, ("Bad 3DES key length\n"));
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
IPSEC_DEBUG(SAAPI, ("Key len more than reserved, allocing new keys\n"));
|
|
if (pSAInfo->EXT_INT_KEYLEN > 0 &&
|
|
!(pSA->INT_KEY(Index) = IPSecAllocateKeyBuffer(pSAInfo->EXT_INT_KEYLEN))) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (realConfKeyLen > 0 &&
|
|
!(pSA->CONF_KEY(Index) = IPSecAllocateKeyBuffer(realConfKeyLen))) {
|
|
if (pSA->INT_KEY(Index)) {
|
|
IPSecFreeKeyBuffer(pSA->INT_KEY(Index));
|
|
pSA->INT_KEY(Index) = NULL;
|
|
}
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
if (pSA->CONF_KEY(Index) && confKeyMatLen) {
|
|
RtlCopyMemory( pSA->CONF_KEY(Index),
|
|
pSAStruct->KeyMat,
|
|
confKeyMatLen);
|
|
|
|
if (confKeyMatLen < realConfKeyLen) {
|
|
if (pSA->INT_KEY(Index)) {
|
|
IPSecFreeKeyBuffer(pSA->INT_KEY(Index));
|
|
pSA->INT_KEY(Index) = NULL;
|
|
}
|
|
if (pSA->CONF_KEY(Index)) {
|
|
IPSecFreeKeyBuffer(pSA->CONF_KEY(Index));
|
|
pSA->CONF_KEY(Index) = NULL;
|
|
}
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
if ((pSA->CONF_ALGO(Index) == IPSEC_ESP_DES &&
|
|
IPSecIsWeakDESKey(pSA->CONF_KEY(Index))) ||
|
|
(pSA->CONF_ALGO(Index) == IPSEC_ESP_3_DES &&
|
|
IPSecIsWeak3DESKey(pSA->CONF_KEY(Index)))) {
|
|
PSA_TABLE_ENTRY pLarvalSA;
|
|
|
|
IPSEC_DEBUG(SAAPI, ("Got a weak key!!: %lx\n", pSA->CONF_KEY(Index)));
|
|
//
|
|
// if initiator, re-start a new negotiation and throw away this one
|
|
//
|
|
if (pSA->sa_Flags & FLAGS_SA_INITIATOR) {
|
|
IPSecNegotiateSA( pSA->sa_Filter,
|
|
pSA->sa_uliSrcDstAddr,
|
|
pSA->sa_uliProtoSrcDstPort,
|
|
pSA->sa_NewMTU,
|
|
&pLarvalSA,
|
|
pSA->sa_DestType);
|
|
IPSecQueuePacket(pLarvalSA, pSA->sa_BlockedBuffer);
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
} else {
|
|
if (pSA->CONF_ALGO(Index) != IPSEC_ESP_NONE) {
|
|
IPSEC_DEBUG(SAAPI, ("Algo: %lx with no keymat!!: %lx\n", pSA->CONF_ALGO(Index)));
|
|
ASSERT(FALSE);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
pSA->sa_ivlen = 0;
|
|
}
|
|
|
|
if (pSAInfo->EXT_INT_KEYLEN > 0) {
|
|
RtlCopyMemory( pSA->INT_KEY(Index),
|
|
(UCHAR UNALIGNED *)(pSAStruct->KeyMat + pSAInfo->EXT_CONF_KEYLEN),
|
|
pSAInfo->EXT_INT_KEYLEN);
|
|
}
|
|
|
|
len = pSAInfo->EXT_CONF_KEYLEN + pSAInfo->EXT_INT_KEYLEN;
|
|
} else {
|
|
//
|
|
// bogus - reject
|
|
//
|
|
IPSEC_DEBUG(SAAPI, ("ESP: Key len is bogus - extra bytes: %lx, keylen in struct: %lx.\n",
|
|
KeyLen-len,
|
|
pSAInfo->EXT_INT_KEYLEN + pSAInfo->EXT_CONF_KEYLEN));
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
|
|
default:
|
|
IPSEC_DEBUG(SAAPI, ("IPSecPopulateSA: Bad operation\n"));
|
|
break;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecCreateSA(
|
|
OUT PSA_TABLE_ENTRY *ppSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a Security Association block.
|
|
|
|
Arguments:
|
|
|
|
ppSA - returns the SA pointer
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the buffer is to be held on to, the normal case.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pSA;
|
|
|
|
IPSEC_DEBUG(SAAPI, ("Entering IPSecCreateSA\n"));
|
|
|
|
pSA = IPSecAllocateMemory(sizeof(SA_TABLE_ENTRY), IPSEC_TAG_SA);
|
|
|
|
if (!pSA) {
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
IPSecZeroMemory(pSA, sizeof(SA_TABLE_ENTRY));
|
|
pSA->sa_Signature = IPSEC_SA_SIGNATURE;
|
|
pSA->sa_NewMTU = MAX_LONG;
|
|
|
|
#if DBG
|
|
pSA->sa_d1 = IPSEC_SA_D_1;
|
|
pSA->sa_d2 = IPSEC_SA_D_2;
|
|
pSA->sa_d3 = IPSEC_SA_D_3;
|
|
pSA->sa_d4 = IPSEC_SA_D_4;
|
|
#endif
|
|
|
|
INIT_LOCK(&pSA->sa_Lock);
|
|
|
|
InitializeListHead(&pSA->sa_SPILinkage);
|
|
InitializeListHead(&pSA->sa_FilterLinkage);
|
|
InitializeListHead(&pSA->sa_LarvalLinkage);
|
|
InitializeListHead(&pSA->sa_PendingLinkage);
|
|
|
|
pSA->sa_Reference = 1;
|
|
pSA->sa_State = STATE_SA_CREATED;
|
|
pSA->sa_ExpiryTime = IPSEC_SA_EXPIRY_TIME;
|
|
|
|
*ppSA = pSA;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
PSA_TABLE_ENTRY
|
|
IPSecLookupSABySPI(
|
|
IN tSPI SPI,
|
|
IN IPAddr DestAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the SA given the SPI and Filter variables.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
KIRQL kIrql;
|
|
PSA_TABLE_ENTRY pSA;
|
|
|
|
AcquireReadLock(&g_ipsec.SPIListLock, &kIrql);
|
|
pSA = IPSecLookupSABySPIWithLock(SPI, DestAddr);
|
|
ReleaseReadLock(&g_ipsec.SPIListLock, kIrql);
|
|
return pSA;
|
|
}
|
|
|
|
|
|
PSA_TABLE_ENTRY
|
|
IPSecLookupSABySPIWithLock(
|
|
IN tSPI SPI,
|
|
IN IPAddr DestAddr
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the SA given the SPI and Filter variables.
|
|
|
|
NOTE: Always call with the SPIListLock held.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PSA_HASH pHash;
|
|
PLIST_ENTRY pEntry;
|
|
PSA_TABLE_ENTRY pSA;
|
|
|
|
//
|
|
// get to hash bucket
|
|
//
|
|
IPSEC_HASH_SPI(DestAddr, SPI, pHash);
|
|
|
|
//
|
|
// search for specific entry in collision chain
|
|
//
|
|
for ( pEntry = pHash->SAList.Flink;
|
|
pEntry != &pHash->SAList;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_SPILinkage);
|
|
|
|
if (pSA->sa_TunnelAddr) {
|
|
if ((DestAddr == pSA->sa_TunnelAddr) &&
|
|
(pSA->sa_SPI == SPI)) {
|
|
IPSEC_DEBUG(HASH, ("Matched Tunnel entry: %lx\n", pSA));
|
|
|
|
return pSA;
|
|
}
|
|
} else {
|
|
if ((DestAddr == pSA->SA_DEST_ADDR) &&
|
|
(pSA->sa_SPI == SPI)) {
|
|
|
|
IPSEC_DEBUG(HASH, ("Matched entry: %lx\n", pSA));
|
|
return pSA;
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// no entry found
|
|
//
|
|
return NULL;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecLookupSAByAddr(
|
|
IN ULARGE_INTEGER uliSrcDstAddr,
|
|
IN ULARGE_INTEGER uliProtoSrcDstPort,
|
|
OUT PFILTER *ppFilter,
|
|
OUT PSA_TABLE_ENTRY *ppSA,
|
|
OUT PSA_TABLE_ENTRY *ppNextSA,
|
|
OUT PSA_TABLE_ENTRY *ppTunnelSA,
|
|
IN BOOLEAN fOutbound,
|
|
IN BOOLEAN fFWPacket,
|
|
IN BOOLEAN fBypass
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the SA given the relevant addresses.
|
|
|
|
Arguments:
|
|
|
|
uliSrcDstAddr - src/dest IP addr
|
|
uliProtoSrcDstPort - protocol, src/dest port
|
|
ppFilter - filter found
|
|
ppSA - SA found
|
|
ppTunnelSA - tunnel SA found
|
|
fOutbound - direction flag
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - both filter and SA found
|
|
STATUS_UNSUCCESSFUL - none found
|
|
STATUS_PENDING - filter found, but no SA
|
|
|
|
Notes:
|
|
|
|
Called with SADBLock held.
|
|
|
|
--*/
|
|
{
|
|
PFILTER pFilter;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pFilterList;
|
|
PLIST_ENTRY pSAChain;
|
|
PSA_TABLE_ENTRY pSA;
|
|
REGISTER ULARGE_INTEGER uliPort;
|
|
REGISTER ULARGE_INTEGER uliAddr;
|
|
BOOLEAN fFound = FALSE;
|
|
|
|
*ppSA = NULL;
|
|
*ppFilter = NULL;
|
|
*ppTunnelSA = NULL;
|
|
|
|
//
|
|
// Search in Tunnel filters list
|
|
//
|
|
pFilterList = IPSecResolveFilterList(TRUE, fOutbound);
|
|
|
|
for ( pEntry = pFilterList->Flink;
|
|
pEntry != pFilterList;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
if (fBypass && IS_EXEMPT_FILTER(pFilter)) {
|
|
//
|
|
// Don't search block/pass-thru filters for host bypass traffic
|
|
//
|
|
continue;
|
|
}
|
|
|
|
uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pFilter->uliSrcDstMask.QuadPart;
|
|
uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pFilter->uliProtoSrcDstMask.QuadPart;
|
|
|
|
if ((uliAddr.QuadPart == pFilter->uliSrcDstAddr.QuadPart) &&
|
|
(uliPort.QuadPart == pFilter->uliProtoSrcDstPort.QuadPart)) {
|
|
IPSEC_DEBUG(HASH, ("Matched entry: %lx\n", pFilter));
|
|
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fFound) {
|
|
fFound = FALSE;
|
|
//
|
|
// Search for the particular SA now.
|
|
//
|
|
pSAChain = IPSecResolveSAChain(pFilter, fOutbound? DEST_ADDR: SRC_ADDR);
|
|
|
|
for ( pEntry = pSAChain->Flink;
|
|
pEntry != pSAChain;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_TUNNEL);
|
|
|
|
if (pFilter->TunnelAddr != 0) {
|
|
//
|
|
// match the outbound flag also
|
|
//
|
|
IPSEC_DEBUG(HASH, ("Matched specific tunnel entry: %lx\n", pSA));
|
|
ASSERT(fOutbound == (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_OUTBOUND) != 0));
|
|
fFound = TRUE;
|
|
*ppTunnelSA = pSA;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fFound) {
|
|
fFound = FALSE;
|
|
*ppFilter = pFilter;
|
|
} else {
|
|
//
|
|
// Found a filter entry, but need to negotiate keys
|
|
//
|
|
*ppFilter = pFilter;
|
|
return STATUS_PENDING;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Search in Masked filters list
|
|
//
|
|
pFilterList = IPSecResolveFilterList(FALSE, fOutbound);
|
|
|
|
for ( pEntry = pFilterList->Flink;
|
|
pEntry != pFilterList;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
if (fFWPacket && !IS_EXEMPT_FILTER(pFilter)) {
|
|
//
|
|
// Search only block/pass-thru filters in forward path
|
|
//
|
|
continue;
|
|
}
|
|
|
|
if (fBypass && IS_EXEMPT_FILTER(pFilter)) {
|
|
//
|
|
// Don't search block/pass-thru filters for host bypass traffic
|
|
//
|
|
continue;
|
|
}
|
|
|
|
uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pFilter->uliSrcDstMask.QuadPart;
|
|
uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pFilter->uliProtoSrcDstMask.QuadPart;
|
|
|
|
if ((uliAddr.QuadPart == pFilter->uliSrcDstAddr.QuadPart) &&
|
|
(uliPort.QuadPart == pFilter->uliProtoSrcDstPort.QuadPart)) {
|
|
IPSEC_DEBUG(HASH, ("Matched entry: %lx\n", pFilter));
|
|
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fFound) {
|
|
//
|
|
// Search for the particular SA now.
|
|
//
|
|
fFound=FALSE;
|
|
pSAChain = IPSecResolveSAChain(pFilter, fOutbound? DEST_ADDR: SRC_ADDR);
|
|
|
|
for ( pEntry = pSAChain->Flink;
|
|
pEntry != pSAChain;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
if (IS_CLASSD(NET_LONG(pSA->SA_SRC_ADDR))
|
|
|| IS_CLASSD(NET_LONG(pSA->SA_DEST_ADDR))) {
|
|
uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pSA->sa_uliSrcDstMask.QuadPart;
|
|
|
|
IPSEC_DEBUG(HASH, ("MCAST: %d %d %d %d", uliAddr.LowPart, uliAddr.HighPart,
|
|
pSA->sa_uliSrcDstAddr.LowPart,pSA->sa_uliSrcDstAddr.HighPart));
|
|
|
|
if (uliAddr.QuadPart == pSA->sa_uliSrcDstAddr.QuadPart) {
|
|
fFound=TRUE;
|
|
}
|
|
} else if (uliSrcDstAddr.QuadPart == pSA->sa_uliSrcDstAddr.QuadPart) {
|
|
fFound=TRUE;
|
|
}
|
|
if (fFound) {
|
|
IPSEC_DEBUG(HASH, ("Matched entry: %lx\n", pSA));
|
|
ASSERT(fOutbound == (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_OUTBOUND) != 0));
|
|
|
|
//
|
|
// if there is also a tunnel SA, associate it here.
|
|
//
|
|
if (*ppTunnelSA && fOutbound) {
|
|
*ppNextSA = *ppTunnelSA;
|
|
IPSEC_DEBUG(SAAPI, ("linked next sa: %lx, next: %lx", pSA, *ppTunnelSA));
|
|
*ppTunnelSA = NULL;
|
|
}
|
|
|
|
*ppFilter = pFilter;
|
|
*ppSA = pSA;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Found a filter entry, but need to negotiate keys
|
|
//
|
|
// Also, ppTunnelSA is set to the proper tunnel SA we need
|
|
// to hook to this end-2-end SA once it is negotiated.
|
|
//
|
|
*ppFilter = pFilter;
|
|
|
|
return STATUS_PENDING;
|
|
} else {
|
|
//
|
|
// if only tunnel SA found, return that as the SA
|
|
// found.
|
|
//
|
|
if (*ppTunnelSA) {
|
|
*ppSA = *ppTunnelSA;
|
|
*ppTunnelSA = NULL;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// no entry found
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecLookupTunnelSA(
|
|
IN ULARGE_INTEGER uliSrcDstAddr,
|
|
IN ULARGE_INTEGER uliProtoSrcDstPort,
|
|
OUT PFILTER *ppFilter,
|
|
OUT PSA_TABLE_ENTRY *ppSA,
|
|
IN BOOLEAN fOutbound
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the SA given the relevant addresses.
|
|
|
|
Arguments:
|
|
|
|
uliSrcDstAddr - src/dest IP addr
|
|
uliProtoSrcDstPort - protocol, src/dest port
|
|
ppFilter - filter found
|
|
ppSA - SA found
|
|
fOutbound - direction flag
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - both filter and SA found
|
|
STATUS_UNSUCCESSFUL - none found
|
|
STATUS_PENDING - filter found, but no SA
|
|
|
|
Notes:
|
|
|
|
Called with SADBLock held.
|
|
|
|
--*/
|
|
{
|
|
PFILTER pFilter;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pFilterList;
|
|
PLIST_ENTRY pSAChain;
|
|
PSA_TABLE_ENTRY pSA;
|
|
REGISTER ULARGE_INTEGER uliPort;
|
|
REGISTER ULARGE_INTEGER uliAddr;
|
|
BOOLEAN fFound = FALSE;
|
|
|
|
*ppSA = NULL;
|
|
*ppFilter = NULL;
|
|
|
|
//
|
|
// Search in Tunnel filters list
|
|
//
|
|
pFilterList = IPSecResolveFilterList(TRUE, fOutbound);
|
|
|
|
for ( pEntry = pFilterList->Flink;
|
|
pEntry != pFilterList;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pFilter->uliSrcDstMask.QuadPart;
|
|
uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pFilter->uliProtoSrcDstMask.QuadPart;
|
|
|
|
if ((uliAddr.QuadPart == pFilter->uliSrcDstAddr.QuadPart) &&
|
|
(uliPort.QuadPart == pFilter->uliProtoSrcDstPort.QuadPart)) {
|
|
|
|
IPSEC_DEBUG(HASH, ("Matched entry: %lx\n", pFilter));
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fFound) {
|
|
//
|
|
// Search for the particular SA now.
|
|
//
|
|
pSAChain = IPSecResolveSAChain(pFilter, fOutbound? DEST_ADDR: SRC_ADDR);
|
|
|
|
for ( pEntry = pSAChain->Flink;
|
|
pEntry != pSAChain;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_TUNNEL);
|
|
|
|
if (pFilter->TunnelAddr != 0) {
|
|
//
|
|
// match the outbound flag also
|
|
//
|
|
IPSEC_DEBUG(HASH, ("Matched specific tunnel entry: %lx\n", pSA));
|
|
ASSERT(fOutbound == (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_OUTBOUND) != 0));
|
|
*ppFilter = pFilter;
|
|
*ppSA = pSA;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Found a filter entry, but need to negotiate keys
|
|
//
|
|
*ppFilter = pFilter;
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// no entry found
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecLookupMaskedSA(
|
|
IN ULARGE_INTEGER uliSrcDstAddr,
|
|
IN ULARGE_INTEGER uliProtoSrcDstPort,
|
|
OUT PFILTER *ppFilter,
|
|
OUT PSA_TABLE_ENTRY *ppSA,
|
|
IN BOOLEAN fOutbound
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Looks up the SA given the relevant addresses.
|
|
|
|
Arguments:
|
|
|
|
uliSrcDstAddr - src/dest IP addr
|
|
uliProtoSrcDstPort - protocol, src/dest port
|
|
ppFilter - filter found
|
|
ppSA - SA found
|
|
fOutbound - direction flag
|
|
|
|
Return Value:
|
|
|
|
STATUS_SUCCESS - both filter and SA found
|
|
STATUS_UNSUCCESSFUL - none found
|
|
STATUS_PENDING - filter found, but no SA
|
|
|
|
Notes:
|
|
|
|
Called with SADBLock held.
|
|
|
|
--*/
|
|
{
|
|
PFILTER pFilter;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pFilterList;
|
|
PLIST_ENTRY pSAChain;
|
|
PSA_TABLE_ENTRY pSA;
|
|
REGISTER ULARGE_INTEGER uliPort;
|
|
REGISTER ULARGE_INTEGER uliAddr;
|
|
BOOLEAN fFound = FALSE;
|
|
|
|
*ppSA = NULL;
|
|
*ppFilter = NULL;
|
|
|
|
//
|
|
// Search in Masked filters list
|
|
//
|
|
pFilterList = IPSecResolveFilterList(FALSE, fOutbound);
|
|
|
|
for ( pEntry = pFilterList->Flink;
|
|
pEntry != pFilterList;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
uliAddr.QuadPart = uliSrcDstAddr.QuadPart & pFilter->uliSrcDstMask.QuadPart;
|
|
uliPort.QuadPart = uliProtoSrcDstPort.QuadPart & pFilter->uliProtoSrcDstMask.QuadPart;
|
|
|
|
if ((uliAddr.QuadPart == pFilter->uliSrcDstAddr.QuadPart) &&
|
|
(uliPort.QuadPart == pFilter->uliProtoSrcDstPort.QuadPart)) {
|
|
|
|
IPSEC_DEBUG(HASH, ("Matched entry: %lx\n", pFilter));
|
|
fFound = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (fFound) {
|
|
//
|
|
// Search for the particular SA now.
|
|
//
|
|
pSAChain = IPSecResolveSAChain(pFilter, fOutbound? DEST_ADDR: SRC_ADDR);
|
|
|
|
for ( pEntry = pSAChain->Flink;
|
|
pEntry != pSAChain;
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
if (uliSrcDstAddr.QuadPart == pSA->sa_uliSrcDstAddr.QuadPart) {
|
|
IPSEC_DEBUG(HASH, ("Matched entry: %lx\n", pSA));
|
|
ASSERT(fOutbound == (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_OUTBOUND) != 0));
|
|
*ppFilter = pFilter;
|
|
*ppSA = pSA;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Found a filter entry, but need to negotiate keys
|
|
//
|
|
*ppFilter = pFilter;
|
|
return STATUS_PENDING;
|
|
}
|
|
|
|
//
|
|
// no entry found
|
|
//
|
|
return STATUS_NOT_FOUND;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecAllocateSPI(
|
|
OUT tSPI * pSpi,
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates an SPI for an incoming SA - guards against collisions
|
|
|
|
Arguments:
|
|
|
|
pSpi - the SPI allocated is filled in here
|
|
|
|
pSA - SA for which SPI is needed
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
ULONG rand;
|
|
ULONG numRetries = 0;
|
|
IPAddr DestAddr;
|
|
|
|
if (pSA->sa_TunnelAddr) {
|
|
DestAddr = pSA->sa_TunnelAddr;
|
|
} else {
|
|
DestAddr = pSA->SA_DEST_ADDR;
|
|
}
|
|
|
|
//
|
|
// if SPI passed in, use that spi else allocate one.
|
|
//
|
|
if (*pSpi) {
|
|
if (IPSecLookupSABySPIWithLock(
|
|
*pSpi,
|
|
DestAddr)) {
|
|
return STATUS_UNSUCCESSFUL;
|
|
} else {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
} else {
|
|
rand = (ULONG)(ULONG_PTR)pSA;
|
|
IPSecGenerateRandom((PUCHAR)&rand, sizeof(ULONG));
|
|
rand = LOWER_BOUND_SPI + rand % (UPPER_BOUND_SPI - LOWER_BOUND_SPI);
|
|
|
|
while (numRetries++ < MAX_SPI_RETRIES) {
|
|
|
|
if (!IPSecLookupSABySPIWithLock(
|
|
(tSPI)rand,
|
|
DestAddr)) {
|
|
*pSpi = (tSPI)rand;
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
rand++;
|
|
|
|
//
|
|
// Collision, retry
|
|
//
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecAllocateSPI: collision for: %lx\n", rand));
|
|
}
|
|
}
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecNegotiateSA(
|
|
IN PFILTER pFilter,
|
|
IN ULARGE_INTEGER uliSrcDstAddr,
|
|
IN ULARGE_INTEGER uliProtoSrcDstPort,
|
|
IN ULONG NewMTU,
|
|
OUT PSA_TABLE_ENTRY *ppSA,
|
|
IN UCHAR DestType
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a Larval Inbound SA block then kicks off key manager to negotiate
|
|
the algorithms/keys.
|
|
|
|
Called with SADB lock held, returns with it.
|
|
|
|
Arguments:
|
|
|
|
pFilter - the filter and policy that matched this packet.
|
|
|
|
ppSA - returns the SA created here.
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the buffer is to be held on to, the normal case.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL kIrql;
|
|
KIRQL OldIrq;
|
|
NTSTATUS status;
|
|
PSA_TABLE_ENTRY pSA;
|
|
|
|
//
|
|
// Make sure we dont already have this SA under negotiation
|
|
// walk the LarvalSA list to see if we can find another SA.
|
|
//
|
|
pSA = IPSecLookupSAInLarval(uliSrcDstAddr, uliProtoSrcDstPort);
|
|
if (pSA != NULL) {
|
|
IPSEC_DEBUG(PATTERN, ("Found in Larval: %lx\n", pSA));
|
|
*ppSA = pSA;
|
|
return STATUS_DUPLICATE_OBJECTID;
|
|
}
|
|
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecNegotiateSA: SA: %lx, DA: %lx, P: %lx, SP: %lx, DP: %lx\n", SRC_ADDR, DEST_ADDR, PROTO, SRC_PORT, DEST_PORT));
|
|
|
|
//
|
|
// Initiator
|
|
//
|
|
status = IPSecInitiatorCreateLarvalSA(
|
|
pFilter,
|
|
uliSrcDstAddr,
|
|
ppSA,
|
|
DestType
|
|
);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecNegotiateSA: IPSecCreateSA failed: %lx\n", status));
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Save the NewMTU value if this SA has been PMTU'd.
|
|
//
|
|
(*ppSA)->sa_NewMTU = NewMTU;
|
|
|
|
//
|
|
// If this is a tunnel filter to be negotiated, save off the tunnel addr in the
|
|
// SA.
|
|
//
|
|
if (pFilter->TunnelFilter) {
|
|
IPSEC_DEBUG(TUNNEL, ("Negotiating tunnel SA: %lx\n", (*ppSA)));
|
|
// (*ppSA)->sa_TunnelAddr = pFilter->TunnelAddr;
|
|
}
|
|
|
|
//
|
|
// Now send this up to the Key Manager to negotiate the keys
|
|
//
|
|
ACQUIRE_LOCK(&g_ipsec.AcquireInfo.Lock, &OldIrq);
|
|
status = IPSecSubmitAcquire(*ppSA, OldIrq, FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecNegotiateSA: IPSecSubmitAcquire failed: %lx\n", status));
|
|
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
|
IPSecRemoveEntryList(&(*ppSA)->sa_LarvalLinkage);
|
|
IPSEC_DEC_STATISTIC(dwNumPendingKeyOps);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
|
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql);
|
|
IPSecRemoveSPIEntry(*ppSA);
|
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql);
|
|
|
|
//
|
|
// also remove from the filter list
|
|
//
|
|
if ((*ppSA)->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
(*ppSA)->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&(*ppSA)->sa_FilterLinkage);
|
|
(*ppSA)->sa_Filter = NULL;
|
|
}
|
|
|
|
if ((*ppSA)->sa_RekeyOriginalSA) {
|
|
ASSERT((*ppSA)->sa_Flags & FLAGS_SA_REKEY);
|
|
ASSERT((*ppSA)->sa_RekeyOriginalSA->sa_RekeyLarvalSA == (*ppSA));
|
|
ASSERT((*ppSA)->sa_RekeyOriginalSA->sa_Flags & FLAGS_SA_REKEY_ORI);
|
|
|
|
(*ppSA)->sa_RekeyOriginalSA->sa_Flags &= ~FLAGS_SA_REKEY_ORI;
|
|
(*ppSA)->sa_RekeyOriginalSA->sa_RekeyLarvalSA = NULL;
|
|
(*ppSA)->sa_RekeyOriginalSA = NULL;
|
|
}
|
|
|
|
(*ppSA)->sa_Flags &= ~FLAGS_SA_TIMER_STARTED;
|
|
IPSecStopTimer(&(*ppSA)->sa_Timer);
|
|
IPSecDerefSA(*ppSA);
|
|
return status;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecFlushQueuedPackets(
|
|
IN PSA_TABLE_ENTRY pSA,
|
|
IN NTSTATUS status
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Flushes queued packets now that the keys are known
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PIPSEC_SEND_COMPLETE_CONTEXT pContext;
|
|
IPOptInfo optInfo;
|
|
ULONG len;
|
|
PNDIS_BUFFER pHdrMdl;
|
|
ULONG dataLen;
|
|
IPHeader UNALIGNED * pIPH;
|
|
KIRQL kIrql;
|
|
|
|
//
|
|
// We need to acquire a lock here because this routine can be called in
|
|
// parallel with one in SA delete and the other in SA update (normal).
|
|
//
|
|
ACQUIRE_LOCK(&pSA->sa_Lock, &kIrql);
|
|
pHdrMdl = pSA->sa_BlockedBuffer;
|
|
dataLen = pSA->sa_BlockedDataLen;
|
|
|
|
pSA->sa_BlockedBuffer = NULL;
|
|
pSA->sa_BlockedDataLen = 0;
|
|
RELEASE_LOCK(&pSA->sa_Lock, kIrql);
|
|
|
|
if (!pHdrMdl) {
|
|
IPSEC_DEBUG(ACQUIRE, ("FlushQueuedPackets: pHdrMdl == NULL\n"));
|
|
return;
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
ASSERT(pSA->sa_State == STATE_SA_ACTIVE);
|
|
ASSERT((pSA->sa_Flags & FLAGS_SA_OUTBOUND) == 0);
|
|
ASSERT(pHdrMdl);
|
|
|
|
pContext = IPSecAllocateSendCompleteCtx(IPSEC_TAG_ESP);
|
|
|
|
if (!pContext) {
|
|
PNDIS_BUFFER pNextMdl;
|
|
PNDIS_BUFFER pMdl = pHdrMdl;
|
|
NTSTATUS status;
|
|
|
|
IPSEC_DEBUG(ESP, ("Failed to alloc. SendCtx\n"));
|
|
|
|
ASSERT(pMdl);
|
|
|
|
while (pMdl) {
|
|
pNextMdl = NDIS_BUFFER_LINKAGE(pMdl);
|
|
IPSecFreeBuffer(&status, pMdl);
|
|
pMdl = pNextMdl;
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
IPSEC_INCREMENT(g_ipsec.NumSends);
|
|
|
|
IPSecZeroMemory(pContext, sizeof(IPSEC_SEND_COMPLETE_CONTEXT));
|
|
|
|
#if DBG
|
|
RtlCopyMemory(pContext->Signature, "ISC6", 4);
|
|
#endif
|
|
|
|
pContext->FlushMdl = pHdrMdl;
|
|
pContext->Flags |= SCF_FLUSH;
|
|
|
|
IPSecQueryNdisBuf(pHdrMdl, (PVOID)&pIPH, &len);
|
|
|
|
//
|
|
// Call IPTransmit with proper Protocol type so it takes this packet
|
|
// at *face* value.
|
|
//
|
|
optInfo = g_ipsec.OptInfo;
|
|
optInfo.ioi_flags |= IP_FLAG_IPSEC;
|
|
status = TCPIP_IP_TRANSMIT( &g_ipsec.IPProtInfo,
|
|
pContext,
|
|
pHdrMdl,
|
|
dataLen,
|
|
pIPH->iph_dest,
|
|
pIPH->iph_src,
|
|
&optInfo,
|
|
NULL,
|
|
pIPH->iph_protocol,
|
|
NULL);
|
|
|
|
//
|
|
// Even in the synchronous case, we free the MDL chain in ProtocolSendComplete
|
|
// (called by IPSecSendComplete). So, we dont call anything here.
|
|
// See IPSecReinjectPacket.
|
|
//
|
|
} else {
|
|
PNDIS_BUFFER pNextMdl;
|
|
PNDIS_BUFFER pMdl = pHdrMdl;
|
|
NTSTATUS status;
|
|
|
|
ASSERT(pMdl);
|
|
|
|
while (pMdl) {
|
|
pNextMdl = NDIS_BUFFER_LINKAGE(pMdl);
|
|
IPSecFreeBuffer(&status, pMdl);
|
|
pMdl = pNextMdl;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecInsertOutboundSA(
|
|
IN PSA_TABLE_ENTRY pSA,
|
|
IN PIPSEC_ACQUIRE_CONTEXT pAcquireCtx,
|
|
IN BOOLEAN fTunnelFilter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds an SA into the database, typically called to add outbound SAs as a
|
|
result of successful negotiation of keys corresponding to the inbound SA
|
|
specified in the context that comes down.
|
|
|
|
NOTE: Called with SADB lock held.
|
|
|
|
Arguments:
|
|
|
|
pSA - SA to be inserted
|
|
|
|
pAcquireContext - The Acquire context
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pInboundSA = pAcquireCtx->pSA;
|
|
PSA_TABLE_ENTRY pAssociatedSA;
|
|
KIRQL kIrql;
|
|
KIRQL kIrql1;
|
|
NTSTATUS status;
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pOutboundSA = NULL;
|
|
PSA_TABLE_ENTRY pTunnelSA = NULL;
|
|
PLIST_ENTRY pSAChain;
|
|
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_OUTBOUND);
|
|
ASSERT((pInboundSA->sa_Flags & FLAGS_SA_OUTBOUND) == 0);
|
|
ASSERT(pInboundSA->sa_State == STATE_SA_LARVAL);
|
|
|
|
//
|
|
// Potential dangling pointer, always go through the lookup path.
|
|
//
|
|
if (fTunnelFilter) {
|
|
status = IPSecLookupTunnelSA( pSA->sa_uliSrcDstAddr,
|
|
pSA->sa_uliProtoSrcDstPort,
|
|
&pFilter,
|
|
&pOutboundSA,
|
|
TRUE);
|
|
} else {
|
|
#if GPC
|
|
if (IS_GPC_ACTIVE()) {
|
|
status = IPSecLookupGpcMaskedSA(pSA->sa_uliSrcDstAddr,
|
|
pSA->sa_uliProtoSrcDstPort,
|
|
&pFilter,
|
|
&pOutboundSA,
|
|
TRUE);
|
|
} else {
|
|
status = IPSecLookupMaskedSA( pSA->sa_uliSrcDstAddr,
|
|
pSA->sa_uliProtoSrcDstPort,
|
|
&pFilter,
|
|
&pOutboundSA,
|
|
TRUE);
|
|
}
|
|
#else
|
|
status = IPSecLookupMaskedSA( pSA->sa_uliSrcDstAddr,
|
|
pSA->sa_uliProtoSrcDstPort,
|
|
&pFilter,
|
|
&pOutboundSA,
|
|
TRUE);
|
|
#endif
|
|
}
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecInsertOutboundSA: IPSecLookupSAByAddr failed: %lx\n", status));
|
|
return status;
|
|
}
|
|
|
|
pSAChain = IPSecResolveSAChain(pFilter, pSA->SA_DEST_ADDR);
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
//
|
|
// re-negotiate case: delete the outbound; expire the inbound; add the new one.
|
|
//
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecInsertOutboundSA: found another: %lx\n", pOutboundSA));
|
|
ASSERT(pOutboundSA);
|
|
ASSERT(pOutboundSA->sa_Flags & FLAGS_SA_OUTBOUND);
|
|
|
|
pSA->sa_Filter = pFilter;
|
|
pSA->sa_Flags |= FLAGS_SA_ON_FILTER_LIST;
|
|
InsertHeadList(pSAChain, &pSA->sa_FilterLinkage);
|
|
|
|
IPSEC_INC_STATISTIC(dwNumReKeys);
|
|
|
|
pAssociatedSA = pOutboundSA->sa_AssociatedSA;
|
|
if (pAssociatedSA &&
|
|
((pOutboundSA->sa_Flags & FLAGS_SA_REKEY_ORI) ||
|
|
!(pInboundSA->sa_Filter))) {
|
|
IPSecExpireInboundSA(pAssociatedSA);
|
|
}
|
|
} else {
|
|
//
|
|
// pending => this will be the add.
|
|
//
|
|
ASSERT(pOutboundSA == NULL);
|
|
pSA->sa_Filter = pFilter;
|
|
pSA->sa_Flags |= FLAGS_SA_ON_FILTER_LIST;
|
|
InsertHeadList(pSAChain, &pSA->sa_FilterLinkage);
|
|
}
|
|
|
|
if (pFilter->TunnelAddr != 0) {
|
|
pSA->sa_Flags |= FLAGS_SA_TUNNEL;
|
|
pSA->sa_TunnelAddr = pFilter->TunnelAddr;
|
|
}
|
|
|
|
//
|
|
// Initiator if the original SA had a filter pointer.
|
|
//
|
|
if (pInboundSA->sa_Filter) {
|
|
pSA->sa_Flags |= FLAGS_SA_INITIATOR;
|
|
}
|
|
|
|
//
|
|
// Flush this filter from cache table so we match the SA next.
|
|
//
|
|
if (IS_EXEMPT_FILTER(pFilter)) {
|
|
IPSecInvalidateFilterCacheEntry(pFilter);
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecAddSA(
|
|
IN PIPSEC_ADD_SA pAddSA,
|
|
IN ULONG TotalSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Adds an SA into the database, typically called to add outbound SAs as a
|
|
result of successful negotiation of keys corresponding to the inbound SA
|
|
specified in the context that comes down.
|
|
|
|
Arguments:
|
|
|
|
pAddSA - Add SA context and info.
|
|
|
|
TotalSize - the total size of the input buffer.
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PSA_STRUCT saInfo = &pAddSA->SAInfo;
|
|
PSA_TABLE_ENTRY pSA;
|
|
ULONG keyLen = 0;
|
|
PSA_TABLE_ENTRY pInboundSA;
|
|
KIRQL kIrql;
|
|
KIRQL kIrql1;
|
|
PIPSEC_ACQUIRE_CONTEXT pAcquireContext = (PIPSEC_ACQUIRE_CONTEXT)(saInfo->Context);
|
|
|
|
//
|
|
// Lock the larval list so this SA does not go away.
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql1);
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
|
|
|
//
|
|
// Sanity check the incoming context to see if it is actually
|
|
// an SA block
|
|
//
|
|
if (!NT_SUCCESS(IPSecValidateHandle(pAcquireContext, STATE_SA_LARVAL))) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecAddSA: invalid context: %lx\n", pAcquireContext));
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// figure out the key length and pass that in
|
|
//
|
|
keyLen = TotalSize - IPSEC_ADD_SA_NO_KEY_SIZE;
|
|
IPSEC_DEBUG(SAAPI, ("IPSecAddSA: keyLen: %d\n", keyLen));
|
|
|
|
//
|
|
// create SA block
|
|
//
|
|
status = IPSecCreateSA(&pSA);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecAddSA: IPSecCreateSA failed: %lx\n", status));
|
|
IPSecAbortAcquire(pAcquireContext);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
return status;
|
|
}
|
|
|
|
pSA->sa_Flags |= FLAGS_SA_OUTBOUND;
|
|
|
|
//
|
|
// Populate with the info in AddSA
|
|
//
|
|
status = IPSecPopulateSA(saInfo, keyLen, pSA);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecAddSA: IPSecPopulateSA failed: %lx\n", status));
|
|
// IPSecPopulateSA will not free the outbound SA so we have to do it.
|
|
IPSecFreeSA(pSA);
|
|
IPSecAbortAcquire(pAcquireContext);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Stash the outermost spi
|
|
//
|
|
pSA->sa_SPI = pSA->sa_OtherSPIs[pSA->sa_NumOps-1];
|
|
|
|
//
|
|
// insert into proper tables
|
|
//
|
|
status = IPSecInsertOutboundSA(pSA, pAcquireContext, (BOOLEAN)((pSA->sa_Flags & FLAGS_SA_TUNNEL) != 0));
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecAddSA: IPSecInsertOutboundSA failed: %lx\n", status));
|
|
IPSecFreeSA(pSA);
|
|
IPSecAbortAcquire(pAcquireContext);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
return status;
|
|
}
|
|
|
|
pInboundSA = pAcquireContext->pSA;
|
|
|
|
//
|
|
// Associate the inbound and outbound SAs
|
|
//
|
|
pSA->sa_AssociatedSA = pInboundSA;
|
|
pInboundSA->sa_AssociatedSA = pSA;
|
|
pInboundSA->sa_State = STATE_SA_ASSOCIATED;
|
|
|
|
//
|
|
// Initialize IPSec overhead for the outbound SA.
|
|
//
|
|
IPSecCalcHeaderOverheadFromSA(pSA, &pSA->sa_IPSecOverhead);
|
|
|
|
// Copy the NewMTU value over to the new SA.
|
|
//
|
|
pSA->sa_NewMTU = pInboundSA->sa_NewMTU;
|
|
|
|
//
|
|
// Adjust SA lifetime to the maximum/minimum allowed in driver
|
|
//
|
|
if (pSA->sa_Lifetime.KeyExpirationTime > IPSEC_MAX_EXPIRE_TIME) {
|
|
pSA->sa_Lifetime.KeyExpirationTime = IPSEC_MAX_EXPIRE_TIME;
|
|
}
|
|
|
|
if (pSA->sa_Lifetime.KeyExpirationTime &&
|
|
pSA->sa_Lifetime.KeyExpirationTime < IPSEC_MIN_EXPIRE_TIME) {
|
|
pSA->sa_Lifetime.KeyExpirationTime = IPSEC_MIN_EXPIRE_TIME;
|
|
}
|
|
|
|
//
|
|
// Setup lifetime characteristics
|
|
//
|
|
IPSecSetupSALifetime(pSA);
|
|
|
|
//
|
|
// Init the LastUsedTime
|
|
//
|
|
NdisGetCurrentSystemTime(&pSA->sa_LastUsedTime);
|
|
|
|
//
|
|
// outbound is ready to go!
|
|
//
|
|
pSA->sa_State = STATE_SA_ACTIVE;
|
|
|
|
IPSEC_DEBUG(SA, ("IPSecAddSA: SA: %lx, S:%lx, D:%lx, O: %c\n",
|
|
pSA,
|
|
pSA->SA_SRC_ADDR,
|
|
pSA->SA_DEST_ADDR,
|
|
(pSA->sa_Operation[0] == Auth) ?
|
|
'A' : (pSA->sa_Operation[0] == Encrypt) ?
|
|
'E' : 'N'));
|
|
|
|
IPSEC_INC_STATISTIC(dwNumActiveAssociations);
|
|
IPSEC_INC_TUNNELS(pSA);
|
|
IPSEC_INCREMENT(g_ipsec.NumOutboundSAs);
|
|
IPSEC_INC_STATISTIC(dwNumKeyAdditions);
|
|
|
|
if (pSA->sa_Operation[0] != None) {
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
} else {
|
|
//
|
|
// The key manager doesnt call update for None;
|
|
// call it ourselves.
|
|
//
|
|
IPSEC_UPDATE_SA updSA;
|
|
|
|
ASSERT(pInboundSA->sa_Flags & FLAGS_SA_TIMER_STARTED);
|
|
IPSEC_DEBUG(SA, ("Calling update on InboundSA: %lx\n", pInboundSA));
|
|
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
|
|
//
|
|
// Reverse the addresses/ports here (by copying from the InboundSA)
|
|
//
|
|
updSA.SAInfo.Context = pAddSA->SAInfo.Context;
|
|
updSA.SAInfo.NumSAs = pAddSA->SAInfo.NumSAs;
|
|
updSA.SAInfo.Flags = pAddSA->SAInfo.Flags;
|
|
updSA.SAInfo.TunnelAddr = pAddSA->SAInfo.TunnelAddr;
|
|
updSA.SAInfo.Lifetime = pAddSA->SAInfo.Lifetime;
|
|
updSA.SAInfo.InstantiatedFilter = pAddSA->SAInfo.InstantiatedFilter;
|
|
updSA.SAInfo.SecAssoc[0] = pAddSA->SAInfo.SecAssoc[0];
|
|
|
|
updSA.SAInfo.SecAssoc[0].SPI = pInboundSA->sa_SPI;
|
|
updSA.SAInfo.InstantiatedFilter.SrcAddr = pInboundSA->SA_SRC_ADDR;
|
|
updSA.SAInfo.InstantiatedFilter.DestAddr = pInboundSA->SA_DEST_ADDR;
|
|
|
|
updSA.SAInfo.InstantiatedFilter.Protocol = pInboundSA->SA_PROTO;
|
|
updSA.SAInfo.InstantiatedFilter.SrcPort = SA_SRC_PORT(pInboundSA);
|
|
updSA.SAInfo.InstantiatedFilter.DestPort = SA_DEST_PORT(pInboundSA);
|
|
|
|
status = IPSecUpdateSA(&updSA, TotalSize);
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecUpdateSA(
|
|
IN PIPSEC_UPDATE_SA pUpdateSA,
|
|
IN ULONG TotalSize
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Updates an inbound SA for which negotiation was kicked off via AcquireSA with
|
|
the relevant keys/algorithms etc.
|
|
|
|
By the time this routine is called, the SA should be ASSOCIATED with an outbound
|
|
SA.
|
|
|
|
Arguments:
|
|
|
|
pUpdateSA - Update SA context and info.
|
|
|
|
TotalSize - the total size of the input buffer.
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
PSA_STRUCT saInfo = &pUpdateSA->SAInfo;
|
|
PSA_TABLE_ENTRY pSA;
|
|
PSA_TABLE_ENTRY pOutboundSA;
|
|
PSA_HASH pHash;
|
|
ULONG keyLen = 0;
|
|
KIRQL kIrql;
|
|
KIRQL kIrql1;
|
|
KIRQL kIrql2;
|
|
PIPSEC_ACQUIRE_CONTEXT pAcquireContext = (PIPSEC_ACQUIRE_CONTEXT)(saInfo->Context);
|
|
|
|
IPSEC_DEBUG(SAAPI, ("IPSecUpdateSA\n"));
|
|
|
|
//
|
|
// Lock the larval list so this SA does not go away.
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql1);
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
|
|
|
//
|
|
// Sanity check the incoming context to see if it is actually
|
|
// an SA block
|
|
//
|
|
if (!NT_SUCCESS(IPSecValidateHandle(pAcquireContext, STATE_SA_ASSOCIATED))) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecUpdSA: invalid context: %lx\n", pAcquireContext));
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
pSA = pAcquireContext->pSA;
|
|
|
|
ASSERT((pSA->sa_Flags & FLAGS_SA_OUTBOUND) == 0);
|
|
ASSERT(pSA->sa_State == STATE_SA_ASSOCIATED);
|
|
|
|
//
|
|
// figure out the key length and pass that in
|
|
//
|
|
keyLen = TotalSize - IPSEC_UPDATE_SA_NO_KEY_SIZE;
|
|
|
|
IPSEC_DEBUG(SAAPI, ("IPSecUpdSA: keyLen: %d\n", keyLen));
|
|
|
|
//
|
|
// sanity check the info passed in against the initial SA
|
|
//
|
|
if (pSA->sa_Filter) {
|
|
status = IPSecCheckInboundSA(saInfo, pSA);
|
|
|
|
if (!NT_SUCCESS(status) ||
|
|
!pSA->sa_AssociatedSA) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecUpdSA: IPSecCheckInboundSA failed: %lx\n", status));
|
|
IPSecAbortAcquire(pAcquireContext);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Populate the SA block
|
|
//
|
|
status = IPSecPopulateSA(saInfo, keyLen, pSA);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(SAAPI, ("IPSecUpdSA: IPSecPopulateSA failed: %lx\n", status));
|
|
// No need to free inbound SA since IPSecAbortAcquire will do it.
|
|
IPSecAbortAcquire(pAcquireContext);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
return status;
|
|
}
|
|
|
|
//
|
|
// Set the source Tunnel IP address in outbound SA
|
|
//
|
|
if (pOutboundSA = pSA->sa_AssociatedSA) {
|
|
//
|
|
// See if we have well-associated SAs
|
|
//
|
|
ASSERT(pSA == pSA->sa_AssociatedSA->sa_AssociatedSA);
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_TUNNEL) {
|
|
pOutboundSA->sa_SrcTunnelAddr = pSA->sa_TunnelAddr;
|
|
}
|
|
if (pOutboundSA->sa_Flags & FLAGS_SA_TUNNEL) {
|
|
pSA->sa_SrcTunnelAddr = pOutboundSA->sa_TunnelAddr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Expire the original SA that kicked off this rekey
|
|
//
|
|
if (pSA->sa_Flags & FLAGS_SA_REKEY) {
|
|
PSA_TABLE_ENTRY pOriSA;
|
|
|
|
if (pOriSA = pSA->sa_RekeyOriginalSA) {
|
|
KIRQL kIrql;
|
|
|
|
pSA->sa_RekeyOriginalSA = NULL;
|
|
IPSEC_DEBUG(SA, ("Deleting original SA: pSA: %lx\n", pOriSA));
|
|
|
|
if (pOriSA->sa_AssociatedSA) {
|
|
IPSecExpireInboundSA(pOriSA->sa_AssociatedSA);
|
|
}
|
|
IPSEC_INC_STATISTIC(dwNumReKeys);
|
|
}
|
|
}
|
|
|
|
//
|
|
// inbound is ready to go!
|
|
//
|
|
pSA->sa_State = STATE_SA_ACTIVE;
|
|
|
|
IPSEC_DEBUG(SA, ("IPSecUpdateSA: SA: %lx, S:%lx, D:%lx, O: %c\n",
|
|
pSA,
|
|
pSA->SA_SRC_ADDR,
|
|
pSA->SA_DEST_ADDR,
|
|
(pSA->sa_Operation[0] == Auth) ?
|
|
'A' : (pSA->sa_Operation[0] == Encrypt) ?
|
|
'E' : 'N'));
|
|
|
|
//
|
|
// Remove from larval list
|
|
//
|
|
IPSecRemoveEntryList(&pSA->sa_LarvalLinkage);
|
|
IPSEC_DEC_STATISTIC(dwNumPendingKeyOps);
|
|
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_TIMER_STARTED);
|
|
|
|
//
|
|
// Bump the SA count for flush SA use; this is necessary because we flush
|
|
// SA after releasing the lock because classification routine needs
|
|
// it and the SA can be deleted right after we release the lock.
|
|
//
|
|
IPSecRefSA(pSA);
|
|
|
|
//
|
|
// Free the Acquire Context
|
|
//
|
|
ACQUIRE_LOCK(&pSA->sa_Lock, &kIrql);
|
|
|
|
if (pSA->sa_AcquireCtx) {
|
|
IPSecInvalidateHandle(pSA->sa_AcquireCtx);
|
|
pSA->sa_AcquireCtx = NULL;
|
|
}
|
|
|
|
//
|
|
// Adjust SA lifetime to the maximum/minimum allowed in driver
|
|
//
|
|
if (pSA->sa_Lifetime.KeyExpirationTime > IPSEC_MAX_EXPIRE_TIME) {
|
|
pSA->sa_Lifetime.KeyExpirationTime = IPSEC_MAX_EXPIRE_TIME;
|
|
}
|
|
|
|
if (pSA->sa_Lifetime.KeyExpirationTime &&
|
|
pSA->sa_Lifetime.KeyExpirationTime < IPSEC_MIN_EXPIRE_TIME) {
|
|
pSA->sa_Lifetime.KeyExpirationTime = IPSEC_MIN_EXPIRE_TIME;
|
|
}
|
|
|
|
//
|
|
// Setup lifetime characteristics
|
|
//
|
|
IPSecSetupSALifetime(pSA);
|
|
|
|
//
|
|
// Init the LastUsedTime
|
|
//
|
|
NdisGetCurrentSystemTime(&pSA->sa_LastUsedTime);
|
|
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_DISABLE_LIFETIME_CHECK)) {
|
|
|
|
if (!IPSecStopTimer(&(pSA->sa_Timer))) {
|
|
IPSEC_DEBUG(TIMER, ("Update: couldnt stop timer: %lx\n", pSA));
|
|
}
|
|
pSA->sa_Flags &= ~FLAGS_SA_TIMER_STARTED;
|
|
} else {
|
|
|
|
//
|
|
// Reschedules the timer on this new value.
|
|
//
|
|
if (pSA->sa_Lifetime.KeyExpirationTime) {
|
|
if (IPSecStopTimer(&pSA->sa_Timer)) {
|
|
IPSecStartTimer(&pSA->sa_Timer,
|
|
IPSecSAExpired,
|
|
pSA->sa_Lifetime.KeyExpirationTime, // expire in key expiration secs
|
|
(PVOID)pSA);
|
|
}
|
|
} else {
|
|
ASSERT(FALSE);
|
|
if (!IPSecStopTimer(&(pSA->sa_Timer))) {
|
|
IPSEC_DEBUG(TIMER, ("Update: couldnt stop timer: %lx\n", pSA));
|
|
}
|
|
pSA->sa_Flags &= ~FLAGS_SA_TIMER_STARTED;
|
|
}
|
|
|
|
}
|
|
RELEASE_LOCK(&pSA->sa_Lock, kIrql);
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
|
|
//
|
|
// Flush all the queued packets
|
|
//
|
|
IPSecFlushQueuedPackets(pSA, STATUS_SUCCESS);
|
|
IPSecDerefSA(pSA);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecRefSA(
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Reference the SA passed in
|
|
|
|
Arguments:
|
|
|
|
pSA - SA to be refed
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
if (IPSEC_INCREMENT(pSA->sa_Reference) == 1) {
|
|
ASSERT(FALSE);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecDerefSA(
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Dereference the SA passed in; if refcount drops to 0, free the block.
|
|
|
|
Arguments:
|
|
|
|
pSA - SA to be derefed
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
ULONG val;
|
|
|
|
if ((val = IPSEC_DECREMENT(pSA->sa_Reference)) == 0) {
|
|
//
|
|
// last reference - destroy SA
|
|
//
|
|
IPSEC_DEBUG(REF, ("Freeing SA: %lx\n", pSA));
|
|
|
|
#if DBG
|
|
if ((pSA->sa_Flags & FLAGS_SA_HW_PLUMBED)) {
|
|
DbgPrint("Freeing SA: %lx with offload on\n", pSA);
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
if (IPSEC_GET_VALUE(pSA->sa_NumSends) != 0) {
|
|
DbgPrint("Freeing SA: %lx with numsends > 0\n", pSA);
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
if ((pSA->sa_Flags & FLAGS_SA_TIMER_STARTED)) {
|
|
DbgPrint("Freeing SA: %lx with timer on\n", pSA);
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
if (pSA->sa_Signature != IPSEC_SA_SIGNATURE) {
|
|
DbgPrint("Signature doesnt match for SA: %lx\n", pSA);
|
|
DbgBreakPoint();
|
|
}
|
|
|
|
if (!IPSEC_DRIVER_IS_INACTIVE() &&
|
|
(pSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST)) {
|
|
DbgPrint("Freeing SA: %lx while still on filter list\n", pSA);
|
|
DbgBreakPoint();
|
|
}
|
|
#endif
|
|
|
|
pSA->sa_Signature = IPSEC_SA_SIGNATURE + 1;
|
|
|
|
IPSecFreeSA(pSA);
|
|
}
|
|
|
|
ASSERT((LONG)val >= 0);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecStopSATimers()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Stop all timers active on Larval SA list and Filter list.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pFilterEntry;
|
|
PLIST_ENTRY pSAEntry;
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pSA;
|
|
KIRQL kIrql;
|
|
LONG Index;
|
|
LONG SAIndex;
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
//
|
|
// Go through all SA's and stop its timers
|
|
//
|
|
for ( Index = MIN_FILTER;
|
|
Index <= MAX_FILTER;
|
|
Index++) {
|
|
|
|
for ( pFilterEntry = g_ipsec.FilterList[Index].Flink;
|
|
pFilterEntry != &g_ipsec.FilterList[Index];
|
|
pFilterEntry = pFilterEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pFilterEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex++) {
|
|
|
|
for ( pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
pSAEntry != &pFilter->SAChain[SAIndex];
|
|
pSAEntry = pSAEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
IPSecStopSATimer(pSA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecFlushLarvalSAList()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
When the Acquire Irp is cancelled, this is called to flush all Larval SAs
|
|
|
|
Called with SADB lock held (first); returns with it.
|
|
Called with AcquireInfo.Lock held; returns with it.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
KIRQL OldIrq;
|
|
KIRQL OldIrq1;
|
|
KIRQL kIrql;
|
|
PSA_TABLE_ENTRY pLarvalSA;
|
|
LIST_ENTRY FreeList;
|
|
|
|
InitializeListHead(&FreeList);
|
|
|
|
while (TRUE) {
|
|
if (!IsListEmpty(&g_ipsec.AcquireInfo.PendingAcquires)) {
|
|
PLIST_ENTRY pEntry;
|
|
|
|
pEntry = RemoveHeadList(&g_ipsec.AcquireInfo.PendingAcquires);
|
|
|
|
pLarvalSA = CONTAINING_RECORD( pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_PendingLinkage);
|
|
ASSERT(pLarvalSA->sa_State == STATE_SA_LARVAL);
|
|
ASSERT(pLarvalSA->sa_Flags & FLAGS_SA_PENDING);
|
|
|
|
pLarvalSA->sa_Flags &= ~FLAGS_SA_PENDING;
|
|
|
|
//
|
|
// Insert into another list, which we walk without the lock
|
|
//
|
|
InsertTailList(&FreeList, &pLarvalSA->sa_PendingLinkage);
|
|
|
|
//
|
|
// also remove from Larval list
|
|
//
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &OldIrq1);
|
|
IPSecRemoveEntryList(&pLarvalSA->sa_LarvalLinkage);
|
|
IPSEC_DEC_STATISTIC(dwNumPendingKeyOps);
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, OldIrq1);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
//
|
|
// get the remaining Larval SAs
|
|
//
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &OldIrq);
|
|
while (TRUE) {
|
|
if (!IsListEmpty(&g_ipsec.LarvalSAList)) {
|
|
PLIST_ENTRY pEntry;
|
|
|
|
pEntry = RemoveHeadList(&g_ipsec.LarvalSAList);
|
|
|
|
pLarvalSA = CONTAINING_RECORD( pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_LarvalLinkage);
|
|
|
|
//
|
|
// Insert into another list, which we walk without the lock
|
|
//
|
|
InsertTailList(&FreeList, &pLarvalSA->sa_PendingLinkage);
|
|
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, OldIrq);
|
|
|
|
while (TRUE) {
|
|
if (!IsListEmpty(&FreeList)) {
|
|
PLIST_ENTRY pEntry;
|
|
|
|
pEntry = RemoveHeadList(&FreeList);
|
|
|
|
pLarvalSA = CONTAINING_RECORD( pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_PendingLinkage);
|
|
|
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql);
|
|
IPSecRemoveSPIEntry(pLarvalSA);
|
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql);
|
|
|
|
//
|
|
// Flush all the queued packets
|
|
//
|
|
IPSecFlushQueuedPackets(pLarvalSA, STATUS_TIMEOUT);
|
|
|
|
//
|
|
// also remove from the filter list
|
|
//
|
|
if (pLarvalSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
pLarvalSA->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&pLarvalSA->sa_FilterLinkage);
|
|
}
|
|
|
|
if (pLarvalSA->sa_RekeyOriginalSA) {
|
|
ASSERT(pLarvalSA->sa_Flags & FLAGS_SA_REKEY);
|
|
ASSERT(pLarvalSA->sa_RekeyOriginalSA->sa_RekeyLarvalSA == pLarvalSA);
|
|
ASSERT(pLarvalSA->sa_RekeyOriginalSA->sa_Flags & FLAGS_SA_REKEY_ORI);
|
|
|
|
pLarvalSA->sa_RekeyOriginalSA->sa_Flags &= ~FLAGS_SA_REKEY_ORI;
|
|
pLarvalSA->sa_RekeyOriginalSA->sa_RekeyLarvalSA = NULL;
|
|
pLarvalSA->sa_RekeyOriginalSA = NULL;
|
|
}
|
|
|
|
//
|
|
// release acquire context and invalidate the associated cache entry
|
|
//
|
|
ACQUIRE_LOCK(&pLarvalSA->sa_Lock, &kIrql);
|
|
if (pLarvalSA->sa_AcquireCtx) {
|
|
IPSecInvalidateHandle(pLarvalSA->sa_AcquireCtx);
|
|
pLarvalSA->sa_AcquireCtx = NULL;
|
|
}
|
|
RELEASE_LOCK(&pLarvalSA->sa_Lock, kIrql);
|
|
|
|
IPSecInvalidateSACacheEntry(pLarvalSA);
|
|
|
|
IPSecStopTimerDerefSA(pLarvalSA);
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecDeleteSA(
|
|
IN PIPSEC_DELETE_SA pDeleteSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Delete the SA matching the particulars passed in. Both inbound and
|
|
outbound SAs are deleted. No timer set for inbound SA.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pSA, pInboundSA;
|
|
PLIST_ENTRY pEntry, pSAEntry;
|
|
KIRQL kIrql;
|
|
LONG Index;
|
|
LONG SAIndex;
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
//
|
|
// Walk through the outbound SAs and delete matched ones.
|
|
//
|
|
for ( Index = OUTBOUND_TRANSPORT_FILTER;
|
|
Index <= OUTBOUND_TUNNEL_FILTER;
|
|
Index += TRANSPORT_TUNNEL_INCREMENT) {
|
|
|
|
for ( pEntry = g_ipsec.FilterList[Index].Flink;
|
|
pEntry != &g_ipsec.FilterList[Index];
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex++) {
|
|
|
|
pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
|
|
while (pSAEntry != &pFilter->SAChain[SAIndex]) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
pSAEntry = pSAEntry->Flink;
|
|
|
|
if (IPSecMatchSATemplate(pSA, &pDeleteSA->SATemplate)) {
|
|
ASSERT(pSA->sa_State == STATE_SA_ACTIVE);
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_OUTBOUND);
|
|
ASSERT(pSA->sa_AssociatedSA);
|
|
|
|
pInboundSA = pSA->sa_AssociatedSA;
|
|
if (pInboundSA) {
|
|
IPSecDeleteInboundSA(pInboundSA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecExpireSA(
|
|
IN PIPSEC_EXPIRE_SA pExpireSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Expires the SA matching the particulars passed in.
|
|
Applied to Inbound SAs - we place the SA in the timer queue
|
|
for the next time the timer hits. Also, we delete the
|
|
corresponding outbound SA so no further packets match that
|
|
SA.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
The final status from the operation.
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pInboundSA;
|
|
KIRQL kIrql;
|
|
NTSTATUS status;
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
pInboundSA = IPSecLookupSABySPI(pExpireSA->DelInfo.SPI,
|
|
pExpireSA->DelInfo.DestAddr);
|
|
|
|
if (pInboundSA) {
|
|
ASSERT((pInboundSA->sa_Flags & FLAGS_SA_OUTBOUND) == 0);
|
|
|
|
if (pInboundSA->sa_State == STATE_SA_ACTIVE) {
|
|
IPSEC_DEBUG(ACQUIRE, ("Expiring SA: %lx\n", pInboundSA));
|
|
|
|
if (pInboundSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
pInboundSA->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&pInboundSA->sa_FilterLinkage);
|
|
}
|
|
|
|
pInboundSA->sa_Flags |= FLAGS_SA_DELETE_BY_IOCTL;
|
|
|
|
IPSecExpireInboundSA(pInboundSA);
|
|
}
|
|
|
|
status = STATUS_SUCCESS;
|
|
} else {
|
|
IPSEC_DEBUG(ACQUIRE, ("Expire for a non-existent SA: %lx\n", pExpireSA));
|
|
|
|
status = STATUS_NO_MATCH;
|
|
}
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecSAExpired(
|
|
IN PIPSEC_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called when an SA has expired or when a Larval SA has timed out.
|
|
|
|
Arguments:
|
|
|
|
pTimer - the timer struct
|
|
|
|
Context - SA ptr
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the buffer is to be held on to, the normal case.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pSA = (PSA_TABLE_ENTRY)Context;
|
|
PSA_TABLE_ENTRY pOutboundSA;
|
|
KIRQL kIrql;
|
|
KIRQL kIrql1;
|
|
KIRQL kIrql2;
|
|
KIRQL OldIrq;
|
|
|
|
IPSEC_DEBUG(TIMER, ("IPSecSAExpired: pSA: %lx state: %lx\n", pSA, pSA->sa_State));
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql1);
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
|
|
|
switch (pSA->sa_State) {
|
|
case STATE_SA_CREATED:
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
case STATE_SA_LARVAL:
|
|
case STATE_SA_ASSOCIATED:
|
|
//
|
|
// Lock the larval list so this SA does not go away.
|
|
//
|
|
ASSERT((pSA->sa_Flags & FLAGS_SA_OUTBOUND) == 0);
|
|
|
|
//
|
|
// Remove from larval list
|
|
//
|
|
IPSecRemoveEntryList(&pSA->sa_LarvalLinkage);
|
|
IPSEC_DEC_STATISTIC(dwNumPendingKeyOps);
|
|
|
|
//
|
|
// Also remove from Pending list if queued there.
|
|
//
|
|
ACQUIRE_LOCK(&g_ipsec.AcquireInfo.Lock, &kIrql1);
|
|
if (pSA->sa_Flags & FLAGS_SA_PENDING) {
|
|
ASSERT(pSA->sa_State == STATE_SA_LARVAL);
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecSAExpired: Removed from pending too: %lx\n", pSA));
|
|
IPSecRemoveEntryList(&pSA->sa_PendingLinkage);
|
|
pSA->sa_Flags &= ~FLAGS_SA_PENDING;
|
|
}
|
|
RELEASE_LOCK(&g_ipsec.AcquireInfo.Lock, kIrql1);
|
|
|
|
//
|
|
// Flush all the queued packets
|
|
//
|
|
IPSecFlushQueuedPackets(pSA, STATUS_TIMEOUT);
|
|
|
|
//
|
|
// remove from inbound sa list
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql1);
|
|
IPSecRemoveSPIEntry(pSA);
|
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql1);
|
|
|
|
//
|
|
// also remove from the filter list
|
|
//
|
|
if (pSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
pSA->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&pSA->sa_FilterLinkage);
|
|
}
|
|
|
|
//
|
|
// invalidate the associated cache entry
|
|
//
|
|
ACQUIRE_LOCK(&pSA->sa_Lock, &kIrql2);
|
|
if (pSA->sa_AcquireCtx) {
|
|
IPSecInvalidateHandle(pSA->sa_AcquireCtx);
|
|
pSA->sa_AcquireCtx = NULL;
|
|
}
|
|
RELEASE_LOCK(&pSA->sa_Lock, kIrql2);
|
|
|
|
IPSecInvalidateSACacheEntry(pSA);
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_TIMER_STARTED;
|
|
|
|
if (pSA->sa_RekeyOriginalSA) {
|
|
ASSERT(pSA->sa_Flags & FLAGS_SA_REKEY);
|
|
ASSERT(pSA->sa_RekeyOriginalSA->sa_RekeyLarvalSA == pSA);
|
|
ASSERT(pSA->sa_RekeyOriginalSA->sa_Flags & FLAGS_SA_REKEY_ORI);
|
|
|
|
pSA->sa_RekeyOriginalSA->sa_Flags &= ~FLAGS_SA_REKEY_ORI;
|
|
pSA->sa_RekeyOriginalSA->sa_RekeyLarvalSA = NULL;
|
|
pSA->sa_RekeyOriginalSA = NULL;
|
|
}
|
|
|
|
if (pOutboundSA = pSA->sa_AssociatedSA) {
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumActiveAssociations);
|
|
IPSEC_DEC_TUNNELS(pOutboundSA);
|
|
IPSEC_DECREMENT(g_ipsec.NumOutboundSAs);
|
|
|
|
IPSecCleanupOutboundSA(pSA, pOutboundSA, FALSE);
|
|
}
|
|
|
|
IPSEC_DEBUG(REF, ("Timer in Deref\n"));
|
|
IPSecDerefSA(pSA);
|
|
|
|
break;
|
|
|
|
case STATE_SA_ZOMBIE:
|
|
ASSERT(FALSE);
|
|
break;
|
|
|
|
case STATE_SA_ACTIVE:
|
|
//
|
|
// Inbound SA being expired; outbound was deleted immediately
|
|
//
|
|
ASSERT((pSA->sa_Flags & FLAGS_SA_OUTBOUND) == 0);
|
|
|
|
ACQUIRE_LOCK(&g_ipsec.AcquireInfo.Lock, &OldIrq);
|
|
IPSecNotifySAExpiration(pSA, NULL, OldIrq, FALSE);
|
|
|
|
//
|
|
// remove from inbound sa list
|
|
//
|
|
AcquireWriteLock(&g_ipsec.SPIListLock, &kIrql1);
|
|
IPSecRemoveSPIEntry(pSA);
|
|
ReleaseWriteLock(&g_ipsec.SPIListLock, kIrql1);
|
|
|
|
//
|
|
// also remove from the filter list
|
|
//
|
|
if (pSA->sa_Flags & FLAGS_SA_ON_FILTER_LIST) {
|
|
pSA->sa_Flags &= ~FLAGS_SA_ON_FILTER_LIST;
|
|
IPSecRemoveEntryList(&pSA->sa_FilterLinkage);
|
|
}
|
|
|
|
//
|
|
// invalidate the associated cache entry
|
|
//
|
|
IPSecInvalidateSACacheEntry(pSA);
|
|
|
|
pSA->sa_Flags &= ~FLAGS_SA_TIMER_STARTED;
|
|
|
|
if (pOutboundSA = pSA->sa_AssociatedSA) {
|
|
|
|
IPSEC_DEC_STATISTIC(dwNumActiveAssociations);
|
|
IPSEC_DEC_TUNNELS(pOutboundSA);
|
|
IPSEC_DECREMENT(g_ipsec.NumOutboundSAs);
|
|
|
|
IPSecCleanupOutboundSA(pSA, pOutboundSA, FALSE);
|
|
}
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) {
|
|
IPSecDelHWSAAtDpc(pSA);
|
|
}
|
|
|
|
ASSERT(pSA->sa_AssociatedSA == NULL);
|
|
IPSEC_DEBUG(REF, ("Timer#2 in Deref\n"));
|
|
IPSecDerefSA(pSA);
|
|
|
|
break;
|
|
|
|
default:
|
|
ASSERT(FALSE);
|
|
}
|
|
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql1);
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecFillSAInfo(
|
|
IN PSA_TABLE_ENTRY pSA,
|
|
OUT PIPSEC_SA_INFO pBuf
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fill out the SA_INFO structure.
|
|
|
|
Arguments:
|
|
|
|
pSA - SA to be filled in
|
|
pBuf - where to fill in
|
|
|
|
Returns:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
LONG Index;
|
|
PSA_TABLE_ENTRY pAssociatedSA = pSA->sa_AssociatedSA;
|
|
|
|
RtlZeroMemory(pBuf, sizeof(IPSEC_SA_INFO));
|
|
|
|
pBuf->PolicyId = pSA->sa_Filter->PolicyId;
|
|
pBuf->FilterId = pSA->sa_Filter->FilterId;
|
|
pBuf->Lifetime = pSA->sa_Lifetime;
|
|
pBuf->InboundTunnelAddr = pSA->sa_SrcTunnelAddr;
|
|
pBuf->NumOps = pSA->sa_NumOps;
|
|
|
|
pBuf->dwQMPFSGroup = pSA->sa_QMPFSGroup;
|
|
RtlCopyMemory( &pBuf->CookiePair,
|
|
&pSA->sa_CookiePair,
|
|
sizeof(IKE_COOKIE_PAIR));
|
|
|
|
for (Index = 0; Index < pSA->sa_NumOps; Index++) {
|
|
pBuf->Operation[Index] = pSA->sa_Operation[Index];
|
|
|
|
pBuf->EXT_INT_ALGO_EX(Index) = pSA->INT_ALGO(Index);
|
|
pBuf->EXT_INT_KEYLEN_EX(Index) = pSA->INT_KEYLEN(Index);
|
|
pBuf->EXT_INT_ROUNDS_EX(Index) = pSA->INT_ROUNDS(Index);
|
|
|
|
pBuf->EXT_CONF_ALGO_EX(Index) = pSA->CONF_ALGO(Index);
|
|
pBuf->EXT_CONF_KEYLEN_EX(Index) = pSA->CONF_KEYLEN(Index);
|
|
pBuf->EXT_CONF_ROUNDS_EX(Index) = pSA->CONF_ROUNDS(Index);
|
|
|
|
if (pAssociatedSA) {
|
|
pBuf->InboundSPI[Index] = pAssociatedSA->sa_OtherSPIs[Index];
|
|
}
|
|
pBuf->OutboundSPI[Index] = pSA->sa_OtherSPIs[Index];
|
|
}
|
|
|
|
pBuf->AssociatedFilter.SrcAddr = pSA->SA_SRC_ADDR & pSA->SA_SRC_MASK;
|
|
pBuf->AssociatedFilter.SrcMask = pSA->SA_SRC_MASK;
|
|
pBuf->AssociatedFilter.DestAddr = pSA->SA_DEST_ADDR & pSA->SA_DEST_MASK;
|
|
pBuf->AssociatedFilter.DestMask = pSA->SA_DEST_MASK;
|
|
pBuf->AssociatedFilter.Protocol = pSA->SA_PROTO;
|
|
pBuf->AssociatedFilter.SrcPort = SA_SRC_PORT(pSA);
|
|
pBuf->AssociatedFilter.DestPort = SA_DEST_PORT(pSA);
|
|
pBuf->AssociatedFilter.TunnelAddr = pSA->sa_TunnelAddr;
|
|
pBuf->AssociatedFilter.TunnelFilter = (pSA->sa_Flags & FLAGS_SA_TUNNEL) != 0;
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_OUTBOUND) {
|
|
pBuf->AssociatedFilter.Flags = FILTER_FLAGS_OUTBOUND;
|
|
} else {
|
|
pBuf->AssociatedFilter.Flags = FILTER_FLAGS_INBOUND;
|
|
}
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_INITIATOR) {
|
|
pBuf->EnumFlags |= SA_ENUM_FLAGS_INITIATOR;
|
|
}
|
|
if (pSA->sa_Flags & FLAGS_SA_MTU_BUMPED) {
|
|
pBuf->EnumFlags |= SA_ENUM_FLAGS_MTU_BUMPED;
|
|
}
|
|
if (pSA->sa_Flags & FLAGS_SA_HW_PLUMBED) {
|
|
pBuf->EnumFlags |= SA_ENUM_FLAGS_OFFLOADED;
|
|
}
|
|
if (pSA->sa_Flags & FLAGS_SA_HW_PLUMB_FAILED) {
|
|
pBuf->EnumFlags |= SA_ENUM_FLAGS_OFFLOAD_FAILED;
|
|
}
|
|
if (pSA->sa_Flags & FLAGS_SA_OFFLOADABLE) {
|
|
pBuf->EnumFlags |= SA_ENUM_FLAGS_OFFLOADABLE;
|
|
}
|
|
if (pSA->sa_Flags & FLAGS_SA_REKEY_ORI) {
|
|
pBuf->EnumFlags |= SA_ENUM_FLAGS_IN_REKEY;
|
|
}
|
|
|
|
pBuf->Stats.ConfidentialBytesSent = pSA->sa_Stats.ConfidentialBytesSent;
|
|
pBuf->Stats.AuthenticatedBytesSent = pSA->sa_Stats.AuthenticatedBytesSent;
|
|
pBuf->Stats.TotalBytesSent = pSA->sa_Stats.TotalBytesSent;
|
|
pBuf->Stats.OffloadedBytesSent = pSA->sa_Stats.OffloadedBytesSent;
|
|
|
|
if (pAssociatedSA) {
|
|
pBuf->Stats.ConfidentialBytesReceived =
|
|
pAssociatedSA->sa_Stats.ConfidentialBytesReceived;
|
|
pBuf->Stats.AuthenticatedBytesReceived =
|
|
pAssociatedSA->sa_Stats.AuthenticatedBytesReceived;
|
|
pBuf->Stats.TotalBytesReceived =
|
|
pAssociatedSA->sa_Stats.TotalBytesReceived;
|
|
pBuf->Stats.OffloadedBytesReceived =
|
|
pAssociatedSA->sa_Stats.OffloadedBytesReceived;
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecEnumSAs(
|
|
IN PIRP pIrp,
|
|
OUT PULONG pBytesCopied
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Fills in the request to enumerate SAs.
|
|
|
|
Arguments:
|
|
|
|
pIrp - The actual Irp
|
|
pBytesCopied - the number of bytes copied.
|
|
|
|
Returns:
|
|
|
|
Status of the operation.
|
|
|
|
--*/
|
|
{
|
|
PNDIS_BUFFER NdisBuffer = NULL;
|
|
PIPSEC_ENUM_SAS pEnum = NULL;
|
|
ULONG BufferLength = 0;
|
|
KIRQL kIrql;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pSAEntry;
|
|
IPSEC_SA_INFO infoBuff = {0};
|
|
NTSTATUS status = STATUS_SUCCESS;
|
|
ULONG BytesCopied = 0;
|
|
ULONG Offset = 0;
|
|
PFILTER pFilter;
|
|
PSA_TABLE_ENTRY pSA;
|
|
LONG Index;
|
|
LONG FilterIndex;
|
|
LONG SAIndex;
|
|
|
|
//
|
|
// Get at the IO buffer - its in the MDL
|
|
//
|
|
NdisBuffer = REQUEST_NDIS_BUFFER(pIrp);
|
|
if (NdisBuffer == NULL) {
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
NdisQueryBufferSafe(NdisBuffer,
|
|
(PVOID *)&pEnum,
|
|
&BufferLength,
|
|
NormalPagePriority);
|
|
|
|
//
|
|
// Make sure NdisQueryBufferSafe succeeds.
|
|
//
|
|
if (!pEnum) {
|
|
IPSEC_DEBUG (IOCTL, ("EnumSAs failed, no resources\n"));
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
|
|
//
|
|
// Make sure we have enough room for just the header not
|
|
// including the data.
|
|
//
|
|
if (BufferLength < (UINT)(FIELD_OFFSET(IPSEC_ENUM_SAS, pInfo[0]))) {
|
|
IPSEC_DEBUG (IOCTL, ("EnumSAs failed, buffer too small\n"));
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
//
|
|
// Make sure we are naturally aligned.
|
|
//
|
|
if (((ULONG_PTR)(pEnum)) & (TYPE_ALIGNMENT(IPSEC_ENUM_SAS) - 1)) {
|
|
IPSEC_DEBUG (IOCTL, ("EnumSAs failed, alignment\n"));
|
|
return STATUS_DATATYPE_MISALIGNMENT_ERROR;
|
|
}
|
|
|
|
pEnum->NumEntries = 0;
|
|
pEnum->NumEntriesPresent = 0;
|
|
|
|
//
|
|
// Now copy over the SA data into the user buffer and fit as many as possible.
|
|
//
|
|
BufferLength -= FIELD_OFFSET(IPSEC_ENUM_SAS, pInfo[0]);
|
|
Offset = FIELD_OFFSET(IPSEC_ENUM_SAS, pInfo[0]);
|
|
|
|
Index = pEnum->Index; // where to start?
|
|
|
|
AcquireReadLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
for ( FilterIndex = MIN_FILTER;
|
|
FilterIndex <= MAX_FILTER;
|
|
FilterIndex++) {
|
|
|
|
for ( pEntry = g_ipsec.FilterList[FilterIndex].Flink;
|
|
pEntry != &g_ipsec.FilterList[FilterIndex];
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex ++) {
|
|
|
|
for ( pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
pSAEntry != &pFilter->SAChain[SAIndex];
|
|
pSAEntry = pSAEntry->Flink) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
//
|
|
// Only interested in outbound or multicast SAs.
|
|
//
|
|
if (!(pSA->sa_Flags & FLAGS_SA_OUTBOUND)) {
|
|
continue;
|
|
}
|
|
|
|
//
|
|
// Dump only SAs that match the template.
|
|
//
|
|
if (IPSecMatchSATemplate(pSA, &pEnum->SATemplate)) {
|
|
if (Index > 0) {
|
|
Index--; // Skip number of Index SAs.
|
|
continue;
|
|
}
|
|
|
|
pEnum->NumEntriesPresent++;
|
|
|
|
if ((INT)(BufferLength - BytesCopied) >= (INT)sizeof(IPSEC_SA_INFO)) {
|
|
IPSecFillSAInfo(pSA, &infoBuff);
|
|
BytesCopied += sizeof(IPSEC_SA_INFO);
|
|
NdisBuffer = CopyToNdis(NdisBuffer, (UCHAR *)&infoBuff, sizeof(IPSEC_SA_INFO), &Offset);
|
|
if (!NdisBuffer) {
|
|
ReleaseReadLock(&g_ipsec.SADBLock, kIrql);
|
|
return STATUS_INSUFFICIENT_RESOURCES;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
ReleaseReadLock(&g_ipsec.SADBLock, kIrql);
|
|
|
|
pEnum->NumEntries = BytesCopied / sizeof(IPSEC_SA_INFO);
|
|
|
|
*pBytesCopied = BytesCopied + FIELD_OFFSET(IPSEC_ENUM_SAS, pInfo[0]);
|
|
|
|
if (pEnum->NumEntries < pEnum->NumEntriesPresent) {
|
|
status = STATUS_BUFFER_OVERFLOW;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecReaper(
|
|
IN PIPSEC_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called every 5 mins; reaps the (active) SA list
|
|
|
|
Arguments:
|
|
|
|
pTimer - the timer struct
|
|
|
|
Context - NULL
|
|
|
|
Return Value:
|
|
|
|
STATUS_PENDING if the buffer is to be held on to, the normal case.
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL kIrql;
|
|
|
|
IPSEC_DEBUG(TIMER, ("Entering IPSecReaper\n"));
|
|
|
|
AcquireWriteLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
//
|
|
// walk the outbound SAs and delete/expire them if they have been
|
|
// idle for sometime (lets say 5 mins for now).
|
|
//
|
|
IPSecReapIdleSAs();
|
|
|
|
ReleaseWriteLock(&g_ipsec.SADBLock, kIrql);
|
|
|
|
IPSEC_DEBUG(TIMER, ("Exiting IPSecReaper\n"));
|
|
if (!IPSEC_DRIVER_IS_INACTIVE()) {
|
|
IPSecStartTimer(&g_ipsec.ReaperTimer,
|
|
IPSecReaper,
|
|
IPSEC_REAPER_TIME,
|
|
(PVOID)NULL);
|
|
}
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecReapIdleSAs()
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called to reap the idle SA list
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
PSA_TABLE_ENTRY pSA;
|
|
PFILTER pFilter;
|
|
PLIST_ENTRY pEntry;
|
|
PLIST_ENTRY pSAEntry;
|
|
BOOLEAN fExpired;
|
|
LONG Index;
|
|
LONG SAIndex;
|
|
|
|
IPSEC_DEBUG(TIMER, ("Entering IPSecReapIdleSAs\n"));
|
|
|
|
//
|
|
// Walk the inbound SAs and delete/expire them if they have been
|
|
// idle for sometime (lets say 5 mins for now).
|
|
//
|
|
for ( Index = INBOUND_TRANSPORT_FILTER;
|
|
Index <= INBOUND_TUNNEL_FILTER;
|
|
Index += TRANSPORT_TUNNEL_INCREMENT) {
|
|
|
|
for ( pEntry = g_ipsec.FilterList[Index].Flink;
|
|
pEntry != &g_ipsec.FilterList[Index];
|
|
pEntry = pEntry->Flink) {
|
|
|
|
pFilter = CONTAINING_RECORD(pEntry,
|
|
FILTER,
|
|
MaskedLinkage);
|
|
|
|
for ( SAIndex = 0;
|
|
SAIndex < pFilter->SAChainSize;
|
|
SAIndex++) {
|
|
|
|
pSAEntry = pFilter->SAChain[SAIndex].Flink;
|
|
|
|
while (pSAEntry != &pFilter->SAChain[SAIndex]) {
|
|
|
|
pSA = CONTAINING_RECORD(pSAEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
ASSERT(!(pSA->sa_Flags & FLAGS_SA_OUTBOUND));
|
|
|
|
pSAEntry = pSAEntry->Flink;
|
|
|
|
if (!(pSA->sa_Flags & FLAGS_SA_IDLED_OUT) &&
|
|
(pSA->sa_State == STATE_SA_ACTIVE) &&
|
|
!(pSA->sa_Flags & FLAGS_SA_DISABLE_IDLE_OUT)) {
|
|
|
|
IPSEC_SA_EXPIRED(pSA, fExpired);
|
|
if (fExpired) {
|
|
pSA->sa_Flags |= FLAGS_SA_IDLED_OUT;
|
|
IPSecExpireInboundSA(pSA);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
IPSEC_DEBUG(TIMER, ("Exiting IPSecReapIdleSAs\n"));
|
|
}
|
|
|
|
|
|
VOID
|
|
IPSecFlushEventLog(
|
|
IN PIPSEC_TIMER pTimer,
|
|
IN PVOID Context
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Called every LogInterval seconds; flush all events currently logged.
|
|
|
|
Arguments:
|
|
|
|
pTimer - the timer struct
|
|
|
|
Context - NULL
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
KIRQL kIrql;
|
|
|
|
IPSEC_DEBUG(TIMER, ("Entering IPSecFlushEventLog\n"));
|
|
|
|
ACQUIRE_LOCK(&g_ipsec.EventLogLock, &kIrql);
|
|
|
|
if (g_ipsec.IPSecLogMemoryLoc > g_ipsec.IPSecLogMemory) {
|
|
//
|
|
// Flush the logs.
|
|
//
|
|
IPSecQueueLogEvent();
|
|
}
|
|
|
|
RELEASE_LOCK(&g_ipsec.EventLogLock, kIrql);
|
|
|
|
if (!IPSEC_DRIVER_IS_INACTIVE()) {
|
|
IPSecStartTimer(&g_ipsec.EventLogTimer,
|
|
IPSecFlushEventLog,
|
|
g_ipsec.LogInterval,
|
|
(PVOID)NULL);
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecQuerySpi(
|
|
IN OUT PIPSEC_QUERY_SPI pQuerySpi
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Queries IPSEC for spis corresponding to given filter
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
NTSTATUS status;
|
|
|
|
ULARGE_INTEGER uliSrcDstAddr;
|
|
ULARGE_INTEGER uliProtoSrcDstPort;
|
|
PFILTER pFilter = NULL;
|
|
PSA_TABLE_ENTRY pSA = NULL;
|
|
PSA_TABLE_ENTRY pNextSA = NULL;
|
|
PSA_TABLE_ENTRY pTunnelSA = NULL;
|
|
KIRQL kIrql;
|
|
|
|
pQuerySpi->Spi = 0;
|
|
pQuerySpi->OtherSpi = 0;
|
|
pQuerySpi->Operation = 0;
|
|
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecQuerySPI: Src %08x.%04x Dst %08x.%04x Protocol %d",
|
|
pQuerySpi->Filter.SrcAddr,
|
|
pQuerySpi->Filter.SrcPort,
|
|
pQuerySpi->Filter.DestAddr,
|
|
pQuerySpi->Filter.DestPort,
|
|
pQuerySpi->Filter.Protocol));
|
|
|
|
IPSEC_BUILD_SRC_DEST_ADDR( uliSrcDstAddr,
|
|
pQuerySpi->Filter.SrcAddr,
|
|
pQuerySpi->Filter.DestAddr);
|
|
|
|
IPSEC_BUILD_PROTO_PORT_LI( uliProtoSrcDstPort,
|
|
pQuerySpi->Filter.Protocol,
|
|
pQuerySpi->Filter.SrcPort,
|
|
pQuerySpi->Filter.DestPort);
|
|
|
|
|
|
AcquireReadLock(&g_ipsec.SADBLock, &kIrql);
|
|
|
|
//
|
|
// search for SA
|
|
//
|
|
status = IPSecLookupSAByAddr( uliSrcDstAddr,
|
|
uliProtoSrcDstPort,
|
|
&pFilter,
|
|
&pSA,
|
|
&pNextSA,
|
|
&pTunnelSA,
|
|
FALSE,
|
|
FALSE,
|
|
FALSE);
|
|
|
|
if (!NT_SUCCESS(status)) {
|
|
IPSEC_DEBUG(ACQUIRE, ("IPSecQuerySPI: IPSecLookupSAByAddr failed: %lx\n", status));
|
|
ReleaseReadLock(&g_ipsec.SADBLock, kIrql);
|
|
return status;
|
|
}
|
|
|
|
if (status == STATUS_SUCCESS) {
|
|
ASSERT(pSA);
|
|
} else {
|
|
ReleaseReadLock(&g_ipsec.SADBLock, kIrql);
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
pQuerySpi->Spi = pSA->sa_SPI;
|
|
|
|
if (pSA->sa_AssociatedSA) {
|
|
pQuerySpi->OtherSpi = pSA->sa_AssociatedSA->sa_SPI;
|
|
}
|
|
|
|
pQuerySpi->Operation = pSA->sa_Operation[pSA->sa_NumOps-1];
|
|
|
|
ReleaseReadLock(&g_ipsec.SADBLock, kIrql);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecSetOperationMode(
|
|
IN PIPSEC_SET_OPERATION_MODE pSetOperationMode
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the driver operation mode.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
g_ipsec.OperationMode = pSetOperationMode->OperationMode;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecInitializeTcpip(
|
|
IN PIPSEC_SET_TCPIP_STATUS pSetTcpipStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Initialize TCP/IP.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
IPInfo Info;
|
|
|
|
if (IPSEC_DRIVER_INIT_TCPIP()) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Store all TCP/IP function pointers for future use. There is no check
|
|
// for NULL pointer here because the function pointer can also be stale
|
|
// address. We trust TCP/IP to pass in the values corretly.
|
|
//
|
|
TCPIP_FREE_BUFF = pSetTcpipStatus->TcpipFreeBuff;
|
|
TCPIP_ALLOC_BUFF = pSetTcpipStatus->TcpipAllocBuff;
|
|
TCPIP_GET_INFO = pSetTcpipStatus->TcpipGetInfo;
|
|
TCPIP_NDIS_REQUEST = pSetTcpipStatus->TcpipNdisRequest;
|
|
TCPIP_SET_IPSEC_STATUS = pSetTcpipStatus->TcpipSetIPSecStatus;
|
|
TCPIP_SET_IPSEC = pSetTcpipStatus->TcpipSetIPSecPtr;
|
|
TCPIP_UNSET_IPSEC = pSetTcpipStatus->TcpipUnSetIPSecPtr;
|
|
TCPIP_UNSET_IPSEC_SEND = pSetTcpipStatus->TcpipUnSetIPSecSendPtr;
|
|
TCPIP_TCP_XSUM = pSetTcpipStatus->TcpipTCPXsum;
|
|
|
|
//
|
|
// Initialize IPInfo for reinjecting packets to TCP/IP.
|
|
//
|
|
if (TCPIP_GET_INFO(&Info, sizeof(IPInfo)) != IP_SUCCESS) {
|
|
ASSERT(FALSE);
|
|
return STATUS_BUFFER_TOO_SMALL;
|
|
}
|
|
|
|
Info.ipi_initopts(&g_ipsec.OptInfo);
|
|
|
|
//
|
|
// The followings come from IPInfo.
|
|
//
|
|
TCPIP_REGISTER_PROTOCOL = Info.ipi_protreg;
|
|
TCPIP_DEREGISTER_PROTOCOL = Info.ipi_protdereg;
|
|
TCPIP_IP_TRANSMIT = Info.ipi_xmit;
|
|
TCPIP_GET_ADDRTYPE = Info.ipi_getaddrtype;
|
|
TCPIP_GEN_IPID = Info.ipi_getipid;
|
|
|
|
//
|
|
// Don't register IPSecStatus function for AH and ESP protocol here.
|
|
// Registration occurs with filter addition.
|
|
//
|
|
|
|
//
|
|
// Everything is ready to go, bind to IP so we will intercept traffic.
|
|
//
|
|
IPSecBindToIP();
|
|
|
|
IPSEC_DRIVER_INIT_TCPIP() = TRUE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecDeinitializeTcpip(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Deinitialize TCP/IP.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
if (!IPSEC_DRIVER_INIT_TCPIP()) {
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
IPSEC_DRIVER_INIT_TCPIP() = FALSE;
|
|
|
|
//
|
|
// Unbind IPSecHandlerPtr from TCP/IP and wait for all transmits, pending
|
|
// sends, worker threads and iotcls to complete.
|
|
//
|
|
IPSecUnbindSendFromIP();
|
|
|
|
//
|
|
// Wait for all threads (transmits) to finish.
|
|
//
|
|
while (IPSEC_GET_VALUE(g_ipsec.NumThreads) != 0) {
|
|
IPSEC_DELAY_EXECUTION();
|
|
}
|
|
|
|
//
|
|
// Wait for all pending IOCTLs to finish. Note this current IOCTL also
|
|
// takes one count.
|
|
//
|
|
while (IPSEC_GET_VALUE(g_ipsec.NumIoctls) != 1) {
|
|
IPSEC_DELAY_EXECUTION();
|
|
}
|
|
|
|
//
|
|
// Wait for all worker threads (logs or plumbs) to finish.
|
|
//
|
|
while (IPSEC_GET_VALUE(g_ipsec.NumWorkers) != 0) {
|
|
IPSEC_DELAY_EXECUTION();
|
|
}
|
|
|
|
//
|
|
// Wait for all send completes to go through.
|
|
//
|
|
while (IPSEC_GET_VALUE(g_ipsec.NumSends) != 0) {
|
|
IPSEC_DELAY_EXECUTION();
|
|
}
|
|
|
|
//
|
|
// Reset IPSecStatus functions in TCP/IP to NULL.
|
|
//
|
|
if (IPSEC_GET_VALUE(gdwInitEsp)) {
|
|
TCPIP_DEREGISTER_PROTOCOL(PROTOCOL_ESP);
|
|
IPSEC_SET_VALUE(gdwInitEsp, 0);
|
|
}
|
|
if (IPSEC_GET_VALUE(gdwInitAh)) {
|
|
TCPIP_DEREGISTER_PROTOCOL(PROTOCOL_AH);
|
|
IPSEC_SET_VALUE(gdwInitAh, 0);
|
|
}
|
|
|
|
//
|
|
// Unbind the rest of IPSec routines from TCP/IP.
|
|
//
|
|
IPSecUnbindFromIP();
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecSetTcpipStatus(
|
|
IN PIPSEC_SET_TCPIP_STATUS pSetTcpipStatus
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Set the TCP/IP driver status indicating whether can register with it.
|
|
|
|
Arguments:
|
|
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PAGED_CODE();
|
|
|
|
if (pSetTcpipStatus->TcpipStatus) {
|
|
return IPSecInitializeTcpip(pSetTcpipStatus);
|
|
} else {
|
|
return IPSecDeinitializeTcpip();
|
|
}
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecResetCacheTable(
|
|
VOID
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Invalidate all cache entries and its associated SA or Filter.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
Notes:
|
|
|
|
|
|
--*/
|
|
{
|
|
PFILTER_CACHE pCache;
|
|
ULONG i;
|
|
|
|
for (i = 0; i < g_ipsec.CacheSize; i ++) {
|
|
pCache = g_ipsec.ppCache[i];
|
|
if (pCache && IS_VALID_CACHE_ENTRY(pCache)) {
|
|
if (pCache->FilterEntry) {
|
|
pCache->pFilter->FilterCache = NULL;
|
|
} else {
|
|
pCache->pSAEntry->sa_FilterCache = NULL;
|
|
if (pCache->pNextSAEntry) {
|
|
pCache->pNextSAEntry->sa_FilterCache = NULL;
|
|
}
|
|
}
|
|
INVALIDATE_CACHE_ENTRY(pCache);
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecPurgeFilterSAs(
|
|
IN PFILTER pFilter
|
|
)
|
|
/*++
|
|
|
|
Routine Description
|
|
|
|
Delete all SAs that are related to this filter.
|
|
|
|
Locks
|
|
|
|
Called with SADB held.
|
|
|
|
Arguments
|
|
|
|
pFilter - filter of interest
|
|
|
|
Return Value
|
|
|
|
STATUS_SUCCESS
|
|
|
|
--*/
|
|
{
|
|
PLIST_ENTRY pEntry;
|
|
PSA_TABLE_ENTRY pSA;
|
|
KIRQL kIrql;
|
|
LONG Index;
|
|
|
|
//
|
|
// Expire each inbound SA and delete outbound SA
|
|
//
|
|
for (Index = 0; Index < pFilter->SAChainSize; Index ++) {
|
|
pEntry = pFilter->SAChain[Index].Flink;
|
|
|
|
while (pEntry != &pFilter->SAChain[Index]) {
|
|
|
|
pSA = CONTAINING_RECORD(pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_FilterLinkage);
|
|
|
|
pEntry = pEntry->Flink;
|
|
|
|
if (pSA->sa_State == STATE_SA_ACTIVE) {
|
|
IPSEC_DEBUG(ACQUIRE, ("Destroying active SA: %lx\n", pSA));
|
|
//
|
|
// Filter is going away, SA must be deleted now
|
|
//
|
|
if (!(pSA->sa_Flags & FLAGS_SA_OUTBOUND)) {
|
|
//ASSERT(pSA->sa_AssociatedSA);
|
|
IPSecDeleteInboundSA(pSA);
|
|
} else {
|
|
ASSERT(pSA->sa_AssociatedSA);
|
|
if (pSA->sa_AssociatedSA->sa_State == STATE_SA_ASSOCIATED) {
|
|
IPSecDeleteLarvalSA(pSA->sa_AssociatedSA);
|
|
} else {
|
|
IPSecDeleteInboundSA(pSA->sa_AssociatedSA);
|
|
}
|
|
}
|
|
} else {
|
|
IPSEC_DEBUG(ACQUIRE, ("Destroying larval SA: %lx\n", pSA));
|
|
//
|
|
// SA undergoing negotiation - just invalidate the context.
|
|
// the timer will take care of the rest
|
|
//
|
|
if (pSA->sa_AssociatedSA) {
|
|
if (pSA->sa_AssociatedSA->sa_AcquireCtx) {
|
|
IPSecInvalidateHandle(pSA->sa_AssociatedSA->sa_AcquireCtx);
|
|
pSA->sa_AssociatedSA->sa_AcquireCtx = NULL;
|
|
}
|
|
}
|
|
|
|
IPSecDeleteLarvalSA(pSA);
|
|
}
|
|
}
|
|
}
|
|
|
|
//
|
|
// Also need to remove all those larval SAs whose sa_Filter is pointing
|
|
// to the filter being deleted.
|
|
//
|
|
ACQUIRE_LOCK(&g_ipsec.LarvalListLock, &kIrql);
|
|
|
|
pEntry = g_ipsec.LarvalSAList.Flink;
|
|
while (pEntry != &g_ipsec.LarvalSAList) {
|
|
pSA = CONTAINING_RECORD(pEntry,
|
|
SA_TABLE_ENTRY,
|
|
sa_LarvalLinkage);
|
|
pEntry = pEntry->Flink;
|
|
|
|
if (pSA->sa_Filter == pFilter) {
|
|
IPSecRemoveEntryList(&pSA->sa_LarvalLinkage);
|
|
IPSEC_DEC_STATISTIC(dwNumPendingKeyOps);
|
|
IPSecCleanupLarvalSA(pSA);
|
|
}
|
|
}
|
|
|
|
RELEASE_LOCK(&g_ipsec.LarvalListLock, kIrql);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
|
|
NTSTATUS
|
|
IPSecSetupSALifetime(
|
|
IN PSA_TABLE_ENTRY pSA
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Setup the SA lifetime characteristics for rekey and idle timeout.
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER CurrentTime;
|
|
LARGE_INTEGER Delta = {0};
|
|
LARGE_INTEGER Pad = {(pSA->sa_Flags & FLAGS_SA_INITIATOR)?
|
|
IPSEC_EXPIRE_TIME_PAD_I :
|
|
IPSEC_EXPIRE_TIME_PAD_R,
|
|
0};
|
|
|
|
//
|
|
// pSA->sa_Lifetime.KeyExpirationTime is in seconds.
|
|
//
|
|
if (pSA->sa_Lifetime.KeyExpirationTime) {
|
|
IPSEC_CONVERT_SECS_TO_100NS(Delta, pSA->sa_Lifetime.KeyExpirationTime);
|
|
|
|
NdisGetCurrentSystemTime(&CurrentTime);
|
|
|
|
pSA->sa_KeyExpirationTime.QuadPart = (CurrentTime.QuadPart + Delta.QuadPart);
|
|
|
|
pSA->sa_KeyExpirationTimeWithPad.QuadPart = pSA->sa_KeyExpirationTime.QuadPart - Pad.QuadPart;
|
|
|
|
if (!(pSA->sa_KeyExpirationTimeWithPad.QuadPart > 0i64)) {
|
|
pSA->sa_KeyExpirationTimeWithPad.QuadPart = 0i64;
|
|
}
|
|
}
|
|
|
|
//
|
|
// pSA->sa_Lifetime.KeyExpirationBytes is in Kbytes.
|
|
//
|
|
if (pSA->sa_Lifetime.KeyExpirationBytes) {
|
|
pSA->sa_KeyExpirationBytes.LowPart = pSA->sa_Lifetime.KeyExpirationBytes;
|
|
pSA->sa_KeyExpirationBytes = EXTENDED_MULTIPLY(pSA->sa_KeyExpirationBytes, 1024);
|
|
|
|
if (pSA->sa_Flags & FLAGS_SA_INITIATOR) {
|
|
pSA->sa_KeyExpirationBytesWithPad.LowPart = pSA->sa_Lifetime.KeyExpirationBytes * IPSEC_EXPIRE_THRESHOLD_I / 100;
|
|
} else {
|
|
pSA->sa_KeyExpirationBytesWithPad.LowPart = pSA->sa_Lifetime.KeyExpirationBytes * IPSEC_EXPIRE_THRESHOLD_R / 100;
|
|
}
|
|
|
|
pSA->sa_KeyExpirationBytesWithPad = EXTENDED_MULTIPLY(pSA->sa_KeyExpirationBytesWithPad, 1024);
|
|
}
|
|
|
|
//
|
|
// Also setup the idle timeout characteristics.
|
|
//
|
|
if (pSA->sa_Flags & FLAGS_SA_INITIATOR) {
|
|
IPSEC_CONVERT_SECS_TO_100NS(pSA->sa_IdleTime,
|
|
(g_ipsec.DefaultSAIdleTime + IPSEC_DEFAULT_SA_IDLE_TIME_PAD_I));
|
|
} else {
|
|
IPSEC_CONVERT_SECS_TO_100NS(pSA->sa_IdleTime,
|
|
(g_ipsec.DefaultSAIdleTime + IPSEC_DEFAULT_SA_IDLE_TIME_PAD_R));
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
DWORD ConvertAddr(IPAddr Addr, IPAddr Mask, ADDR* OutAddr)
|
|
{
|
|
|
|
if (Mask == 0xffffffff) {
|
|
OutAddr->AddrType=IP_ADDR_UNIQUE;
|
|
} else {
|
|
OutAddr->AddrType=IP_ADDR_SUBNET;
|
|
}
|
|
|
|
OutAddr->uSubNetMask=Mask;
|
|
OutAddr->uIpAddr=Addr;
|
|
|
|
return STATUS_SUCCESS;
|
|
|
|
}
|
|
|
|
DWORD ConvertSAToIPSecQMSA(PIPSEC_QM_SA pOutSA,
|
|
PSA_TABLE_ENTRY pInSA)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Convert SA_TABLE_ENTRY to IPSEC_QM_SA
|
|
|
|
Arguments:
|
|
|
|
|
|
Return Value:
|
|
|
|
|
|
--*/
|
|
{
|
|
int i;
|
|
|
|
memcpy(&pOutSA->gQMPolicyID,&pInSA->sa_Filter->PolicyId,sizeof(GUID));
|
|
memcpy(&pOutSA->gQMFilterID,&pInSA->sa_Filter->FilterId,sizeof(GUID));
|
|
|
|
memcpy(&pOutSA->MMSpi.Initiator,&pInSA->sa_CookiePair.Initiator,sizeof(IKE_COOKIE));
|
|
memcpy(&pOutSA->MMSpi.Responder,&pInSA->sa_CookiePair.Responder,sizeof(IKE_COOKIE));
|
|
|
|
ConvertAddr(pInSA->SA_SRC_ADDR,pInSA->SA_SRC_MASK,&pOutSA->IpsecQMFilter.SrcAddr);
|
|
ConvertAddr(pInSA->SA_DEST_ADDR,pInSA->SA_DEST_MASK,&pOutSA->IpsecQMFilter.DesAddr);
|
|
|
|
pOutSA->IpsecQMFilter.Protocol.ProtocolType=PROTOCOL_UNIQUE;
|
|
pOutSA->IpsecQMFilter.Protocol.dwProtocol=pInSA->SA_PROTO;
|
|
|
|
pOutSA->IpsecQMFilter.SrcPort.PortType=PORT_UNIQUE;
|
|
pOutSA->IpsecQMFilter.SrcPort.wPort=NET_SHORT(SA_SRC_PORT(pInSA));
|
|
|
|
pOutSA->IpsecQMFilter.DesPort.PortType=PORT_UNIQUE;
|
|
pOutSA->IpsecQMFilter.DesPort.wPort=NET_SHORT(SA_DEST_PORT(pInSA));
|
|
|
|
if (pInSA->sa_Flags & FLAGS_SA_TUNNEL) {
|
|
pOutSA->IpsecQMFilter.QMFilterType = QM_TUNNEL_FILTER;
|
|
ConvertAddr(pInSA->sa_SrcTunnelAddr,0xffffffff,&pOutSA->IpsecQMFilter.MyTunnelEndpt);
|
|
ConvertAddr(pInSA->sa_TunnelAddr,0xffffffff,&pOutSA->IpsecQMFilter.PeerTunnelEndpt);
|
|
|
|
} else {
|
|
pOutSA->IpsecQMFilter.QMFilterType = QM_TRANSPORT_FILTER;
|
|
}
|
|
|
|
pOutSA->SelectedQMOffer.dwPFSGroup=pInSA->sa_QMPFSGroup;
|
|
if (pOutSA->SelectedQMOffer.dwPFSGroup) {
|
|
pOutSA->SelectedQMOffer.bPFSRequired=TRUE;
|
|
}
|
|
pOutSA->SelectedQMOffer.Lifetime.uKeyExpirationTime=pInSA->sa_Lifetime.KeyExpirationTime;
|
|
pOutSA->SelectedQMOffer.Lifetime.uKeyExpirationKBytes=pInSA->sa_Lifetime.KeyExpirationBytes;
|
|
|
|
pOutSA->SelectedQMOffer.dwNumAlgos=pInSA->sa_NumOps;
|
|
|
|
for (i=0; i < pInSA->sa_NumOps;i++) {
|
|
pOutSA->SelectedQMOffer.Algos[i].Operation=pInSA->sa_Operation[i];
|
|
if (pInSA->sa_AssociatedSA) {
|
|
pOutSA->SelectedQMOffer.Algos[i].MySpi= pInSA->sa_AssociatedSA->sa_OtherSPIs[i];
|
|
}
|
|
pOutSA->SelectedQMOffer.Algos[i].PeerSpi= pInSA->sa_OtherSPIs[i];
|
|
|
|
switch(pOutSA->SelectedQMOffer.Algos[i].Operation) {
|
|
case AUTHENTICATION:
|
|
pOutSA->SelectedQMOffer.Algos[i].uAlgoIdentifier=pInSA->INT_ALGO(i);
|
|
pOutSA->SelectedQMOffer.Algos[i].uAlgoKeyLen=pInSA->INT_KEYLEN(i);
|
|
pOutSA->SelectedQMOffer.Algos[i].uAlgoRounds=pInSA->INT_ROUNDS(i);
|
|
break;
|
|
case ENCRYPTION:
|
|
pOutSA->SelectedQMOffer.Algos[i].uAlgoIdentifier=pInSA->CONF_ALGO(i);
|
|
pOutSA->SelectedQMOffer.Algos[i].uAlgoKeyLen=pInSA->CONF_KEYLEN(i);
|
|
pOutSA->SelectedQMOffer.Algos[i].uAlgoRounds=pInSA->CONF_ROUNDS(i);
|
|
|
|
pOutSA->SelectedQMOffer.Algos[i].uSecAlgoIdentifier=pInSA->INT_ALGO(i);
|
|
pOutSA->SelectedQMOffer.Algos[i].uSecAlgoKeyLen=pInSA->INT_KEYLEN(i);
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
return STATUS_SUCCESS;
|
|
|
|
|
|
|
|
}
|
|
|
|
BOOLEAN
|
|
IPSecMatchSATemplate(
|
|
IN PSA_TABLE_ENTRY pSA,
|
|
IN PIPSEC_QM_SA pSATemplate
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Try to see if the SA passed in matches the template.
|
|
|
|
Arguments:
|
|
|
|
pSA - SA of interest
|
|
pSATemplate - SA template
|
|
|
|
Return Value:
|
|
|
|
TRUE/FALSE
|
|
|
|
--*/
|
|
{
|
|
LARGE_INTEGER ZeroLI = {0};
|
|
ADDR ZeroADDR = {0};
|
|
PROTOCOL ZeroPROTOCOL = {0};
|
|
PORT ZeroPORT = {0};
|
|
|
|
IPSEC_QM_SA CurSA;
|
|
memset(&CurSA,0,sizeof(IPSEC_QM_SA));
|
|
|
|
ConvertSAToIPSecQMSA(&CurSA,pSA);
|
|
|
|
return((BOOLEAN)MatchQMSATemplate(pSATemplate,&CurSA));
|
|
|
|
}
|
|
|