[<prev] [next>] [thread-next>] [day] [month] [year] [list]
Message-ID: <4BCEF6B4.8090105@trash.net>
Date: Wed, 21 Apr 2010 14:59:32 +0200
From: Patrick McHardy <kaber@...sh.net>
To: Jan Engelhardt <jengelh@...ozas.de>
CC: netfilter-devel@...r.kernel.org,
Linux Netdev List <netdev@...r.kernel.org>
Subject: Re: [PATCH 1/2] netfilter: xtables: inclusion of xt_SYSRQ
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@...ozas.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@...ozas.de>");
> +MODULE_LICENSE("GPL");
> +MODULE_ALIAS("ipt_SYSRQ");
> +MODULE_ALIAS("ip6t_SYSRQ");
--
To unsubscribe from this list: send the line "unsubscribe netdev" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
Powered by blists - more mailing lists