diff --git a/extensions/Kbuild b/extensions/Kbuild index a88dff5..9a06eb4 100644 --- a/extensions/Kbuild +++ b/extensions/Kbuild @@ -11,6 +11,7 @@ obj-${build_ECHO} += xt_ECHO.o obj-${build_LOGMARK} += xt_LOGMARK.o obj-${build_TARPIT} += xt_TARPIT.o obj-${build_TEE} += xt_TEE.o +obj-${build_condition} += xt_condition.o obj-${build_geoip} += xt_geoip.o obj-${build_portscan} += xt_portscan.o diff --git a/extensions/Mbuild b/extensions/Mbuild index 336c3f2..c0060a4 100644 --- a/extensions/Mbuild +++ b/extensions/Mbuild @@ -4,5 +4,6 @@ obj-${build_ECHO} += libxt_ECHO.so obj-${build_LOGMARK} += libxt_LOGMARK.so obj-${build_TARPIT} += libxt_TARPIT.so obj-${build_TEE} += libxt_TEE.so +obj-${build_condition} += libxt_condition.so obj-${build_geoip} += libxt_geoip.so obj-${build_portscan} += libxt_portscan.so diff --git a/extensions/libxt_condition.c b/extensions/libxt_condition.c new file mode 100644 index 0000000..71ad6dc --- /dev/null +++ b/extensions/libxt_condition.c @@ -0,0 +1,105 @@ +/* Shared library add-on to iptables for condition match */ +#include +#include +#include +#include +#include +#include +#include +#include "xt_condition.h" + +static void condition_help(void) +{ + printf( +"condition match options:\n" +"[!] --condition name Match on boolean value stored in procfs file\n" +); +} + +static const struct option condition_opts[] = { + {.name = "condition", .has_arg = true, .val = 'X'}, + {NULL}, +}; + +static int condition_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct xt_condition_mtinfo *info = (void *)(*match)->data; + + if (c == 'X') { + if (*flags) + exit_error(PARAMETER_PROBLEM, + "Can't specify multiple conditions"); + + if (strlen(optarg) < sizeof(info->name)) + strcpy(info->name, optarg); + else + exit_error(PARAMETER_PROBLEM, + "File name too long"); + + info->invert = invert; + *flags = 1; + return true; + } + + return false; +} + +static void condition_check(unsigned int flags) +{ + if (flags == 0) + exit_error(PARAMETER_PROBLEM, + "Condition match: must specify --condition"); +} + +static void condition_print(const void *ip, const struct xt_entry_match *match, + int numeric) +{ + const struct xt_condition_mtinfo *info = (const void *)match->data; + + printf("condition %s%s ", (info->invert) ? "!" : "", info->name); +} + + +static void condition_save(const void *ip, const struct xt_entry_match *match) +{ + const struct xt_condition_mtinfo *info = (const void *)match->data; + + printf("--condition %s\"%s\" ", (info->invert) ? "! " : "", info->name); +} + +static struct xtables_match condition_mt4_reg = { + .name = "condition", + .revision = 0, + .family = PF_INET, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_condition_mtinfo)), + .userspacesize = XT_ALIGN(sizeof(struct xt_condition_mtinfo)), + .help = condition_help, + .parse = condition_parse, + .final_check = condition_check, + .print = condition_print, + .save = condition_save, + .extra_opts = condition_opts, +}; + +static struct xtables_match condition_mt6_reg = { + .name = "condition", + .revision = 0, + .family = PF_INET6, + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct xt_condition_mtinfo)), + .userspacesize = offsetof(struct xt_condition_mtinfo, condvar), + .help = condition_help, + .parse = condition_parse, + .final_check = condition_check, + .print = condition_print, + .save = condition_save, + .extra_opts = condition_opts, +}; + +static void _init(void) +{ + xtables_register_match(&condition_mt4_reg); + xtables_register_match(&condition_mt6_reg); +} diff --git a/extensions/libxt_condition.man b/extensions/libxt_condition.man new file mode 100644 index 0000000..c5939ed --- /dev/null +++ b/extensions/libxt_condition.man @@ -0,0 +1,4 @@ +This matches if a specific condition variable is (un)set. +.TP +[\fB!\fP] \fB--condition\fP \fIname\fP +Match on boolean value stored in /proc/net/nf_condition/\fIname\fP. diff --git a/extensions/xt_condition.Kconfig b/extensions/xt_condition.Kconfig new file mode 100644 index 0000000..896a2c9 --- /dev/null +++ b/extensions/xt_condition.Kconfig @@ -0,0 +1,6 @@ +config NETFILTER_XT_MATCH_CONDITION + tristate '"condition" match support' + depends on NETFILTER_XTABLES && NETFILTER_ADVANCED + ---help--- + This option allows you to match firewall rules against condition + variables stored in the /proc/net/nf_condition directory. diff --git a/extensions/xt_condition.c b/extensions/xt_condition.c new file mode 100644 index 0000000..5bfe242 --- /dev/null +++ b/extensions/xt_condition.c @@ -0,0 +1,258 @@ +/* + * xt_condition + * + * Description: This module allows firewall rules to match using + * condition variables available through procfs. + * + * Authors: + * Stephane Ouellette , 2002-10-22 + * Massimiliano Hofer , 2006-05-15 + * + * This software is distributed under the terms of the GNU GPL. + */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "xt_condition.h" +#include "compat_xtables.h" + +#ifndef CONFIG_PROC_FS +# error "proc file system support is required for this module" +#endif +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 24) +# define proc_net init_net.proc_net +#endif + +/* Defaults, these can be overridden on the module command-line. */ +static unsigned int condition_list_perms = S_IRUGO | S_IWUSR; +static unsigned int condition_uid_perms = 0; +static unsigned int condition_gid_perms = 0; + +MODULE_AUTHOR("Stephane Ouellette "); +MODULE_AUTHOR("Massimiliano Hofer "); +MODULE_DESCRIPTION("Allows rules to match against condition variables"); +MODULE_LICENSE("GPL"); +module_param(condition_list_perms, uint, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(condition_list_perms, "permissions on /proc/net/nf_condition/* files"); +module_param(condition_uid_perms, uint, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(condition_uid_perms, "user owner of /proc/net/nf_condition/* files"); +module_param(condition_gid_perms, uint, S_IRUSR | S_IWUSR); +MODULE_PARM_DESC(condition_gid_perms, "group owner of /proc/net/nf_condition/* files"); +MODULE_ALIAS("ipt_condition"); +MODULE_ALIAS("ip6t_condition"); + +struct condition_variable { + struct list_head list; + struct proc_dir_entry *status_proc; + unsigned int refcount; + bool enabled; +}; + +/* proc_lock is a user context only semaphore used for write access */ +/* to the conditions' list. */ +static DECLARE_MUTEX(proc_lock); + +static LIST_HEAD(conditions_list); +static struct proc_dir_entry *proc_net_condition; + +static int condition_proc_read(char __user *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + const struct condition_variable *var = data; + + buffer[0] = var->enabled ? '1' : '0'; + buffer[1] = '\n'; + if (length >= 2) + *eof = true; + + return 2; +} + +static int condition_proc_write(struct file *file, const char __user *buffer, + unsigned long length, void *data) +{ + struct condition_variable *var = data; + char newval; + + if (length > 0) { + if (get_user(newval, buffer) != 0) + return -EFAULT; + /* Match only on the first character */ + switch (newval) { + case '0': + var->enabled = false; + break; + case '1': + var->enabled = true; + break; + } + } + + return length; +} + +static bool +condition_mt(const struct sk_buff *skb, const struct net_device *in, + const struct net_device *out, const struct xt_match *match, + const void *matchinfo, int offset, unsigned int protoff, + bool *hotdrop) +{ + const struct xt_condition_mtinfo *info = matchinfo; + const struct condition_variable *var = info->condvar; + bool x; + + rcu_read_lock(); + x = rcu_dereference(var->enabled); + rcu_read_unlock(); + + return x ^ info->invert; +} + +static bool +condition_mt_check(const char *tablename, const void *entry, + const struct xt_match *match, void *matchinfo, + unsigned int hook_mask) +{ + struct xt_condition_mtinfo *info = matchinfo; + struct condition_variable *var; + + /* Forbid certain names */ + if (*info->name == '\0' || *info->name == '.' || + info->name[sizeof(info->name)-1] != '\0' || + memchr(info->name, '/', sizeof(info->name)) != NULL) { + printk(KERN_INFO KBUILD_MODNAME ": name not allowed or too " + "long: \"%.*s\"\n", sizeof(info->name), info->name); + return false; + } + + /* + * Let's acquire the lock, check for the condition and add it + * or increase the reference counter. + */ + if (down_interruptible(&proc_lock)) + return false; + + list_for_each_entry(var, &conditions_list, list) { + if (strcmp(info->name, var->status_proc->name) == 0) { + var->refcount++; + up(&proc_lock); + info->condvar = var; + return true; + } + } + + /* At this point, we need to allocate a new condition variable. */ + var = kmalloc(sizeof(struct condition_variable), GFP_KERNEL); + + if (var == NULL) { + up(&proc_lock); + return false; + } + + /* Create the condition variable's proc file entry. */ + var->status_proc = create_proc_entry(info->name, condition_list_perms, proc_net_condition); + + if (var->status_proc == NULL) { + kfree(var); + up(&proc_lock); + return false; + } + + var->refcount = 1; + var->enabled = false; + var->status_proc->owner = THIS_MODULE; + var->status_proc->data = var; + wmb(); + var->status_proc->read_proc = condition_proc_read; + var->status_proc->write_proc = condition_proc_write; + + list_add_rcu(&var->list, &conditions_list); + + var->status_proc->uid = condition_uid_perms; + var->status_proc->gid = condition_gid_perms; + + up(&proc_lock); + + info->condvar = var; + return true; +} + +static void condition_mt_destroy(const struct xt_match *match, void *matchinfo) +{ + const struct xt_condition_mtinfo *info = matchinfo; + struct condition_variable *var = info->condvar; + + down(&proc_lock); + if (--var->refcount == 0) { + list_del_rcu(&var->list); + remove_proc_entry(var->status_proc->name, proc_net_condition); + up(&proc_lock); + /* + * synchronize_rcu() would be good enough, but + * synchronize_net() guarantees that no packet + * will go out with the old rule after + * succesful removal. + */ + synchronize_net(); + kfree(var); + return; + } + up(&proc_lock); +} + +static struct xt_match condition_mt_reg[] __read_mostly = { + { + .name = "condition", + .revision = 0, + .family = PF_INET, + .matchsize = XT_ALIGN(sizeof(struct xt_condition_mtinfo)), + .match = condition_mt, + .checkentry = condition_mt_check, + .destroy = condition_mt_destroy, + .me = THIS_MODULE, + }, + { + .name = "condition", + .revision = 0, + .family = PF_INET6, + .matchsize = XT_ALIGN(sizeof(struct xt_condition_mtinfo)), + .match = condition_mt, + .checkentry = condition_mt_check, + .destroy = condition_mt_destroy, + .me = THIS_MODULE, + }, +}; + +static const char *const dir_name = "nf_condition"; + +static int __init condition_mt_init(void) +{ + int ret; + + proc_net_condition = proc_mkdir(dir_name, proc_net); + if (proc_net_condition == NULL) + return -EACCES; + + ret = xt_register_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg)); + if (ret < 0) { + remove_proc_entry(dir_name, proc_net); + return ret; + } + + return 0; +} + +static void __exit condition_mt_exit(void) +{ + xt_unregister_matches(condition_mt_reg, ARRAY_SIZE(condition_mt_reg)); + remove_proc_entry(dir_name, proc_net); +} + +module_init(condition_mt_init); +module_exit(condition_mt_exit); diff --git a/extensions/xt_condition.h b/extensions/xt_condition.h new file mode 100644 index 0000000..db37a31 --- /dev/null +++ b/extensions/xt_condition.h @@ -0,0 +1,16 @@ +#ifndef _XT_CONDITION_H +#define _XT_CONDITION_H + +enum { + CONDITION_NAME_LEN = 31, +}; + +struct xt_condition_mtinfo { + char name[CONDITION_NAME_LEN]; + __u8 invert; + + /* Used internally by the kernel */ + void *condvar __attribute__((aligned(8))); +}; + +#endif /* _XT_CONDITION_H */ diff --git a/mconfig b/mconfig index dd2e0d5..b78570e 100644 --- a/mconfig +++ b/mconfig @@ -6,5 +6,6 @@ build_ECHO= build_LOGMARK=m build_TARPIT=m build_TEE=m +build_condition=m build_geoip=m build_portscan=m