Merge branch 'master' into next
James Morris [Fri, 6 Feb 2009 00:01:45 +0000 (11:01 +1100)]
Conflicts:
fs/namei.c

Manually merged per:

diff --cc fs/namei.c
index 734f2b5,bbc15c2..0000000
--- a/fs/namei.c
+++ b/fs/namei.c
@@@ -860,9 -848,8 +849,10 @@@ static int __link_path_walk(const char
   nd->flags |= LOOKUP_CONTINUE;
   err = exec_permission_lite(inode);
   if (err == -EAGAIN)
-  err = vfs_permission(nd, MAY_EXEC);
+  err = inode_permission(nd->path.dentry->d_inode,
+         MAY_EXEC);
 + if (!err)
 + err = ima_path_check(&nd->path, MAY_EXEC);
    if (err)
   break;

@@@ -1525,14 -1506,9 +1509,14 @@@ int may_open(struct path *path, int acc
   flag &= ~O_TRUNC;
   }

-  error = vfs_permission(nd, acc_mode);
+  error = inode_permission(inode, acc_mode);
   if (error)
   return error;
 +
-  error = ima_path_check(&nd->path,
++ error = ima_path_check(path,
 +        acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
 + if (error)
 + return error;
   /*
    * An append-only file must be opened in append mode for writing.
    */

Signed-off-by: James Morris <jmorris@namei.org>

33 files changed:
Documentation/ABI/testing/ima_policy [new file with mode: 0644]
Documentation/kernel-parameters.txt
MAINTAINERS
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
fs/exec.c
fs/file_table.c
fs/inode.c
fs/namei.c
include/linux/audit.h
include/linux/ima.h [new file with mode: 0644]
include/linux/tpm.h [new file with mode: 0644]
ipc/shm.c
mm/mmap.c
mm/shmem.c
security/Kconfig
security/Makefile
security/inode.c
security/integrity/ima/Kconfig [new file with mode: 0644]
security/integrity/ima/Makefile [new file with mode: 0644]
security/integrity/ima/ima.h [new file with mode: 0644]
security/integrity/ima/ima_api.c [new file with mode: 0644]
security/integrity/ima/ima_audit.c [new file with mode: 0644]
security/integrity/ima/ima_crypto.c [new file with mode: 0644]
security/integrity/ima/ima_fs.c [new file with mode: 0644]
security/integrity/ima/ima_iint.c [new file with mode: 0644]
security/integrity/ima/ima_init.c [new file with mode: 0644]
security/integrity/ima/ima_main.c [new file with mode: 0644]
security/integrity/ima/ima_policy.c [new file with mode: 0644]
security/integrity/ima/ima_queue.c [new file with mode: 0644]
security/selinux/hooks.c
security/selinux/include/objsec.h
security/selinux/include/security.h

diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy
new file mode 100644 (file)
index 0000000..6434f0d
--- /dev/null
@@ -0,0 +1,61 @@
+What:          security/ima/policy
+Date:          May 2008
+Contact:       Mimi Zohar <zohar@us.ibm.com>
+Description:
+               The Trusted Computing Group(TCG) runtime Integrity
+               Measurement Architecture(IMA) maintains a list of hash
+               values of executables and other sensitive system files
+               loaded into the run-time of this system.  At runtime,
+               the policy can be constrained based on LSM specific data.
+               Policies are loaded into the securityfs file ima/policy
+               by opening the file, writing the rules one at a time and
+               then closing the file.  The new policy takes effect after
+               the file ima/policy is closed.
+
+               rule format: action [condition ...]
+
+               action: measure | dont_measure
+               condition:= base | lsm
+                       base:   [[func=] [mask=] [fsmagic=] [uid=]]
+                       lsm:    [[subj_user=] [subj_role=] [subj_type=]
+                                [obj_user=] [obj_role=] [obj_type=]]
+
+               base:   func:= [BPRM_CHECK][FILE_MMAP][INODE_PERMISSION]
+                       mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
+                       fsmagic:= hex value
+                       uid:= decimal value
+               lsm:    are LSM specific
+
+               default policy:
+                       # PROC_SUPER_MAGIC
+                       dont_measure fsmagic=0x9fa0
+                       # SYSFS_MAGIC
+                       dont_measure fsmagic=0x62656572
+                       # DEBUGFS_MAGIC
+                       dont_measure fsmagic=0x64626720
+                       # TMPFS_MAGIC
+                       dont_measure fsmagic=0x01021994
+                       # SECURITYFS_MAGIC
+                       dont_measure fsmagic=0x73636673
+
+                       measure func=BPRM_CHECK
+                       measure func=FILE_MMAP mask=MAY_EXEC
+                       measure func=INODE_PERM mask=MAY_READ uid=0
+
+               The default policy measures all executables in bprm_check,
+               all files mmapped executable in file_mmap, and all files
+               open for read by root in inode_permission.
+
+               Examples of LSM specific definitions:
+
+               SELinux:
+                       # SELINUX_MAGIC
+                       dont_measure fsmagic=0xF97CFF8C
+
+                       dont_measure obj_type=var_log_t
+                       dont_measure obj_type=auditd_log_t
+                       measure subj_user=system_u func=INODE_PERM mask=MAY_READ
+                       measure subj_role=system_r func=INODE_PERM mask=MAY_READ
+
+               Smack:
+                       measure subj_user=_ func=INODE_PERM mask=MAY_READ
index d8362cf..8cc40a1 100644 (file)
@@ -44,6 +44,7 @@ parameter is applicable:
        FB      The frame buffer device is enabled.
        HW      Appropriate hardware is enabled.
        IA-64   IA-64 architecture is enabled.
+       IMA     Integrity measurement architecture is enabled.
        IOSCHED More than one I/O scheduler is enabled.
        IP_PNP  IP DHCP, BOOTP, or RARP is enabled.
        ISAPNP  ISA PnP code is enabled.
@@ -900,6 +901,15 @@ and is between 256 and 4096 characters. It is defined in the file
        ihash_entries=  [KNL]
                        Set number of hash buckets for inode cache.
 
+       ima_audit=      [IMA]
+                       Format: { "0" | "1" }
+                       0 -- integrity auditing messages. (Default)
+                       1 -- enable informational integrity auditing messages.
+
+       ima_hash=       [IMA]
+                       Formt: { "sha1" | "md5" }
+                       default: "sha1"
+
        in2000=         [HW,SCSI]
                        See header of drivers/scsi/in2000.c.
 
index 421504b..a781f2e 100644 (file)
@@ -2201,6 +2201,11 @@ M:       stefanr@s5r6.in-berlin.de
 L:     linux1394-devel@lists.sourceforge.net
 S:     Maintained
 
+INTEGRITY MEASUREMENT ARCHITECTURE (IMA)
+P:     Mimi Zohar
+M:     zohar@us.ibm.com
+S:     Supported
+
 IMS TWINTURBO FRAMEBUFFER DRIVER
 L:     linux-fbdev-devel@lists.sourceforge.net (moderated for non-subscribers)
 S:     Orphan
@@ -3825,6 +3830,7 @@ M:        jmorris@namei.org
 L:     linux-kernel@vger.kernel.org
 L:     linux-security-module@vger.kernel.org (suggested Cc:)
 T:     git kernel.org:pub/scm/linux/kernel/git/jmorris/security-testing-2.6.git
+W:     http://security.wiki.kernel.org/
 S:     Supported
 
 SECURITY CONTACT
index 9c47dc4..ccdd828 100644 (file)
@@ -429,134 +429,148 @@ out:
 #define TPM_DIGEST_SIZE 20
 #define TPM_ERROR_SIZE 10
 #define TPM_RET_CODE_IDX 6
-#define TPM_GET_CAP_RET_SIZE_IDX 10
-#define TPM_GET_CAP_RET_UINT32_1_IDX 14
-#define TPM_GET_CAP_RET_UINT32_2_IDX 18
-#define TPM_GET_CAP_RET_UINT32_3_IDX 22
-#define TPM_GET_CAP_RET_UINT32_4_IDX 26
-#define TPM_GET_CAP_PERM_DISABLE_IDX 16
-#define TPM_GET_CAP_PERM_INACTIVE_IDX 18
-#define TPM_GET_CAP_RET_BOOL_1_IDX 14
-#define TPM_GET_CAP_TEMP_INACTIVE_IDX 16
-
-#define TPM_CAP_IDX 13
-#define TPM_CAP_SUBCAP_IDX 21
 
 enum tpm_capabilities {
-       TPM_CAP_FLAG = 4,
-       TPM_CAP_PROP = 5,
+       TPM_CAP_FLAG = cpu_to_be32(4),
+       TPM_CAP_PROP = cpu_to_be32(5),
+       CAP_VERSION_1_1 = cpu_to_be32(0x06),
+       CAP_VERSION_1_2 = cpu_to_be32(0x1A)
 };
 
 enum tpm_sub_capabilities {
-       TPM_CAP_PROP_PCR = 0x1,
-       TPM_CAP_PROP_MANUFACTURER = 0x3,
-       TPM_CAP_FLAG_PERM = 0x8,
-       TPM_CAP_FLAG_VOL = 0x9,
-       TPM_CAP_PROP_OWNER = 0x11,
-       TPM_CAP_PROP_TIS_TIMEOUT = 0x15,
-       TPM_CAP_PROP_TIS_DURATION = 0x20,
-};
+       TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
+       TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
+       TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
+       TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
+       TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
+       TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
+       TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
 
-/*
- * This is a semi generic GetCapability command for use
- * with the capability type TPM_CAP_PROP or TPM_CAP_FLAG
- * and their associated sub_capabilities.
- */
-
-static const u8 tpm_cap[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 22,            /* length */
-       0, 0, 0, 101,           /* TPM_ORD_GetCapability */
-       0, 0, 0, 0,             /* TPM_CAP_<TYPE> */
-       0, 0, 0, 4,             /* TPM_CAP_SUB_<TYPE> size */
-       0, 0, 1, 0              /* TPM_CAP_SUB_<TYPE> */
 };
 
-static ssize_t transmit_cmd(struct tpm_chip *chip, u8 *data, int len,
-                           char *desc)
+static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
+                           int len, const char *desc)
 {
        int err;
 
-       len = tpm_transmit(chip, data, len);
+       len = tpm_transmit(chip,(u8 *) cmd, len);
        if (len <  0)
                return len;
        if (len == TPM_ERROR_SIZE) {
-               err = be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX)));
+               err = be32_to_cpu(cmd->header.out.return_code);
                dev_dbg(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
                return err;
        }
        return 0;
 }
 
