Merge tag 'v3.6-rc7' into next
authorJames Morris <james.l.morris@oracle.com>
Fri, 28 Sep 2012 03:37:32 +0000 (13:37 +1000)
committerJames Morris <james.l.morris@oracle.com>
Fri, 28 Sep 2012 03:37:32 +0000 (13:37 +1000)
Linux 3.6-rc7

Requested by David Howells so he can merge his key susbsystem work into
my tree with requisite -linus changesets.

53 files changed:
Documentation/ABI/testing/ima_policy
Documentation/ABI/testing/sysfs-driver-ppi [new file with mode: 0644]
Documentation/kernel-parameters.txt
Documentation/security/Smack.txt
arch/powerpc/kernel/prom_init.c
drivers/char/hw_random/Kconfig
drivers/char/hw_random/Makefile
drivers/char/hw_random/tpm-rng.c [new file with mode: 0644]
drivers/char/tpm/Kconfig
drivers/char/tpm/Makefile
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_acpi.c [new file with mode: 0644]
drivers/char/tpm/tpm_bios.c [deleted file]
drivers/char/tpm/tpm_eventlog.c [new file with mode: 0644]
drivers/char/tpm/tpm_eventlog.h [new file with mode: 0644]
drivers/char/tpm/tpm_i2c_infineon.c [new file with mode: 0644]
drivers/char/tpm/tpm_ibmvtpm.c [new file with mode: 0644]
drivers/char/tpm/tpm_ibmvtpm.h [new file with mode: 0644]
drivers/char/tpm/tpm_of.c [new file with mode: 0644]
drivers/char/tpm/tpm_ppi.c [new file with mode: 0644]
drivers/char/tpm/tpm_tis.c
fs/attr.c
fs/file_table.c
fs/xattr.c
include/linux/audit.h
include/linux/ima.h
include/linux/integrity.h
include/linux/ptrace.h
include/linux/security.h
include/linux/tpm.h
include/linux/xattr.h
kernel/auditsc.c
kernel/ptrace.c
samples/seccomp/Makefile
samples/seccomp/bpf-helper.h
security/integrity/evm/evm_main.c
security/integrity/iint.c
security/integrity/ima/Kconfig
security/integrity/ima/Makefile
security/integrity/ima/ima.h
security/integrity/ima/ima_api.c
security/integrity/ima/ima_appraise.c [new file with mode: 0644]
security/integrity/ima/ima_crypto.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/integrity.h
security/keys/trusted.c
security/security.c
security/smack/smack_lsm.c
security/smack/smackfs.c
security/yama/Kconfig
security/yama/yama_lsm.c

index 6cd6daefaaedeb160a6f1ac1d616de7871b38965..986946613542b4bc1852aaf671e62059d3295cea 100644 (file)
@@ -12,11 +12,14 @@ Description:
                then closing the file.  The new policy takes effect after
                the file ima/policy is closed.
 
+               IMA appraisal, if configured, uses these file measurements
+               for local measurement appraisal.
+
                rule format: action [condition ...]
 
-               action: measure | dont_measure
+               action: measure | dont_measure | appraise | dont_appraise | audit
                condition:= base | lsm
-                       base:   [[func=] [mask=] [fsmagic=] [uid=]]
+                       base:   [[func=] [mask=] [fsmagic=] [uid=] [fowner]]
                        lsm:    [[subj_user=] [subj_role=] [subj_type=]
                                 [obj_user=] [obj_role=] [obj_type=]]
 
@@ -24,36 +27,50 @@ Description:
                        mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
                        fsmagic:= hex value
                        uid:= decimal value
+                       fowner:=decimal value
                lsm:    are LSM specific
 
                default policy:
                        # PROC_SUPER_MAGIC
                        dont_measure fsmagic=0x9fa0
+                       dont_appraise fsmagic=0x9fa0
                        # SYSFS_MAGIC
                        dont_measure fsmagic=0x62656572
+                       dont_appraise fsmagic=0x62656572
                        # DEBUGFS_MAGIC
                        dont_measure fsmagic=0x64626720
+                       dont_appraise fsmagic=0x64626720
                        # TMPFS_MAGIC
                        dont_measure fsmagic=0x01021994
+                       dont_appraise fsmagic=0x01021994
+                       # RAMFS_MAGIC
+                       dont_measure fsmagic=0x858458f6
+                       dont_appraise fsmagic=0x858458f6
                        # SECURITYFS_MAGIC
                        dont_measure fsmagic=0x73636673
+                       dont_appraise fsmagic=0x73636673
 
                        measure func=BPRM_CHECK
                        measure func=FILE_MMAP mask=MAY_EXEC
                        measure func=FILE_CHECK mask=MAY_READ uid=0
+                       appraise fowner=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 do_filp_open.
+               open for read by root in do_filp_open.  The default appraisal
+               policy appraises all files owned by root.
 
                Examples of LSM specific definitions:
 
                SELinux:
                        # SELINUX_MAGIC
-                       dont_measure fsmagic=0xF97CFF8C
+                       dont_measure fsmagic=0xf97cff8c
+                       dont_appraise fsmagic=0xf97cff8c
 
                        dont_measure obj_type=var_log_t
+                       dont_appraise obj_type=var_log_t
                        dont_measure obj_type=auditd_log_t
+                       dont_appraise obj_type=auditd_log_t
                        measure subj_user=system_u func=FILE_CHECK mask=MAY_READ
                        measure subj_role=system_r func=FILE_CHECK mask=MAY_READ
 
diff --git a/Documentation/ABI/testing/sysfs-driver-ppi b/Documentation/ABI/testing/sysfs-driver-ppi
new file mode 100644 (file)
index 0000000..97a003e
--- /dev/null
@@ -0,0 +1,70 @@
+What:          /sys/devices/pnp0/<bus-num>/ppi/
+Date:          August 2012
+Kernel Version:        3.6
+Contact:       xiaoyan.zhang@intel.com
+Description:
+               This folder includes the attributes related with PPI (Physical
+               Presence Interface). Only if TPM is supported by BIOS, this
+               folder makes sence. The folder path can be got by command
+               'find /sys/ -name 'pcrs''. For the detail information of PPI,
+               please refer to the PPI specification from
+               http://www.trustedcomputinggroup.org/
+
+What:          /sys/devices/pnp0/<bus-num>/ppi/version
+Date:          August 2012
+Contact:       xiaoyan.zhang@intel.com
+Description:
+               This attribute shows the version of the PPI supported by the
+               platform.
+               This file is readonly.
+
+What:          /sys/devices/pnp0/<bus-num>/ppi/request
+Date:          August 2012
+Contact:       xiaoyan.zhang@intel.com
+Description:
+               This attribute shows the request for an operation to be
+               executed in the pre-OS environment. It is the only input from
+               the OS to the pre-OS environment. The request should be an
+               integer value range from 1 to 160, and 0 means no request.
+               This file can be read and written.
+
+What:          /sys/devices/pnp0/00:<bus-num>/ppi/response
+Date:          August 2012
+Contact:       xiaoyan.zhang@intel.com
+Description:
+               This attribute shows the response to the most recent operation
+               request it acted upon. The format is "<request> <response num>
+               : <response description>".
+               This file is readonly.
+
+What:          /sys/devices/pnp0/<bus-num>/ppi/transition_action
+Date:          August 2012
+Contact:       xiaoyan.zhang@intel.com
+Description:
+               This attribute shows the platform-specific action that should
+               take place in order to transition to the BIOS for execution of
+               a requested operation. The format is "<action num>: <action
+               description>".
+               This file is readonly.
+
+What:          /sys/devices/pnp0/<bus-num>/ppi/tcg_operations
+Date:          August 2012
+Contact:       xiaoyan.zhang@intel.com
+Description:
+               This attribute shows whether it is allowed to request an
+               operation to be executed in the pre-OS environment by the BIOS
+               for the requests defined by TCG, i.e. requests from 1 to 22.
+               The format is "<request> <status num>: <status description>".
+               This attribute is only supported by PPI version 1.2+.
+               This file is readonly.
+
+What:          /sys/devices/pnp0/<bus-num>/ppi/vs_operations
+Date:          August 2012
+Contact:       xiaoyan.zhang@intel.com
+Description:
+               This attribute shows whether it is allowed to request an
+               operation to be executed in the pre-OS environment by the BIOS
+               for the verdor specific requests, i.e. requests from 128 to
+               255. The format is same with tcg_operations. This attribute
+               is also only supported by PPI version 1.2+.
+               This file is readonly.
index ad7e2e5088c126ce48e0362d1f3296f3e8b55d2b..949dddcfd17784724c6c6ce59afd8cd697e998de 100644 (file)
@@ -1051,6 +1051,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
        ihash_entries=  [KNL]
                        Set number of hash buckets for inode cache.
 
+       ima_appraise=   [IMA] appraise integrity measurements
+                       Format: { "off" | "enforce" | "fix" }
+                       default: "enforce"
+
+       ima_appraise_tcb [IMA]
+                       The builtin appraise policy appraises all files
+                       owned by uid=0.
+
        ima_audit=      [IMA]
                        Format: { "0" | "1" }
                        0 -- integrity auditing messages. (Default)
index a416479b8a1c23d6755cd5354027159af832a4dc..8a177e4b6e21237e0e03f0410acb70d9282ce014 100644 (file)
@@ -28,12 +28,11 @@ Smack kernels use the CIPSO IP option. Some network
 configurations are intolerant of IP options and can impede
 access to systems that use them as Smack does.
 
-The current git repositories for Smack user space are:
+The current git repository for Smack user space is:
 
-       git@gitorious.org:meego-platform-security/smackutil.git
-       git@gitorious.org:meego-platform-security/libsmack.git
+       git://github.com/smack-team/smack.git
 
-These should make and install on most modern distributions.
+This should make and install on most modern distributions.
 There are three commands included in smackutil:
 
 smackload  - properly formats data for writing to /smack/load
@@ -194,6 +193,9 @@ onlycap
        these capabilities are effective at for processes with any
        label. The value is set by writing the desired label to the
        file or cleared by writing "-" to the file.
+revoke-subject
+       Writing a Smack label here sets the access to '-' for all access
+       rules with that subject label.
 
 You can add access rules in /etc/smack/accesses. They take the form:
 
index 0794a3017b1b53e65e4d1aa325b79711a9fb04b1..e144498bcddda0471c96ca52e412f476aba7dd07 100644 (file)
@@ -1623,6 +1623,63 @@ static void __init prom_instantiate_rtas(void)
 }
 
 #ifdef CONFIG_PPC64
+/*
+ * Allocate room for and instantiate Stored Measurement Log (SML)
+ */
+static void __init prom_instantiate_sml(void)
+{
+       phandle ibmvtpm_node;
+       ihandle ibmvtpm_inst;
+       u32 entry = 0, size = 0;
+       u64 base;
+
+       prom_debug("prom_instantiate_sml: start...\n");
+
+       ibmvtpm_node = call_prom("finddevice", 1, 1, ADDR("/ibm,vtpm"));
+       prom_debug("ibmvtpm_node: %x\n", ibmvtpm_node);
+       if (!PHANDLE_VALID(ibmvtpm_node))
+               return;
+
+       ibmvtpm_inst = call_prom("open", 1, 1, ADDR("/ibm,vtpm"));
+       if (!IHANDLE_VALID(ibmvtpm_inst)) {
+               prom_printf("opening vtpm package failed (%x)\n", ibmvtpm_inst);
+               return;
+       }
+
+       if (call_prom_ret("call-method", 2, 2, &size,
+                         ADDR("sml-get-handover-size"),
+                         ibmvtpm_inst) != 0 || size == 0) {
+               prom_printf("SML get handover size failed\n");
+               return;
+       }
+
+       base = alloc_down(size, PAGE_SIZE, 0);
+       if (base == 0)
+               prom_panic("Could not allocate memory for sml\n");
+
+       prom_printf("instantiating sml at 0x%x...", base);
+
+       if (call_prom_ret("call-method", 4, 2, &entry,
+                         ADDR("sml-handover"),
+                         ibmvtpm_inst, size, base) != 0 || entry == 0) {
+               prom_printf("SML handover failed\n");
+               return;
+       }
+       prom_printf(" done\n");
+
+       reserve_mem(base, size);
+
+       prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-base",
+                    &base, sizeof(base));
+       prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-size",
+                    &size, sizeof(size));
+
+       prom_debug("sml base     = 0x%x\n", base);
+       prom_debug("sml size     = 0x%x\n", (long)size);
+
+       prom_debug("prom_instantiate_sml: end...\n");
+}
+
 /*
  * Allocate room for and initialize TCE tables
  */
@@ -2916,6 +2973,11 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
                prom_instantiate_opal();
 #endif
 
