Commit 683b96f4 authored by Linus Torvalds's avatar Linus Torvalds

Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security

Pull security subsystem updates from James Morris:
 "Generally pretty quiet for this release. Highlights:

  Yama:
   - allow ptrace access for original parent after re-parenting

  TPM:
   - add documentation
   - many bugfixes & cleanups
   - define a generic open() method for ascii & bios measurements

  Integrity:
   - Harden against malformed xattrs

  SELinux:
   - bugfixes & cleanups

  Smack:
   - Remove unnecessary smack_known_invalid label
   - Do not apply star label in smack_setprocattr hook
   - parse mnt opts after privileges check (fixes unpriv DoS vuln)"

* 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (56 commits)
  Yama: allow access for the current ptrace parent
  tpm: adjust return value of tpm_read_log
  tpm: vtpm_proxy: conditionally call tpm_chip_unregister
  tpm: Fix handling of missing event log
  tpm: Check the bios_dir entry for NULL before accessing it
  tpm: return -ENODEV if np is not set
  tpm: cleanup of printk error messages
  tpm: replace of_find_node_by_name() with dev of_node property
  tpm: redefine read_log() to handle ACPI/OF at runtime
  tpm: fix the missing .owner in tpm_bios_measurements_ops
  tpm: have event log use the tpm_chip
  tpm: drop tpm1_chip_register(/unregister)
  tpm: replace dynamically allocated bios_dir with a static array
  tpm: replace symbolic permission with octal for securityfs files
  char: tpm: fix kerneldoc tpm2_unseal_trusted name typo
  tpm_tis: Allow tpm_tis to be bound using DT
  tpm, tpm_vtpm_proxy: add kdoc comments for VTPM_PROXY_IOC_NEW_DEV
  tpm: Only call pm_runtime_get_sync if device has a parent
  tpm: define a generic open() method for ascii & bios measurements
  Documentation: tpm: add the Physical TPM device tree binding documentation
  ...
