LWN.net Logo

Updated BSD Secure Levels Patch

From:  Michael Halcrow <mahalcro@us.ibm.com>
To:  linux-security-module@wirex.com
Subject:  [PATCH] Updated BSD Secure Levels Patch
Date:  Wed, 3 Dec 2003 15:47:08 -0600

This patches security/Makefile and security/Kconfig and creates
security/seclvl.c.  Previous patches that Serge sent to this list
address the settime hooks.

Changelog:
   12/02/2003  Updated by Michael A. Halcrow:
     1. Removed seclvl from /proc filesystem.
     2. Generated seclvl directory, with seclvl and passwd
             attributes, in the sysfs filesystem.
     3. Implemented password-based secure level reduction.  The
             password may be passed in either as plain text via the
             plaintextPassword module parameter, or in its
             hexadecimal SHA1 form via the sha1Password module
             parameter.  Note that you can generate the SHA1
             representation of a password with the sha1sum utility:
             echo -n "secret" | sha1sum
     4. Implemented rate-limiting of kernel messages to the log.

Notice that, when you compile the module into the kernel, the initial
secure level is set to 0, as opposed to when you compile the module
as a stand-alone, in which case the initial secure level is set to 1.
Most distributions out there want to be able to load modules and the
sort while booting.

The sha1 crypto module must be present in order to use the
password-based seclvl reduction.

Mike
--- lsm-2.5/security/Makefile	2003-11-11 14:36:52.000000000 -0600
+++ lsm-2.5_seclvl/security/Makefile	2003-12-03 13:43:33.000000000 -0600
@@ -26,6 +26,7 @@
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= commoncap.o root_plug.o
 obj-$(CONFIG_SECURITY_OWLSM)		+= owlsm.o
 obj-$(CONFIG_SECURITY_TPE)		+= tpe.o
+obj-$(CONFIG_SECURITY_SECLVL)		+= seclvl.o
 ifeq ($(CONFIG_LIDS),y)
 	obj-$(CONFIG_LIDS)	+= lids/built-in.o
 endif
--- lsm-2.5/security/Kconfig	2003-11-11 14:36:52.000000000 -0600
+++ lsm-2.5_seclvl/security/Kconfig	2003-12-03 13:41:52.000000000 -0600
@@ -181,7 +181,17 @@
 
 	  If you're unsure, answer N.
 
+config SECURITY_SECLVL
+	tristate "BSD SecureLevel"
+	depends on CRYPTO_SHA1!=n && SECURITY!=n
+	help
+	  Implements BSD Secure Levels as an LSM.
+	  Contact Michael A. Halcrow <mike@halcrow.us> for support
+         on this module.
+
+         If you're unsure, answer N.
+
 source security/lids/Kconfig
 
 endmenu