+#ifdef CONFIG_PPC64
+       /* instantiate sml */
+       prom_instantiate_sml();
+#endif
+
        /*
         * On non-powermacs, put all CPUs in spin-loops.
         *
index 7c0d391996b5b4e82432b9b3418d427f7a42894a..fbd9b2b850ef1de0a84a57182ff79bdd38064896 100644 (file)
@@ -289,3 +289,16 @@ config HW_RANDOM_EXYNOS
          module will be called exynos-rng.
 
          If unsure, say Y.
+
+config HW_RANDOM_TPM
+       tristate "TPM HW Random Number Generator support"
+       depends on HW_RANDOM && TCG_TPM
+       default HW_RANDOM
+       ---help---
+         This driver provides kernel-side support for the Random Number
+         Generator in the Trusted Platform Module
+
+         To compile this driver as a module, choose M here: the
+         module will be called tpm-rng.
+
+         If unsure, say Y.
index 39a757ca15b65c59b188d0489b12488e4b4c6ac3..1fd7eec9fbf6421ace05971f95671095917bf4fa 100644 (file)
@@ -25,3 +25,4 @@ obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
 obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
 obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
 obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
+obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
diff --git a/drivers/char/hw_random/tpm-rng.c b/drivers/char/hw_random/tpm-rng.c
new file mode 100644 (file)
index 0000000..d6d4482
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2012 Kent Yoder IBM Corporation
+ *
+ * HWRNG interfaces to pull RNG data from a TPM
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/hw_random.h>
+#include <linux/tpm.h>
+
+#define MODULE_NAME "tpm-rng"
+
+static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
+{
+       return tpm_get_random(TPM_ANY_NUM, data, max);
+}
+
+static struct hwrng tpm_rng = {
+       .name = MODULE_NAME,
+       .read = tpm_rng_read,
+};
+
+static int __init rng_init(void)
+{
+       return hwrng_register(&tpm_rng);
+}
+module_init(rng_init);
+
+static void __exit rng_exit(void)
+{
+       hwrng_unregister(&tpm_rng);
+}
+module_exit(rng_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Kent Yoder <key@linux.vnet.ibm.com>");
+MODULE_DESCRIPTION("RNG driver for TPM devices");
index a048199ce866555f19b98fcd93ff10f3890c1eb3..915875e431d2151f48d9f793da67d0b2817d5e45 100644 (file)
@@ -33,6 +33,17 @@ config TCG_TIS
          from within Linux.  To compile this driver as a module, choose
          M here; the module will be called tpm_tis.
 
+config TCG_TIS_I2C_INFINEON
+       tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
+       depends on I2C
+       ---help---
+         If you have a TPM security chip that is compliant with the
+         TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack
+         Specification 0.20 say Yes and it will be accessible from within
+         Linux.
+         To compile this driver as a module, choose M here; the module
+         will be called tpm_tis_i2c_infineon.
+
 config TCG_NSC
        tristate "National Semiconductor TPM Interface"
        depends on X86
@@ -62,4 +73,12 @@ config TCG_INFINEON
          Further information on this driver and the supported hardware
          can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ 
 
+config TCG_IBMVTPM
+       tristate "IBM VTPM Interface"
+       depends on PPC64
+       ---help---
+         If you have IBM virtual TPM (VTPM) support say Yes and it
+         will be accessible from within Linux.  To compile this driver
+         as a module, choose M here; the module will be called tpm_ibmvtpm.
+
 endif # TCG_TPM
index ea3a1e02a824de2b1a80feaa5ebc0e0125162c1c..5b3fc8bc6c132fa76f1e6ec8723c9bf096670271 100644 (file)
@@ -4,8 +4,16 @@
 obj-$(CONFIG_TCG_TPM) += tpm.o
 ifdef CONFIG_ACPI
        obj-$(CONFIG_TCG_TPM) += tpm_bios.o
+       tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o
+else
+ifdef CONFIG_TCG_IBMVTPM
+       obj-$(CONFIG_TCG_TPM) += tpm_bios.o
+       tpm_bios-objs += tpm_eventlog.o tpm_of.o
+endif
 endif
 obj-$(CONFIG_TCG_TIS) += tpm_tis.o
+obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
 obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
 obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
 obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
+obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
index 817f0ee202b6861e87e146e2cc8864bc2de33ede..6724615a4fdd1827028a24fff16b91a5f29d16d7 100644 (file)
 #include <linux/freezer.h>
 
 #include "tpm.h"
-
-enum tpm_const {
-       TPM_MINOR = 224,        /* officially assigned */
-       TPM_BUFSIZE = 4096,
-       TPM_NUM_DEVICES = 256,
-};
+#include "tpm_eventlog.h"
 
 enum tpm_duration {
        TPM_SHORT = 0,
@@ -482,6 +477,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
 #define TPM_INTERNAL_RESULT_SIZE 200
 #define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
 #define TPM_ORD_GET_CAP cpu_to_be32(101)
+#define TPM_ORD_GET_RANDOM cpu_to_be32(70)
 
 static const struct tpm_input_header tpm_getcap_header = {
        .tag = TPM_TAG_RQU_COMMAND,
@@ -919,7 +915,7 @@ EXPORT_SYMBOL_GPL(tpm_show_pcrs);
 
 #define  READ_PUBEK_RESULT_SIZE 314
 #define TPM_ORD_READPUBEK cpu_to_be32(124)
-struct tpm_input_header tpm_readpubek_header = {
+static struct tpm_input_header tpm_readpubek_header = {
        .tag = TPM_TAG_RQU_COMMAND,
        .length = cpu_to_be32(30),
        .ordinal = TPM_ORD_READPUBEK
@@ -1175,7 +1171,7 @@ int tpm_release(struct inode *inode, struct file *file)
        flush_work_sync(&chip->work);
        file->private_data = NULL;
        atomic_set(&chip->data_pending, 0);
-       kfree(chip->data_buffer);
+       kzfree(chip->data_buffer);
        clear_bit(0, &chip->is_open);
        put_device(chip->dev);
        return 0;
@@ -1227,7 +1223,6 @@ ssize_t tpm_read(struct file *file, char __user *buf,
        del_singleshot_timer_sync(&chip->user_read_timer);
        flush_work_sync(&chip->work);
        ret_size = atomic_read(&chip->data_pending);
-       atomic_set(&chip->data_pending, 0);
        if (ret_size > 0) {     /* relay data */
                ssize_t orig_ret_size = ret_size;
                if (size < ret_size)
@@ -1242,6 +1237,8 @@ ssize_t tpm_read(struct file *file, char __user *buf,
                mutex_unlock(&chip->buffer_mutex);
        }
 
+       atomic_set(&chip->data_pending, 0);
+
        return ret_size;
 }
 EXPORT_SYMBOL_GPL(tpm_read);
@@ -1326,6 +1323,58 @@ int tpm_pm_resume(struct device *dev)
 }
 EXPORT_SYMBOL_GPL(tpm_pm_resume);
 
+#define TPM_GETRANDOM_RESULT_SIZE      18
+static struct tpm_input_header tpm_getrandom_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(14),
+       .ordinal = TPM_ORD_GET_RANDOM
+};
+
+/**
+ * tpm_get_random() - Get random bytes from the tpm's RNG
+ * @chip_num: A specific chip number for the request or TPM_ANY_NUM
+ * @out: destination buffer for the random bytes
+ * @max: the max number of bytes to write to @out
+ *
+ * Returns < 0 on error and the number of bytes read on success
+ */
+int tpm_get_random(u32 chip_num, u8 *out, size_t max)
+{
+       struct tpm_chip *chip;
+       struct tpm_cmd_t tpm_cmd;
+       u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
+       int err, total = 0, retries = 5;
+       u8 *dest = out;
+
+       chip = tpm_chip_find_get(chip_num);
+       if (chip == NULL)
+               return -ENODEV;
+
+       if (!out || !num_bytes || max > TPM_MAX_RNG_DATA)
+               return -EINVAL;
+
+       do {
+               tpm_cmd.header.in = tpm_getrandom_header;
+               tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
+
+               err = transmit_cmd(chip, &tpm_cmd,
+                                  TPM_GETRANDOM_RESULT_SIZE + num_bytes,
+                                  "attempting get random");
+               if (err)
+                       break;
+
+               recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
+               memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
+
+               dest += recd;
+               total += recd;
+               num_bytes -= recd;
+       } while (retries-- && total < max);
+
+       return total ? total : -EIO;
+}
+EXPORT_SYMBOL_GPL(tpm_get_random);
+
 /* In case vendor provided release function, call it too.*/
 
 void tpm_dev_vendor_release(struct tpm_chip *chip)
@@ -1346,7 +1395,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
  * Once all references to platform device are down to 0,
  * release all allocated structures.
  */
-void tpm_dev_release(struct device *dev)
+static void tpm_dev_release(struct device *dev)
 {
        struct tpm_chip *chip = dev_get_drvdata(dev);
 
@@ -1427,6 +1476,11 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
                goto put_device;
        }
 
+       if (sys_add_ppi(&dev->kobj)) {
+               misc_deregister(&chip->vendor.miscdev);
+               goto put_device;
+       }
+
        chip->bios_dir = tpm_bios_log_setup(devname);
 
        /* Make chip available */
index 917f727e674059b7e7daa2fc36a9920a306deef6..02c266aa2bf712657d3507071c1b266a4215e3c2 100644 (file)
 #include <linux/io.h>
 #include <linux/tpm.h>
 
+enum tpm_const {
+       TPM_MINOR = 224,        /* officially assigned */
+       TPM_BUFSIZE = 4096,
+       TPM_NUM_DEVICES = 256,
+};
+
 enum tpm_timeout {
        TPM_TIMEOUT = 5,        /* msecs */
 };
@@ -94,6 +100,7 @@ struct tpm_vendor_specific {
        bool timeout_adjusted;
        unsigned long duration[3]; /* jiffies */
        bool duration_adjusted;
+       void *data;
 
        wait_queue_head_t read_queue;
        wait_queue_head_t int_queue;
@@ -269,6 +276,21 @@ struct tpm_pcrextend_in {
        u8      hash[TPM_DIGEST_SIZE];
 }__attribute__((packed));
 
+/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
+ * bytes, but 128 is still a relatively large number of random bytes and
+ * anything much bigger causes users of struct tpm_cmd_t to start getting
+ * compiler warnings about stack frame size. */
+#define TPM_MAX_RNG_DATA       128
+
+struct tpm_getrandom_out {
+       __be32 rng_data_len;
+       u8     rng_data[TPM_MAX_RNG_DATA];
+}__attribute__((packed));
+
+struct tpm_getrandom_in {
+       __be32 num_bytes;
+}__attribute__((packed));
+
 typedef union {
        struct  tpm_getcap_params_out getcap_out;
        struct  tpm_readpubek_params_out readpubek_out;
@@ -277,6 +299,8 @@ typedef union {
        struct  tpm_pcrread_in  pcrread_in;
        struct  tpm_pcrread_out pcrread_out;
        struct  tpm_pcrextend_in pcrextend_in;
+       struct  tpm_getrandom_in getrandom_in;
+       struct  tpm_getrandom_out getrandom_out;
 } tpm_cmd_params;
 
 struct tpm_cmd_t {
@@ -303,15 +327,12 @@ extern int tpm_pm_suspend(struct device *);
 extern int tpm_pm_resume(struct device *);
 extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
                             wait_queue_head_t *);
+
 #ifdef CONFIG_ACPI
-extern struct dentry ** tpm_bios_log_setup(char *);
-extern void tpm_bios_log_teardown(struct dentry **);
+extern ssize_t sys_add_ppi(struct kobject *parent);
 #else
-static inline struct dentry ** tpm_bios_log_setup(char *name)
-{
-       return NULL;
-}
-static inline void tpm_bios_log_teardown(struct dentry **dir)
+static inline ssize_t sys_add_ppi(struct kobject *parent)
 {
+       return 0;
 }
 #endif
diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c
new file mode 100644 (file)
index 0000000..56051d0
--- /dev/null
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2005 IBM Corporation
+ *
+ * Authors:
+ *     Seiji Munetoh <munetoh@jp.ibm.com>
+ *     Stefan Berger <stefanb@us.ibm.com>
+ *     Reiner Sailer <sailer@watson.ibm.com>
+ *     Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Access to the eventlog extended by the TCG BIOS of PC platform
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/seq_file.h>
+#include <linux/fs.h>
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <acpi/acpi.h>
+
+#include "tpm.h"
+#include "tpm_eventlog.h"
+
+struct acpi_tcpa {
+       struct acpi_table_header hdr;
+       u16 platform_class;
+       union {
+               struct client_hdr {
+                       u32 log_max_len __attribute__ ((packed));
+                       u64 log_start_addr __attribute__ ((packed));
+               } client;
+               struct server_hdr {
+                       u16 reserved;
+                       u64 log_max_len __attribute__ ((packed));
+                       u64 log_start_addr __attribute__ ((packed));
+               } server;
+       };
+};
+
+/* read binary bios log */
+int read_log(struct tpm_bios_log *log)
+{
+       struct acpi_tcpa *buff;
+       acpi_status status;
+       void __iomem *virt;
+       u64 len, start;
+
+       if (log->bios_event_log != NULL) {
+               printk(KERN_ERR
+                      "%s: ERROR - Eventlog already initialized\n",
+                      __func__);
+               return -EFAULT;
+       }
+
+       /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
+       status = acpi_get_table(ACPI_SIG_TCPA, 1,
+                               (struct acpi_table_header **)&buff);
+
+       if (ACPI_FAILURE(status)) {
+               printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
+                      __func__);
+               return -EIO;
+       }
+
+       switch(buff->platform_class) {
+       case BIOS_SERVER:
+               len = buff->server.log_max_len;
+               start = buff->server.log_start_addr;
+               break;
+       case BIOS_CLIENT:
+       default:
+               len = buff->client.log_max_len;
+               start = buff->client.log_start_addr;
+               break;
+       }
+       if (!len) {
+               printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
+               return -EIO;
+       }
+
+       /* malloc EventLog space */
+       log->bios_event_log = kmalloc(len, GFP_KERNEL);
+       if (!log->bios_event_log) {
+               printk("%s: ERROR - Not enough  Memory for BIOS measurements\n",
+                       __func__);
+               return -ENOMEM;
+       }
+
+       log->bios_event_log_end = log->bios_event_log + len;
+
+       virt = acpi_os_map_memory(start, len);
+       if (!virt) {
+               kfree(log->bios_event_log);
+               printk("%s: ERROR - Unable to map memory\n", __func__);
+               return -EIO;
+       }
+
+       memcpy_fromio(log->bios_event_log, virt, len);
+
+       acpi_os_unmap_memory(virt, len);
+       return 0;
+}
diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_bios.c
deleted file mode 100644 (file)
index 0636520..0000000
+++ /dev/null
@@ -1,556 +0,0 @@
-/*
- * Copyright (C) 2005 IBM Corporation
- *
- * Authors:
- *     Seiji Munetoh <munetoh@jp.ibm.com>
- *     Stefan Berger <stefanb@us.ibm.com>
- *     Reiner Sailer <sailer@watson.ibm.com>
- *     Kylene Hall <kjhall@us.ibm.com>
- *
- * Maintained by: <tpmdd-devel@lists.sourceforge.net>
- *
- * Access to the eventlog extended by the TCG BIOS of PC platform
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version
- * 2 of the License, or (at your option) any later version.
- *
- */
-
-#include <linux/seq_file.h>
-#include <linux/fs.h>
-#include <linux/security.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <acpi/acpi.h>
-#include "tpm.h"
-
-#define TCG_EVENT_NAME_LEN_MAX 255
-#define MAX_TEXT_EVENT         1000    /* Max event string length */
-#define ACPI_TCPA_SIG          "TCPA"  /* 0x41504354 /'TCPA' */
-
-enum bios_platform_class {
-       BIOS_CLIENT = 0x00,
-       BIOS_SERVER = 0x01,
-};
-
-struct tpm_bios_log {
-       void *bios_event_log;
-       void *bios_event_log_end;
-};
-
-struct acpi_tcpa {
-       struct acpi_table_header hdr;
-       u16 platform_class;
-       union {
-               struct client_hdr {
-                       u32 log_max_len __attribute__ ((packed));
-                       u64 log_start_addr __attribute__ ((packed));
-               } client;
-               struct server_hdr {
-                       u16 reserved;
-                       u64 log_max_len __attribute__ ((packed));
-                       u64 log_start_addr __attribute__ ((packed));
-               } server;
-       };
-};
-
-struct tcpa_event {
-       u32 pcr_index;
-       u32 event_type;
-       u8 pcr_value[20];       /* SHA1 */
-       u32 event_size;
-       u8 event_data[0];
-};
-
-enum tcpa_event_types {
-       PREBOOT = 0,
-       POST_CODE,
-       UNUSED,
-       NO_ACTION,
-       SEPARATOR,
-       ACTION,
-       EVENT_TAG,
-       SCRTM_CONTENTS,
-       SCRTM_VERSION,
-       CPU_MICROCODE,
-       PLATFORM_CONFIG_FLAGS,
-       TABLE_OF_DEVICES,
-       COMPACT_HASH,
-       IPL,
-       IPL_PARTITION_DATA,
-       NONHOST_CODE,
-       NONHOST_CONFIG,
-       NONHOST_INFO,
-};
-
-static const char* tcpa_event_type_strings[] = {
-       "PREBOOT",
-       "POST CODE",
-       "",
-       "NO ACTION",
-       "SEPARATOR",
-       "ACTION",
-       "EVENT TAG",
-       "S-CRTM Contents",
-       "S-CRTM Version",
-       "CPU Microcode",
-       "Platform Config Flags",
-       "Table of Devices",
-       "Compact Hash",
-       "IPL",
-       "IPL Partition Data",
-       "Non-Host Code",
-       "Non-Host Config",
-       "Non-Host Info"
-};
-
-struct tcpa_pc_event {
-       u32 event_id;
-       u32 event_size;
-       u8 event_data[0];
-};
-
-enum tcpa_pc_event_ids {
-       SMBIOS = 1,
-       BIS_CERT,
-       POST_BIOS_ROM,
-       ESCD,
-       CMOS,
-       NVRAM,
-       OPTION_ROM_EXEC,
-       OPTION_ROM_CONFIG,
-       OPTION_ROM_MICROCODE = 10,
-       S_CRTM_VERSION,
-       S_CRTM_CONTENTS,
-       POST_CONTENTS,
-       HOST_TABLE_OF_DEVICES,
-};
-
-static const char* tcpa_pc_event_id_strings[] = {
-       "",
-       "SMBIOS",
-       "BIS Certificate",
-       "POST BIOS ",
-       "ESCD ",
-       "CMOS",
-       "NVRAM",
-       "Option ROM",
-       "Option ROM config",
-       "",
-       "Option ROM microcode ",
-       "S-CRTM Version",
-       "S-CRTM Contents ",
-       "POST Contents ",
-       "Table of Devices",
-};
-
-/* returns pointer to start of pos. entry of tcg log */
-static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos)
-{
-       loff_t i;
-       struct tpm_bios_log *log = m->private;
-       void *addr = log->bios_event_log;
-       void *limit = log->bios_event_log_end;
-       struct tcpa_event *event;
-
-       /* read over *pos measurements */
-       for (i = 0; i < *pos; i++) {
-               event = addr;
-
-               if ((addr + sizeof(struct tcpa_event)) < limit) {
-                       if (event->event_type == 0 && event->event_size == 0)
-                               return NULL;
-                       addr += sizeof(struct tcpa_event) + event->event_size;
-               }
-       }
-
-       /* now check if current entry is valid */
-       if ((addr + sizeof(struct tcpa_event)) >= limit)
-               return NULL;
-
-       event = addr;
-
-       if ((event->event_type == 0 && event->event_size == 0) ||
-           ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit))
-               return NULL;
-
-       return addr;
-}
-
-static void *tpm_bios_measurements_next(struct seq_file *m, void *v,
-                                       loff_t *pos)
-{
-       struct tcpa_event *event = v;
-       struct tpm_bios_log *log = m->private;
-       void *limit = log->bios_event_log_end;
-
-       v += sizeof(struct tcpa_event) + event->event_size;
-
-       /* now check if current entry is valid */
-       if ((v + sizeof(struct tcpa_event)) >= limit)
-               return NULL;
-
-       event = v;
-
-       if (event->event_type == 0 && event->event_size == 0)
-               return NULL;
-
-       if ((event->event_type == 0 && event->event_size == 0) ||
-           ((v + sizeof(struct tcpa_event) + event->event_size) >= limit))
-               return NULL;
-
-       (*pos)++;
-       return v;
-}
-
-static void tpm_bios_measurements_stop(struct seq_file *m, void *v)
-{
-}
-
-static int get_event_name(char *dest, struct tcpa_event *event,
-                       unsigned char * event_entry)
-{
-       const char *name = "";
-       /* 41 so there is room for 40 data and 1 nul */
-       char data[41] = "";
-       int i, n_len = 0, d_len = 0;
-       struct tcpa_pc_event *pc_event;
-
-       switch(event->event_type) {
-       case PREBOOT:
-       case POST_CODE:
-       case UNUSED:
-       case NO_ACTION:
-       case SCRTM_CONTENTS:
-       case SCRTM_VERSION:
-       case CPU_MICROCODE:
-       case PLATFORM_CONFIG_FLAGS:
-       case TABLE_OF_DEVICES:
-       case COMPACT_HASH:
-       case IPL:
-       case IPL_PARTITION_DATA:
-       case NONHOST_CODE:
-       case NONHOST_CONFIG:
-       case NONHOST_INFO:
-               name = tcpa_event_type_strings[event->event_type];
-               n_len = strlen(name);
-               break;
-       case SEPARATOR:
-       case ACTION:
-               if (MAX_TEXT_EVENT > event->event_size) {
-                       name = event_entry;
-                       n_len = event->event_size;
-               }
-               break;
-       case EVENT_TAG:
-               pc_event = (struct tcpa_pc_event *)event_entry;
-
-               /* ToDo Row data -> Base64 */
-
-               switch (pc_event->event_id) {
-               case SMBIOS:
-               case BIS_CERT:
-               case CMOS:
-               case NVRAM:
-               case OPTION_ROM_EXEC:
-               case OPTION_ROM_CONFIG:
-               case S_CRTM_VERSION:
-                       name = tcpa_pc_event_id_strings[pc_event->event_id];
-                       n_len = strlen(name);
-                       break;
-               /* hash data */
-               case POST_BIOS_ROM:
-               case ESCD:
-               case OPTION_ROM_MICROCODE:
-               case S_CRTM_CONTENTS:
-               case POST_CONTENTS:
-                       name = tcpa_pc_event_id_strings[pc_event->event_id];
-                       n_len = strlen(name);
-                       for (i = 0; i < 20; i++)
-                               d_len += sprintf(&data[2*i], "%02x",
-                                               pc_event->event_data[i]);
-                       break;
-               default:
-                       break;
-               }
-       default:
-               break;
-       }
-
-       return snprintf(dest, MAX_TEXT_EVENT, "[%.*s%.*s]",
-                       n_len, name, d_len, data);
-
-}
-
-static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v)
-{
-       struct tcpa_event *event = v;
-       char *data = v;
-       int i;
-
-       for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++)
-               seq_putc(m, data[i]);
-
-       return 0;
-}
-
-static int tpm_bios_measurements_release(struct inode *inode,
-                                        struct file *file)
-{
-       struct seq_file *seq = file->private_data;
-       struct tpm_bios_log *log = seq->private;
-
-       if (log) {
-               kfree(log->bios_event_log);
-               kfree(log);
-       }
-
-       return seq_release(inode, file);
-}
-
-static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v)
-{
-       int len = 0;
-       int i;
-       char *eventname;
-       struct tcpa_event *event = v;
-       unsigned char *event_entry =
-           (unsigned char *) (v + sizeof(struct tcpa_event));
-
-       eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL);
-       if (!eventname) {
-               printk(KERN_ERR "%s: ERROR - No Memory for event name\n ",
-                      __func__);
-               return -EFAULT;
-       }
-
-       seq_printf(m, "%2d ", event->pcr_index);
-
-       /* 2nd: SHA1 */
-       for (i = 0; i < 20; i++)
-               seq_printf(m, "%02x", event->pcr_value[i]);
-
-       /* 3rd: event type identifier */
-       seq_printf(m, " %02x", event->event_type);
-
-       len += get_event_name(eventname, event, event_entry);
-
-       /* 4th: eventname <= max + \'0' delimiter */
-       seq_printf(m, " %s\n", eventname);
-
-       kfree(eventname);
-       return 0;
-}
-
-static const struct seq_operations tpm_ascii_b_measurments_seqops = {
-       .start = tpm_bios_measurements_start,
-       .next = tpm_bios_measurements_next,
-       .stop = tpm_bios_measurements_stop,
-       .show = tpm_ascii_bios_measurements_show,
-};
-
-static const struct seq_operations tpm_binary_b_measurments_seqops = {
-       .start = tpm_bios_measurements_start,
-       .next = tpm_bios_measurements_next,
-       .stop = tpm_bios_measurements_stop,
-       .show = tpm_binary_bios_measurements_show,
-};
-
-/* read binary bios log */
-static int read_log(struct tpm_bios_log *log)
-{
-       struct acpi_tcpa *buff;
-       acpi_status status;
-       struct acpi_table_header *virt;
-       u64 len, start;
-
-       if (log->bios_event_log != NULL) {
-               printk(KERN_ERR
-                      "%s: ERROR - Eventlog already initialized\n",
-                      __func__);
-               return -EFAULT;
-       }
-
-       /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
-       status = acpi_get_table(ACPI_SIG_TCPA, 1,
-                               (struct acpi_table_header **)&buff);
-
-       if (ACPI_FAILURE(status)) {
-               printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
-                      __func__);
-               return -EIO;
-       }
-
-       switch(buff->platform_class) {
-       case BIOS_SERVER:
-               len = buff->server.log_max_len;
-               start = buff->server.log_start_addr;
-               break;
-       case BIOS_CLIENT:
-       default:
-               len = buff->client.log_max_len;
-               start = buff->client.log_start_addr;
-               break;
-       }
-       if (!len) {
-               printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
-               return -EIO;
-       }
-
-       /* malloc EventLog space */
-       log->bios_event_log = kmalloc(len, GFP_KERNEL);
-       if (!log->bios_event_log) {
-               printk("%s: ERROR - Not enough  Memory for BIOS measurements\n",
-                       __func__);
-               return -ENOMEM;
-       }
-
-       log->bios_event_log_end = log->bios_event_log + len;
-
-       virt = acpi_os_map_memory(start, len);
-
-       memcpy(log->bios_event_log, virt, len);
-
-       acpi_os_unmap_memory(virt, len);
-       return 0;
-}
-
-static int tpm_ascii_bios_measurements_open(struct inode *inode,
-                                           struct file *file)
-{
-       int err;
-       struct tpm_bios_log *log;
-       struct seq_file *seq;
-
-       log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL);
-       if (!log)
-               return -ENOMEM;
-
-       if ((err = read_log(log)))
-               goto out_free;
-
-       /* now register seq file */
-       err = seq_open(file, &tpm_ascii_b_measurments_seqops);
-       if (!err) {
-               seq = file->private_data;
-               seq->private = log;
-       } else {
-               goto out_free;
-       }
-
-out:
-       return err;
-out_free:
-       kfree(log->bios_event_log);
-       kfree(log);
-       goto out;
-}
-
-static const struct file_operations tpm_ascii_bios_measurements_ops = {
-       .open = tpm_ascii_bios_measurements_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = tpm_bios_measurements_release,
-};
-
-static int tpm_binary_bios_measurements_open(struct inode *inode,
-                                            struct file *file)
-{
-       int err;
-       struct tpm_bios_log *log;
-       struct seq_file *seq;
-
-       log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL);
-       if (!log)
-               return -ENOMEM;
-
-       if ((err = read_log(log)))
-               goto out_free;
-
-       /* now register seq file */
-       err = seq_open(file, &tpm_binary_b_measurments_seqops);
-       if (!err) {
-               seq = file->private_data;
-               seq->private = log;
-       } else {
-               goto out_free;
-       }
-
-out:
-       return err;
-out_free:
-       kfree(log->bios_event_log);
-       kfree(log);
-       goto out;
-}
-
-static const struct file_operations tpm_binary_bios_measurements_ops = {
-       .open = tpm_binary_bios_measurements_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = tpm_bios_measurements_release,
-};
-
-static int is_bad(void *p)
-{
-       if (!p)
-               return 1;
-       if (IS_ERR(p) && (PTR_ERR(p) != -ENODEV))
-               return 1;
-       return 0;
-}
-
-struct dentry **tpm_bios_log_setup(char *name)
-{
-       struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
-
-       tpm_dir = securityfs_create_dir(name, NULL);
-       if (is_bad(tpm_dir))
-               goto out;
-
-       bin_file =
-           securityfs_create_file("binary_bios_measurements",
-                                  S_IRUSR | S_IRGRP, tpm_dir, NULL,
-                                  &tpm_binary_bios_measurements_ops);
-       if (is_bad(bin_file))
-               goto out_tpm;
-
-       ascii_file =
-           securityfs_create_file("ascii_bios_measurements",
-                                  S_IRUSR | S_IRGRP, tpm_dir, NULL,
-                                  &tpm_ascii_bios_measurements_ops);
-       if (is_bad(ascii_file))
-               goto out_bin;
-
-       ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL);
-       if (!ret)
-               goto out_ascii;
-
-       ret[0] = ascii_file;
-       ret[1] = bin_file;
-       ret[2] = tpm_dir;
-
-       return ret;
-
-out_ascii:
-       securityfs_remove(ascii_file);
-out_bin:
-       securityfs_remove(bin_file);
-out_tpm:
-       securityfs_remove(tpm_dir);
-out:
-       return NULL;
-}
-EXPORT_SYMBOL_GPL(tpm_bios_log_setup);
-
-void tpm_bios_log_teardown(struct dentry **lst)
-{
-       int i;
-
-       for (i = 0; i < 3; i++)
-               securityfs_remove(lst[i]);
-}
-EXPORT_SYMBOL_GPL(tpm_bios_log_teardown);
-MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c
new file mode 100644 (file)
index 0000000..84ddc55
--- /dev/null
@@ -0,0 +1,419 @@
+/*
+ * Copyright (C) 2005, 2012 IBM Corporation
+ *
+ * Authors:
+ *     Kent Yoder <key@linux.vnet.ibm.com>
+ *     Seiji Munetoh <munetoh@jp.ibm.com>
+ *     Stefan Berger <stefanb@us.ibm.com>
+ *     Reiner Sailer <sailer@watson.ibm.com>
+ *     Kylene Hall <kjhall@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Access to the eventlog created by a system's firmware / BIOS
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/seq_file.h>
+#include <linux/fs.h>
+#include <linux/security.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include "tpm.h"
+#include "tpm_eventlog.h"
+
+
+static const char* tcpa_event_type_strings[] = {
+       "PREBOOT",
+       "POST CODE",
+       "",
+       "NO ACTION",
+       "SEPARATOR",
+       "ACTION",
+       "EVENT TAG",
+       "S-CRTM Contents",
+       "S-CRTM Version",
+       "CPU Microcode",
+       "Platform Config Flags",
+       "Table of Devices",
+       "Compact Hash",
+       "IPL",
+       "IPL Partition Data",
+       "Non-Host Code",
+       "Non-Host Config",
+       "Non-Host Info"
+};
+
+static const char* tcpa_pc_event_id_strings[] = {
+       "",
+       "SMBIOS",
+       "BIS Certificate",
+       "POST BIOS ",
+       "ESCD ",
+       "CMOS",
+       "NVRAM",
+       "Option ROM",
+       "Option ROM config",
+       "",
+       "Option ROM microcode ",
+       "S-CRTM Version",
+       "S-CRTM Contents ",
+       "POST Contents ",
+       "Table of Devices",
+};
+
+/* returns pointer to start of pos. entry of tcg log */
+static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos)
+{
+       loff_t i;
+       struct tpm_bios_log *log = m->private;
+       void *addr = log->bios_event_log;
+       void *limit = log->bios_event_log_end;
+       struct tcpa_event *event;
+
+       /* read over *pos measurements */
+       for (i = 0; i < *pos; i++) {
+               event = addr;
+
+               if ((addr + sizeof(struct tcpa_event)) < limit) {
+                       if (event->event_type == 0 && event->event_size == 0)
+                               return NULL;
+                       addr += sizeof(struct tcpa_event) + event->event_size;
+               }
+       }
+
+       /* now check if current entry is valid */
+       if ((addr + sizeof(struct tcpa_event)) >= limit)
+               return NULL;
+
+       event = addr;
+
+       if ((event->event_type == 0 && event->event_size == 0) ||
+           ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit))
+               return NULL;
+
+       return addr;
+}
+
+static void *tpm_bios_measurements_next(struct seq_file *m, void *v,
+                                       loff_t *pos)
+{
+       struct tcpa_event *event = v;
+       struct tpm_bios_log *log = m->private;
+       void *limit = log->bios_event_log_end;
+
+       v += sizeof(struct tcpa_event) + event->event_size;
+
+       /* now check if current entry is valid */
+       if ((v + sizeof(struct tcpa_event)) >= limit)
+               return NULL;
+
+       event = v;
+
+       if (event->event_type == 0 && event->event_size == 0)
+               return NULL;
+
+       if ((event->event_type == 0 && event->event_size == 0) ||
+           ((v + sizeof(struct tcpa_event) + event->event_size) >= limit))
+               return NULL;
+
+       (*pos)++;
+       return v;
+}
+
+static void tpm_bios_measurements_stop(struct seq_file *m, void *v)
+{
+}
+
+static int get_event_name(char *dest, struct tcpa_event *event,
+                       unsigned char * event_entry)
+{
+       const char *name = "";
+       /* 41 so there is room for 40 data and 1 nul */
+       char data[41] = "";
+       int i, n_len = 0, d_len = 0;
+       struct tcpa_pc_event *pc_event;
+
+       switch(event->event_type) {
+       case PREBOOT:
+       case POST_CODE:
+       case UNUSED:
+       case NO_ACTION:
+       case SCRTM_CONTENTS:
+       case SCRTM_VERSION:
+       case CPU_MICROCODE:
+       case PLATFORM_CONFIG_FLAGS:
+       case TABLE_OF_DEVICES:
+       case COMPACT_HASH:
+       case IPL:
+       case IPL_PARTITION_DATA:
+       case NONHOST_CODE:
+       case NONHOST_CONFIG:
+       case NONHOST_INFO:
+               name = tcpa_event_type_strings[event->event_type];
+               n_len = strlen(name);
+               break;
+       case SEPARATOR:
+       case ACTION:
+               if (MAX_TEXT_EVENT > event->event_size) {
+                       name = event_entry;
+                       n_len = event->event_size;
+               }
+               break;
+       case EVENT_TAG:
+               pc_event = (struct tcpa_pc_event *)event_entry;
+
+               /* ToDo Row data -> Base64 */
+
+               switch (pc_event->event_id) {
+               case SMBIOS:
+               case BIS_CERT:
+               case CMOS:
+               case NVRAM:
+               case OPTION_ROM_EXEC:
+               case OPTION_ROM_CONFIG:
+               case S_CRTM_VERSION:
+                       name = tcpa_pc_event_id_strings[pc_event->event_id];
+                       n_len = strlen(name);
+                       break;
+               /* hash data */
+               case POST_BIOS_ROM:
+               case ESCD:
+               case OPTION_ROM_MICROCODE:
+               case S_CRTM_CONTENTS:
+               case POST_CONTENTS:
+                       name = tcpa_pc_event_id_strings[pc_event->event_id];
+                       n_len = strlen(name);
+                       for (i = 0; i < 20; i++)
+                               d_len += sprintf(&data[2*i], "%02x",
+                                               pc_event->event_data[i]);
+                       break;
+               default:
+                       break;
+               }
+       default:
+               break;
+       }
+
+       return snprintf(dest, MAX_TEXT_EVENT, "[%.*s%.*s]",
+                       n_len, name, d_len, data);
+
+}
+
+static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v)
+{
+       struct tcpa_event *event = v;
+       char *data = v;
+       int i;
+
+       for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++)
+               seq_putc(m, data[i]);
+
+       return 0;
+}
+
+static int tpm_bios_measurements_release(struct inode *inode,
+                                        struct file *file)
+{
+       struct seq_file *seq = file->private_data;
+       struct tpm_bios_log *log = seq->private;
+
+       if (log) {
+               kfree(log->bios_event_log);
+               kfree(log);
+       }
+
+       return seq_release(inode, file);
+}
+
+static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v)
+{
+       int len = 0;
+       int i;
+       char *eventname;
+       struct tcpa_event *event = v;
+       unsigned char *event_entry =
+           (unsigned char *) (v + sizeof(struct tcpa_event));
+
+       eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL);
+       if (!eventname) {
+               printk(KERN_ERR "%s: ERROR - No Memory for event name\n ",
+                      __func__);
+               return -EFAULT;
+       }
+
+       seq_printf(m, "%2d ", event->pcr_index);
+
+       /* 2nd: SHA1 */
+       for (i = 0; i < 20; i++)
+               seq_printf(m, "%02x", event->pcr_value[i]);
+
+       /* 3rd: event type identifier */
+       seq_printf(m, " %02x", event->event_type);
+
+       len += get_event_name(eventname, event, event_entry);
+
+       /* 4th: eventname <= max + \'0' delimiter */
+       seq_printf(m, " %s\n", eventname);
+
+       kfree(eventname);
+       return 0;
+}
+
+static const struct seq_operations tpm_ascii_b_measurments_seqops = {
+       .start = tpm_bios_measurements_start,
+       .next = tpm_bios_measurements_next,
+       .stop = tpm_bios_measurements_stop,
+       .show = tpm_ascii_bios_measurements_show,
+};
+
+static const struct seq_operations tpm_binary_b_measurments_seqops = {
+       .start = tpm_bios_measurements_start,
+       .next = tpm_bios_measurements_next,
+       .stop = tpm_bios_measurements_stop,
+       .show = tpm_binary_bios_measurements_show,
+};
+
+static int tpm_ascii_bios_measurements_open(struct inode *inode,
+                                           struct file *file)
+{
+       int err;
+       struct tpm_bios_log *log;
+       struct seq_file *seq;
+
+       log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL);
+       if (!log)
+               return -ENOMEM;
+
+       if ((err = read_log(log)))
+               goto out_free;
+
+       /* now register seq file */
+       err = seq_open(file, &tpm_ascii_b_measurments_seqops);
+       if (!err) {
+               seq = file->private_data;
+               seq->private = log;
+       } else {
+               goto out_free;
+       }
+
+out:
+       return err;
+out_free:
+       kfree(log->bios_event_log);
+       kfree(log);
+       goto out;
+}
+
+static const struct file_operations tpm_ascii_bios_measurements_ops = {
+       .open = tpm_ascii_bios_measurements_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = tpm_bios_measurements_release,
+};
+
+static int tpm_binary_bios_measurements_open(struct inode *inode,
+                                            struct file *file)
+{
+       int err;
+       struct tpm_bios_log *log;
+       struct seq_file *seq;
+
+       log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL);
+       if (!log)
+               return -ENOMEM;
+
+       if ((err = read_log(log)))
+               goto out_free;
+
+       /* now register seq file */
+       err = seq_open(file, &tpm_binary_b_measurments_seqops);
+       if (!err) {
+               seq = file->private_data;
+               seq->private = log;
+       } else {
+               goto out_free;
+       }
+
+out:
+       return err;
+out_free:
+       kfree(log->bios_event_log);
+       kfree(log);
+       goto out;
+}
+
+static const struct file_operations tpm_binary_bios_measurements_ops = {
+       .open = tpm_binary_bios_measurements_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = tpm_bios_measurements_release,
+};
+
+static int is_bad(void *p)
+{
+       if (!p)
+               return 1;
+       if (IS_ERR(p) && (PTR_ERR(p) != -ENODEV))
+               return 1;
+       return 0;
+}
+
+struct dentry **tpm_bios_log_setup(char *name)
+{
+       struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
+
+       tpm_dir = securityfs_create_dir(name, NULL);
+       if (is_bad(tpm_dir))
+               goto out;
+
+       bin_file =
+           securityfs_create_file("binary_bios_measurements",
+                                  S_IRUSR | S_IRGRP, tpm_dir, NULL,
+                                  &tpm_binary_bios_measurements_ops);
+       if (is_bad(bin_file))
+               goto out_tpm;
+
+       ascii_file =
+           securityfs_create_file("ascii_bios_measurements",
+                                  S_IRUSR | S_IRGRP, tpm_dir, NULL,
+                                  &tpm_ascii_bios_measurements_ops);
+       if (is_bad(ascii_file))
+               goto out_bin;
+
+       ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL);
+       if (!ret)
+               goto out_ascii;
+
+       ret[0] = ascii_file;
+       ret[1] = bin_file;
+       ret[2] = tpm_dir;
+
+       return ret;
+
+out_ascii:
+       securityfs_remove(ascii_file);
+out_bin:
+       securityfs_remove(bin_file);
+out_tpm:
+       securityfs_remove(tpm_dir);
+out:
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(tpm_bios_log_setup);
+
+void tpm_bios_log_teardown(struct dentry **lst)
+{
+       int i;
+
+       for (i = 0; i < 3; i++)
+               securityfs_remove(lst[i]);
+}
+EXPORT_SYMBOL_GPL(tpm_bios_log_teardown);
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h
new file mode 100644 (file)
index 0000000..e7da086
--- /dev/null
@@ -0,0 +1,86 @@
+
+#ifndef __TPM_EVENTLOG_H__
+#define __TPM_EVENTLOG_H__
+
+#define TCG_EVENT_NAME_LEN_MAX 255
+#define MAX_TEXT_EVENT         1000    /* Max event string length */
+#define ACPI_TCPA_SIG          "TCPA"  /* 0x41504354 /'TCPA' */
+
+enum bios_platform_class {
+       BIOS_CLIENT = 0x00,
+       BIOS_SERVER = 0x01,
+};
+
+struct tpm_bios_log {
+       void *bios_event_log;
+       void *bios_event_log_end;
+};
+
+struct tcpa_event {
+       u32 pcr_index;
+       u32 event_type;
+       u8 pcr_value[20];       /* SHA1 */
+       u32 event_size;
+       u8 event_data[0];
+};
+
+enum tcpa_event_types {
+       PREBOOT = 0,
+       POST_CODE,
+       UNUSED,
+       NO_ACTION,
+       SEPARATOR,
+       ACTION,
+       EVENT_TAG,
+       SCRTM_CONTENTS,
+       SCRTM_VERSION,
+       CPU_MICROCODE,
+       PLATFORM_CONFIG_FLAGS,
+       TABLE_OF_DEVICES,
+       COMPACT_HASH,
+       IPL,
+       IPL_PARTITION_DATA,
+       NONHOST_CODE,
+       NONHOST_CONFIG,
+       NONHOST_INFO,
+};
+
+struct tcpa_pc_event {
+       u32 event_id;
+       u32 event_size;
+       u8 event_data[0];
+};
+
+enum tcpa_pc_event_ids {
+       SMBIOS = 1,
+       BIS_CERT,
+       POST_BIOS_ROM,
+       ESCD,
+       CMOS,
+       NVRAM,
+       OPTION_ROM_EXEC,
+       OPTION_ROM_CONFIG,
+       OPTION_ROM_MICROCODE = 10,
+       S_CRTM_VERSION,
+       S_CRTM_CONTENTS,
+       POST_CONTENTS,
+       HOST_TABLE_OF_DEVICES,
+};
+
+int read_log(struct tpm_bios_log *log);
+
+#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
+       defined(CONFIG_ACPI)
+extern struct dentry **tpm_bios_log_setup(char *);
+extern void tpm_bios_log_teardown(struct dentry **);
+#else
+static inline struct dentry **tpm_bios_log_setup(char *name)
+{
+       return NULL;
+}
+static inline void tpm_bios_log_teardown(struct dentry **dir)
+{
+}
+#endif
+
+#endif
diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c
new file mode 100644 (file)
index 0000000..5a831ae
--- /dev/null
@@ -0,0 +1,695 @@
+/*
+ * Copyright (C) 2012 Infineon Technologies
+ *
+ * Authors:
+ * Peter Huewe <peter.huewe@infineon.com>
+ *
+ * Device driver for TCG/TCPA TPM (trusted platform module).
+ * Specifications at www.trustedcomputinggroup.org
+ *
+ * This device driver implements the TPM interface as defined in
+ * the TCG TPM Interface Spec version 1.2, revision 1.0 and the
+ * Infineon I2C Protocol Stack Specification v0.20.
+ *
+ * It is based on the original tpm_tis device driver from Leendert van
+ * Dorn and Kyleen Hall.
+ *
+ * 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/init.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/wait.h>
+#include "tpm.h"
+
+/* max. buffer size supported by our TPM */
+#define TPM_BUFSIZE 1260
+
+/* max. number of iterations after I2C NAK */
+#define MAX_COUNT 3
+
+#define SLEEP_DURATION_LOW 55
+#define SLEEP_DURATION_HI 65
+
+/* max. number of iterations after I2C NAK for 'long' commands
+ * we need this especially for sending TPM_READY, since the cleanup after the
+ * transtion to the ready state may take some time, but it is unpredictable
+ * how long it will take.
+ */
+#define MAX_COUNT_LONG 50
+
+#define SLEEP_DURATION_LONG_LOW 200
+#define SLEEP_DURATION_LONG_HI 220
+
+/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */
+#define SLEEP_DURATION_RESET_LOW 2400
+#define SLEEP_DURATION_RESET_HI 2600
+
+/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */
+#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000)
+#define TPM_TIMEOUT_US_HI  (TPM_TIMEOUT_US_LOW + 2000)
+
+/* expected value for DIDVID register */
+#define TPM_TIS_I2C_DID_VID 0x000b15d1L
+
+/* Structure to store I2C TPM specific stuff */
+struct tpm_inf_dev {
+       struct i2c_client *client;
+       u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
+       struct tpm_chip *chip;
+};
+
+static struct tpm_inf_dev tpm_dev;
+static struct i2c_driver tpm_tis_i2c_driver;
+
+/*
+ * iic_tpm_read() - read from TPM register
+ * @addr: register address to read from
+ * @buffer: provided by caller
+ * @len: number of bytes to read
+ *
+ * Read len bytes from TPM register and put them into
+ * buffer (little-endian format, i.e. first byte is put into buffer[0]).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * NOTE: We can't unfortunately use the combined read/write functions
+ * provided by the i2c core as the TPM currently does not support the
+ * repeated start condition and due to it's special requirements.
+ * The i2c_smbus* functions do not work for this chip.
+ *
+ * Return -EIO on error, 0 on success.
+ */
+static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
+{
+
+       struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr };
+       struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer };
+
+       int rc;
+       int count;
+
+       /* Lock the adapter for the duration of the whole sequence. */
+       if (!tpm_dev.client->adapter->algo->master_xfer)
+               return -EOPNOTSUPP;
+       i2c_lock_adapter(tpm_dev.client->adapter);
+
+       for (count = 0; count < MAX_COUNT; count++) {
+               rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
+               if (rc > 0)
+                       break;  /* break here to skip sleep */
+
+               usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+       }
+
+       if (rc <= 0)
+               goto out;
+
+       /* After the TPM has successfully received the register address it needs
+        * some time, thus we're sleeping here again, before retrieving the data
+        */
+       for (count = 0; count < MAX_COUNT; count++) {
+               usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
+               rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1);
+               if (rc > 0)
+                       break;
+
+       }
+
+out:
+       i2c_unlock_adapter(tpm_dev.client->adapter);
+       if (rc <= 0)
+               return -EIO;
+
+       return 0;
+}
+
+static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
+                                unsigned int sleep_low,
+                                unsigned int sleep_hi, u8 max_count)
+{
+       int rc = -EIO;
+       int count;
+
+       struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf };
+
+       if (len > TPM_BUFSIZE)
+               return -EINVAL;
+
+       if (!tpm_dev.client->adapter->algo->master_xfer)
+               return -EOPNOTSUPP;
+       i2c_lock_adapter(tpm_dev.client->adapter);
+
+       /* prepend the 'register address' to the buffer */
+       tpm_dev.buf[0] = addr;
+       memcpy(&(tpm_dev.buf[1]), buffer, len);
+
+       /*
+        * NOTE: We have to use these special mechanisms here and unfortunately
+        * cannot rely on the standard behavior of i2c_transfer.
+        */
+       for (count = 0; count < max_count; count++) {
+               rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
+               if (rc > 0)
+                       break;
+
+               usleep_range(sleep_low, sleep_hi);
+       }
+
+       i2c_unlock_adapter(tpm_dev.client->adapter);
+       if (rc <= 0)
+               return -EIO;
+
+       return 0;
+}
+
+/*
+ * iic_tpm_write() - write to TPM register
+ * @addr: register address to write to
+ * @buffer: containing data to be written
+ * @len: number of bytes to write
+ *
+ * Write len bytes from provided buffer to TPM register (little
+ * endian format, i.e. buffer[0] is written as first byte).
+ *
+ * NOTE: TPM is big-endian for multi-byte values. Multi-byte
+ * values have to be swapped.
+ *
+ * NOTE: use this function instead of the iic_tpm_write_generic function.
+ *
+ * Return -EIO on error, 0 on success
+ */
+static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
+{
+       return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW,
+                                    SLEEP_DURATION_HI, MAX_COUNT);
+}
+
+/*
+ * This function is needed especially for the cleanup situation after
+ * sending TPM_READY
+ * */
+static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
+{
+       return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW,
+                                    SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG);
+}
+
+enum tis_access {
+       TPM_ACCESS_VALID = 0x80,
+       TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+       TPM_ACCESS_REQUEST_PENDING = 0x04,
+       TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum tis_status {
+       TPM_STS_VALID = 0x80,
+       TPM_STS_COMMAND_READY = 0x40,
+       TPM_STS_GO = 0x20,
+       TPM_STS_DATA_AVAIL = 0x10,
+       TPM_STS_DATA_EXPECT = 0x08,
+};
+
+enum tis_defaults {
+       TIS_SHORT_TIMEOUT = 750,        /* ms */
+       TIS_LONG_TIMEOUT = 2000,        /* 2 sec */
+};
+
+#define        TPM_ACCESS(l)                   (0x0000 | ((l) << 4))
+#define        TPM_STS(l)                      (0x0001 | ((l) << 4))
+#define        TPM_DATA_FIFO(l)                (0x0005 | ((l) << 4))
+#define        TPM_DID_VID(l)                  (0x0006 | ((l) << 4))
+
+static int check_locality(struct tpm_chip *chip, int loc)
+{
+       u8 buf;
+       int rc;
+
+       rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
+       if (rc < 0)
+               return rc;
+
+       if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+           (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
+               chip->vendor.locality = loc;
+               return loc;
+       }
+
+       return -EIO;
+}
+
+/* implementation similar to tpm_tis */
+static void release_locality(struct tpm_chip *chip, int loc, int force)
+{
+       u8 buf;
+       if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
+               return;
+
+       if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
+           (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
+               buf = TPM_ACCESS_ACTIVE_LOCALITY;
+               iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+       }
+}
+
+static int request_locality(struct tpm_chip *chip, int loc)
+{
+       unsigned long stop;
+       u8 buf = TPM_ACCESS_REQUEST_USE;
+
+       if (check_locality(chip, loc) >= 0)
+               return loc;
+
+       iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
+
+       /* wait for burstcount */
+       stop = jiffies + chip->vendor.timeout_a;
+       do {
+               if (check_locality(chip, loc) >= 0)
+                       return loc;
+               usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
+       } while (time_before(jiffies, stop));
+
+       return -ETIME;
+}
+
+static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
+{
+       /* NOTE: since I2C read may fail, return 0 in this case --> time-out */
+       u8 buf;
+       if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
+               return 0;
+       else
+               return buf;
+}
+
+static void tpm_tis_i2c_ready(struct tpm_chip *chip)
+{
+       /* this causes the current command to be aborted */
+       u8 buf = TPM_STS_COMMAND_READY;
+       iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
+}
+
+static ssize_t get_burstcount(struct tpm_chip *chip)
+{
+       unsigned long stop;
+       ssize_t burstcnt;
+       u8 buf[3];
+
+       /* wait for burstcount */
+       /* which timeout value, spec has 2 answers (c & d) */
+       stop = jiffies + chip->vendor.timeout_d;
+       do {
+               /* Note: STS is little endian */
+               if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0)
+                       burstcnt = 0;
+               else
+                       burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
+
+               if (burstcnt)
+                       return burstcnt;
+
+               usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
+       } while (time_before(jiffies, stop));
+       return -EBUSY;
+}
+
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
+                        int *status)
+{
+       unsigned long stop;
+
+       /* check current status */
+       *status = tpm_tis_i2c_status(chip);
+       if ((*status & mask) == mask)
+               return 0;
+
+       stop = jiffies + timeout;
+       do {
+               /* since we just checked the status, give the TPM some time */
+               usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
+               *status = tpm_tis_i2c_status(chip);
+               if ((*status & mask) == mask)
+                       return 0;
+
+       } while (time_before(jiffies, stop));
+
+       return -ETIME;
+}
+
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       size_t size = 0;
+       ssize_t burstcnt;
+       u8 retries = 0;
+       int rc;
+
+       while (size < count) {
+               burstcnt = get_burstcount(chip);
+
+               /* burstcnt < 0 = TPM is busy */
+               if (burstcnt < 0)
+                       return burstcnt;
+
+               /* limit received data to max. left */
+               if (burstcnt > (count - size))
+                       burstcnt = count - size;
+
+               rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
+                                 &(buf[size]), burstcnt);
+               if (rc == 0)
+                       size += burstcnt;
+               else if (rc < 0)
+                       retries++;
+
+               /* avoid endless loop in case of broken HW */
+               if (retries > MAX_COUNT_LONG)
+                       return -EIO;
+
+       }
+       return size;
+}
+
+static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       int size = 0;
+       int expected, status;
+
+       if (count < TPM_HEADER_SIZE) {
+               size = -EIO;
+               goto out;
+       }
+
+       /* read first 10 bytes, including tag, paramsize, and result */
+       size = recv_data(chip, buf, TPM_HEADER_SIZE);
+       if (size < TPM_HEADER_SIZE) {
+               dev_err(chip->dev, "Unable to read header\n");
+               goto out;
+       }
+
+       expected = be32_to_cpu(*(__be32 *)(buf + 2));
+       if ((size_t) expected > count) {
+               size = -EIO;
+               goto out;
+       }
+
+       size += recv_data(chip, &buf[TPM_HEADER_SIZE],
+                         expected - TPM_HEADER_SIZE);
+       if (size < expected) {
+               dev_err(chip->dev, "Unable to read remainder of result\n");
+               size = -ETIME;
+               goto out;
+       }
+
+       wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+       if (status & TPM_STS_DATA_AVAIL) {      /* retry? */
+               dev_err(chip->dev, "Error left over data\n");
+               size = -EIO;
+               goto out;
+       }
+
+out:
+       tpm_tis_i2c_ready(chip);
+       /* The TPM needs some time to clean up here,
+        * so we sleep rather than keeping the bus busy
+        */
+       usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
+       release_locality(chip, chip->vendor.locality, 0);
+       return size;
+}
+
+static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
+{
+       int rc, status;
+       ssize_t burstcnt;
+       size_t count = 0;
+       u8 retries = 0;
+       u8 sts = TPM_STS_GO;
+
+       if (len > TPM_BUFSIZE)
+               return -E2BIG;  /* command is too long for our tpm, sorry */
+
+       if (request_locality(chip, 0) < 0)
+               return -EBUSY;
+
+       status = tpm_tis_i2c_status(chip);
+       if ((status & TPM_STS_COMMAND_READY) == 0) {
+               tpm_tis_i2c_ready(chip);
+               if (wait_for_stat
+                   (chip, TPM_STS_COMMAND_READY,
+                    chip->vendor.timeout_b, &status) < 0) {
+                       rc = -ETIME;
+                       goto out_err;
+               }
+       }
+
+       while (count < len - 1) {
+               burstcnt = get_burstcount(chip);
+
+               /* burstcnt < 0 = TPM is busy */
+               if (burstcnt < 0)
+                       return burstcnt;
+
+               if (burstcnt > (len - 1 - count))
+                       burstcnt = len - 1 - count;
+
+               rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
+                                  &(buf[count]), burstcnt);
+               if (rc == 0)
+                       count += burstcnt;
+               else if (rc < 0)
+                       retries++;
+
+               /* avoid endless loop in case of broken HW */
+               if (retries > MAX_COUNT_LONG) {
+                       rc = -EIO;
+                       goto out_err;
+               }
+
+               wait_for_stat(chip, TPM_STS_VALID,
+                             chip->vendor.timeout_c, &status);
+
+               if ((status & TPM_STS_DATA_EXPECT) == 0) {
+                       rc = -EIO;
+                       goto out_err;
+               }
+
+       }
+
+       /* write last byte */
+       iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
+       wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
+       if ((status & TPM_STS_DATA_EXPECT) != 0) {
+               rc = -EIO;
+               goto out_err;
+       }
+
+       /* go and do it */
+       iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
+
+       return len;
+out_err:
+       tpm_tis_i2c_ready(chip);
+       /* The TPM needs some time to clean up here,
+        * so we sleep rather than keeping the bus busy
+        */
+       usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
+       release_locality(chip, chip->vendor.locality, 0);
+       return rc;
+}
+
+static const struct file_operations tis_ops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = tpm_open,
+       .read = tpm_read,
+       .write = tpm_write,
+       .release = tpm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
+
+static struct attribute *tis_attrs[] = {
+       &dev_attr_pubek.attr,
+       &dev_attr_pcrs.attr,
+       &dev_attr_enabled.attr,
+       &dev_attr_active.attr,
+       &dev_attr_owned.attr,
+       &dev_attr_temp_deactivated.attr,
+       &dev_attr_caps.attr,
+       &dev_attr_cancel.attr,
+       &dev_attr_durations.attr,
+       &dev_attr_timeouts.attr,
+       NULL,
+};
+
+static struct attribute_group tis_attr_grp = {
+       .attrs = tis_attrs
+};
+
+static struct tpm_vendor_specific tpm_tis_i2c = {
+       .status = tpm_tis_i2c_status,
+       .recv = tpm_tis_i2c_recv,
+       .send = tpm_tis_i2c_send,
+       .cancel = tpm_tis_i2c_ready,
+       .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_canceled = TPM_STS_COMMAND_READY,
+       .attr_group = &tis_attr_grp,
+       .miscdev.fops = &tis_ops,
+};
+
+static int __devinit tpm_tis_i2c_init(struct device *dev)
+{
+       u32 vendor;
+       int rc = 0;
+       struct tpm_chip *chip;
+
+       chip = tpm_register_hardware(dev, &tpm_tis_i2c);
+       if (!chip) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
+       /* Disable interrupts */
+       chip->vendor.irq = 0;
+
+       /* Default timeouts */
+       chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
+       chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+       chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
+
+       if (request_locality(chip, 0) != 0) {
+               rc = -ENODEV;
+               goto out_vendor;
+       }
+
+       /* read four bytes from DID_VID register */
+       if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) {
+               rc = -EIO;
+               goto out_release;
+       }
+
+       /* create DID_VID register value, after swapping to little-endian */
+       vendor = be32_to_cpu((__be32) vendor);
+
+       if (vendor != TPM_TIS_I2C_DID_VID) {
+               rc = -ENODEV;
+               goto out_release;
+       }
+
+       dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
+
+       INIT_LIST_HEAD(&chip->vendor.list);
+       tpm_dev.chip = chip;
+
+       tpm_get_timeouts(chip);
+       tpm_do_selftest(chip);
+
+       return 0;
+
+out_release:
+       release_locality(chip, chip->vendor.locality, 1);
+
+out_vendor:
+       /* close file handles */
+       tpm_dev_vendor_release(chip);
+
+       /* remove hardware */
+       tpm_remove_hardware(chip->dev);
+
+       /* reset these pointers, otherwise we oops */
+       chip->dev->release = NULL;
+       chip->release = NULL;
+       tpm_dev.client = NULL;
+       dev_set_drvdata(chip->dev, chip);
+out_err:
+       return rc;
+}
+
+static const struct i2c_device_id tpm_tis_i2c_table[] = {
+       {"tpm_i2c_infineon", 0},
+       {},
+};
+
+MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table);
+static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume);
+
+static int __devinit tpm_tis_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       int rc;
+       if (tpm_dev.client != NULL)
+               return -EBUSY;  /* We only support one client */
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_err(&client->dev,
+                       "no algorithms associated to the i2c bus\n");
+               return -ENODEV;
+       }
+
+       client->driver = &tpm_tis_i2c_driver;
+       tpm_dev.client = client;
+       rc = tpm_tis_i2c_init(&client->dev);
+       if (rc != 0) {
+               client->driver = NULL;
+               tpm_dev.client = NULL;
+               rc = -ENODEV;
+       }
+       return rc;
+}
+
+static int __devexit tpm_tis_i2c_remove(struct i2c_client *client)
+{
+       struct tpm_chip *chip = tpm_dev.chip;
+       release_locality(chip, chip->vendor.locality, 1);
+
+       /* close file handles */
+       tpm_dev_vendor_release(chip);
+
+       /* remove hardware */
+       tpm_remove_hardware(chip->dev);
+
+       /* reset these pointers, otherwise we oops */
+       chip->dev->release = NULL;
+       chip->release = NULL;
+       tpm_dev.client = NULL;
+       dev_set_drvdata(chip->dev, chip);
+
+       return 0;
+}
+
+static struct i2c_driver tpm_tis_i2c_driver = {
+
+       .id_table = tpm_tis_i2c_table,
+       .probe = tpm_tis_i2c_probe,
+       .remove = tpm_tis_i2c_remove,
+       .driver = {
+                  .name = "tpm_i2c_infineon",
+                  .owner = THIS_MODULE,
+                  .pm = &tpm_tis_i2c_ops,
+                  },
+};
+
+module_i2c_driver(tpm_tis_i2c_driver);
+MODULE_AUTHOR("Peter Huewe <peter.huewe@infineon.com>");
+MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver");
+MODULE_VERSION("2.1.5");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c
new file mode 100644 (file)
index 0000000..efc4ab3
--- /dev/null
@@ -0,0 +1,749 @@
+/*
+ * Copyright (C) 2012 IBM Corporation
+ *
+ * Author: Ashley Lai <adlai@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.
+ *
+ */
+
+#include <linux/dma-mapping.h>
+#include <linux/dmapool.h>
+#include <linux/slab.h>
+#include <asm/vio.h>
+#include <asm/irq.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/interrupt.h>
+#include <linux/wait.h>
+#include <asm/prom.h>
+
+#include "tpm.h"
+#include "tpm_ibmvtpm.h"
+
+static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm";
+
+static struct vio_device_id tpm_ibmvtpm_device_table[] __devinitdata = {
+       { "IBM,vtpm", "IBM,vtpm"},
+       { "", "" }
+};
+MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table);
+
+DECLARE_WAIT_QUEUE_HEAD(wq);
+
+/**
+ * ibmvtpm_send_crq - Send a CRQ request
+ * @vdev:      vio device struct
+ * @w1:                first word
+ * @w2:                second word
+ *
+ * Return value:
+ *     0 -Sucess
+ *     Non-zero - Failure
+ */
+static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2)
+{
+       return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, w1, w2);
+}
+
+/**
+ * ibmvtpm_get_data - Retrieve ibm vtpm data
+ * @dev:       device struct
+ *
+ * Return value:
+ *     vtpm device struct
+ */
+static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+       if (chip)
+               return (struct ibmvtpm_dev *)chip->vendor.data;
+       return NULL;
+}
+
+/**
+ * tpm_ibmvtpm_recv - Receive data after send
+ * @chip:      tpm chip struct
+ * @buf:       buffer to read
+ * count:      size of buffer
+ *
+ * Return value:
+ *     Number of bytes read
+ */
+static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct ibmvtpm_dev *ibmvtpm;
+       u16 len;
+
+       ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
+
+       if (!ibmvtpm->rtce_buf) {
+               dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
+               return 0;
+       }
+
+       wait_event_interruptible(wq, ibmvtpm->crq_res.len != 0);
+
+       if (count < ibmvtpm->crq_res.len) {
+               dev_err(ibmvtpm->dev,
+                       "Invalid size in recv: count=%ld, crq_size=%d\n",
+                       count, ibmvtpm->crq_res.len);
+               return -EIO;
+       }
+
+       spin_lock(&ibmvtpm->rtce_lock);
+       memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, ibmvtpm->crq_res.len);
+       memset(ibmvtpm->rtce_buf, 0, ibmvtpm->crq_res.len);
+       ibmvtpm->crq_res.valid = 0;
+       ibmvtpm->crq_res.msg = 0;
+       len = ibmvtpm->crq_res.len;
+       ibmvtpm->crq_res.len = 0;
+       spin_unlock(&ibmvtpm->rtce_lock);
+       return len;
+}
+
+/**
+ * tpm_ibmvtpm_send - Send tpm request
+ * @chip:      tpm chip struct
+ * @buf:       buffer contains data to send
+ * count:      size of buffer
+ *
+ * Return value:
+ *     Number of bytes sent
+ */
+static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       struct ibmvtpm_dev *ibmvtpm;
+       struct ibmvtpm_crq crq;
+       u64 *word = (u64 *) &crq;
+       int rc;
+
+       ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
+
+       if (!ibmvtpm->rtce_buf) {
+               dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
+               return 0;
+       }
+
+       if (count > ibmvtpm->rtce_size) {
+               dev_err(ibmvtpm->dev,
+                       "Invalid size in send: count=%ld, rtce_size=%d\n",
+                       count, ibmvtpm->rtce_size);
+               return -EIO;
+       }
+
+       spin_lock(&ibmvtpm->rtce_lock);
+       memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count);
+       crq.valid = (u8)IBMVTPM_VALID_CMD;
+       crq.msg = (u8)VTPM_TPM_COMMAND;
+       crq.len = (u16)count;
+       crq.data = ibmvtpm->rtce_dma_handle;
+
+       rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]);
+       if (rc != H_SUCCESS) {
+               dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
+               rc = 0;
+       } else
+               rc = count;
+
+       spin_unlock(&ibmvtpm->rtce_lock);
+       return rc;
+}
+
+static void tpm_ibmvtpm_cancel(struct tpm_chip *chip)
+{
+       return;
+}
+
+static u8 tpm_ibmvtpm_status(struct tpm_chip *chip)
+{
+       return 0;
+}
+
+/**
+ * ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size
+ * @ibmvtpm:   vtpm device struct
+ *
+ * Return value:
+ *     0 - Success
+ *     Non-zero - Failure
+ */
+static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm)
+{
+       struct ibmvtpm_crq crq;
+       u64 *buf = (u64 *) &crq;
+       int rc;
+
+       crq.valid = (u8)IBMVTPM_VALID_CMD;
+       crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE;
+
+       rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
+       if (rc != H_SUCCESS)
+               dev_err(ibmvtpm->dev,
+                       "ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc);
+
+       return rc;
+}
+
+/**
+ * ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version
+ *                        - Note that this is vtpm version and not tpm version
+ * @ibmvtpm:   vtpm device struct
+ *
+ * Return value:
+ *     0 - Success
+ *     Non-zero - Failure
+ */
+static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm)
+{
+       struct ibmvtpm_crq crq;
+       u64 *buf = (u64 *) &crq;
+       int rc;
+
+       crq.valid = (u8)IBMVTPM_VALID_CMD;
+       crq.msg = (u8)VTPM_GET_VERSION;
+
+       rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
+       if (rc != H_SUCCESS)
+               dev_err(ibmvtpm->dev,
+                       "ibmvtpm_crq_get_version failed rc=%d\n", rc);
+
+       return rc;
+}
+
+/**
+ * ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message
+ * @ibmvtpm:   vtpm device struct
+ *
+ * Return value:
+ *     0 - Success
+ *     Non-zero - Failure
+ */
+static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
+{
+       int rc;
+
+       rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_COMP_CMD, 0);
+       if (rc != H_SUCCESS)
+               dev_err(ibmvtpm->dev,
+                       "ibmvtpm_crq_send_init_complete failed rc=%d\n", rc);
+
+       return rc;
+}
+
+/**
+ * ibmvtpm_crq_send_init - Send a CRQ initialize message
+ * @ibmvtpm:   vtpm device struct
+ *
+ * Return value:
+ *     0 - Success
+ *     Non-zero - Failure
+ */
+static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
+{
+       int rc;
+
+       rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_CMD, 0);
+       if (rc != H_SUCCESS)
+               dev_err(ibmvtpm->dev,
+                       "ibmvtpm_crq_send_init failed rc=%d\n", rc);
+
+       return rc;
+}
+
+/**
+ * tpm_ibmvtpm_remove - ibm vtpm remove entry point
+ * @vdev:      vio device struct
+ *
+ * Return value:
+ *     0
+ */
+static int __devexit tpm_ibmvtpm_remove(struct vio_dev *vdev)
+{
+       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
+       int rc = 0;
+
+       free_irq(vdev->irq, ibmvtpm);
+       tasklet_kill(&ibmvtpm->tasklet);
+
+       do {
+               if (rc)
+                       msleep(100);
+               rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
+       } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+       dma_unmap_single(ibmvtpm->dev, ibmvtpm->crq_dma_handle,
+                        CRQ_RES_BUF_SIZE, DMA_BIDIRECTIONAL);
+       free_page((unsigned long)ibmvtpm->crq_queue.crq_addr);
+
+       if (ibmvtpm->rtce_buf) {
+               dma_unmap_single(ibmvtpm->dev, ibmvtpm->rtce_dma_handle,
+                                ibmvtpm->rtce_size, DMA_BIDIRECTIONAL);
+               kfree(ibmvtpm->rtce_buf);
+       }
+
+       tpm_remove_hardware(ibmvtpm->dev);
+
+       kfree(ibmvtpm);
+
+       return 0;
+}
+
+/**
+ * tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver
+ * @vdev:      vio device struct
+ *
+ * Return value:
+ *     Number of bytes the driver needs to DMA map
+ */
+static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
+{
+       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
+       return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size;
+}
+
+/**
+ * tpm_ibmvtpm_suspend - Suspend
+ * @dev:       device struct
+ *
+ * Return value:
+ *     0
+ */
+static int tpm_ibmvtpm_suspend(struct device *dev)
+{
+       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
+       struct ibmvtpm_crq crq;
+       u64 *buf = (u64 *) &crq;
+       int rc = 0;
+
+       crq.valid = (u8)IBMVTPM_VALID_CMD;
+       crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND;
+
+       rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
+       if (rc != H_SUCCESS)
+               dev_err(ibmvtpm->dev,
+                       "tpm_ibmvtpm_suspend failed rc=%d\n", rc);
+
+       return rc;
+}
+
+/**
+ * ibmvtpm_reset_crq - Reset CRQ
+ * @ibmvtpm:   ibm vtpm struct
+ *
+ * Return value:
+ *     0 - Success
+ *     Non-zero - Failure
+ */
+static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
+{
+       int rc = 0;
+
+       do {
+               if (rc)
+                       msleep(100);
+               rc = plpar_hcall_norets(H_FREE_CRQ,
+                                       ibmvtpm->vdev->unit_address);
+       } while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+       memset(ibmvtpm->crq_queue.crq_addr, 0, CRQ_RES_BUF_SIZE);
+       ibmvtpm->crq_queue.index = 0;
+
+       return plpar_hcall_norets(H_REG_CRQ, ibmvtpm->vdev->unit_address,
+                                 ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
+}
+
+/**
+ * tpm_ibmvtpm_resume - Resume from suspend
+ * @dev:       device struct
+ *
+ * Return value:
+ *     0
+ */
+static int tpm_ibmvtpm_resume(struct device *dev)
+{
+       struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
+       unsigned long flags;
+       int rc = 0;
+
+       do {
+               if (rc)
+                       msleep(100);
+               rc = plpar_hcall_norets(H_ENABLE_CRQ,
+                                       ibmvtpm->vdev->unit_address);
+       } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
+
+       if (rc) {
+               dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
+               return rc;
+       }
+
+       spin_lock_irqsave(&ibmvtpm->lock, flags);
+       vio_disable_interrupts(ibmvtpm->vdev);
+       tasklet_schedule(&ibmvtpm->tasklet);
+       spin_unlock_irqrestore(&ibmvtpm->lock, flags);
+
+       rc = ibmvtpm_crq_send_init(ibmvtpm);
+       if (rc)
+               dev_err(dev, "Error send_init rc=%d\n", rc);
+
+       return rc;
+}
+
+static const struct file_operations ibmvtpm_ops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .open = tpm_open,
+       .read = tpm_read,
+       .write = tpm_write,
+       .release = tpm_release,
+};
+
+static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
+static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
+static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
+static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
+static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
+static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
+                  NULL);
+static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
+static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
+static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
+static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
+
+static struct attribute *ibmvtpm_attrs[] = {
+       &dev_attr_pubek.attr,
+       &dev_attr_pcrs.attr,
+       &dev_attr_enabled.attr,
+       &dev_attr_active.attr,
+       &dev_attr_owned.attr,
+       &dev_attr_temp_deactivated.attr,
+       &dev_attr_caps.attr,
+       &dev_attr_cancel.attr,
+       &dev_attr_durations.attr,
+       &dev_attr_timeouts.attr, NULL,
+};
+
+static struct attribute_group ibmvtpm_attr_grp = { .attrs = ibmvtpm_attrs };
+
+static const struct tpm_vendor_specific tpm_ibmvtpm = {
+       .recv = tpm_ibmvtpm_recv,
+       .send = tpm_ibmvtpm_send,
+       .cancel = tpm_ibmvtpm_cancel,
+       .status = tpm_ibmvtpm_status,
+       .req_complete_mask = 0,
+       .req_complete_val = 0,
+       .req_canceled = 0,
+       .attr_group = &ibmvtpm_attr_grp,
+       .miscdev = { .fops = &ibmvtpm_ops, },
+};
+
+static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = {
+       .suspend = tpm_ibmvtpm_suspend,
+       .resume = tpm_ibmvtpm_resume,
+};
+
+/**
+ * ibmvtpm_crq_get_next - Get next responded crq
+ * @ibmvtpm    vtpm device struct
+ *
+ * Return value:
+ *     vtpm crq pointer
+ */
+static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm)
+{
+       struct ibmvtpm_crq_queue *crq_q = &ibmvtpm->crq_queue;
+       struct ibmvtpm_crq *crq = &crq_q->crq_addr[crq_q->index];
+
+       if (crq->valid & VTPM_MSG_RES) {
+               if (++crq_q->index == crq_q->num_entry)
+                       crq_q->index = 0;
+               rmb();
+       } else
+               crq = NULL;
+       return crq;
+}
+
+/**
+ * ibmvtpm_crq_process - Process responded crq
+ * @crq                crq to be processed
+ * @ibmvtpm    vtpm device struct
+ *
+ * Return value:
+ *     Nothing
+ */
+static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
+                               struct ibmvtpm_dev *ibmvtpm)
+{
+       int rc = 0;
+
+       switch (crq->valid) {
+       case VALID_INIT_CRQ:
+               switch (crq->msg) {
+               case INIT_CRQ_RES:
+                       dev_info(ibmvtpm->dev, "CRQ initialized\n");
+                       rc = ibmvtpm_crq_send_init_complete(ibmvtpm);
+                       if (rc)
+                               dev_err(ibmvtpm->dev, "Unable to send CRQ init complete rc=%d\n", rc);
+                       return;
+               case INIT_CRQ_COMP_RES:
+                       dev_info(ibmvtpm->dev,
+                                "CRQ initialization completed\n");
+                       return;
+               default:
+                       dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg);
+                       return;
+               }
+               return;
+       case IBMVTPM_VALID_CMD:
+               switch (crq->msg) {
+               case VTPM_GET_RTCE_BUFFER_SIZE_RES:
+                       if (crq->len <= 0) {
+                               dev_err(ibmvtpm->dev, "Invalid rtce size\n");
+                               return;
+                       }
+                       ibmvtpm->rtce_size = crq->len;
+                       ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size,
+                                                   GFP_KERNEL);
+                       if (!ibmvtpm->rtce_buf) {
+                               dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n");
+                               return;
+                       }
+
+                       ibmvtpm->rtce_dma_handle = dma_map_single(ibmvtpm->dev,
+                               ibmvtpm->rtce_buf, ibmvtpm->rtce_size,
+                               DMA_BIDIRECTIONAL);
+
+                       if (dma_mapping_error(ibmvtpm->dev,
+                                             ibmvtpm->rtce_dma_handle)) {
+                               kfree(ibmvtpm->rtce_buf);
+                               ibmvtpm->rtce_buf = NULL;
+                               dev_err(ibmvtpm->dev, "Failed to dma map rtce buffer\n");
+                       }
+
+                       return;
+               case VTPM_GET_VERSION_RES:
+                       ibmvtpm->vtpm_version = crq->data;
+                       return;
+               case VTPM_TPM_COMMAND_RES:
+                       ibmvtpm->crq_res.valid = crq->valid;
+                       ibmvtpm->crq_res.msg = crq->msg;
+                       ibmvtpm->crq_res.len = crq->len;
+                       ibmvtpm->crq_res.data = crq->data;
+                       wake_up_interruptible(&wq);
+                       return;
+               default:
+                       return;
+               }
+       }
+       return;
+}
+
+/**
+ * ibmvtpm_interrupt - Interrupt handler
+ * @irq:               irq number to handle
+ * @vtpm_instance:     vtpm that received interrupt
+ *
+ * Returns:
+ *     IRQ_HANDLED
+ **/
+static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance)
+{
+       struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ibmvtpm->lock, flags);
+       vio_disable_interrupts(ibmvtpm->vdev);
+       tasklet_schedule(&ibmvtpm->tasklet);
+       spin_unlock_irqrestore(&ibmvtpm->lock, flags);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ibmvtpm_tasklet - Interrupt handler tasklet
+ * @data:      ibm vtpm device struct
+ *
+ * Returns:
+ *     Nothing
+ **/
+static void ibmvtpm_tasklet(void *data)
+{
+       struct ibmvtpm_dev *ibmvtpm = data;
+       struct ibmvtpm_crq *crq;
+       unsigned long flags;
+
+       spin_lock_irqsave(&ibmvtpm->lock, flags);
+       while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) {
+               ibmvtpm_crq_process(crq, ibmvtpm);
+               crq->valid = 0;
+               wmb();
+       }
+
+       vio_enable_interrupts(ibmvtpm->vdev);
+       spin_unlock_irqrestore(&ibmvtpm->lock, flags);
+}
+
+/**
+ * tpm_ibmvtpm_probe - ibm vtpm initialize entry point
+ * @vio_dev:   vio device struct
+ * @id:                vio device id struct
+ *
+ * Return value:
+ *     0 - Success
+ *     Non-zero - Failure
+ */
+static int __devinit tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
+                                  const struct vio_device_id *id)
+{
+       struct ibmvtpm_dev *ibmvtpm;
+       struct device *dev = &vio_dev->dev;
+       struct ibmvtpm_crq_queue *crq_q;
+       struct tpm_chip *chip;
+       int rc = -ENOMEM, rc1;
+
+       chip = tpm_register_hardware(dev, &tpm_ibmvtpm);
+       if (!chip) {
+               dev_err(dev, "tpm_register_hardware failed\n");
+               return -ENODEV;
+       }
+
+       ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL);
+       if (!ibmvtpm) {
+               dev_err(dev, "kzalloc for ibmvtpm failed\n");
+               goto cleanup;
+       }
+
+       crq_q = &ibmvtpm->crq_queue;
+       crq_q->crq_addr = (struct ibmvtpm_crq *)get_zeroed_page(GFP_KERNEL);
+       if (!crq_q->crq_addr) {
+               dev_err(dev, "Unable to allocate memory for crq_addr\n");
+               goto cleanup;
+       }
+
+       crq_q->num_entry = CRQ_RES_BUF_SIZE / sizeof(*crq_q->crq_addr);
+       ibmvtpm->crq_dma_handle = dma_map_single(dev, crq_q->crq_addr,
+                                                CRQ_RES_BUF_SIZE,
+                                                DMA_BIDIRECTIONAL);
+
+       if (dma_mapping_error(dev, ibmvtpm->crq_dma_handle)) {
+               dev_err(dev, "dma mapping failed\n");
+               goto cleanup;
+       }
+
+       rc = plpar_hcall_norets(H_REG_CRQ, vio_dev->unit_address,
+                               ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
+       if (rc == H_RESOURCE)
+               rc = ibmvtpm_reset_crq(ibmvtpm);
+
+       if (rc) {
+               dev_err(dev, "Unable to register CRQ rc=%d\n", rc);
+               goto reg_crq_cleanup;
+       }
+
+       tasklet_init(&ibmvtpm->tasklet, (void *)ibmvtpm_tasklet,
+                    (unsigned long)ibmvtpm);
+
+       rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0,
+                        tpm_ibmvtpm_driver_name, ibmvtpm);
+       if (rc) {
+               dev_err(dev, "Error %d register irq 0x%x\n", rc, vio_dev->irq);
+               goto init_irq_cleanup;
+       }
+
+       rc = vio_enable_interrupts(vio_dev);
+       if (rc) {
+               dev_err(dev, "Error %d enabling interrupts\n", rc);
+               goto init_irq_cleanup;
+       }
+
+       crq_q->index = 0;
+
+       ibmvtpm->dev = dev;
+       ibmvtpm->vdev = vio_dev;
+       chip->vendor.data = (void *)ibmvtpm;
+
+       spin_lock_init(&ibmvtpm->lock);
+       spin_lock_init(&ibmvtpm->rtce_lock);
+
+       rc = ibmvtpm_crq_send_init(ibmvtpm);
+       if (rc)
+               goto init_irq_cleanup;
+
+       rc = ibmvtpm_crq_get_version(ibmvtpm);
+       if (rc)
+               goto init_irq_cleanup;
+
+       rc = ibmvtpm_crq_get_rtce_size(ibmvtpm);
+       if (rc)
+               goto init_irq_cleanup;
+
+       return rc;
+init_irq_cleanup:
+       tasklet_kill(&ibmvtpm->tasklet);
+       do {
+               rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address);
+       } while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1));
+reg_crq_cleanup:
+       dma_unmap_single(dev, ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE,
+                        DMA_BIDIRECTIONAL);
+cleanup:
+       if (ibmvtpm) {
+               if (crq_q->crq_addr)
+                       free_page((unsigned long)crq_q->crq_addr);
+               kfree(ibmvtpm);
+       }
+
+       tpm_remove_hardware(dev);
+
+       return rc;
+}
+
+static struct vio_driver ibmvtpm_driver = {
+       .id_table        = tpm_ibmvtpm_device_table,
+       .probe           = tpm_ibmvtpm_probe,
+       .remove          = tpm_ibmvtpm_remove,
+       .get_desired_dma = tpm_ibmvtpm_get_desired_dma,
+       .name            = tpm_ibmvtpm_driver_name,
+       .pm              = &tpm_ibmvtpm_pm_ops,
+};
+
+/**
+ * ibmvtpm_module_init - Initialize ibm vtpm module
+ *
+ * Return value:
+ *     0 -Success
+ *     Non-zero - Failure
+ */
+static int __init ibmvtpm_module_init(void)
+{
+       return vio_register_driver(&ibmvtpm_driver);
+}
+
+/**
+ * ibmvtpm_module_exit - Teardown ibm vtpm module
+ *
+ * Return value:
+ *     Nothing
+ */
+static void __exit ibmvtpm_module_exit(void)
+{
+       vio_unregister_driver(&ibmvtpm_driver);
+}
+
+module_init(ibmvtpm_module_init);
+module_exit(ibmvtpm_module_exit);
+
+MODULE_AUTHOR("adlai@us.ibm.com");
+MODULE_DESCRIPTION("IBM vTPM Driver");
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h
new file mode 100644 (file)
index 0000000..4296eb4
--- /dev/null
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2012 IBM Corporation
+ *
+ * Author: Ashley Lai <adlai@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 __TPM_IBMVTPM_H__
+#define __TPM_IBMVTPM_H__
+
+/* vTPM Message Format 1 */
+struct ibmvtpm_crq {
+       u8 valid;
+       u8 msg;
+       u16 len;
+       u32 data;
+       u64 reserved;
+} __attribute__((packed, aligned(8)));
+
+struct ibmvtpm_crq_queue {
+       struct ibmvtpm_crq *crq_addr;
+       u32 index;
+       u32 num_entry;
+};
+
+struct ibmvtpm_dev {
+       struct device *dev;
+       struct vio_dev *vdev;
+       struct ibmvtpm_crq_queue crq_queue;
+       dma_addr_t crq_dma_handle;
+       spinlock_t lock;
+       struct tasklet_struct tasklet;
+       u32 rtce_size;
+       void __iomem *rtce_buf;
+       dma_addr_t rtce_dma_handle;
+       spinlock_t rtce_lock;
+       struct ibmvtpm_crq crq_res;
+       u32 vtpm_version;
+};
+
+#define CRQ_RES_BUF_SIZE       PAGE_SIZE
+
+/* Initialize CRQ */
+#define INIT_CRQ_CMD           0xC001000000000000LL /* Init cmd */
+#define INIT_CRQ_COMP_CMD      0xC002000000000000LL /* Init complete cmd */
+#define INIT_CRQ_RES           0x01    /* Init respond */
+#define INIT_CRQ_COMP_RES      0x02    /* Init complete respond */
+#define VALID_INIT_CRQ         0xC0    /* Valid command for init crq */
+
+/* vTPM CRQ response is the message type | 0x80 */
+#define VTPM_MSG_RES           0x80
+#define IBMVTPM_VALID_CMD      0x80
+
+/* vTPM CRQ message types */
+#define VTPM_GET_VERSION                       0x01
+#define VTPM_GET_VERSION_RES                   (0x01 | VTPM_MSG_RES)
+
+#define VTPM_TPM_COMMAND                       0x02
+#define VTPM_TPM_COMMAND_RES                   (0x02 | VTPM_MSG_RES)
+
+#define VTPM_GET_RTCE_BUFFER_SIZE              0x03
+#define VTPM_GET_RTCE_BUFFER_SIZE_RES          (0x03 | VTPM_MSG_RES)
+
+#define VTPM_PREPARE_TO_SUSPEND                        0x04
+#define VTPM_PREPARE_TO_SUSPEND_RES            (0x04 | VTPM_MSG_RES)
+
+#endif
diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c
new file mode 100644 (file)
index 0000000..98ba2bd
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2012 IBM Corporation
+ *
+ * Author: Ashley Lai <adlai@us.ibm.com>
+ *
+ * Maintained by: <tpmdd-devel@lists.sourceforge.net>
+ *
+ * Read the event log created by the firmware on PPC64
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/of.h>
+
+#include "tpm.h"
+#include "tpm_eventlog.h"
+
+int read_log(struct tpm_bios_log *log)
+{
+       struct device_node *np;
+       const u32 *sizep;
+       const __be64 *basep;
+
+       if (log->bios_event_log != NULL) {
+               pr_err("%s: ERROR - Eventlog already initialized\n", __func__);
+               return -EFAULT;
+       }
+
+       np = of_find_node_by_name(NULL, "ibm,vtpm");
+       if (!np) {
+               pr_err("%s: ERROR - IBMVTPM not supported\n", __func__);
+               return -ENODEV;
+       }
+
+       sizep = of_get_property(np, "linux,sml-size", NULL);
+       if (sizep == NULL) {
+               pr_err("%s: ERROR - SML size not found\n", __func__);
+               goto cleanup_eio;
+       }
+       if (*sizep == 0) {
+               pr_err("%s: ERROR - event log area empty\n", __func__);
+               goto cleanup_eio;
+       }
+
+       basep = of_get_property(np, "linux,sml-base", NULL);
+       if (basep == NULL) {
+               pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__);
+               goto cleanup_eio;
+       }
+
+       of_node_put(np);
+       log->bios_event_log = kmalloc(*sizep, GFP_KERNEL);
+       if (!log->bios_event_log) {
+               pr_err("%s: ERROR - Not enough memory for BIOS measurements\n",
+                      __func__);
+               return -ENOMEM;
+       }
+
+       log->bios_event_log_end = log->bios_event_log + *sizep;
+
+       memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep);
+
+       return 0;
+
+cleanup_eio:
+       of_node_put(np);
+       return -EIO;
+}
diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c
new file mode 100644 (file)
index 0000000..f27b58c
--- /dev/null
@@ -0,0 +1,461 @@
+#include <linux/acpi.h>
+#include <acpi/acpi_drivers.h>
+#include "tpm.h"
+
+static const u8 tpm_ppi_uuid[] = {
+       0xA6, 0xFA, 0xDD, 0x3D,
+       0x1B, 0x36,
+       0xB4, 0x4E,
+       0xA4, 0x24,
+       0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
+};
+static char *tpm_device_name = "TPM";
+
+#define TPM_PPI_REVISION_ID    1
+#define TPM_PPI_FN_VERSION     1
+#define TPM_PPI_FN_SUBREQ      2
+#define TPM_PPI_FN_GETREQ      3
+#define TPM_PPI_FN_GETACT      4
+#define TPM_PPI_FN_GETRSP      5
+#define TPM_PPI_FN_SUBREQ2     7
+#define TPM_PPI_FN_GETOPR      8
+#define PPI_TPM_REQ_MAX                22
+#define PPI_VS_REQ_START       128
+#define PPI_VS_REQ_END         255
+#define PPI_VERSION_LEN                3
+
+static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context,
+                               void **return_value)
+{
+       acpi_status status;
+       struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
+       status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
+       if (strstr(buffer.pointer, context) != NULL) {
+               *return_value = handle;
+               kfree(buffer.pointer);
+               return AE_CTRL_TERMINATE;
+       }
+       return AE_OK;
+}
+
+static inline void ppi_assign_params(union acpi_object params[4],
+                                    u64 function_num)
+{
+       params[0].type = ACPI_TYPE_BUFFER;
+       params[0].buffer.length = sizeof(tpm_ppi_uuid);
+       params[0].buffer.pointer = (char *)tpm_ppi_uuid;
+       params[1].type = ACPI_TYPE_INTEGER;
+       params[1].integer.value = TPM_PPI_REVISION_ID;
+       params[2].type = ACPI_TYPE_INTEGER;
+       params[2].integer.value = function_num;
+       params[3].type = ACPI_TYPE_PACKAGE;
+       params[3].package.count = 0;
+       params[3].package.elements = NULL;
+}
+
+static ssize_t tpm_show_ppi_version(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       acpi_handle handle;
+       acpi_status status;
+       struct acpi_object_list input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object params[4];
+       union acpi_object *obj;
+
+       input.count = 4;
+       ppi_assign_params(params, TPM_PPI_FN_VERSION);
+       input.pointer = params;
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX, ppi_callback, NULL,
+                                    tpm_device_name, &handle);
+       if (ACPI_FAILURE(status))
+               return -ENXIO;
+
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                        ACPI_TYPE_STRING);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+       obj = (union acpi_object *)output.pointer;
+       status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer);
+       kfree(output.pointer);
+       return status;
+}
+
+static ssize_t tpm_show_ppi_request(struct device *dev,
+                                   struct device_attribute *attr, char *buf)
+{
+       acpi_handle handle;
+       acpi_status status;
+       struct acpi_object_list input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object params[4];
+       union acpi_object *ret_obj;
+
+       input.count = 4;
+       ppi_assign_params(params, TPM_PPI_FN_GETREQ);
+       input.pointer = params;
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX, ppi_callback, NULL,
+                                    tpm_device_name, &handle);
+       if (ACPI_FAILURE(status))
+               return -ENXIO;
+
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                           ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+       /*
+        * output.pointer should be of package type, including two integers.
+        * The first is function return code, 0 means success and 1 means
+        * error. The second is pending TPM operation requested by the OS, 0
+        * means none and >0 means operation value.
+        */
+       ret_obj = ((union acpi_object *)output.pointer)->package.elements;
+       if (ret_obj->type == ACPI_TYPE_INTEGER) {
+               if (ret_obj->integer.value) {
+                       status = -EFAULT;
+                       goto cleanup;
+               }
+               ret_obj++;
+               if (ret_obj->type == ACPI_TYPE_INTEGER)
+                       status = scnprintf(buf, PAGE_SIZE, "%llu\n",
+                                          ret_obj->integer.value);
+               else
+                       status = -EINVAL;
+       } else {
+               status = -EINVAL;
+       }
+cleanup:
+       kfree(output.pointer);
+       return status;
+}
+
+static ssize_t tpm_store_ppi_request(struct device *dev,
+                                    struct device_attribute *attr,
+                                    const char *buf, size_t count)
+{
+       char version[PPI_VERSION_LEN + 1];
+       acpi_handle handle;
+       acpi_status status;
+       struct acpi_object_list input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object params[4];
+       union acpi_object obj;
+       u32 req;
+       u64 ret;
+
+       input.count = 4;
+       ppi_assign_params(params, TPM_PPI_FN_VERSION);
+       input.pointer = params;
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX, ppi_callback, NULL,
+                                    tpm_device_name, &handle);
+       if (ACPI_FAILURE(status))
+               return -ENXIO;
+
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                           ACPI_TYPE_STRING);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+       strncpy(version,
+               ((union acpi_object *)output.pointer)->string.pointer,
+               PPI_VERSION_LEN);
+       kfree(output.pointer);
+       output.length = ACPI_ALLOCATE_BUFFER;
+       output.pointer = NULL;
+       /*
+        * the function to submit TPM operation request to pre-os environment
+        * is updated with function index from SUBREQ to SUBREQ2 since PPI
+        * version 1.1
+        */
+       if (strcmp(version, "1.1") == -1)
+               params[2].integer.value = TPM_PPI_FN_SUBREQ;
+       else
+               params[2].integer.value = TPM_PPI_FN_SUBREQ2;
+       /*
+        * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
+        * accept buffer/string/integer type, but some BIOS accept buffer/
+        * string/package type. For PPI version 1.0 and 1.1, use buffer type
+        * for compatibility, and use package type since 1.2 according to spec.
+        */
+       if (strcmp(version, "1.2") == -1) {
+               params[3].type = ACPI_TYPE_BUFFER;
+               params[3].buffer.length = sizeof(req);
+               sscanf(buf, "%d", &req);
+               params[3].buffer.pointer = (char *)&req;
+       } else {
+               params[3].package.count = 1;
+               obj.type = ACPI_TYPE_INTEGER;
+               sscanf(buf, "%llu", &obj.integer.value);
+               params[3].package.elements = &obj;
+       }
+
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                           ACPI_TYPE_INTEGER);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+       ret = ((union acpi_object *)output.pointer)->integer.value;
+       if (ret == 0)
+               status = (acpi_status)count;
+       else if (ret == 1)
+               status = -EPERM;
+       else
+               status = -EFAULT;
+       kfree(output.pointer);
+       return status;
+}
+
+static ssize_t tpm_show_ppi_transition_action(struct device *dev,
+                                             struct device_attribute *attr,
+                                             char *buf)
+{
+       char version[PPI_VERSION_LEN + 1];
+       acpi_handle handle;
+       acpi_status status;
+       struct acpi_object_list input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object params[4];
+       u32 ret;
+       char *info[] = {
+               "None",
+               "Shutdown",
+               "Reboot",
+               "OS Vendor-specific",
+               "Error",
+       };
+       input.count = 4;
+       ppi_assign_params(params, TPM_PPI_FN_VERSION);
+       input.pointer = params;
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX, ppi_callback, NULL,
+                                    tpm_device_name, &handle);
+       if (ACPI_FAILURE(status))
+               return -ENXIO;
+
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                           ACPI_TYPE_STRING);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+       strncpy(version,
+               ((union acpi_object *)output.pointer)->string.pointer,
+               PPI_VERSION_LEN);
+       /*
+        * PPI spec defines params[3].type as empty package, but some platforms
+        * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
+        * compatibility, define params[3].type as buffer, if PPI version < 1.2
+        */
+       if (strcmp(version, "1.2") == -1) {
+               params[3].type = ACPI_TYPE_BUFFER;
+               params[3].buffer.length =  0;
+               params[3].buffer.pointer = NULL;
+       }
+       params[2].integer.value = TPM_PPI_FN_GETACT;
+       kfree(output.pointer);
+       output.length = ACPI_ALLOCATE_BUFFER;
+       output.pointer = NULL;
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                           ACPI_TYPE_INTEGER);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+       ret = ((union acpi_object *)output.pointer)->integer.value;
+       if (ret < ARRAY_SIZE(info) - 1)
+               status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
+       else
+               status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
+                                  info[ARRAY_SIZE(info)-1]);
+       kfree(output.pointer);
+       return status;
+}
+
+static ssize_t tpm_show_ppi_response(struct device *dev,
+                                    struct device_attribute *attr,
+                                    char *buf)
+{
+       acpi_handle handle;
+       acpi_status status;
+       struct acpi_object_list input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object params[4];
+       union acpi_object *ret_obj;
+       u64 req;
+
+       input.count = 4;
+       ppi_assign_params(params, TPM_PPI_FN_GETRSP);
+       input.pointer = params;
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX, ppi_callback, NULL,
+                                    tpm_device_name, &handle);
+       if (ACPI_FAILURE(status))
+               return -ENXIO;
+
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                           ACPI_TYPE_PACKAGE);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+       /*
+        * parameter output.pointer should be of package type, including
+        * 3 integers. The first means function return code, the second means
+        * most recent TPM operation request, and the last means response to
+        * the most recent TPM operation request. Only if the first is 0, and
+        * the second integer is not 0, the response makes sense.
+        */
+       ret_obj = ((union acpi_object *)output.pointer)->package.elements;
+       if (ret_obj->type != ACPI_TYPE_INTEGER) {
+               status = -EINVAL;
+               goto cleanup;
+       }
+       if (ret_obj->integer.value) {
+               status = -EFAULT;
+               goto cleanup;
+       }
+       ret_obj++;
+       if (ret_obj->type != ACPI_TYPE_INTEGER) {
+               status = -EINVAL;
+               goto cleanup;
+       }
+       if (ret_obj->integer.value) {
+               req = ret_obj->integer.value;
+               ret_obj++;
+               if (ret_obj->type != ACPI_TYPE_INTEGER) {
+                       status = -EINVAL;
+                       goto cleanup;
+               }
+               if (ret_obj->integer.value == 0)
+                       status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
+                                          "0: Success");
+               else if (ret_obj->integer.value == 0xFFFFFFF0)
+                       status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
+                                          "0xFFFFFFF0: User Abort");
+               else if (ret_obj->integer.value == 0xFFFFFFF1)
+                       status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
+                                          "0xFFFFFFF1: BIOS Failure");
+               else if (ret_obj->integer.value >= 1 &&
+                        ret_obj->integer.value <= 0x00000FFF)
+                       status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
+                                          req, ret_obj->integer.value,
+                                          "Corresponding TPM error");
+               else
+                       status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
+                                          req, ret_obj->integer.value,
+                                          "Error");
+       } else {
+               status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
+                                  ret_obj->integer.value, "No Recent Request");
+       }
+cleanup:
+       kfree(output.pointer);
+       return status;
+}
+
+static ssize_t show_ppi_operations(char *buf, u32 start, u32 end)
+{
+       char *str = buf;
+       char version[PPI_VERSION_LEN];
+       acpi_handle handle;
+       acpi_status status;
+       struct acpi_object_list input;
+       struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
+       union acpi_object params[4];
+       union acpi_object obj;
+       int i;
+       u32 ret;
+       char *info[] = {
+               "Not implemented",
+               "BIOS only",
+               "Blocked for OS by BIOS",
+               "User required",
+               "User not required",
+       };
+       input.count = 4;
+       ppi_assign_params(params, TPM_PPI_FN_VERSION);
+       input.pointer = params;
+       status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+                                    ACPI_UINT32_MAX, ppi_callback, NULL,
+                                    tpm_device_name, &handle);
+       if (ACPI_FAILURE(status))
+               return -ENXIO;
+
+       status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
+                                        ACPI_TYPE_STRING);
+       if (ACPI_FAILURE(status))
+               return -ENOMEM;
+
+       strncpy(version,
+               ((union acpi_object *)output.pointer)->string.pointer,
+               PPI_VERSION_LEN);
+       kfree(output.pointer);
+       output.length = ACPI_ALLOCATE_BUFFER;
+       output.pointer = NULL;
+       if (strcmp(version, "1.2") == -1)
+               return -EPERM;
+
+       params[2].integer.value = TPM_PPI_FN_GETOPR;
+       params[3].package.count = 1;
+       obj.type = ACPI_TYPE_INTEGER;
+       params[3].package.elements = &obj;
+       for (i = start; i <= end; i++) {
+               obj.integer.value = i;
+               status = acpi_evaluate_object_typed(handle, "_DSM",
+                        &input, &output, ACPI_TYPE_INTEGER);
+               if (ACPI_FAILURE(status))
+                       return -ENOMEM;
+
+               ret = ((union acpi_object *)output.pointer)->integer.value;
+               if (ret > 0 && ret < ARRAY_SIZE(info))
+                       str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
+                                        i, ret, info[ret]);
+               kfree(output.pointer);
+               output.length = ACPI_ALLOCATE_BUFFER;
+               output.pointer = NULL;
+       }
+       return str - buf;
+}
+
+static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
+                                          struct device_attribute *attr,
+                                          char *buf)
+{
+       return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX);
+}
+
+static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
+                                         struct device_attribute *attr,
+                                         char *buf)
+{
+       return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END);
+}
+
+static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
+static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP,
+                  tpm_show_ppi_request, tpm_store_ppi_request);
+static DEVICE_ATTR(transition_action, S_IRUGO,
+                  tpm_show_ppi_transition_action, NULL);
+static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL);
+static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL);
+static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL);
+
+static struct attribute *ppi_attrs[] = {
+       &dev_attr_version.attr,
+       &dev_attr_request.attr,
+       &dev_attr_transition_action.attr,
+       &dev_attr_response.attr,
+       &dev_attr_tcg_operations.attr,
+       &dev_attr_vs_operations.attr, NULL,
+};
+static struct attribute_group ppi_attr_grp = {
+       .attrs = ppi_attrs
+};
+
+ssize_t sys_add_ppi(struct kobject *parent)
+{
+       struct kobject *ppi;
+       ppi = kobject_create_and_add("ppi", parent);
+       if (sysfs_create_group(ppi, &ppi_attr_grp))
+               return -EFAULT;
+       else
+               return 0;
+}
+EXPORT_SYMBOL_GPL(sys_add_ppi);
+
+MODULE_LICENSE("GPL");
index c4be3519a587c4fe33c3a14be17ba72e6067d9a0..6bdf2671254f405e03bfcca83cebba4cbae2e50c 100644 (file)
@@ -705,6 +705,7 @@ out_err:
        return rc;
 }
 
+#if defined(CONFIG_PNP) || defined(CONFIG_PM_SLEEP)
 static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
 {
        u32 intmask;
@@ -725,7 +726,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
        iowrite32(intmask,
                  chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
 }
-
+#endif
 
 #ifdef CONFIG_PNP
 static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
index 29e38a1f7f77a15100f1d20b3e51507470273633..cce7df53b694373b6288795f14b07eb91c0894fc 100644 (file)
--- a/fs/attr.c
+++ b/fs/attr.c
@@ -14,6 +14,7 @@
 #include <linux/fcntl.h>
 #include <linux/security.h>
 #include <linux/evm.h>
+#include <linux/ima.h>
 
 /**
  * inode_change_ok - check if attribute changes to an inode are allowed
@@ -247,6 +248,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
 
        if (!error) {
                fsnotify_change(dentry, ia_valid);
+               ima_inode_post_setattr(dentry);
                evm_inode_post_setattr(dentry, ia_valid);
        }
 
index 701985e4ccda4fc5afc05977f511bd6ca11ba3af..a41f23f90b17d932a7da824ed6a3f48053e03fc7 100644 (file)
@@ -243,10 +243,10 @@ static void __fput(struct file *file)
                if (file->f_op && file->f_op->fasync)
                        file->f_op->fasync(-1, file, 0);
        }
+       ima_file_free(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 &&
                     !(file->f_mode & FMODE_PATH))) {
                cdev_put(inode->i_cdev);
index 4d45b7189e7eaa352da0567b8fe4a2d1b738c999..107f457143c35ffbcb9ad39ae52815a81332fcec 100644 (file)
@@ -295,11 +295,13 @@ vfs_removexattr(struct dentry *dentry, const char *name)
        if (error)
                return error;
 
+       mutex_lock(&inode->i_mutex);
        error = security_inode_removexattr(dentry, name);
-       if (error)
+       if (error) {
+               mutex_unlock(&inode->i_mutex);
                return error;
+       }
 
-       mutex_lock(&inode->i_mutex);
        error = inode->i_op->removexattr(dentry, name);
        mutex_unlock(&inode->i_mutex);
 
index 36abf2aa7e680e24afe8c52b87ccf8f88707ee17..2a5073cf548a0d848f99c7810a124bded3313eb9 100644 (file)
@@ -529,6 +529,7 @@ extern int  audit_set_loginuid(uid_t loginuid);
 #define audit_get_loginuid(t) ((t)->loginuid)
 #define audit_get_sessionid(t) ((t)->sessionid)
 extern void audit_log_task_context(struct audit_buffer *ab);
+extern void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk);
 extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp);
 extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode);
 extern int __audit_bprm(struct linux_binprm *bprm);
@@ -640,6 +641,7 @@ extern int audit_signals;
 #define audit_get_loginuid(t) (-1)
 #define audit_get_sessionid(t) (-1)
 #define audit_log_task_context(b) do { ; } while (0)
+#define audit_log_task_info(b, t) do { ; } while (0)
 #define audit_ipc_obj(i) ((void)0)
 #define audit_ipc_set_perm(q,u,g,m) ((void)0)
 #define audit_bprm(p) ({ 0; })
index 6ac8e50c6cf5453e338fec108f9dd1418046e3e9..2c7223d7e73b720de899756e3a0433a813c2640e 100644 (file)
@@ -39,5 +39,32 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
 {
        return 0;
 }
+
 #endif /* CONFIG_IMA_H */
+
+#ifdef CONFIG_IMA_APPRAISE
+extern void ima_inode_post_setattr(struct dentry *dentry);
+extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+                      const void *xattr_value, size_t xattr_value_len);
+extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
+#else
+static inline void ima_inode_post_setattr(struct dentry *dentry)
+{
+       return;
+}
+
+static inline int ima_inode_setxattr(struct dentry *dentry,
+                                    const char *xattr_name,
+                                    const void *xattr_value,
+                                    size_t xattr_value_len)
+{
+       return 0;
+}
+
+static inline int ima_inode_removexattr(struct dentry *dentry,
+                                       const char *xattr_name)
+{
+       return 0;
+}
+#endif /* CONFIG_IMA_APPRAISE_H */
 #endif /* _LINUX_IMA_H */
