Added ts3init match.
This commit is contained in:
parent
c1e1b14475
commit
bc4e578d9a
17
README.md
17
README.md
@ -86,6 +86,23 @@ ts3init_get_puzzle match options:
|
||||
`check-cookie` is specified, either `random-seed` or `random-seed-file` needs
|
||||
to be specified too.
|
||||
|
||||
ts3init
|
||||
--------------------
|
||||
Matches a ts3init packet, by checking if the packet starts with the *TS3INIT1*.
|
||||
Additional header checks for client and server packets can be specified:
|
||||
```
|
||||
$ iptables -m ts3init -h
|
||||
<..>
|
||||
ts3init match options:
|
||||
--client Match ts3init client packets.
|
||||
--server Match ts3init server packets.
|
||||
--command <command> Match packets with the specified command.
|
||||
```
|
||||
* `client` checks that the packet has a valid ts3init client header
|
||||
* `server` checks that the packet has a valid ts3init server header
|
||||
* `command` checks that the packet has the specified command in its header.
|
||||
Requires either --client or --server.
|
||||
|
||||
Target extensions
|
||||
=================
|
||||
|
||||
|
@ -1,5 +1,5 @@
|
||||
CFLAGS = -O2 -Wall
|
||||
LIBS = libxt_ts3init_get_cookie.so libxt_ts3init_get_puzzle.so libxt_TS3INIT_RESET.so libxt_TS3INIT_SET_COOKIE.so libxt_TS3INIT_GET_COOKIE.so
|
||||
LIBS = libxt_ts3init.so libxt_ts3init_get_cookie.so libxt_ts3init_get_puzzle.so libxt_TS3INIT_RESET.so libxt_TS3INIT_SET_COOKIE.so libxt_TS3INIT_GET_COOKIE.so
|
||||
all: $(LIBS)
|
||||
|
||||
clean:
|
||||
|
146
src/libxt_ts3init.c
Normal file
146
src/libxt_ts3init.c
Normal file
@ -0,0 +1,146 @@
|
||||
/*
|
||||
* "ts3init" match extension for iptables
|
||||
* Niels Werensteijn <niels werensteijn [at] teamspeak com>, 2016-10-03
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License; either version 2
|
||||
* or 3 of the License, as published by the Free Software Foundation.
|
||||
*/
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stddef.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <getopt.h>
|
||||
#include <xtables.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
#include <unistd.h>
|
||||
#include "ts3init_random_seed.h"
|
||||
#include "ts3init_match.h"
|
||||
|
||||
#define param_act(t, s, f) xtables_param_act((t), "ts3init", (s), (f))
|
||||
#define ARRAY_SIZE(x) (sizeof(x) / sizeof(*(x)))
|
||||
|
||||
static void ts3init_help(void)
|
||||
{
|
||||
printf(
|
||||
"ts3init match options:\n"
|
||||
" --client Match ts3init client packets.\n"
|
||||
" --server Match ts3init server packets.\n"
|
||||
" --command <command> Match packets with the specified command.\n"
|
||||
);
|
||||
}
|
||||
|
||||
static const struct option ts3init_opts[] = {
|
||||
{.name = "client", .has_arg = false, .val = '1'},
|
||||
{.name = "server", .has_arg = false, .val = '2'},
|
||||
{.name = "command", .has_arg = true, .val = '3'},
|
||||
{NULL},
|
||||
};
|
||||
|
||||
static int ts3init_parse(int c, char **argv, int invert, unsigned int *flags,
|
||||
const void *entry, struct xt_entry_match **match)
|
||||
{
|
||||
struct xt_ts3init_mtinfo *info = (void *)(*match)->data;
|
||||
int command;
|
||||
|
||||
switch (c) {
|
||||
case '1':
|
||||
param_act(XTF_ONLY_ONCE, "--client", info->specific_options & CHK_TS3INIT_CLIENT);
|
||||
param_act(XTF_NO_INVERT, "--client", invert);
|
||||
info->specific_options |= CHK_TS3INIT_CLIENT;
|
||||
*flags |= CHK_TS3INIT_CLIENT;
|
||||
return true;
|
||||
|
||||
case '2':
|
||||
param_act(XTF_ONLY_ONCE, "--server", info->specific_options & CHK_TS3INIT_SERVER);
|
||||
param_act(XTF_NO_INVERT, "--server", invert);
|
||||
info->specific_options |= CHK_TS3INIT_SERVER;
|
||||
*flags |= CHK_TS3INIT_SERVER;
|
||||
return true;
|
||||
|
||||
case '3':
|
||||
param_act(XTF_ONLY_ONCE, "--random-seed", info->specific_options & CHK_TS3INIT_COMMAND);
|
||||
param_act(XTF_NO_INVERT, "--random-seed", invert);
|
||||
command = atoi(optarg);
|
||||
if (command < 0 || command > 255)
|
||||
xtables_error(PARAMETER_PROBLEM,
|
||||
"ts3init: invalid command number");
|
||||
info->specific_options |= CHK_TS3INIT_COMMAND;
|
||||
info->command = (__u8)command;
|
||||
*flags |= CHK_TS3INIT_COMMAND;
|
||||
return true;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static void ts3init_save(const void *ip, const struct xt_entry_match *match)
|
||||
{
|
||||
const struct xt_ts3init_mtinfo *info = (const void *)match->data;
|
||||
if (info->specific_options & CHK_TS3INIT_CLIENT)
|
||||
{
|
||||
printf("--client ");
|
||||
}
|
||||
if (info->specific_options & CHK_TS3INIT_SERVER)
|
||||
{
|
||||
printf("--server ");
|
||||
}
|
||||
if (info->specific_options & CHK_TS3INIT_COMMAND)
|
||||
{
|
||||
printf("--command %i ", (int)info->command);
|
||||
}
|
||||
}
|
||||
|
||||
static void ts3init_print(const void *ip, const struct xt_entry_match *match,
|
||||
int numeric)
|
||||
{
|
||||
printf(" -m ts3init ");
|
||||
ts3init_save(ip, match);
|
||||
}
|
||||
|
||||
static void ts3init_check(unsigned int flags)
|
||||
{
|
||||
bool client = flags & CHK_TS3INIT_CLIENT;
|
||||
bool server = flags & CHK_TS3INIT_SERVER;
|
||||
if (client && server)
|
||||
{
|
||||
xtables_error(PARAMETER_PROBLEM,
|
||||
"ts3init_: --client and --server can not be specified at the same time");
|
||||
}
|
||||
if (flags & CHK_TS3INIT_COMMAND)
|
||||
{
|
||||
if (!client && !server)
|
||||
{
|
||||
xtables_error(PARAMETER_PROBLEM,
|
||||
"ts3init: --command requires either --client or --server");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* register and init */
|
||||
static struct xtables_match ts3init_mt_reg[] =
|
||||
{
|
||||
{
|
||||
.name = "ts3init",
|
||||
.revision = 0,
|
||||
.family = NFPROTO_UNSPEC,
|
||||
.version = XTABLES_VERSION,
|
||||
.size = XT_ALIGN(sizeof(struct xt_ts3init_mtinfo)),
|
||||
.userspacesize = XT_ALIGN(sizeof(struct xt_ts3init_mtinfo)),
|
||||
.help = ts3init_help,
|
||||
.parse = ts3init_parse,
|
||||
.print = ts3init_print,
|
||||
.save = ts3init_save,
|
||||
.extra_opts = ts3init_opts,
|
||||
.final_check = ts3init_check,
|
||||
},
|
||||
};
|
||||
|
||||
static __attribute__((constructor)) void ts3init_mt_ldr(void)
|
||||
{
|
||||
xtables_register_matches(ts3init_mt_reg, ARRAY_SIZE(ts3init_mt_reg));
|
||||
}
|
@ -22,7 +22,7 @@ struct ts3_init_header_tag
|
||||
/*
|
||||
* Header of a TS3INIT client packet.
|
||||
*/
|
||||
struct ts3_init_header
|
||||
struct ts3_init_client_header
|
||||
{
|
||||
struct ts3_init_header_tag tag;
|
||||
__be16 packet_id;
|
||||
@ -30,7 +30,17 @@ struct ts3_init_header
|
||||
__u8 flags;
|
||||
__u8 client_version[4];
|
||||
__u8 command;
|
||||
__u8 payload[20];
|
||||
};
|
||||
|
||||
/*
|
||||
* Header of a TS3INIT server packet.
|
||||
*/
|
||||
struct ts3_init_server_header
|
||||
{
|
||||
struct ts3_init_header_tag tag;
|
||||
__be16 packet_id;
|
||||
__u8 flags;
|
||||
__u8 command;
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -31,35 +31,39 @@ static const struct ts3_init_header_tag ts3init_header_tag_signature =
|
||||
{ .tag8 = {'T', 'S', '3', 'I', 'N', 'I', 'T', '1'} };
|
||||
|
||||
|
||||
struct ts3_init_checked_header_data
|
||||
struct ts3_init_checked_client_header_data
|
||||
{
|
||||
struct udphdr *udp, udp_buf;
|
||||
struct ts3_init_header* ts3_header, ts3_header_buf;
|
||||
struct ts3_init_client_header* ts3_header, ts3_header_buf;
|
||||
};
|
||||
|
||||
static int ts3init_payload_sizes[] = { 16, 20, 20, 244, -1, 1 };
|
||||
struct ts3_init_checked_server_header_data
|
||||
{
|
||||
struct udphdr *udp, udp_buf;
|
||||
struct ts3_init_server_header* ts3_header, ts3_header_buf;
|
||||
};
|
||||
|
||||
static const int ts3init_payload_sizes[] = { 16, 20, 20, 244, -1, 1 };
|
||||
|
||||
/*
|
||||
* Check that skb contains a valid TS3INIT client header.
|
||||
* Also initializes header_data, and checks client version.
|
||||
*/
|
||||
static bool check_header(const struct sk_buff *skb, const struct xt_action_param *par,
|
||||
struct ts3_init_checked_header_data* header_data, __u32 min_client_version)
|
||||
static bool check_client_header(const struct sk_buff *skb, const struct xt_action_param *par,
|
||||
struct ts3_init_checked_client_header_data* header_data, __u32 min_client_version)
|
||||
{
|
||||
unsigned int data_len;
|
||||
struct udphdr *udp;
|
||||
struct ts3_init_header* ts3_header;
|
||||
int expected_payload_size;
|
||||
struct ts3_init_client_header* ts3_header;
|
||||
|
||||
udp = skb_header_pointer(skb, par->thoff, sizeof(*udp), &header_data->udp_buf);
|
||||
data_len = be16_to_cpu(udp->len) - sizeof(*udp);
|
||||
|
||||
if (data_len < TS3INIT_HEADER_CLIENT_LENGTH ||
|
||||
data_len > sizeof(header_data->ts3_header_buf))
|
||||
if (data_len < sizeof(header_data->ts3_header_buf))
|
||||
return false;
|
||||
|
||||
ts3_header = (struct ts3_init_header*) skb_header_pointer(skb,
|
||||
par->thoff + sizeof(*udp), data_len,
|
||||
ts3_header = (struct ts3_init_client_header*) skb_header_pointer(skb,
|
||||
par->thoff + sizeof(*udp), sizeof(header_data->ts3_header_buf),
|
||||
&header_data->ts3_header_buf);
|
||||
|
||||
if (!ts3_header) return false;
|
||||
@ -68,7 +72,6 @@ static bool check_header(const struct sk_buff *skb, const struct xt_action_param
|
||||
if (ts3_header->packet_id != cpu_to_be16(101)) return false;
|
||||
if (ts3_header->client_id != 0) return false;
|
||||
if (ts3_header->flags != 0x88) return false;
|
||||
if (ts3_header->command >= COMMAND_MAX) return false;
|
||||
|
||||
/* check min_client_version if needed */
|
||||
if (min_client_version)
|
||||
@ -84,16 +87,52 @@ static bool check_header(const struct sk_buff *skb, const struct xt_action_param
|
||||
return false;
|
||||
}
|
||||
|
||||
/* payload size check*/
|
||||
expected_payload_size = ts3init_payload_sizes[ts3_header->command];
|
||||
if (data_len != TS3INIT_HEADER_CLIENT_LENGTH + expected_payload_size)
|
||||
return false;
|
||||
|
||||
header_data->udp = udp;
|
||||
header_data->udp = udp;
|
||||
header_data->ts3_header = ts3_header;
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Check that skb contains a valid TS3INIT server header.
|
||||
*/
|
||||
static bool check_server_header(const struct sk_buff *skb, const struct xt_action_param *par,
|
||||
struct ts3_init_checked_server_header_data* header_data)
|
||||
{
|
||||
unsigned int data_len;
|
||||
struct udphdr *udp;
|
||||
struct ts3_init_server_header* ts3_header;
|
||||
|
||||
udp = skb_header_pointer(skb, par->thoff, sizeof(*udp), &header_data->udp_buf);
|
||||
data_len = be16_to_cpu(udp->len) - sizeof(*udp);
|
||||
|
||||
if (data_len < sizeof(header_data->ts3_header_buf)) return false;
|
||||
|
||||
ts3_header = (struct ts3_init_server_header*) skb_header_pointer(skb,
|
||||
par->thoff + sizeof(*udp), sizeof(header_data->ts3_header_buf),
|
||||
&header_data->ts3_header_buf);
|
||||
|
||||
if (!ts3_header) return false;
|
||||
|
||||
if (ts3_header->tag.tag64 != ts3init_header_tag_signature.tag64) return false;
|
||||
if (ts3_header->packet_id != cpu_to_be16(101)) return false;
|
||||
if (ts3_header->flags != 0x88) return false;
|
||||
|
||||
header_data->udp = udp;
|
||||
header_data->ts3_header = ts3_header;
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline char* get_payload(const struct sk_buff *skb, const struct xt_action_param *par,
|
||||
const struct ts3_init_checked_client_header_data* header_data,
|
||||
char *buf, size_t buf_size)
|
||||
{
|
||||
const int header_len = sizeof(*header_data->udp) + sizeof(*header_data->ts3_header);
|
||||
unsigned int data_len = be16_to_cpu(header_data->udp->len) - header_len;
|
||||
if (data_len != buf_size)
|
||||
return NULL;
|
||||
return skb_header_pointer(skb, par->thoff + header_len, buf_size, buf);
|
||||
}
|
||||
|
||||
/*
|
||||
* Hashes the cookie with source/destination address/port.
|
||||
*/
|
||||
@ -143,24 +182,29 @@ static bool
|
||||
ts3init_get_cookie_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
{
|
||||
const struct xt_ts3init_get_cookie_mtinfo *info = par->matchinfo;
|
||||
struct ts3_init_checked_header_data header_data;
|
||||
struct ts3_init_checked_client_header_data header_data;
|
||||
|
||||
if (!check_header(skb, par, &header_data, info->min_client_version))
|
||||
if (!check_client_header(skb, par, &header_data, info->min_client_version))
|
||||
return false;
|
||||
|
||||
if (header_data.ts3_header->command != COMMAND_GET_COOKIE) return false;
|
||||
|
||||
if (info->specific_options & CHK_GET_COOKIE_CHECK_TIMESTAMP)
|
||||
{
|
||||
char *payload, payload_buf[ts3init_payload_sizes[COMMAND_GET_COOKIE]];
|
||||
time_t current_unix_time, packet_unix_time;
|
||||
|
||||
payload = get_payload(skb, par, &header_data, payload_buf, sizeof(payload_buf));
|
||||
if (!payload)
|
||||
return false;
|
||||
|
||||
current_unix_time = ts3init_get_cached_unix_time();
|
||||
|
||||
packet_unix_time =
|
||||
header_data.ts3_header->payload[0] << 24 |
|
||||
header_data.ts3_header->payload[1] << 16 |
|
||||
header_data.ts3_header->payload[2] << 8 |
|
||||
header_data.ts3_header->payload[3];
|
||||
payload[0] << 24 |
|
||||
payload[1] << 16 |
|
||||
payload[2] << 8 |
|
||||
payload[3];
|
||||
|
||||
if (abs(current_unix_time - packet_unix_time) > info->max_utc_offset)
|
||||
return false;
|
||||
@ -204,20 +248,24 @@ static int ts3init_get_cookie_mt_check(const struct xt_mtchk_param *par)
|
||||
static bool ts3init_get_puzzle_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
{
|
||||
const struct xt_ts3init_get_puzzle_mtinfo *info = par->matchinfo;
|
||||
struct ts3_init_checked_header_data header_data;
|
||||
struct ts3_init_checked_client_header_data header_data;
|
||||
|
||||
if (!check_header(skb, par, &header_data, info->min_client_version))
|
||||
if (!check_client_header(skb, par, &header_data, info->min_client_version))
|
||||
return false;
|
||||
|
||||
if (header_data.ts3_header->command != COMMAND_GET_PUZZLE) return false;
|
||||
|
||||
if (info->specific_options & CHK_GET_PUZZLE_CHECK_COOKIE)
|
||||
{
|
||||
struct ts3_init_header* ts3_header = header_data.ts3_header;
|
||||
char *payload, payload_buf[ts3init_payload_sizes[COMMAND_GET_PUZZLE]];
|
||||
__u64 cookie_seed[2];
|
||||
__u64 cookie, packet_cookie;
|
||||
|
||||
if (ts3init_get_cookie_seed_for_packet_index(ts3_header->payload[8], info->random_seed, &cookie_seed) == false)
|
||||
payload = get_payload(skb, par, &header_data, payload_buf, sizeof(payload_buf));
|
||||
if (!payload)
|
||||
return false;
|
||||
|
||||
if (ts3init_get_cookie_seed_for_packet_index(payload[8], info->random_seed, &cookie_seed) == false)
|
||||
return false;
|
||||
|
||||
/* use cookie_seed and ipaddress and port to create a hash
|
||||
@ -228,10 +276,10 @@ static bool ts3init_get_puzzle_mt(const struct sk_buff *skb, struct xt_action_pa
|
||||
/* compare cookie with payload bytes 0-7. if equal, cookie
|
||||
* is valid */
|
||||
|
||||
packet_cookie = (((u64)((ts3_header->payload)[0])) | ((u64)((ts3_header->payload)[1]) << 8) |
|
||||
((u64)((ts3_header->payload)[2]) << 16) | ((u64)((ts3_header->payload)[3]) << 24) |
|
||||
((u64)((ts3_header->payload)[4]) << 32) | ((u64)((ts3_header->payload)[5]) << 40) |
|
||||
((u64)((ts3_header->payload)[6]) << 48) | ((u64)((ts3_header->payload)[7]) << 56));
|
||||
packet_cookie = (((u64)((payload)[0])) | ((u64)((payload)[1]) << 8) |
|
||||
((u64)((payload)[2]) << 16) | ((u64)((payload)[3]) << 24) |
|
||||
((u64)((payload)[4]) << 32) | ((u64)((payload)[5]) << 40) |
|
||||
((u64)((payload)[6]) << 48) | ((u64)((payload)[7]) << 56));
|
||||
|
||||
if (packet_cookie != cookie) return false;
|
||||
}
|
||||
@ -266,6 +314,76 @@ static int ts3init_get_puzzle_mt_check(const struct xt_mtchk_param *par)
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* The 'ts3init' match handler.
|
||||
* Checks that the packet is a valid ts3init packet
|
||||
*/
|
||||
static bool ts3init_mt(const struct sk_buff *skb, struct xt_action_param *par)
|
||||
{
|
||||
const struct xt_ts3init_mtinfo *info = par->matchinfo;
|
||||
|
||||
if (info->specific_options & CHK_TS3INIT_CLIENT)
|
||||
{
|
||||
struct ts3_init_checked_client_header_data header_data;
|
||||
|
||||
if (!check_client_header(skb, par, &header_data, 0))
|
||||
return false;
|
||||
if (info->specific_options & CHK_TS3INIT_COMMAND)
|
||||
{
|
||||
if (header_data.ts3_header->command != info->command)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (info->specific_options & CHK_TS3INIT_SERVER)
|
||||
{
|
||||
struct ts3_init_checked_server_header_data header_data;
|
||||
|
||||
if (!check_server_header(skb, par, &header_data))
|
||||
return false;
|
||||
if (info->specific_options & CHK_TS3INIT_COMMAND)
|
||||
{
|
||||
if (header_data.ts3_header->command != info->command)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
struct udphdr *udp, udp_buf;
|
||||
u64 *signature, signature_buf;
|
||||
|
||||
udp = skb_header_pointer(skb, par->thoff, sizeof(udp_buf), &udp_buf);
|
||||
if (!udp)
|
||||
return false;
|
||||
signature = skb_header_pointer(skb, par->thoff + sizeof(*udp),
|
||||
sizeof(signature_buf), &signature_buf);
|
||||
|
||||
if (!signature || *signature != ts3init_header_tag_signature.tag64)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/*
|
||||
* Validates matchinfo recieved from userspace.
|
||||
*/
|
||||
static int ts3init_check(const struct xt_mtchk_param *par)
|
||||
{
|
||||
struct xt_ts3init_get_puzzle_mtinfo *info = par->matchinfo;
|
||||
|
||||
if (info->common_options & ~(CHK_COMMON_VALID_MASK))
|
||||
{
|
||||
printk(KERN_ERR KBUILD_MODNAME ": invalid (common) options for ts3init\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (info->specific_options & ~(CHK_TS3INIT_VALID_MASK))
|
||||
{
|
||||
printk(KERN_ERR KBUILD_MODNAME ": invalid (specific) options for ts3init\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct xt_match ts3init_mt_reg[] __read_mostly =
|
||||
{
|
||||
@ -309,6 +427,16 @@ static struct xt_match ts3init_mt_reg[] __read_mostly =
|
||||
.checkentry = ts3init_get_puzzle_mt_check,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
{
|
||||
.name = "ts3init",
|
||||
.revision = 0,
|
||||
.family = NFPROTO_UNSPEC,
|
||||
.proto = IPPROTO_UDP,
|
||||
.matchsize = sizeof(struct xt_ts3init_mtinfo),
|
||||
.match = ts3init_mt,
|
||||
.checkentry = ts3init_check,
|
||||
.me = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
int ts3init_match_init(void)
|
||||
|
@ -46,4 +46,20 @@ struct xt_ts3init_get_puzzle_mtinfo
|
||||
char random_seed_path[RANDOM_SEED_PATH_MAX];
|
||||
};
|
||||
|
||||
/* Enums and structs for generic ts3init */
|
||||
enum
|
||||
{
|
||||
CHK_TS3INIT_CLIENT = 1 << 0,
|
||||
CHK_TS3INIT_SERVER = 1 << 1,
|
||||
CHK_TS3INIT_COMMAND = 1 << 2,
|
||||
CHK_TS3INIT_VALID_MASK = (1 << 3) - 1,
|
||||
};
|
||||
|
||||
struct xt_ts3init_mtinfo
|
||||
{
|
||||
__u8 common_options;
|
||||
__u8 specific_options;
|
||||
__u16 reserved1;
|
||||
__u8 command;
|
||||
};
|
||||
#endif /* _TS3INIT_MATCH_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user