Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux...
authorLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Feb 2013 16:18:12 +0000 (08:18 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Thu, 21 Feb 2013 16:18:12 +0000 (08:18 -0800)
Pull security subsystem updates from James Morris:
 "This is basically a maintenance update for the TPM driver and EVM/IMA"

Fix up conflicts in lib/digsig.c and security/integrity/ima/ima_main.c

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (45 commits)
  tpm/ibmvtpm: build only when IBM pseries is configured
  ima: digital signature verification using asymmetric keys
  ima: rename hash calculation functions
  ima: use new crypto_shash API instead of old crypto_hash
  ima: add policy support for file system uuid
  evm: add file system uuid to EVM hmac
  tpm_tis: check pnp_acpi_device return code
  char/tpm/tpm_i2c_stm_st33: drop temporary variable for return value
  char/tpm/tpm_i2c_stm_st33: remove dead assignment in tpm_st33_i2c_probe
  char/tpm/tpm_i2c_stm_st33: Remove __devexit attribute
  char/tpm/tpm_i2c_stm_st33: Don't use memcpy for one byte assignment
  tpm_i2c_stm_st33: removed unused variables/code
  TPM: Wait for TPM_ACCESS tpmRegValidSts to go high at startup
  tpm: Fix cancellation of TPM commands (interrupt mode)
  tpm: Fix cancellation of TPM commands (polling mode)
  tpm: Store TPM vendor ID
  TPM: Work around buggy TPMs that block during continue self test
  tpm_i2c_stm_st33: fix oops when i2c client is unavailable
  char/tpm: Use struct dev_pm_ops for power management
  TPM: STMicroelectronics ST33 I2C BUILD STUFF
  ...

35 files changed:
Documentation/ABI/stable/sysfs-class-tpm [new file with mode: 0644]
Documentation/ABI/testing/ima_policy
drivers/char/tpm/Kconfig
drivers/char/tpm/Makefile
drivers/char/tpm/tpm.c
drivers/char/tpm/tpm.h
drivers/char/tpm/tpm_acpi.c
drivers/char/tpm/tpm_atmel.c
drivers/char/tpm/tpm_i2c_infineon.c
drivers/char/tpm/tpm_i2c_stm_st33.c [new file with mode: 0644]
drivers/char/tpm/tpm_i2c_stm_st33.h [new file with mode: 0644]
drivers/char/tpm/tpm_ibmvtpm.c
drivers/char/tpm/tpm_nsc.c
drivers/char/tpm/tpm_tis.c
lib/digsig.c
lib/mpi/mpi-internal.h
lib/mpi/mpicoder.c
security/integrity/Kconfig
security/integrity/Makefile
security/integrity/digsig.c
security/integrity/digsig_asymmetric.c [new file with mode: 0644]
security/integrity/evm/Kconfig
security/integrity/evm/evm.h
security/integrity/evm/evm_crypto.c
security/integrity/evm/evm_main.c
security/integrity/evm/evm_secfs.c
security/integrity/iint.c
security/integrity/ima/ima.h
security/integrity/ima/ima_api.c
security/integrity/ima/ima_appraise.c
security/integrity/ima/ima_crypto.c
security/integrity/ima/ima_init.c
security/integrity/ima/ima_main.c
security/integrity/ima/ima_policy.c
security/integrity/integrity.h

diff --git a/Documentation/ABI/stable/sysfs-class-tpm b/Documentation/ABI/stable/sysfs-class-tpm
new file mode 100644 (file)
index 0000000..a60b45e
--- /dev/null
@@ -0,0 +1,185 @@
+What:          /sys/class/misc/tpmX/device/
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The device/ directory under a specific TPM instance exposes
+               the properties of that TPM chip
+
+
+What:          /sys/class/misc/tpmX/device/active
+Date:          April 2006
+KernelVersion: 2.6.17
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "active" property prints a '1' if the TPM chip is accepting
+               commands. An inactive TPM chip still contains all the state of
+               an active chip (Storage Root Key, NVRAM, etc), and can be
+               visible to the OS, but will only accept a restricted set of
+               commands. See the TPM Main Specification part 2, Structures,
+               section 17 for more information on which commands are
+               available.
+
+What:          /sys/class/misc/tpmX/device/cancel
+Date:          June 2005
+KernelVersion: 2.6.13
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "cancel" property allows you to cancel the currently
+               pending TPM command. Writing any value to cancel will call the
+               TPM vendor specific cancel operation.
+
+What:          /sys/class/misc/tpmX/device/caps
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "caps" property contains TPM manufacturer and version info.
+
+               Example output:
+
+               Manufacturer: 0x53544d20
+               TCG version: 1.2
+               Firmware version: 8.16
+
+               Manufacturer is a hex dump of the 4 byte manufacturer info
+               space in a TPM. TCG version shows the TCG TPM spec level that
+               the chip supports. Firmware version is that of the chip and
+               is manufacturer specific.
+
+What:          /sys/class/misc/tpmX/device/durations
+Date:          March 2011
+KernelVersion: 3.1
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "durations" property shows the 3 vendor-specific values
+               used to wait for a short, medium and long TPM command. All
+               TPM commands are categorized as short, medium or long in
+               execution time, so that the driver doesn't have to wait
+               any longer than necessary before starting to poll for a
+               result.
+
+               Example output:
+
+               3015000 4508000 180995000 [original]
+
+               Here the short, medium and long durations are displayed in
+               usecs. "[original]" indicates that the values are displayed
+               unmodified from when they were queried from the chip.
+               Durations can be modified in the case where a buggy chip
+               reports them in msec instead of usec and they need to be
+               scaled to be displayed in usecs. In this case "[adjusted]"
+               will be displayed in place of "[original]".
+
+What:          /sys/class/misc/tpmX/device/enabled
+Date:          April 2006
+KernelVersion: 2.6.17
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "enabled" property prints a '1' if the TPM chip is enabled,
+               meaning that it should be visible to the OS. This property
+               may be visible but produce a '0' after some operation that
+               disables the TPM.
+
+What:          /sys/class/misc/tpmX/device/owned
+Date:          April 2006
+KernelVersion: 2.6.17
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "owned" property produces a '1' if the TPM_TakeOwnership
+               ordinal has been executed successfully in the chip. A '0'
+               indicates that ownership hasn't been taken.
+
+What:          /sys/class/misc/tpmX/device/pcrs
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "pcrs" property will dump the current value of all Platform
+               Configuration Registers in the TPM. Note that since these
+               values may be constantly changing, the output is only valid
+               for a snapshot in time.
+
+               Example output:
+
+               PCR-00: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75
+               PCR-01: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75
+               PCR-02: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75
+               PCR-03: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75
+               PCR-04: 3A 3F 78 0F 11 A4 B4 99 69 FC AA 80 CD 6E 39 57 C3 3B 22 75
+               ...
+
+               The number of PCRs and hex bytes needed to represent a PCR
+               value will vary depending on TPM chip version. For TPM 1.1 and
+               1.2 chips, PCRs represent SHA-1 hashes, which are 20 bytes
+               long. Use the "caps" property to determine TPM version.
+
+What:          /sys/class/misc/tpmX/device/pubek
+Date:          April 2005
+KernelVersion: 2.6.12
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "pubek" property will return the TPM's public endorsement
+               key if possible. If the TPM has had ownership established and
+               is version 1.2, the pubek will not be available without the
+               owner's authorization. Since the TPM driver doesn't store any
+               secrets, it can't authorize its own request for the pubek,
+               making it unaccessible. The public endorsement key is gener-
+               ated at TPM menufacture time and exists for the life of the
+               chip.
+
+               Example output:
+
+               Algorithm: 00 00 00 01
+               Encscheme: 00 03
+               Sigscheme: 00 01
+               Parameters: 00 00 08 00 00 00 00 02 00 00 00 00
+               Modulus length: 256
+               Modulus:
+               B4 76 41 82 C9 20 2C 10 18 40 BC 8B E5 44 4C 6C
+               3A B2 92 0C A4 9B 2A 83 EB 5C 12 85 04 48 A0 B6
+               1E E4 81 84 CE B2 F2 45 1C F0 85 99 61 02 4D EB
+               86 C4 F7 F3 29 60 52 93 6B B2 E5 AB 8B A9 09 E3
+               D7 0E 7D CA 41 BF 43 07 65 86 3C 8C 13 7A D0 8B
+               82 5E 96 0B F8 1F 5F 34 06 DA A2 52 C1 A9 D5 26
+               0F F4 04 4B D9 3F 2D F2 AC 2F 74 64 1F 8B CD 3E
+               1E 30 38 6C 70 63 69 AB E2 50 DF 49 05 2E E1 8D
+               6F 78 44 DA 57 43 69 EE 76 6C 38 8A E9 8E A3 F0
+               A7 1F 3C A8 D0 12 15 3E CA 0E BD FA 24 CD 33 C6
+               47 AE A4 18 83 8E 22 39 75 93 86 E6 FD 66 48 B6
+               10 AD 94 14 65 F9 6A 17 78 BD 16 53 84 30 BF 70
+               E0 DC 65 FD 3C C6 B0 1E BF B9 C1 B5 6C EF B1 3A
+               F8 28 05 83 62 26 11 DC B4 6B 5A 97 FF 32 26 B6
+               F7 02 71 CF 15 AE 16 DD D1 C1 8E A8 CF 9B 50 7B
+               C3 91 FF 44 1E CF 7C 39 FE 17 77 21 20 BD CE 9B
+
+               Possible values:
+
+               Algorithm:      TPM_ALG_RSA                     (1)
+               Encscheme:      TPM_ES_RSAESPKCSv15             (2)
+                               TPM_ES_RSAESOAEP_SHA1_MGF1      (3)
+               Sigscheme:      TPM_SS_NONE                     (1)
+               Parameters, a byte string of 3 u32 values:
+                       Key Length (bits):      00 00 08 00     (2048)
+                       Num primes:             00 00 00 02     (2)
+                       Exponent Size:          00 00 00 00     (0 means the
+                                                                default exp)
+               Modulus Length: 256 (bytes)
+               Modulus:        The 256 byte Endorsement Key modulus
+
+What:          /sys/class/misc/tpmX/device/temp_deactivated
+Date:          April 2006
+KernelVersion: 2.6.17
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "temp_deactivated" property returns a '1' if the chip has
+               been temporarily dectivated, usually until the next power
+               cycle. Whether a warm boot (reboot) will clear a TPM chip
+               from a temp_deactivated state is platform specific.
+
+What:          /sys/class/misc/tpmX/device/timeouts
+Date:          March 2011
+KernelVersion: 3.1
+Contact:       tpmdd-devel@lists.sf.net
+Description:   The "timeouts" property shows the 4 vendor-specific values
+               for the TPM's interface spec timeouts. The use of these
+               timeouts is defined by the TPM interface spec that the chip
+               conforms to.
+
+               Example output:
+
+               750000 750000 750000 750000 [original]
+
+               The four timeout values are shown in usecs, with a trailing
+               "[original]" or "[adjusted]" depending on whether the values
+               were scaled by the driver to be reported in usec from msecs.
index ec0a38ef3145e30a90ace37c391d0eba33c81bc7..f1c5cc9d17a87def20095fb1ead651bc5887cf00 100644 (file)
@@ -18,17 +18,21 @@ Description:
                rule format: action [condition ...]
 
                action: measure | dont_measure | appraise | dont_appraise | audit
-               condition:= base | lsm
-                       base:   [[func=] [mask=] [fsmagic=] [uid=] [fowner]]
+               condition:= base | lsm  [option]
+                       base:   [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=]
+                                [fowner]]
                        lsm:    [[subj_user=] [subj_role=] [subj_type=]
                                 [obj_user=] [obj_role=] [obj_type=]]
+                       option: [[appraise_type=]]
 
-               base:   func:= [BPRM_CHECK][FILE_MMAP][FILE_CHECK][MODULE_CHECK]
+               base:   func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK]
                        mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
                        fsmagic:= hex value
+                       fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6)
                        uid:= decimal value
                        fowner:=decimal value
                lsm:    are LSM specific
+               option: appraise_type:= [imasig]
 
                default policy:
                        # PROC_SUPER_MAGIC
index 915875e431d2151f48d9f793da67d0b2817d5e45..dbfd56446c3197bb49d8a0a9013e44406f9c600e 100644 (file)
@@ -75,10 +75,20 @@ config TCG_INFINEON
 
 config TCG_IBMVTPM
        tristate "IBM VTPM Interface"
-       depends on PPC64
+       depends on PPC_PSERIES
        ---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.
 
+config TCG_ST33_I2C
+        tristate "STMicroelectronics ST33 I2C TPM"
+        depends on I2C
+        depends on GPIOLIB
+        ---help---
+        If you have a TPM security chip from STMicroelectronics working with
+        an I2C bus 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_stm_st33_i2c.
+
 endif # TCG_TPM
index 5b3fc8bc6c132fa76f1e6ec8723c9bf096670271..a3736c97c65ac005b12507adbfc3b8fb620db573 100644 (file)
@@ -17,3 +17,4 @@ 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
+obj-$(CONFIG_TCG_ST33_I2C) += tpm_i2c_stm_st33.o
index 93211df52aab7cef9ad9615e42a7b9d36dea4d6f..0d2e82f95577ce1038d7bc382b9920967fe3f5a0 100644 (file)
@@ -40,8 +40,9 @@ enum tpm_duration {
 };
 
 #define TPM_MAX_ORDINAL 243
-#define TPM_MAX_PROTECTED_ORDINAL 12
-#define TPM_PROTECTED_ORDINAL_MASK 0xFF
+#define TSC_MAX_ORDINAL 12
+#define TPM_PROTECTED_COMMAND 0x00
+#define TPM_CONNECTION_COMMAND 0x40
 
 /*
  * Bug workaround - some TPM's don't flush the most
@@ -65,21 +66,6 @@ static DECLARE_BITMAP(dev_mask, TPM_NUM_DEVICES);
  * values of the SHORT, MEDIUM, and LONG durations are retrieved
  * from the chip during initialization with a call to tpm_get_timeouts.
  */
-static const u8 tpm_protected_ordinal_duration[TPM_MAX_PROTECTED_ORDINAL] = {
-       TPM_UNDEFINED,          /* 0 */
-       TPM_UNDEFINED,
-       TPM_UNDEFINED,
-       TPM_UNDEFINED,
-       TPM_UNDEFINED,
-       TPM_UNDEFINED,          /* 5 */
-       TPM_UNDEFINED,
-       TPM_UNDEFINED,
-       TPM_UNDEFINED,
-       TPM_UNDEFINED,
-       TPM_SHORT,              /* 10 */
-       TPM_SHORT,
-};
-
 static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
        TPM_UNDEFINED,          /* 0 */
        TPM_UNDEFINED,
