aboutsummaryrefslogtreecommitdiff
path: root/security
diff options
context:
space:
mode:
Diffstat (limited to 'security')
-rw-r--r--security/Kconfig5
-rw-r--r--security/Makefile4
-rw-r--r--security/inode.c7
-rw-r--r--security/integrity/ima/Kconfig55
-rw-r--r--security/integrity/ima/Makefile9
-rw-r--r--security/integrity/ima/ima.h166
-rw-r--r--security/integrity/ima/ima_api.c190
-rw-r--r--security/integrity/ima/ima_audit.c78
-rw-r--r--security/integrity/ima/ima_crypto.c140
-rw-r--r--security/integrity/ima/ima_fs.c376
-rw-r--r--security/integrity/ima/ima_iint.c202
-rw-r--r--security/integrity/ima/ima_init.c96
-rw-r--r--security/integrity/ima/ima_main.c327
-rw-r--r--security/integrity/ima/ima_policy.c413
-rw-r--r--security/integrity/ima/ima_queue.c140
-rw-r--r--security/selinux/hooks.c241
-rw-r--r--security/selinux/include/objsec.h2
-rw-r--r--security/selinux/include/security.h8
18 files changed, 2277 insertions, 182 deletions
diff --git a/security/Kconfig b/security/Kconfig
index 9438535d7fd..bf129f87de7 100644
--- a/security/Kconfig
+++ b/security/Kconfig
@@ -55,7 +55,8 @@ config SECURITYFS
bool "Enable the securityfs filesystem"
help
This will build the securityfs filesystem. It is currently used by
- the TPM bios character driver. It is not used by SELinux or SMACK.
+ the TPM bios character driver and IMA, an integrity provider. It is
+ not used by SELinux or SMACK.
If you are unsure how to answer this question, answer N.
@@ -135,5 +136,7 @@ config SECURITY_DEFAULT_MMAP_MIN_ADDR
source security/selinux/Kconfig
source security/smack/Kconfig
+source security/integrity/ima/Kconfig
+
endmenu
diff --git a/security/Makefile b/security/Makefile
index c05c127fff9..595536cbffb 100644
--- a/security/Makefile
+++ b/security/Makefile
@@ -17,3 +17,7 @@ obj-$(CONFIG_SECURITY_SELINUX) += selinux/built-in.o
obj-$(CONFIG_SECURITY_SMACK) += smack/built-in.o
obj-$(CONFIG_SECURITY_ROOTPLUG) += root_plug.o
obj-$(CONFIG_CGROUP_DEVICE) += device_cgroup.o
+
+# Object integrity file lists
+subdir-$(CONFIG_IMA) += integrity/ima
+obj-$(CONFIG_IMA) += integrity/ima/built-in.o
diff --git a/security/inode.c b/security/inode.c
index 007ef252dde..f3b91bfbe4c 100644
--- a/security/inode.c
+++ b/security/inode.c
@@ -202,12 +202,11 @@ static int create_by_name(const char *name, mode_t mode,
* This function returns a pointer to a dentry if it succeeds. This
* pointer must be passed to the securityfs_remove() function when the file is
* to be removed (no automatic cleanup happens if your module is unloaded,
- * you are responsible here). If an error occurs, %NULL is returned.
+ * you are responsible here). If an error occurs, the function will return
+ * the erorr value (via ERR_PTR).
*
* If securityfs is not enabled in the kernel, the value %-ENODEV is
- * returned. It is not wise to check for this value, but rather, check for
- * %NULL or !%NULL instead as to eliminate the need for #ifdef in the calling
- * code.
+ * returned.
*/
struct dentry *securityfs_create_file(const char *name, mode_t mode,
struct dentry *parent, void *data,
diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig
new file mode 100644
index 00000000000..3d2b6ee778a
--- /dev/null
+++ b/security/integrity/ima/Kconfig
@@ -0,0 +1,55 @@
+# IBM Integrity Measurement Architecture
+#
+config IMA
+ bool "Integrity Measurement Architecture(IMA)"
+ depends on ACPI
+ select SECURITYFS
+ select CRYPTO
+ select CRYPTO_HMAC
+ select CRYPTO_MD5
+ select CRYPTO_SHA1
+ select TCG_TPM
+ select TCG_TIS
+ help
+ The Trusted Computing Group(TCG) runtime Integrity
+ Measurement Architecture(IMA) maintains a list of hash
+ values of executables and other sensitive system files,
+ as they are read or executed. If an attacker manages
+ to change the contents of an important system file
+ being measured, we can tell.
+
+ If your system has a TPM chip, then IMA also maintains
+ an aggregate integrity value over this list inside the
+ TPM hardware, so that the TPM can prove to a third party
+ whether or not critical system files have been modified.
+ Read <http://www.usenix.org/events/sec04/tech/sailer.html>
+ to learn more about IMA.
+ If unsure, say N.
+
+config IMA_MEASURE_PCR_IDX
+ int
+ depends on IMA
+ range 8 14
+ default 10
+ help
+ IMA_MEASURE_PCR_IDX determines the TPM PCR register index
+ that IMA uses to maintain the integrity aggregate of the
+ measurement list. If unsure, use the default 10.
+
+config IMA_AUDIT
+ bool
+ depends on IMA
+ default y
+ help
+ This option adds a kernel parameter 'ima_audit', which
+ allows informational auditing messages to be enabled
+ at boot. If this option is selected, informational integrity
+ auditing messages can be enabled with 'ima_audit=1' on
+ the kernel command line.
+
+config IMA_LSM_RULES
+ bool
+ depends on IMA && (SECURITY_SELINUX || SECURITY_SMACK)
+ default y
+ help
+ Disabling this option will disregard LSM based policy rules
diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile
new file mode 100644
index 00000000000..787c4cb916c
--- /dev/null
+++ b/security/integrity/ima/Makefile
@@ -0,0 +1,9 @@
+#
+# Makefile for building Trusted Computing Group's(TCG) runtime Integrity
+# Measurement Architecture(IMA).
+#
+
+obj-$(CONFIG_IMA) += ima.o
+
+ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
+ ima_policy.o ima_iint.o ima_audit.o
diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h
new file mode 100644
index 00000000000..e3c16a21a38
--- /dev/null
+++ b/security/integrity/ima/ima.h
@@ -0,0 +1,166 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: ima.h
+ * internal Integrity Measurement Architecture (IMA) definitions
+ */
+
+#ifndef __LINUX_IMA_H
+#define __LINUX_IMA_H
+
+#include <linux/types.h>
+#include <linux/crypto.h>
+#include <linux/security.h>
+#include <linux/hash.h>
+#include <linux/tpm.h>
+#include <linux/audit.h>
+
+enum ima_show_type { IMA_SHOW_BINARY, IMA_SHOW_ASCII };
+enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
+
+/* digest size for IMA, fits SHA1 or MD5 */
+#define IMA_DIGEST_SIZE 20
+#define IMA_EVENT_NAME_LEN_MAX 255
+
+#define IMA_HASH_BITS 9
+#define IMA_MEASURE_HTABLE_SIZE (1 << IMA_HASH_BITS)
+
+/* set during initialization */
+extern int ima_initialized;
+extern int ima_used_chip;
+extern char *ima_hash;
+
+/* IMA inode template definition */
+struct ima_template_data {
+ u8 digest[IMA_DIGEST_SIZE]; /* sha1/md5 measurement hash */
+ char file_name[IMA_EVENT_NAME_LEN_MAX + 1]; /* name + \0 */
+};
+
+struct ima_template_entry {
+ u8 digest[IMA_DIGEST_SIZE]; /* sha1 or md5 measurement hash */
+ char *template_name;
+ int template_len;
+ struct ima_template_data template;
+};
+
+struct ima_queue_entry {
+ struct hlist_node hnext; /* place in hash collision list */
+ struct list_head later; /* place in ima_measurements list */
+ struct ima_template_entry *entry;
+};
+extern struct list_head ima_measurements; /* list of all measurements */
+
+/* declarations */
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+ const unsigned char *fname, const char *op,
+ const char *cause, int result, int info);
+
+/* Internal IMA function definitions */
+void ima_iintcache_init(void);
+int ima_init(void);
+void ima_cleanup(void);
+int ima_fs_init(void);
+void ima_fs_cleanup(void);
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+ const char *op, struct inode *inode);
+int ima_calc_hash(struct file *file, char *digest);
+int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_boot_aggregate(char *digest);
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+ const char *op, const char *cause);
+
+/*
+ * used to protect h_table and sha_table
+ */
+extern spinlock_t ima_queue_lock;
+
+struct ima_h_table {
+ atomic_long_t len; /* number of stored measurements in the list */
+ atomic_long_t violations;
+ struct hlist_head queue[IMA_MEASURE_HTABLE_SIZE];
+};
+extern struct ima_h_table ima_htable;
+
+static inline unsigned long ima_hash_key(u8 *digest)
+{
+ return hash_long(*digest, IMA_HASH_BITS);
+}
+
+/* iint cache flags */
+#define IMA_MEASURED 1
+#define IMA_IINT_DUMP_STACK 512
+
+/* integrity data associated with an inode */
+struct ima_iint_cache {
+ u64 version; /* track inode changes */
+ unsigned long flags;
+ u8 digest[IMA_DIGEST_SIZE];
+ struct mutex mutex; /* protects: version, flags, digest */
+ long readcount; /* measured files readcount */
+ long writecount; /* measured files writecount */
+ long opencount; /* opens reference count */
+ struct kref refcount; /* ima_iint_cache reference count */
+ struct rcu_head rcu;
+};
+
+/* LIM API function definitions */
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+ int mask, int function);
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file);
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename);
+int ima_store_template(struct ima_template_entry *entry, int violation,
+ struct inode *inode);
+void ima_template_show(struct seq_file *m, void *e,
+ enum ima_show_type show);
+
+/* radix tree calls to lookup, insert, delete
+ * integrity data associated with an inode.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode);
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode);
+void ima_iint_delete(struct inode *inode);
+void iint_free(struct kref *kref);
+void iint_rcu_free(struct rcu_head *rcu);
+
+/* IMA policy related functions */
+enum ima_hooks { PATH_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
+void ima_init_policy(void);
+void ima_update_policy(void);
+int ima_parse_add_rule(char *);
+void ima_delete_rules(void);
+
+/* LSM based policy rules require audit */
+#ifdef CONFIG_IMA_LSM_RULES
+
+#define security_filter_rule_init security_audit_rule_init
+#define security_filter_rule_match security_audit_rule_match
+
+#else
+
+static inline int security_filter_rule_init(u32 field, u32 op, char *rulestr,
+ void **lsmrule)
+{
+ return -EINVAL;
+}
+
+static inline int security_filter_rule_match(u32 secid, u32 field, u32 op,
+ void *lsmrule,
+ struct audit_context *actx)
+{
+ return -EINVAL;
+}
+#endif /* CONFIG_IMA_LSM_RULES */
+#endif
diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c
new file mode 100644
index 00000000000..a148a25804f
--- /dev/null
+++ b/security/integrity/ima/ima_api.c
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Author: Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: ima_api.c
+ * Implements must_measure, collect_measurement, store_measurement,
+ * and store_template.
+ */
+#include <linux/module.h>
+
+#include "ima.h"
+static char *IMA_TEMPLATE_NAME = "ima";
+
+/*
+ * ima_store_template - store ima template measurements
+ *
+ * Calculate the hash of a template entry, add the template entry
+ * to an ordered list of measurement entries maintained inside the kernel,
+ * and also update the aggregate integrity value (maintained inside the
+ * configured TPM PCR) over the hashes of the current list of measurement
+ * entries.
+ *
+ * Applications retrieve the current kernel-held measurement list through
+ * the securityfs entries in /sys/kernel/security/ima. The signed aggregate
+ * TPM PCR (called quote) can be retrieved using a TPM user space library
+ * and is used to validate the measurement list.
+ *
+ * Returns 0 on success, error code otherwise
+ */
+int ima_store_template(struct ima_template_entry *entry,
+ int violation, struct inode *inode)
+{
+ const char *op = "add_template_measure";
+ const char *audit_cause = "hashing_error";
+ int result;
+
+ memset(entry->digest, 0, sizeof(entry->digest));
+ entry->template_name = IMA_TEMPLATE_NAME;
+ entry->template_len = sizeof(entry->template);
+
+ if (!violation) {
+ result = ima_calc_template_hash(entry->template_len,
+ &entry->template,
+ entry->digest);
+ if (result < 0) {
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
+ entry->template_name, op,
+ audit_cause, result, 0);
+ return result;
+ }
+ }
+ result = ima_add_template_entry(entry, violation, op, inode);
+ return result;
+}
+
+/*
+ * ima_add_violation - add violation to measurement list.
+ *
+ * Violations are flagged in the measurement list with zero hash values.
+ * By extending the PCR with 0xFF's instead of with zeroes, the PCR
+ * value is invalidated.
+ */
+void ima_add_violation(struct inode *inode, const unsigned char *filename,
+ const char *op, const char *cause)
+{
+ struct ima_template_entry *entry;
+ int violation = 1;
+ int result;
+
+ /* can overflow, only indicator */
+ atomic_long_inc(&ima_htable.violations);
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ result = -ENOMEM;
+ goto err_out;
+ }
+ memset(&entry->template, 0, sizeof(entry->template));
+ strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+ result = ima_store_template(entry, violation, inode);
+ if (result < 0)
+ kfree(entry);
+err_out:
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+ op, cause, result, 0);
+}
+
+/**
+ * ima_must_measure - measure decision based on policy.
+ * @inode: pointer to inode to measure
+ * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
+ * @function: calling function (PATH_CHECK, BPRM_CHECK, FILE_MMAP)
+ *
+ * The policy is defined in terms of keypairs:
+ * subj=, obj=, type=, func=, mask=, fsmagic=
+ * subj,obj, and type: are LSM specific.
+ * func: PATH_CHECK | BPRM_CHECK | FILE_MMAP
+ * mask: contains the permission mask
+ * fsmagic: hex value
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 to measure. Return 1 if already measured.
+ * For matching a DONT_MEASURE policy, no policy, or other
+ * error, return an error code.
+*/
+int ima_must_measure(struct ima_iint_cache *iint, struct inode *inode,
+ int mask, int function)
+{
+ int must_measure;
+
+ if (iint->flags & IMA_MEASURED)
+ return 1;
+
+ must_measure = ima_match_policy(inode, function, mask);
+ return must_measure ? 0 : -EACCES;
+}
+
+/*
+ * ima_collect_measurement - collect file measurement
+ *
+ * Calculate the file hash, if it doesn't already exist,
+ * storing the measurement and i_version in the iint.
+ *
+ * Must be called with iint->mutex held.
+ *
+ * Return 0 on success, error code otherwise
+ */
+int ima_collect_measurement(struct ima_iint_cache *iint, struct file *file)
+{
+ int result = -EEXIST;
+
+ if (!(iint->flags & IMA_MEASURED)) {
+ u64 i_version = file->f_dentry->d_inode->i_version;
+
+ memset(iint->digest, 0, IMA_DIGEST_SIZE);
+ result = ima_calc_hash(file, iint->digest);
+ if (!result)
+ iint->version = i_version;
+ }
+ return result;
+}
+
+/*
+ * ima_store_measurement - store file measurement
+ *
+ * Create an "ima" template and then store the template by calling
+ * ima_store_template.
+ *
+ * We only get here if the inode has not already been measured,
+ * but the measurement could already exist:
+ * - multiple copies of the same file on either the same or
+ * different filesystems.
+ * - the inode was previously flushed as well as the iint info,
+ * containing the hashing info.
+ *
+ * Must be called with iint->mutex held.
+ */
+void ima_store_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename)
+{
+ const char *op = "add_template_measure";
+ const char *audit_cause = "ENOMEM";
+ int result = -ENOMEM;
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_template_entry *entry;
+ int violation = 0;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
+ op, audit_cause, result, 0);
+ return;
+ }
+ memset(&entry->template, 0, sizeof(entry->template));
+ memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
+ strncpy(entry->template.file_name, filename, IMA_EVENT_NAME_LEN_MAX);
+
+ result = ima_store_template(entry, violation, inode);
+ if (!result)
+ iint->flags |= IMA_MEASURED;
+ else
+ kfree(entry);
+}
diff --git a/security/integrity/ima/ima_audit.c b/security/integrity/ima/ima_audit.c
new file mode 100644
index 00000000000..8a0f1e23ccf
--- /dev/null
+++ b/security/integrity/ima/ima_audit.c
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.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, version 2 of the License.
+ *
+ * File: integrity_audit.c
+ * Audit calls for the integrity subsystem
+ */
+
+#include <linux/fs.h>
+#include <linux/audit.h>
+#include "ima.h"
+
+static int ima_audit;
+
+#ifdef CONFIG_IMA_AUDIT
+
+/* ima_audit_setup - enable informational auditing messages */
+static int __init ima_audit_setup(char *str)
+{
+ unsigned long audit;
+ int rc;
+ char *op;
+
+ rc = strict_strtoul(str, 0, &audit);
+ if (rc || audit > 1)
+ printk(KERN_INFO "ima: invalid ima_audit value\n");
+ else
+ ima_audit = audit;
+ op = ima_audit ? "ima_audit_enabled" : "ima_audit_not_enabled";
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, NULL, op, 0, 0);
+ return 1;
+}
+__setup("ima_audit=", ima_audit_setup);
+#endif
+
+void integrity_audit_msg(int audit_msgno, struct inode *inode,
+ const unsigned char *fname, const char *op,
+ const char *cause, int result, int audit_info)
+{
+ struct audit_buffer *ab;
+
+ if (!ima_audit && audit_info == 1) /* Skip informational messages */
+ return;
+
+ ab = audit_log_start(current->audit_context, GFP_KERNEL, audit_msgno);
+ audit_log_format(ab, "integrity: pid=%d uid=%u auid=%u",
+ current->pid, current->cred->uid,
+ audit_get_loginuid(current));
+ audit_log_task_context(ab);
+ switch (audit_msgno) {
+ case AUDIT_INTEGRITY_DATA:
+ case AUDIT_INTEGRITY_METADATA:
+ case AUDIT_INTEGRITY_PCR:
+ audit_log_format(ab, " op=%s cause=%s", op, cause);
+ break;
+ case AUDIT_INTEGRITY_HASH:
+ audit_log_format(ab, " op=%s hash=%s", op, cause);
+ break;
+ case AUDIT_INTEGRITY_STATUS:
+ default:
+ audit_log_format(ab, " op=%s", op);
+ }
+ audit_log_format(ab, " comm=");
+ audit_log_untrustedstring(ab, current->comm);
+ if (fname) {
+ audit_log_format(ab, " name=");
+ audit_log_untrustedstring(ab, fname);
+ }
+ if (inode)
+ audit_log_format(ab, " dev=%s ino=%lu",
+ inode->i_sb->s_id, inode->i_ino);
+ audit_log_format(ab, " res=%d", result);
+ audit_log_end(ab);
+}
diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c
new file mode 100644
index 00000000000..c2a46e40999
--- /dev/null
+++ b/security/integrity/ima/ima_crypto.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.ibm.com>
+ * Kylene Hall <kjhall@us.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, version 2 of the License.
+ *
+ * File: ima_crypto.c
+ * Calculates md5/sha1 file hash, template hash, boot-aggreate hash
+ */
+
+#include <linux/kernel.h>
+#include <linux/file.h>
+#include <linux/crypto.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+static int init_desc(struct hash_desc *desc)
+{
+ int rc;
+
+ desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
+ if (IS_ERR(desc->tfm)) {
+ pr_info("failed to load %s transform: %ld\n",
+ ima_hash, PTR_ERR(desc->tfm));
+ rc = PTR_ERR(desc->tfm);
+ return rc;
+ }
+ desc->flags = 0;
+ rc = crypto_hash_init(desc);
+ if (rc)
+ crypto_free_hash(desc->tfm);
+ return rc;
+}
+
+/*
+ * Calculate the MD5/SHA1 file digest
+ */
+int ima_calc_hash(struct file *file, char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ loff_t i_size;
+ char *rbuf;
+ int rc, offset = 0;
+
+ rc = init_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ rbuf = kzalloc(PAGE_SIZE, GFP_KERNEL);
+ if (!rbuf) {
+ rc = -ENOMEM;
+ goto out;
+ }
+ i_size = i_size_read(file->f_dentry->d_inode);
+ while (offset < i_size) {
+ int rbuf_len;
+
+ rbuf_len = kernel_read(file, offset, rbuf, PAGE_SIZE);
+ if (rbuf_len < 0) {
+ rc = rbuf_len;
+ break;
+ }
+ offset += rbuf_len;
+ sg_set_buf(sg, rbuf, rbuf_len);
+
+ rc = crypto_hash_update(&desc, sg, rbuf_len);
+ if (rc)
+ break;
+ }
+ kfree(rbuf);
+ if (!rc)
+ rc = crypto_hash_final(&desc, digest);
+out:
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
+
+/*
+ * Calculate the hash of a given template
+ */
+int ima_calc_template_hash(int template_len, void *template, char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg[1];
+ int rc;
+
+ rc = init_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ sg_set_buf(sg, template, template_len);
+ rc = crypto_hash_update(&desc, sg, template_len);
+ if (!rc)
+ rc = crypto_hash_final(&desc, digest);
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
+
+static void ima_pcrread(int idx, u8 *pcr)
+{
+ if (!ima_used_chip)
+ return;
+
+ if (tpm_pcr_read(TPM_ANY_NUM, idx, pcr) != 0)
+ pr_err("Error Communicating to TPM chip\n");
+}
+
+/*
+ * Calculate the boot aggregate hash
+ */
+int ima_calc_boot_aggregate(char *digest)
+{
+ struct hash_desc desc;
+ struct scatterlist sg;
+ u8 pcr_i[IMA_DIGEST_SIZE];
+ int rc, i;
+
+ rc = init_desc(&desc);
+ if (rc != 0)
+ return rc;
+
+ /* cumulative sha1 over tpm registers 0-7 */
+ for (i = TPM_PCR0; i < TPM_PCR8; i++) {
+ ima_pcrread(i, pcr_i);
+ /* now accumulate with current aggregate */
+ sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
+ rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+ }
+ if (!rc)
+ crypto_hash_final(&desc, digest);
+ crypto_free_hash(desc.tfm);
+ return rc;
+}
diff --git a/security/integrity/ima/ima_fs.c b/security/integrity/ima/ima_fs.c
new file mode 100644
index 00000000000..573780c76f1
--- /dev/null
+++ b/security/integrity/ima/ima_fs.c
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Reiner Sailer <sailer@us.ibm.com>
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: ima_fs.c
+ * implemenents security file system for reporting
+ * current measurement list and IMA statistics
+ */
+#include <linux/module.h>
+#include <linux/seq_file.h>
+#include <linux/rculist.h>
+#include <linux/rcupdate.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+static int valid_policy = 1;
+#define TMPBUFLEN 12
+static ssize_t ima_show_htable_value(char __user *buf, size_t count,
+ loff_t *ppos, atomic_long_t *val)
+{
+ char tmpbuf[TMPBUFLEN];
+ ssize_t len;
+
+ len = scnprintf(tmpbuf, TMPBUFLEN, "%li\n", atomic_long_read(val));
+ return simple_read_from_buffer(buf, count, ppos, tmpbuf, len);
+}
+
+static ssize_t ima_show_htable_violations(struct file *filp,
+ char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &ima_htable.violations);
+}
+
+static struct file_operations ima_htable_violations_ops = {
+ .read = ima_show_htable_violations
+};
+
+static ssize_t ima_show_measurements_count(struct file *filp,
+ char __user *buf,
+ size_t count, loff_t *ppos)
+{
+ return ima_show_htable_value(buf, count, ppos, &ima_htable.len);
+
+}
+
+static struct file_operations ima_measurements_count_ops = {
+ .read = ima_show_measurements_count
+};
+
+/* returns pointer to hlist_node */
+static void *ima_measurements_start(struct seq_file *m, loff_t *pos)
+{
+ loff_t l = *pos;
+ struct ima_queue_entry *qe;
+
+ /* we need a lock since pos could point beyond last element */
+ rcu_read_lock();
+ list_for_each_entry_rcu(qe, &ima_measurements, later) {
+ if (!l--) {
+ rcu_read_unlock();
+ return qe;
+ }
+ }
+ rcu_read_unlock();
+ return NULL;
+}
+
+static void *ima_measurements_next(struct seq_file *m, void *v, loff_t *pos)
+{
+ struct ima_queue_entry *qe = v;
+
+ /* lock protects when reading beyond last element
+ * against concurrent list-extension
+ */
+ rcu_read_lock();
+ qe = list_entry(rcu_dereference(qe->later.next),
+ struct ima_queue_entry, later);
+ rcu_read_unlock();
+ (*pos)++;
+
+ return (&qe->later == &ima_measurements) ? NULL : qe;
+}
+
+static void ima_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+static void ima_putc(struct seq_file *m, void *data, int datalen)
+{
+ while (datalen--)
+ seq_putc(m, *(char *)data++);
+}
+
+/* print format:
+ * 32bit-le=pcr#
+ * char[20]=template digest
+ * 32bit-le=template name size
+ * char[n]=template name
+ * eventdata[n]=template specific data
+ */
+static int ima_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct ima_queue_entry *qe = v;
+ struct ima_template_entry *e;
+ int namelen;
+ u32 pcr = CONFIG_IMA_MEASURE_PCR_IDX;
+
+ /* get entry */
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /*
+ * 1st: PCRIndex
+ * PCR used is always the same (config option) in
+ * little-endian format
+ */
+ ima_putc(m, &pcr, sizeof pcr);
+
+ /* 2nd: template digest */
+ ima_putc(m, e->digest, IMA_DIGEST_SIZE);
+
+ /* 3rd: template name size */
+ namelen = strlen(e->template_name);
+ ima_putc(m, &namelen, sizeof namelen);
+
+ /* 4th: template name */
+ ima_putc(m, e->template_name, namelen);
+
+ /* 5th: template specific data */
+ ima_template_show(m, (struct ima_template_data *)&e->template,
+ IMA_SHOW_BINARY);
+ return 0;
+}
+
+static struct seq_operations ima_measurments_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_measurements_show
+};
+
+static int ima_measurements_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ima_measurments_seqops);
+}
+
+static struct file_operations ima_measurements_ops = {
+ .open = ima_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static void ima_print_digest(struct seq_file *m, u8 *digest)
+{
+ int i;
+
+ for (i = 0; i < IMA_DIGEST_SIZE; i++)
+ seq_printf(m, "%02x", *(digest + i));
+}
+
+void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show)
+{
+ struct ima_template_data *entry = e;
+ int namelen;
+
+ switch (show) {
+ case IMA_SHOW_ASCII:
+ ima_print_digest(m, entry->digest);
+ seq_printf(m, " %s\n", entry->file_name);
+ break;
+ case IMA_SHOW_BINARY:
+ ima_putc(m, entry->digest, IMA_DIGEST_SIZE);
+
+ namelen = strlen(entry->file_name);
+ ima_putc(m, &namelen, sizeof namelen);
+ ima_putc(m, entry->file_name, namelen);
+ default:
+ break;
+ }
+}
+
+/* print in ascii */
+static int ima_ascii_measurements_show(struct seq_file *m, void *v)
+{
+ /* the list never shrinks, so we don't need a lock here */
+ struct ima_queue_entry *qe = v;
+ struct ima_template_entry *e;
+
+ /* get entry */
+ e = qe->entry;
+ if (e == NULL)
+ return -1;
+
+ /* 1st: PCR used (config option) */
+ seq_printf(m, "%2d ", CONFIG_IMA_MEASURE_PCR_IDX);
+
+ /* 2nd: SHA1 template hash */
+ ima_print_digest(m, e->digest);
+
+ /* 3th: template name */
+ seq_printf(m, " %s ", e->template_name);
+
+ /* 4th: template specific data */
+ ima_template_show(m, (struct ima_template_data *)&e->template,
+ IMA_SHOW_ASCII);
+ return 0;
+}
+
+static struct seq_operations ima_ascii_measurements_seqops = {
+ .start = ima_measurements_start,
+ .next = ima_measurements_next,
+ .stop = ima_measurements_stop,
+ .show = ima_ascii_measurements_show
+};
+
+static int ima_ascii_measurements_open(struct inode *inode, struct file *file)
+{
+ return seq_open(file, &ima_ascii_measurements_seqops);
+}
+
+static struct file_operations ima_ascii_measurements_ops = {
+ .open = ima_ascii_measurements_open,
+ .read = seq_read,
+ .llseek = seq_lseek,
+ .release = seq_release,
+};
+
+static ssize_t ima_write_policy(struct file *file, const char __user *buf,
+ size_t datalen, loff_t *ppos)
+{
+ char *data;
+ int rc;
+
+ if (datalen >= PAGE_SIZE)
+ return -ENOMEM;
+ if (*ppos != 0) {
+ /* No partial writes. */
+ return -EINVAL;
+ }
+ data = kmalloc(datalen + 1, GFP_KERNEL);
+ if (!data)
+ return -ENOMEM;
+
+ if (copy_from_user(data, buf, datalen)) {
+ kfree(data);
+ return -EFAULT;
+ }
+ *(data + datalen) = '\0';
+ rc = ima_parse_add_rule(data);
+ if (rc < 0) {
+ datalen = -EINVAL;
+ valid_policy = 0;
+ }
+
+ kfree(data);
+ return datalen;
+}
+
+static struct dentry *ima_dir;
+static struct dentry *binary_runtime_measurements;
+static struct dentry *ascii_runtime_measurements;
+static struct dentry *runtime_measurements_count;
+static struct dentry *violations;
+static struct dentry *ima_policy;
+
+static atomic_t policy_opencount = ATOMIC_INIT(1);
+/*
+ * ima_open_policy: sequentialize access to the policy file
+ */
+int ima_open_policy(struct inode * inode, struct file * filp)
+{
+ if (atomic_dec_and_test(&policy_opencount))
+ return 0;
+ return -EBUSY;
+}
+
+/*
+ * ima_release_policy - start using the new measure policy rules.
+ *
+ * Initially, ima_measure points to the default policy rules, now
+ * point to the new policy rules, and remove the securityfs policy file,
+ * assuming a valid policy.
+ */
+static int ima_release_policy(struct inode *inode, struct file *file)
+{
+ if (!valid_policy) {
+ ima_delete_rules();
+ valid_policy = 1;
+ atomic_set(&policy_opencount, 1);
+ return 0;
+ }
+ ima_update_policy();
+ securityfs_remove(ima_policy);
+ ima_policy = NULL;
+ return 0;
+}
+
+static struct file_operations ima_measure_policy_ops = {
+ .open = ima_open_policy,
+ .write = ima_write_policy,
+ .release = ima_release_policy
+};
+
+int ima_fs_init(void)
+{
+ ima_dir = securityfs_create_dir("ima", NULL);
+ if (IS_ERR(ima_dir))
+ return -1;
+
+ binary_runtime_measurements =
+ securityfs_create_file("binary_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_ops);
+ if (IS_ERR(binary_runtime_measurements))
+ goto out;
+
+ ascii_runtime_measurements =
+ securityfs_create_file("ascii_runtime_measurements",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_ascii_measurements_ops);
+ if (IS_ERR(ascii_runtime_measurements))
+ goto out;
+
+ runtime_measurements_count =
+ securityfs_create_file("runtime_measurements_count",
+ S_IRUSR | S_IRGRP, ima_dir, NULL,
+ &ima_measurements_count_ops);
+ if (IS_ERR(runtime_measurements_count))
+ goto out;
+
+ violations =
+ securityfs_create_file("violations", S_IRUSR | S_IRGRP,
+ ima_dir, NULL, &ima_htable_violations_ops);
+ if (IS_ERR(violations))
+ goto out;
+
+ ima_policy = securityfs_create_file("policy",
+ S_IRUSR | S_IRGRP | S_IWUSR,
+ ima_dir, NULL,
+ &ima_measure_policy_ops);
+ if (IS_ERR(ima_policy))
+ goto out;
+
+ return 0;
+out:
+ securityfs_remove(runtime_measurements_count);
+ securityfs_remove(ascii_runtime_measurements);
+ securityfs_remove(binary_runtime_measurements);
+ securityfs_remove(ima_dir);
+ securityfs_remove(ima_policy);
+ return -1;
+}
+
+void __exit ima_fs_cleanup(void)
+{
+ securityfs_remove(violations);
+ securityfs_remove(runtime_measurements_count);
+ securityfs_remove(ascii_runtime_measurements);
+ securityfs_remove(binary_runtime_measurements);
+ securityfs_remove(ima_dir);
+ securityfs_remove(ima_policy);
+}
diff --git a/security/integrity/ima/ima_iint.c b/security/integrity/ima/ima_iint.c
new file mode 100644
index 00000000000..1f035e8d29c
--- /dev/null
+++ b/security/integrity/ima/ima_iint.c
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ *
+ * Authors:
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: ima_iint.c
+ * - implements the IMA hooks: ima_inode_alloc, ima_inode_free
+ * - cache integrity information associated with an inode
+ * using a radix tree.
+ */
+#include <linux/module.h>
+#include <linux/spinlock.h>
+#include <linux/radix-tree.h>
+#include "ima.h"
+
+#define ima_iint_delete ima_inode_free
+
+RADIX_TREE(ima_iint_store, GFP_ATOMIC);
+DEFINE_SPINLOCK(ima_iint_lock);
+
+static struct kmem_cache *iint_cache __read_mostly;
+
+/* ima_iint_find_get - return the iint associated with an inode
+ *
+ * ima_iint_find_get gets a reference to the iint. Caller must
+ * remember to put the iint reference.
+ */
+struct ima_iint_cache *ima_iint_find_get(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ rcu_read_lock();
+ iint = radix_tree_lookup(&ima_iint_store, (unsigned long)inode);
+ if (!iint)
+ goto out;
+ kref_get(&iint->refcount);
+out:
+ rcu_read_unlock();
+ return iint;
+}
+
+/* Allocate memory for the iint associated with the inode
+ * from the iint_cache slab, initialize the iint, and
+ * insert it into the radix tree.
+ *
+ * On success return a pointer to the iint; on failure return NULL.
+ */
+struct ima_iint_cache *ima_iint_insert(struct inode *inode)
+{
+ struct ima_iint_cache *iint = NULL;
+ int rc = 0;
+
+ if (!ima_initialized)
+ return iint;
+ iint = kmem_cache_alloc(iint_cache, GFP_KERNEL);
+ if (!iint)
+ return iint;
+
+ rc = radix_tree_preload(GFP_KERNEL);
+ if (rc < 0)
+ goto out;
+
+ spin_lock(&ima_iint_lock);
+ rc = radix_tree_insert(&ima_iint_store, (unsigned long)inode, iint);
+ spin_unlock(&ima_iint_lock);
+out:
+ if (rc < 0) {
+ kmem_cache_free(iint_cache, iint);
+ if (rc == -EEXIST) {
+ iint = radix_tree_lookup(&ima_iint_store,
+ (unsigned long)inode);
+ } else
+ iint = NULL;
+ }
+ radix_tree_preload_end();
+ return iint;
+}
+
+/**
+ * ima_inode_alloc - allocate an iint associated with an inode
+ * @inode: pointer to the inode
+ *
+ * Return 0 on success, 1 on failure.
+ */
+int ima_inode_alloc(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized)
+ return 0;
+
+ iint = ima_iint_insert(inode);
+ if (!iint)
+ return 1;
+ return 0;
+}
+
+/* ima_iint_find_insert_get - get the iint associated with an inode
+ *
+ * Most insertions are done at inode_alloc, except those allocated
+ * before late_initcall. When the iint does not exist, allocate it,
+ * initialize and insert it, and increment the iint refcount.
+ *
+ * (Can't initialize at security_initcall before any inodes are
+ * allocated, got to wait at least until proc_init.)
+ *
+ * Return the iint.
+ */
+struct ima_iint_cache *ima_iint_find_insert_get(struct inode *inode)
+{
+ struct ima_iint_cache *iint = NULL;
+
+ iint = ima_iint_find_get(inode);
+ if (iint)
+ return iint;
+
+ iint = ima_iint_insert(inode);
+ if (iint)
+ kref_get(&iint->refcount);
+
+ return iint;
+}
+EXPORT_SYMBOL_GPL(ima_iint_find_insert_get);
+
+/* iint_free - called when the iint refcount goes to zero */
+void iint_free(struct kref *kref)
+{
+ struct ima_iint_cache *iint = container_of(kref, struct ima_iint_cache,
+ refcount);
+ iint->version = 0;
+ iint->flags = 0UL;
+ if (iint->readcount != 0) {
+ printk(KERN_INFO "%s: readcount: %ld\n", __FUNCTION__,
+ iint->readcount);
+ iint->readcount = 0;
+ }
+ if (iint->writecount != 0) {
+ printk(KERN_INFO "%s: writecount: %ld\n", __FUNCTION__,
+ iint->writecount);
+ iint->writecount = 0;
+ }
+ if (iint->opencount != 0) {
+ printk(KERN_INFO "%s: opencount: %ld\n", __FUNCTION__,
+ iint->opencount);
+ iint->opencount = 0;
+ }
+ kref_set(&iint->refcount, 1);
+ kmem_cache_free(iint_cache, iint);
+}
+
+void iint_rcu_free(struct rcu_head *rcu_head)
+{
+ struct ima_iint_cache *iint = container_of(rcu_head,
+ struct ima_iint_cache, rcu);
+ kref_put(&iint->refcount, iint_free);
+}
+
+/**
+ * ima_iint_delete - called on integrity_inode_free
+ * @inode: pointer to the inode
+ *
+ * Free the integrity information(iint) associated with an inode.
+ */
+void ima_iint_delete(struct inode *inode)
+{
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized)
+ return;
+ spin_lock(&ima_iint_lock);
+ iint = radix_tree_delete(&ima_iint_store, (unsigned long)inode);
+ spin_unlock(&ima_iint_lock);
+ if (iint)
+ call_rcu(&iint->rcu, iint_rcu_free);
+}
+
+static void init_once(void *foo)
+{
+ struct ima_iint_cache *iint = foo;
+
+ memset(iint, 0, sizeof *iint);
+ iint->version = 0;
+ iint->flags = 0UL;
+ mutex_init(&iint->mutex);
+ iint->readcount = 0;
+ iint->writecount = 0;
+ iint->opencount = 0;
+ kref_set(&iint->refcount, 1);
+}
+
+void ima_iintcache_init(void)
+{
+ iint_cache =
+ kmem_cache_create("iint_cache", sizeof(struct ima_iint_cache), 0,
+ SLAB_PANIC, init_once);
+}
diff --git a/security/integrity/ima/ima_init.c b/security/integrity/ima/ima_init.c
new file mode 100644
index 00000000000..cf227dbfac2
--- /dev/null
+++ b/security/integrity/ima/ima_init.c
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: ima_init.c
+ * initialization and cleanup functions
+ */
+#include <linux/module.h>
+#include <linux/scatterlist.h>
+#include <linux/err.h>
+#include "ima.h"
+
+/* name for boot aggregate entry */
+static char *boot_aggregate_name = "boot_aggregate";
+int ima_used_chip;
+
+/* Add the boot aggregate to the IMA measurement list and extend
+ * the PCR register.
+ *
+ * Calculate the boot aggregate, a SHA1 over tpm registers 0-7,
+ * assuming a TPM chip exists, and zeroes if the TPM chip does not
+ * exist. Add the boot aggregate measurement to the measurement
+ * list and extend the PCR register.
+ *
+ * If a tpm chip does not exist, indicate the core root of trust is
+ * not hardware based by invalidating the aggregate PCR value.
+ * (The aggregate PCR value is invalidated by adding one value to
+ * the measurement list and extending the aggregate PCR value with
+ * a different value.) Violations add a zero entry to the measurement
+ * list and extend the aggregate PCR value with ff...ff's.
+ */
+static void ima_add_boot_aggregate(void)
+{
+ struct ima_template_entry *entry;
+ const char *op = "add_boot_aggregate";
+ const char *audit_cause = "ENOMEM";
+ int result = -ENOMEM;
+ int violation = 1;
+
+ entry = kmalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry)
+ goto err_out;
+
+ memset(&entry->template, 0, sizeof(entry->template));
+ strncpy(entry->template.file_name, boot_aggregate_name,
+ IMA_EVENT_NAME_LEN_MAX);
+ if (ima_used_chip) {
+ violation = 0;
+ result = ima_calc_boot_aggregate(entry->template.digest);
+ if (result < 0) {
+ audit_cause = "hashing_error";
+ kfree(entry);
+ goto err_out;
+ }
+ }
+ result = ima_store_template(entry, violation, NULL);
+ if (result < 0)
+ kfree(entry);
+ return;
+err_out:
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, NULL, boot_aggregate_name, op,
+ audit_cause, result, 0);
+}
+
+int ima_init(void)
+{
+ u8 pcr_i[IMA_DIGEST_SIZE];
+ int rc;
+
+ ima_used_chip = 0;
+ rc = tpm_pcr_read(TPM_ANY_NUM, 0, pcr_i);
+ if (rc == 0)
+ ima_used_chip = 1;
+
+ if (!ima_used_chip)
+ pr_info("No TPM chip found, activating TPM-bypass!\n");
+
+ ima_add_boot_aggregate(); /* boot aggregate must be first entry */
+ ima_init_policy();
+
+ return ima_fs_init();
+}
+
+void __exit ima_cleanup(void)
+{
+ ima_fs_cleanup();
+}
diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c
new file mode 100644
index 00000000000..f4e7266f5ae
--- /dev/null
+++ b/security/integrity/ima/ima_main.c
@@ -0,0 +1,327 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Serge Hallyn <serue@us.ibm.com>
+ * Kylene Hall <kylene@us.ibm.com>
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: ima_main.c
+ * implements the IMA hooks: ima_bprm_check, ima_file_mmap,
+ * and ima_path_check.
+ */
+#include <linux/module.h>
+#include <linux/file.h>
+#include <linux/binfmts.h>
+#include <linux/mount.h>
+#include <linux/mman.h>
+
+#include "ima.h"
+
+int ima_initialized;
+
+char *ima_hash = "sha1";
+static int __init hash_setup(char *str)
+{
+ const char *op = "hash_setup";
+ const char *hash = "sha1";
+ int result = 0;
+ int audit_info = 0;
+
+ if (strncmp(str, "md5", 3) == 0) {
+ hash = "md5";
+ ima_hash = str;
+ } else if (strncmp(str, "sha1", 4) != 0) {
+ hash = "invalid_hash_type";
+ result = 1;
+ }
+ integrity_audit_msg(AUDIT_INTEGRITY_HASH, NULL, NULL, op, hash,
+ result, audit_info);
+ return 1;
+}
+__setup("ima_hash=", hash_setup);
+
+/**
+ * ima_file_free - called on __fput()
+ * @file: pointer to file structure being freed
+ *
+ * Flag files that changed, based on i_version;
+ * and decrement the iint readcount/writecount.
+ */
+void ima_file_free(struct file *file)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return;
+ iint = ima_iint_find_get(inode);
+ if (!iint)
+ return;
+
+ mutex_lock(&iint->mutex);
+ if (iint->opencount <= 0) {
+ printk(KERN_INFO
+ "%s: %s open/free imbalance (r:%ld w:%ld o:%ld f:%ld)\n",
+ __FUNCTION__, file->f_dentry->d_name.name,
+ iint->readcount, iint->writecount,
+ iint->opencount, atomic_long_read(&file->f_count));
+ if (!(iint->flags & IMA_IINT_DUMP_STACK)) {
+ dump_stack();
+ iint->flags |= IMA_IINT_DUMP_STACK;
+ }
+ }
+ iint->opencount--;
+
+ if ((file->f_mode & (FMODE_READ | FMODE_WRITE)) == FMODE_READ)
+ iint->readcount--;
+
+ if (file->f_mode & FMODE_WRITE) {
+ iint->writecount--;
+ if (iint->writecount == 0) {
+ if (iint->version != inode->i_version)
+ iint->flags &= ~IMA_MEASURED;
+ }
+ }
+ mutex_unlock(&iint->mutex);
+ kref_put(&iint->refcount, iint_free);
+}
+
+/* ima_read_write_check - reflect possible reading/writing errors in the PCR.
+ *
+ * When opening a file for read, if the file is already open for write,
+ * the file could change, resulting in a file measurement error.
+ *
+ * Opening a file for write, if the file is already open for read, results
+ * in a time of measure, time of use (ToMToU) error.
+ *
+ * In either case invalidate the PCR.
+ */
+enum iint_pcr_error { TOMTOU, OPEN_WRITERS };
+static void ima_read_write_check(enum iint_pcr_error error,
+ struct ima_iint_cache *iint,
+ struct inode *inode,
+ const unsigned char *filename)
+{
+ switch (error) {
+ case TOMTOU:
+ if (iint->readcount > 0)
+ ima_add_violation(inode, filename, "invalid_pcr",
+ "ToMToU");
+ break;
+ case OPEN_WRITERS:
+ if (iint->writecount > 0)
+ ima_add_violation(inode, filename, "invalid_pcr",
+ "open_writers");
+ break;
+ }
+}
+
+static int get_path_measurement(struct ima_iint_cache *iint, struct file *file,
+ const unsigned char *filename)
+{
+ int rc = 0;
+
+ if (IS_ERR(file)) {
+ pr_info("%s dentry_open failed\n", filename);
+ return rc;
+ }
+ iint->opencount++;
+ iint->readcount++;
+
+ rc = ima_collect_measurement(iint, file);
+ if (!rc)
+ ima_store_measurement(iint, file, filename);
+ return rc;
+}
+
+/**
+ * ima_path_check - based on policy, collect/store measurement.
+ * @path: contains a pointer to the path to be measured
+ * @mask: contains MAY_READ, MAY_WRITE or MAY_EXECUTE
+ *
+ * Measure the file being open for readonly, based on the
+ * ima_must_measure() policy decision.
+ *
+ * Keep read/write counters for all files, but only
+ * invalidate the PCR for measured files:
+ * - Opening a file for write when already open for read,
+ * results in a time of measure, time of use (ToMToU) error.
+ * - Opening a file for read when already open for write,
+ * could result in a file measurement error.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_path_check(struct path *path, int mask)
+{
+ struct inode *inode = path->dentry->d_inode;
+ struct ima_iint_cache *iint;
+ struct file *file = NULL;
+ int rc;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return 0;
+ iint = ima_iint_find_insert_get(inode);
+ if (!iint)
+ return 0;
+
+ mutex_lock(&iint->mutex);
+ iint->opencount++;
+ if ((mask & MAY_WRITE) || (mask == 0))
+ iint->writecount++;
+ else if (mask & (MAY_READ | MAY_EXEC))
+ iint->readcount++;
+
+ rc = ima_must_measure(iint, inode, MAY_READ, PATH_CHECK);
+ if (rc < 0)
+ goto out;
+
+ if ((mask & MAY_WRITE) || (mask == 0))
+ ima_read_write_check(TOMTOU, iint, inode,
+ path->dentry->d_name.name);
+
+ if ((mask & (MAY_WRITE | MAY_READ | MAY_EXEC)) != MAY_READ)
+ goto out;
+
+ ima_read_write_check(OPEN_WRITERS, iint, inode,
+ path->dentry->d_name.name);
+ if (!(iint->flags & IMA_MEASURED)) {
+ struct dentry *dentry = dget(path->dentry);
+ struct vfsmount *mnt = mntget(path->mnt);
+
+ file = dentry_open(dentry, mnt, O_RDONLY, current->cred);
+ rc = get_path_measurement(iint, file, dentry->d_name.name);
+ }
+out:
+ mutex_unlock(&iint->mutex);
+ if (file)
+ fput(file);
+ kref_put(&iint->refcount, iint_free);
+ return 0;
+}
+
+static int process_measurement(struct file *file, const unsigned char *filename,
+ int mask, int function)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_iint_cache *iint;
+ int rc;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return 0;
+ iint = ima_iint_find_insert_get(inode);
+ if (!iint)
+ return -ENOMEM;
+
+ mutex_lock(&iint->mutex);
+ rc = ima_must_measure(iint, inode, mask, function);
+ if (rc != 0)
+ goto out;
+
+ rc = ima_collect_measurement(iint, file);
+ if (!rc)
+ ima_store_measurement(iint, file, filename);
+out:
+ mutex_unlock(&iint->mutex);
+ kref_put(&iint->refcount, iint_free);
+ return rc;
+}
+
+static void opencount_get(struct file *file)
+{
+ struct inode *inode = file->f_dentry->d_inode;
+ struct ima_iint_cache *iint;
+
+ if (!ima_initialized || !S_ISREG(inode->i_mode))
+ return;
+ iint = ima_iint_find_insert_get(inode);
+ if (!iint)
+ return;
+ mutex_lock(&iint->mutex);
+ iint->opencount++;
+ mutex_unlock(&iint->mutex);
+}
+
+/**
+ * ima_file_mmap - based on policy, collect/store measurement.
+ * @file: pointer to the file to be measured (May be NULL)
+ * @prot: contains the protection that will be applied by the kernel.
+ *
+ * Measure files being mmapped executable based on the ima_must_measure()
+ * policy decision.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_file_mmap(struct file *file, unsigned long prot)
+{
+ int rc;
+
+ if (!file)
+ return 0;
+ if (prot & PROT_EXEC)
+ rc = process_measurement(file, file->f_dentry->d_name.name,
+ MAY_EXEC, FILE_MMAP);
+ return 0;
+}
+
+/*
+ * ima_shm_check - IPC shm and shmat create/fput a file
+ *
+ * Maintain the opencount for these files to prevent unnecessary
+ * imbalance messages.
+ */
+void ima_shm_check(struct file *file)
+{
+ opencount_get(file);
+ return;
+}
+
+/**
+ * ima_bprm_check - based on policy, collect/store measurement.
+ * @bprm: contains the linux_binprm structure
+ *
+ * The OS protects against an executable file, already open for write,
+ * from being executed in deny_write_access() and an executable file,
+ * already open for execute, from being modified in get_write_access().
+ * So we can be certain that what we verify and measure here is actually
+ * what is being executed.
+ *
+ * Return 0 on success, an error code on failure.
+ * (Based on the results of appraise_measurement().)
+ */
+int ima_bprm_check(struct linux_binprm *bprm)
+{
+ int rc;
+
+ rc = process_measurement(bprm->file, bprm->filename,
+ MAY_EXEC, BPRM_CHECK);
+ return 0;
+}
+
+static int __init init_ima(void)
+{
+ int error;
+
+ ima_iintcache_init();
+ error = ima_init();
+ ima_initialized = 1;
+ return error;
+}
+
+static void __exit cleanup_ima(void)
+{
+ ima_cleanup();
+}
+
+late_initcall(init_ima); /* Start IMA after the TPM is available */
+
+MODULE_DESCRIPTION("Integrity Measurement Architecture");
+MODULE_LICENSE("GPL");
diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c
new file mode 100644
index 00000000000..23810e0bfc6
--- /dev/null
+++ b/security/integrity/ima/ima_policy.c
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2008 IBM Corporation
+ * Author: Mimi Zohar <zohar@us.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, version 2 of the License.
+ *
+ * ima_policy.c
+ * - initialize default measure policy rules
+ *
+ */
+#include <linux/module.h>
+#include <linux/list.h>
+#include <linux/audit.h>
+#include <linux/security.h>
+#include <linux/magic.h>
+#include <linux/parser.h>
+
+#include "ima.h"
+
+/* flags definitions */
+#define IMA_FUNC 0x0001
+#define IMA_MASK 0x0002
+#define IMA_FSMAGIC 0x0004
+#define IMA_UID 0x0008
+
+enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
+
+#define MAX_LSM_RULES 6
+enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
+ LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
+};
+
+struct ima_measure_rule_entry {
+ struct list_head list;
+ enum ima_action action;
+ unsigned int flags;
+ enum ima_hooks func;
+ int mask;
+ unsigned long fsmagic;
+ uid_t uid;
+ struct {
+ void *rule; /* LSM file metadata specific */
+ int type; /* audit type */
+ } lsm[MAX_LSM_RULES];
+};
+
+/* Without LSM specific knowledge, the default policy can only be
+ * written in terms of .action, .func, .mask, .fsmagic, and .uid
+ */
+static struct ima_measure_rule_entry default_rules[] = {
+ {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,
+ .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,
+ .flags = IMA_FSMAGIC},
+ {.action = DONT_MEASURE,.fsmagic = 0xF97CFF8C,.flags = IMA_FSMAGIC},
+ {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+ .flags = IMA_FUNC | IMA_MASK},
+ {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
+ .flags = IMA_FUNC | IMA_MASK},
+ {.action = MEASURE,.func = PATH_CHECK,.mask = MAY_READ,.uid = 0,
+ .flags = IMA_FUNC | IMA_MASK | IMA_UID}
+};
+
+static LIST_HEAD(measure_default_rules);
+static LIST_HEAD(measure_policy_rules);
+static struct list_head *ima_measure;
+
+static DEFINE_MUTEX(ima_measure_mutex);
+
+/**
+ * ima_match_rules - determine whether an inode matches the measure rule.
+ * @rule: a pointer to a rule
+ * @inode: a pointer to an inode
+ * @func: LIM hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Returns true on rule match, false on failure.
+ */
+static bool ima_match_rules(struct ima_measure_rule_entry *rule,
+ struct inode *inode, enum ima_hooks func, int mask)
+{
+ struct task_struct *tsk = current;
+ int i;
+
+ if ((rule->flags & IMA_FUNC) && rule->func != func)
+ return false;
+ if ((rule->flags & IMA_MASK) && rule->mask != mask)
+ return false;
+ if ((rule->flags & IMA_FSMAGIC)
+ && rule->fsmagic != inode->i_sb->s_magic)
+ return false;
+ if ((rule->flags & IMA_UID) && rule->uid != tsk->cred->uid)
+ return false;
+ for (i = 0; i < MAX_LSM_RULES; i++) {
+ int rc;
+ u32 osid, sid;
+
+ if (!rule->lsm[i].rule)
+ continue;
+
+ switch (i) {
+ case LSM_OBJ_USER:
+ case LSM_OBJ_ROLE:
+ case LSM_OBJ_TYPE:
+ security_inode_getsecid(inode, &osid);
+ rc = security_filter_rule_match(osid,
+ rule->lsm[i].type,
+ AUDIT_EQUAL,
+ rule->lsm[i].rule,
+ NULL);
+ break;
+ case LSM_SUBJ_USER:
+ case LSM_SUBJ_ROLE:
+ case LSM_SUBJ_TYPE:
+ security_task_getsecid(tsk, &sid);
+ rc = security_filter_rule_match(sid,
+ rule->lsm[i].type,
+ AUDIT_EQUAL,
+ rule->lsm[i].rule,
+ NULL);
+ default:
+ break;
+ }
+ if (!rc)
+ return false;
+ }
+ return true;
+}
+
+/**
+ * ima_match_policy - decision based on LSM and other conditions
+ * @inode: pointer to an inode for which the policy decision is being made
+ * @func: IMA hook identifier
+ * @mask: requested action (MAY_READ | MAY_WRITE | MAY_APPEND | MAY_EXEC)
+ *
+ * Measure decision based on func/mask/fsmagic and LSM(subj/obj/type)
+ * conditions.
+ *
+ * (There is no need for locking when walking the policy list,
+ * as elements in the list are never deleted, nor does the list
+ * change.)
+ */
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
+{
+ struct ima_measure_rule_entry *entry;
+
+ list_for_each_entry(entry, ima_measure, list) {
+ bool rc;
+
+ rc = ima_match_rules(entry, inode, func, mask);
+ if (rc)
+ return entry->action;
+ }
+ return 0;
+}
+
+/**
+ * ima_init_policy - initialize the default measure rules.
+ *
+ * ima_measure points to either the measure_default_rules or the
+ * the new measure_policy_rules.
+ */
+void ima_init_policy(void)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(default_rules); i++)
+ list_add_tail(&default_rules[i].list, &measure_default_rules);
+ ima_measure = &measure_default_rules;
+}
+
+/**
+ * ima_update_policy - update default_rules with new measure rules
+ *
+ * Called on file .release to update the default rules with a complete new
+ * policy. Once updated, the policy is locked, no additional rules can be
+ * added to the policy.
+ */
+void ima_update_policy(void)
+{
+ const char *op = "policy_update";
+ const char *cause = "already exists";
+ int result = 1;
+ int audit_info = 0;
+
+ if (ima_measure == &measure_default_rules) {
+ ima_measure = &measure_policy_rules;
+ cause = "complete";
+ result = 0;
+ }
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+ NULL, op, cause, result, audit_info);
+}
+
+enum {
+ Opt_err = -1,
+ Opt_measure = 1, Opt_dont_measure,
+ Opt_obj_user, Opt_obj_role, Opt_obj_type,
+ Opt_subj_user, Opt_subj_role, Opt_subj_type,
+ Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
+};
+
+static match_table_t policy_tokens = {
+ {Opt_measure, "measure"},
+ {Opt_dont_measure, "dont_measure"},
+ {Opt_obj_user, "obj_user=%s"},
+ {Opt_obj_role, "obj_role=%s"},
+ {Opt_obj_type, "obj_type=%s"},
+ {Opt_subj_user, "subj_user=%s"},
+ {Opt_subj_role, "subj_role=%s"},
+ {Opt_subj_type, "subj_type=%s"},
+ {Opt_func, "func=%s"},
+ {Opt_mask, "mask=%s"},
+ {Opt_fsmagic, "fsmagic=%s"},
+ {Opt_uid, "uid=%s"},
+ {Opt_err, NULL}
+};
+
+static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
+ char *args, int lsm_rule, int audit_type)
+{
+ int result;
+
+ entry->lsm[lsm_rule].type = audit_type;
+ result = security_filter_rule_init(entry->lsm[lsm_rule].type,
+ AUDIT_EQUAL, args,
+ &entry->lsm[lsm_rule].rule);
+ return result;
+}
+
+static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
+{
+ struct audit_buffer *ab;
+ char *p;
+ int result = 0;
+
+ ab = audit_log_start(current->audit_context, GFP_KERNEL,
+ AUDIT_INTEGRITY_STATUS);
+
+ entry->action = -1;
+ while ((p = strsep(&rule, " \n")) != NULL) {
+ substring_t args[MAX_OPT_ARGS];
+ int token;
+ unsigned long lnum;
+
+ if (result < 0)
+ break;
+ if (!*p)
+ continue;
+ token = match_token(p, policy_tokens, args);
+ switch (token) {
+ case Opt_measure:
+ audit_log_format(ab, "%s ", "measure");
+ entry->action = MEASURE;
+ break;
+ case Opt_dont_measure:
+ audit_log_format(ab, "%s ", "dont_measure");
+ entry->action = DONT_MEASURE;
+ break;
+ case Opt_func:
+ audit_log_format(ab, "func=%s ", args[0].from);
+ if (strcmp(args[0].from, "PATH_CHECK") == 0)
+ entry->func = PATH_CHECK;
+ else if (strcmp(args[0].from, "FILE_MMAP") == 0)
+ entry->func = FILE_MMAP;
+ else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
+ entry->func = BPRM_CHECK;
+ else
+ result = -EINVAL;
+ if (!result)
+ entry->flags |= IMA_FUNC;
+ break;
+ case Opt_mask:
+ audit_log_format(ab, "mask=%s ", args[0].from);
+ if ((strcmp(args[0].from, "MAY_EXEC")) == 0)
+ entry->mask = MAY_EXEC;
+ else if (strcmp(args[0].from, "MAY_WRITE") == 0)
+ entry->mask = MAY_WRITE;
+ else if (strcmp(args[0].from, "MAY_READ") == 0)
+ entry->mask = MAY_READ;
+ else if (strcmp(args[0].from, "MAY_APPEND") == 0)
+ entry->mask = MAY_APPEND;
+ else
+ result = -EINVAL;
+ if (!result)
+ entry->flags |= IMA_MASK;
+ break;
+ case Opt_fsmagic:
+ audit_log_format(ab, "fsmagic=%s ", args[0].from);
+ result = strict_strtoul(args[0].from, 16,
+ &entry->fsmagic);
+ if (!result)
+ entry->flags |= IMA_FSMAGIC;
+ break;
+ case Opt_uid:
+ audit_log_format(ab, "uid=%s ", args[0].from);
+ result = strict_strtoul(args[0].from, 10, &lnum);
+ if (!result) {
+ entry->uid = (uid_t) lnum;
+ if (entry->uid != lnum)
+ result = -EINVAL;
+ else
+ entry->flags |= IMA_UID;
+ }
+ break;
+ case Opt_obj_user:
+ audit_log_format(ab, "obj_user=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_OBJ_USER,
+ AUDIT_OBJ_USER);
+ break;
+ case Opt_obj_role:
+ audit_log_format(ab, "obj_role=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_OBJ_ROLE,
+ AUDIT_OBJ_ROLE);
+ break;
+ case Opt_obj_type:
+ audit_log_format(ab, "obj_type=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_OBJ_TYPE,
+ AUDIT_OBJ_TYPE);
+ break;
+ case Opt_subj_user:
+ audit_log_format(ab, "subj_user=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_SUBJ_USER,
+ AUDIT_SUBJ_USER);
+ break;
+ case Opt_subj_role:
+ audit_log_format(ab, "subj_role=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_SUBJ_ROLE,
+ AUDIT_SUBJ_ROLE);
+ break;
+ case Opt_subj_type:
+ audit_log_format(ab, "subj_type=%s ", args[0].from);
+ result = ima_lsm_rule_init(entry, args[0].from,
+ LSM_SUBJ_TYPE,
+ AUDIT_SUBJ_TYPE);
+ break;
+ case Opt_err:
+ printk(KERN_INFO "%s: unknown token: %s\n",
+ __FUNCTION__, p);
+ break;
+ }
+ }
+ if (entry->action == UNKNOWN)
+ result = -EINVAL;
+
+ audit_log_format(ab, "res=%d", result);
+ audit_log_end(ab);
+ return result;
+}
+
+/**
+ * ima_parse_add_rule - add a rule to measure_policy_rules
+ * @rule - ima measurement policy rule
+ *
+ * Uses a mutex to protect the policy list from multiple concurrent writers.
+ * Returns 0 on success, an error code on failure.
+ */
+int ima_parse_add_rule(char *rule)
+{
+ const char *op = "add_rule";
+ struct ima_measure_rule_entry *entry;
+ int result = 0;
+ int audit_info = 0;
+
+ /* Prevent installed policy from changing */
+ if (ima_measure != &measure_default_rules) {
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+ NULL, op, "already exists",
+ -EACCES, audit_info);
+ return -EACCES;
+ }
+
+ entry = kzalloc(sizeof(*entry), GFP_KERNEL);
+ if (!entry) {
+ integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
+ NULL, op, "-ENOMEM", -ENOMEM, audit_info);
+ return -ENOMEM;
+ }
+
+ INIT_LIST_HEAD(&entry->list);
+
+ result = ima_parse_rule(rule, entry);
+ if (!result) {
+ mutex_lock(&ima_measure_mutex);
+ list_add_tail(&entry->list, &measure_policy_rules);
+ mutex_unlock(&ima_measure_mutex);
+ } else
+ kfree(entry);
+ return result;
+}
+
+/* ima_delete_rules called to cleanup invalid policy */
+void ima_delete_rules(void)
+{
+ struct ima_measure_rule_entry *entry, *tmp;
+
+ mutex_lock(&ima_measure_mutex);
+ list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
+ list_del(&entry->list);
+ kfree(entry);
+ }
+ mutex_unlock(&ima_measure_mutex);
+}
diff --git a/security/integrity/ima/ima_queue.c b/security/integrity/ima/ima_queue.c
new file mode 100644
index 00000000000..7ec94314ac0
--- /dev/null
+++ b/security/integrity/ima/ima_queue.c
@@ -0,0 +1,140 @@
+/*
+ * Copyright (C) 2005,2006,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Serge Hallyn <serue@us.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Mimi Zohar <zohar@us.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, version 2 of the
+ * License.
+ *
+ * File: ima_queue.c
+ * Implements queues that store template measurements and
+ * maintains aggregate over the stored measurements
+ * in the pre-configured TPM PCR (if available).
+ * The measurement list is append-only. No entry is
+ * ever removed or changed during the boot-cycle.
+ */
+#include <linux/module.h>
+#include <linux/rculist.h>
+#include "ima.h"
+
+LIST_HEAD(ima_measurements); /* list of all measurements */
+
+/* key: inode (before secure-hashing a file) */
+struct ima_h_table ima_htable = {
+ .len = ATOMIC_LONG_INIT(0),
+ .violations = ATOMIC_LONG_INIT(0),
+ .queue[0 ... IMA_MEASURE_HTABLE_SIZE - 1] = HLIST_HEAD_INIT
+};
+
+/* mutex protects atomicity of extending measurement list
+ * and extending the TPM PCR aggregate. Since tpm_extend can take
+ * long (and the tpm driver uses a mutex), we can't use the spinlock.
+ */
+static DEFINE_MUTEX(ima_extend_list_mutex);
+
+/* lookup up the digest value in the hash table, and return the entry */
+static struct ima_queue_entry *ima_lookup_digest_entry(u8 *digest_value)
+{
+ struct ima_queue_entry *qe, *ret = NULL;
+ unsigned int key;
+ struct hlist_node *pos;
+ int rc;
+
+ key = ima_hash_key(digest_value);
+ rcu_read_lock();
+ hlist_for_each_entry_rcu(qe, pos, &ima_htable.queue[key], hnext) {
+ rc = memcmp(qe->entry->digest, digest_value, IMA_DIGEST_SIZE);
+ if (rc == 0) {
+ ret = qe;
+ break;
+ }
+ }
+ rcu_read_unlock();
+ return ret;
+}
+
+/* ima_add_template_entry helper function:
+ * - Add template entry to measurement list and hash table.
+ *
+ * (Called with ima_extend_list_mutex held.)
+ */
+static int ima_add_digest_entry(struct ima_template_entry *entry)
+{
+ struct ima_queue_entry *qe;
+ unsigned int key;
+
+ qe = kmalloc(sizeof(*qe), GFP_KERNEL);
+ if (qe == NULL) {
+ pr_err("OUT OF MEMORY ERROR creating queue entry.\n");
+ return -ENOMEM;
+ }
+ qe->entry = entry;
+
+ INIT_LIST_HEAD(&qe->later);
+ list_add_tail_rcu(&qe->later, &ima_measurements);
+
+ atomic_long_inc(&ima_htable.len);
+ key = ima_hash_key(entry->digest);
+ hlist_add_head_rcu(&qe->hnext, &ima_htable.queue[key]);
+ return 0;
+}
+
+static int ima_pcr_extend(const u8 *hash)
+{
+ int result = 0;
+
+ if (!ima_used_chip)
+ return result;
+
+ result = tpm_pcr_extend(TPM_ANY_NUM, CONFIG_IMA_MEASURE_PCR_IDX, hash);
+ if (result != 0)
+ pr_err("Error Communicating to TPM chip\n");
+ return result;
+}
+
+/* Add template entry to the measurement list and hash table,
+ * and extend the pcr.
+ */
+int ima_add_template_entry(struct ima_template_entry *entry, int violation,
+ const char *op, struct inode *inode)
+{
+ u8 digest[IMA_DIGEST_SIZE];
+ const char *audit_cause = "hash_added";
+ int audit_info = 1;
+ int result = 0;
+
+ mutex_lock(&ima_extend_list_mutex);
+ if (!violation) {
+ memcpy(digest, entry->digest, sizeof digest);
+ if (ima_lookup_digest_entry(digest)) {
+ audit_cause = "hash_exists";
+ goto out;
+ }
+ }
+
+ result = ima_add_digest_entry(entry);
+ if (result < 0) {
+ audit_cause = "ENOMEM";
+ audit_info = 0;
+ goto out;
+ }
+
+ if (violation) /* invalidate pcr */
+ memset(digest, 0xff, sizeof digest);
+
+ result = ima_pcr_extend(digest);
+ if (result != 0) {
+ audit_cause = "TPM error";
+ audit_info = 0;
+ }
+out:
+ mutex_unlock(&ima_extend_list_mutex);
+ integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, entry->template_name,
+ op, audit_cause, result, audit_info);
+ return result;
+}
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c
index 00815973d41..a69d6f8970c 100644
--- a/security/selinux/hooks.c
+++ b/security/selinux/hooks.c
@@ -89,7 +89,7 @@
#define XATTR_SELINUX_SUFFIX "selinux"
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
-#define NUM_SEL_MNT_OPTS 4
+#define NUM_SEL_MNT_OPTS 5
extern unsigned int policydb_loaded_version;
extern int selinux_nlmsg_lookup(u16 sclass, u16 nlmsg_type, u32 *perm);
@@ -353,6 +353,7 @@ enum {
Opt_fscontext = 2,
Opt_defcontext = 3,
Opt_rootcontext = 4,
+ Opt_labelsupport = 5,
};
static const match_table_t tokens = {
@@ -360,6 +361,7 @@ static const match_table_t tokens = {
{Opt_fscontext, FSCONTEXT_STR "%s"},
{Opt_defcontext, DEFCONTEXT_STR "%s"},
{Opt_rootcontext, ROOTCONTEXT_STR "%s"},
+ {Opt_labelsupport, LABELSUPP_STR},
{Opt_error, NULL},
};
@@ -431,7 +433,7 @@ static int sb_finish_set_opts(struct super_block *sb)
}
}
- sbsec->initialized = 1;
+ sbsec->flags |= (SE_SBINITIALIZED | SE_SBLABELSUPP);
if (sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
printk(KERN_ERR "SELinux: initialized (dev %s, type %s), unknown behavior\n",
@@ -441,6 +443,12 @@ static int sb_finish_set_opts(struct super_block *sb)
sb->s_id, sb->s_type->name,
labeling_behaviors[sbsec->behavior-1]);
+ if (sbsec->behavior == SECURITY_FS_USE_GENFS ||
+ sbsec->behavior == SECURITY_FS_USE_MNTPOINT ||
+ sbsec->behavior == SECURITY_FS_USE_NONE ||
+ sbsec->behavior > ARRAY_SIZE(labeling_behaviors))
+ sbsec->flags &= ~SE_SBLABELSUPP;
+
/* Initialize the root inode. */
rc = inode_doinit_with_dentry(root_inode, root);
@@ -487,23 +495,22 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
security_init_mnt_opts(opts);
- if (!sbsec->initialized)
+ if (!(sbsec->flags & SE_SBINITIALIZED))
return -EINVAL;
if (!ss_initialized)
return -EINVAL;
- /*
- * if we ever use sbsec flags for anything other than tracking mount
- * settings this is going to need a mask
- */
- tmp = sbsec->flags;
+ tmp = sbsec->flags & SE_MNTMASK;
/* count the number of mount options for this sb */
for (i = 0; i < 8; i++) {
if (tmp & 0x01)
opts->num_mnt_opts++;
tmp >>= 1;
}
+ /* Check if the Label support flag is set */
+ if (sbsec->flags & SE_SBLABELSUPP)
+ opts->num_mnt_opts++;
opts->mnt_opts = kcalloc(opts->num_mnt_opts, sizeof(char *), GFP_ATOMIC);
if (!opts->mnt_opts) {
@@ -549,6 +556,10 @@ static int selinux_get_mnt_opts(const struct super_block *sb,
opts->mnt_opts[i] = context;
opts->mnt_opts_flags[i++] = ROOTCONTEXT_MNT;
}
+ if (sbsec->flags & SE_SBLABELSUPP) {
+ opts->mnt_opts[i] = NULL;
+ opts->mnt_opts_flags[i++] = SE_SBLABELSUPP;
+ }
BUG_ON(i != opts->num_mnt_opts);
@@ -562,8 +573,10 @@ out_free:
static int bad_option(struct superblock_security_struct *sbsec, char flag,
u32 old_sid, u32 new_sid)
{
+ char mnt_flags = sbsec->flags & SE_MNTMASK;
+
/* check if the old mount command had the same options */
- if (sbsec->initialized)
+ if (sbsec->flags & SE_SBINITIALIZED)
if (!(sbsec->flags & flag) ||
(old_sid != new_sid))
return 1;
@@ -571,8 +584,8 @@ static int bad_option(struct superblock_security_struct *sbsec, char flag,
/* check if we were passed the same options twice,
* aka someone passed context=a,context=b
*/
- if (!sbsec->initialized)
- if (sbsec->flags & flag)
+ if (!(sbsec->flags & SE_SBINITIALIZED))
+ if (mnt_flags & flag)
return 1;
return 0;
}
@@ -626,7 +639,7 @@ static int selinux_set_mnt_opts(struct super_block *sb,
* this sb does not set any security options. (The first options
* will be used for both mounts)
*/
- if (sbsec->initialized && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
+ if ((sbsec->flags & SE_SBINITIALIZED) && (sb->s_type->fs_flags & FS_BINARY_MOUNTDATA)
&& (num_opts == 0))
goto out;
@@ -637,6 +650,9 @@ static int selinux_set_mnt_opts(struct super_block *sb,
*/
for (i = 0; i < num_opts; i++) {
u32 sid;
+
+ if (flags[i] == SE_SBLABELSUPP)
+ continue;
rc = security_context_to_sid(mount_options[i],
strlen(mount_options[i]), &sid);
if (rc) {
@@ -690,19 +706,19 @@ static int selinux_set_mnt_opts(struct super_block *sb,
}
}
- if (sbsec->initialized) {
+ if (sbsec->flags & SE_SBINITIALIZED) {
/* previously mounted with options, but not on this attempt? */
- if (sbsec->flags && !num_opts)
+ if ((sbsec->flags & SE_MNTMASK) && !num_opts)
goto out_double_mount;
rc = 0;
goto out;
}
if (strcmp(sb->s_type->name, "proc") == 0)
- sbsec->proc = 1;
+ sbsec->flags |= SE_SBPROC;
/* Determine the labeling behavior to use for this filesystem type. */
- rc = security_fs_use(sbsec->proc ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
+ rc = security_fs_use((sbsec->flags & SE_SBPROC) ? "proc" : sb->s_type->name, &sbsec->behavior, &sbsec->sid);
if (rc) {
printk(KERN_WARNING "%s: security_fs_use(%s) returned %d\n",
__func__, sb->s_type->name, rc);
@@ -806,10 +822,10 @@ static void selinux_sb_clone_mnt_opts(const struct super_block *oldsb,
}
/* how can we clone if the old one wasn't set up?? */
- BUG_ON(!oldsbsec->initialized);
+ BUG_ON(!(oldsbsec->flags & SE_SBINITIALIZED));
/* if fs is reusing a sb, just let its options stand... */
- if (newsbsec->initialized)
+ if (newsbsec->flags & SE_SBINITIALIZED)
return;
mutex_lock(&newsbsec->lock);
@@ -917,7 +933,8 @@ static int selinux_parse_opts_str(char *options,
goto out_err;
}
break;
-
+ case Opt_labelsupport:
+ break;
default:
rc = -EINVAL;
printk(KERN_WARNING "SELinux: unknown mount option\n");
@@ -999,7 +1016,12 @@ static void selinux_write_opts(struct seq_file *m,
char *prefix;
for (i = 0; i < opts->num_mnt_opts; i++) {
- char *has_comma = strchr(opts->mnt_opts[i], ',');
+ char *has_comma;
+
+ if (opts->mnt_opts[i])
+ has_comma = strchr(opts->mnt_opts[i], ',');
+ else
+ has_comma = NULL;
switch (opts->mnt_opts_flags[i]) {
case CONTEXT_MNT:
@@ -1014,6 +1036,10 @@ static void selinux_write_opts(struct seq_file *m,
case DEFCONTEXT_MNT:
prefix = DEFCONTEXT_STR;
break;
+ case SE_SBLABELSUPP:
+ seq_putc(m, ',');
+ seq_puts(m, LABELSUPP_STR);
+ continue;
default:
BUG();
};
@@ -1209,7 +1235,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
goto out_unlock;
sbsec = inode->i_sb->s_security;
- if (!sbsec->initialized) {
+ if (!(sbsec->flags & SE_SBINITIALIZED)) {
/* Defer initialization until selinux_complete_init,
after the initial policy is loaded and the security
server is ready to handle calls. */
@@ -1326,7 +1352,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
/* Default to the fs superblock SID. */
isec->sid = sbsec->sid;
- if (sbsec->proc && !S_ISLNK(inode->i_mode)) {
+ if ((sbsec->flags & SE_SBPROC) && !S_ISLNK(inode->i_mode)) {
struct proc_inode *proci = PROC_I(inode);
if (proci->pde) {
isec->sclass = inode_mode_to_security_class(inode->i_mode);
@@ -1587,7 +1613,7 @@ static int may_create(struct inode *dir,
if (rc)
return rc;
- if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
rc = security_transition_sid(sid, dsec->sid, tclass, &newsid);
if (rc)
return rc;
@@ -1866,6 +1892,16 @@ static int selinux_capset(struct cred *new, const struct cred *old,
return cred_has_perm(old, new, PROCESS__SETCAP);
}
+/*
+ * (This comment used to live with the selinux_task_setuid hook,
+ * which was removed).
+ *
+ * Since setuid only affects the current process, and since the SELinux
+ * controls are not based on the Linux identity attributes, SELinux does not
+ * need to control this operation. However, SELinux does control the use of
+ * the CAP_SETUID and CAP_SETGID capabilities using the capable hook.
+ */
+
static int selinux_capable(struct task_struct *tsk, const struct cred *cred,
int cap, int audit)
{
@@ -2156,11 +2192,6 @@ static int selinux_bprm_set_creds(struct linux_binprm *bprm)
return 0;
}
-static int selinux_bprm_check_security(struct linux_binprm *bprm)
-{
- return secondary_ops->bprm_check_security(bprm);
-}
-
static int selinux_bprm_secureexec(struct linux_binprm *bprm)
{
const struct cred *cred = current_cred();
@@ -2290,8 +2321,6 @@ static void selinux_bprm_committing_creds(struct linux_binprm *bprm)
struct rlimit *rlim, *initrlim;
int rc, i;
- secondary_ops->bprm_committing_creds(bprm);
-
new_tsec = bprm->cred->security;
if (new_tsec->sid == new_tsec->osid)
return;
@@ -2337,8 +2366,6 @@ static void selinux_bprm_committed_creds(struct linux_binprm *bprm)
int rc, i;
unsigned long flags;
- secondary_ops->bprm_committed_creds(bprm);
-
osid = tsec->osid;
sid = tsec->sid;
@@ -2400,7 +2427,8 @@ static inline int selinux_option(char *option, int len)
return (match_prefix(CONTEXT_STR, sizeof(CONTEXT_STR)-1, option, len) ||
match_prefix(FSCONTEXT_STR, sizeof(FSCONTEXT_STR)-1, option, len) ||
match_prefix(DEFCONTEXT_STR, sizeof(DEFCONTEXT_STR)-1, option, len) ||
- match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len));
+ match_prefix(ROOTCONTEXT_STR, sizeof(ROOTCONTEXT_STR)-1, option, len) ||
+ match_prefix(LABELSUPP_STR, sizeof(LABELSUPP_STR)-1, option, len));
}
static inline void take_option(char **to, char *from, int *first, int len)
@@ -2513,11 +2541,6 @@ static int selinux_mount(char *dev_name,
void *data)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->sb_mount(dev_name, path, type, flags, data);
- if (rc)
- return rc;
if (flags & MS_REMOUNT)
return superblock_has_perm(cred, path->mnt->mnt_sb,
@@ -2530,11 +2553,6 @@ static int selinux_mount(char *dev_name,
static int selinux_umount(struct vfsmount *mnt, int flags)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->sb_umount(mnt, flags);
- if (rc)
- return rc;
return superblock_has_perm(cred, mnt->mnt_sb,
FILESYSTEM__UNMOUNT, NULL);
@@ -2570,7 +2588,7 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
sid = tsec->sid;
newsid = tsec->create_sid;
- if (!newsid || sbsec->behavior == SECURITY_FS_USE_MNTPOINT) {
+ if (!newsid || !(sbsec->flags & SE_SBLABELSUPP)) {
rc = security_transition_sid(sid, dsec->sid,
inode_mode_to_security_class(inode->i_mode),
&newsid);
@@ -2585,14 +2603,14 @@ static int selinux_inode_init_security(struct inode *inode, struct inode *dir,
}
/* Possibly defer initialization to selinux_complete_init. */
- if (sbsec->initialized) {
+ if (sbsec->flags & SE_SBINITIALIZED) {
struct inode_security_struct *isec = inode->i_security;
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid;
isec->initialized = 1;
}
- if (!ss_initialized || sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ if (!ss_initialized || !(sbsec->flags & SE_SBLABELSUPP))
return -EOPNOTSUPP;
if (name) {
@@ -2622,21 +2640,11 @@ static int selinux_inode_create(struct inode *dir, struct dentry *dentry, int ma
static int selinux_inode_link(struct dentry *old_dentry, struct inode *dir, struct dentry *new_dentry)
{
- int rc;
-
- rc = secondary_ops->inode_link(old_dentry, dir, new_dentry);
- if (rc)
- return rc;
return may_link(dir, old_dentry, MAY_LINK);
}
static int selinux_inode_unlink(struct inode *dir, struct dentry *dentry)
{
- int rc;
-
- rc = secondary_ops->inode_unlink(dir, dentry);
- if (rc)
- return rc;
return may_link(dir, dentry, MAY_UNLINK);
}
@@ -2657,12 +2665,6 @@ static int selinux_inode_rmdir(struct inode *dir, struct dentry *dentry)
static int selinux_inode_mknod(struct inode *dir, struct dentry *dentry, int mode, dev_t dev)
{
- int rc;
-
- rc = secondary_ops->inode_mknod(dir, dentry, mode, dev);
- if (rc)
- return rc;
-
return may_create(dir, dentry, inode_mode_to_security_class(mode));
}
@@ -2682,22 +2684,13 @@ static int selinux_inode_readlink(struct dentry *dentry)
static int selinux_inode_follow_link(struct dentry *dentry, struct nameidata *nameidata)
{
const struct cred *cred = current_cred();
- int rc;
- rc = secondary_ops->inode_follow_link(dentry, nameidata);
- if (rc)
- return rc;
return dentry_has_perm(cred, NULL, dentry, FILE__READ);
}
static int selinux_inode_permission(struct inode *inode, int mask)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->inode_permission(inode, mask);
- if (rc)
- return rc;
if (!mask) {
/* No permission to check. Existence test. */
@@ -2711,11 +2704,6 @@ static int selinux_inode_permission(struct inode *inode, int mask)
static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->inode_setattr(dentry, iattr);
- if (rc)
- return rc;
if (iattr->ia_valid & ATTR_FORCE)
return 0;
@@ -2769,7 +2757,7 @@ static int selinux_inode_setxattr(struct dentry *dentry, const char *name,
return selinux_inode_setotherxattr(dentry, name);
sbsec = inode->i_sb->s_security;
- if (sbsec->behavior == SECURITY_FS_USE_MNTPOINT)
+ if (!(sbsec->flags & SE_SBLABELSUPP))
return -EOPNOTSUPP;
if (!is_owner_or_cap(inode))
@@ -2931,16 +2919,6 @@ static int selinux_inode_listsecurity(struct inode *inode, char *buffer, size_t
return len;
}
-static int selinux_inode_need_killpriv(struct dentry *dentry)
-{
- return secondary_ops->inode_need_killpriv(dentry);
-}
-
-static int selinux_inode_killpriv(struct dentry *dentry)
-{
- return secondary_ops->inode_killpriv(dentry);
-}
-
static void selinux_inode_getsecid(const struct inode *inode, u32 *secid)
{
struct inode_security_struct *isec = inode->i_security;
@@ -3078,18 +3056,13 @@ static int selinux_file_mprotect(struct vm_area_struct *vma,
unsigned long prot)
{
const struct cred *cred = current_cred();
- int rc;
-
- rc = secondary_ops->file_mprotect(vma, reqprot, prot);
- if (rc)
- return rc;
if (selinux_checkreqprot)
prot = reqprot;
#ifndef CONFIG_PPC32
if ((prot & PROT_EXEC) && !(vma->vm_flags & VM_EXEC)) {
- rc = 0;
+ int rc = 0;
if (vma->vm_start >= vma->vm_mm->start_brk &&
vma->vm_end <= vma->vm_mm->brk) {
rc = cred_has_perm(cred, cred, PROCESS__EXECHEAP);
@@ -3239,12 +3212,6 @@ static int selinux_dentry_open(struct file *file, const struct cred *cred)
static int selinux_task_create(unsigned long clone_flags)
{
- int rc;
-
- rc = secondary_ops->task_create(clone_flags);
- if (rc)
- return rc;
-
return current_has_perm(current, PROCESS__FORK);
}
@@ -3278,14 +3245,6 @@ static int selinux_cred_prepare(struct cred *new, const struct cred *old,
}
/*
- * commit new credentials
- */
-static void selinux_cred_commit(struct cred *new, const struct cred *old)
-{
- secondary_ops->cred_commit(new, old);
-}
-
-/*
* set the security data for a kernel service
* - all the creation contexts are set to unlabelled
*/
@@ -3329,29 +3288,6 @@ static int selinux_kernel_create_files_as(struct cred *new, struct inode *inode)
return 0;
}
-static int selinux_task_setuid(uid_t id0, uid_t id1, uid_t id2, int flags)
-{
- /* Since setuid only affects the current process, and
- since the SELinux controls are not based on the Linux
- identity attributes, SELinux does not need to control
- this operation. However, SELinux does control the use
- of the CAP_SETUID and CAP_SETGID capabilities using the
- capable hook. */
- return 0;
-}
-
-static int selinux_task_fix_setuid(struct cred *new, const struct cred *old,
- int flags)
-{
- return secondary_ops->task_fix_setuid(new, old, flags);
-}
-
-static int selinux_task_setgid(gid_t id0, gid_t id1, gid_t id2, int flags)
-{
- /* See the comment for setuid above. */
- return 0;
-}
-
static int selinux_task_setpgid(struct task_struct *p, pid_t pgid)
{
return current_has_perm(p, PROCESS__SETPGID);
@@ -3372,12 +3308,6 @@ static void selinux_task_getsecid(struct task_struct *p, u32 *secid)
*secid = task_sid(p);
}
-static int selinux_task_setgroups(struct group_info *group_info)
-{
- /* See the comment for setuid above. */
- return 0;
-}
-
static int selinux_task_setnice(struct task_struct *p, int nice)
{
int rc;
@@ -3408,11 +3338,6 @@ static int selinux_task_getioprio(struct task_struct *p)
static int selinux_task_setrlimit(unsigned int resource, struct rlimit *new_rlim)
{
struct rlimit *old_rlim = current->signal->rlim + resource;
- int rc;
-
- rc = secondary_ops->task_setrlimit(resource, new_rlim);
- if (rc)
- return rc;
/* Control the ability to change the hard limit (whether
lowering or raising it), so that the hard limit can
@@ -3451,10 +3376,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
u32 perm;
int rc;
- rc = secondary_ops->task_kill(p, info, sig, secid);
- if (rc)
- return rc;
-
if (!sig)
perm = PROCESS__SIGNULL; /* null signal; existence test */
else
@@ -3467,18 +3388,6 @@ static int selinux_task_kill(struct task_struct *p, struct siginfo *info,
return rc;
}
-static int selinux_task_prctl(int option,
- unsigned long arg2,
- unsigned long arg3,
- unsigned long arg4,
- unsigned long arg5)
-{
- /* The current prctl operations do not appear to require
- any SELinux controls since they merely observe or modify
- the state of the current process. */
- return secondary_ops->task_prctl(option, arg2, arg3, arg4, arg5);
-}
-
static int selinux_task_wait(struct task_struct *p)
{
return task_has_perm(p, current, PROCESS__SIGCHLD);
@@ -4047,10 +3956,6 @@ static int selinux_socket_unix_stream_connect(struct socket *sock,
struct avc_audit_data ad;
int err;
- err = secondary_ops->unix_stream_connect(sock, other, newsk);
- if (err)
- return err;
-
isec = SOCK_INODE(sock)->i_security;
other_isec = SOCK_INODE(other)->i_security;
@@ -5167,11 +5072,6 @@ static int selinux_shm_shmat(struct shmid_kernel *shp,
char __user *shmaddr, int shmflg)
{
u32 perms;
- int rc;
-
- rc = secondary_ops->shm_shmat(shp, shmaddr, shmflg);
- if (rc)
- return rc;
if (shmflg & SHM_RDONLY)
perms = SHM__READ;
@@ -5581,7 +5481,6 @@ static struct security_operations selinux_ops = {
.netlink_recv = selinux_netlink_recv,
.bprm_set_creds = selinux_bprm_set_creds,
- .bprm_check_security = selinux_bprm_check_security,
.bprm_committing_creds = selinux_bprm_committing_creds,
.bprm_committed_creds = selinux_bprm_committed_creds,
.bprm_secureexec = selinux_bprm_secureexec,
@@ -5623,8 +5522,6 @@ static struct security_operations selinux_ops = {
.inode_getsecurity = selinux_inode_getsecurity,
.inode_setsecurity = selinux_inode_setsecurity,
.inode_listsecurity = selinux_inode_listsecurity,
- .inode_need_killpriv = selinux_inode_need_killpriv,
- .inode_killpriv = selinux_inode_killpriv,
.inode_getsecid = selinux_inode_getsecid,
.file_permission = selinux_file_permission,
@@ -5644,17 +5541,12 @@ static struct security_operations selinux_ops = {
.task_create = selinux_task_create,
.cred_free = selinux_cred_free,
.cred_prepare = selinux_cred_prepare,
- .cred_commit = selinux_cred_commit,
.kernel_act_as = selinux_kernel_act_as,
.kernel_create_files_as = selinux_kernel_create_files_as,
- .task_setuid = selinux_task_setuid,
- .task_fix_setuid = selinux_task_fix_setuid,
- .task_setgid = selinux_task_setgid,
.task_setpgid = selinux_task_setpgid,
.task_getpgid = selinux_task_getpgid,
.task_getsid = selinux_task_getsid,
.task_getsecid = selinux_task_getsecid,
- .task_setgroups = selinux_task_setgroups,
.task_setnice = selinux_task_setnice,
.task_setioprio = selinux_task_setioprio,
.task_getioprio = selinux_task_getioprio,
@@ -5664,7 +5556,6 @@ static struct security_operations selinux_ops = {
.task_movememory = selinux_task_movememory,
.task_kill = selinux_task_kill,
.task_wait = selinux_task_wait,
- .task_prctl = selinux_task_prctl,
.task_to_inode = selinux_task_to_inode,
.ipc_permission = selinux_ipc_permission,
diff --git a/security/selinux/include/objsec.h b/security/selinux/include/objsec.h
index 3cc45168f67..c4e062336ef 100644
--- a/security/selinux/include/objsec.h
+++ b/security/selinux/include/objsec.h
@@ -60,9 +60,7 @@ struct superblock_security_struct {
u32 def_sid; /* default SID for labeling */
u32 mntpoint_sid; /* SECURITY_FS_USE_MNTPOINT context for files */
unsigned int behavior; /* labeling behavior */
- unsigned char initialized; /* initialization flag */
unsigned char flags; /* which mount options were specified */
- unsigned char proc; /* proc fs */
struct mutex lock;
struct list_head isec_head;
spinlock_t isec_lock;
diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h
index 72447370bc9..e1d9db77998 100644
--- a/security/selinux/include/security.h
+++ b/security/selinux/include/security.h
@@ -37,15 +37,23 @@
#define POLICYDB_VERSION_MAX POLICYDB_VERSION_BOUNDARY
#endif
+/* Mask for just the mount related flags */
+#define SE_MNTMASK 0x0f
+/* Super block security struct flags for mount options */
#define CONTEXT_MNT 0x01
#define FSCONTEXT_MNT 0x02
#define ROOTCONTEXT_MNT 0x04
#define DEFCONTEXT_MNT 0x08
+/* Non-mount related flags */
+#define SE_SBINITIALIZED 0x10
+#define SE_SBPROC 0x20
+#define SE_SBLABELSUPP 0x40
#define CONTEXT_STR "context="
#define FSCONTEXT_STR "fscontext="
#define ROOTCONTEXT_STR "rootcontext="
#define DEFCONTEXT_STR "defcontext="
+#define LABELSUPP_STR "seclabel"
struct netlbl_lsm_secattr;