1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18#include <linux/config.h>
19#include <linux/module.h>
20
21#include <linux/kernel.h>
22#include <linux/errno.h>
23#include <linux/fs.h>
24#include <linux/malloc.h>
25#include <linux/binfmts.h>
26#include <linux/init.h>
27#include <linux/proc_fs.h>
28#include <linux/string.h>
29#include <linux/ctype.h>
30#include <asm/uaccess.h>
31#include <asm/spinlock.h>
32
33
34
35
36
37
38
39
40#ifndef CONFIG_PROC_FS
41#error You really need /proc support for binfmt_misc. Please reconfigure!
42#endif
43
44enum {
45 VERBOSE_STATUS = 1
46};
47
48typedef struct binfmt_entry {
49 struct binfmt_entry *next;
50 long id;
51 long flags;
52 int offset;
53 int size;
54 char *magic;
55 char *mask;
56 char *interpreter;
57 char *name;
58 struct proc_dir_entry *proc_dir;
59} Node;
60
61enum { Enabled, Magic };
62
63static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs);
64static void entry_proc_cleanup(Node *e);
65static int entry_proc_setup(Node *e);
66
67static struct linux_binfmt misc_format = {
68 module: THIS_MODULE,
69 load_binary: load_misc_binary,
70};
71
72static struct proc_dir_entry *bm_dir = NULL;
73
74static Node *entries = NULL;
75static int free_id = 1;
76static int enabled = 1;
77
78static rwlock_t entries_lock __attribute__((unused)) = RW_LOCK_UNLOCKED;
79
80
81
82
83
84static void clear_entry(int id)
85{
86 Node **ep, *e;
87
88 write_lock(&entries_lock);
89 ep = &entries;
90 while (*ep && ((*ep)->id != id))
91 ep = &((*ep)->next);
92 if ((e = *ep))
93 *ep = e->next;
94 write_unlock(&entries_lock);
95
96 if (e) {
97 entry_proc_cleanup(e);
98 kfree(e);
99 }
100}
101
102
103
104
105static void clear_entries(void)
106{
107 Node *e, *n;
108
109 write_lock(&entries_lock);
110 n = entries;
111 entries = NULL;
112 write_unlock(&entries_lock);
113
114 while ((e = n)) {
115 n = e->next;
116 entry_proc_cleanup(e);
117 kfree(e);
118 }
119}
120
121
122
123
124static Node *get_entry(int id)
125{
126 Node *e;
127
128 read_lock(&entries_lock);
129 e = entries;
130 while (e && (e->id != id))
131 e = e->next;
132 if (!e)
133 read_unlock(&entries_lock);
134 return e;
135}
136
137
138
139
140static inline void put_entry(Node *e)
141{
142 if (e)
143 read_unlock(&entries_lock);
144}
145
146
147
148
149
150
151
152static Node *check_file(struct linux_binprm *bprm)
153{
154 Node *e;
155 char *p = strrchr(bprm->filename, '.');
156 int j;
157
158 e = entries;
159 while (e) {
160 if (test_bit(Enabled, &e->flags)) {
161 if (!test_bit(Magic, &e->flags)) {
162 if (p && !strcmp(e->magic, p + 1))
163 return e;
164 } else {
165 j = 0;
166 while ((j < e->size) &&
167 !((bprm->buf[e->offset + j] ^ e->magic[j])
168 & (e->mask ? e->mask[j] : 0xff)))
169 j++;
170 if (j == e->size)
171 return e;
172 }
173 }
174 e = e->next;
175 };
176 return NULL;
177}
178
179
180
181
182static int load_misc_binary(struct linux_binprm *bprm, struct pt_regs *regs)
183{
184 Node *fmt;
185 struct dentry * dentry;
186 char iname[128];
187 char *iname_addr = iname;
188 int retval;
189
190 retval = -ENOEXEC;
191 if (!enabled)
192 goto _ret;
193
194
195 read_lock(&entries_lock);
196 fmt = check_file(bprm);
197 if (fmt) {
198 strncpy(iname, fmt->interpreter, 127);
199 iname[127] = '\0';
200 }
201 read_unlock(&entries_lock);
202 if (!fmt)
203 goto _ret;
204
205 dput(bprm->dentry);
206 bprm->dentry = NULL;
207
208
209 remove_arg_zero(bprm);
210 bprm->p = copy_strings(1, &bprm->filename, bprm->page, bprm->p, 2);
211 bprm->argc++;
212 bprm->p = copy_strings(1, &iname_addr, bprm->page, bprm->p, 2);
213 bprm->argc++;
214 retval = (long)bprm->p;
215 if ((long)bprm->p < 0)
216 goto _ret;
217 bprm->filename = iname;
218
219 dentry = open_namei(iname, 0, 0);
220 retval = PTR_ERR(dentry);
221 if (IS_ERR(dentry))
222 goto _ret;
223 bprm->dentry = dentry;
224
225 retval = prepare_binprm(bprm);
226 if (retval >= 0)
227 retval = search_binary_handler(bprm, regs);
228_ret:
229 return retval;
230}
231
232
233
234
235
236
237
238static char *scanarg(char *s, char del)
239{
240 char c;
241
242 while ((c = *s++) != del) {
243 if (c == '\\' && *s == 'x') {
244 s++;
245 if (!isxdigit(*s++))
246 return NULL;
247 if (!isxdigit(*s++))
248 return NULL;
249 }
250 }
251 return s;
252}
253
254static int unquote(char *from)
255{
256 char c = 0, *s = from, *p = from;
257
258 while ((c = *s++) != '\0') {
259 if (c == '\\' && *s == 'x') {
260 s++;
261 c = toupper(*s++);
262 *p = (c - (isdigit(c) ? '0' : 'A' - 10)) << 4;
263 c = toupper(*s++);
264 *p++ |= c - (isdigit(c) ? '0' : 'A' - 10);
265 continue;
266 }
267 *p++ = c;
268 }
269 return p - from;
270}
271
272
273
274
275
276
277static Node *create_entry(const char *buffer, size_t count)
278{
279 Node *e;
280 int memsize, err;
281 char *buf, *p;
282 char del;
283
284
285 err = -EINVAL;
286 if ((count < 11) || (count > 256))
287 goto out;
288
289 err = -ENOMEM;
290 memsize = sizeof(Node) + count + 8;
291 e = (Node *) kmalloc(memsize, GFP_USER);
292 if (!e)
293 goto out;
294
295 p = buf = (char *)e + sizeof(Node);
296
297 memset(e, 0, sizeof(Node));
298 if (copy_from_user(buf, buffer, count))
299 goto Efault;
300
301 del = *p++;
302
303 memset(buf+count, del, 8);
304
305 e->name = p;
306 p = strchr(p, del);
307 if (!p)
308 goto Einval;
309 *p++ = '\0';
310 if (!e->name[0] ||
311 !strcmp(e->name, ".") ||
312 !strcmp(e->name, "..") ||
313 strchr(e->name, '/'))
314 goto Einval;
315 switch (*p++) {
316 case 'E': e->flags = 1<<Enabled; break;
317 case 'M': e->flags = (1<<Enabled) | (1<<Magic); break;
318 default: goto Einval;
319 }
320 if (*p++ != del)
321 goto Einval;
322 if (test_bit(Magic, &e->flags)) {
323 char *s = strchr(p, del);
324 if (!s)
325 goto Einval;
326 *s++ = '\0';
327 e->offset = simple_strtoul(p, &p, 10);
328 if (*p++)
329 goto Einval;
330 e->magic = p;
331 p = scanarg(p, del);
332 if (!p)
333 goto Einval;
334 p[-1] = '\0';
335 if (!e->magic[0])
336 goto Einval;
337 e->mask = p;
338 p = scanarg(p, del);
339 if (!p)
340 goto Einval;
341 p[-1] = '\0';
342 if (!e->mask[0])
343 e->mask = NULL;
344 e->size = unquote(e->magic);
345 if (e->mask && unquote(e->mask) != e->size)
346 goto Einval;
347 if (e->size + e->offset > 128)
348 goto Einval;
349 } else {
350 p = strchr(p, del);
351 if (!p)
352 goto Einval;
353 *p++ = '\0';
354 e->magic = p;
355 p = strchr(p, del);
356 if (!p)
357 goto Einval;
358 *p++ = '\0';
359 if (!e->magic[0] || strchr(e->magic, '/'))
360 goto Einval;
361 p = strchr(p, del);
362 if (!p)
363 goto Einval;
364 *p++ = '\0';
365 }
366 e->interpreter = p;
367 p = strchr(p, del);
368 if (!p)
369 goto Einval;
370 *p++ = '\0';
371 if (!e->interpreter[0])
372 goto Einval;
373
374 if (*p == '\n')
375 p++;
376 if (p != buf + count)
377 goto Einval;
378 return e;
379
380out:
381 return ERR_PTR(err);
382
383Efault:
384 kfree(e);
385 return ERR_PTR(-EFAULT);
386Einval:
387 kfree(e);
388 return ERR_PTR(-EINVAL);
389}
390
391
392
393
394
395static int parse_command(const char *buffer, size_t count)
396{
397 char s[4];
398
399 if (!count)
400 return 0;
401 if (count > 3)
402 return -EINVAL;
403 if (copy_from_user(s, buffer, count))
404 return -EFAULT;
405 if (s[count-1] == '\n')
406 count--;
407 if (count == 1 && s[0] == '0')
408 return 1;
409 if (count == 1 && s[0] == '1')
410 return 2;
411 if (count == 2 && s[0] == '-' && s[1] == '1')
412 return 3;
413 return -EINVAL;
414}
415
416static void entry_status(Node *e, char *page)
417{
418 char *dp;
419 char *status = "disabled";
420
421 if (test_bit(Enabled, &e->flags))
422 status = "enabled";
423
424 if (!VERBOSE_STATUS) {
425 sprintf(page, "%s\n", status);
426 return;
427 }
428
429 sprintf(page, "%s\ninterpreter %s\n", status, e->interpreter);
430 dp = page + strlen(page);
431 if (!test_bit(Magic, &e->flags)) {
432 sprintf(dp, "extension .%s\n", e->magic);
433 } else {
434 int i;
435
436 sprintf(dp, "offset %i\nmagic ", e->offset);
437 dp = page + strlen(page);
438 for (i = 0; i < e->size; i++) {
439 sprintf(dp, "%02x", 0xff & (int) (e->magic[i]));
440 dp += 2;
441 }
442 if (e->mask) {
443 sprintf(dp, "\nmask ");
444 dp += 6;
445 for (i = 0; i < e->size; i++) {
446 sprintf(dp, "%02x", 0xff & (int) (e->mask[i]));
447 dp += 2;
448 }
449 }
450 *dp++ = '\n';
451 *dp = '\0';
452 }
453}
454
455static int proc_write_register(struct file *file, const char *buffer,
456 unsigned long count, void *data)
457{
458 Node *e = create_entry(buffer, count);
459 int err;
460
461 if (IS_ERR(e))
462 return PTR_ERR(e);
463
464 if (entry_proc_setup(e))
465 goto free_err;
466
467 e->id = free_id++;
468 write_lock(&entries_lock);
469 e->next = entries;
470 entries = e;
471 write_unlock(&entries_lock);
472
473 err = count;
474_err:
475 return err;
476free_err:
477 kfree(e);
478 err = -EINVAL;
479 goto _err;
480}
481
482
483
484
485
486
487static int proc_read_status(char *page, char **start, off_t off,
488 int count, int *eof, void *data)
489{
490 Node *e;
491 int elen;
492
493 if (!data) {
494 sprintf(page, "%s\n", (enabled ? "enabled" : "disabled"));
495 } else {
496 if (!(e = get_entry((long) data)))
497 return -ENOENT;
498 entry_status(e, page);
499 put_entry(e);
500 }
501
502 elen = strlen(page) - off;
503 if (elen < 0)
504 elen = 0;
505 *eof = (elen <= count) ? 1 : 0;
506 *start = page + off;
507 return elen;
508}
509
510
511
512
513
514static int proc_write_status(struct file *file, const char *buffer,
515 unsigned long count, void *data)
516{
517 Node *e;
518 int res = parse_command(buffer, count);
519
520 switch(res) {
521 case 1: if (data) {
522 if ((e = get_entry((long) data)))
523 clear_bit(Enabled, &e->flags);
524 put_entry(e);
525 } else {
526 enabled = 0;
527 }
528 break;
529 case 2: if (data) {
530 if ((e = get_entry((long) data)))
531 set_bit(Enabled, &e->flags);
532 put_entry(e);
533 } else {
534 enabled = 1;
535 }
536 break;
537 case 3: if (data)
538 clear_entry((long) data);
539 else
540 clear_entries();
541 break;
542 default: return res;
543 }
544 return count;
545}
546
547
548
549
550static void entry_proc_cleanup(Node *e)
551{
552 remove_proc_entry(e->name, bm_dir);
553}
554
555
556
557
558static int entry_proc_setup(Node *e)
559{
560 if (!(e->proc_dir = create_proc_entry(e->name,
561 S_IFREG | S_IRUGO | S_IWUSR, bm_dir)))
562 {
563 printk(KERN_WARNING "Unable to create /proc entry.\n");
564 return -ENOENT;
565 }
566 e->proc_dir->data = (void *) (e->id);
567 e->proc_dir->read_proc = proc_read_status;
568 e->proc_dir->write_proc = proc_write_status;
569 return 0;
570}
571
572#ifdef MODULE
573
574
575
576
577
578
579
580
581static void bm_modcount(struct inode *inode, int fill)
582{
583 if (fill)
584 MOD_INC_USE_COUNT;
585 else
586 MOD_DEC_USE_COUNT;
587}
588#endif
589
590int __init init_misc_binfmt(void)
591{
592 int error = -ENOENT;
593 struct proc_dir_entry *status = NULL, *reg;
594
595 bm_dir = create_proc_entry("sys/fs/binfmt_misc", S_IFDIR, NULL);
596 if (!bm_dir)
597 goto out;
598#ifdef MODULE
599 bm_dir->fill_inode = bm_modcount;
600#endif
601
602 status = create_proc_entry("status", S_IFREG | S_IRUGO | S_IWUSR,
603 bm_dir);
604 if (!status)
605 goto cleanup_bm;
606 status->read_proc = proc_read_status;
607 status->write_proc = proc_write_status;
608
609 reg = create_proc_entry("register", S_IFREG | S_IWUSR, bm_dir);
610 if (!reg)
611 goto cleanup_status;
612 reg->write_proc = proc_write_register;
613
614 error = register_binfmt(&misc_format);
615out:
616 return error;
617
618cleanup_status:
619 remove_proc_entry("status", bm_dir);
620cleanup_bm:
621 remove_proc_entry("sys/fs/binfmt_misc", NULL);
622 goto out;
623}
624
625#ifdef MODULE
626EXPORT_NO_SYMBOLS;
627int init_module(void)
628{
629 return init_misc_binfmt();
630}
631
632void cleanup_module(void)
633{
634 unregister_binfmt(&misc_format);
635 remove_proc_entry("register", bm_dir);
636 remove_proc_entry("status", bm_dir);
637 clear_entries();
638 remove_proc_entry("sys/fs/binfmt_misc", NULL);
639}
640#endif
641