cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

nf_conntrack_irc.c (7774B)


      1// SPDX-License-Identifier: GPL-2.0-or-later
      2/* IRC extension for IP connection tracking, Version 1.21
      3 * (C) 2000-2002 by Harald Welte <laforge@gnumonks.org>
      4 * based on RR's ip_conntrack_ftp.c
      5 * (C) 2006-2012 Patrick McHardy <kaber@trash.net>
      6 */
      7
      8#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
      9
     10#include <linux/module.h>
     11#include <linux/moduleparam.h>
     12#include <linux/skbuff.h>
     13#include <linux/in.h>
     14#include <linux/ip.h>
     15#include <linux/tcp.h>
     16#include <linux/netfilter.h>
     17#include <linux/slab.h>
     18
     19#include <net/netfilter/nf_conntrack.h>
     20#include <net/netfilter/nf_conntrack_expect.h>
     21#include <net/netfilter/nf_conntrack_helper.h>
     22#include <linux/netfilter/nf_conntrack_irc.h>
     23
     24#define MAX_PORTS 8
     25static unsigned short ports[MAX_PORTS];
     26static unsigned int ports_c;
     27static unsigned int max_dcc_channels = 8;
     28static unsigned int dcc_timeout __read_mostly = 300;
     29/* This is slow, but it's simple. --RR */
     30static char *irc_buffer;
     31static DEFINE_SPINLOCK(irc_buffer_lock);
     32
     33unsigned int (*nf_nat_irc_hook)(struct sk_buff *skb,
     34				enum ip_conntrack_info ctinfo,
     35				unsigned int protoff,
     36				unsigned int matchoff,
     37				unsigned int matchlen,
     38				struct nf_conntrack_expect *exp) __read_mostly;
     39EXPORT_SYMBOL_GPL(nf_nat_irc_hook);
     40
     41#define HELPER_NAME "irc"
     42
     43MODULE_AUTHOR("Harald Welte <laforge@netfilter.org>");
     44MODULE_DESCRIPTION("IRC (DCC) connection tracking helper");
     45MODULE_LICENSE("GPL");
     46MODULE_ALIAS("ip_conntrack_irc");
     47MODULE_ALIAS_NFCT_HELPER(HELPER_NAME);
     48
     49module_param_array(ports, ushort, &ports_c, 0400);
     50MODULE_PARM_DESC(ports, "port numbers of IRC servers");
     51module_param(max_dcc_channels, uint, 0400);
     52MODULE_PARM_DESC(max_dcc_channels, "max number of expected DCC channels per "
     53				   "IRC session");
     54module_param(dcc_timeout, uint, 0400);
     55MODULE_PARM_DESC(dcc_timeout, "timeout on for unestablished DCC channels");
     56
     57static const char *const dccprotos[] = {
     58	"SEND ", "CHAT ", "MOVE ", "TSEND ", "SCHAT "
     59};
     60
     61#define MINMATCHLEN	5
     62
     63/* tries to get the ip_addr and port out of a dcc command
     64 * return value: -1 on failure, 0 on success
     65 *	data		pointer to first byte of DCC command data
     66 *	data_end	pointer to last byte of dcc command data
     67 *	ip		returns parsed ip of dcc command
     68 *	port		returns parsed port of dcc command
     69 *	ad_beg_p	returns pointer to first byte of addr data
     70 *	ad_end_p	returns pointer to last byte of addr data
     71 */
     72static int parse_dcc(char *data, const char *data_end, __be32 *ip,
     73		     u_int16_t *port, char **ad_beg_p, char **ad_end_p)
     74{
     75	char *tmp;
     76
     77	/* at least 12: "AAAAAAAA P\1\n" */
     78	while (*data++ != ' ')
     79		if (data > data_end - 12)
     80			return -1;
     81
     82	/* Make sure we have a newline character within the packet boundaries
     83	 * because simple_strtoul parses until the first invalid character. */
     84	for (tmp = data; tmp <= data_end; tmp++)
     85		if (*tmp == '\n')
     86			break;
     87	if (tmp > data_end || *tmp != '\n')
     88		return -1;
     89
     90	*ad_beg_p = data;
     91	*ip = cpu_to_be32(simple_strtoul(data, &data, 10));
     92
     93	/* skip blanks between ip and port */
     94	while (*data == ' ') {
     95		if (data >= data_end)
     96			return -1;
     97		data++;
     98	}
     99
    100	*port = simple_strtoul(data, &data, 10);
    101	*ad_end_p = data;
    102
    103	return 0;
    104}
    105
    106static int help(struct sk_buff *skb, unsigned int protoff,
    107		struct nf_conn *ct, enum ip_conntrack_info ctinfo)
    108{
    109	unsigned int dataoff;
    110	const struct iphdr *iph;
    111	const struct tcphdr *th;
    112	struct tcphdr _tcph;
    113	const char *data_limit;
    114	char *data, *ib_ptr;
    115	int dir = CTINFO2DIR(ctinfo);
    116	struct nf_conntrack_expect *exp;
    117	struct nf_conntrack_tuple *tuple;
    118	__be32 dcc_ip;
    119	u_int16_t dcc_port;
    120	__be16 port;
    121	int i, ret = NF_ACCEPT;
    122	char *addr_beg_p, *addr_end_p;
    123	typeof(nf_nat_irc_hook) nf_nat_irc;
    124
    125	/* If packet is coming from IRC server */
    126	if (dir == IP_CT_DIR_REPLY)
    127		return NF_ACCEPT;
    128
    129	/* Until there's been traffic both ways, don't look in packets. */
    130	if (ctinfo != IP_CT_ESTABLISHED && ctinfo != IP_CT_ESTABLISHED_REPLY)
    131		return NF_ACCEPT;
    132
    133	/* Not a full tcp header? */
    134	th = skb_header_pointer(skb, protoff, sizeof(_tcph), &_tcph);
    135	if (th == NULL)
    136		return NF_ACCEPT;
    137
    138	/* No data? */
    139	dataoff = protoff + th->doff*4;
    140	if (dataoff >= skb->len)
    141		return NF_ACCEPT;
    142
    143	spin_lock_bh(&irc_buffer_lock);
    144	ib_ptr = skb_header_pointer(skb, dataoff, skb->len - dataoff,
    145				    irc_buffer);
    146	if (!ib_ptr) {
    147		spin_unlock_bh(&irc_buffer_lock);
    148		return NF_ACCEPT;
    149	}
    150
    151	data = ib_ptr;
    152	data_limit = ib_ptr + skb->len - dataoff;
    153
    154	/* strlen("\1DCC SENT t AAAAAAAA P\1\n")=24
    155	 * 5+MINMATCHLEN+strlen("t AAAAAAAA P\1\n")=14 */
    156	while (data < data_limit - (19 + MINMATCHLEN)) {
    157		if (memcmp(data, "\1DCC ", 5)) {
    158			data++;
    159			continue;
    160		}
    161		data += 5;
    162		/* we have at least (19+MINMATCHLEN)-5 bytes valid data left */
    163
    164		iph = ip_hdr(skb);
    165		pr_debug("DCC found in master %pI4:%u %pI4:%u\n",
    166			 &iph->saddr, ntohs(th->source),
    167			 &iph->daddr, ntohs(th->dest));
    168
    169		for (i = 0; i < ARRAY_SIZE(dccprotos); i++) {
    170			if (memcmp(data, dccprotos[i], strlen(dccprotos[i]))) {
    171				/* no match */
    172				continue;
    173			}
    174			data += strlen(dccprotos[i]);
    175			pr_debug("DCC %s detected\n", dccprotos[i]);
    176
    177			/* we have at least
    178			 * (19+MINMATCHLEN)-5-dccprotos[i].matchlen bytes valid
    179			 * data left (== 14/13 bytes) */
    180			if (parse_dcc(data, data_limit, &dcc_ip,
    181				       &dcc_port, &addr_beg_p, &addr_end_p)) {
    182				pr_debug("unable to parse dcc command\n");
    183				continue;
    184			}
    185
    186			pr_debug("DCC bound ip/port: %pI4:%u\n",
    187				 &dcc_ip, dcc_port);
    188
    189			/* dcc_ip can be the internal OR external (NAT'ed) IP */
    190			tuple = &ct->tuplehash[dir].tuple;
    191			if (tuple->src.u3.ip != dcc_ip &&
    192			    tuple->dst.u3.ip != dcc_ip) {
    193				net_warn_ratelimited("Forged DCC command from %pI4: %pI4:%u\n",
    194						     &tuple->src.u3.ip,
    195						     &dcc_ip, dcc_port);
    196				continue;
    197			}
    198
    199			exp = nf_ct_expect_alloc(ct);
    200			if (exp == NULL) {
    201				nf_ct_helper_log(skb, ct,
    202						 "cannot alloc expectation");
    203				ret = NF_DROP;
    204				goto out;
    205			}
    206			tuple = &ct->tuplehash[!dir].tuple;
    207			port = htons(dcc_port);
    208			nf_ct_expect_init(exp, NF_CT_EXPECT_CLASS_DEFAULT,
    209					  tuple->src.l3num,
    210					  NULL, &tuple->dst.u3,
    211					  IPPROTO_TCP, NULL, &port);
    212
    213			nf_nat_irc = rcu_dereference(nf_nat_irc_hook);
    214			if (nf_nat_irc && ct->status & IPS_NAT_MASK)
    215				ret = nf_nat_irc(skb, ctinfo, protoff,
    216						 addr_beg_p - ib_ptr,
    217						 addr_end_p - addr_beg_p,
    218						 exp);
    219			else if (nf_ct_expect_related(exp, 0) != 0) {
    220				nf_ct_helper_log(skb, ct,
    221						 "cannot add expectation");
    222				ret = NF_DROP;
    223			}
    224			nf_ct_expect_put(exp);
    225			goto out;
    226		}
    227	}
    228 out:
    229	spin_unlock_bh(&irc_buffer_lock);
    230	return ret;
    231}
    232
    233static struct nf_conntrack_helper irc[MAX_PORTS] __read_mostly;
    234static struct nf_conntrack_expect_policy irc_exp_policy;
    235
    236static int __init nf_conntrack_irc_init(void)
    237{
    238	int i, ret;
    239
    240	if (max_dcc_channels < 1) {
    241		pr_err("max_dcc_channels must not be zero\n");
    242		return -EINVAL;
    243	}
    244
    245	if (max_dcc_channels > NF_CT_EXPECT_MAX_CNT) {
    246		pr_err("max_dcc_channels must not be more than %u\n",
    247		       NF_CT_EXPECT_MAX_CNT);
    248		return -EINVAL;
    249	}
    250
    251	irc_exp_policy.max_expected = max_dcc_channels;
    252	irc_exp_policy.timeout = dcc_timeout;
    253
    254	irc_buffer = kmalloc(65536, GFP_KERNEL);
    255	if (!irc_buffer)
    256		return -ENOMEM;
    257
    258	/* If no port given, default to standard irc port */
    259	if (ports_c == 0)
    260		ports[ports_c++] = IRC_PORT;
    261
    262	for (i = 0; i < ports_c; i++) {
    263		nf_ct_helper_init(&irc[i], AF_INET, IPPROTO_TCP, HELPER_NAME,
    264				  IRC_PORT, ports[i], i, &irc_exp_policy,
    265				  0, help, NULL, THIS_MODULE);
    266	}
    267
    268	ret = nf_conntrack_helpers_register(&irc[0], ports_c);
    269	if (ret) {
    270		pr_err("failed to register helpers\n");
    271		kfree(irc_buffer);
    272		return ret;
    273	}
    274
    275	return 0;
    276}
    277
    278static void __exit nf_conntrack_irc_fini(void)
    279{
    280	nf_conntrack_helpers_unregister(irc, ports_c);
    281	kfree(irc_buffer);
    282}
    283
    284module_init(nf_conntrack_irc_init);
    285module_exit(nf_conntrack_irc_fini);