+#define TPM_INTERNAL_RESULT_SIZE 200
+#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
+#define TPM_ORD_GET_CAP cpu_to_be32(101)
+
+static const struct tpm_input_header tpm_getcap_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(22),
+       .ordinal = TPM_ORD_GET_CAP
+};
+
+ssize_t tpm_getcap(struct device *dev, __be32 subcap_id, cap_t *cap,
+                  const char *desc)
+{
+       struct tpm_cmd_t tpm_cmd;
+       int rc;
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+
+       tpm_cmd.header.in = tpm_getcap_header;
+       if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) {
+               tpm_cmd.params.getcap_in.cap = subcap_id;
+               /*subcap field not necessary */
+               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0);
+               tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32));
+       } else {
+               if (subcap_id == TPM_CAP_FLAG_PERM ||
+                   subcap_id == TPM_CAP_FLAG_VOL)
+                       tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG;
+               else
+                       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+               tpm_cmd.params.getcap_in.subcap = subcap_id;
+       }
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, desc);
+       if (!rc)
+               *cap = tpm_cmd.params.getcap_out.cap;
+       return rc;
+}
+
 void tpm_gen_interrupt(struct tpm_chip *chip)
 {
-       u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+       struct  tpm_cmd_t tpm_cmd;
        ssize_t rc;
 
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+       tpm_cmd.header.in = tpm_getcap_header;
+       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+       tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
-       rc = transmit_cmd(chip, data, sizeof(data),
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                        "attempting to determine the timeouts");
 }
 EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 
 void tpm_get_timeouts(struct tpm_chip *chip)
 {
-       u8 data[max_t(int, ARRAY_SIZE(tpm_cap), 30)];
+       struct tpm_cmd_t tpm_cmd;
+       struct timeout_t *timeout_cap;
+       struct duration_t *duration_cap;
        ssize_t rc;
        u32 timeout;
 
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_TIMEOUT;
+       tpm_cmd.header.in = tpm_getcap_header;
+       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+       tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
 
-       rc = transmit_cmd(chip, data, sizeof(data),
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                        "attempting to determine the timeouts");
        if (rc)
                goto duration;
 
-       if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+       if (be32_to_cpu(tpm_cmd.header.out.length)
            != 4 * sizeof(u32))
                goto duration;
 
+       timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout;
        /* Don't overwrite default if value is 0 */
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX)));
+       timeout = be32_to_cpu(timeout_cap->a);
        if (timeout)
                chip->vendor.timeout_a = usecs_to_jiffies(timeout);
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_2_IDX)));
+       timeout = be32_to_cpu(timeout_cap->b);
        if (timeout)
                chip->vendor.timeout_b = usecs_to_jiffies(timeout);
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_3_IDX)));
+       timeout = be32_to_cpu(timeout_cap->c);
        if (timeout)
                chip->vendor.timeout_c = usecs_to_jiffies(timeout);
