From de4bb3b9c788ea5504dfe094e34d831e8395075d Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Tue, 31 Jul 2012 16:15:36 +0200 Subject: [PATCH 01/39] samples/seccomp: fix endianness bug in LO_ARG define The LO_ARG define needs to consider endianness also for 32 bit builds. The "bpf_fancy" test case didn't work on s390 in 32 bit and compat mode because the LO_ARG define resulted in a BPF program which read the upper halve of the 64 bit system call arguments instead of the lower halves. Signed-off-by: Heiko Carstens Acked-by: Kees Cook Signed-off-by: James Morris --- samples/seccomp/bpf-helper.h | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/samples/seccomp/bpf-helper.h b/samples/seccomp/bpf-helper.h index 643279dd30fb..38ee70f3cd5b 100644 --- a/samples/seccomp/bpf-helper.h +++ b/samples/seccomp/bpf-helper.h @@ -59,6 +59,16 @@ void seccomp_bpf_print(struct sock_filter *filter, size_t count); #define FIND_LABEL(labels, label) seccomp_bpf_label((labels), #label) #define EXPAND(...) __VA_ARGS__ + +/* Ensure that we load the logically correct offset. */ +#if __BYTE_ORDER == __LITTLE_ENDIAN +#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) +#elif __BYTE_ORDER == __BIG_ENDIAN +#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) +#else +#error "Unknown endianness" +#endif + /* Map all width-sensitive operations */ #if __BITS_PER_LONG == 32 @@ -70,21 +80,16 @@ void seccomp_bpf_print(struct sock_filter *filter, size_t count); #define JLE(x, jt) JLE32(x, EXPAND(jt)) #define JA(x, jt) JA32(x, EXPAND(jt)) #define ARG(i) ARG_32(i) -#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) #elif __BITS_PER_LONG == 64 /* Ensure that we load the logically correct offset. */ #if __BYTE_ORDER == __LITTLE_ENDIAN #define ENDIAN(_lo, _hi) _lo, _hi -#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) #define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) #elif __BYTE_ORDER == __BIG_ENDIAN #define ENDIAN(_lo, _hi) _hi, _lo -#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32) #define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) -#else -#error "Unknown endianness" #endif union arg64 { From 9f99798ff49e73dded73a8c674044ea6fb6af651 Mon Sep 17 00:00:00 2001 From: Tetsuo Handa Date: Tue, 31 Jul 2012 20:37:00 +0900 Subject: [PATCH 02/39] ptrace: mark __ptrace_may_access() static __ptrace_may_access() is used within only kernel/ptrace.c. Signed-off-by: Tetsuo Handa Signed-off-by: James Morris --- include/linux/ptrace.h | 2 -- kernel/ptrace.c | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/include/linux/ptrace.h b/include/linux/ptrace.h index 597e4fdb97fe..3db698aee34c 100644 --- a/include/linux/ptrace.h +++ b/include/linux/ptrace.h @@ -130,8 +130,6 @@ extern void exit_ptrace(struct task_struct *tracer); #define PTRACE_MODE_READ 0x01 #define PTRACE_MODE_ATTACH 0x02 #define PTRACE_MODE_NOAUDIT 0x04 -/* Returns 0 on success, -errno on denial. */ -extern int __ptrace_may_access(struct task_struct *task, unsigned int mode); /* Returns true on success, false on denial. */ extern bool ptrace_may_access(struct task_struct *task, unsigned int mode); diff --git a/kernel/ptrace.c b/kernel/ptrace.c index a232bb59d93f..1f5e55dda955 100644 --- a/kernel/ptrace.c +++ b/kernel/ptrace.c @@ -180,7 +180,8 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode) return has_ns_capability(current, ns, CAP_SYS_PTRACE); } -int __ptrace_may_access(struct task_struct *task, unsigned int mode) +/* Returns 0 on success, -errno on denial. */ +static int __ptrace_may_access(struct task_struct *task, unsigned int mode) { const struct cred *cred = current_cred(), *tcred; From aad628c1d91a6db57e572e4c1f35e863d81061d7 Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Tue, 7 Aug 2012 11:42:32 +0200 Subject: [PATCH 03/39] char/tpm: Add new driver for Infineon I2C TIS TPM This patch adds a driver to support Infineon's SLB 9635 TT 1.2 Soft I2C TPMs which follow the TGC TIS 1.2 TPM specification[1] and Infineon's I2C Protocol Stack Specification 0.20. The I2C Protocol Stack Specification is a simple adaption of the LPC TIS Protocol to the I2C Bus. The I2C TPMs can be used when LPC Bus is not available (i.e. non x86 architectures like ARM). The driver is based on the tpm_tis.c driver by Leendert van Dorn and Kyleen Hall and has quite similar functionality. Tested on Nvidia ARM Tegra2 Development Platform and Beagleboard (ARM OMAP) Tested with the Trousers[2] TSS API Testsuite v 0.3 [3] Compile-tested on x86 (32/64-bit) Updates since version 2.1.4: - included "Lock the I2C adapter for a sequence of requests", by Bryan Freed - use __i2c_transfer instead of own implementation of unlocked i2c_transfer - use struct dev_pm_ops for power management via SIMPLE_DEV_PM_OPS Updates since version 2.1.3: - use proper probing mechanism * either add the tpm using I2C_BOARD_INFO to your board file or probe it * during runtime e.g on BeagleBoard using : * "echo tpm_i2c_infineon 0x20 > /sys/bus/i2c/devices/i2c-2/new_device" - fix possible endless loop if hardware misbehaves - improved return codes - consistent spelling i2c/tpm -> I2C/TPM - remove hardcoded sleep values and msleep usage - removed debug statements - added check for I2C functionality - renaming to tpm_i2c_infineon Updates since version 2.1.2: - added sysfs entries for duration and timeouts - updated to new tpm_do_selftest Updates since version 2.1.0: - improved error handling - implemented workarounds needed by the tpm - fixed typos References: [1] http://www.trustedcomputinggroup.org/resources/pc_client_work_group_pc_client_ specific_tpm_interface_specification_tis_version_12/ [2] http://trousers.sourceforge.net/ [3] http://sourceforge.net/projects/trousers/files/TSS%20API%20test%20suite/0.3/ Reviewed-by: Andi Shyti Acked-by: Marcel Selhorst Signed-off-by: Peter Huewe Signed-off-by: Bryan Freed Signed-off-by: Kent Yoder --- drivers/char/tpm/Kconfig | 11 + drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm_i2c_infineon.c | 695 ++++++++++++++++++++++++++++ 3 files changed, 707 insertions(+) create mode 100644 drivers/char/tpm/tpm_i2c_infineon.c diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index a048199ce866..c4aac486ade5 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -33,6 +33,17 @@ config TCG_TIS from within Linux. To compile this driver as a module, choose M here; the module will be called tpm_tis. +config TCG_TIS_I2C_INFINEON + tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)" + depends on I2C + ---help--- + If you have a TPM security chip that is compliant with the + TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack + Specification 0.20 say Yes and it will be accessible from within + Linux. + To compile this driver as a module, choose M here; the module + will be called tpm_tis_i2c_infineon. + config TCG_NSC tristate "National Semiconductor TPM Interface" depends on X86 diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index ea3a1e02a824..a9c3afc92dbc 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -6,6 +6,7 @@ ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o endif obj-$(CONFIG_TCG_TIS) += tpm_tis.o +obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c new file mode 100644 index 000000000000..5a831aec9d4b --- /dev/null +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -0,0 +1,695 @@ +/* + * Copyright (C) 2012 Infineon Technologies + * + * Authors: + * Peter Huewe + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This device driver implements the TPM interface as defined in + * the TCG TPM Interface Spec version 1.2, revision 1.0 and the + * Infineon I2C Protocol Stack Specification v0.20. + * + * It is based on the original tpm_tis device driver from Leendert van + * Dorn and Kyleen Hall. + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + * + */ +#include +#include +#include +#include +#include +#include "tpm.h" + +/* max. buffer size supported by our TPM */ +#define TPM_BUFSIZE 1260 + +/* max. number of iterations after I2C NAK */ +#define MAX_COUNT 3 + +#define SLEEP_DURATION_LOW 55 +#define SLEEP_DURATION_HI 65 + +/* max. number of iterations after I2C NAK for 'long' commands + * we need this especially for sending TPM_READY, since the cleanup after the + * transtion to the ready state may take some time, but it is unpredictable + * how long it will take. + */ +#define MAX_COUNT_LONG 50 + +#define SLEEP_DURATION_LONG_LOW 200 +#define SLEEP_DURATION_LONG_HI 220 + +/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */ +#define SLEEP_DURATION_RESET_LOW 2400 +#define SLEEP_DURATION_RESET_HI 2600 + +/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */ +#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000) +#define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000) + +/* expected value for DIDVID register */ +#define TPM_TIS_I2C_DID_VID 0x000b15d1L + +/* Structure to store I2C TPM specific stuff */ +struct tpm_inf_dev { + struct i2c_client *client; + u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */ + struct tpm_chip *chip; +}; + +static struct tpm_inf_dev tpm_dev; +static struct i2c_driver tpm_tis_i2c_driver; + +/* + * iic_tpm_read() - read from TPM register + * @addr: register address to read from + * @buffer: provided by caller + * @len: number of bytes to read + * + * Read len bytes from TPM register and put them into + * buffer (little-endian format, i.e. first byte is put into buffer[0]). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * NOTE: We can't unfortunately use the combined read/write functions + * provided by the i2c core as the TPM currently does not support the + * repeated start condition and due to it's special requirements. + * The i2c_smbus* functions do not work for this chip. + * + * Return -EIO on error, 0 on success. + */ +static int iic_tpm_read(u8 addr, u8 *buffer, size_t len) +{ + + struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr }; + struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer }; + + int rc; + int count; + + /* Lock the adapter for the duration of the whole sequence. */ + if (!tpm_dev.client->adapter->algo->master_xfer) + return -EOPNOTSUPP; + i2c_lock_adapter(tpm_dev.client->adapter); + + for (count = 0; count < MAX_COUNT; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); + if (rc > 0) + break; /* break here to skip sleep */ + + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + } + + if (rc <= 0) + goto out; + + /* After the TPM has successfully received the register address it needs + * some time, thus we're sleeping here again, before retrieving the data + */ + for (count = 0; count < MAX_COUNT; count++) { + usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI); + rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1); + if (rc > 0) + break; + + } + +out: + i2c_unlock_adapter(tpm_dev.client->adapter); + if (rc <= 0) + return -EIO; + + return 0; +} + +static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len, + unsigned int sleep_low, + unsigned int sleep_hi, u8 max_count) +{ + int rc = -EIO; + int count; + + struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf }; + + if (len > TPM_BUFSIZE) + return -EINVAL; + + if (!tpm_dev.client->adapter->algo->master_xfer) + return -EOPNOTSUPP; + i2c_lock_adapter(tpm_dev.client->adapter); + + /* prepend the 'register address' to the buffer */ + tpm_dev.buf[0] = addr; + memcpy(&(tpm_dev.buf[1]), buffer, len); + + /* + * NOTE: We have to use these special mechanisms here and unfortunately + * cannot rely on the standard behavior of i2c_transfer. + */ + for (count = 0; count < max_count; count++) { + rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1); + if (rc > 0) + break; + + usleep_range(sleep_low, sleep_hi); + } + + i2c_unlock_adapter(tpm_dev.client->adapter); + if (rc <= 0) + return -EIO; + + return 0; +} + +/* + * iic_tpm_write() - write to TPM register + * @addr: register address to write to + * @buffer: containing data to be written + * @len: number of bytes to write + * + * Write len bytes from provided buffer to TPM register (little + * endian format, i.e. buffer[0] is written as first byte). + * + * NOTE: TPM is big-endian for multi-byte values. Multi-byte + * values have to be swapped. + * + * NOTE: use this function instead of the iic_tpm_write_generic function. + * + * Return -EIO on error, 0 on success + */ +static int iic_tpm_write(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW, + SLEEP_DURATION_HI, MAX_COUNT); +} + +/* + * This function is needed especially for the cleanup situation after + * sending TPM_READY + * */ +static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len) +{ + return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW, + SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG); +} + +enum tis_access { + TPM_ACCESS_VALID = 0x80, + TPM_ACCESS_ACTIVE_LOCALITY = 0x20, + TPM_ACCESS_REQUEST_PENDING = 0x04, + TPM_ACCESS_REQUEST_USE = 0x02, +}; + +enum tis_status { + TPM_STS_VALID = 0x80, + TPM_STS_COMMAND_READY = 0x40, + TPM_STS_GO = 0x20, + TPM_STS_DATA_AVAIL = 0x10, + TPM_STS_DATA_EXPECT = 0x08, +}; + +enum tis_defaults { + TIS_SHORT_TIMEOUT = 750, /* ms */ + TIS_LONG_TIMEOUT = 2000, /* 2 sec */ +}; + +#define TPM_ACCESS(l) (0x0000 | ((l) << 4)) +#define TPM_STS(l) (0x0001 | ((l) << 4)) +#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4)) +#define TPM_DID_VID(l) (0x0006 | ((l) << 4)) + +static int check_locality(struct tpm_chip *chip, int loc) +{ + u8 buf; + int rc; + + rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1); + if (rc < 0) + return rc; + + if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) == + (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) { + chip->vendor.locality = loc; + return loc; + } + + return -EIO; +} + +/* implementation similar to tpm_tis */ +static void release_locality(struct tpm_chip *chip, int loc, int force) +{ + u8 buf; + if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0) + return; + + if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) == + (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) { + buf = TPM_ACCESS_ACTIVE_LOCALITY; + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + } +} + +static int request_locality(struct tpm_chip *chip, int loc) +{ + unsigned long stop; + u8 buf = TPM_ACCESS_REQUEST_USE; + + if (check_locality(chip, loc) >= 0) + return loc; + + iic_tpm_write(TPM_ACCESS(loc), &buf, 1); + + /* wait for burstcount */ + stop = jiffies + chip->vendor.timeout_a; + do { + if (check_locality(chip, loc) >= 0) + return loc; + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + } while (time_before(jiffies, stop)); + + return -ETIME; +} + +static u8 tpm_tis_i2c_status(struct tpm_chip *chip) +{ + /* NOTE: since I2C read may fail, return 0 in this case --> time-out */ + u8 buf; + if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0) + return 0; + else + return buf; +} + +static void tpm_tis_i2c_ready(struct tpm_chip *chip) +{ + /* this causes the current command to be aborted */ + u8 buf = TPM_STS_COMMAND_READY; + iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1); +} + +static ssize_t get_burstcount(struct tpm_chip *chip) +{ + unsigned long stop; + ssize_t burstcnt; + u8 buf[3]; + + /* wait for burstcount */ + /* which timeout value, spec has 2 answers (c & d) */ + stop = jiffies + chip->vendor.timeout_d; + do { + /* Note: STS is little endian */ + if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0) + burstcnt = 0; + else + burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0]; + + if (burstcnt) + return burstcnt; + + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + } while (time_before(jiffies, stop)); + return -EBUSY; +} + +static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, + int *status) +{ + unsigned long stop; + + /* check current status */ + *status = tpm_tis_i2c_status(chip); + if ((*status & mask) == mask) + return 0; + + stop = jiffies + timeout; + do { + /* since we just checked the status, give the TPM some time */ + usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI); + *status = tpm_tis_i2c_status(chip); + if ((*status & mask) == mask) + return 0; + + } while (time_before(jiffies, stop)); + + return -ETIME; +} + +static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) +{ + size_t size = 0; + ssize_t burstcnt; + u8 retries = 0; + int rc; + + while (size < count) { + burstcnt = get_burstcount(chip); + + /* burstcnt < 0 = TPM is busy */ + if (burstcnt < 0) + return burstcnt; + + /* limit received data to max. left */ + if (burstcnt > (count - size)) + burstcnt = count - size; + + rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[size]), burstcnt); + if (rc == 0) + size += burstcnt; + else if (rc < 0) + retries++; + + /* avoid endless loop in case of broken HW */ + if (retries > MAX_COUNT_LONG) + return -EIO; + + } + return size; +} + +static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + int size = 0; + int expected, status; + + if (count < TPM_HEADER_SIZE) { + size = -EIO; + goto out; + } + + /* read first 10 bytes, including tag, paramsize, and result */ + size = recv_data(chip, buf, TPM_HEADER_SIZE); + if (size < TPM_HEADER_SIZE) { + dev_err(chip->dev, "Unable to read header\n"); + goto out; + } + + expected = be32_to_cpu(*(__be32 *)(buf + 2)); + if ((size_t) expected > count) { + size = -EIO; + goto out; + } + + size += recv_data(chip, &buf[TPM_HEADER_SIZE], + expected - TPM_HEADER_SIZE); + if (size < expected) { + dev_err(chip->dev, "Unable to read remainder of result\n"); + size = -ETIME; + goto out; + } + + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if (status & TPM_STS_DATA_AVAIL) { /* retry? */ + dev_err(chip->dev, "Error left over data\n"); + size = -EIO; + goto out; + } + +out: + tpm_tis_i2c_ready(chip); + /* The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); + release_locality(chip, chip->vendor.locality, 0); + return size; +} + +static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len) +{ + int rc, status; + ssize_t burstcnt; + size_t count = 0; + u8 retries = 0; + u8 sts = TPM_STS_GO; + + if (len > TPM_BUFSIZE) + return -E2BIG; /* command is too long for our tpm, sorry */ + + if (request_locality(chip, 0) < 0) + return -EBUSY; + + status = tpm_tis_i2c_status(chip); + if ((status & TPM_STS_COMMAND_READY) == 0) { + tpm_tis_i2c_ready(chip); + if (wait_for_stat + (chip, TPM_STS_COMMAND_READY, + chip->vendor.timeout_b, &status) < 0) { + rc = -ETIME; + goto out_err; + } + } + + while (count < len - 1) { + burstcnt = get_burstcount(chip); + + /* burstcnt < 0 = TPM is busy */ + if (burstcnt < 0) + return burstcnt; + + if (burstcnt > (len - 1 - count)) + burstcnt = len - 1 - count; + + rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), + &(buf[count]), burstcnt); + if (rc == 0) + count += burstcnt; + else if (rc < 0) + retries++; + + /* avoid endless loop in case of broken HW */ + if (retries > MAX_COUNT_LONG) { + rc = -EIO; + goto out_err; + } + + wait_for_stat(chip, TPM_STS_VALID, + chip->vendor.timeout_c, &status); + + if ((status & TPM_STS_DATA_EXPECT) == 0) { + rc = -EIO; + goto out_err; + } + + } + + /* write last byte */ + iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1); + wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status); + if ((status & TPM_STS_DATA_EXPECT) != 0) { + rc = -EIO; + goto out_err; + } + + /* go and do it */ + iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1); + + return len; +out_err: + tpm_tis_i2c_ready(chip); + /* The TPM needs some time to clean up here, + * so we sleep rather than keeping the bus busy + */ + usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI); + release_locality(chip, chip->vendor.locality, 0); + return rc; +} + +static const struct file_operations tis_ops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpm_open, + .read = tpm_read, + .write = tpm_write, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); +static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL); +static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL); + +static struct attribute *tis_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, + &dev_attr_durations.attr, + &dev_attr_timeouts.attr, + NULL, +}; + +static struct attribute_group tis_attr_grp = { + .attrs = tis_attrs +}; + +static struct tpm_vendor_specific tpm_tis_i2c = { + .status = tpm_tis_i2c_status, + .recv = tpm_tis_i2c_recv, + .send = tpm_tis_i2c_send, + .cancel = tpm_tis_i2c_ready, + .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, + .req_canceled = TPM_STS_COMMAND_READY, + .attr_group = &tis_attr_grp, + .miscdev.fops = &tis_ops, +}; + +static int __devinit tpm_tis_i2c_init(struct device *dev) +{ + u32 vendor; + int rc = 0; + struct tpm_chip *chip; + + chip = tpm_register_hardware(dev, &tpm_tis_i2c); + if (!chip) { + rc = -ENODEV; + goto out_err; + } + + /* Disable interrupts */ + chip->vendor.irq = 0; + + /* Default timeouts */ + chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT); + chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT); + + if (request_locality(chip, 0) != 0) { + rc = -ENODEV; + goto out_vendor; + } + + /* read four bytes from DID_VID register */ + if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) { + rc = -EIO; + goto out_release; + } + + /* create DID_VID register value, after swapping to little-endian */ + vendor = be32_to_cpu((__be32) vendor); + + if (vendor != TPM_TIS_I2C_DID_VID) { + rc = -ENODEV; + goto out_release; + } + + dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16); + + INIT_LIST_HEAD(&chip->vendor.list); + tpm_dev.chip = chip; + + tpm_get_timeouts(chip); + tpm_do_selftest(chip); + + return 0; + +out_release: + release_locality(chip, chip->vendor.locality, 1); + +out_vendor: + /* close file handles */ + tpm_dev_vendor_release(chip); + + /* remove hardware */ + tpm_remove_hardware(chip->dev); + + /* reset these pointers, otherwise we oops */ + chip->dev->release = NULL; + chip->release = NULL; + tpm_dev.client = NULL; + dev_set_drvdata(chip->dev, chip); +out_err: + return rc; +} + +static const struct i2c_device_id tpm_tis_i2c_table[] = { + {"tpm_i2c_infineon", 0}, + {}, +}; + +MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table); +static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume); + +static int __devinit tpm_tis_i2c_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + int rc; + if (tpm_dev.client != NULL) + return -EBUSY; /* We only support one client */ + + if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, + "no algorithms associated to the i2c bus\n"); + return -ENODEV; + } + + client->driver = &tpm_tis_i2c_driver; + tpm_dev.client = client; + rc = tpm_tis_i2c_init(&client->dev); + if (rc != 0) { + client->driver = NULL; + tpm_dev.client = NULL; + rc = -ENODEV; + } + return rc; +} + +static int __devexit tpm_tis_i2c_remove(struct i2c_client *client) +{ + struct tpm_chip *chip = tpm_dev.chip; + release_locality(chip, chip->vendor.locality, 1); + + /* close file handles */ + tpm_dev_vendor_release(chip); + + /* remove hardware */ + tpm_remove_hardware(chip->dev); + + /* reset these pointers, otherwise we oops */ + chip->dev->release = NULL; + chip->release = NULL; + tpm_dev.client = NULL; + dev_set_drvdata(chip->dev, chip); + + return 0; +} + +static struct i2c_driver tpm_tis_i2c_driver = { + + .id_table = tpm_tis_i2c_table, + .probe = tpm_tis_i2c_probe, + .remove = tpm_tis_i2c_remove, + .driver = { + .name = "tpm_i2c_infineon", + .owner = THIS_MODULE, + .pm = &tpm_tis_i2c_ops, + }, +}; + +module_i2c_driver(tpm_tis_i2c_driver); +MODULE_AUTHOR("Peter Huewe "); +MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver"); +MODULE_VERSION("2.1.5"); +MODULE_LICENSE("GPL"); From e5dcd87fee12ed64a9ea911102025facc0c7d10c Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 11 Jul 2012 10:08:12 -0500 Subject: [PATCH 04/39] tpm: modularize event log collection Break ACPI-specific pieces of the event log handling into their own file and create tpm_eventlog.[ch] to store common event log handling code. This will be required to integrate future event log sources on platforms without ACPI tables. Signed-off-by: Kent Yoder --- drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm.c | 1 + drivers/char/tpm/tpm_acpi.c | 104 +++++++++++++ .../char/tpm/{tpm_bios.c => tpm_eventlog.c} | 147 +----------------- drivers/char/tpm/tpm_eventlog.h | 71 +++++++++ 5 files changed, 182 insertions(+), 142 deletions(-) create mode 100644 drivers/char/tpm/tpm_acpi.c rename drivers/char/tpm/{tpm_bios.c => tpm_eventlog.c} (75%) create mode 100644 drivers/char/tpm/tpm_eventlog.h diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index a9c3afc92dbc..beac52f61a82 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o + tpm_bios-objs += tpm_eventlog.o tpm_acpi.o endif obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 817f0ee202b6..677c6e26593f 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -30,6 +30,7 @@ #include #include "tpm.h" +#include "tpm_eventlog.h" enum tpm_const { TPM_MINOR = 224, /* officially assigned */ diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c new file mode 100644 index 000000000000..a1bb5a182df9 --- /dev/null +++ b/drivers/char/tpm/tpm_acpi.c @@ -0,0 +1,104 @@ +/* + * Copyright (C) 2005 IBM Corporation + * + * Authors: + * Seiji Munetoh + * Stefan Berger + * Reiner Sailer + * Kylene Hall + * + * Maintained by: + * + * Access to the eventlog extended by the TCG BIOS of PC platform + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include +#include +#include +#include +#include + +#include "tpm.h" +#include "tpm_eventlog.h" + +struct acpi_tcpa { + struct acpi_table_header hdr; + u16 platform_class; + union { + struct client_hdr { + u32 log_max_len __attribute__ ((packed)); + u64 log_start_addr __attribute__ ((packed)); + } client; + struct server_hdr { + u16 reserved; + u64 log_max_len __attribute__ ((packed)); + u64 log_start_addr __attribute__ ((packed)); + } server; + }; +}; + +/* read binary bios log */ +int read_log(struct tpm_bios_log *log) +{ + struct acpi_tcpa *buff; + acpi_status status; + struct acpi_table_header *virt; + u64 len, start; + + if (log->bios_event_log != NULL) { + printk(KERN_ERR + "%s: ERROR - Eventlog already initialized\n", + __func__); + return -EFAULT; + } + + /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ + status = acpi_get_table(ACPI_SIG_TCPA, 1, + (struct acpi_table_header **)&buff); + + if (ACPI_FAILURE(status)) { + printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", + __func__); + return -EIO; + } + + switch(buff->platform_class) { + case BIOS_SERVER: + len = buff->server.log_max_len; + start = buff->server.log_start_addr; + break; + case BIOS_CLIENT: + default: + len = buff->client.log_max_len; + start = buff->client.log_start_addr; + break; + } + if (!len) { + printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); + return -EIO; + } + + /* malloc EventLog space */ + log->bios_event_log = kmalloc(len, GFP_KERNEL); + if (!log->bios_event_log) { + printk("%s: ERROR - Not enough Memory for BIOS measurements\n", + __func__); + return -ENOMEM; + } + + log->bios_event_log_end = log->bios_event_log + len; + + virt = acpi_os_map_memory(start, len); + + memcpy(log->bios_event_log, virt, len); + + acpi_os_unmap_memory(virt, len); + return 0; +} diff --git a/drivers/char/tpm/tpm_bios.c b/drivers/char/tpm/tpm_eventlog.c similarity index 75% rename from drivers/char/tpm/tpm_bios.c rename to drivers/char/tpm/tpm_eventlog.c index 0636520fa9bf..84ddc557b8f8 100644 --- a/drivers/char/tpm/tpm_bios.c +++ b/drivers/char/tpm/tpm_eventlog.c @@ -1,7 +1,8 @@ /* - * Copyright (C) 2005 IBM Corporation + * Copyright (C) 2005, 2012 IBM Corporation * * Authors: + * Kent Yoder * Seiji Munetoh * Stefan Berger * Reiner Sailer @@ -9,7 +10,7 @@ * * Maintained by: * - * Access to the eventlog extended by the TCG BIOS of PC platform + * Access to the eventlog created by a system's firmware / BIOS * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License @@ -23,67 +24,10 @@ #include #include #include -#include + #include "tpm.h" +#include "tpm_eventlog.h" -#define TCG_EVENT_NAME_LEN_MAX 255 -#define MAX_TEXT_EVENT 1000 /* Max event string length */ -#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ - -enum bios_platform_class { - BIOS_CLIENT = 0x00, - BIOS_SERVER = 0x01, -}; - -struct tpm_bios_log { - void *bios_event_log; - void *bios_event_log_end; -}; - -struct acpi_tcpa { - struct acpi_table_header hdr; - u16 platform_class; - union { - struct client_hdr { - u32 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); - } client; - struct server_hdr { - u16 reserved; - u64 log_max_len __attribute__ ((packed)); - u64 log_start_addr __attribute__ ((packed)); - } server; - }; -}; - -struct tcpa_event { - u32 pcr_index; - u32 event_type; - u8 pcr_value[20]; /* SHA1 */ - u32 event_size; - u8 event_data[0]; -}; - -enum tcpa_event_types { - PREBOOT = 0, - POST_CODE, - UNUSED, - NO_ACTION, - SEPARATOR, - ACTION, - EVENT_TAG, - SCRTM_CONTENTS, - SCRTM_VERSION, - CPU_MICROCODE, - PLATFORM_CONFIG_FLAGS, - TABLE_OF_DEVICES, - COMPACT_HASH, - IPL, - IPL_PARTITION_DATA, - NONHOST_CODE, - NONHOST_CONFIG, - NONHOST_INFO, -}; static const char* tcpa_event_type_strings[] = { "PREBOOT", @@ -106,28 +50,6 @@ static const char* tcpa_event_type_strings[] = { "Non-Host Info" }; -struct tcpa_pc_event { - u32 event_id; - u32 event_size; - u8 event_data[0]; -}; - -enum tcpa_pc_event_ids { - SMBIOS = 1, - BIS_CERT, - POST_BIOS_ROM, - ESCD, - CMOS, - NVRAM, - OPTION_ROM_EXEC, - OPTION_ROM_CONFIG, - OPTION_ROM_MICROCODE = 10, - S_CRTM_VERSION, - S_CRTM_CONTENTS, - POST_CONTENTS, - HOST_TABLE_OF_DEVICES, -}; - static const char* tcpa_pc_event_id_strings[] = { "", "SMBIOS", @@ -358,65 +280,6 @@ static const struct seq_operations tpm_binary_b_measurments_seqops = { .show = tpm_binary_bios_measurements_show, }; -/* read binary bios log */ -static int read_log(struct tpm_bios_log *log) -{ - struct acpi_tcpa *buff; - acpi_status status; - struct acpi_table_header *virt; - u64 len, start; - - if (log->bios_event_log != NULL) { - printk(KERN_ERR - "%s: ERROR - Eventlog already initialized\n", - __func__); - return -EFAULT; - } - - /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ - status = acpi_get_table(ACPI_SIG_TCPA, 1, - (struct acpi_table_header **)&buff); - - if (ACPI_FAILURE(status)) { - printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", - __func__); - return -EIO; - } - - switch(buff->platform_class) { - case BIOS_SERVER: - len = buff->server.log_max_len; - start = buff->server.log_start_addr; - break; - case BIOS_CLIENT: - default: - len = buff->client.log_max_len; - start = buff->client.log_start_addr; - break; - } - if (!len) { - printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); - return -EIO; - } - - /* malloc EventLog space */ - log->bios_event_log = kmalloc(len, GFP_KERNEL); - if (!log->bios_event_log) { - printk("%s: ERROR - Not enough Memory for BIOS measurements\n", - __func__); - return -ENOMEM; - } - - log->bios_event_log_end = log->bios_event_log + len; - - virt = acpi_os_map_memory(start, len); - - memcpy(log->bios_event_log, virt, len); - - acpi_os_unmap_memory(virt, len); - return 0; -} - static int tpm_ascii_bios_measurements_open(struct inode *inode, struct file *file) { diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h new file mode 100644 index 000000000000..8e23ccdf8a83 --- /dev/null +++ b/drivers/char/tpm/tpm_eventlog.h @@ -0,0 +1,71 @@ + +#ifndef __TPM_EVENTLOG_H__ +#define __TPM_EVENTLOG_H__ + +#define TCG_EVENT_NAME_LEN_MAX 255 +#define MAX_TEXT_EVENT 1000 /* Max event string length */ +#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ + +enum bios_platform_class { + BIOS_CLIENT = 0x00, + BIOS_SERVER = 0x01, +}; + +struct tpm_bios_log { + void *bios_event_log; + void *bios_event_log_end; +}; + +struct tcpa_event { + u32 pcr_index; + u32 event_type; + u8 pcr_value[20]; /* SHA1 */ + u32 event_size; + u8 event_data[0]; +}; + +enum tcpa_event_types { + PREBOOT = 0, + POST_CODE, + UNUSED, + NO_ACTION, + SEPARATOR, + ACTION, + EVENT_TAG, + SCRTM_CONTENTS, + SCRTM_VERSION, + CPU_MICROCODE, + PLATFORM_CONFIG_FLAGS, + TABLE_OF_DEVICES, + COMPACT_HASH, + IPL, + IPL_PARTITION_DATA, + NONHOST_CODE, + NONHOST_CONFIG, + NONHOST_INFO, +}; + +struct tcpa_pc_event { + u32 event_id; + u32 event_size; + u8 event_data[0]; +}; + +enum tcpa_pc_event_ids { + SMBIOS = 1, + BIS_CERT, + POST_BIOS_ROM, + ESCD, + CMOS, + NVRAM, + OPTION_ROM_EXEC, + OPTION_ROM_CONFIG, + OPTION_ROM_MICROCODE = 10, + S_CRTM_VERSION, + S_CRTM_CONTENTS, + POST_CONTENTS, + HOST_TABLE_OF_DEVICES, +}; + +int read_log(struct tpm_bios_log *log); +#endif From 41ab999c80f1d368f32a2554ba8f44feff26f54d Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Thu, 7 Jun 2012 13:47:14 -0500 Subject: [PATCH 05/39] tpm: Move tpm_get_random api into the TPM device driver Move the tpm_get_random api from the trusted keys code into the TPM device driver itself so that other callers can make use of it. Also, change the api slightly so that the number of bytes read is returned in the call, since the TPM command can potentially return fewer bytes than requested. Acked-by: David Safford Reviewed-by: H. Peter Anvin Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 59 ++++++++++++++++++++++++++++++++++++----- drivers/char/tpm/tpm.h | 23 ++++++++++++++++ include/linux/tpm.h | 4 +++ security/keys/trusted.c | 54 +++++++++---------------------------- 4 files changed, 92 insertions(+), 48 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 677c6e26593f..36e43e50dcef 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -32,12 +32,6 @@ #include "tpm.h" #include "tpm_eventlog.h" -enum tpm_const { - TPM_MINOR = 224, /* officially assigned */ - TPM_BUFSIZE = 4096, - TPM_NUM_DEVICES = 256, -}; - enum tpm_duration { TPM_SHORT = 0, TPM_MEDIUM = 1, @@ -483,6 +477,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, #define TPM_INTERNAL_RESULT_SIZE 200 #define TPM_TAG_RQU_COMMAND cpu_to_be16(193) #define TPM_ORD_GET_CAP cpu_to_be32(101) +#define TPM_ORD_GET_RANDOM cpu_to_be32(70) static const struct tpm_input_header tpm_getcap_header = { .tag = TPM_TAG_RQU_COMMAND, @@ -1327,6 +1322,58 @@ int tpm_pm_resume(struct device *dev) } EXPORT_SYMBOL_GPL(tpm_pm_resume); +#define TPM_GETRANDOM_RESULT_SIZE 18 +static struct tpm_input_header tpm_getrandom_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(14), + .ordinal = TPM_ORD_GET_RANDOM +}; + +/** + * tpm_get_random() - Get random bytes from the tpm's RNG + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @out: destination buffer for the random bytes + * @max: the max number of bytes to write to @out + * + * Returns < 0 on error and the number of bytes read on success + */ +int tpm_get_random(u32 chip_num, u8 *out, size_t max) +{ + struct tpm_chip *chip; + struct tpm_cmd_t tpm_cmd; + u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA); + int err, total = 0, retries = 5; + u8 *dest = out; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL) + return -ENODEV; + + if (!out || !num_bytes || max > TPM_MAX_RNG_DATA) + return -EINVAL; + + do { + tpm_cmd.header.in = tpm_getrandom_header; + tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes); + + err = transmit_cmd(chip, &tpm_cmd, + TPM_GETRANDOM_RESULT_SIZE + num_bytes, + "attempting get random"); + if (err) + break; + + recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len); + memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd); + + dest += recd; + total += recd; + num_bytes -= recd; + } while (retries-- && total < max); + + return total ? total : -EIO; +} +EXPORT_SYMBOL_GPL(tpm_get_random); + /* In case vendor provided release function, call it too.*/ void tpm_dev_vendor_release(struct tpm_chip *chip) diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 917f727e6740..645136eea890 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -28,6 +28,12 @@ #include #include +enum tpm_const { + TPM_MINOR = 224, /* officially assigned */ + TPM_BUFSIZE = 4096, + TPM_NUM_DEVICES = 256, +}; + enum tpm_timeout { TPM_TIMEOUT = 5, /* msecs */ }; @@ -269,6 +275,21 @@ struct tpm_pcrextend_in { u8 hash[TPM_DIGEST_SIZE]; }__attribute__((packed)); +/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18 + * bytes, but 128 is still a relatively large number of random bytes and + * anything much bigger causes users of struct tpm_cmd_t to start getting + * compiler warnings about stack frame size. */ +#define TPM_MAX_RNG_DATA 128 + +struct tpm_getrandom_out { + __be32 rng_data_len; + u8 rng_data[TPM_MAX_RNG_DATA]; +}__attribute__((packed)); + +struct tpm_getrandom_in { + __be32 num_bytes; +}__attribute__((packed)); + typedef union { struct tpm_getcap_params_out getcap_out; struct tpm_readpubek_params_out readpubek_out; @@ -277,6 +298,8 @@ typedef union { struct tpm_pcrread_in pcrread_in; struct tpm_pcrread_out pcrread_out; struct tpm_pcrextend_in pcrextend_in; + struct tpm_getrandom_in getrandom_in; + struct tpm_getrandom_out getrandom_out; } tpm_cmd_params; struct tpm_cmd_t { diff --git a/include/linux/tpm.h b/include/linux/tpm.h index fdc718abf83b..fcb627ff8d3e 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -32,6 +32,7 @@ extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf); extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash); extern int tpm_send(u32 chip_num, void *cmd, size_t buflen); +extern int tpm_get_random(u32 chip_num, u8 *data, size_t max); #else static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) { return -ENODEV; @@ -42,5 +43,8 @@ static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) { static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) { return -ENODEV; } +static inline int tpm_get_random(u32 chip_num, u8 *data, size_t max) { + return -ENODEV; +} #endif #endif diff --git a/security/keys/trusted.c b/security/keys/trusted.c index 2d5d041f2049..3f163d0489ad 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -368,38 +368,6 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd, return rc; } -/* - * get a random value from TPM - */ -static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len) -{ - int ret; - - INIT_BUF(tb); - store16(tb, TPM_TAG_RQU_COMMAND); - store32(tb, TPM_GETRANDOM_SIZE); - store32(tb, TPM_ORD_GETRANDOM); - store32(tb, len); - ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data); - if (!ret) - memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len); - return ret; -} - -static int my_get_random(unsigned char *buf, int len) -{ - struct tpm_buf *tb; - int ret; - - tb = kmalloc(sizeof *tb, GFP_KERNEL); - if (!tb) - return -ENOMEM; - ret = tpm_get_random(tb, buf, len); - - kfree(tb); - return ret; -} - /* * Lock a trusted key, by extending a selected PCR. * @@ -413,8 +381,8 @@ static int pcrlock(const int pcrnum) if (!capable(CAP_SYS_ADMIN)) return -EPERM; - ret = my_get_random(hash, SHA1_DIGEST_SIZE); - if (ret < 0) + ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE); + if (ret != SHA1_DIGEST_SIZE) return ret; return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0; } @@ -429,8 +397,8 @@ static int osap(struct tpm_buf *tb, struct osapsess *s, unsigned char ononce[TPM_NONCE_SIZE]; int ret; - ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE); - if (ret < 0) + ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE); + if (ret != TPM_NONCE_SIZE) return ret; INIT_BUF(tb); @@ -524,8 +492,8 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype, if (ret < 0) goto out; - ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE); - if (ret < 0) + ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE); + if (ret != TPM_NONCE_SIZE) goto out; ordinal = htonl(TPM_ORD_SEAL); datsize = htonl(datalen); @@ -634,8 +602,8 @@ static int tpm_unseal(struct tpm_buf *tb, ordinal = htonl(TPM_ORD_UNSEAL); keyhndl = htonl(SRKHANDLE); - ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE); - if (ret < 0) { + ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE); + if (ret != TPM_NONCE_SIZE) { pr_info("trusted_key: tpm_get_random failed (%d)\n", ret); return ret; } @@ -935,6 +903,7 @@ static int trusted_instantiate(struct key *key, const void *data, char *datablob; int ret = 0; int key_cmd; + size_t key_len; if (datalen <= 0 || datalen > 32767 || !data) return -EINVAL; @@ -974,8 +943,9 @@ static int trusted_instantiate(struct key *key, const void *data, pr_info("trusted_key: key_unseal failed (%d)\n", ret); break; case Opt_new: - ret = my_get_random(payload->key, payload->key_len); - if (ret < 0) { + key_len = payload->key_len; + ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len); + if (ret != key_len) { pr_info("trusted_key: key_create failed (%d)\n", ret); goto out; } From 578b016fdc91464c08c096f0c5952cae549fdb8f Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 8 Aug 2012 14:18:57 -0500 Subject: [PATCH 06/39] hw_random: add support for the TPM chip as a hardware RNG source This driver will make use of any available TPM chip on the system as a hwrng source. Acked-by: David Safford Signed-off-by: Kent Yoder --- drivers/char/hw_random/Kconfig | 13 +++++++++ drivers/char/hw_random/Makefile | 1 + drivers/char/hw_random/tpm-rng.c | 50 ++++++++++++++++++++++++++++++++ 3 files changed, 64 insertions(+) create mode 100644 drivers/char/hw_random/tpm-rng.c diff --git a/drivers/char/hw_random/Kconfig b/drivers/char/hw_random/Kconfig index 7c0d391996b5..fbd9b2b850ef 100644 --- a/drivers/char/hw_random/Kconfig +++ b/drivers/char/hw_random/Kconfig @@ -289,3 +289,16 @@ config HW_RANDOM_EXYNOS module will be called exynos-rng. If unsure, say Y. + +config HW_RANDOM_TPM + tristate "TPM HW Random Number Generator support" + depends on HW_RANDOM && TCG_TPM + default HW_RANDOM + ---help--- + This driver provides kernel-side support for the Random Number + Generator in the Trusted Platform Module + + To compile this driver as a module, choose M here: the + module will be called tpm-rng. + + If unsure, say Y. diff --git a/drivers/char/hw_random/Makefile b/drivers/char/hw_random/Makefile index 39a757ca15b6..1fd7eec9fbf6 100644 --- a/drivers/char/hw_random/Makefile +++ b/drivers/char/hw_random/Makefile @@ -25,3 +25,4 @@ obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o +obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o diff --git a/drivers/char/hw_random/tpm-rng.c b/drivers/char/hw_random/tpm-rng.c new file mode 100644 index 000000000000..d6d448266f07 --- /dev/null +++ b/drivers/char/hw_random/tpm-rng.c @@ -0,0 +1,50 @@ +/* + * Copyright (C) 2012 Kent Yoder IBM Corporation + * + * HWRNG interfaces to pull RNG data from a TPM + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include +#include + +#define MODULE_NAME "tpm-rng" + +static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait) +{ + return tpm_get_random(TPM_ANY_NUM, data, max); +} + +static struct hwrng tpm_rng = { + .name = MODULE_NAME, + .read = tpm_rng_read, +}; + +static int __init rng_init(void) +{ + return hwrng_register(&tpm_rng); +} +module_init(rng_init); + +static void __exit rng_exit(void) +{ + hwrng_unregister(&tpm_rng); +} +module_exit(rng_exit); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Kent Yoder "); +MODULE_DESCRIPTION("RNG driver for TPM devices"); From dd7da132f7f04f34074efd134847a818ea29ddd7 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 25 Jul 2012 14:14:02 -0500 Subject: [PATCH 07/39] tpm: fix double write race and tpm_release free issue Moved the atomic_set of the data_pending variable until after the tpm_read has completed processing. The existing code had a window of time where a second write to the driver could clobber the tpm command buffer. Also fixed an issue where if close was called on the tpm device before a read completed, the tpm command buffer would be returned to the OS, which could contain sensitive information. Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 36e43e50dcef..0a75638e3e56 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1171,7 +1171,7 @@ int tpm_release(struct inode *inode, struct file *file) flush_work_sync(&chip->work); file->private_data = NULL; atomic_set(&chip->data_pending, 0); - kfree(chip->data_buffer); + kzfree(chip->data_buffer); clear_bit(0, &chip->is_open); put_device(chip->dev); return 0; @@ -1223,7 +1223,6 @@ ssize_t tpm_read(struct file *file, char __user *buf, del_singleshot_timer_sync(&chip->user_read_timer); flush_work_sync(&chip->work); ret_size = atomic_read(&chip->data_pending); - atomic_set(&chip->data_pending, 0); if (ret_size > 0) { /* relay data */ ssize_t orig_ret_size = ret_size; if (size < ret_size) @@ -1238,6 +1237,8 @@ ssize_t tpm_read(struct file *file, char __user *buf, mutex_unlock(&chip->buffer_mutex); } + atomic_set(&chip->data_pending, 0); + return ret_size; } EXPORT_SYMBOL_GPL(tpm_read); From 7e72fe73bfc7e4219b8dd212026c7113f4e37f91 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Thu, 9 Aug 2012 09:20:15 -0500 Subject: [PATCH 08/39] tpm: compile out unused code in the PNP and PM cases The tpm_tis driver doesn't use tpm_tis_resume except when PM is configured and doesn't make use of tpm_tis_reenable_interrupts except when PM or PNP is configured. Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_tis.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index c4be3519a587..6bdf2671254f 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -705,6 +705,7 @@ out_err: return rc; } +#if defined(CONFIG_PNP) || defined(CONFIG_PM_SLEEP) static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { u32 intmask; @@ -725,7 +726,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) iowrite32(intmask, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); } - +#endif #ifdef CONFIG_PNP static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev, From 132f7629474424418a5cdd666796ad3cfa4dc0c5 Mon Sep 17 00:00:00 2001 From: Ashley Lai Date: Wed, 22 Aug 2012 16:17:43 -0500 Subject: [PATCH 09/39] drivers/char/tpm: Add new device driver to support IBM vTPM This patch adds a new device driver to support IBM virtual TPM (vTPM) for PPC64. IBM vTPM is supported through the adjunct partition with firmware release 740 or higher. With vTPM support, each lpar is able to have its own vTPM without the physical TPM hardware. This driver provides TPM functionalities by communicating with the vTPM adjunct partition through Hypervisor calls (Hcalls) and Command/Response Queue (CRQ) commands. Signed-off-by: Ashley Lai Signed-off-by: Kent Yoder --- drivers/char/tpm/Kconfig | 8 + drivers/char/tpm/Makefile | 1 + drivers/char/tpm/tpm.h | 1 + drivers/char/tpm/tpm_ibmvtpm.c | 749 +++++++++++++++++++++++++++++++++ drivers/char/tpm/tpm_ibmvtpm.h | 77 ++++ 5 files changed, 836 insertions(+) create mode 100644 drivers/char/tpm/tpm_ibmvtpm.c create mode 100644 drivers/char/tpm/tpm_ibmvtpm.h diff --git a/drivers/char/tpm/Kconfig b/drivers/char/tpm/Kconfig index c4aac486ade5..915875e431d2 100644 --- a/drivers/char/tpm/Kconfig +++ b/drivers/char/tpm/Kconfig @@ -73,4 +73,12 @@ config TCG_INFINEON Further information on this driver and the supported hardware can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/ +config TCG_IBMVTPM + tristate "IBM VTPM Interface" + depends on PPC64 + ---help--- + If you have IBM virtual TPM (VTPM) support say Yes and it + will be accessible from within Linux. To compile this driver + as a module, choose M here; the module will be called tpm_ibmvtpm. + endif # TCG_TPM diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index beac52f61a82..547509d02046 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -11,3 +11,4 @@ obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o obj-$(CONFIG_TCG_NSC) += tpm_nsc.o obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o +obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 645136eea890..870fde7459c5 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -100,6 +100,7 @@ struct tpm_vendor_specific { bool timeout_adjusted; unsigned long duration[3]; /* jiffies */ bool duration_adjusted; + void *data; wait_queue_head_t read_queue; wait_queue_head_t int_queue; diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c new file mode 100644 index 000000000000..efc4ab36a9d6 --- /dev/null +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -0,0 +1,749 @@ +/* + * Copyright (C) 2012 IBM Corporation + * + * Author: Ashley Lai + * + * Maintained by: + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tpm.h" +#include "tpm_ibmvtpm.h" + +static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm"; + +static struct vio_device_id tpm_ibmvtpm_device_table[] __devinitdata = { + { "IBM,vtpm", "IBM,vtpm"}, + { "", "" } +}; +MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table); + +DECLARE_WAIT_QUEUE_HEAD(wq); + +/** + * ibmvtpm_send_crq - Send a CRQ request + * @vdev: vio device struct + * @w1: first word + * @w2: second word + * + * Return value: + * 0 -Sucess + * Non-zero - Failure + */ +static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2) +{ + return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, w1, w2); +} + +/** + * ibmvtpm_get_data - Retrieve ibm vtpm data + * @dev: device struct + * + * Return value: + * vtpm device struct + */ +static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip) + return (struct ibmvtpm_dev *)chip->vendor.data; + return NULL; +} + +/** + * tpm_ibmvtpm_recv - Receive data after send + * @chip: tpm chip struct + * @buf: buffer to read + * count: size of buffer + * + * Return value: + * Number of bytes read + */ +static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct ibmvtpm_dev *ibmvtpm; + u16 len; + + ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data; + + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); + return 0; + } + + wait_event_interruptible(wq, ibmvtpm->crq_res.len != 0); + + if (count < ibmvtpm->crq_res.len) { + dev_err(ibmvtpm->dev, + "Invalid size in recv: count=%ld, crq_size=%d\n", + count, ibmvtpm->crq_res.len); + return -EIO; + } + + spin_lock(&ibmvtpm->rtce_lock); + memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, ibmvtpm->crq_res.len); + memset(ibmvtpm->rtce_buf, 0, ibmvtpm->crq_res.len); + ibmvtpm->crq_res.valid = 0; + ibmvtpm->crq_res.msg = 0; + len = ibmvtpm->crq_res.len; + ibmvtpm->crq_res.len = 0; + spin_unlock(&ibmvtpm->rtce_lock); + return len; +} + +/** + * tpm_ibmvtpm_send - Send tpm request + * @chip: tpm chip struct + * @buf: buffer contains data to send + * count: size of buffer + * + * Return value: + * Number of bytes sent + */ +static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count) +{ + struct ibmvtpm_dev *ibmvtpm; + struct ibmvtpm_crq crq; + u64 *word = (u64 *) &crq; + int rc; + + ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data; + + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n"); + return 0; + } + + if (count > ibmvtpm->rtce_size) { + dev_err(ibmvtpm->dev, + "Invalid size in send: count=%ld, rtce_size=%d\n", + count, ibmvtpm->rtce_size); + return -EIO; + } + + spin_lock(&ibmvtpm->rtce_lock); + memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count); + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_TPM_COMMAND; + crq.len = (u16)count; + crq.data = ibmvtpm->rtce_dma_handle; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]); + if (rc != H_SUCCESS) { + dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc); + rc = 0; + } else + rc = count; + + spin_unlock(&ibmvtpm->rtce_lock); + return rc; +} + +static void tpm_ibmvtpm_cancel(struct tpm_chip *chip) +{ + return; +} + +static u8 tpm_ibmvtpm_status(struct tpm_chip *chip) +{ + return 0; +} + +/** + * ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version + * - Note that this is vtpm version and not tpm version + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_GET_VERSION; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_get_version failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm) +{ + int rc; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_COMP_CMD, 0); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_send_init_complete failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_crq_send_init - Send a CRQ initialize message + * @ibmvtpm: vtpm device struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm) +{ + int rc; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_CMD, 0); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "ibmvtpm_crq_send_init failed rc=%d\n", rc); + + return rc; +} + +/** + * tpm_ibmvtpm_remove - ibm vtpm remove entry point + * @vdev: vio device struct + * + * Return value: + * 0 + */ +static int __devexit tpm_ibmvtpm_remove(struct vio_dev *vdev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + int rc = 0; + + free_irq(vdev->irq, ibmvtpm); + tasklet_kill(&ibmvtpm->tasklet); + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + dma_unmap_single(ibmvtpm->dev, ibmvtpm->crq_dma_handle, + CRQ_RES_BUF_SIZE, DMA_BIDIRECTIONAL); + free_page((unsigned long)ibmvtpm->crq_queue.crq_addr); + + if (ibmvtpm->rtce_buf) { + dma_unmap_single(ibmvtpm->dev, ibmvtpm->rtce_dma_handle, + ibmvtpm->rtce_size, DMA_BIDIRECTIONAL); + kfree(ibmvtpm->rtce_buf); + } + + tpm_remove_hardware(ibmvtpm->dev); + + kfree(ibmvtpm); + + return 0; +} + +/** + * tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver + * @vdev: vio device struct + * + * Return value: + * Number of bytes the driver needs to DMA map + */ +static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev); + return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size; +} + +/** + * tpm_ibmvtpm_suspend - Suspend + * @dev: device struct + * + * Return value: + * 0 + */ +static int tpm_ibmvtpm_suspend(struct device *dev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + struct ibmvtpm_crq crq; + u64 *buf = (u64 *) &crq; + int rc = 0; + + crq.valid = (u8)IBMVTPM_VALID_CMD; + crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND; + + rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]); + if (rc != H_SUCCESS) + dev_err(ibmvtpm->dev, + "tpm_ibmvtpm_suspend failed rc=%d\n", rc); + + return rc; +} + +/** + * ibmvtpm_reset_crq - Reset CRQ + * @ibmvtpm: ibm vtpm struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm) +{ + int rc = 0; + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_FREE_CRQ, + ibmvtpm->vdev->unit_address); + } while (rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + memset(ibmvtpm->crq_queue.crq_addr, 0, CRQ_RES_BUF_SIZE); + ibmvtpm->crq_queue.index = 0; + + return plpar_hcall_norets(H_REG_CRQ, ibmvtpm->vdev->unit_address, + ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); +} + +/** + * tpm_ibmvtpm_resume - Resume from suspend + * @dev: device struct + * + * Return value: + * 0 + */ +static int tpm_ibmvtpm_resume(struct device *dev) +{ + struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev); + unsigned long flags; + int rc = 0; + + do { + if (rc) + msleep(100); + rc = plpar_hcall_norets(H_ENABLE_CRQ, + ibmvtpm->vdev->unit_address); + } while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc)); + + if (rc) { + dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc); + return rc; + } + + spin_lock_irqsave(&ibmvtpm->lock, flags); + vio_disable_interrupts(ibmvtpm->vdev); + tasklet_schedule(&ibmvtpm->tasklet); + spin_unlock_irqrestore(&ibmvtpm->lock, flags); + + rc = ibmvtpm_crq_send_init(ibmvtpm); + if (rc) + dev_err(dev, "Error send_init rc=%d\n", rc); + + return rc; +} + +static const struct file_operations ibmvtpm_ops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpm_open, + .read = tpm_read, + .write = tpm_write, + .release = tpm_release, +}; + +static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); +static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); +static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); +static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); +static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); +static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, + NULL); +static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL); +static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); +static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL); +static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL); + +static struct attribute *ibmvtpm_attrs[] = { + &dev_attr_pubek.attr, + &dev_attr_pcrs.attr, + &dev_attr_enabled.attr, + &dev_attr_active.attr, + &dev_attr_owned.attr, + &dev_attr_temp_deactivated.attr, + &dev_attr_caps.attr, + &dev_attr_cancel.attr, + &dev_attr_durations.attr, + &dev_attr_timeouts.attr, NULL, +}; + +static struct attribute_group ibmvtpm_attr_grp = { .attrs = ibmvtpm_attrs }; + +static const struct tpm_vendor_specific tpm_ibmvtpm = { + .recv = tpm_ibmvtpm_recv, + .send = tpm_ibmvtpm_send, + .cancel = tpm_ibmvtpm_cancel, + .status = tpm_ibmvtpm_status, + .req_complete_mask = 0, + .req_complete_val = 0, + .req_canceled = 0, + .attr_group = &ibmvtpm_attr_grp, + .miscdev = { .fops = &ibmvtpm_ops, }, +}; + +static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = { + .suspend = tpm_ibmvtpm_suspend, + .resume = tpm_ibmvtpm_resume, +}; + +/** + * ibmvtpm_crq_get_next - Get next responded crq + * @ibmvtpm vtpm device struct + * + * Return value: + * vtpm crq pointer + */ +static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm) +{ + struct ibmvtpm_crq_queue *crq_q = &ibmvtpm->crq_queue; + struct ibmvtpm_crq *crq = &crq_q->crq_addr[crq_q->index]; + + if (crq->valid & VTPM_MSG_RES) { + if (++crq_q->index == crq_q->num_entry) + crq_q->index = 0; + rmb(); + } else + crq = NULL; + return crq; +} + +/** + * ibmvtpm_crq_process - Process responded crq + * @crq crq to be processed + * @ibmvtpm vtpm device struct + * + * Return value: + * Nothing + */ +static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, + struct ibmvtpm_dev *ibmvtpm) +{ + int rc = 0; + + switch (crq->valid) { + case VALID_INIT_CRQ: + switch (crq->msg) { + case INIT_CRQ_RES: + dev_info(ibmvtpm->dev, "CRQ initialized\n"); + rc = ibmvtpm_crq_send_init_complete(ibmvtpm); + if (rc) + dev_err(ibmvtpm->dev, "Unable to send CRQ init complete rc=%d\n", rc); + return; + case INIT_CRQ_COMP_RES: + dev_info(ibmvtpm->dev, + "CRQ initialization completed\n"); + return; + default: + dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg); + return; + } + return; + case IBMVTPM_VALID_CMD: + switch (crq->msg) { + case VTPM_GET_RTCE_BUFFER_SIZE_RES: + if (crq->len <= 0) { + dev_err(ibmvtpm->dev, "Invalid rtce size\n"); + return; + } + ibmvtpm->rtce_size = crq->len; + ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, + GFP_KERNEL); + if (!ibmvtpm->rtce_buf) { + dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n"); + return; + } + + ibmvtpm->rtce_dma_handle = dma_map_single(ibmvtpm->dev, + ibmvtpm->rtce_buf, ibmvtpm->rtce_size, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(ibmvtpm->dev, + ibmvtpm->rtce_dma_handle)) { + kfree(ibmvtpm->rtce_buf); + ibmvtpm->rtce_buf = NULL; + dev_err(ibmvtpm->dev, "Failed to dma map rtce buffer\n"); + } + + return; + case VTPM_GET_VERSION_RES: + ibmvtpm->vtpm_version = crq->data; + return; + case VTPM_TPM_COMMAND_RES: + ibmvtpm->crq_res.valid = crq->valid; + ibmvtpm->crq_res.msg = crq->msg; + ibmvtpm->crq_res.len = crq->len; + ibmvtpm->crq_res.data = crq->data; + wake_up_interruptible(&wq); + return; + default: + return; + } + } + return; +} + +/** + * ibmvtpm_interrupt - Interrupt handler + * @irq: irq number to handle + * @vtpm_instance: vtpm that received interrupt + * + * Returns: + * IRQ_HANDLED + **/ +static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance) +{ + struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance; + unsigned long flags; + + spin_lock_irqsave(&ibmvtpm->lock, flags); + vio_disable_interrupts(ibmvtpm->vdev); + tasklet_schedule(&ibmvtpm->tasklet); + spin_unlock_irqrestore(&ibmvtpm->lock, flags); + + return IRQ_HANDLED; +} + +/** + * ibmvtpm_tasklet - Interrupt handler tasklet + * @data: ibm vtpm device struct + * + * Returns: + * Nothing + **/ +static void ibmvtpm_tasklet(void *data) +{ + struct ibmvtpm_dev *ibmvtpm = data; + struct ibmvtpm_crq *crq; + unsigned long flags; + + spin_lock_irqsave(&ibmvtpm->lock, flags); + while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) { + ibmvtpm_crq_process(crq, ibmvtpm); + crq->valid = 0; + wmb(); + } + + vio_enable_interrupts(ibmvtpm->vdev); + spin_unlock_irqrestore(&ibmvtpm->lock, flags); +} + +/** + * tpm_ibmvtpm_probe - ibm vtpm initialize entry point + * @vio_dev: vio device struct + * @id: vio device id struct + * + * Return value: + * 0 - Success + * Non-zero - Failure + */ +static int __devinit tpm_ibmvtpm_probe(struct vio_dev *vio_dev, + const struct vio_device_id *id) +{ + struct ibmvtpm_dev *ibmvtpm; + struct device *dev = &vio_dev->dev; + struct ibmvtpm_crq_queue *crq_q; + struct tpm_chip *chip; + int rc = -ENOMEM, rc1; + + chip = tpm_register_hardware(dev, &tpm_ibmvtpm); + if (!chip) { + dev_err(dev, "tpm_register_hardware failed\n"); + return -ENODEV; + } + + ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL); + if (!ibmvtpm) { + dev_err(dev, "kzalloc for ibmvtpm failed\n"); + goto cleanup; + } + + crq_q = &ibmvtpm->crq_queue; + crq_q->crq_addr = (struct ibmvtpm_crq *)get_zeroed_page(GFP_KERNEL); + if (!crq_q->crq_addr) { + dev_err(dev, "Unable to allocate memory for crq_addr\n"); + goto cleanup; + } + + crq_q->num_entry = CRQ_RES_BUF_SIZE / sizeof(*crq_q->crq_addr); + ibmvtpm->crq_dma_handle = dma_map_single(dev, crq_q->crq_addr, + CRQ_RES_BUF_SIZE, + DMA_BIDIRECTIONAL); + + if (dma_mapping_error(dev, ibmvtpm->crq_dma_handle)) { + dev_err(dev, "dma mapping failed\n"); + goto cleanup; + } + + rc = plpar_hcall_norets(H_REG_CRQ, vio_dev->unit_address, + ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE); + if (rc == H_RESOURCE) + rc = ibmvtpm_reset_crq(ibmvtpm); + + if (rc) { + dev_err(dev, "Unable to register CRQ rc=%d\n", rc); + goto reg_crq_cleanup; + } + + tasklet_init(&ibmvtpm->tasklet, (void *)ibmvtpm_tasklet, + (unsigned long)ibmvtpm); + + rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0, + tpm_ibmvtpm_driver_name, ibmvtpm); + if (rc) { + dev_err(dev, "Error %d register irq 0x%x\n", rc, vio_dev->irq); + goto init_irq_cleanup; + } + + rc = vio_enable_interrupts(vio_dev); + if (rc) { + dev_err(dev, "Error %d enabling interrupts\n", rc); + goto init_irq_cleanup; + } + + crq_q->index = 0; + + ibmvtpm->dev = dev; + ibmvtpm->vdev = vio_dev; + chip->vendor.data = (void *)ibmvtpm; + + spin_lock_init(&ibmvtpm->lock); + spin_lock_init(&ibmvtpm->rtce_lock); + + rc = ibmvtpm_crq_send_init(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + rc = ibmvtpm_crq_get_version(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + rc = ibmvtpm_crq_get_rtce_size(ibmvtpm); + if (rc) + goto init_irq_cleanup; + + return rc; +init_irq_cleanup: + tasklet_kill(&ibmvtpm->tasklet); + do { + rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address); + } while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1)); +reg_crq_cleanup: + dma_unmap_single(dev, ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE, + DMA_BIDIRECTIONAL); +cleanup: + if (ibmvtpm) { + if (crq_q->crq_addr) + free_page((unsigned long)crq_q->crq_addr); + kfree(ibmvtpm); + } + + tpm_remove_hardware(dev); + + return rc; +} + +static struct vio_driver ibmvtpm_driver = { + .id_table = tpm_ibmvtpm_device_table, + .probe = tpm_ibmvtpm_probe, + .remove = tpm_ibmvtpm_remove, + .get_desired_dma = tpm_ibmvtpm_get_desired_dma, + .name = tpm_ibmvtpm_driver_name, + .pm = &tpm_ibmvtpm_pm_ops, +}; + +/** + * ibmvtpm_module_init - Initialize ibm vtpm module + * + * Return value: + * 0 -Success + * Non-zero - Failure + */ +static int __init ibmvtpm_module_init(void) +{ + return vio_register_driver(&ibmvtpm_driver); +} + +/** + * ibmvtpm_module_exit - Teardown ibm vtpm module + * + * Return value: + * Nothing + */ +static void __exit ibmvtpm_module_exit(void) +{ + vio_unregister_driver(&ibmvtpm_driver); +} + +module_init(ibmvtpm_module_init); +module_exit(ibmvtpm_module_exit); + +MODULE_AUTHOR("adlai@us.ibm.com"); +MODULE_DESCRIPTION("IBM vTPM Driver"); +MODULE_VERSION("1.0"); +MODULE_LICENSE("GPL"); diff --git a/drivers/char/tpm/tpm_ibmvtpm.h b/drivers/char/tpm/tpm_ibmvtpm.h new file mode 100644 index 000000000000..4296eb4b4d82 --- /dev/null +++ b/drivers/char/tpm/tpm_ibmvtpm.h @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2012 IBM Corporation + * + * Author: Ashley Lai + * + * Maintained by: + * + * Device driver for TCG/TCPA TPM (trusted platform module). + * Specifications at www.trustedcomputinggroup.org + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation, version 2 of the + * License. + * + */ + +#ifndef __TPM_IBMVTPM_H__ +#define __TPM_IBMVTPM_H__ + +/* vTPM Message Format 1 */ +struct ibmvtpm_crq { + u8 valid; + u8 msg; + u16 len; + u32 data; + u64 reserved; +} __attribute__((packed, aligned(8))); + +struct ibmvtpm_crq_queue { + struct ibmvtpm_crq *crq_addr; + u32 index; + u32 num_entry; +}; + +struct ibmvtpm_dev { + struct device *dev; + struct vio_dev *vdev; + struct ibmvtpm_crq_queue crq_queue; + dma_addr_t crq_dma_handle; + spinlock_t lock; + struct tasklet_struct tasklet; + u32 rtce_size; + void __iomem *rtce_buf; + dma_addr_t rtce_dma_handle; + spinlock_t rtce_lock; + struct ibmvtpm_crq crq_res; + u32 vtpm_version; +}; + +#define CRQ_RES_BUF_SIZE PAGE_SIZE + +/* Initialize CRQ */ +#define INIT_CRQ_CMD 0xC001000000000000LL /* Init cmd */ +#define INIT_CRQ_COMP_CMD 0xC002000000000000LL /* Init complete cmd */ +#define INIT_CRQ_RES 0x01 /* Init respond */ +#define INIT_CRQ_COMP_RES 0x02 /* Init complete respond */ +#define VALID_INIT_CRQ 0xC0 /* Valid command for init crq */ + +/* vTPM CRQ response is the message type | 0x80 */ +#define VTPM_MSG_RES 0x80 +#define IBMVTPM_VALID_CMD 0x80 + +/* vTPM CRQ message types */ +#define VTPM_GET_VERSION 0x01 +#define VTPM_GET_VERSION_RES (0x01 | VTPM_MSG_RES) + +#define VTPM_TPM_COMMAND 0x02 +#define VTPM_TPM_COMMAND_RES (0x02 | VTPM_MSG_RES) + +#define VTPM_GET_RTCE_BUFFER_SIZE 0x03 +#define VTPM_GET_RTCE_BUFFER_SIZE_RES (0x03 | VTPM_MSG_RES) + +#define VTPM_PREPARE_TO_SUSPEND 0x04 +#define VTPM_PREPARE_TO_SUSPEND_RES (0x04 | VTPM_MSG_RES) + +#endif From 4a727429abec31c4f5d9607cebb4fb1cc21e1167 Mon Sep 17 00:00:00 2001 From: Ashley Lai Date: Tue, 14 Aug 2012 18:34:57 -0500 Subject: [PATCH 10/39] PPC64: Add support for instantiating SML from Open Firmware This patch instantiate Stored Measurement Log (SML) and put the log address and size in the device tree. Signed-off-by: Ashley Lai Signed-off-by: Kent Yoder --- arch/powerpc/kernel/prom_init.c | 62 +++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 0794a3017b1b..e144498bcddd 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1623,6 +1623,63 @@ static void __init prom_instantiate_rtas(void) } #ifdef CONFIG_PPC64 +/* + * Allocate room for and instantiate Stored Measurement Log (SML) + */ +static void __init prom_instantiate_sml(void) +{ + phandle ibmvtpm_node; + ihandle ibmvtpm_inst; + u32 entry = 0, size = 0; + u64 base; + + prom_debug("prom_instantiate_sml: start...\n"); + + ibmvtpm_node = call_prom("finddevice", 1, 1, ADDR("/ibm,vtpm")); + prom_debug("ibmvtpm_node: %x\n", ibmvtpm_node); + if (!PHANDLE_VALID(ibmvtpm_node)) + return; + + ibmvtpm_inst = call_prom("open", 1, 1, ADDR("/ibm,vtpm")); + if (!IHANDLE_VALID(ibmvtpm_inst)) { + prom_printf("opening vtpm package failed (%x)\n", ibmvtpm_inst); + return; + } + + if (call_prom_ret("call-method", 2, 2, &size, + ADDR("sml-get-handover-size"), + ibmvtpm_inst) != 0 || size == 0) { + prom_printf("SML get handover size failed\n"); + return; + } + + base = alloc_down(size, PAGE_SIZE, 0); + if (base == 0) + prom_panic("Could not allocate memory for sml\n"); + + prom_printf("instantiating sml at 0x%x...", base); + + if (call_prom_ret("call-method", 4, 2, &entry, + ADDR("sml-handover"), + ibmvtpm_inst, size, base) != 0 || entry == 0) { + prom_printf("SML handover failed\n"); + return; + } + prom_printf(" done\n"); + + reserve_mem(base, size); + + prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-base", + &base, sizeof(base)); + prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-size", + &size, sizeof(size)); + + prom_debug("sml base = 0x%x\n", base); + prom_debug("sml size = 0x%x\n", (long)size); + + prom_debug("prom_instantiate_sml: end...\n"); +} + /* * Allocate room for and initialize TCE tables */ @@ -2916,6 +2973,11 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4, prom_instantiate_opal(); #endif +#ifdef CONFIG_PPC64 + /* instantiate sml */ + prom_instantiate_sml(); +#endif + /* * On non-powermacs, put all CPUs in spin-loops. * From c5df39262dd59dbbffb1017fca0f1661408ac9d5 Mon Sep 17 00:00:00 2001 From: Ashley Lai Date: Tue, 14 Aug 2012 18:35:32 -0500 Subject: [PATCH 11/39] drivers/char/tpm: Add securityfs support for event log This patch retrieves the event log data from the device tree during file open. The event log data will then displayed through securityfs. Signed-off-by: Ashley Lai Signed-off-by: Kent Yoder --- drivers/char/tpm/Makefile | 5 +++ drivers/char/tpm/tpm.h | 12 ------ drivers/char/tpm/tpm_eventlog.h | 15 +++++++ drivers/char/tpm/tpm_of.c | 73 +++++++++++++++++++++++++++++++++ 4 files changed, 93 insertions(+), 12 deletions(-) create mode 100644 drivers/char/tpm/tpm_of.c diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 547509d02046..9080cc44e3c4 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -5,6 +5,11 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o tpm_bios-objs += tpm_eventlog.o tpm_acpi.o +else +ifdef CONFIG_TCG_IBMVTPM + obj-$(CONFIG_TCG_TPM) += tpm_bios.o + tpm_bios-objs += tpm_eventlog.o tpm_of.o +endif endif obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 870fde7459c5..f1af73821101 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -327,15 +327,3 @@ extern int tpm_pm_suspend(struct device *); extern int tpm_pm_resume(struct device *); extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *); -#ifdef CONFIG_ACPI -extern struct dentry ** tpm_bios_log_setup(char *); -extern void tpm_bios_log_teardown(struct dentry **); -#else -static inline struct dentry ** tpm_bios_log_setup(char *name) -{ - return NULL; -} -static inline void tpm_bios_log_teardown(struct dentry **dir) -{ -} -#endif diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h index 8e23ccdf8a83..e7da086d6928 100644 --- a/drivers/char/tpm/tpm_eventlog.h +++ b/drivers/char/tpm/tpm_eventlog.h @@ -68,4 +68,19 @@ enum tcpa_pc_event_ids { }; int read_log(struct tpm_bios_log *log); + +#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \ + defined(CONFIG_ACPI) +extern struct dentry **tpm_bios_log_setup(char *); +extern void tpm_bios_log_teardown(struct dentry **); +#else +static inline struct dentry **tpm_bios_log_setup(char *name) +{ + return NULL; +} +static inline void tpm_bios_log_teardown(struct dentry **dir) +{ +} +#endif + #endif diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c new file mode 100644 index 000000000000..98ba2bd1a355 --- /dev/null +++ b/drivers/char/tpm/tpm_of.c @@ -0,0 +1,73 @@ +/* + * Copyright 2012 IBM Corporation + * + * Author: Ashley Lai + * + * Maintained by: + * + * Read the event log created by the firmware on PPC64 + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * + */ + +#include +#include + +#include "tpm.h" +#include "tpm_eventlog.h" + +int read_log(struct tpm_bios_log *log) +{ + struct device_node *np; + const u32 *sizep; + const __be64 *basep; + + if (log->bios_event_log != NULL) { + pr_err("%s: ERROR - Eventlog already initialized\n", __func__); + return -EFAULT; + } + + np = of_find_node_by_name(NULL, "ibm,vtpm"); + if (!np) { + pr_err("%s: ERROR - IBMVTPM not supported\n", __func__); + return -ENODEV; + } + + sizep = of_get_property(np, "linux,sml-size", NULL); + if (sizep == NULL) { + pr_err("%s: ERROR - SML size not found\n", __func__); + goto cleanup_eio; + } + if (*sizep == 0) { + pr_err("%s: ERROR - event log area empty\n", __func__); + goto cleanup_eio; + } + + basep = of_get_property(np, "linux,sml-base", NULL); + if (basep == NULL) { + pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__); + goto cleanup_eio; + } + + of_node_put(np); + log->bios_event_log = kmalloc(*sizep, GFP_KERNEL); + if (!log->bios_event_log) { + pr_err("%s: ERROR - Not enough memory for BIOS measurements\n", + __func__); + return -ENOMEM; + } + + log->bios_event_log_end = log->bios_event_log + *sizep; + + memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep); + + return 0; + +cleanup_eio: + of_node_put(np); + return -EIO; +} From 20328b56cdf8fcc79f28c6c50ad8190fc0779e80 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Wed, 22 Aug 2012 15:01:47 -0500 Subject: [PATCH 12/39] ima: enable the IBM vTPM as the default TPM in the PPC64 case Enable tpm_ibmvtpm driver by default when IMA is enabled on PPC64 Signed-off-by: Kent Yoder --- security/integrity/ima/Kconfig | 1 + 1 file changed, 1 insertion(+) diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index b9c1219924f1..809ccf19d09c 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -11,6 +11,7 @@ config IMA select CRYPTO_SHA1 select TCG_TPM if HAS_IOMEM && !UML select TCG_TIS if TCG_TPM && X86 + select TCG_IBMVTPM if TCG_TPM && PPC64 help The Trusted Computing Group(TCG) runtime Integrity Measurement Architecture(IMA) maintains a list of hash From f334ac8da82478b3f8c52e3c01849ad7fe509d5b Mon Sep 17 00:00:00 2001 From: Jesper Juhl Date: Thu, 16 Aug 2012 00:16:33 +0200 Subject: [PATCH 13/39] tpm: Do not dereference NULL pointer if acpi_os_map_memory() fails. In drivers/char/tpm/tpm_acpi.c::read_log() we call acpi_os_map_memory(). That call may fail for a number of reasons (invalid address, out of memory etc). If the call fails it returns NULL and we just pass that to memcpy() unconditionally, which will go bad when it tries to dereference the pointer. Unfortunately we just get NULL back, so we can't really tell the user exactely what went wrong, but we can at least avoid crashing and return an error (-EIO seemed more generic and more suitable here than -ENOMEM or something else, so I picked that). Signed-off-by: Jesper Juhl Signed-off-by: Kent Yoder --- drivers/char/tpm/tpm_acpi.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c index a1bb5a182df9..fe3fa9431dc9 100644 --- a/drivers/char/tpm/tpm_acpi.c +++ b/drivers/char/tpm/tpm_acpi.c @@ -96,6 +96,11 @@ int read_log(struct tpm_bios_log *log) log->bios_event_log_end = log->bios_event_log + len; virt = acpi_os_map_memory(start, len); + if (!virt) { + kfree(log->bios_event_log); + printk("%s: ERROR - Unable to map memory\n", __func__); + return -EIO; + } memcpy(log->bios_event_log, virt, len); From 1f862f0f96abdf8b030bda84d6b66d676f31785f Mon Sep 17 00:00:00 2001 From: Xiaoyan Zhang Date: Wed, 22 Aug 2012 18:47:21 +0800 Subject: [PATCH 14/39] Documentation: sysfs for Physical Presence Interface Signed-off-by: Xiaoyan Zhang Signed-off-by: Kent Yoder --- Documentation/ABI/testing/sysfs-driver-ppi | 70 ++++++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-driver-ppi diff --git a/Documentation/ABI/testing/sysfs-driver-ppi b/Documentation/ABI/testing/sysfs-driver-ppi new file mode 100644 index 000000000000..97a003ee058b --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-ppi @@ -0,0 +1,70 @@ +What: /sys/devices/pnp0//ppi/ +Date: August 2012 +Kernel Version: 3.6 +Contact: xiaoyan.zhang@intel.com +Description: + This folder includes the attributes related with PPI (Physical + Presence Interface). Only if TPM is supported by BIOS, this + folder makes sence. The folder path can be got by command + 'find /sys/ -name 'pcrs''. For the detail information of PPI, + please refer to the PPI specification from + http://www.trustedcomputinggroup.org/ + +What: /sys/devices/pnp0//ppi/version +Date: August 2012 +Contact: xiaoyan.zhang@intel.com +Description: + This attribute shows the version of the PPI supported by the + platform. + This file is readonly. + +What: /sys/devices/pnp0//ppi/request +Date: August 2012 +Contact: xiaoyan.zhang@intel.com +Description: + This attribute shows the request for an operation to be + executed in the pre-OS environment. It is the only input from + the OS to the pre-OS environment. The request should be an + integer value range from 1 to 160, and 0 means no request. + This file can be read and written. + +What: /sys/devices/pnp0/00:/ppi/response +Date: August 2012 +Contact: xiaoyan.zhang@intel.com +Description: + This attribute shows the response to the most recent operation + request it acted upon. The format is " + : ". + This file is readonly. + +What: /sys/devices/pnp0//ppi/transition_action +Date: August 2012 +Contact: xiaoyan.zhang@intel.com +Description: + This attribute shows the platform-specific action that should + take place in order to transition to the BIOS for execution of + a requested operation. The format is ": ". + This file is readonly. + +What: /sys/devices/pnp0//ppi/tcg_operations +Date: August 2012 +Contact: xiaoyan.zhang@intel.com +Description: + This attribute shows whether it is allowed to request an + operation to be executed in the pre-OS environment by the BIOS + for the requests defined by TCG, i.e. requests from 1 to 22. + The format is " : ". + This attribute is only supported by PPI version 1.2+. + This file is readonly. + +What: /sys/devices/pnp0//ppi/vs_operations +Date: August 2012 +Contact: xiaoyan.zhang@intel.com +Description: + This attribute shows whether it is allowed to request an + operation to be executed in the pre-OS environment by the BIOS + for the verdor specific requests, i.e. requests from 128 to + 255. The format is same with tcg_operations. This attribute + is also only supported by PPI version 1.2+. + This file is readonly. From f84fdff0fdcda7e509ce530e0ee612233a2104fb Mon Sep 17 00:00:00 2001 From: Xiaoyan Zhang Date: Wed, 22 Aug 2012 18:47:22 +0800 Subject: [PATCH 15/39] driver: add PPI support in tpm driver The Physical Presence Interface enables the OS and the BIOS to cooperate and provides a simple and straightforward platform user experience for administering the TPM without sacrificing security. V2: separate the patch out in a separate source file, add #ifdef CONFIG_ACPI so it compiles out on ppc, use standard error instead of ACPI error as return code of show/store fns. V3: move #ifdef CONFIG_ACPI from .c file to .h file. V4: move tpm_ppi code from tpm module to tpm_bios module. V5: modify sys_add_ppi() so that ppi_attr_grp doesn't need to be exported Signed-off-by: Xiaoyan Zhang Signed-off-by: Kent Yoder --- drivers/char/tpm/Makefile | 2 +- drivers/char/tpm/tpm.c | 5 + drivers/char/tpm/tpm.h | 9 + drivers/char/tpm/tpm_ppi.c | 460 +++++++++++++++++++++++++++++++++++++ 4 files changed, 475 insertions(+), 1 deletion(-) create mode 100644 drivers/char/tpm/tpm_ppi.c diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index 9080cc44e3c4..5b3fc8bc6c13 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -4,7 +4,7 @@ obj-$(CONFIG_TCG_TPM) += tpm.o ifdef CONFIG_ACPI obj-$(CONFIG_TCG_TPM) += tpm_bios.o - tpm_bios-objs += tpm_eventlog.o tpm_acpi.o + tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o else ifdef CONFIG_TCG_IBMVTPM obj-$(CONFIG_TCG_TPM) += tpm_bios.o diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 0a75638e3e56..39526c053158 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -1476,6 +1476,11 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, goto put_device; } + if (sys_add_ppi(&dev->kobj)) { + misc_deregister(&chip->vendor.miscdev); + goto put_device; + } + chip->bios_dir = tpm_bios_log_setup(devname); /* Make chip available */ diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f1af73821101..02c266aa2bf7 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -327,3 +327,12 @@ extern int tpm_pm_suspend(struct device *); extern int tpm_pm_resume(struct device *); extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long, wait_queue_head_t *); + +#ifdef CONFIG_ACPI +extern ssize_t sys_add_ppi(struct kobject *parent); +#else +static inline ssize_t sys_add_ppi(struct kobject *parent) +{ + return 0; +} +#endif diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c new file mode 100644 index 000000000000..440fa1c2d301 --- /dev/null +++ b/drivers/char/tpm/tpm_ppi.c @@ -0,0 +1,460 @@ +#include +#include +#include "tpm.h" + +static const u8 tpm_ppi_uuid[] = { + 0xA6, 0xFA, 0xDD, 0x3D, + 0x1B, 0x36, + 0xB4, 0x4E, + 0xA4, 0x24, + 0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53 +}; +static char *tpm_device_name = "TPM"; + +#define TPM_PPI_REVISION_ID 1 +#define TPM_PPI_FN_VERSION 1 +#define TPM_PPI_FN_SUBREQ 2 +#define TPM_PPI_FN_GETREQ 3 +#define TPM_PPI_FN_GETACT 4 +#define TPM_PPI_FN_GETRSP 5 +#define TPM_PPI_FN_SUBREQ2 7 +#define TPM_PPI_FN_GETOPR 8 +#define PPI_TPM_REQ_MAX 22 +#define PPI_VS_REQ_START 128 +#define PPI_VS_REQ_END 255 +#define PPI_VERSION_LEN 3 + +static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context, + void **return_value) +{ + acpi_status status; + struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL }; + status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer); + if (strstr(buffer.pointer, context) != NULL) { + *return_value = handle; + kfree(buffer.pointer); + return AE_CTRL_TERMINATE; + } + return AE_OK; +} + +static inline void ppi_assign_params(union acpi_object params[4], + u64 function_num) +{ + params[0].type = ACPI_TYPE_BUFFER; + params[0].buffer.length = sizeof(tpm_ppi_uuid); + params[0].buffer.pointer = (char *)tpm_ppi_uuid; + params[1].type = ACPI_TYPE_INTEGER; + params[1].integer.value = TPM_PPI_REVISION_ID; + params[2].type = ACPI_TYPE_INTEGER; + params[2].integer.value = function_num; + params[3].type = ACPI_TYPE_PACKAGE; + params[3].package.count = 0; + params[3].package.elements = NULL; +} + +ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, + char *buf) +{ + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object *obj; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + obj = (union acpi_object *)output.pointer; + status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer); + kfree(output.pointer); + return status; +} + +ssize_t tpm_show_ppi_request(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object *ret_obj; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_GETREQ); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return -ENOMEM; + /* + * output.pointer should be of package type, including two integers. + * The first is function return code, 0 means success and 1 means + * error. The second is pending TPM operation requested by the OS, 0 + * means none and >0 means operation value. + */ + ret_obj = ((union acpi_object *)output.pointer)->package.elements; + if (ret_obj->type == ACPI_TYPE_INTEGER) { + if (ret_obj->integer.value) { + status = -EFAULT; + goto cleanup; + } + ret_obj++; + if (ret_obj->type == ACPI_TYPE_INTEGER) + status = scnprintf(buf, PAGE_SIZE, "%llu\n", + ret_obj->integer.value); + else + status = -EINVAL; + } else { + status = -EINVAL; + } +cleanup: + kfree(output.pointer); + return status; +} + +ssize_t tpm_store_ppi_request(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + char version[PPI_VERSION_LEN + 1]; + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object obj; + u32 req; + u64 ret; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + strncpy(version, + ((union acpi_object *)output.pointer)->string.pointer, + PPI_VERSION_LEN); + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + /* + * the function to submit TPM operation request to pre-os environment + * is updated with function index from SUBREQ to SUBREQ2 since PPI + * version 1.1 + */ + if (strcmp(version, "1.1") == -1) + params[2].integer.value = TPM_PPI_FN_SUBREQ; + else + params[2].integer.value = TPM_PPI_FN_SUBREQ2; + /* + * PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS + * accept buffer/string/integer type, but some BIOS accept buffer/ + * string/package type. For PPI version 1.0 and 1.1, use buffer type + * for compatibility, and use package type since 1.2 according to spec. + */ + if (strcmp(version, "1.2") == -1) { + params[3].type = ACPI_TYPE_BUFFER; + params[3].buffer.length = sizeof(req); + sscanf(buf, "%d", &req); + params[3].buffer.pointer = (char *)&req; + } else { + params[3].package.count = 1; + obj.type = ACPI_TYPE_INTEGER; + sscanf(buf, "%llu", &obj.integer.value); + params[3].package.elements = &obj; + } + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(status)) + return -ENOMEM; + ret = ((union acpi_object *)output.pointer)->integer.value; + if (ret == 0) + status = (acpi_status)count; + else if (ret == 1) + status = -EPERM; + else + status = -EFAULT; + kfree(output.pointer); + return status; +} + +ssize_t tpm_show_ppi_transition_action(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + char version[PPI_VERSION_LEN + 1]; + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + u32 ret; + char *info[] = { + "None", + "Shutdown", + "Reboot", + "OS Vendor-specific", + "Error", + }; + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + strncpy(version, + ((union acpi_object *)output.pointer)->string.pointer, + PPI_VERSION_LEN); + /* + * PPI spec defines params[3].type as empty package, but some platforms + * (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for + * compatibility, define params[3].type as buffer, if PPI version < 1.2 + */ + if (strcmp(version, "1.2") == -1) { + params[3].type = ACPI_TYPE_BUFFER; + params[3].buffer.length = 0; + params[3].buffer.pointer = NULL; + } + params[2].integer.value = TPM_PPI_FN_GETACT; + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(status)) + return -ENOMEM; + ret = ((union acpi_object *)output.pointer)->integer.value; + if (ret < ARRAY_SIZE(info) - 1) + status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]); + else + status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, + info[ARRAY_SIZE(info)-1]); + kfree(output.pointer); + return status; +} + +ssize_t tpm_show_ppi_response(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object *ret_obj; + u64 req; + + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_GETRSP); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_PACKAGE); + if (ACPI_FAILURE(status)) + return -ENOMEM; + /* + * parameter output.pointer should be of package type, including + * 3 integers. The first means function return code, the second means + * most recent TPM operation request, and the last means response to + * the most recent TPM operation request. Only if the first is 0, and + * the second integer is not 0, the response makes sense. + */ + ret_obj = ((union acpi_object *)output.pointer)->package.elements; + if (ret_obj->type != ACPI_TYPE_INTEGER) { + status = -EINVAL; + goto cleanup; + } + if (ret_obj->integer.value) { + status = -EFAULT; + goto cleanup; + } + ret_obj++; + if (ret_obj->type != ACPI_TYPE_INTEGER) { + status = -EINVAL; + goto cleanup; + } + if (ret_obj->integer.value) { + req = ret_obj->integer.value; + ret_obj++; + if (ret_obj->type != ACPI_TYPE_INTEGER) { + status = -EINVAL; + goto cleanup; + } + if (ret_obj->integer.value == 0) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0: Success"); + else if (ret_obj->integer.value == 0xFFFFFFF0) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0xFFFFFFF0: User Abort"); + else if (ret_obj->integer.value == 0xFFFFFFF1) + status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req, + "0xFFFFFFF1: BIOS Failure"); + else if (ret_obj->integer.value >= 1 && + ret_obj->integer.value <= 0x00000FFF) + status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", + req, ret_obj->integer.value, + "Corresponding TPM error"); + else + status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n", + req, ret_obj->integer.value, + "Error"); + } else { + status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n", + ret_obj->integer.value, "No Recent Request"); + } +cleanup: + kfree(output.pointer); + return status; +} + +static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) +{ + char *str = buf; + char version[PPI_VERSION_LEN]; + acpi_handle handle; + acpi_status status; + struct acpi_object_list input; + struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL }; + union acpi_object params[4]; + union acpi_object obj; + int i; + u32 ret; + char *info[] = { + "Not implemented", + "BIOS only", + "Blocked for OS by BIOS", + "User required", + "User not required", + }; + input.count = 4; + ppi_assign_params(params, TPM_PPI_FN_VERSION); + input.pointer = params; + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT, + ACPI_UINT32_MAX, ppi_callback, NULL, + tpm_device_name, &handle); + if (ACPI_FAILURE(status)) + return -ENXIO; + + status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output, + ACPI_TYPE_STRING); + if (ACPI_FAILURE(status)) + return -ENOMEM; + + strncpy(version, + ((union acpi_object *)output.pointer)->string.pointer, + PPI_VERSION_LEN); + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + if (strcmp(version, "1.2") == -1) + return -EPERM; + + params[2].integer.value = TPM_PPI_FN_GETOPR; + params[3].package.count = 1; + obj.type = ACPI_TYPE_INTEGER; + params[3].package.elements = &obj; + for (i = start; i <= end; i++) { + obj.integer.value = i; + status = acpi_evaluate_object_typed(handle, "_DSM", + &input, &output, ACPI_TYPE_INTEGER); + if (ACPI_FAILURE(status)) + return -ENOMEM; + + ret = ((union acpi_object *)output.pointer)->integer.value; + if (ret > 0 && ret < ARRAY_SIZE(info)) + str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n", + i, ret, info[ret]); + kfree(output.pointer); + output.length = ACPI_ALLOCATE_BUFFER; + output.pointer = NULL; + } + return str - buf; +} + +ssize_t tpm_show_ppi_tcg_operations(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX); +} + +ssize_t tpm_show_ppi_vs_operations(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END); +} + +static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL); +static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP, + tpm_show_ppi_request, tpm_store_ppi_request); +static DEVICE_ATTR(transition_action, S_IRUGO, + tpm_show_ppi_transition_action, NULL); +static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL); +static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL); +static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL); + +static struct attribute *ppi_attrs[] = { + &dev_attr_version.attr, + &dev_attr_request.attr, + &dev_attr_transition_action.attr, + &dev_attr_response.attr, + &dev_attr_tcg_operations.attr, + &dev_attr_vs_operations.attr, NULL, +}; +static struct attribute_group ppi_attr_grp = { + .attrs = ppi_attrs +}; + +ssize_t sys_add_ppi(struct kobject *parent) +{ + struct kobject *ppi; + ppi = kobject_create_and_add("ppi", parent); + if (sysfs_create_group(ppi, &ppi_attr_grp)) + return -EFAULT; + else + return 0; +} +EXPORT_SYMBOL_GPL(sys_add_ppi); + +MODULE_LICENSE("GPL"); From 81198078d7da4240f3cbfc2c6a8ea6cd417f51a7 Mon Sep 17 00:00:00 2001 From: Xiaoyan Zhang Date: Thu, 29 Aug 2013 20:39:11 +0800 Subject: [PATCH 16/39] driver/char/tpm: declare internal symbols as static This patch declares the internal struct and functions as static to provide more security. Signed-off-by: Xiaoyan Zhang Signed-off-by: Fengguang Wu Reviewed-by: Kent Yoder Signed-off-by: James Morris --- drivers/char/tpm/tpm.c | 4 ++-- drivers/char/tpm/tpm_ppi.c | 37 +++++++++++++++++++------------------ 2 files changed, 21 insertions(+), 20 deletions(-) diff --git a/drivers/char/tpm/tpm.c b/drivers/char/tpm/tpm.c index 39526c053158..6724615a4fdd 100644 --- a/drivers/char/tpm/tpm.c +++ b/drivers/char/tpm/tpm.c @@ -915,7 +915,7 @@ EXPORT_SYMBOL_GPL(tpm_show_pcrs); #define READ_PUBEK_RESULT_SIZE 314 #define TPM_ORD_READPUBEK cpu_to_be32(124) -struct tpm_input_header tpm_readpubek_header = { +static struct tpm_input_header tpm_readpubek_header = { .tag = TPM_TAG_RQU_COMMAND, .length = cpu_to_be32(30), .ordinal = TPM_ORD_READPUBEK @@ -1395,7 +1395,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); * Once all references to platform device are down to 0, * release all allocated structures. */ -void tpm_dev_release(struct device *dev) +static void tpm_dev_release(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 440fa1c2d301..f27b58cfae98 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -53,8 +53,8 @@ static inline void ppi_assign_params(union acpi_object params[4], params[3].package.elements = NULL; } -ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_version(struct device *dev, + struct device_attribute *attr, char *buf) { acpi_handle handle; acpi_status status; @@ -82,9 +82,8 @@ ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, return status; } -ssize_t tpm_show_ppi_request(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_request(struct device *dev, + struct device_attribute *attr, char *buf) { acpi_handle handle; acpi_status status; @@ -132,9 +131,9 @@ cleanup: return status; } -ssize_t tpm_store_ppi_request(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +static ssize_t tpm_store_ppi_request(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) { char version[PPI_VERSION_LEN + 1]; acpi_handle handle; @@ -207,9 +206,9 @@ ssize_t tpm_store_ppi_request(struct device *dev, return status; } -ssize_t tpm_show_ppi_transition_action(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_transition_action(struct device *dev, + struct device_attribute *attr, + char *buf) { char version[PPI_VERSION_LEN + 1]; acpi_handle handle; @@ -269,9 +268,9 @@ ssize_t tpm_show_ppi_transition_action(struct device *dev, return status; } -ssize_t tpm_show_ppi_response(struct device *dev, - struct device_attribute *attr, - char *buf) +static ssize_t tpm_show_ppi_response(struct device *dev, + struct device_attribute *attr, + char *buf) { acpi_handle handle; acpi_status status; @@ -413,14 +412,16 @@ static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) return str - buf; } -ssize_t tpm_show_ppi_tcg_operations(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, + struct device_attribute *attr, + char *buf) { return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX); } -ssize_t tpm_show_ppi_vs_operations(struct device *dev, - struct device_attribute *attr, char *buf) +static ssize_t tpm_show_ppi_vs_operations(struct device *dev, + struct device_attribute *attr, + char *buf) { return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END); } From c6993e4ac002c92bc75379212e9179c36d4bf7ee Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 4 Sep 2012 13:32:13 -0700 Subject: [PATCH 17/39] security: allow Yama to be unconditionally stacked Unconditionally call Yama when CONFIG_SECURITY_YAMA_STACKED is selected, no matter what LSM module is primary. Ubuntu and Chrome OS already carry patches to do this, and Fedora has voiced interest in doing this as well. Instead of having multiple distributions (or LSM authors) carrying these patches, just allow Yama to be called unconditionally when selected by the new CONFIG. Signed-off-by: Kees Cook Acked-by: Serge E. Hallyn Acked-by: Eric Paris Acked-by: John Johansen Signed-off-by: James Morris --- include/linux/security.h | 31 +++++++++++++++++++++++++++++++ security/security.c | 21 +++++++++++++++++++++ security/yama/Kconfig | 8 ++++++++ security/yama/yama_lsm.c | 14 ++++++++++---- 4 files changed, 70 insertions(+), 4 deletions(-) diff --git a/include/linux/security.h b/include/linux/security.h index 3dea6a9d568f..01ef030b9409 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -3021,5 +3021,36 @@ static inline void free_secdata(void *secdata) { } #endif /* CONFIG_SECURITY */ +#ifdef CONFIG_SECURITY_YAMA +extern int yama_ptrace_access_check(struct task_struct *child, + unsigned int mode); +extern int yama_ptrace_traceme(struct task_struct *parent); +extern void yama_task_free(struct task_struct *task); +extern int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, + unsigned long arg4, unsigned long arg5); +#else +static inline int yama_ptrace_access_check(struct task_struct *child, + unsigned int mode) +{ + return 0; +} + +static inline int yama_ptrace_traceme(struct task_struct *parent) +{ + return 0; +} + +static inline void yama_task_free(struct task_struct *task) +{ +} + +static inline int yama_task_prctl(int option, unsigned long arg2, + unsigned long arg3, unsigned long arg4, + unsigned long arg5) +{ + return -ENOSYS; +} +#endif /* CONFIG_SECURITY_YAMA */ + #endif /* ! __LINUX_SECURITY_H */ diff --git a/security/security.c b/security/security.c index 860aeb349cb3..68c1b9b45d93 100644 --- a/security/security.c +++ b/security/security.c @@ -136,11 +136,23 @@ int __init register_security(struct security_operations *ops) int security_ptrace_access_check(struct task_struct *child, unsigned int mode) { +#ifdef CONFIG_SECURITY_YAMA_STACKED + int rc; + rc = yama_ptrace_access_check(child, mode); + if (rc) + return rc; +#endif return security_ops->ptrace_access_check(child, mode); } int security_ptrace_traceme(struct task_struct *parent) { +#ifdef CONFIG_SECURITY_YAMA_STACKED + int rc; + rc = yama_ptrace_traceme(parent); + if (rc) + return rc; +#endif return security_ops->ptrace_traceme(parent); } @@ -761,6 +773,9 @@ int security_task_create(unsigned long clone_flags) void security_task_free(struct task_struct *task) { +#ifdef CONFIG_SECURITY_YAMA_STACKED + yama_task_free(task); +#endif security_ops->task_free(task); } @@ -876,6 +891,12 @@ int security_task_wait(struct task_struct *p) int security_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { +#ifdef CONFIG_SECURITY_YAMA_STACKED + int rc; + rc = yama_task_prctl(option, arg2, arg3, arg4, arg5); + if (rc != -ENOSYS) + return rc; +#endif return security_ops->task_prctl(option, arg2, arg3, arg4, arg5); } diff --git a/security/yama/Kconfig b/security/yama/Kconfig index 51d6709d8bbd..20ef5143c0c0 100644 --- a/security/yama/Kconfig +++ b/security/yama/Kconfig @@ -11,3 +11,11 @@ config SECURITY_YAMA Further information can be found in Documentation/security/Yama.txt. If you are unsure how to answer this question, answer N. + +config SECURITY_YAMA_STACKED + bool "Yama stacked with other LSMs" + depends on SECURITY_YAMA + default n + help + When Yama is built into the kernel, force it to stack with the + selected primary LSM. diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index d51b7c76c37d..9ca43c1ea630 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -100,7 +100,7 @@ static void yama_ptracer_del(struct task_struct *tracer, * yama_task_free - check for task_pid to remove from exception list * @task: task being removed */ -static void yama_task_free(struct task_struct *task) +void yama_task_free(struct task_struct *task) { yama_ptracer_del(task, task); } @@ -116,7 +116,7 @@ static void yama_task_free(struct task_struct *task) * Return 0 on success, -ve on error. -ENOSYS is returned when Yama * does not handle the given option. */ -static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, +int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, unsigned long arg4, unsigned long arg5) { int rc; @@ -243,7 +243,7 @@ static int ptracer_exception_found(struct task_struct *tracer, * * Returns 0 if following the ptrace is allowed, -ve on error. */ -static int yama_ptrace_access_check(struct task_struct *child, +int yama_ptrace_access_check(struct task_struct *child, unsigned int mode) { int rc; @@ -296,7 +296,7 @@ static int yama_ptrace_access_check(struct task_struct *child, * * Returns 0 if following the ptrace is allowed, -ve on error. */ -static int yama_ptrace_traceme(struct task_struct *parent) +int yama_ptrace_traceme(struct task_struct *parent) { int rc; @@ -330,6 +330,7 @@ static int yama_ptrace_traceme(struct task_struct *parent) return rc; } +#ifndef CONFIG_SECURITY_YAMA_STACKED static struct security_operations yama_ops = { .name = "yama", @@ -338,6 +339,7 @@ static struct security_operations yama_ops = { .task_prctl = yama_task_prctl, .task_free = yama_task_free, }; +#endif #ifdef CONFIG_SYSCTL static int yama_dointvec_minmax(struct ctl_table *table, int write, @@ -384,13 +386,17 @@ static struct ctl_table yama_sysctl_table[] = { static __init int yama_init(void) { +#ifndef CONFIG_SECURITY_YAMA_STACKED if (!security_module_enable(&yama_ops)) return 0; +#endif printk(KERN_INFO "Yama: becoming mindful.\n"); +#ifndef CONFIG_SECURITY_YAMA_STACKED if (register_security(&yama_ops)) panic("Yama: kernel registration failed.\n"); +#endif #ifdef CONFIG_SYSCTL if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table)) From 2e4930eb7c8fb20a39dfb5f8a8f80402710dcea8 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Mon, 27 Aug 2012 11:38:13 -0700 Subject: [PATCH 18/39] Yama: handle 32-bit userspace prctl When running a 64-bit kernel and receiving prctls from a 32-bit userspace, the "-1" used as an unsigned long will end up being misdetected. The kernel is looking for 0xffffffffffffffff instead of 0xffffffff. Since prctl lacks a distinct compat interface, Yama needs to handle this translation itself. As such, support either value as meaning PR_SET_PTRACER_ANY, to avoid breaking the ABI for 64-bit. Signed-off-by: Kees Cook Acked-by: John Johansen Cc: stable@vger.kernel.org Signed-off-by: James Morris --- security/yama/yama_lsm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/yama/yama_lsm.c b/security/yama/yama_lsm.c index 9ca43c1ea630..01d3b44b62c1 100644 --- a/security/yama/yama_lsm.c +++ b/security/yama/yama_lsm.c @@ -143,7 +143,7 @@ int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3, if (arg2 == 0) { yama_ptracer_del(NULL, myself); rc = 0; - } else if (arg2 == PR_SET_PTRACER_ANY) { + } else if (arg2 == PR_SET_PTRACER_ANY || (int)arg2 == -1) { rc = yama_ptracer_add(NULL, myself); } else { struct task_struct *tracer; From 2ab51f3721f7abdf92d89cb79d3d6c0062ddc14b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 23 Mar 2011 17:23:06 -0400 Subject: [PATCH 19/39] vfs: extend vfs_removexattr locking This patch takes the i_mutex lock before security_inode_removexattr(), instead of after, in preparation of calling ima_inode_removexattr(). Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- fs/xattr.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/fs/xattr.c b/fs/xattr.c index 4d45b7189e7e..107f457143c3 100644 --- a/fs/xattr.c +++ b/fs/xattr.c @@ -295,11 +295,13 @@ vfs_removexattr(struct dentry *dentry, const char *name) if (error) return error; - error = security_inode_removexattr(dentry, name); - if (error) - return error; - mutex_lock(&inode->i_mutex); + error = security_inode_removexattr(dentry, name); + if (error) { + mutex_unlock(&inode->i_mutex); + return error; + } + error = inode->i_op->removexattr(dentry, name); mutex_unlock(&inode->i_mutex); From 4199d35cbc90c15db447d115bd96ffa5f1d60d3a Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 16 Mar 2011 22:48:43 -0400 Subject: [PATCH 20/39] vfs: move ima_file_free before releasing the file ima_file_free(), called on __fput(), currently flags files that have changed, so that the file is re-measured. For appraising a files's integrity, the file's hash must be re-calculated and stored in the 'security.ima' xattr to reflect any changes. This patch moves the ima_file_free() call to before releasing the file in preparation of ima-appraisal measuring the file and updating the 'security.ima' xattr. Signed-off-by: Mimi Zohar Acked-by: Serge Hallyn Acked-by: Dmitry Kasatkin --- fs/file_table.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/fs/file_table.c b/fs/file_table.c index 701985e4ccda..a41f23f90b17 100644 --- a/fs/file_table.c +++ b/fs/file_table.c @@ -243,10 +243,10 @@ static void __fput(struct file *file) if (file->f_op && file->f_op->fasync) file->f_op->fasync(-1, file, 0); } + ima_file_free(file); if (file->f_op && file->f_op->release) file->f_op->release(inode, file); security_file_free(file); - ima_file_free(file); if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL && !(file->f_mode & FMODE_PATH))) { cdev_put(inode->i_cdev); From 2fe5d6def1672ae6635dd71867bf36dcfaa7434b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 13 Feb 2012 10:15:05 -0500 Subject: [PATCH 21/39] ima: integrity appraisal extension IMA currently maintains an integrity measurement list used to assert the integrity of the running system to a third party. The IMA-appraisal extension adds local integrity validation and enforcement of the measurement against a "good" value stored as an extended attribute 'security.ima'. The initial methods for validating 'security.ima' are hashed based, which provides file data integrity, and digital signature based, which in addition to providing file data integrity, provides authenticity. This patch creates and maintains the 'security.ima' xattr, containing the file data hash measurement. Protection of the xattr is provided by EVM, if enabled and configured. Based on policy, IMA calls evm_verifyxattr() to verify a file's metadata integrity and, assuming success, compares the file's current hash value with the one stored as an extended attribute in 'security.ima'. Changelov v4: - changed iint cache flags to hex values Changelog v3: - change appraisal default for filesystems without xattr support to fail Changelog v2: - fix audit msg 'res' value - removed unused 'ima_appraise=' values Changelog v1: - removed unused iint mutex (Dmitry Kasatkin) - setattr hook must not reset appraised (Dmitry Kasatkin) - evm_verifyxattr() now differentiates between no 'security.evm' xattr (INTEGRITY_NOLABEL) and no EVM 'protected' xattrs included in the 'security.evm' (INTEGRITY_NOXATTRS). - replace hash_status with ima_status (Dmitry Kasatkin) - re-initialize slab element ima_status on free (Dmitry Kasatkin) - include 'security.ima' in EVM if CONFIG_IMA_APPRAISE, not CONFIG_IMA - merged half "ima: ima_must_appraise_or_measure API change" (Dmitry Kasatkin) - removed unnecessary error variable in process_measurement() (Dmitry Kasatkin) - use ima_inode_post_setattr() stub function, if IMA_APPRAISE not configured (moved ima_inode_post_setattr() to ima_appraise.c) - make sure ima_collect_measurement() can read file Changelog: - add 'iint' to evm_verifyxattr() call (Dimitry Kasatkin) - fix the race condition between chmod, which takes the i_mutex and then iint->mutex, and ima_file_free() and process_measurement(), which take the locks in the reverse order, by eliminating iint->mutex. (Dmitry Kasatkin) - cleanup of ima_appraise_measurement() (Dmitry Kasatkin) - changes as a result of the iint not allocated for all regular files, but only for those measured/appraised. - don't try to appraise new/empty files - expanded ima_appraisal description in ima/Kconfig - IMA appraise definitions required even if IMA_APPRAISE not enabled - add return value to ima_must_appraise() stub - unconditionally set status = INTEGRITY_PASS *after* testing status, not before. (Found by Joe Perches) Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- Documentation/kernel-parameters.txt | 4 + include/linux/xattr.h | 3 + security/integrity/evm/evm_main.c | 3 + security/integrity/iint.c | 3 +- security/integrity/ima/Kconfig | 15 +++ security/integrity/ima/Makefile | 1 + security/integrity/ima/ima.h | 37 +++++- security/integrity/ima/ima_api.c | 50 +++++--- security/integrity/ima/ima_appraise.c | 168 ++++++++++++++++++++++++++ security/integrity/ima/ima_crypto.c | 8 +- security/integrity/ima/ima_main.c | 79 ++++++++---- security/integrity/ima/ima_policy.c | 32 +++-- security/integrity/integrity.h | 8 +- 13 files changed, 358 insertions(+), 53 deletions(-) create mode 100644 security/integrity/ima/ima_appraise.c diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index ad7e2e5088c1..fa09e64d9877 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1051,6 +1051,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ihash_entries= [KNL] Set number of hash buckets for inode cache. + ima_appraise= [IMA] appraise integrity measurements + Format: { "off" | "enforce" | "fix" } + default: "enforce" + ima_audit= [IMA] Format: { "0" | "1" } 0 -- integrity auditing messages. (Default) diff --git a/include/linux/xattr.h b/include/linux/xattr.h index e5d122031542..77a3e686d566 100644 --- a/include/linux/xattr.h +++ b/include/linux/xattr.h @@ -33,6 +33,9 @@ #define XATTR_EVM_SUFFIX "evm" #define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX +#define XATTR_IMA_SUFFIX "ima" +#define XATTR_NAME_IMA XATTR_SECURITY_PREFIX XATTR_IMA_SUFFIX + #define XATTR_SELINUX_SUFFIX "selinux" #define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX diff --git a/security/integrity/evm/evm_main.c b/security/integrity/evm/evm_main.c index 8901501425f4..eb5484504f50 100644 --- a/security/integrity/evm/evm_main.c +++ b/security/integrity/evm/evm_main.c @@ -33,6 +33,9 @@ char *evm_config_xattrnames[] = { #endif #ifdef CONFIG_SECURITY_SMACK XATTR_NAME_SMACK, +#endif +#ifdef CONFIG_IMA_APPRAISE + XATTR_NAME_IMA, #endif XATTR_NAME_CAPS, NULL diff --git a/security/integrity/iint.c b/security/integrity/iint.c index 399641c3e846..e600986aa49f 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -74,6 +74,7 @@ static void iint_free(struct integrity_iint_cache *iint) { iint->version = 0; iint->flags = 0UL; + iint->ima_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; kmem_cache_free(iint_cache, iint); } @@ -157,7 +158,7 @@ static void init_once(void *foo) memset(iint, 0, sizeof *iint); iint->version = 0; iint->flags = 0UL; - mutex_init(&iint->mutex); + iint->ima_status = INTEGRITY_UNKNOWN; iint->evm_status = INTEGRITY_UNKNOWN; } diff --git a/security/integrity/ima/Kconfig b/security/integrity/ima/Kconfig index 809ccf19d09c..d232c73647ae 100644 --- a/security/integrity/ima/Kconfig +++ b/security/integrity/ima/Kconfig @@ -56,3 +56,18 @@ config IMA_LSM_RULES default y help Disabling this option will disregard LSM based policy rules. + +config IMA_APPRAISE + bool "Appraise integrity measurements" + depends on IMA + default n + help + This option enables local measurement integrity appraisal. + It requires the system to be labeled with a security extended + attribute containing the file hash measurement. To protect + the security extended attributes from offline attack, enable + and configure EVM. + + For more information on integrity appraisal refer to: + + If unsure, say N. diff --git a/security/integrity/ima/Makefile b/security/integrity/ima/Makefile index 5f740f6971e1..3f2ca6bdc384 100644 --- a/security/integrity/ima/Makefile +++ b/security/integrity/ima/Makefile @@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \ ima_policy.o ima-$(CONFIG_IMA_AUDIT) += ima_audit.o +ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index e7c99fd0d223..069a4aa63e95 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 }; extern int ima_initialized; extern int ima_used_chip; extern char *ima_hash; +extern int ima_appraise; /* IMA inode template definition */ struct ima_template_data { @@ -107,6 +108,7 @@ static inline unsigned long ima_hash_key(u8 *digest) } /* LIM API function definitions */ +int ima_must_appraise_or_measure(struct inode *inode, int mask, int function); int ima_must_measure(struct inode *inode, int mask, int function); int ima_collect_measurement(struct integrity_iint_cache *iint, struct file *file); @@ -123,14 +125,45 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK }; +enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR }; -int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask); +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, + int flags); void ima_init_policy(void); void ima_update_policy(void); ssize_t ima_parse_add_rule(char *); void ima_delete_rules(void); +/* Appraise integrity measurements */ +#define IMA_APPRAISE_ENFORCE 0x01 +#define IMA_APPRAISE_FIX 0x02 + +#ifdef CONFIG_IMA_APPRAISE +int ima_appraise_measurement(struct integrity_iint_cache *iint, + struct file *file, const unsigned char *filename); +int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask); +void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file); + +#else +static inline int ima_appraise_measurement(struct integrity_iint_cache *iint, + struct file *file, + const unsigned char *filename) +{ + return INTEGRITY_UNKNOWN; +} + +static inline int ima_must_appraise(struct inode *inode, + enum ima_hooks func, int mask) +{ + return 0; +} + +static inline void ima_update_xattr(struct integrity_iint_cache *iint, + struct file *file) +{ +} +#endif + /* LSM based policy rules require audit */ #ifdef CONFIG_IMA_LSM_RULES diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 032ff03ad907..41cce84416c5 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -9,13 +9,17 @@ * License. * * File: ima_api.c - * Implements must_measure, collect_measurement, store_measurement, - * and store_template. + * Implements must_appraise_or_measure, collect_measurement, + * appraise_measurement, store_measurement and store_template. */ #include #include - +#include +#include +#include +#include #include "ima.h" + static const char *IMA_TEMPLATE_NAME = "ima"; /* @@ -93,7 +97,7 @@ err_out: } /** - * ima_must_measure - measure decision based on policy. + * ima_must_appraise_or_measure - appraise & measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) @@ -105,15 +109,22 @@ err_out: * mask: contains the permission mask * fsmagic: hex value * - * Return 0 to measure. For matching a DONT_MEASURE policy, no policy, - * or other error, return an error code. -*/ + * Returns IMA_MEASURE, IMA_APPRAISE mask. + * + */ +int ima_must_appraise_or_measure(struct inode *inode, int mask, int function) +{ + int flags = IMA_MEASURE | IMA_APPRAISE; + + if (!ima_appraise) + flags &= ~IMA_APPRAISE; + + return ima_match_policy(inode, function, mask, flags); +} + int ima_must_measure(struct inode *inode, int mask, int function) { - int must_measure; - - must_measure = ima_match_policy(inode, function, mask); - return must_measure ? 0 : -EACCES; + return ima_match_policy(inode, function, mask, IMA_MEASURE); } /* @@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function) int ima_collect_measurement(struct integrity_iint_cache *iint, struct file *file) { - int result = -EEXIST; + struct inode *inode = file->f_dentry->d_inode; + const char *filename = file->f_dentry->d_name.name; + int result = 0; - if (!(iint->flags & IMA_MEASURED)) { + if (!(iint->flags & IMA_COLLECTED)) { u64 i_version = file->f_dentry->d_inode->i_version; memset(iint->digest, 0, IMA_DIGEST_SIZE); result = ima_calc_hash(file, iint->digest); - if (!result) + if (!result) { iint->version = i_version; + iint->flags |= IMA_COLLECTED; + } } + if (result) + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, + filename, "collect_data", "failed", + result, 0); return result; } @@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint, struct ima_template_entry *entry; int violation = 0; + if (iint->flags & IMA_MEASURED) + return; + entry = kmalloc(sizeof(*entry), GFP_KERNEL); if (!entry) { integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c new file mode 100644 index 000000000000..4865f61f9044 --- /dev/null +++ b/security/integrity/ima/ima_appraise.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2011 IBM Corporation + * + * Author: + * Mimi Zohar + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 2 of the License. + */ +#include +#include +#include +#include +#include +#include +#include + +#include "ima.h" + +static int __init default_appraise_setup(char *str) +{ + if (strncmp(str, "off", 3) == 0) + ima_appraise = 0; + else if (strncmp(str, "fix", 3) == 0) + ima_appraise = IMA_APPRAISE_FIX; + return 1; +} + +__setup("ima_appraise=", default_appraise_setup); + +/* + * ima_must_appraise - set appraise flag + * + * Return 1 to appraise + */ +int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask) +{ + return 0; +} + +static void ima_fix_xattr(struct dentry *dentry, + struct integrity_iint_cache *iint) +{ + iint->digest[0] = IMA_XATTR_DIGEST; + __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, + iint->digest, IMA_DIGEST_SIZE + 1, 0); +} + +/* + * ima_appraise_measurement - appraise file measurement + * + * Call evm_verifyxattr() to verify the integrity of 'security.ima'. + * Assuming success, compare the xattr hash with the collected measurement. + * + * Return 0 on success, error code otherwise + */ +int ima_appraise_measurement(struct integrity_iint_cache *iint, + struct file *file, const unsigned char *filename) +{ + struct dentry *dentry = file->f_dentry; + struct inode *inode = dentry->d_inode; + u8 xattr_value[IMA_DIGEST_SIZE]; + enum integrity_status status = INTEGRITY_UNKNOWN; + const char *op = "appraise_data"; + char *cause = "unknown"; + int rc; + + if (!ima_appraise) + return 0; + if (!inode->i_op->getxattr) + return INTEGRITY_UNKNOWN; + + if (iint->flags & IMA_APPRAISED) + return iint->ima_status; + + rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, xattr_value, + IMA_DIGEST_SIZE); + if (rc <= 0) { + if (rc && rc != -ENODATA) + goto out; + + cause = "missing-hash"; + status = + (inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL; + goto out; + } + + status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); + if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { + if ((status == INTEGRITY_NOLABEL) + || (status == INTEGRITY_NOXATTRS)) + cause = "missing-HMAC"; + else if (status == INTEGRITY_FAIL) + cause = "invalid-HMAC"; + goto out; + } + + rc = memcmp(xattr_value, iint->digest, IMA_DIGEST_SIZE); + if (rc) { + status = INTEGRITY_FAIL; + cause = "invalid-hash"; + print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, + xattr_value, IMA_DIGEST_SIZE); + print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, + iint->digest, IMA_DIGEST_SIZE); + goto out; + } + status = INTEGRITY_PASS; + iint->flags |= IMA_APPRAISED; +out: + if (status != INTEGRITY_PASS) { + if (ima_appraise & IMA_APPRAISE_FIX) { + ima_fix_xattr(dentry, iint); + status = INTEGRITY_PASS; + } + integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, + op, cause, rc, 0); + } + iint->ima_status = status; + return status; +} + +/* + * ima_update_xattr - update 'security.ima' hash value + */ +void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) +{ + struct dentry *dentry = file->f_dentry; + int rc = 0; + + rc = ima_collect_measurement(iint, file); + if (rc < 0) + return; + ima_fix_xattr(dentry, iint); +} + +/** + * ima_inode_post_setattr - reflect file metadata changes + * @dentry: pointer to the affected dentry + * + * Changes to a dentry's metadata might result in needing to appraise. + * + * This function is called from notify_change(), which expects the caller + * to lock the inode's i_mutex. + */ +void ima_inode_post_setattr(struct dentry *dentry) +{ + struct inode *inode = dentry->d_inode; + struct integrity_iint_cache *iint; + int must_appraise, rc; + + if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode) + || !inode->i_op->removexattr) + return; + + must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR); + iint = integrity_iint_find(inode); + if (iint) { + if (must_appraise) + iint->flags |= IMA_APPRAISE; + else + iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED); + } + if (!must_appraise) + rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); + return; +} diff --git a/security/integrity/ima/ima_crypto.c b/security/integrity/ima/ima_crypto.c index 9b3ade7468b2..b21ee5b5495a 100644 --- a/security/integrity/ima/ima_crypto.c +++ b/security/integrity/ima/ima_crypto.c @@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest) struct scatterlist sg[1]; loff_t i_size, offset = 0; char *rbuf; - int rc; + int rc, read = 0; rc = init_desc(&desc); if (rc != 0) @@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest) rc = -ENOMEM; goto out; } + if (!(file->f_mode & FMODE_READ)) { + file->f_mode |= FMODE_READ; + read = 1; + } i_size = i_size_read(file->f_dentry->d_inode); while (offset < i_size) { int rbuf_len; @@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest) kfree(rbuf); if (!rc) rc = crypto_hash_final(&desc, digest); + if (read) + file->f_mode &= ~FMODE_READ; out: crypto_free_hash(desc.tfm); return rc; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index be8294915cf7..6eb28d47e74b 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -22,12 +22,19 @@ #include #include #include +#include #include #include "ima.h" int ima_initialized; +#ifdef CONFIG_IMA_APPRAISE +int ima_appraise = IMA_APPRAISE_ENFORCE; +#else +int ima_appraise; +#endif + char *ima_hash = "sha1"; static int __init hash_setup(char *str) { @@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file) struct dentry *dentry = file->f_path.dentry; struct inode *inode = dentry->d_inode; fmode_t mode = file->f_mode; - int rc; + int must_measure; bool send_tomtou = false, send_writers = false; unsigned char *pathname = NULL, *pathbuf = NULL; @@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file) goto out; } - rc = ima_must_measure(inode, MAY_READ, FILE_CHECK); - if (rc < 0) + must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK); + if (!must_measure) goto out; if (atomic_read(&inode->i_writecount) > 0) @@ -100,17 +107,21 @@ out: } static void ima_check_last_writer(struct integrity_iint_cache *iint, - struct inode *inode, - struct file *file) + struct inode *inode, struct file *file) { fmode_t mode = file->f_mode; - mutex_lock(&iint->mutex); - if (mode & FMODE_WRITE && - atomic_read(&inode->i_writecount) == 1 && - iint->version != inode->i_version) - iint->flags &= ~IMA_MEASURED; - mutex_unlock(&iint->mutex); + if (!(mode & FMODE_WRITE)) + return; + + mutex_lock(&inode->i_mutex); + if (atomic_read(&inode->i_writecount) == 1 && + iint->version != inode->i_version) { + iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED); + if (iint->flags & IMA_APPRAISE) + ima_update_xattr(iint, file); + } + mutex_unlock(&inode->i_mutex); } /** @@ -140,14 +151,17 @@ static int process_measurement(struct file *file, const unsigned char *filename, struct inode *inode = file->f_dentry->d_inode; struct integrity_iint_cache *iint; unsigned char *pathname = NULL, *pathbuf = NULL; - int rc = 0; + int rc = -ENOMEM, action, must_appraise; if (!ima_initialized || !S_ISREG(inode->i_mode)) return 0; - rc = ima_must_measure(inode, mask, function); - if (rc != 0) - return rc; + /* Determine if in appraise/measurement policy, + * returns IMA_MEASURE, IMA_APPRAISE bitmask. */ + action = ima_must_appraise_or_measure(inode, mask, function); + if (!action) + return 0; + retry: iint = integrity_iint_find(inode); if (!iint) { @@ -157,11 +171,21 @@ retry: return rc; } - mutex_lock(&iint->mutex); + must_appraise = action & IMA_APPRAISE; - rc = iint->flags & IMA_MEASURED ? 1 : 0; - if (rc != 0) + mutex_lock(&inode->i_mutex); + + /* Determine if already appraised/measured based on bitmask + * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */ + iint->flags |= action; + action &= ~((iint->flags & (IMA_MEASURED | IMA_APPRAISED)) >> 1); + + /* Nothing to do, just return existing appraised status */ + if (!action) { + if (iint->flags & IMA_APPRAISED) + rc = iint->ima_status; goto out; + } rc = ima_collect_measurement(iint, file); if (rc != 0) @@ -177,11 +201,16 @@ retry: pathname = NULL; } } - ima_store_measurement(iint, file, !pathname ? filename : pathname); + if (action & IMA_MEASURE) + ima_store_measurement(iint, file, + !pathname ? filename : pathname); + if (action & IMA_APPRAISE) + rc = ima_appraise_measurement(iint, file, + !pathname ? filename : pathname); kfree(pathbuf); out: - mutex_unlock(&iint->mutex); - return rc; + mutex_unlock(&inode->i_mutex); + return (rc && must_appraise) ? -EACCES : 0; } /** @@ -197,14 +226,14 @@ out: */ int ima_file_mmap(struct file *file, unsigned long prot) { - int rc; + int rc = 0; if (!file) return 0; if (prot & PROT_EXEC) rc = process_measurement(file, file->f_dentry->d_name.name, MAY_EXEC, FILE_MMAP); - return 0; + return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; } /** @@ -228,7 +257,7 @@ int ima_bprm_check(struct linux_binprm *bprm) (strcmp(bprm->filename, bprm->interp) == 0) ? bprm->filename : bprm->interp, MAY_EXEC, BPRM_CHECK); - return 0; + return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; } /** @@ -249,7 +278,7 @@ int ima_file_check(struct file *file, int mask) rc = process_measurement(file, file->f_dentry->d_name.name, mask & (MAY_READ | MAY_WRITE | MAY_EXEC), FILE_CHECK); - return 0; + return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0; } EXPORT_SYMBOL_GPL(ima_file_check); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 1a9583008aae..3e22e17da295 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -25,7 +25,13 @@ #define IMA_FSMAGIC 0x0004 #define IMA_UID 0x0008 -enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE }; +#define UNKNOWN 0 +#define MEASURE 1 /* same as IMA_MEASURE */ +#define DONT_MEASURE 2 +#define MEASURE_MASK 3 +#define APPRAISE 4 /* same as IMA_APPRAISE */ +#define DONT_APPRAISE 8 +#define APPRAISE_MASK 12 #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, @@ -34,7 +40,7 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, struct ima_measure_rule_entry { struct list_head list; - enum ima_action action; + int action; unsigned int flags; enum ima_hooks func; int mask; @@ -163,18 +169,28 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, * as elements in the list are never deleted, nor does the list * change.) */ -int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask) +int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, + int flags) { struct ima_measure_rule_entry *entry; + int action = 0, actmask = flags | (flags << 1); list_for_each_entry(entry, ima_measure, list) { - bool rc; - rc = ima_match_rules(entry, inode, func, mask); - if (rc) - return entry->action; + if (!(entry->action & actmask)) + continue; + + if (!ima_match_rules(entry, inode, func, mask)) + continue; + + action |= (entry->action & (IMA_APPRAISE | IMA_MEASURE)); + actmask &= (entry->action & APPRAISE_MASK) ? + ~APPRAISE_MASK : ~MEASURE_MASK; + if (!actmask) + break; } - return 0; + + return action; } /** diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 7a25ecec5aaa..dac6b68e945a 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -16,7 +16,11 @@ #include /* iint cache flags */ -#define IMA_MEASURED 0x01 +#define IMA_MEASURE 0x01 +#define IMA_MEASURED 0x02 +#define IMA_APPRAISE 0x04 +#define IMA_APPRAISED 0x08 +#define IMA_COLLECTED 0x10 enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, @@ -36,7 +40,7 @@ struct integrity_iint_cache { u64 version; /* track inode changes */ unsigned char flags; u8 digest[SHA1_DIGEST_SIZE]; - struct mutex mutex; /* protects: version, flags, digest */ + enum integrity_status ima_status; enum integrity_status evm_status; }; From 07f6a79415d7d502ee0c7d02ace6594a7be7429a Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 9 Mar 2011 22:25:48 -0500 Subject: [PATCH 22/39] ima: add appraise action keywords and default rules Unlike the IMA measurement policy, the appraise policy can not be dependent on runtime process information, such as the task uid, as the 'security.ima' xattr is written on file close and must be updated each time the file changes, regardless of the current task uid. This patch extends the policy language with 'fowner', defines an appraise policy, which appraises all files owned by root, and defines 'ima_appraise_tcb', a new boot command line option, to enable the appraise policy. Changelog v3: - separate the measure from the appraise rules in order to support measuring without appraising and appraising without measuring. - change appraisal default for filesystems without xattr support to fail - update default appraise policy for cgroups Changelog v1: - don't appraise RAMFS (Dmitry Kasatkin) - merged rest of "ima: ima_must_appraise_or_measure API change" commit (Dmtiry Kasatkin) ima_must_appraise_or_measure() called ima_match_policy twice, which searched the policy for a matching rule. Once for a matching measurement rule and subsequently for an appraisal rule. Searching the policy twice is unnecessary overhead, which could be noticeable with a large policy. The new version of ima_must_appraise_or_measure() does everything in a single iteration using a new version of ima_match_policy(). It returns IMA_MEASURE, IMA_APPRAISE mask. With the use of action mask only one efficient matching function is enough. Removed other specific versions of matching functions. Changelog: - change 'owner' to 'fowner' to conform to the new LSM conditions posted by Roberto Sassu. - fix calls to ima_log_string() Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- Documentation/ABI/testing/ima_policy | 25 ++++- Documentation/kernel-parameters.txt | 4 + security/integrity/ima/ima_appraise.c | 5 +- security/integrity/ima/ima_policy.c | 149 +++++++++++++++++++------- 4 files changed, 140 insertions(+), 43 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 6cd6daefaaed..dcff82205477 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -12,11 +12,14 @@ Description: then closing the file. The new policy takes effect after the file ima/policy is closed. + IMA appraisal, if configured, uses these file measurements + for local measurement appraisal. + rule format: action [condition ...] - action: measure | dont_measure + action: measure | dont_measure | appraise | dont_appraise condition:= base | lsm - base: [[func=] [mask=] [fsmagic=] [uid=]] + base: [[func=] [mask=] [fsmagic=] [uid=] [fowner]] lsm: [[subj_user=] [subj_role=] [subj_type=] [obj_user=] [obj_role=] [obj_type=]] @@ -24,36 +27,50 @@ Description: mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] fsmagic:= hex value uid:= decimal value + fowner:=decimal value lsm: are LSM specific default policy: # PROC_SUPER_MAGIC dont_measure fsmagic=0x9fa0 + dont_appraise fsmagic=0x9fa0 # SYSFS_MAGIC dont_measure fsmagic=0x62656572 + dont_appraise fsmagic=0x62656572 # DEBUGFS_MAGIC dont_measure fsmagic=0x64626720 + dont_appraise fsmagic=0x64626720 # TMPFS_MAGIC dont_measure fsmagic=0x01021994 + dont_appraise fsmagic=0x01021994 + # RAMFS_MAGIC + dont_measure fsmagic=0x858458f6 + dont_appraise fsmagic=0x858458f6 # SECURITYFS_MAGIC dont_measure fsmagic=0x73636673 + dont_appraise fsmagic=0x73636673 measure func=BPRM_CHECK measure func=FILE_MMAP mask=MAY_EXEC measure func=FILE_CHECK mask=MAY_READ uid=0 + appraise fowner=0 The default policy measures all executables in bprm_check, all files mmapped executable in file_mmap, and all files - open for read by root in do_filp_open. + open for read by root in do_filp_open. The default appraisal + policy appraises all files owned by root. Examples of LSM specific definitions: SELinux: # SELINUX_MAGIC - dont_measure fsmagic=0xF97CFF8C + dont_measure fsmagic=0xf97cff8c + dont_appraise fsmagic=0xf97cff8c dont_measure obj_type=var_log_t + dont_appraise obj_type=var_log_t dont_measure obj_type=auditd_log_t + dont_appraise obj_type=auditd_log_t measure subj_user=system_u func=FILE_CHECK mask=MAY_READ measure subj_role=system_r func=FILE_CHECK mask=MAY_READ diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index fa09e64d9877..949dddcfd177 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1055,6 +1055,10 @@ bytes respectively. Such letter suffixes can also be entirely omitted. Format: { "off" | "enforce" | "fix" } default: "enforce" + ima_appraise_tcb [IMA] + The builtin appraise policy appraises all files + owned by uid=0. + ima_audit= [IMA] Format: { "0" | "1" } 0 -- integrity auditing messages. (Default) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 4865f61f9044..681cb6e72257 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -36,7 +36,10 @@ __setup("ima_appraise=", default_appraise_setup); */ int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask) { - return 0; + if (!ima_appraise) + return 0; + + return ima_match_policy(inode, func, mask, IMA_APPRAISE); } static void ima_fix_xattr(struct dentry *dentry, diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 3e22e17da295..0d6d60b4ba6f 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -24,6 +24,7 @@ #define IMA_MASK 0x0002 #define IMA_FSMAGIC 0x0004 #define IMA_UID 0x0008 +#define IMA_FOWNER 0x0010 #define UNKNOWN 0 #define MEASURE 1 /* same as IMA_MEASURE */ @@ -38,7 +39,7 @@ enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE }; -struct ima_measure_rule_entry { +struct ima_rule_entry { struct list_head list; int action; unsigned int flags; @@ -46,6 +47,7 @@ struct ima_measure_rule_entry { int mask; unsigned long fsmagic; uid_t uid; + uid_t fowner; struct { void *rule; /* LSM file metadata specific */ int type; /* audit type */ @@ -54,7 +56,7 @@ struct ima_measure_rule_entry { /* * Without LSM specific knowledge, the default policy can only be - * written in terms of .action, .func, .mask, .fsmagic, and .uid + * written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner */ /* @@ -63,7 +65,7 @@ struct ima_measure_rule_entry { * normal users can easily run the machine out of memory simply building * and running executables. */ -static struct ima_measure_rule_entry default_rules[] = { +static struct ima_rule_entry default_rules[] = { {.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, {.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, @@ -81,19 +83,41 @@ static struct ima_measure_rule_entry default_rules[] = { .flags = IMA_FUNC | IMA_MASK | IMA_UID}, }; -static LIST_HEAD(measure_default_rules); -static LIST_HEAD(measure_policy_rules); -static struct list_head *ima_measure; +static struct ima_rule_entry default_appraise_rules[] = { + {.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC}, + {.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC}, + {.action = APPRAISE,.fowner = 0,.flags = IMA_FOWNER}, +}; -static DEFINE_MUTEX(ima_measure_mutex); +static LIST_HEAD(ima_default_rules); +static LIST_HEAD(ima_policy_rules); +static struct list_head *ima_rules; + +static DEFINE_MUTEX(ima_rules_mutex); static bool ima_use_tcb __initdata; -static int __init default_policy_setup(char *str) +static int __init default_measure_policy_setup(char *str) { ima_use_tcb = 1; return 1; } -__setup("ima_tcb", default_policy_setup); +__setup("ima_tcb", default_measure_policy_setup); + +static bool ima_use_appraise_tcb __initdata; +static int __init default_appraise_policy_setup(char *str) +{ + ima_use_appraise_tcb = 1; + return 1; +} +__setup("ima_appraise_tcb", default_appraise_policy_setup); /** * ima_match_rules - determine whether an inode matches the measure rule. @@ -104,7 +128,7 @@ __setup("ima_tcb", default_policy_setup); * * Returns true on rule match, false on failure. */ -static bool ima_match_rules(struct ima_measure_rule_entry *rule, +static bool ima_match_rules(struct ima_rule_entry *rule, struct inode *inode, enum ima_hooks func, int mask) { struct task_struct *tsk = current; @@ -120,6 +144,8 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, return false; if ((rule->flags & IMA_UID) && rule->uid != cred->uid) return false; + if ((rule->flags & IMA_FOWNER) && rule->fowner != inode->i_uid) + return false; for (i = 0; i < MAX_LSM_RULES; i++) { int rc = 0; u32 osid, sid; @@ -172,10 +198,10 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule, int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags) { - struct ima_measure_rule_entry *entry; + struct ima_rule_entry *entry; int action = 0, actmask = flags | (flags << 1); - list_for_each_entry(entry, ima_measure, list) { + list_for_each_entry(entry, ima_rules, list) { if (!(entry->action & actmask)) continue; @@ -196,22 +222,31 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, /** * ima_init_policy - initialize the default measure rules. * - * ima_measure points to either the measure_default_rules or the - * the new measure_policy_rules. + * ima_rules points to either the ima_default_rules or the + * the new ima_policy_rules. */ void __init ima_init_policy(void) { - int i, entries; + int i, measure_entries, appraise_entries; /* if !ima_use_tcb set entries = 0 so we load NO default rules */ - if (ima_use_tcb) - entries = ARRAY_SIZE(default_rules); - else - entries = 0; + measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0; + appraise_entries = ima_use_appraise_tcb ? + ARRAY_SIZE(default_appraise_rules) : 0; + + for (i = 0; i < measure_entries + appraise_entries; i++) { + if (i < measure_entries) + list_add_tail(&default_rules[i].list, + &ima_default_rules); + else { + int j = i - measure_entries; - for (i = 0; i < entries; i++) - list_add_tail(&default_rules[i].list, &measure_default_rules); - ima_measure = &measure_default_rules; + list_add_tail(&default_appraise_rules[j].list, + &ima_default_rules); + } + } + + ima_rules = &ima_default_rules; } /** @@ -228,8 +263,8 @@ void ima_update_policy(void) int result = 1; int audit_info = 0; - if (ima_measure == &measure_default_rules) { - ima_measure = &measure_policy_rules; + if (ima_rules == &ima_default_rules) { + ima_rules = &ima_policy_rules; cause = "complete"; result = 0; } @@ -240,14 +275,17 @@ void ima_update_policy(void) enum { Opt_err = -1, Opt_measure = 1, Opt_dont_measure, + Opt_appraise, Opt_dont_appraise, Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, - Opt_func, Opt_mask, Opt_fsmagic, Opt_uid + Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner }; static match_table_t policy_tokens = { {Opt_measure, "measure"}, {Opt_dont_measure, "dont_measure"}, + {Opt_appraise, "appraise"}, + {Opt_dont_appraise, "dont_appraise"}, {Opt_obj_user, "obj_user=%s"}, {Opt_obj_role, "obj_role=%s"}, {Opt_obj_type, "obj_type=%s"}, @@ -258,10 +296,11 @@ static match_table_t policy_tokens = { {Opt_mask, "mask=%s"}, {Opt_fsmagic, "fsmagic=%s"}, {Opt_uid, "uid=%s"}, + {Opt_fowner, "fowner=%s"}, {Opt_err, NULL} }; -static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry, +static int ima_lsm_rule_init(struct ima_rule_entry *entry, char *args, int lsm_rule, int audit_type) { int result; @@ -285,7 +324,7 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value) audit_log_format(ab, " "); } -static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) +static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) { struct audit_buffer *ab; char *p; @@ -294,6 +333,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE); entry->uid = -1; + entry->fowner = -1; entry->action = UNKNOWN; while ((p = strsep(&rule, " \t")) != NULL) { substring_t args[MAX_OPT_ARGS]; @@ -322,11 +362,27 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->action = DONT_MEASURE; break; + case Opt_appraise: + ima_log_string(ab, "action", "appraise"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = APPRAISE; + break; + case Opt_dont_appraise: + ima_log_string(ab, "action", "dont_appraise"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = DONT_APPRAISE; + break; case Opt_func: ima_log_string(ab, "func", args[0].from); if (entry->func) - result = -EINVAL; + result = -EINVAL; if (strcmp(args[0].from, "FILE_CHECK") == 0) entry->func = FILE_CHECK; @@ -391,6 +447,23 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) entry->flags |= IMA_UID; } break; + case Opt_fowner: + ima_log_string(ab, "fowner", args[0].from); + + if (entry->fowner != -1) { + result = -EINVAL; + break; + } + + result = strict_strtoul(args[0].from, 10, &lnum); + if (!result) { + entry->fowner = (uid_t) lnum; + if (entry->fowner != lnum) + result = -EINVAL; + else + entry->flags |= IMA_FOWNER; + } + break; case Opt_obj_user: ima_log_string(ab, "obj_user", args[0].from); result = ima_lsm_rule_init(entry, args[0].from, @@ -442,7 +515,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry) } /** - * ima_parse_add_rule - add a rule to measure_policy_rules + * ima_parse_add_rule - add a rule to ima_policy_rules * @rule - ima measurement policy rule * * Uses a mutex to protect the policy list from multiple concurrent writers. @@ -452,12 +525,12 @@ ssize_t ima_parse_add_rule(char *rule) { const char *op = "update_policy"; char *p; - struct ima_measure_rule_entry *entry; + struct ima_rule_entry *entry; ssize_t result, len; int audit_info = 0; /* Prevent installed policy from changing */ - if (ima_measure != &measure_default_rules) { + if (ima_rules != &ima_default_rules) { integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL, NULL, op, "already exists", -EACCES, audit_info); @@ -490,9 +563,9 @@ ssize_t ima_parse_add_rule(char *rule) return result; } - mutex_lock(&ima_measure_mutex); - list_add_tail(&entry->list, &measure_policy_rules); - mutex_unlock(&ima_measure_mutex); + mutex_lock(&ima_rules_mutex); + list_add_tail(&entry->list, &ima_policy_rules); + mutex_unlock(&ima_rules_mutex); return len; } @@ -500,12 +573,12 @@ ssize_t ima_parse_add_rule(char *rule) /* ima_delete_rules called to cleanup invalid policy */ void ima_delete_rules(void) { - struct ima_measure_rule_entry *entry, *tmp; + struct ima_rule_entry *entry, *tmp; - mutex_lock(&ima_measure_mutex); - list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) { + mutex_lock(&ima_rules_mutex); + list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) { list_del(&entry->list); kfree(entry); } - mutex_unlock(&ima_measure_mutex); + mutex_unlock(&ima_rules_mutex); } From bf2276d10ce58ff44ab8857266a6718024496af6 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 19 Oct 2011 12:04:40 +0300 Subject: [PATCH 23/39] ima: allocating iint improvements With IMA-appraisal's removal of the iint mutex and taking the i_mutex instead, allocating the iint becomes a lot simplier, as we don't need to be concerned with two processes racing to allocate the iint. This patch cleans up and improves performance for allocating the iint. - removed redundant double i_mutex locking - combined iint allocation with tree search Changelog v2: - removed the rwlock/read_lock changes from this patch Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- include/linux/integrity.h | 7 ++--- security/integrity/iint.c | 45 +++++++++++++------------------ security/integrity/ima/ima_main.c | 13 +++------ 3 files changed, 27 insertions(+), 38 deletions(-) diff --git a/include/linux/integrity.h b/include/linux/integrity.h index a0c41256cb92..66c5fe9550a5 100644 --- a/include/linux/integrity.h +++ b/include/linux/integrity.h @@ -22,13 +22,14 @@ enum integrity_status { /* List of EVM protected security xattrs */ #ifdef CONFIG_INTEGRITY -extern int integrity_inode_alloc(struct inode *inode); +extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode); extern void integrity_inode_free(struct inode *inode); #else -static inline int integrity_inode_alloc(struct inode *inode) +static inline struct integrity_iint_cache * + integrity_inode_get(struct inode *inode) { - return 0; + return NULL; } static inline void integrity_inode_free(struct inode *inode) diff --git a/security/integrity/iint.c b/security/integrity/iint.c index e600986aa49f..c91a436e13ac 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -80,24 +80,26 @@ static void iint_free(struct integrity_iint_cache *iint) } /** - * integrity_inode_alloc - allocate an iint associated with an inode + * integrity_inode_get - find or allocate an iint associated with an inode * @inode: pointer to the inode + * @return: allocated iint + * + * Caller must lock i_mutex */ -int integrity_inode_alloc(struct inode *inode) +struct integrity_iint_cache *integrity_inode_get(struct inode *inode) { struct rb_node **p; - struct rb_node *new_node, *parent = NULL; - struct integrity_iint_cache *new_iint, *test_iint; - int rc; + struct rb_node *node, *parent = NULL; + struct integrity_iint_cache *iint, *test_iint; - new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS); - if (!new_iint) - return -ENOMEM; + iint = integrity_iint_find(inode); + if (iint) + return iint; - new_iint->inode = inode; - new_node = &new_iint->rb_node; + iint = kmem_cache_alloc(iint_cache, GFP_NOFS); + if (!iint) + return NULL; - mutex_lock(&inode->i_mutex); /* i_flags */ spin_lock(&integrity_iint_lock); p = &integrity_iint_tree.rb_node; @@ -105,29 +107,20 @@ int integrity_inode_alloc(struct inode *inode) parent = *p; test_iint = rb_entry(parent, struct integrity_iint_cache, rb_node); - rc = -EEXIST; if (inode < test_iint->inode) p = &(*p)->rb_left; - else if (inode > test_iint->inode) - p = &(*p)->rb_right; else - goto out_err; + p = &(*p)->rb_right; } + iint->inode = inode; + node = &iint->rb_node; inode->i_flags |= S_IMA; - rb_link_node(new_node, parent, p); - rb_insert_color(new_node, &integrity_iint_tree); + rb_link_node(node, parent, p); + rb_insert_color(node, &integrity_iint_tree); spin_unlock(&integrity_iint_lock); - mutex_unlock(&inode->i_mutex); /* i_flags */ - - return 0; -out_err: - spin_unlock(&integrity_iint_lock); - mutex_unlock(&inode->i_mutex); /* i_flags */ - iint_free(new_iint); - - return rc; + return iint; } /** diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 6eb28d47e74b..df6521296051 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -162,19 +162,14 @@ static int process_measurement(struct file *file, const unsigned char *filename, if (!action) return 0; -retry: - iint = integrity_iint_find(inode); - if (!iint) { - rc = integrity_inode_alloc(inode); - if (!rc || rc == -EEXIST) - goto retry; - return rc; - } - must_appraise = action & IMA_APPRAISE; mutex_lock(&inode->i_mutex); + iint = integrity_inode_get(inode); + if (!iint) + goto out; + /* Determine if already appraised/measured based on bitmask * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */ iint->flags |= action; From a10bf26b2f53242836e9362c6c9c857b627b82a9 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 8 Feb 2012 14:15:42 -0500 Subject: [PATCH 24/39] ima: replace iint spinblock with rwlock/read_lock For performance, replace the iint spinlock with rwlock/read_lock. Eric Paris questioned this change, from spinlocks to rwlocks, saying "rwlocks have been shown to actually be slower on multi processor systems in a number of cases due to the cache line bouncing required." Based on performance measurements compiling the kernel on a cold boot with multiple jobs with/without this patch, Dmitry Kasatkin and I found that rwlocks performed better than spinlocks, but very insignificantly. For example with total compilation time around 6 minutes, with rwlocks time was 1 - 3 seconds shorter... but always like that. Changelog v2: - new patch taken from the 'allocating iint improvements' patch Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/iint.c | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/security/integrity/iint.c b/security/integrity/iint.c index c91a436e13ac..d82a5a13d855 100644 --- a/security/integrity/iint.c +++ b/security/integrity/iint.c @@ -22,7 +22,7 @@ #include "integrity.h" static struct rb_root integrity_iint_tree = RB_ROOT; -static DEFINE_SPINLOCK(integrity_iint_lock); +static DEFINE_RWLOCK(integrity_iint_lock); static struct kmem_cache *iint_cache __read_mostly; int iint_initialized; @@ -35,8 +35,6 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode) struct integrity_iint_cache *iint; struct rb_node *n = integrity_iint_tree.rb_node; - assert_spin_locked(&integrity_iint_lock); - while (n) { iint = rb_entry(n, struct integrity_iint_cache, rb_node); @@ -63,9 +61,9 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode) if (!IS_IMA(inode)) return NULL; - spin_lock(&integrity_iint_lock); + read_lock(&integrity_iint_lock); iint = __integrity_iint_find(inode); - spin_unlock(&integrity_iint_lock); + read_unlock(&integrity_iint_lock); return iint; } @@ -100,7 +98,7 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode) if (!iint) return NULL; - spin_lock(&integrity_iint_lock); + write_lock(&integrity_iint_lock); p = &integrity_iint_tree.rb_node; while (*p) { @@ -119,7 +117,7 @@ struct integrity_iint_cache *integrity_inode_get(struct inode *inode) rb_link_node(node, parent, p); rb_insert_color(node, &integrity_iint_tree); - spin_unlock(&integrity_iint_lock); + write_unlock(&integrity_iint_lock); return iint; } @@ -136,10 +134,10 @@ void integrity_inode_free(struct inode *inode) if (!IS_IMA(inode)) return; - spin_lock(&integrity_iint_lock); + write_lock(&integrity_iint_lock); iint = __integrity_iint_find(inode); rb_erase(&iint->rb_node, &integrity_iint_tree); - spin_unlock(&integrity_iint_lock); + write_unlock(&integrity_iint_lock); iint_free(iint); } From 9957a5043e7b0b7361cdf48eea22b2900293e63a Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Wed, 9 Mar 2011 22:57:53 -0500 Subject: [PATCH 25/39] ima: add inode_post_setattr call Changing an inode's metadata may result in our not needing to appraise the file. In such cases, we must remove 'security.ima'. Changelog v1: - use ima_inode_post_setattr() stub function, if IMA_APPRAISE not configured Signed-off-by: Mimi Zohar Acked-by: Serge Hallyn Acked-by: Dmitry Kasatkin --- fs/attr.c | 2 ++ include/linux/ima.h | 10 ++++++++++ 2 files changed, 12 insertions(+) diff --git a/fs/attr.c b/fs/attr.c index 29e38a1f7f77..cce7df53b694 100644 --- a/fs/attr.c +++ b/fs/attr.c @@ -14,6 +14,7 @@ #include #include #include +#include /** * inode_change_ok - check if attribute changes to an inode are allowed @@ -247,6 +248,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr) if (!error) { fsnotify_change(dentry, ia_valid); + ima_inode_post_setattr(dentry); evm_inode_post_setattr(dentry, ia_valid); } diff --git a/include/linux/ima.h b/include/linux/ima.h index 6ac8e50c6cf5..e2bfbb1e9af6 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -39,5 +39,15 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) { return 0; } + #endif /* CONFIG_IMA_H */ + +#ifdef CONFIG_IMA_APPRAISE +extern void ima_inode_post_setattr(struct dentry *dentry); +#else +static inline void ima_inode_post_setattr(struct dentry *dentry) +{ + return; +} +#endif /* CONFIG_IMA_APPRAISE_H */ #endif /* _LINUX_IMA_H */ From 42c63330f2b05aa6077c1bfc2798c04afe54f6b2 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Thu, 10 Mar 2011 18:54:15 -0500 Subject: [PATCH 26/39] ima: add ima_inode_setxattr/removexattr function and calls Based on xattr_permission comments, the restriction to modify 'security' xattr is left up to the underlying fs or lsm. Ensure that not just anyone can modify or remove 'security.ima'. Changelog v1: - Unless IMA-APPRAISE is configured, use stub ima_inode_removexattr()/setxattr() functions. (Moved ima_inode_removexattr()/setxattr() to ima_appraise.c) Changelog: - take i_mutex to fix locking (Dmitry Kasatkin) - ima_reset_appraise_flags should only be called when modifying or removing the 'security.ima' xattr. Requires CAP_SYS_ADMIN privilege. (Incorporated fix from Roberto Sassu) - Even if allowed to update security.ima, reset the appraisal flags, forcing re-appraisal. - Replace CAP_MAC_ADMIN with CAP_SYS_ADMIN - static inline ima_inode_setxattr()/ima_inode_removexattr() stubs - ima_protect_xattr should be static Signed-off-by: Mimi Zohar Signed-off-by: Dmitry Kasatkin --- include/linux/ima.h | 17 ++++++++ security/integrity/ima/ima_appraise.c | 57 +++++++++++++++++++++++++++ security/security.c | 6 +++ 3 files changed, 80 insertions(+) diff --git a/include/linux/ima.h b/include/linux/ima.h index e2bfbb1e9af6..2c7223d7e73b 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -44,10 +44,27 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot) #ifdef CONFIG_IMA_APPRAISE extern void ima_inode_post_setattr(struct dentry *dentry); +extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, + const void *xattr_value, size_t xattr_value_len); +extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name); #else static inline void ima_inode_post_setattr(struct dentry *dentry) { return; } + +static inline int ima_inode_setxattr(struct dentry *dentry, + const char *xattr_name, + const void *xattr_value, + size_t xattr_value_len) +{ + return 0; +} + +static inline int ima_inode_removexattr(struct dentry *dentry, + const char *xattr_name) +{ + return 0; +} #endif /* CONFIG_IMA_APPRAISE_H */ #endif /* _LINUX_IMA_H */ diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 681cb6e72257..becc7e09116d 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -169,3 +169,60 @@ void ima_inode_post_setattr(struct dentry *dentry) rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA); return; } + +/* + * ima_protect_xattr - protect 'security.ima' + * + * Ensure that not just anyone can modify or remove 'security.ima'. + */ +static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name, + const void *xattr_value, size_t xattr_value_len) +{ + if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) { + if (!capable(CAP_SYS_ADMIN)) + return -EPERM; + return 1; + } + return 0; +} + +static void ima_reset_appraise_flags(struct inode *inode) +{ + struct integrity_iint_cache *iint; + + if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)) + return; + + iint = integrity_iint_find(inode); + if (!iint) + return; + + iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED); + return; +} + +int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, + const void *xattr_value, size_t xattr_value_len) +{ + int result; + + result = ima_protect_xattr(dentry, xattr_name, xattr_value, + xattr_value_len); + if (result == 1) { + ima_reset_appraise_flags(dentry->d_inode); + result = 0; + } + return result; +} + +int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name) +{ + int result; + + result = ima_protect_xattr(dentry, xattr_name, NULL, 0); + if (result == 1) { + ima_reset_appraise_flags(dentry->d_inode); + result = 0; + } + return result; +} diff --git a/security/security.c b/security/security.c index 68c1b9b45d93..d23b43522a5a 100644 --- a/security/security.c +++ b/security/security.c @@ -571,6 +571,9 @@ int security_inode_setxattr(struct dentry *dentry, const char *name, if (unlikely(IS_PRIVATE(dentry->d_inode))) return 0; ret = security_ops->inode_setxattr(dentry, name, value, size, flags); + if (ret) + return ret; + ret = ima_inode_setxattr(dentry, name, value, size); if (ret) return ret; return evm_inode_setxattr(dentry, name, value, size); @@ -606,6 +609,9 @@ int security_inode_removexattr(struct dentry *dentry, const char *name) if (unlikely(IS_PRIVATE(dentry->d_inode))) return 0; ret = security_ops->inode_removexattr(dentry, name); + if (ret) + return ret; + ret = ima_inode_removexattr(dentry, name); if (ret) return ret; return evm_inode_removexattr(dentry, name); From 5a44b41207174e1882ce0c24a752f4cfb65dab07 Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Mon, 9 Jan 2012 22:59:36 -0500 Subject: [PATCH 27/39] ima: add support for different security.ima data types IMA-appraisal currently verifies the integrity of a file based on a known 'good' measurement value. This patch reserves the first byte of 'security.ima' as a place holder for the type of method used for verifying file data integrity. Changelog v1: - Use the newly defined 'struct evm_ima_xattr_data' Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_api.c | 6 +++--- security/integrity/ima/ima_appraise.c | 23 +++++++++++++---------- security/integrity/integrity.h | 2 +- 3 files changed, 17 insertions(+), 14 deletions(-) diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 41cce84416c5..33d46859753a 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -147,8 +147,8 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, if (!(iint->flags & IMA_COLLECTED)) { u64 i_version = file->f_dentry->d_inode->i_version; - memset(iint->digest, 0, IMA_DIGEST_SIZE); - result = ima_calc_hash(file, iint->digest); + iint->ima_xattr.type = IMA_XATTR_DIGEST; + result = ima_calc_hash(file, iint->ima_xattr.digest); if (!result) { iint->version = i_version; iint->flags |= IMA_COLLECTED; @@ -196,7 +196,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint, return; } memset(&entry->template, 0, sizeof(entry->template)); - memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE); + memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE); strcpy(entry->template.file_name, (strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ? file->f_dentry->d_name.name : filename); diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index becc7e09116d..f9979976aa5d 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -45,9 +45,9 @@ int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask) static void ima_fix_xattr(struct dentry *dentry, struct integrity_iint_cache *iint) { - iint->digest[0] = IMA_XATTR_DIGEST; - __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, - iint->digest, IMA_DIGEST_SIZE + 1, 0); + iint->ima_xattr.type = IMA_XATTR_DIGEST; + __vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr, + sizeof iint->ima_xattr, 0); } /* @@ -63,7 +63,7 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - u8 xattr_value[IMA_DIGEST_SIZE]; + struct evm_ima_xattr_data xattr_value; enum integrity_status status = INTEGRITY_UNKNOWN; const char *op = "appraise_data"; char *cause = "unknown"; @@ -77,8 +77,8 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, if (iint->flags & IMA_APPRAISED) return iint->ima_status; - rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, xattr_value, - IMA_DIGEST_SIZE); + rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, + sizeof xattr_value); if (rc <= 0) { if (rc && rc != -ENODATA) goto out; @@ -89,7 +89,8 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); + status = evm_verifyxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, + rc, iint); if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { if ((status == INTEGRITY_NOLABEL) || (status == INTEGRITY_NOXATTRS)) @@ -99,14 +100,16 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - rc = memcmp(xattr_value, iint->digest, IMA_DIGEST_SIZE); + rc = memcmp(xattr_value.digest, iint->ima_xattr.digest, + IMA_DIGEST_SIZE); if (rc) { status = INTEGRITY_FAIL; cause = "invalid-hash"; print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, - xattr_value, IMA_DIGEST_SIZE); + &xattr_value, sizeof xattr_value); print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, - iint->digest, IMA_DIGEST_SIZE); + (u8 *)&iint->ima_xattr, + sizeof iint->ima_xattr); goto out; } status = INTEGRITY_PASS; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index dac6b68e945a..91ccef1c704b 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -39,7 +39,7 @@ struct integrity_iint_cache { struct inode *inode; /* back pointer to inode in question */ u64 version; /* track inode changes */ unsigned char flags; - u8 digest[SHA1_DIGEST_SIZE]; + struct evm_ima_xattr_data ima_xattr; enum integrity_status ima_status; enum integrity_status evm_status; }; From 8606404fa555c2ee691376fcc640ab89fe752035 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 31 Aug 2011 14:07:06 +0300 Subject: [PATCH 28/39] ima: digital signature verification support This patch adds support for digital signature based integrity appraisal. With this patch, 'security.ima' contains either the file data hash or a digital signature of the file data hash. The file data hash provides the security attribute of file integrity. In addition to file integrity, a digital signature provides the security attribute of authenticity. Unlike EVM, when the file metadata changes, the digital signature is replaced with an HMAC, modification of the file data does not cause the 'security.ima' digital signature to be replaced with a hash. As a result, after any modification, subsequent file integrity appraisals would fail. Although digitally signed files can be modified, but by not updating 'security.ima' to reflect these modifications, in essence digitally signed files could be considered 'immutable'. IMA uses a different keyring than EVM. While the EVM keyring should not be updated after initialization and locked, the IMA keyring should allow updating or adding new keys when upgrading or installing packages. Changelog v4: - Change IMA_DIGSIG to hex equivalent Changelog v3: - Permit files without any 'security.ima' xattr to be labeled properly. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 70 +++++++++++++++++++-------- security/integrity/integrity.h | 1 + 2 files changed, 52 insertions(+), 19 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index f9979976aa5d..4cdf36ad884a 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -63,7 +63,7 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, { struct dentry *dentry = file->f_dentry; struct inode *inode = dentry->d_inode; - struct evm_ima_xattr_data xattr_value; + struct evm_ima_xattr_data *xattr_value = NULL; enum integrity_status status = INTEGRITY_UNKNOWN; const char *op = "appraise_data"; char *cause = "unknown"; @@ -77,8 +77,8 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, if (iint->flags & IMA_APPRAISED) return iint->ima_status; - rc = inode->i_op->getxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, - sizeof xattr_value); + rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value, + 0, GFP_NOFS); if (rc <= 0) { if (rc && rc != -ENODATA) goto out; @@ -89,8 +89,7 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - status = evm_verifyxattr(dentry, XATTR_NAME_IMA, (u8 *)&xattr_value, - rc, iint); + status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint); if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) { if ((status == INTEGRITY_NOLABEL) || (status == INTEGRITY_NOXATTRS)) @@ -100,30 +99,58 @@ int ima_appraise_measurement(struct integrity_iint_cache *iint, goto out; } - rc = memcmp(xattr_value.digest, iint->ima_xattr.digest, - IMA_DIGEST_SIZE); - if (rc) { - status = INTEGRITY_FAIL; - cause = "invalid-hash"; - 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); - goto out; + switch (xattr_value->type) { + case IMA_XATTR_DIGEST: + rc = memcmp(xattr_value->digest, iint->ima_xattr.digest, + IMA_DIGEST_SIZE); + if (rc) { + cause = "invalid-hash"; + status = INTEGRITY_FAIL; + print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE, + xattr_value, sizeof(*xattr_value)); + print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE, + (u8 *)&iint->ima_xattr, + sizeof iint->ima_xattr); + break; + } + status = INTEGRITY_PASS; + break; + case EVM_IMA_XATTR_DIGSIG: + iint->flags |= IMA_DIGSIG; + rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA, + xattr_value->digest, rc - 1, + iint->ima_xattr.digest, + IMA_DIGEST_SIZE); + if (rc == -EOPNOTSUPP) { + status = INTEGRITY_UNKNOWN; + } else if (rc) { + cause = "invalid-signature"; + status = INTEGRITY_FAIL; + } else { + status = INTEGRITY_PASS; + } + break; + default: + status = INTEGRITY_UNKNOWN; + cause = "unknown-ima-data"; + break; } - status = INTEGRITY_PASS; - iint->flags |= IMA_APPRAISED; + out: if (status != INTEGRITY_PASS) { - if (ima_appraise & IMA_APPRAISE_FIX) { + if ((ima_appraise & IMA_APPRAISE_FIX) && + (!xattr_value || + xattr_value->type != EVM_IMA_XATTR_DIGSIG)) { ima_fix_xattr(dentry, iint); status = INTEGRITY_PASS; } integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename, op, cause, rc, 0); + } else { + iint->flags |= IMA_APPRAISED; } iint->ima_status = status; + kfree(xattr_value); return status; } @@ -135,9 +162,14 @@ void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file) struct dentry *dentry = file->f_dentry; int rc = 0; + /* do not collect and update hash for digital signatures */ + if (iint->flags & IMA_DIGSIG) + return; + rc = ima_collect_measurement(iint, file); if (rc < 0) return; + ima_fix_xattr(dentry, iint); } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 91ccef1c704b..4eec1b14193e 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -21,6 +21,7 @@ #define IMA_APPRAISE 0x04 #define IMA_APPRAISED 0x08 #define IMA_COLLECTED 0x10 +#define IMA_DIGSIG 0x20 enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, From b25b09ecf98bf6a32f3732281c2db13be6aeb14c Mon Sep 17 00:00:00 2001 From: Heiko Carstens Date: Sat, 8 Sep 2012 10:23:42 +0200 Subject: [PATCH 29/39] samples/seccomp: fix 31 bit build on s390 On s390 the flag to force 31 builds is -m31 instead of -m32 unlike on all (?) other architectures. Fixes this compile error: HOSTCC samples/seccomp/bpf-direct.o cc1: error: unrecognized command line option "-m32" make[2]: *** [samples/seccomp/bpf-direct.o] Error 1 Signed-off-by: Heiko Carstens Acked-by: Kees Cook Signed-off-by: James Morris --- samples/seccomp/Makefile | 24 ++++++++++++++++-------- 1 file changed, 16 insertions(+), 8 deletions(-) diff --git a/samples/seccomp/Makefile b/samples/seccomp/Makefile index 16aa2d424985..bbbd276659ba 100644 --- a/samples/seccomp/Makefile +++ b/samples/seccomp/Makefile @@ -18,14 +18,22 @@ HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include bpf-direct-objs := bpf-direct.o # Try to match the kernel target. -ifeq ($(CONFIG_64BIT),) -HOSTCFLAGS_bpf-direct.o += -m32 -HOSTCFLAGS_dropper.o += -m32 -HOSTCFLAGS_bpf-helper.o += -m32 -HOSTCFLAGS_bpf-fancy.o += -m32 -HOSTLOADLIBES_bpf-direct += -m32 -HOSTLOADLIBES_bpf-fancy += -m32 -HOSTLOADLIBES_dropper += -m32 +ifndef CONFIG_64BIT + +# s390 has -m31 flag to build 31 bit binaries +ifndef CONFIG_S390 +MFLAG = -m32 +else +MFLAG = -m31 +endif + +HOSTCFLAGS_bpf-direct.o += $(MFLAG) +HOSTCFLAGS_dropper.o += $(MFLAG) +HOSTCFLAGS_bpf-helper.o += $(MFLAG) +HOSTCFLAGS_bpf-fancy.o += $(MFLAG) +HOSTLOADLIBES_bpf-direct += $(MFLAG) +HOSTLOADLIBES_bpf-fancy += $(MFLAG) +HOSTLOADLIBES_dropper += $(MFLAG) endif # Tell kbuild to always build the programs From a40695edad6a69561b299272028c172e2d981666 Mon Sep 17 00:00:00 2001 From: Kent Yoder Date: Tue, 4 Sep 2012 11:18:21 -0500 Subject: [PATCH 30/39] tpm: fix tpm_acpi sparse warning on different address spaces acpi_os_map_memory expects its return value to be in the __iomem address space. Tag the variable we're using as such and use memcpy_fromio to avoid further sparse warnings. Signed-off-by: Kent Yoder Signed-off-by: James Morris --- drivers/char/tpm/tpm_acpi.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/char/tpm/tpm_acpi.c b/drivers/char/tpm/tpm_acpi.c index fe3fa9431dc9..56051d0c97a2 100644 --- a/drivers/char/tpm/tpm_acpi.c +++ b/drivers/char/tpm/tpm_acpi.c @@ -49,7 +49,7 @@ int read_log(struct tpm_bios_log *log) { struct acpi_tcpa *buff; acpi_status status; - struct acpi_table_header *virt; + void __iomem *virt; u64 len, start; if (log->bios_event_log != NULL) { @@ -102,7 +102,7 @@ int read_log(struct tpm_bios_log *log) return -EIO; } - memcpy(log->bios_event_log, virt, len); + memcpy_fromio(log->bios_event_log, virt, len); acpi_os_unmap_memory(virt, len); return 0; From e23eb920b0f3978687c497de2ac3eb9e281dab32 Mon Sep 17 00:00:00 2001 From: Peter Moody Date: Thu, 14 Jun 2012 10:04:35 -0700 Subject: [PATCH 31/39] audit: export audit_log_task_info At the suggestion of eparis@redhat.com, move this chunk of task logging from audit_log_exit to audit_log_task_info and export this function so it's usuable elsewhere in the kernel. This patch is against git://git.kernel.org/pub/scm/linux/kernel/git/zohar/linux-integrity#next-ima-appraisal Changelog v2: - add empty audit_log_task_info if CONFIG_AUDITSYSCALL isn't set. Changelog v1: - Initial post. Signed-off-by: Peter Moody Signed-off-by: Mimi Zohar --- include/linux/audit.h | 2 ++ kernel/auditsc.c | 74 ++++++++++++++++++++----------------------- 2 files changed, 36 insertions(+), 40 deletions(-) diff --git a/include/linux/audit.h b/include/linux/audit.h index 36abf2aa7e68..2a5073cf548a 100644 --- a/include/linux/audit.h +++ b/include/linux/audit.h @@ -529,6 +529,7 @@ extern int audit_set_loginuid(uid_t loginuid); #define audit_get_loginuid(t) ((t)->loginuid) #define audit_get_sessionid(t) ((t)->sessionid) extern void audit_log_task_context(struct audit_buffer *ab); +extern void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk); extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp); extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode); extern int __audit_bprm(struct linux_binprm *bprm); @@ -640,6 +641,7 @@ extern int audit_signals; #define audit_get_loginuid(t) (-1) #define audit_get_sessionid(t) (-1) #define audit_log_task_context(b) do { ; } while (0) +#define audit_log_task_info(b, t) do { ; } while (0) #define audit_ipc_obj(i) ((void)0) #define audit_ipc_set_perm(q,u,g,m) ((void)0) #define audit_bprm(p) ({ 0; }) diff --git a/kernel/auditsc.c b/kernel/auditsc.c index 4b96415527b8..37f52f27828d 100644 --- a/kernel/auditsc.c +++ b/kernel/auditsc.c @@ -1154,13 +1154,38 @@ error_path: EXPORT_SYMBOL(audit_log_task_context); -static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) +void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk) { + const struct cred *cred; char name[sizeof(tsk->comm)]; struct mm_struct *mm = tsk->mm; struct vm_area_struct *vma; + char *tty; + + if (!ab) + return; /* tsk == current */ + cred = current_cred(); + + spin_lock_irq(&tsk->sighand->siglock); + if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) + tty = tsk->signal->tty->name; + else + tty = "(none)"; + spin_unlock_irq(&tsk->sighand->siglock); + + + audit_log_format(ab, + " ppid=%ld pid=%d auid=%u uid=%u gid=%u" + " euid=%u suid=%u fsuid=%u" + " egid=%u sgid=%u fsgid=%u ses=%u tty=%s", + sys_getppid(), + tsk->pid, + tsk->loginuid, cred->uid, cred->gid, + cred->euid, cred->suid, cred->fsuid, + cred->egid, cred->sgid, cred->fsgid, + tsk->sessionid, tty); get_task_comm(name, tsk); audit_log_format(ab, " comm="); @@ -1183,6 +1208,8 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk audit_log_task_context(ab); } +EXPORT_SYMBOL(audit_log_task_info); + static int audit_log_pid_context(struct audit_context *context, pid_t pid, uid_t auid, uid_t uid, unsigned int sessionid, u32 sid, char *comm) @@ -1585,26 +1612,12 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n, static void audit_log_exit(struct audit_context *context, struct task_struct *tsk) { - const struct cred *cred; int i, call_panic = 0; struct audit_buffer *ab; struct audit_aux_data *aux; - const char *tty; struct audit_names *n; /* tsk == current */ - context->pid = tsk->pid; - if (!context->ppid) - context->ppid = sys_getppid(); - cred = current_cred(); - context->uid = cred->uid; - context->gid = cred->gid; - context->euid = cred->euid; - context->suid = cred->suid; - context->fsuid = cred->fsuid; - context->egid = cred->egid; - context->sgid = cred->sgid; - context->fsgid = cred->fsgid; context->personality = tsk->personality; ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL); @@ -1619,32 +1632,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts (context->return_valid==AUDITSC_SUCCESS)?"yes":"no", context->return_code); - spin_lock_irq(&tsk->sighand->siglock); - if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name) - tty = tsk->signal->tty->name; - else - tty = "(none)"; - spin_unlock_irq(&tsk->sighand->siglock); - audit_log_format(ab, - " a0=%lx a1=%lx a2=%lx a3=%lx items=%d" - " ppid=%d pid=%d auid=%u uid=%u gid=%u" - " euid=%u suid=%u fsuid=%u" - " egid=%u sgid=%u fsgid=%u tty=%s ses=%u", - context->argv[0], - context->argv[1], - context->argv[2], - context->argv[3], - context->name_count, - context->ppid, - context->pid, - tsk->loginuid, - context->uid, - context->gid, - context->euid, context->suid, context->fsuid, - context->egid, context->sgid, context->fsgid, tty, - tsk->sessionid); - + " a0=%lx a1=%lx a2=%lx a3=%lx items=%d", + context->argv[0], + context->argv[1], + context->argv[2], + context->argv[3], + context->name_count); audit_log_task_info(ab, tsk); audit_log_key(ab, context->filterkey); From d9d300cdb6f233c4c591348919c758062198a4f4 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 27 Jun 2012 11:26:14 +0300 Subject: [PATCH 32/39] ima: rename ima_must_appraise_or_measure When AUDIT action support is added to the IMA, ima_must_appraise_or_measure() does not reflect the real meaning anymore. Rename it to ima_get_action(). Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima.h | 2 +- security/integrity/ima/ima_api.c | 4 ++-- security/integrity/ima/ima_main.c | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 069a4aa63e95..48aa0d46d3e7 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -108,7 +108,7 @@ static inline unsigned long ima_hash_key(u8 *digest) } /* LIM API function definitions */ -int ima_must_appraise_or_measure(struct inode *inode, int mask, int function); +int ima_get_action(struct inode *inode, int mask, int function); int ima_must_measure(struct inode *inode, int mask, int function); int ima_collect_measurement(struct integrity_iint_cache *iint, struct file *file); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index 33d46859753a..f0d60e754b3e 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -97,7 +97,7 @@ err_out: } /** - * ima_must_appraise_or_measure - appraise & measure decision based on policy. + * ima_get_action - appraise & measure decision based on policy. * @inode: pointer to inode to measure * @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE) * @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP) @@ -112,7 +112,7 @@ err_out: * Returns IMA_MEASURE, IMA_APPRAISE mask. * */ -int ima_must_appraise_or_measure(struct inode *inode, int mask, int function) +int ima_get_action(struct inode *inode, int mask, int function) { int flags = IMA_MEASURE | IMA_APPRAISE; diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index df6521296051..60b047e96f4e 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -158,7 +158,7 @@ static int process_measurement(struct file *file, const unsigned char *filename, /* Determine if in appraise/measurement policy, * returns IMA_MEASURE, IMA_APPRAISE bitmask. */ - action = ima_must_appraise_or_measure(inode, mask, function); + action = ima_get_action(inode, mask, function); if (!action) return 0; From 45e2472e67bf66f794d507b52e82af92e0614e49 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 12 Sep 2012 20:51:32 +0300 Subject: [PATCH 33/39] ima: generic IMA action flag handling Make the IMA action flag handling generic in order to support additional new actions, without requiring changes to the base implementation. New actions, like audit logging, will only need to modify the define statements. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/ima/ima_appraise.c | 2 +- security/integrity/ima/ima_main.c | 4 ++-- security/integrity/ima/ima_policy.c | 21 +++++++++++---------- security/integrity/integrity.h | 18 ++++++++++++------ 4 files changed, 26 insertions(+), 19 deletions(-) diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 4cdf36ad884a..0aa43bde441c 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -232,7 +232,7 @@ static void ima_reset_appraise_flags(struct inode *inode) if (!iint) return; - iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED); + iint->flags &= ~IMA_DONE_MASK; return; } diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 60b047e96f4e..5da08b75d367 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -117,7 +117,7 @@ static void ima_check_last_writer(struct integrity_iint_cache *iint, mutex_lock(&inode->i_mutex); if (atomic_read(&inode->i_writecount) == 1 && iint->version != inode->i_version) { - iint->flags &= ~(IMA_COLLECTED | IMA_APPRAISED | IMA_MEASURED); + iint->flags &= ~IMA_DONE_MASK; if (iint->flags & IMA_APPRAISE) ima_update_xattr(iint, file); } @@ -173,7 +173,7 @@ static int process_measurement(struct file *file, const unsigned char *filename, /* Determine if already appraised/measured based on bitmask * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */ iint->flags |= action; - action &= ~((iint->flags & (IMA_MEASURED | IMA_APPRAISED)) >> 1); + action &= ~((iint->flags & IMA_DONE_MASK) >> 1); /* Nothing to do, just return existing appraised status */ if (!action) { diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index 0d6d60b4ba6f..f46f685a1711 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -26,13 +26,11 @@ #define IMA_UID 0x0008 #define IMA_FOWNER 0x0010 -#define UNKNOWN 0 -#define MEASURE 1 /* same as IMA_MEASURE */ -#define DONT_MEASURE 2 -#define MEASURE_MASK 3 -#define APPRAISE 4 /* same as IMA_APPRAISE */ -#define DONT_APPRAISE 8 -#define APPRAISE_MASK 12 +#define UNKNOWN 0 +#define MEASURE 0x0001 /* same as IMA_MEASURE */ +#define DONT_MEASURE 0x0002 +#define APPRAISE 0x0004 /* same as IMA_APPRAISE */ +#define DONT_APPRAISE 0x0008 #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, @@ -209,9 +207,12 @@ int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, if (!ima_match_rules(entry, inode, func, mask)) continue; - action |= (entry->action & (IMA_APPRAISE | IMA_MEASURE)); - actmask &= (entry->action & APPRAISE_MASK) ? - ~APPRAISE_MASK : ~MEASURE_MASK; + action |= entry->action & IMA_DO_MASK; + if (entry->action & IMA_DO_MASK) + actmask &= ~(entry->action | entry->action << 1); + else + actmask &= ~(entry->action | entry->action >> 1); + if (!actmask) break; } diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 4eec1b14193e..564ba7db5f6a 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -15,13 +15,19 @@ #include #include +/* iint action cache flags */ +#define IMA_MEASURE 0x0001 +#define IMA_MEASURED 0x0002 +#define IMA_APPRAISE 0x0004 +#define IMA_APPRAISED 0x0008 +/*#define IMA_COLLECT 0x0010 do not use this flag */ +#define IMA_COLLECTED 0x0020 + /* iint cache flags */ -#define IMA_MEASURE 0x01 -#define IMA_MEASURED 0x02 -#define IMA_APPRAISE 0x04 -#define IMA_APPRAISED 0x08 -#define IMA_COLLECTED 0x10 -#define IMA_DIGSIG 0x20 +#define IMA_DIGSIG 0x0100 + +#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE) +#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_COLLECTED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, From e7c568e0fd0cf6d9c8ab8ea537ba8f3a3ae7c3d8 Mon Sep 17 00:00:00 2001 From: Peter Moody Date: Thu, 14 Jun 2012 10:04:36 -0700 Subject: [PATCH 34/39] ima: audit log hashes This adds an 'audit' policy action which audit logs file measurements. Changelog v6: - use new action flag handling (Dmitry Kasatkin). - removed whitespace (Mimi) Changelog v5: - use audit_log_untrustedstring. Changelog v4: - cleanup digest -> hash conversion. - use filename rather than d_path in ima_audit_measurement. Changelog v3: - Use newly exported audit_log_task_info for logging pid/ppid/uid/etc. - Update the ima_policy ABI documentation. Changelog v2: - Use 'audit' action rather than 'measure_and_audit' to permit auditing in the absence of measuring.. Changelog v1: - Initial posting. Signed-off-by: Peter Moody Signed-off-by: Mimi Zohar --- Documentation/ABI/testing/ima_policy | 2 +- security/integrity/ima/ima.h | 2 ++ security/integrity/ima/ima_api.c | 32 +++++++++++++++++++++++++++- security/integrity/ima/ima_main.c | 9 +++++--- security/integrity/ima/ima_policy.c | 11 ++++++++++ security/integrity/integrity.h | 7 ++++-- 6 files changed, 56 insertions(+), 7 deletions(-) diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index dcff82205477..986946613542 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -17,7 +17,7 @@ Description: rule format: action [condition ...] - action: measure | dont_measure | appraise | dont_appraise + action: measure | dont_measure | appraise | dont_appraise | audit condition:= base | lsm base: [[func=] [mask=] [fsmagic=] [uid=] [fowner]] lsm: [[subj_user=] [subj_role=] [subj_type=] diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index 48aa0d46d3e7..8180adde10b7 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -114,6 +114,8 @@ int ima_collect_measurement(struct integrity_iint_cache *iint, struct file *file); void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file, const unsigned char *filename); +void ima_audit_measurement(struct integrity_iint_cache *iint, + const unsigned char *filename); int ima_store_template(struct ima_template_entry *entry, int violation, struct inode *inode); void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show); diff --git a/security/integrity/ima/ima_api.c b/security/integrity/ima/ima_api.c index f0d60e754b3e..b356884fb3ef 100644 --- a/security/integrity/ima/ima_api.c +++ b/security/integrity/ima/ima_api.c @@ -114,7 +114,7 @@ err_out: */ int ima_get_action(struct inode *inode, int mask, int function) { - int flags = IMA_MEASURE | IMA_APPRAISE; + int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE; if (!ima_appraise) flags &= ~IMA_APPRAISE; @@ -207,3 +207,33 @@ void ima_store_measurement(struct integrity_iint_cache *iint, if (result < 0) kfree(entry); } + +void ima_audit_measurement(struct integrity_iint_cache *iint, + const unsigned char *filename) +{ + struct audit_buffer *ab; + char hash[(IMA_DIGEST_SIZE * 2) + 1]; + int i; + + if (iint->flags & IMA_AUDITED) + return; + + for (i = 0; i < IMA_DIGEST_SIZE; i++) + hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]); + hash[i * 2] = '\0'; + + ab = audit_log_start(current->audit_context, GFP_KERNEL, + AUDIT_INTEGRITY_RULE); + if (!ab) + return; + + audit_log_format(ab, "file="); + audit_log_untrustedstring(ab, filename); + audit_log_format(ab, " hash="); + audit_log_untrustedstring(ab, hash); + + audit_log_task_info(ab, current); + audit_log_end(ab); + + iint->flags |= IMA_AUDITED; +} diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 5da08b75d367..73c9a268253e 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -156,8 +156,8 @@ static int process_measurement(struct file *file, const unsigned char *filename, if (!ima_initialized || !S_ISREG(inode->i_mode)) return 0; - /* Determine if in appraise/measurement policy, - * returns IMA_MEASURE, IMA_APPRAISE bitmask. */ + /* Determine if in appraise/audit/measurement policy, + * returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */ action = ima_get_action(inode, mask, function); if (!action) return 0; @@ -171,7 +171,8 @@ static int process_measurement(struct file *file, const unsigned char *filename, goto out; /* Determine if already appraised/measured based on bitmask - * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED) */ + * (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED, + * IMA_AUDIT, IMA_AUDITED) */ iint->flags |= action; action &= ~((iint->flags & IMA_DONE_MASK) >> 1); @@ -202,6 +203,8 @@ static int process_measurement(struct file *file, const unsigned char *filename, if (action & IMA_APPRAISE) rc = ima_appraise_measurement(iint, file, !pathname ? filename : pathname); + if (action & IMA_AUDIT) + ima_audit_measurement(iint, !pathname ? filename : pathname); kfree(pathbuf); out: mutex_unlock(&inode->i_mutex); diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index f46f685a1711..cda903131dbf 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -31,6 +31,7 @@ #define DONT_MEASURE 0x0002 #define APPRAISE 0x0004 /* same as IMA_APPRAISE */ #define DONT_APPRAISE 0x0008 +#define AUDIT 0x0040 #define MAX_LSM_RULES 6 enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE, @@ -277,6 +278,7 @@ enum { Opt_err = -1, Opt_measure = 1, Opt_dont_measure, Opt_appraise, Opt_dont_appraise, + Opt_audit, Opt_obj_user, Opt_obj_role, Opt_obj_type, Opt_subj_user, Opt_subj_role, Opt_subj_type, Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner @@ -287,6 +289,7 @@ static match_table_t policy_tokens = { {Opt_dont_measure, "dont_measure"}, {Opt_appraise, "appraise"}, {Opt_dont_appraise, "dont_appraise"}, + {Opt_audit, "audit"}, {Opt_obj_user, "obj_user=%s"}, {Opt_obj_role, "obj_role=%s"}, {Opt_obj_type, "obj_type=%s"}, @@ -379,6 +382,14 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->action = DONT_APPRAISE; break; + case Opt_audit: + ima_log_string(ab, "action", "audit"); + + if (entry->action != UNKNOWN) + result = -EINVAL; + + entry->action = AUDIT; + break; case Opt_func: ima_log_string(ab, "func", args[0].from); diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 564ba7db5f6a..403ba319a06a 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -22,12 +22,15 @@ #define IMA_APPRAISED 0x0008 /*#define IMA_COLLECT 0x0010 do not use this flag */ #define IMA_COLLECTED 0x0020 +#define IMA_AUDIT 0x0040 +#define IMA_AUDITED 0x0080 /* iint cache flags */ #define IMA_DIGSIG 0x0100 -#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE) -#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_COLLECTED) +#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT) +#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \ + | IMA_COLLECTED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, From c00bedb368ae02a066aed8a888afc286c1df2e60 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Thu, 9 Aug 2012 17:46:38 -0700 Subject: [PATCH 35/39] Smack: remove task_wait() hook. On 12/20/2011 11:20 PM, Jarkko Sakkinen wrote: > Allow SIGCHLD to be passed to child process without > explicit policy. This will help to keep the access > control policy simple and easily maintainable with > complex applications that require use of multiple > security contexts. It will also help to keep them > as isolated as possible. > > Signed-off-by: Jarkko Sakkinen I have a slightly different version that applies to the current smack-next tree. Allow SIGCHLD to be passed to child process without explicit policy. This will help to keep the access control policy simple and easily maintainable with complex applications that require use of multiple security contexts. It will also help to keep them as isolated as possible. Signed-off-by: Casey Schaufler security/smack/smack_lsm.c | 37 ++++++++----------------------------- 1 files changed, 8 insertions(+), 29 deletions(-) --- security/smack/smack_lsm.c | 37 ++++++++----------------------------- 1 file changed, 8 insertions(+), 29 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 8221514cc997..ce9273a18165 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -1691,40 +1691,19 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info, * smack_task_wait - Smack access check for waiting * @p: task to wait for * - * Returns 0 if current can wait for p, error code otherwise + * Returns 0 */ static int smack_task_wait(struct task_struct *p) { - struct smk_audit_info ad; - char *sp = smk_of_current(); - char *tsp = smk_of_forked(task_security(p)); - int rc; - - /* we don't log here, we can be overriden */ - rc = smk_access(tsp, sp, MAY_WRITE, NULL); - if (rc == 0) - goto out_log; - /* - * Allow the operation to succeed if either task - * has privilege to perform operations that might - * account for the smack labels having gotten to - * be different in the first place. - * - * This breaks the strict subject/object access - * control ideal, taking the object's privilege - * state into account in the decision as well as - * the smack value. + * Allow the operation to succeed. + * Zombies are bad. + * In userless environments (e.g. phones) programs + * get marked with SMACK64EXEC and even if the parent + * and child shouldn't be talking the parent still + * may expect to know when the child exits. */ - if (smack_privileged(CAP_MAC_OVERRIDE) || - has_capability(p, CAP_MAC_OVERRIDE)) - rc = 0; - /* we log only if we didn't get overriden */ - out_log: - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); - smk_ad_setfield_u_tsk(&ad, p); - smack_log(tsp, sp, MAY_WRITE, rc, &ad); - return rc; + return 0; } /** From 449543b0436a9146b855aad39eab76ae4853e88d Mon Sep 17 00:00:00 2001 From: Rafal Krypa Date: Wed, 11 Jul 2012 17:49:30 +0200 Subject: [PATCH 36/39] Smack: implement revoking all rules for a subject label Add /smack/revoke-subject special file. Writing a SMACK label to this file will set the access to '-' for all access rules with that subject label. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Rafal Krypa --- Documentation/security/Smack.txt | 3 ++ security/smack/smackfs.c | 75 ++++++++++++++++++++++++++++++++ 2 files changed, 78 insertions(+) diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt index a416479b8a1c..e68536d85680 100644 --- a/Documentation/security/Smack.txt +++ b/Documentation/security/Smack.txt @@ -194,6 +194,9 @@ onlycap these capabilities are effective at for processes with any label. The value is set by writing the desired label to the file or cleared by writing "-" to the file. +revoke-subject + Writing a Smack label here sets the access to '-' for all access + rules with that subject label. You can add access rules in /etc/smack/accesses. They take the form: diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index b1b768e4049a..99929a50093a 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -49,6 +49,7 @@ enum smk_inos { SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */ SMK_ACCESS2 = 16, /* make an access check with long labels */ SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ + SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ }; /* @@ -1991,6 +1992,77 @@ static const struct file_operations smk_access2_ops = { .llseek = generic_file_llseek, }; +/** + * smk_write_revoke_subj - write() for /smack/revoke-subject + * @file: file pointer + * @buf: data from user space + * @count: bytes sent + * @ppos: where to start - must be 0 + */ +static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char *data = NULL; + const char *cp = NULL; + struct smack_known *skp; + struct smack_rule *sp; + struct list_head *rule_list; + struct mutex *rule_lock; + int rc = count; + + if (*ppos != 0) + return -EINVAL; + + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + if (count == 0 || count > SMK_LONGLABEL) + return -EINVAL; + + data = kzalloc(count, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + rc = -EFAULT; + goto free_out; + } + + cp = smk_parse_smack(data, count); + if (cp == NULL) { + rc = -EINVAL; + goto free_out; + } + + skp = smk_find_entry(cp); + if (skp == NULL) { + rc = -EINVAL; + goto free_out; + } + + rule_list = &skp->smk_rules; + rule_lock = &skp->smk_rules_lock; + + mutex_lock(rule_lock); + + list_for_each_entry_rcu(sp, rule_list, list) + sp->smk_access = 0; + + mutex_unlock(rule_lock); + +free_out: + kfree(data); + kfree(cp); + return rc; +} + +static const struct file_operations smk_revoke_subj_ops = { + .write = smk_write_revoke_subj, + .read = simple_transaction_read, + .release = simple_transaction_release, + .llseek = generic_file_llseek, +}; + /** * smk_fill_super - fill the /smackfs superblock * @sb: the empty superblock @@ -2037,6 +2109,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) "access2", &smk_access2_ops, S_IRUGO|S_IWUGO}, [SMK_CIPSO2] = { "cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR}, + [SMK_REVOKE_SUBJ] = { + "revoke-subject", &smk_revoke_subj_ops, + S_IRUGO|S_IWUSR}, /* last one */ {""} }; From 46a2f3b9e99353cc63e15563e8abee71162330f7 Mon Sep 17 00:00:00 2001 From: Casey Schaufler Date: Wed, 22 Aug 2012 11:44:03 -0700 Subject: [PATCH 37/39] Smack: setprocattr memory leak fix The data structure allocations being done in prepare_creds are duplicated in smack_setprocattr. This results in the structure allocated in prepare_creds being orphaned and never freed. The duplicate code is removed from smack_setprocattr. Targeted for git://git.gitorious.org/smack-next/kernel.git Signed-off-by: Casey Schaufler --- security/smack/smack_lsm.c | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index ce9273a18165..2874c7316783 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -2684,9 +2684,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) static int smack_setprocattr(struct task_struct *p, char *name, void *value, size_t size) { - int rc; struct task_smack *tsp; - struct task_smack *oldtsp; struct cred *new; char *newsmack; @@ -2716,21 +2714,13 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (newsmack == smack_known_web.smk_known) return -EPERM; - oldtsp = p->cred->security; new = prepare_creds(); if (new == NULL) return -ENOMEM; - tsp = new_task_smack(newsmack, oldtsp->smk_forked, GFP_KERNEL); - if (tsp == NULL) { - kfree(new); - return -ENOMEM; - } - rc = smk_copy_rules(&tsp->smk_rules, &oldtsp->smk_rules, GFP_KERNEL); - if (rc != 0) - return rc; + tsp = new->security; + tsp->smk_task = newsmack; - new->security = tsp; commit_creds(new); return size; } From 0a72ba7aff26fb6e918cee6d2bbfd289069f10ae Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Wed, 19 Sep 2012 15:32:49 +0300 Subject: [PATCH 38/39] ima: change flags container data type IMA audit hashes patches introduced new IMA flags and required space went beyond 8 bits. Currently the only flag is IMA_DIGSIG. This patch use 16 bit short instead of 8 bit char. Without this fix IMA signature will be replaced with hash, which should not happen. Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- security/integrity/integrity.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 403ba319a06a..e9db763a875e 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -48,7 +48,7 @@ struct integrity_iint_cache { struct rb_node rb_node; /* rooted in integrity_iint_tree */ struct inode *inode; /* back pointer to inode in question */ u64 version; /* track inode changes */ - unsigned char flags; + unsigned short flags; struct evm_ima_xattr_data ima_xattr; enum integrity_status ima_status; enum integrity_status evm_status; From 78a0d8f5d1e9c4a91ee97fc590abbf6e56803769 Mon Sep 17 00:00:00 2001 From: Daniel Wagner Date: Mon, 24 Sep 2012 14:21:29 +0200 Subject: [PATCH 39/39] Documentation: Update git repository URL for Smack userland tools The userland git repository has been moved to a new place. Signed-off-by: Daniel Wagner Cc: Casey Schaufler Cc: Rob Landley Cc: linux-security-module@vger.kernel.org Cc: lkml@vger.kernel.org --- Documentation/security/Smack.txt | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt index e68536d85680..8a177e4b6e21 100644 --- a/Documentation/security/Smack.txt +++ b/Documentation/security/Smack.txt @@ -28,12 +28,11 @@ Smack kernels use the CIPSO IP option. Some network configurations are intolerant of IP options and can impede access to systems that use them as Smack does. -The current git repositories for Smack user space are: +The current git repository for Smack user space is: - git@gitorious.org:meego-platform-security/smackutil.git - git@gitorious.org:meego-platform-security/libsmack.git + git://github.com/smack-team/smack.git -These should make and install on most modern distributions. +This should make and install on most modern distributions. There are three commands included in smackutil: smackload - properly formats data for writing to /smack/load