index a0c41256cb923d5ffb21d9f4acd20680d3fc54a0..66c5fe9550a5e47bfc2a32c67e1339eb7d4f92ae 100644 (file)
@@ -22,13 +22,14 @@ enum integrity_status {
 
 /* List of EVM protected security xattrs */
 #ifdef CONFIG_INTEGRITY
-extern int integrity_inode_alloc(struct inode *inode);
+extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
 extern void integrity_inode_free(struct inode *inode);
 
 #else
-static inline int integrity_inode_alloc(struct inode *inode)
+static inline struct integrity_iint_cache *
+                               integrity_inode_get(struct inode *inode)
 {
-       return 0;
+       return NULL;
 }
 
 static inline void integrity_inode_free(struct inode *inode)
index 597e4fdb97fe4d676b5fa0b717ae53f023893e5e..3db698aee34cd9437f8f42823f17825796597f09 100644 (file)
@@ -130,8 +130,6 @@ extern void exit_ptrace(struct task_struct *tracer);
 #define PTRACE_MODE_READ       0x01
 #define PTRACE_MODE_ATTACH     0x02
 #define PTRACE_MODE_NOAUDIT    0x04
-/* Returns 0 on success, -errno on denial. */
-extern int __ptrace_may_access(struct task_struct *task, unsigned int mode);
 /* Returns true on success, false on denial. */
 extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);
 
