From 808a25129605c9e7de9f86fb8d5a5ed3310edd43 Mon Sep 17 00:00:00 2001 From: Pablo Neira Ayuso Date: Mon, 7 Oct 2013 14:41:20 +0200 Subject: conntrackd: cthelper: add amanda helper This patch adds a userspace port of the amanda helper that is currently implemented in the kernel. Signed-off-by: Pablo Neira Ayuso --- src/helpers/Makefile.am | 7 +- src/helpers/amanda.c | 203 ++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 209 insertions(+), 1 deletion(-) create mode 100644 src/helpers/amanda.c (limited to 'src/helpers') diff --git a/src/helpers/Makefile.am b/src/helpers/Makefile.am index 216a5a7..fe28e83 100644 --- a/src/helpers/Makefile.am +++ b/src/helpers/Makefile.am @@ -1,12 +1,17 @@ include $(top_srcdir)/Make_global.am -pkglib_LTLIBRARIES = ct_helper_dhcpv6.la \ +pkglib_LTLIBRARIES = ct_helper_amanda.la \ + ct_helper_dhcpv6.la \ ct_helper_ftp.la \ ct_helper_rpc.la \ ct_helper_tftp.la \ ct_helper_tns.la \ ct_helper_sane.la +ct_helper_amanda_la_SOURCES = amanda.c +ct_helper_amanda_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) +ct_helper_amanda_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) + ct_helper_dhcpv6_la_SOURCES = dhcpv6.c ct_helper_dhcpv6_la_LDFLAGS = -avoid-version -module $(LIBNETFILTER_CONNTRACK_LIBS) ct_helper_dhcpv6_la_CFLAGS = $(AM_CFLAGS) $(LIBNETFILTER_CONNTRACK_CFLAGS) diff --git a/src/helpers/amanda.c b/src/helpers/amanda.c new file mode 100644 index 0000000..c0cf701 --- /dev/null +++ b/src/helpers/amanda.c @@ -0,0 +1,203 @@ +/* + * (C) 2013 by Pablo Neira Ayuso + * + * Adapted from: + * + * Amanda extension for IP connection tracking + * + * (C) 2002 by Brian J. Murrell + * based on HW's ip_conntrack_irc.c as well as other modules + * (C) 2006 Patrick McHardy + * + * 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. + */ +#include "conntrackd.h" +#include "helper.h" +#include "myct.h" +#include "log.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static int nat_amanda(struct pkt_buff *pkt, uint32_t ctinfo, + unsigned int matchoff, unsigned int matchlen, + struct nf_expect *exp) +{ + char buffer[sizeof("65535")]; + u_int16_t port, initial_port; + unsigned int ret; + const struct nf_conntrack *expected; + struct nf_conntrack *nat_tuple; + + nat_tuple = nfct_new(); + if (nat_tuple == NULL) + return NF_ACCEPT; + + expected = nfexp_get_attr(exp, ATTR_EXP_EXPECTED); + + /* Connection comes from client. */ + initial_port = nfct_get_attr_u16(expected, ATTR_PORT_DST); + nfexp_set_attr_u32(exp, ATTR_EXP_NAT_DIR, IP_CT_DIR_ORIGINAL); + + /* libnetfilter_conntrack needs this */ + nfct_set_attr_u8(nat_tuple, ATTR_L3PROTO, AF_INET); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_SRC, 0); + nfct_set_attr_u32(nat_tuple, ATTR_IPV4_DST, 0); + nfct_set_attr_u8(nat_tuple, ATTR_L4PROTO, IPPROTO_TCP); + nfct_set_attr_u16(nat_tuple, ATTR_PORT_DST, 0); + + /* When you see the packet, we need to NAT it the same as the + * this one (ie. same IP: it will be TCP and master is UDP). */ + nfexp_set_attr(exp, ATTR_EXP_FN, "nat-follow-master"); + + /* Try to get same port: if not, try to change it. */ + for (port = ntohs(initial_port); port != 0; port++) { + int res; + + nfct_set_attr_u16(nat_tuple, ATTR_PORT_SRC, htons(port)); + nfexp_set_attr(exp, ATTR_EXP_NAT_TUPLE, nat_tuple); + + res = cthelper_add_expect(exp); + if (res == 0) + break; + else if (res != -EBUSY) { + port = 0; + break; + } + } + + if (port == 0) { + pr_debug("all ports in use\n"); + return NF_DROP; + } + + sprintf(buffer, "%u", port); + ret = nfq_udp_mangle_ipv4(pkt, matchoff, matchlen, buffer, + strlen(buffer)); + if (ret != NF_ACCEPT) { + pr_debug("cannot mangle packet\n"); + cthelper_del_expect(exp); + } + return ret; +} + +static char amanda_buffer[65536]; +static unsigned int master_timeout = 300; + +enum amanda_strings { + SEARCH_CONNECT, + SEARCH_NEWLINE, + SEARCH_DATA, + SEARCH_MESG, + SEARCH_INDEX, +}; + +static const char *conns[] = { "DATA ", "MESG ", "INDEX " }; + +static int +amanda_helper_cb(struct pkt_buff *pkt, uint32_t protoff, + struct myct *myct, uint32_t ctinfo) +{ + struct nf_expect *exp; + char *data, *data_limit, *tmp; + unsigned int dataoff, i; + u_int16_t port, len; + int ret = NF_ACCEPT; + struct iphdr *iph; + union nfct_attr_grp_addr saddr, daddr; + + /* Only look at packets from the Amanda server */ + if (CTINFO2DIR(ctinfo) == IP_CT_DIR_ORIGINAL) + return NF_ACCEPT; + + /* increase the UDP timeout of the master connection as replies from + * Amanda clients to the server can be quite delayed */ + nfct_set_attr_u32(myct->ct, ATTR_TIMEOUT, master_timeout); + + /* No data? */ + iph = (struct iphdr *)pktb_network_header(pkt); + dataoff = iph->ihl*4 + sizeof(struct udphdr); + if (dataoff >= pktb_len(pkt)) { + pr_debug("amanda_help: pktlen = %u\n", pktb_len(pkt)); + return NF_ACCEPT; + } + + memcpy(amanda_buffer, pktb_network_header(pkt) + dataoff, + pktb_len(pkt) - dataoff); + data = amanda_buffer; + data_limit = amanda_buffer + pktb_len(pkt) - dataoff; + *data_limit = '\0'; + + /* Search for the CONNECT string */ + data = strstr(data, "CONNECT "); + if (!data) + goto out; + data += strlen("CONNECT "); + + /* Only search first line. */ + if ((tmp = strchr(data, '\n'))) + *tmp = '\0'; + + for (i = 0; i < ARRAY_SIZE(conns); i++) { + char *match = strstr(data, conns[i]); + if (!match) + continue; + tmp = data = match + strlen(conns[i]); + port = strtoul(data, &data, 10); + len = data - tmp; + if (port == 0 || len > 5) + break; + + exp = nfexp_new(); + if (exp == NULL) + return NF_DROP; + + cthelper_get_addr_src(myct->ct, MYCT_DIR_ORIG, &saddr); + cthelper_get_addr_dst(myct->ct, MYCT_DIR_ORIG, &daddr); + cthelper_get_port_src(myct->ct, MYCT_DIR_ORIG, &port); + + if (cthelper_expect_init(exp, myct->ct, 0, &saddr, &daddr, + IPPROTO_TCP, NULL, &port, 0)) { + nfexp_destroy(exp); + return NF_DROP; + } + + if (nfct_get_attr_u32(myct->ct, ATTR_STATUS) & IPS_NAT_MASK) { + ret = nat_amanda(pkt, ctinfo, tmp - amanda_buffer, + len, exp); + } else + myct->exp = exp; + } +out: + return ret; +} + +static struct ctd_helper amanda_helper = { + .name = "amanda", + .l4proto = IPPROTO_UDP, + .cb = amanda_helper_cb, + .policy = { + [0] = { + .name = "amanda", + .expect_max = ARRAY_SIZE(conns), + .expect_timeout = 180, + }, + }, +}; + +void __attribute__ ((constructor)) amanda_init(void); + +void amanda_init(void) +{ + helper_register(&amanda_helper); +} -- cgit v1.2.3