1 #include <linux/module.h>
2 #include <linux/moduleparam.h>
3 #include <linux/types.h>
4 #include <linux/kernel.h>
5 #include <linux/version.h>
6 #include <linux/fs_struct.h>
9 #include <linux/reboot.h>
10 #include <linux/delay.h>
11 #include <linux/proc_fs.h>
12 #include <asm/uaccess.h>
13 #include <linux/sysrq.h>
14 #include <linux/timer.h>
15 #include <linux/time.h>
16 #include <linux/lglock.h>
17 #include <linux/init.h>
18 #include <linux/idr.h>
19 #include <linux/namei.h>
20 #include <linux/bitops.h>
21 #include <linux/mount.h>
22 #include <linux/dcache.h>
23 #include <linux/spinlock.h>
24 #include <linux/completion.h>
25 #include <linux/sched.h>
26 #include <linux/seq_file.h>
27 #include <linux/kprobes.h>
28 #include <linux/kallsyms.h>
29 #include <linux/nsproxy.h>
31 #include <linux/magic.h>
32 #include <linux/slab.h>
33 #include <linux/module.h> /* Specifically, a module */
34 #include <linux/kernel.h> /* We're doing kernel work */
35 #include <linux/proc_fs.h> /* Necessary because we use the proc fs */
37 #define VERSION_STR "0.0.1"
40 #error "This code does not support your architecture"
43 static char *aclpath = "procprotect";
45 static struct qstr aclqpath;
47 module_param(aclpath, charp, 0);
48 MODULE_PARM_DESC(aclpath, "Root of directory that stores acl tags for /proc files.");
50 MODULE_AUTHOR("Sapan Bhatia <sapanb@cs.princeton.edu>");
51 MODULE_DESCRIPTION("Lightweight ACLs for /proc.");
52 MODULE_LICENSE("GPL");
53 MODULE_VERSION(VERSION_STR);
55 struct procprotect_ctx {
64 struct hlist_node hlist;
68 Added by Guilherme Sperb Machado <gsm@machados.org>
69 According to recent changes in the kernel, the nameidata struct
70 became opaque in 3.19.1. So, let's declare it in our implementation.
71 The 'if' was added based on the kernel version since the code would
72 considerably change, so, this is the quick fix. We're not sure if
73 this module is really necessary with recent versions of LXC, related
75 Source: https://github.com/torvalds/linux/commit/1f55a6ec940fb45e3edaa52b6e9fc40cf8e18dcb
77 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,19,1)
82 struct inode *inode; /* path.dentry.d_inode */
87 char *saved_names[MAX_NESTED_LINKS + 1];
91 #define HASH_SIZE (1<<10)
93 struct hlist_head procprotect_hash[HASH_SIZE];
95 struct proc_dir_entry *proc_entry;
97 static int run_acl(unsigned long ino) {
98 struct acl_entry *entry;
99 hlist_for_each_entry_rcu_notrace(entry,
100 &procprotect_hash[ino & (HASH_SIZE-1)],
102 if (entry->ino==ino) {
110 Entry point of intercepted call. We need to do two things here:
111 - Decide if we need the heavier return hook to be called
112 - Save the first argument, which is in a register, for consideration in the return hook
114 static int lookup_fast_entry(struct kretprobe_instance *ri, struct pt_regs *regs) {
116 struct procprotect_ctx *ctx;
117 struct nameidata *nd = (struct nameidata *) regs->di;
118 struct dentry *parent;
119 struct inode *pinode;
122 parent = nd->path.dentry;
124 if (!parent) return ret;
125 pinode = parent->d_inode;
127 if (!pinode || !pinode->i_sb || !current || !current->nsproxy) return ret;
129 if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC
130 && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {
131 ctx = (struct procprotect_ctx *) ri->data;
132 ctx->inode = (struct inode **)regs->dx;
133 ctx->flags = nd->flags;
140 /* The entry hook ensures that the return hook is only called for
145 static int lookup_fast_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
147 struct procprotect_ctx *ctx = (struct procprotect_ctx *) ri->data;
151 /* The kernel is going to honor the request. Here's where we step in */
152 struct inode *inode = *(ctx->inode);
153 if (!run_acl(inode->i_ino)) {
162 static int lookup_slow_entry(struct kretprobe_instance *ri, struct pt_regs *regs) {
164 struct procprotect_ctx *ctx;
165 struct nameidata *nd = (struct nameidata *) regs->di;
166 struct path *p = (struct path *) regs->si;
167 struct dentry *parent;
168 struct inode *pinode;
171 parent = nd->path.dentry;
172 if (!parent) return ret;
173 pinode= parent->d_inode;
175 if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC
176 && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {
178 ctx = (struct procprotect_ctx *) ri->data;
180 ctx->flags = nd->flags;
188 /* The entry hook ensures that the return hook is only called for
191 /*static int print_once = 0;*/
193 static int lookup_slow_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
195 struct procprotect_ctx *ctx;
199 if (!ri || !ri->data) {return 0;}
200 ctx = (struct procprotect_ctx *) ri->data;
205 struct path *p = ctx->path;
206 if (!p || !p->dentry || !p->dentry->d_inode /* This last check was responsible for the f18 bug*/) {
209 inode = p->dentry->d_inode;
210 if (!run_acl(inode->i_ino)) {
225 static struct file *do_last_probe(struct nameidata *nd, struct path *path, struct file *file,
226 struct open_flags *op, const char *pathname) {
227 struct dentry *parent = nd->path.dentry;
228 struct inode *pinode = parent->d_inode;
229 /*struct qstr *q = &nd->last;*/
232 if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {
233 /*if (!strncmp(q->name,"sysrq-trigger",13)) {
234 printk(KERN_CRIT "do_last sysrqtrigger: %d",op->open_flag);
236 op->open_flag &= ~O_CREAT;
242 static struct jprobe dolast_probe = {
243 .entry = do_last_probe
246 static struct kretprobe fast_probe = {
247 .entry_handler = lookup_fast_entry,
248 .handler = lookup_fast_ret,
250 .data_size = sizeof(struct procprotect_ctx)
253 static struct kretprobe slow_probe = {
254 .entry_handler = lookup_slow_entry,
255 .handler = lookup_slow_ret,
257 .data_size = sizeof(struct procprotect_ctx)
262 static int init_probes(void) {
264 dolast_probe.kp.addr =
265 (kprobe_opcode_t *) kallsyms_lookup_name("do_last");
267 if (!dolast_probe.kp.addr) {
268 printk("Couldn't find %s to plant kretprobe\n", "do_last");
272 if ((ret = register_jprobe(&dolast_probe)) <0) {
273 printk("register_jprobe failed, returned %u\n", ret);
277 (kprobe_opcode_t *) kallsyms_lookup_name("lookup_fast");
279 if (!fast_probe.kp.addr) {
280 printk("Couldn't find %s to plant kretprobe\n", "lookup_fast");
285 (kprobe_opcode_t *) kallsyms_lookup_name("lookup_slow");
287 if (!slow_probe.kp.addr) {
288 printk("Couldn't find %s to plant kretprobe\n", "lookup_slow");
292 if ((ret = register_kretprobe(&fast_probe)) <0) {
293 printk("register_kretprobe failed, returned %d\n", ret);
297 printk("Planted kretprobe at %p, handler addr %p\n",
298 fast_probe.kp.addr, fast_probe.handler);
300 if ((ret = register_kretprobe(&slow_probe)) <0) {
301 printk("register_kretprobe failed, returned %d\n", ret);
304 printk("Planted kretprobe at %p, handler addr %p\n",
305 slow_probe.kp.addr, slow_probe.handler);
309 static void add_entry(char *pathname) {
311 if (kern_path(pathname, 0, &path)) {
312 printk(KERN_CRIT "Path lookup failed for %s",pathname);
315 unsigned int ino = path.dentry->d_inode->i_ino;
316 struct acl_entry *entry;
317 entry = kmalloc(GFP_KERNEL, sizeof(struct acl_entry));
321 printk(KERN_CRIT "Could not allocate memory for %s",pathname);
325 hlist_add_head_rcu(&entry->hlist,&procprotect_hash[ino&(HASH_SIZE-1)]);
326 printk(KERN_CRIT "Added inode %u",ino);
329 printk(KERN_CRIT "Did not add inode %u, already in list", ino);
337 static void __exit procprotect_exit(void)
339 struct acl_entry *entry;
342 unregister_kretprobe(&fast_probe);
343 unregister_kretprobe(&slow_probe);
344 unregister_jprobe(&dolast_probe);
346 for (i=0;i<HASH_SIZE;i++) {
347 hlist_for_each_entry_rcu(entry,
348 &procprotect_hash[i],
354 remove_proc_entry("procprotect",NULL);
355 printk("Procprotect: Stopped procprotect.\n");
360 ssize_t procfile_write(struct file *file, const char *buffer, size_t count, loff_t *data) {
362 pathname = (char *) kmalloc(count, GFP_KERNEL);
365 printk(KERN_CRIT "Could not allocate memory for pathname buffer");
369 if (current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns)
372 if (copy_from_user(pathname, buffer, count)) {
375 if (count && (pathname[count-1]==10 || pathname[count-1]==13)) {
376 pathname[count-1]='\0';
379 pathname[count]='\0';
385 if (init_probes()==-1)
386 printk(KERN_CRIT "Could not install procprotect probes. Reload module to retry.");
388 printk(KERN_CRIT "Length of buffer=%d",(int)strlen(pathname));
393 static const struct file_operations procprotect_fops = {
394 .owner = THIS_MODULE,
395 .write = procfile_write
399 static int __init procprotect_init(void)
404 printk("Procprotect: starting procprotect version %s with ACLs at path %s.\n",
405 VERSION_STR, aclpath);
407 for(i=0;i<HASH_SIZE;i++) {
408 INIT_HLIST_HEAD(&procprotect_hash[i]);
411 aclqpath.name = aclpath;
412 aclqpath.len = strnlen(aclpath, PATH_MAX);
414 proc_entry = proc_create("procprotect", 0644, NULL, &procprotect_fops);
421 module_init(procprotect_init);
422 module_exit(procprotect_exit);