index 3dea6a9d568f416ccd1b704eec1d4bff9dea6d90..01ef030b94099edab9e884de3ebc3cf2d3749fae 100644 (file)
@@ -3021,5 +3021,36 @@ static inline void free_secdata(void *secdata)
 { }
 #endif /* CONFIG_SECURITY */
 
+#ifdef CONFIG_SECURITY_YAMA
+extern int yama_ptrace_access_check(struct task_struct *child,
+                                   unsigned int mode);
+extern int yama_ptrace_traceme(struct task_struct *parent);
+extern void yama_task_free(struct task_struct *task);
+extern int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+                          unsigned long arg4, unsigned long arg5);
+#else
+static inline int yama_ptrace_access_check(struct task_struct *child,
+                                          unsigned int mode)
+{
+       return 0;
+}
+
+static inline int yama_ptrace_traceme(struct task_struct *parent)
+{
+       return 0;
+}
+
+static inline void yama_task_free(struct task_struct *task)
+{
+}
+
+static inline int yama_task_prctl(int option, unsigned long arg2,
+                                 unsigned long arg3, unsigned long arg4,
+                                 unsigned long arg5)
+{
+       return -ENOSYS;
+}
+#endif /* CONFIG_SECURITY_YAMA */
+
 #endif /* ! __LINUX_SECURITY_H */
 
