Merge branch 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security subsystem updates from James Morris: "Highlights: - Integrity: add local fs integrity verification to detect offline attacks - Integrity: add digital signature verification - Simple stacking of Yama with other LSMs (per LSS discussions) - IBM vTPM support on ppc64 - Add new driver for Infineon I2C TIS TPM - Smack: add rule revocation for subject labels" Fixed conflicts with the user namespace support in kernel/auditsc.c and security/integrity/ima/ima_policy.c. * 'next' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (39 commits) Documentation: Update git repository URL for Smack userland tools ima: change flags container data type Smack: setprocattr memory leak fix Smack: implement revoking all rules for a subject label Smack: remove task_wait() hook. ima: audit log hashes ima: generic IMA action flag handling ima: rename ima_must_appraise_or_measure audit: export audit_log_task_info tpm: fix tpm_acpi sparse warning on different address spaces samples/seccomp: fix 31 bit build on s390 ima: digital signature verification support ima: add support for different security.ima data types ima: add ima_inode_setxattr/removexattr function and calls ima: add inode_post_setattr call ima: replace iint spinblock with rwlock/read_lock ima: allocating iint improvements ima: add appraise action keywords and default rules ima: integrity appraisal extension vfs: move ima_file_free before releasing the file ...
This commit is contained in:
Коммит
88265322c1
|
@ -12,11 +12,14 @@ Description:
|
|||
then closing the file. The new policy takes effect after
|
||||
the file ima/policy is closed.
|
||||
|
||||
IMA appraisal, if configured, uses these file measurements
|
||||
for local measurement appraisal.
|
||||
|
||||
rule format: action [condition ...]
|
||||
|
||||
action: measure | dont_measure
|
||||
action: measure | dont_measure | appraise | dont_appraise | audit
|
||||
condition:= base | lsm
|
||||
base: [[func=] [mask=] [fsmagic=] [uid=]]
|
||||
base: [[func=] [mask=] [fsmagic=] [uid=] [fowner]]
|
||||
lsm: [[subj_user=] [subj_role=] [subj_type=]
|
||||
[obj_user=] [obj_role=] [obj_type=]]
|
||||
|
||||
|
@ -24,36 +27,50 @@ Description:
|
|||
mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC]
|
||||
fsmagic:= hex value
|
||||
uid:= decimal value
|
||||
fowner:=decimal value
|
||||
lsm: are LSM specific
|
||||
|
||||
default policy:
|
||||
# PROC_SUPER_MAGIC
|
||||
dont_measure fsmagic=0x9fa0
|
||||
dont_appraise fsmagic=0x9fa0
|
||||
# SYSFS_MAGIC
|
||||
dont_measure fsmagic=0x62656572
|
||||
dont_appraise fsmagic=0x62656572
|
||||
# DEBUGFS_MAGIC
|
||||
dont_measure fsmagic=0x64626720
|
||||
dont_appraise fsmagic=0x64626720
|
||||
# TMPFS_MAGIC
|
||||
dont_measure fsmagic=0x01021994
|
||||
dont_appraise fsmagic=0x01021994
|
||||
# RAMFS_MAGIC
|
||||
dont_measure fsmagic=0x858458f6
|
||||
dont_appraise fsmagic=0x858458f6
|
||||
# SECURITYFS_MAGIC
|
||||
dont_measure fsmagic=0x73636673
|
||||
dont_appraise fsmagic=0x73636673
|
||||
|
||||
measure func=BPRM_CHECK
|
||||
measure func=FILE_MMAP mask=MAY_EXEC
|
||||
measure func=FILE_CHECK mask=MAY_READ uid=0
|
||||
appraise fowner=0
|
||||
|
||||
The default policy measures all executables in bprm_check,
|
||||
all files mmapped executable in file_mmap, and all files
|
||||
open for read by root in do_filp_open.
|
||||
open for read by root in do_filp_open. The default appraisal
|
||||
policy appraises all files owned by root.
|
||||
|
||||
Examples of LSM specific definitions:
|
||||
|
||||
SELinux:
|
||||
# SELINUX_MAGIC
|
||||
dont_measure fsmagic=0xF97CFF8C
|
||||
dont_measure fsmagic=0xf97cff8c
|
||||
dont_appraise fsmagic=0xf97cff8c
|
||||
|
||||
dont_measure obj_type=var_log_t
|
||||
dont_appraise obj_type=var_log_t
|
||||
dont_measure obj_type=auditd_log_t
|
||||
dont_appraise obj_type=auditd_log_t
|
||||
measure subj_user=system_u func=FILE_CHECK mask=MAY_READ
|
||||
measure subj_role=system_r func=FILE_CHECK mask=MAY_READ
|
||||
|
||||
|
|
|
@ -0,0 +1,70 @@
|
|||
What: /sys/devices/pnp0/<bus-num>/ppi/
|
||||
Date: August 2012
|
||||
Kernel Version: 3.6
|
||||
Contact: xiaoyan.zhang@intel.com
|
||||
Description:
|
||||
This folder includes the attributes related with PPI (Physical
|
||||
Presence Interface). Only if TPM is supported by BIOS, this
|
||||
folder makes sence. The folder path can be got by command
|
||||
'find /sys/ -name 'pcrs''. For the detail information of PPI,
|
||||
please refer to the PPI specification from
|
||||
http://www.trustedcomputinggroup.org/
|
||||
|
||||
What: /sys/devices/pnp0/<bus-num>/ppi/version
|
||||
Date: August 2012
|
||||
Contact: xiaoyan.zhang@intel.com
|
||||
Description:
|
||||
This attribute shows the version of the PPI supported by the
|
||||
platform.
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/devices/pnp0/<bus-num>/ppi/request
|
||||
Date: August 2012
|
||||
Contact: xiaoyan.zhang@intel.com
|
||||
Description:
|
||||
This attribute shows the request for an operation to be
|
||||
executed in the pre-OS environment. It is the only input from
|
||||
the OS to the pre-OS environment. The request should be an
|
||||
integer value range from 1 to 160, and 0 means no request.
|
||||
This file can be read and written.
|
||||
|
||||
What: /sys/devices/pnp0/00:<bus-num>/ppi/response
|
||||
Date: August 2012
|
||||
Contact: xiaoyan.zhang@intel.com
|
||||
Description:
|
||||
This attribute shows the response to the most recent operation
|
||||
request it acted upon. The format is "<request> <response num>
|
||||
: <response description>".
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/devices/pnp0/<bus-num>/ppi/transition_action
|
||||
Date: August 2012
|
||||
Contact: xiaoyan.zhang@intel.com
|
||||
Description:
|
||||
This attribute shows the platform-specific action that should
|
||||
take place in order to transition to the BIOS for execution of
|
||||
a requested operation. The format is "<action num>: <action
|
||||
description>".
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/devices/pnp0/<bus-num>/ppi/tcg_operations
|
||||
Date: August 2012
|
||||
Contact: xiaoyan.zhang@intel.com
|
||||
Description:
|
||||
This attribute shows whether it is allowed to request an
|
||||
operation to be executed in the pre-OS environment by the BIOS
|
||||
for the requests defined by TCG, i.e. requests from 1 to 22.
|
||||
The format is "<request> <status num>: <status description>".
|
||||
This attribute is only supported by PPI version 1.2+.
|
||||
This file is readonly.
|
||||
|
||||
What: /sys/devices/pnp0/<bus-num>/ppi/vs_operations
|
||||
Date: August 2012
|
||||
Contact: xiaoyan.zhang@intel.com
|
||||
Description:
|
||||
This attribute shows whether it is allowed to request an
|
||||
operation to be executed in the pre-OS environment by the BIOS
|
||||
for the verdor specific requests, i.e. requests from 128 to
|
||||
255. The format is same with tcg_operations. This attribute
|
||||
is also only supported by PPI version 1.2+.
|
||||
This file is readonly.
|
|
@ -1051,6 +1051,14 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
|
|||
ihash_entries= [KNL]
|
||||
Set number of hash buckets for inode cache.
|
||||
|
||||
ima_appraise= [IMA] appraise integrity measurements
|
||||
Format: { "off" | "enforce" | "fix" }
|
||||
default: "enforce"
|
||||
|
||||
ima_appraise_tcb [IMA]
|
||||
The builtin appraise policy appraises all files
|
||||
owned by uid=0.
|
||||
|
||||
ima_audit= [IMA]
|
||||
Format: { "0" | "1" }
|
||||
0 -- integrity auditing messages. (Default)
|
||||
|
|
|
@ -28,12 +28,11 @@ Smack kernels use the CIPSO IP option. Some network
|
|||
configurations are intolerant of IP options and can impede
|
||||
access to systems that use them as Smack does.
|
||||
|
||||
The current git repositories for Smack user space are:
|
||||
The current git repository for Smack user space is:
|
||||
|
||||
git@gitorious.org:meego-platform-security/smackutil.git
|
||||
git@gitorious.org:meego-platform-security/libsmack.git
|
||||
git://github.com/smack-team/smack.git
|
||||
|
||||
These should make and install on most modern distributions.
|
||||
This should make and install on most modern distributions.
|
||||
There are three commands included in smackutil:
|
||||
|
||||
smackload - properly formats data for writing to /smack/load
|
||||
|
@ -194,6 +193,9 @@ onlycap
|
|||
these capabilities are effective at for processes with any
|
||||
label. The value is set by writing the desired label to the
|
||||
file or cleared by writing "-" to the file.
|
||||
revoke-subject
|
||||
Writing a Smack label here sets the access to '-' for all access
|
||||
rules with that subject label.
|
||||
|
||||
You can add access rules in /etc/smack/accesses. They take the form:
|
||||
|
||||
|
|
|
@ -1623,6 +1623,63 @@ static void __init prom_instantiate_rtas(void)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
/*
|
||||
* Allocate room for and instantiate Stored Measurement Log (SML)
|
||||
*/
|
||||
static void __init prom_instantiate_sml(void)
|
||||
{
|
||||
phandle ibmvtpm_node;
|
||||
ihandle ibmvtpm_inst;
|
||||
u32 entry = 0, size = 0;
|
||||
u64 base;
|
||||
|
||||
prom_debug("prom_instantiate_sml: start...\n");
|
||||
|
||||
ibmvtpm_node = call_prom("finddevice", 1, 1, ADDR("/ibm,vtpm"));
|
||||
prom_debug("ibmvtpm_node: %x\n", ibmvtpm_node);
|
||||
if (!PHANDLE_VALID(ibmvtpm_node))
|
||||
return;
|
||||
|
||||
ibmvtpm_inst = call_prom("open", 1, 1, ADDR("/ibm,vtpm"));
|
||||
if (!IHANDLE_VALID(ibmvtpm_inst)) {
|
||||
prom_printf("opening vtpm package failed (%x)\n", ibmvtpm_inst);
|
||||
return;
|
||||
}
|
||||
|
||||
if (call_prom_ret("call-method", 2, 2, &size,
|
||||
ADDR("sml-get-handover-size"),
|
||||
ibmvtpm_inst) != 0 || size == 0) {
|
||||
prom_printf("SML get handover size failed\n");
|
||||
return;
|
||||
}
|
||||
|
||||
base = alloc_down(size, PAGE_SIZE, 0);
|
||||
if (base == 0)
|
||||
prom_panic("Could not allocate memory for sml\n");
|
||||
|
||||
prom_printf("instantiating sml at 0x%x...", base);
|
||||
|
||||
if (call_prom_ret("call-method", 4, 2, &entry,
|
||||
ADDR("sml-handover"),
|
||||
ibmvtpm_inst, size, base) != 0 || entry == 0) {
|
||||
prom_printf("SML handover failed\n");
|
||||
return;
|
||||
}
|
||||
prom_printf(" done\n");
|
||||
|
||||
reserve_mem(base, size);
|
||||
|
||||
prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-base",
|
||||
&base, sizeof(base));
|
||||
prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-size",
|
||||
&size, sizeof(size));
|
||||
|
||||
prom_debug("sml base = 0x%x\n", base);
|
||||
prom_debug("sml size = 0x%x\n", (long)size);
|
||||
|
||||
prom_debug("prom_instantiate_sml: end...\n");
|
||||
}
|
||||
|
||||
/*
|
||||
* Allocate room for and initialize TCE tables
|
||||
*/
|
||||
|
@ -2916,6 +2973,11 @@ unsigned long __init prom_init(unsigned long r3, unsigned long r4,
|
|||
prom_instantiate_opal();
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PPC64
|
||||
/* instantiate sml */
|
||||
prom_instantiate_sml();
|
||||
#endif
|
||||
|
||||
/*
|
||||
* On non-powermacs, put all CPUs in spin-loops.
|
||||
*
|
||||
|
|
|
@ -289,3 +289,16 @@ config HW_RANDOM_EXYNOS
|
|||
module will be called exynos-rng.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config HW_RANDOM_TPM
|
||||
tristate "TPM HW Random Number Generator support"
|
||||
depends on HW_RANDOM && TCG_TPM
|
||||
default HW_RANDOM
|
||||
---help---
|
||||
This driver provides kernel-side support for the Random Number
|
||||
Generator in the Trusted Platform Module
|
||||
|
||||
To compile this driver as a module, choose M here: the
|
||||
module will be called tpm-rng.
|
||||
|
||||
If unsure, say Y.
|
||||
|
|
|
@ -25,3 +25,4 @@ obj-$(CONFIG_HW_RANDOM_PICOXCELL) += picoxcell-rng.o
|
|||
obj-$(CONFIG_HW_RANDOM_PPC4XX) += ppc4xx-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_PSERIES) += pseries-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_EXYNOS) += exynos-rng.o
|
||||
obj-$(CONFIG_HW_RANDOM_TPM) += tpm-rng.o
|
||||
|
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Kent Yoder IBM Corporation
|
||||
*
|
||||
* HWRNG interfaces to pull RNG data from a TPM
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*/
|
||||
|
||||
#include <linux/module.h>
|
||||
#include <linux/hw_random.h>
|
||||
#include <linux/tpm.h>
|
||||
|
||||
#define MODULE_NAME "tpm-rng"
|
||||
|
||||
static int tpm_rng_read(struct hwrng *rng, void *data, size_t max, bool wait)
|
||||
{
|
||||
return tpm_get_random(TPM_ANY_NUM, data, max);
|
||||
}
|
||||
|
||||
static struct hwrng tpm_rng = {
|
||||
.name = MODULE_NAME,
|
||||
.read = tpm_rng_read,
|
||||
};
|
||||
|
||||
static int __init rng_init(void)
|
||||
{
|
||||
return hwrng_register(&tpm_rng);
|
||||
}
|
||||
module_init(rng_init);
|
||||
|
||||
static void __exit rng_exit(void)
|
||||
{
|
||||
hwrng_unregister(&tpm_rng);
|
||||
}
|
||||
module_exit(rng_exit);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Kent Yoder <key@linux.vnet.ibm.com>");
|
||||
MODULE_DESCRIPTION("RNG driver for TPM devices");
|
|
@ -33,6 +33,17 @@ config TCG_TIS
|
|||
from within Linux. To compile this driver as a module, choose
|
||||
M here; the module will be called tpm_tis.
|
||||
|
||||
config TCG_TIS_I2C_INFINEON
|
||||
tristate "TPM Interface Specification 1.2 Interface (I2C - Infineon)"
|
||||
depends on I2C
|
||||
---help---
|
||||
If you have a TPM security chip that is compliant with the
|
||||
TCG TIS 1.2 TPM specification and Infineon's I2C Protocol Stack
|
||||
Specification 0.20 say Yes and it will be accessible from within
|
||||
Linux.
|
||||
To compile this driver as a module, choose M here; the module
|
||||
will be called tpm_tis_i2c_infineon.
|
||||
|
||||
config TCG_NSC
|
||||
tristate "National Semiconductor TPM Interface"
|
||||
depends on X86
|
||||
|
@ -62,4 +73,12 @@ config TCG_INFINEON
|
|||
Further information on this driver and the supported hardware
|
||||
can be found at http://www.trust.rub.de/projects/linux-device-driver-infineon-tpm/
|
||||
|
||||
config TCG_IBMVTPM
|
||||
tristate "IBM VTPM Interface"
|
||||
depends on PPC64
|
||||
---help---
|
||||
If you have IBM virtual TPM (VTPM) support say Yes and it
|
||||
will be accessible from within Linux. To compile this driver
|
||||
as a module, choose M here; the module will be called tpm_ibmvtpm.
|
||||
|
||||
endif # TCG_TPM
|
||||
|
|
|
@ -4,8 +4,16 @@
|
|||
obj-$(CONFIG_TCG_TPM) += tpm.o
|
||||
ifdef CONFIG_ACPI
|
||||
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
|
||||
tpm_bios-objs += tpm_eventlog.o tpm_acpi.o tpm_ppi.o
|
||||
else
|
||||
ifdef CONFIG_TCG_IBMVTPM
|
||||
obj-$(CONFIG_TCG_TPM) += tpm_bios.o
|
||||
tpm_bios-objs += tpm_eventlog.o tpm_of.o
|
||||
endif
|
||||
endif
|
||||
obj-$(CONFIG_TCG_TIS) += tpm_tis.o
|
||||
obj-$(CONFIG_TCG_TIS_I2C_INFINEON) += tpm_i2c_infineon.o
|
||||
obj-$(CONFIG_TCG_NSC) += tpm_nsc.o
|
||||
obj-$(CONFIG_TCG_ATMEL) += tpm_atmel.o
|
||||
obj-$(CONFIG_TCG_INFINEON) += tpm_infineon.o
|
||||
obj-$(CONFIG_TCG_IBMVTPM) += tpm_ibmvtpm.o
|
||||
|
|
|
@ -30,12 +30,7 @@
|
|||
#include <linux/freezer.h>
|
||||
|
||||
#include "tpm.h"
|
||||
|
||||
enum tpm_const {
|
||||
TPM_MINOR = 224, /* officially assigned */
|
||||
TPM_BUFSIZE = 4096,
|
||||
TPM_NUM_DEVICES = 256,
|
||||
};
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
enum tpm_duration {
|
||||
TPM_SHORT = 0,
|
||||
|
@ -482,6 +477,7 @@ static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd,
|
|||
#define TPM_INTERNAL_RESULT_SIZE 200
|
||||
#define TPM_TAG_RQU_COMMAND cpu_to_be16(193)
|
||||
#define TPM_ORD_GET_CAP cpu_to_be32(101)
|
||||
#define TPM_ORD_GET_RANDOM cpu_to_be32(70)
|
||||
|
||||
static const struct tpm_input_header tpm_getcap_header = {
|
||||
.tag = TPM_TAG_RQU_COMMAND,
|
||||
|
@ -919,7 +915,7 @@ EXPORT_SYMBOL_GPL(tpm_show_pcrs);
|
|||
|
||||
#define READ_PUBEK_RESULT_SIZE 314
|
||||
#define TPM_ORD_READPUBEK cpu_to_be32(124)
|
||||
struct tpm_input_header tpm_readpubek_header = {
|
||||
static struct tpm_input_header tpm_readpubek_header = {
|
||||
.tag = TPM_TAG_RQU_COMMAND,
|
||||
.length = cpu_to_be32(30),
|
||||
.ordinal = TPM_ORD_READPUBEK
|
||||
|
@ -1175,7 +1171,7 @@ int tpm_release(struct inode *inode, struct file *file)
|
|||
flush_work(&chip->work);
|
||||
file->private_data = NULL;
|
||||
atomic_set(&chip->data_pending, 0);
|
||||
kfree(chip->data_buffer);
|
||||
kzfree(chip->data_buffer);
|
||||
clear_bit(0, &chip->is_open);
|
||||
put_device(chip->dev);
|
||||
return 0;
|
||||
|
@ -1227,7 +1223,6 @@ ssize_t tpm_read(struct file *file, char __user *buf,
|
|||
del_singleshot_timer_sync(&chip->user_read_timer);
|
||||
flush_work(&chip->work);
|
||||
ret_size = atomic_read(&chip->data_pending);
|
||||
atomic_set(&chip->data_pending, 0);
|
||||
if (ret_size > 0) { /* relay data */
|
||||
ssize_t orig_ret_size = ret_size;
|
||||
if (size < ret_size)
|
||||
|
@ -1242,6 +1237,8 @@ ssize_t tpm_read(struct file *file, char __user *buf,
|
|||
mutex_unlock(&chip->buffer_mutex);
|
||||
}
|
||||
|
||||
atomic_set(&chip->data_pending, 0);
|
||||
|
||||
return ret_size;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_read);
|
||||
|
@ -1326,6 +1323,58 @@ int tpm_pm_resume(struct device *dev)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_pm_resume);
|
||||
|
||||
#define TPM_GETRANDOM_RESULT_SIZE 18
|
||||
static struct tpm_input_header tpm_getrandom_header = {
|
||||
.tag = TPM_TAG_RQU_COMMAND,
|
||||
.length = cpu_to_be32(14),
|
||||
.ordinal = TPM_ORD_GET_RANDOM
|
||||
};
|
||||
|
||||
/**
|
||||
* tpm_get_random() - Get random bytes from the tpm's RNG
|
||||
* @chip_num: A specific chip number for the request or TPM_ANY_NUM
|
||||
* @out: destination buffer for the random bytes
|
||||
* @max: the max number of bytes to write to @out
|
||||
*
|
||||
* Returns < 0 on error and the number of bytes read on success
|
||||
*/
|
||||
int tpm_get_random(u32 chip_num, u8 *out, size_t max)
|
||||
{
|
||||
struct tpm_chip *chip;
|
||||
struct tpm_cmd_t tpm_cmd;
|
||||
u32 recd, num_bytes = min_t(u32, max, TPM_MAX_RNG_DATA);
|
||||
int err, total = 0, retries = 5;
|
||||
u8 *dest = out;
|
||||
|
||||
chip = tpm_chip_find_get(chip_num);
|
||||
if (chip == NULL)
|
||||
return -ENODEV;
|
||||
|
||||
if (!out || !num_bytes || max > TPM_MAX_RNG_DATA)
|
||||
return -EINVAL;
|
||||
|
||||
do {
|
||||
tpm_cmd.header.in = tpm_getrandom_header;
|
||||
tpm_cmd.params.getrandom_in.num_bytes = cpu_to_be32(num_bytes);
|
||||
|
||||
err = transmit_cmd(chip, &tpm_cmd,
|
||||
TPM_GETRANDOM_RESULT_SIZE + num_bytes,
|
||||
"attempting get random");
|
||||
if (err)
|
||||
break;
|
||||
|
||||
recd = be32_to_cpu(tpm_cmd.params.getrandom_out.rng_data_len);
|
||||
memcpy(dest, tpm_cmd.params.getrandom_out.rng_data, recd);
|
||||
|
||||
dest += recd;
|
||||
total += recd;
|
||||
num_bytes -= recd;
|
||||
} while (retries-- && total < max);
|
||||
|
||||
return total ? total : -EIO;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(tpm_get_random);
|
||||
|
||||
/* In case vendor provided release function, call it too.*/
|
||||
|
||||
void tpm_dev_vendor_release(struct tpm_chip *chip)
|
||||
|
@ -1346,7 +1395,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_vendor_release);
|
|||
* Once all references to platform device are down to 0,
|
||||
* release all allocated structures.
|
||||
*/
|
||||
void tpm_dev_release(struct device *dev)
|
||||
static void tpm_dev_release(struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
|
||||
|
@ -1427,6 +1476,11 @@ struct tpm_chip *tpm_register_hardware(struct device *dev,
|
|||
goto put_device;
|
||||
}
|
||||
|
||||
if (sys_add_ppi(&dev->kobj)) {
|
||||
misc_deregister(&chip->vendor.miscdev);
|
||||
goto put_device;
|
||||
}
|
||||
|
||||
chip->bios_dir = tpm_bios_log_setup(devname);
|
||||
|
||||
/* Make chip available */
|
||||
|
|
|
@ -28,6 +28,12 @@
|
|||
#include <linux/io.h>
|
||||
#include <linux/tpm.h>
|
||||
|
||||
enum tpm_const {
|
||||
TPM_MINOR = 224, /* officially assigned */
|
||||
TPM_BUFSIZE = 4096,
|
||||
TPM_NUM_DEVICES = 256,
|
||||
};
|
||||
|
||||
enum tpm_timeout {
|
||||
TPM_TIMEOUT = 5, /* msecs */
|
||||
};
|
||||
|
@ -94,6 +100,7 @@ struct tpm_vendor_specific {
|
|||
bool timeout_adjusted;
|
||||
unsigned long duration[3]; /* jiffies */
|
||||
bool duration_adjusted;
|
||||
void *data;
|
||||
|
||||
wait_queue_head_t read_queue;
|
||||
wait_queue_head_t int_queue;
|
||||
|
@ -269,6 +276,21 @@ struct tpm_pcrextend_in {
|
|||
u8 hash[TPM_DIGEST_SIZE];
|
||||
}__attribute__((packed));
|
||||
|
||||
/* 128 bytes is an arbitrary cap. This could be as large as TPM_BUFSIZE - 18
|
||||
* bytes, but 128 is still a relatively large number of random bytes and
|
||||
* anything much bigger causes users of struct tpm_cmd_t to start getting
|
||||
* compiler warnings about stack frame size. */
|
||||
#define TPM_MAX_RNG_DATA 128
|
||||
|
||||
struct tpm_getrandom_out {
|
||||
__be32 rng_data_len;
|
||||
u8 rng_data[TPM_MAX_RNG_DATA];
|
||||
}__attribute__((packed));
|
||||
|
||||
struct tpm_getrandom_in {
|
||||
__be32 num_bytes;
|
||||
}__attribute__((packed));
|
||||
|
||||
typedef union {
|
||||
struct tpm_getcap_params_out getcap_out;
|
||||
struct tpm_readpubek_params_out readpubek_out;
|
||||
|
@ -277,6 +299,8 @@ typedef union {
|
|||
struct tpm_pcrread_in pcrread_in;
|
||||
struct tpm_pcrread_out pcrread_out;
|
||||
struct tpm_pcrextend_in pcrextend_in;
|
||||
struct tpm_getrandom_in getrandom_in;
|
||||
struct tpm_getrandom_out getrandom_out;
|
||||
} tpm_cmd_params;
|
||||
|
||||
struct tpm_cmd_t {
|
||||
|
@ -303,15 +327,12 @@ extern int tpm_pm_suspend(struct device *);
|
|||
extern int tpm_pm_resume(struct device *);
|
||||
extern int wait_for_tpm_stat(struct tpm_chip *, u8, unsigned long,
|
||||
wait_queue_head_t *);
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
extern struct dentry ** tpm_bios_log_setup(char *);
|
||||
extern void tpm_bios_log_teardown(struct dentry **);
|
||||
extern ssize_t sys_add_ppi(struct kobject *parent);
|
||||
#else
|
||||
static inline struct dentry ** tpm_bios_log_setup(char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void tpm_bios_log_teardown(struct dentry **dir)
|
||||
static inline ssize_t sys_add_ppi(struct kobject *parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
* Copyright (C) 2005 IBM Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Seiji Munetoh <munetoh@jp.ibm.com>
|
||||
* Stefan Berger <stefanb@us.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
* Kylene Hall <kjhall@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Access to the eventlog extended by the TCG BIOS of PC platform
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/seq_file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <acpi/acpi.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
struct acpi_tcpa {
|
||||
struct acpi_table_header hdr;
|
||||
u16 platform_class;
|
||||
union {
|
||||
struct client_hdr {
|
||||
u32 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} client;
|
||||
struct server_hdr {
|
||||
u16 reserved;
|
||||
u64 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} server;
|
||||
};
|
||||
};
|
||||
|
||||
/* read binary bios log */
|
||||
int read_log(struct tpm_bios_log *log)
|
||||
{
|
||||
struct acpi_tcpa *buff;
|
||||
acpi_status status;
|
||||
void __iomem *virt;
|
||||
u64 len, start;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
printk(KERN_ERR
|
||||
"%s: ERROR - Eventlog already initialized\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
|
||||
status = acpi_get_table(ACPI_SIG_TCPA, 1,
|
||||
(struct acpi_table_header **)&buff);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch(buff->platform_class) {
|
||||
case BIOS_SERVER:
|
||||
len = buff->server.log_max_len;
|
||||
start = buff->server.log_start_addr;
|
||||
break;
|
||||
case BIOS_CLIENT:
|
||||
default:
|
||||
len = buff->client.log_max_len;
|
||||
start = buff->client.log_start_addr;
|
||||
break;
|
||||
}
|
||||
if (!len) {
|
||||
printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* malloc EventLog space */
|
||||
log->bios_event_log = kmalloc(len, GFP_KERNEL);
|
||||
if (!log->bios_event_log) {
|
||||
printk("%s: ERROR - Not enough Memory for BIOS measurements\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + len;
|
||||
|
||||
virt = acpi_os_map_memory(start, len);
|
||||
if (!virt) {
|
||||
kfree(log->bios_event_log);
|
||||
printk("%s: ERROR - Unable to map memory\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
memcpy_fromio(log->bios_event_log, virt, len);
|
||||
|
||||
acpi_os_unmap_memory(virt, len);
|
||||
return 0;
|
||||
}
|
|
@ -1,7 +1,8 @@
|
|||
/*
|
||||
* Copyright (C) 2005 IBM Corporation
|
||||
* Copyright (C) 2005, 2012 IBM Corporation
|
||||
*
|
||||
* Authors:
|
||||
* Kent Yoder <key@linux.vnet.ibm.com>
|
||||
* Seiji Munetoh <munetoh@jp.ibm.com>
|
||||
* Stefan Berger <stefanb@us.ibm.com>
|
||||
* Reiner Sailer <sailer@watson.ibm.com>
|
||||
|
@ -9,7 +10,7 @@
|
|||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Access to the eventlog extended by the TCG BIOS of PC platform
|
||||
* Access to the eventlog created by a system's firmware / BIOS
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
|
@ -23,67 +24,10 @@
|
|||
#include <linux/security.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
#include <acpi/acpi.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
#define TCG_EVENT_NAME_LEN_MAX 255
|
||||
#define MAX_TEXT_EVENT 1000 /* Max event string length */
|
||||
#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
|
||||
|
||||
enum bios_platform_class {
|
||||
BIOS_CLIENT = 0x00,
|
||||
BIOS_SERVER = 0x01,
|
||||
};
|
||||
|
||||
struct tpm_bios_log {
|
||||
void *bios_event_log;
|
||||
void *bios_event_log_end;
|
||||
};
|
||||
|
||||
struct acpi_tcpa {
|
||||
struct acpi_table_header hdr;
|
||||
u16 platform_class;
|
||||
union {
|
||||
struct client_hdr {
|
||||
u32 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} client;
|
||||
struct server_hdr {
|
||||
u16 reserved;
|
||||
u64 log_max_len __attribute__ ((packed));
|
||||
u64 log_start_addr __attribute__ ((packed));
|
||||
} server;
|
||||
};
|
||||
};
|
||||
|
||||
struct tcpa_event {
|
||||
u32 pcr_index;
|
||||
u32 event_type;
|
||||
u8 pcr_value[20]; /* SHA1 */
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_event_types {
|
||||
PREBOOT = 0,
|
||||
POST_CODE,
|
||||
UNUSED,
|
||||
NO_ACTION,
|
||||
SEPARATOR,
|
||||
ACTION,
|
||||
EVENT_TAG,
|
||||
SCRTM_CONTENTS,
|
||||
SCRTM_VERSION,
|
||||
CPU_MICROCODE,
|
||||
PLATFORM_CONFIG_FLAGS,
|
||||
TABLE_OF_DEVICES,
|
||||
COMPACT_HASH,
|
||||
IPL,
|
||||
IPL_PARTITION_DATA,
|
||||
NONHOST_CODE,
|
||||
NONHOST_CONFIG,
|
||||
NONHOST_INFO,
|
||||
};
|
||||
|
||||
static const char* tcpa_event_type_strings[] = {
|
||||
"PREBOOT",
|
||||
|
@ -106,28 +50,6 @@ static const char* tcpa_event_type_strings[] = {
|
|||
"Non-Host Info"
|
||||
};
|
||||
|
||||
struct tcpa_pc_event {
|
||||
u32 event_id;
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_pc_event_ids {
|
||||
SMBIOS = 1,
|
||||
BIS_CERT,
|
||||
POST_BIOS_ROM,
|
||||
ESCD,
|
||||
CMOS,
|
||||
NVRAM,
|
||||
OPTION_ROM_EXEC,
|
||||
OPTION_ROM_CONFIG,
|
||||
OPTION_ROM_MICROCODE = 10,
|
||||
S_CRTM_VERSION,
|
||||
S_CRTM_CONTENTS,
|
||||
POST_CONTENTS,
|
||||
HOST_TABLE_OF_DEVICES,
|
||||
};
|
||||
|
||||
static const char* tcpa_pc_event_id_strings[] = {
|
||||
"",
|
||||
"SMBIOS",
|
||||
|
@ -358,65 +280,6 @@ static const struct seq_operations tpm_binary_b_measurments_seqops = {
|
|||
.show = tpm_binary_bios_measurements_show,
|
||||
};
|
||||
|
||||
/* read binary bios log */
|
||||
static int read_log(struct tpm_bios_log *log)
|
||||
{
|
||||
struct acpi_tcpa *buff;
|
||||
acpi_status status;
|
||||
struct acpi_table_header *virt;
|
||||
u64 len, start;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
printk(KERN_ERR
|
||||
"%s: ERROR - Eventlog already initialized\n",
|
||||
__func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
/* Find TCPA entry in RSDT (ACPI_LOGICAL_ADDRESSING) */
|
||||
status = acpi_get_table(ACPI_SIG_TCPA, 1,
|
||||
(struct acpi_table_header **)&buff);
|
||||
|
||||
if (ACPI_FAILURE(status)) {
|
||||
printk(KERN_ERR "%s: ERROR - Could not get TCPA table\n",
|
||||
__func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
switch(buff->platform_class) {
|
||||
case BIOS_SERVER:
|
||||
len = buff->server.log_max_len;
|
||||
start = buff->server.log_start_addr;
|
||||
break;
|
||||
case BIOS_CLIENT:
|
||||
default:
|
||||
len = buff->client.log_max_len;
|
||||
start = buff->client.log_start_addr;
|
||||
break;
|
||||
}
|
||||
if (!len) {
|
||||
printk(KERN_ERR "%s: ERROR - TCPA log area empty\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* malloc EventLog space */
|
||||
log->bios_event_log = kmalloc(len, GFP_KERNEL);
|
||||
if (!log->bios_event_log) {
|
||||
printk("%s: ERROR - Not enough Memory for BIOS measurements\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + len;
|
||||
|
||||
virt = acpi_os_map_memory(start, len);
|
||||
|
||||
memcpy(log->bios_event_log, virt, len);
|
||||
|
||||
acpi_os_unmap_memory(virt, len);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tpm_ascii_bios_measurements_open(struct inode *inode,
|
||||
struct file *file)
|
||||
{
|
|
@ -0,0 +1,86 @@
|
|||
|
||||
#ifndef __TPM_EVENTLOG_H__
|
||||
#define __TPM_EVENTLOG_H__
|
||||
|
||||
#define TCG_EVENT_NAME_LEN_MAX 255
|
||||
#define MAX_TEXT_EVENT 1000 /* Max event string length */
|
||||
#define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */
|
||||
|
||||
enum bios_platform_class {
|
||||
BIOS_CLIENT = 0x00,
|
||||
BIOS_SERVER = 0x01,
|
||||
};
|
||||
|
||||
struct tpm_bios_log {
|
||||
void *bios_event_log;
|
||||
void *bios_event_log_end;
|
||||
};
|
||||
|
||||
struct tcpa_event {
|
||||
u32 pcr_index;
|
||||
u32 event_type;
|
||||
u8 pcr_value[20]; /* SHA1 */
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_event_types {
|
||||
PREBOOT = 0,
|
||||
POST_CODE,
|
||||
UNUSED,
|
||||
NO_ACTION,
|
||||
SEPARATOR,
|
||||
ACTION,
|
||||
EVENT_TAG,
|
||||
SCRTM_CONTENTS,
|
||||
SCRTM_VERSION,
|
||||
CPU_MICROCODE,
|
||||
PLATFORM_CONFIG_FLAGS,
|
||||
TABLE_OF_DEVICES,
|
||||
COMPACT_HASH,
|
||||
IPL,
|
||||
IPL_PARTITION_DATA,
|
||||
NONHOST_CODE,
|
||||
NONHOST_CONFIG,
|
||||
NONHOST_INFO,
|
||||
};
|
||||
|
||||
struct tcpa_pc_event {
|
||||
u32 event_id;
|
||||
u32 event_size;
|
||||
u8 event_data[0];
|
||||
};
|
||||
|
||||
enum tcpa_pc_event_ids {
|
||||
SMBIOS = 1,
|
||||
BIS_CERT,
|
||||
POST_BIOS_ROM,
|
||||
ESCD,
|
||||
CMOS,
|
||||
NVRAM,
|
||||
OPTION_ROM_EXEC,
|
||||
OPTION_ROM_CONFIG,
|
||||
OPTION_ROM_MICROCODE = 10,
|
||||
S_CRTM_VERSION,
|
||||
S_CRTM_CONTENTS,
|
||||
POST_CONTENTS,
|
||||
HOST_TABLE_OF_DEVICES,
|
||||
};
|
||||
|
||||
int read_log(struct tpm_bios_log *log);
|
||||
|
||||
#if defined(CONFIG_TCG_IBMVTPM) || defined(CONFIG_TCG_IBMVTPM_MODULE) || \
|
||||
defined(CONFIG_ACPI)
|
||||
extern struct dentry **tpm_bios_log_setup(char *);
|
||||
extern void tpm_bios_log_teardown(struct dentry **);
|
||||
#else
|
||||
static inline struct dentry **tpm_bios_log_setup(char *name)
|
||||
{
|
||||
return NULL;
|
||||
}
|
||||
static inline void tpm_bios_log_teardown(struct dentry **dir)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
|
@ -0,0 +1,695 @@
|
|||
/*
|
||||
* Copyright (C) 2012 Infineon Technologies
|
||||
*
|
||||
* Authors:
|
||||
* Peter Huewe <peter.huewe@infineon.com>
|
||||
*
|
||||
* Device driver for TCG/TCPA TPM (trusted platform module).
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* This device driver implements the TPM interface as defined in
|
||||
* the TCG TPM Interface Spec version 1.2, revision 1.0 and the
|
||||
* Infineon I2C Protocol Stack Specification v0.20.
|
||||
*
|
||||
* It is based on the original tpm_tis device driver from Leendert van
|
||||
* Dorn and Kyleen Hall.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*
|
||||
*/
|
||||
#include <linux/init.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/moduleparam.h>
|
||||
#include <linux/wait.h>
|
||||
#include "tpm.h"
|
||||
|
||||
/* max. buffer size supported by our TPM */
|
||||
#define TPM_BUFSIZE 1260
|
||||
|
||||
/* max. number of iterations after I2C NAK */
|
||||
#define MAX_COUNT 3
|
||||
|
||||
#define SLEEP_DURATION_LOW 55
|
||||
#define SLEEP_DURATION_HI 65
|
||||
|
||||
/* max. number of iterations after I2C NAK for 'long' commands
|
||||
* we need this especially for sending TPM_READY, since the cleanup after the
|
||||
* transtion to the ready state may take some time, but it is unpredictable
|
||||
* how long it will take.
|
||||
*/
|
||||
#define MAX_COUNT_LONG 50
|
||||
|
||||
#define SLEEP_DURATION_LONG_LOW 200
|
||||
#define SLEEP_DURATION_LONG_HI 220
|
||||
|
||||
/* After sending TPM_READY to 'reset' the TPM we have to sleep even longer */
|
||||
#define SLEEP_DURATION_RESET_LOW 2400
|
||||
#define SLEEP_DURATION_RESET_HI 2600
|
||||
|
||||
/* we want to use usleep_range instead of msleep for the 5ms TPM_TIMEOUT */
|
||||
#define TPM_TIMEOUT_US_LOW (TPM_TIMEOUT * 1000)
|
||||
#define TPM_TIMEOUT_US_HI (TPM_TIMEOUT_US_LOW + 2000)
|
||||
|
||||
/* expected value for DIDVID register */
|
||||
#define TPM_TIS_I2C_DID_VID 0x000b15d1L
|
||||
|
||||
/* Structure to store I2C TPM specific stuff */
|
||||
struct tpm_inf_dev {
|
||||
struct i2c_client *client;
|
||||
u8 buf[TPM_BUFSIZE + sizeof(u8)]; /* max. buffer size + addr */
|
||||
struct tpm_chip *chip;
|
||||
};
|
||||
|
||||
static struct tpm_inf_dev tpm_dev;
|
||||
static struct i2c_driver tpm_tis_i2c_driver;
|
||||
|
||||
/*
|
||||
* iic_tpm_read() - read from TPM register
|
||||
* @addr: register address to read from
|
||||
* @buffer: provided by caller
|
||||
* @len: number of bytes to read
|
||||
*
|
||||
* Read len bytes from TPM register and put them into
|
||||
* buffer (little-endian format, i.e. first byte is put into buffer[0]).
|
||||
*
|
||||
* NOTE: TPM is big-endian for multi-byte values. Multi-byte
|
||||
* values have to be swapped.
|
||||
*
|
||||
* NOTE: We can't unfortunately use the combined read/write functions
|
||||
* provided by the i2c core as the TPM currently does not support the
|
||||
* repeated start condition and due to it's special requirements.
|
||||
* The i2c_smbus* functions do not work for this chip.
|
||||
*
|
||||
* Return -EIO on error, 0 on success.
|
||||
*/
|
||||
static int iic_tpm_read(u8 addr, u8 *buffer, size_t len)
|
||||
{
|
||||
|
||||
struct i2c_msg msg1 = { tpm_dev.client->addr, 0, 1, &addr };
|
||||
struct i2c_msg msg2 = { tpm_dev.client->addr, I2C_M_RD, len, buffer };
|
||||
|
||||
int rc;
|
||||
int count;
|
||||
|
||||
/* Lock the adapter for the duration of the whole sequence. */
|
||||
if (!tpm_dev.client->adapter->algo->master_xfer)
|
||||
return -EOPNOTSUPP;
|
||||
i2c_lock_adapter(tpm_dev.client->adapter);
|
||||
|
||||
for (count = 0; count < MAX_COUNT; count++) {
|
||||
rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
|
||||
if (rc > 0)
|
||||
break; /* break here to skip sleep */
|
||||
|
||||
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
|
||||
}
|
||||
|
||||
if (rc <= 0)
|
||||
goto out;
|
||||
|
||||
/* After the TPM has successfully received the register address it needs
|
||||
* some time, thus we're sleeping here again, before retrieving the data
|
||||
*/
|
||||
for (count = 0; count < MAX_COUNT; count++) {
|
||||
usleep_range(SLEEP_DURATION_LOW, SLEEP_DURATION_HI);
|
||||
rc = __i2c_transfer(tpm_dev.client->adapter, &msg2, 1);
|
||||
if (rc > 0)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
out:
|
||||
i2c_unlock_adapter(tpm_dev.client->adapter);
|
||||
if (rc <= 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int iic_tpm_write_generic(u8 addr, u8 *buffer, size_t len,
|
||||
unsigned int sleep_low,
|
||||
unsigned int sleep_hi, u8 max_count)
|
||||
{
|
||||
int rc = -EIO;
|
||||
int count;
|
||||
|
||||
struct i2c_msg msg1 = { tpm_dev.client->addr, 0, len + 1, tpm_dev.buf };
|
||||
|
||||
if (len > TPM_BUFSIZE)
|
||||
return -EINVAL;
|
||||
|
||||
if (!tpm_dev.client->adapter->algo->master_xfer)
|
||||
return -EOPNOTSUPP;
|
||||
i2c_lock_adapter(tpm_dev.client->adapter);
|
||||
|
||||
/* prepend the 'register address' to the buffer */
|
||||
tpm_dev.buf[0] = addr;
|
||||
memcpy(&(tpm_dev.buf[1]), buffer, len);
|
||||
|
||||
/*
|
||||
* NOTE: We have to use these special mechanisms here and unfortunately
|
||||
* cannot rely on the standard behavior of i2c_transfer.
|
||||
*/
|
||||
for (count = 0; count < max_count; count++) {
|
||||
rc = __i2c_transfer(tpm_dev.client->adapter, &msg1, 1);
|
||||
if (rc > 0)
|
||||
break;
|
||||
|
||||
usleep_range(sleep_low, sleep_hi);
|
||||
}
|
||||
|
||||
i2c_unlock_adapter(tpm_dev.client->adapter);
|
||||
if (rc <= 0)
|
||||
return -EIO;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* iic_tpm_write() - write to TPM register
|
||||
* @addr: register address to write to
|
||||
* @buffer: containing data to be written
|
||||
* @len: number of bytes to write
|
||||
*
|
||||
* Write len bytes from provided buffer to TPM register (little
|
||||
* endian format, i.e. buffer[0] is written as first byte).
|
||||
*
|
||||
* NOTE: TPM is big-endian for multi-byte values. Multi-byte
|
||||
* values have to be swapped.
|
||||
*
|
||||
* NOTE: use this function instead of the iic_tpm_write_generic function.
|
||||
*
|
||||
* Return -EIO on error, 0 on success
|
||||
*/
|
||||
static int iic_tpm_write(u8 addr, u8 *buffer, size_t len)
|
||||
{
|
||||
return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LOW,
|
||||
SLEEP_DURATION_HI, MAX_COUNT);
|
||||
}
|
||||
|
||||
/*
|
||||
* This function is needed especially for the cleanup situation after
|
||||
* sending TPM_READY
|
||||
* */
|
||||
static int iic_tpm_write_long(u8 addr, u8 *buffer, size_t len)
|
||||
{
|
||||
return iic_tpm_write_generic(addr, buffer, len, SLEEP_DURATION_LONG_LOW,
|
||||
SLEEP_DURATION_LONG_HI, MAX_COUNT_LONG);
|
||||
}
|
||||
|
||||
enum tis_access {
|
||||
TPM_ACCESS_VALID = 0x80,
|
||||
TPM_ACCESS_ACTIVE_LOCALITY = 0x20,
|
||||
TPM_ACCESS_REQUEST_PENDING = 0x04,
|
||||
TPM_ACCESS_REQUEST_USE = 0x02,
|
||||
};
|
||||
|
||||
enum tis_status {
|
||||
TPM_STS_VALID = 0x80,
|
||||
TPM_STS_COMMAND_READY = 0x40,
|
||||
TPM_STS_GO = 0x20,
|
||||
TPM_STS_DATA_AVAIL = 0x10,
|
||||
TPM_STS_DATA_EXPECT = 0x08,
|
||||
};
|
||||
|
||||
enum tis_defaults {
|
||||
TIS_SHORT_TIMEOUT = 750, /* ms */
|
||||
TIS_LONG_TIMEOUT = 2000, /* 2 sec */
|
||||
};
|
||||
|
||||
#define TPM_ACCESS(l) (0x0000 | ((l) << 4))
|
||||
#define TPM_STS(l) (0x0001 | ((l) << 4))
|
||||
#define TPM_DATA_FIFO(l) (0x0005 | ((l) << 4))
|
||||
#define TPM_DID_VID(l) (0x0006 | ((l) << 4))
|
||||
|
||||
static int check_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
u8 buf;
|
||||
int rc;
|
||||
|
||||
rc = iic_tpm_read(TPM_ACCESS(loc), &buf, 1);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if ((buf & (TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_ACTIVE_LOCALITY | TPM_ACCESS_VALID)) {
|
||||
chip->vendor.locality = loc;
|
||||
return loc;
|
||||
}
|
||||
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
/* implementation similar to tpm_tis */
|
||||
static void release_locality(struct tpm_chip *chip, int loc, int force)
|
||||
{
|
||||
u8 buf;
|
||||
if (iic_tpm_read(TPM_ACCESS(loc), &buf, 1) < 0)
|
||||
return;
|
||||
|
||||
if (force || (buf & (TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) ==
|
||||
(TPM_ACCESS_REQUEST_PENDING | TPM_ACCESS_VALID)) {
|
||||
buf = TPM_ACCESS_ACTIVE_LOCALITY;
|
||||
iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
|
||||
}
|
||||
}
|
||||
|
||||
static int request_locality(struct tpm_chip *chip, int loc)
|
||||
{
|
||||
unsigned long stop;
|
||||
u8 buf = TPM_ACCESS_REQUEST_USE;
|
||||
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
return loc;
|
||||
|
||||
iic_tpm_write(TPM_ACCESS(loc), &buf, 1);
|
||||
|
||||
/* wait for burstcount */
|
||||
stop = jiffies + chip->vendor.timeout_a;
|
||||
do {
|
||||
if (check_locality(chip, loc) >= 0)
|
||||
return loc;
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
static u8 tpm_tis_i2c_status(struct tpm_chip *chip)
|
||||
{
|
||||
/* NOTE: since I2C read may fail, return 0 in this case --> time-out */
|
||||
u8 buf;
|
||||
if (iic_tpm_read(TPM_STS(chip->vendor.locality), &buf, 1) < 0)
|
||||
return 0;
|
||||
else
|
||||
return buf;
|
||||
}
|
||||
|
||||
static void tpm_tis_i2c_ready(struct tpm_chip *chip)
|
||||
{
|
||||
/* this causes the current command to be aborted */
|
||||
u8 buf = TPM_STS_COMMAND_READY;
|
||||
iic_tpm_write_long(TPM_STS(chip->vendor.locality), &buf, 1);
|
||||
}
|
||||
|
||||
static ssize_t get_burstcount(struct tpm_chip *chip)
|
||||
{
|
||||
unsigned long stop;
|
||||
ssize_t burstcnt;
|
||||
u8 buf[3];
|
||||
|
||||
/* wait for burstcount */
|
||||
/* which timeout value, spec has 2 answers (c & d) */
|
||||
stop = jiffies + chip->vendor.timeout_d;
|
||||
do {
|
||||
/* Note: STS is little endian */
|
||||
if (iic_tpm_read(TPM_STS(chip->vendor.locality)+1, buf, 3) < 0)
|
||||
burstcnt = 0;
|
||||
else
|
||||
burstcnt = (buf[2] << 16) + (buf[1] << 8) + buf[0];
|
||||
|
||||
if (burstcnt)
|
||||
return burstcnt;
|
||||
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
} while (time_before(jiffies, stop));
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
static int wait_for_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout,
|
||||
int *status)
|
||||
{
|
||||
unsigned long stop;
|
||||
|
||||
/* check current status */
|
||||
*status = tpm_tis_i2c_status(chip);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
stop = jiffies + timeout;
|
||||
do {
|
||||
/* since we just checked the status, give the TPM some time */
|
||||
usleep_range(TPM_TIMEOUT_US_LOW, TPM_TIMEOUT_US_HI);
|
||||
*status = tpm_tis_i2c_status(chip);
|
||||
if ((*status & mask) == mask)
|
||||
return 0;
|
||||
|
||||
} while (time_before(jiffies, stop));
|
||||
|
||||
return -ETIME;
|
||||
}
|
||||
|
||||
static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
size_t size = 0;
|
||||
ssize_t burstcnt;
|
||||
u8 retries = 0;
|
||||
int rc;
|
||||
|
||||
while (size < count) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
|
||||
/* burstcnt < 0 = TPM is busy */
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
|
||||
/* limit received data to max. left */
|
||||
if (burstcnt > (count - size))
|
||||
burstcnt = count - size;
|
||||
|
||||
rc = iic_tpm_read(TPM_DATA_FIFO(chip->vendor.locality),
|
||||
&(buf[size]), burstcnt);
|
||||
if (rc == 0)
|
||||
size += burstcnt;
|
||||
else if (rc < 0)
|
||||
retries++;
|
||||
|
||||
/* avoid endless loop in case of broken HW */
|
||||
if (retries > MAX_COUNT_LONG)
|
||||
return -EIO;
|
||||
|
||||
}
|
||||
return size;
|
||||
}
|
||||
|
||||
static int tpm_tis_i2c_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
int size = 0;
|
||||
int expected, status;
|
||||
|
||||
if (count < TPM_HEADER_SIZE) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* read first 10 bytes, including tag, paramsize, and result */
|
||||
size = recv_data(chip, buf, TPM_HEADER_SIZE);
|
||||
if (size < TPM_HEADER_SIZE) {
|
||||
dev_err(chip->dev, "Unable to read header\n");
|
||||
goto out;
|
||||
}
|
||||
|
||||
expected = be32_to_cpu(*(__be32 *)(buf + 2));
|
||||
if ((size_t) expected > count) {
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
size += recv_data(chip, &buf[TPM_HEADER_SIZE],
|
||||
expected - TPM_HEADER_SIZE);
|
||||
if (size < expected) {
|
||||
dev_err(chip->dev, "Unable to read remainder of result\n");
|
||||
size = -ETIME;
|
||||
goto out;
|
||||
}
|
||||
|
||||
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
|
||||
if (status & TPM_STS_DATA_AVAIL) { /* retry? */
|
||||
dev_err(chip->dev, "Error left over data\n");
|
||||
size = -EIO;
|
||||
goto out;
|
||||
}
|
||||
|
||||
out:
|
||||
tpm_tis_i2c_ready(chip);
|
||||
/* The TPM needs some time to clean up here,
|
||||
* so we sleep rather than keeping the bus busy
|
||||
*/
|
||||
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
|
||||
release_locality(chip, chip->vendor.locality, 0);
|
||||
return size;
|
||||
}
|
||||
|
||||
static int tpm_tis_i2c_send(struct tpm_chip *chip, u8 *buf, size_t len)
|
||||
{
|
||||
int rc, status;
|
||||
ssize_t burstcnt;
|
||||
size_t count = 0;
|
||||
u8 retries = 0;
|
||||
u8 sts = TPM_STS_GO;
|
||||
|
||||
if (len > TPM_BUFSIZE)
|
||||
return -E2BIG; /* command is too long for our tpm, sorry */
|
||||
|
||||
if (request_locality(chip, 0) < 0)
|
||||
return -EBUSY;
|
||||
|
||||
status = tpm_tis_i2c_status(chip);
|
||||
if ((status & TPM_STS_COMMAND_READY) == 0) {
|
||||
tpm_tis_i2c_ready(chip);
|
||||
if (wait_for_stat
|
||||
(chip, TPM_STS_COMMAND_READY,
|
||||
chip->vendor.timeout_b, &status) < 0) {
|
||||
rc = -ETIME;
|
||||
goto out_err;
|
||||
}
|
||||
}
|
||||
|
||||
while (count < len - 1) {
|
||||
burstcnt = get_burstcount(chip);
|
||||
|
||||
/* burstcnt < 0 = TPM is busy */
|
||||
if (burstcnt < 0)
|
||||
return burstcnt;
|
||||
|
||||
if (burstcnt > (len - 1 - count))
|
||||
burstcnt = len - 1 - count;
|
||||
|
||||
rc = iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality),
|
||||
&(buf[count]), burstcnt);
|
||||
if (rc == 0)
|
||||
count += burstcnt;
|
||||
else if (rc < 0)
|
||||
retries++;
|
||||
|
||||
/* avoid endless loop in case of broken HW */
|
||||
if (retries > MAX_COUNT_LONG) {
|
||||
rc = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
wait_for_stat(chip, TPM_STS_VALID,
|
||||
chip->vendor.timeout_c, &status);
|
||||
|
||||
if ((status & TPM_STS_DATA_EXPECT) == 0) {
|
||||
rc = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/* write last byte */
|
||||
iic_tpm_write(TPM_DATA_FIFO(chip->vendor.locality), &(buf[count]), 1);
|
||||
wait_for_stat(chip, TPM_STS_VALID, chip->vendor.timeout_c, &status);
|
||||
if ((status & TPM_STS_DATA_EXPECT) != 0) {
|
||||
rc = -EIO;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* go and do it */
|
||||
iic_tpm_write(TPM_STS(chip->vendor.locality), &sts, 1);
|
||||
|
||||
return len;
|
||||
out_err:
|
||||
tpm_tis_i2c_ready(chip);
|
||||
/* The TPM needs some time to clean up here,
|
||||
* so we sleep rather than keeping the bus busy
|
||||
*/
|
||||
usleep_range(SLEEP_DURATION_RESET_LOW, SLEEP_DURATION_RESET_HI);
|
||||
release_locality(chip, chip->vendor.locality, 0);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations tis_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
|
||||
static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
|
||||
static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
||||
static struct attribute *tis_attrs[] = {
|
||||
&dev_attr_pubek.attr,
|
||||
&dev_attr_pcrs.attr,
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_active.attr,
|
||||
&dev_attr_owned.attr,
|
||||
&dev_attr_temp_deactivated.attr,
|
||||
&dev_attr_caps.attr,
|
||||
&dev_attr_cancel.attr,
|
||||
&dev_attr_durations.attr,
|
||||
&dev_attr_timeouts.attr,
|
||||
NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group tis_attr_grp = {
|
||||
.attrs = tis_attrs
|
||||
};
|
||||
|
||||
static struct tpm_vendor_specific tpm_tis_i2c = {
|
||||
.status = tpm_tis_i2c_status,
|
||||
.recv = tpm_tis_i2c_recv,
|
||||
.send = tpm_tis_i2c_send,
|
||||
.cancel = tpm_tis_i2c_ready,
|
||||
.req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID,
|
||||
.req_canceled = TPM_STS_COMMAND_READY,
|
||||
.attr_group = &tis_attr_grp,
|
||||
.miscdev.fops = &tis_ops,
|
||||
};
|
||||
|
||||
static int __devinit tpm_tis_i2c_init(struct device *dev)
|
||||
{
|
||||
u32 vendor;
|
||||
int rc = 0;
|
||||
struct tpm_chip *chip;
|
||||
|
||||
chip = tpm_register_hardware(dev, &tpm_tis_i2c);
|
||||
if (!chip) {
|
||||
rc = -ENODEV;
|
||||
goto out_err;
|
||||
}
|
||||
|
||||
/* Disable interrupts */
|
||||
chip->vendor.irq = 0;
|
||||
|
||||
/* Default timeouts */
|
||||
chip->vendor.timeout_a = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_b = msecs_to_jiffies(TIS_LONG_TIMEOUT);
|
||||
chip->vendor.timeout_c = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
chip->vendor.timeout_d = msecs_to_jiffies(TIS_SHORT_TIMEOUT);
|
||||
|
||||
if (request_locality(chip, 0) != 0) {
|
||||
rc = -ENODEV;
|
||||
goto out_vendor;
|
||||
}
|
||||
|
||||
/* read four bytes from DID_VID register */
|
||||
if (iic_tpm_read(TPM_DID_VID(0), (u8 *)&vendor, 4) < 0) {
|
||||
rc = -EIO;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
/* create DID_VID register value, after swapping to little-endian */
|
||||
vendor = be32_to_cpu((__be32) vendor);
|
||||
|
||||
if (vendor != TPM_TIS_I2C_DID_VID) {
|
||||
rc = -ENODEV;
|
||||
goto out_release;
|
||||
}
|
||||
|
||||
dev_info(dev, "1.2 TPM (device-id 0x%X)\n", vendor >> 16);
|
||||
|
||||
INIT_LIST_HEAD(&chip->vendor.list);
|
||||
tpm_dev.chip = chip;
|
||||
|
||||
tpm_get_timeouts(chip);
|
||||
tpm_do_selftest(chip);
|
||||
|
||||
return 0;
|
||||
|
||||
out_release:
|
||||
release_locality(chip, chip->vendor.locality, 1);
|
||||
|
||||
out_vendor:
|
||||
/* close file handles */
|
||||
tpm_dev_vendor_release(chip);
|
||||
|
||||
/* remove hardware */
|
||||
tpm_remove_hardware(chip->dev);
|
||||
|
||||
/* reset these pointers, otherwise we oops */
|
||||
chip->dev->release = NULL;
|
||||
chip->release = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
out_err:
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct i2c_device_id tpm_tis_i2c_table[] = {
|
||||
{"tpm_i2c_infineon", 0},
|
||||
{},
|
||||
};
|
||||
|
||||
MODULE_DEVICE_TABLE(i2c, tpm_tis_i2c_table);
|
||||
static SIMPLE_DEV_PM_OPS(tpm_tis_i2c_ops, tpm_pm_suspend, tpm_pm_resume);
|
||||
|
||||
static int __devinit tpm_tis_i2c_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int rc;
|
||||
if (tpm_dev.client != NULL)
|
||||
return -EBUSY; /* We only support one client */
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
dev_err(&client->dev,
|
||||
"no algorithms associated to the i2c bus\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
client->driver = &tpm_tis_i2c_driver;
|
||||
tpm_dev.client = client;
|
||||
rc = tpm_tis_i2c_init(&client->dev);
|
||||
if (rc != 0) {
|
||||
client->driver = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
rc = -ENODEV;
|
||||
}
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int __devexit tpm_tis_i2c_remove(struct i2c_client *client)
|
||||
{
|
||||
struct tpm_chip *chip = tpm_dev.chip;
|
||||
release_locality(chip, chip->vendor.locality, 1);
|
||||
|
||||
/* close file handles */
|
||||
tpm_dev_vendor_release(chip);
|
||||
|
||||
/* remove hardware */
|
||||
tpm_remove_hardware(chip->dev);
|
||||
|
||||
/* reset these pointers, otherwise we oops */
|
||||
chip->dev->release = NULL;
|
||||
chip->release = NULL;
|
||||
tpm_dev.client = NULL;
|
||||
dev_set_drvdata(chip->dev, chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver tpm_tis_i2c_driver = {
|
||||
|
||||
.id_table = tpm_tis_i2c_table,
|
||||
.probe = tpm_tis_i2c_probe,
|
||||
.remove = tpm_tis_i2c_remove,
|
||||
.driver = {
|
||||
.name = "tpm_i2c_infineon",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &tpm_tis_i2c_ops,
|
||||
},
|
||||
};
|
||||
|
||||
module_i2c_driver(tpm_tis_i2c_driver);
|
||||
MODULE_AUTHOR("Peter Huewe <peter.huewe@infineon.com>");
|
||||
MODULE_DESCRIPTION("TPM TIS I2C Infineon Driver");
|
||||
MODULE_VERSION("2.1.5");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,749 @@
|
|||
/*
|
||||
* Copyright (C) 2012 IBM Corporation
|
||||
*
|
||||
* Author: Ashley Lai <adlai@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Device driver for TCG/TCPA TPM (trusted platform module).
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/dma-mapping.h>
|
||||
#include <linux/dmapool.h>
|
||||
#include <linux/slab.h>
|
||||
#include <asm/vio.h>
|
||||
#include <asm/irq.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/list.h>
|
||||
#include <linux/spinlock.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/wait.h>
|
||||
#include <asm/prom.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_ibmvtpm.h"
|
||||
|
||||
static const char tpm_ibmvtpm_driver_name[] = "tpm_ibmvtpm";
|
||||
|
||||
static struct vio_device_id tpm_ibmvtpm_device_table[] __devinitdata = {
|
||||
{ "IBM,vtpm", "IBM,vtpm"},
|
||||
{ "", "" }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(vio, tpm_ibmvtpm_device_table);
|
||||
|
||||
DECLARE_WAIT_QUEUE_HEAD(wq);
|
||||
|
||||
/**
|
||||
* ibmvtpm_send_crq - Send a CRQ request
|
||||
* @vdev: vio device struct
|
||||
* @w1: first word
|
||||
* @w2: second word
|
||||
*
|
||||
* Return value:
|
||||
* 0 -Sucess
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_send_crq(struct vio_dev *vdev, u64 w1, u64 w2)
|
||||
{
|
||||
return plpar_hcall_norets(H_SEND_CRQ, vdev->unit_address, w1, w2);
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_get_data - Retrieve ibm vtpm data
|
||||
* @dev: device struct
|
||||
*
|
||||
* Return value:
|
||||
* vtpm device struct
|
||||
*/
|
||||
static struct ibmvtpm_dev *ibmvtpm_get_data(const struct device *dev)
|
||||
{
|
||||
struct tpm_chip *chip = dev_get_drvdata(dev);
|
||||
if (chip)
|
||||
return (struct ibmvtpm_dev *)chip->vendor.data;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_recv - Receive data after send
|
||||
* @chip: tpm chip struct
|
||||
* @buf: buffer to read
|
||||
* count: size of buffer
|
||||
*
|
||||
* Return value:
|
||||
* Number of bytes read
|
||||
*/
|
||||
static int tpm_ibmvtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
u16 len;
|
||||
|
||||
ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
|
||||
|
||||
if (!ibmvtpm->rtce_buf) {
|
||||
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
wait_event_interruptible(wq, ibmvtpm->crq_res.len != 0);
|
||||
|
||||
if (count < ibmvtpm->crq_res.len) {
|
||||
dev_err(ibmvtpm->dev,
|
||||
"Invalid size in recv: count=%ld, crq_size=%d\n",
|
||||
count, ibmvtpm->crq_res.len);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock(&ibmvtpm->rtce_lock);
|
||||
memcpy((void *)buf, (void *)ibmvtpm->rtce_buf, ibmvtpm->crq_res.len);
|
||||
memset(ibmvtpm->rtce_buf, 0, ibmvtpm->crq_res.len);
|
||||
ibmvtpm->crq_res.valid = 0;
|
||||
ibmvtpm->crq_res.msg = 0;
|
||||
len = ibmvtpm->crq_res.len;
|
||||
ibmvtpm->crq_res.len = 0;
|
||||
spin_unlock(&ibmvtpm->rtce_lock);
|
||||
return len;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_send - Send tpm request
|
||||
* @chip: tpm chip struct
|
||||
* @buf: buffer contains data to send
|
||||
* count: size of buffer
|
||||
*
|
||||
* Return value:
|
||||
* Number of bytes sent
|
||||
*/
|
||||
static int tpm_ibmvtpm_send(struct tpm_chip *chip, u8 *buf, size_t count)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *word = (u64 *) &crq;
|
||||
int rc;
|
||||
|
||||
ibmvtpm = (struct ibmvtpm_dev *)chip->vendor.data;
|
||||
|
||||
if (!ibmvtpm->rtce_buf) {
|
||||
dev_err(ibmvtpm->dev, "ibmvtpm device is not ready\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (count > ibmvtpm->rtce_size) {
|
||||
dev_err(ibmvtpm->dev,
|
||||
"Invalid size in send: count=%ld, rtce_size=%d\n",
|
||||
count, ibmvtpm->rtce_size);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
spin_lock(&ibmvtpm->rtce_lock);
|
||||
memcpy((void *)ibmvtpm->rtce_buf, (void *)buf, count);
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_TPM_COMMAND;
|
||||
crq.len = (u16)count;
|
||||
crq.data = ibmvtpm->rtce_dma_handle;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, word[0], word[1]);
|
||||
if (rc != H_SUCCESS) {
|
||||
dev_err(ibmvtpm->dev, "tpm_ibmvtpm_send failed rc=%d\n", rc);
|
||||
rc = 0;
|
||||
} else
|
||||
rc = count;
|
||||
|
||||
spin_unlock(&ibmvtpm->rtce_lock);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static void tpm_ibmvtpm_cancel(struct tpm_chip *chip)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static u8 tpm_ibmvtpm_status(struct tpm_chip *chip)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_get_rtce_size - Send a CRQ request to get rtce size
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_get_rtce_size(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *buf = (u64 *) &crq;
|
||||
int rc;
|
||||
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_GET_RTCE_BUFFER_SIZE;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_get_rtce_size failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_get_version - Send a CRQ request to get vtpm version
|
||||
* - Note that this is vtpm version and not tpm version
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_get_version(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *buf = (u64 *) &crq;
|
||||
int rc;
|
||||
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_GET_VERSION;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_get_version failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_send_init_complete - Send a CRQ initialize complete message
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_send_init_complete(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_COMP_CMD, 0);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_send_init_complete failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_send_init - Send a CRQ initialize message
|
||||
* @ibmvtpm: vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_crq_send_init(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, INIT_CRQ_CMD, 0);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"ibmvtpm_crq_send_init failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_remove - ibm vtpm remove entry point
|
||||
* @vdev: vio device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0
|
||||
*/
|
||||
static int __devexit tpm_ibmvtpm_remove(struct vio_dev *vdev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
|
||||
int rc = 0;
|
||||
|
||||
free_irq(vdev->irq, ibmvtpm);
|
||||
tasklet_kill(&ibmvtpm->tasklet);
|
||||
|
||||
do {
|
||||
if (rc)
|
||||
msleep(100);
|
||||
rc = plpar_hcall_norets(H_FREE_CRQ, vdev->unit_address);
|
||||
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
|
||||
|
||||
dma_unmap_single(ibmvtpm->dev, ibmvtpm->crq_dma_handle,
|
||||
CRQ_RES_BUF_SIZE, DMA_BIDIRECTIONAL);
|
||||
free_page((unsigned long)ibmvtpm->crq_queue.crq_addr);
|
||||
|
||||
if (ibmvtpm->rtce_buf) {
|
||||
dma_unmap_single(ibmvtpm->dev, ibmvtpm->rtce_dma_handle,
|
||||
ibmvtpm->rtce_size, DMA_BIDIRECTIONAL);
|
||||
kfree(ibmvtpm->rtce_buf);
|
||||
}
|
||||
|
||||
tpm_remove_hardware(ibmvtpm->dev);
|
||||
|
||||
kfree(ibmvtpm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_get_desired_dma - Get DMA size needed by this driver
|
||||
* @vdev: vio device struct
|
||||
*
|
||||
* Return value:
|
||||
* Number of bytes the driver needs to DMA map
|
||||
*/
|
||||
static unsigned long tpm_ibmvtpm_get_desired_dma(struct vio_dev *vdev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(&vdev->dev);
|
||||
return CRQ_RES_BUF_SIZE + ibmvtpm->rtce_size;
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_suspend - Suspend
|
||||
* @dev: device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0
|
||||
*/
|
||||
static int tpm_ibmvtpm_suspend(struct device *dev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
|
||||
struct ibmvtpm_crq crq;
|
||||
u64 *buf = (u64 *) &crq;
|
||||
int rc = 0;
|
||||
|
||||
crq.valid = (u8)IBMVTPM_VALID_CMD;
|
||||
crq.msg = (u8)VTPM_PREPARE_TO_SUSPEND;
|
||||
|
||||
rc = ibmvtpm_send_crq(ibmvtpm->vdev, buf[0], buf[1]);
|
||||
if (rc != H_SUCCESS)
|
||||
dev_err(ibmvtpm->dev,
|
||||
"tpm_ibmvtpm_suspend failed rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_reset_crq - Reset CRQ
|
||||
* @ibmvtpm: ibm vtpm struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int ibmvtpm_reset_crq(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
do {
|
||||
if (rc)
|
||||
msleep(100);
|
||||
rc = plpar_hcall_norets(H_FREE_CRQ,
|
||||
ibmvtpm->vdev->unit_address);
|
||||
} while (rc == H_BUSY || H_IS_LONG_BUSY(rc));
|
||||
|
||||
memset(ibmvtpm->crq_queue.crq_addr, 0, CRQ_RES_BUF_SIZE);
|
||||
ibmvtpm->crq_queue.index = 0;
|
||||
|
||||
return plpar_hcall_norets(H_REG_CRQ, ibmvtpm->vdev->unit_address,
|
||||
ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_resume - Resume from suspend
|
||||
* @dev: device struct
|
||||
*
|
||||
* Return value:
|
||||
* 0
|
||||
*/
|
||||
static int tpm_ibmvtpm_resume(struct device *dev)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = ibmvtpm_get_data(dev);
|
||||
unsigned long flags;
|
||||
int rc = 0;
|
||||
|
||||
do {
|
||||
if (rc)
|
||||
msleep(100);
|
||||
rc = plpar_hcall_norets(H_ENABLE_CRQ,
|
||||
ibmvtpm->vdev->unit_address);
|
||||
} while (rc == H_IN_PROGRESS || rc == H_BUSY || H_IS_LONG_BUSY(rc));
|
||||
|
||||
if (rc) {
|
||||
dev_err(dev, "Error enabling ibmvtpm rc=%d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&ibmvtpm->lock, flags);
|
||||
vio_disable_interrupts(ibmvtpm->vdev);
|
||||
tasklet_schedule(&ibmvtpm->tasklet);
|
||||
spin_unlock_irqrestore(&ibmvtpm->lock, flags);
|
||||
|
||||
rc = ibmvtpm_crq_send_init(ibmvtpm);
|
||||
if (rc)
|
||||
dev_err(dev, "Error send_init rc=%d\n", rc);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations ibmvtpm_ops = {
|
||||
.owner = THIS_MODULE,
|
||||
.llseek = no_llseek,
|
||||
.open = tpm_open,
|
||||
.read = tpm_read,
|
||||
.write = tpm_write,
|
||||
.release = tpm_release,
|
||||
};
|
||||
|
||||
static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL);
|
||||
static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL);
|
||||
static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL);
|
||||
static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL);
|
||||
static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL);
|
||||
static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated,
|
||||
NULL);
|
||||
static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps_1_2, NULL);
|
||||
static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel);
|
||||
static DEVICE_ATTR(durations, S_IRUGO, tpm_show_durations, NULL);
|
||||
static DEVICE_ATTR(timeouts, S_IRUGO, tpm_show_timeouts, NULL);
|
||||
|
||||
static struct attribute *ibmvtpm_attrs[] = {
|
||||
&dev_attr_pubek.attr,
|
||||
&dev_attr_pcrs.attr,
|
||||
&dev_attr_enabled.attr,
|
||||
&dev_attr_active.attr,
|
||||
&dev_attr_owned.attr,
|
||||
&dev_attr_temp_deactivated.attr,
|
||||
&dev_attr_caps.attr,
|
||||
&dev_attr_cancel.attr,
|
||||
&dev_attr_durations.attr,
|
||||
&dev_attr_timeouts.attr, NULL,
|
||||
};
|
||||
|
||||
static struct attribute_group ibmvtpm_attr_grp = { .attrs = ibmvtpm_attrs };
|
||||
|
||||
static const struct tpm_vendor_specific tpm_ibmvtpm = {
|
||||
.recv = tpm_ibmvtpm_recv,
|
||||
.send = tpm_ibmvtpm_send,
|
||||
.cancel = tpm_ibmvtpm_cancel,
|
||||
.status = tpm_ibmvtpm_status,
|
||||
.req_complete_mask = 0,
|
||||
.req_complete_val = 0,
|
||||
.req_canceled = 0,
|
||||
.attr_group = &ibmvtpm_attr_grp,
|
||||
.miscdev = { .fops = &ibmvtpm_ops, },
|
||||
};
|
||||
|
||||
static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = {
|
||||
.suspend = tpm_ibmvtpm_suspend,
|
||||
.resume = tpm_ibmvtpm_resume,
|
||||
};
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_get_next - Get next responded crq
|
||||
* @ibmvtpm vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* vtpm crq pointer
|
||||
*/
|
||||
static struct ibmvtpm_crq *ibmvtpm_crq_get_next(struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
struct ibmvtpm_crq_queue *crq_q = &ibmvtpm->crq_queue;
|
||||
struct ibmvtpm_crq *crq = &crq_q->crq_addr[crq_q->index];
|
||||
|
||||
if (crq->valid & VTPM_MSG_RES) {
|
||||
if (++crq_q->index == crq_q->num_entry)
|
||||
crq_q->index = 0;
|
||||
rmb();
|
||||
} else
|
||||
crq = NULL;
|
||||
return crq;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_crq_process - Process responded crq
|
||||
* @crq crq to be processed
|
||||
* @ibmvtpm vtpm device struct
|
||||
*
|
||||
* Return value:
|
||||
* Nothing
|
||||
*/
|
||||
static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq,
|
||||
struct ibmvtpm_dev *ibmvtpm)
|
||||
{
|
||||
int rc = 0;
|
||||
|
||||
switch (crq->valid) {
|
||||
case VALID_INIT_CRQ:
|
||||
switch (crq->msg) {
|
||||
case INIT_CRQ_RES:
|
||||
dev_info(ibmvtpm->dev, "CRQ initialized\n");
|
||||
rc = ibmvtpm_crq_send_init_complete(ibmvtpm);
|
||||
if (rc)
|
||||
dev_err(ibmvtpm->dev, "Unable to send CRQ init complete rc=%d\n", rc);
|
||||
return;
|
||||
case INIT_CRQ_COMP_RES:
|
||||
dev_info(ibmvtpm->dev,
|
||||
"CRQ initialization completed\n");
|
||||
return;
|
||||
default:
|
||||
dev_err(ibmvtpm->dev, "Unknown crq message type: %d\n", crq->msg);
|
||||
return;
|
||||
}
|
||||
return;
|
||||
case IBMVTPM_VALID_CMD:
|
||||
switch (crq->msg) {
|
||||
case VTPM_GET_RTCE_BUFFER_SIZE_RES:
|
||||
if (crq->len <= 0) {
|
||||
dev_err(ibmvtpm->dev, "Invalid rtce size\n");
|
||||
return;
|
||||
}
|
||||
ibmvtpm->rtce_size = crq->len;
|
||||
ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size,
|
||||
GFP_KERNEL);
|
||||
if (!ibmvtpm->rtce_buf) {
|
||||
dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n");
|
||||
return;
|
||||
}
|
||||
|
||||
ibmvtpm->rtce_dma_handle = dma_map_single(ibmvtpm->dev,
|
||||
ibmvtpm->rtce_buf, ibmvtpm->rtce_size,
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
if (dma_mapping_error(ibmvtpm->dev,
|
||||
ibmvtpm->rtce_dma_handle)) {
|
||||
kfree(ibmvtpm->rtce_buf);
|
||||
ibmvtpm->rtce_buf = NULL;
|
||||
dev_err(ibmvtpm->dev, "Failed to dma map rtce buffer\n");
|
||||
}
|
||||
|
||||
return;
|
||||
case VTPM_GET_VERSION_RES:
|
||||
ibmvtpm->vtpm_version = crq->data;
|
||||
return;
|
||||
case VTPM_TPM_COMMAND_RES:
|
||||
ibmvtpm->crq_res.valid = crq->valid;
|
||||
ibmvtpm->crq_res.msg = crq->msg;
|
||||
ibmvtpm->crq_res.len = crq->len;
|
||||
ibmvtpm->crq_res.data = crq->data;
|
||||
wake_up_interruptible(&wq);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_interrupt - Interrupt handler
|
||||
* @irq: irq number to handle
|
||||
* @vtpm_instance: vtpm that received interrupt
|
||||
*
|
||||
* Returns:
|
||||
* IRQ_HANDLED
|
||||
**/
|
||||
static irqreturn_t ibmvtpm_interrupt(int irq, void *vtpm_instance)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = (struct ibmvtpm_dev *) vtpm_instance;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ibmvtpm->lock, flags);
|
||||
vio_disable_interrupts(ibmvtpm->vdev);
|
||||
tasklet_schedule(&ibmvtpm->tasklet);
|
||||
spin_unlock_irqrestore(&ibmvtpm->lock, flags);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_tasklet - Interrupt handler tasklet
|
||||
* @data: ibm vtpm device struct
|
||||
*
|
||||
* Returns:
|
||||
* Nothing
|
||||
**/
|
||||
static void ibmvtpm_tasklet(void *data)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm = data;
|
||||
struct ibmvtpm_crq *crq;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&ibmvtpm->lock, flags);
|
||||
while ((crq = ibmvtpm_crq_get_next(ibmvtpm)) != NULL) {
|
||||
ibmvtpm_crq_process(crq, ibmvtpm);
|
||||
crq->valid = 0;
|
||||
wmb();
|
||||
}
|
||||
|
||||
vio_enable_interrupts(ibmvtpm->vdev);
|
||||
spin_unlock_irqrestore(&ibmvtpm->lock, flags);
|
||||
}
|
||||
|
||||
/**
|
||||
* tpm_ibmvtpm_probe - ibm vtpm initialize entry point
|
||||
* @vio_dev: vio device struct
|
||||
* @id: vio device id struct
|
||||
*
|
||||
* Return value:
|
||||
* 0 - Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int __devinit tpm_ibmvtpm_probe(struct vio_dev *vio_dev,
|
||||
const struct vio_device_id *id)
|
||||
{
|
||||
struct ibmvtpm_dev *ibmvtpm;
|
||||
struct device *dev = &vio_dev->dev;
|
||||
struct ibmvtpm_crq_queue *crq_q;
|
||||
struct tpm_chip *chip;
|
||||
int rc = -ENOMEM, rc1;
|
||||
|
||||
chip = tpm_register_hardware(dev, &tpm_ibmvtpm);
|
||||
if (!chip) {
|
||||
dev_err(dev, "tpm_register_hardware failed\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
ibmvtpm = kzalloc(sizeof(struct ibmvtpm_dev), GFP_KERNEL);
|
||||
if (!ibmvtpm) {
|
||||
dev_err(dev, "kzalloc for ibmvtpm failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
crq_q = &ibmvtpm->crq_queue;
|
||||
crq_q->crq_addr = (struct ibmvtpm_crq *)get_zeroed_page(GFP_KERNEL);
|
||||
if (!crq_q->crq_addr) {
|
||||
dev_err(dev, "Unable to allocate memory for crq_addr\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
crq_q->num_entry = CRQ_RES_BUF_SIZE / sizeof(*crq_q->crq_addr);
|
||||
ibmvtpm->crq_dma_handle = dma_map_single(dev, crq_q->crq_addr,
|
||||
CRQ_RES_BUF_SIZE,
|
||||
DMA_BIDIRECTIONAL);
|
||||
|
||||
if (dma_mapping_error(dev, ibmvtpm->crq_dma_handle)) {
|
||||
dev_err(dev, "dma mapping failed\n");
|
||||
goto cleanup;
|
||||
}
|
||||
|
||||
rc = plpar_hcall_norets(H_REG_CRQ, vio_dev->unit_address,
|
||||
ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE);
|
||||
if (rc == H_RESOURCE)
|
||||
rc = ibmvtpm_reset_crq(ibmvtpm);
|
||||
|
||||
if (rc) {
|
||||
dev_err(dev, "Unable to register CRQ rc=%d\n", rc);
|
||||
goto reg_crq_cleanup;
|
||||
}
|
||||
|
||||
tasklet_init(&ibmvtpm->tasklet, (void *)ibmvtpm_tasklet,
|
||||
(unsigned long)ibmvtpm);
|
||||
|
||||
rc = request_irq(vio_dev->irq, ibmvtpm_interrupt, 0,
|
||||
tpm_ibmvtpm_driver_name, ibmvtpm);
|
||||
if (rc) {
|
||||
dev_err(dev, "Error %d register irq 0x%x\n", rc, vio_dev->irq);
|
||||
goto init_irq_cleanup;
|
||||
}
|
||||
|
||||
rc = vio_enable_interrupts(vio_dev);
|
||||
if (rc) {
|
||||
dev_err(dev, "Error %d enabling interrupts\n", rc);
|
||||
goto init_irq_cleanup;
|
||||
}
|
||||
|
||||
crq_q->index = 0;
|
||||
|
||||
ibmvtpm->dev = dev;
|
||||
ibmvtpm->vdev = vio_dev;
|
||||
chip->vendor.data = (void *)ibmvtpm;
|
||||
|
||||
spin_lock_init(&ibmvtpm->lock);
|
||||
spin_lock_init(&ibmvtpm->rtce_lock);
|
||||
|
||||
rc = ibmvtpm_crq_send_init(ibmvtpm);
|
||||
if (rc)
|
||||
goto init_irq_cleanup;
|
||||
|
||||
rc = ibmvtpm_crq_get_version(ibmvtpm);
|
||||
if (rc)
|
||||
goto init_irq_cleanup;
|
||||
|
||||
rc = ibmvtpm_crq_get_rtce_size(ibmvtpm);
|
||||
if (rc)
|
||||
goto init_irq_cleanup;
|
||||
|
||||
return rc;
|
||||
init_irq_cleanup:
|
||||
tasklet_kill(&ibmvtpm->tasklet);
|
||||
do {
|
||||
rc1 = plpar_hcall_norets(H_FREE_CRQ, vio_dev->unit_address);
|
||||
} while (rc1 == H_BUSY || H_IS_LONG_BUSY(rc1));
|
||||
reg_crq_cleanup:
|
||||
dma_unmap_single(dev, ibmvtpm->crq_dma_handle, CRQ_RES_BUF_SIZE,
|
||||
DMA_BIDIRECTIONAL);
|
||||
cleanup:
|
||||
if (ibmvtpm) {
|
||||
if (crq_q->crq_addr)
|
||||
free_page((unsigned long)crq_q->crq_addr);
|
||||
kfree(ibmvtpm);
|
||||
}
|
||||
|
||||
tpm_remove_hardware(dev);
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct vio_driver ibmvtpm_driver = {
|
||||
.id_table = tpm_ibmvtpm_device_table,
|
||||
.probe = tpm_ibmvtpm_probe,
|
||||
.remove = tpm_ibmvtpm_remove,
|
||||
.get_desired_dma = tpm_ibmvtpm_get_desired_dma,
|
||||
.name = tpm_ibmvtpm_driver_name,
|
||||
.pm = &tpm_ibmvtpm_pm_ops,
|
||||
};
|
||||
|
||||
/**
|
||||
* ibmvtpm_module_init - Initialize ibm vtpm module
|
||||
*
|
||||
* Return value:
|
||||
* 0 -Success
|
||||
* Non-zero - Failure
|
||||
*/
|
||||
static int __init ibmvtpm_module_init(void)
|
||||
{
|
||||
return vio_register_driver(&ibmvtpm_driver);
|
||||
}
|
||||
|
||||
/**
|
||||
* ibmvtpm_module_exit - Teardown ibm vtpm module
|
||||
*
|
||||
* Return value:
|
||||
* Nothing
|
||||
*/
|
||||
static void __exit ibmvtpm_module_exit(void)
|
||||
{
|
||||
vio_unregister_driver(&ibmvtpm_driver);
|
||||
}
|
||||
|
||||
module_init(ibmvtpm_module_init);
|
||||
module_exit(ibmvtpm_module_exit);
|
||||
|
||||
MODULE_AUTHOR("adlai@us.ibm.com");
|
||||
MODULE_DESCRIPTION("IBM vTPM Driver");
|
||||
MODULE_VERSION("1.0");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) 2012 IBM Corporation
|
||||
*
|
||||
* Author: Ashley Lai <adlai@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Device driver for TCG/TCPA TPM (trusted platform module).
|
||||
* Specifications at www.trustedcomputinggroup.org
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, version 2 of the
|
||||
* License.
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __TPM_IBMVTPM_H__
|
||||
#define __TPM_IBMVTPM_H__
|
||||
|
||||
/* vTPM Message Format 1 */
|
||||
struct ibmvtpm_crq {
|
||||
u8 valid;
|
||||
u8 msg;
|
||||
u16 len;
|
||||
u32 data;
|
||||
u64 reserved;
|
||||
} __attribute__((packed, aligned(8)));
|
||||
|
||||
struct ibmvtpm_crq_queue {
|
||||
struct ibmvtpm_crq *crq_addr;
|
||||
u32 index;
|
||||
u32 num_entry;
|
||||
};
|
||||
|
||||
struct ibmvtpm_dev {
|
||||
struct device *dev;
|
||||
struct vio_dev *vdev;
|
||||
struct ibmvtpm_crq_queue crq_queue;
|
||||
dma_addr_t crq_dma_handle;
|
||||
spinlock_t lock;
|
||||
struct tasklet_struct tasklet;
|
||||
u32 rtce_size;
|
||||
void __iomem *rtce_buf;
|
||||
dma_addr_t rtce_dma_handle;
|
||||
spinlock_t rtce_lock;
|
||||
struct ibmvtpm_crq crq_res;
|
||||
u32 vtpm_version;
|
||||
};
|
||||
|
||||
#define CRQ_RES_BUF_SIZE PAGE_SIZE
|
||||
|
||||
/* Initialize CRQ */
|
||||
#define INIT_CRQ_CMD 0xC001000000000000LL /* Init cmd */
|
||||
#define INIT_CRQ_COMP_CMD 0xC002000000000000LL /* Init complete cmd */
|
||||
#define INIT_CRQ_RES 0x01 /* Init respond */
|
||||
#define INIT_CRQ_COMP_RES 0x02 /* Init complete respond */
|
||||
#define VALID_INIT_CRQ 0xC0 /* Valid command for init crq */
|
||||
|
||||
/* vTPM CRQ response is the message type | 0x80 */
|
||||
#define VTPM_MSG_RES 0x80
|
||||
#define IBMVTPM_VALID_CMD 0x80
|
||||
|
||||
/* vTPM CRQ message types */
|
||||
#define VTPM_GET_VERSION 0x01
|
||||
#define VTPM_GET_VERSION_RES (0x01 | VTPM_MSG_RES)
|
||||
|
||||
#define VTPM_TPM_COMMAND 0x02
|
||||
#define VTPM_TPM_COMMAND_RES (0x02 | VTPM_MSG_RES)
|
||||
|
||||
#define VTPM_GET_RTCE_BUFFER_SIZE 0x03
|
||||
#define VTPM_GET_RTCE_BUFFER_SIZE_RES (0x03 | VTPM_MSG_RES)
|
||||
|
||||
#define VTPM_PREPARE_TO_SUSPEND 0x04
|
||||
#define VTPM_PREPARE_TO_SUSPEND_RES (0x04 | VTPM_MSG_RES)
|
||||
|
||||
#endif
|
|
@ -0,0 +1,73 @@
|
|||
/*
|
||||
* Copyright 2012 IBM Corporation
|
||||
*
|
||||
* Author: Ashley Lai <adlai@us.ibm.com>
|
||||
*
|
||||
* Maintained by: <tpmdd-devel@lists.sourceforge.net>
|
||||
*
|
||||
* Read the event log created by the firmware on PPC64
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/slab.h>
|
||||
#include <linux/of.h>
|
||||
|
||||
#include "tpm.h"
|
||||
#include "tpm_eventlog.h"
|
||||
|
||||
int read_log(struct tpm_bios_log *log)
|
||||
{
|
||||
struct device_node *np;
|
||||
const u32 *sizep;
|
||||
const __be64 *basep;
|
||||
|
||||
if (log->bios_event_log != NULL) {
|
||||
pr_err("%s: ERROR - Eventlog already initialized\n", __func__);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
np = of_find_node_by_name(NULL, "ibm,vtpm");
|
||||
if (!np) {
|
||||
pr_err("%s: ERROR - IBMVTPM not supported\n", __func__);
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
sizep = of_get_property(np, "linux,sml-size", NULL);
|
||||
if (sizep == NULL) {
|
||||
pr_err("%s: ERROR - SML size not found\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
if (*sizep == 0) {
|
||||
pr_err("%s: ERROR - event log area empty\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
|
||||
basep = of_get_property(np, "linux,sml-base", NULL);
|
||||
if (basep == NULL) {
|
||||
pr_err(KERN_ERR "%s: ERROR - SML not found\n", __func__);
|
||||
goto cleanup_eio;
|
||||
}
|
||||
|
||||
of_node_put(np);
|
||||
log->bios_event_log = kmalloc(*sizep, GFP_KERNEL);
|
||||
if (!log->bios_event_log) {
|
||||
pr_err("%s: ERROR - Not enough memory for BIOS measurements\n",
|
||||
__func__);
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
log->bios_event_log_end = log->bios_event_log + *sizep;
|
||||
|
||||
memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep);
|
||||
|
||||
return 0;
|
||||
|
||||
cleanup_eio:
|
||||
of_node_put(np);
|
||||
return -EIO;
|
||||
}
|
|
@ -0,0 +1,461 @@
|
|||
#include <linux/acpi.h>
|
||||
#include <acpi/acpi_drivers.h>
|
||||
#include "tpm.h"
|
||||
|
||||
static const u8 tpm_ppi_uuid[] = {
|
||||
0xA6, 0xFA, 0xDD, 0x3D,
|
||||
0x1B, 0x36,
|
||||
0xB4, 0x4E,
|
||||
0xA4, 0x24,
|
||||
0x8D, 0x10, 0x08, 0x9D, 0x16, 0x53
|
||||
};
|
||||
static char *tpm_device_name = "TPM";
|
||||
|
||||
#define TPM_PPI_REVISION_ID 1
|
||||
#define TPM_PPI_FN_VERSION 1
|
||||
#define TPM_PPI_FN_SUBREQ 2
|
||||
#define TPM_PPI_FN_GETREQ 3
|
||||
#define TPM_PPI_FN_GETACT 4
|
||||
#define TPM_PPI_FN_GETRSP 5
|
||||
#define TPM_PPI_FN_SUBREQ2 7
|
||||
#define TPM_PPI_FN_GETOPR 8
|
||||
#define PPI_TPM_REQ_MAX 22
|
||||
#define PPI_VS_REQ_START 128
|
||||
#define PPI_VS_REQ_END 255
|
||||
#define PPI_VERSION_LEN 3
|
||||
|
||||
static acpi_status ppi_callback(acpi_handle handle, u32 level, void *context,
|
||||
void **return_value)
|
||||
{
|
||||
acpi_status status;
|
||||
struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
status = acpi_get_name(handle, ACPI_FULL_PATHNAME, &buffer);
|
||||
if (strstr(buffer.pointer, context) != NULL) {
|
||||
*return_value = handle;
|
||||
kfree(buffer.pointer);
|
||||
return AE_CTRL_TERMINATE;
|
||||
}
|
||||
return AE_OK;
|
||||
}
|
||||
|
||||
static inline void ppi_assign_params(union acpi_object params[4],
|
||||
u64 function_num)
|
||||
{
|
||||
params[0].type = ACPI_TYPE_BUFFER;
|
||||
params[0].buffer.length = sizeof(tpm_ppi_uuid);
|
||||
params[0].buffer.pointer = (char *)tpm_ppi_uuid;
|
||||
params[1].type = ACPI_TYPE_INTEGER;
|
||||
params[1].integer.value = TPM_PPI_REVISION_ID;
|
||||
params[2].type = ACPI_TYPE_INTEGER;
|
||||
params[2].integer.value = function_num;
|
||||
params[3].type = ACPI_TYPE_PACKAGE;
|
||||
params[3].package.count = 0;
|
||||
params[3].package.elements = NULL;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_version(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object *obj;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
obj = (union acpi_object *)output.pointer;
|
||||
status = scnprintf(buf, PAGE_SIZE, "%s\n", obj->string.pointer);
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_request(struct device *dev,
|
||||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object *ret_obj;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_GETREQ);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_PACKAGE);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* output.pointer should be of package type, including two integers.
|
||||
* The first is function return code, 0 means success and 1 means
|
||||
* error. The second is pending TPM operation requested by the OS, 0
|
||||
* means none and >0 means operation value.
|
||||
*/
|
||||
ret_obj = ((union acpi_object *)output.pointer)->package.elements;
|
||||
if (ret_obj->type == ACPI_TYPE_INTEGER) {
|
||||
if (ret_obj->integer.value) {
|
||||
status = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
ret_obj++;
|
||||
if (ret_obj->type == ACPI_TYPE_INTEGER)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu\n",
|
||||
ret_obj->integer.value);
|
||||
else
|
||||
status = -EINVAL;
|
||||
} else {
|
||||
status = -EINVAL;
|
||||
}
|
||||
cleanup:
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_store_ppi_request(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
char version[PPI_VERSION_LEN + 1];
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object obj;
|
||||
u32 req;
|
||||
u64 ret;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
strncpy(version,
|
||||
((union acpi_object *)output.pointer)->string.pointer,
|
||||
PPI_VERSION_LEN);
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
/*
|
||||
* the function to submit TPM operation request to pre-os environment
|
||||
* is updated with function index from SUBREQ to SUBREQ2 since PPI
|
||||
* version 1.1
|
||||
*/
|
||||
if (strcmp(version, "1.1") == -1)
|
||||
params[2].integer.value = TPM_PPI_FN_SUBREQ;
|
||||
else
|
||||
params[2].integer.value = TPM_PPI_FN_SUBREQ2;
|
||||
/*
|
||||
* PPI spec defines params[3].type as ACPI_TYPE_PACKAGE. Some BIOS
|
||||
* accept buffer/string/integer type, but some BIOS accept buffer/
|
||||
* string/package type. For PPI version 1.0 and 1.1, use buffer type
|
||||
* for compatibility, and use package type since 1.2 according to spec.
|
||||
*/
|
||||
if (strcmp(version, "1.2") == -1) {
|
||||
params[3].type = ACPI_TYPE_BUFFER;
|
||||
params[3].buffer.length = sizeof(req);
|
||||
sscanf(buf, "%d", &req);
|
||||
params[3].buffer.pointer = (char *)&req;
|
||||
} else {
|
||||
params[3].package.count = 1;
|
||||
obj.type = ACPI_TYPE_INTEGER;
|
||||
sscanf(buf, "%llu", &obj.integer.value);
|
||||
params[3].package.elements = &obj;
|
||||
}
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_INTEGER);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
ret = ((union acpi_object *)output.pointer)->integer.value;
|
||||
if (ret == 0)
|
||||
status = (acpi_status)count;
|
||||
else if (ret == 1)
|
||||
status = -EPERM;
|
||||
else
|
||||
status = -EFAULT;
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_transition_action(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
char version[PPI_VERSION_LEN + 1];
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
u32 ret;
|
||||
char *info[] = {
|
||||
"None",
|
||||
"Shutdown",
|
||||
"Reboot",
|
||||
"OS Vendor-specific",
|
||||
"Error",
|
||||
};
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
strncpy(version,
|
||||
((union acpi_object *)output.pointer)->string.pointer,
|
||||
PPI_VERSION_LEN);
|
||||
/*
|
||||
* PPI spec defines params[3].type as empty package, but some platforms
|
||||
* (e.g. Capella with PPI 1.0) need integer/string/buffer type, so for
|
||||
* compatibility, define params[3].type as buffer, if PPI version < 1.2
|
||||
*/
|
||||
if (strcmp(version, "1.2") == -1) {
|
||||
params[3].type = ACPI_TYPE_BUFFER;
|
||||
params[3].buffer.length = 0;
|
||||
params[3].buffer.pointer = NULL;
|
||||
}
|
||||
params[2].integer.value = TPM_PPI_FN_GETACT;
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_INTEGER);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
ret = ((union acpi_object *)output.pointer)->integer.value;
|
||||
if (ret < ARRAY_SIZE(info) - 1)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret, info[ret]);
|
||||
else
|
||||
status = scnprintf(buf, PAGE_SIZE, "%d: %s\n", ret,
|
||||
info[ARRAY_SIZE(info)-1]);
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_response(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object *ret_obj;
|
||||
u64 req;
|
||||
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_GETRSP);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_PACKAGE);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
/*
|
||||
* parameter output.pointer should be of package type, including
|
||||
* 3 integers. The first means function return code, the second means
|
||||
* most recent TPM operation request, and the last means response to
|
||||
* the most recent TPM operation request. Only if the first is 0, and
|
||||
* the second integer is not 0, the response makes sense.
|
||||
*/
|
||||
ret_obj = ((union acpi_object *)output.pointer)->package.elements;
|
||||
if (ret_obj->type != ACPI_TYPE_INTEGER) {
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret_obj->integer.value) {
|
||||
status = -EFAULT;
|
||||
goto cleanup;
|
||||
}
|
||||
ret_obj++;
|
||||
if (ret_obj->type != ACPI_TYPE_INTEGER) {
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret_obj->integer.value) {
|
||||
req = ret_obj->integer.value;
|
||||
ret_obj++;
|
||||
if (ret_obj->type != ACPI_TYPE_INTEGER) {
|
||||
status = -EINVAL;
|
||||
goto cleanup;
|
||||
}
|
||||
if (ret_obj->integer.value == 0)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
|
||||
"0: Success");
|
||||
else if (ret_obj->integer.value == 0xFFFFFFF0)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
|
||||
"0xFFFFFFF0: User Abort");
|
||||
else if (ret_obj->integer.value == 0xFFFFFFF1)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %s\n", req,
|
||||
"0xFFFFFFF1: BIOS Failure");
|
||||
else if (ret_obj->integer.value >= 1 &&
|
||||
ret_obj->integer.value <= 0x00000FFF)
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
|
||||
req, ret_obj->integer.value,
|
||||
"Corresponding TPM error");
|
||||
else
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu %llu: %s\n",
|
||||
req, ret_obj->integer.value,
|
||||
"Error");
|
||||
} else {
|
||||
status = scnprintf(buf, PAGE_SIZE, "%llu: %s\n",
|
||||
ret_obj->integer.value, "No Recent Request");
|
||||
}
|
||||
cleanup:
|
||||
kfree(output.pointer);
|
||||
return status;
|
||||
}
|
||||
|
||||
static ssize_t show_ppi_operations(char *buf, u32 start, u32 end)
|
||||
{
|
||||
char *str = buf;
|
||||
char version[PPI_VERSION_LEN];
|
||||
acpi_handle handle;
|
||||
acpi_status status;
|
||||
struct acpi_object_list input;
|
||||
struct acpi_buffer output = { ACPI_ALLOCATE_BUFFER, NULL };
|
||||
union acpi_object params[4];
|
||||
union acpi_object obj;
|
||||
int i;
|
||||
u32 ret;
|
||||
char *info[] = {
|
||||
"Not implemented",
|
||||
"BIOS only",
|
||||
"Blocked for OS by BIOS",
|
||||
"User required",
|
||||
"User not required",
|
||||
};
|
||||
input.count = 4;
|
||||
ppi_assign_params(params, TPM_PPI_FN_VERSION);
|
||||
input.pointer = params;
|
||||
status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
|
||||
ACPI_UINT32_MAX, ppi_callback, NULL,
|
||||
tpm_device_name, &handle);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENXIO;
|
||||
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM", &input, &output,
|
||||
ACPI_TYPE_STRING);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
|
||||
strncpy(version,
|
||||
((union acpi_object *)output.pointer)->string.pointer,
|
||||
PPI_VERSION_LEN);
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
if (strcmp(version, "1.2") == -1)
|
||||
return -EPERM;
|
||||
|
||||
params[2].integer.value = TPM_PPI_FN_GETOPR;
|
||||
params[3].package.count = 1;
|
||||
obj.type = ACPI_TYPE_INTEGER;
|
||||
params[3].package.elements = &obj;
|
||||
for (i = start; i <= end; i++) {
|
||||
obj.integer.value = i;
|
||||
status = acpi_evaluate_object_typed(handle, "_DSM",
|
||||
&input, &output, ACPI_TYPE_INTEGER);
|
||||
if (ACPI_FAILURE(status))
|
||||
return -ENOMEM;
|
||||
|
||||
ret = ((union acpi_object *)output.pointer)->integer.value;
|
||||
if (ret > 0 && ret < ARRAY_SIZE(info))
|
||||
str += scnprintf(str, PAGE_SIZE, "%d %d: %s\n",
|
||||
i, ret, info[ret]);
|
||||
kfree(output.pointer);
|
||||
output.length = ACPI_ALLOCATE_BUFFER;
|
||||
output.pointer = NULL;
|
||||
}
|
||||
return str - buf;
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_tcg_operations(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return show_ppi_operations(buf, 0, PPI_TPM_REQ_MAX);
|
||||
}
|
||||
|
||||
static ssize_t tpm_show_ppi_vs_operations(struct device *dev,
|
||||
struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
return show_ppi_operations(buf, PPI_VS_REQ_START, PPI_VS_REQ_END);
|
||||
}
|
||||
|
||||
static DEVICE_ATTR(version, S_IRUGO, tpm_show_ppi_version, NULL);
|
||||
static DEVICE_ATTR(request, S_IRUGO | S_IWUSR | S_IWGRP,
|
||||
tpm_show_ppi_request, tpm_store_ppi_request);
|
||||
static DEVICE_ATTR(transition_action, S_IRUGO,
|
||||
tpm_show_ppi_transition_action, NULL);
|
||||
static DEVICE_ATTR(response, S_IRUGO, tpm_show_ppi_response, NULL);
|
||||
static DEVICE_ATTR(tcg_operations, S_IRUGO, tpm_show_ppi_tcg_operations, NULL);
|
||||
static DEVICE_ATTR(vs_operations, S_IRUGO, tpm_show_ppi_vs_operations, NULL);
|
||||
|
||||
static struct attribute *ppi_attrs[] = {
|
||||
&dev_attr_version.attr,
|
||||
&dev_attr_request.attr,
|
||||
&dev_attr_transition_action.attr,
|
||||
&dev_attr_response.attr,
|
||||
&dev_attr_tcg_operations.attr,
|
||||
&dev_attr_vs_operations.attr, NULL,
|
||||
};
|
||||
static struct attribute_group ppi_attr_grp = {
|
||||
.attrs = ppi_attrs
|
||||
};
|
||||
|
||||
ssize_t sys_add_ppi(struct kobject *parent)
|
||||
{
|
||||
struct kobject *ppi;
|
||||
ppi = kobject_create_and_add("ppi", parent);
|
||||
if (sysfs_create_group(ppi, &ppi_attr_grp))
|
||||
return -EFAULT;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(sys_add_ppi);
|
||||
|
||||
MODULE_LICENSE("GPL");
|
|
@ -705,6 +705,7 @@ out_err:
|
|||
return rc;
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PNP) || defined(CONFIG_PM_SLEEP)
|
||||
static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
|
||||
{
|
||||
u32 intmask;
|
||||
|
@ -725,7 +726,7 @@ static void tpm_tis_reenable_interrupts(struct tpm_chip *chip)
|
|||
iowrite32(intmask,
|
||||
chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality));
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
static int __devinit tpm_tis_pnp_init(struct pnp_dev *pnp_dev,
|
||||
|
|
|
@ -14,6 +14,7 @@
|
|||
#include <linux/fcntl.h>
|
||||
#include <linux/security.h>
|
||||
#include <linux/evm.h>
|
||||
#include <linux/ima.h>
|
||||
|
||||
/**
|
||||
* inode_change_ok - check if attribute changes to an inode are allowed
|
||||
|
@ -247,6 +248,7 @@ int notify_change(struct dentry * dentry, struct iattr * attr)
|
|||
|
||||
if (!error) {
|
||||
fsnotify_change(dentry, ia_valid);
|
||||
ima_inode_post_setattr(dentry);
|
||||
evm_inode_post_setattr(dentry, ia_valid);
|
||||
}
|
||||
|
||||
|
|
|
@ -243,10 +243,10 @@ static void __fput(struct file *file)
|
|||
if (file->f_op && file->f_op->fasync)
|
||||
file->f_op->fasync(-1, file, 0);
|
||||
}
|
||||
ima_file_free(file);
|
||||
if (file->f_op && file->f_op->release)
|
||||
file->f_op->release(inode, file);
|
||||
security_file_free(file);
|
||||
ima_file_free(file);
|
||||
if (unlikely(S_ISCHR(inode->i_mode) && inode->i_cdev != NULL &&
|
||||
!(file->f_mode & FMODE_PATH))) {
|
||||
cdev_put(inode->i_cdev);
|
||||
|
|
10
fs/xattr.c
10
fs/xattr.c
|
@ -296,11 +296,13 @@ vfs_removexattr(struct dentry *dentry, const char *name)
|
|||
if (error)
|
||||
return error;
|
||||
|
||||
error = security_inode_removexattr(dentry, name);
|
||||
if (error)
|
||||
return error;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
error = security_inode_removexattr(dentry, name);
|
||||
if (error) {
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return error;
|
||||
}
|
||||
|
||||
error = inode->i_op->removexattr(dentry, name);
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
|
||||
|
|
|
@ -531,6 +531,7 @@ extern int audit_set_loginuid(kuid_t loginuid);
|
|||
#define audit_get_loginuid(t) ((t)->loginuid)
|
||||
#define audit_get_sessionid(t) ((t)->sessionid)
|
||||
extern void audit_log_task_context(struct audit_buffer *ab);
|
||||
extern void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk);
|
||||
extern void __audit_ipc_obj(struct kern_ipc_perm *ipcp);
|
||||
extern void __audit_ipc_set_perm(unsigned long qbytes, uid_t uid, gid_t gid, umode_t mode);
|
||||
extern int __audit_bprm(struct linux_binprm *bprm);
|
||||
|
@ -642,6 +643,7 @@ extern int audit_signals;
|
|||
#define audit_get_loginuid(t) (INVALID_UID)
|
||||
#define audit_get_sessionid(t) (-1)
|
||||
#define audit_log_task_context(b) do { ; } while (0)
|
||||
#define audit_log_task_info(b, t) do { ; } while (0)
|
||||
#define audit_ipc_obj(i) ((void)0)
|
||||
#define audit_ipc_set_perm(q,u,g,m) ((void)0)
|
||||
#define audit_bprm(p) ({ 0; })
|
||||
|
|
|
@ -39,5 +39,32 @@ static inline int ima_file_mmap(struct file *file, unsigned long prot)
|
|||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
#endif /* CONFIG_IMA_H */
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
extern void ima_inode_post_setattr(struct dentry *dentry);
|
||||
extern int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len);
|
||||
extern int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name);
|
||||
#else
|
||||
static inline void ima_inode_post_setattr(struct dentry *dentry)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
static inline int ima_inode_setxattr(struct dentry *dentry,
|
||||
const char *xattr_name,
|
||||
const void *xattr_value,
|
||||
size_t xattr_value_len)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int ima_inode_removexattr(struct dentry *dentry,
|
||||
const char *xattr_name)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif /* CONFIG_IMA_APPRAISE_H */
|
||||
#endif /* _LINUX_IMA_H */
|
||||
|
|
|
@ -22,13 +22,14 @@ enum integrity_status {
|
|||
|
||||
/* List of EVM protected security xattrs */
|
||||
#ifdef CONFIG_INTEGRITY
|
||||
extern int integrity_inode_alloc(struct inode *inode);
|
||||
extern struct integrity_iint_cache *integrity_inode_get(struct inode *inode);
|
||||
extern void integrity_inode_free(struct inode *inode);
|
||||
|
||||
#else
|
||||
static inline int integrity_inode_alloc(struct inode *inode)
|
||||
static inline struct integrity_iint_cache *
|
||||
integrity_inode_get(struct inode *inode)
|
||||
{
|
||||
return 0;
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static inline void integrity_inode_free(struct inode *inode)
|
||||
|
|
|
@ -130,8 +130,6 @@ extern void exit_ptrace(struct task_struct *tracer);
|
|||
#define PTRACE_MODE_READ 0x01
|
||||
#define PTRACE_MODE_ATTACH 0x02
|
||||
#define PTRACE_MODE_NOAUDIT 0x04
|
||||
/* Returns 0 on success, -errno on denial. */
|
||||
extern int __ptrace_may_access(struct task_struct *task, unsigned int mode);
|
||||
/* Returns true on success, false on denial. */
|
||||
extern bool ptrace_may_access(struct task_struct *task, unsigned int mode);
|
||||
|
||||
|
|
|
@ -3022,5 +3022,36 @@ static inline void free_secdata(void *secdata)
|
|||
{ }
|
||||
#endif /* CONFIG_SECURITY */
|
||||
|
||||
#ifdef CONFIG_SECURITY_YAMA
|
||||
extern int yama_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode);
|
||||
extern int yama_ptrace_traceme(struct task_struct *parent);
|
||||
extern void yama_task_free(struct task_struct *task);
|
||||
extern int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5);
|
||||
#else
|
||||
static inline int yama_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int yama_ptrace_traceme(struct task_struct *parent)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void yama_task_free(struct task_struct *task)
|
||||
{
|
||||
}
|
||||
|
||||
static inline int yama_task_prctl(int option, unsigned long arg2,
|
||||
unsigned long arg3, unsigned long arg4,
|
||||
unsigned long arg5)
|
||||
{
|
||||
return -ENOSYS;
|
||||
}
|
||||
#endif /* CONFIG_SECURITY_YAMA */
|
||||
|
||||
#endif /* ! __LINUX_SECURITY_H */
|
||||
|
||||
|
|
|
@ -32,6 +32,7 @@
|
|||
extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf);
|
||||
extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash);
|
||||
extern int tpm_send(u32 chip_num, void *cmd, size_t buflen);
|
||||
extern int tpm_get_random(u32 chip_num, u8 *data, size_t max);
|
||||
#else
|
||||
static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) {
|
||||
return -ENODEV;
|
||||
|
@ -42,5 +43,8 @@ static inline int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash) {
|
|||
static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) {
|
||||
return -ENODEV;
|
||||
}
|
||||
static inline int tpm_get_random(u32 chip_num, u8 *data, size_t max) {
|
||||
return -ENODEV;
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
#define XATTR_EVM_SUFFIX "evm"
|
||||
#define XATTR_NAME_EVM XATTR_SECURITY_PREFIX XATTR_EVM_SUFFIX
|
||||
|
||||
#define XATTR_IMA_SUFFIX "ima"
|
||||
#define XATTR_NAME_IMA XATTR_SECURITY_PREFIX XATTR_IMA_SUFFIX
|
||||
|
||||
#define XATTR_SELINUX_SUFFIX "selinux"
|
||||
#define XATTR_NAME_SELINUX XATTR_SECURITY_PREFIX XATTR_SELINUX_SUFFIX
|
||||
|
||||
|
|
|
@ -1146,13 +1146,44 @@ error_path:
|
|||
|
||||
EXPORT_SYMBOL(audit_log_task_context);
|
||||
|
||||
static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
|
||||
void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk)
|
||||
{
|
||||
const struct cred *cred;
|
||||
char name[sizeof(tsk->comm)];
|
||||
struct mm_struct *mm = tsk->mm;
|
||||
struct vm_area_struct *vma;
|
||||
char *tty;
|
||||
|
||||
if (!ab)
|
||||
return;
|
||||
|
||||
/* tsk == current */
|
||||
cred = current_cred();
|
||||
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
|
||||
tty = tsk->signal->tty->name;
|
||||
else
|
||||
tty = "(none)";
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
|
||||
|
||||
audit_log_format(ab,
|
||||
" ppid=%ld pid=%d auid=%u uid=%u gid=%u"
|
||||
" euid=%u suid=%u fsuid=%u"
|
||||
" egid=%u sgid=%u fsgid=%u ses=%u tty=%s",
|
||||
sys_getppid(),
|
||||
tsk->pid,
|
||||
from_kuid(&init_user_ns, tsk->loginuid),
|
||||
from_kuid(&init_user_ns, cred->uid),
|
||||
from_kgid(&init_user_ns, cred->gid),
|
||||
from_kuid(&init_user_ns, cred->euid),
|
||||
from_kuid(&init_user_ns, cred->suid),
|
||||
from_kuid(&init_user_ns, cred->fsuid),
|
||||
from_kgid(&init_user_ns, cred->egid),
|
||||
from_kgid(&init_user_ns, cred->sgid),
|
||||
from_kgid(&init_user_ns, cred->fsgid),
|
||||
tsk->sessionid, tty);
|
||||
|
||||
get_task_comm(name, tsk);
|
||||
audit_log_format(ab, " comm=");
|
||||
|
@ -1175,6 +1206,8 @@ static void audit_log_task_info(struct audit_buffer *ab, struct task_struct *tsk
|
|||
audit_log_task_context(ab);
|
||||
}
|
||||
|
||||
EXPORT_SYMBOL(audit_log_task_info);
|
||||
|
||||
static int audit_log_pid_context(struct audit_context *context, pid_t pid,
|
||||
kuid_t auid, kuid_t uid, unsigned int sessionid,
|
||||
u32 sid, char *comm)
|
||||
|
@ -1580,26 +1613,12 @@ static void audit_log_name(struct audit_context *context, struct audit_names *n,
|
|||
|
||||
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
|
||||
{
|
||||
const struct cred *cred;
|
||||
int i, call_panic = 0;
|
||||
struct audit_buffer *ab;
|
||||
struct audit_aux_data *aux;
|
||||
const char *tty;
|
||||
struct audit_names *n;
|
||||
|
||||
/* tsk == current */
|
||||
context->pid = tsk->pid;
|
||||
if (!context->ppid)
|
||||
context->ppid = sys_getppid();
|
||||
cred = current_cred();
|
||||
context->uid = cred->uid;
|
||||
context->gid = cred->gid;
|
||||
context->euid = cred->euid;
|
||||
context->suid = cred->suid;
|
||||
context->fsuid = cred->fsuid;
|
||||
context->egid = cred->egid;
|
||||
context->sgid = cred->sgid;
|
||||
context->fsgid = cred->fsgid;
|
||||
context->personality = tsk->personality;
|
||||
|
||||
ab = audit_log_start(context, GFP_KERNEL, AUDIT_SYSCALL);
|
||||
|
@ -1614,37 +1633,13 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
|
|||
(context->return_valid==AUDITSC_SUCCESS)?"yes":"no",
|
||||
context->return_code);
|
||||
|
||||
spin_lock_irq(&tsk->sighand->siglock);
|
||||
if (tsk->signal && tsk->signal->tty && tsk->signal->tty->name)
|
||||
tty = tsk->signal->tty->name;
|
||||
else
|
||||
tty = "(none)";
|
||||
spin_unlock_irq(&tsk->sighand->siglock);
|
||||
|
||||
audit_log_format(ab,
|
||||
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d"
|
||||
" ppid=%d pid=%d auid=%u uid=%u gid=%u"
|
||||
" euid=%u suid=%u fsuid=%u"
|
||||
" egid=%u sgid=%u fsgid=%u tty=%s ses=%u",
|
||||
context->argv[0],
|
||||
context->argv[1],
|
||||
context->argv[2],
|
||||
context->argv[3],
|
||||
context->name_count,
|
||||
context->ppid,
|
||||
context->pid,
|
||||
from_kuid(&init_user_ns, tsk->loginuid),
|
||||
from_kuid(&init_user_ns, context->uid),
|
||||
from_kgid(&init_user_ns, context->gid),
|
||||
from_kuid(&init_user_ns, context->euid),
|
||||
from_kuid(&init_user_ns, context->suid),
|
||||
from_kuid(&init_user_ns, context->fsuid),
|
||||
from_kgid(&init_user_ns, context->egid),
|
||||
from_kgid(&init_user_ns, context->sgid),
|
||||
from_kgid(&init_user_ns, context->fsgid),
|
||||
tty,
|
||||
tsk->sessionid);
|
||||
|
||||
" a0=%lx a1=%lx a2=%lx a3=%lx items=%d",
|
||||
context->argv[0],
|
||||
context->argv[1],
|
||||
context->argv[2],
|
||||
context->argv[3],
|
||||
context->name_count);
|
||||
|
||||
audit_log_task_info(ab, tsk);
|
||||
audit_log_key(ab, context->filterkey);
|
||||
|
|
|
@ -180,7 +180,8 @@ static int ptrace_has_cap(struct user_namespace *ns, unsigned int mode)
|
|||
return has_ns_capability(current, ns, CAP_SYS_PTRACE);
|
||||
}
|
||||
|
||||
int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
/* Returns 0 on success, -errno on denial. */
|
||||
static int __ptrace_may_access(struct task_struct *task, unsigned int mode)
|
||||
{
|
||||
const struct cred *cred = current_cred(), *tcred;
|
||||
|
||||
|
|
|
@ -18,14 +18,22 @@ HOSTCFLAGS_bpf-direct.o += -idirafter $(objtree)/include
|
|||
bpf-direct-objs := bpf-direct.o
|
||||
|
||||
# Try to match the kernel target.
|
||||
ifeq ($(CONFIG_64BIT),)
|
||||
HOSTCFLAGS_bpf-direct.o += -m32
|
||||
HOSTCFLAGS_dropper.o += -m32
|
||||
HOSTCFLAGS_bpf-helper.o += -m32
|
||||
HOSTCFLAGS_bpf-fancy.o += -m32
|
||||
HOSTLOADLIBES_bpf-direct += -m32
|
||||
HOSTLOADLIBES_bpf-fancy += -m32
|
||||
HOSTLOADLIBES_dropper += -m32
|
||||
ifndef CONFIG_64BIT
|
||||
|
||||
# s390 has -m31 flag to build 31 bit binaries
|
||||
ifndef CONFIG_S390
|
||||
MFLAG = -m32
|
||||
else
|
||||
MFLAG = -m31
|
||||
endif
|
||||
|
||||
HOSTCFLAGS_bpf-direct.o += $(MFLAG)
|
||||
HOSTCFLAGS_dropper.o += $(MFLAG)
|
||||
HOSTCFLAGS_bpf-helper.o += $(MFLAG)
|
||||
HOSTCFLAGS_bpf-fancy.o += $(MFLAG)
|
||||
HOSTLOADLIBES_bpf-direct += $(MFLAG)
|
||||
HOSTLOADLIBES_bpf-fancy += $(MFLAG)
|
||||
HOSTLOADLIBES_dropper += $(MFLAG)
|
||||
endif
|
||||
|
||||
# Tell kbuild to always build the programs
|
||||
|
|
|
@ -59,6 +59,16 @@ void seccomp_bpf_print(struct sock_filter *filter, size_t count);
|
|||
#define FIND_LABEL(labels, label) seccomp_bpf_label((labels), #label)
|
||||
|
||||
#define EXPAND(...) __VA_ARGS__
|
||||
|
||||
/* Ensure that we load the logically correct offset. */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
|
||||
#else
|
||||
#error "Unknown endianness"
|
||||
#endif
|
||||
|
||||
/* Map all width-sensitive operations */
|
||||
#if __BITS_PER_LONG == 32
|
||||
|
||||
|
@ -70,21 +80,16 @@ void seccomp_bpf_print(struct sock_filter *filter, size_t count);
|
|||
#define JLE(x, jt) JLE32(x, EXPAND(jt))
|
||||
#define JA(x, jt) JA32(x, EXPAND(jt))
|
||||
#define ARG(i) ARG_32(i)
|
||||
#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
|
||||
|
||||
#elif __BITS_PER_LONG == 64
|
||||
|
||||
/* Ensure that we load the logically correct offset. */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define ENDIAN(_lo, _hi) _lo, _hi
|
||||
#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
|
||||
#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define ENDIAN(_lo, _hi) _hi, _lo
|
||||
#define LO_ARG(idx) offsetof(struct seccomp_data, args[(idx)]) + sizeof(__u32)
|
||||
#define HI_ARG(idx) offsetof(struct seccomp_data, args[(idx)])
|
||||
#else
|
||||
#error "Unknown endianness"
|
||||
#endif
|
||||
|
||||
union arg64 {
|
||||
|
|
|
@ -33,6 +33,9 @@ char *evm_config_xattrnames[] = {
|
|||
#endif
|
||||
#ifdef CONFIG_SECURITY_SMACK
|
||||
XATTR_NAME_SMACK,
|
||||
#endif
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
XATTR_NAME_IMA,
|
||||
#endif
|
||||
XATTR_NAME_CAPS,
|
||||
NULL
|
||||
|
|
|
@ -22,7 +22,7 @@
|
|||
#include "integrity.h"
|
||||
|
||||
static struct rb_root integrity_iint_tree = RB_ROOT;
|
||||
static DEFINE_SPINLOCK(integrity_iint_lock);
|
||||
static DEFINE_RWLOCK(integrity_iint_lock);
|
||||
static struct kmem_cache *iint_cache __read_mostly;
|
||||
|
||||
int iint_initialized;
|
||||
|
@ -35,8 +35,6 @@ static struct integrity_iint_cache *__integrity_iint_find(struct inode *inode)
|
|||
struct integrity_iint_cache *iint;
|
||||
struct rb_node *n = integrity_iint_tree.rb_node;
|
||||
|
||||
assert_spin_locked(&integrity_iint_lock);
|
||||
|
||||
while (n) {
|
||||
iint = rb_entry(n, struct integrity_iint_cache, rb_node);
|
||||
|
||||
|
@ -63,9 +61,9 @@ struct integrity_iint_cache *integrity_iint_find(struct inode *inode)
|
|||
if (!IS_IMA(inode))
|
||||
return NULL;
|
||||
|
||||
spin_lock(&integrity_iint_lock);
|
||||
read_lock(&integrity_iint_lock);
|
||||
iint = __integrity_iint_find(inode);
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
read_unlock(&integrity_iint_lock);
|
||||
|
||||
return iint;
|
||||
}
|
||||
|
@ -74,59 +72,53 @@ static void iint_free(struct integrity_iint_cache *iint)
|
|||
{
|
||||
iint->version = 0;
|
||||
iint->flags = 0UL;
|
||||
iint->ima_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
kmem_cache_free(iint_cache, iint);
|
||||
}
|
||||
|
||||
/**
|
||||
* integrity_inode_alloc - allocate an iint associated with an inode
|
||||
* integrity_inode_get - find or allocate an iint associated with an inode
|
||||
* @inode: pointer to the inode
|
||||
* @return: allocated iint
|
||||
*
|
||||
* Caller must lock i_mutex
|
||||
*/
|
||||
int integrity_inode_alloc(struct inode *inode)
|
||||
struct integrity_iint_cache *integrity_inode_get(struct inode *inode)
|
||||
{
|
||||
struct rb_node **p;
|
||||
struct rb_node *new_node, *parent = NULL;
|
||||
struct integrity_iint_cache *new_iint, *test_iint;
|
||||
int rc;
|
||||
struct rb_node *node, *parent = NULL;
|
||||
struct integrity_iint_cache *iint, *test_iint;
|
||||
|
||||
new_iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
|
||||
if (!new_iint)
|
||||
return -ENOMEM;
|
||||
iint = integrity_iint_find(inode);
|
||||
if (iint)
|
||||
return iint;
|
||||
|
||||
new_iint->inode = inode;
|
||||
new_node = &new_iint->rb_node;
|
||||
iint = kmem_cache_alloc(iint_cache, GFP_NOFS);
|
||||
if (!iint)
|
||||
return NULL;
|
||||
|
||||
mutex_lock(&inode->i_mutex); /* i_flags */
|
||||
spin_lock(&integrity_iint_lock);
|
||||
write_lock(&integrity_iint_lock);
|
||||
|
||||
p = &integrity_iint_tree.rb_node;
|
||||
while (*p) {
|
||||
parent = *p;
|
||||
test_iint = rb_entry(parent, struct integrity_iint_cache,
|
||||
rb_node);
|
||||
rc = -EEXIST;
|
||||
if (inode < test_iint->inode)
|
||||
p = &(*p)->rb_left;
|
||||
else if (inode > test_iint->inode)
|
||||
p = &(*p)->rb_right;
|
||||
else
|
||||
goto out_err;
|
||||
p = &(*p)->rb_right;
|
||||
}
|
||||
|
||||
iint->inode = inode;
|
||||
node = &iint->rb_node;
|
||||
inode->i_flags |= S_IMA;
|
||||
rb_link_node(new_node, parent, p);
|
||||
rb_insert_color(new_node, &integrity_iint_tree);
|
||||
rb_link_node(node, parent, p);
|
||||
rb_insert_color(node, &integrity_iint_tree);
|
||||
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
mutex_unlock(&inode->i_mutex); /* i_flags */
|
||||
|
||||
return 0;
|
||||
out_err:
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
mutex_unlock(&inode->i_mutex); /* i_flags */
|
||||
iint_free(new_iint);
|
||||
|
||||
return rc;
|
||||
write_unlock(&integrity_iint_lock);
|
||||
return iint;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -142,10 +134,10 @@ void integrity_inode_free(struct inode *inode)
|
|||
if (!IS_IMA(inode))
|
||||
return;
|
||||
|
||||
spin_lock(&integrity_iint_lock);
|
||||
write_lock(&integrity_iint_lock);
|
||||
iint = __integrity_iint_find(inode);
|
||||
rb_erase(&iint->rb_node, &integrity_iint_tree);
|
||||
spin_unlock(&integrity_iint_lock);
|
||||
write_unlock(&integrity_iint_lock);
|
||||
|
||||
iint_free(iint);
|
||||
}
|
||||
|
@ -157,7 +149,7 @@ static void init_once(void *foo)
|
|||
memset(iint, 0, sizeof *iint);
|
||||
iint->version = 0;
|
||||
iint->flags = 0UL;
|
||||
mutex_init(&iint->mutex);
|
||||
iint->ima_status = INTEGRITY_UNKNOWN;
|
||||
iint->evm_status = INTEGRITY_UNKNOWN;
|
||||
}
|
||||
|
||||
|
|
|
@ -11,6 +11,7 @@ config IMA
|
|||
select CRYPTO_SHA1
|
||||
select TCG_TPM if HAS_IOMEM && !UML
|
||||
select TCG_TIS if TCG_TPM && X86
|
||||
select TCG_IBMVTPM if TCG_TPM && PPC64
|
||||
help
|
||||
The Trusted Computing Group(TCG) runtime Integrity
|
||||
Measurement Architecture(IMA) maintains a list of hash
|
||||
|
@ -55,3 +56,18 @@ config IMA_LSM_RULES
|
|||
default y
|
||||
help
|
||||
Disabling this option will disregard LSM based policy rules.
|
||||
|
||||
config IMA_APPRAISE
|
||||
bool "Appraise integrity measurements"
|
||||
depends on IMA
|
||||
default n
|
||||
help
|
||||
This option enables local measurement integrity appraisal.
|
||||
It requires the system to be labeled with a security extended
|
||||
attribute containing the file hash measurement. To protect
|
||||
the security extended attributes from offline attack, enable
|
||||
and configure EVM.
|
||||
|
||||
For more information on integrity appraisal refer to:
|
||||
<http://linux-ima.sourceforge.net>
|
||||
If unsure, say N.
|
||||
|
|
|
@ -8,3 +8,4 @@ obj-$(CONFIG_IMA) += ima.o
|
|||
ima-y := ima_fs.o ima_queue.o ima_init.o ima_main.o ima_crypto.o ima_api.o \
|
||||
ima_policy.o
|
||||
ima-$(CONFIG_IMA_AUDIT) += ima_audit.o
|
||||
ima-$(CONFIG_IMA_APPRAISE) += ima_appraise.o
|
||||
|
|
|
@ -40,6 +40,7 @@ enum tpm_pcrs { TPM_PCR0 = 0, TPM_PCR8 = 8 };
|
|||
extern int ima_initialized;
|
||||
extern int ima_used_chip;
|
||||
extern char *ima_hash;
|
||||
extern int ima_appraise;
|
||||
|
||||
/* IMA inode template definition */
|
||||
struct ima_template_data {
|
||||
|
@ -107,11 +108,14 @@ static inline unsigned long ima_hash_key(u8 *digest)
|
|||
}
|
||||
|
||||
/* LIM API function definitions */
|
||||
int ima_get_action(struct inode *inode, int mask, int function);
|
||||
int ima_must_measure(struct inode *inode, int mask, int function);
|
||||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file);
|
||||
void ima_store_measurement(struct integrity_iint_cache *iint, struct file *file,
|
||||
const unsigned char *filename);
|
||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename);
|
||||
int ima_store_template(struct ima_template_entry *entry, int violation,
|
||||
struct inode *inode);
|
||||
void ima_template_show(struct seq_file *m, void *e, enum ima_show_type show);
|
||||
|
@ -123,14 +127,45 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode);
|
|||
struct integrity_iint_cache *integrity_iint_find(struct inode *inode);
|
||||
|
||||
/* IMA policy related functions */
|
||||
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK };
|
||||
enum ima_hooks { FILE_CHECK = 1, FILE_MMAP, BPRM_CHECK, POST_SETATTR };
|
||||
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask);
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags);
|
||||
void ima_init_policy(void);
|
||||
void ima_update_policy(void);
|
||||
ssize_t ima_parse_add_rule(char *);
|
||||
void ima_delete_rules(void);
|
||||
|
||||
/* Appraise integrity measurements */
|
||||
#define IMA_APPRAISE_ENFORCE 0x01
|
||||
#define IMA_APPRAISE_FIX 0x02
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename);
|
||||
int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask);
|
||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file);
|
||||
|
||||
#else
|
||||
static inline int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
return INTEGRITY_UNKNOWN;
|
||||
}
|
||||
|
||||
static inline int ima_must_appraise(struct inode *inode,
|
||||
enum ima_hooks func, int mask)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline void ima_update_xattr(struct integrity_iint_cache *iint,
|
||||
struct file *file)
|
||||
{
|
||||
}
|
||||
#endif
|
||||
|
||||
/* LSM based policy rules require audit */
|
||||
#ifdef CONFIG_IMA_LSM_RULES
|
||||
|
||||
|
|
|
@ -9,13 +9,17 @@
|
|||
* License.
|
||||
*
|
||||
* File: ima_api.c
|
||||
* Implements must_measure, collect_measurement, store_measurement,
|
||||
* and store_template.
|
||||
* Implements must_appraise_or_measure, collect_measurement,
|
||||
* appraise_measurement, store_measurement and store_template.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/evm.h>
|
||||
#include "ima.h"
|
||||
|
||||
static const char *IMA_TEMPLATE_NAME = "ima";
|
||||
|
||||
/*
|
||||
|
@ -93,7 +97,7 @@ err_out:
|
|||
}
|
||||
|
||||
/**
|
||||
* ima_must_measure - measure decision based on policy.
|
||||
* ima_get_action - appraise & measure decision based on policy.
|
||||
* @inode: pointer to inode to measure
|
||||
* @mask: contains the permission mask (MAY_READ, MAY_WRITE, MAY_EXECUTE)
|
||||
* @function: calling function (FILE_CHECK, BPRM_CHECK, FILE_MMAP)
|
||||
|
@ -105,15 +109,22 @@ err_out:
|
|||
* mask: contains the permission mask
|
||||
* fsmagic: hex value
|
||||
*
|
||||
* Return 0 to measure. For matching a DONT_MEASURE policy, no policy,
|
||||
* or other error, return an error code.
|
||||
*/
|
||||
* Returns IMA_MEASURE, IMA_APPRAISE mask.
|
||||
*
|
||||
*/
|
||||
int ima_get_action(struct inode *inode, int mask, int function)
|
||||
{
|
||||
int flags = IMA_MEASURE | IMA_AUDIT | IMA_APPRAISE;
|
||||
|
||||
if (!ima_appraise)
|
||||
flags &= ~IMA_APPRAISE;
|
||||
|
||||
return ima_match_policy(inode, function, mask, flags);
|
||||
}
|
||||
|
||||
int ima_must_measure(struct inode *inode, int mask, int function)
|
||||
{
|
||||
int must_measure;
|
||||
|
||||
must_measure = ima_match_policy(inode, function, mask);
|
||||
return must_measure ? 0 : -EACCES;
|
||||
return ima_match_policy(inode, function, mask, IMA_MEASURE);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -129,16 +140,24 @@ int ima_must_measure(struct inode *inode, int mask, int function)
|
|||
int ima_collect_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file)
|
||||
{
|
||||
int result = -EEXIST;
|
||||
struct inode *inode = file->f_dentry->d_inode;
|
||||
const char *filename = file->f_dentry->d_name.name;
|
||||
int result = 0;
|
||||
|
||||
if (!(iint->flags & IMA_MEASURED)) {
|
||||
if (!(iint->flags & IMA_COLLECTED)) {
|
||||
u64 i_version = file->f_dentry->d_inode->i_version;
|
||||
|
||||
memset(iint->digest, 0, IMA_DIGEST_SIZE);
|
||||
result = ima_calc_hash(file, iint->digest);
|
||||
if (!result)
|
||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||
result = ima_calc_hash(file, iint->ima_xattr.digest);
|
||||
if (!result) {
|
||||
iint->version = i_version;
|
||||
iint->flags |= IMA_COLLECTED;
|
||||
}
|
||||
}
|
||||
if (result)
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode,
|
||||
filename, "collect_data", "failed",
|
||||
result, 0);
|
||||
return result;
|
||||
}
|
||||
|
||||
|
@ -167,6 +186,9 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
|||
struct ima_template_entry *entry;
|
||||
int violation = 0;
|
||||
|
||||
if (iint->flags & IMA_MEASURED)
|
||||
return;
|
||||
|
||||
entry = kmalloc(sizeof(*entry), GFP_KERNEL);
|
||||
if (!entry) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_PCR, inode, filename,
|
||||
|
@ -174,7 +196,7 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
|||
return;
|
||||
}
|
||||
memset(&entry->template, 0, sizeof(entry->template));
|
||||
memcpy(entry->template.digest, iint->digest, IMA_DIGEST_SIZE);
|
||||
memcpy(entry->template.digest, iint->ima_xattr.digest, IMA_DIGEST_SIZE);
|
||||
strcpy(entry->template.file_name,
|
||||
(strlen(filename) > IMA_EVENT_NAME_LEN_MAX) ?
|
||||
file->f_dentry->d_name.name : filename);
|
||||
|
@ -185,3 +207,33 @@ void ima_store_measurement(struct integrity_iint_cache *iint,
|
|||
if (result < 0)
|
||||
kfree(entry);
|
||||
}
|
||||
|
||||
void ima_audit_measurement(struct integrity_iint_cache *iint,
|
||||
const unsigned char *filename)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
char hash[(IMA_DIGEST_SIZE * 2) + 1];
|
||||
int i;
|
||||
|
||||
if (iint->flags & IMA_AUDITED)
|
||||
return;
|
||||
|
||||
for (i = 0; i < IMA_DIGEST_SIZE; i++)
|
||||
hex_byte_pack(hash + (i * 2), iint->ima_xattr.digest[i]);
|
||||
hash[i * 2] = '\0';
|
||||
|
||||
ab = audit_log_start(current->audit_context, GFP_KERNEL,
|
||||
AUDIT_INTEGRITY_RULE);
|
||||
if (!ab)
|
||||
return;
|
||||
|
||||
audit_log_format(ab, "file=");
|
||||
audit_log_untrustedstring(ab, filename);
|
||||
audit_log_format(ab, " hash=");
|
||||
audit_log_untrustedstring(ab, hash);
|
||||
|
||||
audit_log_task_info(ab, current);
|
||||
audit_log_end(ab);
|
||||
|
||||
iint->flags |= IMA_AUDITED;
|
||||
}
|
||||
|
|
|
@ -0,0 +1,263 @@
|
|||
/*
|
||||
* Copyright (C) 2011 IBM Corporation
|
||||
*
|
||||
* Author:
|
||||
* Mimi Zohar <zohar@us.ibm.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, version 2 of the License.
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/file.h>
|
||||
#include <linux/fs.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/magic.h>
|
||||
#include <linux/ima.h>
|
||||
#include <linux/evm.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
static int __init default_appraise_setup(char *str)
|
||||
{
|
||||
if (strncmp(str, "off", 3) == 0)
|
||||
ima_appraise = 0;
|
||||
else if (strncmp(str, "fix", 3) == 0)
|
||||
ima_appraise = IMA_APPRAISE_FIX;
|
||||
return 1;
|
||||
}
|
||||
|
||||
__setup("ima_appraise=", default_appraise_setup);
|
||||
|
||||
/*
|
||||
* ima_must_appraise - set appraise flag
|
||||
*
|
||||
* Return 1 to appraise
|
||||
*/
|
||||
int ima_must_appraise(struct inode *inode, enum ima_hooks func, int mask)
|
||||
{
|
||||
if (!ima_appraise)
|
||||
return 0;
|
||||
|
||||
return ima_match_policy(inode, func, mask, IMA_APPRAISE);
|
||||
}
|
||||
|
||||
static void ima_fix_xattr(struct dentry *dentry,
|
||||
struct integrity_iint_cache *iint)
|
||||
{
|
||||
iint->ima_xattr.type = IMA_XATTR_DIGEST;
|
||||
__vfs_setxattr_noperm(dentry, XATTR_NAME_IMA, (u8 *)&iint->ima_xattr,
|
||||
sizeof iint->ima_xattr, 0);
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_appraise_measurement - appraise file measurement
|
||||
*
|
||||
* Call evm_verifyxattr() to verify the integrity of 'security.ima'.
|
||||
* Assuming success, compare the xattr hash with the collected measurement.
|
||||
*
|
||||
* Return 0 on success, error code otherwise
|
||||
*/
|
||||
int ima_appraise_measurement(struct integrity_iint_cache *iint,
|
||||
struct file *file, const unsigned char *filename)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct evm_ima_xattr_data *xattr_value = NULL;
|
||||
enum integrity_status status = INTEGRITY_UNKNOWN;
|
||||
const char *op = "appraise_data";
|
||||
char *cause = "unknown";
|
||||
int rc;
|
||||
|
||||
if (!ima_appraise)
|
||||
return 0;
|
||||
if (!inode->i_op->getxattr)
|
||||
return INTEGRITY_UNKNOWN;
|
||||
|
||||
if (iint->flags & IMA_APPRAISED)
|
||||
return iint->ima_status;
|
||||
|
||||
rc = vfs_getxattr_alloc(dentry, XATTR_NAME_IMA, (char **)&xattr_value,
|
||||
0, GFP_NOFS);
|
||||
if (rc <= 0) {
|
||||
if (rc && rc != -ENODATA)
|
||||
goto out;
|
||||
|
||||
cause = "missing-hash";
|
||||
status =
|
||||
(inode->i_size == 0) ? INTEGRITY_PASS : INTEGRITY_NOLABEL;
|
||||
goto out;
|
||||
}
|
||||
|
||||
status = evm_verifyxattr(dentry, XATTR_NAME_IMA, xattr_value, rc, iint);
|
||||
if ((status != INTEGRITY_PASS) && (status != INTEGRITY_UNKNOWN)) {
|
||||
if ((status == INTEGRITY_NOLABEL)
|
||||
|| (status == INTEGRITY_NOXATTRS))
|
||||
cause = "missing-HMAC";
|
||||
else if (status == INTEGRITY_FAIL)
|
||||
cause = "invalid-HMAC";
|
||||
goto out;
|
||||
}
|
||||
|
||||
switch (xattr_value->type) {
|
||||
case IMA_XATTR_DIGEST:
|
||||
rc = memcmp(xattr_value->digest, iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
if (rc) {
|
||||
cause = "invalid-hash";
|
||||
status = INTEGRITY_FAIL;
|
||||
print_hex_dump_bytes("security.ima: ", DUMP_PREFIX_NONE,
|
||||
xattr_value, sizeof(*xattr_value));
|
||||
print_hex_dump_bytes("collected: ", DUMP_PREFIX_NONE,
|
||||
(u8 *)&iint->ima_xattr,
|
||||
sizeof iint->ima_xattr);
|
||||
break;
|
||||
}
|
||||
status = INTEGRITY_PASS;
|
||||
break;
|
||||
case EVM_IMA_XATTR_DIGSIG:
|
||||
iint->flags |= IMA_DIGSIG;
|
||||
rc = integrity_digsig_verify(INTEGRITY_KEYRING_IMA,
|
||||
xattr_value->digest, rc - 1,
|
||||
iint->ima_xattr.digest,
|
||||
IMA_DIGEST_SIZE);
|
||||
if (rc == -EOPNOTSUPP) {
|
||||
status = INTEGRITY_UNKNOWN;
|
||||
} else if (rc) {
|
||||
cause = "invalid-signature";
|
||||
status = INTEGRITY_FAIL;
|
||||
} else {
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
status = INTEGRITY_UNKNOWN;
|
||||
cause = "unknown-ima-data";
|
||||
break;
|
||||
}
|
||||
|
||||
out:
|
||||
if (status != INTEGRITY_PASS) {
|
||||
if ((ima_appraise & IMA_APPRAISE_FIX) &&
|
||||
(!xattr_value ||
|
||||
xattr_value->type != EVM_IMA_XATTR_DIGSIG)) {
|
||||
ima_fix_xattr(dentry, iint);
|
||||
status = INTEGRITY_PASS;
|
||||
}
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_DATA, inode, filename,
|
||||
op, cause, rc, 0);
|
||||
} else {
|
||||
iint->flags |= IMA_APPRAISED;
|
||||
}
|
||||
iint->ima_status = status;
|
||||
kfree(xattr_value);
|
||||
return status;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_update_xattr - update 'security.ima' hash value
|
||||
*/
|
||||
void ima_update_xattr(struct integrity_iint_cache *iint, struct file *file)
|
||||
{
|
||||
struct dentry *dentry = file->f_dentry;
|
||||
int rc = 0;
|
||||
|
||||
/* do not collect and update hash for digital signatures */
|
||||
if (iint->flags & IMA_DIGSIG)
|
||||
return;
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
if (rc < 0)
|
||||
return;
|
||||
|
||||
ima_fix_xattr(dentry, iint);
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_inode_post_setattr - reflect file metadata changes
|
||||
* @dentry: pointer to the affected dentry
|
||||
*
|
||||
* Changes to a dentry's metadata might result in needing to appraise.
|
||||
*
|
||||
* This function is called from notify_change(), which expects the caller
|
||||
* to lock the inode's i_mutex.
|
||||
*/
|
||||
void ima_inode_post_setattr(struct dentry *dentry)
|
||||
{
|
||||
struct inode *inode = dentry->d_inode;
|
||||
struct integrity_iint_cache *iint;
|
||||
int must_appraise, rc;
|
||||
|
||||
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode)
|
||||
|| !inode->i_op->removexattr)
|
||||
return;
|
||||
|
||||
must_appraise = ima_must_appraise(inode, MAY_ACCESS, POST_SETATTR);
|
||||
iint = integrity_iint_find(inode);
|
||||
if (iint) {
|
||||
if (must_appraise)
|
||||
iint->flags |= IMA_APPRAISE;
|
||||
else
|
||||
iint->flags &= ~(IMA_APPRAISE | IMA_APPRAISED);
|
||||
}
|
||||
if (!must_appraise)
|
||||
rc = inode->i_op->removexattr(dentry, XATTR_NAME_IMA);
|
||||
return;
|
||||
}
|
||||
|
||||
/*
|
||||
* ima_protect_xattr - protect 'security.ima'
|
||||
*
|
||||
* Ensure that not just anyone can modify or remove 'security.ima'.
|
||||
*/
|
||||
static int ima_protect_xattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len)
|
||||
{
|
||||
if (strcmp(xattr_name, XATTR_NAME_IMA) == 0) {
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ima_reset_appraise_flags(struct inode *inode)
|
||||
{
|
||||
struct integrity_iint_cache *iint;
|
||||
|
||||
if (!ima_initialized || !ima_appraise || !S_ISREG(inode->i_mode))
|
||||
return;
|
||||
|
||||
iint = integrity_iint_find(inode);
|
||||
if (!iint)
|
||||
return;
|
||||
|
||||
iint->flags &= ~IMA_DONE_MASK;
|
||||
return;
|
||||
}
|
||||
|
||||
int ima_inode_setxattr(struct dentry *dentry, const char *xattr_name,
|
||||
const void *xattr_value, size_t xattr_value_len)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = ima_protect_xattr(dentry, xattr_name, xattr_value,
|
||||
xattr_value_len);
|
||||
if (result == 1) {
|
||||
ima_reset_appraise_flags(dentry->d_inode);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
int ima_inode_removexattr(struct dentry *dentry, const char *xattr_name)
|
||||
{
|
||||
int result;
|
||||
|
||||
result = ima_protect_xattr(dentry, xattr_name, NULL, 0);
|
||||
if (result == 1) {
|
||||
ima_reset_appraise_flags(dentry->d_inode);
|
||||
result = 0;
|
||||
}
|
||||
return result;
|
||||
}
|
|
@ -48,7 +48,7 @@ int ima_calc_hash(struct file *file, char *digest)
|
|||
struct scatterlist sg[1];
|
||||
loff_t i_size, offset = 0;
|
||||
char *rbuf;
|
||||
int rc;
|
||||
int rc, read = 0;
|
||||
|
||||
rc = init_desc(&desc);
|
||||
if (rc != 0)
|
||||
|
@ -59,6 +59,10 @@ int ima_calc_hash(struct file *file, char *digest)
|
|||
rc = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
if (!(file->f_mode & FMODE_READ)) {
|
||||
file->f_mode |= FMODE_READ;
|
||||
read = 1;
|
||||
}
|
||||
i_size = i_size_read(file->f_dentry->d_inode);
|
||||
while (offset < i_size) {
|
||||
int rbuf_len;
|
||||
|
@ -80,6 +84,8 @@ int ima_calc_hash(struct file *file, char *digest)
|
|||
kfree(rbuf);
|
||||
if (!rc)
|
||||
rc = crypto_hash_final(&desc, digest);
|
||||
if (read)
|
||||
file->f_mode &= ~FMODE_READ;
|
||||
out:
|
||||
crypto_free_hash(desc.tfm);
|
||||
return rc;
|
||||
|
|
|
@ -22,12 +22,19 @@
|
|||
#include <linux/mount.h>
|
||||
#include <linux/mman.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/xattr.h>
|
||||
#include <linux/ima.h>
|
||||
|
||||
#include "ima.h"
|
||||
|
||||
int ima_initialized;
|
||||
|
||||
#ifdef CONFIG_IMA_APPRAISE
|
||||
int ima_appraise = IMA_APPRAISE_ENFORCE;
|
||||
#else
|
||||
int ima_appraise;
|
||||
#endif
|
||||
|
||||
char *ima_hash = "sha1";
|
||||
static int __init hash_setup(char *str)
|
||||
{
|
||||
|
@ -52,7 +59,7 @@ static void ima_rdwr_violation_check(struct file *file)
|
|||
struct dentry *dentry = file->f_path.dentry;
|
||||
struct inode *inode = dentry->d_inode;
|
||||
fmode_t mode = file->f_mode;
|
||||
int rc;
|
||||
int must_measure;
|
||||
bool send_tomtou = false, send_writers = false;
|
||||
unsigned char *pathname = NULL, *pathbuf = NULL;
|
||||
|
||||
|
@ -67,8 +74,8 @@ static void ima_rdwr_violation_check(struct file *file)
|
|||
goto out;
|
||||
}
|
||||
|
||||
rc = ima_must_measure(inode, MAY_READ, FILE_CHECK);
|
||||
if (rc < 0)
|
||||
must_measure = ima_must_measure(inode, MAY_READ, FILE_CHECK);
|
||||
if (!must_measure)
|
||||
goto out;
|
||||
|
||||
if (atomic_read(&inode->i_writecount) > 0)
|
||||
|
@ -100,17 +107,21 @@ out:
|
|||
}
|
||||
|
||||
static void ima_check_last_writer(struct integrity_iint_cache *iint,
|
||||
struct inode *inode,
|
||||
struct file *file)
|
||||
struct inode *inode, struct file *file)
|
||||
{
|
||||
fmode_t mode = file->f_mode;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
if (mode & FMODE_WRITE &&
|
||||
atomic_read(&inode->i_writecount) == 1 &&
|
||||
iint->version != inode->i_version)
|
||||
iint->flags &= ~IMA_MEASURED;
|
||||
mutex_unlock(&iint->mutex);
|
||||
if (!(mode & FMODE_WRITE))
|
||||
return;
|
||||
|
||||
mutex_lock(&inode->i_mutex);
|
||||
if (atomic_read(&inode->i_writecount) == 1 &&
|
||||
iint->version != inode->i_version) {
|
||||
iint->flags &= ~IMA_DONE_MASK;
|
||||
if (iint->flags & IMA_APPRAISE)
|
||||
ima_update_xattr(iint, file);
|
||||
}
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -140,29 +151,38 @@ static int process_measurement(struct file *file, const unsigned char *filename,
|
|||
struct inode *inode = file->f_dentry->d_inode;
|
||||
struct integrity_iint_cache *iint;
|
||||
unsigned char *pathname = NULL, *pathbuf = NULL;
|
||||
int rc = 0;
|
||||
int rc = -ENOMEM, action, must_appraise;
|
||||
|
||||
if (!ima_initialized || !S_ISREG(inode->i_mode))
|
||||
return 0;
|
||||
|
||||
rc = ima_must_measure(inode, mask, function);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
retry:
|
||||
iint = integrity_iint_find(inode);
|
||||
if (!iint) {
|
||||
rc = integrity_inode_alloc(inode);
|
||||
if (!rc || rc == -EEXIST)
|
||||
goto retry;
|
||||
return rc;
|
||||
}
|
||||
/* Determine if in appraise/audit/measurement policy,
|
||||
* returns IMA_MEASURE, IMA_APPRAISE, IMA_AUDIT bitmask. */
|
||||
action = ima_get_action(inode, mask, function);
|
||||
if (!action)
|
||||
return 0;
|
||||
|
||||
mutex_lock(&iint->mutex);
|
||||
must_appraise = action & IMA_APPRAISE;
|
||||
|
||||
rc = iint->flags & IMA_MEASURED ? 1 : 0;
|
||||
if (rc != 0)
|
||||
mutex_lock(&inode->i_mutex);
|
||||
|
||||
iint = integrity_inode_get(inode);
|
||||
if (!iint)
|
||||
goto out;
|
||||
|
||||
/* Determine if already appraised/measured based on bitmask
|
||||
* (IMA_MEASURE, IMA_MEASURED, IMA_APPRAISE, IMA_APPRAISED,
|
||||
* IMA_AUDIT, IMA_AUDITED) */
|
||||
iint->flags |= action;
|
||||
action &= ~((iint->flags & IMA_DONE_MASK) >> 1);
|
||||
|
||||
/* Nothing to do, just return existing appraised status */
|
||||
if (!action) {
|
||||
if (iint->flags & IMA_APPRAISED)
|
||||
rc = iint->ima_status;
|
||||
goto out;
|
||||
}
|
||||
|
||||
rc = ima_collect_measurement(iint, file);
|
||||
if (rc != 0)
|
||||
goto out;
|
||||
|
@ -177,11 +197,18 @@ retry:
|
|||
pathname = NULL;
|
||||
}
|
||||
}
|
||||
ima_store_measurement(iint, file, !pathname ? filename : pathname);
|
||||
if (action & IMA_MEASURE)
|
||||
ima_store_measurement(iint, file,
|
||||
!pathname ? filename : pathname);
|
||||
if (action & IMA_APPRAISE)
|
||||
rc = ima_appraise_measurement(iint, file,
|
||||
!pathname ? filename : pathname);
|
||||
if (action & IMA_AUDIT)
|
||||
ima_audit_measurement(iint, !pathname ? filename : pathname);
|
||||
kfree(pathbuf);
|
||||
out:
|
||||
mutex_unlock(&iint->mutex);
|
||||
return rc;
|
||||
mutex_unlock(&inode->i_mutex);
|
||||
return (rc && must_appraise) ? -EACCES : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -197,14 +224,14 @@ out:
|
|||
*/
|
||||
int ima_file_mmap(struct file *file, unsigned long prot)
|
||||
{
|
||||
int rc;
|
||||
int rc = 0;
|
||||
|
||||
if (!file)
|
||||
return 0;
|
||||
if (prot & PROT_EXEC)
|
||||
rc = process_measurement(file, file->f_dentry->d_name.name,
|
||||
MAY_EXEC, FILE_MMAP);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -228,7 +255,7 @@ int ima_bprm_check(struct linux_binprm *bprm)
|
|||
(strcmp(bprm->filename, bprm->interp) == 0) ?
|
||||
bprm->filename : bprm->interp,
|
||||
MAY_EXEC, BPRM_CHECK);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -249,7 +276,7 @@ int ima_file_check(struct file *file, int mask)
|
|||
rc = process_measurement(file, file->f_dentry->d_name.name,
|
||||
mask & (MAY_READ | MAY_WRITE | MAY_EXEC),
|
||||
FILE_CHECK);
|
||||
return 0;
|
||||
return (ima_appraise & IMA_APPRAISE_ENFORCE) ? rc : 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(ima_file_check);
|
||||
|
||||
|
|
|
@ -24,22 +24,29 @@
|
|||
#define IMA_MASK 0x0002
|
||||
#define IMA_FSMAGIC 0x0004
|
||||
#define IMA_UID 0x0008
|
||||
#define IMA_FOWNER 0x0010
|
||||
|
||||
enum ima_action { UNKNOWN = -1, DONT_MEASURE = 0, MEASURE };
|
||||
#define UNKNOWN 0
|
||||
#define MEASURE 0x0001 /* same as IMA_MEASURE */
|
||||
#define DONT_MEASURE 0x0002
|
||||
#define APPRAISE 0x0004 /* same as IMA_APPRAISE */
|
||||
#define DONT_APPRAISE 0x0008
|
||||
#define AUDIT 0x0040
|
||||
|
||||
#define MAX_LSM_RULES 6
|
||||
enum lsm_rule_types { LSM_OBJ_USER, LSM_OBJ_ROLE, LSM_OBJ_TYPE,
|
||||
LSM_SUBJ_USER, LSM_SUBJ_ROLE, LSM_SUBJ_TYPE
|
||||
};
|
||||
|
||||
struct ima_measure_rule_entry {
|
||||
struct ima_rule_entry {
|
||||
struct list_head list;
|
||||
enum ima_action action;
|
||||
int action;
|
||||
unsigned int flags;
|
||||
enum ima_hooks func;
|
||||
int mask;
|
||||
unsigned long fsmagic;
|
||||
kuid_t uid;
|
||||
kuid_t fowner;
|
||||
struct {
|
||||
void *rule; /* LSM file metadata specific */
|
||||
int type; /* audit type */
|
||||
|
@ -48,7 +55,7 @@ struct ima_measure_rule_entry {
|
|||
|
||||
/*
|
||||
* Without LSM specific knowledge, the default policy can only be
|
||||
* written in terms of .action, .func, .mask, .fsmagic, and .uid
|
||||
* written in terms of .action, .func, .mask, .fsmagic, .uid, and .fowner
|
||||
*/
|
||||
|
||||
/*
|
||||
|
@ -57,7 +64,7 @@ struct ima_measure_rule_entry {
|
|||
* normal users can easily run the machine out of memory simply building
|
||||
* and running executables.
|
||||
*/
|
||||
static struct ima_measure_rule_entry default_rules[] = {
|
||||
static struct ima_rule_entry default_rules[] = {
|
||||
{.action = DONT_MEASURE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_MEASURE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
|
@ -75,19 +82,41 @@ static struct ima_measure_rule_entry default_rules[] = {
|
|||
.flags = IMA_FUNC | IMA_MASK | IMA_UID},
|
||||
};
|
||||
|
||||
static LIST_HEAD(measure_default_rules);
|
||||
static LIST_HEAD(measure_policy_rules);
|
||||
static struct list_head *ima_measure;
|
||||
static struct ima_rule_entry default_appraise_rules[] = {
|
||||
{.action = DONT_APPRAISE,.fsmagic = PROC_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SYSFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = DEBUGFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = TMPFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = RAMFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = DEVPTS_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = BINFMTFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SECURITYFS_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = SELINUX_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = DONT_APPRAISE,.fsmagic = CGROUP_SUPER_MAGIC,.flags = IMA_FSMAGIC},
|
||||
{.action = APPRAISE,.fowner = GLOBAL_ROOT_UID,.flags = IMA_FOWNER},
|
||||
};
|
||||
|
||||
static DEFINE_MUTEX(ima_measure_mutex);
|
||||
static LIST_HEAD(ima_default_rules);
|
||||
static LIST_HEAD(ima_policy_rules);
|
||||
static struct list_head *ima_rules;
|
||||
|
||||
static DEFINE_MUTEX(ima_rules_mutex);
|
||||
|
||||
static bool ima_use_tcb __initdata;
|
||||
static int __init default_policy_setup(char *str)
|
||||
static int __init default_measure_policy_setup(char *str)
|
||||
{
|
||||
ima_use_tcb = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_tcb", default_policy_setup);
|
||||
__setup("ima_tcb", default_measure_policy_setup);
|
||||
|
||||
static bool ima_use_appraise_tcb __initdata;
|
||||
static int __init default_appraise_policy_setup(char *str)
|
||||
{
|
||||
ima_use_appraise_tcb = 1;
|
||||
return 1;
|
||||
}
|
||||
__setup("ima_appraise_tcb", default_appraise_policy_setup);
|
||||
|
||||
/**
|
||||
* ima_match_rules - determine whether an inode matches the measure rule.
|
||||
|
@ -98,7 +127,7 @@ __setup("ima_tcb", default_policy_setup);
|
|||
*
|
||||
* Returns true on rule match, false on failure.
|
||||
*/
|
||||
static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
||||
static bool ima_match_rules(struct ima_rule_entry *rule,
|
||||
struct inode *inode, enum ima_hooks func, int mask)
|
||||
{
|
||||
struct task_struct *tsk = current;
|
||||
|
@ -114,6 +143,8 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
|||
return false;
|
||||
if ((rule->flags & IMA_UID) && !uid_eq(rule->uid, cred->uid))
|
||||
return false;
|
||||
if ((rule->flags & IMA_FOWNER) && !uid_eq(rule->fowner, inode->i_uid))
|
||||
return false;
|
||||
for (i = 0; i < MAX_LSM_RULES; i++) {
|
||||
int rc = 0;
|
||||
u32 osid, sid;
|
||||
|
@ -163,39 +194,61 @@ static bool ima_match_rules(struct ima_measure_rule_entry *rule,
|
|||
* as elements in the list are never deleted, nor does the list
|
||||
* change.)
|
||||
*/
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask)
|
||||
int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask,
|
||||
int flags)
|
||||
{
|
||||
struct ima_measure_rule_entry *entry;
|
||||
struct ima_rule_entry *entry;
|
||||
int action = 0, actmask = flags | (flags << 1);
|
||||
|
||||
list_for_each_entry(entry, ima_measure, list) {
|
||||
bool rc;
|
||||
list_for_each_entry(entry, ima_rules, list) {
|
||||
|
||||
rc = ima_match_rules(entry, inode, func, mask);
|
||||
if (rc)
|
||||
return entry->action;
|
||||
if (!(entry->action & actmask))
|
||||
continue;
|
||||
|
||||
if (!ima_match_rules(entry, inode, func, mask))
|
||||
continue;
|
||||
|
||||
action |= entry->action & IMA_DO_MASK;
|
||||
if (entry->action & IMA_DO_MASK)
|
||||
actmask &= ~(entry->action | entry->action << 1);
|
||||
else
|
||||
actmask &= ~(entry->action | entry->action >> 1);
|
||||
|
||||
if (!actmask)
|
||||
break;
|
||||
}
|
||||
return 0;
|
||||
|
||||
return action;
|
||||
}
|
||||
|
||||
/**
|
||||
* ima_init_policy - initialize the default measure rules.
|
||||
*
|
||||
* ima_measure points to either the measure_default_rules or the
|
||||
* the new measure_policy_rules.
|
||||
* ima_rules points to either the ima_default_rules or the
|
||||
* the new ima_policy_rules.
|
||||
*/
|
||||
void __init ima_init_policy(void)
|
||||
{
|
||||
int i, entries;
|
||||
int i, measure_entries, appraise_entries;
|
||||
|
||||
/* if !ima_use_tcb set entries = 0 so we load NO default rules */
|
||||
if (ima_use_tcb)
|
||||
entries = ARRAY_SIZE(default_rules);
|
||||
else
|
||||
entries = 0;
|
||||
measure_entries = ima_use_tcb ? ARRAY_SIZE(default_rules) : 0;
|
||||
appraise_entries = ima_use_appraise_tcb ?
|
||||
ARRAY_SIZE(default_appraise_rules) : 0;
|
||||
|
||||
for (i = 0; i < measure_entries + appraise_entries; i++) {
|
||||
if (i < measure_entries)
|
||||
list_add_tail(&default_rules[i].list,
|
||||
&ima_default_rules);
|
||||
else {
|
||||
int j = i - measure_entries;
|
||||
|
||||
for (i = 0; i < entries; i++)
|
||||
list_add_tail(&default_rules[i].list, &measure_default_rules);
|
||||
ima_measure = &measure_default_rules;
|
||||
list_add_tail(&default_appraise_rules[j].list,
|
||||
&ima_default_rules);
|
||||
}
|
||||
}
|
||||
|
||||
ima_rules = &ima_default_rules;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -212,8 +265,8 @@ void ima_update_policy(void)
|
|||
int result = 1;
|
||||
int audit_info = 0;
|
||||
|
||||
if (ima_measure == &measure_default_rules) {
|
||||
ima_measure = &measure_policy_rules;
|
||||
if (ima_rules == &ima_default_rules) {
|
||||
ima_rules = &ima_policy_rules;
|
||||
cause = "complete";
|
||||
result = 0;
|
||||
}
|
||||
|
@ -224,14 +277,19 @@ void ima_update_policy(void)
|
|||
enum {
|
||||
Opt_err = -1,
|
||||
Opt_measure = 1, Opt_dont_measure,
|
||||
Opt_appraise, Opt_dont_appraise,
|
||||
Opt_audit,
|
||||
Opt_obj_user, Opt_obj_role, Opt_obj_type,
|
||||
Opt_subj_user, Opt_subj_role, Opt_subj_type,
|
||||
Opt_func, Opt_mask, Opt_fsmagic, Opt_uid
|
||||
Opt_func, Opt_mask, Opt_fsmagic, Opt_uid, Opt_fowner
|
||||
};
|
||||
|
||||
static match_table_t policy_tokens = {
|
||||
{Opt_measure, "measure"},
|
||||
{Opt_dont_measure, "dont_measure"},
|
||||
{Opt_appraise, "appraise"},
|
||||
{Opt_dont_appraise, "dont_appraise"},
|
||||
{Opt_audit, "audit"},
|
||||
{Opt_obj_user, "obj_user=%s"},
|
||||
{Opt_obj_role, "obj_role=%s"},
|
||||
{Opt_obj_type, "obj_type=%s"},
|
||||
|
@ -242,10 +300,11 @@ static match_table_t policy_tokens = {
|
|||
{Opt_mask, "mask=%s"},
|
||||
{Opt_fsmagic, "fsmagic=%s"},
|
||||
{Opt_uid, "uid=%s"},
|
||||
{Opt_fowner, "fowner=%s"},
|
||||
{Opt_err, NULL}
|
||||
};
|
||||
|
||||
static int ima_lsm_rule_init(struct ima_measure_rule_entry *entry,
|
||||
static int ima_lsm_rule_init(struct ima_rule_entry *entry,
|
||||
char *args, int lsm_rule, int audit_type)
|
||||
{
|
||||
int result;
|
||||
|
@ -269,7 +328,7 @@ static void ima_log_string(struct audit_buffer *ab, char *key, char *value)
|
|||
audit_log_format(ab, " ");
|
||||
}
|
||||
|
||||
static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
||||
static int ima_parse_rule(char *rule, struct ima_rule_entry *entry)
|
||||
{
|
||||
struct audit_buffer *ab;
|
||||
char *p;
|
||||
|
@ -278,6 +337,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
ab = audit_log_start(NULL, GFP_KERNEL, AUDIT_INTEGRITY_RULE);
|
||||
|
||||
entry->uid = INVALID_UID;
|
||||
entry->fowner = INVALID_UID;
|
||||
entry->action = UNKNOWN;
|
||||
while ((p = strsep(&rule, " \t")) != NULL) {
|
||||
substring_t args[MAX_OPT_ARGS];
|
||||
|
@ -306,11 +366,35 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
|
||||
entry->action = DONT_MEASURE;
|
||||
break;
|
||||
case Opt_appraise:
|
||||
ima_log_string(ab, "action", "appraise");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = APPRAISE;
|
||||
break;
|
||||
case Opt_dont_appraise:
|
||||
ima_log_string(ab, "action", "dont_appraise");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = DONT_APPRAISE;
|
||||
break;
|
||||
case Opt_audit:
|
||||
ima_log_string(ab, "action", "audit");
|
||||
|
||||
if (entry->action != UNKNOWN)
|
||||
result = -EINVAL;
|
||||
|
||||
entry->action = AUDIT;
|
||||
break;
|
||||
case Opt_func:
|
||||
ima_log_string(ab, "func", args[0].from);
|
||||
|
||||
if (entry->func)
|
||||
result = -EINVAL;
|
||||
result = -EINVAL;
|
||||
|
||||
if (strcmp(args[0].from, "FILE_CHECK") == 0)
|
||||
entry->func = FILE_CHECK;
|
||||
|
@ -375,6 +459,23 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
entry->flags |= IMA_UID;
|
||||
}
|
||||
break;
|
||||
case Opt_fowner:
|
||||
ima_log_string(ab, "fowner", args[0].from);
|
||||
|
||||
if (uid_valid(entry->fowner)) {
|
||||
result = -EINVAL;
|
||||
break;
|
||||
}
|
||||
|
||||
result = strict_strtoul(args[0].from, 10, &lnum);
|
||||
if (!result) {
|
||||
entry->fowner = make_kuid(current_user_ns(), (uid_t)lnum);
|
||||
if (!uid_valid(entry->fowner) || (((uid_t)lnum) != lnum))
|
||||
result = -EINVAL;
|
||||
else
|
||||
entry->flags |= IMA_FOWNER;
|
||||
}
|
||||
break;
|
||||
case Opt_obj_user:
|
||||
ima_log_string(ab, "obj_user", args[0].from);
|
||||
result = ima_lsm_rule_init(entry, args[0].from,
|
||||
|
@ -426,7 +527,7 @@ static int ima_parse_rule(char *rule, struct ima_measure_rule_entry *entry)
|
|||
}
|
||||
|
||||
/**
|
||||
* ima_parse_add_rule - add a rule to measure_policy_rules
|
||||
* ima_parse_add_rule - add a rule to ima_policy_rules
|
||||
* @rule - ima measurement policy rule
|
||||
*
|
||||
* Uses a mutex to protect the policy list from multiple concurrent writers.
|
||||
|
@ -436,12 +537,12 @@ ssize_t ima_parse_add_rule(char *rule)
|
|||
{
|
||||
const char *op = "update_policy";
|
||||
char *p;
|
||||
struct ima_measure_rule_entry *entry;
|
||||
struct ima_rule_entry *entry;
|
||||
ssize_t result, len;
|
||||
int audit_info = 0;
|
||||
|
||||
/* Prevent installed policy from changing */
|
||||
if (ima_measure != &measure_default_rules) {
|
||||
if (ima_rules != &ima_default_rules) {
|
||||
integrity_audit_msg(AUDIT_INTEGRITY_STATUS, NULL,
|
||||
NULL, op, "already exists",
|
||||
-EACCES, audit_info);
|
||||
|
@ -474,9 +575,9 @@ ssize_t ima_parse_add_rule(char *rule)
|
|||
return result;
|
||||
}
|
||||
|
||||
mutex_lock(&ima_measure_mutex);
|
||||
list_add_tail(&entry->list, &measure_policy_rules);
|
||||
mutex_unlock(&ima_measure_mutex);
|
||||
mutex_lock(&ima_rules_mutex);
|
||||
list_add_tail(&entry->list, &ima_policy_rules);
|
||||
mutex_unlock(&ima_rules_mutex);
|
||||
|
||||
return len;
|
||||
}
|
||||
|
@ -484,12 +585,12 @@ ssize_t ima_parse_add_rule(char *rule)
|
|||
/* ima_delete_rules called to cleanup invalid policy */
|
||||
void ima_delete_rules(void)
|
||||
{
|
||||
struct ima_measure_rule_entry *entry, *tmp;
|
||||
struct ima_rule_entry *entry, *tmp;
|
||||
|
||||
mutex_lock(&ima_measure_mutex);
|
||||
list_for_each_entry_safe(entry, tmp, &measure_policy_rules, list) {
|
||||
mutex_lock(&ima_rules_mutex);
|
||||
list_for_each_entry_safe(entry, tmp, &ima_policy_rules, list) {
|
||||
list_del(&entry->list);
|
||||
kfree(entry);
|
||||
}
|
||||
mutex_unlock(&ima_measure_mutex);
|
||||
mutex_unlock(&ima_rules_mutex);
|
||||
}
|
||||
|
|
|
@ -15,8 +15,22 @@
|
|||
#include <linux/integrity.h>
|
||||
#include <crypto/sha.h>
|
||||
|
||||
/* iint action cache flags */
|
||||
#define IMA_MEASURE 0x0001
|
||||
#define IMA_MEASURED 0x0002
|
||||
#define IMA_APPRAISE 0x0004
|
||||
#define IMA_APPRAISED 0x0008
|
||||
/*#define IMA_COLLECT 0x0010 do not use this flag */
|
||||
#define IMA_COLLECTED 0x0020
|
||||
#define IMA_AUDIT 0x0040
|
||||
#define IMA_AUDITED 0x0080
|
||||
|
||||
/* iint cache flags */
|
||||
#define IMA_MEASURED 0x01
|
||||
#define IMA_DIGSIG 0x0100
|
||||
|
||||
#define IMA_DO_MASK (IMA_MEASURE | IMA_APPRAISE | IMA_AUDIT)
|
||||
#define IMA_DONE_MASK (IMA_MEASURED | IMA_APPRAISED | IMA_AUDITED \
|
||||
| IMA_COLLECTED)
|
||||
|
||||
enum evm_ima_xattr_type {
|
||||
IMA_XATTR_DIGEST = 0x01,
|
||||
|
@ -34,9 +48,9 @@ struct integrity_iint_cache {
|
|||
struct rb_node rb_node; /* rooted in integrity_iint_tree */
|
||||
struct inode *inode; /* back pointer to inode in question */
|
||||
u64 version; /* track inode changes */
|
||||
unsigned char flags;
|
||||
u8 digest[SHA1_DIGEST_SIZE];
|
||||
struct mutex mutex; /* protects: version, flags, digest */
|
||||
unsigned short flags;
|
||||
struct evm_ima_xattr_data ima_xattr;
|
||||
enum integrity_status ima_status;
|
||||
enum integrity_status evm_status;
|
||||
};
|
||||
|
||||
|
|
|
@ -368,38 +368,6 @@ static int trusted_tpm_send(const u32 chip_num, unsigned char *cmd,
|
|||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* get a random value from TPM
|
||||
*/
|
||||
static int tpm_get_random(struct tpm_buf *tb, unsigned char *buf, uint32_t len)
|
||||
{
|
||||
int ret;
|
||||
|
||||
INIT_BUF(tb);
|
||||
store16(tb, TPM_TAG_RQU_COMMAND);
|
||||
store32(tb, TPM_GETRANDOM_SIZE);
|
||||
store32(tb, TPM_ORD_GETRANDOM);
|
||||
store32(tb, len);
|
||||
ret = trusted_tpm_send(TPM_ANY_NUM, tb->data, sizeof tb->data);
|
||||
if (!ret)
|
||||
memcpy(buf, tb->data + TPM_GETRANDOM_SIZE, len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int my_get_random(unsigned char *buf, int len)
|
||||
{
|
||||
struct tpm_buf *tb;
|
||||
int ret;
|
||||
|
||||
tb = kmalloc(sizeof *tb, GFP_KERNEL);
|
||||
if (!tb)
|
||||
return -ENOMEM;
|
||||
ret = tpm_get_random(tb, buf, len);
|
||||
|
||||
kfree(tb);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Lock a trusted key, by extending a selected PCR.
|
||||
*
|
||||
|
@ -413,8 +381,8 @@ static int pcrlock(const int pcrnum)
|
|||
|
||||
if (!capable(CAP_SYS_ADMIN))
|
||||
return -EPERM;
|
||||
ret = my_get_random(hash, SHA1_DIGEST_SIZE);
|
||||
if (ret < 0)
|
||||
ret = tpm_get_random(TPM_ANY_NUM, hash, SHA1_DIGEST_SIZE);
|
||||
if (ret != SHA1_DIGEST_SIZE)
|
||||
return ret;
|
||||
return tpm_pcr_extend(TPM_ANY_NUM, pcrnum, hash) ? -EINVAL : 0;
|
||||
}
|
||||
|
@ -429,8 +397,8 @@ static int osap(struct tpm_buf *tb, struct osapsess *s,
|
|||
unsigned char ononce[TPM_NONCE_SIZE];
|
||||
int ret;
|
||||
|
||||
ret = tpm_get_random(tb, ononce, TPM_NONCE_SIZE);
|
||||
if (ret < 0)
|
||||
ret = tpm_get_random(TPM_ANY_NUM, ononce, TPM_NONCE_SIZE);
|
||||
if (ret != TPM_NONCE_SIZE)
|
||||
return ret;
|
||||
|
||||
INIT_BUF(tb);
|
||||
|
@ -524,8 +492,8 @@ static int tpm_seal(struct tpm_buf *tb, uint16_t keytype,
|
|||
if (ret < 0)
|
||||
goto out;
|
||||
|
||||
ret = tpm_get_random(tb, td->nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret < 0)
|
||||
ret = tpm_get_random(TPM_ANY_NUM, td->nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret != TPM_NONCE_SIZE)
|
||||
goto out;
|
||||
ordinal = htonl(TPM_ORD_SEAL);
|
||||
datsize = htonl(datalen);
|
||||
|
@ -634,8 +602,8 @@ static int tpm_unseal(struct tpm_buf *tb,
|
|||
|
||||
ordinal = htonl(TPM_ORD_UNSEAL);
|
||||
keyhndl = htonl(SRKHANDLE);
|
||||
ret = tpm_get_random(tb, nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret < 0) {
|
||||
ret = tpm_get_random(TPM_ANY_NUM, nonceodd, TPM_NONCE_SIZE);
|
||||
if (ret != TPM_NONCE_SIZE) {
|
||||
pr_info("trusted_key: tpm_get_random failed (%d)\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
@ -935,6 +903,7 @@ static int trusted_instantiate(struct key *key, const void *data,
|
|||
char *datablob;
|
||||
int ret = 0;
|
||||
int key_cmd;
|
||||
size_t key_len;
|
||||
|
||||
if (datalen <= 0 || datalen > 32767 || !data)
|
||||
return -EINVAL;
|
||||
|
@ -974,8 +943,9 @@ static int trusted_instantiate(struct key *key, const void *data,
|
|||
pr_info("trusted_key: key_unseal failed (%d)\n", ret);
|
||||
break;
|
||||
case Opt_new:
|
||||
ret = my_get_random(payload->key, payload->key_len);
|
||||
if (ret < 0) {
|
||||
key_len = payload->key_len;
|
||||
ret = tpm_get_random(TPM_ANY_NUM, payload->key, key_len);
|
||||
if (ret != key_len) {
|
||||
pr_info("trusted_key: key_create failed (%d)\n", ret);
|
||||
goto out;
|
||||
}
|
||||
|
|
|
@ -136,11 +136,23 @@ int __init register_security(struct security_operations *ops)
|
|||
|
||||
int security_ptrace_access_check(struct task_struct *child, unsigned int mode)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
int rc;
|
||||
rc = yama_ptrace_access_check(child, mode);
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
return security_ops->ptrace_access_check(child, mode);
|
||||
}
|
||||
|
||||
int security_ptrace_traceme(struct task_struct *parent)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
int rc;
|
||||
rc = yama_ptrace_traceme(parent);
|
||||
if (rc)
|
||||
return rc;
|
||||
#endif
|
||||
return security_ops->ptrace_traceme(parent);
|
||||
}
|
||||
|
||||
|
@ -559,6 +571,9 @@ int security_inode_setxattr(struct dentry *dentry, const char *name,
|
|||
if (unlikely(IS_PRIVATE(dentry->d_inode)))
|
||||
return 0;
|
||||
ret = security_ops->inode_setxattr(dentry, name, value, size, flags);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ima_inode_setxattr(dentry, name, value, size);
|
||||
if (ret)
|
||||
return ret;
|
||||
return evm_inode_setxattr(dentry, name, value, size);
|
||||
|
@ -594,6 +609,9 @@ int security_inode_removexattr(struct dentry *dentry, const char *name)
|
|||
if (unlikely(IS_PRIVATE(dentry->d_inode)))
|
||||
return 0;
|
||||
ret = security_ops->inode_removexattr(dentry, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
ret = ima_inode_removexattr(dentry, name);
|
||||
if (ret)
|
||||
return ret;
|
||||
return evm_inode_removexattr(dentry, name);
|
||||
|
@ -761,6 +779,9 @@ int security_task_create(unsigned long clone_flags)
|
|||
|
||||
void security_task_free(struct task_struct *task)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
yama_task_free(task);
|
||||
#endif
|
||||
security_ops->task_free(task);
|
||||
}
|
||||
|
||||
|
@ -876,6 +897,12 @@ int security_task_wait(struct task_struct *p)
|
|||
int security_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
#ifdef CONFIG_SECURITY_YAMA_STACKED
|
||||
int rc;
|
||||
rc = yama_task_prctl(option, arg2, arg3, arg4, arg5);
|
||||
if (rc != -ENOSYS)
|
||||
return rc;
|
||||
#endif
|
||||
return security_ops->task_prctl(option, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
|
|
|
@ -1691,40 +1691,19 @@ static int smack_task_kill(struct task_struct *p, struct siginfo *info,
|
|||
* smack_task_wait - Smack access check for waiting
|
||||
* @p: task to wait for
|
||||
*
|
||||
* Returns 0 if current can wait for p, error code otherwise
|
||||
* Returns 0
|
||||
*/
|
||||
static int smack_task_wait(struct task_struct *p)
|
||||
{
|
||||
struct smk_audit_info ad;
|
||||
char *sp = smk_of_current();
|
||||
char *tsp = smk_of_forked(task_security(p));
|
||||
int rc;
|
||||
|
||||
/* we don't log here, we can be overriden */
|
||||
rc = smk_access(tsp, sp, MAY_WRITE, NULL);
|
||||
if (rc == 0)
|
||||
goto out_log;
|
||||
|
||||
/*
|
||||
* Allow the operation to succeed if either task
|
||||
* has privilege to perform operations that might
|
||||
* account for the smack labels having gotten to
|
||||
* be different in the first place.
|
||||
*
|
||||
* This breaks the strict subject/object access
|
||||
* control ideal, taking the object's privilege
|
||||
* state into account in the decision as well as
|
||||
* the smack value.
|
||||
* Allow the operation to succeed.
|
||||
* Zombies are bad.
|
||||
* In userless environments (e.g. phones) programs
|
||||
* get marked with SMACK64EXEC and even if the parent
|
||||
* and child shouldn't be talking the parent still
|
||||
* may expect to know when the child exits.
|
||||
*/
|
||||
if (smack_privileged(CAP_MAC_OVERRIDE) ||
|
||||
has_capability(p, CAP_MAC_OVERRIDE))
|
||||
rc = 0;
|
||||
/* we log only if we didn't get overriden */
|
||||
out_log:
|
||||
smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK);
|
||||
smk_ad_setfield_u_tsk(&ad, p);
|
||||
smack_log(tsp, sp, MAY_WRITE, rc, &ad);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -2705,9 +2684,7 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value)
|
|||
static int smack_setprocattr(struct task_struct *p, char *name,
|
||||
void *value, size_t size)
|
||||
{
|
||||
int rc;
|
||||
struct task_smack *tsp;
|
||||
struct task_smack *oldtsp;
|
||||
struct cred *new;
|
||||
char *newsmack;
|
||||
|
||||
|
@ -2737,21 +2714,13 @@ static int smack_setprocattr(struct task_struct *p, char *name,
|
|||
if (newsmack == smack_known_web.smk_known)
|
||||
return -EPERM;
|
||||
|
||||
oldtsp = p->cred->security;
|
||||
new = prepare_creds();
|
||||
if (new == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
tsp = new_task_smack(newsmack, oldtsp->smk_forked, GFP_KERNEL);
|
||||
if (tsp == NULL) {
|
||||
kfree(new);
|
||||
return -ENOMEM;
|
||||
}
|
||||
rc = smk_copy_rules(&tsp->smk_rules, &oldtsp->smk_rules, GFP_KERNEL);
|
||||
if (rc != 0)
|
||||
return rc;
|
||||
tsp = new->security;
|
||||
tsp->smk_task = newsmack;
|
||||
|
||||
new->security = tsp;
|
||||
commit_creds(new);
|
||||
return size;
|
||||
}
|
||||
|
|
|
@ -49,6 +49,7 @@ enum smk_inos {
|
|||
SMK_LOAD_SELF2 = 15, /* load task specific rules with long labels */
|
||||
SMK_ACCESS2 = 16, /* make an access check with long labels */
|
||||
SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */
|
||||
SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */
|
||||
};
|
||||
|
||||
/*
|
||||
|
@ -1991,6 +1992,77 @@ static const struct file_operations smk_access2_ops = {
|
|||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_write_revoke_subj - write() for /smack/revoke-subject
|
||||
* @file: file pointer
|
||||
* @buf: data from user space
|
||||
* @count: bytes sent
|
||||
* @ppos: where to start - must be 0
|
||||
*/
|
||||
static ssize_t smk_write_revoke_subj(struct file *file, const char __user *buf,
|
||||
size_t count, loff_t *ppos)
|
||||
{
|
||||
char *data = NULL;
|
||||
const char *cp = NULL;
|
||||
struct smack_known *skp;
|
||||
struct smack_rule *sp;
|
||||
struct list_head *rule_list;
|
||||
struct mutex *rule_lock;
|
||||
int rc = count;
|
||||
|
||||
if (*ppos != 0)
|
||||
return -EINVAL;
|
||||
|
||||
if (!smack_privileged(CAP_MAC_ADMIN))
|
||||
return -EPERM;
|
||||
|
||||
if (count == 0 || count > SMK_LONGLABEL)
|
||||
return -EINVAL;
|
||||
|
||||
data = kzalloc(count, GFP_KERNEL);
|
||||
if (data == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
if (copy_from_user(data, buf, count) != 0) {
|
||||
rc = -EFAULT;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
cp = smk_parse_smack(data, count);
|
||||
if (cp == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
skp = smk_find_entry(cp);
|
||||
if (skp == NULL) {
|
||||
rc = -EINVAL;
|
||||
goto free_out;
|
||||
}
|
||||
|
||||
rule_list = &skp->smk_rules;
|
||||
rule_lock = &skp->smk_rules_lock;
|
||||
|
||||
mutex_lock(rule_lock);
|
||||
|
||||
list_for_each_entry_rcu(sp, rule_list, list)
|
||||
sp->smk_access = 0;
|
||||
|
||||
mutex_unlock(rule_lock);
|
||||
|
||||
free_out:
|
||||
kfree(data);
|
||||
kfree(cp);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static const struct file_operations smk_revoke_subj_ops = {
|
||||
.write = smk_write_revoke_subj,
|
||||
.read = simple_transaction_read,
|
||||
.release = simple_transaction_release,
|
||||
.llseek = generic_file_llseek,
|
||||
};
|
||||
|
||||
/**
|
||||
* smk_fill_super - fill the /smackfs superblock
|
||||
* @sb: the empty superblock
|
||||
|
@ -2037,6 +2109,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent)
|
|||
"access2", &smk_access2_ops, S_IRUGO|S_IWUGO},
|
||||
[SMK_CIPSO2] = {
|
||||
"cipso2", &smk_cipso2_ops, S_IRUGO|S_IWUSR},
|
||||
[SMK_REVOKE_SUBJ] = {
|
||||
"revoke-subject", &smk_revoke_subj_ops,
|
||||
S_IRUGO|S_IWUSR},
|
||||
/* last one */
|
||||
{""}
|
||||
};
|
||||
|
|
|
@ -11,3 +11,11 @@ config SECURITY_YAMA
|
|||
Further information can be found in Documentation/security/Yama.txt.
|
||||
|
||||
If you are unsure how to answer this question, answer N.
|
||||
|
||||
config SECURITY_YAMA_STACKED
|
||||
bool "Yama stacked with other LSMs"
|
||||
depends on SECURITY_YAMA
|
||||
default n
|
||||
help
|
||||
When Yama is built into the kernel, force it to stack with the
|
||||
selected primary LSM.
|
||||
|
|
|
@ -100,7 +100,7 @@ static void yama_ptracer_del(struct task_struct *tracer,
|
|||
* yama_task_free - check for task_pid to remove from exception list
|
||||
* @task: task being removed
|
||||
*/
|
||||
static void yama_task_free(struct task_struct *task)
|
||||
void yama_task_free(struct task_struct *task)
|
||||
{
|
||||
yama_ptracer_del(task, task);
|
||||
}
|
||||
|
@ -116,7 +116,7 @@ static void yama_task_free(struct task_struct *task)
|
|||
* Return 0 on success, -ve on error. -ENOSYS is returned when Yama
|
||||
* does not handle the given option.
|
||||
*/
|
||||
static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
||||
unsigned long arg4, unsigned long arg5)
|
||||
{
|
||||
int rc;
|
||||
|
@ -143,7 +143,7 @@ static int yama_task_prctl(int option, unsigned long arg2, unsigned long arg3,
|
|||
if (arg2 == 0) {
|
||||
yama_ptracer_del(NULL, myself);
|
||||
rc = 0;
|
||||
} else if (arg2 == PR_SET_PTRACER_ANY) {
|
||||
} else if (arg2 == PR_SET_PTRACER_ANY || (int)arg2 == -1) {
|
||||
rc = yama_ptracer_add(NULL, myself);
|
||||
} else {
|
||||
struct task_struct *tracer;
|
||||
|
@ -243,7 +243,7 @@ static int ptracer_exception_found(struct task_struct *tracer,
|
|||
*
|
||||
* Returns 0 if following the ptrace is allowed, -ve on error.
|
||||
*/
|
||||
static int yama_ptrace_access_check(struct task_struct *child,
|
||||
int yama_ptrace_access_check(struct task_struct *child,
|
||||
unsigned int mode)
|
||||
{
|
||||
int rc;
|
||||
|
@ -293,7 +293,7 @@ static int yama_ptrace_access_check(struct task_struct *child,
|
|||
*
|
||||
* Returns 0 if following the ptrace is allowed, -ve on error.
|
||||
*/
|
||||
static int yama_ptrace_traceme(struct task_struct *parent)
|
||||
int yama_ptrace_traceme(struct task_struct *parent)
|
||||
{
|
||||
int rc;
|
||||
|
||||
|
@ -324,6 +324,7 @@ static int yama_ptrace_traceme(struct task_struct *parent)
|
|||
return rc;
|
||||
}
|
||||
|
||||
#ifndef CONFIG_SECURITY_YAMA_STACKED
|
||||
static struct security_operations yama_ops = {
|
||||
.name = "yama",
|
||||
|
||||
|
@ -332,6 +333,7 @@ static struct security_operations yama_ops = {
|
|||
.task_prctl = yama_task_prctl,
|
||||
.task_free = yama_task_free,
|
||||
};
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
static int yama_dointvec_minmax(struct ctl_table *table, int write,
|
||||
|
@ -378,13 +380,17 @@ static struct ctl_table yama_sysctl_table[] = {
|
|||
|
||||
static __init int yama_init(void)
|
||||
{
|
||||
#ifndef CONFIG_SECURITY_YAMA_STACKED
|
||||
if (!security_module_enable(&yama_ops))
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
printk(KERN_INFO "Yama: becoming mindful.\n");
|
||||
|
||||
#ifndef CONFIG_SECURITY_YAMA_STACKED
|
||||
if (register_security(&yama_ops))
|
||||
panic("Yama: kernel registration failed.\n");
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_SYSCTL
|
||||
if (!register_sysctl_paths(yama_sysctl_path, yama_sysctl_table))
|
||||
|
|
Загрузка…
Ссылка в новой задаче