-       timeout =
-           be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_4_IDX)));
+       timeout = be32_to_cpu(timeout_cap->d);
        if (timeout)
                chip->vendor.timeout_d = usecs_to_jiffies(timeout);
 
 duration:
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_TIS_DURATION;
+       tpm_cmd.header.in = tpm_getcap_header;
+       tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+       tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+       tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
 
-       rc = transmit_cmd(chip, data, sizeof(data),
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
                        "attempting to determine the durations");
        if (rc)
                return;
 
-       if (be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_SIZE_IDX)))
+       if (be32_to_cpu(tpm_cmd.header.out.return_code)
            != 3 * sizeof(u32))
                return;
-
+       duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
        chip->vendor.duration[TPM_SHORT] =
-           usecs_to_jiffies(be32_to_cpu
-                            (*((__be32 *) (data +
-                                           TPM_GET_CAP_RET_UINT32_1_IDX))));
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
        /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
         * value wrong and apparently reports msecs rather than usecs. So we
         * fix up the resulting too-small TPM_SHORT value to make things work.
@@ -565,13 +579,9 @@ duration:
                chip->vendor.duration[TPM_SHORT] = HZ;
 
        chip->vendor.duration[TPM_MEDIUM] =
-           usecs_to_jiffies(be32_to_cpu
-                            (*((__be32 *) (data +
-                                           TPM_GET_CAP_RET_UINT32_2_IDX))));
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium));
        chip->vendor.duration[TPM_LONG] =
-           usecs_to_jiffies(be32_to_cpu
-                            (*((__be32 *) (data +
-                                           TPM_GET_CAP_RET_UINT32_3_IDX))));
+           usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long));
 }
 EXPORT_SYMBOL_GPL(tpm_get_timeouts);
 
@@ -587,36 +597,18 @@ void tpm_continue_selftest(struct tpm_chip *chip)
 }
 EXPORT_SYMBOL_GPL(tpm_continue_selftest);
 
-#define  TPM_INTERNAL_RESULT_SIZE 200
-
 ssize_t tpm_show_enabled(struct device * dev, struct device_attribute * attr,
                        char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attemtping to determine the permanent enabled state");
-       if (rc) {
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+                        "attempting to determine the permanent enabled state");
+       if (rc)
                return 0;
-       }
-
-       rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_DISABLE_IDX]);
 
-       kfree(data);
+       rc = sprintf(buf, "%d\n", !cap.perm_flags.disable);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_enabled);
@@ -624,31 +616,15 @@ EXPORT_SYMBOL_GPL(tpm_show_enabled);
 ssize_t tpm_show_active(struct device * dev, struct device_attribute * attr,
                        char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_PERM;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attemtping to determine the permanent active state");
-       if (rc) {
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap,
+                        "attempting to determine the permanent active state");
+       if (rc)
                return 0;
-       }
 
-       rc = sprintf(buf, "%d\n", !data[TPM_GET_CAP_PERM_INACTIVE_IDX]);
-
-       kfree(data);
+       rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_active);
@@ -656,31 +632,15 @@ EXPORT_SYMBOL_GPL(tpm_show_active);
 ssize_t tpm_show_owned(struct device * dev, struct device_attribute * attr,
                        char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_OWNER;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the owner state");
-       if (rc) {
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap,
+                        "attempting to determine the owner state");
+       if (rc)
                return 0;
-       }
-
-       rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_RET_BOOL_1_IDX]);
 
-       kfree(data);
+       rc = sprintf(buf, "%d\n", cap.owned);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_owned);
@@ -688,116 +648,180 @@ EXPORT_SYMBOL_GPL(tpm_show_owned);
 ssize_t tpm_show_temp_deactivated(struct device * dev,
                                struct device_attribute * attr, char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
+       rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap,
+                        "attempting to determine the temporary state");
+       if (rc)
+               return 0;
 
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
+       rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_FLAG;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_FLAG_VOL;
+/*
+ * tpm_chip_find_get - return tpm_chip for given chip number
+ */
+static struct tpm_chip *tpm_chip_find_get(int chip_num)
+{
+       struct tpm_chip *pos, *chip = NULL;
 
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the temporary state");
-       if (rc) {
-               kfree(data);
-               return 0;
+       rcu_read_lock();
+       list_for_each_entry_rcu(pos, &tpm_chip_list, list) {
+               if (chip_num != TPM_ANY_NUM && chip_num != pos->dev_num)
+                       continue;
+
+               if (try_module_get(pos->dev->driver->owner)) {
+                       chip = pos;
+                       break;
+               }
        }
+       rcu_read_unlock();
+       return chip;
+}
 
-       rc = sprintf(buf, "%d\n", data[TPM_GET_CAP_TEMP_INACTIVE_IDX]);
+#define TPM_ORDINAL_PCRREAD cpu_to_be32(21)
+#define READ_PCR_RESULT_SIZE 30
+static struct tpm_input_header pcrread_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(14),
+       .ordinal = TPM_ORDINAL_PCRREAD
+};
 
-       kfree(data);
+int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf)
+{
+       int rc;
+       struct tpm_cmd_t cmd;
+
+       cmd.header.in = pcrread_header;
+       cmd.params.pcrread_in.pcr_idx = cpu_to_be32(pcr_idx);
+       BUILD_BUG_ON(cmd.header.in.length > READ_PCR_RESULT_SIZE);
+       rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+                         "attempting to read a pcr value");
+
+       if (rc == 0)
+               memcpy(res_buf, cmd.params.pcrread_out.pcr_result,
+                      TPM_DIGEST_SIZE);
        return rc;
 }
-EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated);
 
-static const u8 pcrread[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 14,            /* length */
-       0, 0, 0, 21,            /* TPM_ORD_PcrRead */
-       0, 0, 0, 0              /* PCR index */
+/**
+ * tpm_pcr_read - read a pcr value
+ * @chip_num:  tpm idx # or ANY
+ * @pcr_idx:   pcr idx to retrieve
+ * @res_buf:   TPM_PCR value
+ *             size of res_buf is 20 bytes (or NULL if you don't care)
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
+{
+       struct tpm_chip *chip;
+       int rc;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL)
+               return -ENODEV;
+       rc = __tpm_pcr_read(chip, pcr_idx, res_buf);
+       module_put(chip->dev->driver->owner);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_read);
+
+/**
+ * tpm_pcr_extend - extend pcr value with hash
+ * @chip_num:  tpm idx # or AN&
+ * @pcr_idx:   pcr idx to extend
+ * @hash:      hash value used to extend pcr value
+ *
+ * The TPM driver should be built-in, but for whatever reason it
+ * isn't, protect against the chip disappearing, by incrementing
+ * the module usage count.
+ */
+#define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
+#define EXTEND_PCR_SIZE 34
+static struct tpm_input_header pcrextend_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(34),
+       .ordinal = TPM_ORD_PCR_EXTEND
 };
 
+int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
+{
+       struct tpm_cmd_t cmd;
+       int rc;
+       struct tpm_chip *chip;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL)
+               return -ENODEV;
+
+       cmd.header.in = pcrextend_header;
+       BUILD_BUG_ON(be32_to_cpu(cmd.header.in.length) > EXTEND_PCR_SIZE);
+       cmd.params.pcrextend_in.pcr_idx = cpu_to_be32(pcr_idx);
+       memcpy(cmd.params.pcrextend_in.hash, hash, TPM_DIGEST_SIZE);
+       rc = transmit_cmd(chip, &cmd, cmd.header.in.length,
+                         "attempting extend a PCR value");
+
+       module_put(chip->dev->driver->owner);
+       return rc;
+}
+EXPORT_SYMBOL_GPL(tpm_pcr_extend);
+
 ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr,
                      char *buf)
 {
-       u8 *data;
+       cap_t cap;
+       u8 digest[TPM_DIGEST_SIZE];
        ssize_t rc;
        int i, j, num_pcrs;
-       __be32 index;
        char *str = buf;
-
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
 
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_PCR;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+       rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap,
                        "attempting to determine the number of PCRS");
-       if (rc) {
-               kfree(data);
+       if (rc)
                return 0;
-       }
 
-       num_pcrs = be32_to_cpu(*((__be32 *) (data + 14)));
+       num_pcrs = be32_to_cpu(cap.num_pcrs);
        for (i = 0; i < num_pcrs; i++) {
-               memcpy(data, pcrread, sizeof(pcrread));
-               index = cpu_to_be32(i);
-               memcpy(data + 10, &index, 4);
-               rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                               "attempting to read a PCR");
+               rc = __tpm_pcr_read(chip, i, digest);
                if (rc)
-                       goto out;
+                       break;
                str += sprintf(str, "PCR-%02d: ", i);
                for (j = 0; j < TPM_DIGEST_SIZE; j++)
