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