aboutsummaryrefslogtreecommitdiffstats
path: root/security/seclvl.c
diff options
context:
space:
mode:
authorChris Wright <chrisw@sous-sol.org>2006-09-29 04:59:49 -0400
committerLinus Torvalds <torvalds@g5.osdl.org>2006-09-29 12:18:10 -0400
commit3bc1fa8ae18f281b40903cce94baba10c3cf9d88 (patch)
tree9097244b28cbf4eba16368803272af0fc70224d5 /security/seclvl.c
parentcd1c6a48ac16b360746f9f111895931d332c35dd (diff)
[PATCH] LSM: remove BSD secure level security module
This code has suffered from broken core design and lack of developer attention. Broken security modules are too dangerous to leave around. It is time to remove this one. Signed-off-by: Chris Wright <chrisw@sous-sol.org> Acked-by: Michael Halcrow <mhalcrow@us.ibm.com> Acked-by: Serge Hallyn <serue@us.ibm.com> Cc: Davi Arnaut <davi.arnaut@gmail.com> Acked-by: Greg Kroah-Hartman <gregkh@suse.de> Acked-by: James Morris <jmorris@namei.org> Acked-by: Alan Cox <alan@redhat.com> Signed-off-by: Andrew Morton <akpm@osdl.org> Signed-off-by: Linus Torvalds <torvalds@osdl.org>
Diffstat (limited to 'security/seclvl.c')
-rw-r--r--security/seclvl.c671
1 files changed, 0 insertions, 671 deletions
diff --git a/security/seclvl.c b/security/seclvl.c
deleted file mode 100644
index 8f6291991fbc..000000000000
--- a/security/seclvl.c
+++ /dev/null
@@ -1,671 +0,0 @@
1/**
2 * BSD Secure Levels LSM
3 *
4 * Maintainers:
5 * Michael A. Halcrow <mike@halcrow.us>
6 * Serge Hallyn <hallyn@cs.wm.edu>
7 *
8 * Copyright (c) 2001 WireX Communications, Inc <chris@wirex.com>
9 * Copyright (c) 2001 Greg Kroah-Hartman <greg@kroah.com>
10 * Copyright (c) 2002 International Business Machines <robb@austin.ibm.com>
11 * Copyright (c) 2006 Davi E. M. Arnaut <davi.arnaut@gmail.com>
12 *
13 * This program is free software; you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation; either version 2 of the License, or
16 * (at your option) any later version.
17 */
18
19#include <linux/err.h>
20#include <linux/module.h>
21#include <linux/moduleparam.h>
22#include <linux/kernel.h>
23#include <linux/init.h>
24#include <linux/security.h>
25#include <linux/netlink.h>
26#include <linux/fs.h>
27#include <linux/namei.h>
28#include <linux/mount.h>
29#include <linux/capability.h>
30#include <linux/time.h>
31#include <linux/proc_fs.h>
32#include <linux/kobject.h>
33#include <linux/crypto.h>
34#include <asm/scatterlist.h>
35#include <linux/scatterlist.h>
36#include <linux/gfp.h>
37#include <linux/sysfs.h>
38
39#define SHA1_DIGEST_SIZE 20
40
41/**
42 * Module parameter that defines the initial secure level.
43 *
44 * When built as a module, it defaults to seclvl 1, which is the
45 * behavior of BSD secure levels. Note that this default behavior
46 * wrecks havoc on a machine when the seclvl module is compiled into
47 * the kernel. In that case, we default to seclvl 0.
48 */
49#ifdef CONFIG_SECURITY_SECLVL_MODULE
50static int initlvl = 1;
51#else
52static int initlvl;
53#endif
54module_param(initlvl, int, 0);
55MODULE_PARM_DESC(initlvl, "Initial secure level (defaults to 1)");
56
57/* Module parameter that defines the verbosity level */
58static int verbosity;
59module_param(verbosity, int, 0);
60MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults to "
61 "0, which is Quiet)");
62
63/**
64 * Optional password which can be passed in to bring seclvl to 0
65 * (i.e., for halt/reboot). Defaults to NULL (the passwd attribute
66 * file will not be registered in sysfs).
67 *
68 * This gets converted to its SHA1 hash when stored. It's probably
69 * not a good idea to use this parameter when loading seclvl from a
70 * script; use sha1_passwd instead.
71 */
72
73#define MAX_PASSWD_SIZE 32
74static char passwd[MAX_PASSWD_SIZE];
75module_param_string(passwd, passwd, sizeof(passwd), 0);
76MODULE_PARM_DESC(passwd,
77 "Plaintext of password that sets seclvl=0 when written to "
78 "(sysfs mount point)/seclvl/passwd\n");
79
80/**
81 * SHA1 hashed version of the optional password which can be passed in
82 * to bring seclvl to 0 (i.e., for halt/reboot). Must be in
83 * hexadecimal format (40 characters). Defaults to NULL (the passwd
84 * attribute file will not be registered in sysfs).
85 *
86 * Use the sha1sum utility to generate the SHA1 hash of a password:
87 *
88 * echo -n "secret" | sha1sum
89 */
90#define MAX_SHA1_PASSWD 41
91static char sha1_passwd[MAX_SHA1_PASSWD];
92module_param_string(sha1_passwd, sha1_passwd, sizeof(sha1_passwd), 0);
93MODULE_PARM_DESC(sha1_passwd,
94 "SHA1 hash (40 hexadecimal characters) of password that "
95 "sets seclvl=0 when plaintext password is written to "
96 "(sysfs mount point)/seclvl/passwd\n");
97
98static int hideHash = 1;
99module_param(hideHash, int, 0);
100MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs "
101 "will return the SHA1-hashed value of the password that "
102 "lowers the secure level to 0.\n");
103
104#define MY_NAME "seclvl"
105
106/**
107 * This time-limits log writes to one per second.
108 */
109#define seclvl_printk(verb, type, fmt, arg...) \
110 do { \
111 if (verbosity >= verb) { \
112 static unsigned long _prior; \
113 unsigned long _now = jiffies; \
114 if ((_now - _prior) > HZ) { \
115 printk(type "%s: %s: " fmt, \
116 MY_NAME, __FUNCTION__ , \
117 ## arg); \
118 _prior = _now; \
119 } \
120 } \
121 } while (0)
122
123/**
124 * The actual security level. Ranges between -1 and 2 inclusive.
125 */
126static int seclvl;
127
128/**
129 * flag to keep track of how we were registered
130 */
131static int secondary;
132
133/**
134 * Verifies that the requested secure level is valid, given the current
135 * secure level.
136 */
137static int seclvl_sanity(int reqlvl)
138{
139 if ((reqlvl < -1) || (reqlvl > 2)) {
140 seclvl_printk(1, KERN_WARNING, "Attempt to set seclvl out of "
141 "range: [%d]\n", reqlvl);
142 return -EINVAL;
143 }
144 if ((seclvl == 0) && (reqlvl == -1))
145 return 0;
146 if (reqlvl < seclvl) {
147 seclvl_printk(1, KERN_WARNING, "Attempt to lower seclvl to "
148 "[%d]\n", reqlvl);
149 return -EPERM;
150 }
151 return 0;
152}
153
154/**
155 * security level advancement rules:
156 * Valid levels are -1 through 2, inclusive.
157 * From -1, stuck. [ in case compiled into kernel ]
158 * From 0 or above, can only increment.
159 */
160static void do_seclvl_advance(void *data, u64 val)
161{
162 int ret;
163 int newlvl = (int)val;
164
165 ret = seclvl_sanity(newlvl);
166 if (ret)
167 return;
168
169 if (newlvl > 2) {
170 seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "
171 "[%d]\n", newlvl);
172 return;
173 }
174 if (seclvl == -1) {
175 seclvl_printk(1, KERN_WARNING, "Not allowed to advance to "
176 "seclvl [%d]\n", seclvl);
177 return;
178 }
179 seclvl = newlvl; /* would it be more "correct" to set *data? */
180 return;
181}
182
183static u64 seclvl_int_get(void *data)
184{
185 return *(int *)data;
186}
187
188DEFINE_SIMPLE_ATTRIBUTE(seclvl_file_ops, seclvl_int_get, do_seclvl_advance, "%lld\n");
189
190static unsigned char hashedPassword[SHA1_DIGEST_SIZE];
191
192/**
193 * Converts a block of plaintext of into its SHA1 hashed value.
194 *
195 * It would be nice if crypto had a wrapper to do this for us linear
196 * people...
197 */
198static int
199plaintext_to_sha1(unsigned char *hash, const char *plaintext, unsigned int len)
200{
201 struct hash_desc desc;
202 struct scatterlist sg;
203 int err;
204
205 if (len > PAGE_SIZE) {
206 seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d "
207 "characters). Largest possible is %lu "
208 "bytes.\n", len, PAGE_SIZE);
209 return -EINVAL;
210 }
211 desc.tfm = crypto_alloc_hash("sha1", 0, CRYPTO_ALG_ASYNC);
212 if (IS_ERR(desc.tfm)) {
213 seclvl_printk(0, KERN_ERR,
214 "Failed to load transform for SHA1\n");
215 return -EINVAL;
216 }
217 sg_init_one(&sg, (u8 *)plaintext, len);
218 desc.flags = CRYPTO_TFM_REQ_MAY_SLEEP;
219 err = crypto_hash_digest(&desc, &sg, len, hash);
220 crypto_free_hash(desc.tfm);
221 return err;
222}
223
224/**
225 * Called whenever the user writes to the sysfs passwd handle to this kernel
226 * object. It hashes the password and compares the hashed results.
227 */
228static ssize_t
229passwd_write_file(struct file * file, const char __user * buf,
230 size_t count, loff_t *ppos)
231{
232 char *p;
233 int len;
234 unsigned char tmp[SHA1_DIGEST_SIZE];
235
236 if (!*passwd && !*sha1_passwd) {
237 seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the "
238 "seclvl module, but neither a plain text "
239 "password nor a SHA1 hashed password was "
240 "passed in as a module parameter! This is a "
241 "bug, since it should not be possible to be in "
242 "this part of the module; please tell a "
243 "maintainer about this event.\n");
244 return -EINVAL;
245 }
246
247 if (count >= PAGE_SIZE)
248 return -EINVAL;
249 if (*ppos != 0)
250 return -EINVAL;
251 p = kmalloc(count, GFP_KERNEL);
252 if (!p)
253 return -ENOMEM;
254 len = -EFAULT;
255 if (copy_from_user(p, buf, count))
256 goto out;
257
258 len = count;
259 /* ``echo "secret" > seclvl/passwd'' includes a newline */
260 if (p[len - 1] == '\n')
261 len--;
262 /* Hash the password, then compare the hashed values */
263 if ((len = plaintext_to_sha1(tmp, p, len))) {
264 seclvl_printk(0, KERN_ERR, "Error hashing password: rc = "
265 "[%d]\n", len);
266 goto out;
267 }
268
269 len = -EPERM;
270 if (memcmp(hashedPassword, tmp, SHA1_DIGEST_SIZE))
271 goto out;
272
273 seclvl_printk(0, KERN_INFO,
274 "Password accepted; seclvl reduced to 0.\n");
275 seclvl = 0;
276 len = count;
277
278out:
279 kfree (p);
280 return len;
281}
282
283static struct file_operations passwd_file_ops = {
284 .write = passwd_write_file,
285};
286
287/**
288 * Explicitely disallow ptrace'ing the init process.
289 */
290static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child)
291{
292 if (seclvl >= 0 && child->pid == 1) {
293 seclvl_printk(1, KERN_WARNING, "Attempt to ptrace "
294 "the init process dissallowed in "
295 "secure level %d\n", seclvl);
296 return -EPERM;
297 }
298 return 0;
299}
300
301/**
302 * Capability checks for seclvl. The majority of the policy
303 * enforcement for seclvl takes place here.
304 */
305static int seclvl_capable(struct task_struct *tsk, int cap)
306{
307 int rc = 0;
308
309 /* init can do anything it wants */
310 if (tsk->pid == 1)
311 return 0;
312
313 if (seclvl > 0) {
314 rc = -EPERM;
315
316 if (cap == CAP_LINUX_IMMUTABLE)
317 seclvl_printk(1, KERN_WARNING, "Attempt to modify "
318 "the IMMUTABLE and/or APPEND extended "
319 "attribute on a file with the IMMUTABLE "
320 "and/or APPEND extended attribute set "
321 "denied in seclvl [%d]\n", seclvl);
322 else if (cap == CAP_SYS_RAWIO)
323 seclvl_printk(1, KERN_WARNING, "Attempt to perform "
324 "raw I/O while in secure level [%d] "
325 "denied\n", seclvl);
326 else if (cap == CAP_NET_ADMIN)
327 seclvl_printk(1, KERN_WARNING, "Attempt to perform "
328 "network administrative task while "
329 "in secure level [%d] denied\n", seclvl);
330 else if (cap == CAP_SETUID)
331 seclvl_printk(1, KERN_WARNING, "Attempt to setuid "
332 "while in secure level [%d] denied\n",
333 seclvl);
334 else if (cap == CAP_SETGID)
335 seclvl_printk(1, KERN_WARNING, "Attempt to setgid "
336 "while in secure level [%d] denied\n",
337 seclvl);
338 else if (cap == CAP_SYS_MODULE)
339 seclvl_printk(1, KERN_WARNING, "Attempt to perform "
340 "a module operation while in secure "
341 "level [%d] denied\n", seclvl);
342 else
343 rc = 0;
344 }
345
346 if (!rc) {
347 if (!(cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0))
348 rc = -EPERM;
349 }
350
351 if (rc)
352 seclvl_printk(1, KERN_WARNING, "Capability denied\n");
353
354 return rc;
355}
356
357/**
358 * Disallow reversing the clock in seclvl > 1
359 */
360static int seclvl_settime(struct timespec *tv, struct timezone *tz)
361{
362 if (tv && seclvl > 1) {
363 struct timespec now;
364 now = current_kernel_time();
365 if (tv->tv_sec < now.tv_sec ||
366 (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) {
367 seclvl_printk(1, KERN_WARNING, "Attempt to decrement "
368 "time in secure level %d denied: "
369 "current->pid = [%d], "
370 "current->group_leader->pid = [%d]\n",
371 seclvl, current->pid,
372 current->group_leader->pid);
373 return -EPERM;
374 } /* if attempt to decrement time */
375 } /* if seclvl > 1 */
376 return 0;
377}
378
379/* claim the blockdev to exclude mounters, release on file close */
380static int seclvl_bd_claim(struct inode *inode)
381{
382 int holder;
383 struct block_device *bdev = NULL;
384 dev_t dev = inode->i_rdev;
385 bdev = open_by_devnum(dev, FMODE_WRITE);
386 if (bdev) {
387 if (bd_claim(bdev, &holder)) {
388 blkdev_put(bdev);
389 return -EPERM;
390 }
391 /* claimed, mark it to release on close */
392 inode->i_security = current;
393 }
394 return 0;
395}
396
397/* release the blockdev if you claimed it */
398static void seclvl_bd_release(struct inode *inode)
399{
400 if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) {
401 struct block_device *bdev = inode->i_bdev;
402 if (bdev) {
403 bd_release(bdev);
404 blkdev_put(bdev);
405 inode->i_security = NULL;
406 }
407 }
408}
409
410/**
411 * Security for writes to block devices is regulated by this seclvl
412 * function. Deny all writes to block devices in seclvl 2. In
413 * seclvl 1, we only deny writes to *mounted* block devices.
414 */
415static int
416seclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd)
417{
418 if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) {
419 switch (seclvl) {
420 case 2:
421 seclvl_printk(1, KERN_WARNING, "Write to block device "
422 "denied in secure level [%d]\n", seclvl);
423 return -EPERM;
424 case 1:
425 if (seclvl_bd_claim(inode)) {
426 seclvl_printk(1, KERN_WARNING,
427 "Write to mounted block device "
428 "denied in secure level [%d]\n",
429 seclvl);
430 return -EPERM;
431 }
432 }
433 }
434 return 0;
435}
436
437/**
438 * The SUID and SGID bits cannot be set in seclvl >= 1
439 */
440static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr)
441{
442 if (seclvl > 0) {
443 if (iattr->ia_valid & ATTR_MODE)
444 if (iattr->ia_mode & S_ISUID ||
445 iattr->ia_mode & S_ISGID) {
446 seclvl_printk(1, KERN_WARNING, "Attempt to "
447 "modify SUID or SGID bit "
448 "denied in seclvl [%d]\n",
449 seclvl);
450 return -EPERM;
451 }
452 }
453 return 0;
454}
455
456/* release busied block devices */
457static void seclvl_file_free_security(struct file *filp)
458{
459 struct dentry *dentry = filp->f_dentry;
460
461 if (dentry)
462 seclvl_bd_release(dentry->d_inode);
463}
464
465/**
466 * Cannot unmount in secure level 2
467 */
468static int seclvl_umount(struct vfsmount *mnt, int flags)
469{
470 if (current->pid != 1 && seclvl == 2) {
471 seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure "
472 "level %d\n", seclvl);
473 return -EPERM;
474 }
475 return 0;
476}
477
478static struct security_operations seclvl_ops = {
479 .ptrace = seclvl_ptrace,
480 .capable = seclvl_capable,
481 .inode_permission = seclvl_inode_permission,
482 .inode_setattr = seclvl_inode_setattr,
483 .file_free_security = seclvl_file_free_security,
484 .settime = seclvl_settime,
485 .sb_umount = seclvl_umount,
486};
487
488/**
489 * Process the password-related module parameters
490 */
491static int processPassword(void)
492{
493 int rc = 0;
494 if (*passwd) {
495 char *p;
496
497 if (*sha1_passwd) {
498 seclvl_printk(0, KERN_ERR, "Error: Both "
499 "passwd and sha1_passwd "
500 "were set, but they are mutually "
501 "exclusive.\n");
502 return -EINVAL;
503 }
504
505 p = kstrdup(passwd, GFP_KERNEL);
506 if (p == NULL)
507 return -ENOMEM;
508
509 if ((rc = plaintext_to_sha1(hashedPassword, p, strlen(p))))
510 seclvl_printk(0, KERN_ERR, "Error: SHA1 support not "
511 "in kernel\n");
512
513 kfree (p);
514 /* All static data goes to the BSS, which zero's the
515 * plaintext password out for us. */
516 } else if (*sha1_passwd) { // Base 16
517 int i;
518 i = strlen(sha1_passwd);
519 if (i != (SHA1_DIGEST_SIZE * 2)) {
520 seclvl_printk(0, KERN_ERR, "Received [%d] bytes; "
521 "expected [%d] for the hexadecimal "
522 "representation of the SHA1 hash of "
523 "the password.\n",
524 i, (SHA1_DIGEST_SIZE * 2));
525 return -EINVAL;
526 }
527 while ((i -= 2) + 2) {
528 unsigned char tmp;
529 tmp = sha1_passwd[i + 2];
530 sha1_passwd[i + 2] = '\0';
531 hashedPassword[i / 2] = (unsigned char)
532 simple_strtol(&sha1_passwd[i], NULL, 16);
533 sha1_passwd[i + 2] = tmp;
534 }
535 }
536 return rc;
537}
538
539/**
540 * securityfs registrations
541 */
542struct dentry *dir_ino, *seclvl_ino, *passwd_ino;
543
544static int seclvlfs_register(void)
545{
546 int rc = 0;
547
548 dir_ino = securityfs_create_dir("seclvl", NULL);
549
550 if (IS_ERR(dir_ino))
551 return PTR_ERR(dir_ino);
552
553 seclvl_ino = securityfs_create_file("seclvl", S_IRUGO | S_IWUSR,
554 dir_ino, &seclvl, &seclvl_file_ops);
555 if (IS_ERR(seclvl_ino)) {
556 rc = PTR_ERR(seclvl_ino);
557 goto out_deldir;
558 }
559 if (*passwd || *sha1_passwd) {
560 passwd_ino = securityfs_create_file("passwd", S_IRUGO | S_IWUSR,
561 dir_ino, NULL, &passwd_file_ops);
562 if (IS_ERR(passwd_ino)) {
563 rc = PTR_ERR(passwd_ino);
564 goto out_delf;
565 }
566 }
567 return rc;
568
569out_delf:
570 securityfs_remove(seclvl_ino);
571
572out_deldir:
573 securityfs_remove(dir_ino);
574
575 return rc;
576}
577
578static void seclvlfs_unregister(void)
579{
580 securityfs_remove(seclvl_ino);
581
582 if (*passwd || *sha1_passwd)
583 securityfs_remove(passwd_ino);
584
585 securityfs_remove(dir_ino);
586}
587
588/**
589 * Initialize the seclvl module.
590 */
591static int __init seclvl_init(void)
592{
593 int rc = 0;
594 static char once;
595
596 if (verbosity < 0 || verbosity > 1) {
597 printk(KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 "
598 "are valid values\n", verbosity);
599 rc = -EINVAL;
600 goto exit;
601 }
602 if (initlvl < -1 || initlvl > 2) {
603 seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel "
604 "[%d].\n", initlvl);
605 rc = -EINVAL;
606 goto exit;
607 }
608 seclvl = initlvl;
609 if ((rc = processPassword())) {
610 seclvl_printk(0, KERN_ERR, "Error processing the password "
611 "module parameter(s): rc = [%d]\n", rc);
612 goto exit;
613 }
614
615 if ((rc = seclvlfs_register())) {
616 seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n");
617 goto exit;
618 }
619 /* register ourselves with the security framework */
620 if (register_security(&seclvl_ops)) {
621 seclvl_printk(0, KERN_ERR,
622 "seclvl: Failure registering with the "
623 "kernel.\n");
624 /* try registering with primary module */
625 rc = mod_reg_security(MY_NAME, &seclvl_ops);
626 if (rc) {
627 seclvl_printk(0, KERN_ERR, "seclvl: Failure "
628 "registering with primary security "
629 "module.\n");
630 seclvlfs_unregister();
631 goto exit;
632 } /* if primary module registered */
633 secondary = 1;
634 } /* if we registered ourselves with the security framework */
635
636 seclvl_printk(0, KERN_INFO, "seclvl: Successfully initialized.\n");
637
638 if (once) {
639 once = 1;
640 seclvl_printk(0, KERN_INFO, "seclvl is going away. It has been "
641 "buggy for ages. Also, be warned that "
642 "Securelevels are useless.");
643 }
644 exit:
645 if (rc)
646 printk(KERN_ERR "seclvl: Error during initialization: rc = "
647 "[%d]\n", rc);
648 return rc;
649}
650
651/**
652 * Remove the seclvl module.
653 */
654static void __exit seclvl_exit(void)
655{
656 seclvlfs_unregister();
657
658 if (secondary)
659 mod_unreg_security(MY_NAME, &seclvl_ops);
660 else if (unregister_security(&seclvl_ops))
661 seclvl_printk(0, KERN_INFO,
662 "seclvl: Failure unregistering with the "
663 "kernel\n");
664}
665
666module_init(seclvl_init);
667module_exit(seclvl_exit);
668
669MODULE_AUTHOR("Michael A. Halcrow <mike@halcrow.us>");
670MODULE_DESCRIPTION("LSM implementation of the BSD Secure Levels");
671MODULE_LICENSE("GPL");