-                       str += sprintf(str, "%02X ", *(data + 10 + j));
+                       str += sprintf(str, "%02X ", digest[j]);
                str += sprintf(str, "\n");
        }
-out:
-       kfree(data);
        return str - buf;
 }
 EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 
 #define  READ_PUBEK_RESULT_SIZE 314
-static const u8 readpubek[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 30,            /* length */
-       0, 0, 0, 124,           /* TPM_ORD_ReadPubek */
+#define TPM_ORD_READPUBEK cpu_to_be32(124)
+struct tpm_input_header tpm_readpubek_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(30),
+       .ordinal = TPM_ORD_READPUBEK
 };
 
 ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
                       char *buf)
 {
        u8 *data;
+       struct tpm_cmd_t tpm_cmd;
        ssize_t err;
        int i, rc;
        char *str = buf;
 
        struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
 
-       data = kzalloc(READ_PUBEK_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, readpubek, sizeof(readpubek));
-
-       err = transmit_cmd(chip, data, READ_PUBEK_RESULT_SIZE,
+       tpm_cmd.header.in = tpm_readpubek_header;
+       err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE,
                        "attempting to read the PUBEK");
        if (err)
                goto out;
@@ -812,7 +836,7 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
           256 byte modulus
           ignore checksum 20 bytes
         */
-
+       data = tpm_cmd.params.readpubek_out_buffer;
        str +=
            sprintf(str,
                    "Algorithm: %02X %02X %02X %02X\nEncscheme: %02X %02X\n"
@@ -832,65 +856,33 @@ ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr,
        }
 out:
        rc = str - buf;
-       kfree(data);
        return rc;
 }
 EXPORT_SYMBOL_GPL(tpm_show_pubek);
 
-#define CAP_VERSION_1_1 6
-#define CAP_VERSION_1_2 0x1A
-#define CAP_VERSION_IDX 13
-static const u8 cap_version[] = {
-       0, 193,                 /* TPM_TAG_RQU_COMMAND */
-       0, 0, 0, 18,            /* length */
-       0, 0, 0, 101,           /* TPM_ORD_GetCapability */
-       0, 0, 0, 0,
-       0, 0, 0, 0
-};
 
 ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr,
                      char *buf)
 {
-       u8 *data;
+       cap_t cap;
        ssize_t rc;
        char *str = buf;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
+       rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
                        "attempting to determine the manufacturer");
-       if (rc) {
-               kfree(data);
+       if (rc)
                return 0;
-       }
-
        str += sprintf(str, "Manufacturer: 0x%x\n",
-                      be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
+                      be32_to_cpu(cap.manufacturer_id));
 
-       memcpy(data, cap_version, sizeof(cap_version));
-       data[CAP_VERSION_IDX] = CAP_VERSION_1_1;
-       rc = transmit_cmd(chip, data, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the 1.1 version");
+       rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap,
+                       "attempting to determine the 1.1 version");
        if (rc)
-               goto out;
-
+               return 0;
        str += sprintf(str,
                       "TCG version: %d.%d\nFirmware version: %d.%d\n",
-                      (int) data[14], (int) data[15], (int) data[16],
-                      (int) data[17]);
-
-out:
-       kfree(data);
+                      cap.tpm_version.Major, cap.tpm_version.Minor,
+                      cap.tpm_version.revMajor, cap.tpm_version.revMinor);
        return str - buf;
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps);
@@ -898,51 +890,25 @@ EXPORT_SYMBOL_GPL(tpm_show_caps);
 ssize_t tpm_show_caps_1_2(struct device * dev,
                          struct device_attribute * attr, char *buf)
 {
-       u8 *data;
-       ssize_t len;
+       cap_t cap;
+       ssize_t rc;
        char *str = buf;
 
-       struct tpm_chip *chip = dev_get_drvdata(dev);
-       if (chip == NULL)
-               return -ENODEV;
-
-       data = kzalloc(TPM_INTERNAL_RESULT_SIZE, GFP_KERNEL);
-       if (!data)
-               return -ENOMEM;
-
-       memcpy(data, tpm_cap, sizeof(tpm_cap));
-       data[TPM_CAP_IDX] = TPM_CAP_PROP;
-       data[TPM_CAP_SUBCAP_IDX] = TPM_CAP_PROP_MANUFACTURER;
-
-       len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
-       if (len <= TPM_ERROR_SIZE) {
-               dev_dbg(chip->dev, "A TPM error (%d) occurred "
-                       "attempting to determine the manufacturer\n",
-                       be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
-               kfree(data);
+       rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap,
+                       "attempting to determine the manufacturer");
+       if (rc)
                return 0;
-       }
-
        str += sprintf(str, "Manufacturer: 0x%x\n",
-                      be32_to_cpu(*((__be32 *) (data + TPM_GET_CAP_RET_UINT32_1_IDX))));
-
-       memcpy(data, cap_version, sizeof(cap_version));
-       data[CAP_VERSION_IDX] = CAP_VERSION_1_2;
-
-       len = tpm_transmit(chip, data, TPM_INTERNAL_RESULT_SIZE);
-       if (len <= TPM_ERROR_SIZE) {
-               dev_err(chip->dev, "A TPM error (%d) occurred "
-                       "attempting to determine the 1.2 version\n",
-                       be32_to_cpu(*((__be32 *) (data + TPM_RET_CODE_IDX))));
-               goto out;
-       }
+                      be32_to_cpu(cap.manufacturer_id));
+       rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap,
+                        "attempting to determine the 1.2 version");
+       if (rc)
+               return 0;
        str += sprintf(str,
                       "TCG version: %d.%d\nFirmware version: %d.%d\n",
-                      (int) data[16], (int) data[17], (int) data[18],
-                      (int) data[19]);
-
-out:
-       kfree(data);
+                      cap.tpm_version_1_2.Major, cap.tpm_version_1_2.Minor,
+                      cap.tpm_version_1_2.revMajor,
+                      cap.tpm_version_1_2.revMinor);
        return str - buf;
 }
 EXPORT_SYMBOL_GPL(tpm_show_caps_1_2);
