Commit 33673dcb authored by Linus Torvalds's avatar Linus Torvalds

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