From 2e3fadbf730fd0d13c891d5e555af3e7f39ca3f4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Implement a parser [RFC 2315] Implement a parser for a PKCS#7 signed-data message as described in part of RFC 2315. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- include/crypto/pkcs7.h | 23 +++++++++++++++++++++++ include/linux/oid_registry.h | 1 + 2 files changed, 24 insertions(+) create mode 100644 include/crypto/pkcs7.h (limited to 'include') diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h new file mode 100644 index 000000000000..8f2628fbdfce --- /dev/null +++ b/include/crypto/pkcs7.h @@ -0,0 +1,23 @@ +/* PKCS#7 crypto data parser + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +struct pkcs7_message; + +/* + * pkcs7_parser.c + */ +extern struct pkcs7_message *pkcs7_parse_message(const void *data, + size_t datalen); +extern void pkcs7_free_message(struct pkcs7_message *pkcs7); + +extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, + const void **_data, size_t *_datalen, + bool want_wrapper); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index 6926db724258..edeff85cb1e8 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -55,6 +55,7 @@ enum OID { OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ OID_sha1, /* 1.3.14.3.2.26 */ + OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ /* Distinguished Name attribute IDs [RFC 2256] */ OID_commonName, /* 2.5.4.3 */ -- cgit v1.2.3-59-g8ed1b From a4730357ee724f8c64f0292541ba3da8a95510fb Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:19 +0100 Subject: PKCS#7: Find the right key in the PKCS#7 key list and verify the signature Find the appropriate key in the PKCS#7 key list and verify the signature with it. There may be several keys in there forming a chain. Any link in that chain or the root of that chain may be in our keyrings. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/pkcs7_verify.c | 64 +++++++++++++++++++++++++++++++++++ include/crypto/pkcs7.h | 5 +++ 2 files changed, 69 insertions(+) (limited to 'include') diff --git a/crypto/asymmetric_keys/pkcs7_verify.c b/crypto/asymmetric_keys/pkcs7_verify.c index 0bb408a5b64f..745e5c86a35e 100644 --- a/crypto/asymmetric_keys/pkcs7_verify.c +++ b/crypto/asymmetric_keys/pkcs7_verify.c @@ -120,6 +120,55 @@ error_no_desc: } /* + * Find the key (X.509 certificate) to use to verify a PKCS#7 message. PKCS#7 + * uses the issuer's name and the issuing certificate serial number for + * matching purposes. These must match the certificate issuer's name (not + * subject's name) and the certificate serial number [RFC 2315 6.7]. + */ +static int pkcs7_find_key(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo) +{ + struct x509_certificate *x509; + unsigned certix = 1; + + kenter("%u,%u,%u", + sinfo->index, sinfo->raw_serial_size, sinfo->raw_issuer_size); + + for (x509 = pkcs7->certs; x509; x509 = x509->next, certix++) { + /* I'm _assuming_ that the generator of the PKCS#7 message will + * encode the fields from the X.509 cert in the same way in the + * PKCS#7 message - but I can't be 100% sure of that. It's + * possible this will need element-by-element comparison. + */ + if (x509->raw_serial_size != sinfo->raw_serial_size || + memcmp(x509->raw_serial, sinfo->raw_serial, + sinfo->raw_serial_size) != 0) + continue; + pr_devel("Sig %u: Found cert serial match X.509[%u]\n", + sinfo->index, certix); + + if (x509->raw_issuer_size != sinfo->raw_issuer_size || + memcmp(x509->raw_issuer, sinfo->raw_issuer, + sinfo->raw_issuer_size) != 0) { + pr_warn("Sig %u: X.509 subject and PKCS#7 issuer don't match\n", + sinfo->index); + continue; + } + + if (x509->pub->pkey_algo != sinfo->sig.pkey_algo) { + pr_warn("Sig %u: X.509 algo and PKCS#7 sig algo don't match\n", + sinfo->index); + continue; + } + + sinfo->signer = x509; + return 0; + } + pr_warn("Sig %u: Issuing X.509 cert not found (#%*ph)\n", + sinfo->index, sinfo->raw_serial_size, sinfo->raw_serial); + return -ENOKEY; +} + /* * Verify one signed information block from a PKCS#7 message. */ @@ -137,6 +186,21 @@ static int pkcs7_verify_one(struct pkcs7_message *pkcs7, if (ret < 0) return ret; + /* Find the key for the signature */ + ret = pkcs7_find_key(pkcs7, sinfo); + if (ret < 0) + return ret; + + pr_devel("Using X.509[%u] for sig %u\n", + sinfo->signer->index, sinfo->index); + + /* Verify the PKCS#7 binary against the key */ + ret = public_key_verify_signature(sinfo->signer->pub, &sinfo->sig); + if (ret < 0) + return ret; + + pr_devel("Verified signature %u\n", sinfo->index); + return 0; } diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8f2628fbdfce..8ba0f3810d67 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -21,3 +21,8 @@ extern void pkcs7_free_message(struct pkcs7_message *pkcs7); extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, bool want_wrapper); + +/* + * pkcs7_verify.c + */ +extern int pkcs7_verify(struct pkcs7_message *pkcs7); -- cgit v1.2.3-59-g8ed1b From 08815b62d700e4fbeb72a01986ad051c3dd84a15 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:40:20 +0100 Subject: PKCS#7: Find intersection between PKCS#7 message and known, trusted keys Find the intersection between the X.509 certificate chain contained in a PKCS#7 message and a set of keys that we already know and trust. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 1 + crypto/asymmetric_keys/pkcs7_trust.c | 219 +++++++++++++++++++++++++++++++++++ include/crypto/pkcs7.h | 8 ++ 3 files changed, 228 insertions(+) create mode 100644 crypto/asymmetric_keys/pkcs7_trust.c (limited to 'include') diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index b6b39e7bea01..d63cb4320b96 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -33,6 +33,7 @@ obj-$(CONFIG_PKCS7_MESSAGE_PARSER) += pkcs7_message.o pkcs7_message-y := \ pkcs7-asn1.o \ pkcs7_parser.o \ + pkcs7_trust.o \ pkcs7_verify.o $(obj)/pkcs7_parser.o: $(obj)/pkcs7-asn1.h diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c new file mode 100644 index 000000000000..b6b045131403 --- /dev/null +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -0,0 +1,219 @@ +/* Validate the trust chain of a PKCS#7 message. + * + * Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PKCS7: "fmt +#include +#include +#include +#include +#include +#include +#include +#include "public_key.h" +#include "pkcs7_parser.h" + +/* + * Request an asymmetric key. + */ +static struct key *pkcs7_request_asymmetric_key( + struct key *keyring, + const char *signer, size_t signer_len, + const char *authority, size_t auth_len) +{ + key_ref_t key; + char *id; + + kenter(",%zu,,%zu", signer_len, auth_len); + + /* Construct an identifier. */ + id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + if (!id) + return ERR_PTR(-ENOMEM); + + memcpy(id, signer, signer_len); + id[signer_len + 0] = ':'; + id[signer_len + 1] = ' '; + memcpy(id + signer_len + 2, authority, auth_len); + id[signer_len + 2 + auth_len] = 0; + + pr_debug("Look up: \"%s\"\n", id); + + key = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) + pr_debug("Request for module key '%s' err %ld\n", + id, PTR_ERR(key)); + kfree(id); + + if (IS_ERR(key)) { + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(key); + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); + return key_ref_to_ptr(key); +} + +/** + * Check the trust on one PKCS#7 SignedInfo block. + */ +int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, + struct pkcs7_signed_info *sinfo, + struct key *trust_keyring) +{ + struct public_key_signature *sig = &sinfo->sig; + struct x509_certificate *x509, *last = NULL, *p; + struct key *key; + bool trusted; + int ret; + + kenter(",%u,", sinfo->index); + + for (x509 = sinfo->signer; x509; x509 = x509->signer) { + if (x509->seen) { + if (x509->verified) { + trusted = x509->trusted; + goto verified; + } + kleave(" = -ENOKEY [cached]"); + return -ENOKEY; + } + x509->seen = true; + + /* Look to see if this certificate is present in the trusted + * keys. + */ + key = pkcs7_request_asymmetric_key( + trust_keyring, + x509->subject, strlen(x509->subject), + x509->fingerprint, strlen(x509->fingerprint)); + if (!IS_ERR(key)) + /* One of the X.509 certificates in the PKCS#7 message + * is apparently the same as one we already trust. + * Verify that the trusted variant can also validate + * the signature on the descendant. + */ + goto matched; + if (key == ERR_PTR(-ENOMEM)) + return -ENOMEM; + + /* Self-signed certificates form roots of their own, and if we + * don't know them, then we can't accept them. + */ + if (x509->next == x509) { + kleave(" = -ENOKEY [unknown self-signed]"); + return -ENOKEY; + } + + might_sleep(); + last = x509; + sig = &last->sig; + } + + /* No match - see if the root certificate has a signer amongst the + * trusted keys. + */ + if (!last || !last->issuer || !last->authority) { + kleave(" = -ENOKEY [no backref]"); + return -ENOKEY; + } + + key = pkcs7_request_asymmetric_key( + trust_keyring, + last->issuer, strlen(last->issuer), + last->authority, strlen(last->authority)); + if (IS_ERR(key)) + return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; + x509 = last; + +matched: + ret = verify_signature(key, sig); + trusted = test_bit(KEY_FLAG_TRUSTED, &key->flags); + key_put(key); + if (ret < 0) { + if (ret == -ENOMEM) + return ret; + kleave(" = -EKEYREJECTED [verify %d]", ret); + return -EKEYREJECTED; + } + +verified: + x509->verified = true; + for (p = sinfo->signer; p != x509; p = p->signer) { + p->verified = true; + p->trusted = trusted; + } + sinfo->trusted = trusted; + kleave(" = 0"); + return 0; +} + +/** + * pkcs7_validate_trust - Validate PKCS#7 trust chain + * @pkcs7: The PKCS#7 certificate to validate + * @trust_keyring: Signing certificates to use as starting points + * @_trusted: Set to true if trustworth, false otherwise + * + * Validate that the certificate chain inside the PKCS#7 message intersects + * keys we already know and trust. + * + * Returns, in order of descending priority: + * + * (*) -EKEYREJECTED if a signature failed to match for which we have a valid + * key, or: + * + * (*) 0 if at least one signature chain intersects with the keys in the trust + * keyring, or: + * + * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a + * chain. + * + * (*) -ENOKEY if we couldn't find a match for any of the signature chains in + * the message. + * + * May also return -ENOMEM. + */ +int pkcs7_validate_trust(struct pkcs7_message *pkcs7, + struct key *trust_keyring, + bool *_trusted) +{ + struct pkcs7_signed_info *sinfo; + struct x509_certificate *p; + int cached_ret = 0, ret; + + for (p = pkcs7->certs; p; p = p->next) + p->seen = false; + + for (sinfo = pkcs7->signed_infos; sinfo; sinfo = sinfo->next) { + ret = pkcs7_validate_trust_one(pkcs7, sinfo, trust_keyring); + if (ret < 0) { + if (ret == -ENOPKG) { + cached_ret = -ENOPKG; + } else if (ret == -ENOKEY) { + if (cached_ret == 0) + cached_ret = -ENOKEY; + } else { + return ret; + } + } + *_trusted |= sinfo->trusted; + } + + return cached_ret; +} +EXPORT_SYMBOL_GPL(pkcs7_validate_trust); diff --git a/include/crypto/pkcs7.h b/include/crypto/pkcs7.h index 8ba0f3810d67..691c79172a26 100644 --- a/include/crypto/pkcs7.h +++ b/include/crypto/pkcs7.h @@ -9,6 +9,7 @@ * 2 of the Licence, or (at your option) any later version. */ +struct key; struct pkcs7_message; /* @@ -22,6 +23,13 @@ extern int pkcs7_get_content_data(const struct pkcs7_message *pkcs7, const void **_data, size_t *_datalen, bool want_wrapper); +/* + * pkcs7_trust.c + */ +extern int pkcs7_validate_trust(struct pkcs7_message *pkcs7, + struct key *trust_keyring, + bool *_trusted); + /* * pkcs7_verify.c */ -- cgit v1.2.3-59-g8ed1b From 9c87e0f10e281f782312e7b6aa202f2d434c84bf Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:51 +0100 Subject: Provide PE binary definitions Provide some PE binary structural and constant definitions as taken from the pesign package sources. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- include/linux/pe.h | 448 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 448 insertions(+) create mode 100644 include/linux/pe.h (limited to 'include') diff --git a/include/linux/pe.h b/include/linux/pe.h new file mode 100644 index 000000000000..e170b95e763b --- /dev/null +++ b/include/linux/pe.h @@ -0,0 +1,448 @@ +/* + * Copyright 2011 Red Hat, Inc. + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; version 2 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + * + * Author(s): Peter Jones + */ +#ifndef __LINUX_PE_H +#define __LINUX_PE_H + +#include + +#define MZ_MAGIC 0x5a4d /* "MZ" */ + +struct mz_hdr { + uint16_t magic; /* MZ_MAGIC */ + uint16_t lbsize; /* size of last used block */ + uint16_t blocks; /* pages in file, 0x3 */ + uint16_t relocs; /* relocations */ + uint16_t hdrsize; /* header size in "paragraphs" */ + uint16_t min_extra_pps; /* .bss */ + uint16_t max_extra_pps; /* runtime limit for the arena size */ + uint16_t ss; /* relative stack segment */ + uint16_t sp; /* initial %sp register */ + uint16_t checksum; /* word checksum */ + uint16_t ip; /* initial %ip register */ + uint16_t cs; /* initial %cs relative to load segment */ + uint16_t reloc_table_offset; /* offset of the first relocation */ + uint16_t overlay_num; /* overlay number. set to 0. */ + uint16_t reserved0[4]; /* reserved */ + uint16_t oem_id; /* oem identifier */ + uint16_t oem_info; /* oem specific */ + uint16_t reserved1[10]; /* reserved */ + uint32_t peaddr; /* address of pe header */ + char message[64]; /* message to print */ +}; + +struct mz_reloc { + uint16_t offset; + uint16_t segment; +}; + +#define PE_MAGIC 0x00004550 /* "PE\0\0" */ +#define PE_OPT_MAGIC_PE32 0x010b +#define PE_OPT_MAGIC_PE32_ROM 0x0107 +#define PE_OPT_MAGIC_PE32PLUS 0x020b + +/* machine type */ +#define IMAGE_FILE_MACHINE_UNKNOWN 0x0000 +#define IMAGE_FILE_MACHINE_AM33 0x01d3 +#define IMAGE_FILE_MACHINE_AMD64 0x8664 +#define IMAGE_FILE_MACHINE_ARM 0x01c0 +#define IMAGE_FILE_MACHINE_ARMV7 0x01c4 +#define IMAGE_FILE_MACHINE_EBC 0x0ebc +#define IMAGE_FILE_MACHINE_I386 0x014c +#define IMAGE_FILE_MACHINE_IA64 0x0200 +#define IMAGE_FILE_MACHINE_M32R 0x9041 +#define IMAGE_FILE_MACHINE_MIPS16 0x0266 +#define IMAGE_FILE_MACHINE_MIPSFPU 0x0366 +#define IMAGE_FILE_MACHINE_MIPSFPU16 0x0466 +#define IMAGE_FILE_MACHINE_POWERPC 0x01f0 +#define IMAGE_FILE_MACHINE_POWERPCFP 0x01f1 +#define IMAGE_FILE_MACHINE_R4000 0x0166 +#define IMAGE_FILE_MACHINE_SH3 0x01a2 +#define IMAGE_FILE_MACHINE_SH3DSP 0x01a3 +#define IMAGE_FILE_MACHINE_SH3E 0x01a4 +#define IMAGE_FILE_MACHINE_SH4 0x01a6 +#define IMAGE_FILE_MACHINE_SH5 0x01a8 +#define IMAGE_FILE_MACHINE_THUMB 0x01c2 +#define IMAGE_FILE_MACHINE_WCEMIPSV2 0x0169 + +/* flags */ +#define IMAGE_FILE_RELOCS_STRIPPED 0x0001 +#define IMAGE_FILE_EXECUTABLE_IMAGE 0x0002 +#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004 +#define IMAGE_FILE_LOCAL_SYMS_STRIPPED 0x0008 +#define IMAGE_FILE_AGGRESSIVE_WS_TRIM 0x0010 +#define IMAGE_FILE_LARGE_ADDRESS_AWARE 0x0020 +#define IMAGE_FILE_16BIT_MACHINE 0x0040 +#define IMAGE_FILE_BYTES_REVERSED_LO 0x0080 +#define IMAGE_FILE_32BIT_MACHINE 0x0100 +#define IMAGE_FILE_DEBUG_STRIPPED 0x0200 +#define IMAGE_FILE_REMOVABLE_RUN_FROM_SWAP 0x0400 +#define IMAGE_FILE_NET_RUN_FROM_SWAP 0x0800 +#define IMAGE_FILE_SYSTEM 0x1000 +#define IMAGE_FILE_DLL 0x2000 +#define IMAGE_FILE_UP_SYSTEM_ONLY 0x4000 +#define IMAGE_FILE_BYTES_REVERSED_HI 0x8000 + +struct pe_hdr { + uint32_t magic; /* PE magic */ + uint16_t machine; /* machine type */ + uint16_t sections; /* number of sections */ + uint32_t timestamp; /* time_t */ + uint32_t symbol_table; /* symbol table offset */ + uint32_t symbols; /* number of symbols */ + uint16_t opt_hdr_size; /* size of optional header */ + uint16_t flags; /* flags */ +}; + +#define IMAGE_FILE_OPT_ROM_MAGIC 0x107 +#define IMAGE_FILE_OPT_PE32_MAGIC 0x10b +#define IMAGE_FILE_OPT_PE32_PLUS_MAGIC 0x20b + +#define IMAGE_SUBSYSTEM_UNKNOWN 0 +#define IMAGE_SUBSYSTEM_NATIVE 1 +#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2 +#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3 +#define IMAGE_SUBSYSTEM_POSIX_CUI 7 +#define IMAGE_SUBSYSTEM_WINDOWS_CE_GUI 9 +#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10 +#define IMAGE_SUBSYSTEM_EFI_BOOT_SERVICE_DRIVER 11 +#define IMAGE_SUBSYSTEM_EFI_RUNTIME_DRIVER 12 +#define IMAGE_SUBSYSTEM_EFI_ROM_IMAGE 13 +#define IMAGE_SUBSYSTEM_XBOX 14 + +#define IMAGE_DLL_CHARACTERISTICS_DYNAMIC_BASE 0x0040 +#define IMAGE_DLL_CHARACTERISTICS_FORCE_INTEGRITY 0x0080 +#define IMAGE_DLL_CHARACTERISTICS_NX_COMPAT 0x0100 +#define IMAGE_DLLCHARACTERISTICS_NO_ISOLATION 0x0200 +#define IMAGE_DLLCHARACTERISTICS_NO_SEH 0x0400 +#define IMAGE_DLLCHARACTERISTICS_NO_BIND 0x0800 +#define IMAGE_DLLCHARACTERISTICS_WDM_DRIVER 0x2000 +#define IMAGE_DLLCHARACTERISTICS_TERMINAL_SERVER_AWARE 0x8000 + +/* the fact that pe32 isn't padded where pe32+ is 64-bit means union won't + * work right. vomit. */ +struct pe32_opt_hdr { + /* "standard" header */ + uint16_t magic; /* file type */ + uint8_t ld_major; /* linker major version */ + uint8_t ld_minor; /* linker minor version */ + uint32_t text_size; /* size of text section(s) */ + uint32_t data_size; /* size of data section(s) */ + uint32_t bss_size; /* size of bss section(s) */ + uint32_t entry_point; /* file offset of entry point */ + uint32_t code_base; /* relative code addr in ram */ + uint32_t data_base; /* relative data addr in ram */ + /* "windows" header */ + uint32_t image_base; /* preferred load address */ + uint32_t section_align; /* alignment in bytes */ + uint32_t file_align; /* file alignment in bytes */ + uint16_t os_major; /* major OS version */ + uint16_t os_minor; /* minor OS version */ + uint16_t image_major; /* major image version */ + uint16_t image_minor; /* minor image version */ + uint16_t subsys_major; /* major subsystem version */ + uint16_t subsys_minor; /* minor subsystem version */ + uint32_t win32_version; /* reserved, must be 0 */ + uint32_t image_size; /* image size */ + uint32_t header_size; /* header size rounded up to + file_align */ + uint32_t csum; /* checksum */ + uint16_t subsys; /* subsystem */ + uint16_t dll_flags; /* more flags! */ + uint32_t stack_size_req;/* amt of stack requested */ + uint32_t stack_size; /* amt of stack required */ + uint32_t heap_size_req; /* amt of heap requested */ + uint32_t heap_size; /* amt of heap required */ + uint32_t loader_flags; /* reserved, must be 0 */ + uint32_t data_dirs; /* number of data dir entries */ +}; + +struct pe32plus_opt_hdr { + uint16_t magic; /* file type */ + uint8_t ld_major; /* linker major version */ + uint8_t ld_minor; /* linker minor version */ + uint32_t text_size; /* size of text section(s) */ + uint32_t data_size; /* size of data section(s) */ + uint32_t bss_size; /* size of bss section(s) */ + uint32_t entry_point; /* file offset of entry point */ + uint32_t code_base; /* relative code addr in ram */ + /* "windows" header */ + uint64_t image_base; /* preferred load address */ + uint32_t section_align; /* alignment in bytes */ + uint32_t file_align; /* file alignment in bytes */ + uint16_t os_major; /* major OS version */ + uint16_t os_minor; /* minor OS version */ + uint16_t image_major; /* major image version */ + uint16_t image_minor; /* minor image version */ + uint16_t subsys_major; /* major subsystem version */ + uint16_t subsys_minor; /* minor subsystem version */ + uint32_t win32_version; /* reserved, must be 0 */ + uint32_t image_size; /* image size */ + uint32_t header_size; /* header size rounded up to + file_align */ + uint32_t csum; /* checksum */ + uint16_t subsys; /* subsystem */ + uint16_t dll_flags; /* more flags! */ + uint64_t stack_size_req;/* amt of stack requested */ + uint64_t stack_size; /* amt of stack required */ + uint64_t heap_size_req; /* amt of heap requested */ + uint64_t heap_size; /* amt of heap required */ + uint32_t loader_flags; /* reserved, must be 0 */ + uint32_t data_dirs; /* number of data dir entries */ +}; + +struct data_dirent { + uint32_t virtual_address; /* relative to load address */ + uint32_t size; +}; + +struct data_directory { + struct data_dirent exports; /* .edata */ + struct data_dirent imports; /* .idata */ + struct data_dirent resources; /* .rsrc */ + struct data_dirent exceptions; /* .pdata */ + struct data_dirent certs; /* certs */ + struct data_dirent base_relocations; /* .reloc */ + struct data_dirent debug; /* .debug */ + struct data_dirent arch; /* reservered */ + struct data_dirent global_ptr; /* global pointer reg. Size=0 */ + struct data_dirent tls; /* .tls */ + struct data_dirent load_config; /* load configuration structure */ + struct data_dirent bound_imports; /* no idea */ + struct data_dirent import_addrs; /* import address table */ + struct data_dirent delay_imports; /* delay-load import table */ + struct data_dirent clr_runtime_hdr; /* .cor (object only) */ + struct data_dirent reserved; +}; + +struct section_header { + char name[8]; /* name or "/12\0" string tbl offset */ + uint32_t virtual_size; /* size of loaded section in ram */ + uint32_t virtual_address; /* relative virtual address */ + uint32_t raw_data_size; /* size of the section */ + uint32_t data_addr; /* file pointer to first page of sec */ + uint32_t relocs; /* file pointer to relocation entries */ + uint32_t line_numbers; /* line numbers! */ + uint16_t num_relocs; /* number of relocations */ + uint16_t num_lin_numbers; /* srsly. */ + uint32_t flags; +}; + +/* they actually defined 0x00000000 as well, but I think we'll skip that one. */ +#define IMAGE_SCN_RESERVED_0 0x00000001 +#define IMAGE_SCN_RESERVED_1 0x00000002 +#define IMAGE_SCN_RESERVED_2 0x00000004 +#define IMAGE_SCN_TYPE_NO_PAD 0x00000008 /* don't pad - obsolete */ +#define IMAGE_SCN_RESERVED_3 0x00000010 +#define IMAGE_SCN_CNT_CODE 0x00000020 /* .text */ +#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* .data */ +#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* .bss */ +#define IMAGE_SCN_LNK_OTHER 0x00000100 /* reserved */ +#define IMAGE_SCN_LNK_INFO 0x00000200 /* .drectve comments */ +#define IMAGE_SCN_RESERVED_4 0x00000400 +#define IMAGE_SCN_LNK_REMOVE 0x00000800 /* .o only - scn to be rm'd*/ +#define IMAGE_SCN_LNK_COMDAT 0x00001000 /* .o only - COMDAT data */ +#define IMAGE_SCN_RESERVED_5 0x00002000 /* spec omits this */ +#define IMAGE_SCN_RESERVED_6 0x00004000 /* spec omits this */ +#define IMAGE_SCN_GPREL 0x00008000 /* global pointer referenced data */ +/* spec lists 0x20000 twice, I suspect they meant 0x10000 for one of them */ +#define IMAGE_SCN_MEM_PURGEABLE 0x00010000 /* reserved for "future" use */ +#define IMAGE_SCN_16BIT 0x00020000 /* reserved for "future" use */ +#define IMAGE_SCN_LOCKED 0x00040000 /* reserved for "future" use */ +#define IMAGE_SCN_PRELOAD 0x00080000 /* reserved for "future" use */ +/* and here they just stuck a 1-byte integer in the middle of a bitfield */ +#define IMAGE_SCN_ALIGN_1BYTES 0x00100000 /* it does what it says on the box */ +#define IMAGE_SCN_ALIGN_2BYTES 0x00200000 +#define IMAGE_SCN_ALIGN_4BYTES 0x00300000 +#define IMAGE_SCN_ALIGN_8BYTES 0x00400000 +#define IMAGE_SCN_ALIGN_16BYTES 0x00500000 +#define IMAGE_SCN_ALIGN_32BYTES 0x00600000 +#define IMAGE_SCN_ALIGN_64BYTES 0x00700000 +#define IMAGE_SCN_ALIGN_128BYTES 0x00800000 +#define IMAGE_SCN_ALIGN_256BYTES 0x00900000 +#define IMAGE_SCN_ALIGN_512BYTES 0x00a00000 +#define IMAGE_SCN_ALIGN_1024BYTES 0x00b00000 +#define IMAGE_SCN_ALIGN_2048BYTES 0x00c00000 +#define IMAGE_SCN_ALIGN_4096BYTES 0x00d00000 +#define IMAGE_SCN_ALIGN_8192BYTES 0x00e00000 +#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000 /* extended relocations */ +#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000 /* scn can be discarded */ +#define IMAGE_SCN_MEM_NOT_CACHED 0x04000000 /* cannot be cached */ +#define IMAGE_SCN_MEM_NOT_PAGED 0x08000000 /* not pageable */ +#define IMAGE_SCN_MEM_SHARED 0x10000000 /* can be shared */ +#define IMAGE_SCN_MEM_EXECUTE 0x20000000 /* can be executed as code */ +#define IMAGE_SCN_MEM_READ 0x40000000 /* readable */ +#define IMAGE_SCN_MEM_WRITE 0x80000000 /* writeable */ + +enum x64_coff_reloc_type { + IMAGE_REL_AMD64_ABSOLUTE = 0, + IMAGE_REL_AMD64_ADDR64, + IMAGE_REL_AMD64_ADDR32, + IMAGE_REL_AMD64_ADDR32N, + IMAGE_REL_AMD64_REL32, + IMAGE_REL_AMD64_REL32_1, + IMAGE_REL_AMD64_REL32_2, + IMAGE_REL_AMD64_REL32_3, + IMAGE_REL_AMD64_REL32_4, + IMAGE_REL_AMD64_REL32_5, + IMAGE_REL_AMD64_SECTION, + IMAGE_REL_AMD64_SECREL, + IMAGE_REL_AMD64_SECREL7, + IMAGE_REL_AMD64_TOKEN, + IMAGE_REL_AMD64_SREL32, + IMAGE_REL_AMD64_PAIR, + IMAGE_REL_AMD64_SSPAN32, +}; + +enum arm_coff_reloc_type { + IMAGE_REL_ARM_ABSOLUTE, + IMAGE_REL_ARM_ADDR32, + IMAGE_REL_ARM_ADDR32N, + IMAGE_REL_ARM_BRANCH2, + IMAGE_REL_ARM_BRANCH1, + IMAGE_REL_ARM_SECTION, + IMAGE_REL_ARM_SECREL, +}; + +enum sh_coff_reloc_type { + IMAGE_REL_SH3_ABSOLUTE, + IMAGE_REL_SH3_DIRECT16, + IMAGE_REL_SH3_DIRECT32, + IMAGE_REL_SH3_DIRECT8, + IMAGE_REL_SH3_DIRECT8_WORD, + IMAGE_REL_SH3_DIRECT8_LONG, + IMAGE_REL_SH3_DIRECT4, + IMAGE_REL_SH3_DIRECT4_WORD, + IMAGE_REL_SH3_DIRECT4_LONG, + IMAGE_REL_SH3_PCREL8_WORD, + IMAGE_REL_SH3_PCREL8_LONG, + IMAGE_REL_SH3_PCREL12_WORD, + IMAGE_REL_SH3_STARTOF_SECTION, + IMAGE_REL_SH3_SIZEOF_SECTION, + IMAGE_REL_SH3_SECTION, + IMAGE_REL_SH3_SECREL, + IMAGE_REL_SH3_DIRECT32_NB, + IMAGE_REL_SH3_GPREL4_LONG, + IMAGE_REL_SH3_TOKEN, + IMAGE_REL_SHM_PCRELPT, + IMAGE_REL_SHM_REFLO, + IMAGE_REL_SHM_REFHALF, + IMAGE_REL_SHM_RELLO, + IMAGE_REL_SHM_RELHALF, + IMAGE_REL_SHM_PAIR, + IMAGE_REL_SHM_NOMODE, +}; + +enum ppc_coff_reloc_type { + IMAGE_REL_PPC_ABSOLUTE, + IMAGE_REL_PPC_ADDR64, + IMAGE_REL_PPC_ADDR32, + IMAGE_REL_PPC_ADDR24, + IMAGE_REL_PPC_ADDR16, + IMAGE_REL_PPC_ADDR14, + IMAGE_REL_PPC_REL24, + IMAGE_REL_PPC_REL14, + IMAGE_REL_PPC_ADDR32N, + IMAGE_REL_PPC_SECREL, + IMAGE_REL_PPC_SECTION, + IMAGE_REL_PPC_SECREL16, + IMAGE_REL_PPC_REFHI, + IMAGE_REL_PPC_REFLO, + IMAGE_REL_PPC_PAIR, + IMAGE_REL_PPC_SECRELLO, + IMAGE_REL_PPC_GPREL, + IMAGE_REL_PPC_TOKEN, +}; + +enum x86_coff_reloc_type { + IMAGE_REL_I386_ABSOLUTE, + IMAGE_REL_I386_DIR16, + IMAGE_REL_I386_REL16, + IMAGE_REL_I386_DIR32, + IMAGE_REL_I386_DIR32NB, + IMAGE_REL_I386_SEG12, + IMAGE_REL_I386_SECTION, + IMAGE_REL_I386_SECREL, + IMAGE_REL_I386_TOKEN, + IMAGE_REL_I386_SECREL7, + IMAGE_REL_I386_REL32, +}; + +enum ia64_coff_reloc_type { + IMAGE_REL_IA64_ABSOLUTE, + IMAGE_REL_IA64_IMM14, + IMAGE_REL_IA64_IMM22, + IMAGE_REL_IA64_IMM64, + IMAGE_REL_IA64_DIR32, + IMAGE_REL_IA64_DIR64, + IMAGE_REL_IA64_PCREL21B, + IMAGE_REL_IA64_PCREL21M, + IMAGE_REL_IA64_PCREL21F, + IMAGE_REL_IA64_GPREL22, + IMAGE_REL_IA64_LTOFF22, + IMAGE_REL_IA64_SECTION, + IMAGE_REL_IA64_SECREL22, + IMAGE_REL_IA64_SECREL64I, + IMAGE_REL_IA64_SECREL32, + IMAGE_REL_IA64_DIR32NB, + IMAGE_REL_IA64_SREL14, + IMAGE_REL_IA64_SREL22, + IMAGE_REL_IA64_SREL32, + IMAGE_REL_IA64_UREL32, + IMAGE_REL_IA64_PCREL60X, + IMAGE_REL_IA64_PCREL60B, + IMAGE_REL_IA64_PCREL60F, + IMAGE_REL_IA64_PCREL60I, + IMAGE_REL_IA64_PCREL60M, + IMAGE_REL_IA64_IMMGPREL6, + IMAGE_REL_IA64_TOKEN, + IMAGE_REL_IA64_GPREL32, + IMAGE_REL_IA64_ADDEND, +}; + +struct coff_reloc { + uint32_t virtual_address; + uint32_t symbol_table_index; + union { + enum x64_coff_reloc_type x64_type; + enum arm_coff_reloc_type arm_type; + enum sh_coff_reloc_type sh_type; + enum ppc_coff_reloc_type ppc_type; + enum x86_coff_reloc_type x86_type; + enum ia64_coff_reloc_type ia64_type; + uint16_t data; + }; +}; + +/* + * Definitions for the contents of the certs data block + */ +#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 +#define WIN_CERT_TYPE_EFI_OKCS115 0x0EF0 +#define WIN_CERT_TYPE_EFI_GUID 0x0EF1 + +#define WIN_CERT_REVISION_1_0 0x0100 +#define WIN_CERT_REVISION_2_0 0x0200 + +struct win_certificate { + uint32_t length; + uint16_t revision; + uint16_t cert_type; +}; + +#endif /* __LINUX_PE_H */ -- cgit v1.2.3-59-g8ed1b From 26d1164be37f1145a96af15f294122876d8e5c77 Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:51 +0100 Subject: pefile: Parse a PE binary to find a key and a signature contained therein Parse a PE binary to find a key and a signature contained therein. Later patches will check the signature and add the key if the signature checks out. Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Kconfig | 9 ++ crypto/asymmetric_keys/Makefile | 8 ++ crypto/asymmetric_keys/verify_pefile.c | 163 +++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/verify_pefile.h | 37 ++++++++ include/linux/verify_pefile.h | 18 ++++ 5 files changed, 235 insertions(+) create mode 100644 crypto/asymmetric_keys/verify_pefile.c create mode 100644 crypto/asymmetric_keys/verify_pefile.h create mode 100644 include/linux/verify_pefile.h (limited to 'include') diff --git a/crypto/asymmetric_keys/Kconfig b/crypto/asymmetric_keys/Kconfig index 14cac2860afa..ca41be5631c7 100644 --- a/crypto/asymmetric_keys/Kconfig +++ b/crypto/asymmetric_keys/Kconfig @@ -59,4 +59,13 @@ config PKCS7_TEST_KEY This is intended for testing the PKCS#7 parser. +config SIGNED_PE_FILE_VERIFICATION + bool "Support for PE file signature verification" + depends on PKCS7_MESSAGE_PARSER=y + select ASN1 + select OID_REGISTRY + help + This option provides support for verifying the signature(s) on a + signed PE binary. + endif # ASYMMETRIC_KEY_TYPE diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 92d0e9af24d5..3e4de5297015 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -47,3 +47,11 @@ clean-files += pkcs7-asn1.c pkcs7-asn1.h obj-$(CONFIG_PKCS7_TEST_KEY) += pkcs7_test_key.o pkcs7_test_key-y := \ pkcs7_key_type.o + +# +# Signed PE binary-wrapped key handling +# +obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o + +verify_signed_pefile-y := \ + verify_pefile.o diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c new file mode 100644 index 000000000000..aec7c509404e --- /dev/null +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -0,0 +1,163 @@ +/* Parse a signed PE binary + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "PEFILE: "fmt +#include +#include +#include +#include +#include +#include +#include +#include "verify_pefile.h" + +/* + * Parse a PE binary. + */ +static int pefile_parse_binary(const void *pebuf, unsigned int pelen, + struct pefile_context *ctx) +{ + const struct mz_hdr *mz = pebuf; + const struct pe_hdr *pe; + const struct pe32_opt_hdr *pe32; + const struct pe32plus_opt_hdr *pe64; + const struct data_directory *ddir; + const struct data_dirent *dde; + const struct section_header *secs, *sec; + size_t cursor, datalen = pelen; + + kenter(""); + +#define chkaddr(base, x, s) \ + do { \ + if ((x) < base || (s) >= datalen || (x) > datalen - (s)) \ + return -ELIBBAD; \ + } while (0) + + chkaddr(0, 0, sizeof(*mz)); + if (mz->magic != MZ_MAGIC) + return -ELIBBAD; + cursor = sizeof(*mz); + + chkaddr(cursor, mz->peaddr, sizeof(*pe)); + pe = pebuf + mz->peaddr; + if (pe->magic != PE_MAGIC) + return -ELIBBAD; + cursor = mz->peaddr + sizeof(*pe); + + chkaddr(0, cursor, sizeof(pe32->magic)); + pe32 = pebuf + cursor; + pe64 = pebuf + cursor; + + switch (pe32->magic) { + case PE_OPT_MAGIC_PE32: + chkaddr(0, cursor, sizeof(*pe32)); + ctx->image_checksum_offset = + (unsigned long)&pe32->csum - (unsigned long)pebuf; + ctx->header_size = pe32->header_size; + cursor += sizeof(*pe32); + ctx->n_data_dirents = pe32->data_dirs; + break; + + case PE_OPT_MAGIC_PE32PLUS: + chkaddr(0, cursor, sizeof(*pe64)); + ctx->image_checksum_offset = + (unsigned long)&pe64->csum - (unsigned long)pebuf; + ctx->header_size = pe64->header_size; + cursor += sizeof(*pe64); + ctx->n_data_dirents = pe64->data_dirs; + break; + + default: + pr_debug("Unknown PEOPT magic = %04hx\n", pe32->magic); + return -ELIBBAD; + } + + pr_debug("checksum @ %x\n", ctx->image_checksum_offset); + pr_debug("header size = %x\n", ctx->header_size); + + if (cursor >= ctx->header_size || ctx->header_size >= datalen) + return -ELIBBAD; + + if (ctx->n_data_dirents > (ctx->header_size - cursor) / sizeof(*dde)) + return -ELIBBAD; + + ddir = pebuf + cursor; + cursor += sizeof(*dde) * ctx->n_data_dirents; + + ctx->cert_dirent_offset = + (unsigned long)&ddir->certs - (unsigned long)pebuf; + ctx->certs_size = ddir->certs.size; + + if (!ddir->certs.virtual_address || !ddir->certs.size) { + pr_debug("Unsigned PE binary\n"); + return -EKEYREJECTED; + } + + chkaddr(ctx->header_size, ddir->certs.virtual_address, + ddir->certs.size); + ctx->sig_offset = ddir->certs.virtual_address; + ctx->sig_len = ddir->certs.size; + pr_debug("cert = %x @%x [%*ph]\n", + ctx->sig_len, ctx->sig_offset, + ctx->sig_len, pebuf + ctx->sig_offset); + + ctx->n_sections = pe->sections; + if (ctx->n_sections > (ctx->header_size - cursor) / sizeof(*sec)) + return -ELIBBAD; + ctx->secs = secs = pebuf + cursor; + + return 0; +} + +/** + * verify_pefile_signature - Verify the signature on a PE binary image + * @pebuf: Buffer containing the PE binary image + * @pelen: Length of the binary image + * @trust_keyring: Signing certificates to use as starting points + * @_trusted: Set to true if trustworth, false otherwise + * + * Validate that the certificate chain inside the PKCS#7 message inside the PE + * binary image intersects keys we already know and trust. + * + * Returns, in order of descending priority: + * + * (*) -ELIBBAD if the image cannot be parsed, or: + * + * (*) -EKEYREJECTED if a signature failed to match for which we have a valid + * key, or: + * + * (*) 0 if at least one signature chain intersects with the keys in the trust + * keyring, or: + * + * (*) -ENOPKG if a suitable crypto module couldn't be found for a check on a + * chain. + * + * (*) -ENOKEY if we couldn't find a match for any of the signature chains in + * the message. + * + * May also return -ENOMEM. + */ +int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keyring, bool *_trusted) +{ + struct pefile_context ctx; + int ret; + + kenter(""); + + memset(&ctx, 0, sizeof(ctx)); + ret = pefile_parse_binary(pebuf, pelen, &ctx); + if (ret < 0) + return ret; + + return -ENOANO; // Not yet complete +} diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h new file mode 100644 index 000000000000..e165d23458d4 --- /dev/null +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -0,0 +1,37 @@ +/* PE Binary parser bits + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#include +#include +#include + +struct pefile_context { + unsigned header_size; + unsigned image_checksum_offset; + unsigned cert_dirent_offset; + unsigned n_data_dirents; + unsigned n_sections; + unsigned certs_size; + unsigned sig_offset; + unsigned sig_len; + const struct section_header *secs; + struct pkcs7_message *pkcs7; + + /* PKCS#7 MS Individual Code Signing content */ + const void *digest; /* Digest */ + unsigned digest_len; /* Digest length */ + enum hash_algo digest_algo; /* Digest algorithm */ +}; + +#define kenter(FMT, ...) \ + pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) +#define kleave(FMT, ...) \ + pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) diff --git a/include/linux/verify_pefile.h b/include/linux/verify_pefile.h new file mode 100644 index 000000000000..ac34819214f9 --- /dev/null +++ b/include/linux/verify_pefile.h @@ -0,0 +1,18 @@ +/* Signed PE file verification + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#ifndef _LINUX_VERIFY_PEFILE_H +#define _LINUX_VERIFY_PEFILE_H + +extern int verify_pefile_signature(const void *pebuf, unsigned pelen, + struct key *trusted_keyring, bool *_trusted); + +#endif /* _LINUX_VERIFY_PEFILE_H */ -- cgit v1.2.3-59-g8ed1b From 4c0b4b1d1ae0cbc86f150e2905a1c3d2a17b7c1e Mon Sep 17 00:00:00 2001 From: David Howells Date: Tue, 1 Jul 2014 16:02:52 +0100 Subject: pefile: Parse the "Microsoft individual code signing" data blob The PKCS#7 certificate should contain a "Microsoft individual code signing" data blob as its signed content. This blob contains a digest of the signed content of the PE binary and the OID of the digest algorithm used (typically SHA256). Signed-off-by: David Howells Acked-by: Vivek Goyal Reviewed-by: Kees Cook --- crypto/asymmetric_keys/Makefile | 9 ++- crypto/asymmetric_keys/mscode.asn1 | 28 ++++++++ crypto/asymmetric_keys/mscode_parser.c | 120 +++++++++++++++++++++++++++++++++ crypto/asymmetric_keys/verify_pefile.c | 7 ++ crypto/asymmetric_keys/verify_pefile.h | 5 ++ include/linux/oid_registry.h | 7 +- 6 files changed, 174 insertions(+), 2 deletions(-) create mode 100644 crypto/asymmetric_keys/mscode.asn1 create mode 100644 crypto/asymmetric_keys/mscode_parser.c (limited to 'include') diff --git a/crypto/asymmetric_keys/Makefile b/crypto/asymmetric_keys/Makefile index 3e4de5297015..e47fcd9ac5e8 100644 --- a/crypto/asymmetric_keys/Makefile +++ b/crypto/asymmetric_keys/Makefile @@ -54,4 +54,11 @@ pkcs7_test_key-y := \ obj-$(CONFIG_SIGNED_PE_FILE_VERIFICATION) += verify_signed_pefile.o verify_signed_pefile-y := \ - verify_pefile.o + verify_pefile.o \ + mscode_parser.o \ + mscode-asn1.o + +$(obj)/mscode_parser.o: $(obj)/mscode-asn1.h $(obj)/mscode-asn1.h +$(obj)/mscode-asn1.o: $(obj)/mscode-asn1.c $(obj)/mscode-asn1.h + +clean-files += mscode-asn1.c mscode-asn1.h diff --git a/crypto/asymmetric_keys/mscode.asn1 b/crypto/asymmetric_keys/mscode.asn1 new file mode 100644 index 000000000000..6d09ba48c41c --- /dev/null +++ b/crypto/asymmetric_keys/mscode.asn1 @@ -0,0 +1,28 @@ +--- Microsoft individual code signing data blob parser +--- +--- Copyright (C) 2012 Red Hat, Inc. All Rights Reserved. +--- Written by David Howells (dhowells@redhat.com) +--- +--- This program is free software; you can redistribute it and/or +--- modify it under the terms of the GNU General Public Licence +--- as published by the Free Software Foundation; either version +--- 2 of the Licence, or (at your option) any later version. +--- + +MSCode ::= SEQUENCE { + type SEQUENCE { + contentType ContentType, + parameters ANY + }, + content SEQUENCE { + digestAlgorithm DigestAlgorithmIdentifier, + digest OCTET STRING ({ mscode_note_digest }) + } +} + +ContentType ::= OBJECT IDENTIFIER ({ mscode_note_content_type }) + +DigestAlgorithmIdentifier ::= SEQUENCE { + algorithm OBJECT IDENTIFIER ({ mscode_note_digest_algo }), + parameters ANY OPTIONAL +} diff --git a/crypto/asymmetric_keys/mscode_parser.c b/crypto/asymmetric_keys/mscode_parser.c new file mode 100644 index 000000000000..09336c32b3d4 --- /dev/null +++ b/crypto/asymmetric_keys/mscode_parser.c @@ -0,0 +1,120 @@ +/* Parse a Microsoft Individual Code Signing blob + * + * Copyright (C) 2014 Red Hat, Inc. All Rights Reserved. + * Written by David Howells (dhowells@redhat.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public Licence + * as published by the Free Software Foundation; either version + * 2 of the Licence, or (at your option) any later version. + */ + +#define pr_fmt(fmt) "MSCODE: "fmt +#include +#include +#include +#include +#include +#include "verify_pefile.h" +#include "mscode-asn1.h" + +/* + * Parse a Microsoft Individual Code Signing blob + */ +int mscode_parse(struct pefile_context *ctx) +{ + const void *content_data; + size_t data_len; + int ret; + + ret = pkcs7_get_content_data(ctx->pkcs7, &content_data, &data_len, 1); + + if (ret) { + pr_debug("PKCS#7 message does not contain data\n"); + return ret; + } + + pr_devel("Data: %zu [%*ph]\n", data_len, (unsigned)(data_len), + content_data); + + return asn1_ber_decoder(&mscode_decoder, ctx, content_data, data_len); +} + +/* + * Check the content type OID + */ +int mscode_note_content_type(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + enum OID oid; + + oid = look_up_OID(value, vlen); + if (oid == OID__NR) { + char buffer[50]; + + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_err("Unknown OID: %s\n", buffer); + return -EBADMSG; + } + + if (oid != OID_msIndividualSPKeyPurpose) { + pr_err("Unexpected content type OID %u\n", oid); + return -EBADMSG; + } + + return 0; +} + +/* + * Note the digest algorithm OID + */ +int mscode_note_digest_algo(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pefile_context *ctx = context; + char buffer[50]; + enum OID oid; + + oid = look_up_OID(value, vlen); + switch (oid) { + case OID_md4: + ctx->digest_algo = HASH_ALGO_MD4; + break; + case OID_md5: + ctx->digest_algo = HASH_ALGO_MD5; + break; + case OID_sha1: + ctx->digest_algo = HASH_ALGO_SHA1; + break; + case OID_sha256: + ctx->digest_algo = HASH_ALGO_SHA256; + break; + + case OID__NR: + sprint_oid(value, vlen, buffer, sizeof(buffer)); + pr_err("Unknown OID: %s\n", buffer); + return -EBADMSG; + + default: + pr_err("Unsupported content type: %u\n", oid); + return -ENOPKG; + } + + return 0; +} + +/* + * Note the digest we're guaranteeing with this certificate + */ +int mscode_note_digest(void *context, size_t hdrlen, + unsigned char tag, + const void *value, size_t vlen) +{ + struct pefile_context *ctx = context; + + ctx->digest = value; + ctx->digest_len = vlen; + return 0; +} diff --git a/crypto/asymmetric_keys/verify_pefile.c b/crypto/asymmetric_keys/verify_pefile.c index 13f3b44b5046..b975918e82d2 100644 --- a/crypto/asymmetric_keys/verify_pefile.c +++ b/crypto/asymmetric_keys/verify_pefile.c @@ -245,6 +245,13 @@ int verify_pefile_signature(const void *pebuf, unsigned pelen, goto error; } + ret = mscode_parse(&ctx); + if (ret < 0) + goto error; + + pr_debug("Digest: %u [%*ph]\n", + ctx.digest_len, ctx.digest_len, ctx.digest); + ret = -ENOANO; // Not yet complete error: diff --git a/crypto/asymmetric_keys/verify_pefile.h b/crypto/asymmetric_keys/verify_pefile.h index e165d23458d4..55d5f7ebc45a 100644 --- a/crypto/asymmetric_keys/verify_pefile.h +++ b/crypto/asymmetric_keys/verify_pefile.h @@ -35,3 +35,8 @@ struct pefile_context { pr_devel("==> %s("FMT")\n", __func__, ##__VA_ARGS__) #define kleave(FMT, ...) \ pr_devel("<== %s()"FMT"\n", __func__, ##__VA_ARGS__) + +/* + * mscode_parser.c + */ +extern int mscode_parse(struct pefile_context *ctx); diff --git a/include/linux/oid_registry.h b/include/linux/oid_registry.h index edeff85cb1e8..c2bbf672b84e 100644 --- a/include/linux/oid_registry.h +++ b/include/linux/oid_registry.h @@ -52,8 +52,13 @@ enum OID { OID_md4, /* 1.2.840.113549.2.4 */ OID_md5, /* 1.2.840.113549.2.5 */ - OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ + /* Microsoft Authenticode & Software Publishing */ + OID_msIndirectData, /* 1.3.6.1.4.1.311.2.1.4 */ + OID_msPeImageDataObjId, /* 1.3.6.1.4.1.311.2.1.15 */ + OID_msIndividualSPKeyPurpose, /* 1.3.6.1.4.1.311.2.1.21 */ OID_msOutlookExpress, /* 1.3.6.1.4.1.311.16.4 */ + + OID_certAuthInfoAccess, /* 1.3.6.1.5.5.7.1.1 */ OID_sha1, /* 1.3.14.3.2.26 */ OID_sha256, /* 2.16.840.1.101.3.4.2.1 */ -- cgit v1.2.3-59-g8ed1b From 4da6daf4d3df5a977e4623963f141a627fd2efce Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Thu, 10 Jul 2014 10:17:48 -0400 Subject: selinux: fix the default socket labeling in sock_graft() The sock_graft() hook has special handling for AF_INET, AF_INET, and AF_UNIX sockets as those address families have special hooks which label the sock before it is attached its associated socket. Unfortunately, the sock_graft() hook was missing a default approach to labeling sockets which meant that any other address family which made use of connections or the accept() syscall would find the returned socket to be in an "unlabeled" state. This was recently demonstrated by the kcrypto/AF_ALG subsystem and the newly released cryptsetup package (cryptsetup v1.6.5 and later). This patch preserves the special handling in selinux_sock_graft(), but adds a default behavior - setting the sock's label equal to the associated socket - which resolves the problem with AF_ALG and presumably any other address family which makes use of accept(). Cc: stable@vger.kernel.org Signed-off-by: Paul Moore Tested-by: Milan Broz --- include/linux/security.h | 5 ++++- security/selinux/hooks.c | 13 +++++++++++-- 2 files changed, 15 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 6478ce3252c7..794be735ff4b 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -987,7 +987,10 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Retrieve the LSM-specific secid for the sock to enable caching of network * authorizations. * @sock_graft: - * Sets the socket's isec sid to the sock's sid. + * This hook is called in response to a newly created sock struct being + * grafted onto an existing socket and allows the security module to + * perform whatever security attribute management is necessary for both + * the sock and socket. * @inet_conn_request: * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * @inet_csk_clone: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 336f0a04450e..b3a6754e932b 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4499,9 +4499,18 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; struct sk_security_struct *sksec = sk->sk_security; - if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || - sk->sk_family == PF_UNIX) + switch (sk->sk_family) { + case PF_INET: + case PF_INET6: + case PF_UNIX: isec->sid = sksec->sid; + break; + default: + /* by default there is no special labeling mechanism for the + * sksec label so inherit the label from the parent socket */ + BUG_ON(sksec->sid != SECINITSID_UNLABELED); + sksec->sid = isec->sid; + } sksec->sclass = isec->sclass; } -- cgit v1.2.3-59-g8ed1b From 3be4beaf7c91ec9c6fefa5f11173af37113d10ae Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 20 Aug 2013 14:36:27 -0400 Subject: KEYS: verify a certificate is signed by a 'trusted' key Only public keys, with certificates signed by an existing 'trusted' key on the system trusted keyring, should be added to a trusted keyring. This patch adds support for verifying a certificate's signature. This is derived from David Howells pkcs7_request_asymmetric_key() patch. Changelog v6: - on error free key - Dmitry - validate trust only for not already trusted keys - Dmitry - formatting cleanup Changelog: - define get_system_trusted_keyring() to fix kbuild issues Signed-off-by: Mimi Zohar Signed-off-by: David Howells Acked-by: Dmitry Kasatkin --- crypto/asymmetric_keys/x509_public_key.c | 87 +++++++++++++++++++++++++++++++- include/keys/system_keyring.h | 10 +++- 2 files changed, 95 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 382ef0d2ff2e..436fbd8552fc 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -18,11 +18,61 @@ #include #include #include +#include #include #include "asymmetric_keys.h" #include "public_key.h" #include "x509_parser.h" +/* + * Find a key in the given keyring by issuer and authority. + */ +static struct key *x509_request_asymmetric_key(struct key *keyring, + const char *signer, + size_t signer_len, + const char *authority, + size_t auth_len) +{ + key_ref_t key; + char *id; + + /* Construct an identifier. */ + id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + if (!id) + return ERR_PTR(-ENOMEM); + + memcpy(id, signer, signer_len); + id[signer_len + 0] = ':'; + id[signer_len + 1] = ' '; + memcpy(id + signer_len + 2, authority, auth_len); + id[signer_len + 2 + auth_len] = 0; + + pr_debug("Look up: \"%s\"\n", id); + + key = keyring_search(make_key_ref(keyring, 1), + &key_type_asymmetric, id); + if (IS_ERR(key)) + pr_debug("Request for module key '%s' err %ld\n", + id, PTR_ERR(key)); + kfree(id); + + if (IS_ERR(key)) { + switch (PTR_ERR(key)) { + /* Hide some search errors */ + case -EACCES: + case -ENOTDIR: + case -EAGAIN: + return ERR_PTR(-ENOKEY); + default: + return ERR_CAST(key); + } + } + + pr_devel("<==%s() = 0 [%x]\n", __func__, + key_serial(key_ref_to_ptr(key))); + return key_ref_to_ptr(key); +} + /* * Set up the signature parameters in an X.509 certificate. This involves * digesting the signed data and extracting the signature. @@ -102,6 +152,37 @@ int x509_check_signature(const struct public_key *pub, } EXPORT_SYMBOL_GPL(x509_check_signature); +/* + * Check the new certificate against the ones in the trust keyring. If one of + * those is the signing key and validates the new certificate, then mark the + * new certificate as being trusted. + * + * Return 0 if the new certificate was successfully validated, 1 if we couldn't + * find a matching parent certificate in the trusted list and an error if there + * is a matching certificate but the signature check fails. + */ +static int x509_validate_trust(struct x509_certificate *cert, + struct key *trust_keyring) +{ + const struct public_key *pk; + struct key *key; + int ret = 1; + + if (!trust_keyring) + return -EOPNOTSUPP; + + key = x509_request_asymmetric_key(trust_keyring, + cert->issuer, strlen(cert->issuer), + cert->authority, + strlen(cert->authority)); + if (!IS_ERR(key)) { + pk = key->payload.data; + ret = x509_check_signature(pk, cert); + key_put(key); + } + return ret; +} + /* * Attempt to parse a data blob for a key as an X509 certificate. */ @@ -155,9 +236,13 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) /* Check the signature on the key if it appears to be self-signed */ if (!cert->authority || strcmp(cert->fingerprint, cert->authority) == 0) { - ret = x509_check_signature(cert->pub, cert); + ret = x509_check_signature(cert->pub, cert); /* self-signed */ if (ret < 0) goto error_free_cert; + } else if (!prep->trusted) { + ret = x509_validate_trust(cert, get_system_trusted_keyring()); + if (!ret) + prep->trusted = 1; } /* Propose a description */ diff --git a/include/keys/system_keyring.h b/include/keys/system_keyring.h index 8dabc399bd1d..72665eb80692 100644 --- a/include/keys/system_keyring.h +++ b/include/keys/system_keyring.h @@ -17,7 +17,15 @@ #include extern struct key *system_trusted_keyring; - +static inline struct key *get_system_trusted_keyring(void) +{ + return system_trusted_keyring; +} +#else +static inline struct key *get_system_trusted_keyring(void) +{ + return NULL; +} #endif #endif /* _KEYS_SYSTEM_KEYRING_H */ -- cgit v1.2.3-59-g8ed1b From 32c4741cb66703a3c282f41d77deff4afd93342a Mon Sep 17 00:00:00 2001 From: Dmitry Kasatkin Date: Tue, 17 Jun 2014 11:56:59 +0300 Subject: KEYS: validate certificate trust only with builtin keys Instead of allowing public keys, with certificates signed by any key on the system trusted keyring, to be added to a trusted keyring, this patch further restricts the certificates to those signed only by builtin keys on the system keyring. This patch defines a new option 'builtin' for the kernel parameter 'keys_ownerid' to allow trust validation using builtin keys. Simplified Mimi's "KEYS: define an owner trusted keyring" patch Changelog v7: - rename builtin_keys to use_builtin_keys Signed-off-by: Dmitry Kasatkin Signed-off-by: Mimi Zohar --- Documentation/kernel-parameters.txt | 2 +- crypto/asymmetric_keys/x509_public_key.c | 9 ++++++--- include/linux/key.h | 1 + kernel/system_keyring.c | 1 + 4 files changed, 9 insertions(+), 4 deletions(-) (limited to 'include') diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index bdb193afe176..90c12c591168 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -569,7 +569,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted. ca_keys= [KEYS] This parameter identifies a specific key(s) on the system trusted keyring to be used for certificate trust validation. - format: id: + format: { id: | builtin } ccw_timeout_log [S390] See Documentation/s390/CommonIO for details. diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index d376195e1d08..927ce755ff67 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -24,6 +24,7 @@ #include "public_key.h" #include "x509_parser.h" +static bool use_builtin_keys; static char *ca_keyid; #ifndef MODULE @@ -34,6 +35,8 @@ static int __init ca_keys_setup(char *str) if (strncmp(str, "id:", 3) == 0) ca_keyid = str; /* owner key 'id:xxxxxx' */ + else if (strcmp(str, "builtin") == 0) + use_builtin_keys = true; return 1; } @@ -180,7 +183,6 @@ EXPORT_SYMBOL_GPL(x509_check_signature); static int x509_validate_trust(struct x509_certificate *cert, struct key *trust_keyring) { - const struct public_key *pk; struct key *key; int ret = 1; @@ -195,8 +197,9 @@ static int x509_validate_trust(struct x509_certificate *cert, cert->authority, strlen(cert->authority)); if (!IS_ERR(key)) { - pk = key->payload.data; - ret = x509_check_signature(pk, cert); + if (!use_builtin_keys + || test_bit(KEY_FLAG_BUILTIN, &key->flags)) + ret = x509_check_signature(key->payload.data, cert); key_put(key); } return ret; diff --git a/include/linux/key.h b/include/linux/key.h index 017b0826642f..65316f7ae794 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -170,6 +170,7 @@ struct key { #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ +#define KEY_FLAG_BUILTIN 10 /* set if key is builtin */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/kernel/system_keyring.c b/kernel/system_keyring.c index 52ebc70263f4..875f64e8935b 100644 --- a/kernel/system_keyring.c +++ b/kernel/system_keyring.c @@ -89,6 +89,7 @@ static __init int load_system_certificate_list(void) pr_err("Problem loading in-kernel X.509 certificate (%ld)\n", PTR_ERR(key)); } else { + set_bit(KEY_FLAG_BUILTIN, &key_ref_to_ptr(key)->flags); pr_notice("Loaded X.509 cert '%s'\n", key_ref_to_ptr(key)->description); key_ref_put(key); -- cgit v1.2.3-59-g8ed1b From 0c7774abb41bd00d5836d9ba098825a40fa94133 Mon Sep 17 00:00:00 2001 From: David Howells Date: Thu, 17 Jul 2014 20:45:08 +0100 Subject: KEYS: Allow special keys (eg. DNS results) to be invalidated by CAP_SYS_ADMIN Special kernel keys, such as those used to hold DNS results for AFS, CIFS and NFS and those used to hold idmapper results for NFS, used to be 'invalidateable' with key_revoke(). However, since the default permissions for keys were reduced: Commit: 96b5c8fea6c0861621051290d705ec2e971963f1 KEYS: Reduce initial permissions on keys it has become impossible to do this. Add a key flag (KEY_FLAG_ROOT_CAN_INVAL) that will permit a key to be invalidated by root. This should not be used for system keyrings as the garbage collector will try and remove any invalidate key. For system keyrings, KEY_FLAG_ROOT_CAN_CLEAR can be used instead. After this, from userspace, keyctl_invalidate() and "keyctl invalidate" can be used by any possessor of CAP_SYS_ADMIN (typically root) to invalidate DNS and idmapper keys. Invalidated keys are immediately garbage collected and will be immediately rerequested if needed again. Signed-off-by: David Howells Tested-by: Steve Dickson --- fs/nfs/idmap.c | 2 ++ include/linux/key.h | 1 + net/dns_resolver/dns_query.c | 1 + security/keys/keyctl.c | 15 ++++++++++++++- 4 files changed, 18 insertions(+), 1 deletion(-) (limited to 'include') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 567983d2c0eb..b7458d77f511 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -282,6 +282,8 @@ static struct key *nfs_idmap_request_key(const char *name, size_t namelen, desc, "", 0, idmap); mutex_unlock(&idmap->idmap_mutex); } + if (!IS_ERR(rkey)) + set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags); kfree(desc); return rkey; diff --git a/include/linux/key.h b/include/linux/key.h index 017b0826642f..e37a4d807185 100644 --- a/include/linux/key.h +++ b/include/linux/key.h @@ -170,6 +170,7 @@ struct key { #define KEY_FLAG_INVALIDATED 7 /* set if key has been invalidated */ #define KEY_FLAG_TRUSTED 8 /* set if key is trusted */ #define KEY_FLAG_TRUSTED_ONLY 9 /* set if keyring only accepts links to trusted keys */ +#define KEY_FLAG_ROOT_CAN_INVAL 11 /* set if key can be invalidated by root without permission */ /* the key type and key description string * - the desc is used to match a key against search criteria diff --git a/net/dns_resolver/dns_query.c b/net/dns_resolver/dns_query.c index 9acec61f5433..9a32f55cf9b9 100644 --- a/net/dns_resolver/dns_query.c +++ b/net/dns_resolver/dns_query.c @@ -129,6 +129,7 @@ int dns_query(const char *type, const char *name, size_t namelen, } down_read(&rkey->sem); + set_bit(KEY_FLAG_ROOT_CAN_INVAL, &rkey->flags); rkey->perm |= KEY_USR_VIEW; ret = key_validate(rkey); diff --git a/security/keys/keyctl.c b/security/keys/keyctl.c index cd5bd0cef25d..609f8d326ddc 100644 --- a/security/keys/keyctl.c +++ b/security/keys/keyctl.c @@ -404,12 +404,25 @@ long keyctl_invalidate_key(key_serial_t id) key_ref = lookup_user_key(id, 0, KEY_NEED_SEARCH); if (IS_ERR(key_ref)) { ret = PTR_ERR(key_ref); + + /* Root is permitted to invalidate certain special keys */ + if (capable(CAP_SYS_ADMIN)) { + key_ref = lookup_user_key(id, 0, 0); + if (IS_ERR(key_ref)) + goto error; + if (test_bit(KEY_FLAG_ROOT_CAN_INVAL, + &key_ref_to_ptr(key_ref)->flags)) + goto invalidate; + goto error_put; + } + goto error; } +invalidate: key_invalidate(key_ref_to_ptr(key_ref)); ret = 0; - +error_put: key_ref_put(key_ref); error: kleave(" = %ld", ret); -- cgit v1.2.3-59-g8ed1b From 6a09d17bb66a533c165be81e8a4c3557f68e1a3b Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Provide a generic instantiation function Provide a generic instantiation function for key types that use the preparse hook. This makes it easier to prereserve key quota before keyrings get locked to retain the new key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- crypto/asymmetric_keys/asymmetric_type.c | 25 +------------------------ include/linux/key-type.h | 2 ++ security/keys/key.c | 30 ++++++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 24 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index b77eb5304788..c1fe0fcee8e3 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -163,29 +163,6 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) kfree(prep->description); } -/* - * Instantiate a asymmetric_key defined key. The key was preparsed, so we just - * have to transfer the data here. - */ -static int asymmetric_key_instantiate(struct key *key, struct key_preparsed_payload *prep) -{ - int ret; - - pr_devel("==>%s()\n", __func__); - - 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]; - key->payload.data = prep->payload; - prep->type_data[0] = NULL; - prep->type_data[1] = NULL; - prep->payload = NULL; - } - pr_devel("<==%s() = %d\n", __func__, ret); - return ret; -} - /* * dispose of the data dangling from the corpse of a asymmetric key */ @@ -205,7 +182,7 @@ struct key_type key_type_asymmetric = { .name = "asymmetric", .preparse = asymmetric_key_preparse, .free_preparse = asymmetric_key_free_preparse, - .instantiate = asymmetric_key_instantiate, + .instantiate = generic_key_instantiate, .match = asymmetric_key_match, .destroy = asymmetric_key_destroy, .describe = asymmetric_key_describe, diff --git a/include/linux/key-type.h b/include/linux/key-type.h index a74c3a84dfdd..88503dca2a57 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -159,5 +159,7 @@ static inline int key_negate_and_link(struct key *key, return key_reject_and_link(key, timeout, ENOKEY, keyring, instkey); } +extern int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep); + #endif /* CONFIG_KEYS */ #endif /* _LINUX_KEY_TYPE_H */ diff --git a/security/keys/key.c b/security/keys/key.c index 2048a110e7f1..7c9acbf106b6 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1023,6 +1023,36 @@ void key_invalidate(struct key *key) } EXPORT_SYMBOL(key_invalidate); +/** + * generic_key_instantiate - Simple instantiation of a key from preparsed data + * @key: The key to be instantiated + * @prep: The preparsed data to load. + * + * Instantiate a key from preparsed data. We assume we can just copy the data + * in directly and clear the old pointers. + * + * This can be pointed to directly by the key type instantiate op pointer. + */ +int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +{ + int ret; + + pr_devel("==>%s()\n", __func__); + + 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); + prep->type_data[0] = NULL; + prep->type_data[1] = NULL; + prep->payload = NULL; + } + pr_devel("<==%s() = %d\n", __func__, ret); + return ret; +} +EXPORT_SYMBOL(generic_key_instantiate); + /** * register_key_type - Register a type of key. * @ktype: The new key type. -- cgit v1.2.3-59-g8ed1b From 48dc92b9fc3926844257316e75ba11eb5c742b2c Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 25 Jun 2014 16:08:24 -0700 Subject: seccomp: add "seccomp" syscall This adds the new "seccomp" syscall with both an "operation" and "flags" parameter for future expansion. The third argument is a pointer value, used with the SECCOMP_SET_MODE_FILTER operation. Currently, flags must be 0. This is functionally equivalent to prctl(PR_SET_SECCOMP, ...). In addition to the TSYNC flag later in this patch series, there is a non-zero chance that this syscall could be used for configuring a fixed argument area for seccomp-tracer-aware processes to pass syscall arguments in the future. Hence, the use of "seccomp" not simply "seccomp_add_filter" for this syscall. Additionally, this syscall uses operation, flags, and user pointer for arguments because strictly passing arguments via a user pointer would mean seccomp itself would be unable to trivially filter the seccomp syscall itself. Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- arch/Kconfig | 1 + arch/x86/syscalls/syscall_32.tbl | 1 + arch/x86/syscalls/syscall_64.tbl | 1 + include/linux/syscalls.h | 2 ++ include/uapi/asm-generic/unistd.h | 4 ++- include/uapi/linux/seccomp.h | 4 +++ kernel/seccomp.c | 55 +++++++++++++++++++++++++++++++++++---- kernel/sys_ni.c | 3 +++ 8 files changed, 65 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/arch/Kconfig b/arch/Kconfig index 97ff872c7acc..0eae9df35b88 100644 --- a/arch/Kconfig +++ b/arch/Kconfig @@ -321,6 +321,7 @@ config HAVE_ARCH_SECCOMP_FILTER - secure_computing is called from a ptrace_event()-safe context - secure_computing return value is checked and a return value of -1 results in the system call being skipped immediately. + - seccomp syscall wired up config SECCOMP_FILTER def_bool y diff --git a/arch/x86/syscalls/syscall_32.tbl b/arch/x86/syscalls/syscall_32.tbl index d6b867921612..7527eac24122 100644 --- a/arch/x86/syscalls/syscall_32.tbl +++ b/arch/x86/syscalls/syscall_32.tbl @@ -360,3 +360,4 @@ 351 i386 sched_setattr sys_sched_setattr 352 i386 sched_getattr sys_sched_getattr 353 i386 renameat2 sys_renameat2 +354 i386 seccomp sys_seccomp diff --git a/arch/x86/syscalls/syscall_64.tbl b/arch/x86/syscalls/syscall_64.tbl index ec255a1646d2..16272a6c12b7 100644 --- a/arch/x86/syscalls/syscall_64.tbl +++ b/arch/x86/syscalls/syscall_64.tbl @@ -323,6 +323,7 @@ 314 common sched_setattr sys_sched_setattr 315 common sched_getattr sys_sched_getattr 316 common renameat2 sys_renameat2 +317 common seccomp sys_seccomp # # x32-specific system call numbers start at 512 to avoid cache impact diff --git a/include/linux/syscalls.h b/include/linux/syscalls.h index b0881a0ed322..1713977ee26f 100644 --- a/include/linux/syscalls.h +++ b/include/linux/syscalls.h @@ -866,4 +866,6 @@ asmlinkage long sys_process_vm_writev(pid_t pid, asmlinkage long sys_kcmp(pid_t pid1, pid_t pid2, int type, unsigned long idx1, unsigned long idx2); asmlinkage long sys_finit_module(int fd, const char __user *uargs, int flags); +asmlinkage long sys_seccomp(unsigned int op, unsigned int flags, + const char __user *uargs); #endif diff --git a/include/uapi/asm-generic/unistd.h b/include/uapi/asm-generic/unistd.h index 333640608087..65acbf0e2867 100644 --- a/include/uapi/asm-generic/unistd.h +++ b/include/uapi/asm-generic/unistd.h @@ -699,9 +699,11 @@ __SYSCALL(__NR_sched_setattr, sys_sched_setattr) __SYSCALL(__NR_sched_getattr, sys_sched_getattr) #define __NR_renameat2 276 __SYSCALL(__NR_renameat2, sys_renameat2) +#define __NR_seccomp 277 +__SYSCALL(__NR_seccomp, sys_seccomp) #undef __NR_syscalls -#define __NR_syscalls 277 +#define __NR_syscalls 278 /* * All syscalls below here should go away really, diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h index ac2dc9f72973..b258878ba754 100644 --- a/include/uapi/linux/seccomp.h +++ b/include/uapi/linux/seccomp.h @@ -10,6 +10,10 @@ #define SECCOMP_MODE_STRICT 1 /* uses hard-coded filter. */ #define SECCOMP_MODE_FILTER 2 /* uses user-supplied filter. */ +/* Valid operations for seccomp syscall. */ +#define SECCOMP_SET_MODE_STRICT 0 +#define SECCOMP_SET_MODE_FILTER 1 + /* * All BPF programs must return a 32-bit value. * The bottom 16-bits are for optional return data. diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 05cac2c2eca1..f0652578af75 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -18,6 +18,7 @@ #include #include #include +#include /* #define SECCOMP_DEBUG 1 */ @@ -314,7 +315,7 @@ free_prog: * * Returns 0 on success and non-zero otherwise. */ -static long seccomp_attach_user_filter(char __user *user_filter) +static long seccomp_attach_user_filter(const char __user *user_filter) { struct sock_fprog fprog; long ret = -EFAULT; @@ -517,6 +518,7 @@ out: #ifdef CONFIG_SECCOMP_FILTER /** * seccomp_set_mode_filter: internal function for setting seccomp filter + * @flags: flags to change filter behavior * @filter: struct sock_fprog containing filter * * This function may be called repeatedly to install additional filters. @@ -527,11 +529,16 @@ out: * * Returns 0 on success or -EINVAL on failure. */ -static long seccomp_set_mode_filter(char __user *filter) +static long seccomp_set_mode_filter(unsigned int flags, + const char __user *filter) { const unsigned long seccomp_mode = SECCOMP_MODE_FILTER; long ret = -EINVAL; + /* Validate flags. */ + if (flags != 0) + goto out; + if (!seccomp_may_assign_mode(seccomp_mode)) goto out; @@ -544,12 +551,35 @@ out: return ret; } #else -static inline long seccomp_set_mode_filter(char __user *filter) +static inline long seccomp_set_mode_filter(unsigned int flags, + const char __user *filter) { return -EINVAL; } #endif +/* Common entry point for both prctl and syscall. */ +static long do_seccomp(unsigned int op, unsigned int flags, + const char __user *uargs) +{ + switch (op) { + case SECCOMP_SET_MODE_STRICT: + if (flags != 0 || uargs != NULL) + return -EINVAL; + return seccomp_set_mode_strict(); + case SECCOMP_SET_MODE_FILTER: + return seccomp_set_mode_filter(flags, uargs); + default: + return -EINVAL; + } +} + +SYSCALL_DEFINE3(seccomp, unsigned int, op, unsigned int, flags, + const char __user *, uargs) +{ + return do_seccomp(op, flags, uargs); +} + /** * prctl_set_seccomp: configures current->seccomp.mode * @seccomp_mode: requested mode to use @@ -559,12 +589,27 @@ static inline long seccomp_set_mode_filter(char __user *filter) */ long prctl_set_seccomp(unsigned long seccomp_mode, char __user *filter) { + unsigned int op; + char __user *uargs; + switch (seccomp_mode) { case SECCOMP_MODE_STRICT: - return seccomp_set_mode_strict(); + op = SECCOMP_SET_MODE_STRICT; + /* + * Setting strict mode through prctl always ignored filter, + * so make sure it is always NULL here to pass the internal + * check in do_seccomp(). + */ + uargs = NULL; + break; case SECCOMP_MODE_FILTER: - return seccomp_set_mode_filter(filter); + op = SECCOMP_SET_MODE_FILTER; + uargs = filter; + break; default: return -EINVAL; } + + /* prctl interface doesn't have flags, so they are always zero. */ + return do_seccomp(op, 0, uargs); } diff --git a/kernel/sys_ni.c b/kernel/sys_ni.c index 36441b51b5df..2904a2105914 100644 --- a/kernel/sys_ni.c +++ b/kernel/sys_ni.c @@ -213,3 +213,6 @@ cond_syscall(compat_sys_open_by_handle_at); /* compare kernel pointers */ cond_syscall(sys_kcmp); + +/* operate on Secure Computing state */ +cond_syscall(sys_seccomp); -- cgit v1.2.3-59-g8ed1b From 1d4457f99928a968767f6405b4a1f50845aa15fd Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Wed, 21 May 2014 15:23:46 -0700 Subject: sched: move no_new_privs into new atomic flags Since seccomp transitions between threads requires updates to the no_new_privs flag to be atomic, the flag must be part of an atomic flag set. This moves the nnp flag into a separate task field, and introduces accessors. Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- fs/exec.c | 4 ++-- include/linux/sched.h | 18 +++++++++++++++--- kernel/seccomp.c | 2 +- kernel/sys.c | 4 ++-- security/apparmor/domain.c | 4 ++-- 5 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index a3d33fe592d6..0f5c272410f6 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1234,7 +1234,7 @@ static void check_unsafe_exec(struct linux_binprm *bprm) * This isn't strictly necessary, but it makes it harder for LSMs to * mess up. */ - if (current->no_new_privs) + if (task_no_new_privs(current)) bprm->unsafe |= LSM_UNSAFE_NO_NEW_PRIVS; t = p; @@ -1272,7 +1272,7 @@ int prepare_binprm(struct linux_binprm *bprm) bprm->cred->egid = current_egid(); if (!(bprm->file->f_path.mnt->mnt_flags & MNT_NOSUID) && - !current->no_new_privs && + !task_no_new_privs(current) && kuid_has_mapping(bprm->cred->user_ns, inode->i_uid) && kgid_has_mapping(bprm->cred->user_ns, inode->i_gid)) { /* Set-uid? */ diff --git a/include/linux/sched.h b/include/linux/sched.h index 306f4f0c987a..0fd19055bb64 100644 --- a/include/linux/sched.h +++ b/include/linux/sched.h @@ -1307,13 +1307,12 @@ struct task_struct { * execve */ unsigned in_iowait:1; - /* task may not gain privileges */ - unsigned no_new_privs:1; - /* Revert to default priority/policy when forking */ unsigned sched_reset_on_fork:1; unsigned sched_contributes_to_load:1; + unsigned long atomic_flags; /* Flags needing atomic access. */ + pid_t pid; pid_t tgid; @@ -1967,6 +1966,19 @@ static inline void memalloc_noio_restore(unsigned int flags) current->flags = (current->flags & ~PF_MEMALLOC_NOIO) | flags; } +/* Per-process atomic flags. */ +#define PFA_NO_NEW_PRIVS 0x00000001 /* May not gain new privileges. */ + +static inline bool task_no_new_privs(struct task_struct *p) +{ + return test_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags); +} + +static inline void task_set_no_new_privs(struct task_struct *p) +{ + set_bit(PFA_NO_NEW_PRIVS, &p->atomic_flags); +} + /* * task->jobctl flags */ diff --git a/kernel/seccomp.c b/kernel/seccomp.c index f0652578af75..d2596136b0d1 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -241,7 +241,7 @@ static long seccomp_attach_filter(struct sock_fprog *fprog) * This avoids scenarios where unprivileged tasks can affect the * behavior of privileged children. */ - if (!current->no_new_privs && + if (!task_no_new_privs(current) && security_capable_noaudit(current_cred(), current_user_ns(), CAP_SYS_ADMIN) != 0) return -EACCES; diff --git a/kernel/sys.c b/kernel/sys.c index 66a751ebf9d9..ce8129192a26 100644 --- a/kernel/sys.c +++ b/kernel/sys.c @@ -1990,12 +1990,12 @@ SYSCALL_DEFINE5(prctl, int, option, unsigned long, arg2, unsigned long, arg3, if (arg2 != 1 || arg3 || arg4 || arg5) return -EINVAL; - current->no_new_privs = 1; + task_set_no_new_privs(current); break; case PR_GET_NO_NEW_PRIVS: if (arg2 || arg3 || arg4 || arg5) return -EINVAL; - return current->no_new_privs ? 1 : 0; + return task_no_new_privs(current) ? 1 : 0; case PR_GET_THP_DISABLE: if (arg2 || arg3 || arg4 || arg5) return -EINVAL; diff --git a/security/apparmor/domain.c b/security/apparmor/domain.c index 452567d3a08e..d97cba3e3849 100644 --- a/security/apparmor/domain.c +++ b/security/apparmor/domain.c @@ -621,7 +621,7 @@ int aa_change_hat(const char *hats[], int count, u64 token, bool permtest) * There is no exception for unconfined as change_hat is not * available. */ - if (current->no_new_privs) + if (task_no_new_privs(current)) return -EPERM; /* released below */ @@ -776,7 +776,7 @@ int aa_change_profile(const char *ns_name, const char *hname, bool onexec, * no_new_privs is set because this aways results in a reduction * of permissions. */ - if (current->no_new_privs && !unconfined(profile)) { + if (task_no_new_privs(current) && !unconfined(profile)) { put_cred(cred); return -EPERM; } -- cgit v1.2.3-59-g8ed1b From dbd952127d11bb44a4ea30b08cc60531b6a23d71 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Fri, 27 Jun 2014 15:18:48 -0700 Subject: seccomp: introduce writer locking Normally, task_struct.seccomp.filter is only ever read or modified by the task that owns it (current). This property aids in fast access during system call filtering as read access is lockless. Updating the pointer from another task, however, opens up race conditions. To allow cross-thread filter pointer updates, writes to the seccomp fields are now protected by the sighand spinlock (which is shared by all threads in the thread group). Read access remains lockless because pointer updates themselves are atomic. However, writes (or cloning) often entail additional checking (like maximum instruction counts) which require locking to perform safely. In the case of cloning threads, the child is invisible to the system until it enters the task list. To make sure a child can't be cloned from a thread and left in a prior state, seccomp duplication is additionally moved under the sighand lock. Then parent and child are certain have the same seccomp state when they exit the lock. Based on patches by Will Drewry and David Drysdale. Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- include/linux/seccomp.h | 6 +++--- kernel/fork.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++++- kernel/seccomp.c | 16 +++++++++++++++- 3 files changed, 66 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 4054b0994071..9ff98b4bfe2e 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -14,11 +14,11 @@ struct seccomp_filter; * * @mode: indicates one of the valid values above for controlled * system calls available to a process. - * @filter: The metadata and ruleset for determining what system calls - * are allowed for a task. + * @filter: must always point to a valid seccomp-filter or NULL as it is + * accessed without locking during system call entry. * * @filter must only be accessed from the context of current as there - * is no locking. + * is no read locking. */ struct seccomp { int mode; diff --git a/kernel/fork.c b/kernel/fork.c index 6a13c46cd87d..ed4bc339c9dc 100644 --- a/kernel/fork.c +++ b/kernel/fork.c @@ -315,6 +315,15 @@ static struct task_struct *dup_task_struct(struct task_struct *orig) goto free_ti; tsk->stack = ti; +#ifdef CONFIG_SECCOMP + /* + * We must handle setting up seccomp filters once we're under + * the sighand lock in case orig has changed between now and + * then. Until then, filter must be NULL to avoid messing up + * the usage counts on the error path calling free_task. + */ + tsk->seccomp.filter = NULL; +#endif setup_thread_stack(tsk, orig); clear_user_return_notifier(tsk); @@ -1081,6 +1090,39 @@ static int copy_signal(unsigned long clone_flags, struct task_struct *tsk) return 0; } +static void copy_seccomp(struct task_struct *p) +{ +#ifdef CONFIG_SECCOMP + /* + * Must be called with sighand->lock held, which is common to + * all threads in the group. Holding cred_guard_mutex is not + * needed because this new task is not yet running and cannot + * be racing exec. + */ + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + + /* Ref-count the new filter user, and assign it. */ + get_seccomp_filter(current); + p->seccomp = current->seccomp; + + /* + * Explicitly enable no_new_privs here in case it got set + * between the task_struct being duplicated and holding the + * sighand lock. The seccomp state and nnp must be in sync. + */ + if (task_no_new_privs(current)) + task_set_no_new_privs(p); + + /* + * If the parent gained a seccomp mode after copying thread + * flags and between before we held the sighand lock, we have + * to manually enable the seccomp thread flag here. + */ + if (p->seccomp.mode != SECCOMP_MODE_DISABLED) + set_tsk_thread_flag(p, TIF_SECCOMP); +#endif +} + SYSCALL_DEFINE1(set_tid_address, int __user *, tidptr) { current->clear_child_tid = tidptr; @@ -1196,7 +1238,6 @@ static struct task_struct *copy_process(unsigned long clone_flags, goto fork_out; ftrace_graph_init_task(p); - get_seccomp_filter(p); rt_mutex_init_task(p); @@ -1436,6 +1477,12 @@ static struct task_struct *copy_process(unsigned long clone_flags, spin_lock(¤t->sighand->siglock); + /* + * Copy seccomp details explicitly here, in case they were changed + * before holding sighand lock. + */ + copy_seccomp(p); + /* * Process group and session signals need to be delivered to just the * parent before the fork or both the parent and the child after the diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 58125160417c..d5543e787e4e 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -199,6 +199,8 @@ static u32 seccomp_run_filters(int syscall) static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) { + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + if (current->seccomp.mode && current->seccomp.mode != seccomp_mode) return false; @@ -207,6 +209,8 @@ static inline bool seccomp_may_assign_mode(unsigned long seccomp_mode) static inline void seccomp_assign_mode(unsigned long seccomp_mode) { + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + current->seccomp.mode = seccomp_mode; set_tsk_thread_flag(current, TIF_SECCOMP); } @@ -332,6 +336,8 @@ out: * @flags: flags to change filter behavior * @filter: seccomp filter to add to the current process * + * Caller must be holding current->sighand->siglock lock. + * * Returns 0 on success, -ve on error. */ static long seccomp_attach_filter(unsigned int flags, @@ -340,6 +346,8 @@ static long seccomp_attach_filter(unsigned int flags, unsigned long total_insns; struct seccomp_filter *walker; + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + /* Validate resulting filter length. */ total_insns = filter->prog->len; for (walker = current->seccomp.filter; walker; walker = walker->prev) @@ -529,6 +537,8 @@ static long seccomp_set_mode_strict(void) const unsigned long seccomp_mode = SECCOMP_MODE_STRICT; long ret = -EINVAL; + spin_lock_irq(¤t->sighand->siglock); + if (!seccomp_may_assign_mode(seccomp_mode)) goto out; @@ -539,6 +549,7 @@ static long seccomp_set_mode_strict(void) ret = 0; out: + spin_unlock_irq(¤t->sighand->siglock); return ret; } @@ -566,13 +577,15 @@ static long seccomp_set_mode_filter(unsigned int flags, /* Validate flags. */ if (flags != 0) - goto out; + return -EINVAL; /* Prepare the new filter before holding any locks. */ prepared = seccomp_prepare_user_filter(filter); if (IS_ERR(prepared)) return PTR_ERR(prepared); + spin_lock_irq(¤t->sighand->siglock); + if (!seccomp_may_assign_mode(seccomp_mode)) goto out; @@ -584,6 +597,7 @@ static long seccomp_set_mode_filter(unsigned int flags, seccomp_assign_mode(seccomp_mode); out: + spin_unlock_irq(¤t->sighand->siglock); seccomp_filter_free(prepared); return ret; } -- cgit v1.2.3-59-g8ed1b From c2e1f2e30daa551db3c670c0ccfeab20a540b9e1 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Thu, 5 Jun 2014 00:23:17 -0700 Subject: seccomp: implement SECCOMP_FILTER_FLAG_TSYNC Applying restrictive seccomp filter programs to large or diverse codebases often requires handling threads which may be started early in the process lifetime (e.g., by code that is linked in). While it is possible to apply permissive programs prior to process start up, it is difficult to further restrict the kernel ABI to those threads after that point. This change adds a new seccomp syscall flag to SECCOMP_SET_MODE_FILTER for synchronizing thread group seccomp filters at filter installation time. When calling seccomp(SECCOMP_SET_MODE_FILTER, SECCOMP_FILTER_FLAG_TSYNC, filter) an attempt will be made to synchronize all threads in current's threadgroup to its new seccomp filter program. This is possible iff all threads are using a filter that is an ancestor to the filter current is attempting to synchronize to. NULL filters (where the task is running as SECCOMP_MODE_NONE) are also treated as ancestors allowing threads to be transitioned into SECCOMP_MODE_FILTER. If prctrl(PR_SET_NO_NEW_PRIVS, ...) has been set on the calling thread, no_new_privs will be set for all synchronized threads too. On success, 0 is returned. On failure, the pid of one of the failing threads will be returned and no filters will have been applied. The race conditions against another thread are: - requesting TSYNC (already handled by sighand lock) - performing a clone (already handled by sighand lock) - changing its filter (already handled by sighand lock) - calling exec (handled by cred_guard_mutex) The clone case is assisted by the fact that new threads will have their seccomp state duplicated from their parent before appearing on the tasklist. Holding cred_guard_mutex means that seccomp filters cannot be assigned while in the middle of another thread's exec (potentially bypassing no_new_privs or similar). The call to de_thread() may kill threads waiting for the mutex. Changes across threads to the filter pointer includes a barrier. Based on patches by Will Drewry. Suggested-by: Julien Tinnes Signed-off-by: Kees Cook Reviewed-by: Oleg Nesterov Reviewed-by: Andy Lutomirski --- fs/exec.c | 2 +- include/linux/seccomp.h | 2 + include/uapi/linux/seccomp.h | 3 + kernel/seccomp.c | 135 ++++++++++++++++++++++++++++++++++++++++++- 4 files changed, 140 insertions(+), 2 deletions(-) (limited to 'include') diff --git a/fs/exec.c b/fs/exec.c index 0f5c272410f6..ab1f1200ce5d 100644 --- a/fs/exec.c +++ b/fs/exec.c @@ -1216,7 +1216,7 @@ EXPORT_SYMBOL(install_exec_creds); /* * determine how safe it is to execute the proposed program * - the caller must hold ->cred_guard_mutex to protect against - * PTRACE_ATTACH + * PTRACE_ATTACH or seccomp thread-sync */ static void check_unsafe_exec(struct linux_binprm *bprm) { diff --git a/include/linux/seccomp.h b/include/linux/seccomp.h index 9ff98b4bfe2e..5d586a45a319 100644 --- a/include/linux/seccomp.h +++ b/include/linux/seccomp.h @@ -3,6 +3,8 @@ #include +#define SECCOMP_FILTER_FLAG_MASK (SECCOMP_FILTER_FLAG_TSYNC) + #ifdef CONFIG_SECCOMP #include diff --git a/include/uapi/linux/seccomp.h b/include/uapi/linux/seccomp.h index b258878ba754..0f238a43ff1e 100644 --- a/include/uapi/linux/seccomp.h +++ b/include/uapi/linux/seccomp.h @@ -14,6 +14,9 @@ #define SECCOMP_SET_MODE_STRICT 0 #define SECCOMP_SET_MODE_FILTER 1 +/* Valid flags for SECCOMP_SET_MODE_FILTER */ +#define SECCOMP_FILTER_FLAG_TSYNC 1 + /* * All BPF programs must return a 32-bit value. * The bottom 16-bits are for optional return data. diff --git a/kernel/seccomp.c b/kernel/seccomp.c index 9065d2c79c56..74f460179171 100644 --- a/kernel/seccomp.c +++ b/kernel/seccomp.c @@ -26,6 +26,7 @@ #ifdef CONFIG_SECCOMP_FILTER #include #include +#include #include #include #include @@ -225,6 +226,114 @@ static inline void seccomp_assign_mode(struct task_struct *task, } #ifdef CONFIG_SECCOMP_FILTER +/* Returns 1 if the parent is an ancestor of the child. */ +static int is_ancestor(struct seccomp_filter *parent, + struct seccomp_filter *child) +{ + /* NULL is the root ancestor. */ + if (parent == NULL) + return 1; + for (; child; child = child->prev) + if (child == parent) + return 1; + return 0; +} + +/** + * seccomp_can_sync_threads: checks if all threads can be synchronized + * + * Expects sighand and cred_guard_mutex locks to be held. + * + * Returns 0 on success, -ve on error, or the pid of a thread which was + * either not in the correct seccomp mode or it did not have an ancestral + * seccomp filter. + */ +static inline pid_t seccomp_can_sync_threads(void) +{ + struct task_struct *thread, *caller; + + BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + + /* Validate all threads being eligible for synchronization. */ + caller = current; + for_each_thread(caller, thread) { + pid_t failed; + + /* Skip current, since it is initiating the sync. */ + if (thread == caller) + continue; + + if (thread->seccomp.mode == SECCOMP_MODE_DISABLED || + (thread->seccomp.mode == SECCOMP_MODE_FILTER && + is_ancestor(thread->seccomp.filter, + caller->seccomp.filter))) + continue; + + /* Return the first thread that cannot be synchronized. */ + failed = task_pid_vnr(thread); + /* If the pid cannot be resolved, then return -ESRCH */ + if (unlikely(WARN_ON(failed == 0))) + failed = -ESRCH; + return failed; + } + + return 0; +} + +/** + * seccomp_sync_threads: sets all threads to use current's filter + * + * Expects sighand and cred_guard_mutex locks to be held, and for + * seccomp_can_sync_threads() to have returned success already + * without dropping the locks. + * + */ +static inline void seccomp_sync_threads(void) +{ + struct task_struct *thread, *caller; + + BUG_ON(!mutex_is_locked(¤t->signal->cred_guard_mutex)); + BUG_ON(!spin_is_locked(¤t->sighand->siglock)); + + /* Synchronize all threads. */ + caller = current; + for_each_thread(caller, thread) { + /* Skip current, since it needs no changes. */ + if (thread == caller) + continue; + + /* Get a task reference for the new leaf node. */ + get_seccomp_filter(caller); + /* + * Drop the task reference to the shared ancestor since + * current's path will hold a reference. (This also + * allows a put before the assignment.) + */ + put_seccomp_filter(thread); + smp_store_release(&thread->seccomp.filter, + caller->seccomp.filter); + /* + * Opt the other thread into seccomp if needed. + * As threads are considered to be trust-realm + * equivalent (see ptrace_may_access), it is safe to + * allow one thread to transition the other. + */ + if (thread->seccomp.mode == SECCOMP_MODE_DISABLED) { + /* + * Don't let an unprivileged task work around + * the no_new_privs restriction by creating + * a thread that sets it up, enters seccomp, + * then dies. + */ + if (task_no_new_privs(caller)) + task_set_no_new_privs(thread); + + seccomp_assign_mode(thread, SECCOMP_MODE_FILTER); + } + } +} + /** * seccomp_prepare_filter: Prepares a seccomp filter for use. * @fprog: BPF program to install @@ -364,6 +473,15 @@ static long seccomp_attach_filter(unsigned int flags, if (total_insns > MAX_INSNS_PER_PATH) return -ENOMEM; + /* If thread sync has been requested, check that it is possible. */ + if (flags & SECCOMP_FILTER_FLAG_TSYNC) { + int ret; + + ret = seccomp_can_sync_threads(); + if (ret) + return ret; + } + /* * If there is an existing filter, make it the prev and don't drop its * task reference. @@ -371,6 +489,10 @@ static long seccomp_attach_filter(unsigned int flags, filter->prev = current->seccomp.filter; current->seccomp.filter = filter; + /* Now that the new filter is in place, synchronize to all threads. */ + if (flags & SECCOMP_FILTER_FLAG_TSYNC) + seccomp_sync_threads(); + return 0; } @@ -590,7 +712,7 @@ static long seccomp_set_mode_filter(unsigned int flags, long ret = -EINVAL; /* Validate flags. */ - if (flags != 0) + if (flags & ~SECCOMP_FILTER_FLAG_MASK) return -EINVAL; /* Prepare the new filter before holding any locks. */ @@ -598,6 +720,14 @@ static long seccomp_set_mode_filter(unsigned int flags, if (IS_ERR(prepared)) return PTR_ERR(prepared); + /* + * Make sure we cannot change seccomp or nnp state via TSYNC + * while another thread is in the middle of calling exec. + */ + if (flags & SECCOMP_FILTER_FLAG_TSYNC && + mutex_lock_killable(¤t->signal->cred_guard_mutex)) + goto out_free; + spin_lock_irq(¤t->sighand->siglock); if (!seccomp_may_assign_mode(seccomp_mode)) @@ -612,6 +742,9 @@ static long seccomp_set_mode_filter(unsigned int flags, seccomp_assign_mode(current, seccomp_mode); out: spin_unlock_irq(¤t->sighand->siglock); + if (flags & SECCOMP_FILTER_FLAG_TSYNC) + mutex_unlock(¤t->signal->cred_guard_mutex); +out_free: seccomp_filter_free(prepared); return ret; } -- cgit v1.2.3-59-g8ed1b From fc7c70e0b6b637bbf6cf8b9cee547d5ae83899c9 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: struct key_preparsed_payload should have two payload pointers struct key_preparsed_payload should have two payload pointers to correspond with those in struct key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- crypto/asymmetric_keys/asymmetric_type.c | 2 +- crypto/asymmetric_keys/x509_public_key.c | 2 +- include/linux/key-type.h | 2 +- security/keys/encrypted-keys/encrypted.c | 2 +- security/keys/key.c | 6 ++++-- 5 files changed, 8 insertions(+), 6 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/asymmetric_type.c b/crypto/asymmetric_keys/asymmetric_type.c index c1fe0fcee8e3..21960a4e74e8 100644 --- a/crypto/asymmetric_keys/asymmetric_type.c +++ b/crypto/asymmetric_keys/asymmetric_type.c @@ -156,7 +156,7 @@ static void asymmetric_key_free_preparse(struct key_preparsed_payload *prep) pr_devel("==>%s()\n", __func__); if (subtype) { - subtype->destroy(prep->payload); + subtype->destroy(prep->payload[0]); module_put(subtype->owner); } kfree(prep->type_data[1]); diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 382ef0d2ff2e..3fc8a0634ed7 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -177,7 +177,7 @@ static int x509_key_preparse(struct key_preparsed_payload *prep) __module_get(public_key_subtype.owner); prep->type_data[0] = &public_key_subtype; prep->type_data[1] = cert->fingerprint; - prep->payload = cert->pub; + prep->payload[0] = cert->pub; prep->description = desc; prep->quotalen = 100; diff --git a/include/linux/key-type.h b/include/linux/key-type.h index 88503dca2a57..d2b4845d74bf 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -41,7 +41,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; /* Proposed payload */ + void *payload[2]; /* Proposed payload */ const void *data; /* Raw data */ size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ diff --git a/security/keys/encrypted-keys/encrypted.c b/security/keys/encrypted-keys/encrypted.c index 5fe443d120af..d252c5704f8a 100644 --- a/security/keys/encrypted-keys/encrypted.c +++ b/security/keys/encrypted-keys/encrypted.c @@ -811,7 +811,7 @@ static int encrypted_instantiate(struct key *key, goto out; } - rcu_assign_keypointer(key, epayload); + prep->payload[0] = epayload; out: kfree(datablob); return ret; diff --git a/security/keys/key.c b/security/keys/key.c index 7c9acbf106b6..03620a35a4dc 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -1043,10 +1043,12 @@ int generic_key_instantiate(struct key *key, struct key_preparsed_payload *prep) 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); + 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 = NULL; + prep->payload[0] = NULL; + prep->payload[1] = NULL; } pr_devel("<==%s() = %d\n", __func__, ret); return ret; -- cgit v1.2.3-59-g8ed1b From 7dfa0ca6a95de65b7a7760630cdbd7d30f204bfa Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:34 +0100 Subject: KEYS: Allow expiry time to be set when preparsing a key Allow a key type's preparsing routine to set the expiry time for a key. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton Reviewed-by: Sage Weil --- Documentation/security/keys.txt | 10 +++++++--- include/linux/key-type.h | 1 + security/keys/key.c | 8 ++++++++ 3 files changed, 16 insertions(+), 3 deletions(-) (limited to 'include') diff --git a/Documentation/security/keys.txt b/Documentation/security/keys.txt index a4c33f1a7c6d..315cf96a41a2 100644 --- a/Documentation/security/keys.txt +++ b/Documentation/security/keys.txt @@ -1150,20 +1150,24 @@ The structure has a number of fields, some of which are mandatory: const void *data; size_t datalen; size_t quotalen; + time_t expiry; }; Before calling the method, the caller will fill in data and datalen with the payload blob parameters; quotalen will be filled in with the default - quota size from the key type and the rest will be cleared. + quota size from the key type; expiry will be set to TIME_T_MAX and the + rest will be cleared. If a description can be proposed from the payload contents, that should be 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. + 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 should return 0 if success ful or a negative error code + The method should return 0 if successful or a negative error code otherwise. diff --git a/include/linux/key-type.h b/include/linux/key-type.h index d2b4845d74bf..44792ee649de 100644 --- a/include/linux/key-type.h +++ b/include/linux/key-type.h @@ -45,6 +45,7 @@ struct key_preparsed_payload { const void *data; /* Raw data */ size_t datalen; /* Raw datalen */ size_t quotalen; /* Quota length for proposed payload */ + time_t expiry; /* Expiry time of key */ bool trusted; /* True if key is trusted */ }; diff --git a/security/keys/key.c b/security/keys/key.c index 03620a35a4dc..755fb02df5af 100644 --- a/security/keys/key.c +++ b/security/keys/key.c @@ -437,6 +437,11 @@ static int __key_instantiate_and_link(struct key *key, /* disable the authorisation key */ if (authkey) key_revoke(authkey); + + if (prep->expiry != TIME_T_MAX) { + key->expiry = prep->expiry; + key_schedule_gc(prep->expiry + key_gc_delay); + } } } @@ -479,6 +484,7 @@ int key_instantiate_and_link(struct key *key, prep.data = data; prep.datalen = datalen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) @@ -811,6 +817,7 @@ key_ref_t key_create_or_update(key_ref_t keyring_ref, prep.datalen = plen; prep.quotalen = index_key.type->def_datalen; prep.trusted = flags & KEY_ALLOC_TRUSTED; + prep.expiry = TIME_T_MAX; if (index_key.type->preparse) { ret = index_key.type->preparse(&prep); if (ret < 0) { @@ -941,6 +948,7 @@ int key_update(key_ref_t key_ref, const void *payload, size_t plen) prep.data = payload; prep.datalen = plen; prep.quotalen = key->type->def_datalen; + prep.expiry = TIME_T_MAX; if (key->type->preparse) { ret = key->type->preparse(&prep); if (ret < 0) -- cgit v1.2.3-59-g8ed1b From f9167789df53f22af771fb6690a3d36aa21d74c5 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:35 +0100 Subject: KEYS: user: Use key preparsing Make use of key preparsing in user-defined and logon keys so that quota size determination can take place prior to keyring locking when a key is being added. Also the idmapper key types need to change to match as they use the user-defined key type routines. Signed-off-by: David Howells Acked-by: Steve Dickson Acked-by: Jeff Layton --- fs/nfs/idmap.c | 8 ++++++-- include/keys/user-type.h | 3 ++- security/keys/user_defined.c | 41 ++++++++++++++++++++++------------------- 3 files changed, 30 insertions(+), 22 deletions(-) (limited to 'include') diff --git a/fs/nfs/idmap.c b/fs/nfs/idmap.c index 567983d2c0eb..59b217a3266d 100644 --- a/fs/nfs/idmap.c +++ b/fs/nfs/idmap.c @@ -174,7 +174,9 @@ static int nfs_map_numeric_to_string(__u32 id, char *buf, size_t buflen) static struct key_type key_type_id_resolver = { .name = "id_resolver", - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = user_revoke, .destroy = user_destroy, @@ -394,7 +396,9 @@ static const struct rpc_pipe_ops idmap_upcall_ops = { static struct key_type key_type_id_resolver_legacy = { .name = "id_legacy", - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = user_revoke, .destroy = user_destroy, diff --git a/include/keys/user-type.h b/include/keys/user-type.h index 5e452c84f1e6..3ab1873a4bfa 100644 --- a/include/keys/user-type.h +++ b/include/keys/user-type.h @@ -37,7 +37,8 @@ extern struct key_type key_type_logon; struct key_preparsed_payload; -extern int user_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern int user_preparse(struct key_preparsed_payload *prep); +extern void user_free_preparse(struct key_preparsed_payload *prep); extern int user_update(struct key *key, struct key_preparsed_payload *prep); extern int user_match(const struct key *key, const void *criterion); extern void user_revoke(struct key *key); diff --git a/security/keys/user_defined.c b/security/keys/user_defined.c index faa2caeb593f..eee340011f2b 100644 --- a/security/keys/user_defined.c +++ b/security/keys/user_defined.c @@ -27,7 +27,9 @@ static int logon_vet_description(const char *desc); struct key_type key_type_user = { .name = "user", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -47,7 +49,9 @@ EXPORT_SYMBOL_GPL(key_type_user); struct key_type key_type_logon = { .name = "logon", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = user_instantiate, + .preparse = user_preparse, + .free_preparse = user_free_preparse, + .instantiate = generic_key_instantiate, .update = user_update, .match = user_match, .revoke = user_revoke, @@ -58,38 +62,37 @@ struct key_type key_type_logon = { EXPORT_SYMBOL_GPL(key_type_logon); /* - * instantiate a user defined key + * Preparse a user defined key payload */ -int user_instantiate(struct key *key, struct key_preparsed_payload *prep) +int user_preparse(struct key_preparsed_payload *prep) { struct user_key_payload *upayload; size_t datalen = prep->datalen; - int ret; - ret = -EINVAL; if (datalen <= 0 || datalen > 32767 || !prep->data) - goto error; - - ret = key_payload_reserve(key, datalen); - if (ret < 0) - goto error; + return -EINVAL; - ret = -ENOMEM; upayload = kmalloc(sizeof(*upayload) + datalen, GFP_KERNEL); if (!upayload) - goto error; + return -ENOMEM; /* attach the data */ + prep->quotalen = datalen; + prep->payload[0] = upayload; upayload->datalen = datalen; memcpy(upayload->data, prep->data, datalen); - rcu_assign_keypointer(key, upayload); - ret = 0; - -error: - return ret; + return 0; } +EXPORT_SYMBOL_GPL(user_preparse); -EXPORT_SYMBOL_GPL(user_instantiate); +/* + * Free a preparse of a user defined key payload + */ +void user_free_preparse(struct key_preparsed_payload *prep) +{ + kfree(prep->payload[0]); +} +EXPORT_SYMBOL_GPL(user_free_preparse); /* * update a user defined key -- cgit v1.2.3-59-g8ed1b From 002edaf76f09af658241029817f5ef66f6bef5e4 Mon Sep 17 00:00:00 2001 From: David Howells Date: Fri, 18 Jul 2014 18:56:36 +0100 Subject: KEYS: big_key: Use key preparsing Make use of key preparsing in the big key type so that quota size determination can take place prior to keyring locking when a key is being added. Signed-off-by: David Howells Acked-by: Steve Dickson --- include/keys/big_key-type.h | 3 ++- security/keys/big_key.c | 41 +++++++++++++++++++++++++---------------- 2 files changed, 27 insertions(+), 17 deletions(-) (limited to 'include') diff --git a/include/keys/big_key-type.h b/include/keys/big_key-type.h index d69bc8af3292..e0970a578188 100644 --- a/include/keys/big_key-type.h +++ b/include/keys/big_key-type.h @@ -16,7 +16,8 @@ extern struct key_type key_type_big_key; -extern int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep); +extern int big_key_preparse(struct key_preparsed_payload *prep); +extern void big_key_free_preparse(struct key_preparsed_payload *prep); extern void big_key_revoke(struct key *key); extern void big_key_destroy(struct key *key); extern void big_key_describe(const struct key *big_key, struct seq_file *m); diff --git a/security/keys/big_key.c b/security/keys/big_key.c index 8137b27d641d..c2f91a0cf889 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -34,7 +34,9 @@ MODULE_LICENSE("GPL"); struct key_type key_type_big_key = { .name = "big_key", .def_lookup_type = KEYRING_SEARCH_LOOKUP_DIRECT, - .instantiate = big_key_instantiate, + .preparse = big_key_preparse, + .free_preparse = big_key_free_preparse, + .instantiate = generic_key_instantiate, .match = user_match, .revoke = big_key_revoke, .destroy = big_key_destroy, @@ -43,11 +45,11 @@ struct key_type key_type_big_key = { }; /* - * Instantiate a big key + * Preparse a big key */ -int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) +int big_key_preparse(struct key_preparsed_payload *prep) { - struct path *path = (struct path *)&key->payload.data2; + struct path *path = (struct path *)&prep->payload; struct file *file; ssize_t written; size_t datalen = prep->datalen; @@ -58,11 +60,9 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) goto error; /* Set an arbitrary quota */ - ret = key_payload_reserve(key, 16); - if (ret < 0) - goto error; + prep->quotalen = 16; - key->type_data.x[1] = datalen; + prep->type_data[1] = (void *)(unsigned long)datalen; if (datalen > BIG_KEY_FILE_THRESHOLD) { /* Create a shmem file to store the data in. This will permit the data @@ -73,7 +73,7 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) file = shmem_kernel_file_setup("", datalen, 0); if (IS_ERR(file)) { ret = PTR_ERR(file); - goto err_quota; + goto error; } written = kernel_write(file, prep->data, prep->datalen, 0); @@ -93,23 +93,32 @@ int big_key_instantiate(struct key *key, struct key_preparsed_payload *prep) } else { /* Just store the data in a buffer */ void *data = kmalloc(datalen, GFP_KERNEL); - if (!data) { - ret = -ENOMEM; - goto err_quota; - } + if (!data) + return -ENOMEM; - key->payload.data = memcpy(data, prep->data, prep->datalen); + prep->payload[0] = memcpy(data, prep->data, prep->datalen); } return 0; err_fput: fput(file); -err_quota: - key_payload_reserve(key, 0); error: return ret; } +/* + * Clear preparsement. + */ +void big_key_free_preparse(struct key_preparsed_payload *prep) +{ + if (prep->datalen > BIG_KEY_FILE_THRESHOLD) { + struct path *path = (struct path *)&prep->payload; + path_put(path); + } else { + kfree(prep->payload[0]); + } +} + /* * dispose of the links from a revoked keyring * - called with the key sem write-locked -- cgit v1.2.3-59-g8ed1b From 7d8b6c63751cfbbe5eef81a48c22978b3407a3ad Mon Sep 17 00:00:00 2001 From: Eric Paris Date: Wed, 23 Jul 2014 15:36:26 -0400 Subject: CAPABILITIES: remove undefined caps from all processes This is effectively a revert of 7b9a7ec565505699f503b4fcf61500dceb36e744 plus fixing it a different way... We found, when trying to run an application from an application which had dropped privs that the kernel does security checks on undefined capability bits. This was ESPECIALLY difficult to debug as those undefined bits are hidden from /proc/$PID/status. Consider a root application which drops all capabilities from ALL 4 capability sets. We assume, since the application is going to set eff/perm/inh from an array that it will clear not only the defined caps less than CAP_LAST_CAP, but also the higher 28ish bits which are undefined future capabilities. The BSET gets cleared differently. Instead it is cleared one bit at a time. The problem here is that in security/commoncap.c::cap_task_prctl() we actually check the validity of a capability being read. So any task which attempts to 'read all things set in bset' followed by 'unset all things set in bset' will not even attempt to unset the undefined bits higher than CAP_LAST_CAP. So the 'parent' will look something like: CapInh: 0000000000000000 CapPrm: 0000000000000000 CapEff: 0000000000000000 CapBnd: ffffffc000000000 All of this 'should' be fine. Given that these are undefined bits that aren't supposed to have anything to do with permissions. But they do... So lets now consider a task which cleared the eff/perm/inh completely and cleared all of the valid caps in the bset (but not the invalid caps it couldn't read out of the kernel). We know that this is exactly what the libcap-ng library does and what the go capabilities library does. They both leave you in that above situation if you try to clear all of you capapabilities from all 4 sets. If that root task calls execve() the child task will pick up all caps not blocked by the bset. The bset however does not block bits higher than CAP_LAST_CAP. So now the child task has bits in eff which are not in the parent. These are 'meaningless' undefined bits, but still bits which the parent doesn't have. The problem is now in cred_cap_issubset() (or any operation which does a subset test) as the child, while a subset for valid cap bits, is not a subset for invalid cap bits! So now we set durring commit creds that the child is not dumpable. Given it is 'more priv' than its parent. It also means the parent cannot ptrace the child and other stupidity. The solution here: 1) stop hiding capability bits in status This makes debugging easier! 2) stop giving any task undefined capability bits. it's simple, it you don't put those invalid bits in CAP_FULL_SET you won't get them in init and you won't get them in any other task either. This fixes the cap_issubset() tests and resulting fallout (which made the init task in a docker container untraceable among other things) 3) mask out undefined bits when sys_capset() is called as it might use ~0, ~0 to denote 'all capabilities' for backward/forward compatibility. This lets 'capsh --caps="all=eip" -- -c /bin/bash' run. 4) mask out undefined bit when we read a file capability off of disk as again likely all bits are set in the xattr for forward/backward compatibility. This lets 'setcap all+pe /bin/bash; /bin/bash' run Signed-off-by: Eric Paris Reviewed-by: Kees Cook Cc: Andrew Vagin Cc: Andrew G. Morgan Cc: Serge E. Hallyn Cc: Kees Cook Cc: Steve Grubb Cc: Dan Walsh Cc: stable@vger.kernel.org Signed-off-by: James Morris --- fs/proc/array.c | 11 +---------- include/linux/capability.h | 5 ++++- kernel/audit.c | 2 +- kernel/capability.c | 4 ++++ security/commoncap.c | 3 +++ 5 files changed, 13 insertions(+), 12 deletions(-) (limited to 'include') diff --git a/fs/proc/array.c b/fs/proc/array.c index 64db2bceac59..3e1290b0492e 100644 --- a/fs/proc/array.c +++ b/fs/proc/array.c @@ -297,15 +297,11 @@ static void render_cap_t(struct seq_file *m, const char *header, seq_puts(m, header); CAP_FOR_EACH_U32(__capi) { seq_printf(m, "%08x", - a->cap[(_KERNEL_CAPABILITY_U32S-1) - __capi]); + a->cap[CAP_LAST_U32 - __capi]); } seq_putc(m, '\n'); } -/* Remove non-existent capabilities */ -#define NORM_CAPS(v) (v.cap[CAP_TO_INDEX(CAP_LAST_CAP)] &= \ - CAP_TO_MASK(CAP_LAST_CAP + 1) - 1) - static inline void task_cap(struct seq_file *m, struct task_struct *p) { const struct cred *cred; @@ -319,11 +315,6 @@ static inline void task_cap(struct seq_file *m, struct task_struct *p) cap_bset = cred->cap_bset; rcu_read_unlock(); - NORM_CAPS(cap_inheritable); - NORM_CAPS(cap_permitted); - NORM_CAPS(cap_effective); - NORM_CAPS(cap_bset); - render_cap_t(m, "CapInh:\t", &cap_inheritable); render_cap_t(m, "CapPrm:\t", &cap_permitted); render_cap_t(m, "CapEff:\t", &cap_effective); diff --git a/include/linux/capability.h b/include/linux/capability.h index 84b13ad67c1c..aa93e5ef594c 100644 --- a/include/linux/capability.h +++ b/include/linux/capability.h @@ -78,8 +78,11 @@ extern const kernel_cap_t __cap_init_eff_set; # error Fix up hand-coded capability macro initializers #else /* HAND-CODED capability initializers */ +#define CAP_LAST_U32 ((_KERNEL_CAPABILITY_U32S) - 1) +#define CAP_LAST_U32_VALID_MASK (CAP_TO_MASK(CAP_LAST_CAP + 1) -1) + # define CAP_EMPTY_SET ((kernel_cap_t){{ 0, 0 }}) -# define CAP_FULL_SET ((kernel_cap_t){{ ~0, ~0 }}) +# define CAP_FULL_SET ((kernel_cap_t){{ ~0, CAP_LAST_U32_VALID_MASK }}) # define CAP_FS_SET ((kernel_cap_t){{ CAP_FS_MASK_B0 \ | CAP_TO_MASK(CAP_LINUX_IMMUTABLE), \ CAP_FS_MASK_B1 } }) diff --git a/kernel/audit.c b/kernel/audit.c index 3ef2e0e797e8..ba2ff5a5c600 100644 --- a/kernel/audit.c +++ b/kernel/audit.c @@ -1677,7 +1677,7 @@ void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap) audit_log_format(ab, " %s=", prefix); CAP_FOR_EACH_U32(i) { audit_log_format(ab, "%08x", - cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]); + cap->cap[CAP_LAST_U32 - i]); } } diff --git a/kernel/capability.c b/kernel/capability.c index a5cf13c018ce..989f5bfc57dc 100644 --- a/kernel/capability.c +++ b/kernel/capability.c @@ -258,6 +258,10 @@ SYSCALL_DEFINE2(capset, cap_user_header_t, header, const cap_user_data_t, data) i++; } + effective.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + new = prepare_creds(); if (!new) return -ENOMEM; diff --git a/security/commoncap.c b/security/commoncap.c index 9fe46e22c7f2..bab0611afc1e 100644 --- a/security/commoncap.c +++ b/security/commoncap.c @@ -421,6 +421,9 @@ int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable); } + cpu_caps->permitted.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + cpu_caps->inheritable.cap[CAP_LAST_U32] &= CAP_LAST_U32_VALID_MASK; + return 0; } -- cgit v1.2.3-59-g8ed1b From 13752fe2d7f2d41c2fd92a5d1b1c6e38c4de0c05 Mon Sep 17 00:00:00 2001 From: Kees Cook Date: Tue, 25 Feb 2014 10:28:04 -0800 Subject: security: introduce kernel_fw_from_file hook In order to validate the contents of firmware being loaded, there must be a hook to evaluate any loaded firmware that wasn't built into the kernel itself. Without this, there is a risk that a root user could load malicious firmware designed to mount an attack against kernel memory (e.g. via DMA). Signed-off-by: Kees Cook Reviewed-by: Takashi Iwai --- include/linux/security.h | 17 +++++++++++++++++ security/capability.c | 6 ++++++ security/security.c | 6 ++++++ 3 files changed, 29 insertions(+) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 59820f8782a1..0ae4b147718a 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -702,6 +702,15 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * @inode points to the inode to use as a reference. * The current task must be the one that nominated @inode. * Return 0 if successful. + * @kernel_fw_from_file: + * Load firmware from userspace (not called for built-in firmware). + * @file contains the file structure pointing to the file containing + * the firmware to load. This argument will be NULL if the firmware + * was loaded via the uevent-triggered blob-based interface exposed + * by CONFIG_FW_LOADER_USER_HELPER. + * @buf pointer to buffer containing firmware contents. + * @size length of the firmware contents. + * Return 0 if permission is granted. * @kernel_module_request: * Ability to trigger the kernel to automatically upcall to userspace for * userspace to load a kernel module with the given name. @@ -1568,6 +1577,7 @@ struct security_operations { void (*cred_transfer)(struct cred *new, const struct cred *old); int (*kernel_act_as)(struct cred *new, u32 secid); int (*kernel_create_files_as)(struct cred *new, struct inode *inode); + int (*kernel_fw_from_file)(struct file *file, char *buf, size_t size); int (*kernel_module_request)(char *kmod_name); int (*kernel_module_from_file)(struct file *file); int (*task_fix_setuid) (struct cred *new, const struct cred *old, @@ -1840,6 +1850,7 @@ int security_prepare_creds(struct cred *new, const struct cred *old, gfp_t gfp); void security_transfer_creds(struct cred *new, const struct cred *old); int security_kernel_act_as(struct cred *new, u32 secid); int security_kernel_create_files_as(struct cred *new, struct inode *inode); +int security_kernel_fw_from_file(struct file *file, char *buf, size_t size); int security_kernel_module_request(char *kmod_name); int security_kernel_module_from_file(struct file *file); int security_task_fix_setuid(struct cred *new, const struct cred *old, @@ -2366,6 +2377,12 @@ static inline int security_kernel_create_files_as(struct cred *cred, return 0; } +static inline int security_kernel_fw_from_file(struct file *file, + char *buf, size_t size) +{ + return 0; +} + static inline int security_kernel_module_request(char *kmod_name) { return 0; diff --git a/security/capability.c b/security/capability.c index e76373de3129..a74fde6a7468 100644 --- a/security/capability.c +++ b/security/capability.c @@ -401,6 +401,11 @@ static int cap_kernel_create_files_as(struct cred *new, struct inode *inode) return 0; } +static int cap_kernel_fw_from_file(struct file *file, char *buf, size_t size) +{ + return 0; +} + static int cap_kernel_module_request(char *kmod_name) { return 0; @@ -1015,6 +1020,7 @@ void __init security_fixup_ops(struct security_operations *ops) set_to_cap_if_null(ops, cred_transfer); set_to_cap_if_null(ops, kernel_act_as); set_to_cap_if_null(ops, kernel_create_files_as); + set_to_cap_if_null(ops, kernel_fw_from_file); set_to_cap_if_null(ops, kernel_module_request); set_to_cap_if_null(ops, kernel_module_from_file); set_to_cap_if_null(ops, task_fix_setuid); diff --git a/security/security.c b/security/security.c index 31614e9e96e5..35d37d0f0d49 100644 --- a/security/security.c +++ b/security/security.c @@ -845,6 +845,12 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) return security_ops->kernel_create_files_as(new, inode); } +int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) +{ + return security_ops->kernel_fw_from_file(file, buf, size); +} +EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); + int security_kernel_module_request(char *kmod_name) { return security_ops->kernel_module_request(kmod_name); -- cgit v1.2.3-59-g8ed1b From 5a9196d715607f76d6b7d96a0970d6065335e62b Mon Sep 17 00:00:00 2001 From: Mimi Zohar Date: Tue, 22 Jul 2014 10:39:48 -0400 Subject: ima: add support for measuring and appraising firmware The "security: introduce kernel_fw_from_file hook" patch defined a new security hook to evaluate any loaded firmware that wasn't built into the kernel. This patch defines ima_fw_from_file(), which is called from the new security hook, to measure and/or appraise the loaded firmware's integrity. Signed-off-by: Mimi Zohar Signed-off-by: Kees Cook --- Documentation/ABI/testing/ima_policy | 4 +++- include/linux/ima.h | 6 ++++++ security/integrity/ima/ima.h | 3 ++- security/integrity/ima/ima_appraise.c | 8 ++++++++ security/integrity/ima/ima_main.c | 11 +++++++++++ security/integrity/ima/ima_policy.c | 7 +++++++ security/integrity/integrity.h | 9 +++++++-- security/security.c | 7 ++++++- 8 files changed, 50 insertions(+), 5 deletions(-) (limited to 'include') diff --git a/Documentation/ABI/testing/ima_policy b/Documentation/ABI/testing/ima_policy index 4c3efe434806..d0d0c578324c 100644 --- a/Documentation/ABI/testing/ima_policy +++ b/Documentation/ABI/testing/ima_policy @@ -26,6 +26,7 @@ Description: option: [[appraise_type=]] [permit_directio] base: func:= [BPRM_CHECK][MMAP_CHECK][FILE_CHECK][MODULE_CHECK] + [FIRMWARE_CHECK] mask:= [MAY_READ] [MAY_WRITE] [MAY_APPEND] [MAY_EXEC] fsmagic:= hex value fsuuid:= file system UUID (e.g 8bcbe394-4f13-4144-be8e-5aa9ea2ce2f6) @@ -57,7 +58,8 @@ Description: measure func=BPRM_CHECK measure func=FILE_MMAP mask=MAY_EXEC measure func=FILE_CHECK mask=MAY_READ uid=0 - measure func=MODULE_CHECK uid=0 + measure func=MODULE_CHECK + measure func=FIRMWARE_CHECK appraise fowner=0 The default policy measures all executables in bprm_check, diff --git a/include/linux/ima.h b/include/linux/ima.h index 1b7f268cddce..7cf5e9b32550 100644 --- a/include/linux/ima.h +++ b/include/linux/ima.h @@ -19,6 +19,7 @@ extern int ima_file_check(struct file *file, int mask); extern void ima_file_free(struct file *file); extern int ima_file_mmap(struct file *file, unsigned long prot); extern int ima_module_check(struct file *file); +extern int ima_fw_from_file(struct file *file, char *buf, size_t size); #else static inline int ima_bprm_check(struct linux_binprm *bprm) @@ -46,6 +47,11 @@ static inline int ima_module_check(struct file *file) return 0; } +static inline int ima_fw_from_file(struct file *file, char *buf, size_t size) +{ + return 0; +} + #endif /* CONFIG_IMA */ #ifdef CONFIG_IMA_APPRAISE diff --git a/security/integrity/ima/ima.h b/security/integrity/ima/ima.h index c42056edfc97..57da4bd7ba0c 100644 --- a/security/integrity/ima/ima.h +++ b/security/integrity/ima/ima.h @@ -158,7 +158,7 @@ struct integrity_iint_cache *integrity_iint_insert(struct inode *inode); struct integrity_iint_cache *integrity_iint_find(struct inode *inode); /* IMA policy related functions */ -enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, POST_SETATTR }; +enum ima_hooks { FILE_CHECK = 1, MMAP_CHECK, BPRM_CHECK, MODULE_CHECK, FIRMWARE_CHECK, POST_SETATTR }; int ima_match_policy(struct inode *inode, enum ima_hooks func, int mask, int flags); @@ -171,6 +171,7 @@ void ima_delete_rules(void); #define IMA_APPRAISE_ENFORCE 0x01 #define IMA_APPRAISE_FIX 0x02 #define IMA_APPRAISE_MODULES 0x04 +#define IMA_APPRAISE_FIRMWARE 0x08 #ifdef CONFIG_IMA_APPRAISE int ima_appraise_measurement(int func, struct integrity_iint_cache *iint, diff --git a/security/integrity/ima/ima_appraise.c b/security/integrity/ima/ima_appraise.c index 59ac90275070..86bfd5c5df85 100644 --- a/security/integrity/ima/ima_appraise.c +++ b/security/integrity/ima/ima_appraise.c @@ -75,6 +75,8 @@ enum integrity_status ima_get_cache_status(struct integrity_iint_cache *iint, return iint->ima_bprm_status; case MODULE_CHECK: return iint->ima_module_status; + case FIRMWARE_CHECK: + return iint->ima_firmware_status; case FILE_CHECK: default: return iint->ima_file_status; @@ -94,6 +96,9 @@ static void ima_set_cache_status(struct integrity_iint_cache *iint, case MODULE_CHECK: iint->ima_module_status = status; break; + case FIRMWARE_CHECK: + iint->ima_firmware_status = status; + break; case FILE_CHECK: default: iint->ima_file_status = status; @@ -113,6 +118,9 @@ static void ima_cache_flags(struct integrity_iint_cache *iint, int func) case MODULE_CHECK: iint->flags |= (IMA_MODULE_APPRAISED | IMA_APPRAISED); break; + case FIRMWARE_CHECK: + iint->flags |= (IMA_FIRMWARE_APPRAISED | IMA_APPRAISED); + break; case FILE_CHECK: default: iint->flags |= (IMA_FILE_APPRAISED | IMA_APPRAISED); diff --git a/security/integrity/ima/ima_main.c b/security/integrity/ima/ima_main.c index 0d696431209c..2917f980bf30 100644 --- a/security/integrity/ima/ima_main.c +++ b/security/integrity/ima/ima_main.c @@ -319,6 +319,17 @@ int ima_module_check(struct file *file) return process_measurement(file, NULL, MAY_EXEC, MODULE_CHECK); } +int ima_fw_from_file(struct file *file, char *buf, size_t size) +{ + if (!file) { + if ((ima_appraise & IMA_APPRAISE_FIRMWARE) && + (ima_appraise & IMA_APPRAISE_ENFORCE)) + return -EACCES; /* INTEGRITY_UNKNOWN */ + return 0; + } + return process_measurement(file, NULL, MAY_EXEC, FIRMWARE_CHECK); +} + static int __init init_ima(void) { int error; diff --git a/security/integrity/ima/ima_policy.c b/security/integrity/ima/ima_policy.c index cea84d8bd7be..07099a8bc283 100644 --- a/security/integrity/ima/ima_policy.c +++ b/security/integrity/ima/ima_policy.c @@ -84,6 +84,7 @@ static struct ima_rule_entry default_rules[] = { {.action = MEASURE, .func = FILE_CHECK, .mask = MAY_READ, .uid = GLOBAL_ROOT_UID, .flags = IMA_FUNC | IMA_MASK | IMA_UID}, {.action = MEASURE, .func = MODULE_CHECK, .flags = IMA_FUNC}, + {.action = MEASURE, .func = FIRMWARE_CHECK, .flags = IMA_FUNC}, }; static struct ima_rule_entry default_appraise_rules[] = { @@ -241,6 +242,8 @@ static int get_subaction(struct ima_rule_entry *rule, int func) return IMA_BPRM_APPRAISE; case MODULE_CHECK: return IMA_MODULE_APPRAISE; + case FIRMWARE_CHECK: + return IMA_FIRMWARE_APPRAISE; case FILE_CHECK: default: return IMA_FILE_APPRAISE; @@ -486,6 +489,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) entry->func = FILE_CHECK; else if (strcmp(args[0].from, "MODULE_CHECK") == 0) entry->func = MODULE_CHECK; + else if (strcmp(args[0].from, "FIRMWARE_CHECK") == 0) + entry->func = FIRMWARE_CHECK; else if ((strcmp(args[0].from, "FILE_MMAP") == 0) || (strcmp(args[0].from, "MMAP_CHECK") == 0)) entry->func = MMAP_CHECK; @@ -636,6 +641,8 @@ static int ima_parse_rule(char *rule, struct ima_rule_entry *entry) result = -EINVAL; else if (entry->func == MODULE_CHECK) ima_appraise |= IMA_APPRAISE_MODULES; + else if (entry->func == FIRMWARE_CHECK) + ima_appraise |= IMA_APPRAISE_FIRMWARE; audit_log_format(ab, "res=%d", !result); audit_log_end(ab); return result; diff --git a/security/integrity/integrity.h b/security/integrity/integrity.h index 09c440d9aaee..19b8e314ca96 100644 --- a/security/integrity/integrity.h +++ b/security/integrity/integrity.h @@ -46,10 +46,14 @@ #define IMA_BPRM_APPRAISED 0x00002000 #define IMA_MODULE_APPRAISE 0x00004000 #define IMA_MODULE_APPRAISED 0x00008000 +#define IMA_FIRMWARE_APPRAISE 0x00010000 +#define IMA_FIRMWARE_APPRAISED 0x00020000 #define IMA_APPRAISE_SUBMASK (IMA_FILE_APPRAISE | IMA_MMAP_APPRAISE | \ - IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE) + IMA_BPRM_APPRAISE | IMA_MODULE_APPRAISE | \ + IMA_FIRMWARE_APPRAISE) #define IMA_APPRAISED_SUBMASK (IMA_FILE_APPRAISED | IMA_MMAP_APPRAISED | \ - IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED) + IMA_BPRM_APPRAISED | IMA_MODULE_APPRAISED | \ + IMA_FIRMWARE_APPRAISED) enum evm_ima_xattr_type { IMA_XATTR_DIGEST = 0x01, @@ -104,6 +108,7 @@ struct integrity_iint_cache { enum integrity_status ima_mmap_status:4; enum integrity_status ima_bprm_status:4; enum integrity_status ima_module_status:4; + enum integrity_status ima_firmware_status:4; enum integrity_status evm_status:4; struct ima_digest_data *ima_hash; }; diff --git a/security/security.c b/security/security.c index 35d37d0f0d49..e41b1a8d7644 100644 --- a/security/security.c +++ b/security/security.c @@ -847,7 +847,12 @@ int security_kernel_create_files_as(struct cred *new, struct inode *inode) int security_kernel_fw_from_file(struct file *file, char *buf, size_t size) { - return security_ops->kernel_fw_from_file(file, buf, size); + int ret; + + ret = security_ops->kernel_fw_from_file(file, buf, size); + if (ret) + return ret; + return ima_fw_from_file(file, buf, size); } EXPORT_SYMBOL_GPL(security_kernel_fw_from_file); -- cgit v1.2.3-59-g8ed1b From 2873ead7e46694910ac49c3a8ee0f54956f96e0c Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Mon, 28 Jul 2014 10:42:48 -0400 Subject: Revert "selinux: fix the default socket labeling in sock_graft()" This reverts commit 4da6daf4d3df5a977e4623963f141a627fd2efce. Unfortunately, the commit in question caused problems with Bluetooth devices, specifically it caused them to get caught in the newly created BUG_ON() check. The AF_ALG problem still exists, but will be addressed in a future patch. Cc: stable@vger.kernel.org Signed-off-by: Paul Moore --- include/linux/security.h | 5 +---- security/selinux/hooks.c | 13 ++----------- 2 files changed, 3 insertions(+), 15 deletions(-) (limited to 'include') diff --git a/include/linux/security.h b/include/linux/security.h index 794be735ff4b..6478ce3252c7 100644 --- a/include/linux/security.h +++ b/include/linux/security.h @@ -987,10 +987,7 @@ static inline void security_free_mnt_opts(struct security_mnt_opts *opts) * Retrieve the LSM-specific secid for the sock to enable caching of network * authorizations. * @sock_graft: - * This hook is called in response to a newly created sock struct being - * grafted onto an existing socket and allows the security module to - * perform whatever security attribute management is necessary for both - * the sock and socket. + * Sets the socket's isec sid to the sock's sid. * @inet_conn_request: * Sets the openreq's sid to socket's sid with MLS portion taken from peer sid. * @inet_csk_clone: diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index b3a6754e932b..336f0a04450e 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4499,18 +4499,9 @@ static void selinux_sock_graft(struct sock *sk, struct socket *parent) struct inode_security_struct *isec = SOCK_INODE(parent)->i_security; struct sk_security_struct *sksec = sk->sk_security; - switch (sk->sk_family) { - case PF_INET: - case PF_INET6: - case PF_UNIX: + if (sk->sk_family == PF_INET || sk->sk_family == PF_INET6 || + sk->sk_family == PF_UNIX) isec->sid = sksec->sid; - break; - default: - /* by default there is no special labeling mechanism for the - * sksec label so inherit the label from the parent socket */ - BUG_ON(sksec->sid != SECINITSID_UNLABELED); - sksec->sid = isec->sid; - } sksec->sclass = isec->sclass; } -- cgit v1.2.3-59-g8ed1b From 5ce43ad28262115a1eab866392f8cfb985094160 Mon Sep 17 00:00:00 2001 From: David Howells Date: Mon, 28 Jul 2014 14:11:32 +0100 Subject: PKCS#7: Use x509_request_asymmetric_key() pkcs7_request_asymmetric_key() and x509_request_asymmetric_key() do the same thing, the latter being a copy of the former created by the IMA folks, so drop the PKCS#7 version as the X.509 location is more general. Whilst we're at it, rename the arguments of x509_request_asymmetric_key() to better reflect what the values being passed in are intended to match on an X.509 cert. Signed-off-by: David Howells Acked-by: Mimi Zohar --- crypto/asymmetric_keys/pkcs7_trust.c | 61 +++----------------------------- crypto/asymmetric_keys/x509_public_key.c | 36 +++++++++++-------- include/crypto/public_key.h | 4 +++ 3 files changed, 29 insertions(+), 72 deletions(-) (limited to 'include') diff --git a/crypto/asymmetric_keys/pkcs7_trust.c b/crypto/asymmetric_keys/pkcs7_trust.c index b6b045131403..e666eb011a85 100644 --- a/crypto/asymmetric_keys/pkcs7_trust.c +++ b/crypto/asymmetric_keys/pkcs7_trust.c @@ -20,55 +20,6 @@ #include "public_key.h" #include "pkcs7_parser.h" -/* - * Request an asymmetric key. - */ -static struct key *pkcs7_request_asymmetric_key( - struct key *keyring, - const char *signer, size_t signer_len, - const char *authority, size_t auth_len) -{ - key_ref_t key; - char *id; - - kenter(",%zu,,%zu", signer_len, auth_len); - - /* Construct an identifier. */ - id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); - if (!id) - return ERR_PTR(-ENOMEM); - - memcpy(id, signer, signer_len); - id[signer_len + 0] = ':'; - id[signer_len + 1] = ' '; - memcpy(id + signer_len + 2, authority, auth_len); - id[signer_len + 2 + auth_len] = 0; - - pr_debug("Look up: \"%s\"\n", id); - - key = keyring_search(make_key_ref(keyring, 1), - &key_type_asymmetric, id); - if (IS_ERR(key)) - pr_debug("Request for module key '%s' err %ld\n", - id, PTR_ERR(key)); - kfree(id); - - if (IS_ERR(key)) { - switch (PTR_ERR(key)) { - /* Hide some search errors */ - case -EACCES: - case -ENOTDIR: - case -EAGAIN: - return ERR_PTR(-ENOKEY); - default: - return ERR_CAST(key); - } - } - - pr_devel("<==%s() = 0 [%x]\n", __func__, key_serial(key_ref_to_ptr(key))); - return key_ref_to_ptr(key); -} - /** * Check the trust on one PKCS#7 SignedInfo block. */ @@ -98,10 +49,8 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, /* Look to see if this certificate is present in the trusted * keys. */ - key = pkcs7_request_asymmetric_key( - trust_keyring, - x509->subject, strlen(x509->subject), - x509->fingerprint, strlen(x509->fingerprint)); + key = x509_request_asymmetric_key(trust_keyring, x509->subject, + x509->fingerprint); if (!IS_ERR(key)) /* One of the X.509 certificates in the PKCS#7 message * is apparently the same as one we already trust. @@ -133,10 +82,8 @@ int pkcs7_validate_trust_one(struct pkcs7_message *pkcs7, return -ENOKEY; } - key = pkcs7_request_asymmetric_key( - trust_keyring, - last->issuer, strlen(last->issuer), - last->authority, strlen(last->authority)); + key = x509_request_asymmetric_key(trust_keyring, last->issuer, + last->authority); if (IS_ERR(key)) return PTR_ERR(key) == -ENOMEM ? -ENOMEM : -ENOKEY; x509 = last; diff --git a/crypto/asymmetric_keys/x509_public_key.c b/crypto/asymmetric_keys/x509_public_key.c index 4ae982234d78..da1e5fc85346 100644 --- a/crypto/asymmetric_keys/x509_public_key.c +++ b/crypto/asymmetric_keys/x509_public_key.c @@ -43,35 +43,41 @@ static int __init ca_keys_setup(char *str) __setup("ca_keys=", ca_keys_setup); #endif -/* - * Find a key in the given keyring by issuer and authority. +/** + * x509_request_asymmetric_key - Request a key by X.509 certificate params. + * @keyring: The keys to search. + * @subject: The name of the subject to whom the key belongs. + * @key_id: The subject key ID as a hex string. + * + * Find a key in the given keyring by subject name and key ID. These might, + * for instance, be the issuer name and the authority key ID of an X.509 + * certificate that needs to be verified. */ -static struct key *x509_request_asymmetric_key(struct key *keyring, - const char *signer, - const char *authority) +struct key *x509_request_asymmetric_key(struct key *keyring, + const char *subject, + const char *key_id) { key_ref_t key; - size_t signer_len = strlen(signer), auth_len = strlen(authority); + size_t subject_len = strlen(subject), key_id_len = strlen(key_id); char *id; - /* Construct an identifier. */ - id = kmalloc(signer_len + 2 + auth_len + 1, GFP_KERNEL); + /* Construct an identifier ":". */ + id = kmalloc(subject_len + 2 + key_id_len + 1, GFP_KERNEL); if (!id) return ERR_PTR(-ENOMEM); - memcpy(id, signer, signer_len); - id[signer_len + 0] = ':'; - id[signer_len + 1] = ' '; - memcpy(id + signer_len + 2, authority, auth_len); - id[signer_len + 2 + auth_len] = 0; + memcpy(id, subject, subject_len); + id[subject_len + 0] = ':'; + id[subject_len + 1] = ' '; + memcpy(id + subject_len + 2, key_id, key_id_len); + id[subject_len + 2 + key_id_len] = 0; pr_debug("Look up: \"%s\"\n", id); key = keyring_search(make_key_ref(keyring, 1), &key_type_asymmetric, id); if (IS_ERR(key)) - pr_debug("Request for module key '%s' err %ld\n", - id, PTR_ERR(key)); + pr_debug("Request for key '%s' err %ld\n", id, PTR_ERR(key)); kfree(id); if (IS_ERR(key)) { diff --git a/include/crypto/public_key.h b/include/crypto/public_key.h index fc09732613ad..0d164c6af539 100644 --- a/include/crypto/public_key.h +++ b/include/crypto/public_key.h @@ -98,4 +98,8 @@ struct key; extern int verify_signature(const struct key *key, const struct public_key_signature *sig); +extern struct key *x509_request_asymmetric_key(struct key *keyring, + const char *issuer, + const char *key_id); + #endif /* _LINUX_PUBLIC_KEY_H */ -- cgit v1.2.3-59-g8ed1b From 8e54caf407b98efa05409e1fee0e5381abd2b088 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Wed, 21 May 2014 18:26:44 -0600 Subject: tpm: Provide a generic means to override the chip returned timeouts Some Atmel TPMs provide completely wrong timeouts from their TPM_CAP_PROP_TIS_TIMEOUT query. This patch detects that and returns new correct values via a DID/VID table in the TIS driver. Tested on ARM using an AT97SC3204T FW version 37.16 Cc: [PHuewe: without this fix these 'broken' Atmel TPMs won't function on older kernels] Signed-off-by: "Berg, Christopher" Signed-off-by: Jason Gunthorpe Signed-off-by: Peter Huewe --- drivers/char/tpm/tpm-interface.c | 62 ++++++++++++++++++++++++++-------------- drivers/char/tpm/tpm_tis.c | 31 ++++++++++++++++++++ include/linux/tpm.h | 3 ++ 3 files changed, 75 insertions(+), 21 deletions(-) (limited to 'include') diff --git a/drivers/char/tpm/tpm-interface.c b/drivers/char/tpm/tpm-interface.c index bef6cceffc3a..6af17002a115 100644 --- a/drivers/char/tpm/tpm-interface.c +++ b/drivers/char/tpm/tpm-interface.c @@ -491,11 +491,10 @@ static int tpm_startup(struct tpm_chip *chip, __be16 startup_type) int tpm_get_timeouts(struct tpm_chip *chip) { struct tpm_cmd_t tpm_cmd; - struct timeout_t *timeout_cap; + unsigned long new_timeout[4]; + unsigned long old_timeout[4]; struct duration_t *duration_cap; ssize_t rc; - u32 timeout; - unsigned int scale = 1; tpm_cmd.header.in = tpm_getcap_header; tpm_cmd.params.getcap_in.cap = TPM_CAP_PROP; @@ -529,25 +528,46 @@ int tpm_get_timeouts(struct tpm_chip *chip) != sizeof(tpm_cmd.header.out) + sizeof(u32) + 4 * sizeof(u32)) return -EINVAL; - timeout_cap = &tpm_cmd.params.getcap_out.cap.timeout; - /* Don't overwrite default if value is 0 */ - timeout = be32_to_cpu(timeout_cap->a); - if (timeout && timeout < 1000) { - /* timeouts in msec rather usec */ - scale = 1000; - chip->vendor.timeout_adjusted = true; + old_timeout[0] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.a); + old_timeout[1] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.b); + old_timeout[2] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.c); + old_timeout[3] = be32_to_cpu(tpm_cmd.params.getcap_out.cap.timeout.d); + memcpy(new_timeout, old_timeout, sizeof(new_timeout)); + + /* + * Provide ability for vendor overrides of timeout values in case + * of misreporting. + */ + if (chip->ops->update_timeouts != NULL) + chip->vendor.timeout_adjusted = + chip->ops->update_timeouts(chip, new_timeout); + + if (!chip->vendor.timeout_adjusted) { + /* Don't overwrite default if value is 0 */ + if (new_timeout[0] != 0 && new_timeout[0] < 1000) { + int i; + + /* timeouts in msec rather usec */ + for (i = 0; i != ARRAY_SIZE(new_timeout); i++) + new_timeout[i] *= 1000; + chip->vendor.timeout_adjusted = true; + } } - if (timeout) - chip->vendor.timeout_a = usecs_to_jiffies(timeout * scale); - timeout = be32_to_cpu(timeout_cap->b); - if (timeout) - chip->vendor.timeout_b = usecs_to_jiffies(timeout * scale); - timeout = be32_to_cpu(timeout_cap->c); - if (timeout) - chip->vendor.timeout_c = usecs_to_jiffies(timeout * scale); - timeout = be32_to_cpu(timeout_cap->d); - if (timeout) - chip->vendor.timeout_d = usecs_to_jiffies(timeout * scale); + + /* Report adjusted timeouts */ + if (chip->vendor.timeout_adjusted) { + dev_info(chip->dev, + HW_ERR "Adjusting reported timeouts: A %lu->%luus B %lu->%luus C %lu->%luus D %lu->%luus\n", + old_timeout[0], new_timeout[0], + old_timeout[1], new_timeout[1], + old_timeout[2], new_timeout[2], + old_timeout[3], new_timeout[3]); + } + + chip->vendor.timeout_a = usecs_to_jiffies(new_timeout[0]); + chip->vendor.timeout_b = usecs_to_jiffies(new_timeout[1]); + chip->vendor.timeout_c = usecs_to_jiffies(new_timeout[2]); + chip->vendor.timeout_d = usecs_to_jiffies(new_timeout[3]); duration: tpm_cmd.header.in = tpm_getcap_header; diff --git a/drivers/char/tpm/tpm_tis.c b/drivers/char/tpm/tpm_tis.c index a9ed2270c25d..2c46734b266d 100644 --- a/drivers/char/tpm/tpm_tis.c +++ b/drivers/char/tpm/tpm_tis.c @@ -373,6 +373,36 @@ out_err: return rc; } +struct tis_vendor_timeout_override { + u32 did_vid; + unsigned long timeout_us[4]; +}; + +static const struct tis_vendor_timeout_override vendor_timeout_overrides[] = { + /* Atmel 3204 */ + { 0x32041114, { (TIS_SHORT_TIMEOUT*1000), (TIS_LONG_TIMEOUT*1000), + (TIS_SHORT_TIMEOUT*1000), (TIS_SHORT_TIMEOUT*1000) } }, +}; + +static bool tpm_tis_update_timeouts(struct tpm_chip *chip, + unsigned long *timeout_cap) +{ + int i; + u32 did_vid; + + did_vid = ioread32(chip->vendor.iobase + TPM_DID_VID(0)); + + for (i = 0; i != ARRAY_SIZE(vendor_timeout_overrides); i++) { + if (vendor_timeout_overrides[i].did_vid != did_vid) + continue; + memcpy(timeout_cap, vendor_timeout_overrides[i].timeout_us, + sizeof(vendor_timeout_overrides[i].timeout_us)); + return true; + } + + return false; +} + /* * Early probing for iTPM with STS_DATA_EXPECT flaw. * Try sending command without itpm flag set and if that @@ -437,6 +467,7 @@ static const struct tpm_class_ops tpm_tis = { .recv = tpm_tis_recv, .send = tpm_tis_send, .cancel = tpm_tis_ready, + .update_timeouts = tpm_tis_update_timeouts, .req_complete_mask = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_complete_val = TPM_STS_DATA_AVAIL | TPM_STS_VALID, .req_canceled = tpm_tis_req_canceled, diff --git a/include/linux/tpm.h b/include/linux/tpm.h index fff1d0976f80..8350c538b486 100644 --- a/include/linux/tpm.h +++ b/include/linux/tpm.h @@ -39,6 +39,9 @@ struct tpm_class_ops { int (*send) (struct tpm_chip *chip, u8 *buf, size_t len); void (*cancel) (struct tpm_chip *chip); u8 (*status) (struct tpm_chip *chip); + bool (*update_timeouts)(struct tpm_chip *chip, + unsigned long *timeout_cap); + }; #if defined(CONFIG_TCG_TPM) || defined(CONFIG_TCG_TPM_MODULE) -- cgit v1.2.3-59-g8ed1b From 41c3bd2039e0d7b3dc32313141773f20716ec524 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 1 Aug 2014 11:17:03 -0400 Subject: netlabel: fix a problem when setting bits below the previously lowest bit The NetLabel category (catmap) functions have a problem in that they assume categories will be set in an increasing manner, e.g. the next category set will always be larger than the last. Unfortunately, this is not a valid assumption and could result in problems when attempting to set categories less than the startbit in the lowest catmap node. In some cases kernel panics and other nasties can result. This patch corrects the problem by checking for this and allocating a new catmap node instance and placing it at the front of the list. Cc: stable@vger.kernel.org Reported-by: Christian Evans Signed-off-by: Paul Moore Tested-by: Casey Schaufler --- include/net/netlabel.h | 8 ++++---- net/ipv4/cipso_ipv4.c | 6 +++--- net/netlabel/netlabel_kapi.c | 26 ++++++++++++++++++-------- security/smack/smack_access.c | 2 +- 4 files changed, 26 insertions(+), 16 deletions(-) (limited to 'include') diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 4fe018c48ed9..1c40d658d008 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -394,10 +394,10 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags); -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags); @@ -505,14 +505,14 @@ static inline int netlbl_secattr_catmap_walk_rng( return -ENOENT; } static inline int netlbl_secattr_catmap_setbit( - struct netlbl_lsm_secattr_catmap *catmap, + struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags) { return 0; } static inline int netlbl_secattr_catmap_setrng( - struct netlbl_lsm_secattr_catmap *catmap, + struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags) diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 69e77c8ff285..dd433c943537 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -973,7 +973,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, return -EPERM; break; } - ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat, + ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, host_spot, GFP_ATOMIC); if (ret_val != 0) @@ -1075,7 +1075,7 @@ static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def, u32 iter; for (iter = 0; iter < net_cat_len; iter += 2) { - ret_val = netlbl_secattr_catmap_setbit(secattr->attr.mls.cat, + ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, get_unaligned_be16(&net_cat[iter]), GFP_ATOMIC); if (ret_val != 0) @@ -1217,7 +1217,7 @@ static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def, else cat_low = 0; - ret_val = netlbl_secattr_catmap_setrng(secattr->attr.mls.cat, + ret_val = netlbl_secattr_catmap_setrng(&secattr->attr.mls.cat, cat_low, cat_high, GFP_ATOMIC); diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 3045a964f39c..84e810bef39a 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -522,7 +522,7 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, /** * netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap - * @catmap: the category bitmap + * @catmap: pointer to the category bitmap * @bit: the bit to set * @flags: memory allocation flags * @@ -531,18 +531,25 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, * negative values on failure. * */ -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter = catmap; + struct netlbl_lsm_secattr_catmap *iter = *catmap; u32 node_bit; u32 node_idx; while (iter->next != NULL && bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) iter = iter->next; - if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) { + if (bit < iter->startbit) { + iter = netlbl_secattr_catmap_alloc(flags); + if (iter == NULL) + return -ENOMEM; + iter->next = *catmap; + iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1); + *catmap = iter; + } else if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) { iter->next = netlbl_secattr_catmap_alloc(flags); if (iter->next == NULL) return -ENOMEM; @@ -560,7 +567,7 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, /** * netlbl_secattr_catmap_setrng - Set a range of bits in a LSM secattr catmap - * @catmap: the category bitmap + * @catmap: pointer to the category bitmap * @start: the starting bit * @end: the last bit in the string * @flags: memory allocation flags @@ -570,15 +577,16 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap *catmap, * on success, negative values on failure. * */ -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, +int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags) { int ret_val = 0; - struct netlbl_lsm_secattr_catmap *iter = catmap; + struct netlbl_lsm_secattr_catmap *iter = *catmap; u32 iter_max_spot; u32 spot; + u32 orig_spot = iter->startbit; /* XXX - This could probably be made a bit faster by combining writes * to the catmap instead of setting a single bit each time, but for @@ -596,7 +604,9 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap *catmap, iter = iter->next; iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; } - ret_val = netlbl_secattr_catmap_setbit(iter, spot, flags); + ret_val = netlbl_secattr_catmap_setbit(&iter, spot, flags); + if (iter->startbit < orig_spot) + *catmap = iter; } return ret_val; diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 14293cd9b1e5..9ecf4f4b67a1 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -444,7 +444,7 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, for (m = 0x80; m != 0; m >>= 1, cat++) { if ((m & *cp) == 0) continue; - rc = netlbl_secattr_catmap_setbit(sap->attr.mls.cat, + rc = netlbl_secattr_catmap_setbit(&sap->attr.mls.cat, cat, GFP_ATOMIC); if (rc < 0) { netlbl_secattr_catmap_free(sap->attr.mls.cat); -- cgit v1.2.3-59-g8ed1b From 4b8feff251da3d7058b5779e21b33a85c686b974 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 1 Aug 2014 11:17:17 -0400 Subject: netlabel: fix the horribly broken catmap functions The NetLabel secattr catmap functions, and the SELinux import/export glue routines, were broken in many horrible ways and the SELinux glue code fiddled with the NetLabel catmap structures in ways that we probably shouldn't allow. At some point this "worked", but that was likely due to a bit of dumb luck and sub-par testing (both inflicted by yours truly). This patch corrects these problems by basically gutting the code in favor of something less obtuse and restoring the NetLabel abstractions in the SELinux catmap glue code. Everything is working now, and if it decides to break itself in the future this code will be much easier to debug than the code it replaces. One noteworthy side effect of the changes is that it is no longer necessary to allocate a NetLabel catmap before calling one of the NetLabel APIs to set a bit in the catmap. NetLabel will automatically allocate the catmap nodes when needed, resulting in less allocations when the lowest bit is greater than 255 and less code in the LSMs. Cc: stable@vger.kernel.org Reported-by: Christian Evans Signed-off-by: Paul Moore Tested-by: Casey Schaufler --- include/net/netlabel.h | 26 ++++- net/ipv4/cipso_ipv4.c | 12 --- net/netlabel/netlabel_kapi.c | 216 ++++++++++++++++++++++++++++++++---------- security/selinux/ss/ebitmap.c | 127 ++++++++++--------------- security/smack/smack_access.c | 5 +- 5 files changed, 240 insertions(+), 146 deletions(-) (limited to 'include') diff --git a/include/net/netlabel.h b/include/net/netlabel.h index 1c40d658d008..bda7a121f31e 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -285,11 +285,11 @@ static inline void netlbl_secattr_catmap_free( { struct netlbl_lsm_secattr_catmap *iter; - do { + while (catmap) { iter = catmap; catmap = catmap->next; kfree(iter); - } while (catmap); + } } /** @@ -394,6 +394,9 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, u32 offset); +int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, + u32 *offset, + unsigned long *bitmap); int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags); @@ -401,6 +404,10 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 start, u32 end, gfp_t flags); +int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags); /* * LSM protocol operations (NetLabel LSM/kernel API) @@ -504,6 +511,13 @@ static inline int netlbl_secattr_catmap_walk_rng( { return -ENOENT; } +static inline int netlbl_secattr_catmap_getlong( + struct netlbl_lsm_secattr_catmap *catmap, + u32 *offset, + unsigned long *bitmap) +{ + return 0; +} static inline int netlbl_secattr_catmap_setbit( struct netlbl_lsm_secattr_catmap **catmap, u32 bit, @@ -519,6 +533,14 @@ static inline int netlbl_secattr_catmap_setrng( { return 0; } +static int netlbl_secattr_catmap_setlong( + struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) +{ + return 0; +} static inline int netlbl_enabled(void) { return 0; diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index dd433c943537..8a0c7bd6eff4 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -1335,10 +1335,6 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, secattr->flags |= NETLBL_SECATTR_MLS_LVL; if (tag_len > 4) { - secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (secattr->attr.mls.cat == NULL) - return -ENOMEM; - ret_val = cipso_v4_map_cat_rbm_ntoh(doi_def, &tag[4], tag_len - 4, @@ -1430,10 +1426,6 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def, secattr->flags |= NETLBL_SECATTR_MLS_LVL; if (tag_len > 4) { - secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (secattr->attr.mls.cat == NULL) - return -ENOMEM; - ret_val = cipso_v4_map_cat_enum_ntoh(doi_def, &tag[4], tag_len - 4, @@ -1524,10 +1516,6 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, secattr->flags |= NETLBL_SECATTR_MLS_LVL; if (tag_len > 4) { - secattr->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (secattr->attr.mls.cat == NULL) - return -ENOMEM; - ret_val = cipso_v4_map_cat_rng_ntoh(doi_def, &tag[4], tag_len - 4, diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index 84e810bef39a..d9e10466b928 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -405,6 +405,63 @@ out_entry: * Security Attribute Functions */ +#define _CM_F_NONE 0x00000000 +#define _CM_F_ALLOC 0x00000001 + +/** + * _netlbl_secattr_catmap_getnode - Get a individual node from a catmap + * @catmap: pointer to the category bitmap + * @offset: the requested offset + * @cm_flags: catmap flags, see _CM_F_* + * @gfp_flags: memory allocation flags + * + * Description: + * Iterate through the catmap looking for the node associated with @offset; if + * the _CM_F_ALLOC flag is set in @cm_flags and there is no associated node, + * one will be created and inserted into the catmap. Returns a pointer to the + * node on success, NULL on failure. + * + */ +static struct netlbl_lsm_secattr_catmap *_netlbl_secattr_catmap_getnode( + struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned int cm_flags, + gfp_t gfp_flags) +{ + struct netlbl_lsm_secattr_catmap *iter = *catmap; + struct netlbl_lsm_secattr_catmap *prev = NULL; + + if (iter == NULL || offset < iter->startbit) + goto secattr_catmap_getnode_alloc; + while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) { + prev = iter; + iter = iter->next; + } + if (iter == NULL || offset < iter->startbit) + goto secattr_catmap_getnode_alloc; + + return iter; + +secattr_catmap_getnode_alloc: + if (!(cm_flags & _CM_F_ALLOC)) + return NULL; + + iter = netlbl_secattr_catmap_alloc(gfp_flags); + if (iter == NULL) + return NULL; + iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1); + + if (prev == NULL) { + iter->next = *catmap; + *catmap = iter; + } else { + iter->next = prev->next; + prev->next = iter; + } + + return iter; +} + /** * netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit * @catmap: the category bitmap @@ -520,6 +577,54 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, return -ENOENT; } +/** + * netlbl_secattr_catmap_getlong - Export an unsigned long bitmap + * @catmap: pointer to the category bitmap + * @offset: pointer to the requested offset + * @bitmap: the exported bitmap + * + * Description: + * Export a bitmap with an offset greater than or equal to @offset and return + * it in @bitmap. The @offset must be aligned to an unsigned long and will be + * updated on return if different from what was requested; if the catmap is + * empty at the requested offset and beyond, the @offset is set to (u32)-1. + * Returns zero on sucess, negative values on failure. + * + */ +int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, + u32 *offset, + unsigned long *bitmap) +{ + struct netlbl_lsm_secattr_catmap *iter; + u32 off = *offset; + u32 idx; + + /* only allow aligned offsets */ + if ((off & (BITS_PER_LONG - 1)) != 0) + return -EINVAL; + + if (off < catmap->startbit) { + off = catmap->startbit; + *offset = off; + } + iter = _netlbl_secattr_catmap_getnode(&catmap, off, _CM_F_NONE, 0); + if (iter == NULL) { + *offset = (u32)-1; + return 0; + } + + if (off < iter->startbit) { + off = iter->startbit; + *offset = off; + } else + off -= iter->startbit; + + idx = off / NETLBL_CATMAP_MAPSIZE; + *bitmap = iter->bitmap[idx] >> (off % NETLBL_CATMAP_SIZE); + + return 0; +} + /** * netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap * @catmap: pointer to the category bitmap @@ -535,32 +640,16 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, u32 bit, gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter = *catmap; - u32 node_bit; - u32 node_idx; + struct netlbl_lsm_secattr_catmap *iter; + u32 idx; - while (iter->next != NULL && - bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) - iter = iter->next; - if (bit < iter->startbit) { - iter = netlbl_secattr_catmap_alloc(flags); - if (iter == NULL) - return -ENOMEM; - iter->next = *catmap; - iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1); - *catmap = iter; - } else if (bit >= (iter->startbit + NETLBL_CATMAP_SIZE)) { - iter->next = netlbl_secattr_catmap_alloc(flags); - if (iter->next == NULL) - return -ENOMEM; - iter = iter->next; - iter->startbit = bit & ~(NETLBL_CATMAP_SIZE - 1); - } + iter = _netlbl_secattr_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags); + if (iter == NULL) + return -ENOMEM; - /* gcc always rounds to zero when doing integer division */ - node_idx = (bit - iter->startbit) / NETLBL_CATMAP_MAPSIZE; - node_bit = bit - iter->startbit - (NETLBL_CATMAP_MAPSIZE * node_idx); - iter->bitmap[node_idx] |= NETLBL_CATMAP_BIT << node_bit; + bit -= iter->startbit; + idx = bit / NETLBL_CATMAP_MAPSIZE; + iter->bitmap[idx] |= NETLBL_CATMAP_BIT << (bit % NETLBL_CATMAP_MAPSIZE); return 0; } @@ -582,34 +671,61 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, u32 end, gfp_t flags) { - int ret_val = 0; - struct netlbl_lsm_secattr_catmap *iter = *catmap; - u32 iter_max_spot; - u32 spot; - u32 orig_spot = iter->startbit; - - /* XXX - This could probably be made a bit faster by combining writes - * to the catmap instead of setting a single bit each time, but for - * right now skipping to the start of the range in the catmap should - * be a nice improvement over calling the individual setbit function - * repeatedly from a loop. */ - - while (iter->next != NULL && - start >= (iter->startbit + NETLBL_CATMAP_SIZE)) - iter = iter->next; - iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; - - for (spot = start; spot <= end && ret_val == 0; spot++) { - if (spot >= iter_max_spot && iter->next != NULL) { - iter = iter->next; - iter_max_spot = iter->startbit + NETLBL_CATMAP_SIZE; - } - ret_val = netlbl_secattr_catmap_setbit(&iter, spot, flags); - if (iter->startbit < orig_spot) - *catmap = iter; + int rc = 0; + u32 spot = start; + + while (rc == 0 && spot <= end) { + if (((spot & (BITS_PER_LONG - 1)) != 0) && + ((end - spot) > BITS_PER_LONG)) { + rc = netlbl_secattr_catmap_setlong(catmap, + spot, + (unsigned long)-1, + flags); + spot += BITS_PER_LONG; + } else + rc = netlbl_secattr_catmap_setbit(catmap, + spot++, + flags); } - return ret_val; + return rc; +} + +/** + * netlbl_secattr_catmap_setlong - Import an unsigned long bitmap + * @catmap: pointer to the category bitmap + * @offset: offset to the start of the imported bitmap + * @bitmap: the bitmap to import + * @flags: memory allocation flags + * + * Description: + * Import the bitmap specified in @bitmap into @catmap, using the offset + * in @offset. The offset must be aligned to an unsigned long. Returns zero + * on success, negative values on failure. + * + */ +int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) +{ + struct netlbl_lsm_secattr_catmap *iter; + u32 idx; + + /* only allow aligned offsets */ + if ((offset & (BITS_PER_LONG - 1)) != 0) + return -EINVAL; + + iter = _netlbl_secattr_catmap_getnode(catmap, + offset, _CM_F_ALLOC, flags); + if (iter == NULL) + return -ENOMEM; + + offset -= iter->startbit; + idx = offset / NETLBL_CATMAP_MAPSIZE; + iter->bitmap[idx] |= bitmap << (offset % NETLBL_CATMAP_MAPSIZE); + + return 0; } /* diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 820313a04d49..842deca9484d 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -89,48 +89,33 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, struct netlbl_lsm_secattr_catmap **catmap) { struct ebitmap_node *e_iter = ebmap->node; - struct netlbl_lsm_secattr_catmap *c_iter; - u32 cmap_idx, cmap_sft; - int i; - - /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, - * however, it is not always compatible with an array of unsigned long - * in ebitmap_node. - * In addition, you should pay attention the following implementation - * assumes unsigned long has a width equal with or less than 64-bit. - */ + unsigned long e_map; + u32 offset; + unsigned int iter; + int rc; if (e_iter == NULL) { *catmap = NULL; return 0; } - c_iter = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (c_iter == NULL) - return -ENOMEM; - *catmap = c_iter; - c_iter->startbit = e_iter->startbit & ~(NETLBL_CATMAP_SIZE - 1); + if (*catmap != NULL) + netlbl_secattr_catmap_free(*catmap); + *catmap = NULL; while (e_iter) { - for (i = 0; i < EBITMAP_UNIT_NUMS; i++) { - unsigned int delta, e_startbit, c_endbit; - - e_startbit = e_iter->startbit + i * EBITMAP_UNIT_SIZE; - c_endbit = c_iter->startbit + NETLBL_CATMAP_SIZE; - if (e_startbit >= c_endbit) { - c_iter->next - = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (c_iter->next == NULL) + offset = e_iter->startbit; + for (iter = 0; iter < EBITMAP_UNIT_NUMS; iter++) { + e_map = e_iter->maps[iter]; + if (e_map != 0) { + rc = netlbl_secattr_catmap_setlong(catmap, + offset, + e_map, + GFP_ATOMIC); + if (rc != 0) goto netlbl_export_failure; - c_iter = c_iter->next; - c_iter->startbit - = e_startbit & ~(NETLBL_CATMAP_SIZE - 1); } - delta = e_startbit - c_iter->startbit; - cmap_idx = delta / NETLBL_CATMAP_MAPSIZE; - cmap_sft = delta % NETLBL_CATMAP_MAPSIZE; - c_iter->bitmap[cmap_idx] - |= e_iter->maps[i] << cmap_sft; + offset += EBITMAP_UNIT_SIZE; } e_iter = e_iter->next; } @@ -155,56 +140,42 @@ netlbl_export_failure: int ebitmap_netlbl_import(struct ebitmap *ebmap, struct netlbl_lsm_secattr_catmap *catmap) { + int rc; struct ebitmap_node *e_iter = NULL; - struct ebitmap_node *emap_prev = NULL; - struct netlbl_lsm_secattr_catmap *c_iter = catmap; - u32 c_idx, c_pos, e_idx, e_sft; - - /* NetLabel's NETLBL_CATMAP_MAPTYPE is defined as an array of u64, - * however, it is not always compatible with an array of unsigned long - * in ebitmap_node. - * In addition, you should pay attention the following implementation - * assumes unsigned long has a width equal with or less than 64-bit. - */ - - do { - for (c_idx = 0; c_idx < NETLBL_CATMAP_MAPCNT; c_idx++) { - unsigned int delta; - u64 map = c_iter->bitmap[c_idx]; - - if (!map) - continue; + struct ebitmap_node *e_prev = NULL; + u32 offset = 0, idx; + unsigned long bitmap; + + for (;;) { + rc = netlbl_secattr_catmap_getlong(catmap, &offset, &bitmap); + if (rc < 0) + goto netlbl_import_failure; + if (offset == (u32)-1) + return 0; - c_pos = c_iter->startbit - + c_idx * NETLBL_CATMAP_MAPSIZE; - if (!e_iter - || c_pos >= e_iter->startbit + EBITMAP_SIZE) { - e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); - if (!e_iter) - goto netlbl_import_failure; - e_iter->startbit - = c_pos - (c_pos % EBITMAP_SIZE); - if (emap_prev == NULL) - ebmap->node = e_iter; - else - emap_prev->next = e_iter; - emap_prev = e_iter; - } - delta = c_pos - e_iter->startbit; - e_idx = delta / EBITMAP_UNIT_SIZE; - e_sft = delta % EBITMAP_UNIT_SIZE; - while (map) { - e_iter->maps[e_idx++] |= map & (-1UL); - map = EBITMAP_SHIFT_UNIT_SIZE(map); - } + if (e_iter == NULL || + offset >= e_iter->startbit + EBITMAP_SIZE) { + e_prev = e_iter; + e_iter = kzalloc(sizeof(*e_iter), GFP_ATOMIC); + if (e_iter == NULL) + goto netlbl_import_failure; + e_iter->startbit = offset & ~(EBITMAP_SIZE - 1); + if (e_prev == NULL) + ebmap->node = e_iter; + else + e_prev->next = e_iter; + ebmap->highbit = e_iter->startbit + EBITMAP_SIZE; } - c_iter = c_iter->next; - } while (c_iter); - if (e_iter != NULL) - ebmap->highbit = e_iter->startbit + EBITMAP_SIZE; - else - ebitmap_destroy(ebmap); + /* offset will always be aligned to an unsigned long */ + idx = EBITMAP_NODE_INDEX(e_iter, offset); + e_iter->maps[idx] = bitmap; + + /* next */ + offset += EBITMAP_UNIT_SIZE; + } + + /* NOTE: we should never reach this return */ return 0; netlbl_import_failure: diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index 9ecf4f4b67a1..ea1bc5055792 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -435,10 +435,7 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, sap->flags |= NETLBL_SECATTR_MLS_CAT; sap->attr.mls.lvl = level; - sap->attr.mls.cat = netlbl_secattr_catmap_alloc(GFP_ATOMIC); - if (!sap->attr.mls.cat) - return -ENOMEM; - sap->attr.mls.cat->startbit = 0; + sap->attr.mls.cat = NULL; for (cat = 1, cp = catset, byte = 0; byte < len; cp++, byte++) for (m = 0x80; m != 0; m >>= 1, cat++) { -- cgit v1.2.3-59-g8ed1b From 4fbe63d1c773cceef3fe1f6ed0c9c268f4f24760 Mon Sep 17 00:00:00 2001 From: Paul Moore Date: Fri, 1 Aug 2014 11:17:37 -0400 Subject: netlabel: shorter names for the NetLabel catmap funcs/structs Historically the NetLabel LSM secattr catmap functions and data structures have had very long names which makes a mess of the NetLabel code and anyone who uses NetLabel. This patch renames the catmap functions and structures from "*_secattr_catmap_*" to just "*_catmap_*" which improves things greatly. There are no substantial code or logic changes in this patch. Signed-off-by: Paul Moore Tested-by: Casey Schaufler --- include/net/netlabel.h | 100 +++++++++++++++++--------------------- net/ipv4/cipso_ipv4.c | 35 +++++++------- net/netlabel/netlabel_kapi.c | 109 ++++++++++++++++++++---------------------- security/selinux/ss/ebitmap.c | 18 +++---- security/selinux/ss/ebitmap.h | 8 ++-- security/smack/smack_access.c | 6 +-- security/smack/smack_lsm.c | 6 +-- security/smack/smackfs.c | 14 +++--- 8 files changed, 139 insertions(+), 157 deletions(-) (limited to 'include') diff --git a/include/net/netlabel.h b/include/net/netlabel.h index bda7a121f31e..a4fc39bb3e4f 100644 --- a/include/net/netlabel.h +++ b/include/net/netlabel.h @@ -139,7 +139,7 @@ struct netlbl_lsm_cache { }; /** - * struct netlbl_lsm_secattr_catmap - NetLabel LSM secattr category bitmap + * struct netlbl_lsm_catmap - NetLabel LSM secattr category bitmap * @startbit: the value of the lowest order bit in the bitmap * @bitmap: the category bitmap * @next: pointer to the next bitmap "node" or NULL @@ -162,10 +162,10 @@ struct netlbl_lsm_cache { #define NETLBL_CATMAP_SIZE (NETLBL_CATMAP_MAPSIZE * \ NETLBL_CATMAP_MAPCNT) #define NETLBL_CATMAP_BIT (NETLBL_CATMAP_MAPTYPE)0x01 -struct netlbl_lsm_secattr_catmap { +struct netlbl_lsm_catmap { u32 startbit; NETLBL_CATMAP_MAPTYPE bitmap[NETLBL_CATMAP_MAPCNT]; - struct netlbl_lsm_secattr_catmap *next; + struct netlbl_lsm_catmap *next; }; /** @@ -209,7 +209,7 @@ struct netlbl_lsm_secattr { struct netlbl_lsm_cache *cache; struct { struct { - struct netlbl_lsm_secattr_catmap *cat; + struct netlbl_lsm_catmap *cat; u32 lvl; } mls; u32 secid; @@ -258,7 +258,7 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache) } /** - * netlbl_secattr_catmap_alloc - Allocate a LSM secattr catmap + * netlbl_catmap_alloc - Allocate a LSM secattr catmap * @flags: memory allocation flags * * Description: @@ -266,24 +266,22 @@ static inline void netlbl_secattr_cache_free(struct netlbl_lsm_cache *cache) * on failure. * */ -static inline struct netlbl_lsm_secattr_catmap *netlbl_secattr_catmap_alloc( - gfp_t flags) +static inline struct netlbl_lsm_catmap *netlbl_catmap_alloc(gfp_t flags) { - return kzalloc(sizeof(struct netlbl_lsm_secattr_catmap), flags); + return kzalloc(sizeof(struct netlbl_lsm_catmap), flags); } /** - * netlbl_secattr_catmap_free - Free a LSM secattr catmap + * netlbl_catmap_free - Free a LSM secattr catmap * @catmap: the category bitmap * * Description: * Free a LSM secattr catmap. * */ -static inline void netlbl_secattr_catmap_free( - struct netlbl_lsm_secattr_catmap *catmap) +static inline void netlbl_catmap_free(struct netlbl_lsm_catmap *catmap) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; while (catmap) { iter = catmap; @@ -321,7 +319,7 @@ static inline void netlbl_secattr_destroy(struct netlbl_lsm_secattr *secattr) if (secattr->flags & NETLBL_SECATTR_CACHE) netlbl_secattr_cache_free(secattr->cache); if (secattr->flags & NETLBL_SECATTR_MLS_CAT) - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); } /** @@ -390,24 +388,22 @@ int netlbl_cfg_cipsov4_map_add(u32 doi, /* * LSM security attribute operations */ -int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset); -int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset); -int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, - u32 *offset, - unsigned long *bitmap); -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, - u32 bit, - gfp_t flags); -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, - u32 start, - u32 end, - gfp_t flags); -int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned long bitmap, - gfp_t flags); +int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset); +int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset); +int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, + u32 *offset, + unsigned long *bitmap); +int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, + u32 bit, + gfp_t flags); +int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, + u32 start, + u32 end, + gfp_t flags); +int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags); /* * LSM protocol operations (NetLabel LSM/kernel API) @@ -499,45 +495,39 @@ static inline int netlbl_cfg_cipsov4_map_add(u32 doi, { return -ENOSYS; } -static inline int netlbl_secattr_catmap_walk( - struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +static inline int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, + u32 offset) { return -ENOENT; } -static inline int netlbl_secattr_catmap_walk_rng( - struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +static inline int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, + u32 offset) { return -ENOENT; } -static inline int netlbl_secattr_catmap_getlong( - struct netlbl_lsm_secattr_catmap *catmap, - u32 *offset, - unsigned long *bitmap) +static inline int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, + u32 *offset, + unsigned long *bitmap) { return 0; } -static inline int netlbl_secattr_catmap_setbit( - struct netlbl_lsm_secattr_catmap **catmap, - u32 bit, - gfp_t flags) +static inline int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, + u32 bit, + gfp_t flags) { return 0; } -static inline int netlbl_secattr_catmap_setrng( - struct netlbl_lsm_secattr_catmap **catmap, - u32 start, - u32 end, - gfp_t flags) +static inline int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, + u32 start, + u32 end, + gfp_t flags) { return 0; } -static int netlbl_secattr_catmap_setlong( - struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned long bitmap, - gfp_t flags) +static int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) { return 0; } diff --git a/net/ipv4/cipso_ipv4.c b/net/ipv4/cipso_ipv4.c index 8a0c7bd6eff4..05b708bbdb0d 100644 --- a/net/ipv4/cipso_ipv4.c +++ b/net/ipv4/cipso_ipv4.c @@ -890,8 +890,8 @@ static int cipso_v4_map_cat_rbm_hton(const struct cipso_v4_doi *doi_def, } for (;;) { - host_spot = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, - host_spot + 1); + host_spot = netlbl_catmap_walk(secattr->attr.mls.cat, + host_spot + 1); if (host_spot < 0) break; @@ -973,7 +973,7 @@ static int cipso_v4_map_cat_rbm_ntoh(const struct cipso_v4_doi *doi_def, return -EPERM; break; } - ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, + ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, host_spot, GFP_ATOMIC); if (ret_val != 0) @@ -1039,8 +1039,7 @@ static int cipso_v4_map_cat_enum_hton(const struct cipso_v4_doi *doi_def, u32 cat_iter = 0; for (;;) { - cat = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, - cat + 1); + cat = netlbl_catmap_walk(secattr->attr.mls.cat, cat + 1); if (cat < 0) break; if ((cat_iter + 2) > net_cat_len) @@ -1075,9 +1074,9 @@ static int cipso_v4_map_cat_enum_ntoh(const struct cipso_v4_doi *doi_def, u32 iter; for (iter = 0; iter < net_cat_len; iter += 2) { - ret_val = netlbl_secattr_catmap_setbit(&secattr->attr.mls.cat, - get_unaligned_be16(&net_cat[iter]), - GFP_ATOMIC); + ret_val = netlbl_catmap_setbit(&secattr->attr.mls.cat, + get_unaligned_be16(&net_cat[iter]), + GFP_ATOMIC); if (ret_val != 0) return ret_val; } @@ -1155,8 +1154,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, return -ENOSPC; for (;;) { - iter = netlbl_secattr_catmap_walk(secattr->attr.mls.cat, - iter + 1); + iter = netlbl_catmap_walk(secattr->attr.mls.cat, iter + 1); if (iter < 0) break; cat_size += (iter == 0 ? 0 : sizeof(u16)); @@ -1164,8 +1162,7 @@ static int cipso_v4_map_cat_rng_hton(const struct cipso_v4_doi *doi_def, return -ENOSPC; array[array_cnt++] = iter; - iter = netlbl_secattr_catmap_walk_rng(secattr->attr.mls.cat, - iter); + iter = netlbl_catmap_walkrng(secattr->attr.mls.cat, iter); if (iter < 0) return -EFAULT; cat_size += sizeof(u16); @@ -1217,10 +1214,10 @@ static int cipso_v4_map_cat_rng_ntoh(const struct cipso_v4_doi *doi_def, else cat_low = 0; - ret_val = netlbl_secattr_catmap_setrng(&secattr->attr.mls.cat, - cat_low, - cat_high, - GFP_ATOMIC); + ret_val = netlbl_catmap_setrng(&secattr->attr.mls.cat, + cat_low, + cat_high, + GFP_ATOMIC); if (ret_val != 0) return ret_val; } @@ -1340,7 +1337,7 @@ static int cipso_v4_parsetag_rbm(const struct cipso_v4_doi *doi_def, tag_len - 4, secattr); if (ret_val != 0) { - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); return ret_val; } @@ -1431,7 +1428,7 @@ static int cipso_v4_parsetag_enum(const struct cipso_v4_doi *doi_def, tag_len - 4, secattr); if (ret_val != 0) { - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); return ret_val; } @@ -1521,7 +1518,7 @@ static int cipso_v4_parsetag_rng(const struct cipso_v4_doi *doi_def, tag_len - 4, secattr); if (ret_val != 0) { - netlbl_secattr_catmap_free(secattr->attr.mls.cat); + netlbl_catmap_free(secattr->attr.mls.cat); return ret_val; } diff --git a/net/netlabel/netlabel_kapi.c b/net/netlabel/netlabel_kapi.c index bc3883814890..05ea4a4cc0ac 100644 --- a/net/netlabel/netlabel_kapi.c +++ b/net/netlabel/netlabel_kapi.c @@ -410,7 +410,7 @@ out_entry: #define _CM_F_WALK 0x00000002 /** - * _netlbl_secattr_catmap_getnode - Get a individual node from a catmap + * _netlbl_catmap_getnode - Get a individual node from a catmap * @catmap: pointer to the category bitmap * @offset: the requested offset * @cm_flags: catmap flags, see _CM_F_* @@ -424,36 +424,36 @@ out_entry: * be returned. Returns a pointer to the node on success, NULL on failure. * */ -static struct netlbl_lsm_secattr_catmap *_netlbl_secattr_catmap_getnode( - struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned int cm_flags, - gfp_t gfp_flags) +static struct netlbl_lsm_catmap *_netlbl_catmap_getnode( + struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned int cm_flags, + gfp_t gfp_flags) { - struct netlbl_lsm_secattr_catmap *iter = *catmap; - struct netlbl_lsm_secattr_catmap *prev = NULL; + struct netlbl_lsm_catmap *iter = *catmap; + struct netlbl_lsm_catmap *prev = NULL; if (iter == NULL) - goto secattr_catmap_getnode_alloc; + goto catmap_getnode_alloc; if (offset < iter->startbit) - goto secattr_catmap_getnode_walk; + goto catmap_getnode_walk; while (iter && offset >= (iter->startbit + NETLBL_CATMAP_SIZE)) { prev = iter; iter = iter->next; } if (iter == NULL || offset < iter->startbit) - goto secattr_catmap_getnode_walk; + goto catmap_getnode_walk; return iter; -secattr_catmap_getnode_walk: +catmap_getnode_walk: if (cm_flags & _CM_F_WALK) return iter; -secattr_catmap_getnode_alloc: +catmap_getnode_alloc: if (!(cm_flags & _CM_F_ALLOC)) return NULL; - iter = netlbl_secattr_catmap_alloc(gfp_flags); + iter = netlbl_catmap_alloc(gfp_flags); if (iter == NULL) return NULL; iter->startbit = offset & ~(NETLBL_CATMAP_SIZE - 1); @@ -470,7 +470,7 @@ secattr_catmap_getnode_alloc: } /** - * netlbl_secattr_catmap_walk - Walk a LSM secattr catmap looking for a bit + * netlbl_catmap_walk - Walk a LSM secattr catmap looking for a bit * @catmap: the category bitmap * @offset: the offset to start searching at, in bits * @@ -479,15 +479,14 @@ secattr_catmap_getnode_alloc: * returns the spot of the first set bit or -ENOENT if no bits are set. * */ -int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +int netlbl_catmap_walk(struct netlbl_lsm_catmap *catmap, u32 offset) { - struct netlbl_lsm_secattr_catmap *iter = catmap; + struct netlbl_lsm_catmap *iter = catmap; u32 idx; u32 bit; NETLBL_CATMAP_MAPTYPE bitmap; - iter = _netlbl_secattr_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); + iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); if (iter == NULL) return -ENOENT; if (offset > iter->startbit) { @@ -524,7 +523,7 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, } /** - * netlbl_secattr_catmap_walk_rng - Find the end of a string of set bits + * netlbl_catmap_walkrng - Find the end of a string of set bits * @catmap: the category bitmap * @offset: the offset to start searching at, in bits * @@ -534,17 +533,16 @@ int netlbl_secattr_catmap_walk(struct netlbl_lsm_secattr_catmap *catmap, * the end of the bitmap. * */ -int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, - u32 offset) +int netlbl_catmap_walkrng(struct netlbl_lsm_catmap *catmap, u32 offset) { - struct netlbl_lsm_secattr_catmap *iter; - struct netlbl_lsm_secattr_catmap *prev = NULL; + struct netlbl_lsm_catmap *iter; + struct netlbl_lsm_catmap *prev = NULL; u32 idx; u32 bit; NETLBL_CATMAP_MAPTYPE bitmask; NETLBL_CATMAP_MAPTYPE bitmap; - iter = _netlbl_secattr_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); + iter = _netlbl_catmap_getnode(&catmap, offset, _CM_F_WALK, 0); if (iter == NULL) return -ENOENT; if (offset > iter->startbit) { @@ -584,7 +582,7 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, } /** - * netlbl_secattr_catmap_getlong - Export an unsigned long bitmap + * netlbl_catmap_getlong - Export an unsigned long bitmap * @catmap: pointer to the category bitmap * @offset: pointer to the requested offset * @bitmap: the exported bitmap @@ -597,11 +595,11 @@ int netlbl_secattr_catmap_walk_rng(struct netlbl_lsm_secattr_catmap *catmap, * Returns zero on sucess, negative values on failure. * */ -int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, - u32 *offset, - unsigned long *bitmap) +int netlbl_catmap_getlong(struct netlbl_lsm_catmap *catmap, + u32 *offset, + unsigned long *bitmap) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; u32 off = *offset; u32 idx; @@ -613,7 +611,7 @@ int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, off = catmap->startbit; *offset = off; } - iter = _netlbl_secattr_catmap_getnode(&catmap, off, _CM_F_NONE, 0); + iter = _netlbl_catmap_getnode(&catmap, off, _CM_F_NONE, 0); if (iter == NULL) { *offset = (u32)-1; return 0; @@ -632,7 +630,7 @@ int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, } /** - * netlbl_secattr_catmap_setbit - Set a bit in a LSM secattr catmap + * netlbl_catmap_setbit - Set a bit in a LSM secattr catmap * @catmap: pointer to the category bitmap * @bit: the bit to set * @flags: memory allocation flags @@ -642,14 +640,14 @@ int netlbl_secattr_catmap_getlong(struct netlbl_lsm_secattr_catmap *catmap, * negative values on failure. * */ -int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, - u32 bit, - gfp_t flags) +int netlbl_catmap_setbit(struct netlbl_lsm_catmap **catmap, + u32 bit, + gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; u32 idx; - iter = _netlbl_secattr_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags); + iter = _netlbl_catmap_getnode(catmap, bit, _CM_F_ALLOC, flags); if (iter == NULL) return -ENOMEM; @@ -661,7 +659,7 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, } /** - * netlbl_secattr_catmap_setrng - Set a range of bits in a LSM secattr catmap + * netlbl_catmap_setrng - Set a range of bits in a LSM secattr catmap * @catmap: pointer to the category bitmap * @start: the starting bit * @end: the last bit in the string @@ -672,10 +670,10 @@ int netlbl_secattr_catmap_setbit(struct netlbl_lsm_secattr_catmap **catmap, * on success, negative values on failure. * */ -int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, - u32 start, - u32 end, - gfp_t flags) +int netlbl_catmap_setrng(struct netlbl_lsm_catmap **catmap, + u32 start, + u32 end, + gfp_t flags) { int rc = 0; u32 spot = start; @@ -683,22 +681,20 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, while (rc == 0 && spot <= end) { if (((spot & (BITS_PER_LONG - 1)) != 0) && ((end - spot) > BITS_PER_LONG)) { - rc = netlbl_secattr_catmap_setlong(catmap, - spot, - (unsigned long)-1, - flags); + rc = netlbl_catmap_setlong(catmap, + spot, + (unsigned long)-1, + flags); spot += BITS_PER_LONG; } else - rc = netlbl_secattr_catmap_setbit(catmap, - spot++, - flags); + rc = netlbl_catmap_setbit(catmap, spot++, flags); } return rc; } /** - * netlbl_secattr_catmap_setlong - Import an unsigned long bitmap + * netlbl_catmap_setlong - Import an unsigned long bitmap * @catmap: pointer to the category bitmap * @offset: offset to the start of the imported bitmap * @bitmap: the bitmap to import @@ -710,20 +706,19 @@ int netlbl_secattr_catmap_setrng(struct netlbl_lsm_secattr_catmap **catmap, * on success, negative values on failure. * */ -int netlbl_secattr_catmap_setlong(struct netlbl_lsm_secattr_catmap **catmap, - u32 offset, - unsigned long bitmap, - gfp_t flags) +int netlbl_catmap_setlong(struct netlbl_lsm_catmap **catmap, + u32 offset, + unsigned long bitmap, + gfp_t flags) { - struct netlbl_lsm_secattr_catmap *iter; + struct netlbl_lsm_catmap *iter; u32 idx; /* only allow aligned offsets */ if ((offset & (BITS_PER_LONG - 1)) != 0) return -EINVAL; - iter = _netlbl_secattr_catmap_getnode(catmap, - offset, _CM_F_ALLOC, flags); + iter = _netlbl_catmap_getnode(catmap, offset, _CM_F_ALLOC, flags); if (iter == NULL) return -ENOMEM; diff --git a/security/selinux/ss/ebitmap.c b/security/selinux/ss/ebitmap.c index 842deca9484d..afe6a269ec17 100644 --- a/security/selinux/ss/ebitmap.c +++ b/security/selinux/ss/ebitmap.c @@ -86,7 +86,7 @@ int ebitmap_cpy(struct ebitmap *dst, struct ebitmap *src) * */ int ebitmap_netlbl_export(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap **catmap) + struct netlbl_lsm_catmap **catmap) { struct ebitmap_node *e_iter = ebmap->node; unsigned long e_map; @@ -100,7 +100,7 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, } if (*catmap != NULL) - netlbl_secattr_catmap_free(*catmap); + netlbl_catmap_free(*catmap); *catmap = NULL; while (e_iter) { @@ -108,10 +108,10 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, for (iter = 0; iter < EBITMAP_UNIT_NUMS; iter++) { e_map = e_iter->maps[iter]; if (e_map != 0) { - rc = netlbl_secattr_catmap_setlong(catmap, - offset, - e_map, - GFP_ATOMIC); + rc = netlbl_catmap_setlong(catmap, + offset, + e_map, + GFP_ATOMIC); if (rc != 0) goto netlbl_export_failure; } @@ -123,7 +123,7 @@ int ebitmap_netlbl_export(struct ebitmap *ebmap, return 0; netlbl_export_failure: - netlbl_secattr_catmap_free(*catmap); + netlbl_catmap_free(*catmap); return -ENOMEM; } @@ -138,7 +138,7 @@ netlbl_export_failure: * */ int ebitmap_netlbl_import(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap *catmap) + struct netlbl_lsm_catmap *catmap) { int rc; struct ebitmap_node *e_iter = NULL; @@ -147,7 +147,7 @@ int ebitmap_netlbl_import(struct ebitmap *ebmap, unsigned long bitmap; for (;;) { - rc = netlbl_secattr_catmap_getlong(catmap, &offset, &bitmap); + rc = netlbl_catmap_getlong(catmap, &offset, &bitmap); if (rc < 0) goto netlbl_import_failure; if (offset == (u32)-1) diff --git a/security/selinux/ss/ebitmap.h b/security/selinux/ss/ebitmap.h index 712c8a7b8e8b..9637b8c71085 100644 --- a/security/selinux/ss/ebitmap.h +++ b/security/selinux/ss/ebitmap.h @@ -132,17 +132,17 @@ int ebitmap_write(struct ebitmap *e, void *fp); #ifdef CONFIG_NETLABEL int ebitmap_netlbl_export(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap **catmap); + struct netlbl_lsm_catmap **catmap); int ebitmap_netlbl_import(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap *catmap); + struct netlbl_lsm_catmap *catmap); #else static inline int ebitmap_netlbl_export(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap **catmap) + struct netlbl_lsm_catmap **catmap) { return -ENOMEM; } static inline int ebitmap_netlbl_import(struct ebitmap *ebmap, - struct netlbl_lsm_secattr_catmap *catmap) + struct netlbl_lsm_catmap *catmap) { return -ENOMEM; } diff --git a/security/smack/smack_access.c b/security/smack/smack_access.c index ea1bc5055792..732df7b91227 100644 --- a/security/smack/smack_access.c +++ b/security/smack/smack_access.c @@ -441,10 +441,10 @@ int smk_netlbl_mls(int level, char *catset, struct netlbl_lsm_secattr *sap, for (m = 0x80; m != 0; m >>= 1, cat++) { if ((m & *cp) == 0) continue; - rc = netlbl_secattr_catmap_setbit(&sap->attr.mls.cat, - cat, GFP_ATOMIC); + rc = netlbl_catmap_setbit(&sap->attr.mls.cat, + cat, GFP_ATOMIC); if (rc < 0) { - netlbl_secattr_catmap_free(sap->attr.mls.cat); + netlbl_catmap_free(sap->attr.mls.cat); return rc; } } diff --git a/security/smack/smack_lsm.c b/security/smack/smack_lsm.c index 14f52be78c75..c32bba566df9 100644 --- a/security/smack/smack_lsm.c +++ b/security/smack/smack_lsm.c @@ -3091,9 +3091,9 @@ static struct smack_known *smack_from_secattr(struct netlbl_lsm_secattr *sap, break; } for (acat = -1, kcat = -1; acat == kcat; ) { - acat = netlbl_secattr_catmap_walk( - sap->attr.mls.cat, acat + 1); - kcat = netlbl_secattr_catmap_walk( + acat = netlbl_catmap_walk(sap->attr.mls.cat, + acat + 1); + kcat = netlbl_catmap_walk( skp->smk_netlabel.attr.mls.cat, kcat + 1); if (acat < 0 || kcat < 0) diff --git a/security/smack/smackfs.c b/security/smack/smackfs.c index 3198cfe1dcc6..893b06b93f6d 100644 --- a/security/smack/smackfs.c +++ b/security/smack/smackfs.c @@ -777,7 +777,7 @@ static int cipso_seq_show(struct seq_file *s, void *v) struct list_head *list = v; struct smack_known *skp = list_entry(list, struct smack_known, list); - struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; int i; @@ -794,8 +794,8 @@ static int cipso_seq_show(struct seq_file *s, void *v) seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); - for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; - i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + for (i = netlbl_catmap_walk(cmp, 0); i >= 0; + i = netlbl_catmap_walk(cmp, i + 1)) { seq_printf(s, "%c%d", sep, i); sep = ','; } @@ -916,7 +916,7 @@ static ssize_t smk_set_cipso(struct file *file, const char __user *buf, rc = smk_netlbl_mls(maplevel, mapcatset, &ncats, SMK_CIPSOLEN); if (rc >= 0) { - netlbl_secattr_catmap_free(skp->smk_netlabel.attr.mls.cat); + netlbl_catmap_free(skp->smk_netlabel.attr.mls.cat); skp->smk_netlabel.attr.mls.cat = ncats.attr.mls.cat; skp->smk_netlabel.attr.mls.lvl = ncats.attr.mls.lvl; rc = count; @@ -966,14 +966,14 @@ static int cipso2_seq_show(struct seq_file *s, void *v) struct list_head *list = v; struct smack_known *skp = list_entry(list, struct smack_known, list); - struct netlbl_lsm_secattr_catmap *cmp = skp->smk_netlabel.attr.mls.cat; + struct netlbl_lsm_catmap *cmp = skp->smk_netlabel.attr.mls.cat; char sep = '/'; int i; seq_printf(s, "%s %3d", skp->smk_known, skp->smk_netlabel.attr.mls.lvl); - for (i = netlbl_secattr_catmap_walk(cmp, 0); i >= 0; - i = netlbl_secattr_catmap_walk(cmp, i + 1)) { + for (i = netlbl_catmap_walk(cmp, 0); i >= 0; + i = netlbl_catmap_walk(cmp, i + 1)) { seq_printf(s, "%c%d", sep, i); sep = ','; } -- cgit v1.2.3-59-g8ed1b