--- lsm-2.5/security/seclvl.c	1969-12-31 18:00:00.000000000 -0600
+++ lsm-2.5_seclvl/security/seclvl.c	2003-12-03 13:32:25.000000000 -0600
@@ -0,0 +1,875 @@
+/**
+ * LSM SecureLevel module for Linux.
+ *
+ * Maintainers:
+ *     Michael A. Halcrow <mike@halcrow.us>
+ * 	Kent Yoder <yoder1@us.ibm.com>
+ * 	Jerone Young <jeroney@us.ibm.com>
+ *	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) 2001 Networks Associates Technology, Inc <ssmalley@nai.com>
+ * Copyright (C) 2002 International Business Machines <robb@austin.ibm.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.
+ */
+
+/**
+ * Changelog:
+ *    12/02/2003  Updated by Michael A. Halcrow:
+ *      1. Removed seclvl from /proc filesystem.
+ *      2. Generated seclvl directory, with seclvl and passwd
+ *              attributes, in the sysfs filesystem.
+ *      3. Implemented password-based secure level reduction.  The
+ *              password may be passed in either as plain text via the
+ *              plaintextPassword module parameter, or in its
+ *              hexadecimal SHA1 form via the sha1Password module
+ *              parameter.  Note that you can generate the SHA1
+ *              representation of a password with the sha1sum utility:
+ *              echo -n "secret" | sha1sum
+ *      4. Implemented rate-limiting of kernel messages to the log.
+ *
+ *    11/24/2003  Updated by Michael A. Halcrow:
+ *      1. Added verbosity support.
+ *
+ *    11/13/2003  Updated by Michael A. Halcrow:
+ *      1. Polished up code and comments for first patch submission to
+ *              LSM dev.
+ *
+ *    10/23/2003  Updated by Serge Hallyn:
+ *	1. Fixed settime hook which was being activated on seclvl >= 1,
+ *		not seclvl > 1, as it should be.
+ *	2. (perhaps temp) Removed the secondary_ops support.  We can
+ *		add that back in later when all is fixed.  In the
+ *		mean time it's a distraction.
+ *	3. Removed dentry-related code.  We're not using it, and we
+ *		could not submit something with that in it.
+ *
+ *    10/20/2003  Updated by Serge Hallyn:
+ *	1. Optimistically re-based the patch off three new LSM hooks
+ *		whose re-introduction this project will hopefully
+ *		justify
+ *	2. Replaced the sysctl interface with a simple proc file, to
+ *		avoid the need to patch include/linux/sysctl.h
+ *	3. Capabilities code fix.
+ *	4. Removed unused hooks.
+ *
+ *    9/2003      Bulk of functionality written by Michael A. Halcrow
+ *
+ *    4/2003      Updated by Kent Yoder
+ *
+ *    4/2003      Updated by Jerone Young
+ *
+ *    4/2003      Created by Robb Romans
+ */
+
+/**
+ * Potential future enhancements:
+ *  - Export a kill_seclvl function to the rest of the kernel to allow
+ *    other modules to disable or change the seclvl (i.e., rootplug
+ *    could reduce the seclvl).
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/kernel.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>
+
+#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_MODULE
+static int initlvl = 1;
+#else
+static int initlvl = 0;
+#endif
+MODULE_PARM( initlvl, "i" );
+MODULE_PARM_DESC( initlvl, "Initial secure level (defaults to 1)" );
+
+/* Module parameter that defines the verbosity level */
+static int verbosity = 0;
+MODULE_PARM( verbosity, "i" );
+MODULE_PARM_DESC( verbosity, "Initial verbosity level (0 or 1; defaults to "
+		  "0, which is Quiet)" );
+
+/**
+ * Optional pathname which can be executed to bring seclvl to 0
+ * (i.e., for halt/reboot).  Defaults to NULL (no file, when executed,
+ * will cause the secure level to be reduced).
+ *
+ * You may find password-based secure level reduction to be easier to
+ * administrate and secure.  For example, you can pass the SHA1 hash of
+ * a password via the sha1Password seclvl module parameter from an
+ * init script and set the script file's immutable bit.  This module
+ * would then protect that script from modification by an attacker,
+ * and the attacker would (theoretically) gain nothing by reading the
+ * contents of that script file.
+ */
+static char* magicpath = NULL;
+MODULE_PARM( magicpath, "s" );
+MODULE_PARM_DESC( magicpath, "Pathname that sets seclvl=0 when executed.\n");
+
+/* dentry for said pathname */
+struct dentry* magicdentry = NULL;
+
+/**
+ * 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 sha1Password instead.
+ */
+static char* plaintextPassword = NULL;
+MODULE_PARM( plaintextPassword, "s" );
+MODULE_PARM_DESC( plaintextPassword, 
+		  "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
+ */
+static char* sha1Password = NULL;
+MODULE_PARM( sha1Password, "s" );
+MODULE_PARM_DESC( sha1Password, 
+		  "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_PARM( hideHash, "i" );
+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" );
+
+#if defined(CONFIG_SECURITY_SECLVL_MODULE)
+#define MY_NAME THIS_MODULE->name
+#else
+#define MY_NAME "seclvl"
+#endif
+
+/**
+ * This time-limits log writes to one per second.
+ */
+#define seclvl_printk( verb, type, fmt, arg... )                     \
+	do {                                                         \
+		if( verbosity >= verb ) {                            \
+                        static int priorTimestamp = 0;               \
+                        if( ( jiffies - priorTimestamp ) < HZ )      \
+                                break;                               \
+			printk( type "%s: %s: " fmt,                 \
+				MY_NAME, __FUNCTION__,               \
+				## arg );                            \
+			priorTimestamp = jiffies;                    \
+                }                                                    \
+	} while( 0 )
+
+/**
+ * kobject stuff
+ */
+struct subsystem seclvl_subsys;
+
+struct seclvl_obj {
+	char *name;
+	struct list_head slot_list;
+	struct kobject kobj;
+};
+
+struct seclvl_attribute {
+	struct attribute attr;
+	ssize_t (*show)( struct seclvl_obj*, char* );
+	ssize_t (*store)( struct seclvl_obj*, const char*, size_t );
+};
+
+ssize_t seclvl_attr_store( struct kobject* kobj,
+			   struct attribute* attr, const char* buf,
+			   size_t len )
+{
+	struct seclvl_obj* obj = container_of( kobj, struct seclvl_obj, kobj );
+	struct seclvl_attribute* attribute = 
+		container_of( attr, struct seclvl_attribute, attr );
+	return ( attribute->store ? attribute->store( obj, buf, len ) : 0 );
+}
+
+static ssize_t seclvl_attr_show( struct kobject* kobj,
+				 struct attribute* attr, char* buf )
+{
+	struct seclvl_obj* obj =
+		container_of( kobj, struct seclvl_obj, kobj );
+	struct seclvl_attribute* attribute =
+		container_of( attr, struct seclvl_attribute, attr );
+	return ( attribute->show ? attribute->show(obj, buf) : 0 );
+}
+
+struct sysfs_ops seclvlfs_sysfs_ops = {
+	.show = seclvl_attr_show,
+	.store = seclvl_attr_store,
+};
+
+static struct kobj_type seclvl_ktype = {
+	.sysfs_ops = &seclvlfs_sysfs_ops
+};
+
+decl_subsys( seclvl, &seclvl_ktype, NULL );
+
+/**
+ * The actual security level.  Ranges between -1 and 2 inclusive.
+ */
+static int seclvl = 0;
+
+/**
+ * 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.
+ */
+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;
+}
+
+int do_seclvl_advance( int );
+
+/**
+ * Called whenever the user reads the sysfs handle to this kernel
+ * object
+ */
+static ssize_t seclvl_read_file( struct seclvl_obj* obj, char* buff )
+{
+	ssize_t len = 0;
+	len = snprintf( buff, PAGE_SIZE, "%d\n", seclvl );
+	return len;
+}
+
+/**
+ * Called whenever the user writes to the sysfs handle to this kernel
+ * object (seclvl/seclvl).  It expects a single-digit number.
+ */
+static ssize_t seclvl_write_file( struct seclvl_obj* obj, const char* buff,
+				  size_t count)
+{
+	unsigned long val;
+	if( count > 2 || ( count == 2 && buff[1] != '\n' ) ) {
+		seclvl_printk( 1, KERN_WARNING, "Invalid value passed to seclvl: "
+			       "[%s]\n", buff );
+		return -EINVAL;
+	}
+	val = buff[0]-48;
+	if( seclvl_sanity( val ) ) {
+		seclvl_printk( 1, KERN_WARNING, "Illegal secure level "
+			       "requested: [%d]\n", (int)val );
+		return -EPERM;
+	}
+	if( do_seclvl_advance( val ) ) {
+		seclvl_printk( 0, KERN_ERR, "Failure advancing security level "
+			       "to %lu\n", val );
+	}
+	return count;
+}
+
+struct seclvl_attribute seclvlfs_seclvl_attr = {
+	.attr = { .name = "seclvl", .mode = S_IFREG | S_IRUGO | S_IWUSR },
+	.show = seclvl_read_file,
+	.store = seclvl_write_file,
+};
+
+unsigned char hashedPassword[SHA1_DIGEST_SIZE];
+
+/**
+ * Called whenever the user reads the sysfs passwd handle.
+ */
+static ssize_t seclvl_read_passwd( struct seclvl_obj* obj, char* buff )
+{
+	/* So just how good *is* your password? :-) */
+	char tmp[3];
+	int i = 0;
+	buff[0]='\0';
+	if( hideHash ) {
+		/* Security through obscurity */
+		return 0;
+	}
+	while( i < SHA1_DIGEST_SIZE ) {
+		snprintf( tmp, 3, "%02x", hashedPassword[i] );
+		strncat( buff, tmp, 2 );
+		i++;
+	}
+	strcat( buff, "\n" );
+	return ( ( SHA1_DIGEST_SIZE * 2 ) + 1 );
+}
+
+/**
+ * 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...
+ */
+int plaintextToSha1( unsigned char* hash, const char* plaintext, int len )
+{
+        struct crypto_tfm* tfm;
+        struct scatterlist sg[1];
+	int rc = 0;
+	if( len > PAGE_SIZE ) {
+		seclvl_printk( 0, KERN_ERR, "Plaintext password too large (%d "
+			       "characters).  Largest possible is %lu bytes.\n", len,
+			       PAGE_SIZE );
+		rc = -ENOMEM;
+		goto exit;
+	}
+        tfm = crypto_alloc_tfm( "sha1", 0 );
+        if( tfm == NULL ) {
+                seclvl_printk( 0, KERN_ERR, "Failed to load transform for SHA1\n");
+		rc = -ENOSYS;
+		goto exit;
+        }
+	sg[0].page = virt_to_page( plaintext );
+	sg[0].offset = offset_in_page( plaintext );
+	sg[0].length = len;
+	crypto_digest_init( tfm );
+	crypto_digest_update( tfm, sg, 1 );
+	crypto_digest_final( tfm, hash );
+        crypto_free_tfm( tfm );
+exit:
+	return rc;
+}
+
+/**
+ * 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_t seclvl_write_passwd( struct seclvl_obj* obj, const char* buff,
+				    size_t count)
+{
+	int i;
+	unsigned char tmp[SHA1_DIGEST_SIZE];
+	int rc;
+	int len;
+	if( !( plaintextPassword || sha1Password ) ) {
+		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;
+	}
+	len = strlen( buff );
+        /* ``echo "secret" > seclvl/passwd'' includes a	newline */
+	if( buff[len-1] == '\n' ) { 
+		len--;
+	}
+	/* Hash the password, then compare the hashed values */
+	if( ( rc = plaintextToSha1( tmp, buff, len ) ) ) {
+		seclvl_printk( 0, KERN_ERR, "Error hashing password: rc = [%d]\n",
+			       rc );
+		return rc;
+	}
+	for( i = 0; i < SHA1_DIGEST_SIZE; i++ ) {
+		if( hashedPassword[i] != tmp[i] ) {
+			return -EPERM;
+		}
+	}
+	seclvl_printk( 0, KERN_INFO, "Password accepted; seclvl reduced to 0.\n" );
+	seclvl = 0;
+	return count;
+}
+
+struct seclvl_attribute seclvlfs_passwd_attr = {
+	.attr = { .name = "passwd", .mode = S_IFREG | S_IRUGO | S_IWUSR },
+	.show = seclvl_read_passwd,
+	.store = seclvl_write_passwd,
+};
+
+/**
+ * Explicitely disallow ptrace'ing the init process.
+ */
+static int seclvl_ptrace( struct task_struct* parent, 
+			  struct task_struct* child ) 
+{ 
+	if( seclvl >= 0 ) {
+		if( 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 )
+{
+	/* init can do anything it wants */
+	if( tsk->pid == 1 ) {
+		return 0;
+	}
+
+	switch( seclvl ) {
+	case 2:
+		/* fall through */
+	case 1:
+		if( cap == CAP_LINUX_IMMUTABLE ) {
+			seclvl_printk( 1, KERN_WARNING, "Attempt to modify a "
+				       "file with S_IMMUTABLE and/or S_APPEND "
+				       "file attributes in secure level [%d] "
+				       "denied\n", seclvl );
+			return -EPERM;
+		} else if( cap==CAP_SYS_RAWIO ) { // Somewhat broad...
+			seclvl_printk( 1, KERN_WARNING, "Attempt to perform "
+				       "raw I/O while in secure level [%d] "
+				       "denied\n", seclvl );
+			return -EPERM;
+		} 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 );
+			return -EPERM;
+		} else if( cap == CAP_SETUID ) {
+			seclvl_printk( 1, KERN_WARNING, "Attempt to setuid "
+				       "while in secure level [%d] denied\n", 
+				       seclvl );
+			return -EPERM;      
+		} 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 );
+			return -EPERM;      
+		}
+		break;
+	case 0:
+		/* fall through */
+	case -1:
+		/* fall through */
+	default: 
+		break;
+	}
+	/* from dummy.c */
+	if( cap_is_fs_cap( cap ) ? tsk->fsuid == 0 : tsk->euid == 0 )
+		return 0; /* capability granted */
+	seclvl_printk( 1, KERN_WARNING, "Capability denied\n" );
+	return -EPERM; /* capability denied */
+}
+
+/**
+ * Disallow reversing the clock in seclvl > 1
+ */
+static int seclvl_settime( struct timespec* tv, struct timezone* tz )
+{
+	struct timespec now;
+	if( seclvl > 1 ) {
+		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;
+}
+
+/**
+ * This function gives the administrator the option of breaking out of
+ * Secure Levels by executing a file of his choice.  It is assumed
+ * that, if an administrator chooses to use this feature, he is
+ * employing additional security measures to prevent an attacker from
+ * using this to circumvent the security policies of the system.
+ */
+static int seclvl_bprm_set_security( struct linux_binprm* bprm )
+{
+	if( !magicdentry )
+		return 0;
+	if( bprm->file->f_dentry->d_inode == magicdentry->d_inode ) {
+		seclvl_printk( 1, KERN_WARNING, "%s called: reset seclvl=0\n",
+			       bprm->filename );
+		seclvl = 0;
+	}
+	return 0;
+}
+
+int is_mounted( struct inode* inode )
+{
+	struct super_block* sb;
+	if( S_ISBLK( inode->i_mode ) ) {
+		struct block_device* bdev = inode->i_bdev;
+		sb = get_super( bdev );
+		if( sb ) {
+			drop_super(sb);
+			return 1;
+		}
+	}
+	    return 0;
+}
+
+/**
+ * Security for writes to block devices is regulated by this seclvl
+ * function.
+ */
+static int seclvl_inode_permission( struct inode* inode, int mask,
+				    struct nameidata* nd)
+{
+	/*
+	 * For now deny all writes to block devices in seclvl 1 & 2.
+	 * We really want to only do that in 2.  In seclvl 1, we only
+	 * want to deny write to *mounted* block devices.
+	 */
+	if( current->pid == 1 )
+		return 0;
+	if( 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( is_mounted( inode ) ) {
+				seclvl_printk( 1, KERN_WARNING, "Write to "
+					       "mounted block device denied "
+					       "in secure level [%d]\n",
+					       seclvl );
+				return -EPERM;
+			}
+		} /* switch seclvl */
+	} /* If attempting to write to block device */
+	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;
+}
+
+/**
+ * Cannot unmount in secure level 2
+ */
+static int
+seclvl_umount( struct vfsmount* mnt, int flags )
+{
+	if( current->pid == 1 ) {
+		return 0;
+	}
+	if( seclvl == 2 ) {
+		seclvl_printk( 1, KERN_WARNING, "Attempt to unmount in secure "
+			       "level %d\n", seclvl );
+		return -EPERM;
+	}
+	return 0;
+}
+
+static void seclvl_task_reparent_to_init( struct task_struct* p )
+{ 
+	p->euid = p->fsuid = 0;
+	return; 
+}
+
+static int seclvl_register( const char* name, struct security_operations* ops )
+{
+	return -EINVAL;
+}
+
+static int seclvl_unregister( const char* name, 
+			      struct security_operations* ops )
+{
+	return -EINVAL;
+}
+
+static struct security_operations seclvl_ops = {
+	.ptrace =			seclvl_ptrace,
+	.capable =			seclvl_capable,
+	.bprm_set_security =		seclvl_bprm_set_security,
+	.inode_permission =		seclvl_inode_permission,
+	.inode_setattr =		seclvl_inode_setattr,
+	.settime =			seclvl_settime,
+	.sb_umount =			seclvl_umount,
+	.task_reparent_to_init =	seclvl_task_reparent_to_init,
+	.register_security =		seclvl_register,
+	.unregister_security =		seclvl_unregister,
+};
+
+#if defined(CONFIG_SECURITY_SECLVL_MODULE)
+#define MY_NAME THIS_MODULE->name
+#else
+#define MY_NAME "seclvl"
+#endif
+
+/**
+ * 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.
+ */
+int do_seclvl_advance( int newlvl )
+{
+	if( newlvl <= seclvl ) {
+		seclvl_printk( 1, KERN_WARNING, "Cannot advance to seclvl "
+			       "[%d]\n", newlvl );
+		return -EINVAL;
+	}
+	if( newlvl > 2 ) {
+		seclvl_printk( 1, KERN_WARNING, "Cannot advance to seclvl "
+			       "[%d]\n", newlvl );
+		return -EINVAL;
+	}
+	if( seclvl == -1 ) {
+		seclvl_printk( 1, KERN_WARNING, "Not allowed to advance to "
+			       "seclvl [%d]\n", seclvl );
+		return -EPERM;
+	}
+	seclvl = newlvl;
+	return 0;
+}
+
+/**
+ * Process the password-related module parameters
+ */
+int processPassword( void )
+{
+	int rc = 0;
+	hashedPassword[0] = '\0';
+	if( plaintextPassword ) {
+		if( sha1Password ) {
+			seclvl_printk( 0, KERN_ERR, "Error: Both plaintextPassword "
+				       "and sha1Password were set, but they are "
+				       "mutually exclusive.\n" );
+			rc = -EINVAL;
+			goto exit;
+		}
+		if( ( rc = plaintextToSha1( hashedPassword, plaintextPassword,
+					    strlen( plaintextPassword ) ) ) ) {
+			seclvl_printk( 0, KERN_ERR, "Error: SHA1 support not in "
+				       "kernel\n" );
+			goto exit;
+		}
+		memset( plaintextPassword, strlen( plaintextPassword ), 0 );
+	} else if( sha1Password ) { // Base 16
+		int i;
+		i = strlen( sha1Password );
+		if( i != ( SHA1_DIGEST_SIZE * 2 ) ) {
+			seclvl_printk( 0, KERN_ERR, "Received [%d] bytes; expected "
+				       "[%d] for the hexadecimal representation "
+				       "of the SHA1 hash of the password.\n",
+				       i, ( SHA1_DIGEST_SIZE * 2 ) );
+			rc = -EINVAL;
+			goto exit;
+		}
+		while( ( i -= 2 ) + 2 ) {
+			unsigned char tmp;
+			tmp = sha1Password[i+2];
+			sha1Password[i+2] = '\0';
+			hashedPassword[i/2] = (unsigned char)
+				simple_strtol( &sha1Password[i], NULL, 16 );
+			sha1Password[i+2] = tmp;
+		}
+	}
+ exit:
+	return rc;
+}
+
+/**
+ * Process the magicpath module parameter
+ */
+int processPath( void )
+{
+	int rc = 0;
+	if( magicpath ) {
+		struct nameidata nd;
+		rc = path_lookup( magicpath, LOOKUP_FOLLOW, &nd );
+		if( rc ) {
+			/* 
+			 * Better not to load, or to potentially mess up
+			 * filesystem by loading?
+			 * Maybe in these days of jfs's, we should load?
+			 */
+			seclvl_printk( 0, KERN_ERR, "Seclvl: Bad filename [%s] "
+				       " specified for seclvl escape.\n"
+				       "Seclvl NOT LOADED (%d).\n",
+				       magicpath, rc);
+			goto exit;
+		}
+		magicdentry = dget( nd.dentry );
+		path_release( &nd );
+	}
+ exit:
+	return rc;
+}
+
+/**
+ * Sysfs registrations
+ */
+int doSysfsRegistrations( void )
+{
+	int rc = 0;
+	if( ( rc = subsystem_register( &seclvl_subsys ) ) ) {
+		seclvl_printk( 0, KERN_WARNING, 
+			       "Error [%d] registering seclvl subsystem\n", rc );
+		goto exit;
+	}
+	sysfs_create_file( &seclvl_subsys.kset.kobj,
+			   &seclvlfs_seclvl_attr.attr );
+	if( plaintextPassword || sha1Password ) {
+		sysfs_create_file( &seclvl_subsys.kset.kobj,
+				   &seclvlfs_passwd_attr.attr );
+	}
+ exit:
+	return rc;
+}
+
+/**
+ * Initialize the seclvl module.
+ */
+static int __init seclvl_init( void ) {
+	int rc = 0;
+	if( verbosity < 0 || verbosity > 1 ) {
+		printk( KERN_ERR "Error: bad verbosity [%d]; only 0 or 1 "
+			"are valid values\n", verbosity );
+		rc = -EINVAL;
+		goto exit;
+	}
+	if( initlvl < -1 || initlvl > 2 ) {
+		seclvl_printk( 0, KERN_ERR, "Error: bad initial securelevel "
+			       "[%d].\n", initlvl );
+		rc = -EINVAL;
+		goto exit;
+	}
+	seclvl = initlvl;
+	if( ( rc = processPassword() ) ) {
+		seclvl_printk( 0, KERN_ERR, "Error processing the password module "
+			       "parameter(s): rc = [%d]\n", rc );
+		goto exit;
+	}
+	if( ( rc = processPath() ) ) {
+		seclvl_printk( 0, KERN_ERR, "Error processing the magicpath module "
+			       "parameter: rc = [%d]\n", rc );
+		goto exit;
+	}
+	/* register ourselves with the security framework */
+	if( register_security( &seclvl_ops ) ) {
+		seclvl_printk( 0, KERN_ERR,
+			       "seclvl: Failure registering with the "
+			       "kernel.\n" );
+		/* try registering with primary module */
+		if( mod_reg_security( MY_NAME, &seclvl_ops ) ) {
+			seclvl_printk( 0, KERN_ERR, "seclvl: Failure "
+				       "registering with primary security "
+				       "module.\n" );
+			goto exit_dput;
+		} /* if primary module registered */
+		secondary = 1;
+	} /* if we registered ourselves with the security framework */
+	if( ( rc = doSysfsRegistrations() ) ) {
+		seclvl_printk( 0, KERN_ERR, "Error registering with sysfs\n" );
+		goto exit_dput;
+	}
+	seclvl_printk( 0, KERN_INFO, "seclvl: Successfully initialized.\n" );
+	goto exit;
+ exit_dput:
+	if( magicdentry ) {
+		dput( magicdentry );
+		magicdentry = NULL;
+	}
+ exit:
+	if( rc ) {
+		printk( KERN_ERR "seclvl: Error during initialization: rc = [%d]\n",
+			rc );
+	}
+	return rc;
+}
+
+/**
+ * Remove the seclvl module.
+ */
+static void __exit seclvl_exit( void )
+{
+	sysfs_remove_file( &seclvl_subsys.kset.kobj,
+			   &seclvlfs_seclvl_attr.attr );
+	if( plaintextPassword || sha1Password ) {
+		sysfs_remove_file( &seclvl_subsys.kset.kobj,
+				   &seclvlfs_passwd_attr.attr );
+	}
+	subsystem_unregister( &seclvl_subsys );
+	if( magicdentry ) {
+		dput( magicdentry );
+		magicdentry = NULL;
+	}
+	if( unregister_security( &seclvl_ops ) ) {
+		seclvl_printk( 0, KERN_INFO,
+			       "seclvl: Failure unregistering with the "
+			       "kernel\n" );
+	}
+}
+
+module_init( seclvl_init );
+module_exit( seclvl_exit );
+
+MODULE_AUTHOR( "Michael A. Halcrow <mike@halcrow.us>" );
+MODULE_DESCRIPTION( "LSM implementation of the *BSD Secure Levels" );
+MODULE_LICENSE( "GPL" );

Copyright © 2003, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds