diff --git a/extensions/Kbuild b/extensions/Kbuild index e5c01e4..571f3d5 100644 --- a/extensions/Kbuild +++ b/extensions/Kbuild @@ -10,6 +10,7 @@ obj-${build_DELUDE} += xt_DELUDE.o obj-${build_ECHO} += xt_ECHO.o obj-${build_IPMARK} += xt_IPMARK.o obj-${build_LOGMARK} += xt_LOGMARK.o +obj-${build_SYSRQ} += xt_SYSRQ.o obj-${build_TARPIT} += xt_TARPIT.o obj-${build_TEE} += xt_TEE.o obj-${build_condition} += xt_condition.o diff --git a/extensions/Mbuild b/extensions/Mbuild index dbc25b0..9a7fa0e 100644 --- a/extensions/Mbuild +++ b/extensions/Mbuild @@ -3,6 +3,7 @@ obj-${build_DELUDE} += libxt_DELUDE.so obj-${build_ECHO} += libxt_ECHO.so obj-${build_IPMARK} += libxt_IPMARK.so obj-${build_LOGMARK} += libxt_LOGMARK.so +obj-${build_SYSRQ} += libxt_SYSRQ.so obj-${build_TARPIT} += libxt_TARPIT.so obj-${build_TEE} += libxt_TEE.so obj-${build_condition} += libxt_condition.so diff --git a/extensions/libxt_SYSRQ.c b/extensions/libxt_SYSRQ.c new file mode 100644 index 0000000..6041805 --- /dev/null +++ b/extensions/libxt_SYSRQ.c @@ -0,0 +1,50 @@ +/* + * "SYSRQ" target extension to iptables + * this file is in the Public Domain + */ +#include +#include +#include + +static void sysrq_tg_help(void) +{ + printf("SYSRQ takes no options\n\n"); +} + +static int sysrq_tg_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_target **target) +{ + return 0; +} + +static void sysrq_tg_check(unsigned int flags) +{ +} + +static struct xtables_target sysrq_tg4_reg = { + .version = XTABLES_VERSION, + .name = "SYSRQ", + .family = PF_INET, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), + .help = sysrq_tg_help, + .parse = sysrq_tg_parse, + .final_check = sysrq_tg_check, +}; + +static struct xtables_target sysrq_tg6_reg = { + .version = XTABLES_VERSION, + .name = "SYSRQ", + .family = PF_INET6, + .size = XT_ALIGN(0), + .userspacesize = XT_ALIGN(0), + .help = sysrq_tg_help, + .parse = sysrq_tg_parse, + .final_check = sysrq_tg_check, +}; + +static void _init(void) +{ + xtables_register_target(&sysrq_tg4_reg); + xtables_register_target(&sysrq_tg6_reg); +} diff --git a/extensions/libxt_SYSRQ.man b/extensions/libxt_SYSRQ.man new file mode 100644 index 0000000..91c945d --- /dev/null +++ b/extensions/libxt_SYSRQ.man @@ -0,0 +1,47 @@ +The SYSRQ target allows to remotely trigger sysrq on the local machine over the +network. This can be useful when vital parts of the machine hang, for example +an oops in a filesystem causing locks to be not released and processes to get +stuck as a result -- if still possible, use /proc/sysrq-trigger. Even when +processes are stuck, interrupts are likely to be still processed, and as such, +sysrq can be triggered through incoming network packets. +.PP +This xt_SYSRQ implementation does not use any encryption, so you should change +the SYSRQ password after use unless you have made sure it was transmitted +securely and no one sniffed the network, e.g. by use of an IPsec tunnel whose +endpoint is at the machine where you want to trigger the sysrq. Also, you +should limit as to who can issue commands using \fB-s\fP and/or \fB-m mac\fP, +and also that the destination is correct using \fB-d\fP (to protect against +potential broadcast packets), noting that it is still short of MAC/IP spoofing: +.IP +-A INPUT -s 10.10.25.1 -m mac --mac-source aa:bb:cc:dd:ee:ff -d 10.10.25.7 +-p udp --dport 9 -j SYSRQ +.IP +(with IPsec) -A INPUT -s 10.10.25.1 -d 10.10.25.7 -m policy --dir in --pol +ipsec --proto esp --tunnel-src 10.10.25.1 --tunnel-dst 10.10.25.7 +-p udp --dport 9 -j SYSRQ +.PP +This extension does not take any options. The \fB-p udp\fP options are +required. +.PP +The SYSRQ password can be changed through +/sys/module/xt_SYSRQ/parameters/password; note you need to use `echo -n` to +not add a newline to the password, i.e. +.IP +echo -n "password" >/sys/.../password +.PP +Alternatively, the password may be specified at modprobe time, but this is +insecure as people can possible see it through ps(1). You can use an option +line in /etc/modprobe.d/sysrq if it is properly guarded, that is, only readable +by root. +.IP +options xt_SYSRQ password=cookies +.PP +To trigger SYSRQ from a remote host, just use netcat or socat, specifying the +action (only one) as first character, followed by the password: +.IP +echo -n "scookies" | socat stdin udp-sendto:10.10.25.7:9 +.IP +echo -n "scookies" | netcat -u 10.10.25.7 9 +.PP +See the Linux docs for possible sysrq keys. Important ones are: +re(b)oot, power(o)ff, (s)ync filesystems, (u)mount and remount readonly. diff --git a/extensions/xt_SYSRQ.Kconfig b/extensions/xt_SYSRQ.Kconfig new file mode 100644 index 0000000..c92fec6 --- /dev/null +++ b/extensions/xt_SYSRQ.Kconfig @@ -0,0 +1,8 @@ +config NETFILTER_XT_TARGET_SYSRQ + tristate '"SYSRQ" target support' + depends on NETFILTER_XTABLES && NETFILTER_ADVANCED + ---help--- + The SYSRQ target allows to remotely trigger sysrq on the + local machine over the network. This can be useful when vital + parts of the machine hang and sysrq cannot be triggered + through, for example, the shell. diff --git a/extensions/xt_SYSRQ.c b/extensions/xt_SYSRQ.c new file mode 100644 index 0000000..cb12b9f --- /dev/null +++ b/extensions/xt_SYSRQ.c @@ -0,0 +1,158 @@ +/* + * "SYSRQ" target extension for Netfilter + * Copyright © Jan Engelhardt , 2008 + * + * Based upon the ipt_SYSRQ idea by Marek Zalem + * xt_SYSRQ does not use hashing or timestamps. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * version 2 or 3 as published by the Free Software Foundation. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "compat_xtables.h" + +static bool sysrq_once; +static char sysrq_password[64]; +module_param_string(password, sysrq_password, sizeof(sysrq_password), + S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(password, "password for remote sysrq"); + +static unsigned int sysrq_tg(const void *pdata, uint16_t len) +{ + const char *data = pdata; + char c; + + if (*sysrq_password == '\0') { + if (!sysrq_once) + printk(KERN_INFO KBUILD_MODNAME "No password set\n"); + sysrq_once = true; + return NF_DROP; + } + + if (len == 0) + return NF_DROP; + + c = *data; + if (strncmp(&data[1], sysrq_password, len - 1) != 0) { + printk(KERN_INFO KBUILD_MODNAME "Failed attempt - " + "password mismatch\n"); + return NF_DROP; + } + + handle_sysrq(c, NULL); + return NF_ACCEPT; +} + +static unsigned int sysrq_tg4(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknum, + const struct xt_target *target, const void *targinfo) +{ + const struct iphdr *iph; + const struct udphdr *udph; + uint16_t len; + + if (skb_linearize(skb) < 0) + return NF_DROP; + + iph = ip_hdr(skb); + udph = (void *)iph + ip_hdrlen(skb); + len = ntohs(udph->len) - sizeof(struct udphdr); + + printk(KERN_INFO KBUILD_MODNAME ": " NIPQUAD_FMT ":%u -> :%u len=%u\n", + NIPQUAD(iph->saddr), htons(udph->source), htons(udph->dest), + len); + return sysrq_tg((void *)udph + sizeof(struct udphdr), len); +} + +static unsigned int sysrq_tg6(struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, unsigned int hooknum, + const struct xt_target *target, const void *targinfo) +{ + const struct ipv6hdr *iph; + const struct udphdr *udph; + uint16_t len; + + if (skb_linearize(skb) < 0) + return NF_DROP; + + iph = ipv6_hdr(skb); + udph = udp_hdr(skb); + len = ntohs(udph->len) - sizeof(struct udphdr); + + printk(KERN_INFO KBUILD_MODNAME ": " NIP6_FMT ":%hu -> :%hu len=%u\n", + NIP6(iph->saddr), ntohs(udph->source), + ntohs(udph->dest), len); + return sysrq_tg(udph + sizeof(struct udphdr), len); +} + +static bool sysrq_tg_check(const char *table, const void *ventry, + const struct xt_target *target, void *targinfo, unsigned int hook_mask) +{ + if (target->family == PF_INET) { + const struct ipt_entry *entry = ventry; + + if ((entry->ip.proto != IPPROTO_UDP && + entry->ip.proto != IPPROTO_UDPLITE) || + entry->ip.invflags & XT_INV_PROTO) + goto out; + } else if (target->family == PF_INET6) { + const struct ip6t_entry *entry = ventry; + + if ((entry->ipv6.proto != IPPROTO_UDP && + entry->ipv6.proto != IPPROTO_UDPLITE) || + entry->ipv6.invflags & XT_INV_PROTO) + goto out; + } + + return true; + + out: + printk(KERN_ERR KBUILD_MODNAME ": only available for UDP and UDP-Lite"); + return false; +} + +static struct xt_target sysrq_tg_reg[] __read_mostly = { + { + .name = "SYSRQ", + .family = PF_INET, + .revision = 0, + .target = sysrq_tg4, + .checkentry = sysrq_tg_check, + .me = THIS_MODULE, + }, + { + .name = "SYSRQ", + .family = PF_INET6, + .revision = 0, + .target = sysrq_tg6, + .checkentry = sysrq_tg_check, + .me = THIS_MODULE, + }, +}; + +static int __init sysrq_tg_init(void) +{ + return xt_register_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg)); +} + +static void __exit sysrq_tg_exit(void) +{ + return xt_unregister_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg)); +} + +module_init(sysrq_tg_init); +module_exit(sysrq_tg_exit); +MODULE_DESCRIPTION("Xtables: triggering SYSRQ remotely"); +MODULE_AUTHOR("Jan Engelhardt "); +MODULE_LICENSE("GPL"); diff --git a/mconfig b/mconfig index 8d8fcdc..02bc21c 100644 --- a/mconfig +++ b/mconfig @@ -5,6 +5,7 @@ build_DELUDE=m build_ECHO= build_IPMARK=m build_LOGMARK=m +build_SYSRQ=m build_TARPIT=m build_TEE=m build_condition=m