Make backwards compatible with 3.6
[procprotect.git] / procprotect.c
1 #include <linux/module.h>
2 #include <linux/moduleparam.h>
3 #include <linux/types.h>
4 #include <linux/kernel.h>
5 #include <linux/fs_struct.h>
6 #include <linux/fs.h>
7 #include <linux/mm.h>
8 #include <linux/reboot.h>
9 #include <linux/delay.h>
10 #include <linux/proc_fs.h>
11 #include <asm/uaccess.h>
12 #include <linux/sysrq.h>
13 #include <linux/timer.h>
14 #include <linux/time.h>
15 #include <linux/lglock.h>
16 #include <linux/init.h>
17 #include <linux/idr.h>
18 #include <linux/namei.h>
19 #include <linux/bitops.h>
20 #include <linux/mount.h>
21 #include <linux/dcache.h>
22 #include <linux/spinlock.h>
23 #include <linux/completion.h>
24 #include <linux/sched.h>
25 #include <linux/seq_file.h>
26 #include <linux/kprobes.h>
27 #include <linux/kallsyms.h>
28 #include <linux/nsproxy.h>
29
30 #define VERSION_STR "0.0.1"
31
32 #ifndef CONFIG_X86_64
33 #error "This code does not support your architecture"
34 #endif
35
36 #if LINUX_VERSION_CODE >= KERNEL_VERSION(3,8,0)
37 static char *aclpath = "procprotect";
38 #else
39 static char *aclpath __devinitdata = "procprotect";
40 #endif
41
42 static struct qstr aclqpath;
43
44 module_param(aclpath, charp, 0);
45 MODULE_PARM_DESC(aclpath, "Root of directory that stores acl tags for /proc files.");
46
47 MODULE_AUTHOR("Sapan Bhatia <sapanb@cs.princeton.edu>");
48 MODULE_DESCRIPTION("Lightweight ACLs for /proc.");
49 MODULE_LICENSE("GPL");
50 MODULE_VERSION(VERSION_STR);
51
52 struct procprotect_ctx {
53     struct inode **inode;
54     struct qstr *q;
55     struct path *path;
56     int flags;
57 };
58
59 struct acl_entry {
60     unsigned int ino;
61     struct hlist_node hlist;
62 };
63
64 #define HASH_SIZE (1<<16)
65
66 struct hlist_head procprotect_hash[HASH_SIZE];
67
68 struct proc_dir_entry *proc_entry;
69
70 static int run_acl(unsigned long ino) {
71     struct hlist_node *n;
72     struct acl_entry *entry;
73     hlist_for_each_entry_rcu(entry, 
74             n, &procprotect_hash[ino & (HASH_SIZE-1)],
75             hlist) {
76         if (entry->ino==ino) {
77             return 0;
78         }
79     }
80     return 1;
81 }
82
83 /*
84    Entry point of intercepted call. We need to do two things here:
85    - Decide if we need the heavier return hook to be called
86    - Save the first argument, which is in a register, for consideration in the return hook
87    */
88 static int lookup_fast_entry(struct kretprobe_instance *ri, struct pt_regs *regs) {
89     int ret = -1;
90     struct procprotect_ctx *ctx;
91     struct nameidata *nd = (struct nameidata *) regs->di;
92     struct qstr *q = (struct qstr *) regs->si;
93     struct dentry *parent = nd->path.dentry;
94     struct inode *pinode = parent->d_inode;
95
96     if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC
97             && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {   
98         ctx = (struct procprotect_ctx *) ri->data;
99         ctx->inode = regs->cx;
100         ctx->flags = nd->flags;
101         ret = 0;
102     }
103
104     return ret;
105 }
106
107 /* The entry hook ensures that the return hook is only called for
108    accesses to /proc */
109
110 int printed=0;
111
112 static int lookup_fast_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
113 {
114     struct procprotect_ctx *ctx = (struct procprotect_ctx *) ri->data;
115     int ret = regs->ax;
116
117     if (ret==0) {
118         /* The kernel is going to honor the request. Here's where we step in */
119         struct inode *inode = *(ctx->inode);
120         if (!run_acl(inode->i_ino)) {
121             regs->ax = -EPERM;
122         }
123     }
124
125
126     return 0;
127 }
128
129 static int lookup_slow_entry(struct kretprobe_instance *ri, struct pt_regs *regs) {
130     int ret = -1;
131     struct procprotect_ctx *ctx;
132     struct nameidata *nd = (struct nameidata *) regs->di;
133     struct qstr *q = (struct qstr *) regs->si;
134     struct path *p = (struct path *) regs->dx;
135
136     struct dentry *parent = nd->path.dentry;
137     struct inode *pinode = parent->d_inode;
138
139     
140
141     if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC
142             && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {   
143         
144         ctx = (struct procprotect_ctx *) ri->data;
145         ctx->q = q;
146         ctx->flags = nd->flags;
147         ctx->path = p;
148         ret = 0;
149     }
150
151     return ret;
152 }
153
154 /* The entry hook ensures that the return hook is only called for
155    accesses to /proc */
156
157 static int lookup_slow_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
158 {
159     struct procprotect_ctx *ctx = (struct procprotect_ctx *) ri->data;
160     int ret = regs->ax;
161
162     if (ret==0) {
163         /* The kernel is going to honor the request. Here's where we step in */
164         /*struct qstr *q = ctx->q;
165         if (!strncmp(q->name,"sysrq-trigger",13)) {
166             printk(KERN_CRIT "lookup_slow sysrqtrigger");
167         }*/
168         struct path *p = ctx->path;
169         struct inode *inode = p->dentry->d_inode;
170         if (!run_acl(inode->i_ino)) {
171             regs->ax = -EPERM;
172         }
173     }
174
175     return 0;
176 }
177
178 struct open_flags {
179   int open_flag;
180   umode_t mode;
181   int acc_mode;
182   int intent;
183 };
184
185 static struct file *do_last_probe(struct nameidata *nd, struct path *path, struct file *file,
186                          struct open_flags *op, const char *pathname) {
187     struct dentry *parent = nd->path.dentry;
188     struct inode *pinode = parent->d_inode;
189     struct qstr *q = &nd->last;
190
191     
192     if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {
193         /*if (!strncmp(q->name,"sysrq-trigger",13)) {
194             printk(KERN_CRIT "do_last sysrqtrigger: %d",op->open_flag);
195         }*/
196         op->open_flag &= ~O_CREAT;
197     }
198     jprobe_return();
199 }
200
201 static struct jprobe dolast_probe = {
202         .entry = (kprobe_opcode_t *) do_last_probe
203 };
204
205 static struct kretprobe fast_probe = {
206     .entry_handler = (kprobe_opcode_t *) lookup_fast_entry,
207     .handler = (kprobe_opcode_t *) lookup_fast_ret,
208     .maxactive = 20,
209     .data_size = sizeof(struct procprotect_ctx)
210 };
211
212 static struct kretprobe slow_probe = {
213     .entry_handler = (kprobe_opcode_t *) lookup_slow_entry,
214     .handler = (kprobe_opcode_t *) lookup_slow_ret,
215     .maxactive = 20,
216     .data_size = sizeof(struct procprotect_ctx)
217 };
218
219 static void add_entry(char *pathname) {
220     struct path path;
221     if (kern_path(pathname, 0, &path)) {
222         printk(KERN_CRIT "Path lookup failed for %s",pathname);
223     }   
224     else {
225         unsigned int ino = path.dentry->d_inode->i_ino;
226         struct acl_entry *entry;
227         entry = kmalloc(GFP_KERNEL, sizeof(struct acl_entry));
228         entry->ino = ino;
229
230         if (!entry) {
231             printk(KERN_CRIT "Could not allocate memory for %s",pathname);
232         }
233         else {
234             if (run_acl(ino)) {
235                 hlist_add_head_rcu(&entry->hlist,&procprotect_hash[ino&(HASH_SIZE-1)]);
236                 printk(KERN_CRIT "Added inode %u",ino);
237             }
238             else {
239                 printk(KERN_CRIT "Did not add inode %u, already in list", ino);
240             }
241         }
242     }
243 }
244
245
246 static void __exit procprotect_exit(void)
247 {
248     unregister_kretprobe(&fast_probe);
249     unregister_kretprobe(&slow_probe);
250         unregister_jprobe(&dolast_probe);
251     struct hlist_node *n;
252     struct acl_entry *entry;
253     int i;
254
255     for (i=0;i<HASH_SIZE;i++) {
256         hlist_for_each_entry_rcu(entry, 
257                 n, &procprotect_hash[i],
258                 hlist) {
259             kfree(entry);
260         }
261     }
262
263     remove_proc_entry("procprotect",NULL);
264     printk("Procprotect: Stopped procprotect.\n");
265 }
266
267
268
269 int procfile_write(struct file *file, const char *buffer, unsigned long count, void *data) {            
270     char pathname[PATH_MAX];
271
272     if (current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns)
273         return -EPERM;
274
275     if (copy_from_user(pathname, buffer, count)) {
276         return -EFAULT;
277     }
278     if (count && (pathname[count-1]==10 || pathname[count-1]==13)) {
279         pathname[count-1]='\0';
280     }
281     else
282         pathname[count]='\0';
283
284     add_entry(pathname);        
285     printk(KERN_CRIT "Length of buffer=%d",strlen(pathname));
286     return count;
287 }
288
289 static int __init procprotect_init(void)
290 {
291     printk("Procprotect: starting procprotect version %s with ACLs at path %s.\n",
292             VERSION_STR, aclpath);
293     int ret;
294     int i;
295
296     for(i=0;i<HASH_SIZE;i++) {
297         INIT_HLIST_HEAD(&procprotect_hash[i]);
298     }
299
300     aclqpath.name = aclpath;
301     aclqpath.len = strnlen(aclpath, PATH_MAX);
302
303     dolast_probe.kp.addr = 
304         (kprobe_opcode_t *) kallsyms_lookup_name("do_last");
305
306     if (!dolast_probe.kp.addr) {
307         printk("Couldn't find %s to plant kretprobe\n", "do_last");
308         return -1;
309     }
310
311     if ((ret = register_jprobe(&dolast_probe)) <0) {
312                   printk("register_jprobe failed, returned %u\n", ret);
313                   return -1;
314     }
315     fast_probe.kp.addr = 
316         (kprobe_opcode_t *) kallsyms_lookup_name("lookup_fast");
317     if (!fast_probe.kp.addr) {
318         printk("Couldn't find %s to plant kretprobe\n", "lookup_fast");
319         return -1;
320     }
321
322     slow_probe.kp.addr = 
323         (kprobe_opcode_t *) kallsyms_lookup_name("lookup_slow");
324     if (!slow_probe.kp.addr) {
325         printk("Couldn't find %s to plant kretprobe\n", "lookup_slow");
326         return -1;
327     }
328
329     
330
331     if ((ret = register_kretprobe(&fast_probe)) <0) {
332         printk("register_kretprobe failed, returned %d\n", ret);
333         return -1;
334     }
335
336     printk("Planted kretprobe at %p, handler addr %p\n",
337             fast_probe.kp.addr, fast_probe.handler);
338
339     if ((ret = register_kretprobe(&slow_probe)) <0) {
340         printk("register_kretprobe failed, returned %d\n", ret);
341         return -1;
342     }
343     printk("Planted kretprobe at %p, handler addr %p\n",
344             slow_probe.kp.addr, slow_probe.handler);
345
346     proc_entry = create_proc_entry("procprotect", 0644, NULL);
347     proc_entry->write_proc = procfile_write;
348
349     return ret;
350 }
351
352
353
354 module_init(procprotect_init);
355 module_exit(procprotect_exit);