From 72e1eed8abb11c79749266d433c817ce36732893 Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Thu, 10 Sep 2015 22:06:15 +0300 Subject: integrity: prevent loading untrusted certificates on the IMA trusted keyring If IMA_LOAD_X509 is enabled, either directly or indirectly via IMA_APPRAISE_SIGNED_INIT, certificates are loaded onto the IMA trusted keyring by the kernel via key_create_or_update(). When the KEY_ALLOC_TRUSTED flag is provided, certificates are loaded without first verifying the certificate is properly signed by a trusted key on the system keyring. This patch removes the KEY_ALLOC_TRUSTED flag. Signed-off-by: Dmitry Kasatkin Cc: # 3.19+ Signed-off-by: Mimi Zohar diff --git a/security/integrity/digsig.c b/security/integrity/digsig.c index 36fb6b5..5be9ffb 100644 --- a/security/integrity/digsig.c +++ b/security/integrity/digsig.c @@ -105,7 +105,7 @@ int __init integrity_load_x509(const unsigned int id, const char *path) rc, ((KEY_POS_ALL & ~KEY_POS_SETATTR) | KEY_USR_VIEW | KEY_USR_READ), - KEY_ALLOC_NOT_IN_QUOTA | KEY_ALLOC_TRUSTED); + KEY_ALLOC_NOT_IN_QUOTA); if (IS_ERR(key)) { rc = PTR_ERR(key); pr_err("Problem loading X.509 certificate (%d): %s\n", -- cgit v0.10.2 From 5f2bfe2f1de8b745dc294acaf2ca2ad68e09b374 Mon Sep 17 00:00:00 2001 From: Lukasz Pawelczyk Date: Tue, 25 Aug 2015 12:39:46 +0200 Subject: Smack: fix a NULL dereference in wrong smack_import_entry() usage 'commit e774ad683f42 ("smack: pass error code through pointers")' made this function return proper error codes instead of NULL. Reflect that. This is a fix for a NULL dereference introduced in 'commit 21abb1ec414c ("Smack: IPv6 host labeling")' echo "$SOME_IPV6_ADDR \"test" > /smack/ipv6host (this should return EINVAL, it doesn't) cat /smack/ipv6host (derefences 0x000a) Signed-off-by: Lukasz Pawelczyk Acked-by: Casey Schaufler diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index c20b154..103a619 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -1501,8 +1501,8 @@ static ssize_t smk_write_net6addr(struct file *file, const char __user *buf, */ if (smack[0] != '-') { skp = smk_import_entry(smack, 0); - if (skp == NULL) { - rc = -EINVAL; + if (IS_ERR(skp)) { + rc = PTR_ERR(skp); goto free_out; } } else { -- cgit v0.10.2 From 8b549ef42a26f4ef604a9ede84f0260103942727 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Sun, 27 Sep 2015 23:10:25 +0800 Subject: smack: smk_ipv6_port_list should be static Fixes the following sparse warning: security/smack/smack_lsm.c:55:1: warning: symbol 'smk_ipv6_port_list' was not declared. Should it be static? Signed-off-by: Geliang Tang Acked-by: Casey Schaufler diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 996c889..f02438c 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -52,7 +52,7 @@ #define SMK_SENDING 2 #ifdef SMACK_IPV6_PORT_LABELING -LIST_HEAD(smk_ipv6_port_list); +static LIST_HEAD(smk_ipv6_port_list); #endif static struct kmem_cache *smack_inode_cache; int smack_enabled; -- cgit v0.10.2 From d21b7b049c0c1753a10c1d01606f42bad0e0f733 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Bollo?= Date: Fri, 2 Oct 2015 15:15:56 +0200 Subject: Smack: Minor initialisation improvement MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This change has two goals: - delay the setting of 'smack_enabled' until it will be really effective - ensure that smackfs is valid only if 'smack_enabled' is set (it is already the case in smack_netfilter.c) Signed-off-by: José Bollo Acked-by: Casey Schaufler diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index f02438c..2c0579d 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -4708,8 +4708,6 @@ static __init int smack_init(void) if (!security_module_enable("smack")) return 0; - smack_enabled = 1; - smack_inode_cache = KMEM_CACHE(inode_smack, 0); if (!smack_inode_cache) return -ENOMEM; @@ -4721,6 +4719,8 @@ static __init int smack_init(void) return -ENOMEM; } + smack_enabled = 1; + pr_info("Smack: Initializing.\n"); #ifdef CONFIG_SECURITY_SMACK_NETFILTER pr_info("Smack: Netfilter enabled.\n"); diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 103a619..ce8d503 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -2892,7 +2892,7 @@ static int __init init_smk_fs(void) int err; int rc; - if (!security_module_enable("smack")) + if (smack_enabled == 0) return 0; err = smk_init_sysfs(); -- cgit v0.10.2 From 8da4aba5bf9869f58d2a6bb30daaf54b2fa72569 Mon Sep 17 00:00:00 2001 From: Roman Kubiak Date: Mon, 5 Oct 2015 12:27:16 +0200 Subject: Smack: pipefs fix in smack_d_instantiate This fix writes the task label when smack_d_instantiate is called, before the label of the superblock was written on the pipe's inode. Signed-off-by: Roman Kubiak Acked-by: Casey Schaufler diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 2c0579d..c2d66ca 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3354,6 +3354,9 @@ static void smack_d_instantiate(struct dentry *opt_dentry, struct inode *inode) */ isp->smk_inode = smk_of_current(); break; + case PIPEFS_MAGIC: + isp->smk_inode = smk_of_current(); + break; default: isp->smk_inode = sbsp->smk_root; break; -- cgit v0.10.2 From eb6301160dde0227ac27c6ec2a0b57054d88e398 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Sun, 2 Aug 2015 11:08:47 +0200 Subject: tpm: Allow compile test of GPIO consumers if !GPIOLIB The GPIO subsystem provides dummy GPIO consumer functions if GPIOLIB is not enabled. Hence drivers that depend on GPIOLIB, but use GPIO consumer functionality only, can still be compiled if GPIOLIB is not enabled. Relax the dependency on GPIOLIB if COMPILE_TEST is enabled, where appropriate. Signed-off-by: Geert Uytterhoeven Acked-by: Linus Walleij Reviewed-by: Peter Huewe Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/st33zp24/Kconfig b/drivers/char/tpm/st33zp24/Kconfig index 09cb7278..19c0074 100644 --- a/drivers/char/tpm/st33zp24/Kconfig +++ b/drivers/char/tpm/st33zp24/Kconfig @@ -1,6 +1,6 @@ config TCG_TIS_ST33ZP24 tristate "STMicroelectronics TPM Interface Specification 1.2 Interface" - depends on GPIOLIB + depends on GPIOLIB || COMPILE_TEST ---help--- STMicroelectronics ST33ZP24 core driver. It implements the core TPM1.2 logic and hooks into the TPM kernel APIs. Physical layers will -- cgit v0.10.2 From 60ecd86c4d985750efa0ea3d8610972b09951715 Mon Sep 17 00:00:00 2001 From: "Hon Ching \\(Vicky\\) Lo" Date: Wed, 7 Oct 2015 20:11:51 -0400 Subject: vTPM: fix memory allocation flag for rtce buffer at kernel boot At ibm vtpm initialzation, tpm_ibmvtpm_probe() registers its interrupt handler, ibmvtpm_interrupt, which calls ibmvtpm_crq_process to allocate memory for rtce buffer. The current code uses 'GFP_KERNEL' as the type of kernel memory allocation, which resulted a warning at kernel/lockdep.c. This patch uses 'GFP_ATOMIC' instead so that the allocation is high-priority and does not sleep. Cc: stable@kernel.org Signed-off-by: Hon Ching(Vicky) Lo Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm_ibmvtpm.c b/drivers/char/tpm/tpm_ibmvtpm.c index 27ebf95..3e6a226 100644 --- a/drivers/char/tpm/tpm_ibmvtpm.c +++ b/drivers/char/tpm/tpm_ibmvtpm.c @@ -491,7 +491,7 @@ static void ibmvtpm_crq_process(struct ibmvtpm_crq *crq, } ibmvtpm->rtce_size = be16_to_cpu(crq->len); ibmvtpm->rtce_buf = kmalloc(ibmvtpm->rtce_size, - GFP_KERNEL); + GFP_ATOMIC); if (!ibmvtpm->rtce_buf) { dev_err(ibmvtpm->dev, "Failed to allocate memory for rtce buffer\n"); return; -- cgit v0.10.2 From 149789ce9d472e6b4fd99336e779ab843754a96c Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 15 Sep 2015 20:05:40 +0300 Subject: tpm, tpm_crb: fix unaligned read of the command buffer address The command buffer address must be read with exactly two 32-bit reads. Otherwise, on some HW platforms, it seems that HW will abort the read operation, which causes CPU to fill the read bytes with 1's. Therefore, we cannot rely on memcpy_fromio() but must call ioread32() two times instead. Also, this matches the PC Client Platform TPM Profile specification, which defines command buffer address with two 32-bit fields. Cc: stable@kernel.org Signed-off-by: Jarkko Sakkinen Reviewed-by: Peter Huewe Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 1267322..83068fa 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -74,7 +74,8 @@ struct crb_control_area { u32 int_enable; u32 int_sts; u32 cmd_size; - u64 cmd_pa; + u32 cmd_pa_low; + u32 cmd_pa_high; u32 rsp_size; u64 rsp_pa; } __packed; @@ -273,8 +274,8 @@ static int crb_acpi_add(struct acpi_device *device) return -ENOMEM; } - memcpy_fromio(&pa, &priv->cca->cmd_pa, 8); - pa = le64_to_cpu(pa); + pa = ((u64) le32_to_cpu(ioread32(&priv->cca->cmd_pa_high)) << 32) | + (u64) le32_to_cpu(ioread32(&priv->cca->cmd_pa_low)); priv->cmd = devm_ioremap_nocache(dev, pa, ioread32(&priv->cca->cmd_size)); if (!priv->cmd) { -- cgit v0.10.2 From 399235dc6e95400a1322a9999e92073bc572f0c8 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 29 Sep 2015 00:32:19 +0300 Subject: tpm, tpm_tis: fix tpm_tis ACPI detection issue with TPM 2.0 Both for FIFO and CRB interface TCG has decided to use the same HID MSFT0101. They can be differentiated by looking at the start method from TPM2 ACPI table. This patches makes necessary fixes to tpm_tis and tpm_crb modules in order to correctly detect, which module should be used. For MSFT0101 we must use struct acpi_driver because struct pnp_driver has a 7 character limitation. It turned out that the root cause in b371616b8 was not correct for https://bugzilla.kernel.org/show_bug.cgi?id=98181. v2: * One fixup was missing from v1: is_tpm2_fifo -> is_fifo v3: * Use pnp_driver for existing HIDs and acpi_driver only for MSFT0101 in order ensure backwards compatibility. v4: * Check for FIFO before doing *anything* in crb_acpi_add(). * There was return immediately after acpi_bus_unregister_driver() in cleanup_tis(). This caused pnp_unregister_driver() not to be called. Cc: stable@kernel.org Reported-by: Michael Saunders Reported-by: Michael Marley Reported-by: Jethro Beekman Reported-by: Matthew Garrett Signed-off-by: Jarkko Sakkinen Tested-by: Michael Marley Tested-by: Mimi Zohar (on TPM 1.2) Reviewed-by: Peter Huewe Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index f8319a0..39be5ac 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -115,6 +115,13 @@ enum tpm2_startup_types { TPM2_SU_STATE = 0x0001, }; +enum tpm2_start_method { + TPM2_START_ACPI = 2, + TPM2_START_FIFO = 6, + TPM2_START_CRB = 7, + TPM2_START_CRB_WITH_ACPI = 8, +}; + struct tpm_chip; struct tpm_vendor_specific { diff --git a/drivers/char/tpm/tpm_crb.c b/drivers/char/tpm/tpm_crb.c index 83068fa..4bb9727 100644 --- a/drivers/char/tpm/tpm_crb.c +++ b/drivers/char/tpm/tpm_crb.c @@ -34,12 +34,6 @@ enum crb_defaults { CRB_ACPI_START_INDEX = 1, }; -enum crb_start_method { - CRB_SM_ACPI_START = 2, - CRB_SM_CRB = 7, - CRB_SM_CRB_WITH_ACPI_START = 8, -}; - struct acpi_tpm2 { struct acpi_table_header hdr; u16 platform_class; @@ -221,12 +215,6 @@ static int crb_acpi_add(struct acpi_device *device) u64 pa; int rc; - chip = tpmm_chip_alloc(dev, &tpm_crb); - if (IS_ERR(chip)) - return PTR_ERR(chip); - - chip->flags = TPM_CHIP_FLAG_TPM2; - status = acpi_get_table(ACPI_SIG_TPM2, 1, (struct acpi_table_header **) &buf); if (ACPI_FAILURE(status)) { @@ -234,13 +222,15 @@ static int crb_acpi_add(struct acpi_device *device) return -ENODEV; } - /* At least some versions of AMI BIOS have a bug that TPM2 table has - * zero address for the control area and therefore we must fail. - */ - if (!buf->control_area_pa) { - dev_err(dev, "TPM2 ACPI table has a zero address for the control area\n"); - return -EINVAL; - } + /* Should the FIFO driver handle this? */ + if (buf->start_method == TPM2_START_FIFO) + return -ENODEV; + + chip = tpmm_chip_alloc(dev, &tpm_crb); + if (IS_ERR(chip)) + return PTR_ERR(chip); + + chip->flags = TPM_CHIP_FLAG_TPM2; if (buf->hdr.length < sizeof(struct acpi_tpm2)) { dev_err(dev, "TPM2 ACPI table has wrong size"); @@ -260,11 +250,11 @@ static int crb_acpi_add(struct acpi_device *device) * report only ACPI start but in practice seems to require both * ACPI start and CRB start. */ - if (sm == CRB_SM_CRB || sm == CRB_SM_CRB_WITH_ACPI_START || + if (sm == TPM2_START_CRB || sm == TPM2_START_FIFO || !strcmp(acpi_device_hid(device), "MSFT0101")) priv->flags |= CRB_FL_CRB_START; - if (sm == CRB_SM_ACPI_START || sm == CRB_SM_CRB_WITH_ACPI_START) + if (sm == TPM2_START_ACPI || sm == TPM2_START_CRB_WITH_ACPI) priv->flags |= CRB_FL_ACPI_START; priv->cca = (struct crb_control_area __iomem *) diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index f2dffa7..696ef1d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -1,6 +1,6 @@ /* * Copyright (C) 2005, 2006 IBM Corporation - * Copyright (C) 2014 Intel Corporation + * Copyright (C) 2014, 2015 Intel Corporation * * Authors: * Leendert van Doorn @@ -28,6 +28,7 @@ #include #include #include +#include #include "tpm.h" enum tis_access { @@ -65,6 +66,17 @@ enum tis_defaults { TIS_LONG_TIMEOUT = 2000, /* 2 sec */ }; +struct tpm_info { + unsigned long start; + unsigned long len; + unsigned int irq; +}; + +static struct tpm_info tis_default_info = { + .start = TIS_MEM_BASE, + .len = TIS_MEM_LEN, + .irq = 0, +}; /* Some timeout values are needed before it is known whether the chip is * TPM 1.0 or TPM 2.0. @@ -91,26 +103,54 @@ struct priv_data { }; #if defined(CONFIG_PNP) && defined(CONFIG_ACPI) -static int is_itpm(struct pnp_dev *dev) +static int has_hid(struct acpi_device *dev, const char *hid) { - struct acpi_device *acpi = pnp_acpi_device(dev); struct acpi_hardware_id *id; - if (!acpi) - return 0; - - list_for_each_entry(id, &acpi->pnp.ids, list) { - if (!strcmp("INTC0102", id->id)) + list_for_each_entry(id, &dev->pnp.ids, list) + if (!strcmp(hid, id->id)) return 1; - } return 0; } + +static inline int is_itpm(struct acpi_device *dev) +{ + return has_hid(dev, "INTC0102"); +} + +static inline int is_fifo(struct acpi_device *dev) +{ + struct acpi_table_tpm2 *tbl; + acpi_status st; + + /* TPM 1.2 FIFO */ + if (!has_hid(dev, "MSFT0101")) + return 1; + + st = acpi_get_table(ACPI_SIG_TPM2, 1, + (struct acpi_table_header **) &tbl); + if (ACPI_FAILURE(st)) { + dev_err(&dev->dev, "failed to get TPM2 ACPI table\n"); + return 0; + } + + if (le32_to_cpu(tbl->start_method) != TPM2_START_FIFO) + return 0; + + /* TPM 2.0 FIFO */ + return 1; +} #else -static inline int is_itpm(struct pnp_dev *dev) +static inline int is_itpm(struct acpi_device *dev) { return 0; } + +static inline int is_fifo(struct acpi_device *dev) +{ + return 1; +} #endif /* Before we attempt to access the TPM we must see that the valid bit is set. @@ -600,9 +640,8 @@ static void tpm_tis_remove(struct tpm_chip *chip) release_locality(chip, chip->vendor.locality, 1); } -static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, - resource_size_t start, resource_size_t len, - unsigned int irq) +static int tpm_tis_init(struct device *dev, struct tpm_info *tpm_info, + acpi_handle acpi_dev_handle) { u32 vendor, intfcaps, intmask; int rc, i, irq_s, irq_e, probe; @@ -622,7 +661,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, chip->acpi_dev_handle = acpi_dev_handle; #endif - chip->vendor.iobase = devm_ioremap(dev, start, len); + chip->vendor.iobase = devm_ioremap(dev, tpm_info->start, tpm_info->len); if (!chip->vendor.iobase) return -EIO; @@ -707,7 +746,7 @@ static int tpm_tis_init(struct device *dev, acpi_handle acpi_dev_handle, chip->vendor.iobase + TPM_INT_ENABLE(chip->vendor.locality)); if (interrupts) - chip->vendor.irq = irq; + chip->vendor.irq = tpm_info->irq; if (interrupts && !chip->vendor.irq) { irq_s = ioread8(chip->vendor.iobase + @@ -890,27 +929,27 @@ static SIMPLE_DEV_PM_OPS(tpm_tis_pm, tpm_pm_suspend, tpm_tis_resume); static int tpm_tis_pnp_init(struct pnp_dev *pnp_dev, const struct pnp_device_id *pnp_id) { - resource_size_t start, len; - unsigned int irq = 0; + struct tpm_info tpm_info = tis_default_info; acpi_handle acpi_dev_handle = NULL; - start = pnp_mem_start(pnp_dev, 0); - len = pnp_mem_len(pnp_dev, 0); + tpm_info.start = pnp_mem_start(pnp_dev, 0); + tpm_info.len = pnp_mem_len(pnp_dev, 0); if (pnp_irq_valid(pnp_dev, 0)) - irq = pnp_irq(pnp_dev, 0); + tpm_info.irq = pnp_irq(pnp_dev, 0); else interrupts = false; - if (is_itpm(pnp_dev)) - itpm = true; - #ifdef CONFIG_ACPI - if (pnp_acpi_device(pnp_dev)) + if (pnp_acpi_device(pnp_dev)) { + if (is_itpm(pnp_acpi_device(pnp_dev))) + itpm = true; + acpi_dev_handle = pnp_acpi_device(pnp_dev)->handle; + } #endif - return tpm_tis_init(&pnp_dev->dev, acpi_dev_handle, start, len, irq); + return tpm_tis_init(&pnp_dev->dev, &tpm_info, acpi_dev_handle); } static struct pnp_device_id tpm_pnp_tbl[] = { @@ -930,6 +969,7 @@ MODULE_DEVICE_TABLE(pnp, tpm_pnp_tbl); static void tpm_tis_pnp_remove(struct pnp_dev *dev) { struct tpm_chip *chip = pnp_get_drvdata(dev); + tpm_chip_unregister(chip); tpm_tis_remove(chip); } @@ -950,6 +990,79 @@ module_param_string(hid, tpm_pnp_tbl[TIS_HID_USR_IDX].id, MODULE_PARM_DESC(hid, "Set additional specific HID for this driver to probe"); #endif +#ifdef CONFIG_ACPI +static int tpm_check_resource(struct acpi_resource *ares, void *data) +{ + struct tpm_info *tpm_info = (struct tpm_info *) data; + struct resource res; + + if (acpi_dev_resource_interrupt(ares, 0, &res)) { + tpm_info->irq = res.start; + } else if (acpi_dev_resource_memory(ares, &res)) { + tpm_info->start = res.start; + tpm_info->len = resource_size(&res); + } + + return 1; +} + +static int tpm_tis_acpi_init(struct acpi_device *acpi_dev) +{ + struct list_head resources; + struct tpm_info tpm_info = tis_default_info; + int ret; + + if (!is_fifo(acpi_dev)) + return -ENODEV; + + INIT_LIST_HEAD(&resources); + ret = acpi_dev_get_resources(acpi_dev, &resources, tpm_check_resource, + &tpm_info); + if (ret < 0) + return ret; + + acpi_dev_free_resource_list(&resources); + + if (!tpm_info.irq) + interrupts = false; + + if (is_itpm(acpi_dev)) + itpm = true; + + return tpm_tis_init(&acpi_dev->dev, &tpm_info, acpi_dev->handle); +} + +static int tpm_tis_acpi_remove(struct acpi_device *dev) +{ + struct tpm_chip *chip = dev_get_drvdata(&dev->dev); + + tpm_chip_unregister(chip); + tpm_tis_remove(chip); + + return 0; +} + +static struct acpi_device_id tpm_acpi_tbl[] = { + {"MSFT0101", 0}, /* TPM 2.0 */ + /* Add new here */ + {"", 0}, /* User Specified */ + {"", 0} /* Terminator */ +}; +MODULE_DEVICE_TABLE(acpi, tpm_acpi_tbl); + +static struct acpi_driver tis_acpi_driver = { + .name = "tpm_tis", + .ids = tpm_acpi_tbl, + .ops = { + .add = tpm_tis_acpi_init, + .remove = tpm_tis_acpi_remove, + }, + .drv = { + .pm = &tpm_tis_pm, + }, +}; +#endif + static struct platform_driver tis_drv = { .driver = { .name = "tpm_tis", @@ -966,9 +1079,25 @@ static int __init init_tis(void) { int rc; #ifdef CONFIG_PNP - if (!force) - return pnp_register_driver(&tis_pnp_driver); + if (!force) { + rc = pnp_register_driver(&tis_pnp_driver); + if (rc) + return rc; + } +#endif +#ifdef CONFIG_ACPI + if (!force) { + rc = acpi_bus_register_driver(&tis_acpi_driver); + if (rc) { +#ifdef CONFIG_PNP + pnp_unregister_driver(&tis_pnp_driver); #endif + return rc; + } + } +#endif + if (!force) + return 0; rc = platform_driver_register(&tis_drv); if (rc < 0) @@ -978,7 +1107,7 @@ static int __init init_tis(void) rc = PTR_ERR(pdev); goto err_dev; } - rc = tpm_tis_init(&pdev->dev, NULL, TIS_MEM_BASE, TIS_MEM_LEN, 0); + rc = tpm_tis_init(&pdev->dev, &tis_default_info, NULL); if (rc) goto err_init; return 0; @@ -992,9 +1121,14 @@ err_dev: static void __exit cleanup_tis(void) { struct tpm_chip *chip; -#ifdef CONFIG_PNP +#if defined(CONFIG_PNP) || defined(CONFIG_ACPI) if (!force) { +#ifdef CONFIG_ACPI + acpi_bus_unregister_driver(&tis_acpi_driver); +#endif +#ifdef CONFIG_PNP pnp_unregister_driver(&tis_pnp_driver); +#endif return; } #endif -- cgit v0.10.2 From 37c1c04cca920de8a68285751b2c7b3d937ad50c Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 22 Apr 2015 19:36:06 +0300 Subject: sysfs: added __compat_only_sysfs_link_entry_to_kobj() Added a new function __compat_only_sysfs_link_group_to_kobj() that adds a symlink from attribute or group to a kobject. This needed for maintaining backwards compatibility with PPI attributes in the TPM driver. Signed-off-by: Jarkko Sakkinen Signed-off-by: Peter Huewe diff --git a/fs/sysfs/group.c b/fs/sysfs/group.c index 39a0199..e123659 100644 --- a/fs/sysfs/group.c +++ b/fs/sysfs/group.c @@ -352,3 +352,47 @@ void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, } } EXPORT_SYMBOL_GPL(sysfs_remove_link_from_group); + +/** + * __compat_only_sysfs_link_entry_to_kobj - add a symlink to a kobject pointing + * to a group or an attribute + * @kobj: The kobject containing the group. + * @target_kobj: The target kobject. + * @target_name: The name of the target group or attribute. + */ +int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, + struct kobject *target_kobj, + const char *target_name) +{ + struct kernfs_node *target; + struct kernfs_node *entry; + struct kernfs_node *link; + + /* + * We don't own @target_kobj and it may be removed at any time. + * Synchronize using sysfs_symlink_target_lock. See sysfs_remove_dir() + * for details. + */ + spin_lock(&sysfs_symlink_target_lock); + target = target_kobj->sd; + if (target) + kernfs_get(target); + spin_unlock(&sysfs_symlink_target_lock); + if (!target) + return -ENOENT; + + entry = kernfs_find_and_get(target_kobj->sd, target_name); + if (!entry) { + kernfs_put(target); + return -ENOENT; + } + + link = kernfs_create_link(kobj->sd, target_name, entry); + if (IS_ERR(link) && PTR_ERR(link) == -EEXIST) + sysfs_warn_dup(kobj->sd, target_name); + + kernfs_put(entry); + kernfs_put(target); + return IS_ERR(link) ? PTR_ERR(link) : 0; +} +EXPORT_SYMBOL_GPL(__compat_only_sysfs_link_entry_to_kobj); diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h index 9f65758..ea090ea 100644 --- a/include/linux/sysfs.h +++ b/include/linux/sysfs.h @@ -268,6 +268,9 @@ int sysfs_add_link_to_group(struct kobject *kobj, const char *group_name, struct kobject *target, const char *link_name); void sysfs_remove_link_from_group(struct kobject *kobj, const char *group_name, const char *link_name); +int __compat_only_sysfs_link_entry_to_kobj(struct kobject *kobj, + struct kobject *target_kobj, + const char *target_name); void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr); @@ -451,6 +454,14 @@ static inline void sysfs_remove_link_from_group(struct kobject *kobj, { } +static inline int __compat_only_sysfs_link_entry_to_kobj( + struct kobject *kobj, + struct kobject *target_kobj, + const char *target_name) +{ + return 0; +} + static inline void sysfs_notify(struct kobject *kobj, const char *dir, const char *attr) { -- cgit v0.10.2 From 9b774d5cf2db4b27324784e8d2c95a06cdf82373 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Tue, 14 Apr 2015 17:56:48 +0300 Subject: tpm: move the PPI attributes to character device directory. Moved PPI attributes to the character device directory. This aligns with the sysfs guidelines and makes them race free because they are created atomically with the character device as part of device_register().The character device and the sysfs attributes appear at the same time to the user space. As part of this change we enable PPI attributes also for TPM 2.0 devices. In order to retain backwards compatibility with TPM 1.x devices, a symlink is created to the platform device directory. Signed-off-by: Jarkko Sakkinen Reviewed-by: Jason Gunthorpe Tested-by: Mimi Zohar (on TPM 1.2) Tested-by: Chris J Arges Tested-by: Colin Ian King Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm-chip.c b/drivers/char/tpm/tpm-chip.c index 1082d4b..f26b0ae 100644 --- a/drivers/char/tpm/tpm-chip.c +++ b/drivers/char/tpm/tpm-chip.c @@ -119,6 +119,9 @@ struct tpm_chip *tpmm_chip_alloc(struct device *dev, chip->dev.class = tpm_class; chip->dev.release = tpm_dev_release; chip->dev.parent = chip->pdev; +#ifdef CONFIG_ACPI + chip->dev.groups = chip->groups; +#endif if (chip->dev_num == 0) chip->dev.devt = MKDEV(MISC_MAJOR, TPM_MINOR); @@ -182,12 +185,6 @@ static int tpm1_chip_register(struct tpm_chip *chip) if (rc) return rc; - rc = tpm_add_ppi(chip); - if (rc) { - tpm_sysfs_del_device(chip); - return rc; - } - chip->bios_dir = tpm_bios_log_setup(chip->devname); return 0; @@ -201,8 +198,6 @@ static void tpm1_chip_unregister(struct tpm_chip *chip) if (chip->bios_dir) tpm_bios_log_teardown(chip->bios_dir); - tpm_remove_ppi(chip); - tpm_sysfs_del_device(chip); } @@ -225,10 +220,20 @@ int tpm_chip_register(struct tpm_chip *chip) if (rc) return rc; + tpm_add_ppi(chip); + rc = tpm_dev_add_device(chip); if (rc) goto out_err; + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) { + rc = __compat_only_sysfs_link_entry_to_kobj(&chip->pdev->kobj, + &chip->dev.kobj, + "ppi"); + if (rc) + goto out_err; + } + /* Make the chip available. */ spin_lock(&driver_lock); list_add_rcu(&chip->list, &tpm_chip_list); @@ -263,6 +268,9 @@ void tpm_chip_unregister(struct tpm_chip *chip) spin_unlock(&driver_lock); synchronize_rcu(); + if (!(chip->flags & TPM_CHIP_FLAG_TPM2)) + sysfs_remove_link(&chip->pdev->kobj, "ppi"); + tpm1_chip_unregister(chip); tpm_dev_del_device(chip); } diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 39be5ac..36ceb71 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -158,8 +158,7 @@ struct tpm_vendor_specific { enum tpm_chip_flags { TPM_CHIP_FLAG_REGISTERED = BIT(0), - TPM_CHIP_FLAG_PPI = BIT(1), - TPM_CHIP_FLAG_TPM2 = BIT(2), + TPM_CHIP_FLAG_TPM2 = BIT(1), }; struct tpm_chip { @@ -182,6 +181,8 @@ struct tpm_chip { struct dentry **bios_dir; #ifdef CONFIG_ACPI + const struct attribute_group *groups[2]; + unsigned int groups_cnt; acpi_handle acpi_dev_handle; char ppi_version[TPM_PPI_VERSION_LEN + 1]; #endif /* CONFIG_ACPI */ @@ -189,7 +190,7 @@ struct tpm_chip { struct list_head list; }; -#define to_tpm_chip(n) container_of(n, struct tpm_chip, vendor) +#define to_tpm_chip(d) container_of(d, struct tpm_chip, dev) static inline void tpm_chip_put(struct tpm_chip *chip) { @@ -419,15 +420,9 @@ 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 tpm_chip *chip); -extern void tpm_remove_ppi(struct tpm_chip *chip); +extern void tpm_add_ppi(struct tpm_chip *chip); #else -static inline int tpm_add_ppi(struct tpm_chip *chip) -{ - return 0; -} - -static inline void tpm_remove_ppi(struct tpm_chip *chip) +static inline void tpm_add_ppi(struct tpm_chip *chip) { } #endif diff --git a/drivers/char/tpm/tpm_ppi.c b/drivers/char/tpm/tpm_ppi.c index 6ca9b5d..692a2c6 100644 --- a/drivers/char/tpm/tpm_ppi.c +++ b/drivers/char/tpm/tpm_ppi.c @@ -53,7 +53,7 @@ tpm_eval_dsm(acpi_handle ppi_handle, int func, acpi_object_type type, static ssize_t tpm_show_ppi_version(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return scnprintf(buf, PAGE_SIZE, "%s\n", chip->ppi_version); } @@ -63,7 +63,7 @@ static ssize_t tpm_show_ppi_request(struct device *dev, { ssize_t size = -EINVAL; union acpi_object *obj; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETREQ, ACPI_TYPE_PACKAGE, NULL); @@ -100,7 +100,7 @@ static ssize_t tpm_store_ppi_request(struct device *dev, int func = TPM_PPI_FN_SUBREQ; union acpi_object *obj, tmp; union acpi_object argv4 = ACPI_INIT_DSM_ARGV4(1, &tmp); - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); /* * the function to submit TPM operation request to pre-os environment @@ -156,7 +156,7 @@ static ssize_t tpm_show_ppi_transition_action(struct device *dev, .buffer.length = 0, .buffer.pointer = NULL }; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); static char *info[] = { "None", @@ -197,7 +197,7 @@ static ssize_t tpm_show_ppi_response(struct device *dev, acpi_status status = -EINVAL; union acpi_object *obj, *ret_obj; u64 req, res; - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); obj = tpm_eval_dsm(chip->acpi_dev_handle, TPM_PPI_FN_GETRSP, ACPI_TYPE_PACKAGE, NULL); @@ -296,7 +296,7 @@ static ssize_t tpm_show_ppi_tcg_operations(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return show_ppi_operations(chip->acpi_dev_handle, buf, 0, PPI_TPM_REQ_MAX); @@ -306,7 +306,7 @@ static ssize_t tpm_show_ppi_vs_operations(struct device *dev, struct device_attribute *attr, char *buf) { - struct tpm_chip *chip = dev_get_drvdata(dev); + struct tpm_chip *chip = to_tpm_chip(dev); return show_ppi_operations(chip->acpi_dev_handle, buf, PPI_VS_REQ_START, PPI_VS_REQ_END); @@ -334,17 +334,16 @@ static struct attribute_group ppi_attr_grp = { .attrs = ppi_attrs }; -int tpm_add_ppi(struct tpm_chip *chip) +void tpm_add_ppi(struct tpm_chip *chip) { union acpi_object *obj; - int rc; if (!chip->acpi_dev_handle) - return 0; + return; if (!acpi_check_dsm(chip->acpi_dev_handle, tpm_ppi_uuid, TPM_PPI_REVISION_ID, 1 << TPM_PPI_FN_VERSION)) - return 0; + return; /* Cache PPI version string. */ obj = acpi_evaluate_dsm_typed(chip->acpi_dev_handle, tpm_ppi_uuid, @@ -356,16 +355,5 @@ int tpm_add_ppi(struct tpm_chip *chip) ACPI_FREE(obj); } - rc = sysfs_create_group(&chip->pdev->kobj, &ppi_attr_grp); - - if (!rc) - chip->flags |= TPM_CHIP_FLAG_PPI; - - return rc; -} - -void tpm_remove_ppi(struct tpm_chip *chip) -{ - if (chip->flags & TPM_CHIP_FLAG_PPI) - sysfs_remove_group(&chip->pdev->kobj, &ppi_attr_grp); + chip->groups[chip->groups_cnt++] = &ppi_attr_grp; } -- cgit v0.10.2 From b8e98dcdc5ad24bbecc763cd0ac87bbde602e5ea Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Wed, 20 May 2015 16:44:40 +0300 Subject: tpm: update PPI documentation to address the location change. Updated Documentation/ABI/testing/sysfs-driver-ppi in order to explain where PPI attributes are located and how backwards compatibility is addressed. Signed-off-by: Jarkko Sakkinen Signed-off-by: Peter Huewe diff --git a/Documentation/ABI/testing/sysfs-driver-ppi b/Documentation/ABI/testing/sysfs-driver-ppi index 7d1435b..9921ef2 100644 --- a/Documentation/ABI/testing/sysfs-driver-ppi +++ b/Documentation/ABI/testing/sysfs-driver-ppi @@ -1,4 +1,4 @@ -What: /sys/devices/pnp0//ppi/ +What: /sys/class/tpm/tpmX/ppi/ Date: August 2012 Kernel Version: 3.6 Contact: xiaoyan.zhang@intel.com @@ -8,9 +8,14 @@ Description: folder makes sense. The folder path can be got by command 'find /sys/ -name 'pcrs''. For the detail information of PPI, please refer to the PPI specification from + http://www.trustedcomputinggroup.org/ -What: /sys/devices/pnp0//ppi/version + In Linux 4.2 ppi was moved to the character device directory. + A symlink from tpmX/device/ppi to tpmX/ppi to provide backwards + compatibility. + +What: /sys/class/tpm/tpmX/ppi/version Date: August 2012 Contact: xiaoyan.zhang@intel.com Description: @@ -18,7 +23,7 @@ Description: platform. This file is readonly. -What: /sys/devices/pnp0//ppi/request +What: /sys/class/tpm/tpmX/ppi/request Date: August 2012 Contact: xiaoyan.zhang@intel.com Description: @@ -28,7 +33,7 @@ Description: integer value range from 1 to 160, and 0 means no request. This file can be read and written. -What: /sys/devices/pnp0/00:/ppi/response +What: /sys/class/tpm/tpmX/ppi/response Date: August 2012 Contact: xiaoyan.zhang@intel.com Description: @@ -37,7 +42,7 @@ Description: : ". This file is readonly. -What: /sys/devices/pnp0//ppi/transition_action +What: /sys/class/tpm/tpmX/ppi/transition_action Date: August 2012 Contact: xiaoyan.zhang@intel.com Description: @@ -47,7 +52,7 @@ Description: description>". This file is readonly. -What: /sys/devices/pnp0//ppi/tcg_operations +What: /sys/class/tpm/tpmX/ppi/tcg_operations Date: August 2012 Contact: xiaoyan.zhang@intel.com Description: @@ -58,7 +63,7 @@ Description: This attribute is only supported by PPI version 1.2+. This file is readonly. -What: /sys/devices/pnp0//ppi/vs_operations +What: /sys/class/tpm/tpmX/ppi/vs_operations Date: August 2012 Contact: xiaoyan.zhang@intel.com Description: -- cgit v0.10.2 From a74f8b36352e79b13d48fa92759c9ea6b78d5817 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sun, 11 Oct 2015 12:26:58 +0300 Subject: tpm: introduce tpm_buf This patch introduces struct tpm_buf that provides a string buffer for constructing TPM commands. This allows to construct variable sized TPM commands. For the buffer a page is allocated and mapped, which limits maximum size to PAGE_SIZE. Variable sized TPM commands are needed in order to add algorithmic agility. Signed-off-by: Jarkko Sakkinen Reviewed-by: Peter Huewe Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index 36ceb71..cb46f62 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -1,5 +1,6 @@ /* * Copyright (C) 2004 IBM Corporation + * Copyright (C) 2015 Intel Corporation * * Authors: * Leendert van Doorn @@ -28,6 +29,7 @@ #include #include #include +#include enum tpm_const { TPM_MINOR = 224, /* officially assigned */ @@ -390,6 +392,101 @@ struct tpm_cmd_t { tpm_cmd_params params; } __packed; +/* A string buffer type for constructing TPM commands. This is based on the + * ideas of string buffer code in security/keys/trusted.h but is heap based + * in order to keep the stack usage minimal. + */ + +enum tpm_buf_flags { + TPM_BUF_OVERFLOW = BIT(0), +}; + +struct tpm_buf { + struct page *data_page; + unsigned int flags; + u8 *data; +}; + +static inline void tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) +{ + struct tpm_input_header *head; + + buf->data_page = alloc_page(GFP_HIGHUSER); + if (!buf->data_page) + return -ENOMEM; + + buf->flags = 0; + buf->data = kmap(buf->data_page); + + head = (struct tpm_input_header *) buf->data; + + head->tag = cpu_to_be16(tag); + head->length = cpu_to_be32(sizeof(*head)); + head->ordinal = cpu_to_be32(ordinal); + + return 0; +} + +static inline void tpm_buf_destroy(struct tpm_buf *buf) +{ + kunmap(buf->data_page); + __free_page(buf->data_page); +} + +static inline u32 tpm_buf_length(struct tpm_buf *buf) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + + return be32_to_cpu(head->length); +} + +static inline u16 tpm_buf_tag(struct tpm_buf *buf) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + + return be16_to_cpu(head->tag); +} + +static inline void tpm_buf_append(struct tpm_buf *buf, + const unsigned char *new_data, + unsigned int new_len) +{ + struct tpm_input_header *head = (struct tpm_input_header *) buf->data; + u32 len = tpm_buf_length(buf); + + /* Return silently if overflow has already happened. */ + if (buf->flags & TPM_BUF_OVERFLOW) + return; + + if ((len + new_len) > PAGE_SIZE) { + WARN(1, "tpm_buf: overflow\n"); + buf->flags |= TPM_BUF_OVERFLOW; + return; + } + + memcpy(&buf->data[len], new_data, new_len); + head->length = cpu_to_be32(len + new_len); +} + +static inline void tpm_buf_append_u8(struct tpm_buf *buf, const u8 value) +{ + tpm_buf_append(buf, &value, 1); +} + +static inline void tpm_buf_append_u16(struct tpm_buf *buf, const u16 value) +{ + __be16 value2 = cpu_to_be16(value); + + tpm_buf_append(buf, (u8 *) &value2, 2); +} + +static inline void tpm_buf_append_u32(struct tpm_buf *buf, const u32 value) +{ + __be32 value2 = cpu_to_be32(value); + + tpm_buf_append(buf, (u8 *) &value2, 4); +} + extern struct class *tpm_class; extern dev_t tpm_devt; extern const struct file_operations tpm_fops; -- cgit v0.10.2 From fe351e8d4eec801beeba1df1f36d76316be6f1a2 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 12 Jun 2015 23:34:01 +0200 Subject: keys, trusted: move struct trusted_key_options to trusted-type.h Moved struct trusted_key_options to trustes-type.h so that the fields can be accessed from drivers/char/tpm. Signed-off-by: Jarkko Sakkinen Signed-off-by: Peter Huewe diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index 56f82e5..c91651f 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -12,10 +12,12 @@ #include #include +#include #define MIN_KEY_SIZE 32 #define MAX_KEY_SIZE 128 #define MAX_BLOB_SIZE 320 +#define MAX_PCRINFO_SIZE 64 struct trusted_key_payload { struct rcu_head rcu; @@ -26,6 +28,16 @@ struct trusted_key_payload { unsigned char blob[MAX_BLOB_SIZE]; }; +struct trusted_key_options { + uint16_t keytype; + uint32_t keyhandle; + unsigned char keyauth[TPM_DIGEST_SIZE]; + unsigned char blobauth[TPM_DIGEST_SIZE]; + uint32_t pcrinfo_len; + unsigned char pcrinfo[MAX_PCRINFO_SIZE]; + int pcrlock; +}; + extern struct key_type key_type_trusted; #endif /* _KEYS_TRUSTED_TYPE_H */ diff --git a/security/keys/trusted.h b/security/keys/trusted.h index 3249fbd..ff001a5 100644 --- a/security/keys/trusted.h +++ b/security/keys/trusted.h @@ -2,7 +2,6 @@ #define __TRUSTED_KEY_H /* implementation specific TPM constants */ -#define MAX_PCRINFO_SIZE 64 #define MAX_BUF_SIZE 512 #define TPM_GETRANDOM_SIZE 14 #define TPM_OSAP_SIZE 36 @@ -36,16 +35,6 @@ enum { SRK_keytype = 4 }; -struct trusted_key_options { - uint16_t keytype; - uint32_t keyhandle; - unsigned char keyauth[SHA1_DIGEST_SIZE]; - unsigned char blobauth[SHA1_DIGEST_SIZE]; - uint32_t pcrinfo_len; - unsigned char pcrinfo[MAX_PCRINFO_SIZE]; - int pcrlock; -}; - #define TPM_DEBUG 0 #if TPM_DEBUG -- cgit v0.10.2 From 954650efb79f99d5c817c121bb0a7c6c53362048 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Sat, 30 May 2015 08:09:04 +0300 Subject: tpm: seal/unseal for TPM 2.0 Added tpm_trusted_seal() and tpm_trusted_unseal() API for sealing trusted keys. This patch implements basic sealing and unsealing functionality for TPM 2.0: * Seal with a parent key using a 20 byte auth value. * Unseal with a parent key using a 20 byte auth value. Signed-off-by: Jarkko Sakkinen Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index e85d341..c50637d 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -666,6 +666,30 @@ int tpm_pcr_read_dev(struct tpm_chip *chip, int pcr_idx, u8 *res_buf) } /** + * tpm_is_tpm2 - is the chip a TPM2 chip? + * @chip_num: tpm idx # or ANY + * + * Returns < 0 on error, and 1 or 0 on success depending whether the chip + * is a TPM2 chip. + */ +int tpm_is_tpm2(u32 chip_num) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL) + return -ENODEV; + + rc = (chip->flags & TPM_CHIP_FLAG_TPM2) != 0; + + tpm_chip_put(chip); + + return rc; +} +EXPORT_SYMBOL_GPL(tpm_is_tpm2); + +/** * tpm_pcr_read - read a pcr value * @chip_num: tpm idx # or ANY * @pcr_idx: pcr idx to retrieve @@ -1021,6 +1045,58 @@ int tpm_get_random(u32 chip_num, u8 *out, size_t max) } EXPORT_SYMBOL_GPL(tpm_get_random); +/** + * tpm_seal_trusted() - seal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips + * are supported. + */ +int tpm_seal_trusted(u32 chip_num, struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2)) + return -ENODEV; + + rc = tpm2_seal_trusted(chip, payload, options); + + tpm_chip_put(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_seal_trusted); + +/** + * tpm_unseal_trusted() - unseal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. At the moment, only TPM 2.0 chips + * are supported. + */ +int tpm_unseal_trusted(u32 chip_num, struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + struct tpm_chip *chip; + int rc; + + chip = tpm_chip_find_get(chip_num); + if (chip == NULL || !(chip->flags & TPM_CHIP_FLAG_TPM2)) + return -ENODEV; + + rc = tpm2_unseal_trusted(chip, payload, options); + + tpm_chip_put(chip); + return rc; +} +EXPORT_SYMBOL_GPL(tpm_unseal_trusted); + static int __init tpm_init(void) { int rc; diff --git a/drivers/char/tpm/tpm.h b/drivers/char/tpm/tpm.h index cb46f62..a4257a3 100644 --- a/drivers/char/tpm/tpm.h +++ b/drivers/char/tpm/tpm.h @@ -90,6 +90,9 @@ enum tpm2_return_codes { enum tpm2_algorithms { TPM2_ALG_SHA1 = 0x0004, + TPM2_ALG_KEYEDHASH = 0x0008, + TPM2_ALG_SHA256 = 0x000B, + TPM2_ALG_NULL = 0x0010 }; enum tpm2_command_codes { @@ -97,6 +100,10 @@ enum tpm2_command_codes { TPM2_CC_SELF_TEST = 0x0143, TPM2_CC_STARTUP = 0x0144, TPM2_CC_SHUTDOWN = 0x0145, + TPM2_CC_CREATE = 0x0153, + TPM2_CC_LOAD = 0x0157, + TPM2_CC_UNSEAL = 0x015E, + TPM2_CC_FLUSH_CONTEXT = 0x0165, TPM2_CC_GET_CAPABILITY = 0x017A, TPM2_CC_GET_RANDOM = 0x017B, TPM2_CC_PCR_READ = 0x017E, @@ -407,7 +414,7 @@ struct tpm_buf { u8 *data; }; -static inline void tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) +static inline int tpm_buf_init(struct tpm_buf *buf, u16 tag, u32 ordinal) { struct tpm_input_header *head; @@ -527,6 +534,12 @@ static inline void tpm_add_ppi(struct tpm_chip *chip) int tpm2_pcr_read(struct tpm_chip *chip, int pcr_idx, u8 *res_buf); int tpm2_pcr_extend(struct tpm_chip *chip, int pcr_idx, const u8 *hash); int tpm2_get_random(struct tpm_chip *chip, u8 *out, size_t max); +int tpm2_seal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options); +int tpm2_unseal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options); ssize_t tpm2_get_tpm_pt(struct tpm_chip *chip, u32 property_id, u32 *value, const char *desc); diff --git a/drivers/char/tpm/tpm2-cmd.c b/drivers/char/tpm/tpm2-cmd.c index 011909a..bd7039f 100644 --- a/drivers/char/tpm/tpm2-cmd.c +++ b/drivers/char/tpm/tpm2-cmd.c @@ -1,5 +1,5 @@ /* - * Copyright (C) 2014 Intel Corporation + * Copyright (C) 2014, 2015 Intel Corporation * * Authors: * Jarkko Sakkinen @@ -16,6 +16,11 @@ */ #include "tpm.h" +#include + +enum tpm2_object_attributes { + TPM2_ATTR_USER_WITH_AUTH = BIT(6), +}; struct tpm2_startup_in { __be16 startup_type; @@ -381,6 +386,249 @@ static const struct tpm_input_header tpm2_get_tpm_pt_header = { }; /** + * Append TPMS_AUTH_COMMAND to the buffer. The buffer must be allocated with + * tpm_buf_alloc(). + * + * @param buf: an allocated tpm_buf instance + * @param nonce: the session nonce, may be NULL if not used + * @param nonce_len: the session nonce length, may be 0 if not used + * @param attributes: the session attributes + * @param hmac: the session HMAC or password, may be NULL if not used + * @param hmac_len: the session HMAC or password length, maybe 0 if not used + */ +static void tpm2_buf_append_auth(struct tpm_buf *buf, u32 session_handle, + const u8 *nonce, u16 nonce_len, + u8 attributes, + const u8 *hmac, u16 hmac_len) +{ + tpm_buf_append_u32(buf, 9 + nonce_len + hmac_len); + tpm_buf_append_u32(buf, session_handle); + tpm_buf_append_u16(buf, nonce_len); + + if (nonce && nonce_len) + tpm_buf_append(buf, nonce, nonce_len); + + tpm_buf_append_u8(buf, attributes); + tpm_buf_append_u16(buf, hmac_len); + + if (hmac && hmac_len) + tpm_buf_append(buf, hmac, hmac_len); +} + +/** + * tpm2_seal_trusted() - seal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. + */ +int tpm2_seal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + unsigned int blob_len; + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_CREATE); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, options->keyhandle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->keyauth /* hmac */, + TPM_DIGEST_SIZE); + + /* sensitive */ + tpm_buf_append_u16(&buf, 4 + TPM_DIGEST_SIZE + payload->key_len); + + tpm_buf_append_u16(&buf, TPM_DIGEST_SIZE); + tpm_buf_append(&buf, options->blobauth, TPM_DIGEST_SIZE); + tpm_buf_append_u16(&buf, payload->key_len); + tpm_buf_append(&buf, payload->key, payload->key_len); + + /* public */ + tpm_buf_append_u16(&buf, 14); + + tpm_buf_append_u16(&buf, TPM2_ALG_KEYEDHASH); + tpm_buf_append_u16(&buf, TPM2_ALG_SHA256); + tpm_buf_append_u32(&buf, TPM2_ATTR_USER_WITH_AUTH); + tpm_buf_append_u16(&buf, 0); /* policy digest size */ + tpm_buf_append_u16(&buf, TPM2_ALG_NULL); + tpm_buf_append_u16(&buf, 0); + + /* outside info */ + tpm_buf_append_u16(&buf, 0); + + /* creation PCR */ + tpm_buf_append_u32(&buf, 0); + + if (buf.flags & TPM_BUF_OVERFLOW) { + rc = -E2BIG; + goto out; + } + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "sealing data"); + if (rc) + goto out; + + blob_len = be32_to_cpup((__be32 *) &buf.data[TPM_HEADER_SIZE]); + if (blob_len > MAX_BLOB_SIZE) { + rc = -E2BIG; + goto out; + } + + memcpy(payload->blob, &buf.data[TPM_HEADER_SIZE + 4], blob_len); + payload->blob_len = blob_len; + +out: + tpm_buf_destroy(&buf); + + if (rc > 0) + rc = -EPERM; + + return rc; +} + +static int tpm2_load(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 *blob_handle) +{ + struct tpm_buf buf; + unsigned int private_len; + unsigned int public_len; + unsigned int blob_len; + int rc; + + private_len = be16_to_cpup((__be16 *) &payload->blob[0]); + if (private_len > (payload->blob_len - 2)) + return -E2BIG; + + public_len = be16_to_cpup((__be16 *) &payload->blob[2 + private_len]); + blob_len = private_len + public_len + 4; + if (blob_len > payload->blob_len) + return -E2BIG; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_LOAD); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, options->keyhandle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->keyauth /* hmac */, + TPM_DIGEST_SIZE); + + tpm_buf_append(&buf, payload->blob, blob_len); + + if (buf.flags & TPM_BUF_OVERFLOW) { + rc = -E2BIG; + goto out; + } + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "loading blob"); + if (!rc) + *blob_handle = be32_to_cpup( + (__be32 *) &buf.data[TPM_HEADER_SIZE]); + +out: + tpm_buf_destroy(&buf); + + if (rc > 0) + rc = -EPERM; + + return rc; +} + +static void tpm2_flush_context(struct tpm_chip *chip, u32 handle) +{ + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_NO_SESSIONS, TPM2_CC_FLUSH_CONTEXT); + if (rc) { + dev_warn(chip->pdev, "0x%08x was not flushed, out of memory\n", + handle); + return; + } + + tpm_buf_append_u32(&buf, handle); + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "flushing context"); + if (rc) + dev_warn(chip->pdev, "0x%08x was not flushed, rc=%d\n", handle, + rc); + + tpm_buf_destroy(&buf); +} + +static int tpm2_unseal(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options, + u32 blob_handle) +{ + struct tpm_buf buf; + int rc; + + rc = tpm_buf_init(&buf, TPM2_ST_SESSIONS, TPM2_CC_UNSEAL); + if (rc) + return rc; + + tpm_buf_append_u32(&buf, blob_handle); + tpm2_buf_append_auth(&buf, TPM2_RS_PW, + NULL /* nonce */, 0, + 0 /* session_attributes */, + options->blobauth /* hmac */, + TPM_DIGEST_SIZE); + + rc = tpm_transmit_cmd(chip, buf.data, PAGE_SIZE, "unsealing"); + if (rc > 0) + rc = -EPERM; + + if (!rc) { + payload->key_len = be16_to_cpup( + (__be16 *) &buf.data[TPM_HEADER_SIZE + 4]); + + memcpy(payload->key, &buf.data[TPM_HEADER_SIZE + 6], + payload->key_len); + } + + tpm_buf_destroy(&buf); + return rc; +} + +/** + * tpm_unseal_trusted() - unseal a trusted key + * @chip_num: A specific chip number for the request or TPM_ANY_NUM + * @options: authentication values and other options + * @payload: the key data in clear and encrypted form + * + * Returns < 0 on error and 0 on success. + */ +int tpm2_unseal_trusted(struct tpm_chip *chip, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + u32 blob_handle; + int rc; + + rc = tpm2_load(chip, payload, options, &blob_handle); + if (rc) + return rc; + + rc = tpm2_unseal(chip, payload, options, blob_handle); + + tpm2_flush_context(chip, blob_handle); + + return rc; +} + +/** * tpm2_get_tpm_pt() - get value of a TPM_CAP_TPM_PROPERTIES type property * @chip: TPM chip to use. * @property_id: property ID. diff --git a/include/keys/trusted-type.h b/include/keys/trusted-type.h index c91651f..f91ecd9 100644 --- a/include/keys/trusted-type.h +++ b/include/keys/trusted-type.h @@ -16,7 +16,7 @@ #define MIN_KEY_SIZE 32 #define MAX_KEY_SIZE 128 -#define MAX_BLOB_SIZE 320 +#define MAX_BLOB_SIZE 512 #define MAX_PCRINFO_SIZE 64 struct trusted_key_payload { diff --git a/include/linux/tpm.h b/include/linux/tpm.h index 8350c53..706e63e 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -30,6 +30,8 @@ #define TPM_ANY_NUM 0xFFFF struct tpm_chip; +struct trusted_key_payload; +struct trusted_key_options; struct tpm_class_ops { const u8 req_complete_mask; @@ -46,11 +48,22 @@ struct tpm_class_ops { #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) +extern int tpm_is_tpm2(u32 chip_num); extern int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf); extern int tpm_pcr_extend(u32 chip_num, int pcr_idx, const u8 *hash); extern int tpm_send(u32 chip_num, void *cmd, size_t buflen); extern int tpm_get_random(u32 chip_num, u8 *data, size_t max); +extern int tpm_seal_trusted(u32 chip_num, + struct trusted_key_payload *payload, + struct trusted_key_options *options); +extern int tpm_unseal_trusted(u32 chip_num, + struct trusted_key_payload *payload, + struct trusted_key_options *options); #else +static inline int tpm_is_tpm2(u32 chip_num) +{ + return -ENODEV; +} static inline int tpm_pcr_read(u32 chip_num, int pcr_idx, u8 *res_buf) { return -ENODEV; } @@ -63,5 +76,18 @@ static inline int tpm_send(u32 chip_num, void *cmd, size_t buflen) { static inline int tpm_get_random(u32 chip_num, u8 *data, size_t max) { return -ENODEV; } + +static inline int tpm_seal_trusted(u32 chip_num, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + return -ENODEV; +} +static inline int tpm_unseal_trusted(u32 chip_num, + struct trusted_key_payload *payload, + struct trusted_key_options *options) +{ + return -ENODEV; +} #endif #endif -- cgit v0.10.2 From 0fe5480303a1657b328a0a389f8d99249d9961f5 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 26 Jun 2015 22:28:26 +0300 Subject: keys, trusted: seal/unseal with TPM 2.0 chips Call tpm_seal_trusted() and tpm_unseal_trusted() for TPM 2.0 chips. We require explicit 'keyhandle=' option because there's no a fixed storage root key inside TPM2 chips. Signed-off-by: Jarkko Sakkinen Reviewed-by: Andreas Fuchs Tested-by: Mimi Zohar (on TPM 1.2) Tested-by: Chris J Arges Tested-by: Colin Ian King Tested-by: Kevin Strasser Signed-off-by: Peter Huewe diff --git a/security/keys/trusted.c b/security/keys/trusted.c index c0594cb..d3633cf 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -862,12 +862,19 @@ static int datablob_parse(char *datablob, struct trusted_key_payload *p, static struct trusted_key_options *trusted_options_alloc(void) { struct trusted_key_options *options; + int tpm2; + + tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + if (tpm2 < 0) + return NULL; options = kzalloc(sizeof *options, GFP_KERNEL); if (options) { /* set any non-zero defaults */ options->keytype = SRK_keytype; - options->keyhandle = SRKHANDLE; + + if (!tpm2) + options->keyhandle = SRKHANDLE; } return options; } @@ -905,6 +912,11 @@ static int trusted_instantiate(struct key *key, int ret = 0; int key_cmd; size_t key_len; + int tpm2; + + tpm2 = tpm_is_tpm2(TPM_ANY_NUM); + if (tpm2 < 0) + return tpm2; if (datalen <= 0 || datalen > 32767 || !prep->data) return -EINVAL; @@ -932,12 +944,20 @@ static int trusted_instantiate(struct key *key, goto out; } + if (!options->keyhandle) { + ret = -EINVAL; + goto out; + } + dump_payload(payload); dump_options(options); switch (key_cmd) { case Opt_load: - ret = key_unseal(payload, options); + if (tpm2) + ret = tpm_unseal_trusted(TPM_ANY_NUM, payload, options); + else + ret = key_unseal(payload, options); dump_payload(payload); dump_options(options); if (ret < 0) @@ -950,7 +970,10 @@ static int trusted_instantiate(struct key *key, pr_info("trusted_key: key_create failed (%d)\n", ret); goto out; } - ret = key_seal(payload, options); + if (tpm2) + ret = tpm_seal_trusted(TPM_ANY_NUM, payload, options); + else + ret = key_seal(payload, options); if (ret < 0) pr_info("trusted_key: key_seal failed (%d)\n", ret); break; @@ -1018,6 +1041,13 @@ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) kfree(new_p); goto out; } + + if (!new_o->keyhandle) { + ret = -EINVAL; + kfree(new_p); + goto out; + } + /* copy old key values, and reseal with new pcrs */ new_p->migratable = p->migratable; new_p->key_len = p->key_len; -- cgit v0.10.2 From 89adb83c5a378618ed67a1cf4c2c2061364b4d10 Mon Sep 17 00:00:00 2001 From: Jarkko Sakkinen Date: Fri, 16 Oct 2015 12:14:28 +0300 Subject: MAINTAINERS: add new maintainer for TPM DEVICE DRIVER At the moment, no one has time to pay enough attention to this subsystem so that patches get eventually merged into linux-next. Even critical bug fixes can lie for weeks. I'm happy to continue with the current maintainers if they are able to do their job but if this is not the case I cannot find any other solution but apply myself for the job. If there is someone more experienced and/or competent, I'm also happy to let one take the stand. Anything works as long as it works. I just want a solution for this bottleneck. Signed-off-by: Jarkko Sakkinen Acked-by: Peter Huewe Signed-off-by: Peter Huewe diff --git a/MAINTAINERS b/MAINTAINERS index 797236b..da9ee41 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -10586,6 +10586,7 @@ F: drivers/media/pci/tw68/ TPM DEVICE DRIVER M: Peter Huewe M: Marcel Selhorst +M: Jarkko Sakkinen R: Jason Gunthorpe W: http://tpmdd.sourceforge.net L: tpmdd-devel@lists.sourceforge.net (moderated for non-subscribers) -- cgit v0.10.2 From 2f82e98265e0a46e122ee2a035450d065dbd0f4f Mon Sep 17 00:00:00 2001 From: "Hon Ching \\(Vicky\\) Lo" Date: Wed, 7 Oct 2015 20:11:52 -0400 Subject: vTPM: fix searching for the right vTPM node in device tree Replace all occurrences of '/ibm,vtpm' with '/vdevice/vtpm', as only the latter is guanranteed to be available for the client OS. The '/ibm,vtpm' node should only be used by Open Firmware, which is susceptible to changes. Signed-off-by: Hon Ching(Vicky) Lo Signed-off-by: Peter Huewe diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 15099c4..a8af4d4 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1430,12 +1430,12 @@ static void __init prom_instantiate_sml(void) prom_debug("prom_instantiate_sml: start...\n"); - ibmvtpm_node = call_prom("finddevice", 1, 1, ADDR("/ibm,vtpm")); + ibmvtpm_node = call_prom("finddevice", 1, 1, ADDR("/vdevice/vtpm")); prom_debug("ibmvtpm_node: %x\n", ibmvtpm_node); if (!PHANDLE_VALID(ibmvtpm_node)) return; - ibmvtpm_inst = call_prom("open", 1, 1, ADDR("/ibm,vtpm")); + ibmvtpm_inst = call_prom("open", 1, 1, ADDR("/vdevice/vtpm")); if (!IHANDLE_VALID(ibmvtpm_inst)) { prom_printf("opening vtpm package failed (%x)\n", ibmvtpm_inst); return; @@ -1464,9 +1464,9 @@ static void __init prom_instantiate_sml(void) reserve_mem(base, size); - prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-base", + prom_setprop(ibmvtpm_node, "/vdevice/vtpm", "linux,sml-base", &base, sizeof(base)); - prom_setprop(ibmvtpm_node, "/ibm,vtpm", "linux,sml-size", + prom_setprop(ibmvtpm_node, "/vdevice/vtpm", "linux,sml-size", &size, sizeof(size)); prom_debug("sml base = 0x%x\n", base); diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c index eebe625..66b3be4 100644 --- a/drivers/char/tpm/tpm_of.c +++ b/drivers/char/tpm/tpm_of.c @@ -31,7 +31,7 @@ int read_log(struct tpm_bios_log *log) return -EFAULT; } - np = of_find_node_by_name(NULL, "ibm,vtpm"); + np = of_find_node_by_name(NULL, "vtpm"); if (!np) { pr_err("%s: ERROR - IBMVTPM not supported\n", __func__); return -ENODEV; -- cgit v0.10.2 From b4ed0469d00ad7ad051e5acbdb3e7587fd0221e2 Mon Sep 17 00:00:00 2001 From: "Hon Ching \\(Vicky\\) Lo" Date: Wed, 7 Oct 2015 20:11:53 -0400 Subject: vTPM: reformat event log to be byte-aligned The event log generated by OpenFirmware in PowerPC is 4-byte aligned. This patch reformats the log to be byte-aligned for the Linux client. Signed-off-by: Hon Ching(Vicky) Lo Signed-off-by: Peter Huewe diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index a8af4d4..7b2e452 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1425,8 +1425,9 @@ static void __init prom_instantiate_sml(void) { phandle ibmvtpm_node; ihandle ibmvtpm_inst; - u32 entry = 0, size = 0; + u32 entry = 0, size = 0, succ = 0; u64 base; + __be32 val; prom_debug("prom_instantiate_sml: start...\n"); @@ -1441,6 +1442,16 @@ static void __init prom_instantiate_sml(void) return; } + if (prom_getprop(ibmvtpm_node, "ibm,sml-efi-reformat-supported", + &val, sizeof(val)) != PROM_ERROR) { + if (call_prom_ret("call-method", 2, 2, &succ, + ADDR("reformat-sml-to-efi-alignment"), + ibmvtpm_inst) != 0 || succ == 0) { + prom_printf("Reformat SML to EFI alignment failed\n"); + return; + } + } + if (call_prom_ret("call-method", 2, 2, &size, ADDR("sml-get-handover-size"), ibmvtpm_inst) != 0 || size == 0) { -- cgit v0.10.2 From 9e5d4af458057344e8cc35b09b2f7a9c9e95d81f Mon Sep 17 00:00:00 2001 From: "Hon Ching \\(Vicky\\) Lo" Date: Wed, 7 Oct 2015 20:11:54 -0400 Subject: vTPM: get the buffer allocated for event log instead of the actual log The OS should ask Power Firmware (PFW) for the size of the buffer allocated for the event log, instead of the size of the actual event log. It then passes the buffer adddress and size to PFW in the handover process, into which PFW copies the log. Signed-off-by: Hon Ching(Vicky) Lo Signed-off-by: Peter Huewe diff --git a/arch/powerpc/kernel/prom_init.c b/arch/powerpc/kernel/prom_init.c index 7b2e452..92dea8d 100644 --- a/arch/powerpc/kernel/prom_init.c +++ b/arch/powerpc/kernel/prom_init.c @@ -1450,13 +1450,20 @@ static void __init prom_instantiate_sml(void) prom_printf("Reformat SML to EFI alignment failed\n"); return; } - } - if (call_prom_ret("call-method", 2, 2, &size, - ADDR("sml-get-handover-size"), - ibmvtpm_inst) != 0 || size == 0) { - prom_printf("SML get handover size failed\n"); - return; + if (call_prom_ret("call-method", 2, 2, &size, + ADDR("sml-get-allocated-size"), + ibmvtpm_inst) != 0 || size == 0) { + prom_printf("SML get allocated size failed\n"); + return; + } + } else { + if (call_prom_ret("call-method", 2, 2, &size, + ADDR("sml-get-handover-size"), + ibmvtpm_inst) != 0 || size == 0) { + prom_printf("SML get handover size failed\n"); + return; + } } base = alloc_down(size, PAGE_SIZE, 0); @@ -1465,6 +1472,8 @@ static void __init prom_instantiate_sml(void) prom_printf("instantiating sml at 0x%x...", base); + memset((void *)base, 0, size); + if (call_prom_ret("call-method", 4, 2, &entry, ADDR("sml-handover"), ibmvtpm_inst, size, base) != 0 || entry == 0) { -- cgit v0.10.2 From 09e23a4a5dc39ef874403f3f6fa419f49db3677a Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Fri, 10 Jul 2015 14:32:29 +0900 Subject: char: Drop owner assignment from i2c_driver i2c_driver does not need to set an owner because i2c_register_driver() will set it. Signed-off-by: Krzysztof Kozlowski Reviewed-by: Peter Huewe Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/st33zp24/i2c.c b/drivers/char/tpm/st33zp24/i2c.c index ad1ee18..309d276 100644 --- a/drivers/char/tpm/st33zp24/i2c.c +++ b/drivers/char/tpm/st33zp24/i2c.c @@ -258,7 +258,6 @@ static SIMPLE_DEV_PM_OPS(st33zp24_i2c_ops, st33zp24_pm_suspend, static struct i2c_driver st33zp24_i2c_driver = { .driver = { - .owner = THIS_MODULE, .name = TPM_ST33_I2C, .pm = &st33zp24_i2c_ops, .of_match_table = of_match_ptr(of_st33zp24_i2c_match), diff --git a/drivers/char/tpm/tpm_i2c_atmel.c b/drivers/char/tpm/tpm_i2c_atmel.c index 7a0ca78..8dfb88b 100644 --- a/drivers/char/tpm/tpm_i2c_atmel.c +++ b/drivers/char/tpm/tpm_i2c_atmel.c @@ -217,7 +217,6 @@ static struct i2c_driver i2c_atmel_driver = { .remove = i2c_atmel_remove, .driver = { .name = I2C_DRIVER_NAME, - .owner = THIS_MODULE, .pm = &i2c_atmel_pm_ops, .of_match_table = of_match_ptr(i2c_atmel_of_match), }, diff --git a/drivers/char/tpm/tpm_i2c_infineon.c b/drivers/char/tpm/tpm_i2c_infineon.c index 33c5f36..63d5d22 100644 --- a/drivers/char/tpm/tpm_i2c_infineon.c +++ b/drivers/char/tpm/tpm_i2c_infineon.c @@ -711,7 +711,6 @@ static struct i2c_driver tpm_tis_i2c_driver = { .remove = tpm_tis_i2c_remove, .driver = { .name = "tpm_i2c_infineon", - .owner = THIS_MODULE, .pm = &tpm_tis_i2c_ops, .of_match_table = of_match_ptr(tpm_tis_i2c_of_match), }, diff --git a/drivers/char/tpm/tpm_i2c_nuvoton.c b/drivers/char/tpm/tpm_i2c_nuvoton.c index 9d42b7d..847f159 100644 --- a/drivers/char/tpm/tpm_i2c_nuvoton.c +++ b/drivers/char/tpm/tpm_i2c_nuvoton.c @@ -641,7 +641,6 @@ static struct i2c_driver i2c_nuvoton_driver = { .remove = i2c_nuvoton_remove, .driver = { .name = I2C_DRIVER_NAME, - .owner = THIS_MODULE, .pm = &i2c_nuvoton_pm_ops, .of_match_table = of_match_ptr(i2c_nuvoton_of_match), }, -- cgit v0.10.2 From 0cc698af36ff22295dce2f5e46b0cfe605789fa4 Mon Sep 17 00:00:00 2001 From: "Hon Ching \\(Vicky\\) Lo" Date: Wed, 17 Jun 2015 18:17:08 -0400 Subject: vTPM: support little endian guests This patch makes the code endianness independent. We defined a macro do_endian_conversion to apply endianness to raw integers in the event entries so that they will be displayed properly. tpm_binary_bios_measurements_show() is modified for the display. Signed-off-by: Hon Ching(Vicky) Lo Signed-off-by: Joy Latten Reviewed-by: Ashley Lai Reviewed-by: Ashley Lai Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm_eventlog.c b/drivers/char/tpm/tpm_eventlog.c index 3a56a13..bd72fb0 100644 --- a/drivers/char/tpm/tpm_eventlog.c +++ b/drivers/char/tpm/tpm_eventlog.c @@ -76,15 +76,25 @@ static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) void *addr = log->bios_event_log; void *limit = log->bios_event_log_end; struct tcpa_event *event; + u32 converted_event_size; + u32 converted_event_type; + /* read over *pos measurements */ for (i = 0; i < *pos; i++) { event = addr; + converted_event_size = + do_endian_conversion(event->event_size); + converted_event_type = + do_endian_conversion(event->event_type); + if ((addr + sizeof(struct tcpa_event)) < limit) { - if (event->event_type == 0 && event->event_size == 0) + if ((converted_event_type == 0) && + (converted_event_size == 0)) return NULL; - addr += sizeof(struct tcpa_event) + event->event_size; + addr += (sizeof(struct tcpa_event) + + converted_event_size); } } @@ -94,8 +104,12 @@ static void *tpm_bios_measurements_start(struct seq_file *m, loff_t *pos) event = addr; - if ((event->event_type == 0 && event->event_size == 0) || - ((addr + sizeof(struct tcpa_event) + event->event_size) >= limit)) + converted_event_size = do_endian_conversion(event->event_size); + converted_event_type = do_endian_conversion(event->event_type); + + if (((converted_event_type == 0) && (converted_event_size == 0)) + || ((addr + sizeof(struct tcpa_event) + converted_event_size) + >= limit)) return NULL; return addr; @@ -107,8 +121,12 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, struct tcpa_event *event = v; struct tpm_bios_log *log = m->private; void *limit = log->bios_event_log_end; + u32 converted_event_size; + u32 converted_event_type; - v += sizeof(struct tcpa_event) + event->event_size; + converted_event_size = do_endian_conversion(event->event_size); + + v += sizeof(struct tcpa_event) + converted_event_size; /* now check if current entry is valid */ if ((v + sizeof(struct tcpa_event)) >= limit) @@ -116,11 +134,11 @@ static void *tpm_bios_measurements_next(struct seq_file *m, void *v, event = v; - if (event->event_type == 0 && event->event_size == 0) - return NULL; + converted_event_size = do_endian_conversion(event->event_size); + converted_event_type = do_endian_conversion(event->event_type); - if ((event->event_type == 0 && event->event_size == 0) || - ((v + sizeof(struct tcpa_event) + event->event_size) >= limit)) + if (((converted_event_type == 0) && (converted_event_size == 0)) || + ((v + sizeof(struct tcpa_event) + converted_event_size) >= limit)) return NULL; (*pos)++; @@ -140,7 +158,7 @@ static int get_event_name(char *dest, struct tcpa_event *event, int i, n_len = 0, d_len = 0; struct tcpa_pc_event *pc_event; - switch(event->event_type) { + switch (do_endian_conversion(event->event_type)) { case PREBOOT: case POST_CODE: case UNUSED: @@ -156,14 +174,16 @@ static int get_event_name(char *dest, struct tcpa_event *event, case NONHOST_CODE: case NONHOST_CONFIG: case NONHOST_INFO: - name = tcpa_event_type_strings[event->event_type]; + name = tcpa_event_type_strings[do_endian_conversion + (event->event_type)]; n_len = strlen(name); break; case SEPARATOR: case ACTION: - if (MAX_TEXT_EVENT > event->event_size) { + if (MAX_TEXT_EVENT > + do_endian_conversion(event->event_size)) { name = event_entry; - n_len = event->event_size; + n_len = do_endian_conversion(event->event_size); } break; case EVENT_TAG: @@ -171,7 +191,7 @@ static int get_event_name(char *dest, struct tcpa_event *event, /* ToDo Row data -> Base64 */ - switch (pc_event->event_id) { + switch (do_endian_conversion(pc_event->event_id)) { case SMBIOS: case BIS_CERT: case CMOS: @@ -179,7 +199,8 @@ static int get_event_name(char *dest, struct tcpa_event *event, case OPTION_ROM_EXEC: case OPTION_ROM_CONFIG: case S_CRTM_VERSION: - name = tcpa_pc_event_id_strings[pc_event->event_id]; + name = tcpa_pc_event_id_strings[do_endian_conversion + (pc_event->event_id)]; n_len = strlen(name); break; /* hash data */ @@ -188,7 +209,8 @@ static int get_event_name(char *dest, struct tcpa_event *event, case OPTION_ROM_MICROCODE: case S_CRTM_CONTENTS: case POST_CONTENTS: - name = tcpa_pc_event_id_strings[pc_event->event_id]; + name = tcpa_pc_event_id_strings[do_endian_conversion + (pc_event->event_id)]; n_len = strlen(name); for (i = 0; i < 20; i++) d_len += sprintf(&data[2*i], "%02x", @@ -209,13 +231,24 @@ static int get_event_name(char *dest, struct tcpa_event *event, static int tpm_binary_bios_measurements_show(struct seq_file *m, void *v) { struct tcpa_event *event = v; - char *data = v; + struct tcpa_event temp_event; + char *tempPtr; int i; - for (i = 0; i < sizeof(struct tcpa_event) + event->event_size; i++) - seq_putc(m, data[i]); + memcpy(&temp_event, event, sizeof(struct tcpa_event)); + + /* convert raw integers for endianness */ + temp_event.pcr_index = do_endian_conversion(event->pcr_index); + temp_event.event_type = do_endian_conversion(event->event_type); + temp_event.event_size = do_endian_conversion(event->event_size); + + tempPtr = (char *)&temp_event; + + for (i = 0; i < sizeof(struct tcpa_event) + temp_event.event_size; i++) + seq_putc(m, tempPtr[i]); return 0; + } static int tpm_bios_measurements_release(struct inode *inode, @@ -238,7 +271,7 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) char *eventname; struct tcpa_event *event = v; unsigned char *event_entry = - (unsigned char *) (v + sizeof(struct tcpa_event)); + (unsigned char *)(v + sizeof(struct tcpa_event)); eventname = kmalloc(MAX_TEXT_EVENT, GFP_KERNEL); if (!eventname) { @@ -247,13 +280,14 @@ static int tpm_ascii_bios_measurements_show(struct seq_file *m, void *v) return -EFAULT; } - seq_printf(m, "%2d ", event->pcr_index); + /* 1st: PCR */ + seq_printf(m, "%2d ", do_endian_conversion(event->pcr_index)); /* 2nd: SHA1 */ seq_printf(m, "%20phN", event->pcr_value); /* 3rd: event type identifier */ - seq_printf(m, " %02x", event->event_type); + seq_printf(m, " %02x", do_endian_conversion(event->event_type)); len += get_event_name(eventname, event, event_entry); diff --git a/drivers/char/tpm/tpm_eventlog.h b/drivers/char/tpm/tpm_eventlog.h index e7da086..267bfbd 100644 --- a/drivers/char/tpm/tpm_eventlog.h +++ b/drivers/char/tpm/tpm_eventlog.h @@ -6,6 +6,12 @@ #define MAX_TEXT_EVENT 1000 /* Max event string length */ #define ACPI_TCPA_SIG "TCPA" /* 0x41504354 /'TCPA' */ +#ifdef CONFIG_PPC64 +#define do_endian_conversion(x) be32_to_cpu(x) +#else +#define do_endian_conversion(x) x +#endif + enum bios_platform_class { BIOS_CLIENT = 0x00, BIOS_SERVER = 0x01, -- cgit v0.10.2 From d72c39114c3f36785bb71aaf9dea642e59723abc Mon Sep 17 00:00:00 2001 From: "Hon Ching \\(Vicky\\) Lo" Date: Wed, 17 Jun 2015 18:17:09 -0400 Subject: TPM: remove unnecessary little endian conversion The base pointer for the event log is allocated in the local kernel (in prom_instantiate_sml()), therefore it is already in the host's endian byte order and requires no conversion. The content of the 'basep' pointer in read_log() stores the base address of the log. This patch ensures that it is correctly implemented. Signed-off-by: Hon Ching(Vicky) Lo Signed-off-by: Joy Latten Reviewed-by: Ashley Lai Signed-off-by: Peter Huewe diff --git a/drivers/char/tpm/tpm_of.c b/drivers/char/tpm/tpm_of.c index 66b3be4..1141456 100644 --- a/drivers/char/tpm/tpm_of.c +++ b/drivers/char/tpm/tpm_of.c @@ -24,7 +24,7 @@ int read_log(struct tpm_bios_log *log) { struct device_node *np; const u32 *sizep; - const __be64 *basep; + const u64 *basep; if (log->bios_event_log != NULL) { pr_err("%s: ERROR - Eventlog already initialized\n", __func__); @@ -63,7 +63,7 @@ int read_log(struct tpm_bios_log *log) log->bios_event_log_end = log->bios_event_log + *sizep; - memcpy(log->bios_event_log, __va(be64_to_cpup(basep)), *sizep); + memcpy(log->bios_event_log, __va(*basep), *sizep); return 0; -- cgit v0.10.2 From 38416e53936ecf896948fdeffc36b76979117952 Mon Sep 17 00:00:00 2001 From: Zbigniew Jasinski Date: Mon, 19 Oct 2015 18:23:53 +0200 Subject: Smack: limited capability for changing process label This feature introduces new kernel interface: - /relabel-self - for setting transition labels list This list is used to control smack label transition mechanism. List is set by, and per process. Process can transit to new label only if label is on the list. Only process with CAP_MAC_ADMIN capability can add labels to this list. With this list, process can change it's label without CAP_MAC_ADMIN but only once. After label changing, list is unset. Changes in v2: * use list_for_each_entry instead of _rcu during label write * added missing description in security/Smack.txt Changes in v3: * squashed into one commit Changes in v4: * switch from global list to per-task list * since the per-task list is accessed only by the task itself there is no need to use synchronization mechanisms on it Changes in v5: * change smackfs interface of relabel-self to the one used for onlycap multiple labels are accepted, separated by space, which replace the previous list upon write Signed-off-by: Zbigniew Jasinski Signed-off-by: Rafal Krypa Acked-by: Casey Schaufler diff --git a/Documentation/security/Smack.txt b/Documentation/security/Smack.txt index 5e6d07f..945cc63 100644 --- a/Documentation/security/Smack.txt +++ b/Documentation/security/Smack.txt @@ -255,6 +255,16 @@ unconfined the access permitted if it wouldn't be otherwise. Note that this is dangerous and can ruin the proper labeling of your system. It should never be used in production. +relabel-self + This interface contains a list of labels to which the process can + transition to, by writing to /proc/self/attr/current. + Normally a process can change its own label to any legal value, but only + if it has CAP_MAC_ADMIN. This interface allows a process without + CAP_MAC_ADMIN to relabel itself to one of labels from predefined list. + A process without CAP_MAC_ADMIN can change its label only once. When it + does, this list will be cleared. + The values are set by writing the desired labels, separated + by spaces, to the file or cleared by writing "-" to the file. If you are using the smackload utility you can add access rules in /etc/smack/accesses. They take the form: diff --git a/security/smack/smack.h b/security/smack/smack.h index fff0c61..6c91156 100644 --- a/security/smack/smack.h +++ b/security/smack/smack.h @@ -115,6 +115,7 @@ struct task_smack { struct smack_known *smk_forked; /* label when forked */ struct list_head smk_rules; /* per task access rules */ struct mutex smk_rules_lock; /* lock for the rules */ + struct list_head smk_relabel; /* transit allowed labels */ }; #define SMK_INODE_INSTANT 0x01 /* inode is instantiated */ @@ -169,7 +170,7 @@ struct smk_port_label { }; #endif /* SMACK_IPV6_PORT_LABELING */ -struct smack_onlycap { +struct smack_known_list_elem { struct list_head list; struct smack_known *smk_label; }; @@ -301,6 +302,7 @@ struct smack_known *smk_import_entry(const char *, int); void smk_insert_entry(struct smack_known *skp); struct smack_known *smk_find_entry(const char *); int smack_privileged(int cap); +void smk_destroy_label_list(struct list_head *list); /* * Shared data. diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index bc1053f..a283f9e 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -637,7 +637,7 @@ DEFINE_MUTEX(smack_onlycap_lock); int smack_privileged(int cap) { struct smack_known *skp = smk_of_current(); - struct smack_onlycap *sop; + struct smack_known_list_elem *sklep; /* * All kernel tasks are privileged @@ -654,8 +654,8 @@ int smack_privileged(int cap) return 1; } - list_for_each_entry_rcu(sop, &smack_onlycap_list, list) { - if (sop->smk_label == skp) { + list_for_each_entry_rcu(sklep, &smack_onlycap_list, list) { + if (sklep->smk_label == skp) { rcu_read_unlock(); return 1; } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index c2d66ca..ff81026 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -326,6 +326,7 @@ static struct task_smack *new_task_smack(struct smack_known *task, tsp->smk_task = task; tsp->smk_forked = forked; INIT_LIST_HEAD(&tsp->smk_rules); + INIT_LIST_HEAD(&tsp->smk_relabel); mutex_init(&tsp->smk_rules_lock); return tsp; @@ -361,6 +362,35 @@ static int smk_copy_rules(struct list_head *nhead, struct list_head *ohead, } /** + * smk_copy_relabel - copy smk_relabel labels list + * @nhead: new rules header pointer + * @ohead: old rules header pointer + * @gfp: type of the memory for the allocation + * + * Returns 0 on success, -ENOMEM on error + */ +static int smk_copy_relabel(struct list_head *nhead, struct list_head *ohead, + gfp_t gfp) +{ + struct smack_known_list_elem *nklep; + struct smack_known_list_elem *oklep; + + INIT_LIST_HEAD(nhead); + + list_for_each_entry(oklep, ohead, list) { + nklep = kzalloc(sizeof(struct smack_known_list_elem), gfp); + if (nklep == NULL) { + smk_destroy_label_list(nhead); + return -ENOMEM; + } + nklep->smk_label = oklep->smk_label; + list_add(&nklep->list, nhead); + } + + return 0; +} + +/** * smk_ptrace_mode - helper function for converting PTRACE_MODE_* into MAY_* * @mode - input mode in form of PTRACE_MODE_* * @@ -1922,6 +1952,8 @@ static void smack_cred_free(struct cred *cred) return; cred->security = NULL; + smk_destroy_label_list(&tsp->smk_relabel); + list_for_each_safe(l, n, &tsp->smk_rules) { rp = list_entry(l, struct smack_rule, list); list_del(&rp->list); @@ -1953,6 +1985,11 @@ static int smack_cred_prepare(struct cred *new, const struct cred *old, if (rc != 0) return rc; + rc = smk_copy_relabel(&new_tsp->smk_relabel, &old_tsp->smk_relabel, + gfp); + if (rc != 0) + return rc; + new->security = new_tsp; return 0; } @@ -3552,9 +3589,11 @@ static int smack_getprocattr(struct task_struct *p, char *name, char **value) static int smack_setprocattr(struct task_struct *p, char *name, void *value, size_t size) { - struct task_smack *tsp; + struct task_smack *tsp = current_security(); struct cred *new; struct smack_known *skp; + struct smack_known_list_elem *sklep; + int rc; /* * Changing another process' Smack value is too dangerous @@ -3563,7 +3602,7 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (p != current) return -EPERM; - if (!smack_privileged(CAP_MAC_ADMIN)) + if (!smack_privileged(CAP_MAC_ADMIN) && list_empty(&tsp->smk_relabel)) return -EPERM; if (value == NULL || size == 0 || size >= SMK_LONGLABEL) @@ -3582,12 +3621,27 @@ static int smack_setprocattr(struct task_struct *p, char *name, if (skp == &smack_known_web) return -EPERM; + if (!smack_privileged(CAP_MAC_ADMIN)) { + rc = -EPERM; + list_for_each_entry(sklep, &tsp->smk_relabel, list) + if (sklep->smk_label == skp) { + rc = 0; + break; + } + if (rc) + return rc; + } + new = prepare_creds(); if (new == NULL) return -ENOMEM; tsp = new->security; tsp->smk_task = skp; + /* + * process can change its label only once + */ + smk_destroy_label_list(&tsp->smk_relabel); commit_creds(new); return size; diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index ce8d503..94bd9e4 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -61,6 +61,7 @@ enum smk_inos { #if IS_ENABLED(CONFIG_IPV6) SMK_NET6ADDR = 23, /* single label IPv6 hosts */ #endif /* CONFIG_IPV6 */ + SMK_RELABEL_SELF = 24, /* relabel possible without CAP_MAC_ADMIN */ }; /* @@ -1914,10 +1915,10 @@ static void *onlycap_seq_next(struct seq_file *s, void *v, loff_t *pos) static int onlycap_seq_show(struct seq_file *s, void *v) { struct list_head *list = v; - struct smack_onlycap *sop = - list_entry_rcu(list, struct smack_onlycap, list); + struct smack_known_list_elem *sklep = + list_entry_rcu(list, struct smack_known_list_elem, list); - seq_puts(s, sop->smk_label->smk_known); + seq_puts(s, sklep->smk_label->smk_known); seq_putc(s, ' '); return 0; @@ -1974,6 +1975,54 @@ static void smk_list_swap_rcu(struct list_head *public, } /** + * smk_parse_label_list - parse list of Smack labels, separated by spaces + * + * @data: the string to parse + * @private: destination list + * + * Returns zero on success or error code, as appropriate + */ +static int smk_parse_label_list(char *data, struct list_head *list) +{ + char *tok; + struct smack_known *skp; + struct smack_known_list_elem *sklep; + + while ((tok = strsep(&data, " ")) != NULL) { + if (!*tok) + continue; + + skp = smk_import_entry(tok, 0); + if (IS_ERR(skp)) + return PTR_ERR(skp); + + sklep = kzalloc(sizeof(*sklep), GFP_KERNEL); + if (sklep == NULL) + return -ENOMEM; + + sklep->smk_label = skp; + list_add(&sklep->list, list); + } + + return 0; +} + +/** + * smk_destroy_label_list - destroy a list of smack_known_list_elem + * @head: header pointer of the list to destroy + */ +void smk_destroy_label_list(struct list_head *list) +{ + struct smack_known_list_elem *sklep; + struct smack_known_list_elem *sklep2; + + list_for_each_entry_safe(sklep, sklep2, list, list) + kfree(sklep); + + INIT_LIST_HEAD(list); +} + +/** * smk_write_onlycap - write() for smackfs/onlycap * @file: file pointer, not actually used * @buf: where to get the data from @@ -1986,13 +2035,8 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { char *data; - char *data_parse; - char *tok; - struct smack_known *skp; - struct smack_onlycap *sop; - struct smack_onlycap *sop2; LIST_HEAD(list_tmp); - int rc = count; + int rc; if (!smack_privileged(CAP_MAC_ADMIN)) return -EPERM; @@ -2006,26 +2050,7 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, return -EFAULT; } - data_parse = data; - while ((tok = strsep(&data_parse, " ")) != NULL) { - if (!*tok) - continue; - - skp = smk_import_entry(tok, 0); - if (IS_ERR(skp)) { - rc = PTR_ERR(skp); - break; - } - - sop = kzalloc(sizeof(*sop), GFP_KERNEL); - if (sop == NULL) { - rc = -ENOMEM; - break; - } - - sop->smk_label = skp; - list_add_rcu(&sop->list, &list_tmp); - } + rc = smk_parse_label_list(data, &list_tmp); kfree(data); /* @@ -2038,17 +2063,14 @@ static ssize_t smk_write_onlycap(struct file *file, const char __user *buf, * But do so only on invalid label, not on system errors. * The invalid label must be first to count as clearing attempt. */ - if (rc == -EINVAL && list_empty(&list_tmp)) - rc = count; - - if (rc >= 0) { + if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) { mutex_lock(&smack_onlycap_lock); smk_list_swap_rcu(&smack_onlycap_list, &list_tmp); mutex_unlock(&smack_onlycap_lock); + rc = count; } - list_for_each_entry_safe(sop, sop2, &list_tmp, list) - kfree(sop); + smk_destroy_label_list(&list_tmp); return rc; } @@ -2698,6 +2720,113 @@ static const struct file_operations smk_syslog_ops = { .llseek = default_llseek, }; +/* + * Seq_file read operations for /smack/relabel-self + */ + +static void *relabel_self_seq_start(struct seq_file *s, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + + return smk_seq_start(s, pos, &tsp->smk_relabel); +} + +static void *relabel_self_seq_next(struct seq_file *s, void *v, loff_t *pos) +{ + struct task_smack *tsp = current_security(); + + return smk_seq_next(s, v, pos, &tsp->smk_relabel); +} + +static int relabel_self_seq_show(struct seq_file *s, void *v) +{ + struct list_head *list = v; + struct smack_known_list_elem *sklep = + list_entry(list, struct smack_known_list_elem, list); + + seq_puts(s, sklep->smk_label->smk_known); + seq_putc(s, ' '); + + return 0; +} + +static const struct seq_operations relabel_self_seq_ops = { + .start = relabel_self_seq_start, + .next = relabel_self_seq_next, + .show = relabel_self_seq_show, + .stop = smk_seq_stop, +}; + +/** + * smk_open_relabel_self - open() for /smack/relabel-self + * @inode: inode structure representing file + * @file: "relabel-self" file pointer + * + * Connect our relabel_self_seq_* operations with /smack/relabel-self + * file_operations + */ +static int smk_open_relabel_self(struct inode *inode, struct file *file) +{ + return seq_open(file, &relabel_self_seq_ops); +} + +/** + * smk_write_relabel_self - write() for /smack/relabel-self + * @file: file pointer, not actually used + * @buf: where to get the data from + * @count: bytes sent + * @ppos: where to start - must be 0 + * + */ +static ssize_t smk_write_relabel_self(struct file *file, const char __user *buf, + size_t count, loff_t *ppos) +{ + struct task_smack *tsp = current_security(); + char *data; + int rc; + LIST_HEAD(list_tmp); + + /* + * Must have privilege. + */ + if (!smack_privileged(CAP_MAC_ADMIN)) + return -EPERM; + + /* + * Enough data must be present. + */ + if (*ppos != 0) + return -EINVAL; + + data = kzalloc(count + 1, GFP_KERNEL); + if (data == NULL) + return -ENOMEM; + + if (copy_from_user(data, buf, count) != 0) { + kfree(data); + return -EFAULT; + } + + rc = smk_parse_label_list(data, &list_tmp); + kfree(data); + + if (!rc || (rc == -EINVAL && list_empty(&list_tmp))) { + smk_destroy_label_list(&tsp->smk_relabel); + list_splice(&list_tmp, &tsp->smk_relabel); + return count; + } + + smk_destroy_label_list(&list_tmp); + return rc; +} + +static const struct file_operations smk_relabel_self_ops = { + .open = smk_open_relabel_self, + .read = seq_read, + .llseek = seq_lseek, + .write = smk_write_relabel_self, + .release = seq_release, +}; /** * smk_read_ptrace - read() for /smack/ptrace @@ -2824,6 +2953,9 @@ static int smk_fill_super(struct super_block *sb, void *data, int silent) [SMK_NET6ADDR] = { "ipv6host", &smk_net6addr_ops, S_IRUGO|S_IWUSR}, #endif /* CONFIG_IPV6 */ + [SMK_RELABEL_SELF] = { + "relabel-self", &smk_relabel_self_ops, + S_IRUGO|S_IWUGO}, /* last one */ {""} }; -- cgit v0.10.2 From d0e0eba043c78b1d6ce3d29367abb41446d83747 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 21 Oct 2015 14:04:46 +0100 Subject: KEYS: use kvfree() in add_key There is no need to make a flag to tell that this memory is allocated by kmalloc or vmalloc. Just use kvfree to free the memory. Signed-off-by: Geliang Tang Signed-off-by: David Howells diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 0b9ec78..6110fa4 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -67,7 +67,6 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, char type[32], *description; void *payload; long ret; - bool vm; ret = -EINVAL; if (plen > 1024 * 1024 - 1) @@ -98,14 +97,12 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, /* pull the payload in if one was supplied */ payload = NULL; - vm = false; if (_payload) { ret = -ENOMEM; payload = kmalloc(plen, GFP_KERNEL | __GFP_NOWARN); if (!payload) { if (plen <= PAGE_SIZE) goto error2; - vm = true; payload = vmalloc(plen); if (!payload) goto error2; @@ -138,10 +135,7 @@ SYSCALL_DEFINE5(add_key, const char __user *, _type, key_ref_put(keyring_ref); error3: - if (!vm) - kfree(payload); - else - vfree(payload); + kvfree(payload); error2: kfree(description); error: -- cgit v0.10.2 From 48dbc164b40dd9195dea8cd966e394819e420b64 Mon Sep 17 00:00:00 2001 From: Paul Gortmaker Date: Wed, 21 Oct 2015 14:04:47 +0100 Subject: certs: add .gitignore to stop git nagging about x509_certificate_list Currently we see this in "git status" if we build in the source dir: Untracked files: (use "git add ..." to include in what will be committed) certs/x509_certificate_list It looks like it used to live in kernel/ so we squash that .gitignore entry at the same time. I didn't bother to dig through git history to see when it moved, since it is just a minor annoyance at most. Cc: David Woodhouse Cc: keyrings@linux-nfs.org Signed-off-by: Paul Gortmaker Signed-off-by: David Howells diff --git a/certs/.gitignore b/certs/.gitignore new file mode 100644 index 0000000..f51aea4 --- /dev/null +++ b/certs/.gitignore @@ -0,0 +1,4 @@ +# +# Generated files +# +x509_certificate_list diff --git a/kernel/.gitignore b/kernel/.gitignore index 790d83c..b3097bd 100644 --- a/kernel/.gitignore +++ b/kernel/.gitignore @@ -5,4 +5,3 @@ config_data.h config_data.gz timeconst.h hz.bc -x509_certificate_list -- cgit v0.10.2 From 27720e75a7a1597252a81dadcd178331c83af861 Mon Sep 17 00:00:00 2001 From: Insu Yun Date: Wed, 21 Oct 2015 14:04:47 +0100 Subject: keys: Be more consistent in selection of union members used key->description and key->index_key.description are same because they are unioned. But, for readability, using same name for duplication and validation seems better. Signed-off-by: Insu Yun Signed-off-by: David Howells diff --git a/security/keys/key.c b/security/keys/key.c index aee2ec5..c047846 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -278,7 +278,7 @@ struct key *key_alloc(struct key_type *type, const char *desc, key->index_key.desc_len = desclen; key->index_key.description = kmemdup(desc, desclen + 1, GFP_KERNEL); - if (!key->description) + if (!key->index_key.description) goto no_memory_3; atomic_set(&key->usage, 1); -- cgit v0.10.2 From 2221a6ee73e7c8f43af802a1ef9426d4b0d122d3 Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 21 Oct 2015 14:04:47 +0100 Subject: KEYS: Provide a script to extract the sys cert list from a vmlinux file The supplied script takes a vmlinux file - and if necessary a System.map file - locates the system certificates list and extracts it to the named file. Call as: ./scripts/extract-sys-certs vmlinux certs if vmlinux contains symbols and: ./scripts/extract-sys-certs -s System.map vmlinux certs if it does not. It prints something like the following to stdout: Have 27 sections No symbols in vmlinux, trying System.map Have 80088 symbols Have 1346 bytes of certs at VMA 0xffffffff8201c540 Certificate list in section .init.data Certificate list at file offset 0x141c540 If vmlinux contains symbols then that is used rather than System.map - even if one is given. Signed-off-by: David Howells diff --git a/scripts/extract-sys-certs.pl b/scripts/extract-sys-certs.pl new file mode 100755 index 0000000..d476e7d --- /dev/null +++ b/scripts/extract-sys-certs.pl @@ -0,0 +1,144 @@ +#!/usr/bin/perl -w +# +use strict; +use Math::BigInt; +use Fcntl "SEEK_SET"; + +die "Format: $0 [-s ] \n" + if ($#ARGV != 1 && $#ARGV != 3 || + $#ARGV == 3 && $ARGV[0] ne "-s"); + +my $sysmap = ""; +if ($#ARGV == 3) { + shift; + $sysmap = $ARGV[0]; + shift; +} + +my $vmlinux = $ARGV[0]; +my $keyring = $ARGV[1]; + +# +# Parse the vmlinux section table +# +open FD, "objdump -h $vmlinux |" || die $vmlinux; +my @lines = ; +close(FD) || die $vmlinux; + +my @sections = (); + +foreach my $line (@lines) { + chomp($line); + if ($line =~ /\s*([0-9]+)\s+(\S+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+([0-9a-f]+)\s+2[*][*]([0-9]+)/ + ) { + my $seg = $1; + my $name = $2; + my $len = Math::BigInt->new("0x" . $3); + my $vma = Math::BigInt->new("0x" . $4); + my $lma = Math::BigInt->new("0x" . $5); + my $foff = Math::BigInt->new("0x" . $6); + my $align = 2 ** $7; + + push @sections, { name => $name, + vma => $vma, + len => $len, + foff => $foff }; + } +} + +print "Have $#sections sections\n"; + +# +# Try and parse the vmlinux symbol table. If the vmlinux file has been created +# from a vmlinuz file with extract-vmlinux then the symbol table will be empty. +# +open FD, "nm $vmlinux 2>/dev/null |" || die $vmlinux; +@lines = ; +close(FD) || die $vmlinux; + +my %symbols = (); +my $nr_symbols = 0; + +sub parse_symbols(@) { + foreach my $line (@_) { + chomp($line); + if ($line =~ /([0-9a-f]+)\s([a-zA-Z])\s(\S+)/ + ) { + my $addr = "0x" . $1; + my $type = $2; + my $name = $3; + + $symbols{$name} = $addr; + $nr_symbols++; + } + } +} +parse_symbols(@lines); + +if ($nr_symbols == 0 && $sysmap ne "") { + print "No symbols in vmlinux, trying $sysmap\n"; + + open FD, "<$sysmap" || die $sysmap; + @lines = ; + close(FD) || die $sysmap; + parse_symbols(@lines); +} + +die "No symbols available\n" + if ($nr_symbols == 0); + +print "Have $nr_symbols symbols\n"; + +die "Can't find system certificate list" + unless (exists($symbols{"__cert_list_start"}) && + exists($symbols{"__cert_list_end"})); + +my $start = Math::BigInt->new($symbols{"__cert_list_start"}); +my $end = Math::BigInt->new($symbols{"__cert_list_end"}); +my $size = $end - $start; + +printf "Have %u bytes of certs at VMA 0x%x\n", $size, $start; + +my $s = undef; +foreach my $sec (@sections) { + my $s_name = $sec->{name}; + my $s_vma = $sec->{vma}; + my $s_len = $sec->{len}; + my $s_foff = $sec->{foff}; + my $s_vend = $s_vma + $s_len; + + next unless ($start >= $s_vma); + next if ($start >= $s_vend); + + die "Cert object partially overflows section $s_name\n" + if ($end > $s_vend); + + die "Cert object in multiple sections: ", $s_name, " and ", $s->{name}, "\n" + if ($s); + $s = $sec; +} + +die "Cert object not inside a section\n" + unless ($s); + +print "Certificate list in section ", $s->{name}, "\n"; + +my $foff = $start - $s->{vma} + $s->{foff}; + +printf "Certificate list at file offset 0x%x\n", $foff; + +open FD, "<$vmlinux" || die $vmlinux; +binmode(FD); +die $vmlinux if (!defined(sysseek(FD, $foff, SEEK_SET))); +my $buf = ""; +my $len = sysread(FD, $buf, $size); +die "$vmlinux" if (!defined($len)); +die "Short read on $vmlinux\n" if ($len != $size); +close(FD) || die $vmlinux; + +open FD, ">$keyring" || die $keyring; +binmode(FD); +$len = syswrite(FD, $buf, $size); +die "$keyring" if (!defined($len)); +die "Short write on $keyring\n" if ($len != $size); +close(FD) || die $keyring; -- cgit v0.10.2 From 4adc605edc5f744dcf432241b5996ff6a13d868c Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 21 Oct 2015 14:04:47 +0100 Subject: KEYS: Provide a script to extract a module signature The supplied script takes a signed module file and extracts the tailmost signature (there could theoretically be more than one) and dumps all or part of it or the unsigned file to stdout. Call as: scripts/extract-module-sig.pl -[0adnks] module-file >out where the initial flag indicates which bit of the signed file you want dumping to stdout: (*) "-0". Dumps the unsigned data with the signature stripped. (*) "-a". Dumps all of the signature data, including the magic number. (*) "-d". Dumps the signature information block as a sequence of decimal numbers in text form with spaces between (crypto algorithm type, hash type, identifier type, signer's name length, key identifier length and signature length). (*) "-n". Dumps the signer's name contents. (*) "-k". Dumps the key identifier contents. (*) "-s". Dumps the cryptographic signature contents. In the case that the signature is a PKCS#7 (or CMS) message, -n and -k will print a warning to stderr and dump nothing to stdout, but will otherwise complete okay; the entire PKCS#7/CMS message will be dumped by "-s"; and "-d" will show "0 0 2 0 0 ". Signed-off-by: David Howells diff --git a/scripts/extract-module-sig.pl b/scripts/extract-module-sig.pl new file mode 100755 index 0000000..faac6f2 --- /dev/null +++ b/scripts/extract-module-sig.pl @@ -0,0 +1,136 @@ +#!/usr/bin/perl -w +# +# extract-mod-sig +# +# Reads the module file and writes out some or all of the signature +# section to stdout. Part is the bit to be written and is one of: +# +# -0: The unsigned module, no signature data at all +# -a: All of the signature data, including magic number +# -d: Just the descriptor values as a sequence of numbers +# -n: Just the signer's name +# -k: Just the key ID +# -s: Just the crypto signature or PKCS#7 message +# +use strict; + +die "Format: $0 -[0adnks] module-file >out\n" + if ($#ARGV != 1); + +my $part = $ARGV[0]; +my $modfile = $ARGV[1]; + +my $magic_number = "~Module signature appended~\n"; + +# +# Read the module contents +# +open FD, "<$modfile" || die $modfile; +binmode(FD); +my @st = stat(FD); +die "$modfile" unless (@st); +my $buf = ""; +my $len = sysread(FD, $buf, $st[7]); +die "$modfile" unless (defined($len)); +die "Short read on $modfile\n" unless ($len == $st[7]); +close(FD) || die $modfile; + +print STDERR "Read ", $len, " bytes from module file\n"; + +die "The file is too short to have a sig magic number and descriptor\n" + if ($len < 12 + length($magic_number)); + +# +# Check for the magic number and extract the information block +# +my $p = $len - length($magic_number); +my $raw_magic = substr($buf, $p); + +die "Magic number not found at $len\n" + if ($raw_magic ne $magic_number); +print STDERR "Found magic number at $len\n"; + +$p -= 12; +my $raw_info = substr($buf, $p, 12); + +my @info = unpack("CCCCCxxxN", $raw_info); +my ($algo, $hash, $id_type, $name_len, $kid_len, $sig_len) = @info; + +if ($id_type == 0) { + print STDERR "Found PGP key identifier\n"; +} elsif ($id_type == 1) { + print STDERR "Found X.509 cert identifier\n"; +} elsif ($id_type == 2) { + print STDERR "Found PKCS#7/CMS encapsulation\n"; +} else { + print STDERR "Found unsupported identifier type $id_type\n"; +} + +# +# Extract the three pieces of info data +# +die "Insufficient name+kid+sig data in file\n" + unless ($p >= $name_len + $kid_len + $sig_len); + +$p -= $sig_len; +my $raw_sig = substr($buf, $p, $sig_len); +$p -= $kid_len; +my $raw_kid = substr($buf, $p, $kid_len); +$p -= $name_len; +my $raw_name = substr($buf, $p, $name_len); + +my $module_len = $p; + +if ($sig_len > 0) { + print STDERR "Found $sig_len bytes of signature ["; + my $n = $sig_len > 16 ? 16 : $sig_len; + foreach my $i (unpack("C" x $n, substr($raw_sig, 0, $n))) { + printf STDERR "%02x", $i; + } + print STDERR "]\n"; +} + +if ($kid_len > 0) { + print STDERR "Found $kid_len bytes of key identifier ["; + my $n = $kid_len > 16 ? 16 : $kid_len; + foreach my $i (unpack("C" x $n, substr($raw_kid, 0, $n))) { + printf STDERR "%02x", $i; + } + print STDERR "]\n"; +} + +if ($name_len > 0) { + print STDERR "Found $name_len bytes of signer's name [$raw_name]\n"; +} + +# +# Produce the requested output +# +if ($part eq "-0") { + # The unsigned module, no signature data at all + binmode(STDOUT); + print substr($buf, 0, $module_len); +} elsif ($part eq "-a") { + # All of the signature data, including magic number + binmode(STDOUT); + print substr($buf, $module_len); +} elsif ($part eq "-d") { + # Just the descriptor values as a sequence of numbers + print join(" ", @info), "\n"; +} elsif ($part eq "-n") { + # Just the signer's name + print STDERR "No signer's name for PKCS#7 message type sig\n" + if ($id_type == 2); + binmode(STDOUT); + print $raw_name; +} elsif ($part eq "-k") { + # Just the key identifier + print STDERR "No key ID for PKCS#7 message type sig\n" + if ($id_type == 2); + binmode(STDOUT); + print $raw_kid; +} elsif ($part eq "-s") { + # Just the crypto signature or PKCS#7 message + binmode(STDOUT); + print $raw_sig; +} -- cgit v0.10.2 From 146aa8b1453bd8f1ff2304ffb71b4ee0eb9acdcc Mon Sep 17 00:00:00 2001 From: David Howells Date: Wed, 21 Oct 2015 14:04:48 +0100 Subject: KEYS: Merge the type-specific data with the payload data Merge the type-specific data with the payload data into one four-word chunk as it seems pointless to keep them separate. Use user_key_payload() for accessing the payloads of overloaded user-defined keys. Signed-off-by: David Howells cc: linux-cifs@vger.kernel.org cc: ecryptfs@vger.kernel.org cc: linux-ext4@vger.kernel.org cc: linux-f2fs-devel@lists.sourceforge.net cc: linux-nfs@vger.kernel.org cc: ceph-devel@vger.kernel.org cc: linux-ima-devel@lists.sourceforge.net diff --git a/Documentation/crypto/asymmetric-keys.txt b/Documentation/crypto/asymmetric-keys.txt index b767590..8c07e0e 100644 --- a/Documentation/crypto/asymmetric-keys.txt +++ b/Documentation/crypto/asymmetric-keys.txt @@ -186,7 +186,7 @@ and looks like the following: const struct public_key_signature *sig); }; -Asymmetric keys point to this with their type_data[0] member. +Asymmetric keys point to this with their payload[asym_subtype] member. The owner and name fields should be set to the owning module and the name of the subtype. Currently, the name is only used for print statements. @@ -269,8 +269,7 @@ mandatory: struct key_preparsed_payload { char *description; - void *type_data[2]; - void *payload; + void *payload[4]; const void *data; size_t datalen; size_t quotalen; @@ -283,16 +282,18 @@ mandatory: not theirs. If the parser is happy with the blob, it should propose a description for - the key and attach it to ->description, ->type_data[0] should be set to - point to the subtype to be used, ->payload should be set to point to the - initialised data for that subtype, ->type_data[1] should point to a hex - fingerprint and quotalen should be updated to indicate how much quota this - key should account for. - - When clearing up, the data attached to ->type_data[1] and ->description - will be kfree()'d and the data attached to ->payload will be passed to the - subtype's ->destroy() method to be disposed of. A module reference for - the subtype pointed to by ->type_data[0] will be put. + the key and attach it to ->description, ->payload[asym_subtype] should be + set to point to the subtype to be used, ->payload[asym_crypto] should be + set to point to the initialised data for that subtype, + ->payload[asym_key_ids] should point to one or more hex fingerprints and + quotalen should be updated to indicate how much quota this key should + account for. + + When clearing up, the data attached to ->payload[asym_key_ids] and + ->description will be kfree()'d and the data attached to + ->payload[asm_crypto] will be passed to the subtype's ->destroy() method + to be disposed of. A module reference for the subtype pointed to by + ->payload[asym_subtype] will be put. If the data format is not recognised, -EBADMSG should be returned. If it diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index c9e7f4f..8c18387 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1049,12 +1049,12 @@ search a specific keyring, so using keyrings in this way is of limited utility. NOTES ON ACCESSING PAYLOAD CONTENTS =================================== -The simplest payload is just a number in key->payload.value. In this case, -there's no need to indulge in RCU or locking when accessing the payload. +The simplest payload is just data stored in key->payload directly. In this +case, there's no need to indulge in RCU or locking when accessing the payload. -More complex payload contents must be allocated and a pointer to them set in -key->payload.data. One of the following ways must be selected to access the -data: +More complex payload contents must be allocated and pointers to them set in the +key->payload.data[] array. One of the following ways must be selected to +access the data: (1) Unmodifiable key type. @@ -1092,6 +1092,13 @@ data: the payload. key->datalen cannot be relied upon to be consistent with the payload just dereferenced if the key's semaphore is not held. + Note that key->payload.data[0] has a shadow that is marked for __rcu + usage. This is called key->payload.rcu_data0. The following accessors + wrap the RCU calls to this element: + + rcu_assign_keypointer(struct key *key, void *data); + void *rcu_dereference_key(struct key *key); + =================== DEFINING A KEY TYPE @@ -1143,8 +1150,7 @@ The structure has a number of fields, some of which are mandatory: struct key_preparsed_payload { char *description; - void *type_data[2]; - void *payload; + union key_payload payload; const void *data; size_t datalen; size_t quotalen; @@ -1160,10 +1166,9 @@ The structure has a number of fields, some of which are mandatory: attached as a string to the description field. This will be used for the key description if the caller of add_key() passes NULL or "". - The method can attach anything it likes to type_data[] and payload. These - are merely passed along to the instantiate() or update() operations. If - set, the expiry time will be applied to the key if it is instantiated from - this data. + The method can attach anything it likes to payload. This is merely passed + along to the instantiate() or update() operations. If set, the expiry + time will be applied to the key if it is instantiated from this data. The method should return 0 if successful or a negative error code otherwise. @@ -1172,11 +1177,10 @@ The structure has a number of fields, some of which are mandatory: (*) void (*free_preparse)(struct key_preparsed_payload *prep); This method is only required if the preparse() method is provided, - otherwise it is unused. It cleans up anything attached to the - description, type_data and payload fields of the key_preparsed_payload - struct as filled in by the preparse() method. It will always be called - after preparse() returns successfully, even if instantiate() or update() - succeed. + otherwise it is unused. It cleans up anything attached to the description + and payload fields of the key_preparsed_payload struct as filled in by the + preparse() method. It will always be called after preparse() returns + successfully, even if instantiate() or update() succeed. (*) int (*instantiate)(struct key *key, struct key_preparsed_payload *prep); @@ -1197,6 +1201,11 @@ The structure has a number of fields, some of which are mandatory: It is safe to sleep in this method. + generic_key_instantiate() is provided to simply copy the data from + prep->payload.data[] to key->payload.data[], with RCU-safe assignment on + the first element. It will then clear prep->payload.data[] so that the + free_preparse method doesn't release the data. + (*) int (*update)(struct key *key, const void *data, size_t datalen); diff --git a/crypto/asymmetric_keys/asymmetric_keys.h b/crypto/asymmetric_keys/asymmetric_keys.h index 3f5b537..1d450b5 100644 --- a/crypto/asymmetric_keys/asymmetric_keys.h +++ b/crypto/asymmetric_keys/asymmetric_keys.h @@ -14,8 +14,3 @@ extern struct asymmetric_key_id *asymmetric_key_hex_to_key_id(const char *id); extern int __asymmetric_key_hex_to_key_id(const char *id, struct asymmetric_key_id *match_id, size_t hexlen); -static inline -const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) -{ - return key->type_data.p[1]; -} diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index 1916680..9f2165b 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -307,25 +307,34 @@ static int asymmetric_key_preparse(struct key_preparsed_payload *prep) } /* + * Clean up the key ID list + */ +static void asymmetric_key_free_kids(struct asymmetric_key_ids *kids) +{ + int i; + + if (kids) { + for (i = 0; i < ARRAY_SIZE(kids->id); i++) + kfree(kids->id[i]); + kfree(kids); + } +} + +/* * Clean up the preparse data */ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) { - struct asymmetric_key_subtype *subtype = prep->type_data[0]; - struct asymmetric_key_ids *kids = prep->type_data[1]; - int i; + struct asymmetric_key_subtype *subtype = prep->payload.data[asym_subtype]; + struct asymmetric_key_ids *kids = prep->payload.data[asym_key_ids]; pr_devel("==>%s()\n", __func__); if (subtype) { - subtype->destroy(prep->payload[0]); + subtype->destroy(prep->payload.data[asym_crypto]); module_put(subtype->owner); } - if (kids) { - for (i = 0; i < ARRAY_SIZE(kids->id); i++) - kfree(kids->id[i]); - kfree(kids); - } + asymmetric_key_free_kids(kids); kfree(prep->description); } @@ -335,20 +344,19 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) static void asymmetric_key_destroy(struct key *key) { struct asymmetric_key_subtype *subtype = asymmetric_key_subtype(key); - struct asymmetric_key_ids *kids = key->type_data.p[1]; + struct asymmetric_key_ids *kids = key->payload.data[asym_key_ids]; + void *data = key->payload.data[asym_crypto]; + + key->payload.data[asym_crypto] = NULL; + key->payload.data[asym_subtype] = NULL; + key->payload.data[asym_key_ids] = NULL; if (subtype) { - subtype->destroy(key->payload.data); + subtype->destroy(data); module_put(subtype->owner); - key->type_data.p[0] = NULL; } - if (kids) { - kfree(kids->id[0]); - kfree(kids->id[1]); - kfree(kids); - key->type_data.p[1] = NULL; - } + asymmetric_key_free_kids(kids); } struct key_type key_type_asymmetric = { diff --git a/crypto/asymmetric_keys/public_key.c b/crypto/asymmetric_keys/public_key.c index 81efccb..6db4c01 100644 --- a/crypto/asymmetric_keys/public_key.c +++ b/crypto/asymmetric_keys/public_key.c @@ -49,7 +49,7 @@ EXPORT_SYMBOL_GPL(pkey_id_type_name); static void public_key_describe(const struct key *asymmetric_key, struct seq_file *m) { - struct public_key *key = asymmetric_key->payload.data; + struct public_key *key = asymmetric_key->payload.data[asym_crypto]; if (key) seq_printf(m, "%s.%s", @@ -112,7 +112,7 @@ EXPORT_SYMBOL_GPL(public_key_verify_signature); static int public_key_verify_signature_2(const struct key *key, const struct public_key_signature *sig) { - const struct public_key *pk = key->payload.data; + const struct public_key *pk = key->payload.data[asym_crypto]; return public_key_verify_signature(pk, sig); } diff --git a/crypto/asymmetric_keys/signature.c b/crypto/asymmetric_keys/signature.c index 7525fd1..9441240 100644 --- a/crypto/asymmetric_keys/signature.c +++ b/crypto/asymmetric_keys/signature.c @@ -37,7 +37,7 @@ int verify_signature(const struct key *key, return -EINVAL; subtype = asymmetric_key_subtype(key); if (!subtype || - !key->payload.data) + !key->payload.data[0]) return -EINVAL; if (!subtype->verify_signature) return -ENOTSUPP; diff --git a/crypto/asymmetric_keys/x509_parser.h b/crypto/asymmetric_keys/x509_parser.h index 1de01ea..dbeed60 100644 --- a/crypto/asymmetric_keys/x509_parser.h +++ b/crypto/asymmetric_keys/x509_parser.h @@ -11,6 +11,7 @@ #include #include +#include struct x509_certificate { struct x509_certificate *next; diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 1970966..64d4298 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -266,7 +266,8 @@ static int x509_validate_trust(struct x509_certificate *cert, if (!IS_ERR(key)) { if (!use_builtin_keys || test_bit(KEY_FLAG_BUILTIN, &key->flags)) - ret = x509_check_signature(key->payload.data, cert); + ret = x509_check_signature(key->payload.data[asym_crypto], + cert); key_put(key); } return ret; @@ -352,9 +353,9 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* We're pinning the module by being linked against it */ __module_get(public_key_subtype.owner); - prep->type_data[0] = &public_key_subtype; - prep->type_data[1] = kids; - prep->payload[0] = cert->pub; + prep->payload.data[asym_subtype] = &public_key_subtype; + prep->payload.data[asym_key_ids] = kids; + prep->payload.data[asym_crypto] = cert->pub; prep->description = desc; prep->quotalen = 100; diff --git a/fs/cifs/cifs_spnego.c b/fs/cifs/cifs_spnego.c index f4cf200..6908080 100644 --- a/fs/cifs/cifs_spnego.c +++ b/fs/cifs/cifs_spnego.c @@ -42,7 +42,7 @@ cifs_spnego_key_instantiate(struct key *key, struct key_preparsed_payload *prep) goto error; /* attach the data */ - key->payload.data = payload; + key->payload.data[0] = payload; ret = 0; error: @@ -52,7 +52,7 @@ error: static void cifs_spnego_key_destroy(struct key *key) { - kfree(key->payload.data); + kfree(key->payload.data[0]); } @@ -167,7 +167,7 @@ cifs_get_spnego_key(struct cifs_ses *sesInfo) #ifdef CONFIG_CIFS_DEBUG2 if (cifsFYI && !IS_ERR(spnego_key)) { - struct cifs_spnego_msg *msg = spnego_key->payload.data; + struct cifs_spnego_msg *msg = spnego_key->payload.data[0]; cifs_dump_mem("SPNEGO reply blob:", msg->data, min(1024U, msg->secblob_len + msg->sesskey_len)); } diff --git a/fs/cifs/cifsacl.c b/fs/cifs/cifsacl.c index 1ea780b..3f93125 100644 --- a/fs/cifs/cifsacl.c +++ b/fs/cifs/cifsacl.c @@ -58,16 +58,15 @@ cifs_idmap_key_instantiate(struct key *key, struct key_preparsed_payload *prep) * dereference payload.data! */ if (prep->datalen <= sizeof(key->payload)) { - key->payload.value = 0; - memcpy(&key->payload.value, prep->data, prep->datalen); - key->datalen = prep->datalen; - return 0; + key->payload.data[0] = NULL; + memcpy(&key->payload, prep->data, prep->datalen); + } else { + payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); + if (!payload) + return -ENOMEM; + key->payload.data[0] = payload; } - payload = kmemdup(prep->data, prep->datalen, GFP_KERNEL); - if (!payload) - return -ENOMEM; - key->payload.data = payload; key->datalen = prep->datalen; return 0; } @@ -76,7 +75,7 @@ static inline void cifs_idmap_key_destroy(struct key *key) { if (key->datalen > sizeof(key->payload)) - kfree(key->payload.data); + kfree(key->payload.data[0]); } static struct key_type cifs_idmap_key_type = { @@ -233,8 +232,8 @@ id_to_sid(unsigned int cid, uint sidtype, struct cifs_sid *ssid) * it could be. */ ksid = sidkey->datalen <= sizeof(sidkey->payload) ? - (struct cifs_sid *)&sidkey->payload.value : - (struct cifs_sid *)sidkey->payload.data; + (struct cifs_sid *)&sidkey->payload : + (struct cifs_sid *)sidkey->payload.data[0]; ksid_size = CIFS_SID_BASE_SIZE + (ksid->num_subauth * sizeof(__le32)); if (ksid_size > sidkey->datalen) { @@ -307,14 +306,14 @@ sid_to_id(struct cifs_sb_info *cifs_sb, struct cifs_sid *psid, if (sidtype == SIDOWNER) { kuid_t uid; uid_t id; - memcpy(&id, &sidkey->payload.value, sizeof(uid_t)); + memcpy(&id, &sidkey->payload.data[0], sizeof(uid_t)); uid = make_kuid(&init_user_ns, id); if (uid_valid(uid)) fuid = uid; } else { kgid_t gid; gid_t id; - memcpy(&id, &sidkey->payload.value, sizeof(gid_t)); + memcpy(&id, &sidkey->payload.data[0], sizeof(gid_t)); gid = make_kgid(&init_user_ns, id); if (gid_valid(gid)) fgid = gid; diff --git a/fs/cifs/connect.c b/fs/cifs/connect.c index 773f4dc..3f22285 100644 --- a/fs/cifs/connect.c +++ b/fs/cifs/connect.c @@ -2325,13 +2325,14 @@ static int cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) { int rc = 0; - char *desc, *delim, *payload; + const char *delim, *payload; + char *desc; ssize_t len; struct key *key; struct TCP_Server_Info *server = ses->server; struct sockaddr_in *sa; struct sockaddr_in6 *sa6; - struct user_key_payload *upayload; + const struct user_key_payload *upayload; desc = kmalloc(CIFSCREDS_DESC_SIZE, GFP_KERNEL); if (!desc) @@ -2374,14 +2375,14 @@ cifs_set_cifscreds(struct smb_vol *vol, struct cifs_ses *ses) } down_read(&key->sem); - upayload = key->payload.data; + upayload = user_key_payload(key); if (IS_ERR_OR_NULL(upayload)) { rc = upayload ? PTR_ERR(upayload) : -EINVAL; goto out_key_put; } /* find first : in payload */ - payload = (char *)upayload->data; + payload = upayload->data; delim = strnchr(payload, upayload->datalen, ':'); cifs_dbg(FYI, "payload=%s\n", payload); if (!delim) { diff --git a/fs/cifs/sess.c b/fs/cifs/sess.c index bce6fdc..59727e3 100644 --- a/fs/cifs/sess.c +++ b/fs/cifs/sess.c @@ -988,7 +988,7 @@ sess_auth_kerberos(struct sess_data *sess_data) goto out; } - msg = spnego_key->payload.data; + msg = spnego_key->payload.data[0]; /* * check version field to make sure that cifs.upcall is * sending us a response in an expected form diff --git a/fs/cifs/smb2pdu.c b/fs/cifs/smb2pdu.c index ce83e2e..52d14c9 100644 --- a/fs/cifs/smb2pdu.c +++ b/fs/cifs/smb2pdu.c @@ -660,7 +660,7 @@ ssetup_ntlmssp_authenticate: goto ssetup_exit; } - msg = spnego_key->payload.data; + msg = spnego_key->payload.data[0]; /* * check version field to make sure that cifs.upcall is * sending us a response in an expected form diff --git a/fs/ecryptfs/ecryptfs_kernel.h b/fs/ecryptfs/ecryptfs_kernel.h index 5ba029e..7b39260 100644 --- a/fs/ecryptfs/ecryptfs_kernel.h +++ b/fs/ecryptfs/ecryptfs_kernel.h @@ -86,7 +86,7 @@ ecryptfs_get_encrypted_key_payload_data(struct key *key) { if (key->type == &key_type_encrypted) return (struct ecryptfs_auth_tok *) - (&((struct encrypted_key_payload *)key->payload.data)->payload_data); + (&((struct encrypted_key_payload *)key->payload.data[0])->payload_data); else return NULL; } @@ -117,8 +117,7 @@ ecryptfs_get_key_payload_data(struct key *key) auth_tok = ecryptfs_get_encrypted_key_payload_data(key); if (!auth_tok) - return (struct ecryptfs_auth_tok *) - (((struct user_key_payload *)key->payload.data)->data); + return (struct ecryptfs_auth_tok *)user_key_payload(key)->data; else return auth_tok; } diff --git a/fs/ext4/crypto_key.c b/fs/ext4/crypto_key.c index 1d510c1..5c52c79 100644 --- a/fs/ext4/crypto_key.c +++ b/fs/ext4/crypto_key.c @@ -121,7 +121,7 @@ int _ext4_get_encryption_info(struct inode *inode) struct key *keyring_key = NULL; struct ext4_encryption_key *master_key; struct ext4_encryption_context ctx; - struct user_key_payload *ukp; + const struct user_key_payload *ukp; struct ext4_sb_info *sbi = EXT4_SB(inode->i_sb); struct crypto_ablkcipher *ctfm; const char *cipher_str; @@ -209,7 +209,7 @@ retry: } crypt_info->ci_keyring_key = keyring_key; BUG_ON(keyring_key->type != &key_type_logon); - ukp = ((struct user_key_payload *)keyring_key->payload.data); + ukp = user_key_payload(keyring_key); if (ukp->datalen != sizeof(struct ext4_encryption_key)) { res = -EINVAL; goto out; diff --git a/fs/f2fs/crypto_key.c b/fs/f2fs/crypto_key.c index 9f77de2..5de2d86 100644 --- a/fs/f2fs/crypto_key.c +++ b/fs/f2fs/crypto_key.c @@ -122,7 +122,7 @@ int _f2fs_get_encryption_info(struct inode *inode) struct key *keyring_key = NULL; struct f2fs_encryption_key *master_key; struct f2fs_encryption_context ctx; - struct user_key_payload *ukp; + const struct user_key_payload *ukp; struct crypto_ablkcipher *ctfm; const char *cipher_str; char raw_key[F2FS_MAX_KEY_SIZE]; @@ -199,7 +199,7 @@ retry: } crypt_info->ci_keyring_key = keyring_key; BUG_ON(keyring_key->type != &key_type_logon); - ukp = ((struct user_key_payload *)keyring_key->payload.data); + ukp = user_key_payload(keyring_key); if (ukp->datalen != sizeof(struct f2fs_encryption_key)) { res = -EINVAL; goto out; diff --git a/fs/fscache/object-list.c b/fs/fscache/object-list.c index 51dde81..6b028b7 100644 --- a/fs/fscache/object-list.c +++ b/fs/fscache/object-list.c @@ -316,7 +316,7 @@ static const struct seq_operations fscache_objlist_ops = { static void fscache_objlist_config(struct fscache_objlist_data *data) { #ifdef CONFIG_KEYS - struct user_key_payload *confkey; + const struct user_key_payload *confkey; unsigned long config; struct key *key; const char *buf; @@ -329,7 +329,7 @@ static void fscache_objlist_config(struct fscache_objlist_data *data) config = 0; rcu_read_lock(); - confkey = key->payload.data; + confkey = user_key_payload(key); buf = confkey->data; for (len = confkey->datalen - 1; len >= 0; len--) { diff --git a/fs/nfs/nfs4idmap.c b/fs/nfs/nfs4idmap.c index 2e49022..5ba22c6 100644 --- a/fs/nfs/nfs4idmap.c +++ b/fs/nfs/nfs4idmap.c @@ -297,7 +297,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, { const struct cred *saved_cred; struct key *rkey; - struct user_key_payload *payload; + const struct user_key_payload *payload; ssize_t ret; saved_cred = override_creds(id_resolver_cache); @@ -316,7 +316,7 @@ static ssize_t nfs_idmap_get_key(const char *name, size_t namelen, if (ret < 0) goto out_up; - payload = rcu_dereference(rkey->payload.rcudata); + payload = user_key_payload(rkey); if (IS_ERR_OR_NULL(payload)) { ret = PTR_ERR(payload); goto out_up; diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index 067c242..cc2516d 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -15,7 +15,6 @@ #define _LINUX_PUBLIC_KEY_H #include -#include #include enum pkey_algo { diff --git a/include/keys/asymmetric-subtype.h b/include/keys/asymmetric-subtype.h index 4b840e8..4915d40 100644 --- a/include/keys/asymmetric-subtype.h +++ b/include/keys/asymmetric-subtype.h @@ -49,7 +49,7 @@ struct asymmetric_key_subtype { static inline struct asymmetric_key_subtype *asymmetric_key_subtype(const struct key *key) { - return key->type_data.p[0]; + return key->payload.data[asym_subtype]; } #endif /* _KEYS_ASYMMETRIC_SUBTYPE_H */ diff --git a/include/keys/asymmetric-type.h b/include/keys/asymmetric-type.h index c0754ab..59c1df9 100644 --- a/include/keys/asymmetric-type.h +++ b/include/keys/asymmetric-type.h @@ -19,6 +19,16 @@ extern struct key_type key_type_asymmetric; /* + * The key payload is four words. The asymmetric-type key uses them as + * follows: + */ +enum asymmetric_payload_bits { + asym_crypto, + asym_subtype, + asym_key_ids, +}; + +/* * Identifiers for an asymmetric key ID. We have three ways of looking up a * key derived from an X.509 certificate: * @@ -58,6 +68,11 @@ extern struct asymmetric_key_id *asymmetric_key_generate_id(const void *val_1, size_t len_1, const void *val_2, size_t len_2); +static inline +const struct asymmetric_key_ids *asymmetric_key_ids(const struct key *key) +{ + return key->payload.data[asym_key_ids]; +} /* * The payload is at the discretion of the subtype. diff --git a/include/keys/user-type.h b/include/keys/user-type.h index cebefb0..c56fef4 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -15,6 +15,8 @@ #include #include +#ifdef CONFIG_KEYS + /*****************************************************************************/ /* * the payload for a key of type "user" or "logon" @@ -46,5 +48,11 @@ extern void user_describe(const struct key *user, struct seq_file *m); extern long user_read(const struct key *key, char __user *buffer, size_t buflen); +static inline const struct user_key_payload *user_key_payload(const struct key *key) +{ + return (struct user_key_payload *)rcu_dereference_key(key); +} + +#endif /* CONFIG_KEYS */ #endif /* _KEYS_USER_TYPE_H */ diff --git a/include/linux/key-type.h b/include/linux/key-type.h index ff9f1d3..7463355 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -40,8 +40,7 @@ struct key_construction { */ struct key_preparsed_payload { char *description; /* Proposed key description (or NULL) */ - void *type_data[2]; /* Private key-type data */ - void *payload[2]; /* Proposed payload */ + union key_payload payload; /* Proposed payload */ const void *data; /* Raw data */ size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ diff --git a/include/linux/key.h b/include/linux/key.h index e1d4715..66f7052 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -89,6 +89,11 @@ struct keyring_index_key { size_t desc_len; }; +union key_payload { + void __rcu *rcu_data0; + void *data[4]; +}; + /*****************************************************************************/ /* * key reference with possession attribute handling @@ -186,28 +191,18 @@ struct key { }; }; - /* type specific data - * - this is used by the keyring type to index the name - */ - union { - struct list_head link; - unsigned long x[2]; - void *p[2]; - int reject_error; - } type_data; - /* key data * - this is used to hold the data actually used in cryptography or * whatever */ union { - union { - unsigned long value; - void __rcu *rcudata; - void *data; - void *data2[2]; - } payload; - struct assoc_array keys; + union key_payload payload; + struct { + /* Keyring bits */ + struct list_head name_link; + struct assoc_array keys; + }; + int reject_error; }; }; @@ -336,12 +331,12 @@ static inline bool key_is_instantiated(const struct key *key) } #define rcu_dereference_key(KEY) \ - (rcu_dereference_protected((KEY)->payload.rcudata, \ + (rcu_dereference_protected((KEY)->payload.rcu_data0, \ rwsem_is_locked(&((struct key *)(KEY))->sem))) #define rcu_assign_keypointer(KEY, PAYLOAD) \ do { \ - rcu_assign_pointer((KEY)->payload.rcudata, (PAYLOAD)); \ + rcu_assign_pointer((KEY)->payload.rcu_data0, (PAYLOAD)); \ } while (0) #ifdef CONFIG_SYSCTL diff --git a/kernel/module_signing.c b/kernel/module_signing.c index bd62f5c..6528a79 100644 --- a/kernel/module_signing.c +++ b/kernel/module_signing.c @@ -10,6 +10,7 @@ */ #include +#include #include #include #include "module-internal.h" diff --git a/lib/digsig.c b/lib/digsig.c index ae05ea39..07be6c1 100644 --- a/lib/digsig.c +++ b/lib/digsig.c @@ -79,12 +79,13 @@ static int digsig_verify_rsa(struct key *key, unsigned char *out1 = NULL; const char *m; MPI in = NULL, res = NULL, pkey[2]; - uint8_t *p, *datap, *endp; - struct user_key_payload *ukp; + uint8_t *p, *datap; + const uint8_t *endp; + const struct user_key_payload *ukp; struct pubkey_hdr *pkh; down_read(&key->sem); - ukp = key->payload.data; + ukp = user_key_payload(key); if (ukp->datalen < sizeof(*pkh)) goto err1; diff --git a/net/ceph/ceph_common.c b/net/ceph/ceph_common.c index 54a00d6..78f098a 100644 --- a/net/ceph/ceph_common.c +++ b/net/ceph/ceph_common.c @@ -318,7 +318,7 @@ static int get_secret(struct ceph_crypto_key *dst, const char *name) { goto out; } - ckey = ukey->payload.data; + ckey = ukey->payload.data[0]; err = ceph_crypto_key_clone(dst, ckey); if (err) goto out_key; diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index 4440edc..42e8649 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -537,7 +537,7 @@ static int ceph_key_preparse(struct key_preparsed_payload *prep) if (ret < 0) goto err_ckey; - prep->payload[0] = ckey; + prep->payload.data[0] = ckey; prep->quotalen = datalen; return 0; @@ -549,14 +549,14 @@ err: static void ceph_key_free_preparse(struct key_preparsed_payload *prep) { - struct ceph_crypto_key *ckey = prep->payload[0]; + struct ceph_crypto_key *ckey = prep->payload.data[0]; ceph_crypto_key_destroy(ckey); kfree(ckey); } static void ceph_key_destroy(struct key *key) { - struct ceph_crypto_key *ckey = key->payload.data; + struct ceph_crypto_key *ckey = key->payload.data[0]; ceph_crypto_key_destroy(ckey); kfree(ckey); diff --git a/net/dns_resolver/dns_key.c b/net/dns_resolver/dns_key.c index 31cd4fd..c79b85e 100644 --- a/net/dns_resolver/dns_key.c +++ b/net/dns_resolver/dns_key.c @@ -122,7 +122,7 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) goto bad_option_value; kdebug("dns error no. = %lu", derrno); - prep->type_data[0] = ERR_PTR(-derrno); + prep->payload.data[dns_key_error] = ERR_PTR(-derrno); continue; } @@ -137,8 +137,8 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) /* don't cache the result if we're caching an error saying there's no * result */ - if (prep->type_data[0]) { - kleave(" = 0 [h_error %ld]", PTR_ERR(prep->type_data[0])); + if (prep->payload.data[dns_key_error]) { + kleave(" = 0 [h_error %ld]", PTR_ERR(prep->payload.data[dns_key_error])); return 0; } @@ -155,7 +155,7 @@ dns_resolver_preparse(struct key_preparsed_payload *prep) memcpy(upayload->data, data, result_len); upayload->data[result_len] = '\0'; - prep->payload[0] = upayload; + prep->payload.data[dns_key_data] = upayload; kleave(" = 0"); return 0; } @@ -167,7 +167,7 @@ static void dns_resolver_free_preparse(struct key_preparsed_payload *prep) { pr_devel("==>%s()\n", __func__); - kfree(prep->payload[0]); + kfree(prep->payload.data[dns_key_data]); } /* @@ -223,10 +223,10 @@ static int dns_resolver_match_preparse(struct key_match_data *match_data) */ static void dns_resolver_describe(const struct key *key, struct seq_file *m) { - int err = key->type_data.x[0]; - seq_puts(m, key->description); if (key_is_instantiated(key)) { + int err = PTR_ERR(key->payload.data[dns_key_error]); + if (err) seq_printf(m, ": %d", err); else @@ -241,8 +241,10 @@ static void dns_resolver_describe(const struct key *key, struct seq_file *m) static long dns_resolver_read(const struct key *key, char __user *buffer, size_t buflen) { - if (key->type_data.x[0]) - return key->type_data.x[0]; + int err = PTR_ERR(key->payload.data[dns_key_error]); + + if (err) + return err; return user_read(key, buffer, buflen); } diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index 39d2c39..4677b6f 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -70,7 +70,7 @@ int dns_query(const char *type, const char *name, size_t namelen, const char *options, char **_result, time_t *_expiry) { struct key *rkey; - struct user_key_payload *upayload; + const struct user_key_payload *upayload; const struct cred *saved_cred; size_t typelen, desclen; char *desc, *cp; @@ -137,12 +137,11 @@ int dns_query(const char *type, const char *name, size_t namelen, goto put; /* If the DNS server gave an error, return that to the caller */ - ret = rkey->type_data.x[0]; + ret = PTR_ERR(rkey->payload.data[dns_key_error]); if (ret) goto put; - upayload = rcu_dereference_protected(rkey->payload.data, - lockdep_is_held(&rkey->sem)); + upayload = user_key_payload(rkey); len = upayload->datalen; ret = -ENOMEM; diff --git a/net/dns_resolver/internal.h b/net/dns_resolver/internal.h index 7af1ed3..0c570d4 100644 --- a/net/dns_resolver/internal.h +++ b/net/dns_resolver/internal.h @@ -23,6 +23,14 @@ #include /* + * Layout of key payload words. + */ +enum { + dns_key_data, + dns_key_error, +}; + +/* * dns_key.c */ extern const struct cred *dns_resolver_cache; diff --git a/net/rxrpc/af_rxrpc.c b/net/rxrpc/af_rxrpc.c index 25d60ed..1f8a144 100644 --- a/net/rxrpc/af_rxrpc.c +++ b/net/rxrpc/af_rxrpc.c @@ -305,7 +305,7 @@ struct rxrpc_call *rxrpc_kernel_begin_call(struct socket *sock, if (!key) key = rx->key; - if (key && !key->payload.data) + if (key && !key->payload.data[0]) key = NULL; /* a no-security key */ bundle = rxrpc_get_bundle(rx, trans, key, service_id, gfp); diff --git a/net/rxrpc/ar-key.c b/net/rxrpc/ar-key.c index db0f39f..da3cc09 100644 --- a/net/rxrpc/ar-key.c +++ b/net/rxrpc/ar-key.c @@ -148,10 +148,10 @@ static int rxrpc_preparse_xdr_rxkad(struct key_preparsed_payload *prep, token->kad->ticket[6], token->kad->ticket[7]); /* count the number of tokens attached */ - prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1); + prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); /* attach the data */ - for (pptoken = (struct rxrpc_key_token **)&prep->payload[0]; + for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; *pptoken; pptoken = &(*pptoken)->next) continue; @@ -522,7 +522,7 @@ static int rxrpc_preparse_xdr_rxk5(struct key_preparsed_payload *prep, goto inval; /* attach the payload */ - for (pptoken = (struct rxrpc_key_token **)&prep->payload[0]; + for (pptoken = (struct rxrpc_key_token **)&prep->payload.data[0]; *pptoken; pptoken = &(*pptoken)->next) continue; @@ -764,10 +764,10 @@ static int rxrpc_preparse(struct key_preparsed_payload *prep) memcpy(&token->kad->ticket, v1->ticket, v1->ticket_length); /* count the number of tokens attached */ - prep->type_data[0] = (void *)((unsigned long)prep->type_data[0] + 1); + prep->payload.data[1] = (void *)((unsigned long)prep->payload.data[1] + 1); /* attach the data */ - pp = (struct rxrpc_key_token **)&prep->payload[0]; + pp = (struct rxrpc_key_token **)&prep->payload.data[0]; while (*pp) pp = &(*pp)->next; *pp = token; @@ -814,7 +814,7 @@ static void rxrpc_free_token_list(struct rxrpc_key_token *token) */ static void rxrpc_free_preparse(struct key_preparsed_payload *prep) { - rxrpc_free_token_list(prep->payload[0]); + rxrpc_free_token_list(prep->payload.data[0]); } /* @@ -831,7 +831,7 @@ static int rxrpc_preparse_s(struct key_preparsed_payload *prep) if (prep->datalen != 8) return -EINVAL; - memcpy(&prep->type_data, prep->data, 8); + memcpy(&prep->payload.data[2], prep->data, 8); ci = crypto_alloc_blkcipher("pcbc(des)", 0, CRYPTO_ALG_ASYNC); if (IS_ERR(ci)) { @@ -842,7 +842,7 @@ static int rxrpc_preparse_s(struct key_preparsed_payload *prep) if (crypto_blkcipher_setkey(ci, prep->data, 8) < 0) BUG(); - prep->payload[0] = ci; + prep->payload.data[0] = ci; _leave(" = 0"); return 0; } @@ -852,8 +852,8 @@ static int rxrpc_preparse_s(struct key_preparsed_payload *prep) */ static void rxrpc_free_preparse_s(struct key_preparsed_payload *prep) { - if (prep->payload[0]) - crypto_free_blkcipher(prep->payload[0]); + if (prep->payload.data[0]) + crypto_free_blkcipher(prep->payload.data[0]); } /* @@ -861,7 +861,7 @@ static void rxrpc_free_preparse_s(struct key_preparsed_payload *prep) */ static void rxrpc_destroy(struct key *key) { - rxrpc_free_token_list(key->payload.data); + rxrpc_free_token_list(key->payload.data[0]); } /* @@ -869,9 +869,9 @@ static void rxrpc_destroy(struct key *key) */ static void rxrpc_destroy_s(struct key *key) { - if (key->payload.data) { - crypto_free_blkcipher(key->payload.data); - key->payload.data = NULL; + if (key->payload.data[0]) { + crypto_free_blkcipher(key->payload.data[0]); + key->payload.data[0] = NULL; } } @@ -1070,7 +1070,7 @@ static long rxrpc_read(const struct key *key, size += 1 * 4; /* token count */ ntoks = 0; - for (token = key->payload.data; token; token = token->next) { + for (token = key->payload.data[0]; token; token = token->next) { toksize = 4; /* sec index */ switch (token->security_index) { @@ -1163,7 +1163,7 @@ static long rxrpc_read(const struct key *key, ENCODE(ntoks); tok = 0; - for (token = key->payload.data; token; token = token->next) { + for (token = key->payload.data[0]; token; token = token->next) { toksize = toksizes[tok++]; ENCODE(toksize); oldxdr = xdr; diff --git a/net/rxrpc/ar-output.c b/net/rxrpc/ar-output.c index c004280..a40d3af 100644 --- a/net/rxrpc/ar-output.c +++ b/net/rxrpc/ar-output.c @@ -158,7 +158,7 @@ int rxrpc_client_sendmsg(struct rxrpc_sock *rx, struct rxrpc_transport *trans, service_id = htons(srx->srx_service); } key = rx->key; - if (key && !rx->key->payload.data) + if (key && !rx->key->payload.data[0]) key = NULL; bundle = rxrpc_get_bundle(rx, trans, key, service_id, GFP_KERNEL); diff --git a/net/rxrpc/ar-security.c b/net/rxrpc/ar-security.c index 49b3cc31..8334474 100644 --- a/net/rxrpc/ar-security.c +++ b/net/rxrpc/ar-security.c @@ -137,9 +137,9 @@ int rxrpc_init_client_conn_security(struct rxrpc_connection *conn) if (ret < 0) return ret; - if (!key->payload.data) + token = key->payload.data[0]; + if (!token) return -EKEYREJECTED; - token = key->payload.data; sec = rxrpc_security_lookup(token->security_index); if (!sec) diff --git a/net/rxrpc/rxkad.c b/net/rxrpc/rxkad.c index f226709..d7a9ab5 100644 --- a/net/rxrpc/rxkad.c +++ b/net/rxrpc/rxkad.c @@ -67,7 +67,7 @@ static int rxkad_init_connection_security(struct rxrpc_connection *conn) _enter("{%d},{%x}", conn->debug_id, key_serial(conn->key)); - token = conn->key->payload.data; + token = conn->key->payload.data[0]; conn->security_ix = token->security_index; ci = crypto_alloc_blkcipher("pcbc(fcrypt)", 0, CRYPTO_ALG_ASYNC); @@ -125,7 +125,7 @@ static void rxkad_prime_packet_security(struct rxrpc_connection *conn) if (!conn->key) return; - token = conn->key->payload.data; + token = conn->key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); desc.tfm = conn->cipher; @@ -221,7 +221,7 @@ static int rxkad_secure_packet_encrypt(const struct rxrpc_call *call, rxkhdr.checksum = 0; /* encrypt from the session key */ - token = call->conn->key->payload.data; + token = call->conn->key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); desc.tfm = call->conn->cipher; desc.info = iv.x; @@ -433,7 +433,7 @@ static int rxkad_verify_packet_encrypt(const struct rxrpc_call *call, skb_to_sgvec(skb, sg, 0, skb->len); /* decrypt from the session key */ - token = call->conn->key->payload.data; + token = call->conn->key->payload.data[0]; memcpy(&iv, token->kad->session_key, sizeof(iv)); desc.tfm = call->conn->cipher; desc.info = iv.x; @@ -780,7 +780,7 @@ static int rxkad_respond_to_challenge(struct rxrpc_connection *conn, if (conn->security_level < min_level) goto protocol_error; - token = conn->key->payload.data; + token = conn->key->payload.data[0]; /* build the response packet */ memset(&resp, 0, sizeof(resp)); @@ -848,12 +848,12 @@ static int rxkad_decrypt_ticket(struct rxrpc_connection *conn, } } - ASSERT(conn->server_key->payload.data != NULL); + ASSERT(conn->server_key->payload.data[0] != NULL); ASSERTCMP((unsigned long) ticket & 7UL, ==, 0); - memcpy(&iv, &conn->server_key->type_data, sizeof(iv)); + memcpy(&iv, &conn->server_key->payload.data[2], sizeof(iv)); - desc.tfm = conn->server_key->payload.data; + desc.tfm = conn->server_key->payload.data[0]; desc.info = iv.x; desc.flags = 0; diff --git a/security/integrity/evm/evm_crypto.c b/security/integrity/evm/evm_crypto.c index 159ef3e..461f8d8 100644 --- a/security/integrity/evm/evm_crypto.c +++ b/security/integrity/evm/evm_crypto.c @@ -247,7 +247,7 @@ int evm_init_key(void) return -ENOENT; down_read(&evm_key->sem); - ekp = evm_key->payload.data; + ekp = evm_key->payload.data[0]; if (ekp->decrypted_datalen > MAX_KEY_SIZE) { rc = -EINVAL; goto out; diff --git a/security/keys/big_key.c b/security/keys/big_key.c index b6adb94..907c152 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -21,6 +21,16 @@ MODULE_LICENSE("GPL"); /* + * Layout of key payload words. + */ +enum { + big_key_data, + big_key_path, + big_key_path_2nd_part, + big_key_len, +}; + +/* * If the data is under this limit, there's no point creating a shm file to * hold it as the permanently resident metadata for the shmem fs will be at * least as large as the data. @@ -47,7 +57,7 @@ struct key_type key_type_big_key = { */ int big_key_preparse(struct key_preparsed_payload *prep) { - struct path *path = (struct path *)&prep->payload; + struct path *path = (struct path *)&prep->payload.data[big_key_path]; struct file *file; ssize_t written; size_t datalen = prep->datalen; @@ -60,7 +70,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) /* Set an arbitrary quota */ prep->quotalen = 16; - prep->type_data[1] = (void *)(unsigned long)datalen; + prep->payload.data[big_key_len] = (void *)(unsigned long)datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { /* Create a shmem file to store the data in. This will permit the data @@ -94,7 +104,8 @@ int big_key_preparse(struct key_preparsed_payload *prep) if (!data) return -ENOMEM; - prep->payload[0] = memcpy(data, prep->data, prep->datalen); + prep->payload.data[big_key_data] = data; + memcpy(data, prep->data, prep->datalen); } return 0; @@ -110,10 +121,10 @@ error: void big_key_free_preparse(struct key_preparsed_payload *prep) { if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { - struct path *path = (struct path *)&prep->payload; + struct path *path = (struct path *)&prep->payload.data[big_key_path]; path_put(path); } else { - kfree(prep->payload[0]); + kfree(prep->payload.data[big_key_data]); } } @@ -123,11 +134,12 @@ void big_key_free_preparse(struct key_preparsed_payload *prep) */ void big_key_revoke(struct key *key) { - struct path *path = (struct path *)&key->payload.data2; + struct path *path = (struct path *)&key->payload.data[big_key_path]; /* clear the quota */ key_payload_reserve(key, 0); - if (key_is_instantiated(key) && key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) + if (key_is_instantiated(key) && + (size_t)key->payload.data[big_key_len] > BIG_KEY_FILE_THRESHOLD) vfs_truncate(path, 0); } @@ -136,14 +148,16 @@ void big_key_revoke(struct key *key) */ void big_key_destroy(struct key *key) { - if (key->type_data.x[1] > BIG_KEY_FILE_THRESHOLD) { - struct path *path = (struct path *)&key->payload.data2; + size_t datalen = (size_t)key->payload.data[big_key_len]; + + if (datalen) { + struct path *path = (struct path *)&key->payload.data[big_key_path]; path_put(path); path->mnt = NULL; path->dentry = NULL; } else { - kfree(key->payload.data); - key->payload.data = NULL; + kfree(key->payload.data[big_key_data]); + key->payload.data[big_key_data] = NULL; } } @@ -152,12 +166,12 @@ void big_key_destroy(struct key *key) */ void big_key_describe(const struct key *key, struct seq_file *m) { - unsigned long datalen = key->type_data.x[1]; + size_t datalen = (size_t)key->payload.data[big_key_len]; seq_puts(m, key->description); if (key_is_instantiated(key)) - seq_printf(m, ": %lu [%s]", + seq_printf(m, ": %zu [%s]", datalen, datalen > BIG_KEY_FILE_THRESHOLD ? "file" : "buff"); } @@ -168,14 +182,14 @@ void big_key_describe(const struct key *key, struct seq_file *m) */ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) { - unsigned long datalen = key->type_data.x[1]; + size_t datalen = (size_t)key->payload.data[big_key_len]; long ret; if (!buffer || buflen < datalen) return datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { - struct path *path = (struct path *)&key->payload.data2; + struct path *path = (struct path *)&key->payload.data[big_key_path]; struct file *file; loff_t pos; @@ -190,7 +204,8 @@ long big_key_read(const struct key *key, char __user *buffer, size_t buflen) ret = -EIO; } else { ret = datalen; - if (copy_to_user(buffer, key->payload.data, datalen) != 0) + if (copy_to_user(buffer, key->payload.data[big_key_data], + datalen) != 0) ret = -EFAULT; } diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 7bed4ad..927db9f 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -303,10 +303,10 @@ out: * * Use a user provided key to encrypt/decrypt an encrypted-key. */ -static struct key *request_user_key(const char *master_desc, u8 **master_key, +static struct key *request_user_key(const char *master_desc, const u8 **master_key, size_t *master_keylen) { - struct user_key_payload *upayload; + const struct user_key_payload *upayload; struct key *ukey; ukey = request_key(&key_type_user, master_desc, NULL); @@ -314,7 +314,7 @@ static struct key *request_user_key(const char *master_desc, u8 **master_key, goto error; down_read(&ukey->sem); - upayload = ukey->payload.data; + upayload = user_key_payload(ukey); *master_key = upayload->data; *master_keylen = upayload->datalen; error: @@ -426,7 +426,7 @@ static int init_blkcipher_desc(struct blkcipher_desc *desc, const u8 *key, } static struct key *request_master_key(struct encrypted_key_payload *epayload, - u8 **master_key, size_t *master_keylen) + const u8 **master_key, size_t *master_keylen) { struct key *mkey = NULL; @@ -653,7 +653,7 @@ static int encrypted_key_decrypt(struct encrypted_key_payload *epayload, { struct key *mkey; u8 derived_key[HASH_SIZE]; - u8 *master_key; + const u8 *master_key; u8 *hmac; const char *hex_encoded_data; unsigned int encrypted_datalen; @@ -837,7 +837,7 @@ static void encrypted_rcu_free(struct rcu_head *rcu) */ static int encrypted_update(struct key *key, struct key_preparsed_payload *prep) { - struct encrypted_key_payload *epayload = key->payload.data; + struct encrypted_key_payload *epayload = key->payload.data[0]; struct encrypted_key_payload *new_epayload; char *buf; char *new_master_desc = NULL; @@ -896,7 +896,7 @@ static long encrypted_read(const struct key *key, char __user *buffer, { struct encrypted_key_payload *epayload; struct key *mkey; - u8 *master_key; + const u8 *master_key; size_t master_keylen; char derived_key[HASH_SIZE]; char *ascii_buf; @@ -957,13 +957,13 @@ out: */ static void encrypted_destroy(struct key *key) { - struct encrypted_key_payload *epayload = key->payload.data; + struct encrypted_key_payload *epayload = key->payload.data[0]; if (!epayload) return; memset(epayload->decrypted_data, 0, epayload->decrypted_datalen); - kfree(key->payload.data); + kfree(key->payload.data[0]); } struct key_type key_type_encrypted = { diff --git a/security/keys/encrypted-keys/encrypted.h b/security/keys/encrypted-keys/encrypted.h index 8136a2d..47802c0 100644 --- a/security/keys/encrypted-keys/encrypted.h +++ b/security/keys/encrypted-keys/encrypted.h @@ -5,10 +5,10 @@ #if defined(CONFIG_TRUSTED_KEYS) || \ (defined(CONFIG_TRUSTED_KEYS_MODULE) && defined(CONFIG_ENCRYPTED_KEYS_MODULE)) extern struct key *request_trusted_key(const char *trusted_desc, - u8 **master_key, size_t *master_keylen); + const u8 **master_key, size_t *master_keylen); #else static inline struct key *request_trusted_key(const char *trusted_desc, - u8 **master_key, + const u8 **master_key, size_t *master_keylen) { return ERR_PTR(-EOPNOTSUPP); diff --git a/security/keys/encrypted-keys/masterkey_trusted.c b/security/keys/encrypted-keys/masterkey_trusted.c index 013f7e5..b5b4812 100644 --- a/security/keys/encrypted-keys/masterkey_trusted.c +++ b/security/keys/encrypted-keys/masterkey_trusted.c @@ -29,7 +29,7 @@ * data, trusted key type data is not visible decrypted from userspace. */ struct key *request_trusted_key(const char *trusted_desc, - u8 **master_key, size_t *master_keylen) + const u8 **master_key, size_t *master_keylen) { struct trusted_key_payload *tpayload; struct key *tkey; @@ -39,7 +39,7 @@ struct key *request_trusted_key(const char *trusted_desc, goto error; down_read(&tkey->sem); - tpayload = tkey->payload.data; + tpayload = tkey->payload.data[0]; *master_key = tpayload->key; *master_keylen = tpayload->key_len; error: diff --git a/security/keys/key.c b/security/keys/key.c index c047846..ab7997d 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -554,7 +554,7 @@ int key_reject_and_link(struct key *key, if (!test_bit(KEY_FLAG_INSTANTIATED, &key->flags)) { /* mark the key as being negatively instantiated */ atomic_inc(&key->user->nikeys); - key->type_data.reject_error = -error; + key->reject_error = -error; smp_wmb(); set_bit(KEY_FLAG_NEGATIVE, &key->flags); set_bit(KEY_FLAG_INSTANTIATED, &key->flags); @@ -1046,14 +1046,14 @@ int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) ret = key_payload_reserve(key, prep->quotalen); if (ret == 0) { - key->type_data.p[0] = prep->type_data[0]; - key->type_data.p[1] = prep->type_data[1]; - rcu_assign_keypointer(key, prep->payload[0]); - key->payload.data2[1] = prep->payload[1]; - prep->type_data[0] = NULL; - prep->type_data[1] = NULL; - prep->payload[0] = NULL; - prep->payload[1] = NULL; + rcu_assign_keypointer(key, prep->payload.data[0]); + key->payload.data[1] = prep->payload.data[1]; + key->payload.data[2] = prep->payload.data[2]; + key->payload.data[3] = prep->payload.data[3]; + prep->payload.data[0] = NULL; + prep->payload.data[1] = NULL; + prep->payload.data[2] = NULL; + prep->payload.data[3] = NULL; } pr_devel("<==%s() = %d\n", __func__, ret); return ret; diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index 6110fa4..fb111ea 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -1027,7 +1027,7 @@ long keyctl_instantiate_key_common(key_serial_t id, if (!instkey) goto error; - rka = instkey->payload.data; + rka = instkey->payload.data[0]; if (rka->target_key->serial != id) goto error; @@ -1194,7 +1194,7 @@ long keyctl_reject_key(key_serial_t id, unsigned timeout, unsigned error, if (!instkey) goto error; - rka = instkey->payload.data; + rka = instkey->payload.data[0]; if (rka->target_key->serial != id) goto error; diff --git a/security/keys/keyring.c b/security/keys/keyring.c index d334370..f931ccf 100644 --- a/security/keys/keyring.c +++ b/security/keys/keyring.c @@ -118,7 +118,7 @@ static void keyring_publish_name(struct key *keyring) if (!keyring_name_hash[bucket].next) INIT_LIST_HEAD(&keyring_name_hash[bucket]); - list_add_tail(&keyring->type_data.link, + list_add_tail(&keyring->name_link, &keyring_name_hash[bucket]); write_unlock(&keyring_name_lock); @@ -387,9 +387,9 @@ static void keyring_destroy(struct key *keyring) if (keyring->description) { write_lock(&keyring_name_lock); - if (keyring->type_data.link.next != NULL && - !list_empty(&keyring->type_data.link)) - list_del(&keyring->type_data.link); + if (keyring->name_link.next != NULL && + !list_empty(&keyring->name_link)) + list_del(&keyring->name_link); write_unlock(&keyring_name_lock); } @@ -572,7 +572,7 @@ static int keyring_search_iterator(const void *object, void *iterator_data) /* we set a different error code if we pass a negative key */ if (kflags & (1 << KEY_FLAG_NEGATIVE)) { smp_rmb(); - ctx->result = ERR_PTR(key->type_data.reject_error); + ctx->result = ERR_PTR(key->reject_error); kleave(" = %d [neg]", ctx->skipped_ret); goto skipped; } @@ -990,7 +990,7 @@ struct key *find_keyring_by_name(const char *name, bool skip_perm_check) * that's readable and that hasn't been revoked */ list_for_each_entry(keyring, &keyring_name_hash[bucket], - type_data.link + name_link ) { if (!kuid_has_mapping(current_user_ns(), keyring->user->uid)) continue; diff --git a/security/keys/process_keys.c b/security/keys/process_keys.c index 43b4cdd..a3f85d2 100644 --- a/security/keys/process_keys.c +++ b/security/keys/process_keys.c @@ -457,7 +457,7 @@ key_ref_t search_process_keyrings(struct keyring_search_context *ctx) down_read(&cred->request_key_auth->sem); if (key_validate(ctx->cred->request_key_auth) == 0) { - rka = ctx->cred->request_key_auth->payload.data; + rka = ctx->cred->request_key_auth->payload.data[0]; ctx->cred = rka->cred; key_ref = search_process_keyrings(ctx); @@ -647,7 +647,7 @@ try_again: key_ref = ERR_PTR(-EKEYREVOKED); key = NULL; } else { - rka = ctx.cred->request_key_auth->payload.data; + rka = ctx.cred->request_key_auth->payload.data[0]; key = rka->dest_keyring; __key_get(key); } diff --git a/security/keys/request_key.c b/security/keys/request_key.c index 486ef6f..95d5cfc 100644 --- a/security/keys/request_key.c +++ b/security/keys/request_key.c @@ -271,7 +271,7 @@ static void construct_get_dest_keyring(struct key **_dest_keyring) if (cred->request_key_auth) { authkey = cred->request_key_auth; down_read(&authkey->sem); - rka = authkey->payload.data; + rka = authkey->payload.data[0]; if (!test_bit(KEY_FLAG_REVOKED, &authkey->flags)) dest_keyring = @@ -593,7 +593,7 @@ int wait_for_key_construction(struct key *key, bool intr) return -ERESTARTSYS; if (test_bit(KEY_FLAG_NEGATIVE, &key->flags)) { smp_rmb(); - return key->type_data.reject_error; + return key->reject_error; } return key_validate(key); } diff --git a/security/keys/request_key_auth.c b/security/keys/request_key_auth.c index 5d672f7..4f0f112 100644 --- a/security/keys/request_key_auth.c +++ b/security/keys/request_key_auth.c @@ -59,7 +59,7 @@ static void request_key_auth_free_preparse(struct key_preparsed_payload *prep) static int request_key_auth_instantiate(struct key *key, struct key_preparsed_payload *prep) { - key->payload.data = (struct request_key_auth *)prep->data; + key->payload.data[0] = (struct request_key_auth *)prep->data; return 0; } @@ -69,7 +69,7 @@ static int request_key_auth_instantiate(struct key *key, static void request_key_auth_describe(const struct key *key, struct seq_file *m) { - struct request_key_auth *rka = key->payload.data; + struct request_key_auth *rka = key->payload.data[0]; seq_puts(m, "key:"); seq_puts(m, key->description); @@ -84,7 +84,7 @@ static void request_key_auth_describe(const struct key *key, static long request_key_auth_read(const struct key *key, char __user *buffer, size_t buflen) { - struct request_key_auth *rka = key->payload.data; + struct request_key_auth *rka = key->payload.data[0]; size_t datalen; long ret; @@ -110,7 +110,7 @@ static long request_key_auth_read(const struct key *key, */ static void request_key_auth_revoke(struct key *key) { - struct request_key_auth *rka = key->payload.data; + struct request_key_auth *rka = key->payload.data[0]; kenter("{%d}", key->serial); @@ -125,7 +125,7 @@ static void request_key_auth_revoke(struct key *key) */ static void request_key_auth_destroy(struct key *key) { - struct request_key_auth *rka = key->payload.data; + struct request_key_auth *rka = key->payload.data[0]; kenter("{%d}", key->serial); @@ -179,7 +179,7 @@ struct key *request_key_auth_new(struct key *target, const void *callout_info, if (test_bit(KEY_FLAG_REVOKED, &cred->request_key_auth->flags)) goto auth_key_revoked; - irka = cred->request_key_auth->payload.data; + irka = cred->request_key_auth->payload.data[0]; rka->cred = get_cred(irka->cred); rka->pid = irka->pid; diff --git a/security/keys/trusted.c b/security/keys/trusted.c index d3633cf..903dace 100644 --- a/security/keys/trusted.c +++ b/security/keys/trusted.c @@ -1007,7 +1007,7 @@ static void trusted_rcu_free(struct rcu_head *rcu) */ static int trusted_update(struct key *key, struct key_preparsed_payload *prep) { - struct trusted_key_payload *p = key->payload.data; + struct trusted_key_payload *p = key->payload.data[0]; struct trusted_key_payload *new_p; struct trusted_key_options *new_o; size_t datalen = prep->datalen; @@ -1114,12 +1114,12 @@ static long trusted_read(const struct key *key, char __user *buffer, */ static void trusted_destroy(struct key *key) { - struct trusted_key_payload *p = key->payload.data; + struct trusted_key_payload *p = key->payload.data[0]; if (!p) return; memset(p->key, 0, p->key_len); - kfree(key->payload.data); + kfree(key->payload.data[0]); } struct key_type key_type_trusted = { diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index 36b47bb..28cb30f 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -74,7 +74,7 @@ int user_preparse(struct key_preparsed_payload *prep) /* attach the data */ prep->quotalen = datalen; - prep->payload[0] = upayload; + prep->payload.data[0] = upayload; upayload->datalen = datalen; memcpy(upayload->data, prep->data, datalen); return 0; @@ -86,7 +86,7 @@ EXPORT_SYMBOL_GPL(user_preparse); */ void user_free_preparse(struct key_preparsed_payload *prep) { - kfree(prep->payload[0]); + kfree(prep->payload.data[0]); } EXPORT_SYMBOL_GPL(user_free_preparse); @@ -120,7 +120,7 @@ int user_update(struct key *key, struct key_preparsed_payload *prep) if (ret == 0) { /* attach the new data, displacing the old */ - zap = key->payload.data; + zap = key->payload.data[0]; rcu_assign_keypointer(key, upayload); key->expiry = 0; } @@ -140,7 +140,7 @@ EXPORT_SYMBOL_GPL(user_update); */ void user_revoke(struct key *key) { - struct user_key_payload *upayload = key->payload.data; + struct user_key_payload *upayload = key->payload.data[0]; /* clear the quota */ key_payload_reserve(key, 0); @@ -158,7 +158,7 @@ EXPORT_SYMBOL(user_revoke); */ void user_destroy(struct key *key) { - struct user_key_payload *upayload = key->payload.data; + struct user_key_payload *upayload = key->payload.data[0]; kfree(upayload); } @@ -183,10 +183,10 @@ EXPORT_SYMBOL_GPL(user_describe); */ long user_read(const struct key *key, char __user *buffer, size_t buflen) { - struct user_key_payload *upayload; + const struct user_key_payload *upayload; long ret; - upayload = rcu_dereference_key(key); + upayload = user_key_payload(key); ret = upayload->datalen; /* we can return the data as is */ -- cgit v0.10.2 From 2a35d196c160e352fa56eabb7952f78f4c85f577 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Wed, 21 Oct 2015 17:44:25 -0400 Subject: selinux: change CONFIG_SECURITY_SELINUX_CHECKREQPROT_VALUE default Change the SELinux checkreqprot default value to 0 so that SELinux performs access control checking on the actual memory protections used by the kernel and not those requested by the application. Signed-off-by: Paul Moore diff --git a/security/selinux/Kconfig b/security/selinux/Kconfig index bca1b74..8691e92 100644 --- a/security/selinux/Kconfig +++ b/security/selinux/Kconfig @@ -78,7 +78,7 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE int "NSA SELinux checkreqprot default value" depends on SECURITY_SELINUX range 0 1 - default 1 + default 0 help This option sets the default value for the 'checkreqprot' flag that determines whether SELinux checks the protection requested @@ -92,7 +92,7 @@ config SECURITY_SELINUX_CHECKREQPROT_VALUE 'checkreqprot=' boot parameter. It may also be changed at runtime via /selinux/checkreqprot if authorized by policy. - If you are unsure how to answer this question, answer 1. + If you are unsure how to answer this question, answer 0. config SECURITY_SELINUX_POLICYDB_VERSION_MAX bool "NSA SELinux maximum supported policy format version" -- cgit v0.10.2 From 44d37ad3602b3823764eeb0f6c1ee3ef6c4fb936 Mon Sep 17 00:00:00 2001 From: Jeff Vander Stoep Date: Wed, 21 Oct 2015 17:44:25 -0400 Subject: selinux: do not check open perm on ftruncate call Use the ATTR_FILE attribute to distinguish between truncate() and ftruncate() system calls. The two other cases where do_truncate is called with a filp (and therefore ATTR_FILE is set) are for coredump files and for open(O_TRUNC). In both of those cases the open permission has already been checked during file open and therefore does not need to be repeated. Commit 95dbf739313f ("SELinux: check OPEN on truncate calls") fixed a major issue where domains were allowed to truncate files without the open permission. However, it introduced a new bug where a domain with the write permission can no longer ftruncate files without the open permission, even when they receive an already open file. Signed-off-by: Jeff Vander Stoep Acked-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index e4369d8..7cd71ce 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -2946,7 +2946,8 @@ static int selinux_inode_setattr(struct dentry *dentry, struct iattr *iattr) ATTR_ATIME_SET | ATTR_MTIME_SET | ATTR_TIMES_SET)) return dentry_has_perm(cred, dentry, FILE__SETATTR); - if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE)) + if (selinux_policycap_openperm && (ia_valid & ATTR_SIZE) + && !(ia_valid & ATTR_FILE)) av |= FILE__OPEN; return dentry_has_perm(cred, dentry, av); -- cgit v0.10.2 From 44be2f65d979291ffb2a47112449507ffe1f9726 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 21 Oct 2015 17:44:25 -0400 Subject: selinux: introduce security_context_str_to_sid There seems to be a little confusion as to whether the scontext_len parameter of security_context_to_sid() includes the nul-byte or not. Reading security_context_to_sid_core(), it seems that the expectation is that it does not (both the string copying and the test for scontext_len being zero hint at that). Introduce the helper security_context_str_to_sid() to do the strlen() call and fix all callers. Signed-off-by: Rasmus Villemoes Acked-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 7cd71ce..9ed1b5d 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -674,10 +674,9 @@ static int selinux_set_mnt_opts(struct super_block *sb, if (flags[i] == SBLABEL_MNT) continue; - rc = security_context_to_sid(mount_options[i], - strlen(mount_options[i]), &sid, GFP_KERNEL); + rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL); if (rc) { - printk(KERN_WARNING "SELinux: security_context_to_sid" + printk(KERN_WARNING "SELinux: security_context_str_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", mount_options[i], sb->s_id, name, rc); goto out; @@ -2617,15 +2616,12 @@ static int selinux_sb_remount(struct super_block *sb, void *data) for (i = 0; i < opts.num_mnt_opts; i++) { u32 sid; - size_t len; if (flags[i] == SBLABEL_MNT) continue; - len = strlen(mount_options[i]); - rc = security_context_to_sid(mount_options[i], len, &sid, - GFP_KERNEL); + rc = security_context_str_to_sid(mount_options[i], &sid, GFP_KERNEL); if (rc) { - printk(KERN_WARNING "SELinux: security_context_to_sid" + printk(KERN_WARNING "SELinux: security_context_str_to_sid" "(%s) failed for (dev %s, type %s) errno=%d\n", mount_options[i], sb->s_id, sb->s_type->name, rc); goto out_free_opts; diff --git a/security/selinux/include/security.h b/security/selinux/include/security.h index 6a681d2..223e9fd 100644 --- a/security/selinux/include/security.h +++ b/security/selinux/include/security.h @@ -166,6 +166,8 @@ int security_sid_to_context_force(u32 sid, char **scontext, u32 *scontext_len); int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *out_sid, gfp_t gfp); +int security_context_str_to_sid(const char *scontext, u32 *out_sid, gfp_t gfp); + int security_context_to_sid_default(const char *scontext, u32 scontext_len, u32 *out_sid, u32 def_sid, gfp_t gfp_flags); diff --git a/security/selinux/selinuxfs.c b/security/selinux/selinuxfs.c index 5bed7716..c02da25 100644 --- a/security/selinux/selinuxfs.c +++ b/security/selinux/selinuxfs.c @@ -731,13 +731,11 @@ static ssize_t sel_write_access(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, - GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, - GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; @@ -819,13 +817,11 @@ static ssize_t sel_write_create(struct file *file, char *buf, size_t size) objname = namebuf; } - length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, - GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, - GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; @@ -882,13 +878,11 @@ static ssize_t sel_write_relabel(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, - GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, - GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; @@ -940,7 +934,7 @@ static ssize_t sel_write_user(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s", con, user) != 2) goto out; - length = security_context_to_sid(con, strlen(con) + 1, &sid, GFP_KERNEL); + length = security_context_str_to_sid(con, &sid, GFP_KERNEL); if (length) goto out; @@ -1000,13 +994,11 @@ static ssize_t sel_write_member(struct file *file, char *buf, size_t size) if (sscanf(buf, "%s %s %hu", scon, tcon, &tclass) != 3) goto out; - length = security_context_to_sid(scon, strlen(scon) + 1, &ssid, - GFP_KERNEL); + length = security_context_str_to_sid(scon, &ssid, GFP_KERNEL); if (length) goto out; - length = security_context_to_sid(tcon, strlen(tcon) + 1, &tsid, - GFP_KERNEL); + length = security_context_str_to_sid(tcon, &tsid, GFP_KERNEL); if (length) goto out; diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index b7df12b..c550df0 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1476,6 +1476,11 @@ int security_context_to_sid(const char *scontext, u32 scontext_len, u32 *sid, sid, SECSID_NULL, gfp, 0); } +int security_context_str_to_sid(const char *scontext, u32 *sid, gfp_t gfp) +{ + return security_context_to_sid(scontext, strlen(scontext), sid, gfp); +} + /** * security_context_to_sid_default - Obtain a SID for a given security context, * falling back to specified default if needed. -- cgit v0.10.2 From 20ba96aeebd40f09a1d626913235941e290992c7 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 21 Oct 2015 17:44:26 -0400 Subject: selinux: remove pointless cast in selinux_inode_setsecurity() security_context_to_sid() expects a const char* argument, so there's no point in casting away the const qualifier of value. Signed-off-by: Rasmus Villemoes Acked-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 9ed1b5d..1530f66 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3163,7 +3163,7 @@ static int selinux_inode_setsecurity(struct inode *inode, const char *name, if (!value || !size) return -EACCES; - rc = security_context_to_sid((void *)value, size, &newsid, GFP_KERNEL); + rc = security_context_to_sid(value, size, &newsid, GFP_KERNEL); if (rc) return rc; -- cgit v0.10.2 From aa736c36db3e583d249e1d23a3ac9223b1c55f95 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 21 Oct 2015 17:44:26 -0400 Subject: selinux: use kmemdup in security_sid_to_context_core() Signed-off-by: Rasmus Villemoes Acked-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index c550df0..994c824 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1259,12 +1259,12 @@ static int security_sid_to_context_core(u32 sid, char **scontext, *scontext_len = strlen(initial_sid_to_string[sid]) + 1; if (!scontext) goto out; - scontextp = kmalloc(*scontext_len, GFP_ATOMIC); + scontextp = kmemdup(initial_sid_to_string[sid], + *scontext_len, GFP_ATOMIC); if (!scontextp) { rc = -ENOMEM; goto out; } - strcpy(scontextp, initial_sid_to_string[sid]); *scontext = scontextp; goto out; } -- cgit v0.10.2 From 21b76f199e25d32b0a7ed3833ca9204898262c24 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 21 Oct 2015 17:44:26 -0400 Subject: selinux: use kstrdup() in security_get_bools() This is much simpler. Signed-off-by: Rasmus Villemoes Acked-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index 994c824..aa2bdcb 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -2609,18 +2609,12 @@ int security_get_bools(int *len, char ***names, int **values) goto err; for (i = 0; i < *len; i++) { - size_t name_len; - (*values)[i] = policydb.bool_val_to_struct[i]->state; - name_len = strlen(sym_name(&policydb, SYM_BOOLS, i)) + 1; rc = -ENOMEM; - (*names)[i] = kmalloc(sizeof(char) * name_len, GFP_ATOMIC); + (*names)[i] = kstrdup(sym_name(&policydb, SYM_BOOLS, i), GFP_ATOMIC); if (!(*names)[i]) goto err; - - strncpy((*names)[i], sym_name(&policydb, SYM_BOOLS, i), name_len); - (*names)[i][name_len - 1] = 0; } rc = 0; out: -- cgit v0.10.2 From 9529c7886c0741847eeb85cf2b0e0730eebe4fa5 Mon Sep 17 00:00:00 2001 From: Rasmus Villemoes Date: Wed, 21 Oct 2015 17:44:27 -0400 Subject: selinux: use sprintf return value sprintf returns the number of characters printed (excluding '\0'), so we can use that and avoid duplicating the length computation. Signed-off-by: Rasmus Villemoes Acked-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/ss/services.c b/security/selinux/ss/services.c index aa2bdcb..ebb5eb3 100644 --- a/security/selinux/ss/services.c +++ b/security/selinux/ss/services.c @@ -1218,13 +1218,10 @@ static int context_struct_to_string(struct context *context, char **scontext, u3 /* * Copy the user name, role name and type name into the context. */ - sprintf(scontextp, "%s:%s:%s", + scontextp += sprintf(scontextp, "%s:%s:%s", sym_name(&policydb, SYM_USERS, context->user - 1), sym_name(&policydb, SYM_ROLES, context->role - 1), sym_name(&policydb, SYM_TYPES, context->type - 1)); - scontextp += strlen(sym_name(&policydb, SYM_USERS, context->user - 1)) + - 1 + strlen(sym_name(&policydb, SYM_ROLES, context->role - 1)) + - 1 + strlen(sym_name(&policydb, SYM_TYPES, context->type - 1)); mls_sid_to_context(context, &scontextp); -- cgit v0.10.2 From 1d2a168a085f1c65b895f258ee11a52813d25af6 Mon Sep 17 00:00:00 2001 From: Geliang Tang Date: Wed, 21 Oct 2015 17:44:27 -0400 Subject: selinux: ioctl_has_perm should be static Fixes the following sparse warning: security/selinux/hooks.c:3242:5: warning: symbol 'ioctl_has_perm' was not declared. Should it be static? Signed-off-by: Geliang Tang Acked-by: Jeff Vander Stoep Acked-by: Stephen Smalley Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 1530f66..799d15a 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -3235,7 +3235,7 @@ static void selinux_file_free_security(struct file *file) * Check whether a task has the ioctl permission and cmd * operation to an inode. */ -int ioctl_has_perm(const struct cred *cred, struct file *file, +static int ioctl_has_perm(const struct cred *cred, struct file *file, u32 requested, u16 cmd) { struct common_audit_data ad; -- cgit v0.10.2 From 63205654c0e05e5ffa1c6eef2fbef21dcabd2185 Mon Sep 17 00:00:00 2001 From: Sangwoo Date: Wed, 21 Oct 2015 17:44:30 -0400 Subject: selinux: Use a kmem_cache for allocation struct file_security_struct The size of struct file_security_struct is 16byte at my setup. But, the real allocation size for per each file_security_struct is 64bytes in my setup that kmalloc min size is 64bytes because ARCH_DMA_MINALIGN is 64. This allocation is called every times at file allocation(alloc_file()). So, the total slack memory size(allocated size - request size) is increased exponentially. E.g) Min Kmalloc Size : 64bytes, Unit : bytes Allocated Size | Request Size | Slack Size | Allocation Count --------------------------------------------------------------- 770048 | 192512 | 577536 | 12032 At the result, this change reduce memory usage 42bytes per each file_security_struct Signed-off-by: Sangwoo Acked-by: Stephen Smalley [PM: removed extra subject prefix] Signed-off-by: Paul Moore diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 799d15a..3053992 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -126,6 +126,7 @@ int selinux_enabled = 1; #endif static struct kmem_cache *sel_inode_cache; +static struct kmem_cache *file_security_cache; /** * selinux_secmark_enabled - Check to see if SECMARK is currently enabled @@ -287,7 +288,7 @@ static int file_alloc_security(struct file *file) struct file_security_struct *fsec; u32 sid = current_sid(); - fsec = kzalloc(sizeof(struct file_security_struct), GFP_KERNEL); + fsec = kmem_cache_zalloc(file_security_cache, GFP_KERNEL); if (!fsec) return -ENOMEM; @@ -302,7 +303,7 @@ static void file_free_security(struct file *file) { struct file_security_struct *fsec = file->f_security; file->f_security = NULL; - kfree(fsec); + kmem_cache_free(file_security_cache, fsec); } static int superblock_alloc_security(struct super_block *sb) @@ -6086,6 +6087,9 @@ static __init int selinux_init(void) sel_inode_cache = kmem_cache_create("selinux_inode_security", sizeof(struct inode_security_struct), 0, SLAB_PANIC, NULL); + file_security_cache = kmem_cache_create("selinux_file_security", + sizeof(struct file_security_struct), + 0, SLAB_PANIC, NULL); avc_init(); security_add_hooks(selinux_hooks, ARRAY_SIZE(selinux_hooks)); -- cgit v0.10.2 From 083c1290ca73666ce1b551cc89d080d060f02ad6 Mon Sep 17 00:00:00 2001 From: Arnd Bergmann Date: Wed, 21 Oct 2015 21:16:29 +0200 Subject: apparmor: clarify CRYPTO dependency The crypto framework can be built as a loadable module, but the apparmor hash code can only be built-in, which then causes a link error: security/built-in.o: In function `aa_calc_profile_hash': integrity_audit.c:(.text+0x21610): undefined reference to `crypto_shash_update' security/built-in.o: In function `init_profile_hash': integrity_audit.c:(.init.text+0xb4c): undefined reference to `crypto_alloc_shash' This changes Apparmor to use 'select CRYPTO' like a lot of other subsystems do. Signed-off-by: Arnd Bergmann Acked-by: John Johansen Signed-off-by: James Morris diff --git a/security/apparmor/Kconfig b/security/apparmor/Kconfig index d49c539..232469b 100644 --- a/security/apparmor/Kconfig +++ b/security/apparmor/Kconfig @@ -33,7 +33,7 @@ config SECURITY_APPARMOR_BOOTPARAM_VALUE config SECURITY_APPARMOR_HASH bool "SHA1 hash of loaded profiles" depends on SECURITY_APPARMOR - depends on CRYPTO + select CRYPTO select CRYPTO_SHA1 default y -- cgit v0.10.2