diff -Nur ../linux-2.6.10/Documentation/tpe-lsm.txt linux-2.6.10-tpe/Documentation/tpe-lsm.txt --- ../linux-2.6.10/Documentation/tpe-lsm.txt 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.10-tpe/Documentation/tpe-lsm.txt 2005-01-07 01:36:35.000000000 +0100 @@ -0,0 +1,103 @@ +Trusted Path Execution Linux Security Module +Copyright (C) 2003 IBM Corp. +Copyright (C) 2005 Lorenzo Hernandez Garcia-Hierro +Author: Lorenzo Hernandez Garcia-Hierro (Original: Niki Rahimi) +This file is distributed according to the GNU General Public License. +This module was tested on the Linux kernel 2.6.9 and 2.6.10. + +The purpose of the Trusted Path Execution Linux Security Module is to enable a +check in the Linux kernel to limit the running of executables in trusted paths +so that the potential for malicious code to be run on the system is reduced. +A trusted path is one in which the parent directory of a file is owned by root +and is neither group nor other writeable. +The module relies on a kernel hook which checks to see if the given path is +trusted or not and who is the user (or group member) that caused the event (execution). +This check takes place directly upon an attempt to execute the code by using the +file_mmap and file_mprotect hooks which control how mmap() can do the memory mapping +for an executable, preventing the engine to be bypassed by using tricks like the +ld-linux.so.2 one or any other shared-library-wrapping trick.Also, the file_mprotect +hook prevents mprotect() of being abused for bypass the TPE engine. +Old bprm_check_security behavior is not anymore used and is dropped in the source. + +Limitations: +Right now, the engine does not use the bprm_secureexec hook to +prevent use of sensitive LD_{PRELOAD,DEBUG} variables by untrusted users or groups, but +making these checks relying in kernel space has been demonstrated being less efficient +than simply checking and having a well-configured user space. +Also, script or source interpreters like sh for shell scripts, python, etc could still +execute code as they run normally from trusted paths, the solution relies on having them +in non trusted paths or even implementing an user space control tool, use SELinux or +an advanced method for memory mappings execution. +This features could be developed, but it's not a critical, blocking issue and in my opinion, +they shouldn't rely on the kernel space. + +The module also creates two Trusted Path access control lists which can be accessed +by a sysfs mountpoint, inside the ./tpefs/ subdir (ie: /sys/tpefs), writeable by root, +who can add and delete uids (user id's) and a entire range of users using the (new) native +support for per-gid (group id) basis access control lists. A user on the list is +considered trusted. +Also, it creates a procfs entry called "tpe" (ie: /proc/tpe) which provides +version information, total amount of trusted users, total amount of trusted groups and +other information, depending on the code revision), and those who want extra garbage +from the kernel will like to enabled debugging by passing debug=1 to the module parameters. +By default, root is hard coded onto this list with also a trusted gid +which gets automatically added when selecting the "GID for trusted users" option in kernel +configuration (SECURITY_TPE_TRUSTED_GID). + +Thus if user A attempts to run an executable in path A, the following scenarios +can play out: + +1. Trusted user,trusted path = User is able to run the executable. +2. Trusted user,trusted path,trusted group = User is able to run the executable. +3. Trusted user,untrusted path = User is able to run the executable. +4. Trusted user,untrusted path,trusted group = User is able to run the executable. +5. Trusted user,untrusted path,untrusted group = User is able to run the executable. +6. Untrusted user,trusted path = User is able to run the executable. +7. Untrusted user,trusted path,trusted group = User is able to run the executable. +8. Untrusted user,trusted path,untrusted group = User is able to run the executable. +9. Untrusted user,untrusted path = User is not able to run the executable.# +10. Untrusted user,untrusted path,trusted group = User is able to run the executable. +11. Untrusted user,untrusted path,untrusted group= User is not able to run the executable.# + +In execution terms, untrusted users under untrusted groups and paths wouldn't +be able to change or alter the memory layout nor mmap() executables in memory, +preventing any type of untrusted execution. + +In short, if the path and user are both untrusted (and if the group is untrusted), +execution will be denied sending a message with the uid,gid,euid,egid,pid and full path +to binary that caused the event. + +In order to actually modify the access control list for TPE, the module uses +a sysfs subsystem called tpefs. The tpefs contains four files; add, del, add-group +and del-group. +Performing read and write operations on the aforementioned files creates a user +to system interface that will alter the TPE access control list. See +"Modifying the TPE Access Control Lists" below for the methods of altering the list +using these files. +Note: The original two file approach was utilized rather than a single file in order to +keep the code and administration of the module simple for both the kernel and +the user, and it was when only uid-based access control lists were supported, now +i've followed the same style to provide backwards compatibility. + +Installation: +1. Recompile the kernel to include LSM and TPE as a module (or even get a + kernel-independent packaged TPE module. +2. mount -t sysfs sysfs +3. modprobe tpe debug=1/0 (if you want to use debugging set to 1) +4. See "Modifying the TPE Access Control Lists" below for instructions on how to +add/delete users and groups to/from the list. + +Modifying the TPE Access Control Lists: + +- Adding a user : echo > /tpefs/add +- Deleting a user : echo > /tpefs/del +- Adding a group : echo > /tpefs/add-group +- Deleting a group : echo > /tpefs/del-group +- Show uid list to userspace: cat /tpefs/add +- Show gid list to userspace: cat /tpefs/add-group +- Show version and statistics information: cat /proc/tpe + +Note: Advanced users would be interested in writing a script or something +to modify the access control lists in a fine-grained manner, but this is out +of the goals of the TPE development, anyway, feel free to send your contributions +to lorenzo@gnu.org. diff -Nur ../linux-2.6.10/security/Kconfig linux-2.6.10-tpe/security/Kconfig --- ../linux-2.6.10/security/Kconfig 2005-01-03 19:43:05.000000000 +0100 +++ linux-2.6.10-tpe/security/Kconfig 2005-01-07 00:48:29.000000000 +0100 @@ -84,6 +84,36 @@ If you are unsure how to answer this question, answer N. +config SECURITY_TPE + tristate "Trusted Path Execution (EXPERIMENTAL)" + depends on SECURITY && EXPERIMENTAL + help + The TPE module enforces a check on the running of executables. + It will not allow execution if the program is located in a + "trusted path" and the current user is "untrusted". A trusted + path is one which is root owned and neither group nor other + writeable. A user is considered trusted if their uid is added + to a trusted list in memory, also, user groups can be trusted + in kernel compile-time and runtime by adding them to the gid-basis + access control list.Root is trusted, by default.A proc interface + /proc/tpe is created when module loads, it shows version and ACL + information to use third-party checkers or scripts. + + (c) 2005, Lorenzo Hernandez Garcia-Hierro . + + If you are unsure how to answer this question, answer N. + +config SECURITY_TPE_TRUSTED_GID + int "GID for trusted users" + depends on SECURITY_TPE + default 1337 + help + Here you can choose the GID to disable trusted path protection for. + Users relying under the group with id specified here will have TPE + disabled by default, so, they could represent a security risk for + the rest of users.Please, be sure that you can really trust in the + members of this group. + source security/selinux/Kconfig endmenu diff -Nur ../linux-2.6.10/security/Makefile linux-2.6.10-tpe/security/Makefile --- ../linux-2.6.10/security/Makefile 2005-01-03 19:43:05.000000000 +0100 +++ linux-2.6.10-tpe/security/Makefile 2005-01-04 02:10:45.000000000 +0100 @@ -17,3 +17,4 @@ obj-$(CONFIG_SECURITY_CAPABILITIES) += commoncap.o capability.o obj-$(CONFIG_SECURITY_ROOTPLUG) += commoncap.o root_plug.o obj-$(CONFIG_SECURITY_SECLVL) += seclvl.o +obj-$(CONFIG_SECURITY_TPE) += tpe.o diff -Nur ../linux-2.6.10/security/tpe.c linux-2.6.10-tpe/security/tpe.c --- ../linux-2.6.10/security/tpe.c 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.10-tpe/security/tpe.c 2005-01-07 04:03:54.030176136 +0100 @@ -0,0 +1,735 @@ +/* + * Trusted Path Execution Security Module + * + * This module provides a Trusted Path Execution (TPE) engine to the + * Linux kernel. TPE originated as a kernel patch to OpenBSD 2.4 by + * route|daemon9 and Mike Schifman (see Phrack 54). IBM originally modified + * the original project to fit within the constraints of the LSM framework + * and so it should be noted that this is not the same project. Also, + * the module makes use of the sysfs filesystem. + * This module is a rewrite of the IBM's original code in order to add + * debugging, better information outputting, support for per-User-Groups + * access control lists at runtime and kernel-configuration level, a procfs + * interface for general information gathering from the TPE subsystem and the + * protection engine which in the original IBM's code was relying in bprm_set_security + * is now based in executables memory mappings, which makes it secure against + * shared libraries that can wrap untrusted executables and bypass the TPE + * subsystem, also adding a secondary protection against possible attempts to + * bypass the mmap()'ing checks by using PROT_READ instead of PROT_EXEC in + * mprotect() calls. + * + * Copyright (C) 1998 route|daemon9 and Mike D. Schiffman + * Copyright (C) 2001-2002 Greg Kroah-Hartman + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2005 Lorenzo Hernandez Garcia-Hierro + * All rights reversed. + * + * Stephen Smalley contributed the mmap/mprotect + * protections idea and commented its limitations, benefits, etc. + * + * Chris Wright helped me understanding the fullpath + * uncovering hacks and the vma structs used in the file_mprotect hook. + * + * Brad Spengler and Seth Arnold + * reported memory use possible problems, missing free_page() calls and + * other related bugs. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "tpe.h" + +static int secondary; +static spinlock_t tpe_acl_lock = SPIN_LOCK_UNLOCKED; +static spinlock_t tpe_group_acl_lock = SPIN_LOCK_UNLOCKED; + +/* debugging */ +static int debug = 0; + +/* get debug=1/0 param */ +module_param(debug, bool, 0600); +MODULE_PARM_DESC(debug, "Debug enabled or not"); + +#if defined(CONFIG_SECURITY_TPE_MODULE) +#define MY_NAME THIS_MODULE->name +#else +#define MY_NAME "TPE" +#define LEGACY_NAME "tpe" +#endif + +/* Module information */ +#define DESCRIPTION "Trusted Path Execution LSM Implementation" +#define AUTHOR "Lorenzo Hernandez Garcia-Hierro " +#define LICENSE "GPL" + +/* TPE /proc entry */ +struct proc_dir_entry *TPE_pentry; + +/* Outputting and debugging functions */ +/* Debug should be explicitly enabled (debug=1) to give output back from dbg() */ +#define dbg(fmt, arg...) \ + do { \ + if (1 && debug) { \ + printk(KERN_DEBUG "%s: " fmt , MY_NAME, ## arg); \ + } \ + } while (0) + +/* KERN_INFO messages use kinfo() rather than printk() for better outputting layout */ +#define kinfo(fmt, arg...) \ + do { \ + if (1) { \ + printk(KERN_INFO "%s: " fmt , MY_NAME, ## arg); \ + } \ + } while (0) + +/* Insertion sort the list. */ +static void tpe_sort(int low, int high) +{ /* (list low element, list high element) */ + int i, j, n; + /* Standard insertion sort. */ + for (i = low + 1; i <= high; i++) { + if (tpe_acl[i] < tpe_acl[low]) { + tpe_acl[low] ^= tpe_acl[i]; + tpe_acl[i] ^= tpe_acl[low]; + tpe_acl[low] ^= tpe_acl[i]; + } + } + /* tpe_acl */ + for (i = low + 2; i <= high; i++) { + j = i; + n = tpe_acl[i]; + while (n < tpe_acl[j - 1]) { + tpe_acl[j] = tpe_acl[j - 1]; + j--; + } + tpe_acl[j] = n; + } +} +static void tpe_group_sort(int low, int high) +{ /* (list low element, list high element) */ + int i, j, n; + /* Standard insertion sort for tpe_group_acl. */ + for (i = low + 1; i <= high; i++) { + if (tpe_group_acl[i] < tpe_group_acl[low]) { + tpe_group_acl[low] ^= tpe_group_acl[i]; + tpe_group_acl[i] ^= tpe_group_acl[low]; + tpe_group_acl[low] ^= tpe_group_acl[i]; + } + } + /* tpe_group_acl */ + for (i = low + 2; i <= high; i++) { + j = i; + n = tpe_group_acl[i]; + while (n < tpe_group_acl[j - 1]) { + tpe_group_acl[j] = tpe_group_acl[j - 1]; + j--; + } + tpe_group_acl[j] = n; + } +} + +/* Attempt to add an UID candidate to the list. */ +static int tpe_add(uid_t add_candidate) +{ + int retval = -EINVAL; + + /* Full list. */ + if (tpe_acl_candidates == (TPE_ACL_SIZE - 2)) { + kinfo("Unable to add user %d. List is full.\n", add_candidate); + goto out; + } + + if (add_candidate == 0) { + kinfo("Invalid user id. Cannot add.\n"); + goto out; + } + + /* Don't add duplicates */ + if ((tpe_search(add_candidate)) == NACK) { + /* Add to the end of the list, then sort. */ + tpe_acl_candidates++; + tpe_acl[tpe_acl_candidates] = add_candidate; + tpe_acl[tpe_acl_candidates + 1] = '\0'; /* terminate array */ + tpe_sort(0, tpe_acl_candidates); + kinfo("UID %d added as trusted to access control list\n", + add_candidate); + } else { + kinfo("Duplicate UID %d not added\n", add_candidate); + goto out; + } + retval = 0; + out: + return retval; +} + +/* Attempt to add an GID candidate to the list. */ +static int tpe_group_add(gid_t add_candidate) +{ + int retval = -EINVAL; + + /* Full list. */ + if (tpe_acl_group_candidates == (TPE_ACL_SIZE - 2)) { + kinfo("Unable to add user %d. List is full.\n", add_candidate); + goto out; + } + + if (add_candidate == 0) { + kinfo("Invalid userid. Cannot add.\n"); + goto out; + } + + /* Don't add duplicates */ + if ((tpe_search_group(add_candidate)) == NACK) { + /* Add to the end of the list, then sort. */ + tpe_acl_group_candidates++; + tpe_group_acl[tpe_acl_group_candidates] = add_candidate; + tpe_group_acl[tpe_acl_group_candidates + 1] = '\0'; /* terminate array */ + tpe_group_sort(0, tpe_acl_group_candidates); + kinfo("GID %d added as trusted to access control list\n", + add_candidate); + } else { + kinfo("Duplicate GID %d not added\n", add_candidate); + goto out; + } + retval = 0; + out: + return retval; +} + +/* Attempt to remove a candidate from the list. Only fails if the entry is */ +/* not there. */ +static int tpe_remove(uid_t rem_candidate) +{ + int n; + int retval = -EINVAL; + if (tpe_acl_candidates == 0) { + /* Empty list */ + goto out; + } + if (rem_candidate == 0) { + kinfo("Invalid userid. Cannot remove.\n"); + goto out; + } + + n = tpe_search(rem_candidate); + if (n != NACK) { + /* Remove candidate (mark slot as unused), resort the list. */ + tpe_acl[n] = TPE_INITIALIZER; + tpe_acl_candidates--; + tpe_sort(0, tpe_acl_candidates); + kinfo("UID %d removed from trust list\n", rem_candidate); + retval = 0; + goto out; + } + /* Not found. */ + kinfo("UID %d not found in trust list\n", rem_candidate); + out: + return retval; +} + +/* GID removing */ +static int tpe_group_remove(gid_t rem_candidate) +{ + int n; + int retval = -EINVAL; + if (tpe_acl_group_candidates == 0) { + /* Empty groups list */ + goto out; + } + if (rem_candidate == 0) { + kinfo("Invalid GID. Cannot remove.\n"); + goto out; + } + + n = tpe_search_group(rem_candidate); + if (n != NACK) { + /* Remove candidate (mark slot as unused), resort the list. */ + tpe_group_acl[n] = TPE_INITIALIZER; + tpe_acl_group_candidates--; + tpe_group_sort(0, tpe_acl_group_candidates); + kinfo("GID %d removed from trust list\n", rem_candidate); + retval = 0; + goto out; + } + /* Not found. */ + kinfo("GID %d not found in trust list\n", rem_candidate); + out: + return retval; +} + +/* Beginning of a sysfs subsystem for tpe */ +static struct subsystem tpefs_subsys; + +struct tpe_list { + char *name; + + struct list_head slot_list; + struct kobject kobj; +}; + +struct tpefs_attribute { + struct attribute attr; + ssize_t(*show) (struct tpe_list *, char *); + ssize_t(*store) (struct tpe_list *, const char *, size_t); +}; + +static ssize_t tpefs_attr_show(struct kobject *kobj, struct attribute *attr, + char *buf) +{ + struct tpe_list *list = container_of(kobj, struct tpe_list, kobj); + struct tpefs_attribute *attribute = container_of(attr, struct + tpefs_attribute, attr); + return attribute->show ? attribute->show(list, buf) : 0; +} + +static ssize_t tpefs_attr_store(struct kobject *kobj, struct attribute *attr, + const char *buf, size_t len) +{ + struct tpe_list *list = container_of(kobj, struct tpe_list, kobj); + struct tpefs_attribute *attribute = container_of(attr, struct + tpefs_attribute, attr); + return attribute->store ? attribute->store(list, buf, len) : 0; +} + +static struct sysfs_ops tpefs_sysfs_ops = { + .show = tpefs_attr_show, + .store = tpefs_attr_store, +}; + +static struct kobj_type tpefs_ktype = { + .sysfs_ops = &tpefs_sysfs_ops +}; + +static decl_subsys(tpefs, &tpefs_ktype, NULL); + +static ssize_t trustedlistadd_read_file(struct tpe_list *list, char *buf) +{ + int i; + int retval = 0; + char *user = NULL; + char *buffer = kmalloc(400, GFP_KERNEL); + + user = (char *)__get_free_page(GFP_KERNEL); + if (!user) + return -ENOMEM; + + if (!buffer) + return -ENOMEM; + + if (tpe_acl == NULL) { + kinfo("Empty Access Control List\n"); + free_page((unsigned long)user); + return -ENODATA; + } + + buffer[0] = '\0'; + kinfo("%d trusted user(s): \n", tpe_acl_candidates); + spin_lock(&tpe_acl_lock); + for (i = 0; i < tpe_acl_candidates; i++) { + printk(KERN_INFO "%d ", tpe_acl[i]); + retval = sprintf(user, "%d\n", tpe_acl[i]); + strcat(buffer, user); + } + /* leave printk() calls alone, not kinfo() */ + printk(KERN_INFO "\n"); + spin_unlock(&tpe_acl_lock); + + retval = snprintf(buf, 400, "%s\n", buffer); + + kfree(buffer); /* we kfree() the kmalloc()'ed buffer */ + free_page((unsigned long)user); + return retval; +} + +static ssize_t trustedlistadd_write_file(struct tpe_list *list, const char *buf, + size_t count) +{ + int retval; + unsigned long add_candidate; + + add_candidate = simple_strtoul(buf, NULL, 10); + + dbg("value of add_candidate is %d.\n", (int)add_candidate); + spin_lock(&tpe_acl_lock); + retval = tpe_add(add_candidate); + spin_unlock(&tpe_acl_lock); + if (retval) { + return retval; + } + return count; + +} + +/* per-GID basis ACL support */ + +static ssize_t trustedlistadd_group_write_file(struct tpe_list *list, + const char *buf, size_t count) +{ + int retval; + unsigned long add_group_candidate; + + add_group_candidate = simple_strtoul(buf, NULL, 10); + + dbg("value of add_group_candidate is %d.\n", (int)add_group_candidate); + spin_lock(&tpe_group_acl_lock); + retval = tpe_group_add(add_group_candidate); /* tpe_group_add() ninja monkey */ + spin_unlock(&tpe_group_acl_lock); + if (retval) { + return retval; + } + return count; + +} +static ssize_t trustedlistadd_group_read_file(struct tpe_list *list, char *buf) +{ + int i; + int retval = 0; + char *group = NULL; + char *buffer = kmalloc(400, GFP_KERNEL); + + group = (char *)__get_free_page(GFP_KERNEL); + if (!group) + return -ENOMEM; + + if (!buffer) + return -ENOMEM; + + if (tpe_group_acl == NULL) { + kinfo("Empty user groups Access Control List\n"); + free_page((unsigned long)group); + return -ENODATA; + } + + buffer[0] = '\0'; + kinfo("%d trusted group(s): \n", tpe_acl_group_candidates); + spin_lock(&tpe_group_acl_lock); + for (i = 0; i < tpe_acl_group_candidates; i++) { + printk(KERN_INFO "%d ", tpe_group_acl[i]); + retval = sprintf(group, "%d\n", tpe_group_acl[i]); + strcat(buffer, group); + } + /* leave printk() calls alone, not kinfo() */ + printk(KERN_INFO "\n"); + spin_unlock(&tpe_group_acl_lock); + + retval = snprintf(buf, 400, "%s\n", buffer); + + kfree(buffer); /* we kfree() the kmalloc()'ed buffer */ + free_page((unsigned long)group); + return retval; +} + +/* ends sysfs tpefs group acl management */ + +struct tpefs_attribute tpefs_listadd_attr = { + .attr = {.name = "add",.mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = trustedlistadd_read_file, + .store = trustedlistadd_write_file +}; + +/* listadd_group per-GID basis ACL */ +struct tpefs_attribute tpefs_listadd_group_attr = { + .attr = {.name = "add-group",.mode = S_IFREG | S_IRUGO | S_IWUSR}, + .show = trustedlistadd_group_read_file, + .store = trustedlistadd_group_write_file +}; + +/* trusted UIDs deletion */ +static ssize_t trustedlistdel_write_file(struct tpe_list *list, const char *buf, + size_t count) +{ + int retval; + unsigned long rem_candidate; + + rem_candidate = simple_strtoul(buf, NULL, 10); + + dbg("value of rem_candidate is %d.\n", (int)rem_candidate); + spin_lock(&tpe_acl_lock); + retval = tpe_remove(rem_candidate); + spin_unlock(&tpe_acl_lock); + if (retval) { + return retval; + } + return count; + +} + +/* trusted GIDs deletion */ +static ssize_t trustedlistdel_group_write_file(struct tpe_list *list, + const char *buf, size_t count) +{ + int retval; + unsigned long rem_group_candidate; + + rem_group_candidate = simple_strtoul(buf, NULL, 10); + + dbg("value of rem_candidate is %d.\n", (int)rem_group_candidate); + spin_lock(&tpe_group_acl_lock); + retval = tpe_group_remove(rem_group_candidate); + spin_unlock(&tpe_group_acl_lock); + if (retval) { + return retval; + } + return count; + +} + +struct tpefs_attribute tpefs_listdel_attr = { + .attr = {.name = "del",.mode = S_IFREG | S_IRUGO | S_IWUSR}, + .store = trustedlistdel_write_file +}; + +/* sysfs/tpefs/del-group */ +struct tpefs_attribute tpefs_listdel_group_attr = { + .attr = {.name = "del-group",.mode = S_IFREG | S_IRUGO | S_IWUSR}, + .store = trustedlistdel_group_write_file +}; + +/* TPE /proc entry managing */ +ssize_t +procfile_read(char *buffer, + char **buffer_location, + off_t offset, int buffer_length, int *eof, void *data) +{ + int len = 0; /* The number of bytes actually used */ + char *vermagic = VERSION; + if (offset > 0) { + dbg("Offset %d : /proc/tpe : procfile_read, \ + wrote %d bytes\n", (int)(offset), len); + *eof = 1; + return len; + } + /* We write the data to the procfs entry */ + len = sprintf(buffer, + "Version: %s\nTrusted users: %d\nTrusted groups: %d\n", + vermagic, tpe_acl_candidates, tpe_acl_group_candidates); + /* Voila! */ + dbg("Leaving /proc/tpe : procfile_read, wrote %d Bytes\n", len); + return len; +} + +/* Module code */ + +/* prevent SIGKILL in execve() */ + +/* + As suggested by Stephen D. Smalley from the NSA, the following checks will prevent + for example untrusted executables being abused within the execution context of ld-linux + shared library, and also they are a replacement of the original,limited, + tpe_bprm_set_security hook. + */ +/* + * file_mmap hook -> tpe_file_mmap + */ +static int tpe_file_mmap(struct file *file, unsigned long prot, + unsigned long flags) +{ + if ((file) && (file->f_dentry) && (file->f_dentry->d_name.name) + && (current->uid) && (current->gid) && (prot & PROT_EXEC)) { + /* Get the executable execution uid and mode */ + uid_t mpuid = file->f_dentry->d_parent->d_inode->i_uid; + mode_t mpmode = file->f_dentry->d_parent->d_inode->i_mode; + + /* Check the executable properties */ + /* attempt to make a file mapping executable, run TPE trusting test */ + if ((!TRUSTED_PATH(mpmode, mpuid)) + && (!TRUSTED_USER(current->uid)) + && (!TRUSTED_GROUP(current->gid))) { + + /* get a free page and allocate executable real path */ + char *fptmp = + (char *)__get_free_page(GFP_KERNEL), *exfpath; + + /* "Are we out of memory? I can't remember" -ENOMEM in Halloween Panic meeting. */ + if (!(fptmp)) + return -ENOMEM; + + exfpath = + d_path(file->f_dentry, file->f_vfsmnt, fptmp, + PAGE_SIZE); + + /* You can still believe in Ninja Code Monkeys... */ + printk(KERN_ALERT "TPE: Untrusted execution: %s " + "uid/euid=%d/%d gid/egid=%d/%d suid/sgid=%d/%d pid=%d" + "\n", exfpath, current->uid, current->euid, + current->gid, current->egid, current->suid, + current->sgid, current->pid); + + free_page((unsigned long)fptmp); /* free the fptmp page */ + + return -EACCES; /* due to elf_map() this will cause a SIGKILL */ + } + } + return 0; +} + +/* + * file_mprotect hook -> tpe_file_mprotect + */ +static int tpe_file_mprotect(struct vm_area_struct *vma, unsigned long prot) +{ + if ((vma->vm_file) && (prot & PROT_EXEC)) { + /* Get the executable execution uid and mode */ + uid_t mpuid = vma->vm_file->f_dentry->d_parent->d_inode->i_uid; + mode_t mpmode = + vma->vm_file->f_dentry->d_parent->d_inode->i_mode; + + if ((!TRUSTED_PATH(mpmode, mpuid)) + && (!TRUSTED_USER(current->uid)) + && (!TRUSTED_GROUP(current->gid))) { + /* get a free page and allocate executable real path */ + char *fptmp = + (char *)__get_free_page(GFP_KERNEL), *exfpath; + + /* "Are we out of memory? I can't remember" -ENOMEM in Halloween Panic meeting. */ + if (!(fptmp)) + return -ENOMEM; + + exfpath = + d_path(vma->vm_file->f_dentry, + vma->vm_file->f_vfsmnt, fptmp, PAGE_SIZE); + + /* ...but they don't trust in mprotect() */ + printk(KERN_ALERT + "TPE: Untrusted execution by mprotect(): %s " + "uid/euid=%d/%d gid/egid=%d/%d suid/sgid=%d/%d pid=%d" + "\n", exfpath, current->uid, current->euid, + current->gid, current->egid, current->suid, + current->sgid, current->pid); + + free_page((unsigned long)fptmp); /* free the fptmp page */ + + return -EACCES; + } + } + return 0; +} + +/* + TPE security hooks +*/ +struct security_operations tpe_security_ops = { + file_mmap:tpe_file_mmap, + file_mprotect:tpe_file_mprotect, +}; + +static int __init tpe_module_init(void) +{ + int retval; + int cfoo = 0; + /* register ourselves with the security framework */ + if (register_security(&tpe_security_ops)) { + printk(KERN_INFO + "Failure registering tpe module with the kernel\n"); + /* try registering with primary module */ + if (mod_reg_security(LEGACY_NAME, &tpe_security_ops)) { + printk(KERN_INFO "Failure registering tpe module " + "with primary security module.\n"); + return -EINVAL; + } + secondary = 1; + } + + /* register tpe subsystem */ + kinfo("Registering TPE subsystem.\n"); + retval = subsystem_register(&tpefs_subsys); + if (retval) { + kinfo("subsystem_register failed with %d\n", retval); + return retval; + } + kinfo("Registering tpefs support for sysfs.\n"); + sysfs_create_file(&tpefs_subsys.kset.kobj, &tpefs_listadd_attr.attr); + sysfs_create_file(&tpefs_subsys.kset.kobj, &tpefs_listdel_attr.attr); + /* create the group/gid acl sysfs tpefs files */ + sysfs_create_file(&tpefs_subsys.kset.kobj, + &tpefs_listadd_group_attr.attr); + sysfs_create_file(&tpefs_subsys.kset.kobj, + &tpefs_listdel_group_attr.attr); + + kinfo("Registering with LSM subsystem.\n"); + tpe_init(); + /* TPE /proc/ "bilbo" entry begins :) */ + TPE_pentry = create_proc_entry(TPE_PROC_ENTRY_NAME, 0644, NULL); + TPE_pentry->read_proc = procfile_read; + TPE_pentry->owner = THIS_MODULE; + TPE_pentry->mode = S_IFREG | S_IRUGO; + TPE_pentry->uid = 0; + TPE_pentry->gid = 0; + TPE_pentry->size = 20; + if (TPE_pentry == NULL) { + cfoo = -ENOMEM; + /**/ remove_proc_entry(TPE_PROC_ENTRY_NAME, &proc_root); + kinfo("Error: Could not initialize /proc/tpe\n"); + } else { + kinfo("/proc entry initialized.\n"); + } + kinfo("Trusted Path Execution engine initialized.\n"); + return cfoo; +} + +static void __exit tpe_exit(void) +{ + remove_proc_entry(TPE_PROC_ENTRY_NAME, &proc_root); /* remove TPE /proc entry */ + sysfs_remove_file(&tpefs_subsys.kset.kobj, &tpefs_listadd_attr.attr); + sysfs_remove_file(&tpefs_subsys.kset.kobj, &tpefs_listdel_attr.attr); + /* remove sysfs/tpefs/add-group and del-group */ + sysfs_remove_file(&tpefs_subsys.kset.kobj, + &tpefs_listadd_group_attr.attr); + sysfs_remove_file(&tpefs_subsys.kset.kobj, + &tpefs_listdel_group_attr.attr); + subsystem_unregister(&tpefs_subsys); + + /* remove ourselves from the security framework */ + if (secondary) { + if (mod_unreg_security(LEGACY_NAME, &tpe_security_ops)) + printk(KERN_INFO "Failure unregistering tpe module " + "with primary module.\n"); + return; + } + + if (unregister_security(&tpe_security_ops)) { + printk(KERN_INFO + "Failure unregistering tpe module with the kernel\n"); + } + kinfo("Trusted Path Execution subsystem unloaded.\n"); + +} + +module_init(tpe_module_init); +module_exit(tpe_exit); + +/* Modinfo perls */ +MODULE_DESCRIPTION(DESCRIPTION); +MODULE_AUTHOR(AUTHOR); +MODULE_LICENSE(LICENSE); diff -Nur ../linux-2.6.10/security/tpe.h linux-2.6.10-tpe/security/tpe.h --- ../linux-2.6.10/security/tpe.h 1970-01-01 01:00:00.000000000 +0100 +++ linux-2.6.10-tpe/security/tpe.h 2005-01-07 04:03:57.126705392 +0100 @@ -0,0 +1,158 @@ +/* + * Trusted Path Execution Security Module + * + * This module provides a Trusted Path Execution (TPE) engine to the + * Linux kernel. TPE originated as a kernel patch to OpenBSD 2.4 by + * route|daemon9 and Mike Schifman (see Phrack 54). IBM originally modified + * the original project to fit within the constraints of the LSM framework + * and so it should be noted that this is not the same project. Also, + * the module makes use of the sysfs filesystem. + * This module is a rewrite of the IBM's original code in order to add + * debugging, better information outputting, support for per-User-Groups + * access control lists at runtime and kernel-configuration level, a procfs + * interface for general information gathering from the TPE subsystem and the + * protection engine which in the original IBM's code was relying in bprm_set_security + * is now based in executables memory mappings, which makes it secure against + * shared libraries that can wrap untrusted executables and bypass the TPE + * subsystem, also adding a secondary protection against possible attempts to + * bypass the mmap()'ing checks by using PROT_READ instead of PROT_EXEC in + * mprotect() calls. + * + * Copyright (C) 2003 IBM Corp. + * Copyright (C) 2005 Lorenzo Hernandez Garcia-Hierro + * All rights reversed. + * + * 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. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef __TPE_H +#define __TPE_H + +#include +#include + +/* Vermagic and /proc entry name */ + +#define VERSION "0.2-20050107" +#define LSM_VERSION NULL /* unused */ +#define TPE_PROC_ENTRY_NAME "tpe" /* /proc entry name */ + +/* Per-user group basis protection stuff */ + +static int tpe_trusted_gid = 0; +#ifdef CONFIG_SECURITY_TPE_TRUSTED_GID +tpe_trusted_gid = CONFIG_SECURITY_TPE_TRUSTED_GID; +#endif +/* + * syscall stuff + */ +#define TPE_ACL_SIZE 82 /* Maximum number of users in the list + * plus two. This was an original + * component of TPE. This will be + * fixed later on. + */ + +#define TPE_INITIALIZER -1 /* A UID that isn't used */ + +#define ACK 1 /* positive acknowledgement */ +#define NACK -1 /* negative acknowledgement */ +#define DUP 3 /* duplicate id return for tpe_add */ + +/* + * Verify the path. + */ +#define TRUSTED_PATH(mode, uid) \ +(!(mode & (S_IWGRP | S_IWOTH)) && (uid == 0)) + +static uid_t tpe_acl[TPE_ACL_SIZE]; /* trusted users list */ +static gid_t tpe_group_acl[TPE_ACL_SIZE]; /* trusted user groups list */ +static int tpe_acl_candidates; /* number of users on the list */ +static int tpe_acl_group_candidates; /* number of user groups on the list */ + +/* + * Verify the user. This macro is passed the user's ID from the + * tpe_bprm_set_security hook. + */ + +#define TRUSTED_USER(UID) (tpe_verify(UID) == ACK) + +/* + * Verify the user group. This macro is passed the user's group ID from the + * tpe_bprm_set_security hook. + */ + +#define TRUSTED_GROUP(GID) (tpe_group_verify(GID) == ACK) + +/* Initialize the array with default values (TPE_INITIALIZER). */ + +/* Debugging and outputting functions */ + +static inline void tpe_init(void) +{ + memset(tpe_acl, TPE_INITIALIZER, sizeof(uid_t) * TPE_ACL_SIZE); + printk(KERN_INFO "TPE: Access Control List created.\n"); + tpe_acl_candidates = 1; + if (!(tpe_trusted_gid == 0)) { + tpe_acl_group_candidates = 2; + } else { + tpe_acl_group_candidates = 1; + } + tpe_acl[0] = 0; + if (!(tpe_trusted_gid == 0)) { + tpe_group_acl[0] = 0; + + tpe_group_acl[1] = tpe_trusted_gid; + } else { + tpe_group_acl[0] = 0; + } +} + +/* Locate a uid in the list */ +static inline int tpe_search(uid_t candidate) +{ + int i; + for (i = 0; i < tpe_acl_candidates; i++) { + if (candidate == tpe_acl[i]) { + return i; + } + } + return NACK; +} + +/* Verify a candidate user. */ +static inline int tpe_verify(uid_t candidate) +{ + if ((tpe_search(candidate)) != NACK) { + return (ACK); + } + return (NACK); +} + +/* Verify a candidate user group. */ +static inline int tpe_search_group(gid_t group_candidate) +{ + int i; + for (i = 0; i < tpe_acl_group_candidates; i++) { + if (group_candidate == tpe_group_acl[i]) { + return i; + } + } + return NACK; +} +static inline int tpe_group_verify(gid_t group_candidate) +{ + if ((tpe_search_group(group_candidate)) != NACK) { + return (ACK); + } + return (NACK); +} + +#endif /* __TPE_H */