index fdc718abf83becac29f7dd3a01cabd8b6ad7f068..fcb627ff8d3eb71ad49ced6763a0ff061b83f62e 100644 (file)
@@ -32,6 +32,7 @@
 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);
 extern int tpm_send(u32 chip_num, void *cmd, size_t buflen);
+extern int tpm_get_random(u32 chip_num, u8 *data, size_t max);
 #else
 static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
        return -ENODEV;
@@ -42,5 +43,8 @@ static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) {
 static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) {
        return -ENODEV;
 }
+static inline int tpm_get_random(u32 chip_num, u8 *data, size_t max) {
+       return -ENODEV;
+}
 #endif
 #endif
index e5d122031542f5e3628d5a3bbbd044f0f36f51a3..77a3e686d56627cba522616bd9425ae551b0ce35 100644 (file)
@@ -33,6 +33,9 @@
 #define XATTR_EVM_SUFFIX "evm"
 #define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
 
+#define XATTR_IMA_SUFFIX "ima"
+#define XATTR_NAME_IMA XATTR_SECURITY_PREFIX XATTR_IMA_SUFFIX
+
 #define XATTR_SELINUX_SUFFIX "selinux"
 #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
 
index 4b96415527b8664753e18cb169f0de9f391f9314..37f52f27828df4890a4dce17c2a8402f013b74f8 100644 (file)
@@ -1154,13 +1154,38 @@ error_path:
 
 EXPORT_SYMBOL(audit_log_task_context);
 
-static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
+void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
 {
+       const struct cred *cred;
        char name[sizeof(tsk->comm)];
        struct mm_struct *mm = tsk->mm;
        struct vm_area_struct *vma;
+       char *tty;
+
+       if (!ab)
+               return;
 
        /* tsk == current */
+       cred = current_cred();
+
+       spin_lock_irq(&tsk->sighand->siglock);
+       if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
+               tty = tsk->signal->tty->name;
+       else
+               tty = "(none)";
+       spin_unlock_irq(&tsk->sighand->siglock);
+
+
+       audit_log_format(ab,
+                        " ppid=%ld pid=%d auid=%u uid=%u gid=%u"
+                        " euid=%u suid=%u fsuid=%u"
+                        " egid=%u sgid=%u fsgid=%u ses=%u tty=%s",
+                        sys_getppid(),
+                        tsk->pid,
+                        tsk->loginuid, cred->uid, cred->gid,
+                        cred->euid, cred->suid, cred->fsuid,
+                        cred->egid, cred->sgid, cred->fsgid,
+                        tsk->sessionid, tty);
 
        get_task_comm(name, tsk);
        audit_log_format(ab, " comm=");
@@ -1183,6 +1208,8 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk
        audit_log_task_context(ab);
 }
 
+EXPORT_SYMBOL(audit_log_task_info);
+
 static int audit_log_pid_context(struct audit_context *context, pid_t pid,
                                 uid_t auid, uid_t uid, unsigned int sessionid,
                                 u32 sid, char *comm)
@@ -1585,26 +1612,12 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
 
 static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
 {
-       const struct cred *cred;
        int i, call_panic = 0;
        struct audit_buffer *ab;
        struct audit_aux_data *aux;
-       const char *tty;
        struct audit_names *n;
 
        /* tsk == current */
-       context->pid = tsk->pid;
-       if (!context->ppid)
-               context->ppid = sys_getppid();
-       cred = current_cred();
-       context->uid   = cred->uid;
-       context->gid   = cred->gid;
-       context->euid  = cred->euid;
-       context->suid  = cred->suid;
-       context->fsuid = cred->fsuid;
-       context->egid  = cred->egid;
-       context->sgid  = cred->sgid;
-       context->fsgid = cred->fsgid;
        context->personality = tsk->personality;
 
        ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
@@ -1619,32 +1632,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
                                 (context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
                                 context->return_code);
 
-       spin_lock_irq(&tsk->sighand->siglock);
-       if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
-               tty = tsk->signal->tty->name;
-       else
-               tty = "(none)";
-       spin_unlock_irq(&tsk->sighand->siglock);
-
        audit_log_format(ab,
-                 " a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
-                 " ppid=%d pid=%d auid=%u uid=%u gid=%u"
-                 " euid=%u suid=%u fsuid=%u"
-                 " egid=%u sgid=%u fsgid=%u tty=%s ses=%u",
-                 context->argv[0],
-                 context->argv[1],
-                 context->argv[2],
-                 context->argv[3],
-                 context->name_count,
-                 context->ppid,
-                 context->pid,
-                 tsk->loginuid,
-                 context->uid,
-                 context->gid,
-                 context->euid, context->suid, context->fsuid,
-                 context->egid, context->sgid, context->fsgid, tty,
-                 tsk->sessionid);
-
+                        " a0=%lx a1=%lx a2=%lx a3=%lx items=%d",
+                        context->argv[0],
+                        context->argv[1],
+                        context->argv[2],
+                        context->argv[3],
+                        context->name_count);
 
        audit_log_task_info(ab, tsk);
        audit_log_key(ab, context->filterkey);
index a232bb59d93fa220e3720ce3d068b44d96ca24f3..1f5e55dda955544ca4a3f1f944e967f6b561bea2 100644 (file)
@@ -180,7 +180,8 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
                return has_ns_capability(current, ns, CAP_SYS_PTRACE);
 }
 
-int __ptrace_may_access(struct task_struct *task, unsigned int mode)
+/* Returns 0 on success, -errno on denial. */
+static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
 {
        const struct cred *cred = current_cred(), *tcred;
 
index 16aa2d424985fa524e4eb8a60122be8d7ad817b0..bbbd276659ba5edad0e01b915d03639c7980803f 100644 (file)
@@ -18,14 +18,22 @@ HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include
 bpf-direct-objs := bpf-direct.o
 
 # Try to match the kernel target.
-ifeq ($(CONFIG_64BIT),)
-HOSTCFLAGS_bpf-direct.o += -m32
-HOSTCFLAGS_dropper.o += -m32
-HOSTCFLAGS_bpf-helper.o += -m32
-HOSTCFLAGS_bpf-fancy.o += -m32
-HOSTLOADLIBES_bpf-direct += -m32
-HOSTLOADLIBES_bpf-fancy += -m32
-HOSTLOADLIBES_dropper += -m32
+ifndef CONFIG_64BIT
+
+# s390 has -m31 flag to build 31 bit binaries
+ifndef CONFIG_S390
+MFLAG = -m32
+else
+MFLAG = -m31
+endif
+
+HOSTCFLAGS_bpf-direct.o += $(MFLAG)
+HOSTCFLAGS_dropper.o += $(MFLAG)
+HOSTCFLAGS_bpf-helper.o += $(MFLAG)
+HOSTCFLAGS_bpf-fancy.o += $(MFLAG)
+HOSTLOADLIBES_bpf-direct += $(MFLAG)
+HOSTLOADLIBES_bpf-fancy += $(MFLAG)
+HOSTLOADLIBES_dropper += $(MFLAG)
 endif
 
 # Tell kbuild to always build the programs
index 643279dd30fbbe6151578e328be864745cc03216..38ee70f3cd5b970101edd6d2edb52b4df8d73d14 100644 (file)
@@ -59,6 +59,16 @@ void seccomp_bpf_print(struct sock_filter *filter, size_t count);
 #define FIND_LABEL(labels, label) seccomp_bpf_label((labels), #label)
 
 #define EXPAND(...) __VA_ARGS__
+
+/* Ensure that we load the logically correct offset. */
+#if __BYTE_ORDER == __LITTLE_ENDIAN
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
+#elif __BYTE_ORDER == __BIG_ENDIAN
+#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
+#else
+#error "Unknown endianness"
+#endif
+
 /* Map all width-sensitive operations */
 #if __BITS_PER_LONG == 32
 
@@ -70,21 +80,16 @@ void seccomp_bpf_print(struct sock_filter *filter, size_t count);
 #define JLE(x, jt) JLE32(x, EXPAND(jt))
 #define JA(x, jt) JA32(x, EXPAND(jt))
 #define ARG(i) ARG_32(i)
-#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
 
 #elif __BITS_PER_LONG == 64
 
 /* Ensure that we load the logically correct offset. */
 #if __BYTE_ORDER == __LITTLE_ENDIAN
 #define ENDIAN(_lo, _hi) _lo, _hi
-#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
 #define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
 #elif __BYTE_ORDER == __BIG_ENDIAN
 #define ENDIAN(_lo, _hi) _hi, _lo
-#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
 #define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
-#else
-#error "Unknown endianness"
 #endif
 
 union arg64 {
index 8901501425f42beb8a0e23dade6b4e4200019bf1..eb5484504f506490eb7c4f90a81cba92b7020c40 100644 (file)
@@ -33,6 +33,9 @@ char *evm_config_xattrnames[] = {
 #endif
 #ifdef CONFIG_SECURITY_SMACK
        XATTR_NAME_SMACK,
+#endif
+#ifdef CONFIG_IMA_APPRAISE
+       XATTR_NAME_IMA,
 #endif
        XATTR_NAME_CAPS,
        NULL
index 399641c3e84644821e711c0ade263a2ee89cdc49..d82a5a13d8551ca1b857a13c401f706340b88850 100644 (file)
@@ -22,7 +22,7 @@
 #include "integrity.h"
 
 static struct rb_root integrity_iint_tree = RB_ROOT;
-static DEFINE_SPINLOCK(integrity_iint_lock);
+static DEFINE_RWLOCK(integrity_iint_lock);
 static struct kmem_cache *iint_cache __read_mostly;
 
 int iint_initialized;
@@ -35,8 +35,6 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
        struct integrity_iint_cache *iint;
        struct rb_node *n = integrity_iint_tree.rb_node;
 
-       assert_spin_locked(&integrity_iint_lock);
-
        while (n) {
                iint = rb_entry(n, struct integrity_iint_cache, rb_node);
 
@@ -63,9 +61,9 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
        if (!IS_IMA(inode))
                return NULL;
 
-       spin_lock(&integrity_iint_lock);
+       read_lock(&integrity_iint_lock);
        iint = __integrity_iint_find(inode);
-       spin_unlock(&integrity_iint_lock);
+       read_unlock(&integrity_iint_lock);
 
        return iint;
 }
@@ -74,59 +72,53 @@ static void iint_free(struct integrity_iint_cache *iint)
 {
        iint->version = 0;
        iint->flags = 0UL;
+       iint->ima_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
        kmem_cache_free(iint_cache, iint);
 }
 
 /**
- * integrity_inode_alloc - allocate an iint associated with an inode
+ * integrity_inode_get - find or allocate an iint associated with an inode
  * @inode: pointer to the inode
+ * @return: allocated iint
+ *
+ * Caller must lock i_mutex
  */
-int integrity_inode_alloc(struct inode *inode)
+struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
 {
        struct rb_node **p;
-       struct rb_node *new_node, *parent = NULL;
-       struct integrity_iint_cache *new_iint, *test_iint;
-       int rc;
+       struct rb_node *node, *parent = NULL;
+       struct integrity_iint_cache *iint, *test_iint;
 
-       new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
-       if (!new_iint)
-               return -ENOMEM;
+       iint = integrity_iint_find(inode);
+       if (iint)
+               return iint;
 
-       new_iint->inode = inode;
-       new_node = &new_iint->rb_node;
+       iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
+       if (!iint)
+               return NULL;
 
-       mutex_lock(&inode->i_mutex);    /* i_flags */
-       spin_lock(&integrity_iint_lock);
+       write_lock(&integrity_iint_lock);
 
        p = &integrity_iint_tree.rb_node;
        while (*p) {
                parent = *p;
                test_iint = rb_entry(parent, struct integrity_iint_cache,
                                     rb_node);
-               rc = -EEXIST;
                if (inode < test_iint->inode)
                        p = &(*p)->rb_left;
-               else if (inode > test_iint->inode)
-                       p = &(*p)->rb_right;
                else
-                       goto out_err;
+                       p = &(*p)->rb_right;
        }
 
+       iint->inode = inode;
+       node = &iint->rb_node;
        inode->i_flags |= S_IMA;
-       rb_link_node(new_node, parent, p);
-       rb_insert_color(new_node, &integrity_iint_tree);
+       rb_link_node(node, parent, p);
+       rb_insert_color(node, &integrity_iint_tree);
 
-       spin_unlock(&integrity_iint_lock);
-       mutex_unlock(&inode->i_mutex);  /* i_flags */
-
-       return 0;
-out_err:
-       spin_unlock(&integrity_iint_lock);
-       mutex_unlock(&inode->i_mutex);  /* i_flags */
-       iint_free(new_iint);
-
-       return rc;
+       write_unlock(&integrity_iint_lock);
+       return iint;
 }
 
 /**
@@ -142,10 +134,10 @@ void integrity_inode_free(struct inode *inode)
        if (!IS_IMA(inode))
                return;
 
-       spin_lock(&integrity_iint_lock);
+       write_lock(&integrity_iint_lock);
        iint = __integrity_iint_find(inode);
        rb_erase(&iint->rb_node, &integrity_iint_tree);
-       spin_unlock(&integrity_iint_lock);
+       write_unlock(&integrity_iint_lock);
 
        iint_free(iint);
 }
@@ -157,7 +149,7 @@ static void init_once(void *foo)
        memset(iint, 0, sizeof *iint);
        iint->version = 0;
        iint->flags = 0UL;
-       mutex_init(&iint->mutex);
+       iint->ima_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
 }
 
index b9c1219924f197b7195650ea99cc0c28b79aaa56..d232c73647ae46ee0f8295a4965419f5b044ca99 100644 (file)
@@ -11,6 +11,7 @@ config IMA
        select CRYPTO_SHA1
        select TCG_TPM if HAS_IOMEM && !UML
        select TCG_TIS if TCG_TPM && X86
+       select TCG_IBMVTPM if TCG_TPM && PPC64
        help
          The Trusted Computing Group(TCG) runtime Integrity
          Measurement Architecture(IMA) maintains a list of hash
@@ -55,3 +56,18 @@ config IMA_LSM_RULES
        default y
        help
          Disabling this option will disregard LSM based policy rules.
+
+config IMA_APPRAISE
+       bool "Appraise integrity measurements"
+       depends on IMA
+       default n
+       help
+         This option enables local measurement integrity appraisal.
+         It requires the system to be labeled with a security extended
+         attribute containing the file hash measurement.  To protect
+         the security extended attributes from offline attack, enable
+         and configure EVM.
+
+         For more information on integrity appraisal refer to:
+         <http://linux-ima.sourceforge.net>
+         If unsure, say N.
index 5f740f6971e1f59c6d3e7bfb6254d6350f3f55fa..3f2ca6bdc384ecbb5b3de4dc53d32c9a802d5958 100644 (file)
@@ -8,3 +8,4 @@ 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-$(CONFIG_IMA_AUDIT) += ima_audit.o
+ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
index e7c99fd0d2236e7d3a923733bdf7e622f8cd993a..8180adde10b72486644ba9eb1902c02b855a06c2 100644 (file)
@@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
 extern int ima_initialized;
 extern int ima_used_chip;
 extern char *ima_hash;
+extern int ima_appraise;
 
 /* IMA inode template definition */
 struct ima_template_data {
@@ -107,11 +108,14 @@ static inline unsigned long ima_hash_key(u8 *digest)
 }
 
 /* LIM API function definitions */
+int ima_get_action(struct inode *inode, int mask, int function);
 int ima_must_measure(struct inode *inode, int mask, int function);
 int ima_collect_measurement(struct integrity_iint_cache *iint,
                            struct file *file);
 void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
                           const unsigned char *filename);
+void ima_audit_measurement(struct integrity_iint_cache *iint,
+                          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);
@@ -123,14 +127,45 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
 struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
 
 /* IMA policy related functions */
-enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
+enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR };
 
-int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
+                    int flags);
 void ima_init_policy(void);
 void ima_update_policy(void);
 ssize_t ima_parse_add_rule(char *);
 void ima_delete_rules(void);
 