@@ -351,14 +337,11 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
 {
        int duration_idx = TPM_UNDEFINED;
        int duration = 0;
+       u8 category = (ordinal >> 24) & 0xFF;
 
-       if (ordinal < TPM_MAX_ORDINAL)
+       if ((category == TPM_PROTECTED_COMMAND && ordinal < TPM_MAX_ORDINAL) ||
+           (category == TPM_CONNECTION_COMMAND && ordinal < TSC_MAX_ORDINAL))
                duration_idx = tpm_ordinal_duration[ordinal];
-       else if ((ordinal & TPM_PROTECTED_ORDINAL_MASK) <
-                TPM_MAX_PROTECTED_ORDINAL)
-               duration_idx =
-                   tpm_protected_ordinal_duration[ordinal &
-                                                  TPM_PROTECTED_ORDINAL_MASK];
 
        if (duration_idx != TPM_UNDEFINED)
                duration = chip->vendor.duration[duration_idx];
@@ -410,7 +393,7 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf,
                    chip->vendor.req_complete_val)
                        goto out_recv;
 
-               if ((status == chip->vendor.req_canceled)) {
+               if (chip->vendor.req_canceled(chip, status)) {
                        dev_err(chip->dev, "Operation Canceled\n");
                        rc = -ECANCELED;
                        goto out;
@@ -468,7 +451,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
                return -EFAULT;
 
        err = be32_to_cpu(cmd->header.out.return_code);
-       if (err != 0)
+       if (err != 0 && desc)
                dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
 
        return err;
@@ -528,6 +511,25 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
 }
 EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
 
+#define TPM_ORD_STARTUP cpu_to_be32(153)
+#define TPM_ST_CLEAR cpu_to_be16(1)
+#define TPM_ST_STATE cpu_to_be16(2)
+#define TPM_ST_DEACTIVATED cpu_to_be16(3)
+static const struct tpm_input_header tpm_startup_header = {
+       .tag = TPM_TAG_RQU_COMMAND,
+       .length = cpu_to_be32(12),
+       .ordinal = TPM_ORD_STARTUP
+};
+
+static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
+{
+       struct tpm_cmd_t start_cmd;
+       start_cmd.header.in = tpm_startup_header;
+       start_cmd.params.startup_in.startup_type = startup_type;
+       return transmit_cmd(chip, &start_cmd, TPM_INTERNAL_RESULT_SIZE,
+                           "attempting to start the TPM");
+}
+
 int tpm_get_timeouts(struct tpm_chip *chip)
 {
        struct tpm_cmd_t tpm_cmd;
@@ -541,11 +543,28 @@ int tpm_get_timeouts(struct tpm_chip *chip)
        tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
        tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
        tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, NULL);
 
-       rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
-                       "attempting to determine the timeouts");
-       if (rc)
+       if (rc == TPM_ERR_INVALID_POSTINIT) {
+               /* The TPM is not started, we are the first to talk to it.
+                  Execute a startup command. */
+               dev_info(chip->dev, "Issuing TPM_STARTUP");
+               if (tpm_startup(chip, TPM_ST_CLEAR))
+                       return rc;
+
+               tpm_cmd.header.in = tpm_getcap_header;
+               tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
+               tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
+               tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
+               rc = transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
+                                 NULL);
+       }
+       if (rc) {
+               dev_err(chip->dev,
+                       "A TPM error (%zd) occurred attempting to determine the timeouts\n",
+                       rc);
                goto duration;
+       }
 
        if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
            be32_to_cpu(tpm_cmd.header.out.length)
@@ -824,7 +843,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
 {
        int rc;
        unsigned int loops;
-       unsigned int delay_msec = 1000;
+       unsigned int delay_msec = 100;
        unsigned long duration;
        struct tpm_cmd_t cmd;
 
@@ -845,6 +864,14 @@ int tpm_do_selftest(struct tpm_chip *chip)
                cmd.header.in = pcrread_header;
                cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0);
                rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE);
+               /* Some buggy TPMs will not respond to tpm_tis_ready() for
+                * around 300ms while the self test is ongoing, keep trying
+                * until the self test duration expires. */
+               if (rc == -ETIME) {
+                       dev_info(chip->dev, HW_ERR "TPM command timed out during continue self test");
+                       msleep(delay_msec);
+                       continue;
+               }
 
                if (rc < TPM_HEADER_SIZE)
                        return -EFAULT;
@@ -1075,12 +1102,28 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
 }
 EXPORT_SYMBOL_GPL(tpm_store_cancel);
 
+static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, bool check_cancel,
+                                  bool *canceled)
+{
+       u8 status = chip->vendor.status(chip);
+
+       *canceled = false;
+       if ((status & mask) == mask)
+               return true;
+       if (check_cancel && chip->vendor.req_canceled(chip, status)) {
+               *canceled = true;
+               return true;
+       }
+       return false;
+}
+
 int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
-                        wait_queue_head_t *queue)
+                     wait_queue_head_t *queue, bool check_cancel)
 {
        unsigned long stop;
        long rc;
        u8 status;
+       bool canceled = false;
 
        /* check current status */
        status = chip->vendor.status(chip);
@@ -1095,11 +1138,14 @@ again:
                if ((long)timeout <= 0)
                        return -ETIME;
                rc = wait_event_interruptible_timeout(*queue,
-                                                     ((chip->vendor.status(chip)
-                                                     & mask) == mask),
-                                                     timeout);
-               if (rc > 0)
+                       wait_for_tpm_stat_cond(chip, mask, check_cancel,
+                                              &canceled),
+                       timeout);
+               if (rc > 0) {
+                       if (canceled)
+                               return -ECANCELED;
                        return 0;
+               }
                if (rc == -ERESTARTSYS && freezing(current)) {
                        clear_thread_flag(TIF_SIGPENDING);
                        goto again;
index 8ef7649a50aa7d31f32360ea81ad0668cf041065..81b52015f669ecd0b16c4948ddc39834b49001cb 100644 (file)
@@ -47,6 +47,7 @@ enum tpm_addr {
 #define TPM_WARN_DOING_SELFTEST 0x802
 #define TPM_ERR_DEACTIVATED     0x6
 #define TPM_ERR_DISABLED        0x7
+#define TPM_ERR_INVALID_POSTINIT 38
 
 #define TPM_HEADER_SIZE                10
 extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr,
@@ -77,7 +78,7 @@ struct tpm_chip;
 struct tpm_vendor_specific {
        const u8 req_complete_mask;
        const u8 req_complete_val;
-       const u8 req_canceled;
+       bool (*req_canceled)(struct tpm_chip *chip, u8 status);
        void __iomem *iobase;           /* ioremapped address */
        unsigned long base;             /* TPM base address */
 
@@ -100,13 +101,19 @@ struct tpm_vendor_specific {
        bool timeout_adjusted;
        unsigned long duration[3]; /* jiffies */
        bool duration_adjusted;
-       void *data;
+       void *priv;
 
        wait_queue_head_t read_queue;
        wait_queue_head_t int_queue;
+
+       u16 manufacturer_id;
 };
 
+#define TPM_VPRIV(c)   (c)->vendor.priv
+
 #define TPM_VID_INTEL    0x8086
+#define TPM_VID_WINBOND  0x1050
+#define TPM_VID_STM      0x104A
 
 struct tpm_chip {
        struct device *dev;     /* Device stuff */
@@ -154,13 +161,13 @@ struct tpm_input_header {
        __be16  tag;
        __be32  length;
        __be32  ordinal;
-}__attribute__((packed));
+} __packed;
 
 struct tpm_output_header {
        __be16  tag;
        __be32  length;
        __be32  return_code;
-}__attribute__((packed));
+} __packed;
 
 struct stclear_flags_t {
        __be16  tag;
@@ -169,14 +176,14 @@ struct    stclear_flags_t {
        u8      physicalPresence;
        u8      physicalPresenceLock;
        u8      bGlobalLock;
-}__attribute__((packed));
+} __packed;
 
 struct tpm_version_t {
        u8      Major;
        u8      Minor;
        u8      revMajor;
        u8      revMinor;
-}__attribute__((packed));
+} __packed;
 
 struct tpm_version_1_2_t {
        __be16  tag;
@@ -184,20 +191,20 @@ struct    tpm_version_1_2_t {
        u8      Minor;
        u8      revMajor;
        u8      revMinor;
-}__attribute__((packed));
+} __packed;
 
 struct timeout_t {
        __be32  a;
        __be32  b;
        __be32  c;
        __be32  d;
-}__attribute__((packed));
+} __packed;
 
 struct duration_t {
        __be32  tpm_short;
        __be32  tpm_medium;
        __be32  tpm_long;
-}__attribute__((packed));
+} __packed;
 
 struct permanent_flags_t {
        __be16  tag;
@@ -221,7 +228,7 @@ struct permanent_flags_t {
        u8      tpmEstablished;
        u8      maintenanceDone;
        u8      disableFullDALogicInfo;
-}__attribute__((packed));
+} __packed;
 
 typedef union {
        struct  permanent_flags_t perm_flags;
@@ -239,12 +246,12 @@ struct    tpm_getcap_params_in {
        __be32  cap;
        __be32  subcap_size;
        __be32  subcap;
-}__attribute__((packed));
+} __packed;
 
 struct tpm_getcap_params_out {
        __be32  cap_size;
        cap_t   cap;
-}__attribute__((packed));
+} __packed;
 
 struct tpm_readpubek_params_out {
        u8      algorithm[4];
@@ -255,7 +262,7 @@ struct      tpm_readpubek_params_out {
        __be32  keysize;
        u8      modulus[256];
        u8      checksum[20];
-}__attribute__((packed));
+} __packed;
 
 typedef union {
        struct  tpm_input_header in;
@@ -265,16 +272,16 @@ typedef union {
 #define TPM_DIGEST_SIZE 20
 struct tpm_pcrread_out {
        u8      pcr_result[TPM_DIGEST_SIZE];
-}__attribute__((packed));
+} __packed;
 
 struct tpm_pcrread_in {
        __be32  pcr_idx;
-}__attribute__((packed));
+} __packed;
 
 struct tpm_pcrextend_in {
        __be32  pcr_idx;
        u8      hash[TPM_DIGEST_SIZE];
-}__attribute__((packed));
+} __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
@@ -285,11 +292,15 @@ struct tpm_pcrextend_in {
 struct tpm_getrandom_out {
        __be32 rng_data_len;
        u8     rng_data[TPM_MAX_RNG_DATA];
-}__attribute__((packed));
+} __packed;
 
 struct tpm_getrandom_in {
        __be32 num_bytes;
-}__attribute__((packed));
+} __packed;
+
+struct tpm_startup_in {
+       __be16  startup_type;
+} __packed;
 
 typedef union {
        struct  tpm_getcap_params_out getcap_out;
@@ -301,12 +312,13 @@ typedef union {
        struct  tpm_pcrextend_in pcrextend_in;
        struct  tpm_getrandom_in getrandom_in;
        struct  tpm_getrandom_out getrandom_out;
+       struct tpm_startup_in startup_in;
 } tpm_cmd_params;
 
 struct tpm_cmd_t {
        tpm_cmd_header  header;
        tpm_cmd_params  params;
-}__attribute__((packed));
+} __packed;
 
 ssize_t        tpm_getcap(struct device *, __be32, cap_t *, const char *);
 
@@ -326,7 +338,7 @@ extern void tpm_remove_hardware(struct device *);
 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 *);
+                            wait_queue_head_t *, bool);
 
 #ifdef CONFIG_ACPI
 extern int tpm_add_ppi(struct kobject *);
index 56051d0c97a25caa4872d274d1c526d3368453e7..64420b3396a294ccd56c2b6e727b1aaa0459a34b 100644 (file)
@@ -33,13 +33,13 @@ struct acpi_tcpa {
        u16 platform_class;
        union {
                struct client_hdr {
-                       u32 log_max_len __attribute__ ((packed));
-                       u64 log_start_addr __attribute__ ((packed));
+                       u32 log_max_len __packed;
+                       u64 log_start_addr __packed;
                } client;
                struct server_hdr {
                        u16 reserved;
-                       u64 log_max_len __attribute__ ((packed));
-                       u64 log_start_addr __attribute__ ((packed));
+                       u64 log_max_len __packed;
+                       u64 log_start_addr __packed;
                } server;
        };
 };
index 678d57019dc46990d4433afbc1637ff2f9208919..99d6820c611db2e0f0f344c9cf701cfc02b0505d 100644 (file)
@@ -116,6 +116,11 @@ static u8 tpm_atml_status(struct tpm_chip *chip)
        return ioread8(chip->vendor.iobase + 1);
 }
 
+static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       return (status == ATML_STATUS_READY);
+}
+
 static const struct file_operations atmel_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
@@ -147,7 +152,7 @@ static const struct tpm_vendor_specific tpm_atmel = {
        .status = tpm_atml_status,
        .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
        .req_complete_val = ATML_STATUS_DATA_AVAIL,
-       .req_canceled = ATML_STATUS_READY,
+       .req_canceled = tpm_atml_req_canceled,
        .attr_group = &atmel_attr_grp,
        .miscdev = { .fops = &atmel_ops, },
 };
index fb447bd0cb61b35bfc000867ffd56fe895a7cb8a..8fe7ac3d095b1a35425d2bb74fb1378c40506142 100644 (file)
@@ -505,6 +505,11 @@ out_err:
        return rc;
 }
 
+static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       return (status == TPM_STS_COMMAND_READY);
+}
+
 static const struct file_operations tis_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
@@ -550,7 +555,7 @@ static struct tpm_vendor_specific tpm_tis_i2c = {
        .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,
+       .req_canceled = tpm_tis_i2c_req_canceled,
        .attr_group = &tis_attr_grp,
        .miscdev.fops = &tis_ops,
 };
diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c
new file mode 100644 (file)
index 0000000..1f5f71e
--- /dev/null
@@ -0,0 +1,887 @@
+/*
+ * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
+ * Copyright (C) 2009, 2010  STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * STMicroelectronics version 1.2.0, Copyright (C) 2010
+ * STMicroelectronics comes with ABSOLUTELY NO WARRANTY.
+ * This is free software, and you are welcome to redistribute it
+ * under certain conditions.
+ *
+ * @Author: Christophe RICARD tpmsupport@st.com
+ *
+ * @File: tpm_stm_st33_i2c.c
+ *
+ * @Synopsis:
+ *     09/15/2010:     First shot driver tpm_tis driver for
+                        lpc is used as model.
+ */
+
+#include <linux/pci.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/i2c.h>
+#include <linux/fs.h>
+#include <linux/miscdevice.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/wait.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/spinlock.h>
+#include <linux/sysfs.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/uaccess.h>
+#include <linux/io.h>
+#include <linux/slab.h>
+#include <linux/sched.h>
+
+#include "tpm.h"
+#include "tpm_i2c_stm_st33.h"
+
+enum stm33zp24_access {
+       TPM_ACCESS_VALID = 0x80,
+       TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
+       TPM_ACCESS_REQUEST_PENDING = 0x04,
+       TPM_ACCESS_REQUEST_USE = 0x02,
+};
+
+enum stm33zp24_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 stm33zp24_int_flags {
+       TPM_GLOBAL_INT_ENABLE = 0x80,
+       TPM_INTF_CMD_READY_INT = 0x080,
+       TPM_INTF_FIFO_AVALAIBLE_INT = 0x040,
+       TPM_INTF_WAKE_UP_READY_INT = 0x020,
+       TPM_INTF_LOCALITY_CHANGE_INT = 0x004,
+       TPM_INTF_STS_VALID_INT = 0x002,
+       TPM_INTF_DATA_AVAIL_INT = 0x001,
+};
+
+enum tis_defaults {
+       TIS_SHORT_TIMEOUT = 750,
+       TIS_LONG_TIMEOUT = 2000,
+};
+
+/*
+ * write8_reg
+ * Send byte to the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: tpm_register, the tpm tis register where the data should be written
+ * @param: tpm_data, the tpm_data to write inside the tpm_register
+ * @param: tpm_size, The length of the data
+ * @return: Returns negative errno, or else the number of bytes written.
+ */
+static int write8_reg(struct i2c_client *client, u8 tpm_register,
+                     u8 *tpm_data, u16 tpm_size)
+{
+       struct st33zp24_platform_data *pin_infos;
+
+       pin_infos = client->dev.platform_data;
+
+       pin_infos->tpm_i2c_buffer[0][0] = tpm_register;
+       memcpy(&pin_infos->tpm_i2c_buffer[0][1], tpm_data, tpm_size);
+       return i2c_master_send(client, pin_infos->tpm_i2c_buffer[0],
+                               tpm_size + 1);
+} /* write8_reg() */
+
+/*
+ * read8_reg
+ * Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: tpm_register, the tpm tis register where the data should be read
+ * @param: tpm_data, the TPM response
+ * @param: tpm_size, tpm TPM response size to read.
+ * @return: number of byte read successfully: should be one if success.
+ */
+static int read8_reg(struct i2c_client *client, u8 tpm_register,
+                   u8 *tpm_data, int tpm_size)
+{
+       u8 status = 0;
+       u8 data;
+
+       data = TPM_DUMMY_BYTE;
+       status = write8_reg(client, tpm_register, &data, 1);
+       if (status == 2)
+               status = i2c_master_recv(client, tpm_data, tpm_size);
+       return status;
+} /* read8_reg() */
+
+/*
+ * I2C_WRITE_DATA
+ * Send byte to the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: client, the chip description
+ * @param: tpm_register, the tpm tis register where the data should be written
+ * @param: tpm_data, the tpm_data to write inside the tpm_register
+ * @param: tpm_size, The length of the data
+ * @return: number of byte written successfully: should be one if success.
+ */
+#define I2C_WRITE_DATA(client, tpm_register, tpm_data, tpm_size) \
+       (write8_reg(client, tpm_register | \
+       TPM_WRITE_DIRECTION, tpm_data, tpm_size))
+
+/*
+ * I2C_READ_DATA
+ * Recv byte from the TIS register according to the ST33ZP24 I2C protocol.
+ * @param: tpm, the chip description
+ * @param: tpm_register, the tpm tis register where the data should be read
+ * @param: tpm_data, the TPM response
+ * @param: tpm_size, tpm TPM response size to read.
+ * @return: number of byte read successfully: should be one if success.
+ */
+#define I2C_READ_DATA(client, tpm_register, tpm_data, tpm_size) \
+       (read8_reg(client, tpm_register, tpm_data, tpm_size))
+
+/*
+ * clear_interruption
+ * clear the TPM interrupt register.
+ * @param: tpm, the chip description
+ */
+static void clear_interruption(struct i2c_client *client)
+{
+       u8 interrupt;
+       I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1);
+       I2C_WRITE_DATA(client, TPM_INT_STATUS, &interrupt, 1);
+       I2C_READ_DATA(client, TPM_INT_STATUS, &interrupt, 1);
+} /* clear_interruption() */
+
+/*
+ * _wait_for_interrupt_serirq_timeout
+ * @param: tpm, the chip description
+ * @param: timeout, the timeout of the interrupt
+ * @return: the status of the interruption.
+ */
+static long _wait_for_interrupt_serirq_timeout(struct tpm_chip *chip,
+                                               unsigned long timeout)
+{
+       long status;
+       struct i2c_client *client;
+       struct st33zp24_platform_data *pin_infos;
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+       pin_infos = client->dev.platform_data;
+
+       status = wait_for_completion_interruptible_timeout(
+                                       &pin_infos->irq_detection,
+                                               timeout);
+       if (status > 0)
+               enable_irq(gpio_to_irq(pin_infos->io_serirq));
+       gpio_direction_input(pin_infos->io_serirq);
+
+       return status;
+} /* wait_for_interrupt_serirq_timeout() */
+
+static int wait_for_serirq_timeout(struct tpm_chip *chip, bool condition,
+                                unsigned long timeout)
+{
+       int status = 2;
+       struct i2c_client *client;
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+
+       status = _wait_for_interrupt_serirq_timeout(chip, timeout);
+       if (!status) {
+               status = -EBUSY;
+       } else{
+               clear_interruption(client);
+               if (condition)
+                       status = 1;
+       }
+       return status;
+}
+
+/*
+ * tpm_stm_i2c_cancel, cancel is not implemented.
+ * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
+ */
+static void tpm_stm_i2c_cancel(struct tpm_chip *chip)
+{
+       struct i2c_client *client;
+       u8 data;
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+
+       data = TPM_STS_COMMAND_READY;
+       I2C_WRITE_DATA(client, TPM_STS, &data, 1);
+       if (chip->vendor.irq)
+               wait_for_serirq_timeout(chip, 1, chip->vendor.timeout_a);
+}      /* tpm_stm_i2c_cancel() */
+
+/*
+ * tpm_stm_spi_status return the TPM_STS register
+ * @param: chip, the tpm chip description
+ * @return: the TPM_STS register value.
+ */
+static u8 tpm_stm_i2c_status(struct tpm_chip *chip)
+{
+       struct i2c_client *client;
+       u8 data;
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+
+       I2C_READ_DATA(client, TPM_STS, &data, 1);
+       return data;
+}                              /* tpm_stm_i2c_status() */
+
+
+/*
+ * check_locality if the locality is active
+ * @param: chip, the tpm chip description
+ * @return: the active locality or -EACCESS.
+ */
+static int check_locality(struct tpm_chip *chip)
+{
+       struct i2c_client *client;
+       u8 data;
+       u8 status;
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+
+       status = I2C_READ_DATA(client, TPM_ACCESS, &data, 1);
+       if (status && (data &
+               (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
+               (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID))
+               return chip->vendor.locality;
+
+       return -EACCES;
+
+} /* check_locality() */
+
+/*
+ * request_locality request the TPM locality
+ * @param: chip, the chip description
+ * @return: the active locality or EACCESS.
+ */
+static int request_locality(struct tpm_chip *chip)
+{
+       unsigned long stop;
+       long rc;
+       struct i2c_client *client;
+       u8 data;
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+
+       if (check_locality(chip) == chip->vendor.locality)
+               return chip->vendor.locality;
+
+       data = TPM_ACCESS_REQUEST_USE;
+       rc = I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1);
+       if (rc < 0)
+               goto end;
+
+       if (chip->vendor.irq) {
+               rc = wait_for_serirq_timeout(chip, (check_locality
+                                                      (chip) >= 0),
+                                                     chip->vendor.timeout_a);
+               if (rc > 0)
+                       return chip->vendor.locality;
+       } else{
+               stop = jiffies + chip->vendor.timeout_a;
+               do {
+                       if (check_locality(chip) >= 0)
+                               return chip->vendor.locality;
+                       msleep(TPM_TIMEOUT);
+               } while (time_before(jiffies, stop));
+       }
+       rc = -EACCES;
+end:
+       return rc;
+} /* request_locality() */
+
+/*
+ * release_locality release the active locality
+ * @param: chip, the tpm chip description.
+ */
+static void release_locality(struct tpm_chip *chip)
+{
+       struct i2c_client *client;
+       u8 data;
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+       data = TPM_ACCESS_ACTIVE_LOCALITY;
+
+       I2C_WRITE_DATA(client, TPM_ACCESS, &data, 1);
+}
+
+/*
+ * get_burstcount return the burstcount address 0x19 0x1A
+ * @param: chip, the chip description
+ * return: the burstcount.
+ */
+static int get_burstcount(struct tpm_chip *chip)
+{
+       unsigned long stop;
+       int burstcnt, status;
+       u8 tpm_reg, temp;
+
+       struct i2c_client *client = (struct i2c_client *) TPM_VPRIV(chip);
+
+       stop = jiffies + chip->vendor.timeout_d;
+       do {
+               tpm_reg = TPM_STS + 1;
+               status = I2C_READ_DATA(client, tpm_reg, &temp, 1);
+               if (status < 0)
+                       goto end;
+
+               tpm_reg = tpm_reg + 1;
+               burstcnt = temp;
+               status = I2C_READ_DATA(client, tpm_reg, &temp, 1);
+               if (status < 0)
+                       goto end;
+
+               burstcnt |= temp << 8;
+               if (burstcnt)
+                       return burstcnt;
+               msleep(TPM_TIMEOUT);
+       } while (time_before(jiffies, stop));
+
+end:
+       return -EBUSY;
+} /* get_burstcount() */
+
+/*
+ * wait_for_stat wait for a TPM_STS value
+ * @param: chip, the tpm chip description
+ * @param: mask, the value mask to wait
+ * @param: timeout, the timeout
+ * @param: queue, the wait queue.
+ * @return: the tpm status, 0 if success, -ETIME if timeout is reached.
+ */
+static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
+                        wait_queue_head_t *queue)
+{
+       unsigned long stop;
+       long rc;
+       u8 status;
+
+        if (chip->vendor.irq) {
+               rc = wait_for_serirq_timeout(chip, ((tpm_stm_i2c_status
+                                                       (chip) & mask) ==
+                                                      mask), timeout);
+               if (rc > 0)
+                       return 0;
+       } else{
+               stop = jiffies + timeout;
+               do {
+                       msleep(TPM_TIMEOUT);
+                       status = tpm_stm_i2c_status(chip);
+                       if ((status & mask) == mask)
+                               return 0;
+               } while (time_before(jiffies, stop));
+       }
+       return -ETIME;
+} /* wait_for_stat() */
+
+/*
+ * recv_data receive data
+ * @param: chip, the tpm chip description
+ * @param: buf, the buffer where the data are received
+ * @param: count, the number of data to receive
+ * @return: the number of bytes read from TPM FIFO.
+ */
+static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
+{
+       int size = 0, burstcnt, len;
+       struct i2c_client *client;
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+
+       while (size < count &&
+              wait_for_stat(chip,
+                            TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+                            chip->vendor.timeout_c,
+                            &chip->vendor.read_queue)
+              == 0) {
+               burstcnt = get_burstcount(chip);
+               len = min_t(int, burstcnt, count - size);
+               I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len);
+               size += len;
+       }
+       return size;
+}
+
+/*
+ * tpm_ioserirq_handler the serirq irq handler
+ * @param: irq, the tpm chip description
+ * @param: dev_id, the description of the chip
+ * @return: the status of the handler.
+ */
+static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id)
+{
+       struct tpm_chip *chip = dev_id;
+       struct i2c_client *client;
+       struct st33zp24_platform_data *pin_infos;
+
+       disable_irq_nosync(irq);
+
+       client = (struct i2c_client *) TPM_VPRIV(chip);
+       pin_infos = client->dev.platform_data;
+
+       complete(&pin_infos->irq_detection);
+       return IRQ_HANDLED;
+} /* tpm_ioserirq_handler() */
+
+
+/*
+ * tpm_stm_i2c_send send TPM commands through the I2C bus.
+ *
+ * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h
+ * @param: buf,        the buffer to send.
+ * @param: count, the number of bytes to send.
+ * @return: In case of success the number of bytes sent.
+ *                     In other case, a < 0 value describing the issue.
+ */
+static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf,
+                           size_t len)
+{
+       u32 status,
+           burstcnt = 0, i, size;
+       int ret;
+       u8 data;
+       struct i2c_client *client;
+
+       if (chip == NULL)
+               return -EBUSY;
+       if (len < TPM_HEADER_SIZE)
+               return -EBUSY;
+
+       client = (struct i2c_client *)TPM_VPRIV(chip);
+
+       client->flags = 0;
+
+       ret = request_locality(chip);
+       if (ret < 0)
+               return ret;
+
+       status = tpm_stm_i2c_status(chip);
+       if ((status & TPM_STS_COMMAND_READY) == 0) {
+               tpm_stm_i2c_cancel(chip);
+               if (wait_for_stat
+                   (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
+                    &chip->vendor.int_queue) < 0) {
+                       ret = -ETIME;
+                       goto out_err;
+               }
+       }
+
+       for (i = 0 ; i < len - 1 ;) {
+               burstcnt = get_burstcount(chip);
+               size = min_t(int, len - i - 1, burstcnt);
+               ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size);
+               if (ret < 0)
+                       goto out_err;
+
+               i += size;
+       }
+
+       status = tpm_stm_i2c_status(chip);
+       if ((status & TPM_STS_DATA_EXPECT) == 0) {
+               ret = -EIO;
+               goto out_err;
+       }
+
+       ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf + len - 1, 1);
+       if (ret < 0)
+               goto out_err;
+
+       status = tpm_stm_i2c_status(chip);
+       if ((status & TPM_STS_DATA_EXPECT) != 0) {
+               ret = -EIO;
+               goto out_err;
+       }
+
+       data = TPM_STS_GO;
+       I2C_WRITE_DATA(client, TPM_STS, &data, 1);
+
+       return len;
+out_err:
+       tpm_stm_i2c_cancel(chip);
+       release_locality(chip);
+       return ret;
+}
+
+/*
+ * tpm_stm_i2c_recv received TPM response through the I2C bus.
+ * @param: chip, the tpm_chip description as specified in driver/char/tpm/tpm.h.
+ * @param: buf,        the buffer to store datas.
+ * @param: count, the number of bytes to send.
+ * @return: In case of success the number of bytes received.
+ *             In other case, a < 0 value describing the issue.
+ */
+static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf,
+                           size_t count)
+{
+       int size = 0;
+       int expected;
+
+       if (chip == NULL)
+               return -EBUSY;
+
+       if (count < TPM_HEADER_SIZE) {
+               size = -EIO;
+               goto out;
+       }
+
+       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 (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;
+       }
+
+out:
+       chip->vendor.cancel(chip);
+       release_locality(chip);
+       return size;
+}
+
+static bool tpm_st33_i2c_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       return (status == TPM_STS_COMMAND_READY);
+}
+
+static const struct file_operations tpm_st33_i2c_fops = {
+       .owner = THIS_MODULE,
+       .llseek = no_llseek,
+       .read = tpm_read,
+       .write = tpm_write,
+       .open = tpm_open,
+       .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 struct attribute *stm_tpm_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, NULL,
+};
+
+static struct attribute_group stm_tpm_attr_grp = {
+       .attrs = stm_tpm_attrs
+};
+
+static struct tpm_vendor_specific st_i2c_tpm = {
+       .send = tpm_stm_i2c_send,
+       .recv = tpm_stm_i2c_recv,
+       .cancel = tpm_stm_i2c_cancel,
+       .status = tpm_stm_i2c_status,
+       .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
+       .req_canceled = tpm_st33_i2c_req_canceled,
+       .attr_group = &stm_tpm_attr_grp,
+       .miscdev = {.fops = &tpm_st33_i2c_fops,},
+};
+
+static int interrupts ;
+module_param(interrupts, int, 0444);
+MODULE_PARM_DESC(interrupts, "Enable interrupts");
+
+static int power_mgt = 1;
+module_param(power_mgt, int, 0444);
+MODULE_PARM_DESC(power_mgt, "Power Management");
+
+/*
+ * tpm_st33_i2c_probe initialize the TPM device
+ * @param: client, the i2c_client drescription (TPM I2C description).
+ * @param: id, the i2c_device_id struct.
+ * @return: 0 in case of success.
+ *      -1 in other case.
+ */
+static int
+tpm_st33_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+       int err;
+       u8 intmask;
+       struct tpm_chip *chip;
+       struct st33zp24_platform_data *platform_data;
+
+       if (client == NULL) {
+               pr_info("%s: i2c client is NULL. Device not accessible.\n",
+                       __func__);
+               err = -ENODEV;
+               goto end;
+       }
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
+               dev_info(&client->dev, "client not i2c capable\n");
+               err = -ENODEV;
+               goto end;
+       }
+
+       chip = tpm_register_hardware(&client->dev, &st_i2c_tpm);
+       if (!chip) {
+               dev_info(&client->dev, "fail chip\n");
+               err = -ENODEV;
+               goto end;
+       }
+
+       platform_data = client->dev.platform_data;
+
+       if (!platform_data) {
+               dev_info(&client->dev, "chip not available\n");
+               err = -ENODEV;
+               goto _tpm_clean_answer;
+       }
+
+       platform_data->tpm_i2c_buffer[0] =
+           kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+       if (platform_data->tpm_i2c_buffer[0] == NULL) {
+               err = -ENOMEM;
+               goto _tpm_clean_answer;
+       }
+       platform_data->tpm_i2c_buffer[1] =
+           kmalloc(TPM_BUFSIZE * sizeof(u8), GFP_KERNEL);
+       if (platform_data->tpm_i2c_buffer[1] == NULL) {
+               err = -ENOMEM;
+               goto _tpm_clean_response1;
+       }
+
+       TPM_VPRIV(chip) = client;
+
+       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);
+
+       chip->vendor.locality = LOCALITY0;
+
+       if (power_mgt) {
+               err = gpio_request(platform_data->io_lpcpd, "TPM IO_LPCPD");
+               if (err)
+                       goto _gpio_init1;
+               gpio_set_value(platform_data->io_lpcpd, 1);
+       }
+
+       if (interrupts) {
+               init_completion(&platform_data->irq_detection);
+               if (request_locality(chip) != LOCALITY0) {
+                       err = -ENODEV;
+                       goto _tpm_clean_response2;
+               }
+               err = gpio_request(platform_data->io_serirq, "TPM IO_SERIRQ");
+               if (err)
+                       goto _gpio_init2;
+
+               clear_interruption(client);
+               err = request_irq(gpio_to_irq(platform_data->io_serirq),
+                               &tpm_ioserirq_handler,
+                               IRQF_TRIGGER_HIGH,
+                               "TPM SERIRQ management", chip);
+               if (err < 0) {
+                       dev_err(chip->dev , "TPM SERIRQ signals %d not available\n",
+                       gpio_to_irq(platform_data->io_serirq));
+                       goto _irq_set;
+               }
+
+               err = I2C_READ_DATA(client, TPM_INT_ENABLE, &intmask, 1);
+               if (err < 0)
+                       goto _irq_set;
+
+               intmask |= TPM_INTF_CMD_READY_INT
+                       |  TPM_INTF_FIFO_AVALAIBLE_INT
+                       |  TPM_INTF_WAKE_UP_READY_INT
+                       |  TPM_INTF_LOCALITY_CHANGE_INT
+                       |  TPM_INTF_STS_VALID_INT
+                       |  TPM_INTF_DATA_AVAIL_INT;
+
+               err = I2C_WRITE_DATA(client, TPM_INT_ENABLE, &intmask, 1);
+               if (err < 0)
+                       goto _irq_set;
+
+               intmask = TPM_GLOBAL_INT_ENABLE;
+               err = I2C_WRITE_DATA(client, (TPM_INT_ENABLE + 3), &intmask, 1);
+               if (err < 0)
+                       goto _irq_set;
+
+               err = I2C_READ_DATA(client, TPM_INT_STATUS, &intmask, 1);
+               if (err < 0)
+                       goto _irq_set;
+
+               chip->vendor.irq = interrupts;
+
+               tpm_gen_interrupt(chip);
+       }
+
+       tpm_get_timeouts(chip);
+
+       i2c_set_clientdata(client, chip);
+
+       dev_info(chip->dev, "TPM I2C Initialized\n");
+       return 0;
+_irq_set:
+       free_irq(gpio_to_irq(platform_data->io_serirq), (void *) chip);
+_gpio_init2:
+       if (interrupts)
+               gpio_free(platform_data->io_serirq);
+_gpio_init1:
+       if (power_mgt)
+               gpio_free(platform_data->io_lpcpd);
+_tpm_clean_response2:
+       kzfree(platform_data->tpm_i2c_buffer[1]);
+       platform_data->tpm_i2c_buffer[1] = NULL;
+_tpm_clean_response1:
+       kzfree(platform_data->tpm_i2c_buffer[0]);
+       platform_data->tpm_i2c_buffer[0] = NULL;
+_tpm_clean_answer:
+       tpm_remove_hardware(chip->dev);
+end:
+       pr_info("TPM I2C initialisation fail\n");
+       return err;
+}
+
+/*
+ * tpm_st33_i2c_remove remove the TPM device
+ * @param: client, the i2c_client drescription (TPM I2C description).
+               clear_bit(0, &chip->is_open);
+ * @return: 0 in case of success.
+ */
+static int tpm_st33_i2c_remove(struct i2c_client *client)
+{
+       struct tpm_chip *chip = (struct tpm_chip *)i2c_get_clientdata(client);
+       struct st33zp24_platform_data *pin_infos =
+               ((struct i2c_client *) TPM_VPRIV(chip))->dev.platform_data;
+
+       if (pin_infos != NULL) {
+               free_irq(pin_infos->io_serirq, chip);
+
+               gpio_free(pin_infos->io_serirq);
+               gpio_free(pin_infos->io_lpcpd);
+
+               tpm_remove_hardware(chip->dev);
+
+               if (pin_infos->tpm_i2c_buffer[1] != NULL) {
+                       kzfree(pin_infos->tpm_i2c_buffer[1]);
+                       pin_infos->tpm_i2c_buffer[1] = NULL;
+               }
+               if (pin_infos->tpm_i2c_buffer[0] != NULL) {
+                       kzfree(pin_infos->tpm_i2c_buffer[0]);
+                       pin_infos->tpm_i2c_buffer[0] = NULL;
+               }
+       }
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+/*
+ * tpm_st33_i2c_pm_suspend suspend the TPM device
+ * Added: Work around when suspend and no tpm application is running, suspend
+ * may fail because chip->data_buffer is not set (only set in tpm_open in Linux
+ * TPM core)
+ * @param: client, the i2c_client drescription (TPM I2C description).
+ * @param: mesg, the power management message.
+ * @return: 0 in case of success.
+ */
+static int tpm_st33_i2c_pm_suspend(struct device *dev)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct st33zp24_platform_data *pin_infos = dev->platform_data;
+       int ret = 0;
+
+       if (power_mgt)
+               gpio_set_value(pin_infos->io_lpcpd, 0);
+       else{
+               if (chip->data_buffer == NULL)
+                       chip->data_buffer = pin_infos->tpm_i2c_buffer[0];
+               ret = tpm_pm_suspend(dev);
+       }
+       return ret;
+}                              /* tpm_st33_i2c_suspend() */
+
+/*
+ * tpm_st33_i2c_pm_resume resume the TPM device
+ * @param: client, the i2c_client drescription (TPM I2C description).
+ * @return: 0 in case of success.
+ */
+static int tpm_st33_i2c_pm_resume(struct device *dev)
+{
+       struct tpm_chip *chip = dev_get_drvdata(dev);
+       struct st33zp24_platform_data *pin_infos = dev->platform_data;
+
+       int ret = 0;
+
+       if (power_mgt) {
+               gpio_set_value(pin_infos->io_lpcpd, 1);
+               ret = wait_for_serirq_timeout(chip,
+                                         (chip->vendor.status(chip) &
+                                         TPM_STS_VALID) == TPM_STS_VALID,
+                                         chip->vendor.timeout_b);
+       } else{
+       if (chip->data_buffer == NULL)
+               chip->data_buffer = pin_infos->tpm_i2c_buffer[0];
+       ret = tpm_pm_resume(dev);
+       if (!ret)
+               tpm_do_selftest(chip);
+       }
+       return ret;
+}                              /* tpm_st33_i2c_pm_resume() */
+#endif
+
+static const struct i2c_device_id tpm_st33_i2c_id[] = {
+       {TPM_ST33_I2C, 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, tpm_st33_i2c_id);
+static SIMPLE_DEV_PM_OPS(tpm_st33_i2c_ops, tpm_st33_i2c_pm_suspend, tpm_st33_i2c_pm_resume);
+static struct i2c_driver tpm_st33_i2c_driver = {
+       .driver = {
+                  .owner = THIS_MODULE,
+                  .name = TPM_ST33_I2C,
+                  .pm = &tpm_st33_i2c_ops,
+                  },
+       .probe = tpm_st33_i2c_probe,
+       .remove = tpm_st33_i2c_remove,
+       .id_table = tpm_st33_i2c_id
+};
+
+module_i2c_driver(tpm_st33_i2c_driver);
+
+MODULE_AUTHOR("Christophe Ricard (tpmsupport@st.com)");
+MODULE_DESCRIPTION("STM TPM I2C ST33 Driver");
+MODULE_VERSION("1.2.0");
+MODULE_LICENSE("GPL");
diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.h b/drivers/char/tpm/tpm_i2c_stm_st33.h
new file mode 100644 (file)
index 0000000..439a432
--- /dev/null
@@ -0,0 +1,61 @@
+/*
+ * STMicroelectronics TPM I2C Linux driver for TPM ST33ZP24
+ * Copyright (C) 2009, 2010  STMicroelectronics
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * STMicroelectronics version 1.2.0, Copyright (C) 2010
+ * STMicroelectronics comes with ABSOLUTELY NO WARRANTY.
+ * This is free software, and you are welcome to redistribute it
+ * under certain conditions.
+ *
+ * @Author: Christophe RICARD tpmsupport@st.com
+ *
+ * @File: stm_st33_tpm_i2c.h
+ *
+ * @Date: 09/15/2010
+ */
+#ifndef __STM_ST33_TPM_I2C_MAIN_H__
+#define __STM_ST33_TPM_I2C_MAIN_H__
+
+#define TPM_ACCESS                     (0x0)
+#define TPM_STS                                (0x18)
+#define TPM_HASH_END                   (0x20)
+#define TPM_DATA_FIFO                  (0x24)
+#define TPM_HASH_DATA                  (0x24)
+#define TPM_HASH_START                 (0x28)
+#define TPM_INTF_CAPABILITY            (0x14)
+#define TPM_INT_STATUS                 (0x10)
+#define TPM_INT_ENABLE                 (0x08)
+
+#define TPM_DUMMY_BYTE                 0xAA
+#define TPM_WRITE_DIRECTION            0x80
+#define TPM_HEADER_SIZE                        10
+#define TPM_BUFSIZE                    2048
+
+#define LOCALITY0              0
+
+#define TPM_ST33_I2C                   "st33zp24_i2c"
+
+struct st33zp24_platform_data {
+       int io_serirq;
+       int io_lpcpd;
+       struct i2c_client *client;
+       u8 *tpm_i2c_buffer[2]; /* 0 Request 1 Response */
+       struct completion irq_detection;
+       struct mutex lock;
+};
+
+#endif /* __STM_ST33_TPM_I2C_MAIN_H__ */
index 9978609d93b27ef2bdb92bafb72fdf481f1b089c..56b07c35a13e173bf1cc5d10321d30d5a11f259e 100644 (file)
@@ -64,7 +64,7 @@ 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 (struct ibmvtpm_dev *)TPM_VPRIV(chip);
        return NULL;
 }
 
