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