From 72dc73e6a5fea722207952d461ae5f444e305d11 Mon Sep 17 00:00:00 2001 From: Jan Engelhardt Date: Wed, 26 Mar 2008 03:19:47 +0100 Subject: [PATCH] condition: import 20080125 code base --- extensions/Kbuild | 1 + extensions/Mbuild | 1 + extensions/libxt_condition.c | 90 +++++++++ extensions/libxt_condition.man | 4 + extensions/xt_condition.Kconfig | 6 + extensions/xt_condition.c | 345 ++++++++++++++++++++++++++++++++ extensions/xt_condition.h | 11 + mconfig | 1 + 8 files changed, 459 insertions(+) create mode 100644 extensions/libxt_condition.c create mode 100644 extensions/libxt_condition.man create mode 100644 extensions/xt_condition.Kconfig create mode 100644 extensions/xt_condition.c create mode 100644 extensions/xt_condition.h 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..bd520ae --- /dev/null +++ b/extensions/libxt_condition.c @@ -0,0 +1,90 @@ +/* Shared library add-on to iptables for condition match */ +#include +#include +#include +#include +#include +#include "xt_condition.h" + +static void condition_help(void) +{ + printf("condition match options:\n" + "--condition [!] filename " + "Match on boolean value stored in /proc file\n"); +} + +static const struct option condition_opts[] = { + { .name = "condition", .has_arg = 1, .flag = 0, .val = 'X' }, + { .name = 0 } +}; + +static int condition_parse(int c, char **argv, int invert, unsigned int *flags, + const void *entry, struct xt_entry_match **match) +{ + struct condition_info *info = + (struct condition_info *) (*match)->data; + + if (c == 'X') { + if (*flags) + exit_error(PARAMETER_PROBLEM, + "Can't specify multiple conditions"); + + check_inverse(optarg, &invert, &optind, 0); + + if (strlen(argv[optind - 1]) < CONDITION_NAME_LEN) + strcpy(info->name, argv[optind - 1]); + else + exit_error(PARAMETER_PROBLEM, + "File name too long"); + + info->invert = invert; + *flags = 1; + return 1; + } + + return 0; +} + +static void condition_check(unsigned int flags) +{ + if (!flags) + 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 condition_info *info = + (const struct condition_info *) 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 condition_info *info = + (const struct condition_info *) match->data; + + printf("--condition %s\"%s\" ", (info->invert) ? "! " : "", info->name); +} + +static struct xtables_match condition_match = { + .name = "condition", + .version = XTABLES_VERSION, + .size = XT_ALIGN(sizeof(struct condition_info)), + .userspacesize = XT_ALIGN(sizeof(struct condition_info)), + .help = condition_help, + .parse = condition_parse, + .final_check = condition_check, + .print = condition_print, + .save = condition_save, + .extra_opts = condition_opts, +}; + +void _init(void); +void _init(void) +{ + xtables_register_match(&condition_match); +} diff --git a/extensions/libxt_condition.man b/extensions/libxt_condition.man new file mode 100644 index 0000000..ce2aa95 --- /dev/null +++ b/extensions/libxt_condition.man @@ -0,0 +1,4 @@ +This matches if a specific /proc filename is '0' or '1'. +.TP +.BI "--condition " "[!] \fIfilename\fP" +Match on boolean value stored in /proc/net/ipt_condition/filename file 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..de990d3 --- /dev/null +++ b/extensions/xt_condition.c @@ -0,0 +1,345 @@ +/*-------------------------------------------*\ +| Netfilter Condition Module | +| | +| Description: This module allows firewall | +| rules to match using condition variables | +| stored in /proc files. | +| | +| Author: Stephane Ouellette 2002-10-22 | +| | +| Massimiliano Hofer 2006-05-15 | +| | +| | +| History: | +| 2003-02-10 Second version with improved | +| locking and simplified code. | +| 2006-05-15 2.6.16 adaptations. | +| Locking overhaul. | +| Various bug fixes. | +| | +| This software is distributed under the | +| terms of the GNU GPL. | +\*-------------------------------------------*/ + +#include +#include +#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 + +/* Defaults, these can be overridden on the module command-line. */ +static unsigned int condition_list_perms = 0644; +static unsigned int compat_dir_name = 0; +static unsigned int condition_uid_perms = 0; +static unsigned int condition_gid_perms = 0; + +MODULE_AUTHOR("Stephane Ouellette and Massimiliano Hofer "); +MODULE_DESCRIPTION("Allows rules to match against condition variables"); +MODULE_LICENSE("GPL"); +module_param(condition_list_perms, uint, 0600); +MODULE_PARM_DESC(condition_list_perms,"permissions on /proc/net/nf_condition/* files"); +module_param(condition_uid_perms, uint, 0600); +MODULE_PARM_DESC(condition_uid_perms,"user owner of /proc/net/nf_condition/* files"); +module_param(condition_gid_perms, uint, 0600); +MODULE_PARM_DESC(condition_gid_perms,"group owner of /proc/net/nf_condition/* files"); +module_param(compat_dir_name, bool, 0400); +MODULE_PARM_DESC(compat_dir_name,"use old style /proc/net/ipt_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; + int enabled; /* TRUE == 1, FALSE == 0 */ +}; + +/* 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 = NULL; +static const char *dir_name; + +static int +xt_condition_read_info(char __user *buffer, char **start, off_t offset, + int length, int *eof, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) data; + + buffer[0] = (var->enabled) ? '1' : '0'; + buffer[1] = '\n'; + if (length>=2) + *eof = 1; + + return 2; +} + + +static int +xt_condition_write_info(struct file *file, const char __user *buffer, + unsigned long length, void *data) +{ + struct condition_variable *var = + (struct condition_variable *) 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 = 0; + break; + case '1': + var->enabled = 1; + break; + } + } + + return (int) length; +} + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static int +match(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, int *hotdrop) +#else +bool +match(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) +#endif +{ + const struct condition_info *info = + (const struct condition_info *) matchinfo; + struct condition_variable *var; + int condition_status = 0; + + rcu_read_lock(); + list_for_each_entry_rcu(var, &conditions_list, list) { + if (strcmp(info->name, var->status_proc->name) == 0) { + condition_status = var->enabled; + break; + } + } + rcu_read_unlock(); + + return condition_status ^ info->invert; +} + + + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,24) +static int +#else +bool +#endif +checkentry(const char *tablename, const void *ip, + const struct xt_match *match, + void *matchinfo, +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) + unsigned int matchsize, +#endif + unsigned int hook_mask) +{ + static const char * const forbidden_names[]={ "", ".", ".." }; + struct condition_info *info = (struct condition_info *) matchinfo; + struct list_head *pos; + struct condition_variable *var, *newvar; + + int i; + + /* We don't want a '/' in a proc file name. */ + for (i=0; i < CONDITION_NAME_LEN && info->name[i] != '\0'; i++) + if (info->name[i] == '/') + return 0; + /* We can't handle file names longer than CONDITION_NAME_LEN and */ + /* we want a NULL terminated string. */ + if (i == CONDITION_NAME_LEN) + return 0; + + /* We don't want certain reserved names. */ + for (i=0; i < sizeof(forbidden_names)/sizeof(char *); i++) + if(strcmp(info->name, forbidden_names[i])==0) + return 0; + + /* Let's acquire the lock, check for the condition and add it */ + /* or increase the reference counter. */ + if (down_interruptible(&proc_lock)) + return -EINTR; + + list_for_each(pos, &conditions_list) { + var = list_entry(pos, struct condition_variable, list); + if (strcmp(info->name, var->status_proc->name) == 0) { + var->refcount++; + up(&proc_lock); + return 1; + } + } + + /* At this point, we need to allocate a new condition variable. */ + newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL); + + if (newvar == NULL) { + up(&proc_lock); + return -ENOMEM; + } + + /* Create the condition variable's proc file entry. */ + newvar->status_proc = create_proc_entry(info->name, condition_list_perms, proc_net_condition); + + if (newvar->status_proc == NULL) { + kfree(newvar); + up(&proc_lock); + return -ENOMEM; + } + + newvar->refcount = 1; + newvar->enabled = 0; + newvar->status_proc->owner = THIS_MODULE; + newvar->status_proc->data = newvar; + wmb(); + newvar->status_proc->read_proc = xt_condition_read_info; + newvar->status_proc->write_proc = xt_condition_write_info; + + list_add_rcu(&newvar->list, &conditions_list); + + newvar->status_proc->uid = condition_uid_perms; + newvar->status_proc->gid = condition_gid_perms; + + up(&proc_lock); + + return 1; +} + + +static void +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) +destroy(const struct xt_match *match, void *matchinfo, + unsigned int matchsize) +#else +destroy(const struct xt_match *match, void *matchinfo) +#endif +{ + struct condition_info *info = (struct condition_info *) matchinfo; + struct list_head *pos; + struct condition_variable *var; + +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,6,19) + if (matchsize != XT_ALIGN(sizeof(struct condition_info))) + return; +#endif + + down(&proc_lock); + + list_for_each(pos, &conditions_list) { + var = list_entry(pos, struct condition_variable, list); + if (strcmp(info->name, var->status_proc->name) == 0) { + if (--var->refcount == 0) { + list_del_rcu(pos); + remove_proc_entry(var->status_proc->name, proc_net_condition); + up(&proc_lock); + /* synchronize_rcu() would be goog enough, but synchronize_net() */ + /* guarantees that no packet will go out with the old rule after */ + /* succesful removal. */ + synchronize_net(); + kfree(var); + return; + } + break; + } + } + + up(&proc_lock); +} + + +static struct xt_match condition_match = { + .name = "condition", + .family = PF_INET, + .matchsize = sizeof(struct condition_info), + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + +static struct xt_match condition6_match = { + .name = "condition", + .family = PF_INET6, + .matchsize = sizeof(struct condition_info), + .match = &match, + .checkentry = &checkentry, + .destroy = &destroy, + .me = THIS_MODULE +}; + +static int __init +init(void) +{ +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + struct proc_dir_entry * const proc_net=init_net.proc_net; +#endif + int errorcode; + + dir_name = compat_dir_name? "ipt_condition": "nf_condition"; + + proc_net_condition = proc_mkdir(dir_name, proc_net); + if (proc_net_condition == NULL) { + remove_proc_entry(dir_name, proc_net); + return -EACCES; + } + + errorcode = xt_register_match(&condition_match); + if (errorcode) { + xt_unregister_match(&condition_match); + remove_proc_entry(dir_name, proc_net); + return errorcode; + } + + errorcode = xt_register_match(&condition6_match); + if (errorcode) { + xt_unregister_match(&condition6_match); + xt_unregister_match(&condition_match); + remove_proc_entry(dir_name, proc_net); + return errorcode; + } + + return 0; +} + + +static void __exit +fini(void) +{ + xt_unregister_match(&condition6_match); + xt_unregister_match(&condition_match); +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,24) + remove_proc_entry(dir_name, init_net.proc_net); +#else + remove_proc_entry(dir_name, proc_net); +#endif +} + +module_init(init); +module_exit(fini); diff --git a/extensions/xt_condition.h b/extensions/xt_condition.h new file mode 100644 index 0000000..f0706d0 --- /dev/null +++ b/extensions/xt_condition.h @@ -0,0 +1,11 @@ +#ifndef _XT_CONDITION_H +#define _XT_CONDITION_H + +#define CONDITION_NAME_LEN 32 + +struct condition_info { + char name[CONDITION_NAME_LEN]; + int invert; +}; + +#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