parents 0f1d6dfe 50523a29
* Device Tree Bindings for IBM Virtual Trusted Platform Module(vtpm)
Required properties:
- compatible : property name that conveys the platform architecture
identifiers, as 'IBM,vtpm'
- device_type : specifies type of virtual device
- interrupts : property specifying the interrupt source number and
sense code associated with this virtual I/O Adapters
- ibm,my-drc-index : integer index for the connector between the device
and its parent - present only if Dynamic
Reconfiguration(DR) Connector is enabled
- ibm,#dma-address-cells: specifies the number of cells that are used to
encode the physical address field of dma-window
properties
- ibm,#dma-size-cells : specifies the number of cells that are used to
encode the size field of dma-window properties
- ibm,my-dma-window : specifies DMA window associated with this virtual
IOA
- ibm,loc-code : specifies the unique and persistent location code
associated with this virtual I/O Adapters
- linux,sml-base : 64-bit base address of the reserved memory allocated
for the firmware event log
- linux,sml-size : size of the memory allocated for the firmware event log
Example (IBM Virtual Trusted Platform Module)
---------------------------------------------
vtpm@30000003 {
ibm,#dma-size-cells = <0x2>;
compatible = "IBM,vtpm";
device_type = "IBM,vtpm";
ibm,my-drc-index = <0x30000003>;
ibm,#dma-address-cells = <0x2>;
linux,sml-base = <0xc60e 0x0>;
interrupts = <0xa0003 0x0>;
ibm,my-dma-window = <0x10000003 0x0 0x0 0x0 0x10000000>;
ibm,loc-code = "U8286.41A.10082DV-V3-C3";
reg = <0x30000003>;
linux,sml-size = <0xbce10200>;
};
* Device Tree Bindings for I2C based Trusted Platform Module(TPM)
Required properties:
- compatible : 'manufacturer,model', eg. nuvoton,npct650
- label : human readable string describing the device, eg. "tpm"
- linux,sml-base : 64-bit base address of the reserved memory allocated for
the firmware event log
- linux,sml-size : size of the memory allocated for the firmware event log
Example (for OpenPower Systems with Nuvoton TPM 2.0 on I2C)
----------------------------------------------------------
tpm@57 {
reg = <0x57>;
label = "tpm";
compatible = "nuvoton,npct650", "nuvoton,npct601";
linux,sml-base = <0x7f 0xfd450000>;
linux,sml-size = <0x10000>;
status = "okay";
};
Trusted Computing Group MMIO Trusted Platform Module
The TCG defines multi vendor standard for accessing a TPM chip, this
is the standard protocol defined to access the TPM via MMIO. Typically
this interface will be implemented over Intel's LPC bus.
Refer to the 'TCG PC Client Specific TPM Interface Specification (TIS)' TCG
publication for the specification.
Required properties:
- compatible: should contain a string below for the chip, followed by
"tcg,tpm-tis-mmio". Valid chip strings are:
* "atmel,at97sc3204"
- reg: The location of the MMIO registers, should be at least 0x5000 bytes
- interrupt-parent/interrupts: An optional interrupt indicating command completion.
Example:
tpm_tis@90000 {
compatible = "atmel,at97sc3204", "tcg,tpm-tis-mmio";
reg = <0x90000 0x5000>;
interrupt-parent = <&EIC0>;
interrupts = <1 2>;
};
...@@ -32,8 +32,6 @@ Usage: ...@@ -32,8 +32,6 @@ Usage:
(40 ascii zeros) (40 ascii zeros)
blobauth= ascii hex auth for sealed data default 0x00... blobauth= ascii hex auth for sealed data default 0x00...
(40 ascii zeros) (40 ascii zeros)
blobauth= ascii hex auth for sealed data default 0x00...
(40 ascii zeros)
pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default) pcrinfo= ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
pcrlock= pcr number to be extended to "lock" blob pcrlock= pcr number to be extended to "lock" blob
migratable= 0|1 indicating permission to reseal to new PCR values, migratable= 0|1 indicating permission to reseal to new PCR values,
......
...@@ -32,7 +32,7 @@ config TCG_TIS_CORE ...@@ -32,7 +32,7 @@ config TCG_TIS_CORE
config TCG_TIS config TCG_TIS
tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface" tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface"
depends on X86 depends on X86 || OF
select TCG_TIS_CORE select TCG_TIS_CORE
---help--- ---help---
If you have a TPM security chip that is compliant with the If you have a TPM security chip that is compliant with the
......
...@@ -2,16 +2,10 @@ ...@@ -2,16 +2,10 @@
# Makefile for the kernel tpm device drivers. # Makefile for the kernel tpm device drivers.
# #
obj-$(CONFIG_TCG_TPM) += tpm.o obj-$(CONFIG_TCG_TPM) += tpm.o
tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \
tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_eventlog.o
tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o
ifdef CONFIG_ACPI tpm-$(CONFIG_OF) += tpm_of.o
tpm-y += tpm_eventlog.o tpm_acpi.o
else
ifdef CONFIG_TCG_IBMVTPM
tpm-y += tpm_eventlog.o tpm_of.o
endif
endif
obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o
obj-$(CONFIG_TCG_TIS) += tpm_tis.o obj-$(CONFIG_TCG_TIS) += tpm_tis.o
obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o
......
...@@ -127,6 +127,7 @@ static void tpm_dev_release(struct device *dev) ...@@ -127,6 +127,7 @@ static void tpm_dev_release(struct device *dev)
idr_remove(&dev_nums_idr, chip->dev_num); idr_remove(&dev_nums_idr, chip->dev_num);
mutex_unlock(&idr_lock); mutex_unlock(&idr_lock);
kfree(chip->log.bios_event_log);
kfree(chip); kfree(chip);
} }
...@@ -276,27 +277,6 @@ static void tpm_del_char_device(struct tpm_chip *chip) ...@@ -276,27 +277,6 @@ static void tpm_del_char_device(struct tpm_chip *chip)
up_write(&chip->ops_sem); up_write(&chip->ops_sem);
} }
static int tpm1_chip_register(struct tpm_chip *chip)
{
if (chip->flags & TPM_CHIP_FLAG_TPM2)
return 0;
tpm_sysfs_add_device(chip);
chip->bios_dir = tpm_bios_log_setup(dev_name(&chip->dev));
return 0;
}
static void tpm1_chip_unregister(struct tpm_chip *chip)
{
if (chip->flags & TPM_CHIP_FLAG_TPM2)
return;
if (chip->bios_dir)
tpm_bios_log_teardown(chip->bios_dir);
}
static void tpm_del_legacy_sysfs(struct tpm_chip *chip) static void tpm_del_legacy_sysfs(struct tpm_chip *chip)
{ {
struct attribute **i; struct attribute **i;
...@@ -363,20 +343,20 @@ int tpm_chip_register(struct tpm_chip *chip) ...@@ -363,20 +343,20 @@ int tpm_chip_register(struct tpm_chip *chip)
return rc; return rc;
} }
rc = tpm1_chip_register(chip); tpm_sysfs_add_device(chip);
if (rc)
rc = tpm_bios_log_setup(chip);
if (rc != 0 && rc != -ENODEV)
return rc; return rc;
tpm_add_ppi(chip); tpm_add_ppi(chip);
rc = tpm_add_char_device(chip); rc = tpm_add_char_device(chip);
if (rc) { if (rc) {
tpm1_chip_unregister(chip); tpm_bios_log_teardown(chip);
return rc; return rc;
} }
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
rc = tpm_add_legacy_sysfs(chip); rc = tpm_add_legacy_sysfs(chip);
if (rc) { if (rc) {
tpm_chip_unregister(chip); tpm_chip_unregister(chip);
...@@ -402,12 +382,8 @@ EXPORT_SYMBOL_GPL(tpm_chip_register); ...@@ -402,12 +382,8 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
*/ */
void tpm_chip_unregister(struct tpm_chip *chip) void tpm_chip_unregister(struct tpm_chip *chip)
{ {
if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
return;
tpm_del_legacy_sysfs(chip); tpm_del_legacy_sysfs(chip);
tpm_bios_log_teardown(chip);
tpm1_chip_unregister(chip);
tpm_del_char_device(chip); tpm_del_char_device(chip);
} }
EXPORT_SYMBOL_GPL(tpm_chip_unregister); EXPORT_SYMBOL_GPL(tpm_chip_unregister);
...@@ -29,6 +29,7 @@ ...@@ -29,6 +29,7 @@
#include <linux/mutex.h> #include <linux/mutex.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/pm_runtime.h>
#include "tpm.h" #include "tpm.h"
#include "tpm_eventlog.h" #include "tpm_eventlog.h"
...@@ -356,6 +357,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, ...@@ -356,6 +357,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
if (!(flags & TPM_TRANSMIT_UNLOCKED)) if (!(flags & TPM_TRANSMIT_UNLOCKED))
mutex_lock(&chip->tpm_mutex); mutex_lock(&chip->tpm_mutex);
if (chip->dev.parent)
pm_runtime_get_sync(chip->dev.parent);
rc = chip->ops->send(chip, (u8 *) buf, count); rc = chip->ops->send(chip, (u8 *) buf, count);
if (rc < 0) { if (rc < 0) {
dev_err(&chip->dev, dev_err(&chip->dev,
...@@ -397,6 +401,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, ...@@ -397,6 +401,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
dev_err(&chip->dev, dev_err(&chip->dev,
"tpm_transmit: tpm_recv: error %zd\n", rc); "tpm_transmit: tpm_recv: error %zd\n", rc);
out: out:
if (chip->dev.parent)
pm_runtime_put_sync(chip->dev.parent);
if (!(flags & TPM_TRANSMIT_UNLOCKED)) if (!(flags & TPM_TRANSMIT_UNLOCKED))
mutex_unlock(&chip->tpm_mutex); mutex_unlock(&chip->tpm_mutex);
return rc; return rc;
...@@ -437,26 +444,29 @@ static const struct tpm_input_header tpm_getcap_header = { ...@@ -437,26 +444,29 @@ static const struct tpm_input_header tpm_getcap_header = {
.ordinal = TPM_ORD_GET_CAP .ordinal = TPM_ORD_GET_CAP
}; };
ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap, ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
const char *desc) const char *desc)
{ {
struct tpm_cmd_t tpm_cmd; struct tpm_cmd_t tpm_cmd;
int rc; int rc;
tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.header.in = tpm_getcap_header;
if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) { if (subcap_id == TPM_CAP_VERSION_1_1 ||
tpm_cmd.params.getcap_in.cap = subcap_id; subcap_id == TPM_CAP_VERSION_1_2) {
tpm_cmd.params.getcap_in.cap = cpu_to_be32(subcap_id);
/*subcap field not necessary */ /*subcap field not necessary */
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0); tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0);
tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32)); tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32));
} else { } else {
if (subcap_id == TPM_CAP_FLAG_PERM || if (subcap_id == TPM_CAP_FLAG_PERM ||
subcap_id == TPM_CAP_FLAG_VOL) subcap_id == TPM_CAP_FLAG_VOL)
tpm_cmd.params.getcap_in.cap = TPM_CAP_FLAG; tpm_cmd.params.getcap_in.cap =
cpu_to_be32(TPM_CAP_FLAG);
else else
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; tpm_cmd.params.getcap_in.cap =
cpu_to_be32(TPM_CAP_PROP);
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4); tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = subcap_id; tpm_cmd.params.getcap_in.subcap = cpu_to_be32(subcap_id);
} }
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0, rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
desc); desc);
...@@ -488,12 +498,14 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) ...@@ -488,12 +498,14 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
int tpm_get_timeouts(struct tpm_chip *chip) int tpm_get_timeouts(struct tpm_chip *chip)
{ {
struct tpm_cmd_t tpm_cmd; cap_t cap;
unsigned long new_timeout[4]; unsigned long new_timeout[4];
unsigned long old_timeout[4]; unsigned long old_timeout[4];
struct duration_t *duration_cap;
ssize_t rc; ssize_t rc;
if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS)
return 0;
if (chip->flags & TPM_CHIP_FLAG_TPM2) { if (chip->flags & TPM_CHIP_FLAG_TPM2) {
/* Fixed timeouts for TPM2 */ /* Fixed timeouts for TPM2 */
chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A); chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
...@@ -506,46 +518,30 @@ int tpm_get_timeouts(struct tpm_chip *chip) ...@@ -506,46 +518,30 @@ int tpm_get_timeouts(struct tpm_chip *chip)
msecs_to_jiffies(TPM2_DURATION_MEDIUM); msecs_to_jiffies(TPM2_DURATION_MEDIUM);
chip->duration[TPM_LONG] = chip->duration[TPM_LONG] =
msecs_to_jiffies(TPM2_DURATION_LONG); msecs_to_jiffies(TPM2_DURATION_LONG);
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
return 0; return 0;
} }
tpm_cmd.header.in = tpm_getcap_header; rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; "attempting to determine the timeouts");
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
NULL);
if (rc == TPM_ERR_INVALID_POSTINIT) { if (rc == TPM_ERR_INVALID_POSTINIT) {
/* The TPM is not started, we are the first to talk to it. /* The TPM is not started, we are the first to talk to it.
Execute a startup command. */ Execute a startup command. */
dev_info(&chip->dev, "Issuing TPM_STARTUP"); dev_info(&chip->dev, "Issuing TPM_STARTUP\n");
if (tpm_startup(chip, TPM_ST_CLEAR)) if (tpm_startup(chip, TPM_ST_CLEAR))
return rc; return rc;
tpm_cmd.header.in = tpm_getcap_header; rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; "attempting to determine the timeouts");
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE,
0, NULL);
}
if (rc) {
dev_err(&chip->dev,
"A TPM error (%zd) occurred attempting to determine the timeouts\n",
rc);
goto duration;
} }
if (rc)
return rc;
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 || old_timeout[0] = be32_to_cpu(cap.timeout.a);
be32_to_cpu(tpm_cmd.header.out.length) old_timeout[1] = be32_to_cpu(cap.timeout.b);
!= sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) old_timeout[2] = be32_to_cpu(cap.timeout.c);
return -EINVAL; old_timeout[3] = be32_to_cpu(cap.timeout.d);
old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a);
old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b);
old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c);
old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d);
memcpy(new_timeout, old_timeout, sizeof(new_timeout)); memcpy(new_timeout, old_timeout, sizeof(new_timeout));
/* /*
...@@ -583,29 +579,17 @@ int tpm_get_timeouts(struct tpm_chip *chip) ...@@ -583,29 +579,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
chip->timeout_c = usecs_to_jiffies(new_timeout[2]); chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
chip->timeout_d = usecs_to_jiffies(new_timeout[3]); chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
duration: rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
tpm_cmd.header.in = tpm_getcap_header;
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_DURATION;
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
"attempting to determine the durations"); "attempting to determine the durations");
if (rc) if (rc)
return rc; return rc;
if (be32_to_cpu(tpm_cmd.header.out.return_code) != 0 ||
be32_to_cpu(tpm_cmd.header.out.length)
!= sizeof(tpm_cmd.header.out) + sizeof(u32) + 3 * sizeof(u32))
return -EINVAL;
duration_cap = &tpm_cmd.params.getcap_out.cap.duration;
chip->duration[TPM_SHORT] = chip->duration[TPM_SHORT] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short)); usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
chip->duration[TPM_MEDIUM] = chip->duration[TPM_MEDIUM] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_medium)); usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_medium));
chip->duration[TPM_LONG] = chip->duration[TPM_LONG] =
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_long)); usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_long));
/* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above /* The Broadcom BCM0102 chipset in a Dell Latitude D820 gets the above
* value wrong and apparently reports msecs rather than usecs. So we * value wrong and apparently reports msecs rather than usecs. So we
...@@ -619,6 +603,8 @@ int tpm_get_timeouts(struct tpm_chip *chip) ...@@ -619,6 +603,8 @@ int tpm_get_timeouts(struct tpm_chip *chip)
chip->duration_adjusted = true; chip->duration_adjusted = true;
dev_info(&chip->dev, "Adjusting TPM timeout parameters."); dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
} }
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
return 0; return 0;
} }
EXPORT_SYMBOL_GPL(tpm_get_timeouts); EXPORT_SYMBOL_GPL(tpm_get_timeouts);
...@@ -726,6 +712,14 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) ...@@ -726,6 +712,14 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
} }
EXPORT_SYMBOL_GPL(tpm_pcr_read); EXPORT_SYMBOL_GPL(tpm_pcr_read);
#define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
#define EXTEND_PCR_RESULT_SIZE 34
static const struct tpm_input_header pcrextend_header = {
.tag = TPM_TAG_RQU_COMMAND,
.length = cpu_to_be32(34),
.ordinal = TPM_ORD_PCR_EXTEND
};
/** /**
* tpm_pcr_extend - extend pcr value with hash * tpm_pcr_extend - extend pcr value with hash
* @chip_num: tpm idx # or AN& * @chip_num: tpm idx # or AN&
...@@ -736,14 +730,6 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read); ...@@ -736,14 +730,6 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
* isn't, protect against the chip disappearing, by incrementing * isn't, protect against the chip disappearing, by incrementing
* the module usage count. * the module usage count.
*/ */
#define TPM_ORD_PCR_EXTEND cpu_to_be32(20)
#define EXTEND_PCR_RESULT_SIZE 34
static const struct tpm_input_header pcrextend_header = {
.tag = TPM_TAG_RQU_COMMAND,
.length = cpu_to_be32(34),
.ordinal = TPM_ORD_PCR_EXTEND
};
int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash)
{ {
struct tpm_cmd_t cmd; struct tpm_cmd_t cmd;
......
...@@ -193,7 +193,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, ...@@ -193,7 +193,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
be32_to_cpu(cap.manufacturer_id)); be32_to_cpu(cap.manufacturer_id));
/* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */
rc = tpm_getcap(chip, CAP_VERSION_1_2, &cap, rc = tpm_getcap(chip, TPM_CAP_VERSION_1_2, &cap,
"attempting to determine the 1.2 version"); "attempting to determine the 1.2 version");
if (!rc) { if (!rc) {
str += sprintf(str, str += sprintf(str,
...@@ -204,7 +204,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr, ...@@ -204,7 +204,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
cap.tpm_version_1_2.revMinor); cap.tpm_version_1_2.revMinor);
} else { } else {
/* Otherwise just use TPM_STRUCT_VER */ /* Otherwise just use TPM_STRUCT_VER */
rc = tpm_getcap(chip, CAP_VERSION_1_1, &cap, rc = tpm_getcap(chip, TPM_CAP_VERSION_1_1, &cap,
"attempting to determine the 1.1 version"); "attempting to determine the 1.1 version");
if (rc) if (rc)
return 0; return 0;
...@@ -284,6 +284,9 @@ static const struct attribute_group tpm_dev_group = { ...@@ -284,6 +284,9 @@ static const struct attribute_group tpm_dev_group = {
void tpm_sysfs_add_device(struct tpm_chip *chip) void tpm_sysfs_add_device(struct tpm_chip *chip)
{ {
if (chip->flags & TPM_CHIP_FLAG_TPM2)
return;
/* The sysfs routines rely on an implicit tpm_try_get_ops, device_del /* The sysfs routines rely on an implicit tpm_try_get_ops, device_del
* is called before ops is null'd and the sysfs core synchronizes this * is called before ops is null'd and the sysfs core synchronizes this
* removal so that no callbacks are running or can run again * removal so that no callbacks are running or can run again
......
...@@ -35,11 +35,14 @@ ...@@ -35,11 +35,14 @@
#include <linux/cdev.h> #include <linux/cdev.h>
#include <linux/highmem.h> #include <linux/highmem.h>
#include "tpm_eventlog.h"
enum tpm_const { enum tpm_const {
TPM_MINOR = 224, /* officially assigned */ TPM_MINOR = 224, /* officially assigned */
TPM_BUFSIZE = 4096, TPM_BUFSIZE = 4096,
TPM_NUM_DEVICES = 65536, TPM_NUM_DEVICES = 65536,
TPM_RETRY = 50, /* 5 seconds */ TPM_RETRY = 50, /* 5 seconds */
TPM_NUM_EVENT_LOG_FILES = 3,
}; };
enum tpm_timeout { enum tpm_timeout {
...@@ -139,10 +142,15 @@ enum tpm2_startup_types { ...@@ -139,10 +142,15 @@ enum tpm2_startup_types {
#define TPM_PPI_VERSION_LEN 3 #define TPM_PPI_VERSION_LEN 3
enum tpm_chip_flags { enum tpm_chip_flags {
TPM_CHIP_FLAG_REGISTERED = BIT(0),
TPM_CHIP_FLAG_TPM2 = BIT(1), TPM_CHIP_FLAG_TPM2 = BIT(1),
TPM_CHIP_FLAG_IRQ = BIT(2), TPM_CHIP_FLAG_IRQ = BIT(2),
TPM_CHIP_FLAG_VIRTUAL = BIT(3), TPM_CHIP_FLAG_VIRTUAL = BIT(3),
TPM_CHIP_FLAG_HAVE_TIMEOUTS = BIT(4),
};
struct tpm_chip_seqops {
struct tpm_chip *chip;
const struct seq_operations *seqops;
}; };
struct tpm_chip { struct tpm_chip {
...@@ -156,6 +164,10 @@ struct tpm_chip { ...@@ -156,6 +164,10 @@ struct tpm_chip {
struct rw_semaphore ops_sem; struct rw_semaphore ops_sem;
const struct tpm_class_ops *ops; const struct tpm_class_ops *ops;
struct tpm_bios_log log;
struct tpm_chip_seqops bin_log_seqops;
struct tpm_chip_seqops ascii_log_seqops;
unsigned int flags; unsigned int flags;
int dev_num; /* /dev/tpm# */ int dev_num; /* /dev/tpm# */
...@@ -171,7 +183,7 @@ struct tpm_chip { ...@@ -171,7 +183,7 @@ struct tpm_chip {
unsigned long duration[3]; /* jiffies */ unsigned long duration[3]; /* jiffies */
bool duration_adjusted; bool duration_adjusted;
struct dentry **bios_dir; struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];
const struct attribute_group *groups[3]; const struct attribute_group *groups[3];
unsigned int groups_cnt; unsigned int groups_cnt;
...@@ -282,21 +294,20 @@ typedef union { ...@@ -282,21 +294,20 @@ typedef union {
} cap_t; } cap_t;
enum tpm_capabilities { enum tpm_capabilities {
TPM_CAP_FLAG = cpu_to_be32(4), TPM_CAP_FLAG = 4,
TPM_CAP_PROP = cpu_to_be32(5), TPM_CAP_PROP = 5,
CAP_VERSION_1_1 = cpu_to_be32(0x06), TPM_CAP_VERSION_1_1 = 0x06,
CAP_VERSION_1_2 = cpu_to_be32(0x1A) TPM_CAP_VERSION_1_2 = 0x1A,
}; };
enum tpm_sub_capabilities { enum tpm_sub_capabilities {
TPM_CAP_PROP_PCR = cpu_to_be32(0x101), TPM_CAP_PROP_PCR = 0x101,
TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103), TPM_CAP_PROP_MANUFACTURER = 0x103,
TPM_CAP_FLAG_PERM = cpu_to_be32(0x108), TPM_CAP_FLAG_PERM = 0x108,
TPM_CAP_FLAG_VOL = cpu_to_be32(0x109), TPM_CAP_FLAG_VOL = 0x109,
TPM_CAP_PROP_OWNER = cpu_to_be32(0x111), TPM_CAP_PROP_OWNER = 0x111,
TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115), TPM_CAP_PROP_TIS_TIMEOUT = 0x115,
TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120), TPM_CAP_PROP_TIS_DURATION = 0x120,
}; };
struct tpm_getcap_params_in { struct tpm_getcap_params_in {
...@@ -484,7 +495,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz, ...@@ -484,7 +495,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
unsigned int flags); unsigned int flags);
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len, ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len,
unsigned int flags, const char *desc); unsigned int flags, const char *desc);
ssize_t tpm_getcap(struct tpm_chip *chip, __be32 subcap_id, cap_t *cap, ssize_t tpm_getcap(struct tpm_chip *chip, u32 subcap_id, cap_t *cap,
const char *desc); const char *desc);
int tpm_get_timeouts(struct tpm_chip *); int tpm_get_timeouts(struct tpm_chip *);
int tpm1_auto_startup(struct tpm_chip *chip); int tpm1_auto_startup(struct tpm_chip *chip);
......
...@@ -680,7 +680,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip, ...@@ -680,7 +680,7 @@ static int tpm2_unseal_cmd(struct tpm_chip *chip,
} }
/** /**
* tpm_unseal_trusted() - unseal the payload of a trusted key * tpm2_unseal_trusted() - unseal the payload of a trusted key
* @chip_num: TPM chip to use * @chip_num: TPM chip to use
* @payload: the key data in clear and encrypted form * @payload: the key data in clear and encrypted form
* @options: authentication values and other options * @options: authentication values and other options
......
...@@ -6,10 +6,11 @@ ...@@ -6,10 +6,11 @@
* Stefan Berger <stefanb@us.ibm.com> * Stefan Berger <stefanb@us.ibm.com>
* Reiner Sailer <sailer@watson.ibm.com> * Reiner Sailer <sailer@watson.ibm.com>
* Kylene Hall <kjhall@us.ibm.com> * Kylene Hall <kjhall@us.ibm.com>
* Nayna Jain <nayna@linux.vnet.ibm.com>
* *
* Maintained by: <tpmdd-devel@lists.sourceforge.net> * Maintained by: <tpmdd-devel@lists.sourceforge.net>
* *
* Access to the eventlog extended by the TCG BIOS of PC platform * Access to the event log extended by the TCG BIOS of PC platform
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -45,29 +46,28 @@ struct acpi_tcpa { ...@@ -45,29 +46,28 @@ struct acpi_tcpa {
}; };
/* read binary bios log */ /* read binary bios log */
int read_log(struct tpm_bios_log *log) int tpm_read_log_acpi(struct tpm_chip *chip)
{ {
struct acpi_tcpa *buff; struct acpi_tcpa *buff;
acpi_status status; acpi_status status;
void __iomem *virt; void __iomem *virt;
u64 len, start; u64 len, start;
struct tpm_bios_log *log;
if (log->bios_event_log != NULL) { log = &chip->log;
printk(KERN_ERR
"%s: ERROR - Eventlog already initialized\n", /* Unfortuntely ACPI does not associate the event log with a specific
__func__); * TPM, like PPI. Thus all ACPI TPMs will read the same log.
return -EFAULT; */
} if (!chip->acpi_dev_handle)
return -ENODEV;
/* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */ /* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
status = acpi_get_table(ACPI_SIG_TCPA, 1, status = acpi_get_table(ACPI_SIG_TCPA, 1,
(struct acpi_table_header **)&buff); (struct acpi_table_header **)&buff);
if (ACPI_FAILURE(status)) { if (ACPI_FAILURE(status))
printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n", return -ENODEV;
__func__);
return -EIO;
}
switch(buff->platform_class) { switch(buff->platform_class) {
case BIOS_SERVER: case BIOS_SERVER:
...@@ -81,29 +81,29 @@ int read_log(struct tpm_bios_log *log) ...@@ -81,29 +81,29 @@ int read_log(struct tpm_bios_log *log)
break; break;
} }
if (!len) { if (!len) {
printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__); dev_warn(&chip->dev, "%s: TCPA log area empty\n", __func__);
return -EIO; return -EIO;
} }
/* malloc EventLog space */ /* malloc EventLog space */
log->bios_event_log = kmalloc(len, GFP_KERNEL); log->bios_event_log = kmalloc(len, GFP_KERNEL);
if (!log->bios_event_log) { if (!log->bios_event_log)
printk("%s: ERROR - Not enough Memory for BIOS measurements\n",
__func__);
return -ENOMEM; return -ENOMEM;
}
log->bios_event_log_end = log->bios_event_log + len; log->bios_event_log_end = log->bios_event_log + len;
virt = acpi_os_map_iomem(start, len); virt = acpi_os_map_iomem(start, len);
if (!virt) { if (!virt)
kfree(log->bios_event_log); goto err;
printk("%s: ERROR - Unable to map memory\n", __func__);
return -EIO;
}
memcpy_fromio(log->bios_event_log, virt, len); memcpy_fromio(log->bios_event_log, virt, len);
acpi_os_unmap_iomem(virt, len); acpi_os_unmap_iomem(virt, len);
return 0; return 0;
err:
kfree(log->bios_event_log);
log->bios_event_log = NULL;
return -EIO;
} }
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/highmem.h> #include <linux/highmem.h>
#include <linux/rculist.h> #include <linux/rculist.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/pm_runtime.h>
#include "tpm.h" #include "tpm.h"
#define ACPI_SIG_TPM2 "TPM2" #define ACPI_SIG_TPM2 "TPM2"
...@@ -83,7 +84,71 @@ struct crb_priv { ...@@ -83,7 +84,71 @@ struct crb_priv {
u32 cmd_size; u32 cmd_size;
}; };
static SIMPLE_DEV_PM_OPS(crb_pm, tpm_pm_suspend, tpm_pm_resume); /**
* crb_go_idle - request tpm crb device to go the idle state
*
* @dev: crb device
* @priv: crb private data
*
* Write CRB_CTRL_REQ_GO_IDLE to TPM_CRB_CTRL_REQ
* The device should respond within TIMEOUT_C by clearing the bit.
* Anyhow, we do not wait here as a consequent CMD_READY request
* will be handled correctly even if idle was not completed.
*
* The function does nothing for devices with ACPI-start method.
*
* Return: 0 always
*/
static int __maybe_unused crb_go_idle(struct device *dev, struct crb_priv *priv)
{
if (priv->flags & CRB_FL_ACPI_START)
return 0;
iowrite32(CRB_CTRL_REQ_GO_IDLE, &priv->cca->req);
/* we don't really care when this settles */
return 0;
}
/**
* crb_cmd_ready - request tpm crb device to enter ready state
*
* @dev: crb device
* @priv: crb private data
*
* Write CRB_CTRL_REQ_CMD_READY to TPM_CRB_CTRL_REQ
* and poll till the device acknowledge it by clearing the bit.
* The device should respond within TIMEOUT_C.
*
* The function does nothing for devices with ACPI-start method
*
* Return: 0 on success -ETIME on timeout;
*/
static int __maybe_unused crb_cmd_ready(struct device *dev,
struct crb_priv *priv)
{
ktime_t stop, start;
if (priv->flags & CRB_FL_ACPI_START)
return 0;
iowrite32(CRB_CTRL_REQ_CMD_READY, &priv->cca->req);
start = ktime_get();
stop = ktime_add(start, ms_to_ktime(TPM2_TIMEOUT_C));
do {
if (!(ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY))
return 0;
usleep_range(50, 100);
} while (ktime_before(ktime_get(), stop));
if (ioread32(&priv->cca->req) & CRB_CTRL_REQ_CMD_READY) {
dev_warn(dev, "cmdReady timed out\n");
return -ETIME;
}
return 0;
}
static u8 crb_status(struct tpm_chip *chip) static u8 crb_status(struct tpm_chip *chip)
{ {
...@@ -196,21 +261,6 @@ static const struct tpm_class_ops tpm_crb = { ...@@ -196,21 +261,6 @@ static const struct tpm_class_ops tpm_crb = {
.req_complete_val = CRB_DRV_STS_COMPLETE, .req_complete_val = CRB_DRV_STS_COMPLETE,
}; };
static int crb_init(struct acpi_device *device, struct crb_priv *priv)
{
struct tpm_chip *chip;
chip = tpmm_chip_alloc(&device->dev, &tpm_crb);
if (IS_ERR(chip))
return PTR_ERR(chip);
dev_set_drvdata(&chip->dev, priv);
chip->acpi_dev_handle = device->handle;
chip->flags = TPM_CHIP_FLAG_TPM2;
return tpm_chip_register(chip);
}
static int crb_check_resource(struct acpi_resource *ares, void *data) static int crb_check_resource(struct acpi_resource *ares, void *data)
{ {
struct resource *io_res = data; struct resource *io_res = data;
...@@ -249,6 +299,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -249,6 +299,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
struct list_head resources; struct list_head resources;
struct resource io_res; struct resource io_res;
struct device *dev = &device->dev; struct device *dev = &device->dev;
u32 pa_high, pa_low;
u64 cmd_pa; u64 cmd_pa;
u32 cmd_size; u32 cmd_size;
u64 rsp_pa; u64 rsp_pa;
...@@ -276,12 +327,27 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -276,12 +327,27 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
if (IS_ERR(priv->cca)) if (IS_ERR(priv->cca))
return PTR_ERR(priv->cca); return PTR_ERR(priv->cca);
cmd_pa = ((u64) ioread32(&priv->cca->cmd_pa_high) << 32) | /*
(u64) ioread32(&priv->cca->cmd_pa_low); * PTT HW bug w/a: wake up the device to access
* possibly not retained registers.
*/
ret = crb_cmd_ready(dev, priv);
if (ret)
return ret;
pa_high = ioread32(&priv->cca->cmd_pa_high);
pa_low = ioread32(&priv->cca->cmd_pa_low);
cmd_pa = ((u64)pa_high << 32) | pa_low;
cmd_size = ioread32(&priv->cca->cmd_size); cmd_size = ioread32(&priv->cca->cmd_size);
dev_dbg(dev, "cmd_hi = %X cmd_low = %X cmd_size %X\n",
pa_high, pa_low, cmd_size);
priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size); priv->cmd = crb_map_res(dev, priv, &io_res, cmd_pa, cmd_size);
if (IS_ERR(priv->cmd)) if (IS_ERR(priv->cmd)) {
return PTR_ERR(priv->cmd); ret = PTR_ERR(priv->cmd);
goto out;
}
memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8); memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8);
rsp_pa = le64_to_cpu(rsp_pa); rsp_pa = le64_to_cpu(rsp_pa);
...@@ -289,7 +355,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -289,7 +355,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
if (cmd_pa != rsp_pa) { if (cmd_pa != rsp_pa) {
priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size); priv->rsp = crb_map_res(dev, priv, &io_res, rsp_pa, rsp_size);
return PTR_ERR_OR_ZERO(priv->rsp); ret = PTR_ERR_OR_ZERO(priv->rsp);
goto out;
} }
/* According to the PTP specification, overlapping command and response /* According to the PTP specification, overlapping command and response
...@@ -297,18 +364,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv, ...@@ -297,18 +364,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
*/ */
if (cmd_size != rsp_size) { if (cmd_size != rsp_size) {
dev_err(dev, FW_BUG "overlapping command and response buffer sizes are not identical"); dev_err(dev, FW_BUG "overlapping command and response buffer sizes are not identical");
return -EINVAL; ret = -EINVAL;
goto out;
} }
priv->cmd_size = cmd_size; priv->cmd_size = cmd_size;
priv->rsp = priv->cmd; priv->rsp = priv->cmd;
return 0;
out:
crb_go_idle(dev, priv);
return ret;
} }
static int crb_acpi_add(struct acpi_device *device) static int crb_acpi_add(struct acpi_device *device)
{ {
struct acpi_table_tpm2 *buf; struct acpi_table_tpm2 *buf;
struct crb_priv *priv; struct crb_priv *priv;
struct tpm_chip *chip;
struct device *dev = &device->dev; struct device *dev = &device->dev;
acpi_status status; acpi_status status;
u32 sm; u32 sm;
...@@ -346,7 +420,33 @@ static int crb_acpi_add(struct acpi_device *device) ...@@ -346,7 +420,33 @@ static int crb_acpi_add(struct acpi_device *device)
if (rc) if (rc)
return rc; return rc;
return crb_init(device, priv); chip = tpmm_chip_alloc(dev, &tpm_crb);
if (IS_ERR(chip))
return PTR_ERR(chip);
dev_set_drvdata(&chip->dev, priv);
chip->acpi_dev_handle = device->handle;
chip->flags = TPM_CHIP_FLAG_TPM2;
rc = crb_cmd_ready(dev, priv);
if (rc)
return rc;
pm_runtime_get_noresume(dev);
pm_runtime_set_active(dev);
pm_runtime_enable(dev);
rc = tpm_chip_register(chip);
if (rc) {
crb_go_idle(dev, priv);
pm_runtime_put_noidle(dev);
pm_runtime_disable(dev);
return rc;
}
pm_runtime_put(dev);
return 0;
} }
static int crb_acpi_remove(struct acpi_device *device) static int crb_acpi_remove(struct acpi_device *device)
...@@ -356,9 +456,34 @@ static int crb_acpi_remove(struct acpi_device *device) ...@@ -356,9 +456,34 @@ static int crb_acpi_remove(struct acpi_device *device)
tpm_chip_unregister(chip); tpm_chip_unregister(chip);
pm_runtime_disable(dev);
return 0; return 0;
} }
#ifdef CONFIG_PM
static int crb_pm_runtime_suspend(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
return crb_go_idle(dev, priv);
}
static int crb_pm_runtime_resume(struct device *dev)
{
struct tpm_chip *chip = dev_get_drvdata(dev);
struct crb_priv *priv = dev_get_drvdata(&chip->dev);
return crb_cmd_ready(dev, priv);
}
#endif /* CONFIG_PM */
static const struct dev_pm_ops crb_pm = {
SET_SYSTEM_SLEEP_PM_OPS(tpm_pm_suspend, tpm_pm_resume)
SET_RUNTIME_PM_OPS(crb_pm_runtime_suspend, crb_pm_runtime_resume, NULL)
};
static struct acpi_device_id crb_device_ids[] = { static struct acpi_device_id crb_device_ids[] = {
{"MSFT0101", 0}, {"MSFT0101", 0},
{"", 0}, {"", 0},
......
...@@ -7,10 +7,11 @@ ...@@ -7,10 +7,11 @@
* Stefan Berger <stefanb@us.ibm.com> * Stefan Berger <stefanb@us.ibm.com>
* Reiner Sailer <sailer@watson.ibm.com> * Reiner Sailer <sailer@watson.ibm.com>
* Kylene Hall <kjhall@us.ibm.com> * Kylene Hall <kjhall@us.ibm.com>
* Nayna Jain <nayna@linux.vnet.ibm.com>
* *
* Maintained by: <tpmdd-devel@lists.sourceforge.net> * Maintained by: <tpmdd-devel@lists.sourceforge.net>
* *
* Access to the eventlog created by a system's firmware / BIOS * Access to the event log created by a system's firmware / BIOS
* *
* This program is free software; you can redistribute it and/or * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License * modify it under the terms of the GNU General Public License
...@@ -72,7 +73,8 @@ static const char* tcpa_pc_event_id_strings[] = { ...@@ -72,7 +73,8 @@ static const char* tcpa_pc_event_id_strings[] = {
static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos)
{ {
loff_t i; loff_t i;
struct tpm_bios_log *log = m->private; struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
void *addr = log->bios_event_log; void *addr = log->bios_event_log;
void *limit = log->bios_event_log_end; void *limit = log->bios_event_log_end;
struct tcpa_event *event; struct tcpa_event *event;
...@@ -119,7 +121,8 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, ...@@ -119,7 +121,8 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v,
loff_t *pos) loff_t *pos)
{ {
struct tcpa_event *event = v; struct tcpa_event *event = v;
struct tpm_bios_log *log = m->private; struct tpm_chip *chip = m->private;
struct tpm_bios_log *log = &chip->log;
void *limit = log->bios_event_log_end; void *limit = log->bios_event_log_end;
u32 converted_event_size; u32 converted_event_size;
u32 converted_event_type; u32 converted_event_type;
...@@ -260,13 +263,10 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) ...@@ -260,13 +263,10 @@ static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v)
static int tpm_bios_measurements_release(struct inode *inode, static int tpm_bios_measurements_release(struct inode *inode,
struct file *file) struct file *file)
{ {
struct seq_file *seq = file->private_data; struct seq_file *seq = (struct seq_file *)file->private_data;
struct tpm_bios_log *log = seq->private; struct tpm_chip *chip = (struct tpm_chip *)seq->private;
if (log) { put_device(&chip->dev);
kfree(log->bios_event_log);
kfree(log);
}
return seq_release(inode, file); return seq_release(inode, file);
} }
...@@ -304,151 +304,159 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) ...@@ -304,151 +304,159 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v)
return 0; return 0;
} }
static const struct seq_operations tpm_ascii_b_measurments_seqops = { static const struct seq_operations tpm_ascii_b_measurements_seqops = {
.start = tpm_bios_measurements_start, .start = tpm_bios_measurements_start,
.next = tpm_bios_measurements_next, .next = tpm_bios_measurements_next,
.stop = tpm_bios_measurements_stop, .stop = tpm_bios_measurements_stop,
.show = tpm_ascii_bios_measurements_show, .show = tpm_ascii_bios_measurements_show,
}; };
static const struct seq_operations tpm_binary_b_measurments_seqops = { static const struct seq_operations tpm_binary_b_measurements_seqops = {
.start = tpm_bios_measurements_start, .start = tpm_bios_measurements_start,
.next = tpm_bios_measurements_next, .next = tpm_bios_measurements_next,
.stop = tpm_bios_measurements_stop, .stop = tpm_bios_measurements_stop,
.show = tpm_binary_bios_measurements_show, .show = tpm_binary_bios_measurements_show,
}; };
static int tpm_ascii_bios_measurements_open(struct inode *inode, static int tpm_bios_measurements_open(struct inode *inode,
struct file *file) struct file *file)
{ {
int err; int err;
struct tpm_bios_log *log;
struct seq_file *seq; struct seq_file *seq;
struct tpm_chip_seqops *chip_seqops;
log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL); const struct seq_operations *seqops;
if (!log) struct tpm_chip *chip;
return -ENOMEM;
inode_lock(inode);
if ((err = read_log(log))) if (!inode->i_private) {
goto out_free; inode_unlock(inode);
return -ENODEV;
}
chip_seqops = (struct tpm_chip_seqops *)inode->i_private;
seqops = chip_seqops->seqops;
chip = chip_seqops->chip;
get_device(&chip->dev);
inode_unlock(inode);
/* now register seq file */ /* now register seq file */
err = seq_open(file, &tpm_ascii_b_measurments_seqops); err = seq_open(file, seqops);
if (!err) { if (!err) {
seq = file->private_data; seq = file->private_data;
seq->private = log; seq->private = chip;
} else {
goto out_free;
} }
out:
return err; return err;
out_free:
kfree(log->bios_event_log);
kfree(log);
goto out;
} }
static const struct file_operations tpm_ascii_bios_measurements_ops = { static const struct file_operations tpm_bios_measurements_ops = {
.open = tpm_ascii_bios_measurements_open, .owner = THIS_MODULE,
.open = tpm_bios_measurements_open,
.read = seq_read, .read = seq_read,
.llseek = seq_lseek, .llseek = seq_lseek,
.release = tpm_bios_measurements_release, .release = tpm_bios_measurements_release,
}; };
static int tpm_binary_bios_measurements_open(struct inode *inode, static int tpm_read_log(struct tpm_chip *chip)
struct file *file)
{ {
int err; int rc;
struct tpm_bios_log *log;
struct seq_file *seq;
log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL);
if (!log)
return -ENOMEM;
if ((err = read_log(log))) if (chip->log.bios_event_log != NULL) {
goto out_free; dev_dbg(&chip->dev,
"%s: ERROR - event log already initialized\n",
/* now register seq file */ __func__);
err = seq_open(file, &tpm_binary_b_measurments_seqops); return -EFAULT;
if (!err) {
seq = file->private_data;
seq->private = log;
} else {
goto out_free;
} }
out: rc = tpm_read_log_acpi(chip);
return err; if (rc != -ENODEV)
out_free: return rc;
kfree(log->bios_event_log);
kfree(log);
goto out;
}
static const struct file_operations tpm_binary_bios_measurements_ops = { return tpm_read_log_of(chip);
.open = tpm_binary_bios_measurements_open, }
.read = seq_read,
.llseek = seq_lseek,
.release = tpm_bios_measurements_release,
};
static int is_bad(void *p) /*
* tpm_bios_log_setup() - Read the event log from the firmware
* @chip: TPM chip to use.
*
* If an event log is found then the securityfs files are setup to
* export it to userspace, otherwise nothing is done.
*
* Returns -ENODEV if the firmware has no event log or securityfs is not
* supported.
*/
int tpm_bios_log_setup(struct tpm_chip *chip)
{ {
if (!p) const char *name = dev_name(&chip->dev);
return 1; unsigned int cnt;
if (IS_ERR(p) && (PTR_ERR(p) != -ENODEV)) int rc = 0;
return 1;
if (chip->flags & TPM_CHIP_FLAG_TPM2)
return 0; return 0;
}
struct dentry **tpm_bios_log_setup(const char *name) rc = tpm_read_log(chip);
{ if (rc)
struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file; return rc;
tpm_dir = securityfs_create_dir(name, NULL); cnt = 0;
if (is_bad(tpm_dir)) chip->bios_dir[cnt] = securityfs_create_dir(name, NULL);
goto out; /* NOTE: securityfs_create_dir can return ENODEV if securityfs is
* compiled out. The caller should ignore the ENODEV return code.
*/
if (IS_ERR(chip->bios_dir[cnt]))
goto err;
cnt++;
bin_file = chip->bin_log_seqops.chip = chip;
chip->bin_log_seqops.seqops = &tpm_binary_b_measurements_seqops;
chip->bios_dir[cnt] =
securityfs_create_file("binary_bios_measurements", securityfs_create_file("binary_bios_measurements",
S_IRUSR | S_IRGRP, tpm_dir, NULL, 0440, chip->bios_dir[0],
&tpm_binary_bios_measurements_ops); (void *)&chip->bin_log_seqops,
if (is_bad(bin_file)) &tpm_bios_measurements_ops);
goto out_tpm; if (IS_ERR(chip->bios_dir[cnt]))
goto err;
cnt++;
chip->ascii_log_seqops.chip = chip;
chip->ascii_log_seqops.seqops = &tpm_ascii_b_measurements_seqops;
ascii_file = chip->bios_dir[cnt] =
securityfs_create_file("ascii_bios_measurements", securityfs_create_file("ascii_bios_measurements",
S_IRUSR | S_IRGRP, tpm_dir, NULL, 0440, chip->bios_dir[0],
&tpm_ascii_bios_measurements_ops); (void *)&chip->ascii_log_seqops,
if (is_bad(ascii_file)) &tpm_bios_measurements_ops);
goto out_bin; if (IS_ERR(chip->bios_dir[cnt]))
goto err;
ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL); cnt++;
if (!ret)
goto out_ascii; return 0;
ret[0] = ascii_file; err:
ret[1] = bin_file; rc = PTR_ERR(chip->bios_dir[cnt]);
ret[2] = tpm_dir; chip->bios_dir[cnt] = NULL;
tpm_bios_log_teardown(chip);
return ret; return rc;
out_ascii:
securityfs_remove(ascii_file);
out_bin:
securityfs_remove(bin_file);
out_tpm:
securityfs_remove(tpm_dir);
out:
return NULL;
} }
void tpm_bios_log_teardown(struct dentry **lst) void tpm_bios_log_teardown(struct tpm_chip *chip)
{ {
int i; int i;
struct inode *inode;
for (i = 0; i < 3; i++) /* securityfs_remove currently doesn't take care of handling sync
securityfs_remove(lst[i]); * between removal and opening of pseudo files. To handle this, a
* workaround is added by making i_private = NULL here during removal
* and to check it during open(), both within inode_lock()/unlock().
* This design ensures that open() either safely gets kref or fails.
*/
for (i = (TPM_NUM_EVENT_LOG_FILES - 1); i >= 0; i--) {
if (chip->bios_dir[i]) {
inode = d_inode(chip->bios_dir[i]);
inode_lock(inode);
inode->i_private = NULL;
inode_unlock(inode);
securityfs_remove(chip->bios_dir[i]);
}
}
} }
...@@ -73,20 +73,24 @@ enum tcpa_pc_event_ids { ...@@ -73,20 +73,24 @@ enum tcpa_pc_event_ids {
HOST_TABLE_OF_DEVICES, HOST_TABLE_OF_DEVICES,
}; };
int read_log(struct tpm_bios_log *log); #if defined(CONFIG_ACPI)
int tpm_read_log_acpi(struct tpm_chip *chip);
#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
defined(CONFIG_ACPI)
extern struct dentry **tpm_bios_log_setup(const char *);
extern void tpm_bios_log_teardown(struct dentry **);
#else #else
static inline struct dentry **tpm_bios_log_setup(const char *name) static inline int tpm_read_log_acpi(struct tpm_chip *chip)
{ {
return NULL; return -ENODEV;
} }
static inline void tpm_bios_log_teardown(struct dentry **dir) #endif
#if defined(CONFIG_OF)
int tpm_read_log_of(struct tpm_chip *chip);
#else
static inline int tpm_read_log_of(struct tpm_chip *chip)
{ {
return -ENODEV;
} }
#endif #endif
int tpm_bios_log_setup(struct tpm_chip *chip);
void tpm_bios_log_teardown(struct tpm_chip *chip);
#endif #endif
...@@ -2,6 +2,7 @@ ...@@ -2,6 +2,7 @@
* Copyright 2012 IBM Corporation * Copyright 2012 IBM Corporation
* *
* Author: Ashley Lai <ashleydlai@gmail.com> * Author: Ashley Lai <ashleydlai@gmail.com>
* Nayna Jain <nayna@linux.vnet.ibm.com>
* *
* Maintained by: <tpmdd-devel@lists.sourceforge.net> * Maintained by: <tpmdd-devel@lists.sourceforge.net>
* *
...@@ -20,55 +21,38 @@ ...@@ -20,55 +21,38 @@
#include "tpm.h" #include "tpm.h"
#include "tpm_eventlog.h" #include "tpm_eventlog.h"
int read_log(struct tpm_bios_log *log) int tpm_read_log_of(struct tpm_chip *chip)
{ {
struct device_node *np; struct device_node *np;
const u32 *sizep; const u32 *sizep;
const u64 *basep; const u64 *basep;
struct tpm_bios_log *log;
if (log->bios_event_log != NULL) { log = &chip->log;
pr_err("%s: ERROR - Eventlog already initialized\n", __func__); if (chip->dev.parent && chip->dev.parent->of_node)
return -EFAULT; np = chip->dev.parent->of_node;
} else
np = of_find_node_by_name(NULL, "vtpm");
if (!np) {
pr_err("%s: ERROR - IBMVTPM not supported\n", __func__);
return -ENODEV; return -ENODEV;
}
sizep = of_get_property(np, "linux,sml-size", NULL); 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); basep = of_get_property(np, "linux,sml-base", NULL);
if (basep == NULL) { if (sizep == NULL && basep == NULL)
pr_err("%s: ERROR - SML not found\n", __func__); return -ENODEV;
goto cleanup_eio; if (sizep == NULL || basep == NULL)
return -EIO;
if (*sizep == 0) {
dev_warn(&chip->dev, "%s: Event log area empty\n", __func__);
return -EIO;
} }
log->bios_event_log = kmalloc(*sizep, GFP_KERNEL); log->bios_event_log = kmalloc(*sizep, GFP_KERNEL);
if (!log->bios_event_log) { if (!log->bios_event_log)
pr_err("%s: ERROR - Not enough memory for BIOS measurements\n",
__func__);
of_node_put(np);
return -ENOMEM; return -ENOMEM;
}
log->bios_event_log_end = log->bios_event_log + *sizep; log->bios_event_log_end = log->bios_event_log + *sizep;
memcpy(log->bios_event_log, __va(*basep), *sizep); memcpy(log->bios_event_log, __va(*basep), *sizep);
of_node_put(np);
return 0; return 0;
cleanup_eio:
of_node_put(np);
return -EIO;
} }
...@@ -28,6 +28,8 @@ ...@@ -28,6 +28,8 @@
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/acpi.h> #include <linux/acpi.h>
#include <linux/freezer.h> #include <linux/freezer.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include "tpm.h" #include "tpm.h"
#include "tpm_tis_core.h" #include "tpm_tis_core.h"
...@@ -354,12 +356,21 @@ static int tpm_tis_plat_remove(struct platform_device *pdev) ...@@ -354,12 +356,21 @@ static int tpm_tis_plat_remove(struct platform_device *pdev)
return 0; return 0;
} }
#ifdef CONFIG_OF
static const struct of_device_id tis_of_platform_match[] = {
{.compatible = "tcg,tpm-tis-mmio"},
{},
};
MODULE_DEVICE_TABLE(of, tis_of_platform_match);
#endif
static struct platform_driver tis_drv = { static struct platform_driver tis_drv = {
.probe = tpm_tis_plat_probe, .probe = tpm_tis_plat_probe,
.remove = tpm_tis_plat_remove, .remove = tpm_tis_plat_remove,
.driver = { .driver = {
.name = "tpm_tis", .name = "tpm_tis",
.pm = &tpm_tis_pm, .pm = &tpm_tis_pm,
.of_match_table = of_match_ptr(tis_of_platform_match),
}, },
}; };
......
...@@ -180,12 +180,19 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) ...@@ -180,12 +180,19 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev); struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
int size = 0, burstcnt, rc; int size = 0, burstcnt, rc;
while (size < count && while (size < count) {
wait_for_tpm_stat(chip, rc = wait_for_tpm_stat(chip,
TPM_STS_DATA_AVAIL | TPM_STS_VALID, TPM_STS_DATA_AVAIL | TPM_STS_VALID,
chip->timeout_c, chip->timeout_c,
&priv->read_queue, true) == 0) { &priv->read_queue, true);
burstcnt = min_t(int, get_burstcount(chip), count - size); if (rc < 0)
return rc;
burstcnt = get_burstcount(chip);
if (burstcnt < 0) {
dev_err(&chip->dev, "Unable to read burstcount\n");
return burstcnt;
}
burstcnt = min_t(int, burstcnt, count - size);
rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality), rc = tpm_tis_read_bytes(priv, TPM_DATA_FIFO(priv->locality),
burstcnt, buf + size); burstcnt, buf + size);
...@@ -229,8 +236,11 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count) ...@@ -229,8 +236,11 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
goto out; goto out;
} }
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
&priv->int_queue, false); &priv->int_queue, false) < 0) {
size = -ETIME;
goto out;
}
status = tpm_tis_status(chip); status = tpm_tis_status(chip);
if (status & TPM_STS_DATA_AVAIL) { /* retry? */ if (status & TPM_STS_DATA_AVAIL) { /* retry? */
dev_err(&chip->dev, "Error left over data\n"); dev_err(&chip->dev, "Error left over data\n");
...@@ -271,7 +281,13 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) ...@@ -271,7 +281,13 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
} }
while (count < len - 1) { while (count < len - 1) {
burstcnt = min_t(int, get_burstcount(chip), len - count - 1); burstcnt = get_burstcount(chip);
if (burstcnt < 0) {
dev_err(&chip->dev, "Unable to read burstcount\n");
rc = burstcnt;
goto out_err;
}
burstcnt = min_t(int, burstcnt, len - count - 1);
rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality), rc = tpm_tis_write_bytes(priv, TPM_DATA_FIFO(priv->locality),
burstcnt, buf + count); burstcnt, buf + count);
if (rc < 0) if (rc < 0)
...@@ -279,8 +295,11 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) ...@@ -279,8 +295,11 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
count += burstcnt; count += burstcnt;
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
&priv->int_queue, false); &priv->int_queue, false) < 0) {
rc = -ETIME;
goto out_err;
}
status = tpm_tis_status(chip); status = tpm_tis_status(chip);
if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) { if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
rc = -EIO; rc = -EIO;
...@@ -293,8 +312,11 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len) ...@@ -293,8 +312,11 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
if (rc < 0) if (rc < 0)
goto out_err; goto out_err;
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c, if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
&priv->int_queue, false); &priv->int_queue, false) < 0) {
rc = -ETIME;
goto out_err;
}
status = tpm_tis_status(chip); status = tpm_tis_status(chip);
if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) { if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) {
rc = -EIO; rc = -EIO;
...@@ -755,9 +777,13 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, ...@@ -755,9 +777,13 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
if (intfcaps & TPM_INTF_DATA_AVAIL_INT) if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
dev_dbg(dev, "\tData Avail Int Support\n"); dev_dbg(dev, "\tData Avail Int Support\n");
/* Very early on issue a command to the TPM in polling mode to make /* INTERRUPT Setup */
* sure it works. May as well use that command to set the proper init_waitqueue_head(&priv->read_queue);
* timeouts for the driver. init_waitqueue_head(&priv->int_queue);
if (irq != -1) {
/* Before doing irq testing issue a command to the TPM in polling mode
* to make sure it works. May as well use that command to set the
* proper timeouts for the driver.
*/ */
if (tpm_get_timeouts(chip)) { if (tpm_get_timeouts(chip)) {
dev_err(dev, "Could not get TPM timeouts and durations\n"); dev_err(dev, "Could not get TPM timeouts and durations\n");
...@@ -765,10 +791,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq, ...@@ -765,10 +791,6 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
goto out_err; goto out_err;
} }
/* INTERRUPT Setup */
init_waitqueue_head(&priv->read_queue);
init_waitqueue_head(&priv->int_queue);
if (irq != -1) {
if (irq) { if (irq) {
tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED, tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
irq); irq);
......
/* /*
* Copyright (C) 2015, 2016 IBM Corporation * Copyright (C) 2015, 2016 IBM Corporation
* Copyright (C) 2016 Intel Corporation
* *
* Author: Stefan Berger <stefanb@us.ibm.com> * Author: Stefan Berger <stefanb@us.ibm.com>
* *
...@@ -41,6 +42,7 @@ struct proxy_dev { ...@@ -41,6 +42,7 @@ struct proxy_dev {
long state; /* internal state */ long state; /* internal state */
#define STATE_OPENED_FLAG BIT(0) #define STATE_OPENED_FLAG BIT(0)
#define STATE_WAIT_RESPONSE_FLAG BIT(1) /* waiting for emulator response */ #define STATE_WAIT_RESPONSE_FLAG BIT(1) /* waiting for emulator response */
#define STATE_REGISTERED_FLAG BIT(2)
size_t req_len; /* length of queued TPM request */ size_t req_len; /* length of queued TPM request */
size_t resp_len; /* length of queued TPM response */ size_t resp_len; /* length of queued TPM response */
...@@ -369,12 +371,9 @@ static void vtpm_proxy_work(struct work_struct *work) ...@@ -369,12 +371,9 @@ static void vtpm_proxy_work(struct work_struct *work)
rc = tpm_chip_register(proxy_dev->chip); rc = tpm_chip_register(proxy_dev->chip);
if (rc) if (rc)
goto err;
return;
err:
vtpm_proxy_fops_undo_open(proxy_dev); vtpm_proxy_fops_undo_open(proxy_dev);
else
proxy_dev->state |= STATE_REGISTERED_FLAG;
} }
/* /*
...@@ -515,6 +514,7 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev) ...@@ -515,6 +514,7 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
*/ */
vtpm_proxy_fops_undo_open(proxy_dev); vtpm_proxy_fops_undo_open(proxy_dev);
if (proxy_dev->state & STATE_REGISTERED_FLAG)
tpm_chip_unregister(proxy_dev->chip); tpm_chip_unregister(proxy_dev->chip);
vtpm_proxy_delete_proxy_dev(proxy_dev); vtpm_proxy_delete_proxy_dev(proxy_dev);
...@@ -524,41 +524,62 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev) ...@@ -524,41 +524,62 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
* Code related to the control device /dev/vtpmx * Code related to the control device /dev/vtpmx
*/ */
/* /**
* vtpmx_fops_ioctl: ioctl on /dev/vtpmx * vtpmx_ioc_new_dev - handler for the %VTPM_PROXY_IOC_NEW_DEV ioctl
* @file: /dev/vtpmx
* @ioctl: the ioctl number
* @arg: pointer to the struct vtpmx_proxy_new_dev
* *
* Return value: * Creates an anonymous file that is used by the process acting as a TPM to
* Returns 0 on success, a negative error code otherwise. * communicate with the client processes. The function will also add a new TPM
* device through which data is proxied to this TPM acting process. The caller
* will be provided with a file descriptor to communicate with the clients and
* major and minor numbers for the TPM device.
*/ */
static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl, static long vtpmx_ioc_new_dev(struct file *file, unsigned int ioctl,
unsigned long arg) unsigned long arg)
{ {
void __user *argp = (void __user *)arg; void __user *argp = (void __user *)arg;
struct vtpm_proxy_new_dev __user *vtpm_new_dev_p; struct vtpm_proxy_new_dev __user *vtpm_new_dev_p;
struct vtpm_proxy_new_dev vtpm_new_dev; struct vtpm_proxy_new_dev vtpm_new_dev;
struct file *file; struct file *vtpm_file;
switch (ioctl) {
case VTPM_PROXY_IOC_NEW_DEV:
if (!capable(CAP_SYS_ADMIN)) if (!capable(CAP_SYS_ADMIN))
return -EPERM; return -EPERM;
vtpm_new_dev_p = argp; vtpm_new_dev_p = argp;
if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p, if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
sizeof(vtpm_new_dev))) sizeof(vtpm_new_dev)))
return -EFAULT; return -EFAULT;
file = vtpm_proxy_create_device(&vtpm_new_dev);
if (IS_ERR(file)) vtpm_file = vtpm_proxy_create_device(&vtpm_new_dev);
return PTR_ERR(file); if (IS_ERR(vtpm_file))
return PTR_ERR(vtpm_file);
if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev, if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
sizeof(vtpm_new_dev))) { sizeof(vtpm_new_dev))) {
put_unused_fd(vtpm_new_dev.fd); put_unused_fd(vtpm_new_dev.fd);
fput(file); fput(vtpm_file);
return -EFAULT; return -EFAULT;
} }
fd_install(vtpm_new_dev.fd, file); fd_install(vtpm_new_dev.fd, vtpm_file);
return 0; return 0;
}
/*
* vtpmx_fops_ioctl: ioctl on /dev/vtpmx
*
* Return value:
* Returns 0 on success, a negative error code otherwise.
*/
static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
unsigned long arg)
{
switch (ioctl) {
case VTPM_PROXY_IOC_NEW_DEV:
return vtpmx_ioc_new_dev(f, ioctl, arg);
default: default:
return -ENOIOCTLCMD; return -ENOIOCTLCMD;
} }
......
...@@ -307,7 +307,6 @@ static int tpmfront_probe(struct xenbus_device *dev, ...@@ -307,7 +307,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
rv = setup_ring(dev, priv); rv = setup_ring(dev, priv);
if (rv) { if (rv) {
chip = dev_get_drvdata(&dev->dev); chip = dev_get_drvdata(&dev->dev);
tpm_chip_unregister(chip);
ring_free(priv); ring_free(priv);
return rv; return rv;
} }
......
...@@ -1667,7 +1667,8 @@ const struct inode_operations proc_pid_link_inode_operations = { ...@@ -1667,7 +1667,8 @@ const struct inode_operations proc_pid_link_inode_operations = {
/* building an inode */ /* building an inode */
struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *task) struct inode *proc_pid_make_inode(struct super_block * sb,
struct task_struct *task, umode_t mode)
{ {
struct inode * inode; struct inode * inode;
struct proc_inode *ei; struct proc_inode *ei;
...@@ -1681,6 +1682,7 @@ struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *t ...@@ -1681,6 +1682,7 @@ struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *t
/* Common stuff */ /* Common stuff */
ei = PROC_I(inode); ei = PROC_I(inode);
inode->i_mode = mode;
inode->i_ino = get_next_ino(); inode->i_ino = get_next_ino();
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode); inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
inode->i_op = &proc_def_inode_operations; inode->i_op = &proc_def_inode_operations;
...@@ -2007,7 +2009,9 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry, ...@@ -2007,7 +2009,9 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
struct proc_inode *ei; struct proc_inode *ei;
struct inode *inode; struct inode *inode;
inode = proc_pid_make_inode(dir->i_sb, task); inode = proc_pid_make_inode(dir->i_sb, task, S_IFLNK |
((mode & FMODE_READ ) ? S_IRUSR : 0) |
((mode & FMODE_WRITE) ? S_IWUSR : 0));
if (!inode) if (!inode)
return -ENOENT; return -ENOENT;
...@@ -2016,12 +2020,6 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry, ...@@ -2016,12 +2020,6 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
inode->i_op = &proc_map_files_link_inode_operations; inode->i_op = &proc_map_files_link_inode_operations;
inode->i_size = 64; inode->i_size = 64;
inode->i_mode = S_IFLNK;
if (mode & FMODE_READ)
inode->i_mode |= S_IRUSR;
if (mode & FMODE_WRITE)
inode->i_mode |= S_IWUSR;
d_set_d_op(dentry, &tid_map_files_dentry_operations); d_set_d_op(dentry, &tid_map_files_dentry_operations);
d_add(dentry, inode); d_add(dentry, inode);
...@@ -2375,12 +2373,11 @@ static int proc_pident_instantiate(struct inode *dir, ...@@ -2375,12 +2373,11 @@ static int proc_pident_instantiate(struct inode *dir,
struct inode *inode; struct inode *inode;
struct proc_inode *ei; struct proc_inode *ei;
inode = proc_pid_make_inode(dir->i_sb, task); inode = proc_pid_make_inode(dir->i_sb, task, p->mode);
if (!inode) if (!inode)
goto out; goto out;
ei = PROC_I(inode); ei = PROC_I(inode);
inode->i_mode = p->mode;
if (S_ISDIR(inode->i_mode)) if (S_ISDIR(inode->i_mode))
set_nlink(inode, 2); /* Use getattr to fix if necessary */ set_nlink(inode, 2); /* Use getattr to fix if necessary */
if (p->iop) if (p->iop)
...@@ -3062,11 +3059,10 @@ static int proc_pid_instantiate(struct inode *dir, ...@@ -3062,11 +3059,10 @@ static int proc_pid_instantiate(struct inode *dir,
{ {
struct inode *inode; struct inode *inode;
inode = proc_pid_make_inode(dir->i_sb, task); inode = proc_pid_make_inode(dir->i_sb, task, S_IFDIR | S_IRUGO | S_IXUGO);
if (!inode) if (!inode)
goto out; goto out;
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_tgid_base_inode_operations; inode->i_op = &proc_tgid_base_inode_operations;
inode->i_fop = &proc_tgid_base_operations; inode->i_fop = &proc_tgid_base_operations;
inode->i_flags|=S_IMMUTABLE; inode->i_flags|=S_IMMUTABLE;
...@@ -3354,11 +3350,10 @@ static int proc_task_instantiate(struct inode *dir, ...@@ -3354,11 +3350,10 @@ static int proc_task_instantiate(struct inode *dir,
struct dentry *dentry, struct task_struct *task, const void *ptr) struct dentry *dentry, struct task_struct *task, const void *ptr)
{ {
struct inode *inode; struct inode *inode;
inode = proc_pid_make_inode(dir->i_sb, task); inode = proc_pid_make_inode(dir->i_sb, task, S_IFDIR | S_IRUGO | S_IXUGO);
if (!inode) if (!inode)
goto out; goto out;
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
inode->i_op = &proc_tid_base_inode_operations; inode->i_op = &proc_tid_base_inode_operations;
inode->i_fop = &proc_tid_base_operations; inode->i_fop = &proc_tid_base_operations;
inode->i_flags|=S_IMMUTABLE; inode->i_flags|=S_IMMUTABLE;
......
...@@ -183,14 +183,13 @@ proc_fd_instantiate(struct inode *dir, struct dentry *dentry, ...@@ -183,14 +183,13 @@ proc_fd_instantiate(struct inode *dir, struct dentry *dentry,
struct proc_inode *ei; struct proc_inode *ei;
struct inode *inode; struct inode *inode;
inode = proc_pid_make_inode(dir->i_sb, task); inode = proc_pid_make_inode(dir->i_sb, task, S_IFLNK);
if (!inode) if (!inode)
goto out; goto out;
ei = PROC_I(inode); ei = PROC_I(inode);
ei->fd = fd; ei->fd = fd;
inode->i_mode = S_IFLNK;
inode->i_op = &proc_pid_link_inode_operations; inode->i_op = &proc_pid_link_inode_operations;
inode->i_size = 64; inode->i_size = 64;
...@@ -322,14 +321,13 @@ proc_fdinfo_instantiate(struct inode *dir, struct dentry *dentry, ...@@ -322,14 +321,13 @@ proc_fdinfo_instantiate(struct inode *dir, struct dentry *dentry,
struct proc_inode *ei; struct proc_inode *ei;
struct inode *inode; struct inode *inode;
inode = proc_pid_make_inode(dir->i_sb, task); inode = proc_pid_make_inode(dir->i_sb, task, S_IFREG | S_IRUSR);
if (!inode) if (!inode)
goto out; goto out;
ei = PROC_I(inode); ei = PROC_I(inode);
ei->fd = fd; ei->fd = fd;
inode->i_mode = S_IFREG | S_IRUSR;
inode->i_fop = &proc_fdinfo_file_operations; inode->i_fop = &proc_fdinfo_file_operations;
d_set_d_op(dentry, &tid_fd_dentry_operations); d_set_d_op(dentry, &tid_fd_dentry_operations);
......
...@@ -162,7 +162,7 @@ extern int proc_pid_statm(struct seq_file *, struct pid_namespace *, ...@@ -162,7 +162,7 @@ extern int proc_pid_statm(struct seq_file *, struct pid_namespace *,
extern const struct dentry_operations pid_dentry_operations; extern const struct dentry_operations pid_dentry_operations;
extern int pid_getattr(struct vfsmount *, struct dentry *, struct kstat *); extern int pid_getattr(struct vfsmount *, struct dentry *, struct kstat *);
extern int proc_setattr(struct dentry *, struct iattr *); extern int proc_setattr(struct dentry *, struct iattr *);
extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *); extern struct inode *proc_pid_make_inode(struct super_block *, struct task_struct *, umode_t);
extern int pid_revalidate(struct dentry *, unsigned int); extern int pid_revalidate(struct dentry *, unsigned int);
extern int pid_delete_dentry(const struct dentry *); extern int pid_delete_dentry(const struct dentry *);
extern int proc_pid_readdir(struct file *, struct dir_context *); extern int proc_pid_readdir(struct file *, struct dir_context *);
......
...@@ -92,12 +92,11 @@ static int proc_ns_instantiate(struct inode *dir, ...@@ -92,12 +92,11 @@ static int proc_ns_instantiate(struct inode *dir,
struct inode *inode; struct inode *inode;
struct proc_inode *ei; struct proc_inode *ei;
inode = proc_pid_make_inode(dir->i_sb, task); inode = proc_pid_make_inode(dir->i_sb, task, S_IFLNK | S_IRWXUGO);
if (!inode) if (!inode)
goto out; goto out;
ei = PROC_I(inode); ei = PROC_I(inode);
inode->i_mode = S_IFLNK|S_IRWXUGO;
inode->i_op = &proc_ns_link_inode_operations; inode->i_op = &proc_ns_link_inode_operations;
ei->ns_ops = ns_ops; ei->ns_ops = ns_ops;
......
/* /*
* Definitions for the VTPM proxy driver * Definitions for the VTPM proxy driver
* Copyright (c) 2015, 2016, IBM Corporation * Copyright (c) 2015, 2016, IBM Corporation
* Copyright (C) 2016 Intel Corporation
* *
* This program is free software; you can redistribute it and/or modify it * This program is free software; you can redistribute it and/or modify it
* under the terms and conditions of the GNU General Public License, * under the terms and conditions of the GNU General Public License,
...@@ -18,8 +19,23 @@ ...@@ -18,8 +19,23 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioctl.h> #include <linux/ioctl.h>
/* ioctls */ /**
* enum vtpm_proxy_flags - flags for the proxy TPM
* @VTPM_PROXY_FLAG_TPM2: the proxy TPM uses TPM 2.0 protocol
*/
enum vtpm_proxy_flags {
VTPM_PROXY_FLAG_TPM2 = 1,
};
/**
* struct vtpm_proxy_new_dev - parameter structure for the
* %VTPM_PROXY_IOC_NEW_DEV ioctl
* @flags: flags for the proxy TPM
* @tpm_num: index of the TPM device
* @fd: the file descriptor used by the proxy TPM
* @major: the major number of the TPM device
* @minor: the minor number of the TPM device
*/
struct vtpm_proxy_new_dev { struct vtpm_proxy_new_dev {
__u32 flags; /* input */ __u32 flags; /* input */
__u32 tpm_num; /* output */ __u32 tpm_num; /* output */
...@@ -28,9 +44,6 @@ struct vtpm_proxy_new_dev { ...@@ -28,9 +44,6 @@ struct vtpm_proxy_new_dev {
__u32 minor; /* output */ __u32 minor; /* output */
}; };
/* above flags */
#define VTPM_PROXY_FLAG_TPM2 1 /* emulator is TPM 2 */
#define VTPM_PROXY_IOC_NEW_DEV _IOWR(0xa1, 0x00, struct vtpm_proxy_new_dev) #define VTPM_PROXY_IOC_NEW_DEV _IOWR(0xa1, 0x00, struct vtpm_proxy_new_dev)
#endif /* _UAPI_LINUX_VTPM_PROXY_H */ #endif /* _UAPI_LINUX_VTPM_PROXY_H */
...@@ -41,8 +41,7 @@ ...@@ -41,8 +41,7 @@
* outside of a lifetime-guarded section. In general, this * outside of a lifetime-guarded section. In general, this
* is only needed for handling filters shared across tasks. * is only needed for handling filters shared across tasks.
* @prev: points to a previously installed, or inherited, filter * @prev: points to a previously installed, or inherited, filter
* @len: the number of instructions in the program * @prog: the BPF program to evaluate
* @insnsi: the BPF program instructions to evaluate
* *
* seccomp_filter objects are organized in a tree linked via the @prev * seccomp_filter objects are organized in a tree linked via the @prev
* pointer. For any task, it appears to be a singly-linked list starting * pointer. For any task, it appears to be a singly-linked list starting
...@@ -168,8 +167,8 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen) ...@@ -168,8 +167,8 @@ static int seccomp_check_filter(struct sock_filter *filter, unsigned int flen)
} }
/** /**
* seccomp_run_filters - evaluates all seccomp filters against @syscall * seccomp_run_filters - evaluates all seccomp filters against @sd
* @syscall: number of the current system call * @sd: optional seccomp data to be passed to filters
* *
* Returns valid seccomp BPF response codes. * Returns valid seccomp BPF response codes.
*/ */
......
...@@ -36,13 +36,13 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG) ...@@ -36,13 +36,13 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG)
HOSTLOADLIBES_bpf-fancy += $(MFLAG) HOSTLOADLIBES_bpf-fancy += $(MFLAG)
HOSTLOADLIBES_dropper += $(MFLAG) HOSTLOADLIBES_dropper += $(MFLAG)
endif endif
always := $(hostprogs-y) always := $(hostprogs-m)
else else
# MIPS system calls are defined based on the -mabi that is passed # MIPS system calls are defined based on the -mabi that is passed
# to the toolchain which may or may not be a valid option # to the toolchain which may or may not be a valid option
# for the host toolchain. So disable tests if target architecture # for the host toolchain. So disable tests if target architecture
# is MIPS but the host isn't. # is MIPS but the host isn't.
ifndef CONFIG_MIPS ifndef CONFIG_MIPS
always := $(hostprogs-y) always := $(hostprogs-m)
endif endif
endif endif
...@@ -18,41 +18,41 @@ ...@@ -18,41 +18,41 @@
int bpf_resolve_jumps(struct bpf_labels *labels, int bpf_resolve_jumps(struct bpf_labels *labels,
struct sock_filter *filter, size_t count) struct sock_filter *filter, size_t count)
{ {
struct sock_filter *begin = filter; size_t i;
__u8 insn = count - 1;
if (count < 1) if (count < 1 || count > BPF_MAXINSNS)
return -1; return -1;
/* /*
* Walk it once, backwards, to build the label table and do fixups. * Walk it once, backwards, to build the label table and do fixups.
* Since backward jumps are disallowed by BPF, this is easy. * Since backward jumps are disallowed by BPF, this is easy.
*/ */
filter += insn; for (i = 0; i < count; ++i) {
for (; filter >= begin; --insn, --filter) { size_t offset = count - i - 1;
if (filter->code != (BPF_JMP+BPF_JA)) struct sock_filter *instr = &filter[offset];
if (instr->code != (BPF_JMP+BPF_JA))
continue; continue;
switch ((filter->jt<<8)|filter->jf) { switch ((instr->jt<<8)|instr->jf) {
case (JUMP_JT<<8)|JUMP_JF: case (JUMP_JT<<8)|JUMP_JF:
if (labels->labels[filter->k].location == 0xffffffff) { if (labels->labels[instr->k].location == 0xffffffff) {
fprintf(stderr, "Unresolved label: '%s'\n", fprintf(stderr, "Unresolved label: '%s'\n",
labels->labels[filter->k].label); labels->labels[instr->k].label);
return 1; return 1;
} }
filter->k = labels->labels[filter->k].location - instr->k = labels->labels[instr->k].location -
(insn + 1); (offset + 1);
filter->jt = 0; instr->jt = 0;
filter->jf = 0; instr->jf = 0;
continue; continue;
case (LABEL_JT<<8)|LABEL_JF: case (LABEL_JT<<8)|LABEL_JF:
if (labels->labels[filter->k].location != 0xffffffff) { if (labels->labels[instr->k].location != 0xffffffff) {
fprintf(stderr, "Duplicate label use: '%s'\n", fprintf(stderr, "Duplicate label use: '%s'\n",
labels->labels[filter->k].label); labels->labels[instr->k].label);
return 1; return 1;
} }
labels->labels[filter->k].location = insn; labels->labels[instr->k].location = offset;
filter->k = 0; /* fall through */ instr->k = 0; /* fall through */
filter->jt = 0; instr->jt = 0;
filter->jf = 0; instr->jf = 0;
continue; continue;
} }
} }
......
...@@ -11,7 +11,6 @@ ...@@ -11,7 +11,6 @@
* When run, returns the specified errno for the specified * When run, returns the specified errno for the specified
* system call number against the given architecture. * system call number against the given architecture.
* *
* Run this one as root as PR_SET_NO_NEW_PRIVS is not called.
*/ */
#include <errno.h> #include <errno.h>
...@@ -42,8 +41,12 @@ static int install_filter(int nr, int arch, int error) ...@@ -42,8 +41,12 @@ static int install_filter(int nr, int arch, int error)
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])), .len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
.filter = filter, .filter = filter,
}; };
if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
perror("prctl(NO_NEW_PRIVS)");
return 1;
}
if (prctl(PR_SET_SECCOMP, 2, &prog)) { if (prctl(PR_SET_SECCOMP, 2, &prog)) {
perror("prctl"); perror("prctl(PR_SET_SECCOMP)");
return 1; return 1;
} }
return 0; return 0;
......
...@@ -51,7 +51,7 @@ static bool init_keyring __initdata; ...@@ -51,7 +51,7 @@ static bool init_keyring __initdata;
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen, int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
const char *digest, int digestlen) const char *digest, int digestlen)
{ {
if (id >= INTEGRITY_KEYRING_MAX) if (id >= INTEGRITY_KEYRING_MAX || siglen < 2)
return -EINVAL; return -EINVAL;
if (!keyring[id]) { if (!keyring[id]) {
......
...@@ -145,6 +145,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry, ...@@ -145,6 +145,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
/* check value type */ /* check value type */
switch (xattr_data->type) { switch (xattr_data->type) {
case EVM_XATTR_HMAC: case EVM_XATTR_HMAC:
if (xattr_len != sizeof(struct evm_ima_xattr_data)) {
evm_status = INTEGRITY_FAIL;
goto out;
}
rc = evm_calc_hmac(dentry, xattr_name, xattr_value, rc = evm_calc_hmac(dentry, xattr_name, xattr_value,
xattr_value_len, calc.digest); xattr_value_len, calc.digest);
if (rc) if (rc)
......
...@@ -130,6 +130,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, ...@@ -130,6 +130,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
int xattr_len) int xattr_len)
{ {
struct signature_v2_hdr *sig; struct signature_v2_hdr *sig;
enum hash_algo ret;
if (!xattr_value || xattr_len < 2) if (!xattr_value || xattr_len < 2)
/* return default hash algo */ /* return default hash algo */
...@@ -143,7 +144,9 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value, ...@@ -143,7 +144,9 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
return sig->hash_algo; return sig->hash_algo;
break; break;
case IMA_XATTR_DIGEST_NG: case IMA_XATTR_DIGEST_NG:
return xattr_value->digest[0]; ret = xattr_value->digest[0];
if (ret < HASH_ALGO__LAST)
return ret;
break; break;
case IMA_XATTR_DIGEST: case IMA_XATTR_DIGEST:
/* this is for backward compatibility */ /* this is for backward compatibility */
...@@ -384,14 +387,10 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name, ...@@ -384,14 +387,10 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
result = ima_protect_xattr(dentry, xattr_name, xattr_value, result = ima_protect_xattr(dentry, xattr_name, xattr_value,
xattr_value_len); xattr_value_len);
if (result == 1) { if (result == 1) {
bool digsig;
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST)) if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
return -EINVAL; return -EINVAL;
digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG); ima_reset_appraise_flags(d_backing_inode(dentry),
if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE)) (xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
return -EPERM;
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
result = 0; result = 0;
} }
return result; return result;
......
...@@ -401,7 +401,7 @@ static int ima_release_policy(struct inode *inode, struct file *file) ...@@ -401,7 +401,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
const char *cause = valid_policy ? "completed" : "failed"; const char *cause = valid_policy ? "completed" : "failed";
if ((file->f_flags & O_ACCMODE) == O_RDONLY) if ((file->f_flags & O_ACCMODE) == O_RDONLY)
return 0; return seq_release(inode, file);
if (valid_policy && ima_check_policy() < 0) { if (valid_policy && ima_check_policy() < 0) {
cause = "failed"; cause = "failed";
......
...@@ -115,7 +115,8 @@ int __init ima_init(void) ...@@ -115,7 +115,8 @@ int __init ima_init(void)
ima_used_chip = 1; ima_used_chip = 1;
if (!ima_used_chip) if (!ima_used_chip)
pr_info("No TPM chip found, activating TPM-bypass!\n"); pr_info("No TPM chip found, activating TPM-bypass! (rc=%d)\n",
rc);
rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA); rc = integrity_init_keyring(INTEGRITY_KEYRING_IMA);
if (rc) if (rc)
......
...@@ -231,12 +231,13 @@ static int inode_alloc_security(struct inode *inode) ...@@ -231,12 +231,13 @@ static int inode_alloc_security(struct inode *inode)
if (!isec) if (!isec)
return -ENOMEM; return -ENOMEM;
mutex_init(&isec->lock); spin_lock_init(&isec->lock);
INIT_LIST_HEAD(&isec->list); INIT_LIST_HEAD(&isec->list);
isec->inode = inode; isec->inode = inode;
isec->sid = SECINITSID_UNLABELED; isec->sid = SECINITSID_UNLABELED;
isec->sclass = SECCLASS_FILE; isec->sclass = SECCLASS_FILE;
isec->task_sid = sid; isec->task_sid = sid;
isec->initialized = LABEL_INVALID;
inode->i_security = isec; inode->i_security = isec;
return 0; return 0;
...@@ -247,7 +248,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -247,7 +248,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
/* /*
* Try reloading inode security labels that have been marked as invalid. The * Try reloading inode security labels that have been marked as invalid. The
* @may_sleep parameter indicates when sleeping and thus reloading labels is * @may_sleep parameter indicates when sleeping and thus reloading labels is
* allowed; when set to false, returns ERR_PTR(-ECHILD) when the label is * allowed; when set to false, returns -ECHILD when the label is
* invalid. The @opt_dentry parameter should be set to a dentry of the inode; * invalid. The @opt_dentry parameter should be set to a dentry of the inode;
* when no dentry is available, set it to NULL instead. * when no dentry is available, set it to NULL instead.
*/ */
...@@ -1100,11 +1101,12 @@ static int selinux_parse_opts_str(char *options, ...@@ -1100,11 +1101,12 @@ static int selinux_parse_opts_str(char *options,
} }
rc = -ENOMEM; rc = -ENOMEM;
opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_ATOMIC); opts->mnt_opts = kcalloc(NUM_SEL_MNT_OPTS, sizeof(char *), GFP_KERNEL);
if (!opts->mnt_opts) if (!opts->mnt_opts)
goto out_err; goto out_err;
opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int), GFP_ATOMIC); opts->mnt_opts_flags = kcalloc(NUM_SEL_MNT_OPTS, sizeof(int),
GFP_KERNEL);
if (!opts->mnt_opts_flags) { if (!opts->mnt_opts_flags) {
kfree(opts->mnt_opts); kfree(opts->mnt_opts);
goto out_err; goto out_err;
...@@ -1380,7 +1382,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1380,7 +1382,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
{ {
struct superblock_security_struct *sbsec = NULL; struct superblock_security_struct *sbsec = NULL;
struct inode_security_struct *isec = inode->i_security; struct inode_security_struct *isec = inode->i_security;
u32 sid; u32 task_sid, sid = 0;
u16 sclass;
struct dentry *dentry; struct dentry *dentry;
#define INITCONTEXTLEN 255 #define INITCONTEXTLEN 255
char *context = NULL; char *context = NULL;
...@@ -1388,12 +1391,15 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1388,12 +1391,15 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
int rc = 0; int rc = 0;
if (isec->initialized == LABEL_INITIALIZED) if (isec->initialized == LABEL_INITIALIZED)
goto out; return 0;
mutex_lock(&isec->lock); spin_lock(&isec->lock);
if (isec->initialized == LABEL_INITIALIZED) if (isec->initialized == LABEL_INITIALIZED)
goto out_unlock; goto out_unlock;
if (isec->sclass == SECCLASS_FILE)
isec->sclass = inode_mode_to_security_class(inode->i_mode);
sbsec = inode->i_sb->s_security; sbsec = inode->i_sb->s_security;
if (!(sbsec->flags & SE_SBINITIALIZED)) { if (!(sbsec->flags & SE_SBINITIALIZED)) {
/* Defer initialization until selinux_complete_init, /* Defer initialization until selinux_complete_init,
...@@ -1406,12 +1412,18 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1406,12 +1412,18 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
goto out_unlock; goto out_unlock;
} }
sclass = isec->sclass;
task_sid = isec->task_sid;
sid = isec->sid;
isec->initialized = LABEL_PENDING;
spin_unlock(&isec->lock);
switch (sbsec->behavior) { switch (sbsec->behavior) {
case SECURITY_FS_USE_NATIVE: case SECURITY_FS_USE_NATIVE:
break; break;
case SECURITY_FS_USE_XATTR: case SECURITY_FS_USE_XATTR:
if (!(inode->i_opflags & IOP_XATTR)) { if (!(inode->i_opflags & IOP_XATTR)) {
isec->sid = sbsec->def_sid; sid = sbsec->def_sid;
break; break;
} }
/* Need a dentry, since the xattr API requires one. /* Need a dentry, since the xattr API requires one.
...@@ -1433,7 +1445,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1433,7 +1445,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
* inode_doinit with a dentry, before these inodes could * inode_doinit with a dentry, before these inodes could
* be used again by userspace. * be used again by userspace.
*/ */
goto out_unlock; goto out;
} }
len = INITCONTEXTLEN; len = INITCONTEXTLEN;
...@@ -1441,7 +1453,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1441,7 +1453,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
if (!context) { if (!context) {
rc = -ENOMEM; rc = -ENOMEM;
dput(dentry); dput(dentry);
goto out_unlock; goto out;
} }
context[len] = '\0'; context[len] = '\0';
rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
...@@ -1452,14 +1464,14 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1452,14 +1464,14 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0); rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0);
if (rc < 0) { if (rc < 0) {
dput(dentry); dput(dentry);
goto out_unlock; goto out;
} }
len = rc; len = rc;
context = kmalloc(len+1, GFP_NOFS); context = kmalloc(len+1, GFP_NOFS);
if (!context) { if (!context) {
rc = -ENOMEM; rc = -ENOMEM;
dput(dentry); dput(dentry);
goto out_unlock; goto out;
} }
context[len] = '\0'; context[len] = '\0';
rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len); rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, context, len);
...@@ -1471,7 +1483,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1471,7 +1483,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
"%d for dev=%s ino=%ld\n", __func__, "%d for dev=%s ino=%ld\n", __func__,
-rc, inode->i_sb->s_id, inode->i_ino); -rc, inode->i_sb->s_id, inode->i_ino);
kfree(context); kfree(context);
goto out_unlock; goto out;
} }
/* Map ENODATA to the default file SID */ /* Map ENODATA to the default file SID */
sid = sbsec->def_sid; sid = sbsec->def_sid;
...@@ -1501,29 +1513,25 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1501,29 +1513,25 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
} }
} }
kfree(context); kfree(context);
isec->sid = sid;
break; break;
case SECURITY_FS_USE_TASK: case SECURITY_FS_USE_TASK:
isec->sid = isec->task_sid; sid = task_sid;
break; break;
case SECURITY_FS_USE_TRANS: case SECURITY_FS_USE_TRANS:
/* Default to the fs SID. */ /* Default to the fs SID. */
isec->sid = sbsec->sid; sid = sbsec->sid;
/* Try to obtain a transition SID. */ /* Try to obtain a transition SID. */
isec->sclass = inode_mode_to_security_class(inode->i_mode); rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid);
rc = security_transition_sid(isec->task_sid, sbsec->sid,
isec->sclass, NULL, &sid);
if (rc) if (rc)
goto out_unlock; goto out;
isec->sid = sid;
break; break;
case SECURITY_FS_USE_MNTPOINT: case SECURITY_FS_USE_MNTPOINT:
isec->sid = sbsec->mntpoint_sid; sid = sbsec->mntpoint_sid;
break; break;
default: default:
/* Default to the fs superblock SID. */ /* Default to the fs superblock SID. */
isec->sid = sbsec->sid; sid = sbsec->sid;
if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) { if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) {
/* We must have a dentry to determine the label on /* We must have a dentry to determine the label on
...@@ -1546,25 +1554,30 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent ...@@ -1546,25 +1554,30 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
* could be used again by userspace. * could be used again by userspace.
*/ */
if (!dentry) if (!dentry)
goto out_unlock; goto out;
isec->sclass = inode_mode_to_security_class(inode->i_mode); rc = selinux_genfs_get_sid(dentry, sclass,
rc = selinux_genfs_get_sid(dentry, isec->sclass,
sbsec->flags, &sid); sbsec->flags, &sid);
dput(dentry); dput(dentry);
if (rc) if (rc)
goto out_unlock; goto out;
isec->sid = sid;
} }
break; break;
} }
out:
spin_lock(&isec->lock);
if (isec->initialized == LABEL_PENDING) {
if (!sid || rc) {
isec->initialized = LABEL_INVALID;
goto out_unlock;
}
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
isec->sid = sid;
}
out_unlock: out_unlock:
mutex_unlock(&isec->lock); spin_unlock(&isec->lock);
out:
if (isec->sclass == SECCLASS_FILE)
isec->sclass = inode_mode_to_security_class(inode->i_mode);
return rc; return rc;
} }
...@@ -3198,9 +3211,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name, ...@@ -3198,9 +3211,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
} }
isec = backing_inode_security(dentry); isec = backing_inode_security(dentry);
spin_lock(&isec->lock);
isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid; isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
spin_unlock(&isec->lock);
return; return;
} }
...@@ -3293,9 +3308,11 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, ...@@ -3293,9 +3308,11 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
if (rc) if (rc)
return rc; return rc;
spin_lock(&isec->lock);
isec->sclass = inode_mode_to_security_class(inode->i_mode); isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = newsid; isec->sid = newsid;
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
spin_unlock(&isec->lock);
return 0; return 0;
} }
...@@ -3956,8 +3973,11 @@ static void selinux_task_to_inode(struct task_struct *p, ...@@ -3956,8 +3973,11 @@ static void selinux_task_to_inode(struct task_struct *p,
struct inode_security_struct *isec = inode->i_security; struct inode_security_struct *isec = inode->i_security;
u32 sid = task_sid(p); u32 sid = task_sid(p);
spin_lock(&isec->lock);
isec->sclass = inode_mode_to_security_class(inode->i_mode);
isec->sid = sid; isec->sid = sid;
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
spin_unlock(&isec->lock);
} }
/* Returns error only if unable to parse addresses */ /* Returns error only if unable to parse addresses */
...@@ -4276,24 +4296,24 @@ static int selinux_socket_post_create(struct socket *sock, int family, ...@@ -4276,24 +4296,24 @@ static int selinux_socket_post_create(struct socket *sock, int family,
const struct task_security_struct *tsec = current_security(); const struct task_security_struct *tsec = current_security();
struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock)); struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
struct sk_security_struct *sksec; struct sk_security_struct *sksec;
u16 sclass = socket_type_to_security_class(family, type, protocol);
u32 sid = SECINITSID_KERNEL;
int err = 0; int err = 0;
isec->sclass = socket_type_to_security_class(family, type, protocol); if (!kern) {
err = socket_sockcreate_sid(tsec, sclass, &sid);
if (kern)
isec->sid = SECINITSID_KERNEL;
else {
err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid));
if (err) if (err)
return err; return err;
} }
isec->sclass = sclass;
isec->sid = sid;
isec->initialized = LABEL_INITIALIZED; isec->initialized = LABEL_INITIALIZED;
if (sock->sk) { if (sock->sk) {
sksec = sock->sk->sk_security; sksec = sock->sk->sk_security;
sksec->sid = isec->sid; sksec->sclass = sclass;
sksec->sclass = isec->sclass; sksec->sid = sid;
err = selinux_netlbl_socket_post_create(sock->sk, family); err = selinux_netlbl_socket_post_create(sock->sk, family);
} }
...@@ -4469,16 +4489,22 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock) ...@@ -4469,16 +4489,22 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
int err; int err;
struct inode_security_struct *isec; struct inode_security_struct *isec;
struct inode_security_struct *newisec; struct inode_security_struct *newisec;
u16 sclass;
u32 sid;
err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT); err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT);
if (err) if (err)
return err; return err;
newisec = inode_security_novalidate(SOCK_INODE(newsock));
isec = inode_security_novalidate(SOCK_INODE(sock)); isec = inode_security_novalidate(SOCK_INODE(sock));
newisec->sclass = isec->sclass; spin_lock(&isec->lock);
newisec->sid = isec->sid; sclass = isec->sclass;
sid = isec->sid;
spin_unlock(&isec->lock);
newisec = inode_security_novalidate(SOCK_INODE(newsock));
newisec->sclass = sclass;
newisec->sid = sid;
newisec->initialized = LABEL_INITIALIZED; newisec->initialized = LABEL_INITIALIZED;
return 0; return 0;
...@@ -5981,9 +6007,9 @@ static void selinux_inode_invalidate_secctx(struct inode *inode) ...@@ -5981,9 +6007,9 @@ static void selinux_inode_invalidate_secctx(struct inode *inode)
{ {
struct inode_security_struct *isec = inode->i_security; struct inode_security_struct *isec = inode->i_security;
mutex_lock(&isec->lock); spin_lock(&isec->lock);
isec->initialized = LABEL_INVALID; isec->initialized = LABEL_INVALID;
mutex_unlock(&isec->lock); spin_unlock(&isec->lock);
} }
/* /*
......
...@@ -24,6 +24,10 @@ ...@@ -24,6 +24,10 @@
#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \ #define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \
"wake_alarm", "block_suspend", "audit_read" "wake_alarm", "block_suspend", "audit_read"
#if CAP_LAST_CAP > CAP_AUDIT_READ
#error New capability defined, please update COMMON_CAP2_PERMS.
#endif
/* /*
* Note: The name for any socket class should be suffixed by "socket", * Note: The name for any socket class should be suffixed by "socket",
* and doesn't contain more than one substr of "socket". * and doesn't contain more than one substr of "socket".
......
...@@ -39,7 +39,8 @@ struct task_security_struct { ...@@ -39,7 +39,8 @@ struct task_security_struct {
enum label_initialized { enum label_initialized {
LABEL_INVALID, /* invalid or not initialized */ LABEL_INVALID, /* invalid or not initialized */
LABEL_INITIALIZED /* initialized */ LABEL_INITIALIZED, /* initialized */
LABEL_PENDING
}; };
struct inode_security_struct { struct inode_security_struct {
...@@ -52,7 +53,7 @@ struct inode_security_struct { ...@@ -52,7 +53,7 @@ struct inode_security_struct {
u32 sid; /* SID of this object */ u32 sid; /* SID of this object */
u16 sclass; /* security class of this object */ u16 sclass; /* security class of this object */
unsigned char initialized; /* initialization flag */ unsigned char initialized; /* initialization flag */
struct mutex lock; spinlock_t lock;
}; };
struct file_security_struct { struct file_security_struct {
......
...@@ -163,6 +163,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf, ...@@ -163,6 +163,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
if (sscanf(page, "%d", &new_value) != 1) if (sscanf(page, "%d", &new_value) != 1)
goto out; goto out;
new_value = !!new_value;
if (new_value != selinux_enforcing) { if (new_value != selinux_enforcing) {
length = task_has_security(current, SECURITY__SETENFORCE); length = task_has_security(current, SECURITY__SETENFORCE);
if (length) if (length)
...@@ -1301,7 +1303,7 @@ static int sel_make_bools(void) ...@@ -1301,7 +1303,7 @@ static int sel_make_bools(void)
goto out; goto out;
isec->sid = sid; isec->sid = sid;
isec->initialized = 1; isec->initialized = LABEL_INITIALIZED;
inode->i_fop = &sel_bool_ops; inode->i_fop = &sel_bool_ops;
inode->i_ino = i|SEL_BOOL_INO_OFFSET; inode->i_ino = i|SEL_BOOL_INO_OFFSET;
d_add(dentry, inode); d_add(dentry, inode);
...@@ -1834,7 +1836,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent) ...@@ -1834,7 +1836,7 @@ static int sel_fill_super(struct super_block *sb, void *data, int silent)
isec = (struct inode_security_struct *)inode->i_security; isec = (struct inode_security_struct *)inode->i_security;
isec->sid = SECINITSID_DEVNULL; isec->sid = SECINITSID_DEVNULL;
isec->sclass = SECCLASS_CHR_FILE; isec->sclass = SECCLASS_CHR_FILE;
isec->initialized = 1; isec->initialized = LABEL_INITIALIZED;
init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3)); init_special_inode(inode, S_IFCHR | S_IRUGO | S_IWUGO, MKDEV(MEM_MAJOR, 3));
d_add(dentry, inode); d_add(dentry, inode);
......
...@@ -336,7 +336,6 @@ extern int smack_ptrace_rule; ...@@ -336,7 +336,6 @@ extern int smack_ptrace_rule;
extern struct smack_known smack_known_floor; extern struct smack_known smack_known_floor;
extern struct smack_known smack_known_hat; extern struct smack_known smack_known_hat;
extern struct smack_known smack_known_huh; extern struct smack_known smack_known_huh;
extern struct smack_known smack_known_invalid;
extern struct smack_known smack_known_star; extern struct smack_known smack_known_star;
extern struct smack_known smack_known_web; extern struct smack_known smack_known_web;
......
...@@ -36,11 +36,6 @@ struct smack_known smack_known_floor = { ...@@ -36,11 +36,6 @@ struct smack_known smack_known_floor = {
.smk_secid = 5, .smk_secid = 5,
}; };
struct smack_known smack_known_invalid = {
.smk_known = "",
.smk_secid = 6,
};
struct smack_known smack_known_web = { struct smack_known smack_known_web = {
.smk_known = "@", .smk_known = "@",
.smk_secid = 7, .smk_secid = 7,
...@@ -615,7 +610,7 @@ struct smack_known *smack_from_secid(const u32 secid) ...@@ -615,7 +610,7 @@ struct smack_known *smack_from_secid(const u32 secid)
* of a secid that is not on the list. * of a secid that is not on the list.
*/ */
rcu_read_unlock(); rcu_read_unlock();
return &smack_known_invalid; return &smack_known_huh;
} }
/* /*
......
...@@ -692,12 +692,12 @@ static int smack_parse_opts_str(char *options, ...@@ -692,12 +692,12 @@ static int smack_parse_opts_str(char *options,
} }
} }
opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_ATOMIC); opts->mnt_opts = kcalloc(NUM_SMK_MNT_OPTS, sizeof(char *), GFP_KERNEL);
if (!opts->mnt_opts) if (!opts->mnt_opts)
goto out_err; goto out_err;
opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int), opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
GFP_ATOMIC); GFP_KERNEL);
if (!opts->mnt_opts_flags) { if (!opts->mnt_opts_flags) {
kfree(opts->mnt_opts); kfree(opts->mnt_opts);
goto out_err; goto out_err;
...@@ -769,6 +769,31 @@ static int smack_set_mnt_opts(struct super_block *sb, ...@@ -769,6 +769,31 @@ static int smack_set_mnt_opts(struct super_block *sb,
if (sp->smk_flags & SMK_SB_INITIALIZED) if (sp->smk_flags & SMK_SB_INITIALIZED)
return 0; return 0;
if (!smack_privileged(CAP_MAC_ADMIN)) {
/*
* Unprivileged mounts don't get to specify Smack values.
*/
if (num_opts)
return -EPERM;
/*
* Unprivileged mounts get root and default from the caller.
*/
skp = smk_of_current();
sp->smk_root = skp;
sp->smk_default = skp;
/*
* For a handful of fs types with no user-controlled
* backing store it's okay to trust security labels
* in the filesystem. The rest are untrusted.
*/
if (sb->s_user_ns != &init_user_ns &&
sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
sb->s_magic != RAMFS_MAGIC) {
transmute = 1;
sp->smk_flags |= SMK_SB_UNTRUSTED;
}
}
sp->smk_flags |= SMK_SB_INITIALIZED; sp->smk_flags |= SMK_SB_INITIALIZED;
for (i = 0; i < num_opts; i++) { for (i = 0; i < num_opts; i++) {
...@@ -809,31 +834,6 @@ static int smack_set_mnt_opts(struct super_block *sb, ...@@ -809,31 +834,6 @@ static int smack_set_mnt_opts(struct super_block *sb,
} }
} }
if (!smack_privileged(CAP_MAC_ADMIN)) {
/*
* Unprivileged mounts don't get to specify Smack values.
*/
if (num_opts)
return -EPERM;
/*
* Unprivileged mounts get root and default from the caller.
*/
skp = smk_of_current();
sp->smk_root = skp;
sp->smk_default = skp;
/*
* For a handful of fs types with no user-controlled
* backing store it's okay to trust security labels
* in the filesystem. The rest are untrusted.
*/
if (sb->s_user_ns != &init_user_ns &&
sb->s_magic != SYSFS_MAGIC && sb->s_magic != TMPFS_MAGIC &&
sb->s_magic != RAMFS_MAGIC) {
transmute = 1;
sp->smk_flags |= SMK_SB_UNTRUSTED;
}
}
/* /*
* Initialize the root inode. * Initialize the root inode.
*/ */
...@@ -1384,20 +1384,14 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name, ...@@ -1384,20 +1384,14 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
skp = smk_import_entry(value, size); skp = smk_import_entry(value, size);
if (!IS_ERR(skp)) if (!IS_ERR(skp))
isp->smk_inode = skp; isp->smk_inode = skp;
else
isp->smk_inode = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) { } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
skp = smk_import_entry(value, size); skp = smk_import_entry(value, size);
if (!IS_ERR(skp)) if (!IS_ERR(skp))
isp->smk_task = skp; isp->smk_task = skp;
else
isp->smk_task = &smack_known_invalid;
} else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { } else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
skp = smk_import_entry(value, size); skp = smk_import_entry(value, size);
if (!IS_ERR(skp)) if (!IS_ERR(skp))
isp->smk_mmap = skp; isp->smk_mmap = skp;
else
isp->smk_mmap = &smack_known_invalid;
} }
return; return;
...@@ -2023,6 +2017,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, ...@@ -2023,6 +2017,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
if (new_tsp == NULL) if (new_tsp == NULL)
return -ENOMEM; return -ENOMEM;
new->security = new_tsp;
rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp); rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
if (rc != 0) if (rc != 0)
return rc; return rc;
...@@ -2032,7 +2028,6 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, ...@@ -2032,7 +2028,6 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
if (rc != 0) if (rc != 0)
return rc; return rc;
new->security = new_tsp;
return 0; return 0;
} }
...@@ -2067,12 +2062,8 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old) ...@@ -2067,12 +2062,8 @@ static void smack_cred_transfer(struct cred *new, const struct cred *old)
static int smack_kernel_act_as(struct cred *new, u32 secid) static int smack_kernel_act_as(struct cred *new, u32 secid)
{ {
struct task_smack *new_tsp = new->security; struct task_smack *new_tsp = new->security;
struct smack_known *skp = smack_from_secid(secid);
if (skp == NULL)
return -EINVAL;
new_tsp->smk_task = skp; new_tsp->smk_task = smack_from_secid(secid);
return 0; return 0;
} }
...@@ -2337,8 +2328,16 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags) ...@@ -2337,8 +2328,16 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
if (ssp == NULL) if (ssp == NULL)
return -ENOMEM; return -ENOMEM;
/*
* Sockets created by kernel threads receive web label.
*/
if (unlikely(current->flags & PF_KTHREAD)) {
ssp->smk_in = &smack_known_web;
ssp->smk_out = &smack_known_web;
} else {
ssp->smk_in = skp; ssp->smk_in = skp;
ssp->smk_out = skp; ssp->smk_out = skp;
}
ssp->smk_packet = NULL; ssp->smk_packet = NULL;
sk->sk_security = ssp; sk->sk_security = ssp;
...@@ -2434,18 +2433,18 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip) ...@@ -2434,18 +2433,18 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
return NULL; return NULL;
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) { list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
/*
* we break after finding the first match because
* the list is sorted from longest to shortest mask
* so we have found the most specific match
*/
for (found = 1, i = 0; i < 8; i++) {
/* /*
* If the label is NULL the entry has * If the label is NULL the entry has
* been renounced. Ignore it. * been renounced. Ignore it.
*/ */
if (snp->smk_label == NULL) if (snp->smk_label == NULL)
continue; continue;
/*
* we break after finding the first match because
* the list is sorted from longest to shortest mask
* so we have found the most specific match
*/
for (found = 1, i = 0; i < 8; i++) {
if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) != if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
snp->smk_host.s6_addr16[i]) { snp->smk_host.s6_addr16[i]) {
found = 0; found = 0;
...@@ -3661,10 +3660,11 @@ static int smack_setprocattr(struct task_struct *p, char *name, ...@@ -3661,10 +3660,11 @@ static int smack_setprocattr(struct task_struct *p, char *name,
return PTR_ERR(skp); return PTR_ERR(skp);
/* /*
* No process is ever allowed the web ("@") label. * No process is ever allowed the web ("@") label
* and the star ("*") label.
*/ */
if (skp == &smack_known_web) if (skp == &smack_known_web || skp == &smack_known_star)
return -EPERM; return -EINVAL;
if (!smack_privileged(CAP_MAC_ADMIN)) { if (!smack_privileged(CAP_MAC_ADMIN)) {
rc = -EPERM; rc = -EPERM;
...@@ -3884,21 +3884,11 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, ...@@ -3884,21 +3884,11 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
return &smack_known_web; return &smack_known_web;
return &smack_known_star; return &smack_known_star;
} }
if ((sap->flags & NETLBL_SECATTR_SECID) != 0) { if ((sap->flags & NETLBL_SECATTR_SECID) != 0)
/* /*
* Looks like a fallback, which gives us a secid. * Looks like a fallback, which gives us a secid.
*/ */
skp = smack_from_secid(sap->attr.secid); return smack_from_secid(sap->attr.secid);
/*
* This has got to be a bug because it is
* impossible to specify a fallback without
* specifying the label, which will ensure
* it has a secid, and the only way to get a
* secid is from a fallback.
*/
BUG_ON(skp == NULL);
return skp;
}
/* /*
* Without guidance regarding the smack value * Without guidance regarding the smack value
* for the packet fall back on the network * for the packet fall back on the network
...@@ -4761,7 +4751,6 @@ static __init void init_smack_known_list(void) ...@@ -4761,7 +4751,6 @@ static __init void init_smack_known_list(void)
mutex_init(&smack_known_hat.smk_rules_lock); mutex_init(&smack_known_hat.smk_rules_lock);
mutex_init(&smack_known_floor.smk_rules_lock); mutex_init(&smack_known_floor.smk_rules_lock);
mutex_init(&smack_known_star.smk_rules_lock); mutex_init(&smack_known_star.smk_rules_lock);
mutex_init(&smack_known_invalid.smk_rules_lock);
mutex_init(&smack_known_web.smk_rules_lock); mutex_init(&smack_known_web.smk_rules_lock);
/* /*
* Initialize rule lists * Initialize rule lists
...@@ -4770,7 +4759,6 @@ static __init void init_smack_known_list(void) ...@@ -4770,7 +4759,6 @@ static __init void init_smack_known_list(void)
INIT_LIST_HEAD(&smack_known_hat.smk_rules); INIT_LIST_HEAD(&smack_known_hat.smk_rules);
INIT_LIST_HEAD(&smack_known_star.smk_rules); INIT_LIST_HEAD(&smack_known_star.smk_rules);
INIT_LIST_HEAD(&smack_known_floor.smk_rules); INIT_LIST_HEAD(&smack_known_floor.smk_rules);
INIT_LIST_HEAD(&smack_known_invalid.smk_rules);
INIT_LIST_HEAD(&smack_known_web.smk_rules); INIT_LIST_HEAD(&smack_known_web.smk_rules);
/* /*
* Create the known labels list * Create the known labels list
...@@ -4779,7 +4767,6 @@ static __init void init_smack_known_list(void) ...@@ -4779,7 +4767,6 @@ static __init void init_smack_known_list(void)
smk_insert_entry(&smack_known_hat); smk_insert_entry(&smack_known_hat);
smk_insert_entry(&smack_known_star); smk_insert_entry(&smack_known_star);
smk_insert_entry(&smack_known_floor); smk_insert_entry(&smack_known_floor);
smk_insert_entry(&smack_known_invalid);
smk_insert_entry(&smack_known_web); smk_insert_entry(&smack_known_web);
} }
......
...@@ -2996,9 +2996,6 @@ static int __init init_smk_fs(void) ...@@ -2996,9 +2996,6 @@ static int __init init_smk_fs(void)
if (err == 0 && rc < 0) if (err == 0 && rc < 0)
err = rc; err = rc;
rc = smk_preset_netlabel(&smack_known_huh); rc = smk_preset_netlabel(&smack_known_huh);
if (err == 0 && rc < 0)
err = rc;
rc = smk_preset_netlabel(&smack_known_invalid);
if (err == 0 && rc < 0) if (err == 0 && rc < 0)
err = rc; err = rc;
rc = smk_preset_netlabel(&smack_known_star); rc = smk_preset_netlabel(&smack_known_star);
......
...@@ -309,7 +309,7 @@ static int task_is_descendant(struct task_struct *parent, ...@@ -309,7 +309,7 @@ static int task_is_descendant(struct task_struct *parent,
* @tracer: the task_struct of the process attempting ptrace * @tracer: the task_struct of the process attempting ptrace
* @tracee: the task_struct of the process to be ptraced * @tracee: the task_struct of the process to be ptraced
* *
* Returns 1 if tracer has is ptracer exception ancestor for tracee. * Returns 1 if tracer has a ptracer exception ancestor for tracee.
*/ */
static int ptracer_exception_found(struct task_struct *tracer, static int ptracer_exception_found(struct task_struct *tracer,
struct task_struct *tracee) struct task_struct *tracee)
...@@ -320,6 +320,18 @@ static int ptracer_exception_found(struct task_struct *tracer, ...@@ -320,6 +320,18 @@ static int ptracer_exception_found(struct task_struct *tracer,
bool found = false; bool found = false;
rcu_read_lock(); rcu_read_lock();
/*
* If there's already an active tracing relationship, then make an
* exception for the sake of other accesses, like process_vm_rw().
*/
parent = ptrace_parent(tracee);
if (parent != NULL && same_thread_group(parent, tracer)) {
rc = 1;
goto unlock;
}
/* Look for a PR_SET_PTRACER relationship. */
if (!thread_group_leader(tracee)) if (!thread_group_leader(tracee))
tracee = rcu_dereference(tracee->group_leader); tracee = rcu_dereference(tracee->group_leader);
list_for_each_entry_rcu(relation, &ptracer_relations, node) { list_for_each_entry_rcu(relation, &ptracer_relations, node) {
...@@ -334,6 +346,8 @@ static int ptracer_exception_found(struct task_struct *tracer, ...@@ -334,6 +346,8 @@ static int ptracer_exception_found(struct task_struct *tracer,
if (found && (parent == NULL || task_is_descendant(parent, tracer))) if (found && (parent == NULL || task_is_descendant(parent, tracer)))
rc = 1; rc = 1;
unlock:
rcu_read_unlock(); rcu_read_unlock();
return rc; return rc;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment