mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-28 06:34:12 +08:00
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 ...
This commit is contained in:
commit
683b96f4d1
41
Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
Normal file
41
Documentation/devicetree/bindings/security/tpm/ibmvtpm.txt
Normal file
@ -0,0 +1,41 @@
|
||||
* 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>;
|
||||
};
|
21
Documentation/devicetree/bindings/security/tpm/tpm-i2c.txt
Normal file
21
Documentation/devicetree/bindings/security/tpm/tpm-i2c.txt
Normal file
@ -0,0 +1,21 @@
|
||||
* 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";
|
||||
};
|
@ -0,0 +1,25 @@
|
||||
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:
|
||||
(40 ascii zeros)
|
||||
blobauth= ascii hex auth for sealed data default 0x00...
|
||||
(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)
|
||||
pcrlock= pcr number to be extended to "lock" blob
|
||||
migratable= 0|1 indicating permission to reseal to new PCR values,
|
||||
|
@ -32,7 +32,7 @@ config TCG_TIS_CORE
|
||||
|
||||
config TCG_TIS
|
||||
tristate "TPM Interface Specification 1.2 Interface / TPM 2.0 FIFO Interface"
|
||||
depends on X86
|
||||
depends on X86 || OF
|
||||
select TCG_TIS_CORE
|
||||
---help---
|
||||
If you have a TPM security chip that is compliant with the
|
||||
|
@ -2,16 +2,10 @@
|
||||
# Makefile for the kernel tpm device drivers.
|
||||
#
|
||||
obj-$(CONFIG_TCG_TPM) += tpm.o
|
||||
tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o
|
||||
tpm-$(CONFIG_ACPI) += tpm_ppi.o
|
||||
|
||||
ifdef CONFIG_ACPI
|
||||
tpm-y += tpm_eventlog.o tpm_acpi.o
|
||||
else
|
||||
ifdef CONFIG_TCG_IBMVTPM
|
||||
tpm-y += tpm_eventlog.o tpm_of.o
|
||||
endif
|
||||
endif
|
||||
tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-chip.o tpm2-cmd.o \
|
||||
tpm_eventlog.o
|
||||
tpm-$(CONFIG_ACPI) += tpm_ppi.o tpm_acpi.o
|
||||
tpm-$(CONFIG_OF) += tpm_of.o
|
||||
obj-$(CONFIG_TCG_TIS_CORE) += tpm_tis_core.o
|
||||
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
|
||||
obj-$(CONFIG_TCG_TIS_SPI) += tpm_tis_spi.o
|
||||
|
@ -127,6 +127,7 @@ static void tpm_dev_release(struct device *dev)
|
||||
idr_remove(&dev_nums_idr, chip->dev_num);
|
||||
mutex_unlock(&idr_lock);
|
||||
|
||||
kfree(chip->log.bios_event_log);
|
||||
kfree(chip);
|
||||
}
|
||||
|
||||
@ -276,27 +277,6 @@ static void tpm_del_char_device(struct tpm_chip *chip)
|
||||
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)
|
||||
{
|
||||
struct attribute **i;
|
||||
@ -363,20 +343,20 @@ int tpm_chip_register(struct tpm_chip *chip)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = tpm1_chip_register(chip);
|
||||
if (rc)
|
||||
tpm_sysfs_add_device(chip);
|
||||
|
||||
rc = tpm_bios_log_setup(chip);
|
||||
if (rc != 0 && rc != -ENODEV)
|
||||
return rc;
|
||||
|
||||
tpm_add_ppi(chip);
|
||||
|
||||
rc = tpm_add_char_device(chip);
|
||||
if (rc) {
|
||||
tpm1_chip_unregister(chip);
|
||||
tpm_bios_log_teardown(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
chip->flags |= TPM_CHIP_FLAG_REGISTERED;
|
||||
|
||||
rc = tpm_add_legacy_sysfs(chip);
|
||||
if (rc) {
|
||||
tpm_chip_unregister(chip);
|
||||
@ -402,12 +382,8 @@ EXPORT_SYMBOL_GPL(tpm_chip_register);
|
||||
*/
|
||||
void tpm_chip_unregister(struct tpm_chip *chip)
|
||||
{
|
||||
if (!(chip->flags & TPM_CHIP_FLAG_REGISTERED))
|
||||
return;
|
||||
|
||||
tpm_del_legacy_sysfs(chip);
|
||||
|
||||
tpm1_chip_unregister(chip);
|
||||
tpm_bios_log_teardown(chip);
|
||||
tpm_del_char_device(chip);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_chip_unregister);
|
||||
|
@ -29,6 +29,7 @@
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_eventlog.h"
|
||||
@ -356,6 +357,9 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
||||
mutex_lock(&chip->tpm_mutex);
|
||||
|
||||
if (chip->dev.parent)
|
||||
pm_runtime_get_sync(chip->dev.parent);
|
||||
|
||||
rc = chip->ops->send(chip, (u8 *) buf, count);
|
||||
if (rc < 0) {
|
||||
dev_err(&chip->dev,
|
||||
@ -397,6 +401,9 @@ out_recv:
|
||||
dev_err(&chip->dev,
|
||||
"tpm_transmit: tpm_recv: error %zd\n", rc);
|
||||
out:
|
||||
if (chip->dev.parent)
|
||||
pm_runtime_put_sync(chip->dev.parent);
|
||||
|
||||
if (!(flags & TPM_TRANSMIT_UNLOCKED))
|
||||
mutex_unlock(&chip->tpm_mutex);
|
||||
return rc;
|
||||
@ -437,26 +444,29 @@ static const struct tpm_input_header tpm_getcap_header = {
|
||||
.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)
|
||||
{
|
||||
struct tpm_cmd_t tpm_cmd;
|
||||
int rc;
|
||||
|
||||
tpm_cmd.header.in = tpm_getcap_header;
|
||||
if (subcap_id == CAP_VERSION_1_1 || subcap_id == CAP_VERSION_1_2) {
|
||||
tpm_cmd.params.getcap_in.cap = subcap_id;
|
||||
if (subcap_id == TPM_CAP_VERSION_1_1 ||
|
||||
subcap_id == TPM_CAP_VERSION_1_2) {
|
||||
tpm_cmd.params.getcap_in.cap = cpu_to_be32(subcap_id);
|
||||
/*subcap field not necessary */
|
||||
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(0);
|
||||
tpm_cmd.header.in.length -= cpu_to_be32(sizeof(__be32));
|
||||
} else {
|
||||
if (subcap_id == TPM_CAP_FLAG_PERM ||
|
||||
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
|
||||
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 = 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,
|
||||
desc);
|
||||
@ -488,12 +498,14 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type)
|
||||
|
||||
int tpm_get_timeouts(struct tpm_chip *chip)
|
||||
{
|
||||
struct tpm_cmd_t tpm_cmd;
|
||||
cap_t cap;
|
||||
unsigned long new_timeout[4];
|
||||
unsigned long old_timeout[4];
|
||||
struct duration_t *duration_cap;
|
||||
ssize_t rc;
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_HAVE_TIMEOUTS)
|
||||
return 0;
|
||||
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2) {
|
||||
/* Fixed timeouts for TPM2 */
|
||||
chip->timeout_a = msecs_to_jiffies(TPM2_TIMEOUT_A);
|
||||
@ -506,46 +518,30 @@ int tpm_get_timeouts(struct tpm_chip *chip)
|
||||
msecs_to_jiffies(TPM2_DURATION_MEDIUM);
|
||||
chip->duration[TPM_LONG] =
|
||||
msecs_to_jiffies(TPM2_DURATION_LONG);
|
||||
|
||||
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
|
||||
return 0;
|
||||
}
|
||||
|
||||
tpm_cmd.header.in = tpm_getcap_header;
|
||||
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
|
||||
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
|
||||
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
|
||||
rc = tpm_transmit_cmd(chip, &tpm_cmd, TPM_INTERNAL_RESULT_SIZE, 0,
|
||||
NULL);
|
||||
|
||||
rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
|
||||
"attempting to determine the timeouts");
|
||||
if (rc == TPM_ERR_INVALID_POSTINIT) {
|
||||
/* The TPM is not started, we are the first to talk to it.
|
||||
Execute a startup command. */
|
||||
dev_info(&chip->dev, "Issuing TPM_STARTUP");
|
||||
dev_info(&chip->dev, "Issuing TPM_STARTUP\n");
|
||||
if (tpm_startup(chip, TPM_ST_CLEAR))
|
||||
return rc;
|
||||
|
||||
tpm_cmd.header.in = tpm_getcap_header;
|
||||
tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP;
|
||||
tpm_cmd.params.getcap_in.subcap_size = cpu_to_be32(4);
|
||||
tpm_cmd.params.getcap_in.subcap = TPM_CAP_PROP_TIS_TIMEOUT;
|
||||
rc = 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;
|
||||
rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_TIMEOUT, &cap,
|
||||
"attempting to determine the timeouts");
|
||||
}
|
||||
if (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) + 4 * sizeof(u32))
|
||||
return -EINVAL;
|
||||
|
||||
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);
|
||||
old_timeout[0] = be32_to_cpu(cap.timeout.a);
|
||||
old_timeout[1] = be32_to_cpu(cap.timeout.b);
|
||||
old_timeout[2] = be32_to_cpu(cap.timeout.c);
|
||||
old_timeout[3] = be32_to_cpu(cap.timeout.d);
|
||||
memcpy(new_timeout, old_timeout, sizeof(new_timeout));
|
||||
|
||||
/*
|
||||
@ -583,29 +579,17 @@ int tpm_get_timeouts(struct tpm_chip *chip)
|
||||
chip->timeout_c = usecs_to_jiffies(new_timeout[2]);
|
||||
chip->timeout_d = usecs_to_jiffies(new_timeout[3]);
|
||||
|
||||
duration:
|
||||
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");
|
||||
rc = tpm_getcap(chip, TPM_CAP_PROP_TIS_DURATION, &cap,
|
||||
"attempting to determine the durations");
|
||||
if (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] =
|
||||
usecs_to_jiffies(be32_to_cpu(duration_cap->tpm_short));
|
||||
usecs_to_jiffies(be32_to_cpu(cap.duration.tpm_short));
|
||||
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] =
|
||||
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
|
||||
* value wrong and apparently reports msecs rather than usecs. So we
|
||||
@ -619,6 +603,8 @@ duration:
|
||||
chip->duration_adjusted = true;
|
||||
dev_info(&chip->dev, "Adjusting TPM timeout parameters.");
|
||||
}
|
||||
|
||||
chip->flags |= TPM_CHIP_FLAG_HAVE_TIMEOUTS;
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_get_timeouts);
|
||||
@ -726,6 +712,14 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf)
|
||||
}
|
||||
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
|
||||
* @chip_num: tpm idx # or AN&
|
||||
@ -736,14 +730,6 @@ EXPORT_SYMBOL_GPL(tpm_pcr_read);
|
||||
* isn't, protect against the chip disappearing, by incrementing
|
||||
* 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)
|
||||
{
|
||||
struct tpm_cmd_t cmd;
|
||||
|
@ -193,7 +193,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
|
||||
be32_to_cpu(cap.manufacturer_id));
|
||||
|
||||
/* 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");
|
||||
if (!rc) {
|
||||
str += sprintf(str,
|
||||
@ -204,7 +204,7 @@ static ssize_t caps_show(struct device *dev, struct device_attribute *attr,
|
||||
cap.tpm_version_1_2.revMinor);
|
||||
} else {
|
||||
/* 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");
|
||||
if (rc)
|
||||
return 0;
|
||||
@ -284,6 +284,9 @@ static const struct attribute_group tpm_dev_group = {
|
||||
|
||||
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
|
||||
* is called before ops is null'd and the sysfs core synchronizes this
|
||||
* removal so that no callbacks are running or can run again
|
||||
|
@ -35,11 +35,14 @@
|
||||
#include <linux/cdev.h>
|
||||
#include <linux/highmem.h>
|
||||
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
enum tpm_const {
|
||||
TPM_MINOR = 224, /* officially assigned */
|
||||
TPM_BUFSIZE = 4096,
|
||||
TPM_NUM_DEVICES = 65536,
|
||||
TPM_RETRY = 50, /* 5 seconds */
|
||||
TPM_NUM_EVENT_LOG_FILES = 3,
|
||||
};
|
||||
|
||||
enum tpm_timeout {
|
||||
@ -139,10 +142,15 @@ enum tpm2_startup_types {
|
||||
#define TPM_PPI_VERSION_LEN 3
|
||||
|
||||
enum tpm_chip_flags {
|
||||
TPM_CHIP_FLAG_REGISTERED = BIT(0),
|
||||
TPM_CHIP_FLAG_TPM2 = BIT(1),
|
||||
TPM_CHIP_FLAG_IRQ = BIT(2),
|
||||
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 {
|
||||
@ -156,6 +164,10 @@ struct tpm_chip {
|
||||
struct rw_semaphore ops_sem;
|
||||
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;
|
||||
|
||||
int dev_num; /* /dev/tpm# */
|
||||
@ -171,7 +183,7 @@ struct tpm_chip {
|
||||
unsigned long duration[3]; /* jiffies */
|
||||
bool duration_adjusted;
|
||||
|
||||
struct dentry **bios_dir;
|
||||
struct dentry *bios_dir[TPM_NUM_EVENT_LOG_FILES];
|
||||
|
||||
const struct attribute_group *groups[3];
|
||||
unsigned int groups_cnt;
|
||||
@ -282,21 +294,20 @@ typedef union {
|
||||
} cap_t;
|
||||
|
||||
enum tpm_capabilities {
|
||||
TPM_CAP_FLAG = cpu_to_be32(4),
|
||||
TPM_CAP_PROP = cpu_to_be32(5),
|
||||
CAP_VERSION_1_1 = cpu_to_be32(0x06),
|
||||
CAP_VERSION_1_2 = cpu_to_be32(0x1A)
|
||||
TPM_CAP_FLAG = 4,
|
||||
TPM_CAP_PROP = 5,
|
||||
TPM_CAP_VERSION_1_1 = 0x06,
|
||||
TPM_CAP_VERSION_1_2 = 0x1A,
|
||||
};
|
||||
|
||||
enum tpm_sub_capabilities {
|
||||
TPM_CAP_PROP_PCR = cpu_to_be32(0x101),
|
||||
TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103),
|
||||
TPM_CAP_FLAG_PERM = cpu_to_be32(0x108),
|
||||
TPM_CAP_FLAG_VOL = cpu_to_be32(0x109),
|
||||
TPM_CAP_PROP_OWNER = cpu_to_be32(0x111),
|
||||
TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115),
|
||||
TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120),
|
||||
|
||||
TPM_CAP_PROP_PCR = 0x101,
|
||||
TPM_CAP_PROP_MANUFACTURER = 0x103,
|
||||
TPM_CAP_FLAG_PERM = 0x108,
|
||||
TPM_CAP_FLAG_VOL = 0x109,
|
||||
TPM_CAP_PROP_OWNER = 0x111,
|
||||
TPM_CAP_PROP_TIS_TIMEOUT = 0x115,
|
||||
TPM_CAP_PROP_TIS_DURATION = 0x120,
|
||||
};
|
||||
|
||||
struct tpm_getcap_params_in {
|
||||
@ -484,7 +495,7 @@ ssize_t tpm_transmit(struct tpm_chip *chip, const u8 *buf, size_t bufsiz,
|
||||
unsigned int flags);
|
||||
ssize_t tpm_transmit_cmd(struct tpm_chip *chip, const void *cmd, int len,
|
||||
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);
|
||||
int tpm_get_timeouts(struct tpm_chip *);
|
||||
int tpm1_auto_startup(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
|
||||
* @payload: the key data in clear and encrypted form
|
||||
* @options: authentication values and other options
|
||||
|
@ -6,10 +6,11 @@
|
||||
* Stefan Berger <stefanb@us.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
* Kylene Hall <kjhall@us.ibm.com>
|
||||
* Nayna Jain <nayna@linux.vnet.ibm.com>
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -45,29 +46,28 @@ struct acpi_tcpa {
|
||||
};
|
||||
|
||||
/* 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;
|
||||
acpi_status status;
|
||||
void __iomem *virt;
|
||||
u64 len, start;
|
||||
struct tpm_bios_log *log;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
printk(KERN_ERR
|
||||
"%s: ERROR - Eventlog already initialized\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
log = &chip->log;
|
||||
|
||||
/* Unfortuntely ACPI does not associate the event log with a specific
|
||||
* TPM, like PPI. Thus all ACPI TPMs will read the same log.
|
||||
*/
|
||||
if (!chip->acpi_dev_handle)
|
||||
return -ENODEV;
|
||||
|
||||
/* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
|
||||
status = acpi_get_table(ACPI_SIG_TCPA, 1,
|
||||
(struct acpi_table_header **)&buff);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENODEV;
|
||||
|
||||
switch(buff->platform_class) {
|
||||
case BIOS_SERVER:
|
||||
@ -81,29 +81,29 @@ int read_log(struct tpm_bios_log *log)
|
||||
break;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
/* malloc EventLog space */
|
||||
log->bios_event_log = kmalloc(len, GFP_KERNEL);
|
||||
if (!log->bios_event_log) {
|
||||
printk("%s: ERROR - Not enough Memory for BIOS measurements\n",
|
||||
__func__);
|
||||
if (!log->bios_event_log)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + len;
|
||||
|
||||
virt = acpi_os_map_iomem(start, len);
|
||||
if (!virt) {
|
||||
kfree(log->bios_event_log);
|
||||
printk("%s: ERROR - Unable to map memory\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
if (!virt)
|
||||
goto err;
|
||||
|
||||
memcpy_fromio(log->bios_event_log, virt, len);
|
||||
|
||||
acpi_os_unmap_iomem(virt, len);
|
||||
return 0;
|
||||
|
||||
err:
|
||||
kfree(log->bios_event_log);
|
||||
log->bios_event_log = NULL;
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
|
@ -19,6 +19,7 @@
|
||||
#include <linux/highmem.h>
|
||||
#include <linux/rculist.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/pm_runtime.h>
|
||||
#include "tpm.h"
|
||||
|
||||
#define ACPI_SIG_TPM2 "TPM2"
|
||||
@ -83,7 +84,71 @@ struct crb_priv {
|
||||
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)
|
||||
{
|
||||
@ -196,21 +261,6 @@ static const struct tpm_class_ops tpm_crb = {
|
||||
.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)
|
||||
{
|
||||
struct resource *io_res = data;
|
||||
@ -249,6 +299,7 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
struct list_head resources;
|
||||
struct resource io_res;
|
||||
struct device *dev = &device->dev;
|
||||
u32 pa_high, pa_low;
|
||||
u64 cmd_pa;
|
||||
u32 cmd_size;
|
||||
u64 rsp_pa;
|
||||
@ -276,12 +327,27 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
if (IS_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);
|
||||
|
||||
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);
|
||||
if (IS_ERR(priv->cmd))
|
||||
return PTR_ERR(priv->cmd);
|
||||
if (IS_ERR(priv->cmd)) {
|
||||
ret = PTR_ERR(priv->cmd);
|
||||
goto out;
|
||||
}
|
||||
|
||||
memcpy_fromio(&rsp_pa, &priv->cca->rsp_pa, 8);
|
||||
rsp_pa = le64_to_cpu(rsp_pa);
|
||||
@ -289,7 +355,8 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
|
||||
if (cmd_pa != rsp_pa) {
|
||||
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
|
||||
@ -297,18 +364,25 @@ static int crb_map_io(struct acpi_device *device, struct crb_priv *priv,
|
||||
*/
|
||||
if (cmd_size != rsp_size) {
|
||||
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->rsp = priv->cmd;
|
||||
return 0;
|
||||
|
||||
out:
|
||||
crb_go_idle(dev, priv);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int crb_acpi_add(struct acpi_device *device)
|
||||
{
|
||||
struct acpi_table_tpm2 *buf;
|
||||
struct crb_priv *priv;
|
||||
struct tpm_chip *chip;
|
||||
struct device *dev = &device->dev;
|
||||
acpi_status status;
|
||||
u32 sm;
|
||||
@ -346,7 +420,33 @@ static int crb_acpi_add(struct acpi_device *device)
|
||||
if (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)
|
||||
@ -356,9 +456,34 @@ static int crb_acpi_remove(struct acpi_device *device)
|
||||
|
||||
tpm_chip_unregister(chip);
|
||||
|
||||
pm_runtime_disable(dev);
|
||||
|
||||
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[] = {
|
||||
{"MSFT0101", 0},
|
||||
{"", 0},
|
||||
|
@ -7,10 +7,11 @@
|
||||
* Stefan Berger <stefanb@us.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
* Kylene Hall <kjhall@us.ibm.com>
|
||||
* Nayna Jain <nayna@linux.vnet.ibm.com>
|
||||
*
|
||||
* 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
|
||||
* modify it under the terms of the GNU General Public License
|
||||
@ -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)
|
||||
{
|
||||
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 *limit = log->bios_event_log_end;
|
||||
struct tcpa_event *event;
|
||||
@ -119,7 +121,8 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v,
|
||||
loff_t *pos)
|
||||
{
|
||||
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;
|
||||
u32 converted_event_size;
|
||||
u32 converted_event_type;
|
||||
@ -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,
|
||||
struct file *file)
|
||||
{
|
||||
struct seq_file *seq = file->private_data;
|
||||
struct tpm_bios_log *log = seq->private;
|
||||
struct seq_file *seq = (struct seq_file *)file->private_data;
|
||||
struct tpm_chip *chip = (struct tpm_chip *)seq->private;
|
||||
|
||||
if (log) {
|
||||
kfree(log->bios_event_log);
|
||||
kfree(log);
|
||||
}
|
||||
put_device(&chip->dev);
|
||||
|
||||
return seq_release(inode, file);
|
||||
}
|
||||
@ -304,151 +304,159 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v)
|
||||
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,
|
||||
.next = tpm_bios_measurements_next,
|
||||
.stop = tpm_bios_measurements_stop,
|
||||
.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,
|
||||
.next = tpm_bios_measurements_next,
|
||||
.stop = tpm_bios_measurements_stop,
|
||||
.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)
|
||||
{
|
||||
int err;
|
||||
struct tpm_bios_log *log;
|
||||
struct seq_file *seq;
|
||||
struct tpm_chip_seqops *chip_seqops;
|
||||
const struct seq_operations *seqops;
|
||||
struct tpm_chip *chip;
|
||||
|
||||
log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL);
|
||||
if (!log)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = read_log(log)))
|
||||
goto out_free;
|
||||
inode_lock(inode);
|
||||
if (!inode->i_private) {
|
||||
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 */
|
||||
err = seq_open(file, &tpm_ascii_b_measurments_seqops);
|
||||
err = seq_open(file, seqops);
|
||||
if (!err) {
|
||||
seq = file->private_data;
|
||||
seq->private = log;
|
||||
} else {
|
||||
goto out_free;
|
||||
seq->private = chip;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
out_free:
|
||||
kfree(log->bios_event_log);
|
||||
kfree(log);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static const struct file_operations tpm_ascii_bios_measurements_ops = {
|
||||
.open = tpm_ascii_bios_measurements_open,
|
||||
static const struct file_operations tpm_bios_measurements_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.open = tpm_bios_measurements_open,
|
||||
.read = seq_read,
|
||||
.llseek = seq_lseek,
|
||||
.release = tpm_bios_measurements_release,
|
||||
};
|
||||
|
||||
static int tpm_binary_bios_measurements_open(struct inode *inode,
|
||||
struct file *file)
|
||||
static int tpm_read_log(struct tpm_chip *chip)
|
||||
{
|
||||
int err;
|
||||
struct tpm_bios_log *log;
|
||||
struct seq_file *seq;
|
||||
int rc;
|
||||
|
||||
log = kzalloc(sizeof(struct tpm_bios_log), GFP_KERNEL);
|
||||
if (!log)
|
||||
return -ENOMEM;
|
||||
|
||||
if ((err = read_log(log)))
|
||||
goto out_free;
|
||||
|
||||
/* now register seq file */
|
||||
err = seq_open(file, &tpm_binary_b_measurments_seqops);
|
||||
if (!err) {
|
||||
seq = file->private_data;
|
||||
seq->private = log;
|
||||
} else {
|
||||
goto out_free;
|
||||
if (chip->log.bios_event_log != NULL) {
|
||||
dev_dbg(&chip->dev,
|
||||
"%s: ERROR - event log already initialized\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
out:
|
||||
return err;
|
||||
out_free:
|
||||
kfree(log->bios_event_log);
|
||||
kfree(log);
|
||||
goto out;
|
||||
rc = tpm_read_log_acpi(chip);
|
||||
if (rc != -ENODEV)
|
||||
return rc;
|
||||
|
||||
return tpm_read_log_of(chip);
|
||||
}
|
||||
|
||||
static const struct file_operations tpm_binary_bios_measurements_ops = {
|
||||
.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)
|
||||
return 1;
|
||||
if (IS_ERR(p) && (PTR_ERR(p) != -ENODEV))
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
const char *name = dev_name(&chip->dev);
|
||||
unsigned int cnt;
|
||||
int rc = 0;
|
||||
|
||||
struct dentry **tpm_bios_log_setup(const char *name)
|
||||
{
|
||||
struct dentry **ret = NULL, *tpm_dir, *bin_file, *ascii_file;
|
||||
if (chip->flags & TPM_CHIP_FLAG_TPM2)
|
||||
return 0;
|
||||
|
||||
tpm_dir = securityfs_create_dir(name, NULL);
|
||||
if (is_bad(tpm_dir))
|
||||
goto out;
|
||||
rc = tpm_read_log(chip);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
bin_file =
|
||||
cnt = 0;
|
||||
chip->bios_dir[cnt] = securityfs_create_dir(name, NULL);
|
||||
/* 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++;
|
||||
|
||||
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",
|
||||
S_IRUSR | S_IRGRP, tpm_dir, NULL,
|
||||
&tpm_binary_bios_measurements_ops);
|
||||
if (is_bad(bin_file))
|
||||
goto out_tpm;
|
||||
0440, chip->bios_dir[0],
|
||||
(void *)&chip->bin_log_seqops,
|
||||
&tpm_bios_measurements_ops);
|
||||
if (IS_ERR(chip->bios_dir[cnt]))
|
||||
goto err;
|
||||
cnt++;
|
||||
|
||||
ascii_file =
|
||||
chip->ascii_log_seqops.chip = chip;
|
||||
chip->ascii_log_seqops.seqops = &tpm_ascii_b_measurements_seqops;
|
||||
|
||||
chip->bios_dir[cnt] =
|
||||
securityfs_create_file("ascii_bios_measurements",
|
||||
S_IRUSR | S_IRGRP, tpm_dir, NULL,
|
||||
&tpm_ascii_bios_measurements_ops);
|
||||
if (is_bad(ascii_file))
|
||||
goto out_bin;
|
||||
0440, chip->bios_dir[0],
|
||||
(void *)&chip->ascii_log_seqops,
|
||||
&tpm_bios_measurements_ops);
|
||||
if (IS_ERR(chip->bios_dir[cnt]))
|
||||
goto err;
|
||||
cnt++;
|
||||
|
||||
ret = kmalloc(3 * sizeof(struct dentry *), GFP_KERNEL);
|
||||
if (!ret)
|
||||
goto out_ascii;
|
||||
return 0;
|
||||
|
||||
ret[0] = ascii_file;
|
||||
ret[1] = bin_file;
|
||||
ret[2] = tpm_dir;
|
||||
|
||||
return ret;
|
||||
|
||||
out_ascii:
|
||||
securityfs_remove(ascii_file);
|
||||
out_bin:
|
||||
securityfs_remove(bin_file);
|
||||
out_tpm:
|
||||
securityfs_remove(tpm_dir);
|
||||
out:
|
||||
return NULL;
|
||||
err:
|
||||
rc = PTR_ERR(chip->bios_dir[cnt]);
|
||||
chip->bios_dir[cnt] = NULL;
|
||||
tpm_bios_log_teardown(chip);
|
||||
return rc;
|
||||
}
|
||||
|
||||
void tpm_bios_log_teardown(struct dentry **lst)
|
||||
void tpm_bios_log_teardown(struct tpm_chip *chip)
|
||||
{
|
||||
int i;
|
||||
struct inode *inode;
|
||||
|
||||
for (i = 0; i < 3; i++)
|
||||
securityfs_remove(lst[i]);
|
||||
/* securityfs_remove currently doesn't take care of handling sync
|
||||
* 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 {
|
||||
HOST_TABLE_OF_DEVICES,
|
||||
};
|
||||
|
||||
int read_log(struct tpm_bios_log *log);
|
||||
|
||||
#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
|
||||
defined(CONFIG_ACPI)
|
||||
extern struct dentry **tpm_bios_log_setup(const char *);
|
||||
extern void tpm_bios_log_teardown(struct dentry **);
|
||||
#if defined(CONFIG_ACPI)
|
||||
int tpm_read_log_acpi(struct tpm_chip *chip);
|
||||
#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
|
||||
|
||||
int tpm_bios_log_setup(struct tpm_chip *chip);
|
||||
void tpm_bios_log_teardown(struct tpm_chip *chip);
|
||||
|
||||
#endif
|
||||
|
@ -2,6 +2,7 @@
|
||||
* Copyright 2012 IBM Corporation
|
||||
*
|
||||
* Author: Ashley Lai <ashleydlai@gmail.com>
|
||||
* Nayna Jain <nayna@linux.vnet.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
@ -20,55 +21,38 @@
|
||||
#include "tpm.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;
|
||||
const u32 *sizep;
|
||||
const u64 *basep;
|
||||
struct tpm_bios_log *log;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
pr_err("%s: ERROR - Eventlog already initialized\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
np = of_find_node_by_name(NULL, "vtpm");
|
||||
if (!np) {
|
||||
pr_err("%s: ERROR - IBMVTPM not supported\n", __func__);
|
||||
log = &chip->log;
|
||||
if (chip->dev.parent && chip->dev.parent->of_node)
|
||||
np = chip->dev.parent->of_node;
|
||||
else
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sizep = of_get_property(np, "linux,sml-size", NULL);
|
||||
if (sizep == NULL) {
|
||||
pr_err("%s: ERROR - SML size not found\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
if (*sizep == 0) {
|
||||
pr_err("%s: ERROR - event log area empty\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
|
||||
basep = of_get_property(np, "linux,sml-base", NULL);
|
||||
if (basep == NULL) {
|
||||
pr_err("%s: ERROR - SML not found\n", __func__);
|
||||
goto cleanup_eio;
|
||||
if (sizep == NULL && basep == NULL)
|
||||
return -ENODEV;
|
||||
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);
|
||||
if (!log->bios_event_log) {
|
||||
pr_err("%s: ERROR - Not enough memory for BIOS measurements\n",
|
||||
__func__);
|
||||
of_node_put(np);
|
||||
if (!log->bios_event_log)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + *sizep;
|
||||
|
||||
memcpy(log->bios_event_log, __va(*basep), *sizep);
|
||||
of_node_put(np);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_eio:
|
||||
of_node_put(np);
|
||||
return -EIO;
|
||||
}
|
||||
|
@ -28,6 +28,8 @@
|
||||
#include <linux/wait.h>
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/freezer.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include "tpm.h"
|
||||
#include "tpm_tis_core.h"
|
||||
|
||||
@ -354,12 +356,21 @@ static int tpm_tis_plat_remove(struct platform_device *pdev)
|
||||
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 = {
|
||||
.probe = tpm_tis_plat_probe,
|
||||
.remove = tpm_tis_plat_remove,
|
||||
.driver = {
|
||||
.name = "tpm_tis",
|
||||
.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)
|
||||
struct tpm_tis_data *priv = dev_get_drvdata(&chip->dev);
|
||||
int size = 0, burstcnt, rc;
|
||||
|
||||
while (size < count &&
|
||||
wait_for_tpm_stat(chip,
|
||||
while (size < count) {
|
||||
rc = wait_for_tpm_stat(chip,
|
||||
TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
chip->timeout_c,
|
||||
&priv->read_queue, true) == 0) {
|
||||
burstcnt = min_t(int, get_burstcount(chip), count - size);
|
||||
&priv->read_queue, true);
|
||||
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),
|
||||
burstcnt, buf + size);
|
||||
@ -229,8 +236,11 @@ static int tpm_tis_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
goto out;
|
||||
}
|
||||
|
||||
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
|
||||
&priv->int_queue, false);
|
||||
if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
|
||||
&priv->int_queue, false) < 0) {
|
||||
size = -ETIME;
|
||||
goto out;
|
||||
}
|
||||
status = tpm_tis_status(chip);
|
||||
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
|
||||
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)
|
||||
}
|
||||
|
||||
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),
|
||||
burstcnt, buf + count);
|
||||
if (rc < 0)
|
||||
@ -279,8 +295,11 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
|
||||
count += burstcnt;
|
||||
|
||||
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
|
||||
&priv->int_queue, false);
|
||||
if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
|
||||
&priv->int_queue, false) < 0) {
|
||||
rc = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
status = tpm_tis_status(chip);
|
||||
if (!itpm && (status & TPM_STS_DATA_EXPECT) == 0) {
|
||||
rc = -EIO;
|
||||
@ -293,8 +312,11 @@ static int tpm_tis_send_data(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
if (rc < 0)
|
||||
goto out_err;
|
||||
|
||||
wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
|
||||
&priv->int_queue, false);
|
||||
if (wait_for_tpm_stat(chip, TPM_STS_VALID, chip->timeout_c,
|
||||
&priv->int_queue, false) < 0) {
|
||||
rc = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
status = tpm_tis_status(chip);
|
||||
if (!itpm && (status & TPM_STS_DATA_EXPECT) != 0) {
|
||||
rc = -EIO;
|
||||
@ -755,20 +777,20 @@ int tpm_tis_core_init(struct device *dev, struct tpm_tis_data *priv, int irq,
|
||||
if (intfcaps & TPM_INTF_DATA_AVAIL_INT)
|
||||
dev_dbg(dev, "\tData Avail Int Support\n");
|
||||
|
||||
/* Very early on 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)) {
|
||||
dev_err(dev, "Could not get TPM timeouts and durations\n");
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* INTERRUPT Setup */
|
||||
init_waitqueue_head(&priv->read_queue);
|
||||
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)) {
|
||||
dev_err(dev, "Could not get TPM timeouts and durations\n");
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
if (irq) {
|
||||
tpm_tis_probe_irq_single(chip, intmask, IRQF_SHARED,
|
||||
irq);
|
||||
|
@ -1,5 +1,6 @@
|
||||
/*
|
||||
* Copyright (C) 2015, 2016 IBM Corporation
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* Author: Stefan Berger <stefanb@us.ibm.com>
|
||||
*
|
||||
@ -41,6 +42,7 @@ struct proxy_dev {
|
||||
long state; /* internal state */
|
||||
#define STATE_OPENED_FLAG BIT(0)
|
||||
#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 resp_len; /* length of queued TPM response */
|
||||
@ -369,12 +371,9 @@ static void vtpm_proxy_work(struct work_struct *work)
|
||||
|
||||
rc = tpm_chip_register(proxy_dev->chip);
|
||||
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,7 +514,8 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
|
||||
*/
|
||||
vtpm_proxy_fops_undo_open(proxy_dev);
|
||||
|
||||
tpm_chip_unregister(proxy_dev->chip);
|
||||
if (proxy_dev->state & STATE_REGISTERED_FLAG)
|
||||
tpm_chip_unregister(proxy_dev->chip);
|
||||
|
||||
vtpm_proxy_delete_proxy_dev(proxy_dev);
|
||||
}
|
||||
@ -524,6 +524,50 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
|
||||
* Code related to the control device /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
|
||||
*
|
||||
* Creates an anonymous file that is used by the process acting as a TPM to
|
||||
* 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_ioc_new_dev(struct file *file, unsigned int ioctl,
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
struct vtpm_proxy_new_dev __user *vtpm_new_dev_p;
|
||||
struct vtpm_proxy_new_dev vtpm_new_dev;
|
||||
struct file *vtpm_file;
|
||||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
vtpm_new_dev_p = argp;
|
||||
|
||||
if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
|
||||
sizeof(vtpm_new_dev)))
|
||||
return -EFAULT;
|
||||
|
||||
vtpm_file = vtpm_proxy_create_device(&vtpm_new_dev);
|
||||
if (IS_ERR(vtpm_file))
|
||||
return PTR_ERR(vtpm_file);
|
||||
|
||||
if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
|
||||
sizeof(vtpm_new_dev))) {
|
||||
put_unused_fd(vtpm_new_dev.fd);
|
||||
fput(vtpm_file);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fd_install(vtpm_new_dev.fd, vtpm_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* vtpmx_fops_ioctl: ioctl on /dev/vtpmx
|
||||
*
|
||||
@ -531,34 +575,11 @@ static void vtpm_proxy_delete_device(struct proxy_dev *proxy_dev)
|
||||
* Returns 0 on success, a negative error code otherwise.
|
||||
*/
|
||||
static long vtpmx_fops_ioctl(struct file *f, unsigned int ioctl,
|
||||
unsigned long arg)
|
||||
unsigned long arg)
|
||||
{
|
||||
void __user *argp = (void __user *)arg;
|
||||
struct vtpm_proxy_new_dev __user *vtpm_new_dev_p;
|
||||
struct vtpm_proxy_new_dev vtpm_new_dev;
|
||||
struct file *file;
|
||||
|
||||
switch (ioctl) {
|
||||
case VTPM_PROXY_IOC_NEW_DEV:
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
vtpm_new_dev_p = argp;
|
||||
if (copy_from_user(&vtpm_new_dev, vtpm_new_dev_p,
|
||||
sizeof(vtpm_new_dev)))
|
||||
return -EFAULT;
|
||||
file = vtpm_proxy_create_device(&vtpm_new_dev);
|
||||
if (IS_ERR(file))
|
||||
return PTR_ERR(file);
|
||||
if (copy_to_user(vtpm_new_dev_p, &vtpm_new_dev,
|
||||
sizeof(vtpm_new_dev))) {
|
||||
put_unused_fd(vtpm_new_dev.fd);
|
||||
fput(file);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
fd_install(vtpm_new_dev.fd, file);
|
||||
return 0;
|
||||
|
||||
return vtpmx_ioc_new_dev(f, ioctl, arg);
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
@ -307,7 +307,6 @@ static int tpmfront_probe(struct xenbus_device *dev,
|
||||
rv = setup_ring(dev, priv);
|
||||
if (rv) {
|
||||
chip = dev_get_drvdata(&dev->dev);
|
||||
tpm_chip_unregister(chip);
|
||||
ring_free(priv);
|
||||
return rv;
|
||||
}
|
||||
|
@ -1667,7 +1667,8 @@ const struct inode_operations proc_pid_link_inode_operations = {
|
||||
|
||||
/* 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 proc_inode *ei;
|
||||
@ -1681,6 +1682,7 @@ struct inode *proc_pid_make_inode(struct super_block * sb, struct task_struct *t
|
||||
|
||||
/* Common stuff */
|
||||
ei = PROC_I(inode);
|
||||
inode->i_mode = mode;
|
||||
inode->i_ino = get_next_ino();
|
||||
inode->i_mtime = inode->i_atime = inode->i_ctime = current_time(inode);
|
||||
inode->i_op = &proc_def_inode_operations;
|
||||
@ -2007,7 +2009,9 @@ proc_map_files_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
struct proc_inode *ei;
|
||||
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)
|
||||
return -ENOENT;
|
||||
|
||||
@ -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_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_add(dentry, inode);
|
||||
@ -2375,12 +2373,11 @@ static int proc_pident_instantiate(struct inode *dir,
|
||||
struct inode *inode;
|
||||
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)
|
||||
goto out;
|
||||
|
||||
ei = PROC_I(inode);
|
||||
inode->i_mode = p->mode;
|
||||
if (S_ISDIR(inode->i_mode))
|
||||
set_nlink(inode, 2); /* Use getattr to fix if necessary */
|
||||
if (p->iop)
|
||||
@ -3062,11 +3059,10 @@ static int proc_pid_instantiate(struct inode *dir,
|
||||
{
|
||||
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)
|
||||
goto out;
|
||||
|
||||
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
|
||||
inode->i_op = &proc_tgid_base_inode_operations;
|
||||
inode->i_fop = &proc_tgid_base_operations;
|
||||
inode->i_flags|=S_IMMUTABLE;
|
||||
@ -3354,11 +3350,10 @@ static int proc_task_instantiate(struct inode *dir,
|
||||
struct dentry *dentry, struct task_struct *task, const void *ptr)
|
||||
{
|
||||
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)
|
||||
goto out;
|
||||
inode->i_mode = S_IFDIR|S_IRUGO|S_IXUGO;
|
||||
inode->i_op = &proc_tid_base_inode_operations;
|
||||
inode->i_fop = &proc_tid_base_operations;
|
||||
inode->i_flags|=S_IMMUTABLE;
|
||||
|
@ -183,14 +183,13 @@ proc_fd_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
struct proc_inode *ei;
|
||||
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)
|
||||
goto out;
|
||||
|
||||
ei = PROC_I(inode);
|
||||
ei->fd = fd;
|
||||
|
||||
inode->i_mode = S_IFLNK;
|
||||
inode->i_op = &proc_pid_link_inode_operations;
|
||||
inode->i_size = 64;
|
||||
|
||||
@ -322,14 +321,13 @@ proc_fdinfo_instantiate(struct inode *dir, struct dentry *dentry,
|
||||
struct proc_inode *ei;
|
||||
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)
|
||||
goto out;
|
||||
|
||||
ei = PROC_I(inode);
|
||||
ei->fd = fd;
|
||||
|
||||
inode->i_mode = S_IFREG | S_IRUSR;
|
||||
inode->i_fop = &proc_fdinfo_file_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 *,
|
||||
extern const struct dentry_operations pid_dentry_operations;
|
||||
extern int pid_getattr(struct vfsmount *, struct dentry *, struct kstat *);
|
||||
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_delete_dentry(const struct dentry *);
|
||||
extern int proc_pid_readdir(struct file *, struct dir_context *);
|
||||
|
@ -92,12 +92,11 @@ static int proc_ns_instantiate(struct inode *dir,
|
||||
struct inode *inode;
|
||||
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)
|
||||
goto out;
|
||||
|
||||
ei = PROC_I(inode);
|
||||
inode->i_mode = S_IFLNK|S_IRWXUGO;
|
||||
inode->i_op = &proc_ns_link_inode_operations;
|
||||
ei->ns_ops = ns_ops;
|
||||
|
||||
|
@ -1,6 +1,7 @@
|
||||
/*
|
||||
* Definitions for the VTPM proxy driver
|
||||
* Copyright (c) 2015, 2016, IBM Corporation
|
||||
* Copyright (C) 2016 Intel Corporation
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
@ -18,8 +19,23 @@
|
||||
#include <linux/types.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 {
|
||||
__u32 flags; /* input */
|
||||
__u32 tpm_num; /* output */
|
||||
@ -28,9 +44,6 @@ struct vtpm_proxy_new_dev {
|
||||
__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 */
|
||||
|
@ -41,8 +41,7 @@
|
||||
* outside of a lifetime-guarded section. In general, this
|
||||
* is only needed for handling filters shared across tasks.
|
||||
* @prev: points to a previously installed, or inherited, filter
|
||||
* @len: the number of instructions in the program
|
||||
* @insnsi: the BPF program instructions to evaluate
|
||||
* @prog: the BPF program to evaluate
|
||||
*
|
||||
* 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
|
||||
@ -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
|
||||
* @syscall: number of the current system call
|
||||
* seccomp_run_filters - evaluates all seccomp filters against @sd
|
||||
* @sd: optional seccomp data to be passed to filters
|
||||
*
|
||||
* Returns valid seccomp BPF response codes.
|
||||
*/
|
||||
|
@ -36,13 +36,13 @@ HOSTLOADLIBES_bpf-direct += $(MFLAG)
|
||||
HOSTLOADLIBES_bpf-fancy += $(MFLAG)
|
||||
HOSTLOADLIBES_dropper += $(MFLAG)
|
||||
endif
|
||||
always := $(hostprogs-y)
|
||||
always := $(hostprogs-m)
|
||||
else
|
||||
# MIPS system calls are defined based on the -mabi that is passed
|
||||
# to the toolchain which may or may not be a valid option
|
||||
# for the host toolchain. So disable tests if target architecture
|
||||
# is MIPS but the host isn't.
|
||||
ifndef CONFIG_MIPS
|
||||
always := $(hostprogs-y)
|
||||
always := $(hostprogs-m)
|
||||
endif
|
||||
endif
|
||||
|
@ -18,41 +18,41 @@
|
||||
int bpf_resolve_jumps(struct bpf_labels *labels,
|
||||
struct sock_filter *filter, size_t count)
|
||||
{
|
||||
struct sock_filter *begin = filter;
|
||||
__u8 insn = count - 1;
|
||||
size_t i;
|
||||
|
||||
if (count < 1)
|
||||
if (count < 1 || count > BPF_MAXINSNS)
|
||||
return -1;
|
||||
/*
|
||||
* Walk it once, backwards, to build the label table and do fixups.
|
||||
* Since backward jumps are disallowed by BPF, this is easy.
|
||||
*/
|
||||
filter += insn;
|
||||
for (; filter >= begin; --insn, --filter) {
|
||||
if (filter->code != (BPF_JMP+BPF_JA))
|
||||
for (i = 0; i < count; ++i) {
|
||||
size_t offset = count - i - 1;
|
||||
struct sock_filter *instr = &filter[offset];
|
||||
if (instr->code != (BPF_JMP+BPF_JA))
|
||||
continue;
|
||||
switch ((filter->jt<<8)|filter->jf) {
|
||||
switch ((instr->jt<<8)|instr->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",
|
||||
labels->labels[filter->k].label);
|
||||
labels->labels[instr->k].label);
|
||||
return 1;
|
||||
}
|
||||
filter->k = labels->labels[filter->k].location -
|
||||
(insn + 1);
|
||||
filter->jt = 0;
|
||||
filter->jf = 0;
|
||||
instr->k = labels->labels[instr->k].location -
|
||||
(offset + 1);
|
||||
instr->jt = 0;
|
||||
instr->jf = 0;
|
||||
continue;
|
||||
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",
|
||||
labels->labels[filter->k].label);
|
||||
labels->labels[instr->k].label);
|
||||
return 1;
|
||||
}
|
||||
labels->labels[filter->k].location = insn;
|
||||
filter->k = 0; /* fall through */
|
||||
filter->jt = 0;
|
||||
filter->jf = 0;
|
||||
labels->labels[instr->k].location = offset;
|
||||
instr->k = 0; /* fall through */
|
||||
instr->jt = 0;
|
||||
instr->jf = 0;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
* When run, returns the specified errno for the specified
|
||||
* system call number against the given architecture.
|
||||
*
|
||||
* Run this one as root as PR_SET_NO_NEW_PRIVS is not called.
|
||||
*/
|
||||
|
||||
#include <errno.h>
|
||||
@ -42,8 +41,12 @@ static int install_filter(int nr, int arch, int error)
|
||||
.len = (unsigned short)(sizeof(filter)/sizeof(filter[0])),
|
||||
.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)) {
|
||||
perror("prctl");
|
||||
perror("prctl(PR_SET_SECCOMP)");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
|
@ -51,7 +51,7 @@ static bool init_keyring __initdata;
|
||||
int integrity_digsig_verify(const unsigned int id, const char *sig, int siglen,
|
||||
const char *digest, int digestlen)
|
||||
{
|
||||
if (id >= INTEGRITY_KEYRING_MAX)
|
||||
if (id >= INTEGRITY_KEYRING_MAX || siglen < 2)
|
||||
return -EINVAL;
|
||||
|
||||
if (!keyring[id]) {
|
||||
|
@ -145,6 +145,10 @@ static enum integrity_status evm_verify_hmac(struct dentry *dentry,
|
||||
/* check value type */
|
||||
switch (xattr_data->type) {
|
||||
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,
|
||||
xattr_value_len, calc.digest);
|
||||
if (rc)
|
||||
|
@ -130,6 +130,7 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
|
||||
int xattr_len)
|
||||
{
|
||||
struct signature_v2_hdr *sig;
|
||||
enum hash_algo ret;
|
||||
|
||||
if (!xattr_value || xattr_len < 2)
|
||||
/* return default hash algo */
|
||||
@ -143,7 +144,9 @@ enum hash_algo ima_get_hash_algo(struct evm_ima_xattr_data *xattr_value,
|
||||
return sig->hash_algo;
|
||||
break;
|
||||
case IMA_XATTR_DIGEST_NG:
|
||||
return xattr_value->digest[0];
|
||||
ret = xattr_value->digest[0];
|
||||
if (ret < HASH_ALGO__LAST)
|
||||
return ret;
|
||||
break;
|
||||
case IMA_XATTR_DIGEST:
|
||||
/* this is for backward compatibility */
|
||||
@ -384,14 +387,10 @@ int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
|
||||
xattr_value_len);
|
||||
if (result == 1) {
|
||||
bool digsig;
|
||||
|
||||
if (!xattr_value_len || (xvalue->type >= IMA_XATTR_LAST))
|
||||
return -EINVAL;
|
||||
digsig = (xvalue->type == EVM_IMA_XATTR_DIGSIG);
|
||||
if (!digsig && (ima_appraise & IMA_APPRAISE_ENFORCE))
|
||||
return -EPERM;
|
||||
ima_reset_appraise_flags(d_backing_inode(dentry), digsig);
|
||||
ima_reset_appraise_flags(d_backing_inode(dentry),
|
||||
(xvalue->type == EVM_IMA_XATTR_DIGSIG) ? 1 : 0);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
|
@ -401,7 +401,7 @@ static int ima_release_policy(struct inode *inode, struct file *file)
|
||||
const char *cause = valid_policy ? "completed" : "failed";
|
||||
|
||||
if ((file->f_flags & O_ACCMODE) == O_RDONLY)
|
||||
return 0;
|
||||
return seq_release(inode, file);
|
||||
|
||||
if (valid_policy && ima_check_policy() < 0) {
|
||||
cause = "failed";
|
||||
|
@ -115,7 +115,8 @@ int __init ima_init(void)
|
||||
ima_used_chip = 1;
|
||||
|
||||
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);
|
||||
if (rc)
|
||||
|
@ -231,12 +231,13 @@ static int inode_alloc_security(struct inode *inode)
|
||||
if (!isec)
|
||||
return -ENOMEM;
|
||||
|
||||
mutex_init(&isec->lock);
|
||||
spin_lock_init(&isec->lock);
|
||||
INIT_LIST_HEAD(&isec->list);
|
||||
isec->inode = inode;
|
||||
isec->sid = SECINITSID_UNLABELED;
|
||||
isec->sclass = SECCLASS_FILE;
|
||||
isec->task_sid = sid;
|
||||
isec->initialized = LABEL_INVALID;
|
||||
inode->i_security = isec;
|
||||
|
||||
return 0;
|
||||
@ -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
|
||||
* @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;
|
||||
* when no dentry is available, set it to NULL instead.
|
||||
*/
|
||||
@ -1100,11 +1101,12 @@ static int selinux_parse_opts_str(char *options,
|
||||
}
|
||||
|
||||
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)
|
||||
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) {
|
||||
kfree(opts->mnt_opts);
|
||||
goto out_err;
|
||||
@ -1380,7 +1382,8 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
|
||||
{
|
||||
struct superblock_security_struct *sbsec = NULL;
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
u32 sid;
|
||||
u32 task_sid, sid = 0;
|
||||
u16 sclass;
|
||||
struct dentry *dentry;
|
||||
#define INITCONTEXTLEN 255
|
||||
char *context = NULL;
|
||||
@ -1388,12 +1391,15 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
|
||||
int rc = 0;
|
||||
|
||||
if (isec->initialized == LABEL_INITIALIZED)
|
||||
goto out;
|
||||
return 0;
|
||||
|
||||
mutex_lock(&isec->lock);
|
||||
spin_lock(&isec->lock);
|
||||
if (isec->initialized == LABEL_INITIALIZED)
|
||||
goto out_unlock;
|
||||
|
||||
if (isec->sclass == SECCLASS_FILE)
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
|
||||
sbsec = inode->i_sb->s_security;
|
||||
if (!(sbsec->flags & SE_SBINITIALIZED)) {
|
||||
/* Defer initialization until selinux_complete_init,
|
||||
@ -1406,12 +1412,18 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
|
||||
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) {
|
||||
case SECURITY_FS_USE_NATIVE:
|
||||
break;
|
||||
case SECURITY_FS_USE_XATTR:
|
||||
if (!(inode->i_opflags & IOP_XATTR)) {
|
||||
isec->sid = sbsec->def_sid;
|
||||
sid = sbsec->def_sid;
|
||||
break;
|
||||
}
|
||||
/* 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
|
||||
* inode_doinit with a dentry, before these inodes could
|
||||
* be used again by userspace.
|
||||
*/
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
|
||||
len = INITCONTEXTLEN;
|
||||
@ -1441,7 +1453,7 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
|
||||
if (!context) {
|
||||
rc = -ENOMEM;
|
||||
dput(dentry);
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
context[len] = '\0';
|
||||
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
|
||||
rc = __vfs_getxattr(dentry, inode, XATTR_NAME_SELINUX, NULL, 0);
|
||||
if (rc < 0) {
|
||||
dput(dentry);
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
len = rc;
|
||||
context = kmalloc(len+1, GFP_NOFS);
|
||||
if (!context) {
|
||||
rc = -ENOMEM;
|
||||
dput(dentry);
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
context[len] = '\0';
|
||||
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
|
||||
"%d for dev=%s ino=%ld\n", __func__,
|
||||
-rc, inode->i_sb->s_id, inode->i_ino);
|
||||
kfree(context);
|
||||
goto out_unlock;
|
||||
goto out;
|
||||
}
|
||||
/* Map ENODATA to the default file SID */
|
||||
sid = sbsec->def_sid;
|
||||
@ -1501,29 +1513,25 @@ static int inode_doinit_with_dentry(struct inode *inode, struct dentry *opt_dent
|
||||
}
|
||||
}
|
||||
kfree(context);
|
||||
isec->sid = sid;
|
||||
break;
|
||||
case SECURITY_FS_USE_TASK:
|
||||
isec->sid = isec->task_sid;
|
||||
sid = task_sid;
|
||||
break;
|
||||
case SECURITY_FS_USE_TRANS:
|
||||
/* Default to the fs SID. */
|
||||
isec->sid = sbsec->sid;
|
||||
sid = sbsec->sid;
|
||||
|
||||
/* Try to obtain a transition SID. */
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
rc = security_transition_sid(isec->task_sid, sbsec->sid,
|
||||
isec->sclass, NULL, &sid);
|
||||
rc = security_transition_sid(task_sid, sid, sclass, NULL, &sid);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
isec->sid = sid;
|
||||
goto out;
|
||||
break;
|
||||
case SECURITY_FS_USE_MNTPOINT:
|
||||
isec->sid = sbsec->mntpoint_sid;
|
||||
sid = sbsec->mntpoint_sid;
|
||||
break;
|
||||
default:
|
||||
/* Default to the fs superblock SID. */
|
||||
isec->sid = sbsec->sid;
|
||||
sid = sbsec->sid;
|
||||
|
||||
if ((sbsec->flags & SE_SBGENFS) && !S_ISLNK(inode->i_mode)) {
|
||||
/* 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
|
||||
* could be used again by userspace.
|
||||
*/
|
||||
if (!dentry)
|
||||
goto out_unlock;
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
rc = selinux_genfs_get_sid(dentry, isec->sclass,
|
||||
goto out;
|
||||
rc = selinux_genfs_get_sid(dentry, sclass,
|
||||
sbsec->flags, &sid);
|
||||
dput(dentry);
|
||||
if (rc)
|
||||
goto out_unlock;
|
||||
isec->sid = sid;
|
||||
goto out;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
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->sid = sid;
|
||||
}
|
||||
|
||||
out_unlock:
|
||||
mutex_unlock(&isec->lock);
|
||||
out:
|
||||
if (isec->sclass == SECCLASS_FILE)
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
spin_unlock(&isec->lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@ -3198,9 +3211,11 @@ static void selinux_inode_post_setxattr(struct dentry *dentry, const char *name,
|
||||
}
|
||||
|
||||
isec = backing_inode_security(dentry);
|
||||
spin_lock(&isec->lock);
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
isec->sid = newsid;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
spin_unlock(&isec->lock);
|
||||
|
||||
return;
|
||||
}
|
||||
@ -3293,9 +3308,11 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name,
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
spin_lock(&isec->lock);
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
isec->sid = newsid;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
spin_unlock(&isec->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -3956,8 +3973,11 @@ static void selinux_task_to_inode(struct task_struct *p,
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
u32 sid = task_sid(p);
|
||||
|
||||
spin_lock(&isec->lock);
|
||||
isec->sclass = inode_mode_to_security_class(inode->i_mode);
|
||||
isec->sid = sid;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
spin_unlock(&isec->lock);
|
||||
}
|
||||
|
||||
/* Returns error only if unable to parse addresses */
|
||||
@ -4276,24 +4296,24 @@ static int selinux_socket_post_create(struct socket *sock, int family,
|
||||
const struct task_security_struct *tsec = current_security();
|
||||
struct inode_security_struct *isec = inode_security_novalidate(SOCK_INODE(sock));
|
||||
struct sk_security_struct *sksec;
|
||||
u16 sclass = socket_type_to_security_class(family, type, protocol);
|
||||
u32 sid = SECINITSID_KERNEL;
|
||||
int err = 0;
|
||||
|
||||
isec->sclass = socket_type_to_security_class(family, type, protocol);
|
||||
|
||||
if (kern)
|
||||
isec->sid = SECINITSID_KERNEL;
|
||||
else {
|
||||
err = socket_sockcreate_sid(tsec, isec->sclass, &(isec->sid));
|
||||
if (!kern) {
|
||||
err = socket_sockcreate_sid(tsec, sclass, &sid);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
|
||||
isec->sclass = sclass;
|
||||
isec->sid = sid;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
|
||||
if (sock->sk) {
|
||||
sksec = sock->sk->sk_security;
|
||||
sksec->sid = isec->sid;
|
||||
sksec->sclass = isec->sclass;
|
||||
sksec->sclass = sclass;
|
||||
sksec->sid = sid;
|
||||
err = selinux_netlbl_socket_post_create(sock->sk, family);
|
||||
}
|
||||
|
||||
@ -4469,16 +4489,22 @@ static int selinux_socket_accept(struct socket *sock, struct socket *newsock)
|
||||
int err;
|
||||
struct inode_security_struct *isec;
|
||||
struct inode_security_struct *newisec;
|
||||
u16 sclass;
|
||||
u32 sid;
|
||||
|
||||
err = sock_has_perm(current, sock->sk, SOCKET__ACCEPT);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
newisec = inode_security_novalidate(SOCK_INODE(newsock));
|
||||
|
||||
isec = inode_security_novalidate(SOCK_INODE(sock));
|
||||
newisec->sclass = isec->sclass;
|
||||
newisec->sid = isec->sid;
|
||||
spin_lock(&isec->lock);
|
||||
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;
|
||||
|
||||
return 0;
|
||||
@ -5981,9 +6007,9 @@ static void selinux_inode_invalidate_secctx(struct inode *inode)
|
||||
{
|
||||
struct inode_security_struct *isec = inode->i_security;
|
||||
|
||||
mutex_lock(&isec->lock);
|
||||
spin_lock(&isec->lock);
|
||||
isec->initialized = LABEL_INVALID;
|
||||
mutex_unlock(&isec->lock);
|
||||
spin_unlock(&isec->lock);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -24,6 +24,10 @@
|
||||
#define COMMON_CAP2_PERMS "mac_override", "mac_admin", "syslog", \
|
||||
"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",
|
||||
* and doesn't contain more than one substr of "socket".
|
||||
|
@ -39,7 +39,8 @@ struct task_security_struct {
|
||||
|
||||
enum label_initialized {
|
||||
LABEL_INVALID, /* invalid or not initialized */
|
||||
LABEL_INITIALIZED /* initialized */
|
||||
LABEL_INITIALIZED, /* initialized */
|
||||
LABEL_PENDING
|
||||
};
|
||||
|
||||
struct inode_security_struct {
|
||||
@ -52,7 +53,7 @@ struct inode_security_struct {
|
||||
u32 sid; /* SID of this object */
|
||||
u16 sclass; /* security class of this object */
|
||||
unsigned char initialized; /* initialization flag */
|
||||
struct mutex lock;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
struct file_security_struct {
|
||||
|
@ -163,6 +163,8 @@ static ssize_t sel_write_enforce(struct file *file, const char __user *buf,
|
||||
if (sscanf(page, "%d", &new_value) != 1)
|
||||
goto out;
|
||||
|
||||
new_value = !!new_value;
|
||||
|
||||
if (new_value != selinux_enforcing) {
|
||||
length = task_has_security(current, SECURITY__SETENFORCE);
|
||||
if (length)
|
||||
@ -1301,7 +1303,7 @@ static int sel_make_bools(void)
|
||||
goto out;
|
||||
|
||||
isec->sid = sid;
|
||||
isec->initialized = 1;
|
||||
isec->initialized = LABEL_INITIALIZED;
|
||||
inode->i_fop = &sel_bool_ops;
|
||||
inode->i_ino = i|SEL_BOOL_INO_OFFSET;
|
||||
d_add(dentry, inode);
|
||||
@ -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->sid = SECINITSID_DEVNULL;
|
||||
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));
|
||||
d_add(dentry, inode);
|
||||
|
@ -336,7 +336,6 @@ extern int smack_ptrace_rule;
|
||||
extern struct smack_known smack_known_floor;
|
||||
extern struct smack_known smack_known_hat;
|
||||
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_web;
|
||||
|
||||
|
@ -36,11 +36,6 @@ struct smack_known smack_known_floor = {
|
||||
.smk_secid = 5,
|
||||
};
|
||||
|
||||
struct smack_known smack_known_invalid = {
|
||||
.smk_known = "",
|
||||
.smk_secid = 6,
|
||||
};
|
||||
|
||||
struct smack_known smack_known_web = {
|
||||
.smk_known = "@",
|
||||
.smk_secid = 7,
|
||||
@ -615,7 +610,7 @@ struct smack_known *smack_from_secid(const u32 secid)
|
||||
* of a secid that is not on the list.
|
||||
*/
|
||||
rcu_read_unlock();
|
||||
return &smack_known_invalid;
|
||||
return &smack_known_huh;
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -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)
|
||||
goto out_err;
|
||||
|
||||
opts->mnt_opts_flags = kcalloc(NUM_SMK_MNT_OPTS, sizeof(int),
|
||||
GFP_ATOMIC);
|
||||
GFP_KERNEL);
|
||||
if (!opts->mnt_opts_flags) {
|
||||
kfree(opts->mnt_opts);
|
||||
goto out_err;
|
||||
@ -769,6 +769,31 @@ static int smack_set_mnt_opts(struct super_block *sb,
|
||||
if (sp->smk_flags & SMK_SB_INITIALIZED)
|
||||
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;
|
||||
|
||||
for (i = 0; i < num_opts; i++) {
|
||||
@ -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.
|
||||
*/
|
||||
@ -1384,20 +1384,14 @@ static void smack_inode_post_setxattr(struct dentry *dentry, const char *name,
|
||||
skp = smk_import_entry(value, size);
|
||||
if (!IS_ERR(skp))
|
||||
isp->smk_inode = skp;
|
||||
else
|
||||
isp->smk_inode = &smack_known_invalid;
|
||||
} else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0) {
|
||||
skp = smk_import_entry(value, size);
|
||||
if (!IS_ERR(skp))
|
||||
isp->smk_task = skp;
|
||||
else
|
||||
isp->smk_task = &smack_known_invalid;
|
||||
} else if (strcmp(name, XATTR_NAME_SMACKMMAP) == 0) {
|
||||
skp = smk_import_entry(value, size);
|
||||
if (!IS_ERR(skp))
|
||||
isp->smk_mmap = skp;
|
||||
else
|
||||
isp->smk_mmap = &smack_known_invalid;
|
||||
}
|
||||
|
||||
return;
|
||||
@ -2023,6 +2017,8 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
|
||||
if (new_tsp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
new->security = new_tsp;
|
||||
|
||||
rc = smk_copy_rules(&new_tsp->smk_rules, &old_tsp->smk_rules, gfp);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
@ -2032,7 +2028,6 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old,
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
|
||||
new->security = new_tsp;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -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)
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
@ -2337,8 +2328,16 @@ static int smack_sk_alloc_security(struct sock *sk, int family, gfp_t gfp_flags)
|
||||
if (ssp == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
ssp->smk_in = skp;
|
||||
ssp->smk_out = skp;
|
||||
/*
|
||||
* 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_out = skp;
|
||||
}
|
||||
ssp->smk_packet = NULL;
|
||||
|
||||
sk->sk_security = ssp;
|
||||
@ -2434,18 +2433,18 @@ static struct smack_known *smack_ipv6host_label(struct sockaddr_in6 *sip)
|
||||
return NULL;
|
||||
|
||||
list_for_each_entry_rcu(snp, &smk_net6addr_list, list) {
|
||||
/*
|
||||
* If the label is NULL the entry has
|
||||
* been renounced. Ignore it.
|
||||
*/
|
||||
if (snp->smk_label == NULL)
|
||||
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 the label is NULL the entry has
|
||||
* been renounced. Ignore it.
|
||||
*/
|
||||
if (snp->smk_label == NULL)
|
||||
continue;
|
||||
if ((sap->s6_addr16[i] & snp->smk_mask.s6_addr16[i]) !=
|
||||
snp->smk_host.s6_addr16[i]) {
|
||||
found = 0;
|
||||
@ -3661,10 +3660,11 @@ static int smack_setprocattr(struct task_struct *p, char *name,
|
||||
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)
|
||||
return -EPERM;
|
||||
if (skp == &smack_known_web || skp == &smack_known_star)
|
||||
return -EINVAL;
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN)) {
|
||||
rc = -EPERM;
|
||||
@ -3884,21 +3884,11 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap,
|
||||
return &smack_known_web;
|
||||
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.
|
||||
*/
|
||||
skp = 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;
|
||||
}
|
||||
return smack_from_secid(sap->attr.secid);
|
||||
/*
|
||||
* Without guidance regarding the smack value
|
||||
* for the packet fall back on the network
|
||||
@ -4761,7 +4751,6 @@ static __init void init_smack_known_list(void)
|
||||
mutex_init(&smack_known_hat.smk_rules_lock);
|
||||
mutex_init(&smack_known_floor.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);
|
||||
/*
|
||||
* Initialize rule lists
|
||||
@ -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_star.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);
|
||||
/*
|
||||
* Create the known labels list
|
||||
@ -4779,7 +4767,6 @@ static __init void init_smack_known_list(void)
|
||||
smk_insert_entry(&smack_known_hat);
|
||||
smk_insert_entry(&smack_known_star);
|
||||
smk_insert_entry(&smack_known_floor);
|
||||
smk_insert_entry(&smack_known_invalid);
|
||||
smk_insert_entry(&smack_known_web);
|
||||
}
|
||||
|
||||
|
@ -2996,9 +2996,6 @@ static int __init init_smk_fs(void)
|
||||
if (err == 0 && rc < 0)
|
||||
err = rc;
|
||||
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)
|
||||
err = rc;
|
||||
rc = smk_preset_netlabel(&smack_known_star);
|
||||
|
@ -309,7 +309,7 @@ static int task_is_descendant(struct task_struct *parent,
|
||||
* @tracer: the task_struct of the process attempting ptrace
|
||||
* @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,
|
||||
struct task_struct *tracee)
|
||||
@ -320,6 +320,18 @@ static int ptracer_exception_found(struct task_struct *tracer,
|
||||
bool found = false;
|
||||
|
||||
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))
|
||||
tracee = rcu_dereference(tracee->group_leader);
|
||||
list_for_each_entry_rcu(relation, &ptracer_relations, node) {
|
||||
@ -334,6 +346,8 @@ static int ptracer_exception_found(struct task_struct *tracer,
|
||||
|
||||
if (found && (parent == NULL || task_is_descendant(parent, tracer)))
|
||||
rc = 1;
|
||||
|
||||
unlock:
|
||||
rcu_read_unlock();
|
||||
|
||||
return rc;
|
||||
|
Loading…
Reference in New Issue
Block a user