@@ -83,7 +83,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        u16 len;
        int sig;
 
-       ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
+       ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
 
        if (!ibmvtpm->rtce_buf) {
                dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
@@ -127,7 +127,7 @@ static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
        u64 *word = (u64 *) &crq;
        int rc;
 
-       ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
+       ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
 
        if (!ibmvtpm->rtce_buf) {
                dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
@@ -398,6 +398,11 @@ static int tpm_ibmvtpm_resume(struct device *dev)
        return rc;
 }
 
+static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       return (status == 0);
+}
+
 static const struct file_operations ibmvtpm_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
@@ -441,7 +446,7 @@ static const struct tpm_vendor_specific tpm_ibmvtpm = {
        .status = tpm_ibmvtpm_status,
        .req_complete_mask = 0,
        .req_complete_val = 0,
-       .req_canceled = 0,
+       .req_canceled = tpm_ibmvtpm_req_canceled,
        .attr_group = &ibmvtpm_attr_grp,
        .miscdev = { .fops = &ibmvtpm_ops, },
 };
@@ -647,7 +652,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
 
        ibmvtpm->dev = dev;
        ibmvtpm->vdev = vio_dev;
-       chip->vendor.data = (void *)ibmvtpm;
+       TPM_VPRIV(chip) = (void *)ibmvtpm;
 
        spin_lock_init(&ibmvtpm->rtce_lock);
 
