winws: ssid-filter

This commit is contained in:
bol-van
2024-06-19 19:46:16 +03:00
parent eebe68ca65
commit fc6bae8fcd
13 changed files with 223 additions and 68 deletions

View File

@@ -5,7 +5,7 @@ CFLAGS_MAC = -mmacosx-version-min=10.8
CFLAGS_CYGWIN = -Wno-address-of-packed-member -static
LIBS_LINUX = -lnetfilter_queue -lnfnetlink -lz
LIBS_BSD = -lz
LIBS_CYGWIN = -lz -Lwindivert -lwindivert
LIBS_CYGWIN = -lz -Lwindivert -lwindivert -lwlanapi
SRC_FILES = *.c crypto/*.c
all: nfqws

View File

@@ -2,5 +2,5 @@
#include "gcm.h"
// mode : ENCRYPT, DECRYPT
// mode : AES_ENCRYPT, AES_DECRYPT
int aes_gcm_crypt(int mode, uint8_t *output, const uint8_t *input, size_t input_length, const uint8_t *key, const size_t key_len, const uint8_t *iv, const size_t iv_len, const uint8_t *adata, size_t adata_len, uint8_t *atag, size_t atag_len);

View File

@@ -28,8 +28,8 @@
#include <string.h>
#define ENCRYPT 1 // specify whether we're encrypting
#define DECRYPT 0 // or decrypting
#define AES_ENCRYPT 1 // specify whether we're encrypting
#define AES_DECRYPT 0 // or decrypting
#if defined(_MSC_VER)
#include <basetsd.h>

View File

@@ -186,7 +186,7 @@ int gcm_setkey(gcm_context *ctx, // pointer to caller-provided gcm context
// encrypt the null 128-bit block to generate a key-based value
// which is then used to initialize our GHASH lookup tables
if ((ret = aes_setkey(&ctx->aes_ctx, ENCRYPT, key, keysize)) != 0)
if ((ret = aes_setkey(&ctx->aes_ctx, AES_ENCRYPT, key, keysize)) != 0)
return(ret);
if ((ret = aes_cipher(&ctx->aes_ctx, h, h)) != 0)
return(ret);
@@ -266,7 +266,7 @@ int gcm_start(gcm_context *ctx, // pointer to user-provided GCM context
ctx->add_len = 0;
ctx->mode = mode; // set the GCM encryption/decryption mode
ctx->aes_ctx.mode = ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
ctx->aes_ctx.mode = AES_ENCRYPT; // GCM *always* runs AES in ENCRYPTION mode
if (iv_len == 12) { // GCM natively uses a 12-byte, 96-bit IV
memcpy(ctx->y, iv, iv_len); // copy the IV to the top of the 'y' buff
@@ -338,7 +338,7 @@ int gcm_update(gcm_context *ctx, // pointer to user-provided GCM context
return(ret);
// encrypt or decrypt the input to the output
if (ctx->mode == ENCRYPT)
if (ctx->mode == AES_ENCRYPT)
{
for (i = 0; i < use_len; i++) {
// XOR the cipher's ouptut vector (ectr) with our input
@@ -481,7 +481,7 @@ int gcm_auth_decrypt(
(which is an identical XORing to reverse the previous one)
and also to re-generate the matching authentication tag
*/
gcm_crypt_and_tag(ctx, DECRYPT, iv, iv_len, add, add_len,
gcm_crypt_and_tag(ctx, AES_DECRYPT, iv, iv_len, add, add_len,
input, output, length, check_tag, tag_len);
// now we verify the authentication tag in 'constant time'

View File

@@ -14,6 +14,9 @@
#include "params.h"
#include "nfqws.h"
#ifdef __CYGWIN__
#include <wlanapi.h>
#endif
uint32_t net32_add(uint32_t netorder_value, uint32_t cpuorder_increment)
{
@@ -959,8 +962,83 @@ void tcp_rewrite_winsize(struct tcphdr *tcp, uint16_t winsize, uint8_t scale_fac
static HANDLE w_filter = NULL;
static OVERLAPPED ovl = { .hEvent = NULL };
static const struct str_list_head *wlan_filter_ssid = NULL;
static DWORD wlan_filter_tick=0;
uint32_t w_win32_error=0;
bool wlan_filter_match(const struct str_list_head *ssid_list)
{
DWORD dwCurVersion;
HANDLE hClient = NULL;
PWLAN_INTERFACE_INFO_LIST pIfList = NULL;
PWLAN_INTERFACE_INFO pIfInfo;
PWLAN_CONNECTION_ATTRIBUTES pConnectInfo;
DWORD connectInfoSize, k;
bool bRes;
struct str_list *ssid;
size_t len;
// no filter given. always matches.
if (!ssid_list || LIST_EMPTY(ssid_list))
{
w_win32_error = 0;
return true;
}
w_win32_error = WlanOpenHandle(2, NULL, &dwCurVersion, &hClient);
if (w_win32_error != ERROR_SUCCESS) goto fail;
w_win32_error = WlanEnumInterfaces(hClient, NULL, &pIfList);
if (w_win32_error != ERROR_SUCCESS) goto fail;
for (k = 0; k < pIfList->dwNumberOfItems; k++)
{
pIfInfo = pIfList->InterfaceInfo + k;
if (pIfInfo->isState == wlan_interface_state_connected)
{
w_win32_error = WlanQueryInterface(hClient,
&pIfInfo->InterfaceGuid,
wlan_intf_opcode_current_connection,
NULL,
&connectInfoSize,
(PVOID *)&pConnectInfo,
NULL);
if (w_win32_error != ERROR_SUCCESS) goto fail;
// printf("%s\n", pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID);
LIST_FOREACH(ssid, ssid_list, next)
{
len = strlen(ssid->str);
if (len==pConnectInfo->wlanAssociationAttributes.dot11Ssid.uSSIDLength && !memcmp(ssid->str,pConnectInfo->wlanAssociationAttributes.dot11Ssid.ucSSID,len))
{
WlanFreeMemory(pConnectInfo);
goto found;
}
}
WlanFreeMemory(pConnectInfo);
}
}
w_win32_error = 0;
fail:
bRes = false;
ex:
if (pIfList) WlanFreeMemory(pIfList);
if (hClient) WlanCloseHandle(hClient, 0);
return bRes;
found:
w_win32_error = 0;
bRes = true;
goto ex;
}
static bool wlan_filter_match_rate_limited(void)
{
DWORD dwTick = GetTickCount() / 1000;
if (wlan_filter_tick == dwTick) return true;
wlan_filter_tick = dwTick;
return wlan_filter_match(wlan_filter_ssid);
}
static HANDLE windivert_init_filter(const char *filter, UINT64 flags)
{
LPSTR errormessage = NULL;
@@ -1006,8 +1084,9 @@ void rawsend_cleanup(void)
CloseHandle(ovl.hEvent);
ovl.hEvent=NULL;
}
wlan_filter_ssid = NULL;
}
bool windivert_init(const char *filter)
bool windivert_init(const char *filter, const struct str_list_head *ssid_filter)
{
rawsend_cleanup();
w_filter = windivert_init_filter(filter, 0);
@@ -1020,6 +1099,7 @@ bool windivert_init(const char *filter)
rawsend_cleanup();
return false;
}
wlan_filter_ssid = ssid_filter;
return true;
}
return false;
@@ -1037,6 +1117,11 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len,
errno=EINTR;
return false;
}
if (!wlan_filter_match_rate_limited())
{
errno=ENODEV;
return false;
}
usleep(0);
if (WinDivertRecvEx(hFilter, packet, *len, &recv_len, 0, wa, NULL, &ovl))
{
@@ -1057,6 +1142,11 @@ static bool windivert_recv_filter(HANDLE hFilter, uint8_t *packet, size_t *len,
errno=EINTR;
return false;
}
if (!wlan_filter_match_rate_limited())
{
errno=ENODEV;
return false;
}
usleep(0);
}
if (!GetOverlappedResult(hFilter,&ovl,&rd,TRUE))

