670 lines · 17.1 KB · C
RawDownload
/** * BSD Secure Levels LSM * * Maintainers: *	Michael A. Halcrow <mike@halcrow.us> *	Serge Hallyn <hallyn@cs.wm.edu> * * Copyright (c) 2001 WireX Communications, Inc <chris@wirex.com> * Copyright (c) 2001 Greg Kroah-Hartman <greg@kroah.com> * Copyright (c) 2002 International Business Machines <robb@austin.ibm.com> * Copyright (c) 2006 Davi E. M. Arnaut <davi.arnaut@gmail.com> * *	This program is free software; you can redistribute it and/or modify *	it under the terms of the GNU General Public License as published by *	the Free Software Foundation; either version 2 of the License, or *	(at your option) any later version. */ #include <linux/config.h>#include <linux/module.h>#include <linux/moduleparam.h>#include <linux/kernel.h>#include <linux/init.h>#include <linux/security.h>#include <linux/netlink.h>#include <linux/fs.h>#include <linux/namei.h>#include <linux/mount.h>#include <linux/capability.h>#include <linux/time.h>#include <linux/proc_fs.h>#include <linux/kobject.h>#include <linux/crypto.h>#include <asm/scatterlist.h>#include <linux/scatterlist.h>#include <linux/gfp.h>#include <linux/sysfs.h> #define SHA1_DIGEST_SIZE 20 /** * Module parameter that defines the initial secure level. * * When built as a module, it defaults to seclvl 1, which is the * behavior of BSD secure levels.  Note that this default behavior * wrecks havoc on a machine when the seclvl module is compiled into * the kernel.	In that case, we default to seclvl 0. */#ifdef CONFIG_SECURITY_SECLVL_MODULEstatic int initlvl = 1;#elsestatic int initlvl;#endifmodule_param(initlvl, int, 0);MODULE_PARM_DESC(initlvl, "Initial secure level (defaults to 1)"); /* Module parameter that defines the verbosity level */static int verbosity;module_param(verbosity, int, 0);MODULE_PARM_DESC(verbosity, "Initial verbosity level (0 or 1; defaults to "		 "0, which is Quiet)"); /** * Optional password which can be passed in to bring seclvl to 0 * (i.e., for halt/reboot).  Defaults to NULL (the passwd attribute * file will not be registered in sysfs). * * This gets converted to its SHA1 hash when stored.  It's probably * not a good idea to use this parameter when loading seclvl from a * script; use sha1_passwd instead. */ #define MAX_PASSWD_SIZE	32static char passwd[MAX_PASSWD_SIZE];module_param_string(passwd, passwd, sizeof(passwd), 0);MODULE_PARM_DESC(passwd,		 "Plaintext of password that sets seclvl=0 when written to "		 "(sysfs mount point)/seclvl/passwd\n"); /** * SHA1 hashed version of the optional password which can be passed in * to bring seclvl to 0 (i.e., for halt/reboot).  Must be in * hexadecimal format (40 characters).	Defaults to NULL (the passwd * attribute file will not be registered in sysfs). * * Use the sha1sum utility to generate the SHA1 hash of a password: * * echo -n "secret" | sha1sum */#define MAX_SHA1_PASSWD	41static char sha1_passwd[MAX_SHA1_PASSWD];module_param_string(sha1_passwd, sha1_passwd, sizeof(sha1_passwd), 0);MODULE_PARM_DESC(sha1_passwd,		 "SHA1 hash (40 hexadecimal characters) of password that "		 "sets seclvl=0 when plaintext password is written to "		 "(sysfs mount point)/seclvl/passwd\n"); static int hideHash = 1;module_param(hideHash, int, 0);MODULE_PARM_DESC(hideHash, "When set to 0, reading seclvl/passwd from sysfs "		 "will return the SHA1-hashed value of the password that "		 "lowers the secure level to 0.\n"); #define MY_NAME "seclvl" /** * This time-limits log writes to one per second. */#define seclvl_printk(verb, type, fmt, arg...)			\	do {							\		if (verbosity >= verb) {			\			static unsigned long _prior;		\			unsigned long _now = jiffies;		\			if ((_now - _prior) > HZ) {		\				printk(type "%s: %s: " fmt,	\					MY_NAME, __FUNCTION__ ,	\					## arg);		\				_prior = _now;			\			}					\		}						\	} while (0) /** * The actual security level.  Ranges between -1 and 2 inclusive. */static int seclvl; /** * flag to keep track of how we were registered */static int secondary; /** * Verifies that the requested secure level is valid, given the current * secure level. */static int seclvl_sanity(int reqlvl){	if ((reqlvl < -1) || (reqlvl > 2)) {		seclvl_printk(1, KERN_WARNING, "Attempt to set seclvl out of "			      "range: [%d]\n", reqlvl);		return -EINVAL;	}	if ((seclvl == 0) && (reqlvl == -1))		return 0;	if (reqlvl < seclvl) {		seclvl_printk(1, KERN_WARNING, "Attempt to lower seclvl to "			      "[%d]\n", reqlvl);		return -EPERM;	}	return 0;} /** * security level advancement rules: *   Valid levels are -1 through 2, inclusive. *   From -1, stuck.  [ in case compiled into kernel ] *   From 0 or above, can only increment. */static void do_seclvl_advance(void *data, u64 val){	int ret;	int newlvl = (int)val; 	ret = seclvl_sanity(newlvl);	if (ret)		return; 	if (newlvl > 2) {		seclvl_printk(1, KERN_WARNING, "Cannot advance to seclvl "			      "[%d]\n", newlvl);		return;	}	if (seclvl == -1) {		seclvl_printk(1, KERN_WARNING, "Not allowed to advance to "			      "seclvl [%d]\n", seclvl);		return;	}	seclvl = newlvl;  /* would it be more "correct" to set *data? */	return;} static u64 seclvl_int_get(void *data){	return *(int *)data;} DEFINE_SIMPLE_ATTRIBUTE(seclvl_file_ops, seclvl_int_get, do_seclvl_advance, "%lld\n"); static unsigned char hashedPassword[SHA1_DIGEST_SIZE]; /** * Converts a block of plaintext of into its SHA1 hashed value. * * It would be nice if crypto had a wrapper to do this for us linear * people... */static intplaintext_to_sha1(unsigned char *hash, const char *plaintext, unsigned int len){	struct crypto_tfm *tfm;	struct scatterlist sg;	if (len > PAGE_SIZE) {		seclvl_printk(0, KERN_ERR, "Plaintext password too large (%d "			      "characters).  Largest possible is %lu "			      "bytes.\n", len, PAGE_SIZE);		return -EINVAL;	}	tfm = crypto_alloc_tfm("sha1", CRYPTO_TFM_REQ_MAY_SLEEP);	if (tfm == NULL) {		seclvl_printk(0, KERN_ERR,			      "Failed to load transform for SHA1\n");		return -EINVAL;	}	sg_init_one(&sg, (u8 *)plaintext, len);	crypto_digest_init(tfm);	crypto_digest_update(tfm, &sg, 1);	crypto_digest_final(tfm, hash);	crypto_free_tfm(tfm);	return 0;} /** * Called whenever the user writes to the sysfs passwd handle to this kernel * object.  It hashes the password and compares the hashed results. */static ssize_tpasswd_write_file(struct file * file, const char __user * buf,				size_t count, loff_t *ppos){	char *p;	int len;	unsigned char tmp[SHA1_DIGEST_SIZE]; 	if (!*passwd && !*sha1_passwd) {		seclvl_printk(0, KERN_ERR, "Attempt to password-unlock the "			      "seclvl module, but neither a plain text "			      "password nor a SHA1 hashed password was "			      "passed in as a module parameter!  This is a "			      "bug, since it should not be possible to be in "			      "this part of the module; please tell a "			      "maintainer about this event.\n");		return -EINVAL;	} 	if (count >= PAGE_SIZE)		return -EINVAL;	if (*ppos != 0)		return -EINVAL;	p = kmalloc(count, GFP_KERNEL);	if (!p)		return -ENOMEM;	len = -EFAULT;	if (copy_from_user(p, buf, count))		goto out;		len = count;	/* ``echo "secret" > seclvl/passwd'' includes a newline */	if (p[len - 1] == '\n')		len--;	/* Hash the password, then compare the hashed values */	if ((len = plaintext_to_sha1(tmp, p, len))) {		seclvl_printk(0, KERN_ERR, "Error hashing password: rc = "			      "[%d]\n", len);		goto out;	} 	len = -EPERM;	if (memcmp(hashedPassword, tmp, SHA1_DIGEST_SIZE))		goto out; 	seclvl_printk(0, KERN_INFO,		      "Password accepted; seclvl reduced to 0.\n");	seclvl = 0;	len = count; out:	kfree (p);	return len;} static struct file_operations passwd_file_ops = {	.write = passwd_write_file,}; /** * Explicitely disallow ptrace'ing the init process. */static int seclvl_ptrace(struct task_struct *parent, struct task_struct *child){	if (seclvl >= 0 && child->pid == 1) {		seclvl_printk(1, KERN_WARNING, "Attempt to ptrace "			      "the init process dissallowed in "			      "secure level %d\n", seclvl);		return -EPERM;	}	return 0;} /** * Capability checks for seclvl.  The majority of the policy * enforcement for seclvl takes place here. */static int seclvl_capable(struct task_struct *tsk, int cap){	int rc = 0; 	/* init can do anything it wants */	if (tsk->pid == 1)		return 0; 	if (seclvl > 0) {		rc = -EPERM; 		if (cap == CAP_LINUX_IMMUTABLE)			seclvl_printk(1, KERN_WARNING, "Attempt to modify "				      "the IMMUTABLE and/or APPEND extended "				      "attribute on a file with the IMMUTABLE "				      "and/or APPEND extended attribute set "				      "denied in seclvl [%d]\n", seclvl);		else if (cap == CAP_SYS_RAWIO)			seclvl_printk(1, KERN_WARNING, "Attempt to perform "				      "raw I/O while in secure level [%d] "				      "denied\n", seclvl);		else if (cap == CAP_NET_ADMIN)			seclvl_printk(1, KERN_WARNING, "Attempt to perform "				      "network administrative task while "				      "in secure level [%d] denied\n", seclvl);		else if (cap == CAP_SETUID)			seclvl_printk(1, KERN_WARNING, "Attempt to setuid "				      "while in secure level [%d] denied\n",				      seclvl);		else if (cap == CAP_SETGID)			seclvl_printk(1, KERN_WARNING, "Attempt to setgid "				      "while in secure level [%d] denied\n",				      seclvl);		else if (cap == CAP_SYS_MODULE)			seclvl_printk(1, KERN_WARNING, "Attempt to perform "				      "a module operation while in secure "				      "level [%d] denied\n", seclvl);		else			rc = 0;	} 	if (!rc) {		if (!(cap_is_fs_cap(cap) ? tsk->fsuid == 0 : tsk->euid == 0))			rc = -EPERM;	} 	if (rc)		seclvl_printk(1, KERN_WARNING, "Capability denied\n"); 	return rc;} /** * Disallow reversing the clock in seclvl > 1 */static int seclvl_settime(struct timespec *tv, struct timezone *tz){	if (tv && seclvl > 1) {		struct timespec now;		now = current_kernel_time();		if (tv->tv_sec < now.tv_sec ||		    (tv->tv_sec == now.tv_sec && tv->tv_nsec < now.tv_nsec)) {			seclvl_printk(1, KERN_WARNING, "Attempt to decrement "				      "time in secure level %d denied: "				      "current->pid = [%d], "				      "current->group_leader->pid = [%d]\n",				      seclvl, current->pid,				      current->group_leader->pid);			return -EPERM;		}		/* if attempt to decrement time */	}			/* if seclvl > 1 */	return 0;} /* claim the blockdev to exclude mounters, release on file close */static int seclvl_bd_claim(struct inode *inode){	int holder;	struct block_device *bdev = NULL;	dev_t dev = inode->i_rdev;	bdev = open_by_devnum(dev, FMODE_WRITE);	if (bdev) {		if (bd_claim(bdev, &holder)) {			blkdev_put(bdev);			return -EPERM;		}		/* claimed, mark it to release on close */		inode->i_security = current;	}	return 0;} /* release the blockdev if you claimed it */static void seclvl_bd_release(struct inode *inode){	if (inode && S_ISBLK(inode->i_mode) && inode->i_security == current) {		struct block_device *bdev = inode->i_bdev;		if (bdev) {			bd_release(bdev);			blkdev_put(bdev);			inode->i_security = NULL;		}	}} /** * Security for writes to block devices is regulated by this seclvl * function.  Deny all writes to block devices in seclvl 2.  In * seclvl 1, we only deny writes to *mounted* block devices. */static intseclvl_inode_permission(struct inode *inode, int mask, struct nameidata *nd){	if (current->pid != 1 && S_ISBLK(inode->i_mode) && (mask & MAY_WRITE)) {		switch (seclvl) {		case 2:			seclvl_printk(1, KERN_WARNING, "Write to block device "				      "denied in secure level [%d]\n", seclvl);			return -EPERM;		case 1:			if (seclvl_bd_claim(inode)) {				seclvl_printk(1, KERN_WARNING,					      "Write to mounted block device "					      "denied in secure level [%d]\n",					      seclvl);				return -EPERM;			}		}	}	return 0;} /** * The SUID and SGID bits cannot be set in seclvl >= 1 */static int seclvl_inode_setattr(struct dentry *dentry, struct iattr *iattr){	if (seclvl > 0) {		if (iattr->ia_valid & ATTR_MODE)			if (iattr->ia_mode & S_ISUID ||			    iattr->ia_mode & S_ISGID) {				seclvl_printk(1, KERN_WARNING, "Attempt to "					      "modify SUID or SGID bit "					      "denied in seclvl [%d]\n",					      seclvl);				return -EPERM;			}	}	return 0;} /* release busied block devices */static void seclvl_file_free_security(struct file *filp){	struct dentry *dentry = filp->f_dentry; 	if (dentry)		seclvl_bd_release(dentry->d_inode);} /** * Cannot unmount in secure level 2 */static int seclvl_umount(struct vfsmount *mnt, int flags){	if (current->pid != 1 && seclvl == 2) {		seclvl_printk(1, KERN_WARNING, "Attempt to unmount in secure "			      "level %d\n", seclvl);		return -EPERM;	}	return 0;} static struct security_operations seclvl_ops = {	.ptrace = seclvl_ptrace,	.capable = seclvl_capable,	.inode_permission = seclvl_inode_permission,	.inode_setattr = seclvl_inode_setattr,	.file_free_security = seclvl_file_free_security,	.settime = seclvl_settime,	.sb_umount = seclvl_umount,}; /** * Process the password-related module parameters */static int processPassword(void){	int rc = 0;	if (*passwd) {		char *p; 		if (*sha1_passwd) {			seclvl_printk(0, KERN_ERR, "Error: Both "				      "passwd and sha1_passwd "				      "were set, but they are mutually "				      "exclusive.\n");

Lines 1–500 of 670.

Keyboard shortcuts

General

Show keyboard shortcuts
?
Focus the current search
/

Issues and pull requests

Open filters
f

Issues, pull requests and Inbox

Select next item
j↓
Select previous item
k↑
Open selected item
Enter
Dismiss filters or clear selection
Esc

Repository code

Go to file
t