index 640c9a427b59b1f788a6541e4d81e88e9f9f9f42..770c46f8eb3059806a76056cff34f8cf73fc317a 100644 (file)
@@ -227,6 +227,11 @@ static u8 tpm_nsc_status(struct tpm_chip *chip)
        return inb(chip->vendor.base + NSC_STATUS);
 }
 
+static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       return (status == NSC_STATUS_RDY);
+}
+
 static const struct file_operations nsc_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
@@ -258,7 +263,7 @@ static const struct tpm_vendor_specific tpm_nsc = {
        .status = tpm_nsc_status,
        .req_complete_mask = NSC_STATUS_OBF,
        .req_complete_val = NSC_STATUS_OBF,
-       .req_canceled = NSC_STATUS_RDY,
+       .req_canceled = tpm_nsc_req_canceled,
        .attr_group = &nsc_attr_grp,
        .miscdev = { .fops = &nsc_ops, },
 };
index ea31dafbcac2be1f627928963aad908550502a38..8a41b6be23a057bd5ff033f30d7de52ac7085a38 100644 (file)
@@ -84,6 +84,9 @@ static int is_itpm(struct pnp_dev *dev)
        struct acpi_device *acpi = pnp_acpi_device(dev);
        struct acpi_hardware_id *id;
 
+       if (!acpi)
+               return 0;
+
        list_for_each_entry(id, &acpi->pnp.ids, list) {
                if (!strcmp("INTC0102", id->id))
                        return 1;
@@ -98,6 +101,22 @@ static inline int is_itpm(struct pnp_dev *dev)
 }
 #endif
 
+/* Before we attempt to access the TPM we must see that the valid bit is set.
+ * The specification says that this bit is 0 at reset and remains 0 until the
+ * 'TPM has gone through its self test and initialization and has established
+ * correct values in the other bits.' */
+static int wait_startup(struct tpm_chip *chip, int l)
+{
+       unsigned long stop = jiffies + chip->vendor.timeout_a;
+       do {
+               if (ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
+                   TPM_ACCESS_VALID)
+                       return 0;
+               msleep(TPM_TIMEOUT);
+       } while (time_before(jiffies, stop));
+       return -1;
+}
+
 static int check_locality(struct tpm_chip *chip, int l)
 {
        if ((ioread8(chip->vendor.iobase + TPM_ACCESS(l)) &
@@ -198,7 +217,7 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
               wait_for_tpm_stat(chip,
                                 TPM_STS_DATA_AVAIL | TPM_STS_VALID,
                                 chip->vendor.timeout_c,
-                                &chip->vendor.read_queue)
+                                &chip->vendor.read_queue, true)
               == 0) {
                burstcnt = get_burstcount(chip);
                for (; burstcnt > 0 && size < count; burstcnt--)
@@ -241,7 +260,7 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
        }
 
        wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                         &chip->vendor.int_queue);
+                         &chip->vendor.int_queue, false);
        status = tpm_tis_status(chip);
        if (status & TPM_STS_DATA_AVAIL) {      /* retry? */
                dev_err(chip->dev, "Error left over data\n");
@@ -277,7 +296,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
                tpm_tis_ready(chip);
                if (wait_for_tpm_stat
                    (chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
-                    &chip->vendor.int_queue) < 0) {
+                    &chip->vendor.int_queue, false) < 0) {
                        rc = -ETIME;
                        goto out_err;
                }
@@ -292,7 +311,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
                }
 
                wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                                 &chip->vendor.int_queue);
+                                 &chip->vendor.int_queue, false);
                status = tpm_tis_status(chip);
                if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
                        rc = -EIO;
@@ -304,7 +323,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
        iowrite8(buf[count],
                 chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
        wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
-                         &chip->vendor.int_queue);
+                         &chip->vendor.int_queue, false);
        status = tpm_tis_status(chip);
        if ((status & TPM_STS_DATA_EXPECT) != 0) {
                rc = -EIO;
@@ -342,7 +361,7 @@ static int tpm_tis_send(struct tpm_chip *chip, u8 *buf, size_t len)
                if (wait_for_tpm_stat
                    (chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
                     tpm_calc_ordinal_duration(chip, ordinal),
-                    &chip->vendor.read_queue) < 0) {
+                    &chip->vendor.read_queue, false) < 0) {
                        rc = -ETIME;
                        goto out_err;
                }
@@ -374,7 +393,7 @@ static int probe_itpm(struct tpm_chip *chip)
        if (vendor != TPM_VID_INTEL)
                return 0;
 
-       itpm = 0;
+       itpm = false;
 
        rc = tpm_tis_send_data(chip, cmd_getticks, len);
        if (rc == 0)
@@ -383,7 +402,7 @@ static int probe_itpm(struct tpm_chip *chip)
        tpm_tis_ready(chip);
        release_locality(chip, chip->vendor.locality, 0);
 
-       itpm = 1;
+       itpm = true;
 
        rc = tpm_tis_send_data(chip, cmd_getticks, len);
        if (rc == 0) {
@@ -400,6 +419,19 @@ out:
        return rc;
 }
 
+static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status)
+{
+       switch (chip->vendor.manufacturer_id) {
+       case TPM_VID_WINBOND:
+               return ((status == TPM_STS_VALID) ||
+                       (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY)));
+       case TPM_VID_STM:
+               return (status == (TPM_STS_VALID | TPM_STS_COMMAND_READY));
+       default:
+               return (status == TPM_STS_COMMAND_READY);
+       }
+}
+
 static const struct file_operations tis_ops = {
        .owner = THIS_MODULE,
        .llseek = no_llseek,
@@ -445,7 +477,7 @@ static struct tpm_vendor_specific tpm_tis = {
        .cancel = tpm_tis_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,
+       .req_canceled = tpm_tis_req_canceled,
        .attr_group = &tis_attr_grp,
        .miscdev = {
                    .fops = &tis_ops,},
@@ -502,7 +534,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
        return IRQ_HANDLED;
 }
 
-static bool interrupts = 1;
+static bool interrupts = true;
 module_param(interrupts, bool, 0444);
 MODULE_PARM_DESC(interrupts, "Enable interrupts");
 
@@ -528,12 +560,18 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
        chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
        chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
 
+       if (wait_startup(chip, 0) != 0) {
+               rc = -ENODEV;
+               goto out_err;
+       }
+
        if (request_locality(chip, 0) != 0) {
                rc = -ENODEV;
                goto out_err;
        }
 
        vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
+       chip->vendor.manufacturer_id = vendor;
 
        dev_info(dev,
                 "1.2 TPM (device-id 0x%X, rev-id %d)\n",
@@ -545,7 +583,7 @@ static int tpm_tis_init(struct device *dev, resource_size_t start,
                        rc = -ENODEV;
                        goto out_err;
                }
-               itpm = (probe == 0) ? 0 : 1;
+               itpm = !!probe;
        }
 
        if (itpm)
@@ -741,10 +779,10 @@ static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
        if (pnp_irq_valid(pnp_dev, 0))
                irq = pnp_irq(pnp_dev, 0);
        else
-               interrupts = 0;
+               interrupts = false;
 
        if (is_itpm(pnp_dev))
-               itpm = 1;
+               itpm = true;
 
        return tpm_tis_init(&pnp_dev->dev, start, len, irq);
 }
index dc2be7ed1765b0dc3675c97b2cf862dc969dcd90..2f31e6a45f0af3f4e4bccd38aeb1d8324c72eeff 100644 (file)
 
 static struct crypto_shash *shash;
 
-static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
-                       unsigned long  msglen,
-                       unsigned long  modulus_bitlen,
-                       unsigned char *out,
-                       unsigned long *outlen)
+static const char *pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
+                                               unsigned long  msglen,
+                                               unsigned long  modulus_bitlen,
+                                               unsigned long *outlen)
 {
        unsigned long modulus_len, ps_len, i;
 
@@ -42,11 +41,11 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
 
        /* test message size */
        if ((msglen > modulus_len) || (modulus_len < 11))
-               return -EINVAL;
+               return NULL;
 
        /* separate encoded message */
-       if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1))
-               return -EINVAL;
+       if (msg[0] != 0x00 || msg[1] != 0x01)
+               return NULL;
 
        for (i = 2; i < modulus_len - 1; i++)
                if (msg[i] != 0xFF)
@@ -56,19 +55,13 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
        if (msg[i] != 0)
                /* There was no octet with hexadecimal value 0x00
                to separate ps from m. */
-               return -EINVAL;
+               return NULL;
 
        ps_len = i - 2;
 
-       if (*outlen < (msglen - (2 + ps_len + 1))) {
-               *outlen = msglen - (2 + ps_len + 1);
-               return -EOVERFLOW;
-       }
-
        *outlen = (msglen - (2 + ps_len + 1));
-       memcpy(out, &msg[2 + ps_len + 1], *outlen);
 
-       return 0;
+       return msg + 2 + ps_len + 1;
 }
 
 /*
@@ -83,7 +76,8 @@ static int digsig_verify_rsa(struct key *key,
        unsigned long mlen, mblen;
        unsigned nret, l;
        int head, i;
-       unsigned char *out1 = NULL, *out2 = NULL;
+       unsigned char *out1 = NULL;
+       const char *m;
        MPI in = NULL, res = NULL, pkey[2];
        uint8_t *p, *datap, *endp;
        struct user_key_payload *ukp;
@@ -120,7 +114,7 @@ static int digsig_verify_rsa(struct key *key,
        }
 
        mblen = mpi_get_nbits(pkey[0]);
-       mlen = (mblen + 7)/8;
+       mlen = DIV_ROUND_UP(mblen, 8);
 
        if (mlen == 0)
                goto err;
@@ -129,10 +123,6 @@ static int digsig_verify_rsa(struct key *key,
        if (!out1)
                goto err;
 
-       out2 = kzalloc(mlen, GFP_KERNEL);
-       if (!out2)
-               goto err;
-
        nret = siglen;
        in = mpi_read_from_buffer(sig, &nret);
        if (!in)
@@ -164,18 +154,15 @@ static int digsig_verify_rsa(struct key *key,
 
        kfree(p);
 
-       err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len);
-       if (err)
-               goto err;
+       m = pkcs_1_v1_5_decode_emsa(out1, len, mblen, &len);
 
-       if (len != hlen || memcmp(out2, h, hlen))
+       if (!m || len != hlen || memcmp(m, h, hlen))
                err = -EINVAL;
 
 err:
        mpi_free(in);
        mpi_free(res);
        kfree(out1);
-       kfree(out2);
        while (--i >= 0)
                mpi_free(pkey[i]);
 err1:
index 77adcf6bc2576e4266a76d2092d637f2f7c1fa9c..60cf765628e90d84de0f177173b2d025ad870039 100644 (file)
 typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
 typedef int mpi_size_t;                /* (must be a signed type) */
 
-#define ABS(x) (x >= 0 ? x : -x)
-#define MIN(l, o) ((l) < (o) ? (l) : (o))
-#define MAX(h, i) ((h) > (i) ? (h) : (i))
-
 static inline int RESIZE_IF_NEEDED(MPI a, unsigned b)
 {
        if (a->alloced < b)
index 3962b7f7fe3f08ec4b639e328ddc6dcf4baa05fd..5f9c44cdf1f548f5440e6ee86473af3ab5248b32 100644 (file)
@@ -52,7 +52,7 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes)
        else
                nbits = 0;
 
-       nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
+       nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB);
        val = mpi_alloc(nlimbs);
        if (!val)
                return NULL;
@@ -96,8 +96,8 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread)
        buffer += 2;
        nread = 2;
 
-       nbytes = (nbits + 7) / 8;
-       nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
+       nbytes = DIV_ROUND_UP(nbits, 8);
+       nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB);
        val = mpi_alloc(nlimbs);
        if (!val)
                return NULL;
@@ -193,7 +193,7 @@ int mpi_set_buffer(MPI a, const void *xbuffer, unsigned nbytes, int sign)
        int nlimbs;
        int i;
 
-       nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
+       nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB);
        if (RESIZE_IF_NEEDED(a, nlimbs) < 0)
                return -ENOMEM;
        a->sign = sign;
index 5bd1cc1b4a54dcae8681bf7e0c092d374614ab6d..4bb3a775a996041c02fe6db7373e340e73a5f7ac 100644 (file)
@@ -17,5 +17,17 @@ config INTEGRITY_SIGNATURE
          This is useful for evm and module keyrings, when keys are
          usually only added from initramfs.
 
+config INTEGRITY_ASYMMETRIC_KEYS
+       boolean "Enable asymmetric keys support"
+       depends on INTEGRITY_SIGNATURE
+       default n
+        select ASYMMETRIC_KEY_TYPE
+        select ASYMMETRIC_PUBLIC_KEY_SUBTYPE
+        select PUBLIC_KEY_ALGO_RSA
+        select X509_CERTIFICATE_PARSER
+       help
+         This option enables digital signature verification using
+         asymmetric keys.
+
 source security/integrity/ima/Kconfig
 source security/integrity/evm/Kconfig
index d43799cc14f69a67da22249d637b5f9d1334b357..ebb6409b3fcb97caeb15d211e1427a1148050f60 100644 (file)
@@ -4,6 +4,7 @@
 
 obj-$(CONFIG_INTEGRITY) += integrity.o
 obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
+obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
 
 integrity-y := iint.o
 
index 2dc167d7cde9f1ca3158eacbead2c2aa7cb06a48..0b759e17a1311abc3a2fb1f6fbf7e554b8f71ce5 100644 (file)
@@ -44,5 +44,14 @@ int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
                }
        }
 
-       return digsig_verify(keyring[id], sig, siglen, digest, digestlen);
+       switch (sig[0]) {
+       case 1:
+               return digsig_verify(keyring[id], sig, siglen,
+                                    digest, digestlen);
+       case 2:
+               return asymmetric_verify(keyring[id], sig, siglen,
+                                        digest, digestlen);
+       }
+
+       return -EOPNOTSUPP;
 }
