Jan Engelhardt wrote: > The SYSRQ target will allow to remotely invoke sysrq on the local > machine. Authentication is by means of a pre-shared key that can > either be transmitted plaintext or digest-secured. I really think this is pushing what netfilter is meant for a bit far. Its basically abusing the firewall ruleset to offer a network service. I can see that its useful to have this in the kernel instead of userspace, but why isn't this implemented as a stand-alone module? That seems like a better design to me and also makes it more useful by not depending on netfilter. > Signed-off-by: Jan Engelhardt <jengelh@xxxxxxxxxx> > --- > net/netfilter/Kconfig | 12 ++ > net/netfilter/Makefile | 1 + > net/netfilter/xt_SYSRQ.c | 354 ++++++++++++++++++++++++++++++++++++++++++++++ > 3 files changed, 367 insertions(+), 0 deletions(-) > create mode 100644 net/netfilter/xt_SYSRQ.c > > diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig > index 673a6c8..bfd9b6f 100644 > --- a/net/netfilter/Kconfig > +++ b/net/netfilter/Kconfig > @@ -502,6 +502,18 @@ config NETFILTER_XT_TARGET_RATEEST > > To compile it as a module, choose M here. If unsure, say N. > > +config NETFILTER_XT_TARGET_SYSRQ > + tristate '"SYSRQ" - remote sysrq invocation' > + depends on NETFILTER_ADVANCED > + ---help--- > + This option enables the "SYSRQ" target which can be used to trigger > + sysrq from a remote machine using a magic UDP packet with a pre-shared > + password. This is useful when the receiving host has locked up in an > + Oops yet still can process incoming packets. > + > + Besides plaintext packets, digest-secured SYSRQ requests will be > + supported when CONFIG_CRYPTO is enabled. > + > config NETFILTER_XT_TARGET_TEE > tristate '"TEE" - packet cloning to alternate destiantion' > depends on NETFILTER_ADVANCED > diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile > index 14e3a8f..f032195 100644 > --- a/net/netfilter/Makefile > +++ b/net/netfilter/Makefile > @@ -56,6 +56,7 @@ obj-$(CONFIG_NETFILTER_XT_TARGET_NFQUEUE) += xt_NFQUEUE.o > obj-$(CONFIG_NETFILTER_XT_TARGET_NOTRACK) += xt_NOTRACK.o > obj-$(CONFIG_NETFILTER_XT_TARGET_RATEEST) += xt_RATEEST.o > obj-$(CONFIG_NETFILTER_XT_TARGET_SECMARK) += xt_SECMARK.o > +obj-$(CONFIG_NETFILTER_XT_TARGET_SYSRQ) += xt_SYSRQ.o > obj-$(CONFIG_NETFILTER_XT_TARGET_TPROXY) += xt_TPROXY.o > obj-$(CONFIG_NETFILTER_XT_TARGET_TCPMSS) += xt_TCPMSS.o > obj-$(CONFIG_NETFILTER_XT_TARGET_TCPOPTSTRIP) += xt_TCPOPTSTRIP.o > diff --git a/net/netfilter/xt_SYSRQ.c b/net/netfilter/xt_SYSRQ.c > new file mode 100644 > index 0000000..929b204 > --- /dev/null > +++ b/net/netfilter/xt_SYSRQ.c > @@ -0,0 +1,354 @@ > +/* > + * "SYSRQ" target extension for Netfilter > + * Copyright © Jan Engelhardt <jengelh [at] medozas de>, 2008 - 2010 > + * > + * Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk> > + * > + * 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 later as published by the Free Software Foundation. > + */ > +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt > +#include <linux/in.h> > +#include <linux/ip.h> > +#include <linux/ipv6.h> > +#include <linux/module.h> > +#include <linux/skbuff.h> > +#include <linux/sysrq.h> > +#include <linux/udp.h> > +#include <linux/netfilter_ipv4/ip_tables.h> > +#include <linux/netfilter_ipv6/ip6_tables.h> > +#include <linux/netfilter/x_tables.h> > +#include <linux/crypto.h> > +#include <linux/scatterlist.h> > +#include <net/ip.h> > + > +#if defined(CONFIG_CRYPTO) || defined(CRYPTO_CONFIG_MODULE) > +# define WITH_CRYPTO 1 > +#endif > +#if defined(CONFIG_IPV6) || defined(CONFIG_IPV6_MODULE) > +# define WITH_IPV6 1 > +#endif > + > +static bool sysrq_once; > +static char sysrq_password[64]; > +static char sysrq_hash[16] = "sha1"; > +static long sysrq_seqno; > +static int sysrq_debug; > +module_param_string(password, sysrq_password, sizeof(sysrq_password), > + S_IRUSR | S_IWUSR); > +module_param_string(hash, sysrq_hash, sizeof(sysrq_hash), S_IRUSR); > +module_param_named(seqno, sysrq_seqno, long, S_IRUSR | S_IWUSR); > +module_param_named(debug, sysrq_debug, int, S_IRUSR | S_IWUSR); > +MODULE_PARM_DESC(password, "password for remote sysrq"); > +MODULE_PARM_DESC(hash, "hash algorithm, default sha1"); > +MODULE_PARM_DESC(seqno, "sequence number for remote sysrq"); > +MODULE_PARM_DESC(debug, "debugging: 0=off, 1=on"); > + > +#ifdef WITH_CRYPTO > +static struct crypto_hash *sysrq_tfm; > +static int sysrq_digest_size; > +static unsigned char *sysrq_digest_password; > +static unsigned char *sysrq_digest; > +static char *sysrq_hexdigest; > + > +/* > + * The data is of the form "<requests>,<seqno>,<salt>,<hash>" where <requests> > + * is a series of sysrq requests; <seqno> is a sequence number that must be > + * greater than the last sequence number; <salt> is some random bytes; and > + * <hash> is the hash of everything up to and including the preceding "," > + * together with the password. > + * > + * For example > + * > + * salt=$RANDOM > + * req="s,$(date +%s),$salt" > + * echo "$req,$(echo -n $req,secret | sha1sum | cut -c1-40)" > + * > + * You will want a better salt and password than that though :-) > + */ > +static unsigned int sysrq_tg(const void *pdata, uint16_t len) > +{ > + const char *data = pdata; > + int i, n; > + struct scatterlist sg[2]; > + struct hash_desc desc; > + int ret; > + long new_seqno = 0; > + > + if (*sysrq_password == '\0') { > + if (!sysrq_once) > + pr_info("No password set\n"); > + sysrq_once = true; > + return NF_DROP; > + } > + if (len == 0) > + return NF_DROP; > + > + for (i = 0; sysrq_password[i] != '\0' && > + sysrq_password[i] != '\n'; ++i) > + /* loop */; > + sysrq_password[i] = '\0'; > + > + i = 0; > + for (n = 0; n < len - 1; ++n) { > + if (i == 1 && '0' <= data[n] && data[n] <= '9') > + new_seqno = 10L * new_seqno + data[n] - '0'; > + if (data[n] == ',' && ++i == 3) > + break; > + } > + ++n; > + if (i != 3) { > + if (sysrq_debug) > + pr_info("badly formatted request\n"); > + return NF_DROP; > + } > + if (sysrq_seqno >= new_seqno) { > + if (sysrq_debug) > + pr_info("old sequence number ignored\n"); > + return NF_DROP; > + } > + > + desc.tfm = sysrq_tfm; > + desc.flags = 0; > + ret = crypto_hash_init(&desc); > + if (ret != 0) > + goto hash_fail; > + sg_init_table(sg, 2); > + sg_set_buf(&sg[0], data, n); > + strcpy(sysrq_digest_password, sysrq_password); > + i = strlen(sysrq_digest_password); > + sg_set_buf(&sg[1], sysrq_digest_password, i); > + ret = crypto_hash_digest(&desc, sg, n + i, sysrq_digest); > + if (ret != 0) > + goto hash_fail; > + > + for (i = 0; i < sysrq_digest_size; ++i) { > + sysrq_hexdigest[2*i] = > + "0123456789abcdef"[(sysrq_digest[i] >> 4) & 0xf]; > + sysrq_hexdigest[2*i+1] = > + "0123456789abcdef"[sysrq_digest[i] & 0xf]; > + } > + sysrq_hexdigest[2*sysrq_digest_size] = '\0'; > + if (len - n < sysrq_digest_size) { > + if (sysrq_debug) > + pr_info("Short digest, expected %s\n", > + sysrq_hexdigest); > + return NF_DROP; > + } > + if (strncmp(data + n, sysrq_hexdigest, sysrq_digest_size) != 0) { > + if (sysrq_debug) > + pr_info("Bad digest, expected %s\n", sysrq_hexdigest); > + return NF_DROP; > + } > + > + /* Now we trust the requester */ > + sysrq_seqno = new_seqno; > + for (i = 0; i < len && data[i] != ','; ++i) { > + pr_info("SysRq %c\n", data[i]); > + handle_sysrq(data[i], NULL); > + } > + return NF_ACCEPT; > + > + hash_fail: > + pr_warning("digest failure\n"); > + return NF_DROP; > +} > +#else > +static unsigned int sysrq_tg(const void *pdata, uint16_t len) > +{ > + const char *data = pdata; > + char c; > + > + if (*sysrq_password == '\0') { > + if (!sysrq_once) > + pr_info("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) { > + pr_warning("Failed attempt - password mismatch\n"); > + return NF_DROP; > + } > + > + handle_sysrq(c, NULL); > + return NF_ACCEPT; > +} > +#endif > + > +static unsigned int > +sysrq_tg4(struct sk_buff *skb, const struct xt_target_param *par) > +{ > + const struct iphdr *iph; > + const struct udphdr *udph; > + uint16_t len; > + > + if (skb_linearize(skb) < 0) > + return NF_DROP; > + > + iph = ip_hdr(skb); > + if (iph->protocol != IPPROTO_UDP && iph->protocol != IPPROTO_UDPLITE) > + return NF_DROP; > + > + udph = (const void *)iph + ip_hdrlen(skb); > + len = ntohs(udph->len) - sizeof(struct udphdr); > + > + if (sysrq_debug) > + pr_info(": %pI4:%u -> :%u len=%u\n", &iph->saddr, > + htons(udph->source), htons(udph->dest), len); > + return sysrq_tg((void *)udph + sizeof(struct udphdr), len); > +} > + > +#ifdef WITH_IPV6 > +static unsigned int > +sysrq_tg6(struct sk_buff *skb, const struct xt_target_param *par) > +{ > + const struct ipv6hdr *iph; > + const struct udphdr *udph; > + unsigned short frag_off; > + unsigned int th_off; > + uint16_t len; > + > + if (skb_linearize(skb) < 0) > + return NF_DROP; > + > + iph = ipv6_hdr(skb); > + if (ipv6_find_hdr(skb, &th_off, IPPROTO_UDP, &frag_off) < 0 || > + frag_off > 0) > + return NF_ACCEPT; /* sink it */ > + > + udph = (const void *)iph + th_off; > + len = ntohs(udph->len) - sizeof(struct udphdr); > + > + if (sysrq_debug) > + pr_info("%pI6:%hu -> :%hu len=%u\n", &iph->saddr, > + ntohs(udph->source), ntohs(udph->dest), len); > + return sysrq_tg(udph + sizeof(struct udphdr), len); > +} > +#endif > + > +static int sysrq_tg_check(const struct xt_tgchk_param *par) > +{ > + if (par->target->family == NFPROTO_IPV4) { > + const struct ipt_entry *entry = par->entryinfo; > + > + if ((entry->ip.proto != IPPROTO_UDP && > + entry->ip.proto != IPPROTO_UDPLITE) || > + entry->ip.invflags & XT_INV_PROTO) > + goto out; > + } else if (par->target->family == NFPROTO_IPV6) { > + const struct ip6t_entry *entry = par->entryinfo; > + > + if ((entry->ipv6.proto != IPPROTO_UDP && > + entry->ipv6.proto != IPPROTO_UDPLITE) || > + entry->ipv6.invflags & XT_INV_PROTO) > + goto out; > + } > + > + return true; > + > + out: > + pr_info("only available for UDP and UDP-Lite"); > + return false; > +} > + > +static struct xt_target sysrq_tg_reg[] __read_mostly = { > + { > + .name = "SYSRQ", > + .revision = 1, > + .family = NFPROTO_IPV4, > + .target = sysrq_tg4, > + .checkentry = sysrq_tg_check, > + .me = THIS_MODULE, > + }, > +#ifdef WITH_IPV6 > + { > + .name = "SYSRQ", > + .revision = 1, > + .family = NFPROTO_IPV6, > + .target = sysrq_tg6, > + .checkentry = sysrq_tg_check, > + .me = THIS_MODULE, > + }, > +#endif > +}; > + > +static void sysrq_crypto_exit(void) > +{ > +#ifdef WITH_CRYPTO > + if (sysrq_tfm) > + crypto_free_hash(sysrq_tfm); > + if (sysrq_digest) > + kfree(sysrq_digest); > + if (sysrq_hexdigest) > + kfree(sysrq_hexdigest); > + if (sysrq_digest_password) > + kfree(sysrq_digest_password); > +#endif > +} > + > +static int __init sysrq_crypto_init(void) > +{ > +#if defined(WITH_CRYPTO) > + struct timeval now; > + int ret; > + > + sysrq_tfm = crypto_alloc_hash(sysrq_hash, 0, CRYPTO_ALG_ASYNC); > + if (IS_ERR(sysrq_tfm)) { > + pr_err("Could not find or load %s hash\n", sysrq_hash); > + sysrq_tfm = NULL; > + ret = PTR_ERR(sysrq_tfm); > + goto fail; > + } > + sysrq_digest_size = crypto_hash_digestsize(sysrq_tfm); > + sysrq_digest = kmalloc(sysrq_digest_size, GFP_KERNEL); > + ret = -ENOMEM; > + if (sysrq_digest == NULL) > + goto fail; > + sysrq_hexdigest = kmalloc(2 * sysrq_digest_size + 1, GFP_KERNEL); > + if (sysrq_hexdigest == NULL) > + goto fail; > + sysrq_digest_password = kmalloc(sizeof(sysrq_password), GFP_KERNEL); > + if (sysrq_digest_password == NULL) > + goto fail; > + do_gettimeofday(&now); > + sysrq_seqno = now.tv_sec; > + ret = xt_register_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg)); > + if (ret < 0) > + goto fail; > + return ret; > + > + fail: > + sysrq_crypto_exit(); > + return ret; > +#else > + pr_info("compiled without crypto\n"); > +#endif > + return -EINVAL; > +} > + > +static int __init sysrq_tg_init(void) > +{ > + if (sysrq_crypto_init() < 0) > + pr_info("starting without crypto\n"); > + return xt_register_targets(sysrq_tg_reg, ARRAY_SIZE(sysrq_tg_reg)); > +} > + > +static void __exit sysrq_tg_exit(void) > +{ > + sysrq_crypto_exit(); > + 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 <jengelh@xxxxxxxxxx>"); > +MODULE_LICENSE("GPL"); > +MODULE_ALIAS("ipt_SYSRQ"); > +MODULE_ALIAS("ip6t_SYSRQ"); -- To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html