All of lore.kernel.org
 help / color / mirror / Atom feed
From: Patrick McHardy <kaber@trash.net>
To: Jan Engelhardt <jengelh@medozas.de>
Cc: netfilter-devel@vger.kernel.org,
	Linux Netdev List <netdev@vger.kernel.org>
Subject: Re: [PATCH 1/2] netfilter: xtables: inclusion of xt_SYSRQ
Date: Wed, 21 Apr 2010 14:59:32 +0200	[thread overview]
Message-ID: <4BCEF6B4.8090105@trash.net> (raw)
In-Reply-To: <1271845618-28569-2-git-send-email-jengelh@medozas.de>

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@medozas.de>
> ---
>  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@medozas.de>");
> +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@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

  reply	other threads:[~2010-04-21 12:59 UTC|newest]

Thread overview: 15+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2010-04-21 10:26 nf-next: sysrq and condition 20100421 Jan Engelhardt
2010-04-21 10:26 ` [PATCH 1/2] netfilter: xtables: inclusion of xt_SYSRQ Jan Engelhardt
2010-04-21 12:59   ` Patrick McHardy [this message]
2010-04-21 13:07     ` Jan Engelhardt
2010-04-21 13:17       ` Patrick McHardy
2010-04-21 13:35         ` Jan Engelhardt
2010-04-28 14:43           ` John Haxby
2010-04-28 14:54             ` John Haxby
2010-04-28 15:03               ` Jan Engelhardt
2010-04-28 15:50                 ` John Haxby
2010-07-25 16:49                 ` Jan Engelhardt
2010-07-25 18:13                   ` John Haxby
2012-01-05 13:19     ` Shan Wei
2010-04-21 10:26 ` [PATCH 2/2] netfilter: xtables: inclusion of xt_condition Jan Engelhardt
2010-04-21 13:07   ` Patrick McHardy

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=4BCEF6B4.8090105@trash.net \
    --to=kaber@trash.net \
    --cc=jengelh@medozas.de \
    --cc=netdev@vger.kernel.org \
    --cc=netfilter-devel@vger.kernel.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 an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.