[NOT READY TO BE SENT ANYWHERE]

  Hello,

  this patch (v0.1, against 2.5.64) introduces some trivial and very
preliminary support for security levels concept, implemented as LSM. It exports
its interface through sysfs at /sys/security/levels now and it should be pretty
self-explaining. There is number of issues with it yet and only fraction of the
desired support is implemented so far, but it already seems to work at least
somehow ;-). Note that you need to have all other LSMs disabled (both
capabilities and the usb example).

  This patch depends on
http://pasky.ji.cz/~pasky/dev/kernel/security_kobj.patch (against 2.5.64 as
well, really quite trivial one).

  See http://pasky.ji.cz/securitylevels/ for more info, TODO list and bug list,
etc.

 MAINTAINERS                              |    6
 security/Kconfig                         |    2
 security/Makefile                        |    1
 security/securitylevels/Kconfig          |   18
 security/securitylevels/Makefile         |    6
 security/securitylevels/securitylevels.c |  608 +++++++++++++++++++++++++++++++

  Kind regards,
				Petr Baudis

--- linux/MAINTAINERS	Thu Mar  6 20:37:57 2003
+++ linux+pasky/MAINTAINERS	Thu Mar  6 20:38:48 2003
@@ -1590,6 +1590,12 @@
 W:	http://www.weinigel.se
 S:	Supported
 
+SECURITY LEVELS
+P:	Petr Baudis
+M:	pasky@ucw.cz
+W:	http://pasky.ji.cz/securitylevels/
+S:	Maintained
+
 SGI VISUAL WORKSTATION 320 AND 540
 P:	Andrey Panin
 M:	pazke@orbita1.ru
diff -ruN linux/security/Kconfig linux+pasky/security/Kconfig
--- linux/security/Kconfig	Thu Mar  6 20:31:32 2003
+++ linux+pasky/security/Kconfig	Thu Mar  6 23:22:04 2003
@@ -31,6 +31,8 @@
 	  This enables the "default" Linux capabilities functionality.
 	  If you are unsure how to answer this question, answer Y.
 
+source "security/securitylevels/Kconfig"
+
 config SECURITY_ROOTPLUG
 	tristate "Root Plug Support"
 	depends on SECURITY!=n
diff -ruN linux/security/Makefile linux+pasky/security/Makefile
--- linux/security/Makefile	Thu Mar  6 20:31:32 2003
+++ linux+pasky/security/Makefile	Thu Mar  6 23:22:47 2003
@@ -10,4 +10,5 @@
 # Object file lists
 obj-$(CONFIG_SECURITY)			+= security.o dummy.o
 obj-$(CONFIG_SECURITY_CAPABILITIES)	+= capability.o
+obj-$(CONFIG_SECURITY_SECURITYLEVELS)	+= securitylevels/
 obj-$(CONFIG_SECURITY_ROOTPLUG)		+= root_plug.o