index 8e30df4..8e00b4d 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/miscdevice.h>
 #include <linux/platform_device.h>
 #include <linux/io.h>
+#include <linux/tpm.h>
 
 enum tpm_timeout {
        TPM_TIMEOUT = 5,        /* msecs */
@@ -123,6 +124,147 @@ static inline void tpm_write_index(int base, int index, int value)
        outb(index, base);
        outb(value & 0xFF, base+1);
 }
+struct tpm_input_header {
+       __be16  tag;
+       __be32  length;
+       __be32  ordinal;
+}__attribute__((packed));
+
+struct tpm_output_header {
+       __be16  tag;
+       __be32  length;
+       __be32  return_code;
+}__attribute__((packed));
+
+struct stclear_flags_t {
+       __be16  tag;
+       u8      deactivated;
+       u8      disableForceClear;
+       u8      physicalPresence;
+       u8      physicalPresenceLock;
+       u8      bGlobalLock;
+}__attribute__((packed));
+
+struct tpm_version_t {
+       u8      Major;
+       u8      Minor;
+       u8      revMajor;
+       u8      revMinor;
+}__attribute__((packed));
+
+struct tpm_version_1_2_t {
+       __be16  tag;
+       u8      Major;
+       u8      Minor;
+       u8      revMajor;
+       u8      revMinor;
+}__attribute__((packed));
+
+struct timeout_t {
+       __be32  a;
+       __be32  b;
+       __be32  c;
+       __be32  d;
+}__attribute__((packed));
+
+struct duration_t {
+       __be32  tpm_short;
+       __be32  tpm_medium;
+       __be32  tpm_long;
+}__attribute__((packed));
+
+struct permanent_flags_t {
+       __be16  tag;
+       u8      disable;
+       u8      ownership;
+       u8      deactivated;
+       u8      readPubek;
+       u8      disableOwnerClear;
+       u8      allowMaintenance;
+       u8      physicalPresenceLifetimeLock;
+       u8      physicalPresenceHWEnable;
+       u8      physicalPresenceCMDEnable;
+       u8      CEKPUsed;
+       u8      TPMpost;
+       u8      TPMpostLock;
+       u8      FIPS;
+       u8      operator;
+       u8      enableRevokeEK;
+       u8      nvLocked;
+       u8      readSRKPub;
+       u8      tpmEstablished;
+       u8      maintenanceDone;
+       u8      disableFullDALogicInfo;
+}__attribute__((packed));
+
+typedef union {
+       struct  permanent_flags_t perm_flags;
+       struct  stclear_flags_t stclear_flags;
+       bool    owned;
+       __be32  num_pcrs;
+       struct  tpm_version_t   tpm_version;
+       struct  tpm_version_1_2_t tpm_version_1_2;
+       __be32  manufacturer_id;
+       struct timeout_t  timeout;
+       struct duration_t duration;
+} cap_t;
+
+struct tpm_getcap_params_in {
+       __be32  cap;
+       __be32  subcap_size;
+       __be32  subcap;
+}__attribute__((packed));
+
+struct tpm_getcap_params_out {
+       __be32  cap_size;
+       cap_t   cap;
+}__attribute__((packed));
+
+struct tpm_readpubek_params_out {
+       u8      algorithm[4];
+       u8      encscheme[2];
+       u8      sigscheme[2];
+       u8      parameters[12]; /*assuming RSA*/
+       __be32  keysize;
+       u8      modulus[256];
+       u8      checksum[20];
+}__attribute__((packed));
+
+typedef union {
+       struct  tpm_input_header in;
+       struct  tpm_output_header out;
+} tpm_cmd_header;
+
+#define TPM_DIGEST_SIZE 20
+struct tpm_pcrread_out {
+       u8      pcr_result[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+struct tpm_pcrread_in {
+       __be32  pcr_idx;
+}__attribute__((packed));
+
+struct tpm_pcrextend_in {
+       __be32  pcr_idx;
+       u8      hash[TPM_DIGEST_SIZE];
+}__attribute__((packed));
+
+typedef union {
+       struct  tpm_getcap_params_out getcap_out;
+       struct  tpm_readpubek_params_out readpubek_out;
+       u8      readpubek_out_buffer[sizeof(struct tpm_readpubek_params_out)];
+       struct  tpm_getcap_params_in getcap_in;
+       struct  tpm_pcrread_in  pcrread_in;
+       struct  tpm_pcrread_out pcrread_out;
+       struct  tpm_pcrextend_in pcrextend_in;
+} tpm_cmd_params;
+
+struct tpm_cmd_t {
+       tpm_cmd_header  header;
+       tpm_cmd_params  params;
+}__attribute__((packed));
+
+ssize_t        tpm_getcap(struct device *, __be32, cap_t *, const char *);
 
 extern void tpm_get_timeouts(struct tpm_chip *);
 extern void tpm_gen_interrupt(struct tpm_chip *);
index 0dd60a0..febfd8e 100644 (file)
--- a/fs/exec.c
+++ b/fs/exec.c
@@ -45,6 +45,7 @@
 #include <linux/proc_fs.h>
 #include <linux/mount.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/tsacct_kern.h>
 #include <linux/cn_proc.h>
@@ -127,6 +128,9 @@ SYSCALL_DEFINE1(uselib, const char __user *, library)
                                 MAY_READ | MAY_EXEC | MAY_OPEN);
        if (error)
                goto exit;
+       error = ima_path_check(&nd.path, MAY_READ | MAY_EXEC | MAY_OPEN);
+       if (error)
+               goto exit;
 
        file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
        error = PTR_ERR(file);
@@ -674,6 +678,9 @@ struct file *open_exec(const char *name)
        err = inode_permission(nd.path.dentry->d_inode, MAY_EXEC | MAY_OPEN);
        if (err)
                goto out_path_put;
+       err = ima_path_check(&nd.path, MAY_EXEC | MAY_OPEN);
+       if (err)
+               goto out_path_put;
 
        file = nameidata_to_filp(&nd, O_RDONLY|O_LARGEFILE);
        if (IS_ERR(file))
@@ -1168,6 +1175,9 @@ int search_binary_handler(struct linux_binprm *bprm,struct pt_regs *regs)
        retval = security_bprm_check(bprm);
        if (retval)
                return retval;
+       retval = ima_bprm_check(bprm);
+       if (retval)
+               return retval;
 
        /* kernel module loader fixup */
        /* so we don't try to load run modprobe in kernel space. */
index bbeeac6..da806ac 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/fs.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/eventpoll.h>
 #include <linux/rcupdate.h>
 #include <linux/mount.h>
@@ -279,6 +280,7 @@ void __fput(struct file *file)
        if (file->f_op && file->f_op->release)
                file->f_op->release(inode, file);
        security_file_free(file);
+       ima_file_free(file);
        if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL))
                cdev_put(inode->i_cdev);
        fops_put(file->f_op);
