Since the struct nameidata became opaque in recent changes of the
[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 #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 */
36
37 #define VERSION_STR "0.0.1"
38
39 #ifndef CONFIG_X86_64
40 #error "This code does not support your architecture"
41 #endif
42
43 static char *aclpath = "procprotect";
44
45 static struct qstr aclqpath;
46
47 module_param(aclpath, charp, 0);
48 MODULE_PARM_DESC(aclpath, "Root of directory that stores acl tags for /proc files.");
49
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);
54
55 struct procprotect_ctx {
56         struct inode **inode;
57         struct qstr *q;
58         struct path *path;
59         int flags;
60 };
61
62 struct acl_entry {
63         unsigned int ino;
64         struct hlist_node hlist;
65 };
66
67 /*
68    Added by Guilherme Sperb Machado <gsm@machados.org>
69    According to recent changes in the kernel, the nameidata struct
70    became opaque. So, let's declare it in our implementation.
71    Source: https://github.com/torvalds/linux/commit/1f55a6ec940fb45e3edaa52b6e9fc40cf8e18dcb
72    */
73 struct nameidata {
74         struct path     path;
75         struct qstr     last;
76         struct path     root;
77         struct inode    *inode; /* path.dentry.d_inode */
78         unsigned int    flags;
79         unsigned        seq;
80         int             last_type;
81         unsigned        depth;
82         char *saved_names[MAX_NESTED_LINKS + 1];
83 };
84
85 #define HASH_SIZE (1<<10)
86
87 struct hlist_head procprotect_hash[HASH_SIZE];
88
89 struct proc_dir_entry *proc_entry;
90
91 static int run_acl(unsigned long ino) {
92         struct acl_entry *entry;
93         hlist_for_each_entry_rcu_notrace(entry,
94                                  &procprotect_hash[ino & (HASH_SIZE-1)],
95                                  hlist) {
96                 if (entry->ino==ino) {
97                         return 0;
98                 }
99         }
100         return 1;
101 }
102
103 /*
104    Entry point of intercepted call. We need to do two things here:
105    - Decide if we need the heavier return hook to be called
106    - Save the first argument, which is in a register, for consideration in the return hook
107    */
108 static int lookup_fast_entry(struct kretprobe_instance *ri, struct pt_regs *regs) {
109         int ret = -1;
110         struct procprotect_ctx *ctx;
111         struct nameidata *nd = (struct nameidata *) regs->di;
112         struct dentry *parent;
113         struct inode *pinode;
114
115         if (!nd) return ret;
116         parent = nd->path.dentry;
117
118         if (!parent) return ret;
119         pinode = parent->d_inode;
120
121         if (!pinode || !pinode->i_sb || !current || !current->nsproxy) return ret;
122
123         if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC
124                         && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {
125                 ctx = (struct procprotect_ctx *) ri->data;
126                 ctx->inode = (struct inode **)regs->dx;
127                 ctx->flags = nd->flags;
128                 ret = 0;
129         }
130
131         return ret;
132 }
133
134 /* The entry hook ensures that the return hook is only called for
135    accesses to /proc */
136
137 int printed=0;
138
139 static int lookup_fast_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
140 {
141         struct procprotect_ctx *ctx = (struct procprotect_ctx *) ri->data;
142         int ret = regs->ax;
143
144         if (ret==0) {
145                 /* The kernel is going to honor the request. Here's where we step in */
146                 struct inode *inode = *(ctx->inode);
147                 if (!run_acl(inode->i_ino)) {
148                         regs->ax = -EPERM;
149                 }
150         }
151
152
153         return 0;
154 }
155
156 static int lookup_slow_entry(struct kretprobe_instance *ri, struct pt_regs *regs) {
157         int ret = -1;
158         struct procprotect_ctx *ctx;
159         struct nameidata *nd = (struct nameidata *) regs->di;
160         struct path *p = (struct path *) regs->si;
161         struct dentry *parent;
162         struct inode *pinode;
163
164         if (!nd) return ret;
165         parent = nd->path.dentry;
166         if (!parent) return ret;
167         pinode= parent->d_inode;
168
169         if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC
170                         && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {
171
172                 ctx = (struct procprotect_ctx *) ri->data;
173                 ctx->q = &nd->last;
174                 ctx->flags = nd->flags;
175                 ctx->path = p;
176                 ret = 0;
177         }
178
179         return ret;
180 }
181
182 /* The entry hook ensures that the return hook is only called for
183    accesses to /proc */
184
185 /*static int print_once = 0;*/
186
187 static int lookup_slow_ret(struct kretprobe_instance *ri, struct pt_regs *regs)
188 {
189         struct procprotect_ctx *ctx;
190         struct inode *inode;
191         int ret;
192
193         if (!ri || !ri->data) {return 0;}
194         ctx = (struct procprotect_ctx *) ri->data;
195
196         ret = regs->ax;
197
198         if (ret==0) {
199                 struct path *p = ctx->path;
200                 if (!p || !p->dentry || !p->dentry->d_inode /* This last check was responsible for the f18 bug*/) {
201                         return 0;
202                 }
203                 inode = p->dentry->d_inode;
204                 if (!run_acl(inode->i_ino)) {
205                         regs->ax = -EPERM;
206                 }
207         }
208
209         return 0;
210 }
211
212 struct open_flags {
213   int open_flag;
214   umode_t mode;
215   int acc_mode;
216   int intent;
217 };
218
219 static struct file *do_last_probe(struct nameidata *nd, struct path *path, struct file *file,
220                                                  struct open_flags *op, const char *pathname) {
221         struct dentry *parent = nd->path.dentry;
222         struct inode *pinode = parent->d_inode;
223         /*struct qstr *q = &nd->last;*/
224
225
226         if (pinode->i_sb->s_magic == PROC_SUPER_MAGIC && current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns) {
227                 /*if (!strncmp(q->name,"sysrq-trigger",13)) {
228                         printk(KERN_CRIT "do_last sysrqtrigger: %d",op->open_flag);
229                 }*/
230                 op->open_flag &= ~O_CREAT;
231         }
232         jprobe_return();
233         return file;
234 }
235
236 static struct jprobe dolast_probe = {
237         .entry = do_last_probe
238 };
239
240 static struct kretprobe fast_probe = {
241         .entry_handler = lookup_fast_entry,
242         .handler = lookup_fast_ret,
243         .maxactive = 20,
244         .data_size = sizeof(struct procprotect_ctx)
245 };
246
247 static struct kretprobe slow_probe = {
248         .entry_handler = lookup_slow_entry,
249         .handler = lookup_slow_ret,
250         .maxactive = 20,
251         .data_size = sizeof(struct procprotect_ctx)
252 };
253
254 int once_only = 0;
255
256 static int init_probes(void) {
257         int ret;
258         dolast_probe.kp.addr =
259                 (kprobe_opcode_t *) kallsyms_lookup_name("do_last");
260
261         if (!dolast_probe.kp.addr) {
262                 printk("Couldn't find %s to plant kretprobe\n", "do_last");
263                 return -1;
264         }
265
266         if ((ret = register_jprobe(&dolast_probe)) <0) {
267                 printk("register_jprobe failed, returned %u\n", ret);
268                 return -1;
269         }
270         fast_probe.kp.addr =
271                 (kprobe_opcode_t *) kallsyms_lookup_name("lookup_fast");
272
273         if (!fast_probe.kp.addr) {
274                 printk("Couldn't find %s to plant kretprobe\n", "lookup_fast");
275                 return -1;
276         }
277
278         slow_probe.kp.addr =
279                 (kprobe_opcode_t *) kallsyms_lookup_name("lookup_slow");
280
281         if (!slow_probe.kp.addr) {
282                 printk("Couldn't find %s to plant kretprobe\n", "lookup_slow");
283                 return -1;
284         }
285
286         if ((ret = register_kretprobe(&fast_probe)) <0) {
287                 printk("register_kretprobe failed, returned %d\n", ret);
288                 return -1;
289         }
290
291         printk("Planted kretprobe at %p, handler addr %p\n",
292                         fast_probe.kp.addr, fast_probe.handler);
293
294         if ((ret = register_kretprobe(&slow_probe)) <0) {
295                 printk("register_kretprobe failed, returned %d\n", ret);
296                 return -1;
297         }
298         printk("Planted kretprobe at %p, handler addr %p\n",
299                         slow_probe.kp.addr, slow_probe.handler);
300         return 0;
301 }
302
303 static void add_entry(char *pathname) {
304         struct path path;
305         if (kern_path(pathname, 0, &path)) {
306                 printk(KERN_CRIT "Path lookup failed for %s",pathname);
307         }
308         else {
309                 unsigned int ino = path.dentry->d_inode->i_ino;
310                 struct acl_entry *entry;
311                 entry = kmalloc(GFP_KERNEL, sizeof(struct acl_entry));
312                 entry->ino = ino;
313
314                 if (!entry) {
315                         printk(KERN_CRIT "Could not allocate memory for %s",pathname);
316                 }
317                 else {
318                         if (run_acl(ino)) {
319                                 hlist_add_head_rcu(&entry->hlist,&procprotect_hash[ino&(HASH_SIZE-1)]);
320                                 printk(KERN_CRIT "Added inode %u",ino);
321                         }
322                         else {
323                                 printk(KERN_CRIT "Did not add inode %u, already in list", ino);
324                         }
325                 }
326         }
327
328 }
329
330
331 static void __exit procprotect_exit(void)
332 {
333         struct acl_entry *entry;
334         int i;
335
336         unregister_kretprobe(&fast_probe);
337         unregister_kretprobe(&slow_probe);
338         unregister_jprobe(&dolast_probe);
339
340         for (i=0;i<HASH_SIZE;i++) {
341                 hlist_for_each_entry_rcu(entry,
342                                  &procprotect_hash[i],
343                                  hlist) {
344                         kfree(entry);
345                 }
346         }
347
348         remove_proc_entry("procprotect",NULL);
349         printk("Procprotect: Stopped procprotect.\n");
350 }
351
352
353
354 ssize_t procfile_write(struct file *file, const char *buffer, size_t count, loff_t *data) {
355         char *pathname;
356         pathname = (char *) kmalloc(count, GFP_KERNEL);
357
358         if (!pathname) {
359                 printk(KERN_CRIT "Could not allocate memory for pathname buffer");
360                 return -EFAULT;
361         }
362
363         if (current->nsproxy->mnt_ns!=init_task.nsproxy->mnt_ns)
364                 return -EPERM;
365
366         if (copy_from_user(pathname, buffer, count)) {
367                 return -EFAULT;
368         }
369         if (count && (pathname[count-1]==10 || pathname[count-1]==13)) {
370                 pathname[count-1]='\0';
371         }
372         else
373                 pathname[count]='\0';
374
375         add_entry(pathname);
376
377         if (!once_only) {
378                 once_only=1;
379                 if (init_probes()==-1)
380                         printk(KERN_CRIT "Could not install procprotect probes. Reload module to retry.");
381         }
382         printk(KERN_CRIT "Length of buffer=%d",(int)strlen(pathname));
383         kfree(pathname);
384         return count;
385 }
386
387 static const struct file_operations procprotect_fops = {
388         .owner = THIS_MODULE,
389         .write = procfile_write
390 };
391
392
393 static int __init procprotect_init(void)
394 {
395         int ret = 0;
396         int i;
397
398         printk("Procprotect: starting procprotect version %s with ACLs at path %s.\n",
399                         VERSION_STR, aclpath);
400
401         for(i=0;i<HASH_SIZE;i++) {
402                 INIT_HLIST_HEAD(&procprotect_hash[i]);
403         }
404
405         aclqpath.name = aclpath;
406         aclqpath.len = strnlen(aclpath, PATH_MAX);
407
408         proc_entry = proc_create("procprotect", 0644, NULL, &procprotect_fops);
409
410         return ret;
411 }
412
413
414
415 module_init(procprotect_init);
416 module_exit(procprotect_exit);