+/* Appraise integrity measurements */
+#define IMA_APPRAISE_ENFORCE   0x01
+#define IMA_APPRAISE_FIX       0x02
+
+#ifdef CONFIG_IMA_APPRAISE
+int ima_appraise_measurement(struct integrity_iint_cache *iint,
+                            struct file *file, const unsigned char *filename);
+int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask);
+void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
+
+#else
+static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
+                                          struct file *file,
+                                          const unsigned char *filename)
+{
+       return INTEGRITY_UNKNOWN;
+}
+
+static inline int ima_must_appraise(struct inode *inode,
+                                   enum ima_hooks func, int mask)
+{
+       return 0;
+}
+
+static inline void ima_update_xattr(struct integrity_iint_cache *iint,
+                                   struct file *file)
+{
+}
+#endif
+
 /* LSM based policy rules require audit */
 #ifdef CONFIG_IMA_LSM_RULES
 
index 032ff03ad9078bc956faa6c6e6d6759b6e2a0420..b356884fb3ef9c17dbc3d71396ac01c98e20a400 100644 (file)
@@ -9,13 +9,17 @@
  * License.
  *
  * File: ima_api.c
- *     Implements must_measure, collect_measurement, store_measurement,
- *     and store_template.
+ *     Implements must_appraise_or_measure, collect_measurement,
+ *     appraise_measurement, store_measurement and store_template.
  */
 #include <linux/module.h>
 #include <linux/slab.h>
-
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/evm.h>
 #include "ima.h"
+
 static const char *IMA_TEMPLATE_NAME = "ima";
 
 /*
@@ -93,7 +97,7 @@ err_out:
 }
 
 /**
- * ima_must_measure - measure decision based on policy.
+ * ima_get_action - appraise & 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 (FILE_CHECK, BPRM_CHECK, FILE_MMAP)
@@ -105,15 +109,22 @@ err_out:
  *     mask: contains the permission mask
  *     fsmagic: hex value
  *
- * Return 0 to measure. For matching a DONT_MEASURE policy, no policy,
- * or other error, return an error code.
-*/
-int ima_must_measure(struct inode *inode, int mask, int function)
+ * Returns IMA_MEASURE, IMA_APPRAISE mask.
+ *
+ */
+int ima_get_action(struct inode *inode, int mask, int function)
 {
-       int must_measure;
+       int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
+
+       if (!ima_appraise)
+               flags &= ~IMA_APPRAISE;
 
-       must_measure = ima_match_policy(inode, function, mask);
-       return must_measure ? 0 : -EACCES;
+       return ima_match_policy(inode, function, mask, flags);
+}
+
+int ima_must_measure(struct inode *inode, int mask, int function)
+{
+       return ima_match_policy(inode, function, mask, IMA_MEASURE);
 }
 
 /*
@@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function)
 int ima_collect_measurement(struct integrity_iint_cache *iint,
                            struct file *file)
 {
-       int result = -EEXIST;
+       struct inode *inode = file->f_dentry->d_inode;
+       const char *filename = file->f_dentry->d_name.name;
+       int result = 0;
 
-       if (!(iint->flags & IMA_MEASURED)) {
+       if (!(iint->flags & IMA_COLLECTED)) {
                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->ima_xattr.type = IMA_XATTR_DIGEST;
+               result = ima_calc_hash(file, iint->ima_xattr.digest);
+               if (!result) {
                        iint->version = i_version;
+                       iint->flags |= IMA_COLLECTED;
+               }
        }
+       if (result)
+               integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
+                                   filename, "collect_data", "failed",
+                                   result, 0);
        return result;
 }
 
@@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
        struct ima_template_entry *entry;
        int violation = 0;
 
+       if (iint->flags & IMA_MEASURED)
+               return;
+
        entry = kmalloc(sizeof(*entry), GFP_KERNEL);
        if (!entry) {
                integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
@@ -174,7 +196,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
                return;
        }
        memset(&entry->template, 0, sizeof(entry->template));
-       memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
+       memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE);
        strcpy(entry->template.file_name,
               (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
               file->f_dentry->d_name.name : filename);
@@ -185,3 +207,33 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
        if (result < 0)
                kfree(entry);
 }
+
+void ima_audit_measurement(struct integrity_iint_cache *iint,
+                          const unsigned char *filename)
+{
+       struct audit_buffer *ab;
+       char hash[(IMA_DIGEST_SIZE * 2) + 1];
+       int i;
+
+       if (iint->flags & IMA_AUDITED)
+               return;
+
+       for (i = 0; i < IMA_DIGEST_SIZE; i++)
+               hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]);
+       hash[i * 2] = '\0';
+
+       ab = audit_log_start(current->audit_context, GFP_KERNEL,
+                            AUDIT_INTEGRITY_RULE);
+       if (!ab)
+               return;
+
+       audit_log_format(ab, "file=");
+       audit_log_untrustedstring(ab, filename);
+       audit_log_format(ab, " hash=");
+       audit_log_untrustedstring(ab, hash);
+
+       audit_log_task_info(ab, current);
+       audit_log_end(ab);
+
+       iint->flags |= IMA_AUDITED;
+}
diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c
new file mode 100644 (file)
index 0000000..0aa43bd
--- /dev/null
@@ -0,0 +1,263 @@
+/*
+ * Copyright (C) 2011 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/module.h>
+#include <linux/file.h>
+#include <linux/fs.h>
+#include <linux/xattr.h>
+#include <linux/magic.h>
+#include <linux/ima.h>
+#include <linux/evm.h>
+
+#include "ima.h"
+
+static int __init default_appraise_setup(char *str)
+{
+       if (strncmp(str, "off", 3) == 0)
+               ima_appraise = 0;
+       else if (strncmp(str, "fix", 3) == 0)
+               ima_appraise = IMA_APPRAISE_FIX;
+       return 1;
+}
+
+__setup("ima_appraise=", default_appraise_setup);
+
+/*
+ * ima_must_appraise - set appraise flag
+ *
+ * Return 1 to appraise
+ */
+int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask)
+{
+       if (!ima_appraise)
+               return 0;
+
+       return ima_match_policy(inode, func, mask, IMA_APPRAISE);
+}
+
+static void ima_fix_xattr(struct dentry *dentry,
+                         struct integrity_iint_cache *iint)
+{
+       iint->ima_xattr.type = IMA_XATTR_DIGEST;
+       __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
+                             sizeof iint->ima_xattr, 0);
+}
+
+/*
+ * ima_appraise_measurement - appraise file measurement
+ *
+ * Call evm_verifyxattr() to verify the integrity of 'security.ima'.
+ * Assuming success, compare the xattr hash with the collected measurement.
+ *
+ * Return 0 on success, error code otherwise
+ */
+int ima_appraise_measurement(struct integrity_iint_cache *iint,
+                            struct file *file, const unsigned char *filename)
+{
+       struct dentry *dentry = file->f_dentry;
+       struct inode *inode = dentry->d_inode;
+       struct evm_ima_xattr_data *xattr_value = NULL;
+       enum integrity_status status = INTEGRITY_UNKNOWN;
+       const char *op = "appraise_data";
+       char *cause = "unknown";
+       int rc;
+
+       if (!ima_appraise)
+               return 0;
+       if (!inode->i_op->getxattr)
+               return INTEGRITY_UNKNOWN;
+
+       if (iint->flags & IMA_APPRAISED)
+               return iint->ima_status;
+
+       rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
+                               0, GFP_NOFS);
+       if (rc <= 0) {
+               if (rc && rc != -ENODATA)
+                       goto out;
+
+               cause = "missing-hash";
+               status =
+                   (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
+               goto out;
+       }
+
+       status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
+       if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
+               if ((status == INTEGRITY_NOLABEL)
+                   || (status == INTEGRITY_NOXATTRS))
+                       cause = "missing-HMAC";
+               else if (status == INTEGRITY_FAIL)
+                       cause = "invalid-HMAC";
+               goto out;
+       }
+
+       switch (xattr_value->type) {
+       case IMA_XATTR_DIGEST:
+               rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
+                           IMA_DIGEST_SIZE);
+               if (rc) {
+                       cause = "invalid-hash";
+                       status = INTEGRITY_FAIL;
+                       print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
+                                            xattr_value, sizeof(*xattr_value));
+                       print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
+                                            (u8 *)&iint->ima_xattr,
+                                            sizeof iint->ima_xattr);
+                       break;
+               }
+               status = INTEGRITY_PASS;
+               break;
+       case EVM_IMA_XATTR_DIGSIG:
+               iint->flags |= IMA_DIGSIG;
+               rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
+                                            xattr_value->digest, rc - 1,
+                                            iint->ima_xattr.digest,
+                                            IMA_DIGEST_SIZE);
+               if (rc == -EOPNOTSUPP) {
+                       status = INTEGRITY_UNKNOWN;
+               } else if (rc) {
+                       cause = "invalid-signature";
+                       status = INTEGRITY_FAIL;
+               } else {
+                       status = INTEGRITY_PASS;
+               }
+               break;
+       default:
+               status = INTEGRITY_UNKNOWN;
+               cause = "unknown-ima-data";
+               break;
+       }
+
+out:
+       if (status != INTEGRITY_PASS) {
+               if ((ima_appraise & IMA_APPRAISE_FIX) &&
+                   (!xattr_value ||
+                    xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
+                       ima_fix_xattr(dentry, iint);
+                       status = INTEGRITY_PASS;
+               }
+               integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
+                                   op, cause, rc, 0);
+       } else {
+               iint->flags |= IMA_APPRAISED;
+       }
+       iint->ima_status = status;
+       kfree(xattr_value);
+       return status;
+}
+
+/*
+ * ima_update_xattr - update 'security.ima' hash value
+ */
+void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
+{
+       struct dentry *dentry = file->f_dentry;
+       int rc = 0;
+
+       /* do not collect and update hash for digital signatures */
+       if (iint->flags & IMA_DIGSIG)
+               return;
+
+       rc = ima_collect_measurement(iint, file);
+       if (rc < 0)
+               return;
+
+       ima_fix_xattr(dentry, iint);
+}
+
+/**
+ * ima_inode_post_setattr - reflect file metadata changes
+ * @dentry: pointer to the affected dentry
+ *
+ * Changes to a dentry's metadata might result in needing to appraise.
+ *
+ * This function is called from notify_change(), which expects the caller
+ * to lock the inode's i_mutex.
+ */
+void ima_inode_post_setattr(struct dentry *dentry)
+{
+       struct inode *inode = dentry->d_inode;
+       struct integrity_iint_cache *iint;
+       int must_appraise, rc;
+
+       if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
+           || !inode->i_op->removexattr)
+               return;
+
+       must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
+       iint = integrity_iint_find(inode);
+       if (iint) {
+               if (must_appraise)
+                       iint->flags |= IMA_APPRAISE;
+               else
+                       iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
+       }
+       if (!must_appraise)
+               rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
+       return;
+}
+
+/*
+ * ima_protect_xattr - protect 'security.ima'
+ *
+ * Ensure that not just anyone can modify or remove 'security.ima'.
+ */
+static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
+                            const void *xattr_value, size_t xattr_value_len)
+{
+       if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
+               if (!capable(CAP_SYS_ADMIN))
+                       return -EPERM;
+               return 1;
+       }
+       return 0;
+}
+
+static void ima_reset_appraise_flags(struct inode *inode)
+{
+       struct integrity_iint_cache *iint;
+
+       if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
+               return;
+
+       iint = integrity_iint_find(inode);
+       if (!iint)
+               return;
+
+       iint->flags &= ~IMA_DONE_MASK;
+       return;
+}
+
+int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
+                      const void *xattr_value, size_t xattr_value_len)
+{
+       int result;
+
+       result = ima_protect_xattr(dentry, xattr_name, xattr_value,
+                                  xattr_value_len);
+       if (result == 1) {
+               ima_reset_appraise_flags(dentry->d_inode);
+               result = 0;
+       }
+       return result;
+}
+
+int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
+{
+       int result;
+
+       result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
+       if (result == 1) {
+               ima_reset_appraise_flags(dentry->d_inode);
+               result = 0;
+       }
+       return result;
+}
index 9b3ade7468b283412fa962fa291f0c3f6413812f..b21ee5b5495a8a4a3bf3e5626cd367d16158757f 100644 (file)
@@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest)
        struct scatterlist sg[1];
        loff_t i_size, offset = 0;
        char *rbuf;
-       int rc;
+       int rc, read = 0;
 
        rc = init_desc(&desc);
        if (rc != 0)
@@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest)
                rc = -ENOMEM;
                goto out;
        }
+       if (!(file->f_mode & FMODE_READ)) {
+               file->f_mode |= FMODE_READ;
+               read = 1;
+       }
        i_size = i_size_read(file->f_dentry->d_inode);
        while (offset < i_size) {
                int rbuf_len;
@@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest)
        kfree(rbuf);
        if (!rc)
                rc = crypto_hash_final(&desc, digest);
+       if (read)
+               file->f_mode &= ~FMODE_READ;
 out:
        crypto_free_hash(desc.tfm);
        return rc;
index be8294915cf7f65524a21ab2dd3101ecb0a6edd9..73c9a268253e44718c1f02395f2ffb1f905511ca 100644 (file)
 #include <linux/mount.h>
 #include <linux/mman.h>
 #include <linux/slab.h>
+#include <linux/xattr.h>
 #include <linux/ima.h>
 
 #include "ima.h"
 
 int ima_initialized;
 
+#ifdef CONFIG_IMA_APPRAISE
+int ima_appraise = IMA_APPRAISE_ENFORCE;
+#else
+int ima_appraise;
+#endif
+
 char *ima_hash = "sha1";
 static int __init hash_setup(char *str)
 {
@@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file)
        struct dentry *dentry = file->f_path.dentry;
        struct inode *inode = dentry->d_inode;
        fmode_t mode = file->f_mode;
-       int rc;
+       int must_measure;
        bool send_tomtou = false, send_writers = false;
        unsigned char *pathname = NULL, *pathbuf = NULL;
 
@@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file)
                goto out;
        }
 
-       rc = ima_must_measure(inode, MAY_READ, FILE_CHECK);
-       if (rc < 0)
+       must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
+       if (!must_measure)
                goto out;
 
        if (atomic_read(&inode->i_writecount) > 0)
@@ -100,17 +107,21 @@ out:
 }
 
 static void ima_check_last_writer(struct integrity_iint_cache *iint,
-                                 struct inode *inode,
-                                 struct file *file)
+                                 struct inode *inode, struct file *file)
 {
        fmode_t mode = file->f_mode;
 
-       mutex_lock(&iint->mutex);
-       if (mode & FMODE_WRITE &&
-           atomic_read(&inode->i_writecount) == 1 &&
-           iint->version != inode->i_version)
-               iint->flags &= ~IMA_MEASURED;
-       mutex_unlock(&iint->mutex);
+       if (!(mode & FMODE_WRITE))
+               return;
+
+       mutex_lock(&inode->i_mutex);
+       if (atomic_read(&inode->i_writecount) == 1 &&
+           iint->version != inode->i_version) {
+               iint->flags &= ~IMA_DONE_MASK;
+               if (iint->flags & IMA_APPRAISE)
+                       ima_update_xattr(iint, file);
+       }
+       mutex_unlock(&inode->i_mutex);
 }
 
 /**
@@ -140,28 +151,37 @@ static int process_measurement(struct file *file, const unsigned char *filename,
        struct inode *inode = file->f_dentry->d_inode;
        struct integrity_iint_cache *iint;
        unsigned char *pathname = NULL, *pathbuf = NULL;
-       int rc = 0;
+       int rc = -ENOMEM, action, must_appraise;
 
        if (!ima_initialized || !S_ISREG(inode->i_mode))
                return 0;
 
-       rc = ima_must_measure(inode, mask, function);
-       if (rc != 0)
-               return rc;
-retry:
-       iint = integrity_iint_find(inode);
-       if (!iint) {
-               rc = integrity_inode_alloc(inode);
-               if (!rc || rc == -EEXIST)
-                       goto retry;
-               return rc;
-       }
+       /* Determine if in appraise/audit/measurement policy,
+        * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask.  */
+       action = ima_get_action(inode, mask, function);
+       if (!action)
+               return 0;
 
-       mutex_lock(&iint->mutex);
+       must_appraise = action & IMA_APPRAISE;
 
-       rc = iint->flags & IMA_MEASURED ? 1 : 0;
-       if (rc != 0)
+       mutex_lock(&inode->i_mutex);
+
+       iint = integrity_inode_get(inode);
+       if (!iint)
+               goto out;
+
+       /* Determine if already appraised/measured based on bitmask
+        * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
+        *  IMA_AUDIT, IMA_AUDITED) */
+       iint->flags |= action;
+       action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
+
+       /* Nothing to do, just return existing appraised status */
+       if (!action) {
+               if (iint->flags & IMA_APPRAISED)
+                       rc = iint->ima_status;
                goto out;
+       }
 
        rc = ima_collect_measurement(iint, file);
        if (rc != 0)
@@ -177,11 +197,18 @@ retry:
                                pathname = NULL;
                }
        }
-       ima_store_measurement(iint, file, !pathname ? filename : pathname);
+       if (action & IMA_MEASURE)
+               ima_store_measurement(iint, file,
+                                     !pathname ? filename : pathname);
+       if (action & IMA_APPRAISE)
+               rc = ima_appraise_measurement(iint, file,
+                                             !pathname ? filename : pathname);
+       if (action & IMA_AUDIT)
+               ima_audit_measurement(iint, !pathname ? filename : pathname);
        kfree(pathbuf);
 out:
-       mutex_unlock(&iint->mutex);
-       return rc;
+       mutex_unlock(&inode->i_mutex);
+       return (rc && must_appraise) ? -EACCES : 0;
 }
 
 /**
@@ -197,14 +224,14 @@ out:
  */
 int ima_file_mmap(struct file *file, unsigned long prot)
 {
-       int rc;
+       int rc = 0;
 
        if (!file)
                return 0;
        if (prot & PROT_EXEC)
                rc = process_measurement(file, file->f_dentry->d_name.name,
                                         MAY_EXEC, FILE_MMAP);
-       return 0;
+       return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
 }
 
 /**
@@ -228,7 +255,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
                                 (strcmp(bprm->filename, bprm->interp) == 0) ?
                                 bprm->filename : bprm->interp,
                                 MAY_EXEC, BPRM_CHECK);
-       return 0;
+       return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
 }
 
 /**
@@ -249,7 +276,7 @@ int ima_file_check(struct file *file, int mask)
        rc = process_measurement(file, file->f_dentry->d_name.name,
                                 mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
                                 FILE_CHECK);
-       return 0;
+       return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
index 1a9583008aaef1bf1fe6868f921408ac83d06a63..cda903131dbfb2122abb027261db38ed09e83a17 100644 (file)
 #define IMA_MASK       0x0002
 #define IMA_FSMAGIC    0x0004
 #define IMA_UID                0x0008
+#define IMA_FOWNER     0x0010
 
-enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
+#define UNKNOWN                0
+#define MEASURE                0x0001  /* same as IMA_MEASURE */
+#define DONT_MEASURE   0x0002
+#define APPRAISE       0x0004  /* same as IMA_APPRAISE */
+#define DONT_APPRAISE  0x0008
+#define AUDIT          0x0040
 
 #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 ima_rule_entry {
        struct list_head list;
-       enum ima_action action;
+       int action;
        unsigned int flags;
        enum ima_hooks func;
        int mask;
        unsigned long fsmagic;
        uid_t uid;
