Browse Source

Added ts3init match.

pull/1/head
Maximilian Münchow 8 years ago
parent
commit
bc4e578d9a
  1. 17
      README.md
  2. 2
      src/Makefile.xtables
  3. 146
      src/libxt_ts3init.c
  4. 14
      src/ts3init_header.h
  5. 190
      src/ts3init_match.c
  6. 16
      src/ts3init_match.h

17
README.md

@ -86,6 +86,23 @@ ts3init_get_puzzle match options: @@ -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
=================

2
src/Makefile.xtables

@ -1,5 +1,5 @@ @@ -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

@ -0,0 +1,146 @@ @@ -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));
}

14
src/ts3init_header.h

@ -22,7 +22,7 @@ struct ts3_init_header_tag @@ -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 @@ -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;
};
/*

190
src/ts3init_match.c

@ -31,35 +31,39 @@ static const struct ts3_init_header_tag ts3init_header_tag_signature = @@ -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 @@ -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 @@ -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->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);
header_data->udp = udp;
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 @@ -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) @@ -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 @@ -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) @@ -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 = @@ -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)

16
src/ts3init_match.h

@ -46,4 +46,20 @@ struct xt_ts3init_get_puzzle_mtinfo @@ -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…
Cancel
Save