diff --git a/security/integrity/digsig_asymmetric.c b/security/integrity/digsig_asymmetric.c
new file mode 100644 (file)
index 0000000..b475466
--- /dev/null
@@ -0,0 +1,115 @@
+/*
+ * Copyright (C) 2013 Intel Corporation
+ *
+ * Author:
+ * Dmitry Kasatkin <dmitry.kasatkin@intel.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.
+ *
+ */
+
+#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
+
+#include <linux/err.h>
+#include <linux/key-type.h>
+#include <crypto/public_key.h>
+#include <keys/asymmetric-type.h>
+
+#include "integrity.h"
+
+/*
+ * signature format v2 - for using with asymmetric keys
+ */
+struct signature_v2_hdr {
+       uint8_t version;        /* signature format version */
+       uint8_t hash_algo;      /* Digest algorithm [enum pkey_hash_algo] */
+       uint32_t keyid;         /* IMA key identifier - not X509/PGP specific*/
+       uint16_t sig_size;      /* signature size */
+       uint8_t sig[0];         /* signature payload */
+} __packed;
+
+/*
+ * Request an asymmetric key.
+ */
+static struct key *request_asymmetric_key(struct key *keyring, uint32_t keyid)
+{
+       struct key *key;
+       char name[12];
+
+       sprintf(name, "id:%x", keyid);
+
+       pr_debug("key search: \"%s\"\n", name);
+
+       if (keyring) {
+               /* search in specific keyring */
+               key_ref_t kref;
+               kref = keyring_search(make_key_ref(keyring, 1),
+                                     &key_type_asymmetric, name);
+               if (IS_ERR(kref))
+                       key = ERR_CAST(kref);
+               else
+                       key = key_ref_to_ptr(kref);
+       } else {
+               key = request_key(&key_type_asymmetric, name, NULL);
+       }
+
+       if (IS_ERR(key)) {
+               pr_warn("Request for unknown key '%s' err %ld\n",
+                       name, PTR_ERR(key));
+               switch (PTR_ERR(key)) {
+                       /* Hide some search errors */
+               case -EACCES:
+               case -ENOTDIR:
+               case -EAGAIN:
+                       return ERR_PTR(-ENOKEY);
+               default:
+                       return key;
+               }
+       }
+
+       pr_debug("%s() = 0 [%x]\n", __func__, key_serial(key));
+
+       return key;
+}
+
+int asymmetric_verify(struct key *keyring, const char *sig,
+                     int siglen, const char *data, int datalen)
+{
+       struct public_key_signature pks;
+       struct signature_v2_hdr *hdr = (struct signature_v2_hdr *)sig;
+       struct key *key;
+       int ret = -ENOMEM;
+
+       if (siglen <= sizeof(*hdr))
+               return -EBADMSG;
+
+       siglen -= sizeof(*hdr);
+
+       if (siglen != __be16_to_cpu(hdr->sig_size))
+               return -EBADMSG;
+
+       if (hdr->hash_algo >= PKEY_HASH__LAST)
+               return -ENOPKG;
+
+       key = request_asymmetric_key(keyring, __be32_to_cpu(hdr->keyid));
+       if (IS_ERR(key))
+               return PTR_ERR(key);
+
+       memset(&pks, 0, sizeof(pks));
+
+       pks.pkey_hash_algo = hdr->hash_algo;
+       pks.digest = (u8 *)data;
+       pks.digest_size = datalen;
+       pks.nr_mpi = 1;
+       pks.rsa.s = mpi_read_raw_data(hdr->sig, siglen);
+
+       if (pks.rsa.s)
+               ret = verify_signature(key, &pks);
+
+       mpi_free(pks.rsa.s);
+       key_put(key);
+       pr_debug("%s() = %d\n", __func__, ret);
+       return ret;
+}
index afbb59dd262d57f9ee635c3dcd6caeff6751d932..fea9749c37562b1006885cf56410361a64f31db1 100644 (file)
@@ -11,3 +11,16 @@ config EVM
          integrity attacks.
 
          If you are unsure how to answer this question, answer N.
+
+config EVM_HMAC_VERSION
+       int "EVM HMAC version"
+       depends on EVM
+       default 2
+       help
+         This options adds EVM HMAC version support.
+         1 - original version
+         2 - add per filesystem unique identifier (UUID) (default)
+
+         WARNING: changing the HMAC calculation method or adding 
+         additional info to the calculation, requires existing EVM
+         labeled file systems to be relabeled.  
index c885247ebcf7d7b64c5f97ac0449e75488de317b..30bd1ec0232e1ddd2a7d7585e752ae6c6bedcdc9 100644 (file)
@@ -24,6 +24,7 @@
 extern int evm_initialized;
 extern char *evm_hmac;
 extern char *evm_hash;
+extern int evm_hmac_version;
 
 extern struct crypto_shash *hmac_tfm;
 extern struct crypto_shash *hash_tfm;
@@ -45,6 +46,5 @@ extern int evm_calc_hash(struct dentry *dentry, const char *req_xattr_name,
 extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
                         char *hmac_val);
 extern int evm_init_secfs(void);
-extern void evm_cleanup_secfs(void);
 
 #endif
index 7dd538ef5b8319e645465eb62294158f3dbe271a..3bab89eb21d608b1123b97188234137db83869b8 100644 (file)
@@ -110,6 +110,9 @@ static void hmac_add_misc(struct shash_desc *desc, struct inode *inode,
        hmac_misc.gid = from_kgid(&init_user_ns, inode->i_gid);
        hmac_misc.mode = inode->i_mode;
        crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
+       if (evm_hmac_version > 1)
+               crypto_shash_update(desc, inode->i_sb->s_uuid,
+                                   sizeof(inode->i_sb->s_uuid));
        crypto_shash_final(desc, digest);
 }
 
index eb5484504f506490eb7c4f90a81cba92b7020c40..cdbde1762189f56ebdcd133e3cf6fdfba37b3933 100644 (file)
@@ -26,6 +26,7 @@ int evm_initialized;
 
 char *evm_hmac = "hmac(sha1)";
 char *evm_hash = "sha1";
+int evm_hmac_version = CONFIG_EVM_HMAC_VERSION;
 
 char *evm_config_xattrnames[] = {
 #ifdef CONFIG_SECURITY_SELINUX
@@ -427,15 +428,6 @@ err:
        return error;
 }
 
-static void __exit cleanup_evm(void)
-{
-       evm_cleanup_secfs();
-       if (hmac_tfm)
-               crypto_free_shash(hmac_tfm);
-       if (hash_tfm)
-               crypto_free_shash(hash_tfm);
-}
-
 /*
  * evm_display_config - list the EVM protected security extended attributes
  */
index ac7629950578edea449e196a520512a197834770..30f670ad6ac3a63ce98db857ad812eab144d6419 100644 (file)
@@ -100,9 +100,3 @@ int __init evm_init_secfs(void)
                error = -EFAULT;
        return error;
 }
-
-void __exit evm_cleanup_secfs(void)
-{
-       if (evm_init_tpm)
-               securityfs_remove(evm_init_tpm);
-}
index d82a5a13d8551ca1b857a13c401f706340b88850..74522dbd10a6e093fe293786f83a10ca8d7361bc 100644 (file)
@@ -72,7 +72,10 @@ static void iint_free(struct integrity_iint_cache *iint)
 {
        iint->version = 0;
        iint->flags = 0UL;
-       iint->ima_status = INTEGRITY_UNKNOWN;
+       iint->ima_file_status = INTEGRITY_UNKNOWN;
+       iint->ima_mmap_status = INTEGRITY_UNKNOWN;
+       iint->ima_bprm_status = INTEGRITY_UNKNOWN;
+       iint->ima_module_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
        kmem_cache_free(iint_cache, iint);
 }
@@ -149,7 +152,10 @@ static void init_once(void *foo)
        memset(iint, 0, sizeof *iint);
        iint->version = 0;
        iint->flags = 0UL;
-       iint->ima_status = INTEGRITY_UNKNOWN;
+       iint->ima_file_status = INTEGRITY_UNKNOWN;
+       iint->ima_mmap_status = INTEGRITY_UNKNOWN;
+       iint->ima_bprm_status = INTEGRITY_UNKNOWN;
+       iint->ima_module_status = INTEGRITY_UNKNOWN;
        iint->evm_status = INTEGRITY_UNKNOWN;
 }
 
index 079a85dc37b2ba9fd7d649a1a0da0e2843a9252c..a41c9c18e5e0706498d44a26e3ece7d49f307e4c 100644 (file)
@@ -84,11 +84,12 @@ void ima_fs_cleanup(void);
 int ima_inode_alloc(struct inode *inode);
 int ima_add_template_entry(struct ima_template_entry *entry, int violation,
                           const char *op, struct inode *inode);
-int ima_calc_hash(struct file *file, char *digest);
-int ima_calc_template_hash(int template_len, void *template, char *digest);
+int ima_calc_file_hash(struct file *file, char *digest);
+int ima_calc_buffer_hash(const void *data, int len, char *digest);
 int ima_calc_boot_aggregate(char *digest);
 void ima_add_violation(struct inode *inode, const unsigned char *filename,
                       const char *op, const char *cause);
+int ima_init_crypto(void);
 
 /*
  * used to protect h_table and sha_table
@@ -119,6 +120,7 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
 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);
+const char *ima_d_path(struct path *path, char **pathbuf);
 
 /* rbtree tree calls to lookup, insert, delete
  * integrity data associated with an inode.
@@ -127,7 +129,7 @@ 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, MODULE_CHECK, POST_SETATTR };
+enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR };
 
 int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
                     int flags);
@@ -142,13 +144,16 @@ void ima_delete_rules(void);
 #define IMA_APPRAISE_MODULES   0x04
 
 #ifdef CONFIG_IMA_APPRAISE
-int ima_appraise_measurement(struct integrity_iint_cache *iint,
+int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
                             struct file *file, const unsigned char *filename);
 int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
 void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
+enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+                                          int func);
 
 #else
-static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
+static inline int ima_appraise_measurement(int func,
+                                          struct integrity_iint_cache *iint,
                                           struct file *file,
                                           const unsigned char *filename)
 {
@@ -165,6 +170,12 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint,
                                    struct file *file)
 {
 }
+
+static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
+                                                        *iint, int func)
+{
+       return INTEGRITY_UNKNOWN;
+}
 #endif
 
 /* LSM based policy rules require audit */
index 0cea3db216576520373a40c4d2df6ac8b1450257..d9030b29d84d0d90de855d77418b7ed47f74b484 100644 (file)
@@ -50,8 +50,8 @@ int ima_store_template(struct ima_template_entry *entry,
        entry->template_len = sizeof(entry->template);
 
        if (!violation) {
-               result = ima_calc_template_hash(entry->template_len,
-                                               &entry->template,
+               result = ima_calc_buffer_hash(&entry->template,
+                                               entry->template_len,
                                                entry->digest);
                if (result < 0) {
                        integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
@@ -100,12 +100,12 @@ err_out:
  * 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, MODULE_CHECK)
+ * @function: calling function (FILE_CHECK, BPRM_CHECK, MMAP_CHECK, MODULE_CHECK)
  *
  * The policy is defined in terms of keypairs:
  *             subj=, obj=, type=, func=, mask=, fsmagic=
  *     subj,obj, and type: are LSM specific.
- *     func: FILE_CHECK | BPRM_CHECK | FILE_MMAP | MODULE_CHECK
+ *     func: FILE_CHECK | BPRM_CHECK | MMAP_CHECK | MODULE_CHECK
  *     mask: contains the permission mask
  *     fsmagic: hex value
  *
@@ -148,7 +148,7 @@ int ima_collect_measurement(struct integrity_iint_cache *iint,
                u64 i_version = file->f_dentry->d_inode->i_version;
 
                iint->ima_xattr.type = IMA_XATTR_DIGEST;
-               result = ima_calc_hash(file, iint->ima_xattr.digest);
+               result = ima_calc_file_hash(file, iint->ima_xattr.digest);
                if (!result) {
                        iint->version = i_version;
                        iint->flags |= IMA_COLLECTED;
@@ -237,3 +237,20 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
 
        iint->flags |= IMA_AUDITED;
 }
+
+const char *ima_d_path(struct path *path, char **pathbuf)
+{
+       char *pathname = NULL;
+
+       /* We will allow 11 spaces for ' (deleted)' to be appended */
+       *pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
+       if (*pathbuf) {
+               pathname = d_path(path, *pathbuf, PATH_MAX + 11);
+               if (IS_ERR(pathname)) {
+                       kfree(*pathbuf);
+                       *pathbuf = NULL;
+                       pathname = NULL;
+               }
+       }
+       return pathname;
+}
index bdc8ba1d1d27855527aa7a265cd6de07fee2d291..2d4becab8918053a6d3520dc32527a33d92a3717 100644 (file)
@@ -42,12 +42,69 @@ int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func)
        return ima_match_policy(inode, func, mask, IMA_APPRAISE);
 }
 
-static void ima_fix_xattr(struct dentry *dentry,
+static int 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);
+       return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
+                                    (u8 *)&iint->ima_xattr,
+                                     sizeof(iint->ima_xattr), 0);
+}
+
+/* Return specific func appraised cached result */
+enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint,
+                                          int func)
+{
+       switch(func) {
+       case MMAP_CHECK:
+               return iint->ima_mmap_status;
+       case BPRM_CHECK:
+               return iint->ima_bprm_status;
+       case MODULE_CHECK:
+               return iint->ima_module_status;
+       case FILE_CHECK:
+       default:
+               return iint->ima_file_status;
+       }
+}
+
+static void ima_set_cache_status(struct integrity_iint_cache *iint,
+                                int func, enum integrity_status status)
+{
+       switch(func) {
+       case MMAP_CHECK:
+               iint->ima_mmap_status = status;
+               break;
+       case BPRM_CHECK:
+               iint->ima_bprm_status = status;
+               break;
+       case MODULE_CHECK:
+               iint->ima_module_status = status;
+               break;
+       case FILE_CHECK:
+       default:
+               iint->ima_file_status = status;
+               break;
+       }
+}
+
+static void ima_cache_flags(struct integrity_iint_cache *iint, int func)
+{
+       switch(func) {
+       case MMAP_CHECK:
+               iint->flags |= (IMA_MMAP_APPRAISED | IMA_APPRAISED);
+               break;
+       case BPRM_CHECK:
+               iint->flags |= (IMA_BPRM_APPRAISED | IMA_APPRAISED);
+               break;
+       case MODULE_CHECK:
+               iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED);
+               break;
+       case FILE_CHECK:
+       default:
+               iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED);
+               break;
+       }
 }
 
 /*
@@ -58,7 +115,7 @@ static void ima_fix_xattr(struct dentry *dentry,
  *
  * Return 0 on success, error code otherwise
  */
