1/*-------------------------------------------*\ 2| Netfilter Condition Module | 3| | 4| Description: This module allows firewall | 5| rules to match using condition variables | 6| stored in /proc files. | 7| | 8| Author: Stephane Ouellette 2002-10-22 | 9| <ouellettes@videotron.ca> | 10| Massimiliano Hofer 2006-05-15 | 11| <max@nucleus.it> | 12| | 13| History: | 14| 2003-02-10 Second version with improved | 15| locking and simplified code. | 16| 2006-05-15 2.6.16 adaptations. | 17| Locking overhaul. | 18| Various bug fixes. | 19| | 20| This software is distributed under the | 21| terms of the GNU GPL. | 22\*-------------------------------------------*/ 23 24#include <linux/version.h> 25#include <linux/kernel.h> 26#include <linux/module.h> 27#include <linux/proc_fs.h> 28#include <linux/spinlock.h> 29#include <linux/string.h> 30#include <linux/list.h> 31#include <asm/atomic.h> 32#include <asm/uaccess.h> 33#include <linux/netfilter/x_tables.h> 34#include <linux/netfilter/xt_condition.h> 35 36#ifndef CONFIG_PROC_FS 37#error "Proc file system support is required for this module" 38#endif 39 40/* Defaults, these can be overridden on the module command-line. */ 41static unsigned int condition_list_perms = 0644; 42static unsigned int compat_dir_name = 0; 43static unsigned int condition_uid_perms = 0; 44static unsigned int condition_gid_perms = 0; 45 46MODULE_AUTHOR("Stephane Ouellette <ouellettes@videotron.ca> and Massimiliano Hofer <max@nucleus.it>"); 47MODULE_DESCRIPTION("Allows rules to match against condition variables"); 48MODULE_LICENSE("GPL"); 49module_param(condition_list_perms, uint, 0600); 50MODULE_PARM_DESC(condition_list_perms,"permissions on /proc/net/nf_condition/* files"); 51module_param(condition_uid_perms, uint, 0600); 52MODULE_PARM_DESC(condition_uid_perms,"user owner of /proc/net/nf_condition/* files"); 53module_param(condition_gid_perms, uint, 0600); 54MODULE_PARM_DESC(condition_gid_perms,"group owner of /proc/net/nf_condition/* files"); 55module_param(compat_dir_name, bool, 0400); 56MODULE_PARM_DESC(compat_dir_name,"use old style /proc/net/ipt_condition/* files"); 57MODULE_ALIAS("ipt_condition"); 58MODULE_ALIAS("ip6t_condition"); 59 60struct condition_variable { 61 struct list_head list; 62 struct proc_dir_entry *status_proc; 63 unsigned int refcount; 64 int enabled; /* TRUE == 1, FALSE == 0 */ 65}; 66 67/* proc_lock is a user context only semaphore used for write access */ 68/* to the conditions' list. */ 69static DEFINE_MUTEX(proc_lock); 70 71static LIST_HEAD(conditions_list); 72static struct proc_dir_entry *proc_net_condition = NULL; 73static const char *dir_name; 74 75static int 76xt_condition_read_info(char __user *buffer, char **start, off_t offset, 77 int length, int *eof, void *data) 78{ 79 struct condition_variable *var = 80 (struct condition_variable *) data; 81 82 buffer[0] = (var->enabled) ? '1' : '0'; 83 buffer[1] = '\n'; 84 if (length>=2) 85 *eof = 1; 86 87 return 2; 88} 89 90 91static int 92xt_condition_write_info(struct file *file, const char __user *buffer, 93 unsigned long length, void *data) 94{ 95 struct condition_variable *var = 96 (struct condition_variable *) data; 97 char newval; 98 99 if (length>0) { 100 if (get_user(newval, buffer)) 101 return -EFAULT; 102 /* Match only on the first character */ 103 switch (newval) { 104 case '0': 105 var->enabled = 0; 106 break; 107 case '1': 108 var->enabled = 1; 109 break; 110 } 111 } 112 113 return (int) length; 114} 115 116 117static bool 118match(const struct sk_buff *skb, struct xt_action_param *par) 119{ 120 const struct condition_info *info = 121 (const struct condition_info *) par->matchinfo; 122 struct condition_variable *var; 123 int condition_status = 0; 124 125 rcu_read_lock(); 126 list_for_each_entry_rcu(var, &conditions_list, list) { 127 if (strcmp(info->name, var->status_proc->name) == 0) { 128 condition_status = var->enabled; 129 break; 130 } 131 } 132 rcu_read_unlock(); 133 134 return condition_status ^ info->invert; 135} 136 137 138 139static int 140checkentry(const struct xt_mtchk_param *par) 141{ 142 static const char * const forbidden_names[]={ "", ".", ".." }; 143 struct condition_info *info = (struct condition_info *) par->matchinfo; 144 struct list_head *pos; 145 struct condition_variable *var, *newvar; 146 147 int i; 148 149 /* We don't want a '/' in a proc file name. */ 150 for (i=0; i < CONDITION_NAME_LEN && info->name[i] != '\0'; i++) 151 if (info->name[i] == '/') 152 return -EINVAL; 153 /* We can't handle file names longer than CONDITION_NAME_LEN and */ 154 /* we want a NULL terminated string. */ 155 if (i == CONDITION_NAME_LEN) 156 return -EINVAL; 157 158 /* We don't want certain reserved names. */ 159 for (i=0; i < sizeof(forbidden_names)/sizeof(char *); i++) 160 if(strcmp(info->name, forbidden_names[i])==0) 161 return -EINVAL; 162 163 /* Let's acquire the lock, check for the condition and add it */ 164 /* or increase the reference counter. */ 165 if (mutex_lock_interruptible(&proc_lock)) 166 return -EINTR; 167 168 list_for_each(pos, &conditions_list) { 169 var = list_entry(pos, struct condition_variable, list); 170 if (strcmp(info->name, var->status_proc->name) == 0) { 171 var->refcount++; 172 mutex_unlock(&proc_lock); 173 return 0; 174 } 175 } 176 177 /* At this point, we need to allocate a new condition variable. */ 178 newvar = kmalloc(sizeof(struct condition_variable), GFP_KERNEL); 179 180 if (!newvar) { 181 mutex_unlock(&proc_lock); 182 return -ENOMEM; 183 } 184 185 /* Create the condition variable's proc file entry. */ 186 newvar->status_proc = create_proc_entry(info->name, condition_list_perms, proc_net_condition); 187 188 if (!newvar->status_proc) { 189 kfree(newvar); 190 mutex_unlock(&proc_lock); 191 return -ENOMEM; 192 } 193 194 newvar->refcount = 1; 195 newvar->enabled = 0; 196 newvar->status_proc->data = newvar; 197 wmb(); 198 newvar->status_proc->read_proc = xt_condition_read_info; 199 newvar->status_proc->write_proc = xt_condition_write_info; 200 201 list_add_rcu(&newvar->list, &conditions_list); 202 203 newvar->status_proc->uid = condition_uid_perms; 204 newvar->status_proc->gid = condition_gid_perms; 205 206 mutex_unlock(&proc_lock); 207 208 return 0; 209} 210 211 212static void 213destroy(const struct xt_mtdtor_param *par) 214{ 215 struct condition_info *info = (struct condition_info *) par->matchinfo; 216 struct list_head *pos; 217 struct condition_variable *var; 218 219 220 mutex_lock(&proc_lock); 221 222 list_for_each(pos, &conditions_list) { 223 var = list_entry(pos, struct condition_variable, list); 224 if (strcmp(info->name, var->status_proc->name) == 0) { 225 if (--var->refcount == 0) { 226 list_del_rcu(pos); 227 remove_proc_entry(var->status_proc->name, proc_net_condition); 228 mutex_unlock(&proc_lock); 229 /* synchronize_rcu() would be goog enough, but synchronize_net() */ 230 /* guarantees that no packet will go out with the old rule after */ 231 /* succesful removal. */ 232 synchronize_net(); 233 kfree(var); 234 return; 235 } 236 break; 237 } 238 } 239 240 mutex_unlock(&proc_lock); 241} 242 243 244static struct xt_match condition_match = { 245 .name = "condition", 246 .family = NFPROTO_IPV4, 247 .matchsize = sizeof(struct condition_info), 248 .match = &match, 249 .checkentry = &checkentry, 250 .destroy = &destroy, 251 .me = THIS_MODULE 252}; 253 254static struct xt_match condition6_match = { 255 .name = "condition", 256 .family = NFPROTO_IPV6, 257 .matchsize = sizeof(struct condition_info), 258 .match = &match, 259 .checkentry = &checkentry, 260 .destroy = &destroy, 261 .me = THIS_MODULE 262}; 263 264static int __init 265init(void) 266{ 267 struct proc_dir_entry * const proc_net=init_net.proc_net; 268 int errorcode; 269 270 dir_name = compat_dir_name? "ipt_condition": "nf_condition"; 271 272 proc_net_condition = proc_mkdir(dir_name, proc_net); 273 if (!proc_net_condition) { 274 remove_proc_entry(dir_name, proc_net); 275 return -EACCES; 276 } 277 278 errorcode = xt_register_match(&condition_match); 279 if (errorcode) { 280 xt_unregister_match(&condition_match); 281 remove_proc_entry(dir_name, proc_net); 282 return errorcode; 283 } 284 285 errorcode = xt_register_match(&condition6_match); 286 if (errorcode) { 287 xt_unregister_match(&condition6_match); 288 xt_unregister_match(&condition_match); 289 remove_proc_entry(dir_name, proc_net); 290 return errorcode; 291 } 292 293 return 0; 294} 295 296 297static void __exit 298fini(void) 299{ 300 xt_unregister_match(&condition6_match); 301 xt_unregister_match(&condition_match); 302 remove_proc_entry(dir_name, init_net.proc_net); 303} 304 305module_init(init); 306module_exit(fini); 307