+       uid_t fowner;
        struct {
                void *rule;     /* LSM file metadata specific */
                int type;       /* audit type */
@@ -48,7 +55,7 @@ struct ima_measure_rule_entry {
 
 /*
  * Without LSM specific knowledge, the default policy can only be
- * written in terms of .action, .func, .mask, .fsmagic, and .uid
+ * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
  */
 
 /*
@@ -57,7 +64,7 @@ struct ima_measure_rule_entry {
  * normal users can easily run the machine out of memory simply building
  * and running executables.
  */
-static struct ima_measure_rule_entry default_rules[] = {
+static struct ima_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},
@@ -75,19 +82,41 @@ static struct ima_measure_rule_entry default_rules[] = {
         .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 struct ima_rule_entry default_appraise_rules[] = {
+       {.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC},
+       {.action = APPRAISE,.fowner = 0,.flags = IMA_FOWNER},
+};
+
+static LIST_HEAD(ima_default_rules);
+static LIST_HEAD(ima_policy_rules);
+static struct list_head *ima_rules;
 
-static DEFINE_MUTEX(ima_measure_mutex);
+static DEFINE_MUTEX(ima_rules_mutex);
 
 static bool ima_use_tcb __initdata;
-static int __init default_policy_setup(char *str)
+static int __init default_measure_policy_setup(char *str)
 {
        ima_use_tcb = 1;
        return 1;
 }
-__setup("ima_tcb", default_policy_setup);
+__setup("ima_tcb", default_measure_policy_setup);
+
+static bool ima_use_appraise_tcb __initdata;
+static int __init default_appraise_policy_setup(char *str)
+{
+       ima_use_appraise_tcb = 1;
+       return 1;
+}
+__setup("ima_appraise_tcb", default_appraise_policy_setup);
 
 /**
  * ima_match_rules - determine whether an inode matches the measure rule.
@@ -98,7 +127,7 @@ __setup("ima_tcb", default_policy_setup);
  *
  * Returns true on rule match, false on failure.
  */
-static bool ima_match_rules(struct ima_measure_rule_entry *rule,
+static bool ima_match_rules(struct ima_rule_entry *rule,
                            struct inode *inode, enum ima_hooks func, int mask)
 {
        struct task_struct *tsk = current;
@@ -114,6 +143,8 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
                return false;
        if ((rule->flags & IMA_UID) && rule->uid != cred->uid)
                return false;
+       if ((rule->flags & IMA_FOWNER) && rule->fowner != inode->i_uid)
+               return false;
        for (i = 0; i < MAX_LSM_RULES; i++) {
                int rc = 0;
                u32 osid, sid;
@@ -163,39 +194,61 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
  * 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)
+int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
+                    int flags)
 {
-       struct ima_measure_rule_entry *entry;
+       struct ima_rule_entry *entry;
+       int action = 0, actmask = flags | (flags << 1);
+
+       list_for_each_entry(entry, ima_rules, list) {
+
+               if (!(entry->action & actmask))
+                       continue;
+
+               if (!ima_match_rules(entry, inode, func, mask))
+                       continue;
 
-       list_for_each_entry(entry, ima_measure, list) {
-               bool rc;
+               action |= entry->action & IMA_DO_MASK;
+               if (entry->action & IMA_DO_MASK)
+                       actmask &= ~(entry->action | entry->action << 1);
+               else
+                       actmask &= ~(entry->action | entry->action >> 1);
 
-               rc = ima_match_rules(entry, inode, func, mask);
-               if (rc)
-                       return entry->action;
+               if (!actmask)
+                       break;
        }
-       return 0;
+
+       return action;
 }
 
 /**
  * ima_init_policy - initialize the default measure rules.
  *
- * ima_measure points to either the measure_default_rules or the
- * the new measure_policy_rules.
+ * ima_rules points to either the ima_default_rules or the
+ * the new ima_policy_rules.
  */
 void __init ima_init_policy(void)
 {
-       int i, entries;
+       int i, measure_entries, appraise_entries;
 
        /* if !ima_use_tcb set entries = 0 so we load NO default rules */
-       if (ima_use_tcb)
-               entries = ARRAY_SIZE(default_rules);
-       else
-               entries = 0;
-
-       for (i = 0; i < entries; i++)
-               list_add_tail(&default_rules[i].list, &measure_default_rules);
-       ima_measure = &measure_default_rules;
+       measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0;
+       appraise_entries = ima_use_appraise_tcb ?
+                        ARRAY_SIZE(default_appraise_rules) : 0;
+       
+       for (i = 0; i < measure_entries + appraise_entries; i++) {
+               if (i < measure_entries)
+                       list_add_tail(&default_rules[i].list,
+                                     &ima_default_rules);
+               else {
+                       int j = i - measure_entries;
+
+                       list_add_tail(&default_appraise_rules[j].list,
+                                     &ima_default_rules);
+               }
+       }
+
+       ima_rules = &ima_default_rules;
 }
 
 /**
@@ -212,8 +265,8 @@ void ima_update_policy(void)
        int result = 1;
        int audit_info = 0;
 
-       if (ima_measure == &measure_default_rules) {
-               ima_measure = &measure_policy_rules;
+       if (ima_rules == &ima_default_rules) {
+               ima_rules = &ima_policy_rules;
                cause = "complete";
                result = 0;
        }
@@ -224,14 +277,19 @@ void ima_update_policy(void)
 enum {
        Opt_err = -1,
        Opt_measure = 1, Opt_dont_measure,
+       Opt_appraise, Opt_dont_appraise,
+       Opt_audit,
        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
+       Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner
 };
 
 static match_table_t policy_tokens = {
        {Opt_measure, "measure"},
        {Opt_dont_measure, "dont_measure"},
+       {Opt_appraise, "appraise"},
+       {Opt_dont_appraise, "dont_appraise"},
+       {Opt_audit, "audit"},
        {Opt_obj_user, "obj_user=%s"},
        {Opt_obj_role, "obj_role=%s"},
        {Opt_obj_type, "obj_type=%s"},
@@ -242,10 +300,11 @@ static match_table_t policy_tokens = {
        {Opt_mask, "mask=%s"},
        {Opt_fsmagic, "fsmagic=%s"},
        {Opt_uid, "uid=%s"},
+       {Opt_fowner, "fowner=%s"},
        {Opt_err, NULL}
 };
 
-static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
+static int ima_lsm_rule_init(struct ima_rule_entry *entry,
                             char *args, int lsm_rule, int audit_type)
 {
        int result;
@@ -269,7 +328,7 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
        audit_log_format(ab, " ");
 }
 
-static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
+static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
 {
        struct audit_buffer *ab;
        char *p;
@@ -278,6 +337,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
        ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
 
        entry->uid = -1;
+       entry->fowner = -1;
        entry->action = UNKNOWN;
        while ((p = strsep(&rule, " \t")) != NULL) {
                substring_t args[MAX_OPT_ARGS];
@@ -306,11 +366,35 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
 
                        entry->action = DONT_MEASURE;
                        break;
+               case Opt_appraise:
+                       ima_log_string(ab, "action", "appraise");
+
+                       if (entry->action != UNKNOWN)
+                               result = -EINVAL;
+
+                       entry->action = APPRAISE;
+                       break;
+               case Opt_dont_appraise:
+                       ima_log_string(ab, "action", "dont_appraise");
+
+                       if (entry->action != UNKNOWN)
+                               result = -EINVAL;
+
+                       entry->action = DONT_APPRAISE;
+                       break;
+               case Opt_audit:
+                       ima_log_string(ab, "action", "audit");
+
+                       if (entry->action != UNKNOWN)
+                               result = -EINVAL;
+
+                       entry->action = AUDIT;
+                       break;
                case Opt_func:
                        ima_log_string(ab, "func", args[0].from);
 
                        if (entry->func)
-                               result  = -EINVAL;
+                               result = -EINVAL;
 
                        if (strcmp(args[0].from, "FILE_CHECK") == 0)
                                entry->func = FILE_CHECK;
@@ -375,6 +459,23 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
                                        entry->flags |= IMA_UID;
                        }
                        break;
+               case Opt_fowner:
+                       ima_log_string(ab, "fowner", args[0].from);
+
+                       if (entry->fowner != -1) {
+                               result = -EINVAL;
+                               break;
+                       }
+
+                       result = strict_strtoul(args[0].from, 10, &lnum);
+                       if (!result) {
+                               entry->fowner = (uid_t) lnum;
+                               if (entry->fowner != lnum)
+                                       result = -EINVAL;
+                               else
+                                       entry->flags |= IMA_FOWNER;
+                       }
+                       break;
                case Opt_obj_user:
                        ima_log_string(ab, "obj_user", args[0].from);
                        result = ima_lsm_rule_init(entry, args[0].from,
@@ -426,7 +527,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
 }
 
 /**
- * ima_parse_add_rule - add a rule to measure_policy_rules
+ * ima_parse_add_rule - add a rule to ima_policy_rules
  * @rule - ima measurement policy rule
  *
  * Uses a mutex to protect the policy list from multiple concurrent writers.
@@ -436,12 +537,12 @@ ssize_t ima_parse_add_rule(char *rule)
 {
        const char *op = "update_policy";
        char *p;
-       struct ima_measure_rule_entry *entry;
+       struct ima_rule_entry *entry;
        ssize_t result, len;
        int audit_info = 0;
 
        /* Prevent installed policy from changing */
-       if (ima_measure != &measure_default_rules) {
+       if (ima_rules != &ima_default_rules) {
                integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
                                    NULL, op, "already exists",
                                    -EACCES, audit_info);
@@ -474,9 +575,9 @@ ssize_t ima_parse_add_rule(char *rule)
                return result;
        }
 
-       mutex_lock(&ima_measure_mutex);
-       list_add_tail(&entry->list, &measure_policy_rules);
-       mutex_unlock(&ima_measure_mutex);
+       mutex_lock(&ima_rules_mutex);
+       list_add_tail(&entry->list, &ima_policy_rules);
+       mutex_unlock(&ima_rules_mutex);
 
        return len;
 }
@@ -484,12 +585,12 @@ ssize_t ima_parse_add_rule(char *rule)
 /* ima_delete_rules called to cleanup invalid policy */
 void ima_delete_rules(void)
 {
-       struct ima_measure_rule_entry *entry, *tmp;
+       struct ima_rule_entry *entry, *tmp;
 
-       mutex_lock(&ima_measure_mutex);
-       list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
+       mutex_lock(&ima_rules_mutex);
+       list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
                list_del(&entry->list);
                kfree(entry);
        }
-       mutex_unlock(&ima_measure_mutex);
+       mutex_unlock(&ima_rules_mutex);
 }
index 7a25ecec5aaac6b8d00c2bf0f926deaeea561163..e9db763a875e80f8fc4bd528974922d7e98ae97e 100644 (file)
 #include <linux/integrity.h>
 #include <crypto/sha.h>
 
+/* iint action cache flags */
+#define IMA_MEASURE            0x0001
+#define IMA_MEASURED           0x0002
+#define IMA_APPRAISE           0x0004
+#define IMA_APPRAISED          0x0008
+/*#define IMA_COLLECT          0x0010  do not use this flag */
+#define IMA_COLLECTED          0x0020
+#define IMA_AUDIT              0x0040
+#define IMA_AUDITED            0x0080
+
 /* iint cache flags */
-#define IMA_MEASURED           0x01
+#define IMA_DIGSIG             0x0100
+
+#define IMA_DO_MASK            (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT)
+#define IMA_DONE_MASK          (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \
+                                | IMA_COLLECTED)
 
 enum evm_ima_xattr_type {
        IMA_XATTR_DIGEST = 0x01,
@@ -34,9 +48,9 @@ struct integrity_iint_cache {
        struct rb_node rb_node; /* rooted in integrity_iint_tree */
        struct inode *inode;    /* back pointer to inode in question */
        u64 version;            /* track inode changes */
-       unsigned char flags;
-       u8 digest[SHA1_DIGEST_SIZE];
-       struct mutex mutex;     /* protects: version, flags, digest */
+       unsigned short flags;
+       struct evm_ima_xattr_data ima_xattr;
+       enum integrity_status ima_status;
        enum integrity_status evm_status;
 };
 
index 2d5d041f2049f323e5072c701f068d695f3f2c6c..3f163d0489ad2e8705a8f57c4751038b9d7d6f36 100644 (file)
@@ -368,38 +368,6 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
        return rc;
 }
 
-/*
- * get a random value from TPM
- */
-static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
-{
-       int ret;
-
-       INIT_BUF(tb);
-       store16(tb, TPM_TAG_RQU_COMMAND);
-       store32(tb, TPM_GETRANDOM_SIZE);
-       store32(tb, TPM_ORD_GETRANDOM);
-       store32(tb, len);
-       ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
-       if (!ret)
-               memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);
-       return ret;
-}
-
-static int my_get_random(unsigned char *buf, int len)
-{
-       struct tpm_buf *tb;
-       int ret;
-
-       tb = kmalloc(sizeof *tb, GFP_KERNEL);
-       if (!tb)
-               return -ENOMEM;
-       ret = tpm_get_random(tb, buf, len);
-
-       kfree(tb);
-       return ret;
-}
-
 /*
  * Lock a trusted key, by extending a selected PCR.
  *
@@ -413,8 +381,8 @@ static int pcrlock(const int pcrnum)
 
        if (!capable(CAP_SYS_ADMIN))
                return -EPERM;
-       ret = my_get_random(hash, SHA1_DIGEST_SIZE);
-       if (ret < 0)
+       ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE);
+       if (ret != SHA1_DIGEST_SIZE)
                return ret;
        return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
 }
@@ -429,8 +397,8 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
        unsigned char ononce[TPM_NONCE_SIZE];
        int ret;
 
-       ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
-       if (ret < 0)
+       ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE);
+       if (ret != TPM_NONCE_SIZE)
                return ret;
 
        INIT_BUF(tb);
@@ -524,8 +492,8 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
        if (ret < 0)
                goto out;
 
-       ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE);
-       if (ret < 0)
+       ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE);
+       if (ret != TPM_NONCE_SIZE)
                goto out;
        ordinal = htonl(TPM_ORD_SEAL);
        datsize = htonl(datalen);
@@ -634,8 +602,8 @@ static int tpm_unseal(struct tpm_buf *tb,
 
        ordinal = htonl(TPM_ORD_UNSEAL);
        keyhndl = htonl(SRKHANDLE);
-       ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
-       if (ret < 0) {
+       ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE);
+       if (ret != TPM_NONCE_SIZE) {
                pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
                return ret;
        }
@@ -935,6 +903,7 @@ static int trusted_instantiate(struct key *key, const void *data,
        char *datablob;
        int ret = 0;
        int key_cmd;
+       size_t key_len;
 
        if (datalen <= 0 || datalen > 32767 || !data)
                return -EINVAL;
@@ -974,8 +943,9 @@ static int trusted_instantiate(struct key *key, const void *data,
                        pr_info("trusted_key: key_unseal failed (%d)\n", ret);
                break;
        case Opt_new:
-               ret = my_get_random(payload->key, payload->key_len);
-               if (ret < 0) {
+               key_len = payload->key_len;
+               ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len);
+               if (ret != key_len) {
                        pr_info("trusted_key: key_create failed (%d)\n", ret);
                        goto out;
                }
index 860aeb349cb337bbccf4346d99120d4d1fd51c90..d23b43522a5a3206d87a663175550b9acea7e777 100644 (file)
@@ -136,11 +136,23 @@ int __init register_security(struct security_operations *ops)
 
 int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
 {
+#ifdef CONFIG_SECURITY_YAMA_STACKED
+       int rc;
+       rc = yama_ptrace_access_check(child, mode);
+       if (rc)
+               return rc;
+#endif
        return security_ops->ptrace_access_check(child, mode);
 }
 
 int security_ptrace_traceme(struct task_struct *parent)
 {
+#ifdef CONFIG_SECURITY_YAMA_STACKED
+       int rc;
+       rc = yama_ptrace_traceme(parent);
+       if (rc)
+               return rc;
+#endif
        return security_ops->ptrace_traceme(parent);
 }
 
@@ -559,6 +571,9 @@ int security_inode_setxattr(struct dentry *dentry, const char *name,
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return 0;
        ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
+       if (ret)
+               return ret;
+       ret = ima_inode_setxattr(dentry, name, value, size);
        if (ret)
                return ret;
        return evm_inode_setxattr(dentry, name, value, size);
@@ -594,6 +609,9 @@ int security_inode_removexattr(struct dentry *dentry, const char *name)
        if (unlikely(IS_PRIVATE(dentry->d_inode)))
                return 0;
        ret = security_ops->inode_removexattr(dentry, name);
+       if (ret)
+               return ret;
+       ret = ima_inode_removexattr(dentry, name);
        if (ret)
                return ret;
        return evm_inode_removexattr(dentry, name);
@@ -761,6 +779,9 @@ int security_task_create(unsigned long clone_flags)
 
 void security_task_free(struct task_struct *task)
 {
+#ifdef CONFIG_SECURITY_YAMA_STACKED
+       yama_task_free(task);
+#endif
        security_ops->task_free(task);
 }
 
@@ -876,6 +897,12 @@ int security_task_wait(struct task_struct *p)
 int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                         unsigned long arg4, unsigned long arg5)
 {
+#ifdef CONFIG_SECURITY_YAMA_STACKED
+       int rc;
+       rc = yama_task_prctl(option, arg2, arg3, arg4, arg5);
+       if (rc != -ENOSYS)
+               return rc;
+#endif
        return security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
 }
 
index 8221514cc997303f5d5d8844198c233fe79b64ac..2874c73167831f6f72cdb8aefffbf29c3aa0f7a2 100644 (file)
@@ -1691,40 +1691,19 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
  * smack_task_wait - Smack access check for waiting
  * @p: task to wait for
  *
- * Returns 0 if current can wait for p, error code otherwise
+ * Returns 0
  */
 static int smack_task_wait(struct task_struct *p)
 {
-       struct smk_audit_info ad;
-       char *sp = smk_of_current();
-       char *tsp = smk_of_forked(task_security(p));
-       int rc;
-
-       /* we don't log here, we can be overriden */
-       rc = smk_access(tsp, sp, MAY_WRITE, NULL);
-       if (rc == 0)
-               goto out_log;
-
        /*
-        * Allow the operation to succeed if either task
-        * has privilege to perform operations that might
-        * account for the smack labels having gotten to
-        * be different in the first place.
-        *
-        * This breaks the strict subject/object access
-        * control ideal, taking the object's privilege
-        * state into account in the decision as well as
-        * the smack value.
+        * Allow the operation to succeed.
+        * Zombies are bad.
+        * In userless environments (e.g. phones) programs
+        * get marked with SMACK64EXEC and even if the parent
+        * and child shouldn't be talking the parent still
+        * may expect to know when the child exits.
         */
-       if (smack_privileged(CAP_MAC_OVERRIDE) ||
-           has_capability(p, CAP_MAC_OVERRIDE))
-               rc = 0;
-       /* we log only if we didn't get overriden */
- out_log:
-       smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
-       smk_ad_setfield_u_tsk(&ad, p);
-       smack_log(tsp, sp, MAY_WRITE, rc, &ad);
-       return rc;
+       return 0;
 }
 
 /**
@@ -2705,9 +2684,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
 static int smack_setprocattr(struct task_struct *p, char *name,
                             void *value, size_t size)
 {
-       int rc;
        struct task_smack *tsp;
-       struct task_smack *oldtsp;
        struct cred *new;
        char *newsmack;
 
@@ -2737,21 +2714,13 @@ static int smack_setprocattr(struct task_struct *p, char *name,
        if (newsmack == smack_known_web.smk_known)
                return -EPERM;
 
-       oldtsp = p->cred->security;
        new = prepare_creds();
        if (new == NULL)
                return -ENOMEM;
 
-       tsp = new_task_smack(newsmack, oldtsp->smk_forked, GFP_KERNEL);
-       if (tsp == NULL) {
-               kfree(new);
-               return -ENOMEM;
-       }
-       rc = smk_copy_rules(&tsp->smk_rules, &oldtsp->smk_rules, GFP_KERNEL);
-       if (rc != 0)
-               return rc;
+       tsp = new->security;
+       tsp->smk_task = newsmack;
 
-       new->security = tsp;
        commit_creds(new);
        return size;
 }
index b1b768e4049af3304d457451f06c8fa834909b89..99929a50093aa38fdd2917974f68df9c267b9933 100644 (file)
@@ -49,6 +49,7 @@ enum smk_inos {
        SMK_LOAD_SELF2  = 15,   /* load task specific rules with long labels */
        SMK_ACCESS2     = 16,   /* make an access check with long labels */
        SMK_CIPSO2      = 17,   /* load long label -> CIPSO mapping */
+       SMK_REVOKE_SUBJ = 18,   /* set rules with subject label to '-' */
 };
 
 /*
@@ -1991,6 +1992,77 @@ static const struct file_operations smk_access2_ops = {
        .llseek         = generic_file_llseek,
 };
 
+/**
+ * smk_write_revoke_subj - write() for /smack/revoke-subject
+ * @file: file pointer
+ * @buf: data from user space
+ * @count: bytes sent
+ * @ppos: where to start - must be 0
+ */
+static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
+                               size_t count, loff_t *ppos)
+{
+       char *data = NULL;
+       const char *cp = NULL;
+       struct smack_known *skp;
+       struct smack_rule *sp;
+       struct list_head *rule_list;
+       struct mutex *rule_lock;
+       int rc = count;
+
+       if (*ppos != 0)
+               return -EINVAL;
+
+       if (!smack_privileged(CAP_MAC_ADMIN))
+               return -EPERM;
+
+       if (count == 0 || count > SMK_LONGLABEL)
+               return -EINVAL;
+
+       data = kzalloc(count, GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       if (copy_from_user(data, buf, count) != 0) {
+               rc = -EFAULT;
+               goto free_out;
+       }
+
+       cp = smk_parse_smack(data, count);
+       if (cp == NULL) {
+               rc = -EINVAL;
+               goto free_out;
+       }
+
+       skp = smk_find_entry(cp);
+       if (skp == NULL) {
+               rc = -EINVAL;
+               goto free_out;
+       }
+
+       rule_list = &skp->smk_rules;
+       rule_lock = &skp->smk_rules_lock;
+
+       mutex_lock(rule_lock);
+
+       list_for_each_entry_rcu(sp, rule_list, list)
+               sp->smk_access = 0;
+
+       mutex_unlock(rule_lock);
+
+free_out:
+       kfree(data);
+       kfree(cp);
+       return rc;
+}
+
+static const struct file_operations smk_revoke_subj_ops = {
+       .write          = smk_write_revoke_subj,
+       .read           = simple_transaction_read,
+       .release        = simple_transaction_release,
+       .llseek         = generic_file_llseek,
+};
+
 /**
  * smk_fill_super - fill the /smackfs superblock
  * @sb: the empty superblock
@@ -2037,6 +2109,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
                        "access2", &smk_access2_ops, S_IRUGO|S_IWUGO},
                [SMK_CIPSO2] = {
                        "cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR},
+               [SMK_REVOKE_SUBJ] = {
+                       "revoke-subject", &smk_revoke_subj_ops,
+                       S_IRUGO|S_IWUSR},
                /* last one */
                        {""}
        };
index 51d6709d8bbd3fdd065ae119c3608149ee94d1cd..20ef5143c0c06bbedbfaf4114bf08c4579d75011 100644 (file)
@@ -11,3 +11,11 @@ config SECURITY_YAMA
          Further information can be found in Documentation/security/Yama.txt.
 
          If you are unsure how to answer this question, answer N.
+
+config SECURITY_YAMA_STACKED
+       bool "Yama stacked with other LSMs"
+       depends on SECURITY_YAMA
+       default n
+       help
+         When Yama is built into the kernel, force it to stack with the
+         selected primary LSM.
index 0cc99a3ea42d65c81188c302626681fae6044473..b4c29848b49d2ab2741ade9f1aece147ac02e6a3 100644 (file)
@@ -100,7 +100,7 @@ static void yama_ptracer_del(struct task_struct *tracer,
  * yama_task_free - check for task_pid to remove from exception list
  * @task: task being removed
  */
-static void yama_task_free(struct task_struct *task)
+void yama_task_free(struct task_struct *task)
 {
        yama_ptracer_del(task, task);
 }
@@ -116,7 +116,7 @@ static void yama_task_free(struct task_struct *task)
  * Return 0 on success, -ve on error.  -ENOSYS is returned when Yama
  * does not handle the given option.
  */
-static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
+int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                           unsigned long arg4, unsigned long arg5)
 {
        int rc;
@@ -143,7 +143,7 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
                if (arg2 == 0) {
                        yama_ptracer_del(NULL, myself);
                        rc = 0;
-               } else if (arg2 == PR_SET_PTRACER_ANY) {
+               } else if (arg2 == PR_SET_PTRACER_ANY || (int)arg2 == -1) {
                        rc = yama_ptracer_add(NULL, myself);
                } else {
                        struct task_struct *tracer;
@@ -243,7 +243,7 @@ static int ptracer_exception_found(struct task_struct *tracer,
  *
  * Returns 0 if following the ptrace is allowed, -ve on error.
  */
-static int yama_ptrace_access_check(struct task_struct *child,
+int yama_ptrace_access_check(struct task_struct *child,
                                    unsigned int mode)
 {
        int rc;
@@ -293,7 +293,7 @@ static int yama_ptrace_access_check(struct task_struct *child,
  *
  * Returns 0 if following the ptrace is allowed, -ve on error.
  */
-static int yama_ptrace_traceme(struct task_struct *parent)
+int yama_ptrace_traceme(struct task_struct *parent)
 {
        int rc;
 
@@ -324,6 +324,7 @@ static int yama_ptrace_traceme(struct task_struct *parent)
        return rc;
 }
 
+#ifndef CONFIG_SECURITY_YAMA_STACKED
 static struct security_operations yama_ops = {
        .name =                 "yama",
 
@@ -332,6 +333,7 @@ static struct security_operations yama_ops = {
        .task_prctl =           yama_task_prctl,
        .task_free =            yama_task_free,
 };
+#endif
 
 #ifdef CONFIG_SYSCTL
 static int yama_dointvec_minmax(struct ctl_table *table, int write,
@@ -378,13 +380,17 @@ static struct ctl_table yama_sysctl_table[] = {
 
 static __init int yama_init(void)
 {
+#ifndef CONFIG_SECURITY_YAMA_STACKED
        if (!security_module_enable(&yama_ops))
                return 0;
+#endif
 
        printk(KERN_INFO "Yama: becoming mindful.\n");
 
+#ifndef CONFIG_SECURITY_YAMA_STACKED
        if (register_security(&yama_ops))
                panic("Yama: kernel registration failed.\n");
+#endif
 
 #ifdef CONFIG_SYSCTL
        if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))