-int ima_appraise_measurement(struct integrity_iint_cache *iint,
+int ima_appraise_measurement(int func, struct integrity_iint_cache *iint,
                             struct file *file, const unsigned char *filename)
 {
        struct dentry *dentry = file->f_dentry;
@@ -74,9 +131,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
        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) {
@@ -98,19 +152,18 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
                        cause = "invalid-HMAC";
                goto out;
        }
-
        switch (xattr_value->type) {
        case IMA_XATTR_DIGEST:
+               if (iint->flags & IMA_DIGSIG_REQUIRED) {
+                       cause = "IMA signature required";
+                       status = INTEGRITY_FAIL;
+                       break;
+               }
                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;
@@ -141,15 +194,15 @@ out:
                if ((ima_appraise & IMA_APPRAISE_FIX) &&
                    (!xattr_value ||
                     xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
-                       ima_fix_xattr(dentry, iint);
-                       status = INTEGRITY_PASS;
+                       if (!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;
+               ima_cache_flags(iint, func);
        }
-       iint->ima_status = status;
+       ima_set_cache_status(iint, func, status);
        kfree(xattr_value);
        return status;
 }
@@ -195,10 +248,11 @@ void ima_inode_post_setattr(struct dentry *dentry)
        must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
        iint = integrity_iint_find(inode);
        if (iint) {
+               iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
+                                IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
+                                IMA_ACTION_FLAGS);
                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);
index b21ee5b5495a8a4a3bf3e5626cd367d16158757f..b691e0f3830ca7189955d8cb8a2095ead58c1d1b 100644 (file)
 #include <linux/scatterlist.h>
 #include <linux/err.h>
 #include <linux/slab.h>
+#include <crypto/hash.h>
 #include "ima.h"
 
-static int init_desc(struct hash_desc *desc)
+static struct crypto_shash *ima_shash_tfm;
+
+int ima_init_crypto(void)
 {
-       int rc;
+       long rc;
 
-       desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
-       if (IS_ERR(desc->tfm)) {
-               pr_info("IMA: failed to load %s transform: %ld\n",
-                       ima_hash, PTR_ERR(desc->tfm));
-               rc = PTR_ERR(desc->tfm);
+       ima_shash_tfm = crypto_alloc_shash(ima_hash, 0, 0);
+       if (IS_ERR(ima_shash_tfm)) {
+               rc = PTR_ERR(ima_shash_tfm);
+               pr_err("Can not allocate %s (reason: %ld)\n", ima_hash, rc);
                return rc;
        }
-       desc->flags = 0;
-       rc = crypto_hash_init(desc);
-       if (rc)
-               crypto_free_hash(desc->tfm);
-       return rc;
+       return 0;
 }
 
 /*
  * Calculate the MD5/SHA1 file digest
  */
-int ima_calc_hash(struct file *file, char *digest)
+int ima_calc_file_hash(struct file *file, char *digest)
 {
-       struct hash_desc desc;
-       struct scatterlist sg[1];
        loff_t i_size, offset = 0;
        char *rbuf;
        int rc, read = 0;
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(ima_shash_tfm)];
+       } desc;
 
-       rc = init_desc(&desc);
+       desc.shash.tfm = ima_shash_tfm;
+       desc.shash.flags = 0;
+
+       rc = crypto_shash_init(&desc.shash);
        if (rc != 0)
                return rc;
 
@@ -75,41 +78,34 @@ int ima_calc_hash(struct file *file, char *digest)
                if (rbuf_len == 0)
                        break;
                offset += rbuf_len;
-               sg_init_one(sg, rbuf, rbuf_len);
 
-               rc = crypto_hash_update(&desc, sg, rbuf_len);
+               rc = crypto_shash_update(&desc.shash, rbuf, rbuf_len);
                if (rc)
                        break;
        }
        kfree(rbuf);
        if (!rc)
-               rc = crypto_hash_final(&desc, digest);
+               rc = crypto_shash_final(&desc.shash, digest);
        if (read)
                file->f_mode &= ~FMODE_READ;
 out:
-       crypto_free_hash(desc.tfm);
        return rc;
 }
 
 /*
- * Calculate the hash of a given template
+ * Calculate the hash of a given buffer
  */
-int ima_calc_template_hash(int template_len, void *template, char *digest)
+int ima_calc_buffer_hash(const void *data, int len, char *digest)
 {
-       struct hash_desc desc;
-       struct scatterlist sg[1];
-       int rc;
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(ima_shash_tfm)];
+       } desc;
 
-       rc = init_desc(&desc);
-       if (rc != 0)
-               return rc;
+       desc.shash.tfm = ima_shash_tfm;
+       desc.shash.flags = 0;
 
-       sg_init_one(sg, template, template_len);
-       rc = crypto_hash_update(&desc, sg, template_len);
-       if (!rc)
-               rc = crypto_hash_final(&desc, digest);
-       crypto_free_hash(desc.tfm);
-       return rc;
+       return crypto_shash_digest(&desc.shash, data, len, digest);
 }
 
 static void __init ima_pcrread(int idx, u8 *pcr)
@@ -126,12 +122,17 @@ static void __init ima_pcrread(int idx, u8 *pcr)
  */
 int __init ima_calc_boot_aggregate(char *digest)
 {
-       struct hash_desc desc;
-       struct scatterlist sg;
        u8 pcr_i[IMA_DIGEST_SIZE];
        int rc, i;
+       struct {
+               struct shash_desc shash;
+               char ctx[crypto_shash_descsize(ima_shash_tfm)];
+       } desc;
+
+       desc.shash.tfm = ima_shash_tfm;
+       desc.shash.flags = 0;
 
-       rc = init_desc(&desc);
+       rc = crypto_shash_init(&desc.shash);
        if (rc != 0)
                return rc;
 
@@ -139,11 +140,9 @@ int __init ima_calc_boot_aggregate(char *digest)
        for (i = TPM_PCR0; i < TPM_PCR8; i++) {
                ima_pcrread(i, pcr_i);
                /* now accumulate with current aggregate */
-               sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
-               rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
+               rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE);
        }
        if (!rc)
-               crypto_hash_final(&desc, digest);
-       crypto_free_hash(desc.tfm);
+               crypto_shash_final(&desc.shash, digest);
        return rc;
 }
index b5dfd534f13dad49b958748687800ccc02df3353..162ea723db3df5f07a2dd23f4bcc66e1af03a898 100644 (file)
@@ -85,6 +85,9 @@ int __init ima_init(void)
        if (!ima_used_chip)
                pr_info("IMA: No TPM chip found, activating TPM-bypass!\n");
 
+       rc = ima_init_crypto();
+       if (rc)
+               return rc;
        ima_add_boot_aggregate();       /* boot aggregate must be first entry */
        ima_init_policy();
 
index dba965de90d3f838c82e1a93202bf585fa1e5cd6..5127afcc4b8982a4353b87ccc5f4542ec7862f39 100644 (file)
@@ -61,7 +61,8 @@ static void ima_rdwr_violation_check(struct file *file)
        fmode_t mode = file->f_mode;
        int must_measure;
        bool send_tomtou = false, send_writers = false;
-       unsigned char *pathname = NULL, *pathbuf = NULL;
+       char *pathbuf = NULL;
+       const char *pathname;
 
        if (!S_ISREG(inode->i_mode) || !ima_initialized)
                return;
@@ -86,22 +87,15 @@ out:
        if (!send_tomtou && !send_writers)
                return;
 
-       /* We will allow 11 spaces for ' (deleted)' to be appended */
-       pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
-       if (pathbuf) {
-               pathname = d_path(&file->f_path, pathbuf, PATH_MAX + 11);
-               if (IS_ERR(pathname))
-                       pathname = NULL;
-               else if (strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
-                       pathname = NULL;
-       }
+       pathname = ima_d_path(&file->f_path, &pathbuf);
+       if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
+               pathname = dentry->d_name.name;
+
        if (send_tomtou)
-               ima_add_violation(inode,
-                                 !pathname ? dentry->d_name.name : pathname,
+               ima_add_violation(inode, pathname,
                                  "invalid_pcr", "ToMToU");
        if (send_writers)
-               ima_add_violation(inode,
-                                 !pathname ? dentry->d_name.name : pathname,
+               ima_add_violation(inode, pathname,
                                  "invalid_pcr", "open_writers");
        kfree(pathbuf);
 }
@@ -145,25 +139,31 @@ void ima_file_free(struct file *file)
        ima_check_last_writer(iint, inode, file);
 }
 
-static int process_measurement(struct file *file, const unsigned char *filename,
+static int process_measurement(struct file *file, const char *filename,
                               int mask, int function)
 {
        struct inode *inode = file->f_dentry->d_inode;
        struct integrity_iint_cache *iint;
-       unsigned char *pathname = NULL, *pathbuf = NULL;
-       int rc = -ENOMEM, action, must_appraise;
+       char *pathbuf = NULL;
+       const char *pathname = NULL;
+       int rc = -ENOMEM, action, must_appraise, _func;
 
        if (!ima_initialized || !S_ISREG(inode->i_mode))
                return 0;
 
-       /* Determine if in appraise/audit/measurement policy,
-        * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask.  */
+       /* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
+        * bitmask based on the appraise/audit/measurement policy.
+        * Included is the appraise submask.
+        */
        action = ima_get_action(inode, mask, function);
        if (!action)
                return 0;
 
        must_appraise = action & IMA_APPRAISE;
 
+       /*  Is the appraise rule hook specific?  */
+       _func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function;
+
        mutex_lock(&inode->i_mutex);
 
        iint = integrity_inode_get(inode);
@@ -171,44 +171,45 @@ static int process_measurement(struct file *file, const unsigned char *filename,
                goto out;
 
        /* Determine if already appraised/measured based on bitmask
-        * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
-        *  IMA_AUDIT, IMA_AUDITED) */
+        * (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
+        *  IMA_AUDIT, IMA_AUDITED)
+        */
        iint->flags |= action;
+       action &= IMA_DO_MASK;
        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;
+               if (must_appraise)
+                       rc = ima_get_cache_status(iint, _func);
+               goto out_digsig;
        }
 
        rc = ima_collect_measurement(iint, file);
        if (rc != 0)
-               goto out;
+               goto out_digsig;
+
+       if (function != BPRM_CHECK)
+               pathname = ima_d_path(&file->f_path, &pathbuf);
+
+       if (!pathname)
+               pathname = filename;
 
-       if (function != BPRM_CHECK) {
-               /* We will allow 11 spaces for ' (deleted)' to be appended */
-               pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
-               if (pathbuf) {
-                       pathname =
-                           d_path(&file->f_path, pathbuf, PATH_MAX + 11);
-                       if (IS_ERR(pathname))
-                               pathname = NULL;
-               }
-       }
        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);
+               ima_store_measurement(iint, file, pathname);
+       if (action & IMA_APPRAISE_SUBMASK)
+               rc = ima_appraise_measurement(_func, iint, file, pathname);
        if (action & IMA_AUDIT)
-               ima_audit_measurement(iint, !pathname ? filename : pathname);
+               ima_audit_measurement(iint, pathname);
        kfree(pathbuf);
+out_digsig:
+       if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
+               rc = -EACCES;
 out:
        mutex_unlock(&inode->i_mutex);
-       return (rc && must_appraise) ? -EACCES : 0;
+       if ((rc && must_appraise) && (ima_appraise & IMA_APPRAISE_ENFORCE))
+               return -EACCES;
+       return 0;
 }
 
 /**
@@ -219,19 +220,15 @@ out:
  * Measure files being mmapped executable based on the ima_must_measure()
  * policy decision.
  *
- * Return 0 on success, an error code on failure.
- * (Based on the results of appraise_measurement().)
+ * On success return 0.  On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
  */
 int ima_file_mmap(struct file *file, unsigned long prot)
 {
-       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 (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
+       if (file && (prot & PROT_EXEC))
+               return process_measurement(file, file->f_dentry->d_name.name,
+                                          MAY_EXEC, MMAP_CHECK);
+       return 0;
 }
 
 /**
@@ -244,18 +241,15 @@ int ima_file_mmap(struct file *file, unsigned long prot)
  * So we can be certain that what we verify and measure here is actually
  * what is being executed.
  *
- * Return 0 on success, an error code on failure.
- * (Based on the results of appraise_measurement().)
+ * On success return 0.  On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
  */
 int ima_bprm_check(struct linux_binprm *bprm)
 {
-       int rc;
-
-       rc = process_measurement(bprm->file,
+       return process_measurement(bprm->file,
                                 (strcmp(bprm->filename, bprm->interp) == 0) ?
                                 bprm->filename : bprm->interp,
                                 MAY_EXEC, BPRM_CHECK);
-       return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
 }
 
 /**
@@ -265,18 +259,15 @@ int ima_bprm_check(struct linux_binprm *bprm)
  *
  * Measure files based on the ima_must_measure() policy decision.
  *
- * Always return 0 and audit dentry_open failures.
- * (Return code will be based upon measurement appraisal.)
+ * On success return 0.  On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
  */
 int ima_file_check(struct file *file, int mask)
 {
-       int rc;
-
        ima_rdwr_violation_check(file);
-       rc = process_measurement(file, file->f_dentry->d_name.name,
+       return process_measurement(file, file->f_dentry->d_name.name,
                                 mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
                                 FILE_CHECK);
-       return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
 }
 EXPORT_SYMBOL_GPL(ima_file_check);
 
@@ -286,23 +277,20 @@ EXPORT_SYMBOL_GPL(ima_file_check);
  *
  * Measure/appraise kernel modules based on policy.
  *
- * Always return 0 and audit dentry_open failures.
- * Return code is based upon measurement appraisal.
+ * On success return 0.  On integrity appraisal error, assuming the file
+ * is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
  */
 int ima_module_check(struct file *file)
 {
-       int rc = 0;
-
        if (!file) {
-               if (ima_appraise & IMA_APPRAISE_MODULES) {
 #ifndef CONFIG_MODULE_SIG_FORCE
-                       rc = -EACCES;   /* INTEGRITY_UNKNOWN */
+               if (ima_appraise & IMA_APPRAISE_MODULES)
+                       return -EACCES; /* INTEGRITY_UNKNOWN */
 #endif
-               }
-       } else
-               rc = process_measurement(file, file->f_dentry->d_name.name,
-                                        MAY_EXEC, MODULE_CHECK);
-       return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
+               return 0;       /* We rely on module signature checking */
+       }
+       return process_measurement(file, file->f_dentry->d_name.name,
+                                  MAY_EXEC, MODULE_CHECK);
 }
 
 static int __init init_ima(void)
index 479fca940bb5552df6591723f09408c3325054f0..b27535a13a791f281d8686bec90fa043481d55d3 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/magic.h>
 #include <linux/parser.h>
 #include <linux/slab.h>
+#include <linux/genhd.h>
 
 #include "ima.h"
 
@@ -25,6 +26,7 @@
 #define IMA_FSMAGIC    0x0004
 #define IMA_UID                0x0008
 #define IMA_FOWNER     0x0010
+#define IMA_FSUUID     0x0020
 
 #define UNKNOWN                0
 #define MEASURE                0x0001  /* same as IMA_MEASURE */
@@ -45,10 +47,12 @@ struct ima_rule_entry {
        enum ima_hooks func;
        int mask;
        unsigned long fsmagic;
+       u8 fsuuid[16];
        kuid_t uid;
        kuid_t fowner;
        struct {
                void *rule;     /* LSM file metadata specific */
+               void *args_p;   /* audit value */
                int type;       /* audit type */
        } lsm[MAX_LSM_RULES];
 };
@@ -74,7 +78,7 @@ static struct ima_rule_entry default_rules[] = {
        {.action = DONT_MEASURE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
        {.action = DONT_MEASURE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
-       {.action = MEASURE,.func = FILE_MMAP,.mask = MAY_EXEC,
+       {.action = MEASURE,.func = MMAP_CHECK,.mask = MAY_EXEC,
         .flags = IMA_FUNC | IMA_MASK},
        {.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
         .flags = IMA_FUNC | IMA_MASK},
@@ -119,6 +123,35 @@ static int __init default_appraise_policy_setup(char *str)
 }
 __setup("ima_appraise_tcb", default_appraise_policy_setup);
 
+/* 
+ * Although the IMA policy does not change, the LSM policy can be
+ * reloaded, leaving the IMA LSM based rules referring to the old,
+ * stale LSM policy.
+ *
+ * Update the IMA LSM based rules to reflect the reloaded LSM policy. 
+ * We assume the rules still exist; and BUG_ON() if they don't.
+ */
+static void ima_lsm_update_rules(void)
+{
+       struct ima_rule_entry *entry, *tmp;
+       int result;
+       int i;
+
+       mutex_lock(&ima_rules_mutex);
+       list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+               for (i = 0; i < MAX_LSM_RULES; i++) {
+                       if (!entry->lsm[i].rule)
+                               continue;
+                       result = security_filter_rule_init(entry->lsm[i].type,
+                                                          Audit_equal,
+                                                          entry->lsm[i].args_p,
+                                                          &entry->lsm[i].rule);
+                       BUG_ON(!entry->lsm[i].rule);
+               }
+       }
+       mutex_unlock(&ima_rules_mutex);
+}
+
 /**
  * ima_match_rules - determine whether an inode matches the measure rule.
  * @rule: a pointer to a rule
@@ -142,6 +175,9 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
        if ((rule->flags & IMA_FSMAGIC)
            && rule->fsmagic != inode->i_sb->s_magic)
                return false;
+       if ((rule->flags & IMA_FSUUID) &&
+               memcmp(rule->fsuuid, inode->i_sb->s_uuid, sizeof(rule->fsuuid)))
+               return false;
        if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
                return false;
        if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
@@ -149,10 +185,11 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
        for (i = 0; i < MAX_LSM_RULES; i++) {
                int rc = 0;
                u32 osid, sid;
+               int retried = 0;
 
                if (!rule->lsm[i].rule)
                        continue;
-
+retry:
                switch (i) {
                case LSM_OBJ_USER:
                case LSM_OBJ_ROLE:
@@ -176,12 +213,39 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
                default:
                        break;
                }
+               if ((rc < 0) && (!retried)) {
+                       retried = 1;
+                       ima_lsm_update_rules();
+                       goto retry;
+               } 
                if (!rc)
                        return false;
        }
        return true;
 }
 
+/*
+ * In addition to knowing that we need to appraise the file in general,
+ * we need to differentiate between calling hooks, for hook specific rules.
+ */
+static int get_subaction(struct ima_rule_entry *rule, int func)
+{
+       if (!(rule->flags & IMA_FUNC))
+               return IMA_FILE_APPRAISE;
+
+       switch(func) {
+       case MMAP_CHECK:
+               return IMA_MMAP_APPRAISE;
+       case BPRM_CHECK:
+               return IMA_BPRM_APPRAISE;
+       case MODULE_CHECK:
+               return IMA_MODULE_APPRAISE;
+       case FILE_CHECK:
+       default:
+               return IMA_FILE_APPRAISE;
+       }
+}
+
 /**
  * ima_match_policy - decision based on LSM and other conditions
  * @inode: pointer to an inode for which the policy decision is being made
@@ -209,7 +273,12 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
                if (!ima_match_rules(entry, inode, func, mask))
                        continue;
 
+               action |= entry->flags & IMA_ACTION_FLAGS;
+
                action |= entry->action & IMA_DO_MASK;
+               if (entry->action & IMA_APPRAISE)
+                       action |= get_subaction(entry, func);
+
                if (entry->action & IMA_DO_MASK)
                        actmask &= ~(entry->action | entry->action << 1);
                else
@@ -282,7 +351,8 @@ enum {
        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_fowner
+       Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner,
+       Opt_appraise_type, Opt_fsuuid
 };
 
 static match_table_t policy_tokens = {
@@ -300,25 +370,35 @@ static match_table_t policy_tokens = {
        {Opt_func, "func=%s"},
        {Opt_mask, "mask=%s"},
        {Opt_fsmagic, "fsmagic=%s"},
+       {Opt_fsuuid, "fsuuid=%s"},
        {Opt_uid, "uid=%s"},
        {Opt_fowner, "fowner=%s"},
+       {Opt_appraise_type, "appraise_type=%s"},
        {Opt_err, NULL}
 };
 
 static int ima_lsm_rule_init(struct ima_rule_entry *entry,
-                            char *args, int lsm_rule, int audit_type)
+                            substring_t *args, int lsm_rule, int audit_type)
 {
        int result;
 
        if (entry->lsm[lsm_rule].rule)
                return -EINVAL;
 
+       entry->lsm[lsm_rule].args_p = match_strdup(args);
+       if (!entry->lsm[lsm_rule].args_p)
+               return -ENOMEM;
+
        entry->lsm[lsm_rule].type = audit_type;
        result = security_filter_rule_init(entry->lsm[lsm_rule].type,
-                                          Audit_equal, args,
+                                          Audit_equal,
+                                          entry->lsm[lsm_rule].args_p,
                                           &entry->lsm[lsm_rule].rule);
-       if (!entry->lsm[lsm_rule].rule)
+       if (!entry->lsm[lsm_rule].rule) {
+               kfree(entry->lsm[lsm_rule].args_p);
                return -EINVAL;
+       }
+
        return result;
 }
 
@@ -404,8 +484,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                                entry->func = FILE_CHECK;
                        else if (strcmp(args[0].from, "MODULE_CHECK") == 0)
                                entry->func = MODULE_CHECK;
-                       else if (strcmp(args[0].from, "FILE_MMAP") == 0)
-                               entry->func = FILE_MMAP;
+                       else if ((strcmp(args[0].from, "FILE_MMAP") == 0)
+                               || (strcmp(args[0].from, "MMAP_CHECK") == 0))
+                               entry->func = MMAP_CHECK;
                        else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
                                entry->func = BPRM_CHECK;
                        else
@@ -445,6 +526,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                        if (!result)
                                entry->flags |= IMA_FSMAGIC;
                        break;
+               case Opt_fsuuid:
+                       ima_log_string(ab, "fsuuid", args[0].from);
+
+                       if (memchr_inv(entry->fsuuid, 0x00,
+                           sizeof(entry->fsuuid))) {
+                               result = -EINVAL;
+                               break;
+                       }
+
+                       part_pack_uuid(args[0].from, entry->fsuuid);
+                       entry->flags |= IMA_FSUUID;
+                       result = 0;
+                       break;
                case Opt_uid:
                        ima_log_string(ab, "uid", args[0].from);
 
@@ -481,40 +575,52 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
                        break;
                case Opt_obj_user:
                        ima_log_string(ab, "obj_user", args[0].from);
-                       result = ima_lsm_rule_init(entry, args[0].from,
+                       result = ima_lsm_rule_init(entry, args,
                                                   LSM_OBJ_USER,
                                                   AUDIT_OBJ_USER);
                        break;
                case Opt_obj_role:
                        ima_log_string(ab, "obj_role", args[0].from);
-                       result = ima_lsm_rule_init(entry, args[0].from,
+                       result = ima_lsm_rule_init(entry, args,
                                                   LSM_OBJ_ROLE,
                                                   AUDIT_OBJ_ROLE);
                        break;
                case Opt_obj_type:
                        ima_log_string(ab, "obj_type", args[0].from);
-                       result = ima_lsm_rule_init(entry, args[0].from,
+                       result = ima_lsm_rule_init(entry, args,
                                                   LSM_OBJ_TYPE,
                                                   AUDIT_OBJ_TYPE);
                        break;
                case Opt_subj_user:
                        ima_log_string(ab, "subj_user", args[0].from);
-                       result = ima_lsm_rule_init(entry, args[0].from,
+                       result = ima_lsm_rule_init(entry, args,
                                                   LSM_SUBJ_USER,
                                                   AUDIT_SUBJ_USER);
                        break;
                case Opt_subj_role:
                        ima_log_string(ab, "subj_role", args[0].from);
-                       result = ima_lsm_rule_init(entry, args[0].from,
+                       result = ima_lsm_rule_init(entry, args,
                                                   LSM_SUBJ_ROLE,
                                                   AUDIT_SUBJ_ROLE);
                        break;
                case Opt_subj_type:
                        ima_log_string(ab, "subj_type", args[0].from);
-                       result = ima_lsm_rule_init(entry, args[0].from,
+                       result = ima_lsm_rule_init(entry, args,
                                                   LSM_SUBJ_TYPE,
                                                   AUDIT_SUBJ_TYPE);
                        break;
+               case Opt_appraise_type:
+                       if (entry->action != APPRAISE) {
+                               result = -EINVAL;
+                               break;
+                       }
+
+                       ima_log_string(ab, "appraise_type", args[0].from);
+                       if ((strcmp(args[0].from, "imasig")) == 0)
+                               entry->flags |= IMA_DIGSIG_REQUIRED;
+                       else
+                               result = -EINVAL;
+                       break;
                case Opt_err:
                        ima_log_string(ab, "UNKNOWN", p);
                        result = -EINVAL;
@@ -590,9 +696,13 @@ ssize_t ima_parse_add_rule(char *rule)
 void ima_delete_rules(void)
 {
        struct ima_rule_entry *entry, *tmp;
+       int i;
 
        mutex_lock(&ima_rules_mutex);
        list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
+               for (i = 0; i < MAX_LSM_RULES; i++)
+                       kfree(entry->lsm[i].args_p);
+
                list_del(&entry->list);
                kfree(entry);
        }
index e9db763a875e80f8fc4bd528974922d7e98ae97e..84c37c4db914ac46cd07adae4ee4040fe9446390 100644 (file)
 #include <linux/types.h>
 #include <linux/integrity.h>
 #include <crypto/sha.h>
+#include <linux/key.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
+#define IMA_MEASURE            0x00000001
+#define IMA_MEASURED           0x00000002
+#define IMA_APPRAISE           0x00000004
+#define IMA_APPRAISED          0x00000008
+/*#define IMA_COLLECT          0x00000010  do not use this flag */
+#define IMA_COLLECTED          0x00000020
+#define IMA_AUDIT              0x00000040
+#define IMA_AUDITED            0x00000080
 
 /* iint cache flags */
-#define IMA_DIGSIG             0x0100
+#define IMA_ACTION_FLAGS       0xff000000
+#define IMA_DIGSIG             0x01000000
+#define IMA_DIGSIG_REQUIRED    0x02000000
 
-#define IMA_DO_MASK            (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT)
-#define IMA_DONE_MASK          (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \
-                                | IMA_COLLECTED)
+#define IMA_DO_MASK            (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
+                                IMA_APPRAISE_SUBMASK)
+#define IMA_DONE_MASK          (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED | \
+                                IMA_COLLECTED | IMA_APPRAISED_SUBMASK)
+
+/* iint subaction appraise cache flags */
+#define IMA_FILE_APPRAISE      0x00000100
+#define IMA_FILE_APPRAISED     0x00000200
+#define IMA_MMAP_APPRAISE      0x00000400
+#define IMA_MMAP_APPRAISED     0x00000800
+#define IMA_BPRM_APPRAISE      0x00001000
+#define IMA_BPRM_APPRAISED     0x00002000
+#define IMA_MODULE_APPRAISE    0x00004000
+#define IMA_MODULE_APPRAISED   0x00008000
+#define IMA_APPRAISE_SUBMASK   (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \
+                                IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE)
+#define IMA_APPRAISED_SUBMASK  (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \
+                                IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED)
 
 enum evm_ima_xattr_type {
        IMA_XATTR_DIGEST = 0x01,
@@ -48,10 +66,13 @@ 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 short flags;
+       unsigned long flags;
        struct evm_ima_xattr_data ima_xattr;
-       enum integrity_status ima_status;
-       enum integrity_status evm_status;
+       enum integrity_status ima_file_status:4;
+       enum integrity_status ima_mmap_status:4;
+       enum integrity_status ima_bprm_status:4;
+       enum integrity_status ima_module_status:4;
+       enum integrity_status evm_status:4;
 };
 
 /* rbtree tree calls to lookup, insert, delete
@@ -81,5 +102,16 @@ static inline int integrity_digsig_verify(const unsigned int id,
 
 #endif /* CONFIG_INTEGRITY_SIGNATURE */
 
+#ifdef CONFIG_INTEGRITY_ASYMMETRIC_KEYS
+int asymmetric_verify(struct key *keyring, const char *sig,
+                     int siglen, const char *data, int datalen);
+#else
+static inline int asymmetric_verify(struct key *keyring, const char *sig,
+                                   int siglen, const char *data, int datalen)
+{
+       return -EOPNOTSUPP;
+}
+#endif
+
 /* set during initialization */
 extern int iint_initialized;