kernel-wfp: Add an ipsecdump utility to show installed SAs/SPs on Windows
This commit is contained in:
@@ -19,3 +19,12 @@ libstrongswan_kernel_wfp_la_SOURCES = \
|
||||
|
||||
libstrongswan_kernel_wfp_la_LDFLAGS = -module -avoid-version
|
||||
libstrongswan_kernel_wfp_la_LIBADD = -lfwpuclnt
|
||||
|
||||
|
||||
noinst_PROGRAMS = ipsecdump
|
||||
|
||||
ipsecdump_SOURCES = \
|
||||
ipsecdump.c
|
||||
ipsecdump_LDADD = \
|
||||
libstrongswan-kernel-wfp.la \
|
||||
$(top_builddir)/src/libstrongswan/libstrongswan.la
|
||||
|
||||
@@ -0,0 +1,419 @@
|
||||
/*
|
||||
* Copyright (C) 2013 Martin Willi
|
||||
* Copyright (C) 2013 revosec AG
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 of the License, or (at your
|
||||
* option) any later version. See <http://www.fsf.org/copyleft/gpl.txt>.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
/* Windows 7, for some fwpmu.h functionality */
|
||||
#define _WIN32_WINNT 0x0601
|
||||
|
||||
#include "kernel_wfp_compat.h"
|
||||
|
||||
#include <library.h>
|
||||
|
||||
ENUM(auth_type_names, IPSEC_AUTH_MD5, IPSEC_AUTH_AES_256,
|
||||
"MD5",
|
||||
"SHA1",
|
||||
"SHA256",
|
||||
"AES128",
|
||||
"AES192",
|
||||
"AES256",
|
||||
);
|
||||
|
||||
ENUM(auth_config_names, 0, 5,
|
||||
"HMAC96",
|
||||
"HMAC96",
|
||||
"HMAC128",
|
||||
"GMAC",
|
||||
"GMAC",
|
||||
"GMAC",
|
||||
);
|
||||
|
||||
ENUM(cipher_type_names, IPSEC_CIPHER_TYPE_DES, IPSEC_CIPHER_TYPE_AES_256,
|
||||
"DES",
|
||||
"3DES",
|
||||
"AES128",
|
||||
"AES192",
|
||||
"AES256",
|
||||
);
|
||||
|
||||
ENUM(cipher_config_names, 1, 8,
|
||||
"CBC",
|
||||
"CBC",
|
||||
"CBC",
|
||||
"CBC",
|
||||
"CBC",
|
||||
"GCM",
|
||||
"GCM",
|
||||
"GCM",
|
||||
);
|
||||
|
||||
ENUM(match_type_names, FWP_MATCH_EQUAL, FWP_MATCH_NOT_EQUAL,
|
||||
"equals",
|
||||
"greater",
|
||||
"less than",
|
||||
"greater or equal than",
|
||||
"less or equal than",
|
||||
"in range",
|
||||
"has all flags set",
|
||||
"has any flags set",
|
||||
"has none flags set",
|
||||
"equals case insensitive",
|
||||
"not equal",
|
||||
);
|
||||
|
||||
ENUM(traffic_type_names, IPSEC_TRAFFIC_TYPE_TRANSPORT, IPSEC_TRAFFIC_TYPE_TUNNEL,
|
||||
"Transport",
|
||||
"Tunnel",
|
||||
);
|
||||
|
||||
/**
|
||||
* Print a GUID to a static buffer
|
||||
*/
|
||||
static char *guid2string(GUID *guid)
|
||||
{
|
||||
static char buf[64];
|
||||
|
||||
snprintf(buf, sizeof(buf),
|
||||
"%08x,%04x,%04x%02x,%02x,%02x,%02x,%02x,%02x,%02x,%02x",
|
||||
guid->Data1, guid->Data2, guid->Data3,
|
||||
guid->Data4[0], guid->Data4[1], guid->Data4[2], guid->Data4[3],
|
||||
guid->Data4[4], guid->Data4[5], guid->Data4[6], guid->Data4[7]);
|
||||
|
||||
return buf;
|
||||
}
|
||||
|
||||
const GUID FWPM_CONDITION_IP_SOURCE_ADDRESS = {
|
||||
0xae96897e, 0x2e94, 0x4bc9, { 0xb3,0x13,0xb2,0x7e,0xe8,0x0e,0x57,0x4d }
|
||||
};
|
||||
const GUID FWPM_CONDITION_IP_DESTINATION_ADDRESS = {
|
||||
0x2d79133b, 0xb390, 0x45c6, { 0x86,0x99,0xac,0xac,0xea,0xaf,0xed,0x33 }
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert filter condition key GUID to some known strings
|
||||
*/
|
||||
static char* cond2name(GUID *guid, bool *address)
|
||||
{
|
||||
struct {
|
||||
GUID guid;
|
||||
char *name;
|
||||
bool address;
|
||||
} map[] = {
|
||||
{ FWPM_CONDITION_IP_LOCAL_ADDRESS, "local address", TRUE},
|
||||
{ FWPM_CONDITION_IP_REMOTE_ADDRESS, "remote address", TRUE},
|
||||
{ FWPM_CONDITION_IP_SOURCE_ADDRESS, "source address", TRUE},
|
||||
{ FWPM_CONDITION_IP_DESTINATION_ADDRESS, "destination address", TRUE},
|
||||
{ FWPM_CONDITION_IP_LOCAL_PORT, "local port", FALSE},
|
||||
{ FWPM_CONDITION_IP_REMOTE_PORT, "remote port", FALSE},
|
||||
{ FWPM_CONDITION_IP_PROTOCOL, "protocol", FALSE},
|
||||
{ FWPM_CONDITION_ICMP_CODE, "icmp code", FALSE},
|
||||
{ FWPM_CONDITION_ICMP_TYPE, "icmp type", FALSE},
|
||||
};
|
||||
int i;
|
||||
|
||||
for (i = 0; i < countof(map); i++)
|
||||
{
|
||||
if (memeq(&map[i].guid, guid, sizeof(GUID)))
|
||||
{
|
||||
*address = map[i].address;
|
||||
return map[i].name;
|
||||
}
|
||||
}
|
||||
*address = FALSE;
|
||||
return guid2string(guid);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print a host from raw data and IP version
|
||||
*/
|
||||
static void print_host(FWP_IP_VERSION version, void *data)
|
||||
{
|
||||
host_t *host = NULL;
|
||||
UINT32 ints[4];
|
||||
|
||||
switch (version)
|
||||
{
|
||||
case FWP_IP_VERSION_V4:
|
||||
ints[0] = untoh32(data);
|
||||
host = host_create_from_chunk(AF_INET, chunk_from_thing(ints[0]), 0);
|
||||
break;
|
||||
case FWP_IP_VERSION_V6:
|
||||
ints[3] = untoh32(data);
|
||||
ints[2] = untoh32(data + 4);
|
||||
ints[1] = untoh32(data + 8);
|
||||
ints[0] = untoh32(data + 12);
|
||||
host = host_create_from_chunk(AF_INET6, chunk_from_thing(ints), 0);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
if (host)
|
||||
{
|
||||
printf("%H", host);
|
||||
host->destroy(host);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Print IPSEC_SA_AUTH_INFORMATION0
|
||||
*/
|
||||
static void print_auth(IPSEC_SA_AUTH_INFORMATION0 *a)
|
||||
{
|
||||
printf("%N-%N",
|
||||
auth_type_names, a->authTransform.authTransformId.authType,
|
||||
auth_config_names, a->authTransform.authTransformId.authConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print IPSEC_SA_CIPHER_INFORMATION0
|
||||
*/
|
||||
static void print_cipher(IPSEC_SA_CIPHER_INFORMATION0 *c)
|
||||
{
|
||||
printf("%N-%N",
|
||||
cipher_type_names, c->cipherTransform.cipherTransformId.cipherType,
|
||||
cipher_config_names, c->cipherTransform.cipherTransformId.cipherConfig);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print IPsec SA transform
|
||||
*/
|
||||
static void list_sa(HANDLE engine, IPSEC_SA0 *sa)
|
||||
{
|
||||
printf(" SPI 0x%08x\n", sa->spi);
|
||||
switch (sa->saTransformType)
|
||||
{
|
||||
case IPSEC_TRANSFORM_AH:
|
||||
printf(" AH: ");
|
||||
print_auth(sa->ahInformation);
|
||||
break;
|
||||
case IPSEC_TRANSFORM_ESP_AUTH:
|
||||
printf(" ESP: ");
|
||||
print_auth(sa->espAuthInformation);
|
||||
break;
|
||||
case IPSEC_TRANSFORM_ESP_CIPHER:
|
||||
printf(" ESP: ");
|
||||
print_cipher(sa->espCipherInformation);
|
||||
break;
|
||||
case IPSEC_TRANSFORM_ESP_AUTH_AND_CIPHER:
|
||||
printf(" ESP: ");
|
||||
print_auth(&sa->espAuthAndCipherInformation->saAuthInformation);
|
||||
printf(", ");
|
||||
print_cipher(&sa->espAuthAndCipherInformation->saCipherInformation);
|
||||
break;
|
||||
default:
|
||||
printf(" (Transform %d)", sa->saTransformType);
|
||||
break;
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* List a filter condition value, optionally as IP address
|
||||
*/
|
||||
static void print_value(FWP_CONDITION_VALUE0 *value, bool address)
|
||||
{
|
||||
chunk_t chunk;
|
||||
|
||||
switch (value->type)
|
||||
{
|
||||
case FWP_EMPTY:
|
||||
printf("empty");
|
||||
break;
|
||||
case FWP_UINT8:
|
||||
printf("%u", value->uint8);
|
||||
break;
|
||||
case FWP_UINT16:
|
||||
printf("%u", value->uint16);
|
||||
break;
|
||||
case FWP_UINT32:
|
||||
if (address)
|
||||
{
|
||||
print_host(FWP_IP_VERSION_V4, &value->uint32);
|
||||
}
|
||||
else
|
||||
{
|
||||
printf("%u", value->uint32);
|
||||
}
|
||||
break;
|
||||
case FWP_UINT64:
|
||||
printf("%llu", value->uint64);
|
||||
break;
|
||||
case FWP_INT8:
|
||||
printf("%d", value->int8);
|
||||
break;
|
||||
case FWP_INT16:
|
||||
printf("%d", value->int16);
|
||||
break;
|
||||
case FWP_INT32:
|
||||
printf("%d", value->int32);
|
||||
break;
|
||||
case FWP_INT64:
|
||||
printf("%lld", value->int64);
|
||||
break;
|
||||
case FWP_FLOAT:
|
||||
printf("%f", value->float32);
|
||||
break;
|
||||
case FWP_DOUBLE:
|
||||
printf("%lf", value->double64);
|
||||
break;
|
||||
case FWP_BYTE_ARRAY16_TYPE:
|
||||
if (address)
|
||||
{
|
||||
print_host(FWP_IP_VERSION_V6, value->byteArray16);
|
||||
}
|
||||
else
|
||||
{
|
||||
chunk = chunk_create((u_char*)value->byteArray16, 16);
|
||||
printf("%#B", &chunk);
|
||||
}
|
||||
break;
|
||||
case FWP_BYTE_BLOB_TYPE:
|
||||
chunk = chunk_create(value->byteBlob->data, value->byteBlob->size);
|
||||
printf("%#B", &chunk);
|
||||
break;
|
||||
case FWP_V4_ADDR_MASK:
|
||||
print_host(FWP_IP_VERSION_V4, &value->v4AddrMask->addr);
|
||||
printf("/");
|
||||
print_host(FWP_IP_VERSION_V4, &value->v4AddrMask->mask);
|
||||
break;
|
||||
case FWP_V6_ADDR_MASK:
|
||||
print_host(FWP_IP_VERSION_V6, &value->v6AddrMask->addr);
|
||||
printf("/%u", &value->v6AddrMask->prefixLength);
|
||||
break;
|
||||
case FWP_RANGE_TYPE:
|
||||
print_value((FWP_CONDITION_VALUE0*)&value->rangeValue->valueLow,
|
||||
address);
|
||||
printf(" - ");
|
||||
print_value((FWP_CONDITION_VALUE0*)&value->rangeValue->valueHigh,
|
||||
address);
|
||||
break;
|
||||
default:
|
||||
printf("(unsupported)");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List a filter condition
|
||||
*/
|
||||
static void list_cond(HANDLE engine, FWPM_FILTER_CONDITION0 *cond)
|
||||
{
|
||||
bool address;
|
||||
|
||||
printf(" '%s' %N '", cond2name(&cond->fieldKey, &address),
|
||||
match_type_names, cond->matchType);
|
||||
print_value(&cond->conditionValue, address);
|
||||
printf("'\n");
|
||||
}
|
||||
|
||||
/**
|
||||
* Print IPsec SA details
|
||||
*/
|
||||
static void list_details(HANDLE engine, IPSEC_SA_DETAILS1 *details)
|
||||
{
|
||||
int i;
|
||||
|
||||
printf(" %sbound SA: ",
|
||||
details->saDirection == FWP_DIRECTION_INBOUND ? "In" : "Out");
|
||||
print_host(details->traffic.ipVersion, &details->traffic.localV4Address);
|
||||
printf(" %s ", details->saDirection == FWP_DIRECTION_INBOUND ? "<-" : "->");
|
||||
print_host(details->traffic.ipVersion, &details->traffic.remoteV4Address);
|
||||
printf("\n %N, flags: 0x%06x, lifetime: %us\n",
|
||||
traffic_type_names, details->traffic.trafficType,
|
||||
details->saBundle.flags, details->saBundle.lifetime.lifetimeSeconds);
|
||||
if (details->udpEncapsulation)
|
||||
{
|
||||
printf(" UDP encap ports %u - %u\n",
|
||||
details->udpEncapsulation->localUdpEncapPort,
|
||||
details->udpEncapsulation->remoteUdpEncapPort);
|
||||
}
|
||||
for (i = 0; i < details->saBundle.numSAs; i++)
|
||||
{
|
||||
list_sa(engine, &details->saBundle.saList[i]);
|
||||
}
|
||||
printf(" Filter ID %llu\n", details->transportFilter->filterId);
|
||||
for (i = 0; i < details->transportFilter->numFilterConditions; i++)
|
||||
{
|
||||
list_cond(engine, &details->transportFilter->filterCondition[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* List installed SA contexts
|
||||
*/
|
||||
static bool list_contexts(HANDLE engine)
|
||||
{
|
||||
HANDLE handle;
|
||||
UINT32 returned;
|
||||
DWORD res;
|
||||
IPSEC_SA_CONTEXT1 **entries;
|
||||
|
||||
res = IPsecSaContextCreateEnumHandle0(engine, NULL, &handle);
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "IPsecSaContextCreateEnumHandle0(): 0x%08x\n", res);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
while (TRUE)
|
||||
{
|
||||
res = IPsecSaContextEnum1(engine, handle, 1, &entries, &returned);
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "IPsecSaContextEnum1(): 0x%08x\n", res);
|
||||
IPsecSaContextDestroyEnumHandle0(engine, handle);
|
||||
return FALSE;
|
||||
}
|
||||
if (returned == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
printf("SA context %llu:\n", entries[0]->saContextId);
|
||||
list_details(engine, entries[0]->inboundSa);
|
||||
list_details(engine, entries[0]->outboundSa);
|
||||
|
||||
FwpmFreeMemory0((void**)&entries);
|
||||
}
|
||||
IPsecSaContextDestroyEnumHandle0(engine, handle);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* ipsecdump main()
|
||||
*/
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
FWPM_SESSION0 session = {
|
||||
.displayData = {
|
||||
.name = L"ipsecdump",
|
||||
.description = L"strongSwan SAD/SPD dumper",
|
||||
},
|
||||
};
|
||||
HANDLE engine;
|
||||
DWORD res;
|
||||
int code;
|
||||
|
||||
library_init(NULL, "ipsecdump");
|
||||
atexit(library_deinit);
|
||||
|
||||
res = FwpmEngineOpen0(NULL, RPC_C_AUTHN_WINNT, NULL, &session, &engine);
|
||||
if (res != ERROR_SUCCESS)
|
||||
{
|
||||
fprintf(stderr, "FwpmEngineOpen(): 0x%08x\n", res);
|
||||
return 2;
|
||||
}
|
||||
code = list_contexts(engine) ? 0 : 1;
|
||||
FwpmEngineClose0(engine);
|
||||
return code;
|
||||
}
|
||||
@@ -96,3 +96,7 @@ STUB(fwpuclnt, DWORD, IPsecSaContextGetById1, 24,
|
||||
|
||||
STUB(fwpuclnt, DWORD, IPsecSaContextUpdate0, 24,
|
||||
HANDLE engineHandle, UINT32 flags, const void *newValues)
|
||||
|
||||
STUB(fwpuclnt, DWORD, IPsecSaContextEnum1, 40,
|
||||
HANDLE engineHandle, HANDLE enumHandle, UINT32 numEntriesRequested,
|
||||
void ***entries, UINT32 *numEntriesReturned)
|
||||
|
||||
Reference in New Issue
Block a user