diff -ruN linux/security/securitylevels/Kconfig linux+pasky/security/securitylevels/Kconfig
--- linux/security/securitylevels/Kconfig	Thu Jan  1 01:00:00 1970
+++ linux+pasky/security/securitylevels/Kconfig	Thu Mar  6 23:22:00 2003
@@ -0,0 +1,18 @@
+
+config SECURITY_SECURITYLEVELS
+	tristate "Security Levels Support"
+	depends on SECURITY!=n
+	help
+	  This enables support for scaling of global control over the
+	  machine to several levels, so-called "security levels". You
+	  can limit various actions to certain security level and
+	  lower, from network (re)configuration to file permissions
+	  validity. Under normal conditions, security level can be only
+	  raised unless a special condition is fulfilled. This feature
+	  can resemble OpenBSD's security levels, but it is much more
+	  powerful and versatile.
+
+	  See <http://pasky.ji.cz/securitylevels/> for more information
+	  about this functionality.
+
+	  If you are unsure how to answer this question, answer N.
diff -ruN linux/security/securitylevels/Makefile linux+pasky/security/securitylevels/Makefile
--- linux/security/securitylevels/Makefile	Thu Jan  1 01:00:00 1970
+++ linux+pasky/security/securitylevels/Makefile	Thu Mar  6 23:23:24 2003
@@ -0,0 +1,6 @@
+#
+# Makefile for the security levels code
+#
+
+# Object file lists
+obj-$(CONFIG_SECURITY_SECURITYLEVELS)	+= securitylevels.o
diff -ruN linux/security/securitylevels/securitylevels.c linux+pasky/security/securitylevels/securitylevels.c
--- linux/security/securitylevels/securitylevels.c	Thu Jan  1 01:00:00 1970
+++ linux+pasky/security/securitylevels/securitylevels.c	Sun Mar  9 00:37:09 2003
@@ -0,0 +1,608 @@
+/*
+ * Security levels support for scaling of global control over the system
+ *
+ * Copyright (C) 2003 Petr Baudis <pasky@ucw.cz>
+ *
+ * This enables support for scaling of global control over the machine to
+ * several levels, so-called "security levels". You can limit various actions
+ * to certain security level and lower, from network (re)configuration to file
+ * permissions validity.  Under normal conditions, security level can be only
+ * raised if a special condition is fulfilled. This feature can resemble
+ * OpenBSD's security levels, but it is much more powerful and versatile.
+ *
+ *	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.
+ *
+ * Based on capability.c.
+ *
+ * TODO: Make this security module stackable. --pasky
+ */
+
+#include <linux/config.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/kobject.h>
+#include <linux/capability.h>
+#include <linux/security.h>
+
+
+struct privileged_action {
+	char *name; /* Used as a file name as well; thus it must be less than
+		     * KOBJ_NAME_LEN (16). */
+	int level; /* Limiting usability by current security level (from this
+		    * one down) */
+	char *desc; /* Short description of the action */
+
+	struct kobject kobj;
+};
+
+/* Note that you shouldn't rely on real PRIVACT_* values and privileged_actions
+ * indexes, the order can change in future. If you will ever export this to the
+ * rest of the kernel, you OUGHT TO provide some name2index() function. */
+
+/* TODO: This should have own exported interface and others should be able to
+ * at least register to it, depending mainly on whether this will be merged to
+ * mainline or not (then the registration could be maybe spreat a little around
+ * to specific places). */
+
+static struct privileged_action privileged_actions[] = {
+#define	PRIVACT_LEVEL_LOWER	0
+	{
+		.name	= "level_lower",
+		.level	= 1,
+		.desc	= "Lower the security level (without further "
+			  "restrictions).",
+	},
+
+#define	PRIVACT_LEVEL_RAISE	1
+	{
+		.name	= "level_raise",
+		.level	= 7,
+		.desc	= "Raise the security level.",
+	},
+
+#define	PRIVACT_KILL_ALIENS	2
+	{
+		.name	= "kill_aliens",
+		.level	= 5,
+		.desc	= "Allow processes with CAP_KILL (ie. normally all "
+			  "root processes) to kill processes belonging to "
+			  "other users.",
+	},
+
+#define	PRIVACT_SETUID		3
+	{
+		.name	= "setuid",
+		.level	= 5,
+		.desc	= "Allow processes with CAP_SETUID (ie. normally all "
+			  "root processes) to call setuid() and setgid() calls "
+			  "and render setuid bit of files void.",
+	},
+
+#define	PRIVACT_IMMUTABLE_CHG	4
+	{
+		.name	= "immutable_chg",
+		.level	= 3,
+		.desc	= "Allow changing of the immutable and append file "
+			  "flags.",
+	},
+
+#define	PRIVACT_BIND_PRIVILEGED	5
+	{
+		.name	= "bind_privileged",
+		.level	= 5,
+		.desc	= "Allow root processes to bind() a port < 1024.",
+	},
+
+	/* FIXME: This shares CAP_NET_ADMIN. */
+#define	PRIVACT_NET_IFACES	6
+	{
+		.name	= "net_ifaces",
+		.level	= 4,
+		.desc	= "Allow to manipulate with the network interfaces.",
+	},
+
+	/* FIXME: This shares CAP_NET_ADMIN. */
+#define	PRIVACT_NET_ROUTES	7
+	{
+		.name	= "net_routes",
+		.level	= 4,
+		.desc	= "Allow to manipulate with the routing table.",
+	},
+
+	/* FIXME: This shares CAP_NET_ADMIN. */
+#define	PRIVACT_NET_IPTABLES	8
+	{
+		.name	= "net_iptables",
+		.level	= 4,
+		.desc	= "Allow to manipulate with iptables.",
+	},
+
+	/* FIXME: This shares CAP_NET_ADMIN. */
+#define PRIVACT_NET_PROMISC	9
+	{
+		.name	= "net_promisc",
+		.level	= 5,
+		.desc	= "Allow to put an interface to promiscuous mode.",
+	},
+
+#define	PRIVACT_NET_RAWSOCK	10
+	{
+		.name	= "net_rawsock",
+		.level	= 5,
+		.desc	= "Allow to create raw sockets.",
+	},
+
+#define	PRIVACT_MODULES		11
+	{
+		.name	= "modules",
+		.level	= 2,
+		.desc	= "Allow to load/unload modules from kernel.",
+	},
+
+	/* TODO: Merge VM86_REQUEST_IRQ. */
+#define	PRIVACT_RAWIO		12
+	{
+		.name	= "rawio",
+		.level	= 2,
+		.desc	= "Allow userland a direct access to hardware.",
+	},
+
+#define	PRIVACT_CHROOT		13
+	{
+		.name	= "chroot",
+		.level	= 4,
+		.desc	= "Allow processes to be chroot()ed.",
+	},
+
+#define	PRIVACT_PTRACE		14
+	{
+		.name	= "ptrace",
+		.level	= 5,
+		.desc	= "Allow a process to be ptrace()d.",
+	},
+
+#define	PRIVACT_PACCT		15
+	{
+		.name	= "pacct",
+		.level	= 4,
+		.desc	= "Allow to reconfigure process accounting.",
+	},
+
+	/* FIXME: This shares CAP_SYS_ADMIN. */
+#define	PRIVACT_QUOTAS		16
+	{
+		.name	= "quotas",
+		.level	= 4,
+		.desc	= "Allow to modify the disk quotas.",
+	},
+
+	/* FIXME: This shares CAP_SYS_ADMIN. */
+#define	PRIVACT_PRINTK_LOG	17
+	{
+		.name	= "printk_log",
+		.level	= 3,
+		.desc	= "Allow to modify printk() behaviour (log level).",
+	},
+
+	/* FIXME: This shares CAP_SYS_ADMIN. */
+#define	PRIVACT_HOSTNAME	18
+	{
+		.name	= "hostname",
+		.level	= 3,
+		.desc	= "Allow to change machine's hostname and domainname.",
+	},
+
+	/* FIXME: This shares CAP_SYS_ADMIN. */
+#define	PRIVACT_MOUNT		19
+	{
+		.name	= "mount",
+		.level	= 3,
+		.desc	= "Allow to (un)mount devices.",
+	},
+
+	/* FIXME: This shares CAP_SYS_ADMIN. */
+#define	PRIVACT_SWAP		20
+	{
+		.name	= "swap",
+		.level	= 3,
+		.desc	= "Allow to configure swap devices.",
+	},
+
+	/* Covers the most of CAP_SYS_ADMIN, unassigned things are stuffed into
+	 * it.
+	 * TODO: Always allow semaphores removing, create bdflush action
+	 * (connected with sync...?), always allow chowning IPC stuff (?),
+	 * always allow IPC shm locking, always allow forged pids (?), create
+	 * loopback encryption action. */
+#define	PRIVACT_HWCTL		21
+	{
+		.name	= "hwctl",
+		.level	= 2,
+		.desc	= "Allow configuration of various drivers and certain "
+			  "operations related with hardware setup.",
+	},
+
+#define	PRIVACT_REBOOT		22
+	{
+		.name	= "reboot",
+		.level	= 6,
+		.desc	= "Allow reboot of the system.",
+	},
+
+#define	PRIVACT_NICE		23
+	{
+		.name	= "nice",
+		.level	= 5,
+		.desc	= "Allow to renice a task.",
+	},
+
+#define	PRIVACT_RESLIMITS	24
+	{
+		.name	= "reslimits",
+		.level	= 6,
+		.desc	= "Allow root to override the user limits.",
+	},
+
+#define	PRIVACT_TIME_SET	25
+	{
+		.name	= "time_set",
+		.level	= 6,
+		.desc	= "Allow root to change time.",
+	},
+
+	/* TODO: Allow S_IFREG or S_IFIFO. */
+#define	PRIVACT_MKNOD		27
+	{
+		.name	= "mknod",
+		.level	= 3,
+		.desc	= "Allow mknod() (to create device inodes).",
+	},
+
+#define	PRIVACT_LEASE		28
+	{
+		.name	= "file_lease",
+		.level	= 5,
+		.desc	= "Allow root to lease a file.",
+	},
+
+#define PRIVACT_MAX		29
+	{
+		.name	= NULL,
+		.level	= -1,
+		.desc	= NULL,
+	}
+};
+
+
+/* Global system security level */
+static int security_level;
+
+
+int sl_capable (struct task_struct *tsk, int cap)
+{
+	/* This is intentionally not C99-initializerized. */
+	static struct {
+		kernel_cap_t cap;
+		int action;
+	} cap2action[] = {
+		{ CAP_KILL,		PRIVACT_KILL_ALIENS },
+		{ CAP_SETUID,		PRIVACT_SETUID },
+		{ CAP_SETGID,		PRIVACT_SETUID },
+		{ CAP_LINUX_IMMUTABLE,	PRIVACT_IMMUTABLE_CHG },
+		{ CAP_NET_BIND_SERVICE,	PRIVACT_BIND_PRIVILEGED },
+		{ CAP_NET_ADMIN,	PRIVACT_NET_IFACES },
+		{ CAP_NET_ADMIN,	PRIVACT_NET_ROUTES },
+		{ CAP_NET_ADMIN,	PRIVACT_NET_IPTABLES },
+		{ CAP_NET_ADMIN,	PRIVACT_NET_PROMISC },
+		{ CAP_NET_RAW,		PRIVACT_NET_RAWSOCK },
+		{ CAP_SYS_MODULE,	PRIVACT_MODULES },
+		{ CAP_SYS_RAWIO,	PRIVACT_RAWIO },
+		{ CAP_SYS_CHROOT,	PRIVACT_CHROOT },
+		{ CAP_SYS_PTRACE,	PRIVACT_PTRACE },
+		{ CAP_SYS_PACCT,	PRIVACT_PACCT },
+		{ CAP_SYS_ADMIN,	PRIVACT_QUOTAS },
+		{ CAP_SYS_ADMIN,	PRIVACT_PRINTK_LOG },
+		{ CAP_SYS_ADMIN,	PRIVACT_HOSTNAME },
+		{ CAP_SYS_ADMIN,	PRIVACT_MOUNT },
+		{ CAP_SYS_ADMIN,	PRIVACT_SWAP },
+		{ CAP_SYS_ADMIN,	PRIVACT_HWCTL },
+		{ CAP_SYS_BOOT,		PRIVACT_REBOOT },
+		{ CAP_SYS_NICE,		PRIVACT_NICE },
+		{ CAP_SYS_RESOURCE,	PRIVACT_RESLIMITS },
+		{ CAP_SYS_TIME,		PRIVACT_TIME_SET },
+		{ CAP_MKNOD,		PRIVACT_MKNOD },
+		{ CAP_LEASE,		PRIVACT_LEASE },
+		{ -1,			PRIVACT_MAX },
+	};
+	int i;
+
+	for (i = 0; cap2action[i].action < PRIVACT_MAX; i++) {
+		/* XXX: We won't escape at the first occurence of cap yet as
+		 * some caps are overloaded. Fix this when this hack will
+		 * disappear. */
+		if (cap2action[i].cap == cap
+		    && security_level >= privileged_actions[cap2action[i].action].level)
+			return -EPERM;
+	}
+	return 0;
+}
+
+int sl_ptrace (struct task_struct *parent, struct task_struct *child)
+{
+	if (security_level < privileged_actions[PRIVACT_PTRACE].level)
+		return 0;
+	else
+		return -EPERM;
+}
+
+static struct security_operations securitylevels_ops = {
+	.ptrace =			sl_ptrace,
+	.capable =			sl_capable,
+};
+
+
+
+/* kobject stuff */
+
+/* levels/ */
+/*   actions/ */
+/*     <name>/ */
+/*       level */
+/*       desc */
+/*   status/ */
+/*     level */
+
+
+static struct subsystem securitylevels_subsys = {
+	.kset =	{
+		.kobj = { .name = "levels" },
+	},
+};
+
+
+
+#define to_action(obj) container_of(obj,struct privileged_action,kobj)
+#define to_action_attr(attr) container_of(attr,struct action_attribute,attr)
+
+struct action_attribute {
+	struct attribute attr;
+	ssize_t (*show) (struct privileged_action *, char *);
+	ssize_t (*store) (struct privileged_action *, const char *, size_t count);
+};
+
+
+static ssize_t action_show (struct kobject *kobj, struct attribute *attr,
+			    char *page)
+{
+	struct privileged_action *action = to_action (kobj);
+	struct action_attribute *action_attr = to_action_attr (attr);
+
+	if (action_attr->show)
+		return action_attr->show (action, page);
+	return 0;
+}
+
+static ssize_t action_store (struct kobject *kobj, struct attribute *attr,
+			     const char *page, size_t count)
+{
+	struct privileged_action *action = to_action (kobj);
+	struct action_attribute *action_attr = to_action_attr (attr);
+
+	if (action_attr->store)
+		return action_attr->store (action, page, count);
+	return 0;
+}
+
+static struct sysfs_ops action_sysfs_ops = {
+        .show	= action_show,
+        .store	= action_store,
+};
+
+
+static ssize_t action_level_show (struct privileged_action *action, char *page)
+{
+	return sprintf (page, "%d\n", action->level);
+}
+
+static ssize_t action_level_store (struct privileged_action *action,
+				   const char *page, size_t count)
+{
+	char *num_end;
+	int new_level = simple_strtol (page, &num_end, 10);
+
+	if (!count || (num_end < page + count && *num_end > ' '))
+		return -EINVAL;
+
+	action->level = new_level;
+	return count;
+}
+
+static struct action_attribute action_attr_level = {
+	.attr	= { .name = "level", .mode = S_IRUSR | S_IWUSR },
+	.show	= action_level_show,
+	.store	= action_level_store,
+};
+
+static ssize_t action_desc_show (struct privileged_action *action, char *page)
+{
+	return snprintf (page, PAGE_SIZE, "%s\n", action->desc);
+}
+
+static struct action_attribute action_attr_desc = {
+	.attr	= { .name = "desc", .mode = S_IRUGO },
+	.show	= action_desc_show,
+	.store	= NULL,
+};
+
+static struct attribute *action_attrs[] = {
+	&action_attr_level.attr,
+	&action_attr_desc.attr,
+	NULL,
+};
+
+static struct kobj_type action_ktype = {
+        .sysfs_ops	= &action_sysfs_ops,
+	.default_attrs	= action_attrs,
+};
+
+static struct kset actions_kset = {
+	.kobj	= { .name = "actions" },
+	.subsys	= &securitylevels_subsys,
+	.ktype	= &action_ktype,
+};
+
+static void register_action_kobjects (void)
+{
+	struct privileged_action *action = privileged_actions;
+
+	while (action->name) {
+		kobject_init (&action->kobj);
+		strncpy (action->kobj.name, action->name, KOBJ_NAME_LEN);
+		action->kobj.name[KOBJ_NAME_LEN - 1] = 0;
+		action->kobj.ktype = &action_ktype;
+		action->kobj.kset = &actions_kset;
+		kobject_add (&action->kobj);
+		action++;
+	}
+}
+
+
+#define to_status_attr(attr) container_of(attr,struct status_attribute,attr)
+
+struct status_attribute {
+	struct attribute attr;
+	ssize_t (*show) (char *);
+	ssize_t (*store) (const char *, size_t count);
+};
+
+
+static ssize_t status_show (struct kobject *kobj, struct attribute *attr,
+			    char *page)
+{
+	struct status_attribute *status_attr = to_status_attr (attr);
+
+	if (status_attr->show)
+		return status_attr->show (page);
+	return 0;
+}
+
+static ssize_t status_store (struct kobject *kobj, struct attribute *attr,
+			     const char *page, size_t count)
+{
+	struct status_attribute *status_attr = to_status_attr (attr);
+
+	if (status_attr->store)
+		return status_attr->store (page, count);
+	return 0;
+}
+
+static struct sysfs_ops status_sysfs_ops = {
+        .show	= status_show,
+        .store	= status_store,
+};
+
+
+static ssize_t status_level_show (char *page)
+{
+	return sprintf (page, "%d\n", security_level);
+}
+
+static ssize_t status_level_store (const char *page, size_t count)
+{
+	char *num_end;
+	int new_level = simple_strtol (page, &num_end, 10);
+
+	if (!count || (num_end < page + count && *num_end > ' '))
+		return -EINVAL;
+
+	security_level = new_level;
+	return count;
+}
+
+static struct status_attribute status_attr_level = {
+	.attr	= { .name = "level", .mode = S_IRUGO | S_IWUSR },
+	.show	= status_level_show,
+	.store	= status_level_store,
+};
+
+static struct attribute *status_attrs[] = {
+	&status_attr_level.attr,
+	NULL,
+};
+
+static struct kobj_type status_ktype = {
+        .sysfs_ops	= &status_sysfs_ops,
+	.default_attrs	= status_attrs,
+};
+
+static struct kobject status_kobject = {
+	.name	= "status",
+	.kset	= &securitylevels_subsys.kset,
+	.ktype	= &status_ktype,
+};
+
+
+
+#if defined(CONFIG_SECURITY_SECURITYLEVELS_MODULE)
+#define MY_NAME THIS_MODULE->name
+#else
+#define MY_NAME "securitylevels"
+#endif
+
+/* flag to keep track of how we were registered */
+static int secondary;
+
+
+static int __init securitylevels_init (void)
+{
+	/* register ourselves with the security framework */
+	if (register_security (&securitylevels_ops)) {
+		printk (KERN_INFO
+			"Failure registering security levels in the kernel\n");
+		/* try registering with primary module */
+		if (mod_reg_security (MY_NAME, &securitylevels_ops)) {
+			printk (KERN_INFO "Failure registering security levels "
+				"in primary security module.\n");
+			return -EINVAL;
+		}
+		secondary = 1;
+	}
+
+	kset_set_kset_s (&securitylevels_subsys, security_subsys);
+	subsystem_register ( &securitylevels_subsys);
+	kset_register (&actions_kset);
+	register_action_kobjects ();
+	kobject_register (&status_kobject);
+
+	printk (KERN_INFO "Security levels LSM initialized\n");
+
+	return 0;
+}
+
+static void __exit securitylevels_exit (void)
+{
+	subsystem_unregister(&securitylevels_subsys);
+
+	/* remove ourselves from the security framework */
+	if (secondary) {
+		if (mod_unreg_security (MY_NAME, &securitylevels_ops))
+			printk (KERN_INFO "Failure unregistering security levels "
+				"from primary module.\n");
+		return;
+	}
+
+	if (unregister_security (&securitylevels_ops)) {
+		printk (KERN_INFO
+			"Failure unregistering security levels from the kernel\n");
+	}
+}
+
+module_init (securitylevels_init);
+module_exit (securitylevels_exit);
+
+MODULE_DESCRIPTION("Linux Security Levels Security Module");
+MODULE_LICENSE("GPL");