View File

@@ -18,6 +18,7 @@
#endif
#include "packet_queue.h"
#include "pools.h"
#ifndef IPPROTO_DIVERT
#define IPPROTO_DIVERT 258
@@ -151,9 +152,10 @@ bool tcp_has_fastopen(const struct tcphdr *tcp);
#ifdef __CYGWIN__
extern uint32_t w_win32_error;
bool windivert_init(const char *filter);
bool windivert_init(const char *filter, const struct str_list_head *ssid_filter);
bool windivert_recv(uint8_t *packet, size_t *len, WINDIVERT_ADDRESS *wa);
bool windivert_send(const uint8_t *packet, size_t len, const WINDIVERT_ADDRESS *wa);
bool wlan_filter_match(const struct str_list_head *ssid_list);
#else
// should pre-do it if dropping privileges. otherwise its not necessary
bool rawsend_preinit(bool bind_fix4, bool bind_fix6);

View File

@@ -410,72 +410,99 @@ static int win_main(const char *windivert_filter)
WINDIVERT_ADDRESS wa;
char ifout[22];
if (!windivert_init(windivert_filter))
return w_win32_error;
printf("windivert initialized. capture is started.\n");
pre_desync();
// cygwin auto flush fails when piping
fflush(stdout);
fflush(stderr);
for (id=0;;id++)
for(;;)
{
len = sizeof(packet);
if (!windivert_recv(packet, &len, &wa))
if (!wlan_filter_match(&params.ssid_filter))
{
if (errno==ENOBUFS)
printf("logical network is not present. waiting it to appear.\n");
fflush(stdout);
do
{
DLOG("windivert: ignoring too large packet\n")
continue; // too large packet
if (bQuit)
{
DLOG("QUIT requested\n")
return 0;
}
usleep(500000);
}
else if (errno==EINTR)
{
DLOG("QUIT requested\n")
break;
}
fprintf(stderr, "windivert: recv failed. errno %d\n", errno);
while (!wlan_filter_match(&params.ssid_filter));
printf("logical network now present\n");
fflush(stdout);
}
if (!windivert_init(windivert_filter, &params.ssid_filter))
return w_win32_error;
}
*ifout=0;
if (wa.Outbound) snprintf(ifout,sizeof(ifout),"%u.%u", wa.Network.IfIdx, wa.Network.SubIfIdx);
DLOG("packet: id=%u len=%zu %s IPv6=%u IPChecksum=%u TCPChecksum=%u UDPChecksum=%u IfIdx=%u.%u\n", id, len, wa.Outbound ? "outbound" : "inbound", wa.IPv6, wa.IPChecksum, wa.TCPChecksum, wa.UDPChecksum, wa.Network.IfIdx, wa.Network.SubIfIdx)
if (wa.Impostor)
{
DLOG("windivert: passing impostor packet\n")
verdict = VERDICT_PASS;
}
else if (wa.Loopback)
{
DLOG("windivert: passing loopback packet\n")
verdict = VERDICT_PASS;
}
else
{
mark=0;
// pseudo interface id IfIdx.SubIfIdx
verdict = processPacketData(&mark, ifout, packet, &len);
}
switch (verdict & VERDICT_MASK)
{
case VERDICT_PASS:
case VERDICT_MODIFY:
if ((verdict & VERDICT_MASK)==VERDICT_PASS)
DLOG("packet: id=%u reinject unmodified\n", id)
else
DLOG("packet: id=%u reinject modified len=%zu\n", id, len)
if (!windivert_send(packet, len, &wa))
fprintf(stderr,"windivert: reinject of packet id=%u failed\n", id);
break;
default:
DLOG("packet: id=%u drop\n", id);
}
printf("windivert initialized. capture is started.\n");
// cygwin auto flush fails when piping
fflush(stdout);
fflush(stderr);
for (id=0;;id++)
{
len = sizeof(packet);
if (!windivert_recv(packet, &len, &wa))
{
if (errno==ENOBUFS)
{
DLOG("windivert: ignoring too large packet\n")
continue; // too large packet
}
else if (errno==ENODEV)
{
printf("logical network disappeared. deinitializing windivert.\n");
rawsend_cleanup();
break;
}
else if (errno==EINTR)
{
DLOG("QUIT requested\n")
return 0;
}
fprintf(stderr, "windivert: recv failed. errno %d\n", errno);
return w_win32_error;
}
*ifout=0;
if (wa.Outbound) snprintf(ifout,sizeof(ifout),"%u.%u", wa.Network.IfIdx, wa.Network.SubIfIdx);
DLOG("packet: id=%u len=%zu %s IPv6=%u IPChecksum=%u TCPChecksum=%u UDPChecksum=%u IfIdx=%u.%u\n", id, len, wa.Outbound ? "outbound" : "inbound", wa.IPv6, wa.IPChecksum, wa.TCPChecksum, wa.UDPChecksum, wa.Network.IfIdx, wa.Network.SubIfIdx)
if (wa.Impostor)
{
DLOG("windivert: passing impostor packet\n")
verdict = VERDICT_PASS;
}
else if (wa.Loopback)
{
DLOG("windivert: passing loopback packet\n")
verdict = VERDICT_PASS;
}
else
{
mark=0;
// pseudo interface id IfIdx.SubIfIdx
verdict = processPacketData(&mark, ifout, packet, &len);
}
switch (verdict & VERDICT_MASK)
{
case VERDICT_PASS:
case VERDICT_MODIFY:
if ((verdict & VERDICT_MASK)==VERDICT_PASS)
DLOG("packet: id=%u reinject unmodified\n", id)
else
DLOG("packet: id=%u reinject modified len=%zu\n", id, len)
if (!windivert_send(packet, len, &wa))
fprintf(stderr,"windivert: reinject of packet id=%u failed\n", id);
break;
default:
DLOG("packet: id=%u drop\n", id);
}
// cygwin auto flush fails when piping
fflush(stdout);
fflush(stderr);
}
}
return 0;
}
@@ -521,6 +548,9 @@ static void cleanup_params(void)
StrPoolDestroy(&params.hostlist_exclude);
StrPoolDestroy(&params.hostlist);
HostFailPoolDestroy(&params.hostlist_auto_fail_counters);
#ifdef __CYGWIN__
strlist_destroy(&params.ssid_filter);
#endif
}
static void exit_clean(int code)
{
@@ -760,6 +790,8 @@ static void exithelp(void)
" --wf-udp=[~]port1[-port2]\t\t\t; UDP port filter. ~ means negation. multiple comma separated values allowed.\n"
" --wf-raw=<filter>|@<filename>\t\t\t; raw windivert filter string or filename\n"
" --wf-save=<filename>\t\t\t\t; save windivert filter string to a file and exit\n"
"\nLOGICAL NETWORK FILTER:\n"
" --ssid-filter=ssid1[,ssid2,ssid3,...]\t\t; enable winws only if any of specified wifi SSIDs connected\n"
#endif
"\nHOSTLIST FILTER:\n"
" --hostlist=<filename>\t\t\t\t; apply dpi desync only to the listed hosts (one host per line, subdomains auto apply, gzip supported, multiple hostlists allowed)\n"
@@ -915,7 +947,9 @@ int main(int argc, char **argv)
LIST_INIT(&params.hostlist_files);
LIST_INIT(&params.hostlist_exclude_files);
#ifndef __CYGWIN__
#ifdef __CYGWIN__
LIST_INIT(&params.ssid_filter);
#else
if (can_drop_root()) // are we root ?
{
params.uid = params.gid = 0x7FFFFFFF; // default uid:gid
@@ -1002,6 +1036,7 @@ int main(int argc, char **argv)
{"wf-udp",required_argument,0,0}, // optidx=53
{"wf-raw",required_argument,0,0}, // optidx=54
{"wf-save",required_argument,0,0}, // optidx=55
{"ssid-filter",required_argument,0,0}, // optidx=56
#endif
{NULL,0,NULL,0}
};
@@ -1499,6 +1534,24 @@ int main(int argc, char **argv)
strncpy(wf_save_file, optarg, sizeof(wf_save_file));
wf_save_file[sizeof(wf_save_file) - 1] = '\0';
break;
case 56: /* ssid-filter */
{
char *e,*p = optarg;
while (p)
{
e = strchr(p,',');
if (e) *e++=0;
if (*p && !strlist_add(&params.ssid_filter, p))
{
fprintf(stderr, "strlist_add failed\n");
exit_clean(1);
}
p = e;
}
}
break;
#endif
}
}

View File

@@ -65,7 +65,9 @@ struct params_s
size_t fake_http_size,fake_tls_size,fake_quic_size,fake_wg_size,fake_dht_size,fake_unknown_size,fake_syndata_size,fake_unknown_udp_size;
int udplen_increment;
#ifndef __CYGWIN__
#ifdef __CYGWIN__
struct str_list_head ssid_filter;
#else
bool droproot;
uid_t uid;
gid_t gid;

View File

@@ -600,7 +600,7 @@ bool QUICDecryptInitial(const uint8_t *data, size_t data_len, uint8_t *clean, si
header[0] = packet0;
for(uint8_t i = 0; i < pkn_len; i++) header[header_len - 1 - i] = (uint8_t)(pkn >> (8 * i));
if (aes_gcm_crypt(DECRYPT, clean, decrypt_begin, cryptlen, aeskey, sizeof(aeskey), aesiv, sizeof(aesiv), header, header_len, atag, sizeof(atag)))
if (aes_gcm_crypt(AES_DECRYPT, clean, decrypt_begin, cryptlen, aeskey, sizeof(aeskey), aesiv, sizeof(aesiv), header, header_len, atag, sizeof(atag)))
return false;
// check if message was decrypted correctly : good keys , no data corruption