850 lines
24 KiB
C
850 lines
24 KiB
C
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF
|
|
// ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO
|
|
// THE IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A
|
|
// PARTICULAR PURPOSE.
|
|
//
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
/*++
|
|
|
|
Module Name:
|
|
|
|
Tcpcommon.c
|
|
|
|
Abstract:
|
|
|
|
This file contains common shared routines between the sample secure TCP
|
|
client and secure TCP server. These routines mainly contain sample code for
|
|
managing custom IPsec policy and querying IPsec SA information using the
|
|
FWP API.
|
|
|
|
--*/
|
|
|
|
#ifndef UNICODE
|
|
#define UNICODE
|
|
#endif
|
|
|
|
#include <wchar.h>
|
|
#include <Winsock2.h>
|
|
#include <mstcpip.h>
|
|
#include <ws2tcpip.h>
|
|
#include "Tcpcommon.h"
|
|
|
|
DWORD
|
|
AddCustomIPsecPolicy(
|
|
OUT HANDLE* fwpHandle,
|
|
OUT GUID* qmPolicyKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine creates a customized FWP IPsec quick mode policy and adds it to
|
|
the FWP platform.
|
|
|
|
Arguments:
|
|
|
|
fwpHandle - Pointer that will upon return contain the handle for the
|
|
FWP session created.
|
|
|
|
qmPolicyKey - Pointer that will upon return contain the unique
|
|
identifier of the quick mode policy that was just added.
|
|
|
|
Return Value:
|
|
|
|
Windows error code indicating the status of the operation, or NO_ERROR if
|
|
the operation succeeded.
|
|
|
|
--*/
|
|
{
|
|
DWORD result = 0;
|
|
IPSEC_TRANSPORT_POLICY0 qmPolicy = {0};
|
|
IPSEC_PROPOSAL0 qmProposal = {0};
|
|
IKEEXT_EM_POLICY0 emPolicy = {0};
|
|
IKEEXT_AUTHENTICATION_METHOD0 emAuthMethod = {0};
|
|
FWPM_PROVIDER_CONTEXT0 fwpProviderCtxt = {0};
|
|
IPSEC_SA_TRANSFORM0 qmTransform = {0};
|
|
IPSEC_AUTH_AND_CIPHER_TRANSFORM0 espAuthAndCipherTransform = {0};
|
|
|
|
//-----------------------------------------
|
|
// Construct the QM ESP algorithms (SHA1 for hash, AES_128 for encryption)
|
|
espAuthAndCipherTransform.authTransform.authTransformId =
|
|
IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96;
|
|
espAuthAndCipherTransform.cipherTransform.cipherTransformId =
|
|
IPSEC_CIPHER_TRANSFORM_ID_AES_128;
|
|
|
|
//-----------------------------------------
|
|
// Construct the QM ESP policy transform for ESP auth + cipher
|
|
qmTransform.ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;
|
|
qmTransform.espAuthAndCipherTransform = &espAuthAndCipherTransform;
|
|
|
|
//-----------------------------------------
|
|
// Construct the QM policy proposal
|
|
// QM lifetime 1 hr, 55GB,
|
|
qmProposal.lifetime.lifetimeSeconds = 60 * 60;
|
|
qmProposal.lifetime.lifetimeKilobytes = 55 * 1024;
|
|
qmProposal.lifetime.lifetimePackets = 0x7fffffff;
|
|
qmProposal.numSaTransforms = 1;
|
|
qmProposal.saTransforms = &qmTransform;
|
|
// No PFS
|
|
qmProposal.pfsGroup = IPSEC_PFS_NONE;
|
|
|
|
//-----------------------------------------
|
|
// Construct the EM auth method as Kerberos
|
|
emAuthMethod.authenticationMethodType = IKEEXT_KERBEROS;
|
|
emAuthMethod.kerberosAuthentication.flags = 0;
|
|
|
|
//-----------------------------------------
|
|
// Construct the EM policy
|
|
emPolicy.numAuthenticationMethods = 1;
|
|
emPolicy.authenticationMethods = &emAuthMethod;
|
|
// Turn on impersonation / user auth.
|
|
emPolicy.initiatorImpersonationType = IKEEXT_IMPERSONATION_SOCKET_PRINCIPAL;
|
|
|
|
//-----------------------------------------
|
|
// Construct the QM policy
|
|
qmPolicy.numIpsecProposals = 1;
|
|
qmPolicy.ipsecProposals = &qmProposal;
|
|
// Default value for ND clear timeout
|
|
qmPolicy.ndAllowClearTimeoutSeconds = 10;
|
|
// Default idle timeouts (5 min, 1 min)
|
|
qmPolicy.saIdleTimeout.idleTimeoutSeconds = 5 * 60;
|
|
qmPolicy.saIdleTimeout.idleTimeoutSecondsFailOver = 60;
|
|
qmPolicy.emPolicy = &emPolicy;
|
|
|
|
//-----------------------------------------
|
|
// Construct the FWP provider context
|
|
fwpProviderCtxt.displayData.name = L"Secure Socket App customized IPsec Policy";
|
|
fwpProviderCtxt.displayData.description = L"Secure Socket App customized IPsec Policy";
|
|
fwpProviderCtxt.type = FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT;
|
|
fwpProviderCtxt.authipQmTransportPolicy = &qmPolicy;
|
|
// Create a unique identifier
|
|
result = UuidCreate(&fwpProviderCtxt.providerContextKey);
|
|
if (result)
|
|
{
|
|
wprintf(L"UuidCreate returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Open a session handle to FWP
|
|
result = FwpmEngineOpen0(
|
|
NULL,
|
|
RPC_C_AUTHN_WINNT,
|
|
NULL,
|
|
NULL,
|
|
fwpHandle
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"FwpmEngineOpen0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Add the provider context to FWP
|
|
result = FwpmProviderContextAdd0(
|
|
*fwpHandle,
|
|
&fwpProviderCtxt,
|
|
NULL,
|
|
NULL
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"FwpmProviderContextAdd0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
|
|
*qmPolicyKey = fwpProviderCtxt.providerContextKey;
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|
|
VOID
|
|
RemoveCustomIPsecPolicy(
|
|
IN HANDLE fwpHandle,
|
|
IN const GUID* qmPolicyKey
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine removes the customized FWP IPsec quick mode policy from the
|
|
platform and closes the FWP session handle
|
|
|
|
Arguments:
|
|
|
|
fwpHandle - Handle for the FWP session
|
|
|
|
qmPolicyKey - Pointer to the unique identifier of the quick mode policy
|
|
that was added to the platform
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
GUID zeroGuid = {0};
|
|
|
|
if(fwpHandle)
|
|
{
|
|
if(memcmp(
|
|
&zeroGuid,
|
|
qmPolicyKey,
|
|
sizeof(GUID)
|
|
) != 0)
|
|
{
|
|
FwpmProviderContextDeleteByKey0(
|
|
fwpHandle,
|
|
qmPolicyKey
|
|
);
|
|
}
|
|
|
|
FwpmEngineClose0(fwpHandle);
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PrintQmSa(
|
|
IN const IPSEC_SA_CONTEXT0* qmSa
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prints sample information from the QM SA for illustration
|
|
purposes
|
|
|
|
Arguments:
|
|
|
|
qmSa - Pointer to the quick mode SA context
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
UINT32 i = 0;
|
|
// Static array containing string description of various encryption
|
|
// algorithms
|
|
static const wchar_t* const cipherString[] =
|
|
{
|
|
L"None",
|
|
L"DES",
|
|
L"3DES",
|
|
L"AES-128",
|
|
L"AES-192",
|
|
L"AES-256"
|
|
};
|
|
// Static array containing string description of various hashing algorithms
|
|
static const wchar_t* const hashString[] =
|
|
{
|
|
L"MD5",
|
|
L"SHA1"
|
|
};
|
|
IPSEC_AUTH_TRANSFORM_ID0* authTransformId = NULL;
|
|
IPSEC_CIPHER_TRANSFORM_ID0* cipherTransformId = NULL;
|
|
IPSEC_SA0* sa = NULL;
|
|
|
|
wprintf(L"QM SA context ID %I64d\n", qmSa->saContextId);
|
|
|
|
//-----------------------------------------
|
|
// Print all the SAs in the bundle
|
|
for(i=0; i<qmSa->outboundSa->saBundle.numSAs; i++)
|
|
{
|
|
sa = &qmSa->outboundSa->saBundle.saList[i];
|
|
|
|
// For each SA print information based on its type
|
|
switch(sa->saTransformType)
|
|
{
|
|
case IPSEC_TRANSFORM_AH:
|
|
{
|
|
// Print AH hash algorithm
|
|
authTransformId =
|
|
&sa->ahInformation->authTransform.authTransformId;
|
|
if(authTransformId->authType < RTL_NUMBER_OF(hashString))
|
|
{
|
|
wprintf(L"QM SA AH: Hash %s\n", hashString[authTransformId->authType]);
|
|
}
|
|
break;
|
|
}
|
|
case IPSEC_TRANSFORM_ESP_AUTH:
|
|
{
|
|
// Print ESP hash algorithm
|
|
authTransformId =
|
|
&sa->espAuthInformation->authTransform.authTransformId;
|
|
if(authTransformId->authType < RTL_NUMBER_OF(hashString))
|
|
{
|
|
wprintf(L"QM SA ESP: Hash %s\n", hashString[authTransformId->authType]);
|
|
}
|
|
break;
|
|
}
|
|
case IPSEC_TRANSFORM_ESP_CIPHER:
|
|
{
|
|
// Print ESP encryption algorithm
|
|
cipherTransformId =
|
|
&sa->espCipherInformation->cipherTransform.cipherTransformId;
|
|
if(cipherTransformId->cipherType < RTL_NUMBER_OF(cipherString))
|
|
{
|
|
wprintf(L"QM SA ESP: Encryption %s\n", cipherString[cipherTransformId->cipherType]);
|
|
}
|
|
break;
|
|
}
|
|
case IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER:
|
|
{
|
|
// Print ESP hash & encryption algorithm
|
|
authTransformId =
|
|
&sa->espAuthAndCipherInformation->saAuthInformation.authTransform.authTransformId;
|
|
cipherTransformId =
|
|
&sa->espAuthAndCipherInformation->saCipherInformation.cipherTransform.cipherTransformId;
|
|
if((authTransformId->authType < RTL_NUMBER_OF(hashString)) &&
|
|
(cipherTransformId->cipherType < RTL_NUMBER_OF(cipherString)))
|
|
{
|
|
wprintf(
|
|
L"QM SA ESP: Hash %s, Encryption %s\n",
|
|
hashString[authTransformId->authType],
|
|
cipherString[cipherTransformId->cipherType]
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
VOID
|
|
PrintMmSa(
|
|
IN const IKEEXT_SA_DETAILS0* mmSa
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine prints sample information from the MM SA for illustration
|
|
purposes
|
|
|
|
Arguments:
|
|
|
|
mmSa - Pointer to the main mode SA
|
|
|
|
Return Value:
|
|
|
|
None
|
|
|
|
--*/
|
|
{
|
|
// Static array containing string description of various encryption
|
|
// algorithms
|
|
static const wchar_t* const cipherString[] =
|
|
{
|
|
L"DES",
|
|
L"3DES",
|
|
L"AES-128",
|
|
L"AES-192",
|
|
L"AES-256"
|
|
};
|
|
// Static array containing string description of various hashing algorithms
|
|
static const wchar_t* const hashString[] =
|
|
{
|
|
L"MD5",
|
|
L"SHA1"
|
|
};
|
|
// Static array containing string description of various DH algorithms
|
|
static const wchar_t* const dhString[] =
|
|
{
|
|
L"None",
|
|
L"DH_1",
|
|
L"DH_2",
|
|
L"DH_2048",
|
|
L"DH_ECP_256",
|
|
L"DH_ECP_384"
|
|
};
|
|
// Static array containing string description of various authentication
|
|
// methods
|
|
static const wchar_t* const authString[] =
|
|
{
|
|
L"Preshared Key",
|
|
L"Certificate",
|
|
L"Kerberos",
|
|
L"Anonymous",
|
|
L"SSL",
|
|
L"NTLMv2",
|
|
L"CGA"
|
|
};
|
|
|
|
//-----------------------------------------
|
|
// Print various pieces of information from the MM SA
|
|
|
|
// Print MM SA ID
|
|
wprintf(L"MM SA context ID %I64d\n", mmSa->saId);
|
|
|
|
// Print MM SA hash, encryption algorithms
|
|
if((mmSa->ikeProposal.integrityAlgorithm.algoIdentifier <
|
|
RTL_NUMBER_OF(hashString)) &&
|
|
(mmSa->ikeProposal.cipherAlgorithm.algoIdentifier <
|
|
RTL_NUMBER_OF(cipherString)))
|
|
{
|
|
wprintf(
|
|
L"MM SA Hash %s, Encryption %s\n",
|
|
hashString[mmSa->ikeProposal.integrityAlgorithm.algoIdentifier],
|
|
cipherString[mmSa->ikeProposal.cipherAlgorithm.algoIdentifier]
|
|
);
|
|
}
|
|
|
|
// Print MM SA DH algorithm
|
|
if(mmSa->ikeProposal.dhGroup < RTL_NUMBER_OF(dhString))
|
|
{
|
|
wprintf(L"MM SA DH group %s\n", dhString[mmSa->ikeProposal.dhGroup]);
|
|
}
|
|
|
|
// Print MM SA auth method
|
|
if(mmSa->ikeCredentials.credentials[0].localCredentials.authenticationMethodType <
|
|
RTL_NUMBER_OF(authString))
|
|
{
|
|
wprintf(
|
|
L"MM SA Auth method %s\n",
|
|
authString[
|
|
mmSa->ikeCredentials.credentials[0].localCredentials.authenticationMethodType]
|
|
);
|
|
}
|
|
|
|
// Print EM auth method (if it exists)
|
|
if((mmSa->ikeCredentials.numCredentials > 1) &&
|
|
(mmSa->ikeCredentials.credentials[1].localCredentials.authenticationMethodType <
|
|
RTL_NUMBER_OF(authString)))
|
|
{
|
|
wprintf(
|
|
L"MM SA EM Auth method %s\n",
|
|
authString[
|
|
mmSa->ikeCredentials.credentials[1].localCredentials.authenticationMethodType]
|
|
);
|
|
}
|
|
}
|
|
|
|
BOOL
|
|
IsQmSAExactMatch(
|
|
IN const IPSEC_SA_CONTEXT0* qmSa,
|
|
IN const FWPM_FILTER0* matchingFwpFilter,
|
|
IN UINT16 peerPort
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine verifies if a quick mode SA matches the given paramters
|
|
|
|
Arguments:
|
|
|
|
qmSa - Pointer to the quick mode SA context
|
|
matchingFwpFilter - Pointer to the outbound transport layer filter that
|
|
matched the application traffic
|
|
peerPort - Port corresponding to the peer application
|
|
|
|
Return Value:
|
|
|
|
TRUE if the QM SA matches the parameters, FALSE if it does not match.
|
|
|
|
--*/
|
|
{
|
|
if(!qmSa->outboundSa)
|
|
{
|
|
return FALSE;
|
|
}
|
|
if(qmSa->outboundSa->transportFilter->filterId !=
|
|
matchingFwpFilter->filterId)
|
|
{
|
|
// The filter IDs dont match.
|
|
return FALSE;
|
|
}
|
|
if(qmSa->outboundSa->traffic.remotePort &&
|
|
(qmSa->outboundSa->traffic.remotePort != peerPort))
|
|
{
|
|
// The remote ports dont match.
|
|
return FALSE;
|
|
}
|
|
|
|
// SA matches our criteria
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
MatchIPsecSAsForConnection(
|
|
IN const SOCKADDR_STORAGE* localAddress,
|
|
IN const SOCKADDR_STORAGE* peerAddress
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the IPsec SAs (MM & QM) that match a given connection
|
|
5-tuple (Local address, Remote address, Local port, Remote port, TCP)
|
|
|
|
Arguments:
|
|
|
|
localAddress - Pointer to the socket address that contains local
|
|
address, port information
|
|
peerAddress - Pointer to the socket address that contains peer
|
|
address, port information
|
|
|
|
Return Value:
|
|
|
|
Windows error code indicating the status of the operation, or NO_ERROR if
|
|
the operation succeeded.
|
|
|
|
--*/
|
|
{
|
|
DWORD result = 0;
|
|
FWPM_FILTER_CONDITION0 filterConditions[6] = {0};
|
|
HANDLE fwpHandle = NULL;
|
|
FWPM_FILTER_ENUM_TEMPLATE0 filterEnumTemplate = {0};
|
|
HANDLE filterEnumHandle = NULL;
|
|
FWPM_FILTER0** matchingFwpFilter=NULL;
|
|
UINT32 numEntriesReturned=0;
|
|
IPSEC_SA_CONTEXT_ENUM_TEMPLATE0 saEnumTemplate = {0};
|
|
HANDLE saEnumHandle = NULL;
|
|
IPSEC_SA_CONTEXT0** qmSaList = NULL;
|
|
IPSEC_SA_CONTEXT0* matchingQmSa = NULL;
|
|
IKEEXT_SA_DETAILS0* matchingMmSa = NULL;
|
|
UINT32 i = 0;
|
|
const GUID* layerKey = NULL;
|
|
UINT16 localPort = 0, remotePort = 0;
|
|
|
|
//-----------------------------------------
|
|
// Open a session handle to FWP
|
|
result = FwpmEngineOpen0(
|
|
NULL,
|
|
RPC_C_AUTHN_WINNT,
|
|
NULL,
|
|
NULL,
|
|
&fwpHandle
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"FwpmEngineOpen0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Construct the FWP outbound IPsec transport layer filter enum template
|
|
if(localAddress->ss_family == AF_INET)
|
|
{
|
|
filterConditions[0].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS;
|
|
filterConditions[0].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[0].conditionValue.type = FWP_UINT32;
|
|
filterConditions[0].conditionValue.uint32 =
|
|
ntohl(((struct sockaddr_in*)localAddress)->sin_addr.s_addr);
|
|
|
|
filterConditions[1].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
|
filterConditions[1].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[1].conditionValue.type = FWP_UINT32;
|
|
filterConditions[1].conditionValue.uint32 =
|
|
ntohl(((struct sockaddr_in*)peerAddress)->sin_addr.s_addr);
|
|
|
|
layerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V4;
|
|
localPort = ntohs(((struct sockaddr_in*)localAddress)->sin_port);
|
|
remotePort = ntohs(((struct sockaddr_in*)peerAddress)->sin_port);
|
|
}
|
|
else
|
|
{
|
|
// Assume AF_INET6
|
|
filterConditions[0].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS;
|
|
filterConditions[0].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[0].conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
|
|
filterConditions[0].conditionValue.byteArray16 =
|
|
(FWP_BYTE_ARRAY16*)&(((struct sockaddr_in6*)localAddress)->sin6_addr);
|
|
|
|
filterConditions[1].fieldKey = FWPM_CONDITION_IP_REMOTE_ADDRESS;
|
|
filterConditions[1].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[1].conditionValue.type = FWP_BYTE_ARRAY16_TYPE;
|
|
filterConditions[1].conditionValue.byteArray16 =
|
|
(FWP_BYTE_ARRAY16*)&(((struct sockaddr_in6*)peerAddress)->sin6_addr);
|
|
|
|
layerKey = &FWPM_LAYER_OUTBOUND_TRANSPORT_V6;
|
|
localPort = ntohs(((struct sockaddr_in6*)localAddress)->sin6_port);
|
|
remotePort = ntohs(((struct sockaddr_in6*)peerAddress)->sin6_port);
|
|
}
|
|
|
|
filterConditions[2].fieldKey = FWPM_CONDITION_IP_LOCAL_PORT;
|
|
filterConditions[2].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[2].conditionValue.type = FWP_UINT16;
|
|
filterConditions[2].conditionValue.uint16 = localPort;
|
|
|
|
filterConditions[3].fieldKey = FWPM_CONDITION_IP_REMOTE_PORT;
|
|
filterConditions[3].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[3].conditionValue.type = FWP_UINT16;
|
|
filterConditions[3].conditionValue.uint16 = remotePort;
|
|
|
|
filterConditions[4].fieldKey = FWPM_CONDITION_IP_PROTOCOL;
|
|
filterConditions[4].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[4].conditionValue.type = FWP_UINT8;
|
|
filterConditions[4].conditionValue.uint8 = IPPROTO_TCP;
|
|
|
|
filterConditions[5].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE;
|
|
filterConditions[5].matchType = FWP_MATCH_EQUAL;
|
|
filterConditions[5].conditionValue.type = FWP_UINT8;
|
|
filterConditions[5].conditionValue.uint8 = NlatUnicast;
|
|
|
|
filterEnumTemplate.layerKey = *layerKey;
|
|
filterEnumTemplate.enumType = FWP_FILTER_ENUM_FULLY_CONTAINED;
|
|
filterEnumTemplate.flags= FWP_FILTER_ENUM_FLAG_BEST_TERMINATING_MATCH;
|
|
filterEnumTemplate.numFilterConditions = RTL_NUMBER_OF(filterConditions);
|
|
filterEnumTemplate.filterCondition = filterConditions;
|
|
filterEnumTemplate.actionMask = FWP_ACTION_FLAG_TERMINATING;
|
|
|
|
//-----------------------------------------
|
|
// Enumerate and get the matching FWP outbound IPsec transport layer filter
|
|
result = FwpmFilterCreateEnumHandle0(
|
|
fwpHandle,
|
|
&filterEnumTemplate,
|
|
&filterEnumHandle
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"FwpmFilterCreateEnumHandle0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
result = FwpmFilterEnum0(
|
|
fwpHandle,
|
|
filterEnumHandle,
|
|
1,
|
|
&matchingFwpFilter,
|
|
&numEntriesReturned
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"FwpmFilterEnum0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
// Verify the matching filter
|
|
if((numEntriesReturned < 1) ||
|
|
!(matchingFwpFilter[0]->action.type & FWP_ACTION_FLAG_CALLOUT) ||
|
|
!((memcmp(
|
|
&matchingFwpFilter[0]->action.calloutKey,
|
|
&FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4,
|
|
sizeof(GUID)
|
|
) == 0) ||
|
|
(memcmp(
|
|
&matchingFwpFilter[0]->action.calloutKey,
|
|
&FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V4,
|
|
sizeof(GUID)
|
|
) == 0) ||
|
|
(memcmp(
|
|
&matchingFwpFilter[0]->action.calloutKey,
|
|
&FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6,
|
|
sizeof(GUID)
|
|
) == 0) ||
|
|
(memcmp(
|
|
&matchingFwpFilter[0]->action.calloutKey,
|
|
&FWPM_CALLOUT_IPSEC_OUTBOUND_TUNNEL_V6,
|
|
sizeof(GUID)
|
|
) == 0)))
|
|
{
|
|
result = ERROR_INVALID_PARAMETER;
|
|
wprintf(L"FwpmFilterEnum0 didn't return any matching filters\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Construct IPsec QM SA enum template
|
|
if(localAddress->ss_family == AF_INET)
|
|
{
|
|
saEnumTemplate.localSubNet.type = FWP_UINT32;
|
|
saEnumTemplate.localSubNet.uint32 =
|
|
ntohl(((struct sockaddr_in*)localAddress)->sin_addr.s_addr);
|
|
saEnumTemplate.remoteSubNet.type = FWP_UINT32;
|
|
saEnumTemplate.remoteSubNet.uint32 =
|
|
ntohl(((struct sockaddr_in*)peerAddress)->sin_addr.s_addr);
|
|
}
|
|
else
|
|
{
|
|
saEnumTemplate.localSubNet.type = FWP_BYTE_ARRAY16_TYPE;
|
|
saEnumTemplate.localSubNet.byteArray16 =
|
|
(FWP_BYTE_ARRAY16*)&(((struct sockaddr_in6*)localAddress)->sin6_addr);
|
|
saEnumTemplate.remoteSubNet.type = FWP_BYTE_ARRAY16_TYPE;
|
|
saEnumTemplate.remoteSubNet.byteArray16 =
|
|
(FWP_BYTE_ARRAY16*)&(((struct sockaddr_in6*)peerAddress)->sin6_addr);
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Enumerate the IPsec QM SAs
|
|
result = IPsecSaContextCreateEnumHandle0(
|
|
fwpHandle,
|
|
&saEnumTemplate,
|
|
&saEnumHandle
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"IPsecSaContextCreateEnumHandle0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
result = IPsecSaContextEnum0(
|
|
fwpHandle,
|
|
saEnumHandle,
|
|
UINT_MAX,
|
|
&qmSaList,
|
|
&numEntriesReturned
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"IPsecSaContextEnum0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
if(numEntriesReturned < 1)
|
|
{
|
|
result = ERROR_INVALID_PARAMETER;
|
|
wprintf(L"IPsecSaContextEnum0 didn't return any matching SAs\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Go through the enumerated QM SAs and look for an exact match
|
|
for(i=0; i<numEntriesReturned; i++)
|
|
{
|
|
if(IsQmSAExactMatch(
|
|
qmSaList[i],
|
|
matchingFwpFilter[0],
|
|
remotePort
|
|
))
|
|
{
|
|
matchingQmSa = qmSaList[i];
|
|
|
|
//-----------------------------------------
|
|
// Get the IPsec MM SA using the MM SA ID
|
|
result = IkeextSaGetById0(
|
|
fwpHandle,
|
|
matchingQmSa->outboundSa->saBundle.mmSaId,
|
|
&matchingMmSa
|
|
);
|
|
if (result)
|
|
{
|
|
wprintf(L"IkeextSaGetById0 returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Print the SAs
|
|
wprintf(L"---------------------\n");
|
|
PrintQmSa(matchingQmSa);
|
|
PrintMmSa(matchingMmSa);
|
|
|
|
//-----------------------------------------
|
|
// Free the MM SA pointer
|
|
FwpmFreeMemory0(&((void*)matchingMmSa));
|
|
}
|
|
}
|
|
if(!matchingQmSa)
|
|
{
|
|
result = ERROR_INVALID_PARAMETER;
|
|
wprintf(L"Didn't find any matching SAs\n");
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
if(matchingFwpFilter)
|
|
{
|
|
FwpmFreeMemory0(&((void*)matchingFwpFilter));
|
|
}
|
|
if(qmSaList)
|
|
{
|
|
FwpmFreeMemory0(&((void*)qmSaList));
|
|
}
|
|
if(filterEnumHandle)
|
|
{
|
|
FwpmFilterDestroyEnumHandle0(fwpHandle, filterEnumHandle);
|
|
}
|
|
if(saEnumHandle)
|
|
{
|
|
IPsecSaContextDestroyEnumHandle0(fwpHandle, saEnumHandle);
|
|
}
|
|
if(fwpHandle)
|
|
{
|
|
FwpmEngineClose0(fwpHandle);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
DWORD
|
|
MatchIPsecSAsForConnectedSocket(
|
|
IN SOCKET sock
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
This routine finds the IPsec SAs (MM & QM) that match a given connected TCP
|
|
socket (i-e after Connect() or Accept() have taken place)
|
|
|
|
Arguments:
|
|
|
|
sock - The connected socket
|
|
|
|
Return Value:
|
|
|
|
Windows error code indicating the status of the operation, or NO_ERROR if
|
|
the operation succeeded.
|
|
|
|
--*/
|
|
{
|
|
DWORD result = 0;
|
|
int sockErr = 0;
|
|
SOCKADDR_STORAGE localAddress = {0};
|
|
int localAddrLen = sizeof(SOCKADDR_STORAGE);
|
|
SOCKADDR_STORAGE peerAddress = {0};
|
|
int peerAddrLen = sizeof(SOCKADDR_STORAGE);
|
|
|
|
//-----------------------------------------
|
|
// Get local address bound to the socket
|
|
sockErr = getsockname(
|
|
sock,
|
|
(struct sockaddr*)&localAddress,
|
|
&localAddrLen
|
|
);
|
|
if (sockErr == SOCKET_ERROR)
|
|
{
|
|
result = WSAGetLastError();
|
|
wprintf(L"getsockname returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
if(!((localAddress.ss_family == AF_INET) ||
|
|
(localAddress.ss_family == AF_INET6)))
|
|
{
|
|
result = ERROR_NOT_SUPPORTED;
|
|
wprintf(L"Received incorrect address family %d\n", localAddress.ss_family);
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Get peer address bound to the socket
|
|
sockErr = getpeername(
|
|
sock,
|
|
(struct sockaddr*)&peerAddress,
|
|
&peerAddrLen
|
|
);
|
|
if (sockErr == SOCKET_ERROR)
|
|
{
|
|
result = WSAGetLastError();
|
|
wprintf(L"getpeername returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
|
|
//-----------------------------------------
|
|
// Call the routine to do the match
|
|
result = MatchIPsecSAsForConnection(&localAddress, &peerAddress);
|
|
if (result)
|
|
{
|
|
wprintf(L"MatchIPsecSAsForConnection returned error %ld\n", result);
|
|
goto cleanup;
|
|
}
|
|
|
|
cleanup:
|
|
return result;
|
|
}
|
|
|