Transport Mode Example From MSDN

//About  Transport Mode,   Negotiation Discovery Transport Mode,  Negotiation Discovery Transport Mode in Boundary Mode,

//Remote Identity Authorization , Manual SA 


#include

#include

#include

#include

#pragma comment(lib, "fwpuclnt.lib")

#pragma comment(lib, "crypt32.lib")

#pragma comment(lib, "rpcrt4.lib")

#define EXIT_ON_ERROR(fnName) \

  if (result != ERROR_SUCCESS) \

  { \

      printf(#fnName " = 0x%08X\n", result); \

      goto CLEANUP; \

  }


//配置IPSec TransportMode

DWORD ConfigureIPsecTransportMode(

__in HANDLE engine,

__in PCWSTR policyName,

__in_opt const GUID* providerKey,

__in PCWSTR rootCertSubject

)

{

DWORD result = ERROR_SUCCESS;

HCERTSTORE certStore = NULL;

PCCERT_CONTEXT rootCert = NULL;

IKEEXT_CERT_ROOT_CONFIG0 certRootConfig;

BOOL txnInProgress = FALSE;

IKEEXT_AUTHENTICATION_METHOD0 mmAuthMethods[2];

IKEEXT_PROPOSAL0 mmProposals[2];

IKEEXT_POLICY0 mmPolicy;

FWPM_PROVIDER_CONTEXT0 provCtxt;

FWPM_FILTER0 filter;

IKEEXT_AUTHENTICATION_METHOD0 emAuthMethods[2];

IKEEXT_EM_POLICY0 emPolicy;

const IPSEC_SA_LIFETIME0 qmLifetime =

{

3600,      // lifetimeSeconds

100000,    // lifetimeKilobytes

0x7FFFFFFF  // lifetimePackets

};

IPSEC_AUTH_TRANSFORM0 qmTransform00;

IPSEC_SA_TRANSFORM0 qmTransforms0[1];

IPSEC_AUTH_TRANSFORM0 qmTransform10;

IPSEC_SA_TRANSFORM0 qmTransforms1[1];

IPSEC_AUTH_AND_CIPHER_TRANSFORM0 qmTransform20;

IPSEC_SA_TRANSFORM0 qmTransforms2[1];

IPSEC_AUTH_AND_CIPHER_TRANSFORM0 qmTransform30;

IPSEC_SA_TRANSFORM0 qmTransforms3[1];

IPSEC_PROPOSAL0 qmProposals[4];

IPSEC_TRANSPORT_POLICY0 qmPolicy;

FWPM_FILTER_CONDITION0 filterConditions[2];

// Open the local machine's CA certificate store.

certStore = CertOpenStore(

CERT_STORE_PROV_SYSTEM,

0,

0,

(CERT_STORE_OPEN_EXISTING_FLAG |

CERT_STORE_READONLY_FLAG |

CERT_SYSTEM_STORE_LOCAL_MACHINE),

L"Root"

);

result = (certStore != NULL) ? ERROR_SUCCESS : GetLastError();

EXIT_ON_ERROR(CertOpenStore);

// Find the specified root certificate.

rootCert = CertFindCertificateInStore(

certStore,

(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING),

0,

CERT_FIND_SUBJECT_STR,

rootCertSubject,

NULL

);

result = (rootCert != NULL) ? ERROR_SUCCESS : GetLastError();

EXIT_ON_ERROR(CertFindCertificateInStore);

// The CERT_INFO.Subject name blob is already in the format expected by

// AuthIP.

memset(&certRootConfig, 0, sizeof(certRootConfig));

certRootConfig.certData.size = rootCert->pCertInfo->Subject.cbData;

certRootConfig.certData.data = rootCert->pCertInfo->Subject.pbData;

// We add all the objects from within a single transaction to make it easy

// to clean up partial results in error paths.

result = FwpmTransactionBegin0(engine, 0);

EXIT_ON_ERROR(FwpmTransactionBegin0);

txnInProgress = TRUE;

memset(mmAuthMethods, 0, sizeof(mmAuthMethods));

// Primary main mode auth method is Kerberos. This is the most efficient for

// domain-joined machines.

mmAuthMethods[0].authenticationMethodType = IKEEXT_KERBEROS;

// Secondary main mode auth method is SSL. This allows non-joined machines

// to authenticate as well provided they have been provisioned with an

// appropriate certificate.

mmAuthMethods[1].authenticationMethodType = IKEEXT_SSL;

mmAuthMethods[1].sslAuthentication.inboundConfigType =

IKEEXT_CERT_CONFIG_EXPLICIT_TRUST_LIST;

mmAuthMethods[1].sslAuthentication.inboundRootArraySize = 1;

mmAuthMethods[1].sslAuthentication.inboundRootArray = &certRootConfig;

mmAuthMethods[1].sslAuthentication.outboundConfigType =

IKEEXT_CERT_CONFIG_EXPLICIT_TRUST_LIST;

mmAuthMethods[1].sslAuthentication.outboundRootArraySize = 1;

mmAuthMethods[1].sslAuthentication.outboundRootArray = &certRootConfig;

// Disable CRL checks for test purposes. Consider enabling them in real use.

mmAuthMethods[1].sslAuthentication.flags =

IKEEXT_CERT_AUTH_FLAG_DISABLE_CRL_CHECK;

// Configure two main mode proposals: AES-128/SHA-1 & 3DES/SHA-1 each with a

// lifetime of 8 hours. Fallback to 3DES might be desirable to allow some

// servers to use 3DES for performance reasons.

//配置两个主模式提议:AES - 128 / SHA - 1和3DES / SHA - 1,

//每个提案的生命周期为8小时。 可能需要回退到3DES以允许某些服务器出于性能原因使用3DES。

memset(mmProposals, 0, sizeof(mmProposals));

mmProposals[0].cipherAlgorithm.algoIdentifier = IKEEXT_CIPHER_AES_128;

mmProposals[0].integrityAlgorithm.algoIdentifier = IKEEXT_INTEGRITY_SHA1;

mmProposals[0].maxLifetimeSeconds = 8 * 60 * 60;

mmProposals[1].cipherAlgorithm.algoIdentifier = IKEEXT_CIPHER_3DES;

mmProposals[1].integrityAlgorithm.algoIdentifier = IKEEXT_INTEGRITY_SHA1;

mmProposals[1].maxLifetimeSeconds = 8 * 60 * 60;

memset(&mmPolicy, 0, sizeof(mmPolicy));

mmPolicy.numAuthenticationMethods = ARRAYSIZE(mmAuthMethods);

mmPolicy.authenticationMethods = mmAuthMethods;

mmPolicy.numIkeProposals = ARRAYSIZE(mmProposals);

mmPolicy.ikeProposals = mmProposals;

memset(&provCtxt, 0, sizeof(provCtxt));

// We have to assign the key ourselves since we will need it when adding the

// filters that reference this provider context.

result = UuidCreate(&(provCtxt.providerContextKey));

EXIT_ON_ERROR(UuidCreate);

// For MUI compatibility, object names should be indirect strings. See

// SHLoadIndirectString for details.

provCtxt.displayData.name = (PWSTR)policyName;

// Link all our objects to our provider. When multiple providers are

// installed on a computer, this makes it easy to determine who added what.

provCtxt.providerKey = (GUID*)providerKey;

provCtxt.type = FWPM_IPSEC_AUTHIP_MM_CONTEXT;//for AUTHIP set  FWPM_IPSEC_AUTHIP_MM_CONTEXT

provCtxt.authIpMmPolicy = &mmPolicy;

// 添加MM模式策略

result = FwpmProviderContextAdd0(engine, &provCtxt, NULL, NULL);

EXIT_ON_ERROR(FwpmProviderContextAdd0);

// Add filters referencing the policy to the IKEEXT policy layers.

// We could also add remote address conditions to the filters to

// limit which machines or subnets are protected. Note that we use the same

// main mode policy for both IPv4 and IPv6.

//将引用该策略的过滤器添加到IKEEXT策略层。

//我们还可以为过滤器添加远程地址条件

//限制哪些机器或子网受到保护。 请注意,我们对IPv4和IPv6使用相同的主模式策略。

memset(&filter, 0, sizeof(filter));

filter.displayData.name = (PWSTR)policyName;

filter.flags = FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;

filter.providerKey = (GUID*)providerKey;

// Policy filters should always have an action type of PERMIT.

filter.action.type = FWP_ACTION_PERMIT;

filter.providerContextKey = provCtxt.providerContextKey;

filter.layerKey = FWPM_LAYER_IKEEXT_V4;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_IKEEXT_V6;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

//////////////////////////////////////////////////////////////////////

//                      EM QM 模式 添加                            //

//////////////////////////////////////////////////////////////////////

// 允许两种扩展模式身份验证方法:

//1.用于加入域的计算机的Kerberos

//2.用于未加入的NTLMv2。

memset(&emAuthMethods, 0, sizeof(emAuthMethods));

emAuthMethods[0].authenticationMethodType = IKEEXT_KERBEROS;

emAuthMethods[1].authenticationMethodType = IKEEXT_NTLM_V2;

memset(&emPolicy, 0, sizeof(emPolicy));

emPolicy.numAuthenticationMethods = ARRAYSIZE(emAuthMethods);

emPolicy.authenticationMethods = emAuthMethods;

emPolicy.initiatorImpersonationType = IKEEXT_IMPERSONATION_SOCKET_PRINCIPAL;

memset(qmProposals, 0, sizeof(qmProposals));

// First QM proposal is AH. This is preferable to ESP auth since

// more routers are capable of parsing AH.

memset(&qmTransform00, 0, sizeof(qmTransform00));

qmTransform00.authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96;

memset(qmTransforms0, 0, sizeof(qmTransforms0));

qmTransforms0[0].ipsecTransformType = IPSEC_TRANSFORM_AH;

qmTransforms0[0].ahTransform = &qmTransform00;

qmProposals[0].lifetime = qmLifetime;

qmProposals[0].numSaTransforms = ARRAYSIZE(qmTransforms0);

qmProposals[0].saTransforms = qmTransforms0;

// Second QM proposal is ESP auth. This might be necessary for NAT

// traversal.

memset(&qmTransform10, 0, sizeof(qmTransform10));

qmTransform10.authTransformId = IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96;

memset(&qmTransforms1, 0, sizeof(qmTransforms1));

qmTransforms1[0].ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH;

qmTransforms1[0].espAuthTranform = &qmTransform10;

qmProposals[1].lifetime = qmLifetime;

qmProposals[1].numSaTransforms = ARRAYSIZE(qmTransforms1);

qmProposals[1].saTransforms = qmTransforms1;

// Third QM proposal is ESP auth and cipher using AES-128. This will

// allow remote peers to require encryption if necessary.

memset(&qmTransform20, 0, sizeof(qmTransform20));

qmTransform20.authTransform.authTransformId =

IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96;

qmTransform20.cipherTransform.cipherTransformId =

IPSEC_CIPHER_TRANSFORM_ID_AES_128;

memset(&qmTransforms2, 0, sizeof(qmTransforms2));

qmTransforms2[0].ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;

qmTransforms2[0].espAuthAndCipherTransform = &qmTransform20;

qmProposals[2].lifetime = qmLifetime;

qmProposals[2].numSaTransforms = ARRAYSIZE(qmTransforms2);

qmProposals[2].saTransforms = qmTransforms2;

// Fourth QM proposal allows fallback to 3DES for perf reasons.

memset(&qmTransform30, 0, sizeof(qmTransform30));

qmTransform30.authTransform.authTransformId =

IPSEC_AUTH_TRANSFORM_ID_HMAC_SHA_1_96;

qmTransform30.cipherTransform.cipherTransformId =

IPSEC_CIPHER_TRANSFORM_ID_CBC_3DES;

memset(&qmTransforms3, 0, sizeof(qmTransforms3));

qmTransforms3[0].ipsecTransformType = IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER;

qmTransforms3[0].espAuthAndCipherTransform = &qmTransform30;

qmProposals[3].lifetime = qmLifetime;

qmProposals[3].numSaTransforms = ARRAYSIZE(qmTransforms3);

qmProposals[3].saTransforms = qmTransforms3;

memset(&qmPolicy, 0, sizeof(qmPolicy));

qmPolicy.numIpsecProposals = ARRAYSIZE(qmProposals);

qmPolicy.ipsecProposals = qmProposals;

// Set ND_SECURE since we require IPsec for all inbound connections.

// This flag alone doesn't guarantee enforcement and is only used to

// ensure connectivity in IPsec+NAT scenarios.

qmPolicy.flags = IPSEC_POLICY_FLAG_ND_SECURE;

qmPolicy.ndAllowClearTimeoutSeconds = 10;

qmPolicy.saIdleTimeout.idleTimeoutSeconds = 300;

qmPolicy.saIdleTimeout.idleTimeoutSecondsFailOver = 60;

qmPolicy.emPolicy = &emPolicy;

memset(&provCtxt, 0, sizeof(provCtxt));

result = UuidCreate(&(provCtxt.providerContextKey));

EXIT_ON_ERROR(UuidCreate);

provCtxt.displayData.name = (PWSTR)policyName;

provCtxt.providerKey = (GUID*)providerKey;

provCtxt.type = FWPM_IPSEC_AUTHIP_QM_TRANSPORT_CONTEXT;

provCtxt.authipQmTransportPolicy = &qmPolicy;

// Add the main mode policy.

result = FwpmProviderContextAdd0(engine, &provCtxt, NULL, NULL);

EXIT_ON_ERROR(FwpmProviderContextAdd0);

// Add filters referencing the policy to the IPsec policy layers.

// As above, we could also add remote address conditions to the

// filters to limit which machines or subnets are protected.

// Note that we use the same quick mode policy for both IPv4 and IPv6.

memset(&filter, 0, sizeof(filter));

filter.displayData.name = (PWSTR)policyName;

filter.flags = FWPM_FILTER_FLAG_HAS_PROVIDER_CONTEXT;

filter.providerKey = (GUID*)providerKey;

filter.action.type = FWP_ACTION_PERMIT;

filter.providerContextKey = provCtxt.providerContextKey;

filter.layerKey = FWPM_LAYER_IPSEC_V4;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_IPSEC_V6;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

// 我们只保护单播流量。

memset(filterConditions, 0, sizeof(filterConditions));

filterConditions[0].fieldKey = FWPM_CONDITION_IP_LOCAL_ADDRESS_TYPE;

filterConditions[0].matchType = FWP_MATCH_EQUAL;

filterConditions[0].conditionValue.type = FWP_UINT8;

filterConditions[0].conditionValue.uint8 = NlatUnicast;

// Fields common to all transport and ALE IPsec callout filters.

//所有传输和ALE IPsec callout 过滤器共有的字段

memset(&filter, 0, sizeof(filter));

filter.displayData.name = (PWSTR)policyName;

filter.providerKey = (GUID*)providerKey;

filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;

filter.numFilterConditions = 1;

filter.filterCondition = filterConditions;

filter.providerContextKey = provCtxt.providerContextKey;

//OutBound 针对于FWPM_LAYER_OUTBOUND_TRANSPORT_V4和针对于FWPM_LAYER_OUTBOUND_TRANSPORT_V6

filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;

filter.action.calloutKey = FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V4;

filter.rawContext = FWPM_CONTEXT_IPSEC_OUTBOUND_NEGOTIATE_DISCOVER;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;

filter.action.calloutKey = FWPM_CALLOUT_IPSEC_OUTBOUND_TRANSPORT_V6;

filter.rawContext = FWPM_CONTEXT_IPSEC_OUTBOUND_NEGOTIATE_DISCOVER;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

//InBound 针对于FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4和针对于FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6

filter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V4;

filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V4;

filter.rawContext = FWPM_CONTEXT_IPSEC_INBOUND_PERSIST_CONNECTION_SECURITY;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V6;

filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_TRANSPORT_V6;

filter.rawContext = FWPM_CONTEXT_IPSEC_INBOUND_PERSIST_CONNECTION_SECURITY;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;

filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_INITIATE_SECURE_V4;

filter.rawContext = FWPM_CONTEXT_IPSEC_INBOUND_PERSIST_CONNECTION_SECURITY;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;

filter.action.calloutKey = FWPM_CALLOUT_IPSEC_INBOUND_INITIATE_SECURE_V6;

filter.rawContext = FWPM_CONTEXT_IPSEC_INBOUND_PERSIST_CONNECTION_SECURITY;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

//接下来我们将添加过滤器以免除ICMP流量

filterConditions[1].fieldKey = FWPM_CONDITION_IP_PROTOCOL;

filterConditions[1].matchType = FWP_MATCH_EQUAL;

filterConditions[1].conditionValue.type = FWP_UINT8;

filterConditions[1].conditionValue.uint8 = IPPROTO_ICMP;

memset(&filter, 0, sizeof(filter));

filter.displayData.name = (PWSTR)policyName;

filter.providerKey = (GUID*)providerKey;

filter.weight.type = FWP_UINT8;

// The exemptions must be added at a higher weight range to ensure

// that they will match before the IPsec callout filters added above.

//必须在更高的权重范围内添加免除项,以确保它们在上面添加的IPsec标注过滤器之前匹配

filter.weight.uint8 = FWPM_WEIGHT_RANGE_IKE_EXEMPTIONS;

filter.action.type = FWP_ACTION_CALLOUT_TERMINATING;

filter.numFilterConditions = 2;

filter.filterCondition = filterConditions;

filter.action.type = FWP_ACTION_PERMIT;

filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V4;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V4;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V4;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filterConditions[1].conditionValue.uint8 = IPPROTO_ICMPV6;

filter.layerKey = FWPM_LAYER_OUTBOUND_TRANSPORT_V6;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_INBOUND_TRANSPORT_V6;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

filter.layerKey = FWPM_LAYER_ALE_AUTH_RECV_ACCEPT_V6;

result = FwpmFilterAdd0(engine, &filter, NULL, NULL);

EXIT_ON_ERROR(FwpmFilterAdd0);

// Once all the adds have succeeded, we commit the transaction to

// add all the new objects atomically.

///一旦所有添加成功,我们提交事务以原子方式添加所有新对象。

result = FwpmTransactionCommit0(engine);

EXIT_ON_ERROR(FwpmTransactionCommit0);

txnInProgress = FALSE;

CLEANUP:

if (txnInProgress)

{

// Abort any transaction still in progress to clean up

// partial results.

FwpmTransactionAbort0(engine);

}

if (rootCert != NULL)

{

CertFreeCertificateContext(rootCert);

}

if (certStore != NULL)

{

CertCloseStore(certStore, CERT_CLOSE_STORE_CHECK_FLAG);

}

return result;

}

你可能感兴趣的:(Transport Mode Example From MSDN)