index 913ab2d..40e37c0 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/hash.h>
 #include <linux/swap.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/pagemap.h>
 #include <linux/cdev.h>
 #include <linux/bootmem.h>
@@ -147,13 +148,13 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
        inode->i_cdev = NULL;
        inode->i_rdev = 0;
        inode->dirtied_when = 0;
-       if (security_inode_alloc(inode)) {
-               if (inode->i_sb->s_op->destroy_inode)
-                       inode->i_sb->s_op->destroy_inode(inode);
-               else
-                       kmem_cache_free(inode_cachep, (inode));
-               return NULL;
-       }
+
+       if (security_inode_alloc(inode))
+               goto out_free_inode;
+
+       /* allocate and initialize an i_integrity */
+       if (ima_inode_alloc(inode))
+               goto out_free_security;
 
        spin_lock_init(&inode->i_lock);
        lockdep_set_class(&inode->i_lock, &sb->s_type->i_lock_key);
@@ -189,6 +190,15 @@ struct inode *inode_init_always(struct super_block *sb, struct inode *inode)
        inode->i_mapping = mapping;
 
        return inode;
+
+out_free_security:
+       security_inode_free(inode);
+out_free_inode:
+       if (inode->i_sb->s_op->destroy_inode)
+               inode->i_sb->s_op->destroy_inode(inode);
+       else
+               kmem_cache_free(inode_cachep, (inode));
+       return NULL;
 }
 EXPORT_SYMBOL(inode_init_always);
 
index bbc15c2..1993176 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/fsnotify.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/syscalls.h>
 #include <linux/mount.h>
 #include <linux/audit.h>
@@ -850,6 +851,8 @@ static int __link_path_walk(const char *name, struct nameidata *nd)
                if (err == -EAGAIN)
                        err = inode_permission(nd->path.dentry->d_inode,
                                               MAY_EXEC);
+               if (!err)
+                       err = ima_path_check(&nd->path, MAY_EXEC);
                if (err)
                        break;
 
@@ -1509,6 +1512,11 @@ int may_open(struct path *path, int acc_mode, int flag)
        error = inode_permission(inode, acc_mode);
        if (error)
                return error;
+
+       error = ima_path_check(path,
+                              acc_mode & (MAY_READ | MAY_WRITE | MAY_EXEC));
+       if (error)
+               return error;
        /*
         * An append-only file must be opened in append mode for writing.
         */
index 67e5dbf..930939a 100644 (file)
 #define AUDIT_LAST_KERN_ANOM_MSG    1799
 #define AUDIT_ANOM_PROMISCUOUS      1700 /* Device changed promiscuous mode */
 #define AUDIT_ANOM_ABEND            1701 /* Process ended abnormally */
+#define AUDIT_INTEGRITY_DATA       1800 /* Data integrity verification */
+#define AUDIT_INTEGRITY_METADATA    1801 /* Metadata integrity verification */
+#define AUDIT_INTEGRITY_STATUS     1802 /* Integrity enable status */
+#define AUDIT_INTEGRITY_HASH       1803 /* Integrity HASH type */
+#define AUDIT_INTEGRITY_PCR        1804 /* PCR invalidation msgs */
 
 #define AUDIT_KERNEL           2000    /* Asynchronous audit record. NOT A REQUEST. */
 
