| From: |
| Michael Halcrow <mike@halcrow.us> |
| To: |
| linux-security-module@wirex.com |
| Subject: |
| [PATCH] BSD Secure Levels, w/out magicpath |
| Date: |
| Mon, 5 Jan 2004 17:04:17 -0600 |
Magicpath was redundant, so I removed it, in preference to the
password-based seclvl reduction. I made the patch against the vanilla
2.6.0 kernel. It depends on Serge's settime patch. I haven't heard
much discussion regarding it on this list though. Are we ready to
send these patches to the LKML?
Mike
.___________________________________________________________________.
Michael A. Halcrow
Security Software Engineer, IBM Linux Technology Center
GnuPG Fingerprint: 05B5 08A8 713A 64C1 D35D 2371 2D3C FDDA 3EB6 601D
diff -Nur linux-2.6.0_vanilla/security/Kconfig linux-2.6.0_seclvl/security/Kconfig
--- linux-2.6.0_vanilla/security/Kconfig 2003-12-17 20:59:05.000000000 -0600
+++ linux-2.6.0_seclvl/security/Kconfig 2004-01-05 14:47:15.000000000 -0600
@@ -44,6 +44,16 @@
If you are unsure how to answer this question, 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/selinux/Kconfig
endmenu
diff -Nur linux-2.6.0_vanilla/security/Makefile linux-2.6.0_seclvl/security/Makefile
--- linux-2.6.0_vanilla/security/Makefile 2003-12-17 20:59:06.000000000 -0600
+++ linux-2.6.0_seclvl/security/Makefile 2004-01-05 14:47:45.000000000 -0600
@@ -17,3 +17,4 @@
endif
obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o
+obj-$(CONFIG_SECURITY_SECLVL) += seclvl.o
diff -Nur linux-2.6.0_vanilla/security/seclvl.c linux-2.6.0_seclvl/security/seclvl.c
--- linux-2.6.0_vanilla/security/seclvl.c 1969-12-31 18:00:00.000000000 -0600
+++ linux-2.6.0_seclvl/security/seclvl.c 2004-01-05 15:35:50.000000000 -0600
@@ -0,0 +1,803 @@
+/**
+ * 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:
+ * 01/05/2004 Updated by Michael A. Halcrow:
+ * 1. Removed magicpath kludge
+ *
+ * 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 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 the "
+ "IMMUTABLE and/or APPEND extended "
+ "attribute on a file with the IMMUTABLE "
+ "extended attribute set denied in selvl "
+ "[%d]", 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;
+}
+
+static int seclvl_bprm_set_security( struct linux_binprm* bprm )
+{
+ 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;
+}
+
+/**
+ * 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;
+ }
+ /* 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;
+ } /* 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;
+ }
+ seclvl_printk( 0, KERN_INFO, "seclvl: Successfully initialized.\n" );
+ goto exit;
+ 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( 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" );