/* Kernel module to match the program name tied to sockets associated with locally generated outgoing packets. */ /* * (C) 2004 Luke Kenneth Casson Leighton * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as * published by the Free Software Foundation. * * Module based on ipt_owner.c, by Mark Boucher. * * (C) 2000 Marc Boucher */ #include #include #include #include #include #include #include #include #include #include #include MODULE_LICENSE("GPL"); MODULE_AUTHOR("Luke Kenneth Casson Leighton "); MODULE_DESCRIPTION("iptables program match"); /* lkcl: this function is in fs/proc/base.c. it's a generic function * derived from proc_exe_link(). it's inappropriate to leave that * function in fs/proc/base.c. but i don't care: i don't have the * knowledge to say where it should go. therefore i'm leaving * it in fs/proc/base.c. */ extern int proc_task_dentry_lookup(struct task_struct *task, struct dentry **dentry, struct vfsmount **mnt); /* * look up the dentry (for the inode) of the task's executable, * plus lookup the mountpoint of the filesystem from where that * executable came from. then do exactly the same socket checking * that all the other checks seem to be doing. */ static int proc_exe_check(struct task_struct *task, const char *prog_full_name) { int result; struct vfsmount *mnt; struct dentry *dentry; char exe_path[IPT_PROGNAME_SZ]; char *p; result = proc_task_dentry_lookup(task, &dentry, &mnt); if (result != 0) return result; /* turn the task info (dentry + mountpoint) into a program name... */ p = d_path(dentry, mnt, exe_path, sizeof(exe_path)); if (p == ERR_PTR(-ENAMETOOLONG)) return -1; printk("ipt_program: program name %s\n", prog_full_name); return strncmp(p, prog_full_name, sizeof(exe_path)); } static int match_inode(const struct sk_buff *skb, const char *prog_full_name) { struct task_struct *g, *p; struct files_struct *files; int i; read_lock(&tasklist_lock); do_each_thread(g, p) { if (proc_exe_check(p, prog_full_name)) continue; //printk("ipt_program: program name %s matched, now looking for socket...\n", prog_full_name); task_lock(p); files = p->files; if(files) { spin_lock(&files->file_lock); for (i=0; i < files->max_fds; i++) { if (fcheck_files(files, i) == skb->sk->sk_socket->file) { spin_unlock(&files->file_lock); task_unlock(p); read_unlock(&tasklist_lock); printk("ipt_program: program name %s matched\n", prog_full_name); return 1; } } spin_unlock(&files->file_lock); } task_unlock(p); } while_each_thread(g, p); read_unlock(&tasklist_lock); return 0; } static int match(const struct sk_buff *skb, const struct net_device *in, const struct net_device *out, const void *matchinfo, int offset, int *hotdrop) { const struct ipt_program_info *info = matchinfo; printk("ipt_program...\n"); if (!skb->sk || !skb->sk->sk_socket || !skb->sk->sk_socket->file) { printk("ipt_program: not enough info, no sk, no sk_socket, no file\n"); return 0; } if (!match_inode(skb, info->full_exe_path) ^ !!info->invert) return 0; return 1; } static int checkentry(const char *tablename, const struct ipt_ip *ip, void *matchinfo, unsigned int matchsize, unsigned int hook_mask) { if (hook_mask & ~((1 << NF_IP_LOCAL_OUT) | (1 << NF_IP_POST_ROUTING))) { printk("ipt_program: warning - code based on ipt_owner, which is only valid for LOCAL_OUT or POST_ROUTING. continuing at your own risk!\n"); /*return 0;*/ } if (matchsize != IPT_ALIGN(sizeof(struct ipt_program_info))) { printk("Matchsize %u != %Zu\n", matchsize, IPT_ALIGN(sizeof(struct ipt_program_info))); return 0; } return 1; } static struct ipt_match program_match = { .name = "program", .match = &match, .checkentry = &checkentry, .me = THIS_MODULE, }; static int __init init(void) { return ipt_register_match(&program_match); } static void __exit fini(void) { ipt_unregister_match(&program_match); } module_init(init); module_exit(fini);