トレーニング
モジュール
Explore connection security rules - Training
This module describes how connection security rules can be used to provide additional security to protect data transmitted across networks.
このブラウザーはサポートされなくなりました。
Microsoft Edge にアップグレードすると、最新の機能、セキュリティ更新プログラム、およびテクニカル サポートを利用できます。
次のサンプル コードは、ユーザー認証とネゴシエーション検出で AuthIP を使用して IPsec トランスポート モードを構成する方法を示しています。
#include <winsock2.h>
#include <fwpmu.h>
#include <mstcpip.h>
#include <stdio.h>
#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; \
}
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.
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;
provCtxt.authIpMmPolicy = &mmPolicy;
// Add the main mode policy.
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.
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);
// Allow two extended mode authentication methods: Kerberos for
// domain-joined machines and NTLMv2 for non-joined machines.
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);
// We only protect unicast traffic.
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.
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;
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);
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);
// Next we'll add filters to exempt ICMP traffic.
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.
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;
}
トレーニング
モジュール
Explore connection security rules - Training
This module describes how connection security rules can be used to provide additional security to protect data transmitted across networks.
ドキュメント
次のサンプル コードは、サーバー アプリケーションに影響を与える可能性があるフィルターの追加と削除を監視する方法を示しています。
次のサンプル コードは、サーバー アプリケーションに影響を与える可能性があるすべてのフィルターを検索する方法を示しています。
次のサンプル コードは、サーバー アプリケーションが、その影響を受けた可能性のある最近のネット イベントを見つける方法を示しています。