netfilter-devel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
From: Jan Engelhardt <jengelh@inai.de>
To: pablo@netfilter.org
Cc: netfilter-devel@vger.kernel.org
Subject: [PATCH 4/4] netfilter: xtables: inclusion of xt_SYSRQ
Date: Wed, 11 Jul 2012 01:52:30 +0200	[thread overview]
Message-ID: <1341964350-13809-5-git-send-email-jengelh@inai.de> (raw)
In-Reply-To: <1341964350-13809-1-git-send-email-jengelh@inai.de>

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.

Signed-off-by: Jan Engelhardt <jengelh@inai.de>
---
 net/netfilter/Kconfig    |   12 ++
 net/netfilter/Makefile   |    1 +
 net/netfilter/xt_SYSRQ.c |  361 ++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 374 insertions(+), 0 deletions(-)
 create mode 100644 net/netfilter/xt_SYSRQ.c

diff --git a/net/netfilter/Kconfig b/net/netfilter/Kconfig
index c19b214..fbe8e40 100644
--- a/net/netfilter/Kconfig
+++ b/net/netfilter/Kconfig
@@ -644,6 +644,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 destination'
 	depends on NETFILTER_ADVANCED
diff --git a/net/netfilter/Makefile b/net/netfilter/Makefile
index 1c5160f..68881c8 100644
--- a/net/netfilter/Makefile
+++ b/net/netfilter/Makefile
@@ -70,6 +70,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..885b902
--- /dev/null
+++ b/net/netfilter/xt_SYSRQ.c
@@ -0,0 +1,361 @@
+/*
+ *	"SYSRQ" target extension for Xtables
+ *	Copyright © Jan Engelhardt, 2008-2012
+ *
+ *	Based upon the ipt_SYSRQ idea by Marek Zalem <marek [at] terminus sk>
+ *	Security additions John Haxby <john.haxby [at] oracle com>
+ *
+ *	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 "<dstaddr>,<password>".
+ *
+ * For example
+ *
+ *   salt=$RANDOM
+ *   req="s,$(date +%s),$salt"
+ *   echo "$req,$(echo -n $req,10.10.25.1,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);
+	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 * 2) {
+		if (sysrq_debug)
+			pr_info("Short digest, expected %s\n",
+				sysrq_hexdigest);
+		return NF_DROP;
+	}
+	if (strncmp(data + n, sysrq_hexdigest, sysrq_digest_size * 2) != 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]);
+	}
+	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_action_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);
+#ifdef WITH_CRYPTO
+	sprintf(sysrq_digest_password, "%pI4,%s", &iph->daddr, sysrq_password);
+#endif
+	return sysrq_tg((void *)udph + sizeof(struct udphdr), len);
+}
+
+#ifdef WITH_IPV6
+static unsigned int
+sysrq_tg6(struct sk_buff *skb, const struct xt_action_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, NULL) < 0 ||
+	    ipv6_find_hdr(skb, &th_off, IPPROTO_UDPLITE,
+	                  &frag_off, NULL) < 0 ||
+	    frag_off > 0)
+		return NF_DROP;
+
+	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);
+#ifdef WITH_CRYPTO
+	sprintf(sysrq_digest_password, "%pI6,%s", &iph->daddr, sysrq_password);
+#endif
+	return sysrq_tg((void *)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 0;
+
+ out:
+	pr_info("only available for UDP and UDP-Lite");
+	return -EINVAL;
+}
+
+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("xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:255.255.255.255") +
+					sizeof(sysrq_password), GFP_KERNEL);
+	if (sysrq_digest_password == NULL)
+		goto fail;
+	do_gettimeofday(&now);
+	sysrq_seqno = now.tv_sec;
+	return 0;
+
+ 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@inai.de>");
+MODULE_AUTHOR("John Haxby <john.haxby@oracle.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("ipt_SYSRQ");
+MODULE_ALIAS("ip6t_SYSRQ");
-- 
1.7.7

--
To unsubscribe from this list: send the line "unsubscribe netfilter-devel" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

  parent reply	other threads:[~2012-07-10 23:52 UTC|newest]

Thread overview: 18+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-07-10 23:52 xt_recent cleanups, xt_SYSRQ Jan Engelhardt
2012-07-10 23:52 ` [PATCH 1/4] netfilter: xt_recent: remove ip_list_hash_size parameter Jan Engelhardt
2012-07-12 15:42   ` Pablo Neira Ayuso
2012-07-10 23:52 ` [PATCH 2/4] netfilter: cleanup use of the term "IPs" Jan Engelhardt
2012-07-12 15:43   ` Pablo Neira Ayuso
2012-07-10 23:52 ` [PATCH 3/4] netfilter: use permission mnemonics in module_param Jan Engelhardt
2012-07-10 23:52 ` Jan Engelhardt [this message]
2012-07-12 15:49   ` [PATCH 4/4] netfilter: xtables: inclusion of xt_SYSRQ Pablo Neira Ayuso
2012-07-12 16:25     ` Jan Engelhardt
2012-07-12 20:26       ` Florian Westphal
2012-07-12 20:29         ` Jan Engelhardt
2012-07-12 20:35           ` Florian Westphal
2012-07-12 21:25             ` Jan Engelhardt
2012-07-13  9:16       ` Pablo Neira Ayuso
2012-07-14  1:43         ` Maciej Żenczykowski
2012-07-14 13:11           ` Pablo Neira Ayuso
2012-07-14 14:49             ` Aft nix
2012-07-14 15:24               ` Jan Engelhardt

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1341964350-13809-5-git-send-email-jengelh@inai.de \
    --to=jengelh@inai.de \
    --cc=netfilter-devel@vger.kernel.org \
    --cc=pablo@netfilter.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).