diff --git a/include/linux/ima.h b/include/linux/ima.h
new file mode 100644 (file)
index 0000000..6db30a3
--- /dev/null
@@ -0,0 +1,60 @@
+/*
+ * 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.
+ */
+
+#include <linux/fs.h>
+
+#ifndef _LINUX_IMA_H
+#define _LINUX_IMA_H
+
+#ifdef CONFIG_IMA
+extern int ima_bprm_check(struct linux_binprm *bprm);
+extern int ima_inode_alloc(struct inode *inode);
+extern void ima_inode_free(struct inode *inode);
+extern int ima_path_check(struct path *path, int mask);
+extern void ima_file_free(struct file *file);
+extern int ima_file_mmap(struct file *file, unsigned long prot);
+extern void ima_shm_check(struct file *file);
+
+#else
+static inline int ima_bprm_check(struct linux_binprm *bprm)
+{
+       return 0;
+}
+
+static inline int ima_inode_alloc(struct inode *inode)
+{
+       return 0;
+}
+
+static inline void ima_inode_free(struct inode *inode)
+{
+       return;
+}
+
+static inline int ima_path_check(struct path *path, int mask)
+{
+       return 0;
+}
+
+static inline void ima_file_free(struct file *file)
+{
+       return;
+}
+
+static inline int ima_file_mmap(struct file *file, unsigned long prot)
+{
+       return 0;
+}
+
+static inline void ima_shm_check(struct file *file)
+{
+       return;
+}
+#endif /* CONFIG_IMA_H */
+#endif /* _LINUX_IMA_H */
diff --git a/include/linux/tpm.h b/include/linux/tpm.h
new file mode 100644 (file)
index 0000000..3338b3f
--- /dev/null
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2004,2007,2008 IBM Corporation
+ *
+ * Authors:
+ * Leendert van Doorn <leendert@watson.ibm.com>
+ * Dave Safford <safford@watson.ibm.com>
+ * Reiner Sailer <sailer@watson.ibm.com>
+ * Kylene Hall <kjhall@us.ibm.com>
+ * Debora Velarde <dvelarde@us.ibm.com>
+ *
+ * Maintained by: <tpmdd_devel@lists.sourceforge.net>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * 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.
+ *
+ */
+#ifndef __LINUX_TPM_H__
+#define __LINUX_TPM_H__
+
+/*
+ * Chip num is this value or a valid tpm idx
+ */
+#define        TPM_ANY_NUM 0xFFFF
+
+#if defined(CONFIG_TCG_TPM)
+
+extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
+extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
+#endif
+#endif
index f8f69fa..2bd4809 100644 (file)
--- a/ipc/shm.c
+++ b/ipc/shm.c
@@ -39,6 +39,7 @@
 #include <linux/nsproxy.h>
 #include <linux/mount.h>
 #include <linux/ipc_namespace.h>
+#include <linux/ima.h>
 
 #include <asm/uaccess.h>
 
@@ -381,6 +382,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
        error = PTR_ERR(file);
        if (IS_ERR(file))
                goto no_file;
+       ima_shm_check(file);
 
        id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
        if (id < 0) {
@@ -885,6 +887,7 @@ long do_shmat(int shmid, char __user *shmaddr, int shmflg, ulong *raddr)
        file = alloc_file(path.mnt, path.dentry, f_mode, &shm_file_operations);
        if (!file)
                goto out_free;
+       ima_shm_check(file);
 
        file->private_data = sfd;
        file->f_mapping = shp->shm_file->f_mapping;
index 214b6a2..3b3ed0b 100644 (file)
--- a/mm/mmap.c
+++ b/mm/mmap.c
@@ -20,6 +20,7 @@
 #include <linux/fs.h>
 #include <linux/personality.h>
 #include <linux/security.h>
+#include <linux/ima.h>
 #include <linux/hugetlb.h>
 #include <linux/profile.h>
 #include <linux/module.h>
@@ -1052,6 +1053,9 @@ unsigned long do_mmap_pgoff(struct file *file, unsigned long addr,
        error = security_file_mmap(file, reqprot, prot, flags, addr, 0);
        if (error)
                return error;
+       error = ima_file_mmap(file, prot);
+       if (error)
+               return error;
 
        return mmap_region(file, addr, len, flags, vm_flags, pgoff,
                           accountable);
index 19d566c..7519988 100644 (file)
@@ -59,6 +59,7 @@ static struct vfsmount *shm_mnt;
 #include <linux/highmem.h>
 #include <linux/seq_file.h>
 #include <linux/magic.h>
+#include <linux/ima.h>
 
 #include <asm/uaccess.h>
 #include <asm/div64.h>
@@ -2666,6 +2667,7 @@ int shmem_zero_setup(struct vm_area_struct *vma)
        if (IS_ERR(file))
                return PTR_ERR(file);
 
+       ima_shm_check(file);
        if (vma->vm_file)
                fput(vma->vm_file);
        vma->vm_file = file;
index 9438535..bf129f8 100644 (file)
@@ -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
 
index c05c127..595536c 100644 (file)
@@ -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
index 007ef25..f3b91bf 100644 (file)
@@ -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 (file)
index 0000000..3d2b6ee
--- /dev/null
@@ -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 (file)
index 0000000..787c4cb
--- /dev/null
@@ -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 (file)
index 0000000..e3c16a2
--- /dev/null
@@ -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 (file)
index 0000000..a148a25
--- /dev/null
@@ -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 (file)
index 0000000..8a0f1e2
--- /dev/null
@@ -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 (file)
index 0000000..c2a46e4
--- /dev/null
@@ -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 (file)
index 0000000..573780c
--- /dev/null
@@ -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 (file)
index 0000000..1f035e8
--- /dev/null
@@ -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 (file)
index 0000000..cf227db
--- /dev/null
@@ -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 (file)
index 0000000..f4e7266
--- /dev/null
@@ -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 (file)
index 0000000..23810e0
--- /dev/null
@@ -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 (file)
index 0000000..7ec9431
--- /dev/null
@@ -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;
+}
index 0081597..a69d6f8 100644 (file)
@@ -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,
index 3cc4516..c4e0623 100644 (file)
@@ -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;
index 7244737..e1d9db7 100644 (file)
 #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;