diff options
Diffstat (limited to 'security/seclvl.c')
-rw-r--r-- | security/seclvl.c | 747 |
1 files changed, 747 insertions, 0 deletions
diff --git a/security/seclvl.c b/security/seclvl.c new file mode 100644 index 000000000000..8a0ab0d7949e --- /dev/null +++ b/security/seclvl.c | |||
@@ -0,0 +1,747 @@ | |||
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 | * | ||
12 | * This program is free software; you can redistribute it and/or modify | ||
13 | * it under the terms of the GNU General Public License as published by | ||
14 | * the Free Software Foundation; either version 2 of the License, or | ||
15 | * (at your option) any later version. | ||
16 | */ | ||
17 | |||
18 | #include <linux/config.h> | ||
19 | #include <linux/module.h> | ||
20 | #include <linux/moduleparam.h> | ||
21 | #include <linux/kernel.h> | ||
22 | #include <linux/init.h> | ||
23 | #include <linux/security.h> | ||
24 | #include <linux/netlink.h> | ||
25 | #include <linux/fs.h> | ||
26 | #include <linux/namei.h> | ||
27 | #include <linux/mount.h> | ||
28 | #include <linux/capability.h> | ||
29 | #include <linux/time.h> | ||
30 | #include <linux/proc_fs.h> | ||
31 | #include <linux/kobject.h> | ||
32 | #include <linux/crypto.h> | ||
33 | #include <asm/scatterlist.h> | ||
34 | #include <linux/gfp.h> | ||
35 | #include <linux/sysfs.h> | ||
36 | |||
37 | #define SHA1_DIGEST_SIZE 20 | ||
38 | |||
39 | /** | ||
40 | * Module parameter that defines the initial secure level. | ||
41 | * | ||
42 | * When built as a module, it defaults to seclvl 1, which is the | ||
43 | * behavior of BSD secure levels. Note that this default behavior | ||
44 | * wrecks havoc on a machine when the seclvl module is compiled into | ||
45 | * the kernel. In that case, we default to seclvl 0. | ||
46 | */ | ||
47 | #ifdef CONFIG_SECURITY_SECLVL_MODULE | ||
48 | static int initlvl = 1; | ||
49 | #else | ||
50 | static int initlvl; | ||
51 | #endif | ||
52 | module_param(initlvl, int, 0); | ||
53 | MODULE_PARM_DESC(initlvl, "Initial secure level (defaults to 1)"); | ||
54 | |||
55 | /* Module parameter that defines the verbosity level */ | ||
56 | static int verbosity; | ||
57 | module_param(verbosity, int, 0); | ||
58 | MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults to " | ||
59 | "0, which is Quiet)"); | ||
60 | |||
61 | /** | ||
62 | * Optional password which can be passed in to bring seclvl to 0 | ||
63 | * (i.e., for halt/reboot). Defaults to NULL (the passwd attribute | ||
64 | * file will not be registered in sysfs). | ||
65 | * | ||
66 | * This gets converted to its SHA1 hash when stored. It's probably | ||
67 | * not a good idea to use this parameter when loading seclvl from a | ||
68 | * script; use sha1_passwd instead. | ||
69 | */ | ||
70 | |||
71 | #define MAX_PASSWD_SIZE 32 | ||
72 | static char passwd[MAX_PASSWD_SIZE]; | ||
73 | module_param_string(passwd, passwd, sizeof(passwd), 0); | ||
74 | MODULE_PARM_DESC(passwd, | ||
75 | "Plaintext of password that sets seclvl=0 when written to " | ||
76 | "(sysfs mount point)/seclvl/passwd\n"); | ||
77 | |||
78 | /** | ||
79 | * SHA1 hashed version of the optional password which can be passed in | ||
80 | * to bring seclvl to 0 (i.e., for halt/reboot). Must be in | ||
81 | * hexadecimal format (40 characters). Defaults to NULL (the passwd | ||
82 | * attribute file will not be registered in sysfs). | ||
83 | * | ||
84 | * Use the sha1sum utility to generate the SHA1 hash of a password: | ||
85 | * | ||
86 | * echo -n "secret" | sha1sum | ||
87 | */ | ||
88 | #define MAX_SHA1_PASSWD 41 | ||
89 | static char sha1_passwd[MAX_SHA1_PASSWD]; | ||
90 | module_param_string(sha1_passwd, sha1_passwd, sizeof(sha1_passwd), 0); | ||
91 | MODULE_PARM_DESC(sha1_passwd, | ||
92 | "SHA1 hash (40 hexadecimal characters) of password that " | ||
93 | "sets seclvl=0 when plaintext password is written to " | ||
94 | "(sysfs mount point)/seclvl/passwd\n"); | ||
95 | |||
96 | static int hideHash = 1; | ||
97 | module_param(hideHash, int, 0); | ||
98 | MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs " | ||
99 | "will return the SHA1-hashed value of the password that " | ||
100 | "lowers the secure level to 0.\n"); | ||
101 | |||
102 | #define MY_NAME "seclvl" | ||
103 | |||
104 | /** | ||
105 | * This time-limits log writes to one per second. | ||
106 | */ | ||
107 | #define seclvl_printk(verb, type, fmt, arg...) \ | ||
108 | do { \ | ||
109 | if (verbosity >= verb) { \ | ||
110 | static unsigned long _prior; \ | ||
111 | unsigned long _now = jiffies; \ | ||
112 | if ((_now - _prior) > HZ) { \ | ||
113 | printk(type "%s: %s: " fmt, \ | ||
114 | MY_NAME, __FUNCTION__ , \ | ||
115 | ## arg); \ | ||
116 | _prior = _now; \ | ||
117 | } \ | ||
118 | } \ | ||
119 | } while (0) | ||
120 | |||
121 | /** | ||
122 | * kobject stuff | ||
123 | */ | ||
124 | |||
125 | struct subsystem seclvl_subsys; | ||
126 | |||
127 | struct seclvl_obj { | ||
128 | char *name; | ||
129 | struct list_head slot_list; | ||
130 | struct kobject kobj; | ||
131 | }; | ||
132 | |||
133 | /** | ||
134 | * There is a seclvl_attribute struct for each file in sysfs. | ||
135 | * | ||
136 | * In our case, we have one of these structs for "passwd" and another | ||
137 | * for "seclvl". | ||
138 | */ | ||
139 | struct seclvl_attribute { | ||
140 | struct attribute attr; | ||
141 | ssize_t(*show) (struct seclvl_obj *, char *); | ||
142 | ssize_t(*store) (struct seclvl_obj *, const char *, size_t); | ||
143 | }; | ||
144 | |||
145 | /** | ||
146 | * When this function is called, one of the files in sysfs is being | ||
147 | * written to. attribute->store is a function pointer to whatever the | ||
148 | * struct seclvl_attribute store function pointer points to. It is | ||
149 | * unique for "passwd" and "seclvl". | ||
150 | */ | ||
151 | static ssize_t | ||
152 | seclvl_attr_store(struct kobject *kobj, | ||
153 | struct attribute *attr, const char *buf, size_t len) | ||
154 | { | ||
155 | struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj); | ||
156 | struct seclvl_attribute *attribute = | ||
157 | container_of(attr, struct seclvl_attribute, attr); | ||
158 | return (attribute->store ? attribute->store(obj, buf, len) : 0); | ||
159 | } | ||
160 | |||
161 | static ssize_t | ||
162 | seclvl_attr_show(struct kobject *kobj, struct attribute *attr, char *buf) | ||
163 | { | ||
164 | struct seclvl_obj *obj = container_of(kobj, struct seclvl_obj, kobj); | ||
165 | struct seclvl_attribute *attribute = | ||
166 | container_of(attr, struct seclvl_attribute, attr); | ||
167 | return (attribute->show ? attribute->show(obj, buf) : 0); | ||
168 | } | ||
169 | |||
170 | /** | ||
171 | * Callback function pointers for show and store | ||
172 | */ | ||
173 | static struct sysfs_ops seclvlfs_sysfs_ops = { | ||
174 | .show = seclvl_attr_show, | ||
175 | .store = seclvl_attr_store, | ||
176 | }; | ||
177 | |||
178 | static struct kobj_type seclvl_ktype = { | ||
179 | .sysfs_ops = &seclvlfs_sysfs_ops | ||
180 | }; | ||
181 | |||
182 | decl_subsys(seclvl, &seclvl_ktype, NULL); | ||
183 | |||
184 | /** | ||
185 | * The actual security level. Ranges between -1 and 2 inclusive. | ||
186 | */ | ||
187 | static int seclvl; | ||
188 | |||
189 | /** | ||
190 | * flag to keep track of how we were registered | ||
191 | */ | ||
192 | static int secondary; | ||
193 | |||
194 | /** | ||
195 | * Verifies that the requested secure level is valid, given the current | ||
196 | * secure level. | ||
197 | */ | ||
198 | static int seclvl_sanity(int reqlvl) | ||
199 | { | ||
200 | if ((reqlvl < -1) || (reqlvl > 2)) { | ||
201 | seclvl_printk(1, KERN_WARNING, "Attempt to set seclvl out of " | ||
202 | "range: [%d]\n", reqlvl); | ||
203 | return -EINVAL; | ||
204 | } | ||
205 | if ((seclvl == 0) && (reqlvl == -1)) | ||
206 | return 0; | ||
207 | if (reqlvl < seclvl) { | ||
208 | seclvl_printk(1, KERN_WARNING, "Attempt to lower seclvl to " | ||
209 | "[%d]\n", reqlvl); | ||
210 | return -EPERM; | ||
211 | } | ||
212 | return 0; | ||
213 | } | ||
214 | |||
215 | /** | ||
216 | * Called whenever the user reads the sysfs handle to this kernel | ||
217 | * object | ||
218 | */ | ||
219 | static ssize_t seclvl_read_file(struct seclvl_obj *obj, char *buff) | ||
220 | { | ||
221 | return snprintf(buff, PAGE_SIZE, "%d\n", seclvl); | ||
222 | } | ||
223 | |||
224 | /** | ||
225 | * security level advancement rules: | ||
226 | * Valid levels are -1 through 2, inclusive. | ||
227 | * From -1, stuck. [ in case compiled into kernel ] | ||
228 | * From 0 or above, can only increment. | ||
229 | */ | ||
230 | static int do_seclvl_advance(int newlvl) | ||
231 | { | ||
232 | if (newlvl <= seclvl) { | ||
233 | seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl " | ||
234 | "[%d]\n", newlvl); | ||
235 | return -EINVAL; | ||
236 | } | ||
237 | if (newlvl > 2) { | ||
238 | seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl " | ||
239 | "[%d]\n", newlvl); | ||
240 | return -EINVAL; | ||
241 | } | ||
242 | if (seclvl == -1) { | ||
243 | seclvl_printk(1, KERN_WARNING, "Not allowed to advance to " | ||
244 | "seclvl [%d]\n", seclvl); | ||
245 | return -EPERM; | ||
246 | } | ||
247 | seclvl = newlvl; | ||
248 | return 0; | ||
249 | } | ||
250 | |||
251 | /** | ||
252 | * Called whenever the user writes to the sysfs handle to this kernel | ||
253 | * object (seclvl/seclvl). It expects a single-digit number. | ||
254 | */ | ||
255 | static ssize_t | ||
256 | seclvl_write_file(struct seclvl_obj *obj, const char *buff, size_t count) | ||
257 | { | ||
258 | unsigned long val; | ||
259 | if (count > 2 || (count == 2 && buff[1] != '\n')) { | ||
260 | seclvl_printk(1, KERN_WARNING, "Invalid value passed to " | ||
261 | "seclvl: [%s]\n", buff); | ||
262 | return -EINVAL; | ||
263 | } | ||
264 | val = buff[0] - 48; | ||
265 | if (seclvl_sanity(val)) { | ||
266 | seclvl_printk(1, KERN_WARNING, "Illegal secure level " | ||
267 | "requested: [%d]\n", (int)val); | ||
268 | return -EPERM; | ||
269 | } | ||
270 | if (do_seclvl_advance(val)) { | ||
271 | seclvl_printk(0, KERN_ERR, "Failure advancing security level " | ||
272 | "to %lu\n", val); | ||
273 | } | ||
274 | return count; | ||
275 | } | ||
276 | |||
277 | /* Generate sysfs_attr_seclvl */ | ||
278 | static struct seclvl_attribute sysfs_attr_seclvl = | ||
279 | __ATTR(seclvl, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_file, | ||
280 | seclvl_write_file); | ||
281 | |||
282 | static unsigned char hashedPassword[SHA1_DIGEST_SIZE]; | ||
283 | |||
284 | /** | ||
285 | * Called whenever the user reads the sysfs passwd handle. | ||
286 | */ | ||
287 | static ssize_t seclvl_read_passwd(struct seclvl_obj *obj, char *buff) | ||
288 | { | ||
289 | /* So just how good *is* your password? :-) */ | ||
290 | char tmp[3]; | ||
291 | int i = 0; | ||
292 | buff[0] = '\0'; | ||
293 | if (hideHash) { | ||
294 | /* Security through obscurity */ | ||
295 | return 0; | ||
296 | } | ||
297 | while (i < SHA1_DIGEST_SIZE) { | ||
298 | snprintf(tmp, 3, "%02x", hashedPassword[i]); | ||
299 | strncat(buff, tmp, 2); | ||
300 | i++; | ||
301 | } | ||
302 | strcat(buff, "\n"); | ||
303 | return ((SHA1_DIGEST_SIZE * 2) + 1); | ||
304 | } | ||
305 | |||
306 | /** | ||
307 | * Converts a block of plaintext of into its SHA1 hashed value. | ||
308 | * | ||
309 | * It would be nice if crypto had a wrapper to do this for us linear | ||
310 | * people... | ||
311 | */ | ||
312 | static int | ||
313 | plaintext_to_sha1(unsigned char *hash, const char *plaintext, int len) | ||
314 | { | ||
315 | char *pgVirtAddr; | ||
316 | struct crypto_tfm *tfm; | ||
317 | struct scatterlist sg[1]; | ||
318 | if (len > PAGE_SIZE) { | ||
319 | seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d " | ||
320 | "characters). Largest possible is %lu " | ||
321 | "bytes.\n", len, PAGE_SIZE); | ||
322 | return -ENOMEM; | ||
323 | } | ||
324 | tfm = crypto_alloc_tfm("sha1", 0); | ||
325 | if (tfm == NULL) { | ||
326 | seclvl_printk(0, KERN_ERR, | ||
327 | "Failed to load transform for SHA1\n"); | ||
328 | return -ENOSYS; | ||
329 | } | ||
330 | // Just get a new page; don't play around with page boundaries | ||
331 | // and scatterlists. | ||
332 | pgVirtAddr = (char *)__get_free_page(GFP_KERNEL); | ||
333 | sg[0].page = virt_to_page(pgVirtAddr); | ||
334 | sg[0].offset = 0; | ||
335 | sg[0].length = len; | ||
336 | strncpy(pgVirtAddr, plaintext, len); | ||
337 | crypto_digest_init(tfm); | ||
338 | crypto_digest_update(tfm, sg, 1); | ||
339 | crypto_digest_final(tfm, hash); | ||
340 | crypto_free_tfm(tfm); | ||
341 | free_page((unsigned long)pgVirtAddr); | ||
342 | return 0; | ||
343 | } | ||
344 | |||
345 | /** | ||
346 | * Called whenever the user writes to the sysfs passwd handle to this kernel | ||
347 | * object. It hashes the password and compares the hashed results. | ||
348 | */ | ||
349 | static ssize_t | ||
350 | seclvl_write_passwd(struct seclvl_obj *obj, const char *buff, size_t count) | ||
351 | { | ||
352 | int i; | ||
353 | unsigned char tmp[SHA1_DIGEST_SIZE]; | ||
354 | int rc; | ||
355 | int len; | ||
356 | if (!*passwd && !*sha1_passwd) { | ||
357 | seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the " | ||
358 | "seclvl module, but neither a plain text " | ||
359 | "password nor a SHA1 hashed password was " | ||
360 | "passed in as a module parameter! This is a " | ||
361 | "bug, since it should not be possible to be in " | ||
362 | "this part of the module; please tell a " | ||
363 | "maintainer about this event.\n"); | ||
364 | return -EINVAL; | ||
365 | } | ||
366 | len = strlen(buff); | ||
367 | /* ``echo "secret" > seclvl/passwd'' includes a newline */ | ||
368 | if (buff[len - 1] == '\n') { | ||
369 | len--; | ||
370 | } | ||
371 | /* Hash the password, then compare the hashed values */ | ||
372 | if ((rc = plaintext_to_sha1(tmp, buff, len))) { | ||
373 | seclvl_printk(0, KERN_ERR, "Error hashing password: rc = " | ||
374 | "[%d]\n", rc); | ||
375 | return rc; | ||
376 | } | ||
377 | for (i = 0; i < SHA1_DIGEST_SIZE; i++) { | ||
378 | if (hashedPassword[i] != tmp[i]) { | ||
379 | return -EPERM; | ||
380 | } | ||
381 | } | ||
382 | seclvl_printk(0, KERN_INFO, | ||
383 | "Password accepted; seclvl reduced to 0.\n"); | ||
384 | seclvl = 0; | ||
385 | return count; | ||
386 | } | ||
387 | |||
388 | /* Generate sysfs_attr_passwd */ | ||
389 | static struct seclvl_attribute sysfs_attr_passwd = | ||
390 | __ATTR(passwd, (S_IFREG | S_IRUGO | S_IWUSR), seclvl_read_passwd, | ||
391 | seclvl_write_passwd); | ||
392 | |||
393 | /** | ||
394 | * Explicitely disallow ptrace'ing the init process. | ||
395 | */ | ||
396 | static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child) | ||
397 | { | ||
398 | if (seclvl >= 0) { | ||
399 | if (child->pid == 1) { | ||
400 | seclvl_printk(1, KERN_WARNING, "Attempt to ptrace " | ||
401 | "the init process dissallowed in " | ||
402 | "secure level %d\n", seclvl); | ||
403 | return -EPERM; | ||
404 | } | ||
405 | } | ||
406 | return 0; | ||
407 | } | ||
408 | |||
409 | /** | ||
410 | * Capability checks for seclvl. The majority of the policy | ||
411 | * enforcement for seclvl takes place here. | ||
412 | */ | ||
413 | static int seclvl_capable(struct task_struct *tsk, int cap) | ||
414 | { | ||
415 | /* init can do anything it wants */ | ||
416 | if (tsk->pid == 1) | ||
417 | return 0; | ||
418 | |||
419 | switch (seclvl) { | ||
420 | case 2: | ||
421 | /* fall through */ | ||
422 | case 1: | ||
423 | if (cap == CAP_LINUX_IMMUTABLE) { | ||
424 | seclvl_printk(1, KERN_WARNING, "Attempt to modify " | ||
425 | "the IMMUTABLE and/or APPEND extended " | ||
426 | "attribute on a file with the IMMUTABLE " | ||
427 | "and/or APPEND extended attribute set " | ||
428 | "denied in seclvl [%d]\n", seclvl); | ||
429 | return -EPERM; | ||
430 | } else if (cap == CAP_SYS_RAWIO) { // Somewhat broad... | ||
431 | seclvl_printk(1, KERN_WARNING, "Attempt to perform " | ||
432 | "raw I/O while in secure level [%d] " | ||
433 | "denied\n", seclvl); | ||
434 | return -EPERM; | ||
435 | } else if (cap == CAP_NET_ADMIN) { | ||
436 | seclvl_printk(1, KERN_WARNING, "Attempt to perform " | ||
437 | "network administrative task while " | ||
438 | "in secure level [%d] denied\n", seclvl); | ||
439 | return -EPERM; | ||
440 | } else if (cap == CAP_SETUID) { | ||
441 | seclvl_printk(1, KERN_WARNING, "Attempt to setuid " | ||
442 | "while in secure level [%d] denied\n", | ||
443 | seclvl); | ||
444 | return -EPERM; | ||
445 | } else if (cap == CAP_SETGID) { | ||
446 | seclvl_printk(1, KERN_WARNING, "Attempt to setgid " | ||
447 | "while in secure level [%d] denied\n", | ||
448 | seclvl); | ||
449 | } else if (cap == CAP_SYS_MODULE) { | ||
450 | seclvl_printk(1, KERN_WARNING, "Attempt to perform " | ||
451 | "a module operation while in secure " | ||
452 | "level [%d] denied\n", seclvl); | ||
453 | return -EPERM; | ||
454 | } | ||
455 | break; | ||
456 | default: | ||
457 | break; | ||
458 | } | ||
459 | /* from dummy.c */ | ||
460 | if (cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0) | ||
461 | return 0; /* capability granted */ | ||
462 | seclvl_printk(1, KERN_WARNING, "Capability denied\n"); | ||
463 | return -EPERM; /* capability denied */ | ||
464 | } | ||
465 | |||
466 | /** | ||
467 | * Disallow reversing the clock in seclvl > 1 | ||
468 | */ | ||
469 | static int seclvl_settime(struct timespec *tv, struct timezone *tz) | ||
470 | { | ||
471 | struct timespec now; | ||
472 | if (seclvl > 1) { | ||
473 | now = current_kernel_time(); | ||
474 | if (tv->tv_sec < now.tv_sec || | ||
475 | (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) { | ||
476 | seclvl_printk(1, KERN_WARNING, "Attempt to decrement " | ||
477 | "time in secure level %d denied: " | ||
478 | "current->pid = [%d], " | ||
479 | "current->group_leader->pid = [%d]\n", | ||
480 | seclvl, current->pid, | ||
481 | current->group_leader->pid); | ||
482 | return -EPERM; | ||
483 | } /* if attempt to decrement time */ | ||
484 | } /* if seclvl > 1 */ | ||
485 | return 0; | ||
486 | } | ||
487 | |||
488 | /* claim the blockdev to exclude mounters, release on file close */ | ||
489 | static int seclvl_bd_claim(struct inode *inode) | ||
490 | { | ||
491 | int holder; | ||
492 | struct block_device *bdev = NULL; | ||
493 | dev_t dev = inode->i_rdev; | ||
494 | bdev = open_by_devnum(dev, FMODE_WRITE); | ||
495 | if (bdev) { | ||
496 | if (bd_claim(bdev, &holder)) { | ||
497 | blkdev_put(bdev); | ||
498 | return -EPERM; | ||
499 | } | ||
500 | /* claimed, mark it to release on close */ | ||
501 | inode->i_security = current; | ||
502 | } | ||
503 | return 0; | ||
504 | } | ||
505 | |||
506 | /* release the blockdev if you claimed it */ | ||
507 | static void seclvl_bd_release(struct inode *inode) | ||
508 | { | ||
509 | if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) { | ||
510 | struct block_device *bdev = inode->i_bdev; | ||
511 | if (bdev) { | ||
512 | bd_release(bdev); | ||
513 | blkdev_put(bdev); | ||
514 | inode->i_security = NULL; | ||
515 | } | ||
516 | } | ||
517 | } | ||
518 | |||
519 | /** | ||
520 | * Security for writes to block devices is regulated by this seclvl | ||
521 | * function. Deny all writes to block devices in seclvl 2. In | ||
522 | * seclvl 1, we only deny writes to *mounted* block devices. | ||
523 | */ | ||
524 | static int | ||
525 | seclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd) | ||
526 | { | ||
527 | if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) { | ||
528 | switch (seclvl) { | ||
529 | case 2: | ||
530 | seclvl_printk(1, KERN_WARNING, "Write to block device " | ||
531 | "denied in secure level [%d]\n", seclvl); | ||
532 | return -EPERM; | ||
533 | case 1: | ||
534 | if (seclvl_bd_claim(inode)) { | ||
535 | seclvl_printk(1, KERN_WARNING, | ||
536 | "Write to mounted block device " | ||
537 | "denied in secure level [%d]\n", | ||
538 | seclvl); | ||
539 | return -EPERM; | ||
540 | } | ||
541 | } | ||
542 | } | ||
543 | return 0; | ||
544 | } | ||
545 | |||
546 | /** | ||
547 | * The SUID and SGID bits cannot be set in seclvl >= 1 | ||
548 | */ | ||
549 | static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr) | ||
550 | { | ||
551 | if (seclvl > 0) { | ||
552 | if (iattr->ia_valid & ATTR_MODE) | ||
553 | if (iattr->ia_mode & S_ISUID || | ||
554 | iattr->ia_mode & S_ISGID) { | ||
555 | seclvl_printk(1, KERN_WARNING, "Attempt to " | ||
556 | "modify SUID or SGID bit " | ||
557 | "denied in seclvl [%d]\n", | ||
558 | seclvl); | ||
559 | return -EPERM; | ||
560 | } | ||
561 | } | ||
562 | return 0; | ||
563 | } | ||
564 | |||
565 | /* release busied block devices */ | ||
566 | static void seclvl_file_free_security(struct file *filp) | ||
567 | { | ||
568 | struct dentry *dentry = filp->f_dentry; | ||
569 | struct inode *inode = NULL; | ||
570 | |||
571 | if (dentry) { | ||
572 | inode = dentry->d_inode; | ||
573 | seclvl_bd_release(inode); | ||
574 | } | ||
575 | } | ||
576 | |||
577 | /** | ||
578 | * Cannot unmount in secure level 2 | ||
579 | */ | ||
580 | static int seclvl_umount(struct vfsmount *mnt, int flags) | ||
581 | { | ||
582 | if (current->pid == 1) { | ||
583 | return 0; | ||
584 | } | ||
585 | if (seclvl == 2) { | ||
586 | seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure " | ||
587 | "level %d\n", seclvl); | ||
588 | return -EPERM; | ||
589 | } | ||
590 | return 0; | ||
591 | } | ||
592 | |||
593 | static struct security_operations seclvl_ops = { | ||
594 | .ptrace = seclvl_ptrace, | ||
595 | .capable = seclvl_capable, | ||
596 | .inode_permission = seclvl_inode_permission, | ||
597 | .inode_setattr = seclvl_inode_setattr, | ||
598 | .file_free_security = seclvl_file_free_security, | ||
599 | .settime = seclvl_settime, | ||
600 | .sb_umount = seclvl_umount, | ||
601 | }; | ||
602 | |||
603 | /** | ||
604 | * Process the password-related module parameters | ||
605 | */ | ||
606 | static int processPassword(void) | ||
607 | { | ||
608 | int rc = 0; | ||
609 | hashedPassword[0] = '\0'; | ||
610 | if (*passwd) { | ||
611 | if (*sha1_passwd) { | ||
612 | seclvl_printk(0, KERN_ERR, "Error: Both " | ||
613 | "passwd and sha1_passwd " | ||
614 | "were set, but they are mutually " | ||
615 | "exclusive.\n"); | ||
616 | return -EINVAL; | ||
617 | } | ||
618 | if ((rc = plaintext_to_sha1(hashedPassword, passwd, | ||
619 | strlen(passwd)))) { | ||
620 | seclvl_printk(0, KERN_ERR, "Error: SHA1 support not " | ||
621 | "in kernel\n"); | ||
622 | return rc; | ||
623 | } | ||
624 | /* All static data goes to the BSS, which zero's the | ||
625 | * plaintext password out for us. */ | ||
626 | } else if (*sha1_passwd) { // Base 16 | ||
627 | int i; | ||
628 | i = strlen(sha1_passwd); | ||
629 | if (i != (SHA1_DIGEST_SIZE * 2)) { | ||
630 | seclvl_printk(0, KERN_ERR, "Received [%d] bytes; " | ||
631 | "expected [%d] for the hexadecimal " | ||
632 | "representation of the SHA1 hash of " | ||
633 | "the password.\n", | ||
634 | i, (SHA1_DIGEST_SIZE * 2)); | ||
635 | return -EINVAL; | ||
636 | } | ||
637 | while ((i -= 2) + 2) { | ||
638 | unsigned char tmp; | ||
639 | tmp = sha1_passwd[i + 2]; | ||
640 | sha1_passwd[i + 2] = '\0'; | ||
641 | hashedPassword[i / 2] = (unsigned char) | ||
642 | simple_strtol(&sha1_passwd[i], NULL, 16); | ||
643 | sha1_passwd[i + 2] = tmp; | ||
644 | } | ||
645 | } | ||
646 | return 0; | ||
647 | } | ||
648 | |||
649 | /** | ||
650 | * Sysfs registrations | ||
651 | */ | ||
652 | static int doSysfsRegistrations(void) | ||
653 | { | ||
654 | int rc = 0; | ||
655 | if ((rc = subsystem_register(&seclvl_subsys))) { | ||
656 | seclvl_printk(0, KERN_WARNING, | ||
657 | "Error [%d] registering seclvl subsystem\n", rc); | ||
658 | return rc; | ||
659 | } | ||
660 | sysfs_create_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr); | ||
661 | if (*passwd || *sha1_passwd) { | ||
662 | sysfs_create_file(&seclvl_subsys.kset.kobj, | ||
663 | &sysfs_attr_passwd.attr); | ||
664 | } | ||
665 | return 0; | ||
666 | } | ||
667 | |||
668 | /** | ||
669 | * Initialize the seclvl module. | ||
670 | */ | ||
671 | static int __init seclvl_init(void) | ||
672 | { | ||
673 | int rc = 0; | ||
674 | if (verbosity < 0 || verbosity > 1) { | ||
675 | printk(KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 " | ||
676 | "are valid values\n", verbosity); | ||
677 | rc = -EINVAL; | ||
678 | goto exit; | ||
679 | } | ||
680 | sysfs_attr_seclvl.attr.owner = THIS_MODULE; | ||
681 | sysfs_attr_passwd.attr.owner = THIS_MODULE; | ||
682 | if (initlvl < -1 || initlvl > 2) { | ||
683 | seclvl_printk(0, KERN_ERR, "Error: bad initial securelevel " | ||
684 | "[%d].\n", initlvl); | ||
685 | rc = -EINVAL; | ||
686 | goto exit; | ||
687 | } | ||
688 | seclvl = initlvl; | ||
689 | if ((rc = processPassword())) { | ||
690 | seclvl_printk(0, KERN_ERR, "Error processing the password " | ||
691 | "module parameter(s): rc = [%d]\n", rc); | ||
692 | goto exit; | ||
693 | } | ||
694 | /* register ourselves with the security framework */ | ||
695 | if (register_security(&seclvl_ops)) { | ||
696 | seclvl_printk(0, KERN_ERR, | ||
697 | "seclvl: Failure registering with the " | ||
698 | "kernel.\n"); | ||
699 | /* try registering with primary module */ | ||
700 | rc = mod_reg_security(MY_NAME, &seclvl_ops); | ||
701 | if (rc) { | ||
702 | seclvl_printk(0, KERN_ERR, "seclvl: Failure " | ||
703 | "registering with primary security " | ||
704 | "module.\n"); | ||
705 | goto exit; | ||
706 | } /* if primary module registered */ | ||
707 | secondary = 1; | ||
708 | } /* if we registered ourselves with the security framework */ | ||
709 | if ((rc = doSysfsRegistrations())) { | ||
710 | seclvl_printk(0, KERN_ERR, "Error registering with sysfs\n"); | ||
711 | goto exit; | ||
712 | } | ||
713 | seclvl_printk(0, KERN_INFO, "seclvl: Successfully initialized.\n"); | ||
714 | exit: | ||
715 | if (rc) { | ||
716 | printk(KERN_ERR "seclvl: Error during initialization: rc = " | ||
717 | "[%d]\n", rc); | ||
718 | } | ||
719 | return rc; | ||
720 | } | ||
721 | |||
722 | /** | ||
723 | * Remove the seclvl module. | ||
724 | */ | ||
725 | static void __exit seclvl_exit(void) | ||
726 | { | ||
727 | sysfs_remove_file(&seclvl_subsys.kset.kobj, &sysfs_attr_seclvl.attr); | ||
728 | if (*passwd || *sha1_passwd) { | ||
729 | sysfs_remove_file(&seclvl_subsys.kset.kobj, | ||
730 | &sysfs_attr_passwd.attr); | ||
731 | } | ||
732 | subsystem_unregister(&seclvl_subsys); | ||
733 | if (secondary == 1) { | ||
734 | mod_unreg_security(MY_NAME, &seclvl_ops); | ||
735 | } else if (unregister_security(&seclvl_ops)) { | ||
736 | seclvl_printk(0, KERN_INFO, | ||
737 | "seclvl: Failure unregistering with the " | ||
738 | "kernel\n"); | ||
739 | } | ||
740 | } | ||
741 | |||
742 | module_init(seclvl_init); | ||
743 | module_exit(seclvl_exit); | ||
744 | |||
745 | MODULE_AUTHOR("Michael A. Halcrow <mike@halcrow.us>"); | ||
746 | MODULE_DESCRIPTION("LSM implementation of the BSD Secure Levels"); | ||
747 | MODULE_LICENSE("GPL"); | ||