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