diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-21 09:06:02 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2014-01-21 09:06:02 -0800 |
commit | fb2e2c85375a0380d6818f153ffa2ae9ebbd055f (patch) | |
tree | cf8498a01357c220e4d664ff67125f60146f0da3 | |
parent | ec513b16c480c6cdda1e3d597e611eafca05227b (diff) | |
parent | 923b49ff69fcbffe6f8b2739de218c45544392a7 (diff) |
Merge branch 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security
Pull security layer updates from James Morris:
"Changes for this kernel include maintenance updates for Smack, SELinux
(and several networking fixes), IMA and TPM"
* 'for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/jmorris/linux-security: (39 commits)
SELinux: Fix memory leak upon loading policy
tpm/tpm-sysfs: active_show() can be static
tpm: tpm_tis: Fix compile problems with CONFIG_PM_SLEEP/CONFIG_PNP
tpm: Make tpm-dev allocate a per-file structure
tpm: Use the ops structure instead of a copy in tpm_vendor_specific
tpm: Create a tpm_class_ops structure and use it in the drivers
tpm: Pull all driver sysfs code into tpm-sysfs.c
tpm: Move sysfs functions from tpm-interface to tpm-sysfs
tpm: Pull everything related to /dev/tpmX into tpm-dev.c
char: tpm: nuvoton: remove unused variable
tpm: MAINTAINERS: Cleanup TPM Maintainers file
tpm/tpm_i2c_atmel: fix coccinelle warnings
tpm/tpm_ibmvtpm: fix unreachable code warning (smatch warning)
tpm/tpm_i2c_stm_st33: Check return code of get_burstcount
tpm/tpm_ppi: Check return value of acpi_get_name
tpm/tpm_ppi: Do not compare strcmp(a,b) == -1
ima: remove unneeded size_limit argument from ima_eventdigest_init_common()
ima: update IMA-templates.txt documentation
ima: pass HASH_ALGO__LAST as hash algo in ima_eventdigest_init()
ima: change the default hash algorithm to SHA1 in ima_eventdigest_ng_init()
...
30 files changed, 1007 insertions, 1041 deletions
diff --git a/Documentation/security/IMA-templates.txt b/Documentation/security/IMA-templates.txt index a777e5f1df5b..a4e102dddfea 100644 --- a/Documentation/security/IMA-templates.txt +++ b/Documentation/security/IMA-templates.txt @@ -67,12 +67,14 @@ descriptors by adding their identifier to the format string - 'd-ng': the digest of the event, calculated with an arbitrary hash algorithm (field format: [<hash algo>:]digest, where the digest prefix is shown only if the hash algorithm is not SHA1 or MD5); - - 'n-ng': the name of the event, without size limitations. + - 'n-ng': the name of the event, without size limitations; + - 'sig': the file signature. Below, there is the list of defined template descriptors: - "ima": its format is 'd|n'; - - "ima-ng" (default): its format is 'd-ng|n-ng'. + - "ima-ng" (default): its format is 'd-ng|n-ng'; + - "ima-sig": its format is 'd-ng|n-ng|sig'. diff --git a/MAINTAINERS b/MAINTAINERS index 88ae0eca7cd8..5c214024f60a 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -8746,14 +8746,10 @@ S: Odd fixes F: drivers/media/usb/tm6000/ TPM DEVICE DRIVER -M: Leonidas Da Silva Barbosa <leosilva@linux.vnet.ibm.com> -M: Ashley Lai <ashley@ashleylai.com> M: Peter Huewe <peterhuewe@gmx.de> -M: Rajiv Andrade <mail@srajiv.net> -W: http://tpmdd.sourceforge.net +M: Ashley Lai <ashley@ashleylai.com> M: Marcel Selhorst <tpmdd@selhorst.net> -M: Sirrix AG <tpmdd@sirrix.com> -W: http://www.sirrix.com +W: http://tpmdd.sourceforge.net L: tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers) S: Maintained F: drivers/char/tpm/ diff --git a/drivers/char/tpm/Makefile b/drivers/char/tpm/Makefile index b80a4000daee..4d85dd681b81 100644 --- a/drivers/char/tpm/Makefile +++ b/drivers/char/tpm/Makefile @@ -2,7 +2,7 @@ # Makefile for the kernel tpm device drivers. # obj-$(CONFIG_TCG_TPM) += tpm.o -tpm-y := tpm-interface.o +tpm-y := tpm-interface.o tpm-dev.o tpm-sysfs.o tpm-$(CONFIG_ACPI) += tpm_ppi.o ifdef CONFIG_ACPI diff --git a/drivers/char/tpm/tpm-dev.c b/drivers/char/tpm/tpm-dev.c new file mode 100644 index 000000000000..d9b774e02a1f --- /dev/null +++ b/drivers/char/tpm/tpm-dev.c @@ -0,0 +1,213 @@ +/* + * Copyright (C) 2004 IBM Corporation + * Authors: + * Leendert van Doorn <leendert@watson.ibm.com> + * Dave Safford <safford@watson.ibm.com> + * Reiner Sailer <sailer@watson.ibm.com> + * Kylene Hall <kjhall@us.ibm.com> + * + * Copyright (C) 2013 Obsidian Research Corp + * Jason Gunthorpe <jgunthorpe@obsidianresearch.com> + * + * Device file system interface to the TPM + * + * 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/miscdevice.h> +#include <linux/slab.h> +#include <linux/uaccess.h> +#include "tpm.h" + +struct file_priv { + struct tpm_chip *chip; + + /* Data passed to and from the tpm via the read/write calls */ + atomic_t data_pending; + struct mutex buffer_mutex; + + struct timer_list user_read_timer; /* user needs to claim result */ + struct work_struct work; + + u8 data_buffer[TPM_BUFSIZE]; +}; + +static void user_reader_timeout(unsigned long ptr) +{ + struct file_priv *priv = (struct file_priv *)ptr; + + schedule_work(&priv->work); +} + +static void timeout_work(struct work_struct *work) +{ + struct file_priv *priv = container_of(work, struct file_priv, work); + + mutex_lock(&priv->buffer_mutex); + atomic_set(&priv->data_pending, 0); + memset(priv->data_buffer, 0, sizeof(priv->data_buffer)); + mutex_unlock(&priv->buffer_mutex); +} + +static int tpm_open(struct inode *inode, struct file *file) +{ + struct miscdevice *misc = file->private_data; + struct tpm_chip *chip = container_of(misc, struct tpm_chip, + vendor.miscdev); + struct file_priv *priv; + + /* It's assured that the chip will be opened just once, + * by the check of is_open variable, which is protected + * by driver_lock. */ + if (test_and_set_bit(0, &chip->is_open)) { + dev_dbg(chip->dev, "Another process owns this TPM\n"); + return -EBUSY; + } + + priv = kzalloc(sizeof(*priv), GFP_KERNEL); + if (priv == NULL) { + clear_bit(0, &chip->is_open); + return -ENOMEM; + } + + priv->chip = chip; + atomic_set(&priv->data_pending, 0); + mutex_init(&priv->buffer_mutex); + setup_timer(&priv->user_read_timer, user_reader_timeout, + (unsigned long)priv); + INIT_WORK(&priv->work, timeout_work); + + file->private_data = priv; + get_device(chip->dev); + return 0; +} + +static ssize_t tpm_read(struct file *file, char __user *buf, + size_t size, loff_t *off) +{ + struct file_priv *priv = file->private_data; + ssize_t ret_size; + int rc; + + del_singleshot_timer_sync(&priv->user_read_timer); + flush_work(&priv->work); + ret_size = atomic_read(&priv->data_pending); + if (ret_size > 0) { /* relay data */ + ssize_t orig_ret_size = ret_size; + if (size < ret_size) + ret_size = size; + + mutex_lock(&priv->buffer_mutex); + rc = copy_to_user(buf, priv->data_buffer, ret_size); + memset(priv->data_buffer, 0, orig_ret_size); + if (rc) + ret_size = -EFAULT; + + mutex_unlock(&priv->buffer_mutex); + } + + atomic_set(&priv->data_pending, 0); + + return ret_size; +} + +static ssize_t tpm_write(struct file *file, const char __user *buf, + size_t size, loff_t *off) +{ + struct file_priv *priv = file->private_data; + size_t in_size = size; + ssize_t out_size; + + /* cannot perform a write until the read has cleared + either via tpm_read or a user_read_timer timeout. + This also prevents splitted buffered writes from blocking here. + */ + if (atomic_read(&priv->data_pending) != 0) + return -EBUSY; + + if (in_size > TPM_BUFSIZE) + return -E2BIG; + + mutex_lock(&priv->buffer_mutex); + + if (copy_from_user + (priv->data_buffer, (void __user *) buf, in_size)) { + mutex_unlock(&priv->buffer_mutex); + return -EFAULT; + } + + /* atomic tpm command send and result receive */ + out_size = tpm_transmit(priv->chip, priv->data_buffer, + sizeof(priv->data_buffer)); + if (out_size < 0) { + mutex_unlock(&priv->buffer_mutex); + return out_size; + } + + atomic_set(&priv->data_pending, out_size); + mutex_unlock(&priv->buffer_mutex); + + /* Set a timeout by which the reader must come claim the result */ + mod_timer(&priv->user_read_timer, jiffies + (60 * HZ)); + + return in_size; +} + +/* + * Called on file close + */ +static int tpm_release(struct inode *inode, struct file *file) +{ + struct file_priv *priv = file->private_data; + + del_singleshot_timer_sync(&priv->user_read_timer); + flush_work(&priv->work); + file->private_data = NULL; + atomic_set(&priv->data_pending, 0); + clear_bit(0, &priv->chip->is_open); + put_device(priv->chip->dev); + kfree(priv); + return 0; +} + +static const struct file_operations tpm_fops = { + .owner = THIS_MODULE, + .llseek = no_llseek, + .open = tpm_open, + .read = tpm_read, + .write = tpm_write, + .release = tpm_release, +}; + +int tpm_dev_add_device(struct tpm_chip *chip) +{ + int rc; + + chip->vendor.miscdev.fops = &tpm_fops; + if (chip->dev_num == 0) + chip->vendor.miscdev.minor = TPM_MINOR; + else + chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR; + + chip->vendor.miscdev.name = chip->devname; + chip->vendor.miscdev.parent = chip->dev; + + rc = misc_register(&chip->vendor.miscdev); + if (rc) { + chip->vendor.miscdev.name = NULL; + dev_err(chip->dev, + "unable to misc_register %s, minor %d err=%d\n", + chip->vendor.miscdev.name, + chip->vendor.miscdev.minor, rc); + } + return rc; +} + +void tpm_dev_del_device(struct tpm_chip *chip) +{ + if (chip->vendor.miscdev.name) + misc_deregister(&chip->vendor.miscdev); +} diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index 6ae41d337630..62e10fd1e1cb 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -32,13 +32,6 @@ #include "tpm.h" #include "tpm_eventlog.h" -enum tpm_duration { - TPM_SHORT = 0, - TPM_MEDIUM = 1, - TPM_LONG = 2, - TPM_UNDEFINED, -}; - #define TPM_MAX_ORDINAL 243 #define TSC_MAX_ORDINAL 12 #define TPM_PROTECTED_COMMAND 0x00 @@ -312,23 +305,6 @@ static const u8 tpm_ordinal_duration[TPM_MAX_ORDINAL] = { TPM_MEDIUM, }; -static void user_reader_timeout(unsigned long ptr) -{ - struct tpm_chip *chip = (struct tpm_chip *) ptr; - - schedule_work(&chip->work); -} - -static void timeout_work(struct work_struct *work) -{ - struct tpm_chip *chip = container_of(work, struct tpm_chip, work); - - mutex_lock(&chip->buffer_mutex); - atomic_set(&chip->data_pending, 0); - memset(chip->data_buffer, 0, TPM_BUFSIZE); - mutex_unlock(&chip->buffer_mutex); -} - /* * Returns max number of jiffies to wait */ @@ -355,8 +331,8 @@ EXPORT_SYMBOL_GPL(tpm_calc_ordinal_duration); /* * Internal kernel interface to transmit TPM commands */ -static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, - size_t bufsiz) +ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, + size_t bufsiz) { ssize_t rc; u32 count, ordinal; @@ -377,7 +353,7 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, mutex_lock(&chip->tpm_mutex); - rc = chip->vendor.send(chip, (u8 *) buf, count); + rc = chip->ops->send(chip, (u8 *) buf, count); if (rc < 0) { dev_err(chip->dev, "tpm_transmit: tpm_send: error %zd\n", rc); @@ -389,12 +365,12 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, stop = jiffies + tpm_calc_ordinal_duration(chip, ordinal); do { - u8 status = chip->vendor.status(chip); - if ((status & chip->vendor.req_complete_mask) == - chip->vendor.req_complete_val) + u8 status = chip->ops->status(chip); + if ((status & chip->ops->req_complete_mask) == + chip->ops->req_complete_val) goto out_recv; - if (chip->vendor.req_canceled(chip, status)) { + if (chip->ops->req_canceled(chip, status)) { dev_err(chip->dev, "Operation Canceled\n"); rc = -ECANCELED; goto out; @@ -404,13 +380,13 @@ static ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, rmb(); } while (time_before(jiffies, stop)); - chip->vendor.cancel(chip); + chip->ops->cancel(chip); dev_err(chip->dev, "Operation Timed out\n"); rc = -ETIME; goto out; out_recv: - rc = chip->vendor.recv(chip, (u8 *) buf, bufsiz); + rc = chip->ops->recv(chip, (u8 *) buf, bufsiz); if (rc < 0) dev_err(chip->dev, "tpm_transmit: tpm_recv: error %zd\n", rc); @@ -422,24 +398,6 @@ out: #define TPM_DIGEST_SIZE 20 #define TPM_RET_CODE_IDX 6 -enum tpm_capabilities { - TPM_CAP_FLAG = cpu_to_be32(4), - TPM_CAP_PROP = cpu_to_be32(5), - CAP_VERSION_1_1 = cpu_to_be32(0x06), - CAP_VERSION_1_2 = cpu_to_be32(0x1A) -}; - -enum tpm_sub_capabilities { - TPM_CAP_PROP_PCR = cpu_to_be32(0x101), - TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103), - TPM_CAP_FLAG_PERM = cpu_to_be32(0x108), - TPM_CAP_FLAG_VOL = cpu_to_be32(0x109), - TPM_CAP_PROP_OWNER = cpu_to_be32(0x111), - TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115), - TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120), - -}; - static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, int len, const char *desc) { @@ -459,7 +417,6 @@ 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) @@ -659,70 +616,6 @@ static int tpm_continue_selftest(struct tpm_chip *chip) return rc; } -ssize_t tpm_show_enabled(struct device *dev, struct device_attribute *attr, - char *buf) -{ - cap_t cap; - ssize_t rc; - - rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent enabled state"); - if (rc) - return 0; - - rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); - return rc; -} -EXPORT_SYMBOL_GPL(tpm_show_enabled); - -ssize_t tpm_show_active(struct device *dev, struct device_attribute *attr, - char *buf) -{ - cap_t cap; - ssize_t rc; - - rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, - "attempting to determine the permanent active state"); - if (rc) - return 0; - - rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); - return rc; -} -EXPORT_SYMBOL_GPL(tpm_show_active); - -ssize_t tpm_show_owned(struct device *dev, struct device_attribute *attr, - char *buf) -{ - cap_t cap; - ssize_t rc; - - rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap, - "attempting to determine the owner state"); - if (rc) - return 0; - - rc = sprintf(buf, "%d\n", cap.owned); - return rc; -} -EXPORT_SYMBOL_GPL(tpm_show_owned); - -ssize_t tpm_show_temp_deactivated(struct device *dev, - struct device_attribute *attr, char *buf) -{ - cap_t cap; - ssize_t rc; - - rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap, - "attempting to determine the temporary state"); - if (rc) - return 0; - - rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); - return rc; -} -EXPORT_SYMBOL_GPL(tpm_show_temp_deactivated); - /* * tpm_chip_find_get - return tpm_chip for given chip number */ @@ -752,7 +645,7 @@ static struct tpm_input_header pcrread_header = { .ordinal = TPM_ORDINAL_PCRREAD }; -static int __tpm_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) +int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) { int rc; struct tpm_cmd_t cmd; @@ -787,7 +680,7 @@ int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) chip = tpm_chip_find_get(chip_num); if (chip == NULL) return -ENODEV; - rc = __tpm_pcr_read(chip, pcr_idx, res_buf); + rc = tpm_pcr_read_dev(chip, pcr_idx, res_buf); tpm_chip_put(chip); return rc; } @@ -911,196 +804,15 @@ int tpm_send(u32 chip_num, void *cmd, size_t buflen) } EXPORT_SYMBOL_GPL(tpm_send); -ssize_t tpm_show_pcrs(struct device *dev, struct device_attribute *attr, - char *buf) -{ - cap_t cap; - u8 digest[TPM_DIGEST_SIZE]; - ssize_t rc; - int i, j, num_pcrs; - char *str = buf; - struct tpm_chip *chip = dev_get_drvdata(dev); - - rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap, - "attempting to determine the number of PCRS"); - if (rc) - return 0; - - num_pcrs = be32_to_cpu(cap.num_pcrs); - for (i = 0; i < num_pcrs; i++) { - rc = __tpm_pcr_read(chip, i, digest); - if (rc) - break; - str += sprintf(str, "PCR-%02d: ", i); - for (j = 0; j < TPM_DIGEST_SIZE; j++) - str += sprintf(str, "%02X ", digest[j]); - str += sprintf(str, "\n"); - } - return str - buf; -} -EXPORT_SYMBOL_GPL(tpm_show_pcrs); - -#define READ_PUBEK_RESULT_SIZE 314 -#define TPM_ORD_READPUBEK cpu_to_be32(124) -static struct tpm_input_header tpm_readpubek_header = { - .tag = TPM_TAG_RQU_COMMAND, - .length = cpu_to_be32(30), - .ordinal = TPM_ORD_READPUBEK -}; - -ssize_t tpm_show_pubek(struct device *dev, struct device_attribute *attr, - char *buf) -{ - u8 *data; - struct tpm_cmd_t tpm_cmd; - ssize_t err; - int i, rc; - char *str = buf; - - struct tpm_chip *chip = dev_get_drvdata(dev); - - tpm_cmd.header.in = tpm_readpubek_header; - err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, - "attempting to read the PUBEK"); - if (err) - goto out; - - /* - ignore header 10 bytes - algorithm 32 bits (1 == RSA ) - encscheme 16 bits - sigscheme 16 bits - parameters (RSA 12->bytes: keybit, #primes, expbit) - keylenbytes 32 bits - 256 byte modulus - ignore checksum 20 bytes - */ - data = tpm_cmd.params.readpubek_out_buffer; - str += - sprintf(str, - "Algorithm: %02X %02X %02X %02X\n" - "Encscheme: %02X %02X\n" - "Sigscheme: %02X %02X\n" - "Parameters: %02X %02X %02X %02X " - "%02X %02X %02X %02X " - "%02X %02X %02X %02X\n" - "Modulus length: %d\n" - "Modulus:\n", - data[0], data[1], data[2], data[3], - data[4], data[5], - data[6], data[7], - data[12], data[13], data[14], data[15], - data[16], data[17], data[18], data[19], - data[20], data[21], data[22], data[23], - be32_to_cpu(*((__be32 *) (data + 24)))); - - for (i = 0; i < 256; i++) { - str += sprintf(str, "%02X ", data[i + 28]); - if ((i + 1) % 16 == 0) - str += sprintf(str, "\n"); - } -out: - rc = str - buf; - return rc; -} -EXPORT_SYMBOL_GPL(tpm_show_pubek); - - -ssize_t tpm_show_caps(struct device *dev, struct device_attribute *attr, - char *buf) -{ - cap_t cap; - ssize_t rc; - char *str = buf; - - rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap, - "attempting to determine the manufacturer"); - if (rc) - return 0; - str += sprintf(str, "Manufacturer: 0x%x\n", - be32_to_cpu(cap.manufacturer_id)); - - /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ - rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap, - "attempting to determine the 1.2 version"); - if (!rc) { - str += sprintf(str, - "TCG version: %d.%d\nFirmware version: %d.%d\n", - cap.tpm_version_1_2.Major, - cap.tpm_version_1_2.Minor, - cap.tpm_version_1_2.revMajor, - cap.tpm_version_1_2.revMinor); - } else { - /* Otherwise just use TPM_STRUCT_VER */ - rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap, - "attempting to determine the 1.1 version"); - if (rc) - return 0; - str += sprintf(str, - "TCG version: %d.%d\nFirmware version: %d.%d\n", - cap.tpm_version.Major, - cap.tpm_version.Minor, - cap.tpm_version.revMajor, - cap.tpm_version.revMinor); - } - - return str - buf; -} -EXPORT_SYMBOL_GPL(tpm_show_caps); - -ssize_t tpm_show_durations(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - - if (chip->vendor.duration[TPM_LONG] == 0) - return 0; - - return sprintf(buf, "%d %d %d [%s]\n", - jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), - jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), - jiffies_to_usecs(chip->vendor.duration[TPM_LONG]), - chip->vendor.duration_adjusted - ? "adjusted" : "original"); -} -EXPORT_SYMBOL_GPL(tpm_show_durations); - -ssize_t tpm_show_timeouts(struct device *dev, struct device_attribute *attr, - char *buf) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - - return sprintf(buf, "%d %d %d %d [%s]\n", - jiffies_to_usecs(chip->vendor.timeout_a), - jiffies_to_usecs(chip->vendor.timeout_b), - jiffies_to_usecs(chip->vendor.timeout_c), - jiffies_to_usecs(chip->vendor.timeout_d), - chip->vendor.timeout_adjusted - ? "adjusted" : "original"); -} -EXPORT_SYMBOL_GPL(tpm_show_timeouts); - -ssize_t tpm_store_cancel(struct device *dev, struct device_attribute *attr, - const char *buf, size_t count) -{ - struct tpm_chip *chip = dev_get_drvdata(dev); - if (chip == NULL) - return 0; - - chip->vendor.cancel(chip); - return count; -} -EXPORT_SYMBOL_GPL(tpm_store_cancel); - static bool wait_for_tpm_stat_cond(struct tpm_chip *chip, u8 mask, bool check_cancel, bool *canceled) { - u8 status = chip->vendor.status(chip); + u8 status = chip->ops->status(chip); *canceled = false; if ((status & mask) == mask) return true; - if (check_cancel && chip->vendor.req_canceled(chip, status)) { + if (check_cancel && chip->ops->req_canceled(chip, status)) { *canceled = true; return true; } @@ -1116,7 +828,7 @@ int wait_for_tpm_stat(struct tpm_chip *chip, u8 mask, unsigned long timeout, bool canceled = false; /* check current status */ - status = chip->vendor.status(chip); + status = chip->ops->status(chip); if ((status & mask) == mask) return 0; @@ -1143,7 +855,7 @@ again: } else { do { msleep(TPM_TIMEOUT); - status = chip->vendor.status(chip); + status = chip->ops->status(chip); if ((status & mask) == mask) return 0; } while (time_before(jiffies, stop)); @@ -1151,127 +863,6 @@ again: return -ETIME; } EXPORT_SYMBOL_GPL(wait_for_tpm_stat); -/* - * Device file system interface to the TPM - * - * It's assured that the chip will be opened just once, - * by the check of is_open variable, which is protected - * by driver_lock. - */ -int tpm_open(struct inode *inode, struct file *file) -{ - struct miscdevice *misc = file->private_data; - struct tpm_chip *chip = container_of(misc, struct tpm_chip, - vendor.miscdev); - - if (test_and_set_bit(0, &chip->is_open)) { - dev_dbg(chip->dev, "Another process owns this TPM\n"); - return -EBUSY; - } - - chip->data_buffer = kzalloc(TPM_BUFSIZE, GFP_KERNEL); - if (chip->data_buffer == NULL) { - clear_bit(0, &chip->is_open); - return -ENOMEM; - } - - atomic_set(&chip->data_pending, 0); - - file->private_data = chip; - get_device(chip->dev); - return 0; -} -EXPORT_SYMBOL_GPL(tpm_open); - -/* - * Called on file close - */ -int tpm_release(struct inode *inode, struct file *file) -{ - struct tpm_chip *chip = file->private_data; - - del_singleshot_timer_sync(&chip->user_read_timer); - flush_work(&chip->work); - file->private_data = NULL; - atomic_set(&chip->data_pending, 0); - kzfree(chip->data_buffer); - clear_bit(0, &chip->is_open); - put_device(chip->dev); - return 0; -} -EXPORT_SYMBOL_GPL(tpm_release); - -ssize_t tpm_write(struct file *file, const char __user *buf, - size_t size, loff_t *off) -{ - struct tpm_chip *chip = file->private_data; - size_t in_size = size; - ssize_t out_size; - - /* cannot perform a write until the read has cleared - either via tpm_read or a user_read_timer timeout. - This also prevents splitted buffered writes from blocking here. - */ - if (atomic_read(&chip->data_pending) != 0) - return -EBUSY; - - if (in_size > TPM_BUFSIZE) - return -E2BIG; - - mutex_lock(&chip->buffer_mutex); - - if (copy_from_user - (chip->data_buffer, (void __user *) buf, in_size)) { - mutex_unlock(&chip->buffer_mutex); - return -EFAULT; - } - - /* atomic tpm command send and result receive */ - out_size = tpm_transmit(chip, chip->data_buffer, TPM_BUFSIZE); - if (out_size < 0) { - mutex_unlock(&chip->buffer_mutex); - return out_size; - } - - atomic_set(&chip->data_pending, out_size); - mutex_unlock(&chip->buffer_mutex); - - /* Set a timeout by which the reader must come claim the result */ - mod_timer(&chip->user_read_timer, jiffies + (60 * HZ)); - - return in_size; -} -EXPORT_SYMBOL_GPL(tpm_write); - -ssize_t tpm_read(struct file *file, char __user *buf, - size_t size, loff_t *off) -{ - struct tpm_chip *chip = file->private_data; - ssize_t ret_size; - int rc; - - del_singleshot_timer_sync(&chip->user_read_timer); - flush_work(&chip->work); - ret_size = atomic_read(&chip->data_pending); - if (ret_size > 0) { /* relay data */ - ssize_t orig_ret_size = ret_size; - if (size < ret_size) - ret_size = size; - - mutex_lock(&chip->buffer_mutex); - rc = copy_to_user(buf, chip->data_buffer, ret_size); - memset(chip->data_buffer, 0, orig_ret_size); - if (rc) - ret_size = -EFAULT; - - mutex_unlock(&chip->buffer_mutex); - } - - atomic_set(&chip->data_pending, 0); - - return ret_size; -} -EXPORT_SYMBOL_GPL(tpm_read); void tpm_remove_hardware(struct device *dev) { @@ -1287,8 +878,8 @@ void tpm_remove_hardware(struct device *dev) spin_unlock(&driver_lock); synchronize_rcu(); - misc_deregister(&chip->vendor.miscdev); - sysfs_remove_group(&dev->kobj, chip->vendor.attr_group); + tpm_dev_del_device(chip); + tpm_sysfs_del_device(chip); tpm_remove_ppi(&dev->kobj); tpm_bios_log_teardown(chip->bios_dir); @@ -1436,9 +1027,6 @@ void tpm_dev_vendor_release(struct tpm_chip *chip) if (!chip) return; - if (chip->vendor.release) - chip->vendor.release(chip->dev); - clear_bit(chip->dev_num, dev_mask); } EXPORT_SYMBOL_GPL(tpm_dev_vendor_release); @@ -1448,7 +1036,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); @@ -1460,7 +1048,6 @@ void tpm_dev_release(struct device *dev) chip->release(dev); kfree(chip); } -EXPORT_SYMBOL_GPL(tpm_dev_release); /* * Called from tpm_<specific>.c probe function only for devices @@ -1470,7 +1057,7 @@ EXPORT_SYMBOL_GPL(tpm_dev_release); * pci_disable_device */ struct tpm_chip *tpm_register_hardware(struct device *dev, - const struct tpm_vendor_specific *entry) + const struct tpm_class_ops *ops) { struct tpm_chip *chip; @@ -1480,56 +1067,35 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, if (chip == NULL) return NULL; - mutex_init(&chip->buffer_mutex); mutex_init(&chip->tpm_mutex); INIT_LIST_HEAD(&chip->list); - INIT_WORK(&chip->work, timeout_work); - - setup_timer(&chip->user_read_timer, user_reader_timeout, - (unsigned long)chip); - - memcpy(&chip->vendor, entry, sizeof(struct tpm_vendor_specific)); - + chip->ops = ops; chip->dev_num = find_first_zero_bit(dev_mask, TPM_NUM_DEVICES); if (chip->dev_num >= TPM_NUM_DEVICES) { dev_err(dev, "No available tpm device numbers\n"); goto out_free; - } else if (chip->dev_num == 0) - chip->vendor.miscdev.minor = TPM_MINOR; - else - chip->vendor.miscdev.minor = MISC_DYNAMIC_MINOR; + } set_bit(chip->dev_num, dev_mask); scnprintf(chip->devname, sizeof(chip->devname), "%s%d", "tpm", chip->dev_num); - chip->vendor.miscdev.name = chip->devname; - chip->vendor.miscdev.parent = dev; chip->dev = get_device(dev); chip->release = dev->release; dev->release = tpm_dev_release; dev_set_drvdata(dev, chip); - if (misc_register(&chip->vendor.miscdev)) { - dev_err(chip->dev, - "unable to misc_register %s, minor %d\n", - chip->vendor.miscdev.name, - chip->vendor.miscdev.minor); + if (tpm_dev_add_device(chip)) goto put_device; - } - if (sysfs_create_group(&dev->kobj, chip->vendor.attr_group)) { - misc_deregister(&chip->vendor.miscdev); - goto put_device; - } + if (tpm_sysfs_add_device(chip)) + goto del_misc; - if (tpm_add_ppi(&dev->kobj)) { - misc_deregister(&chip->vendor.miscdev); - goto put_device; - } + if (tpm_add_ppi(&dev->kobj)) + goto del_misc; chip->bios_dir = tpm_bios_log_setup(chip->devname); @@ -1540,6 +1106,8 @@ struct tpm_chip *tpm_register_hardware(struct device *dev, return chip; +del_misc: + tpm_dev_del_device(chip); put_device: put_device(chip->dev); out_free: diff --git a/drivers/char/tpm/tpm-sysfs.c b/drivers/char/tpm/tpm-sysfs.c new file mode 100644 index 000000000000..01730a27ae07 --- /dev/null +++ b/drivers/char/tpm/tpm-sysfs.c @@ -0,0 +1,318 @@ +/* + * Copyright (C) 2004 IBM Corporation + * Authors: + * Leendert van Doorn <leendert@watson.ibm.com> + * Dave Safford <safford@watson.ibm.com> + * Reiner Sailer <sailer@watson.ibm.com> + * Kylene Hall <kjhall@us.ibm.com> + * + * Copyright (C) 2013 Obsidian Research Corp + * Jason Gunthorpe <jgunthorpe@obsidianresearch.com> + * + * sysfs filesystem inspection interface to the TPM + * + * 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/device.h> +#include "tpm.h" + +/* XXX for now this helper is duplicated in tpm-interface.c */ +static ssize_t transmit_cmd(struct tpm_chip *chip, struct tpm_cmd_t *cmd, + int len, const char *desc) +{ + int err; + + len = tpm_transmit(chip, (u8 *) cmd, len); + if (len < 0) + return len; + else if (len < TPM_HEADER_SIZE) + return -EFAULT; + + err = be32_to_cpu(cmd->header.out.return_code); + if (err != 0 && desc) + dev_err(chip->dev, "A TPM error (%d) occurred %s\n", err, desc); + + return err; +} + +#define READ_PUBEK_RESULT_SIZE 314 +#define TPM_ORD_READPUBEK cpu_to_be32(124) +static struct tpm_input_header tpm_readpubek_header = { + .tag = TPM_TAG_RQU_COMMAND, + .length = cpu_to_be32(30), + .ordinal = TPM_ORD_READPUBEK +}; +static ssize_t pubek_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + u8 *data; + struct tpm_cmd_t tpm_cmd; + ssize_t err; + int i, rc; + char *str = buf; + + struct tpm_chip *chip = dev_get_drvdata(dev); + + tpm_cmd.header.in = tpm_readpubek_header; + err = transmit_cmd(chip, &tpm_cmd, READ_PUBEK_RESULT_SIZE, + "attempting to read the PUBEK"); + if (err) + goto out; + + /* + ignore header 10 bytes + algorithm 32 bits (1 == RSA ) + encscheme 16 bits + sigscheme 16 bits + parameters (RSA 12->bytes: keybit, #primes, expbit) + keylenbytes 32 bits + 256 byte modulus + ignore checksum 20 bytes + */ + data = tpm_cmd.params.readpubek_out_buffer; + str += + sprintf(str, + "Algorithm: %02X %02X %02X %02X\n" + "Encscheme: %02X %02X\n" + "Sigscheme: %02X %02X\n" + "Parameters: %02X %02X %02X %02X " + "%02X %02X %02X %02X " + "%02X %02X %02X %02X\n" + "Modulus length: %d\n" + "Modulus:\n", + data[0], data[1], data[2], data[3], + data[4], data[5], + data[6], data[7], + data[12], data[13], data[14], data[15], + data[16], data[17], data[18], data[19], + data[20], data[21], data[22], data[23], + be32_to_cpu(*((__be32 *) (data + 24)))); + + for (i = 0; i < 256; i++) { + str += sprintf(str, "%02X ", data[i + 28]); + if ((i + 1) % 16 == 0) + str += sprintf(str, "\n"); + } +out: + rc = str - buf; + return rc; +} +static DEVICE_ATTR_RO(pubek); + +static ssize_t pcrs_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cap_t cap; + u8 digest[TPM_DIGEST_SIZE]; + ssize_t rc; + int i, j, num_pcrs; + char *str = buf; + struct tpm_chip *chip = dev_get_drvdata(dev); + + rc = tpm_getcap(dev, TPM_CAP_PROP_PCR, &cap, + "attempting to determine the number of PCRS"); + if (rc) + return 0; + + num_pcrs = be32_to_cpu(cap.num_pcrs); + for (i = 0; i < num_pcrs; i++) { + rc = tpm_pcr_read_dev(chip, i, digest); + if (rc) + break; + str += sprintf(str, "PCR-%02d: ", i); + for (j = 0; j < TPM_DIGEST_SIZE; j++) + str += sprintf(str, "%02X ", digest[j]); + str += sprintf(str, "\n"); + } + return str - buf; +} +static DEVICE_ATTR_RO(pcrs); + +static ssize_t enabled_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cap_t cap; + ssize_t rc; + + rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent enabled state"); + if (rc) + return 0; + + rc = sprintf(buf, "%d\n", !cap.perm_flags.disable); + return rc; +} +static DEVICE_ATTR_RO(enabled); + +static ssize_t active_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cap_t cap; + ssize_t rc; + + rc = tpm_getcap(dev, TPM_CAP_FLAG_PERM, &cap, + "attempting to determine the permanent active state"); + if (rc) + return 0; + + rc = sprintf(buf, "%d\n", !cap.perm_flags.deactivated); + return rc; +} +static DEVICE_ATTR_RO(active); + +static ssize_t owned_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cap_t cap; + ssize_t rc; + + rc = tpm_getcap(dev, TPM_CAP_PROP_OWNER, &cap, + "attempting to determine the owner state"); + if (rc) + return 0; + + rc = sprintf(buf, "%d\n", cap.owned); + return rc; +} +static DEVICE_ATTR_RO(owned); + +static ssize_t temp_deactivated_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + cap_t cap; + ssize_t rc; + + rc = tpm_getcap(dev, TPM_CAP_FLAG_VOL, &cap, + "attempting to determine the temporary state"); + if (rc) + return 0; + + rc = sprintf(buf, "%d\n", cap.stclear_flags.deactivated); + return rc; +} +static DEVICE_ATTR_RO(temp_deactivated); + +static ssize_t caps_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + cap_t cap; + ssize_t rc; + char *str = buf; + + rc = tpm_getcap(dev, TPM_CAP_PROP_MANUFACTURER, &cap, + "attempting to determine the manufacturer"); + if (rc) + return 0; + str += sprintf(str, "Manufacturer: 0x%x\n", + be32_to_cpu(cap.manufacturer_id)); + + /* Try to get a TPM version 1.2 TPM_CAP_VERSION_INFO */ + rc = tpm_getcap(dev, CAP_VERSION_1_2, &cap, + "attempting to determine the 1.2 version"); + if (!rc) { + str += sprintf(str, + "TCG version: %d.%d\nFirmware version: %d.%d\n", + cap.tpm_version_1_2.Major, + cap.tpm_version_1_2.Minor, + cap.tpm_version_1_2.revMajor, + cap.tpm_version_1_2.revMinor); + } else { + /* Otherwise just use TPM_STRUCT_VER */ + rc = tpm_getcap(dev, CAP_VERSION_1_1, &cap, + "attempting to determine the 1.1 version"); + if (rc) + return 0; + str += sprintf(str, + "TCG version: %d.%d\nFirmware version: %d.%d\n", + cap.tpm_version.Major, + cap.tpm_version.Minor, + cap.tpm_version.revMajor, + cap.tpm_version.revMinor); + } + + return str - buf; +} +static DEVICE_ATTR_RO(caps); + +static ssize_t cancel_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + if (chip == NULL) + return 0; + + chip->ops->cancel(chip); + return count; +} +static DEVICE_ATTR_WO(cancel); + +static ssize_t durations_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + + if (chip->vendor.duration[TPM_LONG] == 0) + return 0; + + return sprintf(buf, "%d %d %d [%s]\n", + jiffies_to_usecs(chip->vendor.duration[TPM_SHORT]), + jiffies_to_usecs(chip->vendor.duration[TPM_MEDIUM]), + jiffies_to_usecs(chip->vendor.duration[TPM_LONG]), + chip->vendor.duration_adjusted + ? "adjusted" : "original"); +} +static DEVICE_ATTR_RO(durations); + +static ssize_t timeouts_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct tpm_chip *chip = dev_get_drvdata(dev); + + return sprintf(buf, "%d %d %d %d [%s]\n", + jiffies_to_usecs(chip->vendor.timeout_a), + jiffies_to_usecs(chip->vendor.timeout_b), + jiffies_to_usecs(chip->vendor.timeout_c), + jiffies_to_usecs(chip->vendor.timeout_d), + chip->vendor.timeout_adjusted + ? "adjusted" : "original"); +} +static DEVICE_ATTR_RO(timeouts); + +static struct attribute *tpm_dev_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 const struct attribute_group tpm_dev_group = { + .attrs = tpm_dev_attrs, +}; + +int tpm_sysfs_add_device(struct tpm_chip *chip) +{ + int err; + err = sysfs_create_group(&chip->dev->kobj, + &tpm_dev_group); + + if (err) + dev_err(chip->dev, + "failed to create sysfs attributes, %d\n", err); + return err; +} + +void tpm_sysfs_del_device(struct tpm_chip *chip) +{ + sysfs_remove_group(&chip->dev->kobj, &tpm_dev_group); +} diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f32847872193..e4d0888d2eab 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -46,6 +46,14 @@ enum tpm_addr { TPM_ADDR = 0x4E, }; +/* Indexes the duration array */ +enum tpm_duration { + TPM_SHORT = 0, + TPM_MEDIUM = 1, + TPM_LONG = 2, + TPM_UNDEFINED, +}; + #define TPM_WARN_RETRY 0x800 #define TPM_WARN_DOING_SELFTEST 0x802 #define TPM_ERR_DEACTIVATED 0x6 @@ -53,33 +61,9 @@ enum tpm_addr { #define TPM_ERR_INVALID_POSTINIT 38 #define TPM_HEADER_SIZE 10 -extern ssize_t tpm_show_pubek(struct device *, struct device_attribute *attr, - char *); -extern ssize_t tpm_show_pcrs(struct device *, struct device_attribute *attr, - char *); -extern ssize_t tpm_show_caps(struct device *, struct device_attribute *attr, - char *); -extern ssize_t tpm_store_cancel(struct device *, struct device_attribute *attr, - const char *, size_t); -extern ssize_t tpm_show_enabled(struct device *, struct device_attribute *attr, - char *); -extern ssize_t tpm_show_active(struct device *, struct device_attribute *attr, - char *); -extern ssize_t tpm_show_owned(struct device *, struct device_attribute *attr, - char *); -extern ssize_t tpm_show_temp_deactivated(struct device *, - struct device_attribute *attr, char *); -extern ssize_t tpm_show_durations(struct device *, - struct device_attribute *attr, char *); -extern ssize_t tpm_show_timeouts(struct device *, - struct device_attribute *attr, char *); - struct tpm_chip; struct tpm_vendor_specific { - const u8 req_complete_mask; - const u8 req_complete_val; - bool (*req_canceled)(struct tpm_chip *chip, u8 status); void __iomem *iobase; /* ioremapped address */ unsigned long base; /* TPM base address */ @@ -89,13 +73,7 @@ struct tpm_vendor_specific { int region_size; int have_region; - int (*recv) (struct tpm_chip *, u8 *, size_t); - int (*send) (struct tpm_chip *, u8 *, size_t); - void (*cancel) (struct tpm_chip *); - u8 (*status) (struct tpm_chip *); - void (*release) (struct device *); struct miscdevice miscdev; - struct attribute_group *attr_group; struct list_head list; int locality; unsigned long timeout_a, timeout_b, timeout_c, timeout_d; /* jiffies */ @@ -118,19 +96,13 @@ struct tpm_vendor_specific { struct tpm_chip { struct device *dev; /* Device stuff */ + const struct tpm_class_ops *ops; int dev_num; /* /dev/tpm# */ char devname[7]; unsigned long is_open; /* only one allowed */ int time_expired; - /* Data passed to and from the tpm via the read/write calls */ - u8 *data_buffer; - atomic_t data_pending; - struct mutex buffer_mutex; - - struct timer_list user_read_timer; /* user needs to claim result */ - struct work_struct work; struct mutex tpm_mutex; /* tpm is processing */ struct tpm_vendor_specific vendor; @@ -171,6 +143,8 @@ struct tpm_output_header { __be32 return_code; } __packed; +#define TPM_TAG_RQU_COMMAND cpu_to_be16(193) + struct stclear_flags_t { __be16 tag; u8 deactivated; @@ -244,6 +218,24 @@ typedef union { struct duration_t duration; } cap_t; +enum tpm_capabilities { + TPM_CAP_FLAG = cpu_to_be32(4), + TPM_CAP_PROP = cpu_to_be32(5), + CAP_VERSION_1_1 = cpu_to_be32(0x06), + CAP_VERSION_1_2 = cpu_to_be32(0x1A) +}; + +enum tpm_sub_capabilities { + TPM_CAP_PROP_PCR = cpu_to_be32(0x101), + TPM_CAP_PROP_MANUFACTURER = cpu_to_be32(0x103), + TPM_CAP_FLAG_PERM = cpu_to_be32(0x108), + TPM_CAP_FLAG_VOL = cpu_to_be32(0x109), + TPM_CAP_PROP_OWNER = cpu_to_be32(0x111), + TPM_CAP_PROP_TIS_TIMEOUT = cpu_to_be32(0x115), + TPM_CAP_PROP_TIS_DURATION = cpu_to_be32(0x120), + +}; + struct tpm_getcap_params_in { __be32 cap; __be32 subcap_size; @@ -323,25 +315,28 @@ struct tpm_cmd_t { ssize_t tpm_getcap(struct device *, __be32, cap_t *, const char *); +ssize_t tpm_transmit(struct tpm_chip *chip, const char *buf, + size_t bufsiz); extern int tpm_get_timeouts(struct tpm_chip *); extern void tpm_gen_interrupt(struct tpm_chip *); extern int tpm_do_selftest(struct tpm_chip *); extern unsigned long tpm_calc_ordinal_duration(struct tpm_chip *, u32); extern struct tpm_chip* tpm_register_hardware(struct device *, - const struct tpm_vendor_specific *); -extern int tpm_open(struct inode *, struct file *); -extern int tpm_release(struct inode *, struct file *); -extern void tpm_dev_release(struct device *dev); + const struct tpm_class_ops *ops); extern void tpm_dev_vendor_release(struct tpm_chip *); -extern ssize_t tpm_write(struct file *, const char __user *, size_t, - loff_t *); -extern ssize_t tpm_read(struct file *, char __user *, size_t, loff_t *); extern void tpm_remove_hardware(struct device *); 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 *, bool); +int tpm_dev_add_device(struct tpm_chip *chip); +void tpm_dev_del_device(struct tpm_chip *chip); +int tpm_sysfs_add_device(struct tpm_chip *chip); +void tpm_sysfs_del_device(struct tpm_chip *chip); + +int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); + #ifdef CONFIG_ACPI extern int tpm_add_ppi(struct kobject *); extern void tpm_remove_ppi(struct kobject *); diff --git a/drivers/char/tpm/tpm_atmel.c b/drivers/char/tpm/tpm_atmel.c index c9a528d25d22..6069d13ae4ac 100644 --- a/drivers/char/tpm/tpm_atmel.c +++ b/drivers/char/tpm/tpm_atmel.c @@ -121,31 +121,7 @@ static bool tpm_atml_req_canceled(struct tpm_chip *chip, u8 status) return (status == ATML_STATUS_READY); } -static const struct file_operations atmel_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(caps, S_IRUGO, tpm_show_caps, NULL); -static DEVICE_ATTR(cancel, S_IWUSR |S_IWGRP, NULL, tpm_store_cancel); - -static struct attribute* atmel_attrs[] = { - &dev_attr_pubek.attr, - &dev_attr_pcrs.attr, - &dev_attr_caps.attr, - &dev_attr_cancel.attr, - NULL, -}; - -static struct attribute_group atmel_attr_grp = { .attrs = atmel_attrs }; - -static const struct tpm_vendor_specific tpm_atmel = { +static const struct tpm_class_ops tpm_atmel = { .recv = tpm_atml_recv, .send = tpm_atml_send, .cancel = tpm_atml_cancel, @@ -153,8 +129,6 @@ static const struct tpm_vendor_specific tpm_atmel = { .req_complete_mask = ATML_STATUS_BUSY | ATML_STATUS_DATA_AVAIL, .req_complete_val = ATML_STATUS_DATA_AVAIL, .req_canceled = tpm_atml_req_canceled, - .attr_group = &atmel_attr_grp, - .miscdev = { .fops = &atmel_ops, }, }; static struct platform_device *pdev; diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index c3cd7fe481a1..77272925dee6 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -135,50 +135,12 @@ static u8 i2c_atmel_read_status(struct tpm_chip *chip) return ATMEL_STS_OK; } -static const struct file_operations i2c_atmel_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, 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 *i2c_atmel_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 i2c_atmel_attr_grp = { - .attrs = i2c_atmel_attrs -}; - static bool i2c_atmel_req_canceled(struct tpm_chip *chip, u8 status) { - return 0; + return false; } -static const struct tpm_vendor_specific i2c_atmel = { +static const struct tpm_class_ops i2c_atmel = { .status = i2c_atmel_read_status, .recv = i2c_atmel_recv, .send = i2c_atmel_send, @@ -186,8 +148,6 @@ static const struct tpm_vendor_specific i2c_atmel = { .req_complete_mask = ATMEL_STS_OK, .req_complete_val = ATMEL_STS_OK, .req_canceled = i2c_atmel_req_canceled, - .attr_group = &i2c_atmel_attr_grp, - .miscdev.fops = &i2c_atmel_ops, }; static int i2c_atmel_probe(struct i2c_client *client, diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index fefd2aa5c81e..52b9b2b2f300 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -566,45 +566,7 @@ static bool tpm_tis_i2c_req_canceled(struct tpm_chip *chip, u8 status) return (status == TPM_STS_COMMAND_READY); } -static const struct file_operations tis_ops = { - .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, 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 = { +static const struct tpm_class_ops tpm_tis_i2c = { .status = tpm_tis_i2c_status, .recv = tpm_tis_i2c_recv, .send = tpm_tis_i2c_send, @@ -612,8 +574,6 @@ static struct tpm_vendor_specific tpm_tis_i2c = { .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = tpm_tis_i2c_req_canceled, - .attr_group = &tis_attr_grp, - .miscdev.fops = &tis_ops, }; static int tpm_tis_i2c_init(struct device *dev) diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 6276fea01ff0..7b158efd49f7 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -178,7 +178,6 @@ static int i2c_nuvoton_wait_for_stat(struct tpm_chip *chip, u8 mask, u8 value, { if (chip->vendor.irq && queue) { s32 rc; - DEFINE_WAIT(wait); struct priv_data *priv = chip->vendor.priv; unsigned int cur_intrs = priv->intrs; @@ -456,45 +455,7 @@ static bool i2c_nuvoton_req_canceled(struct tpm_chip *chip, u8 status) return (status == TPM_STS_COMMAND_READY); } -static const struct file_operations i2c_nuvoton_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, 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 *i2c_nuvoton_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 i2c_nuvoton_attr_grp = { - .attrs = i2c_nuvoton_attrs -}; - -static const struct tpm_vendor_specific tpm_i2c = { +static const struct tpm_class_ops tpm_i2c = { .status = i2c_nuvoton_read_status, .recv = i2c_nuvoton_recv, .send = i2c_nuvoton_send, @@ -502,8 +463,6 @@ static const struct tpm_vendor_specific tpm_i2c = { .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = i2c_nuvoton_req_canceled, - .attr_group = &i2c_nuvoton_attr_grp, - .miscdev.fops = &i2c_nuvoton_ops, }; /* The only purpose for the handler is to signal to any waiting threads that diff --git a/drivers/char/tpm/tpm_i2c_stm_st33.c b/drivers/char/tpm/tpm_i2c_stm_st33.c index a0d6ceb5d005..5b0dd8ef74c0 100644 --- a/drivers/char/tpm/tpm_i2c_stm_st33.c +++ b/drivers/char/tpm/tpm_i2c_stm_st33.c @@ -410,6 +410,8 @@ static int recv_data(struct tpm_chip *chip, u8 *buf, size_t count) &chip->vendor.read_queue) == 0) { burstcnt = get_burstcount(chip); + if (burstcnt < 0) + return burstcnt; len = min_t(int, burstcnt, count - size); I2C_READ_DATA(client, TPM_DATA_FIFO, buf + size, len); size += len; @@ -451,7 +453,8 @@ static irqreturn_t tpm_ioserirq_handler(int irq, void *dev_id) static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, size_t len) { - u32 status, burstcnt = 0, i, size; + u32 status, i, size; + int burstcnt = 0; int ret; u8 data; struct i2c_client *client; @@ -482,6 +485,8 @@ static int tpm_stm_i2c_send(struct tpm_chip *chip, unsigned char *buf, for (i = 0; i < len - 1;) { burstcnt = get_burstcount(chip); + if (burstcnt < 0) + return burstcnt; size = min_t(int, len - i - 1, burstcnt); ret = I2C_WRITE_DATA(client, TPM_DATA_FIFO, buf, size); if (ret < 0) @@ -559,7 +564,7 @@ static int tpm_stm_i2c_recv(struct tpm_chip *chip, unsigned char *buf, } out: - chip->vendor.cancel(chip); + chip->ops->cancel(chip); release_locality(chip); return size; } @@ -569,40 +574,7 @@ static bool tpm_st33_i2c_req_canceled(struct tpm_chip *chip, u8 status) return (status == TPM_STS_COMMAND_READY); } -static const struct file_operations tpm_st33_i2c_fops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .read = tpm_read, - .write = tpm_write, - .open = tpm_open, - .release = tpm_release, -}; - -static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); -static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); -static DEVICE_ATTR(enabled, S_IRUGO, tpm_show_enabled, NULL); -static DEVICE_ATTR(active, S_IRUGO, tpm_show_active, NULL); -static DEVICE_ATTR(owned, S_IRUGO, tpm_show_owned, NULL); -static DEVICE_ATTR(temp_deactivated, S_IRUGO, tpm_show_temp_deactivated, NULL); -static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); -static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); - -static struct attribute *stm_tpm_attrs[] = { - &dev_attr_pubek.attr, - &dev_attr_pcrs.attr, - &dev_attr_enabled.attr, - &dev_attr_active.attr, - &dev_attr_owned.attr, - &dev_attr_temp_deactivated.attr, - &dev_attr_caps.attr, - &dev_attr_cancel.attr, NULL, -}; - -static struct attribute_group stm_tpm_attr_grp = { - .attrs = stm_tpm_attrs -}; - -static struct tpm_vendor_specific st_i2c_tpm = { +static const struct tpm_class_ops st_i2c_tpm = { .send = tpm_stm_i2c_send, .recv = tpm_stm_i2c_recv, .cancel = tpm_stm_i2c_cancel, @@ -610,8 +582,6 @@ static struct tpm_vendor_specific st_i2c_tpm = { .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = tpm_st33_i2c_req_canceled, - .attr_group = &stm_tpm_attr_grp, - .miscdev = {.fops = &tpm_st33_i2c_fops,}, }; static int interrupts; @@ -837,7 +807,7 @@ static int tpm_st33_i2c_pm_resume(struct device *dev) if (power_mgt) { gpio_set_value(pin_infos->io_lpcpd, 1); ret = wait_for_serirq_timeout(chip, - (chip->vendor.status(chip) & + (chip->ops->status(chip) & TPM_STS_VALID) == TPM_STS_VALID, chip->vendor.timeout_b); } else { diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 2783a42aa732..af74c57e5090 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -403,43 +403,7 @@ static bool tpm_ibmvtpm_req_canceled(struct tpm_chip *chip, u8 status) return (status == 0); } -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, 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 = { +static const struct tpm_class_ops tpm_ibmvtpm = { .recv = tpm_ibmvtpm_recv, .send = tpm_ibmvtpm_send, .cancel = tpm_ibmvtpm_cancel, @@ -447,8 +411,6 @@ static const struct tpm_vendor_specific tpm_ibmvtpm = { .req_complete_mask = 0, .req_complete_val = 0, .req_canceled = tpm_ibmvtpm_req_canceled, - .attr_group = &ibmvtpm_attr_grp, - .miscdev = { .fops = &ibmvtpm_ops, }, }; static const struct dev_pm_ops tpm_ibmvtpm_pm_ops = { @@ -507,7 +469,6 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, 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: diff --git a/drivers/char/tpm/tpm_infineon.c b/drivers/char/tpm/tpm_infineon.c index 2b480c2960bb..dc0a2554034e 100644 --- a/drivers/char/tpm/tpm_infineon.c +++ b/drivers/char/tpm/tpm_infineon.c @@ -371,39 +371,13 @@ static u8 tpm_inf_status(struct tpm_chip *chip) return tpm_data_in(STAT); } -static DEVICE_ATTR(pubek, S_IRUGO, tpm_show_pubek, NULL); -static DEVICE_ATTR(pcrs, S_IRUGO, tpm_show_pcrs, NULL); -static DEVICE_ATTR(caps, S_IRUGO, tpm_show_caps, NULL); -static DEVICE_ATTR(cancel, S_IWUSR | S_IWGRP, NULL, tpm_store_cancel); - -static struct attribute *inf_attrs[] = { - &dev_attr_pubek.attr, - &dev_attr_pcrs.attr, - &dev_attr_caps.attr, - &dev_attr_cancel.attr, - NULL, -}; - -static struct attribute_group inf_attr_grp = {.attrs = inf_attrs }; - -static const struct file_operations inf_ops = { - .owner = THIS_MODULE, - .llseek = no_llseek, - .open = tpm_open, - .read = tpm_read, - .write = tpm_write, - .release = tpm_release, -}; - -static const struct tpm_vendor_specific tpm_inf = { +static const struct tpm_class_ops tpm_inf = { .recv = tpm_inf_recv, .send = tpm_inf_send, .cancel = tpm_inf_cancel, .status = tpm_inf_status, .req_complete_mask = 0, .req_complete_val = 0, - .attr_group = &inf_attr_grp, - .miscdev = {.fops = &inf_ops,}, }; static const struct pnp_device_id tpm_inf_pnp_tbl[] = { diff --git a/drivers/char/tpm/tpm_nsc.c b/drivers/char/tpm/tpm_nsc.c index 770c46f8eb30..3179ec9cffdc 100644 --- a/drivers/char/tpm/tpm_nsc.c +++ b/drivers/char/tpm/tpm_nsc.c @@ -232,31 +232,7 @@ static bool tpm_nsc_req_canceled(struct tpm_chip *chip, u8 status) return (status == NSC_STATUS_RDY); } -static const struct file_operations nsc_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(caps, S_IRUGO, tpm_show_caps, NULL); -static DEVICE_ATTR(cancel, S_IWUSR|S_IWGRP, NULL, tpm_store_cancel); - -static struct attribute * nsc_attrs[] = { - &dev_attr_pubek.attr, - &dev_attr_pcrs.attr, - &dev_attr_caps.attr, - &dev_attr_cancel.attr, - NULL, -}; - -static struct attribute_group nsc_attr_grp = { .attrs = nsc_attrs }; - -static const struct tpm_vendor_specific tpm_nsc = { +static const struct tpm_class_ops tpm_nsc = { .recv = tpm_nsc_recv, .send = tpm_nsc_send, .cancel = tpm_nsc_cancel, @@ -264,8 +240,6 @@ static const struct tpm_vendor_specific tpm_nsc = { .req_complete_mask = NSC_STATUS_OBF, .req_complete_val = NSC_STATUS_OBF, .req_canceled = tpm_nsc_req_canceled, - .attr_group = &nsc_attr_grp, - .miscdev = { .fops = &nsc_ops, }, }; static struct platform_device *pdev = NULL; diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index e1f3337a0cf9..2db4419831be 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -172,7 +172,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, * is updated with function index from SUBREQ to SUBREQ2 since PPI * version 1.1 */ - if (strcmp(version, "1.1") == -1) + if (strcmp(version, "1.1") < 0) params[2].integer.value = TPM_PPI_FN_SUBREQ; else params[2].integer.value = TPM_PPI_FN_SUBREQ2; @@ -182,7 +182,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, * 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) { + if (strcmp(version, "1.2") < 0) { params[3].type = ACPI_TYPE_BUFFER; params[3].buffer.length = sizeof(req); sscanf(buf, "%d", &req); @@ -248,7 +248,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, * (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) { + if (strcmp(version, "1.2") < 0) { params[3].type = ACPI_TYPE_BUFFER; params[3].buffer.length = 0; params[3].buffer.pointer = NULL; @@ -390,7 +390,7 @@ static ssize_t show_ppi_operations(char *buf, u32 start, u32 end) kfree(output.pointer); output.length = ACPI_ALLOCATE_BUFFER; output.pointer = NULL; - if (strcmp(version, "1.2") == -1) + if (strcmp(version, "1.2") < 0) return -EPERM; params[2].integer.value = TPM_PPI_FN_GETOPR; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index 1b74459c0723..a9ed2270c25d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -432,45 +432,7 @@ static bool tpm_tis_req_canceled(struct tpm_chip *chip, u8 status) } } -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, 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 = { +static const struct tpm_class_ops tpm_tis = { .status = tpm_tis_status, .recv = tpm_tis_recv, .send = tpm_tis_send, @@ -478,9 +440,6 @@ static struct tpm_vendor_specific tpm_tis = { .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = tpm_tis_req_canceled, - .attr_group = &tis_attr_grp, - .miscdev = { - .fops = &tis_ops,}, }; static irqreturn_t tis_int_probe(int irq, void *dev_id) @@ -743,7 +702,7 @@ out_err: return rc; } -#if defined(CONFIG_PNP) || defined(CONFIG_PM_SLEEP) +#ifdef CONFIG_PM_SLEEP static void tpm_tis_reenable_interrupts(struct tpm_chip *chip) { u32 intmask; @@ -764,9 +723,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_PM_SLEEP static int tpm_tis_resume(struct device *dev) { struct tpm_chip *chip = dev_get_drvdata(dev); @@ -835,11 +792,9 @@ static struct pnp_driver tis_pnp_driver = { .id_table = tpm_pnp_tbl, .probe = tpm_tis_pnp_init, .remove = tpm_tis_pnp_remove, -#ifdef CONFIG_PM_SLEEP .driver = { .pm = &tpm_tis_pm, }, -#endif }; #define TIS_HID_USR_IDX sizeof(tpm_pnp_tbl)/sizeof(struct pnp_device_id) -2 diff --git a/drivers/char/tpm/xen-tpmfront.c b/drivers/char/tpm/xen-tpmfront.c index c8ff4df81779..92b097064df5 100644 --- a/drivers/char/tpm/xen-tpmfront.c +++ b/drivers/char/tpm/xen-tpmfront.c @@ -143,46 +143,7 @@ static int vtpm_recv(struct tpm_chip *chip, u8 *buf, size_t count) return length; } -static const struct file_operations vtpm_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, 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 *vtpm_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 vtpm_attr_grp = { - .attrs = vtpm_attrs, -}; - -static const struct tpm_vendor_specific tpm_vtpm = { +static const struct tpm_class_ops tpm_vtpm = { .status = vtpm_status, .recv = vtpm_recv, .send = vtpm_send, @@ -190,10 +151,6 @@ static const struct tpm_vendor_specific tpm_vtpm = { .req_complete_mask = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT, .req_complete_val = VTPM_STATUS_IDLE | VTPM_STATUS_RESULT, .req_canceled = vtpm_req_canceled, - .attr_group = &vtpm_attr_grp, - .miscdev = { - .fops = &vtpm_ops, - }, }; static irqreturn_t tpmif_interrupt(int dummy, void *dev_id) diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 9a9051bb1a03..fff1d0976f80 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -29,6 +29,18 @@ */ #define TPM_ANY_NUM 0xFFFF +struct tpm_chip; + +struct tpm_class_ops { + const u8 req_complete_mask; + const u8 req_complete_val; + bool (*req_canceled)(struct tpm_chip *chip, u8 status); + int (*recv) (struct tpm_chip *chip, u8 *buf, size_t len); + int (*send) (struct tpm_chip *chip, u8 *buf, size_t len); + void (*cancel) (struct tpm_chip *chip); + u8 (*status) (struct tpm_chip *chip); +}; + #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf); diff --git a/security/integrity/ima/ima_template_lib.c b/security/integrity/ima/ima_template_lib.c index c38adcc910fb..1683bbf289a4 100644 --- a/security/integrity/ima/ima_template_lib.c +++ b/security/integrity/ima/ima_template_lib.c @@ -162,8 +162,7 @@ void ima_show_template_sig(struct seq_file *m, enum ima_show_type show, } static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, - struct ima_field_data *field_data, - bool size_limit) + struct ima_field_data *field_data) { /* * digest formats: @@ -176,11 +175,10 @@ static int ima_eventdigest_init_common(u8 *digest, u32 digestsize, u8 hash_algo, enum data_formats fmt = DATA_FMT_DIGEST; u32 offset = 0; - if (!size_limit) { + if (hash_algo < HASH_ALGO__LAST) { fmt = DATA_FMT_DIGEST_WITH_ALGO; - if (hash_algo < HASH_ALGO__LAST) - offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, - "%s", hash_algo_name[hash_algo]); + offset += snprintf(buffer, CRYPTO_MAX_ALG_NAME + 1, "%s", + hash_algo_name[hash_algo]); buffer[offset] = ':'; offset += 2; } @@ -243,8 +241,8 @@ int ima_eventdigest_init(struct integrity_iint_cache *iint, struct file *file, cur_digest = hash.hdr.digest; cur_digestsize = hash.hdr.length; out: - return ima_eventdigest_init_common(cur_digest, cur_digestsize, -1, - field_data, true); + return ima_eventdigest_init_common(cur_digest, cur_digestsize, + HASH_ALGO__LAST, field_data); } /* @@ -255,7 +253,7 @@ int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, struct evm_ima_xattr_data *xattr_value, int xattr_len, struct ima_field_data *field_data) { - u8 *cur_digest = NULL, hash_algo = HASH_ALGO__LAST; + u8 *cur_digest = NULL, hash_algo = HASH_ALGO_SHA1; u32 cur_digestsize = 0; /* If iint is NULL, we are recording a violation. */ @@ -268,7 +266,7 @@ int ima_eventdigest_ng_init(struct integrity_iint_cache *iint, hash_algo = iint->ima_hash->algo; out: return ima_eventdigest_init_common(cur_digest, cur_digestsize, - hash_algo, field_data, false); + hash_algo, field_data); } static int ima_eventname_init_common(struct integrity_iint_cache *iint, diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 57b0b49f4e6e..4b34847208cc 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -82,7 +82,6 @@ #include <linux/syslog.h> #include <linux/user_namespace.h> #include <linux/export.h> -#include <linux/security.h> #include <linux/msg.h> #include <linux/shm.h> @@ -4490,14 +4489,10 @@ static int selinux_inet_conn_request(struct sock *sk, struct sk_buff *skb, { struct sk_security_struct *sksec = sk->sk_security; int err; - u16 family = sk->sk_family; + u16 family = req->rsk_ops->family; u32 connsid; u32 peersid; - /* handle mapped IPv4 packets arriving via IPv6 sockets */ - if (family == PF_INET6 && skb->protocol == htons(ETH_P_IP)) - family = PF_INET; - err = selinux_skb_peerlbl_sid(skb, family, &peersid); if (err) return err; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index fe341ae37004..8ed8daf7f1ee 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -33,13 +33,14 @@ #define POLICYDB_VERSION_ROLETRANS 26 #define POLICYDB_VERSION_NEW_OBJECT_DEFAULTS 27 #define POLICYDB_VERSION_DEFAULT_TYPE 28 +#define POLICYDB_VERSION_CONSTRAINT_NAMES 29 /* Range of policy versions we understand*/ #define POLICYDB_VERSION_MIN POLICYDB_VERSION_BASE #ifdef CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX #define POLICYDB_VERSION_MAX CONFIG_SECURITY_SELINUX_POLICYDB_VERSION_MAX_VALUE #else -#define POLICYDB_VERSION_MAX POLICYDB_VERSION_DEFAULT_TYPE +#define POLICYDB_VERSION_MAX POLICYDB_VERSION_CONSTRAINT_NAMES #endif /* Mask for just the mount related flags */ diff --git a/security/selinux/netlabel.c b/security/selinux/netlabel.c index 6235d052338b..0364120d1ec8 100644 --- a/security/selinux/netlabel.c +++ b/security/selinux/netlabel.c @@ -101,6 +101,32 @@ static struct netlbl_lsm_secattr *selinux_netlbl_sock_genattr(struct sock *sk) } /** + * selinux_netlbl_sock_getattr - Get the cached NetLabel secattr + * @sk: the socket + * @sid: the SID + * + * Query the socket's cached secattr and if the SID matches the cached value + * return the cache, otherwise return NULL. + * + */ +static struct netlbl_lsm_secattr *selinux_netlbl_sock_getattr( + const struct sock *sk, + u32 sid) +{ + struct sk_security_struct *sksec = sk->sk_security; + struct netlbl_lsm_secattr *secattr = sksec->nlbl_secattr; + + if (secattr == NULL) + return NULL; + + if ((secattr->flags & NETLBL_SECATTR_SECID) && + (secattr->attr.secid == sid)) + return secattr; + + return NULL; +} + +/** * selinux_netlbl_cache_invalidate - Invalidate the NetLabel cache * * Description: @@ -224,7 +250,7 @@ int selinux_netlbl_skbuff_setsid(struct sk_buff *skb, struct sk_security_struct *sksec = sk->sk_security; if (sksec->nlbl_state != NLBL_REQSKB) return 0; - secattr = sksec->nlbl_secattr; + secattr = selinux_netlbl_sock_getattr(sk, sid); } if (secattr == NULL) { secattr = &secattr_storage; @@ -410,6 +436,9 @@ int selinux_netlbl_socket_setsockopt(struct socket *sock, sksec->nlbl_state == NLBL_CONNLABELED)) { netlbl_secattr_init(&secattr); lock_sock(sk); + /* call the netlabel function directly as we want to see the + * on-the-wire label that is assigned via the socket's options + * and not the cached netlabel/lsm attributes */ rc = netlbl_sock_getattr(sk, &secattr); release_sock(sk); if (rc == 0) diff --git a/security/selinux/ss/constraint.h b/security/selinux/ss/constraint.h index 149dda731fd3..96fd947c494b 100644 --- a/security/selinux/ss/constraint.h +++ b/security/selinux/ss/constraint.h @@ -48,6 +48,7 @@ struct constraint_expr { u32 op; /* operator */ struct ebitmap names; /* names */ + struct type_set *type_names; struct constraint_expr *next; /* next expression */ }; diff --git a/security/selinux/ss/policydb.c b/security/selinux/ss/policydb.c index f6195ebde3c9..c0f498842129 100644 --- a/security/selinux/ss/policydb.c +++ b/security/selinux/ss/policydb.c @@ -143,6 +143,11 @@ static struct policydb_compat_info policydb_compat[] = { .sym_num = SYM_NUM, .ocon_num = OCON_NUM, }, + { + .version = POLICYDB_VERSION_CONSTRAINT_NAMES, + .sym_num = SYM_NUM, + .ocon_num = OCON_NUM, + }, }; static struct policydb_compat_info *policydb_lookup_compat(int version) @@ -613,6 +618,19 @@ static int common_destroy(void *key, void *datum, void *p) return 0; } +static void constraint_expr_destroy(struct constraint_expr *expr) +{ + if (expr) { + ebitmap_destroy(&expr->names); + if (expr->type_names) { + ebitmap_destroy(&expr->type_names->types); + ebitmap_destroy(&expr->type_names->negset); + kfree(expr->type_names); + } + kfree(expr); + } +} + static int cls_destroy(void *key, void *datum, void *p) { struct class_datum *cladatum; @@ -628,10 +646,9 @@ static int cls_destroy(void *key, void *datum, void *p) while (constraint) { e = constraint->expr; while (e) { - ebitmap_destroy(&e->names); etmp = e; e = e->next; - kfree(etmp); + constraint_expr_destroy(etmp); } ctemp = constraint; constraint = constraint->next; @@ -642,16 +659,14 @@ static int cls_destroy(void *key, void *datum, void *p) while (constraint) { e = constraint->expr; while (e) { - ebitmap_destroy(&e->names); etmp = e; e = e->next; - kfree(etmp); + constraint_expr_destroy(etmp); } ctemp = constraint; constraint = constraint->next; kfree(ctemp); } - kfree(cladatum->comkey); } kfree(datum); @@ -1156,8 +1171,34 @@ bad: return rc; } -static int read_cons_helper(struct constraint_node **nodep, int ncons, - int allowxtarget, void *fp) +static void type_set_init(struct type_set *t) +{ + ebitmap_init(&t->types); + ebitmap_init(&t->negset); +} + +static int type_set_read(struct type_set *t, void *fp) +{ + __le32 buf[1]; + int rc; + + if (ebitmap_read(&t->types, fp)) + return -EINVAL; + if (ebitmap_read(&t->negset, fp)) + return -EINVAL; + + rc = next_entry(buf, fp, sizeof(u32)); + if (rc < 0) + return -EINVAL; + t->flags = le32_to_cpu(buf[0]); + + return 0; +} + + +static int read_cons_helper(struct policydb *p, + struct constraint_node **nodep, + int ncons, int allowxtarget, void *fp) { struct constraint_node *c, *lc; struct constraint_expr *e, *le; @@ -1225,6 +1266,18 @@ static int read_cons_helper(struct constraint_node **nodep, int ncons, rc = ebitmap_read(&e->names, fp); if (rc) return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + e->type_names = kzalloc(sizeof + (*e->type_names), + GFP_KERNEL); + if (!e->type_names) + return -ENOMEM; + type_set_init(e->type_names); + rc = type_set_read(e->type_names, fp); + if (rc) + return rc; + } break; default: return -EINVAL; @@ -1301,7 +1354,7 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) goto bad; } - rc = read_cons_helper(&cladatum->constraints, ncons, 0, fp); + rc = read_cons_helper(p, &cladatum->constraints, ncons, 0, fp); if (rc) goto bad; @@ -1311,7 +1364,8 @@ static int class_read(struct policydb *p, struct hashtab *h, void *fp) if (rc) goto bad; ncons = le32_to_cpu(buf[0]); - rc = read_cons_helper(&cladatum->validatetrans, ncons, 1, fp); + rc = read_cons_helper(p, &cladatum->validatetrans, + ncons, 1, fp); if (rc) goto bad; } @@ -1941,7 +1995,19 @@ static int filename_trans_read(struct policydb *p, void *fp) if (rc) goto out; - hashtab_insert(p->filename_trans, ft, otype); + rc = hashtab_insert(p->filename_trans, ft, otype); + if (rc) { + /* + * Do not return -EEXIST to the caller, or the system + * will not boot. + */ + if (rc != -EEXIST) + goto out; + /* But free memory to avoid memory leak. */ + kfree(ft); + kfree(name); + kfree(otype); + } } hash_eval(p->filename_trans, "filenametr"); return 0; @@ -2753,6 +2819,24 @@ static int common_write(void *vkey, void *datum, void *ptr) return 0; } +static int type_set_write(struct type_set *t, void *fp) +{ + int rc; + __le32 buf[1]; + + if (ebitmap_write(&t->types, fp)) + return -EINVAL; + if (ebitmap_write(&t->negset, fp)) + return -EINVAL; + + buf[0] = cpu_to_le32(t->flags); + rc = put_entry(buf, sizeof(u32), 1, fp); + if (rc) + return -EINVAL; + + return 0; +} + static int write_cons_helper(struct policydb *p, struct constraint_node *node, void *fp) { @@ -2784,6 +2868,12 @@ static int write_cons_helper(struct policydb *p, struct constraint_node *node, rc = ebitmap_write(&e->names, fp); if (rc) return rc; + if (p->policyvers >= + POLICYDB_VERSION_CONSTRAINT_NAMES) { + rc = type_set_write(e->type_names, fp); + if (rc) + return rc; + } break; default: break; diff --git a/security/selinux/ss/policydb.h b/security/selinux/ss/policydb.h index da637471d4ce..725d5945a97e 100644 --- a/security/selinux/ss/policydb.h +++ b/security/selinux/ss/policydb.h @@ -154,6 +154,17 @@ struct cond_bool_datum { struct cond_node; /* + * type set preserves data needed to determine constraint info from + * policy source. This is not used by the kernel policy but allows + * utilities such as audit2allow to determine constraint denials. + */ +struct type_set { + struct ebitmap types; + struct ebitmap negset; + u32 flags; +}; + +/* * The configuration data includes security contexts for * initial SIDs, unlabeled file systems, TCP and UDP port numbers, * network interfaces, and nodes. This structure stores the diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index d106733ad987..fc5a63a05a1c 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1831,7 +1831,7 @@ static int security_preserve_bools(struct policydb *p); */ int security_load_policy(void *data, size_t len) { - struct policydb oldpolicydb, newpolicydb; + struct policydb *oldpolicydb, *newpolicydb; struct sidtab oldsidtab, newsidtab; struct selinux_mapping *oldmap, *map = NULL; struct convert_context_args args; @@ -1840,12 +1840,19 @@ int security_load_policy(void *data, size_t len) int rc = 0; struct policy_file file = { data, len }, *fp = &file; + oldpolicydb = kzalloc(2 * sizeof(*oldpolicydb), GFP_KERNEL); + if (!oldpolicydb) { + rc = -ENOMEM; + goto out; + } + newpolicydb = oldpolicydb + 1; + if (!ss_initialized) { avtab_cache_init(); rc = policydb_read(&policydb, fp); if (rc) { avtab_cache_destroy(); - return rc; + goto out; } policydb.len = len; @@ -1855,14 +1862,14 @@ int security_load_policy(void *data, size_t len) if (rc) { policydb_destroy(&policydb); avtab_cache_destroy(); - return rc; + goto out; } rc = policydb_load_isids(&policydb, &sidtab); if (rc) { policydb_destroy(&policydb); avtab_cache_destroy(); - return rc; + goto out; } security_load_policycaps(); @@ -1874,36 +1881,36 @@ int security_load_policy(void *data, size_t len) selinux_status_update_policyload(seqno); selinux_netlbl_cache_invalidate(); selinux_xfrm_notify_policyload(); - return 0; + goto out; } #if 0 sidtab_hash_eval(&sidtab, "sids"); #endif - rc = policydb_read(&newpolicydb, fp); + rc = policydb_read(newpolicydb, fp); if (rc) - return rc; + goto out; - newpolicydb.len = len; + newpolicydb->len = len; /* If switching between different policy types, log MLS status */ - if (policydb.mls_enabled && !newpolicydb.mls_enabled) + if (policydb.mls_enabled && !newpolicydb->mls_enabled) printk(KERN_INFO "SELinux: Disabling MLS support...\n"); - else if (!policydb.mls_enabled && newpolicydb.mls_enabled) + else if (!policydb.mls_enabled && newpolicydb->mls_enabled) printk(KERN_INFO "SELinux: Enabling MLS support...\n"); - rc = policydb_load_isids(&newpolicydb, &newsidtab); + rc = policydb_load_isids(newpolicydb, &newsidtab); if (rc) { printk(KERN_ERR "SELinux: unable to load the initial SIDs\n"); - policydb_destroy(&newpolicydb); - return rc; + policydb_destroy(newpolicydb); + goto out; } - rc = selinux_set_mapping(&newpolicydb, secclass_map, &map, &map_size); + rc = selinux_set_mapping(newpolicydb, secclass_map, &map, &map_size); if (rc) goto err; - rc = security_preserve_bools(&newpolicydb); + rc = security_preserve_bools(newpolicydb); if (rc) { printk(KERN_ERR "SELinux: unable to preserve booleans\n"); goto err; @@ -1921,7 +1928,7 @@ int security_load_policy(void *data, size_t len) * in the new SID table. */ args.oldp = &policydb; - args.newp = &newpolicydb; + args.newp = newpolicydb; rc = sidtab_map(&newsidtab, convert_context, &args); if (rc) { printk(KERN_ERR "SELinux: unable to convert the internal" @@ -1931,12 +1938,12 @@ int security_load_policy(void *data, size_t len) } /* Save the old policydb and SID table to free later. */ - memcpy(&oldpolicydb, &policydb, sizeof policydb); + memcpy(oldpolicydb, &policydb, sizeof(policydb)); sidtab_set(&oldsidtab, &sidtab); /* Install the new policydb and SID table. */ write_lock_irq(&policy_rwlock); - memcpy(&policydb, &newpolicydb, sizeof policydb); + memcpy(&policydb, newpolicydb, sizeof(policydb)); sidtab_set(&sidtab, &newsidtab); security_load_policycaps(); oldmap = current_mapping; @@ -1946,7 +1953,7 @@ int security_load_policy(void *data, size_t len) write_unlock_irq(&policy_rwlock); /* Free the old policydb and SID table. */ - policydb_destroy(&oldpolicydb); + policydb_destroy(oldpolicydb); sidtab_destroy(&oldsidtab); kfree(oldmap); @@ -1956,14 +1963,17 @@ int security_load_policy(void *data, size_t len) selinux_netlbl_cache_invalidate(); selinux_xfrm_notify_policyload(); - return 0; + rc = 0; + goto out; err: kfree(map); sidtab_destroy(&newsidtab); - policydb_destroy(&newpolicydb); - return rc; + policydb_destroy(newpolicydb); +out: + kfree(oldpolicydb); + return rc; } size_t security_policydb_len(void) diff --git a/security/smack/smack.h b/security/smack/smack.h index 364cc64fce71..d072fd32212d 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -241,7 +241,8 @@ u32 smack_to_secid(const char *); extern int smack_cipso_direct; extern int smack_cipso_mapped; extern struct smack_known *smack_net_ambient; -extern char *smack_onlycap; +extern struct smack_known *smack_onlycap; +extern struct smack_known *smack_syslog_label; extern const char *smack_cipso_option; extern struct smack_known smack_known_floor; @@ -312,7 +313,7 @@ static inline int smack_privileged(int cap) if (!capable(cap)) return 0; - if (smack_onlycap == NULL || smack_onlycap == skp->smk_known) + if (smack_onlycap == NULL || smack_onlycap == skp) return 1; return 0; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index b0be893ad44d..d814e35987be 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -219,8 +219,6 @@ static int smack_ptrace_traceme(struct task_struct *ptp) * smack_syslog - Smack approval on syslog * @type: message type * - * Require that the task has the floor label - * * Returns 0 on success, error code otherwise. */ static int smack_syslog(int typefrom_file) @@ -231,7 +229,7 @@ static int smack_syslog(int typefrom_file) if (smack_privileged(CAP_MAC_OVERRIDE)) return 0; - if (skp != &smack_known_floor) + if (smack_syslog_label != NULL && smack_syslog_label != skp) rc = -EACCES; return rc; @@ -341,10 +339,12 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) struct inode *inode = root->d_inode; struct superblock_smack *sp = sb->s_security; struct inode_smack *isp; + struct smack_known *skp; char *op; char *commap; char *nsp; int transmute = 0; + int specified = 0; if (sp->smk_initialized) return 0; @@ -359,34 +359,56 @@ static int smack_sb_kern_mount(struct super_block *sb, int flags, void *data) if (strncmp(op, SMK_FSHAT, strlen(SMK_FSHAT)) == 0) { op += strlen(SMK_FSHAT); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_hat = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSFLOOR, strlen(SMK_FSFLOOR)) == 0) { op += strlen(SMK_FSFLOOR); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_floor = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSDEFAULT, strlen(SMK_FSDEFAULT)) == 0) { op += strlen(SMK_FSDEFAULT); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_default = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSROOT, strlen(SMK_FSROOT)) == 0) { op += strlen(SMK_FSROOT); nsp = smk_import(op, 0); - if (nsp != NULL) + if (nsp != NULL) { sp->smk_root = nsp; + specified = 1; + } } else if (strncmp(op, SMK_FSTRANS, strlen(SMK_FSTRANS)) == 0) { op += strlen(SMK_FSTRANS); nsp = smk_import(op, 0); if (nsp != NULL) { sp->smk_root = nsp; transmute = 1; + specified = 1; } } } + if (!smack_privileged(CAP_MAC_ADMIN)) { + /* + * Unprivileged mounts don't get to specify Smack values. + */ + if (specified) + return -EPERM; + /* + * Unprivileged mounts get root and default from the caller. + */ + skp = smk_of_current(); + sp->smk_root = skp->smk_known; + sp->smk_default = skp->smk_known; + } /* * Initialize the root inode. */ @@ -423,53 +445,6 @@ static int smack_sb_statfs(struct dentry *dentry) return rc; } -/** - * smack_sb_mount - Smack check for mounting - * @dev_name: unused - * @path: mount point - * @type: unused - * @flags: unused - * @data: unused - * - * Returns 0 if current can write the floor of the filesystem - * being mounted on, an error code otherwise. - */ -static int smack_sb_mount(const char *dev_name, struct path *path, - const char *type, unsigned long flags, void *data) -{ - struct superblock_smack *sbp = path->dentry->d_sb->s_security; - struct smk_audit_info ad; - - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); - smk_ad_setfield_u_fs_path(&ad, *path); - - return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); -} - -/** - * smack_sb_umount - Smack check for unmounting - * @mnt: file system to unmount - * @flags: unused - * - * Returns 0 if current can write the floor of the filesystem - * being unmounted, an error code otherwise. - */ -static int smack_sb_umount(struct vfsmount *mnt, int flags) -{ - struct superblock_smack *sbp; - struct smk_audit_info ad; - struct path path; - - path.dentry = mnt->mnt_root; - path.mnt = mnt; - - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); - smk_ad_setfield_u_fs_path(&ad, path); - - sbp = path.dentry->d_sb->s_security; - return smk_curacc(sbp->smk_floor, MAY_WRITE, &ad); -} - /* * BPRM hooks */ @@ -837,31 +812,43 @@ static int smack_inode_setxattr(struct dentry *dentry, const char *name, const void *value, size_t size, int flags) { struct smk_audit_info ad; + struct smack_known *skp; + int check_priv = 0; + int check_import = 0; + int check_star = 0; int rc = 0; + /* + * Check label validity here so import won't fail in post_setxattr + */ if (strcmp(name, XATTR_NAME_SMACK) == 0 || strcmp(name, XATTR_NAME_SMACKIPIN) == 0 || - strcmp(name, XATTR_NAME_SMACKIPOUT) == 0 || - strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || - strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { - if (!smack_privileged(CAP_MAC_ADMIN)) - rc = -EPERM; - /* - * check label validity here so import wont fail on - * post_setxattr - */ - if (size == 0 || size >= SMK_LONGLABEL || - smk_import(value, size) == NULL) - rc = -EINVAL; + strcmp(name, XATTR_NAME_SMACKIPOUT) == 0) { + check_priv = 1; + check_import = 1; + } else if (strcmp(name, XATTR_NAME_SMACKEXEC) == 0 || + strcmp(name, XATTR_NAME_SMACKMMAP) == 0) { + check_priv = 1; + check_import = 1; + check_star = 1; } else if (strcmp(name, XATTR_NAME_SMACKTRANSMUTE) == 0) { - if (!smack_privileged(CAP_MAC_ADMIN)) - rc = -EPERM; + check_priv = 1; if (size != TRANS_TRUE_SIZE || strncmp(value, TRANS_TRUE, TRANS_TRUE_SIZE) != 0) rc = -EINVAL; } else rc = cap_inode_setxattr(dentry, name, value, size, flags); + if (check_priv && !smack_privileged(CAP_MAC_ADMIN)) + rc = -EPERM; + + if (rc == 0 && check_import) { + skp = smk_import_entry(value, size); + if (skp == NULL || (check_star && + (skp == &smack_known_star || skp == &smack_known_web))) + rc = -EINVAL; + } + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_DENTRY); smk_ad_setfield_u_fs_path_dentry(&ad, dentry); @@ -1364,7 +1351,7 @@ static int smack_file_receive(struct file *file) int may = 0; struct smk_audit_info ad; - smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_TASK); + smk_ad_init(&ad, __func__, LSM_AUDIT_DATA_PATH); smk_ad_setfield_u_fs_path(&ad, file->f_path); /* * This code relies on bitmasks. @@ -2847,8 +2834,17 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) if (rc >= 0) transflag = SMK_INODE_TRANSMUTE; } - isp->smk_task = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); - isp->smk_mmap = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); + /* + * Don't let the exec or mmap label be "*" or "@". + */ + skp = smk_fetch(XATTR_NAME_SMACKEXEC, inode, dp); + if (skp == &smack_known_star || skp == &smack_known_web) + skp = NULL; + isp->smk_task = skp; + skp = smk_fetch(XATTR_NAME_SMACKMMAP, inode, dp); + if (skp == &smack_known_star || skp == &smack_known_web) + skp = NULL; + isp->smk_mmap = skp; dput(dp); break; @@ -3743,8 +3739,6 @@ struct security_operations smack_ops = { .sb_copy_data = smack_sb_copy_data, .sb_kern_mount = smack_sb_kern_mount, .sb_statfs = smack_sb_statfs, - .sb_mount = smack_sb_mount, - .sb_umount = smack_sb_umount, .bprm_set_creds = smack_bprm_set_creds, .bprm_committing_creds = smack_bprm_committing_creds, diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 160aa08e3cd5..3198cfe1dcc6 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -52,6 +52,7 @@ enum smk_inos { SMK_CIPSO2 = 17, /* load long label -> CIPSO mapping */ SMK_REVOKE_SUBJ = 18, /* set rules with subject label to '-' */ SMK_CHANGE_RULE = 19, /* change or add rules (long labels) */ + SMK_SYSLOG = 20, /* change syslog label) */ }; /* @@ -59,6 +60,7 @@ enum smk_inos { */ static DEFINE_MUTEX(smack_cipso_lock); static DEFINE_MUTEX(smack_ambient_lock); +static DEFINE_MUTEX(smack_syslog_lock); static DEFINE_MUTEX(smk_netlbladdr_lock); /* @@ -90,7 +92,13 @@ int smack_cipso_mapped = SMACK_CIPSO_MAPPED_DEFAULT; * everyone. It is expected that the hat (^) label * will be used if any label is used. */ -char *smack_onlycap; +struct smack_known *smack_onlycap; + +/* + * If this value is set restrict syslog use to the label specified. + * It can be reset via smackfs/syslog + */ +struct smack_known *smack_syslog_label; /* * Certain IP addresses may be designated as single label hosts. @@ -301,7 +309,8 @@ static int smk_perm_from_str(const char *string) * @import: if non-zero, import labels * @len: label length limit * - * Returns 0 on success, -1 on failure + * Returns 0 on success, -EINVAL on failure and -ENOENT when either subject + * or object is missing. */ static int smk_fill_rule(const char *subject, const char *object, const char *access1, const char *access2, @@ -314,28 +323,28 @@ static int smk_fill_rule(const char *subject, const char *object, if (import) { rule->smk_subject = smk_import_entry(subject, len); if (rule->smk_subject == NULL) - return -1; + return -EINVAL; rule->smk_object = smk_import(object, len); if (rule->smk_object == NULL) - return -1; + return -EINVAL; } else { cp = smk_parse_smack(subject, len); if (cp == NULL) - return -1; + return -EINVAL; skp = smk_find_entry(cp); kfree(cp); if (skp == NULL) - return -1; + return -ENOENT; rule->smk_subject = skp; cp = smk_parse_smack(object, len); if (cp == NULL) - return -1; + return -EINVAL; skp = smk_find_entry(cp); kfree(cp); if (skp == NULL) - return -1; + return -ENOENT; rule->smk_object = skp->smk_known; } @@ -381,6 +390,7 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule, { ssize_t cnt = 0; char *tok[4]; + int rc; int i; /* @@ -405,10 +415,8 @@ static ssize_t smk_parse_long_rule(char *data, struct smack_parsed_rule *rule, while (i < 4) tok[i++] = NULL; - if (smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0)) - return -1; - - return cnt; + rc = smk_fill_rule(tok[0], tok[1], tok[2], tok[3], rule, import, 0); + return rc == 0 ? cnt : rc; } #define SMK_FIXED24_FMT 0 /* Fixed 24byte label format */ @@ -1603,7 +1611,7 @@ static const struct file_operations smk_ambient_ops = { }; /** - * smk_read_onlycap - read() for /smack/onlycap + * smk_read_onlycap - read() for smackfs/onlycap * @filp: file pointer, not actually used * @buf: where to put the result * @cn: maximum to send along @@ -1622,7 +1630,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, return 0; if (smack_onlycap != NULL) - smack = smack_onlycap; + smack = smack_onlycap->smk_known; asize = strlen(smack) + 1; @@ -1633,7 +1641,7 @@ static ssize_t smk_read_onlycap(struct file *filp, char __user *buf, } /** - * smk_write_onlycap - write() for /smack/onlycap + * smk_write_onlycap - write() for smackfs/onlycap * @file: file pointer, not actually used * @buf: where to get the data from * @count: bytes sent @@ -1656,7 +1664,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, * explicitly for clarity. The smk_access() implementation * would use smk_access(smack_onlycap, MAY_WRITE) */ - if (smack_onlycap != NULL && smack_onlycap != skp->smk_known) + if (smack_onlycap != NULL && smack_onlycap != skp) return -EPERM; data = kzalloc(count, GFP_KERNEL); @@ -1676,7 +1684,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, if (copy_from_user(data, buf, count) != 0) rc = -EFAULT; else - smack_onlycap = smk_import(data, count); + smack_onlycap = smk_import_entry(data, count); kfree(data); return rc; @@ -1856,11 +1864,12 @@ static ssize_t smk_user_access(struct file *file, const char __user *buf, res = smk_parse_long_rule(data, &rule, 0, 3); } - if (res < 0) + if (res >= 0) + res = smk_access(rule.smk_subject, rule.smk_object, + rule.smk_access1, NULL); + else if (res != -ENOENT) return -EINVAL; - res = smk_access(rule.smk_subject, rule.smk_object, - rule.smk_access1, NULL); data[0] = res == 0 ? '1' : '0'; data[1] = '\0'; @@ -2143,7 +2152,7 @@ static ssize_t smk_write_change_rule(struct file *file, const char __user *buf, /* * Must have privilege. */ - if (!capable(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; return smk_write_rules_list(file, buf, count, ppos, NULL, NULL, @@ -2158,12 +2167,89 @@ static const struct file_operations smk_change_rule_ops = { }; /** - * smk_fill_super - fill the /smackfs superblock + * smk_read_syslog - read() for smackfs/syslog + * @filp: file pointer, not actually used + * @buf: where to put the result + * @cn: maximum to send along + * @ppos: where to start + * + * Returns number of bytes read or error code, as appropriate + */ +static ssize_t smk_read_syslog(struct file *filp, char __user *buf, + size_t cn, loff_t *ppos) +{ + struct smack_known *skp; + ssize_t rc = -EINVAL; + int asize; + + if (*ppos != 0) + return 0; + + if (smack_syslog_label == NULL) + skp = &smack_known_star; + else + skp = smack_syslog_label; + + asize = strlen(skp->smk_known) + 1; + + if (cn >= asize) + rc = simple_read_from_buffer(buf, cn, ppos, skp->smk_known, + asize); + + return rc; +} + +/** + * smk_write_syslog - write() for smackfs/syslog + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start + * + * Returns number of bytes written or error code, as appropriate + */ +static ssize_t smk_write_syslog(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + char *data; + struct smack_known *skp; + int rc = count; + + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + data = kzalloc(count, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) + rc = -EFAULT; + else { + skp = smk_import_entry(data, count); + if (skp == NULL) + rc = -EINVAL; + else + smack_syslog_label = smk_import_entry(data, count); + } + + kfree(data); + return rc; +} + +static const struct file_operations smk_syslog_ops = { + .read = smk_read_syslog, + .write = smk_write_syslog, + .llseek = default_llseek, +}; + + +/** + * smk_fill_super - fill the smackfs superblock * @sb: the empty superblock * @data: unused * @silent: unused * - * Fill in the well known entries for /smack + * Fill in the well known entries for the smack filesystem * * Returns 0 on success, an error code on failure */ @@ -2208,6 +2294,8 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) S_IRUGO|S_IWUSR}, [SMK_CHANGE_RULE] = { "change-rule", &smk_change_rule_ops, S_IRUGO|S_IWUSR}, + [SMK_SYSLOG] = { + "syslog", &smk_syslog_ops, S_IRUGO|S_IWUSR}, /* last one */ {""} }; |