Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
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 ...hifive-unleashed-5.1
commit
33673dcb37
|
@ -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.
|
|
@ -18,17 +18,21 @@ Description:
|
||||||
rule format: action [condition ...]
|
rule format: action [condition ...]
|
||||||
|
|
||||||
action: measure | dont_measure | appraise | dont_appraise | audit
|
action: measure | dont_measure | appraise | dont_appraise | audit
|
||||||
condition:= base | lsm
|
condition:= base | lsm [option]
|
||||||
base: [[func=] [mask=] [fsmagic=] [uid=] [fowner]]
|
base: [[func=] [mask=] [fsmagic=] [fsuuid=] [uid=]
|
||||||
|
[fowner]]
|
||||||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||||
[obj_user=] [obj_role=] [obj_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]
|
mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
|
||||||
fsmagic:= hex value
|
fsmagic:= hex value
|
||||||
|
fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6)
|
||||||
uid:= decimal value
|
uid:= decimal value
|
||||||
fowner:=decimal value
|
fowner:=decimal value
|
||||||
lsm: are LSM specific
|
lsm: are LSM specific
|
||||||
|
option: appraise_type:= [imasig]
|
||||||
|
|
||||||
default policy:
|
default policy:
|
||||||
# PROC_SUPER_MAGIC
|
# PROC_SUPER_MAGIC
|
||||||
|
|
|
@ -75,10 +75,20 @@ config TCG_INFINEON
|
||||||
|
|
||||||
config TCG_IBMVTPM
|
config TCG_IBMVTPM
|
||||||
tristate "IBM VTPM Interface"
|
tristate "IBM VTPM Interface"
|
||||||
depends on PPC64
|
depends on PPC_PSERIES
|
||||||
---help---
|
---help---
|
||||||
If you have IBM virtual TPM (VTPM) support say Yes and it
|
If you have IBM virtual TPM (VTPM) support say Yes and it
|
||||||
will be accessible from within Linux. To compile this driver
|
will be accessible from within Linux. To compile this driver
|
||||||
as a module, choose M here; the module will be called tpm_ibmvtpm.
|
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
|
endif # TCG_TPM
|
||||||
|
|
|
@ -17,3 +17,4 @@ obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
|
||||||
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
|
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
|
||||||
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
|
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
|
||||||
obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
|
obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
|
||||||
|
obj-$(CONFIG_TCG_ST33_I2C) += tpm_i2c_stm_st33.o
|
||||||
|
|
|
@ -40,8 +40,9 @@ enum tpm_duration {
|
||||||
};
|
};
|
||||||
|
|
||||||
#define TPM_MAX_ORDINAL 243
|
#define TPM_MAX_ORDINAL 243
|
||||||
#define TPM_MAX_PROTECTED_ORDINAL 12
|
#define TSC_MAX_ORDINAL 12
|
||||||
#define TPM_PROTECTED_ORDINAL_MASK 0xFF
|
#define TPM_PROTECTED_COMMAND 0x00
|
||||||
|
#define TPM_CONNECTION_COMMAND 0x40
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Bug workaround - some TPM's don't flush the most
|
* 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
|
* values of the SHORT, MEDIUM, and LONG durations are retrieved
|
||||||
* from the chip during initialization with a call to tpm_get_timeouts.
|
* 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] = {
|
static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = {
|
||||||
TPM_UNDEFINED, /* 0 */
|
TPM_UNDEFINED, /* 0 */
|
||||||
TPM_UNDEFINED,
|
TPM_UNDEFINED,
|
||||||
|
@ -351,14 +337,11 @@ unsigned long tpm_calc_ordinal_duration(struct tpm_chip *chip,
|
||||||
{
|
{
|
||||||
int duration_idx = TPM_UNDEFINED;
|
int duration_idx = TPM_UNDEFINED;
|
||||||
int duration = 0;
|
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];
|
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)
|
if (duration_idx != TPM_UNDEFINED)
|
||||||
duration = chip->vendor.duration[duration_idx];
|
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)
|
chip->vendor.req_complete_val)
|
||||||
goto out_recv;
|
goto out_recv;
|
||||||
|
|
||||||
if ((status == chip->vendor.req_canceled)) {
|
if (chip->vendor.req_canceled(chip, status)) {
|
||||||
dev_err(chip->dev, "Operation Canceled\n");
|
dev_err(chip->dev, "Operation Canceled\n");
|
||||||
rc = -ECANCELED;
|
rc = -ECANCELED;
|
||||||
goto out;
|
goto out;
|
||||||
|
@ -468,7 +451,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
|
||||||
err = be32_to_cpu(cmd->header.out.return_code);
|
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);
|
dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc);
|
||||||
|
|
||||||
return err;
|
return err;
|
||||||
|
@ -528,6 +511,25 @@ void tpm_gen_interrupt(struct tpm_chip *chip)
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tpm_gen_interrupt);
|
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)
|
int tpm_get_timeouts(struct tpm_chip *chip)
|
||||||
{
|
{
|
||||||
struct tpm_cmd_t tpm_cmd;
|
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.cap = TPM_CAP_PROP;
|
||||||
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
|
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
|
||||||
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
|
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,
|
if (rc == TPM_ERR_INVALID_POSTINIT) {
|
||||||
"attempting to determine the timeouts");
|
/* The TPM is not started, we are the first to talk to it.
|
||||||
if (rc)
|
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;
|
goto duration;
|
||||||
|
}
|
||||||
|
|
||||||
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
|
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
|
||||||
be32_to_cpu(tpm_cmd.header.out.length)
|
be32_to_cpu(tpm_cmd.header.out.length)
|
||||||
|
@ -824,7 +843,7 @@ int tpm_do_selftest(struct tpm_chip *chip)
|
||||||
{
|
{
|
||||||
int rc;
|
int rc;
|
||||||
unsigned int loops;
|
unsigned int loops;
|
||||||
unsigned int delay_msec = 1000;
|
unsigned int delay_msec = 100;
|
||||||
unsigned long duration;
|
unsigned long duration;
|
||||||
struct tpm_cmd_t cmd;
|
struct tpm_cmd_t cmd;
|
||||||
|
|
||||||
|
@ -845,6 +864,14 @@ int tpm_do_selftest(struct tpm_chip *chip)
|
||||||
cmd.header.in = pcrread_header;
|
cmd.header.in = pcrread_header;
|
||||||
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0);
|
cmd.params.pcrread_in.pcr_idx = cpu_to_be32(0);
|
||||||
rc = tpm_transmit(chip, (u8 *) &cmd, READ_PCR_RESULT_SIZE);
|
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)
|
if (rc < TPM_HEADER_SIZE)
|
||||||
return -EFAULT;
|
return -EFAULT;
|
||||||
|
@ -1075,12 +1102,28 @@ ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr,
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(tpm_store_cancel);
|
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,
|
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;
|
unsigned long stop;
|
||||||
long rc;
|
long rc;
|
||||||
u8 status;
|
u8 status;
|
||||||
|
bool canceled = false;
|
||||||
|
|
||||||
/* check current status */
|
/* check current status */
|
||||||
status = chip->vendor.status(chip);
|
status = chip->vendor.status(chip);
|
||||||
|
@ -1095,11 +1138,14 @@ again:
|
||||||
if ((long)timeout <= 0)
|
if ((long)timeout <= 0)
|
||||||
return -ETIME;
|
return -ETIME;
|
||||||
rc = wait_event_interruptible_timeout(*queue,
|
rc = wait_event_interruptible_timeout(*queue,
|
||||||
((chip->vendor.status(chip)
|
wait_for_tpm_stat_cond(chip, mask, check_cancel,
|
||||||
& mask) == mask),
|
&canceled),
|
||||||
timeout);
|
timeout);
|
||||||
if (rc > 0)
|
if (rc > 0) {
|
||||||
|
if (canceled)
|
||||||
|
return -ECANCELED;
|
||||||
return 0;
|
return 0;
|
||||||
|
}
|
||||||
if (rc == -ERESTARTSYS && freezing(current)) {
|
if (rc == -ERESTARTSYS && freezing(current)) {
|
||||||
clear_thread_flag(TIF_SIGPENDING);
|
clear_thread_flag(TIF_SIGPENDING);
|
||||||
goto again;
|
goto again;
|
||||||
|
|
|
@ -47,6 +47,7 @@ enum tpm_addr {
|
||||||
#define TPM_WARN_DOING_SELFTEST 0x802
|
#define TPM_WARN_DOING_SELFTEST 0x802
|
||||||
#define TPM_ERR_DEACTIVATED 0x6
|
#define TPM_ERR_DEACTIVATED 0x6
|
||||||
#define TPM_ERR_DISABLED 0x7
|
#define TPM_ERR_DISABLED 0x7
|
||||||
|
#define TPM_ERR_INVALID_POSTINIT 38
|
||||||
|
|
||||||
#define TPM_HEADER_SIZE 10
|
#define TPM_HEADER_SIZE 10
|
||||||
extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr,
|
extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr,
|
||||||
|
@ -77,7 +78,7 @@ struct tpm_chip;
|
||||||
struct tpm_vendor_specific {
|
struct tpm_vendor_specific {
|
||||||
const u8 req_complete_mask;
|
const u8 req_complete_mask;
|
||||||
const u8 req_complete_val;
|
const u8 req_complete_val;
|
||||||
const u8 req_canceled;
|
bool (*req_canceled)(struct tpm_chip *chip, u8 status);
|
||||||
void __iomem *iobase; /* ioremapped address */
|
void __iomem *iobase; /* ioremapped address */
|
||||||
unsigned long base; /* TPM base address */
|
unsigned long base; /* TPM base address */
|
||||||
|
|
||||||
|
@ -100,13 +101,19 @@ struct tpm_vendor_specific {
|
||||||
bool timeout_adjusted;
|
bool timeout_adjusted;
|
||||||
unsigned long duration[3]; /* jiffies */
|
unsigned long duration[3]; /* jiffies */
|
||||||
bool duration_adjusted;
|
bool duration_adjusted;
|
||||||
void *data;
|
void *priv;
|
||||||
|
|
||||||
wait_queue_head_t read_queue;
|
wait_queue_head_t read_queue;
|
||||||
wait_queue_head_t int_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_INTEL 0x8086
|
||||||
|
#define TPM_VID_WINBOND 0x1050
|
||||||
|
#define TPM_VID_STM 0x104A
|
||||||
|
|
||||||
struct tpm_chip {
|
struct tpm_chip {
|
||||||
struct device *dev; /* Device stuff */
|
struct device *dev; /* Device stuff */
|
||||||
|
@ -154,13 +161,13 @@ struct tpm_input_header {
|
||||||
__be16 tag;
|
__be16 tag;
|
||||||
__be32 length;
|
__be32 length;
|
||||||
__be32 ordinal;
|
__be32 ordinal;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_output_header {
|
struct tpm_output_header {
|
||||||
__be16 tag;
|
__be16 tag;
|
||||||
__be32 length;
|
__be32 length;
|
||||||
__be32 return_code;
|
__be32 return_code;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct stclear_flags_t {
|
struct stclear_flags_t {
|
||||||
__be16 tag;
|
__be16 tag;
|
||||||
|
@ -169,14 +176,14 @@ struct stclear_flags_t {
|
||||||
u8 physicalPresence;
|
u8 physicalPresence;
|
||||||
u8 physicalPresenceLock;
|
u8 physicalPresenceLock;
|
||||||
u8 bGlobalLock;
|
u8 bGlobalLock;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_version_t {
|
struct tpm_version_t {
|
||||||
u8 Major;
|
u8 Major;
|
||||||
u8 Minor;
|
u8 Minor;
|
||||||
u8 revMajor;
|
u8 revMajor;
|
||||||
u8 revMinor;
|
u8 revMinor;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_version_1_2_t {
|
struct tpm_version_1_2_t {
|
||||||
__be16 tag;
|
__be16 tag;
|
||||||
|
@ -184,20 +191,20 @@ struct tpm_version_1_2_t {
|
||||||
u8 Minor;
|
u8 Minor;
|
||||||
u8 revMajor;
|
u8 revMajor;
|
||||||
u8 revMinor;
|
u8 revMinor;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct timeout_t {
|
struct timeout_t {
|
||||||
__be32 a;
|
__be32 a;
|
||||||
__be32 b;
|
__be32 b;
|
||||||
__be32 c;
|
__be32 c;
|
||||||
__be32 d;
|
__be32 d;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct duration_t {
|
struct duration_t {
|
||||||
__be32 tpm_short;
|
__be32 tpm_short;
|
||||||
__be32 tpm_medium;
|
__be32 tpm_medium;
|
||||||
__be32 tpm_long;
|
__be32 tpm_long;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct permanent_flags_t {
|
struct permanent_flags_t {
|
||||||
__be16 tag;
|
__be16 tag;
|
||||||
|
@ -221,7 +228,7 @@ struct permanent_flags_t {
|
||||||
u8 tpmEstablished;
|
u8 tpmEstablished;
|
||||||
u8 maintenanceDone;
|
u8 maintenanceDone;
|
||||||
u8 disableFullDALogicInfo;
|
u8 disableFullDALogicInfo;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct permanent_flags_t perm_flags;
|
struct permanent_flags_t perm_flags;
|
||||||
|
@ -239,12 +246,12 @@ struct tpm_getcap_params_in {
|
||||||
__be32 cap;
|
__be32 cap;
|
||||||
__be32 subcap_size;
|
__be32 subcap_size;
|
||||||
__be32 subcap;
|
__be32 subcap;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_getcap_params_out {
|
struct tpm_getcap_params_out {
|
||||||
__be32 cap_size;
|
__be32 cap_size;
|
||||||
cap_t cap;
|
cap_t cap;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_readpubek_params_out {
|
struct tpm_readpubek_params_out {
|
||||||
u8 algorithm[4];
|
u8 algorithm[4];
|
||||||
|
@ -255,7 +262,7 @@ struct tpm_readpubek_params_out {
|
||||||
__be32 keysize;
|
__be32 keysize;
|
||||||
u8 modulus[256];
|
u8 modulus[256];
|
||||||
u8 checksum[20];
|
u8 checksum[20];
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct tpm_input_header in;
|
struct tpm_input_header in;
|
||||||
|
@ -265,16 +272,16 @@ typedef union {
|
||||||
#define TPM_DIGEST_SIZE 20
|
#define TPM_DIGEST_SIZE 20
|
||||||
struct tpm_pcrread_out {
|
struct tpm_pcrread_out {
|
||||||
u8 pcr_result[TPM_DIGEST_SIZE];
|
u8 pcr_result[TPM_DIGEST_SIZE];
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_pcrread_in {
|
struct tpm_pcrread_in {
|
||||||
__be32 pcr_idx;
|
__be32 pcr_idx;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_pcrextend_in {
|
struct tpm_pcrextend_in {
|
||||||
__be32 pcr_idx;
|
__be32 pcr_idx;
|
||||||
u8 hash[TPM_DIGEST_SIZE];
|
u8 hash[TPM_DIGEST_SIZE];
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
|
/* 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
|
* 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 {
|
struct tpm_getrandom_out {
|
||||||
__be32 rng_data_len;
|
__be32 rng_data_len;
|
||||||
u8 rng_data[TPM_MAX_RNG_DATA];
|
u8 rng_data[TPM_MAX_RNG_DATA];
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
struct tpm_getrandom_in {
|
struct tpm_getrandom_in {
|
||||||
__be32 num_bytes;
|
__be32 num_bytes;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
|
struct tpm_startup_in {
|
||||||
|
__be16 startup_type;
|
||||||
|
} __packed;
|
||||||
|
|
||||||
typedef union {
|
typedef union {
|
||||||
struct tpm_getcap_params_out getcap_out;
|
struct tpm_getcap_params_out getcap_out;
|
||||||
|
@ -301,12 +312,13 @@ typedef union {
|
||||||
struct tpm_pcrextend_in pcrextend_in;
|
struct tpm_pcrextend_in pcrextend_in;
|
||||||
struct tpm_getrandom_in getrandom_in;
|
struct tpm_getrandom_in getrandom_in;
|
||||||
struct tpm_getrandom_out getrandom_out;
|
struct tpm_getrandom_out getrandom_out;
|
||||||
|
struct tpm_startup_in startup_in;
|
||||||
} tpm_cmd_params;
|
} tpm_cmd_params;
|
||||||
|
|
||||||
struct tpm_cmd_t {
|
struct tpm_cmd_t {
|
||||||
tpm_cmd_header header;
|
tpm_cmd_header header;
|
||||||
tpm_cmd_params params;
|
tpm_cmd_params params;
|
||||||
}__attribute__((packed));
|
} __packed;
|
||||||
|
|
||||||
ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *);
|
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_suspend(struct device *);
|
||||||
extern int tpm_pm_resume(struct device *);
|
extern int tpm_pm_resume(struct device *);
|
||||||
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
|
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
|
||||||
wait_queue_head_t *);
|
wait_queue_head_t *, bool);
|
||||||
|
|
||||||
#ifdef CONFIG_ACPI
|
#ifdef CONFIG_ACPI
|
||||||
extern int tpm_add_ppi(struct kobject *);
|
extern int tpm_add_ppi(struct kobject *);
|
||||||
|
|
|
@ -33,13 +33,13 @@ struct acpi_tcpa {
|
||||||
u16 platform_class;
|
u16 platform_class;
|
||||||
union {
|
union {
|
||||||
struct client_hdr {
|
struct client_hdr {
|
||||||
u32 log_max_len __attribute__ ((packed));
|
u32 log_max_len __packed;
|
||||||
u64 log_start_addr __attribute__ ((packed));
|
u64 log_start_addr __packed;
|
||||||
} client;
|
} client;
|
||||||
struct server_hdr {
|
struct server_hdr {
|
||||||
u16 reserved;
|
u16 reserved;
|
||||||
u64 log_max_len __attribute__ ((packed));
|
u64 log_max_len __packed;
|
||||||
u64 log_start_addr __attribute__ ((packed));
|
u64 log_start_addr __packed;
|
||||||
} server;
|
} server;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -116,6 +116,11 @@ static u8 tpm_atml_status(struct tpm_chip *chip)
|
||||||
return ioread8(chip->vendor.iobase + 1);
|
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 = {
|
static const struct file_operations atmel_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = no_llseek,
|
.llseek = no_llseek,
|
||||||
|
@ -147,7 +152,7 @@ static const struct tpm_vendor_specific tpm_atmel = {
|
||||||
.status = tpm_atml_status,
|
.status = tpm_atml_status,
|
||||||
.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
|
.req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL,
|
||||||
.req_complete_val = 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,
|
.attr_group = &atmel_attr_grp,
|
||||||
.miscdev = { .fops = &atmel_ops, },
|
.miscdev = { .fops = &atmel_ops, },
|
||||||
};
|
};
|
||||||
|
|
|
@ -505,6 +505,11 @@ out_err:
|
||||||
return rc;
|
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 = {
|
static const struct file_operations tis_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = no_llseek,
|
.llseek = no_llseek,
|
||||||
|
@ -550,7 +555,7 @@ static struct tpm_vendor_specific tpm_tis_i2c = {
|
||||||
.cancel = tpm_tis_i2c_ready,
|
.cancel = tpm_tis_i2c_ready,
|
||||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||||
.req_complete_val = 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,
|
.attr_group = &tis_attr_grp,
|
||||||
.miscdev.fops = &tis_ops,
|
.miscdev.fops = &tis_ops,
|
||||||
};
|
};
|
||||||
|
|
|
@ -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");
|
|
@ -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__ */
|
|
@ -64,7 +64,7 @@ static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
|
||||||
{
|
{
|
||||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||||
if (chip)
|
if (chip)
|
||||||
return (struct ibmvtpm_dev *)chip->vendor.data;
|
return (struct ibmvtpm_dev *)TPM_VPRIV(chip);
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -83,7 +83,7 @@ static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||||
u16 len;
|
u16 len;
|
||||||
int sig;
|
int sig;
|
||||||
|
|
||||||
ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
|
ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
|
||||||
|
|
||||||
if (!ibmvtpm->rtce_buf) {
|
if (!ibmvtpm->rtce_buf) {
|
||||||
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
|
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;
|
u64 *word = (u64 *) &crq;
|
||||||
int rc;
|
int rc;
|
||||||
|
|
||||||
ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
|
ibmvtpm = (struct ibmvtpm_dev *)TPM_VPRIV(chip);
|
||||||
|
|
||||||
if (!ibmvtpm->rtce_buf) {
|
if (!ibmvtpm->rtce_buf) {
|
||||||
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
|
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
|
||||||
|
@ -398,6 +398,11 @@ static int tpm_ibmvtpm_resume(struct device *dev)
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status)
|
||||||
|
{
|
||||||
|
return (status == 0);
|
||||||
|
}
|
||||||
|
|
||||||
static const struct file_operations ibmvtpm_ops = {
|
static const struct file_operations ibmvtpm_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = no_llseek,
|
.llseek = no_llseek,
|
||||||
|
@ -441,7 +446,7 @@ static const struct tpm_vendor_specific tpm_ibmvtpm = {
|
||||||
.status = tpm_ibmvtpm_status,
|
.status = tpm_ibmvtpm_status,
|
||||||
.req_complete_mask = 0,
|
.req_complete_mask = 0,
|
||||||
.req_complete_val = 0,
|
.req_complete_val = 0,
|
||||||
.req_canceled = 0,
|
.req_canceled = tpm_ibmvtpm_req_canceled,
|
||||||
.attr_group = &ibmvtpm_attr_grp,
|
.attr_group = &ibmvtpm_attr_grp,
|
||||||
.miscdev = { .fops = &ibmvtpm_ops, },
|
.miscdev = { .fops = &ibmvtpm_ops, },
|
||||||
};
|
};
|
||||||
|
@ -647,7 +652,7 @@ static int tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
|
||||||
|
|
||||||
ibmvtpm->dev = dev;
|
ibmvtpm->dev = dev;
|
||||||
ibmvtpm->vdev = vio_dev;
|
ibmvtpm->vdev = vio_dev;
|
||||||
chip->vendor.data = (void *)ibmvtpm;
|
TPM_VPRIV(chip) = (void *)ibmvtpm;
|
||||||
|
|
||||||
spin_lock_init(&ibmvtpm->rtce_lock);
|
spin_lock_init(&ibmvtpm->rtce_lock);
|
||||||
|
|
||||||
|
|
|
@ -227,6 +227,11 @@ static u8 tpm_nsc_status(struct tpm_chip *chip)
|
||||||
return inb(chip->vendor.base + NSC_STATUS);
|
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 = {
|
static const struct file_operations nsc_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = no_llseek,
|
.llseek = no_llseek,
|
||||||
|
@ -258,7 +263,7 @@ static const struct tpm_vendor_specific tpm_nsc = {
|
||||||
.status = tpm_nsc_status,
|
.status = tpm_nsc_status,
|
||||||
.req_complete_mask = NSC_STATUS_OBF,
|
.req_complete_mask = NSC_STATUS_OBF,
|
||||||
.req_complete_val = 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,
|
.attr_group = &nsc_attr_grp,
|
||||||
.miscdev = { .fops = &nsc_ops, },
|
.miscdev = { .fops = &nsc_ops, },
|
||||||
};
|
};
|
||||||
|
|
|
@ -84,6 +84,9 @@ static int is_itpm(struct pnp_dev *dev)
|
||||||
struct acpi_device *acpi = pnp_acpi_device(dev);
|
struct acpi_device *acpi = pnp_acpi_device(dev);
|
||||||
struct acpi_hardware_id *id;
|
struct acpi_hardware_id *id;
|
||||||
|
|
||||||
|
if (!acpi)
|
||||||
|
return 0;
|
||||||
|
|
||||||
list_for_each_entry(id, &acpi->pnp.ids, list) {
|
list_for_each_entry(id, &acpi->pnp.ids, list) {
|
||||||
if (!strcmp("INTC0102", id->id))
|
if (!strcmp("INTC0102", id->id))
|
||||||
return 1;
|
return 1;
|
||||||
|
@ -98,6 +101,22 @@ static inline int is_itpm(struct pnp_dev *dev)
|
||||||
}
|
}
|
||||||
#endif
|
#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)
|
static int check_locality(struct tpm_chip *chip, int l)
|
||||||
{
|
{
|
||||||
if ((ioread8(chip->vendor.iobase + TPM_ACCESS(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,
|
wait_for_tpm_stat(chip,
|
||||||
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||||
chip->vendor.timeout_c,
|
chip->vendor.timeout_c,
|
||||||
&chip->vendor.read_queue)
|
&chip->vendor.read_queue, true)
|
||||||
== 0) {
|
== 0) {
|
||||||
burstcnt = get_burstcount(chip);
|
burstcnt = get_burstcount(chip);
|
||||||
for (; burstcnt > 0 && size < count; burstcnt--)
|
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,
|
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);
|
status = tpm_tis_status(chip);
|
||||||
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
|
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
|
||||||
dev_err(chip->dev, "Error left over data\n");
|
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);
|
tpm_tis_ready(chip);
|
||||||
if (wait_for_tpm_stat
|
if (wait_for_tpm_stat
|
||||||
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
|
(chip, TPM_STS_COMMAND_READY, chip->vendor.timeout_b,
|
||||||
&chip->vendor.int_queue) < 0) {
|
&chip->vendor.int_queue, false) < 0) {
|
||||||
rc = -ETIME;
|
rc = -ETIME;
|
||||||
goto out_err;
|
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,
|
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);
|
status = tpm_tis_status(chip);
|
||||||
if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
|
if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
|
||||||
rc = -EIO;
|
rc = -EIO;
|
||||||
|
@ -304,7 +323,7 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||||
iowrite8(buf[count],
|
iowrite8(buf[count],
|
||||||
chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
|
chip->vendor.iobase + TPM_DATA_FIFO(chip->vendor.locality));
|
||||||
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c,
|
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);
|
status = tpm_tis_status(chip);
|
||||||
if ((status & TPM_STS_DATA_EXPECT) != 0) {
|
if ((status & TPM_STS_DATA_EXPECT) != 0) {
|
||||||
rc = -EIO;
|
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
|
if (wait_for_tpm_stat
|
||||||
(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
(chip, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||||
tpm_calc_ordinal_duration(chip, ordinal),
|
tpm_calc_ordinal_duration(chip, ordinal),
|
||||||
&chip->vendor.read_queue) < 0) {
|
&chip->vendor.read_queue, false) < 0) {
|
||||||
rc = -ETIME;
|
rc = -ETIME;
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
@ -374,7 +393,7 @@ static int probe_itpm(struct tpm_chip *chip)
|
||||||
if (vendor != TPM_VID_INTEL)
|
if (vendor != TPM_VID_INTEL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
itpm = 0;
|
itpm = false;
|
||||||
|
|
||||||
rc = tpm_tis_send_data(chip, cmd_getticks, len);
|
rc = tpm_tis_send_data(chip, cmd_getticks, len);
|
||||||
if (rc == 0)
|
if (rc == 0)
|
||||||
|
@ -383,7 +402,7 @@ static int probe_itpm(struct tpm_chip *chip)
|
||||||
tpm_tis_ready(chip);
|
tpm_tis_ready(chip);
|
||||||
release_locality(chip, chip->vendor.locality, 0);
|
release_locality(chip, chip->vendor.locality, 0);
|
||||||
|
|
||||||
itpm = 1;
|
itpm = true;
|
||||||
|
|
||||||
rc = tpm_tis_send_data(chip, cmd_getticks, len);
|
rc = tpm_tis_send_data(chip, cmd_getticks, len);
|
||||||
if (rc == 0) {
|
if (rc == 0) {
|
||||||
|
@ -400,6 +419,19 @@ out:
|
||||||
return rc;
|
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 = {
|
static const struct file_operations tis_ops = {
|
||||||
.owner = THIS_MODULE,
|
.owner = THIS_MODULE,
|
||||||
.llseek = no_llseek,
|
.llseek = no_llseek,
|
||||||
|
@ -445,7 +477,7 @@ static struct tpm_vendor_specific tpm_tis = {
|
||||||
.cancel = tpm_tis_ready,
|
.cancel = tpm_tis_ready,
|
||||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||||
.req_complete_val = 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,
|
.attr_group = &tis_attr_grp,
|
||||||
.miscdev = {
|
.miscdev = {
|
||||||
.fops = &tis_ops,},
|
.fops = &tis_ops,},
|
||||||
|
@ -502,7 +534,7 @@ static irqreturn_t tis_int_handler(int dummy, void *dev_id)
|
||||||
return IRQ_HANDLED;
|
return IRQ_HANDLED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool interrupts = 1;
|
static bool interrupts = true;
|
||||||
module_param(interrupts, bool, 0444);
|
module_param(interrupts, bool, 0444);
|
||||||
MODULE_PARM_DESC(interrupts, "Enable interrupts");
|
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_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||||
chip->vendor.timeout_d = 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) {
|
if (request_locality(chip, 0) != 0) {
|
||||||
rc = -ENODEV;
|
rc = -ENODEV;
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
|
vendor = ioread32(chip->vendor.iobase + TPM_DID_VID(0));
|
||||||
|
chip->vendor.manufacturer_id = vendor;
|
||||||
|
|
||||||
dev_info(dev,
|
dev_info(dev,
|
||||||
"1.2 TPM (device-id 0x%X, rev-id %d)\n",
|
"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;
|
rc = -ENODEV;
|
||||||
goto out_err;
|
goto out_err;
|
||||||
}
|
}
|
||||||
itpm = (probe == 0) ? 0 : 1;
|
itpm = !!probe;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (itpm)
|
if (itpm)
|
||||||
|
@ -741,10 +779,10 @@ static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
|
||||||
if (pnp_irq_valid(pnp_dev, 0))
|
if (pnp_irq_valid(pnp_dev, 0))
|
||||||
irq = pnp_irq(pnp_dev, 0);
|
irq = pnp_irq(pnp_dev, 0);
|
||||||
else
|
else
|
||||||
interrupts = 0;
|
interrupts = false;
|
||||||
|
|
||||||
if (is_itpm(pnp_dev))
|
if (is_itpm(pnp_dev))
|
||||||
itpm = 1;
|
itpm = true;
|
||||||
|
|
||||||
return tpm_tis_init(&pnp_dev->dev, start, len, irq);
|
return tpm_tis_init(&pnp_dev->dev, start, len, irq);
|
||||||
}
|
}
|
||||||
|
|
41
lib/digsig.c
41
lib/digsig.c
|
@ -30,11 +30,10 @@
|
||||||
|
|
||||||
static struct crypto_shash *shash;
|
static struct crypto_shash *shash;
|
||||||
|
|
||||||
static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
|
static const char *pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
|
||||||
unsigned long msglen,
|
unsigned long msglen,
|
||||||
unsigned long modulus_bitlen,
|
unsigned long modulus_bitlen,
|
||||||
unsigned char *out,
|
unsigned long *outlen)
|
||||||
unsigned long *outlen)
|
|
||||||
{
|
{
|
||||||
unsigned long modulus_len, ps_len, i;
|
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 */
|
/* test message size */
|
||||||
if ((msglen > modulus_len) || (modulus_len < 11))
|
if ((msglen > modulus_len) || (modulus_len < 11))
|
||||||
return -EINVAL;
|
return NULL;
|
||||||
|
|
||||||
/* separate encoded message */
|
/* separate encoded message */
|
||||||
if ((msg[0] != 0x00) || (msg[1] != (unsigned char)1))
|
if (msg[0] != 0x00 || msg[1] != 0x01)
|
||||||
return -EINVAL;
|
return NULL;
|
||||||
|
|
||||||
for (i = 2; i < modulus_len - 1; i++)
|
for (i = 2; i < modulus_len - 1; i++)
|
||||||
if (msg[i] != 0xFF)
|
if (msg[i] != 0xFF)
|
||||||
|
@ -56,19 +55,13 @@ static int pkcs_1_v1_5_decode_emsa(const unsigned char *msg,
|
||||||
if (msg[i] != 0)
|
if (msg[i] != 0)
|
||||||
/* There was no octet with hexadecimal value 0x00
|
/* There was no octet with hexadecimal value 0x00
|
||||||
to separate ps from m. */
|
to separate ps from m. */
|
||||||
return -EINVAL;
|
return NULL;
|
||||||
|
|
||||||
ps_len = i - 2;
|
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));
|
*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 long mlen, mblen;
|
||||||
unsigned nret, l;
|
unsigned nret, l;
|
||||||
int head, i;
|
int head, i;
|
||||||
unsigned char *out1 = NULL, *out2 = NULL;
|
unsigned char *out1 = NULL;
|
||||||
|
const char *m;
|
||||||
MPI in = NULL, res = NULL, pkey[2];
|
MPI in = NULL, res = NULL, pkey[2];
|
||||||
uint8_t *p, *datap, *endp;
|
uint8_t *p, *datap, *endp;
|
||||||
struct user_key_payload *ukp;
|
struct user_key_payload *ukp;
|
||||||
|
@ -120,7 +114,7 @@ static int digsig_verify_rsa(struct key *key,
|
||||||
}
|
}
|
||||||
|
|
||||||
mblen = mpi_get_nbits(pkey[0]);
|
mblen = mpi_get_nbits(pkey[0]);
|
||||||
mlen = (mblen + 7)/8;
|
mlen = DIV_ROUND_UP(mblen, 8);
|
||||||
|
|
||||||
if (mlen == 0)
|
if (mlen == 0)
|
||||||
goto err;
|
goto err;
|
||||||
|
@ -129,10 +123,6 @@ static int digsig_verify_rsa(struct key *key,
|
||||||
if (!out1)
|
if (!out1)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
out2 = kzalloc(mlen, GFP_KERNEL);
|
|
||||||
if (!out2)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
nret = siglen;
|
nret = siglen;
|
||||||
in = mpi_read_from_buffer(sig, &nret);
|
in = mpi_read_from_buffer(sig, &nret);
|
||||||
if (!in)
|
if (!in)
|
||||||
|
@ -164,18 +154,15 @@ static int digsig_verify_rsa(struct key *key,
|
||||||
|
|
||||||
kfree(p);
|
kfree(p);
|
||||||
|
|
||||||
err = pkcs_1_v1_5_decode_emsa(out1, len, mblen, out2, &len);
|
m = pkcs_1_v1_5_decode_emsa(out1, len, mblen, &len);
|
||||||
if (err)
|
|
||||||
goto err;
|
|
||||||
|
|
||||||
if (len != hlen || memcmp(out2, h, hlen))
|
if (!m || len != hlen || memcmp(m, h, hlen))
|
||||||
err = -EINVAL;
|
err = -EINVAL;
|
||||||
|
|
||||||
err:
|
err:
|
||||||
mpi_free(in);
|
mpi_free(in);
|
||||||
mpi_free(res);
|
mpi_free(res);
|
||||||
kfree(out1);
|
kfree(out1);
|
||||||
kfree(out2);
|
|
||||||
while (--i >= 0)
|
while (--i >= 0)
|
||||||
mpi_free(pkey[i]);
|
mpi_free(pkey[i]);
|
||||||
err1:
|
err1:
|
||||||
|
|
|
@ -65,10 +65,6 @@
|
||||||
typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
|
typedef mpi_limb_t *mpi_ptr_t; /* pointer to a limb */
|
||||||
typedef int mpi_size_t; /* (must be a signed type) */
|
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)
|
static inline int RESIZE_IF_NEEDED(MPI a, unsigned b)
|
||||||
{
|
{
|
||||||
if (a->alloced < b)
|
if (a->alloced < b)
|
||||||
|
|
|
@ -52,7 +52,7 @@ MPI mpi_read_raw_data(const void *xbuffer, size_t nbytes)
|
||||||
else
|
else
|
||||||
nbits = 0;
|
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);
|
val = mpi_alloc(nlimbs);
|
||||||
if (!val)
|
if (!val)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -96,8 +96,8 @@ MPI mpi_read_from_buffer(const void *xbuffer, unsigned *ret_nread)
|
||||||
buffer += 2;
|
buffer += 2;
|
||||||
nread = 2;
|
nread = 2;
|
||||||
|
|
||||||
nbytes = (nbits + 7) / 8;
|
nbytes = DIV_ROUND_UP(nbits, 8);
|
||||||
nlimbs = (nbytes + BYTES_PER_MPI_LIMB - 1) / BYTES_PER_MPI_LIMB;
|
nlimbs = DIV_ROUND_UP(nbytes, BYTES_PER_MPI_LIMB);
|
||||||
val = mpi_alloc(nlimbs);
|
val = mpi_alloc(nlimbs);
|
||||||
if (!val)
|
if (!val)
|
||||||
return NULL;
|
return NULL;
|
||||||
|
@ -193,7 +193,7 @@ int mpi_set_buffer(MPI a, const void *xbuffer, unsigned nbytes, int sign)
|
||||||
int nlimbs;
|
int nlimbs;
|
||||||
int i;
|
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)
|
if (RESIZE_IF_NEEDED(a, nlimbs) < 0)
|
||||||
return -ENOMEM;
|
return -ENOMEM;
|
||||||
a->sign = sign;
|
a->sign = sign;
|
||||||
|
|
|
@ -17,5 +17,17 @@ config INTEGRITY_SIGNATURE
|
||||||
This is useful for evm and module keyrings, when keys are
|
This is useful for evm and module keyrings, when keys are
|
||||||
usually only added from initramfs.
|
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/ima/Kconfig
|
||||||
source security/integrity/evm/Kconfig
|
source security/integrity/evm/Kconfig
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
|
|
||||||
obj-$(CONFIG_INTEGRITY) += integrity.o
|
obj-$(CONFIG_INTEGRITY) += integrity.o
|
||||||
obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
|
obj-$(CONFIG_INTEGRITY_SIGNATURE) += digsig.o
|
||||||
|
obj-$(CONFIG_INTEGRITY_ASYMMETRIC_KEYS) += digsig_asymmetric.o
|
||||||
|
|
||||||
integrity-y := iint.o
|
integrity-y := iint.o
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
|
@ -11,3 +11,16 @@ config EVM
|
||||||
integrity attacks.
|
integrity attacks.
|
||||||
|
|
||||||
If you are unsure how to answer this question, answer N.
|
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.
|
||||||
|
|
|
@ -24,6 +24,7 @@
|
||||||
extern int evm_initialized;
|
extern int evm_initialized;
|
||||||
extern char *evm_hmac;
|
extern char *evm_hmac;
|
||||||
extern char *evm_hash;
|
extern char *evm_hash;
|
||||||
|
extern int evm_hmac_version;
|
||||||
|
|
||||||
extern struct crypto_shash *hmac_tfm;
|
extern struct crypto_shash *hmac_tfm;
|
||||||
extern struct crypto_shash *hash_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,
|
extern int evm_init_hmac(struct inode *inode, const struct xattr *xattr,
|
||||||
char *hmac_val);
|
char *hmac_val);
|
||||||
extern int evm_init_secfs(void);
|
extern int evm_init_secfs(void);
|
||||||
extern void evm_cleanup_secfs(void);
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -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.gid = from_kgid(&init_user_ns, inode->i_gid);
|
||||||
hmac_misc.mode = inode->i_mode;
|
hmac_misc.mode = inode->i_mode;
|
||||||
crypto_shash_update(desc, (const u8 *)&hmac_misc, sizeof hmac_misc);
|
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);
|
crypto_shash_final(desc, digest);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,6 +26,7 @@ int evm_initialized;
|
||||||
|
|
||||||
char *evm_hmac = "hmac(sha1)";
|
char *evm_hmac = "hmac(sha1)";
|
||||||
char *evm_hash = "sha1";
|
char *evm_hash = "sha1";
|
||||||
|
int evm_hmac_version = CONFIG_EVM_HMAC_VERSION;
|
||||||
|
|
||||||
char *evm_config_xattrnames[] = {
|
char *evm_config_xattrnames[] = {
|
||||||
#ifdef CONFIG_SECURITY_SELINUX
|
#ifdef CONFIG_SECURITY_SELINUX
|
||||||
|
@ -427,15 +428,6 @@ err:
|
||||||
return error;
|
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
|
* evm_display_config - list the EVM protected security extended attributes
|
||||||
*/
|
*/
|
||||||
|
|
|
@ -100,9 +100,3 @@ int __init evm_init_secfs(void)
|
||||||
error = -EFAULT;
|
error = -EFAULT;
|
||||||
return error;
|
return error;
|
||||||
}
|
}
|
||||||
|
|
||||||
void __exit evm_cleanup_secfs(void)
|
|
||||||
{
|
|
||||||
if (evm_init_tpm)
|
|
||||||
securityfs_remove(evm_init_tpm);
|
|
||||||
}
|
|
||||||
|
|
|
@ -72,7 +72,10 @@ static void iint_free(struct integrity_iint_cache *iint)
|
||||||
{
|
{
|
||||||
iint->version = 0;
|
iint->version = 0;
|
||||||
iint->flags = 0UL;
|
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;
|
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||||
kmem_cache_free(iint_cache, iint);
|
kmem_cache_free(iint_cache, iint);
|
||||||
}
|
}
|
||||||
|
@ -149,7 +152,10 @@ static void init_once(void *foo)
|
||||||
memset(iint, 0, sizeof *iint);
|
memset(iint, 0, sizeof *iint);
|
||||||
iint->version = 0;
|
iint->version = 0;
|
||||||
iint->flags = 0UL;
|
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;
|
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -84,11 +84,12 @@ void ima_fs_cleanup(void);
|
||||||
int ima_inode_alloc(struct inode *inode);
|
int ima_inode_alloc(struct inode *inode);
|
||||||
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
|
int ima_add_template_entry(struct ima_template_entry *entry, int violation,
|
||||||
const char *op, struct inode *inode);
|
const char *op, struct inode *inode);
|
||||||
int ima_calc_hash(struct file *file, char *digest);
|
int ima_calc_file_hash(struct file *file, char *digest);
|
||||||
int ima_calc_template_hash(int template_len, void *template, char *digest);
|
int ima_calc_buffer_hash(const void *data, int len, char *digest);
|
||||||
int ima_calc_boot_aggregate(char *digest);
|
int ima_calc_boot_aggregate(char *digest);
|
||||||
void ima_add_violation(struct inode *inode, const unsigned char *filename,
|
void ima_add_violation(struct inode *inode, const unsigned char *filename,
|
||||||
const char *op, const char *cause);
|
const char *op, const char *cause);
|
||||||
|
int ima_init_crypto(void);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* used to protect h_table and sha_table
|
* 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,
|
int ima_store_template(struct ima_template_entry *entry, int violation,
|
||||||
struct inode *inode);
|
struct inode *inode);
|
||||||
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
|
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
|
/* rbtree tree calls to lookup, insert, delete
|
||||||
* integrity data associated with an inode.
|
* 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);
|
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
|
||||||
|
|
||||||
/* IMA policy related functions */
|
/* 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 ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||||
int flags);
|
int flags);
|
||||||
|
@ -142,13 +144,16 @@ void ima_delete_rules(void);
|
||||||
#define IMA_APPRAISE_MODULES 0x04
|
#define IMA_APPRAISE_MODULES 0x04
|
||||||
|
|
||||||
#ifdef CONFIG_IMA_APPRAISE
|
#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);
|
struct file *file, const unsigned char *filename);
|
||||||
int ima_must_appraise(struct inode *inode, int mask, enum ima_hooks func);
|
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);
|
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
|
#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,
|
struct file *file,
|
||||||
const unsigned char *filename)
|
const unsigned char *filename)
|
||||||
{
|
{
|
||||||
|
@ -165,6 +170,12 @@ static inline void ima_update_xattr(struct integrity_iint_cache *iint,
|
||||||
struct file *file)
|
struct file *file)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline enum integrity_status ima_get_cache_status(struct integrity_iint_cache
|
||||||
|
*iint, int func)
|
||||||
|
{
|
||||||
|
return INTEGRITY_UNKNOWN;
|
||||||
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
/* LSM based policy rules require audit */
|
/* LSM based policy rules require audit */
|
||||||
|
|
|
@ -50,8 +50,8 @@ int ima_store_template(struct ima_template_entry *entry,
|
||||||
entry->template_len = sizeof(entry->template);
|
entry->template_len = sizeof(entry->template);
|
||||||
|
|
||||||
if (!violation) {
|
if (!violation) {
|
||||||
result = ima_calc_template_hash(entry->template_len,
|
result = ima_calc_buffer_hash(&entry->template,
|
||||||
&entry->template,
|
entry->template_len,
|
||||||
entry->digest);
|
entry->digest);
|
||||||
if (result < 0) {
|
if (result < 0) {
|
||||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
|
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode,
|
||||||
|
@ -100,12 +100,12 @@ err_out:
|
||||||
* ima_get_action - appraise & measure decision based on policy.
|
* ima_get_action - appraise & measure decision based on policy.
|
||||||
* @inode: pointer to inode to measure
|
* @inode: pointer to inode to measure
|
||||||
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
|
* @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:
|
* The policy is defined in terms of keypairs:
|
||||||
* subj=, obj=, type=, func=, mask=, fsmagic=
|
* subj=, obj=, type=, func=, mask=, fsmagic=
|
||||||
* subj,obj, and type: are LSM specific.
|
* 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
|
* mask: contains the permission mask
|
||||||
* fsmagic: hex value
|
* 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;
|
u64 i_version = file->f_dentry->d_inode->i_version;
|
||||||
|
|
||||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
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) {
|
if (!result) {
|
||||||
iint->version = i_version;
|
iint->version = i_version;
|
||||||
iint->flags |= IMA_COLLECTED;
|
iint->flags |= IMA_COLLECTED;
|
||||||
|
@ -237,3 +237,20 @@ void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||||
|
|
||||||
iint->flags |= IMA_AUDITED;
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -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);
|
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)
|
struct integrity_iint_cache *iint)
|
||||||
{
|
{
|
||||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||||
__vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
|
return __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA,
|
||||||
sizeof iint->ima_xattr, 0);
|
(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
|
* 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 file *file, const unsigned char *filename)
|
||||||
{
|
{
|
||||||
struct dentry *dentry = file->f_dentry;
|
struct dentry *dentry = file->f_dentry;
|
||||||
|
@ -74,9 +131,6 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||||
if (!inode->i_op->getxattr)
|
if (!inode->i_op->getxattr)
|
||||||
return INTEGRITY_UNKNOWN;
|
return INTEGRITY_UNKNOWN;
|
||||||
|
|
||||||
if (iint->flags & IMA_APPRAISED)
|
|
||||||
return iint->ima_status;
|
|
||||||
|
|
||||||
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
|
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
|
||||||
0, GFP_NOFS);
|
0, GFP_NOFS);
|
||||||
if (rc <= 0) {
|
if (rc <= 0) {
|
||||||
|
@ -98,19 +152,18 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||||
cause = "invalid-HMAC";
|
cause = "invalid-HMAC";
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (xattr_value->type) {
|
switch (xattr_value->type) {
|
||||||
case IMA_XATTR_DIGEST:
|
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,
|
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
|
||||||
IMA_DIGEST_SIZE);
|
IMA_DIGEST_SIZE);
|
||||||
if (rc) {
|
if (rc) {
|
||||||
cause = "invalid-hash";
|
cause = "invalid-hash";
|
||||||
status = INTEGRITY_FAIL;
|
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;
|
break;
|
||||||
}
|
}
|
||||||
status = INTEGRITY_PASS;
|
status = INTEGRITY_PASS;
|
||||||
|
@ -141,15 +194,15 @@ out:
|
||||||
if ((ima_appraise & IMA_APPRAISE_FIX) &&
|
if ((ima_appraise & IMA_APPRAISE_FIX) &&
|
||||||
(!xattr_value ||
|
(!xattr_value ||
|
||||||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
||||||
ima_fix_xattr(dentry, iint);
|
if (!ima_fix_xattr(dentry, iint))
|
||||||
status = INTEGRITY_PASS;
|
status = INTEGRITY_PASS;
|
||||||
}
|
}
|
||||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
|
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
|
||||||
op, cause, rc, 0);
|
op, cause, rc, 0);
|
||||||
} else {
|
} else {
|
||||||
iint->flags |= IMA_APPRAISED;
|
ima_cache_flags(iint, func);
|
||||||
}
|
}
|
||||||
iint->ima_status = status;
|
ima_set_cache_status(iint, func, status);
|
||||||
kfree(xattr_value);
|
kfree(xattr_value);
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
@ -195,10 +248,11 @@ void ima_inode_post_setattr(struct dentry *dentry)
|
||||||
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
||||||
iint = integrity_iint_find(inode);
|
iint = integrity_iint_find(inode);
|
||||||
if (iint) {
|
if (iint) {
|
||||||
|
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED |
|
||||||
|
IMA_APPRAISE_SUBMASK | IMA_APPRAISED_SUBMASK |
|
||||||
|
IMA_ACTION_FLAGS);
|
||||||
if (must_appraise)
|
if (must_appraise)
|
||||||
iint->flags |= IMA_APPRAISE;
|
iint->flags |= IMA_APPRAISE;
|
||||||
else
|
|
||||||
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
|
|
||||||
}
|
}
|
||||||
if (!must_appraise)
|
if (!must_appraise)
|
||||||
rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
|
rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
|
||||||
|
|
|
@ -19,38 +19,41 @@
|
||||||
#include <linux/scatterlist.h>
|
#include <linux/scatterlist.h>
|
||||||
#include <linux/err.h>
|
#include <linux/err.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <crypto/hash.h>
|
||||||
#include "ima.h"
|
#include "ima.h"
|
||||||
|
|
||||||
static int init_desc(struct hash_desc *desc)
|
static struct crypto_shash *ima_shash_tfm;
|
||||||
{
|
|
||||||
int rc;
|
|
||||||
|
|
||||||
desc->tfm = crypto_alloc_hash(ima_hash, 0, CRYPTO_ALG_ASYNC);
|
int ima_init_crypto(void)
|
||||||
if (IS_ERR(desc->tfm)) {
|
{
|
||||||
pr_info("IMA: failed to load %s transform: %ld\n",
|
long rc;
|
||||||
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;
|
return rc;
|
||||||
}
|
}
|
||||||
desc->flags = 0;
|
return 0;
|
||||||
rc = crypto_hash_init(desc);
|
|
||||||
if (rc)
|
|
||||||
crypto_free_hash(desc->tfm);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Calculate the MD5/SHA1 file digest
|
* 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;
|
loff_t i_size, offset = 0;
|
||||||
char *rbuf;
|
char *rbuf;
|
||||||
int rc, read = 0;
|
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)
|
if (rc != 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
@ -75,41 +78,34 @@ int ima_calc_hash(struct file *file, char *digest)
|
||||||
if (rbuf_len == 0)
|
if (rbuf_len == 0)
|
||||||
break;
|
break;
|
||||||
offset += rbuf_len;
|
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)
|
if (rc)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
kfree(rbuf);
|
kfree(rbuf);
|
||||||
if (!rc)
|
if (!rc)
|
||||||
rc = crypto_hash_final(&desc, digest);
|
rc = crypto_shash_final(&desc.shash, digest);
|
||||||
if (read)
|
if (read)
|
||||||
file->f_mode &= ~FMODE_READ;
|
file->f_mode &= ~FMODE_READ;
|
||||||
out:
|
out:
|
||||||
crypto_free_hash(desc.tfm);
|
|
||||||
return rc;
|
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 {
|
||||||
struct scatterlist sg[1];
|
struct shash_desc shash;
|
||||||
int rc;
|
char ctx[crypto_shash_descsize(ima_shash_tfm)];
|
||||||
|
} desc;
|
||||||
|
|
||||||
rc = init_desc(&desc);
|
desc.shash.tfm = ima_shash_tfm;
|
||||||
if (rc != 0)
|
desc.shash.flags = 0;
|
||||||
return rc;
|
|
||||||
|
|
||||||
sg_init_one(sg, template, template_len);
|
return crypto_shash_digest(&desc.shash, data, len, digest);
|
||||||
rc = crypto_hash_update(&desc, sg, template_len);
|
|
||||||
if (!rc)
|
|
||||||
rc = crypto_hash_final(&desc, digest);
|
|
||||||
crypto_free_hash(desc.tfm);
|
|
||||||
return rc;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void __init ima_pcrread(int idx, u8 *pcr)
|
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)
|
int __init ima_calc_boot_aggregate(char *digest)
|
||||||
{
|
{
|
||||||
struct hash_desc desc;
|
|
||||||
struct scatterlist sg;
|
|
||||||
u8 pcr_i[IMA_DIGEST_SIZE];
|
u8 pcr_i[IMA_DIGEST_SIZE];
|
||||||
int rc, i;
|
int rc, i;
|
||||||
|
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)
|
if (rc != 0)
|
||||||
return rc;
|
return rc;
|
||||||
|
|
||||||
|
@ -139,11 +140,9 @@ int __init ima_calc_boot_aggregate(char *digest)
|
||||||
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
|
for (i = TPM_PCR0; i < TPM_PCR8; i++) {
|
||||||
ima_pcrread(i, pcr_i);
|
ima_pcrread(i, pcr_i);
|
||||||
/* now accumulate with current aggregate */
|
/* now accumulate with current aggregate */
|
||||||
sg_init_one(&sg, pcr_i, IMA_DIGEST_SIZE);
|
rc = crypto_shash_update(&desc.shash, pcr_i, IMA_DIGEST_SIZE);
|
||||||
rc = crypto_hash_update(&desc, &sg, IMA_DIGEST_SIZE);
|
|
||||||
}
|
}
|
||||||
if (!rc)
|
if (!rc)
|
||||||
crypto_hash_final(&desc, digest);
|
crypto_shash_final(&desc.shash, digest);
|
||||||
crypto_free_hash(desc.tfm);
|
|
||||||
return rc;
|
return rc;
|
||||||
}
|
}
|
||||||
|
|
|
@ -85,6 +85,9 @@ int __init ima_init(void)
|
||||||
if (!ima_used_chip)
|
if (!ima_used_chip)
|
||||||
pr_info("IMA: No TPM chip found, activating TPM-bypass!\n");
|
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_add_boot_aggregate(); /* boot aggregate must be first entry */
|
||||||
ima_init_policy();
|
ima_init_policy();
|
||||||
|
|
||||||
|
|
|
@ -61,7 +61,8 @@ static void ima_rdwr_violation_check(struct file *file)
|
||||||
fmode_t mode = file->f_mode;
|
fmode_t mode = file->f_mode;
|
||||||
int must_measure;
|
int must_measure;
|
||||||
bool send_tomtou = false, send_writers = false;
|
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)
|
if (!S_ISREG(inode->i_mode) || !ima_initialized)
|
||||||
return;
|
return;
|
||||||
|
@ -86,22 +87,15 @@ out:
|
||||||
if (!send_tomtou && !send_writers)
|
if (!send_tomtou && !send_writers)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/* We will allow 11 spaces for ' (deleted)' to be appended */
|
pathname = ima_d_path(&file->f_path, &pathbuf);
|
||||||
pathbuf = kmalloc(PATH_MAX + 11, GFP_KERNEL);
|
if (!pathname || strlen(pathname) > IMA_EVENT_NAME_LEN_MAX)
|
||||||
if (pathbuf) {
|
pathname = dentry->d_name.name;
|
||||||
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;
|
|
||||||
}
|
|
||||||
if (send_tomtou)
|
if (send_tomtou)
|
||||||
ima_add_violation(inode,
|
ima_add_violation(inode, pathname,
|
||||||
!pathname ? dentry->d_name.name : pathname,
|
|
||||||
"invalid_pcr", "ToMToU");
|
"invalid_pcr", "ToMToU");
|
||||||
if (send_writers)
|
if (send_writers)
|
||||||
ima_add_violation(inode,
|
ima_add_violation(inode, pathname,
|
||||||
!pathname ? dentry->d_name.name : pathname,
|
|
||||||
"invalid_pcr", "open_writers");
|
"invalid_pcr", "open_writers");
|
||||||
kfree(pathbuf);
|
kfree(pathbuf);
|
||||||
}
|
}
|
||||||
|
@ -145,25 +139,31 @@ void ima_file_free(struct file *file)
|
||||||
ima_check_last_writer(iint, inode, 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)
|
int mask, int function)
|
||||||
{
|
{
|
||||||
struct inode *inode = file->f_dentry->d_inode;
|
struct inode *inode = file->f_dentry->d_inode;
|
||||||
struct integrity_iint_cache *iint;
|
struct integrity_iint_cache *iint;
|
||||||
unsigned char *pathname = NULL, *pathbuf = NULL;
|
char *pathbuf = NULL;
|
||||||
int rc = -ENOMEM, action, must_appraise;
|
const char *pathname = NULL;
|
||||||
|
int rc = -ENOMEM, action, must_appraise, _func;
|
||||||
|
|
||||||
if (!ima_initialized || !S_ISREG(inode->i_mode))
|
if (!ima_initialized || !S_ISREG(inode->i_mode))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
/* Determine if in appraise/audit/measurement policy,
|
/* Return an IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT action
|
||||||
* returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */
|
* bitmask based on the appraise/audit/measurement policy.
|
||||||
|
* Included is the appraise submask.
|
||||||
|
*/
|
||||||
action = ima_get_action(inode, mask, function);
|
action = ima_get_action(inode, mask, function);
|
||||||
if (!action)
|
if (!action)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
must_appraise = action & IMA_APPRAISE;
|
must_appraise = action & IMA_APPRAISE;
|
||||||
|
|
||||||
|
/* Is the appraise rule hook specific? */
|
||||||
|
_func = (action & IMA_FILE_APPRAISE) ? FILE_CHECK : function;
|
||||||
|
|
||||||
mutex_lock(&inode->i_mutex);
|
mutex_lock(&inode->i_mutex);
|
||||||
|
|
||||||
iint = integrity_inode_get(inode);
|
iint = integrity_inode_get(inode);
|
||||||
|
@ -171,44 +171,45 @@ static int process_measurement(struct file *file, const unsigned char *filename,
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
/* Determine if already appraised/measured based on bitmask
|
/* Determine if already appraised/measured based on bitmask
|
||||||
* (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
|
* (IMA_MEASURE, IMA_MEASURED, IMA_XXXX_APPRAISE, IMA_XXXX_APPRAISED,
|
||||||
* IMA_AUDIT, IMA_AUDITED) */
|
* IMA_AUDIT, IMA_AUDITED)
|
||||||
|
*/
|
||||||
iint->flags |= action;
|
iint->flags |= action;
|
||||||
|
action &= IMA_DO_MASK;
|
||||||
action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
|
action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
|
||||||
|
|
||||||
/* Nothing to do, just return existing appraised status */
|
/* Nothing to do, just return existing appraised status */
|
||||||
if (!action) {
|
if (!action) {
|
||||||
if (iint->flags & IMA_APPRAISED)
|
if (must_appraise)
|
||||||
rc = iint->ima_status;
|
rc = ima_get_cache_status(iint, _func);
|
||||||
goto out;
|
goto out_digsig;
|
||||||
}
|
}
|
||||||
|
|
||||||
rc = ima_collect_measurement(iint, file);
|
rc = ima_collect_measurement(iint, file);
|
||||||
if (rc != 0)
|
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)
|
if (action & IMA_MEASURE)
|
||||||
ima_store_measurement(iint, file,
|
ima_store_measurement(iint, file, pathname);
|
||||||
!pathname ? filename : pathname);
|
if (action & IMA_APPRAISE_SUBMASK)
|
||||||
if (action & IMA_APPRAISE)
|
rc = ima_appraise_measurement(_func, iint, file, pathname);
|
||||||
rc = ima_appraise_measurement(iint, file,
|
|
||||||
!pathname ? filename : pathname);
|
|
||||||
if (action & IMA_AUDIT)
|
if (action & IMA_AUDIT)
|
||||||
ima_audit_measurement(iint, !pathname ? filename : pathname);
|
ima_audit_measurement(iint, pathname);
|
||||||
kfree(pathbuf);
|
kfree(pathbuf);
|
||||||
|
out_digsig:
|
||||||
|
if ((mask & MAY_WRITE) && (iint->flags & IMA_DIGSIG))
|
||||||
|
rc = -EACCES;
|
||||||
out:
|
out:
|
||||||
mutex_unlock(&inode->i_mutex);
|
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()
|
* Measure files being mmapped executable based on the ima_must_measure()
|
||||||
* policy decision.
|
* policy decision.
|
||||||
*
|
*
|
||||||
* Return 0 on success, an error code on failure.
|
* On success return 0. On integrity appraisal error, assuming the file
|
||||||
* (Based on the results of appraise_measurement().)
|
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
|
||||||
*/
|
*/
|
||||||
int ima_file_mmap(struct file *file, unsigned long prot)
|
int ima_file_mmap(struct file *file, unsigned long prot)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
if (file && (prot & PROT_EXEC))
|
||||||
|
return process_measurement(file, file->f_dentry->d_name.name,
|
||||||
if (!file)
|
MAY_EXEC, MMAP_CHECK);
|
||||||
return 0;
|
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -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
|
* So we can be certain that what we verify and measure here is actually
|
||||||
* what is being executed.
|
* what is being executed.
|
||||||
*
|
*
|
||||||
* Return 0 on success, an error code on failure.
|
* On success return 0. On integrity appraisal error, assuming the file
|
||||||
* (Based on the results of appraise_measurement().)
|
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
|
||||||
*/
|
*/
|
||||||
int ima_bprm_check(struct linux_binprm *bprm)
|
int ima_bprm_check(struct linux_binprm *bprm)
|
||||||
{
|
{
|
||||||
int rc;
|
return process_measurement(bprm->file,
|
||||||
|
|
||||||
rc = process_measurement(bprm->file,
|
|
||||||
(strcmp(bprm->filename, bprm->interp) == 0) ?
|
(strcmp(bprm->filename, bprm->interp) == 0) ?
|
||||||
bprm->filename : bprm->interp,
|
bprm->filename : bprm->interp,
|
||||||
MAY_EXEC, BPRM_CHECK);
|
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.
|
* Measure files based on the ima_must_measure() policy decision.
|
||||||
*
|
*
|
||||||
* Always return 0 and audit dentry_open failures.
|
* On success return 0. On integrity appraisal error, assuming the file
|
||||||
* (Return code will be based upon measurement appraisal.)
|
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
|
||||||
*/
|
*/
|
||||||
int ima_file_check(struct file *file, int mask)
|
int ima_file_check(struct file *file, int mask)
|
||||||
{
|
{
|
||||||
int rc;
|
|
||||||
|
|
||||||
ima_rdwr_violation_check(file);
|
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),
|
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||||
FILE_CHECK);
|
FILE_CHECK);
|
||||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
EXPORT_SYMBOL_GPL(ima_file_check);
|
||||||
|
|
||||||
|
@ -286,23 +277,20 @@ EXPORT_SYMBOL_GPL(ima_file_check);
|
||||||
*
|
*
|
||||||
* Measure/appraise kernel modules based on policy.
|
* Measure/appraise kernel modules based on policy.
|
||||||
*
|
*
|
||||||
* Always return 0 and audit dentry_open failures.
|
* On success return 0. On integrity appraisal error, assuming the file
|
||||||
* Return code is based upon measurement appraisal.
|
* is in policy and IMA-appraisal is in enforcing mode, return -EACCES.
|
||||||
*/
|
*/
|
||||||
int ima_module_check(struct file *file)
|
int ima_module_check(struct file *file)
|
||||||
{
|
{
|
||||||
int rc = 0;
|
|
||||||
|
|
||||||
if (!file) {
|
if (!file) {
|
||||||
if (ima_appraise & IMA_APPRAISE_MODULES) {
|
|
||||||
#ifndef CONFIG_MODULE_SIG_FORCE
|
#ifndef CONFIG_MODULE_SIG_FORCE
|
||||||
rc = -EACCES; /* INTEGRITY_UNKNOWN */
|
if (ima_appraise & IMA_APPRAISE_MODULES)
|
||||||
|
return -EACCES; /* INTEGRITY_UNKNOWN */
|
||||||
#endif
|
#endif
|
||||||
}
|
return 0; /* We rely on module signature checking */
|
||||||
} else
|
}
|
||||||
rc = process_measurement(file, file->f_dentry->d_name.name,
|
return process_measurement(file, file->f_dentry->d_name.name,
|
||||||
MAY_EXEC, MODULE_CHECK);
|
MAY_EXEC, MODULE_CHECK);
|
||||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __init init_ima(void)
|
static int __init init_ima(void)
|
||||||
|
|
|
@ -16,6 +16,7 @@
|
||||||
#include <linux/magic.h>
|
#include <linux/magic.h>
|
||||||
#include <linux/parser.h>
|
#include <linux/parser.h>
|
||||||
#include <linux/slab.h>
|
#include <linux/slab.h>
|
||||||
|
#include <linux/genhd.h>
|
||||||
|
|
||||||
#include "ima.h"
|
#include "ima.h"
|
||||||
|
|
||||||
|
@ -25,6 +26,7 @@
|
||||||
#define IMA_FSMAGIC 0x0004
|
#define IMA_FSMAGIC 0x0004
|
||||||
#define IMA_UID 0x0008
|
#define IMA_UID 0x0008
|
||||||
#define IMA_FOWNER 0x0010
|
#define IMA_FOWNER 0x0010
|
||||||
|
#define IMA_FSUUID 0x0020
|
||||||
|
|
||||||
#define UNKNOWN 0
|
#define UNKNOWN 0
|
||||||
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
||||||
|
@ -45,10 +47,12 @@ struct ima_rule_entry {
|
||||||
enum ima_hooks func;
|
enum ima_hooks func;
|
||||||
int mask;
|
int mask;
|
||||||
unsigned long fsmagic;
|
unsigned long fsmagic;
|
||||||
|
u8 fsuuid[16];
|
||||||
kuid_t uid;
|
kuid_t uid;
|
||||||
kuid_t fowner;
|
kuid_t fowner;
|
||||||
struct {
|
struct {
|
||||||
void *rule; /* LSM file metadata specific */
|
void *rule; /* LSM file metadata specific */
|
||||||
|
void *args_p; /* audit value */
|
||||||
int type; /* audit type */
|
int type; /* audit type */
|
||||||
} lsm[MAX_LSM_RULES];
|
} 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 = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||||
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
|
{.action = DONT_MEASURE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||||
{.action = DONT_MEASURE,.fsmagic = SELINUX_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},
|
.flags = IMA_FUNC | IMA_MASK},
|
||||||
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
|
{.action = MEASURE,.func = BPRM_CHECK,.mask = MAY_EXEC,
|
||||||
.flags = IMA_FUNC | IMA_MASK},
|
.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);
|
__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.
|
* ima_match_rules - determine whether an inode matches the measure rule.
|
||||||
* @rule: a pointer to a 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)
|
if ((rule->flags & IMA_FSMAGIC)
|
||||||
&& rule->fsmagic != inode->i_sb->s_magic)
|
&& rule->fsmagic != inode->i_sb->s_magic)
|
||||||
return false;
|
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))
|
if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
|
||||||
return false;
|
return false;
|
||||||
if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
|
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++) {
|
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||||
int rc = 0;
|
int rc = 0;
|
||||||
u32 osid, sid;
|
u32 osid, sid;
|
||||||
|
int retried = 0;
|
||||||
|
|
||||||
if (!rule->lsm[i].rule)
|
if (!rule->lsm[i].rule)
|
||||||
continue;
|
continue;
|
||||||
|
retry:
|
||||||
switch (i) {
|
switch (i) {
|
||||||
case LSM_OBJ_USER:
|
case LSM_OBJ_USER:
|
||||||
case LSM_OBJ_ROLE:
|
case LSM_OBJ_ROLE:
|
||||||
|
@ -176,12 +213,39 @@ static bool ima_match_rules(struct ima_rule_entry *rule,
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
if ((rc < 0) && (!retried)) {
|
||||||
|
retried = 1;
|
||||||
|
ima_lsm_update_rules();
|
||||||
|
goto retry;
|
||||||
|
}
|
||||||
if (!rc)
|
if (!rc)
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
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
|
* ima_match_policy - decision based on LSM and other conditions
|
||||||
* @inode: pointer to an inode for which the policy decision is being made
|
* @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))
|
if (!ima_match_rules(entry, inode, func, mask))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
action |= entry->flags & IMA_ACTION_FLAGS;
|
||||||
|
|
||||||
action |= entry->action & IMA_DO_MASK;
|
action |= entry->action & IMA_DO_MASK;
|
||||||
|
if (entry->action & IMA_APPRAISE)
|
||||||
|
action |= get_subaction(entry, func);
|
||||||
|
|
||||||
if (entry->action & IMA_DO_MASK)
|
if (entry->action & IMA_DO_MASK)
|
||||||
actmask &= ~(entry->action | entry->action << 1);
|
actmask &= ~(entry->action | entry->action << 1);
|
||||||
else
|
else
|
||||||
|
@ -282,7 +351,8 @@ enum {
|
||||||
Opt_audit,
|
Opt_audit,
|
||||||
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
||||||
Opt_subj_user, Opt_subj_role, Opt_subj_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 = {
|
static match_table_t policy_tokens = {
|
||||||
|
@ -300,25 +370,35 @@ static match_table_t policy_tokens = {
|
||||||
{Opt_func, "func=%s"},
|
{Opt_func, "func=%s"},
|
||||||
{Opt_mask, "mask=%s"},
|
{Opt_mask, "mask=%s"},
|
||||||
{Opt_fsmagic, "fsmagic=%s"},
|
{Opt_fsmagic, "fsmagic=%s"},
|
||||||
|
{Opt_fsuuid, "fsuuid=%s"},
|
||||||
{Opt_uid, "uid=%s"},
|
{Opt_uid, "uid=%s"},
|
||||||
{Opt_fowner, "fowner=%s"},
|
{Opt_fowner, "fowner=%s"},
|
||||||
|
{Opt_appraise_type, "appraise_type=%s"},
|
||||||
{Opt_err, NULL}
|
{Opt_err, NULL}
|
||||||
};
|
};
|
||||||
|
|
||||||
static int ima_lsm_rule_init(struct ima_rule_entry *entry,
|
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;
|
int result;
|
||||||
|
|
||||||
if (entry->lsm[lsm_rule].rule)
|
if (entry->lsm[lsm_rule].rule)
|
||||||
return -EINVAL;
|
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;
|
entry->lsm[lsm_rule].type = audit_type;
|
||||||
result = security_filter_rule_init(entry->lsm[lsm_rule].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);
|
&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 -EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,8 +484,9 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
entry->func = FILE_CHECK;
|
entry->func = FILE_CHECK;
|
||||||
else if (strcmp(args[0].from, "MODULE_CHECK") == 0)
|
else if (strcmp(args[0].from, "MODULE_CHECK") == 0)
|
||||||
entry->func = MODULE_CHECK;
|
entry->func = MODULE_CHECK;
|
||||||
else if (strcmp(args[0].from, "FILE_MMAP") == 0)
|
else if ((strcmp(args[0].from, "FILE_MMAP") == 0)
|
||||||
entry->func = FILE_MMAP;
|
|| (strcmp(args[0].from, "MMAP_CHECK") == 0))
|
||||||
|
entry->func = MMAP_CHECK;
|
||||||
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
|
else if (strcmp(args[0].from, "BPRM_CHECK") == 0)
|
||||||
entry->func = BPRM_CHECK;
|
entry->func = BPRM_CHECK;
|
||||||
else
|
else
|
||||||
|
@ -445,6 +526,19 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||||
if (!result)
|
if (!result)
|
||||||
entry->flags |= IMA_FSMAGIC;
|
entry->flags |= IMA_FSMAGIC;
|
||||||
break;
|
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:
|
case Opt_uid:
|
||||||
ima_log_string(ab, "uid", args[0].from);
|
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;
|
break;
|
||||||
case Opt_obj_user:
|
case Opt_obj_user:
|
||||||
ima_log_string(ab, "obj_user", args[0].from);
|
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,
|
LSM_OBJ_USER,
|
||||||
AUDIT_OBJ_USER);
|
AUDIT_OBJ_USER);
|
||||||
break;
|
break;
|
||||||
case Opt_obj_role:
|
case Opt_obj_role:
|
||||||
ima_log_string(ab, "obj_role", args[0].from);
|
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,
|
LSM_OBJ_ROLE,
|
||||||
AUDIT_OBJ_ROLE);
|
AUDIT_OBJ_ROLE);
|
||||||
break;
|
break;
|
||||||
case Opt_obj_type:
|
case Opt_obj_type:
|
||||||
ima_log_string(ab, "obj_type", args[0].from);
|
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,
|
LSM_OBJ_TYPE,
|
||||||
AUDIT_OBJ_TYPE);
|
AUDIT_OBJ_TYPE);
|
||||||
break;
|
break;
|
||||||
case Opt_subj_user:
|
case Opt_subj_user:
|
||||||
ima_log_string(ab, "subj_user", args[0].from);
|
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,
|
LSM_SUBJ_USER,
|
||||||
AUDIT_SUBJ_USER);
|
AUDIT_SUBJ_USER);
|
||||||
break;
|
break;
|
||||||
case Opt_subj_role:
|
case Opt_subj_role:
|
||||||
ima_log_string(ab, "subj_role", args[0].from);
|
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,
|
LSM_SUBJ_ROLE,
|
||||||
AUDIT_SUBJ_ROLE);
|
AUDIT_SUBJ_ROLE);
|
||||||
break;
|
break;
|
||||||
case Opt_subj_type:
|
case Opt_subj_type:
|
||||||
ima_log_string(ab, "subj_type", args[0].from);
|
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,
|
LSM_SUBJ_TYPE,
|
||||||
AUDIT_SUBJ_TYPE);
|
AUDIT_SUBJ_TYPE);
|
||||||
break;
|
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:
|
case Opt_err:
|
||||||
ima_log_string(ab, "UNKNOWN", p);
|
ima_log_string(ab, "UNKNOWN", p);
|
||||||
result = -EINVAL;
|
result = -EINVAL;
|
||||||
|
@ -590,9 +696,13 @@ ssize_t ima_parse_add_rule(char *rule)
|
||||||
void ima_delete_rules(void)
|
void ima_delete_rules(void)
|
||||||
{
|
{
|
||||||
struct ima_rule_entry *entry, *tmp;
|
struct ima_rule_entry *entry, *tmp;
|
||||||
|
int i;
|
||||||
|
|
||||||
mutex_lock(&ima_rules_mutex);
|
mutex_lock(&ima_rules_mutex);
|
||||||
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
|
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);
|
list_del(&entry->list);
|
||||||
kfree(entry);
|
kfree(entry);
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,23 +14,41 @@
|
||||||
#include <linux/types.h>
|
#include <linux/types.h>
|
||||||
#include <linux/integrity.h>
|
#include <linux/integrity.h>
|
||||||
#include <crypto/sha.h>
|
#include <crypto/sha.h>
|
||||||
|
#include <linux/key.h>
|
||||||
|
|
||||||
/* iint action cache flags */
|
/* iint action cache flags */
|
||||||
#define IMA_MEASURE 0x0001
|
#define IMA_MEASURE 0x00000001
|
||||||
#define IMA_MEASURED 0x0002
|
#define IMA_MEASURED 0x00000002
|
||||||
#define IMA_APPRAISE 0x0004
|
#define IMA_APPRAISE 0x00000004
|
||||||
#define IMA_APPRAISED 0x0008
|
#define IMA_APPRAISED 0x00000008
|
||||||
/*#define IMA_COLLECT 0x0010 do not use this flag */
|
/*#define IMA_COLLECT 0x00000010 do not use this flag */
|
||||||
#define IMA_COLLECTED 0x0020
|
#define IMA_COLLECTED 0x00000020
|
||||||
#define IMA_AUDIT 0x0040
|
#define IMA_AUDIT 0x00000040
|
||||||
#define IMA_AUDITED 0x0080
|
#define IMA_AUDITED 0x00000080
|
||||||
|
|
||||||
/* iint cache flags */
|
/* 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_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT | \
|
||||||
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \
|
IMA_APPRAISE_SUBMASK)
|
||||||
| IMA_COLLECTED)
|
#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 {
|
enum evm_ima_xattr_type {
|
||||||
IMA_XATTR_DIGEST = 0x01,
|
IMA_XATTR_DIGEST = 0x01,
|
||||||
|
@ -48,10 +66,13 @@ struct integrity_iint_cache {
|
||||||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||||
struct inode *inode; /* back pointer to inode in question */
|
struct inode *inode; /* back pointer to inode in question */
|
||||||
u64 version; /* track inode changes */
|
u64 version; /* track inode changes */
|
||||||
unsigned short flags;
|
unsigned long flags;
|
||||||
struct evm_ima_xattr_data ima_xattr;
|
struct evm_ima_xattr_data ima_xattr;
|
||||||
enum integrity_status ima_status;
|
enum integrity_status ima_file_status:4;
|
||||||
enum integrity_status evm_status;
|
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
|
/* 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 */
|
#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 */
|
/* set during initialization */
|
||||||
extern int iint_initialized;
|
extern int iint_initialized;
|
||||||
|
|
Loading…
Reference in New Issue